#include "Geom.h" namespace Upp { #define POLY_TIMING(n) // RTIMING(n) #define POLY_LOGGING 0 // 1 = on, 0 = off /* static bool ContainsPoints(const Point *poly, int count, Point P) { if(count <= 2 || IsNull(P)) return false; bool in = false; Point A = poly[count - 1]; Size ap(cmp(A.x, P.x), cmp(A.y, P.y)); for(Point B; --count >= 0; A = B) { B = *poly++; Size bp(cmp(B.x, P.x), cmp(B.y, P.y)); if(ap.cx < 0 && bp.cx < 0 || ap.cy > 0 && bp.cy > 0 || ap.cy < 0 && bp.cy < 0) continue; if((ap.cx | ap.cy) == 0 || (bp.cx | bp.cy) == 0) return true; else if(ap.cy == 0 && bp.cy == 0) { if(ap.cx <= 0 || bp.cx <= 0) return true; } else if(ap.cy == 0) { if(ap.cx > 0 && bp.cy > 0) in = !in; } else if(bp.cy == 0) { if(bp.cx > 0 && ap.cy > 0) in = !in; } else if(ap.cx > 0 && bp.cx > 0) { in = !in; } else { int s = (B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x); if(ap.cy > 0) s = -s; if(s == 0) return true; if(s > 0) in = !in; } } return in; } */ static inline int PolyCompare(Pointf P, Pointf A, Pointf B, Size ap, Size bp) { if(ap.cx < 0 && bp.cx < 0 || ap.cy > 0 && bp.cy > 0 || ap.cy < 0 && bp.cy < 0) return -1; if((ap.cx | ap.cy) == 0 || (bp.cx | bp.cy) == 0) return 0; else if(ap.cy == 0 && bp.cy == 0) return (ap.cx <= 0 || bp.cx <= 0 ? 0 : -1); else if(ap.cy == 0) return (ap.cx > 0 && bp.cy > 0 ? 1 : -1); else if(bp.cy == 0) return (bp.cx > 0 && ap.cy > 0 ? 1 : -1); else if(ap.cx > 0 && bp.cx > 0) return 1; else { double s = (B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x); if(ap.cy > 0) s = -s; return cmp(s, 0.0); } } struct PolyPart : public Rectf { PolyPart(Rectf rc, int first, int begin, int end) : Rectf(rc), first(first), begin(begin), end(end) {} int first, begin, end; }; static Rectf SplitPoly(const Array& polygon, const Vector& polyend, bool closed, Array& parts) { if(polyend.IsEmpty()) return Null; unsigned c = polygon.GetCount(); int n = 1; if(c >= 1 << 16) { c >>= 16; n <<= 8; } if(c >= 1 << 8) { c >>= 8; n <<= 4; } if(c >= 1 << 4) { c >>= 4; n <<= 2; } if(c >= 2) n <<= 1; Rectf sum = Null; int i = 0; for(int p = 0; p < polyend.GetCount(); p++) { int e = polyend[p], f = (closed ? e - 1 : i++); while(i < e) { // RTIMING("SplitPoly / part"); int ib = i; int ie = min(i + n, e); const Pointf *A = &polygon[f]; Rectf rc; rc.left = rc.right = A -> x; rc.top = rc.bottom = A -> y; while(i < ie) { // RTIMING("SplitPoly / vertex"); A = &polygon[i++]; double Ax = A -> x, Ay = A -> y; if (Ax < rc.left) rc.left = Ax; else if(Ax > rc.right) rc.right = Ax; if (Ay < rc.top) rc.top = Ay; else if(Ay > rc.bottom) rc.bottom = Ay; } // RTIMING("SplitPoly / new"); parts.Add(new PolyPart(rc, f, ib, ie)); sum |= rc; f = ie - 1; } } return sum; } int ContainsPoints(const Array& polygon, const Vector& polyend, const Array& points) { if(points.IsEmpty() || polygon.GetCount() <= 1) return CMP_OUT; Array parts; Rectf prect = SplitPoly(polygon, polyend, true, parts); bool is_in = false, is_out = false; for(int p = 0; p < points.GetCount(); p++) { const Pointf& P = points[p]; if(!prect.Contains(P)) { if(is_in) return CMP_SECT; is_out = true; continue; } bool in = false; for(int pp = 0; pp < parts.GetCount(); pp++) { const PolyPart& part = parts[pp]; if(part.right < P.x || part.top > P.y || part.bottom < P.y) continue; const Pointf *A = &polygon[part.first]; Size ap(cmp(A -> x, P.x), cmp(A -> y, P.y)); for(int j = part.begin; j < part.end; j++) { const Pointf *B = &polygon[j]; Size bp(cmp(B -> x, P.x), cmp(B -> y, P.y)); switch(PolyCompare(P, *A, *B, ap, bp)) { case 0: return CMP_SECT; case 1: in = !in; break; } A = B; ap = bp; } } (in ? is_in : is_out) = true; if(is_in && is_out) return CMP_SECT; } return is_in ? CMP_IN : CMP_OUT; } int ContainsPoints(const Array& polygon, const Array& points) { if(points.IsEmpty() || polygon.GetCount() <= 1) return CMP_OUT; Vector polyend; polyend.SetCount(1); polyend[0] = polygon.GetCount(); return ContainsPoints(polygon, polyend, points); } int ContainsPoint(const Array& polygon, const Vector& polyend, Pointf pt) { if(IsNull(pt) || polygon.GetCount() < 3 || polyend.IsEmpty()) return CMP_OUT; Array plist; plist.SetCount(1, pt); return ContainsPoints(polygon, polyend, plist); } int ContainsPoint(const Array& polygon, Pointf pt) { if(IsNull(pt) || polygon.GetCount() <= 1) return CMP_OUT; Array plist; plist.SetCount(1, pt); return ContainsPoints(polygon, plist); } int ContainsPoly(const Array& chkpoly, const Array& polygon, const Vector& polyend, bool closed) { // RTIMING("Contains(Poly / Poly)"); if(chkpoly.IsEmpty() || polyend.IsEmpty()) return CMP_OUT; if(polyend.GetCount() == polygon.GetCount()) return ContainsPoints(chkpoly, polygon); // static TimingInspector prep("Contains(Poly / Poly) -> preprocess"); // prep.Start(); Array pp, cp; Rectf pfull = SplitPoly(polygon, polyend, closed, pp); Vector chkend; chkend.SetCount(1); chkend[0] = chkpoly.GetCount(); Rectf cfull = SplitPoly(chkpoly, chkend, true, cp); int i, j; for(i = pp.GetCount(); --i >= 0;) if(!(pp[i] && cfull)) pp.Remove(i); // prep.End(); if(pp.IsEmpty()) return CMP_OUT; for(i = 0; i < pp.GetCount(); i++) { const PolyPart& api = pp[i]; if(!(api && cfull)) continue; const Pointf *A0 = &polygon[api.first]; for(j = 0; j < cp.GetCount(); j++) { const PolyPart& bpi = cp[j]; if(api && bpi) { const Pointf *A = A0; const Pointf *C0 = &chkpoly[bpi.first]; for(int ia = api.begin; ia < api.end; ia++) { const Pointf *B = &polygon[ia]; Pointf AB = *B - *A; const Pointf *C = C0; for(int ib = bpi.begin; ib < bpi.end; ib++) { // RTIMING("Contains(Poly / Poly) -> cross"); const Pointf *D = &chkpoly[ib]; Pointf CD = *D - *C, AC = *C - *A, BD = *D - *B; int acd = sgn(AC % CD) + 1, bcd = sgn(BD % CD) + 1; int cab = sgn(AC % AB) + 1, dab = sgn(BD % AB) + 1; static const int clss[3] = { 0376104000, 0104104104, 0000104376 }; if(clss[acd] & (1 << (9 * bcd + 3 * cab + dab))) return CMP_SECT; C = D; } A = B; } } } } if(!cfull.Contains(pfull)) return CMP_OUT; // RTIMING("Contains(Poly / Poly) -> in / out"); Pointf P = polygon[0]; bool in = false; for(i = 0; i < cp.GetCount(); i++) { const PolyPart& api = cp[i]; if(api.right < P.x || api.top > P.y || api.bottom < P.y) continue; const Pointf *A = &chkpoly[api.first]; Size cap(cmp(A -> x, P.x), cmp(A -> y, P.y)); for(j = api.begin; j < api.end; j++) { const Pointf *B = &chkpoly[j]; Size cbp(cmp(B -> x, P.x), cmp(B -> y, P.y)); switch(PolyCompare(P, *A, *B, cap, cbp)) { case 0: return CMP_SECT; case 1: in = !in; break; } A = B; cap = cbp; } } return (in ? CMP_IN : CMP_OUT); } int ContainsPoly(const Array& chkpoly, const Array& polygon, bool closed) { if(chkpoly.GetCount() <= 2 || polygon.IsEmpty()) return CMP_OUT; if(polygon.GetCount() == 1) return ContainsPoint(chkpoly, polygon[0]); Vector polyend; polyend.SetCount(1); polyend[0] = polygon.GetCount(); return ContainsPoly(chkpoly, polygon, polyend, closed); } template struct PIUtils { typedef int PointList; typedef int Iterator; typedef int ConstIterator; }; template <> struct PIUtils { typedef Vector PointList; typedef Point *Iterator; typedef const Point *ConstIterator; static int tscale(int x, int y, int z) { return iscale(x, y, z); } static Size tscale(Size x, int y, int z) { return iscale(x, y, z); } static Rect EmptyRect(Point pt) { return Rect(pt.x, pt.y, pt.x + 1, pt.y + 1); } static void Copy(Iterator dst, ConstIterator src, ConstIterator lim) { while(src != lim) *dst = *src, ++dst, ++src; } static int MAXVAL() { return INT_MAX; } static int MulVector(Size a, Size b) { return a.cx * b.cy - a.cy * b.cx; } }; template <> struct PIUtils { typedef Array PointList; typedef Array::Iterator Iterator; typedef Array::ConstIterator ConstIterator; static double tscale(double x, double y, double z) { return x * y / z; } static Sizef tscale(Sizef x, double y, double z) { return x * (y / z); } static Rectf EmptyRect(Pointf pt) { return Rectf(pt.x, pt.y, pt.x, pt.y); } static void Copy(Iterator dst, ConstIterator src, ConstIterator lim) { while(src != lim) *dst = *src, ++dst, ++src; } static double MAXVAL() { return DBL_MAX; } static double MulVector(Sizef a, Sizef b) { return a.cx * b.cy - a.cy * b.cx; } }; template struct PolygonIterator { PolygonIterator(Rect_ clip, int max_trace_points); void Add(typename PIUtils::ConstIterator vertices, int vertex_count, const int *counts, int count_count); void Run(); static bool SegmentLess(const typename PIUtils::PointList& a, const typename PIUtils::PointList& b) { Point_ a0 = a[0], b0 = b[0]; if(a0.y != b0.y) return a0.y < b0.y; if(a0.x != b0.x) return a0.x < b0.x; return a[1].x < b[1].x; // this is guess but perhaps usually true } struct Trace { typename PIUtils::PointList done; typename PIUtils::PointList avail; typename PIUtils::ConstIterator next; typename PIUtils::ConstIterator stop; Point_ ypos; Point_ end; Size_ delta; T GetX(T y) const { return delta.cy ? end.x - PIUtils::tscale(end.y - y, delta.cx, delta.cy) : end.x; } void Add(Point_ p) { while(done.GetCount() >= 2 && PIUtils::MulVector(p - done.Top(), p - done[done.GetCount() - 2]) == 0) done.Drop(); done.Add(p); } }; void Step(T ymax); void Flush(int index, T ypos); void CheckTraces(); void DumpTraces(T y) const; void DumpSegments() const; void DumpDone(int i) const; Rect_ clip; T ymin, ymax; Rect_ src_clip; int max_trace_points; Array traces; Vector< typename PIUtils::PointList > segments; typename PIUtils::PointList out_vertices; Vector out_counts; }; void SplitPolygon(const Point *vertices, int vertex_count, const int *counts, int count_count, Vector& out_vertices, Vector& out_counts, Rect clip, int max_trace_points) { POLY_TIMING("SplitPolygon"); PolygonIterator pi(clip, max_trace_points); pi.Add(vertices, vertex_count, counts, count_count); pi.Run(); out_vertices = pick(pi.out_vertices); out_counts = pick(pi.out_counts); } void SplitPolygon(const Vector& vertices, const Vector& counts, Vector& out_vertices, Vector& out_counts, Rect clip, int max_trace_points) { SplitPolygon(vertices.Begin(), vertices.GetCount(), counts.Begin(), counts.GetCount(), out_vertices, out_counts, clip, max_trace_points); } void SplitPolygon(Array::ConstIterator vertices, int vertex_count, const int *counts, int count_count, Array& out_vertices, Vector& out_counts, const Rectf& clip, int max_trace_points) { POLY_TIMING("SplitPolygon"); PolygonIterator pi(clip, max_trace_points); pi.Add(vertices, vertex_count, counts, count_count); pi.Run(); out_vertices = pick(pi.out_vertices); out_counts = pick(pi.out_counts); } void SplitPolygon(const Array& vertices, const Vector& counts, Array& out_vertices, Vector& out_counts, const Rectf& clip, int max_trace_points) { SplitPolygon(vertices.Begin(), vertices.GetCount(), counts.Begin(), counts.GetCount(), out_vertices, out_counts, clip, max_trace_points); } template PolygonIterator::PolygonIterator(Rect_ clip, int max_trace_points) : clip(clip), max_trace_points(max_trace_points) { ymax = INT_MIN; } template void PolygonIterator::Add(typename PIUtils::ConstIterator vertices, int vertex_count, const int *counts, int count_count) { POLY_TIMING("PolygonIterator::Add"); if(vertex_count <= 2) return; T cmin = IsNull(clip) ? -PIUtils::MAXVAL() : clip.top; T cmax = IsNull(clip) ? +PIUtils::MAXVAL() : clip.bottom; src_clip = PIUtils::EmptyRect(vertices[0]); for(; --count_count >= 0; vertices += *counts++) { typename PIUtils::ConstIterator p = vertices, e = vertices + *counts, l = e - 1; T ytop = p -> y; typename PIUtils::ConstIterator q = p; for(; p != e; ++p) { if(p -> y > ytop) ytop = (q = p) -> y; #if POLY_LOGGING src_clip |= PIUtils::EmptyRect(*p); #endif } ymax = max(ymax, ytop); p = q; do { typename PIUtils::ConstIterator ub = p; ASSERT(ub < e); if(++p > q) { while(p != e && p[0].y <= p[-1].y) p++; if(p == e && q != vertices && vertices[0].y <= p[-1].y) p = vertices + 1; } while(p < q && p[0].y <= p[-1].y) p++; if(p == q && p[-1].y == ub -> y) break; typename PIUtils::ConstIterator ue = --p; bool degen1 = (ub -> y == ue -> y || ub -> y <= cmin || ue -> y >= cmax); if(!degen1 && ub > ue) { while(ue > vertices && (ue[-1].y == ue[0].y || ue[-1].y <= cmin)) ue--; while(ub < l && (ub[1].y == ub[0].y || ub[1].y >= cmax)) ub++; if(ub == l && ue > vertices && (l -> y == vertices -> y || vertices -> y >= cmax)) ub = vertices; else if(ue == vertices && ub < l && (l -> y == ue -> y || l -> y <= cmin)) ue = l; } if(!degen1) { while(ub < ue && (ub[1].y == ub[0].y || ub[1].y >= cmax)) ub++; while(ue > ub && (ue[0].y == ue[-1].y || ue[-1].y <= cmin)) ue--; } typename PIUtils::PointList& seg1 = segments.Add(); seg1.SetCount(degen1 ? 2 : (int)(ue - ub) + 1 + (ue > ub ? 0 : *counts)); ASSERT(seg1.GetCount() >= 2); ASSERT(ub != e); typename PIUtils::Iterator sp = seg1.End(); if(degen1) { sp -= 2; sp[0] = sp[1] = (ub -> y <= cmin ? Point_(ub -> x, cmin) : ue -> y >= cmax ? Point_(ue -> x, cmax) : *ub); } else { if(ub -> y > cmax) { Size_ delta = (ub == l ? vertices[0] : ub[1]) - *ub; *--sp = Point_(ub -> x + PIUtils::tscale(cmax - ub -> y, delta.cx, delta.cy), cmax); if(++ub == e) ub = vertices; } else *--sp = *ub++; if(ub > ue) { for(; ub < e; *--sp = *ub++) ; ub = vertices; } while(ub < ue) *--sp = *ub++; if(ue -> y < cmin) { Size delta = (ue == vertices ? *l : ue[-1]) - *ue; *--sp = Point_(ue -> x + PIUtils::tscale(cmin - ue -> y, delta.cx, delta.cy), cmin); } else *--sp = *ub++; } ASSERT(sp == seg1.Begin()); ub = p; if(p >= q) { while(p < l && p[1].y >= p[0].y) p++; if(p == l && vertices -> y >= p -> y) p = vertices; } while(p < q && p[1].y >= p[0].y) p++; ue = p; bool degen2 = (ub -> y == ue -> y || ub -> y >= cmax || ue -> y <= cmin); if(!degen2 && ub > ue) { while(ue > vertices && (ue[-1].y == ue[0].y || ue[-1].y >= cmax)) ue--; while(ub < l && (ub[1].y == ub[0].y || ub[1].y <= cmin)) ub++; if(ub == l && ue > vertices && (l -> y == vertices -> y || vertices -> y <= cmin)) ub = vertices; else if(ue == vertices && ub < l && (l -> y == ue -> y || l -> y >= cmax)) ue = l; } if(!degen2) { while(ub < ue && (ub[1].y == ub[0].y || ub[1].y <= cmin)) ub++; while(ue > ub && (ue[0].y == ue[-1].y || ue[-1].y >= cmax)) ue--; } typename PIUtils::PointList& seg2 = segments.Add(); seg2.SetCount(degen2 ? 2 : (int)(ue - ub) + 1 + (ue > ub ? 0 : *counts)); ASSERT(seg2.GetCount() >= 2); sp = seg2.Begin(); if(degen2) sp[0] = sp[1] = (ub -> y >= cmax ? Point_(ub -> x, cmax) : ue -> y <= cmin ? Point_(ue -> x, cmin) : *ub); else { if(ub -> y < cmin) { Size_ delta = (ub == l ? vertices[0] : ub[1]) - *ub; *sp++ = Point_(ub -> x + PIUtils::tscale(cmin - ub -> y, delta.cx, delta.cy), cmin); } else *sp++ = *ub; if(++ub == e) ub = vertices; if(ue -> y > cmax) { Size_ delta = (ue == vertices ? *l : ue[-1]) - *ue; seg2.Top() = Point_(ue -> x + PIUtils::tscale(cmax - ue -> y, delta.cx, delta.cy), cmax); } else seg2.Top() = *ue; if(ub > ue) { PIUtils::Copy(sp, ub, e); sp += e - ub; PIUtils::Copy(sp, vertices, ue); sp += ue - vertices; } else { PIUtils::Copy(sp, ub, ue); sp += ue - ub; } ASSERT(sp == seg2.End() - 1); } if(degen1 && degen2) segments.Remove(segments.GetCount() - 2, 2); } while(p != q); ASSERT(!(segments.GetCount() & 1)); } ymax = min(ymax, cmax); #if POLY_LOGGING { // check segment pair integrity VectorMap counts; int i; for(i = 0; i < segments.GetCount(); i++) counts.GetAdd(segments[i].Top().y, 0)++; for(i = 0; i < counts.GetCount(); i++) if(counts[i] & 1) RLOG("odd bottom position " << counts.GetKey(i)); } #endif//POLY_LOGGING } template void PolygonIterator::DumpSegments() const { for(int i = 0; i < segments.GetCount(); i++) { String seglog; seglog << "[" << i << "]: " << segments[i][0]; for(int j = 1; j < segments[i].GetCount(); j++) seglog << " -> " << segments[i][j]; RLOG(seglog); } } template void PolygonIterator::Run() { POLY_TIMING("PolygonIterator::Run"); if(segments.GetCount() < 2) return; Sort(segments, &PolygonIterator::SegmentLess); #if POLY_LOGGING RLOG("PolygonIterator::Run"); DumpSegments(); #endif//POLY_LOGGING ymin = segments[0][0].y; for(int s = 0; s < segments.GetCount();) { T y = segments[s][0].y; Step(y); int t = 0; for(; s < segments.GetCount() && segments[s][0].y == y; s += 2) { ASSERT(segments[s + 1][0].y == y); T x1 = segments[s + 0][0].x; while(t < traces.GetCount() && traces[t].ypos.x < x1) t++; int i1 = t; T x2 = segments[s + 1][0].x; while(t < traces.GetCount() && traces[t].ypos.x < x2) t++; int i2 = t; #if POLY_LOGGING RLOG("y = " << y << ", insert[" << i1 << " - " << i2 << "]: segment #" << s); #endif//POLY_LOGGING for(int flush = (i1 + 1) | 1; flush <= i2; flush += 2) Flush(flush, y); Trace& n = traces.Insert(i1); i2++; n.avail = pick(segments[s]); n.next = n.avail.Begin(); n.stop = n.avail.End() - 1; n.end = *++n.next; n.delta = n.end - n.next[-1]; if(i1 & 1) { // hole opening Trace& r = traces[i1 + 1]; n.done = pick(r.done); #if POLY_LOGGING RLOG("insert: r.ypos = " << r.ypos); if(r.ypos.y < n.done.Top().y) RLOG("join-insert (1) y reversal: " << n.done.Top() << " -> " << r.ypos); #endif//POLY_LOGGING n.Add(r.ypos); if(r.ypos.x != x1) { #if POLY_LOGGING if(r.ypos.y < n.done.Top().y) RLOG("join-insert (2) y reversal: " << n.done.Top() << " -> " << r.ypos); #endif//POLY_LOGGING n.Add(n.next[-1]); } r.done.Clear(); r.Add(r.ypos); #if POLY_LOGGING DumpDone(i1 + 1); #endif//POLY_LOGGING } else // simply insert segment n.Add(n.next[-1]); Trace& o = traces.Insert(i2); o.avail = pick(segments[s + 1]); o.next = o.avail.Begin(); o.stop = o.avail.End() - 1; o.Add(*o.next); o.end = *++o.next; o.delta = o.end - o.next[-1]; #if POLY_LOGGING RLOG("insert: o.avail = " << o.avail.GetCount() << ", end = " << o.end); DumpDone(i2); #endif//POLY_LOGGING t += 2; } } #if POLY_LOGGING CheckTraces(); #endif//POLY_LOGGING Step(ymax + 1); #if POLY_LOGGING LOG("//PolygonIterator::Run"); #endif//POLY_LOGING } template void PolygonIterator::Step(T ymax) { POLY_TIMING("PolygonIterator::Step"); while(ymin < ymax && !traces.IsEmpty()) { Trace *p, *q; for(;;) { // check for trace swaps p = &traces[0]; p -> ypos.y = min(p -> end.y, ymax); p -> ypos.x = p -> GetX(p -> ypos.y); int swap_index = -1; Point_ swap; int t; for(t = 1; t < traces.GetCount(); t++) { q = &traces[t]; if(q -> end.y >= p -> ypos.y) q -> ypos.x = q -> GetX(q -> ypos.y = p -> ypos.y); else { q -> ypos = q -> end; p -> ypos.x = p -> GetX(p -> ypos.y = q -> end.y); swap_index = -1; } if(q -> ypos.x <= p -> ypos.x) { T det = PIUtils::MulVector(p -> delta, q -> delta); if(det > 0) { T num = PIUtils::MulVector(p -> end - q -> end, q -> delta); Point_ new_swap = p -> end - PIUtils::tscale(p -> delta, minmax(num, 0, det), det); if(new_swap.y <= q -> ypos.y) { swap_index = t; swap = new_swap; q -> ypos = new_swap; } } } p = q; } if(swap_index < 0) break; p = &traces[swap_index - 1]; q = &traces[swap_index - 0]; #if POLY_LOGGING RLOG("ymin = " << ymin << ", swap[" << swap_index << "]: " << swap << ": l = " << (p -> end - p -> delta) << " -> " << p -> end << " (" << p -> delta << "), " "r = " << (q -> end - q -> delta) << " -> " << q -> end << " (" << q -> delta << ")"); DumpDone(swap_index - 1); if(swap.y < p -> done.Top().y) RLOG("swap y reversal in p: " << p -> done.Top() << " -> " << swap); DumpDone(swap_index - 0); if(swap.y < q -> done.Top().y) RLOG("swap y reversal in q: " << q -> done.Top() << " -> " << swap); #endif//POLY_LOGGING p -> Add(swap); q -> Add(swap); Swap(p -> done, q -> done); traces.Swap(swap_index, swap_index - 1); } ymin = p -> ypos.y; #if POLY_LOGGING RLOG("after swap: ymin = " << ymin); DumpTraces(ymin); #endif//POLY_LOGGING if(p -> ypos.y >= ymax) return; ASSERT(!(traces.GetCount() & 1)); Buffer remove(traces.GetCount()); Fill(remove, remove + traces.GetCount(), false); int nremove = 0; bool hseg = false; for(int t = 0; t < traces.GetCount(); t++) { Trace& p = traces[t]; while(p.end.y <= ymin) { #if POLY_LOGGING if(p.end.y < p.done.Top().y) RLOG("remove y reversal[" << t << "]: " << p.done.Top() << " -> " << p.end); #endif//POLY_LOGGING p.Add(p.end); if(p.next < p.stop) { p.delta = (p.end = p.next[1]) - p.next[0]; if(p.delta.cy == 0) hseg = true; p.next++; } else { nremove++; remove[t] = true; break; } } } if(nremove) { ASSERT(!(nremove & 1)); int t = traces.GetCount(); if(t == nremove) { for(int i = 1; i < traces.GetCount(); i += 2) Flush(i, ymin); traces.Clear(); return; } while(--t >= 0) { while(t >= 0 && !remove[t]) t--; if(t < 0) break; int i2 = t; ASSERT(i2 >= 1); while(!remove[--t]) ; int i1 = t; ASSERT(i1 >= 0); #if POLY_LOGGING LOG("ymin = " << ymin << ", remove[" << i1 << " - " << i2 << "] & " << ymin << ": l = " << (traces[i1].end - traces[i1].delta) << " -> " << traces[i1].end << " (" << traces[i1].delta << "), " "r = " << (traces[i2].end - traces[i2].delta) << " -> " << traces[i2].end << " (" << traces[i2].delta << ")"); #endif//POLY_LOGGING for(int flush = i2 | 1; flush >= i1; flush -= 2) Flush(flush, ymin); if(i2 == i1 + 1) traces.Remove(i1, 2); else { traces.Remove(i2); traces.Remove(i1); } } } #if POLY_LOGGING RLOG("After removals:"); DumpTraces(ymin); #endif//POLY_LOGGING if(hseg) { // horizontal segments present, check for horizontal swaps ASSERT(!traces.IsEmpty()); Trace *p = &traces[0]; p -> ypos.x = p -> GetX(p -> ypos.y = ymin); for(int t = 1; t < traces.GetCount(); t++) { Trace *q = &traces[t]; if((q -> ypos.x = q -> GetX(q -> ypos.y = ymin)) >= p -> ypos.x) p = q; else { int u = t - 1; do { Trace *r = &traces[u]; q -> Add(q -> ypos); q -> Add(r -> ypos); r -> Add(r -> ypos); r -> Add(q -> ypos); Swap(q -> done, r -> done); traces.Swap(u, u + 1); } while(--u >= 0 && q -> ypos.x < traces[u].ypos.x); } } #if POLY_LOGGING RLOG("After horizontal swaps:"); DumpTraces(ymin); #endif//POLY_LOGGING } } } template void PolygonIterator::Flush(int index, T ypos) { POLY_TIMING("PolygonIterator::Flush"); ASSERT(index & 1); Trace& p = traces[index - 1]; Trace& q = traces[index - 0]; p.ypos.x = p.GetX(p.ypos.y = ypos); q.ypos.x = q.GetX(q.ypos.y = ypos); int count = out_vertices.GetCount(); #if POLY_LOGGING RLOG("flush[" << index << "] at pos " << ypos << ": left done = " << p.done.GetCount() << ", right done = " << q.done.GetCount()); String plist, qlist; int c; for(c = 1; c < p.done.GetCount(); c++) { plist << ' ' << p.done[c]; if(p.done[c].y < p.done[c - 1].y) { RLOG("y reversal in p & " << c << ": " << p.done[c - 1] << " -> " << p.done[c]); } } RLOG("plist = " << p.done[0] << plist); for(c = 1; c < q.done.GetCount(); c++) { qlist << ' ' << q.done[c]; if(q.done[c].y < q.done[c - 1].y) { RLOG("y reversal in q & " << c << ": " << q.done[c - 1] << " -> " << q.done[c]); } } RLOG("qlist = " << q.done[0] << qlist); #endif out_vertices.Append(p.done); if(out_vertices.Top() != p.ypos) out_vertices.Add(p.ypos); if(q.ypos != p.ypos) out_vertices.Add(q.ypos); typename PIUtils::ConstIterator qe = q.done.End(); if(*--qe != q.ypos) out_vertices.Add(*qe); typename PIUtils::ConstIterator qb = q.done.Begin(); if(*qb == p.done[0] && qb < qe) qb++; out_vertices.SetCountR(out_vertices.GetCount() + (int)(qe - qb)); typename PIUtils::Iterator out = out_vertices.End(); while(qb < qe) *--out = *qb++; if(out_vertices.GetCount() <= count + 2) out_vertices.SetCountR(count); // trash degenerate segment else { #if POLY_LOGGING String seg; Rect_ extent = PIUtils::EmptyRect(out_vertices[count]); for(int i = count; i < out_vertices.GetCount(); i++) { seg << ' ' << out_vertices[i]; extent |= PIUtils::EmptyRect(out_vertices[i]); } RLOG("extent = " << extent << ":" << seg); if(!src_clip.Contains(extent)) RLOG("-> out of source clip " << src_clip); #endif out_counts.Add(out_vertices.GetCount() - count); } p.done.Clear(); p.done.Add(p.ypos); q.done.Clear(); q.done.Add(q.ypos); #if POLY_LOGGING DumpDone(index - 1); DumpDone(index - 0); #endif//POLY_LOGGING } template void PolygonIterator::CheckTraces() { VectorMap counts; int i; for(i = 0; i < traces.GetCount(); i++) counts.GetAdd(traces[i].stop -> y, 0)++; for(i = 0; i < counts.GetCount(); i++) if(counts[i] & 1) { RLOG("unpaired end of trace #" << i << ": " << traces[i].stop -> y); NEVER(); } } template void PolygonIterator::DumpTraces(T y) const { String out; out << traces.GetCount() << " traces & " << y << ":"; for(int t = 0; t < traces.GetCount(); t++) out << ' ' << traces[t].GetX(y); RLOG(out); } template void PolygonIterator::DumpDone(int i) const { String out; out << "done[" << i << "]:"; for(int t = 0; t < traces[i].done.GetCount(); t++) out << ' ' << traces[i].done[t]; RLOG(out); } }