blob: 4144add6fb2008da91a058fe9e4ede7376a513df [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 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000134 if (fSide2 == 0 && rh.fSide2 == 0) {
135 return COMPARE_RESULT("7a !fComputed && !rh.fComputed", x_ry < rx_y);
136 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000137 } else {
138 // if the vector was a result of subdividing a curve, see if it is stable
139 bool sloppy1 = x_ry < rx_y;
140 bool sloppy2 = !sloppy1;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000141 if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1))
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000142 && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2))
143 && sloppy1 != sloppy2) {
144 return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1);
145 }
146 }
147 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000148 if (fSide2 * rh.fSide2 == 0) { // one is zero
149#if DEBUG_ANGLE
150 if (fSide2 == rh.fSide2 && y_ry) { // both is zero; coincidence was undetected
151 SkDebugf("%s coincidence!\n", __FUNCTION__);
152 }
153#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000154 return COMPARE_RESULT("9a fSide2 * rh.fSide2 == 0 ...", fSide2 < rh.fSide2);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000155 }
156 // at this point, the initial tangent line is nearly coincident
157 // see if edges curl away from each other
158 if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
159 return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide);
160 }
161 if (fUnsortable || rh.fUnsortable) {
162 // even with no solution, return a stable sort
163 return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh);
164 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000165 if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_zero(x))
166 || (rVerb == SkPath::kLine_Verb
167 && approximately_zero(ry) && approximately_zero(rx))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000168 // See general unsortable comment below. This case can happen when
169 // one line has a non-zero change in t but no change in x and y.
170 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000171 return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000173 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000175 return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000176 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000177 SkASSERT(verb >= SkPath::kQuad_Verb);
178 SkASSERT(rVerb >= SkPath::kQuad_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 // FIXME: until I can think of something better, project a ray from the
180 // end of the shorter tangent to midway between the end points
181 // through both curves and use the resulting angle to sort
182 // 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 +0000183 double len = fTangentPart.normalSquared();
184 double rlen = rh.fTangentPart.normalSquared();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185 SkDLine ray;
186 SkIntersections i, ri;
187 int roots, rroots;
188 bool flip = false;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000189 bool useThis;
190 bool leftLessThanRight = fSide > 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191 do {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000192 useThis = (len < rlen) ^ flip;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000193 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000194 SkPath::Verb partVerb = useThis ? verb : rVerb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
196 part[2] : part[1];
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000197 ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198 SkASSERT(ray[0] != ray[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000199 roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray);
200 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts(), ray);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000201 } while ((roots == 0 || rroots == 0) && (flip ^= true));
202 if (roots == 0 || rroots == 0) {
203 // FIXME: we don't have a solution in this case. The interim solution
204 // is to mark the edges as unsortable, exclude them from this and
205 // future computations, and allow the returned path to be fragmented
206 fUnsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000207 return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000209 SkASSERT(fSide != 0 && rh.fSide != 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000210 if (fSide * rh.fSide < 0) {
211 fUnsortable = true;
212 return COMPARE_RESULT("14 fSide * rh.fSide < 0", this < &rh);
213 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000214 SkDPoint lLoc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000215 double best = SK_ScalarInfinity;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000216#if DEBUG_SORT
217 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n",
218 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray[0].fY,
219 ray[1].fX, ray[1].fY, "-+"[fSide > 0]);
220#endif
221 for (int index = 0; index < roots; ++index) {
222 SkDPoint loc = i.pt(index);
223 SkDVector dxy = loc - ray[0];
224 double dist = dxy.lengthSquared();
225#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000226 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 +0000227 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY);
228#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000229 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000230 lLoc = loc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 best = dist;
232 }
233 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000234 flip = false;
235 SkDPoint rLoc;
236 for (int index = 0; index < rroots; ++index) {
237 rLoc = ri.pt(index);
238 SkDVector dxy = rLoc - ray[0];
239 double dist = dxy.lengthSquared();
240#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000241 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 +0000242 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY);
243#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000244 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000245 flip = true;
246 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000247 }
248 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000249 if (flip) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000250 leftLessThanRight = !leftLessThanRight;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000251 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000252 return COMPARE_RESULT("15 leftLessThanRight", leftLessThanRight);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000253}
254
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000255bool SkOpAngle::isHorizontal() const {
256 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000257}
258
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000259// lengthen cannot cross opposite angle
260bool SkOpAngle::lengthen(const SkOpAngle& opp) {
261 if (fSegment->other(fEnd) == opp.fSegment) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000262 return false;
263 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000264 // FIXME: make this a while loop instead and make it as large as possible?
265 int newEnd = fEnd;
266 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000267 fEnd = newEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 setSpans();
269 return true;
270 }
271 return false;
272}
273
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000274void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000275 fSegment = segment;
276 fStart = start;
277 fEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278 setSpans();
279}
280
caryclark@google.com07393ca2013-04-08 11:47:37 +0000281void SkOpAngle::setSpans() {
caryclark@google.com570863f2013-09-16 15:55:01 +0000282 fUnorderable = fSegment->isTiny(this);
283 fLastMarked = NULL;
284 fUnsortable = false;
285 const SkPoint* pts = fSegment->pts();
286 if (fSegment->verb() != SkPath::kLine_Verb) {
287 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart);
288 fSegment->subDivide(fStart, fStart < fEnd ? fSegment->count() - 1 : 0, &fCurveHalf);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000289 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000290 // FIXME: slight errors in subdivision cause sort trouble later on. As an experiment, try
291 // rounding the curve part to float precision here
292 // fCurvePart.round(fSegment->verb());
293 switch (fSegment->verb()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000294 case SkPath::kLine_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000295 SkASSERT(fStart != fEnd);
296 fCurvePart[0].set(pts[fStart > fEnd]);
297 fCurvePart[1].set(pts[fStart < fEnd]);
298 fComputed = false;
299 // OPTIMIZATION: for pure line compares, we never need fTangentPart.c
300 fTangentPart.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301 fSide = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000302 fSide2 = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000303 } break;
304 case SkPath::kQuad_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000305 fSide2 = -fTangentHalf.quadPart(*SkTCast<SkDQuad*>(&fCurveHalf));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000306 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
caryclark@google.com570863f2013-09-16 15:55:01 +0000307 fTangentPart.quadEndPoints(quad);
308 fSide = -fTangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000309 if (fComputed && dx() > 0 && approximately_zero(dy())) {
310 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
311 int last = fSegment->count() - 1;
312 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
313 SkLineParameters origTan;
314 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve));
caryclark@google.com570863f2013-09-16 15:55:01 +0000315 if (origTan.dx() <= 0
316 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0)) { // signs match?
317 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000318 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000319 }
320 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000321 } break;
322 case SkPath::kCubic_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000323 double startT = fSegment->t(fStart);
324 fSide2 = -fTangentHalf.cubicPart(fCurveHalf);
325 fTangentPart.cubicEndPoints(fCurvePart);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000326 double testTs[4];
327 // OPTIMIZATION: keep inflections precomputed with cubic segment?
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000328 int testCount = SkDCubic::FindInflections(pts, testTs);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000329 double endT = fSegment->t(fEnd);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000330 double limitT = endT;
331 int index;
332 for (index = 0; index < testCount; ++index) {
333 if (!between(startT, testTs[index], limitT)) {
334 testTs[index] = -1;
335 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000336 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000337 testTs[testCount++] = startT;
338 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000339 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000340 double bestSide = 0;
341 int testCases = (testCount << 1) - 1;
342 index = 0;
343 while (testTs[index] < 0) {
344 ++index;
345 }
346 index <<= 1;
347 for (; index < testCases; ++index) {
348 int testIndex = index >> 1;
349 double testT = testTs[testIndex];
350 if (index & 1) {
351 testT = (testT + testTs[testIndex + 1]) / 2;
352 }
353 // OPTIMIZE: could avoid call for t == startT, endT
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000354 SkDPoint pt = dcubic_xy_at_t(pts, testT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000355 double testSide = fTangentPart.pointDistance(pt);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000356 if (fabs(bestSide) < fabs(testSide)) {
357 bestSide = testSide;
358 }
359 }
360 fSide = -bestSide; // compare sign only
caryclark@google.com570863f2013-09-16 15:55:01 +0000361 SkASSERT(fSide == 0 || fSide2 != 0);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000362 if (fComputed && dx() > 0 && approximately_zero(dy())) {
363 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
364 int last = fSegment->count() - 1;
365 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000366 SkDCubicPair split = origCurve.chopAt(startT);
367 SkLineParameters splitTan;
368 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first());
caryclark@google.com570863f2013-09-16 15:55:01 +0000369 if (splitTan.dx() <= 0) {
370 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000371 fUnsortable = fSegment->isTiny(this);
372 return;
373 }
374 // if one is < 0 and the other is >= 0
caryclark@google.com570863f2013-09-16 15:55:01 +0000375 if (dy() * splitTan.dy() < 0) {
376 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000377 fUnsortable = fSegment->isTiny(this);
378 return;
379 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000380 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000381 } break;
382 default:
383 SkASSERT(0);
384 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000385 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000386 return;
387 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000388 if (fSegment->verb() == SkPath::kLine_Verb) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000389 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000390 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000391 SkASSERT(fStart != fEnd);
392 int smaller = SkMin32(fStart, fEnd);
393 int larger = SkMax32(fStart, fEnd);
394 while (smaller < larger && fSegment->span(smaller).fTiny) {
395 ++smaller;
396 }
397 if (precisely_equal(fSegment->span(smaller).fT, fSegment->span(larger).fT)) {
398 #if DEBUG_UNSORTABLE
399 SkPoint iPt = fSegment->xyAtT(fStart);
400 SkPoint ePt = fSegment->xyAtT(fEnd);
401 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
402 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
403 #endif
404 fUnsortable = true;
405 return;
406 }
407 fUnsortable = fStart < fEnd ? fSegment->span(smaller).fUnsortableStart
408 : fSegment->span(larger).fUnsortableEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000409#if DEBUG_UNSORTABLE
caryclark@google.com570863f2013-09-16 15:55:01 +0000410 if (fUnsortable) {
411 SkPoint iPt = fSegment->xyAtT(smaller);
412 SkPoint ePt = fSegment->xyAtT(larger);
413 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
414 smaller, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
415 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000416#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000417 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000418}
caryclark@google.com570863f2013-09-16 15:55:01 +0000419
420#ifdef SK_DEBUG
421void SkOpAngle::dump() const {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000422 const SkOpSpan& spanStart = fSegment->span(fStart);
423 const SkOpSpan& spanEnd = fSegment->span(fEnd);
424 const SkOpSpan& spanMin = fStart < fEnd ? spanStart : spanEnd;
425 SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g) sumWind=",
426 fSegment->debugID(), fSegment->xAtT(fStart), fSegment->yAtT(fStart),
427 fStart, spanStart.fT, fEnd, spanEnd.fT);
428 SkPathOpsDebug::WindingPrintf(spanMin.fWindSum);
429 SkDebugf(" oppWind=");
430 SkPathOpsDebug::WindingPrintf(spanMin.fOppSum),
431 SkDebugf(" done=%d\n", spanMin.fDone);
caryclark@google.com570863f2013-09-16 15:55:01 +0000432}
433#endif