From 9d8317cd593d47911bb6b2e6fb8ef0077e24ae36 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Thu, 21 Dec 2017 16:34:58 +0000 Subject: edge bending working --- tikzit/src/data/edge.cpp | 22 +++- tikzit/src/data/edge.h | 5 + tikzit/src/data/tikzparser.parser.cpp | 11 +- tikzit/src/data/tikzparser.y | 5 +- tikzit/src/gui/edgeitem.cpp | 27 ++++- tikzit/src/gui/edgeitem.h | 5 +- tikzit/src/gui/tikzscene.cpp | 184 +++++++++++++++++++++++++++++----- tikzit/src/gui/tikzscene.h | 4 +- tikzit/src/gui/undocommands.cpp | 10 +- tikzit/src/tikzit.h | 14 --- tikzit/src/util.cpp | 48 +++++++++ tikzit/src/util.h | 24 +++++ 12 files changed, 303 insertions(+), 56 deletions(-) create mode 100644 tikzit/src/util.cpp create mode 100644 tikzit/src/util.h (limited to 'tikzit/src') diff --git a/tikzit/src/data/edge.cpp b/tikzit/src/data/edge.cpp index b999f8a..3ff6c6e 100644 --- a/tikzit/src/data/edge.cpp +++ b/tikzit/src/data/edge.cpp @@ -1,9 +1,9 @@ #include "edge.h" #include "tikzit.h" +#include "util.h" #include #include -#include Edge::Edge(Node *s, Node *t, QObject *parent) : QObject(parent), _source(s), _target(t) @@ -278,6 +278,26 @@ float Edge::cpDist() const return _cpDist; } +void Edge::setBend(int bend) +{ + _bend = bend; +} + +void Edge::setInAngle(int inAngle) +{ + _inAngle = inAngle; +} + +void Edge::setOutAngle(int outAngle) +{ + _outAngle = outAngle; +} + +void Edge::setWeight(float weight) +{ + _weight = weight; +} + QPointF Edge::mid() const { return _mid; diff --git a/tikzit/src/data/edge.h b/tikzit/src/data/edge.h index e392c56..d2913b8 100644 --- a/tikzit/src/data/edge.h +++ b/tikzit/src/data/edge.h @@ -50,6 +50,11 @@ public: bool basicBendMode() const; float cpDist() const; + void setBend(int bend); + void setInAngle(int inAngle); + void setOutAngle(int outAngle); + void setWeight(float weight); + signals: public slots: diff --git a/tikzit/src/data/tikzparser.parser.cpp b/tikzit/src/data/tikzparser.parser.cpp index 53c8da7..3dadb45 100644 --- a/tikzit/src/data/tikzparser.parser.cpp +++ b/tikzit/src/data/tikzparser.parser.cpp @@ -532,7 +532,7 @@ static const yytype_uint8 yyrline[] = 0, 124, 124, 130, 130, 131, 131, 131, 131, 133, 133, 136, 138, 140, 141, 148, 154, 156, 163, 169, 169, 171, 172, 190, 190, 191, 198, 199, 201, 202, - 210, 243, 243, 244, 244, 245, 247 + 210, 246, 246, 247, 247, 248, 250 }; #endif @@ -1677,8 +1677,11 @@ yyreduce: } Edge *edge = assembler->graph()->addEdge(s, t); - if ((yyvsp[(2) - (7)].data)) + if ((yyvsp[(2) - (7)].data)) { edge->setData((yyvsp[(2) - (7)].data)); + edge->setAttributesFromData(); + } + if ((yyvsp[(5) - (7)].node)) edge->setEdgeNode((yyvsp[(5) - (7)].node)); if ((yyvsp[(3) - (7)].noderef).anchor) { @@ -1698,7 +1701,7 @@ yyreduce: break; case 36: -#line 248 "../tikzit/src/data/tikzparser.y" +#line 251 "../tikzit/src/data/tikzparser.y" { assembler->graph()->setBbox(QRectF(*(yyvsp[(3) - (6)].pt), *(yyvsp[(5) - (6)].pt))); delete (yyvsp[(3) - (6)].pt); @@ -1708,7 +1711,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 1712 "../tikzit/src/data/tikzparser.parser.cpp" +#line 1715 "../tikzit/src/data/tikzparser.parser.cpp" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/tikzit/src/data/tikzparser.y b/tikzit/src/data/tikzparser.y index b5bca35..eaf09eb 100644 --- a/tikzit/src/data/tikzparser.y +++ b/tikzit/src/data/tikzparser.y @@ -221,8 +221,11 @@ edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";" } Edge *edge = assembler->graph()->addEdge(s, t); - if ($2) + if ($2) { edge->setData($2); + edge->setAttributesFromData(); + } + if ($5) edge->setEdgeNode($5); if ($3.anchor) { diff --git a/tikzit/src/gui/edgeitem.cpp b/tikzit/src/gui/edgeitem.cpp index cb275a5..497fa07 100644 --- a/tikzit/src/gui/edgeitem.cpp +++ b/tikzit/src/gui/edgeitem.cpp @@ -24,12 +24,12 @@ EdgeItem::EdgeItem(Edge *edge) GLOBAL_SCALEF * 0.1, GLOBAL_SCALEF * 0.1); _cp2Item->setVisible(false); - syncPos(); + readPos(); } -void EdgeItem::syncPos() +void EdgeItem::readPos() { - _edge->setAttributesFromData(); + //_edge->setAttributesFromData(); _edge->updateControls(); QPainterPath path; @@ -104,7 +104,28 @@ QRectF EdgeItem::boundingRect() const return shape().boundingRect().adjusted(-r,-r,r,r); } +QPainterPath EdgeItem::shape() const +{ + // get the shape of the edge, and expand a bit to make selection easier + QPainterPath oldShape = QGraphicsPathItem::shape(); + QPainterPathStroker stroker; + stroker.setWidth(5); + stroker.setJoinStyle(Qt::MiterJoin); + QPainterPath newShape = (stroker.createStroke(oldShape) + oldShape).simplified(); + return newShape; +} + Edge *EdgeItem::edge() const { return _edge; } + +QGraphicsEllipseItem *EdgeItem::cp1Item() const +{ + return _cp1Item; +} + +QGraphicsEllipseItem *EdgeItem::cp2Item() const +{ + return _cp2Item; +} diff --git a/tikzit/src/gui/edgeitem.h b/tikzit/src/gui/edgeitem.h index 5bdf3be..b017265 100644 --- a/tikzit/src/gui/edgeitem.h +++ b/tikzit/src/gui/edgeitem.h @@ -18,11 +18,14 @@ class EdgeItem : public QGraphicsPathItem { public: EdgeItem(Edge *edge); - void syncPos(); + void readPos(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QRectF boundingRect() const; + QPainterPath shape() const; Edge *edge() const; + QGraphicsEllipseItem *cp1Item() const; + QGraphicsEllipseItem *cp2Item() const; private: Edge *_edge; diff --git a/tikzit/src/gui/tikzscene.cpp b/tikzit/src/gui/tikzscene.cpp index b12b769..b502f84 100644 --- a/tikzit/src/gui/tikzscene.cpp +++ b/tikzit/src/gui/tikzscene.cpp @@ -1,4 +1,5 @@ #include "tikzit.h" +#include "util.h" #include "tikzscene.h" #include "undocommands.h" @@ -10,6 +11,7 @@ TikzScene::TikzScene(TikzDocument *tikzDocument, QObject *parent) : QGraphicsScene(parent), _tikzDocument(tikzDocument) { + _modifyEdgeItem = 0; } TikzScene::~TikzScene() { @@ -49,23 +51,59 @@ void TikzScene::graphReplaced() void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { + // current mouse position, in scene coordinates QPointF mousePos(event->buttonDownScenePos(Qt::LeftButton).x(), event->buttonDownScenePos(Qt::LeftButton).y()); + // 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: - // TODO: check if we grabbed a control point - QGraphicsScene::mousePressEvent(event); - if (!selectedItems().empty() && !items(mousePos).empty()) { + // check if we grabbed a control point of an edge + foreach (QGraphicsItem *gi, selectedItems()) { + if (EdgeItem *ei = dynamic_cast(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) { + // disable rubber band drag, which will clear the selection + views()[0]->setDragMode(QGraphicsView::NoDrag); + qDebug() << "Got a control point"; + } 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(gi)) { _oldNodePositions.insert(ni->node(), ni->node()->point()); } } - qDebug() << "I am dragging"; } + break; case ToolPalette::VERTEX: break; @@ -78,9 +116,96 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + // current mouse position, in scene coordinates + + QPointF mousePos = event->scenePos(); + switch (tikzit->toolPalette()->currentTool()) { case ToolPalette::SELECT: - QGraphicsScene::mouseMoveEvent(event); + 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; @@ -89,35 +214,37 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) case ToolPalette::CROP: break; } - - // TODO: only sync edges that change - foreach (EdgeItem *ei, _edgeItems) { - ei->edge()->updateControls(); - ei->syncPos(); - } } void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { switch (tikzit->toolPalette()->currentTool()) { case ToolPalette::SELECT: - QGraphicsScene::mouseReleaseEvent(event); + if (_modifyEdgeItem != 0) { + // finished dragging a control point + // TODO - if (!_oldNodePositions.empty()) { - QMap newNodePositions; + _modifyEdgeItem = 0; + } else { + // otherwise, process mouse move normally + QGraphicsScene::mouseReleaseEvent(event); - foreach (QGraphicsItem *gi, selectedItems()) { - if (NodeItem *ni = dynamic_cast(gi)) { - ni->writePos(); - newNodePositions.insert(ni->node(), ni->node()->point()); + if (!_oldNodePositions.empty()) { + QMap newNodePositions; + + foreach (QGraphicsItem *gi, selectedItems()) { + if (NodeItem *ni = dynamic_cast(gi)) { + ni->writePos(); + newNodePositions.insert(ni->node(), ni->node()->point()); + } } - } - //qDebug() << _oldNodePositions; - //qDebug() << newNodePositions; + //qDebug() << _oldNodePositions; + //qDebug() << newNodePositions; - _tikzDocument->undoStack()->push(new MoveCommand(this, _oldNodePositions, newNodePositions)); - _oldNodePositions.clear(); + _tikzDocument->undoStack()->push(new MoveCommand(this, _oldNodePositions, newNodePositions)); + _oldNodePositions.clear(); + } } break; @@ -146,6 +273,17 @@ QVector TikzScene::edgeItems() const return _edgeItems; } +void TikzScene::refreshAdjacentEdges(QList 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(); + } + } +} + QVector TikzScene::nodeItems() const { return _nodeItems; diff --git a/tikzit/src/gui/tikzscene.h b/tikzit/src/gui/tikzscene.h index 9d598ad..cc7f4e4 100644 --- a/tikzit/src/gui/tikzscene.h +++ b/tikzit/src/gui/tikzscene.h @@ -27,8 +27,8 @@ public: ~TikzScene(); Graph *graph() const; QVector nodeItems() const; - QVector edgeItems() const; + void refreshAdjacentEdges(QList nodes); TikzDocument *tikzDocument() const; void setTikzDocument(TikzDocument *tikzDocument); @@ -43,6 +43,8 @@ private: TikzDocument *_tikzDocument; QVector _nodeItems; QVector _edgeItems; + EdgeItem *_modifyEdgeItem; + bool _firstControlPoint; QMap _oldNodePositions; }; diff --git a/tikzit/src/gui/undocommands.cpp b/tikzit/src/gui/undocommands.cpp index b8a045b..c28611f 100644 --- a/tikzit/src/gui/undocommands.cpp +++ b/tikzit/src/gui/undocommands.cpp @@ -21,10 +21,7 @@ void MoveCommand::undo() } } - foreach (EdgeItem *ei, _scene->edgeItems()) { - ei->edge()->updateControls(); - ei->syncPos(); - } + _scene->refreshAdjacentEdges(_oldNodePositions.keys()); } void MoveCommand::redo() @@ -36,8 +33,5 @@ void MoveCommand::redo() } } - foreach (EdgeItem *ei, _scene->edgeItems()) { - ei->edge()->updateControls(); - ei->syncPos(); - } + _scene->refreshAdjacentEdges(_newNodePositions.keys()); } diff --git a/tikzit/src/tikzit.h b/tikzit/src/tikzit.h index b0e372d..bf4f7f1 100644 --- a/tikzit/src/tikzit.h +++ b/tikzit/src/tikzit.h @@ -32,20 +32,6 @@ inline QPointF toScreen(QPointF src) inline QPointF fromScreen(QPointF src) { src.setY(-src.y()); src /= GLOBAL_SCALEF; return src; } -// interpolate on a cubic bezier curve -inline float bezierInterpolate(float dist, float c0, float c1, float c2, float c3) { - float distp = 1 - dist; - return (distp*distp*distp) * c0 + - 3 * (distp*distp) * dist * c1 + - 3 * (dist*dist) * distp * c2 + - (dist*dist*dist) * c3; -} - -inline QPointF bezierInterpolateFull (float dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3) { - return QPointF(bezierInterpolate (dist, c0.x(), c1.x(), c2.x(), c3.x()), - bezierInterpolate (dist, c0.y(), c1.y(), c2.y(), c3.y())); -} - class Tikzit : public QObject { Q_OBJECT diff --git a/tikzit/src/util.cpp b/tikzit/src/util.cpp new file mode 100644 index 0000000..64716d2 --- /dev/null +++ b/tikzit/src/util.cpp @@ -0,0 +1,48 @@ +#include "util.h" + +float bezierInterpolate(float dist, float c0, float c1, float c2, float c3) { + float distp = 1 - dist; + return (distp*distp*distp) * c0 + + 3 * (distp*distp) * dist * c1 + + 3 * (dist*dist) * distp * c2 + + (dist*dist*dist) * c3; +} + +QPointF bezierInterpolateFull (float dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3) { + return QPointF(bezierInterpolate (dist, c0.x(), c1.x(), c2.x(), c3.x()), + bezierInterpolate (dist, c0.y(), c1.y(), c2.y(), c3.y())); +} + + +float roundToNearest(float stepSize, float val) { + if (stepSize==0.0f) return val; + else return round(val/stepSize)*stepSize; +} + +float radiansToDegrees (float radians) { + return (radians * 180.0f) / M_PI; +} + +float degreesToRadians(float degrees) { + return (degrees * M_PI) / 180.0f; +} + +int normaliseAngleDeg (int degrees) { + while (degrees > 180) { + degrees -= 360; + } + while (degrees <= -180) { + degrees += 360; + } + return degrees; +} + +float normaliseAngleRad (float rads) { + while (rads > M_PI) { + rads -= 2 * M_PI; + } + while (rads <= -M_PI) { + rads += 2 * M_PI; + } + return rads; +} diff --git a/tikzit/src/util.h b/tikzit/src/util.h new file mode 100644 index 0000000..2952214 --- /dev/null +++ b/tikzit/src/util.h @@ -0,0 +1,24 @@ +/** + * Various utility functions, mostly for mathematical calculation. + */ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +// interpolate on a cubic bezier curve +float bezierInterpolate(float dist, float c0, float c1, float c2, float c3); +QPointF bezierInterpolateFull (float dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3); + +// rounding +float roundToNearest(float stepSize, float val); +float radiansToDegrees (float radians); + +// angles +float degreesToRadians(float degrees); +int normaliseAngleDeg (int degrees); +float normaliseAngleRad (float rads); + +#endif // UTIL_H -- cgit v1.2.3