static void gts_triangulate_test_stuff() { gdouble v[4][3] = { {0.0, 0.0, 0.0}, {0.0, 3.0, 0.0}, {3.0, 3.0, 0.0}, {1.0, 1.0, 0.0} }; int i; GtsVertex *vtx[4]; GCList *polygon = NULL; GtsVector orient; GCList *p; for (i = 0; i < 4; ++i) { vtx[i] = gts_vertex_new(gts_vertex_class(), v[i][0],v[i][1],v[i][2]); polygon = g_clist_append(polygon, vtx[i]); } g_assert(g_clist_length(polygon)); g_assert(gts_polygon_orientation(polygon, orient)); g_assert(orient[0] == 0 && orient[1] == 0 && orient[2] < 0); p = g_clist_next(polygon); g_assert(is_convex(GTS_POINT(p->prev->data), GTS_POINT(p->data),GTS_POINT(p->next->data), orient)); g_assert(is_inside(GTS_POINT(vtx[0]), GTS_POINT(vtx[1]), GTS_POINT(vtx[2]), GTS_POINT(vtx[3]))); g_assert(!is_inside(GTS_POINT(vtx[0]), GTS_POINT(vtx[1]), GTS_POINT(vtx[3]), GTS_POINT(vtx[2]))); g_clist_free(p); for (i = 0; i < 4; ++i) gts_object_destroy(GTS_OBJECT(vtx[i])); }
AirspacePolygon::AirspacePolygon(const std::vector<GeoPoint>& pts, const bool prune) :AbstractAirspace(POLYGON) { if (pts.size()<2) { m_is_convex = true; } else { TaskProjection task_projection; // note dummy blank projection for (std::vector<GeoPoint>::const_iterator v = pts.begin(); v != pts.end(); ++v) { m_border.push_back(SearchPoint(*v, task_projection)); } // ensure airspace is closed GeoPoint p_start = pts[0]; GeoPoint p_end = *(pts.end()-1); if (p_start != p_end) { m_border.push_back(SearchPoint(p_start, task_projection)); } if (prune) { // only for testing prune_interior(m_border); m_is_convex = true; } else { m_is_convex = is_convex(m_border); } } }
static gboolean add_ear(GtsSurface *s, GtsEdgePool *pool, GCList *p, GCList *polygon, GtsVector orientation) { GtsPoint *v1, *v2, *v3; GCList *i; // ears are convex GCList *pp = p->prev; GCList *pn = p->next; v1 = GTS_POINT(pp->data); v2 = GTS_POINT(p->data); v3 = GTS_POINT(pn->data); GTS_IS_POINT(v1); GTS_IS_POINT(v2); GTS_IS_POINT(v3); if ( !is_convex(v1, v2, v3, orientation) ) { g_debug("concave face (%6.2f %6.2f %6.2f) (%6.2f %6.2f %6.2f) (%6.2f %6.2f %6.2f)", v1->x, v1->y, v1->z, v2->x, v2->y, v2->z, v3->x, v3->y, v3->z); return FALSE; } i = polygon; do { GtsPoint *p1 = GTS_POINT(i->data); if (p1 != v1 && p1 != v2 && p1 != v3) { GtsPoint *p0 = GTS_POINT(i->prev->data); GtsPoint *p2 = GTS_POINT(i->next->data); if (!is_convex(p0, p1, p2, orientation) && is_inside(v1, v2, v3, p1)) { g_debug("reject face\n"); return FALSE; } } i = g_clist_next(i); } while (i != polygon); add_face_from_polygon_corner(s, pool, GTS_VERTEX(v1), GTS_VERTEX(v2), GTS_VERTEX(v3)); return TRUE; }
// The objective here is to inset all of the edges by the given distance, and then // remove any invalid inset edges by detecting right-hand turns. In a ccw polygon, // we should only be making left-hand turns (for cw polygons, we use the winding // parameter to reverse this). We detect this by checking whether the second intersection // on an edge is closer to its tail than the first one. // // We might also have the case that there is no intersection between two neighboring inset edges. // In this case, one edge will lie to the right of the other and should be discarded along with // its previous intersection (if any). // // Note: the assumption is that inputPolygon is convex and has no coincident points. // bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize, std::function<SkScalar(int index)> insetDistanceFunc, SkTDArray<SkPoint>* insetPolygon) { if (inputPolygonSize < 3) { return false; } int winding = get_winding(inputPolygonVerts, inputPolygonSize); if (0 == winding) { return false; } // set up struct EdgeData { InsetSegment fInset; SkPoint fIntersection; SkScalar fTValue; bool fValid; }; SkAutoSTMalloc<64, EdgeData> edgeData(inputPolygonSize); for (int i = 0; i < inputPolygonSize; ++i) { int j = (i + 1) % inputPolygonSize; SkOffsetSegment(inputPolygonVerts[i], inputPolygonVerts[j], insetDistanceFunc(i), insetDistanceFunc(j), winding, &edgeData[i].fInset.fP0, &edgeData[i].fInset.fP1); edgeData[i].fIntersection = edgeData[i].fInset.fP0; edgeData[i].fTValue = SK_ScalarMin; edgeData[i].fValid = true; } int prevIndex = inputPolygonSize - 1; int currIndex = 0; int insetVertexCount = inputPolygonSize; while (prevIndex != currIndex) { if (!edgeData[prevIndex].fValid) { prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize; continue; } SkScalar s, t; SkPoint intersection; if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset, &intersection, &s, &t)) { // if new intersection is further back on previous inset from the prior intersection if (s < edgeData[prevIndex].fTValue) { // no point in considering this one again edgeData[prevIndex].fValid = false; --insetVertexCount; // go back one segment prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize; // we've already considered this intersection, we're done } else if (edgeData[currIndex].fTValue > SK_ScalarMin && intersection.equalsWithinTolerance(edgeData[currIndex].fIntersection, 1.0e-6f)) { break; } else { // add intersection edgeData[currIndex].fIntersection = intersection; edgeData[currIndex].fTValue = t; // go to next segment prevIndex = currIndex; currIndex = (currIndex + 1) % inputPolygonSize; } } else { // if prev to right side of curr int side = winding*compute_side(edgeData[currIndex].fInset.fP0, edgeData[currIndex].fInset.fP1, edgeData[prevIndex].fInset.fP1); if (side < 0 && side == winding*compute_side(edgeData[currIndex].fInset.fP0, edgeData[currIndex].fInset.fP1, edgeData[prevIndex].fInset.fP0)) { // no point in considering this one again edgeData[prevIndex].fValid = false; --insetVertexCount; // go back one segment prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize; } else { // move to next segment edgeData[currIndex].fValid = false; --insetVertexCount; currIndex = (currIndex + 1) % inputPolygonSize; } } } // store all the valid intersections that aren't nearly coincident // TODO: look at the main algorithm and see if we can detect these better static constexpr SkScalar kCleanupTolerance = 0.01f; insetPolygon->reset(); insetPolygon->setReserve(insetVertexCount); currIndex = -1; for (int i = 0; i < inputPolygonSize; ++i) { if (edgeData[i].fValid && (currIndex == -1 || !edgeData[i].fIntersection.equalsWithinTolerance((*insetPolygon)[currIndex], kCleanupTolerance))) { *insetPolygon->push() = edgeData[i].fIntersection; currIndex++; } } // make sure the first and last points aren't coincident if (currIndex >= 1 && (*insetPolygon)[0].equalsWithinTolerance((*insetPolygon)[currIndex], kCleanupTolerance)) { insetPolygon->pop(); } SkASSERT(is_convex(*insetPolygon)); return (insetPolygon->count() >= 3); }