| 1 | /* $Id$ $Revision$ */ | 
|---|
| 2 | /* vim:set shiftwidth=4 ts=8: */ | 
|---|
| 3 |  | 
|---|
| 4 | /************************************************************************* | 
|---|
| 5 | * Copyright (c) 2011 AT&T Intellectual Property | 
|---|
| 6 | * All rights reserved. This program and the accompanying materials | 
|---|
| 7 | * are made available under the terms of the Eclipse Public License v1.0 | 
|---|
| 8 | * which accompanies this distribution, and is available at | 
|---|
| 9 | * http://www.eclipse.org/legal/epl-v10.html | 
|---|
| 10 | * | 
|---|
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ | 
|---|
| 12 | *************************************************************************/ | 
|---|
| 13 |  | 
|---|
| 14 |  | 
|---|
| 15 | #include <stdio.h> | 
|---|
| 16 | #include <math.h> | 
|---|
| 17 | #include <stdlib.h> | 
|---|
| 18 | #include <setjmp.h> | 
|---|
| 19 | #include "pathutil.h" | 
|---|
| 20 | #include "tri.h" | 
|---|
| 21 |  | 
|---|
| 22 | #ifdef DMALLOC | 
|---|
| 23 | #include "dmalloc.h" | 
|---|
| 24 | #endif | 
|---|
| 25 |  | 
|---|
| 26 | typedef struct lvertex_2_t { | 
|---|
| 27 | double x, y; | 
|---|
| 28 | } lvertex_2_t; | 
|---|
| 29 |  | 
|---|
| 30 | typedef struct dpd_triangle { | 
|---|
| 31 | Ppoint_t *v[3]; | 
|---|
| 32 | } ltriangle_t; | 
|---|
| 33 |  | 
|---|
| 34 | #define ISCCW 1 | 
|---|
| 35 | #define ISCW  2 | 
|---|
| 36 | #define ISON  3 | 
|---|
| 37 |  | 
|---|
| 38 | #ifndef TRUE | 
|---|
| 39 | #define TRUE 1 | 
|---|
| 40 | #define FALSE 0 | 
|---|
| 41 | #endif | 
|---|
| 42 |  | 
|---|
| 43 | static jmp_buf jbuf; | 
|---|
| 44 | static int dpd_ccw(Ppoint_t *, Ppoint_t *, Ppoint_t *); | 
|---|
| 45 | static int dpd_isdiagonal(int, int, Ppoint_t **, int); | 
|---|
| 46 | static int dpd_intersects(Ppoint_t *, Ppoint_t *, Ppoint_t *, Ppoint_t *); | 
|---|
| 47 | static int dpd_between(Ppoint_t *, Ppoint_t *, Ppoint_t *); | 
|---|
| 48 | static void triangulate(Ppoint_t ** pointp, int pointn, | 
|---|
| 49 | void (*fn) (void *, Ppoint_t *), void *vc); | 
|---|
| 50 |  | 
|---|
| 51 | static int dpd_ccw(Ppoint_t * p1, Ppoint_t * p2, Ppoint_t * p3) | 
|---|
| 52 | { | 
|---|
| 53 | double d = | 
|---|
| 54 | ((p1->y - p2->y) * (p3->x - p2->x)) - | 
|---|
| 55 | ((p3->y - p2->y) * (p1->x - p2->x)); | 
|---|
| 56 | return (d > 0) ? ISCW : ((d < 0) ? ISCCW : ISON); | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | /* Ptriangulate: | 
|---|
| 60 | * Return 0 on success; non-zero on error. | 
|---|
| 61 | */ | 
|---|
| 62 | int Ptriangulate(Ppoly_t * polygon, void (*fn) (void *, Ppoint_t *), | 
|---|
| 63 | void *vc) | 
|---|
| 64 | { | 
|---|
| 65 | int i; | 
|---|
| 66 | int pointn; | 
|---|
| 67 | Ppoint_t **pointp; | 
|---|
| 68 |  | 
|---|
| 69 | pointn = polygon->pn; | 
|---|
| 70 |  | 
|---|
| 71 | pointp = (Ppoint_t **) malloc(pointn * sizeof(Ppoint_t *)); | 
|---|
| 72 |  | 
|---|
| 73 | for (i = 0; i < pointn; i++) | 
|---|
| 74 | pointp[i] = &(polygon->ps[i]); | 
|---|
| 75 |  | 
|---|
| 76 | if (setjmp(jbuf)) { | 
|---|
| 77 | free(pointp); | 
|---|
| 78 | return 1; | 
|---|
| 79 | } | 
|---|
| 80 | triangulate(pointp, pointn, fn, vc); | 
|---|
| 81 |  | 
|---|
| 82 | free(pointp); | 
|---|
| 83 | return 0; | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | /* triangulate: | 
|---|
| 87 | * Triangulates the given polygon. | 
|---|
| 88 | * Throws an exception if no diagonal exists. | 
|---|
| 89 | */ | 
|---|
| 90 | static void | 
|---|
| 91 | triangulate(Ppoint_t ** pointp, int pointn, | 
|---|
| 92 | void (*fn) (void *, Ppoint_t *), void *vc) | 
|---|
| 93 | { | 
|---|
| 94 | int i, ip1, ip2, j; | 
|---|
| 95 | Ppoint_t A[3]; | 
|---|
| 96 | if (pointn > 3) { | 
|---|
| 97 | for (i = 0; i < pointn; i++) { | 
|---|
| 98 | ip1 = (i + 1) % pointn; | 
|---|
| 99 | ip2 = (i + 2) % pointn; | 
|---|
| 100 | if (dpd_isdiagonal(i, ip2, pointp, pointn)) { | 
|---|
| 101 | A[0] = *pointp[i]; | 
|---|
| 102 | A[1] = *pointp[ip1]; | 
|---|
| 103 | A[2] = *pointp[ip2]; | 
|---|
| 104 | fn(vc, A); | 
|---|
| 105 | j = 0; | 
|---|
| 106 | for (i = 0; i < pointn; i++) | 
|---|
| 107 | if (i != ip1) | 
|---|
| 108 | pointp[j++] = pointp[i]; | 
|---|
| 109 | triangulate(pointp, pointn - 1, fn, vc); | 
|---|
| 110 | return; | 
|---|
| 111 | } | 
|---|
| 112 | } | 
|---|
| 113 | longjmp(jbuf,1); | 
|---|
| 114 | } else { | 
|---|
| 115 | A[0] = *pointp[0]; | 
|---|
| 116 | A[1] = *pointp[1]; | 
|---|
| 117 | A[2] = *pointp[2]; | 
|---|
| 118 | fn(vc, A); | 
|---|
| 119 | } | 
|---|
| 120 | } | 
|---|
| 121 |  | 
|---|
| 122 | /* check if (i, i + 2) is a diagonal */ | 
|---|
| 123 | static int dpd_isdiagonal(int i, int ip2, Ppoint_t ** pointp, int pointn) | 
|---|
| 124 | { | 
|---|
| 125 | int ip1, im1, j, jp1, res; | 
|---|
| 126 |  | 
|---|
| 127 | /* neighborhood test */ | 
|---|
| 128 | ip1 = (i + 1) % pointn; | 
|---|
| 129 | im1 = (i + pointn - 1) % pointn; | 
|---|
| 130 | /* If P[i] is a convex vertex [ i+1 left of (i-1,i) ]. */ | 
|---|
| 131 | if (dpd_ccw(pointp[im1], pointp[i], pointp[ip1]) == ISCCW) | 
|---|
| 132 | res = (dpd_ccw(pointp[i], pointp[ip2], pointp[im1]) == ISCCW) && | 
|---|
| 133 | (dpd_ccw(pointp[ip2], pointp[i], pointp[ip1]) == ISCCW); | 
|---|
| 134 | /* Assume (i - 1, i, i + 1) not collinear. */ | 
|---|
| 135 | else | 
|---|
| 136 | res = ((dpd_ccw(pointp[i], pointp[ip2], pointp[ip1]) == ISCW) | 
|---|
| 137 | ); | 
|---|
| 138 | /* | 
|---|
| 139 | && | 
|---|
| 140 | (dpd_ccw (pointp[ip2], pointp[i], pointp[im1]) != ISCW)); | 
|---|
| 141 | */ | 
|---|
| 142 | if (!res) { | 
|---|
| 143 | return FALSE; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | /* check against all other edges */ | 
|---|
| 147 | for (j = 0; j < pointn; j++) { | 
|---|
| 148 | jp1 = (j + 1) % pointn; | 
|---|
| 149 | if (!((j == i) || (jp1 == i) || (j == ip2) || (jp1 == ip2))) | 
|---|
| 150 | if (dpd_intersects | 
|---|
| 151 | (pointp[i], pointp[ip2], pointp[j], pointp[jp1])) { | 
|---|
| 152 | return FALSE; | 
|---|
| 153 | } | 
|---|
| 154 | } | 
|---|
| 155 | return TRUE; | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | /* line to line intersection */ | 
|---|
| 159 | static int dpd_intersects(Ppoint_t * pa, Ppoint_t * pb, Ppoint_t * pc, | 
|---|
| 160 | Ppoint_t * pd) | 
|---|
| 161 | { | 
|---|
| 162 | int ccw1, ccw2, ccw3, ccw4; | 
|---|
| 163 |  | 
|---|
| 164 | if (dpd_ccw(pa, pb, pc) == ISON || dpd_ccw(pa, pb, pd) == ISON || | 
|---|
| 165 | dpd_ccw(pc, pd, pa) == ISON || dpd_ccw(pc, pd, pb) == ISON) { | 
|---|
| 166 | if (dpd_between(pa, pb, pc) || dpd_between(pa, pb, pd) || | 
|---|
| 167 | dpd_between(pc, pd, pa) || dpd_between(pc, pd, pb)) | 
|---|
| 168 | return TRUE; | 
|---|
| 169 | } else { | 
|---|
| 170 | ccw1 = (dpd_ccw(pa, pb, pc) == ISCCW) ? 1 : 0; | 
|---|
| 171 | ccw2 = (dpd_ccw(pa, pb, pd) == ISCCW) ? 1 : 0; | 
|---|
| 172 | ccw3 = (dpd_ccw(pc, pd, pa) == ISCCW) ? 1 : 0; | 
|---|
| 173 | ccw4 = (dpd_ccw(pc, pd, pb) == ISCCW) ? 1 : 0; | 
|---|
| 174 | return (ccw1 ^ ccw2) && (ccw3 ^ ccw4); | 
|---|
| 175 | } | 
|---|
| 176 | return FALSE; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | static int dpd_between(Ppoint_t * pa, Ppoint_t * pb, Ppoint_t * pc) | 
|---|
| 180 | { | 
|---|
| 181 | Ppoint_t pba, pca; | 
|---|
| 182 |  | 
|---|
| 183 | pba.x = pb->x - pa->x, pba.y = pb->y - pa->y; | 
|---|
| 184 | pca.x = pc->x - pa->x, pca.y = pc->y - pa->y; | 
|---|
| 185 | if (dpd_ccw(pa, pb, pc) != ISON) | 
|---|
| 186 | return FALSE; | 
|---|
| 187 | return (pca.x * pba.x + pca.y * pba.y >= 0) && | 
|---|
| 188 | (pca.x * pca.x + pca.y * pca.y <= pba.x * pba.x + pba.y * pba.y); | 
|---|
| 189 | } | 
|---|
| 190 |  | 
|---|