Graphviz 13.0.0~dev.20250210.0415
Loading...
Searching...
No Matches
multispline.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include <assert.h>
12#include <float.h>
13#include <limits.h>
15#include <neatogen/delaunay.h>
16#include <neatogen/neatoprocs.h>
17#include <math.h>
18#include <stdbool.h>
19#include <stddef.h>
20#include <util/alloc.h>
21#include <util/gv_math.h>
22
23static bool spline_merge(node_t * n)
24{
25 (void)n;
26 return false;
27}
28
29static bool swap_ends_p(edge_t * e)
30{
31 (void)e;
32 return false;
33}
34
36 .splineMerge = spline_merge};
37
38typedef struct {
39 int i, j;
40} ipair;
41
42typedef struct _tri {
44 struct _tri *nxttri;
46
47typedef struct {
48 Ppoly_t poly; /* base polygon used for routing an edge */
49 tri **triMap; /* triMap[j] is list of all opposite sides of
50 triangles containing vertex j, represented
51 as the indices of the two points in poly */
52} tripoly_t;
53
54/*
55 * Support for map from I x I -> I
56 */
57typedef struct {
58 Dtlink_t link; /* cdt data */
59 int a[2]; /* key */
60 int t;
61} item;
62
63static int cmpItem(void *item1, void *item2) {
64 const int *p1 = item1;
65 const int *p2 = item2;
66 if (p1[0] < p2[0]) return -1;
67 if (p1[0] > p2[0]) return 1;
68 if (p1[1] < p2[1]) return -1;
69 if (p1[1] > p2[1]) return 1;
70 return 0;
71}
72
73/* newItem:
74 */
75static void *newItem(void *p, Dtdisc_t *disc) {
76 item *objp = p;
77 item *newp = gv_alloc(sizeof(item));
78
79 (void)disc;
80 newp->a[0] = objp->a[0];
81 newp->a[1] = objp->a[1];
82 newp->t = objp->t;
83
84 return newp;
85}
86
88 .key = offsetof(item, a),
89 .size = 2 * sizeof(int),
90 .link = offsetof(item, link),
91 .makef = newItem,
92 .freef = free,
93 .comparf = cmpItem,
94};
95
96static void addMap(Dt_t * map, int a, int b, int t)
97{
98 item it;
99 if (a > b) {
100 SWAP(&a, &b);
101 }
102 it.a[0] = a;
103 it.a[1] = b;
104 it.t = t;
105 dtinsert(map, &it);
106}
107
108/* mapSegToTri:
109 * Create mapping from indices of side endpoints to triangle id
110 * We use a set rather than a bag because the segments used for lookup
111 * will always be a side of a polygon and hence have a unique triangle.
112 */
114{
115 Dt_t *map = dtopen(&itemdisc, Dtoset);
116 int i, a, b, c;
117 int *ps = sf->faces;
118 for (i = 0; i < sf->nfaces; i++) {
119 a = *ps++;
120 b = *ps++;
121 c = *ps++;
122 addMap(map, a, b, i);
123 addMap(map, b, c, i);
124 addMap(map, c, a, i);
125 }
126 return map;
127}
128
129static int findMap(Dt_t * map, int a, int b)
130{
131 item it;
132 item *ip;
133 if (a > b) {
134 SWAP(&a, &b);
135 }
136 it.a[0] = a;
137 it.a[1] = b;
138 ip = dtsearch(map, &it);
139 assert(ip);
140 return ip->t;
141}
142
143/*
144 * Support for map from I -> I
145 */
146
147typedef struct {
148 Dtlink_t link; /* cdt data */
149 int i; /* key */
150 int j;
151} Ipair;
152
153static int cmpIpair(void *pair1, void *pair2) {
154 const int *p1 = pair1;
155 const int *p2 = pair2;
156 if (*p1 < *p2) {
157 return -1;
158 }
159 if (*p1 > *p2) {
160 return 1;
161 }
162 return 0;
163}
164
165static void *newIpair(void *p, Dtdisc_t *disc) {
166 Ipair *objp = p;
167 Ipair *newp = gv_alloc(sizeof(Ipair));
168
169 (void)disc;
170 newp->i = objp->i;
171 newp->j = objp->j;
172
173 return newp;
174}
175
177 .key = offsetof(Ipair, i),
178 .size = sizeof(int),
179 .link = offsetof(Ipair, link),
180 .makef = newIpair,
181 .freef = free,
182 .comparf = cmpIpair,
183};
184
185static void vmapAdd(Dt_t * map, int i, int j)
186{
187 Ipair obj;
188 obj.i = i;
189 obj.j = j;
190 dtinsert(map, &obj);
191}
192
193static int vMap(Dt_t * map, int i)
194{
195 Ipair *ip;
196 ip = dtmatch(map, &i);
197 return ip->j;
198}
199
200/* mapTri:
201 * Map vertex indices from router_t to tripoly_t coordinates.
202 */
203static void mapTri(Dt_t * map, tri * tp)
204{
205 for (; tp; tp = tp->nxttri) {
206 tp->v.i = vMap(map, tp->v.i);
207 tp->v.j = vMap(map, tp->v.j);
208 }
209}
210
211/* addTri:
212 */
213static tri *
214addTri(int i, int j, tri * oldp)
215{
216 tri *tp = gv_alloc(sizeof(tri));
217 tp->v.i = i;
218 tp->v.j = j;
219 tp->nxttri = oldp;
220 return tp;
221}
222
223/* bisect:
224 * Return the angle bisecting the angle pp--cp--np
225 */
226static double bisect(pointf pp, pointf cp, pointf np)
227{
228 double theta, phi;
229 theta = atan2(np.y - cp.y, np.x - cp.x);
230 phi = atan2(pp.y - cp.y, pp.x - cp.x);
231 return (theta + phi) / 2.0;
232}
233
234/* raySeg:
235 * Check if ray v->w intersects segment a--b.
236 */
237static int raySeg(pointf v, pointf w, pointf a, pointf b)
238{
239 int wa = wind(v, w, a);
240 int wb = wind(v, w, b);
241 if (wa == wb)
242 return 0;
243 if (wa == 0) {
244 return wind(v, b, w) * wind(v, b, a) >= 0;
245 } else {
246 return wind(v, a, w) * wind(v, a, b) >= 0;
247 }
248}
249
250/* raySegIntersect:
251 * Find the point p where ray v->w intersects segment ai-bi, if any.
252 * Return 1 on success, 0 on failure
253 */
254static int
256{
257 if (raySeg(v, w, a, b))
258 return line_intersect(v, w, a, b, p);
259 else
260 return 0;
261}
262
263/* triPoint:
264 * Given the triangle vertex v, and point w so that v->w points
265 * into the polygon, return where the ray v->w intersects the
266 * polygon. The search uses all of the opposite sides of triangles
267 * with v as vertex.
268 * Return 0 on success; 1 on failure.
269 */
270static int
271triPoint(tripoly_t * trip, int vx, pointf v, pointf w, pointf * ip)
272{
273 tri *tp;
274
275 for (tp = trip->triMap[vx]; tp; tp = tp->nxttri) {
277 (v, w, trip->poly.ps[tp->v.i], trip->poly.ps[tp->v.j], ip))
278 return 0;
279 }
280 return 1;
281}
282
283/* ctrlPtIdx:
284 * Find the index of v in the points polys->ps.
285 * We start at 1 since the point corresponding to 0
286 * will never be used as v.
287 */
288static int ctrlPtIdx(pointf v, Ppoly_t * polys)
289{
290 pointf w;
291 for (size_t i = 1; i < polys->pn; i++) {
292 w = polys->ps[i];
293 if (w.x == v.x && w.y == v.y) {
294 assert(i <= INT_MAX);
295 return (int)i;
296 }
297 }
298 return -1;
299}
300
301#define SEP 15
302
303/* mkCtrlPts:
304 * Generate mult points associated with v.
305 * The points will lie on the ray bisecting the angle prev--v--nxt.
306 * The first point will aways be v.
307 * The rest are positioned equally spaced with maximum spacing SEP.
308 * In addition, they all lie within the polygon trip->poly.
309 * Parameter s gives the index after which a vertex lies on the
310 * opposite side. This is necessary to get the "curvature" of the
311 * path correct.
312 */
313static pointf *mkCtrlPts(int s, int mult, pointf prev, pointf v,
314 pointf nxt, tripoly_t * trip)
315{
316 int idx = ctrlPtIdx(v, &trip->poly);
317 int i;
318 double d, sep, theta, sinTheta, cosTheta;
319 pointf q, w;
320
321 if (idx < 0)
322 return NULL;
323
324 pointf *ps = gv_calloc(mult, sizeof(pointf));
325 theta = bisect(prev, v, nxt);
326 sinTheta = sin(theta);
327 cosTheta = cos(theta);
328 w.x = v.x + 100 * cosTheta;
329 w.y = v.y + 100 * sinTheta;
330 if (idx > s) {
331 if (wind(prev, v, w) != 1) {
332 sinTheta *= -1;
333 cosTheta *= -1;
334 w.x = v.x + 100 * cosTheta;
335 w.y = v.y + 100 * sinTheta;
336 }
337 } else if (wind(prev, v, w) != -1) {
338 sinTheta *= -1;
339 cosTheta *= -1;
340 w.x = v.x + 100 * cosTheta;
341 w.y = v.y + 100 * sinTheta;
342 }
343
344 if (triPoint(trip, idx, v, w, &q)) {
345 return 0;
346 }
347
348 d = DIST(q, v);
349 if (d >= mult * SEP)
350 sep = SEP;
351 else
352 sep = d / mult;
353 if (idx < s) {
354 for (i = 0; i < mult; i++) {
355 ps[i].x = v.x + i * sep * cosTheta;
356 ps[i].y = v.y + i * sep * sinTheta;
357 }
358 } else {
359 for (i = 0; i < mult; i++) {
360 ps[mult - i - 1].x = v.x + i * sep * cosTheta;
361 ps[mult - i - 1].y = v.y + i * sep * sinTheta;
362 }
363 }
364 return ps;
365}
366
367/*
368 * Simple graph structure for recording the triangle graph.
369 */
370
371typedef struct {
372 size_t ne; // no. of edges.
373 int *edges; /* indices of edges adjacent to node. */
374 pointf ctr; /* center of triangle. */
375} tnode;
376
377typedef struct {
378 int t, h; /* indices of head and tail nodes */
379 ipair seg; /* indices of points forming shared segment */
380 double dist; /* length of edge; usually distance between centers */
381} tedge;
382
383typedef struct {
385 size_t nnodes; // number of nodes
387 int nedges; // number of edges
388} tgraph;
389
390struct router_s {
391 int pn; /* no. of points */
392 pointf *ps; /* all points in configuration */
393 int *obs; /* indices in obstacle i are obs[i]...obs[i+1]-1 */
394 int *tris; /* indices of triangle i are tris[3*i]...tris[3*i+2] */
395 Dt_t *trimap; /* map from obstacle side (a,b) to index of adj. triangle */
396 int tn; /* no. of nodes in tg */
397 tgraph *tg; /* graph of triangles */
398};
399
400/* triCenter:
401 * Given an array of points and 3 integer indices,
402 * compute and return the center of the triangle.
403 */
404static pointf triCenter(pointf * pts, int *idxs)
405{
406 pointf a = pts[*idxs++];
407 pointf b = pts[*idxs++];
408 pointf c = pts[*idxs++];
409 pointf p;
410 p.x = (a.x + b.x + c.x) / 3.0;
411 p.y = (a.y + b.y + c.y) / 3.0;
412 return p;
413}
414
415#define MARGIN 32
416
417/* bbox:
418 * Compute bounding box of polygons, and return it
419 * with an added margin of MARGIN.
420 * Store total number of points in *np.
421 */
422static boxf bbox(Ppoly_t** obsp, int npoly, int *np)
423{
424 boxf bb;
425 int i, cnt = 0;
426 pointf p;
427 Ppoly_t* obs;
428
429 bb.LL.x = bb.LL.y = DBL_MAX;
430 bb.UR.x = bb.UR.y = -DBL_MAX;
431
432 for (i = 0; i < npoly; i++) {
433 obs = *obsp++;
434 for (size_t j = 0; j < obs->pn; j++) {
435 p = obs->ps[j];
436 bb.LL.x = fmin(bb.LL.x, p.x);
437 bb.UR.x = fmax(bb.UR.x, p.x);
438 bb.LL.y = fmin(bb.LL.y, p.y);
439 bb.UR.y = fmax(bb.UR.y, p.y);
440 cnt++;
441 }
442 }
443
444 *np = cnt;
445
446 bb.LL.x -= MARGIN;
447 bb.LL.y -= MARGIN;
448 bb.UR.x += MARGIN;
449 bb.UR.y += MARGIN;
450
451 return bb;
452}
453
454static int *mkTriIndices(surface_t * sf)
455{
456 int *tris = gv_calloc(3 * sf->nfaces, sizeof(int));
457 memcpy(tris, sf->faces, 3 * sf->nfaces * sizeof(int));
458 return tris;
459}
460
461/* sharedEdge:
462 * Returns a pair of integer (x,y), x < y, where x and y are the
463 * indices of the two vertices of the shared edge.
464 */
465static ipair sharedEdge(int *p, int *q)
466{
467 ipair pt;
468 int p1, p2;
469 p1 = *p;
470 p2 = *(p + 1);
471 if (p1 == *q) {
472 if (p2 != *(q + 1) && p2 != *(q + 2)) {
473 p2 = *(p + 2);
474 }
475 } else if (p1 == *(q + 1)) {
476 if (p2 != *q && p2 != *(q + 2)) {
477 p2 = *(p + 2);
478 }
479 } else if (p1 == *(q + 2)) {
480 if (p2 != *q && p2 != *(q + 1)) {
481 p2 = *(p + 2);
482 }
483 } else {
484 p1 = *(p + 2);
485 }
486
487 if (p1 > p2) {
488 SWAP(&p1, &p2);
489 }
490 pt.i = p1;
491 pt.j = p2;
492 return pt;
493}
494
495/* addTriEdge:
496 * Add an edge to g, with tail t, head h, and shared
497 * segment seg.
498 */
499static void addTriEdge(tgraph *g, int t, int h, ipair seg) {
500 g->edges = gv_recalloc(g->edges, g->nedges, g->nedges + 1,
501 sizeof(g->edges[0]));
502 tedge *ep = g->edges + g->nedges;
503 tnode *tp = g->nodes + t;
504 tnode *hp = g->nodes + h;
505
506 ep->t = t;
507 ep->h = h;
508 ep->dist = DIST(tp->ctr, hp->ctr);
509 ep->seg = seg;
510
511 tp->edges = gv_recalloc(tp->edges, tp->ne, tp->ne + 1,
512 sizeof(tp->edges[0]));
513 tp->edges[tp->ne++] = g->nedges;
514 hp->edges = gv_recalloc(hp->edges, hp->ne, hp->ne + 1,
515 sizeof(hp->edges[0]));
516 hp->edges[hp->ne++] = g->nedges;
517
518 g->nedges++;
519}
520
521static void freeTriGraph(tgraph * tg)
522{
523 for (size_t i = 0; i < tg->nnodes; ++i) {
524 free(tg->nodes[i].edges);
525 }
526 free(tg->nodes);
527 free(tg->edges);
528 free(tg);
529}
530
531/* mkTriGraph:
532 * Generate graph with triangles as nodes and an edge iff two triangles
533 * share an edge.
534 */
535static tgraph *mkTriGraph(surface_t *sf, pointf *pts) {
536 tnode *np;
537 int j, i, ne = 0;
538 int *jp;
539
540 /* ne is twice no. of edges */
541 for (i = 0; i < 3 * sf->nfaces; i++)
542 if (sf->neigh[i] != -1)
543 ne++;
544
545 tgraph *g = gv_alloc(sizeof(tgraph));
546
547 /* plus 2 for nodes added as endpoints of an edge */
548 g->nnodes = sf->nfaces + 2;
549 g->nodes = gv_calloc(g->nnodes, sizeof(tnode));
550
551 for (i = 0; i < sf->nfaces; i++) {
552 np = g->nodes + i;
553 np->ctr = triCenter(pts, sf->faces + 3 * i);
554 }
555
556 for (i = 0; i < sf->nfaces; i++) {
557 np = g->nodes + i;
558 jp = sf->neigh + 3 * i;
559 ne = 0;
560 while (ne < 3 && (j = *jp++) != -1) {
561 if (i < j) {
562 ipair seg =
563 sharedEdge(sf->faces + 3 * i, sf->faces + 3 * j);
564 addTriEdge(g, i, j, seg);
565 }
566 ne++;
567 }
568 }
569
570 return g;
571}
572
574{
575 free(rtr->ps);
576 free(rtr->obs);
577 free(rtr->tris);
578 dtclose(rtr->trimap);
579 freeTriGraph(rtr->tg);
580 free(rtr);
581}
582
583router_t *mkRouter(Ppoly_t** obsp, int npoly)
584{
585 router_t *rtr = gv_alloc(sizeof(router_t));
586 Ppoly_t* obs;
587 boxf bb;
588 int npts;
589 surface_t *sf;
590 /* points in obstacle i have indices obsi[i] through obsi[i+1]-1 in pts
591 */
592 int *obsi = gv_calloc(npoly + 1, sizeof(int));
593 int i, ix = 4, six = 0;
594
595 bb = bbox(obsp, npoly, &npts);
596 npts += 4; /* 4 points of bounding box */
597 pointf *pts = gv_calloc(npts, sizeof(pointf)); // all points are stored in pts
598 int *segs = gv_calloc(2 * npts, sizeof(int)); // indices of points forming segments
599
600 /* store bounding box in CCW order */
601 pts[0] = bb.LL;
602 pts[1].x = bb.UR.x;
603 pts[1].y = bb.LL.y;
604 pts[2] = bb.UR;
605 pts[3].x = bb.LL.x;
606 pts[3].y = bb.UR.y;
607 for (i = 1; i <= 4; i++) {
608 segs[six++] = i - 1;
609 if (i < 4)
610 segs[six++] = i;
611 else
612 segs[six++] = 0;
613 }
614
615 /* store obstacles in CW order and generate constraint segments */
616 for (i = 0; i < npoly; i++) {
617 obsi[i] = ix;
618 obs = *obsp++;
619 for (size_t j = 1; j <= obs->pn; j++) {
620 segs[six++] = ix;
621 if (j < obs->pn)
622 segs[six++] = ix + 1;
623 else
624 segs[six++] = obsi[i];
625 pts[ix++] = obs->ps[j - 1];
626 }
627 }
628 obsi[i] = ix;
629
630 /* copy points into coordinate arrays */
631 double *x = gv_calloc(npts, sizeof(double));
632 double *y = gv_calloc(npts, sizeof(double));
633 for (i = 0; i < npts; i++) {
634 x[i] = pts[i].x;
635 y[i] = pts[i].y;
636 }
637 sf = mkSurface(x, y, npts, segs, npts);
638 free(x);
639 free(y);
640 free(segs);
641
642 rtr->ps = pts;
643 rtr->pn = npts;
644 rtr->obs = obsi;
645 rtr->tris = mkTriIndices(sf);
646 rtr->trimap = mapSegToTri(sf);
647 rtr->tn = sf->nfaces;
648 rtr->tg = mkTriGraph(sf, pts);
649
650 freeSurface(sf);
651 return rtr;
652}
653
654/* finishEdge:
655 * Finish edge generation, clipping to nodes and adding arrowhead
656 * if necessary, and adding edge labels
657 */
658static void finishEdge(edge_t* e, Ppoly_t spl, int flip) {
659 if (flip) {
660 for (size_t j = 0; j < spl.pn / 2; j++) {
661 SWAP(&spl.ps[spl.pn - 1 - j], &spl.ps[j]);
662 }
663 }
664 if (Verbose > 1)
665 fprintf(stderr, "spline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
666 clip_and_install(e, aghead(e), spl.ps, spl.pn, &sinfo);
667
668 addEdgeLabels(e);
669}
670
671#define EQPT(p,q) (((p).x==(q).x)&&((p).y==(q).y))
672
673/* tweakEnd:
674 * Hack because path routing doesn't know about the interiors
675 * of polygons. If the first or last segment of the shortest path
676 * lies along one of the polygon boundaries, the path may flip
677 * inside the polygon. To avoid this, we shift the point a bit.
678 *
679 * If the edge p(=poly.ps[s])-q of the shortest path is also an
680 * edge of the border polygon, move p slightly inside the polygon
681 * and return it. If prv and nxt are the two vertices adjacent to
682 * p in the polygon, let m be the midpoint of prv--nxt. We then
683 * move a tiny bit along the ray p->m.
684 *
685 * Otherwise, return p unchanged.
686 */
688 Ppoint_t prv, nxt, p;
689
690 p = poly.ps[s];
691 nxt = poly.ps[(s + 1) % poly.pn];
692 if (s == 0)
693 prv = poly.ps[poly.pn-1];
694 else
695 prv = poly.ps[s - 1];
696 if (EQPT(q, nxt) || EQPT(q, prv) ){
697 Ppoint_t m;
698 m.x = (nxt.x + prv.x)/2.0 - p.x;
699 m.y = (nxt.y + prv.y)/2.0 - p.y;
700 const double d = hypot(m.x, m.y);
701 p.x += 0.1*m.x/d;
702 p.y += 0.1*m.y/d;
703 }
704 return p;
705}
706
707static void tweakPath(Ppoly_t poly, size_t t, Ppolyline_t pl) {
708 pl.ps[0] = tweakEnd(poly, 0, pl.ps[1]);
709 pl.ps[pl.pn-1] = tweakEnd (poly, t, pl.ps[pl.pn-2]);
710}
711
712
713/* genroute:
714 * Generate splines for e and cohorts.
715 * Edges go from 0 to t.
716 * Return 0 on success.
717 */
718static int genroute(tripoly_t *trip, int t, edge_t *e, int doPolyline) {
719 pointf eps[2];
720 Pvector_t evs[2];
721 pointf **cpts = NULL; /* lists of control points */
723 Ppolyline_t pl, spl;
724 Ppolyline_t mmpl;
725 int mult = ED_count(e);
726 node_t* head = aghead(e);
727 int rv = 0;
728
729 poly.ps = NULL;
730 pl.pn = 0;
731 eps[0].x = trip->poly.ps[0].x, eps[0].y = trip->poly.ps[0].y;
732 eps[1].x = trip->poly.ps[t].x, eps[1].y = trip->poly.ps[t].y;
733 if (Pshortestpath(&(trip->poly), eps, &pl) < 0) {
734 agwarningf("Could not create control points for multiple spline for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
735 rv = 1;
736 goto finish;
737 }
738
739 if (pl.pn == 2) {
740 makeStraightEdge(agraphof(head), e, doPolyline, &sinfo);
741 goto finish;
742 }
743
744 evs[0].x = evs[0].y = 0;
745 evs[1].x = evs[1].y = 0;
746
747 if (mult == 1 || Concentrate) {
748 poly = trip->poly;
749 Pedge_t *medges = gv_calloc(poly.pn, sizeof(Pedge_t));
750 for (size_t j = 0; j < poly.pn; j++) {
751 medges[j].a = poly.ps[j];
752 medges[j].b = poly.ps[(j + 1) % poly.pn];
753 }
754 assert(t >= 0);
755 tweakPath(poly, (size_t)t, pl);
756 if (Proutespline(medges, poly.pn, pl, evs, &spl) < 0) {
757 agwarningf("Could not create control points for multiple spline for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
758 rv = 1;
759 goto finish;
760 }
761 finishEdge(e, spl, aghead(e) != head);
762 free(medges);
763
764 return 0;
765 }
766
767 const size_t pn = 2 * (pl.pn - 1);
768
769 cpts = gv_calloc(pl.pn - 2, sizeof(pointf *));
770 for (size_t i = 0; i + 2 < pl.pn; i++) {
771 cpts[i] =
772 mkCtrlPts(t, mult+1, pl.ps[i], pl.ps[i + 1], pl.ps[i + 2], trip);
773 if (!cpts[i]) {
774 agwarningf("Could not create control points for multiple spline for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
775 rv = 1;
776 goto finish;
777 }
778 }
779
780 poly.ps = gv_calloc(pn, sizeof(pointf));
781 poly.pn = pn;
782
783 for (int i = 0; i < mult; i++) {
784 poly.ps[0] = eps[0];
785 for (size_t j = 1; j + 1 < pl.pn; j++) {
786 poly.ps[j] = cpts[j - 1][i];
787 }
788 poly.ps[pl.pn - 1] = eps[1];
789 for (size_t j = 1; j + 1 < pl.pn; j++) {
790 poly.ps[pn - j] = cpts[j - 1][i + 1];
791 }
792 if (Pshortestpath(&poly, eps, &mmpl) < 0) {
793 agwarningf("Could not create control points for multiple spline for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
794 rv = 1;
795 goto finish;
796 }
797
798 if (doPolyline) {
799 make_polyline (mmpl, &spl);
800 }
801 else {
802 Pedge_t *medges = gv_calloc(poly.pn, sizeof(Pedge_t));
803 for (size_t j = 0; j < poly.pn; j++) {
804 medges[j].a = poly.ps[j];
805 medges[j].b = poly.ps[(j + 1) % poly.pn];
806 }
807 tweakPath(poly, pl.pn - 1, mmpl);
808 const bool failed_routing = Proutespline(medges, poly.pn, mmpl, evs, &spl) < 0;
809 free(medges);
810 if (failed_routing) {
811 agwarningf("Could not create control points for multiple spline for edge (%s,%s)\n",
812 agnameof(agtail(e)), agnameof(aghead(e)));
813 rv = 1;
814 goto finish;
815 }
816 }
817 finishEdge(e, spl, aghead(e) != head);
818
819 e = ED_to_virt(e);
820 }
821
822finish :
823 if (cpts) {
824 for (size_t i = 0; i + 2 < pl.pn; i++)
825 free(cpts[i]);
826 free(cpts);
827 }
828 free(poly.ps);
829 return rv;
830}
831
832#define NSMALL -0.0000000001
833
834/* inCone:
835 * Returns true iff q is in the convex cone a-b-c
836 */
837static int
839{
840 return area2(q,a,b) >= NSMALL && area2(q,b,c) >= NSMALL;
841}
842
843static pointf north = {0, 1};
844static pointf northeast = {1, 1};
845static pointf east = {1, 0};
846static pointf southeast = {1, -1};
847static pointf south = {0, -1};
848static pointf southwest = {-1, -1};
849static pointf west = {-1, 0};
850static pointf northwest = {-1, 1};
851
852/* addEndpoint:
853 * Add node to graph representing spline end point p inside obstruction obs_id.
854 * For each side of obstruction, add edge from p to corresponding triangle.
855 * The node id of the new node in the graph is v_id.
856 * If p lies on the side of its node (sides != 0), we limit the triangles
857 * to those within 45 degrees of each side of the natural direction of p.
858 */
859static void addEndpoint(router_t * rtr, pointf p, node_t* v, int v_id, int sides)
860{
861 int obs_id = ND_lim(v);
862 int starti = rtr->obs[obs_id];
863 int endi = rtr->obs[obs_id + 1];
864 pointf* pts = rtr->ps;
865 int i, t;
866 pointf vr, v0, v1;
867
868 switch (sides) {
869 case TOP :
870 vr = add_pointf (p, north);
871 v0 = add_pointf (p, northwest);
872 v1 = add_pointf (p, northeast);
873 break;
874 case TOP|RIGHT :
875 vr = add_pointf (p, northeast);
876 v0 = add_pointf (p, north);
877 v1 = add_pointf (p, east);
878 break;
879 case RIGHT :
880 vr = add_pointf (p, east);
881 v0 = add_pointf (p, northeast);
882 v1 = add_pointf (p, southeast);
883 break;
884 case BOTTOM|RIGHT :
885 vr = add_pointf (p, southeast);
886 v0 = add_pointf (p, east);
887 v1 = add_pointf (p, south);
888 break;
889 case BOTTOM :
890 vr = add_pointf (p, south);
891 v0 = add_pointf (p, southeast);
892 v1 = add_pointf (p, southwest);
893 break;
894 case BOTTOM|LEFT :
895 vr = add_pointf (p, southwest);
896 v0 = add_pointf (p, south);
897 v1 = add_pointf (p, west);
898 break;
899 case LEFT :
900 vr = add_pointf (p, west);
901 v0 = add_pointf (p, southwest);
902 v1 = add_pointf (p, northwest);
903 break;
904 case TOP|LEFT :
905 vr = add_pointf (p, northwest);
906 v0 = add_pointf (p, west);
907 v1 = add_pointf (p, north);
908 break;
909 case 0 :
910 break;
911 default :
912 assert (0);
913 break;
914 }
915
916 rtr->tg->nodes[v_id].ne = 0;
917 rtr->tg->nodes[v_id].ctr = p;
918 for (i = starti; i < endi; i++) {
919 ipair seg;
920 seg.i = i;
921 if (i < endi - 1)
922 seg.j = i + 1;
923 else
924 seg.j = starti;
925 t = findMap(rtr->trimap, seg.i, seg.j);
926 if (sides && !inCone (v0, p, v1, pts[seg.i]) && !inCone (v0, p, v1, pts[seg.j]) && !raySeg(p,vr,pts[seg.i],pts[seg.j]))
927 continue;
928 addTriEdge(rtr->tg, v_id, t, seg);
929 }
930 assert(rtr->tg->nodes[v_id].ne > 0 && "no edges were added");
931}
932
933/* edgeToSeg:
934 * Given edge from i to j, find segment associated
935 * with the edge.
936 *
937 * This lookup could be made faster by modifying the
938 * shortest path algorithm to store the edges rather than
939 * the nodes.
940 */
941static ipair edgeToSeg(tgraph * tg, int i, int j)
942{
943 ipair ip = {0, 0};
944 tnode *np = tg->nodes + i;
945 tedge *ep;
946
947 for (size_t k = 0; k < np->ne; k++) {
948 ep = tg->edges + np->edges[k];
949 if (ep->t == j || ep->h == j)
950 return ep->seg;
951 }
952
953 assert(0);
954 return ip;
955}
956
957static void
959{
960 tri* tp;
961 tri* nxt;
962
963 free (trip->poly.ps);
964 for (size_t i = 0; i < trip->poly.pn; i++) {
965 for (tp = trip->triMap[i]; tp; tp = nxt) {
966 nxt = tp->nxttri;
967 free (tp);
968 }
969 }
970 free (trip->triMap);
971 free (trip);
972}
973
974/* Auxiliary data structure used to translate a path of rectangles
975 * into a polygon. Each side_t represents a vertex on one side of
976 * the polygon. v is the index of the vertex in the global router_t,
977 * and ts is a linked list of the indices of segments of sides opposite
978 * to v in some triangle on the path. These lists will be translated
979 * to polygon indices by mapTri, and stored in tripoly_t.triMap.
980 */
981typedef struct {
982 int v;
984} side_t;
985
986/* mkPoly:
987 * Construct simple polygon from shortest path from t to s in g.
988 * dad gives the indices of the triangles on path.
989 * sx used to store index of s in points.
990 * index of t is always 0
991 */
992static tripoly_t *mkPoly(router_t * rtr, int *dad, int s, int t,
993 pointf p_s, pointf p_t, int *sx)
994{
995 tripoly_t *ps;
996 int nxt;
997 ipair p;
998 size_t nt = 0;
999 int idx;
1000 int cnt1 = 0;
1001 int cnt2 = 0;
1002 pointf *pts;
1003 /* maps vertex index used in router_t to vertex index used in tripoly */
1004 Dt_t *vmap;
1005
1006 /* count number of triangles in path */
1007 for (nxt = dad[t]; nxt != s; nxt = dad[nxt]) {
1008 nt++;
1009 assert (nxt != dad[nxt] && "infinite loop due to 'nxt' not changing");
1010 }
1011
1012 side_t *side1 = gv_calloc(nt + 4, sizeof(side_t));
1013 side_t *side2 = gv_calloc(nt + 4, sizeof(side_t));
1014
1015 nxt = dad[t];
1016 p = edgeToSeg(rtr->tg, nxt, t);
1017 side1[cnt1].ts = addTri(-1, p.j, NULL);
1018 side1[cnt1++].v = p.i;
1019 side2[cnt2].ts = addTri(-1, p.i, NULL);
1020 side2[cnt2++].v = p.j;
1021
1022 t = nxt;
1023 for (nxt = dad[t]; nxt >= 0; nxt = dad[nxt]) {
1024 p = edgeToSeg(rtr->tg, t, nxt);
1025 if (p.i == side1[cnt1 - 1].v) {
1026 side1[cnt1 - 1].ts =
1027 addTri(side2[cnt2 - 1].v, p.j, side1[cnt1 - 1].ts);
1028 side2[cnt2 - 1].ts =
1029 addTri(side1[cnt1 - 1].v, p.j, side2[cnt2 - 1].ts);
1030 side2[cnt2].ts =
1031 addTri(side2[cnt2 - 1].v, side1[cnt1 - 1].v, NULL);
1032 side2[cnt2++].v = p.j;
1033 } else if (p.i == side2[cnt2 - 1].v) {
1034 side1[cnt1 - 1].ts =
1035 addTri(side2[cnt2 - 1].v, p.j, side1[cnt1 - 1].ts);
1036 side2[cnt2 - 1].ts =
1037 addTri(side1[cnt1 - 1].v, p.j, side2[cnt2 - 1].ts);
1038 side1[cnt1].ts =
1039 addTri(side2[cnt2 - 1].v, side1[cnt1 - 1].v, NULL);
1040 side1[cnt1++].v = p.j;
1041 } else if (p.j == side1[cnt1 - 1].v) {
1042 side1[cnt1 - 1].ts =
1043 addTri(side2[cnt2 - 1].v, p.i, side1[cnt1 - 1].ts);
1044 side2[cnt2 - 1].ts =
1045 addTri(side1[cnt1 - 1].v, p.i, side2[cnt2 - 1].ts);
1046 side2[cnt2].ts =
1047 addTri(side2[cnt2 - 1].v, side1[cnt1 - 1].v, NULL);
1048 side2[cnt2++].v = p.i;
1049 } else {
1050 side1[cnt1 - 1].ts =
1051 addTri(side2[cnt2 - 1].v, p.i, side1[cnt1 - 1].ts);
1052 side2[cnt2 - 1].ts =
1053 addTri(side1[cnt1 - 1].v, p.i, side2[cnt2 - 1].ts);
1054 side1[cnt1].ts =
1055 addTri(side2[cnt2 - 1].v, side1[cnt1 - 1].v, NULL);
1056 side1[cnt1++].v = p.i;
1057 }
1058 t = nxt;
1059 }
1060 side1[cnt1 - 1].ts = addTri(-2, side2[cnt2 - 1].v, side1[cnt1 - 1].ts);
1061 side2[cnt2 - 1].ts = addTri(-2, side1[cnt1 - 1].v, side2[cnt2 - 1].ts);
1062
1063 /* store points in pts starting with t in 0,
1064 * then side1, then s, then side2
1065 */
1066 vmap = dtopen(&ipairdisc, Dtoset);
1067 vmapAdd(vmap, -1, 0);
1068 vmapAdd(vmap, -2, cnt1 + 1);
1069 pointf *pps = pts = gv_calloc(nt + 4, sizeof(pointf));
1070 tri **trim = gv_calloc(nt + 4, sizeof(tri*));
1071 *pps++ = p_t;
1072 idx = 1;
1073 for (int i = 0; i < cnt1; i++) {
1074 vmapAdd(vmap, side1[i].v, idx);
1075 *pps++ = rtr->ps[side1[i].v];
1076 trim[idx++] = side1[i].ts;
1077 }
1078 *pps++ = p_s;
1079 idx++;
1080 for (int i = cnt2 - 1; i >= 0; i--) {
1081 vmapAdd(vmap, side2[i].v, idx);
1082 *pps++ = rtr->ps[side2[i].v];
1083 trim[idx++] = side2[i].ts;
1084 }
1085
1086 for (size_t i = 0; i < nt + 4; i++) {
1087 mapTri(vmap, trim[i]);
1088 }
1089
1090 ps = gv_alloc(sizeof(tripoly_t));
1091 ps->poly.pn = nt + 4; /* nt triangles gives nt+2 points plus s and t */
1092 ps->poly.ps = pts;
1093 ps->triMap = trim;
1094
1095 free (side1);
1096 free (side2);
1097 dtclose(vmap);
1098 *sx = cnt1 + 1; /* index of s in ps */
1099 return ps;
1100}
1101
1102/* resetGraph:
1103 * Remove edges and nodes added for current edge routing
1104 */
1105static void resetGraph(tgraph *g, int ncnt, int ecnt,
1106 size_t *original_edge_count) {
1107 int i;
1108 tnode *np = g->nodes;
1109 g->nedges = ecnt;
1110 for (i = 0; i < ncnt; i++) {
1111 np->ne = original_edge_count[i];
1112 np++;
1113 }
1114}
1115
1116#define PQTYPE int
1117#define PQVTYPE float
1118
1119#define PQ_TYPES
1120#include <neatogen/fPQ.h>
1121#undef PQ_TYPES
1122
1123typedef struct {
1124 PQ pq;
1126 int *idxs;
1127} PPQ;
1128
1129#define N_VAL(pq,n) ((PPQ*)pq)->vals[n]
1130#define N_IDX(pq,n) ((PPQ*)pq)->idxs[n]
1131
1132#define PQ_CODE
1133#include <neatogen/fPQ.h>
1134#undef PQ_CODE
1135
1136#define N_DAD(n) dad[n]
1137#define E_WT(e) (e->dist)
1138#define UNSEEN (-FLT_MAX)
1139
1140/* triPath:
1141 * Find the shortest path with lengths in g from
1142 * v0 to v1. The returned vector (dad) encodes the
1143 * shorted path from v1 to v0. That path is given by
1144 * v1, dad[v1], dad[dad[v1]], ..., v0.
1145 */
1146static int *
1147triPath(tgraph * g, int n, int v0, int v1, PQ * pq)
1148{
1149 int i, adjn;
1150 double d;
1151 tnode *np;
1152 tedge *e;
1153 int *dad = gv_calloc(n, sizeof(int));
1154
1155 for (i = 0; i < pq->PQsize; i++)
1156 N_VAL(pq, i) = UNSEEN;
1157
1158 PQinit(pq);
1159 N_DAD(v0) = -1;
1160 N_VAL(pq, v0) = 0;
1161 if (PQinsert(pq, v0))
1162 return NULL;
1163
1164 while ((i = PQremove(pq)) != -1) {
1165 N_VAL(pq, i) *= -1;
1166 if (i == v1)
1167 break;
1168 np = g->nodes + i;
1169 for (size_t j = 0; j < np->ne; j++) {
1170 e = g->edges + np->edges[j];
1171 if (e->t == i)
1172 adjn = e->h;
1173 else
1174 adjn = e->t;
1175 if (N_VAL(pq, adjn) < 0) {
1176 d = -(N_VAL(pq, i) + E_WT(e));
1177 if (N_VAL(pq, adjn) == UNSEEN) {
1178 N_VAL(pq, adjn) = d;
1179 N_DAD(adjn) = i;
1180 if (PQinsert(pq, adjn)) {
1181 free(dad);
1182 return NULL;
1183 }
1184 } else if (N_VAL(pq, adjn) < d) {
1185 PQupdate(pq, adjn, d);
1186 N_DAD(adjn) = i;
1187 }
1188 }
1189 }
1190 }
1191 return dad;
1192}
1193
1194/* makeMultiSpline:
1195 * FIX: we don't really use the shortest path provided by ED_path,
1196 * so avoid in neato spline code.
1197 * Return 0 on success.
1198 */
1199int makeMultiSpline(edge_t* e, router_t * rtr, int doPolyline) {
1200 Ppolyline_t line = ED_path(e);
1201 node_t *t = agtail(e);
1202 node_t *h = aghead(e);
1203 pointf t_p = line.ps[0];
1204 pointf h_p = line.ps[line.pn - 1];
1205 tripoly_t *poly;
1206 int idx;
1207 int *sp;
1208 int t_id = rtr->tn;
1209 int h_id = rtr->tn + 1;
1210 int ecnt = rtr->tg->nedges;
1211 PPQ pq;
1212 int ret;
1213
1214 // record the number of edges in each node, so we can drop the added ones
1215 // later
1216 size_t *original_edge_count = gv_calloc(rtr->tg->nnodes,
1217 sizeof(original_edge_count[0]));
1218 for (size_t i = 0; i < rtr->tg->nnodes; ++i)
1219 original_edge_count[i] = rtr->tg->nodes[i].ne;
1220
1221 /* Add endpoints to triangle graph */
1222 addEndpoint(rtr, t_p, t, t_id, ED_tail_port(e).side);
1223 addEndpoint(rtr, h_p, h, h_id, ED_head_port(e).side);
1224
1225 /* Initialize priority queue */
1226 PQgen(&pq.pq, rtr->tn + 2, -1);
1227 PQTYPE *idxs = gv_calloc(pq.pq.PQsize + 1, sizeof(PQTYPE));
1228 PQVTYPE *vals = gv_calloc(pq.pq.PQsize + 1, sizeof(PQVTYPE));
1229 vals[0] = 0;
1230 pq.vals = vals + 1;
1231 pq.idxs = idxs + 1;
1232
1233 /* Find shortest path of triangles */
1234 sp = triPath(rtr->tg, rtr->tn+2, h_id, t_id, (PQ *) & pq);
1235
1236 free(vals);
1237 free(idxs);
1238 PQfree(&(pq.pq), 0);
1239
1240 /* Use path of triangles to generate guiding polygon */
1241 if (sp) {
1242 poly = mkPoly(rtr, sp, h_id, t_id, h_p, t_p, &idx);
1243 free(sp);
1244
1245 /* Generate multiple splines using polygon */
1246 ret = genroute(poly, idx, e, doPolyline);
1247 freeTripoly (poly);
1248 }
1249 else ret = -1;
1250
1251 resetGraph(rtr->tg, rtr->tn, ecnt, original_edge_count);
1252 free(original_edge_count);
1253 return ret;
1254}
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
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
#define dtmatch(d, o)
Definition cdt.h:184
#define dtsearch(d, o)
Definition cdt.h:183
#define dtinsert(d, o)
Definition cdt.h:185
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
#define LEFT
Definition const.h:120
#define RIGHT
Definition const.h:118
#define BOTTOM
Definition const.h:117
#define TOP
Definition const.h:119
surface_t * mkSurface(double *x, double *y, int n, int *segs, int nsegs)
Definition delaunay.c:725
void freeSurface(surface_t *s)
Definition delaunay.c:737
#define head
Definition dthdr.h:15
static Dtdisc_t disc
Definition exparse.y:207
void PQinit(void)
Definition fPQ.c:44
void PQgen(int sz)
Definition fPQ.c:25
void PQfree(void)
Definition fPQ.c:36
snode * PQremove(void)
Definition fPQ.c:121
void PQupdate(snode *n, int d)
Definition fPQ.c:137
int line_intersect(pointf a, pointf b, pointf c, pointf d, pointf *p)
Definition geom.c:235
#define DIST(p, q)
Definition geom.h:56
static pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
bool Concentrate
Definition globals.h:59
static bool Verbose
Definition gml2gv.c:23
void free(void *)
node NULL
Definition grammar.y:163
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:200
#define ED_count(e)
Definition types.h:580
#define agtail(e)
Definition cgraph.h:883
#define ED_path(e)
Definition types.h:593
#define aghead(e)
Definition cgraph.h:884
#define ED_head_port(e)
Definition types.h:588
#define ED_tail_port(e)
Definition types.h:597
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define ND_lim(n)
Definition types.h:504
Agraph_t * agraphof(void *obj)
Definition obj.c:185
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:130
void PQinsert(pq_t *pq, Halfedge *he, Site *v, double offset)
Definition heap.c:54
$2 u p prev
Definition htmlparse.y:297
static int * ps
Definition lu.c:51
static tripoly_t * mkPoly(router_t *rtr, int *dad, int s, int t, pointf p_s, pointf p_t, int *sx)
static int findMap(Dt_t *map, int a, int b)
static void addMap(Dt_t *map, int a, int b, int t)
Definition multispline.c:96
static tgraph * mkTriGraph(surface_t *sf, pointf *pts)
static pointf southwest
static void mapTri(Dt_t *map, tri *tp)
#define PQVTYPE
router_t * mkRouter(Ppoly_t **obsp, int npoly)
static pointf northwest
static ipair sharedEdge(int *p, int *q)
struct _tri tri
static int raySegIntersect(pointf v, pointf w, pointf a, pointf b, pointf *p)
static int * mkTriIndices(surface_t *sf)
static bool swap_ends_p(edge_t *e)
Definition multispline.c:29
static bool spline_merge(node_t *n)
Definition multispline.c:23
void freeRouter(router_t *rtr)
static void addTriEdge(tgraph *g, int t, int h, ipair seg)
#define N_DAD(n)
static int vMap(Dt_t *map, int i)
static void finishEdge(edge_t *e, Ppoly_t spl, int flip)
static pointf east
static int inCone(pointf a, pointf b, pointf c, pointf q)
static void * newIpair(void *p, Dtdisc_t *disc)
static int cmpItem(void *item1, void *item2)
Definition multispline.c:63
static void resetGraph(tgraph *g, int ncnt, int ecnt, size_t *original_edge_count)
int makeMultiSpline(edge_t *e, router_t *rtr, int doPolyline)
static pointf * mkCtrlPts(int s, int mult, pointf prev, pointf v, pointf nxt, tripoly_t *trip)
static ipair edgeToSeg(tgraph *tg, int i, int j)
static void * newItem(void *p, Dtdisc_t *disc)
Definition multispline.c:75
static tri * addTri(int i, int j, tri *oldp)
static void tweakPath(Ppoly_t poly, size_t t, Ppolyline_t pl)
#define NSMALL
static int genroute(tripoly_t *trip, int t, edge_t *e, int doPolyline)
static pointf triCenter(pointf *pts, int *idxs)
static pointf northeast
static Ppoint_t tweakEnd(Ppoly_t poly, size_t s, Ppoint_t q)
#define SEP
static pointf west
static int * triPath(tgraph *g, int n, int v0, int v1, PQ *pq)
#define E_WT(e)
static Dtdisc_t itemdisc
Definition multispline.c:87
static int cmpIpair(void *pair1, void *pair2)
static int raySeg(pointf v, pointf w, pointf a, pointf b)
static pointf southeast
#define UNSEEN
#define MARGIN
static pointf north
static Dt_t * mapSegToTri(surface_t *sf)
static Dtdisc_t ipairdisc
#define N_VAL(pq, n)
static splineInfo sinfo
Definition multispline.c:35
static void freeTriGraph(tgraph *tg)
static int triPoint(tripoly_t *trip, int vx, pointf v, pointf w, pointf *ip)
static int ctrlPtIdx(pointf v, Ppoly_t *polys)
static boxf bbox(Ppoly_t **obsp, int npoly, int *np)
static pointf south
static void vmapAdd(Dt_t *map, int i, int j)
static void addEndpoint(router_t *rtr, pointf p, node_t *v, int v_id, int sides)
#define PQTYPE
static double bisect(pointf pp, pointf cp, pointf np)
#define EQPT(p, q)
static void freeTripoly(tripoly_t *trip)
void make_polyline(Ppolyline_t line, Ppolyline_t *sline)
Definition util.c:59
int Proutespline(Pedge_t *barriers, size_t n_barriers, Ppolyline_t input_route, Pvector_t endpoint_slopes[2], Ppolyline_t *output_route)
Definition route.c:69
int Pshortestpath(Ppoly_t *boundary, Ppoint_t endpoints[2], Ppolyline_t *output_route)
Definition shortest.c:83
PATHUTIL_API int wind(Ppoint_t a, Ppoint_t b, Ppoint_t c)
Definition visibility.c:53
PATHUTIL_API COORD area2(Ppoint_t, Ppoint_t, Ppoint_t)
Definition visibility.c:44
void clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn, splineInfo *info)
Definition splines.c:239
void makeStraightEdge(graph_t *g, edge_t *e, int edgetype, splineInfo *info)
Definition routespl.c:926
void addEdgeLabels(edge_t *e)
Definition splines.c:1326
static triangles_t tris
Definition shortest.c:55
Dtlink_t link
PQVTYPE * vals
int * idxs
Ppoint_t b
Definition pathgeom.h:53
Ppoint_t a
Definition pathgeom.h:53
size_t pn
Definition pathgeom.h:47
Ppoint_t * ps
Definition pathgeom.h:46
double x
Definition pathgeom.h:38
double y
Definition pathgeom.h:38
struct _tri * nxttri
Definition multispline.c:44
ipair v
Definition multispline.c:43
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition cdt.h:100
int key
Definition cdt.h:85
int i
Definition multispline.c:39
int j
Definition multispline.c:39
Definition utils.c:749
node_t * t
Definition utils.c:752
int a[2]
Definition multispline.c:59
int t
Definition multispline.c:60
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition heap.c:19
tgraph * tg
pointf * ps
Dt_t * trimap
int * tris
int * obs
tri * ts
bool(* swapEnds)(edge_t *e)
Definition types.h:67
int * faces
Definition delaunay.h:19
int nfaces
Definition delaunay.h:18
int * neigh
Definition delaunay.h:20
ipair seg
double dist
tnode * nodes
int nedges
size_t nnodes
tedge * edges
size_t ne
int * edges
pointf ctr
tri ** triMap
Definition multispline.c:49
Ppoly_t poly
Definition multispline.c:48
struct poly_s poly
Definition grammar.c:93