Text cuts: the 2D prism route takes a font's curves exactly (#1429)

The 2D prism route for text cuts takes a font's curves exactly instead of as polylines.
This commit is contained in:
phanoob authored and GitHub committed 2026-10-15 13:32:02 -04:00
1 parent f0c5bfcdf2
commit 3047aa60cd
6 files changed
+647 -62

No files matched your search

+28 -2
View File
@@ -80,8 +80,17 @@ impl EdgeSpec {
EdgeSpec::Line { .. } => None,
EdgeSpec::Arc { mid, .. } => Some(key(*mid)),
EdgeSpec::Chain { ctrl } => {
let n = ctrl.len().max(1) as f64;
Some(key(ctrl.iter().fold(Vec3::ZERO, |s, p| s + *p) * (1.0 / n)))
// Summed in pairs from both ends, so that the chain run either way gives the very
// same point: a font's control points are simple fractions, whose mean can fall
// exactly halfway between two steps of the key, where summing in another order
// would round it to the other one.
let n = ctrl.len();
let sum = (0..n.div_ceil(2)).fold(Vec3::ZERO, |s, i| match (ctrl.get(i), ctrl.get(n - 1 - i)) {
(Some(p), Some(q)) if i != n - 1 - i => s + (*p + *q),
(Some(p), _) => s + *p,
_ => s,
});
Some(key(sum * (1.0 / n.max(1) as f64)))
}
}
}
@@ -952,6 +961,23 @@ mod tests {
vec![quad(a, d, c, b), quad(e, f, g, h), quad(a, b, f, e), quad(b, c, g, f), quad(c, d, h, g), quad(d, a, e, h)]
}
/// A chain's key is the same whichever way it runs. These control points (a joining stroke
/// of a script font) have their mean exactly halfway between two steps of the key, and
/// summed in order one way and the other they rounded to different steps: a wall and the cap
/// beside it, reading the curve opposite ways, got two edges and the solid did not close.
#[test]
fn a_chain_has_one_key_both_ways() {
let p = |x: f64, y: f64| Vec3::new(x, y, 40.0);
let ctrl = [
p(34.74439583333333, 11.397236111111111),
p(35.066196759259256, 11.722652777777778),
p(35.32833796296296, 11.885361111111111),
p(35.53081944444444, 11.885361111111111),
];
let forward = EdgeSpec::Chain { ctrl: ctrl.to_vec().into() };
assert_eq!(forward.mid_key(), forward.reversed().mid_key());
}
#[test]
fn disconnected_closed_faces_keep_every_material_component() {
let mut faces = box_faces(Vec3::ZERO, Vec3::new(2.0, 3.0, 4.0));