mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 10:47:11 -06:00
M3.97: freeform points drag out to delete, spread ring drags, Lines mode draws lines
With the Gradient tool on a freeform gradient the selected point shows its spread as a dashed ring with a handle; dragging either sets the spread. A point dragged out of the object is removed on release, and double-clicking a point opens the gradientStop popover, which now edits the selected freeform point (colour, opacity, spread). In Lines mode successive clicks draw one smooth line (with a rubber band to the pointer); clicking an existing point continues the line from it, and Escape or a click off the art ends it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
37acaebf95
commit
b8793eeaef
@@ -5,7 +5,7 @@
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::freeform::spread_scale;
|
||||
use vectorcraft_color::{Freeform, FreeformMode, FreeformPoint, GradientKind, Paint};
|
||||
use vectorcraft_doc::{Node, NodeKind};
|
||||
use vectorcraft_doc::NodeKind;
|
||||
use vectorcraft_geom::{Affine, Rect};
|
||||
|
||||
use super::appearance::{edit_items, edits_stroke, index_param, item_target};
|
||||
@@ -323,10 +323,3 @@ fn get(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
v["selected"] = json!(selected);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Whether `n`'s shape contains `p` (its filled regions; true for objects without them, such as
|
||||
/// type, whose boxes stand in).
|
||||
pub(crate) fn inside_fn(n: &Node) -> impl Fn(vectorcraft_geom::Point) -> bool + use<> {
|
||||
let shapes = n.clip_shapes(None);
|
||||
move |p| shapes.is_empty() || shapes.iter().any(|(bp, rule)| vectorcraft_geom::hit::fill_contains(bp, *rule, p))
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ fn edit_gradient(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
let b = item_paint_bounds(n, index, !stroke);
|
||||
// Only a gradient turning freeform needs the shape (its first points go inside it).
|
||||
let inside = (freeform && b.is_some()).then(|| super::freeform::inside_fn(n));
|
||||
let inside = (freeform && b.is_some()).then(|| n.contains_fn());
|
||||
let inside: &dyn Fn(Point) -> bool = match &inside {
|
||||
Some(f) => f,
|
||||
None => &|_| true,
|
||||
|
||||
@@ -61,7 +61,7 @@ fn the_first_application_places_points_inside_the_shape() {
|
||||
let g = fill_gradient(&s, id);
|
||||
assert_eq!(g.gradient.kind, GradientKind::Freeform);
|
||||
let f = g.freeform.as_ref().unwrap();
|
||||
let inside = cmd::freeform::inside_fn(node(&s, id));
|
||||
let inside = node(&s, id).contains_fn();
|
||||
assert!(f.points.len() >= 4 && f.points.iter().all(|p| inside(p.at)), "{:?}", f.points);
|
||||
// The stops follow the points' colours (swatch chips and exports show them).
|
||||
assert_eq!(g.gradient.stops.iter().map(|st| st.color).collect::<Vec<_>>(), f.points.iter().map(|p| p.color).collect::<Vec<_>>());
|
||||
|
||||
Reference in New Issue
Block a user