mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 10:56:51 -06:00
* STRETCH moves only the multiline vertices inside the area (#1712) * STRETCH selects a multiline when a crossing area holds one of its vertices (#1712) --------- Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
co-authored by
Brandon Thomas
parent
c92bb5cdad
commit
9d9e4c3b0d
@@ -219,6 +219,22 @@ impl StretchArea {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add to `hs` the visible multilines in `space` with a control vertex inside `area`. A crossing
|
||||
/// selection tests the drawn element lines, which sit away from the control vertices by the
|
||||
/// multiline's scale, so a small area around a vertex alone would miss them.
|
||||
fn add_mline_vertex_hits(d: &cadcraft_doc::Drawing, space: &cadcraft_doc::Space, area: &StretchArea, hs: &mut Vec<Handle>) {
|
||||
let Some(store) = d.space(space) else { return };
|
||||
for e in store.iter() {
|
||||
if let EntityKind::MLine(m) = &e.kind
|
||||
&& d.is_visible(e)
|
||||
&& !hs.contains(&e.handle)
|
||||
&& m.vertices.iter().any(|v| area.contains(v.p.xy()))
|
||||
{
|
||||
hs.push(e.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the vertices of `e` that lie inside any of the areas `wins` by `d` (STRETCH).
|
||||
fn stretch_entity(e: &mut Entity, wins: &[StretchArea], d: Vec2) {
|
||||
let inside = |p: Vec2| wins.iter().any(|a| a.contains(p));
|
||||
@@ -240,6 +256,7 @@ fn stretch_entity(e: &mut Entity, wins: &[StretchArea], d: Vec2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityKind::MLine(m) => m.vertices.iter_mut().for_each(|v| mv(&mut v.p)),
|
||||
EntityKind::Arc(a) => {
|
||||
// Arcs keep their shape: move when the centre is inside.
|
||||
if inside(a.center.xy()) {
|
||||
@@ -287,6 +304,7 @@ fn run_stretch(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let bx = cadcraft_geom::Bounds2::new(*a, *b);
|
||||
(crate::select::select_window(s.doc()?, &space, bx, true), StretchArea::Box(bx))
|
||||
};
|
||||
add_mline_vertex_hits(s.doc()?, &space, &area, &mut hs);
|
||||
hs.retain(|h| !super::curves::is_locked(s, *h));
|
||||
let doc = s.doc_mut()?;
|
||||
let areas = [area];
|
||||
@@ -353,7 +371,12 @@ impl StretchM {
|
||||
/// of two) the mode is dropped.
|
||||
fn finish_mode(&mut self, s: &mut Session, m: SelectMode) -> Result<()> {
|
||||
let space = s.space();
|
||||
let Some(hs) = m.select(s.doc()?, &space) else { return Ok(()) };
|
||||
let Some(mut hs) = m.select(s.doc()?, &space) else { return Ok(()) };
|
||||
let crossing = match (m.kind, m.pts.first().zip(m.pts.get(1))) {
|
||||
(SelectKind::Crossing | SelectKind::CPolygon, _) => true,
|
||||
(SelectKind::Box, Some((a, b))) => b.x < a.x,
|
||||
_ => false,
|
||||
};
|
||||
let area = match (m.kind, m.pts.first().zip(m.pts.get(1))) {
|
||||
(SelectKind::WPolygon | SelectKind::CPolygon, _) => Some(StretchArea::Polygon(m.pts)),
|
||||
(SelectKind::Window | SelectKind::Crossing | SelectKind::Box, Some((a, b))) => {
|
||||
@@ -363,6 +386,9 @@ impl StretchM {
|
||||
_ => None,
|
||||
};
|
||||
let whole = area.is_none();
|
||||
if let (Some(a), true, false) = (&area, crossing, self.removing) {
|
||||
add_mline_vertex_hits(s.doc()?, &space, a, &mut hs);
|
||||
}
|
||||
if let (Some(a), false) = (area, self.removing) {
|
||||
self.windows.push(a);
|
||||
}
|
||||
@@ -400,8 +426,11 @@ impl StretchM {
|
||||
Some(c) => {
|
||||
let bx = cadcraft_geom::Bounds2::new(c, p);
|
||||
let space = s.space();
|
||||
let hs = crate::select::select_window(s.doc()?, &space, bx, p.x < c.x);
|
||||
let mut hs = crate::select::select_window(s.doc()?, &space, bx, p.x < c.x);
|
||||
if !self.removing {
|
||||
if p.x < c.x {
|
||||
add_mline_vertex_hits(s.doc()?, &space, &StretchArea::Box(bx), &mut hs);
|
||||
}
|
||||
self.windows.push(StretchArea::Box(bx));
|
||||
}
|
||||
self.add(s, hs, false);
|
||||
|
||||
@@ -128,3 +128,49 @@ fn hostile_polygon_and_fence_never_panic() {
|
||||
s.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn polygon_moves_only_the_multiline_vertices_inside_it() {
|
||||
let mut s = Session::new();
|
||||
s.execute("mline", &json!({ "points": [[0, 0], [3000, 0], [3000, 2000]], "scale": 200 })).unwrap();
|
||||
let r = s.execute("stretch", &json!({ "polygon": [[2500, 1500], [3500, 1500], [3500, 2500], [2500, 2500]], "delta": [610, 0] })).unwrap();
|
||||
assert_eq!(r["stretched"], 1);
|
||||
let pts = |s: &Session| -> Vec<(f64, f64)> {
|
||||
s.doc()
|
||||
.unwrap()
|
||||
.model
|
||||
.iter()
|
||||
.find_map(|e| match &e.kind {
|
||||
EntityKind::MLine(m) => Some(m.vertices.iter().map(|v| (v.p.x, v.p.y)).collect()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(pts(&s), vec![(0.0, 0.0), (3000.0, 0.0), (3610.0, 2000.0)]);
|
||||
// A window over the first vertex moves that vertex only, not the whole multiline.
|
||||
s.execute("stretch", &json!({ "window": [[-100, -100], [100, 100]], "delta": [610, 0] })).unwrap();
|
||||
assert_eq!(pts(&s), vec![(610.0, 0.0), (3000.0, 0.0), (3610.0, 2000.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_area_around_a_multiline_vertex_moves_it_at_a_large_scale() {
|
||||
let mline_points = |s: &Session| -> Vec<(f64, f64)> {
|
||||
s.doc()
|
||||
.unwrap()
|
||||
.model
|
||||
.iter()
|
||||
.find_map(|e| match &e.kind {
|
||||
EntityKind::MLine(m) => Some(m.vertices.iter().map(|v| (v.p.x, v.p.y)).collect()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
let mut s = Session::new();
|
||||
s.execute("mline", &json!({ "points": [[0, 0], [3000, 0], [3000, 2000]], "scale": 200 })).unwrap();
|
||||
// The element lines run 100 away from the middle vertex, so this area touches none of them.
|
||||
let r = s.execute("stretch", &json!({ "polygon": [[2995, -5], [3005, -5], [3005, 5], [2995, 5]], "delta": [0, 610] })).unwrap();
|
||||
assert_eq!(r["stretched"], 1);
|
||||
assert_eq!(mline_points(&s), vec![(0.0, 0.0), (3000.0, 610.0), (3000.0, 2000.0)]);
|
||||
s.execute("stretch", &json!({ "window": [[2995, 605], [3005, 615]], "delta": [0, -610] })).unwrap();
|
||||
assert_eq!(mline_points(&s), vec![(0.0, 0.0), (3000.0, 0.0), (3000.0, 2000.0)]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user