blob: 5e1d9e745ebf98c14eed55dd874121155801de8f [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);
210 SkASSERT(fSide * rh.fSide > 0); // both are the same sign
211 SkDPoint lLoc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212 double best = SK_ScalarInfinity;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000213#if DEBUG_SORT
214 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n",
215 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray[0].fY,
216 ray[1].fX, ray[1].fY, "-+"[fSide > 0]);
217#endif
218 for (int index = 0; index < roots; ++index) {
219 SkDPoint loc = i.pt(index);
220 SkDVector dxy = loc - ray[0];
221 double dist = dxy.lengthSquared();
222#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000223 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 +0000224 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY);
225#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000226 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000227 lLoc = loc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000228 best = dist;
229 }
230 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000231 flip = false;
232 SkDPoint rLoc;
233 for (int index = 0; index < rroots; ++index) {
234 rLoc = ri.pt(index);
235 SkDVector dxy = rLoc - ray[0];
236 double dist = dxy.lengthSquared();
237#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000238 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 +0000239 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY);
240#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000241 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000242 flip = true;
243 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000244 }
245 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000246 if (flip) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000247 leftLessThanRight = !leftLessThanRight;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000248 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000249 return COMPARE_RESULT("14 leftLessThanRight", leftLessThanRight);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000250}
251
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000252bool SkOpAngle::isHorizontal() const {
253 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000254}
255
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000256// lengthen cannot cross opposite angle
257bool SkOpAngle::lengthen(const SkOpAngle& opp) {
258 if (fSegment->other(fEnd) == opp.fSegment) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000259 return false;
260 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000261 // FIXME: make this a while loop instead and make it as large as possible?
262 int newEnd = fEnd;
263 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000264 fEnd = newEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000265 setSpans();
266 return true;
267 }
268 return false;
269}
270
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000271void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000272 fSegment = segment;
273 fStart = start;
274 fEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000275 setSpans();
276}
277
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278void SkOpAngle::setSpans() {
caryclark@google.com570863f2013-09-16 15:55:01 +0000279 fUnorderable = fSegment->isTiny(this);
280 fLastMarked = NULL;
281 fUnsortable = false;
282 const SkPoint* pts = fSegment->pts();
283 if (fSegment->verb() != SkPath::kLine_Verb) {
284 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart);
285 fSegment->subDivide(fStart, fStart < fEnd ? fSegment->count() - 1 : 0, &fCurveHalf);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000286 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000287 // FIXME: slight errors in subdivision cause sort trouble later on. As an experiment, try
288 // rounding the curve part to float precision here
289 // fCurvePart.round(fSegment->verb());
290 switch (fSegment->verb()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000291 case SkPath::kLine_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000292 SkASSERT(fStart != fEnd);
293 fCurvePart[0].set(pts[fStart > fEnd]);
294 fCurvePart[1].set(pts[fStart < fEnd]);
295 fComputed = false;
296 // OPTIMIZATION: for pure line compares, we never need fTangentPart.c
297 fTangentPart.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 fSide = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000299 fSide2 = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000300 } break;
301 case SkPath::kQuad_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000302 fSide2 = -fTangentHalf.quadPart(*SkTCast<SkDQuad*>(&fCurveHalf));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000303 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
caryclark@google.com570863f2013-09-16 15:55:01 +0000304 fTangentPart.quadEndPoints(quad);
305 fSide = -fTangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000306 if (fComputed && dx() > 0 && approximately_zero(dy())) {
307 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
308 int last = fSegment->count() - 1;
309 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
310 SkLineParameters origTan;
311 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve));
caryclark@google.com570863f2013-09-16 15:55:01 +0000312 if (origTan.dx() <= 0
313 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0)) { // signs match?
314 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000315 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000316 }
317 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000318 } break;
319 case SkPath::kCubic_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +0000320 double startT = fSegment->t(fStart);
321 fSide2 = -fTangentHalf.cubicPart(fCurveHalf);
322 fTangentPart.cubicEndPoints(fCurvePart);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000323 double testTs[4];
324 // OPTIMIZATION: keep inflections precomputed with cubic segment?
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000325 int testCount = SkDCubic::FindInflections(pts, testTs);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000326 double endT = fSegment->t(fEnd);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000327 double limitT = endT;
328 int index;
329 for (index = 0; index < testCount; ++index) {
330 if (!between(startT, testTs[index], limitT)) {
331 testTs[index] = -1;
332 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000333 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000334 testTs[testCount++] = startT;
335 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000336 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000337 double bestSide = 0;
338 int testCases = (testCount << 1) - 1;
339 index = 0;
340 while (testTs[index] < 0) {
341 ++index;
342 }
343 index <<= 1;
344 for (; index < testCases; ++index) {
345 int testIndex = index >> 1;
346 double testT = testTs[testIndex];
347 if (index & 1) {
348 testT = (testT + testTs[testIndex + 1]) / 2;
349 }
350 // OPTIMIZE: could avoid call for t == startT, endT
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000351 SkDPoint pt = dcubic_xy_at_t(pts, testT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000352 double testSide = fTangentPart.pointDistance(pt);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000353 if (fabs(bestSide) < fabs(testSide)) {
354 bestSide = testSide;
355 }
356 }
357 fSide = -bestSide; // compare sign only
caryclark@google.com570863f2013-09-16 15:55:01 +0000358 SkASSERT(fSide == 0 || fSide2 != 0);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000359 if (fComputed && dx() > 0 && approximately_zero(dy())) {
360 SkDCubic origCurve; // can't use segment's curve in place since it may be flipped
361 int last = fSegment->count() - 1;
362 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000363 SkDCubicPair split = origCurve.chopAt(startT);
364 SkLineParameters splitTan;
365 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first());
caryclark@google.com570863f2013-09-16 15:55:01 +0000366 if (splitTan.dx() <= 0) {
367 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000368 fUnsortable = fSegment->isTiny(this);
369 return;
370 }
371 // if one is < 0 and the other is >= 0
caryclark@google.com570863f2013-09-16 15:55:01 +0000372 if (dy() * splitTan.dy() < 0) {
373 fUnorderable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000374 fUnsortable = fSegment->isTiny(this);
375 return;
376 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000377 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000378 } break;
379 default:
380 SkASSERT(0);
381 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000382 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000383 return;
384 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000385 if (fSegment->verb() == SkPath::kLine_Verb) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000386 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000387 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000388 SkASSERT(fStart != fEnd);
389 int smaller = SkMin32(fStart, fEnd);
390 int larger = SkMax32(fStart, fEnd);
391 while (smaller < larger && fSegment->span(smaller).fTiny) {
392 ++smaller;
393 }
394 if (precisely_equal(fSegment->span(smaller).fT, fSegment->span(larger).fT)) {
395 #if DEBUG_UNSORTABLE
396 SkPoint iPt = fSegment->xyAtT(fStart);
397 SkPoint ePt = fSegment->xyAtT(fEnd);
398 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
399 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
400 #endif
401 fUnsortable = true;
402 return;
403 }
404 fUnsortable = fStart < fEnd ? fSegment->span(smaller).fUnsortableStart
405 : fSegment->span(larger).fUnsortableEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000406#if DEBUG_UNSORTABLE
caryclark@google.com570863f2013-09-16 15:55:01 +0000407 if (fUnsortable) {
408 SkPoint iPt = fSegment->xyAtT(smaller);
409 SkPoint ePt = fSegment->xyAtT(larger);
410 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
411 smaller, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
412 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000413#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000414 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000415}
caryclark@google.com570863f2013-09-16 15:55:01 +0000416
417#ifdef SK_DEBUG
418void SkOpAngle::dump() const {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000419 const SkOpSpan& spanStart = fSegment->span(fStart);
420 const SkOpSpan& spanEnd = fSegment->span(fEnd);
421 const SkOpSpan& spanMin = fStart < fEnd ? spanStart : spanEnd;
422 SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g) sumWind=",
423 fSegment->debugID(), fSegment->xAtT(fStart), fSegment->yAtT(fStart),
424 fStart, spanStart.fT, fEnd, spanEnd.fT);
425 SkPathOpsDebug::WindingPrintf(spanMin.fWindSum);
426 SkDebugf(" oppWind=");
427 SkPathOpsDebug::WindingPrintf(spanMin.fOppSum),
428 SkDebugf(" done=%d\n", spanMin.fDone);
caryclark@google.com570863f2013-09-16 15:55:01 +0000429}
430#endif