Merge branch 'master' into q3k/clickity
This commit is contained in:
commit
940886f9fa
@ -351,7 +351,7 @@ int main(int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
case TOK_REF:
|
case TOK_REF:
|
||||||
if (s.tokenComments[i].empty())
|
if (s.tokenComments[i].empty())
|
||||||
printf("ref %s %s\n", labelNames[v].c_str());
|
printf("ref %s\n", labelNames[v].c_str());
|
||||||
else
|
else
|
||||||
printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
|
printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
|
||||||
break;
|
break;
|
||||||
|
@ -145,21 +145,25 @@ struct GraphicElement
|
|||||||
{
|
{
|
||||||
enum type_t
|
enum type_t
|
||||||
{
|
{
|
||||||
G_NONE,
|
TYPE_NONE,
|
||||||
G_LINE,
|
TYPE_LINE,
|
||||||
G_ARROW,
|
TYPE_ARROW,
|
||||||
G_BOX,
|
TYPE_BOX,
|
||||||
G_CIRCLE,
|
TYPE_CIRCLE,
|
||||||
G_LABEL
|
TYPE_LABEL,
|
||||||
} type = G_NONE;
|
|
||||||
|
TYPE_MAX
|
||||||
|
} type = TYPE_NONE;
|
||||||
|
|
||||||
enum style_t
|
enum style_t
|
||||||
{
|
{
|
||||||
G_FRAME, // Static "frame". Contrast between G_INACTIVE and G_ACTIVE
|
STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
|
||||||
G_HIDDEN, // Only display when object is selected or highlighted
|
STYLE_HIDDEN, // Only display when object is selected or highlighted
|
||||||
G_INACTIVE, // Render using low-contrast color
|
STYLE_INACTIVE, // Render using low-contrast color
|
||||||
G_ACTIVE, // Render using high-contast color
|
STYLE_ACTIVE, // Render using high-contast color
|
||||||
} style = G_FRAME;
|
|
||||||
|
STYLE_MAX
|
||||||
|
} style = STYLE_FRAME;
|
||||||
|
|
||||||
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
|
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
|
||||||
std::string text;
|
std::string text;
|
||||||
|
@ -31,220 +31,11 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
|
FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
|
||||||
const QVector2D *next) const
|
QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
|
||||||
{
|
lineShader_(this), zoom_(500.0f),
|
||||||
// buildPoint emits two vertices per line point, along with normals to move
|
rendererData_(new FPGAViewWidget::RendererData),
|
||||||
// them the right directio when rendering and miter to compensate for
|
rendererArgs_(new FPGAViewWidget::RendererArgs)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
colors_.background = QColor("#000000");
|
colors_.background = QColor("#000000");
|
||||||
colors_.grid = QColor("#333");
|
colors_.grid = QColor("#333");
|
||||||
@ -311,68 +102,49 @@ void FPGAViewWidget::initializeGL()
|
|||||||
0.0);
|
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;
|
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 offsetX = decal.x;
|
||||||
float offsetY = decal.y;
|
float offsetY = decal.y;
|
||||||
|
|
||||||
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
||||||
|
drawGraphicElement(out, el, offsetX, offsetY);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = decal.x;
|
||||||
float offsetX = 0.0, offsetY = 0.0;
|
float offsetY = decal.y;
|
||||||
|
|
||||||
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
||||||
offsetX = decal.x;
|
switch (el.style) {
|
||||||
offsetY = decal.y;
|
case GraphicElement::STYLE_FRAME:
|
||||||
|
case GraphicElement::STYLE_INACTIVE:
|
||||||
if (el.type == GraphicElement::G_BOX) {
|
case GraphicElement::STYLE_ACTIVE:
|
||||||
auto line = PolyLine(true);
|
drawGraphicElement(out[el.style], el, offsetX, offsetY);
|
||||||
line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
|
break;
|
||||||
line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
|
default:
|
||||||
line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,24 +174,28 @@ void FPGAViewWidget::paintGL()
|
|||||||
float thick1Px = mouseToWorldDimensions(1, 0).x();
|
float thick1Px = mouseToWorldDimensions(1, 0).x();
|
||||||
float thick11Px = mouseToWorldDimensions(1.1, 0).x();
|
float thick11Px = mouseToWorldDimensions(1.1, 0).x();
|
||||||
|
|
||||||
// Draw grid.
|
// Render grid.
|
||||||
auto grid = LineShaderData();
|
auto grid = LineShaderData();
|
||||||
for (float i = -100.0f; i < 100.0f; i += 1.0f) {
|
for (float i = -100.0f; i < 100.0f; i += 1.0f) {
|
||||||
PolyLine(-100.0f, i, 100.0f, i).build(grid);
|
PolyLine(-100.0f, i, 100.0f, i).build(grid);
|
||||||
PolyLine(i, -100.0f, i, 100.0f).build(grid);
|
PolyLine(i, -100.0f, i, 100.0f).build(grid);
|
||||||
}
|
}
|
||||||
|
// Draw grid.
|
||||||
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
|
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
|
||||||
|
|
||||||
rendererDataLock_.lock();
|
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++)
|
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();
|
rendererDataLock_.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,125 +206,151 @@ void FPGAViewWidget::renderLines(void)
|
|||||||
if (ctx_ == nullptr)
|
if (ctx_ == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ctx_->lock_ui();
|
// Data from Context needed to render all decals.
|
||||||
|
std::vector<DecalXY> belDecals;
|
||||||
// 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;
|
|
||||||
std::vector<DecalXY> wireDecals;
|
std::vector<DecalXY> wireDecals;
|
||||||
std::vector<DecalXY> pipDecals;
|
std::vector<DecalXY> pipDecals;
|
||||||
std::vector<DecalXY> groupDecals;
|
std::vector<DecalXY> groupDecals;
|
||||||
|
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);
|
||||||
|
|
||||||
if (decalsChanged) {
|
// For now, collapse any decal changes into change of all decals.
|
||||||
for (auto bel : ctx_->getBels()) {
|
// TODO(q3k): fix this
|
||||||
belDecals.push_back(std::pair<BelId, DecalXY>(bel, ctx_->getBelDecal(bel)));
|
if (ctx_->allUiReload) {
|
||||||
|
ctx_->allUiReload = false;
|
||||||
|
decalsChanged = true;
|
||||||
}
|
}
|
||||||
for (auto wire : ctx_->getWires()) {
|
if (ctx_->frameUiReload) {
|
||||||
wireDecals.push_back(ctx_->getWireDecal(wire));
|
ctx_->frameUiReload = false;
|
||||||
|
decalsChanged = true;
|
||||||
}
|
}
|
||||||
for (auto pip : ctx_->getPips()) {
|
if (ctx_->belUiReload.size() > 0) {
|
||||||
pipDecals.push_back(ctx_->getPipDecal(pip));
|
ctx_->belUiReload.clear();
|
||||||
|
decalsChanged = true;
|
||||||
}
|
}
|
||||||
for (auto group : ctx_->getGroups()) {
|
if (ctx_->wireUiReload.size() > 0) {
|
||||||
groupDecals.push_back(ctx_->getGroupDecal(group));
|
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();
|
// Arguments from the main UI thread on what we should render.
|
||||||
auto selectedItems = rendererArgs_->selectedItems;
|
std::vector<DecalXY> selectedDecals;
|
||||||
auto highlightedItems = rendererArgs_->highlightedItems;
|
std::vector<DecalXY> highlightedDecals[8];
|
||||||
auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
|
bool highlightedOrSelectedChanged;
|
||||||
rendererArgs_->highlightedOrSelectedChanged = false;
|
{
|
||||||
rendererArgsLock_.unlock();
|
// 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);
|
QuadTreeBels::BoundingBox globalBB = QuadTreeBels::BoundingBox(-1000, -1000, 1000, 1000);
|
||||||
|
|
||||||
|
// Render decals if necessary.
|
||||||
if (decalsChanged) {
|
if (decalsChanged) {
|
||||||
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
||||||
// Draw Bels.
|
// Draw Bels.
|
||||||
data->qtBels = std::unique_ptr<QuadTreeBels>(new QuadTreeBels(globalBB));
|
data->qtBels = std::unique_ptr<QuadTreeBels>(new QuadTreeBels(globalBB));
|
||||||
for (auto const &decal : belDecals) {
|
for (auto const &decal : belDecals) {
|
||||||
drawDecal(data->decals, decal.second);
|
drawArchDecal(data->gfxByStyle, decal);
|
||||||
commitToQuadtree(data->qtBels.get(), decal.second, decal.first);
|
|
||||||
}
|
}
|
||||||
// Draw Wires.
|
// Draw Wires.
|
||||||
for (auto const &decal : wireDecals) {
|
for (auto const &decal : wireDecals) {
|
||||||
drawDecal(data->decals, decal);
|
drawArchDecal(data->gfxByStyle, decal);
|
||||||
}
|
}
|
||||||
// Draw Pips.
|
// Draw Pips.
|
||||||
for (auto const &decal : pipDecals) {
|
for (auto const &decal : pipDecals) {
|
||||||
drawDecal(data->decals, decal);
|
drawArchDecal(data->gfxByStyle, decal);
|
||||||
}
|
}
|
||||||
// Draw Groups.
|
// Draw Groups.
|
||||||
for (auto const &decal : groupDecals) {
|
for (auto const &decal : groupDecals) {
|
||||||
drawDecal(data->decals, decal);
|
drawArchDecal(data->gfxByStyle, decal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap over.
|
// Swap over.
|
||||||
rendererDataLock_.lock();
|
{
|
||||||
rendererData_ = std::move(data);
|
QMutexLocker lock(&rendererDataLock_);
|
||||||
rendererDataLock_.unlock();
|
|
||||||
|
// 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 (highlightedOrSelectedChanged) {
|
||||||
if (decalsChanged || highlightedOrSelectedChanged) {
|
QMutexLocker locker(&rendererDataLock_);
|
||||||
rendererData_->selected.clear();
|
|
||||||
for (auto &decal : selectedItems) {
|
// Render selected.
|
||||||
drawDecal(rendererData_->selected, decal);
|
rendererData_->gfxSelected.clear();
|
||||||
|
for (auto &decal : selectedDecals) {
|
||||||
|
drawDecal(rendererData_->gfxSelected, decal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render highlighted.
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
rendererData_->highlighted[i].clear();
|
rendererData_->gfxHighlighted[i].clear();
|
||||||
for (auto &decal : highlightedItems[i]) {
|
for (auto &decal : highlightedDecals[i]) {
|
||||||
drawDecal(rendererData_->highlighted[i], decal);
|
drawDecal(rendererData_->gfxHighlighted[i], decal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rendererDataLock_.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
|
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
|
||||||
{
|
{
|
||||||
rendererArgsLock_.lock();
|
{
|
||||||
rendererArgs_->selectedItems = decals;
|
QMutexLocker locker(&rendererArgsLock_);
|
||||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
rendererArgs_->selectedDecals = decals;
|
||||||
rendererArgsLock_.unlock();
|
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||||
|
}
|
||||||
pokeRenderer();
|
pokeRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
|
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
|
||||||
{
|
{
|
||||||
rendererArgsLock_.lock();
|
{
|
||||||
rendererArgs_->highlightedItems[group] = decals;
|
QMutexLocker locker(&rendererArgsLock_);
|
||||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
rendererArgs_->highlightedDecals[group] = decals;
|
||||||
rendererArgsLock_.unlock();
|
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||||
|
}
|
||||||
pokeRenderer();
|
pokeRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +359,7 @@ void FPGAViewWidget::resizeGL(int width, int height) {}
|
|||||||
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
|
void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
||||||
lastPos_ = event->pos();
|
lastDragPos_ = event->pos();
|
||||||
}
|
}
|
||||||
if (event->buttons() & Qt::LeftButton) {
|
if (event->buttons() & Qt::LeftButton) {
|
||||||
int x = event->x();
|
int x = event->x();
|
||||||
@ -609,9 +411,9 @@ QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)
|
|||||||
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
|
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) {
|
||||||
const int dx = event->x() - lastPos_.x();
|
const int dx = event->x() - lastDragPos_.x();
|
||||||
const int dy = event->y() - lastPos_.y();
|
const int dy = event->y() - lastDragPos_.y();
|
||||||
lastPos_ = event->pos();
|
lastDragPos_ = event->pos();
|
||||||
|
|
||||||
auto world = mouseToWorldDimensions(dx, dy);
|
auto world = mouseToWorldDimensions(dx, dy);
|
||||||
viewMove_.translate(world.x(), -world.y());
|
viewMove_.translate(world.x(), -world.y());
|
||||||
|
@ -34,183 +34,10 @@
|
|||||||
|
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "quadtree.h"
|
#include "quadtree.h"
|
||||||
|
#include "lineshader.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
|
|
||||||
// directly.
|
|
||||||
NPNR_PACKED_STRUCT(struct Vertex2DPOD {
|
|
||||||
GLfloat x;
|
|
||||||
GLfloat y;
|
|
||||||
|
|
||||||
Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
LineShaderData(void) {}
|
|
||||||
|
|
||||||
void clear(void)
|
|
||||||
{
|
|
||||||
vertices.clear();
|
|
||||||
normals.clear();
|
|
||||||
miters.clear();
|
|
||||||
indices.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 buffers
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
QOpenGLBuffer position;
|
|
||||||
QOpenGLBuffer normal;
|
|
||||||
QOpenGLBuffer miter;
|
|
||||||
QOpenGLBuffer index;
|
|
||||||
} buffers_;
|
|
||||||
|
|
||||||
// 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_;
|
|
||||||
|
|
||||||
QOpenGLVertexArrayObject vao_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
LineShader(QObject *parent) : parent_(parent), program_(nullptr)
|
|
||||||
{
|
|
||||||
buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
|
||||||
buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
||||||
|
|
||||||
buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
|
||||||
buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
||||||
|
|
||||||
buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
|
||||||
buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
||||||
|
|
||||||
buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
|
|
||||||
buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr const char *vertexShaderSource_ =
|
|
||||||
"#version 110\n"
|
|
||||||
"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_ = "#version 110\n"
|
|
||||||
"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 QColor &color, float thickness, const QMatrix4x4 &projection);
|
|
||||||
};
|
|
||||||
|
|
||||||
class PeriodicRunner : public QThread
|
class PeriodicRunner : public QThread
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -268,18 +95,17 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
FPGAViewWidget(QWidget *parent = 0);
|
FPGAViewWidget(QWidget *parent = 0);
|
||||||
~FPGAViewWidget();
|
~FPGAViewWidget();
|
||||||
|
|
||||||
QSize minimumSizeHint() const override;
|
|
||||||
QSize sizeHint() const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Qt callbacks.
|
||||||
void initializeGL() Q_DECL_OVERRIDE;
|
void initializeGL() Q_DECL_OVERRIDE;
|
||||||
void paintGL() Q_DECL_OVERRIDE;
|
void paintGL() Q_DECL_OVERRIDE;
|
||||||
void resizeGL(int width, int height) Q_DECL_OVERRIDE;
|
void resizeGL(int width, int height) Q_DECL_OVERRIDE;
|
||||||
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 drawDecal(LineShaderData &data, const DecalXY &decal);
|
QSize minimumSizeHint() const override;
|
||||||
void drawDecal(LineShaderData out[], const DecalXY &decal);
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void newContext(Context *ctx);
|
void newContext(Context *ctx);
|
||||||
@ -295,26 +121,13 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
void clickedBel(BelId bel);
|
void clickedBel(BelId bel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void renderLines(void);
|
|
||||||
void zoom(int level);
|
|
||||||
|
|
||||||
QPoint lastPos_;
|
|
||||||
LineShader lineShader_;
|
|
||||||
QMatrix4x4 viewMove_;
|
|
||||||
float zoom_;
|
|
||||||
QMatrix4x4 getProjection(void);
|
|
||||||
QVector4D mouseToWorldCoordinates(int x, int y);
|
|
||||||
QVector4D mouseToWorldDimensions(int x, int y);
|
|
||||||
|
|
||||||
const float zoomNear_ = 1.0f; // do not zoom closer than this
|
const float zoomNear_ = 1.0f; // do not zoom closer than this
|
||||||
const float zoomFar_ = 10000.0f; // do not zoom further than this
|
const float zoomFar_ = 10000.0f; // do not zoom further than this
|
||||||
|
|
||||||
const float zoomLvl1_ = 100.0f;
|
const float zoomLvl1_ = 100.0f;
|
||||||
const float zoomLvl2_ = 50.0f;
|
const float zoomLvl2_ = 50.0f;
|
||||||
|
|
||||||
Context *ctx_;
|
Context *ctx_;
|
||||||
QTimer paintTimer_;
|
QTimer paintTimer_;
|
||||||
|
|
||||||
std::unique_ptr<PeriodicRunner> renderRunner_;
|
std::unique_ptr<PeriodicRunner> renderRunner_;
|
||||||
|
|
||||||
using QuadTreeBels = QuadTree<float, BelId>;
|
using QuadTreeBels = QuadTree<float, BelId>;
|
||||||
@ -326,12 +139,17 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
float offsetY = decal.y;
|
float offsetY = decal.y;
|
||||||
|
|
||||||
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
||||||
if (el.type == GraphicElement::G_BOX) {
|
if (el.type == GraphicElement::TYPE_BOX) {
|
||||||
tree->insert(typename T::BoundingBox(offsetX + el.x1, offsetY + el.y1, offsetX + el.x2, offsetY + el.y2), bel);
|
tree->insert(typename T::BoundingBox(offsetX + el.x1, offsetY + el.y1, offsetX + el.x2, offsetY + el.y2), bel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPoint lastDragPos_;
|
||||||
|
LineShader lineShader_;
|
||||||
|
QMatrix4x4 viewMove_;
|
||||||
|
float zoom_;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
QColor background;
|
QColor background;
|
||||||
@ -346,23 +164,31 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
|||||||
|
|
||||||
struct RendererData
|
struct RendererData
|
||||||
{
|
{
|
||||||
LineShaderData decals[4];
|
LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
|
||||||
LineShaderData selected;
|
LineShaderData gfxSelected;
|
||||||
LineShaderData highlighted[8];
|
LineShaderData gfxHighlighted[8];
|
||||||
std::unique_ptr<QuadTreeBels> qtBels;
|
std::unique_ptr<QuadTreeBels> qtBels;
|
||||||
};
|
};
|
||||||
|
std::unique_ptr<RendererData> rendererData_;
|
||||||
|
QMutex rendererDataLock_;
|
||||||
|
|
||||||
struct RendererArgs
|
struct RendererArgs
|
||||||
{
|
{
|
||||||
std::vector<DecalXY> selectedItems;
|
std::vector<DecalXY> selectedDecals;
|
||||||
std::vector<DecalXY> highlightedItems[8];
|
std::vector<DecalXY> highlightedDecals[8];
|
||||||
bool highlightedOrSelectedChanged;
|
bool highlightedOrSelectedChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<RendererData> rendererData_;
|
|
||||||
QMutex rendererDataLock_;
|
|
||||||
std::unique_ptr<RendererArgs> rendererArgs_;
|
std::unique_ptr<RendererArgs> rendererArgs_;
|
||||||
QMutex rendererArgsLock_;
|
QMutex rendererArgsLock_;
|
||||||
|
|
||||||
|
void zoom(int level);
|
||||||
|
void renderLines(void);
|
||||||
|
void drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y);
|
||||||
|
void drawDecal(LineShaderData &out, const DecalXY &decal);
|
||||||
|
void drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal);
|
||||||
|
QVector4D mouseToWorldCoordinates(int x, int y);
|
||||||
|
QVector4D mouseToWorldDimensions(int x, int y);
|
||||||
|
QMatrix4x4 getProjection(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
236
gui/lineshader.cc
Normal file
236
gui/lineshader.cc
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* 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 "log.h"
|
||||||
|
#include "lineshader.h"
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
209
gui/lineshader.h
Normal file
209
gui/lineshader.h
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* 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 LINESHADER_H
|
||||||
|
#define LINESHADER_H
|
||||||
|
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
|
||||||
|
// directly.
|
||||||
|
NPNR_PACKED_STRUCT(struct Vertex2DPOD {
|
||||||
|
GLfloat x;
|
||||||
|
GLfloat y;
|
||||||
|
|
||||||
|
Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
LineShaderData(void) {}
|
||||||
|
|
||||||
|
void clear(void)
|
||||||
|
{
|
||||||
|
vertices.clear();
|
||||||
|
normals.clear();
|
||||||
|
miters.clear();
|
||||||
|
indices.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 buffers
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
QOpenGLBuffer position;
|
||||||
|
QOpenGLBuffer normal;
|
||||||
|
QOpenGLBuffer miter;
|
||||||
|
QOpenGLBuffer index;
|
||||||
|
} buffers_;
|
||||||
|
|
||||||
|
// 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_;
|
||||||
|
|
||||||
|
QOpenGLVertexArrayObject vao_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LineShader(QObject *parent) : parent_(parent), program_(nullptr)
|
||||||
|
{
|
||||||
|
buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
||||||
|
buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
|
||||||
|
buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
||||||
|
buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
|
||||||
|
buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
|
||||||
|
buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
|
||||||
|
buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
|
||||||
|
buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr const char *vertexShaderSource_ =
|
||||||
|
"#version 110\n"
|
||||||
|
"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_ = "#version 110\n"
|
||||||
|
"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 QColor &color, float thickness, const QMatrix4x4 &projection);
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
@ -654,8 +654,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (type == GroupId::TYPE_FRAME) {
|
if (type == GroupId::TYPE_FRAME) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_LINE;
|
el.type = GraphicElement::TYPE_LINE;
|
||||||
el.style = GraphicElement::G_FRAME;
|
el.style = GraphicElement::STYLE_FRAME;
|
||||||
|
|
||||||
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;
|
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;
|
||||||
ret.push_back(el);
|
ret.push_back(el);
|
||||||
@ -680,8 +680,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (type == GroupId::TYPE_MAIN_SW) {
|
if (type == GroupId::TYPE_MAIN_SW) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = GraphicElement::G_FRAME;
|
el.style = GraphicElement::STYLE_FRAME;
|
||||||
|
|
||||||
el.x1 = x + main_swbox_x1;
|
el.x1 = x + main_swbox_x1;
|
||||||
el.x2 = x + main_swbox_x2;
|
el.x2 = x + main_swbox_x2;
|
||||||
@ -692,8 +692,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (type == GroupId::TYPE_LOCAL_SW) {
|
if (type == GroupId::TYPE_LOCAL_SW) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = GraphicElement::G_FRAME;
|
el.style = GraphicElement::STYLE_FRAME;
|
||||||
|
|
||||||
el.x1 = x + local_swbox_x1;
|
el.x1 = x + local_swbox_x1;
|
||||||
el.x2 = x + local_swbox_x2;
|
el.x2 = x + local_swbox_x2;
|
||||||
@ -707,7 +707,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
int n = chip_info->wire_data[decal.index].num_segments;
|
int n = chip_info->wire_data[decal.index].num_segments;
|
||||||
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get();
|
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get();
|
||||||
|
|
||||||
GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
|
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
|
||||||
@ -715,7 +715,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (decal.type == DecalId::TYPE_PIP) {
|
if (decal.type == DecalId::TYPE_PIP) {
|
||||||
const PipInfoPOD &p = chip_info->pip_data[decal.index];
|
const PipInfoPOD &p = chip_info->pip_data[decal.index];
|
||||||
GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_HIDDEN;
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
|
||||||
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
|
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,8 +727,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (bel_type == TYPE_ICESTORM_LC) {
|
if (bel_type == TYPE_ICESTORM_LC) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
||||||
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
||||||
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
||||||
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
||||||
@ -740,8 +740,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
|
|
||||||
if (bel_type == TYPE_SB_IO) {
|
if (bel_type == TYPE_SB_IO) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
||||||
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
||||||
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
||||||
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
||||||
@ -754,8 +754,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
if (bel_type == TYPE_ICESTORM_RAM) {
|
if (bel_type == TYPE_ICESTORM_RAM) {
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
||||||
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
||||||
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
||||||
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
|
||||||
|
@ -24,7 +24,7 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
|
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
|
||||||
{
|
{
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_LINE;
|
el.type = GraphicElement::TYPE_LINE;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
|
|
||||||
// Horizontal Span-4 Wires
|
// Horizontal Span-4 Wires
|
||||||
@ -647,7 +647,7 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl
|
|||||||
float ty = 0.5 * (y1 + y2);
|
float ty = 0.5 * (y1 + y2);
|
||||||
|
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_ARROW;
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
|
|
||||||
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
|
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
|
||||||
@ -704,7 +704,7 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
|
|||||||
|
|
||||||
if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
|
if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_ARROW;
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
||||||
el.x2 = el.x1;
|
el.x2 = el.x1;
|
||||||
|
@ -52,13 +52,13 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
|
|||||||
const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
|
const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
|
||||||
|
|
||||||
for (auto &el : ctx->getDecalGraphics(decal.decal)) {
|
for (auto &el : ctx->getDecalGraphics(decal.decal)) {
|
||||||
if (el.type == GraphicElement::G_BOX) {
|
if (el.type == GraphicElement::TYPE_BOX) {
|
||||||
std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
|
std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
|
||||||
<< (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))
|
<< (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))
|
||||||
<< "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n";
|
<< "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type == GraphicElement::G_LINE) {
|
if (el.type == GraphicElement::TYPE_LINE) {
|
||||||
std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
|
std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
|
||||||
<< (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))
|
<< (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))
|
||||||
<< "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";
|
<< "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";
|
||||||
|
Loading…
Reference in New Issue
Block a user