xref: /aosp_15_r20/external/skia/modules/bentleyottmann/src/Myers.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 // Copyright 2023 Google LLC
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3 
4 #include "modules/bentleyottmann/include/Myers.h"
5 
6 #include "include/core/SkSpan.h"
7 #include "include/private/base/SkAssert.h"
8 #include "include/private/base/SkTo.h"
9 #include "modules/bentleyottmann/include/Int96.h"
10 
11 #include <algorithm>
12 #include <climits>
13 #include <cstdint>
14 #include <iterator>
15 #include <tuple>
16 #include <utility>
17 #include <vector>
18 
19 namespace myers {
20 
21 // -- Point ----------------------------------------------------------------------------------------
operator -(const Point & p0,const Point & p1)22 Point operator-(const Point& p0, const Point& p1) {
23     return {p0.x - p1.x, p0.y - p1.y};
24 }
25 
point_to_s64(Point p)26 std::tuple<int64_t, int64_t> point_to_s64(Point p) {
27     return std::make_tuple(SkToS64(p.x), SkToS64(p.y));
28 }
29 
30 // -- Segment --------------------------------------------------------------------------------------
upper() const31 const Point& Segment::upper() const {
32     return fUpper;
33 }
34 
lower() const35 const Point& Segment::lower() const {
36     return fLower;
37 }
38 
bounds() const39 std::tuple<int32_t, int32_t, int32_t, int32_t> Segment::bounds() const {
40     auto [left, right] = std::minmax(fUpper.x, fLower.x);
41     return std::make_tuple(left, fUpper.y, right, fLower.y);
42 }
43 
isHorizontal() const44 bool Segment::isHorizontal() const {
45     return fUpper.y == fLower.y;
46 }
47 
isVertical() const48 bool Segment::isVertical() const {
49     return fUpper.x == fLower.x;
50 }
51 
52 // Return:
53 //    | d0x   d1x |
54 //    | d0y   d1y |
cross(Point d0,Point d1)55 int64_t cross(Point d0, Point d1) {
56     const auto [d0x, d0y] = point_to_s64(d0);
57     const auto [d1x, d1y] = point_to_s64(d1);
58     return d0x * d1y - d1x * d0y;
59 }
60 
61 // compare_slopes returns a comparison of the slope of s0 to the slope of s1 where
62 //    slope(s) = dx / dy
63 // instead of the regular dy / dx, and neither s0 nor s1 are horizontal.
64 //
65 // The slope for non-horizontal segments monotonically increases from the smallest along the
66 // negative x-axis increasing counterclockwise to the largest along the positive x-axis.
compare_slopes(const Segment & s0,const Segment & s1)67 int64_t compare_slopes(const Segment& s0, const Segment& s1) {
68     // Handle cases involving horizontal segments.
69     if (s0.isHorizontal() || s1.isHorizontal()) {
70         if (s0.isHorizontal() && s1.isHorizontal()) {
71             // slope(s0) == slope(s1)
72             return 0;
73         }
74         if (s0.isHorizontal()) {
75             // slope(s0) > slope(s1)
76             return 1;
77         } else {
78             // slope(s0) < slope(s1)
79             return -1;
80         }
81     }
82 
83     const auto [u0, l0] = s0;
84     const auto [u1, l1] = s1;
85 
86     const Point d0 = l0 - u0;
87     const Point d1 = l1 - u1;
88 
89     // Since horizontal lines are handled separately and because of the ordering of points for
90     // a segment, then d0y and d1y should always be positive.
91     SkASSERT(d0.y > 0 && d1.y > 0);
92 
93     //     * slope(s0) = d0.x / d0.y
94     //     * slope(s1) = d1.x / d1.y
95     // If we want to find d0.x / d0.y < d1.x / d1.y, then
96     //    d0x * d1y < d1x * d0y
97     //    d0x * d1y - d1x * d0y < 0.
98     //
99     // We know that d0.y and d1.y are both positive, therefore, we can do a cross multiply without
100     // worrying about changing the relation.
101     // We can define ==, and > in a similar way:
102     //    * <  - cross_of_slopes(s0, s1) < 0
103     //    * == - cross_of_slopes(s0, s1) == 0
104     //    * >  - cross_of_slopes(s0, s1) > 0
105     return cross(d0, d1);
106 }
107 
108 // Returns true of slope(s0) < slope(s1). See compare_slopes above for more information.
slope_s0_less_than_slope_s1(const Segment & s0,const Segment & s1)109 bool slope_s0_less_than_slope_s1(const Segment& s0, const Segment& s1) {
110     return compare_slopes(s0, s1) < 0;
111 }
112 
113 // compare_point_to_segment the relation between a point p and a segment s in the following way:
114 //     * p < s if the cross product is negative.
115 //     * p == s if the cross product is zero.
116 //     * p > s if the cross product is positive.
compare_point_to_segment(Point p,const Segment & s)117 int64_t compare_point_to_segment(Point p, const Segment& s) {
118     const auto [u, l] = s;
119 
120     // The segment must span p vertically.
121     SkASSERT(u.y <= p.y && p.y <= l.y);
122 
123     // Check horizontal extents.
124     {
125         const auto [left, right] = std::minmax(u.x, l.x);
126         if (p.x < left) {
127             return -1;
128         }
129 
130         if (right < p.x) {
131             return 1;
132         }
133     }
134 
135     // If s is horizontal, then p is on the interval [u.x, l.x].
136     if (s.isHorizontal()) {
137         return 0;
138     }
139 
140     // The point p < s when:
141     //     p.x < u.x + (l.x - u.x)(p.y - u.y) / (l.y - u.y),
142     //     p.x - u.x < (l.x - u.x)(p.y - u.y) / (l.y - u.y),
143     //     (p.x - u.x)(l.y - u.y) < (l.x - u.x)(p.y - u.y),
144     //     (p.x - u.x)(l.y - u.y) - (l.x - u.x)(p.y - u.y) < 0,
145     //     (p - u) x (l - u) < 0,
146     //     dUtoP x dS < 0.
147     // The other relations can be implemented in a similar way.
148     const Point dUToP = p - u;
149     const Point dS = l - u;
150 
151     SkASSERT(dS.y > 0);
152     return cross(dUToP, dS);
153 }
154 
155 // segment_less_than_upper_to_insert is used with std::lower_bound to find the place to insert the
156 // segment to_insert in a vector. The signature of this function is crafted to work with
157 // lower_bound.
segment_less_than_upper_to_insert(const Segment & segment,const Segment & to_insert)158 bool segment_less_than_upper_to_insert(const Segment& segment, const Segment& to_insert) {
159     const int64_t compare = compare_point_to_segment(to_insert.upper(), segment);
160 
161     // compare > 0 when segment < to_insert.upper().
162     return (compare > 0) || ((compare == 0) && slope_s0_less_than_slope_s1(segment, to_insert));
163 }
164 
165 // Return true if s0(y) < s1(y) else if s0(y) == s1(y) then slope(s0) < slope(s1)
s0_less_than_s1_at_y(const Segment & s0,const Segment & s1,int32_t y)166 bool s0_less_than_s1_at_y(const Segment& s0, const Segment& s1, int32_t y) {
167     // Neither s0 nor s1 are horizontal because this is used during the sorting phase
168     SkASSERT(!s0.isHorizontal() && !s1.isHorizontal());
169 
170     const auto [u0, l0] = s0;
171     const auto [u1, l1] = s1;
172 
173     const auto [left0, right0] = std::minmax(u0.x, l0.x);
174     const auto [left1, right1] = std::minmax(u1.x, l1.x);
175 
176     if (right0 < left1) {
177         return true;
178     } else if (right1 < left0) {
179         return false;
180     }
181 
182     const Point d0 = l0 - u0;
183     const Point d1 = l1 - u1;
184 
185     // Since horizontal lines are handled separately and the ordering of points for the segment,
186     // then there should always be positive Dy.
187     SkASSERT(d0.y > 0 && d1.y > 0);
188 
189     namespace bo = bentleyottmann;
190     using Int96 = bo::Int96;
191 
192     // Defining s0(y) and s1(y),
193     //    s0(y) = u0.x + (y - u0.y) * d0.x / d0.y
194     //    s1(y) = u1.x + (y - u1.y) * d1.x / d1.y
195     // Find the following
196     //    s0(y) < s1(y)
197     // Substituting s0(y) and s1(y)
198     //    u0.x + (y - u0.y) * d0.x / d0.y < u1.x + (y - u1.y) * d1.x / d1.y
199     // Factoring out the denominator.
200     //    (u0.x * d0.y + (y - u0.y) * d0.x) / d0.y < (u1.x * d1.y + (y - u1.y) * d1.x) / d1.y
201     // Cross-multiplying the denominators. The sign will not switch because d0.y and d1.y are
202     // always positive.
203     //    d1.y * (u0.x * d0.y + (y - u0.y) * d0.x) < d0.y * (u1.x * d1.y + (y - u1.y) * d1.x)
204     // If these are equal, then we use the slope to break the tie.
205     //    d0.x / d0.y < d1.x / d1.y
206     // Cross multiplying leaves.
207     //    d0.x * d1.y < d1.x * d0.y
208     const Int96 lhs = bo::multiply(d1.y, u0.x * SkToS64(d0.y) + (y - u0.y) * SkToS64(d0.x));
209     const Int96 rhs = bo::multiply(d0.y, u1.x * SkToS64(d1.y) + (y - u1.y) * SkToS64(d1.x));
210 
211     return lhs < rhs || ((lhs == rhs) && slope_s0_less_than_slope_s1(s0, s1));
212 }
213 
214 // -- Event ----------------------------------------------------------------------------------------
215 // Events are horizontal lines at a given y where segments are added, or they contain one or
216 // more horizontal lines, or segments end.
217 struct Event {
218     const int32_t y;
219 
220     // The set of segments that begin at y.
221     SkSpan<const Segment> begin;
222 
223     // The set of segments that are horizontal on y.
224     SkSpan<const Segment> horizontal;
225 
226     // The set of segments that end on y.
227     SkSpan<const Segment> end;
228 };
229 
230 // -- EventQueue -----------------------------------------------------------------------------------
231 // The EventQueue produces Events. Events are never added to the queue after initial creation.
232 class EventQueue {
233     class Iterator {
234     public:
235         using value_type = Event;
236         using difference_type = ptrdiff_t;
237         using pointer = value_type*;
238         using reference = value_type;
239         using iterator_category = std::input_iterator_tag;
Iterator(const EventQueue & eventQueue,size_t index)240         Iterator(const EventQueue& eventQueue, size_t index)
241             : fEventQueue{eventQueue}
242             , fIndex{index} { }
Iterator(const Iterator & that)243         Iterator(const Iterator& that) : Iterator{ that.fEventQueue, that.fIndex } { }
operator ++()244         Iterator& operator++() { ++fIndex; return *this; }
operator ++(int)245         Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
operator ==(const Iterator & rhs) const246         bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
operator !=(const Iterator & rhs) const247         bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
operator *()248         value_type operator*() { return fEventQueue[fIndex]; }
operator -(Iterator lhs,Iterator rhs)249         friend difference_type operator-(Iterator lhs, Iterator rhs) {
250             return lhs.fIndex - rhs.fIndex;
251         }
252 
253     private:
254         const EventQueue& fEventQueue;
255         size_t fIndex = 0;
256     };
257 
258     // Events are stored using CompactEvent, Events are only passed back from nextEvent. start,
259     // endOfBegin, etc. are all indexes into fSegmentStorage. The beginning segments for the event
260     // are from start to endOfBegin, the horizontal segments are from endOfBegin to endOfStart, and
261     // the end segments are from endOfHorizontal to endOfEnd.
262     class CompactEvent {
263     public:
264         const int32_t y;
265         const int32_t start;
266         const int32_t endOfBegin;
267         const int32_t endOfHorizontal;
268         const int32_t endOfEnd;
269     };
270 
271 public:
272     // Given a list of segments make an EventQueue, and populate its queue with events.
Make(const SkSpan<const Segment> segments)273     static EventQueue Make(const SkSpan<const Segment> segments) {
274         SkASSERT(!segments.empty());
275         SkASSERT(segments.size() < INT32_MAX);
276 
277         enum EventType {
278             kBegin = 0,
279             kHorizontal = 1,
280             kEnd = 2
281         };
282 
283         // A vector of SetupTuple when ordered will produce the events, and all the different
284         // sets of segments (beginning, etc.).
285         struct SetupTuple {
286             // The main ordering of events.
287             int32_t yOrdering;
288 
289             // Group together like event types together.
290             EventType type;
291 
292             // Break ties if yOrdering is the same.
293             int32_t xTieBreaker;
294 
295             // We want to sort the segments, but they are not part of the key.
296             Segment originalSegment;
297 
298             bool operator==(const SetupTuple& r) const {
299                 return
300                     std::tie(this->yOrdering, this->type, this->xTieBreaker, this->originalSegment)
301                         == std::tie(r.yOrdering, r.type, r.xTieBreaker, r.originalSegment);
302             }
303         };
304 
305         std::vector<SetupTuple> eventOrdering;
306         for (const auto& s : segments) {
307 
308             // Exclude zero length segments.
309             if (s.upper() == s.lower()) {
310                 continue;
311             }
312 
313             if (s.isHorizontal()) {
314                 // Tag for the horizontal set.
315                 eventOrdering.push_back(SetupTuple{s.upper().y, kHorizontal, -s.upper().x, s});
316             } else {
317                 // Tag for the beginning and ending sets.
318                 eventOrdering.push_back(SetupTuple{s.upper().y, kBegin, -s.upper().x, s});
319                 eventOrdering.push_back(SetupTuple{s.lower().y, kEnd, -s.lower().x, s});
320             }
321         }
322 
323         // Order the tuples by y, then by set type, then by x value.
324         auto eventLess = [](const SetupTuple& l, const SetupTuple& r) {
325             return std::tie(l.yOrdering, l.type, l.xTieBreaker) <
326                    std::tie(r.yOrdering, r.type, r.xTieBreaker);
327         };
328 
329         // Sort the events.
330         std::sort(eventOrdering.begin(), eventOrdering.end(), eventLess);
331 
332         // Remove duplicate segments.
333         auto eraseFrom = std::unique(eventOrdering.begin(), eventOrdering.end());
334         eventOrdering.erase(eraseFrom, eventOrdering.end());
335 
336         std::vector<CompactEvent> events;
337         std::vector<Segment> segmentStorage;
338         segmentStorage.reserve(eventOrdering.size());
339 
340         int32_t currentY = eventOrdering.front().yOrdering;
341         int32_t start = 0,
342                 endOfBegin = 0,
343                 endOfHorizontal = 0,
344                 endOfEnd = 0;
345         for (const auto& [y, type, _, s] : eventOrdering) {
346             // If this is a new y then create the compact event.
347             if (currentY != y) {
348                 events.push_back(CompactEvent{currentY,
349                                               start,
350                                               endOfBegin,
351                                               endOfHorizontal,
352                                               endOfEnd});
353                 start = endOfBegin = endOfHorizontal = endOfEnd = segmentStorage.size();
354                 currentY = y;
355             }
356 
357             segmentStorage.push_back(s);
358 
359             // Increment the various set indices.
360             const size_t segmentCount = segmentStorage.size();
361             switch (type) {
362                 case kBegin: endOfBegin = segmentCount;
363                     [[fallthrough]];
364                 case kHorizontal: endOfHorizontal = segmentCount;
365                     [[fallthrough]];
366                 case kEnd: endOfEnd = segmentCount;
367             }
368         }
369 
370         // Store the last event.
371         events.push_back(CompactEvent{currentY,
372                                       start,
373                                       endOfBegin,
374                                       endOfHorizontal,
375                                       endOfEnd});
376 
377         return EventQueue{std::move(events), std::move(segmentStorage)};
378     }
379 
operator [](size_t i) const380     Event operator[](size_t i) const {
381         SkASSERT(i < fEvents.size());
382         auto& [y, start, endOfBegin, endOfHorizontal, endOfEnd] = fEvents[i];
383         SkSpan<const Segment> begin{&fSegmentStorage[start], endOfBegin - start};
384         SkSpan<const Segment>
385             horizontal{&fSegmentStorage[endOfBegin], endOfHorizontal - endOfBegin};
386         SkSpan<const Segment> end{&fSegmentStorage[endOfHorizontal], endOfEnd - endOfHorizontal};
387         return Event{y, begin, horizontal, end};
388     }
389 
begin() const390     Iterator begin() const {
391         return Iterator{*this, 0};
392     }
393 
end() const394     Iterator end() const {
395         return Iterator{*this, fEvents.size()};
396     }
397 
size() const398     size_t size() const {
399         return fEvents.size();
400     }
401 
empty() const402     bool empty() const {
403         return fEvents.empty();
404     }
405 
406 private:
EventQueue(std::vector<CompactEvent> && events,std::vector<Segment> && segmentStorage)407     EventQueue(std::vector<CompactEvent>&& events, std::vector<Segment>&& segmentStorage)
408             : fEvents{std::move(events)}
409             , fSegmentStorage{std::move(segmentStorage)} {}
410 
411     const std::vector<CompactEvent> fEvents;
412     const std::vector<Segment> fSegmentStorage;
413 };
414 
415 // -- CrossingAccumulator --------------------------------------------------------------------------
416 // Collect all the crossings, and reject endpoint-to-endpoint crossings as those intersections
417 // are already represented in the data.
418 class CrossingAccumulator {
419 public:
recordCrossing(const Segment & s0,const Segment & s1)420     void recordCrossing(const Segment& s0, const Segment& s1) {
421         // Endpoints with no possible interior overlap.
422         if (s0.upper() == s1.lower() || s0.lower() == s1.upper()) {
423             return;
424         }
425 
426         // Segments don't overlap if they are not collinear.
427         if ((s0.upper() == s1.upper() || s0.lower() == s1.lower()) && compare_slopes(s0, s1) != 0) {
428             return;
429         }
430 
431         fCrossings.emplace_back(s0, s1);
432     }
433 
finishAndReleaseCrossings()434     std::vector<Crossing> finishAndReleaseCrossings() {
435         return std::move(fCrossings);
436     }
437 
438 private:
439     std::vector<Crossing> fCrossings;
440 };
441 
442 class SweepLine {
443     static constexpr Segment kLeftStatusSentinel{{INT32_MIN, INT32_MIN}, {INT32_MIN, INT32_MAX}};
444     static constexpr Segment kRightStatusSentinel{{INT32_MAX, INT32_MIN}, {INT32_MAX, INT32_MAX}};
445 
446 public:
SweepLine()447     SweepLine() {
448         fStatus.push_back(kLeftStatusSentinel);
449         fStatus.push_back(kRightStatusSentinel);
450     }
451 
handleEvent(Event e)452     void handleEvent(Event e) {
453         auto& [y, beginnings, horizontals, endings] = e;
454 
455         // Things could be out of order from last event.
456         this->sortAndRecord(y);
457 
458         this->handleBeginnings(y, beginnings);
459         this->handleHorizontals(y, horizontals);
460         this->handleEndings(endings);
461     }
462 
finishAndReleaseCrossings()463     std::vector<Crossing> finishAndReleaseCrossings() {
464         // Only the sentinels should be left.
465         SkASSERT(this->statusEmpty());
466         return fCrossings.finishAndReleaseCrossings();
467     }
468 
469 private:
470     using StatusLine = std::vector<Segment>;
471 
statusEmpty() const472     bool statusEmpty() const {
473         return fStatus.size() == 2;
474     }
475 
476     // Sort the status line, if items are swapped, then there is a crossing to record.
sortAndRecord(int32_t y)477     void sortAndRecord(int32_t y) {
478         // If there are only the sentinels or just 1 segment, then nothing to sort.
479         if (fStatus.size() <= 3) {
480             return;
481         }
482 
483         // Skip the first and last sentinels.
484         for (size_t i = 2; i < fStatus.size() - 1; ++i) {
485             const Segment t = fStatus[i];
486             size_t j = i;
487             for (; j > 1 && s0_less_than_s1_at_y(t, fStatus[j - 1], y); --j) {
488                 // While t < the thing before it move it down.
489                 fCrossings.recordCrossing(t, fStatus[j-1]);
490                 fStatus[j] = fStatus[j-1];
491             }
492             fStatus[j] = t;
493         }
494     }
495 
496     // When inserting a starting point (either a beginning or a horizontal) check the segments to
497     // the left and the right checking nearby segments for crossings.
498     template <typename CrossingCheck>
checkCrossingsLeftAndRight(const Segment & segment,StatusLine::iterator insertionPoint,CrossingCheck check)499     void checkCrossingsLeftAndRight(
500             const Segment& segment, StatusLine::iterator insertionPoint, CrossingCheck check) {
501 
502         // Match to the left using the left sentinel to break the loop.
503         for (auto cursor = std::make_reverse_iterator(insertionPoint); check(*cursor); ++cursor) {
504             fCrossings.recordCrossing(segment, *cursor);
505         }
506 
507         // Match to the right using the right sentinel to break the loop.
508         for (auto cursor = insertionPoint; check(*cursor); ++cursor) {
509             fCrossings.recordCrossing(segment, *cursor);
510         }
511     }
512 
513     // Add segments that start on y excluding horizontals.
handleBeginnings(int32_t y,SkSpan<const Segment> inserting)514     void handleBeginnings(int32_t y, SkSpan<const Segment> inserting) {
515         for (const Segment& s : inserting) {
516             auto insertionPoint =
517                     std::lower_bound(fStatus.begin(), fStatus.end(), s,
518                                      segment_less_than_upper_to_insert);
519 
520             // Checking intersections left and right checks if the point s.upper() lies on
521             // the nearby segment.
522             auto checkIntersect = [&](const Segment& toCheck) {
523                 return compare_point_to_segment(s.upper(), toCheck) == 0;
524             };
525             this->checkCrossingsLeftAndRight(s, insertionPoint, checkIntersect);
526 
527             fStatus.insert(insertionPoint, s);
528         }
529     }
530 
531     // Horizontals on y are handled by checking for crossings by adding them, and then immediately
532     // removing them.
handleHorizontals(int32_t y,SkSpan<const Segment> horizontals)533     void handleHorizontals(int32_t y, SkSpan<const Segment> horizontals) {
534         for (const Segment& s : horizontals) {
535             auto insertionPoint =
536                     std::lower_bound(fStatus.begin(), fStatus.end(), s,
537                                      segment_less_than_upper_to_insert);
538 
539             // Check if the nearby segment crosses the horizontal line.
540             auto checkIntersection = [&](const Segment& toCheck) {
541                 return compare_point_to_segment(s.upper(), toCheck) <= 0 &&
542                        compare_point_to_segment(s.lower(), toCheck) >= 0;
543             };
544             this->checkCrossingsLeftAndRight(s, insertionPoint, checkIntersection);
545 
546             fStatus.insert(insertionPoint, s);
547         }
548 
549         this->handleEndings(horizontals);
550     }
551 
552     // Remove all the segments ending on y.
handleEndings(SkSpan<const Segment> removing)553     void handleEndings(SkSpan<const Segment> removing) {
554         for (const Segment& s : removing) {
555             auto removedPoint = std::remove(fStatus.begin(), fStatus.end(), s);
556             SkASSERT(removedPoint != fStatus.end());
557             fStatus.erase(removedPoint, fStatus.end());
558         }
559     }
560 
561     StatusLine fStatus;
562     CrossingAccumulator fCrossings;
563 };
564 
myers_find_crossings(const SkSpan<const Segment> segments)565 std::vector<Crossing> myers_find_crossings(const SkSpan<const Segment> segments) {
566     const EventQueue eventQueue = EventQueue::Make(segments);
567     SweepLine sweepLine;
568 
569     for (const Event& event : eventQueue) {
570         sweepLine.handleEvent(event);
571     }
572 
573     return sweepLine.finishAndReleaseCrossings();
574 }
575 
576 // This intersection algorithm is from "Robust Plane Sweep for Intersecting Segments" page 10.
s0_intersects_s1(const Segment & s0,const Segment & s1)577 bool s0_intersects_s1(const Segment& s0, const Segment& s1) {
578     // Make sure that s0 upper is above s1 upper.
579     if (s1.upper().y < s0.upper().y
580         || ((s1.upper().y == s0.upper().y) && (s1.lower().y > s0.lower().y))) {
581 
582         // Swap to put in the right orientation.
583         return s0_intersects_s1(s1, s0);
584     }
585 
586     SkASSERT(s0.upper().y <= s1.upper().y);
587 
588     {  // If extents don't overlap then there is no intersection.
589         auto [left0, top0, right0, bottom0] = s0.bounds();
590         auto [left1, top1, right1, bottom1] = s1.bounds();
591         if (right1 < left0 || right0 < left1 || bottom1 < top0 || bottom0 < top1) {
592             return false;
593         }
594     }
595 
596     auto [u0, l0] = s0;
597     auto [u1, l1] = s1;
598 
599     const Point D0 = l0 - u0,
600                 D1 = l1 - u1;
601 
602     // If the vector from u0 to l0 (named D0) and the vector from u0 to u1 have an angle of 0
603     // between them, then u1 is on the segment u0 to l0 (named s0).
604     const Point U0toU1 = (u1 - u0);
605     const int64_t D0xU0toU1 = cross(D0, U0toU1);
606     if (D0xU0toU1 == 0) {
607         // u1 is on s0.
608         return true;
609     }
610 
611     if (l1.y <= l0.y) {
612         // S1 is between the upper and lower points of S0.
613         const Point U0toL1 = (l1 - u0);
614         const int64_t D0xU0toL1 = cross(D0, U0toL1);
615         if (D0xU0toL1 == 0) {
616             // l1 is on s0.
617             return true;
618         }
619 
620         // If U1 and L1 are on opposite sides of D0 then the segments cross.
621         return (D0xU0toU1 ^ D0xU0toL1) < 0;
622     } else {
623         // S1 extends past S0. It could be that S1 crosses the line of S0 (not the bound segment)
624         // beyond the endpoints of S0. Make sure that it crosses on the segment and not beyond.
625         const Point U1toL0 = (l0 - u1);
626         const int64_t D1xU1toL0 = cross(D1, U1toL0);
627         if (D1xU1toL0 == 0) {
628             return true;
629         }
630 
631         // For D1 to cross D0, then D1 must be on the same side of U1toL0 as D0. D0xU0toU1
632         // describes the orientation of U0 compared to D0. The angle from D1 to U1toL0 must
633         // have the same direction as the angle from U0toU1 to D0.
634         return (D0xU0toU1 ^ D1xU1toL0) >= 0;
635     }
636 }
637 
brute_force_crossings(SkSpan<Segment> segments)638 std::vector<Crossing> brute_force_crossings(SkSpan<Segment> segments) {
639 
640     auto isNonZeroSegment = [](const Segment& segment) {
641         return segment.upper() != segment.lower();
642     };
643     const auto zeroSegments = std::partition(segments.begin(), segments.end(), isNonZeroSegment);
644 
645     std::sort(segments.begin(), zeroSegments);
646 
647     const auto duplicateSegments = std::unique(segments.begin(), zeroSegments);
648 
649     SkSpan<const Segment> cleanSegments =
650             SkSpan{segments.data(), std::distance(segments.begin(), duplicateSegments)};
651 
652     CrossingAccumulator crossings;
653     if (cleanSegments.size() >= 2) {
654         for (auto i = cleanSegments.begin(); i != std::prev(cleanSegments.end()); ++i) {
655             for (auto j = std::next(i); j != cleanSegments.end(); ++j) {
656                 if (s0_intersects_s1(*i, *j)) {
657                     crossings.recordCrossing(*i, *j);
658                 }
659             }
660         }
661     }
662     return crossings.finishAndReleaseCrossings();
663 }
664 }  // namespace myers
665