Eraser: Size follows the pen pressure too (#3961)

Eraser Tool Options' Size takes Fixed or Pressure and a Variation as the
Blob Brush's does: the tool records the pen, and path.eraseRegion outlines
the squashed drag at the pen's widths, stretched back to the tip's angle
and roundness. The pressure parsing and outline move to draw2's common
helpers, shared by both commands.
This commit is contained in:
bflatastic
2026-10-30 01:13:20 +07:00
parent 4a89ba84f8
commit 71fbdf5ff0
11 changed files with 154 additions and 89 deletions
+21
View File
@@ -503,6 +503,27 @@ fn blob_brush_size_follows_the_pen_pressure() {
assert!(inside(&s, drawn, 290.0, 707.0) && !inside(&s, drawn, 110.0, 703.0), "the tool's stroke follows the pen");
}
/// Eraser Tool Options' Size set to Pressure (#3961): the tip is Size − Variation across where the
/// pen pressed lightest and Size + Variation where it pressed hardest; Fixed erases at the Size.
#[test]
fn eraser_size_follows_the_pen_pressure() {
let pressed = |y: f64| (0..=20).map(|i| json!([100.0 + f64::from(i) * 10.0, y, f64::from(i) / 20.0])).collect::<Vec<_>>();
for (mode, thin_end_erased) in [("pressure", false), ("fixed", true)] {
let mut s = session();
rect(&mut s, 0.0, 0.0, 400.0, 200.0);
s.execute("path.eraseRegion", &json!({"points": pressed(100.0), "size": 10, "sizeMode": mode, "sizeVariation": 8})).unwrap();
let filled = |x: f64, y: f64| {
paths(&s).iter().any(|(_, pd)| vectorcraft_geom::hit::fill_contains(&pd.to_bezpath(), FillRule::NonZero, Point::new(x, y)))
};
assert!(!filled(290.0, 107.0), "{mode}: erased where the pen pressed hard");
assert_eq!(!filled(110.0, 103.0), thin_end_erased, "{mode}: the light end");
assert!(filled(110.0, 150.0), "{mode}: the rest stays");
}
let mut s = session();
let err = s.execute("path.eraseRegion", &json!({"points": [[0, 0], [10, 0]], "sizeMode": 5})).unwrap_err();
assert!(err.to_string().contains("sizeMode"), "{err}");
}
/// A Blob Brush stroke merges with the topmost matching path it touches and with the touched
/// matching paths next to it in stacking order in the same group or layer; matching paths in other
/// layers or groups, or below an object of another kind, are left alone.