mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 20:27:05 -06:00
Path: Object › Path › Smooth smooths the selected paths 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",
|
||||
|
||||
Reference in New Issue
Block a user