blob: 9a104bfe2bb9609aa132ed84c5a94229c2fc6e22 [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
Cary Clark65247e52018-03-13 10:39:30 -0400144static int handle_zero(const double B, const double C, double s[2]) {
145 if (approximately_zero(B)) {
146 s[0] = 0;
147 return C == 0;
148 }
149 s[0] = -C / B;
150 return 1;
151}
152
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153/*
154Numeric Solutions (5.6) suggests to solve the quadratic by computing
155 Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
156and using the roots
157 t1 = Q / A
158 t2 = C / Q
159*/
160// this does not discard real roots <= 0 or >= 1
161int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) {
Cary Clark65247e52018-03-13 10:39:30 -0400162 if (!A) {
163 return handle_zero(B, C, s);
164 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 const double p = B / (2 * A);
166 const double q = C / A;
Cary Clark65247e52018-03-13 10:39:30 -0400167 if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) {
168 return handle_zero(B, C, s);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000169 }
170 /* normal form: x^2 + px + q = 0 */
171 const double p2 = p * p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000172 if (!AlmostDequalUlps(p2, q) && p2 < q) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000173 return 0;
174 }
175 double sqrt_D = 0;
176 if (p2 > q) {
177 sqrt_D = sqrt(p2 - q);
178 }
179 s[0] = sqrt_D - p;
180 s[1] = -sqrt_D - p;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000181 return 1 + !AlmostDequalUlps(s[0], s[1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000182}
183
184bool SkDQuad::isLinear(int startIndex, int endIndex) const {
185 SkLineParameters lineParameters;
186 lineParameters.quadEndPoints(*this, startIndex, endIndex);
187 // FIXME: maybe it's possible to avoid this and compare non-normalized
188 lineParameters.normalize();
189 double distance = lineParameters.controlPtDistance(*this);
caryclark54359292015-03-26 07:52:43 -0700190 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
191 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
192 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
193 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
194 largest = SkTMax(largest, -tiniest);
195 return approximately_zero_when_compared_to(distance, largest);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000196}
197
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198SkDVector SkDQuad::dxdyAtT(double t) const {
199 double a = t - 1;
200 double b = 1 - 2 * t;
201 double c = t;
202 SkDVector result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
203 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
caryclark94c902e2015-08-18 07:12:43 -0700204 if (result.fX == 0 && result.fY == 0) {
205 if (zero_or_one(t)) {
206 result = fPts[2] - fPts[0];
207 } else {
208 // incomplete
209 SkDebugf("!q");
210 }
211 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212 return result;
213}
214
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000215// OPTIMIZE: assert if caller passes in t == 0 / t == 1 ?
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000216SkDPoint SkDQuad::ptAtT(double t) const {
217 if (0 == t) {
218 return fPts[0];
219 }
220 if (1 == t) {
221 return fPts[2];
222 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000223 double one_t = 1 - t;
224 double a = one_t * one_t;
225 double b = 2 * one_t * t;
226 double c = t * t;
227 SkDPoint result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
228 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
229 return result;
230}
231
caryclark1049f122015-04-20 08:31:59 -0700232static double interp_quad_coords(const double* src, double t) {
caryclark55888e42016-07-18 10:01:36 -0700233 if (0 == t) {
234 return src[0];
235 }
236 if (1 == t) {
237 return src[4];
238 }
caryclark1049f122015-04-20 08:31:59 -0700239 double ab = SkDInterp(src[0], src[2], t);
240 double bc = SkDInterp(src[2], src[4], t);
241 double abc = SkDInterp(ab, bc, t);
242 return abc;
243}
244
caryclarkaec25102015-04-29 08:28:30 -0700245bool SkDQuad::monotonicInX() const {
246 return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
247}
248
caryclark1049f122015-04-20 08:31:59 -0700249bool SkDQuad::monotonicInY() const {
250 return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
251}
252
caryclark@google.com07393ca2013-04-08 11:47:37 +0000253/*
254Given a quadratic q, t1, and t2, find a small quadratic segment.
255
256The new quadratic is defined by A, B, and C, where
257 A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
258 C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
259
260To find B, compute the point halfway between t1 and t2:
261
262q(at (t1 + t2)/2) == D
263
264Next, compute where D must be if we know the value of B:
265
266_12 = A/2 + B/2
26712_ = B/2 + C/2
268123 = A/4 + B/2 + C/4
269 = D
270
271Group the known values on one side:
272
273B = D*2 - A/2 - C/2
274*/
275
caryclark55888e42016-07-18 10:01:36 -0700276// OPTIMIZE? : special case t1 = 1 && t2 = 0
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277SkDQuad SkDQuad::subDivide(double t1, double t2) const {
caryclark55888e42016-07-18 10:01:36 -0700278 if (0 == t1 && 1 == t2) {
279 return *this;
280 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000281 SkDQuad dst;
282 double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
283 double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
284 double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
285 double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
286 double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
287 double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
caryclark1049f122015-04-20 08:31:59 -0700288 /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
289 /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000290 return dst;
291}
292
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000293void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000294 if (fPts[endIndex].fX == fPts[1].fX) {
295 dstPt->fX = fPts[endIndex].fX;
296 }
297 if (fPts[endIndex].fY == fPts[1].fY) {
298 dstPt->fY = fPts[endIndex].fY;
299 }
300}
301
caryclark@google.com07393ca2013-04-08 11:47:37 +0000302SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000303 SkASSERT(t1 != t2);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304 SkDPoint b;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000305 SkDQuad sub = subDivide(t1, t2);
306 SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
307 SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
308 SkIntersections i;
309 i.intersectRay(b0, b1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000310 if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000311 b = i.pt(0);
312 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000313 SkASSERT(i.used() <= 2);
caryclark55888e42016-07-18 10:01:36 -0700314 return SkDPoint::Mid(b0[1], b1[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000315 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000316 if (t1 == 0 || t2 == 0) {
317 align(0, &b);
318 }
319 if (t1 == 1 || t2 == 1) {
320 align(2, &b);
321 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000322 if (AlmostBequalUlps(b.fX, a.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000323 b.fX = a.fX;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000324 } else if (AlmostBequalUlps(b.fX, c.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000325 b.fX = c.fX;
326 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000327 if (AlmostBequalUlps(b.fY, a.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000328 b.fY = a.fY;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000329 } else if (AlmostBequalUlps(b.fY, c.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000330 b.fY = c.fY;
331 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000332 return b;
333}
334
335/* classic one t subdivision */
336static void interp_quad_coords(const double* src, double* dst, double t) {
337 double ab = SkDInterp(src[0], src[2], t);
338 double bc = SkDInterp(src[2], src[4], t);
339 dst[0] = src[0];
340 dst[2] = ab;
341 dst[4] = SkDInterp(ab, bc, t);
342 dst[6] = bc;
343 dst[8] = src[4];
344}
345
346SkDQuadPair SkDQuad::chopAt(double t) const
347{
348 SkDQuadPair dst;
349 interp_quad_coords(&fPts[0].fX, &dst.pts[0].fX, t);
350 interp_quad_coords(&fPts[0].fY, &dst.pts[0].fY, t);
351 return dst;
352}
353
354static int valid_unit_divide(double numer, double denom, double* ratio)
355{
356 if (numer < 0) {
357 numer = -numer;
358 denom = -denom;
359 }
360 if (denom == 0 || numer == 0 || numer >= denom) {
361 return 0;
362 }
363 double r = numer / denom;
364 if (r == 0) { // catch underflow if numer <<<< denom
365 return 0;
366 }
367 *ratio = r;
368 return 1;
369}
370
371/** Quad'(t) = At + B, where
372 A = 2(a - 2b + c)
373 B = 2(b - a)
374 Solve for t, only if it fits between 0 < t < 1
375*/
caryclarkaec25102015-04-29 08:28:30 -0700376int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000377 /* At + B == 0
378 t = -B / A
379 */
caryclarkaec25102015-04-29 08:28:30 -0700380 double a = src[0];
381 double b = src[2];
382 double c = src[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000383 return valid_unit_divide(a - b, a - b - b + c, tValue);
384}
385
386/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
387 *
388 * a = A - 2*B + C
389 * b = 2*B - 2*C
390 * c = C
391 */
392void SkDQuad::SetABC(const double* quad, double* a, double* b, double* c) {
393 *a = quad[0]; // a = A
394 *b = 2 * quad[2]; // b = 2*B
395 *c = quad[4]; // c = C
396 *b -= *c; // b = 2*B - C
397 *a -= *b; // a = A - 2*B + C
398 *b -= *c; // b = 2*B - 2*C
399}