blob: dafa3f5b7588cbebbcb64465fb527526d1efba5b [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.comcffbcc32013-06-04 17:59:42 +00007#include "SkIntersections.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00008#include "SkLineParameters.h"
9#include "SkPathOpsCubic.h"
caryclark03b03ca2015-04-23 09:13:37 -070010#include "SkPathOpsCurve.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011#include "SkPathOpsQuad.h"
caryclark54359292015-03-26 07:52:43 -070012
13/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
14// Do a quick reject by rotating all points relative to a line formed by
15// a pair of one quad's points. If the 2nd quad's points
16// are on the line or on the opposite side from the 1st quad's 'odd man', the
17// curves at most intersect at the endpoints.
18/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
19 if returning false, check contains true if the the quad pair have only the end point in common
20*/
21bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
22 bool linear = true;
23 for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
24 const SkDPoint* endPt[2];
25 this->otherPts(oddMan, endPt);
26 double origX = endPt[0]->fX;
27 double origY = endPt[0]->fY;
28 double adj = endPt[1]->fX - origX;
29 double opp = endPt[1]->fY - origY;
30 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
31 if (approximately_zero(sign)) {
32 continue;
33 }
34 linear = false;
35 bool foundOutlier = false;
36 for (int n = 0; n < kPointCount; ++n) {
37 double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
38 if (test * sign > 0 && !precisely_zero(test)) {
39 foundOutlier = true;
40 break;
41 }
42 }
43 if (!foundOutlier) {
44 return false;
45 }
46 }
47 *isLinear = linear;
48 return true;
49}
50
caryclark1049f122015-04-20 08:31:59 -070051bool SkDQuad::hullIntersects(const SkDConic& conic, bool* isLinear) const {
52 return conic.hullIntersects(*this, isLinear);
53}
54
55bool SkDQuad::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
56 return cubic.hullIntersects(*this, isLinear);
57}
58
caryclark54359292015-03-26 07:52:43 -070059/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
60oddMan opp x=oddMan^opp x=x-oddMan m=x>>2 x&~m
61 0 1 1 1 0 1
62 2 2 2 0 2
63 1 1 0 -1 -1 0
64 2 3 2 0 2
65 2 1 3 1 0 1
66 2 0 -2 -1 0
67*/
68void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
69 for (int opp = 1; opp < kPointCount; ++opp) {
70 int end = (oddMan ^ opp) - oddMan; // choose a value not equal to oddMan
71 end &= ~(end >> 2); // if the value went negative, set it to zero
72 endPt[opp - 1] = &fPts[end];
73 }
74}
caryclark@google.com07393ca2013-04-08 11:47:37 +000075
caryclark@google.com07393ca2013-04-08 11:47:37 +000076int SkDQuad::AddValidTs(double s[], int realRoots, double* t) {
77 int foundRoots = 0;
78 for (int index = 0; index < realRoots; ++index) {
79 double tValue = s[index];
80 if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
81 if (approximately_less_than_zero(tValue)) {
82 tValue = 0;
83 } else if (approximately_greater_than_one(tValue)) {
84 tValue = 1;
85 }
86 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
87 if (approximately_equal(t[idx2], tValue)) {
88 goto nextRoot;
89 }
90 }
91 t[foundRoots++] = tValue;
92 }
93nextRoot:
94 {}
95 }
96 return foundRoots;
97}
98
99// note: caller expects multiple results to be sorted smaller first
100// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
101// analysis of the quadratic equation, suggesting why the following looks at
102// the sign of B -- and further suggesting that the greatest loss of precision
103// is in b squared less two a c
104int SkDQuad::RootsValidT(double A, double B, double C, double t[2]) {
105 double s[2];
106 int realRoots = RootsReal(A, B, C, s);
107 int foundRoots = AddValidTs(s, realRoots, t);
108 return foundRoots;
109}
110
111/*
112Numeric Solutions (5.6) suggests to solve the quadratic by computing
113 Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
114and using the roots
115 t1 = Q / A
116 t2 = C / Q
117*/
118// this does not discard real roots <= 0 or >= 1
119int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) {
120 const double p = B / (2 * A);
121 const double q = C / A;
caryclarkb6693002015-12-16 12:28:35 -0800122 if (!A || (approximately_zero(A) && (approximately_zero_inverse(p)
123 || approximately_zero_inverse(q)))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000124 if (approximately_zero(B)) {
125 s[0] = 0;
126 return C == 0;
127 }
128 s[0] = -C / B;
129 return 1;
130 }
131 /* normal form: x^2 + px + q = 0 */
132 const double p2 = p * p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000133 if (!AlmostDequalUlps(p2, q) && p2 < q) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000134 return 0;
135 }
136 double sqrt_D = 0;
137 if (p2 > q) {
138 sqrt_D = sqrt(p2 - q);
139 }
140 s[0] = sqrt_D - p;
141 s[1] = -sqrt_D - p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000142 return 1 + !AlmostDequalUlps(s[0], s[1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000143}
144
145bool SkDQuad::isLinear(int startIndex, int endIndex) const {
146 SkLineParameters lineParameters;
147 lineParameters.quadEndPoints(*this, startIndex, endIndex);
148 // FIXME: maybe it's possible to avoid this and compare non-normalized
149 lineParameters.normalize();
150 double distance = lineParameters.controlPtDistance(*this);
caryclark54359292015-03-26 07:52:43 -0700151 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
152 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
153 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
154 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
155 largest = SkTMax(largest, -tiniest);
156 return approximately_zero_when_compared_to(distance, largest);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157}
158
caryclark@google.com07393ca2013-04-08 11:47:37 +0000159SkDVector SkDQuad::dxdyAtT(double t) const {
160 double a = t - 1;
161 double b = 1 - 2 * t;
162 double c = t;
163 SkDVector result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
164 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
caryclark94c902e2015-08-18 07:12:43 -0700165 if (result.fX == 0 && result.fY == 0) {
166 if (zero_or_one(t)) {
167 result = fPts[2] - fPts[0];
168 } else {
169 // incomplete
170 SkDebugf("!q");
171 }
172 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000173 return result;
174}
175
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000176// OPTIMIZE: assert if caller passes in t == 0 / t == 1 ?
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000177SkDPoint SkDQuad::ptAtT(double t) const {
178 if (0 == t) {
179 return fPts[0];
180 }
181 if (1 == t) {
182 return fPts[2];
183 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 double one_t = 1 - t;
185 double a = one_t * one_t;
186 double b = 2 * one_t * t;
187 double c = t * t;
188 SkDPoint result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
189 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
190 return result;
191}
192
caryclark1049f122015-04-20 08:31:59 -0700193static double interp_quad_coords(const double* src, double t) {
caryclark55888e42016-07-18 10:01:36 -0700194 if (0 == t) {
195 return src[0];
196 }
197 if (1 == t) {
198 return src[4];
199 }
caryclark1049f122015-04-20 08:31:59 -0700200 double ab = SkDInterp(src[0], src[2], t);
201 double bc = SkDInterp(src[2], src[4], t);
202 double abc = SkDInterp(ab, bc, t);
203 return abc;
204}
205
caryclarkaec25102015-04-29 08:28:30 -0700206bool SkDQuad::monotonicInX() const {
207 return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
208}
209
caryclark1049f122015-04-20 08:31:59 -0700210bool SkDQuad::monotonicInY() const {
211 return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
212}
213
caryclark@google.com07393ca2013-04-08 11:47:37 +0000214/*
215Given a quadratic q, t1, and t2, find a small quadratic segment.
216
217The new quadratic is defined by A, B, and C, where
218 A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
219 C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
220
221To find B, compute the point halfway between t1 and t2:
222
223q(at (t1 + t2)/2) == D
224
225Next, compute where D must be if we know the value of B:
226
227_12 = A/2 + B/2
22812_ = B/2 + C/2
229123 = A/4 + B/2 + C/4
230 = D
231
232Group the known values on one side:
233
234B = D*2 - A/2 - C/2
235*/
236
caryclark55888e42016-07-18 10:01:36 -0700237// OPTIMIZE? : special case t1 = 1 && t2 = 0
caryclark@google.com07393ca2013-04-08 11:47:37 +0000238SkDQuad SkDQuad::subDivide(double t1, double t2) const {
caryclark55888e42016-07-18 10:01:36 -0700239 if (0 == t1 && 1 == t2) {
240 return *this;
241 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000242 SkDQuad dst;
243 double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
244 double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
245 double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
246 double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
247 double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
248 double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
caryclark1049f122015-04-20 08:31:59 -0700249 /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
250 /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000251 return dst;
252}
253
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000254void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000255 if (fPts[endIndex].fX == fPts[1].fX) {
256 dstPt->fX = fPts[endIndex].fX;
257 }
258 if (fPts[endIndex].fY == fPts[1].fY) {
259 dstPt->fY = fPts[endIndex].fY;
260 }
261}
262
caryclark@google.com07393ca2013-04-08 11:47:37 +0000263SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000264 SkASSERT(t1 != t2);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000265 SkDPoint b;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000266 SkDQuad sub = subDivide(t1, t2);
267 SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
268 SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
269 SkIntersections i;
270 i.intersectRay(b0, b1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000271 if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000272 b = i.pt(0);
273 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000274 SkASSERT(i.used() <= 2);
caryclark55888e42016-07-18 10:01:36 -0700275 return SkDPoint::Mid(b0[1], b1[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000276 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000277 if (t1 == 0 || t2 == 0) {
278 align(0, &b);
279 }
280 if (t1 == 1 || t2 == 1) {
281 align(2, &b);
282 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000283 if (AlmostBequalUlps(b.fX, a.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000284 b.fX = a.fX;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000285 } else if (AlmostBequalUlps(b.fX, c.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000286 b.fX = c.fX;
287 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000288 if (AlmostBequalUlps(b.fY, a.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000289 b.fY = a.fY;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000290 } else if (AlmostBequalUlps(b.fY, c.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000291 b.fY = c.fY;
292 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000293 return b;
294}
295
296/* classic one t subdivision */
297static void interp_quad_coords(const double* src, double* dst, double t) {
298 double ab = SkDInterp(src[0], src[2], t);
299 double bc = SkDInterp(src[2], src[4], t);
300 dst[0] = src[0];
301 dst[2] = ab;
302 dst[4] = SkDInterp(ab, bc, t);
303 dst[6] = bc;
304 dst[8] = src[4];
305}
306
307SkDQuadPair SkDQuad::chopAt(double t) const
308{
309 SkDQuadPair dst;
310 interp_quad_coords(&fPts[0].fX, &dst.pts[0].fX, t);
311 interp_quad_coords(&fPts[0].fY, &dst.pts[0].fY, t);
312 return dst;
313}
314
315static int valid_unit_divide(double numer, double denom, double* ratio)
316{
317 if (numer < 0) {
318 numer = -numer;
319 denom = -denom;
320 }
321 if (denom == 0 || numer == 0 || numer >= denom) {
322 return 0;
323 }
324 double r = numer / denom;
325 if (r == 0) { // catch underflow if numer <<<< denom
326 return 0;
327 }
328 *ratio = r;
329 return 1;
330}
331
332/** Quad'(t) = At + B, where
333 A = 2(a - 2b + c)
334 B = 2(b - a)
335 Solve for t, only if it fits between 0 < t < 1
336*/
caryclarkaec25102015-04-29 08:28:30 -0700337int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000338 /* At + B == 0
339 t = -B / A
340 */
caryclarkaec25102015-04-29 08:28:30 -0700341 double a = src[0];
342 double b = src[2];
343 double c = src[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000344 return valid_unit_divide(a - b, a - b - b + c, tValue);
345}
346
347/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
348 *
349 * a = A - 2*B + C
350 * b = 2*B - 2*C
351 * c = C
352 */
353void SkDQuad::SetABC(const double* quad, double* a, double* b, double* c) {
354 *a = quad[0]; // a = A
355 *b = 2 * quad[2]; // b = 2*B
356 *c = quad[4]; // c = C
357 *b -= *c; // b = 2*B - C
358 *a -= *b; // a = A - 2*B + C
359 *b -= *c; // b = 2*B - 2*C
360}