sweep.typ
1 #import "@preview/ctheorems:1.1.3": * 2 #import "@preview/cetz:0.4.0" 3 #import "@preview/lovelace:0.3.0": * 4 #set par(justify: true) 5 #set math.equation(numbering: "(1)") 6 7 #show: thmrules 8 #let lemma = thmbox("lemma", "Lemma") 9 #let def = thmbox("lemma", "Definition") 10 #let proof = thmproof("proof", "Proof") 11 #let remark = thmbox("lemma", "Remark") 12 13 #let inexact(term) = { 14 block(inset: 16pt, 15 block( 16 inset: 16pt, 17 stroke: 1pt + gray, 18 radius: 12pt, 19 text( 20 size: 10pt, 21 [Note for inexact arithmetic: #term] 22 ) 23 ) 24 ) 25 } 26 27 // TODO: figure out how to get rid of the ":" in the caption 28 #let invariant(term) = { 29 figure( 30 block(inset: 16pt, 31 text( 32 size: 10pt, 33 [#term] 34 ) 35 ), 36 kind: "invariant", 37 supplement: "invariant", 38 caption: "" 39 ) 40 } 41 42 #let fl = text([fl]) 43 44 We'll be talking about sweep line algorithms, where the sweep line is horizontal and increasing in $y$. 45 Therefore, every line segment "starts" at the coordinate with smaller $y$ and "ends" at the coordinate 46 with larger $y$ (we'll assume for now that there are no horizontal segments). We'll parametrize each 47 line segment as a function of $y$. So if $alpha: [y_0 (alpha), y_1 (alpha)] arrow bb(R)$ is a line segment 48 then $alpha(y)$ is the $x$ coordinate at $y$-coordinate $y in [y_0 (alpha), y_1 (alpha)]$. 49 We write $theta_alpha$ for the angle that $alpha$ makes with the positive horizontal axis. 50 Let's have a picture. (In the discussion, it won't matter whether positive $y$ points up or down, but in the 51 pictures we'll adopt the graphics convention of having positive $y$ point down.) 52 53 #figure( 54 cetz.canvas({ 55 import cetz.draw: * 56 57 line((1, 3), (0, 0), name: "a") 58 line((-4, 3), (4, 3), stroke: (dash: "dotted")) 59 line((-4, 0), (4, 0), stroke: (dash: "dotted")) 60 content((-4, 3), $y_0(alpha)$, anchor: "east") 61 content((-4, 0), $y_1(alpha)$, anchor: "east") 62 content((0.6, 1.5), $alpha$, anchor: "west") 63 64 cetz.angle.angle("a.start", "a.end", (4, 3), label: $theta_alpha$, label-radius: 0.8) 65 }), 66 67 caption: "A segment and its angle" 68 ) 69 70 We'll be dealing with inexact arithmetic, so let's define some "error bars" on our line segments. 71 For an error parameter $epsilon > 0$, offsetting from $alpha$ by $plus.minus epsilon$ in the perpendicular-to-$alpha$ direction 72 is the same as offsetting by $alpha plus.minus epsilon / (|sin theta_alpha|)$ in the horizontal direction. 73 Roughly speaking, the "error bars" on $alpha$ amount to adding this horizontal error. But we'll be slightly 74 more accurate around the corners, by truncating these error bars to the horizontal extents of $alpha$. Precisely, we define 75 76 $ 77 alpha_(+,epsilon)(y) = min(alpha(y) + epsilon / (|sin theta_alpha|), max(alpha(y_0), alpha(y_1)) + epsilon) \ 78 alpha_(-,epsilon)(y) = max(alpha(y) - epsilon / (|sin theta_alpha|), min(alpha(y_0), alpha(y_1)) - epsilon) \ 79 $ 80 81 In pictures, the gray shaded region is the region between $alpha_(-,epsilon)$ and $alpha_(+,epsilon)$: 82 The point of the scaling by $|sin theta_alpha|$ is to make this an approximation of an $epsilon$-neighborhood (in the 83 perpendicular direction) of the line segment. The truncation near the corners ensures that if $x$ is between 84 $alpha_(-,epsilon)(y)$ and $alpha_(+,epsilon)(y)$ then it is within $sqrt(2) epsilon$ of $alpha$. 85 86 #figure( 87 cetz.canvas({ 88 import cetz.draw: * 89 90 line((0.5, 3), (-0.3, 0.6), (-0.3, 0), (0.5, 0), (1.3, 2.4), (1.3, 3), close: true, fill: gray, stroke: 0pt) 91 line((0.5, 3), (-0.3, 0.6), (-0.3, 0), stroke: ( dash: "dashed" )) 92 line((0.5, 0), (1.3, 2.4), (1.3, 3), stroke: ( dash: "dashed" )) 93 94 line((1, 3), (0, 0), name: "a") 95 line((-4, 3), (4, 3), stroke: (dash: "dotted")) 96 line((-4, 0), (4, 0), stroke: (dash: "dotted")) 97 content((-4, 3), $y_0(alpha)$, anchor: "east") 98 content((-4, 0), $y_1(alpha)$, anchor: "east") 99 content((0.6, 1.5), $alpha$, anchor: "west") 100 101 content((0.8, 0.5), $alpha_(+,epsilon)$, anchor: "west") 102 content((0.2, 2.4), $alpha_(-,epsilon)$, anchor: "east") 103 }), 104 caption: "A segment and its error bars" 105 )<fig-error-bars> 106 107 108 Define a relation $prec_(y,epsilon)$ on line segments whose domain contains $y$, where 109 $alpha prec_(y,epsilon) beta$ if $alpha_(+,epsilon)(y) < beta_(-,epsilon)(y)$. 110 Intuitively, $alpha prec_(y,epsilon) beta$ if $alpha$ is definitely to the left of $beta$ 111 at height $y$: $alpha$ is far enough to the left that their error bars don't overlap. 112 Clearly, for a given $y$ and $epsilon$ there are three mutually exclusive possibilities: either 113 $alpha prec_(y,epsilon) beta$ or $beta prec_(y,epsilon) alpha$ or neither of the two holds. We'll denote 114 this third possibility by $alpha approx_(y,epsilon) beta$, and we write 115 $alpha prec.tilde_(y,epsilon) beta$ for "$alpha prec_(y,epsilon) beta$ or $alpha approx_(y,epsilon) beta$." 116 117 Here are a few basic properties of our definitions: 118 #lemma[ 119 1. For any $y$ and any $epsilon > 0$, $prec_(y,epsilon)$ is transitive: 120 if $alpha prec_(y,epsilon) beta$ 121 and $beta prec_(y,epsilon) gamma$ then $alpha prec_(y,epsilon) gamma$. (However, $prec.tilde_(y,epsilon)$ is not transitive.) 122 2. For any $y$ and any $epsilon > 0$, 123 if $alpha prec_(y,epsilon) beta$ 124 and $beta prec.tilde_(y,epsilon) gamma$ then $alpha prec.tilde_(y,epsilon) gamma$. 125 3. For any $y$ and any $epsilon > 0$, 126 if $alpha prec.tilde_(y,epsilon) beta$ 127 and $beta prec_(y,epsilon) gamma$ then $alpha prec.tilde_(y,epsilon) gamma$. 128 4. For any $y$, the relation $prec_(y,epsilon)$ is monotone in $epsilon$, in that if $alpha prec_(y,epsilon) beta$ then $alpha prec_(y,eta) beta$ for 129 any $eta in (0, epsilon)$. 130 ]<lem-basic-order-properties> 131 132 Since $epsilon$ for us will usually be fixed, we will often drop it from the notation, and write $alpha_-$ and $alpha_+$ instead 133 of $alpha_(-,epsilon)$ and $alpha_(+,epsilon)$. 134 135 #def[ 136 Suppose $alpha$ and $beta$ are two segments whose domain contains $y$. We say that *$(alpha, beta)$ 137 $epsilon$-cross by $y$* if $y$ belongs to both domains and $alpha succ_(y,epsilon) beta$. 138 We say that *$(alpha, beta)$ $epsilon$-cross* if they $epsilon$-cross by $min(y_1 (alpha), y_1 (beta))$. 139 140 When $epsilon$ is zero, we leave it out: we say that $(alpha, beta)$ cross by $y$ if they $0$-cross by $y$. 141 ] 142 143 Note that the definition of $epsilon$-crossing is not symmetric: $(alpha, beta)$ $epsilon$-crossing is 144 not the same as $(beta, alpha)$ $epsilon$-crossing. We will usually talk about $(alpha, beta)$ $epsilon$-crossing 145 in the context that $alpha$ starts off to the left of $beta$, and in this context "$(alpha, beta)$ $epsilon$-cross" means 146 that at some height before the end of $alpha$ and $beta$, $alpha$ has definitely crossed to the right of $beta$. 147 148 == Partially ordered sweep-lines 149 150 At our first pass, we won't try to detect intersections at all. Instead, we'll produce 151 a continuum of sweep-lines (constant except at a finite number of points) that *approximately* 152 track the horizontal order of the segments. 153 154 #def[ 155 The ordered collection $(alpha^1, ..., alpha^m)$ of line segments is #emph[$epsilon$-ordered at $y$] 156 if each $alpha^i$ has $y$ in its domain and $alpha^i prec.tilde_(y,epsilon) alpha^j$ for all $1 <= i < j <= m$. 157 ] 158 159 Our algorithm will produce a family of sweep-lines that are $epsilon$-ordered at every $y$ 160 (and also the sweep-lines will be #emph[complete] in the sense that the sweep-line at $y$ will contain all line segments whose 161 domain contains $y$). This seems weaker than finding all the intersections (for example, because if you 162 find all intersections you can use them to produce a completely ordered family of sweep-lines), but 163 in fact they're more-or-less equivalent: given weakly-ordered sweep-lines, you can perturb the lines so 164 that your weak order becomes the real order of the perturbed lines. 165 166 #lemma[ 167 If $(alpha^1, ..., alpha^m)$ is $epsilon$-ordered at $y$ then there exist $x^1 <= ... <= x^m$ such that 168 $alpha_(-,epsilon)^i (y) <= x^i <= alpha_(+,epsilon)^i (y)$ for all $i$. 169 ]<lem-horizontal-realization> 170 171 #proof[ 172 Define $x^i = max_(j <= i) alpha_(-,epsilon)^j (y)$. 173 Since we've included $j = i$, this clearly satisfies $x^i >= alpha_(-,epsilon)^j (y)$. 174 For the other inequality, the ordering condition implies that $alpha^j_(-,epsilon) (y) <= alpha^i_(+,epsilon) (y)$ 175 for every $j <= i$. Therefore this inequality still holds for the maximum over these $j$. 176 ] 177 178 One consequence of our approximate approach is that we need to do a little extra bookkeeping to maintain 179 the continuity of the input paths: when one segment exits and its path-neighbor enters, we need to remember 180 that they are connected because the approximate sweep-line might not keep them together. We'll ignore this 181 bookkeeping for now; 182 the goal here is to get into detail about the sweep-line invariants, and prove that we can maintain them. 183 184 == The sweep-line invariants 185 186 We're going to have a sweep-line that depends on $y$. When we need to emphasize this, we'll use the 187 cumbersome but explicit notation 188 $(alpha_y^1, ..., alpha_y^(m_y))$. 189 In addition to the sweep-line, we'll maintain a queue of "events." The events are ordered by $y$-position 190 and they are similar to the classical Bentley-Ottmann events so we'll skimp on the details here. There 191 is an "enter" event, an "exit" event, and an "intersection" event. 192 193 Our sweep-line will maintain two invariants: 194 + At every $y$, the sweep-line is $epsilon$-ordered at $y$. (We'll call this the "order" invariant.) 195 + For every $y$ and every $1 <= i < j <= m_y$, if $alpha_y^i$ and $alpha_y^j$ $epsilon$-cross 196 then the event queue contains an event between $i$ and $j$, 197 and at least one of these events occurs before the $epsilon$-crossing height, or at $y$. 198 (We'll call this the "crossing" invariant.) 199 200 (When we say the event queue contains an event between $i$ and $j$, we mean that either there's 201 an exit event for some $alpha^k$ with $i < k < j$ or there's an intersection event for some $alpha^k$ and $alpha^ell$ 202 with $i <= k < ell <= j$.) 203 204 Hopefully the first invariant is already well-motivated, so let's discuss the second. 205 To naively ensure that we find 206 all intersections, we could adopt a stronger crossing invariant that requires all pairs of intersecting segments 207 in the sweep-line to immediately put an intersection event in the queue. This would be inefficient to maintain because 208 it would require testing all pairs of segments. The main Bentley-Ottmann observation is that it's enough to have intersection 209 events for all sweep-line-adjacent pairs, because any pair of lines will have to become sweep-line adjacent strictly 210 before they intersect. We can't rely only on sweep-line adjacency because of the $epsilon$ fudge, but our "crossing" 211 event essentially encodes the same property. Note that if $j = i+1$ and $y$ is just before the $epsilon$-crossing height and 212 there are no other segments nearby, then the mysterious $j'$ must be either $i$ or $j$ (because there is nothing in between) 213 and the mysterious queue event must be the crossing of $i$ and $j$ (it couldn't be an exit event, because we assumed they 214 cross and they must cross before they exit). In other cases, the crossing event ensures that even if we haven't 215 recorded the upcoming crossing of $alpha^i$ and $alpha^j$, something will happen in between them that will give us 216 a chance to test their crossing. 217 218 == Sweeping the sweep line 219 220 The first observation is that the sweep line invariants are maintained as we increase $y$ continuously up 221 to the next event: 222 + For the order invariant, first note that there is an event whenever $y$ leaves the domain of a segment, so $y$ remains in all domains until the next event. 223 Moreover, if at any $y' > y$ the ordering breaks, two line segments must have $epsilon$-crossed one another by $y'$. 224 The third invariant guarantees that there's an event before this happens, so by the contra-positive until an event happens the ordering 225 constraint is maintained. 226 + The crossing invariant is maintained because the set of things to check (i.e. the set of line segments that $epsilon$-cross 227 one another after $y$) only shrinks as $y$ increases. 228 229 == Interaction with adjacent segments 230 231 In vanilla Bentley-Ottmann, each segment gets compared to its two sweep-line neighbors; they can either intersect or not intersect. 232 When numerical errors are taken into account, we may need to compare to 233 more segments. 234 235 #figure( 236 cetz.canvas({ 237 import cetz.draw: * 238 239 let calc_eps(x0, x1) = 0.5 * calc.sqrt(1 + calc.pow(calc.abs(x1 - x0) / 2, 2)) 240 241 let mkline(x0, x1) = { 242 let eps = calc_eps(x0, x1) 243 line((x0, 1), (x1, -1)) 244 line((x0 + eps, 1), (x1 + eps, -1), stroke: ( thickness: 0.2pt, dash: "dashed" )) 245 line((x0 - eps, 1), (x1 - eps, -1), stroke: ( thickness: 0.2pt, dash: "dashed" )) 246 } 247 248 let a0 = -8 249 let a1 = 8 250 let b0 = -2 251 let b1 = 2 252 let c0 = 0.5 253 let c1 = 0.5 254 255 content((a0, 1), $alpha^1$, anchor: "south", padding: 5pt) 256 content((b0, 1), $alpha^2$, anchor: "south", padding: 5pt) 257 content((c0, 1), $alpha^3$, anchor: "south", padding: 5pt) 258 259 mkline(a0, a1) 260 mkline(b0, b1) 261 mkline(c0, c1) 262 263 let a_eps = calc_eps(a0, a1) 264 let b_eps = calc_eps(b0, b1) 265 let c_eps = calc_eps(c0, c1) 266 let cross12 = 1 - 2*(b0 + b_eps - a0 + a_eps) / (b0 - a0 - (b1 - a1)) 267 let cross13 = 1 - 2*(c0 + c_eps - a0 + a_eps) / (c0 - a0 - (c1 - a1)) 268 269 line((-10, cross13), (10, cross13), stroke: ( thickness: 0.2pt )) 270 line((-10, cross12), (10, cross12), stroke: ( thickness: 0.2pt )) 271 }), 272 caption: [Three segments all mixed up. The upper horizontal line is the height at which $(alpha^1, alpha^3)$ $epsilon$-cross, 273 and the lower horizontal line is the height at which $(alpha^1, alpha^2)$ $epsilon$-cross.] 274 )<mixed-up-segments> 275 276 For example, in @mixed-up-segments imagine that $alpha^1$ and $alpha^2$ 277 are present in the sweep line, and we just added $alpha^3$. If we compare 278 $alpha^3$ to $alpha^2$, we'll see that the $epsilon$-cross and so we'll add an 279 intersection event between them sometime betfore they $epsilon$-cross. But if 280 that's all we do then we're in trouble, because by the time we come around to 281 looking at that intersection event $alpha^1$ may have already $epsilon$-crossed 282 $alpha^3$, breaking the ordering invariant. Note that $alpha^2$ doesn't 283 $epsilon$-cross $alpha^3$ at all in @mixed-up-segments, so there's no guarantee of 284 an intersection event between those two. 285 286 Fix $y$ and $epsilon$, and 287 suppose we have a collection of lines $(alpha^i, ..., alpha^n)$ that satisfy the ordering invariant 288 at $y$, and suppose also that $(alpha^(i+1), ..., alpha^n)$ 289 satisfy the crossing invariant at $y$. 290 To make the whole collection satisfy both invariants, we run the following algorithm. 291 We call this an *intersection scan to the right*. 292 293 #figure( 294 pseudocode-list([ 295 + *for* $j = i+1$ up to $n$ 296 + #line-label(<w-def>) let $w^j$ be the smallest height of any event between $i$ and $j$ 297 298 + #line-label(<crossing-test>) *if* $(alpha^i, alpha^j_(+,epsilon))$ cross by $w^j$ 299 + choose $z$ before the crossing, such that $alpha^i approx_(z,epsilon) alpha^j$ 300 + insert an intersection event for $(alpha^i, alpha^j)$ at $z$ 301 302 + #line-label(<protect>) *if* $alpha^i (z) <= alpha^j_-(z)$ for all $z in [y, w^j]$ 303 + *break* 304 ]), 305 caption: "Intersection scan to the right" 306 ) 307 308 #inexact[ 309 The test at @protect can be seen as an early-stopping optimization, and is not necessary for correctness. 310 In particular, an approximation with no false positives 311 is also fine. 312 ] 313 314 #lemma[ 315 Suppose that $(alpha^i, ..., alpha^n)$ satisfy the ordering invariant 316 at $y$, and suppose also that $(alpha^(i+1), ..., alpha^n)$ 317 satisfy the crossing invariant at $y$. 318 After running an intersection scan to the right, 319 $(alpha^i, ..., alpha^n)$ satisfy the crossing invariant at $y$. 320 321 In fact, $(alpha^i, ..., alpha^n)$ satisfy a slightly stronger crossing invariant at $y$: for every $j > i$, 322 if $(alpha^i, alpha^j_(+,epsilon))$ cross then the event queue contains an event between $i$ and $j$, and before 323 the crossing height. 324 ]<lem-intersection-scan> 325 326 (The special thing about the stronger crossing invariant is that it asks whether 327 $(alpha^i, alpha^j_(+,epsilon))$ cross, where the usual crossing invariant asks 328 whether 329 $(alpha^i_(-,epsilon), alpha^j_(+,epsilon))$ cross.) 330 331 #proof[ 332 It suffices to check the stronger crossing invariant. 333 So take some $k > i$ 334 that $(alpha^i, alpha^k_(+,epsilon))$ cross. We consider two cases: whether or not the loop terminated 335 before reaching $k$. 336 337 - Suppose the loop terminated at some $j < k$. If $(alpha^i, alpha^k_(+,epsilon))$ cross 338 after $w^j$, then the definition of $w^j$ ensures that there is an event between $i$ and $j$ (and therefore between 339 $i$ and $k$) before the crossing. On the other hand, the termination condition ensures that 340 $alpha^i (z) <= alpha^j_-(z)$ until $w^j$, and so if $(alpha^i, alpha^k_(+,epsilon))$ cross before $w^j$ then 341 also $(alpha^j, alpha^k)$ cross before that. In this case, the crossing invariant for $(alpha^(i+1), ..., alpha^n)$ 342 implies the existence of an event between $j$ and $k$ (and therefore between $i$ and $k$) before the crossing. 343 - If the loop included the case $j = k$, we break into two more cases: 344 - If $(alpha^i, alpha^k_(+,epsilon))$ cross by $w^j$, then the algorithm inserted a witnessing event between $i$ and $j$. 345 - Otherwise, the definition of $w^j$ ensures that there is an event between $i$ and $j$ before the crossing. 346 ] 347 348 #remark[ 349 We can tweak the algorithm a little to try and reduce the number of comparisons. 350 For a start, if we add an intersection event between $i$ and $j$ then we can 351 set $w^j$ to $z$, the height of that new event (because $z$ is the new smallest 352 height of any event between $i$ and $j$). 353 Therefore, if we broaden the test @crossing-test to check whether $(alpha^i, 354 alpha^j_-)$ cross, and we choose $z$ to be before the crossing of $(alpha^i, 355 alpha^j_-)$ then we can skip the test at @protect and terminate straight away. 356 Thus, there is no loop at all: we only need to consider $j = i + 1$. 357 358 One potential issue with this is finding a height $z$ with $alpha^i approx_(z,epsilon) alpha^j$ 359 that's before $(alpha^i, alpha^j_-)$ cross; in other words, we want a height after $(alpha^i_+, alpha^j_-)$ 360 cross but before $(alpha^i, alpha^j_-)$ cross. If $alpha^j$ is almost horizontal, this window might be very 361 small (or maybe even not representable with our restricted precision). 362 It may still be worthwhile to have a fast path with a single comparison, and a slow path with a loop. 363 ] 364 365 As you might have already guessed, we can also intersection scan to the left; it's pretty much a reflection 366 of the other one. 367 368 #figure( 369 pseudocode-list([ 370 + *for* $j = i$ down to $1$ 371 + let $w^j$ be the smallest height of any event between $j$ and $i$ 372 373 + *if* $(alpha^j_(-,epsilon), alpha^i)$ cross by $w^j$ 374 + choose $z$ before the crossing, such that $alpha^j approx_(z,epsilon) alpha^i$ 375 + insert an intersection event for $(alpha^j, alpha^i)$ at $z$ 376 377 + *if* $alpha^j_+ (z) <= alpha^i (z)$ for all $z in [y, w^j]$ 378 + *break* 379 ]), 380 caption: "Intersection scan to the left" 381 ) 382 383 We'll skip the proof of the following lemma, because it's basically the same as the last one. 384 385 #lemma[ 386 Suppose that $(alpha^1, ..., alpha^i)$ satisfy the ordering invariant 387 at $y$, and suppose also that $(alpha^1, ..., alpha^(i+1))$ 388 satisfy the crossing invariant at $y$. 389 After running an intersection scan to the left, 390 $(alpha^1, ..., alpha^(i+1))$ satisfy a slightly stronger crossing invariant at $y$: for every $j <= i$, 391 if $(alpha^j_(-,epsilon), alpha^(i+1))$ cross then the event queue contains an event between $j$ and $i+1$, and before 392 the crossing height. 393 ]<lem-intersection-scan-left> 394 395 The purpose of the stronger crossing invariant comes in the next lemma, which deals with scanning in both directions 396 and allows the insertion of a segment in the middle of a sweep-line. 397 398 #lemma[ 399 Suppose that $(alpha^1, ..., alpha^n)$ satisfy the ordering invariant at $y$, and suppose that 400 $(alpha^1, ..., alpha^i)$ and $(alpha^(i+1), ..., alpha^n)$ satisfy the crossing invariant at $y$. 401 Let $beta$ be another segment and assume that $(alpha^1, ..., alpha^i, beta, alpha^(i+1), ... alpha^n)$ 402 satisfy the ordering invariant at $y$. After running an intersection scan to the left and an intersection 403 scan to the right from $beta$, 404 $(alpha^1, ..., alpha^i, beta, alpha^(i+1), ... alpha^n)$ satisfies the crossing invariant at $y$. 405 ]<lem-intersection-scan-bidirectional> 406 407 #proof[ 408 @lem-intersection-scan implies that $(beta, alpha^(i+1), dots, alpha^n)$ satisfies the crossing invariant, 409 and 410 @lem-intersection-scan-left implies that $(alpha^1, ..., alpha^i, beta)$ does also. It only remains 411 to consider interactions between a segment before $beta$ and one after. So fix $j <= i < k$ and suppose 412 that $(alpha^j, alpha^k)$ $epsilon$-cross. If they $epsilon$-cross after $y_1(beta)$ then $beta$ exit 413 event witnesses the crossing invariant, so assume that $(alpha^j, alpha^k)$ $epsilon$-cross by $y_1(beta)$. 414 Then $(alpha^j_-, alpha^k_+)$ cross by $y_1(beta)$, and so one of them crosses $beta$ before $(alpha^j, alpha^k)$ $epsilon$-cross. 415 If $(alpha^j_-, beta)$ cross then @lem-intersection-scan-left implies that there is an event between $alpha^j$ and $beta$ (and 416 therefore between $alpha^j$) and $alpha^k$ before the crossing height; otherwise, $(beta, alpha^k_+)$ cross 417 and so @lem-intersection-scan provides the required event. 418 ] 419 420 One last observation in this section, that follows trivially from the algorithm: 421 422 #lemma[ 423 If an intersection scan inserts an intersection event for $(alpha, beta)$ 424 then the intersection event's height $z$ satisfies $alpha approx_(z,epsilon) beta$. 425 ]<lem-valid-intersection-events> 426 427 == An "enter" event 428 429 When inserting a new segment into the current sweep-line, we first choose its sweep-line position using 430 a binary search on its horizontal coordinate. Let's write $(alpha^1, dots, alpha^m)$ for the sweep-line 431 before inserting the new segment, and let's call the new segment $beta$. First, we observe that 432 there is a place to insert the new segment while preserving the ordering invariant. 433 434 #lemma[ 435 Suppose $(alpha^1, ..., alpha^m)$ is $epsilon$-ordered at $y$, and let $i$ be the largest $j$ for which 436 $alpha^j prec_(y,epsilon) beta$. Then 437 $(alpha^1, ..., alpha^i, beta, alpha^(i+1), ..., alpha^m)$ is $epsilon$-ordered at $y$. 438 (Here, we can allow the corner cases $i = 0$ and $i = m$ by declaring that 439 "$alpha^0$" is a vertical line at $x = -infinity$ and "$alpha^(m+1)$" is a vertical line at $x = infinity$). 440 ]<lem-insert-preserving-order> 441 442 #proof[ 443 Since $(alpha^1, ..., alpha^m)$ was $epsilon$-ordered at $y$, it suffices to compare beta with all $alpha^j$. 444 For $i + 1 <= j <= m$, our choice of $i$ immediately implies that $alpha^j succ.tilde_(y,epsilon) beta$. 445 So consider $1 <= j <= i$. Since $(alpha^1, ..., alpha^m)$ is $epsilon$-ordered, $alpha^j prec.tilde_(y,epsilon) alpha^i$. 446 Since $alpha^i prec_(y,epsilon) beta$, @lem-basic-order-properties implies that $alpha^j prec.tilde_(y,epsilon) beta$. 447 ] 448 449 #inexact[ 450 @lem-insert-preserving-order guarantees the existence of an insertion point, but it doesn't say how to 451 find it efficiently. 452 But consider a predicate $f(alpha^j)$ that returns true whenever $alpha^j prec_(y,epsilon) beta$, and 453 false whenever $alpha^j_+(y) > beta_+(y)$. Running a binary search with this predicate will find some $i$ 454 for which $f(alpha^i)$ is true and $f(alpha^(i+1))$ is false. By scanning to the right from there, we can 455 find the largest such $i$. 456 457 This $i$ is at least as large as the $i$ in @lem-insert-preserving-order, so 458 to check that it's a valid insertion point we only need to check that it isn't too large. So if $1 <= j <= i$ then 459 $alpha^j prec.tilde_(y,epsilon) alpha^i$ and so $alpha^j_-(y) <= alpha^i_+(y)$. On the other hand, $f(alpha^i)$ 460 was true and so $alpha^i_+ <= beta_+(y)$. Putting these together shows that $alpha^j prec.tilde_(y,epsilon) beta$. 461 462 This algorithm can be implemented with approximate arithmetic, and its running time is logarithmic in the 463 total length of the sweep line, plus linear in the number of elements that are very close to $beta$. 464 ] 465 466 @lem-insert-preserving-order implies that we can insert a new segment while preserving the ordering invariant. By 467 @lem-intersection-scan-bidirectional, running an intersection scan restores the crossing invariant. 468 Thus, we can insert a new segment while preserving the sweep-line invariants. 469 470 == An "exit" event 471 472 When a segments exits the sweep-line, the ordering invariant clearly doesn't break. 473 Regarding the crossing invariant, it can only break because of $epsilon$-crossing pairs whose 474 crossing invariant was witnessed by the exit event that was just processed. 475 To restore the crossing invariant, we need to enqueue some new intersection events. 476 477 Let $(alpha^1, ..., alpha^m)$ be the sweep-line after removing the just-exited segment 478 which, we assume, used to live between $alpha^i$ and $alpha^(i+1)$. Note that both 479 $(alpha^1, ..., alpha^i)$ and $(alpha^(i+1), ..., alpha^n)$ satisfy the crossing invariant. 480 By @lem-intersection-scan-bidirectional, running an intersection scan from $alpha^i$ in both 481 directions restores the crossing invariant. (Technically, this isn't covered by the statement 482 of @lem-intersection-scan-bidirectional -- which involves a new segment $beta$ -- but the proof 483 is basically the same.) 484 485 == An "intersection" event 486 487 Suppose our sweep-line is $(alpha^1, ..., alpha^m)$ and we've just encountered an intersection event for $(alpha^i, alpha^j)$ 488 at height $y$. 489 If $i > j$ then they've already been swapped in our sweep-line, so we don't need to swap them again. If $i < j$, we need to 490 swap them. According to @lem-valid-intersection-events, $alpha^j prec.tilde_(y,epsilon) alpha^i$. It seems reasonable, therefore, 491 to reorder the sweep line by putting $alpha^i$ after $alpha^j$, like 492 493 $ 494 alpha^1, ..., alpha^(i-1), alpha^(i+1), ..., alpha^j, alpha^i, alpha^(j+1), ... alpha^n. 495 $ 496 497 The issue with this is that $prec.tilde_(y,epsilon)$ is that it's changed the order of pairs other than $alpha^i$ and $alpha^j$: 498 for every $i < k < j$, the ordering between $alpha^i$ and $alpha^k$ has been swapped. If $prec.tilde$ were transitive, this would 499 be fine. But it isn't. 500 501 To fix this issue, we allow $alpha^i$ to "push" some of the intermediate segments along with it. It's a bit tedious to write (and read) 502 this precisely, so hopefully this description is enough: for each $k$ between $i$ and $j$, if $alpha^i prec_(y,epsilon) alpha^k$ then 503 we also move $alpha^k$ after $alpha^j$. Also, we preserve the relative order of all segments moved in this way. 504 To see that this "pushing" maintains the ordering invariants, note that by definition it preserves the ordering for all comparisons 505 with $alpha^i$: if some $alpha^ell$ was pushed along with $alpha^i$ then their relative orders haven't changed; and if $alpha^ell$ 506 wasn't pushed then $alpha^ell prec.tilde_(y,epsilon) alpha^i$ and the new order is fine. 507 508 What about other pairs? If $alpha^k$ and $alpha^ell$ 509 changed orders then one of them ($alpha^ell$, say) was pushed and the other wasn't. Then $alpha^i prec_(y,epsilon) alpha^ell$ 510 by our choice of the pushed segments, and $alpha^k prec.tilde_(y,epsilon) alpha^i$ by the previous paragraph. Putting these 511 together, @lem-basic-order-properties implies that $alpha^k prec.tilde_(y,epsilon) alpha^ell$ and so the new order is ok. 512 513 #inexact[ 514 We might not be able to tell exactly which segments $alpha^k$ between $alpha^i$ and $alpha^j$ satisfy 515 $alpha^i prec_(y,epsilon) alpha^k$. Fortunately, we can push a few too many segments while still maintaining 516 correctness: it suffices to 517 include all segments $alpha^k succ_(y,epsilon) alpha^i$, while also ensuring that we only include $alpha^k$ for which 518 $alpha^k_+(y) >= alpha^i_+(y)$. 519 ] 520 521 Finally, we need to maintain the crossing invariant. We can probably be more efficient about this, but one 522 way to be correct is to treat the various swappings as a bunch of deletions from the sweep-line followed by 523 a bunch of insertions into the sweep-line. We have already shown that running an intersection scan 524 after each insertion and deletion correctly maintains the crossing invariant, so we can just do that. 525 526 = Correctness of the output 527 528 We've described how to turn a bunch of segments into a continuum of sweep-lines, but we probably actually wanted 529 to find the segments' intersection points. Let's define exactly what that means and how to get there. 530 We'll assume that we start with one or more polylines that may or may not be closed. Equivalently, each segment 531 comes with an orientation and (optionally) a "following" segment whose starting point is the ending point of the 532 current segment. 533 Our goal is to subdivide all segments at all intersection points, meaning that for each segment we'll produce 534 a polyline; we'll call the segments in each such polyline "output segments," to distinguish them from 535 the original segments. We require three properties: 536 537 - "approximation": for each input segment, every vertex in its output polyline must be within $epsilon$ of the original segment. 538 Also the first and last vertices in the output polyline must be within $epsilon$ of the start and end of the segment, respectively. 539 - "continuity": if a segment has a following segment, then the last vertex in its output polyline must coincide 540 with the first vertex of the following segment's output polyline. 541 - "completeness": output segments intersect only at their endpoints, unless they are identical. 542 543 In the solution we describe, we'll achieve both continuity and the second half 544 of approximation by insisting that output polylines have exactly the same start- 545 and endpoints as their input segment. This stronger condition makes the algorithm 546 simpler, but it also subjectively can hurt output "quality" by requiring more segments. 547 548 == The "dense" version 549 550 Suppose we've already found a weakly-ordered sweep line at every height. We say 551 a height is "important" if the sweep line changed at that height, either because 552 some segments entered or exited, or because the order changed. The naive version 553 of our subdivision algorithm will subdivide every segment at every important 554 height. Note that because the sweep line changes at a discrete set of heights, 555 at every important height $y$ we actually have two weakly-ordered sweep line: 556 the "old" one from infinitesmally before $y$ height and the "new" one from $y$ 557 onwards. Because everything is continuous and the weak ordering conditions are 558 closed, both the old and new sweep lines satisfy the weak ordering condi0.2tions 559 at $y$. 560 561 The algorithm goes like this: at every important height we use 562 @lem-horizontal-realization to assign horizontal positions to segments in the 563 old sweep line; we subdivide each segment at the resulting coordinate. Then we 564 use @lem-horizontal-realization again to assign horizontal positions to segments 565 in the new sweep line. If a segment gets assigned two different horizontal positions 566 by the old and new sweep lines, we add a horizontal segment between them. Also, 567 if a segment starts or ends at the current height and its assigned position is not the 568 same as its starting or ending position, we fix that up with another horizontal segment. 569 Finally, we subdivide all horizontal segments as necessary to ensure that they only 570 intersect at endpoints. 571 572 I think it's pretty clear that this algorithm is correct. The approximation property 573 holds (with $sqrt 2 epsilon$ instead of $epsilon$, but that doesn't matter) because 574 @lem-horizontal-realization always puts a new point within its segment's upper and lower bounds, 575 and the bounds are chosen to be within $sqrt 2 epsilon$ of the segment; see @fig-error-bars. 576 As mentioned above, the "continuity" property and the other half of "approximation" follow 577 because we insisted on including the segment endpoints exactly. 578 579 For the "completeness" property, let's focus on the bit between the important heights 580 (because everything gets subdivided at the important heights, and so maintaining the 581 completeness property there is just a matter of splitting into enough horizontal segments). 582 Between two important heights, the sweep line is constant, so the new sweep line 583 at the old height is the same as the old sweep line at the new height. Thus, all output 584 subsegments have the same order when leaving the old sweep line as they do when entering 585 the new sweep line. Therefore any two of them that intersect between the two important 586 heights must be identical. 587 588 == The "sparse" version 589 590 We'd like to avoid subdividing every segment at every important height. This basically 591 involves detecting which segments need to be divided and ignoring the rest. It's implemented 592 but not yet written up (TODO). 593 594 == A more detailed insertion algorithm 595 596 It took me a few tries to get the approximate insertion algorithm working, so 597 I figured it was worth writing up a few more details. First of all, a slight 598 tweak of @lem-insert-preserving-order shows that there is a position $i$ such 599 that $alpha^j_- (y) <= beta(y)$ for all $j <= i$ and $alpha^j_+ (y) >= beta(y)$ 600 for all $j > i$. We will look for this $i$, but with some slight slack in our 601 arithmetic. The slack will be small enough to guarantee we find a position that 602 satisfies the weaker properties of @lem-insert-preserving-order. 603 604 First, we consider a predicate $p(j)$ that returns `false` whenever $alpha^j_+ 605 (y) > beta(y)$. We put no conditions on the values of $j$ for which $p(j)$ 606 returns `true`; a tighter predicate will give a more efficient algorithm, but 607 doesn't affect correctness. Let $i_-$ be such that $p(i_-) = #true$ and $p(i_- 608 + 1) = #false$ (where we allow $i_- = -1$, and handle the boundary cases by 609 declaring that $p(-1) = #true$ and $p(m+1) = #false)$. Note that $i_-$ can be 610 found with a binary search. 611 612 The definition of $p$ ensures that $alpha^(i_-)_+(y) <= beta(y)$. Under the 613 assumption that $(alpha^1, ..., alpha^m)$ is weakly ordered, it follows that 614 for every $j <= i_-$, $alpha^j_-(y) <= beta(y)$. That is, the $i$ that we are 615 looking for is larger than or equal to $i_-$. 616 617 The next step is a linear scan up from $i_-$. Let $q(j)$ be a predicate 618 that returns `true` whenever $alpha^j_- (y) < beta^j (y)$ and `false` whenever 619 $alpha^j_- (y) > beta^j_+ (y)$. Take $i >= i_-$ to be the index just before $q$ 620 first returns false; we will insert $beta$ after $alpha^i$. 621 622 Let's check that this insertion position is valid: for every $j <= i$ we have 623 either $j <= i_-$ or $i_- < j <= i$. In the first case, we already observed 624 that $alpha^j_-(y) <= beta(y)$; in the second case, $q(j)$ returned `true` and 625 so $alpha^j_-(y) <= beta^j^+ (y)$. In either case, $alpha^j prec.tilde beta$. 626 On the other hand, $q(i+i)$ being `false` implies that $alpha^(i+1)_- (y) >= 627 beta(y)$. This implies that for all $j >= i+1$, $alpha^j_+ (y) >= beta(y)$ and 628 so $alpha^j succ.tilde_y beta$. 629 630 = Accuracy analysis 631 632 We're going to implement some algorithms in floating point. Suppose $p$ is the number of mantissa bits 633 of the floating-point type, and let $fl(t)$ denote the rounding function. Let $epsilon_0 = 2^(-1-p)$; 634 this is the relative error of the rounding function for all normal numbers: for a real number $x$, 635 as long as $fl(x)$ is finite and non-subnormal, 636 $ 637 fl(x) = x plus.minus epsilon_0 |x|. 638 $ 639 Now, if the result of floating point addition or subtraction is subnormal then it is exact; therefore, 640 for any floating-point $x$ and $y$, if $x - y$ and $x + y$ don't overflow then after rounding they 641 have relative error at most $epsilon_0$. 642 643 #lemma[ 644 If $y_0 <= y <= y_1$ are floating-point numbers and $epsilon_0 <= 1/2$ then computing $(y - y_0) / (y_1 - y_0)$ 645 the naive way gives an absolute error of 646 at most $3 epsilon_0 + 4 epsilon_0^2$. 647 ]<lem-ratio-error> 648 649 #proof[ 650 The assumption $y_0 <= y <= y_1$ ensures that $y - y_0$ and $y_1 - y_0$ do not overflow. Therefore 651 652 $ 653 fl(y - y_0) &= (y - y_0) (1 plus.minus epsilon_0) \ 654 fl(y + y_0) &= (y + y_0) (1 plus.minus epsilon_0) 655 $ 656 and so 657 $ 658 fl(y - y_0) / 659 fl(y_1 - y_0) = (y - y_0) / (y_1 - y_0) times 660 (1 plus.minus epsilon_0) / (1 plus.minus epsilon_0). 661 $ 662 For the upper bound, 663 $ 664 (1 + epsilon_0) / (1 - epsilon_0) = (1 + epsilon_0) (1 + epsilon_0 + epsilon_0^2 / (1 - epsilon_0)) 665 = 1 + 2 epsilon_0 + 2 / (1 - epsilon_0) epsilon_0^2 <= 1 + 2 epsilon_0 + 4 epsilon_0^2. 666 $ 667 For the lower bound, 668 $ 669 (1 - epsilon_0) / (1 + epsilon_0) = 1 - epsilon_0 - (epsilon_0 (1-epsilon_0)) / (1+epsilon_0) >= 1 - 2 epsilon_0. 670 $ 671 Putting these together, 672 $ 673 fl(y - y_0) / 674 fl(y_1 - y_0) = (y - y_0) / (y_1 - y_0) (1 plus.minus (2 epsilon_0 + 4 epsilon_0^2)). 675 $ 676 Since $0 <= (y - y_0) / (y_1 - y_0) <= 1$, this relative error is also an absolute error: 677 $ 678 fl(y - y_0) / 679 fl(y_1 - y_0) = (y - y_0) / (y_1 - y_0) plus.minus (2 epsilon_0 + 4 epsilon_0^2). 680 $ 681 The final rounding step adds another absolute error of at most $epsilon_0$. 682 ] 683 684 #lemma[ 685 Suppose that $x_0$, $x_1$, and $M$ are floating-point numbers with $|x_0|, |x_1| <= M/2$. 686 Let $0 <= t <= 1$ be a real number, and let $0 <= hat(t) <= 1$ be a floating point number 687 with $|t - hat(t)| <= epsilon$. Then computing $x_0 + t (x_1 - x_0)$ the obvious way gives an 688 absolute error of at most $(3 epsilon_0 + epsilon) M$. 689 ]<lem-convex-combination-error> 690 691 #proof[ 692 Let's first consider the error introduced by approximating $t (x_1 - x_0)$ with $hat(t) fl(x_1 - x_0)$: 693 $ 694 hat(t) fl(x_1 - x_0) = hat(t) (x_1 - x_0)(1 plus.minus epsilon_0) = hat(t) (x_1 - x_0) plus.minus epsilon_0 M, 695 $ 696 where the second approximation follows because $0 <= hat(t) <= 1$ and $|x_1 - x_0| <= M$. 697 Since $hat(t) = t plus.minus epsilon$, we continue with 698 $ 699 hat(t) fl(x_1 - x_0) 700 = (t plus.minus epsilon) (x_1 - x_0) plus.minus epsilon_0 M, 701 = t (x_1 - x_0) plus.minus (epsilon_0 + epsilon) M. 702 $ 703 Rounding the left hand side introduces a relative error of at most $epsilon_0$, which is an absolute error of at most $M epsilon_0$. Therefore, 704 $ 705 fl(hat(t) fl(x_1 - x_0)) 706 = t (x_1 - x_0) plus.minus (2 epsilon_0 + epsilon) M. 707 $ 708 709 Finally, we subtract both sides from $x_0$ and round one more time. This last rounding introduces a relative error of at most $epsilon_0$, 710 which again leads to an absolute error of at most $2 epsilon_0 M$ (and I think maybe the 2 is unnecessary, but let's not worry). 711 ] 712 713 Next, let's analyze the computation of the $y$ intersection point. 714 715 #lemma[ 716 Suppose we have two segments $alpha$ and $beta$. Set $y_0 = y_0(alpha) or y_0(beta)$ and $y_1 = y_1(alpha) and y_1(beta)$ 717 (these can be computed exactly). Suppose that we can compute $alpha(y)$ and $beta(y)$ to additive accuracy $epsilon$, 718 and assume that $beta(y_0) - alpha(y_0) >= 0$ and 719 $ 720 alpha(y_1) - beta(y_1) > 16/3 (epsilon + 2 epsilon_0 M). 721 $ 722 Define $t = (alpha(y_1) - beta(y_1)) / (alpha(y_1) - beta(y_1) + beta(y_0) - alpha(y_0)$ and set $y = y_0 + t(y_1 - y_0)$. The 723 natural way to compute $y$ produces an approximation $hat(y)$ satisfying 724 $ 725 |alpha(hat(y)) - beta(hat(y))| <= 4(epsilon + 2 epsilon_0 M) (1 + max(1/m_alpha, 1/m_beta)), 726 $ 727 where $m_alpha$ and $m_beta$ are the slopes of $alpha$ and $beta$. 728 ]<lem-intersection-height-error> 729 730 Basically, this says we can find a good approximate crossing height as long as the two segments cross by at least $16/3 (epsilon + 2 epsilon_0 M)$. 731 732 #proof[ 733 First, note that at least one of $alpha(y_0)$ or $beta(y_0)$ is calculated exactly; similarly for $alpha(y_1)$ and $beta(y_1)$. 734 Therefore the absolute error in calculating $alpha(y_1) - beta(y_1)$ is at most $epsilon + epsilon_0 M$ (where the $epsilon_0 M$ comes 735 from rounding after the summation) and the absolute error in calculating 736 $(alpha(y_1) - beta(y_1)) + (beta(y_0) - alpha(y_0))$ is at most $2 epsilon + 3 epsilon_0 M$. 737 These errors aren't independent, though, since both terms involve a computation of $alpha(y_1) - beta(y_1)$. 738 Specifically, let $c = alpha(y_1) - beta(y_1)$ and let $d = (alpha(y_1) - beta(y_1)) + (beta(y_0) - alpha(y_0))$; suppose 739 our calculation of $c$ has error $epsilon_c$ and our calculation of $d$ has error $epsilon_d$. Then 740 741 $ 742 |epsilon_c - epsilon_d| <= epsilon + 2 epsilon_0 M, 743 $ <eq-error-cancellation> 744 745 because this is the *additional* error introduced into $d$ by computing $beta(y_0) - alpha(y_0)$ and adding it to the other term. 746 747 Now we consider the quotient 748 749 $ 750 (c + epsilon_c) / (d + epsilon_d) 751 &= c / (d (1 + epsilon_d/d)) + epsilon_c / (d (1 + epsilon_d/d)) \ 752 &= c / d (1 - epsilon_d/d + (epsilon_d/d)^2 / (1 + epsilon_d/d)) + epsilon_c / d (1 - (epsilon_d/d)/(1 + epsilon_d / d)). 753 $ 754 755 We've assumed that $1 + epsilon_d/d >= 1/2$, and so the error 756 $ 757 abs((c + epsilon_c) / (d + epsilon_d) - c/d) 758 $ 759 is at most 760 $ 761 & abs(c / d ( - epsilon_d/d + (epsilon_d/d)^2 / (1 + epsilon_d/d)) + epsilon_c / d (1 - (epsilon_d/d)/(1 + epsilon_d / d))) \ 762 & <= abs(- c / d epsilon_d/d + epsilon_c / d ) + 2 c/d (epsilon_d/d)^2 + 2 abs(epsilon_c epsilon_d) / d^2 \ 763 & <= c/d abs(epsilon_c/d - epsilon_d/d) + (1 - c/d) abs(epsilon_c/d) + 2 (epsilon_d/d)^2 + 2 abs(epsilon_c epsilon_d) / d^2, 764 $ 765 where in the last line we recall that $0 <= c/d <= 1$. Now, recall from @eq-error-cancellation and the paragraph before it 766 that both $abs(epsilon_c)$ and $abs(epsilon_c - epsilon_d)$ are bounded by $epsilon + 2 epsilon_0 M$, and that 767 $abs(epsilon_d)$ is bounded by $2 epsilon + 3 epsilon_0 M$. Therefore, our computation of $c/d$ (which, recall, we called $t$ above) has absolute error of at most 768 769 $ 770 (epsilon + 2 epsilon_0 M)/d + 4 (2 epsilon + 3 epsilon_0 M)^2 / d^2. 771 $ 772 773 Let's call this expression $epsilon_t$. 774 775 By @lem-convex-combination-error, the computation of $y = y_0 + t (y_1 - y_0)$ has absolute error at most $(2 epsilon_0 + epsilon_t) |y_1 - y_0| + epsilon_0 M$. 776 Let's see what this means for the horizontal accuracy of $alpha(y)$ and $beta(y)$. We can write 777 $ 778 alpha(y) = alpha(y_0) + (y - y_0) / (y_1 - y_0) (alpha(y_1) - alpha(y_0)), 779 $ 780 and similarly for $beta(y)$. Combining these and recalling our definitions of $c$ and $d$, 781 $ 782 alpha(y) - beta(y) = -c + (y - y_0) / (y_1 - y_0) d. 783 $ 784 For the true value of $y$, this is zero of course. With our error bound of $(2 epsilon_0 + epsilon_t) |y_1 - y_0| + epsilon_0 M$ on our 785 approximation of $y$ (let's call it $hat(y)$), 786 $ 787 abs(alpha(hat(y)) - beta(hat(y))) <= (2 epsilon_0 + epsilon_t) d + (epsilon_0 M) / (y_1 - y_0) d 788 <= epsilon_t d + 3 epsilon_0 (M d) / (y_1 - y_0). 789 $ 790 791 If we assume that $16 (epsilon + 2 epsilon_0 M) / d <= 3$ then $epsilon_t d <= 4 epsilon + 8 epsilon_0 M$. 792 Since $d/(y_1 - y_1)$ is the difference of the inverse-slopes of $alpha$ and $beta$, it's bounded by twice 793 the maximum of these inverse-slopes. 794 ] 795 796 Putting all this together (and handling the "chamfers" yet): 797 #lemma[ 798 Assume all line segments are inside the square $[-M/2, M/2]^2$, 799 and take $delta = 64 epsilon_0 M$. If $epsilon_0 <= 1/4$ then 800 801 - We can compute $x$ coordinates of line segments to additive accuracy $delta/8$. 802 - If $alpha$ $(3 delta)/4$-crosses $beta$, we can compute a crossing height $hat(y)$ such 803 that 804 $ 805 |alpha(hat(y)) - beta(hat(y))| <= (9 delta) /16 (1 + max(1/m_alpha, 1/m_beta)). 806 $ 807 ] 808 809 #proof[ 810 We compute $x$ coordinates by first computing $t = (y - y_0) / (y_1 - y_0)$ (with error at most $3 epsilon_0 + 4 epsilon_0^2$ by 811 @lem-ratio-error). Then we apply @lem-convex-combination-error with $epsilon = 3 epsilon_0 + 4 epsilon_0^2$ to see that we 812 can compute the horizontal coordinate with error at most $(6 epsilon_0 + 4 epsilon_0^2)M$. If $epsilon_0 <= 1/4$ as we assumed, 813 this is at most $7 epsilon_0 M <= delta/8$. This proves the first claim. 814 815 For the second claim, we apply @lem-intersection-height-error with $epsilon = (6 epsilon_0 + 4 epsilon_0^2) M$. 816 With this $epsilon$, $epsilon + 2 epsilon_0 M) <= 9 epsilon_0 M$ and so $16/3 (epsilon + 2 epsilon_0 M) <= 48 epsilon_0 M = (3 delta)/4$. 817 Thus, if $alpha$ $(3 delta)/4$-crosses $beta$ then the assumptions of @lem-intersection-height-error are satisfied 818 (and in the conclusion, $4 (epsilon + 2 epsilon_0) M <= 36 epsilon_0 M = (9 delta) /16 $. 819 ] 820 821 = Curves etc. 822 823 Let's move on from lines and talk about curves. Each curve is of the form $alpha: [0, 1] -> bb(R)^2$, which we'll sometimes 824 in components as $alpha_0: [0, 1] -> bb(R)$ and $alpha_1: [0, 1] -> bb(R)$. We'll assume that each $alpha_0$ is a strictly 825 increasing function. Write $S = [-1, 1] times [-1, 1]$ for the unit square in $bb(R)^2$. For a curve $alpha$ and a set $B subset bb(R)^2$, we write 826 $alpha + B$ for the set 827 828 $ 829 alpha + B = {(x, y) in bb(R)^2 : exists t in [0, 1] "with" (x, y) - alpha(t) in B} 830 $ 831 832 In other words, $alpha + B$ is the Minkowski sum between $B$ and the locus of $alpha$. For any set $B subset bb(R)^2$, 833 let 834 835 $ 836 B_y = {x in bb(R) : (x, y) in B}. 837 $ 838 839 For example, here is a picture of $(alpha + epsilon B)_y$: (TODO) 840 841 == The curve orders 842 843 When moving to curves, it seems useful to work with more abstract orders. For fixed $epsilon$ and $y$, we'll define 844 three of them: the "weak pre-order," the "strong order," and the "weak order." 845 846 #let weakprelt = sym.lt.tri 847 #let weakpreleq = sym.lt.eq.tri 848 #let notweakprelt = sym.lt.tri.not 849 #let weakpreeq = sym.eq.dots 850 #let weaklt = sym.lt 851 #let stronglt = sym.lt.double 852 #let weaklt = sym.lt 853 #let weakeq = sym.tilde 854 855 We begin with the weak pre-order. 856 857 #def[ 858 For each $epsilon >= 0$ and $y in bb(R)$, let $weakprelt_(y,epsilon)$ be a relation satisfying: 859 860 - if $alpha weakprelt_(y,epsilon) beta$ then $(alpha + epsilon/2 S)_y <= (beta + epsilon/2 S)_y$, 861 - if $(alpha + epsilon S)_y <= (beta + epsilon S)_y$ then $alpha weakprelt_(y,epsilon) beta$, 862 - for any $k >= 1$, there are no $alpha_1, ..., alpha_k$ with $alpha_1 weakprelt_(y,epsilon) alpha_2 weakprelt_(y,epsilon) dots.h.c weakprelt_(y,epsilon) alpha_k weakprelt_(y,epsilon) alpha_1$. 863 864 We call this last condition the "no cycles" condition. 865 ] 866 867 Note that we *do not* assume transitivity: $alpha weakprelt_(y,epsilon) beta 868 weakprelt_(y,epsilon) gamma$ does not imply that $alpha weakprelt_(y,epsilon) 869 gamma$. We also do not assume anti-symmetry: it's possible to have neither 870 $alpha weakprelt_(y,epsilon) beta$ nor $beta weakprelt_(y,epsilon) alpha$. In 871 fact, if $(alpha + epsilon/2)_y$ and $(beta + epsilon/2 S)_y$ overlap, it is 872 guaranteed that neither of these orderings hold. 873 874 #def[ 875 If neither $alpha weakprelt_(y,epsilon) beta$ nor $beta weakprelt_(y,epsilon) alpha$, we say that 876 877 $ 878 alpha weakpreeq_(y,epsilon) beta. 879 $ 880 881 If either $alpha weakprelt_(y,epsilon) beta$ or 882 $alpha weakpreeq_(y,epsilon) beta$, we write 883 $alpha weakpreleq_(y,epsilon) beta$. Equivalently, 884 $alpha weakpreleq_(y,epsilon) beta$ is the negation of 885 $beta weakprelt_(y,epsilon) alpha$. 886 ] 887 888 TODO: draw a picture. 889 890 The strong order is similar to the weak pre-order, but with bigger epsilons. 891 #def[ 892 For each $epsilon >= 0$ and $y in bb(R)$, let $stronglt_(y,epsilon)$ be a relation satisfying: 893 894 - if $alpha stronglt_(y,epsilon) beta$ then $(alpha + 2 epsilon S)_y <= (beta + 2 epsilon S)_y$, 895 - if $(alpha + 3 epsilon S)_y <= (beta + 3 epsilon S)_y$ then $alpha stronglt_(y,epsilon) beta$, 896 - for any $k >= 1$, there are no $alpha_1, ..., alpha_k$ with $alpha_1 stronglt_(y,epsilon) alpha_2 stronglt_(y,epsilon) dots.h.c stronglt_(y,epsilon) alpha_k stronglt_(y,epsilon) alpha_1$. 897 ] 898 899 The definition of the weak order is a little strange at first. The initial idea 900 is that we want to ensure that our sweep-line is ordered by the weak pre-order: 901 we are going to try to ensure that if our sweep-line at $y$ is $alpha^1, ..., alpha^n$ 902 then for all $i < j$, $alpha^i weakpreleq_y alpha^j$. Or in other words, we want to 903 ensure that whenever $alpha^j$ is definitely to the right of $alpha^i$, it comes after $alpha^i$ 904 in the sweep-line. 905 906 For sweep-line algorithms to be efficient, we need to avoid too many segment comparisons. 907 In the classical Bentley-Ottmann algorithm with exact arithmetic this is easy: whenever 908 we look for intersections against segment $alpha^i$, we compare it with its sweep-line 909 neighbors to the left and right (so, $alpha^(i-1)$ and $alpha^(i+1)$ in our notation). 910 Inexact arithmetic will make this a little trickier; we will typically need to look further 911 than just the immediate neighbors of $alpha^i$. However, we will need *some* mechanism 912 for early stopping, some way to say that after we have compared $alpha^i$ to, say, $alpha^(i-k)$ 913 through $alpha^(i-1)$, we can be sure that no other $alpha^j$ (for $j < i - k$) needs 914 to be checked. The intuitive idea is that if $alpha^(i-k)$ is very far to the left of $alpha^i$ 915 then we can stop, because then $alpha^(i-k)$ will "shield" $alpha^i$ from segments further 916 left, in much the same way that its immediate neighbors "shield" $alpha^i$ in the classical 917 sweep-line algorithm. 918 919 We would like to use the strong order $stronglt_y$ to check whether $alpha^(i-k)$ is "very far 920 to the left" of $alpha^i$, but we run into trouble with examples like this: 921 922 TODO: picture. 923 924 Here, we clearly see that $alpha$ is to the left of $beta$; in situations like this, if we're 925 examining $beta$ for potential intersections and we see $alpha$ to its left, we'd really like 926 to stop looking further left. But $gamma$ is a problem: it's almost horizontal, and the 927 sweep-line at $y$ is at an ambiguous height, being more than $epsilon / 2$ but less than $epsilon$ below $gamma$. 928 According to the definition of our weak pre-order, 929 $alpha weakprelt_y gamma$ and $alpha weakpreeq_y gamma$ are 930 both allowed, as are both 931 $beta weakprelt_y gamma$ and $beta weakpreeq_y gamma$. So imagine that $alpha weakpreeq_y gamma$ and $beta weakprelt_y gamma$. 932 Before adding $beta$, the sweep-line could have been $(gamma, alpha)$. Then we could have tried to add $beta$ after $alpha$ 933 (because clearly $beta$ is to the right of $alpha$), leading to the sweep-line $(gamma, alpha, beta)$. 934 Scanning for any intersections with the newly-added $beta$ won't find any because we'll look to the left, see 935 $alpha$, and stop. The result of all this is the illegal sweep-line $(gamma, alpha, beta)$, in which 936 $beta weakprelt_y gamma$ but $gamma$ comes first in the sweep-line. 937 938 Basically, it seems hard to have both early stopping (which we want for efficiency) and the 939 ordering invariants that we need for correctness. Our "fix" is to change the definition of 940 our ordering (while still keeping it tight enough for the correctness guarantees that we need). 941 It looks almost like cheating, because we're going to redefine it precisely so that our early 942 stopping will work. 943 944 #def[ 945 Let $cal(S)_y$ be the set of all segments whose vertical range contains $y$. Say that $alpha weaklt_y beta$ 946 if $alpha weakprelt_y beta$ and there do not exist $alpha', beta' in cal(S)_y$ with $beta weakpreleq_y beta' stronglt alpha' weakpreleq_y alpha$. 947 ]<def-weak-order> 948 949 In other words, we define $weaklt$ to be the same as $weakprelt$, but if there's ever a situation where the (not-yet-specified) 950 early stopping algorithm could miss a wrongly ordered pair $alpha weakprelt beta$ by witnessing a gap between $beta'$ and $alpha'$, 951 then we weaken the $weakprelt$ order and declare instead that $alpha weakeq beta$ instead of $alpha weaklt beta$. 952 To see how this solves our tricky example above, note that when instantiating @def-weak-order with $alpha = beta$, $beta = gamma$, 953 $beta' = alpha$, and $alpha' = beta$, we see that there *do* exist $alpha', beta' in cal(S)_y$ with the required properties, 954 and so in fact $beta$ and $gamma$ are not strictly ordered in the weak order $weaklt$ even though they were strictly ordered 955 in the weak pre-order $weakprelt$. In particular, the sweep-line $(gamma, alpha, beta)$ is legal with respect to the weak order $weaklt$. 956 957 You might object to @def-weak-order because of how hilariously inefficient it will be to implement. The 958 clever part, though, is that we won't need to implement it. Our algorithm will make its decisions based 959 only on the weak pre-order $weakprelt$ and the strong order $stronglt$. The inefficient weak order $weaklt$ 960 will only be used in the correctness proof.