summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleks Kissinger <aleks0@gmail.com>2017-12-21 16:34:58 +0000
committerAleks Kissinger <aleks0@gmail.com>2017-12-21 16:34:58 +0000
commit9d8317cd593d47911bb6b2e6fb8ef0077e24ae36 (patch)
tree6f2d44782c37f8eaa36f2bb1de625343af65649e
parent82bdd42a475d240bb08e201a47b0972d0b2862a6 (diff)
edge bending working
-rw-r--r--tikzit/src/data/edge.cpp22
-rw-r--r--tikzit/src/data/edge.h5
-rw-r--r--tikzit/src/data/tikzparser.parser.cpp11
-rw-r--r--tikzit/src/data/tikzparser.y5
-rw-r--r--tikzit/src/gui/edgeitem.cpp27
-rw-r--r--tikzit/src/gui/edgeitem.h5
-rw-r--r--tikzit/src/gui/tikzscene.cpp184
-rw-r--r--tikzit/src/gui/tikzscene.h4
-rw-r--r--tikzit/src/gui/undocommands.cpp10
-rw-r--r--tikzit/src/tikzit.h14
-rw-r--r--tikzit/src/util.cpp48
-rw-r--r--tikzit/src/util.h24
-rw-r--r--tikzit/tikzit.pro6
13 files changed, 307 insertions, 58 deletions
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 <QDebug>
#include <QPointF>
-#include <cmath>
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<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) {
+ // 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<NodeItem*>(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<Node*,QPointF> newNodePositions;
+ _modifyEdgeItem = 0;
+ } else {
+ // otherwise, process mouse move normally
+ QGraphicsScene::mouseReleaseEvent(event);
- foreach (QGraphicsItem *gi, selectedItems()) {
- if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
- ni->writePos();
- newNodePositions.insert(ni->node(), ni->node()->point());
+ 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;
+ //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<EdgeItem *> TikzScene::edgeItems() const
return _edgeItems;
}
+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();
+ }
+ }
+}
+
QVector<NodeItem *> 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<NodeItem *> nodeItems() const;
-
QVector<EdgeItem *> edgeItems() const;
+ void refreshAdjacentEdges(QList<Node*> nodes);
TikzDocument *tikzDocument() const;
void setTikzDocument(TikzDocument *tikzDocument);
@@ -43,6 +43,8 @@ private:
TikzDocument *_tikzDocument;
QVector<NodeItem*> _nodeItems;
QVector<EdgeItem*> _edgeItems;
+ EdgeItem *_modifyEdgeItem;
+ bool _firstControlPoint;
QMap<Node*,QPointF> _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 <QPoint>
+#include <cmath>
+
+// 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
diff --git a/tikzit/tikzit.pro b/tikzit/tikzit.pro
index f1a1d54..b14822b 100644
--- a/tikzit/tikzit.pro
+++ b/tikzit/tikzit.pro
@@ -47,7 +47,8 @@ SOURCES += src/gui/mainwindow.cpp \
src/gui/commands.cpp \
src/data/tikzdocument.cpp \
src/gui/undocommands.cpp \
- src/gui/mainmenu.cpp
+ src/gui/mainmenu.cpp \
+ src/util.cpp
HEADERS += src/gui/mainwindow.h \
src/gui/toolpalette.h \
@@ -68,7 +69,8 @@ HEADERS += src/gui/mainwindow.h \
src/gui/commands.h \
src/data/tikzdocument.h \
src/gui/undocommands.h \
- src/gui/mainmenu.h
+ src/gui/mainmenu.h \
+ src/util.h
FORMS += src/gui/mainwindow.ui \
src/gui/propertypalette.ui \