Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr
This commit is contained in:
commit
6a783ef94f
@ -117,7 +117,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent)
|
|||||||
for (auto bel : ctx->getBels()) {
|
for (auto bel : ctx->getBels()) {
|
||||||
auto name = ctx->getBelName(bel);
|
auto name = ctx->getBelName(bel);
|
||||||
bel_items.append(
|
bel_items.append(
|
||||||
new BelTreeItem(name, ElementType::BEL, QString(name.c_str())));
|
new BelTreeItem(name, ElementType::BEL, QString(name.c_str(ctx))));
|
||||||
}
|
}
|
||||||
bel_root->addChildren(bel_items);
|
bel_root->addChildren(bel_items);
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent)
|
|||||||
for (auto wire : ctx->getWires()) {
|
for (auto wire : ctx->getWires()) {
|
||||||
auto name = ctx->getWireName(wire);
|
auto name = ctx->getWireName(wire);
|
||||||
wire_items.append(new WireTreeItem(name, ElementType::WIRE,
|
wire_items.append(new WireTreeItem(name, ElementType::WIRE,
|
||||||
QString(name.c_str())));
|
QString(name.c_str(ctx))));
|
||||||
}
|
}
|
||||||
wire_root->addChildren(wire_items);
|
wire_root->addChildren(wire_items);
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent)
|
|||||||
for (auto pip : ctx->getPips()) {
|
for (auto pip : ctx->getPips()) {
|
||||||
auto name = ctx->getPipName(pip);
|
auto name = ctx->getPipName(pip);
|
||||||
pip_items.append(
|
pip_items.append(
|
||||||
new PipTreeItem(name, ElementType::PIP, QString(name.c_str())));
|
new PipTreeItem(name, ElementType::PIP, QString(name.c_str(ctx))));
|
||||||
}
|
}
|
||||||
pip_root->addChildren(pip_items);
|
pip_root->addChildren(pip_items);
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ void DesignWidget::addProperty(QtVariantProperty *property, const QString &id)
|
|||||||
{
|
{
|
||||||
propertyToId[property] = id;
|
propertyToId[property] = id;
|
||||||
idToProperty[id] = property;
|
idToProperty[id] = property;
|
||||||
QtBrowserItem *item = propertyEditor->addProperty(property);
|
propertyEditor->addProperty(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesignWidget::clearProperties()
|
void DesignWidget::clearProperties()
|
||||||
@ -213,14 +213,14 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *item, int pos)
|
|||||||
|
|
||||||
QtVariantProperty *topItem =
|
QtVariantProperty *topItem =
|
||||||
variantManager->addProperty(QVariant::String, QString("Name"));
|
variantManager->addProperty(QVariant::String, QString("Name"));
|
||||||
topItem->setValue(QString(c.c_str()));
|
topItem->setValue(QString(c.c_str(ctx)));
|
||||||
addProperty(topItem, QString("Name"));
|
addProperty(topItem, QString("Name"));
|
||||||
} else if (type == ElementType::WIRE) {
|
} else if (type == ElementType::WIRE) {
|
||||||
IdString c = static_cast<WireTreeItem *>(item)->getData();
|
IdString c = static_cast<WireTreeItem *>(item)->getData();
|
||||||
|
|
||||||
QtVariantProperty *topItem =
|
QtVariantProperty *topItem =
|
||||||
variantManager->addProperty(QVariant::String, QString("Name"));
|
variantManager->addProperty(QVariant::String, QString("Name"));
|
||||||
topItem->setValue(QString(c.c_str()));
|
topItem->setValue(QString(c.c_str(ctx)));
|
||||||
addProperty(topItem, QString("Name"));
|
addProperty(topItem, QString("Name"));
|
||||||
|
|
||||||
} else if (type == ElementType::PIP) {
|
} else if (type == ElementType::PIP) {
|
||||||
@ -228,7 +228,7 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *item, int pos)
|
|||||||
|
|
||||||
QtVariantProperty *topItem =
|
QtVariantProperty *topItem =
|
||||||
variantManager->addProperty(QVariant::String, QString("Name"));
|
variantManager->addProperty(QVariant::String, QString("Name"));
|
||||||
topItem->setValue(QString(c.c_str()));
|
topItem->setValue(QString(c.c_str(ctx)));
|
||||||
addProperty(topItem, QString("Name"));
|
addProperty(topItem, QString("Name"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,221 @@
|
|||||||
#include "fpgaviewwidget.h"
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <math.h>
|
|
||||||
|
#include "fpgaviewwidget.h"
|
||||||
|
#include "log.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
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;
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
|
||||||
|
{
|
||||||
|
auto gl = QOpenGLContext::currentContext()->functions();
|
||||||
|
program_->bind();
|
||||||
|
|
||||||
|
program_->setUniformValue(uniforms_.projection, projection);
|
||||||
|
program_->setUniformValue(uniforms_.thickness, line.thickness);
|
||||||
|
program_->setUniformValue(uniforms_.color, line.color.r, line.color.g,
|
||||||
|
line.color.b, line.color.a);
|
||||||
|
|
||||||
|
gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
&line.vertices[0]);
|
||||||
|
gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
&line.normals[0]);
|
||||||
|
gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
&line.miters[0]);
|
||||||
|
|
||||||
|
gl->glEnableVertexAttribArray(0);
|
||||||
|
gl->glEnableVertexAttribArray(1);
|
||||||
|
gl->glEnableVertexAttribArray(2);
|
||||||
|
|
||||||
|
gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT,
|
||||||
|
&line.indices[0]);
|
||||||
|
|
||||||
|
gl->glDisableVertexAttribArray(2);
|
||||||
|
gl->glDisableVertexAttribArray(1);
|
||||||
|
gl->glDisableVertexAttribArray(0);
|
||||||
|
program_->release();
|
||||||
|
}
|
||||||
|
|
||||||
FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
||||||
: QOpenGLWidget(parent), m_xMove(0), m_yMove(0), m_zDistance(1.0)
|
: QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f),
|
||||||
|
lineShader_(this)
|
||||||
{
|
{
|
||||||
ctx = qobject_cast<BaseMainWindow *>(getMainWindow())->getContext();
|
ctx = qobject_cast<BaseMainWindow *>(getMainWindow())->getContext();
|
||||||
}
|
}
|
||||||
@ -31,139 +237,127 @@ QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); }
|
|||||||
|
|
||||||
void FPGAViewWidget::setXTranslation(float t_x)
|
void FPGAViewWidget::setXTranslation(float t_x)
|
||||||
{
|
{
|
||||||
if (t_x != m_xMove) {
|
if (t_x == moveX_)
|
||||||
m_xMove = t_x;
|
return;
|
||||||
update();
|
|
||||||
}
|
moveX_ = t_x;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::setYTranslation(float t_y)
|
void FPGAViewWidget::setYTranslation(float t_y)
|
||||||
{
|
{
|
||||||
if (t_y != m_yMove) {
|
if (t_y == moveY_)
|
||||||
m_yMove = t_y;
|
return;
|
||||||
update();
|
|
||||||
}
|
moveY_ = t_y;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::setZoom(float t_z)
|
void FPGAViewWidget::setZoom(float t_z)
|
||||||
{
|
{
|
||||||
if (t_z != m_zDistance) {
|
if (t_z == zoom_)
|
||||||
m_zDistance -= t_z;
|
return;
|
||||||
if (m_zDistance < 0.1f)
|
zoom_ = t_z;
|
||||||
m_zDistance = 0.1f;
|
|
||||||
if (m_zDistance > 10.0f)
|
|
||||||
m_zDistance = 10.0f;
|
|
||||||
|
|
||||||
update();
|
if (zoom_ < 1.0f)
|
||||||
}
|
zoom_ = 1.0f;
|
||||||
|
if (zoom_ > 100.f)
|
||||||
|
zoom_ = 100.0f;
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::initializeGL()
|
void FPGAViewWidget::initializeGL()
|
||||||
{
|
{
|
||||||
|
if (!lineShader_.compile()) {
|
||||||
|
log_error("Could not compile shader.\n");
|
||||||
|
}
|
||||||
initializeOpenGLFunctions();
|
initializeOpenGLFunctions();
|
||||||
glClearColor(1.0, 1.0, 1.0, 0.0);
|
glClearColor(1.0, 1.0, 1.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::drawElement(const GraphicElement &el)
|
void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el)
|
||||||
{
|
{
|
||||||
float scale = 1.0, offset = 0.0;
|
const float scale = 1.0, offset = 0.0;
|
||||||
|
|
||||||
if (el.type == GraphicElement::G_BOX) {
|
if (el.type == GraphicElement::G_BOX) {
|
||||||
glBegin(GL_LINES);
|
auto line = PolyLine(true);
|
||||||
glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f);
|
line.point(offset + scale * el.x1, offset + scale * el.y1);
|
||||||
glVertex3f((offset + scale * el.x2), (offset + scale * el.y1), 0.0f);
|
line.point(offset + scale * el.x2, offset + scale * el.y1);
|
||||||
|
line.point(offset + scale * el.x2, offset + scale * el.y2);
|
||||||
glVertex3f((offset + scale * el.x2), (offset + scale * el.y1), 0.0f);
|
line.point(offset + scale * el.x1, offset + scale * el.y2);
|
||||||
glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f);
|
line.build(out);
|
||||||
|
|
||||||
glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f);
|
|
||||||
glVertex3f((offset + scale * el.x1), (offset + scale * el.y2), 0.0f);
|
|
||||||
|
|
||||||
glVertex3f((offset + scale * el.x1), (offset + scale * el.y2), 0.0f);
|
|
||||||
glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f);
|
|
||||||
glEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type == GraphicElement::G_LINE) {
|
if (el.type == GraphicElement::G_LINE) {
|
||||||
glBegin(GL_LINES);
|
PolyLine(offset + scale * el.x1, offset + scale * el.y1,
|
||||||
glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f);
|
offset + scale * el.x2, offset + scale * el.y2)
|
||||||
glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f);
|
.build(out);
|
||||||
glEnd();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::paintGL()
|
void FPGAViewWidget::paintGL()
|
||||||
{
|
{
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
auto gl = QOpenGLContext::currentContext()->functions();
|
||||||
glLoadIdentity();
|
const qreal retinaScale = devicePixelRatio();
|
||||||
|
gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
||||||
|
gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glTranslatef(m_xMove, m_yMove, -10.0);
|
const float aspect = float(width()) / float(height());
|
||||||
glScalef(m_zDistance, m_zDistance, 0.0f);
|
|
||||||
|
|
||||||
// Grid
|
QMatrix4x4 matrix;
|
||||||
glColor3f(0.9, 0.9, 0.9);
|
matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f));
|
||||||
glBegin(GL_LINES);
|
matrix.translate(moveX_, moveY_, -0.5);
|
||||||
for (float i = -100; i <= 100; i += 0.1) {
|
matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0);
|
||||||
glVertex3f((float)i, -100.0f, 0.0f);
|
|
||||||
glVertex3f((float)i, 100.0f, 0.0f);
|
// Draw grid.
|
||||||
glVertex3f(-100.0f, (float)i, 0.0f);
|
auto grid = LineShaderData(0.01f, QColor("#DDD"));
|
||||||
glVertex3f(100.0f, (float)i, 0.0f);
|
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);
|
||||||
}
|
}
|
||||||
glColor3f(0.7, 0.7, 0.7);
|
lineShader_.draw(grid, matrix);
|
||||||
for (int i = -100; i <= 100; i += 1) {
|
|
||||||
glVertex3f((float)i, -100.0f, 0.0f);
|
|
||||||
glVertex3f((float)i, 100.0f, 0.0f);
|
|
||||||
glVertex3f(-100.0f, (float)i, 0.0f);
|
|
||||||
glVertex3f(100.0f, (float)i, 0.0f);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glColor3f(0.1, 0.1, 0.1);
|
// Draw Bels.
|
||||||
glLineWidth(0.1);
|
auto bels = LineShaderData(0.02f, QColor("#b000ba"));
|
||||||
// Draw Bels
|
|
||||||
for (auto bel : ctx->getBels()) {
|
for (auto bel : ctx->getBels()) {
|
||||||
for (auto &el : ctx->getBelGraphics(bel))
|
for (auto &el : ctx->getBelGraphics(bel))
|
||||||
drawElement(el);
|
drawElement(bels, el);
|
||||||
}
|
}
|
||||||
// Draw Frame Graphics
|
lineShader_.draw(bels, matrix);
|
||||||
for (auto &el : ctx->getFrameGraphics())
|
|
||||||
drawElement(el);
|
// Draw Frame Graphics.
|
||||||
|
auto frames = LineShaderData(0.02f, QColor("#0066ba"));
|
||||||
|
for (auto &el : ctx->getFrameGraphics()) {
|
||||||
|
drawElement(frames, el);
|
||||||
|
}
|
||||||
|
lineShader_.draw(frames, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::resizeGL(int width, int height)
|
void FPGAViewWidget::resizeGL(int width, int height) {}
|
||||||
{
|
|
||||||
m_windowWidth = width;
|
|
||||||
m_windowHeight = height;
|
|
||||||
glViewport(0, 0, m_windowWidth, m_windowHeight);
|
|
||||||
|
|
||||||
float aspect = width * 1.0 / height;
|
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
glOrtho(-1.0 * aspect, +1.0 * aspect, -1.0, +1.0, 1.0, 15.0);
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
|
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
m_lastPos = event->pos();
|
startDragX_ = moveX_;
|
||||||
|
startDragY_ = moveY_;
|
||||||
|
lastPos_ = event->pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
|
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
int dx = event->x() - m_lastPos.x();
|
const int dx = event->x() - lastPos_.x();
|
||||||
int dy = event->y() - m_lastPos.y();
|
const int dy = event->y() - lastPos_.y();
|
||||||
float dx_scale = dx * (1 / (float)640);
|
|
||||||
float dy_scale = -dy * (1 / (float)480);
|
|
||||||
|
|
||||||
if (event->buttons() & Qt::LeftButton) {
|
const qreal retinaScale = devicePixelRatio();
|
||||||
float xpos = m_xMove + dx_scale;
|
float aspect = float(width()) / float(height());
|
||||||
float ypos = m_yMove + dy_scale;
|
const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect);
|
||||||
if (m_xMove / m_zDistance <= 100.0 && m_xMove / m_zDistance >= -100.0)
|
const float dy_scale = dy * (1 / (float)height() * retinaScale);
|
||||||
setXTranslation(xpos);
|
|
||||||
if (m_yMove / m_zDistance <= 100.0 && m_yMove / m_zDistance >= -100.0)
|
float xpos = dx_scale + startDragX_;
|
||||||
setYTranslation(ypos);
|
float ypos = dy_scale + startDragY_;
|
||||||
}
|
|
||||||
m_lastPos = event->pos();
|
setXTranslation(xpos);
|
||||||
|
setYTranslation(ypos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
||||||
@ -171,8 +365,8 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
|||||||
QPoint degree = event->angleDelta() / 8;
|
QPoint degree = event->angleDelta() / 8;
|
||||||
|
|
||||||
if (!degree.isNull()) {
|
if (!degree.isNull()) {
|
||||||
QPoint step = degree / 15;
|
float steps = degree.y() / 15.0;
|
||||||
setZoom(step.y() * -0.1f);
|
setZoom(zoom_ + steps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MAPGLWIDGET_H
|
#ifndef MAPGLWIDGET_H
|
||||||
#define MAPGLWIDGET_H
|
#define MAPGLWIDGET_H
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
|
||||||
|
// directly.
|
||||||
|
struct Vertex2DPOD
|
||||||
|
{
|
||||||
|
GLfloat x;
|
||||||
|
GLfloat y;
|
||||||
|
|
||||||
|
Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// Vertex2DPOD is a structure of R, G, B, A values that can be passed to OpenGL
|
||||||
|
// directly.
|
||||||
|
struct ColorPOD
|
||||||
|
{
|
||||||
|
GLfloat r;
|
||||||
|
GLfloat g;
|
||||||
|
GLfloat b;
|
||||||
|
GLfloat a;
|
||||||
|
|
||||||
|
ColorPOD(GLfloat R, GLfloat G, GLfloat B, GLfloat A)
|
||||||
|
: r(R), g(G), b(B), a(A)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
ColorPOD(const QColor &color)
|
||||||
|
: r(color.redF()), g(color.greenF()), b(color.blueF()),
|
||||||
|
a(color.alphaF())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// LineShaderData is a built set of vertices that can be rendered by the
|
||||||
|
// LineShader.
|
||||||
|
// Each LineShaderData can have its' own color and thickness.
|
||||||
|
struct LineShaderData
|
||||||
|
{
|
||||||
|
std::vector<Vertex2DPOD> vertices;
|
||||||
|
std::vector<Vertex2DPOD> normals;
|
||||||
|
std::vector<GLfloat> miters;
|
||||||
|
std::vector<GLuint> indices;
|
||||||
|
|
||||||
|
GLfloat thickness;
|
||||||
|
ColorPOD color;
|
||||||
|
|
||||||
|
LineShaderData(GLfloat Thickness, QColor Color)
|
||||||
|
: thickness(Thickness), color(Color)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// PolyLine is a set of segments defined by points, that can be built to a
|
||||||
|
// ShaderLine for GPU rendering.
|
||||||
|
class PolyLine
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<QVector2D> points_;
|
||||||
|
bool closed_;
|
||||||
|
|
||||||
|
void buildPoint(LineShaderData *building, const QVector2D *prev,
|
||||||
|
const QVector2D *cur, const QVector2D *next) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Create an empty PolyLine.
|
||||||
|
PolyLine(bool closed = false) : closed_(closed) {}
|
||||||
|
|
||||||
|
// Create a non-closed polyline consisting of one segment.
|
||||||
|
PolyLine(float x0, float y0, float x1, float y1) : closed_(false)
|
||||||
|
{
|
||||||
|
point(x0, y0);
|
||||||
|
point(x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a point to the PolyLine.
|
||||||
|
void point(float x, float y) { points_.push_back(QVector2D(x, y)); }
|
||||||
|
|
||||||
|
// Built PolyLine to shader data.
|
||||||
|
void build(LineShaderData &target) const;
|
||||||
|
|
||||||
|
// Set whether line is closed (ie. a loop).
|
||||||
|
void setClosed(bool closed) { closed_ = closed; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// LineShader is an OpenGL shader program that renders LineShaderData on the
|
||||||
|
// GPU.
|
||||||
|
// The LineShader expects two vertices per line point. It will push those
|
||||||
|
// vertices along the given normal * miter. This is used to 'stretch' the line
|
||||||
|
// to be as wide as the given thickness. The normal and miter are calculated
|
||||||
|
// by the PolyLine build method in order to construct a constant thickness line
|
||||||
|
// with miter edge joints.
|
||||||
|
//
|
||||||
|
// +------+------+
|
||||||
|
//
|
||||||
|
// |
|
||||||
|
// PolyLine.build()
|
||||||
|
// |
|
||||||
|
// V
|
||||||
|
//
|
||||||
|
// ^ ^ ^
|
||||||
|
// | | | <--- normal vectors (x2, pointing in the same
|
||||||
|
// +/+----+/+----+/+ direction)
|
||||||
|
//
|
||||||
|
// |
|
||||||
|
// vertex shader
|
||||||
|
// |
|
||||||
|
// V
|
||||||
|
//
|
||||||
|
// +------+------+ ^ by normal * miter * thickness/2
|
||||||
|
// | | |
|
||||||
|
// +------+------+ V by normal * miter * thickness/2
|
||||||
|
//
|
||||||
|
// (miter is flipped for every second vertex generated)
|
||||||
|
class LineShader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
QObject *parent_;
|
||||||
|
QOpenGLShaderProgram *program_;
|
||||||
|
|
||||||
|
// GL attribute locations.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
// original position of line vertex
|
||||||
|
GLuint position;
|
||||||
|
// normal by which vertex should be translated
|
||||||
|
GLuint normal;
|
||||||
|
// scalar defining:
|
||||||
|
// - how stretched the normal vector should be to
|
||||||
|
// compensate for bends
|
||||||
|
// - which way the normal should be applied (+1 for one vertex, -1
|
||||||
|
// for the other)
|
||||||
|
GLuint miter;
|
||||||
|
} attributes_;
|
||||||
|
|
||||||
|
// GL uniform locations.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
// combines m/v/p matrix to apply
|
||||||
|
GLuint projection;
|
||||||
|
// desired thickness of line
|
||||||
|
GLuint thickness;
|
||||||
|
// color of line
|
||||||
|
GLuint color;
|
||||||
|
} uniforms_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LineShader(QObject *parent) : parent_(parent), program_(nullptr) {}
|
||||||
|
|
||||||
|
static constexpr const char *vertexShaderSource_ =
|
||||||
|
"attribute highp vec2 position;\n"
|
||||||
|
"attribute highp vec2 normal;\n"
|
||||||
|
"attribute highp float miter;\n"
|
||||||
|
"uniform highp float thickness;\n"
|
||||||
|
"uniform highp mat4 projection;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" vec2 p = position.xy + vec2(normal * thickness/2.0 / miter);\n"
|
||||||
|
" gl_Position = projection * vec4(p, 0.0, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static constexpr const char *fragmentShaderSource_ =
|
||||||
|
"uniform lowp vec4 color;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" gl_FragColor = color;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
// Must be called on initialization.
|
||||||
|
bool compile(void);
|
||||||
|
|
||||||
|
// Render a LineShaderData with a given M/V/P transformation.
|
||||||
|
void draw(const LineShaderData &data, const QMatrix4x4 &projection);
|
||||||
|
};
|
||||||
|
|
||||||
class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -35,16 +226,18 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||||
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||||
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
|
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
|
||||||
void drawElement(const GraphicElement &el);
|
void drawElement(LineShaderData &data, const GraphicElement &el);
|
||||||
QMainWindow *getMainWindow();
|
QMainWindow *getMainWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_windowWidth;
|
QPoint lastPos_;
|
||||||
int m_windowHeight;
|
float moveX_;
|
||||||
float m_xMove;
|
float moveY_;
|
||||||
float m_yMove;
|
float zoom_;
|
||||||
float m_zDistance;
|
LineShader lineShader_;
|
||||||
QPoint m_lastPos;
|
|
||||||
|
float startDragX_;
|
||||||
|
float startDragY_;
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ def init_tiletypes(device):
|
|||||||
num_tile_types = 10
|
num_tile_types = 10
|
||||||
else:
|
else:
|
||||||
num_tile_types = 5
|
num_tile_types = 5
|
||||||
tile_sizes = {_: (0, 0) for _ in range(num_tile_types)}
|
tile_sizes = {i: (0, 0) for i in range(num_tile_types)}
|
||||||
tile_bits = [[] for _ in range(num_tile_types)]
|
tile_bits = [[] for _ in range(num_tile_types)]
|
||||||
|
|
||||||
with open(sys.argv[1], "r") as f:
|
with open(sys.argv[1], "r") as f:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#ifdef MAIN_EXECUTABLE
|
#ifdef MAIN_EXECUTABLE
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QSurfaceFormat>
|
||||||
#include <boost/filesystem/convenience.hpp>
|
#include <boost/filesystem/convenience.hpp>
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
Loading…
Reference in New Issue
Block a user