MEASUREGEOM Volume: the height can be given by two points, and Enter at the height prompt says a height is required

This commit is contained in:
aiongg
2026-10-25 04:26:14 +00:00
parent 8296bbc642
commit 23d44cd9c8
2 changed files with 40 additions and 11 deletions
+32 -11
View File
@@ -933,6 +933,8 @@ pub(super) struct AreaM {
volume: bool,
/// Areas measured in volume mode that still wait for their height, oldest first.
pending: Vec<f64>,
/// The first point of a height given by two points.
height_from: Option<Vec2>,
}
impl AreaM {
@@ -960,6 +962,19 @@ impl AreaM {
AreaMode::Subtract => self.total - v,
}
}
/// Measure the oldest pending area's volume with height `h`.
fn height(&mut self, s: &mut Session, h: f64) -> Step {
match self.pending.first().map(|a| a * h.abs()) {
Some(v) if v.is_finite() && self.total_with(v).is_finite() => {
self.pending.remove(0);
self.volume_measured(s, v)
}
_ => {
s.echo("The volume is too large.");
Step::Continue
}
}
}
/// Report a volume (and the running total in add/subtract mode); ends the command in single mode.
fn volume_measured(&mut self, s: &mut Session, v: f64) -> Step {
s.echo(volume_text(s, v));
@@ -1003,7 +1018,10 @@ impl Interactive for AreaM {
}
fn prompt(&self, _s: &Session) -> Prompt {
if !self.pending.is_empty() {
return Prompt::new(format!("{}Specify height", self.mode_prefix()), Accept::NUMBER);
return match self.height_from {
Some(p) => Prompt::new("Specify second point", Accept::POINT).base(p),
None => Prompt::new(format!("{}Specify height", self.mode_prefix()), Accept::POINT_OR_NUMBER),
};
}
if self.object {
return Prompt::new(format!("{}Select objects", self.mode_prefix()), Accept::SELECT);
@@ -1023,17 +1041,20 @@ impl Interactive for AreaM {
}
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
if !self.pending.is_empty() {
if let Input::Text(t) = i {
match crate::units::parse_distance(&t).filter(|v| v.is_finite()) {
Some(h) => match self.pending.first().map(|a| a * h.abs()) {
Some(v) if v.is_finite() && self.total_with(v).is_finite() => {
self.pending.remove(0);
return Ok(self.volume_measured(s, v));
}
_ => s.echo("The volume is too large."),
},
None => s.echo("Requires a distance."),
// A height is typed, or given by two points (their distance).
match (i, self.height_from) {
(Input::Point(p), None) => self.height_from = Some(p),
(Input::Point(p), Some(from)) => {
self.height_from = None;
return Ok(self.height(s, p.dist(from)));
}
(Input::Text(t), None) => match crate::units::parse_distance(&t).filter(|v| v.is_finite()) {
Some(h) => return Ok(self.height(s, h)),
None => s.echo("Requires a distance."),
},
(Input::Enter, None) => s.echo("Requires a height."),
(Input::Enter, Some(_)) => s.echo("Requires a second point."),
_ => {}
}
return Ok(Step::Continue);
}
@@ -229,6 +229,14 @@ fn measuregeom_volume_multiplies_an_area_by_a_height() {
assert!(has(&log, "Requires a distance."), "{log:?}");
assert!(s.prompt_text().contains("Specify height"), "{}", s.prompt_text());
// Enter is not a height; two points give the height as their distance.
let (mut s, log) = run(&["measuregeom", "v", "0,0", "4,0", "4,3", "", "", "1,1"]);
assert!(has(&log, "Requires a height."), "{log:?}");
assert!(s.prompt_text().contains("Specify second point"), "{}", s.prompt_text());
s.cmdline("7,9").unwrap();
assert!(has(&s.log, "Volume = 60.0000"), "{:?}", s.log);
assert!(s.running.is_none(), "{}", s.prompt_text());
// The JSON forms, by points and by handle.
let mut s = Session::new();
let r = s.execute("measuregeom", &json!({"mode": "volume", "points": [[0, 0], [4, 0], [4, 3]], "height": 10})).unwrap();