OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "SkIntersections.h" | 7 #include "SkIntersections.h" |
8 #include "SkPathOpsConic.h" | 8 #include "SkPathOpsConic.h" |
9 #include "SkPathOpsLine.h" | 9 #include "SkPathOpsLine.h" |
10 | 10 |
11 class LineConicIntersections { | 11 class LineConicIntersections { |
12 public: | 12 public: |
13 enum PinTPoint { | 13 enum PinTPoint { |
14 kPointUninitialized, | 14 kPointUninitialized, |
15 kPointInitialized | 15 kPointInitialized |
16 }; | 16 }; |
17 | 17 |
18 LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections*
i) | 18 LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections*
i) |
19 : fConic(c) | 19 : fConic(c) |
20 , fLine(l) | 20 , fLine(&l) |
21 , fIntersections(i) | 21 , fIntersections(i) |
22 , fAllowNear(true) { | 22 , fAllowNear(true) { |
23 i->setMax(3); // allow short partial coincidence plus discrete intersec
tion | 23 i->setMax(3); // allow short partial coincidence plus discrete intersec
tion |
24 } | 24 } |
25 | 25 |
| 26 LineConicIntersections(const SkDConic& c) |
| 27 : fConic(c) |
| 28 SkDEBUGPARAMS(fLine(NULL)) |
| 29 SkDEBUGPARAMS(fIntersections(NULL)) |
| 30 SkDEBUGPARAMS(fAllowNear(false)) { |
| 31 } |
| 32 |
26 void allowNear(bool allow) { | 33 void allowNear(bool allow) { |
27 fAllowNear = allow; | 34 fAllowNear = allow; |
28 } | 35 } |
29 | 36 |
30 void checkCoincident() { | 37 void checkCoincident() { |
31 int last = fIntersections->used() - 1; | 38 int last = fIntersections->used() - 1; |
32 for (int index = 0; index < last; ) { | 39 for (int index = 0; index < last; ) { |
33 double conicMidT = ((*fIntersections)[0][index] + (*fIntersections)[
0][index + 1]) / 2; | 40 double conicMidT = ((*fIntersections)[0][index] + (*fIntersections)[
0][index + 1]) / 2; |
34 SkDPoint conicMidPt = fConic.ptAtT(conicMidT); | 41 SkDPoint conicMidPt = fConic.ptAtT(conicMidT); |
35 double t = fLine.nearPoint(conicMidPt, NULL); | 42 double t = fLine->nearPoint(conicMidPt, NULL); |
36 if (t < 0) { | 43 if (t < 0) { |
37 ++index; | 44 ++index; |
38 continue; | 45 continue; |
39 } | 46 } |
40 if (fIntersections->isCoincident(index)) { | 47 if (fIntersections->isCoincident(index)) { |
41 fIntersections->removeOne(index); | 48 fIntersections->removeOne(index); |
42 --last; | 49 --last; |
43 } else if (fIntersections->isCoincident(index + 1)) { | 50 } else if (fIntersections->isCoincident(index + 1)) { |
44 fIntersections->removeOne(index + 1); | 51 fIntersections->removeOne(index + 1); |
45 --last; | 52 --last; |
46 } else { | 53 } else { |
47 fIntersections->setCoincident(index++); | 54 fIntersections->setCoincident(index++); |
48 } | 55 } |
49 fIntersections->setCoincident(index); | 56 fIntersections->setCoincident(index); |
50 } | 57 } |
51 } | 58 } |
52 | 59 |
53 #ifdef SK_DEBUG | 60 #ifdef SK_DEBUG |
54 static bool close_to(double a, double b, const double c[3]) { | 61 static bool close_to(double a, double b, const double c[3]) { |
55 double max = SkTMax(-SkTMin(SkTMin(c[0], c[1]), c[2]), SkTMax(SkTMax(c[0
], c[1]), c[2])); | 62 double max = SkTMax(-SkTMin(SkTMin(c[0], c[1]), c[2]), SkTMax(SkTMax(c[0
], c[1]), c[2])); |
56 return approximately_zero_when_compared_to(a - b, max); | 63 return approximately_zero_when_compared_to(a - b, max); |
57 } | 64 } |
58 #endif | 65 #endif |
| 66 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 } |
59 | 70 |
60 int horizontalIntersect(double axisIntercept, double left, double right, boo
l flipped) { | 71 int horizontalIntersect(double axisIntercept, double left, double right, boo
l flipped) { |
61 this->addExactHorizontalEndPoints(left, right, axisIntercept); | 72 this->addExactHorizontalEndPoints(left, right, axisIntercept); |
62 if (fAllowNear) { | 73 if (fAllowNear) { |
63 this->addNearHorizontalEndPoints(left, right, axisIntercept); | 74 this->addNearHorizontalEndPoints(left, right, axisIntercept); |
64 } | 75 } |
65 double roots[2]; | 76 double roots[2]; |
66 double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY }; | 77 int count = this->horizontalIntersect(axisIntercept, roots); |
67 int count = this->validT(conicVals, axisIntercept, roots); | |
68 for (int index = 0; index < count; ++index) { | 78 for (int index = 0; index < count; ++index) { |
69 double conicT = roots[index]; | 79 double conicT = roots[index]; |
70 SkDPoint pt = fConic.ptAtT(conicT); | 80 SkDPoint pt = fConic.ptAtT(conicT); |
| 81 SkDEBUGCODE_(double conicVals[] = { fConic[0].fY, fConic[1].fY, fCon
ic[2].fY }); |
71 SkASSERT(close_to(pt.fY, axisIntercept, conicVals)); | 82 SkASSERT(close_to(pt.fY, axisIntercept, conicVals)); |
72 double lineT = (pt.fX - left) / (right - left); | 83 double lineT = (pt.fX - left) / (right - left); |
73 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) | 84 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) |
74 && this->uniqueAnswer(conicT, pt)) { | 85 && this->uniqueAnswer(conicT, pt)) { |
75 fIntersections->insert(conicT, lineT, pt); | 86 fIntersections->insert(conicT, lineT, pt); |
76 } | 87 } |
77 } | 88 } |
78 if (flipped) { | 89 if (flipped) { |
79 fIntersections->flip(); | 90 fIntersections->flip(); |
80 } | 91 } |
81 this->checkCoincident(); | 92 this->checkCoincident(); |
82 return fIntersections->used(); | 93 return fIntersections->used(); |
83 } | 94 } |
84 | 95 |
85 int intersect() { | 96 int intersect() { |
86 this->addExactEndPoints(); | 97 this->addExactEndPoints(); |
87 if (fAllowNear) { | 98 if (fAllowNear) { |
88 this->addNearEndPoints(); | 99 this->addNearEndPoints(); |
89 } | 100 } |
90 double rootVals[2]; | 101 double rootVals[2]; |
91 int roots = this->intersectRay(rootVals); | 102 int roots = this->intersectRay(rootVals); |
92 for (int index = 0; index < roots; ++index) { | 103 for (int index = 0; index < roots; ++index) { |
93 double conicT = rootVals[index]; | 104 double conicT = rootVals[index]; |
94 double lineT = this->findLineT(conicT); | 105 double lineT = this->findLineT(conicT); |
95 SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT)); | 106 SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT)); |
96 SkDEBUGCODE(SkDPoint linePt = fLine.ptAtT(lineT)); | 107 SkDEBUGCODE(SkDPoint linePt = fLine->ptAtT(lineT)); |
97 SkASSERT(conicPt.approximatelyEqual(linePt)); | 108 SkASSERT(conicPt.approximatelyEqual(linePt)); |
98 SkDPoint pt; | 109 SkDPoint pt; |
99 if (this->pinTs(&conicT, &lineT, &pt, kPointUninitialized) | 110 if (this->pinTs(&conicT, &lineT, &pt, kPointUninitialized) |
100 && this->uniqueAnswer(conicT, pt)) { | 111 && this->uniqueAnswer(conicT, pt)) { |
101 fIntersections->insert(conicT, lineT, pt); | 112 fIntersections->insert(conicT, lineT, pt); |
102 } | 113 } |
103 } | 114 } |
104 this->checkCoincident(); | 115 this->checkCoincident(); |
105 return fIntersections->used(); | 116 return fIntersections->used(); |
106 } | 117 } |
107 | 118 |
108 int intersectRay(double roots[2]) { | 119 int intersectRay(double roots[2]) { |
109 double adj = fLine[1].fX - fLine[0].fX; | 120 double adj = (*fLine)[1].fX - (*fLine)[0].fX; |
110 double opp = fLine[1].fY - fLine[0].fY; | 121 double opp = (*fLine)[1].fY - (*fLine)[0].fY; |
111 double r[3]; | 122 double r[3]; |
112 for (int n = 0; n < 3; ++n) { | 123 for (int n = 0; n < 3; ++n) { |
113 r[n] = (fConic[n].fY - fLine[0].fY) * adj - (fConic[n].fX - fLine[0]
.fX) * opp; | 124 r[n] = (fConic[n].fY - (*fLine)[0].fY) * adj - (fConic[n].fX - (*fLi
ne)[0].fX) * opp; |
114 } | 125 } |
115 return this->validT(r, 0, roots); | 126 return this->validT(r, 0, roots); |
116 } | 127 } |
117 | 128 |
118 int validT(double r[3], double axisIntercept, double roots[2]) { | 129 int validT(double r[3], double axisIntercept, double roots[2]) { |
119 double A = r[2]; | 130 double A = r[2]; |
120 double B = r[1] * fConic.fWeight - axisIntercept * fConic.fWeight + axis
Intercept; | 131 double B = r[1] * fConic.fWeight - axisIntercept * fConic.fWeight + axis
Intercept; |
121 double C = r[0]; | 132 double C = r[0]; |
122 A += C - 2 * B; // A = a + c - 2*(b*w - xCept*w + xCept) | 133 A += C - 2 * B; // A = a + c - 2*(b*w - xCept*w + xCept) |
123 B -= C; // B = b*w - w * xCept + xCept - a | 134 B -= C; // B = b*w - w * xCept + xCept - a |
124 C -= axisIntercept; | 135 C -= axisIntercept; |
125 return SkDQuad::RootsValidT(A, 2 * B, C, roots); | 136 return SkDQuad::RootsValidT(A, 2 * B, C, roots); |
126 } | 137 } |
127 | 138 |
| 139 int verticalIntersect(double axisIntercept, double roots[2]) { |
| 140 double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX }; |
| 141 return this->validT(conicVals, axisIntercept, roots); |
| 142 } |
| 143 |
128 int verticalIntersect(double axisIntercept, double top, double bottom, bool
flipped) { | 144 int verticalIntersect(double axisIntercept, double top, double bottom, bool
flipped) { |
129 this->addExactVerticalEndPoints(top, bottom, axisIntercept); | 145 this->addExactVerticalEndPoints(top, bottom, axisIntercept); |
130 if (fAllowNear) { | 146 if (fAllowNear) { |
131 this->addNearVerticalEndPoints(top, bottom, axisIntercept); | 147 this->addNearVerticalEndPoints(top, bottom, axisIntercept); |
132 } | 148 } |
133 double roots[2]; | 149 double roots[2]; |
134 double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX }; | 150 int count = this->verticalIntersect(axisIntercept, roots); |
135 int count = this->validT(conicVals, axisIntercept, roots); | |
136 for (int index = 0; index < count; ++index) { | 151 for (int index = 0; index < count; ++index) { |
137 double conicT = roots[index]; | 152 double conicT = roots[index]; |
138 SkDPoint pt = fConic.ptAtT(conicT); | 153 SkDPoint pt = fConic.ptAtT(conicT); |
| 154 SkDEBUGCODE_(double conicVals[] = { fConic[0].fX, fConic[1].fX, fCon
ic[2].fX }); |
139 SkASSERT(close_to(pt.fX, axisIntercept, conicVals)); | 155 SkASSERT(close_to(pt.fX, axisIntercept, conicVals)); |
140 double lineT = (pt.fY - top) / (bottom - top); | 156 double lineT = (pt.fY - top) / (bottom - top); |
141 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) | 157 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) |
142 && this->uniqueAnswer(conicT, pt)) { | 158 && this->uniqueAnswer(conicT, pt)) { |
143 fIntersections->insert(conicT, lineT, pt); | 159 fIntersections->insert(conicT, lineT, pt); |
144 } | 160 } |
145 } | 161 } |
146 if (flipped) { | 162 if (flipped) { |
147 fIntersections->flip(); | 163 fIntersections->flip(); |
148 } | 164 } |
149 this->checkCoincident(); | 165 this->checkCoincident(); |
150 return fIntersections->used(); | 166 return fIntersections->used(); |
151 } | 167 } |
152 | 168 |
153 protected: | 169 protected: |
154 // OPTIMIZE: Functions of the form add .. points are indentical to the conic rou
tines. | 170 // OPTIMIZE: Functions of the form add .. points are indentical to the conic rou
tines. |
155 // add endpoints first to get zero and one t values exactly | 171 // add endpoints first to get zero and one t values exactly |
156 void addExactEndPoints() { | 172 void addExactEndPoints() { |
157 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { | 173 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { |
158 double lineT = fLine.exactPoint(fConic[cIndex]); | 174 double lineT = fLine->exactPoint(fConic[cIndex]); |
159 if (lineT < 0) { | 175 if (lineT < 0) { |
160 continue; | 176 continue; |
161 } | 177 } |
162 double conicT = (double) (cIndex >> 1); | 178 double conicT = (double) (cIndex >> 1); |
163 fIntersections->insert(conicT, lineT, fConic[cIndex]); | 179 fIntersections->insert(conicT, lineT, fConic[cIndex]); |
164 } | 180 } |
165 } | 181 } |
166 | 182 |
167 void addNearEndPoints() { | 183 void addNearEndPoints() { |
168 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { | 184 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { |
169 double conicT = (double) (cIndex >> 1); | 185 double conicT = (double) (cIndex >> 1); |
170 if (fIntersections->hasT(conicT)) { | 186 if (fIntersections->hasT(conicT)) { |
171 continue; | 187 continue; |
172 } | 188 } |
173 double lineT = fLine.nearPoint(fConic[cIndex], NULL); | 189 double lineT = fLine->nearPoint(fConic[cIndex], NULL); |
174 if (lineT < 0) { | 190 if (lineT < 0) { |
175 continue; | 191 continue; |
176 } | 192 } |
177 fIntersections->insert(conicT, lineT, fConic[cIndex]); | 193 fIntersections->insert(conicT, lineT, fConic[cIndex]); |
178 } | 194 } |
179 // FIXME: see if line end is nearly on conic | 195 // FIXME: see if line end is nearly on conic |
180 } | 196 } |
181 | 197 |
182 void addExactHorizontalEndPoints(double left, double right, double y) { | 198 void addExactHorizontalEndPoints(double left, double right, double y) { |
183 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { | 199 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic:
:kPointLast) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 if (lineT < 0) { | 242 if (lineT < 0) { |
227 continue; | 243 continue; |
228 } | 244 } |
229 fIntersections->insert(conicT, lineT, fConic[cIndex]); | 245 fIntersections->insert(conicT, lineT, fConic[cIndex]); |
230 } | 246 } |
231 // FIXME: see if line end is nearly on conic | 247 // FIXME: see if line end is nearly on conic |
232 } | 248 } |
233 | 249 |
234 double findLineT(double t) { | 250 double findLineT(double t) { |
235 SkDPoint xy = fConic.ptAtT(t); | 251 SkDPoint xy = fConic.ptAtT(t); |
236 double dx = fLine[1].fX - fLine[0].fX; | 252 double dx = (*fLine)[1].fX - (*fLine)[0].fX; |
237 double dy = fLine[1].fY - fLine[0].fY; | 253 double dy = (*fLine)[1].fY - (*fLine)[0].fY; |
238 if (fabs(dx) > fabs(dy)) { | 254 if (fabs(dx) > fabs(dy)) { |
239 return (xy.fX - fLine[0].fX) / dx; | 255 return (xy.fX - (*fLine)[0].fX) / dx; |
240 } | 256 } |
241 return (xy.fY - fLine[0].fY) / dy; | 257 return (xy.fY - (*fLine)[0].fY) / dy; |
242 } | 258 } |
243 | 259 |
244 bool pinTs(double* conicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) { | 260 bool pinTs(double* conicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) { |
245 if (!approximately_one_or_less_double(*lineT)) { | 261 if (!approximately_one_or_less_double(*lineT)) { |
246 return false; | 262 return false; |
247 } | 263 } |
248 if (!approximately_zero_or_more_double(*lineT)) { | 264 if (!approximately_zero_or_more_double(*lineT)) { |
249 return false; | 265 return false; |
250 } | 266 } |
251 double qT = *conicT = SkPinT(*conicT); | 267 double qT = *conicT = SkPinT(*conicT); |
252 double lT = *lineT = SkPinT(*lineT); | 268 double lT = *lineT = SkPinT(*lineT); |
253 if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT
!= 1)) { | 269 if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT
!= 1)) { |
254 *pt = fLine.ptAtT(lT); | 270 *pt = (*fLine).ptAtT(lT); |
255 } else if (ptSet == kPointUninitialized) { | 271 } else if (ptSet == kPointUninitialized) { |
256 *pt = fConic.ptAtT(qT); | 272 *pt = fConic.ptAtT(qT); |
257 } | 273 } |
258 SkPoint gridPt = pt->asSkPoint(); | 274 SkPoint gridPt = pt->asSkPoint(); |
259 if (SkDPoint::ApproximatelyEqual(gridPt, fLine[0].asSkPoint())) { | 275 if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) { |
260 *pt = fLine[0]; | 276 *pt = (*fLine)[0]; |
261 *lineT = 0; | 277 *lineT = 0; |
262 } else if (SkDPoint::ApproximatelyEqual(gridPt, fLine[1].asSkPoint())) { | 278 } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())
) { |
263 *pt = fLine[1]; | 279 *pt = (*fLine)[1]; |
264 *lineT = 1; | 280 *lineT = 1; |
265 } | 281 } |
266 if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[
1][0], *lineT)) { | 282 if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[
1][0], *lineT)) { |
267 return false; | 283 return false; |
268 } | 284 } |
269 if (gridPt == fConic[0].asSkPoint()) { | 285 if (gridPt == fConic[0].asSkPoint()) { |
270 *pt = fConic[0]; | 286 *pt = fConic[0]; |
271 *conicT = 0; | 287 *conicT = 0; |
272 } else if (gridPt == fConic[2].asSkPoint()) { | 288 } else if (gridPt == fConic[2].asSkPoint()) { |
273 *pt = fConic[2]; | 289 *pt = fConic[2]; |
(...skipping 21 matching lines...) Expand all Loading... |
295 #if ONE_OFF_DEBUG | 311 #if ONE_OFF_DEBUG |
296 SkDPoint qPt = fConic.ptAtT(conicT); | 312 SkDPoint qPt = fConic.ptAtT(conicT); |
297 SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX,
pt.fY, | 313 SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX,
pt.fY, |
298 qPt.fX, qPt.fY); | 314 qPt.fX, qPt.fY); |
299 #endif | 315 #endif |
300 return true; | 316 return true; |
301 } | 317 } |
302 | 318 |
303 private: | 319 private: |
304 const SkDConic& fConic; | 320 const SkDConic& fConic; |
305 const SkDLine& fLine; | 321 const SkDLine* fLine; |
306 SkIntersections* fIntersections; | 322 SkIntersections* fIntersections; |
307 bool fAllowNear; | 323 bool fAllowNear; |
308 }; | 324 }; |
309 | 325 |
310 int SkIntersections::horizontal(const SkDConic& conic, double left, double right
, double y, | 326 int SkIntersections::horizontal(const SkDConic& conic, double left, double right
, double y, |
311 bool flipped) { | 327 bool flipped) { |
312 SkDLine line = {{{ left, y }, { right, y }}}; | 328 SkDLine line = {{{ left, y }, { right, y }}}; |
313 LineConicIntersections c(conic, line, this); | 329 LineConicIntersections c(conic, line, this); |
314 return c.horizontalIntersect(y, left, right, flipped); | 330 return c.horizontalIntersect(y, left, right, flipped); |
315 } | 331 } |
(...skipping 12 matching lines...) Expand all Loading... |
328 } | 344 } |
329 | 345 |
330 int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) { | 346 int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) { |
331 LineConicIntersections c(conic, line, this); | 347 LineConicIntersections c(conic, line, this); |
332 fUsed = c.intersectRay(fT[0]); | 348 fUsed = c.intersectRay(fT[0]); |
333 for (int index = 0; index < fUsed; ++index) { | 349 for (int index = 0; index < fUsed; ++index) { |
334 fPt[index] = conic.ptAtT(fT[0][index]); | 350 fPt[index] = conic.ptAtT(fT[0][index]); |
335 } | 351 } |
336 return fUsed; | 352 return fUsed; |
337 } | 353 } |
| 354 |
| 355 int SkIntersections::HorizontalIntercept(const SkDConic& conic, SkScalar y, doub
le* roots) { |
| 356 LineConicIntersections c(conic); |
| 357 return c.horizontalIntersect(y, roots); |
| 358 } |
| 359 |
| 360 int SkIntersections::VerticalIntercept(const SkDConic& conic, SkScalar x, double
* roots) { |
| 361 LineConicIntersections c(conic); |
| 362 return c.verticalIntersect(x, roots); |
| 363 } |
OLD | NEW |