Graphviz 15.1.1~dev.20260630.1303
Loading...
Searching...
No Matches
quad_prog_vpsc.c
Go to the documentation of this file.
1
17/**********************************************************
18 *
19 * Solve a quadratic function f(X) = X' e->A X + b X
20 * subject to a set of separation constraints e->cs
21 *
22 * Tim Dwyer, 2006
23 **********************************************************/
24
25#include "config.h"
26
27#include <common/geomprocs.h>
28#include <limits.h>
29#include <neatogen/digcola.h>
30#include <stdbool.h>
31#include <util/alloc.h>
32#include <util/gv_math.h>
33#include <util/prisize_t.h>
34#ifdef IPSEPCOLA
35#include <math.h>
36#include <stdlib.h>
37#include <time.h>
38#include <stdio.h>
39#include <float.h>
40#include <assert.h>
41#include <neatogen/matrix_ops.h>
42#include <neatogen/kkutils.h>
43#include <vpsc/csolve_VPSC.h>
46
47/* #define CONMAJ_LOGGING 1 */
48#define quad_prog_tol 1e-4
49
50/*
51 * Use gradient-projection to solve Variable Placement with Separation Constraints problem.
52 */
53int
54constrained_majorization_vpsc(CMajEnvVPSC * e, float *b, float *place,
55 int max_iterations)
56{
57 int counter;
58 float *g, *old_place, *d;
59 /* for laplacian computation need number of real vars and those
60 * dummy vars included in lap
61 */
62 const size_t n = e->nv + e->nldv;
63 bool converged = false;
64#ifdef CONMAJ_LOGGING
65 static int call_no = 0;
66#endif /* CONMAJ_LOGGING */
67
68 if (max_iterations == 0)
69 return 0;
70 g = e->fArray1;
71 old_place = e->fArray2;
72 d = e->fArray3;
73 if (e->m > 0) {
74 for (size_t i = 0; i < n; i++) {
75 setVariableDesiredPos(e->vs[i], place[i]);
76 }
77 satisfyVPSC(e->vpsc);
78 for (size_t i = 0; i < n; i++) {
79 place[i] = getVariablePos(e->vs[i]);
80 }
81 }
82#ifdef CONMAJ_LOGGING
83 float prev_stress = 0;
84 for (size_t i = 0; i < n; i++) {
85 prev_stress += 2 * b[i] * place[i];
86 for (size_t j = 0; j < n; j++) {
87 prev_stress -= e->A[i][j] * place[j] * place[i];
88 }
89 }
90 FILE *logfile = fopen("constrained_majorization_log", "a");
91#endif
92
93 for (counter = 0; counter < max_iterations && !converged; counter++) {
94 float test = 0;
95 float alpha, beta;
96 float numerator = 0, denominator = 0, r;
97 converged = true;
98 /* find steepest descent direction */
99 for (size_t i = 0; i < n; i++) {
100 old_place[i] = place[i];
101 g[i] = 2 * b[i];
102 for (size_t j = 0; j < n; j++) {
103 g[i] -= 2 * e->A[i][j] * place[j];
104 }
105 }
106 for (size_t i = 0; i < n; i++) {
107 numerator += g[i] * g[i];
108 r = 0;
109 for (size_t j = 0; j < n; j++) {
110 r += 2 * e->A[i][j] * g[j];
111 }
112 denominator -= r * g[i];
113 }
114 if (denominator != 0)
115 alpha = numerator / denominator;
116 else
117 alpha = 1.0;
118 for (size_t i = 0; i < n; i++) {
119 place[i] -= alpha * g[i];
120 }
121 if (e->m > 0) {
122 /* project to constraint boundary */
123 for (size_t i = 0; i < n; i++) {
124 setVariableDesiredPos(e->vs[i], place[i]);
125 }
126 satisfyVPSC(e->vpsc);
127 for (size_t i = 0; i < n; i++) {
128 place[i] = getVariablePos(e->vs[i]);
129 }
130 }
131 /* set place to the intersection of old_place-g and boundary and
132 * compute d, the vector from intersection pnt to projection pnt
133 */
134 for (size_t i = 0; i < n; i++) {
135 d[i] = place[i] - old_place[i];
136 }
137 /* now compute beta */
138 numerator = 0, denominator = 0;
139 for (size_t i = 0; i < n; i++) {
140 numerator += g[i] * d[i];
141 r = 0;
142 for (size_t j = 0; j < n; j++) {
143 r += 2 * e->A[i][j] * d[j];
144 }
145 denominator += r * d[i];
146 }
147 if (!is_exactly_zero(denominator) && !is_exactly_equal(denominator, -0.0))
148 beta = numerator / denominator;
149 else
150 beta = 1.0;
151
152 for (size_t i = 0; i < n; i++) {
153 /* beta > 1.0 takes us back outside the feasible region
154 * beta < 0 clearly not useful and may happen due to numerical imp.
155 */
156 if (beta > 0 && beta < 1.0) {
157 place[i] = old_place[i] + beta * d[i];
158 }
159 test += fabsf(place[i] - old_place[i]);
160 }
161#ifdef CONMAJ_LOGGING
162 float stress = 0;
163 for (size_t i = 0; i < n; i++) {
164 stress += 2 * b[i] * place[i];
165 for (size_t j = 0; j < n; j++) {
166 stress -= e->A[i][j] * place[j] * place[i];
167 }
168 }
169 fprintf(logfile, "%d: stress=%f, test=%f, %s\n", call_no, stress,
170 test, (stress >= prev_stress) ? "No Improvement" : "");
171 prev_stress = stress;
172#endif
173 if (test > quad_prog_tol) {
174 converged = false;
175 }
176 }
177#ifdef CONMAJ_LOGGING
178 call_no++;
179 fclose(logfile);
180#endif
181 return counter;
182}
183
184static DigColaLevel *assign_digcola_levels(const int *ordering, size_t n,
185 int *level_inds,
186 size_t num_divisions);
187
188/*
189 * Set up environment and global constraints (dir-edge constraints, containment constraints
190 * etc).
191 *
192 * diredges: 0=no dir edge constraints
193 * 1=one separation constraint for each edge (in acyclic subgraph)
194 * 2=DiG-CoLa level constraints
195 */
196CMajEnvVPSC *initCMajVPSC(int n, float *packedMat, vtx_data * graph,
197 ipsep_options * opt, int diredges)
198{
199 /* nv is the number of real nodes */
200 CMajEnvVPSC *e = gv_alloc(sizeof(CMajEnvVPSC));
201 e->A = NULL;
202 /* if we have clusters then we'll need two constraints for each var in
203 * a cluster */
204 e->nldv = 2 * opt->clusters.nclusters;
205 e->nv = n - e->nldv;
206 e->ndv = 0;
207
208 e->gcs = NULL;
209 e->vs = gv_calloc(n, sizeof(Variable*));
210 for (int i = 0; i < n; i++) {
211 e->vs[i] = newVariable(i, 1.0, 1.0);
212 }
213 e->gm = 0;
214 if (diredges == 1) {
215 if (Verbose)
216 fprintf(stderr, " generate edge constraints...\n");
217 for (size_t i = 0; i < e->nv; i++) {
218 for (size_t j = 1; j < graph[i].nedges; j++) {
219 if (graph[i].edists[j] > 0) {
220 e->gm++;
221 }
222 }
223 }
224 e->gcs = newConstraints(e->gm);
225 e->gm = 0;
226 for (size_t i = 0; i < e->nv; i++) {
227 for (size_t j = 1; j < graph[i].nedges; j++) {
228 const size_t u = i;
229 int v = graph[i].edges[j];
230 if (graph[i].edists[j] > 0) {
231 e->gcs[e->gm++] =
232 newConstraint(e->vs[u], e->vs[v], opt->edge_gap);
233 }
234 }
235 }
236 } else if (diredges == 2) {
237 int *ordering = NULL, *ls = NULL, cvar;
238 double halfgap;
239 DigColaLevel *levels;
240 Variable **vs = e->vs;
241 /* e->ndv is the number of dummy variables required, one for each boundary */
242 if (compute_hierarchy(graph, e->nv, 1e-2, 1e-1, NULL, &ordering, &ls,
243 &e->ndv)) return NULL;
244 levels = assign_digcola_levels(ordering, e->nv, ls, e->ndv);
245 free(ordering);
246 if (Verbose)
247 fprintf(stderr, "Found %" PRISIZE_T " DiG-CoLa boundaries\n", e->ndv);
248 e->gm =
249 get_num_digcola_constraints(levels, e->ndv + 1) + e->ndv - 1;
250 e->gcs = newConstraints(e->gm);
251 e->gm = 0;
252 e->vs = gv_calloc(n + e->ndv, sizeof(Variable*));
253 for (int i = 0; i < n; i++) {
254 e->vs[i] = vs[i];
255 }
256 free(vs);
257 /* create dummy vars */
258 for (size_t i = 0; i < e->ndv; i++) {
259 /* dummy vars should have 0 weight */
260 cvar = n + i;
261 e->vs[cvar] = newVariable(cvar, 1.0, 0.000001);
262 }
263 halfgap = opt->edge_gap;
264 for (size_t i = 0; i < e->ndv; i++) {
265 cvar = n + i;
266 /* outgoing constraints for each var in level below boundary */
267 for (int j = 0; j < levels[i].num_nodes; j++) {
268 e->gcs[e->gm++] =
269 newConstraint(e->vs[levels[i].nodes[j]], e->vs[cvar],
270 halfgap);
271 }
272 /* incoming constraints for each var in level above boundary */
273 for (int j = 0; j < levels[i + 1].num_nodes; j++) {
274 e->gcs[e->gm++] =
275 newConstraint(e->vs[cvar],
276 e->vs[levels[i + 1].nodes[j]], halfgap);
277 }
278 }
279 /* constraints between adjacent boundary dummy vars */
280 for (size_t i = 0; i < e->ndv - 1; i++) {
281 e->gcs[e->gm++] =
282 newConstraint(e->vs[n + i], e->vs[n + i + 1], 0);
283 }
284 }
285 if (opt->clusters.nclusters > 0) {
286 Constraint **ecs = e->gcs;
287 const size_t nConCs = 2 * opt->clusters.nvars;
288 e->gcs = newConstraints(e->gm + nConCs);
289 for (size_t i = 0; i < e->gm; i++) {
290 e->gcs[i] = ecs[i];
291 }
292 if (ecs != NULL)
293 deleteConstraints(0, ecs);
294 for (size_t i = 0; i < opt->clusters.nclusters; i++) {
295 for (size_t j = 0; j < opt->clusters.clustersizes[i]; j++) {
296 Variable *v = e->vs[opt->clusters.clusters[i][j]];
297 Variable *cl = e->vs[e->nv + 2 * i];
298 Variable *cr = e->vs[e->nv + 2 * i + 1];
299 e->gcs[e->gm++] = newConstraint(cl, v, 0);
300 e->gcs[e->gm++] = newConstraint(v, cr, 0);
301 }
302 }
303 }
304
305 e->m = 0;
306 e->cs = NULL;
307 if (e->gm > 0) {
308 e->vpsc = newIncVPSC(n + e->ndv, e->vs, e->gm, e->gcs);
309 e->m = e->gm;
310 e->cs = e->gcs;
311 }
312 if (packedMat != NULL) {
313 e->A = unpackMatrix(packedMat, n);
314 }
315
316 e->fArray1 = gv_calloc(n, sizeof(float));
317 e->fArray2 = gv_calloc(n, sizeof(float));
318 e->fArray3 = gv_calloc(n, sizeof(float));
319 if (Verbose)
320 fprintf(stderr,
321 " initCMajVPSC done: %" PRISIZE_T " global constraints generated.\n",
322 e->m);
323 return e;
324}
325
326void deleteCMajEnvVPSC(CMajEnvVPSC * e)
327{
328 if (e->A != NULL) {
329 free(e->A[0]);
330 free(e->A);
331 }
332 if (e->m > 0) {
333 deleteVPSC(e->vpsc);
334 if (e->cs != e->gcs && e->gcs != NULL)
335 deleteConstraints(0, e->gcs);
336 deleteConstraints(e->m, e->cs);
337 for (size_t i = 0; i < e->nv + e->nldv + e->ndv; i++) {
338 deleteVariable(e->vs[i]);
339 }
340 free(e->vs);
341 }
342 free(e->fArray1);
343 free(e->fArray2);
344 free(e->fArray3);
345 free(e);
346}
347
348/* generate non-overlap constraints inside each cluster, including dummy
349 * nodes at bounds of cluster
350 * generate constraints again for top level nodes and clusters treating
351 * clusters as rectangles of dim (l,r,b,t)
352 * for each cluster map in-constraints to l out-constraints to r
353 *
354 * For now, we'll keep the global containment constraints already
355 * generated for each cluster, and simply generate non-overlap constraints
356 * for all nodes and then an additional set of non-overlap constraints for
357 * clusters that we'll map back to the dummy vars as above.
358 */
359void generateNonoverlapConstraints(CMajEnvVPSC * e,
360 float nsizeScale,
361 float **coords,
362 int k,
363 bool transitiveClosure,
364 ipsep_options * opt)
365{
366 Constraint **csol, **csolptr;
367 int mol = 0;
368 size_t n = e->nv + e->nldv;
369 boxf* bb = gv_calloc(n, sizeof(boxf));
370 bool genclusters = opt->clusters.nclusters > 0;
371 if (genclusters) {
372 /* n is the number of real variables, not dummy cluster vars */
373 n -= 2 * opt->clusters.nclusters;
374 }
375 if (k == 0) {
376 /* grow a bit in the x dimension, so that if overlap is resolved
377 * horizontally then it won't be considered overlapping vertically
378 */
379 nsizeScale *= 1.0001f;
380 }
381 for (size_t i = 0; i < n; i++) {
382 bb[i].LL.x =
383 coords[0][i] - nsizeScale * opt->nsize[i].x / 2.0 -
384 opt->gap.x / 2.0;
385 bb[i].UR.x =
386 coords[0][i] + nsizeScale * opt->nsize[i].x / 2.0 +
387 opt->gap.x / 2.0;
388 bb[i].LL.y =
389 coords[1][i] - nsizeScale * opt->nsize[i].y / 2.0 -
390 opt->gap.y / 2.0;
391 bb[i].UR.y =
392 coords[1][i] + nsizeScale * opt->nsize[i].y / 2.0 +
393 opt->gap.y / 2.0;
394 }
395 if (genclusters) {
396 Constraint ***cscl = gv_calloc(opt->clusters.nclusters + 1,
397 sizeof(Constraint**));
398 int* cm = gv_calloc(opt->clusters.nclusters + 1, sizeof(int));
399 for (size_t i = 0; i < opt->clusters.nclusters; i++) {
400 const size_t cn = opt->clusters.clustersizes[i];
401 Variable** cvs = gv_calloc(cn + 2, sizeof(Variable*));
402 boxf* cbb = gv_calloc(cn + 2, sizeof(boxf));
403 /* compute cluster bounding bb */
404 boxf container;
405 container.LL.x = container.LL.y = DBL_MAX;
406 container.UR.x = container.UR.y = -DBL_MAX;
407 for (size_t j = 0; j < cn; j++) {
408 int iv = opt->clusters.clusters[i][j];
409 cvs[j] = e->vs[iv];
410 B2BF(bb[iv], cbb[j]);
411 EXPANDBB(&container, bb[iv]);
412 }
413 B2BF(container, opt->clusters.bb[i]);
414 cvs[cn] = e->vs[n + 2 * i];
415 cvs[cn + 1] = e->vs[n + 2 * i + 1];
416 B2BF(container, cbb[cn]);
417 B2BF(container, cbb[cn + 1]);
418 if (k == 0) {
419 cbb[cn].UR.x = container.LL.x + 0.0001;
420 cbb[cn + 1].LL.x = container.UR.x - 0.0001;
421 cm[i] =
422 genXConstraints(cn + 2, cbb, cvs, &cscl[i],
423 transitiveClosure);
424 } else {
425 cbb[cn].UR.y = container.LL.y + 0.0001;
426 cbb[cn + 1].LL.y = container.UR.y - 0.0001;
427 cm[i] = genYConstraints(cn + 2, cbb, cvs, &cscl[i]);
428 }
429 mol += cm[i];
430 free (cvs);
431 free (cbb);
432 }
433 /* generate top level constraints */
434 {
435 const size_t cn = opt->clusters.ntoplevel + opt->clusters.nclusters;
436 Variable** cvs = gv_calloc(cn, sizeof(Variable*));
437 boxf* cbb = gv_calloc(cn, sizeof(boxf));
438 for (size_t i = 0; i < opt->clusters.ntoplevel; i++) {
439 int iv = opt->clusters.toplevel[i];
440 cvs[i] = e->vs[iv];
441 B2BF(bb[iv], cbb[i]);
442 }
443 /* make dummy variables for clusters */
444 for (size_t i = opt->clusters.ntoplevel; i < cn; i++) {
445 assert(i <= INT_MAX);
446 cvs[i] = newVariable(123 + (int)i, 1, 1);
447 const size_t j = i - opt->clusters.ntoplevel;
448 B2BF(opt->clusters.bb[j], cbb[i]);
449 }
450 if (k == 0) {
451 const size_t i = opt->clusters.nclusters;
452 cm[i] =
453 genXConstraints(cn, cbb, cvs, &cscl[i],
454 transitiveClosure);
455 } else {
456 const size_t i = opt->clusters.nclusters;
457 cm[i] = genYConstraints(cn, cbb, cvs, &cscl[i]);
458 }
459 /* remap constraints from tmp dummy vars to cluster l and r vars */
460 for (size_t i = opt->clusters.ntoplevel; i < cn; i++) {
461 double dgap;
462 const size_t j = i - opt->clusters.ntoplevel;
463 /* dgap is the change in required constraint gap.
464 * since we are going from a source rectangle the size
465 * of the cluster bounding box to a zero width (in x dim,
466 * zero height in y dim) rectangle, the change will be
467 * half the bb width.
468 */
469 if (k == 0) {
470 dgap = -(cbb[i].UR.x - cbb[i].LL.x) / 2.0;
471 } else {
472 dgap = -(cbb[i].UR.y - cbb[i].LL.y) / 2.0;
473 }
474 remapInConstraints(cvs[i], e->vs[n + 2 * j], dgap);
475 remapOutConstraints(cvs[i], e->vs[n + 2 * j + 1], dgap);
476 /* there may be problems with cycles between
477 * cluster non-overlap and diredge constraints,
478 * to resolve:
479 *
480 * for each constraint c:v->cvs[i]:
481 * if exists diredge constraint u->v where u in c:
482 * remap v->cl to cr->v (gap = height(v)/2)
483 *
484 * in = getInConstraints(cvs[i])
485 * for(c : in) {
486 * assert(c.right==cvs[i]);
487 * vin = getOutConstraints(v=c.left)
488 * for(d : vin) {
489 * if(d.left.cluster==i):
490 * tmp = d.left
491 * d.left = d.right
492 * d.right = tmp
493 * d.gap = height(d.right)/2
494 * }
495 * }
496 *
497 */
498 deleteVariable(cvs[i]);
499 }
500 mol += cm[opt->clusters.nclusters];
501 free (cvs);
502 free (cbb);
503 }
504 csolptr = csol = newConstraints(mol);
505 for (size_t i = 0; i < opt->clusters.nclusters + 1; i++) {
506 /* copy constraints into csol */
507 for (int j = 0; j < cm[i]; j++) {
508 *csolptr++ = cscl[i][j];
509 }
510 deleteConstraints(0, cscl[i]);
511 }
512 free (cscl);
513 free (cm);
514 } else {
515 if (k == 0) {
516 mol = genXConstraints(n, bb, e->vs, &csol, transitiveClosure);
517 } else {
518 mol = genYConstraints(n, bb, e->vs, &csol);
519 }
520 }
521 /* remove constraints from previous iteration */
522 if (e->m > 0) {
523 /* can't reuse instance of VPSC when constraints change! */
524 deleteVPSC(e->vpsc);
525 for (size_t i = e->gm; i < e->m; i++) {
526 /* delete previous overlap constraints */
527 deleteConstraint(e->cs[i]);
528 }
529 /* just delete the array, not the elements */
530 if (e->cs != e->gcs)
531 deleteConstraints(0, e->cs);
532 }
533 /* if we have no global constraints then the overlap constraints
534 * are all we have to worry about.
535 * Otherwise, we have to copy the global and overlap constraints
536 * into the one array
537 */
538 if (e->gm == 0) {
539 e->m = mol;
540 e->cs = csol;
541 } else {
542 e->m = mol + e->gm;
543 e->cs = newConstraints(e->m);
544 for (size_t i = 0; i < e->m; i++) {
545 if (i < e->gm) {
546 e->cs[i] = e->gcs[i];
547 } else {
548 e->cs[i] = csol[i - e->gm];
549 }
550 }
551 /* just delete the array, not the elements */
552 deleteConstraints(0, csol);
553 }
554 if (Verbose)
555 fprintf(stderr, " generated %" PRISIZE_T " constraints\n", e->m);
556 e->vpsc = newIncVPSC(e->nv + e->nldv + e->ndv, e->vs, e->m, e->cs);
557 free (bb);
558}
559
560/*
561 * Statically remove overlaps, that is remove all overlaps by moving each node as
562 * little as possible.
563 */
564void removeoverlaps(int n, float **coords, ipsep_options * opt)
565{
566 int i;
567 CMajEnvVPSC *e = initCMajVPSC(n, NULL, NULL, opt, 0);
568 generateNonoverlapConstraints(e, 1.0, coords, 0, true, opt);
569 solveVPSC(e->vpsc);
570 for (i = 0; i < n; i++) {
571 coords[0][i] = getVariablePos(e->vs[i]);
572 }
573 generateNonoverlapConstraints(e, 1.0, coords, 1, false, opt);
574 solveVPSC(e->vpsc);
575 for (i = 0; i < n; i++) {
576 coords[1][i] = getVariablePos(e->vs[i]);
577 }
578 deleteCMajEnvVPSC(e);
579}
580
581/*
582 unpack the "ordering" array into an array of DigColaLevel
583*/
584static DigColaLevel *assign_digcola_levels(const int *ordering, size_t n,
585 int *level_inds,
586 size_t num_divisions) {
587 int j;
588 DigColaLevel *l = gv_calloc(num_divisions + 1, sizeof(DigColaLevel));
589 /* first level */
590 l[0].num_nodes = level_inds[0];
591 l[0].nodes = gv_calloc(l[0].num_nodes, sizeof(int));
592 for (int i = 0; i < l[0].num_nodes; i++) {
593 l[0].nodes[i] = ordering[i];
594 }
595 /* second through second last level */
596 for (size_t i = 1; i < num_divisions; i++) {
597 l[i].num_nodes = level_inds[i] - level_inds[i - 1];
598 l[i].nodes = gv_calloc(l[i].num_nodes, sizeof(int));
599 for (j = 0; j < l[i].num_nodes; j++) {
600 l[i].nodes[j] = ordering[level_inds[i - 1] + j];
601 }
602 }
603 /* last level */
604 if (num_divisions > 0) {
605 l[num_divisions].num_nodes = n - level_inds[num_divisions - 1];
606 l[num_divisions].nodes = gv_calloc(l[num_divisions].num_nodes, sizeof(int));
607 for (int i = 0; i < l[num_divisions].num_nodes; i++) {
608 l[num_divisions].nodes[i] =
609 ordering[level_inds[num_divisions - 1] + i];
610 }
611 }
612 return l;
613}
614
615/*********************
616get number of separation constraints based on the number of nodes in each level
617ie, num_sep_constraints = sum_i^{num_levels-1} (|L[i]|+|L[i+1]|)
618**********************/
619int get_num_digcola_constraints(DigColaLevel *levels, size_t num_levels) {
620 int nc = 0;
621 for (size_t i = 1; i < num_levels; i++) {
622 nc += levels[i].num_nodes + levels[i - 1].num_nodes;
623 }
624 nc += levels[0].num_nodes + levels[num_levels - 1].num_nodes;
625 return nc;
626}
627
628#endif /* IPSEPCOLA */
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
void setVariableDesiredPos(Variable *v, double desiredPos)
int genYConstraints(size_t n, boxf *bb, Variable **vs, Constraint ***cs)
Variable * newVariable(int id, double desiredPos, double weight)
Bridge for C programs to access solve_VPSC (which is in C++)
Constraint ** newConstraints(int m)
void deleteConstraint(Constraint *c)
VPSC * newIncVPSC(int n, Variable *vs[], int m, Constraint *cs[])
void deleteVariable(Variable *v)
Constraint * newConstraint(Variable *left, Variable *right, double gap)
void remapInConstraints(Variable *u, Variable *v, double dgap)
void remapOutConstraints(Variable *u, Variable *v, double dgap)
double getVariablePos(const Variable *v)
void deleteConstraints(size_t m, Constraint **cs)
void satisfyVPSC(VPSC *vpsc)
void deleteVPSC(VPSC *vpsc)
void solveVPSC(VPSC *vpsc)
int genXConstraints(size_t n, boxf *bb, Variable **vs, Constraint ***cs, bool transitiveClosure)
#define B2BF(b, bf)
Definition geom.h:69
geometric functions (e.g. on points and boxes)
#define EXPANDBB(b0, b1)
Definition geomprocs.h:65
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
Agraph_t * graph(char *name)
Definition gv.cpp:34
Arithmetic helper functions.
static bool is_exactly_zero(double v)
is a value precisely 0.0?
Definition gv_math.h:70
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:51
#define PRISIZE_T
Definition prisize_t.h:25
#define alpha
Definition shapes.c:4034
A constraint determines a minimum or exact spacing required between two variables.
Definition constraint.h:25
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
double x
Definition geom.h:29
double y
Definition geom.h:29