blob: c1e2eae831062c8abb337b100baeeb8e1d7a1200 [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
111 && rh.fSegment->other(rh.fEnd) != fSegment) { // and not intersecting
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 SkOpAngle longer = *this;
113 SkOpAngle rhLonger = rh;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000114 if ((longer.lengthen(rh) | rhLonger.lengthen(*this)) // lengthen both
115 && (fUnorderable || !longer.fUnorderable)
116 && (rh.fUnorderable || !rhLonger.fUnorderable)) {
117#if DEBUG_ANGLE
118 bugOut.prepend(" ");
119#endif
120 return COMPARE_RESULT("10 longer.lengthen(rh) ...", longer < rhLonger);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000121 }
122 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000123 SkPath::Verb verb = fSegment->verb();
124 SkPath::Verb rVerb = rh.fSegment->verb();
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000125 if (y_ry != 0) { // if they aren't coincident, look for a stable cross product
126 // at this point, y's are the same sign, neither is zero
127 // and x's are the same sign, or one (or both) is zero
128 double x_ry = x * ry;
129 double rx_y = rx * y;
130 if (!fComputed && !rh.fComputed) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000131 if (!SkDLine::NearRay(x, y, rx, ry) && x_ry != rx_y) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000132 return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx_y);
133 }
134 } else {
135 // if the vector was a result of subdividing a curve, see if it is stable
136 bool sloppy1 = x_ry < rx_y;
137 bool sloppy2 = !sloppy1;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000138 if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1))
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000139 && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2))
140 && sloppy1 != sloppy2) {
141 return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1);
142 }
143 }
144 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000145 if (fSide2 * rh.fSide2 == 0) {
146// SkASSERT(fSide2 + rh.fSide2 != 0); // hitting this assert means coincidence was undetected
147 return COMPARE_RESULT("9a fSide2 * rh.fSide2 == 0 ...", fSide2 < rh.fSide2);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000148 }
149 // at this point, the initial tangent line is nearly coincident
150 // see if edges curl away from each other
151 if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
152 return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide);
153 }
154 if (fUnsortable || rh.fUnsortable) {
155 // even with no solution, return a stable sort
156 return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh);
157 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000158 if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_zero(x))
159 || (rVerb == SkPath::kLine_Verb
160 && approximately_zero(ry) && approximately_zero(rx))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 // See general unsortable comment below. This case can happen when
162 // one line has a non-zero change in t but no change in x and y.
163 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000164 return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000166 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000168 return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000169 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000170 SkASSERT(verb >= SkPath::kQuad_Verb);
171 SkASSERT(rVerb >= SkPath::kQuad_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 // FIXME: until I can think of something better, project a ray from the
173 // end of the shorter tangent to midway between the end points
174 // through both curves and use the resulting angle to sort
175 // 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 +0000176 double len = fTangentPart.normalSquared();
177 double rlen = rh.fTangentPart.normalSquared();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 SkDLine ray;
179 SkIntersections i, ri;
180 int roots, rroots;
181 bool flip = false;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000182 bool useThis;
183 bool leftLessThanRight = fSide > 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 do {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000185 useThis = (len < rlen) ^ flip;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000186 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000187 SkPath::Verb partVerb = useThis ? verb : rVerb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
189 part[2] : part[1];
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000190 ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191 SkASSERT(ray[0] != ray[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000192 roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray);
193 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts(), ray);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194 } while ((roots == 0 || rroots == 0) && (flip ^= true));
195 if (roots == 0 || rroots == 0) {
196 // FIXME: we don't have a solution in this case. The interim solution
197 // is to mark the edges as unsortable, exclude them from this and
198 // future computations, and allow the returned path to be fragmented
199 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000200 return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000201 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000202 SkASSERT(fSide != 0 && rh.fSide != 0);
203 SkASSERT(fSide * rh.fSide > 0); // both are the same sign
204 SkDPoint lLoc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205 double best = SK_ScalarInfinity;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000206#if DEBUG_SORT
207 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n",
208 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray[0].fY,
209 ray[1].fX, ray[1].fY, "-+"[fSide > 0]);
210#endif
211 for (int index = 0; index < roots; ++index) {
212 SkDPoint loc = i.pt(index);
213 SkDVector dxy = loc - ray[0];
214 double dist = dxy.lengthSquared();
215#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000216 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 +0000217 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY);
218#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000219 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000220 lLoc = loc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000221 best = dist;
222 }
223 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000224 flip = false;
225 SkDPoint rLoc;
226 for (int index = 0; index < rroots; ++index) {
227 rLoc = ri.pt(index);
228 SkDVector dxy = rLoc - ray[0];
229 double dist = dxy.lengthSquared();
230#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000231 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 +0000232 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY);
233#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000234 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000235 flip = true;
236 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000237 }
238 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000239 if (flip) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000240 leftLessThanRight = !leftLessThanRight;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000241 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000242 return COMPARE_RESULT("14 leftLessThanRight", leftLessThanRight);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000243}
244
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000245bool SkOpAngle::isHorizontal() const {
246 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000247}
248
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000249// lengthen cannot cross opposite angle
250bool SkOpAngle::lengthen(const SkOpAngle& opp) {
251 if (fSegment->other(fEnd) == opp.fSegment) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000252 return false;
253 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000254 // FIXME: make this a while loop instead and make it as large as possible?
255 int newEnd = fEnd;
256 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000257 fEnd = newEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000258 setSpans();
259 return true;
260 }
261 return false;
262}
263
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000264void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000265 fSegment = segment;
266 fStart = start;
267 fEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 setSpans();
269}
270
caryclark@google.com07393ca2013-04-08 11:47:37 +0000271void SkOpAngle::setSpans() {
caryclark@google.com570863f2013-09-16 15:55:01 +0000272 fUnorderable = fSegment->isTiny(this);
273 fLastMarked = NULL;
274 fUnsortable = false;
275 const SkPoint* pts = fSegment->pts();
276 if (fSegment->verb() != SkPath::kLine_Verb) {
277 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart);
278 fSegment->subDivide(fStart, fStart < fEnd ? fSegment->count() - 1 : 0, &fCurveHalf);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000279 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000280 // FIXME: slight errors in subdivision cause sort trouble later on. As an experiment, try
281 // rounding the curve part to float precision here
282 // fCurvePart.round(fSegment->verb());
283 switch (fSegment->verb()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000284 case SkPath::kLine_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000285 SkASSERT(fStart != fEnd);
286 fCurvePart[0].set(pts[fStart > fEnd]);
287 fCurvePart[1].set(pts[fStart < fEnd]);
288 fComputed = false;
289 // OPTIMIZATION: for pure line compares, we never need fTangentPart.c
290 fTangentPart.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000291 fSide = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000292 fSide2 = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000293 } break;
294 case SkPath::kQuad_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000295 fSide2 = -fTangentHalf.quadPart(*SkTCast<SkDQuad*>(&fCurveHalf));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000296 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
caryclark@google.com570863f2013-09-16 15:55:01 +0000297 fTangentPart.quadEndPoints(quad);
298 fSide = -fTangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000299 if (fComputed && dx() > 0 && approximately_zero(dy())) {
300 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
301 int last = fSegment->count() - 1;
302 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
303 SkLineParameters origTan;
304 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve));
caryclark@google.com570863f2013-09-16 15:55:01 +0000305 if (origTan.dx() <= 0
306 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0)) { // signs match?
307 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000308 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000309 }
310 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000311 } break;
312 case SkPath::kCubic_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000313 double startT = fSegment->t(fStart);
314 fSide2 = -fTangentHalf.cubicPart(fCurveHalf);
315 fTangentPart.cubicEndPoints(fCurvePart);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000316 double testTs[4];
317 // OPTIMIZATION: keep inflections precomputed with cubic segment?
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000318 int testCount = SkDCubic::FindInflections(pts, testTs);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000319 double endT = fSegment->t(fEnd);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000320 double limitT = endT;
321 int index;
322 for (index = 0; index < testCount; ++index) {
323 if (!between(startT, testTs[index], limitT)) {
324 testTs[index] = -1;
325 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000326 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000327 testTs[testCount++] = startT;
328 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000329 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000330 double bestSide = 0;
331 int testCases = (testCount << 1) - 1;
332 index = 0;
333 while (testTs[index] < 0) {
334 ++index;
335 }
336 index <<= 1;
337 for (; index < testCases; ++index) {
338 int testIndex = index >> 1;
339 double testT = testTs[testIndex];
340 if (index & 1) {
341 testT = (testT + testTs[testIndex + 1]) / 2;
342 }
343 // OPTIMIZE: could avoid call for t == startT, endT
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000344 SkDPoint pt = dcubic_xy_at_t(pts, testT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000345 double testSide = fTangentPart.pointDistance(pt);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000346 if (fabs(bestSide) < fabs(testSide)) {
347 bestSide = testSide;
348 }
349 }
350 fSide = -bestSide; // compare sign only
caryclark@google.com570863f2013-09-16 15:55:01 +0000351 SkASSERT(fSide == 0 || fSide2 != 0);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000352 if (fComputed && dx() > 0 && approximately_zero(dy())) {
353 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
354 int last = fSegment->count() - 1;
355 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000356 SkDCubicPair split = origCurve.chopAt(startT);
357 SkLineParameters splitTan;
358 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first());
caryclark@google.com570863f2013-09-16 15:55:01 +0000359 if (splitTan.dx() <= 0) {
360 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000361 fUnsortable = fSegment->isTiny(this);
362 return;
363 }
364 // if one is < 0 and the other is >= 0
caryclark@google.com570863f2013-09-16 15:55:01 +0000365 if (dy() * splitTan.dy() < 0) {
366 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000367 fUnsortable = fSegment->isTiny(this);
368 return;
369 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000370 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000371 } break;
372 default:
373 SkASSERT(0);
374 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000375 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000376 return;
377 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000378 if (fSegment->verb() == SkPath::kLine_Verb) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000379 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000380 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000381 SkASSERT(fStart != fEnd);
382 int smaller = SkMin32(fStart, fEnd);
383 int larger = SkMax32(fStart, fEnd);
384 while (smaller < larger && fSegment->span(smaller).fTiny) {
385 ++smaller;
386 }
387 if (precisely_equal(fSegment->span(smaller).fT, fSegment->span(larger).fT)) {
388 #if DEBUG_UNSORTABLE
389 SkPoint iPt = fSegment->xyAtT(fStart);
390 SkPoint ePt = fSegment->xyAtT(fEnd);
391 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
392 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
393 #endif
394 fUnsortable = true;
395 return;
396 }
397 fUnsortable = fStart < fEnd ? fSegment->span(smaller).fUnsortableStart
398 : fSegment->span(larger).fUnsortableEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000399#if DEBUG_UNSORTABLE
caryclark@google.com570863f2013-09-16 15:55:01 +0000400 if (fUnsortable) {
401 SkPoint iPt = fSegment->xyAtT(smaller);
402 SkPoint ePt = fSegment->xyAtT(larger);
403 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
404 smaller, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
405 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000406#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000407 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000408}
caryclark@google.com570863f2013-09-16 15:55:01 +0000409
410#ifdef SK_DEBUG
411void SkOpAngle::dump() const {
412 SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g)\n", fSegment->debugID(),
413 fSegment->xAtT(fStart), fSegment->yAtT(fStart), fStart, fSegment->span(fStart).fT,
414 fEnd, fSegment->span(fEnd).fT);
415}
416#endif