Dashes fitted to corners take a subpath with no segments

An HP-GL wedge of almost no radius drawn with an adaptive line type
(LT-1;EW0.000001,0,0;) leaves a lone moveto. Fitting the dashes to its
corners indexed the empty segment list and panicked while rendering. Such
a subpath now uses the exact plan, which has no runs to fit.

Found by import_fuzz::random_hpgl_instructions_never_panic.
This commit is contained in:
bflatastic
2026-11-13 00:47:30 +07:00
parent 2304b38f19
commit cfce336215
3 changed files with 25 additions and 0 deletions
+4
View File
@@ -134,6 +134,10 @@ impl Plan {
fn fitted(mut segs: Vec<PathSeg>, mut lens: Vec<f64>, closed: bool, period: f64) -> Self {
let n = segs.len();
// A subpath with no segments (a lone moveto) has no runs to fit.
if n == 0 {
return Self::exact(segs, lens, closed, period);
}
// Corners: where segments with length meet at a kink (zero-length ones have no direction).
let live: Vec<usize> = (0..n).filter(|&i| lens[i] > EPS).collect();
let mut corners: Vec<usize> = live.windows(2).filter(|w| kink(&segs[w[0]], &segs[w[1]])).map(|w| w[1]).collect();
+14
View File
@@ -37,6 +37,20 @@ fn near(a: f64, b: f64, tol: f64) -> bool {
// ---------------------------------------------------------------- dashes fitted to corners
/// A subpath with no segments (a lone moveto, as an HP-GL wedge of almost no radius gives) is
/// dashed fitted to corners without a panic, and the other subpaths keep their dashes.
#[test]
fn fitted_dashes_take_a_subpath_with_no_segments() {
let mut b = BezPath::new();
b.move_to((5.0, 5.0));
b.extend(line(100.0));
let d = dash(&b, &fitted(&[10.0, 5.0], 0.0)).expect("dashed");
assert!(moves(&d.path) > 1 && length(&d.path) > 0.0, "{:?}", d.path);
let mut lone = BezPath::new();
lone.move_to((0.0, 0.0));
assert!(dash(&lone, &fitted(&[10.0, 5.0], 0.0)).is_some_and(|d| d.path.elements().is_empty()));
}
#[test]
fn every_corner_of_a_rectangle_falls_mid_dash() {
// 100 × 50 with 12/6: the long sides hold 6 periods (scaled by 100/108), the short ones 3
+7
View File
@@ -2958,6 +2958,13 @@ fn the_hpgl_sample_opens() {
assert!(paths > 15, "{paths} paths, warnings {:?}", l.warnings);
}
/// A wedge drawn with the adaptive line type `LT-1` and a near-zero radius (a case the fuzz test
/// found) opens and places without a panic.
#[test]
fn an_adaptive_line_type_wedge_of_almost_no_radius_opens() {
survive_metafile("HP-GL tiny adaptive wedge", "x.plt", b"IN;SP1;LT-1;EW0.000001,0,0;").unwrap();
}
/// A random instruction: a mnemonic the reader plays (or one it doesn't) with hostile numbers.
fn arb_hpgl_instruction() -> impl Strategy<Value = String> {
let op = prop::sample::select(vec![