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