Let the conic tool take a typed Rho, and preview the curve it is about to draw (#736)
The conic tool takes a typed Rho in its canvas value box and previews the curve it's about to draw, with the tangent legs as a faint guide.
This commit is contained in:
1 parent
9b5e39844a
commit
aae70053ee
10 files changed
+349
-47
No files matched your search
@@ -15,7 +15,7 @@
|
||||
use std::f64::consts::{PI, TAU};
|
||||
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::{Analysis, Budget, ConstraintKind, CurveKind, Hold, Sketch};
|
||||
use solidcraft_sketch::{Analysis, Budget, ConstraintKind, CurveKind, Hold, Sketch, conic_rho_from_point};
|
||||
|
||||
use super::{Ent, Left, counted_solve};
|
||||
|
||||
@@ -47,13 +47,18 @@ enum Path {
|
||||
Radius { c: usize, from: f64, to: f64 },
|
||||
/// An arc's ends move along their rays from the centre `o` to the cursor's radius.
|
||||
ArcRadius { o: Vec2, ends: Vec<(usize, Vec2)>, from: f64, to: f64 },
|
||||
/// A conic's `rho` follows the cursor, read off its position between the chord's midpoint
|
||||
/// and the apex (see [`solidcraft_sketch::conic_rho_from_point`]). `a`, `b` and `apex` stay
|
||||
/// put; only the curve's fullness changes, the way an ellipse's outline changes its minor
|
||||
/// radius rather than sliding the ellipse.
|
||||
ConicRho { c: usize, from: f64, to: f64 },
|
||||
}
|
||||
|
||||
impl Path {
|
||||
fn length(&self) -> f64 {
|
||||
match self {
|
||||
Path::Slide { d, .. } => d.len(),
|
||||
Path::Radius { from, to, .. } | Path::ArcRadius { from, to, .. } => (to - from).abs(),
|
||||
Path::Radius { from, to, .. } | Path::ArcRadius { from, to, .. } | Path::ConicRho { from, to, .. } => (to - from).abs(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +66,7 @@ impl Path {
|
||||
fn points(&self) -> impl Iterator<Item = usize> + '_ {
|
||||
let pts = match self {
|
||||
Path::Slide { pts, .. } | Path::ArcRadius { ends: pts, .. } => pts.as_slice(),
|
||||
Path::Radius { .. } => &[],
|
||||
Path::Radius { .. } | Path::ConicRho { .. } => &[],
|
||||
};
|
||||
pts.iter().map(|(q, _)| *q)
|
||||
}
|
||||
@@ -90,13 +95,19 @@ impl Path {
|
||||
*r = from + (to - from) * t;
|
||||
}
|
||||
}
|
||||
Path::ConicRho { c, from, to } => {
|
||||
if let Some(CurveKind::Conic { rho, .. }) = sk.curves.get_mut(*c).map(|x| &mut x.kind) {
|
||||
*rho = from + (to - from) * t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Drag: the grabbed entity follows the cursor exactly (a point goes to it; a line,
|
||||
/// spline or conic slides by the cursor's move; a circle or arc grabbed on its edge changes
|
||||
/// radius about its centre) and the rest of the sketch moves as little as it can, staying on the
|
||||
/// Drag: the grabbed entity follows the cursor exactly (a point goes to it; a line or spline
|
||||
/// slides by the cursor's move; a circle or arc grabbed on its edge changes radius about its
|
||||
/// centre; a conic grabbed on its curve reads its fullness, `rho`, off the cursor) and the rest
|
||||
/// of the sketch moves as little as it can, staying on the
|
||||
/// solution it started on. Fixed geometry stays. A step that cannot be solved with the entity
|
||||
/// held (the path crosses where the geometry cannot reach) is approached by halving the way to
|
||||
/// it, and the next steps are tried from the last solved state, the last of them being the whole
|
||||
@@ -430,8 +441,18 @@ fn plan(sk: &Sketch, e: Ent, from: Vec2, to: Vec2) -> Option<(Path, Hold)> {
|
||||
hold.radii.push(c);
|
||||
Path::Radius { c, from: r, to: if minor > 1e-9 { minor } else { r } }
|
||||
}
|
||||
CurveKind::Conic { a, b, apex, rho } => {
|
||||
// Grabbed on the curve itself: its fullness (`rho`) follows the drag, the
|
||||
// way an ellipse's outline changes its minor radius rather than sliding the
|
||||
// ellipse. `a`, `b` and the apex (the tangent intersection, dragged
|
||||
// separately as an ordinary point) stay where they are.
|
||||
let (pa, px, pb) = (sk.point(a)?, sk.point(apex)?, sk.point(b)?);
|
||||
hold.points.extend([a, b, apex]);
|
||||
let to_rho = conic_rho_from_point(pa, px, pb, to);
|
||||
Path::ConicRho { c, from: rho, to: to_rho }
|
||||
}
|
||||
k => {
|
||||
// Lines, splines and conics slide with the cursor.
|
||||
// Lines and splines slide with the cursor.
|
||||
let mut ids = k.point_ids();
|
||||
ids.sort_unstable();
|
||||
ids.dedup();
|
||||
|
||||
@@ -534,6 +534,32 @@ fn dragging_circles_and_arcs() {
|
||||
assert!(pt(&s, "a1.start").dist(v(70.0, 0.0)) < 1e-9, "{:?}", pt(&s, "a1.start"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_a_conic_reads_rho_off_the_cursor_and_its_apex_slides_like_any_point() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.conic", json!({"start": [0, 0], "end": [40, 0], "apex": [20, 20]}));
|
||||
let rho = |s: &Session| match sketch(s).curves[0].kind {
|
||||
solidcraft_sketch::CurveKind::Conic { rho, .. } => rho,
|
||||
ref k => panic!("not a conic: {k:?}"),
|
||||
};
|
||||
assert!((rho(&s) - 0.5).abs() < 1e-9, "default rho: {}", rho(&s));
|
||||
|
||||
// Grabbed on the curve itself (its midpoint, at the default rho of 0.5): `rho` follows the
|
||||
// cursor, read off where it falls between the chord's midpoint (20, 0) and the apex (20,
|
||||
// 20). The three defining points stay exactly where they were.
|
||||
drag(&mut s, "k1", [20.0, 10.0], [20.0, 14.0]);
|
||||
assert!((rho(&s) - 0.7).abs() < 1e-6, "rho after drag: {}", rho(&s));
|
||||
assert!(pt(&s, "k1.start").dist(v(0.0, 0.0)) < 1e-9);
|
||||
assert!(pt(&s, "k1.end").dist(v(40.0, 0.0)) < 1e-9);
|
||||
assert!(pt(&s, "k1.apex").dist(v(20.0, 20.0)) < 1e-9);
|
||||
|
||||
// Grabbed on the apex itself: it slides like any other point (not a `rho` edit), and `rho`
|
||||
// is unaffected.
|
||||
drag(&mut s, "k1.apex", [20.0, 20.0], [10.0, 30.0]);
|
||||
assert!(pt(&s, "k1.apex").dist(v(10.0, 30.0)) < 1e-9);
|
||||
assert!((rho(&s) - 0.7).abs() < 1e-6, "rho after apex drag: {}", rho(&s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrained_geometry_resists_drags() {
|
||||
let mut s = new_sketch();
|
||||
|
||||
Reference in new issue
Block a user