blob: 2d906072fa549f79bca75a96a5323d234dd1df15 [file] [log] [blame]
caryclark1049f122015-04-20 08:31:59 -07001/*
2 * Copyright 2015 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 "SkIntersections.h"
8#include "SkPathOpsConic.h"
9#include "SkPathOpsLine.h"
10
11class LineConicIntersections {
12public:
13 enum PinTPoint {
14 kPointUninitialized,
15 kPointInitialized
16 };
17
18 LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections* i)
19 : fConic(c)
caryclark624637c2015-05-11 07:21:27 -070020 , fLine(&l)
caryclark1049f122015-04-20 08:31:59 -070021 , fIntersections(i)
22 , fAllowNear(true) {
23 i->setMax(3); // allow short partial coincidence plus discrete intersection
24 }
25
caryclark624637c2015-05-11 07:21:27 -070026 LineConicIntersections(const SkDConic& c)
27 : fConic(c)
halcanary96fcdcc2015-08-27 07:41:13 -070028 SkDEBUGPARAMS(fLine(nullptr))
29 SkDEBUGPARAMS(fIntersections(nullptr))
caryclark624637c2015-05-11 07:21:27 -070030 SkDEBUGPARAMS(fAllowNear(false)) {
31 }
32
caryclark1049f122015-04-20 08:31:59 -070033 void allowNear(bool allow) {
34 fAllowNear = allow;
35 }
36
37 void checkCoincident() {
38 int last = fIntersections->used() - 1;
39 for (int index = 0; index < last; ) {
40 double conicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
41 SkDPoint conicMidPt = fConic.ptAtT(conicMidT);
halcanary96fcdcc2015-08-27 07:41:13 -070042 double t = fLine->nearPoint(conicMidPt, nullptr);
caryclark1049f122015-04-20 08:31:59 -070043 if (t < 0) {
44 ++index;
45 continue;
46 }
47 if (fIntersections->isCoincident(index)) {
48 fIntersections->removeOne(index);
49 --last;
50 } else if (fIntersections->isCoincident(index + 1)) {
51 fIntersections->removeOne(index + 1);
52 --last;
53 } else {
54 fIntersections->setCoincident(index++);
55 }
56 fIntersections->setCoincident(index);
57 }
58 }
59
60#ifdef SK_DEBUG
61 static bool close_to(double a, double b, const double c[3]) {
62 double max = SkTMax(-SkTMin(SkTMin(c[0], c[1]), c[2]), SkTMax(SkTMax(c[0], c[1]), c[2]));
63 return approximately_zero_when_compared_to(a - b, max);
64 }
65#endif
caryclark624637c2015-05-11 07:21:27 -070066 int horizontalIntersect(double axisIntercept, double roots[2]) {
67 double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY };
68 return this->validT(conicVals, axisIntercept, roots);
69 }
caryclark1049f122015-04-20 08:31:59 -070070
71 int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
72 this->addExactHorizontalEndPoints(left, right, axisIntercept);
73 if (fAllowNear) {
74 this->addNearHorizontalEndPoints(left, right, axisIntercept);
75 }
76 double roots[2];
caryclark624637c2015-05-11 07:21:27 -070077 int count = this->horizontalIntersect(axisIntercept, roots);
caryclark1049f122015-04-20 08:31:59 -070078 for (int index = 0; index < count; ++index) {
79 double conicT = roots[index];
80 SkDPoint pt = fConic.ptAtT(conicT);
caryclark624637c2015-05-11 07:21:27 -070081 SkDEBUGCODE_(double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY });
caryclark1049f122015-04-20 08:31:59 -070082 SkASSERT(close_to(pt.fY, axisIntercept, conicVals));
83 double lineT = (pt.fX - left) / (right - left);
84 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized)
85 && this->uniqueAnswer(conicT, pt)) {
86 fIntersections->insert(conicT, lineT, pt);
87 }
88 }
89 if (flipped) {
90 fIntersections->flip();
91 }
92 this->checkCoincident();
93 return fIntersections->used();
94 }
95
96 int intersect() {
97 this->addExactEndPoints();
98 if (fAllowNear) {
99 this->addNearEndPoints();
100 }
101 double rootVals[2];
102 int roots = this->intersectRay(rootVals);
103 for (int index = 0; index < roots; ++index) {
104 double conicT = rootVals[index];
105 double lineT = this->findLineT(conicT);
caryclarkdae6b972016-06-08 04:28:19 -0700106#ifdef SK_DEBUG
107 if (!fIntersections->debugGlobalState()
108 || !fIntersections->debugGlobalState()->debugSkipAssert()) {
109 SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT));
110 SkDEBUGCODE(SkDPoint linePt = fLine->ptAtT(lineT));
111 SkASSERT(conicPt.approximatelyEqual(linePt));
112 }
113#endif
caryclark1049f122015-04-20 08:31:59 -0700114 SkDPoint pt;
115 if (this->pinTs(&conicT, &lineT, &pt, kPointUninitialized)
116 && this->uniqueAnswer(conicT, pt)) {
117 fIntersections->insert(conicT, lineT, pt);
118 }
119 }
120 this->checkCoincident();
121 return fIntersections->used();
122 }
123
124 int intersectRay(double roots[2]) {
caryclark624637c2015-05-11 07:21:27 -0700125 double adj = (*fLine)[1].fX - (*fLine)[0].fX;
126 double opp = (*fLine)[1].fY - (*fLine)[0].fY;
caryclark1049f122015-04-20 08:31:59 -0700127 double r[3];
128 for (int n = 0; n < 3; ++n) {
caryclark624637c2015-05-11 07:21:27 -0700129 r[n] = (fConic[n].fY - (*fLine)[0].fY) * adj - (fConic[n].fX - (*fLine)[0].fX) * opp;
caryclark1049f122015-04-20 08:31:59 -0700130 }
131 return this->validT(r, 0, roots);
132 }
133
134 int validT(double r[3], double axisIntercept, double roots[2]) {
135 double A = r[2];
136 double B = r[1] * fConic.fWeight - axisIntercept * fConic.fWeight + axisIntercept;
137 double C = r[0];
138 A += C - 2 * B; // A = a + c - 2*(b*w - xCept*w + xCept)
139 B -= C; // B = b*w - w * xCept + xCept - a
140 C -= axisIntercept;
141 return SkDQuad::RootsValidT(A, 2 * B, C, roots);
142 }
143
caryclark624637c2015-05-11 07:21:27 -0700144 int verticalIntersect(double axisIntercept, double roots[2]) {
145 double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX };
146 return this->validT(conicVals, axisIntercept, roots);
147 }
148
caryclark1049f122015-04-20 08:31:59 -0700149 int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
150 this->addExactVerticalEndPoints(top, bottom, axisIntercept);
151 if (fAllowNear) {
152 this->addNearVerticalEndPoints(top, bottom, axisIntercept);
153 }
154 double roots[2];
caryclark624637c2015-05-11 07:21:27 -0700155 int count = this->verticalIntersect(axisIntercept, roots);
caryclark1049f122015-04-20 08:31:59 -0700156 for (int index = 0; index < count; ++index) {
157 double conicT = roots[index];
158 SkDPoint pt = fConic.ptAtT(conicT);
caryclark624637c2015-05-11 07:21:27 -0700159 SkDEBUGCODE_(double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX });
caryclark1049f122015-04-20 08:31:59 -0700160 SkASSERT(close_to(pt.fX, axisIntercept, conicVals));
161 double lineT = (pt.fY - top) / (bottom - top);
162 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized)
163 && this->uniqueAnswer(conicT, pt)) {
164 fIntersections->insert(conicT, lineT, pt);
165 }
166 }
167 if (flipped) {
168 fIntersections->flip();
169 }
170 this->checkCoincident();
171 return fIntersections->used();
172 }
173
174protected:
175// OPTIMIZE: Functions of the form add .. points are indentical to the conic routines.
176 // add endpoints first to get zero and one t values exactly
177 void addExactEndPoints() {
178 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
caryclark624637c2015-05-11 07:21:27 -0700179 double lineT = fLine->exactPoint(fConic[cIndex]);
caryclark1049f122015-04-20 08:31:59 -0700180 if (lineT < 0) {
181 continue;
182 }
183 double conicT = (double) (cIndex >> 1);
184 fIntersections->insert(conicT, lineT, fConic[cIndex]);
185 }
186 }
187
188 void addNearEndPoints() {
189 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
190 double conicT = (double) (cIndex >> 1);
191 if (fIntersections->hasT(conicT)) {
192 continue;
193 }
halcanary96fcdcc2015-08-27 07:41:13 -0700194 double lineT = fLine->nearPoint(fConic[cIndex], nullptr);
caryclark1049f122015-04-20 08:31:59 -0700195 if (lineT < 0) {
196 continue;
197 }
198 fIntersections->insert(conicT, lineT, fConic[cIndex]);
199 }
200 // FIXME: see if line end is nearly on conic
201 }
202
203 void addExactHorizontalEndPoints(double left, double right, double y) {
204 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
205 double lineT = SkDLine::ExactPointH(fConic[cIndex], left, right, y);
206 if (lineT < 0) {
207 continue;
208 }
209 double conicT = (double) (cIndex >> 1);
210 fIntersections->insert(conicT, lineT, fConic[cIndex]);
211 }
212 }
213
214 void addNearHorizontalEndPoints(double left, double right, double y) {
215 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
216 double conicT = (double) (cIndex >> 1);
217 if (fIntersections->hasT(conicT)) {
218 continue;
219 }
220 double lineT = SkDLine::NearPointH(fConic[cIndex], left, right, y);
221 if (lineT < 0) {
222 continue;
223 }
224 fIntersections->insert(conicT, lineT, fConic[cIndex]);
225 }
226 // FIXME: see if line end is nearly on conic
227 }
228
229 void addExactVerticalEndPoints(double top, double bottom, double x) {
230 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
231 double lineT = SkDLine::ExactPointV(fConic[cIndex], top, bottom, x);
232 if (lineT < 0) {
233 continue;
234 }
235 double conicT = (double) (cIndex >> 1);
236 fIntersections->insert(conicT, lineT, fConic[cIndex]);
237 }
238 }
239
240 void addNearVerticalEndPoints(double top, double bottom, double x) {
241 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
242 double conicT = (double) (cIndex >> 1);
243 if (fIntersections->hasT(conicT)) {
244 continue;
245 }
246 double lineT = SkDLine::NearPointV(fConic[cIndex], top, bottom, x);
247 if (lineT < 0) {
248 continue;
249 }
250 fIntersections->insert(conicT, lineT, fConic[cIndex]);
251 }
252 // FIXME: see if line end is nearly on conic
253 }
254
255 double findLineT(double t) {
256 SkDPoint xy = fConic.ptAtT(t);
caryclark624637c2015-05-11 07:21:27 -0700257 double dx = (*fLine)[1].fX - (*fLine)[0].fX;
258 double dy = (*fLine)[1].fY - (*fLine)[0].fY;
caryclark1049f122015-04-20 08:31:59 -0700259 if (fabs(dx) > fabs(dy)) {
caryclark624637c2015-05-11 07:21:27 -0700260 return (xy.fX - (*fLine)[0].fX) / dx;
caryclark1049f122015-04-20 08:31:59 -0700261 }
caryclark624637c2015-05-11 07:21:27 -0700262 return (xy.fY - (*fLine)[0].fY) / dy;
caryclark1049f122015-04-20 08:31:59 -0700263 }
264
265 bool pinTs(double* conicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) {
266 if (!approximately_one_or_less_double(*lineT)) {
267 return false;
268 }
269 if (!approximately_zero_or_more_double(*lineT)) {
270 return false;
271 }
272 double qT = *conicT = SkPinT(*conicT);
273 double lT = *lineT = SkPinT(*lineT);
274 if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) {
caryclark624637c2015-05-11 07:21:27 -0700275 *pt = (*fLine).ptAtT(lT);
caryclark1049f122015-04-20 08:31:59 -0700276 } else if (ptSet == kPointUninitialized) {
277 *pt = fConic.ptAtT(qT);
278 }
279 SkPoint gridPt = pt->asSkPoint();
caryclark624637c2015-05-11 07:21:27 -0700280 if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) {
281 *pt = (*fLine)[0];
caryclark1049f122015-04-20 08:31:59 -0700282 *lineT = 0;
caryclark624637c2015-05-11 07:21:27 -0700283 } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())) {
284 *pt = (*fLine)[1];
caryclark1049f122015-04-20 08:31:59 -0700285 *lineT = 1;
286 }
287 if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) {
288 return false;
289 }
290 if (gridPt == fConic[0].asSkPoint()) {
291 *pt = fConic[0];
292 *conicT = 0;
293 } else if (gridPt == fConic[2].asSkPoint()) {
294 *pt = fConic[2];
295 *conicT = 1;
296 }
297 return true;
298 }
299
300 bool uniqueAnswer(double conicT, const SkDPoint& pt) {
301 for (int inner = 0; inner < fIntersections->used(); ++inner) {
302 if (fIntersections->pt(inner) != pt) {
303 continue;
304 }
305 double existingConicT = (*fIntersections)[0][inner];
306 if (conicT == existingConicT) {
307 return false;
308 }
309 // check if midway on conic is also same point. If so, discard this
310 double conicMidT = (existingConicT + conicT) / 2;
311 SkDPoint conicMidPt = fConic.ptAtT(conicMidT);
312 if (conicMidPt.approximatelyEqual(pt)) {
313 return false;
314 }
315 }
316#if ONE_OFF_DEBUG
317 SkDPoint qPt = fConic.ptAtT(conicT);
318 SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
319 qPt.fX, qPt.fY);
320#endif
321 return true;
322 }
323
324private:
325 const SkDConic& fConic;
caryclark624637c2015-05-11 07:21:27 -0700326 const SkDLine* fLine;
caryclark1049f122015-04-20 08:31:59 -0700327 SkIntersections* fIntersections;
328 bool fAllowNear;
329};
330
331int SkIntersections::horizontal(const SkDConic& conic, double left, double right, double y,
332 bool flipped) {
333 SkDLine line = {{{ left, y }, { right, y }}};
334 LineConicIntersections c(conic, line, this);
335 return c.horizontalIntersect(y, left, right, flipped);
336}
337
338int SkIntersections::vertical(const SkDConic& conic, double top, double bottom, double x,
339 bool flipped) {
340 SkDLine line = {{{ x, top }, { x, bottom }}};
341 LineConicIntersections c(conic, line, this);
342 return c.verticalIntersect(x, top, bottom, flipped);
343}
344
345int SkIntersections::intersect(const SkDConic& conic, const SkDLine& line) {
346 LineConicIntersections c(conic, line, this);
347 c.allowNear(fAllowNear);
348 return c.intersect();
349}
350
351int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) {
352 LineConicIntersections c(conic, line, this);
353 fUsed = c.intersectRay(fT[0]);
354 for (int index = 0; index < fUsed; ++index) {
355 fPt[index] = conic.ptAtT(fT[0][index]);
356 }
357 return fUsed;
358}
caryclark624637c2015-05-11 07:21:27 -0700359
360int SkIntersections::HorizontalIntercept(const SkDConic& conic, SkScalar y, double* roots) {
361 LineConicIntersections c(conic);
362 return c.horizontalIntersect(y, roots);
363}
364
365int SkIntersections::VerticalIntercept(const SkDConic& conic, SkScalar x, double* roots) {
366 LineConicIntersections c(conic);
367 return c.verticalIntersect(x, roots);
368}