Graphviz 14.0.5~dev.20251117.1017
Loading...
Searching...
No Matches
delaunay.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 <stdbool.h>
14#include <stddef.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <math.h>
19#include <cgraph/cgraph.h> /* for agerr() and friends */
20#include <neatogen/delaunay.h>
21#include <util/alloc.h>
22#include <util/sort.h>
23
24#ifdef HAVE_GTS
25#include <gts.h>
26
27static int triangle_is_hole(void *triangle, void *ignored) {
28 GtsTriangle *t = triangle;
29 (void)ignored;
30
31 GtsEdge *e1, *e2, *e3;
32 GtsVertex *v1, *v2, *v3;
33
34 gts_triangle_vertices_edges(t, NULL, &v1, &v2, &v3, &e1, &e2, &e3);
35
36 if ((GTS_IS_CONSTRAINT(e1) && GTS_SEGMENT(e1)->v1 != v1) ||
37 (GTS_IS_CONSTRAINT(e2) && GTS_SEGMENT(e2)->v1 != v2) ||
38 (GTS_IS_CONSTRAINT(e3) && GTS_SEGMENT(e3)->v1 != v3))
39 return TRUE;
40
41 return FALSE;
42}
43
44static unsigned delaunay_remove_holes(GtsSurface *surface) {
45 return gts_surface_foreach_face_remove(surface, triangle_is_hole, NULL);
46}
47
48/* Derived classes for vertices and faces so we can assign integer ids
49 * to make it easier to identify them. In particular, this allows the
50 * segments and faces to refer to vertices using the order in which
51 * they were passed in.
52 */
53typedef struct {
54 GtsVertex v;
55 int idx;
56} GVertex;
57
59static GVertex *downcast(GtsVertex *base) {
60 return (GVertex *)((char *)base - offsetof(GVertex, v));
61}
62
64static GtsVertex *upcast(GVertex *derived) {
65 return &derived->v;
66}
67
68typedef struct {
69 GtsVertexClass parent_class;
70} GVertexClass;
71
72static GtsVertexClass *g_vertex_class(void) {
73 static GVertexClass *klass = NULL;
74
75 if (klass == NULL) {
76 GtsObjectClassInfo vertex_info = {
77 .name = "GVertex",
78 .object_size = sizeof(GVertex),
79 .class_size = sizeof(GVertexClass),
80 };
81 klass = gts_object_class_new(GTS_OBJECT_CLASS(gts_vertex_class()),
82 &vertex_info);
83 }
84
85 return &klass->parent_class;
86}
87
88typedef struct {
89 GtsFace v;
90 int idx;
91} GFace;
92
93typedef struct {
94 GtsFaceClass parent_class;
95} GFaceClass;
96
97static GtsFaceClass *g_face_class(void) {
98 static GFaceClass *klass = NULL;
99
100 if (klass == NULL) {
101 GtsObjectClassInfo face_info = {
102 .name = "GFace",
103 .object_size = sizeof(GFace),
104 .class_size = sizeof(GFaceClass),
105 };
106 klass = gts_object_class_new(GTS_OBJECT_CLASS(gts_face_class()),
107 &face_info);
108 }
109
110 return &klass->parent_class;
111}
112
114static void
115destroy (GtsVertex* v)
116{
117 GSList * i;
118
119 i = v->segments;
120 while (i) {
121 GSList * next = i->next;
122 gts_object_destroy (i->data);
123 i = next;
124 }
125 g_assert (v->segments == NULL);
126 gts_object_destroy(GTS_OBJECT(v));
127}
128
129/* Main entry point to using GTS for triangulation.
130 * Input is npt points with x and y coordinates stored either separately
131 * in x[] and y[] (sepArr != 0) or consecutively in x[] (sepArr == 0).
132 * Optionally, the input can include nsegs line segments, whose endpoint
133 * indices are supplied in segs[2*i] and segs[2*i+1] yielding a constrained
134 * triangulation.
135 *
136 * The return value is the corresponding gts surface, which can be queries for
137 * the triangles and line segments composing the triangulation.
138 */
139static GtsSurface*
140tri(double *x, double *y, int npt, int *segs, int nsegs, int sepArr)
141{
142 int i;
143 GtsSurface *surface;
144 GVertex **vertices = gv_calloc(npt, sizeof(GVertex *));
145 GtsEdge **edges = gv_calloc(nsegs, sizeof(GtsEdge *));
146 GSList *list = NULL;
147 GtsVertex *v1, *v2, *v3;
148 GtsTriangle *t;
149 GtsVertexClass *vcl = g_vertex_class();
150 GtsEdgeClass *ecl = GTS_EDGE_CLASS (gts_constraint_class ());
151
152 if (sepArr) {
153 for (i = 0; i < npt; i++) {
154 GVertex *p = downcast(gts_vertex_new(vcl, x[i], y[i], 0));
155 p->idx = i;
156 vertices[i] = p;
157 }
158 }
159 else {
160 for (i = 0; i < npt; i++) {
161 GVertex *p = downcast(gts_vertex_new(vcl, x[2*i], x[2*i+1], 0));
162 p->idx = i;
163 vertices[i] = p;
164 }
165 }
166
167 /* N.B. Edges need to be created here, presumably before the
168 * the vertices are added to the face. In particular, they cannot
169 * be added created and added vi gts_delaunay_add_constraint() below.
170 */
171 for (i = 0; i < nsegs; i++) {
172 edges[i] = gts_edge_new(ecl,
173 upcast(vertices[segs[2 * i]]),
174 upcast(vertices[segs[2 * i + 1]]));
175 }
176
177 for (i = 0; i < npt; i++)
178 list = g_slist_prepend(list, vertices[i]);
179 t = gts_triangle_enclosing(gts_triangle_class(), list, 100.);
180 g_slist_free(list);
181
182 gts_triangle_vertices(t, &v1, &v2, &v3);
183
184 surface = gts_surface_new(gts_surface_class(), g_face_class(),
185 gts_edge_class(), gts_vertex_class());
186 gts_surface_add_face(surface, gts_face_new(gts_face_class(),
187 t->e1, t->e2, t->e3));
188
189 for (i = 0; i < npt; i++) {
190 GtsVertex *v4 = upcast(vertices[i]);
191 GtsVertex *v = gts_delaunay_add_vertex(surface, v4, NULL);
192
193 /* if v != NULL, it is a previously added pt with the same
194 * coordinates as v4, in which case we replace v4 with v
195 */
196 if (v && v4 != v) {
197 gts_vertex_replace(v4, v);
198 }
199 }
200
201 for (i = 0; i < nsegs; i++) {
202 gts_delaunay_add_constraint(surface,GTS_CONSTRAINT(edges[i]));
203 }
204
205 /* destroy enclosing triangle */
206 gts_allow_floating_vertices = TRUE;
207 gts_allow_floating_edges = TRUE;
208 destroy(v1);
209 destroy(v2);
210 destroy(v3);
211 gts_allow_floating_edges = FALSE;
212 gts_allow_floating_vertices = FALSE;
213
214 if (nsegs)
215 delaunay_remove_holes(surface);
216
217 free (edges);
218 free(vertices);
219 return surface;
220}
221
222typedef struct {
223 int n;
224 v_data *delaunay;
225} estats;
226
227static int cnt_edge(void *edge, void *stats) {
228 GtsSegment *e = edge;
229 estats *sp = stats;
230
231 sp->n++;
232 if (sp->delaunay) {
233 sp->delaunay[downcast(e->v1)->idx].nedges++;
234 sp->delaunay[downcast(e->v2)->idx].nedges++;
235 }
236
237 return 0;
238}
239
240static void
241edgeStats (GtsSurface* s, estats* sp)
242{
243 gts_surface_foreach_edge(s, cnt_edge, sp);
244}
245
246static int add_edge(void *edge, void *data) {
247 GtsSegment *e = edge;
248 v_data *delaunay = data;
249
250 int source = downcast(e->v1)->idx;
251 int dest = downcast(e->v2)->idx;
252
253 delaunay[source].edges[delaunay[source].nedges++] = dest;
254 delaunay[dest].edges[delaunay[dest].nedges++] = source;
255
256 return 0;
257}
258
259static v_data *delaunay_triangulation(double *x, double *y, int n) {
260 GtsSurface* s = tri(x, y, n, NULL, 0, 1);
261 int i, nedges;
262 estats stats;
263
264 if (!s) return NULL;
265
266 v_data *delaunay = gv_calloc(n, sizeof(v_data));
267
268 for (i = 0; i < n; i++) {
269 delaunay[i].ewgts = NULL;
270 delaunay[i].nedges = 1;
271 }
272
273 stats.n = 0;
274 stats.delaunay = delaunay;
275 edgeStats (s, &stats);
276 nedges = stats.n;
277 int *edges = gv_calloc(2 * nedges + n, sizeof(int));
278
279 for (i = 0; i < n; i++) {
280 delaunay[i].edges = edges;
281 edges += delaunay[i].nedges;
282 delaunay[i].edges[0] = i;
283 delaunay[i].nedges = 1;
284 }
285 gts_surface_foreach_edge(s, add_edge, delaunay);
286
287 gts_object_destroy (GTS_OBJECT (s));
288
289 return delaunay;
290}
291
292typedef struct {
293 int n;
294 int* edges;
295} estate;
296
297static int addEdge(void *edge, void *state) {
298 GtsSegment *e = edge;
299 estate *es = state;
300
301 int source = downcast(e->v1)->idx;
302 int dest = downcast(e->v2)->idx;
303
304 es->edges[2 * es->n] = source;
305 es->edges[2 * es->n + 1] = dest;
306 es->n += 1;
307
308 return 0;
309}
310
311static int vcmp(const void *x, const void *y, void *values) {
312 const int *a = x;
313 const int *b = y;
314 const double *_vals = values;
315 double va = _vals[*a];
316 double vb = _vals[*b];
317
318 if (va < vb) return -1;
319 if (va > vb) return 1;
320 return 0;
321}
322
323/* Given n points whose coordinates are in the x[] and y[]
324 * arrays, compute a Delaunay triangulation of the points.
325 * The number of edges in the triangulation is returned in pnedges.
326 * The return value itself is an array e of 2*(*pnedges) integers,
327 * with edge i having points whose indices are e[2*i] and e[2*i+1].
328 *
329 * If the points are collinear, GTS fails with 0 edges.
330 * In this case, we sort the points by x coordinates (or y coordinates
331 * if the points form a vertical line). We then return a "triangulation"
332 * consisting of the n-1 pairs of adjacent points.
333 */
334int *delaunay_tri(double *x, double *y, int n, int* pnedges)
335{
336 GtsSurface* s = tri(x, y, n, NULL, 0, 1);
337 int* edges;
338
339 if (!s) return NULL;
340
341 estats stats = {0};
342 edgeStats (s, &stats);
343 int nedges = *pnedges = stats.n;
344
345 if (nedges) {
346 edges = gv_calloc(2 * nedges, sizeof(int));
347 gts_surface_foreach_edge(s, addEdge, &(estate){.edges = edges});
348 }
349 else {
350 int* vs = gv_calloc(n, sizeof(int));
351
352 *pnedges = nedges = n-1;
353 int *ip = edges = gv_calloc(2 * nedges, sizeof(int));
354
355 for (int i = 0; i < n; i++)
356 vs[i] = i;
357
358 gv_sort(vs, n, sizeof(int), vcmp, x[0] == x[1] /* vertical line? */ ? y : x);
359
360 int tl = vs[0];
361 for (int i = 1; i < n; i++) {
362 const int hd = vs[i];
363 *ip++ = tl;
364 *ip++ = hd;
365 tl = hd;
366 }
367
368 free (vs);
369 }
370
371 gts_object_destroy (GTS_OBJECT (s));
372
373 return edges;
374}
375
376static int cntFace(void *face, void *data) {
377 GFace *fp = face;
378 int *ip = data;
379
380 fp->idx = *ip;
381 *ip += 1;
382
383 return 0;
384}
385
386typedef struct {
387 GtsSurface* s;
388 int* faces;
389 int* neigh;
390} fstate;
391
392typedef struct {
393 int nneigh;
394 int* neigh;
395} ninfo;
396
397static int addNeighbor(void *face, void *ni) {
398 GFace *f = face;
399 ninfo *es = ni;
400
401 es->neigh[es->nneigh] = f->idx;
402 es->nneigh++;
403
404 return 0;
405}
406
407static int addFace(void *face, void *state) {
408 GFace *f = face;
409 fstate *es = state;
410
411 int i, myid = f->idx;
412 int* ip = es->faces + 3*myid;
413 int* neigh = es->neigh + 3*myid;
414 ninfo ni;
415 GtsVertex *v1, *v2, *v3;
416
417 gts_triangle_vertices (&f->v.triangle, &v1, &v2, &v3);
418 *ip++ = downcast(v1)->idx;
419 *ip++ = downcast(v2)->idx;
420 *ip++ = downcast(v3)->idx;
421
422 ni.nneigh = 0;
423 ni.neigh = neigh;
424 gts_face_foreach_neighbor((GtsFace*)f, 0, addNeighbor, &ni);
425 for (i = ni.nneigh; i < 3; i++)
426 neigh[i] = -1;
427
428 return 0;
429}
430
431static int addTri(void *face, void *state) {
432 GFace *f = face;
433 fstate *es = state;
434
435 int myid = f->idx;
436 int* ip = es->faces + 3*myid;
437 GtsVertex *v1, *v2, *v3;
438
439 gts_triangle_vertices (&f->v.triangle, &v1, &v2, &v3);
440 *ip++ = downcast(v1)->idx;
441 *ip++ = downcast(v2)->idx;
442 *ip++ = downcast(v3)->idx;
443
444 return 0;
445}
446
447/* Given n points whose coordinates are in x[] and y[], and nsegs line
448 * segments whose end point indices are given in segs, return a surface
449 * corresponding the constrained Delaunay triangulation.
450 * The surface records the line segments, the triangles, and the neighboring
451 * triangles.
452 */
453surface_t*
454mkSurface (double *x, double *y, int n, int* segs, int nsegs)
455{
456 GtsSurface* s = tri(x, y, n, segs, nsegs, 1);
457 estats stats;
458 estate state;
459 fstate statf;
460 int nfaces = 0;
461
462 if (!s) return NULL;
463
464 surface_t *sf = gv_alloc(sizeof(surface_t));
465 stats.n = 0;
466 stats.delaunay = NULL;
467 edgeStats (s, &stats);
468 nsegs = stats.n;
469 segs = gv_calloc(2 * nsegs, sizeof(int));
470
471 state.n = 0;
472 state.edges = segs;
473 gts_surface_foreach_edge(s, addEdge, &state);
474
475 gts_surface_foreach_face(s, cntFace, &nfaces);
476
477 int *faces = gv_calloc(3 * nfaces, sizeof(int));
478 int *neigh = gv_calloc(3 * nfaces, sizeof(int));
479
480 statf.faces = faces;
481 statf.neigh = neigh;
482 gts_surface_foreach_face(s, addFace, &statf);
483
484 sf->nedges = nsegs;
485 sf->edges = segs;
486 sf->nfaces = nfaces;
487 sf->faces = faces;
488 sf->neigh = neigh;
489
490 gts_object_destroy (GTS_OBJECT (s));
491
492 return sf;
493}
494
495/* Given n points whose coordinates are stored as (x[2*i],x[2*i+1]),
496 * compute a Delaunay triangulation of the points.
497 * The number of triangles in the triangulation is returned in tris.
498 * The return value t is an array of 3*(*tris) integers,
499 * with triangle i having points whose indices are t[3*i], t[3*i+1] and t[3*i+2].
500 */
501int*
502get_triangles (double *x, int n, int* tris)
503{
504 GtsSurface* s;
505 int nfaces = 0;
506 fstate statf;
507
508 if (n <= 2) return NULL;
509
510 s = tri(x, NULL, n, NULL, 0, 0);
511 if (!s) return NULL;
512
513 gts_surface_foreach_face(s, cntFace, &nfaces);
514 statf.faces = gv_calloc(3 * nfaces, sizeof(int));
515 gts_surface_foreach_face(s, addTri, &statf);
516
517 gts_object_destroy (GTS_OBJECT (s));
518
519 *tris = nfaces;
520 return statf.faces;
521}
522
523void
525{
526 free (s->edges);
527 free (s->faces);
528 free (s->neigh);
529 free(s);
530}
531#else
532static char* err = "Graphviz built without any triangulation library\n";
533int* get_triangles (double *x, int n, int* tris)
534{
535 (void)x;
536 (void)n;
537 (void)tris;
538
539 agerrorf("get_triangles: %s\n", err);
540 return 0;
541}
542static v_data *delaunay_triangulation(double *x, double *y, int n) {
543 (void)x;
544 (void)y;
545 (void)n;
546
547 agerrorf("delaunay_triangulation: %s\n", err);
548 return 0;
549}
550int *delaunay_tri(double *x, double *y, int n, int* nedges)
551{
552 (void)x;
553 (void)y;
554 (void)n;
555 (void)nedges;
556
557 agerrorf("delaunay_tri: %s\n", err);
558 return 0;
559}
560surface_t*
561mkSurface (double *x, double *y, int n, int* segs, int nsegs)
562{
563 (void)x;
564 (void)y;
565 (void)n;
566 (void)segs;
567 (void)nsegs;
568
569 agerrorf("mkSurface: %s\n", err);
570 return 0;
571}
572void
574{
575 (void)s;
576
577 agerrorf("freeSurface: %s\n", err);
578}
579#endif
580
581static void remove_edge(v_data * graph, int source, int dest)
582{
583 int i;
584 for (i = 1; i < graph[source].nedges; i++) {
585 if (graph[source].edges[i] == dest) {
586 graph[source].edges[i] = graph[source].edges[--graph[source].nedges];
587 break;
588 }
589 }
590}
591
592v_data *UG_graph(double *x, double *y, int n) {
593 v_data *delaunay;
594 int i;
595 double dist_ij, dist_ik, dist_jk, x_i, y_i, x_j, y_j;
596 int j, k, neighbor_j, neighbor_k;
597
598 if (n == 2) {
599 int *edges = gv_calloc(4, sizeof(int));
600 delaunay = gv_calloc(n, sizeof(v_data));
601 delaunay[0].ewgts = NULL;
602 delaunay[0].edges = edges;
603 delaunay[0].nedges = 2;
604 delaunay[0].edges[0] = 0;
605 delaunay[0].edges[1] = 1;
606 delaunay[1].edges = edges + 2;
607 delaunay[1].ewgts = NULL;
608 delaunay[1].nedges = 2;
609 delaunay[1].edges[0] = 1;
610 delaunay[1].edges[1] = 0;
611 return delaunay;
612 } else if (n == 1) {
613 int *edges = gv_calloc(1, sizeof(int));
614 delaunay = gv_calloc(n, sizeof(v_data));
615 delaunay[0].ewgts = NULL;
616 delaunay[0].edges = edges;
617 delaunay[0].nedges = 1;
618 delaunay[0].edges[0] = 0;
619 return delaunay;
620 }
621
622 delaunay = delaunay_triangulation(x, y, n);
623
624 // remove all edges v-u if there is w, neighbor of u or v, that is closer to both u and v than dist(u,v)
625 for (i = 0; i < n; i++) {
626 x_i = x[i];
627 y_i = y[i];
628 for (j = 1; j < delaunay[i].nedges;) {
629 neighbor_j = delaunay[i].edges[j];
630 x_j = x[neighbor_j];
631 y_j = y[neighbor_j];
632 dist_ij = (x_j - x_i) * (x_j - x_i) + (y_j - y_i) * (y_j - y_i);
633 // now look at i'th neighbors to see whether there is a node in the "forbidden region"
634 // we will also go through neighbor_j's neighbors when we traverse the edge from its other side
635 bool removed = false;
636 for (k = 1; k < delaunay[i].nedges && !removed; k++) {
637 neighbor_k = delaunay[i].edges[k];
638 dist_ik = (x[neighbor_k] - x_i) * (x[neighbor_k] - x_i) +
639 (y[neighbor_k] - y_i) * (y[neighbor_k] - y_i);
640 if (dist_ik < dist_ij) {
641 dist_jk = (x[neighbor_k] - x_j) * (x[neighbor_k] - x_j) +
642 (y[neighbor_k] - y_j) * (y[neighbor_k] - y_j);
643 if (dist_jk < dist_ij) {
644 // remove the edge between i and neighbor j
645 delaunay[i].edges[j] = delaunay[i].edges[--delaunay[i].nedges];
646 remove_edge(delaunay, neighbor_j, i);
647 removed = true;
648 }
649 }
650 }
651 if (!removed) {
652 j++;
653 }
654 }
655 }
656 return delaunay;
657}
658
660{
661 if (graph != NULL) {
662 free(graph[0].edges);
663 free(graph[0].ewgts);
664 free(graph);
665 }
666}
667
669{
670 if (graph != NULL) {
671 free(graph[0].edges);
672 free(graph[0].ewgts);
673#ifdef DIGCOLA
674 free(graph[0].edists);
675#endif
676 free(graph);
677 }
678}
679
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
abstract graph C library, Cgraph API
int * get_triangles(double *x, int n, int *tris)
Definition delaunay.c:533
void freeGraph(v_data *graph)
Definition delaunay.c:659
v_data * UG_graph(double *x, double *y, int n)
Definition delaunay.c:592
void freeGraphData(vtx_data *graph)
Definition delaunay.c:668
static char * err
Definition delaunay.c:532
static v_data * delaunay_triangulation(double *x, double *y, int n)
Definition delaunay.c:542
surface_t * mkSurface(double *x, double *y, int n, int *segs, int nsegs)
Definition delaunay.c:561
static void remove_edge(v_data *graph, int source, int dest)
Definition delaunay.c:581
int * delaunay_tri(double *x, double *y, int n, int *nedges)
Definition delaunay.c:550
void freeSurface(surface_t *s)
Definition delaunay.c:573
void add_edge(edgelist *list, Agedge_t *e)
Definition edgelist.c:57
void free(void *)
edge
Definition gmlparse.y:244
node NULL
Definition grammar.y:181
void agerrorf(const char *fmt,...)
Definition agerror.c:165
Agraph_t * graph(char *name)
Definition gv.cpp:32
static void addEdge(edge_t *de, edge_t *e)
Definition layout.c:349
struct _tri tri
static tri * addTri(int i, int j, tri *oldp)
static int nedges
total no. of edges used in routing
Definition routespl.c:32
qsort with carried along context
static void gv_sort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
qsort with an extra state parameter, ala qsort_r
Definition sort.h:24
int nedges
Definition delaunay.h:17
int * edges
Definition delaunay.h:18
int * faces
Definition delaunay.h:20
int nfaces
Definition delaunay.h:19
int * neigh
Definition delaunay.h:21
int nedges
Definition sparsegraph.h:23
int * edges
Definition sparsegraph.h:24
float * ewgts
Definition sparsegraph.h:25
Definition grammar.c:90