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