blob: 082e2987b9b7ba2db5c80dd04623107bf89f5348 [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) {
caryclark94c902e2015-08-18 07:12:43 -070015 bool startMatch = fT[0][0] == 0 || zero_or_one(fT[1][0]);
16 bool endMatch = fT[0][1] == 1 || zero_or_one(fT[1][1]);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000017 if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) {
18 SkASSERT(startMatch || endMatch);
caryclark94c902e2015-08-18 07:12:43 -070019 if (startMatch && endMatch && (fT[0][0] != 0 || !zero_or_one(fT[1][0]))
20 && fT[0][1] == 1 && zero_or_one(fT[1][1])) {
21 removeOne(0);
22 } else {
23 removeOne(endMatch);
24 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000025 }
26 }
caryclark54359292015-03-26 07:52:43 -070027 if (fUsed == 2) {
28 fIsCoincident[0] = fIsCoincident[1] = 0x03;
29 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000030}
31
32void SkIntersections::computePoints(const SkDLine& line, int used) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000033 fPt[0] = line.ptAtT(fT[0][0]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 if ((fUsed = used) == 2) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000035 fPt[1] = line.ptAtT(fT[0][1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000036 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000037}
38
caryclark@google.comcffbcc32013-06-04 17:59:42 +000039int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000040 fMax = 2;
caryclark@google.com570863f2013-09-16 15:55:01 +000041 SkDVector aLen = a[1] - a[0];
42 SkDVector bLen = b[1] - b[0];
caryclark@google.comcffbcc32013-06-04 17:59:42 +000043 /* Slopes match when denom goes to zero:
44 axLen / ayLen == bxLen / byLen
45 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
46 byLen * axLen == ayLen * bxLen
47 byLen * axLen - ayLen * bxLen == 0 ( == denom )
48 */
caryclark@google.com570863f2013-09-16 15:55:01 +000049 double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX;
50 SkDVector ab0 = a[0] - b[0];
51 double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
52 double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000053 numerA /= denom;
54 numerB /= denom;
55 int used;
56 if (!approximately_zero(denom)) {
57 fT[0][0] = numerA;
58 fT[1][0] = numerB;
59 used = 1;
60 } else {
61 /* See if the axis intercepts match:
62 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
63 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
64 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
65 */
caryclark@google.com570863f2013-09-16 15:55:01 +000066 if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX,
67 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000068 return fUsed = 0;
69 }
70 // there's no great answer for intersection points for coincident rays, but return something
71 fT[0][0] = fT[1][0] = 0;
72 fT[1][0] = fT[1][1] = 1;
73 used = 2;
74 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000075 computePoints(a, used);
76 return fUsed;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000077}
78
caryclark@google.com07e97fc2013-07-08 17:17:02 +000079// note that this only works if both lines are neither horizontal nor vertical
caryclark@google.com07393ca2013-04-08 11:47:37 +000080int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000081 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 +000082 // see if end points intersect the opposite line
83 double t;
84 for (int iA = 0; iA < 2; ++iA) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000085 if ((t = b.exactPoint(a[iA])) >= 0) {
86 insert(iA, t, a[iA]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000087 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000088 }
89 for (int iB = 0; iB < 2; ++iB) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000090 if ((t = a.exactPoint(b[iB])) >= 0) {
91 insert(t, iB, b[iB]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000092 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000093 }
94 /* Determine the intersection point of two line segments
95 Return FALSE if the lines don't intersect
96 from: http://paulbourke.net/geometry/lineline2d/ */
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 double axLen = a[1].fX - a[0].fX;
98 double ayLen = a[1].fY - a[0].fY;
99 double bxLen = b[1].fX - b[0].fX;
100 double byLen = b[1].fY - b[0].fY;
101 /* Slopes match when denom goes to zero:
102 axLen / ayLen == bxLen / byLen
103 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
104 byLen * axLen == ayLen * bxLen
105 byLen * axLen - ayLen * bxLen == 0 ( == denom )
106 */
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000107 double axByLen = axLen * byLen;
108 double ayBxLen = ayLen * bxLen;
109 // detect parallel lines the same way here and in SkOpAngle operator <
110 // so that non-parallel means they are also sortable
caryclarkb6693002015-12-16 12:28:35 -0800111 bool unparallel = fAllowNear ? NotAlmostEqualUlps_Pin(axByLen, ayBxLen)
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000112 : NotAlmostDequalUlps(axByLen, ayBxLen);
113 if (unparallel && fUsed == 0) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000114 double ab0y = a[0].fY - b[0].fY;
115 double ab0x = a[0].fX - b[0].fX;
116 double numerA = ab0y * bxLen - byLen * ab0x;
117 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000118 double denom = axByLen - ayBxLen;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000119 if (between(0, numerA, denom) && between(0, numerB, denom)) {
120 fT[0][0] = numerA / denom;
121 fT[1][0] = numerB / denom;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000122 computePoints(a, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000124 }
caryclarkdac1d172014-06-17 05:15:38 -0700125/* Allow tracking that both sets of end points are near each other -- the lines are entirely
126 coincident -- even when the end points are not exactly the same.
127 Mark this as a 'wild card' for the end points, so that either point is considered totally
128 coincident. Then, avoid folding the lines over each other, but allow either end to mate
129 to the next set of lines.
130 */
caryclark@google.com570863f2013-09-16 15:55:01 +0000131 if (fAllowNear || !unparallel) {
caryclarkdac1d172014-06-17 05:15:38 -0700132 double aNearB[2];
133 double bNearA[2];
134 bool aNotB[2] = {false, false};
135 bool bNotA[2] = {false, false};
136 int nearCount = 0;
137 for (int index = 0; index < 2; ++index) {
138 aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]);
139 nearCount += t >= 0;
140 bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]);
141 nearCount += t >= 0;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000142 }
caryclarkdac1d172014-06-17 05:15:38 -0700143 if (nearCount > 0) {
caryclark19eb3b22014-07-18 05:08:14 -0700144 // Skip if each segment contributes to one end point.
145 if (nearCount != 2 || aNotB[0] == aNotB[1]) {
146 for (int iA = 0; iA < 2; ++iA) {
147 if (!aNotB[iA]) {
148 continue;
149 }
150 int nearer = aNearB[iA] > 0.5;
151 if (!bNotA[nearer]) {
152 continue;
153 }
154 SkASSERT(a[iA] != b[nearer]);
caryclarka35ab3e2016-10-20 08:32:18 -0700155 SkOPASSERT(iA == (bNearA[nearer] > 0.5));
caryclark19eb3b22014-07-18 05:08:14 -0700156 insertNear(iA, nearer, a[iA], b[nearer]);
157 aNearB[iA] = -1;
158 bNearA[nearer] = -1;
159 nearCount -= 2;
caryclarkdac1d172014-06-17 05:15:38 -0700160 }
caryclarkdac1d172014-06-17 05:15:38 -0700161 }
162 if (nearCount > 0) {
163 for (int iA = 0; iA < 2; ++iA) {
164 if (aNearB[iA] >= 0) {
165 insert(iA, aNearB[iA], a[iA]);
166 }
167 }
168 for (int iB = 0; iB < 2; ++iB) {
169 if (bNearA[iB] >= 0) {
170 insert(bNearA[iB], iB, b[iB]);
171 }
172 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000173 }
174 }
175 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000176 cleanUpParallelLines(!unparallel);
177 SkASSERT(fUsed <= 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000178 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179}
180
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000181static int horizontal_coincident(const SkDLine& line, double y) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000182 double min = line[0].fY;
183 double max = line[1].fY;
184 if (min > max) {
185 SkTSwap(min, max);
186 }
187 if (min > y || max < y) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000188 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000190 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000191 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000193 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194}
195
caryclark624637c2015-05-11 07:21:27 -0700196double SkIntersections::HorizontalIntercept(const SkDLine& line, double y) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000197 return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000198}
199
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200int SkIntersections::horizontal(const SkDLine& line, double left, double right,
201 double y, bool flipped) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000202 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 +0000203 // see if end points intersect the opposite line
204 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000205 const SkDPoint leftPt = { left, y };
206 if ((t = line.exactPoint(leftPt)) >= 0) {
207 insert(t, (double) flipped, leftPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000209 if (left != right) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000210 const SkDPoint rightPt = { right, y };
211 if ((t = line.exactPoint(rightPt)) >= 0) {
212 insert(t, (double) !flipped, rightPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000213 }
214 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000215 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
216 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000217 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000218 }
219 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000220 int result = horizontal_coincident(line, y);
221 if (result == 1 && fUsed == 0) {
caryclark624637c2015-05-11 07:21:27 -0700222 fT[0][0] = HorizontalIntercept(line, y);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000223 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
224 if (between(left, xIntercept, right)) {
225 fT[1][0] = (xIntercept - left) / (right - left);
226 if (flipped) {
227 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
228 for (int index = 0; index < result; ++index) {
229 fT[1][index] = 1 - fT[1][index];
230 }
231 }
caryclark@google.com15107262013-11-08 18:00:01 +0000232 fPt[0].fX = xIntercept;
233 fPt[0].fY = y;
234 fUsed = 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000235 }
236 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000237 if (fAllowNear || result == 2) {
halcanary96fcdcc2015-08-27 07:41:13 -0700238 if ((t = line.nearPoint(leftPt, nullptr)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000239 insert(t, (double) flipped, leftPt);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000240 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000241 if (left != right) {
242 const SkDPoint rightPt = { right, y };
halcanary96fcdcc2015-08-27 07:41:13 -0700243 if ((t = line.nearPoint(rightPt, nullptr)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000244 insert(t, (double) !flipped, rightPt);
245 }
246 for (int index = 0; index < 2; ++index) {
247 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
248 insert((double) index, flipped ? 1 - t : t, line[index]);
249 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000250 }
251 }
252 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000253 cleanUpParallelLines(result == 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000254 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255}
256
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000257static int vertical_coincident(const SkDLine& line, double x) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000258 double min = line[0].fX;
259 double max = line[1].fX;
260 if (min > max) {
261 SkTSwap(min, max);
262 }
caryclark@google.com03610322013-04-18 15:58:21 +0000263 if (!precisely_between(min, x, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000264 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000265 }
266 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000267 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000269 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270}
271
caryclark624637c2015-05-11 07:21:27 -0700272double SkIntersections::VerticalIntercept(const SkDLine& line, double x) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000273 return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000274}
275
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000276int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000277 double x, bool flipped) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000278 fMax = 3; // cleanup parallel lines will bring this back line
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000279 // see if end points intersect the opposite line
280 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000281 SkDPoint topPt = { x, top };
282 if ((t = line.exactPoint(topPt)) >= 0) {
283 insert(t, (double) flipped, topPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000284 }
285 if (top != bottom) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000286 SkDPoint bottomPt = { x, bottom };
287 if ((t = line.exactPoint(bottomPt)) >= 0) {
288 insert(t, (double) !flipped, bottomPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000289 }
290 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000291 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
292 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000293 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000294 }
295 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000296 int result = vertical_coincident(line, x);
297 if (result == 1 && fUsed == 0) {
caryclark624637c2015-05-11 07:21:27 -0700298 fT[0][0] = VerticalIntercept(line, x);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000299 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
300 if (between(top, yIntercept, bottom)) {
301 fT[1][0] = (yIntercept - top) / (bottom - top);
302 if (flipped) {
303 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
304 for (int index = 0; index < result; ++index) {
305 fT[1][index] = 1 - fT[1][index];
306 }
307 }
caryclark@google.com15107262013-11-08 18:00:01 +0000308 fPt[0].fX = x;
309 fPt[0].fY = yIntercept;
310 fUsed = 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000311 }
312 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000313 if (fAllowNear || result == 2) {
halcanary96fcdcc2015-08-27 07:41:13 -0700314 if ((t = line.nearPoint(topPt, nullptr)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000315 insert(t, (double) flipped, topPt);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000316 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000317 if (top != bottom) {
318 SkDPoint bottomPt = { x, bottom };
halcanary96fcdcc2015-08-27 07:41:13 -0700319 if ((t = line.nearPoint(bottomPt, nullptr)) >= 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000320 insert(t, (double) !flipped, bottomPt);
321 }
322 for (int index = 0; index < 2; ++index) {
323 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
324 insert((double) index, flipped ? 1 - t : t, line[index]);
325 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000326 }
327 }
328 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000329 cleanUpParallelLines(result == 2);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000330 SkASSERT(fUsed <= 2);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000331 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000332}
333