mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 07:16:52 +00:00
Contour: make it a built-in add-on and translate its labels
Review of #5694 asked for two changes. Add-on, not a core tool, as Enclosing Lasso is: - The command is addon.contour.set, in an add-on module of its own (crates/engine/src/cmd/addons/contour.rs) with its tests beside it. - The tool is listed under Object > Plug-ins > Contour and under its own "Contour" heading in All Tools. It has no Basic or Advanced toolbar slot and no default shortcut. The Contour bar is unchanged. - The add-on drawer test lists Contour with the other add-on tools. - Docs (mcp.md, the parity documents, ROADMAP) say add-on and use the new command name; mcp.md gains a Contour add-on section. Translations: rows for "Contour", "Contour Tool", "Sharp", "Bake" and "Turn the Contour into plain paths" in all 26 catalogs ("Round" and "Square" already had them). Drafted from each catalog's own vocabulary (for Bake, the verb it uses for Expand Appearance; a word other than the catalog's Stroke or Path word for the tool's name where they collide); they are not native-speaker reviewed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Sonnet 5.5
parent
8ce22e71eb
commit
db1ef1bfed
+2
-2
@@ -1,6 +1,6 @@
|
||||
# VectorCraft Roadmap
|
||||
|
||||
- Contour tool (beyond Illustrator): drag a selected shape's edge outward or inward for a live, editable offset copy, with a floating bar for distance and Sharp, Round or Square corners; Bake turns it into paths. Scores and estimates unchanged.
|
||||
- Contour add-on (beyond Illustrator): drag a selected shape's edge outward or inward for a live, editable offset copy, with a floating bar for distance and Sharp, Round or Square corners; Bake turns it into paths. Scores and estimates unchanged.
|
||||
|
||||
- Paint: new gradients use the document's color model for default stops, including Gradient tool creation. Scores and estimates unchanged.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
**Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour tool, beyond Illustrator; scores and estimates unchanged; new gradients follow document color mode; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour add-on, beyond Illustrator; scores and estimates unchanged; new gradients follow document color mode; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs
|
||||
on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON
|
||||
|
||||
@@ -12,7 +12,6 @@ mod colorcmds;
|
||||
pub mod colormgmt;
|
||||
pub mod colortheme;
|
||||
mod compoundshape;
|
||||
pub(crate) mod contour;
|
||||
pub(crate) mod create;
|
||||
mod css;
|
||||
mod cut;
|
||||
@@ -263,7 +262,6 @@ pub fn command_specs() -> &'static [CommandSpec] {
|
||||
v.extend(xform::specs());
|
||||
v.extend(effectcmd::specs());
|
||||
v.extend(pathops::specs());
|
||||
v.extend(contour::specs());
|
||||
v.extend(compoundshape::specs());
|
||||
v.extend(typecmd::specs());
|
||||
v.extend(menucmds::specs());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
mod arrange;
|
||||
mod cleanup;
|
||||
mod color;
|
||||
mod contour;
|
||||
mod document;
|
||||
mod pathtools;
|
||||
mod pen;
|
||||
@@ -17,6 +18,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
let mut v = arrange::specs();
|
||||
v.extend(cleanup::specs());
|
||||
v.extend(color::specs());
|
||||
v.extend(contour::specs());
|
||||
v.extend(document::specs());
|
||||
v.extend(pathtools::specs());
|
||||
v.extend(pen::specs());
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
//! `object.contour`: the Contour tool's command. A Contour is a copy of a selected shape that
|
||||
//! Contour (`addon.contour.*`), a built-in add-on: the Contour tool's command. A Contour is a copy of a selected shape that
|
||||
//! carries a live Offset Path effect, so it grows or shrinks the shape without baking anything: its
|
||||
//! fill, stroke and effect stay editable, and Expand Appearance bakes it into plain paths. The
|
||||
//! original stays where it is. Running the command on an object that already carries an Offset
|
||||
//! Path effect (a Contour made earlier) changes that effect instead of nesting another one.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_doc::NodeKind;
|
||||
use vectorcraft_render::effects;
|
||||
|
||||
use super::edit::{copy_above, selected_roots};
|
||||
use super::pathops::{len_param, num_param};
|
||||
use super::{CommandSpec, EngineError, NodeId, Result, Session, bad, cmd, has_selection, str_param};
|
||||
use super::super::edit::{copy_above, selected_roots};
|
||||
use super::super::pathops::{len_param, num_param};
|
||||
use super::super::{CommandSpec, EngineError, NodeId, Result, Session, bad, cmd, has_selection, str_param};
|
||||
|
||||
const C: &str = "object.contour";
|
||||
const C: &str = "addon.contour.set";
|
||||
/// The live effect that does the offsetting (the one Effect › Path › Offset Path applies).
|
||||
const EFFECT: &str = "path.offsetPath";
|
||||
/// The largest offset, in points; Offset Path takes the same.
|
||||
@@ -20,7 +23,7 @@ const MAX_OFFSET: f64 = 1e5;
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![cmd!(
|
||||
"object.contour",
|
||||
"addon.contour.set",
|
||||
"Contour",
|
||||
[],
|
||||
None,
|
||||
+14
-14
@@ -1,4 +1,4 @@
|
||||
//! Tests for `object.contour`: a live Offset Path copy of a shape that stays editable until baked.
|
||||
//! Tests for `addon.contour.set`: a live Offset Path copy of a shape that stays editable until baked.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_doc::NodeKind;
|
||||
@@ -46,7 +46,7 @@ fn contour_is_a_live_copy_above_the_original() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 100.0, 100.0, 50.0, 40.0);
|
||||
let before = bounds(&s, a);
|
||||
let r = s.execute("object.contour", &json!({"offset": 10})).unwrap();
|
||||
let r = s.execute("addon.contour.set", &json!({"offset": 10})).unwrap();
|
||||
let (made, updated) = (ids_of(&r, "created"), ids_of(&r, "updated"));
|
||||
assert_eq!((made.len(), updated.len()), (1, 0));
|
||||
// The original is untouched, the copy is above it and selected, and nothing is baked: the
|
||||
@@ -70,7 +70,7 @@ fn baking_a_contour_gives_the_grown_and_the_shrunk_shape() {
|
||||
let a = rect(&mut s, 100.0, 100.0, 50.0, 40.0);
|
||||
for (offset, want) in [(10.0, Rect::new(90.0, 90.0, 160.0, 150.0)), (-5.0, Rect::new(105.0, 105.0, 145.0, 135.0))] {
|
||||
select(&mut s, &[a]);
|
||||
s.execute("object.contour", &json!({"offset": offset})).unwrap();
|
||||
s.execute("addon.contour.set", &json!({"offset": offset})).unwrap();
|
||||
let baked = ids_of(&s.execute("effect.expandAppearance", &json!({})).unwrap(), "ids");
|
||||
let b = bounds(&s, baked[0]);
|
||||
// The default stroke widens the baked art by its weight; compare the fill's edges.
|
||||
@@ -92,10 +92,10 @@ fn baked_fill_bounds(s: &Session, id: NodeId) -> Rect {
|
||||
fn a_second_drag_changes_the_contour_instead_of_nesting_one() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 100.0, 100.0, 50.0, 40.0);
|
||||
let made = ids_of(&s.execute("object.contour", &json!({"offset": 10})).unwrap(), "created");
|
||||
let made = ids_of(&s.execute("addon.contour.set", &json!({"offset": 10})).unwrap(), "created");
|
||||
let count = node_count(&s);
|
||||
// The Contour is selected: the next call edits it.
|
||||
let r = s.execute("object.contour", &json!({"offset": -4, "corners": "round"})).unwrap();
|
||||
let r = s.execute("addon.contour.set", &json!({"offset": -4, "corners": "round"})).unwrap();
|
||||
assert_eq!(ids_of(&r, "updated"), made);
|
||||
assert!(ids_of(&r, "created").is_empty());
|
||||
assert_eq!(node_count(&s), count, "no new object");
|
||||
@@ -110,7 +110,7 @@ fn corner_styles_choose_the_join() {
|
||||
for (corners, joins) in [("sharp", "miter"), ("round", "round"), ("square", "bevel"), ("Round", "round")] {
|
||||
let mut s = session();
|
||||
rect(&mut s, 0.0, 0.0, 30.0, 30.0);
|
||||
let made = ids_of(&s.execute("object.contour", &json!({"offset": 3, "corners": corners})).unwrap(), "created");
|
||||
let made = ids_of(&s.execute("addon.contour.set", &json!({"offset": 3, "corners": corners})).unwrap(), "created");
|
||||
assert_eq!(effects_of(&s, made[0])[0].1["joins"], json!(joins), "{corners}");
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ fn corner_styles_choose_the_join() {
|
||||
fn contour_keeps_the_color_and_stroke_editable_on_the_copy() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 0.0, 30.0, 30.0);
|
||||
let made = ids_of(&s.execute("object.contour", &json!({"offset": 6})).unwrap(), "created");
|
||||
let made = ids_of(&s.execute("addon.contour.set", &json!({"offset": 6})).unwrap(), "created");
|
||||
let original = node(&s, a).appearance.items.clone();
|
||||
assert_eq!(node(&s, made[0]).appearance.items, original, "the copy starts with the shape's own paint");
|
||||
// Recolouring the Contour (it is the selection) changes its fill only: the live effect stays
|
||||
@@ -135,7 +135,7 @@ fn contour_is_one_undo_step() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 0.0, 30.0, 30.0);
|
||||
let count = node_count(&s);
|
||||
s.execute("object.contour", &json!({"offset": 6})).unwrap();
|
||||
s.execute("addon.contour.set", &json!({"offset": 6})).unwrap();
|
||||
assert_eq!(node_count(&s), count + 1);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(node_count(&s), count);
|
||||
@@ -148,13 +148,13 @@ fn contour_refuses_what_it_cannot_offset() {
|
||||
rect(&mut s, 0.0, 0.0, 30.0, 30.0);
|
||||
let count = node_count(&s);
|
||||
for params in [json!({}), json!({"offset": "abc"}), json!({"offset": 1e300}), json!({"offset": 5, "corners": "pointy"})] {
|
||||
assert!(s.execute("object.contour", ¶ms).is_err(), "{params}");
|
||||
assert!(s.execute("addon.contour.set", ¶ms).is_err(), "{params}");
|
||||
}
|
||||
// Nothing was added by the refused calls.
|
||||
assert_eq!(node_count(&s), count);
|
||||
// Type is not a path to offset.
|
||||
s.execute("text.create", &json!({"x": 10, "y": 10, "text": "Hi"})).unwrap();
|
||||
let err = s.execute("object.contour", &json!({"offset": 5})).unwrap_err().to_string();
|
||||
let err = s.execute("addon.contour.set", &json!({"offset": 5})).unwrap_err().to_string();
|
||||
assert!(err.contains("select paths or shapes"), "{err}");
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ fn border_with_a_gap_from_two_contours_and_minus_front() {
|
||||
// The gap first (+8), then the border (+20) from the same shape: both land directly above it,
|
||||
// the later one lower, so the gap ends up in front of the border.
|
||||
select(&mut s, &[a]);
|
||||
let gap = ids_of(&s.execute("object.contour", &json!({"offset": 8})).unwrap(), "created")[0];
|
||||
let gap = ids_of(&s.execute("addon.contour.set", &json!({"offset": 8})).unwrap(), "created")[0];
|
||||
select(&mut s, &[a]);
|
||||
let border = ids_of(&s.execute("object.contour", &json!({"offset": 20})).unwrap(), "created")[0];
|
||||
let border = ids_of(&s.execute("addon.contour.set", &json!({"offset": 20})).unwrap(), "created")[0];
|
||||
let d = &s.doc().unwrap().doc;
|
||||
assert!(d.index_path(gap).unwrap() > d.index_path(border).unwrap(), "the gap is in front of the border");
|
||||
select(&mut s, &[border, gap]);
|
||||
@@ -239,7 +239,7 @@ mod tool {
|
||||
assert!(!s.in_interaction());
|
||||
assert_eq!(undo_depth(&s), before + 1);
|
||||
assert_eq!(node_count(&s), nodes + 1);
|
||||
assert_eq!(s.journal.last().map(|j| j.0.as_str()), Some("object.contour"));
|
||||
assert_eq!(s.journal.last().map(|j| j.0.as_str()), Some("addon.contour.set"));
|
||||
let made = s.doc().unwrap().selection.objects.clone();
|
||||
assert_eq!(made.len(), 1);
|
||||
assert_ne!(made[0], a);
|
||||
@@ -304,7 +304,7 @@ mod tool {
|
||||
#[test]
|
||||
fn dragging_a_contour_again_keeps_its_corners_and_miter_limit() {
|
||||
let (mut s, _) = ready();
|
||||
s.execute("object.contour", &json!({"offset": 5, "corners": "square", "miterLimit": 7})).unwrap();
|
||||
s.execute("addon.contour.set", &json!({"offset": 5, "corners": "square", "miterLimit": 7})).unwrap();
|
||||
let contour = s.doc().unwrap().selection.objects[0];
|
||||
s.set_tool_option("corners", &json!("sharp"));
|
||||
at(&mut s, PointerKind::Down, 150.0, 120.0, Mods::default());
|
||||
@@ -36,7 +36,6 @@ mod compoundpaint;
|
||||
mod constrain_new;
|
||||
mod constrain_tools;
|
||||
mod containers;
|
||||
mod contour;
|
||||
mod css;
|
||||
mod cut;
|
||||
mod dashalign;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! submenu selects them. Tool gestures emit engine commands.
|
||||
|
||||
pub mod anchor_eraser;
|
||||
pub mod contour;
|
||||
pub mod corner_tool;
|
||||
pub mod enclosing_lasso;
|
||||
pub mod extend_path;
|
||||
@@ -28,14 +29,17 @@ pub const PATH_TOOLS: &[ToolInfo] = &[
|
||||
/// Smart Pen's tool, a flyout group of its own.
|
||||
pub const SMART_PEN: &[ToolInfo] = &[ToolInfo { id: smart_pen::ID, label: "Smart Pen Tool", shortcut: None, icon: "spline-pointer" }];
|
||||
|
||||
pub const CONTOUR: &[ToolInfo] = &[ToolInfo { id: contour::ID, label: "Contour Tool", shortcut: None, icon: "tool-contour" }];
|
||||
|
||||
pub const ENCLOSING_LASSO: &[ToolInfo] = &[ToolInfo { id: enclosing_lasso::ID, label: "Enclosing Lasso Tool", shortcut: None, icon: "tool-lasso" }];
|
||||
|
||||
/// The add-ons' tool groups, after the toolbar's own ([`crate::TOOL_GROUPS`]), each with the
|
||||
/// heading the All Tools drawer lists it under (its add-on's name).
|
||||
const HEADED: &[(&[ToolInfo], &str)] = &[(PATH_TOOLS, "Path Precision Tools"), (SMART_PEN, "Smart Pen"), (ENCLOSING_LASSO, "Enclosing Lasso")];
|
||||
const HEADED: &[(&[ToolInfo], &str)] =
|
||||
&[(PATH_TOOLS, "Path Precision Tools"), (SMART_PEN, "Smart Pen"), (ENCLOSING_LASSO, "Enclosing Lasso"), (CONTOUR, "Contour")];
|
||||
|
||||
/// The add-ons' tool groups, after the toolbar's own ([`crate::TOOL_GROUPS`]).
|
||||
pub const GROUPS: &[&[ToolInfo]] = &[PATH_TOOLS, SMART_PEN, ENCLOSING_LASSO];
|
||||
pub const GROUPS: &[&[ToolInfo]] = &[PATH_TOOLS, SMART_PEN, ENCLOSING_LASSO, CONTOUR];
|
||||
|
||||
/// The heading the All Tools drawer lists tool `id` under: its add-on's name.
|
||||
pub fn heading(id: &str) -> Option<&'static str> {
|
||||
@@ -54,6 +58,7 @@ pub fn create(id: &str) -> Option<Box<dyn Tool>> {
|
||||
slide_anchor::ID => Box::new(slide_anchor::SlideAnchorTool::default()),
|
||||
smart_pen::ID => Box::new(smart_pen::SmartPenTool::default()),
|
||||
enclosing_lasso::ID => Box::new(enclosing_lasso::EnclosingLassoTool::default()),
|
||||
contour::ID => Box::new(contour::ContourTool::default()),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -74,6 +79,7 @@ mod tests {
|
||||
assert!(PATH_TOOLS.iter().all(|t| heading(t.id) == Some("Path Precision Tools")));
|
||||
assert_eq!(heading(smart_pen::ID), Some("Smart Pen"));
|
||||
assert_eq!(heading(enclosing_lasso::ID), Some("Enclosing Lasso"));
|
||||
assert_eq!(heading(contour::ID), Some("Contour"));
|
||||
assert_eq!(GROUPS.len(), HEADED.len());
|
||||
assert!(crate::catalog::all_tools().any(|t| t.id == extend_path::ID));
|
||||
assert!(create("pen").is_none() && heading("pen").is_none());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! The Contour tool: drag away from a selected shape's edge to grow a live copy of it, or toward the
|
||||
//! inside to shrink it (`object.contour`). The copy is the original's outline offset by the distance
|
||||
//! inside to shrink it (`addon.contour.set`). The copy is the original's outline offset by the distance
|
||||
//! the pointer has moved from the edge, previewed while the button is down and kept as one undo step
|
||||
//! on release. Nothing is baked: the copy carries a live Offset Path effect, so it keeps being
|
||||
//! editable (a second drag on a selected Contour changes its distance instead of nesting another),
|
||||
@@ -16,9 +16,12 @@ use vectorcraft_geom::{BezPath, FillRule, Point, hit};
|
||||
|
||||
use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, ToolContext, ToolKey};
|
||||
|
||||
/// The live effect a Contour is made of (the one `object.contour` applies).
|
||||
/// The tool's id.
|
||||
pub const ID: &str = "contour";
|
||||
|
||||
/// The live effect a Contour is made of (the one `addon.contour.set` applies).
|
||||
const EFFECT: &str = "path.offsetPath";
|
||||
/// The largest distance, in points, `object.contour` accepts.
|
||||
/// The largest distance, in points, `addon.contour.set` accepts.
|
||||
const MAX_OFFSET: f64 = 1e5;
|
||||
/// The guide showing the shape a Contour was made from.
|
||||
const GUIDE: [u8; 3] = [0x1e, 0x88, 0xe5];
|
||||
@@ -51,7 +54,7 @@ fn contour_offset(n: &Node) -> Option<f64> {
|
||||
Some(e.params.get("offset").and_then(Value::as_f64).unwrap_or(0.0))
|
||||
}
|
||||
|
||||
/// The corner style and miter limit of a Contour's Offset Path effect, as `object.contour` takes
|
||||
/// The corner style and miter limit of a Contour's Offset Path effect, as `addon.contour.set` takes
|
||||
/// them.
|
||||
fn contour_style(n: &Node) -> Option<Value> {
|
||||
let e = n.appearance.effects.iter().rev().find(|e| e.id == EFFECT)?;
|
||||
@@ -154,7 +157,7 @@ fn round_distance(v: f64, mods: Mods) -> f64 {
|
||||
|
||||
impl Tool for ContourTool {
|
||||
fn id(&self) -> &'static str {
|
||||
"contour"
|
||||
ID
|
||||
}
|
||||
|
||||
fn pointer(&mut self, cx: &ToolContext, ev: &PointerEvent) -> Vec<Action> {
|
||||
@@ -182,7 +185,7 @@ impl Tool for ContourTool {
|
||||
drag.last = Some(offset);
|
||||
let mut params = drag.style.clone();
|
||||
params["offset"] = json!(offset);
|
||||
vec![Action::Preview("object.contour".into(), params)]
|
||||
vec![Action::Preview("addon.contour.set".into(), params)]
|
||||
}
|
||||
PointerKind::Up => match self.drag.take() {
|
||||
Some(d) if d.last.is_some() => vec![Action::Commit],
|
||||
@@ -249,8 +252,3 @@ impl Tool for ContourTool {
|
||||
self.drag.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Contour tool, for [`crate::create`].
|
||||
pub fn create(id: &str) -> Option<Box<dyn Tool>> {
|
||||
(id == "contour").then(|| Box::new(ContourTool::default()) as Box<dyn Tool>)
|
||||
}
|
||||
@@ -94,7 +94,6 @@ pub const TOOL_GROUPS: &[&[ToolInfo]] = &[
|
||||
t("livePaintBucket", "Live Paint Bucket", Some("K"), "tool-bucket"),
|
||||
t("livePaintSelection", "Live Paint Selection Tool", Some("Shift+L"), "tool-live-select"),
|
||||
],
|
||||
&[t("contour", "Contour Tool", None, "tool-contour")],
|
||||
&[
|
||||
t("perspectiveGrid", "Perspective Grid Tool", Some("Shift+P"), "tool-perspective"),
|
||||
t("perspectiveSelection", "Perspective Selection Tool", Some("Shift+V"), "tool-perspective-select"),
|
||||
|
||||
@@ -11,7 +11,6 @@ pub mod addons;
|
||||
pub mod bbox;
|
||||
pub mod builder;
|
||||
pub mod catalog;
|
||||
pub mod contour;
|
||||
pub mod corners;
|
||||
pub mod cropimage;
|
||||
pub mod cut;
|
||||
@@ -297,7 +296,6 @@ pub fn create(id: &str) -> Box<dyn Tool> {
|
||||
.or_else(|| printtiling::create(other))
|
||||
.or_else(|| cropimage::create(other))
|
||||
.or_else(|| cut::create(other))
|
||||
.or_else(|| contour::create(other))
|
||||
.or_else(|| intertwine::create(other))
|
||||
.or_else(|| objects_on_path::create(other))
|
||||
.or_else(|| touchtype::create(other))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
pub mod arrange;
|
||||
pub mod cleanup;
|
||||
pub mod color;
|
||||
pub mod contour;
|
||||
pub mod document;
|
||||
pub mod enclosing_lasso;
|
||||
pub mod pathtools;
|
||||
@@ -18,6 +19,7 @@ pub fn plugin_menu() -> Vec<Item> {
|
||||
Item::Sub("Arrange Tools", arrange::menu()),
|
||||
Item::Sub("Artwork Cleanup", cleanup::menu()),
|
||||
Item::Sub("Color Effects", color::menu()),
|
||||
Item::Sub("Contour", contour::menu()),
|
||||
Item::Sub("Document Tools", document::menu()),
|
||||
Item::Sub("Enclosing Lasso", enclosing_lasso::menu()),
|
||||
Item::Sub("Path Precision Tools", pathtools::menu()),
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//! The Contour add-on in the interface: its submenu (Object › Plug-ins › Contour), which selects the
|
||||
//! Contour tool. The Contour bar (`canvas/contour_bar.rs`) shows over the selection while the tool is
|
||||
//! active. Everything acts through `addon.contour.set` and Expand Appearance.
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_tools::addons::contour;
|
||||
|
||||
use crate::menus::Item;
|
||||
|
||||
pub fn menu() -> Vec<Item> {
|
||||
vec![Item::Cmd("Contour Tool", "tool.select", json!({"tool": contour::ID}))]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The Contour bar: a floating bar above the selection while the Contour tool is active. It has the
|
||||
//! Contour's distance in a number box, its corner style (Sharp, Round or Square) and Bake, which
|
||||
//! turns the live Contour into plain paths. It acts through `object.contour` and
|
||||
//! turns the live Contour into plain paths. It acts through `addon.contour.set` and
|
||||
//! `effect.expandAppearance`, so everything it does is a command with its own undo step.
|
||||
|
||||
use serde_json::Value;
|
||||
@@ -98,7 +98,7 @@ pub(super) fn contour_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) {
|
||||
if resp.drag_stopped() || resp.lost_focus() {
|
||||
ui.data_mut(|d| d.remove_temp::<f64>(key));
|
||||
if (v - shown).abs() > 1e-9 {
|
||||
run = Some(("object.contour", json!({"offset": v, "corners": corners})));
|
||||
run = Some(("addon.contour.set", json!({"offset": v, "corners": corners})));
|
||||
}
|
||||
}
|
||||
for (label, id) in [(tl!("Sharp"), "sharp"), (tl!("Round"), "round"), (tl!("Square"), "square")] {
|
||||
@@ -119,7 +119,7 @@ pub(super) fn contour_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) {
|
||||
if result.is_ok()
|
||||
&& let Some(s) = &stand
|
||||
{
|
||||
result = app.run("object.contour", json!({"offset": s.offset, "corners": id}));
|
||||
result = app.run("addon.contour.set", json!({"offset": s.offset, "corners": id}));
|
||||
}
|
||||
if let Err(e) = result {
|
||||
app.ui.status = e;
|
||||
|
||||
@@ -9053,7 +9053,7 @@ fn the_contour_bar_sets_corners_and_bakes() {
|
||||
assert!(offsets(&app).is_empty());
|
||||
|
||||
// A Contour made now has round corners; Square changes the selected Contour in place.
|
||||
app.session.execute("object.contour", &json!({"offset": 8, "corners": "round"})).unwrap();
|
||||
app.session.execute("addon.contour.set", &json!({"offset": 8, "corners": "round"})).unwrap();
|
||||
assert_eq!(offsets(&app), ["round"]);
|
||||
click_contour_bar(&mut app, &ctx, "Square");
|
||||
assert_eq!(offsets(&app), ["bevel"], "the selected Contour changed, none was added");
|
||||
|
||||
@@ -410,7 +410,7 @@ fn heading(id: &str) -> &'static str {
|
||||
_ if id.starts_with("perspective") => "Perspective",
|
||||
"artboard" => "Artboards",
|
||||
"slice" | "sliceSelection" => "Slices",
|
||||
"objectsOnPath" | "contour" => "Modify",
|
||||
"objectsOnPath" => "Modify",
|
||||
_ => "More",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,10 +529,22 @@ fn drop_targets_follow_the_buttons_edges() {
|
||||
fn addon_tools_are_in_the_drawer_not_the_toolbars() {
|
||||
let mut app = app();
|
||||
let enclosing = vectorcraft_tools::addons::enclosing_lasso::ID;
|
||||
let contour = vectorcraft_tools::addons::contour::ID;
|
||||
let addon: Vec<&str> = vectorcraft_tools::addons::GROUPS.iter().flat_map(|g| g.iter().map(|t| t.id)).collect();
|
||||
assert_eq!(
|
||||
addon,
|
||||
["anchorEraserBrush", "extendPath", "measureInspect", "parametricShape", "cornerTool", "slideAnchor", "sharedEdge", "smartPen", enclosing]
|
||||
[
|
||||
"anchorEraserBrush",
|
||||
"extendPath",
|
||||
"measureInspect",
|
||||
"parametricShape",
|
||||
"cornerTool",
|
||||
"slideAnchor",
|
||||
"sharedEdge",
|
||||
"smartPen",
|
||||
enclosing,
|
||||
contour
|
||||
]
|
||||
);
|
||||
for advanced in [false, true] {
|
||||
if advanced {
|
||||
@@ -546,7 +558,8 @@ fn addon_tools_are_in_the_drawer_not_the_toolbars() {
|
||||
assert_eq!(listed("extendPath"), Some(("Path Precision Tools", addon[..7].to_vec())));
|
||||
assert_eq!(listed("smartPen"), Some(("Smart Pen", vec!["smartPen"])));
|
||||
assert_eq!(listed(enclosing), Some(("Enclosing Lasso", vec![enclosing])));
|
||||
for tool in ["extendPath", "smartPen", enclosing] {
|
||||
assert_eq!(listed(contour), Some(("Contour", vec![contour])));
|
||||
for tool in ["extendPath", "smartPen", enclosing, contour] {
|
||||
app.run("toolbar.addTool", json!({"tool": tool})).unwrap();
|
||||
assert!(slot_of(&app, tool).is_some(), "{tool}");
|
||||
}
|
||||
|
||||
@@ -6201,6 +6201,13 @@ breakText Glyphs Глифи
|
||||
Fully Outside Изцяло извън
|
||||
No artboards match. Няма съвпадащи работни области.
|
||||
Chosen: {n} of {total} Избрани: {n} от {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Очертание
|
||||
Contour Tool Инструмент „Очертание“
|
||||
Sharp Заостряне
|
||||
Bake Разшири
|
||||
Turn the Contour into plain paths Превръща очертанието в обикновени пътища
|
||||
Set Corners… Задаване на ъгли…
|
||||
All Anchors Всички опорни точки
|
||||
Odd-Numbered Anchors Нечетни опорни точки
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6236,6 +6236,13 @@ shape Open Otvoren
|
||||
Fully Outside Potpuno vani
|
||||
No artboards match. Nijedna radna ploča ne odgovara.
|
||||
Chosen: {n} of {total} Odabrano: {n} od {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kontura
|
||||
Contour Tool Alat kontura
|
||||
Sharp Oštro
|
||||
Bake Proširi
|
||||
Turn the Contour into plain paths Pretvori konturu u obične putanje
|
||||
Set Corners… Postavi uglove…
|
||||
All Anchors Sva sidra
|
||||
Odd-Numbered Anchors Neparna sidra
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6337,6 +6337,13 @@ breakText Glyphs Glifs
|
||||
Fully Outside Totalment fora
|
||||
No artboards match. Cap tauler no coincideix.
|
||||
Chosen: {n} of {total} Triats: {n} de {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Contorn
|
||||
Contour Tool Eina de contorn
|
||||
Sharp Punxegut
|
||||
Bake Expandeix
|
||||
Turn the Contour into plain paths Converteix el contorn en traçats simples
|
||||
Set Corners… Defineix les cantonades…
|
||||
All Anchors Tots els ancoratges
|
||||
Odd-Numbered Anchors Ancoratges senars
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6351,6 +6351,13 @@ breakText Glyphs Glyfy
|
||||
Fully Outside Zcela vně
|
||||
No artboards match. Žádná pracovní plocha neodpovídá.
|
||||
Chosen: {n} of {total} Zvoleno: {n} z {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Obrys
|
||||
Contour Tool Nástroj Obrys
|
||||
Sharp Ostrý
|
||||
Bake Rozložit
|
||||
Turn the Contour into plain paths Převést obrys na běžné cesty
|
||||
Set Corners… Nastavit rohy…
|
||||
All Anchors Všechny kotevní body
|
||||
Odd-Numbered Anchors Liché kotevní body
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6217,6 +6217,13 @@ breakText Glyphs Glyffer
|
||||
Fully Outside Helt udenfor
|
||||
No artboards match. Ingen tegneflader passer.
|
||||
Chosen: {n} of {total} Valgt: {n} af {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kontur
|
||||
Contour Tool Kontur-værktøj
|
||||
Sharp Spids
|
||||
Bake Udvid
|
||||
Turn the Contour into plain paths Gør konturen til almindelige stier
|
||||
Set Corners… Angiv hjørner…
|
||||
All Anchors Alle ankre
|
||||
Odd-Numbered Anchors Ulige ankre
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5937,6 +5937,13 @@ breakText Glyphs Glyphen
|
||||
Fully Outside Vollständig außen
|
||||
No artboards match. Keine Zeichenfläche passt.
|
||||
Chosen: {n} of {total} Gewählt: {n} von {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Umriss
|
||||
Contour Tool Umriss-Werkzeug
|
||||
Sharp Spitz
|
||||
Bake Umwandeln
|
||||
Turn the Contour into plain paths Wandelt den Umriss in einfache Pfade um
|
||||
Set Corners… Ecken festlegen…
|
||||
All Anchors Alle Ankerpunkte
|
||||
Odd-Numbered Anchors Ungerade Ankerpunkte
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6303,6 +6303,13 @@ breakText Glyphs Γλύφοι
|
||||
Fully Outside Πλήρως εκτός
|
||||
No artboards match. Κανένας πίνακας σχεδίασης δεν ταιριάζει.
|
||||
Chosen: {n} of {total} Επιλεγμένοι: {n} από {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Παράλληλο περίγραμμα
|
||||
Contour Tool Εργαλείο παράλληλου περιγράμματος
|
||||
Sharp Οξεία
|
||||
Bake Επέκταση
|
||||
Turn the Contour into plain paths Μετατρέπει το παράλληλο περίγραμμα σε απλές διαδρομές
|
||||
Set Corners… Ορισμός γωνιών…
|
||||
All Anchors Όλα τα σημεία αγκύρωσης
|
||||
Odd-Numbered Anchors Μονά σημεία αγκύρωσης
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 163 and column 5.
|
@@ -6293,6 +6293,13 @@ breakText Glyphs Glifos
|
||||
Fully Outside Totalmente fuera
|
||||
No artboards match. Ninguna mesa de trabajo coincide.
|
||||
Chosen: {n} of {total} Elegidas: {n} de {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Contorno
|
||||
Contour Tool Herramienta Contorno
|
||||
Sharp Puntiaguda
|
||||
Bake Expandir
|
||||
Turn the Contour into plain paths Convierte el contorno en trazados simples
|
||||
Set Corners… Definir esquinas…
|
||||
All Anchors Todas las anclas
|
||||
Odd-Numbered Anchors Anclas impares
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6386,6 +6386,13 @@ breakText Glyphs Merkit
|
||||
Fully Outside Kokonaan ulkona
|
||||
No artboards match. Mikään piirtoalusta ei vastaa hakua.
|
||||
Chosen: {n} of {total} Valittu: {n}/{total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Ääriviiva
|
||||
Contour Tool Ääriviivatyökalu
|
||||
Sharp Terävä
|
||||
Bake Laajenna
|
||||
Turn the Contour into plain paths Muuntaa ääriviivan tavallisiksi poluiksi
|
||||
Set Corners… Aseta kulmat…
|
||||
All Anchors Kaikki ankkurit
|
||||
Odd-Numbered Anchors Parittomat ankkurit
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6346,6 +6346,13 @@ breakText Glyphs Glyphes
|
||||
Fully Outside Entièrement à l’extérieur
|
||||
No artboards match. Aucun plan de travail ne correspond.
|
||||
Chosen: {n} of {total} Choisis : {n} sur {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Pourtour
|
||||
Contour Tool Outil Pourtour
|
||||
Sharp Pointu
|
||||
Bake Décomposer
|
||||
Turn the Contour into plain paths Transforme le pourtour en tracés simples
|
||||
Set Corners… Définir les angles…
|
||||
All Anchors Tous les points d’ancrage
|
||||
Odd-Numbered Anchors Points d’ancrage impairs
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6335,6 +6335,13 @@ breakText Glyphs Glifák
|
||||
Fully Outside Teljesen kívül
|
||||
No artboards match. Nincs egyező rajztábla.
|
||||
Chosen: {n} of {total} Kiválasztva: {n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kontúr
|
||||
Contour Tool Kontúr eszköz
|
||||
Sharp Hegyes
|
||||
Bake Kibontás
|
||||
Turn the Contour into plain paths A kontúrt egyszerű útvonalakká alakítja
|
||||
Set Corners… Sarkok beállítása…
|
||||
All Anchors Összes horgonypont
|
||||
Odd-Numbered Anchors Páratlan horgonypontok
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6217,6 +6217,13 @@ breakText Glyphs Glif
|
||||
Fully Outside Sepenuhnya di luar
|
||||
No artboards match. Tidak ada artboard yang cocok.
|
||||
Chosen: {n} of {total} Dipilih: {n} dari {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kontur
|
||||
Contour Tool Alat kontur
|
||||
Sharp Tajam
|
||||
Bake Perluas
|
||||
Turn the Contour into plain paths Ubah kontur menjadi jalur biasa
|
||||
Set Corners… Atur sudut…
|
||||
All Anchors Semua jangkar
|
||||
Odd-Numbered Anchors Jangkar bernomor ganjil
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2235 and column 5.
|
@@ -5971,6 +5971,13 @@ breakText Glyphs Glifi
|
||||
Fully Outside Completamente esterno
|
||||
No artboards match. Nessuna tavola da disegno corrisponde.
|
||||
Chosen: {n} of {total} Scelte: {n} di {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Contorno
|
||||
Contour Tool Strumento Contorno
|
||||
Sharp Appuntito
|
||||
Bake Espandi
|
||||
Turn the Contour into plain paths Trasforma il contorno in tracciati semplici
|
||||
Set Corners… Imposta angoli…
|
||||
All Anchors Tutti gli ancoraggi
|
||||
Odd-Numbered Anchors Ancoraggi dispari
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6036,6 +6036,13 @@ breakText Glyphs 字形
|
||||
Fully Outside 完全に外側
|
||||
No artboards match. 一致するアートボードはありません。
|
||||
Chosen: {n} of {total} 選択中: {n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour 輪郭
|
||||
Contour Tool 輪郭ツール
|
||||
Sharp 鋭角
|
||||
Bake 分割
|
||||
Turn the Contour into plain paths 輪郭を通常のパスに変換します
|
||||
Set Corners… コーナーを設定…
|
||||
All Anchors すべてのアンカー
|
||||
Odd-Numbered Anchors 奇数番目のアンカー
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6362,6 +6362,13 @@ font-script Japanese იაპონური
|
||||
Fully Outside მთლიანად გარეთ
|
||||
No artboards match. შესაბამისი სამუშაო დაფა არ არის.
|
||||
Chosen: {n} of {total} არჩეულია: {n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour მოხაზულობა
|
||||
Contour Tool მოხაზულობის ხელსაწყო
|
||||
Sharp მახვილი
|
||||
Bake გაშლა
|
||||
Turn the Contour into plain paths მოხაზულობას ჩვეულებრივ ტრაექტორიებად აქცევს
|
||||
Set Corners… კუთხეების დაყენება…
|
||||
All Anchors ყველა საყრდენი წერტილი
|
||||
Odd-Numbered Anchors კენტი საყრდენი წერტილები
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -5932,6 +5932,13 @@ breakText Glyphs 글리프
|
||||
Fully Outside 완전히 바깥쪽
|
||||
No artboards match. 일치하는 아트보드가 없습니다.
|
||||
Chosen: {n} of {total} 선택됨: {n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour 윤곽
|
||||
Contour Tool 윤곽 도구
|
||||
Sharp 뾰족하게
|
||||
Bake 확장
|
||||
Turn the Contour into plain paths 윤곽을 일반 패스로 바꿉니다
|
||||
Set Corners… 모퉁이 설정…
|
||||
All Anchors 모든 앵커
|
||||
Odd-Numbered Anchors 홀수 번째 앵커
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6261,6 +6261,13 @@ breakText Glyphs Glyphs
|
||||
Fully Outside Volledig buiten
|
||||
No artboards match. Geen tekengebied komt overeen.
|
||||
Chosen: {n} of {total} Gekozen: {n} van {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Omtrek
|
||||
Contour Tool Omtrek-gereedschap
|
||||
Sharp Scherp
|
||||
Bake Uitbreiden
|
||||
Turn the Contour into plain paths Zet de omtrek om in gewone paden
|
||||
Set Corners… Hoeken instellen…
|
||||
All Anchors Alle ankerpunten
|
||||
Odd-Numbered Anchors Oneven ankerpunten
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6206,6 +6206,13 @@ breakText Glyphs Glify
|
||||
Fully Outside Całkowicie na zewnątrz
|
||||
No artboards match. Żaden obszar kompozycji nie pasuje.
|
||||
Chosen: {n} of {total} Wybrano: {n} z {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kontur
|
||||
Contour Tool Narzędzie Kontur
|
||||
Sharp Ostry
|
||||
Bake Rozszerz
|
||||
Turn the Contour into plain paths Zamienia kontur w zwykłe ścieżki
|
||||
Set Corners… Ustaw narożniki…
|
||||
All Anchors Wszystkie punkty
|
||||
Odd-Numbered Anchors Punkty nieparzyste
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6424,6 +6424,13 @@ swatch Yellow Green Verde-amarelado
|
||||
Fully Outside Totalmente fora
|
||||
No artboards match. Nenhuma mesa de trabalho corresponde.
|
||||
Chosen: {n} of {total} Escolhidas: {n} de {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Contorno
|
||||
Contour Tool Ferramenta Contorno
|
||||
Sharp Pontiagudo
|
||||
Bake Expandir
|
||||
Turn the Contour into plain paths Transforma o contorno em caminhos simples
|
||||
Set Corners… Definir Cantos…
|
||||
All Anchors Todas as Âncoras
|
||||
Odd-Numbered Anchors Âncoras Ímpares
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -6440,6 +6440,13 @@ shape Open Открытый
|
||||
Fully Outside Полностью снаружи
|
||||
No artboards match. Нет подходящих монтажных областей.
|
||||
Chosen: {n} of {total} Выбрано: {n} из {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Эквидистанта
|
||||
Contour Tool Инструмент «Эквидистанта»
|
||||
Sharp Острый
|
||||
Bake Разобрать
|
||||
Turn the Contour into plain paths Преобразует эквидистанту в обычные контуры
|
||||
Set Corners… Задать углы…
|
||||
All Anchors Все опорные точки
|
||||
Odd-Numbered Anchors Нечётные опорные точки
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6407,6 +6407,13 @@ digits Hindi Hindi
|
||||
Fully Outside Helt utanför
|
||||
No artboards match. Ingen rityta matchar.
|
||||
Chosen: {n} of {total} Valda: {n} av {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Kantlinje
|
||||
Contour Tool Kantlinjeverktyg
|
||||
Sharp Spetsig
|
||||
Bake Expandera
|
||||
Turn the Contour into plain paths Gör om kantlinjen till vanliga banor
|
||||
Set Corners… Ange hörn…
|
||||
All Anchors Alla ankare
|
||||
Odd-Numbered Anchors Udda ankare
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -6308,6 +6308,13 @@ breakText Glyphs Glifler
|
||||
Fully Outside Tamamen dışarıda
|
||||
No artboards match. Eşleşen çalışma yüzeyi yok.
|
||||
Chosen: {n} of {total} Seçilen: {n}/{total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Dış Hat
|
||||
Contour Tool Dış Hat Aracı
|
||||
Sharp Keskin
|
||||
Bake Genişlet
|
||||
Turn the Contour into plain paths Dış hattı sade yollara dönüştürür
|
||||
Set Corners… Köşeleri Ayarla…
|
||||
All Anchors Tüm Çapalar
|
||||
Odd-Numbered Anchors Tek Numaralı Çapalar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -5951,6 +5951,13 @@ breakText Glyphs Гліфи
|
||||
Fully Outside Повністю зовні
|
||||
No artboards match. Немає відповідних монтажних областей.
|
||||
Chosen: {n} of {total} Вибрано: {n} з {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Еквідистанта
|
||||
Contour Tool Інструмент «Еквідистанта»
|
||||
Sharp Гострий
|
||||
Bake Розібрати
|
||||
Turn the Contour into plain paths Перетворює еквідистанту на звичайні контури
|
||||
Set Corners… Задати кути…
|
||||
All Anchors Усі опорні точки
|
||||
Odd-Numbered Anchors Непарні опорні точки
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 177 and column 5.
|
@@ -5952,6 +5952,13 @@ breakText Glyphs Glyph
|
||||
Fully Outside Hoàn toàn bên ngoài
|
||||
No artboards match. Không có bảng vẽ nào khớp.
|
||||
Chosen: {n} of {total} Đã chọn: {n} trên {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour Đường viền
|
||||
Contour Tool Công cụ Đường viền
|
||||
Sharp Nhọn
|
||||
Bake Mở rộng
|
||||
Turn the Contour into plain paths Chuyển đường viền thành các đường dẫn thường
|
||||
Set Corners… Đặt góc…
|
||||
All Anchors Tất cả điểm neo
|
||||
Odd-Numbered Anchors Điểm neo số lẻ
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 199 and column 5.
|
@@ -6192,6 +6192,13 @@ breakText Glyphs 字形
|
||||
Fully Outside 完全在外
|
||||
No artboards match. 没有匹配的画板。
|
||||
Chosen: {n} of {total} 已选:{n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour 轮廓
|
||||
Contour Tool 轮廓工具
|
||||
Sharp 尖角
|
||||
Bake 扩展
|
||||
Turn the Contour into plain paths 将轮廓转为普通路径
|
||||
Set Corners… 设置转角…
|
||||
All Anchors 所有锚点
|
||||
Odd-Numbered Anchors 奇数锚点
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5989,6 +5989,13 @@ breakText Glyphs 字符
|
||||
Fully Outside 完全在外
|
||||
No artboards match. 沒有符合的工作區域。
|
||||
Chosen: {n} of {total} 已選:{n} / {total}
|
||||
|
||||
# Contour add-on
|
||||
Contour 輪廓
|
||||
Contour Tool 輪廓工具
|
||||
Sharp 尖角
|
||||
Bake 展開
|
||||
Turn the Contour into plain paths 將輪廓轉為一般路徑
|
||||
Set Corners… 設定轉角…
|
||||
All Anchors 所有錨點
|
||||
Odd-Numbered Anchors 奇數錨點
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -59,7 +59,6 @@ pub const BASIC: &[(&str, &[&[&str]])] = &[
|
||||
&["rotate", "reflect", "scale", "shear", "reshape", "freeTransform", "puppetWarp"],
|
||||
&["shapeBuilder", "livePaintBucket", "livePaintSelection", "blend"],
|
||||
&["dimension"],
|
||||
&["contour"],
|
||||
],
|
||||
),
|
||||
("Type", &[&["areaType", "typeOnPath", "verticalAreaType", "verticalTypeOnPath"], &["type", "verticalType", "touchType"]]),
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# Gaps
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour tool, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -28,6 +28,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-04 | minor | Contour add-on (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `addon.contour.set` is the command behind it. It is a built-in add-on: Object › Plug-ins › Contour, and its own heading in All Tools, with no default shortcut or toolbar slot. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Contour tool (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `object.contour` is the command behind it. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-05 | minor | G12: Hyphenation › Default Language sets the language of new type unless Document Setup chose one, stored as a tag; each run hyphenates in its own language, English only so far, so 2 Preferences options stay unused (#394) |
|
||||
| 2026-11-03 | minor | New gradients use CMYK default stops in CMYK documents, including Gradient tool creation. Scores and estimates unchanged. |
|
||||
|
||||
+21
@@ -6780,6 +6780,27 @@ The panel sets the next segment and runs Remove Points Keep Shape (`addon.pathto
|
||||
{"name":"run_command","arguments":{"command":"addon.pen.reshape","params":{"id":12,"segment":0,"t":0.3,"dx":0,"dy":-25}}}
|
||||
```
|
||||
|
||||
## Contour add-on
|
||||
|
||||
Contour is a built-in add-on: it grows or shrinks a copy of a path or shape by a distance, live. Its command is
|
||||
`addon.contour.set`; in the desktop app it lives in Object › Plug-ins › Contour and in the Contour Tool (`contour`), which
|
||||
the toolbars don't show until it is added from the All Tools drawer (or chosen with `tool.select`); it has no shortcut. A
|
||||
floating bar over the selection (the distance, Sharp / Round / Square and Bake) acts through the same commands.
|
||||
|
||||
`addon.contour.set {offset, corners?, miterLimit?}` puts a copy of each selected path, shape or compound path directly above
|
||||
it with a live Offset Path effect of `offset` points (negative insets), and selects the copies. `corners` is `"sharp"`
|
||||
(default), `"round"` or `"square"` (corners cut off flat), `miterLimit` 1 to 500 (4; sharp corners). A selected object that
|
||||
already has that effect (a Contour made earlier) has its effect changed instead of getting another, so the same call
|
||||
adjusts a Contour. The original is kept, and nothing is baked: `effect.expandAppearance` turns a Contour into plain paths.
|
||||
Type, images and groups are left alone; with nothing to offset it says "select paths or shapes". One undo step. Result:
|
||||
`{ids, created, updated}`.
|
||||
|
||||
The tool (`contour`) drags a distance from the selected shape's edge, outward to grow and inward to shrink: the press
|
||||
changes nothing until the pointer moves, a Contour that already stands at a distance continues from it, Shift rounds to
|
||||
whole points and Escape cancels. A drag keeps a Contour's own corners and miter limit; a new Contour takes the tool's
|
||||
options `corners` and `miterLimit` (`tool.setOption`). A border with a gap is two Contours of one shape (the gap first, so
|
||||
it ends up in front), baked with `effect.expandAppearance`, then `object.pathfinder.minusFront`.
|
||||
|
||||
## Color Effects add-on
|
||||
|
||||
Color Effects is a built-in add-on: its effects are colour adjustments in Effect › Color Effects, its command is
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Parity with Adobe Illustrator
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour tool, beyond Illustrator; scores and estimates unchanged; Select by Artboards add-on selects by overlap, inside or outside several artboards; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour add-on, beyond Illustrator; scores and estimates unchanged; Optical kerning spaces pairs by the font's own stem spacing instead of a fixed tight gap; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -482,7 +482,7 @@ since 2026-10-01.
|
||||
| **Agent automation** | beyond Illustrator | Every command, gesture and dialog is drivable over MCP, the CLI and the control channel; synthetic-input counts, pending events and per-frame control requests are bounded (#1709); what an agent reads and writes on disk can be confined to chosen folders (`--automation-read-root` / `--automation-write-root`, as in PhotoCraft, #832) |
|
||||
| **Scrubby numeric fields** | beyond Illustrator | As in Photoshop, dragging a numeric field's label (`W:`, `Opacity:`, the Character panel's icons) left or right steps its value live; one undo step per drag, Escape cancels (#400) |
|
||||
| **Align Stroke on live type** | beyond Illustrator | The Stroke panel's Align Stroke works on type's character stroke: inside or outside the glyphs without Create Outlines, the type staying editable (as InDesign does; users have asked Illustrator for it since 2010, e.g. [redd.it/1dwhos6](https://redd.it/1dwhos6)). Canvas, PDF and EPS clip it to its side of the glyphs; SVG keeps the text live and draws the stroke on the glyph outlines (#2294) |
|
||||
| **Contour tool** | beyond Illustrator | Drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it (Shift: whole points); the copy carries a live Offset Path effect so it stays editable, with its own fill and stroke, until Bake (Expand Appearance). A second drag on a selected Contour changes it instead of nesting another; Escape cancels. A floating bar above the selection sets the distance and the corners (Sharp, Round, Square); `object.contour` makes or changes one over MCP and the control channel. Two Contours baked and combined with Minus Front give a border with a gap. Paths, shapes and compound paths only; type isn't offset. Tests: `crates/engine/src/tests/contour.rs`, `crates/ui-egui/src/canvas/tests.rs` |
|
||||
| **Contour add-on** | beyond Illustrator | Drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it (Shift: whole points); the copy carries a live Offset Path effect so it stays editable, with its own fill and stroke, until Bake (Expand Appearance). A second drag on a selected Contour changes it instead of nesting another; Escape cancels. A floating bar above the selection sets the distance and the corners (Sharp, Round, Square); `addon.contour.set` makes or changes one over MCP and the control channel. The tool is in Object › Plug-ins › Contour and under its own heading in All Tools, with no default shortcut or toolbar slot. Two Contours baked and combined with Minus Front give a border with a gap. Paths, shapes and compound paths only; type isn't offset. Tests: `crates/engine/src/cmd/addons/contour/tests.rs`, `crates/ui-egui/src/canvas/tests.rs` |
|
||||
|
||||
## Evidence: what ships today, by area
|
||||
|
||||
@@ -1650,6 +1650,7 @@ Edit › Copy and Cut put the selected ruler guides on the clipboard, with the s
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-04 | minor | Contour add-on (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `addon.contour.set` is the command behind it. It is a built-in add-on: Object › Plug-ins › Contour, and its own heading in All Tools, with no default shortcut or toolbar slot. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Contour tool (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `object.contour` is the command behind it. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-05 | minor | View & navigation: document tabs are reordered by dragging them along the tab strip (`document.move`, #5663); evidence added; scores unchanged. |
|
||||
| 2026-10-30 | minor | Effects: Color Halftone's Max Radius is 4–127 px of the Document Raster Effects resolution, shown in px whatever the units; older documents open converted, and files saved for older app versions hold points (#3260); scores unchanged. |
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour tool, beyond Illustrator; scores and estimates unchanged; Select by Artboards add-on selects by overlap, inside or outside several artboards; Cmd+Space keeps the active drawing cursor during its drag; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (Contour add-on, beyond Illustrator; scores and estimates unchanged; Select by Artboards add-on selects by overlap, inside or outside several artboards; Cmd+Space keeps the active drawing cursor during its drag; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -437,6 +437,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-04 | minor | Contour add-on (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `addon.contour.set` is the command behind it. It is a built-in add-on: Object › Plug-ins › Contour, and its own heading in All Tools, with no default shortcut or toolbar slot. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Contour tool (beyond Illustrator): drag away from a selected shape's edge to grow a live copy of it, or toward the inside to shrink it; the copy carries a live Offset Path effect, so it stays editable until Bake (Expand Appearance), a second drag changes the same Contour instead of nesting one, and a floating bar sets the distance and the Sharp, Round or Square corners. `object.contour` is the command behind it. A VectorCraft design built on Offset Path; no other application was inspected. Scores and estimates unchanged. |
|
||||
| 2026-11-05 | minor | Document tabs are reordered by dragging them along the tab strip (`document.move`, #5663). |
|
||||
| 2026-11-04 | minor | Path Precision Tools add-on (a VectorCraft addition): Set Corners… rounds only the corners in an angle range (#5545); scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user