blob: 742a161f6c49221e373c78f1144ffb8af480fda3 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkIntersections.h"
8#include "SkOpAngle.h"
caryclark@google.comcffbcc32013-06-04 17:59:42 +00009#include "SkOpSegment.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000010#include "SkPathOpsCurve.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000011#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
caryclark@google.comcffbcc32013-06-04 17:59:42 +000013#if DEBUG_ANGLE
14#include "SkString.h"
15
16static const char funcName[] = "SkOpSegment::operator<";
17static const int bugChar = strlen(funcName) + 1;
caryclark@google.coma5e55922013-05-07 18:51:31 +000018#endif
19
caryclark@google.comcffbcc32013-06-04 17:59:42 +000020/* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest
21 positive y. The largest angle has a positive x and a zero y. */
caryclark@google.com07393ca2013-04-08 11:47:37 +000022
caryclark@google.comcffbcc32013-06-04 17:59:42 +000023#if DEBUG_ANGLE
24 static bool CompareResult(SkString* bugOut, const char* append, bool compare) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +000025 bugOut->appendf("%s", append);
caryclark@google.comcffbcc32013-06-04 17:59:42 +000026 bugOut->writable_str()[bugChar] = "><"[compare];
27 SkDebugf("%s\n", bugOut->c_str());
28 return compare;
29 }
30
31 #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare)
32#else
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +000033 #define COMPARE_RESULT(append, compare) compare
caryclark@google.comcffbcc32013-06-04 17:59:42 +000034#endif
35
36bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const{
37 double absX = fabs(x);
38 double absY = fabs(y);
39 double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
40 int exponent;
41 (void) frexp(length, &exponent);
42 double epsilon = ldexp(FLT_EPSILON, exponent);
43 SkPath::Verb verb = fSegment->verb();
44 SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb);
45 // FIXME: the quad and cubic factors are made up ; determine actual values
46 double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon;
47 double xSlop = slop;
48 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
49 double x1 = x - xSlop;
50 double y1 = y + ySlop;
51 double x_ry1 = x1 * ry;
52 double rx_y1 = rx * y1;
53 *result = x_ry1 < rx_y1;
54 double x2 = x + xSlop;
55 double y2 = y - ySlop;
56 double x_ry2 = x2 * ry;
57 double rx_y2 = rx * y2;
58 bool less2 = x_ry2 < rx_y2;
59 return *result == less2;
60}
61
62/*
caryclark@google.com07393ca2013-04-08 11:47:37 +000063for quads and cubics, set up a parameterized line (e.g. LineParameters )
64for points [0] to [1]. See if point [2] is on that line, or on one side
65or the other. If it both quads' end points are on the same side, choose
66the shorter tangent. If the tangents are equal, choose the better second
67tangent angle
68
caryclark@google.comcffbcc32013-06-04 17:59:42 +000069FIXME: maybe I could set up LineParameters lazily
caryclark@google.com07393ca2013-04-08 11:47:37 +000070*/
caryclark@google.comcffbcc32013-06-04 17:59:42 +000071bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; rh: right-hand
caryclark@google.coma5e55922013-05-07 18:51:31 +000072 double y = dy();
caryclark@google.coma5e55922013-05-07 18:51:31 +000073 double ry = rh.dy();
caryclark@google.comcffbcc32013-06-04 17:59:42 +000074#if DEBUG_ANGLE
75 SkString bugOut;
76 bugOut.printf("%s _ id=%d segId=%d tStart=%1.9g tEnd=%1.9g"
77 " | id=%d segId=%d tStart=%1.9g tEnd=%1.9g ", funcName,
78 fID, fSegment->debugID(), fSegment->t(fStart), fSegment->t(fEnd),
79 rh.fID, rh.fSegment->debugID(), rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
80#endif
81 double y_ry = y * ry;
82 if (y_ry < 0) { // if y's are opposite signs, we can do a quick return
83 return COMPARE_RESULT("1 y * ry < 0", y < 0);
caryclark@google.coma5e55922013-05-07 18:51:31 +000084 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +000085 // at this point, both y's must be the same sign, or one (or both) is zero
86 double x = dx();
87 double rx = rh.dx();
88 if (x * rx < 0) { // if x's are opposite signs, use y to determine first or second half
89 if (y < 0 && ry < 0) { // if y's are negative, lh x is smaller if positive
90 return COMPARE_RESULT("2 x_rx < 0 && y < 0 ...", x > 0);
91 }
92 if (y >= 0 && ry >= 0) { // if y's are zero or positive, lh x is smaller if negative
93 return COMPARE_RESULT("3 x_rx < 0 && y >= 0 ...", x < 0);
94 }
95 SkASSERT((y == 0) ^ (ry == 0)); // if one y is zero and one is negative, neg y is smaller
96 return COMPARE_RESULT("4 x_rx < 0 && y == 0 ...", y < 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +000098 // at this point, both x's must be the same sign, or one (or both) is zero
99 if (y_ry == 0) { // if either y is zero
100 if (y + ry < 0) { // if the other y is less than zero, it must be smaller
101 return COMPARE_RESULT("5 y_ry == 0 && y + ry < 0", y < 0);
102 }
103 if (y + ry > 0) { // if a y is greater than zero and an x is positive, non zero is smaller
104 return COMPARE_RESULT("6 y_ry == 0 && y + ry > 0", (x + rx > 0) ^ (y == 0));
105 }
106 // at this point, both y's are zero, so lines are coincident or one is degenerate
107 SkASSERT(x * rx != 0); // and a degenerate line should haven't gotten this far
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000108 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000109 // see if either curve can be lengthened before trying the tangent
110 if (fSegment->other(fEnd) != rh.fSegment // tangents not absolutely identical
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000111 && rh.fSegment->other(rh.fEnd) != fSegment
skia.committer@gmail.comf61ebc02013-11-22 07:02:24 +0000112 && y != -DBL_EPSILON
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000113 && ry != -DBL_EPSILON) { // and not intersecting
caryclark@google.com07393ca2013-04-08 11:47:37 +0000114 SkOpAngle longer = *this;
115 SkOpAngle rhLonger = rh;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000116 if ((longer.lengthen(rh) | rhLonger.lengthen(*this)) // lengthen both
117 && (fUnorderable || !longer.fUnorderable)
118 && (rh.fUnorderable || !rhLonger.fUnorderable)) {
119#if DEBUG_ANGLE
120 bugOut.prepend(" ");
121#endif
122 return COMPARE_RESULT("10 longer.lengthen(rh) ...", longer < rhLonger);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 }
124 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000125 SkPath::Verb verb = fSegment->verb();
126 SkPath::Verb rVerb = rh.fSegment->verb();
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000127 if (y_ry != 0) { // if they aren't coincident, look for a stable cross product
128 // at this point, y's are the same sign, neither is zero
129 // and x's are the same sign, or one (or both) is zero
130 double x_ry = x * ry;
131 double rx_y = rx * y;
132 if (!fComputed && !rh.fComputed) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000133 if (!SkDLine::NearRay(x, y, rx, ry) && x_ry != rx_y) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000134 return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx_y);
135 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000136 if (fSide2 == 0 && rh.fSide2 == 0) {
137 return COMPARE_RESULT("7a !fComputed && !rh.fComputed", x_ry < rx_y);
138 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000139 } else {
140 // if the vector was a result of subdividing a curve, see if it is stable
141 bool sloppy1 = x_ry < rx_y;
142 bool sloppy2 = !sloppy1;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000143 if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1))
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000144 && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2))
145 && sloppy1 != sloppy2) {
146 return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1);
147 }
148 }
149 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000150 if (fSide2 * rh.fSide2 == 0) { // one is zero
151#if DEBUG_ANGLE
152 if (fSide2 == rh.fSide2 && y_ry) { // both is zero; coincidence was undetected
153 SkDebugf("%s coincidence!\n", __FUNCTION__);
154 }
155#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000156 return COMPARE_RESULT("9a fSide2 * rh.fSide2 == 0 ...", fSide2 < rh.fSide2);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000157 }
158 // at this point, the initial tangent line is nearly coincident
159 // see if edges curl away from each other
160 if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
161 return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide);
162 }
163 if (fUnsortable || rh.fUnsortable) {
164 // even with no solution, return a stable sort
165 return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh);
166 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000167 if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_zero(x))
168 || (rVerb == SkPath::kLine_Verb
169 && approximately_zero(ry) && approximately_zero(rx))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000170 // See general unsortable comment below. This case can happen when
171 // one line has a non-zero change in t but no change in x and y.
172 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000173 return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000175 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000176 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000177 return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000179 SkASSERT(verb >= SkPath::kQuad_Verb);
180 SkASSERT(rVerb >= SkPath::kQuad_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181 // FIXME: until I can think of something better, project a ray from the
182 // end of the shorter tangent to midway between the end points
183 // through both curves and use the resulting angle to sort
184 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
caryclark@google.com570863f2013-09-16 15:55:01 +0000185 double len = fTangentPart.normalSquared();
186 double rlen = rh.fTangentPart.normalSquared();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000187 SkDLine ray;
188 SkIntersections i, ri;
189 int roots, rroots;
190 bool flip = false;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000191 bool useThis;
192 bool leftLessThanRight = fSide > 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000193 do {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000194 useThis = (len < rlen) ^ flip;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000196 SkPath::Verb partVerb = useThis ? verb : rVerb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000197 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
198 part[2] : part[1];
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000199 ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 SkASSERT(ray[0] != ray[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000201 roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray);
202 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts(), ray);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203 } while ((roots == 0 || rroots == 0) && (flip ^= true));
204 if (roots == 0 || rroots == 0) {
205 // FIXME: we don't have a solution in this case. The interim solution
206 // is to mark the edges as unsortable, exclude them from this and
207 // future computations, and allow the returned path to be fragmented
208 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000209 return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000211 SkASSERT(fSide != 0 && rh.fSide != 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000212 if (fSide * rh.fSide < 0) {
213 fUnsortable = true;
214 return COMPARE_RESULT("14 fSide * rh.fSide < 0", this < &rh);
215 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000216 SkDPoint lLoc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000217 double best = SK_ScalarInfinity;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000218#if DEBUG_SORT
219 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n",
220 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray[0].fY,
221 ray[1].fX, ray[1].fY, "-+"[fSide > 0]);
222#endif
223 for (int index = 0; index < roots; ++index) {
224 SkDPoint loc = i.pt(index);
225 SkDVector dxy = loc - ray[0];
226 double dist = dxy.lengthSquared();
227#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000228 SkDebugf("best=%1.9g dist=%1.9g loc={%1.9g,%1.9g} dxy={%1.9g,%1.9g}\n",
caryclark@google.coma5e55922013-05-07 18:51:31 +0000229 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY);
230#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000232 lLoc = loc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233 best = dist;
234 }
235 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000236 flip = false;
237 SkDPoint rLoc;
238 for (int index = 0; index < rroots; ++index) {
239 rLoc = ri.pt(index);
240 SkDVector dxy = rLoc - ray[0];
241 double dist = dxy.lengthSquared();
242#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000243 SkDebugf("best=%1.9g dist=%1.9g %c=(fSide < 0) rLoc={%1.9g,%1.9g} dxy={%1.9g,%1.9g}\n",
caryclark@google.coma5e55922013-05-07 18:51:31 +0000244 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY);
245#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000246 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000247 flip = true;
248 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000249 }
250 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000251 if (flip) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000252 leftLessThanRight = !leftLessThanRight;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000253 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000254 return COMPARE_RESULT("15 leftLessThanRight", leftLessThanRight);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255}
256
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000257bool SkOpAngle::isHorizontal() const {
258 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000259}
260
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000261// lengthen cannot cross opposite angle
262bool SkOpAngle::lengthen(const SkOpAngle& opp) {
263 if (fSegment->other(fEnd) == opp.fSegment) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000264 return false;
265 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000266 // FIXME: make this a while loop instead and make it as large as possible?
267 int newEnd = fEnd;
268 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000269 fEnd = newEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 setSpans();
271 return true;
272 }
273 return false;
274}
275
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000276void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277 fSegment = segment;
278 fStart = start;
279 fEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000280 setSpans();
281}
282
caryclark@google.com07393ca2013-04-08 11:47:37 +0000283void SkOpAngle::setSpans() {
caryclark@google.com570863f2013-09-16 15:55:01 +0000284 fUnorderable = fSegment->isTiny(this);
285 fLastMarked = NULL;
286 fUnsortable = false;
287 const SkPoint* pts = fSegment->pts();
288 if (fSegment->verb() != SkPath::kLine_Verb) {
289 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart);
290 fSegment->subDivide(fStart, fStart < fEnd ? fSegment->count() - 1 : 0, &fCurveHalf);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000291 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000292 // FIXME: slight errors in subdivision cause sort trouble later on. As an experiment, try
293 // rounding the curve part to float precision here
294 // fCurvePart.round(fSegment->verb());
295 switch (fSegment->verb()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000296 case SkPath::kLine_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000297 SkASSERT(fStart != fEnd);
298 fCurvePart[0].set(pts[fStart > fEnd]);
299 fCurvePart[1].set(pts[fStart < fEnd]);
300 fComputed = false;
301 // OPTIMIZATION: for pure line compares, we never need fTangentPart.c
302 fTangentPart.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000303 fSide = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000304 fSide2 = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000305 } break;
306 case SkPath::kQuad_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000307 fSide2 = -fTangentHalf.quadPart(*SkTCast<SkDQuad*>(&fCurveHalf));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000308 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
caryclark@google.com570863f2013-09-16 15:55:01 +0000309 fTangentPart.quadEndPoints(quad);
310 fSide = -fTangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000311 if (fComputed && dx() > 0 && approximately_zero(dy())) {
312 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
313 int last = fSegment->count() - 1;
314 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
315 SkLineParameters origTan;
316 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve));
caryclark@google.com570863f2013-09-16 15:55:01 +0000317 if (origTan.dx() <= 0
318 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0)) { // signs match?
319 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000320 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000321 }
322 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000323 } break;
324 case SkPath::kCubic_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000325 double startT = fSegment->t(fStart);
326 fSide2 = -fTangentHalf.cubicPart(fCurveHalf);
327 fTangentPart.cubicEndPoints(fCurvePart);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000328 double testTs[4];
329 // OPTIMIZATION: keep inflections precomputed with cubic segment?
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000330 int testCount = SkDCubic::FindInflections(pts, testTs);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000331 double endT = fSegment->t(fEnd);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000332 double limitT = endT;
333 int index;
334 for (index = 0; index < testCount; ++index) {
335 if (!between(startT, testTs[index], limitT)) {
336 testTs[index] = -1;
337 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000338 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000339 testTs[testCount++] = startT;
340 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000341 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000342 double bestSide = 0;
343 int testCases = (testCount << 1) - 1;
344 index = 0;
345 while (testTs[index] < 0) {
346 ++index;
347 }
348 index <<= 1;
349 for (; index < testCases; ++index) {
350 int testIndex = index >> 1;
351 double testT = testTs[testIndex];
352 if (index & 1) {
353 testT = (testT + testTs[testIndex + 1]) / 2;
354 }
355 // OPTIMIZE: could avoid call for t == startT, endT
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000356 SkDPoint pt = dcubic_xy_at_t(pts, testT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000357 double testSide = fTangentPart.pointDistance(pt);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000358 if (fabs(bestSide) < fabs(testSide)) {
359 bestSide = testSide;
360 }
361 }
362 fSide = -bestSide; // compare sign only
caryclark@google.com570863f2013-09-16 15:55:01 +0000363 SkASSERT(fSide == 0 || fSide2 != 0);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000364 if (fComputed && dx() > 0 && approximately_zero(dy())) {
365 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
366 int last = fSegment->count() - 1;
367 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000368 SkDCubicPair split = origCurve.chopAt(startT);
369 SkLineParameters splitTan;
370 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first());
caryclark@google.com570863f2013-09-16 15:55:01 +0000371 if (splitTan.dx() <= 0) {
372 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000373 fUnsortable = fSegment->isTiny(this);
374 return;
375 }
376 // if one is < 0 and the other is >= 0
caryclark@google.com570863f2013-09-16 15:55:01 +0000377 if (dy() * splitTan.dy() < 0) {
378 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000379 fUnsortable = fSegment->isTiny(this);
380 return;
381 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000382 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000383 } break;
384 default:
385 SkASSERT(0);
386 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000387 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000388 return;
389 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000390 if (fSegment->verb() == SkPath::kLine_Verb) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000391 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000392 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000393 SkASSERT(fStart != fEnd);
394 int smaller = SkMin32(fStart, fEnd);
395 int larger = SkMax32(fStart, fEnd);
396 while (smaller < larger && fSegment->span(smaller).fTiny) {
397 ++smaller;
398 }
399 if (precisely_equal(fSegment->span(smaller).fT, fSegment->span(larger).fT)) {
400 #if DEBUG_UNSORTABLE
401 SkPoint iPt = fSegment->xyAtT(fStart);
402 SkPoint ePt = fSegment->xyAtT(fEnd);
403 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
404 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
405 #endif
406 fUnsortable = true;
407 return;
408 }
409 fUnsortable = fStart < fEnd ? fSegment->span(smaller).fUnsortableStart
410 : fSegment->span(larger).fUnsortableEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000411#if DEBUG_UNSORTABLE
caryclark@google.com570863f2013-09-16 15:55:01 +0000412 if (fUnsortable) {
413 SkPoint iPt = fSegment->xyAtT(smaller);
414 SkPoint ePt = fSegment->xyAtT(larger);
415 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
416 smaller, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
417 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000418#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000419 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000420}
caryclark@google.com570863f2013-09-16 15:55:01 +0000421
422#ifdef SK_DEBUG
423void SkOpAngle::dump() const {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000424 const SkOpSpan& spanStart = fSegment->span(fStart);
425 const SkOpSpan& spanEnd = fSegment->span(fEnd);
426 const SkOpSpan& spanMin = fStart < fEnd ? spanStart : spanEnd;
427 SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g) sumWind=",
428 fSegment->debugID(), fSegment->xAtT(fStart), fSegment->yAtT(fStart),
429 fStart, spanStart.fT, fEnd, spanEnd.fT);
430 SkPathOpsDebug::WindingPrintf(spanMin.fWindSum);
431 SkDebugf(" oppWind=");
432 SkPathOpsDebug::WindingPrintf(spanMin.fOppSum),
433 SkDebugf(" done=%d\n", spanMin.fDone);
caryclark@google.com570863f2013-09-16 15:55:01 +0000434}
435#endif