Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
colafd.cpp
Go to the documentation of this file.
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-2015 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 * Author(s): Tim Dwyer
20 * Michael Wybrow
21 *
22*/
23
24// cmath needs ::strcpy_s under MinGW so include cstring.
25#include <cstring>
26
27#include <vector>
28#include <cmath>
29#include <limits>
30
31#include "libvpsc/solve_VPSC.h"
32#include "libvpsc/variable.h"
33#include "libvpsc/constraint.h"
34#include "libvpsc/rectangle.h"
35#include "libvpsc/exceptions.h"
36
37#include "libcola/commondefs.h"
38#include "libcola/cola.h"
43
44#ifdef MAKEFEASIBLE_DEBUG
45 #include "libcola/output_svg.h"
46#endif
47
48// Needs to come last since it will include windows.h on WIN32 and
49// may mess up C++ std library include on GCC 4.4
50#include "libcola/cola_log.h"
51
52using namespace std;
53using vpsc::Dim;
54using vpsc::XDIM;
55using vpsc::YDIM;
56using vpsc::IncSolver;
57using vpsc::Variable;
58using vpsc::Variables;
61using vpsc::Rectangle;
63
64namespace cola {
65
66template <class T>
67void delete_vector(vector<T*> &v) {
68 for_each(v.begin(),v.end(),delete_object());
69 v.clear();
70}
73inline double dotProd(valarray<double> x, valarray<double> y) {
74 COLA_ASSERT(x.size()==y.size());
75 double dp=0;
76 for(unsigned i=0;i<x.size();i++) {
77 dp+=x[i]*y[i];
78 }
79 return dp;
80}
81template <typename T>
82void dumpSquareMatrix(unsigned n, T** L) {
83 printf("Matrix %dX%d\n{",n,n);
84 for(unsigned i=0;i<n;i++) {
85 printf("{");
86 for(unsigned j=0;j<n;j++) {
87 std::cout<<L[i][j];
88 char c=j==n-1?'}':',';
89 printf("%c",c);
90 }
91 char c=i==n-1?'}':',';
92 printf("%c\n",c);
93 }
94}
95
97 const std::vector< Edge >& es, const double idealLength,
98 const EdgeLengths& eLengths,
99 TestConvergence *doneTest, PreIteration* preIteration)
100 : n(rs.size()),
101 X(valarray<double>(n)),
102 Y(valarray<double>(n)),
103 done(doneTest),
104 using_default_done(false),
105 preIteration(preIteration),
106 topologyAddon(new TopologyAddonInterface()),
107 rungekutta(true),
108 desiredPositions(nullptr),
109 clusterHierarchy(nullptr),
110 rectClusterBuffer(0),
111 m_idealEdgeLength(idealLength),
112 m_generateNonOverlapConstraints(false),
113 m_useNeighbourStress(false),
114 m_edge_lengths(eLengths.data(), eLengths.size()),
115 m_nonoverlap_exemptions(new NonOverlapConstraintExemptions())
116{
117 minD = DBL_MAX;
118
119 if (done == nullptr)
120 {
121 done = new TestConvergence();
122 using_default_done = true;
123 }
124
126
127 //FILELog::ReportingLevel() = logDEBUG1;
130 done->reset();
131 unsigned i=0;
132 for(vpsc::Rectangles::const_iterator ri=rs.begin();ri!=rs.end();++ri,++i) {
133 X[i]=(*ri)->getCentreX();
134 Y[i]=(*ri)->getCentreY();
135 FILE_LOG(logDEBUG) << *ri;
136 }
137 D=new double*[n];
138 G=new unsigned short*[n];
139 for(unsigned i=0;i<n;i++) {
140 D[i]=new double[n];
141 G[i]=new unsigned short[n];
142 }
143
145}
146
147std::vector<double> ConstrainedFDLayout::readLinearD(void)
148{
149 std::vector<double> d;
150 d.resize(n*n);
151 for (unsigned i = 0; i < n; ++i) {
152 for (unsigned j = 0; j < n; ++j) {
153 d[n*i + j] = D[i][j];
154 }
155 }
156 return d;
157}
158
159std::vector<unsigned> ConstrainedFDLayout::readLinearG(void)
160{
161 std::vector<unsigned> g;
162 g.resize(n*n);
163 for (unsigned i = 0; i < n; ++i) {
164 for (unsigned j = 0; j < n; ++j) {
165 g[n*i + j] = G[i][j];
166 }
167 }
168 return g;
169}
170
172 for (unsigned i = 0; i < n; ++i) {
173 neighbours.push_back(vector<unsigned>(n));
174 }
175 for (vector<Edge>::iterator it = es.begin(); it!=es.end(); ++it) {
176 Edge e = *it;
177 unsigned s = e.first, t = e.second;
178 neighbours[s][t] = 1;
179 neighbours[t][s] = 1;
180 }
181}
182
183void dijkstra(const unsigned s, const unsigned n, double* d,
184 const vector<Edge>& es, const std::valarray<double> & eLengths)
185{
186 shortest_paths::dijkstra(s,n,d,es,eLengths);
187}
188
190{
191 this->ccs = ccs;
192}
193
195 std::vector<std::vector<unsigned> > listOfNodeGroups)
196{
197 m_generateNonOverlapConstraints = avoidOverlaps;
199}
200
202{
203 m_useNeighbourStress = useNeighbourStress;
204}
205
207{
208 this->desiredPositions = desiredPositions;
209}
210
211
212/*
213 * Sets up the D and G matrices. D is the required euclidean distances
214 * between pairs of nodes based on the shortest paths between them (using
215 * m_idealEdgeLength*eLengths[edge] as the edge length, if eLengths array
216 * is provided otherwise just m_idealEdgeLength). G is a matrix of
217 * unsigned ints such that G[u][v]=
218 * 0 if there are no forces required between u and v
219 * (for example, if u and v are in unconnected components)
220 * 1 if attractive forces are required between u and v
221 * (i.e. if u and v are immediately connected by an edge and there is
222 * no topology route between u and v (for which an attractive force
223 * is computed elsewhere))
224 * 2 if no attractive force is required between u and v but there is
225 * a connected path between them.
226 */
228 const vector<Edge>& es, std::valarray<double> eLengths)
229{
230 // Correct zero or negative entries in eLengths array.
231 for (size_t i = 0; i < eLengths.size(); ++i)
232 {
233 if (eLengths[i] <= 0)
234 {
235 fprintf(stderr, "Warning: ignoring non-positive length at index %d "
236 "in ideal edge length array.\n", (int) i);
237 eLengths[i] = 1;
238 }
239 }
240
241 shortest_paths::johnsons(n,D,es,eLengths);
242 //dumpSquareMatrix<double>(n,D);
243 for(unsigned i=0;i<n;i++) {
244 for(unsigned j=0;j<n;j++) {
245 if(i==j) continue;
246 double& d=D[i][j];
247 unsigned short& p=G[i][j];
248 p=2;
249 if(d==DBL_MAX) {
250 // i and j are in disconnected subgraphs
251 p=0;
252 } else {
254 }
255
256 if ((d > 0) && (d < minD)) {
257 minD = d;
258 }
259 }
260 }
261 if (minD == DBL_MAX) minD = 1;
262
263 for(vector<Edge>::const_iterator e=es.begin();e!=es.end();++e) {
264 unsigned u=e->first, v=e->second;
265 G[u][v]=G[v][u]=1;
266 }
268 //dumpSquareMatrix<short>(n,G);
269}
270
271typedef valarray<double> Position;
273 unsigned n=X.size();
274 COLA_ASSERT(Y.size()==n);
275 COLA_ASSERT(pos.size()==2*n);
276 for(unsigned i=0;i<n;++i) {
277 pos[i]=X[i];
278 pos[i+n]=Y[i];
279 }
280}
281/*
282 * moves all rectangles to the desired position while respecting
283 * constraints.
284 * @param pos target positions of both axes
285 */
287 COLA_ASSERT(Y.size()==X.size());
288 COLA_ASSERT(pos.size()==2*X.size());
291}
292/*
293 * Layout is performed by minimizing the P-stress goal function iteratively.
294 * At each iteration taking a step in the steepest-descent direction.
295 * x0 is the current position, x1 is the x0 - descentvector.
296 */
298 const bool xAxis, const bool yAxis,
299 double stress, Position& x0, Position& x1) {
300 setPosition(x0);
301 if(xAxis) {
303 }
304 if(yAxis) {
306 }
307 getPosition(X,Y,x1);
308}
309
310/*
311 * run() implements the main layout loop, taking descent steps until
312 * stress is no-longer significantly reduced.
313 * done is a callback used to check stress but also to report updated
314 * positions.
315 */
316void ConstrainedFDLayout::run(const bool xAxis, const bool yAxis)
317{
318 // This generates constraints for non-overlap inside and outside
319 // of clusters. To assign correct variable indexes it requires
320 // that vs[] contains elements equal to the number of rectangles.
321 vpsc::Variables vs[2];
322 vs[0].resize(n);
323 vs[1].resize(n);
325
326 FILE_LOG(logDEBUG) << "ConstrainedFDLayout::run...";
327 double stress=DBL_MAX;
328 do {
329 if(preIteration) {
330 if(!(*preIteration)()) {
331 break;
332 }
333 //printf("preIteration->changed=%d\n",preIteration->changed);
334 if(preIteration->changed) {
335 stress=DBL_MAX;
336 }
337 if(preIteration->resizes.size()>0) {
338 FILE_LOG(logDEBUG) << " Resize event!";
340 }
341 }
342 unsigned N=2*n;
343 Position x0(N),x1(N);
344 getPosition(X,Y,x0);
345 if(rungekutta) {
346 Position a(N),b(N),c(N),d(N),ia(N),ib(N);
347 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,x0,a);
348 ia=x0+(a-x0)/2.0;
349 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,ia,b);
350 ib=x0+(b-x0)/2.0;
351 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,ib,c);
352 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,c,d);
353 x1=a+2.0*b+2.0*c+d;
354 x1/=6.0;
355 } else {
356 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,x0,x1);
357 }
358 setPosition(x1);
359 stress=computeStress();
360 FILE_LOG(logDEBUG) << "stress="<<stress;
361 } while(!(*done)(stress,X,Y));
362 for(unsigned i=0;i<n;i++) {
364 FILE_LOG(logDEBUG) << *r;
365 }
366 FILE_LOG(logDEBUG) << "ConstrainedFDLayout::run done.";
367
368 // Clear extra constraints.
369 for_each(extraConstraints.begin(), extraConstraints.end(), delete_object());
370 extraConstraints.clear();
371
372 // Free extra variables used for cluster containment.
373 for (size_t dim = 0; dim < 2; ++dim)
374 {
375 for (size_t i = n; i < vs[dim].size(); ++i)
376 {
377 delete vs[dim][i];
378 }
379 }
380}
381
382/*
383 * Same as run, but only applies one iteration. This may be useful
384 * where it's too hard to implement a call-back (e.g. in java apps)
385 */
386void ConstrainedFDLayout::runOnce(const bool xAxis, const bool yAxis) {
387 if(n==0) return;
388 double stress=DBL_MAX;
389 unsigned N=2*n;
390 Position x0(N),x1(N);
391 getPosition(X,Y,x0);
392 if(rungekutta) {
393 Position a(N),b(N),c(N),d(N),ia(N),ib(N);
394 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,x0,a);
395 ia=x0+(a-x0)/2.0;
396 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,ia,b);
397 ib=x0+(b-x0)/2.0;
398 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,ib,c);
399 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,c,d);
400 x1=a+2.0*b+2.0*c+d;
401 x1/=6.0;
402 } else {
403 computeDescentVectorOnBothAxes(xAxis,yAxis,stress,x0,x1);
404 }
405}
406
407
408// Used for sorting the CompoundConstraints from lowest priority to highest.
410 const cola::CompoundConstraint *rhs)
411{
412 return lhs->priority() < rhs->priority();
413}
414
415
417 vpsc::Variables (&vars)[2], unsigned int& priority,
419 cola::CompoundConstraints& idleConstraints)
420{
421 for (std::vector<Cluster*>::iterator curr = cluster->clusters.begin();
422 curr != cluster->clusters.end(); ++curr)
423 {
424 // For each of the child clusters, recursively call this function.
426 noc, *curr, idleConstraints);
427 }
428
429 if ( (noc == nullptr) && (dynamic_cast<RootCluster *> (cluster) == nullptr) )
430 {
431 double freeWeight = 0.00000000001;
432 // Then create left and right variables for the boundary of this
433 // cluster.
434 vpsc::Variable *variable = nullptr;
435 cluster->clusterVarId = vars[XDIM].size();
436 COLA_ASSERT(vars[XDIM].size() == vars[YDIM].size());
437 // Left:
438 variable = new vpsc::Variable(vars[XDIM].size(),
439 cluster->bounds.getMinX(), freeWeight);
440 vars[XDIM].push_back(variable);
441 // Right:
442 variable = new vpsc::Variable(vars[XDIM].size(),
443 cluster->bounds.getMaxX(), freeWeight);
444 vars[XDIM].push_back(variable);
445 // Bottom::
446 variable = new vpsc::Variable(vars[YDIM].size(),
447 cluster->bounds.getMinY(), freeWeight);
448 vars[YDIM].push_back(variable);
449 // Top:
450 variable = new vpsc::Variable(vars[YDIM].size(),
451 cluster->bounds.getMaxY(), freeWeight);
452 vars[YDIM].push_back(variable);
453
454 RectangularCluster *rc = dynamic_cast<RectangularCluster *> (cluster);
455 if (rc)
456 {
457 rc->generateFixedRectangleConstraints(idleConstraints,
458 boundingBoxes, vars);
459 }
460
461 priority--;
463 new cola::ClusterContainmentConstraints(cluster, priority,
465 idleConstraints.push_back(ccc);
466 }
467
468 if (noc)
469 {
470 // Enforce non-overlap between all the shapes and clusters at this
471 // level.
472 //printf("Cluster #%d non-overlap constraints - nodes %d clusters %d\n",
473 // (int) cluster->clusterVarId, (int) cluster->nodes.size(),
474 // (int) cluster->clusters.size());
475 unsigned int group = cluster->clusterVarId;
476 // The set of clusters to put non-overlap constraints between is the
477 // child clusters of this cluster. We will also add any overlapping
478 // clusters (due to multiple inheritence) to this set.
479 std::set<Cluster *> expandedClusterSet(cluster->clusters.begin(),
480 cluster->clusters.end());
481 for (std::set<unsigned>::iterator curr = cluster->nodes.begin();
482 curr != cluster->nodes.end(); ++curr)
483 {
484 unsigned id = *curr;
485
486 if (cluster->m_overlap_replacement_map.count(id) > 0)
487 {
488 // This shape is child of another cluster also, so replace
489 // this node with the other cluster for the purpose of
490 // non-overlap with other children of the current cluster.
491 expandedClusterSet.insert(
492 cluster->m_overlap_replacement_map[id]);
493 }
494 // Normal case: Add shape for generation of non-overlap
495 // constraints.
496 noc->addShape(id, boundingBoxes[id]->width() / 2,
497 boundingBoxes[id]->height() / 2, group);
498 }
499 for (std::set<Cluster*>::iterator curr = expandedClusterSet.begin();
500 curr != expandedClusterSet.end(); ++curr)
501 {
502 Cluster *cluster = *curr;
503 RectangularCluster *rectCluster =
504 dynamic_cast<RectangularCluster *> (cluster);
505 if (rectCluster && rectCluster->clusterIsFromFixedRectangle())
506 {
507 // Treat it like a shape for non-overlap.
508 unsigned id = rectCluster->rectangleIndex();
509 noc->addShape(id, boundingBoxes[id]->width() / 2,
510 boundingBoxes[id]->height() / 2, group);
511 }
512 else
513 {
514 // Normal cluster.
515 noc->addCluster(cluster, group);
516 }
517 }
518
519 // For the set of shapes that have been replaced due to multiple
520 // inheritance, still generate overlap constraints between them.
521 // (The group uses the ID of the right side variable of the cluster
522 // so it is not the same group as the cluster itself.)
523 for (std::set<unsigned>::iterator curr =
524 cluster->m_nodes_replaced_with_clusters.begin();
525 curr != cluster->m_nodes_replaced_with_clusters.end(); ++curr)
526 {
527 unsigned id = *curr;
528 noc->addShape(id, boundingBoxes[id]->width() / 2,
529 boundingBoxes[id]->height() / 2, group + 1);
530 }
531
532 }
533}
534
536 vpsc::Variables (&vs)[2])
537{
539 {
540 // Add remaining nodes that aren't contained within any clusters
541 // as children of the root cluster.
542 std::vector<unsigned> nodesInClusterCounts(boundingBoxes.size(), 0);
543 clusterHierarchy->countContainedNodes(nodesInClusterCounts);
544
545 for (unsigned int i = 0; i < nodesInClusterCounts.size(); ++i)
546 {
547 unsigned count = nodesInClusterCounts[i];
549 count > 1)
550 {
551 fprintf(stderr, "Warning: node %u is contained in %d "
552 "clusters.\n", i, count);
553 }
554
555 if (count == 0)
556 {
557 // Not present in hierarchy, so add to root cluster.
558 clusterHierarchy->nodes.insert(i);
559 }
560 }
561
562 // Add non-overlap and containment constraints for all clusters
563 // and nodes.
564 unsigned int priority = PRIORITY_NONOVERLAP;
566
567 // Generate the containment constraints
570
571 // Compute overlapping clusters.
573
574 // Generate non-overlap constraints between all clusters and
575 // all contained nodes.
577 {
578 priority--;
581 priority);
586 extraConstraints.push_back(noc);
587 }
588 }
590 {
591 // Add standard non-overlap constraints between each pair of
592 // nodes.
595 for (unsigned int i = 0; i < boundingBoxes.size(); ++i)
596 {
597 noc->addShape(i, boundingBoxes[i]->width() / 2,
598 boundingBoxes[i]->height() / 2);
599 }
600 extraConstraints.push_back(noc);
601 }
602}
603
604void ConstrainedFDLayout::makeFeasible(double xBorder, double yBorder)
605{
606 vpsc::Variables vs[2];
607 vpsc::Constraints valid[2];
608
611
612 // Populate all the variables for shapes.
613 for (unsigned int dim = 0; dim < 2; ++dim)
614 {
615 vs[dim] = vpsc::Variables(boundingBoxes.size());
616 for (unsigned int i = 0; i < vs[dim].size(); ++i)
617 {
618 double pos = (dim == 0) ?
619 boundingBoxes[i]->getCentreX() :
620 boundingBoxes[i]->getCentreY();
621 vs[dim][i] = new vpsc::Variable(i, pos, 1);
622 }
623 }
624
625 vector<double> priorPos(boundingBoxes.size());
626
628
629 // Make a copy of the compound constraints and sort them by priority.
630 cola::CompoundConstraints idleConstraints = ccs;
631 // Append extraConstraints to idleConstraints.
632 idleConstraints.insert(idleConstraints.end(),
633 extraConstraints.begin(), extraConstraints.end());
634
635 std::sort(idleConstraints.begin(), idleConstraints.end(),
637
638 // Initialise extra variables for compound constraints.
639 for (unsigned int dim = 0; dim < 2; ++dim)
640 {
641 generateVariables(idleConstraints, (vpsc::Dim) dim, vs[dim]);
642 }
643
644#ifdef MAKEFEASIBLE_DEBUG
645 int iteration = 0;
646 vector<string> labels(boundingBoxes.size());
647 for(unsigned i=0;i<boundingBoxes.size();++i)
648 {
649 stringstream ss;
650 ss << i;
651 labels[i]=ss.str();
652 }
653#endif
654
655 // We can keep adding new constraints to the existing VPSC instances so
656 // long as everything is satisfiable. Only when it's not do we discard
657 // the existing VPSC instance for that dimension and create a new one.
658 vpsc::IncSolver *solver[2] = { nullptr };
659
660 // Main makeFeasible loop.
661 while (!idleConstraints.empty())
662 {
663 // idleConstraints is sorted lowest to highest priority, so the
664 // highest priority constraint will be at the back of the vector.
665 cola::CompoundConstraint *cc = idleConstraints.back();
666 idleConstraints.pop_back();
667
668#ifdef MAKEFEASIBLE_DEBUG
669 {
670 // Debugging SVG time slice output.
671 std::vector<cola::Edge> es;
672 for (unsigned int i = 0; i < boundingBoxes.size(); ++i)
673 {
674 boundingBoxes[i]->moveCentreX(vs[0][i]->finalPosition);
675 boundingBoxes[i]->moveCentreY(vs[1][i]->finalPosition);
676 }
677 iteration++;
678 std::sstream filename;
679 filename << "out/file-" << std::setfill('0') << std::setw(5) << iteration << ".pdf";
680
681 OutputFile of(boundingBoxes,es,clusterHierarchy,filename.str().c_str(),true,false);
682 of.setLabels(labels);
683 of.generate();
684 }
685#endif
686
688 bool subConstraintSatisfiable = true;
689
691 {
692 // We are processing a combined set of satisfiable constraints,
693 // such as for containment within cluster boundary variables, so
694 // we just add all the required constraints and solve in both
695 // the X and Y dimension once to set the cluster boundaries to
696 // meaningful values.
697 while (cc->subConstraintsRemaining())
698 {
701 // There should be no alternatives, just guaranteed
702 // satisfiable constraints.
703 COLA_ASSERT(alternatives.size() == 1);
704 vpsc::Dim& dim = alternatives.front().dim;
705 vpsc::Constraint& constraint = alternatives.front().constraint;
706 vpsc::Constraint *newConstraint =
707 new vpsc::Constraint(constraint);
708 valid[dim].push_back(newConstraint);
709 if (solver[dim])
710 {
711 // If we have an existing valid solver instance, add the
712 // constraint to that.
713 solver[dim]->addConstraint(newConstraint);
714 }
715 cc->markCurrSubConstraintAsActive(subConstraintSatisfiable);
716 }
717 // Satisfy the constraints in each dimension.
718 for (size_t dim = 0; dim < 2; ++dim)
719 {
720 if (solver[dim] == nullptr)
721 {
722 // Create a new VPSC solver if necessary.
723 solver[dim] = new vpsc::IncSolver(vs[dim], valid[dim]);
724 }
725 solver[dim]->satisfy();
726 }
727 continue;
728 }
729
730 while (cc->subConstraintsRemaining())
731 {
734 alternatives.sort();
735
736 if (alternatives.empty())
737 {
738 continue;
739 }
740
741 while (!alternatives.empty())
742 {
743 // Reset subConstraintSatisfiable for new solve.
744 subConstraintSatisfiable = true;
745
746 vpsc::Dim& dim = alternatives.front().dim;
747 vpsc::Constraint& constraint = alternatives.front().constraint;
748
749 // Store current values for variables.
750 for (unsigned int i = 0; i < priorPos.size(); ++i)
751 {
752 priorPos[i] = vs[dim][i]->finalPosition;
753 }
754
755 // Some solving...
756 try
757 {
758 // Add the constraint from this alternative to the
759 // valid constraint set.
760 vpsc::Constraint *newConstraint =
761 new vpsc::Constraint(constraint);
762 valid[dim].push_back(newConstraint);
763
764 //fprintf(stderr, ".%d %3d - ", dim, valid[dim].size());
765
766 // Try to satisfy this set of constraints..
767 if (solver[dim] == nullptr)
768 {
769 // Create a new VPSC solver if necessary.
770 solver[dim] = new vpsc::IncSolver(vs[dim], valid[dim]);
771 }
772 else
773 {
774 // Or just add the constraint to the existing solver.
775 solver[dim]->addConstraint(newConstraint);
776 }
777 solver[dim]->satisfy();
778 }
779 catch (char *str)
780 {
781 subConstraintSatisfiable = false;
782
783 std::cerr << "++++ IN ERROR BLOCK" << std::endl;
784 std::cerr << str << std::endl;
785 for (vpsc::Rectangles::iterator r = boundingBoxes.begin();
786 r != boundingBoxes.end(); ++r)
787 {
788 std::cerr << **r <<std::endl;
789 }
790 }
791 for (size_t i = 0; i < valid[dim].size(); ++i)
792 {
793 if (valid[dim][i]->unsatisfiable)
794 {
795 // It might have made one of the earlier added
796 // constraints unsatisfiable, so we mark that one
797 // as okay since we will be reverting the most
798 // recent one.
799 valid[dim][i]->unsatisfiable = false;
800
801 subConstraintSatisfiable = false;
802 }
803 }
804
805 if (!subConstraintSatisfiable)
806 {
807 // Since we had unsatisfiable constraints we must
808 // discard this solver instance.
809 delete solver[dim];
810 solver[dim] = nullptr;
811
812 // Restore previous values for variables.
813 for (unsigned int i = 0; i < priorPos.size(); ++i)
814 {
815 vs[dim][i]->finalPosition = priorPos[i];
816 }
817
818 // Delete the newly added (and unsatisfiable)
819 // constraint from the valid constraint set.
820 delete valid[dim].back();
821 valid[dim].pop_back();
822 }
823 else
824 {
825 // Satisfied, so don't need to consider other alternatives.
826 break;
827 }
828 // Move on to the next alternative.
829 alternatives.pop_front();
830 }
831#ifdef MAKEFEASIBLE_DEBUG
832 if (true || idleConstraints.size() == 0)
833 {
834 // Debugging SVG time slice output, but don't show this for
835 // constraints that promised satisfiable.
836 std::vector<cola::Edge> es;
837 for (unsigned int i = 0; i < boundingBoxes.size(); ++i)
838 {
839 boundingBoxes[i]->moveCentreX(vs[0][i]->finalPosition);
840 boundingBoxes[i]->moveCentreY(vs[1][i]->finalPosition);
841 }
842 iteration++;
843 std::sstream filename;
844 filename << "out/file-" << std::setfill('0') << std::setw(5) << iteration << ".pdf";
845
846 OutputFile of(boundingBoxes,es,clusterHierarchy,filename.str().c_str(),
847 true,false);
848 of.setLabels(labels);
849 of.generate();
850 }
851#endif
852 cc->markCurrSubConstraintAsActive(subConstraintSatisfiable);
853 }
854 }
855
856 // Delete the persistent VPSC solver instances.
857 for (size_t dim = 0; dim < 2; ++dim)
858 {
859 if (solver[dim])
860 {
861 delete solver[dim];
862 solver[dim] = nullptr;
863 }
864 }
865
866 // Write positions from solver variables back to Rectangles.
867 for (unsigned int i = 0; i < boundingBoxes.size(); ++i)
868 {
869 boundingBoxes[i]->moveCentreX(vs[0][i]->finalPosition);
870 boundingBoxes[i]->moveCentreY(vs[1][i]->finalPosition);
871 }
872
875
876 // Cleanup.
877 for (unsigned int dim = 0; dim < 2; ++dim)
878 {
879 for_each(valid[dim].begin(), valid[dim].end(), delete_object());
880 for_each(vs[dim].begin(), vs[dim].end(), delete_object());
881 }
882
885
886 // Update the X and Y vectors with the new shape positions.
887 for (unsigned int i = 0; i < boundingBoxes.size(); ++i)
888 {
889 X[i] = boundingBoxes[i]->getCentreX();
890 Y[i] = boundingBoxes[i]->getCentreY();
891 }
892
893 // Clear extra constraints for cluster containment and non-overlap.
894 for_each(extraConstraints.begin(), extraConstraints.end(), delete_object());
895 extraConstraints.clear();
896}
897
899{
901 {
902 delete done;
903 }
904
905 for (unsigned i = 0; i < n; ++i)
906 {
907 delete [] G[i];
908 delete [] D[i];
909 }
910 delete [] G;
911 delete [] D;
912 delete topologyAddon;
914}
915
917{
918 // Free Rectangles
919 for_each(boundingBoxes.begin(), boundingBoxes.end(), delete_object());
920 boundingBoxes.clear();
921
922 // Free compound constraints
923 std::list<CompoundConstraint *> freeList(ccs.begin(), ccs.end());
924 freeList.sort();
925 freeList.unique();
926 if (freeList.size() != ccs.size())
927 {
928 // The compound constraint list had repeated elements.
929 fprintf(stderr, "Warning: CompoundConstraints vector contained %d "
930 "duplicates.\n", (int) (ccs.size() - freeList.size()));
931 }
932 ccs.clear();
933 for_each(freeList.begin(), freeList.end(), delete_object());
934
936 {
937 delete clusterHierarchy;
938 clusterHierarchy = nullptr;
939 }
940
942}
943
945{
946 COLA_ASSERT(topologyAddon);
947 delete topologyAddon;
948 topologyAddon = newTopology->clone();
949}
950
955
956
958 const vpsc::Dim dim, vpsc::Rectangles& boundingBoxes,
959 RootCluster *clusterHierarchy,
961 valarray<double> &coords)
962{
963 vs.resize(n);
964 for (unsigned i = 0; i < n; ++i)
965 {
966 vs[i] = new vpsc::Variable(i, coords[i]);
967 }
968
969 if (clusterHierarchy && !clusterHierarchy->flat())
970 {
971 // Create variables for clusters
972 clusterHierarchy->computeBoundingRect(boundingBoxes);
973 clusterHierarchy->createVars(dim, boundingBoxes, vs);
974 }
975
976 for (CompoundConstraints::const_iterator c = ccs.begin();
977 c != ccs.end(); ++c)
978 {
979 (*c)->generateVariables(dim, vs);
980 }
981 for (CompoundConstraints::const_iterator c = ccs.begin();
982 c != ccs.end(); ++c)
983 {
984 (*c)->generateSeparationConstraints(dim, vs, cs, boundingBoxes);
985 }
986}
987
988
990 const vpsc::Dim dim, vpsc::Variables& vs, vpsc::Constraints& cs,
991 vpsc::Rectangles& boundingBoxes)
992{
993 for (CompoundConstraints::const_iterator c = ccs.begin();
994 c != ccs.end(); ++c)
995 {
996 (*c)->generateVariables(dim, vs);
997 }
998 for (CompoundConstraints::const_iterator c = ccs.begin();
999 c != ccs.end(); ++c)
1000 {
1001 (*c)->generateSeparationConstraints(dim, vs, cs, boundingBoxes);
1002 }
1003}
1004
1006 const CompoundConstraints& ccs)
1007{
1008 for (CompoundConstraints::const_iterator c = ccs.begin();
1009 c != ccs.end(); ++c)
1010 {
1011 (*c)->updatePosition(dim);
1012 }
1013}
1014void project(vpsc::Variables& vs, vpsc::Constraints& cs, valarray<double>& coords) {
1015 unsigned n=coords.size();
1016 vpsc::IncSolver s(vs,cs);
1017 s.solve();
1018 for(unsigned i=0;i<n;++i) {
1019 coords[i]=vs[i]->finalPosition;
1020 }
1021}
1023 const DesiredPositionsInDim& des, valarray<double>& coords)
1024{
1025 COLA_UNUSED(cs);
1026
1027 unsigned n=coords.size();
1028 COLA_ASSERT(vs.size()>=n);
1029 for(unsigned i=0;i<n;++i) {
1030 vpsc::Variable* v=vs[i];
1031 v->desiredPosition = coords[i];
1032 v->weight=1;
1033 }
1034 for (DesiredPositionsInDim::const_iterator d=des.begin();
1035 d!=des.end(); ++d) {
1036 COLA_ASSERT(d->first<vs.size());
1037 vpsc::Variable* v=vs[d->first];
1038 v->desiredPosition = d->second;
1039 v->weight=10000;
1040 }
1041}
1043 UnsatisfiableConstraintInfos* unsatisfiable) {
1044 for(vpsc::Constraints::const_iterator c=cs.begin();c!=cs.end();++c) {
1045 if((*c)->unsatisfiable) {
1047 unsatisfiable->push_back(i);
1048 }
1049 }
1050}
1051
1053{
1054 topologyAddon->handleResizes(resizeList, n, X, Y, ccs, boundingBoxes,
1056}
1057
1058/*
1059 * move positions of nodes in specified axis while respecting constraints
1060 * @param dim axis
1061 * @param target array of desired positions (for both axes)
1062 */
1064 COLA_ASSERT(target.size()==2*n);
1065 FILE_LOG(logDEBUG) << "ConstrainedFDLayout::moveTo(): dim="<<dim;
1066 valarray<double> &coords = (dim==vpsc::HORIZONTAL)?X:Y;
1067 vpsc::Variables vs;
1070 clusterHierarchy, vs, cs, coords);
1072 if(preIteration) {
1073 for(vector<Lock>::iterator l=preIteration->locks.begin();
1074 l!=preIteration->locks.end();l++) {
1075 des.push_back(make_pair(l->getID(),l->pos(dim)));
1076 FILE_LOG(logDEBUG1)<<"desi: v["<<l->getID()<<"]=("<<l->pos(vpsc::HORIZONTAL)
1077 <<","<<l->pos(vpsc::VERTICAL)<<")";
1078 }
1079 }
1080 for(unsigned i=0, j=(dim==vpsc::HORIZONTAL?0:n);i<n;++i,++j) {
1081 vpsc::Variable* v=vs[i];
1082 v->desiredPosition = target[j];
1083 }
1084 setVariableDesiredPositions(vs,cs,des,coords);
1086 {
1087 topologyAddon->moveTo(dim, vs, cs, coords, clusterHierarchy);
1088 } else {
1089 // Add non-overlap constraints, but not variables again.
1091 // Projection.
1092 project(vs,cs,coords);
1094 }
1096 for_each(vs.begin(),vs.end(),delete_object());
1097 for_each(cs.begin(),cs.end(),delete_object());
1098}
1099/*
1100 * The following computes an unconstrained solution then uses Projection to
1101 * make this solution feasible with respect to constraints by moving things as
1102 * little as possible. If "meta-constraints" such as avoidOverlaps or edge
1103 * straightening are required then dummy variables will be generated.
1104 */
1105double ConstrainedFDLayout::applyForcesAndConstraints(const vpsc::Dim dim, const double oldStress) {
1106 FILE_LOG(logDEBUG) << "ConstrainedFDLayout::applyForcesAndConstraints(): dim="<<dim;
1107 valarray<double> g(n);
1108 valarray<double> &coords = (dim==vpsc::HORIZONTAL)?X:Y;
1110 if(preIteration) {
1111 for(vector<Lock>::iterator l=preIteration->locks.begin();
1112 l!=preIteration->locks.end();l++) {
1113 des.push_back(make_pair(l->getID(),l->pos(dim)));
1114 FILE_LOG(logDEBUG1)<<"desi: v["<<l->getID()<<"]=("<<l->pos(vpsc::HORIZONTAL)
1115 <<","<<l->pos(vpsc::VERTICAL)<<")";
1116 }
1117 }
1118 vpsc::Variables vs;
1120 double stress;
1122 clusterHierarchy, vs, cs, coords);
1123
1125 {
1126 stress = topologyAddon->applyForcesAndConstraints(this, dim, g, vs, cs,
1127 coords, des, oldStress);
1128 } else {
1129 // Add non-overlap constraints, but not variables again.
1131 // Projection.
1132 SparseMap HMap(n);
1133 computeForces(dim,HMap,g);
1134 SparseMatrix H(HMap);
1135 valarray<double> oldCoords=coords;
1136 applyDescentVector(g,oldCoords,coords,oldStress,computeStepSize(H,g,g));
1137 setVariableDesiredPositions(vs,cs,des,coords);
1138 project(vs,cs,coords);
1139 valarray<double> d(n);
1140 d=oldCoords-coords;
1141 double stepsize=computeStepSize(H,g,d);
1142 stepsize=max(0.,min(stepsize,1.));
1143 //printf(" dim=%d beta: ",dim);
1144 stress = applyDescentVector(d,oldCoords,coords,oldStress,stepsize);
1146 }
1148 if(unsatisfiable.size()==2) {
1150 }
1151 FILE_LOG(logDEBUG) << "ConstrainedFDLayout::applyForcesAndConstraints... done, stress="<<stress;
1152 if (clusterHierarchy)
1153 {
1156 }
1157
1158 for_each(vs.begin(),vs.end(),delete_object());
1159 for_each(cs.begin(),cs.end(),delete_object());
1160 return stress;
1161}
1162/*
1163 * Attempts to set coords=oldCoords-stepsize*d. If this does not reduce
1164 * the stress from oldStress then stepsize is halved. This is repeated
1165 * until stepsize falls below a threshhold.
1166 * @param d is a descent vector (a movement vector intended to reduce the
1167 * stress)
1168 * @param oldCoords are the previous position vector
1169 * @param coords will hold the new position after applying d
1170 * @param stepsize is a scalar multiple of the d to apply
1171 */
1173 valarray<double> const &d,
1174 valarray<double> const &oldCoords,
1175 valarray<double> &coords,
1176 const double oldStress,
1177 double stepsize
1178 )
1179{
1180 COLA_UNUSED(oldStress);
1181
1182 COLA_ASSERT(d.size()==oldCoords.size());
1183 COLA_ASSERT(d.size()==coords.size());
1184 while(fabs(stepsize)>0.00000000001) {
1185 coords=oldCoords-stepsize*d;
1186 double stress=computeStress();
1187 //printf(" applyDV: oldstress=%f, stress=%f, stepsize=%f\n", oldStress,stress,stepsize);
1188 //if(oldStress>=stress) {
1189 return stress;
1190 //}
1191 coords=oldCoords;
1192 stepsize*=0.5;
1193 }
1194 return computeStress();
1195}
1196
1197
1198// Computes X and Y offsets for nodes that are at the same position.
1199std::vector<double> ConstrainedFDLayout::offsetDir(double minD)
1200{
1201 std::vector<double> u(2);
1202 double l = 0;
1203 for (size_t i = 0; i < 2; ++i)
1204 {
1205 double x = u[i] = random.getNextBetween(0.01, 1) - 0.5;
1206 l += x * x;
1207 }
1208 l = sqrt(l);
1209
1210 for (size_t i = 0; i < 2; ++i)
1211 {
1212 u[i] *= (minD / l);
1213 }
1214
1215 return u;
1216}
1217
1218
1219/*
1220 * Computes:
1221 * - the matrix of second derivatives (the Hessian) H, used in
1222 * calculating stepsize; and
1223 * - the vector g, the negative gradient (steepest-descent) direction.
1224 */
1226 const vpsc::Dim dim,
1227 SparseMap &H,
1228 valarray<double> &g) {
1229 if(n==1) return;
1230 g=0;
1231 // for each node:
1232 for(unsigned u=0;u<n;u++) {
1233 // Stress model
1234 double Huu=0;
1235 for(unsigned v=0;v<n;v++) {
1236 if(u==v) continue;
1237 if (m_useNeighbourStress && neighbours[u][v]!=1) continue;
1238
1239 // The following loop randomly displaces nodes that are at identical positions
1240 double rx=X[u]-X[v], ry=Y[u]-Y[v];
1241 double sd2 = rx*rx+ry*ry;
1242 unsigned maxDisplaces = n; // avoid infinite loop in the case of numerical issues, such as huge values
1243
1244 while (maxDisplaces--)
1245 {
1246 if ((sd2) > 1e-3)
1247 {
1248 break;
1249 }
1250
1251 std::vector<double> rd = offsetDir(minD);
1252 X[v] += rd[0];
1253 Y[v] += rd[1];
1254 rx=X[u]-X[v], ry=Y[u]-Y[v];
1255 sd2 = rx*rx+ry*ry;
1256 }
1257
1258 unsigned short p = G[u][v];
1259 // no forces between disconnected parts of the graph
1260 if(p==0) continue;
1261 double l=sqrt(sd2);
1262 double d=D[u][v];
1263 if(l>d && p>1) continue; // attractive forces not required
1264 double d2=d*d;
1265 /* force apart zero distances */
1266 if (l < 1e-30) {
1267 l=0.1;
1268 }
1269 double dx=dim==vpsc::HORIZONTAL?rx:ry;
1270 double dy=dim==vpsc::HORIZONTAL?ry:rx;
1271 g[u]+=dx*(l-d)/(d2*l);
1272 Huu-=H(u,v)=(d*dy*dy/(l*l*l)-1)/d2;
1273 }
1274 H(u,u)=Huu;
1275 }
1276 if(desiredPositions) {
1277 for(DesiredPositions::const_iterator p=desiredPositions->begin();
1278 p!=desiredPositions->end();++p) {
1279 unsigned i = p->id;
1280 double d=(dim==vpsc::HORIZONTAL)
1281 ?p->x-X[i]:p->y-Y[i];
1282 d*=p->weight;
1283 g[i]-=d;
1284 H(i,i)+=p->weight;
1285 }
1286 }
1287}
1288/*
1289 * Returns the optimal step-size in the direction d, given gradient g and
1290 * hessian H.
1291 */
1293 SparseMatrix const &H,
1294 valarray<double> const &g,
1295 valarray<double> const &d) const
1296{
1297 COLA_ASSERT(g.size()==d.size());
1298 COLA_ASSERT(g.size()==H.rowSize());
1299 // stepsize = g'd / (d' H d)
1300 double numerator = dotProd(g,d);
1301 valarray<double> Hd(d.size());
1302 H.rightMultiply(d,Hd);
1303 double denominator = dotProd(d,Hd);
1304 //COLA_ASSERT(numerator>=0);
1305 //COLA_ASSERT(denominator>=0);
1306 if(denominator==0) return 0;
1307 return numerator/denominator;
1308}
1309/*
1310 * Just computes the cost (Stress) at the current X,Y position
1311 * used to test termination.
1312 * This method will call preIteration if one is set.
1313 */
1315 FILE_LOG(logDEBUG)<<"ConstrainedFDLayout::computeStress()";
1316 double stress=0;
1317 for(unsigned u=0;(u + 1)<n;u++) {
1318 for(unsigned v=u+1;v<n;v++) {
1319 if (m_useNeighbourStress && neighbours[u][v]!=1) continue;
1320 unsigned short p=G[u][v];
1321 // no forces between disconnected parts of the graph
1322 if(p==0) continue;
1323 double rx=X[u]-X[v], ry=Y[u]-Y[v];
1324 double l=sqrt(rx*rx+ry*ry);
1325 double d=D[u][v];
1326 if(l>d && p>1) continue; // no attractive forces required
1327 double d2=d*d;
1328 double rl=d-l;
1329 double s=rl*rl/d2;
1330 stress+=s;
1331 FILE_LOG(logDEBUG2)<<"s("<<u<<","<<v<<")="<<s;
1332 }
1333 }
1334 if(preIteration) {
1335 if ((*preIteration)()) {
1336 for(vector<Lock>::iterator l=preIteration->locks.begin();
1337 l!=preIteration->locks.end();l++) {
1338 double dx=l->pos(vpsc::HORIZONTAL)-X[l->getID()], dy=l->pos(vpsc::VERTICAL)-Y[l->getID()];
1339 double s=10000*(dx*dx+dy*dy);
1340 stress+=s;
1341 FILE_LOG(logDEBUG2)<<"d("<<l->getID()<<")="<<s;
1342 }
1343 }
1344 }
1345 stress += topologyAddon->computeStress();
1346 if(desiredPositions) {
1347 for(DesiredPositions::const_iterator p = desiredPositions->begin();
1348 p!=desiredPositions->end();++p) {
1349 double dx = X[p->id] - p->x, dy = Y[p->id] - p->y;
1350 stress+=0.5*p->weight*(dx*dx+dy*dy);
1351 }
1352 }
1353 return stress;
1354}
1356 for(unsigned i=0;i<n;i++) {
1357 boundingBoxes[i]->moveCentre(X[i],Y[i]);
1358 }
1359}
1360
1361static const double LIMIT = 100000000;
1362
1363static void reduceRange(double& val)
1364{
1365 val = std::min(val, LIMIT);
1366 val = std::max(val, -LIMIT);
1367}
1368
1369void ConstrainedFDLayout::outputInstanceToSVG(std::string instanceName)
1370{
1371 std::string filename;
1372 if (!instanceName.empty())
1373 {
1374 filename = instanceName;
1375 }
1376 else
1377 {
1378 filename = "libcola-debug";
1379 }
1380 filename += ".svg";
1381 FILE *fp = fopen(filename.c_str(), "w");
1382
1383 if (fp == nullptr)
1384 {
1385 return;
1386 }
1387
1388
1389 double minX = LIMIT;
1390 double minY = LIMIT;
1391 double maxX = -LIMIT;
1392 double maxY = -LIMIT;
1393
1394 // Find the bounds of the diagram.
1395 for (size_t i = 0; i < boundingBoxes.size(); ++i)
1396 {
1397 double rMinX = boundingBoxes[i]->getMinX();
1398 double rMaxX = boundingBoxes[i]->getMaxX();
1399 double rMinY = boundingBoxes[i]->getMinY();
1400 double rMaxY = boundingBoxes[i]->getMaxY();
1401
1402 reduceRange(rMinX);
1403 reduceRange(rMaxX);
1404 reduceRange(rMinY);
1405 reduceRange(rMaxY);
1406
1407 if (rMinX > -LIMIT)
1408 {
1409 minX = std::min(minX, rMinX);
1410 }
1411 if (rMaxX < LIMIT)
1412 {
1413 maxX = std::max(maxX,rMaxX);
1414 }
1415 if (rMinY > -LIMIT)
1416 {
1417 minY = std::min(minY, rMinY);
1418 }
1419 if (rMaxY < LIMIT)
1420 {
1421 maxY = std::max(maxY, rMaxY);
1422 }
1423 }
1424
1425 minX -= 50;
1426 minY -= 50;
1427 maxX += 50;
1428 maxY += 50;
1429
1430 fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1431 fprintf(fp, "<svg xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100%%\" height=\"100%%\" viewBox=\"%g %g %g %g\">\n", minX, minY, maxX - minX, maxY - minY);
1432
1433 // Output source code to generate this ConstrainedFDLayout instance.
1434 fprintf(fp, "<!-- Source code to generate this instance:\n");
1435 fprintf(fp, "#include <vector>\n");
1436 fprintf(fp, "#include <utility>\n");
1437 fprintf(fp, "#include \"libcola/cola.h\"\n");
1438 fprintf(fp, "using namespace cola;\n");
1439 fprintf(fp, "int main(void) {\n");
1440 fprintf(fp, " CompoundConstraints ccs;\n");
1441 fprintf(fp, " std::vector<Edge> es;\n");
1442 fprintf(fp, " EdgeLengths eLengths;\n");
1443 fprintf(fp, " double defaultEdgeLength=%g;\n", m_idealEdgeLength);
1444 fprintf(fp, " std::vector<vpsc::Rectangle*> rs;\n");
1445 fprintf(fp, " vpsc::Rectangle *rect = nullptr;\n\n");
1446 for (size_t i = 0; i < boundingBoxes.size(); ++i)
1447 {
1448 fprintf(fp, " rect = new vpsc::Rectangle(%g, %g, %g, %g);\n",
1449 boundingBoxes[i]->getMinX(), boundingBoxes[i]->getMaxX(),
1450 boundingBoxes[i]->getMinY(), boundingBoxes[i]->getMaxY());
1451 fprintf(fp, " rs.push_back(rect);\n\n");
1452 }
1453
1454 for (size_t i = 0; i < n; ++i)
1455 {
1456 for (size_t j = i + 1; j < n; ++j)
1457 {
1458 if (G[i][j] == 1)
1459 {
1460 fprintf(fp, " es.push_back(std::make_pair(%lu, %lu));\n", i, j);
1461 }
1462 }
1463 }
1464 fprintf(fp, "\n");
1465
1466 if (m_edge_lengths.size() > 0)
1467 {
1468 fprintf(fp, " eLengths.resize(%d);\n", (int) m_edge_lengths.size());
1469 for (size_t i = 0; i < m_edge_lengths.size(); ++i)
1470 {
1471 fprintf(fp, " eLengths[%d] = %g;\n", (int) i, m_edge_lengths[i]);
1472 }
1473 fprintf(fp, "\n");
1474 }
1475
1476 for (cola::CompoundConstraints::iterator c = ccs.begin();
1477 c != ccs.end(); ++c)
1478 {
1479 (*c)->printCreationCode(fp);
1480 }
1481
1482 fprintf(fp, " ConstrainedFDLayout alg(rs, es, defaultEdgeLength, eLengths);\n");
1483 if (clusterHierarchy)
1484 {
1486 fprintf(fp, " alg.setClusterHierarchy(cluster%llu);\n",
1487 (unsigned long long) clusterHierarchy);
1488 }
1489 fprintf(fp, " alg.setConstraints(ccs);\n");
1490 fprintf(fp, " alg.setAvoidNodeOverlaps(%s);\n",
1491 (m_generateNonOverlapConstraints) ? "true" : "false");
1492 fprintf(fp, " alg.makeFeasible();\n");
1493 fprintf(fp, " alg.run();\n");
1494 fprintf(fp, " alg.freeAssociatedObjects();\n");
1495 fprintf(fp, " return 0;\n");
1496 fprintf(fp, "};\n");
1497 fprintf(fp, "-->\n");
1498
1499 if (clusterHierarchy)
1500 {
1502 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
1503 "inkscape:label=\"Clusters\">\n");
1505 fprintf(fp, "</g>\n");
1506 }
1507
1508 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
1509 "inkscape:label=\"Rects\">\n");
1510 for (size_t i = 0; i < boundingBoxes.size(); ++i)
1511 {
1512 double minX = boundingBoxes[i]->getMinX();
1513 double maxX = boundingBoxes[i]->getMaxX();
1514 double minY = boundingBoxes[i]->getMinY();
1515 double maxY = boundingBoxes[i]->getMaxY();
1516
1517 fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" "
1518 "height=\"%g\" style=\"stroke-width: 1px; stroke: black; "
1519 "fill: blue; fill-opacity: 0.3;\" />\n",
1520 (unsigned) i, minX, minY, maxX - minX, maxY - minY);
1521 }
1522 fprintf(fp, "</g>\n");
1523
1524 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
1525 "inkscape:label=\"Edges\">\n");
1526 for (size_t i = 0; i < n; ++i)
1527 {
1528 for (size_t j = i + 1; j < n; ++j)
1529 {
1530 if (G[i][j] == 1)
1531 {
1532 fprintf(fp, "<path d=\"M %g %g L %g %g\" "
1533 "style=\"stroke-width: 1px; stroke: black;\" />\n",
1534 boundingBoxes[i]->getCentreX(),
1535 boundingBoxes[i]->getCentreY(),
1536 boundingBoxes[j]->getCentreX(),
1537 boundingBoxes[j]->getCentreY());
1538 }
1539 }
1540 }
1541 fprintf(fp, "</g>\n");
1542
1543 fprintf(fp, "</svg>\n");
1544 fclose(fp);
1545}
1546
1548 bool preventOverlaps, int accept, unsigned debugLevel)
1549{
1550 size_t n = rs.size();
1551 // Set up nonoverlap constraints if desired.
1552 NonOverlapConstraintExemptions *nocexemps = nullptr;
1553 NonOverlapConstraints *noc = nullptr;
1554 if (preventOverlaps) {
1555 nocexemps = new NonOverlapConstraintExemptions();
1556 noc = new NonOverlapConstraints(nocexemps);
1557 for (size_t i = 0; i < n; ++i) {
1558 noc->addShape(i, rs[i]->width()/2.0, rs[i]->height()/2.0);
1559 }
1560 ccs.push_back(noc);
1561 }
1562 // Set up vars and constraints.
1563 Variables vs;
1564 Constraints cs;
1565 vs.resize(n);
1566 for (size_t i = 0; i < n; ++i) {
1567 vs[i] = new Variable(i, rs[i]->getCentreD(dim));
1568 }
1569 for (CompoundConstraints::iterator it=ccs.begin(); it!=ccs.end(); ++it) {
1570 CompoundConstraint *cc = *it;
1571 cc->generateVariables(dim, vs);
1572 cc->generateSeparationConstraints(dim, vs, cs, rs);
1573 }
1574 // Solve, if possible.
1575 ProjectionResult result = solve(vs, cs, rs, debugLevel);
1576 // If good enough, accept positions.
1577 if (result.errorLevel <= accept) {
1578 for (size_t i = 0; i < n; ++i) {
1579 rs[i]->moveCentreD(dim, vs[i]->finalPosition);
1580 }
1581 }
1582 // Clean up
1583 for (Variables::iterator it=vs.begin(); it!=vs.end(); ++it) delete *it;
1584 for (Constraints::iterator it=cs.begin(); it!=cs.end(); ++it) delete *it;
1585 delete noc;
1586 delete nocexemps;
1587 // Return
1588 return result;
1589}
1590
1591ProjectionResult solve(Variables &vs, Constraints &cs, Rectangles &rs, unsigned debugLevel)
1592{
1593 int result = 0;
1594 IncSolver solv(vs,cs);
1595 try {
1596 solv.solve();
1597 } catch (vpsc::UnsatisfiedConstraint uc) {
1598 }
1599 for (Constraints::iterator it=cs.begin(); it!=cs.end(); it++) {
1600 Constraint *c = *it;
1601 if (c->unsatisfiable) {
1602 CompoundConstraint *cc = (CompoundConstraint*)(c->creator);
1603 if (cc->toString() == "NonOverlapConstraints()") {
1604 result = 1;
1605 } else {
1606 result = 2;
1607 break;
1608 }
1609 }
1610 }
1611 std::string unsatinfo;
1612 if (debugLevel>0) {
1613 std::set<Variable*> varsInvolved;
1614 unsatinfo += "===================================================\n";
1615 unsatinfo += "UNSATISFIED CONSTRAINTS:\n";
1616 char buf [1000];
1617 for (Constraints::iterator it=cs.begin(); it!=cs.end(); it++) {
1618 Constraint *c = *it;
1619 if (c->unsatisfiable) {
1620 varsInvolved.insert(c->left);
1621 varsInvolved.insert(c->right);
1622 sprintf(buf, "v_%d + %f", c->left->id, c->gap);
1623 unsatinfo += buf;
1624 unsatinfo += c->equality ? " == " : " <= ";
1625 sprintf(buf, "v_%d\n", c->right->id);
1626 unsatinfo += buf;
1627 if ((unsigned) c->left->id < rs.size()) {
1628 Rectangle *r = rs[c->left->id];
1629 sprintf(buf, " v_%d rect: [%f, %f] x [%f, %f]\n", c->left->id,
1630 r->getMinX(), r->getMaxX(), r->getMinY(), r->getMaxY());
1631 unsatinfo += buf;
1632 }
1633 if ((unsigned) c->right->id < rs.size()) {
1634 Rectangle *r = rs[c->right->id];
1635 sprintf(buf, " v_%d rect: [%f, %f] x [%f, %f]\n", c->right->id,
1636 r->getMinX(), r->getMaxX(), r->getMinY(), r->getMaxY());
1637 unsatinfo += buf;
1638 }
1639 CompoundConstraint *cc = (CompoundConstraint*)(c->creator);
1640 unsatinfo += " Creator: " + cc->toString() + "\n";
1641 }
1642 }
1643 if (debugLevel>1) {
1644 unsatinfo += "--------------------------------------------------\n";
1645 unsatinfo += "RELATED CONSTRAINTS:\n";
1646 std::set<Variable*>::iterator lit, rit, eit = varsInvolved.end();
1647 for (Constraints::iterator it=cs.begin(); it!=cs.end(); it++) {
1648 Constraint *c = *it;
1649 lit = varsInvolved.find(c->left);
1650 rit = varsInvolved.find(c->right);
1651 if (lit != eit || rit != eit) {
1652 sprintf(buf, "v_%d + %f", c->left->id, c->gap);
1653 unsatinfo += buf;
1654 unsatinfo += c->equality ? " == " : " <= ";
1655 sprintf(buf, "v_%d\n", c->right->id);
1656 unsatinfo += buf;
1657 if ((unsigned) c->left->id < rs.size()) {
1658 Rectangle *r = rs[c->left->id];
1659 sprintf(buf, " v_%d rect: [%f, %f] x [%f, %f]\n", c->left->id,
1660 r->getMinX(), r->getMaxX(), r->getMinY(), r->getMaxY());
1661 unsatinfo += buf;
1662 }
1663 if ((unsigned) c->right->id < rs.size()) {
1664 Rectangle *r = rs[c->right->id];
1665 sprintf(buf, " v_%d rect: [%f, %f] x [%f, %f]\n", c->right->id,
1666 r->getMinX(), r->getMaxX(), r->getMinY(), r->getMaxY());
1667 unsatinfo += buf;
1668 }
1669 CompoundConstraint *cc = (CompoundConstraint*)(c->creator);
1670 unsatinfo += " Creator: " + cc->toString() + "\n";
1671 }
1672 }
1673 }
1674 }
1676 pr.errorLevel = result;
1677 pr.unsatinfo = unsatinfo;
1678 return pr;
1679}
1680
1681} // namespace cola
void generate()
void setLabels(std::vector< std::string > ls)
Definition output_svg.h:55
A cluster defines a hierarchical partitioning over the nodes which should be kept disjoint by the lay...
Definition cluster.h:51
std::set< unsigned > m_nodes_replaced_with_clusters
Definition cluster.h:137
std::map< unsigned, Cluster * > m_overlap_replacement_map
Definition cluster.h:136
std::vector< Cluster * > clusters
Definition cluster.h:116
virtual void countContainedNodes(std::vector< unsigned > &counts)
Definition cluster.cpp:69
vpsc::Rectangle bounds
Definition cluster.h:105
std::set< ShapePair > m_cluster_cluster_overlap_exceptions
Definition cluster.h:135
virtual void computeBoundingRect(const vpsc::Rectangles &rs)
Definition cluster.cpp:99
std::set< unsigned > nodes
Definition cluster.h:115
unsigned clusterVarId
Definition cluster.h:111
virtual void computeVarRect(vpsc::Variables &vs, size_t dim)
Definition cluster.cpp:119
void createVars(const vpsc::Dim dim, const vpsc::Rectangles &rs, vpsc::Variables &vars)
Definition cluster.cpp:647
An abstract base class for all high-level compound constraints.
unsigned int priority(void) const
virtual void generateSeparationConstraints(const vpsc::Dim dim, vpsc::Variables &var, vpsc::Constraints &cs, vpsc::Rectangles &bbs)=0
Implemented by the compound constraint to generate the low-level separation constraints in the given ...
virtual bool subConstraintsRemaining(void) const
virtual void generateVariables(const vpsc::Dim dim, vpsc::Variables &vars)=0
Implemented by the compound constraint to generate any additional required variables in the given dim...
virtual SubConstraintAlternatives getCurrSubConstraintAlternatives(vpsc::Variables vs[])=0
virtual std::string toString(void) const =0
Returns a textual description of the compound constraint.
virtual void markAllSubConstraintsAsInactive(void)
virtual void markCurrSubConstraintAsActive(const bool satisfiable)
bool shouldCombineSubConstraints(void) const
RootCluster * clusterHierarchy
Definition cola.h:912
void run(bool x=true, bool y=true)
Implements the main layout loop, taking descent steps until stress is no-longer significantly reduced...
Definition colafd.cpp:316
ConstrainedFDLayout(const vpsc::Rectangles &rs, const std::vector< cola::Edge > &es, const double idealLength, const EdgeLengths &eLengths=StandardEdgeLengths, TestConvergence *doneTest=nullptr, PreIteration *preIteration=nullptr)
Constructs a constrained force-directed layout instance.
Definition colafd.cpp:96
void computeDescentVectorOnBothAxes(const bool xaxis, const bool yaxis, double stress, std::valarray< double > &x0, std::valarray< double > &x1)
Definition colafd.cpp:297
void freeAssociatedObjects(void)
A convenience method that can be called from Java to free the memory of nodes (Rectangles),...
Definition colafd.cpp:916
DesiredPositions * desiredPositions
Definition cola.h:909
bool m_generateNonOverlapConstraints
Definition cola.h:915
void computeNeighbours(std::vector< Edge > es)
Definition colafd.cpp:171
void setConstraints(const cola::CompoundConstraints &ccs)
Specify a set of compound constraints to apply to the layout.
Definition colafd.cpp:189
std::vector< std::vector< unsigned > > neighbours
Definition cola.h:895
void computePathLengths(const std::vector< Edge > &es, std::valarray< double > eLengths)
Definition colafd.cpp:227
TestConvergence * done
Definition cola.h:897
std::vector< double > offsetDir(double minD)
Definition colafd.cpp:1199
double applyDescentVector(const std::valarray< double > &d, const std::valarray< double > &oldCoords, std::valarray< double > &coords, const double oldStress, double stepsize)
Definition colafd.cpp:1172
void setPosition(std::valarray< double > &pos)
Definition colafd.cpp:286
TopologyAddonInterface * topologyAddon
Definition cola.h:906
double applyForcesAndConstraints(const vpsc::Dim dim, const double oldStress)
Definition colafd.cpp:1105
vpsc::Rectangles boundingBoxes
Definition cola.h:864
TopologyAddonInterface * getTopology(void)
Definition colafd.cpp:951
std::vector< unsigned > readLinearG(void)
Retrieve a copy of the "G matrix" computed by the computePathLengths method, linearised as a vector.
Definition colafd.cpp:159
double computeStepSize(const SparseMatrix &H, const std::valarray< double > &g, const std::valarray< double > &d) const
Definition colafd.cpp:1292
void setTopology(TopologyAddonInterface *topology)
Set an addon for doing topology preserving layout.
Definition colafd.cpp:944
NonOverlapConstraintExemptions * m_nonoverlap_exemptions
Definition cola.h:919
void setDesiredPositions(DesiredPositions *desiredPositions)
Definition colafd.cpp:206
std::valarray< double > Y
Definition cola.h:863
double computeStress() const
Definition colafd.cpp:1314
std::valarray< double > X
Definition cola.h:863
void computeForces(const vpsc::Dim dim, SparseMap &H, std::valarray< double > &g)
Definition colafd.cpp:1225
void handleResizes(const Resizes &)
Definition colafd.cpp:1052
std::vector< double > readLinearD(void)
Retrieve a copy of the "D matrix" computed by the computePathLengths method, linearised as a vector.
Definition colafd.cpp:147
std::vector< UnsatisfiableConstraintInfos * > unsatisfiable
Definition cola.h:907
const std::valarray< double > m_edge_lengths
Definition cola.h:917
void moveTo(const vpsc::Dim dim, std::valarray< double > &target)
Definition colafd.cpp:1063
PseudoRandom random
Definition cola.h:904
void makeFeasible(double xBorder=1, double yBorder=1)
Finds a feasible starting position for nodes that satisfies the given constraints.
Definition colafd.cpp:604
void setAvoidNodeOverlaps(bool avoidOverlaps, ListOfNodeIndexes listOfNodeGroups=ListOfNodeIndexes())
Specifies whether non-overlap constraints should be automatically generated between all nodes,...
Definition colafd.cpp:194
cola::CompoundConstraints ccs
Definition cola.h:900
void generateNonOverlapAndClusterCompoundConstraints(vpsc::Variables(&vs)[2])
Definition colafd.cpp:535
void recGenerateClusterVariablesAndConstraints(vpsc::Variables(&vars)[2], unsigned int &priority, cola::NonOverlapConstraints *noc, Cluster *cluster, cola::CompoundConstraints &idleConstraints)
Definition colafd.cpp:416
void outputInstanceToSVG(std::string filename=std::string())
Generates an SVG file containing debug output and code that can be used to regenerate the instance.
Definition colafd.cpp:1369
cola::CompoundConstraints extraConstraints
Definition cola.h:910
PreIteration * preIteration
Definition cola.h:899
unsigned short ** G
Definition cola.h:902
void runOnce(bool x=true, bool y=true)
Same as run(), but only applies one iteration.
Definition colafd.cpp:386
void setUseNeighbourStress(bool useNeighbourStress)
Specifies whether neighbour stress should be used.
Definition colafd.cpp:201
static TLogLevel & ReportingLevel()
Definition cola_log.h:82
void addExemptGroupOfNodes(ListOfNodeIndexes listOfNodeGroups)
void addShape(unsigned id, double halfW, double halfH, unsigned int group=1, std::set< unsigned > exemptions=std::set< unsigned >())
Use this method to add all the shapes between which you want to prevent overlaps.
void setClusterClusterExemptions(std::set< ShapePair > exemptions)
void addCluster(Cluster *cluster, unsigned int group)
A default functor that is called before each iteration in the main loop of the ConstrainedFDLayout::r...
Definition cola.h:168
static Resizes __resizesNotUsed
Definition cola.h:202
Resizes & resizes
Definition cola.h:198
Locks & locks
Definition cola.h:197
static Locks __locksNotUsed
Definition cola.h:201
double getNextBetween(double min, double max)
Defines a rectangular cluster, either variable-sized with floating sides or a fixed size based on a p...
Definition cluster.h:219
virtual bool clusterIsFromFixedRectangle(void) const
Definition cluster.cpp:431
void generateFixedRectangleConstraints(cola::CompoundConstraints &idleConstraints, vpsc::Rectangles &rc, vpsc::Variables(&vars)[2]) const
Definition cluster.cpp:300
int rectangleIndex(void) const
Definition cluster.cpp:426
Holds the cluster hierarchy specification for a diagram.
Definition cluster.h:173
bool flat(void) const
Definition cluster.h:179
virtual void outputToSVG(FILE *fp) const
Definition cluster.cpp:607
virtual void printCreationCode(FILE *fp) const
Definition cluster.cpp:588
void calculateClusterPathsToEachNode(size_t nodesCount)
Definition cluster.cpp:491
bool allowsMultipleParents(void) const
Returns true if this cluster hierarchy allows multiple parents, otherwise returns false.
Definition cluster.cpp:618
unsigned rowSize() const
void rightMultiply(std::valarray< double > const &v, std::valarray< double > &r) const
A default functor that is called after each iteration of the layout algorithm.
Definition cola.h:216
Interface for writing COLA addons to handle topology preserving layout.
Definition cola.h:532
virtual double applyForcesAndConstraints(ConstrainedFDLayout *layout, const vpsc::Dim dim, std::valarray< double > &g, vpsc::Variables &vs, vpsc::Constraints &cs, std::valarray< double > &coords, DesiredPositionsInDim &des, double oldStress)
Definition cola.h:595
virtual bool useTopologySolver(void) const
Definition cola.h:573
virtual void freeAssociatedObjects(void)
Definition cola.h:547
virtual double computeStress(void) const
Definition cola.h:569
virtual TopologyAddonInterface * clone(void) const
Definition cola.h:542
virtual void makeFeasible(bool generateNonOverlapConstraints, vpsc::Rectangles &boundingBoxes, cola::RootCluster *clusterHierarchy)
Definition cola.h:577
virtual void computePathLengths(unsigned short **G)
Definition cola.h:565
virtual void moveTo(const vpsc::Dim dim, vpsc::Variables &vs, vpsc::Constraints &cs, std::valarray< double > &coords, cola::RootCluster *clusterHierarchy)
Definition cola.h:585
virtual void handleResizes(const Resizes &resizeList, unsigned n, std::valarray< double > &X, std::valarray< double > &Y, cola::CompoundConstraints &ccs, vpsc::Rectangles &boundingBoxes, cola::RootCluster *clusterHierarchy)
Definition cola.h:551
Info about constraints that could not be satisfied in gradient projection process.
A constraint determines a minimum or exact spacing required between two Variable objects.
Definition constraint.h:45
Incremental solver for Variable Placement with Separation Constraints problem instance.
Definition solve_VPSC.h:105
void addConstraint(Constraint *constraint)
Adds a constraint to the existing VPSC solver.
bool solve()
Results in an optimum solution subject to the constraints.
bool satisfy()
Results in an approximate solution subject to the constraints.
A rectangle represents a fixed-size shape in the diagram that may be moved to prevent overlaps and sa...
Definition rectangle.h:78
static void setXBorder(double x)
Definition rectangle.h:237
double getMaxX() const
Definition rectangle.h:108
double getMaxY() const
Definition rectangle.h:109
double getMinY() const
Definition rectangle.h:111
static void setYBorder(double y)
Definition rectangle.h:238
double getMinX() const
Definition rectangle.h:110
A variable is comprised of an ideal position, final position and a weight.
Definition variable.h:45
vector< Edge > es
vector< vpsc::Rectangle * > rs
RectangularCluster rd
RectangularCluster rc
unsigned iteration
Css & result
Geom::IntPoint size
double c[8][4]
Geom::Point end
libcola: Force-directed network layout subject to separation constraints library.
Definition box.cpp:25
double dotProd(valarray< double > x, valarray< double > y)
Definition colafd.cpp:73
ProjectionResult solve(vpsc::Variables &vs, vpsc::Constraints &cs, vpsc::Rectangles &rs, unsigned debugLevel=0)
Constructs a solver and attempts to solve the passed constraints on the passed vars.
std::vector< cola::Lock > Locks
A vector of Lock objects.
Definition cola.h:102
std::vector< CompoundConstraint * > CompoundConstraints
A vector of pointers to CompoundConstraint objects.
static const unsigned int PRIORITY_NONOVERLAP
void setVariableDesiredPositions(vpsc::Variables &vs, vpsc::Constraints &cs, const DesiredPositionsInDim &des, std::valarray< double > &coords)
Definition colafd.cpp:1022
void updateCompoundConstraints(const vpsc::Dim dim, const CompoundConstraints &ccs)
Definition colafd.cpp:1005
static const double freeWeight
static void setupExtraConstraints(const CompoundConstraints &ccs, const vpsc::Dim dim, vpsc::Variables &vs, vpsc::Constraints &cs, vpsc::Rectangles &boundingBoxes)
Definition colafd.cpp:989
std::vector< cola::Resize > Resizes
A vector of Resize objects.
Definition cola.h:135
void setupVarsAndConstraints(unsigned n, const CompoundConstraints &ccs, const vpsc::Dim dim, vpsc::Rectangles &boundingBoxes, RootCluster *clusterHierarchy, vpsc::Variables &vs, vpsc::Constraints &cs, std::valarray< double > &coords)
Definition colafd.cpp:957
void checkUnsatisfiable(const vpsc::Constraints &cs, UnsatisfiableConstraintInfos *unsatisfiable)
Definition colafd.cpp:1042
static const double LIMIT
Definition colafd.cpp:1361
static void reduceRange(double &val)
Definition colafd.cpp:1363
void project(vpsc::Variables &vs, vpsc::Constraints &cs, valarray< double > &coords)
Definition colafd.cpp:1014
std::vector< UnsatisfiableConstraintInfo * > UnsatisfiableConstraintInfos
A vector of pointers to UnsatisfiableConstraintInfo objects.
std::vector< cola::DesiredPosition > DesiredPositions
Definition cola.h:147
void generateVariables(CompoundConstraints &ccs, const vpsc::Dim dim, vpsc::Variables &vars)
Generate just all the variables for a collection of CompoundConstraints.
static bool cmpCompoundConstraintPriority(const cola::CompoundConstraint *lhs, const cola::CompoundConstraint *rhs)
Definition colafd.cpp:409
void dumpSquareMatrix(unsigned n, T **L)
Definition colafd.cpp:82
void delete_vector(vector< T * > &v)
Definition colafd.cpp:67
std::list< SubConstraint > SubConstraintAlternatives
void dijkstra(const unsigned s, const unsigned n, double *d, const std::vector< cola::Edge > &es, const std::valarray< double > &eLengths)
valarray< double > Position
Definition colafd.cpp:271
@ logERROR
Definition cola_log.h:40
@ logDEBUG1
Definition cola_log.h:40
@ logDEBUG2
Definition cola_log.h:40
@ logDEBUG
Definition cola_log.h:40
std::pair< unsigned, unsigned > Edge
Edges are simply a pair of indices to entries in the Node vector.
Definition cola.h:68
ProjectionResult projectOntoCCs(vpsc::Dim dim, vpsc::Rectangles &rs, cola::CompoundConstraints ccs, bool preventOverlaps, int accept=0, unsigned debugLevel=0)
Attempt to do a projection onto a vector of cola CompoundConstraints.
void getPosition(Position &X, Position &Y, Position &pos)
Definition colafd.cpp:272
std::vector< double > EdgeLengths
EdgeLengths is a vector of ideal lengths for edges corresponding to edges in the edge list.
Definition cola.h:72
std::vector< DesiredPositionInDim > DesiredPositionsInDim
Definition cola.h:155
void dijkstra(unsigned const s, unsigned const n, T *d, std::vector< Edge > const &es, std::valarray< T > const &eweights=std::valarray< T >())
find shortest path lengths from node s to all other nodes
void johnsons(unsigned const n, T **D, std::vector< Edge > const &es, std::valarray< T > const &eweights=std::valarray< T >())
find all pairs shortest paths, faster, uses dijkstra
STL namespace.
Dim
Indicates the x- or y-dimension.
Definition rectangle.h:41
@ HORIZONTAL
The x-dimension (0).
Definition rectangle.h:43
@ XDIM
The x-dimension (0).
Definition rectangle.h:45
@ VERTICAL
The y-dimension (1).
Definition rectangle.h:47
@ YDIM
The y-dimension (1).
Definition rectangle.h:49
std::vector< vpsc::Variable * > Variables
A vector of pointers to Variable objects.
std::vector< vpsc::Constraint * > Constraints
A vector of pointers to Constraint objects.
std::vector< Rectangle * > Rectangles
A vector of pointers to Rectangle objects.
Definition rectangle.h:246
int buf
size_t N
static const Point data[]
std::string unsatinfo
Definition cola.h:927
double height
double width