Work on item positioning algorithm
MODIFY
src/graphics/connection.cpp
src/graphics/connection.cpp
namespace Graphics {
static const float Pi = 3.1415926535;
-Connection::Connection(Shape *source, QPointF sourcePoint, Shape *dest,
- QPointF destPoint) {
- m_sourcePoint = mapFromItem(source, sourcePoint);
- m_destPoint = mapFromItem(dest, destPoint);
-}
+Connection::Connection(Shape *source, QPointF *sourcePoint, Shape *dest,
+ QPointF *destPoint)
+ : m_source(source),
+ m_dest(dest),
+ m_sourcePointPtr(sourcePoint),
+ m_destPointPtr(destPoint) {}
QRectF Connection::boundingRect() const {
qreal penWidth = 1;
qreal extra = (penWidth + m_arrowSize) / 2.0;
- return QRectF(m_sourcePoint, QSizeF(m_destPoint.x() - m_sourcePoint.x(),
- m_destPoint.y() - m_sourcePoint.y()))
+ QPointF sourcePoint = mapFromItem(m_source, *m_sourcePointPtr);
+ QPointF destPoint = mapFromItem(m_dest, *m_destPointPtr);
+
+ return QRectF(sourcePoint, QSizeF(destPoint.x() - sourcePoint.x(),
+ destPoint.y() - sourcePoint.y()))
.normalized()
.adjusted(-extra, -extra, extra, extra);
}
+QPair<QPointF, QPointF> Connection::getPoints() const {
+ return QPair<QPointF, QPointF>(mapFromItem(m_source, *m_sourcePointPtr),
+ mapFromItem(m_dest, *m_destPointPtr));
+}
+
void Connection::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget) {
- QLineF line(m_sourcePoint, m_destPoint);
+ QPointF sourcePoint = mapFromItem(m_source, *m_sourcePointPtr);
+ QPointF destPoint = mapFromItem(m_dest, *m_destPointPtr);
+
+ QLineF line(sourcePoint, destPoint);
painter->setPen(
QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
@@ -34,14 +46,14 @@ void Connection::paint(QPainter *painter,
// Draw destination arrow
double angle = ::acos(line.dx() / line.length());
QPointF destArrowP1 =
- m_destPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize,
+ destPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize,
cos(angle - Pi / 3) * m_arrowSize);
QPointF destArrowP2 =
- m_destPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize,
+ destPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize,
cos(angle - Pi + Pi / 3) * m_arrowSize);
painter->setBrush(Qt::black);
painter->drawPolygon(QPolygonF()
- << m_destPoint << destArrowP1 << destArrowP2);
+ << destPoint << destArrowP1 << destArrowP2);
}
} // namespace Graphics

MODIFY
src/graphics/connection.h
src/graphics/connection.h
@@ -14,8 +14,8 @@ enum class ValueDrawPos { Source, Middle, Destination };
*/
class Connection : public QGraphicsItem {
public:
- Connection(Shape *source, QPointF sourcePoint, Shape *dest,
- QPointF destPoint);
+ Connection(Shape *source, QPointF *sourcePoint, Shape *dest,
+ QPointF *destPoint);
void setValueDrawPos(ValueDrawPos pos) { m_valuePos = pos, update(); }
void setValue(int value) { m_value = value; }
@@ -23,6 +23,10 @@ class Connection : public QGraphicsItem {
m_showValue = state;
update();
}
+ QPair<QPointF, QPointF> getPoints() const;
+ QPair<Shape *, Shape *> getShapes() const {
+ return QPair<Shape *, Shape *>(m_source, m_dest);
+ }
protected:
QRectF boundingRect() const override;
@@ -40,8 +44,11 @@ class Connection : public QGraphicsItem {
ValueDrawPos m_valuePos = ValueDrawPos::Source;
int m_arrowSize = 10;
- QPointF m_sourcePoint;
- QPointF m_destPoint;
+
+ Shape *m_source;
+ Shape *m_dest;
+ QPointF *m_sourcePointPtr;
+ QPointF *m_destPointPtr;
};
} // namespace Graphics

MODIFY
src/graphics/pipelinewidget.cpp
src/graphics/pipelinewidget.cpp
@@ -14,9 +14,9 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
setDragMode(QGraphicsView::ScrollHandDrag);
// ------------ Create pipeline objects ---------------
- // Instruction memory
- Graphics::Shape* registers =
- new Graphics::Shape(Graphics::ShapeType::Block, 10, 15);
+ // Registers memory
+ 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,58 +26,172 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
registers->setName("Registers");
// Data memory
- Graphics::Shape* data_mem =
- new Graphics::Shape(Graphics::ShapeType::Block, 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");
data_mem->setName("Data\nmemory");
// Instruction memory
- Graphics::Shape* instr_mem =
- new Graphics::Shape(Graphics::ShapeType::Block, 30, 0);
+ Graphics::Shape* instr_mem = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::IF, 0, 0);
instr_mem->addInput(QStringList() << "Read\naddress");
instr_mem->addOutput(QStringList() << "Instruction");
instr_mem->setName("Instruction\nmemory");
// PC
- Graphics::Shape* pc = new Graphics::Shape(Graphics::ShapeType::Block, 30, 3);
+ Graphics::Shape* pc = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::IF, 30, 3);
pc->addInput("");
pc->addOutput("");
pc->setName("PC");
- // MUX
- Graphics::Shape* mux1 = new Graphics::Shape(Graphics::ShapeType::MUX, 20, 8);
+ // MUXes
+ Graphics::Shape* mux1 =
+ new Graphics::Shape(Graphics::ShapeType::MUX, Graphics::Stage::IF, 20, 8);
mux1->addInput(QStringList() << "0"
- << "1"
- << "2");
- mux1->addOutput("C");
+ << "1");
+ mux1->addOutput("");
mux1->setName("M\nu\nx");
- // ALU
- Graphics::Shape* ALU1 = new Graphics::Shape(Graphics::ShapeType::ALU, 70, 30);
- ALU1->setName("Add");
- ALU1->addOutput("Sum");
+ Graphics::Shape* mux2 =
+ new Graphics::Shape(Graphics::ShapeType::MUX, Graphics::Stage::EX, 20, 8);
+ mux2->addInput(QStringList() << "0"
+ << "1");
+ mux2->addOutput("");
+ mux2->setName("M\nu\nx");
+
+ Graphics::Shape* mux3 =
+ new Graphics::Shape(Graphics::ShapeType::MUX, Graphics::Stage::WB, 20, 8);
+ mux3->addInput(QStringList() << "0"
+ << "1");
+ mux3->addOutput("");
+ mux3->setName("M\nu\nx");
+
+ // ALUs
+ Graphics::Shape* alu1 = new Graphics::Shape(Graphics::ShapeType::ALU,
+ Graphics::Stage::ID, 70, 30);
+ alu1->setName("Add");
+ alu1->addOutput("Sum");
+
+ Graphics::Shape* alu2 = new Graphics::Shape(Graphics::ShapeType::ALU,
+ Graphics::Stage::EX, 70, 30);
+ alu2->setName("Add");
+ alu2->addOutput("Sum");
+
+ Graphics::Shape* alu3 = new Graphics::Shape(Graphics::ShapeType::ALU,
+ Graphics::Stage::EX, 70, 30);
+ alu3->setName("Add");
+ alu3->addOutput("Sum");
+
+ // State registers
+ Graphics::Shape* ifid = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::ID, 150, 10);
+ ifid->setName("IF/ID");
+ ifid->addInput("");
+ ifid->addOutput("");
+
+ Graphics::Shape* idex = new Graphics::Shape(Graphics::ShapeType::Block,
+ Graphics::Stage::EX, 150, 10);
+ idex->setName("ID/EX");
+ for (int i = 0; i < 6; i++) {
+ idex->addInput("");
+ idex->addOutput("");
+ }
// 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);
- scene->addItem(instr_mem);
- scene->addItem(pc);
- scene->addItem(mux1);
- scene->addItem(ALU1);
+ scene->addItem(mux2);
+ scene->addItem(mux3);
+ 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);
+ createConnection(registers, 0, idex, 1);
+ createConnection(registers, 1, idex, 2);
+ createConnection(idex, 0, alu2, 0);
+ createConnection(idex, 1, alu3, 0);
+ createConnection(idex, 2, mux2, 0);
+ */
- // Set item positions
- pc->setPos(-250, -100);
- instr_mem->setPos(-300, 0);
- registers->setPos(0, 0);
- data_mem->setPos(200, 0);
- mux1->setPos(0, -200);
- ALU1->setPos(100, 200);
+ // Adjust positioning of items in scene
+ adjustPositioning();
+}
- // Create connections
- scene->addItem(new Graphics::Connection(
- registers, registers->getOutputPoint(1), mux1, mux1->getInputPoint(0)));
+void PipelineWidget::createConnection(Graphics::Shape* source, int index1,
+ Graphics::Shape* dest, int index2) {
+ Graphics::Connection* connection =
+ new Graphics::Connection(source, source->getOutputPoint(index1), dest,
+ dest->getInputPoint(index2));
+ m_connections.append(connection);
+ scene()->addItem(connection);
+}
+
+void PipelineWidget::adjustPositioning() {
+ QList<Graphics::Shape*> movedShapes;
+ const static int minimumLength = 40; // Minimum connection length
+ for (const auto& connection : m_connections) {
+ auto shapes = connection->getShapes();
+ if (!movedShapes.contains(shapes.first) &&
+ !movedShapes.contains(shapes.second)) {
+ // None of the shapes have been repositioned
+
+ // Step 1 - set source and destination points to equal y coord
+ auto pointPair = connection->getPoints();
+ int newY = (pointPair.first.y() - pointPair.second.y()) / 2;
+
+ // Step 2: set source and destination points to a sensible
+ // x position. Middle point +- minimumLength/2
+ // This also enforces that a source shape is positioned to the left
+ // of a destination shape
+ int dx = 0;
+ int diff = pointPair.second.x() - pointPair.first.x();
+ if (diff < minimumLength) {
+ dx = (minimumLength - diff) / 2;
+ }
+
+ // Move shapes
+ shapes.first->moveBy(-dx, -newY);
+ shapes.second->moveBy(dx, newY);
+
+ movedShapes.append(shapes.first);
+ movedShapes.append(shapes.second);
+
+ // Adjust connection to new positioning of its shapes
+ } 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();
+ if (diff < minimumLength) {
+ dx = minimumLength - diff;
+ }
+ // Move shape
+ shapes.second->moveBy(dx, newY);
+ // shapes.second->calculatePoints();
+ movedShapes.append(shapes.second);
+ }
+ }
}
void PipelineWidget::wheelEvent(QWheelEvent* event) {

MODIFY
src/graphics/pipelinewidget.h
src/graphics/pipelinewidget.h
#include <QGraphicsView>
+namespace Graphics {
+class Connection;
+class Shape;
+}
+
class PipelineWidget : public QGraphicsView {
Q_OBJECT
public:
@@ -12,6 +17,14 @@ class PipelineWidget : public QGraphicsView {
private:
void scaleView(qreal scaleFactor);
+ void adjustPositioning();
+
+ void createConnection(Graphics::Shape* source, int index1,
+ Graphics::Shape* dest, int index2);
+
+ qreal shapeMargin = 10; // Minimum distance between two shapes
+
+ QList<Graphics::Connection*> m_connections;
};
#endif // PIPELINEWIDGET_H

MODIFY
src/graphics/shape.cpp
src/graphics/shape.cpp
namespace Graphics {
-Shape::Shape(ShapeType type, int verticalPad, int horizontalPad)
- : m_verticalPad(verticalPad), m_horizontalPad(horizontalPad), m_type(type) {
+Shape::Shape(ShapeType type, Stage stage, int verticalPad, int horizontalPad)
+ : m_verticalPad(verticalPad),
+ m_horizontalPad(horizontalPad),
+ m_type(type),
+ m_stage(stage) {
setCacheMode(DeviceCoordinateCache);
+
+ m_nameFont.setPointSize(nameFontSize);
+ m_nameFont.setBold(true);
+
+ m_ioFont.setPointSize(ioFontSize);
}
QRectF Shape::boundingRect() const { return m_rect; }
@@ -17,7 +25,9 @@ void Shape::calculateRect() {
// Placeholder rect used for calculating (0,0) aligned text rects
QRect largeRect = QRect(0, 0, 200, 200);
- auto fontMetricsObject = QFontMetrics(QFont());
+
+ // Get name rect
+ auto fontMetricsObject = QFontMetrics(m_nameFont);
auto nameRect = fontMetricsObject.boundingRect(largeRect, 0, m_name);
// Get size of the largest left and right IO descriptors
@@ -26,8 +36,8 @@ void Shape::calculateRect() {
int leftHeight = 0;
// Iterate over left and right IO descriptors, sum up their heights, and
- // get
- // the largest width of a descriptor
+ // get the largest width of a descriptor
+ fontMetricsObject = QFontMetrics(m_ioFont);
for (const auto &input : m_inputs) {
auto textRect = fontMetricsObject.boundingRect(
largeRect, 0,
@@ -42,7 +52,7 @@ void Shape::calculateRect() {
// largest
int rightHeight = 0;
for (const auto &output : m_outputs) {
- QRect textRect = fontMetricsObject.boundingRect(
+ auto textRect = fontMetricsObject.boundingRect(
largeRect, 0,
output); // create a rect large enough to not obstruct the text
// rect
@@ -57,7 +67,9 @@ void Shape::calculateRect() {
int height = leftHeight > rightHeight ? leftHeight : rightHeight;
height += nameRect.height() + m_verticalPad;
m_rect = QRectF(-(width / 2), -(height / 2), width, height);
+}
+void Shape::calculatePoints() {
// --------- CONNECTION POINTS ------------
m_inputPoints.clear();
m_outputPoints.clear();
@@ -103,6 +115,7 @@ void Shape::addInput(QString input) {
m_inputs.append(input);
prepareGeometryChange();
calculateRect();
+ calculatePoints();
}
void Shape::addInput(QStringList inputs) {
@@ -115,6 +128,7 @@ void Shape::addOutput(QString output) {
m_outputs.append(output);
prepareGeometryChange();
calculateRect();
+ calculatePoints();
}
void Shape::addOutput(QStringList outputs) {
@@ -127,6 +141,7 @@ void Shape::setName(QString name) {
m_name = name;
prepareGeometryChange();
calculateRect();
+ calculatePoints();
}
QPainterPath Shape::drawALUPath(QRectF rect) const {
@@ -163,16 +178,13 @@ void Shape::paint(QPainter *painter,
}
// Translate text in relation to the bounding rectangle, and draw shape name
- QFont font;
- font.setPointSize(nameFontSize);
- font.setBold(true);
- auto fontMetricsObject = QFontMetrics(font);
+ auto fontMetricsObject = QFontMetrics(m_nameFont);
QRectF textRect =
fontMetricsObject.boundingRect(QRect(0, 0, 200, 200), 0, m_name);
textRect.moveTo(0, 0);
textRect.translate(-textRect.width() / 2,
-textRect.height() / 2); // center text rect
- painter->setFont(font);
+ painter->setFont(m_nameFont);
if (m_type == ShapeType::ALU) {
// Shift ALU name a bit to the right
textRect.translate(-rect.width() / 8, 0);
@@ -180,11 +192,9 @@ void Shape::paint(QPainter *painter,
painter->drawText(textRect, Qt::AlignCenter, m_name);
// Iterate through node descriptors and draw text
- font.setPointSize(ioFontSize);
- font.setBold(false);
fontMetricsObject =
- QFontMetrics(font); // update fontMetrics object to new font
- painter->setFont(font);
+ QFontMetrics(m_ioFont); // update fontMetrics object to new font
+ painter->setFont(m_ioFont);
// Draw input descriptors
for (int i = 0; i < m_inputs.size(); i++) {
@@ -198,8 +208,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
@@ -218,14 +228,14 @@ void Shape::paint(QPainter *painter,
}
}
-QPointF Shape::getInputPoint(int index) const {
- if (index >= m_inputPoints.size()) return QPointF();
- return m_inputPoints[index];
+QPointF *Shape::getInputPoint(int index) {
+ if (index >= m_inputPoints.size()) return nullptr;
+ return &m_inputPoints[index];
}
-QPointF Shape::getOutputPoint(int index) const {
- if (index >= m_outputPoints.size()) return QPointF();
- return m_outputPoints[index];
+QPointF *Shape::getOutputPoint(int index) {
+ if (index >= m_outputPoints.size()) return nullptr;
+ return &m_outputPoints[index];
}
} // namespace Graphics

MODIFY
src/graphics/shape.h
src/graphics/shape.h
#define SHAPE_H
#include <QBrush>
+#include <QFont>
#include <QGraphicsItem>
#include <QPainterPath>
#include <QPen>
namespace Graphics {
enum class ShapeType { Block, ALU, MUX };
+enum class Stage { IF = 1, ID = 2, EX = 3, MEM = 4, WB = 5 };
class Shape : public QGraphicsItem {
public:
- Shape(ShapeType type = ShapeType::Block, int verticalPad = 0,
- int horizontalPad = 0);
+ Shape(ShapeType type = ShapeType::Block, Stage stage = Stage::IF,
+ int verticalPad = 0, int horizontalPad = 0);
QRectF boundingRect() const;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
@@ -23,11 +25,12 @@ class Shape : public QGraphicsItem {
void addOutput(QStringList output);
void setName(QString name);
- QPointF getInputPoint(int index) const;
- QPointF getOutputPoint(int index) const;
+ QPointF* getInputPoint(int index);
+ QPointF* getOutputPoint(int index);
+ void calculateRect();
+ void calculatePoints();
private:
- void calculateRect();
QRectF m_rect;
bool m_hasChanged = true; // Flag is set whenever the item has changed (ie.
// when new descriptors have been added)
@@ -35,6 +38,7 @@ class Shape : public QGraphicsItem {
QPainterPath drawALUPath(QRectF boundingRect) const;
ShapeType m_type;
+ Stage m_stage; // Used for correctly positioning the shape
QString m_name;
// Extra size that is added onto each dimension of the shape.
@@ -53,6 +57,9 @@ class Shape : public QGraphicsItem {
QPointF m_topPoint;
QPointF m_bottomPoint;
+ QFont m_nameFont;
+ QFont m_ioFont;
+
// drawing constants
qreal nodeHeight = 20;
qreal nameFontSize = 10;

Work on item positioning algorithm