blob: 3deab211339035498238defdd6ea4a1032fef538 [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) {
194 double ab = SkDInterp(src[0], src[2], t);
195 double bc = SkDInterp(src[2], src[4], t);
196 double abc = SkDInterp(ab, bc, t);
197 return abc;
198}
199
caryclarkaec25102015-04-29 08:28:30 -0700200bool SkDQuad::monotonicInX() const {
201 return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
202}
203
caryclark1049f122015-04-20 08:31:59 -0700204bool SkDQuad::monotonicInY() const {
205 return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
206}
207
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208/*
209Given a quadratic q, t1, and t2, find a small quadratic segment.
210
211The new quadratic is defined by A, B, and C, where
212 A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
213 C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
214
215To find B, compute the point halfway between t1 and t2:
216
217q(at (t1 + t2)/2) == D
218
219Next, compute where D must be if we know the value of B:
220
221_12 = A/2 + B/2
22212_ = B/2 + C/2
223123 = A/4 + B/2 + C/4
224 = D
225
226Group the known values on one side:
227
228B = D*2 - A/2 - C/2
229*/
230
caryclark1049f122015-04-20 08:31:59 -0700231// OPTIMIZE : special case either or both of t1 = 0, t2 = 1
caryclark@google.com07393ca2013-04-08 11:47:37 +0000232SkDQuad SkDQuad::subDivide(double t1, double t2) const {
233 SkDQuad dst;
234 double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
235 double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
236 double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
237 double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
238 double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
239 double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
caryclark1049f122015-04-20 08:31:59 -0700240 /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
241 /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000242 return dst;
243}
244
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000245void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000246 if (fPts[endIndex].fX == fPts[1].fX) {
247 dstPt->fX = fPts[endIndex].fX;
248 }
249 if (fPts[endIndex].fY == fPts[1].fY) {
250 dstPt->fY = fPts[endIndex].fY;
251 }
252}
253
caryclark@google.com07393ca2013-04-08 11:47:37 +0000254SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000255 SkASSERT(t1 != t2);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256 SkDPoint b;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000257 SkDQuad sub = subDivide(t1, t2);
258 SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
259 SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
260 SkIntersections i;
261 i.intersectRay(b0, b1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000262 if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000263 b = i.pt(0);
264 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000265 SkASSERT(i.used() <= 2);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000266 b = SkDPoint::Mid(b0[1], b1[1]);
267 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000268 if (t1 == 0 || t2 == 0) {
269 align(0, &b);
270 }
271 if (t1 == 1 || t2 == 1) {
272 align(2, &b);
273 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000274 if (AlmostBequalUlps(b.fX, a.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000275 b.fX = a.fX;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000276 } else if (AlmostBequalUlps(b.fX, c.fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000277 b.fX = c.fX;
278 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000279 if (AlmostBequalUlps(b.fY, a.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000280 b.fY = a.fY;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000281 } else if (AlmostBequalUlps(b.fY, c.fY)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000282 b.fY = c.fY;
283 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000284 return b;
285}
286
287/* classic one t subdivision */
288static void interp_quad_coords(const double* src, double* dst, double t) {
289 double ab = SkDInterp(src[0], src[2], t);
290 double bc = SkDInterp(src[2], src[4], t);
291 dst[0] = src[0];
292 dst[2] = ab;
293 dst[4] = SkDInterp(ab, bc, t);
294 dst[6] = bc;
295 dst[8] = src[4];
296}
297
298SkDQuadPair SkDQuad::chopAt(double t) const
299{
300 SkDQuadPair dst;
301 interp_quad_coords(&fPts[0].fX, &dst.pts[0].fX, t);
302 interp_quad_coords(&fPts[0].fY, &dst.pts[0].fY, t);
303 return dst;
304}
305
306static int valid_unit_divide(double numer, double denom, double* ratio)
307{
308 if (numer < 0) {
309 numer = -numer;
310 denom = -denom;
311 }
312 if (denom == 0 || numer == 0 || numer >= denom) {
313 return 0;
314 }
315 double r = numer / denom;
316 if (r == 0) { // catch underflow if numer <<<< denom
317 return 0;
318 }
319 *ratio = r;
320 return 1;
321}
322
323/** Quad'(t) = At + B, where
324 A = 2(a - 2b + c)
325 B = 2(b - a)
326 Solve for t, only if it fits between 0 < t < 1
327*/
caryclarkaec25102015-04-29 08:28:30 -0700328int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000329 /* At + B == 0
330 t = -B / A
331 */
caryclarkaec25102015-04-29 08:28:30 -0700332 double a = src[0];
333 double b = src[2];
334 double c = src[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000335 return valid_unit_divide(a - b, a - b - b + c, tValue);
336}
337
338/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
339 *
340 * a = A - 2*B + C
341 * b = 2*B - 2*C
342 * c = C
343 */
344void SkDQuad::SetABC(const double* quad, double* a, double* b, double* c) {
345 *a = quad[0]; // a = A
346 *b = 2 * quad[2]; // b = 2*B
347 *c = quad[4]; // c = C
348 *b -= *c; // b = 2*B - C
349 *a -= *b; // a = A - 2*B + C
350 *b -= *c; // b = 2*B - 2*C
351}