Measure: centre-to-centre for holes, diameters for circles and arcs, slot sizes (#1392) (#1419)

Measure now reads the picked item's exact shape (cylindrical faces, circular and arc edges,
sketch circles, arcs and lines, profiles, planes, points):

- A circle, hole or arc shows Ø, R and its length (circumference, or arc length and angle).
- A slot (a sketch profile, or a half-cylinder end of a slot in a solid) shows its width,
  centre-to-centre length and overall length.
- Two items where one is round measure from the centre or axis by default (axis to axis,
  axis to plane, edge or point), with the closest and farthest distances listed too; the
  panel switches between centre, min and max, and the canvas line follows.
- inspect.measure and MCP `measure` (new `items` argument) add diameter, radius,
  center_distance, min and max; existing fields are unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-15 10:27:13 -04:00
1 parent c36a6f9f90
commit 1744fbb262
18 files changed
+1244 -73

No files matched your search

+18
View File
@@ -1373,3 +1373,21 @@ fn tool_annotations_tell_the_whole_truth() {
}
assert!(tools.iter().all(|t| t["annotations"]["readOnlyHint"].is_boolean()), "every tool is annotated");
}
/// `measure` with `items` (#1392): a sketch circle's diameter and radius, and two circles'
/// centre-to-centre with the closest and farthest distances.
#[test]
fn measure_items_gives_diameters_and_centre_distances() {
let mut s = server();
tool(&mut s, "execute", json!({"command": "sketch.create", "params": {"plane": "XY"}}));
let a = tool(&mut s, "execute", json!({"command": "sketch.circle.center", "params": {"center": [0, 0], "radius": 3}}));
let b = tool(&mut s, "execute", json!({"command": "sketch.circle.center", "params": {"center": [30, 40], "radius": 2}}));
let id = |r: &Value| r["curves"][0].as_str().or(r["result"]["curves"][0].as_str()).unwrap().to_string();
let (a, b) = (id(&a), id(&b));
let one = tool(&mut s, "measure", json!({"items": [{"type": "sketch_curve", "id": a}]}));
assert_eq!((one["diameter"].as_f64(), one["radius"].as_f64()), (Some(6.0), Some(3.0)), "{one}");
let two = tool(&mut s, "measure", json!({"items": [{"type": "sketch_curve", "id": a}, {"type": "sketch_curve", "id": b}]}));
assert!((two["center_distance"].as_f64().unwrap() - 50.0).abs() < 1e-9, "{two}");
assert!((two["min"].as_f64().unwrap() - 45.0).abs() < 1e-9 && (two["max"].as_f64().unwrap() - 55.0).abs() < 1e-9, "{two}");
assert!(two["distance_mm"].is_number() && two["from"].is_array(), "{two}");
}
+14 -2
View File
@@ -135,8 +135,17 @@ pub fn tool_definitions() -> Vec<Value> {
"measure",
"Measure bodies",
"Volume (mm³), surface area (mm²), centre of mass, bounding box and face/edge/vertex counts of one body or all \
bodies, with totals.",
obj(json!({"body": string("Body name (default: all bodies)")}), &[]),
bodies, with totals. With `items` (one or two selections, as `execute` takes them: faces, edges, vertices, \
sketch curves, profiles): one item's length, area or position, a circle's, arc's or cylinder's `diameter` and \
`radius` (with `circumference`, or an arc's `arc_length` and `angle_deg`), a slot's `slot` sizes; two items' \
`min` and `max` distance and, where one is round, `center_distance` (centre to centre, or from its axis).",
obj(
json!({
"body": string("Body name (default: all bodies)"),
"items": {"type": "array", "items": {"type": "object"}, "description": "One or two selections to measure instead of bodies, e.g. {\"type\": \"face\", \"body\": \"Body1\", \"index\": 3, \"point\": [x,y,z]}"},
}),
&[],
),
Effect::ReadOnly,
),
tool(
@@ -388,6 +397,9 @@ fn dispatch(b: &mut dyn Backend, name: &str, a: &Map<String, Value>) -> Result<V
Ok(d)
}
"measure" => {
if let Some(items) = a.get("items") {
return exec(b, "inspect.measure", json!({ "items": items }));
}
let p = match str_arg(a, "body") {
Some(body) => json!({"bodies": [body]}),
None => json!({}),