Merge pull request #3507 from nrq6993-a11y/stream/vc-feat-g11-arc-tool-dialog

G11: Open Arc tool options on canvas click
This commit is contained in:
BFlat
2026-10-22 13:50:46 +07:00
committed by GitHub
33 changed files with 564 additions and 29 deletions
+37 -4
View File
@@ -32,7 +32,15 @@ pub fn specs() -> Vec<CommandSpec> {
),
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<Value> {
}
fn arc(s: &mut Session, p: &Value) -> Result<Value> {
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
+28 -10
View File
@@ -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);
}
}
+101 -6
View File
@@ -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
+1 -1
View File
@@ -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"]),
+3 -1
View File
@@ -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"],
_ => &[],
}
}
+1 -1
View File
@@ -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,
+100 -6
View File
@@ -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<Value, String> {
"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<Value, String> {
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)}),
+21
View File
@@ -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.
+22
View File
@@ -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) {
+10
View File
@@ -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} файла
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+11
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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} αρχεία
Can't render this file because it contains an unexpected character in line 2045 and column 19.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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
Can't render this file because it contains an unexpected character in line 3816 and column 19.
+11
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+11
View File
@@ -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} 個のファイルをパッケージしました
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+11
View File
@@ -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}개를 패키지했습니다
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 6 and column 47.
+11
View File
@@ -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}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+10
View File
@@ -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)
Can't render this file because it contains an unexpected character in line 5 and column 25.
+11
View File
@@ -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ı
Can't render this file because it contains an unexpected character in line 5 and column 25.
+11
View File
@@ -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}
Can't render this file because it contains an unexpected character in line 733 and column 40.
+11
View File
@@ -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
Can't render this file because it contains an unexpected character in line 4028 and column 10.
+11
View File
@@ -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} 个文件
Can't render this file because it contains an unexpected character in line 2 and column 73.
+11
View File
@@ -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} 個檔案
Can't render this file because it contains an unexpected character in line 6 and column 47.