Flutter Engine
The Flutter Engine
Myers.cpp
Go to the documentation of this file.
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
5
10
11#include <algorithm>
12#include <climits>
13#include <cstdint>
14#include <iterator>
15#include <tuple>
16#include <utility>
17#include <vector>
18
19namespace myers {
20
21// -- Point ----------------------------------------------------------------------------------------
22Point operator-(const Point& p0, const Point& p1) {
23 return {p0.x - p1.x, p0.y - p1.y};
24}
25
26std::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 --------------------------------------------------------------------------------------
31const Point& Segment::upper() const {
32 return fUpper;
33}
34
35const Point& Segment::lower() const {
36 return fLower;
37}
38
39std::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
45 return fUpper.y == fLower.y;
46}
47
48bool Segment::isVertical() const {
49 return fUpper.x == fLower.x;
50}
51
52// Return:
53// | d0x d1x |
54// | d0y d1y |
55int64_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.
67int64_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.
109bool 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.
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.
158bool 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)
166bool 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.
217struct Event {
218 const int32_t y;
219
220 // The set of segments that begin at y.
222
223 // The set of segments that are horizontal on y.
225
226 // The set of segments that end on y.
228};
229
230// -- EventQueue -----------------------------------------------------------------------------------
231// The EventQueue produces Events. Events are never added to the queue after initial creation.
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;
240 Iterator(const EventQueue& eventQueue, size_t index)
241 : fEventQueue{eventQueue}
242 , fIndex{index} { }
243 Iterator(const Iterator& that) : Iterator{ that.fEventQueue, that.fIndex } { }
244 Iterator& operator++() { ++fIndex; return *this; }
245 Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
246 bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
247 bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
248 value_type operator*() { return fEventQueue[fIndex]; }
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
271public:
272 // Given a list of segments make an EventQueue, and populate its queue with events.
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.
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
380 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};
385 horizontal{&fSegmentStorage[endOfBegin], endOfHorizontal - endOfBegin};
386 SkSpan<const Segment> end{&fSegmentStorage[endOfHorizontal], endOfEnd - endOfHorizontal};
387 return Event{y, begin, horizontal, end};
388 }
389
390 Iterator begin() const {
391 return Iterator{*this, 0};
392 }
393
394 Iterator end() const {
395 return Iterator{*this, fEvents.size()};
396 }
397
398 size_t size() const {
399 return fEvents.size();
400 }
401
402 bool empty() const {
403 return fEvents.empty();
404 }
405
406private:
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.
419public:
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
434 std::vector<Crossing> finishAndReleaseCrossings() {
435 return std::move(fCrossings);
436 }
437
438private:
439 std::vector<Crossing> fCrossings;
440};
441
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
446public:
448 fStatus.push_back(kLeftStatusSentinel);
449 fStatus.push_back(kRightStatusSentinel);
450 }
451
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
463 std::vector<Crossing> finishAndReleaseCrossings() {
464 // Only the sentinels should be left.
465 SkASSERT(this->statusEmpty());
466 return fCrossings.finishAndReleaseCrossings();
467 }
468
469private:
470 using StatusLine = std::vector<Segment>;
471
472 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.
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>
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.
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,
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.
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,
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.
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
565std::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.
577bool 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
638std::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
GrAATriangulator::Event Event
static float next(float f)
static float prev(float f)
#define check(reporter, ref, unref, make, kill)
Definition: RefCntTest.cpp:85
#define SkASSERT(cond)
Definition: SkAssert.h:116
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
constexpr int64_t SkToS64(S x)
Definition: SkTo.h:27
GLenum type
constexpr T * data() const
Definition: SkSpan_impl.h:94
constexpr T * begin() const
Definition: SkSpan_impl.h:90
constexpr T * end() const
Definition: SkSpan_impl.h:91
constexpr bool empty() const
Definition: SkSpan_impl.h:96
constexpr size_t size() const
Definition: SkSpan_impl.h:95
void recordCrossing(const Segment &s0, const Segment &s1)
Definition: Myers.cpp:420
std::vector< Crossing > finishAndReleaseCrossings()
Definition: Myers.cpp:434
static EventQueue Make(const SkSpan< const Segment > segments)
Definition: Myers.cpp:273
Event operator[](size_t i) const
Definition: Myers.cpp:380
Iterator begin() const
Definition: Myers.cpp:390
bool empty() const
Definition: Myers.cpp:402
size_t size() const
Definition: Myers.cpp:398
Iterator end() const
Definition: Myers.cpp:394
const Point & upper() const
Definition: Myers.cpp:31
std::tuple< int32_t, int32_t, int32_t, int32_t > bounds() const
Definition: Myers.cpp:39
bool isVertical() const
Definition: Myers.cpp:48
bool isHorizontal() const
Definition: Myers.cpp:44
const Point & lower() const
Definition: Myers.cpp:35
std::vector< Crossing > finishAndReleaseCrossings()
Definition: Myers.cpp:463
void handleEvent(Event e)
Definition: Myers.cpp:452
struct MyStruct s
FlKeyEvent * event
double y
Int96 multiply(int64_t a, int32_t b)
Definition: Int96.cpp:41
std::variant< Lower, Cross, Upper > EventType
Definition: EventQueue.h:47
constexpr Color operator*(T value, const Color &c)
Definition: color.h:911
Definition: Contour.h:18
std::tuple< int64_t, int64_t > point_to_s64(Point p)
Definition: Myers.cpp:26
bool s0_less_than_s1_at_y(const Segment &s0, const Segment &s1, int32_t y)
Definition: Myers.cpp:166
bool s0_intersects_s1(const Segment &s0, const Segment &s1)
Definition: Myers.cpp:577
Point operator-(const Point &p0, const Point &p1)
Definition: Myers.cpp:22
std::vector< Crossing > brute_force_crossings(SkSpan< Segment >)
Definition: Myers.cpp:638
int64_t cross(Point d0, Point d1)
Definition: Myers.cpp:55
int64_t compare_slopes(const Segment &s0, const Segment &s1)
Definition: Myers.cpp:67
constexpr bool operator!=(const Point &p0, const Point &p1)
Definition: Myers.h:33
bool slope_s0_less_than_slope_s1(const Segment &s0, const Segment &s1)
Definition: Myers.cpp:109
std::vector< Crossing > myers_find_crossings(const SkSpan< const Segment > segments)
Definition: Myers.cpp:565
int64_t compare_point_to_segment(Point p, const Segment &s)
Definition: Myers.cpp:117
bool segment_less_than_upper_to_insert(const Segment &segment, const Segment &to_insert)
Definition: Myers.cpp:158
constexpr bool operator==(const Point &p0, const Point &p1)
Definition: Myers.h:29
def remove(*paths)
Definition: ref_ptr.h:256
int compare(const void *untyped_lhs, const void *untyped_rhs)
Definition: skdiff.h:161
SkSpan< const Segment > horizontal
Definition: Myers.cpp:224
SkSpan< const Segment > end
Definition: Myers.cpp:227
const int32_t y
Definition: Myers.cpp:218
SkSpan< const Segment > begin
Definition: Myers.cpp:221
int32_t x
Definition: Myers.h:20
int32_t y
Definition: Myers.h:21