blob: ed96b9c5d7997033d92f4e1c630c50e2814546e6 [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 */
7#include "SkIntersections.h"
8#include "SkPathOpsLine.h"
9
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000010void SkIntersections::cleanUpParallelLines(bool parallel) {
11 while (fUsed > 2) {
12 removeOne(1);
13 }
14 if (fUsed == 2 && !parallel) {
15 bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
16 bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
17 if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) {
18 SkASSERT(startMatch || endMatch);
19 removeOne(endMatch);
20 }
21 }
caryclarkccec0f92015-03-24 07:28:17 -070022 if (fUsed == 2) {
23 fIsCoincident[0] = fIsCoincident[1] = 0x03;
24 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000025}
26
27void SkIntersections::computePoints(const SkDLine& line, int used) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000028 fPt[0] = line.ptAtT(fT[0][0]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000029 if ((fUsed = used) == 2) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000030 fPt[1] = line.ptAtT(fT[0][1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000031 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000032}
33
caryclark@google.comcffbcc32013-06-04 17:59:42 +000034int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000035 fMax = 2;
caryclark@google.com570863f2013-09-16 15:55:01 +000036 SkDVector aLen = a[1] - a[0];
37 SkDVector bLen = b[1] - b[0];
caryclark@google.comcffbcc32013-06-04 17:59:42 +000038 /* Slopes match when denom goes to zero:
39 axLen / ayLen == bxLen / byLen
40 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
41 byLen * axLen == ayLen * bxLen
42 byLen * axLen - ayLen * bxLen == 0 ( == denom )
43 */
caryclark@google.com570863f2013-09-16 15:55:01 +000044 double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX;
45 SkDVector ab0 = a[0] - b[0];
46 double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
47 double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000048 numerA /= denom;
49 numerB /= denom;
50 int used;
51 if (!approximately_zero(denom)) {
52 fT[0][0] = numerA;
53 fT[1][0] = numerB;
54 used = 1;
55 } else {
56 /* See if the axis intercepts match:
57 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
58 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
59 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
60 */
caryclark@google.com570863f2013-09-16 15:55:01 +000061 if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX,
62 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000063 return fUsed = 0;
64 }
65 // there's no great answer for intersection points for coincident rays, but return something
66 fT[0][0] = fT[1][0] = 0;
67 fT[1][0] = fT[1][1] = 1;
68 used = 2;
69 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000070 computePoints(a, used);
71 return fUsed;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000072}
73
caryclark@google.com07e97fc2013-07-08 17:17:02 +000074// note that this only works if both lines are neither horizontal nor vertical
caryclark@google.com07393ca2013-04-08 11:47:37 +000075int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000076 fMax = 3; // note that we clean up so that there is no more than two in the end
caryclark@google.com07e97fc2013-07-08 17:17:02 +000077 // see if end points intersect the opposite line
78 double t;
79 for (int iA = 0; iA < 2; ++iA) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000080 if ((t = b.exactPoint(a[iA])) >= 0) {
81 insert(iA, t, a[iA]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000082 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000083 }
84 for (int iB = 0; iB < 2; ++iB) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000085 if ((t = a.exactPoint(b[iB])) >= 0) {
86 insert(t, iB, b[iB]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000087 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000088 }
89 /* Determine the intersection point of two line segments
90 Return FALSE if the lines don't intersect
91 from: http://paulbourke.net/geometry/lineline2d/ */
caryclark@google.com07393ca2013-04-08 11:47:37 +000092 double axLen = a[1].fX - a[0].fX;
93 double ayLen = a[1].fY - a[0].fY;
94 double bxLen = b[1].fX - b[0].fX;
95 double byLen = b[1].fY - b[0].fY;
96 /* Slopes match when denom goes to zero:
97 axLen / ayLen == bxLen / byLen
98 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
99 byLen * axLen == ayLen * bxLen
100 byLen * axLen - ayLen * bxLen == 0 ( == denom )
101 */
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000102 double axByLen = axLen * byLen;
103 double ayBxLen = ayLen * bxLen;
104 // detect parallel lines the same way here and in SkOpAngle operator <
105 // so that non-parallel means they are also sortable
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000106 bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen)
107 : NotAlmostDequalUlps(axByLen, ayBxLen);
108 if (unparallel && fUsed == 0) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000109 double ab0y = a[0].fY - b[0].fY;
110 double ab0x = a[0].fX - b[0].fX;
111 double numerA = ab0y * bxLen - byLen * ab0x;
112 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000113 double denom = axByLen - ayBxLen;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000114 if (between(0, numerA, denom) && between(0, numerB, denom)) {
115 fT[0][0] = numerA / denom;
116 fT[1][0] = numerB / denom;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000117 computePoints(a, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 }
caryclarkdac1d172014-06-17 05:15:38 -0700120/* Allow tracking that both sets of end points are near each other -- the lines are entirely
121 coincident -- even when the end points are not exactly the same.
122 Mark this as a 'wild card' for the end points, so that either point is considered totally
123 coincident. Then, avoid folding the lines over each other, but allow either end to mate
124 to the next set of lines.
125 */
caryclark@google.com570863f2013-09-16 15:55:01 +0000126 if (fAllowNear || !unparallel) {
caryclarkdac1d172014-06-17 05:15:38 -0700127 double aNearB[2];
128 double bNearA[2];
129 bool aNotB[2] = {false, false};
130 bool bNotA[2] = {false, false};
131 int nearCount = 0;
132 for (int index = 0; index < 2; ++index) {
133 aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]);
134 nearCount += t >= 0;
135 bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]);
136 nearCount += t >= 0;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000137 }
caryclarkdac1d172014-06-17 05:15:38 -0700138 if (nearCount > 0) {
caryclark19eb3b22014-07-18 05:08:14 -0700139 // Skip if each segment contributes to one end point.
140 if (nearCount != 2 || aNotB[0] == aNotB[1]) {
141 for (int iA = 0; iA < 2; ++iA) {
142 if (!aNotB[iA]) {
143 continue;
144 }
145 int nearer = aNearB[iA] > 0.5;
146 if (!bNotA[nearer]) {
147 continue;
148 }
149 SkASSERT(a[iA] != b[nearer]);
150 SkASSERT(iA == (bNearA[nearer] > 0.5));
caryclark19eb3b22014-07-18 05:08:14 -0700151 insertNear(iA, nearer, a[iA], b[nearer]);
152 aNearB[iA] = -1;
153 bNearA[nearer] = -1;
154 nearCount -= 2;
caryclarkdac1d172014-06-17 05:15:38 -0700155 }
caryclarkdac1d172014-06-17 05:15:38 -0700156 }
157 if (nearCount > 0) {
158 for (int iA = 0; iA < 2; ++iA) {
159 if (aNearB[iA] >= 0) {
160 insert(iA, aNearB[iA], a[iA]);
161 }
162 }
163 for (int iB = 0; iB < 2; ++iB) {
164 if (bNearA[iB] >= 0) {
165 insert(bNearA[iB], iB, b[iB]);
166 }
167 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000168 }
169 }
170 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000171 cleanUpParallelLines(!unparallel);
172 SkASSERT(fUsed <= 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000173 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174}
175
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000176static int horizontal_coincident(const SkDLine& line, double y) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000177 double min = line[0].fY;
178 double max = line[1].fY;
179 if (min > max) {
180 SkTSwap(min, max);
181 }
182 if (min > y || max < y) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000183 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000185 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000186 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000187 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000188 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189}
190
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000191static double horizontal_intercept(const SkDLine& line, double y) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000192 return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000193}
194
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195int SkIntersections::horizontal(const SkDLine& line, double left, double right,
196 double y, bool flipped) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000197 fMax = 3; // clean up parallel at the end will limit the result to 2 at the most
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000198 // see if end points intersect the opposite line
199 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000200 const SkDPoint leftPt = { left, y };
201 if ((t = line.exactPoint(leftPt)) >= 0) {
202 insert(t, (double) flipped, leftPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000204 if (left != right) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000205 const SkDPoint rightPt = { right, y };
206 if ((t = line.exactPoint(rightPt)) >= 0) {
207 insert(t, (double) !flipped, rightPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000208 }
209 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000210 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
211 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000212 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000213 }
214 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000215 int result = horizontal_coincident(line, y);
216 if (result == 1 && fUsed == 0) {
217 fT[0][0] = horizontal_intercept(line, y);
218 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
219 if (between(left, xIntercept, right)) {
220 fT[1][0] = (xIntercept - left) / (right - left);
221 if (flipped) {
222 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
223 for (int index = 0; index < result; ++index) {
224 fT[1][index] = 1 - fT[1][index];
225 }
226 }
caryclark@google.com15107262013-11-08 18:00:01 +0000227 fPt[0].fX = xIntercept;
228 fPt[0].fY = y;
229 fUsed = 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 }
231 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000232 if (fAllowNear || result == 2) {
caryclarkdac1d172014-06-17 05:15:38 -0700233 if ((t = line.nearPoint(leftPt, NULL)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000234 insert(t, (double) flipped, leftPt);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000235 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000236 if (left != right) {
237 const SkDPoint rightPt = { right, y };
caryclarkdac1d172014-06-17 05:15:38 -0700238 if ((t = line.nearPoint(rightPt, NULL)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000239 insert(t, (double) !flipped, rightPt);
240 }
241 for (int index = 0; index < 2; ++index) {
242 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
243 insert((double) index, flipped ? 1 - t : t, line[index]);
244 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000245 }
246 }
247 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000248 cleanUpParallelLines(result == 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000249 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000250}
251
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000252static int vertical_coincident(const SkDLine& line, double x) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000253 double min = line[0].fX;
254 double max = line[1].fX;
255 if (min > max) {
256 SkTSwap(min, max);
257 }
caryclark@google.com03610322013-04-18 15:58:21 +0000258 if (!precisely_between(min, x, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000259 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000260 }
261 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000262 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000263 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000264 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000265}
266
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000267static double vertical_intercept(const SkDLine& line, double x) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000268 return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000269}
270
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000271int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000272 double x, bool flipped) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000273 fMax = 3; // cleanup parallel lines will bring this back line
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000274 // see if end points intersect the opposite line
275 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000276 SkDPoint topPt = { x, top };
277 if ((t = line.exactPoint(topPt)) >= 0) {
278 insert(t, (double) flipped, topPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000279 }
280 if (top != bottom) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000281 SkDPoint bottomPt = { x, bottom };
282 if ((t = line.exactPoint(bottomPt)) >= 0) {
283 insert(t, (double) !flipped, bottomPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000284 }
285 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000286 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
287 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000288 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000289 }
290 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000291 int result = vertical_coincident(line, x);
292 if (result == 1 && fUsed == 0) {
293 fT[0][0] = vertical_intercept(line, x);
294 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
295 if (between(top, yIntercept, bottom)) {
296 fT[1][0] = (yIntercept - top) / (bottom - top);
297 if (flipped) {
298 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
299 for (int index = 0; index < result; ++index) {
300 fT[1][index] = 1 - fT[1][index];
301 }
302 }
caryclark@google.com15107262013-11-08 18:00:01 +0000303 fPt[0].fX = x;
304 fPt[0].fY = yIntercept;
305 fUsed = 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000306 }
307 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000308 if (fAllowNear || result == 2) {
caryclarkdac1d172014-06-17 05:15:38 -0700309 if ((t = line.nearPoint(topPt, NULL)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000310 insert(t, (double) flipped, topPt);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000311 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000312 if (top != bottom) {
313 SkDPoint bottomPt = { x, bottom };
caryclarkdac1d172014-06-17 05:15:38 -0700314 if ((t = line.nearPoint(bottomPt, NULL)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000315 insert(t, (double) !flipped, bottomPt);
316 }
317 for (int index = 0; index < 2; ++index) {
318 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
319 insert((double) index, flipped ? 1 - t : t, line[index]);
320 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000321 }
322 }
323 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000324 cleanUpParallelLines(result == 2);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000325 SkASSERT(fUsed <= 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000326 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000327}
328