Graphviz  2.31.20130525.0447
lib/pathplan/triang.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include <stdio.h>
00016 #include <math.h>
00017 #include <stdlib.h>
00018 #include <setjmp.h>
00019 #include "pathutil.h"
00020 #include "tri.h"
00021 
00022 #ifdef DMALLOC
00023 #include "dmalloc.h"
00024 #endif
00025 
00026 typedef struct lvertex_2_t {
00027     double x, y;
00028 } lvertex_2_t;
00029 
00030 typedef struct dpd_triangle {
00031     Ppoint_t *v[3];
00032 } ltriangle_t;
00033 
00034 #define ISCCW 1
00035 #define ISCW  2
00036 #define ISON  3
00037 
00038 #ifndef TRUE
00039 #define TRUE 1
00040 #define FALSE 0
00041 #endif
00042 
00043 static jmp_buf jbuf;
00044 static int dpd_ccw(Ppoint_t *, Ppoint_t *, Ppoint_t *);
00045 static int dpd_isdiagonal(int, int, Ppoint_t **, int);
00046 static int dpd_intersects(Ppoint_t *, Ppoint_t *, Ppoint_t *, Ppoint_t *);
00047 static int dpd_between(Ppoint_t *, Ppoint_t *, Ppoint_t *);
00048 static void triangulate(Ppoint_t ** pointp, int pointn,
00049                         void (*fn) (void *, Ppoint_t *), void *vc);
00050 
00051 static int dpd_ccw(Ppoint_t * p1, Ppoint_t * p2, Ppoint_t * p3)
00052 {
00053     double d =
00054         ((p1->y - p2->y) * (p3->x - p2->x)) -
00055         ((p3->y - p2->y) * (p1->x - p2->x));
00056     return (d > 0) ? ISCW : ((d < 0) ? ISCCW : ISON);
00057 }
00058 
00059 /* Ptriangulate:
00060  * Return 0 on success; non-zero on error.
00061  */
00062 int Ptriangulate(Ppoly_t * polygon, void (*fn) (void *, Ppoint_t *),
00063                   void *vc)
00064 {
00065     int i;
00066     int pointn;
00067     Ppoint_t **pointp;
00068 
00069     pointn = polygon->pn;
00070 
00071     pointp = (Ppoint_t **) malloc(pointn * sizeof(Ppoint_t *));
00072 
00073     for (i = 0; i < pointn; i++)
00074         pointp[i] = &(polygon->ps[i]);
00075 
00076     if (setjmp(jbuf)) {
00077         free(pointp);
00078         return 1;
00079     }
00080     triangulate(pointp, pointn, fn, vc);
00081 
00082     free(pointp);
00083     return 0;
00084 }
00085 
00086 /* triangulate:
00087  * Triangulates the given polygon. 
00088  * Throws an exception if no diagonal exists.
00089  */
00090 static void
00091 triangulate(Ppoint_t ** pointp, int pointn,
00092             void (*fn) (void *, Ppoint_t *), void *vc)
00093 {
00094     int i, ip1, ip2, j;
00095     Ppoint_t A[3];
00096     if (pointn > 3) {
00097         for (i = 0; i < pointn; i++) {
00098             ip1 = (i + 1) % pointn;
00099             ip2 = (i + 2) % pointn;
00100             if (dpd_isdiagonal(i, ip2, pointp, pointn)) {
00101                 A[0] = *pointp[i];
00102                 A[1] = *pointp[ip1];
00103                 A[2] = *pointp[ip2];
00104                 fn(vc, A);
00105                 j = 0;
00106                 for (i = 0; i < pointn; i++)
00107                     if (i != ip1)
00108                         pointp[j++] = pointp[i];
00109                 triangulate(pointp, pointn - 1, fn, vc);
00110                 return;
00111             }
00112         }
00113         longjmp(jbuf,1);
00114     } else {
00115         A[0] = *pointp[0];
00116         A[1] = *pointp[1];
00117         A[2] = *pointp[2];
00118         fn(vc, A);
00119     }
00120 }
00121 
00122 /* check if (i, i + 2) is a diagonal */
00123 static int dpd_isdiagonal(int i, int ip2, Ppoint_t ** pointp, int pointn)
00124 {
00125     int ip1, im1, j, jp1, res;
00126 
00127     /* neighborhood test */
00128     ip1 = (i + 1) % pointn;
00129     im1 = (i + pointn - 1) % pointn;
00130     /* If P[i] is a convex vertex [ i+1 left of (i-1,i) ]. */
00131     if (dpd_ccw(pointp[im1], pointp[i], pointp[ip1]) == ISCCW)
00132         res = (dpd_ccw(pointp[i], pointp[ip2], pointp[im1]) == ISCCW) &&
00133             (dpd_ccw(pointp[ip2], pointp[i], pointp[ip1]) == ISCCW);
00134     /* Assume (i - 1, i, i + 1) not collinear. */
00135     else
00136         res = ((dpd_ccw(pointp[i], pointp[ip2], pointp[ip1]) == ISCW)
00137             );
00138 /*
00139                 &&
00140                 (dpd_ccw (pointp[ip2], pointp[i], pointp[im1]) != ISCW));
00141 */
00142     if (!res) {
00143         return FALSE;
00144     }
00145 
00146     /* check against all other edges */
00147     for (j = 0; j < pointn; j++) {
00148         jp1 = (j + 1) % pointn;
00149         if (!((j == i) || (jp1 == i) || (j == ip2) || (jp1 == ip2)))
00150             if (dpd_intersects
00151                 (pointp[i], pointp[ip2], pointp[j], pointp[jp1])) {
00152                 return FALSE;
00153             }
00154     }
00155     return TRUE;
00156 }
00157 
00158 /* line to line intersection */
00159 static int dpd_intersects(Ppoint_t * pa, Ppoint_t * pb, Ppoint_t * pc,
00160                           Ppoint_t * pd)
00161 {
00162     int ccw1, ccw2, ccw3, ccw4;
00163 
00164     if (dpd_ccw(pa, pb, pc) == ISON || dpd_ccw(pa, pb, pd) == ISON ||
00165         dpd_ccw(pc, pd, pa) == ISON || dpd_ccw(pc, pd, pb) == ISON) {
00166         if (dpd_between(pa, pb, pc) || dpd_between(pa, pb, pd) ||
00167             dpd_between(pc, pd, pa) || dpd_between(pc, pd, pb))
00168             return TRUE;
00169     } else {
00170         ccw1 = (dpd_ccw(pa, pb, pc) == ISCCW) ? 1 : 0;
00171         ccw2 = (dpd_ccw(pa, pb, pd) == ISCCW) ? 1 : 0;
00172         ccw3 = (dpd_ccw(pc, pd, pa) == ISCCW) ? 1 : 0;
00173         ccw4 = (dpd_ccw(pc, pd, pb) == ISCCW) ? 1 : 0;
00174         return (ccw1 ^ ccw2) && (ccw3 ^ ccw4);
00175     }
00176     return FALSE;
00177 }
00178 
00179 static int dpd_between(Ppoint_t * pa, Ppoint_t * pb, Ppoint_t * pc)
00180 {
00181     Ppoint_t pba, pca;
00182 
00183     pba.x = pb->x - pa->x, pba.y = pb->y - pa->y;
00184     pca.x = pc->x - pa->x, pca.y = pc->y - pa->y;
00185     if (dpd_ccw(pa, pb, pc) != ISON)
00186         return FALSE;
00187     return (pca.x * pba.x + pca.y * pba.y >= 0) &&
00188         (pca.x * pca.x + pca.y * pca.y <= pba.x * pba.x + pba.y * pba.y);
00189 }