aboutsummaryrefslogtreecommitdiffstats
path: root/gui
diff options
context:
space:
mode:
authorSergiusz Bazanski <q3k@q3k.org>2018-07-26 22:40:45 +0100
committerSergiusz Bazanski <q3k@q3k.org>2018-07-26 22:40:45 +0100
commitdf908374dc233c23aef0790cbce65aa0a58c81ec (patch)
treed467c0c385f967e1bf2b030d34c62492228fda4d /gui
parent567566585ce9c4e15bd72f914084a92f7a62d553 (diff)
downloadnextpnr-df908374dc233c23aef0790cbce65aa0a58c81ec.tar.gz
nextpnr-df908374dc233c23aef0790cbce65aa0a58c81ec.tar.bz2
nextpnr-df908374dc233c23aef0790cbce65aa0a58c81ec.zip
gui: implement basic cursor picking
Diffstat (limited to 'gui')
-rw-r--r--gui/basewindow.cc1
-rw-r--r--gui/designwidget.cc7
-rw-r--r--gui/designwidget.h4
-rw-r--r--gui/fpgaviewwidget.cc267
-rw-r--r--gui/fpgaviewwidget.h58
5 files changed, 285 insertions, 52 deletions
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index cc6ef4a5..c7e637f6 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -83,6 +83,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
connect(designview, SIGNAL(selected(std::vector<DecalXY>)), fpgaView,
SLOT(onSelectedArchItem(std::vector<DecalXY>)));
connect(fpgaView, SIGNAL(clickedBel(BelId)), designview, SLOT(onClickedBel(BelId)));
+ connect(fpgaView, SIGNAL(clickedWire(WireId)), designview, SLOT(onClickedWire(WireId)));
connect(designview, SIGNAL(highlight(std::vector<DecalXY>, int)), fpgaView,
SLOT(onHighlightGroupChanged(std::vector<DecalXY>, int)));
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index 989b55fe..89babda2 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -514,6 +514,13 @@ void DesignWidget::onClickedBel(BelId bel)
Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)));
}
+void DesignWidget::onClickedWire(WireId wire)
+{
+ QTreeWidgetItem *item = nameToItem[getElementIndex(ElementType::WIRE)].value(ctx->getWireName(wire).c_str(ctx));
+ treeWidget->setCurrentItem(item);
+ Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)));
+}
+
void DesignWidget::onItemSelectionChanged()
{
if (treeWidget->selectedItems().size() == 0)
diff --git a/gui/designwidget.h b/gui/designwidget.h
index 85c326d0..fec0d069 100644
--- a/gui/designwidget.h
+++ b/gui/designwidget.h
@@ -37,7 +37,8 @@ enum class ElementType
WIRE,
PIP,
NET,
- CELL
+ CELL,
+ GROUP
};
class DesignWidget : public QWidget
@@ -75,6 +76,7 @@ class DesignWidget : public QWidget
void newContext(Context *ctx);
void updateTree();
void onClickedBel(BelId bel);
+ void onClickedWire(WireId wire);
private:
Context *ctx;
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc
index fef6c328..46008ece 100644
--- a/gui/fpgaviewwidget.cc
+++ b/gui/fpgaviewwidget.cc
@@ -44,6 +44,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
colors_.inactive = QColor("#303030");
colors_.active = QColor("#f0f0f0");
colors_.selected = QColor("#ff6600");
+ colors_.hovered = QColor("#906030");
colors_.highlight[0] = QColor("#6495ed");
colors_.highlight[1] = QColor("#7fffd4");
colors_.highlight[2] = QColor("#98fb98");
@@ -53,7 +54,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
colors_.highlight[6] = QColor("#ff69b4");
colors_.highlight[7] = QColor("#da70d6");
- rendererArgs_->highlightedOrSelectedChanged = false;
+ rendererArgs_->changed = false;
auto fmt = format();
fmt.setMajorVersion(3);
@@ -75,6 +76,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
renderRunner_ = std::unique_ptr<PeriodicRunner>(new PeriodicRunner(this, [this] { renderLines(); }));
renderRunner_->start();
renderRunner_->startTimer(1000 / 2); // render lines 2 times per second
+ setMouseTracking(true);
}
FPGAViewWidget::~FPGAViewWidget() {}
@@ -102,6 +104,68 @@ void FPGAViewWidget::initializeGL()
0.0);
}
+float FPGAViewWidget::PickedElement::distance(Context *ctx, float wx, float wy) const
+{
+ // Get DecalXY for this element.
+ DecalXY dec = decal(ctx);
+
+ // Coordinates within decal.
+ float dx = wx - dec.x;
+ float dy = wy - dec.y;
+
+ auto graphics = ctx->getDecalGraphics(dec.decal);
+ if (graphics.size() == 0)
+ return -1;
+
+ // TODO(q3k): For multi-line decals, find intersections and also calculate distance to them.
+
+ // Go over its' GraphicElements, and calculate the distance to them.
+ std::vector<float> distances;
+ std::transform(graphics.begin(), graphics.end(), std::back_inserter(distances), [&](const GraphicElement &ge) -> float {
+ switch(ge.type) {
+ case GraphicElement::TYPE_BOX:
+ {
+ // If outside the box, return unit distance to closest border.
+ float outside_x = -1, outside_y = -1;
+ if (dx < ge.x1 || dx > ge.x2) {
+ outside_x = std::min(std::abs(dx - ge.x1), std::abs(dx - ge.x2));
+ }
+ if (dy < ge.y1 || dy > ge.y2) {
+ outside_y = std::min(std::abs(dy - ge.y1), std::abs(dy - ge.y2));
+ }
+ if (outside_x != -1 && outside_y != -1)
+ return std::min(outside_x, outside_y);
+
+ // If in box, return 0.
+ return 0;
+ }
+ case GraphicElement::TYPE_LINE:
+ case GraphicElement::TYPE_ARROW:
+ {
+ // Return somewhat primitively calculated distance to segment.
+ // TODO(q3k): consider coming up with a better algorithm
+ QVector2D w(wx, wy);
+ QVector2D a(ge.x1, ge.y1);
+ QVector2D b(ge.x2, ge.y2);
+ float dw = a.distanceToPoint(w) + b.distanceToPoint(w);
+ float dab = a.distanceToPoint(b);
+ return std::abs(dw-dab) / dab;
+ }
+ default:
+ // Not close to antyhing.
+ return -1;
+ }
+ });
+
+ // Find smallest non -1 distance.
+ // Find closest element.
+ return *std::min_element(distances.begin(), distances.end(), [&](float a, float b) {
+ if (a == -1) return false;
+ if (b == -1) return true;
+ return a < b;
+ });
+}
+
void FPGAViewWidget::renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x, float y)
{
if (el.type == GraphicElement::TYPE_BOX) {
@@ -153,8 +217,40 @@ void FPGAViewWidget::renderArchDecal(RendererData *data, const DecalXY &decal)
}
}
-void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, IdString id)
+void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, const PickedElement &element)
{
+ float x = decal.x;
+ float y = decal.y;
+
+ for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
+ if (el.style == GraphicElement::STYLE_HIDDEN) {
+ continue;
+ }
+
+ if (el.type == GraphicElement::TYPE_BOX) {
+ // Boxes are bounded by themselves.
+ data->qt->insert(PickQuadTree::BoundingBox(x+el.x1, y+el.y1, x+el.x2, y+el.y2), element);
+ }
+
+ if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
+ // Lines are bounded by their AABB slightly enlarged.
+ float x0 = x+el.x1;
+ float y0 = y+el.y1;
+ float x1 = x+el.x2;
+ float y1 = y+el.y2;
+ if (x1 < x0)
+ std::swap(x0, x1);
+ if (y1 < y0)
+ std::swap(y0, y1);
+
+ x0 -= 0.01;
+ y0 -= 0.01;
+ x1 += 0.01;
+ y1 += 0.01;
+
+ data->qt->insert(PickQuadTree::BoundingBox(x0, y0, x1, y1), element);
+ }
+ }
}
QMatrix4x4 FPGAViewWidget::getProjection(void)
@@ -181,6 +277,7 @@ void FPGAViewWidget::paintGL()
// Calculate world thickness to achieve a screen 1px/1.1px line.
float thick1Px = mouseToWorldDimensions(1, 0).x();
float thick11Px = mouseToWorldDimensions(1.1, 0).x();
+ float thick2Px = mouseToWorldDimensions(2, 0).x();
// Render grid.
auto grid = LineShaderData();
@@ -205,6 +302,7 @@ void FPGAViewWidget::paintGL()
lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix);
lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxHovered, colors_.hovered, thick2Px, matrix);
}
}
@@ -216,10 +314,10 @@ void FPGAViewWidget::renderLines(void)
return;
// Data from Context needed to render all decals.
- std::vector<std::pair<DecalXY, IdString>> belDecals;
- std::vector<std::pair<DecalXY, IdString>> wireDecals;
- std::vector<std::pair<DecalXY, IdString>> pipDecals;
- std::vector<std::pair<DecalXY, IdString>> groupDecals;
+ std::vector<std::pair<DecalXY, BelId>> belDecals;
+ std::vector<std::pair<DecalXY, WireId>> wireDecals;
+ std::vector<std::pair<DecalXY, PipId>> pipDecals;
+ std::vector<std::pair<DecalXY, GroupId>> groupDecals;
bool decalsChanged = false;
{
// Take the UI/Normal mutex on the Context, copy over all we need as
@@ -257,32 +355,34 @@ void FPGAViewWidget::renderLines(void)
// Local copy of decals, taken as fast as possible to not block the P&R.
if (decalsChanged) {
for (auto bel : ctx_->getBels()) {
- belDecals.push_back({ctx_->getBelDecal(bel), ctx_->getBelName(bel)});
+ belDecals.push_back({ctx_->getBelDecal(bel), bel});
}
for (auto wire : ctx_->getWires()) {
- wireDecals.push_back({ctx_->getWireDecal(wire), ctx_->getWireName(wire)});
+ wireDecals.push_back({ctx_->getWireDecal(wire), wire});
}
for (auto pip : ctx_->getPips()) {
- pipDecals.push_back({ctx_->getPipDecal(pip), ctx_->getPipName(pip)});
+ pipDecals.push_back({ctx_->getPipDecal(pip), pip});
}
for (auto group : ctx_->getGroups()) {
- groupDecals.push_back({ctx_->getGroupDecal(group), ctx_->getGroupName(group)});
+ groupDecals.push_back({ctx_->getGroupDecal(group), group});
}
}
}
// Arguments from the main UI thread on what we should render.
std::vector<DecalXY> selectedDecals;
+ DecalXY hoveredDecal;
std::vector<DecalXY> highlightedDecals[8];
bool highlightedOrSelectedChanged;
{
// Take the renderer arguments lock, copy over all we need.
QMutexLocker lock(&rendererArgsLock_);
selectedDecals = rendererArgs_->selectedDecals;
+ hoveredDecal = rendererArgs_->hoveredDecal;
for (int i = 0; i < 8; i++)
highlightedDecals[i] = rendererArgs_->highlightedDecals[i];
- highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
- rendererArgs_->highlightedOrSelectedChanged = false;
+ highlightedOrSelectedChanged = rendererArgs_->changed;
+ rendererArgs_->changed = false;
}
@@ -315,14 +415,22 @@ void FPGAViewWidget::renderLines(void)
// Bounding box should be calculated by now.
NPNR_ASSERT((data->bbX1 - data->bbX0) != 0);
NPNR_ASSERT((data->bbY1 - data->bbY0) != 0);
- auto bb = QuadTreeElements::BoundingBox(data->bbX0, data->bbY0, data->bbX1, data->bbY1);
+ auto bb = PickQuadTree::BoundingBox(data->bbX0, data->bbY0, data->bbX1, data->bbY1);
// Populate picking quadtree.
- //data->qt = std::unique_ptr<QuadTreeElements>(new QuadTreeElements(bb));
-
- //for (auto const &decal : belDecals) {
- // populateQuadTree(data.get(), decal.first, decal.second);
- //}
+ data->qt = std::unique_ptr<PickQuadTree>(new PickQuadTree(bb));
+ for (auto const &decal : belDecals) {
+ populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
+ }
+ for (auto const &decal : wireDecals) {
+ populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
+ }
+ for (auto const &decal : pipDecals) {
+ populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
+ }
+ for (auto const &decal : groupDecals) {
+ populateQuadTree(data.get(), decal.first, PickedElement(decal.second, decal.first.x, decal.first.y));
+ }
// Swap over.
{
@@ -332,6 +440,7 @@ void FPGAViewWidget::renderLines(void)
// copy them over from teh current object.
if (!highlightedOrSelectedChanged) {
data->gfxSelected = rendererData_->gfxSelected;
+ data->gfxHovered = rendererData_->gfxHovered;
for (int i = 0; i < 8; i++)
data->gfxHighlighted[i] = rendererData_->gfxHighlighted[i];
}
@@ -343,12 +452,22 @@ void FPGAViewWidget::renderLines(void)
if (highlightedOrSelectedChanged) {
QMutexLocker locker(&rendererDataLock_);
+ // Whether the currently being hovered decal is also selected.
+ bool hoveringSelected = false;
// Render selected.
rendererData_->gfxSelected.clear();
for (auto &decal : selectedDecals) {
+ if (decal == hoveredDecal)
+ hoveringSelected = true;
renderDecal(rendererData_.get(), rendererData_->gfxSelected, decal);
}
+ // Render hovered.
+ rendererData_->gfxHovered.clear();
+ if (!hoveringSelected) {
+ renderDecal(rendererData_.get(), rendererData_->gfxHovered, hoveredDecal);
+ }
+
// Render highlighted.
for (int i = 0; i < 8; i++) {
rendererData_->gfxHighlighted[i].clear();
@@ -364,7 +483,7 @@ void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
{
QMutexLocker locker(&rendererArgsLock_);
rendererArgs_->selectedDecals = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
+ rendererArgs_->changed = true;
}
pokeRenderer();
}
@@ -374,33 +493,103 @@ void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int gr
{
QMutexLocker locker(&rendererArgsLock_);
rendererArgs_->highlightedDecals[group] = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
+ rendererArgs_->changed = true;
}
pokeRenderer();
}
void FPGAViewWidget::resizeGL(int width, int height) {}
+boost::optional<FPGAViewWidget::PickedElement> FPGAViewWidget::pickElement(float worldx, float worldy)
+{
+ // Get elements from renderer whose BBs correspond to the pick.
+ std::vector<PickedElement> elems;
+ {
+ QMutexLocker locker(&rendererDataLock_);
+ if (rendererData_->qt == nullptr) {
+ return {};
+ }
+ elems = rendererData_->qt->get(worldx, worldy);
+ }
+
+ if (elems.size() == 0) {
+ return {};
+ }
+
+ // Calculate distances to all elements picked.
+ using ElemDist = std::pair<const PickedElement *, float>;
+ std::vector<ElemDist> distances;
+ std::transform(elems.begin(), elems.end(), std::back_inserter(distances),
+ [&](const PickedElement &e) -> ElemDist {
+ return std::make_pair(&e, e.distance(ctx_, worldx, worldy));
+ });
+
+ // Find closest non -1 element.
+ auto closest = std::min_element(distances.begin(), distances.end(), [&](const ElemDist &a, const ElemDist &b){
+ if (a.second == -1) return false;
+ if (b.second == -1) return true;
+ return a.second < b.second;
+ });
+
+ // All out of reach?
+ if (closest->second < 0) {
+ return {};
+ }
+
+ return *(closest->first);
+}
+
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
lastDragPos_ = event->pos();
}
if (event->buttons() & Qt::LeftButton) {
- int x = event->x();
- int y = event->y();
- auto world = mouseToWorldCoordinates(x, y);
- rendererDataLock_.lock();
- //if (rendererData_->qtBels != nullptr) {
- // auto elems = rendererData_->qtBels->get(world.x(), world.y());
- // if (elems.size() > 0) {
- // clickedBel(elems[0]);
- // }
- //}
- rendererDataLock_.unlock();
+ auto world = mouseToWorldCoordinates(event->x(), event->y());
+ auto closestOr = pickElement(world.x(), world.y());
+ if (!closestOr)
+ return;
+
+ auto closest = closestOr.value();
+ if (closest.type == ElementType::BEL) {
+ clickedBel(closest.element.bel);
+ } else if (closest.type == ElementType::WIRE) {
+ clickedWire(closest.element.wire);
+ }
+ }
+}
+
+void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
+ const int dx = event->x() - lastDragPos_.x();
+ const int dy = event->y() - lastDragPos_.y();
+ lastDragPos_ = event->pos();
+
+ auto world = mouseToWorldDimensions(dx, dy);
+ viewMove_.translate(world.x(), -world.y());
+
+ update();
+ return;
+ }
+
+ auto world = mouseToWorldCoordinates(event->x(), event->y());
+ auto closestOr = pickElement(world.x(), world.y());
+ if (!closestOr)
+ return;
+
+ auto closest = closestOr.value();
+
+ {
+ QMutexLocker locked(&rendererArgsLock_);
+ rendererArgs_->hoveredDecal = closest.decal(ctx_);
+ rendererArgs_->changed = true;
+ pokeRenderer();
}
+ update();
}
+
// Invert the projection matrix to calculate screen/mouse to world/grid
// coordinates.
QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
@@ -423,7 +612,7 @@ QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
return world;
}
-QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)
+QVector4D FPGAViewWidget::mouseToWorldDimensions(float x, float y)
{
QMatrix4x4 p = getProjection();
QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine();
@@ -433,20 +622,6 @@ QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)
return QVector4D(sx / unit.x(), sy / unit.y(), 0, 1);
}
-void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
-{
- if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
- const int dx = event->x() - lastDragPos_.x();
- const int dy = event->y() - lastDragPos_.y();
- lastDragPos_ = event->pos();
-
- auto world = mouseToWorldDimensions(dx, dy);
- viewMove_.translate(world.x(), -world.y());
-
- update();
- }
-}
-
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
{
QPoint degree = event->angleDelta() / 8;
diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h
index 3ab32dbf..b1eda33a 100644
--- a/gui/fpgaviewwidget.h
+++ b/gui/fpgaviewwidget.h
@@ -20,6 +20,7 @@
#ifndef MAPGLWIDGET_H
#define MAPGLWIDGET_H
+#include <boost/optional.hpp>
#include <QMainWindow>
#include <QMutex>
#include <QOpenGLBuffer>
@@ -120,6 +121,7 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
Q_SIGNALS:
void clickedBel(BelId bel);
+ void clickedWire(WireId wire);
private:
const float zoomNear_ = 1.0f; // do not zoom closer than this
@@ -127,7 +129,49 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
const float zoomLvl1_ = 100.0f;
const float zoomLvl2_ = 50.0f;
- using QuadTreeElements = QuadTree<float, std::pair<ElementType, IdString>>;
+ struct PickedElement {
+ ElementType type;
+ union Inner {
+ BelId bel;
+ WireId wire;
+ PipId pip;
+ GroupId group;
+
+ Inner(BelId _bel) : bel(_bel) {}
+ Inner(WireId _wire) : wire(_wire) {}
+ Inner(PipId _pip) : pip(_pip) {}
+ Inner(GroupId _group) : group(_group) {}
+ } element;
+ float x, y; // Decal X and Y
+ PickedElement(BelId bel, float x, float y) : type(ElementType::BEL), element(bel), x(x), y(y) {}
+ PickedElement(WireId wire, float x, float y) : type(ElementType::WIRE), element(wire), x(x), y(y) {}
+ PickedElement(PipId pip, float x, float y) : type(ElementType::PIP), element(pip), x(x), y(y) {}
+ PickedElement(GroupId group, float x, float y) : type(ElementType::GROUP), element(group), x(x), y(y) {}
+
+ DecalXY decal(Context *ctx) const
+ {
+ DecalXY decal;
+ switch (type) {
+ case ElementType::BEL:
+ decal = ctx->getBelDecal(element.bel);
+ break;
+ case ElementType::WIRE:
+ decal = ctx->getWireDecal(element.wire);
+ break;
+ case ElementType::PIP:
+ decal = ctx->getPipDecal(element.pip);
+ break;
+ case ElementType::GROUP:
+ decal = ctx->getGroupDecal(element.group);
+ break;
+ default:
+ NPNR_ASSERT_FALSE("Invalid ElementType");
+ }
+ return decal;
+ }
+ float distance(Context *ctx, float wx, float wy) const;
+ };
+ using PickQuadTree = QuadTree<float, PickedElement>;
Context *ctx_;
QTimer paintTimer_;
@@ -147,6 +191,7 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
QColor inactive;
QColor active;
QColor selected;
+ QColor hovered;
QColor highlight[8];
} colors_;
@@ -154,11 +199,12 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
LineShaderData gfxSelected;
+ LineShaderData gfxHovered;
LineShaderData gfxHighlighted[8];
// Global bounding box of data from Arch.
float bbX0, bbY0, bbX1, bbY1;
// Quadtree for picking objects.
- std::unique_ptr<QuadTreeElements> qt;
+ std::unique_ptr<PickQuadTree> qt;
};
std::unique_ptr<RendererData> rendererData_;
QMutex rendererDataLock_;
@@ -167,7 +213,8 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
std::vector<DecalXY> selectedDecals;
std::vector<DecalXY> highlightedDecals[8];
- bool highlightedOrSelectedChanged;
+ DecalXY hoveredDecal;
+ bool changed;
};
std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_;
@@ -177,9 +224,10 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
void renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x, float y);
void renderDecal(RendererData *data, LineShaderData &out, const DecalXY &decal);
void renderArchDecal(RendererData *data, const DecalXY &decal);
- void populateQuadTree(RendererData *data, const DecalXY &decal, IdString id);
+ void populateQuadTree(RendererData *data, const DecalXY &decal, const PickedElement& element);
+ boost::optional<PickedElement> pickElement(float worldx, float worldy);
QVector4D mouseToWorldCoordinates(int x, int y);
- QVector4D mouseToWorldDimensions(int x, int y);
+ QVector4D mouseToWorldDimensions(float x, float y);
QMatrix4x4 getProjection(void);
};