mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 09:20:56 -06:00
Merge main into fix/outline-stroke-keeps-effects-on-outline
This commit is contained in:
@@ -130,7 +130,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Simplify…",
|
||||
["Object", "Path"],
|
||||
None,
|
||||
"{tolerance?: pt (1), cornerAngle?: deg (30), straightLines?: bool} → {ids, before, after} anchor counts",
|
||||
"{tolerance?: pt (1), level?: 0..100 (the Simplify slider, in place of tolerance: 0 fewest anchor points, 50 is 1 pt, every 10 levels halves or doubles it), auto?: bool (Auto-Simplify: the lowest level whose tolerance is at most 1/400 of the selection's longer side, or 100 when that side is under 12.5 pt), cornerAngle?: deg (30), straightLines?: bool} → {ids, before, after} anchor counts, and the level used with level or auto",
|
||||
has_selection,
|
||||
simplify
|
||||
),
|
||||
@@ -582,9 +582,9 @@ pub(crate) fn with_transparency(d: &mut Document, mut n: Node, opacity: f32, ble
|
||||
n
|
||||
}
|
||||
|
||||
/// The art stroke `st` of a path (`path`, `rule`) paints, as fills: its brush art, or the
|
||||
/// outline of the stroke (as on the canvas) filled with the stroke's paint, opacity, blend mode
|
||||
/// and effects (a gradient along or across the stroke: gradient meshes in a clip group shaped
|
||||
/// The art stroke `st` of a path (`path`, `rule`) paints, as fills: its brush art (with the
|
||||
/// stroke's opacity, blend mode and raster effects), or the outline of the stroke (as on the canvas)
|
||||
/// filled with the stroke's paint, opacity, blend mode and effects (a gradient along or across the stroke: gradient meshes in a clip group shaped
|
||||
/// like the outline). `None` when it paints nothing.
|
||||
pub(crate) fn outlined_stroke(
|
||||
d: &mut Document,
|
||||
@@ -601,7 +601,15 @@ pub(crate) fn outlined_stroke(
|
||||
_ => Node::group(NodeId(0), pieces.into_iter().map(Arc::new).collect()),
|
||||
};
|
||||
let art = d.reid(&art);
|
||||
return Some(with_transparency(d, art, st.opacity, st.blend));
|
||||
let mut art = with_transparency(d, art, st.opacity, st.blend);
|
||||
let raster: Vec<_> = st.effects.iter().filter(|e| !vectorcraft_render::effects::is_geometry(&e.id)).cloned().collect();
|
||||
if !raster.is_empty() {
|
||||
if !art.appearance.effects.is_empty() {
|
||||
art = Node::group(d.alloc_id(), vec![Arc::new(art)]);
|
||||
}
|
||||
art.appearance.effects = raster;
|
||||
}
|
||||
return Some(art);
|
||||
}
|
||||
let outline = vectorcraft_render::effects::stroke::outline_region(path, rule, st);
|
||||
if outline.is_empty() {
|
||||
@@ -711,12 +719,13 @@ pub(crate) fn styled_copy(d: &mut Document, l: &Node, items: Vec<AppearanceItem>
|
||||
f
|
||||
}
|
||||
|
||||
/// What stands for object `l` once its appearance became `parts` (paint order): the one part or a
|
||||
/// group of them, with `l`'s transparency, name, opacity mask and effects. `None` without parts.
|
||||
/// What stands for object `l` once its appearance became `parts` (paint order): the one part (in a
|
||||
/// group when it has effects of its own) or a group of them, with `l`'s transparency, name, opacity
|
||||
/// mask and effects. `None` without parts.
|
||||
pub(crate) fn assemble(d: &mut Document, l: &Node, mut parts: Vec<Node>) -> Option<Node> {
|
||||
let root = match parts.len() {
|
||||
0 => return None,
|
||||
1 => parts.remove(0),
|
||||
1 if parts[0].appearance.effects.is_empty() => parts.remove(0),
|
||||
_ => Node::group(d.alloc_id(), parts.into_iter().map(Arc::new).collect()),
|
||||
};
|
||||
let mut root = with_transparency(d, root, l.opacity, l.blend);
|
||||
@@ -829,13 +838,29 @@ fn edit_paths(s: &mut Session, label: &str, f: impl Fn(&PathData) -> PathData) -
|
||||
}
|
||||
|
||||
fn simplify(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
// With auto, the slider's level is Auto-Simplify's for the selection's size; otherwise it is the level given.
|
||||
let level = if bool_or(p, "auto", false) {
|
||||
let roots = selected_roots(s)?;
|
||||
let size = s.doc()?.doc.bounds_of(&roots, false).map_or(0.0, |b| b.width().max(b.height()));
|
||||
Some(po::auto_simplify_level(size))
|
||||
} else {
|
||||
num_param(p, "level").map(|l| if l.is_finite() { l.round().clamp(0.0, 100.0) } else { po::SIMPLIFY_LEVEL })
|
||||
};
|
||||
let tolerance = match level {
|
||||
Some(l) => po::simplify_level_tolerance(l),
|
||||
None => len_param(p, "tolerance").unwrap_or(1.0),
|
||||
};
|
||||
let opts = po::SimplifyOptions {
|
||||
tolerance: len_param(p, "tolerance").unwrap_or(1.0).clamp(1e-4, 1e4),
|
||||
tolerance: tolerance.clamp(1e-4, 1e4),
|
||||
corner_angle_deg: num_param(p, "cornerAngle").unwrap_or(30.0).clamp(0.0, 180.0),
|
||||
straight_lines: bool_or(p, "straightLines", false),
|
||||
};
|
||||
let (ids, before, after) = edit_paths(s, "Simplify", |path| po::simplify_with(path, &opts))?;
|
||||
Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>(), "before": before, "after": after }))
|
||||
let mut out = json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>(), "before": before, "after": after });
|
||||
if let Some(l) = level {
|
||||
out["level"] = json!(l);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn add_anchor_points(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
|
||||
Reference in New Issue
Block a user