mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Merge pull request #4543 from storytold/feat/path-smooth
Path: Object › Path › Smooth with a slider and Auto-Smooth (#4115)
This commit is contained in:
@@ -14,7 +14,10 @@ use vectorcraft_pathops as po;
|
||||
use vectorcraft_pathops::{BoolOp, PathfinderOp};
|
||||
|
||||
use super::edit::selected_roots;
|
||||
use super::{CommandSpec, EngineError, Result, Session, always, bad, bool_or, cmd, has_doc, has_multi, has_selection, str_param};
|
||||
use super::{
|
||||
CommandSpec, EngineError, Result, Session, always, bad, bool_or, checked_ids_or_selection, cmd, has_doc, has_multi, has_selection, number_or,
|
||||
str_param, switch_or,
|
||||
};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
@@ -153,6 +156,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_selection,
|
||||
simplify
|
||||
),
|
||||
cmd!(
|
||||
"object.path.smooth",
|
||||
"Smooth…",
|
||||
["Object", "Path"],
|
||||
None,
|
||||
"{level?: 0..100 (50; the Smooth slider: 0 minimum smoothing, the path staying within 0.1% of its longer side, 100 maximum: refitted with curves within up to 2.5% of it, corners kept only where the path turns back, never with more anchor points), auto?: bool (Auto-Smooth: level 50), ids?: [id…] (default: the selection)} smooth each path under the objects, compound path members included, as one undo step → {ids, before, after} anchor counts, and the level used",
|
||||
has_selection,
|
||||
smooth
|
||||
),
|
||||
cmd!("object.path.addAnchorPoints", "Add Anchor Points", ["Object", "Path"], None, "{} → {ids}", has_selection, add_anchor_points),
|
||||
cmd!(
|
||||
"object.path.divideObjectsBelow",
|
||||
@@ -957,20 +969,20 @@ fn path_ids(d: &Document, roots: &[NodeId]) -> Vec<NodeId> {
|
||||
/// selected segments when they were picked as segments). `None` selects the path whole.
|
||||
type PartSelection = Option<(BTreeSet<AnchorRef>, Option<BTreeSet<SegmentRef>>)>;
|
||||
|
||||
/// Replace each path under the selection ([`path_ids`]) by what `f` makes of it, given its
|
||||
/// Replace each path under `roots` ([`path_ids`]) by what `f` makes of it, given its
|
||||
/// direct-selected anchors and picked segments (none when it is selected whole): → (their ids, how
|
||||
/// many anchors they have before and after). A path that `f` would empty or returns unchanged is
|
||||
/// left as it is, with its live shape and its direct selection; a replaced path loses its live
|
||||
/// shape and has the direct selection `f` gives it.
|
||||
fn edit_paths(
|
||||
s: &mut Session,
|
||||
roots: &[NodeId],
|
||||
label: &str,
|
||||
f: impl Fn(&PathData, Option<&BTreeSet<AnchorRef>>, Option<&BTreeSet<SegmentRef>>) -> (PathData, PartSelection),
|
||||
) -> Result<(Vec<NodeId>, usize, usize)> {
|
||||
let roots = selected_roots(s)?;
|
||||
let st = s.doc()?;
|
||||
let (d, sel) = (&st.doc, &st.selection);
|
||||
let ids = path_ids(d, &roots);
|
||||
let ids = path_ids(d, roots);
|
||||
if ids.is_empty() {
|
||||
return Err(EngineError::Other(format!("{label}: select paths")));
|
||||
}
|
||||
@@ -1064,7 +1076,8 @@ fn simplify(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let whole = bool_or(p, "wholePaths", false);
|
||||
// A path with segments direct-selected simplifies only their runs, and keeps those runs (and the
|
||||
// other anchors it had selected) direct-selected.
|
||||
let (ids, before, after) = edit_paths(s, "Simplify", |path, anchors, segments| {
|
||||
let roots = selected_roots(s)?;
|
||||
let (ids, before, after) = edit_paths(s, &roots, "Simplify", |path, anchors, segments| {
|
||||
let Some(picked) = selected_segments(path, anchors, segments).filter(|_| !whole) else {
|
||||
return (po::simplify_with(path, &opts), None);
|
||||
};
|
||||
@@ -1084,8 +1097,20 @@ fn simplify(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Object › Path › Smooth (`object.path.smooth`): [`po::smooth_level`] on each path under the
|
||||
/// objects given or selected, as one undo step; Auto-Smooth (`auto`) uses [`po::SMOOTH_AUTO_LEVEL`].
|
||||
fn smooth(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "object.path.smooth";
|
||||
let level = number_or(p, "level", po::SMOOTH_AUTO_LEVEL, C)?.round().clamp(0.0, 100.0);
|
||||
let level = if switch_or(p, "auto", false, C)? { po::SMOOTH_AUTO_LEVEL } else { level };
|
||||
let roots = checked_ids_or_selection(s, p, C)?;
|
||||
let (ids, before, after) = edit_paths(s, &roots, "Smooth", |path, _, _| (po::smooth_level(path, level), None))?;
|
||||
Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>(), "before": before, "after": after, "level": level }))
|
||||
}
|
||||
|
||||
fn add_anchor_points(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let (ids, before, after) = edit_paths(s, "Add Anchor Points", |path, _, _| (po::add_anchor_points(path), None))?;
|
||||
let roots = selected_roots(s)?;
|
||||
let (ids, before, after) = edit_paths(s, &roots, "Add Anchor Points", |path, _, _| (po::add_anchor_points(path), None))?;
|
||||
Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>(), "before": before, "after": after }))
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ pub use journal::Journal;
|
||||
pub use tooling::{UiRequest, ViewInfo};
|
||||
pub use vectorcraft_doc as doc;
|
||||
pub use vectorcraft_doc::cancel;
|
||||
pub use vectorcraft_pathops::{SIMPLIFY_LEVEL, simplify_level_tolerance};
|
||||
pub use vectorcraft_pathops::{SIMPLIFY_LEVEL, SMOOTH_AUTO_LEVEL, simplify_level_tolerance};
|
||||
pub use vectorcraft_tools as tools;
|
||||
|
||||
/// Coordinates beyond this (points) are rejected: ~1,400 m, far past Illustrator's large canvas.
|
||||
|
||||
@@ -140,6 +140,7 @@ mod shortcutset;
|
||||
mod simplify_part;
|
||||
mod slices;
|
||||
mod smartguides;
|
||||
mod smooth_path;
|
||||
mod store;
|
||||
mod strokegeom;
|
||||
mod strokegradient;
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
//! Object › Path › Smooth (`object.path.smooth`): the slider from minimum to maximum smoothing and
|
||||
//! Auto-Smooth, as the Smooth slider help page describes
|
||||
//! (https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/refine-path-segments-with-the-smooth-slider.html).
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_doc::NodeKind;
|
||||
use vectorcraft_geom::kurbo::ParamCurveNearest;
|
||||
use vectorcraft_geom::{ParamCurve, PathData, Point};
|
||||
|
||||
use crate::*;
|
||||
|
||||
fn session() -> Session {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 800, "height": 600})).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
fn path_of(s: &Session, id: NodeId) -> PathData {
|
||||
match &s.doc().unwrap().doc.node(id).unwrap().kind {
|
||||
NodeKind::Path { path, .. } => path.clone(),
|
||||
_ => panic!("not a path"),
|
||||
}
|
||||
}
|
||||
|
||||
/// A path through `points`, selected: its id.
|
||||
fn polyline(s: &mut Session, points: &[(f64, f64)], closed: bool) -> NodeId {
|
||||
let anchors: Vec<Value> = points.iter().map(|&(x, y)| json!({"x": x, "y": y})).collect();
|
||||
NodeId(s.execute("path.create", &json!({"anchors": anchors, "closed": closed})).unwrap()["id"].as_u64().unwrap())
|
||||
}
|
||||
|
||||
/// A sine wave 300 pt long with an anchor every 3 pt, its anchors 1.5 pt above and below it in turn.
|
||||
fn jagged_wave(s: &mut Session) -> NodeId {
|
||||
let pts: Vec<(f64, f64)> = (0..=100u32)
|
||||
.map(|i| (f64::from(i) * 3.0, 200.0 + 40.0 * (f64::from(i) * 3.0 / 50.0).sin() + if i.is_multiple_of(2) { 1.5 } else { -1.5 }))
|
||||
.collect();
|
||||
polyline(s, &pts, false)
|
||||
}
|
||||
|
||||
/// A closed ring of radius `r` around (`cx`, 300) with 120 anchors, `jag` pt in and out in turn.
|
||||
fn jagged_ring(s: &mut Session, cx: f64, r: f64, jag: f64) -> NodeId {
|
||||
let pts: Vec<(f64, f64)> = (0..120u32)
|
||||
.map(|i| {
|
||||
let a = f64::from(i) * std::f64::consts::TAU / 120.0;
|
||||
let rr = r + if i.is_multiple_of(2) { jag } else { -jag };
|
||||
(cx + rr * a.cos(), 300.0 + rr * a.sin())
|
||||
})
|
||||
.collect();
|
||||
polyline(s, &pts, true)
|
||||
}
|
||||
|
||||
/// The farthest that 11 points on each segment of `from` lie from the nearest point of `to`.
|
||||
fn farthest(from: &PathData, to: &PathData) -> f64 {
|
||||
let off = |p: Point| {
|
||||
to.subpaths
|
||||
.iter()
|
||||
.flat_map(|sp| (0..sp.segment_count()).map(move |i| sp.segment(i).nearest(p, 1e-9).distance_sq.sqrt()))
|
||||
.fold(f64::INFINITY, f64::min)
|
||||
};
|
||||
from.subpaths
|
||||
.iter()
|
||||
.flat_map(|sp| (0..sp.segment_count()).flat_map(move |i| (0..=10).map(move |k| sp.segment(i).eval(f64::from(k) / 10.0))))
|
||||
.map(off)
|
||||
.fold(0.0, f64::max)
|
||||
}
|
||||
|
||||
fn undo_len(s: &Session) -> usize {
|
||||
s.doc().unwrap().history.undo.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_smoothing_leaves_the_path_where_it_was() {
|
||||
let mut s = session();
|
||||
let id = jagged_wave(&mut s);
|
||||
let before = path_of(&s, id);
|
||||
let out = s.execute("object.path.smooth", &json!({"level": 0})).unwrap();
|
||||
assert_eq!(out["level"], json!(0.0));
|
||||
let after = path_of(&s, id);
|
||||
// 0.1 % of the longer side (300 pt), both ways.
|
||||
assert!(farthest(&before, &after) <= 0.3 + 1e-6 && farthest(&after, &before) <= 0.3 + 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn more_smoothing_removes_the_jags_and_keeps_the_shape() {
|
||||
let mut s = session();
|
||||
let id = jagged_wave(&mut s);
|
||||
let before = path_of(&s, id);
|
||||
let mut counts = vec![];
|
||||
for level in [25, 50, 100] {
|
||||
let out = s.execute("object.path.smooth", &json!({ "level": level })).unwrap();
|
||||
let after = path_of(&s, id);
|
||||
assert_eq!(out["after"].as_u64().unwrap() as usize, after.anchor_count());
|
||||
counts.push(after.anchor_count());
|
||||
// Within the tolerance (2.5 % of 300 pt at most) plus the jag the refit smooths away.
|
||||
assert!(farthest(&before, &after) <= 7.5 + 1.5, "level {level}: {}", farthest(&before, &after));
|
||||
// The end points of an open path stay put.
|
||||
let (a, b) = (&before.subpaths[0].anchors, &after.subpaths[0].anchors);
|
||||
assert_eq!((a[0].p, a[a.len() - 1].p), (b[0].p, b[b.len() - 1].p));
|
||||
assert!(!after.subpaths[0].closed);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
}
|
||||
assert!(counts[0] < 101 / 2 && counts[2] <= 10, "{counts:?}");
|
||||
assert!(counts.windows(2).all(|w| w[0] >= w[1]), "{counts:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_smooth_uses_the_middle_level() {
|
||||
let mut s = session();
|
||||
let id = jagged_wave(&mut s);
|
||||
let out = s.execute("object.path.smooth", &json!({"auto": true, "level": 0})).unwrap();
|
||||
assert_eq!(out["level"], json!(50.0));
|
||||
let auto = path_of(&s, id);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
s.execute("object.path.smooth", &json!({"level": 50})).unwrap();
|
||||
assert_eq!(path_of(&s, id), auto);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closed_paths_and_compound_path_members_smooth_in_one_undo_step() {
|
||||
let mut s = session();
|
||||
let outer = jagged_ring(&mut s, 300.0, 100.0, 2.0);
|
||||
let inner = jagged_ring(&mut s, 300.0, 40.0, 1.0);
|
||||
s.execute("select.set", &json!({"ids": [outer.0, inner.0]})).unwrap();
|
||||
s.execute("object.compoundPath.make", &json!({})).unwrap();
|
||||
let (o, i) = (path_of(&s, outer), path_of(&s, inner));
|
||||
let steps = undo_len(&s);
|
||||
let out = s.execute("object.path.smooth", &json!({"level": 100})).unwrap();
|
||||
assert_eq!(out["ids"].as_array().unwrap().len(), 2);
|
||||
assert_eq!(undo_len(&s), steps + 1);
|
||||
for id in [outer, inner] {
|
||||
let now = path_of(&s, id);
|
||||
assert!(now.subpaths[0].closed);
|
||||
assert!(now.anchor_count() < 120 / 4, "{}", now.anchor_count());
|
||||
}
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!((path_of(&s, outer), path_of(&s, inner)), (o, i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smooths_the_objects_given() {
|
||||
let mut s = session();
|
||||
let a = jagged_wave(&mut s);
|
||||
let b = jagged_ring(&mut s, 300.0, 50.0, 1.0);
|
||||
let ring = path_of(&s, b);
|
||||
// `b` is selected; `ids` names `a` instead.
|
||||
let out = s.execute("object.path.smooth", &json!({"ids": [a.0], "level": 100})).unwrap();
|
||||
assert_eq!(out["ids"], json!([a.0]));
|
||||
assert_eq!(path_of(&s, b), ring);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_params_are_refused_and_change_nothing() {
|
||||
let mut s = session();
|
||||
let id = jagged_wave(&mut s);
|
||||
let before = path_of(&s, id);
|
||||
let steps = undo_len(&s);
|
||||
for p in [json!({"level": "50"}), json!({"auto": true, "level": "max"}), json!({"auto": "yes"}), json!({"ids": "all"}), json!({"ids": [999_999]})]
|
||||
{
|
||||
assert!(s.execute("object.path.smooth", &p).is_err(), "{p}");
|
||||
}
|
||||
assert_eq!((path_of(&s, id), undo_len(&s)), (before, steps));
|
||||
// Levels past the ends count as the ends.
|
||||
assert_eq!(s.execute("object.path.smooth", &json!({"level": 250})).unwrap()["level"], json!(100.0));
|
||||
}
|
||||
@@ -314,6 +314,55 @@ pub fn smooth(path: &PathData, amount: f64) -> PathData {
|
||||
out
|
||||
}
|
||||
|
||||
/// The Smooth slider's level Auto-Smooth picks, halfway between minimum and maximum smoothing.
|
||||
/// VectorCraft's own rule: the help page says only that Auto-Smooth smooths the path automatically
|
||||
/// (https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/refine-path-segments-with-the-smooth-slider.html).
|
||||
/// [`smooth_level`] already scales with each path's size, so one level suits every path.
|
||||
pub const SMOOTH_AUTO_LEVEL: f64 = 50.0;
|
||||
|
||||
/// Object › Path › Smooth at slider level `level`, from 0 (minimum smoothing: the path stays within
|
||||
/// 0.1 % of its longer side of where it was) to 100 (maximum). The help page names only the two
|
||||
/// ends; the rest is VectorCraft's own rule. Each subpath is refitted with curves as Simplify does,
|
||||
/// but keeping a corner only where the path turns back on itself (so jags and kinks become curves),
|
||||
/// within a tolerance from 0.1 % of the path's longer side at level 0 to 2.5 % at level 100. A refit
|
||||
/// never has more anchor points than the subpath had: a shape whose corners the tolerance can't
|
||||
/// round, such as a rectangle, stays as it is. Closed subpaths stay closed, with no kink left where
|
||||
/// they start, and open ones keep their end points. A level outside 0..=100 counts as the nearer end, and one that is not a number as
|
||||
/// [`SMOOTH_AUTO_LEVEL`].
|
||||
pub fn smooth_level(path: &PathData, level: f64) -> PathData {
|
||||
let level = if level.is_finite() { level.clamp(0.0, 100.0) } else { SMOOTH_AUTO_LEVEL };
|
||||
let size = path.bounds().map_or(0.0, |b| b.width().max(b.height()));
|
||||
if !size.is_finite() {
|
||||
return path.clone();
|
||||
}
|
||||
let opts = SimplifyOptions { tolerance: size * (0.001 + 0.024 * level / 100.0), corner_angle_deg: SMOOTH_CORNER_DEG, straight_lines: false };
|
||||
let smoothed = |sp: &SubPath| {
|
||||
let Some(mut fit) = simplify_subpath(sp, &opts).filter(|f| f != sp) else { return sp.clone() };
|
||||
smooth_seam(&mut fit);
|
||||
fit
|
||||
};
|
||||
PathData::new(path.subpaths.iter().map(smoothed).collect())
|
||||
}
|
||||
|
||||
/// [`smooth_level`] keeps a corner only where the path turns by more than this (turns back).
|
||||
const SMOOTH_CORNER_DEG: f64 = 179.0;
|
||||
|
||||
/// A closed subpath's refit leaves its start anchor with the tangents of the original segments on
|
||||
/// either side of it, a kink when the path had one there: turn it into a smooth anchor with the same
|
||||
/// handle lengths, unless the path turns back there.
|
||||
fn smooth_seam(sp: &mut SubPath) {
|
||||
if !sp.closed {
|
||||
return;
|
||||
}
|
||||
let Some(a) = sp.anchors.first_mut() else { return };
|
||||
let (Some(i), Some(o)) = (crate::fit::unit(a.p - a.h_in), crate::fit::unit(a.h_out - a.p)) else { return };
|
||||
if i.dot(o) < SMOOTH_CORNER_DEG.to_radians().cos() {
|
||||
return;
|
||||
}
|
||||
let Some(dir) = crate::fit::unit(i + o) else { return };
|
||||
*a = Anchor::with_handles(a.p, a.p - dir * a.h_in.distance(a.p), a.p + dir * a.h_out.distance(a.p));
|
||||
}
|
||||
|
||||
/// Remove anchors that can be deleted without changing the shape by more than `tolerance`
|
||||
/// (collinear corner points, duplicate points, and curve joints that one cubic can replace).
|
||||
pub fn remove_redundant_points(path: &PathData, tolerance: f64) -> PathData {
|
||||
|
||||
@@ -20,8 +20,8 @@ pub use boolean::{
|
||||
try_unite_all_with, unite_all, unite_all_with,
|
||||
};
|
||||
pub use edit::{
|
||||
AverageAxis, SIMPLIFY_LEVEL, SimplifiedPart, SimplifyOptions, add_anchor_points, auto_simplify_level, average, join, remove_anchor,
|
||||
remove_redundant_points, simplify, simplify_level_tolerance, simplify_part, simplify_with, smooth, split_into_grid,
|
||||
AverageAxis, SIMPLIFY_LEVEL, SMOOTH_AUTO_LEVEL, SimplifiedPart, SimplifyOptions, add_anchor_points, auto_simplify_level, average, join,
|
||||
remove_anchor, remove_redundant_points, simplify, simplify_level_tolerance, simplify_part, simplify_with, smooth, smooth_level, split_into_grid,
|
||||
};
|
||||
pub use offset::{Cap, Join, offset_path, outline_stroke, stroke_region};
|
||||
pub use pathfinder::{FaceMerger, PathfinderOp, Region, Shape, merge_regions, pathfinder, pathfinder_with, region_at, regions};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use kurbo::{ParamCurve, Point, Rect, Shape as _};
|
||||
use vectorcraft_geom::{FillRule, PathData, SubPath};
|
||||
use vectorcraft_geom::{AnchorKind, FillRule, PathData, SubPath};
|
||||
use vectorcraft_pathops::*;
|
||||
|
||||
const NZ: FillRule = FillRule::NonZero;
|
||||
@@ -430,6 +430,21 @@ fn smooth_rounds_corners() {
|
||||
assert_eq!(same, rect(0.0, 0.0, 10.0, 10.0));
|
||||
}
|
||||
|
||||
/// Object › Path › Smooth: a rectangle, whose corners no curve rounds within the tolerance, stays as
|
||||
/// it is even at maximum smoothing; a polygon of 16 sides becomes a closed curve of fewer anchors once the tolerance
|
||||
/// passes its 0.96 pt sagitta, smooth all round.
|
||||
#[test]
|
||||
fn smooth_level_keeps_corners_it_cannot_round_and_curves_polygons() {
|
||||
let r = rect(0.0, 0.0, 100.0, 50.0);
|
||||
assert_eq!(smooth_level(&r, 100.0), r);
|
||||
let pts: Vec<Point> = (0..16).map(|i| Point::new(50.0 * (f64::from(i) * PI / 8.0).cos(), 50.0 * (f64::from(i) * PI / 8.0).sin())).collect();
|
||||
let polygon = PathData::single(SubPath::polyline(&pts, true));
|
||||
let s = smooth_level(&polygon, SMOOTH_AUTO_LEVEL);
|
||||
assert!(s.subpaths.len() == 1 && s.subpaths[0].closed && s.anchor_count() < 8, "{}", s.anchor_count());
|
||||
assert!(s.subpaths[0].anchors.iter().all(|a| a.kind == AnchorKind::Smooth), "no kink, its start included: {s:?}");
|
||||
assert_eq!(smooth_level(&polygon, 0.0), polygon, "0.1 pt is less than the sagitta");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_redundant_collinear_and_curve() {
|
||||
let p = PathData::single(SubPath::polyline(
|
||||
|
||||
@@ -327,8 +327,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
if app.ui.view.rulers && app.ui.screen_mode < 3 {
|
||||
ruler_bands(app, ui, &xf, (full, rect), origin_preview, &t);
|
||||
}
|
||||
// The task bar is not drawn while the Simplify slider shows.
|
||||
if app.ui.task_bar && !revolve_edit && !app.session.tool_busy() && app.ui.screen_mode < 3 && !crate::dialogs::simplify_slider::showing(app) {
|
||||
// The task bar is not drawn while the Simplify or Smooth slider shows.
|
||||
let strip = crate::dialogs::simplify_slider::showing(app) || crate::dialogs::smooth_slider::showing(app);
|
||||
if app.ui.task_bar && !revolve_edit && !app.session.tool_busy() && app.ui.screen_mode < 3 && !strip {
|
||||
task_bar(app, ui, &xf);
|
||||
}
|
||||
if app.ui.screen_mode < 3 {
|
||||
|
||||
@@ -99,6 +99,7 @@ pub(crate) mod shapes;
|
||||
pub mod shortcut_import;
|
||||
pub(crate) mod simplify_slider;
|
||||
pub mod slices;
|
||||
pub(crate) mod smooth_slider;
|
||||
pub mod spot_colors;
|
||||
pub(crate) mod svg_options;
|
||||
pub mod swatch_conflict;
|
||||
@@ -352,6 +353,7 @@ registry! {
|
||||
ScriptCheck: [script_check::KIND] => script_check::SPEC,
|
||||
VectorHalftone: [halftone::KIND] => halftone::SPEC,
|
||||
SimplifySlider: [simplify_slider::KIND] => simplify_slider::SPEC,
|
||||
SmoothSlider: [smooth_slider::KIND] => smooth_slider::SPEC,
|
||||
PerspectiveGrid: [perspective_grid::KIND] => perspective_grid::SPEC,
|
||||
Envelope: [envelope::WARP, envelope::MESH, envelope::OPTIONS] => envelope::SPEC,
|
||||
LiquifyOptions: [liquify::KIND] => liquify::SPEC,
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
//! (https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html).
|
||||
//! `vectorcraft_pathops::simplify_level_tolerance` maps levels to tolerances.
|
||||
//!
|
||||
//! The strip itself ([`Strip`]) is shared with Object › Path › Smooth's slider (`smooth_slider`).
|
||||
//!
|
||||
//! Fields: `level` (0..=100, the command's parameter).
|
||||
|
||||
use egui::{Color32, CornerRadius, Order, Pos2, Sense, Stroke, vec2};
|
||||
@@ -25,9 +27,122 @@ pub(crate) const KIND: &str = "simplifySlider";
|
||||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec { window: Some(show), confirm, preview: true, ..DialogSpec::FORM };
|
||||
|
||||
/// The command the slider previews, and its undo label.
|
||||
const CMD: &str = "object.path.simplify";
|
||||
const LABEL: &str = "Simplify";
|
||||
/// What the Simplify slider previews, and its buttons.
|
||||
const STRIP: Strip = Strip {
|
||||
kind: KIND,
|
||||
cmd: "object.path.simplify",
|
||||
label: "Simplify",
|
||||
default_level: SIMPLIFY_LEVEL,
|
||||
auto: ("Auto-Simplify", auto),
|
||||
more_options: Some(more_options),
|
||||
};
|
||||
|
||||
/// A UI command a button of a strip runs.
|
||||
type Button = fn(&mut VectorcraftApp) -> Result<Value, String>;
|
||||
|
||||
/// A slider strip under the selection that previews `cmd {level}` while it is open: its first
|
||||
/// button picks the level `cmd {auto: true}` does, and an optional last one, More Options, opens a
|
||||
/// dialog in its place. Enter or a click outside the strip keeps the result as one undo step, and
|
||||
/// Escape puts the paths back.
|
||||
pub(super) struct Strip {
|
||||
/// The dialog kind.
|
||||
pub kind: &'static str,
|
||||
/// The command previewed.
|
||||
pub cmd: &'static str,
|
||||
/// The undo step's name, and the slider's.
|
||||
pub label: &'static str,
|
||||
/// The level when the field holds no number.
|
||||
pub default_level: f64,
|
||||
/// The auto button's tip, and what it runs.
|
||||
pub auto: (&'static str, Button),
|
||||
/// What More Options runs, when the strip has it.
|
||||
pub more_options: Option<Button>,
|
||||
}
|
||||
|
||||
impl Strip {
|
||||
/// Whether this strip is open.
|
||||
pub fn showing(&self, app: &VectorcraftApp) -> bool {
|
||||
app.ui.dialog.as_ref().is_some_and(|d| d.kind == self.kind)
|
||||
}
|
||||
|
||||
/// The open strip, if it is this one.
|
||||
pub fn open(&self, app: &VectorcraftApp) -> Option<Dialog> {
|
||||
app.ui.dialog.clone().filter(|d| d.kind == self.kind)
|
||||
}
|
||||
|
||||
/// The slider's level, a whole number from 0 to 100.
|
||||
fn level(&self, d: &Dialog) -> f64 {
|
||||
let l = d.f64("level", self.default_level);
|
||||
if l.is_finite() { l.round().clamp(0.0, 100.0) } else { self.default_level }
|
||||
}
|
||||
|
||||
fn params(&self, d: &Dialog) -> Value {
|
||||
json!({ "level": self.level(d) })
|
||||
}
|
||||
|
||||
pub fn confirm(&self, app: &mut VectorcraftApp, d: &Dialog) -> DialogResult {
|
||||
form::commit_preview(app, self.cmd, self.params(d))
|
||||
}
|
||||
|
||||
/// The open strip `d` moves to the level `cmd {auto: true}` picks for the selection, and the
|
||||
/// canvas previews it → {level}.
|
||||
pub fn auto(&self, app: &mut VectorcraftApp, mut d: Dialog) -> Result<Value, String> {
|
||||
let _ = app.session.begin_interaction(self.label);
|
||||
let r = app.session.preview(self.cmd, &json!({ "auto": true })).map_err(|e| e.to_string())?;
|
||||
let level = r["level"].as_f64().unwrap_or(self.default_level);
|
||||
d.fields.insert("level".into(), json!(level));
|
||||
d.fields.insert(form::PREVIEWED.into(), self.params(&d));
|
||||
app.ui.dialog = Some(d);
|
||||
Ok(json!({ "level": level }))
|
||||
}
|
||||
|
||||
pub fn show(&self, app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
let Some(mut d) = app.ui.dialog.clone() else { return };
|
||||
let t = Tokens::get(ctx);
|
||||
let id = egui::Id::unique(("dialog", self.kind));
|
||||
let b = bounds(app, &mut d);
|
||||
let pos = place(app, ctx, id, b);
|
||||
// A click anywhere else keeps the result, and the rest of the app doesn't take it.
|
||||
let dim = modal::backdrop();
|
||||
let outside =
|
||||
egui::Area::new(dim.id).order(dim.order).fixed_pos(Pos2::ZERO).show(ctx, |ui| ui.allocate_rect(ctx.content_rect(), Sense::click())).inner;
|
||||
let mut action: Option<Button> = None;
|
||||
egui::Area::new(id).order(Order::Foreground).fixed_pos(pos).show(ctx, |ui| {
|
||||
egui::Frame::NONE
|
||||
.fill(t.panel)
|
||||
.stroke(Stroke::new(1.0, t.tool_active))
|
||||
.corner_radius(CornerRadius::same(5))
|
||||
.inner_margin(egui::Margin::symmetric(8, 6))
|
||||
.shadow(egui::epaint::Shadow { offset: [0, 3], blur: 10, spread: 0, color: Color32::from_black_alpha(70) })
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 6.0;
|
||||
if widgets::icon_button(ui, "wand-sparkles", tl!(self.auto.0), false, 28.0).clicked() {
|
||||
action = Some(self.auto.1);
|
||||
}
|
||||
let rail = t.input_border;
|
||||
form::slider_rail_named(ui, &mut d, "level", tl!(self.label), 0.0..=100.0, &|_| rail);
|
||||
if let Some(more) = self.more_options
|
||||
&& widgets::icon_button(ui, "ellipsis", tl!("More Options"), false, 28.0).clicked()
|
||||
{
|
||||
action = Some(more);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
let p = self.params(&d);
|
||||
let _ = form::preview_now(app, &mut d, self.label, self.cmd, p);
|
||||
app.ui.dialog = Some(d);
|
||||
let r = match action {
|
||||
Some(run) => run(app),
|
||||
None if outside.clicked() || ctx.input(|i| i.key_pressed(egui::Key::Enter)) => super::confirm(app),
|
||||
None => Ok(Value::Null),
|
||||
};
|
||||
if let Err(e) = r {
|
||||
app.status(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields the slider opens with: Auto-Simplify's level for the selection, or
|
||||
/// [`SIMPLIFY_LEVEL`] without a document.
|
||||
@@ -37,46 +152,29 @@ pub(crate) fn fields(app: &VectorcraftApp) -> Value {
|
||||
|
||||
/// Whether the Simplify slider is open.
|
||||
pub(crate) fn showing(app: &VectorcraftApp) -> bool {
|
||||
app.ui.dialog.as_ref().is_some_and(|d| d.kind == KIND)
|
||||
}
|
||||
|
||||
/// The slider's level, a whole number from 0 to 100.
|
||||
fn level(d: &Dialog) -> f64 {
|
||||
let l = d.f64("level", SIMPLIFY_LEVEL);
|
||||
if l.is_finite() { l.round().clamp(0.0, 100.0) } else { SIMPLIFY_LEVEL }
|
||||
}
|
||||
|
||||
fn params(d: &Dialog) -> Value {
|
||||
json!({ "level": level(d) })
|
||||
STRIP.showing(app)
|
||||
}
|
||||
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> DialogResult {
|
||||
form::commit_preview(app, CMD, params(d))
|
||||
STRIP.confirm(app, d)
|
||||
}
|
||||
|
||||
/// The open slider, or an error when it isn't open.
|
||||
fn open_slider(app: &VectorcraftApp) -> Result<Dialog, String> {
|
||||
app.ui.dialog.clone().filter(|d| d.kind == KIND).ok_or_else(|| "the Simplify slider is not open".to_string())
|
||||
fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
STRIP.show(app, ctx);
|
||||
}
|
||||
|
||||
/// Auto-Simplify (`ui.simplifyAuto`): the open slider moves to the level `object.path.simplify
|
||||
/// {auto: true}` picks for the selection, and the canvas previews it → {level}.
|
||||
pub(crate) fn auto(app: &mut VectorcraftApp) -> Result<Value, String> {
|
||||
let mut d = open_slider(app)?;
|
||||
let _ = app.session.begin_interaction(LABEL);
|
||||
let r = app.session.preview(CMD, &json!({ "auto": true })).map_err(|e| e.to_string())?;
|
||||
let level = r["level"].as_f64().unwrap_or(SIMPLIFY_LEVEL);
|
||||
d.fields.insert("level".into(), json!(level));
|
||||
d.fields.insert(form::PREVIEWED.into(), params(&d));
|
||||
app.ui.dialog = Some(d);
|
||||
Ok(json!({ "level": level }))
|
||||
let d = STRIP.open(app).ok_or_else(|| "the Simplify slider is not open".to_string())?;
|
||||
STRIP.auto(app, d)
|
||||
}
|
||||
|
||||
/// More Options (`ui.simplifyMoreOptions`): the Simplify dialog takes the slider's place, at the
|
||||
/// slider's tolerance → {tolerance}.
|
||||
pub(crate) fn more_options(app: &mut VectorcraftApp) -> Result<Value, String> {
|
||||
let d = open_slider(app)?;
|
||||
let tolerance = simplify_level_tolerance(level(&d));
|
||||
let d = STRIP.open(app).ok_or_else(|| "the Simplify slider is not open".to_string())?;
|
||||
let tolerance = simplify_level_tolerance(STRIP.level(&d));
|
||||
app.ui.dialog = Some(Dialog::new("simplify", super::path_ops::simplify_dialog(tolerance)));
|
||||
Ok(json!({ "tolerance": tolerance }))
|
||||
}
|
||||
@@ -115,55 +213,3 @@ fn place(app: &mut VectorcraftApp, ctx: &egui::Context, id: egui::Id, bounds: Op
|
||||
let bounds = rect.shrink(8.0);
|
||||
under.clamp(bounds.min, (bounds.max - size).max(bounds.min))
|
||||
}
|
||||
|
||||
/// What a button of the strip asked for.
|
||||
enum Action {
|
||||
Auto,
|
||||
MoreOptions,
|
||||
}
|
||||
|
||||
fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
let Some(mut d) = app.ui.dialog.clone() else { return };
|
||||
let t = Tokens::get(ctx);
|
||||
let id = egui::Id::unique(("dialog", KIND));
|
||||
let b = bounds(app, &mut d);
|
||||
let pos = place(app, ctx, id, b);
|
||||
// A click anywhere else keeps the result, and the rest of the app doesn't take it.
|
||||
let dim = modal::backdrop();
|
||||
let outside =
|
||||
egui::Area::new(dim.id).order(dim.order).fixed_pos(Pos2::ZERO).show(ctx, |ui| ui.allocate_rect(ctx.content_rect(), Sense::click())).inner;
|
||||
let mut action = None;
|
||||
egui::Area::new(id).order(Order::Foreground).fixed_pos(pos).show(ctx, |ui| {
|
||||
egui::Frame::NONE
|
||||
.fill(t.panel)
|
||||
.stroke(Stroke::new(1.0, t.tool_active))
|
||||
.corner_radius(CornerRadius::same(5))
|
||||
.inner_margin(egui::Margin::symmetric(8, 6))
|
||||
.shadow(egui::epaint::Shadow { offset: [0, 3], blur: 10, spread: 0, color: Color32::from_black_alpha(70) })
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 6.0;
|
||||
if widgets::icon_button(ui, "wand-sparkles", tl!("Auto-Simplify"), false, 28.0).clicked() {
|
||||
action = Some(Action::Auto);
|
||||
}
|
||||
let rail = t.input_border;
|
||||
form::slider_rail_named(ui, &mut d, "level", tl!("Simplify"), 0.0..=100.0, &|_| rail);
|
||||
if widgets::icon_button(ui, "ellipsis", tl!("More Options"), false, 28.0).clicked() {
|
||||
action = Some(Action::MoreOptions);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
let p = params(&d);
|
||||
let _ = form::preview_now(app, &mut d, LABEL, CMD, p);
|
||||
app.ui.dialog = Some(d);
|
||||
let r = match action {
|
||||
Some(Action::Auto) => auto(app),
|
||||
Some(Action::MoreOptions) => more_options(app),
|
||||
None if outside.clicked() || ctx.input(|i| i.key_pressed(egui::Key::Enter)) => super::confirm(app),
|
||||
None => Ok(Value::Null),
|
||||
};
|
||||
if let Err(e) = r {
|
||||
app.status(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//! Object › Path › Smooth's slider, as the Smooth slider help page describes
|
||||
//! (https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/refine-path-segments-with-the-smooth-slider.html):
|
||||
//! the strip of Simplify's slider (`simplify_slider::Strip`) with Auto-Smooth and a slider from
|
||||
//! minimum smoothing (left) to maximum smoothing (right). While it is open the canvas previews
|
||||
//! `object.path.smooth {level}`; Enter or a click outside the strip keeps the result as one undo
|
||||
//! step, and Escape puts the paths back. It opens at Auto-Smooth's level, as Simplify's slider
|
||||
//! opens at Auto-Simplify's (VectorCraft's choice: the page doesn't say where it starts).
|
||||
//!
|
||||
//! Fields: `level` (0..=100, the command's parameter).
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_engine::SMOOTH_AUTO_LEVEL;
|
||||
|
||||
use super::simplify_slider::Strip;
|
||||
use super::{DialogResult, DialogSpec};
|
||||
use crate::VectorcraftApp;
|
||||
use crate::state::Dialog;
|
||||
|
||||
/// The dialog kind of the Smooth slider.
|
||||
pub(crate) const KIND: &str = "smoothSlider";
|
||||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec { window: Some(show), confirm, preview: true, ..DialogSpec::FORM };
|
||||
|
||||
/// What the Smooth slider previews, and its button.
|
||||
const STRIP: Strip = Strip {
|
||||
kind: KIND,
|
||||
cmd: "object.path.smooth",
|
||||
label: "Smooth",
|
||||
default_level: SMOOTH_AUTO_LEVEL,
|
||||
auto: ("Auto-Smooth", auto),
|
||||
more_options: None,
|
||||
};
|
||||
|
||||
/// The fields the slider opens with: Auto-Smooth's level.
|
||||
pub(crate) fn fields() -> Value {
|
||||
json!({ "level": SMOOTH_AUTO_LEVEL })
|
||||
}
|
||||
|
||||
/// Whether the Smooth slider is open.
|
||||
pub(crate) fn showing(app: &VectorcraftApp) -> bool {
|
||||
STRIP.showing(app)
|
||||
}
|
||||
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> DialogResult {
|
||||
STRIP.confirm(app, d)
|
||||
}
|
||||
|
||||
fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
STRIP.show(app, ctx);
|
||||
}
|
||||
|
||||
/// Auto-Smooth (`ui.smoothAuto`): the open slider moves to the level `object.path.smooth {auto:
|
||||
/// true}` picks, and the canvas previews it → {level}.
|
||||
pub(crate) fn auto(app: &mut VectorcraftApp) -> Result<Value, String> {
|
||||
let d = STRIP.open(app).ok_or_else(|| "the Smooth slider is not open".to_string())?;
|
||||
STRIP.auto(app, d)
|
||||
}
|
||||
@@ -513,6 +513,38 @@ fn simplify_slider_and_dialog_simplify_only_the_direct_selected_part_of_a_path()
|
||||
kept(&app, "the dialog");
|
||||
}
|
||||
|
||||
/// Object › Path › Smooth opens its slider (#4115) at the Auto-Smooth level, previewing that
|
||||
/// result; moving it previews the level, Auto-Smooth (the strip's only button) moves it back, and a
|
||||
/// click outside keeps the result as one undo step.
|
||||
#[test]
|
||||
fn smooth_slider_previews_auto_smooth_and_keeps_the_result() {
|
||||
let ctx = ctx();
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
let anchors: Vec<_> =
|
||||
(0..=60).map(|i| json!({"x": 20 + i * 5, "y": 150.0 + 40.0 * (f64::from(i) / 10.0).sin() + f64::from(i % 2) * 3.0})).collect();
|
||||
app.run("path.create", json!({"anchors": anchors})).unwrap();
|
||||
let before = app.session.doc().unwrap().doc.clone();
|
||||
let undo_len = app.session.doc().unwrap().history.undo.len();
|
||||
assert!(app.run("ui.smoothAuto", json!({})).is_err(), "the slider is not open");
|
||||
crate::menus::invoke(&mut app, "object.path.smooth", json!({}));
|
||||
assert_eq!(app.ui.dialog.as_ref().map(|d| (d.kind.as_str(), d.f64("level", -1.0))), Some((smooth_slider::KIND, 50.0)));
|
||||
settle(&ctx, &mut app);
|
||||
assert_eq!(simplify_preview(&app), json!({"level": 50.0}));
|
||||
assert!(anchor_count(&app) < 61 / 4, "the preview smooths the jags: {}", anchor_count(&app));
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("level".into(), json!(0));
|
||||
settle(&ctx, &mut app);
|
||||
assert_eq!(simplify_preview(&app), json!({"level": 0.0}));
|
||||
let strip = rect_of(&ctx, egui::Id::unique(("dialog", smooth_slider::KIND)));
|
||||
click(&ctx, &mut app, Pos2::new(strip.left() + 8.0 + 14.0, strip.center().y));
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().f64("level", 0.0), 50.0);
|
||||
assert_eq!(simplify_preview(&app), json!({"auto": true}));
|
||||
click(&ctx, &mut app, Pos2::new(20.0, 20.0));
|
||||
assert!(app.ui.dialog.is_none() && !app.session.in_interaction());
|
||||
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo_len + 1);
|
||||
assert_ne!(app.session.doc().unwrap().doc, before);
|
||||
}
|
||||
|
||||
/// More Options opens the Simplify dialog at the slider's tolerance. With "Open this dialog with
|
||||
/// these settings next time" ticked, OK makes the menu item open the dialog on its settings; with
|
||||
/// it unticked, the slider again.
|
||||
|
||||
@@ -2950,6 +2950,7 @@ font-technology Variable Променливи
|
||||
Show Original Path Показване на оригиналния път
|
||||
Open this dialog with these settings next time Отваряне на този прозорец с тези настройки следващия път
|
||||
Auto-Simplify Автоматично опростяване
|
||||
Auto-Smooth Автоматично изглаждане
|
||||
Show Package Показване на пакет
|
||||
Show Panel Показване на панел
|
||||
Show Pattern Swatches Показване на образци за мостра
|
||||
@@ -2984,6 +2985,7 @@ font-technology Variable Променливи
|
||||
Simplify Опростяване
|
||||
Simplify: Опростяване:
|
||||
Simplify… Опростяване…
|
||||
Smooth… Изглаждане…
|
||||
Simulate Colored Paper Симулиране на цветна хартия
|
||||
Simulate Paper Симулиране на хартия
|
||||
Double Column Две колони
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -224,6 +224,7 @@
|
||||
Offset Path… Odsadit cestu…
|
||||
Reverse Path Direction Obrátit směr cesty
|
||||
Simplify… Zjednodušit…
|
||||
Smooth… Vyhladit…
|
||||
Add Anchor Points Přidat kotevní body
|
||||
Remove Anchor Points Odstranit kotevní body
|
||||
Divide Objects Below Rozdělit objekty pod
|
||||
@@ -1755,6 +1756,7 @@ lighting Light Světlo
|
||||
Show Original Path Zobrazit původní cestu
|
||||
Open this dialog with these settings next time Příště otevřít toto dialogové okno s tímto nastavením
|
||||
Auto-Simplify Automaticky zjednodušit
|
||||
Auto-Smooth Automaticky vyhladit
|
||||
Transparency Options Možnosti průhlednosti
|
||||
Color Mixer (Shift-click the chip) Míchání barev (Shift+kliknutí na políčko)
|
||||
Fill: click for swatches, Shift-click for the color mixer Výplň: kliknutím otevřete vzorník, Shift+kliknutím míchání barev
|
||||
@@ -4307,6 +4309,7 @@ imageTrace Outline Obrys
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio musí být číslo od 0,1 do 10
|
||||
@msg the Color Picker is not open Výběr barvy není otevřený
|
||||
@msg the Simplify slider is not open Posuvník Zjednodušit není otevřený
|
||||
@msg the Smooth slider is not open Posuvník Vyhladit není otevřený
|
||||
@msg the screen color sampler is already open nástroj pro nabírání barvy z obrazovky už je otevřený
|
||||
@msg this system has no screen color sampler tento systém nemá nástroj pro nabírání barvy z obrazovky
|
||||
@msg bad colour `{hex}` (#rrggbb) chybná barva `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -208,6 +208,7 @@
|
||||
Offset Path… Forskyd kurve…
|
||||
Reverse Path Direction Omvend kurveretning
|
||||
Simplify… Forenkl…
|
||||
Smooth… Udglat…
|
||||
Add Anchor Points Tilføj ankerpunkter
|
||||
Remove Anchor Points Fjern ankerpunkter
|
||||
Divide Objects Below Opdel objekter nedenunder
|
||||
@@ -5196,6 +5197,7 @@ swatch Bright Violet Kraftig violet
|
||||
Show Original Path Vis oprindelig kurve
|
||||
Open this dialog with these settings next time Åbn denne dialogboks med disse indstillinger næste gang
|
||||
Auto-Simplify Forenkl automatisk
|
||||
Auto-Smooth Udglat automatisk
|
||||
@msg check a layer to export markér et lag, der skal eksporteres
|
||||
@msg select the art to export first markér først grafikken, der skal eksporteres
|
||||
@msg no saved selection recalled ingen gemt markering hentet
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2981,6 +2981,7 @@ mark-style Japanese Japanisch
|
||||
Show Original Path Originalpfad einblenden
|
||||
Open this dialog with these settings next time Dieses Dialogfeld nächstes Mal mit diesen Einstellungen öffnen
|
||||
Auto-Simplify Automatisch vereinfachen
|
||||
Auto-Smooth Automatisch glätten
|
||||
Color Panel Options Optionen für das Farbbedienfeld
|
||||
Show Package Paket anzeigen
|
||||
Show Panel Bedienfeld einblenden
|
||||
@@ -3017,6 +3018,7 @@ mark-style Japanese Japanisch
|
||||
Simplify Vereinfachen
|
||||
Simplify: Vereinfachen:
|
||||
Simplify… Vereinfachen…
|
||||
Smooth… Glätten…
|
||||
Simulate Colored Paper Farbiges Papier simulieren
|
||||
Simulate Paper Papier simulieren
|
||||
Double Column Zweispaltig
|
||||
@@ -3924,6 +3926,7 @@ theme Appearance Erscheinungsbild
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio muss eine Zahl von 0,1 bis 10 sein
|
||||
@msg the Color Picker is not open der Farbwähler ist nicht geöffnet
|
||||
@msg the Simplify slider is not open der Regler „Vereinfachen“ ist nicht geöffnet
|
||||
@msg the Smooth slider is not open der Regler „Glätten“ ist nicht geöffnet
|
||||
@msg the screen color sampler is already open die Farbaufnahme vom Bildschirm ist bereits geöffnet
|
||||
@msg this system has no screen color sampler dieses System hat keine Farbaufnahme vom Bildschirm
|
||||
@msg bad colour `{hex}` (#rrggbb) ungültige Farbe `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1866,6 +1866,7 @@ font-technology Variable Μεταβλητές
|
||||
Simplify Απλοποίηση
|
||||
Simplify: Απλοποίηση:
|
||||
Simplify… Απλοποίηση…
|
||||
Smooth… Εξομάλυνση…
|
||||
Size Μέγεθος
|
||||
Size: Μέγεθος:
|
||||
Skew Λοξότητα
|
||||
@@ -3424,6 +3425,7 @@ theme Appearance Εμφάνιση
|
||||
Show Original Path Εμφάνιση αρχικής διαδρομής
|
||||
Open this dialog with these settings next time Άνοιγμα αυτού του παραθύρου με αυτές τις ρυθμίσεις την επόμενη φορά
|
||||
Auto-Simplify Αυτόματη απλοποίηση
|
||||
Auto-Smooth Αυτόματη εξομάλυνση
|
||||
Show Package Εμφάνιση συσκευασίας
|
||||
Show Panel Εμφάνιση πίνακα
|
||||
Show Pattern Swatches Εμφάνιση δειγμάτων μοτίβου
|
||||
@@ -5300,6 +5302,7 @@ effect Points Σημεία
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: το "selection" τοποθετεί το ίδιο το ντεγκραντέ: παραλείψτε τα `start` και `end` (το τοποθετούν ήδη σε συντεταγμένες εγγράφου)
|
||||
@msg the Color Picker is not open ο επιλογέας χρώματος δεν είναι ανοιχτός
|
||||
@msg the Simplify slider is not open ο ρυθμιστής Απλοποίησης δεν είναι ανοιχτός
|
||||
@msg the Smooth slider is not open ο ρυθμιστής Εξομάλυνσης δεν είναι ανοιχτός
|
||||
@msg the action has no steps η ενέργεια δεν έχει βήματα
|
||||
@msg the file's layers weren't read ({_1}): it comes in as its page, in one layer τα επίπεδα του αρχείου δεν διαβάστηκαν ({_1}): εισάγεται ως η σελίδα του, σε ένα επίπεδο
|
||||
@msg the file's layers weren't read from its editing data ({_1}): they come from its PDF part τα επίπεδα του αρχείου δεν διαβάστηκαν από τα δεδομένα επεξεργασίας του ({_1}): προέρχονται από το τμήμα PDF του
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2071 and column 19.
|
@@ -3016,6 +3016,7 @@ font-technology Variable Variables
|
||||
Show Original Path Mostrar trazado original
|
||||
Open this dialog with these settings next time Abrir este cuadro de diálogo con estos ajustes la próxima vez
|
||||
Auto-Simplify Simplificar automáticamente
|
||||
Auto-Smooth Suavizar automáticamente
|
||||
Color Panel Options Opciones del panel Color
|
||||
Show Package Mostrar paquete
|
||||
Show Panel Mostrar panel
|
||||
@@ -3052,6 +3053,7 @@ font-technology Variable Variables
|
||||
Simplify Simplificar
|
||||
Simplify: Simplificar:
|
||||
Simplify… Simplificar…
|
||||
Smooth… Suavizar…
|
||||
Simulate Colored Paper Simular papel de color
|
||||
Simulate Paper Simular papel
|
||||
Double Column Columna doble
|
||||
@@ -4004,6 +4006,7 @@ axis Both Ambos
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio debe ser un número de 0,1 a 10
|
||||
@msg the Color Picker is not open el Selector de color no está abierto
|
||||
@msg the Simplify slider is not open el regulador de Simplificar no está abierto
|
||||
@msg the Smooth slider is not open el regulador de Suavizar no está abierto
|
||||
@msg the screen color sampler is already open el muestreador de color de la pantalla ya está abierto
|
||||
@msg this system has no screen color sampler este sistema no tiene muestreador de color de la pantalla
|
||||
@msg bad colour `{hex}` (#rrggbb) color no válido `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -231,6 +231,7 @@
|
||||
Offset Path… Siirrä polkua…
|
||||
Reverse Path Direction Käännä polun suunta
|
||||
Simplify… Yksinkertaista…
|
||||
Smooth… Tasoita…
|
||||
Add Anchor Points Lisää ankkuripisteitä
|
||||
Remove Anchor Points Poista ankkuripisteitä
|
||||
Divide Objects Below Jaa alla olevat objektit
|
||||
@@ -1718,6 +1719,7 @@ lighting Light Valo
|
||||
Show Original Path Näytä alkuperäinen polku
|
||||
Open this dialog with these settings next time Avaa tämä valintaikkuna näillä asetuksilla ensi kerralla
|
||||
Auto-Simplify Yksinkertaista automaattisesti
|
||||
Auto-Smooth Tasoita automaattisesti
|
||||
Transparency Options Läpinäkyvyysasetukset
|
||||
Color Mixer (Shift-click the chip) Värisekoitin (Vaihto-napsauta värilaattaa)
|
||||
Fill: click for swatches, Shift-click for the color mixer Täyttö: napsauta avataksesi värimallit, Vaihto-napsauta avataksesi värisekoittimen
|
||||
@@ -5312,6 +5314,7 @@ mark-style Japanese Japanilainen
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" sijoittaa itse liukuvärin: jätä pois `start` ja `end` (ne sijoittavat sen jo asiakirjan koordinaatteihin)
|
||||
@msg the Color Picker is not open värinvalitsin ei ole auki
|
||||
@msg the Simplify slider is not open Yksinkertaista-liukusäädin ei ole auki
|
||||
@msg the Smooth slider is not open Tasoita-liukusäädin ei ole auki
|
||||
@msg the action has no steps toiminnossa ei ole vaiheita
|
||||
@msg the file's layers weren't read ({_1}): it comes in as its page, in one layer tiedoston tasoja ei luettu ({_1}): se tuodaan sivunaan yhdelle tasolle
|
||||
@msg the file's layers weren't read from its editing data ({_1}): they come from its PDF part tiedoston tasoja ei luettu sen muokkaustiedoista ({_1}): ne tulevat sen PDF-osasta
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3013,6 +3013,7 @@ font-technology Variable Variables
|
||||
Show Original Path Afficher le tracé d’origine
|
||||
Open this dialog with these settings next time Ouvrir cette boîte de dialogue avec ces réglages la prochaine fois
|
||||
Auto-Simplify Simplifier automatiquement
|
||||
Auto-Smooth Lisser automatiquement
|
||||
Color Panel Options Options du panneau Couleur
|
||||
Show Package Afficher le dossier d’assemblage
|
||||
Show Panel Afficher le panneau
|
||||
@@ -3049,6 +3050,7 @@ font-technology Variable Variables
|
||||
Simplify Simplifier
|
||||
Simplify: Simplifier :
|
||||
Simplify… Simplifier…
|
||||
Smooth… Lisser…
|
||||
Simulate Colored Paper Simuler un papier de couleur
|
||||
Simulate Paper Simuler le papier
|
||||
Double Column Deux colonnes
|
||||
@@ -4001,6 +4003,7 @@ axis Both Les deux
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio doit être un nombre de 0,1 à 10
|
||||
@msg the Color Picker is not open le Sélecteur de couleurs n’est pas ouvert
|
||||
@msg the Simplify slider is not open le curseur Simplifier n’est pas ouvert
|
||||
@msg the Smooth slider is not open le curseur Lisser n’est pas ouvert
|
||||
@msg the screen color sampler is already open l’outil de prélèvement de couleur à l’écran est déjà ouvert
|
||||
@msg this system has no screen color sampler ce système n’a pas d’outil de prélèvement de couleur à l’écran
|
||||
@msg bad colour `{hex}` (#rrggbb) couleur non valide `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3640,6 +3640,7 @@ mark-style Japanese Japán
|
||||
Show Original Path Eredeti útvonal megjelenítése
|
||||
Open this dialog with these settings next time Legközelebb ez a párbeszédablak nyíljon meg ezekkel a beállításokkal
|
||||
Auto-Simplify Automatikus egyszerűsítés
|
||||
Auto-Smooth Automatikus simítás
|
||||
Color Panel Options Szín panel beállításai
|
||||
Show Package Csomag megjelenítése
|
||||
Show Panel Panel megjelenítése
|
||||
@@ -3683,6 +3684,7 @@ mark-style Japanese Japán
|
||||
Simplify Egyszerűsítés
|
||||
Simplify: Egyszerűsítés:
|
||||
Simplify… Egyszerűsítés…
|
||||
Smooth… Simítás…
|
||||
Simulate Szimulálás
|
||||
Simulate Colored Paper Színes papír szimulálása
|
||||
Simulate Paper Papír szimulálása
|
||||
@@ -4560,6 +4562,7 @@ imageTrace Outline Körvonal
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio: 0,1 és 10 közötti szám kell
|
||||
@msg the Color Picker is not open a Színválasztó nincs megnyitva
|
||||
@msg the Simplify slider is not open az Egyszerűsítés csúszka nincs megnyitva
|
||||
@msg the Smooth slider is not open a Simítás csúszka nincs megnyitva
|
||||
@msg the screen color sampler is already open a képernyős színmintavevő már nyitva van
|
||||
@msg this system has no screen color sampler ezen a rendszeren nincs képernyős színmintavevő
|
||||
@msg bad colour `{hex}` (#rrggbb) hibás szín: `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -208,6 +208,7 @@
|
||||
Offset Path… Jalur ofset…
|
||||
Reverse Path Direction Balikkan arah jalur
|
||||
Simplify… Sederhanakan…
|
||||
Smooth… Haluskan…
|
||||
Add Anchor Points Tambahkan titik jangkar
|
||||
Remove Anchor Points Hapus titik jangkar
|
||||
Divide Objects Below Bagi objek di bawah
|
||||
@@ -5196,6 +5197,7 @@ swatch Bright Violet Violet cerah
|
||||
Show Original Path Tampilkan jalur asli
|
||||
Open this dialog with these settings next time Buka dialog ini dengan pengaturan ini lain kali
|
||||
Auto-Simplify Sederhanakan otomatis
|
||||
Auto-Smooth Haluskan otomatis
|
||||
@msg check a layer to export centang lapisan yang akan diekspor
|
||||
@msg select the art to export first pilih dulu karya yang akan diekspor
|
||||
@msg no saved selection recalled tidak ada seleksi tersimpan yang dipanggil
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3862 and column 19.
|
@@ -3050,6 +3050,7 @@ mark-style Japanese Giapponese
|
||||
Show Original Path Mostra tracciato originale
|
||||
Open this dialog with these settings next time Apri questa finestra di dialogo con queste impostazioni la prossima volta
|
||||
Auto-Simplify Semplifica automaticamente
|
||||
Auto-Smooth Smussa automaticamente
|
||||
Color Panel Options Opzioni pannello Colore
|
||||
Show Package Mostra pacchetto
|
||||
Show Panel Mostra pannello
|
||||
@@ -3086,6 +3087,7 @@ mark-style Japanese Giapponese
|
||||
Simplify Semplifica
|
||||
Simplify: Semplifica:
|
||||
Simplify… Semplifica…
|
||||
Smooth… Smussa…
|
||||
Simulate Colored Paper Simula carta colorata
|
||||
Simulate Paper Simula carta
|
||||
Double Column Colonna doppia
|
||||
@@ -4044,6 +4046,7 @@ axis Both Entrambi
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio deve essere un numero da 0,1 a 10
|
||||
@msg the Color Picker is not open il Selettore colore non è aperto
|
||||
@msg the Simplify slider is not open il cursore Semplifica non è aperto
|
||||
@msg the Smooth slider is not open il cursore Smussa non è aperto
|
||||
@msg the screen color sampler is already open il campionatore di colore dello schermo è già aperto
|
||||
@msg this system has no screen color sampler questo sistema non ha un campionatore di colore dello schermo
|
||||
@msg bad colour `{hex}` (#rrggbb) colore non valido `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -228,6 +228,7 @@ font-technology Variable バリアブル
|
||||
Offset Path… パスのオフセット…
|
||||
Reverse Path Direction パスの方向反転
|
||||
Simplify… 単純化…
|
||||
Smooth… スムーズ…
|
||||
Add Anchor Points アンカーポイントの追加
|
||||
Remove Anchor Points アンカーポイントの削除
|
||||
Divide Objects Below 背面のオブジェクトで型抜き
|
||||
@@ -2850,6 +2851,7 @@ mark-style Japanese 日本式
|
||||
Show Original Path 元のパスを表示
|
||||
Open this dialog with these settings next time 次回はこの設定でこのダイアログを開く
|
||||
Auto-Simplify 自動単純化
|
||||
Auto-Smooth 自動スムーズ
|
||||
Color Panel Options カラーパネルオプション
|
||||
Show Package パッケージを表示
|
||||
Show Panel パネルを表示
|
||||
@@ -4367,6 +4369,7 @@ swatch Bright Violet ビビッドバイオレット
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio には 0.1 〜 10 の数値を指定してください
|
||||
@msg the Color Picker is not open カラーピッカーが開いていません
|
||||
@msg the Simplify slider is not open 単純化スライダーが開いていません
|
||||
@msg the Smooth slider is not open スムーズスライダーが開いていません
|
||||
@msg the screen color sampler is already open 画面のカラーサンプラーはすでに開いています
|
||||
@msg this system has no screen color sampler このシステムには画面のカラーサンプラーがありません
|
||||
@msg Corners edits the corners of a path 「コーナー」で編集できるのはパスの角だけです
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -173,6 +173,7 @@
|
||||
Offset Path… ტრაექტორიის წანაცვლება…
|
||||
Reverse Path Direction ტრაექტორიის მიმართულების შებრუნება
|
||||
Simplify… გამარტივება…
|
||||
Smooth… გაგლუვება…
|
||||
Add Anchor Points საყრდენი წერტილების დამატება
|
||||
Remove Anchor Points საყრდენი წერტილების მოცილება
|
||||
Divide Objects Below ქვედა ობიექტების დაყოფა
|
||||
@@ -4910,6 +4911,7 @@ imageTrace Outline კონტური
|
||||
@msg the file's layers weren't read from its editing data ({_1}): they come from its PDF part ფაილის შრეები რედაქტირების მონაცემებიდან ვერ წაიკითხა ({_1}): ისინი მისი PDF ნაწილიდან აიღება
|
||||
@msg the Color Picker is not open ფერის ამრჩევი გახსნილი არ არის
|
||||
@msg the Simplify slider is not open გამარტივების სლაიდერი გახსნილი არ არის
|
||||
@msg the Smooth slider is not open გაგლუვების სლაიდერი გახსნილი არ არის
|
||||
@msg the screen color sampler is already open ეკრანიდან ფერის აღება უკვე გახსნილია
|
||||
@msg this system has no screen color sampler ამ სისტემას ეკრანიდან ფერის აღების საშუალება არ აქვს
|
||||
# Dimension tool and Object › Dimensions. A drafting dimension is ზომა (ზომის დასმა), like a dimension on a technical drawing.
|
||||
@@ -5070,6 +5072,7 @@ imageTrace Outline კონტური
|
||||
Show Original Path საწყისი ტრაექტორიის ჩვენება
|
||||
Open this dialog with these settings next time შემდეგ ჯერზე ამ დიალოგის ამ პარამეტრებით გახსნა
|
||||
Auto-Simplify ავტომატური გამარტივება
|
||||
Auto-Smooth ავტომატური გაგლუვება
|
||||
Texture Image ტექსტურის გამოსახულება
|
||||
@msg check a layer to export მონიშნეთ საექსპორტო შრე
|
||||
@msg select the art to export first ჯერ მონიშნეთ საექსპორტო ნახატი
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -224,6 +224,7 @@ font-technology Variable 가변
|
||||
Offset Path… 패스 오프셋…
|
||||
Reverse Path Direction 패스 방향 반전
|
||||
Simplify… 단순화…
|
||||
Smooth… 매끄럽게…
|
||||
Add Anchor Points 앵커 포인트 추가
|
||||
Remove Anchor Points 앵커 포인트 제거
|
||||
Divide Objects Below 아래 개체 나누기
|
||||
@@ -2844,6 +2845,7 @@ mark-style Japanese 일본식
|
||||
Show Original Path 원래 패스 표시
|
||||
Open this dialog with these settings next time 다음에 이 설정으로 이 대화상자 열기
|
||||
Auto-Simplify 자동 단순화
|
||||
Auto-Smooth 자동 매끄럽게
|
||||
Color Panel Options 색상 패널 옵션
|
||||
Show Package 패키지 표시
|
||||
Show Panel 패널 표시
|
||||
@@ -4299,6 +4301,7 @@ theme Appearance 외관
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio은(는) 0.1에서 10 사이의 숫자여야 합니다
|
||||
@msg the Color Picker is not open 색상 피커가 열려 있지 않습니다
|
||||
@msg the Simplify slider is not open 단순화 슬라이더가 열려 있지 않습니다
|
||||
@msg the Smooth slider is not open 매끄럽게 슬라이더가 열려 있지 않습니다
|
||||
@msg the screen color sampler is already open 화면 색상 추출기가 이미 열려 있습니다
|
||||
@msg this system has no screen color sampler 이 시스템에는 화면 색상 추출기가 없습니다
|
||||
@msg Corners edits the corners of a path 모퉁이는 패스의 모퉁이를 편집합니다
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -222,6 +222,7 @@
|
||||
Offset Path… Pad verschuiven…
|
||||
Reverse Path Direction Padrichting omkeren
|
||||
Simplify… Vereenvoudigen…
|
||||
Smooth… Gladmaken…
|
||||
Add Anchor Points Ankerpunten toevoegen
|
||||
Remove Anchor Points Ankerpunten verwijderen
|
||||
Divide Objects Below Objecten eronder verdelen
|
||||
@@ -5207,6 +5208,7 @@ swatch Bright Violet Felviolet
|
||||
Show Original Path Oorspronkelijk pad tonen
|
||||
Open this dialog with these settings next time Dit dialoogvenster de volgende keer met deze instellingen openen
|
||||
Auto-Simplify Automatisch vereenvoudigen
|
||||
Auto-Smooth Automatisch gladmaken
|
||||
@msg check a layer to export vink een laag aan om te exporteren
|
||||
@msg select the art to export first selecteer eerst de illustratie die je wilt exporteren
|
||||
@msg no saved selection recalled geen opgeslagen selectie opgeroepen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -189,6 +189,7 @@
|
||||
Offset Path… Odsuń ścieżkę…
|
||||
Reverse Path Direction Odwróć kierunek ścieżki
|
||||
Simplify… Uprość…
|
||||
Smooth… Wygładź…
|
||||
Add Anchor Points Dodaj punkty kontrolne
|
||||
Remove Anchor Points Usuń punkty kontrolne
|
||||
Divide Objects Below Podziel obiekty poniżej
|
||||
@@ -1675,6 +1676,7 @@ lighting Light Światło
|
||||
Show Original Path Pokaż oryginalną ścieżkę
|
||||
Open this dialog with these settings next time Następnym razem otwórz to okno dialogowe z tymi ustawieniami
|
||||
Auto-Simplify Uprość automatycznie
|
||||
Auto-Smooth Wygładź automatycznie
|
||||
Transparency Options Opcje przezroczystości
|
||||
Color Mixer (Shift-click the chip) Mikser kolorów (kliknij próbkę z Shift)
|
||||
Fill: click for swatches, Shift-click for the color mixer Wypełnienie: kliknij, aby wybrać próbkę, z Shift — mikser kolorów
|
||||
@@ -4131,6 +4133,7 @@ imageTrace Outline Kontur
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio musi być liczbą od 0,1 do 10
|
||||
@msg the Color Picker is not open Próbnik kolorów nie jest otwarty
|
||||
@msg the Simplify slider is not open Suwak Uprość nie jest otwarty
|
||||
@msg the Smooth slider is not open Suwak Wygładź nie jest otwarty
|
||||
@msg the screen color sampler is already open narzędzie do pobierania koloru z ekranu jest już otwarte
|
||||
@msg this system has no screen color sampler ten system nie ma narzędzia do pobierania koloru z ekranu
|
||||
@msg bad colour `{hex}` (#rrggbb) nieprawidłowy kolor `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2498,6 +2498,7 @@
|
||||
Show Original Path Mostrar Caminho Original
|
||||
Open this dialog with these settings next time Abrir este diálogo com estas configurações na próxima vez
|
||||
Auto-Simplify Simplificar Automaticamente
|
||||
Auto-Smooth Suavizar Automaticamente
|
||||
Show Package Mostrar Pacote
|
||||
Show Panel Mostrar Painel
|
||||
Show Perspective Grid Mostrar Grade de Perspectiva
|
||||
@@ -2535,6 +2536,7 @@
|
||||
Simplify Simplificar
|
||||
Simplify: Simplificar:
|
||||
Simplify… Simplificar…
|
||||
Smooth… Suavizar…
|
||||
Simulate Colored Paper Simular Papel Colorido
|
||||
Simulate Paper Simular Papel
|
||||
Double Column Coluna Dupla
|
||||
@@ -5255,6 +5257,7 @@ mark-style Japanese Japonesas
|
||||
@msg Unknown dimension option {k} Opção de cota desconhecida {k}
|
||||
@msg the Color Picker is not open o Seletor de Cores não está aberto
|
||||
@msg the Simplify slider is not open o controle deslizante Simplificar não está aberto
|
||||
@msg the Smooth slider is not open o controle deslizante Suavizar não está aberto
|
||||
@msg the screen color sampler is already open o coletor de cores da tela já está aberto
|
||||
@msg this system has no screen color sampler este sistema não tem coletor de cores da tela
|
||||
Darkness Escuridão
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3046,6 +3046,7 @@ mark-style Japanese Японский
|
||||
Show Original Path Показать исходный контур
|
||||
Open this dialog with these settings next time Открывать это диалоговое окно с этими параметрами в следующий раз
|
||||
Auto-Simplify Автоматическое упрощение
|
||||
Auto-Smooth Автоматическое сглаживание
|
||||
Color Panel Options Параметры панели «Цвет»
|
||||
Show Package Показать пакет
|
||||
Show Panel Показать панель
|
||||
@@ -3082,6 +3083,7 @@ mark-style Japanese Японский
|
||||
Simplify Упростить
|
||||
Simplify: Упростить:
|
||||
Simplify… Упростить…
|
||||
Smooth… Сгладить…
|
||||
Simulate Colored Paper Имитировать цветную бумагу
|
||||
Simulate Paper Имитировать бумагу
|
||||
Double Column Двойной столбец
|
||||
@@ -4040,6 +4042,7 @@ axis Both Оба
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio должно быть числом от 0,1 до 10
|
||||
@msg the Color Picker is not open палитра цветов не открыта
|
||||
@msg the Simplify slider is not open ползунок «Упростить» не открыт
|
||||
@msg the Smooth slider is not open ползунок «Сгладить» не открыт
|
||||
@msg the screen color sampler is already open средство выбора цвета с экрана уже открыто
|
||||
@msg this system has no screen color sampler в этой системе нет средства выбора цвета с экрана
|
||||
@msg bad colour `{hex}` (#rrggbb) недопустимый цвет `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2950,6 +2950,7 @@ font-technology Variable Variabla
|
||||
Show Original Path Visa ursprunglig bana
|
||||
Open this dialog with these settings next time Öppna den här dialogrutan med de här inställningarna nästa gång
|
||||
Auto-Simplify Förenkla automatiskt
|
||||
Auto-Smooth Jämna ut automatiskt
|
||||
Show Package Visa paket
|
||||
Show Panel Visa panel
|
||||
Show Pattern Swatches Visa mönsterprover
|
||||
@@ -2984,6 +2985,7 @@ font-technology Variable Variabla
|
||||
Simplify Förenkla
|
||||
Simplify: Förenkla:
|
||||
Simplify… Förenkla…
|
||||
Smooth… Jämna ut…
|
||||
Simulate Colored Paper Simulera färgat papper
|
||||
Simulate Paper Simulera papper
|
||||
Double Column Dubbel kolumn
|
||||
@@ -5304,6 +5306,7 @@ theme Appearance Utseende
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" placerar själva gradienten: utelämna `start` och `end` (de placerar den redan i dokumentkoordinater)
|
||||
@msg the Color Picker is not open färgväljaren är inte öppen
|
||||
@msg the Simplify slider is not open Förenkla-reglaget är inte öppet
|
||||
@msg the Smooth slider is not open Jämna ut-reglaget är inte öppet
|
||||
@msg the action has no steps åtgärden har inga steg
|
||||
@msg the file's layers weren't read ({_1}): it comes in as its page, in one layer filens lager lästes inte ({_1}): den hämtas in som en sida, i ett enda lager
|
||||
@msg the file's layers weren't read from its editing data ({_1}): they come from its PDF part filens lager lästes inte från dess redigeringsdata ({_1}): de kommer från dess PDF-del
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -2891,6 +2891,7 @@ font-technology Variable Değişken
|
||||
Simplify Basitleştir
|
||||
Simplify: Basitleştirin:
|
||||
Simplify… Basitleştir…
|
||||
Smooth… Düzgünleştir…
|
||||
Simulate Colored Paper Renkli Kağıdı Simüle Et
|
||||
Simulate Paper Kağıdı Simüle Et
|
||||
Double Column Çift Sütun
|
||||
@@ -5249,11 +5250,13 @@ theme Appearance Görünüm
|
||||
@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for {name} veri kümesinden hiçbir şey uygulanmadı: değer içerdiği bir değişkene bağlı hiçbir nesne yok
|
||||
@msg select an Objects on Path group bir Yol Üzerindeki Nesneler grubu seçin
|
||||
@msg the Simplify slider is not open Basitleştirme kaydırıcısı açık değil
|
||||
@msg the Smooth slider is not open Düzgünleştirme kaydırıcısı açık değil
|
||||
@msg {_1} is an exporter (File › Export › Export As…) {_1} bir dışa aktarma eklentisidir (Dosya › Dışa Aktar › Farklı Dışa Aktar…)
|
||||
@msg `documentData` is {size} bytes (limit {MAX_DOCUMENT_DATA}) `documentData` {size} bayt (sınır {MAX_DOCUMENT_DATA})
|
||||
Alt key toggles to Smooth Tool Alt tuşu Düzleştirme Aracına geçiş yapar
|
||||
Live curve fitting Çizerken eğri uydurma
|
||||
Auto-Simplify Otomatik Basitleştir
|
||||
Auto-Smooth Otomatik Düzgünleştir
|
||||
Drag an item here, or right-click one, to put it in this group. Bu gruba eklemek için bir öğeyi buraya sürükleyin veya sağ tıklayın.
|
||||
Drop here to take it out of its group Grup dışına çıkarmak için buraya bırakın
|
||||
Locate Selected Objects Seçili Nesneleri Bul
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -3042,6 +3042,7 @@ mark-style Japanese Японський
|
||||
Show Original Path Показати початковий контур
|
||||
Open this dialog with these settings next time Наступного разу відкривати це діалогове вікно з цими параметрами
|
||||
Auto-Simplify Автоматичне спрощення
|
||||
Auto-Smooth Автоматичне згладжування
|
||||
Color Panel Options Параметри панелі «Колір»
|
||||
Show Package Показати пакет
|
||||
Show Panel Показати панель
|
||||
@@ -3078,6 +3079,7 @@ mark-style Japanese Японський
|
||||
Simplify Спростити
|
||||
Simplify: Спростити:
|
||||
Simplify… Спростити…
|
||||
Smooth… Згладити…
|
||||
Simulate Colored Paper Імітувати кольоровий папір
|
||||
Simulate Paper Імітувати папір
|
||||
Double Column Подвійний стовпець
|
||||
@@ -4028,6 +4030,7 @@ axis Both Обидві
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio має бути числом від 0,1 до 10
|
||||
@msg the Color Picker is not open палітра кольорів не відкрита
|
||||
@msg the Simplify slider is not open повзунок «Спростити» не відкрито
|
||||
@msg the Smooth slider is not open повзунок «Згладити» не відкрито
|
||||
@msg the screen color sampler is already open засіб вибору кольору з екрана вже відкритий
|
||||
@msg this system has no screen color sampler у цій системі немає засобу вибору кольору з екрана
|
||||
@msg bad colour `{hex}` (#rrggbb) неприпустимий колір `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 749 and column 40.
|
@@ -3380,6 +3380,7 @@ mark-style Japanese Kiểu Nhật
|
||||
Show Original Path Hiện đường dẫn gốc
|
||||
Open this dialog with these settings next time Lần sau mở hộp thoại này với các cài đặt này
|
||||
Auto-Simplify Tự động đơn giản hóa
|
||||
Auto-Smooth Tự động làm mượt
|
||||
Color Panel Options Tùy chọn bảng màu
|
||||
Show Package Hiện gói
|
||||
Show Panel Hiện bảng
|
||||
@@ -3420,6 +3421,7 @@ mark-style Japanese Kiểu Nhật
|
||||
Simplify Đơn giản hóa
|
||||
Simplify: Đơn giản hóa:
|
||||
Simplify… Đơn giản hóa…
|
||||
Smooth… Làm mượt…
|
||||
Simulate Colored Paper Giả lập giấy có màu
|
||||
Simulate Paper Giả lập giấy
|
||||
Single Left Quotation Mark Dấu nháy đơn mở
|
||||
@@ -4210,6 +4212,7 @@ theme Appearance Giao diện
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio phải là một số từ 0,1 đến 10
|
||||
@msg the Color Picker is not open Bộ chọn màu chưa được mở
|
||||
@msg the Simplify slider is not open Thanh trượt Đơn giản hóa chưa được mở
|
||||
@msg the Smooth slider is not open Thanh trượt Làm mượt chưa được mở
|
||||
@msg the screen color sampler is already open công cụ lấy mẫu màu trên màn hình đã được mở
|
||||
@msg this system has no screen color sampler hệ thống này không có công cụ lấy mẫu màu trên màn hình
|
||||
@msg bad colour `{hex}` (#rrggbb) màu không hợp lệ `{hex}` (#rrggbb)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4079 and column 10.
|
@@ -2882,6 +2882,7 @@ font-script Japanese 汉字
|
||||
Show Original Path 显示原始路径
|
||||
Open this dialog with these settings next time 下次以这些设置打开此对话框
|
||||
Auto-Simplify 自动简化
|
||||
Auto-Smooth 自动平滑
|
||||
Color Panel Options 颜色面板选项
|
||||
Show Package 显示打包
|
||||
Show Panel 显示面板
|
||||
@@ -2918,6 +2919,7 @@ font-script Japanese 汉字
|
||||
Simplify 简化
|
||||
Simplify: 简化:
|
||||
Simplify… 简化…
|
||||
Smooth… 平滑…
|
||||
Simulate Colored Paper 模拟有色纸张
|
||||
Simulate Paper 模拟纸张
|
||||
Double Column 双列
|
||||
@@ -4098,6 +4100,7 @@ imageTrace Outline 轮廓
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio 必须是 0.1 到 10 之间的数字
|
||||
@msg the Color Picker is not open 拾色器未打开
|
||||
@msg the Simplify slider is not open 简化滑块未打开
|
||||
@msg the Smooth slider is not open 平滑滑块未打开
|
||||
@msg the screen color sampler is already open 屏幕取色器已打开
|
||||
@msg this system has no screen color sampler 此系统没有屏幕取色器
|
||||
@msg Corners edits the corners of a path 转角用于编辑路径的转角
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2055,6 +2055,7 @@ file-filter Templates 範本
|
||||
@msg videoRulerPixelAspectRatio must be a number from 0.1 to 10 videoRulerPixelAspectRatio 必須是 0.1 到 10 之間的數字
|
||||
@msg the Color Picker is not open 「顏色選擇器」未開啟
|
||||
@msg the Simplify slider is not open 「簡化」滑桿未開啟
|
||||
@msg the Smooth slider is not open 「平滑」滑桿未開啟
|
||||
@msg the screen color sampler is already open 螢幕取色器已開啟
|
||||
@msg this system has no screen color sampler 此系統沒有螢幕取色器
|
||||
@msg Corners edits the corners of a path 「轉角」用於編輯路徑的轉角
|
||||
@@ -3831,6 +3832,7 @@ font-script Japanese 漢字
|
||||
Show Original Path 顯示原始路徑
|
||||
Open this dialog with these settings next time 下次以這些設定開啟此對話方塊
|
||||
Auto-Simplify 自動簡化
|
||||
Auto-Smooth 自動平滑
|
||||
Color Panel Options 顏色面板選項
|
||||
Show Package 顯示封裝
|
||||
Show Panel 顯示面板
|
||||
@@ -3867,6 +3869,7 @@ font-script Japanese 漢字
|
||||
Simplify 簡化
|
||||
Simplify: 簡化:
|
||||
Simplify… 簡化…
|
||||
Smooth… 平滑…
|
||||
Simulate Colored Paper 模擬有色紙張
|
||||
Simulate Paper 模擬紙張
|
||||
Double Column 雙欄
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -54,6 +54,7 @@ pub(super) fn menu_dialog(app: &VectorcraftApp, id: &str) -> Option<(&'static st
|
||||
// "Open this dialog with these settings next time" was ticked.
|
||||
"object.path.simplify" if app.ui.simplify.is_object() => ("simplify", app.ui.simplify.clone()),
|
||||
"object.path.simplify" => (crate::dialogs::simplify_slider::KIND, crate::dialogs::simplify_slider::fields(app)),
|
||||
"object.path.smooth" => (crate::dialogs::smooth_slider::KIND, crate::dialogs::smooth_slider::fields()),
|
||||
"object.path.splitIntoGrid" => {
|
||||
// The grid starts at the size of the selection.
|
||||
let size =
|
||||
|
||||
@@ -695,6 +695,7 @@ fn run_ui_part10(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result
|
||||
"ui.expandDialog" => crate::dialogs::expand::open(app),
|
||||
"ui.simplifyAuto" => crate::dialogs::simplify_slider::auto(app),
|
||||
"ui.simplifyMoreOptions" => crate::dialogs::simplify_slider::more_options(app),
|
||||
"ui.smoothAuto" => crate::dialogs::smooth_slider::auto(app),
|
||||
"attributes.openUrl" => crate::panels::attributes::open_url(app, p),
|
||||
"ui.javascriptFilesDialog" => crate::panels::svg_interactivity::files_dialog(app, p),
|
||||
"ui.spotColors" => crate::dialogs::spot_colors::open(app),
|
||||
|
||||
@@ -237,6 +237,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool {
|
||||
"ui.spotColors" => app.session.active().is_some(),
|
||||
"ui.menuDialog" => app.session.active().is_some_and(|d| !d.selection.is_empty()),
|
||||
"ui.simplifyAuto" | "ui.simplifyMoreOptions" => crate::dialogs::simplify_slider::showing(app),
|
||||
"ui.smoothAuto" => crate::dialogs::smooth_slider::showing(app),
|
||||
"ui.dxfOptionsDialog" => app.session.active().is_some(),
|
||||
"links.editOriginal" | "links.reveal" => selected_image(app, |im| im.link.is_some()) || selected_placed(app),
|
||||
"ui.placementOptionsDialog" => selected_image(app, |_| true) || selected_placed(app),
|
||||
|
||||
@@ -219,6 +219,7 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec<Item>)> {
|
||||
c("Reverse Path Direction", "path.reverse"),
|
||||
Sep,
|
||||
c("Simplify…", "object.path.simplify"),
|
||||
c("Smooth…", "object.path.smooth"),
|
||||
c("Add Anchor Points", "object.path.addAnchorPoints"),
|
||||
c("Remove Anchor Points", "path.removeAnchors"),
|
||||
c("Divide Objects Below", "object.path.divideObjectsBelow"),
|
||||
|
||||
@@ -838,7 +838,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
"ui.menuDialog",
|
||||
"Menu Dialog",
|
||||
"",
|
||||
"{command: object.move|object.rotate|object.scale|object.reflect|object.shear|object.transformEach|path.average|object.path.offsetPath|object.path.simplify|object.path.splitIntoGrid|object.vectorHalftone|artboard.rearrange|object.envelope.makeWithWarp|object.envelope.resetWithWarp|object.envelope.makeWithMesh|object.envelope.resetWithMesh|object.envelope.options} open the dialog that command's menu item opens (dialog kind: move, rotate, scale, reflect, shear, transformEach, …, vectorHalftone, rearrangeArtboards, envelopeWarp, envelopeMesh, envelopeOptions; Scale and Transform Each have `corners` and `strokes`, from the preferences, which OK updates; Transform Each opens on the values it was last confirmed with (not `copy`), and its Copy button confirms with `copy: true`; the envelope dialogs start from the selected envelope, Make opens as Reset (`reset: true`) while one is selected)",
|
||||
"{command: object.move|object.rotate|object.scale|object.reflect|object.shear|object.transformEach|path.average|object.path.offsetPath|object.path.simplify|object.path.smooth|object.path.splitIntoGrid|object.vectorHalftone|artboard.rearrange|object.envelope.makeWithWarp|object.envelope.resetWithWarp|object.envelope.makeWithMesh|object.envelope.resetWithMesh|object.envelope.options} open the dialog that command's menu item opens (dialog kind: move, rotate, scale, reflect, shear, transformEach, …, vectorHalftone, rearrangeArtboards, envelopeWarp, envelopeMesh, envelopeOptions; Scale and Transform Each have `corners` and `strokes`, from the preferences, which OK updates; Transform Each opens on the values it was last confirmed with (not `copy`), and its Copy button confirms with `copy: true`; the envelope dialogs start from the selected envelope, Make opens as Reset (`reset: true`) while one is selected)",
|
||||
),
|
||||
(
|
||||
"ui.widthPointEdit",
|
||||
@@ -858,6 +858,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
"",
|
||||
"{} the open Simplify slider (Object › Path › Simplify opens it: dialog `simplifySlider` with field `level` 0..100, object.path.simplify's level, opening at the level object.path.simplify {auto: true} picks for the selection, previewed on the canvas until ui.dialog.confirm keeps it as one undo step; after OK in the Simplify dialog with `retain` ticked, the menu item opens the dialog `simplify` with the fields of that OK instead) moves to the level object.path.simplify {auto: true} picks for the selection and previews it → {level}; an error when the slider isn't open",
|
||||
),
|
||||
(
|
||||
"ui.smoothAuto",
|
||||
"Auto-Smooth",
|
||||
"",
|
||||
"{} the open Smooth slider (Object › Path › Smooth opens it: dialog `smoothSlider` with field `level` 0..100, object.path.smooth's level, opening at the level object.path.smooth {auto: true} picks, previewed on the canvas until ui.dialog.confirm keeps it as one undo step) moves to the level object.path.smooth {auto: true} picks and previews it → {level}; an error when the slider isn't open",
|
||||
),
|
||||
(
|
||||
"ui.simplifyMoreOptions",
|
||||
"More Options",
|
||||
|
||||
+17
@@ -2569,6 +2569,23 @@ dialog `simplifySlider` with field `level`) at the Auto-Simplify level for the s
|
||||
opens the Simplify dialog at the slider's tolerance. When OK confirms the dialog with `retain` ticked, the menu item opens
|
||||
the dialog on those settings next time.
|
||||
|
||||
## Smooth
|
||||
|
||||
`object.path.smooth` smooths the selected paths (or the objects in `ids`), compound path members included, as one undo
|
||||
step. `level` is the Smooth slider's, a whole number from 0 (minimum smoothing: the path stays within 0.1% of its longer
|
||||
side) to 100 (maximum smoothing); `auto: true` (Auto-Smooth) uses level 50, the default. Each path is refitted with
|
||||
curves within 0.1% to 2.5% of its longer side, keeping a corner only where it turns back on itself, so jags and kinks
|
||||
become curves; a refit never has more anchor points than the path had, so a shape whose corners the tolerance can't
|
||||
round, such as a rectangle, stays as it is. Closed paths stay closed and open paths keep their end points. The reply has the anchor counts `before` and `after` and the `level` used:
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"object.path.smooth","params":{"level":80}}}
|
||||
```
|
||||
|
||||
In the desktop app, Object › Path › Smooth opens the slider (`ui.menuDialog {command: "object.path.smooth"}`, dialog
|
||||
`smoothSlider` with field `level`) at the Auto-Smooth level and previews the result until `ui.dialog.confirm`;
|
||||
`ui.smoothAuto` moves the open slider back to the Auto-Smooth level.
|
||||
|
||||
## Editing a path with the Curvature tool
|
||||
|
||||
The Curvature tool edits any selected path, whatever drew it, keeping its shape except where edited (#798). Each edit
|
||||
|
||||
@@ -786,6 +786,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
The Simplify dialog also has Corner Point Angle Threshold, Convert to Straight Lines and Show Original Path. The threshold is a slider from 0° to 180° and opens at 150°: an anchor whose corner is sharper than the threshold stays a corner, so moving the slider right keeps more corners, and the dialog sends `cornerAngle` as 180° less the threshold. While Preview is on, Show Original Path draws the paths as they were before the preview in a wide, faint band of their layer's color under the selection's outlines.
|
||||
Object › Path › Simplify opens a slider under the selection ([Manually simplify paths](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html), [Auto simplify paths](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/auto-simplify-paths.html)) with Auto-Simplify, a rail from the fewest anchor points to the closest fit, and More Options, which opens the dialog at the slider's tolerance. The rail's levels are VectorCraft's own scale (`object.path.simplify {level}`): level 50 is 1 pt, and every 10 levels halve or double the tolerance, from 32 pt at 0 to 1/32 pt at 100. Auto-Simplify picks the lowest level whose tolerance is at most 1/400 of the selection's longer side, or level 100 when that side is under 12.5 pt; the slider opens at that level, with its result previewed, as the manual-simplify page describes. The dialog's "Open this dialog with these settings next time" makes the menu item open the dialog on the settings of its last OK.
|
||||
With anchors or segments of a path selected with Direct Selection, Simplify acts on just that part of the path, as the [Manually simplify paths](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html) page puts it: the slider, Auto-Simplify and the dialog refit only each run of selected segments, between its end anchors, which keep their place and their handles on the side away from the run. The anchors and segments outside the runs stay exactly as they were, a closed path's run may cross its start point, separate runs on one path are refitted independently in one undo step, and the runs stay selected. A path with only lone anchors selected is still simplified whole, and `object.path.simplify {wholePaths: true}` simplifies partly selected paths whole. Tests: `crates/engine/src/tests/simplify_part.rs` and `simplify_slider_and_dialog_simplify_only_the_direct_selected_part_of_a_path`.
|
||||
Object › Path › Smooth… (#4115) opens a slider under the selection from Minimum to Maximum smoothing with an Auto-Smooth button, as the [Smooth slider](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/refine-path-segments-with-the-smooth-slider.html) page describes; the canvas previews it and Enter or a click outside keeps it as one undo step (`object.path.smooth {level, auto, ids}`). The page names only the ends, so the rest is VectorCraft's own rule: each selected path, compound path members included, is refitted with curves within 0.1% (level 0) to 2.5% (level 100) of its longer side, corners kept only where it turns back and never with more anchors (a rectangle stays as it is); Auto-Smooth is level 50. Closed paths stay closed and open ones keep their end points. Tests: `crates/engine/src/tests/smooth_path.rs` and `smooth_slider_previews_auto_smooth_and_keeps_the_result`.
|
||||
- **Devanagari shaping:** with an OpenType Devanagari font, Ka, virama and Ssa (क्ष) form one conjunct glyph, Ra and virama before a consonant (र्क) become a reph drawn after it, the i matra (कि) is drawn before its consonant, and Ka with a nukta is drawn like the precomposed क़ (U+0958), as [Microsoft's Devanagari shaping specification](https://learn.microsoft.com/en-us/typography/script-development/devanagari) describes. Each syllable is one cluster of its source bytes, and the arrow keys stop at its ends. A click inside a syllable drawn with several glyphs puts the caret at the syllable's nearer end, in point, area and path type. Tests: `crates/text/src/tests/devanagari.rs`; they print SKIPPED when no such font is installed.
|
||||
- **Vertical punctuation:** fullwidth `!` (U+FF01) and `?` (U+FF1F) use their nominal glyph upright when no vertical alternate is present, following the Tu fallback in [Unicode UAX #50 §3.1](https://www.unicode.org/reports/tr50/#vo). Code-built test fonts cover point and area type with and without `vert` alternates, retain the rotated fallback for a bracket and long vowel mark, and leave horizontal type unchanged. Tests: `crates/text/src/tests/vertical.rs`.
|
||||
- Free Distort's dialog (Effect › Distort & Transform › Free Distort…) shows a box in the proportions of the first selected object's bounds with a handle on each corner. Dragging a handle moves that corner. A handle focused by a click or Tab moves 1% of the box with each arrow key, and 10% with Shift. Reset puts the four corners back on the box. The preview and OK run `effect.apply`, or `effect.setParams` when the dialog edits an applied effect, and the Appearance panel's inline editor opens the dialog with Edit…. Behavior reference: Illustrator CC Help (2014, [PDF](https://help.adobe.com/archive/en/illustrator/cc/2014/illustrator_reference.pdf)), page 285 (PDF page 291).
|
||||
@@ -1168,6 +1169,7 @@ applies to fills). Tests: `color::gradient::tests` (perceptual midpoint, expansi
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Object › Path › Smooth… with its slider and Auto-Smooth (#4115) added to the evidence; score and hours unchanged. |
|
||||
| 2026-10-26 | minor | File formats: EPS Save and Save As: an opened EPS saves back to its own path as EPS, keeping the chosen artboard's page frame and transparent margins and the complete editable VectorCraft document; Save's TIFF preview is capped at 1024 px (Export keeps 2048); partly read, preview-only or damaged-editing-data sources still need confirmation before they are overwritten; evidence added ([file-format-parity.md](file-format-parity.md#eps-save-and-source-protection)); writing Illustrator's editing data (#868) and the `.ai` structure issues stay in the missing items; score and hours unchanged (estimated) |
|
||||
| 2026-10-26 | minor | View & navigation: New View… / Edit Views… taken off the missing items (all 25 saved views in the View menu, an Edit Views… list dialog, views checked on open); evidence added; score and hours unchanged. |
|
||||
| 2026-10-25 | minor | Type core: a double-click on a threaded port breaks the thread there (`text.thread.break`); evidence added; scores unchanged. |
|
||||
|
||||
@@ -99,6 +99,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Smooth tool smooths only the stretch of path under the drag | done | #1512 |
|
||||
| Drawing modes apply to pastes: Draw Inside takes Paste, Paste in Place and Paste on All Artboards inside the object; Draw Behind puts Paste in Place and Paste on All Artboards behind the selection; Shift+D cycles past Draw Behind with any selection ([drawing modes](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/learn-drawing-basics/drawing-modes-overview.html)) | done | #3705; `pasting_in_draw_inside_puts_the_copies_inside_the_object`, `paste_in_place_in_draw_behind_goes_behind_the_selection`, `shift_d_goes_past_draw_behind_whatever_is_selected` |
|
||||
| Simplify (slider, auto, corner angle threshold), of a whole path or of the part of it selected with Direct Selection ([manually simplify paths](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html)) | done | Tolerance, default-on Preview and original/new point counts (#1272); Corner Point Angle Threshold, Convert to Straight Lines and Show Original Path; the slider under the selection with Auto-Simplify and More Options, opening at the Auto-Simplify level for the selection with that result previewed, and the dialog's option to open it directly with its last settings. With anchors or segments direct-selected, the slider, Auto-Simplify and the dialog refit only each run of selected segments between its end anchors, a closed path's run across its start point included; the rest of the path stays exactly as it was (`tests/simplify_part.rs`, `simplify_slider_and_dialog_simplify_only_the_direct_selected_part_of_a_path`) |
|
||||
| Smooth (Object › Path › Smooth…: slider from minimum to maximum smoothing, Auto-Smooth) ([smooth slider](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/refine-path-segments-with-the-smooth-slider.html)) | done | #4115: the slider under the selection previews `object.path.smooth {level}`, Auto-Smooth picks level 50, Enter or a click outside keeps one undo step and Escape restores; the smoothing rule beyond the two ends is VectorCraft's own (`tests/smooth_path.rs`, `smooth_slider_previews_auto_smooth_and_keeps_the_result`) |
|
||||
| Draw Inside in isolation mode: art drawn or pasted inside the isolated object takes isolation mode into its clip group, clickable and undimmed ([isolation mode](https://helpx.adobe.com/illustrator/desktop/manage-objects/select-objects/isolate-objects.html), [drawing modes](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/learn-drawing-basics/drawing-modes-overview.html)) | done | #3705; `drawing_inside_an_isolated_object_keeps_the_new_art_in_isolation_mode` |
|
||||
| Simplify (slider, auto, corner angle threshold) | partial | Tolerance, default-on Preview and original/new point counts (#1272); Corner Point Angle Threshold, Convert to Straight Lines and Show Original Path; the slider under the selection with Auto-Simplify and More Options, and the dialog's option to open it directly with its last settings; the slider opens at 1 pt rather than at the Auto-Simplify level, and simplifying only a Direct Selection part of a path remains missing |
|
||||
| Simplify (slider, auto, corner angle threshold) | partial | Tolerance, default-on Preview and original/new point counts (#1272); Corner Point Angle Threshold, Convert to Straight Lines and Show Original Path; the slider under the selection with Auto-Simplify and More Options, opening at the Auto-Simplify level for the selection with that result previewed, and the dialog's option to open it directly with its last settings; simplifying only a Direct Selection part of a path remains missing |
|
||||
@@ -357,6 +358,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Object › Path › Smooth… with its slider and Auto-Smooth (#4115); scores and estimates unchanged |
|
||||
| 2026-10-25 | minor | Selection tool: a double-click on a threaded port breaks the thread there (`text.thread.break`); scores and estimates unchanged |
|
||||
| 2026-10-25 | minor | A selected Grid Repeat shows handles: the one below the grid adds or removes rows, the one right of it columns, the one past its bottom-right corner both, and the sliders between the first two columns and rows set the horizontal and vertical spacing; each drag is one undo step and Escape cancels it (#2691); headless canvas and tool regressions; scores and estimates unchanged. |
|
||||
| 2026-10-25 | minor | Floating panels open in OS windows of their own on the desktop (inside the window on the web and on Wayland); saved layouts from earlier versions convert to the shared docking layout (#4135); scores and estimates unchanged (estimated) |
|
||||
|
||||
Reference in New Issue
Block a user