Adaptagrams
straightener.h
1 /*
2  * vim: ts=4 sw=4 et tw=0 wm=0
3  *
4  * libcola - A library providing force-directed network layout using the
5  * stress-majorization method subject to separation constraints.
6  *
7  * Copyright (C) 2006-2008 Monash University
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  * See the file LICENSE.LGPL distributed with the library.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  *
19 */
20 
21 #ifndef STRAIGHTENER_H
22 #define STRAIGHTENER_H
23 
24 #include <set>
25 #include <cfloat>
26 #include <iostream>
27 #include <iterator>
28 
29 #include "libvpsc/rectangle.h"
30 #include "libcola/commondefs.h"
31 
32 namespace cola {
33  class Cluster;
34  class ConvexCluster;
35  class SeparationConstraint;
36 }
37 namespace straightener {
38 
39 struct Route {
40  Route(unsigned n) : n(n), xs(new double[n]), ys(new double[n]) {}
41  ~Route() {
42  delete [] xs;
43  delete [] ys;
44  }
45  void print() {
46  std::cout << "double xs[]={";
47  std::copy(xs,xs+n-1,std::ostream_iterator<double>(std::cout,","));
48  std::cout << xs[n-1] << "};" << std::endl << "double ys[]={";
49  std::copy(ys,ys+n-1,std::ostream_iterator<double>(std::cout,","));
50  std::cout << ys[n-1] << "};" << std::endl;
51  }
52  void boundingBox(double &xmin,double &ymin,double &xmax,double &ymax) {
53  xmin=ymin=DBL_MAX;
54  xmax=ymax=-DBL_MAX;
55  for(unsigned i=0;i<n;i++) {
56  xmin=std::min(xmin,xs[i]);
57  xmax=std::max(xmax,xs[i]);
58  ymin=std::min(ymin,ys[i]);
59  ymax=std::max(ymax,ys[i]);
60  }
61  }
62  double routeLength () const {
63  double length=0;
64  for(unsigned i=1;i<n;i++) {
65  double dx=xs[i-1]-xs[i];
66  double dy=ys[i-1]-ys[i];
67  length+=sqrt(dx*dx+dy*dy);
68  }
69  return length;
70  }
71  void rerouteAround(vpsc::Rectangle *rect);
72  unsigned n;
73  double *xs;
74  double *ys;
75 };
76 class Node;
77 struct DebugPoint {
78  double x,y;
79 };
80 struct DebugLine {
81  DebugLine(double x0,double y0,double x1,double y1,unsigned colour)
82  : x0(x0),y0(y0),x1(x1),y1(y1),colour(colour) {}
83  double x0,y0,x1,y1;
84  unsigned colour;
85 };
86 class ScanObject {
87 public:
88  const unsigned id;
89  double getMin(vpsc::Dim d) const {
90  return min[d];
91  }
92  double getMax(vpsc::Dim d) const {
93  return max[d];
94  }
95  ScanObject(unsigned id) : id(id) {}
96 protected:
97  double min[2], max[2];
98 };
99 class Edge : public ScanObject {
100 public:
101  unsigned openInd; // position in openEdges
102  unsigned startNode, endNode;
103  double idealLength;
104  std::vector<unsigned> dummyNodes;
105  std::vector<unsigned> path;
106  std::vector<unsigned> activePath;
107  std::vector<DebugPoint> debugPoints;
108  std::vector<DebugLine> debugLines;
109  void print() {
110  printf("Edge[%d]=(%d,%d)\n",id,startNode,endNode);
111  route->print();
112  }
113  // if the edge route intersects with any of the rectangles in rects then reroute
114  // those parts of the route around the rectangle boundaries
115  void rerouteAround(std::vector<vpsc::Rectangle*> const &rects) {
116  unsigned rid=0;
117  for(std::vector<vpsc::Rectangle*>::const_iterator r=rects.begin();r!=rects.end();r++,rid++) {
118  if(rid!=startNode && rid!=endNode) {
119  route->rerouteAround(*r);
120  }
121  }
122  updateBoundingBox();
123  }
124  // Edge with a non-trivial route
125  Edge(unsigned id, unsigned start, unsigned end, Route* route)
126  : ScanObject(id), startNode(start), endNode(end), route(route)
127  {
128  updateBoundingBox();
129  }
130  // Edge with a trivial route
131  Edge(unsigned id, unsigned start, unsigned end,
132  double x1, double y1, double x2, double y2)
133  : ScanObject(id), startNode(start), endNode(end) {
134  route = new Route(2);
135  route->xs[0]=x1; route->ys[0]=y1;
136  route->xs[1]=x2; route->ys[1]=y2;
137  updateBoundingBox();
138  }
139  ~Edge() {
140  delete route;
141  }
142  bool isEnd(unsigned n) const {
143  if(startNode==n||endNode==n) return true;
144  return false;
145  }
146  void nodePath(std::vector<Node*>& nodes, bool allActive);
147  void createRouteFromPath(std::vector<Node *> const & nodes);
148  void xpos(double y, std::vector<double>& xs) const {
149  // search line segments for intersection points with y pos
150  for(unsigned i=1;i<route->n;i++) {
151  double ax=route->xs[i-1], bx=route->xs[i], ay=route->ys[i-1], by=route->ys[i];
152  double r=(y-ay)/(by-ay);
153  // as long as y is between ay and by then r>0
154  if(r>=0&&r<=1) {
155  xs.push_back(ax+(bx-ax)*r);
156  }
157  }
158  }
159  void ypos(double x, std::vector<double>& ys) const {
160  // search line segments for intersection points with x pos
161  for(unsigned i=1;i<route->n;i++) {
162  double ax=route->xs[i-1], bx=route->xs[i], ay=route->ys[i-1], by=route->ys[i];
163  double r=(x-ax)/(bx-ax);
164  // as long as y is between ax and bx then r>0
165  if(r>0&&r<=1) {
166  ys.push_back(ay+(by-ay)*r);
167  }
168  }
169  }
170  Route const * getRoute() const {
171  return route;
172  }
173  void setRoute(Route * r) {
174  delete route;
175  route=r;
176  updateBoundingBox();
177  }
178 private:
179  void updateBoundingBox() {
180  route->boundingBox(min[0],min[1],max[0],max[1]);
181  }
182  Route* route;
183 };
184 class Straightener {
185 public:
186  Straightener(
187  const double strength,
188  const vpsc::Dim dim,
189  std::vector<vpsc::Rectangle*> const & rs,
190  cola::FixedList const & fixed,
191  std::vector<Edge*> const & edges,
192  vpsc::Variables const & vs,
193  vpsc::Variables & lvs,
194  vpsc::Constraints & lcs,
195  std::valarray<double> & oldCoords,
196  std::valarray<double> & oldG);
197  ~Straightener();
198  void updateNodePositions();
199  void finalizeRoutes();
200  void computeForces(cola::SparseMap &H);
201  void computeForces2(cola::SparseMap &H);
202  double computeStress(std::valarray<double> const &coords);
203  double computeStress2(std::valarray<double> const &coords);
204  std::valarray<double> dummyNodesX;
205  std::valarray<double> dummyNodesY;
206  std::valarray<double> g;
207  std::valarray<double> coords;
208  unsigned N;
209 private:
210  double strength;
211  const vpsc::Dim dim;
212  cola::FixedList const & fixed;
213  std::vector<Edge*> const & edges;
214  vpsc::Variables const & vs;
215  vpsc::Variables & lvs;
216  std::vector<Node*> nodes;
217  double len(const unsigned u, const unsigned v,
218  double& dx, double& dy,
219  double& dx2, double& dy2);
220  double gRule1(const unsigned a, const unsigned b);
221  double gRule2(const unsigned a, const unsigned b, const unsigned c);
222  double hRuleD1(const unsigned u, const unsigned v, const double sqrtf);
223  double hRuleD2(const unsigned u, const unsigned v, const unsigned w, const double sqrtf);
224  double hRule2(const unsigned u, const unsigned v, const unsigned w, const double sqrtf);
225  double hRule3(const unsigned u, const unsigned v, const unsigned w, const double sqrtf);
226  double hRule4(const unsigned a, const unsigned b, const unsigned c, const unsigned d);
227  double hRule56(const unsigned u, const unsigned v,
228  const unsigned a, const unsigned b, const unsigned c);
229  double hRule7(const unsigned a, const unsigned b,
230  const unsigned c, const unsigned d, const double sqrtf);
231  double hRule8(const unsigned u, const unsigned v, const unsigned w,
232  const unsigned a, const unsigned b, const unsigned c);
233 };
234 class Cluster {
235 public:
236  cola::ConvexCluster* colaCluster;
237  Cluster(cola::ConvexCluster* c) : colaCluster(c) {}
238  double scanpos;
239  std::vector<Edge*> boundary;
240  void updateActualBoundary();
241 };
242 class Node : public ScanObject {
243 public:
244  Cluster* cluster;
245  // Nodes may optionally belong to a cluster.
246  // Neg cluster_id means no cluster - i.e. top-level membership.
247  double pos[2];
248  double scanpos;
249  double length[2];
250  Edge* edge;
251  bool dummy; // nodes on edge paths (but not ends) are dummy
252  bool scan; // triggers scan events
253  bool active; // node is active if it is not dummy or is dummy and involved in
254  // a violated constraint
255  bool open; // a node is opened (if scan is true) when the scanline first reaches
256  // its boundary and closed when the scanline leaves it.
257  Node(unsigned id, vpsc::Rectangle const * r) :
258  ScanObject(id),cluster(nullptr),
259  edge(nullptr),dummy(false),scan(true),active(true),open(false) {
260  for(unsigned i=0;i<2;i++) {
261  pos[i]=r->getCentreD(i);
262  min[i]=r->getMinD(i);
263  max[i]=r->getMaxD(i);
264  length[i]=r->length(i);
265  }
266  }
267  Node(unsigned id, const double x, const double y) :
268  ScanObject(id),cluster(nullptr),
269  edge(nullptr),dummy(false),scan(false),active(true),open(false) {
270  pos[vpsc::HORIZONTAL]=x;
271  pos[vpsc::VERTICAL]=y;
272  for(unsigned i=0;i<2;i++) {
273  length[i]=4;
274  min[i]=pos[i]-length[i]/2.0;
275  max[i]=pos[i]+length[i]/2.0;
276  }
277  }
278  double euclidean_distance(Node const * v) const {
279  double dx=pos[0]-v->pos[0];
280  double dy=pos[1]-v->pos[1];
281  return sqrt(dx*dx+dy*dy);
282  }
283 
284 private:
285  friend void sortNeighbours(const vpsc::Dim dim, Node * v, Node * l, Node * r,
286  const double conjpos, std::vector<Edge*> const & openEdges,
287  std::vector<Node *>& L, std::vector<Node *>& nodes);
288  Node(const unsigned id, const double x, const double y, Edge* e) :
289  ScanObject(id),cluster(nullptr),
290  edge(e),dummy(true),scan(false),active(false) {
291  pos[vpsc::HORIZONTAL]=x;
292  pos[vpsc::VERTICAL]=y;
293  for(unsigned i=0;i<2;i++) {
294  length[i]=4;
295  min[i]=pos[i]-length[i]/2.0;
296  max[i]=pos[i]+length[i]/2.0;
297  }
298  e->dummyNodes.push_back(id);
299  }
300 };
301 struct CmpNodePos {
302  bool operator() (const Node* u, const Node* v) const {
303  double upos = u->scanpos;
304  double vpos = v->scanpos;
305  bool tiebreaker = u < v;
306  if (u->cluster != v->cluster) {
307  if(u->cluster!=nullptr) {
308  upos = u->cluster->scanpos;
309  }
310  if(v->cluster!=nullptr) {
311  vpos = v->cluster->scanpos;
312  }
313  tiebreaker = u->cluster < v->cluster;
314  }
315  if (upos < vpos) {
316  return true;
317  }
318  if (vpos < upos) {
319  return false;
320  }
321  return tiebreaker;
322  }
323 };
324 typedef std::set<Node*,CmpNodePos> NodeSet;
325 // defines references to three variables for which the goal function
326 // will be altered to prefer points u-b-v are in a linear arrangement
327 // such that b is placed at u+t(v-u).
328 struct LinearConstraint {
329  LinearConstraint(
330  Node const & u,
331  Node const & v,
332  Node const & b,
333  double w)
334  : u(u.id),v(v.id),b(b.id),w(w)
335  {
336  // from cosine rule: ub.uv/|uv|=|ub|cos(theta)
337  double uvx = v.pos[0] - u.pos[0],
338  uvy = v.pos[1] - u.pos[1],
339  ubx = b.pos[0] - u.pos[0],
340  uby = b.pos[1] - u.pos[1],
341  duv2 = uvx * uvx + uvy * uvy;
342  if(duv2 < 0.0001) {
343  t=0;
344  } else {
345  t = (uvx * ubx + uvy * uby)/duv2;
346  }
347  duu=(1-t)*(1-t);
348  duv=t*(1-t);
349  dub=t-1;
350  dvv=t*t;
351  dvb=-t;
352  dbb=1;
353  //printf("New LC: t=%f\n",t);
354  }
355  unsigned u;
356  unsigned v;
357  unsigned b;
358  double w; // weight
359  double t;
360  // 2nd partial derivatives of the goal function
361  // (X[b] - (1-t) X[u] - t X[v])^2
362  double duu;
363  double duv;
364  double dub;
365  double dvv;
366  double dvb;
367  double dbb;
368 };
369 void setEdgeLengths(double **D, std::vector<Edge*> & edges);
370 double pathLength(Edge const * e, std::vector<Node*> const & nodes);
371 double computeStressFromRoutes(double strength, std::vector<Edge*> & edges);
372 typedef std::vector<LinearConstraint*> LinearConstraints;
373 void generateConstraints(
374  const vpsc::Dim dim,
375  std::vector<Node*> & nodes,
376  std::vector<Edge*> const & edges,
377  std::vector<cola::SeparationConstraint*>& cs,
378  bool xSkipping);
379 void nodePath(Edge& e, std::vector<Node*>& nodes, std::vector<unsigned>& path);
380 void generateClusterBoundaries(
381  const vpsc::Dim dim,
382  std::vector<Node*> & nodes,
383  std::vector<Edge*> & edges,
384  std::vector<vpsc::Rectangle*> const & rs,
385  cola::Cluster const & clusterHierarchy,
386  std::vector<straightener::Cluster*>& sclusters);
387 
388 } // namespace straightener
389 #endif
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
std::vector< Variable * > Variables
A vector of pointers to Variable objects.
Definition: constraint.h:38
std::vector< Constraint * > Constraints
A vector of pointers to Constraint objects.
Definition: constraint.h:125
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
A cluster defines a hierarchical partitioning over the nodes which should be kept disjoint by the lay...
Definition: cluster.h:50
libcola: Force-directed network layout subject to separation constraints library. ...
Definition: box.cpp:25
The x-dimension (0).
Definition: rectangle.h:43
Dim
Indicates the x- or y-dimension.
Definition: rectangle.h:41
Defines a cluster that will be treated as a convex boundary around the child nodes and clusters...
Definition: cluster.h:358