2018-06-22 02:12:20 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-06-06 03:03:06 +08:00
|
|
|
#ifndef MAPGLWIDGET_H
|
|
|
|
#define MAPGLWIDGET_H
|
|
|
|
|
2018-06-11 02:48:52 +08:00
|
|
|
#include <QMainWindow>
|
2018-07-18 02:16:26 +08:00
|
|
|
#include <QMutex>
|
2018-06-22 02:12:20 +08:00
|
|
|
#include <QOpenGLBuffer>
|
2018-06-06 03:03:06 +08:00
|
|
|
#include <QOpenGLFunctions>
|
2018-06-22 02:12:20 +08:00
|
|
|
#include <QOpenGLShaderProgram>
|
2018-06-23 03:16:49 +08:00
|
|
|
#include <QOpenGLVertexArrayObject>
|
2018-06-07 04:53:52 +08:00
|
|
|
#include <QOpenGLWidget>
|
2018-06-06 03:03:06 +08:00
|
|
|
#include <QPainter>
|
2018-07-18 02:16:26 +08:00
|
|
|
#include <QThread>
|
2018-07-20 20:19:45 +08:00
|
|
|
#include <QTimer>
|
2018-07-18 02:16:26 +08:00
|
|
|
#include <QWaitCondition>
|
2018-07-27 08:22:29 +08:00
|
|
|
#include <boost/optional.hpp>
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-27 08:22:29 +08:00
|
|
|
#include "designwidget.h"
|
|
|
|
#include "lineshader.h"
|
2018-06-12 02:12:57 +08:00
|
|
|
#include "nextpnr.h"
|
2018-07-26 23:20:58 +08:00
|
|
|
#include "quadtree.h"
|
2018-06-06 03:03:06 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2018-07-20 17:58:30 +08:00
|
|
|
class PeriodicRunner : public QThread
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
2018-07-20 20:19:45 +08:00
|
|
|
private:
|
2018-07-20 17:58:30 +08:00
|
|
|
QMutex mutex_;
|
|
|
|
QWaitCondition condition_;
|
|
|
|
bool abort_;
|
|
|
|
std::function<void()> target_;
|
|
|
|
QTimer timer_;
|
2018-07-20 20:19:45 +08:00
|
|
|
|
|
|
|
public:
|
|
|
|
explicit PeriodicRunner(QObject *parent, std::function<void()> target)
|
|
|
|
: QThread(parent), abort_(false), target_(target), timer_(this)
|
2018-07-20 17:58:30 +08:00
|
|
|
{
|
|
|
|
connect(&timer_, &QTimer::timeout, this, &PeriodicRunner::poke);
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(void) override
|
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
mutex_.lock();
|
|
|
|
condition_.wait(&mutex_);
|
|
|
|
|
|
|
|
if (abort_) {
|
|
|
|
mutex_.unlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
target_();
|
|
|
|
|
|
|
|
mutex_.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-22 04:44:40 +08:00
|
|
|
void startTimer(int msecs) { timer_.start(msecs); }
|
2018-07-20 17:58:30 +08:00
|
|
|
|
|
|
|
~PeriodicRunner()
|
|
|
|
{
|
|
|
|
mutex_.lock();
|
|
|
|
abort_ = true;
|
|
|
|
condition_.wakeOne();
|
|
|
|
mutex_.unlock();
|
|
|
|
|
|
|
|
wait();
|
|
|
|
}
|
|
|
|
|
2018-07-20 20:19:45 +08:00
|
|
|
void poke(void) { condition_.wakeOne(); }
|
2018-07-20 17:58:30 +08:00
|
|
|
};
|
|
|
|
|
2018-06-06 03:03:06 +08:00
|
|
|
class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
public:
|
2018-06-06 03:03:06 +08:00
|
|
|
FPGAViewWidget(QWidget *parent = 0);
|
|
|
|
~FPGAViewWidget();
|
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
protected:
|
2018-07-27 00:33:19 +08:00
|
|
|
// Qt callbacks.
|
2018-06-06 03:03:06 +08:00
|
|
|
void initializeGL() Q_DECL_OVERRIDE;
|
|
|
|
void paintGL() Q_DECL_OVERRIDE;
|
|
|
|
void resizeGL(int width, int height) Q_DECL_OVERRIDE;
|
|
|
|
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
|
|
|
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
|
|
|
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
|
2018-07-27 00:33:19 +08:00
|
|
|
QSize minimumSizeHint() const override;
|
|
|
|
QSize sizeHint() const override;
|
2018-08-22 17:54:08 +08:00
|
|
|
void leaveEvent(QEvent *event) override;
|
2018-07-27 00:33:19 +08:00
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
public Q_SLOTS:
|
|
|
|
void newContext(Context *ctx);
|
2018-07-27 09:14:40 +08:00
|
|
|
void onSelectedArchItem(std::vector<DecalXY> decals, bool keep);
|
2018-07-15 23:50:58 +08:00
|
|
|
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
|
2018-08-22 23:38:42 +08:00
|
|
|
void onHoverItemChanged(DecalXY decal);
|
2018-07-18 02:16:26 +08:00
|
|
|
void pokeRenderer(void);
|
2018-07-26 23:26:05 +08:00
|
|
|
void zoomIn();
|
|
|
|
void zoomOut();
|
|
|
|
void zoomSelected();
|
|
|
|
void zoomOutbound();
|
2019-12-20 22:25:10 +08:00
|
|
|
void enableDisableDecals(bool bels, bool wires, bool pips, bool groups);
|
2020-01-11 22:48:43 +08:00
|
|
|
void movieStart(QString dir, long frameSkip, bool skipSame);
|
2020-01-05 20:51:12 +08:00
|
|
|
void movieStop();
|
2018-07-26 23:20:58 +08:00
|
|
|
Q_SIGNALS:
|
2018-07-27 09:14:40 +08:00
|
|
|
void clickedBel(BelId bel, bool add);
|
|
|
|
void clickedWire(WireId wire, bool add);
|
2018-07-27 09:28:01 +08:00
|
|
|
void clickedPip(PipId pip, bool add);
|
2018-07-15 18:39:19 +08:00
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
private:
|
2020-01-05 20:51:12 +08:00
|
|
|
QString movieDir;
|
|
|
|
long currentMovieFrame;
|
|
|
|
long currentFrameSkip;
|
|
|
|
long movieCounter;
|
|
|
|
bool movieSaving;
|
2020-01-11 22:48:43 +08:00
|
|
|
bool movieSkipSame;
|
|
|
|
QImage movieLastImage;
|
2019-10-07 00:09:23 +08:00
|
|
|
const float zoomNear_ = 0.05f; // do not zoom closer than this
|
2019-12-08 16:33:06 +08:00
|
|
|
float zoomFar_ = 10.0f; // do not zoom further than this
|
2018-07-27 08:21:50 +08:00
|
|
|
const float zoomLvl1_ = 1.0f;
|
|
|
|
const float zoomLvl2_ = 5.0f;
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-27 08:22:29 +08:00
|
|
|
struct PickedElement
|
|
|
|
{
|
2018-07-27 05:40:45 +08:00
|
|
|
ElementType type;
|
2018-07-27 22:11:41 +08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2018-07-27 05:40:45 +08:00
|
|
|
float x, y; // Decal X and Y
|
2018-07-27 22:11:41 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-07-27 20:46:44 +08:00
|
|
|
PickedElement(const PickedElement &other) : type(other.type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case ElementType::BEL:
|
2018-07-27 22:11:41 +08:00
|
|
|
bel = other.bel;
|
2018-07-27 20:46:44 +08:00
|
|
|
break;
|
|
|
|
case ElementType::WIRE:
|
2018-07-27 22:11:41 +08:00
|
|
|
wire = other.wire;
|
2018-07-27 20:46:44 +08:00
|
|
|
break;
|
|
|
|
case ElementType::PIP:
|
2018-07-27 22:11:41 +08:00
|
|
|
pip = other.pip;
|
2018-07-27 20:46:44 +08:00
|
|
|
break;
|
|
|
|
case ElementType::GROUP:
|
2018-07-27 22:11:41 +08:00
|
|
|
group = other.group;
|
2018-07-27 20:46:44 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NPNR_ASSERT_FALSE("Invalid ElementType");
|
|
|
|
}
|
|
|
|
}
|
2018-07-27 05:40:45 +08:00
|
|
|
|
2021-02-25 19:21:39 +08:00
|
|
|
PickedElement &operator=(const PickedElement &other) = default;
|
|
|
|
|
2018-07-27 05:40:45 +08:00
|
|
|
DecalXY decal(Context *ctx) const
|
|
|
|
{
|
|
|
|
DecalXY decal;
|
|
|
|
switch (type) {
|
|
|
|
case ElementType::BEL:
|
2018-07-27 22:11:41 +08:00
|
|
|
decal = ctx->getBelDecal(bel);
|
2018-07-27 05:40:45 +08:00
|
|
|
break;
|
|
|
|
case ElementType::WIRE:
|
2018-07-27 22:11:41 +08:00
|
|
|
decal = ctx->getWireDecal(wire);
|
2018-07-27 05:40:45 +08:00
|
|
|
break;
|
|
|
|
case ElementType::PIP:
|
2018-07-27 22:11:41 +08:00
|
|
|
decal = ctx->getPipDecal(pip);
|
2018-07-27 05:40:45 +08:00
|
|
|
break;
|
|
|
|
case ElementType::GROUP:
|
2018-07-27 22:11:41 +08:00
|
|
|
decal = ctx->getGroupDecal(group);
|
2018-07-27 05:40:45 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NPNR_ASSERT_FALSE("Invalid ElementType");
|
|
|
|
}
|
|
|
|
return decal;
|
|
|
|
}
|
|
|
|
float distance(Context *ctx, float wx, float wy) const;
|
|
|
|
};
|
|
|
|
using PickQuadTree = QuadTree<float, PickedElement>;
|
2018-07-27 01:43:00 +08:00
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
Context *ctx_;
|
2018-07-20 17:58:30 +08:00
|
|
|
QTimer paintTimer_;
|
|
|
|
std::unique_ptr<PeriodicRunner> renderRunner_;
|
2018-07-18 02:16:26 +08:00
|
|
|
|
2018-07-27 00:33:19 +08:00
|
|
|
QPoint lastDragPos_;
|
|
|
|
LineShader lineShader_;
|
|
|
|
QMatrix4x4 viewMove_;
|
|
|
|
float zoom_;
|
|
|
|
|
2019-12-20 22:25:10 +08:00
|
|
|
bool displayBel_;
|
|
|
|
bool displayWire_;
|
|
|
|
bool displayPip_;
|
|
|
|
bool displayGroup_;
|
|
|
|
|
2018-07-20 20:19:45 +08:00
|
|
|
struct
|
|
|
|
{
|
2018-07-18 02:16:26 +08:00
|
|
|
QColor background;
|
|
|
|
QColor grid;
|
|
|
|
QColor frame;
|
|
|
|
QColor hidden;
|
|
|
|
QColor inactive;
|
|
|
|
QColor active;
|
|
|
|
QColor selected;
|
2018-07-27 05:40:45 +08:00
|
|
|
QColor hovered;
|
2018-07-18 02:16:26 +08:00
|
|
|
QColor highlight[8];
|
|
|
|
} colors_;
|
|
|
|
|
2018-07-27 08:21:50 +08:00
|
|
|
struct RendererArgs
|
|
|
|
{
|
|
|
|
// Decals that he user selected.
|
|
|
|
std::vector<DecalXY> selectedDecals;
|
|
|
|
// Decals that the user highlighted.
|
|
|
|
std::vector<DecalXY> highlightedDecals[8];
|
|
|
|
// Decals that the user's mouse is hovering in.
|
|
|
|
DecalXY hoveredDecal;
|
|
|
|
// Whether to render the above three or skip it.
|
|
|
|
bool changed;
|
2018-10-27 17:07:14 +08:00
|
|
|
// Whether to render grid or skip it.
|
|
|
|
bool gridChanged;
|
2018-07-27 08:21:50 +08:00
|
|
|
|
2018-11-11 16:04:20 +08:00
|
|
|
// Flags for rendering.
|
|
|
|
bool zoomOutbound;
|
2018-10-21 20:01:44 +08:00
|
|
|
// Hint text
|
|
|
|
std::string hintText;
|
2018-10-25 02:50:38 +08:00
|
|
|
// cursor pos
|
2018-11-11 16:04:20 +08:00
|
|
|
int x, y;
|
2018-07-27 08:21:50 +08:00
|
|
|
};
|
|
|
|
std::unique_ptr<RendererArgs> rendererArgs_;
|
|
|
|
QMutex rendererArgsLock_;
|
|
|
|
|
2018-07-20 20:19:45 +08:00
|
|
|
struct RendererData
|
|
|
|
{
|
2018-10-27 17:07:14 +08:00
|
|
|
LineShaderData gfxGrid;
|
2018-07-27 00:26:26 +08:00
|
|
|
LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
|
|
|
|
LineShaderData gfxSelected;
|
2018-07-27 05:40:45 +08:00
|
|
|
LineShaderData gfxHovered;
|
2018-07-27 00:26:26 +08:00
|
|
|
LineShaderData gfxHighlighted[8];
|
2018-07-27 01:43:00 +08:00
|
|
|
// Global bounding box of data from Arch.
|
2018-07-27 09:04:24 +08:00
|
|
|
PickQuadTree::BoundingBox bbGlobal;
|
|
|
|
// Bounding box of selected items.
|
|
|
|
PickQuadTree::BoundingBox bbSelected;
|
2018-07-27 01:43:00 +08:00
|
|
|
// Quadtree for picking objects.
|
2018-07-27 05:40:45 +08:00
|
|
|
std::unique_ptr<PickQuadTree> qt;
|
2018-07-18 02:16:26 +08:00
|
|
|
};
|
2018-07-27 00:33:19 +08:00
|
|
|
std::unique_ptr<RendererData> rendererData_;
|
|
|
|
QMutex rendererDataLock_;
|
2018-07-20 20:19:45 +08:00
|
|
|
|
2018-07-27 22:46:00 +08:00
|
|
|
void clampZoom();
|
2018-10-14 18:40:19 +08:00
|
|
|
void zoomToBB(const PickQuadTree::BoundingBox &bb, float margin, bool clamp);
|
2018-07-27 00:33:19 +08:00
|
|
|
void zoom(int level);
|
|
|
|
void renderLines(void);
|
2018-07-27 22:11:41 +08:00
|
|
|
void renderGraphicElement(LineShaderData &out, PickQuadTree::BoundingBox &bb, const GraphicElement &el, float x,
|
|
|
|
float y);
|
2018-07-27 09:04:24 +08:00
|
|
|
void renderDecal(LineShaderData &out, PickQuadTree::BoundingBox &bb, const DecalXY &decal);
|
2018-07-27 22:11:41 +08:00
|
|
|
void renderArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], PickQuadTree::BoundingBox &bb,
|
|
|
|
const DecalXY &decal);
|
2018-07-27 08:22:29 +08:00
|
|
|
void populateQuadTree(RendererData *data, const DecalXY &decal, const PickedElement &element);
|
2018-07-27 05:40:45 +08:00
|
|
|
boost::optional<PickedElement> pickElement(float worldx, float worldy);
|
2018-07-27 00:33:19 +08:00
|
|
|
QVector4D mouseToWorldCoordinates(int x, int y);
|
2018-07-27 05:40:45 +08:00
|
|
|
QVector4D mouseToWorldDimensions(float x, float y);
|
2018-07-27 00:33:19 +08:00
|
|
|
QMatrix4x4 getProjection(void);
|
2018-08-17 03:44:37 +08:00
|
|
|
void update_vbos();
|
2018-06-06 03:03:06 +08:00
|
|
|
};
|
2018-06-12 20:24:59 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
NEXTPNR_NAMESPACE_END
|
|
|
|
|
2018-06-06 03:03:06 +08:00
|
|
|
#endif
|