ocs_doc_api: Phase 2b-c (linear Dimension) — Phase 2 complete

- CreateDimensionLinear(DimensionSpec) op + GetDimensionMeasurement query (append-only).
- backend add_dimension_linear (Dimension::Linear via DimensionLinear::new) +
  dimension_measurement (linear/radius/diameter distance).
- Dimension bounds from measured + definition points; Dimension handle + measurement().
- spec Dimension family; api_reference + binding_schema regenerated; mocks + baseline.
- Test phase2c_dimension_linear_create_measurement_bounds -> 607 host lib + 8 crate
  + 3 consistency green. Phase 2 (full 2D + annotations) is now complete.
This commit is contained in:
Sebastian committed 2026-09-06 09:43:37 +02:00
1 parent ba8e5ed0cd
commit 2dba0158ca
16 files changed
+209 -3

No files matched your search

+56
View File
@@ -238,6 +238,30 @@ impl DocApiBackend for HostSession<'_> {
Ok(loops)
}
fn add_dimension_linear(&mut self, spec: &ocs_doc_api::ops::DimensionSpec) -> ApiResult<ObjectId> {
use acadrust::entities::{Dimension, DimensionLinear};
use acadrust::types::Vector3;
let v3 = |p: [f64; 3]| Vector3::new(p[0], p[1], p[2]);
let mut dim = DimensionLinear::new(v3(spec.first_point), v3(spec.second_point));
dim.definition_point = v3(spec.definition_point);
let handle = self.scene_mut().add_entity(EntityType::Dimension(Dimension::Linear(dim)));
Ok(handle_to_obj(handle))
}
fn dimension_measurement(&self, id: ObjectId) -> ApiResult<f64> {
let entity = self.document().get_entity(obj_to_handle(id)).ok_or(ApiError::UnknownId(id))?;
let EntityType::Dimension(d) = entity else {
return Err(ApiError::Unsupported("GetDimensionMeasurement is only for Dimension".into()));
};
// Distance for linear/radius/diameter; degrees for angular.
Ok(match d {
acadrust::entities::Dimension::Linear(x) => x.measurement(),
acadrust::entities::Dimension::Radius(x) => x.measurement(),
acadrust::entities::Dimension::Diameter(x) => x.measurement(),
_ => return Err(ApiError::Unsupported("dimension measurement for this sub-type".into())),
})
}
fn add_vertex(&mut self, id: ObjectId, at: usize, point: [f64; 3]) -> ApiResult<()> {
let handle = obj_to_handle(id);
let Some(entity) = self.document().get_entity(handle).cloned() else {
@@ -905,6 +929,38 @@ mod tests {
assert!((t.insertion_point.x - 15.0).abs() < 1e-6);
}
// ── Phase 2b-c: dimension ─────────────────────────────────────────────────
#[test]
fn phase2c_dimension_linear_create_measurement_bounds() {
let mut app = OpenCADStudio::new_for_test();
let mut host = HostSession::new(&mut app, 0);
// Linear dimension from (0,0,0) to (30,0,0) with the line at (0,5,0).
let dim = new_id(&dispatch(&mut host, DocApiEnvelope::op(Operation::CreateDimensionLinear(
ocs_doc_api::ops::DimensionSpec {
first_point: [0.0, 0.0, 0.0],
second_point: [30.0, 0.0, 0.0],
definition_point: [0.0, 5.0, 0.0],
}))).unwrap());
let receipt = dispatch(&mut host, DocApiEnvelope::queries(vec![
Query::GetDimensionMeasurement { id: dim },
Query::GetEntity { id: dim },
Query::GetBounds { id: dim },
])).unwrap();
match &receipt.query_results[0] {
QueryResult::DimensionMeasurement(v) => assert!((*v - 30.0).abs() < 1e-6, "measurement {v}"),
other => panic!("expected measurement, got {other:?}"),
}
match &receipt.query_results[1] {
QueryResult::Entity(e) => assert_eq!(e.kind, "Dimension"),
other => panic!("expected entity, got {other:?}"),
}
match &receipt.query_results[2] {
QueryResult::Bounds(b) => assert!((b.min[0] - 0.0).abs() < 1e-6 && (b.max[0] - 30.0).abs() < 1e-6, "{b:?}"),
other => panic!("expected bounds, got {other:?}"),
}
}
// ── Phase 2b-b: hatch ────────────────────────────────────────────────────
#[test]
+10
View File
@@ -412,6 +412,16 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
min: [t.insertion_point.x, t.insertion_point.y - t.height, t.insertion_point.z],
max: [t.insertion_point.x + t.height * t.value.len() as f64, t.insertion_point.y, t.insertion_point.z],
},
EntityType::Dimension(acadrust::entities::Dimension::Linear(d)) => {
let pts = [d.first_point, d.second_point, d.definition_point];
let mut min = [f64::INFINITY; 3];
let mut max = [f64::NEG_INFINITY; 3];
for p in pts {
min = [min[0].min(p.x), min[1].min(p.y), min[2].min(p.z)];
max = [max[0].max(p.x), max[1].max(p.y), max[2].max(p.z)];
}
Aabb { min, max }
}
EntityType::Hatch(h) => {
let mut min = [f64::INFINITY; 3];
let mut max = [f64::NEG_INFINITY; 3];