Adaptagrams
aca.h
1 /*
2  * vim: ts=4 sw=4 et tw=0 wm=0
3  *
4  * libdialect - A library for computing DiAlEcT layouts:
5  * D = Decompose/Distribute
6  * A = Arrange
7  * E = Expand/Emend
8  * T = Transform
9  *
10  * Copyright (C) 2018 Monash University
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  * See the file LICENSE.LGPL distributed with the library.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21  *
22  * Author(s): Steve Kieffer <http://skieffer.info>
23 */
24 
25 #ifndef ACA_H
26 #define ACA_H
27 
28 #include <vector>
29 #include <string>
30 #include <utility>
31 #include <map>
32 #include <set>
33 
34 #include "libcola/cola.h"
35 
36 #include "libdialect/util.h"
37 #include "libdialect/commontypes.h"
38 #include "libdialect/constraints.h"
39 #include "libdialect/graphs.h"
40 
41 namespace dialect {
42 
43 enum ACAFlag {
44  ACAHORIZ = 1,
45  ACAVERT = 2,
46  ACADELIB = 4,
47  ACACONN = 8
48 };
49 
50 enum ACASepFlag {
51  ACANOSEP = 0,
52 
53  ACANORTH = 1,
54  ACAEAST = 2,
55  ACASOUTH = 4,
56  ACAWEST = 8,
57 
58  ACANORTHEAST = 3,
59  ACASOUTHEAST = 6,
60  ACANORTHWEST = 9,
61  ACASOUTHWEST = 12,
62 
63  ACANORTHSOUTH = 5,
64  ACAEASTWEST = 10,
65 
66  ACANOTNORTH = 14,
67  ACANOTEAST = 13,
68  ACANOTSOUTH = 11,
69  ACANOTWEST = 7,
70 
71  ACAALLSEP = 15
72 };
73 
74 ACASepFlag negateSepFlag(ACASepFlag sf);
75 ACAFlag sepToAlignFlag(ACASepFlag sf);
76 ACAFlag perpAlignFlag(ACAFlag af);
77 ACASepFlag vectorToSepFlag(double dx, double dy);
78 bool propsedSepConflictsWithExistingPosition(ACASepFlag pro, ACASepFlag ex);
79 
80 struct OrderedAlignment {
81  OrderedAlignment() :
82  af(ACACONN), sf(ACANOSEP), src(-1), tgt(-1),
83  offsetSrc(0), offsetTgt(0),
84  separation(nullptr), alignment(nullptr), edgeIndex(-1) {}
95  ~OrderedAlignment() {}
96  ACAFlag af;
97  ACASepFlag sf;
98  vpsc::Dim dim;
99  int src;
100  int tgt;
101  double offsetSrc;
102  double offsetTgt;
103  cola::SeparationConstraint* separation;
104  cola::AlignmentConstraint* alignment;
105  int edgeIndex;
106  double penalty;
107 };
108 
109 bool sortOrdAlignsByPenalty(const OrderedAlignment *lhs, const OrderedAlignment *rhs);
110 
111 typedef std::vector<OrderedAlignment*> OrderedAlignments;
112 typedef std::pair<double,double> EdgeOffset;
113 typedef std::vector<EdgeOffset> EdgeOffsets;
114 typedef std::vector<ACASepFlag> ACASepFlags;
115 
124 class ACALayout {
125 public:
160  ACALayout(
161  const vpsc::Rectangles& rs,
162  const std::vector<cola::Edge>& es,
164  const double idealLength,
165  const cola::EdgeLengths& eLengths = cola::StandardEdgeLengths,
166  cola::TestConvergence* doneTest = nullptr,
167  cola::PreIteration* preIteration = nullptr);
168 
173  ACALayout(std::shared_ptr<dialect::Graph> G);
174 
175  // To prevent C++ objects from being destroyed in garbage collected languages
176  // when the libraries are called from SWIG, we hide the declarations of the
177  // destructors and prevent generation of default destructors.
178  #ifndef SWIG
179  ~ACALayout(void);
180  #endif
181 
191  void createAlignments(void);
202  bool createOneAlignment(void);
208  bool applyOAsAllOrNothing(OrderedAlignments oas);
215  void layout(void);
219  void removeOverlaps(void);
224  void layoutWithCurrentConstraints(void);
230  void updateGraph(void);
234  void updateSepMatrix(void);
242  void updateSepMatrix(SepMatrix &M, const std::map<size_t, id_type> &ix2id);
243 
244  void outputInstanceToSVG(std::string filename);
245 
246  cola::ConstrainedFDLayout *getFDLayout(void);
247 
248  // Configuration methods:
249 
267  void addBendPointPenalty(bool b);
275  void favourLongEdges(bool b);
281  void postponeLeaves(bool b);
295  void useNonLeafDegree(bool b);
307  void allAtOnce(bool b);
325  void aggressiveOrdering(bool b);
332  void setAvoidNodeOverlaps(bool avoidOverlaps);
340  void ignoreEdges(std::vector<bool> ignore);
349  void ignoreNodesForOPWithOffsets(std::vector<bool> ignore);
356  void setNodeAliases(std::map<int,int> aliases);
367  void setAlignmentOffsetsForCompassDirection(ACASepFlag sf, EdgeOffsets offsets);
381  void setAllowedDirections(ACASepFlags seps);
391  bool edgeIsAligned(int j) const;
395  void addGroupOfNonOverlapExemptRectangles(std::vector<unsigned> rs);
396 
397  bool nodesAreAligned(int i, int j) const;
398 
399  void layoutPeriod(unsigned p);
400  void doFinalLayout(bool b);
401 
402  void addOrderedAlignments(OrderedAlignments oas);
403  OrderedAlignment *initOrdAlign(int j, ACASepFlag sf) const;
404  OrderedAlignment *initOrdAlign(int s, int t, ACASepFlag sf, int edgeIndex=-1) const;
405 
406  // Useful for debugging:
407  OrderedAlignment *mostRecentOA(void);
408  std::string writeAlignmentTable(void) const;
409  std::string aStateBeforeChop;
410  std::string writeStateForNodeIds(id_type id1, id_type id2);
411 
412 private:
418  void computeDegrees(void);
424  void generateVPSCConstraints(void);
429  int adjustVarNumForExtraVars(vpsc::Dim dim, int k);
435  void initStateTables(void);
445  void recordAlignmentWithClosure(int i, int j, ACAFlag af, int numCols = 0);
446 
447  void initNOCs(void);
448 
449  bool acaLoopOnce(void);
450  void acaLoopOneByOne(void);
451  void acaLoopAllAtOnce(void);
452 
453  bool layoutIfAppropriate(void);
454  cola::CompoundConstraints writeAllVPSCConstraintsAsCompound(void);
455  vpsc::Rectangles properAndAuxRects(void);
456 
457  void updateStateTables(OrderedAlignment *oa);
458 
459  vpsc::Rectangle *makeRectForOA(OrderedAlignment *oa);
460  vpsc::Rectangle *makeRectForEdge(int j, vpsc::Dim dim);
461  void updateRectForEdge(vpsc::Rectangle *R, int j, vpsc::Dim dim);
462  void recomputeEdgeShapes(vpsc::Dim dim);
463  void updateNodeRectsFromVars(void);
464  void updateVarsFromNodeRects(void);
465  void pushState(void);
466  void popState(void);
467  void dropState(void);
468  void pushRectCoords(void);
469  void popRectCoords(void);
470  void dropRectCoords(void);
471  void removeNewEdgeShapesAccordingToStateStack(void);
472  std::set<unsigned> exemptionSetForEdge(int j);
473  OrderedAlignment *chooseOA(void);
474  bool createsOverlap(OrderedAlignment *oa);
475  bool allOrNothing(OrderedAlignments oas);
476  bool applyIfFeasible(OrderedAlignment *oa);
477  vpsc::IncSolver *satisfy(vpsc::Variables &vs, vpsc::Constraints &cs, bool &sat);
478 
479  // In the following methods, the separation flag always means the direction from src to tgt.
480  void completeOrdAlign(OrderedAlignment *oa);
481  bool badSeparation(int j, ACASepFlag sf);
482  bool badSeparation(int l, int r, ACASepFlag sf);
483 
484  double computePenalty(int j, ACASepFlag sf);
485  double lengthPenaltyForEdge(int j);
486  double deflectionForEdge(int j, ACASepFlag sf);
487  double deflection(double sx, double sy, double tx, double ty, ACASepFlag sf);
488  double bendPointPenalty(int src, int tgt, ACASepFlag sf);
489  double leafPenalty(int src, int tgt);
490 
491  EdgeOffset getEdgeOffsetForCompassDirection(int j, ACASepFlag sf);
492  int alias(int i);
493  vpsc::Rectangle *getRect(int i, bool doAlias=false);
494 
495  // The penalty values determine the order in which certain types of
496  // alignments will be created. (See above.)
497  // The PENALTY_BOUND must be greater than the sum of the other penalty
498  // values plus 1 (the maximum possible deflection score).
499 
500  static const double BP_PENALTY; // can be applied twice for one edge
501  static const double LEAF_PENALTY;
502  static const double PENALTY_BOUND;
503 
504  static const double EDGE_SHAPE_HALF_THICKNESS;
505  static const double EDGE_SHAPE_BUFFER;
506 
507  Graph_SP m_graph;
508 
509  int m_n; // number of nodes
510  int m_m; // number of edges
511  int m_numExtraXVars;
512  int m_numExtraYVars;
513  vpsc::Rectangles m_rs;
514  std::vector<cola::Edge> m_es;
516  cola::RootCluster *m_rc;
517 
518  std::vector<bool> m_ignoreEdge;
519  std::vector<bool> m_ignoreNodeForOPWithOffsets;
520  std::map<ACASepFlag,EdgeOffsets> m_edgeOffsets;
521  ACASepFlags m_allowedSeps;
522  std::map<int,int> m_nodeAliases;
523 
524  vpsc::Constraints m_xEqCs;
525  vpsc::Constraints m_xIneqCs;
526  vpsc::Constraints m_yEqCs;
527  vpsc::Constraints m_yIneqCs;
528  vpsc::Variables m_xvs;
529  vpsc::Variables m_yvs;
530 
531  vpsc::Constraints m_xcs;
532  vpsc::Constraints m_ycs;
533 
534  vpsc::Rectangles m_xrs;
535  vpsc::Rectangles m_yrs;
536 
537  cola::NonOverlapConstraints *m_xnocs = nullptr;
538  cola::NonOverlapConstraints *m_ynocs = nullptr;
539 
540  vpsc::Rectangles m_extendedRS;
541  std::map<int,int> m_xAuxRectIndexInExtendedRS;
542  std::map<int,int> m_yAuxRectIndexInExtendedRS;
543 
544  double m_idealLength;
545  bool m_preventOverlaps;
546  cola::EdgeLengths m_edgeLengths;
547  cola::TestConvergence *m_doneTest = nullptr;
548  cola::PreIteration *m_preIteration = nullptr;
549 
550  // Configuration:
551  bool m_addBendPointPenalty;
552  bool m_favourLongEdges;
553  bool m_postponeLeaves;
554  bool m_useNonLeafDegree;
555  bool m_allAtOnce;
556  bool m_aggressiveOrdering;
557 
558  std::multimap<int,int> m_incidentEdges; // map node index to indices of incident edges
559  std::multimap<int,int> m_nlincidentEdges; // map node index to indices of incident edges
560  std::multimap<int,int> m_nbrs; // neighbours
561  std::multimap<int,int> m_nlnbrs; // non-leaf neighbours
562  std::set<int> m_leaves; // indices of rectangles that are leaves
563  std::set<int> m_deg2Nodes; // degree-2 nodes w.r.t. ordinary nbrs
564  std::set<int> m_nldeg2Nodes; // degree-2 nodes w.r.t. only non-leaf neighbours
565 
566  Matrix2d<int> *m_alignmentState = nullptr;
567  // Keep a record of all OrderedAlignments created and applied.
568  std::vector<OrderedAlignment*> m_ordAligns;
569  std::set<int> m_alignedEdges;
570 
571  cola::ConstrainedFDLayout *m_fdlayout = nullptr;
572 
573  double m_lengthUpperBound;
574 
575  std::vector<unsigned> m_sizeStack;
576  std::vector<double> m_rectXStack;
577  std::vector<double> m_rectYStack;
578  std::map<int,int> m_edgeIndexToGuidelineIndex;
579  std::map<int,int> m_guidelineIndexToEdgeIndex;
580 
581  std::map<int,int> m_xGuidelineIndexToEdgeIndex;
582  std::map<int,int> m_yGuidelineIndexToEdgeIndex;
583 
584  bool m_didLayoutForLastAlignment;
585  bool m_doFinalFDLayout;
586 
587  std::vector < std::vector<unsigned> > m_nocExemptRects;
588  cola::NonOverlapConstraintExemptions *m_nocExemptions = nullptr;
589  bool m_nocsInitialised;
590  // Map each rect index to the set of all rect indices with which it is
591  // exempt from non-overlap constraints:
592  std::multimap<unsigned,unsigned> m_nocExemptionSets;
593 
594  unsigned m_layoutPeriod;
595 };
596 
597 } // namespace dialect
598 
599 
600 #endif // ACA_H
void useNonLeafDegree(bool b)
Say whether leaves should be counted when computing node degrees.
Definition: aca.cpp:333
void setAvoidNodeOverlaps(bool avoidOverlaps)
Specifies whether non-overlap constraints should be automatically generated between all nodes...
Definition: aca.cpp:348
Incremental solver for Variable Placement with Separation Constraints problem instance.
Definition: solve_VPSC.h:105
void addBendPointPenalty(bool b)
Control whether we avoid making bend points.
Definition: aca.cpp:318
void setClusterHierarchy(cola::RootCluster *rc)
sets the cluster hierarchy of the underlying FDLayout.
Definition: aca.cpp:381
void layout(void)
Do an initial stress-minimising layout, and then create alignments.
Definition: aca.cpp:297
A default functor that is called after each iteration of the layout algorithm.
Definition: cola.h:216
ACALayout(const vpsc::Rectangles &rs, const std::vector< cola::Edge > &es, cola::CompoundConstraints &ccs, const double idealLength, const cola::EdgeLengths &eLengths=cola::StandardEdgeLengths, cola::TestConvergence *doneTest=nullptr, cola::PreIteration *preIteration=nullptr)
Constructs an adaptive constrained alignment layout instance.
bool createOneAlignment(void)
Creates one alignment.
Definition: aca.cpp:284
libdialect: A library for computing human-like orthogonal network (DiAlEcT) layouts.
Definition: cola.h:44
void ignoreEdges(std::vector< bool > ignore)
Specify that certain edges are never to be aligned.
Definition: aca.cpp:353
An alignment constraint specifies a alignment line that a set of nodes must be constrained to by an e...
Definition: compound_constraints.h:345
void createAlignments(void)
Creates alignments.
Definition: aca.cpp:274
void favourLongEdges(bool b)
Prefer long edges instead of ones that are close to aligned.
Definition: aca.cpp:323
void ignoreNodesForOPWithOffsets(std::vector< bool > ignore)
Say that certain nodes may be crossed by edges.
Definition: aca.cpp:359
Implements the Adaptive Constrained Alignment (ACA) algorithm.
Definition: aca.h:124
std::vector< Variable * > Variables
A vector of pointers to Variable objects.
Definition: constraint.h:38
std::vector< double > EdgeLengths
Definition: cola.h:72
void postponeLeaves(bool b)
Say whether alignment of leaf edges should be saved for last.
Definition: aca.cpp:328
void updateGraph(void)
For forward compatibility (i.e. with Graphs), we offer a convenience method to update the Graph (when...
Definition: aca.cpp:1295
std::vector< Constraint * > Constraints
A vector of pointers to Constraint objects.
Definition: constraint.h:125
std::vector< Rectangle * > Rectangles
A vector of pointers to Rectangle objects.
Definition: rectangle.h:246
A default functor that is called before each iteration in the main loop of the ConstrainedFDLayout::r...
Definition: cola.h:168
Holds the cluster hierarchy specification for a diagram.
Definition: cluster.h:172
void allAtOnce(bool b)
Say whether alignment choices should alternate with stress minimisation steps.
Definition: aca.cpp:338
void addGroupOfNonOverlapExemptRectangles(std::vector< unsigned > rs)
Stop non-overlap constraints from being generated between certain nodes.
Definition: aca.cpp:410
bool applyOAsAllOrNothing(OrderedAlignments oas)
Creates all the requested alignments, or none if any is infeasible.
Definition: aca.cpp:290
void removeOverlaps(void)
Do an FD layout with overlap prevention, then stop.
Definition: aca.cpp:304
void setAllowedDirections(ACASepFlags seps)
Say which separations are allowed for the source and target of each edge.
Definition: aca.cpp:456
void setAlignmentOffsetsForCompassDirection(ACASepFlag sf, EdgeOffsets offsets)
Say how to offset nodes when edges are aligned in a certain direction.
Definition: aca.cpp:450
A rectangle represents a fixed-size shape in the diagram that may be moved to prevent overlaps and sa...
Definition: rectangle.h:78
Dim
Indicates the x- or y-dimension.
Definition: rectangle.h:41
Implements a constrained force-directed layout algorithm.
Definition: cola.h:622
void setNodeAliases(std::map< int, int > aliases)
Set certain nodes to be used in place of others.
Definition: aca.cpp:365
std::vector< CompoundConstraint * > CompoundConstraints
A vector of pointers to CompoundConstraint objects.
Definition: compound_constraints.h:259
bool edgeIsAligned(int j) const
Check whether an edge is aligned.
Definition: aca.cpp:386
A separation constraint specifies a simple horizontal or vertical spacing constraint between 2 nodes ...
Definition: compound_constraints.h:432
void layoutWithCurrentConstraints(void)
Run layout with current constraints, and with or without overlap prevention, as per the current setti...
Definition: aca.cpp:772
void updateSepMatrix(void)
Update the SepMatrix of the Graph on which this ACALayout was built (if any).
Definition: aca.cpp:1301
void aggressiveOrdering(bool b)
Say whether to consider changing orthogonal ordering of nodes.
Definition: aca.cpp:343