Blame | Last modification | View Log | RSS feed
#include "Canvas.h"
Canvas::Canvas(QWidget *parent) : QGraphicsView(parent)
{
// Set some drawing options to improve performance
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
setFrameStyle(Sunken | StyledPanel);
setRenderHint(QPainter::Antialiasing, true);
setDragMode(QGraphicsView::RubberBandDrag);
setOptimizationFlags(QGraphicsView::DontSavePainterState);
setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
scene = new QGraphicsScene;
setScene(scene);
zoomLevel = 100;
_pan = false;
circuit = NULL;
// Draw axis lines when running in debug mode
#ifdef _DEBUG
QGraphicsLineItem *xAxis = new QGraphicsLineItem(-1000, 0, 1000, 0);
QGraphicsLineItem *yAxis = new QGraphicsLineItem(0, -800, 0, 800);
scene->addItem(xAxis);
scene->addItem(yAxis);
debugPen = QPen(QColor(COLOR_WIRE_REGION_OUTLINE), 1, Qt::DashDotLine);
#endif
/*
Gate_BASE *g;
for (int i = 1; i < 6; i++) {
g = new Gate_INPUT(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 000);
g = new Gate_OUTPUT(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 150);
g = new Gate_BUFFER(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 300);
g = new Gate_AND(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 500);
g = new Gate_NAND(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 700);
g = new Gate_OR(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 900);
g = new Gate_NOR(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 1100);
g = new Gate_XOR(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 1300);
g = new Gate_XNOR(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 1500);
g = new Gate_NOT(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 1700);
g = new Gate_DFF(0, gate_INPUT, i, 0);
scene->addItem(g);
g->setCanvasPosition(BASE_GATE_SIZE_X + i * 2 * (BASE_GATE_SIZE_X + 20), 1900);
}*/
}
QSize Canvas::minimumSizeHint() const
{
return QSize(400,400);
}
QSize Canvas::sizeHint() const
{
return QSize(800,600);
}
void Canvas::drawCircuit(Circuit *circuit)
{
this->circuit = circuit;
// Calculate the maximum distance between gates including spacing
int largestLevelCount = 0; int largestYSize = 0;
for (int i = 0; i < circuit->gatesPerLevel.keys().size(); i++) {
int level = circuit->gatesPerLevel.keys()[i];
QList<Gate_BASE*> gates = circuit->gatesPerLevel[level];
// We want center to center distance of top and bottom gate
int levelYSize = (gates[0]->ySize / 2) + (gates[gates.size() - 1]->ySize / 2);
levelYSize += CANVAS_GATE_Y_SPACING * (gates.size() - 1);
for (int j = 1; j < gates.size() - 1; j++) {
levelYSize += gates[j]->ySize;
}
if (levelYSize > largestYSize) {
largestLevelCount = gates.size();
largestYSize = levelYSize;
}
}
// Iterate through each level of the circuit and draw the gates
int xOffset = 0, yOffset = 0, ySpacing = 0;
int xSize = (circuit->gatesPerLevel.keys().size() - 1) * CANVAS_GATE_X_SPACING;
xOffset = - xSize / 2;
for (int i = 0; i < circuit->gatesPerLevel.keys().size(); i++) {
int level = circuit->gatesPerLevel.keys()[i];
QList<Gate_BASE*> gates = circuit->gatesPerLevel[level];
// Calculate offsets and spacing between gates
if (gates.size() == largestLevelCount) {
ySpacing = largestYSize / (gates.size() - 1);
yOffset = - largestYSize / 2;
} else {
ySpacing = largestYSize / (gates.size());
yOffset = - largestYSize / 2 + ySpacing / 2;
}
// Draw gates onto canvas
for (int j = 0; j < gates.size(); j++) {
scene->addItem(gates[j]);
connect(gates[j], SIGNAL(updateStatus(QString)), this, SIGNAL(updateStatus(QString)));
gates[j]->setCanvasPosition(xOffset, yOffset);
yOffset += ySpacing;
}
xOffset += CANVAS_GATE_X_SPACING;
}
/*
// Go through and calculate wiring region between gate objects
wireSpace *vSpacing;
for (int i = 1; i < circuit->gatesPerLevel.keys().size() - 1; i++) {
int level = circuit->gatesPerLevel.keys()[i];
QList<Gate_BASE*> gates = circuit->gatesPerLevel[level];
// Compute and save top region
QRectF topSpace = QRectF(gates[0]->x(), -largestYSize / 2, gates[0]->xSize, gates[0]->y() + largestYSize / 2);
vSpacing = new wireSpace;
vSpacing->region = topSpace;
circuit->wireVSpacing[level].append(vSpacing);
// Compute and save intermediate regions
for (int j = 0; j < gates.size() - 1; j++) {
QRectF midSpace;
if (gates[j]->xSize >= gates[j+1]->xSize) {
midSpace = QRectF(gates[j]->x(), gates[j]->y() + gates[j]->ySize, gates[j]->xSize, gates[j+1]->y() - (gates[j]->y() + gates[j]->ySize));
} else {
midSpace = QRectF(gates[j+1]->x(), gates[j]->y() + gates[j]->ySize, gates[j+1]->xSize, gates[j+1]->y() - (gates[j]->y() + gates[j]->ySize));
}
vSpacing = new wireSpace;
vSpacing->region = midSpace;
circuit->wireVSpacing[level].append(vSpacing);
}
// Compute and save bottom region
Gate_BASE *lastGate = gates[gates.size() - 1];
QRectF botSpace = QRectF(lastGate->x(), lastGate->y() + lastGate->ySize, lastGate->xSize, largestYSize / 2 - (lastGate->y() + lastGate->ySize));
vSpacing = new wireSpace;
vSpacing->region = botSpace;
circuit->wireVSpacing[level].append(vSpacing);
// Draw regions in debug mode
#ifdef _DEBUG
for (int j = 0; j < circuit->wireVSpacing[level].size(); j++)
scene->addRect(circuit->wireVSpacing[level][j]->region, debugPen, QBrush(QColor(COLOR_WIRE_REGION_V)));
#endif
}*/
/*
// Go through and calculate wiring region between levels
wireSpace *hSpacing;
for (int i = 0; i < circuit->gatesPerLevel.keys().size() - 1; i++) {
int level = circuit->gatesPerLevel.keys()[i];
QList<Gate_BASE*> gates = circuit->gatesPerLevel[level];
// Compute the max X position for this level and min X position for next level
}*/
// Instantiate wires for each output->input connection on each gate
QList<int> gateIDs = circuit->gateIndex.keys();
for (int i = 0; i < gateIDs.size(); i++) {
Gate_BASE *tmp = circuit->gateIndex[gateIDs[i]];
// Connect each gate's input to other gate's output
for (int j = 0; j < tmp->fanInGates.size(); j++) {
Wire *wire = new Wire();
wire->setPoints(tmp->fanInGates[j], tmp, j);
// Store wires in gates on both ends
tmp->gateInputWires.append(wire);
tmp->fanInGates[j]->gateOutputWires.append(wire);
// Save wire information into circuit
circuit->wires.append(wire);
connect(wire, SIGNAL(updateStatus(QString)), this, SIGNAL(updateStatus(QString)));
connect(circuit, SIGNAL(toggleShowWireValues()), wire, SLOT(toggleShowValues()));
scene->addItem(wire);
}
}
scene->update();
emit updateStatus("Circuit file successfully loaded");
}
/**
* Zooms the canvas using matrix scaling
*/
void Canvas::zoomCanvas(int level)
{
zoomLevel = level;
qreal scale = qPow(qreal(2), (zoomLevel - 160) / qreal(40));
QMatrix matrix;
matrix.scale(scale,scale);
setMatrix(matrix);
}
#ifndef QT_NO_WHEELEVENT
void Canvas::wheelEvent(QWheelEvent *event)
{
// Zoom canvas on scroll wheel
if (event->delta() > 0) {
zoomCanvas(zoomLevel + 5);
emit updateZoomSlider(zoomLevel + 5);
} else {
zoomCanvas(zoomLevel - 5);
emit updateZoomSlider(zoomLevel - 5);
}
event->accept();
}
#endif
void Canvas::mousePressEvent(QMouseEvent *event)
{
// If CTRL is held down, pan the canvas
if (event->modifiers() & Qt::ControlModifier) {
_pan = true;
_panStartX = event->x();
_panStartY = event->y();
setCursor(Qt::ClosedHandCursor);
event->accept();
return;
}
if (circuit != NULL && circuit->circuitLoaded) {
// Clear any previously selected gates and wires
for (int i = 0; i < circuit->gatesPerLevel.size(); i++) {
QList<Gate_BASE*> gates = circuit->gatesPerLevel[i];
for (int j = 0; j < gates.size(); j++) {
gates[j]->setHighlight(false, QColor(0,0,0,255));
}
}
for (int i = 0; i < circuit->wires.size(); i++) {
circuit->wires[i]->setHighlight(false, QColor(0,0,0,255));
}
updateStatus("");
}
// If not panning, propogate event to child objects
QGraphicsView::mousePressEvent(event);
}
void Canvas::mouseMoveEvent(QMouseEvent *event)
{
// Process panning if active
if (_pan) {
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - _panStartX));
verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - _panStartY));
_panStartX = event->x();
_panStartY = event->y();
event->accept();
return;
}
// Otherwise propogate event to child objects
QGraphicsView::mouseMoveEvent(event);
}
void Canvas::mouseReleaseEvent(QMouseEvent *event)
{
// Stop panning if active
_pan = false;
setCursor(Qt::ArrowCursor);
event->accept();
// Propogate event to child objects
QGraphicsView::mouseReleaseEvent(event);
}