/* * Copyright (c) 2008-2011 Hypertriton, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Polygon routines. */ #include #include void M_PolygonInit(M_Polygon *P) { P->v = NULL; P->n = 0; } void M_PolygonFree(M_Polygon *P) { Free(P->v); P->v = NULL; P->n = 0; } M_Polygon M_PolygonRead(AG_DataSource *ds) { M_Polygon P; Uint i; P.n = (Uint)AG_ReadUint32(ds); P.v = Malloc(P.n*sizeof(M_Vector2)); for (i = 0; i < P.n; i++) { P.v[i] = M_ReadVector2(ds); } P._pad = 0; return (P); } void M_PolygonWrite(AG_DataSource *ds, const M_Polygon *P) { Uint i; AG_WriteUint32(ds, (Uint32)P->n); for (i = 0; i < P->n; i++) M_WriteVector2(ds, &P->v[i]); } /* Duplicate a polygon structure. */ int M_PolygonCopy(M_Polygon *D, const M_Polygon *S) { if ((D->v = TryMalloc(S->n*sizeof(M_Vector2))) == NULL) { return (-1); } D->n = S->n; memcpy(D->v, S->v, S->n*sizeof(M_Vector2)); return (0); } /* Scale the vertices of a polygon. */ void M_PolygonScale(M_Polygon *P, M_Real xs, M_Real ys) { Uint i; for (i = 0; i < P->n; i++) { P->v[i].x = xs*P->v[i].x; P->v[i].y = ys*P->v[i].y; } } /* Offset the vertices of a polygon. */ void M_PolygonOffset(M_Polygon *P, M_Real xo, M_Real yo) { Uint i; for (i = 0; i < P->n; i++) { P->v[i].x += xo; P->v[i].y += yo; } } /* Create a new polygon from an array of vectors. */ M_Polygon M_PolygonFromPts(Uint n, const M_Vector2 *v) { M_Polygon P; P.v = Malloc(n*sizeof(M_Vector2)); P.n = n; memcpy(P.v, v, n*sizeof(M_Vector2)); P._pad = 0; return (P); } /* Create a new polygon from a M_PointSet2. */ M_Polygon M_PolygonFromPointSet2(const M_PointSet2 *ps) { M_Polygon P; P.v = Malloc(ps->n*sizeof(M_Vector2)); P.n = ps->n; memcpy(P.v, ps->p, ps->n*sizeof(M_Vector2)); P._pad = 0; return (P); } /* Create a new polygon from a M_PointSet2i. */ M_Polygon M_PolygonFromPointSet2i(const M_PointSet2i *ps) { M_Polygon P; Uint i; P.v = Malloc(ps->n*sizeof(M_Vector2)); P.n = ps->n; for (i = 0; i < ps->n; i++) { P.v[i].x = ((M_Real)ps->x[i])/ps->w; P.v[i].y = ((M_Real)ps->y[i])/ps->h; } P._pad = 0; return (P); } /* Convert polygon to M_PointSet2 */ M_PointSet2 M_PolygonToPointSet2(const M_Polygon *P) { M_PointSet2 ps; M_PointSetInit2(&ps); M_PointSetAlloc2(&ps, P->n); memcpy(ps.p, P->v, P->n*sizeof(M_Vector2)); return (ps); } /* Convert polygon to a M_PointSet2i of specified real dimensions. */ M_PointSet2i M_PolygonToPointSet2i(const M_Polygon *P, M_Real w, M_Real h) { M_PointSet2i ps; Uint i; M_PointSetInit2i(&ps, w, h); M_PointSetAlloc2i(&ps, P->n); for (i = 0; i < P->n; i++) { ps.x[i] = (int)(P->v[i].x*w); ps.y[i] = (int)(P->v[i].y*h); } return (ps); } /* * Create a new polygon from a set of lines. * XXX TODO sanity check point order */ M_Polygon M_PolygonFromLines(Uint n, const M_Line2 *ln) { M_Polygon P; Uint i; P.v = Malloc(n*sizeof(M_Vector2)); P.n = n; for (i = 0; i < n; i++) { P.v[i] = ln[i].p; } P._pad = 0; return (P); } /* * Append a line segment to a polygon. * Return segment index on success, -1 on failure. * XXX TODO sanity check point order */ int M_PolygonAddLine(M_Polygon *P, M_Line2 L) { M_Vector2 *vNew; if ((vNew = TryRealloc(P->v, (P->n+1)*sizeof(M_Vector2))) == NULL) { return (-1); } P->v = vNew; P->v[P->n] = L.p; return (P->n++); } /* * Add a vertex to a polygon. * Return vertex index on success, -1 on failure. */ int M_PolygonAddVertex(M_Polygon *_Nonnull P, M_Vector2 v) { M_Vector2 *vNew; if ((vNew = TryRealloc(P->v, (P->n+1)*sizeof(M_Vector2))) == NULL) { return (-1); } P->v = vNew; P->v[P->n] = v; return (P->n++); } /* Remove a vertex from a polygon. */ int M_PolygonDelVertex(M_Polygon *P, int i) { if (i < 0 || i >= P->n) { AG_SetError("Bad vertex"); return (-1); } if (i < P->n - 1) { memmove(&P->v[i], &P->v[i+1], (P->n - i - 1)*sizeof(M_Vector2)); } P->n--; return (0); } /* Test whether point p lies inside the polygon. */ int M_PointInPolygon(const M_Polygon *P, M_Vector2 p) { int i, count = 0; M_Real ix; M_Vector2 p1, p2; if (P->n < 3) return (0); p1 = P->v[0]; for (i = 1; i <= P->n; i++) { p2 = P->v[i % P->n]; if (p.y > MIN(p1.y, p2.y) && p.y <= MAX(p1.y, p2.y) && p.x <= MAX(p1.x, p2.x)) { if (p1.y != p2.y) { /* * Compute the intersection of the X axis * with this line segment. */ ix = (p.y - p1.y)*(p2.x - p1.x) / (p2.y - p1.y) + p1.x; if (p1.x == p2.x || p.x <= ix) count++; } } p1 = p2; } if (count%2 == 0) { return (0); } else { return (1); } } /* * Test whether the polygon is convex. Polygon must be non self-intersecting. * Return 1 if the polygon is convex or 0 if the polygon is concave. */ int M_PolygonIsConvex(const M_Polygon *P) { int i, flag = 0; if (P->n < 3) { AG_SetError("<3 vertices"); return (-1); } for (i = 0; i < P->n; i++) { M_Vector2 pi = P->v[(i) % P->n]; M_Vector2 pj = P->v[(i+1) % P->n]; M_Vector2 pk = P->v[(i+2) % P->n]; M_Vector2 pji = M_VecSub2p(&pj, &pi); M_Vector2 pkj = M_VecSub2p(&pk, &pj); M_Real dot; dot = M_VecPerpDot2p(&pji, &pkj); if (dot < 0.0) { flag |= 1; } else if (dot >= 0.0) { flag |= 2; } if (flag == 3) { return (0); } } if (flag != 0) { return (1); } return (0); }