diff --git a/crates/engine/src/cmd/create.rs b/crates/engine/src/cmd/create.rs index 617f3a529..a9a86195d 100644 --- a/crates/engine/src/cmd/create.rs +++ b/crates/engine/src/cmd/create.rs @@ -32,7 +32,15 @@ pub fn specs() -> Vec { ), cmd!("shape.line", "Line Segment", [], None, "{x1, y1, x2, y2} → {id}", has_doc, line), cmd!("shape.spiral", "Spiral", [], None, "{cx, cy, radius, decay=80 (%), segments=10, clockwise?} → {id}", has_doc, spiral), - cmd!("shape.arc", "Arc", [], None, "{x1, y1, x2, y2, closed?} → {id}", has_doc, arc), + cmd!( + "shape.arc", + "Arc", + [], + None, + "{x1, y1, x2, y2, closed?, slope?} or {x, y, lengthX, lengthY, reference?, baseAlong?: x|y, slope?, closed?, fillArc?} → {id}", + has_doc, + arc + ), cmd!( "shape.rectangularGrid", "Rectangular Grid", @@ -393,9 +401,34 @@ fn spiral(s: &mut Session, p: &Value) -> Result { } fn arc(s: &mut Session, p: &Value) -> Result { - let a = Point::new(f64_req(p, "x1", "shape.arc")?, f64_req(p, "y1", "shape.arc")?); - let b = Point::new(f64_req(p, "x2", "shape.arc")?, f64_req(p, "y2", "shape.arc")?); - add_art(s, "Arc", path_kind(shapes::arc(a, b, 0.0, switch_or(p, "closed", false, "shape.arc")?), None), None) + let closed = switch_or(p, "closed", false, "shape.arc")?; + let fill_arc = switch_or(p, "fillArc", false, "shape.arc")?; + let base_x = !matches!(p.get("baseAlong").and_then(Value::as_str), Some("y" | "Y")); + let path = if p.get("lengthX").is_some() || p.get("lengthY").is_some() { + let anchor = Point::new(f64_req(p, "x", "shape.arc")?, f64_req(p, "y", "shape.arc")?); + let reference = p.get("reference").and_then(Value::as_u64).unwrap_or(0) as usize; + shapes::arc_segment( + anchor, + reference, + number_or(p, "lengthX", 100.0, "shape.arc")?, + number_or(p, "lengthY", 100.0, "shape.arc")?, + base_x, + number_or(p, "slope", 50.0, "shape.arc")?, + closed, + ) + } else { + let a = Point::new(f64_req(p, "x1", "shape.arc")?, f64_req(p, "y1", "shape.arc")?); + let b = Point::new(f64_req(p, "x2", "shape.arc")?, f64_req(p, "y2", "shape.arc")?); + let slope = number_or(p, "slope", 100.0, "shape.arc")?; + shapes::arc(a, b, slope, closed, base_x) + }; + let fill = if fill_arc { s.paint.fill.clone() } else { vectorcraft_color::Paint::None }; + let look = s.new_art_look( + fill, + if s.paint.stroke.is_none() { vectorcraft_color::Paint::solid(vectorcraft_color::Color::BLACK) } else { s.paint.stroke.clone() }, + s.paint.stroke_width.max(0.1), + ); + add_look(s, "Arc", path_kind(path, None), look, None) } /// The grid `paths` (bottom first) as one selected group in the new-art stroke, filled with the diff --git a/crates/geom/src/shapes.rs b/crates/geom/src/shapes.rs index e677d34ff..4d9f92281 100644 --- a/crates/geom/src/shapes.rs +++ b/crates/geom/src/shapes.rs @@ -176,17 +176,33 @@ pub fn line(a: Point, b: Point) -> PathData { PathData::single(SubPath::polyline(&[a, b], false)) } -/// Quarter-ellipse arc from `a` to `b` (Arc tool default: concave, open, x-axis base). -pub fn arc(a: Point, b: Point, slope: f64, closed: bool) -> PathData { - // Corner of the arc's bounding box is at (a.x, b.y): the arc bows away from it. - let k = KAPPA * (1.0 + slope.clamp(-1.0, 1.0) * 0.0); - let corner = Point::new(b.x, a.y); - let h1 = a + (corner - a) * k; - let h2 = b + (corner - b) * k; +/// Bounding box of an arc segment when reference point `reference` (0…8) is placed on `anchor`. +pub fn arc_bbox(anchor: Point, reference: usize, length_x: f64, length_y: f64) -> Rect { + let w = length_x.abs().max(1e-9); + let h = length_y.abs().max(1e-9); + let rp = crate::reference_point(Rect::new(0.0, 0.0, w, h), reference.min(8)); + Rect::new(anchor.x - rp.x, anchor.y - rp.y, anchor.x - rp.x + w, anchor.y - rp.y + h) +} + +/// Arc segment from the Arc tool dialog: lengths, reference point, base axis, slope and open/closed. +pub fn arc_segment(anchor: Point, reference: usize, length_x: f64, length_y: f64, base_along_x: bool, slope: f64, closed: bool) -> PathData { + let r = arc_bbox(anchor, reference, length_x, length_y).abs(); + let (a, b) = if base_along_x { (Point::new(r.x0, r.y0), Point::new(r.x1, r.y1)) } else { (Point::new(r.x0, r.y1), Point::new(r.x1, r.y0)) }; + arc(a, b, slope, closed, base_along_x) +} + +/// Quarter-ellipse arc from `a` to `b` (Arc tool default: open, x-axis base when `base_along_x`). +/// `slope` is −100…100 (concave…convex); 0 is a straight chord. +pub fn arc(a: Point, b: Point, slope: f64, closed: bool, base_along_x: bool) -> PathData { + let t = slope.clamp(-100.0, 100.0) / 100.0; + let corner = if t >= 0.0 { Point::new(b.x, a.y) } else { Point::new(a.x, b.y) }; + let k = KAPPA * t.abs(); + let h1 = if k == 0.0 { a } else { a + (corner - a) * k }; + let h2 = if k == 0.0 { b } else { b + (corner - b) * k }; let mut anchors = vec![Anchor { p: a, h_in: a, h_out: h1, kind: AnchorKind::Corner }, Anchor { p: b, h_in: h2, h_out: b, kind: AnchorKind::Corner }]; if closed { - let o = Point::new(a.x, b.y); + let o = if base_along_x { Point::new(a.x, b.y) } else { Point::new(b.x, a.y) }; anchors.push(Anchor::corner(o)); } PathData::single(SubPath::new(anchors, closed)) @@ -531,7 +547,9 @@ mod tests { #[test] fn arc_open_and_closed() { - assert_eq!(arc(Point::ZERO, Point::new(10.0, 10.0), 0.0, false).anchor_count(), 2); - assert!(arc(Point::ZERO, Point::new(10.0, 10.0), 0.0, true).subpaths[0].closed); + assert_eq!(arc(Point::ZERO, Point::new(10.0, 10.0), 0.0, false, true).anchor_count(), 2); + assert!(arc(Point::ZERO, Point::new(10.0, 10.0), 0.0, true, true).subpaths[0].closed); + let seg = arc_segment(Point::ZERO, 0, 100.0, 50.0, true, 50.0, false); + assert_eq!(seg.anchor_count(), 2); } } diff --git a/crates/tools/src/draw2/family.rs b/crates/tools/src/draw2/family.rs index 9b068bc05..ff2cc8853 100644 --- a/crates/tools/src/draw2/family.rs +++ b/crates/tools/src/draw2/family.rs @@ -9,7 +9,7 @@ use serde_json::{Value, json}; use vectorcraft_geom::Point; -use crate::guides::{DrawSnap, Leave, square}; +use crate::guides::{DrawSnap, Leave}; use crate::shape::drag_rect; use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, ToolContext, ToolKey}; @@ -20,6 +20,12 @@ pub struct FamilyTool { mods: Mods, began: bool, pub closed: bool, + length_x: f64, + length_y: f64, + reference: usize, + base_along: &'static str, + slope: f64, + fill_arc: bool, pub decay: f64, pub segments: u32, pub clockwise: bool, @@ -53,6 +59,12 @@ impl FamilyTool { mods: Mods::default(), began: false, closed: false, + length_x: 100.0, + length_y: 100.0, + reference: 0, + base_along: "x", + slope: 50.0, + fill_arc: false, decay: 80.0, segments: 10, clockwise: true, @@ -101,9 +113,24 @@ impl FamilyTool { (format!("shape.{}", self.id), params) } _ => { - let d = if m.shift { square(p - start) } else { p - start }; - let (a, b) = if m.alt { (start - d, start + d) } else { (start, start + d) }; - ("shape.arc".into(), json!({"x1": a.x, "y1": a.y, "x2": b.x, "y2": b.y, "closed": self.closed})) + let r = drag_rect(start, p, m).abs(); + let (w, h) = (r.width().max(1e-9), r.height().max(1e-9)); + let rp = vectorcraft_geom::reference_point(vectorcraft_geom::Rect::new(0.0, 0.0, w, h), self.reference.min(8)); + let anchor = Point::new(r.x0 + rp.x, r.y0 + rp.y); + ( + "shape.arc".into(), + json!({ + "x": anchor.x, + "y": anchor.y, + "lengthX": w, + "lengthY": h, + "reference": self.reference, + "baseAlong": self.base_along, + "slope": self.slope, + "closed": self.closed, + "fillArc": self.fill_arc, + }), + ) } } } @@ -219,12 +246,30 @@ impl Tool for FamilyTool { "polarGrid" => json!({ "concentric": self.rows, "radial": self.columns, "concentricSkew": self.skew[0], "radialSkew": self.skew[1], "compound": self.compound, "fill": self.fill, }), - _ => json!({"closed": self.closed}), + _ => json!({ + "closed": self.closed, + "lengthX": self.length_x, + "lengthY": self.length_y, + "reference": self.reference, + "baseAlong": self.base_along, + "slope": self.slope, + "fillArc": self.fill_arc, + }), } } fn set_option(&mut self, key: &str, v: &Value) { match key { "closed" => self.closed = v.as_bool().unwrap_or(self.closed), + "lengthX" => self.length_x = arc_length(v, self.length_x), + "lengthY" => self.length_y = arc_length(v, self.length_y), + "reference" => self.reference = v.as_u64().unwrap_or(self.reference as u64).min(8) as usize, + "baseAlong" => { + if let Some(s) = v.as_str() { + self.base_along = if s.eq_ignore_ascii_case("y") { "y" } else { "x" }; + } + } + "slope" => self.slope = v.as_f64().filter(|n| n.is_finite()).map_or(self.slope, |n| n.clamp(-100.0, 100.0)), + "fillArc" => self.fill_arc = v.as_bool().unwrap_or(self.fill_arc), "decay" => self.decay = v.as_f64().unwrap_or(self.decay).clamp(5.0, 150.0), "segments" => self.segments = v.as_u64().unwrap_or(10).clamp(2, 1000) as u32, "clockwise" => self.clockwise = v.as_bool().unwrap_or(self.clockwise), @@ -240,6 +285,11 @@ impl Tool for FamilyTool { } } +/// Arc segment length from a dialog or preference value. +fn arc_length(v: &Value, current: f64) -> f64 { + v.as_f64().filter(|n| n.is_finite()).map_or(current, |n| n.abs().max(1e-9)) +} + /// A grid divider skew from option value `v`: a finite number within the range, else `current`. fn grid_skew(v: &Value, current: f64) -> f64 { let max = vectorcraft_geom::shapes::GRID_SKEW_MAX; @@ -327,7 +377,52 @@ mod tests { let t = FamilyTool::new("arc"); let (c, v) = t.command(Point::new(0.0, 0.0), Point::new(10.0, 4.0), Mods { shift: true, ..Default::default() }); assert_eq!(c, "shape.arc"); - assert_eq!(v, json!({"x1": 0.0, "y1": 0.0, "x2": 10.0, "y2": 10.0, "closed": false})); + assert_eq!( + v, + json!({ + "x": 0.0, + "y": 0.0, + "lengthX": 10.0, + "lengthY": 10.0, + "reference": 0, + "baseAlong": "x", + "slope": 50.0, + "closed": false, + "fillArc": false, + }) + ); + } + + #[test] + fn arc_drag_uses_stored_segment_options() { + let mut t = FamilyTool::new("arc"); + for (key, value) in [ + ("lengthX", json!(80.0)), + ("lengthY", json!(40.0)), + ("reference", json!(4)), + ("baseAlong", json!("y")), + ("slope", json!(-25.0)), + ("closed", json!(true)), + ("fillArc", json!(true)), + ] { + t.set_option(key, &value); + } + let (command, params) = t.command(Point::new(10.0, 20.0), Point::new(50.0, 70.0), Mods::default()); + assert_eq!(command, "shape.arc"); + assert_eq!( + params, + json!({ + "x": 30.0, + "y": 45.0, + "lengthX": 40.0, + "lengthY": 50.0, + "reference": 4, + "baseAlong": "y", + "slope": -25.0, + "closed": true, + "fillArc": true, + }) + ); } /// The start and the dragged corner snap to Smart Guides (#506), Shift sliding the corner of diff --git a/crates/tools/src/settings.rs b/crates/tools/src/settings.rs index 8abb10f44..e395105c0 100644 --- a/crates/tools/src/settings.rs +++ b/crates/tools/src/settings.rs @@ -26,7 +26,7 @@ const ROWS: &[Row] = &[ (SYMBOLISM, Some("symbolism"), &["diameter", "intensity", "density"]), (&["mirrorCut"], None, &["axis", "keep"]), (&["puppetWarp"], None, &["showMesh", "expand"]), - (&["arc"], None, &["closed"]), + (&["arc"], None, &["closed", "lengthX", "lengthY", "reference", "baseAlong", "slope", "fillArc"]), (&["spiral"], None, &["decay", "segments", "clockwise"]), (&["rectangularGrid"], None, &["rows", "columns", "rowSkew", "columnSkew", "frame", "fill"]), (&["polarGrid"], None, &["concentric", "radial", "concentricSkew", "radialSkew", "compound", "fill"]), diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index e7092235d..ffa232135 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -64,6 +64,7 @@ fn lengths(kind: &str) -> &'static [&'static str] { "polygon" => &["radius"], "star" => &["radius1", "radius2"], "lineSegment" => &["length"], + "arc" => &["lengthX", "lengthY"], "offsetPath" => &["offset"], _ => &[], } @@ -73,7 +74,7 @@ fn lengths(kind: &str) -> &'static [&'static str] { /// number: fields that ↑/↓ step by one (Shift: ten) and that do math, as the distances do. fn suffix(kind: &str, key: &str) -> Option<&'static str> { match (kind, key) { - ("rotate" | "shear" | "reflect" | "move", "angle") => Some("°"), + ("rotate" | "shear" | "reflect" | "move" | "lineSegment", "angle") => Some("°"), ("scale", "sx" | "sy") => Some("%"), _ => None, } @@ -96,6 +97,7 @@ fn order(kind: &str) -> &'static [&'static str] { "rectangularGrid" => &["width", "height", "rows", "rowSkew", "columns", "columnSkew"], "polarGrid" => &["width", "height", "concentric", "concentricSkew", "radial", "radialSkew"], "artboardOptions" => &["name", "width", "height"], + "lineSegment" => &["length", "angle"], _ => &[], } } diff --git a/crates/ui-egui/src/dialogs/mod.rs b/crates/ui-egui/src/dialogs/mod.rs index ac2c422fb..b75313615 100644 --- a/crates/ui-egui/src/dialogs/mod.rs +++ b/crates/ui-egui/src/dialogs/mod.rs @@ -246,7 +246,7 @@ macro_rules! registry { registry! { DimensionOptions: [dimension::KIND] => dimension::SPEC, NewDocument: [new_document::KIND] => new_document::SPEC, - Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "rectangularGrid", "polarGrid"] => shapes::SPEC, + Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "arc", "rectangularGrid", "polarGrid"] => shapes::SPEC, Transform: ["move", "rotate", "scale", "reflect", "shear"] => transform::SPEC, PathOp: ["average", "offsetPath", "simplify", "splitIntoGrid"] => path_ops::SPEC, DocumentSetup: ["documentSetup"] => document_setup::SPEC, diff --git a/crates/ui-egui/src/dialogs/shapes.rs b/crates/ui-egui/src/dialogs/shapes.rs index b0a3a1fd0..da21b9fb2 100644 --- a/crates/ui-egui/src/dialogs/shapes.rs +++ b/crates/ui-egui/src/dialogs/shapes.rs @@ -1,7 +1,8 @@ //! Shape size dialogs (a click with a shape tool): Rectangle, Rounded Rectangle, Ellipse, Polygon, -//! Star, Line Segment Tool Options and the two grids (size, divider counts and skews, switches). +//! Star, Line Segment and Arc tool options, and the two grids (size, divider counts and skews, switches). use serde_json::{Value, json}; +use vectorcraft_doc::Unit; use vectorcraft_geom::{Point, Rect}; use super::{DialogSpec, form, run_and_close}; @@ -21,6 +22,7 @@ fn title(kind: &str) -> &'static str { "star" => tl!("Star"), "rectangularGrid" => tl!("Rectangular Grid"), "polarGrid" => tl!("Polar Grid"), + "arc" => tl!("Arc Segment Tool Options"), _ => tl!("Line Segment Tool Options"), } } @@ -48,6 +50,21 @@ fn set_grid_placed(d: &mut Dialog, r: Rect) { d.fields.insert("height".into(), json!(r.height())); } +/// Arc segment dialog: keep reference `refi` on the canvas click (`__anchorX`/`__anchorY`). +pub(crate) fn arc_pin_to_anchor(d: &mut Dialog) { + let anchor = Point::new(d.f64("__anchorX", d.f64("x", 0.0)), d.f64("__anchorY", d.f64("y", 0.0))); + let refi = d.fields.get("reference").and_then(|v| v.as_u64()).map(|n| (n as usize).min(8)).unwrap_or(0); + let w = d.f64("lengthX", 100.0).abs().max(1e-9); + let h = d.f64("lengthY", 100.0).abs().max(1e-9); + let rp = vectorcraft_geom::reference_point(Rect::new(0.0, 0.0, w, h), refi); + d.fields.insert("x".into(), json!(anchor.x - rp.x)); + d.fields.insert("y".into(), json!(anchor.y - rp.y)); +} + +fn arc_reference(d: &Dialog) -> usize { + d.fields.get("reference").and_then(|v| v.as_u64()).map(|n| (n as usize).min(8)).unwrap_or(0) +} + /// Top-left `x`/`y` so reference point `refi` sits on the canvas click (`__anchorX`/`__anchorY`). pub(crate) fn grid_pin_to_anchor(d: &mut Dialog) { let anchor = Point::new(d.f64("__anchorX", d.f64("x", 0.0)), d.f64("__anchorY", d.f64("y", 0.0))); @@ -78,6 +95,8 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { set_grid_placed(d, artboard_rect(was, refi, None, None, Some(w), Some(h), false)); } }); + } else if d.kind == "arc" { + arc_form(ui, d, unit); } else { form::grid(ui, d, unit); } @@ -93,6 +112,50 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { false } +fn arc_form(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit) { + ui.horizontal(|ui| { + let refi = arc_reference(d); + if let Some(i) = widgets::reference_point(ui, refi) { + d.fields.insert("reference".into(), json!(i)); + arc_pin_to_anchor(d); + } + }); + ui.add_space(4.0); + let rail = crate::theme::Tokens::get(ui.ctx()).input_border; + egui::Grid::new("arc-dlg").num_columns(2).spacing([10.0, 8.0]).show(ui, |ui| { + let lx0 = d.f64("lengthX", 100.0); + form::length_field(ui, d, "lengthX", tl!("Length X-Axis:"), unit); + if d.f64("lengthX", 100.0) != lx0 { + arc_pin_to_anchor(d); + } + let ly0 = d.f64("lengthY", 100.0); + form::length_field(ui, d, "lengthY", tl!("Length Y-Axis:"), unit); + if d.f64("lengthY", 100.0) != ly0 { + arc_pin_to_anchor(d); + } + form::choice(ui, d, "arcType", tl!("Type:"), (form::FIELD_W, form::FIELD_W - 12.0), &[("open", tl!("Open")), ("closed", tl!("Closed"))]); + form::choice( + ui, + d, + "baseAlong", + tl!("Base Along:"), + (form::FIELD_W, form::FIELD_W - 12.0), + &[ + ("x", crate::i18n::tr_ctx(crate::i18n::current(), "axis", "X Axis")), + ("y", crate::i18n::tr_ctx(crate::i18n::current(), "axis", "Y Axis")), + ], + ); + form::row_label(ui, tl!("Slope:")); + ui.vertical(|ui| { + form::slider_field_named(ui, d, "slope", tl!("Slope:"), -100.0..=100.0, "", &|_| rail); + form::slider_ends(ui, form::SLIDER_LABEL, (tl!("Concave"), tl!("Convex"))); + }); + ui.end_row(); + }); + ui.add_space(8.0); + form::check(ui, d, "fillArc", tl!("Fill Arc")); +} + /// The top-left corner of a rectangle or ellipse: the click itself, or with Alt held (draw from centre) the click minus half the size. fn box_origin(d: &Dialog) -> (f64, f64) { let (x, y) = (d.f64("x", 0.0), d.f64("y", 0.0)); @@ -109,11 +172,25 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { "star" => Some(("points", 5.0, 2.0)), _ => None, }; - let options = count.map(|(key, default, min)| { - let mut values = json!({}); - values[key] = json!(d.f64(key, default).clamp(min, 1000.0) as u64); - values - }); + let options = count + .map(|(key, default, min)| { + let mut values = json!({}); + values[key] = json!(d.f64(key, default).clamp(min, 1000.0) as u64); + values + }) + .or_else(|| { + (d.kind == "arc").then(|| { + json!({ + "closed": d.str("arcType").eq_ignore_ascii_case("closed"), + "lengthX": d.f64("lengthX", 100.0), + "lengthY": d.f64("lengthY", 100.0), + "reference": arc_reference(d), + "baseAlong": d.str("baseAlong"), + "slope": d.f64("slope", 50.0), + "fillArc": d.bool("fillArc"), + }) + }) + }); let (x, y) = (d.f64("x", 0.0), d.f64("y", 0.0)); let (bx, by) = box_origin(d); let (id, params) = match d.kind.as_str() { @@ -140,6 +217,23 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { let (l, a) = (d.f64("length", 100.0), d.f64("angle", 0.0).to_radians()); ("shape.line", json!({"x1": x, "y1": y, "x2": x + l * a.cos(), "y2": y - l * a.sin()})) } + "arc" => { + let anchor = Point::new(d.f64("__anchorX", x), d.f64("__anchorY", y)); + ( + "shape.arc", + json!({ + "x": anchor.x, + "y": anchor.y, + "lengthX": d.f64("lengthX", 100.0), + "lengthY": d.f64("lengthY", 100.0), + "reference": arc_reference(d), + "baseAlong": d.str("baseAlong"), + "slope": d.f64("slope", 50.0), + "closed": d.str("arcType").eq_ignore_ascii_case("closed"), + "fillArc": d.bool("fillArc"), + }), + ) + } _ => ( "shape.rectangle", json!({"x": bx, "y": by, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0), "radius": d.f64("radius", 0.0)}), diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 8fbdf2d3a..775232b5e 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -41,6 +41,7 @@ fn kinds_map_to_their_dialogs() { ("newDocument", DialogKind::NewDocument), ("roundedRectangle", DialogKind::Shape), ("lineSegment", DialogKind::Shape), + ("arc", DialogKind::Shape), ("rectangularGrid", DialogKind::Shape), ("polarGrid", DialogKind::Shape), ("shear", DialogKind::Transform), @@ -88,6 +89,7 @@ fn every_dialog_draws() { "polygon", "star", "lineSegment", + "arc", "rectangularGrid", "polarGrid", "rotate", @@ -1711,6 +1713,25 @@ fn polar_grid_shared_canvas_click_defaults_units_counts_and_history() { assert_eq!(app.session.doc().unwrap().doc, made); } +/// Part of gap G11: an Arc tool click opens its options at the pointer; OK draws an arc there. +#[test] +fn arc_click_opens_options_and_ok_draws() { + use vectorcraft_doc::NodeKind; + let mut app = app(); + open_tool_dialog(&mut app, "arc", json!({"x": 100.0, "y": 50.0})); + let d = app.ui.dialog.as_ref().unwrap(); + assert_eq!(d.kind, "arc"); + assert!(d.fields.contains_key("lengthX") && d.fields.contains_key("lengthY")); + super::confirm(&mut app).unwrap(); + let n = app.session.doc().unwrap().doc.layers[0].children().unwrap().last().unwrap(); + assert!(matches!(n.kind, NodeKind::Path { .. })); + let pd = n.path_data().unwrap(); + let a0 = pd.subpaths[0].anchors[0].p; + let a1 = pd.subpaths[0].anchors[1].p; + assert!((a0.x - 100.0).abs() < 1e-6 && (a0.y - 50.0).abs() < 1e-6); + assert!((a1.x - 200.0).abs() < 1e-6 && (a1.y - 150.0).abs() < 1e-6); +} + /// #1377: Polar Grid's dialog has a skew slider under each divider count, Create Compound Path /// From Ellipses and Fill Grid, opened with the tool's stored values; OK draws with them and /// stores them. diff --git a/crates/ui-egui/src/dialogs/tools.rs b/crates/ui-egui/src/dialogs/tools.rs index acd76f3f4..bcad596f8 100644 --- a/crates/ui-egui/src/dialogs/tools.rs +++ b/crates/ui-egui/src/dialogs/tools.rs @@ -20,6 +20,28 @@ pub fn open_tool_dialog(app: &mut VectorcraftApp, kind: &str, p: Value) { Dialog::new(kind, json!({"x": x, "y": y, "radius1": "50 pt", "radius2": "25 pt", "points": app.session.tool_options_of(kind)["points"]})) } "lineSegment" => Dialog::new(kind, json!({"x": x, "y": y, "length": "100 pt", "angle": 0})), + "arc" => { + let opts = app.session.tool_options_of(kind); + let closed = opts.get("closed").and_then(Value::as_bool).unwrap_or(false); + let mut d = Dialog::new( + kind, + json!({ + "__anchorX": x, + "__anchorY": y, + "x": x, + "y": y, + "lengthX": opts.get("lengthX").cloned().unwrap_or(json!("100 pt")), + "lengthY": opts.get("lengthY").cloned().unwrap_or(json!("100 pt")), + "reference": opts.get("reference").cloned().unwrap_or(json!(0)), + "arcType": if closed { "closed" } else { "open" }, + "baseAlong": opts.get("baseAlong").cloned().unwrap_or(json!("x")), + "slope": opts.get("slope").cloned().unwrap_or(json!(50)), + "fillArc": opts.get("fillArc").and_then(Value::as_bool).unwrap_or(false), + }), + ); + super::shapes::arc_pin_to_anchor(&mut d); + d + } // The grids: the tool's stored options (divider counts and skews, switches) at the default size. "rectangularGrid" | "polarGrid" => { let mut obj = match app.session.tool_options_of(kind) { diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index b66475eb0..5f74d8a0c 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5358,6 +5358,16 @@ theme Appearance Външен вид Fresco Фреска Effect Gallery… Галерия с ефекти… Effect Gallery Dialog Диалог „Галерия с ефекти“ + Arc Segment Tool Options Опции на инструмента „Дъгов сегмент“ + Base Along: Основа по: + Closed Затворен + Convex Изпъкнал + Fill Arc Запълване на дъга + Length X-Axis: Дължина по X: + Length Y-Axis: Дължина по Y: + Slope: Наклон: +axis X Axis X ос +axis Y Axis Y ос @msg Packaged {n} file(s) in {folder} Пакетирани са {n} файла в {folder} @msg Packaged {n} file(s) {note} Пакетирани са {n} файла {note} @msg Packaged {n} file(s) Пакетирани са {n} файла diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 9a5256f57..2c031b25b 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5402,6 +5402,16 @@ mark-style Japanese Japonské Fresco Freska Effect Gallery… Galerie efektů… Effect Gallery Dialog Dialog Galerie efektů + Arc Segment Tool Options Možnosti nástroje Obloukový segment + Base Along: Základna podél: + Closed Uzavřený + Convex Konvexní + Fill Arc Výplň oblouku + Length X-Axis: Délka na ose X: + Length Y-Axis: Délka na ose Y: + Slope: Sklon: +axis X Axis osa X +axis Y Axis osa Y @msg Packaged {n} file(s) in {folder} Zabaleno souborů: {n} do {folder} @msg Packaged {n} file(s) {note} Zabaleno souborů: {n} {note} @msg Packaged {n} file(s) Zabaleno souborů: {n} diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index ea3b4cf9f..1e9d5a076 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5374,6 +5374,16 @@ swatch Bright Violet Kraftig violet Fresco Fresko Effect Gallery… Effektgalleri… Effect Gallery Dialog Dialogboksen Effektgalleri + Arc Segment Tool Options Indstillinger for buesegmentværktøj + Base Along: Base langs: + Closed Lukket + Convex Konvex + Fill Arc Udfyld bue + Length X-Axis: Længde X-akse: + Length Y-Axis: Længde Y-akse: + Slope: Hældning: +axis X Axis X-akse +axis Y Axis Y-akse @msg Packaged {n} file(s) in {folder} Pakkede {n} fil(er) i {folder} @msg Packaged {n} file(s) {note} Pakkede {n} fil(er) {note} @msg Packaged {n} file(s) Pakkede {n} fil(er) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index be635c5d5..8681b03b1 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4983,6 +4983,17 @@ effect Soft Weich Fresco Fresko Effect Gallery… Effekt-Galerie… Effect Gallery Dialog Dialogfeld „Effekt-Galerie“ + Arc Segment Tool Options Optionen für das Bogensegment-Werkzeug + Base Along: Basis entlang: + Closed Geschlossen + Concave Konkav + Convex Konvex + Fill Arc Bogen füllen + Length X-Axis: Länge X-Achse: + Length Y-Axis: Länge Y-Achse: + Slope: Steigung: +axis X Axis X-Achse +axis Y Axis Y-Achse @msg Packaged {n} file(s) in {folder} {n} Datei(en) in {folder} verpackt @msg Packaged {n} file(s) {note} {n} Datei(en) verpackt {note} @msg Packaged {n} file(s) {n} Datei(en) verpackt diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 3d5783a54..4358fb5f5 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5354,6 +5354,16 @@ effect Points Σημεία Fresco Νωπογραφία Effect Gallery… Συλλογή εφέ… Effect Gallery Dialog Παράθυρο διαλόγου Συλλογή εφέ + Arc Segment Tool Options Επιλογές εργαλείου τμήματος τόξου + Base Along: Βάση κατά: + Closed Κλειστό + Convex Κυρτό + Fill Arc Γέμισμα τόξου + Length X-Axis: Μήκος άξονα X: + Length Y-Axis: Μήκος άξονα Y: + Slope: Κλίση: +axis X Axis Άξονας X +axis Y Axis Άξονας Y @msg Packaged {n} file(s) in {folder} Συσκευάστηκαν {n} αρχεία στον φάκελο {folder} @msg Packaged {n} file(s) {note} Συσκευάστηκαν {n} αρχεία {note} @msg Packaged {n} file(s) Συσκευάστηκαν {n} αρχεία diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 369d8c0a7..fcb70e9a4 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5344,6 +5344,16 @@ mark-style Japanese Japonesas Fresco Fresco Effect Gallery… Galería de efectos… Effect Gallery Dialog Cuadro de diálogo Galería de efectos + Arc Segment Tool Options Opciones de la herramienta Segmento de arco + Base Along: Base a lo largo de: + Closed Cerrado + Convex Convexo + Fill Arc Rellenar arco + Length X-Axis: Longitud eje X: + Length Y-Axis: Longitud eje Y: + Slope: Pendiente: +axis X Axis Eje X +axis Y Axis Eje Y @msg Packaged {n} file(s) in {folder} Se empaquetaron {n} archivo(s) en {folder} @msg Packaged {n} file(s) {note} Se empaquetaron {n} archivo(s) {note} @msg Packaged {n} file(s) Se empaquetaron {n} archivo(s) diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 7d93f0751..0c3e3d646 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5433,6 +5433,16 @@ swatch Bright Violet Kirkas violetti Fresco Fresko Effect Gallery… Tehostegalleria… Effect Gallery Dialog Tehostegalleria-valintaikkuna + Arc Segment Tool Options Kaarisegmenttityökalun asetukset + Base Along: Pohja pitkin: + Closed Suljettu + Convex Kupera + Fill Arc Täytä kaari + Length X-Axis: Pituus X-akseli: + Length Y-Axis: Pituus Y-akseli: + Slope: Kaltevuus: +axis X Axis X-akseli +axis Y Axis Y-akseli @msg Packaged {n} file(s) in {folder} Paketoitiin {n} tiedosto(a) kansioon {folder} @msg Packaged {n} file(s) {note} Paketoitiin {n} tiedosto(a) {note} @msg Packaged {n} file(s) Paketoitiin {n} tiedosto(a) diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 2e2c13196..c8f714e95 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5401,6 +5401,16 @@ theme Appearance Apparence Fresco Fresque Effect Gallery… Galerie d'effets… Effect Gallery Dialog Boîte de dialogue Galerie d'effets + Arc Segment Tool Options Options de l’outil Segment d’arc + Base Along: Base le long de : + Closed Fermé + Convex Convexe + Fill Arc Remplir l’arc + Length X-Axis: Longueur axe X : + Length Y-Axis: Longueur axe Y : + Slope: Pente : +axis X Axis Axe X +axis Y Axis Axe Y @msg Packaged {n} file(s) in {folder} {n} fichier(s) assemblé(s) dans {folder} @msg Packaged {n} file(s) {note} {n} fichier(s) assemblé(s) {note} @msg Packaged {n} file(s) {n} fichier(s) assemblé(s) diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 44166d5a0..8ccba9237 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5388,6 +5388,16 @@ imageTrace Outline Körvonal Fresco Freskó Effect Gallery… Effektusgaléria… Effect Gallery Dialog Effektusgaléria párbeszédpanel + Arc Segment Tool Options Ívszakasz eszköz beállításai + Base Along: Alap mentén: + Closed Zárt + Convex Domború + Fill Arc Ív kitöltése + Length X-Axis: Hossz X tengely: + Length Y-Axis: Hossz Y tengely: + Slope: Lejtés: +axis X Axis X tengely +axis Y Axis Y tengely @msg Packaged {n} file(s) in {folder} Becsomagolt fájlok: {n}, mappa: {folder} @msg Packaged {n} file(s) {note} Becsomagolt fájlok: {n} {note} @msg Packaged {n} file(s) Becsomagolt fájlok: {n} diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 0d18d3799..4f096a63c 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5374,6 +5374,16 @@ swatch Bright Violet Violet cerah Fresco Fresko Effect Gallery… Galeri Efek… Effect Gallery Dialog Dialog Galeri Efek + Arc Segment Tool Options Opsi alat segmen busur + Base Along: Dasar sepanjang: + Closed Tertutup + Convex Cembung + Fill Arc Isi busur + Length X-Axis: Panjang sumbu X: + Length Y-Axis: Panjang sumbu Y: + Slope: Kemiringan: +axis X Axis Sumbu X +axis Y Axis Sumbu Y @msg Packaged {n} file(s) in {folder} Berhasil mengemas {n} file di {folder} @msg Packaged {n} file(s) {note} Berhasil mengemas {n} file {note} @msg Packaged {n} file(s) Berhasil mengemas {n} file diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index fdb0f9e19..e93bde038 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5017,6 +5017,17 @@ lighting Light Luce Fresco Affresco Effect Gallery… Galleria effetti… Effect Gallery Dialog Finestra di dialogo Galleria effetti + Arc Segment Tool Options Opzioni strumento Segmento arco + Base Along: Base lungo: + Closed Chiuso + Concave Concavo + Convex Convesso + Fill Arc Riempi arco + Length X-Axis: Lunghezza asse X: + Length Y-Axis: Lunghezza asse Y: + Slope: Pendenza: +axis X Axis Asse X +axis Y Axis Asse Y @msg Packaged {n} file(s) in {folder} Pacchetto creato con {n} file in {folder} @msg Packaged {n} file(s) {note} Pacchetto creato con {n} file {note} @msg Packaged {n} file(s) Pacchetto creato con {n} file diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 671a3f835..2c1e7210a 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5079,6 +5079,17 @@ lighting Light ライト Simulate prints overprints as Overprint Preview shows them; separations always keep them. A preset flattens transparency before printing (PostScript files always flatten it). シミュレートでは、オーバープリントプレビューと同じ見え方でオーバープリントを印刷します。分版では常にオーバープリントが保持されます。プリセットを選ぶと、印刷前に透明が分割・統合されます (PostScript ファイルでは常に分割・統合されます)。 Effect Gallery… 効果ギャラリー… Effect Gallery Dialog 効果ギャラリーダイアログ + Arc Segment Tool Options 円弧セグメントツールオプション + Base Along: 基準方向: + Closed 閉じる + Concave 凹 + Convex 凸 + Fill Arc 円弧を塗りつぶす + Length X-Axis: X 軸の長さ: + Length Y-Axis: Y 軸の長さ: + Slope: 勾配: +axis X Axis X 軸 +axis Y Axis Y 軸 @msg Packaged {n} file(s) in {folder} {n} 個のファイルを {folder} にパッケージしました @msg Packaged {n} file(s) {note} {n} 個のファイルをパッケージしました {note} @msg Packaged {n} file(s) {n} 個のファイルをパッケージしました diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index e5bd4b2fc..6a2ec9d6d 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5311,6 +5311,16 @@ imageTrace Outline კონტური Select artwork to apply effects აირჩიეთ ნამუშევარი ეფექტების გამოსაყენებლად Effect Gallery… ეფექტების გალერეა… Effect Gallery Dialog ეფექტების გალერეა დიალოგი + Arc Segment Tool Options რკალის სეგმენტის ხელსაწყოს პარამეტრები + Base Along: საფუძველი გასწვრივ: + Closed დახურული + Convex ამობზარი + Fill Arc რკალის შევსება + Length X-Axis: სიგრძე X ღერძზე: + Length Y-Axis: სიგრძე Y ღერძზე: + Slope: დახრა: +axis X Axis X ღერძი +axis Y Axis Y ღერძი @msg Packaged {n} file(s) in {folder} შეფუთულია ფაილი ({n}) საქაღალდეში {folder} @msg Packaged {n} file(s) {note} შეფუთულია ფაილი ({n}) {note} @msg Packaged {n} file(s) შეფუთულია ფაილი ({n}) diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index bcb591a38..193f0286b 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4980,6 +4980,17 @@ lighting Light 광원 Fresco 프레스코 Effect Gallery… 효과 갤러리… Effect Gallery Dialog 효과 갤러리 대화 상자 + Arc Segment Tool Options 호 세그먼트 도구 옵션 + Base Along: 기준 방향: + Closed 닫힘 + Concave 오목 + Convex 볼록 + Fill Arc 호 채우기 + Length X-Axis: X축 길이: + Length Y-Axis: Y축 길이: + Slope: 기울기: +axis X Axis X축 +axis Y Axis Y축 @msg Packaged {n} file(s) in {folder} {folder}에 파일 {n}개를 패키지했습니다 @msg Packaged {n} file(s) {note} 파일 {n}개를 패키지했습니다 {note} @msg Packaged {n} file(s) 파일 {n}개를 패키지했습니다 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 6666f4515..2fc7256a7 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5417,6 +5417,16 @@ swatch Bright Violet Felviolet Fresco Fresco Effect Gallery… Effectgalerij… Effect Gallery Dialog Dialoogvenster Effectgalerij + Arc Segment Tool Options Opties boogsegmentgereedschap + Base Along: Basis langs: + Closed Gesloten + Convex Concaaf + Fill Arc Boog vullen + Length X-Axis: Lengte X-as: + Length Y-Axis: Lengte Y-as: + Slope: Helling: +axis X Axis X-as +axis Y Axis Y-as @msg Packaged {n} file(s) in {folder} {n} bestand(en) verpakt in {folder} @msg Packaged {n} file(s) {note} {n} bestand(en) verpakt {note} @msg Packaged {n} file(s) {n} bestand(en) verpakt diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 6728f08a3..bfd546675 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5254,6 +5254,16 @@ mark-style Japanese Japońskie Fresco Fresk Effect Gallery… Galeria efektów… Effect Gallery Dialog Okno dialogowe Galeria efektów + Arc Segment Tool Options Opcje narzędzia Segment łuku + Base Along: Podstawa wzdłuż: + Closed Zamknięty + Convex Wypukła + Fill Arc Wypełnij łuk + Length X-Axis: Długość osi X: + Length Y-Axis: Długość osi Y: + Slope: Nachylenie: +axis X Axis Oś X +axis Y Axis Oś Y @msg Packaged {n} file(s) in {folder} Spakowane pliki: {n} w {folder} @msg Packaged {n} file(s) {note} Spakowane pliki: {n} {note} @msg Packaged {n} file(s) Spakowane pliki: {n} diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 2652b733e..8434a666f 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5319,6 +5319,16 @@ mark-style Japanese Japonesas Fresco Afresco Effect Gallery… Galeria de Efeitos… Effect Gallery Dialog Caixa de diálogo Galeria de Efeitos + Arc Segment Tool Options Opções da Ferramenta Segmento de Arco + Base Along: Base ao longo de: + Closed Fechado + Convex Convexo + Fill Arc Preencher arco + Length X-Axis: Comprimento eixo X: + Length Y-Axis: Comprimento eixo Y: + Slope: Inclinação: +axis X Axis Eixo X +axis Y Axis Eixo Y @msg Packaged {n} file(s) in {folder} {n} arquivo(s) empacotado(s) em {folder} @msg Packaged {n} file(s) {note} {n} arquivo(s) empacotado(s) {note} @msg Packaged {n} file(s) {n} arquivo(s) empacotado(s) diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 121c69b02..23f269d9a 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5013,6 +5013,17 @@ lighting Light Свет Fresco Фреска Effect Gallery… Галерея эффектов… Effect Gallery Dialog Диалог «Галерея эффектов» + Arc Segment Tool Options Параметры инструмента «Дуговой сегмент» + Base Along: Основание вдоль: + Closed Замкнутый + Concave Вогнутый + Convex Выпуклый + Fill Arc Заливка дуги + Length X-Axis: Длина по оси X: + Length Y-Axis: Длина по оси Y: + Slope: Уклон: +axis X Axis Ось X +axis Y Axis Ось Y @msg Packaged {n} file(s) in {folder} Упаковано файлов: {n} в {folder} @msg Packaged {n} file(s) {note} Упаковано файлов: {n} {note} @msg Packaged {n} file(s) Упаковано файлов: {n} diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index e667d47fe..794858f95 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5358,6 +5358,16 @@ theme Appearance Utseende Fresco Fresk Effect Gallery… Effektgalleri… Effect Gallery Dialog Dialogrutan Effektgalleri + Arc Segment Tool Options Alternativ för bågsegmentsverktyget + Base Along: Bas längs: + Closed Stängd + Convex Konvex + Fill Arc Fyll båge + Length X-Axis: Längd X-axel: + Length Y-Axis: Längd Y-axel: + Slope: Lutning: +axis X Axis X-axel +axis Y Axis Y-axel @msg Packaged {n} file(s) in {folder} Paketerade {n} fil(er) i {folder} @msg Packaged {n} file(s) {note} Paketerade {n} fil(er) {note} @msg Packaged {n} file(s) Paketerade {n} fil(er) diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index d2bd8ad07..712abedc8 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5361,6 +5361,17 @@ theme Appearance Görünüm Fresco Fresk Effect Gallery… Efekt Galerisi… Effect Gallery Dialog Efekt Galerisi İletişim Kutusu + Arc Segment Tool Options Yay Segmenti Aracı Seçenekleri + Base Along: Taban boyunca: + Closed Kapalı + Convex dışbükey + Fill Arc Yayı doldur + Length X-Axis: X ekseni uzunluğu: + Length Y-Axis: Y ekseni uzunluğu: + Slope: Eğim: +axis X Axis X ekseni +axis Y Axis Y ekseni + @msg Packaged {n} file(s) in {folder} {folder} biçiminde paketlenmiş {n} dosyası/dosyaları @msg Packaged {n} file(s) {note} Paketlenmiş {n} dosyası/dosyaları {note} @msg Packaged {n} file(s) Paketlenmiş {n} dosyası/dosyaları diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 5aeb9b144..2bf4ccc2b 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4997,6 +4997,17 @@ lighting Light Світло Fresco Фреска Effect Gallery… Галерея ефектів… Effect Gallery Dialog Діалог «Галерея ефектів» + Arc Segment Tool Options Параметри інструменту «Дуговий сегмент» + Base Along: Основа вздовж: + Closed Замкнений + Concave Увігнутий + Convex Опуклий + Fill Arc Заливка дуги + Length X-Axis: Довжина вздовж осі X: + Length Y-Axis: Довжина вздовж осі Y: + Slope: Ухил: +axis X Axis Вісь X +axis Y Axis Вісь Y @msg Packaged {n} file(s) in {folder} Упаковано файли: {n} {folder} @msg Packaged {n} file(s) {note} Упаковано файли: {n} {note} @msg Packaged {n} file(s) Упаковано файли: {n} diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 00f5cf042..a66258d11 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4997,6 +4997,17 @@ theme Appearance Giao diện Fresco Bích họa Effect Gallery… Thư viện hiệu ứng… Effect Gallery Dialog Hộp thoại Thư viện hiệu ứng + Arc Segment Tool Options Tùy chọn công cụ cung phân đoạn + Base Along: Cơ sở dọc theo: + Closed Đóng + Concave Lõm + Convex Lồi + Fill Arc Tô cung + Length X-Axis: Chiều dài trục X: + Length Y-Axis: Chiều dài trục Y: + Slope: Độ dốc: +axis X Axis Trục X +axis Y Axis Trục Y @msg Packaged {n} file(s) in {folder} Đã đóng gói {n} tệp trong {folder} @msg Packaged {n} file(s) {note} Đã đóng gói {n} tệp {note} @msg Packaged {n} file(s) Đã đóng gói {n} tệp diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index be7bdf10b..0c0687a1b 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5241,6 +5241,17 @@ lighting Light 光源 Fresco 壁画 Effect Gallery… 效果库… Effect Gallery Dialog 效果库对话框 + Arc Segment Tool Options 弧段工具选项 + Base Along: 基线方向: + Closed 闭合 + Concave 凹 + Convex 凸 + Fill Arc 填充弧 + Length X-Axis: X 轴长度: + Length Y-Axis: Y 轴长度: + Slope: 斜率: +axis X Axis X 轴 +axis Y Axis Y 轴 @msg Packaged {n} file(s) in {folder} 已将 {n} 个文件打包到 {folder} @msg Packaged {n} file(s) {note} 已打包 {n} 个文件 {note} @msg Packaged {n} file(s) 已打包 {n} 个文件 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 01e3a90b3..94344b535 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5036,6 +5036,17 @@ lighting Light 光源 Fresco 壁畫 Effect Gallery… 效果庫… Effect Gallery Dialog 效果庫對話框 + Arc Segment Tool Options 弧段工具選項 + Base Along: 基線方向: + Closed 封閉 + Concave 凹 + Convex 凸 + Fill Arc 填色弧形 + Length X-Axis: X 軸長度: + Length Y-Axis: Y 軸長度: + Slope: 斜率: +axis X Axis X 軸 +axis Y Axis Y 軸 @msg Packaged {n} file(s) in {folder} 已將 {n} 個檔案封裝到 {folder} @msg Packaged {n} file(s) {note} 已封裝 {n} 個檔案 {note} @msg Packaged {n} file(s) 已封裝 {n} 個檔案