blob: f41ef234d49d553fdbbf0c00272712e2a6b02192 [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 */
caryclark@google.com07393ca2013-04-08 11:47:37 +00007#include "SkOpAngle.h"
caryclark@google.comcffbcc32013-06-04 17:59:42 +00008#include "SkOpSegment.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkPathOpsCurve.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000010#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
caryclark@google.comcffbcc32013-06-04 17:59:42 +000012/* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest
13 positive y. The largest angle has a positive x and a zero y. */
caryclark@google.com07393ca2013-04-08 11:47:37 +000014
caryclark@google.comcffbcc32013-06-04 17:59:42 +000015#if DEBUG_ANGLE
caryclark54359292015-03-26 07:52:43 -070016 static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, int append,
17 bool compare) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000018 SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append);
caryclark54359292015-03-26 07:52:43 -070019 SkDebugf("%sPart %s\n", func, bugPart[0].c_str());
20 SkDebugf("%sPart %s\n", func, bugPart[1].c_str());
21 SkDebugf("%sPart %s\n", func, bugPart[2].c_str());
caryclark@google.comcffbcc32013-06-04 17:59:42 +000022 return compare;
23 }
24
caryclark54359292015-03-26 07:52:43 -070025 #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
26 compare)
caryclark@google.comcffbcc32013-06-04 17:59:42 +000027#else
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +000028 #define COMPARE_RESULT(append, compare) compare
caryclark@google.comcffbcc32013-06-04 17:59:42 +000029#endif
30
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000031/* quarter angle values for sector
32
3331 x > 0, y == 0 horizontal line (to the right)
340 x > 0, y == epsilon quad/cubic horizontal tangent eventually going +y
351 x > 0, y > 0, x > y nearer horizontal angle
362 x + e == y quad/cubic 45 going horiz
373 x > 0, y > 0, x == y 45 angle
384 x == y + e quad/cubic 45 going vert
395 x > 0, y > 0, x < y nearer vertical angle
406 x == epsilon, y > 0 quad/cubic vertical tangent eventually going +x
417 x == 0, y > 0 vertical line (to the top)
42
43 8 7 6
44 9 | 5
45 10 | 4
46 11 | 3
47 12 \ | / 2
48 13 | 1
49 14 | 0
50 15 --------------+------------- 31
51 16 | 30
52 17 | 29
53 18 / | \ 28
54 19 | 27
55 20 | 26
56 21 | 25
57 22 23 24
58*/
59
60// return true if lh < this < rh
caryclark54359292015-03-26 07:52:43 -070061bool SkOpAngle::after(SkOpAngle* test) {
62 SkOpAngle* lh = test;
63 SkOpAngle* rh = lh->fNext;
64 SkASSERT(lh != rh);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000065#if DEBUG_ANGLE
66 SkString bugOut;
Cary Clarkd2eb5812017-01-18 11:00:57 -050067 this->debugAfter(lh, rh, &bugOut);
caryclark54359292015-03-26 07:52:43 -070068 SkString bugPart[3] = { lh->debugPart(), this->debugPart(), rh->debugPart() };
Cary Clarkd2eb5812017-01-18 11:00:57 -050069#if 0 // convenient place to set a breakpoint to trace through a specific angle compare
70 if (lh->debugID() == 4 && this->debugID() == 16 && rh->debugID() == 5) {
71 SkDebugf("");
72 }
73#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000074#endif
caryclark54359292015-03-26 07:52:43 -070075 if (lh->fComputeSector && !lh->computeSector()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000076 return COMPARE_RESULT(1, true);
77 }
caryclark54359292015-03-26 07:52:43 -070078 if (fComputeSector && !this->computeSector()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000079 return COMPARE_RESULT(2, true);
80 }
caryclark54359292015-03-26 07:52:43 -070081 if (rh->fComputeSector && !rh->computeSector()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000082 return COMPARE_RESULT(3, true);
83 }
84#if DEBUG_ANGLE // reset bugOut with computed sectors
Cary Clarkd2eb5812017-01-18 11:00:57 -050085 this->debugAfter(lh, rh, &bugOut);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000086#endif
Cary Clarkd2eb5812017-01-18 11:00:57 -050087 /* If the curve pairs share a point, the computed sector is valid. Otherwise, the sectors must
88 be sufficiently different that translating them won't change the sort order. For instance,
89 curves with different origins may mis-sort if the computed sectors are 1 and 5.
90
91 Curves with different origins have more information though -- there are more ways for their
92 convex hulls not to overlap. Try to resolve different origins directly before translating
93 one curve to share the opposite's origin.
94 */
95 bool lrOverlap, ltrOverlap;
96 SkDVector lhOffset = fOriginalCurvePart[0] - lh->fOriginalCurvePart[0];
97 bool lhHasOffset = lhOffset.fX || lhOffset.fY;
98 SkDVector rhOffset = fOriginalCurvePart[0] - rh->fOriginalCurvePart[0];
99 bool rhHasOffset = rhOffset.fX || rhOffset.fY;
100 int lhStart, lhEnd, thStart, thEnd, rhStart, rhEnd;
101 bool lhX0, thX0, rhX0;
102 if (lhHasOffset | rhHasOffset) {
103 lhX0 = lh->sectorRange(&lhStart, &lhEnd, lhHasOffset);
104 thX0 = this->sectorRange(&thStart, &thEnd, false);
105 rhX0 = rh->sectorRange(&rhStart, &rhEnd, rhHasOffset);
106 lrOverlap = lhX0 + rhX0 + (lhStart <= rhEnd) + (rhStart <= lhEnd) >= 2;
107 ltrOverlap = thX0 + lhX0 + (lhStart <= thEnd) + (thStart <= lhEnd) >= 2
108 || rhX0 + thX0 + (thStart <= rhEnd) + (rhStart <= thEnd) >= 2;
109 } else {
110 lrOverlap = lh->fSectorMask & rh->fSectorMask;
111 ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
112 }
113 if (!lrOverlap & !ltrOverlap) { // no lh/this/rh sector overlap
114 return COMPARE_RESULT(4, (lh->fSectorEnd > rh->fSectorStart)
115 ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
116 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000117 int lrOrder; // set to -1 if either order works
Cary Clarkd2eb5812017-01-18 11:00:57 -0500118 fPart.fCurve = fOriginalCurvePart;
119 lh->fPart.fCurve = lh->fOriginalCurvePart;
120 rh->fPart.fCurve = rh->fOriginalCurvePart;
121 if (lhHasOffset | rhHasOffset) {
122 bool lhSweepsCCW = lh->sweepsCCW();
123 bool thSweepsCCW = this->sweepsCCW();
124 bool rhSweepsCCW = rh->sweepsCCW();
125 Turn thStartFromLhEnd = this->ccwOf(lh, lhSweepsCCW, !thSweepsCCW);
126 Turn thEndFromRhStart = this->ccwOf(rh, !rhSweepsCCW, thSweepsCCW);
127 if (!lrOverlap && Turn::kCcw == thStartFromLhEnd && Turn::kCw == thEndFromRhStart) {
128 if (!this->sweepContains(lh) && !this->sweepContains(rh)) {
129 return COMPARE_RESULT(5, true);
130 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000131 }
Cary Clarkd2eb5812017-01-18 11:00:57 -0500132 Turn lhStartFromRhStart = lh->ccwOf(rh, !rhSweepsCCW, !lhSweepsCCW);
133 Turn lhEndFromRhStart = lh->fPart.isCurve()
134 ? lh->ccwOf(rh, !rhSweepsCCW, lhSweepsCCW) : lhStartFromRhStart;
135 bool lhOrRhIsCurve = lh->fPart.isCurve() || rh->fPart.isCurve();
136 Turn lhStartFromRhEnd;
137 if (lhOrRhIsCurve) {
138 if (rh->fPart.isCurve()) {
139 lhStartFromRhEnd = lh->ccwOf(rh, rhSweepsCCW, !lhSweepsCCW);
140 } else {
141 lhStartFromRhEnd = lhStartFromRhStart;
142 }
143 // cancel overlap only if sweep doesn't contain other curve's sweep pts
144 if (!lh->sweepContains(rh)) {
145 // clear overlap if both turn in the same direction
146 lrOverlap &= (int) lhStartFromRhEnd * (int) lhEndFromRhStart < 0;
147 }
148 } else {
149 lrOverlap = false;
150 }
151 Turn thStartFromRhEnd SkDEBUGCODE(= Turn::kDebugUninitialized);
152 Turn thEndFromLhStart SkDEBUGCODE(= Turn::kDebugUninitialized);
153 if (lhOrRhIsCurve || fPart.isCurve()) {
154 thStartFromRhEnd = rh->fPart.isCurve() || fPart.isCurve()
155 ? this->ccwOf(rh, rhSweepsCCW, !thSweepsCCW) : thEndFromRhStart;
156 thEndFromLhStart = lh->fPart.isCurve() || fPart.isCurve()
157 ? this->ccwOf(lh, !lhSweepsCCW, thSweepsCCW) : thStartFromLhEnd;
158 // clear overlap if both pairs turn in the same direction
159 if (!this->sweepContains(lh) && !this->sweepContains(rh)) {
160 ltrOverlap &= (int) thStartFromRhEnd * (int) thEndFromRhStart <= 0
161 || (int) thStartFromLhEnd * (int) thEndFromLhStart <= 0;
162 }
163 } else {
164 ltrOverlap = false;
165 }
166 if (!lrOverlap & !ltrOverlap) {
167 Turn lhFromRh = (Turn) ((int) lhEndFromRhStart | (int) lhStartFromRhStart);
168 Turn thFromLh = (Turn) ((int) thEndFromLhStart | (int) thStartFromLhEnd);
169 Turn thFromRh = (Turn) ((int) thEndFromRhStart | (int) thStartFromRhEnd);
170 bool result = Turn::kCw == lhFromRh ?
171 Turn::kCcw == thFromLh && Turn::kCw == thFromRh :
172 Turn::kCcw == thFromLh || Turn::kCw == thFromRh;
173 return COMPARE_RESULT(6, result);
174 }
175 if (lhHasOffset) {
176 lh->fPart.fCurve.offset(lh->segment()->verb(), lhOffset);
177 }
178 if (rhHasOffset) {
179 rh->fPart.fCurve.offset(rh->segment()->verb(), rhOffset);
180 }
181 lrOverlap = lh->fSectorMask & rh->fSectorMask;
182 ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
183 }
184 if (!lrOverlap) { // no lh/rh sector overlap, no offsets
caryclark54359292015-03-26 07:52:43 -0700185 int lrGap = (rh->fSectorStart - lh->fSectorStart + 32) & 0x1f;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000186 /* A tiny change can move the start +/- 4. The order can only be determined if
187 lr gap is not 12 to 20 or -12 to -20.
188 -31 ..-21 1
189 -20 ..-12 -1
190 -11 .. -1 0
191 0 shouldn't get here
192 11 .. 1 1
193 12 .. 20 -1
194 21 .. 31 0
195 */
196 lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1;
197 } else {
caryclark54359292015-03-26 07:52:43 -0700198 lrOrder = (int) lh->orderable(rh);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000199 if (!ltrOverlap) {
Cary Clarkd2eb5812017-01-18 11:00:57 -0500200 return COMPARE_RESULT(7, !lrOrder);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000201 }
202 }
203 int ltOrder;
Cary Clarkd2eb5812017-01-18 11:00:57 -0500204 SkDEBUGCODE(bool ltOverlap = lhHasOffset || lh->fSectorMask & fSectorMask);
205 SkDEBUGCODE(bool trOverlap = rhHasOffset || rh->fSectorMask & fSectorMask);
206 SkASSERT(ltOverlap || trOverlap);
caryclark54359292015-03-26 07:52:43 -0700207 if (lh->fSectorMask & fSectorMask) {
208 ltOrder = (int) lh->orderable(this);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000209 } else {
caryclark54359292015-03-26 07:52:43 -0700210 int ltGap = (fSectorStart - lh->fSectorStart + 32) & 0x1f;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000211 ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1;
212 }
213 int trOrder;
caryclark54359292015-03-26 07:52:43 -0700214 if (rh->fSectorMask & fSectorMask) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000215 trOrder = (int) orderable(rh);
216 } else {
caryclark54359292015-03-26 07:52:43 -0700217 int trGap = (rh->fSectorStart - fSectorStart + 32) & 0x1f;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000218 trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1;
219 }
Cary Clarkff114282016-12-14 11:56:16 -0500220 this->alignmentSameSide(lh, &ltOrder);
221 this->alignmentSameSide(rh, &trOrder);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000222 if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) {
Cary Clarkd2eb5812017-01-18 11:00:57 -0500223 return COMPARE_RESULT(8, lrOrder ? (ltOrder & trOrder) : (ltOrder | trOrder));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000224 }
225 SkASSERT(lrOrder >= 0 || ltOrder >= 0 || trOrder >= 0);
226// There's not enough information to sort. Get the pairs of angles in opposite planes.
227// If an order is < 0, the pair is already in an opposite plane. Check the remaining pairs.
228 // FIXME : once all variants are understood, rewrite this more simply
229 if (ltOrder == 0 && lrOrder == 0) {
230 SkASSERT(trOrder < 0);
231 // FIXME : once this is verified to work, remove one opposite angle call
caryclark54359292015-03-26 07:52:43 -0700232 SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
233 bool ltOpposite = lh->oppositePlanes(this);
caryclarka35ab3e2016-10-20 08:32:18 -0700234 SkOPASSERT(lrOpposite != ltOpposite);
Cary Clarkd2eb5812017-01-18 11:00:57 -0500235 return COMPARE_RESULT(9, ltOpposite);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000236 } else if (ltOrder == 1 && trOrder == 0) {
237 SkASSERT(lrOrder < 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000238 bool trOpposite = oppositePlanes(rh);
Cary Clarkd2eb5812017-01-18 11:00:57 -0500239 return COMPARE_RESULT(10, trOpposite);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000240 } else if (lrOrder == 1 && trOrder == 1) {
241 SkASSERT(ltOrder < 0);
caryclark78a37a52016-10-20 12:36:16 -0700242// SkDEBUGCODE(bool trOpposite = oppositePlanes(rh));
caryclark54359292015-03-26 07:52:43 -0700243 bool lrOpposite = lh->oppositePlanes(rh);
caryclark78a37a52016-10-20 12:36:16 -0700244// SkASSERT(lrOpposite != trOpposite);
Cary Clarkd2eb5812017-01-18 11:00:57 -0500245 return COMPARE_RESULT(11, lrOpposite);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000246 }
247 if (lrOrder < 0) {
248 if (ltOrder < 0) {
Cary Clarkd2eb5812017-01-18 11:00:57 -0500249 return COMPARE_RESULT(12, trOrder);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000250 }
Cary Clarkd2eb5812017-01-18 11:00:57 -0500251 return COMPARE_RESULT(13, ltOrder);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000252 }
Cary Clarkd2eb5812017-01-18 11:00:57 -0500253 return COMPARE_RESULT(14, !lrOrder);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000254}
255
256// given a line, see if the opposite curve's convex hull is all on one side
257// returns -1=not on one side 0=this CW of test 1=this CCW of test
caryclark54359292015-03-26 07:52:43 -0700258int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
caryclarkeed356d2016-09-14 07:18:20 -0700259 SkASSERT(!fPart.isCurve());
260 SkASSERT(test->fPart.isCurve());
261 SkDPoint origin = fPart.fCurve[0];
262 SkDVector line = fPart.fCurve[1] - origin;
caryclark81681942016-07-21 10:44:07 -0700263 double crosses[3];
caryclark54359292015-03-26 07:52:43 -0700264 SkPath::Verb testVerb = test->segment()->verb();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000265 int iMax = SkPathOpsVerbToPoints(testVerb);
266// SkASSERT(origin == test.fCurveHalf[0]);
caryclarkeed356d2016-09-14 07:18:20 -0700267 const SkDCurve& testCurve = test->fPart.fCurve;
caryclark54359292015-03-26 07:52:43 -0700268 for (int index = 1; index <= iMax; ++index) {
caryclark81681942016-07-21 10:44:07 -0700269 double xy1 = line.fX * (testCurve[index].fY - origin.fY);
270 double xy2 = line.fY * (testCurve[index].fX - origin.fX);
271 crosses[index - 1] = AlmostBequalUlps(xy1, xy2) ? 0 : xy1 - xy2;
caryclark54359292015-03-26 07:52:43 -0700272 }
273 if (crosses[0] * crosses[1] < 0) {
274 return -1;
275 }
276 if (SkPath::kCubic_Verb == testVerb) {
277 if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000278 return -1;
279 }
caryclark54359292015-03-26 07:52:43 -0700280 }
281 if (crosses[0]) {
282 return crosses[0] < 0;
283 }
284 if (crosses[1]) {
285 return crosses[1] < 0;
286 }
287 if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
288 return crosses[2] < 0;
289 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000290 fUnorderable = true;
291 return -1;
292}
293
Cary Clarkff114282016-12-14 11:56:16 -0500294// To sort the angles, all curves are translated to have the same starting point.
295// If the curve's control point in its original position is on one side of a compared line,
296// and translated is on the opposite side, reverse the previously computed order.
297void SkOpAngle::alignmentSameSide(const SkOpAngle* test, int* order) const {
298 if (*order < 0) {
299 return;
300 }
301 if (fPart.isCurve()) {
302 // This should support all curve types, but only bug that requires this has lines
303 // Turning on for curves causes existing tests to fail
304 return;
305 }
306 if (test->fPart.isCurve()) {
307 return;
308 }
309 const SkDPoint& xOrigin = test->fPart.fCurve.fLine[0];
310 const SkDPoint& oOrigin = test->fOriginalCurvePart.fLine[0];
311 if (xOrigin == oOrigin) {
312 return;
313 }
314 int iMax = SkPathOpsVerbToPoints(this->segment()->verb());
315 SkDVector xLine = test->fPart.fCurve.fLine[1] - xOrigin;
316 SkDVector oLine = test->fOriginalCurvePart.fLine[1] - oOrigin;
317 for (int index = 1; index <= iMax; ++index) {
318 const SkDPoint& testPt = fPart.fCurve[index];
319 double xCross = oLine.crossCheck(testPt - xOrigin);
320 double oCross = xLine.crossCheck(testPt - oOrigin);
321 if (oCross * xCross < 0) {
322 *order ^= 1;
323 break;
324 }
325 }
326}
327
Cary Clarkd2eb5812017-01-18 11:00:57 -0500328static bool same_side(double cross1, double cross2) {
329 return cross1 * cross2 > 0 && !roughly_zero_when_compared_to(cross1, cross2)
330 && !roughly_zero_when_compared_to(cross2, cross1);
331}
332
333static double same_side_candidate(double cross1, double cross2) {
334 return same_side(cross1, cross2) ? SkTAbs(cross1) > SkTAbs(cross2) ? cross1 : cross2 : 0;
335}
336
337SkOpAngle::Turn SkOpAngle::ccwOf(const SkOpAngle* rh, bool rhCW, bool thisCCW, bool recursed)
338 const {
339 const SkDPoint& startPt = fPart.fCurve[0];
340 const SkDPoint& rhStartPt = rh->fPart.fCurve[0];
341 SkDVector startOffset = rhStartPt - startPt;
342 bool commonPt = 0 == startOffset.fX && 0 == startOffset.fY;
343 const SkDVector& sweep = fPart.fSweep[(int) thisCCW];
344 const SkDVector& rhSweep = rh->fPart.fSweep[(int) rhCW];
345 const SkDVector* testV;
346 SkDVector rhEndV;
347 if (commonPt) {
348 testV = &rhSweep;
349 } else {
350 rhEndV = rhSweep + startOffset;
351 testV = &rhEndV;
352 }
353 double endCheck = sweep.crossCheck(*testV);
354#if 0 && DEBUG_ANGLE // too verbose to show on all the time
355 SkDebugf("%s {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}} id=1\n", __func__, rhStartPt.fX, rhStartPt.fY,
356 rhStartPt.fX + rhSweep.fX, rhStartPt.fY + rhSweep.fY);
357 SkDebugf("%s {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}} id=2\n", __func__, startPt.fX, startPt.fY,
358 startPt.fX + sweep.fX, startPt.fY + sweep.fY);
359#endif
360 if (0 == endCheck) {
361 if (sweep.dot(*testV) < 0) {
362 return Turn::kNone; // neither clockwise nor counterclockwise
363 }
364 // if the pair of angles share an edge, use its other sweep to check the turn value
365 if ((fPart.isCurve() || rh->fPart.isCurve())) {
366 if (recursed) {
367 return Turn::kNone;
368 }
369 return this->ccwOf(rh, !rhCW, !thisCCW, true);
370 }
371 }
372 if (commonPt) {
373 return toTurn(endCheck > 0);
374 }
375 double startCheck = sweep.crossCheck(startOffset);
376 if ((startCheck == 0 || startOffset.lengthSquared() < FLT_EPSILON_SQUARED * 2049) &&
377 (endCheck == 0 || testV->lengthSquared() < FLT_EPSILON_SQUARED * 2049)) {
378 double cross = sweep.cross(rhSweep);
379 if (cross != 0)
380 return toTurn(cross > 0);
381 }
382 if (same_side(startCheck, endCheck)) {
383 return toTurn(startCheck > 0);
384 }
385 SkDVector reverseSweep = -sweep;
386 SkDVector rhReverseStartV = startOffset - sweep;
387 double reverseStartCheck = reverseSweep.crossCheck(rhReverseStartV);
388 SkDVector rhReverseEndV = rhReverseStartV + rhSweep;
389 double reverseEndCheck = reverseSweep.crossCheck(rhReverseEndV);
390 if (same_side(reverseStartCheck, reverseEndCheck)) {
391 return toTurn(reverseStartCheck < 0);
392 }
393 double rhEndCheck = rhSweep.crossCheck(rhReverseEndV);
394 double rhStartCheck = rhSweep.crossCheck(*testV);
395 double endSide = same_side_candidate(rhEndCheck, rhStartCheck);
396 SkDVector rhReverseSweep = -rhSweep;
397 double rhReverseEndCheck = rhReverseSweep.crossCheck(rhReverseStartV);
398 double rhReverseStartCheck = rhReverseSweep.crossCheck(startOffset);
399 double startSide = -same_side_candidate(rhReverseEndCheck, rhReverseStartCheck);
400 if (startSide || endSide) {
401 return toTurn((SkTAbs(startSide) > SkTAbs(endSide) ? startSide : endSide) > 0);
402 }
403 // if a pair crosses only slightly, no two ends will be on the same side
404 // look for the smaller ratio to guess which segment only crosses the other slightly
405 double crosses[] = { endCheck, reverseStartCheck, reverseEndCheck,
406 rhStartCheck, rhEndCheck, rhReverseStartCheck, rhReverseEndCheck };
407 int smallest = -1;
408 double smallCross = SkTAbs(startCheck);
409 for (int index = 0; index < (int) SK_ARRAY_COUNT(crosses); ++index) {
410 double testCross = SkTAbs(crosses[index]);
411 if (smallCross > testCross) {
412 smallCross = testCross;
413 smallest = index;
414 }
415 }
416 return toTurn((smallest <= 2 ? endCheck : -rhReverseEndCheck) > 0);
417}
418
419bool SkOpAngle::checkCrossesZero(int* start, int* end) const {
420 *start = SkTMin(fSectorStart, fSectorEnd);
421 *end = SkTMax(fSectorStart, fSectorEnd);
422 bool crossesZero = *end - *start > 16;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000423 return crossesZero;
424}
caryclark@google.com07393ca2013-04-08 11:47:37 +0000425
caryclark54359292015-03-26 07:52:43 -0700426bool SkOpAngle::checkParallel(SkOpAngle* rh) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000427 SkDVector scratch[2];
428 const SkDVector* sweep, * tweep;
caryclarkeed356d2016-09-14 07:18:20 -0700429 if (this->fPart.isOrdered()) {
430 sweep = this->fPart.fSweep;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000431 } else {
caryclarkeed356d2016-09-14 07:18:20 -0700432 scratch[0] = this->fPart.fCurve[1] - this->fPart.fCurve[0];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000433 sweep = &scratch[0];
caryclark@google.coma5e55922013-05-07 18:51:31 +0000434 }
caryclarkeed356d2016-09-14 07:18:20 -0700435 if (rh->fPart.isOrdered()) {
436 tweep = rh->fPart.fSweep;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000437 } else {
caryclarkeed356d2016-09-14 07:18:20 -0700438 scratch[1] = rh->fPart.fCurve[1] - rh->fPart.fCurve[0];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000439 tweep = &scratch[1];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000440 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000441 double s0xt0 = sweep->crossCheck(*tweep);
442 if (tangentsDiverge(rh, s0xt0)) {
443 return s0xt0 < 0;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000444 }
caryclark54359292015-03-26 07:52:43 -0700445 // compute the perpendicular to the endpoints and see where it intersects the opposite curve
446 // if the intersections within the t range, do a cross check on those
447 bool inside;
caryclark55888e42016-07-18 10:01:36 -0700448 if (!fEnd->contains(rh->fEnd)) {
caryclark1049f122015-04-20 08:31:59 -0700449 if (this->endToSide(rh, &inside)) {
450 return inside;
451 }
452 if (rh->endToSide(this, &inside)) {
453 return !inside;
454 }
caryclark54359292015-03-26 07:52:43 -0700455 }
456 if (this->midToSide(rh, &inside)) {
457 return inside;
458 }
459 if (rh->midToSide(this, &inside)) {
460 return !inside;
461 }
462 // compute the cross check from the mid T values (last resort)
caryclarkeed356d2016-09-14 07:18:20 -0700463 SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
464 SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000465 double m0xm1 = m0.crossCheck(m1);
466 if (m0xm1 == 0) {
caryclark54359292015-03-26 07:52:43 -0700467 this->fUnorderable = true;
468 rh->fUnorderable = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000469 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000470 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000471 return m0xm1 < 0;
472}
473
474// the original angle is too short to get meaningful sector information
475// lengthen it until it is long enough to be meaningful or leave it unset if lengthening it
476// would cause it to intersect one of the adjacent angles
477bool SkOpAngle::computeSector() {
478 if (fComputedSector) {
caryclarkdac1d172014-06-17 05:15:38 -0700479 return !fUnorderable;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000480 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000481 fComputedSector = true;
caryclark54359292015-03-26 07:52:43 -0700482 bool stepUp = fStart->t() < fEnd->t();
caryclark55888e42016-07-18 10:01:36 -0700483 SkOpSpanBase* checkEnd = fEnd;
caryclark54359292015-03-26 07:52:43 -0700484 if (checkEnd->final() && stepUp) {
caryclarkccec0f92015-03-24 07:28:17 -0700485 fUnorderable = true;
486 return false;
487 }
caryclark54359292015-03-26 07:52:43 -0700488 do {
489// advance end
490 const SkOpSegment* other = checkEnd->segment();
491 const SkOpSpanBase* oSpan = other->head();
492 do {
493 if (oSpan->segment() != segment()) {
494 continue;
495 }
496 if (oSpan == checkEnd) {
497 continue;
498 }
499 if (!approximately_equal(oSpan->t(), checkEnd->t())) {
500 continue;
501 }
502 goto recomputeSector;
503 } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
504 checkEnd = stepUp ? !checkEnd->final()
halcanary96fcdcc2015-08-27 07:41:13 -0700505 ? checkEnd->upCast()->next() : nullptr
caryclark54359292015-03-26 07:52:43 -0700506 : checkEnd->prev();
507 } while (checkEnd);
508recomputeSector:
caryclark55888e42016-07-18 10:01:36 -0700509 SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
caryclark54359292015-03-26 07:52:43 -0700510 : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
511 if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
512 fUnorderable = true;
513 return false;
514 }
caryclark624637c2015-05-11 07:21:27 -0700515 if (stepUp != (fStart->t() < computedEnd->t())) {
516 fUnorderable = true;
517 return false;
518 }
caryclark54359292015-03-26 07:52:43 -0700519 SkOpSpanBase* saveEnd = fEnd;
520 fComputedEnd = fEnd = computedEnd;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000521 setSpans();
522 setSector();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000523 fEnd = saveEnd;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000524 return !fUnorderable;
525}
526
caryclarkb36a3cd2016-10-18 07:59:44 -0700527int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) {
caryclarkeed356d2016-09-14 07:18:20 -0700528 const SkDVector* sweep = this->fPart.fSweep;
529 const SkDVector* tweep = rh->fPart.fSweep;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000530 double s0xs1 = sweep[0].crossCheck(sweep[1]);
531 double s0xt0 = sweep[0].crossCheck(tweep[0]);
532 double s1xt0 = sweep[1].crossCheck(tweep[0]);
533 bool tBetweenS = s0xs1 > 0 ? s0xt0 > 0 && s1xt0 < 0 : s0xt0 < 0 && s1xt0 > 0;
534 double s0xt1 = sweep[0].crossCheck(tweep[1]);
535 double s1xt1 = sweep[1].crossCheck(tweep[1]);
536 tBetweenS |= s0xs1 > 0 ? s0xt1 > 0 && s1xt1 < 0 : s0xt1 < 0 && s1xt1 > 0;
537 double t0xt1 = tweep[0].crossCheck(tweep[1]);
538 if (tBetweenS) {
539 return -1;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000540 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000541 if ((s0xt0 == 0 && s1xt1 == 0) || (s1xt0 == 0 && s0xt1 == 0)) { // s0 to s1 equals t0 to t1
542 return -1;
543 }
544 bool sBetweenT = t0xt1 > 0 ? s0xt0 < 0 && s0xt1 > 0 : s0xt0 > 0 && s0xt1 < 0;
545 sBetweenT |= t0xt1 > 0 ? s1xt0 < 0 && s1xt1 > 0 : s1xt0 > 0 && s1xt1 < 0;
546 if (sBetweenT) {
547 return -1;
548 }
549 // if all of the sweeps are in the same half plane, then the order of any pair is enough
550 if (s0xt0 >= 0 && s0xt1 >= 0 && s1xt0 >= 0 && s1xt1 >= 0) {
551 return 0;
552 }
553 if (s0xt0 <= 0 && s0xt1 <= 0 && s1xt0 <= 0 && s1xt1 <= 0) {
554 return 1;
555 }
556 // if the outside sweeps are greater than 180 degress:
557 // first assume the inital tangents are the ordering
558 // if the midpoint direction matches the inital order, that is enough
caryclarkeed356d2016-09-14 07:18:20 -0700559 SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
560 SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000561 double m0xm1 = m0.crossCheck(m1);
562 if (s0xt0 > 0 && m0xm1 > 0) {
563 return 0;
564 }
565 if (s0xt0 < 0 && m0xm1 < 0) {
566 return 1;
567 }
568 if (tangentsDiverge(rh, s0xt0)) {
569 return s0xt0 < 0;
570 }
571 return m0xm1 < 0;
572}
573
574// OPTIMIZATION: longest can all be either lazily computed here or precomputed in setup
575double SkOpAngle::distEndRatio(double dist) const {
576 double longest = 0;
577 const SkOpSegment& segment = *this->segment();
578 int ptCount = SkPathOpsVerbToPoints(segment.verb());
579 const SkPoint* pts = segment.pts();
580 for (int idx1 = 0; idx1 <= ptCount - 1; ++idx1) {
581 for (int idx2 = idx1 + 1; idx2 <= ptCount; ++idx2) {
582 if (idx1 == idx2) {
583 continue;
584 }
585 SkDVector v;
586 v.set(pts[idx2] - pts[idx1]);
587 double lenSq = v.lengthSquared();
588 longest = SkTMax(longest, lenSq);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000589 }
590 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000591 return sqrt(longest) / dist;
592}
593
caryclark54359292015-03-26 07:52:43 -0700594bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
595 SkPath::Verb lVerb = this->segment()->verb();
596 SkPath::Verb rVerb = rh->segment()->verb();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000597 int lPts = SkPathOpsVerbToPoints(lVerb);
598 int rPts = SkPathOpsVerbToPoints(rVerb);
caryclarkeed356d2016-09-14 07:18:20 -0700599 SkDLine rays[] = {{{this->fPart.fCurve[0], rh->fPart.fCurve[rPts]}},
600 {{this->fPart.fCurve[0], this->fPart.fCurve[lPts]}}};
caryclark55888e42016-07-18 10:01:36 -0700601 if (this->fEnd->contains(rh->fEnd)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000602 return checkParallel(rh);
603 }
604 double smallTs[2] = {-1, -1};
605 bool limited[2] = {false, false};
606 for (int index = 0; index < 2; ++index) {
caryclark1049f122015-04-20 08:31:59 -0700607 SkPath::Verb cVerb = index ? rVerb : lVerb;
caryclark65f55312014-11-13 06:58:52 -0800608 // if the curve is a line, then the line and the ray intersect only at their crossing
caryclark1049f122015-04-20 08:31:59 -0700609 if (cVerb == SkPath::kLine_Verb) {
caryclark65f55312014-11-13 06:58:52 -0800610 continue;
611 }
caryclark54359292015-03-26 07:52:43 -0700612 const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
613 SkIntersections i;
caryclark1049f122015-04-20 08:31:59 -0700614 (*CurveIntersectRay[cVerb])(segment.pts(), segment.weight(), rays[index], &i);
caryclark54359292015-03-26 07:52:43 -0700615 double tStart = index ? rh->fStart->t() : this->fStart->t();
616 double tEnd = index ? rh->fComputedEnd->t() : this->fComputedEnd->t();
617 bool testAscends = tStart < (index ? rh->fComputedEnd->t() : this->fComputedEnd->t());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000618 double t = testAscends ? 0 : 1;
619 for (int idx2 = 0; idx2 < i.used(); ++idx2) {
620 double testT = i[0][idx2];
621 if (!approximately_between_orderable(tStart, testT, tEnd)) {
622 continue;
623 }
624 if (approximately_equal_orderable(tStart, testT)) {
625 continue;
626 }
627 smallTs[index] = t = testAscends ? SkTMax(t, testT) : SkTMin(t, testT);
628 limited[index] = approximately_equal_orderable(t, tEnd);
629 }
630 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000631 bool sRayLonger = false;
632 SkDVector sCept = {0, 0};
633 double sCeptT = -1;
634 int sIndex = -1;
635 bool useIntersect = false;
636 for (int index = 0; index < 2; ++index) {
637 if (smallTs[index] < 0) {
638 continue;
639 }
caryclark54359292015-03-26 07:52:43 -0700640 const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000641 const SkDPoint& dPt = segment.dPtAtT(smallTs[index]);
642 SkDVector cept = dPt - rays[index][0];
643 // If this point is on the curve, it should have been detected earlier by ordinary
644 // curve intersection. This may be hard to determine in general, but for lines,
645 // the point could be close to or equal to its end, but shouldn't be near the start.
646 if ((index ? lPts : rPts) == 1) {
647 SkDVector total = rays[index][1] - rays[index][0];
648 if (cept.lengthSquared() * 2 < total.lengthSquared()) {
649 continue;
650 }
651 }
652 SkDVector end = rays[index][1] - rays[index][0];
653 if (cept.fX * end.fX < 0 || cept.fY * end.fY < 0) {
654 continue;
655 }
656 double rayDist = cept.length();
657 double endDist = end.length();
658 bool rayLonger = rayDist > endDist;
659 if (limited[0] && limited[1] && rayLonger) {
660 useIntersect = true;
661 sRayLonger = rayLonger;
662 sCept = cept;
663 sCeptT = smallTs[index];
664 sIndex = index;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000665 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000666 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000667 double delta = fabs(rayDist - endDist);
668 double minX, minY, maxX, maxY;
669 minX = minY = SK_ScalarInfinity;
670 maxX = maxY = -SK_ScalarInfinity;
caryclarkeed356d2016-09-14 07:18:20 -0700671 const SkDCurve& curve = index ? rh->fPart.fCurve : this->fPart.fCurve;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000672 int ptCount = index ? rPts : lPts;
673 for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
674 minX = SkTMin(minX, curve[idx2].fX);
675 minY = SkTMin(minY, curve[idx2].fY);
676 maxX = SkTMax(maxX, curve[idx2].fX);
677 maxY = SkTMax(maxY, curve[idx2].fY);
678 }
679 double maxWidth = SkTMax(maxX - minX, maxY - minY);
680 delta /= maxWidth;
caryclark54359292015-03-26 07:52:43 -0700681 if (delta > 1e-3 && (useIntersect ^= true)) { // FIXME: move this magic number
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000682 sRayLonger = rayLonger;
683 sCept = cept;
684 sCeptT = smallTs[index];
685 sIndex = index;
686 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000687 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000688 if (useIntersect) {
caryclarkeed356d2016-09-14 07:18:20 -0700689 const SkDCurve& curve = sIndex ? rh->fPart.fCurve : this->fPart.fCurve;
caryclark54359292015-03-26 07:52:43 -0700690 const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
691 double tStart = sIndex ? rh->fStart->t() : fStart->t();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000692 SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
693 double septDir = mid.crossCheck(sCept);
694 if (!septDir) {
695 return checkParallel(rh);
696 }
697 return sRayLonger ^ (sIndex == 0) ^ (septDir < 0);
698 } else {
699 return checkParallel(rh);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000700 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000701}
702
caryclark54359292015-03-26 07:52:43 -0700703bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
704 const SkOpSegment* segment = this->segment();
705 SkPath::Verb verb = segment->verb();
caryclark54359292015-03-26 07:52:43 -0700706 SkDLine rayEnd;
707 rayEnd[0].set(this->fEnd->pt());
708 rayEnd[1] = rayEnd[0];
caryclark1049f122015-04-20 08:31:59 -0700709 SkDVector slopeAtEnd = (*CurveDSlopeAtT[verb])(segment->pts(), segment->weight(),
710 this->fEnd->t());
caryclark54359292015-03-26 07:52:43 -0700711 rayEnd[1].fX += slopeAtEnd.fY;
712 rayEnd[1].fY -= slopeAtEnd.fX;
713 SkIntersections iEnd;
714 const SkOpSegment* oppSegment = rh->segment();
715 SkPath::Verb oppVerb = oppSegment->verb();
caryclark1049f122015-04-20 08:31:59 -0700716 (*CurveIntersectRay[oppVerb])(oppSegment->pts(), oppSegment->weight(), rayEnd, &iEnd);
caryclark54359292015-03-26 07:52:43 -0700717 double endDist;
718 int closestEnd = iEnd.closestTo(rh->fStart->t(), rh->fEnd->t(), rayEnd[0], &endDist);
719 if (closestEnd < 0) {
720 return false;
721 }
722 if (!endDist) {
723 return false;
724 }
725 SkDPoint start;
726 start.set(this->fStart->pt());
727 // OPTIMIZATION: multiple times in the code we find the max scalar
728 double minX, minY, maxX, maxY;
729 minX = minY = SK_ScalarInfinity;
730 maxX = maxY = -SK_ScalarInfinity;
caryclarkeed356d2016-09-14 07:18:20 -0700731 const SkDCurve& curve = rh->fPart.fCurve;
caryclark1049f122015-04-20 08:31:59 -0700732 int oppPts = SkPathOpsVerbToPoints(oppVerb);
caryclark54359292015-03-26 07:52:43 -0700733 for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
734 minX = SkTMin(minX, curve[idx2].fX);
735 minY = SkTMin(minY, curve[idx2].fY);
736 maxX = SkTMax(maxX, curve[idx2].fX);
737 maxY = SkTMax(maxY, curve[idx2].fY);
738 }
739 double maxWidth = SkTMax(maxX - minX, maxY - minY);
740 endDist /= maxWidth;
caryclark55888e42016-07-18 10:01:36 -0700741 if (endDist < 5e-12) { // empirically found
caryclark54359292015-03-26 07:52:43 -0700742 return false;
743 }
744 const SkDPoint* endPt = &rayEnd[0];
745 SkDPoint oppPt = iEnd.pt(closestEnd);
746 SkDVector vLeft = *endPt - start;
747 SkDVector vRight = oppPt - start;
caryclark55888e42016-07-18 10:01:36 -0700748 double dir = vLeft.crossNoNormalCheck(vRight);
caryclark54359292015-03-26 07:52:43 -0700749 if (!dir) {
750 return false;
751 }
752 *inside = dir < 0;
753 return true;
754}
755
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000756/* y<0 y==0 y>0 x<0 x==0 x>0 xy<0 xy==0 xy>0
757 0 x x x
758 1 x x x
759 2 x x x
760 3 x x x
761 4 x x x
762 5 x x x
763 6 x x x
764 7 x x x
765 8 x x x
766 9 x x x
767 10 x x x
768 11 x x x
769 12 x x x
770 13 x x x
771 14 x x x
772 15 x x x
773*/
774int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const {
775 double absX = fabs(x);
776 double absY = fabs(y);
777 double xy = SkPath::kLine_Verb == verb || !AlmostEqualUlps(absX, absY) ? absX - absY : 0;
778 // If there are four quadrants and eight octants, and since the Latin for sixteen is sedecim,
779 // one could coin the term sedecimant for a space divided into 16 sections.
780 // http://english.stackexchange.com/questions/133688/word-for-something-partitioned-into-16-parts
781 static const int sedecimant[3][3][3] = {
782 // y<0 y==0 y>0
783 // x<0 x==0 x>0 x<0 x==0 x>0 x<0 x==0 x>0
784 {{ 4, 3, 2}, { 7, -1, 15}, {10, 11, 12}}, // abs(x) < abs(y)
785 {{ 5, -1, 1}, {-1, -1, -1}, { 9, -1, 13}}, // abs(x) == abs(y)
786 {{ 6, 3, 0}, { 7, -1, 15}, { 8, 11, 14}}, // abs(x) > abs(y)
787 };
788 int sector = sedecimant[(xy >= 0) + (xy > 0)][(y >= 0) + (y > 0)][(x >= 0) + (x > 0)] * 2 + 1;
caryclark65b427c2014-09-18 10:32:57 -0700789// SkASSERT(SkPath::kLine_Verb == verb || sector >= 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000790 return sector;
791}
792
caryclark54359292015-03-26 07:52:43 -0700793SkOpGlobalState* SkOpAngle::globalState() const {
794 return this->segment()->globalState();
795}
796
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000797// OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
798// OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
caryclarkb36a3cd2016-10-18 07:59:44 -0700799bool SkOpAngle::insert(SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000800 if (angle->fNext) {
801 if (loopCount() >= angle->loopCount()) {
802 if (!merge(angle)) {
caryclarkb36a3cd2016-10-18 07:59:44 -0700803 return true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000804 }
805 } else if (fNext) {
806 if (!angle->merge(this)) {
caryclarkb36a3cd2016-10-18 07:59:44 -0700807 return true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000808 }
809 } else {
810 angle->insert(this);
811 }
caryclarkb36a3cd2016-10-18 07:59:44 -0700812 return true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000813 }
halcanary96fcdcc2015-08-27 07:41:13 -0700814 bool singleton = nullptr == fNext;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000815 if (singleton) {
816 fNext = this;
817 }
818 SkOpAngle* next = fNext;
819 if (next->fNext == this) {
820 if (singleton || angle->after(this)) {
821 this->fNext = angle;
822 angle->fNext = next;
823 } else {
824 next->fNext = angle;
825 angle->fNext = this;
826 }
827 debugValidateNext();
caryclarkb36a3cd2016-10-18 07:59:44 -0700828 return true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000829 }
830 SkOpAngle* last = this;
caryclarkb36a3cd2016-10-18 07:59:44 -0700831 bool flipAmbiguity = false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000832 do {
833 SkASSERT(last->fNext == next);
caryclarkb36a3cd2016-10-18 07:59:44 -0700834 if (angle->after(last) ^ (angle->tangentsAmbiguous() & flipAmbiguity)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000835 last->fNext = angle;
836 angle->fNext = next;
837 debugValidateNext();
caryclarkb36a3cd2016-10-18 07:59:44 -0700838 return true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000839 }
840 last = next;
caryclarkb36a3cd2016-10-18 07:59:44 -0700841 if (last == this) {
842 FAIL_IF(flipAmbiguity);
843 // We're in a loop. If a sort was ambiguous, flip it to end the loop.
844 flipAmbiguity = true;
845 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000846 next = next->fNext;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000847 } while (true);
caryclarkb36a3cd2016-10-18 07:59:44 -0700848 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000849}
850
caryclark54359292015-03-26 07:52:43 -0700851SkOpSpanBase* SkOpAngle::lastMarked() const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000852 if (fLastMarked) {
caryclark54359292015-03-26 07:52:43 -0700853 if (fLastMarked->chased()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700854 return nullptr;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000855 }
caryclark54359292015-03-26 07:52:43 -0700856 fLastMarked->setChased(true);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000857 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000858 return fLastMarked;
859}
860
caryclark54359292015-03-26 07:52:43 -0700861bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000862 if (!fNext) {
863 return false;
864 }
865 const SkOpAngle* first = this;
866 const SkOpAngle* loop = this;
caryclark54359292015-03-26 07:52:43 -0700867 const SkOpSegment* tSegment = angle->fStart->segment();
868 double tStart = angle->fStart->t();
869 double tEnd = angle->fEnd->t();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000870 do {
caryclark54359292015-03-26 07:52:43 -0700871 const SkOpSegment* lSegment = loop->fStart->segment();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000872 if (lSegment != tSegment) {
873 continue;
874 }
caryclark54359292015-03-26 07:52:43 -0700875 double lStart = loop->fStart->t();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000876 if (lStart != tEnd) {
877 continue;
878 }
caryclark54359292015-03-26 07:52:43 -0700879 double lEnd = loop->fEnd->t();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000880 if (lEnd == tStart) {
881 return true;
882 }
883 } while ((loop = loop->fNext) != first);
884 return false;
885}
886
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000887int SkOpAngle::loopCount() const {
888 int count = 0;
889 const SkOpAngle* first = this;
890 const SkOpAngle* next = this;
891 do {
892 next = next->fNext;
893 ++count;
894 } while (next && next != first);
895 return count;
896}
897
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000898bool SkOpAngle::merge(SkOpAngle* angle) {
899 SkASSERT(fNext);
900 SkASSERT(angle->fNext);
901 SkOpAngle* working = angle;
902 do {
903 if (this == working) {
904 return false;
905 }
906 working = working->fNext;
907 } while (working != angle);
908 do {
909 SkOpAngle* next = working->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700910 working->fNext = nullptr;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000911 insert(working);
912 working = next;
913 } while (working != angle);
914 // it's likely that a pair of the angles are unorderable
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000915 debugValidateNext();
916 return true;
917}
918
919double SkOpAngle::midT() const {
caryclark54359292015-03-26 07:52:43 -0700920 return (fStart->t() + fEnd->t()) / 2;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000921}
922
caryclark54359292015-03-26 07:52:43 -0700923bool SkOpAngle::midToSide(const SkOpAngle* rh, bool* inside) const {
924 const SkOpSegment* segment = this->segment();
925 SkPath::Verb verb = segment->verb();
caryclark54359292015-03-26 07:52:43 -0700926 const SkPoint& startPt = this->fStart->pt();
927 const SkPoint& endPt = this->fEnd->pt();
928 SkDPoint dStartPt;
929 dStartPt.set(startPt);
930 SkDLine rayMid;
931 rayMid[0].fX = (startPt.fX + endPt.fX) / 2;
932 rayMid[0].fY = (startPt.fY + endPt.fY) / 2;
933 rayMid[1].fX = rayMid[0].fX + (endPt.fY - startPt.fY);
934 rayMid[1].fY = rayMid[0].fY - (endPt.fX - startPt.fX);
935 SkIntersections iMid;
caryclark1049f122015-04-20 08:31:59 -0700936 (*CurveIntersectRay[verb])(segment->pts(), segment->weight(), rayMid, &iMid);
caryclark54359292015-03-26 07:52:43 -0700937 int iOutside = iMid.mostOutside(this->fStart->t(), this->fEnd->t(), dStartPt);
938 if (iOutside < 0) {
939 return false;
940 }
941 const SkOpSegment* oppSegment = rh->segment();
942 SkPath::Verb oppVerb = oppSegment->verb();
caryclark54359292015-03-26 07:52:43 -0700943 SkIntersections oppMid;
caryclark1049f122015-04-20 08:31:59 -0700944 (*CurveIntersectRay[oppVerb])(oppSegment->pts(), oppSegment->weight(), rayMid, &oppMid);
caryclark54359292015-03-26 07:52:43 -0700945 int oppOutside = oppMid.mostOutside(rh->fStart->t(), rh->fEnd->t(), dStartPt);
946 if (oppOutside < 0) {
947 return false;
948 }
949 SkDVector iSide = iMid.pt(iOutside) - dStartPt;
950 SkDVector oppSide = oppMid.pt(oppOutside) - dStartPt;
951 double dir = iSide.crossCheck(oppSide);
952 if (!dir) {
953 return false;
954 }
955 *inside = dir < 0;
956 return true;
957}
958
959bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
bungeman60e0fee2015-08-26 05:15:46 -0700960 int startSpan = SkTAbs(rh->fSectorStart - fSectorStart);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000961 return startSpan >= 8;
962}
963
caryclark54359292015-03-26 07:52:43 -0700964bool SkOpAngle::orderable(SkOpAngle* rh) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000965 int result;
caryclarkeed356d2016-09-14 07:18:20 -0700966 if (!fPart.isCurve()) {
967 if (!rh->fPart.isCurve()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000968 double leftX = fTangentHalf.dx();
969 double leftY = fTangentHalf.dy();
caryclark54359292015-03-26 07:52:43 -0700970 double rightX = rh->fTangentHalf.dx();
971 double rightY = rh->fTangentHalf.dy();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000972 double x_ry = leftX * rightY;
973 double rx_y = rightX * leftY;
974 if (x_ry == rx_y) {
975 if (leftX * rightX < 0 || leftY * rightY < 0) {
976 return true; // exactly 180 degrees apart
977 }
978 goto unorderable;
979 }
980 SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- worth finding earlier
981 return x_ry < rx_y;
982 }
caryclark55888e42016-07-18 10:01:36 -0700983 if ((result = this->allOnOneSide(rh)) >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000984 return result;
985 }
caryclark54359292015-03-26 07:52:43 -0700986 if (fUnorderable || approximately_zero(rh->fSide)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000987 goto unorderable;
988 }
caryclarkeed356d2016-09-14 07:18:20 -0700989 } else if (!rh->fPart.isCurve()) {
caryclark54359292015-03-26 07:52:43 -0700990 if ((result = rh->allOnOneSide(this)) >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000991 return !result;
992 }
caryclark54359292015-03-26 07:52:43 -0700993 if (rh->fUnorderable || approximately_zero(fSide)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000994 goto unorderable;
995 }
caryclark55888e42016-07-18 10:01:36 -0700996 } else if ((result = this->convexHullOverlaps(rh)) >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000997 return result;
998 }
caryclark55888e42016-07-18 10:01:36 -0700999 return this->endsIntersect(rh);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001000unorderable:
1001 fUnorderable = true;
caryclark54359292015-03-26 07:52:43 -07001002 rh->fUnorderable = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001003 return true;
1004}
1005
1006// OPTIMIZE: if this shows up in a profile, add a previous pointer
1007// as is, this should be rarely called
1008SkOpAngle* SkOpAngle::previous() const {
1009 SkOpAngle* last = fNext;
1010 do {
1011 SkOpAngle* next = last->fNext;
1012 if (next == this) {
1013 return last;
1014 }
1015 last = next;
1016 } while (true);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001017}
1018
Cary Clarkd2eb5812017-01-18 11:00:57 -05001019// returns true if rounded sector range crosses zero
1020bool SkOpAngle::sectorRange(int* start, int* end, bool roundOut) const {
1021 if (checkCrossesZero(start, end)) {
1022 SkTSwap(*start, *end);
1023 }
1024 // round away since the offset curves may swap order
1025 if (roundOut) {
1026 *start = (((*start + 1) & ~0x03) - 1) & 0x1f;
1027 *end |= 0x03;
1028 }
1029 return *end < *start;
1030}
1031
caryclark54359292015-03-26 07:52:43 -07001032SkOpSegment* SkOpAngle::segment() const {
1033 return fStart->segment();
1034}
1035
1036void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001037 fStart = start;
caryclarkdac1d172014-06-17 05:15:38 -07001038 fComputedEnd = fEnd = end;
caryclark54359292015-03-26 07:52:43 -07001039 SkASSERT(start != end);
halcanary96fcdcc2015-08-27 07:41:13 -07001040 fNext = nullptr;
caryclarkb36a3cd2016-10-18 07:59:44 -07001041 fComputeSector = fComputedSector = fCheckCoincidence = fTangentsAmbiguous = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001042 setSpans();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001043 setSector();
caryclark1049f122015-04-20 08:31:59 -07001044 SkDEBUGCODE(fID = start ? start->globalState()->nextAngleID() : -1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001045}
1046
caryclark@google.com07393ca2013-04-08 11:47:37 +00001047void SkOpAngle::setSpans() {
caryclark54359292015-03-26 07:52:43 -07001048 fUnorderable = false;
halcanary96fcdcc2015-08-27 07:41:13 -07001049 fLastMarked = nullptr;
caryclark1049f122015-04-20 08:31:59 -07001050 if (!fStart) {
1051 fUnorderable = true;
1052 return;
1053 }
caryclark54359292015-03-26 07:52:43 -07001054 const SkOpSegment* segment = fStart->segment();
1055 const SkPoint* pts = segment->pts();
caryclark6c3b9cd2016-09-26 05:36:58 -07001056 SkDEBUGCODE(fPart.fCurve.fVerb = SkPath::kCubic_Verb); // required for SkDCurve debug check
caryclarkeed356d2016-09-14 07:18:20 -07001057 SkDEBUGCODE(fPart.fCurve[2].fX = fPart.fCurve[2].fY = fPart.fCurve[3].fX = fPart.fCurve[3].fY
caryclark6c3b9cd2016-09-26 05:36:58 -07001058 = SK_ScalarNaN); // make the non-line part uninitialized
1059 SkDEBUGCODE(fPart.fCurve.fVerb = segment->verb()); // set the curve type for real
1060 segment->subDivide(fStart, fEnd, &fPart.fCurve); // set at least the line part if not more
caryclarkeed356d2016-09-14 07:18:20 -07001061 fOriginalCurvePart = fPart.fCurve;
caryclark54359292015-03-26 07:52:43 -07001062 const SkPath::Verb verb = segment->verb();
caryclarkeed356d2016-09-14 07:18:20 -07001063 fPart.setCurveHullSweep(verb);
1064 if (SkPath::kLine_Verb != verb && !fPart.isCurve()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001065 SkDLine lineHalf;
caryclarkeed356d2016-09-14 07:18:20 -07001066 fPart.fCurve[1] = fPart.fCurve[SkPathOpsVerbToPoints(verb)];
1067 fOriginalCurvePart[1] = fPart.fCurve[1];
1068 lineHalf[0].set(fPart.fCurve[0].asSkPoint());
1069 lineHalf[1].set(fPart.fCurve[1].asSkPoint());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001070 fTangentHalf.lineEndPoints(lineHalf);
1071 fSide = 0;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001072 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001073 switch (verb) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001074 case SkPath::kLine_Verb: {
caryclark@google.com570863f2013-09-16 15:55:01 +00001075 SkASSERT(fStart != fEnd);
caryclark54359292015-03-26 07:52:43 -07001076 const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001077 SkDLine lineHalf;
caryclark54359292015-03-26 07:52:43 -07001078 lineHalf[0].set(fStart->pt());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001079 lineHalf[1].set(cP1);
1080 fTangentHalf.lineEndPoints(lineHalf);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001081 fSide = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001082 } return;
caryclark1049f122015-04-20 08:31:59 -07001083 case SkPath::kQuad_Verb:
1084 case SkPath::kConic_Verb: {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001085 SkLineParameters tangentPart;
caryclarkeed356d2016-09-14 07:18:20 -07001086 (void) tangentPart.quadEndPoints(fPart.fCurve.fQuad);
1087 fSide = -tangentPart.pointDistance(fPart.fCurve[2]); // not normalized -- compare sign only
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001088 } break;
1089 case SkPath::kCubic_Verb: {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001090 SkLineParameters tangentPart;
caryclarkeed356d2016-09-14 07:18:20 -07001091 (void) tangentPart.cubicPart(fPart.fCurve.fCubic);
1092 fSide = -tangentPart.pointDistance(fPart.fCurve[3]);
caryclark@google.comb3f09212013-04-17 15:49:16 +00001093 double testTs[4];
1094 // OPTIMIZATION: keep inflections precomputed with cubic segment?
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001095 int testCount = SkDCubic::FindInflections(pts, testTs);
caryclark54359292015-03-26 07:52:43 -07001096 double startT = fStart->t();
1097 double endT = fEnd->t();
caryclark@google.comb3f09212013-04-17 15:49:16 +00001098 double limitT = endT;
1099 int index;
1100 for (index = 0; index < testCount; ++index) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001101 if (!::between(startT, testTs[index], limitT)) {
caryclark@google.comb3f09212013-04-17 15:49:16 +00001102 testTs[index] = -1;
1103 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001104 }
caryclark@google.comb3f09212013-04-17 15:49:16 +00001105 testTs[testCount++] = startT;
1106 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +00001107 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +00001108 double bestSide = 0;
1109 int testCases = (testCount << 1) - 1;
1110 index = 0;
1111 while (testTs[index] < 0) {
1112 ++index;
1113 }
1114 index <<= 1;
1115 for (; index < testCases; ++index) {
1116 int testIndex = index >> 1;
1117 double testT = testTs[testIndex];
1118 if (index & 1) {
1119 testT = (testT + testTs[testIndex + 1]) / 2;
1120 }
1121 // OPTIMIZE: could avoid call for t == startT, endT
caryclark1049f122015-04-20 08:31:59 -07001122 SkDPoint pt = dcubic_xy_at_t(pts, segment->weight(), testT);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001123 SkLineParameters tangentPart;
caryclarkeed356d2016-09-14 07:18:20 -07001124 tangentPart.cubicEndPoints(fPart.fCurve.fCubic);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001125 double testSide = tangentPart.pointDistance(pt);
caryclark@google.comb3f09212013-04-17 15:49:16 +00001126 if (fabs(bestSide) < fabs(testSide)) {
1127 bestSide = testSide;
1128 }
1129 }
1130 fSide = -bestSide; // compare sign only
caryclark@google.com07393ca2013-04-08 11:47:37 +00001131 } break;
1132 default:
1133 SkASSERT(0);
1134 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001135}
caryclark@google.com570863f2013-09-16 15:55:01 +00001136
caryclark54359292015-03-26 07:52:43 -07001137void SkOpAngle::setSector() {
caryclark1049f122015-04-20 08:31:59 -07001138 if (!fStart) {
1139 fUnorderable = true;
1140 return;
1141 }
caryclark54359292015-03-26 07:52:43 -07001142 const SkOpSegment* segment = fStart->segment();
1143 SkPath::Verb verb = segment->verb();
caryclarkeed356d2016-09-14 07:18:20 -07001144 fSectorStart = this->findSector(verb, fPart.fSweep[0].fX, fPart.fSweep[0].fY);
caryclark54359292015-03-26 07:52:43 -07001145 if (fSectorStart < 0) {
1146 goto deferTilLater;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001147 }
caryclarkeed356d2016-09-14 07:18:20 -07001148 if (!fPart.isCurve()) { // if it's a line or line-like, note that both sectors are the same
caryclark54359292015-03-26 07:52:43 -07001149 SkASSERT(fSectorStart >= 0);
1150 fSectorEnd = fSectorStart;
1151 fSectorMask = 1 << fSectorStart;
1152 return;
1153 }
1154 SkASSERT(SkPath::kLine_Verb != verb);
caryclarkeed356d2016-09-14 07:18:20 -07001155 fSectorEnd = this->findSector(verb, fPart.fSweep[1].fX, fPart.fSweep[1].fY);
caryclark54359292015-03-26 07:52:43 -07001156 if (fSectorEnd < 0) {
1157deferTilLater:
1158 fSectorStart = fSectorEnd = -1;
1159 fSectorMask = 0;
1160 fComputeSector = true; // can't determine sector until segment length can be found
1161 return;
1162 }
1163 if (fSectorEnd == fSectorStart
1164 && (fSectorStart & 3) != 3) { // if the sector has no span, it can't be an exact angle
1165 fSectorMask = 1 << fSectorStart;
1166 return;
1167 }
Cary Clarkd2eb5812017-01-18 11:00:57 -05001168 int start, end;
1169 bool crossesZero = this->checkCrossesZero(&start, &end);
1170 bool bumpStart = (fSectorStart & 3) == 3;
1171 bool bumpEnd = (fSectorEnd & 3) == 3;
1172 if (bumpStart | bumpEnd) {
1173 bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
1174 // bump the start and end of the sector span if they are on exact compass points
1175 if (bumpStart) {
1176 fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
1177 }
1178 if (bumpEnd) {
1179 fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
1180 }
1181 crossesZero = this->checkCrossesZero(&start, &end);
caryclark54359292015-03-26 07:52:43 -07001182 }
caryclark54359292015-03-26 07:52:43 -07001183 if (!crossesZero) {
1184 fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
1185 } else {
Cary Clarkd2eb5812017-01-18 11:00:57 -05001186 fSectorMask = (unsigned) -1 >> (31 - start) | (unsigned) -1 << end;
caryclark54359292015-03-26 07:52:43 -07001187 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001188}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001189
caryclark54359292015-03-26 07:52:43 -07001190SkOpSpan* SkOpAngle::starter() {
1191 return fStart->starter(fEnd);
1192}
1193
Cary Clarkd2eb5812017-01-18 11:00:57 -05001194bool SkOpAngle::sweepsCCW() const {
1195 if (!fPart.isCurve()) {
1196 return false; // lines have no sweep
1197 }
1198#if 0 && DEBUG_ANGLE // too verbose to show all the time
1199 SkDebugf("%s {{{0,0}, {%g,%g}}} id=1\n", __func__, fPart.fSweep[0].fX, fPart.fSweep[0].fY);
1200 SkDebugf("%s {{{0,0}, {%g,%g}}} id=2\n", __func__, fPart.fSweep[1].fX, fPart.fSweep[1].fY);
1201#endif
1202 return fPart.fSweep[0].crossCheck(fPart.fSweep[1]) < 0;
1203}
1204
1205static bool sweep_edge_contains(const SkDVector& edge, const SkDVector& v, double* direction) {
1206 double cross = edge.crossCheck(v);
1207 if (cross) {
1208 if (cross * *direction < 0) {
1209 return true;
1210 }
1211 *direction = cross;
1212 }
1213 return false;
1214}
1215
1216static bool sweep_contains(const SkDVector sweep[2], const SkDVector& v, double* direction) {
1217 if (sweep_edge_contains(sweep[0], v, direction)) {
1218 return true;
1219 }
1220 return sweep_edge_contains(sweep[1], v, direction);
1221}
1222
1223bool SkOpAngle::sweepContains(const SkOpAngle* rh) const {
1224 if (!fPart.isCurve()) {
1225 return false;
1226 }
1227 if (!rh->fPart.isCurve()) {
1228 return false;
1229 }
1230 const SkDPoint& startPt = fPart.fCurve[0];
1231 const SkDVector* sweep = fPart.fSweep;
1232 const SkDPoint& rhStartPt = rh->fPart.fCurve[0];
1233 double direction = 0;
1234 if (startPt != rhStartPt) {
1235 SkDVector vTest = rhStartPt - startPt;
1236 if (sweep_contains(sweep, vTest, &direction)) {
1237 return true;
1238 }
1239 for (int index = 0; index < (int) SK_ARRAY_COUNT(rh->fPart.fSweep); ++index) {
1240 SkDPoint sweepEnd = rhStartPt;
1241 sweepEnd += rh->fPart.fSweep[index];
1242 SkDVector vTest = sweepEnd - startPt;
1243 if (sweep_contains(sweep, vTest, &direction)) {
1244 return true;
1245 }
1246 }
1247 } else {
1248 for (int index = 0; index < (int) SK_ARRAY_COUNT(rh->fPart.fSweep); ++index) {
1249 const SkDVector& vTest = rh->fPart.fSweep[index];
1250 if (sweep_contains(sweep, vTest, &direction)) {
1251 return true;
1252 }
1253 }
1254 }
1255 return false;
1256}
1257
caryclarkb36a3cd2016-10-18 07:59:44 -07001258bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001259 if (s0xt0 == 0) {
1260 return false;
1261 }
1262 // if the ctrl tangents are not nearly parallel, use them
1263 // solve for opposite direction displacement scale factor == m
1264 // initial dir = v1.cross(v2) == v2.x * v1.y - v2.y * v1.x
1265 // displacement of q1[1] : dq1 = { -m * v1.y, m * v1.x } + q1[1]
1266 // straight angle when : v2.x * (dq1.y - q1[0].y) == v2.y * (dq1.x - q1[0].x)
1267 // v2.x * (m * v1.x + v1.y) == v2.y * (-m * v1.y + v1.x)
1268 // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x
1269 // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
1270 // m = v1.cross(v2) / v1.dot(v2)
caryclarkeed356d2016-09-14 07:18:20 -07001271 const SkDVector* sweep = fPart.fSweep;
1272 const SkDVector* tweep = rh->fPart.fSweep;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001273 double s0dt0 = sweep[0].dot(tweep[0]);
1274 if (!s0dt0) {
1275 return true;
1276 }
1277 SkASSERT(s0dt0 != 0);
1278 double m = s0xt0 / s0dt0;
1279 double sDist = sweep[0].length() * m;
1280 double tDist = tweep[0].length() * m;
1281 bool useS = fabs(sDist) < fabs(tDist);
caryclark54359292015-03-26 07:52:43 -07001282 double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
caryclarkb36a3cd2016-10-18 07:59:44 -07001283 fTangentsAmbiguous = mFactor >= 50 && mFactor < 200;
caryclark55888e42016-07-18 10:01:36 -07001284 return mFactor < 50; // empirically found limit
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001285}