From ed17e8fc1729a801de513d660cd535a9b87e1601 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 10 Nov 2026 16:13:04 -0800 Subject: [PATCH] Dimension tool: linear dimensions measure horizontally, vertically or aligned A linear dimension between two clicked points, or dragged out from a segment, always measured the straight distance between its points and drew its line parallel to them, so two points at different heights gave only a slanted dimension. The linear dimensions help describes aligned, horizontal and vertical linear dimensions: after the second click, clicking again gives an aligned one and moving the pointer gives a horizontal or vertical one. https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/plot-linear-dimensions-of-objects.html While a linear dimension is being placed, the pointer's position sets whether it is aligned, horizontal or vertical. The rule is VectorCraft's own. With the pointer beside the two points (the nearest point of the line through them lies between them) or on that line, the dimension is aligned. Past either point, the line through the points and the perpendicular through that point bound a quarter on each side of the line. The quarter that holds the direction straight up or down gives the horizontal distance, and the quarter that holds the direction straight left or right gives the vertical distance. A click within reach of the second point, or of where the pointer was for the second click, places it aligned. Points on one horizontal or vertical line give an aligned dimension. The dimension stays linked to the anchors it measures. - Dimension recipes carry an orientation (aligned by default; files without it open as before). The line runs along the axis, and each extension line runs from its point to it. - dimension.create and dimension.update take orientation: aligned, horizontal or vertical, for linear dimensions only. Any other value is an error. A new orientation keeps the line on the side of the points it was on, and an update that changes nothing records no undo step. - A horizontal or vertical dimension keeps its orientation when it or the art it measures turns, and shows 0 when its points come into line across its axis. A line that lay outside the points stays outside them after a turn. - The hint bar names the two-click placement. Docs: docs/dimensions.md, the Evidence list in docs/target-app-parity.md. --- crates/doc/src/dimension.rs | 137 +++++++++++- crates/engine/src/cmd/dimension.rs | 83 ++++++-- crates/engine/tests/it/dimensions.rs | 307 +++++++++++++++++++++++++++ crates/tools/src/dimension.rs | 109 +++++++++- crates/ui-egui/src/chrome/hints.rs | 2 + crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/bs.tsv | 3 + crates/ui-egui/src/i18n/ca.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/da.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fi.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hi.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/id.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/ka.tsv | 3 + crates/ui-egui/src/i18n/ko.tsv | 3 + crates/ui-egui/src/i18n/nl.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sv.tsv | 3 + crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + docs/dimensions.md | 29 ++- docs/target-app-parity.md | 1 + 34 files changed, 722 insertions(+), 27 deletions(-) diff --git a/crates/doc/src/dimension.rs b/crates/doc/src/dimension.rs index 5ca27311e..301e1beb8 100644 --- a/crates/doc/src/dimension.rs +++ b/crates/doc/src/dimension.rs @@ -25,6 +25,17 @@ impl DimensionMode { } } +/// The direction a linear dimension measures in: along the line through its two points, or only +/// their horizontal or vertical distance. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Orientation { + #[default] + Aligned, + Horizontal, + Vertical, +} + /// The unit a length is labelled in. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] @@ -349,6 +360,10 @@ pub struct Dimension { pub length: f64, pub options: DimensionOptions, pub source: Option, + /// What a linear dimension measures: the distance between its points (aligned), or only its + /// horizontal or vertical part. Other modes ignore it. + #[serde(default, skip_serializing_if = "crate::skip::is_default")] + pub orientation: Orientation, /// How far a linked dimension was moved on its own since the last refresh: the refresh turns /// it into a new placement unless the measured art moved too. #[serde(skip)] @@ -359,16 +374,107 @@ impl Dimension { /// A point on the drawn dimension line (linear), arc (angular) or radius line. pub fn handle(&self) -> Point { match self.mode { - DimensionMode::Linear => self.a.midpoint(self.b) + self.normal() * self.offset, + DimensionMode::Linear => { + let (a, b) = self.line_ends(); + a.midpoint(b) + } DimensionMode::Angular => self.vertex + self.bisector() * self.offset.abs().max(12.0), DimensionMode::Radius | DimensionMode::Diameter => self.b, } } - /// The left normal of a → b. + /// The unit direction a linear dimension measures along: a → b when aligned, else the x axis + /// (horizontal) or the y axis (vertical), whichever way the points lie. + pub fn direction(&self) -> Vec2 { + match self.orientation { + Orientation::Horizontal if self.mode == DimensionMode::Linear => Vec2::new(1.0, 0.0), + Orientation::Vertical if self.mode == DimensionMode::Linear => Vec2::new(0.0, 1.0), + _ => { + let u = self.b - self.a; + let len = u.hypot(); + if len > 1e-9 { u / len } else { Vec2::new(1.0, 0.0) } + } + } + } + /// The left normal of [`Self::direction`]. pub fn normal(&self) -> Vec2 { - let u = self.b - self.a; - let len = u.hypot(); - if len > 1e-9 { Vec2::new(-u.y, u.x) / len } else { Vec2::new(0.0, 1.0) } + let u = self.direction(); + Vec2::new(-u.y, u.x) + } + /// The length a linear dimension shows: the distance between its points along + /// [`Self::direction`]. + pub fn measured(&self) -> f64 { + (self.b - self.a).dot(self.direction()).abs() + } + /// The two ends of a linear dimension's line: the measured points moved onto one line along + /// [`Self::direction`], `offset` from a along the normal. + pub fn line_ends(&self) -> (Point, Point) { + let (u, n) = (self.direction(), self.normal()); + let a = self.a + n * self.offset; + (a, a + u * (self.b - self.a).dot(u)) + } + /// A horizontal or vertical dimension whose points moved from `old`'s keeps its line as far + /// outside them as it was, on the same side: the gap is measured from the outermost point + /// on the line's side, so the line stays clear of art that turns. A line that lay between + /// the two points keeps its distance from the first point. + pub fn keep_gap(&mut self, old: &Dimension) { + if self.mode != DimensionMode::Linear + || self.orientation == Orientation::Aligned + || old.orientation != self.orientation + || (self.a, self.b) == (old.a, old.b) + { + return; + } + let n = self.normal(); + let across = |p: Point| p.to_vec2().dot(n); + let line = across(old.a) + old.offset; + let (low, high) = (across(old.a).min(across(old.b)), across(old.a).max(across(old.b))); + let (new_low, new_high) = (across(self.a).min(across(self.b)), across(self.a).max(across(self.b))); + let at = if line >= high { + new_high + (line - high) + } else if line <= low { + new_low + (line - low) + } else { + return; + }; + let offset = at - across(self.a); + if offset.is_finite() && offset.abs() <= 1e6 { + self.offset = offset; + } + } + /// Whether a linear dimension's line lies strictly between its two points along the normal. + fn line_between_points(&self) -> bool { + let n = self.normal(); + let across = |p: Point| p.to_vec2().dot(n); + let line = across(self.a) + self.offset; + across(self.a).min(across(self.b)) < line && line < across(self.a).max(across(self.b)) + } + /// Makes a linear dimension measure in `orientation`, with its line on the side of the points + /// it was on. The line goes through the middle of the old line when that point lies outside + /// the two points along the new normal; otherwise it lies as far past the outermost point on + /// that side as the old line lay from its points. + pub fn set_orientation(&mut self, orientation: Orientation) { + if self.mode != DimensionMode::Linear || self.orientation == orientation { + self.orientation = orientation; + return; + } + let (handle, gap) = (self.handle(), self.offset.abs()); + self.orientation = orientation; + let n = self.normal(); + let across = |p: Point| p.to_vec2().dot(n); + let (low, high) = (across(self.a).min(across(self.b)), across(self.a).max(across(self.b))); + let h = across(handle); + let at = if h >= high || h <= low { + h + } else if 2.0 * h >= low + high { + high + gap + } else { + low - gap + }; + let offset = at - across(self.a); + if offset.is_finite() && offset.abs() <= 1e6 { + self.offset = offset; + } + self.length = self.measured(); } fn bisector(&self) -> Vec2 { let start = (self.a - self.vertex).atan2(); @@ -397,11 +503,24 @@ impl Dimension { self.moved = Some((xf * at) - at + self.moved.unwrap_or_default()); return; } + // A horizontal or vertical dimension keeps measuring along its axis, between the points + // where they now are, and its line goes through where the transform puts it. When that + // puts the line between the points (a quarter turn), the line keeps its gap from them. + let before = (self.mode == DimensionMode::Linear && self.orientation != Orientation::Aligned).then(|| (self.clone(), xf * self.handle())); self.a = xf * self.a; self.b = xf * self.b; self.vertex = xf * self.vertex; + if let Some((old, p)) = before { + let offset = (p - self.a).dot(self.normal()); + if offset.is_finite() && offset.abs() <= 1e6 { + self.offset = offset; + } + if self.line_between_points() && !old.line_between_points() { + self.keep_gap(&old); + } + } if self.mode == DimensionMode::Linear { - self.length = self.a.distance(self.b); + self.length = self.measured(); } } pub fn resolve(&mut self, doc: &Document) -> Result<(), String> { @@ -412,13 +531,13 @@ impl Dimension { *point = r.position(doc).ok_or("Measured anchor no longer exists")?; } } - self.length = self.a.distance(self.b); + self.length = if self.mode == DimensionMode::Linear { self.measured() } else { self.a.distance(self.b) }; } Some(DimensionSource::Segment { edge }) => { let (a, b) = edge.endpoints(doc).ok_or("Measured segment no longer exists")?; self.a = a; self.b = b; - self.length = a.distance(b); + self.length = self.measured(); } Some(DimensionSource::Angle { first, second }) => { for edge in [first, second] { @@ -486,7 +605,7 @@ impl Dimension { DimensionMode::Angular => self.angle().abs().to_degrees(), DimensionMode::Radius => self.a.distance(self.b), DimensionMode::Diameter => self.a.distance(self.b) * 2.0, - DimensionMode::Linear => self.a.distance(self.b), + DimensionMode::Linear => self.measured(), }; self.options.label(value, self.mode == DimensionMode::Angular) } diff --git a/crates/engine/src/cmd/dimension.rs b/crates/engine/src/cmd/dimension.rs index f39aba2b1..01f1effe3 100644 --- a/crates/engine/src/cmd/dimension.rs +++ b/crates/engine/src/cmd/dimension.rs @@ -3,7 +3,9 @@ use super::{CommandSpec, EngineError, Result, Session, cmd, has_doc}; use serde_json::{Value, json}; use std::sync::Arc; use vectorcraft_color::{Color, Paint}; -use vectorcraft_doc::dimension::{ArrowStyle, Dimension, DimensionMode, DimensionOptions, DimensionSource, LineType, TextAlignment, TextPosition}; +use vectorcraft_doc::dimension::{ + ArrowStyle, Dimension, DimensionMode, DimensionOptions, DimensionSource, LineType, Orientation, TextAlignment, TextPosition, +}; use vectorcraft_doc::{Appearance, Arrowhead, CharStyle, ColorMode, Dash, Document, Node, NodeId, NodeKind, TextObject}; use vectorcraft_geom::{Affine, BezPath, PathData, Point, Vec2}; @@ -14,7 +16,7 @@ pub fn specs() -> Vec { "Create Dimension", [], None, - "{mode?: linear|angular|radius|diameter, a?: [x,y], b?: [x,y], vertex?: [x,y], source?: {kind: segment, edge: {node,subpath,segment}}|{kind: angle, first: edge, second: edge}|{kind: circle, node}|{kind: arc, edge}|{kind: points, a?, b?, vertex?: {node,subpath,anchor}}, offset?: pt, options?: {units: in|mm|pt|pc|px|cm|m|ft|yd, precision: 0-6, scale: drawing:real, hideUnits, lineWeight, lineType: solid|dashed|dotted, lineColor: [r,g,b], arrowStyle: filled|open|tick|none, arrowScale: %, extensionWeight, extensionType, extensionColor, extensionOffset, extensionLength, hideExtensions, fontFamily, fontStyle, fontSize, textColor, textPosition: away|between, textAlignment: left|center|right, textOffset}} → {id,label}; linked measurements follow source edits", + "{mode?: linear|angular|radius|diameter, a?: [x,y], b?: [x,y], vertex?: [x,y], source?: {kind: segment, edge: {node,subpath,segment}}|{kind: angle, first: edge, second: edge}|{kind: circle, node}|{kind: arc, edge}|{kind: points, a?, b?, vertex?: {node,subpath,anchor}}, orientation?: aligned|horizontal|vertical (linear only: the distance between the points, or only its horizontal or vertical part; default aligned), offset?: pt (the line's distance from the first point: along the left normal of a → b when aligned, downward when horizontal, leftward when vertical; negative goes the other way), options?: {units: in|mm|pt|pc|px|cm|m|ft|yd, precision: 0-6, scale: drawing:real, hideUnits, lineWeight, lineType: solid|dashed|dotted, lineColor: [r,g,b], arrowStyle: filled|open|tick|none, arrowScale: %, extensionWeight, extensionType, extensionColor, extensionOffset, extensionLength, hideExtensions, fontFamily, fontStyle, fontSize, textColor, textPosition: away|between, textAlignment: left|center|right, textOffset}} → {id,label}; linked measurements follow source edits", has_doc, create ), @@ -23,7 +25,7 @@ pub fn specs() -> Vec { "Update Dimensions", [], None, - "{ids?: [dimension ids] (default selection), options?: dimension options, offset?: pt} restyle or move existing dimensions → {ids}", + "{ids?: [dimension ids] (default selection), options?: dimension options, orientation?: aligned|horizontal|vertical (linear dimensions only; the line stays on the side of the points it was on), offset?: pt (as in dimension.create)} restyle, turn or move existing dimensions → {ids}", has_doc, update ), @@ -92,11 +94,36 @@ fn recipe(s: &Session, p: &Value) -> Result { } else { Point::ZERO }; - let mut d = Dimension { mode, a, b, vertex, offset: distance(p, 30.0)?, length: a.distance(b), options: options(s, p)?, source, moved: None }; + let orientation = orientation(p, mode)?.unwrap_or_default(); + let mut d = Dimension { + mode, + a, + b, + vertex, + offset: distance(p, 30.0)?, + length: a.distance(b), + options: options(s, p)?, + source, + orientation, + moved: None, + }; + if mode == DimensionMode::Linear { + d.length = d.measured(); + } d.resolve(&s.doc()?.doc).map_err(error)?; Ok(d) } +/// The `orientation` param, if given: only a linear dimension is horizontal or vertical. +fn orientation(p: &Value, mode: DimensionMode) -> Result> { + let Some(v) = p.get("orientation") else { return Ok(None) }; + let orientation: Orientation = serde_json::from_value(v.clone()).map_err(|_| error("Unknown dimension orientation"))?; + if orientation != Orientation::Aligned && mode != DimensionMode::Linear { + return Err(error("Only linear dimensions are horizontal or vertical")); + } + Ok(Some(orientation)) +} + fn insert(doc: &mut Document, parent: Option, dim: Dimension) -> Result { let id = doc.alloc_id(); let mut group = Node::new(id, NodeKind::Group { children: vec![], clip: false }); @@ -134,21 +161,38 @@ fn update(s: &mut Session, p: &Value) -> Result { return Err(error("Select dimensions to update")); } let o = if p.get("options").is_some() { Some(options(s, p)?) } else { None }; - s.edit("Update Dimensions", |doc, _| { + // Every dimension is checked before anything changes, and an update that changes none of + // them records no undo step. + let mut next = Vec::with_capacity(ids.len()); + { + let doc = &s.doc()?.doc; for id in &ids { if !doc.is_editable(*id) { return Err(error("Dimension is locked or hidden")); } - let mut dim = dimension_of(doc, *id)?; + let old = dimension_of(doc, *id)?; + let mut dim = old.clone(); if let Some(o) = &o { dim.options = o.clone(); } + if let Some(orientation) = orientation(p, dim.mode)? { + dim.set_orientation(orientation); + } dim.offset = distance(p, dim.offset)?; dim.resolve(doc).map_err(error)?; - replace(doc, *id, dim)?; + if dim != old { + next.push((*id, dim)); + } } - Ok(()) - })?; + } + if !next.is_empty() { + s.edit("Update Dimensions", |doc, _| { + for (id, dim) in next { + replace(doc, id, dim)?; + } + Ok(()) + })?; + } Ok(json!({"ids":ids})) } @@ -277,6 +321,7 @@ pub(crate) fn refresh(before: &Document, doc: &mut Document) -> Result { changed = true; continue; } + next.keep_gap(&old); // Moved on its own (the art it measures stayed put): it keeps its new place. if let Some(v) = moved && (next.a, next.b, next.vertex) == (old.a, old.b, old.vertex) @@ -424,17 +469,19 @@ fn artwork(doc: &mut Document, d: &Dimension, old: &[NodeId]) -> Result (d.a, d.b), DimensionMode::Diameter => (d.a - delta, d.b), - _ => (d.a + n * d.offset, d.b + n * d.offset), + _ => d.line_ends(), }; - // The side of the line facing away from the object. + // The side of the line facing away from the object: the side the line is on, seen from + // the middle of the measured points. let outward = match d.mode { DimensionMode::Linear => { - if d.offset < 0.0 { + if (a.midpoint(b) - d.a.midpoint(d.b)).dot(n) < 0.0 { -n } else { n @@ -443,8 +490,16 @@ fn artwork(doc: &mut Document, d: &Dimension, old: &[NodeId]) -> Result n, }; if d.mode == DimensionMode::Linear && !o.hide_extensions { + // Each extension line runs from its point toward the dimension line, which can lie on + // different sides of the two points of a horizontal or vertical dimension. for (p, q) in [(d.a, a), (d.b, b)] { - paths.push((line(p + outward * o.extension_offset, q + outward * o.extension_length), appearance(o, mode, true, false))); + let side = (q - p).dot(n); + let toward = match side { + s if s < 0.0 => -n, + s if s > 0.0 => n, + _ => outward, + }; + paths.push((line(p + toward * o.extension_offset, q + toward * o.extension_length), appearance(o, mode, true, false))); } } angle = u.atan2(); diff --git a/crates/engine/tests/it/dimensions.rs b/crates/engine/tests/it/dimensions.rs index 16deb3d24..bcb684b0a 100644 --- a/crates/engine/tests/it/dimensions.rs +++ b/crates/engine/tests/it/dimensions.rs @@ -210,6 +210,313 @@ fn clicking_two_points_then_placing_measures_between_them() { assert_eq!(text(&s, id), "72.00 pt"); } +/// After two clicked points, a pointer beside them (between the perpendiculars at the points) +/// gives the distance between them (aligned), as does clicking again on the second point. Past +/// either point, the quarter that holds the direction straight up or down from it gives their +/// horizontal distance, and the quarter that holds the direction straight left or right gives +/// their vertical distance. The dimension stays linked to the anchors it measures. +#[test] +fn two_points_measure_horizontally_vertically_or_aligned() { + use vectorcraft_doc::dimension::Orientation; + for ((x, y), want, orientation) in [ + ((210.0, 120.0), "100.00 pt", Orientation::Horizontal), + ((90.0, 380.0), "100.00 pt", Orientation::Horizontal), + ((280.0, 220.0), "100.00 pt", Orientation::Vertical), + ((40.0, 310.0), "100.00 pt", Orientation::Vertical), + ((120.0, 160.0), "141.42 pt", Orientation::Aligned), + ((200.0, 300.0), "141.42 pt", Orientation::Aligned), + ((200.0, 200.0), "141.42 pt", Orientation::Aligned), + ] { + let mut s = session(); + let line = run(&mut s, "shape.line", json!({"x1":100,"y1":300,"x2":200,"y2":200}))["id"].as_u64().unwrap(); + s.select_tool("dimension", ViewInfo::default()).unwrap(); + for (px, py) in [(100.0, 300.0), (200.0, 200.0), (x, y)] { + ev(&mut s, PointerKind::Move, px, py); + ev(&mut s, PointerKind::Down, px, py); + ev(&mut s, PointerKind::Up, px, py); + } + let id = s.doc().unwrap().selection.objects[0].0; + assert_eq!((text(&s, id), recipe(&s, id).orientation), (want.to_string(), orientation), "pointer at ({x}, {y})"); + assert!(recipe(&s, id).source.is_some_and(|src| src.nodes() == vec![NodeId(line), NodeId(line)])); + if orientation != Orientation::Aligned { + run(&mut s, "select.set", json!({"ids":[line]})); + run(&mut s, "object.scale", json!({"sx":200,"sy":200})); + assert_eq!(text(&s, id), "200.00 pt", "follows the art"); + } + } +} + +/// A third click a little past the second point, still within reach of it, places an aligned +/// dimension, also when Shift moved the second point away from where the pointer was: the click +/// counts as clicking again on the second point. +#[test] +fn clicking_again_by_the_second_point_places_an_aligned_dimension() { + use vectorcraft_doc::dimension::Orientation; + let click = |s: &mut Session, x: f64, y: f64, shift: bool| { + for kind in [PointerKind::Move, PointerKind::Down, PointerKind::Up] { + let e = PointerEvent::new(kind, x, y).with_mods(Mods { shift, ..Mods::default() }); + s.pointer(&e, ViewInfo::default()).unwrap(); + } + }; + // Free points, the third click 2.2 pt past the second. + let mut s = session(); + s.select_tool("dimension", ViewInfo::default()).unwrap(); + click(&mut s, 100.0, 300.0, false); + click(&mut s, 200.0, 200.0, false); + click(&mut s, 202.0, 201.0, false); + let id = s.doc().unwrap().selection.objects[0].0; + assert_eq!((text(&s, id), recipe(&s, id).orientation), ("141.42 pt".to_string(), Orientation::Aligned)); + // Shift puts the second point on the 45 degree line, about 21 pt from the pointer. + let mut s = session(); + s.select_tool("dimension", ViewInfo::default()).unwrap(); + click(&mut s, 100.0, 300.0, false); + click(&mut s, 260.0, 170.0, true); + click(&mut s, 262.0, 168.0, false); + let id = s.doc().unwrap().selection.objects[0].0; + assert_eq!((text(&s, id), recipe(&s, id).orientation), ("206.16 pt".to_string(), Orientation::Aligned)); +} + +/// Dragged out from a slanted segment, the dimension follows the drag the same way, wherever on +/// the segment the press was: beside the segment it measures its length, and past either end +/// its horizontal or vertical length. +#[test] +fn a_segment_drag_measures_horizontally_vertically_or_aligned() { + use vectorcraft_doc::dimension::Orientation; + for (press, (x, y), want, orientation) in [ + ((150.0, 250.0), (210.0, 120.0), "100.00 pt", Orientation::Horizontal), + ((110.0, 290.0), (210.0, 120.0), "100.00 pt", Orientation::Horizontal), + ((150.0, 250.0), (280.0, 220.0), "100.00 pt", Orientation::Vertical), + ((190.0, 210.0), (40.0, 310.0), "100.00 pt", Orientation::Vertical), + ((150.0, 250.0), (120.0, 160.0), "141.42 pt", Orientation::Aligned), + ((110.0, 290.0), (200.0, 300.0), "141.42 pt", Orientation::Aligned), + ] { + let mut s = session(); + run(&mut s, "shape.line", json!({"x1":100,"y1":300,"x2":200,"y2":200})); + s.select_tool("dimension", ViewInfo::default()).unwrap(); + ev(&mut s, PointerKind::Move, press.0, press.1); + ev(&mut s, PointerKind::Down, press.0, press.1); + ev(&mut s, PointerKind::Drag, (press.0 + x) / 2.0, (press.1 + y) / 2.0); + ev(&mut s, PointerKind::Drag, x, y); + ev(&mut s, PointerKind::Up, x, y); + let id = s.doc().unwrap().selection.objects[0].0; + assert_eq!((text(&s, id), recipe(&s, id).orientation), (want.to_string(), orientation), "pressed at {press:?}, dragged to ({x}, {y})"); + } +} + +/// Two points on one vertical line give an aligned dimension wherever the pointer goes: their +/// vertical distance is the distance between them, and they have no horizontal distance. +#[test] +fn points_on_a_vertical_line_measure_aligned() { + use vectorcraft_doc::dimension::Orientation; + for (x, y) in [(60.0, 200.0), (160.0, 80.0), (100.0, 40.0), (40.0, 340.0), (100.0, 300.0)] { + let mut s = session(); + s.select_tool("dimension", ViewInfo::default()).unwrap(); + for (px, py) in [(100.0, 100.0), (100.0, 300.0), (x, y)] { + ev(&mut s, PointerKind::Move, px, py); + ev(&mut s, PointerKind::Down, px, py); + ev(&mut s, PointerKind::Up, px, py); + } + let id = s.doc().unwrap().selection.objects[0].0; + assert_eq!((text(&s, id), recipe(&s, id).orientation), ("200.00 pt".to_string(), Orientation::Aligned), "pointer at ({x}, {y})"); + } +} + +/// The ends of a dimension's generated paths (extension lines first, then the dimension line), +/// rounded. +fn path_ends(s: &Session, id: u64) -> Vec> { + let round = |v: f64| (v * 1e6).round() / 1e6; + let doc = &s.doc().unwrap().doc; + let art = doc.node(NodeId(id)).unwrap().children().unwrap(); + art.iter().filter_map(|n| n.path_data()).map(|p| p.subpaths[0].anchors.iter().map(|a| (round(a.p.x), round(a.p.y))).collect()).collect() +} + +/// A horizontal dimension's line is horizontal and a vertical one's vertical, through the point +/// it was placed at, and each extension line runs from its point to the dimension line. Points +/// given right to left or bottom to top draw the same lines. +#[test] +fn horizontal_and_vertical_dimensions_draw_lines_along_their_axis() { + let mut s = session(); + let r = run(&mut s, "dimension.create", json!({"a":[100,300],"b":[200,200],"orientation":"horizontal","offset":-150})); + assert_eq!(r["label"], "100.00 pt"); + // Extension lines: 4 pt off each point, 6 pt past the line at y = 150. + let horizontal = vec![vec![(100.0, 296.0), (100.0, 144.0)], vec![(200.0, 196.0), (200.0, 144.0)], vec![(100.0, 150.0), (200.0, 150.0)]]; + assert_eq!(path_ends(&s, r["id"].as_u64().unwrap()), horizontal); + let r = run(&mut s, "dimension.create", json!({"a":[200,200],"b":[100,300],"orientation":"horizontal","offset":-50})); + let swapped = vec![vec![(200.0, 196.0), (200.0, 144.0)], vec![(100.0, 296.0), (100.0, 144.0)], vec![(200.0, 150.0), (100.0, 150.0)]]; + assert_eq!(path_ends(&s, r["id"].as_u64().unwrap()), swapped, "the line stays on the side the offset puts it"); + // A vertical dimension 50 pt left of the first point: the line at x = 50. + let r = run(&mut s, "dimension.create", json!({"a":[100,300],"b":[200,200],"orientation":"vertical","offset":50})); + assert_eq!(r["label"], "100.00 pt"); + let vertical = vec![vec![(96.0, 300.0), (44.0, 300.0)], vec![(196.0, 200.0), (44.0, 200.0)], vec![(50.0, 300.0), (50.0, 200.0)]]; + assert_eq!(path_ends(&s, r["id"].as_u64().unwrap()), vertical); +} + +/// A horizontal dimension stays horizontal when it or the art it measures turns: it shows the +/// new horizontal distance, and 0 when the points come one above the other. It is not removed. +#[test] +fn turned_horizontal_dimensions_stay_horizontal() { + use vectorcraft_doc::dimension::Orientation; + let mut s = session(); + let id = run(&mut s, "dimension.create", json!({"a":[100,200],"b":[200,260],"orientation":"horizontal","offset":-50}))["id"].as_u64().unwrap(); + run(&mut s, "select.set", json!({"ids":[id]})); + run(&mut s, "object.rotate", json!({"angle":90,"origin":[150,230]})); + assert_eq!((text(&s, id), recipe(&s, id).orientation), ("60.00 pt".to_string(), Orientation::Horizontal)); + // The points are now at y = 180 and 280: the line stays 50 pt above the upper one instead of + // crossing between them. + let d = recipe(&s, id); + let mut ys = [d.a.y.round(), d.b.y.round()]; + ys.sort_by(f64::total_cmp); + assert_eq!(ys, [180.0, 280.0]); + assert!(path_ends(&s, id)[2].iter().all(|(_, y)| (y - 130.0).abs() < 1e-6), "{:?}", path_ends(&s, id)); + let line = run(&mut s, "shape.line", json!({"x1":100,"y1":400,"x2":200,"y2":400}))["id"].as_u64().unwrap(); + let anchor = |anchor: u64| json!({"node":line,"subpath":0,"anchor":anchor}); + let src = json!({"kind":"points","a":anchor(0),"b":anchor(1)}); + let id = run(&mut s, "dimension.create", json!({"source":src,"orientation":"horizontal"}))["id"].as_u64().unwrap(); + run(&mut s, "select.set", json!({"ids":[line]})); + run(&mut s, "object.rotate", json!({"angle":90})); + assert_eq!((text(&s, id), recipe(&s, id).orientation), ("0.00 pt".to_string(), Orientation::Horizontal)); + run(&mut s, "edit.undo", json!({})); + assert_eq!(text(&s, id), "100.00 pt"); +} + +/// The line of a linked horizontal dimension keeps its gap from the outermost point on its side +/// when the art turns, so it stays clear of the art: after a half turn the line is still 50 pt +/// above the upper point, which is now the other anchor. +#[test] +fn a_linked_horizontal_dimension_keeps_its_gap_from_turned_art() { + let mut s = session(); + let line = run(&mut s, "shape.line", json!({"x1":100,"y1":300,"x2":200,"y2":200}))["id"].as_u64().unwrap(); + let anchor = |anchor: u64| json!({"node":line,"subpath":0,"anchor":anchor}); + let src = json!({"kind":"points","a":anchor(0),"b":anchor(1)}); + let id = run(&mut s, "dimension.create", json!({"source":src,"orientation":"horizontal","offset":-150}))["id"].as_u64().unwrap(); + assert_eq!(path_ends(&s, id)[2], vec![(100.0, 150.0), (200.0, 150.0)]); + run(&mut s, "select.set", json!({"ids":[line]})); + run(&mut s, "object.rotate", json!({"angle":180,"origin":[150,250]})); + assert_eq!(text(&s, id), "100.00 pt"); + assert_eq!(path_ends(&s, id)[2], vec![(200.0, 150.0), (100.0, 150.0)]); + // Scaled to twice the size about the lower left point, the upper point is at y = 100. + run(&mut s, "object.scale", json!({"sx":200,"sy":200,"origin":[100,300]})); + assert_eq!(text(&s, id), "200.00 pt"); + assert_eq!(path_ends(&s, id)[2], vec![(300.0, 50.0), (100.0, 50.0)]); + run(&mut s, "edit.undo", json!({})); + run(&mut s, "edit.undo", json!({})); + assert_eq!(path_ends(&s, id)[2], vec![(100.0, 150.0), (200.0, 150.0)]); +} + +/// An unlinked horizontal dimension reflected on its own has its line where the reflection puts +/// it: a line above the points is below them after a flip top to bottom. +#[test] +fn a_reflected_horizontal_dimension_keeps_its_line_on_the_reflected_side() { + let mut s = session(); + let id = run(&mut s, "dimension.create", json!({"a":[100,300],"b":[200,200],"orientation":"horizontal","offset":-150}))["id"].as_u64().unwrap(); + run(&mut s, "select.set", json!({"ids":[id]})); + run(&mut s, "object.reflect", json!({"axis":"horizontal","origin":[0,250]})); + let d = recipe(&s, id); + assert_eq!((d.a.y.round(), d.b.y.round()), (200.0, 300.0)); + assert_eq!(path_ends(&s, id)[2], vec![(100.0, 350.0), (200.0, 350.0)]); + assert_eq!(text(&s, id), "100.00 pt"); +} + +/// The line of a linked horizontal dimension below both points stays below them when the art +/// turns a half turn; a line between the points keeps its distance from the first point. +#[test] +fn a_linked_horizontal_dimension_below_or_between_its_points_follows_turned_art() { + for (offset, want) in [(50.0, vec![(200.0, 350.0), (100.0, 350.0)]), (-50.0, vec![(200.0, 150.0), (100.0, 150.0)])] { + let mut s = session(); + let line = run(&mut s, "shape.line", json!({"x1":100,"y1":300,"x2":200,"y2":200}))["id"].as_u64().unwrap(); + let anchor = |anchor: u64| json!({"node":line,"subpath":0,"anchor":anchor}); + let src = json!({"kind":"points","a":anchor(0),"b":anchor(1)}); + let id = run(&mut s, "dimension.create", json!({"source":src,"orientation":"horizontal","offset":offset}))["id"].as_u64().unwrap(); + run(&mut s, "select.set", json!({"ids":[line]})); + run(&mut s, "object.rotate", json!({"angle":180,"origin":[150,250]})); + assert_eq!(text(&s, id), "100.00 pt"); + assert_eq!(path_ends(&s, id)[2], want, "offset {offset}"); + } +} + +/// A dimension recipe without an orientation reads as aligned. +#[test] +fn a_dimension_without_an_orientation_reads_as_aligned() { + use vectorcraft_doc::dimension::{Dimension, Orientation}; + let mut s = session(); + let id = run(&mut s, "dimension.create", json!({"a":[0,0],"b":[30,40]}))["id"].as_u64().unwrap(); + let v = serde_json::to_value(recipe(&s, id)).unwrap(); + assert!(v.get("orientation").is_none()); + let d: Dimension = serde_json::from_value(v).unwrap(); + assert_eq!(d.orientation, Orientation::Aligned); +} + +/// `orientation` is checked on create and update, and only linear dimensions take one. +#[test] +fn dimension_orientation_is_checked() { + let mut s = session(); + for (p, want) in [ + (json!({"a":[0,0],"b":[30,40],"orientation":"horizontal"}), Some("30.00 pt")), + (json!({"a":[0,0],"b":[30,40],"orientation":"vertical"}), Some("40.00 pt")), + (json!({"a":[0,0],"b":[30,40],"orientation":"aligned"}), Some("50.00 pt")), + (json!({"a":[0,0],"b":[0,40],"orientation":"horizontal"}), Some("0.00 pt")), + (json!({"a":[0,0],"b":[30,40],"orientation":"diagonal"}), None), + (json!({"a":[0,0],"b":[30,40],"orientation":7}), None), + (json!({"a":[0,0],"b":[30,40],"orientation":null}), None), + (json!({"mode":"angular","a":[0,0],"b":[30,40],"vertex":[10,0],"orientation":"horizontal"}), None), + ] { + let before = serde_json::to_value(&*s.doc().unwrap().doc).unwrap(); + match (s.execute("dimension.create", &p), want) { + (Ok(r), Some(want)) => assert_eq!(r["label"], want, "{p}"), + (Err(_), None) => assert_eq!(serde_json::to_value(&*s.doc().unwrap().doc).unwrap(), before, "{p}"), + (r, _) => panic!("{p}: {r:?}"), + } + } + // An aligned dimension is written as before, without the field. + let doc = serde_json::to_string(&*s.doc().unwrap().doc).unwrap(); + assert_eq!(doc.matches("\"orientation\"").count(), 3, "only the horizontal and vertical ones"); + // dimension.update turns an existing linear dimension, and refuses the same bad values. + let id = run(&mut s, "dimension.create", json!({"a":[0,0],"b":[30,40]}))["id"].as_u64().unwrap(); + let angle = run(&mut s, "dimension.create", json!({"mode":"angular","a":[0,0],"b":[30,40],"vertex":[10,0]}))["id"].as_u64().unwrap(); + for (orientation, want) in [("horizontal", "30.00 pt"), ("vertical", "40.00 pt"), ("aligned", "50.00 pt")] { + run(&mut s, "dimension.update", json!({"ids":[id],"orientation":orientation})); + assert_eq!(text(&s, id), want); + } + run(&mut s, "dimension.update", json!({"ids":[id],"orientation":"vertical"})); + run(&mut s, "edit.undo", json!({})); + assert_eq!(text(&s, id), "50.00 pt", "one undo step"); + let before = serde_json::to_value(&*s.doc().unwrap().doc).unwrap(); + for p in [ + json!({"ids":[id],"orientation":"diagonal"}), + json!({"ids":[id],"orientation":7}), + json!({"ids":[angle],"orientation":"vertical"}), + // A linear and an angular dimension together: neither changes. + json!({"ids":[id, angle],"orientation":"vertical"}), + ] { + assert!(s.execute("dimension.update", &p).is_err(), "{p}"); + assert_eq!(serde_json::to_value(&*s.doc().unwrap().doc).unwrap(), before, "{p}"); + } + // The orientation it already has, or aligned for an angular one, changes nothing and records + // no undo step: the undo after it goes back to the vertical dimension of the earlier update. + for p in [json!({"ids":[id],"orientation":"aligned"}), json!({"ids":[angle],"orientation":"aligned"})] { + run(&mut s, "dimension.update", p); + assert_eq!(serde_json::to_value(&*s.doc().unwrap().doc).unwrap(), before); + } + run(&mut s, "edit.undo", json!({})); + assert_eq!(text(&s, id), "40.00 pt"); +} + +/// A dimension turned with `dimension.update` keeps its line on the side of the points it was +/// on: an aligned line 30 pt above a pair made right to left stays 30 pt above as horizontal, +/// and an aligned line below and right of slanted points goes 30 pt below the lower point. +#[test] +fn a_turned_dimension_keeps_its_line_on_its_side() { + let mut s = session(); + let id = run(&mut s, "dimension.create", json!({"a":[200,200],"b":[100,200],"offset":30}))["id"].as_u64().unwrap(); + assert_eq!(path_ends(&s, id)[2], vec![(200.0, 170.0), (100.0, 170.0)]); + run(&mut s, "dimension.update", json!({"ids":[id],"orientation":"horizontal"})); + assert_eq!(path_ends(&s, id)[2], vec![(200.0, 170.0), (100.0, 170.0)]); + // The middle of the aligned line (about 171, 271) lies between the points' heights. + let id = run(&mut s, "dimension.create", json!({"a":[100,300],"b":[200,200],"offset":30}))["id"].as_u64().unwrap(); + run(&mut s, "dimension.update", json!({"ids":[id],"orientation":"horizontal"})); + assert_eq!(path_ends(&s, id)[2], vec![(100.0, 330.0), (200.0, 330.0)]); +} + /// Dragging out from a corner of two straight segments measures the angle between them. #[test] fn dragging_out_from_a_corner_measures_its_angle() { diff --git a/crates/tools/src/dimension.rs b/crates/tools/src/dimension.rs index 53fbd3f3f..d13227369 100644 --- a/crates/tools/src/dimension.rs +++ b/crates/tools/src/dimension.rs @@ -6,7 +6,7 @@ use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, Tool use serde_json::{Value, json}; use vectorcraft_doc::{ Node, - dimension::{self, Dimension, DimensionMode, DimensionOptions, DimensionSource, PointRef, SegmentRef}, + dimension::{self, Dimension, DimensionMode, DimensionOptions, DimensionSource, Orientation, PointRef, SegmentRef}, }; use vectorcraft_geom::{BezPath, Point, Vec2}; @@ -69,6 +69,42 @@ pub struct DimensionTool { snapped: Option<(Point, Option)>, /// Whether the press under way began on an anchor a Linear click picks. press_on_anchor: bool, + /// The second point clicked for the Linear dimension being placed, and where the pointer was + /// for that click (a snap or Shift can move the point away from it): placed with the pointer + /// still at either, it is aligned. + second: Option<(Point, Point)>, +} + +/// What a linear dimension between `a` and `b` measures with the pointer at `p` while it is +/// placed (VectorCraft's own rule). Beside the points, where the nearest point of the line +/// through them lies between them, the dimension is aligned: it measures the distance between +/// them. Past either point it measures along an axis. The line through the points and the +/// perpendicular at that point bound a quarter on each side; the pointer's quarter holds either +/// the straight up or down direction, which gives the horizontal distance, or the straight left +/// or right direction, which gives the vertical distance. Points on one horizontal or vertical +/// line only give an aligned dimension. +fn orientation_at(a: Point, b: Point, p: Point) -> Orientation { + let u = b - a; + let len = u.hypot(); + if len < 1e-9 { + return Orientation::Aligned; + } + let along = (p - a).dot(u) / (len * len); + if (0.0..=1.0).contains(&along) { + return Orientation::Aligned; + } + // Out past the nearer point, and the side of the line the pointer is on. + let (out, side) = (if along > 1.0 { u } else { -u }, u.cross(p - a)); + let axes = [ + (Vec2::new(0.0, 1.0), Orientation::Horizontal), + (Vec2::new(0.0, -1.0), Orientation::Horizontal), + (Vec2::new(1.0, 0.0), Orientation::Vertical), + (Vec2::new(-1.0, 0.0), Orientation::Vertical), + ]; + let within = len * 1e-9; + axes.into_iter() + .find(|(axis, _)| axis.dot(out) > within && u.cross(*axis).abs() > within && u.cross(*axis) * side > 0.0) + .map_or(Orientation::Aligned, |(_, orientation)| orientation) } /// How near an anchor (screen pixels) a pick snaps to it. @@ -214,7 +250,18 @@ impl DimensionTool { } } fn recipe(&self, mode: DimensionMode, a: Point, b: Point, vertex: Point, source: Option) -> Dimension { - Dimension { mode, a, b, vertex, offset: 0.0, length: a.distance(b), options: self.style.clone(), source, moved: None } + Dimension { + mode, + a, + b, + vertex, + offset: 0.0, + length: a.distance(b), + options: self.style.clone(), + source, + orientation: Orientation::Aligned, + moved: None, + } } /// The dimension a target gives when dragged from. fn dimension_for(&self, target: &Target, p: Point) -> Option { @@ -247,6 +294,9 @@ impl DimensionTool { if let Some(source) = &d.source { p["source"] = json!(source); } + if d.orientation != Orientation::Aligned { + p["orientation"] = json!(d.orientation); + } p } fn preview(&mut self, cx: &ToolContext, p: Point) -> Vec { @@ -256,6 +306,18 @@ impl DimensionTool { { d.mode = if p.distance(*center) >= *radius { DimensionMode::Radius } else { DimensionMode::Diameter }; } + if d.mode == DimensionMode::Linear { + // The points the dimension measures: a dragged segment's ends, read from the art. + let ends = match &d.source { + Some(DimensionSource::Segment { edge }) => edge.endpoints(cx.doc), + _ => Some((d.a, d.b)), + }; + let again = self.second.is_some_and(|(at, pointer)| at.distance(p).min(pointer.distance(p)) <= cx.tol(ANCHOR_REACH)); + d.orientation = match ends { + Some((a, b)) if !again => orientation_at(a, b, p), + _ => Orientation::Aligned, + }; + } d.place(p); if d.resolve(cx.doc).is_err() { return vec![]; @@ -287,6 +349,7 @@ impl DimensionTool { self.began = false; self.snap.clear(); self.snapped = None; + self.second = None; } /// A plain click: picks a point or a line, or measures a circle's radius. fn click(&mut self, cx: &ToolContext, p: Point, target: Option, mods: Mods) -> Vec { @@ -304,6 +367,7 @@ impl DimensionTool { let source = (ra.is_some() || rb.is_some()).then(|| DimensionSource::Points { a: ra.clone(), b: rb.clone(), vertex: None }); let mut d = self.recipe(DimensionMode::Linear, *a, *b, Point::ZERO, source); if d.resolve(cx.doc).is_ok() { + self.second = Some((*b, p)); self.pending = Some(d); } self.points.clear(); @@ -655,6 +719,47 @@ mod tests { assert_eq!(t.points, vec![corner]); } + /// Beside the two points the dimension is aligned; past either point it is horizontal when + /// the pointer's quarter holds the straight up or down direction and vertical when it holds + /// the straight left or right direction. Points on one horizontal or vertical line, a pointer + /// on the line through the points, and two equal points give aligned. + #[test] + fn the_pointer_chooses_aligned_horizontal_or_vertical() { + use Orientation::{Aligned, Horizontal, Vertical}; + let at = |a: (f64, f64), b: (f64, f64), p: (f64, f64)| orientation_at(Point::new(a.0, a.1), Point::new(b.0, b.1), Point::new(p.0, p.1)); + // A line rising to the right (y grows downwards), and the same points in the other order. + for (a, b) in [((100.0, 300.0), (200.0, 200.0)), ((200.0, 200.0), (100.0, 300.0))] { + for (p, want) in [ + ((120.0, 160.0), Aligned), + ((200.0, 300.0), Aligned), + ((150.0, 250.0), Aligned), + ((200.0, 200.0), Aligned), + ((100.0, 300.0), Aligned), + ((210.0, 120.0), Horizontal), + ((240.0, 150.0), Horizontal), + ((280.0, 220.0), Vertical), + ((240.0, 170.0), Vertical), + ((40.0, 310.0), Vertical), + ((90.0, 380.0), Horizontal), + ((250.0, 150.0), Aligned), + ] { + assert_eq!(at(a, b, p), want, "{a:?} to {b:?}, pointer at {p:?}"); + } + } + // A line falling to the right. + let (a, b) = ((100.0, 100.0), (300.0, 200.0)); + for (p, want) in + [((200.0, 50.0), Aligned), ((320.0, 300.0), Horizontal), ((400.0, 210.0), Vertical), ((60.0, 20.0), Horizontal), ((20.0, 90.0), Vertical)] + { + assert_eq!(at(a, b, p), want, "pointer at {p:?}"); + } + for (a, b) in [((100.0, 100.0), (300.0, 100.0)), ((100.0, 100.0), (100.0, 300.0)), ((100.0, 100.0), (100.0, 100.0))] { + for p in [(50.0, 50.0), (350.0, 50.0), (350.0, 350.0), (50.0, 350.0), (200.0, 60.0), (60.0, 200.0), (400.0, 100.0), (100.0, 400.0)] { + assert_eq!(at(a, b, p), Aligned, "{a:?} to {b:?}, pointer at {p:?}"); + } + } + } + #[test] fn placement_press_previews_without_a_prior_move() { let (d, _) = doc_with_rect(); diff --git a/crates/ui-egui/src/chrome/hints.rs b/crates/ui-egui/src/chrome/hints.rs index 7d85a07be..062059e6d 100644 --- a/crates/ui-egui/src/chrome/hints.rs +++ b/crates/ui-egui/src/chrome/hints.rs @@ -6,6 +6,8 @@ fn hint_for(tool: &str) -> Option<&'static [(&'static str, bool)]> { "dimension" => &[ ("Drag", true), (" out from a highlighted edge to place its dimension | ", false), + ("Click", true), + (" two points, then move and click to place an aligned, horizontal or vertical dimension | ", false), ("Esc", true), (" cancels | Double-click the tool for options", false), ], diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 354d811a3..bd6eca6ef 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5502,6 +5502,7 @@ theme Appearance Външен вид Preview (1 inch on the page) Преглед (1 инч на страницата) Set as Default Задаване по подразбиране out from a highlighted edge to place its dimension | навън от осветен ръб, за да поставите размера му | + two points, then move and click to place an aligned, horizontal or vertical dimension | върху две точки, после преместете и щракнете, за да поставите подравнен, хоризонтален или вертикален размер | cancels | Double-click the tool for options отказва | Двойно щракване върху инструмента за опции Angular Ъглов Away from the Line Встрани от линията @@ -5533,12 +5534,14 @@ theme Appearance Външен вид @msg Measured anchor no longer exists Измерената опорна точка вече не съществува @msg Measured segment no longer exists Измереният сегмент вече не съществува @msg Non-finite dimension Размерът не е крайно число +@msg Only linear dimensions are horizontal or vertical Само линейните размери са хоризонтални или вертикални @msg Scale Text Attributes: selected type has no scale to bake Мащабиране на текстови атрибути: избраният текст няма мащаб за прилагане @msg Scale must be drawing:real, for example 1:10 Мащабът трябва да е чертеж:реалност, например 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Съотношението на мащаба трябва да е между 1:1000000 и 1000000:1 @msg Second measured segment no longer exists Вторият измерен сегмент вече не съществува @msg Unknown dimension mode Непознат режим на размера @msg Unknown dimension option {k} Непозната опция за размер {k} +@msg Unknown dimension orientation Непозната ориентация на размера @msg Unknown dimension style Непознат стил на размера @msg Unknown unit Непозната единица Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Затваря кръговите краища на отворени профили. При частично завъртане срязаните страни остават отворени. diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index d1fc0ea54..c3a9baad4 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5551,6 +5551,7 @@ mark-style Japanese Japanski Preview (1 inch on the page) Pregled (1 inč na stranici) Set as Default Postavi kao zadano out from a highlighted edge to place its dimension | od istaknute ivice da postavite njenu kotu | + two points, then move and click to place an aligned, horizontal or vertical dimension | na dvije tačke, zatim pomjerite i kliknite da postavite poravnatu, horizontalnu ili vertikalnu kotu | cancels | Double-click the tool for options otkazuje | Dvostruki klik na alat za opcije Angular Ugaono Away from the Line Dalje od linije @@ -5581,12 +5582,14 @@ mark-style Japanese Japanski @msg Measured anchor no longer exists Izmjereno sidro više ne postoji @msg Measured segment no longer exists Izmjereni segment više ne postoji @msg Non-finite dimension Nekonačna kota +@msg Only linear dimensions are horizontal or vertical Samo linearne kote su horizontalne ili vertikalne @msg Scale Text Attributes: selected type has no scale to bake Skaliraj atribute teksta: odabrani tekst nema skalu za primjenu @msg Scale must be drawing:real, for example 1:10 Skala mora biti crtež:stvarno, npr. 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Omjer skale mora biti između 1:1000000 i 1000000:1 @msg Second measured segment no longer exists Drugi izmjereni segment više ne postoji @msg Unknown dimension mode Nepoznat način kote @msg Unknown dimension option {k} Nepoznata opcija kote {k} +@msg Unknown dimension orientation Nepoznata orijentacija kote @msg Unknown dimension style Nepoznat stil kote @msg Unknown unit Nepoznata jedinica Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Zatvori kružne krajeve otvorenih profila. Djelimična izvlačenja zadržavaju otvorene rezane strane. diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 9e77ecb32..264af4775 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -35,6 +35,7 @@ on the canvas to create one al llenç per crear-ne un on the canvas to create one ( al llenç per crear-ne un ( out from a highlighted edge to place its dimension | des d’una vora ressaltada cap enfora per col·locar-ne la dimensió | + two points, then move and click to place an aligned, horizontal or vertical dimension | dos punts, després moveu i feu clic per col·locar una dimensió alineada, horitzontal o vertical | over paths to roughen their outlines | sobre traçats per fer aspres els contorns | over paths, and hold still to keep going | sobre traçats i mantén el punter quiet per continuar | right or left to zoom in or out | a la dreta o a l’esquerra per ampliar o reduir | @@ -5515,6 +5516,7 @@ digits Malayalam Malaiàlam @msg node {_1} is not text el node {_1} no és text @msg node {_1} is on a locked artboard el node {_1} és en un tauler bloquejat @msg Non-finite dimension Cota no finita +@msg Only linear dimensions are horizontal or vertical Només les dimensions lineals són horitzontals o verticals @msg not a blend no és una fusió @msg not a DXF drawing no és un dibuix DXF @msg not a file no és un fitxer @@ -5885,6 +5887,7 @@ digits Malayalam Malaiàlam @msg unknown control `{name}`: fillStroke, coloring, drawMode or screenMode control desconegut `{name}`: fillStroke, coloring, drawMode o screenMode @msg Unknown dimension mode Mode de dimensió desconegut @msg Unknown dimension option {k} Opció de dimensió desconeguda {k} +@msg Unknown dimension orientation Orientació de dimensió desconeguda @msg Unknown dimension style Estil de dimensió desconegut @msg unknown effect `{id}` efecte desconegut `{id}` @msg unknown format format desconegut diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index de1723b90..39c772990 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5682,6 +5682,7 @@ mark-style Japanese Japonské Preview (1 inch on the page) Náhled (1 palec na stránce) Set as Default Nastavit jako výchozí out from a highlighted edge to place its dimension | ze zvýrazněné hrany ven a umístíte její kótu | + two points, then move and click to place an aligned, horizontal or vertical dimension | na dva body, pak posuňte ukazatel a klepnutím umístěte zarovnanou, vodorovnou nebo svislou kótu | cancels | Double-click the tool for options zruší | Dvojitým kliknutím na nástroj otevřete volby Angular Úhlová Away from the Line Dál od čáry @@ -5712,12 +5713,14 @@ mark-style Japanese Japonské @msg Measured anchor no longer exists Měřený kotevní bod už neexistuje @msg Measured segment no longer exists Měřený segment už neexistuje @msg Non-finite dimension Kóta není konečné číslo +@msg Only linear dimensions are horizontal or vertical Vodorovné nebo svislé jsou jen lineární kóty @msg Scale Text Attributes: selected type has no scale to bake Měnit velikost atributů textu: vybraný text nemá žádnou skalaci, kterou lze použít @msg Scale must be drawing:real, for example 1:10 Měřítko musí mít tvar výkres:skutečnost, například 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Poměr měřítka musí být mezi 1:1000000 a 1000000:1 @msg Second measured segment no longer exists Druhý měřený segment už neexistuje @msg Unknown dimension mode Neznámý režim kóty @msg Unknown dimension option {k} Neznámá volba kóty {k} +@msg Unknown dimension orientation Neznámá orientace kóty @msg Unknown dimension style Neznámý styl kóty @msg Unknown unit Neznámá jednotka # Revolve cap description diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 77687493e..89a302521 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5272,6 +5272,7 @@ mark-style Japanese Japansk Preview (1 inch on the page) Forhåndsvisning (1 tomme på siden) Set as Default Angiv som standard out from a highlighted edge to place its dimension | ud fra en fremhævet kant for at placere dens mål | + two points, then move and click to place an aligned, horizontal or vertical dimension | på to punkter, flyt derefter, og klik for at placere et justeret, vandret eller lodret mål | cancels | Double-click the tool for options annullerer | Dobbeltklik på værktøjet for indstillinger Angular Vinklet Away from the Line Væk fra linjen @@ -5303,12 +5304,14 @@ mark-style Japanese Japansk @msg Measured anchor no longer exists Det målte ankerpunkt findes ikke længere @msg Measured segment no longer exists Det målte segment findes ikke længere @msg Non-finite dimension Ikke-endeligt mål +@msg Only linear dimensions are horizontal or vertical Kun lineære mål er vandrette eller lodrette @msg Scale Text Attributes: selected type has no scale to bake Skalér tekstattributter: den valgte tekst har ingen skalering at anvende @msg Scale must be drawing:real, for example 1:10 Målestokken skal være tegning:virkelig, for eksempel 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Målestoksforholdet skal være mellem 1:1000000 og 1000000:1 @msg Second measured segment no longer exists Det andet målte segment findes ikke længere @msg Unknown dimension mode Ukendt måltilstand @msg Unknown dimension option {k} Ukendt målindstilling {k} +@msg Unknown dimension orientation Ukendt målretning @msg Unknown dimension style Ukendt målstil @msg Unknown unit Ukendt enhed Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Luk de runde ender af åbne profiler. Delvise omdrejninger beholder deres skårne sider åbne. diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 7e2619386..d6a221470 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5255,6 +5255,7 @@ effect Soft Weich Preview (1 inch on the page) Vorschau (1 Zoll auf der Seite) Set as Default Als Standard festlegen out from a highlighted edge to place its dimension | von einer hervorgehobenen Kante nach außen, um ihre Bemaßung zu platzieren | + two points, then move and click to place an aligned, horizontal or vertical dimension | auf zwei Punkte, dann bewegen und klicken, um eine ausgerichtete, horizontale oder vertikale Bemaßung zu platzieren | cancels | Double-click the tool for options bricht ab | Doppelklick auf das Werkzeug für Optionen Linear Linear Angular Winkel @@ -5289,12 +5290,14 @@ effect Soft Weich @msg Measured anchor no longer exists Der gemessene Ankerpunkt existiert nicht mehr @msg Measured segment no longer exists Das gemessene Segment existiert nicht mehr @msg Non-finite dimension Die Bemaßung ist keine endliche Zahl +@msg Only linear dimensions are horizontal or vertical Nur lineare Bemaßungen sind horizontal oder vertikal @msg Scale Text Attributes: selected type has no scale to bake Textattribute skalieren: Der ausgewählte Text weist keine zu übernehmende Skalierung auf @msg Scale must be drawing:real, for example 1:10 Der Maßstab muss Zeichnung:Realität sein, zum Beispiel 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Das Maßstabsverhältnis muss zwischen 1:1000000 und 1000000:1 liegen @msg Second measured segment no longer exists Das zweite gemessene Segment existiert nicht mehr @msg Unknown dimension mode Unbekannter Bemaßungsmodus @msg Unknown dimension option {k} Unbekannte Bemaßungsoption {k} +@msg Unknown dimension orientation Unbekannte Bemaßungsausrichtung @msg Unknown dimension style Unbekannter Bemaßungsstil @msg Unknown unit Unbekannte Einheit # Revolve cap description diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index a95d84be3..bbcbbe09a 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5569,6 +5569,7 @@ effect Points Σημεία # Strings added upstream after this catalog was written (machine-drafted; review welcome). cancels | Double-click the tool for options για ακύρωση | Διπλό κλικ στο εργαλείο για επιλογές out from a highlighted edge to place its dimension | προς τα έξω από μια επισημασμένη ακμή για τοποθέτηση της διάστασής της | + two points, then move and click to place an aligned, horizontal or vertical dimension | σε δύο σημεία, μετά μετακινήστε και κάντε κλικ για τοποθέτηση ευθυγραμμισμένης, οριζόντιας ή κατακόρυφης διάστασης | A legacy .ai file holds one artboard: each artboard you check is saved as a separate file. Ένα αρχείο .ai παλαιού τύπου περιέχει έναν πίνακα σχεδίασης: κάθε πίνακας σχεδίασης που επιλέγετε αποθηκεύεται ως ξεχωριστό αρχείο. Arrange Intertwine Area Διάταξη περιοχής διαπλοκής Batch… Μαζική επεξεργασία… @@ -5667,12 +5668,14 @@ effect Points Σημεία @msg Measured anchor no longer exists Το μετρημένο σημείο αγκύρωσης δεν υπάρχει πλέον @msg Measured segment no longer exists Το μετρημένο τμήμα δεν υπάρχει πλέον @msg Non-finite dimension Η διάσταση δεν είναι πεπερασμένος αριθμός +@msg Only linear dimensions are horizontal or vertical Μόνο οι γραμμικές διαστάσεις είναι οριζόντιες ή κατακόρυφες @msg Scale Text Attributes: selected type has no scale to bake Κλιμάκωση ιδιοτήτων κειμένου: το επιλεγμένο κείμενο δεν έχει κλίμακα προς εφαρμογή @msg Scale must be drawing:real, for example 1:10 Η κλίμακα πρέπει να είναι σχέδιο:πραγματικό, για παράδειγμα 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Ο λόγος κλίμακας πρέπει να είναι μεταξύ 1:1000000 και 1000000:1 @msg Second measured segment no longer exists Το δεύτερο μετρημένο τμήμα δεν υπάρχει πλέον @msg Unknown dimension mode Άγνωστη λειτουργία διάστασης @msg Unknown dimension option {k} Άγνωστη επιλογή διάστασης {k} +@msg Unknown dimension orientation Άγνωστος προσανατολισμός διάστασης @msg `mode` must be grayscale, rgb, hsb, cmyk or websafe το `mode` πρέπει να είναι grayscale, rgb, hsb, cmyk ή websafe @msg `{key}` must be an array of these names: {_1} το `{key}` πρέπει να είναι πίνακας με αυτά τα ονόματα: {_1} @msg choose a destination folder επιλέξτε φάκελο προορισμού diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index e0903d19d..68cedd655 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5630,6 +5630,7 @@ mark-style Japanese Japonesas Preview (1 inch on the page) Vista previa (1 pulgada en la página) Set as Default Establecer como predeterminado out from a highlighted edge to place its dimension | hacia fuera desde un borde resaltado para colocar su cota | + two points, then move and click to place an aligned, horizontal or vertical dimension | en dos puntos, luego mueva y haga clic para colocar una cota alineada, horizontal o vertical | cancels | Double-click the tool for options cancela | Doble clic en la herramienta para ver las opciones Angular Angular Away from the Line Lejos de la línea @@ -5653,12 +5654,14 @@ mark-style Japanese Japonesas @msg Measured anchor no longer exists El punto de ancla medido ya no existe @msg Measured segment no longer exists El segmento medido ya no existe @msg Non-finite dimension Cota no finita +@msg Only linear dimensions are horizontal or vertical Solo las cotas lineales son horizontales o verticales @msg Scale Text Attributes: selected type has no scale to bake Escalar atributos de texto: el texto seleccionado no tiene escala que aplicar @msg Scale must be drawing:real, for example 1:10 La escala debe ser dibujo:real, por ejemplo 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 La relación de escala debe estar entre 1:1000000 y 1000000:1 @msg Second measured segment no longer exists El segundo segmento medido ya no existe @msg Unknown dimension mode Modo de cota desconocido @msg Unknown dimension option {k} Opción de cota desconocida {k} +@msg Unknown dimension orientation Orientación de cota desconocida @msg Unknown dimension style Estilo de cota desconocido @msg Unknown unit Unidad desconocida # Revolve cap description diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index b593200aa..af540dd78 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5581,6 +5581,7 @@ mark-style Japanese Japanilainen # Strings added upstream after this catalog was written (machine-drafted; review welcome). cancels | Double-click the tool for options peruuttaa | Kaksoisnapsauta työkalua avataksesi asetukset out from a highlighted edge to place its dimension | ulospäin korostetusta reunasta sijoittaaksesi sen mitan | + two points, then move and click to place an aligned, horizontal or vertical dimension | kahta pistettä, siirrä sitten ja napsauta sijoittaaksesi tasatun, vaaka- tai pystysuuntaisen mitan | A legacy .ai file holds one artboard: each artboard you check is saved as a separate file. Vanhassa .ai-tiedostossa on yksi piirtoalusta: jokainen valitsemasi piirtoalusta tallennetaan omaksi tiedostokseen. Arrange Intertwine Area Järjestä lomitusalue Batch… Erä… @@ -5679,12 +5680,14 @@ mark-style Japanese Japanilainen @msg Measured anchor no longer exists Mitattua ankkuripistettä ei ole enää @msg Measured segment no longer exists Mitattua segmenttiä ei ole enää @msg Non-finite dimension Mitta ei ole äärellinen luku +@msg Only linear dimensions are horizontal or vertical Vain lineaariset mitat ovat vaaka- tai pystysuuntaisia @msg Scale Text Attributes: selected type has no scale to bake Skaalaa tekstin attribuutit: valitulla tekstillä ei ole sovellettavaa skaalausta @msg Scale must be drawing:real, for example 1:10 Mittakaavan on oltava muotoa piirustus:todellinen, esimerkiksi 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Mittakaavan suhteen on oltava välillä 1:1000000–1000000:1 @msg Second measured segment no longer exists Toista mitattua segmenttiä ei ole enää @msg Unknown dimension mode Tuntematon mitoitustila @msg Unknown dimension option {k} Tuntematon mitoitusasetus {k} +@msg Unknown dimension orientation Tuntematon mitan suunta @msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode`-arvon on oltava grayscale, rgb, hsb, cmyk tai websafe @msg `{key}` must be an array of these names: {_1} `{key}`-arvon on oltava taulukko, jossa on näitä nimiä: {_1} @msg choose a destination folder valitse kohdekansio diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 6dd4e37a4..0bc4b0197 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5676,6 +5676,7 @@ theme Appearance Apparence Preview (1 inch on the page) Aperçu (1 pouce sur la page) Set as Default Définir par défaut out from a highlighted edge to place its dimension | vers l'extérieur depuis un bord mis en évidence pour placer sa cote | + two points, then move and click to place an aligned, horizontal or vertical dimension | sur deux points, puis déplacez et cliquez pour placer une cote alignée, horizontale ou verticale | cancels | Double-click the tool for options annule | Double-clic sur l'outil pour les options Angular Angulaire Away from the Line À l'écart de la ligne @@ -5707,12 +5708,14 @@ theme Appearance Apparence @msg Measured anchor no longer exists Le point d’ancrage mesuré n’existe plus @msg Measured segment no longer exists Le segment mesuré n’existe plus @msg Non-finite dimension Cote non finie +@msg Only linear dimensions are horizontal or vertical Seules les cotes linéaires sont horizontales ou verticales @msg Scale Text Attributes: selected type has no scale to bake Mettre à l’échelle les attributs de texte : le texte sélectionné n’a pas d’échelle à intégrer @msg Scale must be drawing:real, for example 1:10 L’échelle doit être dessin:réel, par exemple 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Le rapport d’échelle doit être compris entre 1:1000000 et 1000000:1 @msg Second measured segment no longer exists Le second segment mesuré n’existe plus @msg Unknown dimension mode Mode de cote inconnu @msg Unknown dimension option {k} Option de cote inconnue {k} +@msg Unknown dimension orientation Orientation de cote inconnue @msg Unknown dimension style Style de cote inconnu @msg Unknown unit Unité inconnue # Revolve cap description diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 3397e7f34..67558909f 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -27,6 +27,7 @@ on the canvas to create one बनाने के लिए कैनवास पर on the canvas to create one ( बनाने के लिए कैनवास पर ( out from a highlighted edge to place its dimension | माप रखने के लिए हाइलाइट किए किनारे से बाहर की ओर | + two points, then move and click to place an aligned, horizontal or vertical dimension | दो बिंदुओं पर, फिर संरेखित, क्षैतिज या ऊर्ध्वाधर आयाम रखने के लिए पॉइंटर ले जाएँ और क्लिक करें | over paths to roughen their outlines | आउटलाइन खुरदरी करने के लिए पाथ पर | over paths, and hold still to keep going | पाथ पर, और जारी रखने के लिए स्थिर रखें | right or left to zoom in or out | ज़ूम इन या आउट करने के लिए दाएँ या बाएँ | @@ -6963,6 +6964,7 @@ antiAlias Sharp शार्प @msg node {_1} is not type नोड {_1} टाइप नहीं है @msg node {_1} is on a locked artboard नोड {_1} एक लॉक्ड आर्टबोर्ड पर है @msg Non-finite dimension गैर-परिष्कृत आयाम +@msg Only linear dimensions are horizontal or vertical केवल रैखिक आयाम क्षैतिज या ऊर्ध्वाधर होते हैं @msg None of these issues are left: check again इन मुद्दों में से कोई भी छोड़ दिया जाता है: फिर से जांच करें @msg not a blend मिश्रण नहीं @msg not a closed path एक बंद पाथ नहीं @@ -7809,6 +7811,7 @@ antiAlias Sharp शार्प @msg unknown control `{name}`: fillStroke, coloring, drawMode or screenMode अज्ञात नियंत्रण `{name}`: fillStroke, रंग, drawMode या screenMode @msg Unknown dimension mode अज्ञात आयाम मोड @msg Unknown dimension option {k} अज्ञात आयाम ऑप्शन {k} +@msg Unknown dimension orientation अज्ञात आयाम अभिविन्यास @msg Unknown dimension style अज्ञात आयाम स्टाइल @msg unknown effect `{e}` अज्ञात इफ़ेक्ट `{e}` @msg unknown effect `{id}` अज्ञात इफ़ेक्ट `{id}` diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 4b813215e..e644a9d87 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5663,6 +5663,7 @@ imageTrace Outline Körvonal Preview (1 inch on the page) Előnézet (1 hüvelyk az oldalon) Set as Default Beállítás alapértelmezettként out from a highlighted edge to place its dimension | egy kiemelt élről kifelé a méret elhelyezéséhez | + two points, then move and click to place an aligned, horizontal or vertical dimension | két pontra, majd mozgasd a mutatót, és kattints az igazított, vízszintes vagy függőleges méret elhelyezéséhez | cancels | Double-click the tool for options megszakítja | Dupla kattintás az eszközre a beállításokhoz Angular Szög Away from the Line A vonaltól távol @@ -5694,12 +5695,14 @@ imageTrace Outline Körvonal @msg Measured anchor no longer exists A mért horgonypont már nem létezik @msg Measured segment no longer exists A mért szakasz már nem létezik @msg Non-finite dimension Nem véges méret +@msg Only linear dimensions are horizontal or vertical Csak a lineáris méretek lehetnek vízszintesek vagy függőlegesek @msg Scale Text Attributes: selected type has no scale to bake Szövegattribútumok méretezése: a kijelölt szövegnek nincs alkalmazható méretezése @msg Scale must be drawing:real, for example 1:10 A léptéknek rajz:valós alakúnak kell lennie, például 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 A lépték aránya 1:1000000 és 1000000:1 között lehet @msg Second measured segment no longer exists A második mért szakasz már nem létezik @msg Unknown dimension mode Ismeretlen méretezési mód @msg Unknown dimension option {k} Ismeretlen méretezési beállítás: {k} +@msg Unknown dimension orientation Ismeretlen méretezési tájolás @msg Unknown dimension style Ismeretlen méretezési stílus @msg Unknown unit Ismeretlen mértékegység # Revolve cap description diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 33d229c85..bb1b2deb6 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5272,6 +5272,7 @@ mark-style Japanese Jepang Preview (1 inch on the page) Pratinjau (1 inci di halaman) Set as Default Atur sebagai default out from a highlighted edge to place its dimension | keluar dari tepi yang disorot untuk menempatkan dimensinya | + two points, then move and click to place an aligned, horizontal or vertical dimension | dua titik, lalu gerakkan dan klik untuk menempatkan dimensi sejajar, horizontal, atau vertikal | cancels | Double-click the tool for options membatalkan | Klik dua kali alat untuk opsi Angular Sudut Away from the Line Menjauhi garis @@ -5303,12 +5304,14 @@ mark-style Japanese Jepang @msg Measured anchor no longer exists Titik jangkar yang diukur sudah tidak ada @msg Measured segment no longer exists Segmen yang diukur sudah tidak ada @msg Non-finite dimension Dimensi bukan bilangan berhingga +@msg Only linear dimensions are horizontal or vertical Hanya dimensi linear yang horizontal atau vertikal @msg Scale Text Attributes: selected type has no scale to bake Skalakan atribut teks: teks yang dipilih tidak memiliki skala untuk diterapkan @msg Scale must be drawing:real, for example 1:10 Skala harus dalam format gambar:sebenarnya, misalnya 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Rasio skala harus antara 1:1000000 dan 1000000:1 @msg Second measured segment no longer exists Segmen yang diukur kedua sudah tidak ada @msg Unknown dimension mode Mode dimensi tidak dikenal @msg Unknown dimension option {k} Opsi dimensi {k} tidak dikenal +@msg Unknown dimension orientation Orientasi dimensi tidak dikenal @msg Unknown dimension style Gaya dimensi tidak dikenal @msg Unknown unit Satuan tidak dikenal Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Menutup ujung melingkar profil terbuka. Sapuan parsial mempertahankan sisi potongannya tetap terbuka. diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index b5fd752e9..8994c73bc 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5290,6 +5290,7 @@ lighting Light Luce Preview (1 inch on the page) Anteprima (1 pollice sulla pagina) Set as Default Imposta come predefinito out from a highlighted edge to place its dimension | verso l'esterno da un bordo evidenziato per posizionarne la quota | + two points, then move and click to place an aligned, horizontal or vertical dimension | su due punti, poi sposta e fai clic per posizionare una quota allineata, orizzontale o verticale | cancels | Double-click the tool for options annulla | Doppio clic sullo strumento per le opzioni Linear Lineare Angular Angolare @@ -5324,12 +5325,14 @@ lighting Light Luce @msg Measured anchor no longer exists Il punto di ancoraggio misurato non esiste più @msg Measured segment no longer exists Il segmento misurato non esiste più @msg Non-finite dimension Quota non finita +@msg Only linear dimensions are horizontal or vertical Solo le quote lineari sono orizzontali o verticali @msg Scale Text Attributes: selected type has no scale to bake Scala attributi di testo: il testo selezionato non ha una scala da incorporare @msg Scale must be drawing:real, for example 1:10 La scala deve essere disegno:reale, ad esempio 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Il rapporto di scala deve essere compreso tra 1:1000000 e 1000000:1 @msg Second measured segment no longer exists Il secondo segmento misurato non esiste più @msg Unknown dimension mode Modalità di quota sconosciuta @msg Unknown dimension option {k} Opzione di quota sconosciuta {k} +@msg Unknown dimension orientation Orientamento della quota sconosciuto @msg Unknown dimension style Stile di quota sconosciuto @msg Unknown unit Unità sconosciuta # Revolve cap description diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 56fc8a590..565d9cdb5 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5341,6 +5341,7 @@ lighting Light ライト Preview (1 inch on the page) プレビュー(ページ上の 1 インチ) Set as Default デフォルトに設定 out from a highlighted edge to place its dimension | でハイライトされた辺から外側へ寸法を配置 | + two points, then move and click to place an aligned, horizontal or vertical dimension | で 2 点を指定し、移動してクリックすると平行、水平または垂直の寸法を配置 | cancels | Double-click the tool for options でキャンセル | ツールをダブルクリックしてオプション Linear 直線 Angular 角度 @@ -5367,12 +5368,14 @@ lighting Light ライト @msg Measured anchor no longer exists 測定したアンカーポイントはもう存在しません @msg Measured segment no longer exists 測定したセグメントはもう存在しません @msg Non-finite dimension 寸法が有限ではありません +@msg Only linear dimensions are horizontal or vertical 水平または垂直にできるのは直線寸法のみです @msg Scale Text Attributes: selected type has no scale to bake テキスト属性を拡大・縮小: 選択したテキストに取り込むスケールがありません @msg Scale must be drawing:real, for example 1:10 縮尺は 図面:実寸 の形式にしてください (例: 1:10) @msg Scale ratio must be between 1:1000000 and 1000000:1 縮尺の比率は 1:1000000 から 1000000:1 の間にしてください @msg Second measured segment no longer exists 2 番目の測定セグメントはもう存在しません @msg Unknown dimension mode 寸法のモードが不明です @msg Unknown dimension option {k} 不明な寸法オプション {k} +@msg Unknown dimension orientation 寸法の方向が不明です @msg Unknown dimension style 寸法のスタイルが不明です @msg Unknown unit 単位が不明です # Revolve cap description diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 388dd4a01..b3f0ef67b 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5287,6 +5287,7 @@ imageTrace Outline კონტური Preview (1 inch on the page) წინასწარი ნახვა (1 დუიმი გვერდზე) Set as Default ნაგულისხმევად დაყენება out from a highlighted edge to place its dimension | — გამოკვეთილი კიდიდან გარეთ, მისი ზომის დასასმელად | + two points, then move and click to place an aligned, horizontal or vertical dimension | — ორ წერტილზე, შემდეგ გადაადგილეთ და დააწკაპუნეთ გასწორებული, ჰორიზონტალური ან ვერტიკალური ზომის დასასმელად | cancels | Double-click the tool for options — გაუქმება | პარამეტრებისთვის ორჯერ დაწკაპეთ ხელსაწყოზე Angular კუთხური Away from the Line ხაზისგან მოშორებით @@ -5318,12 +5319,14 @@ imageTrace Outline კონტური @msg Measured anchor no longer exists გაზომილი საყრდენი წერტილი აღარ არსებობს @msg Measured segment no longer exists გაზომილი სეგმენტი აღარ არსებობს @msg Non-finite dimension ზომა სასრული რიცხვი არ არის +@msg Only linear dimensions are horizontal or vertical მხოლოდ ხაზოვანი ზომებია ჰორიზონტალური ან ვერტიკალური @msg Scale Text Attributes: selected type has no scale to bake ტექსტის ატრიბუტების მასშტაბირება: არჩეულ ტექსტს არ აქვს გადასაყვანი მასშტაბი @msg Scale must be drawing:real, for example 1:10 მასშტაბი უნდა იყოს ნახაზი:რეალური, მაგალითად 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 მასშტაბის შეფარდება 1:1000000-სა და 1000000:1-ს შორის უნდა იყოს @msg Second measured segment no longer exists მეორე გაზომილი სეგმენტი აღარ არსებობს @msg Unknown dimension mode ზომის უცნობი რეჟიმი @msg Unknown dimension option {k} ზომის უცნობი პარამეტრი {k} +@msg Unknown dimension orientation ზომის უცნობი ორიენტაცია @msg Unknown dimension style ზომის უცნობი სტილი @msg Unknown unit უცნობი ერთეული Close the circular ends of open profiles. Partial sweeps keep their cut sides open. ღია პროფილების წრიული ბოლოების დახურვა. ნაწილობრივი ბრუნვისას ჭრის მხარეები ღია რჩება. diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 03930932a..f43c225f4 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5251,6 +5251,7 @@ lighting Light 광원 Preview (1 inch on the page) 미리 보기 (페이지의 1인치) Set as Default 기본값으로 설정 out from a highlighted edge to place its dimension | 강조된 가장자리에서 바깥쪽으로 끌어 치수 배치 | + two points, then move and click to place an aligned, horizontal or vertical dimension | 으로 두 점을 지정한 다음 이동하고 클릭하여 정렬, 가로 또는 세로 치수 배치 | cancels | Double-click the tool for options 취소 | 도구를 두 번 클릭하면 옵션 Linear 선형 Angular 각도 @@ -5285,12 +5286,14 @@ lighting Light 광원 @msg Measured anchor no longer exists 측정한 고정점이 더 이상 없습니다 @msg Measured segment no longer exists 측정한 세그먼트가 더 이상 없습니다 @msg Non-finite dimension 유한하지 않은 치수 +@msg Only linear dimensions are horizontal or vertical 선형 치수만 가로 또는 세로로 지정할 수 있습니다 @msg Scale Text Attributes: selected type has no scale to bake 텍스트 속성 크기 조절: 선택한 텍스트에 적용할 크기 조절이 없습니다 @msg Scale must be drawing:real, for example 1:10 축척은 도면:실제 형식이어야 합니다(예: 1:10) @msg Scale ratio must be between 1:1000000 and 1000000:1 축척 비율은 1:1000000에서 1000000:1 사이여야 합니다 @msg Second measured segment no longer exists 두 번째 측정 세그먼트가 더 이상 없습니다 @msg Unknown dimension mode 알 수 없는 치수 모드 @msg Unknown dimension option {k} 알 수 없는 치수 옵션 {k} +@msg Unknown dimension orientation 알 수 없는 치수 방향 @msg Unknown dimension style 알 수 없는 치수 스타일 @msg Unknown unit 알 수 없는 단위 # Revolve cap description diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index c5151622d..9dc6de451 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5284,6 +5284,7 @@ mark-style Japanese Japans Preview (1 inch on the page) Voorbeeld (1 inch op de pagina) Set as Default Als standaard instellen out from a highlighted edge to place its dimension | uit een gemarkeerde rand om de bijbehorende maatlijn te plaatsen | + two points, then move and click to place an aligned, horizontal or vertical dimension | op twee punten, verplaats daarna en klik om een uitgelijnde, horizontale of verticale maatlijn te plaatsen | cancels | Double-click the tool for options annuleert | Dubbelklik op het gereedschap voor opties Angular Hoek Away from the Line Van de lijn af @@ -5315,12 +5316,14 @@ mark-style Japanese Japans @msg Measured anchor no longer exists Gemeten ankerpunt bestaat niet meer @msg Measured segment no longer exists Gemeten segment bestaat niet meer @msg Non-finite dimension Niet-eindige maatlijn +@msg Only linear dimensions are horizontal or vertical Alleen lineaire maatlijnen zijn horizontaal of verticaal @msg Scale Text Attributes: selected type has no scale to bake Tekstkenmerken schalen: de geselecteerde tekst heeft geen schaling om toe te passen @msg Scale must be drawing:real, for example 1:10 Schaal moet tekening:werkelijk zijn, bijvoorbeeld 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Schaalverhouding moet tussen 1:1000000 en 1000000:1 liggen @msg Second measured segment no longer exists Tweede gemeten segment bestaat niet meer @msg Unknown dimension mode Onbekende maatlijnmodus @msg Unknown dimension option {k} Onbekende maatlijnoptie {k} +@msg Unknown dimension orientation Onbekende maatlijnrichting @msg Unknown dimension style Onbekende maatlijnstijl @msg Unknown unit Onbekende eenheid Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Sluit de ronde uiteinden van open profielen. Gedeeltelijke omwentelingen houden hun afgesneden zijden open. diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 58016a4bd..7f7b5c7e2 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5533,6 +5533,7 @@ mark-style Japanese Japońskie Preview (1 inch on the page) Podgląd (1 cal na stronie) Set as Default Ustaw jako domyślne out from a highlighted edge to place its dimension | od podświetlonej krawędzi na zewnątrz, aby umieścić jej wymiar | + two points, then move and click to place an aligned, horizontal or vertical dimension | dwa punkty, potem przesuń i kliknij, aby umieścić wymiar wyrównany, poziomy lub pionowy | cancels | Double-click the tool for options anuluje | Kliknij dwukrotnie narzędzie, aby wyświetlić opcje Angular Kątowy Away from the Line Z dala od linii @@ -5564,12 +5565,14 @@ mark-style Japanese Japońskie @msg Measured anchor no longer exists Mierzony punkt kontrolny już nie istnieje @msg Measured segment no longer exists Mierzony odcinek już nie istnieje @msg Non-finite dimension Wymiar nie jest liczbą skończoną +@msg Only linear dimensions are horizontal or vertical Tylko wymiary liniowe są poziome lub pionowe @msg Scale Text Attributes: selected type has no scale to bake Skaluj atrybuty tekstu: wybrany tekst nie ma skali do zastosowania @msg Scale must be drawing:real, for example 1:10 Skala musi mieć postać rysunek:rzeczywistość, na przykład 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Współczynnik skali musi mieścić się między 1:1000000 a 1000000:1 @msg Second measured segment no longer exists Drugi mierzony odcinek już nie istnieje @msg Unknown dimension mode Nieznany tryb wymiaru @msg Unknown dimension option {k} Nieznana opcja wymiaru {k} +@msg Unknown dimension orientation Nieznana orientacja wymiaru @msg Unknown dimension style Nieznany styl wymiaru @msg Unknown unit Nieznana jednostka # Revolve cap description diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index b0d940693..6ba8425d5 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5644,12 +5644,14 @@ mark-style Japanese Japonesas @msg Measured anchor no longer exists O ponto de ancoragem medido não existe mais @msg Measured segment no longer exists O segmento medido não existe mais @msg Non-finite dimension Cota não finita +@msg Only linear dimensions are horizontal or vertical Somente cotas lineares são horizontais ou verticais @msg Scale Text Attributes: selected type has no scale to bake Dimensionar Atributos de Texto: o texto selecionado não tem escala para aplicar @msg Scale must be drawing:real, for example 1:10 A escala deve ser desenho:real, por exemplo 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 A proporção da escala deve estar entre 1:1000000 e 1000000:1 @msg Second measured segment no longer exists O segundo segmento medido não existe mais @msg Unknown dimension mode Modo de cota desconhecido @msg Unknown dimension option {k} Opção de cota desconhecida {k} +@msg Unknown dimension orientation Orientação de cota desconhecida @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 @@ -5669,6 +5671,7 @@ mark-style Japanese Japonesas This system has no screen color sampler Este sistema não tem coletor de cores da tela cancels | Double-click the tool for options cancela | Clique duas vezes na ferramenta para ver as opções out from a highlighted edge to place its dimension | para fora a partir de uma borda destacada para posicionar a cota | + two points, then move and click to place an aligned, horizontal or vertical dimension | em dois pontos, depois mova e clique para posicionar uma cota alinhada, horizontal ou vertical | Color Panel Options Opções do Painel Cor @msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` deve ser grayscale, rgb, hsb, cmyk ou websafe @msg the image can't be read as a texture a imagem não pode ser lida como textura diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index a00325e8b..205691ecd 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5284,6 +5284,7 @@ lighting Light Свет Preview (1 inch on the page) Просмотр (1 дюйм на странице) Set as Default Установить по умолчанию out from a highlighted edge to place its dimension | наружу от подсвеченного края, чтобы разместить его размер | + two points, then move and click to place an aligned, horizontal or vertical dimension | по двум точкам, затем переместите и щёлкните, чтобы разместить выровненный, горизонтальный или вертикальный размер | cancels | Double-click the tool for options отменяет | Двойной щелчок по инструменту открывает параметры Linear Линейный Angular Угловой @@ -5318,12 +5319,14 @@ lighting Light Свет @msg Measured anchor no longer exists Измеряемая опорная точка больше не существует @msg Measured segment no longer exists Измеряемый сегмент больше не существует @msg Non-finite dimension Размер не является конечным числом +@msg Only linear dimensions are horizontal or vertical Только линейные размеры бывают горизонтальными или вертикальными @msg Scale Text Attributes: selected type has no scale to bake Масштабировать атрибуты текста: у выбранного текста нет масштаба для применения @msg Scale must be drawing:real, for example 1:10 Масштаб должен быть в виде чертёж:реальность, например 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Соотношение масштаба должно быть от 1:1000000 до 1000000:1 @msg Second measured segment no longer exists Второй измеряемый сегмент больше не существует @msg Unknown dimension mode Неизвестный режим размера @msg Unknown dimension option {k} Неизвестный параметр размера {k} +@msg Unknown dimension orientation Неизвестная ориентация размера @msg Unknown dimension style Неизвестный стиль размера @msg Unknown unit Неизвестная единица # Revolve cap description diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index f2b24e7d0..7f01d73bd 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5573,6 +5573,7 @@ theme Appearance Utseende 3d Light Ljus cancels | Double-click the tool for options avbryter | Dubbelklicka på verktyget för alternativ out from a highlighted edge to place its dimension | utåt från en framhävd kant för att placera dess mått | + two points, then move and click to place an aligned, horizontal or vertical dimension | på två punkter, flytta sedan och klicka för att placera ett justerat, vågrätt eller lodrätt mått | A legacy .ai file holds one artboard: each artboard you check is saved as a separate file. En äldre .ai-fil rymmer en rityta: varje rityta du markerar sparas som en separat fil. Arrange Intertwine Area Ordna sammanflätningsområde Batch… Batch… @@ -5671,12 +5672,14 @@ theme Appearance Utseende @msg Measured anchor no longer exists Den mätta ankarpunkten finns inte längre @msg Measured segment no longer exists Det mätta segmentet finns inte längre @msg Non-finite dimension Måttet är inte ett ändligt tal +@msg Only linear dimensions are horizontal or vertical Endast linjära mått är vågräta eller lodräta @msg Scale Text Attributes: selected type has no scale to bake Skala textattribut: det valda texten har ingen skalning att tillämpa @msg Scale must be drawing:real, for example 1:10 Skalan måste anges som ritning:verklighet, till exempel 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Skalförhållandet måste ligga mellan 1:1000000 och 1000000:1 @msg Second measured segment no longer exists Det andra mätta segmentet finns inte längre @msg Unknown dimension mode Okänt måttläge @msg Unknown dimension option {k} Okänt måttalternativ {k} +@msg Unknown dimension orientation Okänd måttriktning @msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` måste vara grayscale, rgb, hsb, cmyk eller websafe @msg `{key}` must be an array of these names: {_1} `{key}` måste vara en lista med dessa namn: {_1} @msg choose a destination folder välj en målmapp diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 76d73994b..8d2c0aae3 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5361,6 +5361,7 @@ theme Appearance Görünüm Preview (1 inch on the page) Önizleme (sayfada 1 inç) Set as Default Varsayılan Olarak Ayarla out from a highlighted edge to place its dimension | boyutunu yerleştirmek için vurgulanan kenardan dışarı | + two points, then move and click to place an aligned, horizontal or vertical dimension | ile iki nokta seçin, ardından hizalı, yatay veya dikey bir boyut yerleştirmek için hareket ettirip tıklayın | cancels | Double-click the tool for options iptal | Seçenekler için araca çift tıklayın Angular Açısal Away from the Line Çizgiden Uzakta @@ -5392,11 +5393,13 @@ theme Appearance Görünüm @msg Measured anchor no longer exists Ölçülen çapa artık mevcut değil @msg Measured segment no longer exists Ölçülen segment artık mevcut değil @msg Non-finite dimension Sonlu olmayan boyut +@msg Only linear dimensions are horizontal or vertical Yalnızca doğrusal boyutlar yatay veya dikey olabilir @msg Scale must be drawing:real, for example 1:10 Ölçek çizim olmalıdır:gerçek, örneğin 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Ölçek oranı 1:1000000 ile 1000000:1 arasında olmalıdır @msg Second measured segment no longer exists Ölçülen ikinci segment artık mevcut değil @msg Unknown dimension mode Bilinmeyen boyut modu @msg Unknown dimension option {k} Bilinmeyen boyut seçeneği {k} +@msg Unknown dimension orientation Bilinmeyen boyut yönü @msg Unknown dimension style Bilinmeyen boyut stili @msg Unknown unit Bilinmeyen birim Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Açık profillerin dairesel uçlarını kapatın. Kısmi süpürmeler kesik taraflarını açık tutar. diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 4652ce4ca..9cb1442c7 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5270,6 +5270,7 @@ lighting Light Світло Preview (1 inch on the page) Попередній перегляд (1 дюйм на сторінці) Set as Default Встановити за замовчуванням out from a highlighted edge to place its dimension | назовні від підсвіченого краю, щоб розмістити його розмір | + two points, then move and click to place an aligned, horizontal or vertical dimension | по двох точках, потім перемістіть і клацніть, щоб розмістити вирівняний, горизонтальний або вертикальний розмір | cancels | Double-click the tool for options скасовує | Подвійне клацання по інструменту відкриває параметри Linear Лінійний Angular Кутовий @@ -5304,12 +5305,14 @@ lighting Light Світло @msg Measured anchor no longer exists Виміряна опорна точка більше не існує @msg Measured segment no longer exists Виміряний сегмент більше не існує @msg Non-finite dimension Розмір не є скінченним числом +@msg Only linear dimensions are horizontal or vertical Лише лінійні розміри бувають горизонтальними або вертикальними @msg Scale Text Attributes: selected type has no scale to bake Масштабувати атрибути тексту: у вибраного тексту немає масштабу для застосування @msg Scale must be drawing:real, for example 1:10 Масштаб має бути у вигляді креслення:реальність, наприклад 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Співвідношення масштабу має бути від 1:1000000 до 1000000:1 @msg Second measured segment no longer exists Другий виміряний сегмент більше не існує @msg Unknown dimension mode Невідомий режим розміру @msg Unknown dimension option {k} Невідомий параметр розміру {k} +@msg Unknown dimension orientation Невідома орієнтація розміру @msg Unknown dimension style Невідомий стиль розміру @msg Unknown unit Невідома одиниця # Revolve cap description diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 4c9dac833..23a80bb43 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5273,6 +5273,7 @@ theme Appearance Giao diện Preview (1 inch on the page) Xem trước (1 inch trên trang) Set as Default Đặt làm mặc định out from a highlighted edge to place its dimension | ra ngoài từ một cạnh được tô sáng để đặt kích thước của nó | + two points, then move and click to place an aligned, horizontal or vertical dimension | hai điểm, rồi di chuyển và bấm để đặt kích thước căn theo hai điểm, nằm ngang hoặc thẳng đứng | cancels | Double-click the tool for options để hủy | Nhấp đúp vào công cụ để mở tùy chọn Linear Tuyến tính Angular Góc @@ -5305,12 +5306,14 @@ theme Appearance Giao diện @msg Measured anchor no longer exists Điểm neo được đo không còn tồn tại @msg Measured segment no longer exists Đoạn được đo không còn tồn tại @msg Non-finite dimension Kích thước không phải là số hữu hạn +@msg Only linear dimensions are horizontal or vertical Chỉ kích thước tuyến tính mới nằm ngang hoặc thẳng đứng @msg Scale Text Attributes: selected type has no scale to bake Tỷ lệ thuộc tính văn bản: văn bản được chọn không có tỷ lệ để áp dụng @msg Scale must be drawing:real, for example 1:10 Tỷ lệ phải có dạng bản vẽ:thực tế, ví dụ 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 Tỷ lệ phải nằm trong khoảng 1:1000000 đến 1000000:1 @msg Second measured segment no longer exists Đoạn được đo thứ hai không còn tồn tại @msg Unknown dimension mode Chế độ kích thước không xác định @msg Unknown dimension option {k} Tùy chọn kích thước không xác định {k} +@msg Unknown dimension orientation Hướng kích thước không xác định @msg Unknown dimension style Kiểu kích thước không xác định @msg Unknown unit Đơn vị không xác định # Revolve cap description diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index c62b17668..6eb26772f 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -4756,12 +4756,14 @@ imageTrace Outline 轮廓 @msg Measured anchor no longer exists 测量锚点已不存在 @msg Measured segment no longer exists 测量线段已不存在 @msg Non-finite dimension 尺寸标注不是有限值 +@msg Only linear dimensions are horizontal or vertical 只有线性尺寸可以是水平或垂直的 @msg Scale Text Attributes: selected type has no scale to bake 缩放文本属性:所选文本没有可应用的缩放 @msg Scale must be drawing:real, for example 1:10 比例必须为 图纸:实际,例如 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 比例必须介于 1:1000000 和 1000000:1 之间 @msg Second measured segment no longer exists 第二条测量线段已不存在 @msg Unknown dimension mode 未知的尺寸标注模式 @msg Unknown dimension option {k} 未知的尺寸标注选项 {k} +@msg Unknown dimension orientation 未知的尺寸标注方向 @msg span must be "object" or "selection", got {v} span 必须是 "object" 或 "selection",实际为 {v} @msg span: "selection" places a `gradient`, not a swatch span: "selection" 放置的是 `gradient`,而不是色板 @msg span: "selection" places a gradient: give `gradient` span: "selection" 放置渐变:请给出 `gradient` @@ -5558,6 +5560,7 @@ lighting Light 光源 Preview (1 inch on the page) 预览(页面上 1 英寸) Set as Default 设为默认 out from a highlighted edge to place its dimension | 从高亮的边向外拖动以放置其尺寸 | + two points, then move and click to place an aligned, horizontal or vertical dimension | 两个点,然后移动并单击以放置对齐、水平或垂直尺寸 | cancels | Double-click the tool for options 取消 | 双击工具打开选项 Linear 线性 Angular 角度 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 0b3a21250..2f4fe3bc0 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2591,12 +2591,14 @@ file-filter Templates 範本 @msg Measured anchor no longer exists 測量的錨點已不存在 @msg Measured segment no longer exists 測量的線段已不存在 @msg Non-finite dimension 尺寸標註不是有限值 +@msg Only linear dimensions are horizontal or vertical 只有線性尺寸可以是水平或垂直的 @msg Scale Text Attributes: selected type has no scale to bake 縮放文字屬性:所選文字沒有可套用的縮放 @msg Scale must be drawing:real, for example 1:10 比例必須為 圖紙:實際,例如 1:10 @msg Scale ratio must be between 1:1000000 and 1000000:1 比例必須介於 1:1000000 和 1000000:1 之間 @msg Second measured segment no longer exists 第二條測量線段已不存在 @msg Unknown dimension mode 未知的尺寸標註模式 @msg Unknown dimension option {k} 未知的尺寸標註選項 {k} +@msg Unknown dimension orientation 未知的尺寸標註方向 @msg span must be "object" or "selection", got {v} span 必須是 "object" 或 "selection",收到 {v} @msg span: "selection" places a `gradient`, not a swatch span: "selection" 放置的是 `gradient`,而非色票 @msg span: "selection" places a gradient: give `gradient` span: "selection" 會放置漸層:請提供 `gradient` @@ -5381,6 +5383,7 @@ lighting Light 光源 Preview (1 inch on the page) 預覽(頁面上 1 英寸) Set as Default 設為預設 out from a highlighted edge to place its dimension | 從醒目提示的邊緣向外拖曳以放置其尺寸 | + two points, then move and click to place an aligned, horizontal or vertical dimension | 兩個點,然後移動並按一下以放置對齊、水平或垂直尺寸 | cancels | Double-click the tool for options 取消 | 按兩下工具開啟選項 Linear 線性 Angular 角度 diff --git a/docs/dimensions.md b/docs/dimensions.md index 1f1db9a49..6327c9f50 100644 --- a/docs/dimensions.md +++ b/docs/dimensions.md @@ -11,6 +11,26 @@ The **Dimension Tool** is in the Modify section of the toolbar. Its strip offers curved segment is measured from end to end. With Smart Guides on, both points also snap into line with other objects' anchors and centres, and the second onto the construction guides through the first; the guides show while hovering, before each click. + A linear dimension is aligned, horizontal or vertical, as the + [linear dimensions help](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/plot-linear-dimensions-of-objects.html) + describes: after the second click, clicking again places an aligned dimension, and moving the + pointer away gives a horizontal or vertical one. Which one a pointer position gives is + VectorCraft's own rule. When the pointer is beside the two points (the nearest point of the + line through them lies between them) or on that line, the dimension is aligned. When the + pointer is past either point, the line through the points and the perpendicular through that + point divide the area beyond it into two quarters, one on each side of the line. The quarter + that contains the direction straight up or down from the point gives the horizontal distance, + and the quarter that contains the direction straight left or right gives the vertical distance. + A click within 8 pixels of the second point, or of where the pointer was for the second click, + places an aligned dimension. Two points on one horizontal or vertical line always give an + aligned dimension. A dragged segment uses the same rule with its two ends. + A horizontal or vertical dimension keeps its orientation when it or the art it measures turns, + and shows the new horizontal or vertical distance, as the help describes for rotated objects. It + shows 0 when its points lie on one line across its axis. When the line of a linked dimension + lies outside its points, the line stays on the same side of the art, as far past the outermost + point as before. An unlinked dimension that + turns has its line where the turn puts it, or, when that would put it between the points, as + far past the outermost point as before. - **Angular:** hover a corner where two straight segments meet and drag outwards. Or click two straight lines that meet (or would, extended), then click to place. - **Radial:** hover a circle, or a segment that is a perfect arc, and drag: outwards gives its @@ -55,8 +75,13 @@ document. ## Commands - `dimension.create`: two points, or a source (segment, angle, circle, arc, points), the mode, - offset and style; returns the group id and label. -- `dimension.update`: restyle or move existing dimensions. + the orientation of a linear dimension (`aligned`, the default, `horizontal` or `vertical`), + offset and style; returns the group id and label. `offset` is the distance of the dimension + line from the first point: along the left normal of a → b for an aligned dimension, downward + for a horizontal one and leftward for a vertical one. A negative offset goes the other way. +- `dimension.update`: restyle or move existing dimensions, or change the orientation of linear + ones. A new orientation keeps the line on the side of the points it was on. An update that + changes nothing records no undo step. - `dimension.applyToAll`: every dimension takes one dimension's style, in one undo step. - `tool.select {"tool":"dimension"}` and `tool.setOption {"tool":"dimension","values":{"mode":"linear","style":{"units":"mm","scale":"1:10"}}}` diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6bf45e9c8..3171a2b08 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -564,6 +564,7 @@ since 2026-10-01. **Drawing tools — Polygon and Star counts:** The existing canvas placement dialog reads the stored count and saves it only after successful placement. `polygon_and_star_canvas_counts_persist_without_new_toolbar_dialogs` covers both tools, repeated drags, tool switching, reopening and Cancel. Counts remain session tool options. Existing click and toolbar gestures are retained; no score or hour change. **Drawing tools, Star inner radius held with Ctrl/Cmd:** While a star is dragged out, Ctrl/Cmd keeps the inner radius the star showed when the key went down, also when the key is pressed with the pointer still. `a_star_drag_holds_its_inner_radius_with_ctrl_and_straightens_with_alt` and `a_star_drag_holds_the_inner_radius_it_shows_when_ctrl_goes_down` cover it, with Alt's straight shoulders and a drag canceled with Escape. +**Drawing tools, Dimension tool horizontal and vertical linear dimensions:** A linear dimension between two clicked points or dragged out from a segment is aligned, horizontal or vertical, chosen by where the pointer moves while it is placed, and a click again by the second point gives aligned; `dimension.create` and `dimension.update` take `orientation`, and the dimension stays linked to the anchors it measures, keeps its orientation when it or the art turns, with a line that lay outside the points kept outside them; an unlinked horizontal or vertical dimension that is reflected has its line on the reflected side. `the_pointer_chooses_aligned_horizontal_or_vertical`, `two_points_measure_horizontally_vertically_or_aligned`, `clicking_again_by_the_second_point_places_an_aligned_dimension`, `a_segment_drag_measures_horizontally_vertically_or_aligned`, `points_on_a_vertical_line_measure_aligned`, `horizontal_and_vertical_dimensions_draw_lines_along_their_axis`, `turned_horizontal_dimensions_stay_horizontal`, `a_linked_horizontal_dimension_keeps_its_gap_from_turned_art`, `a_linked_horizontal_dimension_below_or_between_its_points_follows_turned_art`, `a_reflected_horizontal_dimension_keeps_its_line_on_the_reflected_side`, `a_dimension_without_an_orientation_reads_as_aligned`, `dimension_orientation_is_checked` and `a_turned_dimension_keeps_its_line_on_its_side` cover it. **Live trace result bounds and selection highlights exclude the retained bitmap except in source-image views:** `live_trace_result_bounds_exclude_the_retained_bitmap_at_any_angle` checks every TraceView, geometric and visual bounds and rotated result bounds. `live_trace_selection_highlight_excludes_the_retained_source_rectangle` checks result highlights and the existing ordinary-group policy. Shared editing-child traversal also preserves Shaper handling. **Artboard Options shares existing presets and orientation controls and can fit artwork without moving it; artwork fitting includes all drawn document art across artboards and off-board; artwork bounds are cached by active document and revision, and selection uses the shared bounds cache across artboards:** Shared existing preset definitions are reused; no external preset assets are added. Presets preserve each selected artboard's orientation, and orientation choices apply even when the active board already matches. Fit operations draft bounds until OK, affect only the active artboard and leave artwork in place. Regression coverage checks draft/Cancel, selected-set application, active-only fitting and Undo.