Commands: ids leave out an object listed with a group that holds it, as the selection does

Commands that read the selection through edit::selected_roots leave
out an object whose group is also selected (edit::roots_of). Fourteen
places that read `ids` kept an object listed together with its group.
Given ids that list an object and a group that holds it, Make Repeat
failed with "no such object", New Symbol failed or stored the object
twice in the symbol, and object.transform moved it twice (20 pt for a
10 pt move).

These places now pass explicit ids through edit::outermost, which
drops an id whose ancestor is also listed, with the filter that
roots_of runs on the selection (now edit::without_inner). Nothing else
is dropped or replaced: unlike roots_of, it keeps layers and
compound-path members. A list without nesting gives the same result as
before. Commands that take both ids and the selection as listed, such
as paint.setFill without item, are unchanged.

Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-10-16 06:16:35 +07:00
committed by bflatastic
parent a6f56caa0e
commit 67a278b053
19 changed files with 208 additions and 41 deletions
+4 -3
View File
@@ -13,7 +13,7 @@ use vectorcraft_tools::distort::liquify::{Dabber, LiquifyParams, PathStroke, Sam
use vectorcraft_tools::distort::perspective::{self as persp, PerspectiveGrid, Plane};
use vectorcraft_tools::distort::{PinSet, arap, collect_points, mesh_for, project_node, stroke_owner, warp_from_rest, warp_node_with};
use super::edit::{duplicate_in, selected_roots};
use super::edit::{duplicate_in, outermost, selected_roots};
use super::*;
use crate::EngineError;
@@ -706,7 +706,8 @@ fn puppet_warp(s: &mut Session, p: &Value) -> Result<Value> {
if pins.iter().chain(&moved).any(|q| !q.x.is_finite() || !q.y.is_finite()) {
return Err(bad(C, "pins must be finite"));
}
let ids = if rest { puppet_ids(p, || Ok(s.doc()?.selection.objects.clone()))? } else { puppet_ids(p, || selected_roots(s))? };
let ids =
if rest { puppet_ids(p, || Ok(s.doc()?.selection.objects.clone()))? } else { outermost(&s.doc()?.doc, puppet_ids(p, || selected_roots(s))?) };
let expand = f64_or(p, "expand", 3.0).clamp(0.0, 1000.0);
let angles: Vec<Option<f64>> = match p.get("angles") {
None | Some(Value::Null) => vec![None; pins.len()],
@@ -846,7 +847,7 @@ fn plane_param(g: &PerspectiveGrid, p: &Value, cmd: &str) -> Result<Plane> {
fn roots(s: &Session, p: &Value) -> Result<Vec<NodeId>> {
match ids_param(p, "ids").or_else(|| id_param(p, "id").map(|i| vec![i])) {
Some(v) => Ok(v),
Some(v) => Ok(outermost(&s.doc()?.doc, v)),
None => selected_roots(s),
}
}