Implement 1st order item positioning
Adding item connections between items that have not been referenced before, now positions the items successfully
MODIFY
src/graphics/connection.cpp
src/graphics/connection.cpp
@@ -45,15 +45,13 @@ void Connection::paint(QPainter *painter,
// Draw destination arrow
double angle = ::acos(line.dx() / line.length());
- QPointF destArrowP1 =
- destPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize,
+ QPointF destArrowP1 = destPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize,
cos(angle - Pi / 3) * m_arrowSize);
QPointF destArrowP2 =
destPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize,
cos(angle - Pi + Pi / 3) * m_arrowSize);
painter->setBrush(Qt::black);
- painter->drawPolygon(QPolygonF()
- << destPoint << destArrowP1 << destArrowP2);
+ painter->drawPolygon(QPolygonF() << destPoint << destArrowP1 << destArrowP2);
}
} // namespace Graphics

MODIFY
src/graphics/connection.h
src/graphics/connection.h
#include <QGraphicsItem>
+#include "pipelinewidget.h"
+
namespace Graphics {
class Shape;
@@ -27,6 +29,8 @@ class Connection : public QGraphicsItem {
QPair<Shape *, Shape *> getShapes() const {
return QPair<Shape *, Shape *>(m_source, m_dest);
}
+ static int connectionType() { return QGraphicsItem::UserType + 1; }
+ int type() const { return connectionType(); }
protected:
QRectF boundingRect() const override;

MODIFY
src/graphics/pipelinewidget.cpp
src/graphics/pipelinewidget.cpp
#include "connection.h"
#include "shape.h"
+#include <QDebug>
+#include <QRectF>
+
+#include <QCoreApplication>
#include <QWheelEvent>
PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
@@ -15,8 +19,8 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
// ------------ Create pipeline objects ---------------
// Registers memory
- Graphics::Shape* registers = new Graphics::Shape(
- Graphics::ShapeType::Block, Graphics::Stage::ID, 10, 15);
+ Graphics::Shape* registers = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::ID, 10, 15);
registers->addInput(QStringList() << "Read\nregister 1"
<< "Read\nregister 2"
<< "Write\nregister"
@@ -26,8 +30,8 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
registers->setName("Registers");
// Data memory
- Graphics::Shape* data_mem = new Graphics::Shape(
- Graphics::ShapeType::Block, Graphics::Stage::MEM, 50, 5);
+ Graphics::Shape* data_mem = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::MEM, 50, 5);
data_mem->addInput(QStringList() << "Address"
<< "Write\ndata");
data_mem->addOutput(QStringList() << "Read\ndata");
@@ -87,25 +91,27 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
// State registers
Graphics::Shape* ifid = new Graphics::Shape(Graphics::ShapeType::Block,
- Graphics::Stage::ID, 150, 10);
+ Graphics::Stage::ID, 250, 10);
ifid->setName("IF/ID");
ifid->addInput("");
ifid->addOutput("");
Graphics::Shape* idex = new Graphics::Shape(Graphics::ShapeType::Block,
- Graphics::Stage::EX, 150, 10);
+ Graphics::Stage::EX, 250, 10);
idex->setName("ID/EX");
for (int i = 0; i < 6; i++) {
idex->addInput("");
idex->addOutput("");
}
+ // add a point at 0,0 for testing purposes
+ scene->addEllipse(0, 0, 5, 5, QPen(Qt::red));
+
// Add pipeline objects to graphics scene
scene->addItem(mux1);
scene->addItem(pc);
scene->addItem(instr_mem);
scene->addItem(alu1);
- /*
scene->addItem(ifid);
scene->addItem(registers);
scene->addItem(data_mem);
@@ -114,13 +120,12 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
scene->addItem(alu2);
scene->addItem(alu3);
scene->addItem(idex);
-*/
+
// Create connections
// Connections should be added in a left to right ordering!
createConnection(mux1, 0, pc, 0);
createConnection(pc, 0, instr_mem, 0);
createConnection(pc, 0, alu1, 0);
- /*
createConnection(instr_mem, 0, ifid, 0);
createConnection(ifid, 0, registers, 0);
createConnection(ifid, 0, registers, 1);
@@ -129,7 +134,6 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
createConnection(idex, 0, alu2, 0);
createConnection(idex, 1, alu3, 0);
createConnection(idex, 2, mux2, 0);
- */
// Adjust positioning of items in scene
adjustPositioning();
@@ -141,9 +145,33 @@ void PipelineWidget::createConnection(Graphics::Shape* source, int index1,
new Graphics::Connection(source, source->getOutputPoint(index1), dest,
dest->getInputPoint(index2));
m_connections.append(connection);
+ source->addConnection(connection);
+ dest->addConnection(connection);
scene()->addItem(connection);
}
+QList<QGraphicsItem*> PipelineWidget::filterAllowedItems(
+ Graphics::Shape* shape, QList<QGraphicsItem*> items) {
+ // Removes $shape and all of its connections from $items
+ auto returnList = items;
+ for (const auto& item : items) {
+ if (item->type() == Graphics::Connection::connectionType()) {
+ if (shape->isConnectedTo(
+ qgraphicsitem_cast<Graphics::Connection*>(item))) {
+ returnList.removeAll(item);
+ }
+ } else if (item->type() == Graphics::Shape::connectionType()) {
+ if (shape == item) {
+ // Remove item if it intersects with itself (this happens when we just
+ // check what is intersecting with a rectangle in the scene -ofcourse
+ // the item itself will intersect that rectangle)
+ returnList.removeAll(item);
+ }
+ }
+ }
+ return returnList;
+}
+
void PipelineWidget::adjustPositioning() {
QList<Graphics::Shape*> movedShapes;
const static int minimumLength = 40; // Minimum connection length
@@ -170,6 +198,7 @@ void PipelineWidget::adjustPositioning() {
// Move shapes
shapes.first->moveBy(-dx, -newY);
shapes.second->moveBy(dx, newY);
+ QCoreApplication::processEvents();
movedShapes.append(shapes.first);
movedShapes.append(shapes.second);
@@ -178,18 +207,58 @@ void PipelineWidget::adjustPositioning() {
} else if (movedShapes.contains(shapes.first) &&
!movedShapes.contains(shapes.second)) {
// First shape has been moved, but not the second
-
auto pointPair = connection->getPoints();
int newY = (pointPair.first.y() - pointPair.second.y());
int dx = 0;
- int diff = pointPair.second.x() - pointPair.first.x();
+ int diff =
+ pointPair.second.x() - shapes.first->sceneBoundingRect().right();
if (diff < minimumLength) {
dx = minimumLength - diff;
}
- // Move shape
+ // Move shape to the new position. Afterwards, process all pending events
+ // in the event loop - this will indirectly update the scene. We need to
+ // verify that the scene has been updated before checking intersecting
+ // items
+
shapes.second->moveBy(dx, newY);
- // shapes.second->calculatePoints();
+ QCoreApplication::processEvents();
+ auto newRect = shapes.second->sceneBoundingRect();
+ // Check for intersections with other shapes
+ auto intersectingItems =
+ items(newRect.toRect(), Qt::IntersectsItemBoundingRect);
+ intersectingItems = filterAllowedItems(shapes.second, intersectingItems);
+
+ if (intersectingItems.size() == 0) {
+ // Accept new position
movedShapes.append(shapes.second);
+ } else {
+ // Intersecting items detected. Move second shape in pair up or down,
+ // until the shapeMargin requirement is met
+ auto rectBelow = shapes.second->boundingRect().toRect();
+ auto rectAbove = shapes.second->boundingRect().toRect();
+ while (intersectingItems.size() != 0) {
+ auto intersectingRect = intersectingItems.first()->boundingRect();
+ // Translate the shapes rect to be below and above the intersecting
+ // item
+ rectBelow.translate(0, intersectingRect.bottom() + shapeMargin);
+ rectAbove.translate(0, intersectingRect.top() - shapeMargin);
+
+ // Initially, test with the rect that is closest to y=0
+ QPair<QRect, QRect> rectOrdered =
+ abs(rectBelow.y()) < abs(rectAbove.y())
+ ? QPair<QRect, QRect>(rectBelow, rectAbove)
+ : QPair<QRect, QRect>(rectAbove, rectBelow);
+ if (items(rectOrdered.first, Qt::IntersectsItemBoundingRect).size() ==
+ 0) {
+ shapes.second->setPos(rectOrdered.first.topLeft());
+ break;
+ } else if (items(rectOrdered.second, Qt::IntersectsItemBoundingRect)
+ .size() == 0) {
+ shapes.second->setPos(rectOrdered.second.topLeft());
+ break;
+ }
+ }
+ }
}
}
}

MODIFY
src/graphics/pipelinewidget.h
src/graphics/pipelinewidget.h
#ifndef PIPELINEWIDGET_H
#define PIPELINEWIDGET_H
+#include <QGraphicsItem>
#include <QGraphicsView>
namespace Graphics {
@@ -22,6 +23,9 @@ class PipelineWidget : public QGraphicsView {
void createConnection(Graphics::Shape* source, int index1,
Graphics::Shape* dest, int index2);
+ QList<QGraphicsItem*> filterAllowedItems(Graphics::Shape* shape,
+ QList<QGraphicsItem*> items);
+
qreal shapeMargin = 10; // Minimum distance between two shapes
QList<Graphics::Connection*> m_connections;

MODIFY
src/graphics/shape.cpp
src/graphics/shape.cpp
@@ -73,7 +73,6 @@ void Shape::calculatePoints() {
// --------- CONNECTION POINTS ------------
m_inputPoints.clear();
m_outputPoints.clear();
-
// All shapes have a top and bottom point
m_topPoint = m_rect.topLeft();
m_topPoint.setX(m_topPoint.x() + m_rect.width() / 2);
@@ -144,6 +143,10 @@ void Shape::setName(QString name) {
calculatePoints();
}
+bool Shape::isConnectedTo(Connection *connection) const {
+ return m_connections.contains(connection);
+}
+
QPainterPath Shape::drawALUPath(QRectF rect) const {
QPointF topLeft = rect.topLeft();
rect.moveTo(0, 0);
@@ -208,8 +211,8 @@ void Shape::paint(QPainter *painter,
// Draw output descriptors
for (int i = 0; i < m_outputs.size(); i++) {
- textRect = fontMetricsObject.boundingRect(QRect(0, 0, 200, 200), 0,
- m_outputs[i]);
+ textRect =
+ fontMetricsObject.boundingRect(QRect(0, 0, 200, 200), 0, m_outputs[i]);
textRect.moveTo(m_outputPoints[i]);
textRect.translate(-textRect.width() - sidePadding,
-textRect.height() / 2); // move text away from side

MODIFY
src/graphics/shape.h
src/graphics/shape.h
#include <QPainterPath>
#include <QPen>
+#include "pipelinewidget.h"
+
namespace Graphics {
+class Connection;
enum class ShapeType { Block, ALU, MUX };
enum class Stage { IF = 1, ID = 2, EX = 3, MEM = 4, WB = 5 };
@@ -24,12 +27,20 @@ class Shape : public QGraphicsItem {
void addOutput(QString output);
void addOutput(QStringList output);
void setName(QString name);
+ void addConnection(Connection* connection) {
+ m_connections.append(connection);
+ }
+
+ bool isConnectedTo(Connection* connection) const;
QPointF* getInputPoint(int index);
QPointF* getOutputPoint(int index);
void calculateRect();
void calculatePoints();
+ static int connectionType() { return QGraphicsItem::UserType + 2; }
+ int type() const { return connectionType(); }
+
private:
QRectF m_rect;
bool m_hasChanged = true; // Flag is set whenever the item has changed (ie.
@@ -52,6 +63,8 @@ class Shape : public QGraphicsItem {
QList<QPointF> m_inputPoints;
QList<QPointF> m_outputPoints;
+ QList<Connection*> m_connections;
+
// Top- and bottom center-points of the shape
// Can be used to affix labels to shapes in the graphics scene
QPointF m_topPoint;

Implement 1st order item positioning

Adding item connections between items that have not been referenced before, now positions the items successfully