mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 14:41:52 +00:00
Merge pull request #7382 from ncic/pencil-alt-close-straight
Pencil: a closed stroke closes with a straight segment
This commit is contained in:
@@ -11,7 +11,9 @@ use super::{
|
||||
// ---------- Pencil / Paintbrush ----------
|
||||
|
||||
/// Fit a freehand polyline: drop jitter, build a spline through the samples (sharp turns stay
|
||||
/// corners) and simplify it to `tol`.
|
||||
/// corners) and simplify it to `tol`. A closed stroke is fitted as the open one is and closed by a
|
||||
/// straight segment from its end back to its start: fitting round the gap would curve that segment
|
||||
/// on from both ends, past them and back.
|
||||
fn fit_freehand(pts: &[Point], tol: f64, closed: bool) -> SubPath {
|
||||
let min = (tol * 0.3).max(vectorcraft_tools::draw2::MIN_FIT_TOLERANCE * 0.3);
|
||||
let (Some(&p0), Some(&pn)) = (pts.first(), pts.last()) else { return SubPath::new(vec![], closed) };
|
||||
@@ -41,15 +43,26 @@ fn fit_freehand(pts: &[Point], tol: f64, closed: bool) -> SubPath {
|
||||
(kept[i], corner)
|
||||
})
|
||||
.collect();
|
||||
let sp = vectorcraft_tools::draw2::catmull_rom(&marked, closed);
|
||||
let sp = vectorcraft_tools::draw2::catmull_rom(&marked, false);
|
||||
if sp.anchors.len() < 3 {
|
||||
return sp;
|
||||
// Two anchors closed would run back over the line itself.
|
||||
return SubPath::new(sp.anchors, closed && n > 2);
|
||||
}
|
||||
let fitted = po::simplify_with(
|
||||
&PathData::single(sp.clone()),
|
||||
&SimplifyOptions { tolerance: tol, corner_angle_deg: 60.0, straight_lines: false, ..Default::default() },
|
||||
);
|
||||
fitted.subpaths.into_iter().next().filter(|s| s.anchors.len() >= 2).unwrap_or(sp)
|
||||
let mut anchors = fitted.subpaths.into_iter().next().filter(|s| s.anchors.len() >= 2).unwrap_or(sp).anchors;
|
||||
if closed {
|
||||
// No handle on either side of the closing segment.
|
||||
if let Some(l) = anchors.last_mut() {
|
||||
l.h_out = l.p;
|
||||
}
|
||||
if let Some(f) = anchors.first_mut() {
|
||||
f.h_in = f.p;
|
||||
}
|
||||
}
|
||||
SubPath::new(anchors, closed)
|
||||
}
|
||||
|
||||
/// The path of a freehand stroke: fitted at `tol` ([`fit_freehand`]), or with no `tol` the points
|
||||
|
||||
@@ -184,6 +184,25 @@ fn pencil_alt_closes_path() {
|
||||
assert!(ps[0].1.is_closed());
|
||||
}
|
||||
|
||||
/// A Pencil stroke drawn as a dome and closed with Alt at the release closes with a straight segment
|
||||
/// across the bottom, from where the stroke ends to where it starts: the path stays within where the
|
||||
/// pen went, rather than curving on past both ends and back like a hat's brim.
|
||||
#[test]
|
||||
fn pencil_alt_closes_a_stroke_straight_back_to_its_start() {
|
||||
let mut s = session();
|
||||
let pts: Vec<(f64, f64)> =
|
||||
(0..=60).map(|i| f64::from(i) * std::f64::consts::PI / 60.0).map(|a| (300.0 - 150.0 * a.cos(), 400.0 - 200.0 * a.sin())).collect();
|
||||
drag(&mut s, "pencil", &pts, Mods { alt: true, ..Default::default() });
|
||||
let ps = paths(&s);
|
||||
let sp = &ps[0].1.subpaths[0];
|
||||
assert!(sp.closed);
|
||||
let b = ps[0].1.bounds().unwrap();
|
||||
assert!(b.x0 > 149.5 && b.x1 < 450.5 && b.y1 < 400.5, "within the stroke: {b:?}");
|
||||
let (first, last) = (sp.anchors.first().unwrap(), sp.anchors.last().unwrap());
|
||||
assert!(first.p.distance(Point::new(150.0, 400.0)) < 1e-6 && last.p.distance(Point::new(450.0, 400.0)) < 1e-6, "{first:?} {last:?}");
|
||||
assert_eq!((last.h_out, first.h_in), (last.p, first.p), "a straight closing segment");
|
||||
}
|
||||
|
||||
/// #3799: with Shift held, a wavy Pencil drag draws one straight segment at 45° steps from the
|
||||
/// Constrain Angle (here 30°: a drag at about 60° comes out at 75°).
|
||||
#[test]
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user