| 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 | |