aboutsummaryrefslogtreecommitdiffstats
path: root/gui/fpgaviewwidget.h
diff options
context:
space:
mode:
Diffstat (limited to 'gui/fpgaviewwidget.h')
-rw-r--r--gui/fpgaviewwidget.h170
1 files changed, 153 insertions, 17 deletions
diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h
index 260ebf05..c35821d9 100644
--- a/gui/fpgaviewwidget.h
+++ b/gui/fpgaviewwidget.h
@@ -31,9 +31,12 @@
#include <QThread>
#include <QTimer>
#include <QWaitCondition>
+#include <boost/optional.hpp>
-#include "nextpnr.h"
+#include "designwidget.h"
#include "lineshader.h"
+#include "nextpnr.h"
+#include "quadtree.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -105,10 +108,9 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
-
public Q_SLOTS:
void newContext(Context *ctx);
- void onSelectedArchItem(std::vector<DecalXY> decals);
+ void onSelectedArchItem(std::vector<DecalXY> decals, bool keep);
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
void pokeRenderer(void);
void zoomIn();
@@ -116,11 +118,103 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
void zoomSelected();
void zoomOutbound();
+ Q_SIGNALS:
+ void clickedBel(BelId bel, bool add);
+ void clickedWire(WireId wire, bool add);
+ void clickedPip(PipId pip, bool add);
+
private:
- const float zoomNear_ = 1.0f; // do not zoom closer than this
- const float zoomFar_ = 10000.0f; // do not zoom further than this
- const float zoomLvl1_ = 100.0f;
- const float zoomLvl2_ = 50.0f;
+ const float zoomNear_ = 0.1f; // do not zoom closer than this
+ const float zoomFar_ = 30.0f; // do not zoom further than this
+ const float zoomLvl1_ = 1.0f;
+ const float zoomLvl2_ = 5.0f;
+
+ struct PickedElement
+ {
+ ElementType type;
+
+ // These are not in an union (and thus this structure is very verbose
+ // and somewhat heavy) because the Id types are typedef'd to StringIds
+ // in the generic architecture. Once that changes (or we get an AnyId
+ // construct from Arches), this should go away.
+ BelId bel;
+ WireId wire;
+ PipId pip;
+ GroupId group;
+
+ float x, y; // Decal X and Y
+
+ PickedElement(ElementType type, float x, float y) : type(type), x(x), y(y) {}
+
+ static PickedElement fromBel(BelId bel, float x, float y)
+ {
+ PickedElement e(ElementType::BEL, x, y);
+ e.bel = bel;
+ return e;
+ }
+ static PickedElement fromWire(WireId wire, float x, float y)
+ {
+ PickedElement e(ElementType::WIRE, x, y);
+ e.wire = wire;
+ return e;
+ }
+ static PickedElement fromPip(PipId pip, float x, float y)
+ {
+ PickedElement e(ElementType::PIP, x, y);
+ e.pip = pip;
+ return e;
+ }
+ static PickedElement fromGroup(GroupId group, float x, float y)
+ {
+ PickedElement e(ElementType::GROUP, x, y);
+ e.group = group;
+ return e;
+ }
+
+ PickedElement(const PickedElement &other) : type(other.type)
+ {
+ switch (type) {
+ case ElementType::BEL:
+ bel = other.bel;
+ break;
+ case ElementType::WIRE:
+ wire = other.wire;
+ break;
+ case ElementType::PIP:
+ pip = other.pip;
+ break;
+ case ElementType::GROUP:
+ group = other.group;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("Invalid ElementType");
+ }
+ }
+
+ DecalXY decal(Context *ctx) const
+ {
+ DecalXY decal;
+ switch (type) {
+ case ElementType::BEL:
+ decal = ctx->getBelDecal(bel);
+ break;
+ case ElementType::WIRE:
+ decal = ctx->getWireDecal(wire);
+ break;
+ case ElementType::PIP:
+ decal = ctx->getPipDecal(pip);
+ break;
+ case ElementType::GROUP:
+ decal = ctx->getGroupDecal(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_;
@@ -140,33 +234,75 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
QColor inactive;
QColor active;
QColor selected;
+ QColor hovered;
QColor highlight[8];
} colors_;
- struct RendererData
+ // Flags that are passed through from renderer arguments to renderer data.
+ // These are used by the UI code to signal events that will only fire when
+ // the next frame gets rendered.
+ struct PassthroughFlags
{
- LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
- LineShaderData gfxSelected;
- LineShaderData gfxHighlighted[8];
+ bool zoomOutbound;
+
+ PassthroughFlags() : zoomOutbound(false) {}
+ PassthroughFlags &operator=(const PassthroughFlags &other) noexcept
+ {
+ zoomOutbound = other.zoomOutbound;
+ return *this;
+ }
+
+ void clear() { zoomOutbound = false; }
};
- std::unique_ptr<RendererData> rendererData_;
- QMutex rendererDataLock_;
struct RendererArgs
{
+ // Decals that he user selected.
std::vector<DecalXY> selectedDecals;
+ // Decals that the user highlighted.
std::vector<DecalXY> highlightedDecals[8];
- bool highlightedOrSelectedChanged;
+ // Decals that the user's mouse is hovering in.
+ DecalXY hoveredDecal;
+ // Whether to render the above three or skip it.
+ bool changed;
+
+ // Flags to pass back into the RendererData.
+ PassthroughFlags flags;
};
std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_;
+ struct RendererData
+ {
+ LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
+ LineShaderData gfxSelected;
+ LineShaderData gfxHovered;
+ LineShaderData gfxHighlighted[8];
+ // Global bounding box of data from Arch.
+ PickQuadTree::BoundingBox bbGlobal;
+ // Bounding box of selected items.
+ PickQuadTree::BoundingBox bbSelected;
+ // Quadtree for picking objects.
+ std::unique_ptr<PickQuadTree> qt;
+ // Flags from args.
+ PassthroughFlags flags;
+ };
+ std::unique_ptr<RendererData> rendererData_;
+ QMutex rendererDataLock_;
+
+ void clampZoom();
+ void zoomToBB(const PickQuadTree::BoundingBox &bb, float margin);
void zoom(int level);
void renderLines(void);
- void drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y);
- void drawDecal(LineShaderData &out, const DecalXY &decal);
- void drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal);
+ void renderGraphicElement(LineShaderData &out, PickQuadTree::BoundingBox &bb, const GraphicElement &el, float x,
+ float y);
+ void renderDecal(LineShaderData &out, PickQuadTree::BoundingBox &bb, 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);
+ boost::optional<PickedElement> pickElement(float worldx, float worldy);
QVector4D mouseToWorldCoordinates(int x, int y);
+ QVector4D mouseToWorldDimensions(float x, float y);
QMatrix4x4 getProjection(void);
};