1 /*
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 "src/pathops/SkIntersections.h"
8 #include "src/pathops/SkLineParameters.h"
9 #include "src/pathops/SkPathOpsCubic.h"
10 #include "src/pathops/SkPathOpsCurve.h"
11 #include "src/pathops/SkPathOpsQuad.h"
12 #include "src/pathops/SkPathOpsRect.h"
13
14 // from blackpawn.com/texts/pointinpoly
pointInTriangle(const SkDPoint fPts[3],const SkDPoint & test)15 static bool pointInTriangle(const SkDPoint fPts[3], const SkDPoint& test) {
16 SkDVector v0 = fPts[2] - fPts[0];
17 SkDVector v1 = fPts[1] - fPts[0];
18 SkDVector v2 = test - fPts[0];
19 double dot00 = v0.dot(v0);
20 double dot01 = v0.dot(v1);
21 double dot02 = v0.dot(v2);
22 double dot11 = v1.dot(v1);
23 double dot12 = v1.dot(v2);
24 // Compute barycentric coordinates
25 double denom = dot00 * dot11 - dot01 * dot01;
26 double u = dot11 * dot02 - dot01 * dot12;
27 double v = dot00 * dot12 - dot01 * dot02;
28 // Check if point is in triangle
29 if (denom >= 0) {
30 return u >= 0 && v >= 0 && u + v < denom;
31 }
32 return u <= 0 && v <= 0 && u + v > denom;
33 }
34
matchesEnd(const SkDPoint fPts[3],const SkDPoint & test)35 static bool matchesEnd(const SkDPoint fPts[3], const SkDPoint& test) {
36 return fPts[0] == test || fPts[2] == test;
37 }
38
39 /* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
40 // Do a quick reject by rotating all points relative to a line formed by
41 // a pair of one quad's points. If the 2nd quad's points
42 // are on the line or on the opposite side from the 1st quad's 'odd man', the
43 // curves at most intersect at the endpoints.
44 /* if returning true, check contains true if quad's hull collapsed, making the cubic linear
45 if returning false, check contains true if the the quad pair have only the end point in common
46 */
hullIntersects(const SkDQuad & q2,bool * isLinear) const47 bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
48 bool linear = true;
49 for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
50 const SkDPoint* endPt[2];
51 this->otherPts(oddMan, endPt);
52 double origX = endPt[0]->fX;
53 double origY = endPt[0]->fY;
54 double adj = endPt[1]->fX - origX;
55 double opp = endPt[1]->fY - origY;
56 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
57 if (approximately_zero(sign)) {
58 continue;
59 }
60 linear = false;
61 bool foundOutlier = false;
62 for (int n = 0; n < kPointCount; ++n) {
63 double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
64 if (test * sign > 0 && !precisely_zero(test)) {
65 foundOutlier = true;
66 break;
67 }
68 }
69 if (!foundOutlier) {
70 return false;
71 }
72 }
73 if (linear && !matchesEnd(fPts, q2.fPts[0]) && !matchesEnd(fPts, q2.fPts[2])) {
74 // if the end point of the opposite quad is inside the hull that is nearly a line,
75 // then representing the quad as a line may cause the intersection to be missed.
76 // Check to see if the endpoint is in the triangle.
77 if (pointInTriangle(fPts, q2.fPts[0]) || pointInTriangle(fPts, q2.fPts[2])) {
78 linear = false;
79 }
80 }
81 *isLinear = linear;
82 return true;
83 }
84
hullIntersects(const SkDConic & conic,bool * isLinear) const85 bool SkDQuad::hullIntersects(const SkDConic& conic, bool* isLinear) const {
86 return conic.hullIntersects(*this, isLinear);
87 }
88
hullIntersects(const SkDCubic & cubic,bool * isLinear) const89 bool SkDQuad::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
90 return cubic.hullIntersects(*this, isLinear);
91 }
92
93 /* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
94 oddMan opp x=oddMan^opp x=x-oddMan m=x>>2 x&~m
95 0 1 1 1 0 1
96 2 2 2 0 2
97 1 1 0 -1 -1 0
98 2 3 2 0 2
99 2 1 3 1 0 1
100 2 0 -2 -1 0
101 */
otherPts(int oddMan,const SkDPoint * endPt[2]) const102 void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
103 for (int opp = 1; opp < kPointCount; ++opp) {
104 int end = (oddMan ^ opp) - oddMan; // choose a value not equal to oddMan
105 end &= ~(end >> 2); // if the value went negative, set it to zero
106 endPt[opp - 1] = &fPts[end];
107 }
108 }
109
AddValidTs(double s[],int realRoots,double * t)110 int SkDQuad::AddValidTs(double s[], int realRoots, double* t) {
111 int foundRoots = 0;
112 for (int index = 0; index < realRoots; ++index) {
113 double tValue = s[index];
114 if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
115 if (approximately_less_than_zero(tValue)) {
116 tValue = 0;
117 } else if (approximately_greater_than_one(tValue)) {
118 tValue = 1;
119 }
120 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
121 if (approximately_equal(t[idx2], tValue)) {
122 goto nextRoot;
123 }
124 }
125 t[foundRoots++] = tValue;
126 }
127 nextRoot:
128 {}
129 }
130 return foundRoots;
131 }
132
133 // note: caller expects multiple results to be sorted smaller first
134 // note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
135 // analysis of the quadratic equation, suggesting why the following looks at
136 // the sign of B -- and further suggesting that the greatest loss of precision
137 // is in b squared less two a c
RootsValidT(double A,double B,double C,double t[2])138 int SkDQuad::RootsValidT(double A, double B, double C, double t[2]) {
139 double s[2];
140 int realRoots = RootsReal(A, B, C, s);
141 int foundRoots = AddValidTs(s, realRoots, t);
142 return foundRoots;
143 }
144
handle_zero(const double B,const double C,double s[2])145 static int handle_zero(const double B, const double C, double s[2]) {
146 if (approximately_zero(B)) {
147 s[0] = 0;
148 return C == 0;
149 }
150 s[0] = -C / B;
151 return 1;
152 }
153
154 /*
155 Numeric Solutions (5.6) suggests to solve the quadratic by computing
156 Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
157 and using the roots
158 t1 = Q / A
159 t2 = C / Q
160 */
161 // this does not discard real roots <= 0 or >= 1
RootsReal(const double A,const double B,const double C,double s[2])162 int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) {
163 if (!A) {
164 return handle_zero(B, C, s);
165 }
166 const double p = B / (2 * A);
167 const double q = C / A;
168 if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) {
169 return handle_zero(B, C, s);
170 }
171 /* normal form: x^2 + px + q = 0 */
172 const double p2 = p * p;
173 if (!AlmostDequalUlps(p2, q) && p2 < q) {
174 return 0;
175 }
176 double sqrt_D = 0;
177 if (p2 > q) {
178 sqrt_D = sqrt(p2 - q);
179 }
180 s[0] = sqrt_D - p;
181 s[1] = -sqrt_D - p;
182 return 1 + !AlmostDequalUlps(s[0], s[1]);
183 }
184
isLinear(int startIndex,int endIndex) const185 bool SkDQuad::isLinear(int startIndex, int endIndex) const {
186 SkLineParameters lineParameters;
187 lineParameters.quadEndPoints(*this, startIndex, endIndex);
188 // FIXME: maybe it's possible to avoid this and compare non-normalized
189 lineParameters.normalize();
190 double distance = lineParameters.controlPtDistance(*this);
191 double tiniest = std::min(std::min(std::min(std::min(std::min(fPts[0].fX, fPts[0].fY),
192 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
193 double largest = std::max(std::max(std::max(std::max(std::max(fPts[0].fX, fPts[0].fY),
194 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
195 largest = std::max(largest, -tiniest);
196 return approximately_zero_when_compared_to(distance, largest);
197 }
198
dxdyAtT(double t) const199 SkDVector SkDQuad::dxdyAtT(double t) const {
200 double a = t - 1;
201 double b = 1 - 2 * t;
202 double c = t;
203 SkDVector result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
204 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
205 if (result.fX == 0 && result.fY == 0) {
206 if (zero_or_one(t)) {
207 result = fPts[2] - fPts[0];
208 } else {
209 // incomplete
210 SkDebugf("!q");
211 }
212 }
213 return result;
214 }
215
216 // OPTIMIZE: assert if caller passes in t == 0 / t == 1 ?
ptAtT(double t) const217 SkDPoint SkDQuad::ptAtT(double t) const {
218 if (0 == t) {
219 return fPts[0];
220 }
221 if (1 == t) {
222 return fPts[2];
223 }
224 double one_t = 1 - t;
225 double a = one_t * one_t;
226 double b = 2 * one_t * t;
227 double c = t * t;
228 SkDPoint result = { a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX,
229 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY };
230 return result;
231 }
232
interp_quad_coords(const double * src,double t)233 static double interp_quad_coords(const double* src, double t) {
234 if (0 == t) {
235 return src[0];
236 }
237 if (1 == t) {
238 return src[4];
239 }
240 double ab = SkDInterp(src[0], src[2], t);
241 double bc = SkDInterp(src[2], src[4], t);
242 double abc = SkDInterp(ab, bc, t);
243 return abc;
244 }
245
monotonicInX() const246 bool SkDQuad::monotonicInX() const {
247 return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
248 }
249
monotonicInY() const250 bool SkDQuad::monotonicInY() const {
251 return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
252 }
253
254 /*
255 Given a quadratic q, t1, and t2, find a small quadratic segment.
256
257 The new quadratic is defined by A, B, and C, where
258 A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
259 C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
260
261 To find B, compute the point halfway between t1 and t2:
262
263 q(at (t1 + t2)/2) == D
264
265 Next, compute where D must be if we know the value of B:
266
267 _12 = A/2 + B/2
268 12_ = B/2 + C/2
269 123 = A/4 + B/2 + C/4
270 = D
271
272 Group the known values on one side:
273
274 B = D*2 - A/2 - C/2
275 */
276
277 // OPTIMIZE? : special case t1 = 1 && t2 = 0
subDivide(double t1,double t2) const278 SkDQuad SkDQuad::subDivide(double t1, double t2) const {
279 if (0 == t1 && 1 == t2) {
280 return *this;
281 }
282 SkDQuad dst;
283 double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
284 double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
285 double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
286 double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
287 double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
288 double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
289 /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
290 /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
291 return dst;
292 }
293
align(int endIndex,SkDPoint * dstPt) const294 void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
295 if (fPts[endIndex].fX == fPts[1].fX) {
296 dstPt->fX = fPts[endIndex].fX;
297 }
298 if (fPts[endIndex].fY == fPts[1].fY) {
299 dstPt->fY = fPts[endIndex].fY;
300 }
301 }
302
subDivide(const SkDPoint & a,const SkDPoint & c,double t1,double t2) const303 SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
304 SkASSERT(t1 != t2);
305 SkDPoint b;
306 SkDQuad sub = subDivide(t1, t2);
307 SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
308 SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
309 SkIntersections i;
310 i.intersectRay(b0, b1);
311 if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) {
312 b = i.pt(0);
313 } else {
314 SkASSERT(i.used() <= 2);
315 return SkDPoint::Mid(b0[1], b1[1]);
316 }
317 if (t1 == 0 || t2 == 0) {
318 align(0, &b);
319 }
320 if (t1 == 1 || t2 == 1) {
321 align(2, &b);
322 }
323 if (AlmostBequalUlps(b.fX, a.fX)) {
324 b.fX = a.fX;
325 } else if (AlmostBequalUlps(b.fX, c.fX)) {
326 b.fX = c.fX;
327 }
328 if (AlmostBequalUlps(b.fY, a.fY)) {
329 b.fY = a.fY;
330 } else if (AlmostBequalUlps(b.fY, c.fY)) {
331 b.fY = c.fY;
332 }
333 return b;
334 }
335
336 /* classic one t subdivision */
interp_quad_coords(const double * src,double * dst,double t)337 static void interp_quad_coords(const double* src, double* dst, double t) {
338 double ab = SkDInterp(src[0], src[2], t);
339 double bc = SkDInterp(src[2], src[4], t);
340 dst[0] = src[0];
341 dst[2] = ab;
342 dst[4] = SkDInterp(ab, bc, t);
343 dst[6] = bc;
344 dst[8] = src[4];
345 }
346
chopAt(double t) const347 SkDQuadPair SkDQuad::chopAt(double t) const
348 {
349 SkDQuadPair dst;
350 interp_quad_coords(&fPts[0].fX, &dst.pts[0].fX, t);
351 interp_quad_coords(&fPts[0].fY, &dst.pts[0].fY, t);
352 return dst;
353 }
354
valid_unit_divide(double numer,double denom,double * ratio)355 static int valid_unit_divide(double numer, double denom, double* ratio)
356 {
357 if (numer < 0) {
358 numer = -numer;
359 denom = -denom;
360 }
361 if (denom == 0 || numer == 0 || numer >= denom) {
362 return 0;
363 }
364 double r = numer / denom;
365 if (r == 0) { // catch underflow if numer <<<< denom
366 return 0;
367 }
368 *ratio = r;
369 return 1;
370 }
371
372 /** Quad'(t) = At + B, where
373 A = 2(a - 2b + c)
374 B = 2(b - a)
375 Solve for t, only if it fits between 0 < t < 1
376 */
FindExtrema(const double src[],double tValue[1])377 int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
378 /* At + B == 0
379 t = -B / A
380 */
381 double a = src[0];
382 double b = src[2];
383 double c = src[4];
384 return valid_unit_divide(a - b, a - b - b + c, tValue);
385 }
386
387 /* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
388 *
389 * a = A - 2*B + C
390 * b = 2*B - 2*C
391 * c = C
392 */
SetABC(const double * quad,double * a,double * b,double * c)393 void SkDQuad::SetABC(const double* quad, double* a, double* b, double* c) {
394 *a = quad[0]; // a = A
395 *b = 2 * quad[2]; // b = 2*B
396 *c = quad[4]; // c = C
397 *b -= *c; // b = 2*B - C
398 *a -= *b; // a = A - 2*B + C
399 *b -= *c; // b = 2*B - 2*C
400 }
401
intersectRay(SkIntersections * i,const SkDLine & line) const402 int SkTQuad::intersectRay(SkIntersections* i, const SkDLine& line) const {
403 return i->intersectRay(fQuad, line);
404 }
405
hullIntersects(const SkDConic & conic,bool * isLinear) const406 bool SkTQuad::hullIntersects(const SkDConic& conic, bool* isLinear) const {
407 return conic.hullIntersects(fQuad, isLinear);
408 }
409
hullIntersects(const SkDCubic & cubic,bool * isLinear) const410 bool SkTQuad::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
411 return cubic.hullIntersects(fQuad, isLinear);
412 }
413
setBounds(SkDRect * rect) const414 void SkTQuad::setBounds(SkDRect* rect) const {
415 rect->setBounds(fQuad);
416 }
417