aboutsummaryrefslogtreecommitdiffstats
path: root/gui/fpgaviewwidget.cc
diff options
context:
space:
mode:
authorSergiusz Bazanski <q3k@q3k.org>2018-07-26 17:46:27 +0100
committerSergiusz Bazanski <q3k@q3k.org>2018-07-26 17:46:27 +0100
commit940886f9fa9e9d4238daa77e38a3a066187d66a5 (patch)
tree1cafa9803c986a2cc91e7e3108b07b2b37b803a4 /gui/fpgaviewwidget.cc
parent340c2520b009e8a84b0f8e4bdbce91daad74f367 (diff)
parentae6eeb9d810c647ca1684459627b8dd20870f993 (diff)
downloadnextpnr-940886f9fa9e9d4238daa77e38a3a066187d66a5.tar.gz
nextpnr-940886f9fa9e9d4238daa77e38a3a066187d66a5.tar.bz2
nextpnr-940886f9fa9e9d4238daa77e38a3a066187d66a5.zip
Merge branch 'master' into q3k/clickity
Diffstat (limited to 'gui/fpgaviewwidget.cc')
-rw-r--r--gui/fpgaviewwidget.cc500
1 files changed, 151 insertions, 349 deletions
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc
index 7ee2b4f9..ed3a0bce 100644
--- a/gui/fpgaviewwidget.cc
+++ b/gui/fpgaviewwidget.cc
@@ -31,220 +31,11 @@
NEXTPNR_NAMESPACE_BEGIN
-void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
- const QVector2D *next) const
-{
- // buildPoint emits two vertices per line point, along with normals to move
- // them the right directio when rendering and miter to compensate for
- // bends.
-
- if (cur == nullptr) {
- // BUG
- return;
- }
-
- if (prev == nullptr && next == nullptr) {
- // BUG
- return;
- }
-
- // TODO(q3k): fast path for vertical/horizontal lines?
-
- // TODO(q3k): consider moving some of the linear algebra to the GPU,
- // they're better at this than poor old CPUs.
-
- // Build two unit vectors pointing in the direction of the two segments
- // defined by (prev, cur) and (cur, next)
- QVector2D dprev, dnext;
- if (prev == nullptr) {
- dnext = *next - *cur;
- dprev = dnext;
- } else if (next == nullptr) {
- dprev = *cur - *prev;
- dnext = dprev;
- } else {
- dprev = *cur - *prev;
- dnext = *next - *cur;
- }
- dprev.normalize();
- dnext.normalize();
-
- // Calculate tangent unit vector.
- QVector2D tangent(dprev + dnext);
- tangent.normalize();
-
- // Calculate normal to tangent - this is the line on which the vectors need
- // to be pushed to build a thickened line.
- const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());
-
- // Calculate normal to one of the lines.
- const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
- // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
- // (the ^-1 is performed in the shader)
- const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);
-
- const float x = cur->x();
- const float y = cur->y();
- const float mx = tangent_normal.x();
- const float my = tangent_normal.y();
-
- // Push back 'left' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(miter);
-
- // Push back 'right' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(-miter);
-}
-
-void PolyLine::build(LineShaderData &target) const
-{
- if (points_.size() < 2) {
- return;
- }
- const QVector2D *first = &points_.front();
- const QVector2D *last = &points_.back();
-
- // Index number of vertices, used to build the index buffer.
- unsigned int startIndex = target.vertices.size();
- unsigned int index = startIndex;
-
- // For every point on the line, call buildPoint with (prev, point, next).
- // If we're building a closed line, prev/next wrap around. Otherwise
- // they are passed as nullptr and buildPoint interprets that accordinglu.
- const QVector2D *prev = nullptr;
-
- // Loop iterator used to ensure next is valid.
- unsigned int i = 0;
- for (const QVector2D &point : points_) {
- const QVector2D *next = nullptr;
- if (++i < points_.size()) {
- next = (&point + 1);
- }
-
- // If the line is closed, wrap around. Otherwise, pass nullptr.
- if (prev == nullptr && closed_) {
- buildPoint(&target, last, &point, next);
- } else if (next == nullptr && closed_) {
- buildPoint(&target, prev, &point, first);
- } else {
- buildPoint(&target, prev, &point, next);
- }
-
- // If we have a prev point relative to cur, build a pair of triangles
- // to render vertices into lines.
- if (prev != nullptr) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 2);
-
- target.indices.push_back(index + 2);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 3);
-
- index += 2;
- }
- prev = &point;
- }
-
- // If we're closed, build two more vertices that loop the line around.
- if (closed_) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex);
-
- target.indices.push_back(startIndex);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex + 1);
- }
-}
-
-bool LineShader::compile(void)
-{
- program_ = new QOpenGLShaderProgram(parent_);
- program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
- program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
- if (!program_->link()) {
- printf("could not link program: %s\n", program_->log().toStdString().c_str());
- return false;
- }
-
- if (!vao_.create())
- log_abort();
- vao_.bind();
-
- if (!buffers_.position.create())
- log_abort();
- if (!buffers_.normal.create())
- log_abort();
- if (!buffers_.miter.create())
- log_abort();
- if (!buffers_.index.create())
- log_abort();
-
- attributes_.position = program_->attributeLocation("position");
- attributes_.normal = program_->attributeLocation("normal");
- attributes_.miter = program_->attributeLocation("miter");
- uniforms_.thickness = program_->uniformLocation("thickness");
- uniforms_.projection = program_->uniformLocation("projection");
- uniforms_.color = program_->uniformLocation("color");
-
- vao_.release();
- return true;
-}
-
-void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
-{
- auto gl = QOpenGLContext::currentContext()->functions();
- if (line.vertices.size() == 0)
- return;
- vao_.bind();
- program_->bind();
-
- buffers_.position.bind();
- buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
-
- buffers_.normal.bind();
- buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
-
- buffers_.miter.bind();
- buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
-
- buffers_.index.bind();
- buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
-
- program_->setUniformValue(uniforms_.projection, projection);
- program_->setUniformValue(uniforms_.thickness, thickness);
- program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
-
- buffers_.position.bind();
- program_->enableAttributeArray("position");
- gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.normal.bind();
- program_->enableAttributeArray("normal");
- gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.miter.bind();
- program_->enableAttributeArray("miter");
- gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.index.bind();
- gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0);
-
- program_->disableAttributeArray("miter");
- program_->disableAttributeArray("normal");
- program_->disableAttributeArray("position");
-
- program_->release();
- vao_.release();
-}
-
-FPGAViewWidget::FPGAViewWidget(QWidget *parent)
- : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), paintTimer_(this),
- rendererData_(new FPGAViewWidget::RendererData), rendererArgs_(new FPGAViewWidget::RendererArgs)
+FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
+ QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
+ lineShader_(this), zoom_(500.0f),
+ rendererData_(new FPGAViewWidget::RendererData),
+ rendererArgs_(new FPGAViewWidget::RendererArgs)
{
colors_.background = QColor("#000000");
colors_.grid = QColor("#333");
@@ -311,68 +102,49 @@ void FPGAViewWidget::initializeGL()
0.0);
}
-void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
+void FPGAViewWidget::drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y)
{
const float scale = 1.0;
+
+ if (el.type == GraphicElement::TYPE_BOX) {
+ auto line = PolyLine(true);
+ line.point(x + scale * el.x1, y + scale * el.y1);
+ line.point(x + scale * el.x2, y + scale * el.y1);
+ line.point(x + scale * el.x2, y + scale * el.y2);
+ line.point(x + scale * el.x1, y + scale * el.y2);
+ line.build(out);
+ }
+
+ if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
+ PolyLine(x + scale * el.x1, y + scale * el.y1, x + scale * el.x2, y + scale * el.y2)
+ .build(out);
+ }
+}
+
+void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
+{
float offsetX = decal.x;
float offsetY = decal.y;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
-
- if (el.type == GraphicElement::G_BOX) {
- auto line = PolyLine(true);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
- line.build(out);
- }
-
- if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
- PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
- .build(out);
- }
+ drawGraphicElement(out, el, offsetX, offsetY);
}
}
-void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal)
+void FPGAViewWidget::drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal)
{
- const float scale = 1.0;
- float offsetX = 0.0, offsetY = 0.0;
+ float offsetX = decal.x;
+ float offsetY = decal.y;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
- offsetX = decal.x;
- offsetY = decal.y;
-
- if (el.type == GraphicElement::G_BOX) {
- auto line = PolyLine(true);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
- switch (el.style) {
- case GraphicElement::G_FRAME:
- case GraphicElement::G_INACTIVE:
- case GraphicElement::G_ACTIVE:
- line.build(out[el.style]);
- break;
- default:
- break;
- }
- }
-
- if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
- auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2,
- offsetY + scale * el.y2);
- switch (el.style) {
- case GraphicElement::G_FRAME:
- case GraphicElement::G_INACTIVE:
- case GraphicElement::G_ACTIVE:
- line.build(out[el.style]);
- break;
- default:
- break;
- }
+ switch (el.style) {
+ case GraphicElement::STYLE_FRAME:
+ case GraphicElement::STYLE_INACTIVE:
+ case GraphicElement::STYLE_ACTIVE:
+ drawGraphicElement(out[el.style], el, offsetX, offsetY);
+ break;
+ default:
+ break;
}
}
}
@@ -402,24 +174,28 @@ void FPGAViewWidget::paintGL()
float thick1Px = mouseToWorldDimensions(1, 0).x();
float thick11Px = mouseToWorldDimensions(1.1, 0).x();
- // Draw grid.
+ // Render grid.
auto grid = LineShaderData();
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);
}
+ // Draw grid.
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
rendererDataLock_.lock();
- lineShader_.draw(rendererData_->decals[0], colors_.frame, thick11Px, matrix);
- lineShader_.draw(rendererData_->decals[1], colors_.hidden, thick11Px, matrix);
- lineShader_.draw(rendererData_->decals[2], colors_.inactive, thick11Px, matrix);
- lineShader_.draw(rendererData_->decals[3], colors_.active, thick11Px, matrix);
+ // 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);
+
+ // Draw highlighted items.
for (int i = 0; i < 8; i++)
- lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix);
- lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
rendererDataLock_.unlock();
}
@@ -430,125 +206,151 @@ void FPGAViewWidget::renderLines(void)
if (ctx_ == nullptr)
return;
- ctx_->lock_ui();
-
- // For now, collapse any decal changes into change of all decals.
- // TODO(q3k): fix this
- bool decalsChanged = false;
- if (ctx_->allUiReload) {
- ctx_->allUiReload = false;
- decalsChanged = true;
- }
- if (ctx_->frameUiReload) {
- ctx_->frameUiReload = false;
- decalsChanged = true;
- }
- if (ctx_->belUiReload.size() > 0) {
- ctx_->belUiReload.clear();
- decalsChanged = true;
- }
- if (ctx_->wireUiReload.size() > 0) {
- ctx_->wireUiReload.clear();
- decalsChanged = true;
- }
- 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.
- std::vector<std::pair<BelId, DecalXY>> belDecals;
+ // Data from Context needed to render all decals.
+ std::vector<DecalXY> belDecals;
std::vector<DecalXY> wireDecals;
std::vector<DecalXY> pipDecals;
std::vector<DecalXY> groupDecals;
-
- if (decalsChanged) {
- for (auto bel : ctx_->getBels()) {
- belDecals.push_back(std::pair<BelId, DecalXY>(bel, ctx_->getBelDecal(bel)));
+ 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;
}
- for (auto wire : ctx_->getWires()) {
- wireDecals.push_back(ctx_->getWireDecal(wire));
+ if (ctx_->frameUiReload) {
+ ctx_->frameUiReload = false;
+ decalsChanged = true;
}
- for (auto pip : ctx_->getPips()) {
- pipDecals.push_back(ctx_->getPipDecal(pip));
+ if (ctx_->belUiReload.size() > 0) {
+ ctx_->belUiReload.clear();
+ decalsChanged = true;
}
- for (auto group : ctx_->getGroups()) {
- groupDecals.push_back(ctx_->getGroupDecal(group));
+ if (ctx_->wireUiReload.size() > 0) {
+ ctx_->wireUiReload.clear();
+ decalsChanged = true;
+ }
+ 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()) {
+ belDecals.push_back(ctx_->getBelDecal(bel));
+ }
+ for (auto wire : ctx_->getWires()) {
+ wireDecals.push_back(ctx_->getWireDecal(wire));
+ }
+ for (auto pip : ctx_->getPips()) {
+ pipDecals.push_back(ctx_->getPipDecal(pip));
+ }
+ for (auto group : ctx_->getGroups()) {
+ groupDecals.push_back(ctx_->getGroupDecal(group));
+ }
}
}
- ctx_->unlock_ui();
- rendererArgsLock_.lock();
- auto selectedItems = rendererArgs_->selectedItems;
- auto highlightedItems = rendererArgs_->highlightedItems;
- auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
- rendererArgs_->highlightedOrSelectedChanged = false;
- rendererArgsLock_.unlock();
+ // 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;
+ }
QuadTreeBels::BoundingBox globalBB = QuadTreeBels::BoundingBox(-1000, -1000, 1000, 1000);
+ // Render decals if necessary.
if (decalsChanged) {
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
// Draw Bels.
data->qtBels = std::unique_ptr<QuadTreeBels>(new QuadTreeBels(globalBB));
for (auto const &decal : belDecals) {
- drawDecal(data->decals, decal.second);
- commitToQuadtree(data->qtBels.get(), decal.second, decal.first);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Wires.
for (auto const &decal : wireDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Pips.
for (auto const &decal : pipDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Groups.
for (auto const &decal : groupDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Swap over.
- rendererDataLock_.lock();
- rendererData_ = std::move(data);
- rendererDataLock_.unlock();
+ {
+ 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);
+ }
}
- rendererDataLock_.lock();
- if (decalsChanged || highlightedOrSelectedChanged) {
- rendererData_->selected.clear();
- for (auto &decal : selectedItems) {
- drawDecal(rendererData_->selected, decal);
+ if (highlightedOrSelectedChanged) {
+ QMutexLocker locker(&rendererDataLock_);
+
+ // Render selected.
+ rendererData_->gfxSelected.clear();
+ for (auto &decal : selectedDecals) {
+ drawDecal(rendererData_->gfxSelected, decal);
}
+
+ // Render highlighted.
for (int i = 0; i < 8; i++) {
- rendererData_->highlighted[i].clear();
- for (auto &decal : highlightedItems[i]) {
- drawDecal(rendererData_->highlighted[i], decal);
+ rendererData_->gfxHighlighted[i].clear();
+ for (auto &decal : highlightedDecals[i]) {
+ drawDecal(rendererData_->gfxHighlighted[i], decal);
}
}
}
- rendererDataLock_.unlock();
}
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
{
- rendererArgsLock_.lock();
- rendererArgs_->selectedItems = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
- rendererArgsLock_.unlock();
+ {
+ QMutexLocker locker(&rendererArgsLock_);
+ rendererArgs_->selectedDecals = decals;
+ rendererArgs_->highlightedOrSelectedChanged = true;
+ }
pokeRenderer();
}
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
{
- rendererArgsLock_.lock();
- rendererArgs_->highlightedItems[group] = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
- rendererArgsLock_.unlock();
+ {
+ QMutexLocker locker(&rendererArgsLock_);
+ rendererArgs_->highlightedDecals[group] = decals;
+ rendererArgs_->highlightedOrSelectedChanged = true;
+ }
pokeRenderer();
}
@@ -557,7 +359,7 @@ void FPGAViewWidget::resizeGL(int width, int height) {}
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
- lastPos_ = event->pos();
+ lastDragPos_ = event->pos();
}
if (event->buttons() & Qt::LeftButton) {
int x = event->x();
@@ -609,9 +411,9 @@ QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
- const int dx = event->x() - lastPos_.x();
- const int dy = event->y() - lastPos_.y();
- lastPos_ = event->pos();
+ 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());