gui: implement zoom to outbounds

This commit is contained in:
Sergiusz Bazanski 2018-07-27 01:21:50 +01:00
parent 48713be0eb
commit 0eb40da749
2 changed files with 128 additions and 35 deletions

View File

@ -33,9 +33,9 @@ NEXTPNR_NAMESPACE_BEGIN
FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
lineShader_(this), zoom_(500.0f),
rendererData_(new FPGAViewWidget::RendererData),
rendererArgs_(new FPGAViewWidget::RendererArgs)
lineShader_(this), zoom_(10.0f),
rendererArgs_(new FPGAViewWidget::RendererArgs),
rendererData_(new FPGAViewWidget::RendererData)
{
colors_.background = QColor("#000000");
colors_.grid = QColor("#333");
@ -55,6 +55,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
colors_.highlight[7] = QColor("#da70d6");
rendererArgs_->changed = false;
rendererArgs_->flags.zoomOutbound = true;
auto fmt = format();
fmt.setMajorVersion(3);
@ -87,6 +88,10 @@ void FPGAViewWidget::newContext(Context *ctx)
onSelectedArchItem(std::vector<DecalXY>());
for (int i = 0; i < 8; i++)
onHighlightGroupChanged(std::vector<DecalXY>(), i);
{
QMutexLocker lock(&rendererArgsLock_);
rendererArgs_->flags.zoomOutbound = true;
}
pokeRenderer();
}
@ -258,8 +263,7 @@ QMatrix4x4 FPGAViewWidget::getProjection(void)
QMatrix4x4 matrix;
const float aspect = float(width()) / float(height());
matrix.perspective(3.14 / 2, aspect, zoomNear_, zoomFar_);
matrix.translate(0.0f, 0.0f, -zoom_);
matrix.perspective(90, aspect, zoomNear_, zoomFar_);
return matrix;
}
@ -271,6 +275,7 @@ void FPGAViewWidget::paintGL()
gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix = getProjection();
matrix.translate(0.0f, 0.0f, -zoom_);
matrix *= viewMove_;
@ -285,6 +290,8 @@ void FPGAViewWidget::paintGL()
PolyLine(-100.0f, i, 100.0f, i).build(grid);
PolyLine(i, -100.0f, i, 100.0f).build(grid);
}
// Flags from pipeline.
PassthroughFlags flags;
// Draw grid.
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
@ -303,6 +310,18 @@ void FPGAViewWidget::paintGL()
lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
lineShader_.draw(rendererData_->gfxHovered, colors_.hovered, thick2Px, matrix);
flags = rendererData_->flags;
}
{
QMutexLocker locker(&rendererArgsLock_);
rendererArgs_->flags.clear();
}
// Check flags passed through pipeline.
if (flags.zoomOutbound) {
zoomOutbound();
}
}
@ -374,15 +393,21 @@ void FPGAViewWidget::renderLines(void)
DecalXY hoveredDecal;
std::vector<DecalXY> highlightedDecals[8];
bool highlightedOrSelectedChanged;
PassthroughFlags flags;
{
// 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_->changed;
rendererArgs_->changed = false;
flags = rendererArgs_->flags;
}
@ -444,7 +469,7 @@ void FPGAViewWidget::renderLines(void)
for (int i = 0; i < 8; i++)
data->gfxHighlighted[i] = rendererData_->gfxHighlighted[i];
}
rendererData_ = std::move(data);
}
}
@ -476,6 +501,11 @@ void FPGAViewWidget::renderLines(void)
}
}
}
{
QMutexLocker locker(&rendererDataLock_);
rendererData_->flags = flags;
}
}
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
@ -601,20 +631,29 @@ QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
QMatrix4x4 vp;
vp.viewport(0, 0, width() * retinaScale, height() * retinaScale);
QVector4D vec(x, y, 0, 1);
QVector4D vec(x, y, 1, 1);
vec = vp.inverted() * vec;
vec = projection.inverted() * vec;
vec = projection.inverted() * QVector4D(vec.x(), vec.y(), -1, 1);
auto ray = vec.toVector3DAffine();
auto world = QVector4D(ray.x()*ray.z(), -ray.y()*ray.z(), 0, 1);
world = viewMove_.inverted() * world;
// Hic sunt dracones.
// TODO(q3k): grab a book, remind yourselfl linear algebra and undo this
// operation properly.
QVector3D ray = vec.toVector3DAffine();
ray.normalize();
ray.setX((ray.x()/-ray.z()) * zoom_);
ray.setY((ray.y()/ray.z()) * zoom_);
ray.setZ(1.0);
return world;
vec = viewMove_.inverted() * QVector4D(ray.x(), ray.y(), ray.z(), 1.0);
vec.setZ(0);
return vec;
}
QVector4D FPGAViewWidget::mouseToWorldDimensions(float x, float y)
{
QMatrix4x4 p = getProjection();
p.translate(0.0f, 0.0f, -zoom_);
QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine();
float sx = (((float)x) / (width() / 2));
@ -632,17 +671,19 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
void FPGAViewWidget::zoom(int level)
{
if (zoom_ < zoomNear_) {
zoom_ = zoomNear_;
} else if (zoom_ < zoomLvl1_) {
zoom_ -= level / 10.0;
if (zoom_ < zoomLvl1_) {
zoom_ -= level / 500.0;
} else if (zoom_ < zoomLvl2_) {
zoom_ -= level / 5.0;
} else if (zoom_ < zoomFar_) {
zoom_ -= level;
zoom_ -= level / 100.0;
} else {
zoom_ = zoomFar_;
zoom_ -= level / 10.0;
}
if (zoom_ < zoomNear_)
zoom_ = zoomNear_;
else if (zoom_ > zoomFar_)
zoom_ = zoomFar_;
update();
}
@ -652,6 +693,29 @@ void FPGAViewWidget::zoomOut() { zoom(-10); }
void FPGAViewWidget::zoomSelected() {}
void FPGAViewWidget::zoomOutbound() {}
void FPGAViewWidget::zoomOutbound()
{
// Get design bounding box.
float x0, y0, x1, y1;
{
QMutexLocker lock(&rendererDataLock_);
x0 = rendererData_->bbX0;
y0 = rendererData_->bbY0;
x1 = rendererData_->bbX1;
y1 = rendererData_->bbY1;
}
float w = x1 - x0;
float h = y1 - y0;
viewMove_.setToIdentity();
viewMove_.translate(-w/2, -h/2);
// Our FOV is π/2, so distance for camera to see a plane of width H is H/2.
// We add 1 unit to cover half a unit of extra space around.
float distance_w = w/2 + 1;
float distance_h = h/2 + 1;
zoom_ = std::max(distance_w, distance_h);
update();
}
NEXTPNR_NAMESPACE_END

View File

@ -124,10 +124,10 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
void clickedWire(WireId wire);
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_ = 100.0f; // do not zoom further than this
const float zoomLvl1_ = 1.0f;
const float zoomLvl2_ = 5.0f;
struct PickedElement {
ElementType type;
@ -195,6 +195,43 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
QColor highlight[8];
} colors_;
// 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
{
bool zoomOutbound;
PassthroughFlags() :
zoomOutbound(false) {}
PassthroughFlags &operator=(const PassthroughFlags &other) noexcept {
zoomOutbound = other.zoomOutbound;
return *this;
}
void clear()
{
zoomOutbound = false;
}
};
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;
// Flags to pass back into the RendererData.
PassthroughFlags flags;
};
std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_;
struct RendererData
{
LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
@ -205,20 +242,12 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
float bbX0, bbY0, bbX1, bbY1;
// Quadtree for picking objects.
std::unique_ptr<PickQuadTree> qt;
// Flags from args.
PassthroughFlags flags;
};
std::unique_ptr<RendererData> rendererData_;
QMutex rendererDataLock_;
struct RendererArgs
{
std::vector<DecalXY> selectedDecals;
std::vector<DecalXY> highlightedDecals[8];
DecalXY hoveredDecal;
bool changed;
};
std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_;
void zoom(int level);
void renderLines(void);
void renderGraphicElement(RendererData *data, LineShaderData &out, const GraphicElement &el, float x, float y);