Graphviz 16.1.1~dev.20261012.0359
Loading...
Searching...
No Matches
triang.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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <assert.h>
14#include <math.h>
15#include <pathplan/pathutil.h>
16#include <pathplan/tri.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <util/alloc.h>
21
22static int triangulate(Ppoint_t **pointp, size_t pointn,
23 void (*fn)(void *, const Ppoint_t *), void *vc);
24
25int ccw(Ppoint_t p1, Ppoint_t p2, Ppoint_t p3) {
26 double d = (p1.y - p2.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p1.x - p2.x);
27 return d > 0 ? ISCW : (d < 0 ? ISCCW : ISON);
28}
29
30static Ppoint_t point_indexer(void *base, size_t index) {
31 Ppoint_t **b = base;
32 return *b[index];
33}
34
35/* Ptriangulate:
36 * Return 0 on success; non-zero on error.
37 */
38int Ptriangulate(Ppoly_t *polygon, void (*fn)(void *, const Ppoint_t *),
39 void *vc) {
40 Ppoint_t **pointp;
41
42 const size_t pointn = polygon->pn;
43
44 pointp = gv_calloc(pointn, sizeof(Ppoint_t *));
45
46 for (size_t i = 0; i < pointn; i++)
47 pointp[i] = &(polygon->ps[i]);
48
49 assert(pointn >= 3);
50 if (triangulate(pointp, pointn, fn, vc) != 0) {
51 free(pointp);
52 return 1;
53 }
54
55 free(pointp);
56 return 0;
57}
58
59/* triangulate:
60 * Triangulates the given polygon.
61 * Returns non-zero if no diagonal exists.
62 */
63static int triangulate(Ppoint_t **pointp, size_t pointn,
64 void (*fn)(void *, const Ppoint_t *), void *vc) {
65 assert(pointn >= 3);
66 if (pointn > 3) {
67 for (size_t i = 0; i < pointn; i++) {
68 const size_t ip1 = (i + 1) % pointn;
69 const size_t ip2 = (i + 2) % pointn;
70 if (isdiagonal(i, pointp, pointn, point_indexer)) {
71 fn(vc, (Ppoint_t[]){*pointp[i], *pointp[ip1], *pointp[ip2]});
72 size_t j = 0;
73 for (i = 0; i < pointn; i++)
74 if (i != ip1)
75 pointp[j++] = pointp[i];
76 return triangulate(pointp, pointn - 1, fn, vc);
77 }
78 }
79 return -1;
80 } else {
81 fn(vc, (Ppoint_t[]){*pointp[0], *pointp[1], *pointp[2]});
82 }
83 return 0;
84}
85
87static bool between(Ppoint_t pa, Ppoint_t pb, Ppoint_t pc) {
88 const Ppoint_t pba = {.x = pb.x - pa.x, .y = pb.y - pa.y};
89 const Ppoint_t pca = {.x = pc.x - pa.x, .y = pc.y - pa.y};
90 if (ccw(pa, pb, pc) != ISON)
91 return false;
92 return pca.x * pba.x + pca.y * pba.y >= 0 &&
93 pca.x * pca.x + pca.y * pca.y <= pba.x * pba.x + pba.y * pba.y;
94}
95
97static bool intersects(Ppoint_t pa, Ppoint_t pb, Ppoint_t pc, Ppoint_t pd) {
98 int ccw1, ccw2, ccw3, ccw4;
99
100 if (ccw(pa, pb, pc) == ISON || ccw(pa, pb, pd) == ISON ||
101 ccw(pc, pd, pa) == ISON || ccw(pc, pd, pb) == ISON) {
102 if (between(pa, pb, pc) || between(pa, pb, pd) || between(pc, pd, pa) ||
103 between(pc, pd, pb))
104 return true;
105 } else {
106 ccw1 = ccw(pa, pb, pc) == ISCCW ? 1 : 0;
107 ccw2 = ccw(pa, pb, pd) == ISCCW ? 1 : 0;
108 ccw3 = ccw(pc, pd, pa) == ISCCW ? 1 : 0;
109 ccw4 = ccw(pc, pd, pb) == ISCCW ? 1 : 0;
110 return (ccw1 ^ ccw2) && (ccw3 ^ ccw4);
111 }
112 return false;
113}
114
115bool isdiagonal(size_t i, void *pointp, size_t pointn, indexer_t indexer) {
116 int res;
117
118 /* neighborhood test */
119 const size_t ip1 = (i + 1) % pointn;
120 const size_t ip2 = (i + 2) % pointn;
121 const size_t im1 = (i + pointn - 1) % pointn;
122 /* If P[i] is a convex vertex [ i+1 left of (i-1,i) ]. */
123 if (ccw(indexer(pointp, im1), indexer(pointp, i), indexer(pointp, ip1)) ==
124 ISCCW)
125 res = ccw(indexer(pointp, i), indexer(pointp, ip2), indexer(pointp, im1)) ==
126 ISCCW &&
127 ccw(indexer(pointp, ip2), indexer(pointp, i), indexer(pointp, ip1)) ==
128 ISCCW;
129 /* Assume (i - 1, i, i + 1) not collinear. */
130 else
131 res = ccw(indexer(pointp, i), indexer(pointp, ip2), indexer(pointp, ip1)) ==
132 ISCW;
133 if (!res) {
134 return false;
135 }
136
137 /* check against all other edges */
138 for (size_t j = 0; j < pointn; j++) {
139 const size_t jp1 = (j + 1) % pointn;
140 if (!(j == i || jp1 == i || j == ip2 || jp1 == ip2))
141 if (intersects(indexer(pointp, i), indexer(pointp, ip2),
142 indexer(pointp, j), indexer(pointp, jp1))) {
143 return false;
144 }
145 }
146 return true;
147}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
void free(void *)
double x
Definition pathgeom.h:38
double y
Definition pathgeom.h:38
@ ISON
co-linear
Definition tri.h:43
@ ISCW
clockwise
Definition tri.h:42
@ ISCCW
counter-clockwise
Definition tri.h:41
Ppoint_t(* indexer_t)(void *base, size_t index)
Definition tri.h:49
static bool between(Ppoint_t pa, Ppoint_t pb, Ppoint_t pc)
is pb between pa and pc?
Definition triang.c:87
static Ppoint_t point_indexer(void *base, size_t index)
Definition triang.c:30
int ccw(Ppoint_t p1, Ppoint_t p2, Ppoint_t p3)
are the given points counter-clockwise, clockwise, or co-linear?
Definition triang.c:25
bool isdiagonal(size_t i, void *pointp, size_t pointn, indexer_t indexer)
is (i, i + 2) a diagonal?
Definition triang.c:115
static int triangulate(Ppoint_t **pointp, size_t pointn, void(*fn)(void *, const Ppoint_t *), void *vc)
Definition triang.c:63
static bool intersects(Ppoint_t pa, Ppoint_t pb, Ppoint_t pc, Ppoint_t pd)
line to line intersection
Definition triang.c:97
int Ptriangulate(Ppoly_t *polygon, void(*fn)(void *, const Ppoint_t *), void *vc)
Definition triang.c:38