mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 05:07:51 -06:00
pathops: a huge stroke width with round joins no longer panics Outline Stroke
normalize_bez ran the sweep without the catch_unwind guard that Sweep::new has, so an outline_stroke width of 1e200 or more with a round join panicked inside linesweeper (Left == Ish). The panic is now a Degenerate error and the art is left unchanged. Adds hostile-number tests for the pathops edit, stroke and offset functions.
This commit is contained in:
@@ -656,8 +656,10 @@ pub(crate) fn normalize_bez(bp: &BezPath, rule: FillRule) -> Result<Contours, Pa
|
||||
if !crate::budget::sweep_fits([&bp]) {
|
||||
return Err(PathOpsError::TooComplex);
|
||||
}
|
||||
let top = Topology::<i32>::from_path(&bp, eps).map_err(|_| PathOpsError::OpenPath)?;
|
||||
Ok(top.contours(|w| inside(rule, *w)))
|
||||
// As in `Sweep::new`, a panic in the sweep on near-degenerate input becomes an error.
|
||||
let swept =
|
||||
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| Topology::<i32>::from_path(&bp, eps).map(|top| top.contours(|w| inside(rule, *w)))));
|
||||
swept.map_err(|_| PathOpsError::Degenerate)?.map_err(|_| PathOpsError::OpenPath)
|
||||
}
|
||||
|
||||
/// Boolean operation between two filled paths. Returns an empty path on invalid (non-finite) input.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//! Absurd but finite or non-finite numeric parameters: no panic, hang or runaway allocation.
|
||||
|
||||
use vectorcraft_geom::{PathData, Point, SubPath};
|
||||
use vectorcraft_pathops::*;
|
||||
|
||||
const HOSTILE: [f64; 9] = [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0, -0.0, 1e-300, 1e300, -1e300, f64::MAX];
|
||||
|
||||
fn square() -> PathData {
|
||||
PathData::single(SubPath::polyline(&[Point::new(0.0, 0.0), Point::new(10.0, 0.0), Point::new(10.0, 10.0), Point::new(0.0, 10.0)], true))
|
||||
}
|
||||
|
||||
fn zigzag() -> PathData {
|
||||
let pts: Vec<Point> = (0..40).map(|i| Point::new(i as f64 * 3.0, if i % 2 == 0 { 0.0 } else { 7.0 })).collect();
|
||||
PathData::single(SubPath::polyline(&pts, false))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edit_functions_survive_hostile_numbers() {
|
||||
for paths in [square(), zigzag()] {
|
||||
for &v in &HOSTILE {
|
||||
let _ = simplify(&paths, v);
|
||||
let _ = simplify_with(&paths, &SimplifyOptions { tolerance: v, corner_angle_deg: v, straight_lines: false });
|
||||
let _ = simplify_with(&paths, &SimplifyOptions { tolerance: 0.5, corner_angle_deg: v, straight_lines: true });
|
||||
let _ = smooth(&paths, v);
|
||||
let _ = smooth_level(&paths, v);
|
||||
let _ = remove_redundant_points(&paths, v);
|
||||
let _ = join(&[paths.clone(), zigzag()], v);
|
||||
let _ = simplify_level_tolerance(v);
|
||||
let _ = auto_simplify_level(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A stroke width of 1e200 or more with round joins used to panic inside the sweep (`Left == Ish`).
|
||||
#[test]
|
||||
fn stroke_and_offset_survive_hostile_numbers() {
|
||||
for paths in [square(), zigzag()] {
|
||||
for &w in &HOSTILE {
|
||||
for &m in &[f64::NAN, 0.0, 1.0, 1e300] {
|
||||
for join in [Join::Miter, Join::Round, Join::Bevel] {
|
||||
for cap in [Cap::Butt, Cap::Round] {
|
||||
let _ = outline_stroke(&paths, w, cap, join, m);
|
||||
}
|
||||
let _ = offset_path(&paths, w, join, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#[path = "../common/mod.rs"]
|
||||
mod common;
|
||||
|
||||
mod hostile_params;
|
||||
mod ops;
|
||||
mod pathfinder_pinned;
|
||||
mod prop_pathops;
|
||||
|
||||
Reference in New Issue
Block a user