mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2537 from vangel76/feat/2530-tangent-perpendicular-snap
Snapping: tangent and perpendicular snapping, View > Snapping (#2530)
This commit is contained in:
@@ -235,6 +235,8 @@ pub const PREF_SPECS: &[PrefSpec] = &[
|
||||
p!("measurementLabels", "Smart Guides", "Display Options", "Measurement Labels", bool),
|
||||
p!("spacingGuides", "Smart Guides", "Display Options", "Spacing Guides", bool),
|
||||
p!("snappingTolerance", "Smart Guides", "", "Snapping Tolerance", num(0.0, 40.0, "pt")),
|
||||
p!("snapTangent", "Smart Guides", "Geometric Guides", "Snap to Tangent", bool),
|
||||
p!("snapPerpendicular", "Smart Guides", "Geometric Guides", "Snap to Perpendicular", bool),
|
||||
// Slices
|
||||
p!("showSliceNumbers", "Slices", "", "Show Slice Numbers", bool),
|
||||
p!("sliceLineColor", "Slices", "", "Line Color", color),
|
||||
|
||||
@@ -969,6 +969,8 @@ pub struct Prefs {
|
||||
pub measurement_labels: bool,
|
||||
pub spacing_guides: bool,
|
||||
pub snapping_tolerance: f64,
|
||||
pub snap_tangent: bool,
|
||||
pub snap_perpendicular: bool,
|
||||
// Slices
|
||||
pub show_slice_numbers: bool,
|
||||
pub slice_line_color: String,
|
||||
@@ -1218,6 +1220,8 @@ impl Default for Prefs {
|
||||
measurement_labels: true,
|
||||
spacing_guides: true,
|
||||
snapping_tolerance: 4.0,
|
||||
snap_tangent: true,
|
||||
snap_perpendicular: true,
|
||||
show_slice_numbers: true,
|
||||
slice_line_color: s("#ff3f3f"),
|
||||
hyphenation_language: s("English: USA"),
|
||||
|
||||
@@ -238,6 +238,8 @@ impl Session {
|
||||
// wins instead of pixels quietly rounding every drag to whole points (#1816).
|
||||
snap_to_pixel: view.snap_to_pixel && !view.snap_to_grid,
|
||||
snap_to_point: view.snap_to_point,
|
||||
snap_tangent: self.prefs.snap_tangent,
|
||||
snap_perpendicular: self.prefs.snap_perpendicular,
|
||||
snap_to_glyph: view.snap_to_glyph && view.smart_guides && !view.snap_to_grid,
|
||||
corner_widgets: view.corner_widgets,
|
||||
fill_active: self.fill_active,
|
||||
|
||||
@@ -376,8 +376,8 @@ impl CurvatureTool {
|
||||
/// and the segment to it: the path being drawn, or the selected one whose end is current.
|
||||
fn leave(&self, cx: &ToolContext) -> Option<(NodeId, Leave)> {
|
||||
match self.active(cx) {
|
||||
Some(id) => self.pts.last().map(|(p, _)| (id, Leave::segment(cx, *p, false))),
|
||||
None => current_end(cx).map(|e| (e.id, Leave::segment(cx, e.p, false))),
|
||||
Some(id) => self.pts.last().map(|(p, _)| (id, Leave::segment(cx, *p, false).without_contact())),
|
||||
None => current_end(cx).map(|e| (e.id, Leave::segment(cx, e.p, false).without_contact())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ impl CurvatureTool {
|
||||
return Some(out);
|
||||
}
|
||||
if let Some(e) = end {
|
||||
let q = self.snap.press(cx, p, &[e.id], Some(&Leave::segment(cx, e.p, false)));
|
||||
let q = self.snap.press(cx, p, &[e.id], Some(&Leave::segment(cx, e.p, false).without_contact()));
|
||||
let how = self.how(&e);
|
||||
self.placed = Some(Placed { id: e.id, si: e.si, start: e.start, anchors: e.anchors + 1, corner: ev.mods.alt });
|
||||
let v = json!({"id": e.id.0, "subpath": e.si, "op": "extend", "end": e.name(), "x": q.x, "y": q.y, "from": how.name(), "corner": ev.mods.alt});
|
||||
|
||||
+216
-6
@@ -13,8 +13,8 @@ use std::sync::Arc;
|
||||
|
||||
use vectorcraft_doc::hit::{HitOptions, hit_test};
|
||||
use vectorcraft_doc::{Document, Node, NodeId, NodeKind, OrientedBox, Selection};
|
||||
use vectorcraft_geom::kurbo::ParamCurveNearest;
|
||||
use vectorcraft_geom::{Affine, Line, ParamCurve, PathSeg, Point, Rect, Shape, SubPath, Vec2, constrain_angle_from};
|
||||
use vectorcraft_geom::kurbo::{ParamCurveDeriv, ParamCurveNearest};
|
||||
use vectorcraft_geom::{Affine, BezPath, Line, ParamCurve, PathSeg, Point, Rect, Shape, SubPath, Vec2, constrain_angle_from};
|
||||
use vectorcraft_text::{FontDb, layout as layout_text};
|
||||
|
||||
use crate::bbox::Handle;
|
||||
@@ -89,6 +89,10 @@ pub struct Targets {
|
||||
/// it ends, as the end that stays put and the unit direction towards the dragged one
|
||||
/// ([`Self::for_anchor_drag`]).
|
||||
extensions: Vec<(Point, Vec2)>,
|
||||
/// Snap to Tangent and Snap to Perpendicular: a drawn line from an anchor lands where it
|
||||
/// grazes a curve, or meets a segment square ([`Self::touching`]).
|
||||
tangent: bool,
|
||||
perpendicular: bool,
|
||||
/// How the guides these targets produce look.
|
||||
style: GuideStyle,
|
||||
}
|
||||
@@ -560,6 +564,11 @@ impl Targets {
|
||||
if let Some(q) = self.crossing(p, tol) {
|
||||
return (q, self.label(q, "intersect").into_iter().collect());
|
||||
}
|
||||
if let Some(f) = from.filter(|f| f.contact)
|
||||
&& let Some((q, perpendicular, dir)) = self.touching(f.at, p, tol)
|
||||
{
|
||||
return (q, self.touch_guides(f.at, q, perpendicular, dir, tol));
|
||||
}
|
||||
if let Some(q) = self.on_segment(p, tol) {
|
||||
return (q, self.label(q, Kind::Edge.label()).into_iter().collect());
|
||||
}
|
||||
@@ -602,6 +611,52 @@ impl Targets {
|
||||
(out, ov)
|
||||
}
|
||||
|
||||
/// Where a line from `at` through a point near `p` touches a target segment: square to it
|
||||
/// (`perpendicular`, the foot of the perpendicular from `at`) or just grazing a curve
|
||||
/// (`tangent`), within `tol` of `p`. The point, whether it is the perpendicular one, and the
|
||||
/// segment's direction there. Targets gather segments for drawn points and dragged anchors only.
|
||||
fn touching(&self, at: Point, p: Point, tol: f64) -> Option<(Point, bool, Vec2)> {
|
||||
if !(self.tangent || self.perpendicular) || ![at.x, at.y, p.x, p.y, tol].iter().all(|v| v.is_finite()) {
|
||||
return None;
|
||||
}
|
||||
let mut best: Option<(f64, Point, bool, Vec2)> = None;
|
||||
// Every segment in reach is tried: their order is the document's, not the distance.
|
||||
for (_, seg) in self.segments.iter().filter(|(b, _)| b.inflate(tol, tol).contains(p)) {
|
||||
let curved = !matches!(seg, PathSeg::Line(_));
|
||||
for (perpendicular, wanted) in [(true, self.perpendicular && !curved), (false, self.tangent && curved)] {
|
||||
if !wanted {
|
||||
continue;
|
||||
}
|
||||
for t in touch_params(seg, at, perpendicular) {
|
||||
let (q, d) = (seg.eval(t), seg_direction(seg, t));
|
||||
let dist = q.distance(p);
|
||||
if dist <= tol && q.distance(at) > tol && best.is_none_or(|b| dist < b.0) {
|
||||
best = Some((dist, q, perpendicular, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
best.map(|(_, q, perpendicular, d)| (q, perpendicular, d))
|
||||
}
|
||||
|
||||
/// The guides of a point landed where the line from `at` touches a segment at `q` running
|
||||
/// along `dir`: the line itself, a square corner mark for the perpendicular, and the label.
|
||||
fn touch_guides(&self, at: Point, q: Point, perpendicular: bool, dir: Vec2, tol: f64) -> Vec<Overlay> {
|
||||
let mut out: Vec<Overlay> = self.line(at, q).into_iter().collect();
|
||||
let (u, v) = (at - q, dir);
|
||||
if perpendicular && self.style.lines && u.hypot2() > 1e-18 && v.hypot2() > 1e-18 {
|
||||
let (u, v) = (u / u.hypot(), v / v.hypot());
|
||||
let s = tol * 1.5;
|
||||
let mut mark = BezPath::new();
|
||||
mark.move_to(q + u * s);
|
||||
mark.line_to(q + u * s + v * s);
|
||||
mark.line_to(q + v * s);
|
||||
out.push(Overlay::Path { path: mark, color: self.style.color, width: 1.0, dashed: false });
|
||||
}
|
||||
out.extend(self.label(q, if perpendicular { "perpendicular" } else { "tangent" }));
|
||||
out
|
||||
}
|
||||
|
||||
/// The segment extension nearest `p`, within `tol` of it (and `p` farther than that from the
|
||||
/// end it starts at): that end, its direction and where `p` lands on it. Where `p` is nearer
|
||||
/// the horizontal or the vertical through that end there is none: lining up with it ("align")
|
||||
@@ -1123,6 +1178,53 @@ fn meeting(a: &PathSeg, b: &PathSeg, p: Point) -> Option<Point> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The direction of `seg` at `t` (not normalised; zero in a cusp).
|
||||
fn seg_direction(seg: &PathSeg, t: f64) -> Vec2 {
|
||||
match seg {
|
||||
PathSeg::Line(l) => l.p1 - l.p0,
|
||||
PathSeg::Quad(q) => q.deriv().eval(t).to_vec2(),
|
||||
PathSeg::Cubic(c) => c.deriv().eval(t).to_vec2(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The parameters in (0, 1) where the line from `at` to `seg` is square to it (`perpendicular`)
|
||||
/// or grazes it, found by sampling the curve for a change of sign and bisecting.
|
||||
fn touch_params(seg: &PathSeg, at: Point, perpendicular: bool) -> Vec<f64> {
|
||||
const STEPS: usize = 24;
|
||||
let f = |t: f64| {
|
||||
let (v, d) = (seg.eval(t) - at, seg_direction(seg, t));
|
||||
if perpendicular { v.dot(d) } else { v.cross(d) }
|
||||
};
|
||||
let mut out = vec![];
|
||||
let mut prev = (0.0, f(0.0));
|
||||
for i in 1..=STEPS {
|
||||
let t = i as f64 / STEPS as f64;
|
||||
let cur = (t, f(t));
|
||||
if prev.1.is_finite() && cur.1.is_finite() && prev.1 * cur.1 < 0.0 {
|
||||
let (mut lo, mut hi) = (prev, cur);
|
||||
for _ in 0..40 {
|
||||
let mid = 0.5 * (lo.0 + hi.0);
|
||||
let m = (mid, f(mid));
|
||||
if lo.1 * m.1 <= 0.0 {
|
||||
hi = m;
|
||||
} else {
|
||||
lo = m;
|
||||
}
|
||||
}
|
||||
let t = 0.5 * (lo.0 + hi.0);
|
||||
// A jump through a cusp changes sign without crossing zero: the line must really
|
||||
// run along (or square to) the curve there.
|
||||
let (v, d) = (seg.eval(t) - at, seg_direction(seg, t));
|
||||
let scale = v.hypot() * d.hypot();
|
||||
if scale > 1e-12 && f(t).abs() <= 1e-3 * scale {
|
||||
out.push(t);
|
||||
}
|
||||
}
|
||||
prev = cur;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Snap `p` for a drawing tool when smart guides (or grid snapping) are on.
|
||||
pub fn snap_draw(cx: &ToolContext, p: Point, exclude: &[NodeId]) -> (Point, Vec<Overlay>) {
|
||||
snap_with(cx, p, || Targets::collect(cx.doc, exclude, None))
|
||||
@@ -1148,7 +1250,8 @@ impl PointSnap {
|
||||
None
|
||||
} else if cx.smart_guides {
|
||||
// Snap to Point keeps pulling points onto the ruler guides with Smart Guides on (#812).
|
||||
let t = targets().styled(cx);
|
||||
let mut t = targets().styled(cx);
|
||||
(t.tangent, t.perpendicular) = (cx.snap_tangent, cx.snap_perpendicular);
|
||||
Some((if cx.snap_to_point { t.with_rulers(cx) } else { t }, cx.snapping_tolerance))
|
||||
} else if cx.snap_to_point {
|
||||
Some((targets().for_snap_to_point(cx), cx.snap_tolerance))
|
||||
@@ -1247,6 +1350,8 @@ pub struct Leave {
|
||||
shift: Option<Keep>,
|
||||
/// The angles of the construction guides through `at` (none with them off).
|
||||
angles: &'static [f64],
|
||||
/// A drawn segment: it may land square to a segment or grazing a curve (a box does not).
|
||||
contact: bool,
|
||||
}
|
||||
|
||||
impl Leave {
|
||||
@@ -1257,14 +1362,21 @@ impl Leave {
|
||||
at,
|
||||
shift: shift.then_some(Keep::Angle { step: 45.0, base: cx.constrain_angle }),
|
||||
angles: if cx.smart_guides { cx.construction_angles } else { &[] },
|
||||
contact: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// A curved source cannot make a line-to-line perpendicular or line-to-curve tangent contact.
|
||||
pub fn without_contact(mut self) -> Self {
|
||||
self.contact = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// A box dragged from its corner or centre `at` (Rectangle, Ellipse, Arc, the grids, and
|
||||
/// Artboard, which passes no Shift): Shift keeps the dragged corner on a diagonal, the box a
|
||||
/// square.
|
||||
pub fn diagonal(at: Point, shift: bool) -> Self {
|
||||
Self { at, shift: shift.then_some(Keep::Square), angles: &[] }
|
||||
Self { at, shift: shift.then_some(Keep::Square), angles: &[], contact: false }
|
||||
}
|
||||
|
||||
/// Where Shift puts `p`: none without it.
|
||||
@@ -1324,7 +1436,7 @@ struct SnapKey {
|
||||
revision: (u64, u64),
|
||||
exclude: Vec<NodeId>,
|
||||
screen: Option<ScreenFrame>,
|
||||
view: [bool; 5],
|
||||
view: [bool; 7],
|
||||
snap_to_glyph: bool,
|
||||
style: GuideStyle,
|
||||
tolerances: [f64; 2],
|
||||
@@ -1336,7 +1448,7 @@ impl SnapKey {
|
||||
revision: cx.revision,
|
||||
exclude: exclude.to_vec(),
|
||||
screen: cx.screen,
|
||||
view: [cx.smart_guides, cx.snap_to_point, cx.snap_to_pixel, cx.snap_to_grid, cx.guides],
|
||||
view: [cx.smart_guides, cx.snap_to_point, cx.snap_to_pixel, cx.snap_to_grid, cx.guides, cx.snap_tangent, cx.snap_perpendicular],
|
||||
snap_to_glyph: cx.snap_to_glyph && cx.alignment_guides,
|
||||
style: GuideStyle::of(cx),
|
||||
tolerances: [cx.snapping_tolerance, cx.snap_tolerance],
|
||||
@@ -1868,6 +1980,104 @@ mod tests {
|
||||
assert_eq!(labels(&t.snap_from(Point::new(170.0, 201.0), None, 4.0).1), ["path"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn perpendicular_snapping_rejects_a_curved_target() {
|
||||
let (mut d, _) = doc_with_rect();
|
||||
let layer = d.layers[0].id;
|
||||
let id = d.alloc_id();
|
||||
d.insert(
|
||||
Some(layer),
|
||||
1,
|
||||
vectorcraft_doc::Node::path(
|
||||
id,
|
||||
vectorcraft_geom::shapes::ellipse(Rect::new(450.0, 450.0, 550.0, 550.0)),
|
||||
vectorcraft_doc::Appearance::default_art(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let targets = Targets { tangent: false, perpendicular: true, ..Targets::for_drawing(&d, &[], None, false) };
|
||||
assert!(targets.touching(Point::new(400.0, 500.0), Point::new(450.0, 500.0), 4.0).is_none());
|
||||
assert!(targets.touching(Point::new(300.0, 160.0), Point::new(200.0, 160.0), 4.0).is_some());
|
||||
}
|
||||
|
||||
/// A line drawn from an anchor lands square to a segment (the foot of its perpendicular) and
|
||||
/// just grazing a curve, with a guide and a label; the options switch each off, and the
|
||||
/// result is the same each time and survives non-finite input.
|
||||
#[test]
|
||||
fn drawn_lines_land_perpendicular_to_segments_and_tangent_to_curves() {
|
||||
use vectorcraft_doc::{Appearance, Node};
|
||||
use vectorcraft_geom::shapes;
|
||||
let (mut d, _) = doc_with_rect();
|
||||
let l = d.layers[0].id;
|
||||
let id = d.alloc_id();
|
||||
d.insert(Some(l), 1, Node::path(id, shapes::ellipse(Rect::new(450.0, 450.0, 550.0, 550.0)), Appearance::default_art())).unwrap();
|
||||
let (s, p) = (Selection::default(), paint());
|
||||
let c = cx(&d, &s, &p);
|
||||
let snap = |c: &ToolContext, from: Point, to: Point| {
|
||||
let leave = Leave::segment(c, from, false);
|
||||
PointSnap::new(c, || Targets::for_drawing(&d, &[], None, false)).snap_from(c, to, Some(&leave))
|
||||
};
|
||||
// From (300, 160) the rect's right side (x = 200) is square at (200, 160).
|
||||
let (q, ov) = snap(&c, Point::new(300.0, 160.0), Point::new(202.0, 163.0));
|
||||
assert!((q - Point::new(200.0, 160.0)).hypot() < 1e-6, "{q:?}");
|
||||
assert_eq!(labels(&ov), ["perpendicular"]);
|
||||
assert_eq!(lines(&ov), vec![(Point::new(300.0, 160.0), q)]);
|
||||
assert!(ov.iter().any(|o| matches!(o, Overlay::Path { .. })), "the square corner mark: {ov:?}");
|
||||
assert_eq!(snap(&c, Point::new(300.0, 160.0), Point::new(202.0, 163.0)).0, q, "deterministic");
|
||||
// Off, the point only lands on the path, where the pointer is.
|
||||
let curved = Leave::segment(&c, Point::new(300.0, 160.0), false).without_contact();
|
||||
let (_, ov) = PointSnap::new(&c, || Targets::for_drawing(&d, &[], None, false)).snap_from(&c, Point::new(202.0, 163.0), Some(&curved));
|
||||
assert!(!labels(&ov).contains(&"perpendicular"));
|
||||
let off = ToolContext { snap_perpendicular: false, ..cx(&d, &s, &p) };
|
||||
let (q, ov) = snap(&off, Point::new(300.0, 160.0), Point::new(202.0, 163.0));
|
||||
assert_eq!((q, labels(&ov)), (Point::new(200.0, 163.0), vec!["path"]));
|
||||
// The circle (centre (500, 500), radius 50) seen from (400, 500): the tangent points are 60°
|
||||
// round from the way back, at (475, 456.7) and (475, 543.3).
|
||||
let (q, ov) = snap(&c, Point::new(400.0, 500.0), Point::new(477.0, 459.0));
|
||||
assert_eq!(labels(&ov), ["tangent"]);
|
||||
assert!((q - Point::new(475.0, 500.0 - 1875f64.sqrt())).hypot() < 0.1, "{q:?}");
|
||||
assert!(((q - Point::new(400.0, 500.0)).dot(q - Point::new(500.0, 500.0))).abs() < 20.0, "{q:?}");
|
||||
let none = ToolContext { snap_tangent: false, ..cx(&d, &s, &p) };
|
||||
assert!(!labels(&snap(&none, Point::new(400.0, 500.0), Point::new(477.0, 459.0)).1).contains(&"tangent"));
|
||||
// Out of reach, or with nothing finite to go on: left alone, no panic.
|
||||
assert_eq!(snap(&c, Point::new(300.0, 160.0), Point::new(250.0, 175.0)).0, Point::new(250.0, 175.0));
|
||||
let t = Targets::for_drawing(&d, &[], None, false);
|
||||
let t = Targets { tangent: true, perpendicular: true, ..t };
|
||||
for bad in [f64::NAN, f64::INFINITY] {
|
||||
assert!(t.touching(Point::new(bad, 1.0), Point::new(200.0, 160.0), 4.0).is_none());
|
||||
assert!(t.touching(Point::new(300.0, 160.0), Point::new(bad, 1.0), 4.0).is_none());
|
||||
assert!(t.touching(Point::new(300.0, 160.0), Point::new(200.0, 160.0), bad).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
/// A box dragged from a corner (Rectangle, Ellipse, ...) never snaps square to a segment, and
|
||||
/// a busy spot does not hide the contact: every segment in reach is tried.
|
||||
#[test]
|
||||
fn contact_snaps_only_for_segments_and_in_busy_spots() {
|
||||
use vectorcraft_geom::kurbo::Line;
|
||||
let (d, _) = doc_with_rect();
|
||||
let (s, p) = (Selection::default(), paint());
|
||||
let c = cx(&d, &s, &p);
|
||||
let snap = |t: &Targets, leave: &Leave| t.snap_from(Point::new(202.0, 163.0), Some(leave), 4.0);
|
||||
let t = Targets { tangent: true, perpendicular: true, ..Targets::for_drawing(&d, &[], None, false) };
|
||||
let (_, ov) = snap(&t, &Leave::segment(&c, Point::new(300.0, 160.0), false));
|
||||
assert_eq!(labels(&ov), ["perpendicular"]);
|
||||
let (_, ov) = snap(&t, &Leave::diagonal(Point::new(300.0, 160.0), false));
|
||||
assert!(!labels(&ov).contains(&"perpendicular"), "{ov:?}");
|
||||
// 70 short segments near the pointer, none square to the leave point, then the rect's side.
|
||||
let mut busy = t;
|
||||
let mut segments: Vec<(Rect, PathSeg)> = (0..70)
|
||||
.map(|i| {
|
||||
let y = 160.0 + f64::from(i) * 0.01;
|
||||
(Rect::new(199.0, y, 205.0, y + 0.001), PathSeg::Line(Line::new((199.0, y), (205.0, y + 0.001))))
|
||||
})
|
||||
.collect();
|
||||
segments.extend(busy.segments.iter().cloned());
|
||||
busy.segments = segments;
|
||||
let (_, ov) = snap(&busy, &Leave::segment(&c, Point::new(300.0, 160.0), false));
|
||||
assert_eq!(labels(&ov), ["perpendicular"]);
|
||||
}
|
||||
|
||||
/// The angle sets of Preferences › Smart Guides › Construction Guides.
|
||||
#[test]
|
||||
fn construction_angle_sets() {
|
||||
|
||||
@@ -252,6 +252,11 @@ pub struct ToolContext<'a> {
|
||||
/// points (a transform's reference point) land on anchors and ruler guides within
|
||||
/// [`Self::snap_tolerance`].
|
||||
pub snap_to_point: bool,
|
||||
/// View → Snapping → Snap to Tangent: with Smart Guides on, a line drawn
|
||||
/// from an anchor lands where it touches a nearby curve.
|
||||
pub snap_tangent: bool,
|
||||
/// View → Snapping → Snap to Perpendicular: likewise where it meets a nearby segment square.
|
||||
pub snap_perpendicular: bool,
|
||||
/// View → Snap to Glyph: drawn points snap to live-text baselines and glyph bounds (with Smart Guides).
|
||||
pub snap_to_glyph: bool,
|
||||
/// View → Show Corner Widget: paths show draggable Live Corners widgets in their corners.
|
||||
@@ -1003,6 +1008,8 @@ pub(crate) mod testutil {
|
||||
show_bbox: true,
|
||||
snap_to_pixel: false,
|
||||
snap_to_point: true,
|
||||
snap_tangent: true,
|
||||
snap_perpendicular: true,
|
||||
snap_to_glyph: false,
|
||||
corner_widgets: true,
|
||||
fill_active: true,
|
||||
|
||||
@@ -190,7 +190,10 @@ impl Tool for PenTool {
|
||||
}
|
||||
// The path being drawn moves its bounds as it grows: only its anchors and segments pull.
|
||||
let drawn = active.map(|a| a.0);
|
||||
let from = active.map(|(_, _, last, _)| Leave::segment(cx, last, ev.mods.shift));
|
||||
let from = active.map(|(_, _, last, handle)| {
|
||||
let leave = Leave::segment(cx, last, ev.mods.shift);
|
||||
if handle == last { leave } else { leave.without_contact() }
|
||||
});
|
||||
let p = match ev.kind {
|
||||
PointerKind::Move => self.snap.hover(cx, ev.pos, drawn.as_slice(), from.as_ref()),
|
||||
PointerKind::Down => self.snap.press(cx, ev.pos, drawn.as_slice(), from.as_ref()),
|
||||
|
||||
@@ -215,6 +215,35 @@ pub fn anchor_buttons(app: &mut VectorcraftApp, ui: &mut Ui, controls: AnchorCon
|
||||
/// The id of the Control bar's isolation button.
|
||||
const ISOLATION_BUTTON: &str = "cb-isolation";
|
||||
|
||||
/// The room the Control bar needs for the Snapping menu next to the Artboard tool's controls; with
|
||||
/// less, the menu is left out so those controls still fit the narrowest window.
|
||||
const ARTBOARD_SNAPPING_WIDTH: f32 = 900.0;
|
||||
|
||||
/// The Control bar's snapping quick access; every change uses its existing command.
|
||||
fn snapping_menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.menu_button(tl!("Snapping"), |ui| {
|
||||
for (label, cmd, on) in [
|
||||
(tl!("Smart Guides"), "view.smartGuides", app.ui.view.smart_guides),
|
||||
(tl!("Snap to Point"), "view.snapToPoint", app.ui.view.snap_to_point),
|
||||
(tl!("Snap to Grid"), "view.snapToGrid", app.ui.view.snap_to_grid),
|
||||
(tl!("Snap to Pixel"), "view.snapToPixel", app.ui.view.snap_to_pixel),
|
||||
] {
|
||||
if widgets::check(ui, label, on, true) {
|
||||
crate::menus::invoke(app, cmd, json!({}));
|
||||
}
|
||||
}
|
||||
ui.separator();
|
||||
for (key, label, on) in [
|
||||
("snapTangent", tl!("Snap to Tangent"), app.session.prefs.snap_tangent),
|
||||
("snapPerpendicular", tl!("Snap to Perpendicular"), app.session.prefs.snap_perpendicular),
|
||||
] {
|
||||
if widgets::check(ui, label, on, true) {
|
||||
crate::menus::invoke(app, "prefs.set", json!({"key": key, "value": !on}));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// The Control bar's isolation button. In isolation mode it is Exit Isolation Mode, except while a
|
||||
/// pattern is edited (the isolation bar's Done and Cancel end that). Otherwise, with one object
|
||||
/// selected that [`vectorcraft_tools::select::isolates`], it is Isolate Selected Object, which
|
||||
@@ -258,10 +287,15 @@ pub fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
for i in 0..5 {
|
||||
ui.painter().circle_filled(g.center_top() + vec2(0.0, 2.0 + i as f32 * 4.0), 0.9, t.text_disabled);
|
||||
}
|
||||
let Some(st) = app.session.active() else {
|
||||
if app.session.active().is_none() {
|
||||
ui.label(egui::RichText::new(tl!("No Document")).color(t.text_dim));
|
||||
return;
|
||||
};
|
||||
}
|
||||
// The Artboard tool's controls fill the narrowest window, so they keep it to themselves there.
|
||||
if app.session.tool_id() != "artboard" || ui.available_width() >= ARTBOARD_SNAPPING_WIDTH {
|
||||
snapping_menu(app, ui);
|
||||
}
|
||||
let Some(st) = app.session.active() else { return };
|
||||
let sel = st.selection.objects.clone();
|
||||
let units = app.session.general_unit();
|
||||
let first = sel.first().and_then(|id| st.doc.node(*id)).cloned();
|
||||
|
||||
@@ -5307,6 +5307,10 @@ theme Appearance Външен вид
|
||||
Persistent Постоянна
|
||||
Locate Library… Намиране на библиотека…
|
||||
Library Panel Options Опции на панела с библиотека
|
||||
Snapping Прилепване
|
||||
Snap to Tangent Прилепване към допирателна
|
||||
Snap to Perpendicular Прилепване към перпендикуляр
|
||||
@msg unknown snap option `{key}` (use {options}) неизвестна опция за прилепване `{key}` (използвайте {options})
|
||||
Gap Length: Дължина на пропуска:
|
||||
Artboard {n} of {total} Работна област {n} от {total}
|
||||
Encoding and writing the file… Кодиране и записване на файла…
|
||||
@@ -5357,6 +5361,7 @@ theme Appearance Външен вид
|
||||
Water Paper Мокра хартия
|
||||
Ruby… Руби…
|
||||
Ruby Руби
|
||||
Geometric Guides Геометрични водачи
|
||||
Ease In Плавно начало
|
||||
Ease Out Плавен край
|
||||
Ease In Out Плавно начало и край
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5352,6 +5352,10 @@ mark-style Japanese Japonské
|
||||
@msg too many library panels were opened in this session v této relaci bylo otevřeno příliš mnoho panelů knihoven
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz má {_1} × {_2} pixelů, na jedné straně více než {LARGE_TRACE_SIDE}: vektorizuje se, ale pomaleji. Rychleji se vektorizuje s předvolbou nižší věrnosti nebo menší kopie obrazu.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz byl vektorizován v rozlišení {_1} × {_2} pixelů, zmenšený z {_3} × {_4} (nejvýše {_5} megapixelů).
|
||||
Snapping Přichytávání
|
||||
Snap to Tangent Přichytávat k tečně
|
||||
Snap to Perpendicular Přichytávat ke kolmici
|
||||
@msg unknown snap option `{key}` (use {options}) neznámá možnost přichytávání `{key}` (použijte {options})
|
||||
Gap Length: Délka mezery:
|
||||
Artboard {n} of {total} Pracovní plocha {n} z {total}
|
||||
Encoding and writing the file… Kódování a zápis souboru…
|
||||
@@ -5402,6 +5406,7 @@ mark-style Japanese Japonské
|
||||
Water Paper Mokrý papír
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometrická vodítka
|
||||
Ease In Pozvolný začátek
|
||||
Ease Out Pozvolný konec
|
||||
Ease In Out Pozvolný začátek a konec
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5303,6 +5303,10 @@ swatch Bright Violet Kraftig violet
|
||||
Persistent Vedvarende
|
||||
Locate Library… Lokalisér bibliotek…
|
||||
Library Panel Options Indstillinger for bibliotekspanel
|
||||
Snapping Fastgørelse
|
||||
Snap to Tangent Fastgør til tangent
|
||||
Snap to Perpendicular Fastgør vinkelret
|
||||
@msg unknown snap option `{key}` (use {options}) ukendt fastgørelsesindstilling `{key}` (brug {options})
|
||||
Gap Length: Hullængde:
|
||||
Artboard {n} of {total} Tegneflade {n} af {total}
|
||||
Encoding and writing the file… Koder og skriver filen…
|
||||
@@ -5373,6 +5377,7 @@ swatch Bright Violet Kraftig violet
|
||||
Water Paper Vådt papir
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometriske hjælpelinjer
|
||||
Ease In Langsom start
|
||||
Ease Out Langsom slutning
|
||||
Ease In Out Langsom start og slutning
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4932,6 +4932,10 @@ effect Soft Weich
|
||||
@msg too many library panels were opened in this session in dieser Sitzung wurden zu viele Bibliotheksbedienfelder geöffnet
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Das Bild hat {_1} × {_2} Pixel, mehr als {LARGE_TRACE_SIDE} auf einer Seite: Es wird nachgezeichnet, aber langsamer. Eine Vorgabe mit geringerer Detailtreue oder eine kleinere Kopie des Bildes geht schneller.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Das Bild wurde mit {_1} × {_2} Pixeln nachgezeichnet, verkleinert von {_3} × {_4} (höchstens {_5} Megapixel).
|
||||
Snapping Einrasten
|
||||
Snap to Tangent An Tangente ausrichten
|
||||
Snap to Perpendicular An Senkrechter ausrichten
|
||||
@msg unknown snap option `{key}` (use {options}) unbekannte Einrastoption `{key}` (möglich: {options})
|
||||
Gap Length: Lückenlänge:
|
||||
Artboard {n} of {total} Zeichenfläche {n} von {total}
|
||||
Encoding and writing the file… Datei wird codiert und geschrieben…
|
||||
@@ -4983,6 +4987,7 @@ effect Soft Weich
|
||||
Water Paper Feuchtes Papier
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometrische Hilfslinien
|
||||
Ease In Langsam starten
|
||||
Ease Out Langsam enden
|
||||
Ease In Out Langsam starten und enden
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5304,6 +5304,10 @@ effect Points Σημεία
|
||||
@msg too many library panels were opened in this session άνοιξαν πάρα πολλοί πίνακες βιβλιοθηκών σε αυτή τη συνεδρία
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Η εικόνα είναι {_1} × {_2} εικονοστοιχεία, πάνω από {LARGE_TRACE_SIDE} σε μία πλευρά: ανιχνεύεται, αλλά πιο αργά. Μια προρύθμιση χαμηλότερης πιστότητας ή ένα μικρότερο αντίγραφο της εικόνας ανιχνεύεται γρηγορότερα.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Η εικόνα ανιχνεύτηκε σε {_1} × {_2} εικονοστοιχεία, σμικρυμένη από {_3} × {_4} (το πολύ {_5} megapixel).
|
||||
Snapping Προσκόλληση
|
||||
Snap to Tangent Προσκόλληση σε εφαπτομένη
|
||||
Snap to Perpendicular Προσκόλληση σε κάθετο
|
||||
@msg unknown snap option `{key}` (use {options}) άγνωστη επιλογή προσκόλλησης `{key}` (χρησιμοποιήστε {options})
|
||||
Gap Length: Μήκος κενού:
|
||||
Artboard {n} of {total} Πίνακας σχεδίασης {n} από {total}
|
||||
Encoding and writing the file… Κωδικοποίηση και εγγραφή του αρχείου…
|
||||
@@ -5354,6 +5358,7 @@ effect Points Σημεία
|
||||
Water Paper Βρεγμένο χαρτί
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Γεωμετρικοί οδηγοί
|
||||
Ease In Ομαλή αρχή
|
||||
Ease Out Ομαλό τέλος
|
||||
Ease In Out Ομαλή αρχή και τέλος
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2045 and column 19.
|
@@ -5294,6 +5294,10 @@ mark-style Japanese Japonesas
|
||||
@msg too many library panels were opened in this session se abrieron demasiados paneles de biblioteca en esta sesión
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. La imagen tiene {_1} × {_2} píxeles, más de {LARGE_TRACE_SIDE} en un lado: se calcará, pero más despacio. Un ajuste preestablecido de menor fidelidad o una copia más pequeña de la imagen se calcan más rápido.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). La imagen se calcó a {_1} × {_2} píxeles, reducida desde {_3} × {_4} ({_5} megapíxeles como máximo).
|
||||
Snapping Ajuste
|
||||
Snap to Tangent Ajustar a tangente
|
||||
Snap to Perpendicular Ajustar a perpendicular
|
||||
@msg unknown snap option `{key}` (use {options}) opción de ajuste desconocida `{key}` (use {options})
|
||||
Gap Length: Longitud del hueco:
|
||||
Artboard {n} of {total} Mesa de trabajo {n} de {total}
|
||||
Encoding and writing the file… Codificando y escribiendo el archivo…
|
||||
@@ -5344,6 +5348,7 @@ mark-style Japanese Japonesas
|
||||
Water Paper Papel mojado
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Guías geométricas
|
||||
Ease In Entrada suave
|
||||
Ease Out Salida suave
|
||||
Ease In Out Entrada y salida suaves
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5316,6 +5316,10 @@ mark-style Japanese Japanilainen
|
||||
@msg too many library panels were opened in this session tässä istunnossa on avattu liian monta kirjastopaneelia
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Kuva on {_1} × {_2} kuvapistettä, yhdeltä sivultaan yli {LARGE_TRACE_SIDE}: se jäljitetään, mutta hitaammin. Pienemmän tarkkuuden esiasetus tai kuvan pienempi kopio jäljittyy nopeammin.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Kuva jäljitettiin kokoon {_1} × {_2} kuvapistettä pienennettynä koosta {_3} × {_4} (enintään {_5} megapikseliä).
|
||||
Snapping Tartunta
|
||||
Snap to Tangent Kohdista tangenttiin
|
||||
Snap to Perpendicular Kohdista kohtisuoraan
|
||||
@msg unknown snap option `{key}` (use {options}) tuntematon tartuntavalinta `{key}` (käytä: {options})
|
||||
Gap Length: Aukon pituus:
|
||||
Artboard {n} of {total} Piirtoalusta {n}/{total}
|
||||
Encoding and writing the file… Tiedostoa koodataan ja kirjoitetaan…
|
||||
@@ -5433,6 +5437,7 @@ swatch Bright Violet Kirkas violetti
|
||||
Water Paper Märkä paperi
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometriset apuviivat
|
||||
Ease In Hidas alku
|
||||
Ease Out Hidas loppu
|
||||
Ease In Out Hidas alku ja loppu
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5351,6 +5351,10 @@ theme Appearance Apparence
|
||||
@msg too many library panels were opened in this session trop de panneaux de bibliothèque ont été ouverts dans cette session
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’image fait {_1} × {_2} pixels, plus de {LARGE_TRACE_SIDE} sur un côté : elle est vectorisée, mais plus lentement. Un paramètre prédéfini moins fidèle ou une copie plus petite de l’image se vectorise plus vite.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’image a été vectorisée à {_1} × {_2} pixels, réduite de {_3} × {_4} ({_5} mégapixels au plus).
|
||||
Snapping Magnétisme
|
||||
Snap to Tangent Magnétisme de la tangente
|
||||
Snap to Perpendicular Magnétisme de la perpendiculaire
|
||||
@msg unknown snap option `{key}` (use {options}) option de magnétisme inconnue `{key}` (utilisez {options})
|
||||
Gap Length: Longueur de lacune :
|
||||
Artboard {n} of {total} Plan de travail {n} sur {total}
|
||||
Encoding and writing the file… Encodage et écriture du fichier…
|
||||
@@ -5401,6 +5405,7 @@ theme Appearance Apparence
|
||||
Water Paper Papier humide
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Repères géométriques
|
||||
Ease In Accélération progressive
|
||||
Ease Out Décélération progressive
|
||||
Ease In Out Accélération et décélération
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5338,6 +5338,10 @@ imageTrace Outline Körvonal
|
||||
@msg too many library panels were opened in this session ebben a munkamenetben túl sok könyvtárpanel nyílt meg
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A kép {_1} × {_2} képpont, egyik oldalán több mint {LARGE_TRACE_SIDE}: a vektorizálás megtörténik, de lassabban. Kisebb hűségű előbeállítással vagy a kép kisebb másolatával gyorsabb.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A kép vektorizálása {_1} × {_2} képponton történt, {_3} × {_4} méretről kicsinyítve (legfeljebb {_5} megapixel).
|
||||
Snapping Illesztés
|
||||
Snap to Tangent Illesztés érintőre
|
||||
Snap to Perpendicular Illesztés merőlegesre
|
||||
@msg unknown snap option `{key}` (use {options}) ismeretlen illesztési beállítás: `{key}` (használható: {options})
|
||||
Gap Length: Rés hossza:
|
||||
Artboard {n} of {total} Rajztábla: {n} / {total}
|
||||
Encoding and writing the file… A fájl kódolása és írása…
|
||||
@@ -5388,6 +5392,7 @@ imageTrace Outline Körvonal
|
||||
Water Paper Nedves papír
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometriai segédvonalak
|
||||
Ease In Lassú kezdés
|
||||
Ease Out Lassú befejezés
|
||||
Ease In Out Lassú kezdés és befejezés
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5303,6 +5303,10 @@ swatch Bright Violet Violet cerah
|
||||
Persistent Persisten
|
||||
Locate Library… Temukan pustaka…
|
||||
Library Panel Options Opsi panel pustaka
|
||||
Snapping Snap
|
||||
Snap to Tangent Snap ke garis singgung
|
||||
Snap to Perpendicular Snap tegak lurus
|
||||
@msg unknown snap option `{key}` (use {options}) opsi snap `{key}` tidak dikenal (gunakan {options})
|
||||
Gap Length: Panjang celah:
|
||||
Artboard {n} of {total} Artboard {n} dari {total}
|
||||
Encoding and writing the file… Mengodekan dan menulis berkas…
|
||||
@@ -5373,6 +5377,7 @@ swatch Bright Violet Violet cerah
|
||||
Water Paper Kertas Basah
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Panduan Geometris
|
||||
Ease In Awal Lambat
|
||||
Ease Out Akhir Lambat
|
||||
Ease In Out Awal dan Akhir Lambat
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3816 and column 19.
|
@@ -4966,6 +4966,10 @@ lighting Light Luce
|
||||
@msg too many library panels were opened in this session in questa sessione sono stati aperti troppi pannelli libreria
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’immagine è di {_1} × {_2} pixel, più di {LARGE_TRACE_SIDE} su un lato: viene vettorializzata, ma più lentamente. Un predefinito a fedeltà più bassa o una copia più piccola dell’immagine è più veloce.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’immagine è stata vettorializzata a {_1} × {_2} pixel, ridotta da {_3} × {_4} (al massimo {_5} megapixel).
|
||||
Snapping Aggancio
|
||||
Snap to Tangent Aggancia alla tangente
|
||||
Snap to Perpendicular Aggancia alla perpendicolare
|
||||
@msg unknown snap option `{key}` (use {options}) opzione di aggancio sconosciuta `{key}` (usare {options})
|
||||
Gap Length: Lunghezza lacuna:
|
||||
Artboard {n} of {total} Tavola da disegno {n} di {total}
|
||||
Encoding and writing the file… Codifica e scrittura del file…
|
||||
@@ -5017,6 +5021,7 @@ lighting Light Luce
|
||||
Water Paper Carta bagnata
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Guide geometriche
|
||||
Ease In Inizio graduale
|
||||
Ease Out Fine graduale
|
||||
Ease In Out Inizio e fine graduali
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5006,6 +5006,10 @@ lighting Light ライト
|
||||
@msg too many library panels were opened in this session このセッションで開いたライブラリパネルが多すぎます
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 画像は {_1} × {_2} ピクセルで、一辺が {LARGE_TRACE_SIDE} を超えています。トレースはできますが、時間がかかります。忠実度の低いプリセットや画像の小さいコピーを使うと速くなります。
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 画像は {_3} × {_4} から縮小した {_1} × {_2} ピクセルでトレースされました (最大 {_5} メガピクセル)。
|
||||
Snapping スナップ
|
||||
Snap to Tangent 接線にスナップ
|
||||
Snap to Perpendicular 垂線にスナップ
|
||||
@msg unknown snap option `{key}` (use {options}) 不明なスナップオプション `{key}` ({options} を使用してください)
|
||||
Gap Length: ギャップの長さ:
|
||||
Artboard {n} of {total} アートボード {n} / {total}
|
||||
Encoding and writing the file… ファイルをエンコードして書き込み中…
|
||||
@@ -5064,6 +5068,7 @@ lighting Light ライト
|
||||
Yonroku-ban 四六判
|
||||
Ruby… ルビ…
|
||||
Ruby ルビ
|
||||
Geometric Guides 幾何ガイド
|
||||
Ease In イーズイン
|
||||
Ease Out イーズアウト
|
||||
Ease In Out イーズインアウト
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5257,6 +5257,10 @@ imageTrace Outline კონტური
|
||||
# Select by Color (select.color)
|
||||
Select by Color მონიშვნა ფერის მიხედვით
|
||||
@msg thumbnail: no page {n} (the file has 1) thumbnail: გვერდი {n} არ არსებობს (ფაილს 1 გვერდი აქვს)
|
||||
Snapping მიბმა
|
||||
Snap to Tangent მხებზე მიბმა
|
||||
Snap to Perpendicular პერპენდიკულარზე მიბმა
|
||||
@msg unknown snap option `{key}` (use {options}) უცნობი მიბმის პარამეტრი `{key}` (გამოიყენეთ: {options})
|
||||
Gap Length: ღრეჩოს სიგრძე:
|
||||
Smudge Stick… დასაზელი ჯოხი…
|
||||
Smudge Stick დასაზელი ჯოხი
|
||||
@@ -5307,6 +5311,7 @@ imageTrace Outline კონტური
|
||||
selects several, or draws one inside an artboard) რამდენიმეს მონიშნავს ან ახალს სამუშაო დაფის შიგნით ხატავს)
|
||||
Ruby… რუბი…
|
||||
Ruby რუბი
|
||||
Geometric Guides გეომეტრიული მიმმართველები
|
||||
Ease In ნელი დასაწყისი
|
||||
Ease Out ნელი დასასრული
|
||||
Ease In Out ნელი დასაწყისი და დასასრული
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4929,6 +4929,10 @@ lighting Light 광원
|
||||
@msg too many library panels were opened in this session 이 세션에서 라이브러리 패널을 너무 많이 열었습니다
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 이미지가 {_1} × {_2}픽셀로 한 변이 {LARGE_TRACE_SIDE}을 넘습니다. 추적은 되지만 더 느립니다. 충실도가 낮은 사전 설정이나 이미지의 작은 사본을 사용하면 더 빠릅니다.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 이미지를 {_3} × {_4}에서 축소한 {_1} × {_2}픽셀로 추적했습니다(최대 {_5}메가픽셀).
|
||||
Snapping 스냅
|
||||
Snap to Tangent 접선에 스냅
|
||||
Snap to Perpendicular 수직선에 스냅
|
||||
@msg unknown snap option `{key}` (use {options}) 알 수 없는 스냅 옵션 `{key}` ({options} 중에서 사용하세요)
|
||||
Gap Length: 간격 길이:
|
||||
Artboard {n} of {total} 아트보드 {n} / {total}
|
||||
Encoding and writing the file… 파일을 인코딩하고 쓰는 중…
|
||||
@@ -4980,6 +4984,7 @@ lighting Light 광원
|
||||
Water Paper 물 종이
|
||||
Ruby… 루비…
|
||||
Ruby 루비
|
||||
Geometric Guides 기하학적 안내선
|
||||
Ease In 서서히 시작
|
||||
Ease Out 서서히 종료
|
||||
Ease In Out 서서히 시작 및 종료
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5313,6 +5313,10 @@ swatch Bright Violet Felviolet
|
||||
Typing Typen
|
||||
Persistent Permanent
|
||||
Library Panel Options Opties voor bibliotheekpaneel
|
||||
Snapping Uitlijnen
|
||||
Snap to Tangent Uitlijnen op raaklijn
|
||||
Snap to Perpendicular Uitlijnen op loodlijn
|
||||
@msg unknown snap option `{key}` (use {options}) onbekende uitlijnoptie `{key}` (gebruik {options})
|
||||
Gap Length: Lengte van opening:
|
||||
Locate Library… Bibliotheek lokaliseren…
|
||||
|
||||
@@ -5416,6 +5420,7 @@ swatch Bright Violet Felviolet
|
||||
Water Paper Nat papier
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometrische hulplijnen
|
||||
Ease In Langzaam beginnen
|
||||
Ease Out Langzaam eindigen
|
||||
Ease In Out Langzaam beginnen en eindigen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5204,6 +5204,10 @@ mark-style Japanese Japońskie
|
||||
@msg too many library panels were opened in this session w tej sesji otwarto zbyt wiele paneli bibliotek
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz ma {_1} × {_2} pikseli, ponad {LARGE_TRACE_SIDE} na jednym boku: zostanie obrysowany, ale wolniej. Ustawienie wstępne o niższej wierności lub mniejsza kopia obrazu obrysuje się szybciej.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz obrysowano w rozdzielczości {_1} × {_2} pikseli, po zmniejszeniu z {_3} × {_4} (najwyżej {_5} megapikseli).
|
||||
Snapping Przyciąganie
|
||||
Snap to Tangent Przyciągaj do stycznych
|
||||
Snap to Perpendicular Przyciągaj do prostopadłych
|
||||
@msg unknown snap option `{key}` (use {options}) nieznana opcja przyciągania `{key}` (użyj: {options})
|
||||
Gap Length: Długość przerwy:
|
||||
Artboard {n} of {total} Obszar kompozycji {n} z {total}
|
||||
Encoding and writing the file… Kodowanie i zapisywanie pliku…
|
||||
@@ -5254,6 +5258,7 @@ mark-style Japanese Japońskie
|
||||
Water Paper Mokry papier
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Prowadnice geometryczne
|
||||
Ease In Łagodny początek
|
||||
Ease Out Łagodny koniec
|
||||
Ease In Out Łagodny początek i koniec
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5269,6 +5269,10 @@ mark-style Japanese Japonesas
|
||||
@msg too many library panels were opened in this session foram abertos painéis de biblioteca demais nesta sessão
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A imagem tem {_1} × {_2} pixels, mais de {LARGE_TRACE_SIDE} em um lado: ela é traçada, mas mais devagar. Uma predefinição de menor fidelidade ou uma cópia menor da imagem traça mais rápido.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A imagem foi traçada em {_1} × {_2} pixels, reduzida de {_3} × {_4} (no máximo {_5} megapixels).
|
||||
Snapping Ajuste
|
||||
Snap to Tangent Ajustar à Tangente
|
||||
Snap to Perpendicular Ajustar à Perpendicular
|
||||
@msg unknown snap option `{key}` (use {options}) opção de ajuste desconhecida `{key}` (use {options})
|
||||
Gap Length: Comprimento da Lacuna:
|
||||
Artboard {n} of {total} Mesa de Trabalho {n} de {total}
|
||||
Encoding and writing the file… Codificando e gravando o arquivo…
|
||||
@@ -5319,6 +5323,7 @@ mark-style Japanese Japonesas
|
||||
Water Paper Papel molhado
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Guias geométricas
|
||||
Ease In Entrada suave
|
||||
Ease Out Saída suave
|
||||
Ease In Out Entrada e saída suaves
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4962,6 +4962,10 @@ lighting Light Свет
|
||||
@msg too many library panels were opened in this session в этом сеансе открыто слишком много панелей библиотек
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Изображение {_1} × {_2} пикселей, больше {LARGE_TRACE_SIDE} по одной стороне: трассировка выполняется, но медленнее. Стиль с меньшей точностью или уменьшенная копия изображения трассируются быстрее.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Изображение трассировано в размере {_1} × {_2} пикселей, уменьшенное с {_3} × {_4} (не более {_5} мегапикселей).
|
||||
Snapping Привязка
|
||||
Snap to Tangent Привязать к касательной
|
||||
Snap to Perpendicular Привязать к перпендикуляру
|
||||
@msg unknown snap option `{key}` (use {options}) неизвестный параметр привязки `{key}` (допустимо: {options})
|
||||
Gap Length: Длина зазора:
|
||||
Artboard {n} of {total} Монтажная область {n} из {total}
|
||||
Encoding and writing the file… Кодирование и запись файла…
|
||||
@@ -5013,6 +5017,7 @@ lighting Light Свет
|
||||
Water Paper Мокрая бумага
|
||||
Ruby… Руби…
|
||||
Ruby Руби
|
||||
Geometric Guides Геометрические направляющие
|
||||
Ease In Плавное начало
|
||||
Ease Out Плавное окончание
|
||||
Ease In Out Плавное начало и окончание
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5308,6 +5308,10 @@ theme Appearance Utseende
|
||||
@msg too many library panels were opened in this session för många bibliotekspaneler har öppnats i den här sessionen
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Bilden är {_1} × {_2} pixlar, mer än {LARGE_TRACE_SIDE} på en sida: den vektoriseras, men långsammare. En förinställning med lägre återgivning eller en mindre kopia av bilden går snabbare.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Bilden vektoriserades vid {_1} × {_2} pixlar, nedskalad från {_3} × {_4} (högst {_5} megapixlar).
|
||||
Snapping Fästning
|
||||
Snap to Tangent Fäst mot tangent
|
||||
Snap to Perpendicular Fäst mot vinkelrät
|
||||
@msg unknown snap option `{key}` (use {options}) okänt fästalternativ `{key}` (använd {options})
|
||||
Gap Length: Lucklängd:
|
||||
Artboard {n} of {total} Rityta {n} av {total}
|
||||
Encoding and writing the file… Kodar och skriver filen…
|
||||
@@ -5358,6 +5362,7 @@ theme Appearance Utseende
|
||||
Water Paper Vått papper
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Geometriska stödlinjer
|
||||
Ease In Långsam start
|
||||
Ease Out Långsamt slut
|
||||
Ease In Out Långsam start och slut
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -5399,4 +5399,8 @@ axis Y Axis Y ekseni
|
||||
@msg `dither` applies to gradient fills (stroke: false) `dither` degrade dolgulara uygulanır (stroke: false)
|
||||
@msg `dither` applies to gradient fills (paint.setFill) `dither` degrade dolgulara uygulanır (paint.setFill)
|
||||
@msg `dither` must be true or false "dither" doğru veya yanlış olmalıdır
|
||||
Snapping Yaslama
|
||||
Snap to Tangent Teğete Yasla
|
||||
Snap to Perpendicular Dikeye Yasla
|
||||
Geometric Guides Geometrik Kılavuzlar
|
||||
@msg `method` must be classic or perceptual `method` classic veya perceptual olmalıdır
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -4946,6 +4946,10 @@ lighting Light Світло
|
||||
@msg too many library panels were opened in this session у цьому сеансі відкрито забагато панелей бібліотек
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Зображення {_1} × {_2} пікселів, понад {LARGE_TRACE_SIDE} з одного боку: трасування виконується, але повільніше. Стиль із нижчою точністю або менша копія зображення трасуються швидше.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Зображення трасовано в розмірі {_1} × {_2} пікселів, зменшене з {_3} × {_4} (щонайбільше {_5} мегапікселів).
|
||||
Snapping Прив’язка
|
||||
Snap to Tangent Прив’язати до дотичної
|
||||
Snap to Perpendicular Прив’язати до перпендикуляра
|
||||
@msg unknown snap option `{key}` (use {options}) невідомий параметр прив’язки `{key}` (використовуйте {options})
|
||||
Gap Length: Довжина проміжку:
|
||||
Artboard {n} of {total} Монтажна область {n} з {total}
|
||||
Encoding and writing the file… Кодування та запис файлу…
|
||||
@@ -4997,6 +5001,7 @@ lighting Light Світло
|
||||
Water Paper Мокрий папір
|
||||
Ruby… Рубі…
|
||||
Ruby Рубі
|
||||
Geometric Guides Геометричні напрямні
|
||||
Ease In Плавний початок
|
||||
Ease Out Плавне завершення
|
||||
Ease In Out Плавний початок і завершення
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 733 and column 40.
|
@@ -4946,6 +4946,10 @@ theme Appearance Giao diện
|
||||
@msg too many library panels were opened in this session đã mở quá nhiều bảng thư viện trong phiên này
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Ảnh có kích thước {_1} × {_2} điểm ảnh, hơn {LARGE_TRACE_SIDE} ở một cạnh: ảnh vẫn được dò, nhưng chậm hơn. Một thiết lập sẵn có độ trung thực thấp hơn hoặc một bản sao nhỏ hơn của ảnh sẽ dò nhanh hơn.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Ảnh đã được dò ở {_1} × {_2} điểm ảnh, thu nhỏ từ {_3} × {_4} (tối đa {_5} megapixel).
|
||||
Snapping Hút
|
||||
Snap to Tangent Hút vào tiếp tuyến
|
||||
Snap to Perpendicular Hút vào đường vuông góc
|
||||
@msg unknown snap option `{key}` (use {options}) tùy chọn hút không hợp lệ `{key}` (dùng {options})
|
||||
Gap Length: Độ dài khoảng hở:
|
||||
Artboard {n} of {total} Bảng vẽ {n} / {total}
|
||||
Encoding and writing the file… Đang mã hóa và ghi tệp…
|
||||
@@ -4997,6 +5001,7 @@ theme Appearance Giao diện
|
||||
Water Paper Giấy ướt
|
||||
Ruby… Ruby…
|
||||
Ruby Ruby
|
||||
Geometric Guides Đường dẫn hình học
|
||||
Ease In Chậm lúc đầu
|
||||
Ease Out Chậm lúc cuối
|
||||
Ease In Out Chậm lúc đầu và cuối
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4030 and column 10.
|
@@ -5185,6 +5185,10 @@ lighting Light 光源
|
||||
@msg too many library panels were opened in this session 本次会话中打开的库面板过多
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 图像为 {_1} × {_2} 像素,某一边超过 {LARGE_TRACE_SIDE}:会进行描摹,但速度较慢。使用保真度较低的预设或图像的较小副本会更快。
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 图像已按 {_1} × {_2} 像素描摹,由 {_3} × {_4} 缩小而来(最多 {_5} 百万像素)。
|
||||
Snapping 对齐
|
||||
Snap to Tangent 对齐切线
|
||||
Snap to Perpendicular 对齐垂线
|
||||
@msg unknown snap option `{key}` (use {options}) 未知的对齐选项 `{key}`(可用:{options})
|
||||
Gap Length: 间隙长度:
|
||||
# Links and Attributes: files and URLs handed to other apps.
|
||||
@msg Edit Original opens image and document files only; “{name}” is not one 编辑原稿只能打开图像和文档文件;“{name}” 不是其中之一
|
||||
@@ -5241,6 +5245,7 @@ lighting Light 光源
|
||||
Water Paper 水彩画纸
|
||||
Ruby… 注音…
|
||||
Ruby 注音
|
||||
Geometric Guides 几何参考线
|
||||
Ease In 缓入
|
||||
Ease Out 缓出
|
||||
Ease In Out 缓入缓出
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4980,6 +4980,10 @@ lighting Light 光源
|
||||
Persistent 持久
|
||||
Locate Library… 尋找資料庫…
|
||||
Library Panel Options 資料庫面板選項
|
||||
Snapping 靠齊
|
||||
Snap to Tangent 靠齊切線
|
||||
Snap to Perpendicular 靠齊垂線
|
||||
@msg unknown snap option `{key}` (use {options}) 未知的靠齊選項 `{key}`(可用:{options})
|
||||
Gap Length: 間隙長度:
|
||||
Artboard {n} of {total} 工作區域 {n} / {total}
|
||||
Encoding and writing the file… 正在編碼並寫入檔案…
|
||||
@@ -5036,6 +5040,7 @@ lighting Light 光源
|
||||
Water Paper 水彩畫紙
|
||||
Ruby… 注音…
|
||||
Ruby 注音
|
||||
Geometric Guides 幾何參考線
|
||||
Ease In 緩入
|
||||
Ease Out 緩出
|
||||
Ease In Out 緩入緩出
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1372,11 +1372,14 @@ mod tests {
|
||||
bar_frame_in(&mut app, &ctx, vec![], width);
|
||||
let texts = bar_frame_in(&mut app, &ctx, vec![], width);
|
||||
assert!(has(&texts, "Scale Artwork with Artboard") && !has(&texts, "X:") && !has(&texts, "W:"), "{texts:?}");
|
||||
// The Snapping menu sits first on a wide bar and leaves the narrow bar to the artboard controls.
|
||||
assert!(has(&bar_texts(&mut app, &ctx), "Snapping"));
|
||||
// In the app's narrowest window, 800 points wide, the name and the Transform link still fit,
|
||||
// and a click on the link shows the artboard's X, Y, W and H in the Transform panel's popover.
|
||||
let narrow = |app: &mut VectorcraftApp, events| bar_frame_in(app, &ctx, events, 800.0);
|
||||
narrow(&mut app, vec![]);
|
||||
let texts = narrow(&mut app, vec![]);
|
||||
assert!(!has(&texts, "Snapping"), "{texts:?}");
|
||||
let link = text_rect(&texts, "Transform");
|
||||
assert!(link.right() < 790.0 && text_rect(&texts, "Artboard 1").right() < link.left(), "{texts:?}");
|
||||
// Move and Scale Artwork with Artboard show as their boxes alone, inside the window, and the
|
||||
|
||||
@@ -423,6 +423,20 @@ mod tests {
|
||||
use crate::Services;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
#[test]
|
||||
fn geometric_snapping_is_a_saved_smart_guides_preference() {
|
||||
let mut a = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
open(&mut a, Some("Smart Guides"));
|
||||
let text = crate::tests_labels::painted_text(&mut a, |a, ui| show(a, ui.ctx()));
|
||||
assert!(text.contains("Geometric Guides") && text.contains("Snap to Perpendicular") && text.contains("Snap to Tangent"), "{text}");
|
||||
a.ui.dialog.as_mut().unwrap().fields.insert("snapPerpendicular".into(), json!(false));
|
||||
confirm(&mut a).unwrap();
|
||||
assert!(!a.session.prefs.snap_perpendicular && a.session.prefs.snap_tangent);
|
||||
let saved: Prefs = serde_json::from_value(a.ui.engine_prefs.clone()).unwrap();
|
||||
assert!(!saved.snap_perpendicular && saved.snap_tangent);
|
||||
assert!(a.run("view.snapping", json!({})).is_err());
|
||||
}
|
||||
|
||||
/// A resting checkbox must have a visible outline in both states and every brightness.
|
||||
#[test]
|
||||
fn preference_checkboxes_draw_outlines_without_hover() {
|
||||
|
||||
+8
-1
File diff suppressed because one or more lines are too long
+3
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5
-3
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user