blob: f410ea37374a337b3200bf302716178b35dc9b8e [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
caryclarke839e782016-09-15 07:48:18 -070013// from blackpawn.com/texts/pointinpoly
14static bool pointInTriangle(const SkDPoint fPts[3], const SkDPoint& test) {
15 SkDVector v0 = fPts[2] - fPts[0];
16 SkDVector v1 = fPts[1] - fPts[0];
17 SkDVector v2 = test - fPts[0];
18 double dot00 = v0.dot(v0);
19 double dot01 = v0.dot(v1);
20 double dot02 = v0.dot(v2);
21 double dot11 = v1.dot(v1);
22 double dot12 = v1.dot(v2);
23 // Compute barycentric coordinates
Cary Clark1c2c2e22018-02-27 08:48:32 -050024 double denom = dot00 * dot11 - dot01 * dot01;
25 double u = dot11 * dot02 - dot01 * dot12;
26 double v = dot00 * dot12 - dot01 * dot02;
caryclarke839e782016-09-15 07:48:18 -070027 // Check if point is in triangle
Cary Clark1c2c2e22018-02-27 08:48:32 -050028 if (denom >= 0) {
29 return u >= 0 && v >= 0 && u + v < denom;
30 }
31 return u <= 0 && v <= 0 && u + v > denom;
caryclarke839e782016-09-15 07:48:18 -070032}
33
34static bool matchesEnd(const SkDPoint fPts[3], const SkDPoint& test) {
35 return fPts[0] == test || fPts[2] == test;
36}
37
caryclark54359292015-03-26 07:52:43 -070038/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
39// Do a quick reject by rotating all points relative to a line formed by
40// a pair of one quad's points. If the 2nd quad's points
41// are on the line or on the opposite side from the 1st quad's 'odd man', the
42// curves at most intersect at the endpoints.
43/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
44 if returning false, check contains true if the the quad pair have only the end point in common
45*/
46bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
47 bool linear = true;
48 for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
49 const SkDPoint* endPt[2];
50 this->otherPts(oddMan, endPt);
51 double origX = endPt[0]->fX;
52 double origY = endPt[0]->fY;
53 double adj = endPt[1]->fX - origX;
54 double opp = endPt[1]->fY - origY;
55 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
56 if (approximately_zero(sign)) {
57 continue;
58 }
59 linear = false;
60 bool foundOutlier = false;
61 for (int n = 0; n < kPointCount; ++n) {
62 double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
63 if (test * sign > 0 && !precisely_zero(test)) {
64 foundOutlier = true;
65 break;
66 }
67 }
68 if (!foundOutlier) {
69 return false;
70 }
71 }
caryclarke839e782016-09-15 07:48:18 -070072 if (linear && !matchesEnd(fPts, q2.fPts[0]) && !matchesEnd(fPts, q2.fPts[2])) {
73 // if the end point of the opposite quad is inside the hull that is nearly a line,
74 // then representing the quad as a line may cause the intersection to be missed.
75 // Check to see if the endpoint is in the triangle.
76 if (pointInTriangle(fPts, q2.fPts[0]) || pointInTriangle(fPts, q2.fPts[2])) {
77 linear = false;
78 }
79 }
caryclark54359292015-03-26 07:52:43 -070080 *isLinear = linear;
81 return true;
82}
83
caryclark1049f122015-04-20 08:31:59 -070084bool SkDQuad::hullIntersects(const SkDConic& conic, bool* isLinear) const {
85 return conic.hullIntersects(*this, isLinear);
86}
87
88bool SkDQuad::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
89 return cubic.hullIntersects(*this, isLinear);
90}
91
caryclark54359292015-03-26 07:52:43 -070092/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
93oddMan opp x=oddMan^opp x=x-oddMan m=x>>2 x&~m
94 0 1 1 1 0 1
95 2 2 2 0 2
96 1 1 0 -1 -1 0
97 2 3 2 0 2
98 2 1 3 1 0 1
99 2 0 -2 -1 0
100*/
101void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
102 for (int opp = 1; opp < kPointCount; ++opp) {
103 int end = (oddMan ^ opp) - oddMan; // choose a value not equal to oddMan
104 end &= ~(end >> 2); // if the value went negative, set it to zero
105 endPt[opp - 1] = &fPts[end];
106 }
107}
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108
caryclark@google.com07393ca2013-04-08 11:47:37 +0000109int SkDQuad::AddValidTs(double s[], int realRoots, double* t) {
110 int foundRoots = 0;
111 for (int index = 0; index < realRoots; ++index) {
112 double tValue = s[index];
113 if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
114 if (approximately_less_than_zero(tValue)) {
115 tValue = 0;
116 } else if (approximately_greater_than_one(tValue)) {
117 tValue = 1;
118 }
119 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
120 if (approximately_equal(t[idx2], tValue)) {
121 goto nextRoot;
122 }
123 }
124 t[foundRoots++] = tValue;
125 }
126nextRoot:
127 {}
128 }
129 return foundRoots;
130}
131
132// note: caller expects multiple results to be sorted smaller first
133// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
134// analysis of the quadratic equation, suggesting why the following looks at
135// the sign of B -- and further suggesting that the greatest loss of precision
136// is in b squared less two a c
137int SkDQuad::RootsValidT(double A, double B, double C, double t[2]) {
138 double s[2];
139 int realRoots = RootsReal(A, B, C, s);
140 int foundRoots = AddValidTs(s, realRoots, t);
141 return foundRoots;
142}
143
144/*
145Numeric Solutions (5.6) suggests to solve the quadratic by computing
146 Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
147and using the roots
148 t1 = Q / A
149 t2 = C / Q
150*/
151// this does not discard real roots <= 0 or >= 1
152int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) {
153 const double p = B / (2 * A);
154 const double q = C / A;
caryclarkb6693002015-12-16 12:28:35 -0800155 if (!A || (approximately_zero(A) && (approximately_zero_inverse(p)
156 || approximately_zero_inverse(q)))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 if (approximately_zero(B)) {
158 s[0] = 0;
159 return C == 0;
160 }
161 s[0] = -C / B;
162 return 1;
163 }
164 /* normal form: x^2 + px + q = 0 */
165 const double p2 = p * p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000166 if (!AlmostDequalUlps(p2, q) && p2 < q) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167 return 0;
168 }
169 double sqrt_D = 0;
170 if (p2 > q) {
171 sqrt_D = sqrt(p2 - q);
172 }
173 s[0] = sqrt_D - p;
174 s[1] = -sqrt_D - p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000175 return 1 + !AlmostDequalUlps(s[0], s[1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000176}
177
178bool SkDQuad::isLinear(int startIndex, int endIndex) const {
179 SkLineParameters lineParameters;
180 lineParameters.quadEndPoints(*this, startIndex, endIndex);
181 // FIXME: maybe it's possible to avoid this and compare non-normalized
182 lineParameters.normalize();
183 double distance = lineParameters.controlPtDistance(*this);
caryclark54359292015-03-26 07:52:43 -0700184 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
185 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
186 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
187 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
188 largest = SkTMax(largest, -tiniest);
189 return approximately_zero_when_compared_to(distance, largest);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000190}
191
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192SkDVector SkDQuad::dxdyAtT(double t) const {
193 double a = t - 1;
194 double b = 1 - 2 * t;
195 double c = t;
196 SkDVector result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
197 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
caryclark94c902e2015-08-18 07:12:43 -0700198 if (result.fX == 0 && result.fY == 0) {
199 if (zero_or_one(t)) {
200 result = fPts[2] - fPts[0];
201 } else {
202 // incomplete
203 SkDebugf("!q");
204 }
205 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000206 return result;
207}
208
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000209// OPTIMIZE: assert if caller passes in t == 0 / t == 1 ?
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000210SkDPoint SkDQuad::ptAtT(double t) const {
211 if (0 == t) {
212 return fPts[0];
213 }
214 if (1 == t) {
215 return fPts[2];
216 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000217 double one_t = 1 - t;
218 double a = one_t * one_t;
219 double b = 2 * one_t * t;
220 double c = t * t;
221 SkDPoint result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
222 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
223 return result;
224}
225
caryclark1049f122015-04-20 08:31:59 -0700226static double interp_quad_coords(const double* src, double t) {
caryclark55888e42016-07-18 10:01:36 -0700227 if (0 == t) {
228 return src[0];
229 }
230 if (1 == t) {
231 return src[4];
232 }
caryclark1049f122015-04-20 08:31:59 -0700233 double ab = SkDInterp(src[0], src[2], t);
234 double bc = SkDInterp(src[2], src[4], t);
235 double abc = SkDInterp(ab, bc, t);
236 return abc;
237}
238
caryclarkaec25102015-04-29 08:28:30 -0700239bool SkDQuad::monotonicInX() const {
240 return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
241}
242
caryclark1049f122015-04-20 08:31:59 -0700243bool SkDQuad::monotonicInY() const {
244 return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
245}
246
caryclark@google.com07393ca2013-04-08 11:47:37 +0000247/*
248Given a quadratic q, t1, and t2, find a small quadratic segment.
249
250The new quadratic is defined by A, B, and C, where
251 A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
252 C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
253
254To find B, compute the point halfway between t1 and t2:
255
256q(at (t1 + t2)/2) == D
257
258Next, compute where D must be if we know the value of B:
259
260_12 = A/2 + B/2
26112_ = B/2 + C/2
262123 = A/4 + B/2 + C/4
263 = D
264
265Group the known values on one side:
266
267B = D*2 - A/2 - C/2
268*/
269
caryclark55888e42016-07-18 10:01:36 -0700270// OPTIMIZE? : special case t1 = 1 && t2 = 0
caryclark@google.com07393ca2013-04-08 11:47:37 +0000271SkDQuad SkDQuad::subDivide(double t1, double t2) const {
caryclark55888e42016-07-18 10:01:36 -0700272 if (0 == t1 && 1 == t2) {
273 return *this;
274 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000275 SkDQuad dst;
276 double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
277 double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
278 double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
279 double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
280 double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
281 double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
caryclark1049f122015-04-20 08:31:59 -0700282 /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
283 /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000284 return dst;
285}
286
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000287void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000288 if (fPts[endIndex].fX == fPts[1].fX) {
289 dstPt->fX = fPts[endIndex].fX;
290 }
291 if (fPts[endIndex].fY == fPts[1].fY) {
292 dstPt->fY = fPts[endIndex].fY;
293 }
294}
295
caryclark@google.com07393ca2013-04-08 11:47:37 +0000296SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000297 SkASSERT(t1 != t2);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 SkDPoint b;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000299 SkDQuad sub = subDivide(t1, t2);
300 SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
301 SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
302 SkIntersections i;
303 i.intersectRay(b0, b1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000304 if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000305 b = i.pt(0);
306 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000307 SkASSERT(i.used() <= 2);
caryclark55888e42016-07-18 10:01:36 -0700308 return SkDPoint::Mid(b0[1], b1[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000309 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000310 if (t1 == 0 || t2 == 0) {
311 align(0, &b);
312 }
313 if (t1 == 1 || t2 == 1) {
314 align(2, &b);
315 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000316 if (AlmostBequalUlps(b.fX, a.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000317 b.fX = a.fX;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000318 } else if (AlmostBequalUlps(b.fX, c.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000319 b.fX = c.fX;
320 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000321 if (AlmostBequalUlps(b.fY, a.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000322 b.fY = a.fY;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000323 } else if (AlmostBequalUlps(b.fY, c.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000324 b.fY = c.fY;
325 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000326 return b;
327}
328
329/* classic one t subdivision */
330static void interp_quad_coords(const double* src, double* dst, double t) {
331 double ab = SkDInterp(src[0], src[2], t);
332 double bc = SkDInterp(src[2], src[4], t);
333 dst[0] = src[0];
334 dst[2] = ab;
335 dst[4] = SkDInterp(ab, bc, t);
336 dst[6] = bc;
337 dst[8] = src[4];
338}
339
340SkDQuadPair SkDQuad::chopAt(double t) const
341{
342 SkDQuadPair dst;
343 interp_quad_coords(&fPts[0].fX, &dst.pts[0].fX, t);
344 interp_quad_coords(&fPts[0].fY, &dst.pts[0].fY, t);
345 return dst;
346}
347
348static int valid_unit_divide(double numer, double denom, double* ratio)
349{
350 if (numer < 0) {
351 numer = -numer;
352 denom = -denom;
353 }
354 if (denom == 0 || numer == 0 || numer >= denom) {
355 return 0;
356 }
357 double r = numer / denom;
358 if (r == 0) { // catch underflow if numer <<<< denom
359 return 0;
360 }
361 *ratio = r;
362 return 1;
363}
364
365/** Quad'(t) = At + B, where
366 A = 2(a - 2b + c)
367 B = 2(b - a)
368 Solve for t, only if it fits between 0 < t < 1
369*/
caryclarkaec25102015-04-29 08:28:30 -0700370int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000371 /* At + B == 0
372 t = -B / A
373 */
caryclarkaec25102015-04-29 08:28:30 -0700374 double a = src[0];
375 double b = src[2];
376 double c = src[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000377 return valid_unit_divide(a - b, a - b - b + c, tValue);
378}
379
380/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
381 *
382 * a = A - 2*B + C
383 * b = 2*B - 2*C
384 * c = C
385 */
386void SkDQuad::SetABC(const double* quad, double* a, double* b, double* c) {
387 *a = quad[0]; // a = A
388 *b = 2 * quad[2]; // b = 2*B
389 *c = quad[4]; // c = C
390 *b -= *c; // b = 2*B - C
391 *a -= *b; // a = A - 2*B + C
392 *b -= *c; // b = 2*B - 2*C
393}