gui: implement zoom to selection
This commit is contained in:
parent
5a7fe84a04
commit
6db0731ea5
@ -170,8 +170,7 @@ float FPGAViewWidget::PickedElement::distance(Context *ctx, float wx, float wy)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x,
|
void FPGAViewWidget::renderGraphicElement(LineShaderData &out, PickQuadTree::BoundingBox &bb, const GraphicElement &el, float x, float y)
|
||||||
float y)
|
|
||||||
{
|
{
|
||||||
if (el.type == GraphicElement::TYPE_BOX) {
|
if (el.type == GraphicElement::TYPE_BOX) {
|
||||||
auto line = PolyLine(true);
|
auto line = PolyLine(true);
|
||||||
@ -181,30 +180,34 @@ void FPGAViewWidget::renderGraphicElement(RendererData *data, LineShaderData &ou
|
|||||||
line.point(x + el.x1, y + el.y2);
|
line.point(x + el.x1, y + el.y2);
|
||||||
line.build(out);
|
line.build(out);
|
||||||
|
|
||||||
data->bbX0 = std::min(data->bbX0, x + el.x1);
|
bb.setX0(std::min(bb.x0(), x + el.x1));
|
||||||
data->bbY0 = std::min(data->bbY0, x + el.y1);
|
bb.setY0(std::min(bb.y0(), y + el.y1));
|
||||||
data->bbX1 = std::max(data->bbX1, x + el.x2);
|
bb.setX1(std::max(bb.x1(), x + el.x2));
|
||||||
data->bbY1 = std::max(data->bbY1, x + el.y2);
|
bb.setY1(std::max(bb.y1(), y + el.y2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
|
if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
|
||||||
PolyLine(x + el.x1, y + el.y1, x + el.x2, y + el.y2).build(out);
|
PolyLine(x + el.x1, y + el.y1, x + el.x2, y + el.y2).build(out);
|
||||||
|
bb.setX0(std::min(bb.x0(), x + el.x1));
|
||||||
|
bb.setY0(std::min(bb.y0(), y + el.y1));
|
||||||
|
bb.setX1(std::max(bb.x1(), x + el.x2));
|
||||||
|
bb.setY1(std::max(bb.y1(), y + el.y2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::renderDecal(RendererData *data, LineShaderData &out, const DecalXY &decal)
|
void FPGAViewWidget::renderDecal(LineShaderData &out, PickQuadTree::BoundingBox &bb, const DecalXY &decal)
|
||||||
{
|
{
|
||||||
float offsetX = decal.x;
|
float offsetX = decal.x;
|
||||||
float offsetY = decal.y;
|
float offsetY = decal.y;
|
||||||
|
|
||||||
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
||||||
renderGraphicElement(data, out, el, offsetX, offsetY);
|
renderGraphicElement(out, bb, el, offsetX, offsetY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::renderArchDecal(RendererData *data, const DecalXY &decal)
|
void FPGAViewWidget::renderArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], PickQuadTree::BoundingBox &bb, const DecalXY &decal)
|
||||||
{
|
{
|
||||||
float offsetX = decal.x;
|
float offsetX = decal.x;
|
||||||
float offsetY = decal.y;
|
float offsetY = decal.y;
|
||||||
@ -214,7 +217,7 @@ void FPGAViewWidget::renderArchDecal(RendererData *data, const DecalXY &decal)
|
|||||||
case GraphicElement::STYLE_FRAME:
|
case GraphicElement::STYLE_FRAME:
|
||||||
case GraphicElement::STYLE_INACTIVE:
|
case GraphicElement::STYLE_INACTIVE:
|
||||||
case GraphicElement::STYLE_ACTIVE:
|
case GraphicElement::STYLE_ACTIVE:
|
||||||
renderGraphicElement(data, data->gfxByStyle[el.style], el, offsetX, offsetY);
|
renderGraphicElement(out[el.style], bb, el, offsetX, offsetY);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -415,35 +418,31 @@ void FPGAViewWidget::renderLines(void)
|
|||||||
if (decalsChanged) {
|
if (decalsChanged) {
|
||||||
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
||||||
// Reset bounding box.
|
// Reset bounding box.
|
||||||
data->bbX0 = 0;
|
data->bbGlobal.clear();
|
||||||
data->bbY0 = 0;
|
|
||||||
data->bbX1 = 0;
|
|
||||||
data->bbY1 = 0;
|
|
||||||
|
|
||||||
// Draw Bels.
|
// Draw Bels.
|
||||||
for (auto const &decal : belDecals) {
|
for (auto const &decal : belDecals) {
|
||||||
renderArchDecal(data.get(), decal.first);
|
renderArchDecal(data->gfxByStyle, data->bbGlobal, decal.first);
|
||||||
}
|
}
|
||||||
// Draw Wires.
|
// Draw Wires.
|
||||||
for (auto const &decal : wireDecals) {
|
for (auto const &decal : wireDecals) {
|
||||||
renderArchDecal(data.get(), decal.first);
|
renderArchDecal(data->gfxByStyle, data->bbGlobal, decal.first);
|
||||||
}
|
}
|
||||||
// Draw Pips.
|
// Draw Pips.
|
||||||
for (auto const &decal : pipDecals) {
|
for (auto const &decal : pipDecals) {
|
||||||
renderArchDecal(data.get(), decal.first);
|
renderArchDecal(data->gfxByStyle, data->bbGlobal, decal.first);
|
||||||
}
|
}
|
||||||
// Draw Groups.
|
// Draw Groups.
|
||||||
for (auto const &decal : groupDecals) {
|
for (auto const &decal : groupDecals) {
|
||||||
renderArchDecal(data.get(), decal.first);
|
renderArchDecal(data->gfxByStyle, data->bbGlobal, decal.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounding box should be calculated by now.
|
// Bounding box should be calculated by now.
|
||||||
NPNR_ASSERT((data->bbX1 - data->bbX0) != 0);
|
NPNR_ASSERT(data->bbGlobal.w() != 0);
|
||||||
NPNR_ASSERT((data->bbY1 - data->bbY0) != 0);
|
NPNR_ASSERT(data->bbGlobal.h() != 0);
|
||||||
auto bb = PickQuadTree::BoundingBox(data->bbX0, data->bbY0, data->bbX1, data->bbY1);
|
|
||||||
|
|
||||||
// Populate picking quadtree.
|
// Populate picking quadtree.
|
||||||
data->qt = std::unique_ptr<PickQuadTree>(new PickQuadTree(bb));
|
data->qt = std::unique_ptr<PickQuadTree>(new PickQuadTree(data->bbGlobal));
|
||||||
for (auto const &decal : belDecals) {
|
for (auto const &decal : belDecals) {
|
||||||
populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
|
populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
|
||||||
}
|
}
|
||||||
@ -480,24 +479,25 @@ void FPGAViewWidget::renderLines(void)
|
|||||||
// Whether the currently being hovered decal is also selected.
|
// Whether the currently being hovered decal is also selected.
|
||||||
bool hoveringSelected = false;
|
bool hoveringSelected = false;
|
||||||
// Render selected.
|
// Render selected.
|
||||||
|
rendererData_->bbSelected.clear();
|
||||||
rendererData_->gfxSelected.clear();
|
rendererData_->gfxSelected.clear();
|
||||||
for (auto &decal : selectedDecals) {
|
for (auto &decal : selectedDecals) {
|
||||||
if (decal == hoveredDecal)
|
if (decal == hoveredDecal)
|
||||||
hoveringSelected = true;
|
hoveringSelected = true;
|
||||||
renderDecal(rendererData_.get(), rendererData_->gfxSelected, decal);
|
renderDecal(rendererData_->gfxSelected, rendererData_->bbSelected, decal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render hovered.
|
// Render hovered.
|
||||||
rendererData_->gfxHovered.clear();
|
rendererData_->gfxHovered.clear();
|
||||||
if (!hoveringSelected) {
|
if (!hoveringSelected) {
|
||||||
renderDecal(rendererData_.get(), rendererData_->gfxHovered, hoveredDecal);
|
renderDecal(rendererData_->gfxHovered, rendererData_->bbGlobal, hoveredDecal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render highlighted.
|
// Render highlighted.
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
rendererData_->gfxHighlighted[i].clear();
|
rendererData_->gfxHighlighted[i].clear();
|
||||||
for (auto &decal : highlightedDecals[i]) {
|
for (auto &decal : highlightedDecals[i]) {
|
||||||
renderDecal(rendererData_.get(), rendererData_->gfxHighlighted[i], decal);
|
renderDecal(rendererData_->gfxHighlighted[i], rendererData_->bbGlobal, decal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,31 +690,36 @@ void FPGAViewWidget::zoomIn() { zoom(10); }
|
|||||||
|
|
||||||
void FPGAViewWidget::zoomOut() { zoom(-10); }
|
void FPGAViewWidget::zoomOut() { zoom(-10); }
|
||||||
|
|
||||||
void FPGAViewWidget::zoomSelected() {}
|
void FPGAViewWidget::zoomToBB(const PickQuadTree::BoundingBox &bb)
|
||||||
|
|
||||||
void FPGAViewWidget::zoomOutbound()
|
|
||||||
{
|
{
|
||||||
// Get design bounding box.
|
if (bb.w() < 0.00005 && bb.h() < 0.00005)
|
||||||
float x0, y0, x1, y1;
|
return;
|
||||||
{
|
|
||||||
QMutexLocker lock(&rendererDataLock_);
|
|
||||||
x0 = rendererData_->bbX0;
|
|
||||||
y0 = rendererData_->bbY0;
|
|
||||||
x1 = rendererData_->bbX1;
|
|
||||||
y1 = rendererData_->bbY1;
|
|
||||||
}
|
|
||||||
float w = x1 - x0;
|
|
||||||
float h = y1 - y0;
|
|
||||||
|
|
||||||
viewMove_.setToIdentity();
|
viewMove_.setToIdentity();
|
||||||
viewMove_.translate(-w / 2, -h / 2);
|
viewMove_.translate(-(bb.x0() + bb.w() / 2), -(bb.y0() + bb.h() / 2));
|
||||||
|
|
||||||
// Our FOV is π/2, so distance for camera to see a plane of width H is H/2.
|
// Our FOV is π/2, so distance for camera to see a plane of width H is H/2.
|
||||||
// We add 1 unit to cover half a unit of extra space around.
|
// We add 1 unit to cover half a unit of extra space around.
|
||||||
float distance_w = w / 2 + 1;
|
float distance_w = bb.w() / 2 + 1;
|
||||||
float distance_h = h / 2 + 1;
|
float distance_h = bb.h() / 2 + 1;
|
||||||
zoom_ = std::max(distance_w, distance_h);
|
zoom_ = std::max(distance_w, distance_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FPGAViewWidget::zoomSelected()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&rendererDataLock_);
|
||||||
|
zoomToBB(rendererData_->bbSelected);
|
||||||
|
}
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FPGAViewWidget::zoomOutbound()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&rendererDataLock_);
|
||||||
|
zoomToBB(rendererData_->bbGlobal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -237,7 +237,9 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
LineShaderData gfxHovered;
|
LineShaderData gfxHovered;
|
||||||
LineShaderData gfxHighlighted[8];
|
LineShaderData gfxHighlighted[8];
|
||||||
// Global bounding box of data from Arch.
|
// Global bounding box of data from Arch.
|
||||||
float bbX0, bbY0, bbX1, bbY1;
|
PickQuadTree::BoundingBox bbGlobal;
|
||||||
|
// Bounding box of selected items.
|
||||||
|
PickQuadTree::BoundingBox bbSelected;
|
||||||
// Quadtree for picking objects.
|
// Quadtree for picking objects.
|
||||||
std::unique_ptr<PickQuadTree> qt;
|
std::unique_ptr<PickQuadTree> qt;
|
||||||
// Flags from args.
|
// Flags from args.
|
||||||
@ -246,11 +248,12 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
std::unique_ptr<RendererData> rendererData_;
|
std::unique_ptr<RendererData> rendererData_;
|
||||||
QMutex rendererDataLock_;
|
QMutex rendererDataLock_;
|
||||||
|
|
||||||
|
void zoomToBB(const PickQuadTree::BoundingBox &bb);
|
||||||
void zoom(int level);
|
void zoom(int level);
|
||||||
void renderLines(void);
|
void renderLines(void);
|
||||||
void renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x, float y);
|
void renderGraphicElement(LineShaderData &out, PickQuadTree::BoundingBox &bb, const GraphicElement &el, float x, float y);
|
||||||
void renderDecal(RendererData *data, LineShaderData &out, const DecalXY &decal);
|
void renderDecal(LineShaderData &out, PickQuadTree::BoundingBox &bb, const DecalXY &decal);
|
||||||
void renderArchDecal(RendererData *data, const DecalXY &decal);
|
void renderArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], PickQuadTree::BoundingBox &bb, const DecalXY &decal);
|
||||||
void populateQuadTree(RendererData *data, const DecalXY &decal, const PickedElement &element);
|
void populateQuadTree(RendererData *data, const DecalXY &decal, const PickedElement &element);
|
||||||
boost::optional<PickedElement> pickElement(float worldx, float worldy);
|
boost::optional<PickedElement> pickElement(float worldx, float worldy);
|
||||||
QVector4D mouseToWorldCoordinates(int x, int y);
|
QVector4D mouseToWorldCoordinates(int x, int y);
|
||||||
|
@ -34,7 +34,10 @@ template <typename CoordinateT, typename ElementT> class QuadTreeNode
|
|||||||
friend class QuadTreeNode;
|
friend class QuadTreeNode;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CoordinateT x0_, x1_, y0_, y1_;
|
CoordinateT x0_, y0_, x1_, y1_;
|
||||||
|
|
||||||
|
static constexpr float pinf = std::numeric_limits<CoordinateT>::infinity();
|
||||||
|
static constexpr float ninf = -std::numeric_limits<CoordinateT>::infinity();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Standard constructor for a given (x0,y0), (x1,y1) bounding box
|
// Standard constructor for a given (x0,y0), (x1,y1) bounding box
|
||||||
@ -43,11 +46,14 @@ template <typename CoordinateT, typename ElementT> class QuadTreeNode
|
|||||||
// @param y0 y coordinate of top-left corner of box
|
// @param y0 y coordinate of top-left corner of box
|
||||||
// @param x1 x coordinate of bottom-right corner of box
|
// @param x1 x coordinate of bottom-right corner of box
|
||||||
// @param y1 y coordinate of bottom-right corner of box
|
// @param y1 y coordinate of bottom-right corner of box
|
||||||
BoundingBox(CoordinateT x0, CoordinateT y0, CoordinateT x1, CoordinateT y1) : x0_(x0), x1_(x1), y0_(y0), y1_(y1)
|
BoundingBox(CoordinateT x0, CoordinateT y0, CoordinateT x1, CoordinateT y1) : x0_(x0), y0_(y0), x1_(x1), y1_(y1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox(const BoundingBox &other) : x0_(other.x0_), x1_(other.x1_), y0_(other.y0_), y1_(other.y1_) {}
|
|
||||||
|
BoundingBox() : x0_(pinf), y0_(pinf), x1_(ninf), y1_(ninf) {}
|
||||||
|
|
||||||
|
BoundingBox(const BoundingBox &other) : x0_(other.x0_), y0_(other.y0_), x1_(other.x1_), y1_(other.y1_) {}
|
||||||
|
|
||||||
// Whether a bounding box contains a given points.
|
// Whether a bounding box contains a given points.
|
||||||
// A point is defined to be in a bounding box when it's not lesser than
|
// A point is defined to be in a bounding box when it's not lesser than
|
||||||
@ -72,6 +78,27 @@ template <typename CoordinateT, typename ElementT> class QuadTreeNode
|
|||||||
if (y1_ < y0_)
|
if (y1_ < y0_)
|
||||||
std::swap(y0_, y1_);
|
std::swap(y0_, y1_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoordinateT x0() const { return x0_; }
|
||||||
|
CoordinateT y0() const { return y0_; }
|
||||||
|
CoordinateT x1() const { return x1_; }
|
||||||
|
CoordinateT y1() const { return y1_; }
|
||||||
|
|
||||||
|
void setX0(CoordinateT v) { x0_ = v; }
|
||||||
|
void setY0(CoordinateT v) { y0_ = v; }
|
||||||
|
void setX1(CoordinateT v) { x1_ = v; }
|
||||||
|
void setY1(CoordinateT v) { y1_ = v; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
x0_ = pinf;
|
||||||
|
y0_ = pinf;
|
||||||
|
x1_ = ninf;
|
||||||
|
y1_ = ninf;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoordinateT w() const { return x1_ - x0_; }
|
||||||
|
CoordinateT h() const { return y1_ - y0_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user