Subversion Repositories Code-Repo

Rev

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);
}