/* Copyright (c) 2013 yvt Triangulates arbitary polygons represented by cells. Algorithm's time complexity is roughly O(W*H). Created to fix #213 ( https://github.com/yvt/openspades/issues/213 ). See this web page for algorithm demonstration: https://dl.dropboxusercontent.com/u/37804131/triangulate-2.html BSD license. */ #include #include #include #include #include namespace c2t { struct Point { int x, y; Point() = default; Point(int x, int y) : x(x), y(y) {} }; // structures used internally struct SpanRange { int x1, x2; SpanRange() = default; SpanRange(int x1, int x2) : x1(x1), x2(x2) {} inline static SpanRange CreateInvalid() { return SpanRange(-10, -10); } inline bool IsValid() { return x1 != -10; } }; struct Edge { std::vector points; }; struct Span { int x1, x2; std::vector xs; int y; std::shared_ptr leftEdge; std::shared_ptr rightEdge; }; template class Trianglulator { T model; const int w, h; SpanRange SearchSpan(int x, int y) { while (x < w && !model(x, y)) x++; if (x >= w) return SpanRange::CreateInvalid(); while (x > 0 && model(x - 1, y)) x--; auto x1 = x; while (x < w && model(x, y)) x++; return SpanRange(x1, x); } template static bool TriangleSide(const Point &p1, const Point &p2, const Point &p3) { auto x1 = p2.x - p1.x, y1 = p2.y - p1.y; auto x2 = p3.x - p1.x, y2 = p3.y - p1.y; auto area = x2 * y1 - x1 * y2; return flipped ? area < 0 : area > 0; } std::vector> lastLeftEdges; std::vector> lastRightEdges; std::vector lastLeftEdgesY; std::vector lastRightEdgesY; std::vector lastProcessedY; std::vector polys; void Init() { lastLeftEdges.resize(w + 1); lastRightEdges.resize(w + 1); lastLeftEdgesY.resize(w + 1); lastRightEdgesY.resize(w + 1); lastProcessedY.resize(w + 1); } std::vector edgeStack; template void EmitEdge(Edge *edge) { const auto &points = edge->points; edgeStack.clear(); edgeStack.push_back(0); edgeStack.push_back(1); for (std::size_t i = 2; i < points.size(); i++) { while (edgeStack.size() > 1) { auto j = edgeStack.back(); edgeStack.pop_back(); auto k = edgeStack.back(); if (TriangleSide(points[j], points[k], points[i])) { if (flipped) { polys.push_back(points[k]); polys.push_back(points[j]); } else { polys.push_back(points[j]); polys.push_back(points[k]); } polys.push_back(points[i]); } else { edgeStack.push_back(j); break; } } edgeStack.push_back(i); } } public: Trianglulator(const T &model) : model(model), w(model.GetWidth()), h(model.GetHeight()) { Init(); } Trianglulator(T &&model) : model(std::move(model)), w(model.GetWidth()), h(model.GetHeight()) { Init(); } std::vector Triangulate() { std::fill(lastLeftEdgesY.begin(), lastLeftEdgesY.end(), -1); std::fill(lastRightEdgesY.begin(), lastRightEdgesY.end(), -1); std::fill(lastProcessedY.begin(), lastProcessedY.end(), -1); polys.clear(); std::list spans; std::vector::iterator> removedIterators; std::vector points; for (int y = 0; y <= h; y++) { removedIterators.clear(); for (auto it = spans.begin(); it != spans.end(); it++) { auto &span = *it; bool removeSpan = false; int x = span.x1; for (; x < span.x2; x++) { if (!model(x, y)) break; } if (x < span.x2) { removeSpan = true; } else if (model(span.x1 - 1, y) || model(span.x2, y)) { removeSpan = true; } if (removeSpan) { // generate polygons for span { const auto &startpoints = span.xs; auto &endpoints = points; endpoints.clear(); if (model(span.x1 - 1, y) || !model(span.x1, y)) endpoints.push_back(span.x1); bool last = model(span.x1, y); for (int x = span.x1 + 1; x < span.x2; x++) { bool b = model(x, y); if (b != last) { endpoints.push_back(x); last = b; } } if (model(span.x2, y) || !model(span.x2 - 1, y)) endpoints.push_back(span.x2); int y1 = span.y, y2 = y; { auto *leftEdge = span.leftEdge.get(); if (leftEdge) leftEdge->points.emplace_back(endpoints.front(), y2); } if (endpoints.front() > span.x1) { if (span.leftEdge == nullptr) { span.leftEdge = std::make_shared(); span.leftEdge->points.emplace_back(span.x1, span.y); span.leftEdge->points.emplace_back(endpoints.front(), y2); } lastLeftEdges[span.x1] = span.leftEdge; lastLeftEdgesY[span.x1] = y; } else { if (span.leftEdge) EmitEdge(span.leftEdge.get()); span.leftEdge.reset(); } { auto *rightEdge = span.rightEdge.get(); if (rightEdge) rightEdge->points.emplace_back(endpoints.back(), y2); } if (endpoints.back() < span.x2) { if (span.rightEdge == nullptr) { span.rightEdge = std::make_shared(); span.rightEdge->points.emplace_back(span.x2, span.y); span.rightEdge->points.emplace_back(endpoints.back(), y2); } lastRightEdges[span.x2] = span.rightEdge; lastRightEdgesY[span.x2] = y; } else { if (span.rightEdge) EmitEdge(span.rightEdge.get()); span.rightEdge.reset(); } // emit polygons for (std::size_t i = 1; i < endpoints.size(); i++) { polys.emplace_back(startpoints.front(), y1); polys.emplace_back(endpoints[i - 1], y2); polys.emplace_back(endpoints[i], y2); } for (std::size_t i = 1; i < startpoints.size(); i++) { polys.emplace_back(startpoints[i], y1); polys.emplace_back(startpoints[i - 1], y1); polys.emplace_back(endpoints.back(), y2); } } removedIterators.push_back(it); } // -- one span done } for (auto &it : removedIterators) { spans.erase(it); } // mark processed spans for (auto &span : spans) { lastProcessedY[span.x1] = y; } // new span discovery for (int x = 0;;) { auto sp = SearchSpan(x, y); if (!sp.IsValid()) break; if (lastProcessedY[sp.x1] < y) { // unprocessed span found. spans.emplace_back(); auto &span = spans.back(); if (lastLeftEdgesY[sp.x1] >= y) span.leftEdge = lastLeftEdges[sp.x1]; if (lastRightEdgesY[sp.x2] >= y) span.rightEdge = lastRightEdges[sp.x2]; auto &beginpoints = span.xs; if (model(sp.x1 - 1, y - 1) || !model(sp.x1, y - 1)) beginpoints.push_back(sp.x1); bool last = model(sp.x1, y - 1); for (int x = sp.x1 + 1; x < sp.x2; x++) { bool b = model(x, y - 1); if (b != last) { beginpoints.push_back(x); last = b; } } if (model(sp.x2, y - 1) || !model(sp.x2 - 1, y - 1)) beginpoints.push_back(sp.x2); assert(beginpoints.size() > 0); span.x1 = sp.x1; span.x2 = sp.x2; span.y = y; } x = sp.x2 + 1; } // -- one Y level done } return std::move(polys); } }; } // namespace c2t