mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
G1: show distance and angle on Pen, Line and Curvature rubber bands
Measurement labels while drawing now match the Measure tool (D, angle, dX/dY) when the preference is on. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Cursor
parent
d41af02637
commit
5bf159d5d1
@@ -598,6 +598,12 @@ impl Tool for CurvatureTool {
|
||||
curvature_extend(&mut tail, false, h, self.how(&e), false);
|
||||
tail.to_bezpath_into(&mut bp);
|
||||
o.push(Overlay::Path { path: bp, color: FEEDBACK, width: 1.0, dashed: false });
|
||||
if cx.measurement_labels && e.p.distance(h) > 1e-9 {
|
||||
o.push(Overlay::Measure {
|
||||
p: h + Vec2::new(cx.tol(12.0), cx.tol(12.0)),
|
||||
text: cx.segment_measure_label(e.p, h),
|
||||
});
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
@@ -609,6 +615,15 @@ impl Tool for CurvatureTool {
|
||||
pts.push((h, false));
|
||||
curvature_spline(&pts, false).to_bezpath_into(&mut bp);
|
||||
o.push(Overlay::Path { path: bp, color: FEEDBACK, width: 1.0, dashed: false });
|
||||
if let Some((from, _)) = self.pts.last()
|
||||
&& cx.measurement_labels
|
||||
&& from.distance(h) > 1e-9
|
||||
{
|
||||
o.push(Overlay::Measure {
|
||||
p: h + Vec2::new(cx.tol(12.0), cx.tol(12.0)),
|
||||
text: cx.segment_measure_label(*from, h),
|
||||
});
|
||||
}
|
||||
}
|
||||
for (i, (p, _)) in self.pts.iter().enumerate() {
|
||||
o.push(Overlay::CurvePoint { p: *p, color: FEEDBACK, filled: Some(i) == self.current, size: 6.0 });
|
||||
@@ -1033,7 +1048,11 @@ mod tests {
|
||||
let s = selected(id, Some(5));
|
||||
let cx = quiet(&d, &s, &p);
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Move, 650.0, 60.0));
|
||||
assert!(t.overlays(&cx).iter().any(|o| matches!(o, Overlay::Path { .. })), "the rubber band shows");
|
||||
let ovs = t.overlays(&cx);
|
||||
assert!(ovs.iter().any(|o| matches!(o, Overlay::Path { .. })), "the rubber band shows");
|
||||
let from = pt(550.0, 120.0);
|
||||
let to = pt(650.0, 60.0);
|
||||
assert!(ovs.iter().any(|o| matches!(o, Overlay::Measure { text, .. } if text == &cx.segment_measure_label(from, to))));
|
||||
let a = press(&mut t, &cx, 650.0, 60.0, true);
|
||||
assert!(matches!(edit_op(&a[0]), Some(("extend", v)) if v["from"] == "smooth"), "{a:?}");
|
||||
curvature_extend(&mut sp, false, pt(650.0, 60.0), EndCurve::Smooth, false);
|
||||
|
||||
@@ -380,6 +380,11 @@ impl ToolContext<'_> {
|
||||
pub fn offset_label(&self, dx: f64, dy: f64) -> String {
|
||||
format!("dX: {}\ndY: {}", self.len(dx), self.len(dy))
|
||||
}
|
||||
/// A segment label while drawing: distance, angle and offsets between two points.
|
||||
pub fn segment_measure_label(&self, a: Point, b: Point) -> String {
|
||||
let (dist, ang, dx, dy) = xform::measure(a, b);
|
||||
format!("D: {}\nAngle: {ang:.1}°\n{}", self.len(dist), self.offset_label(dx, dy))
|
||||
}
|
||||
/// What Snap to Grid snaps to: the grid's subdivisions (or its lines without any).
|
||||
pub fn grid_step(&self) -> f64 {
|
||||
self.doc.grid.spacing / self.doc.grid.subdivisions.max(1) as f64
|
||||
|
||||
+10
-1
@@ -27,7 +27,7 @@
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_doc::{NodeId, NodeKind};
|
||||
use vectorcraft_geom::{BezPath, Point};
|
||||
use vectorcraft_geom::{BezPath, Point, Vec2};
|
||||
|
||||
use crate::direct::hit_handle;
|
||||
use crate::draw2::{AnchorTool, editable_paths};
|
||||
@@ -396,6 +396,12 @@ impl Tool for PenTool {
|
||||
bp.line_to(h);
|
||||
}
|
||||
o.push(Overlay::Path { path: bp, color: cx.doc.layer_color(id), width: 1.0, dashed: false });
|
||||
if cx.measurement_labels && last.distance(h) > 1e-9 {
|
||||
o.push(Overlay::Measure {
|
||||
p: h + Vec2::new(cx.tol(12.0), cx.tol(12.0)),
|
||||
text: cx.segment_measure_label(last, h),
|
||||
});
|
||||
}
|
||||
}
|
||||
o.extend_from_slice(self.snap.guides());
|
||||
o
|
||||
@@ -1206,6 +1212,9 @@ mod tests {
|
||||
assert!(o.iter().any(
|
||||
|o| matches!(o, Overlay::Path { path, .. } if path.elements().last().and_then(|e| e.end_point()) == Some(Point::new(150.0, 318.0)))
|
||||
));
|
||||
let from = Point::new(60.0, 300.0);
|
||||
let to = Point::new(150.0, 318.0);
|
||||
assert!(o.iter().any(|o| matches!(o, Overlay::Measure { text, .. } if text == &cx.segment_measure_label(from, to))));
|
||||
let a = click(&mut t, &cx, 152.0, 318.0, Mods::default());
|
||||
assert_eq!(a[1], Action::Preview("path.appendAnchor".into(), json!({"id": id.0, "x": 150.0, "y": 318.0})));
|
||||
let shift = Mods { shift: true, ..Mods::default() };
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! the dragged corner snap to Smart Guides ([`DrawSnap`]), hovering before the press too.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_geom::{Point, Rect};
|
||||
use vectorcraft_geom::{Point, Rect, Vec2};
|
||||
|
||||
use crate::guides::{DrawSnap, Leave, square};
|
||||
use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, ToolContext, ToolKey};
|
||||
@@ -251,15 +251,21 @@ impl Tool for ShapeTool {
|
||||
fn overlays(&self, cx: &ToolContext) -> Vec<Overlay> {
|
||||
let mut o = self.snap.guides().to_vec();
|
||||
if let Some(s) = self.start.filter(|_| self.began && cx.measurement_labels) {
|
||||
// A box's size as drawn (from its centre with Alt, square with Shift).
|
||||
let (w, h) = match self.id {
|
||||
"polygon" | "star" | "lineSegment" => ((self.last - s).x.abs(), (self.last - s).y.abs()),
|
||||
_ => {
|
||||
let r = drag_rect(s, self.last, self.mods);
|
||||
(r.width(), r.height())
|
||||
}
|
||||
let p = self.last + Vec2::new(cx.tol(12.0), cx.tol(12.0));
|
||||
let text = if self.id == "lineSegment" {
|
||||
cx.segment_measure_label(s, self.last)
|
||||
} else {
|
||||
// A box's size as drawn (from its centre with Alt, square with Shift).
|
||||
let (w, h) = match self.id {
|
||||
"polygon" | "star" => ((self.last - s).x.abs(), (self.last - s).y.abs()),
|
||||
_ => {
|
||||
let r = drag_rect(s, self.last, self.mods);
|
||||
(r.width(), r.height())
|
||||
}
|
||||
};
|
||||
cx.size_label(w, h)
|
||||
};
|
||||
o.push(Overlay::Measure { p: self.last, text: cx.size_label(w, h) });
|
||||
o.push(Overlay::Measure { p, text });
|
||||
}
|
||||
o
|
||||
}
|
||||
@@ -422,5 +428,8 @@ mod tests {
|
||||
let shift = Mods { shift: true, ..Mods::default() };
|
||||
let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 203.0, 302.0).with_mods(shift));
|
||||
assert!(matches!(&a[..], [_, Action::Preview(_, v)] if v["x2"] == 200.0 && (v["y2"].as_f64().unwrap() - 300.0).abs() < 1e-9), "{a:?}");
|
||||
let from = Point::new(300.0, 300.0);
|
||||
let to = Point::new(200.0, 300.0);
|
||||
assert!(t.overlays(&cx).iter().any(|o| matches!(o, Overlay::Measure { text, .. } if text == &cx.segment_measure_label(from, to))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use artboard::ArtboardTool;
|
||||
pub use eyedropper::EyedropperTool;
|
||||
pub use free::{DistortMode, FreeTransformTool};
|
||||
pub use gradient::GradientTool;
|
||||
pub use measure::MeasureTool;
|
||||
pub use measure::{MeasureTool, measure};
|
||||
pub use transform::{TransformKind, TransformTool};
|
||||
pub use wand::{LassoTool, MagicWandTool};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user