| Index: experimental/Intersection/thingsToDo.txt
|
| diff --git a/experimental/Intersection/thingsToDo.txt b/experimental/Intersection/thingsToDo.txt
|
| deleted file mode 100644
|
| index f74d3ee1268f9fede56b2b8225809fcb5a47ff32..0000000000000000000000000000000000000000
|
| --- a/experimental/Intersection/thingsToDo.txt
|
| +++ /dev/null
|
| @@ -1,525 +0,0 @@
|
| -/*
|
| - * Copyright 2012 Google Inc.
|
| - *
|
| - * Use of this source code is governed by a BSD-style license that can be
|
| - * found in the LICENSE file.
|
| - */
|
| -add unit test for quadratic horizontal intersection
|
| -add unit test for cubic horizontal intersection with left/right
|
| -add unit test for ActiveEdge::calcLeft (can currently loop forever)
|
| -does ActiveEdge::isCoincidentWith need to support quad, cubic?
|
| -figure out why variation in ActiveEdge::tooCloseToCall isn't better
|
| -why does 'lastPtr - 2' in addIntersectingTs break testSimplifyTriangle22?
|
| -add code to promote quad to cubic, or add quad/cubic intersection
|
| -figure out why testSimplifySkinnyTriangle13 fails
|
| -
|
| -for quadratics and cubics, once various T values are added, see if consecutive
|
| -Ts have ys that go up instead of down. If so, the edge needs to be broken.
|
| -
|
| -when splitting curves at inflection pts, should I retain the original curve
|
| -data and note that the first/last T are no longer 0/1 ?
|
| -I need to figure this out before I can proceed
|
| -
|
| -would it make sense to leave the InEdge alone, and add multiple copies of
|
| -ActiveEdge, pointing to the same InEdge, where the copy has only the subset
|
| -of Ts that need to be walked in reverse order?
|
| -
|
| -
|
| --- A Digression Which Shows Why Resolving Coincidence Does Not Make Sense --
|
| -
|
| -Consider the following fine ASCII art:
|
| -
|
| - +------>-------+ +------>-------+
|
| - | | | |
|
| - ^ V ^ V
|
| - | | | |
|
| - +------<-------+ +------<-------+
|
| - +------>-------+ +------<-------+
|
| - | | | |
|
| - ^ V V ^
|
| - | | | |
|
| - +------<-------+ +------>-------+
|
| -
|
| -(assume the bottom and top of the stacked rectangles are coincident)
|
| -
|
| -Simplifying said rectangles, regardless of rectangle direction, and regardless
|
| -of winding or even/odd, eliminates the coincident edge, i.e., the result is
|
| -always:
|
| -
|
| - +------>-------+
|
| - | |
|
| - | |
|
| - | |
|
| - ^ V
|
| - | |
|
| - | |
|
| - | |
|
| - +------<-------+
|
| -
|
| -But when the rectangles are enclosed in a larger rectangle:
|
| -
|
| -+-------->---------+ +-------->---------+
|
| -| +------>-------+ | | +------>-------+ |
|
| -| | | | | | | |
|
| -| ^ V | | ^ V |
|
| -| | | | | | | |
|
| -| +------<-------+ | | +------<-------+ |
|
| -| +------>-------+ | | +------<-------+ |
|
| -| | | | | | | |
|
| -| ^ V | | V ^ |
|
| -| | | | | | | |
|
| -| +------<-------+ | | +------>-------+ |
|
| -+--------<---------+ +--------<---------+
|
| -
|
| -Simplifying them gives different results depending on the winding setting:
|
| -
|
| -winding:
|
| -+-------->---------+ +-------->---------+
|
| -| | | |
|
| -| | | |
|
| -| | | |
|
| -| | | |
|
| -| | | +------<-------+ |
|
| -| | | | | |
|
| -| | | V ^ |
|
| -| | | | | |
|
| -| | | +------>-------+ |
|
| -+--------<---------+ +--------<---------+
|
| -
|
| -even odd:
|
| -+-------->---------+ +-------->---------+
|
| -| +------<-------+ | | +------<-------+ |
|
| -| | | | | | | |
|
| -| | | | | | | |
|
| -| | | | | | | |
|
| -| | | | | | | |
|
| -| V ^ | | V ^ |
|
| -| | | | | | | |
|
| -| | | | | | | |
|
| -| | | | | | | |
|
| -| +------>-------+ | | +------>-------+ |
|
| -+--------<---------+ +--------<---------+
|
| -
|
| -So, given the inner rectangles alone (e.g., given coincident pairs in some local
|
| -context), we can't know whether to keep the coincident edges or not.
|
| -
|
| -
|
| --- Thoughts About Sortless Ops --
|
| -
|
| -I can't come up with anything truly sortless. It seems that the crossings need
|
| -to be sorted to know which segment is next on the outside, although sometimes
|
| -we can use that it is not coincident just to follow the direction.
|
| -
|
| -If it is coincident or if there's more than two crossing segments, sorting
|
| -seems inevitable.
|
| -
|
| -Likewise, to resolve whether one contour is inside another, it seems that
|
| -sorting is required. Given a pair of segments on different contours, to know
|
| -if one is inside of the other, I need to know for each which side of the edge
|
| -is the inside/filled side. When the outer contour is walked, it seems like I
|
| -could record the inside info. I guess when the inner contour is found, its
|
| -inside sense is reversed (inside is above the top). But how do I know if the
|
| -next contour is inside another? Maybe shoot out a line and brute-force
|
| -intersect it with all the segments in all the other contours? If every contour
|
| -has an extra segment when the intersections are computed, this may not be as
|
| -crazy as it seems.
|
| -
|
| -Suppose each contour has one extra segment shooting straight up from the top
|
| -(or straight up from any point on the segment). This ray is not intersected
|
| -with the home contour, but is intersected with all other contours as part of
|
| -the normal intersection engine. If it is possible to get from the T values to
|
| -the other segments to the other contours, it would be straightforward to
|
| -count the contour crossings and determine if the home contour is in another
|
| -contour or not (if the count is even, not, if odd, is inside). By itself that
|
| -doesn't tell us about winding, but it's a start.
|
| -
|
| -
|
| -Since intersecting these rays is unrelated to computing other intersections,
|
| -it can be lazily done once the contour is found.
|
| -
|
| -So
|
| -repeat the following
|
| -find the top segment of all contours
|
| -trace the outside, marking touching first and last segments as inside
|
| -continue tracing the touched segments with reversed outside/inside sense
|
| -once the edges are exhausted, remaining must be disjoint contours
|
| -send a ray from a disjoint point through all other contours
|
| -count the crossings, determine if disjoint is inside or outside, then continue
|
| -
|
| -===
|
| -
|
| -On Quadratic (and Cubic) Intersections
|
| -
|
| -Currently, if only the end points touch, QuadracticIntersections does a lot of
|
| -work to figure that out. Can I test for that up front, then short circuit the
|
| -recursive search for the end points?
|
| -
|
| -Or, is there something defective in the current approach that makes the end
|
| -point recursion go so deep? I'm seeing 56 stack frames (about 28 divides, but
|
| -thankfully, no splits) to find one matching endpoint.
|
| -
|
| -
|
| -Bezier curve focus may allow more quickly determining that end points with
|
| -identical tangents are practically coicident for some range of T, but I don't
|
| -understand the math yet to know.
|
| -
|
| -Another approach is to determine how flat the curve is to make good guesses
|
| -about how far to move away in T before doing the intersection for the remainder
|
| -and/or to determine whether one curve is to the inside or outside of another.
|
| -According to Mike/Rob, the flatness for quadratics increases by 4 for each
|
| -subdivision, and a crude guess of the curvature can be had by comparing P1 to
|
| -(P0+P2)/2. By looking at the ULPS of the numbers, I can guess what value of
|
| -T may be far enough that the curves diverge but don't cross.
|
| -
|
| -====
|
| -
|
| -Code I May Not Need Any More
|
| -
|
| - static bool CoincidentCandidate(const Angle* current) {
|
| - const Segment* segment = current->segment();
|
| - int min = SkMin32(current->start(), current->end());
|
| - do {
|
| - const Span& span = segment->fTs[min];
|
| - if (span.fCoincident == Span::kStart_Coincidence) {
|
| - return true;
|
| - }
|
| - } while (--min >= 0);
|
| - return false;
|
| - }
|
| -
|
| - static bool CoincidentHalf(const Angle* current, const Angle* next) {
|
| - const Segment* other = next->segment();
|
| - const Segment* segment = current->segment();
|
| - int min = SkMin32(current->start(), current->end());
|
| - const Span& minSpan = segment->fTs[min];
|
| - if (minSpan.fOther == other) {
|
| - return minSpan.fCoincident == Span::kStart_Coincidence;
|
| - }
|
| - int index = min;
|
| - int spanCount = segment->fTs.count();
|
| - while (++index < spanCount) {
|
| - const Span& span = segment->fTs[index];
|
| - if (minSpan.fT != span.fT) {
|
| - break;
|
| - }
|
| - if (span.fOther != other) {
|
| - continue;
|
| - }
|
| - return span.fCoincident == Span::kStart_Coincidence;
|
| - }
|
| - index = min;
|
| - while (--index >= 0) {
|
| - const Span& span = segment->fTs[index];
|
| - if (span.fOther != other) {
|
| - continue;
|
| - }
|
| - return span.fCoincident == Span::kStart_Coincidence;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - static bool Coincident(const Angle* current, const Angle* next) {
|
| - return CoincidentHalf(current, next) &&
|
| - CoincidentHalf(next, current);
|
| - }
|
| -
|
| - // If three lines cancel in a - b - c order, a - b may or may not
|
| - // eliminate the edge that describes the b - c cancellation. Check done to
|
| - // determine this.
|
| - static bool CoincidentCancels(const Angle* current, const Angle* next) {
|
| - int curMin = SkMin32(current->start(), current->end());
|
| - if (current->segment()->fTs[curMin].fDone) {
|
| - return false;
|
| - }
|
| - int nextMin = SkMin32(next->start(), next->end());
|
| - if (next->segment()->fTs[nextMin].fDone) {
|
| - return false;
|
| - }
|
| - return SkSign32(current->start() - current->end())
|
| - != SkSign32(next->start() - next->end());
|
| - }
|
| -
|
| - // FIXME: at this point, just have two functions for the different steps
|
| - int coincidentEnd(int from, int step) const {
|
| - double fromT = fTs[from].fT;
|
| - int count = fTs.count();
|
| - int to = from;
|
| - while (step > 0 ? ++to < count : --to >= 0) {
|
| - const Span& span = fTs[to];
|
| - if ((step > 0 ? span.fT - fromT : fromT - span.fT) >= FLT_EPSILON ) {
|
| - // FIXME: we assume that if the T changes, we don't care about
|
| - // coincident -- but in nextSpan, we require that both the T
|
| - // and actual loc change to represent a span. This asymettry may
|
| - // be OK or may be trouble -- if trouble, probably will need to
|
| - // detect coincidence earlier or sort differently
|
| - break;
|
| - }
|
| -#if 01
|
| - if (span.fCoincident == (step < 0 ? Span::kStart_Coincidence :
|
| - Span::kEnd_Coincidence)) {
|
| - from = to;
|
| - }
|
| -#else
|
| - from = to;
|
| -#endif
|
| - }
|
| - return from;
|
| - }
|
| -
|
| - // once past current span, if step>0, look for coicident==1
|
| - // if step<0, look for coincident==-1
|
| - int nextSpanEnd(int from, int step) const {
|
| - int result = nextSpan(from, step);
|
| - if (result < 0) {
|
| - return result;
|
| - }
|
| - return coincidentEnd(result, step);
|
| - }
|
| -
|
| -
|
| - void adjustFirst(const SkTDArray<Angle*>& sorted, int& first, int& winding,
|
| - bool outside) {
|
| - int firstIndex = first;
|
| - int angleCount = sorted.count();
|
| - if (true || outside) {
|
| - const Angle* angle = sorted[firstIndex];
|
| - int prior = firstIndex;
|
| - do {
|
| - if (--prior < 0) {
|
| - prior = angleCount - 1;
|
| - }
|
| - if (prior == firstIndex) { // all are coincident with each other
|
| - return;
|
| - }
|
| - if (!Coincident(sorted[prior], sorted[first])) {
|
| - return;
|
| - }
|
| - winding += angle->sign();
|
| - first = prior;
|
| - angle = sorted[prior];
|
| - winding -= angle->sign();
|
| - } while (true);
|
| - }
|
| - do {
|
| - int next = first + 1;
|
| - if (next == angleCount) {
|
| - next = 0;
|
| - }
|
| - if (next == firstIndex) { // all are coincident with each other
|
| - return;
|
| - }
|
| - if (!Coincident(sorted[first], sorted[next])) {
|
| - return;
|
| - }
|
| - first = next;
|
| - } while (true);
|
| - }
|
| -
|
| - bool nextIsCoincident = CoincidentCandidate(nextAngle);
|
| - bool finalOrNoCoincident = true;
|
| - bool pairCoincides = false;
|
| - bool pairCancels = false;
|
| - if (nextIsCoincident) {
|
| - int followIndex = nextIndex + 1;
|
| - if (followIndex == angleCount) {
|
| - followIndex = 0;
|
| - }
|
| - const Angle* followAngle = sorted[followIndex];
|
| - finalOrNoCoincident = !Coincident(nextAngle, followAngle);
|
| - if ((pairCoincides = Coincident(angle, nextAngle))) {
|
| - pairCancels = CoincidentCancels(angle, nextAngle);
|
| - }
|
| - }
|
| - if (pairCancels && !foundAngle && !nextSegment->done()) {
|
| - Segment* aSeg = angle->segment();
|
| - // alreadyMarked |= aSeg == sorted[firstIndex]->segment();
|
| - aSeg->markAndChaseCoincident(angle->start(), angle->end(),
|
| - nextSegment);
|
| - if (firstEdge) {
|
| - return NULL;
|
| - }
|
| - }
|
| - if (pairCoincides) {
|
| - if (pairCancels) {
|
| - goto doNext;
|
| - }
|
| - int minT = SkMin32(nextAngle->start(), nextAngle->end());
|
| - bool markNext = abs(maxWinding) < abs(winding);
|
| - if (markNext) {
|
| - nextSegment->markDone(minT, winding);
|
| - }
|
| - int oldMinT = SkMin32(angle->start(), angle->end());
|
| - if (true || !foundAngle) {
|
| - // SkASSERT(0); // do we ever get here?
|
| - Segment* aSeg = angle->segment();
|
| - // alreadyMarked |= aSeg == sorted[firstIndex]->segment();
|
| - aSeg->markDone(oldMinT, maxWinding);
|
| - }
|
| - }
|
| -
|
| - // OPTIMIZATION: uses tail recursion. Unwise?
|
| - void innerCoincidentChase(int step, Segment* other) {
|
| - // find other at index
|
| - // SkASSERT(!done());
|
| - const Span* start = NULL;
|
| - const Span* end = NULL;
|
| - int index, startIndex, endIndex;
|
| - int count = fTs.count();
|
| - for (index = 0; index < count; ++index) {
|
| - const Span& span = fTs[index];
|
| - if (!span.fCoincident || span.fOther != other) {
|
| - continue;
|
| - }
|
| - if (!start) {
|
| - startIndex = index;
|
| - start = &span;
|
| - } else {
|
| - SkASSERT(!end);
|
| - endIndex = index;
|
| - end = &span;
|
| - }
|
| - }
|
| - if (!end) {
|
| - return;
|
| - }
|
| - bool thisDone = fTs[SkMin32(startIndex, endIndex)].fDone;
|
| - bool otherDone = other->fTs[SkMin32(start->fOtherIndex,
|
| - end->fOtherIndex)].fDone;
|
| - if (thisDone && otherDone) {
|
| - return;
|
| - }
|
| - Segment* next;
|
| - Segment* nextOther;
|
| - if (step < 0) {
|
| - next = start->fT == 0 ? NULL : this;
|
| - nextOther = other->fTs[start->fOtherIndex].fT > 1 - FLT_EPSILON ? NULL : other;
|
| - } else {
|
| - next = end->fT == 1 ? NULL : this;
|
| - nextOther = other->fTs[end->fOtherIndex].fT < FLT_EPSILON ? NULL : other;
|
| - }
|
| - SkASSERT(!next || !nextOther);
|
| - for (index = 0; index < count; ++index) {
|
| - const Span& span = fTs[index];
|
| - if (span.fCoincident || span.fOther == other) {
|
| - continue;
|
| - }
|
| - bool checkNext = !next && (step < 0 ? span.fT < FLT_EPSILON
|
| - && span.fOtherT > 1 - FLT_EPSILON : span.fT > 1 - FLT_EPSILON
|
| - && span.fOtherT < FLT_EPSILON);
|
| - bool checkOther = !nextOther && (step < 0 ? fabs(span.fT - start->fT) < FLT_EPSILON
|
| - && span.fOtherT < FLT_EPSILON : fabs(span.fT - end->fT) < FLT_EPSILON
|
| - && span.fOtherT > 1 - FLT_EPSILON);
|
| - if (!checkNext && !checkOther) {
|
| - continue;
|
| - }
|
| - Segment* oSegment = span.fOther;
|
| - if (oSegment->done()) {
|
| - continue;
|
| - }
|
| - int oCount = oSegment->fTs.count();
|
| - for (int oIndex = 0; oIndex < oCount; ++oIndex) {
|
| - const Span& oSpan = oSegment->fTs[oIndex];
|
| - if (oSpan.fT >= FLT_EPSILON && oSpan.fT <= 1 - FLT_EPSILON) {
|
| - continue;
|
| - }
|
| - if (!oSpan.fCoincident) {
|
| - continue;
|
| - }
|
| - if (checkNext && (oSpan.fT < FLT_EPSILON ^ step < 0)) {
|
| - next = oSegment;
|
| - checkNext = false;
|
| - }
|
| - if (checkOther && (oSpan.fT > 1 - FLT_EPSILON ^ step < 0)) {
|
| - nextOther = oSegment;
|
| - checkOther = false;
|
| - }
|
| - }
|
| - }
|
| - // this needs to walk both spans in lock step, skipping edges that
|
| - // are already marked done on one or the other
|
| - markCanceled(startIndex, endIndex);
|
| - if (next && nextOther) {
|
| - next->innerCoincidentChase(step, nextOther);
|
| - }
|
| - }
|
| -
|
| - // cancel coincident edges in lock step
|
| - void markCanceled(int start, int end) {
|
| - if (done()) {
|
| - return;
|
| - }
|
| - Segment* other = fTs[start].fOther;
|
| - if (other->done()) {
|
| - return;
|
| - }
|
| - if (start > end) {
|
| - SkTSwap<int>(start, end);
|
| - }
|
| - double maxT = fTs[end].fT - FLT_EPSILON;
|
| - int spanCount = fTs.count();
|
| - // since these cancel, this walks up and other walks down
|
| - int oStart = fTs[start].fOtherIndex;
|
| - double oStartT = other->fTs[oStart].fT;
|
| - while (oStartT - other->fTs[--oStart].fT < FLT_EPSILON)
|
| - ;
|
| - double startT = fTs[start].fT;
|
| - while (start > 0 && startT - fTs[start - 1].fT < FLT_EPSILON) {
|
| - --start;
|
| - }
|
| - do {
|
| - Span* span = &fTs[start];
|
| - Span* oSpan = &other->fTs[oStart];
|
| - // find start of each, and see if both are not done
|
| - bool markDone = !span->fDone && !oSpan->fDone;
|
| - double spanT = span->fT;
|
| - do {
|
| - if (markDone) {
|
| - span->fCanceled = true;
|
| - #if DEBUG_MARK_DONE
|
| - const SkPoint& pt = xyAtT(span);
|
| - SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
|
| - __FUNCTION__, fID, start, span->fT, pt.fX, pt.fY);
|
| - #endif
|
| - SkASSERT(!span->fDone);
|
| - span->fDone = true;
|
| - span->fWinding = 0;
|
| - fDoneSpans++;
|
| - }
|
| - if (++start == spanCount) {
|
| - break;
|
| - }
|
| - span = &fTs[start];
|
| - } while (span->fT - spanT < FLT_EPSILON);
|
| - double oSpanT = oSpan->fT;
|
| - do {
|
| - if (markDone) {
|
| - oSpan->fCanceled = true;
|
| - #if DEBUG_MARK_DONE
|
| - const SkPoint& oPt = xyAtT(oSpan);
|
| - SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
|
| - __FUNCTION__, other->fID, oStart, oSpan->fT,
|
| - oPt.fX, oPt.fY);
|
| - #endif
|
| - SkASSERT(!oSpan->fDone);
|
| - oSpan->fDone = true;
|
| - oSpan->fWinding = 0;
|
| - other->fDoneSpans++;
|
| - }
|
| - if (--oStart < 0) {
|
| - break;
|
| - }
|
| - oSpan = &other->fTs[oStart];
|
| - } while (oSpanT - oSpan->fT < FLT_EPSILON);
|
| - } while (fTs[start].fT <= maxT);
|
| - }
|
| -
|
| - bool canceled(int start, int end) const {
|
| - int min = SkMin32(start, end);
|
| - return fTs[min].fCanceled;
|
| - }
|
| -
|
| - void markAndChaseCoincident(int index, int endIndex, Segment* other) {
|
| - int step = SkSign32(endIndex - index);
|
| - innerCoincidentChase(step, other);
|
| - }
|
| -
|
|
|