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-06-23 22:06:49 +08:00
|
|
|
void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
|
|
|
|
const QVector2D *next) const
|
2018-06-22 02:12:20 +08:00
|
|
|
{
|
|
|
|
// 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.
|
2018-06-22 22:13:05 +08:00
|
|
|
const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
// Calculate normal to one of the lines.
|
2018-06-22 22:13:05 +08:00
|
|
|
const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
|
2018-06-22 02:12:20 +08:00
|
|
|
// https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
|
|
|
|
// (the ^-1 is performed in the shader)
|
2018-06-22 22:13:05 +08:00
|
|
|
const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-06-22 22:13:05 +08:00
|
|
|
const float x = cur->x();
|
|
|
|
const float y = cur->y();
|
|
|
|
const float mx = tangent_normal.x();
|
|
|
|
const float my = tangent_normal.y();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2018-06-22 22:13:05 +08:00
|
|
|
// If we're closed, build two more vertices that loop the line around.
|
2018-06-22 02:12:20 +08:00
|
|
|
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_);
|
2018-06-23 22:06:49 +08:00
|
|
|
program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
|
|
|
|
program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
|
2018-06-22 02:12:20 +08:00
|
|
|
if (!program_->link()) {
|
2018-06-23 22:06:49 +08:00
|
|
|
printf("could not link program: %s\n", program_->log().toStdString().c_str());
|
2018-06-22 02:12:20 +08:00
|
|
|
return false;
|
|
|
|
}
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
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");
|
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
vao_.release();
|
2018-06-22 02:12:20 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
|
|
|
|
{
|
|
|
|
auto gl = QOpenGLContext::currentContext()->functions();
|
2018-06-23 03:16:49 +08:00
|
|
|
vao_.bind();
|
2018-06-22 02:12:20 +08:00
|
|
|
program_->bind();
|
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
buffers_.position.bind();
|
2018-06-23 22:06:49 +08:00
|
|
|
buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
buffers_.normal.bind();
|
2018-06-23 22:06:49 +08:00
|
|
|
buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
buffers_.miter.bind();
|
2018-06-23 22:06:49 +08:00
|
|
|
buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
buffers_.index.bind();
|
2018-06-23 22:06:49 +08:00
|
|
|
buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
|
2018-06-23 03:16:49 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
program_->setUniformValue(uniforms_.projection, projection);
|
|
|
|
program_->setUniformValue(uniforms_.thickness, line.thickness);
|
2018-06-23 22:06:49 +08:00
|
|
|
program_->setUniformValue(uniforms_.color, line.color.r, line.color.g, line.color.b, line.color.a);
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
buffers_.position.bind();
|
|
|
|
program_->enableAttributeArray("position");
|
2018-06-23 22:06:49 +08:00
|
|
|
gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
buffers_.normal.bind();
|
|
|
|
program_->enableAttributeArray("normal");
|
2018-06-23 22:06:49 +08:00
|
|
|
gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
buffers_.miter.bind();
|
|
|
|
program_->enableAttributeArray("miter");
|
2018-06-23 22:06:49 +08:00
|
|
|
gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0);
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-06-23 03:16:49 +08:00
|
|
|
buffers_.index.bind();
|
2018-06-23 22:06:49 +08:00
|
|
|
gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0);
|
2018-06-23 03:16:49 +08:00
|
|
|
|
|
|
|
program_->disableAttributeArray("miter");
|
|
|
|
program_->disableAttributeArray("normal");
|
|
|
|
program_->disableAttributeArray("position");
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
program_->release();
|
2018-06-23 03:16:49 +08:00
|
|
|
vao_.release();
|
2018-06-22 02:12:20 +08:00
|
|
|
}
|
|
|
|
|
2018-07-13 04:04:13 +08:00
|
|
|
FPGAViewWidget::FPGAViewWidget(QWidget *parent) : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr)
|
2018-06-06 03:03:06 +08:00
|
|
|
{
|
2018-07-13 00:02:57 +08:00
|
|
|
backgroundColor = QColor("#ffffff");
|
|
|
|
gridColor = QColor("#ddd");
|
2018-07-13 17:03:09 +08:00
|
|
|
gFrameColor = QColor("#303030");
|
|
|
|
gHiddenColor = QColor("#a0a0a0");
|
|
|
|
gInactiveColor = QColor("#d0d0d0");
|
|
|
|
gActiveColor = QColor("#101010");
|
2018-07-13 00:02:57 +08:00
|
|
|
frameColor = QColor("#0066ba");
|
2018-06-23 03:16:49 +08:00
|
|
|
auto fmt = format();
|
|
|
|
fmt.setMajorVersion(3);
|
|
|
|
fmt.setMinorVersion(1);
|
|
|
|
setFormat(fmt);
|
|
|
|
|
|
|
|
fmt = format();
|
2018-07-07 01:19:18 +08:00
|
|
|
// printf("FPGAViewWidget running on OpenGL %d.%d\n", fmt.majorVersion(), fmt.minorVersion());
|
2018-06-23 03:16:49 +08:00
|
|
|
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-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;
|
|
|
|
update();
|
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-13 04:04:13 +08:00
|
|
|
glClearColor(backgroundColor.red() / 255, backgroundColor.green() / 255, backgroundColor.blue() / 255, 0.0);
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-07-13 04:30:36 +08:00
|
|
|
void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
|
2018-06-11 01:56:17 +08:00
|
|
|
{
|
2018-07-13 04:30:36 +08:00
|
|
|
const float scale = 1.0;
|
|
|
|
float offsetX = 0.0, offsetY = 0.0;
|
|
|
|
|
|
|
|
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);
|
|
|
|
line.build(out);
|
|
|
|
}
|
2018-06-11 01:56:17 +08:00
|
|
|
|
2018-07-13 04:30:36 +08:00
|
|
|
if (el.type == GraphicElement::G_LINE) {
|
|
|
|
PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
|
|
|
|
.build(out);
|
|
|
|
}
|
2018-06-11 01:56:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-13 17:03:09 +08:00
|
|
|
void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal)
|
|
|
|
{
|
|
|
|
const float scale = 1.0;
|
|
|
|
float offsetX = 0.0, offsetY = 0.0;
|
|
|
|
|
|
|
|
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:
|
|
|
|
line.build(out[0]);
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_HIDDEN:
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_INACTIVE:
|
|
|
|
line.build(out[2]);
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_ACTIVE:
|
|
|
|
line.build(out[3]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (el.type == GraphicElement::G_LINE) {
|
|
|
|
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:
|
|
|
|
line.build(out[0]);
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_HIDDEN:
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_INACTIVE:
|
|
|
|
line.build(out[2]);
|
|
|
|
break;
|
|
|
|
case GraphicElement::G_ACTIVE:
|
|
|
|
line.build(out[3]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
float thick1Px = mouseToWorldCoordinates(1, 0).x();
|
|
|
|
float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
// Draw grid.
|
2018-07-13 03:24:00 +08:00
|
|
|
auto grid = LineShaderData(thick1Px, gridColor);
|
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-06-22 02:12:20 +08:00
|
|
|
lineShader_.draw(grid, matrix);
|
2018-06-06 03:03:06 +08:00
|
|
|
|
2018-07-13 17:03:09 +08:00
|
|
|
LineShaderData shaders[4] = {LineShaderData(thick11Px, gFrameColor), // GraphicElement::G_FRAME
|
|
|
|
LineShaderData(thick11Px, gHiddenColor), // GraphicElement::G_HIDDEN
|
|
|
|
LineShaderData(thick11Px, gInactiveColor), // GraphicElement::G_INACTIVE
|
|
|
|
LineShaderData(thick11Px, gActiveColor)}; // GraphicElement::G_ACTIVE
|
|
|
|
|
2018-06-29 00:06:31 +08:00
|
|
|
if (ctx_) {
|
2018-07-13 17:03:09 +08:00
|
|
|
// Draw Bels.
|
2018-06-26 21:47:22 +08:00
|
|
|
for (auto bel : ctx_->getBels()) {
|
2018-07-13 17:03:09 +08:00
|
|
|
drawDecal(shaders, ctx_->getBelDecal(bel));
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
2018-07-13 17:03:09 +08:00
|
|
|
// Draw Wires.
|
2018-07-11 20:03:23 +08:00
|
|
|
for (auto wire : ctx_->getWires()) {
|
2018-07-13 17:03:09 +08:00
|
|
|
drawDecal(shaders, ctx_->getWireDecal(wire));
|
2018-07-11 20:03:23 +08:00
|
|
|
}
|
2018-07-13 17:03:09 +08:00
|
|
|
// Draw Pips.
|
2018-07-13 04:30:36 +08:00
|
|
|
for (auto pip : ctx_->getPips()) {
|
2018-07-13 17:03:09 +08:00
|
|
|
drawDecal(shaders, ctx_->getPipDecal(pip));
|
2018-07-11 20:03:23 +08:00
|
|
|
}
|
2018-07-13 17:03:09 +08:00
|
|
|
// Draw Groups.
|
2018-07-12 23:22:29 +08:00
|
|
|
for (auto group : ctx_->getGroups()) {
|
2018-07-13 17:03:09 +08:00
|
|
|
drawDecal(shaders, ctx_->getGroupDecal(group));
|
2018-07-12 23:22:29 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-13 17:03:09 +08:00
|
|
|
lineShader_.draw(shaders[0], matrix);
|
|
|
|
lineShader_.draw(shaders[1], matrix);
|
|
|
|
lineShader_.draw(shaders[2], matrix);
|
|
|
|
lineShader_.draw(shaders[3], matrix);
|
2018-07-12 23:22:29 +08:00
|
|
|
|
2018-06-22 02:12:20 +08:00
|
|
|
// Draw Frame Graphics.
|
2018-07-13 03:24:00 +08:00
|
|
|
auto frames = LineShaderData(thick11Px, frameColor);
|
2018-06-29 00:06:31 +08:00
|
|
|
if (ctx_) {
|
2018-07-13 04:30:36 +08:00
|
|
|
drawDecal(frames, ctx_->getFrameDecal());
|
2018-07-05 16:33:11 +08:00
|
|
|
lineShader_.draw(frames, matrix);
|
2018-06-22 02:12:20 +08:00
|
|
|
}
|
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-13 04:04:13 +08:00
|
|
|
void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { lastPos_ = event->pos(); }
|
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)
|
|
|
|
{
|
|
|
|
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-06-22 02:12:20 +08:00
|
|
|
const int dx = event->x() - lastPos_.x();
|
|
|
|
const int dy = event->y() - lastPos_.y();
|
2018-07-13 03:22:53 +08:00
|
|
|
lastPos_ = event->pos();
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
auto world = mouseToWorldCoordinates(dx, dy);
|
|
|
|
viewMove_.translate(world.x(), -world.y());
|
2018-06-22 02:12:20 +08:00
|
|
|
|
2018-07-13 03:22:53 +08:00
|
|
|
update();
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
|
|
|
QPoint degree = event->angleDelta() / 8;
|
|
|
|
|
2018-06-07 04:53:52 +08:00
|
|
|
if (!degree.isNull()) {
|
2018-07-13 03:22:53 +08:00
|
|
|
|
|
|
|
if (zoom_ < zoomNear_) {
|
|
|
|
zoom_ = zoomNear_;
|
|
|
|
} else if (zoom_ < zoomLvl1_) {
|
|
|
|
zoom_ -= degree.y() / 10.0;
|
|
|
|
} else if (zoom_ < zoomLvl2_) {
|
|
|
|
zoom_ -= degree.y() / 5.0;
|
|
|
|
} else if (zoom_ < zoomFar_) {
|
|
|
|
zoom_ -= degree.y();
|
|
|
|
} else {
|
|
|
|
zoom_ = zoomFar_;
|
|
|
|
}
|
|
|
|
update();
|
2018-06-06 03:03:06 +08:00
|
|
|
}
|
|
|
|
}
|
2018-06-22 02:12:20 +08:00
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|