From 2d0bb80a52a67f494dee62b4c5d732c8d9aabdce Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 21:14:06 -0800 Subject: [PATCH] Area type: contours whose crossings round to one x keep the gap between them Two intervals at the middle of a band joined whenever their crossings came out at the same x, even when the two edges are not along one line and only round to that x. A slanted edge whose crossings of the band all round to the x of a separate contour's edge then let a line run across the gap between the contours. The crossings now carry the line they were found along. Intervals join at an x only when both crossings are along one line (contours that meet along an edge) or both lie exactly at that x. --- crates/text/src/layout/region.rs | 71 ++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/crates/text/src/layout/region.rs b/crates/text/src/layout/region.rs index 34c48942d5..7e9815fbfb 100644 --- a/crates/text/src/layout/region.rs +++ b/crates/text/src/layout/region.rs @@ -206,6 +206,10 @@ type Slant = (Point, Point, (f64, f64), (f64, f64)); /// The line through two points. type Line = (Point, Point); +/// The x at which an edge crosses a height, and the line it was found along (`None` when it is +/// the edge's end). +type Crossing = (f64, Option); + /// The x of the line through `p` and `q` (not horizontal) at `y`. fn line_x(p: Point, q: Point, y: f64) -> f64 { p.x + (y - p.y) / (q.y - p.y) * (q.x - p.x) @@ -367,20 +371,36 @@ fn band_intervals(polys: &[Vec], top: f64, bottom: f64) -> Vec<(f64, f64) let (edges, _, mut xs) = band_boundaries(polys, top, bottom); // The edges of contours that meet along an edge cross the middle of the band at the same x, // since each is crossed on the line through the same two points: the intervals on either side - // join there (an edge that bounds the shape there is still subtracted). Intervals with any gap + // join there (an edge that bounds the shape there is still subtracted). Crossings of other edges + // that round to the same x join only when both lie exactly at that x. Intervals with any gap // between them stay apart. - xs.sort_by(f64::total_cmp); - let mut mid: Vec<(f64, f64)> = xs.as_chunks::<2>().0.iter().map(|c| (c[0], c[1])).collect(); - mid.dedup_by(|next, prev| { - let seam = next.0 <= prev.1; - if seam { - prev.1 = prev.1.max(next.1); + let y = (top + bottom) * 0.5; + xs.sort_by(|a, b| a.0.total_cmp(&b.0).then_with(|| line_order(a.1, b.1))); + let mut mid: Vec<(f64, f64)> = Vec::new(); + let mut last: Option = None; + for &[(s, ks), (t, kt)] in xs.as_chunks::<2>().0 { + match mid.last_mut() { + Some(prev) if s <= prev.1 && ((ks.is_some() && last == ks) || (exactly_at(last, s, y) && exactly_at(ks, s, y))) => prev.1 = prev.1.max(t), + _ => mid.push((s, t)), } - seam - }); + last = kt; + } subtract_intervals(&mid, &edges) } +/// An order of the lines crossings of the band's middle were found along, which keeps crossings +/// along one line next to each other; a crossing at an edge's end (`None`) comes first. +fn line_order(a: Option, b: Option) -> std::cmp::Ordering { + let key = |l: Option| l.map_or([f64::NEG_INFINITY; 4], |(p, q)| [p.x, p.y, q.x, q.y]); + key(a).iter().zip(key(b)).fold(std::cmp::Ordering::Equal, |o, (u, v)| o.then(u.total_cmp(&v))) +} + +/// Does a crossing of the height `y` at `x`, found along `line` (or at an edge's end when `None`), +/// lie exactly at `x`? +fn exactly_at(line: Option, x: f64, y: f64) -> bool { + line.is_none_or(|(p, q)| on_line_exactly(p, q, Point::new(x, y))) +} + /// The x at which `polys` (even-odd) is inside somewhere in the band `top..bottom`, sorted and /// disjoint: the intervals at the middle of the band and every x at which an edge lies within the /// band. A vertical line at any other x meets no edge inside the band, so it is inside somewhere in @@ -423,17 +443,19 @@ fn band_cover_closed(polys: &[Vec], top: f64, bottom: f64) -> Vec<(f64, f /// the band, stretches of edges that an even number of edges cover, and vertical edges. The flag is /// false when [`MAX_EDGE_PAIRS`] left edges unpaired, which can only add to the result. Last, the x /// at which the edges cross the middle of the band (even-odd, as [`poly_intervals`] finds them), -/// unsorted, each found along the line [`slant_boundaries`] gives its edge. -fn band_boundaries(polys: &[Vec], top: f64, bottom: f64) -> (Vec<(f64, f64)>, bool, Vec) { +/// unsorted, each found along the line [`slant_boundaries`] gives its edge, with that line (`None` +/// for a crossing at an edge's end). +fn band_boundaries(polys: &[Vec], top: f64, bottom: f64) -> (Vec<(f64, f64)>, bool, Vec) { let (mut flats, mut slants) = band_edges(polys, top, bottom); let (mut edges, paired, lines) = slant_boundaries(&mut slants); let y = (top + bottom) * 0.5; - let mut xs: Vec = slants.iter().zip(&lines).filter(|((p, q, ..), _)| p.y <= y && y < q.y).map(|(_, &(p, q))| line_x(p, q, y)).collect(); + let mut xs: Vec = + slants.iter().zip(&lines).filter(|((p, q, ..), _)| p.y <= y && y < q.y).map(|(_, &(p, q))| (line_x(p, q, y), Some((p, q)))).collect(); // When the middle is the band's bottom (a band of no height), the edges that start there cross // it but are not in the band: they cross it at their lower end. if y == bottom { let starts = polys.iter().flat_map(|poly| poly.iter().zip(poly.iter().cycle().skip(1))); - xs.extend(starts.filter(|(a, b)| a.y != b.y && a.y.min(b.y) == y).map(|(a, b)| if a.y < b.y { a.x } else { b.x })); + xs.extend(starts.filter(|(a, b)| a.y != b.y && a.y.min(b.y) == y).map(|(a, b)| (if a.y < b.y { a.x } else { b.x }, None))); } // The x covered by an odd number of the horizontal edges at one height: pairs of their // sorted ends. @@ -1004,6 +1026,29 @@ fn shared_slanted_edge_still_joins_near_canvas_limit() { assert_spans(&cells[0].spans(top, bottom), &[(-100.0 + 3.0 * bottom / 7.0, 100.0 + 3.0 * top / 7.0)]); } +/// A slanted edge and a vertical edge of two contours less than one unit in the last place apart +/// near the edge of the canvas, whose crossings of the band's middle round to the same x, are not +/// one edge: the line stops at the gap, drawn either way around. +#[cfg(test)] +#[test] +fn contours_whose_crossings_round_together_stay_apart() { + let x = 3_999_000.0; + let u = 2f64.powi(-31); + assert_eq!((x + u).to_bits(), x.to_bits() + 1); + let left: &[(f64, f64)] = &[(x - 100.0, 0.0), (x, 0.0), (x + u, 10.0), (x - 100.0, 10.0)]; + let right: &[(f64, f64)] = &[(x + u, 0.0), (x + 100.0, 0.0), (x + 100.0, 10.0), (x + u, 10.0)]; + let mirror = |c: &[(f64, f64)]| -> Vec<(f64, f64)> { c.iter().map(|&(px, py)| (px, 10.0 - py)).collect() }; + let (left_m, right_m) = (mirror(left), mirror(right)); + for frame in [closed(&[left, right]), closed(&[&right_m, &left_m])] { + let cells = Region::cells(&frame, &LayoutOptions::default(), &[]); + for (top, bottom) in [(8.0, 9.0), (1.0, 2.0)] { + let spans = cells[0].spans(top, bottom); + assert_eq!(spans.len(), 2, "{top}..{bottom}: {spans:?}"); + assert_spans(&spans, &[(x - 100.0, x + u), (x + u, x + 100.0)]); + } + } +} + /// Area type in two slanted contours four units in the last place apart near the edge of the canvas /// sets no line across the gap between them. #[cfg(test)]