Flutter Engine
The Flutter Engine
Classes | Typedefs | Functions
bentleyottmann Namespace Reference

Classes

struct  Cross
 
struct  Crossing
 
struct  Event
 
class  EventQueue
 
class  EventQueueInterface
 
class  EventQueueTestingPeer
 
struct  Int96
 
struct  Lower
 
struct  OrderBySlope
 
struct  Point
 
struct  Segment
 
class  SweepLine
 
class  SweepLineInterface
 
struct  SweepLineTestingPeer
 
struct  Upper
 
struct  Visitor
 

Typedefs

using EventType = std::variant< Lower, Cross, Upper >
 
using DeletionSegmentSet = std::set< Segment >
 
using InsertionSegmentSet = std::set< Segment, OrderBySlope >
 

Functions

std::optional< std::vector< Crossing > > bentley_ottmann_1 (SkSpan< const Segment > segments)
 
std::optional< std::vector< Crossing > > brute_force_crossings (SkSpan< const Segment > segments)
 
bool operator== (const Int96 &a, const Int96 &b)
 
bool operator< (const Int96 &a, const Int96 &b)
 
Int96 operator+ (const Int96 &a, const Int96 &b)
 
Int96 multiply (int64_t a, int32_t b)
 
Int96 multiply (int32_t a, int64_t b)
 
bool operator== (const Segment &s0, const Segment &s1)
 
bool operator< (const Segment &s0, const Segment &s1)
 
bool no_intersection_by_bounding_box (const Segment &s0, const Segment &s1)
 
std::optional< Pointintersect (const Segment &s0, const Segment &s1)
 
bool less_than_at (const Segment &s0, const Segment &s1, int32_t y)
 
bool point_less_than_segment_in_x (Point p, const Segment &segment)
 
bool rounded_point_less_than_segment_in_x_lower (const Segment &s, Point p)
 
bool rounded_point_less_than_segment_in_x_upper (const Segment &s, Point p)
 
int compare_slopes (const Segment &s0, const Segment &s1)
 
template<class... Ts>
 Visitor (Ts...) -> Visitor< Ts... >
 
bool operator< (const Point &p0, const Point &p1)
 
bool operator> (const Point &p0, const Point &p1)
 
bool operator>= (const Point &p0, const Point &p1)
 
bool operator<= (const Point &p0, const Point &p1)
 
bool operator== (const Point &p0, const Point &p1)
 
bool operator!= (const Point &p0, const Point &p1)
 

Typedef Documentation

◆ DeletionSegmentSet

using bentleyottmann::DeletionSegmentSet = typedef std::set<Segment>

Definition at line 31 of file EventQueueInterface.h.

◆ EventType

using bentleyottmann::EventType = typedef std::variant<Lower, Cross, Upper>

Definition at line 47 of file EventQueue.h.

◆ InsertionSegmentSet

Definition at line 38 of file EventQueueInterface.h.

Function Documentation

◆ bentley_ottmann_1()

std::optional< std::vector< Crossing > > bentleyottmann::bentley_ottmann_1 ( SkSpan< const Segment segments)

Definition at line 16 of file BentleyOttmann1.cpp.

16 {
17 if (auto possibleEQ = EventQueue::Make(segments)) {
18 EventQueue eventQueue = std::move(possibleEQ.value());
19 SweepLine sweepLine;
20 while(eventQueue.hasMoreEvents()) {
21 eventQueue.handleNextEventPoint(&sweepLine);
22 }
23 return eventQueue.crossings();
24 }
25 return std::nullopt;
26}
std::vector< Crossing > crossings()
Definition: EventQueue.cpp:125
void handleNextEventPoint(SweepLineInterface *handler)
Definition: EventQueue.cpp:63
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)

◆ brute_force_crossings()

std::optional< std::vector< Crossing > > bentleyottmann::brute_force_crossings ( SkSpan< const Segment segments)

Definition at line 12 of file BruteForceCrossings.cpp.

12 {
13 std::vector<Crossing> answer;
14 if (segments.size() >= 2) {
15 for (auto i0 = segments.begin(); i0 != segments.end() - 1; ++i0) {
16 for (auto i1 = i0 + 1; i1 != segments.end(); ++i1) {
17 if (auto possiblePoint = intersect(*i0, *i1)) {
18 answer.push_back({*i0, *i1, possiblePoint.value()});
19 }
20 }
21 }
22 }
23 return answer;
24}
static bool intersect(const SkPoint &p0, const SkPoint &n0, const SkPoint &p1, const SkPoint &n1, SkScalar *t)
constexpr T * begin() const
Definition: SkSpan_impl.h:90
constexpr T * end() const
Definition: SkSpan_impl.h:91
constexpr size_t size() const
Definition: SkSpan_impl.h:95

◆ compare_slopes()

int bentleyottmann::compare_slopes ( const Segment s0,
const Segment s1 
)

Definition at line 329 of file Segment.cpp.

329 {
330 Point s0Delta = s0.lower() - s0.upper(),
331 s1Delta = s1.lower() - s1.upper();
332
333 // Handle the horizontal cases to avoid dealing with infinities.
334 if (s0Delta.y == 0 || s1Delta.y == 0) {
335 if (s0Delta.y != 0) {
336 return -1;
337 } else if (s1Delta.y != 0) {
338 return 1;
339 } else {
340 return 0;
341 }
342 }
343
344 // Compare s0Delta.x / s0Delta.y ? s1Delta.x / s1Delta.y. I used the alternate slope form for
345 // two reasons.
346 // * no change of sign - since the delta ys are always positive, then I don't need to worry
347 // about the change in sign with the cross-multiply.
348 // * proper slope ordering - the slope monotonically increases from the smallest along the
349 // negative x-axis increasing counterclockwise to the largest along
350 // the positive x-axis.
351 int64_t lhs = SkToS64(s0Delta.x) * SkToS64(s1Delta.y),
352 rhs = SkToS64(s1Delta.x) * SkToS64(s0Delta.y);
353
354 if (lhs < rhs) {
355 return -1;
356 } else if (lhs > rhs) {
357 return 1;
358 } else {
359 return 0;
360 }
361}
constexpr int64_t SkToS64(S x)
Definition: SkTo.h:27
Point lower() const
Definition: Segment.cpp:20
Point upper() const
Definition: Segment.cpp:16

◆ intersect()

std::optional< Point > bentleyottmann::intersect ( const Segment s0,
const Segment s1 
)

Definition at line 97 of file Segment.cpp.

97 {
98
99 // Check if the bounds intersect.
101 return std::nullopt;
102 }
103
104 // Create the end Points for s0 and s1
105 const Point P0 = s0.upper(),
106 P1 = s0.lower(),
107 P2 = s1.upper(),
108 P3 = s1.lower();
109
110 if (P0 == P2 || P1 == P3 || P1 == P2 || P3 == P0) {
111 // Lines don't intersect if they share an end point.
112 return std::nullopt;
113 }
114
115 // Create the Q, R, and T.
116 const Point Q = P1 - P0,
117 R = P2 - P0,
118 T = P3 - P2;
119
120 // 64-bit cross product.
121 auto cross = [](const Point& v0, const Point& v1) {
122 int64_t x0 = SkToS64(v0.x),
123 y0 = SkToS64(v0.y),
124 x1 = SkToS64(v1.x),
125 y1 = SkToS64(v1.y);
126 return x0 * y1 - y0 * x1;
127 };
128
129 // Calculate the cross products needed for calculating s and t.
130 const int64_t QxR = cross(Q, R),
131 TxR = cross(T, R),
132 TxQ = cross(T, Q);
133
134 if (TxQ == 0) {
135 // Both t and s are either < 0 or > 1 because the denominator is 0.
136 return std::nullopt;
137 }
138
139 // t = (Q x R) / (T x Q). s = (T x R) / (T x Q). Check that t & s are on [0, 1]
140 if ((QxR ^ TxQ) < 0 || (TxR ^ TxQ) < 0) {
141 // The division is negative and t or s < 0.
142 return std::nullopt;
143 }
144
145 if (TxQ > 0) {
146 if (QxR > TxQ || TxR > TxQ) {
147 // t or s is greater than 1.
148 return std::nullopt;
149 }
150 } else {
151 if (QxR < TxQ || TxR < TxQ) {
152 // t or s is greater than 1.
153 return std::nullopt;
154 }
155 }
156
157 // Calculate the intersection using doubles.
158 // TODO: This is just a placeholder approximation for calculating x and y should use big math
159 // above.
160 const double t = static_cast<double>(QxR) / static_cast<double>(TxQ);
161 SkASSERT(0 <= t && t <= 1);
162 const int32_t x = std::round(t * (P3.x - P2.x) + P2.x),
163 y = std::round(t * (P3.y - P2.y) + P2.y);
164
165 return Point{x, y};
166}
static void round(SkPoint *p)
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define R(r)
double y
double x
bool no_intersection_by_bounding_box(const Segment &s0, const Segment &s1)
Definition: Segment.cpp:39
TPoint< Scalar > Point
Definition: point.h:322
int64_t cross(Point d0, Point d1)
Definition: Myers.cpp:55
#define T
Definition: precompiler.cc:65

◆ less_than_at()

bool bentleyottmann::less_than_at ( const Segment s0,
const Segment s1,
int32_t  y 
)

Definition at line 178 of file Segment.cpp.

178 {
179 auto [l0, t0, r0, b0] = s0.bounds();
180 auto [l1, t1, r1, b1] = s1.bounds();
181 SkASSERT(t0 <= y && y <= b0);
182 SkASSERT(t1 <= y && y <= b1);
183
184 // Return true if the bounding box of s0 is fully to the left of s1.
185 if (r0 < l1) {
186 return true;
187 }
188
189 // Return false if the bounding box of s0 is fully to the right of s1.
190 if (r1 < l0) {
191 return false;
192 }
193
194 // Check the x intercepts along the horizontal line at y.
195 // Make s0 be (x0, y0) -> (x1, y1) and s1 be (x2, y2) -> (x3, y3).
196 auto [x0, y0] = s0.upper();
197 auto [x1, y1] = s0.lower();
198 auto [x2, y2] = s1.upper();
199 auto [x3, y3] = s1.lower();
200
201 int64_t s0YDiff = y - y0,
202 s1YDiff = y - y2,
203 s0YDelta = y1 - y0,
204 s1YDelta = y3 - y2,
205 x0Offset = x0 * s0YDelta + s0YDiff * (x1 - x0),
206 x2Offset = x2 * s1YDelta + s1YDiff * (x3 - x2);
207
208 Int96 s0Factor = multiply(x0Offset, y3 - y2),
209 s1Factor = multiply(x2Offset, y1 - y0);
210
211 return s0Factor < s1Factor;
212}
Int96 multiply(int64_t a, int32_t b)
Definition: Int96.cpp:41
std::tuple< int32_t, int32_t, int32_t, int32_t > bounds() const
Definition: Segment.cpp:25

◆ multiply() [1/2]

Int96 bentleyottmann::multiply ( int32_t  a,
int64_t  b 
)

Definition at line 65 of file Int96.cpp.

65 {
66 return multiply(b, a);
67}
static bool b
struct MyStruct a[10]

◆ multiply() [2/2]

Int96 bentleyottmann::multiply ( int64_t  a,
int32_t  b 
)

Definition at line 41 of file Int96.cpp.

41 {
42 // Multiply the low 32-bits generating a 64-bit lower part.
43 uint64_t loA = a & 0xFFFFFFFF;
44 uint64_t loB = (uint32_t)b;
45 uint64_t newLo = loA * loB;
46
47 // Multiply the upper bits 32-bits of a and b resulting in the hi 64-bits of the Int96.
48 int64_t newHi = (a >> 32) * (int64_t)b;
49
50 // Calculate the overflow into the upper 32-bits.
51 // Remember that newLo is unsigned so will be zero filled by the shift.
52 int64_t lowOverflow = newLo >> 32;
53
54 // Add overflow from the low multiply into hi.
55 newHi += lowOverflow;
56
57 // Compensate for the negative b in the calculation of newLo by subtracting out 2^32 * a.
58 if (b < 0) {
59 newHi -= loA;
60 }
61
62 return {newHi, (uint32_t)newLo};
63}

◆ no_intersection_by_bounding_box()

bool bentleyottmann::no_intersection_by_bounding_box ( const Segment s0,
const Segment s1 
)

Definition at line 39 of file Segment.cpp.

39 {
40 auto [left0, top0, right0, bottom0] = s0.bounds();
41 auto [left1, top1, right1, bottom1] = s1.bounds();
42 // If the sides of the box touch, then there is no new intersection.
43 return right0 <= left1 || right1 <= left0 || bottom0 <= top1 || bottom1 <= top0;
44}

◆ operator!=()

bool bentleyottmann::operator!= ( const Point p0,
const Point p1 
)

Definition at line 32 of file Point.cpp.

32 {
33 return !(p0 == p1);
34}

◆ operator+()

Int96 bentleyottmann::operator+ ( const Int96 a,
const Int96 b 
)

Definition at line 27 of file Int96.cpp.

27 {
28 uint32_t lo = a.lo + b.lo;
29 int64_t carry = lo < a.lo;
30 int64_t hi = a.hi + b.hi + carry;
31 return {hi, lo};
32}

◆ operator<() [1/3]

bool bentleyottmann::operator< ( const Int96 a,
const Int96 b 
)

Definition at line 23 of file Int96.cpp.

23 {
24 return std::tie(a.hi, a.lo) < std::tie(b.hi, b.lo);
25}

◆ operator<() [2/3]

bool bentleyottmann::operator< ( const Point p0,
const Point p1 
)

Definition at line 12 of file Point.cpp.

12 {
13 return std::tie(p0.y, p0.x) < std::tie(p1.y, p1.x);
14}

◆ operator<() [3/3]

bool bentleyottmann::operator< ( const Segment s0,
const Segment s1 
)

Definition at line 35 of file Segment.cpp.

35 {
36 return std::make_tuple(s0.upper(), s0.lower()) < std::make_tuple(s1.upper(), s1.lower());
37}

◆ operator<=()

bool bentleyottmann::operator<= ( const Point p0,
const Point p1 
)

Definition at line 24 of file Point.cpp.

24 {
25 return !(p0 > p1);
26}

◆ operator==() [1/3]

bool bentleyottmann::operator== ( const Int96 a,
const Int96 b 
)

Definition at line 19 of file Int96.cpp.

19 {
20 return std::tie(a.hi, a.lo) == std::tie(b.hi, b.lo);
21}

◆ operator==() [2/3]

bool bentleyottmann::operator== ( const Point p0,
const Point p1 
)

Definition at line 28 of file Point.cpp.

28 {
29 return std::tie(p0.y, p0.x) == std::tie(p1.y, p1.x);
30}

◆ operator==() [3/3]

bool bentleyottmann::operator== ( const Segment s0,
const Segment s1 
)

Definition at line 31 of file Segment.cpp.

31 {
32 return s0.upper() == s1.upper() && s0.lower() == s1.lower();
33}

◆ operator>()

bool bentleyottmann::operator> ( const Point p0,
const Point p1 
)

Definition at line 16 of file Point.cpp.

16 {
17 return p1 < p0;
18}

◆ operator>=()

bool bentleyottmann::operator>= ( const Point p0,
const Point p1 
)

Definition at line 20 of file Point.cpp.

20 {
21 return !(p0 < p1);
22}

◆ point_less_than_segment_in_x()

bool bentleyottmann::point_less_than_segment_in_x ( Point  p,
const Segment segment 
)

Definition at line 214 of file Segment.cpp.

214 {
215 auto [l, t, r, b] = segment.bounds();
216
217 // Ensure that the segment intersects the horizontal sweep line
218 SkASSERT(t <= p.y && p.y <= b);
219
220 // Fast answers using bounding boxes.
221 if (p.x < l) {
222 return true;
223 } else if (p.x >= r) {
224 return false;
225 }
226
227 auto [x0, y0] = segment.upper();
228 auto [x1, y1] = segment.lower();
229 auto [x2, y2] = p;
230
231 // For a point and a segment the comparison is:
232 // x2 < x0 + (y2 - y0)(x1 - x0) / (y1 - y0)
233 // becomes
234 // (x2 - x0)(y1 - y0) < (x1 - x0)(y2 - y0)
235 // We don't need to worry about the signs changing in the cross multiply because (y1 - y0) is
236 // always positive. Manipulating a little further derives predicate 2 from "Robust Plane
237 // Sweep for Intersecting Segments" page 9.
238 // 0 < (x1 - x0)(y2 - y0) - (x2 - x0)(y1 - y0)
239 // becomes
240 // | x1-x0 x2-x0 |
241 // 0 < | y1-y0 y2-y0 |
242 return SkToS64(x2 - x0) * SkToS64(y1 - y0) < SkToS64(y2 - y0) * SkToS64(x1 - x0);
243}

◆ rounded_point_less_than_segment_in_x_lower()

bool bentleyottmann::rounded_point_less_than_segment_in_x_lower ( const Segment s,
Point  p 
)

Definition at line 248 of file Segment.cpp.

248 {
249 const auto [l, t, r, b] = s.bounds();
250 const auto [x, y] = p;
251
252 // Ensure that the segment intersects the horizontal sweep line
253 SkASSERT(t <= y && y <= b);
254
255 // In the comparisons below, x is really x - ½
256 if (r < x) {
257 // s is entirely < p.
258 return true;
259 } else if (x <= l) {
260 // s is entirely > p. This also handles vertical lines, so we don't have to handle them
261 // below.
262 return false;
263 }
264
265 const auto [x0, y0] = s.upper();
266 const auto [x1, y1] = s.lower();
267
268 // Horizontal - from the guards above we know that p is on s.
269 if (y0 == y1) {
270 return false;
271 }
272
273 // s is not horizontal or vertical.
274 SkASSERT(x0 != x1 && y0 != y1);
275
276 // Given the segment upper = (x0, y0) and lower = (x1, y1)
277 // x0 + (x1 - x0)(y - y0) / (y1 - y0) < x - ½
278 // (x1 - x0)(y - y0) / (y1 - y0) < x - x0 - ½
279 // Because (y1 - y0) is always positive we can multiply through the inequality without
280 // worrying about sign changes.
281 // (x1 - x0)(y - y0) < (x - x0 - ½)(y1 - y0)
282 // (x1 - x0)(y - y0) < ½(2x - 2x0 - 1)(y1 - y0)
283 // 2(x1 - x0)(y - y0) < (2(x - x0) - 1)(y1 - y0)
284 return 2 * SkToS64(x1 - x0) * SkToS64(y - y0) < (2 * SkToS64(x - x0) - 1) * SkToS64(y1 - y0);
285}
struct MyStruct s

◆ rounded_point_less_than_segment_in_x_upper()

bool bentleyottmann::rounded_point_less_than_segment_in_x_upper ( const Segment s,
Point  p 
)

Definition at line 290 of file Segment.cpp.

290 {
291 const auto [l, t, r, b] = s.bounds();
292 const auto [x, y] = p;
293
294 // Ensure that the segment intersects the horizontal sweep line
295 SkASSERT(t <= y && y <= b);
296
297 // In the comparisons below, x is really x + ½
298 if (r <= x) {
299 // s is entirely < p.
300 return true;
301 } else if (x < l) {
302 // s is entirely > p. This also handles vertical lines, so we don't have to handle them
303 // below.
304 return false;
305 }
306
307 const auto [x0, y0] = s.upper();
308 const auto [x1, y1] = s.lower();
309
310 // Horizontal - from the guards above we know that p is on s.
311 if (y0 == y1) {
312 return false;
313 }
314
315 // s is not horizontal or vertical.
316 SkASSERT(x0 != x1 && y0 != y1);
317
318 // Given the segment upper = (x0, y0) and lower = (x1, y1)
319 // x0 + (x1 - x0)(y - y0) / (y1 - y0) < x + ½
320 // (x1 - x0)(y - y0) / (y1 - y0) < x - x0 + ½
321 // Because (y1 - y0) is always positive we can multiply through the inequality without
322 // worrying about sign changes.
323 // (x1 - x0)(y - y0) < (x - x0 + ½)(y1 - y0)
324 // (x1 - x0)(y - y0) < ½(2x - 2x0 + 1)(y1 - y0)
325 // 2(x1 - x0)(y - y0) < (2(x - x0) + 1)(y1 - y0)
326 return 2 * SkToS64(x1 - x0) * SkToS64(y - y0) < (2 * SkToS64(x - x0) + 1) * SkToS64(y1 - y0);
327}

◆ Visitor()

template<class... Ts>
bentleyottmann::Visitor ( Ts...  ) -> Visitor< Ts... >