diff options
Diffstat (limited to 'src/gui/tikzscene.cpp')
-rw-r--r-- | src/gui/tikzscene.cpp | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp new file mode 100644 index 0000000..3431c0c --- /dev/null +++ b/src/gui/tikzscene.cpp @@ -0,0 +1,384 @@ +#include "tikzit.h" +#include "util.h" +#include "tikzscene.h" +#include "undocommands.h" + +#include <QPen> +#include <QBrush> +#include <QDebug> + + +TikzScene::TikzScene(TikzDocument *tikzDocument, QObject *parent) : + QGraphicsScene(parent), _tikzDocument(tikzDocument) +{ + _modifyEdgeItem = 0; + _drawEdgeItem = new QGraphicsLineItem(); + setSceneRect(-310,-230,620,450); + + QPen pen; + pen.setColor(QColor::fromRgbF(0.5f, 0.0f, 0.5f)); + pen.setWidth(3); + _drawEdgeItem->setPen(pen); + _drawEdgeItem->setLine(0,0,0,0); + _drawEdgeItem->setVisible(false); + addItem(_drawEdgeItem); +} + +TikzScene::~TikzScene() { +} + +Graph *TikzScene::graph() +{ + return _tikzDocument->graph(); +} + +void TikzScene::graphReplaced() +{ + foreach (NodeItem *ni, _nodeItems) { + removeItem(ni); + delete ni; + } + _nodeItems.clear(); + + foreach (EdgeItem *ei, _edgeItems) { + removeItem(ei); + delete ei; + } + _edgeItems.clear(); + + foreach (Edge *e, graph()->edges()) { + EdgeItem *ei = new EdgeItem(e); + _edgeItems.insert(e, ei); + addItem(ei); + } + + foreach (Node *n, graph()->nodes()) { + NodeItem *ni = new NodeItem(n); + _nodeItems.insert(n, ni); + addItem(ni); + } +} + +void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + // current mouse position, in scene coordinates + QPointF mousePos = event->scenePos(); + + // disable rubber band drag, which will clear the selection. Only re-enable it + // for the SELECT tool, and when no control point has been clicked. + views()[0]->setDragMode(QGraphicsView::NoDrag); + + // radius of a control point for bezier edges, in scene coordinates + qreal cpR = GLOBAL_SCALEF * (0.05); + qreal cpR2 = cpR * cpR; + + switch (tikzit->toolPalette()->currentTool()) { + case ToolPalette::SELECT: + // check if we grabbed a control point of an edge + foreach (QGraphicsItem *gi, selectedItems()) { + if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) { + qreal dx, dy; + + dx = ei->cp1Item()->pos().x() - mousePos.x(); + dy = ei->cp1Item()->pos().y() - mousePos.y(); + + if (dx*dx + dy*dy <= cpR2) { + _modifyEdgeItem = ei; + _firstControlPoint = true; + break; + } + + dx = ei->cp2Item()->pos().x() - mousePos.x(); + dy = ei->cp2Item()->pos().y() - mousePos.y(); + + if (dx*dx + dy*dy <= cpR2) { + _modifyEdgeItem = ei; + _firstControlPoint = false; + break; + } + } + } + + if (_modifyEdgeItem != 0) { + // store for undo purposes + Edge *e = _modifyEdgeItem->edge(); + _oldBend = e->bend(); + _oldInAngle = e->inAngle(); + _oldOutAngle = e->outAngle(); + _oldWeight = e->weight(); + } else { + // since we are not dragging a control point, process the click normally + views()[0]->setDragMode(QGraphicsView::RubberBandDrag); + QGraphicsScene::mousePressEvent(event); + + // save current node positions for undo support + _oldNodePositions.clear(); + foreach (QGraphicsItem *gi, selectedItems()) { + if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) { + _oldNodePositions.insert(ni->node(), ni->node()->point()); + } + } + } + + break; + case ToolPalette::VERTEX: + break; + case ToolPalette::EDGE: + break; + case ToolPalette::CROP: + break; + } +} + +void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + // current mouse position, in scene coordinates + QPointF mousePos = event->scenePos(); + + switch (tikzit->toolPalette()->currentTool()) { + case ToolPalette::SELECT: + if (_modifyEdgeItem != 0) { + Edge *e = _modifyEdgeItem->edge(); + + // dragging a control point + QPointF src = toScreen(e->source()->point()); + QPointF targ = toScreen(e->target()->point()); + float dx1 = targ.x() - src.x(); + float dy1 = targ.y() - src.y(); + float dx2, dy2; + if (_firstControlPoint) { + dx2 = mousePos.x() - src.x(); + dy2 = mousePos.y() - src.y(); + } else { + dx2 = mousePos.x() - targ.x(); + dy2 = mousePos.y() - targ.y(); + } + + float baseDist = sqrt(dx1*dx1 + dy1*dy1); + float handleDist = sqrt(dx2*dx2 + dy2*dy2); + float wcoarseness = 0.1f; + + if (!e->isSelfLoop()) { + if (baseDist != 0) { + e->setWeight(roundToNearest(wcoarseness, handleDist/baseDist)); + } else { + e->setWeight(roundToNearest(wcoarseness, handleDist/GLOBAL_SCALEF)); + } + } + + float control_angle = atan2(-dy2, dx2); + + int bcoarseness = 15; + + if(e->basicBendMode()) { + float bnd; + float base_angle = atan2(-dy1, dx1); + if (_firstControlPoint) { + bnd = base_angle - control_angle; + } else { + bnd = control_angle - base_angle + M_PI; + if (bnd > M_PI) bnd -= 2*M_PI; + } + + e->setBend(round(bnd * (180.0f / M_PI) * (1.0f / (float)bcoarseness)) * bcoarseness); + + } else { + int bnd = round(control_angle * (180.0f / M_PI) * + (1.0f / (float)bcoarseness)) * + bcoarseness; + if (_firstControlPoint) { + // TODO: enable moving both control points +// if ([theEvent modifierFlags] & NSAlternateKeyMask) { +// if ([modifyEdge isSelfLoop]) { +// [modifyEdge setInAngle:[modifyEdge inAngle] + +// (bnd - [modifyEdge outAngle])]; +// } else { +// [modifyEdge setInAngle:[modifyEdge inAngle] - +// (bnd - [modifyEdge outAngle])]; +// } +// } + + e->setOutAngle(bnd); + } else { +// if (theEvent.modifierFlags & NSAlternateKeyMask) { +// if ([modifyEdge isSelfLoop]) { +// [modifyEdge setOutAngle:[modifyEdge outAngle] + +// (bnd - [modifyEdge inAngle])]; +// } else { +// [modifyEdge setOutAngle:[modifyEdge outAngle] - +// (bnd - [modifyEdge inAngle])]; +// } +// } + + e->setInAngle(bnd); + } + } + + _modifyEdgeItem->readPos(); + + } else { + // otherwise, process mouse move normally + QGraphicsScene::mouseMoveEvent(event); + refreshAdjacentEdges(_oldNodePositions.keys()); + } + + break; + case ToolPalette::VERTEX: + break; + case ToolPalette::EDGE: + break; + case ToolPalette::CROP: + break; + } +} + +void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + // current mouse position, in scene coordinates + QPointF mousePos = event->scenePos(); + + switch (tikzit->toolPalette()->currentTool()) { + case ToolPalette::SELECT: + if (_modifyEdgeItem != 0) { + // finished dragging a control point + Edge *e = _modifyEdgeItem->edge(); + + if (_oldWeight != e->weight() || + _oldBend != e->bend() || + _oldInAngle != e->inAngle() || + _oldOutAngle != e->outAngle()) + { + EdgeBendCommand *cmd = new EdgeBendCommand(this, e, _oldWeight, _oldBend, _oldInAngle, _oldOutAngle); + _tikzDocument->undoStack()->push(cmd); + } + + _modifyEdgeItem = 0; + } else { + // otherwise, process mouse move normally + QGraphicsScene::mouseReleaseEvent(event); + + if (!_oldNodePositions.empty()) { + QMap<Node*,QPointF> newNodePositions; + + foreach (QGraphicsItem *gi, selectedItems()) { + if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) { + ni->writePos(); + newNodePositions.insert(ni->node(), ni->node()->point()); + } + } + + //qDebug() << _oldNodePositions; + //qDebug() << newNodePositions; + + _tikzDocument->undoStack()->push(new MoveCommand(this, _oldNodePositions, newNodePositions)); + _oldNodePositions.clear(); + } + } + + break; + case ToolPalette::VERTEX: + { + int gridSize = GLOBAL_SCALE / 8; + QPointF gridPos(round(mousePos.x()/gridSize)*gridSize, round(mousePos.y()/gridSize)*gridSize); + Node *n = new Node(); + n->setPoint(fromScreen(gridPos)); + + QRectF grow(gridPos.x() - GLOBAL_SCALEF, gridPos.y() - GLOBAL_SCALEF, 2 * GLOBAL_SCALEF, 2 * GLOBAL_SCALEF); + QRectF newBounds = sceneRect().united(grow); + qDebug() << grow; + qDebug() << newBounds; + + AddNodeCommand *cmd = new AddNodeCommand(this, n, newBounds); + _tikzDocument->undoStack()->push(cmd); + } + break; + case ToolPalette::EDGE: + break; + case ToolPalette::CROP: + break; + } +} + +void TikzScene::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { + QSet<Node*> selNodes; + QSet<Edge*> selEdges; + getSelection(selNodes, selEdges); + + QMap<int,Node*> deleteNodes; + QMap<int,Edge*> deleteEdges; + + for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) { + Node *n = _tikzDocument->graph()->nodes()[i]; + if (selNodes.contains(n)) deleteNodes.insert(i, n); + } + + for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) { + Edge *e = _tikzDocument->graph()->edges()[i]; + if (selEdges.contains(e) || + selNodes.contains(e->source()) || + selNodes.contains(e->target())) deleteEdges.insert(i, e); + } + + //qDebug() << "nodes:" << deleteNodes; + //qDebug() << "edges:" << deleteEdges; + DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, selEdges); + _tikzDocument->undoStack()->push(cmd); + } +} + +void TikzScene::getSelection(QSet<Node *> &selNodes, QSet<Edge *> &selEdges) +{ + foreach (QGraphicsItem *gi, selectedItems()) { + if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) selNodes << ni->node(); + if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) selEdges << ei->edge(); + } +} + + +TikzDocument *TikzScene::tikzDocument() const +{ + return _tikzDocument; +} + +void TikzScene::setTikzDocument(TikzDocument *tikzDocument) +{ + _tikzDocument = tikzDocument; + graphReplaced(); +} + +void TikzScene::refreshAdjacentEdges(QList<Node*> nodes) +{ + if (nodes.empty()) return; + foreach (EdgeItem *ei, _edgeItems) { + if (nodes.contains(ei->edge()->source()) || nodes.contains(ei->edge()->target())) { + ei->edge()->updateControls(); + ei->readPos(); + } + } +} + +void TikzScene::setBounds(QRectF bounds) +{ + if (bounds != sceneRect()) { + if (!views().empty()) { + QGraphicsView *v = views().first(); + QPointF c = v->mapToScene(v->viewport()->rect().center()); + setSceneRect(bounds); + v->centerOn(c); + } else { + setSceneRect(bounds); + } + } +} + +QMap<Node*,NodeItem *> &TikzScene::nodeItems() +{ + return _nodeItems; +} + +QMap<Edge*,EdgeItem*> &TikzScene::edgeItems() +{ + return _edgeItems; +} |