blob: aa72394043a695c6b57203ef6c407fe87a40132d [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"
9#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.coma5e55922013-05-07 18:51:31 +000012#if DEBUG_SORT || DEBUG_SORT_SINGLE
13#include "SkOpSegment.h"
14#endif
15
caryclark@google.com07393ca2013-04-08 11:47:37 +000016// FIXME: this is bogus for quads and cubics
17// if the quads and cubics' line from end pt to ctrl pt are coincident,
18// there's no obvious way to determine the curve ordering from the
19// derivatives alone. In particular, if one quadratic's coincident tangent
20// is longer than the other curve, the final control point can place the
21// longer curve on either side of the shorter one.
22// Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
23// may provide some help, but nothing has been figured out yet.
24
25/*(
26for quads and cubics, set up a parameterized line (e.g. LineParameters )
27for points [0] to [1]. See if point [2] is on that line, or on one side
28or the other. If it both quads' end points are on the same side, choose
29the shorter tangent. If the tangents are equal, choose the better second
30tangent angle
31
32maybe I could set up LineParameters lazily
33*/
caryclark@google.coma5e55922013-05-07 18:51:31 +000034static int simple_compare(double x, double y, double rx, double ry) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000035 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
36 return y < 0;
37 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000038 if (y == 0 && ry == 0 && x * rx < 0) {
39 return x < rx;
40 }
41 double x_ry = x * ry;
42 double rx_y = rx * y;
43 double cmp = x_ry - rx_y;
44 if (!approximately_zero(cmp)) {
45 return cmp < 0;
46 }
47 if (approximately_zero(x_ry) && approximately_zero(rx_y)
48 && !approximately_zero_squared(cmp)) {
49 return cmp < 0;
50 }
caryclark@google.coma5e55922013-05-07 18:51:31 +000051 return -1;
52}
53
54bool SkOpAngle::operator<(const SkOpAngle& rh) const {
55 double x = dx();
56 double y = dy();
57 double rx = rh.dx();
58 double ry = rh.dy();
59 int simple = simple_compare(x, y, rx, ry);
60 if (simple >= 0) {
61 return simple;
62 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000063 // at this point, the initial tangent line is coincident
64 // see if edges curl away from each other
65 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
66 || !approximately_zero(rh.fSide))) {
67 // FIXME: running demo will trigger this assertion
68 // (don't know if commenting out will trigger further assertion or not)
69 // commenting it out allows demo to run in release, though
70 return fSide < rh.fSide;
71 }
72 // see if either curve can be lengthened and try the tangent compare again
caryclark@google.com03610322013-04-18 15:58:21 +000073 if (/* cmp && */ (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
75 SkOpAngle longer = *this;
76 SkOpAngle rhLonger = rh;
77 if (longer.lengthen() | rhLonger.lengthen()) {
78 return longer < rhLonger;
79 }
80 }
81 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
82 || (rh.fVerb == SkPath::kLine_Verb
83 && approximately_zero(rx) && approximately_zero(ry))) {
84 // See general unsortable comment below. This case can happen when
85 // one line has a non-zero change in t but no change in x and y.
86 fUnsortable = true;
87 rh.fUnsortable = true;
88 return this < &rh; // even with no solution, return a stable sort
89 }
90 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
91 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
92 fUnsortable = true;
93 rh.fUnsortable = true;
94 return this < &rh; // even with no solution, return a stable sort
95 }
96 SkASSERT(fVerb >= SkPath::kQuad_Verb);
97 SkASSERT(rh.fVerb >= SkPath::kQuad_Verb);
98 // FIXME: until I can think of something better, project a ray from the
99 // end of the shorter tangent to midway between the end points
100 // through both curves and use the resulting angle to sort
101 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
102 double len = fTangent1.normalSquared();
103 double rlen = rh.fTangent1.normalSquared();
104 SkDLine ray;
105 SkIntersections i, ri;
106 int roots, rroots;
107 bool flip = false;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000108 bool useThis;
109 bool leftLessThanRight = fSide > 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 do {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000111 useThis = (len < rlen) ^ flip;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart;
113 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb;
114 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
115 part[2] : part[1];
116 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2;
117 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2;
118 SkASSERT(ray[0] != ray[1]);
119 roots = (i.*CurveRay[fVerb])(fPts, ray);
120 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray);
121 } while ((roots == 0 || rroots == 0) && (flip ^= true));
122 if (roots == 0 || rroots == 0) {
123 // FIXME: we don't have a solution in this case. The interim solution
124 // is to mark the edges as unsortable, exclude them from this and
125 // future computations, and allow the returned path to be fragmented
126 fUnsortable = true;
127 rh.fUnsortable = true;
128 return this < &rh; // even with no solution, return a stable sort
129 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000130 SkASSERT(fSide != 0 && rh.fSide != 0);
131 SkASSERT(fSide * rh.fSide > 0); // both are the same sign
132 SkDPoint lLoc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 double best = SK_ScalarInfinity;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000134#if DEBUG_SORT
135 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n",
136 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray[0].fY,
137 ray[1].fX, ray[1].fY, "-+"[fSide > 0]);
138#endif
139 for (int index = 0; index < roots; ++index) {
140 SkDPoint loc = i.pt(index);
141 SkDVector dxy = loc - ray[0];
142 double dist = dxy.lengthSquared();
143#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000144 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 +0000145 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY);
146#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000147 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000148 lLoc = loc;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000149 best = dist;
150 }
151 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000152 flip = false;
153 SkDPoint rLoc;
154 for (int index = 0; index < rroots; ++index) {
155 rLoc = ri.pt(index);
156 SkDVector dxy = rLoc - ray[0];
157 double dist = dxy.lengthSquared();
158#if DEBUG_SORT
skia.committer@gmail.com2b34fe02013-05-08 07:01:40 +0000159 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 +0000160 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY);
161#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000162 if (best > dist) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000163 flip = true;
164 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 }
166 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000167 #if 0
168 SkDVector lRay = lLoc - fCurvePart[0];
169 SkDVector rRay = rLoc - fCurvePart[0];
170 int rayDir = simple_compare(lRay.fX, lRay.fY, rRay.fX, rRay.fY);
171 SkASSERT(rayDir >= 0);
172 if (rayDir < 0) {
173 fUnsortable = true;
174 rh.fUnsortable = true;
175 return this < &rh; // even with no solution, return a stable sort
176 }
177#endif
178 if (flip) {
179 leftLessThanRight = !leftLessThanRight;
180 // rayDir = !rayDir;
181 }
182#if 0 && (DEBUG_SORT || DEBUG_SORT_SINGLE)
183 SkDebugf("%d %c %d (fSide %c 0) loc={{%1.9g,%1.9g}, {%1.9g,%1.9g}} flip=%d rayDir=%d\n",
184 fSegment->debugID(), "><"[leftLessThanRight], rh.fSegment->debugID(),
185 "<>"[fSide > 0], lLoc.fX, lLoc.fY, rLoc.fX, rLoc.fY, flip, rayDir);
186#endif
187// SkASSERT(leftLessThanRight == (bool) rayDir);
188 return leftLessThanRight;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189}
190
191bool SkOpAngle::lengthen() {
192 int newEnd = fEnd;
193 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
194 fEnd = newEnd;
195 setSpans();
196 return true;
197 }
198 return false;
199}
200
201bool SkOpAngle::reverseLengthen() {
202 if (fReversed) {
203 return false;
204 }
205 int newEnd = fStart;
206 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
207 fEnd = newEnd;
208 fReversed = true;
209 setSpans();
210 return true;
211 }
212 return false;
213}
214
215void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* segment,
216 int start, int end, const SkTDArray<SkOpSpan>& spans) {
217 fSegment = segment;
218 fStart = start;
219 fEnd = end;
220 fPts = orig;
221 fVerb = verb;
222 fSpans = &spans;
223 fReversed = false;
224 fUnsortable = false;
225 setSpans();
226}
227
228
229void SkOpAngle::setSpans() {
230 double startT = (*fSpans)[fStart].fT;
231 double endT = (*fSpans)[fEnd].fT;
232 switch (fVerb) {
233 case SkPath::kLine_Verb: {
234 SkDLine l = SkDLine::SubDivide(fPts, startT, endT);
235 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
236 fTangent1.lineEndPoints(l);
237 fSide = 0;
238 } break;
239 case SkPath::kQuad_Verb: {
240 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
241 quad = SkDQuad::SubDivide(fPts, startT, endT);
242 fTangent1.quadEndPoints(quad, 0, 1);
243 if (dx() == 0 && dy() == 0) {
244 fTangent1.quadEndPoints(quad);
245 }
246 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
247 } break;
248 case SkPath::kCubic_Verb: {
caryclark@google.comb3f09212013-04-17 15:49:16 +0000249 // int nextC = 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000250 fCurvePart = SkDCubic::SubDivide(fPts, startT, endT);
251 fTangent1.cubicEndPoints(fCurvePart, 0, 1);
252 if (dx() == 0 && dy() == 0) {
253 fTangent1.cubicEndPoints(fCurvePart, 0, 2);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000254 // nextC = 3;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255 if (dx() == 0 && dy() == 0) {
256 fTangent1.cubicEndPoints(fCurvePart, 0, 3);
257 }
258 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000259 // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only
260 // if (nextC == 2 && approximately_zero(fSide)) {
261 // fSide = -fTangent1.pointDistance(fCurvePart[3]);
262 // }
263 double testTs[4];
264 // OPTIMIZATION: keep inflections precomputed with cubic segment?
265 int testCount = SkDCubic::FindInflections(fPts, testTs);
266 double limitT = endT;
267 int index;
268 for (index = 0; index < testCount; ++index) {
269 if (!between(startT, testTs[index], limitT)) {
270 testTs[index] = -1;
271 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000272 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000273 testTs[testCount++] = startT;
274 testTs[testCount++] = endT;
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000275 SkTQSort<double>(testTs, &testTs[testCount - 1]);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000276 double bestSide = 0;
277 int testCases = (testCount << 1) - 1;
278 index = 0;
279 while (testTs[index] < 0) {
280 ++index;
281 }
282 index <<= 1;
283 for (; index < testCases; ++index) {
284 int testIndex = index >> 1;
285 double testT = testTs[testIndex];
286 if (index & 1) {
287 testT = (testT + testTs[testIndex + 1]) / 2;
288 }
289 // OPTIMIZE: could avoid call for t == startT, endT
290 SkDPoint pt = dcubic_xy_at_t(fPts, testT);
291 double testSide = fTangent1.pointDistance(pt);
292 if (fabs(bestSide) < fabs(testSide)) {
293 bestSide = testSide;
294 }
295 }
296 fSide = -bestSide; // compare sign only
caryclark@google.com07393ca2013-04-08 11:47:37 +0000297 } break;
298 default:
299 SkASSERT(0);
300 }
301 fUnsortable = dx() == 0 && dy() == 0;
302 if (fUnsortable) {
303 return;
304 }
305 SkASSERT(fStart != fEnd);
306 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
307 for (int index = fStart; index != fEnd; index += step) {
308#if 1
309 const SkOpSpan& thisSpan = (*fSpans)[index];
310 const SkOpSpan& nextSpan = (*fSpans)[index + step];
311 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) {
312 continue;
313 }
314 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
315#if DEBUG_UNSORTABLE
316 if (fUnsortable) {
317 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT);
318 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT);
319 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
320 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
321 }
322#endif
323 return;
324#else
325 if ((*fSpans)[index].fUnsortableStart) {
326 fUnsortable = true;
327 return;
328 }
329#endif
330 }
331#if 1
332#if DEBUG_UNSORTABLE
333 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT);
334 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT);
335 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
336 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
337#endif
338 fUnsortable = true;
339#endif
340}