From bbe0f89c242bb11cbcae89aee9110fee56a5f8a2 Mon Sep 17 00:00:00 2001 From: Adrian Holfter Date: Thu, 16 May 2019 14:39:02 +0200 Subject: implement horizontal scrolling while holding shift (only if shift-to-scroll is disabled) --- src/gui/tikzview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/tikzview.cpp b/src/gui/tikzview.cpp index 3615685..3479d22 100644 --- a/src/gui/tikzview.cpp +++ b/src/gui/tikzview.cpp @@ -149,6 +149,8 @@ void TikzView::wheelEvent(QWheelEvent *event) } else if (event->angleDelta().y() < 0) { zoomOut(); } + } else if (event->modifiers() & Qt::ShiftModifier) { + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + event->angleDelta().y()); } } -- cgit v1.2.3 From c56b682750e9f2a911a841e89e4e51b7d0608ab5 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 12 Apr 2020 13:31:11 +0100 Subject: added support for parsing paths --- src/data/graph.h | 1 + src/data/graphelementdata.cpp | 8 ++++ src/data/graphelementdata.h | 1 + src/data/path.cpp | 6 +++ src/data/path.h | 19 ++++++++++ src/data/tikzassembler.cpp | 41 ++++++++++++++++++++ src/data/tikzassembler.h | 14 +++++++ src/data/tikzparser.y | 88 ++++++++++++++++++++++++++++--------------- src/data/tikzparserdefs.h | 2 + tikzit.pro | 2 + 10 files changed, 151 insertions(+), 31 deletions(-) create mode 100644 src/data/path.cpp create mode 100644 src/data/path.h diff --git a/src/data/graph.h b/src/data/graph.h index 286ccdc..a996bcb 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -25,6 +25,7 @@ #include "node.h" #include "edge.h" +#include "path.h" #include "graphelementdata.h" #include diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp index cd09a6d..931f86a 100644 --- a/src/data/graphelementdata.cpp +++ b/src/data/graphelementdata.cpp @@ -110,6 +110,14 @@ int GraphElementData::indexOfKey(QString key) return -1; } +void GraphElementData::mergeData(GraphElementData *d) +{ + GraphElementProperty p; + foreach (p, d->properties()) { + if (!hasProperty(p.key())) add(p); + } +} + bool GraphElementData::removeRows(int row, int /*count*/, const QModelIndex &parent) { if (row >= 0 && row < _properties.length()) { diff --git a/src/data/graphelementdata.h b/src/data/graphelementdata.h index dce0d46..8a50a93 100644 --- a/src/data/graphelementdata.h +++ b/src/data/graphelementdata.h @@ -49,6 +49,7 @@ public: bool hasProperty(QString key); bool atom(QString atom); int indexOfKey(QString key); + void mergeData(GraphElementData *d); bool removeRows(int row, int count, const QModelIndex &parent) override; bool moveRows(const QModelIndex &sourceParent, int sourceRow, int, diff --git a/src/data/path.cpp b/src/data/path.cpp new file mode 100644 index 0000000..f213b22 --- /dev/null +++ b/src/data/path.cpp @@ -0,0 +1,6 @@ +#include "path.h" + +Path::Path(QObject *parent) : QObject(parent) +{ + +} diff --git a/src/data/path.h b/src/data/path.h new file mode 100644 index 0000000..381d486 --- /dev/null +++ b/src/data/path.h @@ -0,0 +1,19 @@ +#ifndef PATH_H +#define PATH_H + +#include "edge.h" + +#include + +class Path : public QObject +{ + Q_OBJECT +public: + explicit Path(QObject *parent = nullptr); + +private: + QVector _edges; + +}; + +#endif // PATH_H diff --git a/src/data/tikzassembler.cpp b/src/data/tikzassembler.cpp index cd0b517..ee75f7b 100644 --- a/src/data/tikzassembler.cpp +++ b/src/data/tikzassembler.cpp @@ -29,6 +29,7 @@ TikzAssembler::TikzAssembler(Graph *graph, QObject *parent) : { yylex_init(&scanner); yyset_extra(this, scanner); + _currentEdgeData = nullptr; } TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : @@ -36,6 +37,7 @@ TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : { yylex_init(&scanner); yyset_extra(this, scanner); + _currentEdgeData = nullptr; } void TikzAssembler::addNodeToMap(Node *n) { _nodeMap.insert(n->name(), n); } @@ -70,3 +72,42 @@ bool TikzAssembler::isTikzStyles() const return _tikzStyles != 0; } +Node *TikzAssembler::currentEdgeSource() const +{ + return _currentEdgeSource; +} + +void TikzAssembler::setCurrentEdgeSource(Node *currentEdgeSource) +{ + _currentEdgeSource = currentEdgeSource; +} + +GraphElementData *TikzAssembler::currentEdgeData() const +{ + return _currentEdgeData; +} + +void TikzAssembler::setCurrentEdgeData(GraphElementData *currentEdgeData) +{ + _currentEdgeData = currentEdgeData; +} + +QString TikzAssembler::currentEdgeSourceAnchor() const +{ + return _currentEdgeSourceAnchor; +} + +void TikzAssembler::setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor) +{ + _currentEdgeSourceAnchor = currentEdgeSourceAnchor; +} + +void TikzAssembler::finishCurrentPath() +{ + if (_currentEdgeData) { + delete _currentEdgeData; + _currentEdgeData = nullptr; + } + // TODO: create a path and add it to graph +} + diff --git a/src/data/tikzassembler.h b/src/data/tikzassembler.h index 7b32224..f3cabb6 100644 --- a/src/data/tikzassembler.h +++ b/src/data/tikzassembler.h @@ -46,6 +46,17 @@ public: bool isTikzStyles() const; + Node *currentEdgeSource() const; + void setCurrentEdgeSource(Node *currentEdgeSource); + + GraphElementData *currentEdgeData() const; + void setCurrentEdgeData(GraphElementData *currentEdgeData); + + QString currentEdgeSourceAnchor() const; + void setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor); + + void finishCurrentPath(); + signals: public slots: @@ -54,6 +65,9 @@ private: QHash _nodeMap; Graph *_graph; TikzStyles *_tikzStyles; + Node *_currentEdgeSource; + GraphElementData *_currentEdgeData; + QString _currentEdgeSourceAnchor; void *scanner; }; diff --git a/src/data/tikzparser.y b/src/data/tikzparser.y index 4473107..895f75d 100644 --- a/src/data/tikzparser.y +++ b/src/data/tikzparser.y @@ -32,13 +32,13 @@ /* we use features added to bison 2.4 */ %require "2.3" -%error-verbose +%define parse.error verbose /* enable maintaining locations for better error messages */ %locations /* the name of the header file */ /*%defines "common/tikzparser.h"*/ /* make it re-entrant (no global variables) */ -%pure-parser +%define api.pure /* We use a pure (re-entrant) lexer. This means yylex will take a void* (opaque) type to maintain its state */ %lex-param {void *scanner} @@ -214,10 +214,13 @@ noderef: "(" REFSTRING optanchor ")" $$.node = assembler->nodeWithName(QString($2)); free($2); $$.anchor = $3; + $$.loop = false; + $$.cycle = false; }; optnoderef: - noderef { $$ = $1; } - | "(" ")" { $$.node = 0; $$.anchor = 0; } + noderef { $$ = $1; } + | "(" ")" { $$.node = 0; $$.anchor = 0; $$.loop = true; $$.cycle = false; } + | "cycle" { $$.node = 0; $$.anchor = 0; $$.loop = false; $$.cycle = true; } optedgenode: { $$ = 0; } | "node" optproperties DELIMITEDSTRING @@ -228,45 +231,68 @@ optedgenode: $$->setLabel(QString($3)); free($3); } -edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";" - { - Node *s; + +edgesource: optproperties noderef { + assembler->setCurrentEdgeSource($2.node); + if ($2.anchor) { + assembler->setCurrentEdgeSourceAnchor(QString($2.anchor)); + free($2.anchor); + } else { + assembler->setCurrentEdgeSourceAnchor(QString()); + } + assembler->setCurrentEdgeData($1); + } + +optedgetargets: edgetarget optedgetargets | + +edgetarget: "to" optproperties optedgenode optnoderef { + Node *s = assembler->currentEdgeSource();; Node *t; - - s = $3.node; - if ($6.node) { - t = $6.node; + if ($4.loop) { + t = assembler->currentEdgeSource(); + } else if ($4.cycle) { + // TODO: should be source of first edge in path + t = assembler->currentEdgeSource(); } else { - t = s; + t = $4.node; } - // if the source or the target of the edge doesn't exist, quietly ignore it. - if (s != 0 && t != 0) { - Edge *edge = new Edge(s, t); - if ($2) { - edge->setData($2); - edge->setAttributesFromData(); - } + if (s != 0 && t != 0) { // if source or target don't exist, quietly ignore edge + Edge *e = new Edge(s, t); + assembler->setCurrentEdgeSource(t); - if ($5) - edge->setEdgeNode($5); - if ($3.anchor) { - edge->setSourceAnchor(QString($3.anchor)); - free($3.anchor); + if (!assembler->currentEdgeSourceAnchor().isEmpty()) { + e->setSourceAnchor(assembler->currentEdgeSourceAnchor()); } - if ($6.node) { - if ($6.anchor) { - edge->setTargetAnchor(QString($6.anchor)); - free($6.anchor); - } + if ($4.anchor) { + QString a($4.anchor); + free($4.anchor); + e->setTargetAnchor(a); + assembler->setCurrentEdgeSourceAnchor(a); } else { - edge->setTargetAnchor(edge->sourceAnchor()); + assembler->setCurrentEdgeSourceAnchor(QString()); } - assembler->graph()->addEdge(edge); + if ($3) e->setEdgeNode($3); + + GraphElementData *cd = assembler->currentEdgeData(); + if ($2) { + if (cd) $2->mergeData(cd); + e->setData($2); + } else { + if (cd) e->setData(cd->copy()); + } + e->setAttributesFromData(); + assembler->graph()->addEdge(e); } + } + + +edge: "\\draw" edgesource edgetarget optedgetargets ";" + { + assembler->finishCurrentPath(); }; ignoreprop: val | val "=" val; diff --git a/src/data/tikzparserdefs.h b/src/data/tikzparserdefs.h index 02743fe..da60bbf 100644 --- a/src/data/tikzparserdefs.h +++ b/src/data/tikzparserdefs.h @@ -33,6 +33,8 @@ struct noderef { Node *node; char *anchor; + bool cycle; + bool loop; }; inline int isatty(int) { return 0; } diff --git a/tikzit.pro b/tikzit.pro index e0f3544..878caaa 100644 --- a/tikzit.pro +++ b/tikzit.pro @@ -53,6 +53,7 @@ include(flex.pri) include(bison.pri) SOURCES += src/gui/mainwindow.cpp \ + src/data/path.cpp \ src/gui/toolpalette.cpp \ src/gui/tikzscene.cpp \ src/data/graph.cpp \ @@ -85,6 +86,7 @@ SOURCES += src/gui/mainwindow.cpp \ src/gui/preferencedialog.cpp HEADERS += src/gui/mainwindow.h \ + src/data/path.h \ src/gui/toolpalette.h \ src/gui/tikzscene.h \ src/data/graph.h \ -- cgit v1.2.3 From d9ec25d1bcea4e45d1965e95bb3099c3864e04a0 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 12 Apr 2020 16:43:44 +0100 Subject: parsing and outputting complex paths --- src/data/edge.cpp | 11 ++++ src/data/edge.h | 8 ++- src/data/graph.cpp | 117 +++++++++++++++++++++++++++++++++--------- src/data/graph.h | 4 ++ src/data/graphelementdata.cpp | 26 ++++++++++ src/data/graphelementdata.h | 4 ++ src/data/path.cpp | 29 +++++++++++ src/data/path.h | 6 +++ src/data/tikzassembler.cpp | 34 +++++++++++- src/data/tikzassembler.h | 5 +- src/data/tikzlexer.l | 1 + src/data/tikzparser.y | 7 +-- src/gui/undocommands.cpp | 8 +-- 13 files changed, 225 insertions(+), 35 deletions(-) diff --git a/src/data/edge.cpp b/src/data/edge.cpp index 0bd49e8..652b480 100644 --- a/src/data/edge.cpp +++ b/src/data/edge.cpp @@ -28,6 +28,7 @@ Edge::Edge(Node *s, Node *t, QObject *parent) : { _data = new GraphElementData(this); _edgeNode = nullptr; + _path = nullptr; _dirty = true; if (s != t) { @@ -436,3 +437,13 @@ QPointF Edge::bezierTangent(qreal start, qreal end) const return QPointF(dx, dy); } + +Path *Edge::path() const +{ + return _path; +} + +void Edge::setPath(Path *path) +{ + _path = path; +} diff --git a/src/data/edge.h b/src/data/edge.h index 954145f..ad21f36 100644 --- a/src/data/edge.h +++ b/src/data/edge.h @@ -27,6 +27,8 @@ #include "node.h" #include "style.h" +class Path; + #include #include @@ -92,12 +94,15 @@ public: void setStyleName(const QString & styleName); Style *style() const; + Path *path() const; + void setPath(Path *path); + signals: public slots: private: - QPointF bezierTangent(qreal start, qreal end) const; + QPointF bezierTangent(qreal start, qreal end) const; QString _sourceAnchor; QString _targetAnchor; @@ -108,6 +113,7 @@ private: // referenced Node *_source; Node *_target; + Path *_path; Style *_style; diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 1dd5574..354d22d 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -73,6 +73,17 @@ void Graph::removeEdge(Edge *e) _edges.removeOne(e); } +void Graph::addPath(Path *p) +{ + p->setParent(this); + _paths << p; +} + +void Graph::removePath(Path *p) +{ + _paths.removeOne(p); +} + int Graph::maxIntName() { int max = -1; @@ -167,6 +178,11 @@ const QVector &Graph::edges() return _edges; } +const QVector &Graph::paths() +{ + return _paths; +} + QRectF Graph::bbox() const { return _bbox; @@ -230,37 +246,90 @@ QString Graph::tikz() Edge *e; + Path *p; foreach (e, _edges) { e->setTikzLine(line); e->updateData(); - code << "\t\t\\draw "; - if (!e->data()->isEmpty()) - code << e->data()->tikz() << " "; - - code << "(" << e->source()->name(); - if (e->sourceAnchor() != "") - code << "." << e->sourceAnchor(); - code << ") to "; + p = e->path(); + if (p) { // if edge is part of a path + if (p->edges().first() == e) { // only add tikz code once per path + code << "\t\t\\draw "; + + GraphElementData *npd = e->data()->nonPathData(); + if (!npd->isEmpty()) + code << npd->tikz() << " "; + delete npd; + + code << "(" << e->source()->name(); + if (e->sourceAnchor() != "") { + code << "." << e->sourceAnchor(); + } else if (p->isCycle()) { + code << ".center"; + } + code << ")"; + + foreach (Edge *e1, p->edges()) { + e1->updateData(); + code << " to "; + + GraphElementData *pd = e1->data()->pathData(); + if (!pd->isEmpty()) + code << pd->tikz() << " "; + delete pd; + + if (e1->hasEdgeNode()) { + code << "node "; + if (!e1->edgeNode()->data()->isEmpty()) + code << e1->edgeNode()->data()->tikz() << " "; + code << "{" << e1->edgeNode()->label() << "} "; + } + + if (e->source() == e1->target()) { + code << "cycle"; + } else { + code << "(" << e1->target()->name(); + if (e1->targetAnchor() != "") { + code << "." << e1->targetAnchor(); + } else if (e1 != p->edges().last()) { + code << ".center"; + } + code << ")"; + } + } + code << ";\n"; + line++; + } + } else { // edge is not part of a path + code << "\t\t\\draw "; + + if (!e->data()->isEmpty()) + code << e->data()->tikz() << " "; + + code << "(" << e->source()->name(); + if (e->sourceAnchor() != "") + code << "." << e->sourceAnchor(); + code << ") to "; + + if (e->hasEdgeNode()) { + code << "node "; + if (!e->edgeNode()->data()->isEmpty()) + code << e->edgeNode()->data()->tikz() << " "; + code << "{" << e->edgeNode()->label() << "} "; + } - if (e->hasEdgeNode()) { - code << "node "; - if (!e->edgeNode()->data()->isEmpty()) - code << e->edgeNode()->data()->tikz() << " "; - code << "{" << e->edgeNode()->label() << "} "; - } + if (e->source() == e->target()) { + code << "()"; + } else { + code << "(" << e->target()->name(); + if (e->targetAnchor() != "") + code << "." << e->targetAnchor(); + code << ")"; + } - if (e->source() == e->target()) { - code << "()"; - } else { - code << "(" << e->target()->name(); - if (e->targetAnchor() != "") - code << "." << e->targetAnchor(); - code << ")"; + code << ";\n"; + line++; } - - code << ";\n"; - line++; } if (!_edges.isEmpty()) { diff --git a/src/data/graph.h b/src/data/graph.h index a996bcb..c306bb2 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -47,6 +47,8 @@ public: void addEdge(Edge *e); void addEdge(Edge *e, int index); void removeEdge(Edge *e); + void addPath(Path *p); + void removePath(Path *p); int maxIntName(); void reorderNodes(const QVector &newOrder); void reorderEdges(const QVector &newOrder); @@ -65,6 +67,7 @@ public: const QVector &nodes(); const QVector &edges(); + const QVector &paths(); QRectF bbox() const; void setBbox(const QRectF &bbox); @@ -122,6 +125,7 @@ public slots: private: QVector _nodes; QVector _edges; + QVector _paths; //QMultiHash inEdges; //QMultiHash outEdges; GraphElementData *_data; diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp index 931f86a..d2146d9 100644 --- a/src/data/graphelementdata.cpp +++ b/src/data/graphelementdata.cpp @@ -265,3 +265,29 @@ QVector GraphElementData::properties() const { return _properties; } + +GraphElementData *GraphElementData::pathData() const +{ + GraphElementData *d = new GraphElementData(); + foreach(GraphElementProperty p, _properties) { + if (isPathProperty(p.key())) d->add(p); + } + return d; +} + +GraphElementData *GraphElementData::nonPathData() const +{ + GraphElementData *d = new GraphElementData(); + foreach(GraphElementProperty p, _properties) { + if (!isPathProperty(p.key())) d->add(p); + } + return d; +} + +bool GraphElementData::isPathProperty(QString key) +{ + return (key == "bend left" || + key == "bend right" || + key == "in" || + key == "out"); +} diff --git a/src/data/graphelementdata.h b/src/data/graphelementdata.h index 8a50a93..8022a14 100644 --- a/src/data/graphelementdata.h +++ b/src/data/graphelementdata.h @@ -78,12 +78,16 @@ public: bool isEmpty(); QVector properties() const; + GraphElementData *pathData() const; + GraphElementData *nonPathData() const; + signals: public slots: private: QVector _properties; + static bool isPathProperty(QString key); }; #endif // GRAPHELEMENTDATA_H diff --git a/src/data/path.cpp b/src/data/path.cpp index f213b22..1438d64 100644 --- a/src/data/path.cpp +++ b/src/data/path.cpp @@ -4,3 +4,32 @@ Path::Path(QObject *parent) : QObject(parent) { } + +int Path::length() const +{ + return _edges.length(); +} + +void Path::addEdge(Edge *e) +{ + e->setPath(this); + _edges << e; +} + +void Path::removeEdges() +{ + foreach(Edge *e, _edges) { + e->setPath(nullptr); + } + _edges.clear(); +} + +bool Path::isCycle() const +{ + return !_edges.isEmpty() && _edges.first()->source() == _edges.last()->target(); +} + +QVector Path::edges() const +{ + return _edges; +} diff --git a/src/data/path.h b/src/data/path.h index 381d486..3c83170 100644 --- a/src/data/path.h +++ b/src/data/path.h @@ -10,6 +10,12 @@ class Path : public QObject Q_OBJECT public: explicit Path(QObject *parent = nullptr); + int length() const; + void addEdge(Edge *e); + void removeEdges(); + bool isCycle() const; + + QVector edges() const; private: QVector _edges; diff --git a/src/data/tikzassembler.cpp b/src/data/tikzassembler.cpp index ee75f7b..3cb3c10 100644 --- a/src/data/tikzassembler.cpp +++ b/src/data/tikzassembler.cpp @@ -30,6 +30,7 @@ TikzAssembler::TikzAssembler(Graph *graph, QObject *parent) : yylex_init(&scanner); yyset_extra(this, scanner); _currentEdgeData = nullptr; + _currentPath = nullptr; } TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : @@ -38,6 +39,7 @@ TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : yylex_init(&scanner); yyset_extra(this, scanner); _currentEdgeData = nullptr; + _currentPath = nullptr; } void TikzAssembler::addNodeToMap(Node *n) { _nodeMap.insert(n->name(), n); } @@ -82,6 +84,15 @@ void TikzAssembler::setCurrentEdgeSource(Node *currentEdgeSource) _currentEdgeSource = currentEdgeSource; } +Node *TikzAssembler::currentPathSource() const +{ + if (_currentPath && _currentPath->length() > 0) { + return _currentPath->edges()[0]->source(); + } else { + return nullptr; + } +} + GraphElementData *TikzAssembler::currentEdgeData() const { return _currentEdgeData; @@ -102,12 +113,31 @@ void TikzAssembler::setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceA _currentEdgeSourceAnchor = currentEdgeSourceAnchor; } +void TikzAssembler::addEdge(Edge *e) +{ + if (!_currentPath) _currentPath = new Path(); + _currentPath->addEdge(e); + _graph->addEdge(e); +} + void TikzAssembler::finishCurrentPath() { if (_currentEdgeData) { - delete _currentEdgeData; + GraphElementData *d = _currentEdgeData; _currentEdgeData = nullptr; + delete d; + } + + if (_currentPath) { + if (_currentPath->length() < 2) { + _currentPath->removeEdges(); + Path *p = _currentPath; + _currentPath = nullptr; + delete p; + } else { + _graph->addPath(_currentPath); + _currentPath = nullptr; + } } - // TODO: create a path and add it to graph } diff --git a/src/data/tikzassembler.h b/src/data/tikzassembler.h index f3cabb6..f86abcd 100644 --- a/src/data/tikzassembler.h +++ b/src/data/tikzassembler.h @@ -23,7 +23,6 @@ #ifndef TIKZASSEMBLER_H #define TIKZASSEMBLER_H -#include "node.h" #include "graph.h" #include "tikzstyles.h" @@ -49,12 +48,15 @@ public: Node *currentEdgeSource() const; void setCurrentEdgeSource(Node *currentEdgeSource); + Node *currentPathSource() const; + GraphElementData *currentEdgeData() const; void setCurrentEdgeData(GraphElementData *currentEdgeData); QString currentEdgeSourceAnchor() const; void setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor); + void addEdge(Edge *e); void finishCurrentPath(); signals: @@ -65,6 +67,7 @@ private: QHash _nodeMap; Graph *_graph; TikzStyles *_tikzStyles; + Path *_currentPath; Node *_currentEdgeSource; GraphElementData *_currentEdgeData; QString _currentEdgeSourceAnchor; diff --git a/src/data/tikzlexer.l b/src/data/tikzlexer.l index 0d80467..615cf0d 100644 --- a/src/data/tikzlexer.l +++ b/src/data/tikzlexer.l @@ -82,6 +82,7 @@ FLOAT \-?[0-9]*(\.[0-9]+)? node { return NODE; } at { return AT; } to { return TO; } +cycle { return CYCLE; } \([ ]*{FLOAT}[ ]*,[ ]*{FLOAT}[ ]*\) { yylloc->last_column = yylloc->first_column + 1; diff --git a/src/data/tikzparser.y b/src/data/tikzparser.y index 895f75d..1e7b8cc 100644 --- a/src/data/tikzparser.y +++ b/src/data/tikzparser.y @@ -96,6 +96,7 @@ void yyerror(YYLTYPE *yylloc, void * /*scanner*/, const char *str) { %token NODE "node" %token AT "at" %token TO "to" +%token CYCLE "cycle" %token SEMICOLON ";" %token COMMA "," @@ -252,8 +253,8 @@ edgetarget: "to" optproperties optedgenode optnoderef { if ($4.loop) { t = assembler->currentEdgeSource(); } else if ($4.cycle) { - // TODO: should be source of first edge in path - t = assembler->currentEdgeSource(); + t = assembler->currentPathSource(); + if (!t) t = s; } else { t = $4.node; } @@ -285,7 +286,7 @@ edgetarget: "to" optproperties optedgenode optnoderef { if (cd) e->setData(cd->copy()); } e->setAttributesFromData(); - assembler->graph()->addEdge(e); + assembler->addEdge(e); } } diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index c5c26af..9a1ef34 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -488,7 +488,7 @@ void ReflectNodesCommand::undo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::undo(); } @@ -501,7 +501,7 @@ void ReflectNodesCommand::redo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::redo(); } @@ -520,7 +520,7 @@ void RotateNodesCommand::undo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::undo(); } @@ -533,7 +533,7 @@ void RotateNodesCommand::redo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::redo(); } -- cgit v1.2.3 From 4ac10e81fc5e514140aeaf9d30aa00c79c91045e Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 12 Apr 2020 20:16:30 +0100 Subject: tikz logo in tikzit --- src/data/graph.cpp | 11 +++++++---- src/data/graphelementdata.cpp | 3 ++- tex/tikzit-logo.tikz | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100755 tex/tikzit-logo.tikz diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 354d22d..24a17a5 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -248,12 +248,11 @@ QString Graph::tikz() Edge *e; Path *p; foreach (e, _edges) { - e->setTikzLine(line); - e->updateData(); - p = e->path(); if (p) { // if edge is part of a path if (p->edges().first() == e) { // only add tikz code once per path + e->setTikzLine(line); + e->updateData(); code << "\t\t\\draw "; GraphElementData *npd = e->data()->nonPathData(); @@ -270,8 +269,10 @@ QString Graph::tikz() code << ")"; foreach (Edge *e1, p->edges()) { + e1->setTikzLine(line); e1->updateData(); - code << " to "; + code << "\n\t\t\t to "; + line++; GraphElementData *pd = e1->data()->pathData(); if (!pd->isEmpty()) @@ -301,6 +302,8 @@ QString Graph::tikz() line++; } } else { // edge is not part of a path + e->setTikzLine(line); + e->updateData(); code << "\t\t\\draw "; if (!e->data()->isEmpty()) diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp index d2146d9..e1e89b1 100644 --- a/src/data/graphelementdata.cpp +++ b/src/data/graphelementdata.cpp @@ -289,5 +289,6 @@ bool GraphElementData::isPathProperty(QString key) return (key == "bend left" || key == "bend right" || key == "in" || - key == "out"); + key == "out" || + key == "looseness"); } diff --git a/tex/tikzit-logo.tikz b/tex/tikzit-logo.tikz new file mode 100755 index 0000000..4cc1b06 --- /dev/null +++ b/tex/tikzit-logo.tikz @@ -0,0 +1,42 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (5) at (-0.5, -1.25) {}; + \node [style=none] (6) at (0.5, 1.25) {}; + \node [style=none] (7) at (1.25, 0.5) {}; + \node [style=none] (8) at (1.25, 2) {}; + \node [style=none] (9) at (2, 1.25) {}; + \node [style=none] (10) at (2.75, 2) {}; + \node [style=none] (11) at (2, 2.75) {}; + \node [style=none] (18) at (1.25, -2) {}; + \node [style=none] (20) at (2.75, -2) {}; + \node [style=none] (22) at (2, -2.75) {}; + \node [style=none] (23) at (2, -1.25) {}; + \node [style=none] (26) at (-2.75, -2) {}; + \node [style=none] (28) at (-1.25, -2) {}; + \node [style=none] (30) at (-2, -2.75) {}; + \node [style=none] (31) at (-2, -1.25) {}; + \node [style=none] (32) at (0.5, -1.25) {}; + \node [style=none] (33) at (-1.25, -0.5) {}; + \node [style=none] (35) at (1.25, -0.5) {}; + \node [style=none] (36) at (-0.5, 1.25) {}; + \node [style=none] (37) at (-1.25, 0.5) {}; + \node [style=none] (65) at (0, 3) {}; + \node [style=none] (69) at (0.775, -2.9) {}; + \node [style=none] (70) at (2, -3.5) {}; + \node [style=none] (71) at (3.5, -2) {}; + \node [style=none] (72) at (2.9, -0.775) {}; + \node [style=none] (80) at (-3, 0) {}; + \node [style=none] (81) at (-0.775, -2.9) {}; + \node [style=none] (82) at (-2, -3.5) {}; + \node [style=none] (83) at (-3.5, -2) {}; + \node [style=none] (84) at (-2.9, -0.775) {}; + \node [style=none] (85) at (0.775, 2.9) {}; + \node [style=none] (86) at (2, 3.5) {}; + \node [style=none] (87) at (3.5, 2) {}; + \node [style=none] (88) at (2.9, 0.775) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [fill=black] (36.center) to [in=135, out=45] (6.center) to [in=-90, out=-45, looseness=1.25] (8.center) to [in=-180, out=90] (11.center) to [in=90, out=0] (10.center) to [in=0, out=-90] (9.center) to [in=135, out=180, looseness=1.25] (7.center) to [in=45, out=-45] (35.center) to [in=180, out=-135, looseness=1.25] (23.center) to [in=90, out=0] (20.center) to [in=0, out=-90] (22.center) to [in=-90, out=180] (18.center) to [in=45, out=90, looseness=1.25] (32.center) to [in=-45, out=-135, looseness=1.25] (5.center) to [in=90, out=135, looseness=1.25] (28.center) to [in=0, out=-90] (30.center) to [in=-90, out=180] (26.center) to [in=-180, out=90] (31.center) to [in=-45, out=0, looseness=1.25] (33.center) to [in=-135, out=135] (37.center) to cycle; + \draw (72.center) to [in=90, out=-30, looseness=0.75] (71.center) to [in=0, out=-90] (70.center) to [in=-60, out=180, looseness=0.75] (69.center) to [in=-15, out=-165] (81.center) to [in=0, out=-120, looseness=0.75] (82.center) to [in=-90, out=180] (83.center) to [in=-150, out=90, looseness=0.75] (84.center) to [in=-90, out=105, looseness=0.75] (80.center) to [in=-180, out=90] (65.center) to [in=165, out=0, looseness=0.75] (85.center) to [in=-180, out=60, looseness=0.75] (86.center) to [in=90, out=0] (87.center) to [in=30, out=-90, looseness=0.75] (88.center) to [in=75, out=-75] cycle; + \end{pgfonlayer} +\end{tikzpicture} -- cgit v1.2.3 From b48731917964d263175716a29a58d965cb726798 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 13 Apr 2020 14:39:51 +0100 Subject: logo tikzstyles --- tex/logo.tikzstyles | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tex/logo.tikzstyles diff --git a/tex/logo.tikzstyles b/tex/logo.tikzstyles new file mode 100644 index 0000000..4e05673 --- /dev/null +++ b/tex/logo.tikzstyles @@ -0,0 +1,11 @@ +% TiKZ style file generated by TikZiT. You may edit this file manually, +% but some things (e.g. comments) may be overwritten. To be readable in +% TikZiT, the only non-comment lines must be of the form: +% \tikzstyle{NAME}=[PROPERTY LIST] + +% Node styles + +% Edge styles +\tikzstyle{bg}=[-, line width=0.5mm, fill={rgb,255: red,217; green,217; blue,217}] +\tikzstyle{fg}=[-, fill=black] +\tikzstyle{dir}=[->] -- cgit v1.2.3 From 6dda16a24dfe7cbd0d90b77c57f1cf789210feb5 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 13 Apr 2020 14:40:07 +0100 Subject: ability to make paths from edges --- src/data/graph.cpp | 4 +- src/data/tikzdocument.cpp | 1 + src/gui/mainmenu.cpp | 18 ++++++++- src/gui/mainmenu.h | 2 + src/gui/mainmenu.ui | 25 +++++++++++++ src/gui/tikzscene.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++++++ src/gui/tikzscene.h | 2 + src/gui/undocommands.cpp | 48 ++++++++++++++++++++++++ src/gui/undocommands.h | 16 ++++++++ tex/tikzit-logo.tikz | 38 ++++++++++++++++++- 10 files changed, 242 insertions(+), 6 deletions(-) diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 24a17a5..979423e 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -269,10 +269,10 @@ QString Graph::tikz() code << ")"; foreach (Edge *e1, p->edges()) { - e1->setTikzLine(line); - e1->updateData(); code << "\n\t\t\t to "; line++; + e1->setTikzLine(line); + e1->updateData(); GraphElementData *pd = e1->data()->pathData(); if (!pd->isEmpty()) diff --git a/src/data/tikzdocument.cpp b/src/data/tikzdocument.cpp index 1099779..633242d 100644 --- a/src/data/tikzdocument.cpp +++ b/src/data/tikzdocument.cpp @@ -90,6 +90,7 @@ void TikzDocument::open(QString fileName) refreshTikz(); setClean(); } else { + // TODO: should not quietly fail to open newGraph->deleteLater(); _parseSuccess = false; } diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp index 7b7623b..092d8b4 100644 --- a/src/gui/mainmenu.cpp +++ b/src/gui/mainmenu.cpp @@ -239,6 +239,18 @@ void MainMenu::on_actionMerge_Nodes_triggered() tikzit->activeWindow()->tikzScene()->mergeNodes(); } +void MainMenu::on_actionMake_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->makePath(); +} + +void MainMenu::on_actionSplit_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->splitPath(); +} + // Tikz void MainMenu::on_actionParse_triggered() @@ -333,8 +345,10 @@ void MainMenu::on_actionZoom_Out_triggered() void MainMenu::on_actionShow_Node_Labels_triggered() { - tikzit->activeWindow()->tikzScene()->setDrawNodeLabels(ui.actionShow_Node_Labels->isChecked()); - tikzit->activeWindow()->tikzScene()->invalidate(); + if (tikzit->activeWindow() != 0) { + tikzit->activeWindow()->tikzScene()->setDrawNodeLabels(ui.actionShow_Node_Labels->isChecked()); + tikzit->activeWindow()->tikzScene()->invalidate(); + } } void MainMenu::on_actionAbout_triggered() diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h index 8268802..431e43a 100644 --- a/src/gui/mainmenu.h +++ b/src/gui/mainmenu.h @@ -67,6 +67,8 @@ public slots: void on_actionExtendRight_triggered(); void on_actionReverse_Edge_Direction_triggered(); void on_actionMerge_Nodes_triggered(); + void on_actionMake_Path_triggered(); + void on_actionSplit_Path_triggered(); // Tools void on_actionParse_triggered(); diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui index 46f4881..2e390f9 100644 --- a/src/gui/mainmenu.ui +++ b/src/gui/mainmenu.ui @@ -58,6 +58,13 @@ + + + Path + + + + @@ -74,6 +81,8 @@ + + @@ -460,6 +469,22 @@ Ctrl+Shift+Space + + + Make Path + + + Ctrl+P + + + + + Split Path + + + Ctrl+Shift+P + + diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index 087f621..cebf5be 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,99 @@ void TikzScene::reverseSelectedEdges() _tikzDocument->undoStack()->push(cmd); } +void TikzScene::makePath() +{ + QSet selNodes; + QSet edges; + getSelection(selNodes, edges); + + // if no edges are selected, try to infer edges from nodes + if (edges.isEmpty()) { + foreach(Edge *e, graph()->edges()) { + if (selNodes.contains(e->source()) && selNodes.contains(e->target())) + edges << e; + } + } + + if (edges.size() < 2) { + //QMessageBox::warning(nullptr, "Error", "Paths must contain at least 2 edges."); + return; + } + + foreach (Edge *e, edges) { + if (e->path() != nullptr) { + //QMessageBox::warning(nullptr, "Error", "Edges must not already be in another path."); + // TODO: maybe we want to automatically split paths if edges are in a path already? + return; + } + } + + // try to turn selected edges into one contiguous chain or cycle, recording + // which edges need to be flipped. + + // n.b. this is O(n^2) in path length. This could be optimised by saving + // vertex neighbourhoods, but probably doesn't win anything for n < 100. + + QSet flip; + QVector p; + int pLen = -1; + + // keep going as long as 'p' grows + while (pLen < p.length()) { + pLen = p.length(); + Edge *e = nullptr; + foreach (e, edges) { + Node *s = e->source(); + Node *t = e->target(); + if (p.isEmpty()) { + p.append(e); + break; + } + + Node *head = (flip.contains(p.first())) ? p.first()->target() : p.first()->source(); + Node *tail = (flip.contains(p.last())) ? p.last()->source() : p.last()->target(); + + if (s == head || t == head) { + if (s == head) flip << e; + p.prepend(e); + break; + } + + if (s == tail || t == tail) { + if (t == tail) flip << e; + p.append(e); + break; + } + } + + if (e) edges.remove(e); + } + + if (!edges.isEmpty()) { + QMessageBox::warning(nullptr, "Error", "Selected edges do not form a path."); + return; + } + + //qDebug() << p; + //qDebug() << flip; + + QMap oldEdgeData; + foreach (Edge *e, p) { + if (e != p.first()) oldEdgeData[e] = e->data()->copy(); + } + qDebug() << oldEdgeData; + + _tikzDocument->undoStack()->beginMacro("Make Path"); + _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); + _tikzDocument->undoStack()->push(new MakePathCommand(this, p, oldEdgeData)); + _tikzDocument->undoStack()->endMacro(); +} + +void TikzScene::splitPath() +{ + // TODO: stub +} + void TikzScene::refreshZIndices() { qreal z = 0.0; diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index 2e7baa5..5996263 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -81,6 +81,8 @@ public: void reverseSelectedEdges(); + void makePath(); + void splitPath(); void getSelection(QSet &selNodes, QSet &selEdges) const; QSet getSelectedNodes() const; diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index 9a1ef34..a07f251 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -594,3 +594,51 @@ void ReverseEdgesCommand::redo() GraphUpdateCommand::redo(); } + +MakePathCommand::MakePathCommand(TikzScene *scene, + const QVector &edgeList, + const QMap &oldEdgeData, + QUndoCommand *parent) : + GraphUpdateCommand(scene, parent), + _edgeList(edgeList), _oldEdgeData(oldEdgeData) +{ +} + +void MakePathCommand::undo() +{ + Path *p = _edgeList.first()->path(); + p->removeEdges(); + _scene->graph()->removePath(p); + + foreach (Edge *e, _edgeList) { + if (e != _edgeList.first()) { + // setData transfers ownership, so make a copy + e->setData(_oldEdgeData[e]->copy()); + } + } + + GraphUpdateCommand::undo(); +} + +void MakePathCommand::redo() +{ + GraphElementData *npd = _edgeList.first()->data()->nonPathData(); + GraphElementData *d; + + Path *p = new Path(); + foreach (Edge *e, _edgeList) { + p->addEdge(e); + + if (e != _edgeList.first()) { + d = e->data()->pathData(); + d->mergeData(npd); + e->setData(d); + } + } + + delete npd; + + _scene->graph()->addPath(p); + + GraphUpdateCommand::redo(); +} diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h index 40f0a3b..a1daa07 100644 --- a/src/gui/undocommands.h +++ b/src/gui/undocommands.h @@ -255,4 +255,20 @@ private: QVector _newEdgeOrder; }; +class MakePathCommand : public GraphUpdateCommand +{ +public: + explicit MakePathCommand(TikzScene *scene, + const QVector &edgeList, + const QMap &oldEdgeData, + QUndoCommand *parent = nullptr); + void undo() override; + void redo() override; +private: + QVector _edgeList; + + // creating path clobbers data on all but first edge + QMap _oldEdgeData; +}; + #endif // UNDOCOMMANDS_H diff --git a/tex/tikzit-logo.tikz b/tex/tikzit-logo.tikz index 4cc1b06..be1eb66 100755 --- a/tex/tikzit-logo.tikz +++ b/tex/tikzit-logo.tikz @@ -36,7 +36,41 @@ \node [style=none] (88) at (2.9, 0.775) {}; \end{pgfonlayer} \begin{pgfonlayer}{edgelayer} - \draw [fill=black] (36.center) to [in=135, out=45] (6.center) to [in=-90, out=-45, looseness=1.25] (8.center) to [in=-180, out=90] (11.center) to [in=90, out=0] (10.center) to [in=0, out=-90] (9.center) to [in=135, out=180, looseness=1.25] (7.center) to [in=45, out=-45] (35.center) to [in=180, out=-135, looseness=1.25] (23.center) to [in=90, out=0] (20.center) to [in=0, out=-90] (22.center) to [in=-90, out=180] (18.center) to [in=45, out=90, looseness=1.25] (32.center) to [in=-45, out=-135, looseness=1.25] (5.center) to [in=90, out=135, looseness=1.25] (28.center) to [in=0, out=-90] (30.center) to [in=-90, out=180] (26.center) to [in=-180, out=90] (31.center) to [in=-45, out=0, looseness=1.25] (33.center) to [in=-135, out=135] (37.center) to cycle; - \draw (72.center) to [in=90, out=-30, looseness=0.75] (71.center) to [in=0, out=-90] (70.center) to [in=-60, out=180, looseness=0.75] (69.center) to [in=-15, out=-165] (81.center) to [in=0, out=-120, looseness=0.75] (82.center) to [in=-90, out=180] (83.center) to [in=-150, out=90, looseness=0.75] (84.center) to [in=-90, out=105, looseness=0.75] (80.center) to [in=-180, out=90] (65.center) to [in=165, out=0, looseness=0.75] (85.center) to [in=-180, out=60, looseness=0.75] (86.center) to [in=90, out=0] (87.center) to [in=30, out=-90, looseness=0.75] (88.center) to [in=75, out=-75] cycle; + \draw [style=bg] (72.center) + to [in=90, out=-30, looseness=0.75] (71.center) + to [in=0, out=-90] (70.center) + to [in=-60, out=180, looseness=0.75] (69.center) + to [in=-15, out=-165] (81.center) + to [in=0, out=-120, looseness=0.75] (82.center) + to [in=-90, out=180] (83.center) + to [in=-150, out=90, looseness=0.75] (84.center) + to [in=-90, out=105, looseness=0.75] (80.center) + to [in=-180, out=90] (65.center) + to [in=165, out=0, looseness=0.75] (85.center) + to [in=-180, out=60, looseness=0.75] (86.center) + to [in=90, out=0] (87.center) + to [in=30, out=-90, looseness=0.75] (88.center) + to [in=75, out=-75] cycle; + \draw [style=fg] (36.center) + to [in=135, out=45] (6.center) + to [in=-90, out=-45, looseness=1.25] (8.center) + to [in=-180, out=90] (11.center) + to [in=90, out=0] (10.center) + to [in=0, out=-90] (9.center) + to [in=135, out=180, looseness=1.25] (7.center) + to [in=45, out=-45] (35.center) + to [in=180, out=-135, looseness=1.25] (23.center) + to [in=90, out=0] (20.center) + to [in=0, out=-90] (22.center) + to [in=-90, out=180] (18.center) + to [in=45, out=90, looseness=1.25] (32.center) + to [in=-45, out=-135, looseness=1.25] (5.center) + to [in=90, out=135, looseness=1.25] (28.center) + to [in=0, out=-90] (30.center) + to [in=-90, out=180] (26.center) + to [in=-180, out=90] (31.center) + to [in=-45, out=0, looseness=1.25] (33.center) + to [in=-135, out=135] (37.center) + to cycle; \end{pgfonlayer} \end{tikzpicture} -- cgit v1.2.3 From 732499eead71b23a7ba551fe788b47a4cf45124e Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 13 Apr 2020 16:21:39 +0100 Subject: adding and drawing paths works --- src/gui/edgeitem.cpp | 87 +++++++++++++++++++++++++----------------------- src/gui/pathitem.cpp | 65 ++++++++++++++++++++++++++++++++++++ src/gui/pathitem.h | 28 ++++++++++++++++ src/gui/tikzscene.cpp | 36 +++++++++++++++++++- src/gui/tikzscene.h | 3 ++ src/gui/undocommands.cpp | 25 ++++++++++++++ tikzit.pro | 2 ++ 7 files changed, 203 insertions(+), 43 deletions(-) create mode 100644 src/gui/pathitem.cpp create mode 100644 src/gui/pathitem.h diff --git a/src/gui/edgeitem.cpp b/src/gui/edgeitem.cpp index 45ae159..675ddd7 100644 --- a/src/gui/edgeitem.cpp +++ b/src/gui/edgeitem.cpp @@ -71,7 +71,8 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge QPen pen = _edge->style()->pen(); painter->setPen(pen); painter->setBrush(Qt::NoBrush); - painter->drawPath(path()); + + if (!_edge->path()) painter->drawPath(path()); QPointF ht = _edge->headTangent(); QPointF hLeft(-ht.y(), ht.x()); @@ -83,27 +84,27 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge pen.setStyle(Qt::SolidLine); painter->setPen(pen); - - - switch (_edge->style()->arrowHead()) { - case Style::Flat: - { - painter->drawLine( - toScreen(_edge->head() + hLeft), - toScreen(_edge->head() + hRight)); - break; - } - case Style::Pointer: - { - QPainterPath pth; - pth.moveTo(toScreen(_edge->head() + ht + hLeft)); - pth.lineTo(toScreen(_edge->head())); - pth.lineTo(toScreen(_edge->head() + ht + hRight)); - painter->drawPath(pth); - break; - } - case Style::NoTip: - break; + if (!_edge->path() || _edge->path()->edges().last() == _edge) { + switch (_edge->style()->arrowHead()) { + case Style::Flat: + { + painter->drawLine( + toScreen(_edge->head() + hLeft), + toScreen(_edge->head() + hRight)); + break; + } + case Style::Pointer: + { + QPainterPath pth; + pth.moveTo(toScreen(_edge->head() + ht + hLeft)); + pth.lineTo(toScreen(_edge->head())); + pth.lineTo(toScreen(_edge->head() + ht + hRight)); + painter->drawPath(pth); + break; + } + case Style::NoTip: + break; + } } //QPen outline = QPen(Qt::red); @@ -111,26 +112,28 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge //painter->drawPath(_expPath); //painter->setPen(pen); - switch (_edge->style()->arrowTail()) { - case Style::Flat: - { - painter->drawLine( - toScreen(_edge->tail() + tLeft), - toScreen(_edge->tail() + tRight)); - break; - } - case Style::Pointer: - { - QPainterPath pth; - pth.moveTo(toScreen(_edge->tail() + tt + tLeft)); - pth.lineTo(toScreen(_edge->tail())); - pth.lineTo(toScreen(_edge->tail() + tt + tRight)); - painter->drawPath(pth); - break; - } - case Style::NoTip: - break; - } + if (!_edge->path() || _edge->path()->edges().first() == _edge) { + switch (_edge->style()->arrowTail()) { + case Style::Flat: + { + painter->drawLine( + toScreen(_edge->tail() + tLeft), + toScreen(_edge->tail() + tRight)); + break; + } + case Style::Pointer: + { + QPainterPath pth; + pth.moveTo(toScreen(_edge->tail() + tt + tLeft)); + pth.lineTo(toScreen(_edge->tail())); + pth.lineTo(toScreen(_edge->tail() + tt + tRight)); + painter->drawPath(pth); + break; + } + case Style::NoTip: + break; + } + } if (isSelected()) { QColor draw; diff --git a/src/gui/pathitem.cpp b/src/gui/pathitem.cpp new file mode 100644 index 0000000..107281f --- /dev/null +++ b/src/gui/pathitem.cpp @@ -0,0 +1,65 @@ +#include "pathitem.h" +#include "tikzit.h" + +PathItem::PathItem(Path *path) +{ + _path = path; + readPos(); +} + +void PathItem::readPos() +{ + QPainterPath painterPath; + + foreach (Edge *e, _path->edges()) { + e->updateControls(); + + if (e == _path->edges().first()) + painterPath.moveTo (toScreen(e->tail())); + + if (e->bend() != 0 || !e->basicBendMode()) { + painterPath.cubicTo(toScreen(e->cp1()), + toScreen(e->cp2()), + toScreen(e->head())); + } else { + painterPath.lineTo(toScreen(e->head())); + } + } + + setPainterPath(painterPath); +} + +void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + Style *st = _path->edges().first()->style(); + QPen pen = st->pen(); + painter->setPen(st->pen()); + painter->setBrush(st->brush()); + painter->drawPath(painterPath()); +} + +Path *PathItem::path() const +{ + return _path; +} + +QPainterPath PathItem::painterPath() const +{ + return _painterPath; +} + +void PathItem::setPainterPath(const QPainterPath &painterPath) +{ + prepareGeometryChange(); + + _painterPath = painterPath; + float r = GLOBAL_SCALEF * 0.1; + _boundingRect = _painterPath.boundingRect().adjusted(-r,-r,r,r); + + update(); +} + +QRectF PathItem::boundingRect() const +{ + return _boundingRect; +} diff --git a/src/gui/pathitem.h b/src/gui/pathitem.h new file mode 100644 index 0000000..7dc9c80 --- /dev/null +++ b/src/gui/pathitem.h @@ -0,0 +1,28 @@ +#ifndef PATHITEM_H +#define PATHITEM_H + +#include "path.h" + +#include + +class PathItem : public QGraphicsItem +{ +public: + PathItem(Path *path); + void readPos(); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override; + + Path *path() const; + + QPainterPath painterPath() const; + void setPainterPath(const QPainterPath &painterPath); + + QRectF boundingRect() const override; + +private: + Path *_path; + QPainterPath _painterPath; + QRectF _boundingRect; +}; + +#endif // PATHITEM_H diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index cebf5be..da52277 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -97,12 +97,25 @@ void TikzScene::graphReplaced() } _edgeItems.clear(); + foreach (PathItem *pi, _pathItems) { + removeItem(pi); + delete pi; + } + _pathItems.clear(); + foreach (Edge *e, graph()->edges()) { //e->attachStyle(); //e->updateControls(); EdgeItem *ei = new EdgeItem(e); _edgeItems.insert(e, ei); addItem(ei); + + Path *p = e->path(); + if (p && p->edges().first() == e) { + PathItem *pi = new PathItem(p); + _pathItems.insert(p, pi); + addItem(pi); + } } foreach (Node *n, graph()->nodes()) { @@ -393,7 +406,12 @@ void TikzScene::refreshZIndices() { qreal z = 0.0; foreach (Edge *e, graph()->edges()) { - edgeItems()[e]->setZValue(z); + if (e->path() && e == e->path()->edges().first()) { + pathItems()[e->path()]->setZValue(z); + edgeItems()[e]->setZValue(z + 0.1); + } else { + edgeItems()[e]->setZValue(z); + } z += 1.0; } @@ -606,6 +624,8 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } _modifyEdgeItem->readPos(); + Path *p = _modifyEdgeItem->edge()->path(); + if (p) pathItems()[p]->readPos(); } else if (_draggingNodes) { // nodes being dragged QGraphicsScene::mouseMoveEvent(event); @@ -1253,6 +1273,8 @@ void TikzScene::refreshSceneBounds() { void TikzScene::refreshAdjacentEdges(QList nodes) { if (nodes.empty()) return; + + QSet paths; foreach (Edge *e, _edgeItems.keys()) { EdgeItem *ei = _edgeItems[e]; @@ -1263,6 +1285,13 @@ void TikzScene::refreshAdjacentEdges(QList nodes) ei->readPos(); } } + + // only update paths once + Path *p = ei->edge()->path(); + if (p && !paths.contains(p)) { + pathItems()[p]->readPos(); + paths << p; + } } } @@ -1289,3 +1318,8 @@ QMap &TikzScene::edgeItems() { return _edgeItems; } + +QMap &TikzScene::pathItems() +{ + return _pathItems; +} diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index 5996263..2e72faf 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -27,6 +27,7 @@ #include "graph.h" #include "nodeitem.h" #include "edgeitem.h" +#include "pathitem.h" #include "tikzdocument.h" #include "toolpalette.h" #include "stylepalette.h" @@ -48,6 +49,7 @@ public: Graph *graph(); QMap &nodeItems(); QMap &edgeItems(); + QMap &pathItems(); void refreshAdjacentEdges(QList nodes); // void setBounds(QRectF bounds); @@ -112,6 +114,7 @@ private: StylePalette *_styles; QMap _nodeItems; QMap _edgeItems; + QMap _pathItems; QGraphicsLineItem *_drawEdgeItem; QGraphicsRectItem *_rubberBandItem; EdgeItem *_modifyEdgeItem; diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index a07f251..87d5a13 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -102,6 +102,9 @@ void EdgeBendCommand::undo() foreach(EdgeItem *ei, _scene->edgeItems()) { if (ei->edge() == _edge) { ei->readPos(); + + Path *p = ei->edge()->path(); + if (p) _scene->pathItems()[p]->readPos(); break; } } @@ -118,6 +121,9 @@ void EdgeBendCommand::redo() foreach(EdgeItem *ei, _scene->edgeItems()) { if (ei->edge() == _edge) { ei->readPos(); + + Path *p = ei->edge()->path(); + if (p) _scene->pathItems()[p]->readPos(); break; } } @@ -287,6 +293,9 @@ void ChangeEdgeModeCommand::undo() // FIXME: this act strangely sometimes _edge->setBasicBendMode(!_edge->basicBendMode()); _scene->edgeItems()[_edge]->readPos(); + Path *p = _edge->path(); + if (p) _scene->pathItems()[p]->readPos(); + GraphUpdateCommand::undo(); } @@ -294,6 +303,9 @@ void ChangeEdgeModeCommand::redo() { _edge->setBasicBendMode(!_edge->basicBendMode()); _scene->edgeItems()[_edge]->readPos(); + Path *p = _edge->path(); + if (p) _scene->pathItems()[p]->readPos(); + GraphUpdateCommand::redo(); } @@ -607,6 +619,12 @@ MakePathCommand::MakePathCommand(TikzScene *scene, void MakePathCommand::undo() { Path *p = _edgeList.first()->path(); + + PathItem *pi = _scene->pathItems()[p]; + _scene->removeItem(pi); + _scene->pathItems().remove(p); + delete pi; + p->removeEdges(); _scene->graph()->removePath(p); @@ -617,6 +635,7 @@ void MakePathCommand::undo() } } + _scene->refreshZIndices(); GraphUpdateCommand::undo(); } @@ -640,5 +659,11 @@ void MakePathCommand::redo() _scene->graph()->addPath(p); + PathItem *pi = new PathItem(p); + _scene->addItem(pi); + _scene->pathItems().insert(p, pi); + pi->readPos(); + + _scene->refreshZIndices(); GraphUpdateCommand::redo(); } diff --git a/tikzit.pro b/tikzit.pro index 878caaa..46a411a 100644 --- a/tikzit.pro +++ b/tikzit.pro @@ -54,6 +54,7 @@ include(bison.pri) SOURCES += src/gui/mainwindow.cpp \ src/data/path.cpp \ + src/gui/pathitem.cpp \ src/gui/toolpalette.cpp \ src/gui/tikzscene.cpp \ src/data/graph.cpp \ @@ -87,6 +88,7 @@ SOURCES += src/gui/mainwindow.cpp \ HEADERS += src/gui/mainwindow.h \ src/data/path.h \ + src/gui/pathitem.h \ src/gui/toolpalette.h \ src/gui/tikzscene.h \ src/data/graph.h \ -- cgit v1.2.3 From e333ef9cb80e40637235198f9c113fa8cfe0fceb Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 13 Apr 2020 16:54:08 +0100 Subject: split path works --- src/gui/tikzscene.cpp | 20 +++++++++++++++++++- src/gui/undocommands.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/undocommands.h | 15 +++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index da52277..57ddb48 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -399,7 +399,25 @@ void TikzScene::makePath() void TikzScene::splitPath() { - // TODO: stub + QSet selNodes; + QSet edges; + getSelection(selNodes, edges); + + // if no edges are selected, try to infer edges from nodes + if (edges.isEmpty()) { + foreach(Edge *e, graph()->edges()) { + if (selNodes.contains(e->source()) && selNodes.contains(e->target())) + edges << e; + } + } + + QVector paths; + foreach (Edge *e, edges) { + Path *p = e->path(); + if (p && !paths.contains(p)) paths << p; + } + + _tikzDocument->undoStack()->push(new SplitPathCommand(this, paths)); } void TikzScene::refreshZIndices() diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index 87d5a13..9771f44 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -667,3 +667,47 @@ void MakePathCommand::redo() _scene->refreshZIndices(); GraphUpdateCommand::redo(); } + +SplitPathCommand::SplitPathCommand(TikzScene *scene, + const QVector &paths, + QUndoCommand *parent) : + GraphUpdateCommand(scene, parent), _paths(paths) +{ + foreach (Path *p, paths) _edgeLists << p->edges(); +} + +void SplitPathCommand::undo() +{ + for (int i = 0; i < _paths.length(); ++i) { + Path *p = _paths[i]; + foreach (Edge *e, _edgeLists[i]) { + p->addEdge(e); + } + + _scene->graph()->addPath(p); + + PathItem *pi = new PathItem(p); + _scene->addItem(pi); + _scene->pathItems().insert(p, pi); + pi->readPos(); + } + + _scene->refreshZIndices(); + GraphUpdateCommand::undo(); +} + +void SplitPathCommand::redo() +{ + foreach (Path *p, _paths) { + PathItem *pi = _scene->pathItems()[p]; + _scene->removeItem(pi); + _scene->pathItems().remove(p); + delete pi; + + p->removeEdges(); + _scene->graph()->removePath(p); + } + + _scene->refreshZIndices(); + GraphUpdateCommand::redo(); +} diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h index a1daa07..00b9a40 100644 --- a/src/gui/undocommands.h +++ b/src/gui/undocommands.h @@ -271,4 +271,19 @@ private: QMap _oldEdgeData; }; +class SplitPathCommand : public GraphUpdateCommand +{ +public: + explicit SplitPathCommand(TikzScene *scene, + const QVector &paths, + QUndoCommand *parent = nullptr); + void undo() override; + void redo() override; +private: + QVector _paths; + + // keep a copy of the edge lists so they can be added back to each path in undo() + QVector> _edgeLists; +}; + #endif // UNDOCOMMANDS_H -- cgit v1.2.3 From 5ca0a2e48d0198102dabad4af5048a53e6938fe8 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 13 Apr 2020 22:30:51 +0100 Subject: automatically expact selection to path --- src/data/style.cpp | 4 +++- src/gui/tikzscene.cpp | 34 ++++++++++++++++++++++++++++++---- src/gui/tikzscene.h | 1 + 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/data/style.cpp b/src/data/style.cpp index d0f011d..fe33ee0 100644 --- a/src/data/style.cpp +++ b/src/data/style.cpp @@ -62,7 +62,9 @@ QColor Style::fillColor(bool tikzitOverride) const QBrush Style::brush() const { - return QBrush(fillColor()); + QString col = propertyWithDefault("fill", "none", true); + if (col == "none") return Qt::NoBrush; + else return QBrush(tikzit->colorByName(col)); } QString Style::shape(bool tikzitOverride) const diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index 57ddb48..e510931 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -389,7 +389,6 @@ void TikzScene::makePath() foreach (Edge *e, p) { if (e != p.first()) oldEdgeData[e] = e->data()->copy(); } - qDebug() << oldEdgeData; _tikzDocument->undoStack()->beginMacro("Make Path"); _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); @@ -448,6 +447,7 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) _mouseDownPos = event->scenePos(); _draggingNodes = false; + _selectingEdge = nullptr; // radius of a control point for bezier edges, in scene coordinates qreal cpR = GLOBAL_SCALEF * (0.1); @@ -528,9 +528,19 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) } } - auto its = items(_mouseDownPos); - if (!its.isEmpty() && dynamic_cast(its[0])) - _draggingNodes = true; + QList its = items(_mouseDownPos); + if (!its.isEmpty()) { + if (dynamic_cast(its[0])) { + _draggingNodes = true; + } else { + foreach (QGraphicsItem *gi, its) { + if (EdgeItem *ei = dynamic_cast(gi)) { + _selectingEdge = ei->edge(); + break; + } + } + } + } } break; @@ -730,6 +740,22 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) // otherwise, process mouse move normally QGraphicsScene::mouseReleaseEvent(event); + if (_selectingEdge) { + bool sel = edgeItems()[_selectingEdge]->isSelected(); + Path *p = _selectingEdge->path(); + if (p) { + foreach (Edge *e, p->edges()) { + if (e != _selectingEdge) + edgeItems()[e]->setSelected(sel); + nodeItems()[e->source()]->setSelected(sel); + nodeItems()[e->target()]->setSelected(sel); + } + } else { + nodeItems()[_selectingEdge->source()]->setSelected(sel); + nodeItems()[_selectingEdge->target()]->setSelected(sel); + } + } + if (_rubberBandItem->isVisible()) { QPainterPath sel; sel.addRect(_rubberBandItem->rect()); diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index 2e72faf..3f035e2 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -118,6 +118,7 @@ private: QGraphicsLineItem *_drawEdgeItem; QGraphicsRectItem *_rubberBandItem; EdgeItem *_modifyEdgeItem; + Edge *_selectingEdge; NodeItem *_edgeStartNodeItem; NodeItem *_edgeEndNodeItem; bool _firstControlPoint; -- cgit v1.2.3 From 08ce6c91fd37c904362a5ec6f054bcd3ee061b74 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 18 Apr 2020 15:09:39 +0100 Subject: fixed pasting for paths --- src/data/graph.cpp | 26 +++++++++++++++++++++++++- src/gui/undocommands.cpp | 16 ++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 979423e..74de24d 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -360,9 +360,33 @@ Graph *Graph::copyOfSubgraphWithNodes(QSet nds) g->addNode(n1); } } + + QMap edgeTable; foreach (Edge *e, edges()) { if (nds.contains(e->source()) && nds.contains(e->target())) { - g->addEdge(e->copy(&nodeTable)); + Edge *e1 = e->copy(&nodeTable); + g->addEdge(e1); + edgeTable.insert(e,e1); + } + } + + // add a copy of a path to the new graph if all of the edges are there + foreach (Path *p, paths()) { + bool allEdges = true; + Path *p1 = new Path(); + foreach (Edge *e1, p->edges()) { + if (edgeTable.contains(e1)) { + p1->addEdge(edgeTable[e1]); + } else { + allEdges = false; + break; + } + } + if (allEdges) { + g->addPath(p1); + } else { + p1->removeEdges(); + delete p1; } } diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index 9771f44..3b1109e 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -381,6 +381,16 @@ void PasteCommand::undo() { _scene->clearSelection(); + foreach (Path *p, _graph->paths()) { + PathItem *pi = _scene->pathItems()[p]; + _scene->pathItems().remove(p); + _scene->removeItem(pi); + delete pi; + + p->removeEdges(); + _scene->graph()->removePath(p); + } + foreach (Edge *e, _graph->edges()) { EdgeItem *ei = _scene->edgeItems()[e]; _scene->edgeItems().remove(e); @@ -411,6 +421,12 @@ void PasteCommand::redo() _scene->clearSelection(); _scene->graph()->insertGraph(_graph); + foreach (Path *p, _graph->paths()) { + PathItem *pi = new PathItem(p); + _scene->pathItems().insert(p, pi); + _scene->addItem(pi); + } + foreach (Edge *e, _graph->edges()) { e->attachStyle(); // in case styles have changed EdgeItem *ei = new EdgeItem(e); -- cgit v1.2.3 From ef07176e5c5c8e2aa9ea5a380000302234f2934c Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 18 Apr 2020 15:30:31 +0100 Subject: deleting works correctly with paths --- src/gui/tikzscene.cpp | 33 ++++++++++++++++++++++----------- src/gui/undocommands.cpp | 9 ++++----- src/gui/undocommands.h | 6 +++--- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index e510931..9da8639 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -250,13 +250,18 @@ void TikzScene::mergeNodes() Node *n = _tikzDocument->graph()->nodes()[i]; if (m1.contains(n)) delNodes.insert(i, n); } + + QSet delPaths; for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) { Edge *e = _tikzDocument->graph()->edges()[i]; - if (m1.contains(e->source()) || m1.contains(e->target())) delEdges.insert(i, e); + if (m1.contains(e->source()) || m1.contains(e->target())) { + delEdges.insert(i, e); + if (e->path()) delPaths << e->path(); + } } - DeleteCommand *cmd = new DeleteCommand(this, delNodes, delEdges, - selNodes, selEdges); - _tikzDocument->undoStack()->push(cmd); + _tikzDocument->undoStack()->push(new SplitPathCommand(this, delPaths)); + _tikzDocument->undoStack()->push(new DeleteCommand(this, delNodes, delEdges, + selNodes, selEdges)); _tikzDocument->undoStack()->endMacro(); } @@ -410,10 +415,9 @@ void TikzScene::splitPath() } } - QVector paths; + QSet paths; foreach (Edge *e, edges) { - Path *p = e->path(); - if (p && !paths.contains(p)) paths << p; + if (e->path()) paths << e->path(); } _tikzDocument->undoStack()->push(new SplitPathCommand(this, paths)); @@ -1121,6 +1125,7 @@ void TikzScene::deleteSelectedItems() QMap deleteNodes; QMap deleteEdges; + QSet deletePaths; for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) { Node *n = _tikzDocument->graph()->nodes()[i]; @@ -1131,14 +1136,20 @@ void TikzScene::deleteSelectedItems() Edge *e = _tikzDocument->graph()->edges()[i]; if (selEdges.contains(e) || selNodes.contains(e->source()) || - selNodes.contains(e->target())) deleteEdges.insert(i, e); + selNodes.contains(e->target())) + { + if (e->path()) deletePaths << e->path(); + deleteEdges.insert(i, e); + } } //qDebug() << "nodes:" << deleteNodes; //qDebug() << "edges:" << deleteEdges; - DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, - selNodes, selEdges); - _tikzDocument->undoStack()->push(cmd); + _tikzDocument->undoStack()->beginMacro("Delete"); + _tikzDocument->undoStack()->push(new SplitPathCommand(this, deletePaths)); + _tikzDocument->undoStack()->push(new DeleteCommand(this, deleteNodes, deleteEdges, + selNodes, selEdges)); + _tikzDocument->undoStack()->endMacro(); } void TikzScene::copyToClipboard() diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index 3b1109e..35345de 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -685,18 +685,17 @@ void MakePathCommand::redo() } SplitPathCommand::SplitPathCommand(TikzScene *scene, - const QVector &paths, + const QSet &paths, QUndoCommand *parent) : GraphUpdateCommand(scene, parent), _paths(paths) { - foreach (Path *p, paths) _edgeLists << p->edges(); + foreach (Path *p, paths) _edgeLists[p] = p->edges(); } void SplitPathCommand::undo() { - for (int i = 0; i < _paths.length(); ++i) { - Path *p = _paths[i]; - foreach (Edge *e, _edgeLists[i]) { + foreach (Path *p, _paths) { + foreach (Edge *e, _edgeLists[p]) { p->addEdge(e); } diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h index 00b9a40..c10eecc 100644 --- a/src/gui/undocommands.h +++ b/src/gui/undocommands.h @@ -275,15 +275,15 @@ class SplitPathCommand : public GraphUpdateCommand { public: explicit SplitPathCommand(TikzScene *scene, - const QVector &paths, + const QSet &paths, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - QVector _paths; + QSet _paths; // keep a copy of the edge lists so they can be added back to each path in undo() - QVector> _edgeLists; + QMap> _edgeLists; }; #endif // UNDOCOMMANDS_H -- cgit v1.2.3 From 706b7fc38c4ed4769a3a1a8602e729b672f13118 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 18 Apr 2020 23:05:52 +0100 Subject: set no fill and no draw --- src/data/style.cpp | 10 ++++++ src/data/style.h | 11 ++++--- src/gui/styleeditor.cpp | 81 ++++++++++++++++++++++++++++++++++--------------- src/gui/styleeditor.h | 2 ++ src/gui/styleeditor.ui | 38 +++++++++++++++++++++++ tex/logo.tikzstyles | 1 + 6 files changed, 115 insertions(+), 28 deletions(-) diff --git a/src/data/style.cpp b/src/data/style.cpp index fe33ee0..af6a13f 100644 --- a/src/data/style.cpp +++ b/src/data/style.cpp @@ -67,6 +67,16 @@ QBrush Style::brush() const else return QBrush(tikzit->colorByName(col)); } +bool Style::hasFill() const +{ + return (propertyWithDefault("fill", "none") != "none"); +} + +bool Style::hasStroke() const +{ + return (propertyWithDefault("draw", "none") != "none"); +} + QString Style::shape(bool tikzitOverride) const { return propertyWithDefault("shape", "circle", tikzitOverride); diff --git a/src/data/style.h b/src/data/style.h index 78e11dc..0e795e1 100644 --- a/src/data/style.h +++ b/src/data/style.h @@ -57,18 +57,21 @@ public: void setName(const QString &name); QString propertyWithDefault(QString prop, QString def, bool tikzitOverride=true) const; QString tikz() const; - void setArrowAtom(QString atom); - - // only relevant for node styles QColor fillColor(bool tikzitOverride=true) const; QBrush brush() const; + bool hasFill() const; + bool hasStroke() const; + + // only relevant for node styles QString shape(bool tikzitOverride=true) const; + QString category() const; // only relevant for edge styles Style::ArrowTipStyle arrowHead() const; Style::ArrowTipStyle arrowTail() const; Style::DrawStyle drawStyle() const; - QString category() const; + void setArrowAtom(QString atom); + protected: QString _name; diff --git a/src/gui/styleeditor.cpp b/src/gui/styleeditor.cpp index e2ade45..f8972b1 100644 --- a/src/gui/styleeditor.cpp +++ b/src/gui/styleeditor.cpp @@ -32,8 +32,8 @@ StyleEditor::StyleEditor(QWidget *parent) : { ui->setupUi(this); _formWidgets << ui->name << ui->category << - ui->fillColor << ui->hasTikzitFillColor << ui->tikzitFillColor << - ui->drawColor << ui->hasTikzitDrawColor << ui->tikzitDrawColor << + ui->fillColor << ui->noFill << ui->hasTikzitFillColor << ui->tikzitFillColor << + ui->drawColor << ui->noDraw << ui->hasTikzitDrawColor << ui->tikzitDrawColor << ui->shape << ui->hasTikzitShape << ui->tikzitShape << ui->leftArrow << ui->rightArrow << ui->properties; @@ -283,8 +283,16 @@ void StyleEditor::refreshDisplay() // draw QColor realDraw = s->strokeColor(false); QColor draw = s->strokeColor(); - ui->drawColor->setEnabled(true); - setColor(ui->drawColor, realDraw); + + ui->noDraw->setEnabled(true); + if (s->hasStroke()) { + ui->drawColor->setEnabled(true); + setColor(ui->drawColor, realDraw); + ui->noDraw->setChecked(false); + } else { + ui->noDraw->setChecked(true); + ui->drawColor->setEnabled(false); + } // tikzit draw bool drawOverride = s->data()->hasProperty("tikzit draw"); @@ -294,6 +302,27 @@ void StyleEditor::refreshDisplay() ui->tikzitDrawColor->setEnabled(drawOverride); if (drawOverride) setColor(ui->tikzitDrawColor, draw); + // fill + QColor realFill = s->fillColor(false); + QColor fill = s->fillColor(); + + ui->noFill->setEnabled(true); + if (s->hasFill()) { + ui->fillColor->setEnabled(true); + setColor(ui->fillColor, realFill); + ui->noFill->setChecked(false); + } else { + ui->noFill->setChecked(true); + ui->fillColor->setEnabled(false); + } + + // tikzit fill + bool fillOverride = s->data()->hasProperty("tikzit fill"); + ui->hasTikzitFillColor->setEnabled(true); + ui->hasTikzitFillColor->setChecked(fillOverride); + ui->tikzitFillColor->setEnabled(fillOverride); + if (fillOverride) setColor(ui->tikzitFillColor, fill); + if (!s->isEdgeStyle()) { // qDebug() << "node style update"; // category @@ -301,19 +330,6 @@ void StyleEditor::refreshDisplay() ui->category->setCurrentText( s->propertyWithDefault("tikzit category", "", false)); - // fill - QColor realFill = s->fillColor(false); - QColor fill = s->fillColor(); - ui->fillColor->setEnabled(true); - setColor(ui->fillColor, realFill); - - // tikzit fill - bool fillOverride = s->data()->hasProperty("tikzit fill"); - ui->hasTikzitFillColor->setEnabled(true); - ui->hasTikzitFillColor->setChecked(fillOverride); - ui->tikzitFillColor->setEnabled(fillOverride); - if (fillOverride) setColor(ui->tikzitFillColor, fill); - // shape QString realShape = s->propertyWithDefault("shape", "", false); QString shape = s->propertyWithDefault("tikzit shape", "", false); @@ -327,13 +343,6 @@ void StyleEditor::refreshDisplay() ui->tikzitShape->setEnabled(shapeOverride); if (shapeOverride) ui->tikzitShape->setCurrentText(shape); } else { -// qDebug() << "edge style update"; - - // set fill to gray (disabled) - ui->fillColor->setEnabled(false); - ui->tikzitFillColor->setEnabled(false); - ui->hasTikzitFillColor->setEnabled(false); - ui->shape->setEnabled(false); ui->tikzitShape->setEnabled(false); ui->hasTikzitShape->setEnabled(false); @@ -425,6 +434,30 @@ void StyleEditor::on_hasTikzitDrawColor_stateChanged(int state) } } +void StyleEditor::on_noFill_stateChanged(int state) +{ + Style *s = activeStyle(); + if (s != nullptr) { + if (state == Qt::Checked) s->data()->setProperty("fill", "none"); + else s->data()->setProperty("fill", "white"); + refreshActiveStyle(); + refreshDisplay(); + setDirty(true); + } +} + +void StyleEditor::on_noDraw_stateChanged(int state) +{ + Style *s = activeStyle(); + if (s != nullptr) { + if (state == Qt::Checked) s->data()->setProperty("draw", "none"); + else s->data()->setProperty("draw", "black"); + refreshActiveStyle(); + refreshDisplay(); + setDirty(true); + } +} + void StyleEditor::on_hasTikzitShape_stateChanged(int state) { Style *s = activeStyle(); diff --git a/src/gui/styleeditor.h b/src/gui/styleeditor.h index 2c35d56..7fc62c0 100644 --- a/src/gui/styleeditor.h +++ b/src/gui/styleeditor.h @@ -68,6 +68,8 @@ public slots: void on_tikzitDrawColor_clicked(); void on_hasTikzitFillColor_stateChanged(int state); void on_hasTikzitDrawColor_stateChanged(int state); + void on_noFill_stateChanged(int state); + void on_noDraw_stateChanged(int state); void on_hasTikzitShape_stateChanged(int state); void on_tikzitShape_currentIndexChanged(int); diff --git a/src/gui/styleeditor.ui b/src/gui/styleeditor.ui index 9c06894..22b04a6 100644 --- a/src/gui/styleeditor.ui +++ b/src/gui/styleeditor.ui @@ -822,6 +822,44 @@ true + + + + 340 + 110 + 71 + 17 + + + + + 9 + false + + + + none + + + + + + 340 + 140 + 71 + 17 + + + + + 9 + false + + + + none + + diff --git a/tex/logo.tikzstyles b/tex/logo.tikzstyles index 4e05673..3419ee3 100644 --- a/tex/logo.tikzstyles +++ b/tex/logo.tikzstyles @@ -4,6 +4,7 @@ % \tikzstyle{NAME}=[PROPERTY LIST] % Node styles +\tikzstyle{test}=[fill=none, draw=black, shape=circle] % Edge styles \tikzstyle{bg}=[-, line width=0.5mm, fill={rgb,255: red,217; green,217; blue,217}] -- cgit v1.2.3 From 05361085127d9849ee02b59b115a516c1bd05106 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 19 Apr 2020 11:29:14 +0100 Subject: draw fill color in edge style icon --- src/data/style.cpp | 69 ++++++++++++++++++++++++++++++++-------------------- src/gui/pathitem.cpp | 7 ++++-- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/data/style.cpp b/src/data/style.cpp index af6a13f..f4aa892 100644 --- a/src/data/style.cpp +++ b/src/data/style.cpp @@ -62,9 +62,11 @@ QColor Style::fillColor(bool tikzitOverride) const QBrush Style::brush() const { - QString col = propertyWithDefault("fill", "none", true); - if (col == "none") return Qt::NoBrush; - else return QBrush(tikzit->colorByName(col)); + if (hasFill()) { + return QBrush(fillColor()); + } else { + return Qt::NoBrush; + } } bool Style::hasFill() const @@ -74,7 +76,8 @@ bool Style::hasFill() const bool Style::hasStroke() const { - return (propertyWithDefault("draw", "none") != "none"); + if (isEdgeStyle()) return propertyWithDefault("draw", "black") != "none"; + else return (propertyWithDefault("draw", "none") != "none"); } QString Style::shape(bool tikzitOverride) const @@ -164,24 +167,28 @@ Style::DrawStyle Style::drawStyle() const QPen Style::pen() const { - QPen p(strokeColor()); - p.setWidthF((float)strokeThickness() * 2.0f); - - QVector pat; - switch (drawStyle()) { - case Dashed: - pat << 3.0 << 3.0; - p.setDashPattern(pat); - break; - case Dotted: - pat << 1.0 << 1.0; - p.setDashPattern(pat); - break; - case Solid: - break; - } + if (hasStroke()) { + QPen p(strokeColor()); + p.setWidthF((float)strokeThickness() * 2.0f); + + QVector pat; + switch (drawStyle()) { + case Dashed: + pat << 3.0 << 3.0; + p.setDashPattern(pat); + break; + case Dotted: + pat << 1.0 << 1.0; + p.setDashPattern(pat); + break; + case Solid: + break; + } - return p; + return p; + } else { + return Qt::NoPen; + } } QPainterPath Style::path() const @@ -237,19 +244,27 @@ QIcon Style::icon() const px.fill(Qt::transparent); QPainter painter(&px); - if (_data == 0) { - QPen pen(Qt::black); - pen.setWidth(3); - } else { - painter.setPen(pen()); +// if (_data == 0) { +// QPen pen(Qt::black); +// pen.setWidth(3); +// } else { +// painter.setPen(pen()); +// } + + QPen pn = pen(); + painter.setPen(pn); + + if (hasFill()) { + painter.fillRect(10,50,80,30,brush()); } painter.drawLine(10, 50, 90, 50); - QPen pn = pen(); pn.setStyle(Qt::SolidLine); painter.setPen(pn); + + switch (arrowHead()) { case Pointer: painter.drawLine(90,50,80,40); diff --git a/src/gui/pathitem.cpp b/src/gui/pathitem.cpp index 107281f..b45c0a5 100644 --- a/src/gui/pathitem.cpp +++ b/src/gui/pathitem.cpp @@ -33,8 +33,11 @@ void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge { Style *st = _path->edges().first()->style(); QPen pen = st->pen(); - painter->setPen(st->pen()); - painter->setBrush(st->brush()); + QBrush brush = st->brush(); + QColor c = brush.color(); + brush.setColor(QColor(c.red(),c.green(),c.blue(),200)); + painter->setPen(pen); + painter->setBrush(brush); painter->drawPath(painterPath()); } -- cgit v1.2.3 From 8f3f804990387607b7e52b5550ebce52e36aa619 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 19 Apr 2020 18:15:26 +0100 Subject: add path as background --- src/gui/mainmenu.cpp | 8 +++++++- src/gui/mainmenu.h | 1 + src/gui/mainmenu.ui | 9 ++++++++ src/gui/tikzscene.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++--------- src/gui/tikzscene.h | 2 +- tex/logo.tikzstyles | 5 ++++- 6 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp index 092d8b4..efd453d 100644 --- a/src/gui/mainmenu.cpp +++ b/src/gui/mainmenu.cpp @@ -242,7 +242,13 @@ void MainMenu::on_actionMerge_Nodes_triggered() void MainMenu::on_actionMake_Path_triggered() { if (tikzit->activeWindow() != 0) - tikzit->activeWindow()->tikzScene()->makePath(); + tikzit->activeWindow()->tikzScene()->makePath(false); +} + +void MainMenu::on_actionMake_Path_as_Background_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->makePath(true); } void MainMenu::on_actionSplit_Path_triggered() diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h index 431e43a..d0d73e9 100644 --- a/src/gui/mainmenu.h +++ b/src/gui/mainmenu.h @@ -68,6 +68,7 @@ public slots: void on_actionReverse_Edge_Direction_triggered(); void on_actionMerge_Nodes_triggered(); void on_actionMake_Path_triggered(); + void on_actionMake_Path_as_Background_triggered(); void on_actionSplit_Path_triggered(); // Tools diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui index 2e390f9..11778db 100644 --- a/src/gui/mainmenu.ui +++ b/src/gui/mainmenu.ui @@ -64,6 +64,7 @@ + @@ -485,6 +486,14 @@ Ctrl+Shift+P + + + Make Path as Background + + + Ctrl+B + + diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index 9da8639..33e4710 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -314,11 +314,14 @@ void TikzScene::reverseSelectedEdges() _tikzDocument->undoStack()->push(cmd); } -void TikzScene::makePath() +void TikzScene::makePath(bool duplicateEdges) { QSet selNodes; + QSet selEdges; QSet edges; - getSelection(selNodes, edges); + getSelection(selNodes, selEdges); + + edges = selEdges; // if no edges are selected, try to infer edges from nodes if (edges.isEmpty()) { @@ -334,13 +337,29 @@ void TikzScene::makePath() } foreach (Edge *e, edges) { - if (e->path() != nullptr) { + if (e->path() != nullptr && !duplicateEdges) { //QMessageBox::warning(nullptr, "Error", "Edges must not already be in another path."); // TODO: maybe we want to automatically split paths if edges are in a path already? return; } } + _tikzDocument->undoStack()->beginMacro("Make Path"); + + QVector oldEdgeOrder = graph()->edges(); + QSet oldEdges, newEdges; + oldEdges = edges; + + if (duplicateEdges) { + foreach (Edge *e, edges) { + Edge *e1 = e->copy(); + _tikzDocument->undoStack()->push(new AddEdgeCommand(this, e1, false, selNodes, selEdges)); + newEdges << e1; + oldEdgeOrder << e1; + } + edges = newEdges; + } + // try to turn selected edges into one contiguous chain or cycle, recording // which edges need to be flipped. @@ -387,16 +406,33 @@ void TikzScene::makePath() return; } - //qDebug() << p; - //qDebug() << flip; + _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); + + // order all of the edges together, and in the case of + // duplicate edges, just below the first original. + QVector newEdgeOrder; + bool firstEdge = true; + foreach (Edge *e, oldEdgeOrder) { + if (oldEdges.contains(e)) { + if (firstEdge) { + newEdgeOrder += p; + firstEdge = false; + } + + if (duplicateEdges) newEdgeOrder << e; + } else if (!newEdges.contains(e)) { + newEdgeOrder << e; + } + } + + _tikzDocument->undoStack()->push(new ReorderCommand(this, + graph()->nodes(), graph()->nodes(), oldEdgeOrder, newEdgeOrder)); QMap oldEdgeData; foreach (Edge *e, p) { if (e != p.first()) oldEdgeData[e] = e->data()->copy(); } - _tikzDocument->undoStack()->beginMacro("Make Path"); - _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); _tikzDocument->undoStack()->push(new MakePathCommand(this, p, oldEdgeData)); _tikzDocument->undoStack()->endMacro(); } @@ -754,10 +790,11 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) nodeItems()[e->source()]->setSelected(sel); nodeItems()[e->target()]->setSelected(sel); } - } else { - nodeItems()[_selectingEdge->source()]->setSelected(sel); - nodeItems()[_selectingEdge->target()]->setSelected(sel); } +// else { +// nodeItems()[_selectingEdge->source()]->setSelected(sel); +// nodeItems()[_selectingEdge->target()]->setSelected(sel); +// } } if (_rubberBandItem->isVisible()) { diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index 3f035e2..e1d30d2 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -83,7 +83,7 @@ public: void reverseSelectedEdges(); - void makePath(); + void makePath(bool duplicateEdges); void splitPath(); void getSelection(QSet &selNodes, QSet &selEdges) const; diff --git a/tex/logo.tikzstyles b/tex/logo.tikzstyles index 3419ee3..1103b0b 100644 --- a/tex/logo.tikzstyles +++ b/tex/logo.tikzstyles @@ -4,9 +4,12 @@ % \tikzstyle{NAME}=[PROPERTY LIST] % Node styles -\tikzstyle{test}=[fill=none, draw=black, shape=circle] +\tikzstyle{white dot}=[fill=white, draw=black, shape=circle] % Edge styles \tikzstyle{bg}=[-, line width=0.5mm, fill={rgb,255: red,217; green,217; blue,217}] \tikzstyle{fg}=[-, fill=black] \tikzstyle{dir}=[->] +\tikzstyle{region A}=[-, draw=none, fill={rgb,255: red,255; green,160; blue,162}, opacity=0.8] +\tikzstyle{region B}=[-, draw=none, fill={rgb,255: red,190; green,185; blue,255}, opacity=0.8] +\tikzstyle{region C}=[-, fill={rgb,255: red,179; green,255; blue,192}, draw=none, opacity=0.8] -- cgit v1.2.3 From e32f594d080ce7b2de3f7c15e4243e477c0193c0 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Wed, 22 Apr 2020 16:08:15 +0100 Subject: reverted (again!) deprecated command fix --- src/data/tikzparser.y | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/tikzparser.y b/src/data/tikzparser.y index 1e7b8cc..678d8bf 100644 --- a/src/data/tikzparser.y +++ b/src/data/tikzparser.y @@ -32,13 +32,13 @@ /* we use features added to bison 2.4 */ %require "2.3" -%define parse.error verbose +%error-verbose /* enable maintaining locations for better error messages */ %locations /* the name of the header file */ /*%defines "common/tikzparser.h"*/ /* make it re-entrant (no global variables) */ -%define api.pure +%pure-parser /* We use a pure (re-entrant) lexer. This means yylex will take a void* (opaque) type to maintain its state */ %lex-param {void *scanner} -- cgit v1.2.3 From 7f9b3685e94b544c1c585221fc815bc08fb72b0f Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 25 Apr 2020 14:21:31 +0100 Subject: some sample tikz files --- tex/2cat.tikz | 40 ++++++++++++++++++++++++++++++++ tex/2cat2.tikz | 36 +++++++++++++++++++++++++++++ tex/3cat.tikz | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tex/paths.tikz | 42 ++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 tex/2cat.tikz create mode 100644 tex/2cat2.tikz create mode 100644 tex/3cat.tikz create mode 100644 tex/paths.tikz diff --git a/tex/2cat.tikz b/tex/2cat.tikz new file mode 100644 index 0000000..7888ad2 --- /dev/null +++ b/tex/2cat.tikz @@ -0,0 +1,40 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, 2.75) {}; + \node [style=none] (1) at (2, 2.75) {}; + \node [style=none] (2) at (2, -2.75) {}; + \node [style=none] (3) at (0, -2.75) {}; + \node [style=none] (4) at (0, -1.25) {}; + \node [style=none] (5) at (0, 1.25) {}; + \node [style=none] (6) at (-2, -2.75) {}; + \node [style=none] (7) at (-2, 2.75) {}; + \node [style=white dot] (8) at (0, 1.25) {$\mu$}; + \node [style=white dot] (9) at (0, -1.25) {$\delta$}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (5.center) + to (0.center) + to (1.center) + to (2.center) + to (3.center) + to (4.center) + to [bend right=60, looseness=1.25] cycle; + \draw [style=region B] (3.center) to (4.center); + \draw [style=region B, bend right=60, looseness=1.25] (4.center) to (5.center); + \draw [style=region B] (5.center) to (0.center); + \draw [style=region A] (0.center) + to (5.center) + to [bend right=60, looseness=1.25] (4.center) + to (3.center) + to (6.center) + to (7.center) + to cycle; + \draw [style=region C] (4.center) + to [bend left=60, looseness=1.25] (5.center) + to [bend left=60, looseness=1.25] cycle; + \draw (3.center) to (4.center); + \draw [bend left=60, looseness=1.25] (4.center) to (5.center); + \draw (5.center) to (0.center); + \draw [bend right=60, looseness=1.25] (4.center) to (5.center); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/2cat2.tikz b/tex/2cat2.tikz new file mode 100644 index 0000000..7a9ce93 --- /dev/null +++ b/tex/2cat2.tikz @@ -0,0 +1,36 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, 2) {}; + \node [style=none] (1) at (0, 0) {}; + \node [style=none] (2) at (1, -2) {}; + \node [style=none] (3) at (2, -2) {}; + \node [style=none] (4) at (2, 2) {}; + \node [style=none] (5) at (0, 2) {}; + \node [style=none] (6) at (0, 0) {}; + \node [style=none] (7) at (-1, -2) {}; + \node [style=none] (8) at (-2, -2) {}; + \node [style=none] (9) at (-2, 2) {}; + \node [style=white dot] (10) at (0, 0) {$\mu$}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (3.center) + to (4.center) + to (0.center) + to (1.center) + to [bend left] (2.center) + to cycle; + \draw [style=region A] (8.center) + to (9.center) + to (5.center) + to (6.center) + to [bend right] (7.center) + to cycle; + \draw [style=region C] (6.center) + to [bend right] (7.center) + to (2.center) + to [bend right] cycle; + \draw [bend left] (7.center) to (6.center); + \draw [bend right] (2.center) to (6.center); + \draw (6.center) to (5.center); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/3cat.tikz b/tex/3cat.tikz new file mode 100644 index 0000000..221bcd4 --- /dev/null +++ b/tex/3cat.tikz @@ -0,0 +1,72 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, -3.25) {}; + \node [style=none] (1) at (0, -1.5) {}; + \node [style=none] (2) at (0, 1.5) {}; + \node [style=none] (3) at (0, 3.25) {}; + \node [style=none] (4) at (2.25, -3.25) {}; + \node [style=none] (5) at (2.25, 3.25) {}; + \node [style=none] (6) at (-2.25, -3.25) {}; + \node [style=none] (7) at (-2.25, 3.25) {}; + \node [style=white dot] (8) at (0, -1.5) {}; + \node [style=white dot] (9) at (0, 1.5) {}; + \node [style=none] (10) at (1, -2.5) {}; + \node [style=none] (11) at (1, -0.75) {}; + \node [style=none] (12) at (1, 2.25) {}; + \node [style=none] (13) at (1, 4) {}; + \node [style=none] (14) at (3.25, -2.5) {}; + \node [style=none] (15) at (3.25, 4) {}; + \node [style=none] (16) at (-1.25, -2.5) {}; + \node [style=none] (17) at (-1.25, 4) {}; + \node [style=white dot] (18) at (1, -0.75) {}; + \node [style=white dot] (19) at (1, 2.25) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (3.center) + to (5.center) + to (4.center) + to (0.center) + to (1.center) + to [bend right=45, looseness=1.25] (2.center) + to cycle; + \draw [style=region A] (7.center) + to (6.center) + to (0.center) + to (1.center) + to [bend left=45, looseness=1.25] (2.center) + to (3.center) + to cycle; + \draw (0.center) to (1.center); + \draw [style=region C] (2.center) + to [bend left=45, looseness=1.25] (1.center) + to [bend left=45, looseness=1.25] cycle; + \draw [bend right=45, looseness=1.25] (1.center) to (2.center); + \draw (2.center) to (3.center); + \draw [bend left=45, looseness=1.25] (1.center) to (2.center); + \draw [style=region C, in=135, out=45, loop] (1.center) to (); + \draw [style=region C, in=135, out=45, loop] (1.center) to (); + \draw [style=region B] (13.center) + to (15.center) + to (14.center) + to (10.center) + to (11.center) + to [bend right=45, looseness=1.25] (12.center) + to cycle; + \draw [style=region A] (17.center) + to (16.center) + to (10.center) + to (11.center) + to [bend left=45, looseness=1.25] (12.center) + to (13.center) + to cycle; + \draw (10.center) to (11.center); + \draw [style=region C] (12.center) + to [bend left=45, looseness=1.25] (11.center) + to [bend left=45, looseness=1.25] cycle; + \draw [bend right=45, looseness=1.25] (11.center) to (12.center); + \draw (12.center) to (13.center); + \draw [bend left=45, looseness=1.25] (11.center) to (12.center); + \draw [style=region C, in=135, out=45, loop] (11.center) to (); + \draw [style=region C, in=135, out=45, loop] (11.center) to (); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/paths.tikz b/tex/paths.tikz new file mode 100644 index 0000000..ebbf676 --- /dev/null +++ b/tex/paths.tikz @@ -0,0 +1,42 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (-4, 1) {}; + \node [style=none] (1) at (-1, 1) {}; + \node [style=none] (2) at (-2.5, -1.5) {}; + \node [style=none] (7) at (-3.5, 0.75) {}; + \node [style=none] (8) at (-0.5, 0.75) {}; + \node [style=none] (9) at (-2, -1.75) {}; + \node [style=none] (10) at (-3, 0.5) {}; + \node [style=none] (11) at (0, 0.5) {}; + \node [style=none] (12) at (-1.5, -2) {}; + \node [style=none] (13) at (-2.5, 0.25) {}; + \node [style=none] (14) at (0.5, 0.25) {}; + \node [style=none] (15) at (-1, -2.25) {}; + \node [style=none] (3) at (-0.25, 4.25) {}; + \node [style=none] (4) at (2, 3.75) {}; + \node [style=none] (5) at (2, 2) {}; + \node [style=none] (6) at (3.25, 1.25) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=bg] (2.center) + to (0.center) + to (1.center) + to cycle; + \draw [style=bg] (9.center) + to (7.center) + to (8.center) + to cycle; + \draw [style=bg] (12.center) + to (10.center) + to (11.center) + to cycle; + \draw [style=bg] (15.center) + to (13.center) + to (14.center) + to cycle; + \draw [style=dir] (3.center) + to (4.center) + to (5.center) + to (6.center); + \end{pgfonlayer} +\end{tikzpicture} -- cgit v1.2.3 From e1207288d547dbe2f427824f88d1345f1c3acd91 Mon Sep 17 00:00:00 2001 From: Adrian Holfter Date: Wed, 29 Apr 2020 16:55:35 +0200 Subject: invert horizontal scrolling so it behaves the same as e.g. gimp --- src/gui/tikzview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/tikzview.cpp b/src/gui/tikzview.cpp index 3479d22..eb87002 100644 --- a/src/gui/tikzview.cpp +++ b/src/gui/tikzview.cpp @@ -150,7 +150,7 @@ void TikzView::wheelEvent(QWheelEvent *event) zoomOut(); } } else if (event->modifiers() & Qt::ShiftModifier) { - horizontalScrollBar()->setValue(horizontalScrollBar()->value() + event->angleDelta().y()); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - event->angleDelta().y()); } } -- cgit v1.2.3 From 300267089b80785551c4721684280efe654ec834 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Tue, 18 Aug 2020 12:40:34 +0100 Subject: pushed tikzit version --- src/tikzit.h | 2 +- tikzit.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tikzit.h b/src/tikzit.h index d27d416..5debdf3 100644 --- a/src/tikzit.h +++ b/src/tikzit.h @@ -49,7 +49,7 @@ #ifndef TIKZIT_H #define TIKZIT_H -#define TIKZIT_VERSION "2.1.5" +#define TIKZIT_VERSION "2.1.6" #include "mainwindow.h" #include "mainmenu.h" diff --git a/tikzit.pro b/tikzit.pro index 46a411a..a8305da 100644 --- a/tikzit.pro +++ b/tikzit.pro @@ -2,7 +2,7 @@ QT += core gui widgets network -VERSION = 2.1.5 +VERSION = 2.1.6 test { CONFIG += testcase -- cgit v1.2.3