Ignore repeated initial sketch points while drawing
This commit is contained in:
1 parent
f0292b1606
commit
8c13914d6c
3 files changed
+58
-1
No files matched your search
@@ -63,6 +63,9 @@ headline counts in-scope tabs only and lists the deferred ones separately.
|
||||
|
||||
## Dimensioning and error reporting
|
||||
|
||||
- Repeating the initial point of a planar multi-point sketch drawing tool is neutral: the tool
|
||||
waits for a distinct point without a degenerate-shape error or history entry. Typed input
|
||||
and programmatic commands retain their validation.
|
||||
- Single-line and rectangle-side dimensioning supports selecting a line or its two endpoints.
|
||||
Type a value and press Enter to commit one undo step; rejected values remain editable and
|
||||
cancellation leaves the sketch unchanged. Fit keeps geometry clear of the Sketch Palette
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use crate::scenario::Harness;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn repeated_first_point_is_neutral_and_drawing_can_continue() {
|
||||
for (command, rest) in [
|
||||
("sketch.rectangle.two_point", vec![[10, 5, 0]]),
|
||||
("sketch.rectangle.center", vec![[10, 5, 0]]),
|
||||
("sketch.rectangle.three_point", vec![[10, 0, 0], [10, 5, 0]]),
|
||||
("sketch.circle.center", vec![[5, 0, 0]]),
|
||||
("sketch.circle.three_point", vec![[10, 0, 0], [5, 5, 0]]),
|
||||
("sketch.line", vec![[10, 5, 0]]),
|
||||
("sketch.polygon.inscribed", vec![[10, 5, 0]]),
|
||||
("sketch.slot.center_to_center", vec![[10, 0, 0], [10, 3, 0]]),
|
||||
] {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("sketch.create", json!({"plane":"XY"})).unwrap();
|
||||
h.app.cam.distance = 50.0;
|
||||
h.step(&json!({"start":command})).unwrap();
|
||||
let before = h.app.session.doc.to_json();
|
||||
let undo = h.app.session.undo.len();
|
||||
let p = h.call("ui.at", json!({"sketch":[0,0]}))["result"].clone();
|
||||
h.call("ui.click", json!({"x":p[0],"y":p[1],"double":true}));
|
||||
assert!(!h.app.status.as_ref().is_some_and(|s| s.2), "{command}: {:?}", h.app.status);
|
||||
assert_eq!(h.app.tool.as_ref().unwrap().pts.len(), 1, "{command}");
|
||||
assert_eq!(h.app.session.doc.to_json(), before);
|
||||
assert_eq!(h.app.session.undo.len(), undo);
|
||||
for point in rest {
|
||||
h.step(&json!({"move":{"world":point}})).unwrap();
|
||||
if command == "sketch.slot.center_to_center" {
|
||||
// The live width editor is placed at the cursor; Enter accepts it.
|
||||
h.step(&json!({"key":"Enter"})).unwrap();
|
||||
} else {
|
||||
h.step(&json!({"click":{"world":point}})).unwrap();
|
||||
}
|
||||
}
|
||||
assert!(!h.app.status.as_ref().is_some_and(|s| s.2), "{command}: {:?}", h.app.status);
|
||||
let state = h.app.session.world_state();
|
||||
assert!(!state.sketches[0].sketch.curves.is_empty(), "{command}: {:?}; {:?}", h.app.tool, h.app.status);
|
||||
assert_eq!(h.app.session.undo.len(), undo + 1, "{command}");
|
||||
h.app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(h.app.session.doc.to_json(), before, "{command}");
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,10 @@ use crate::viewport::{Hit, Proj, pick, sketch_point_at};
|
||||
#[path = "sketch_display_preview_tests.rs"]
|
||||
mod display_preview_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "sketch_neutral_click_tests.rs"]
|
||||
mod neutral_click_tests;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Kind {
|
||||
/// Click points; `n` points complete the shape (0 = chain until finished).
|
||||
@@ -144,12 +148,18 @@ pub fn on_click(app: &mut SolveApp, proj: &Proj, pos: Pos2) {
|
||||
return;
|
||||
}
|
||||
match tool.kind {
|
||||
Kind::Draw(_) => {
|
||||
Kind::Draw(n) => {
|
||||
if let Some(mut p) = sketch_point_at(app, proj, pos) {
|
||||
// Typed values win over the cursor; lines snap to horizontal/vertical.
|
||||
if tool.dims.iter().any(|b| b.locked) || (tool.cmd == "sketch.line" && p.1.is_none()) {
|
||||
p = (crate::sketch_dims::effective(app, &tool, p.0), None);
|
||||
}
|
||||
// A repeated initial point supplies no extent. Keep waiting rather than
|
||||
// attempting a degenerate shape; explicit typed values still validate.
|
||||
if n != 1 && tool.pts.len() == 1 && tool.pts.first().is_some_and(|first| first.0 == p.0) && !tool.dims.iter().any(|b| b.locked) {
|
||||
app.tool = Some(tool);
|
||||
return;
|
||||
}
|
||||
app.tool = Some(tool);
|
||||
place(app, p);
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user