blob: 7cdfd8c04b68705eb0242034882626f0ea12833d [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"
caryclark@google.comb3f09212013-04-17 15:49:16 +000010#include "TSearch.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
12// FIXME: this is bogus for quads and cubics
13// if the quads and cubics' line from end pt to ctrl pt are coincident,
14// there's no obvious way to determine the curve ordering from the
15// derivatives alone. In particular, if one quadratic's coincident tangent
16// is longer than the other curve, the final control point can place the
17// longer curve on either side of the shorter one.
18// Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
19// may provide some help, but nothing has been figured out yet.
20
21/*(
22for quads and cubics, set up a parameterized line (e.g. LineParameters )
23for points [0] to [1]. See if point [2] is on that line, or on one side
24or the other. If it both quads' end points are on the same side, choose
25the shorter tangent. If the tangents are equal, choose the better second
26tangent angle
27
28maybe I could set up LineParameters lazily
29*/
30bool SkOpAngle::operator<(const SkOpAngle& rh) const {
31 double y = dy();
32 double ry = rh.dy();
33 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
34 return y < 0;
35 }
36 double x = dx();
37 double rx = rh.dx();
38 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 }
51 // at this point, the initial tangent line is coincident
52 // see if edges curl away from each other
53 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
54 || !approximately_zero(rh.fSide))) {
55 // FIXME: running demo will trigger this assertion
56 // (don't know if commenting out will trigger further assertion or not)
57 // commenting it out allows demo to run in release, though
58 return fSide < rh.fSide;
59 }
60 // see if either curve can be lengthened and try the tangent compare again
61 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
62 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
63 SkOpAngle longer = *this;
64 SkOpAngle rhLonger = rh;
65 if (longer.lengthen() | rhLonger.lengthen()) {
66 return longer < rhLonger;
67 }
68 }
69 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
70 || (rh.fVerb == SkPath::kLine_Verb
71 && approximately_zero(rx) && approximately_zero(ry))) {
72 // See general unsortable comment below. This case can happen when
73 // one line has a non-zero change in t but no change in x and y.
74 fUnsortable = true;
75 rh.fUnsortable = true;
76 return this < &rh; // even with no solution, return a stable sort
77 }
78 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
79 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
80 fUnsortable = true;
81 rh.fUnsortable = true;
82 return this < &rh; // even with no solution, return a stable sort
83 }
84 SkASSERT(fVerb >= SkPath::kQuad_Verb);
85 SkASSERT(rh.fVerb >= SkPath::kQuad_Verb);
86 // FIXME: until I can think of something better, project a ray from the
87 // end of the shorter tangent to midway between the end points
88 // through both curves and use the resulting angle to sort
89 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
90 double len = fTangent1.normalSquared();
91 double rlen = rh.fTangent1.normalSquared();
92 SkDLine ray;
93 SkIntersections i, ri;
94 int roots, rroots;
95 bool flip = false;
96 do {
97 bool useThis = (len < rlen) ^ flip;
98 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart;
99 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb;
100 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
101 part[2] : part[1];
102 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2;
103 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2;
104 SkASSERT(ray[0] != ray[1]);
105 roots = (i.*CurveRay[fVerb])(fPts, ray);
106 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray);
107 } while ((roots == 0 || rroots == 0) && (flip ^= true));
108 if (roots == 0 || rroots == 0) {
109 // FIXME: we don't have a solution in this case. The interim solution
110 // is to mark the edges as unsortable, exclude them from this and
111 // future computations, and allow the returned path to be fragmented
112 fUnsortable = true;
113 rh.fUnsortable = true;
114 return this < &rh; // even with no solution, return a stable sort
115 }
116 SkDPoint loc;
117 double best = SK_ScalarInfinity;
118 SkDVector dxy;
119 double dist;
120 int index;
121 for (index = 0; index < roots; ++index) {
122 loc = (*CurveDPointAtT[fVerb])(fPts, i[0][index]);
123 dxy = loc - ray[0];
124 dist = dxy.lengthSquared();
125 if (best > dist) {
126 best = dist;
127 }
128 }
129 for (index = 0; index < rroots; ++index) {
130 loc = (*CurveDPointAtT[rh.fVerb])(rh.fPts, ri[0][index]);
131 dxy = loc - ray[0];
132 dist = dxy.lengthSquared();
133 if (best > dist) {
134 return fSide < 0;
135 }
136 }
137 return fSide > 0;
138}
139
140bool SkOpAngle::lengthen() {
141 int newEnd = fEnd;
142 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
143 fEnd = newEnd;
144 setSpans();
145 return true;
146 }
147 return false;
148}
149
150bool SkOpAngle::reverseLengthen() {
151 if (fReversed) {
152 return false;
153 }
154 int newEnd = fStart;
155 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
156 fEnd = newEnd;
157 fReversed = true;
158 setSpans();
159 return true;
160 }
161 return false;
162}
163
164void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* segment,
165 int start, int end, const SkTDArray<SkOpSpan>& spans) {
166 fSegment = segment;
167 fStart = start;
168 fEnd = end;
169 fPts = orig;
170 fVerb = verb;
171 fSpans = &spans;
172 fReversed = false;
173 fUnsortable = false;
174 setSpans();
175}
176
177
178void SkOpAngle::setSpans() {
179 double startT = (*fSpans)[fStart].fT;
180 double endT = (*fSpans)[fEnd].fT;
181 switch (fVerb) {
182 case SkPath::kLine_Verb: {
183 SkDLine l = SkDLine::SubDivide(fPts, startT, endT);
184 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
185 fTangent1.lineEndPoints(l);
186 fSide = 0;
187 } break;
188 case SkPath::kQuad_Verb: {
189 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart);
190 quad = SkDQuad::SubDivide(fPts, startT, endT);
191 fTangent1.quadEndPoints(quad, 0, 1);
192 if (dx() == 0 && dy() == 0) {
193 fTangent1.quadEndPoints(quad);
194 }
195 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
196 } break;
197 case SkPath::kCubic_Verb: {
caryclark@google.comb3f09212013-04-17 15:49:16 +0000198 // int nextC = 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000199 fCurvePart = SkDCubic::SubDivide(fPts, startT, endT);
200 fTangent1.cubicEndPoints(fCurvePart, 0, 1);
201 if (dx() == 0 && dy() == 0) {
202 fTangent1.cubicEndPoints(fCurvePart, 0, 2);
caryclark@google.comb3f09212013-04-17 15:49:16 +0000203 // nextC = 3;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000204 if (dx() == 0 && dy() == 0) {
205 fTangent1.cubicEndPoints(fCurvePart, 0, 3);
206 }
207 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000208 // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only
209 // if (nextC == 2 && approximately_zero(fSide)) {
210 // fSide = -fTangent1.pointDistance(fCurvePart[3]);
211 // }
212 double testTs[4];
213 // OPTIMIZATION: keep inflections precomputed with cubic segment?
214 int testCount = SkDCubic::FindInflections(fPts, testTs);
215 double limitT = endT;
216 int index;
217 for (index = 0; index < testCount; ++index) {
218 if (!between(startT, testTs[index], limitT)) {
219 testTs[index] = -1;
220 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000221 }
caryclark@google.comb3f09212013-04-17 15:49:16 +0000222 testTs[testCount++] = startT;
223 testTs[testCount++] = endT;
224 QSort<double>(testTs, &testTs[testCount - 1]);
225 double bestSide = 0;
226 int testCases = (testCount << 1) - 1;
227 index = 0;
228 while (testTs[index] < 0) {
229 ++index;
230 }
231 index <<= 1;
232 for (; index < testCases; ++index) {
233 int testIndex = index >> 1;
234 double testT = testTs[testIndex];
235 if (index & 1) {
236 testT = (testT + testTs[testIndex + 1]) / 2;
237 }
238 // OPTIMIZE: could avoid call for t == startT, endT
239 SkDPoint pt = dcubic_xy_at_t(fPts, testT);
240 double testSide = fTangent1.pointDistance(pt);
241 if (fabs(bestSide) < fabs(testSide)) {
242 bestSide = testSide;
243 }
244 }
245 fSide = -bestSide; // compare sign only
caryclark@google.com07393ca2013-04-08 11:47:37 +0000246 } break;
247 default:
248 SkASSERT(0);
249 }
250 fUnsortable = dx() == 0 && dy() == 0;
251 if (fUnsortable) {
252 return;
253 }
254 SkASSERT(fStart != fEnd);
255 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
256 for (int index = fStart; index != fEnd; index += step) {
257#if 1
258 const SkOpSpan& thisSpan = (*fSpans)[index];
259 const SkOpSpan& nextSpan = (*fSpans)[index + step];
260 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) {
261 continue;
262 }
263 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
264#if DEBUG_UNSORTABLE
265 if (fUnsortable) {
266 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT);
267 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT);
268 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
269 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
270 }
271#endif
272 return;
273#else
274 if ((*fSpans)[index].fUnsortableStart) {
275 fUnsortable = true;
276 return;
277 }
278#endif
279 }
280#if 1
281#if DEBUG_UNSORTABLE
282 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT);
283 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT);
284 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
285 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
286#endif
287 fUnsortable = true;
288#endif
289}