Adaptagrams
topology_graph.h
1 /*
2  * vim: ts=4 sw=4 et tw=0 wm=0
3  *
4  * libtopology - Classes used in generating and managing topology constraints.
5  *
6  * Copyright (C) 2007-2008 Monash University
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  * See the file LICENSE.LGPL distributed with the library.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17  *
18  * Author(s): Tim Dwyer
19 */
20 
21 /*
22  * Class definitions for graph elements used in determining topology
23  * preserving constraints.
24  */
25 
26 #ifndef TOPOLOGY_GRAPH_H
27 #define TOPOLOGY_GRAPH_H
28 
29 #include <vector>
30 
31 #include "libvpsc/assertions.h"
32 #include "libvpsc/rectangle.h"
33 
34 #include "libtopology/util.h"
35 
36 namespace vpsc {
37  class Variable;
38 }
39 namespace straightener {
40  struct Route;
41 }
42 namespace topology {
43  class Segment;
44  class TopologyConstraint;
45  class BendConstraint;
46  class StraightConstraint;
47  class Edge;
58  class Node {
59  public:
60  // the index of the associated node / variable / rectangle
61  const unsigned id;
62  // the bounding box of the associated node
63  vpsc::Rectangle* rect;
64  /*
65  * When an edge path is being defined externally with a vector of
66  * EdgePoint, a variable would not be specified.
67  * @param id
68  * @param r
69  */
70  Node(unsigned id, vpsc::Rectangle *r, vpsc::Variable *v = nullptr);
71  void setDesiredPos(double d, double weight=1.0);
72  double initialPos(vpsc::Dim scanDim) const;
73  double finalPos() const;
74  double posOnLine(vpsc::Dim scanDim, double alpha) const;
75  vpsc::Variable* getVar() const { return var; }
76  // variable positions used by solver
77  vpsc::Variable* var;
78  };
82  typedef std::vector<Node *> Nodes;
83  /*
84  * let n=ns.size(), where n<=vs.size(),
85  * for i<n we set the variable for ns[i] to be vs[i].
86  */
87  void setNodeVariables(Nodes& ns, std::vector<vpsc::Variable*>& vs);
88  /*
89  * An EdgePoint is a point along an edge path. It must correspond to
90  * either the middle of a Node (the start/end of the edge) or to a corner
91  * of a Node (a bend point around an edge).
92  */
93  class EdgePoint {
94  public:
95  // the node / variable / rectangle associated with this EdgePoint
96  Node* node;
97  // where the EdgePoint lies on the rectangle
98  enum RectIntersect {
99  TR, //< top right corner
100  BR, //< bottom right corner
101  BL, //< bottom left corner
102  TL, //< bends around rectangle's top-left corner
103  CENTRE //< connected to the rectangle's centre, hence the end of the edge.
104  } rectIntersect;
105  // the incoming segment to this EdgePoint on the edge path
106  Segment* inSegment;
107  // the outgoing segment to this EdgePoint on the edge path
108  Segment* outSegment;
109  /* each articulation EdgePoint (where isReal()==false)
110  * will be assigned (not immediately) a bendConstraint
111  */
112  BendConstraint* bendConstraint;
113  // append bendConstraint (if not nullptr) to ts
114  void getBendConstraint(std::vector<TopologyConstraint*>* ts);
115  // @return true if constraint created
116  bool createBendConstraint(vpsc::Dim scanDim);
117  // delete the bendConstraint and reset pointer to nullptr
118  void deleteBendConstraint();
119  /*
120  * Constructor associates the point with a node vertex but
121  * not an edge.
122  */
123  EdgePoint(Node* n, RectIntersect i)
124  : node(n), rectIntersect(i)
125  , inSegment(nullptr), outSegment(nullptr)
126  , bendConstraint(nullptr)
127  {
128  }
129  /*
130  * @param dim the axis (either horizontal or
131  * vertical) of the coordinate to return
132  * @return the position, computed based on rectIntersect and rectangle
133  * vertices of the node
134  */
135  double pos(vpsc::Dim dim) const;
136  // @return x position
137  double posX() const { return pos(vpsc::HORIZONTAL); }
138  // @return y position
139  double posY() const { return pos(vpsc::VERTICAL); }
140  /*
141  * @return where the EdgePoint on the rectangle as a vertex index
142  * for libavoid.
143  */
144  unsigned short rectIntersectAsVertexNumber(void) const
145  {
146  switch(rectIntersect) {
147  case topology::EdgePoint::BR:
148  return 0;
149  case topology::EdgePoint::TR:
150  return 1;
151  case topology::EdgePoint::TL:
152  return 2;
153  case topology::EdgePoint::BL:
154  return 3;
155  default:
156  return 4;
157  }
158  }
159  /*
160  * for any two EdgePoint the following should always be false!
161  * @param e an EdgePoint (not this one)
162  */
163  bool uniqueCheck(const EdgePoint* e) const {
164  COLA_ASSERT(this!=e);
165  return node==e->node && rectIntersect==e->rectIntersect;
166  }
167 // To prevent C++ objects from being destroyed in garbage collected languages
168 // when the libraries are called from SWIG, we hide the declarations of the
169 // destructors and prevent generation of default destructors.
170 #ifndef SWIG
171  ~EdgePoint();
172 #endif
173  /*
174  * @return true if the EdgePoint is the end of an edge otherwise
175  * asserts that it is a valid bend point.
176  */
177  bool isEnd() const;
178  /*
179  * asserts that, if this is a bend point, it does not double back in either
180  * the horizontal or vertical directions.
181  */
182  bool assertConvexBend() const;
183  /*
184  * @return offset from centre of node
185  */
186  double offset(vpsc::Dim scanDim) const;
187  /*
188  * remove this point from the edge replacing its in and out
189  * segments with a single new Segment.
190  * @return the replacement Segment
191  */
192  Segment* prune(vpsc::Dim scanDim);
193  };
194  /*
195  * A vector of pointers to EdgePoint objects.
196  */
197  typedef std::vector<EdgePoint *> EdgePoints;
198  typedef std::vector<const EdgePoint *> ConstEdgePoints;
199  /*
200  * a Segment is one straightline segment between two EdgePoint which are
201  * either bend points and/or ends of the edge.
202  */
203  class Segment {
204  public:
205  /*
206  * Create segment for a given edge between two EdgePoints.
207  * Note that segments can be zero length, for example between
208  * opposite corners of two rectangles.
209  * @param edge the edge to which this segment belongs
210  * @param start the EdgePoint at the start of the segment
211  * @param end the EdgePoint at the end of the segment
212  */
213  Segment(Edge* edge, EdgePoint* start, EdgePoint* end)
214  : edge(edge), start(start), end(end)
215  {
216  // no self loops!
217  COLA_ASSERT(start!=end);
218  // the ends of the segment should not involve the same rectangle vertex
219  COLA_ASSERT(!start->uniqueCheck(end));
220  start->outSegment=this;
221  end->inSegment=this;
222  }
223  /*
224  * add a new StraightConstraint to this segment (if necessary)
225  * @param node the node with which the constraint is associated
226  * @param pos the scanPos, i.e. the position in the scan dimension
227  * of the opening or closing of node.
228  * @return true if a constraint was created
229  */
230  bool createStraightConstraint(vpsc::Dim dim, Node* node, double pos);
231  /*
232  * creates a copy of the StraightConstraint in our own
233  * straightConstraints list, but only if this segment is not directly
234  * connected to the centre of the StraightConstraint node. @param s
235  * the StraightConstraint to be copied across
236  */
237  void transferStraightConstraint(StraightConstraint* s);
238  /*
239  * this typedef can be used to declare a wrapper functor
240  * for transferStraightConstraint
241  */
242  typedef std::binder1st<
243  std::mem_fun1_t<void, Segment, StraightConstraint*>
244  > TransferStraightConstraint;
245  /*
246  * TransferStraightConstraint might for example be applied to
247  * forEachStraightConstraint
248  */
249  template <typename T>
250  void forEachStraightConstraint(T f) {
251  for_each(straightConstraints.begin(),straightConstraints.end(),f);
252  }
253  /*
254  * append straightConstraints to ts
255  */
256  void getStraightConstraints(std::vector<TopologyConstraint*>* ts)
257  const;
258  /*
259  * clean up topologyConstraints
260  */
261  void deleteStraightConstraints();
262  ~Segment();
263  // the edge which this segment is part of
264  Edge* edge;
265  // the start point of the segment - either the end of the edge
266  // if connected to a real node, or a bend point
267  EdgePoint* start;
268  // the end point of the segment
269  EdgePoint* end;
270  /*
271  * @return the EdgePoint at the minimum extent of this segment on the
272  * scan axis
273  */
274  EdgePoint* getMin(vpsc::Dim scanDim) const
275  {
276  if (start->pos(vpsc::conjugate(scanDim)) <=
277  end->pos(vpsc::conjugate(scanDim)))
278  {
279  return start;
280  }
281  return end;
282  }
283  /*
284  * @return the EdgePoint on the maximum extent of this segment on the
285  * scan axis
286  */
287  EdgePoint* getMax(vpsc::Dim scanDim) const
288  {
289  if (start->pos(vpsc::conjugate(scanDim)) >
290  end->pos(vpsc::conjugate(scanDim)))
291  {
292  return start;
293  }
294  return end;
295  }
296  /*
297  * compute the intersection with the line !dim=pos.
298  * if called when Segment is parallel to scan line it will throw an
299  * assertion error.
300  * @param pos position of scanline
301  * @param p distance along line from start to end at which intersection
302  * occurs (where 0 is at the start and 1 is at the end -- though
303  * note that p will be outside this range for BendConstraints).
304  * @return position along scanline of intersection with the line along
305  * this edge segment
306  */
307  double forwardIntersection(vpsc::Dim scanDim, double pos, double &p) const {
308  return intersection(scanDim, pos, start, end, p);
309  }
310  double reverseIntersection(vpsc::Dim scanDim, double pos, double &p) const {
311  return intersection(scanDim, pos, end, start, p);
312  }
313  double forwardIntersection(vpsc::Dim scanDim, double pos) const {
314  double p;
315  return forwardIntersection(scanDim, pos,p);
316  }
317  double intersection(vpsc::Dim scanDim, const double pos,
318  const EdgePoint* s, const EdgePoint* e, double& p) const
319  {
320  double ux=s->pos(scanDim);
321  double vx=e->pos(scanDim);
322  double uy=s->pos(vpsc::conjugate(scanDim));
323  double vy=e->pos(vpsc::conjugate(scanDim));
324  double denom = vy - uy;
325  COLA_ASSERT(denom!=0); // must not be parallel to scanline!
326  p = (pos - uy)/denom;
327  return ux + p * (vx-ux);
328  }
329  std::string toString() const;
330  /*
331  * Compute the length in the specified dimension.
332  */
333  double length(vpsc::Dim dim) const;
334  /*
335  * Compute the euclidean distance between #start and #end.
336  */
337  double length() const;
338  void assertNonZeroLength() const;
339  /*
340  * does this segment have Node v as a CENTRE start or end point?
341  */
342  bool connectedToNode(const Node* v) const;
343  private:
344  // a set of topology constraints (left-/right-/above-/below-of
345  // relationships / between this segment and nodes
346  std::vector<StraightConstraint*> straightConstraints;
347  };
348  // do nothing operator used in ForEach
349  template <typename T>
350  struct NoOp {
351  void operator() (T t)
352  {
353  COLA_UNUSED(t);
354  }
355  };
356  /*
357  * defines (hopefully just once) a loop over the bipartite linked-list
358  * of Segment and EdgePoint in an Edge.
359  * In the case of a cluster boundary, the edge will be a cycle, where
360  * the last EdgePoint is also the first. Thus, we process from
361  * Edge::firstSegment to Edge::lastSegment. We visit every EdgePoint
362  * (i.e. nSegments+1), in the case of a cycle, the first/last
363  * point will be visited (PointOp applied) twice unless noCycle is set
364  * true.
365  */
366  template <typename PEdge,
367  typename PointOp,
368  typename SegmentOp >
369  void ForEach(PEdge e, PointOp po, SegmentOp so, bool noCycle=false) {
370  Segment* s=e->firstSegment;
371  if(!(e->cycle()&&noCycle)) {
372  po(s->start);
373  }
374  bool last=false;
375  do {
376  EdgePoint* p=s->end;
377  so(s);
378  if(s==e->lastSegment) {
379  last=true;
380  } else {
381  s=p->outSegment;
382  }
383  po(p);
384  } while(!last);
385  }
396  class Edge {
397  public:
399  unsigned id;
401  double idealLength;
406  Segment* firstSegment;
410  Segment* lastSegment;
411  // size of segments list headed by firstSegment
412  size_t nSegments;
416  Edge(unsigned id, double idealLength, EdgePoints &vs)
417  : id(id)
418  , idealLength(idealLength)
419  , firstSegment(nullptr), lastSegment(nullptr)
420  , nSegments(0)
421  {
422  EdgePoints::iterator a=vs.begin();
423  for(EdgePoints::iterator b=a+1;b!=vs.end();++a,++b) {
424  Segment* s = new Segment(this,*a,*b);
425  nSegments++;
426  if(firstSegment==nullptr) {
427  firstSegment = s;
428  }
429  lastSegment = s;
430  }
431  }
432  /*
433  * apply an operation to every Segment and EdgePoint associated with
434  * this Edge
435  * @param po operation to apply to each EdgePoint
436  * @param so operation to apply to each Segment
437  */
438  template <typename PointOp, typename SegmentOp>
439  void forEach(PointOp po, SegmentOp so, bool noCycle=false) {
440  ForEach<Edge*,PointOp,SegmentOp>(this,po,so,noCycle);
441  }
442  /*
443  * apply an operation to every Segment and EdgePoint associated with
444  * this Edge, without changing anything
445  * @param po operation to apply to each EdgePoint
446  * @param so operation to apply to each Segment
447  */
448  template <typename PointOp, typename SegmentOp>
449  void forEach(PointOp po, SegmentOp so, bool noCycle=false) const {
450  ForEach<const Edge*,PointOp,SegmentOp>(this,po,so,noCycle);
451  }
452  /*
453  * apply an operation to every Segment associated with this Edge
454  * @param o operation (a function or functor that takes a pointer to
455  * a segment as an argument)
456  */
457  template <typename T>
458  void forEachSegment(T o) {
459  forEach(NoOp<EdgePoint*>(),o);
460  }
461  /*
462  * a version of forEachSegment for const edges
463  * @param o an operation on a const Segment
464  */
465  template <typename T>
466  void forEachSegment(T o) const {
467  forEach(NoOp<const EdgePoint*>(),o);
468  }
469  /*
470  * apply an operation to every EdgePoint associated with this edge
471  * @param o operation (a function or functor that takes a pointer to
472  * an EdgePoint as an argument)
473  * @param noCycle if the edge is a cycle don't apply o to the
474  * start/end point twice.
475  */
476  template <typename T>
477  void forEachEdgePoint(T o, bool noCycle=false) {
478  forEach(o,NoOp<Segment*>(),noCycle);
479  }
480  /*
481  * a version of forEachEdgePoint for const edges
482  * @param o an operation on a const EdgePoint
483  * @param noCycle if the edge is a cycle apply o to the
484  * start/end point only once.
485  */
486  template <typename T>
487  void forEachEdgePoint(T o, bool noCycle=false) const {
488  forEach(o,NoOp<const Segment*>(),noCycle);
489  }
490 
491 // To prevent C++ objects from being destroyed in garbage collected languages
492 // when the libraries are called from SWIG, we hide the declarations of the
493 // destructors and prevent generation of default destructors.
494 #ifndef SWIG
495  /*
496  * cleanup segments
497  */
498  ~Edge() {
499  forEach(delete_object(),delete_object(),true);
500  }
501 #endif
502 
503  /*
504  * the sum of the lengths of all the segments
505  */
506  double pathLength() const;
507  /*
508  * get a list of all the EdgePoints along the Edge path
509  */
510  void getPath(ConstEdgePoints& vs) const;
511  /*
512  * @return a list of the coordinates along the edge route
513  */
514  straightener::Route* getRoute() const;
515  void getTopologyConstraints(std::vector<TopologyConstraint*>* ts)
516  const {
517  forEach(
518  std::bind2nd(
519  std::mem_fun(&EdgePoint::getBendConstraint),ts),
520  std::bind2nd(
521  std::mem_fun(&Segment::getStraightConstraints),ts),
522  true);
523  }
524  bool assertConvexBends() const;
525  bool cycle() const {
526  return firstSegment->start==lastSegment->end;
527  }
528  std::string toString() const;
529  };
533  typedef std::vector<Edge *> Edges;
534  double compute_stress(const Edges&);
535  void printEdges(const Edges&);
536 /*
537  * CrossProduct of three points: If the result is 0, the points are collinear;
538  * if it is positive, the three points (in order) constitute a "left turn",
539  * otherwise a "right turn".
540  */
541 inline double crossProduct(
542  double x0, double y0,
543  double x1, double y1,
544  double x2, double y2) {
545  return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
546 }
547 
548 #ifndef NDEBUG
549  bool assertConvexBends(const Edges&);
550  /*
551  * Asserts that there are no intersections between any of the segments
552  * in edges and rectangles in nodes
553  * @param nodes containing rectangles
554  * @param edges containing segments
555  * @return true if assertions succeed
556  */
557  bool assertNoSegmentRectIntersection(const Nodes&, const Edges&);
558  bool assertNoZeroLengthEdgeSegments(const Edges& es);
559 #endif
560 } // namespace topology
561 #endif // TOPOLOGY_GRAPH_H
562 
A line between two points.
Definition: geomtypes.h:188
A variable is comprised of an ideal position, final position and a weight.
Definition: variable.h:44
libtopology: Extensions for topology preservation for libcola and libavoid libraries.
Definition: shape.h:41
Segment * lastSegment
Definition: topology_graph.h:410
Edge(unsigned id, double idealLength, EdgePoints &vs)
Definition: topology_graph.h:416
Definition: gradient_projection.h:40
std::pair< unsigned, unsigned > Edge
Edges are simply a pair of indices to entries in the Node vector.
Definition: cola.h:68
libvpsc: Variable Placement with Separation Constraints quadratic program solver library.
Definition: assertions.h:61
Point a
The first point.
Definition: geomtypes.h:192
Point b
The second point.
Definition: geomtypes.h:194
std::vector< Node * > Nodes
A vector of pointers to Node objects.
Definition: topology_graph.h:82
A rectangle represents a fixed-size shape in the diagram that may be moved to prevent overlaps and sa...
Definition: rectangle.h:78
The y-dimension (1).
Definition: rectangle.h:47
std::vector< Edge * > Edges
A vector of pointers to Edge objects.
Definition: topology_graph.h:533
The x-dimension (0).
Definition: rectangle.h:43
double idealLength
the ideal length which the layout should try to obtain for this edge
Definition: topology_graph.h:401
Dim
Indicates the x- or y-dimension.
Definition: rectangle.h:41
Segment * firstSegment
Definition: topology_graph.h:406
unsigned id
id specified by user. Can be used to match to external edge.
Definition: topology_graph.h:399