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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <math.h>
|
|
|
|
|
2018-06-11 02:48:52 +08:00
|
|
|
#include <QApplication>
|
2018-06-06 03:03:06 +08:00
|
|
|
#include <QCoreApplication>
|
2018-06-07 04:53:52 +08:00
|
|
|
#include <QMouseEvent>
|
2018-06-11 02:48:52 +08:00
|
|
|
#include <QWidget>
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
#include "fpgaviewwidget.h"
|
|
|
|
#include "log.h"
|
2018-06-11 00:33:39 +08:00
|
|
|
#include "mainwindow.h"
|
2018-06-06 03:03:06 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2018-07-27 00:33:19 +08:00
|
|
|
FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
|
|
|
|
QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
|
|
|
|
lineShader_(this), zoom_(500.0f),
|
|
|
|
rendererData_(new FPGAViewWidget::RendererData),
|
|
|
|
rendererArgs_(new FPGAViewWidget::RendererArgs)
|
2018-06-06 03:03:06 +08:00
|
|
|
{
|
2018-07-20 20:19:45 +08:00
|
|
|
colors_.background = QColor("#000000");
|
2018-07-18 02:16:26 +08:00
|
|
|
colors_.grid = QColor("#333");
|
2018-07-24 18:24:14 +08:00
|
|
|
colors_.frame = QColor("#808080");
|
2018-07-18 02:16:26 +08:00
|
|
|
colors_.hidden = QColor("#606060");
|
|
|
|
colors_.inactive = QColor("#303030");
|
|
|
|
colors_.active = QColor("#f0f0f0");
|
|
|
|
colors_.selected = QColor("#ff6600");
|
|
|
|
colors_.highlight[0] = QColor("#6495ed");
|
|
|
|
colors_.highlight[1] = QColor("#7fffd4");
|
|
|
|
colors_.highlight[2] = QColor("#98fb98");
|
|
|
|
colors_.highlight[3] = QColor("#ffd700");
|
|
|
|
colors_.highlight[4] = QColor("#cd5c5c");
|
|
|
|
colors_.highlight[5] = QColor("#fa8072");
|
|
|
|
colors_.highlight[6] = QColor("#ff69b4");
|
|
|
|
colors_.highlight[7] = QColor("#da70d6");
|
|
|
|
|
|
|
|
rendererArgs_->highlightedOrSelectedChanged = false;
|
2018-07-13 17:21:49 +08:00
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
auto fmt = format();
|
|
|
|
fmt.setMajorVersion(3);
|
|
|
|
fmt.setMinorVersion(1);
|
|
|
|
setFormat(fmt);
|
|
|
|
|
|
|
|
fmt = format();
|
|
|
|
if (fmt.majorVersion() < 3) {
|
|
|
|
printf("Could not get OpenGL 3.0 context. Aborting.\n");
|
|
|
|
log_abort();
|
|
|
|
}
|
|
|
|
if (fmt.minorVersion() < 1) {
|
|
|
|
printf("Could not get OpenGL 3.1 context - trying anyway...\n ");
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
|
2018-07-20 20:19:45 +08:00
|
|
|
connect(&paintTimer_, SIGNAL(timeout()), this, SLOT(update()));
|
2018-07-22 04:44:40 +08:00
|
|
|
paintTimer_.start(1000 / 20); // paint GL 20 times per second
|
2018-07-18 02:16:26 +08:00
|
|
|
|
2018-07-20 17:58:30 +08:00
|
|
|
renderRunner_ = std::unique_ptr<PeriodicRunner>(new PeriodicRunner(this, [this] { renderLines(); }));
|
|
|
|
renderRunner_->start();
|
2018-07-22 04:44:40 +08:00
|
|
|
renderRunner_->startTimer(1000 / 2); // render lines 2 times per second
|
2018-06-11 02:48:52 +08:00
|
|
|
}
|
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
FPGAViewWidget::~FPGAViewWidget() {}
|
|
|
|
|
|
|
|
void FPGAViewWidget::newContext(Context *ctx)
|
2018-06-11 02:48:52 +08:00
|
|
|
{
|
2018-06-26 21:47:22 +08:00
|
|
|
ctx_ = ctx;
|
2018-07-23 23:10:06 +08:00
|
|
|
onSelectedArchItem(std::vector<DecalXY>());
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
onHighlightGroupChanged(std::vector<DecalXY>(), i);
|
2018-07-18 02:16:26 +08:00
|
|
|
pokeRenderer();
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); }
|
2018-06-06 03:03:06 +08:00
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); }
|
2018-06-06 03:03:06 +08:00
|
|
|
|
|
|
|
void FPGAViewWidget::initializeGL()
|
|
|
|
{
|
2018-06-22 02:12:20 +08:00
|
|
|
if (!lineShader_.compile()) {
|
|
|
|
log_error("Could not compile shader.\n");
|
|
|
|
}
|
2018-06-06 03:03:06 +08:00
|
|
|
initializeOpenGLFunctions();
|
2018-07-20 20:19:45 +08:00
|
|
|
glClearColor(colors_.background.red() / 255, colors_.background.green() / 255, colors_.background.blue() / 255,
|
|
|
|
0.0);
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
void FPGAViewWidget::renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x, float y)
|
2018-07-15 01:50:50 +08:00
|
|
|
{
|
2018-07-27 00:26:26 +08:00
|
|
|
if (el.type == GraphicElement::TYPE_BOX) {
|
|
|
|
auto line = PolyLine(true);
|
2018-07-27 01:43:00 +08:00
|
|
|
line.point(x + el.x1, y + el.y1);
|
|
|
|
line.point(x + el.x2, y + el.y1);
|
|
|
|
line.point(x + el.x2, y + el.y2);
|
|
|
|
line.point(x + el.x1, y + el.y2);
|
2018-07-27 00:26:26 +08:00
|
|
|
line.build(out);
|
2018-07-27 01:43:00 +08:00
|
|
|
|
|
|
|
data->bbX0 = std::min(data->bbX0, x + el.x1);
|
|
|
|
data->bbY0 = std::min(data->bbY0, x + el.y1);
|
|
|
|
data->bbX1 = std::max(data->bbX1, x + el.x2);
|
|
|
|
data->bbY1 = std::max(data->bbY1, x + el.y2);
|
|
|
|
return;
|
2018-07-27 00:26:26 +08:00
|
|
|
}
|
2018-07-15 01:50:50 +08:00
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
|
2018-07-27 01:43:00 +08:00
|
|
|
PolyLine(x + el.x1, y + el.y1, x + el.x2, y + el.y2).build(out);
|
|
|
|
return;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
void FPGAViewWidget::renderDecal(RendererData *data, LineShaderData &out, const DecalXY &decal)
|
2018-07-15 01:50:50 +08:00
|
|
|
{
|
2018-07-26 23:20:58 +08:00
|
|
|
float offsetX = decal.x;
|
|
|
|
float offsetY = decal.y;
|
2018-07-15 01:50:50 +08:00
|
|
|
|
|
|
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderGraphicElement(data, out, el, offsetX, offsetY);
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
void FPGAViewWidget::renderArchDecal(RendererData *data, const DecalXY &decal)
|
2018-07-15 01:50:50 +08:00
|
|
|
{
|
2018-07-27 00:26:26 +08:00
|
|
|
float offsetX = decal.x;
|
|
|
|
float offsetY = decal.y;
|
2018-07-15 01:50:50 +08:00
|
|
|
|
|
|
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
2018-07-27 00:26:26 +08:00
|
|
|
switch (el.style) {
|
|
|
|
case GraphicElement::STYLE_FRAME:
|
|
|
|
case GraphicElement::STYLE_INACTIVE:
|
|
|
|
case GraphicElement::STYLE_ACTIVE:
|
2018-07-27 01:43:00 +08:00
|
|
|
renderGraphicElement(data, data->gfxByStyle[el.style], el, offsetX, offsetY);
|
2018-07-27 00:26:26 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, IdString id)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
QMatrix4x4 FPGAViewWidget::getProjection(void)
|
|
|
|
{
|
|
|
|
QMatrix4x4 matrix;
|
|
|
|
|
|
|
|
const float aspect = float(width()) / float(height());
|
2018-07-13 04:04:13 +08:00
|
|
|
matrix.perspective(3.14 / 2, aspect, zoomNear_, zoomFar_);
|
2018-07-13 03:22:53 +08:00
|
|
|
matrix.translate(0.0f, 0.0f, -zoom_);
|
|
|
|
return matrix;
|
|
|
|
}
|
|
|
|
|
2018-06-06 03:03:06 +08:00
|
|
|
void FPGAViewWidget::paintGL()
|
|
|
|
{
|
2018-06-22 02:12:20 +08:00
|
|
|
auto gl = QOpenGLContext::currentContext()->functions();
|
|
|
|
const qreal retinaScale = devicePixelRatio();
|
|
|
|
gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
|
|
|
gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
QMatrix4x4 matrix = getProjection();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
matrix *= viewMove_;
|
|
|
|
|
|
|
|
// Calculate world thickness to achieve a screen 1px/1.1px line.
|
2018-07-26 23:20:58 +08:00
|
|
|
float thick1Px = mouseToWorldDimensions(1, 0).x();
|
|
|
|
float thick11Px = mouseToWorldDimensions(1.1, 0).x();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
// Render grid.
|
2018-07-14 03:53:52 +08:00
|
|
|
auto grid = LineShaderData();
|
2018-06-22 02:12:20 +08:00
|
|
|
for (float i = -100.0f; i < 100.0f; i += 1.0f) {
|
|
|
|
PolyLine(-100.0f, i, 100.0f, i).build(grid);
|
|
|
|
PolyLine(i, -100.0f, i, 100.0f).build(grid);
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
// Draw grid.
|
2018-07-18 02:16:26 +08:00
|
|
|
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
|
2018-07-15 01:50:50 +08:00
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
{
|
|
|
|
QMutexLocker locker(&rendererDataLock_);
|
2018-07-13 17:03:09 +08:00
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
// Render Arch graphics.
|
|
|
|
lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_FRAME], colors_.frame, thick11Px, matrix);
|
|
|
|
lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_HIDDEN], colors_.hidden, thick11Px, matrix);
|
|
|
|
lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_INACTIVE], colors_.inactive, thick11Px, matrix);
|
|
|
|
lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_ACTIVE], colors_.active, thick11Px, matrix);
|
2018-07-27 00:26:26 +08:00
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
// Draw highlighted items.
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix);
|
2018-07-18 02:16:26 +08:00
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-20 20:19:45 +08:00
|
|
|
void FPGAViewWidget::pokeRenderer(void) { renderRunner_->poke(); }
|
2018-07-18 02:16:26 +08:00
|
|
|
|
|
|
|
void FPGAViewWidget::renderLines(void)
|
|
|
|
{
|
|
|
|
if (ctx_ == nullptr)
|
|
|
|
return;
|
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
// Data from Context needed to render all decals.
|
2018-07-27 01:43:00 +08:00
|
|
|
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;
|
2018-07-27 00:26:26 +08:00
|
|
|
bool decalsChanged = false;
|
|
|
|
{
|
|
|
|
// Take the UI/Normal mutex on the Context, copy over all we need as
|
|
|
|
// fast as we can.
|
|
|
|
std::lock_guard<std::mutex> lock_ui(ctx_->ui_mutex);
|
|
|
|
std::lock_guard<std::mutex> lock(ctx_->mutex);
|
|
|
|
|
|
|
|
// For now, collapse any decal changes into change of all decals.
|
|
|
|
// TODO(q3k): fix this
|
|
|
|
if (ctx_->allUiReload) {
|
|
|
|
ctx_->allUiReload = false;
|
|
|
|
decalsChanged = true;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
if (ctx_->frameUiReload) {
|
|
|
|
ctx_->frameUiReload = false;
|
|
|
|
decalsChanged = true;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
if (ctx_->belUiReload.size() > 0) {
|
|
|
|
ctx_->belUiReload.clear();
|
|
|
|
decalsChanged = true;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
if (ctx_->wireUiReload.size() > 0) {
|
|
|
|
ctx_->wireUiReload.clear();
|
|
|
|
decalsChanged = true;
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
if (ctx_->pipUiReload.size() > 0) {
|
|
|
|
ctx_->pipUiReload.clear();
|
|
|
|
decalsChanged = true;
|
|
|
|
}
|
|
|
|
if (ctx_->groupUiReload.size() > 0) {
|
|
|
|
ctx_->groupUiReload.clear();
|
|
|
|
decalsChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Local copy of decals, taken as fast as possible to not block the P&R.
|
|
|
|
if (decalsChanged) {
|
|
|
|
for (auto bel : ctx_->getBels()) {
|
2018-07-27 01:43:00 +08:00
|
|
|
belDecals.push_back({ctx_->getBelDecal(bel), ctx_->getBelName(bel)});
|
2018-07-27 00:26:26 +08:00
|
|
|
}
|
|
|
|
for (auto wire : ctx_->getWires()) {
|
2018-07-27 01:43:00 +08:00
|
|
|
wireDecals.push_back({ctx_->getWireDecal(wire), ctx_->getWireName(wire)});
|
2018-07-27 00:26:26 +08:00
|
|
|
}
|
|
|
|
for (auto pip : ctx_->getPips()) {
|
2018-07-27 01:43:00 +08:00
|
|
|
pipDecals.push_back({ctx_->getPipDecal(pip), ctx_->getPipName(pip)});
|
2018-07-27 00:26:26 +08:00
|
|
|
}
|
|
|
|
for (auto group : ctx_->getGroups()) {
|
2018-07-27 01:43:00 +08:00
|
|
|
groupDecals.push_back({ctx_->getGroupDecal(group), ctx_->getGroupName(group)});
|
2018-07-27 00:26:26 +08:00
|
|
|
}
|
2018-07-12 23:22:29 +08:00
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
2018-07-12 23:22:29 +08:00
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
// Arguments from the main UI thread on what we should render.
|
|
|
|
std::vector<DecalXY> selectedDecals;
|
|
|
|
std::vector<DecalXY> highlightedDecals[8];
|
|
|
|
bool highlightedOrSelectedChanged;
|
|
|
|
{
|
|
|
|
// Take the renderer arguments lock, copy over all we need.
|
|
|
|
QMutexLocker lock(&rendererArgsLock_);
|
|
|
|
selectedDecals = rendererArgs_->selectedDecals;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
highlightedDecals[i] = rendererArgs_->highlightedDecals[i];
|
|
|
|
highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
|
|
|
|
rendererArgs_->highlightedOrSelectedChanged = false;
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
|
2018-07-26 23:20:58 +08:00
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
// Render decals if necessary.
|
2018-07-18 02:16:26 +08:00
|
|
|
if (decalsChanged) {
|
|
|
|
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
2018-07-27 01:43:00 +08:00
|
|
|
// Reset bounding box.
|
|
|
|
data->bbX0 = 0;
|
|
|
|
data->bbY0 = 0;
|
|
|
|
data->bbX1 = 0;
|
|
|
|
data->bbY1 = 0;
|
|
|
|
|
2018-07-18 02:16:26 +08:00
|
|
|
// Draw Bels.
|
|
|
|
for (auto const &decal : belDecals) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderArchDecal(data.get(), decal.first);
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
// Draw Wires.
|
|
|
|
for (auto const &decal : wireDecals) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderArchDecal(data.get(), decal.first);
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
// Draw Pips.
|
|
|
|
for (auto const &decal : pipDecals) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderArchDecal(data.get(), decal.first);
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
// Draw Groups.
|
|
|
|
for (auto const &decal : groupDecals) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderArchDecal(data.get(), decal.first);
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 01:43:00 +08:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
// Populate picking quadtree.
|
|
|
|
//data->qt = std::unique_ptr<QuadTreeElements>(new QuadTreeElements(bb));
|
|
|
|
|
|
|
|
//for (auto const &decal : belDecals) {
|
|
|
|
// populateQuadTree(data.get(), decal.first, decal.second);
|
|
|
|
//}
|
|
|
|
|
2018-07-18 02:16:26 +08:00
|
|
|
// Swap over.
|
2018-07-27 00:26:26 +08:00
|
|
|
{
|
|
|
|
QMutexLocker lock(&rendererDataLock_);
|
|
|
|
|
|
|
|
// If we're not re-rendering any highlights/selections, let's
|
|
|
|
// copy them over from teh current object.
|
|
|
|
if (!highlightedOrSelectedChanged) {
|
|
|
|
data->gfxSelected = rendererData_->gfxSelected;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
data->gfxHighlighted[i] = rendererData_->gfxHighlighted[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
rendererData_ = std::move(data);
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 00:26:26 +08:00
|
|
|
if (highlightedOrSelectedChanged) {
|
|
|
|
QMutexLocker locker(&rendererDataLock_);
|
|
|
|
|
|
|
|
// Render selected.
|
|
|
|
rendererData_->gfxSelected.clear();
|
|
|
|
for (auto &decal : selectedDecals) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderDecal(rendererData_.get(), rendererData_->gfxSelected, decal);
|
2018-07-15 01:44:37 +08:00
|
|
|
}
|
2018-07-27 00:26:26 +08:00
|
|
|
|
|
|
|
// Render highlighted.
|
2018-07-15 23:50:58 +08:00
|
|
|
for (int i = 0; i < 8; i++) {
|
2018-07-27 00:26:26 +08:00
|
|
|
rendererData_->gfxHighlighted[i].clear();
|
|
|
|
for (auto &decal : highlightedDecals[i]) {
|
2018-07-27 01:43:00 +08:00
|
|
|
renderDecal(rendererData_.get(), rendererData_->gfxHighlighted[i], decal);
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-15 01:50:50 +08:00
|
|
|
}
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-07-15 01:44:37 +08:00
|
|
|
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
|
|
|
|
{
|
2018-07-27 00:26:26 +08:00
|
|
|
{
|
|
|
|
QMutexLocker locker(&rendererArgsLock_);
|
|
|
|
rendererArgs_->selectedDecals = decals;
|
|
|
|
rendererArgs_->highlightedOrSelectedChanged = true;
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
pokeRenderer();
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
|
|
|
|
{
|
2018-07-27 00:26:26 +08:00
|
|
|
{
|
|
|
|
QMutexLocker locker(&rendererArgsLock_);
|
|
|
|
rendererArgs_->highlightedDecals[group] = decals;
|
|
|
|
rendererArgs_->highlightedOrSelectedChanged = true;
|
|
|
|
}
|
2018-07-18 02:16:26 +08:00
|
|
|
pokeRenderer();
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
void FPGAViewWidget::resizeGL(int width, int height) {}
|
|
|
|
|
2018-07-26 23:20:58 +08:00
|
|
|
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
2018-07-27 00:46:27 +08:00
|
|
|
lastDragPos_ = event->pos();
|
2018-07-26 23:20:58 +08:00
|
|
|
}
|
|
|
|
if (event->buttons() & Qt::LeftButton) {
|
|
|
|
int x = event->x();
|
|
|
|
int y = event->y();
|
|
|
|
auto world = mouseToWorldCoordinates(x, y);
|
|
|
|
rendererDataLock_.lock();
|
2018-07-27 01:43:00 +08:00
|
|
|
//if (rendererData_->qtBels != nullptr) {
|
|
|
|
// auto elems = rendererData_->qtBels->get(world.x(), world.y());
|
|
|
|
// if (elems.size() > 0) {
|
|
|
|
// clickedBel(elems[0]);
|
|
|
|
// }
|
|
|
|
//}
|
2018-07-26 23:20:58 +08:00
|
|
|
rendererDataLock_.unlock();
|
|
|
|
}
|
|
|
|
}
|
2018-06-06 03:03:06 +08:00
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
// Invert the projection matrix to calculate screen/mouse to world/grid
|
|
|
|
// coordinates.
|
|
|
|
QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
|
2018-07-26 23:20:58 +08:00
|
|
|
{
|
|
|
|
const qreal retinaScale = devicePixelRatio();
|
|
|
|
|
|
|
|
auto projection = getProjection();
|
|
|
|
|
|
|
|
QMatrix4x4 vp;
|
|
|
|
vp.viewport(0, 0, width() * retinaScale, height() * retinaScale);
|
|
|
|
|
|
|
|
QVector4D vec(x, y, 0, 1);
|
|
|
|
vec = vp.inverted() * vec;
|
|
|
|
vec = projection.inverted() * vec;
|
|
|
|
|
|
|
|
auto ray = vec.toVector3DAffine();
|
|
|
|
auto world = QVector4D(ray.x()*ray.z(), -ray.y()*ray.z(), 0, 1);
|
|
|
|
world = viewMove_.inverted() * world;
|
|
|
|
|
|
|
|
return world;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)
|
2018-07-13 03:22:53 +08:00
|
|
|
{
|
|
|
|
QMatrix4x4 p = getProjection();
|
|
|
|
QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine();
|
|
|
|
|
2018-07-13 04:04:13 +08:00
|
|
|
float sx = (((float)x) / (width() / 2));
|
|
|
|
float sy = (((float)y) / (height() / 2));
|
2018-07-13 03:22:53 +08:00
|
|
|
return QVector4D(sx / unit.x(), sy / unit.y(), 0, 1);
|
|
|
|
}
|
|
|
|
|
2018-06-06 03:03:06 +08:00
|
|
|
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
|
|
|
|
{
|
2018-07-26 23:20:58 +08:00
|
|
|
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
2018-07-27 00:46:27 +08:00
|
|
|
const int dx = event->x() - lastDragPos_.x();
|
|
|
|
const int dy = event->y() - lastDragPos_.y();
|
|
|
|
lastDragPos_ = event->pos();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-26 23:20:58 +08:00
|
|
|
auto world = mouseToWorldDimensions(dx, dy);
|
|
|
|
viewMove_.translate(world.x(), -world.y());
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-26 23:20:58 +08:00
|
|
|
update();
|
|
|
|
}
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
|
|
|
QPoint degree = event->angleDelta() / 8;
|
|
|
|
|
2018-07-26 19:21:46 +08:00
|
|
|
if (!degree.isNull())
|
|
|
|
zoom(degree.y());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FPGAViewWidget::zoom(int level)
|
|
|
|
{
|
|
|
|
if (zoom_ < zoomNear_) {
|
|
|
|
zoom_ = zoomNear_;
|
|
|
|
} else if (zoom_ < zoomLvl1_) {
|
|
|
|
zoom_ -= level / 10.0;
|
|
|
|
} else if (zoom_ < zoomLvl2_) {
|
|
|
|
zoom_ -= level / 5.0;
|
|
|
|
} else if (zoom_ < zoomFar_) {
|
|
|
|
zoom_ -= level;
|
|
|
|
} else {
|
|
|
|
zoom_ = zoomFar_;
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
2018-07-26 19:21:46 +08:00
|
|
|
update();
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-26 23:26:05 +08:00
|
|
|
void FPGAViewWidget::zoomIn() { zoom(10); }
|
2018-07-26 19:21:46 +08:00
|
|
|
|
2018-07-26 23:26:05 +08:00
|
|
|
void FPGAViewWidget::zoomOut() { zoom(-10); }
|
2018-07-26 19:21:46 +08:00
|
|
|
|
2018-07-26 23:26:05 +08:00
|
|
|
void FPGAViewWidget::zoomSelected() {}
|
2018-07-26 19:21:46 +08:00
|
|
|
|
2018-07-26 23:26:05 +08:00
|
|
|
void FPGAViewWidget::zoomOutbound() {}
|
2018-07-26 19:21:46 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
NEXTPNR_NAMESPACE_END
|