• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkIntersections.h"
8 #include "SkPathOpsLine.h"
9 
10 /* Determine the intersection point of two lines. This assumes the lines are not parallel,
11    and that that the lines are infinite.
12    From http://en.wikipedia.org/wiki/Line-line_intersection
13  */
Line(const SkDLine & a,const SkDLine & b)14 SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
15     double axLen = a[1].fX - a[0].fX;
16     double ayLen = a[1].fY - a[0].fY;
17     double bxLen = b[1].fX - b[0].fX;
18     double byLen = b[1].fY - b[0].fY;
19     double denom = byLen * axLen - ayLen * bxLen;
20     SkASSERT(denom);
21     double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
22     double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
23     SkDPoint p;
24     p.fX = (term1 * bxLen - axLen * term2) / denom;
25     p.fY = (term1 * byLen - ayLen * term2) / denom;
26     return p;
27 }
28 
cleanUpCoincidence()29 void SkIntersections::cleanUpCoincidence() {
30     SkASSERT(fUsed == 2);
31     // both t values are good
32     bool startMatch = fT[0][0] == 0 && (fT[1][0] == 0 || fT[1][0] == 1);
33     bool endMatch = fT[0][1] == 1 && (fT[1][1] == 0 || fT[1][1] == 1);
34     if (startMatch || endMatch) {
35         removeOne(startMatch);
36         return;
37     }
38     // either t value is good
39     bool pStartMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
40     bool pEndMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
41     removeOne(pStartMatch || !pEndMatch);
42 }
43 
cleanUpParallelLines(bool parallel)44 void SkIntersections::cleanUpParallelLines(bool parallel) {
45     while (fUsed > 2) {
46         removeOne(1);
47     }
48     if (fUsed == 2 && !parallel) {
49         bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
50         bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
51         if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) {
52             SkASSERT(startMatch || endMatch);
53             removeOne(endMatch);
54         }
55     }
56 }
57 
computePoints(const SkDLine & line,int used)58 void SkIntersections::computePoints(const SkDLine& line, int used) {
59     fPt[0] = line.ptAtT(fT[0][0]);
60     if ((fUsed = used) == 2) {
61         fPt[1] = line.ptAtT(fT[0][1]);
62     }
63 }
64 
intersectRay(const SkDLine & a,const SkDLine & b)65 int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
66     fMax = 2;
67     SkDVector aLen = a[1] - a[0];
68     SkDVector bLen = b[1] - b[0];
69     /* Slopes match when denom goes to zero:
70                       axLen / ayLen ==                   bxLen / byLen
71     (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
72              byLen  * axLen         ==  ayLen          * bxLen
73              byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
74      */
75     double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX;
76     SkDVector ab0 = a[0] - b[0];
77     double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
78     double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
79 #if 0
80     if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
81         fUsed = 0;
82         return 0;
83     }
84 #endif
85     numerA /= denom;
86     numerB /= denom;
87     int used;
88     if (!approximately_zero(denom)) {
89         fT[0][0] = numerA;
90         fT[1][0] = numerB;
91         used = 1;
92     } else {
93        /* See if the axis intercepts match:
94                   ay - ax * ayLen / axLen  ==          by - bx * ayLen / axLen
95          axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
96          axLen *  ay - ax * ayLen          == axLen *  by - bx * ayLen
97         */
98         if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX,
99                 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) {
100             return fUsed = 0;
101         }
102         // there's no great answer for intersection points for coincident rays, but return something
103         fT[0][0] = fT[1][0] = 0;
104         fT[1][0] = fT[1][1] = 1;
105         used = 2;
106     }
107     computePoints(a, used);
108     return fUsed;
109 }
110 
111 // note that this only works if both lines are neither horizontal nor vertical
intersect(const SkDLine & a,const SkDLine & b)112 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
113     fMax = 3;  // note that we clean up so that there is no more than two in the end
114     // see if end points intersect the opposite line
115     double t;
116     for (int iA = 0; iA < 2; ++iA) {
117         if ((t = b.exactPoint(a[iA])) >= 0) {
118             insert(iA, t, a[iA]);
119         }
120     }
121     for (int iB = 0; iB < 2; ++iB) {
122         if ((t = a.exactPoint(b[iB])) >= 0) {
123             insert(t, iB, b[iB]);
124         }
125     }
126     /* Determine the intersection point of two line segments
127        Return FALSE if the lines don't intersect
128        from: http://paulbourke.net/geometry/lineline2d/ */
129     double axLen = a[1].fX - a[0].fX;
130     double ayLen = a[1].fY - a[0].fY;
131     double bxLen = b[1].fX - b[0].fX;
132     double byLen = b[1].fY - b[0].fY;
133     /* Slopes match when denom goes to zero:
134                       axLen / ayLen ==                   bxLen / byLen
135     (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
136              byLen  * axLen         ==  ayLen          * bxLen
137              byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
138      */
139     double axByLen = axLen * byLen;
140     double ayBxLen = ayLen * bxLen;
141     // detect parallel lines the same way here and in SkOpAngle operator <
142     // so that non-parallel means they are also sortable
143     bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen)
144             : NotAlmostDequalUlps(axByLen, ayBxLen);
145     if (unparallel && fUsed == 0) {
146         double ab0y = a[0].fY - b[0].fY;
147         double ab0x = a[0].fX - b[0].fX;
148         double numerA = ab0y * bxLen - byLen * ab0x;
149         double numerB = ab0y * axLen - ayLen * ab0x;
150         double denom = axByLen - ayBxLen;
151         if (between(0, numerA, denom) && between(0, numerB, denom)) {
152             fT[0][0] = numerA / denom;
153             fT[1][0] = numerB / denom;
154             computePoints(a, 1);
155         }
156     }
157 /* Allow tracking that both sets of end points are near each other -- the lines are entirely
158    coincident -- even when the end points are not exactly the same.
159    Mark this as a 'wild card' for the end points, so that either point is considered totally
160    coincident. Then, avoid folding the lines over each other, but allow either end to mate
161    to the next set of lines.
162  */
163     if (fAllowNear || !unparallel) {
164         double aNearB[2];
165         double bNearA[2];
166         bool aNotB[2] = {false, false};
167         bool bNotA[2] = {false, false};
168         int nearCount = 0;
169         for (int index = 0; index < 2; ++index) {
170             aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]);
171             nearCount += t >= 0;
172             bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]);
173             nearCount += t >= 0;
174         }
175         if (nearCount > 0) {
176             for (int iA = 0; iA < 2; ++iA) {
177                 if (!aNotB[iA]) {
178                     continue;
179                 }
180                 int nearer = aNearB[iA] > 0.5;
181                 if (!bNotA[nearer]) {
182                     continue;
183                 }
184                 SkASSERT(a[iA] != b[nearer]);
185                 SkASSERT(iA == (bNearA[nearer] > 0.5));
186                 fNearlySame[iA] = true;
187                 insertNear(iA, nearer, a[iA], b[nearer]);
188                 aNearB[iA] = -1;
189                 bNearA[nearer] = -1;
190                 nearCount -= 2;
191             }
192             if (nearCount > 0) {
193                 for (int iA = 0; iA < 2; ++iA) {
194                     if (aNearB[iA] >= 0) {
195                         insert(iA, aNearB[iA], a[iA]);
196                     }
197                 }
198                 for (int iB = 0; iB < 2; ++iB) {
199                     if (bNearA[iB] >= 0) {
200                         insert(bNearA[iB], iB, b[iB]);
201                     }
202                 }
203             }
204         }
205     }
206     cleanUpParallelLines(!unparallel);
207     SkASSERT(fUsed <= 2);
208     return fUsed;
209 }
210 
horizontal_coincident(const SkDLine & line,double y)211 static int horizontal_coincident(const SkDLine& line, double y) {
212     double min = line[0].fY;
213     double max = line[1].fY;
214     if (min > max) {
215         SkTSwap(min, max);
216     }
217     if (min > y || max < y) {
218         return 0;
219     }
220     if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) {
221         return 2;
222     }
223     return 1;
224 }
225 
horizontal_intercept(const SkDLine & line,double y)226 static double horizontal_intercept(const SkDLine& line, double y) {
227      return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
228 }
229 
horizontal(const SkDLine & line,double y)230 int SkIntersections::horizontal(const SkDLine& line, double y) {
231     fMax = 2;
232     int horizontalType = horizontal_coincident(line, y);
233     if (horizontalType == 1) {
234         fT[0][0] = horizontal_intercept(line, y);
235     } else if (horizontalType == 2) {
236         fT[0][0] = 0;
237         fT[0][1] = 1;
238     }
239     return fUsed = horizontalType;
240 }
241 
horizontal(const SkDLine & line,double left,double right,double y,bool flipped)242 int SkIntersections::horizontal(const SkDLine& line, double left, double right,
243                                 double y, bool flipped) {
244     fMax = 3;  // clean up parallel at the end will limit the result to 2 at the most
245     // see if end points intersect the opposite line
246     double t;
247     const SkDPoint leftPt = { left, y };
248     if ((t = line.exactPoint(leftPt)) >= 0) {
249         insert(t, (double) flipped, leftPt);
250     }
251     if (left != right) {
252         const SkDPoint rightPt = { right, y };
253         if ((t = line.exactPoint(rightPt)) >= 0) {
254             insert(t, (double) !flipped, rightPt);
255         }
256         for (int index = 0; index < 2; ++index) {
257             if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
258                 insert((double) index, flipped ? 1 - t : t, line[index]);
259             }
260         }
261     }
262     int result = horizontal_coincident(line, y);
263     if (result == 1 && fUsed == 0) {
264         fT[0][0] = horizontal_intercept(line, y);
265         double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
266         if (between(left, xIntercept, right)) {
267             fT[1][0] = (xIntercept - left) / (right - left);
268             if (flipped) {
269                 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
270                 for (int index = 0; index < result; ++index) {
271                     fT[1][index] = 1 - fT[1][index];
272                 }
273             }
274             fPt[0].fX = xIntercept;
275             fPt[0].fY = y;
276             fUsed = 1;
277         }
278     }
279     if (fAllowNear || result == 2) {
280         if ((t = line.nearPoint(leftPt, NULL)) >= 0) {
281             insert(t, (double) flipped, leftPt);
282         }
283         if (left != right) {
284             const SkDPoint rightPt = { right, y };
285             if ((t = line.nearPoint(rightPt, NULL)) >= 0) {
286                 insert(t, (double) !flipped, rightPt);
287             }
288             for (int index = 0; index < 2; ++index) {
289                 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
290                     insert((double) index, flipped ? 1 - t : t, line[index]);
291                 }
292             }
293         }
294     }
295     cleanUpParallelLines(result == 2);
296     return fUsed;
297 }
298 
vertical_coincident(const SkDLine & line,double x)299 static int vertical_coincident(const SkDLine& line, double x) {
300     double min = line[0].fX;
301     double max = line[1].fX;
302     if (min > max) {
303         SkTSwap(min, max);
304     }
305     if (!precisely_between(min, x, max)) {
306         return 0;
307     }
308     if (AlmostEqualUlps(min, max)) {
309         return 2;
310     }
311     return 1;
312 }
313 
vertical_intercept(const SkDLine & line,double x)314 static double vertical_intercept(const SkDLine& line, double x) {
315     return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
316 }
317 
vertical(const SkDLine & line,double x)318 int SkIntersections::vertical(const SkDLine& line, double x) {
319     fMax = 2;
320     int verticalType = vertical_coincident(line, x);
321     if (verticalType == 1) {
322         fT[0][0] = vertical_intercept(line, x);
323     } else if (verticalType == 2) {
324         fT[0][0] = 0;
325         fT[0][1] = 1;
326     }
327     return fUsed = verticalType;
328 }
329 
vertical(const SkDLine & line,double top,double bottom,double x,bool flipped)330 int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
331                               double x, bool flipped) {
332     fMax = 3;  // cleanup parallel lines will bring this back line
333     // see if end points intersect the opposite line
334     double t;
335     SkDPoint topPt = { x, top };
336     if ((t = line.exactPoint(topPt)) >= 0) {
337         insert(t, (double) flipped, topPt);
338     }
339     if (top != bottom) {
340         SkDPoint bottomPt = { x, bottom };
341         if ((t = line.exactPoint(bottomPt)) >= 0) {
342             insert(t, (double) !flipped, bottomPt);
343         }
344         for (int index = 0; index < 2; ++index) {
345             if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
346                 insert((double) index, flipped ? 1 - t : t, line[index]);
347             }
348         }
349     }
350     int result = vertical_coincident(line, x);
351     if (result == 1 && fUsed == 0) {
352         fT[0][0] = vertical_intercept(line, x);
353         double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
354         if (between(top, yIntercept, bottom)) {
355             fT[1][0] = (yIntercept - top) / (bottom - top);
356             if (flipped) {
357                 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
358                 for (int index = 0; index < result; ++index) {
359                     fT[1][index] = 1 - fT[1][index];
360                 }
361             }
362             fPt[0].fX = x;
363             fPt[0].fY = yIntercept;
364             fUsed = 1;
365         }
366     }
367     if (fAllowNear || result == 2) {
368         if ((t = line.nearPoint(topPt, NULL)) >= 0) {
369             insert(t, (double) flipped, topPt);
370         }
371         if (top != bottom) {
372             SkDPoint bottomPt = { x, bottom };
373             if ((t = line.nearPoint(bottomPt, NULL)) >= 0) {
374                 insert(t, (double) !flipped, bottomPt);
375             }
376             for (int index = 0; index < 2; ++index) {
377                 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
378                     insert((double) index, flipped ? 1 - t : t, line[index]);
379                 }
380             }
381         }
382     }
383     cleanUpParallelLines(result == 2);
384     SkASSERT(fUsed <= 2);
385     return fUsed;
386 }
387 
388 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
389 // 4 subs, 2 muls, 1 cmp
ccw(const SkDPoint & A,const SkDPoint & B,const SkDPoint & C)390 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
391     return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
392 }
393 
394 // 16 subs, 8 muls, 6 cmps
Test(const SkDLine & a,const SkDLine & b)395 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
396     return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
397             && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
398 }
399