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

+6
View File
@@ -60,6 +60,12 @@ pub trait DocApiBackend {
/// A hatch's boundary loops (outer + islands) as closed polylines.
fn hatch_boundary(&self, id: ObjectId) -> ApiResult<Vec<Vec<[f64; 2]>>>;
/// Add a linear `DIMENSION`; returns the fresh `ObjectId`.
fn add_dimension_linear(&mut self, spec: &crate::ops::DimensionSpec) -> ApiResult<ObjectId>;
/// A dimension's measured value (distance for linear/radius, degrees for angular).
fn dimension_measurement(&self, id: ObjectId) -> ApiResult<f64>;
/// Can `id` be modified in place right now (exists, is the expected family,
/// not on a locked layer)? Read-only pre-check used before mutations.
/// Default: existence + not-locked (backends narrow the family check).
+7
View File
@@ -221,6 +221,12 @@ pub fn apply_op<B: DocApiBackend>(b: &mut B, op: Operation) -> ApiResult<Receipt
b.finalize_op();
OpOutcome::NewId(id)
}
Operation::CreateDimensionLinear(spec) => {
b.push_undo(name);
let id = b.add_dimension_linear(spec)?;
b.finalize_op();
OpOutcome::NewId(id)
}
};
Ok(Receipt {
outcome: Some(outcome),
@@ -260,6 +266,7 @@ pub fn apply_queries<B: DocApiBackend>(b: &mut B, queries: Vec<Query>) -> ApiRes
Query::GetGeometryRevision => QueryResult::Revision(b.revision()),
Query::GetTextContent { id } => QueryResult::TextContent(b.text_content(id)?),
Query::GetHatchBoundary { id } => QueryResult::HatchBoundary(b.hatch_boundary(id)?),
Query::GetDimensionMeasurement { id } => QueryResult::DimensionMeasurement(b.dimension_measurement(id)?),
};
results.push(r);
}
+22
View File
@@ -269,6 +269,18 @@ handle!(Ray);
handle!(XLine);
handle!(Text);
handle!(MText);
handle!(Dimension);
impl Dimension {
/// The measured value of this dimension (distance for linear/radius, degrees
/// for angular).
pub fn measurement(&self) -> ApiResult<f64> {
match self.session.one_query(Query::GetDimensionMeasurement { id: self.id })? {
QueryResult::DimensionMeasurement(v) => Ok(v),
_ => Err(ApiError::Transport("unexpected measurement result".into())),
}
}
}
impl Entity {
pub fn view(&self) -> ApiResult<EntityView> {
@@ -475,6 +487,16 @@ impl CurveCollection {
Ok(Entity::new(self.session.clone(), id))
}
/// A linear DIMENSION between `first_point` and `second_point`, with the
/// dimension line placed at `definition_point`. `measurement()` reads the value.
pub fn create_dimension_linear(&self, first_point: [f64; 3], second_point: [f64; 3], definition_point: [f64; 3]) -> ApiResult<Dimension> {
let receipt = self.session.apply_op(Operation::CreateDimensionLinear(crate::ops::DimensionSpec {
first_point, second_point, definition_point,
}))?;
let id = receipt.outcome.and_then(|o| o.new_id()).ok_or_else(|| ApiError::Transport("create_dimension returned no id".into()))?;
Ok(Dimension::new(self.session.clone(), id))
}
/// Single-line TEXT annotation.
pub fn create_text(&self, value: &str, insertion_point: [f64; 3], height: f64, rotation: f64) -> ApiResult<Text> {
let receipt = self.session.apply_op(Operation::CreateText(crate::ops::TextSpec {
@@ -125,6 +125,18 @@ Filled region over a closed polyline boundary. boundary() returns the loops; bou
- **boundary**(``) -> `Vec<Vec<[f64; 2]>>` — query `GetHatchBoundary`
## `Dimension` (acadrust `Dimension`, collection `curves`)
Linear dimension between two measured points. measurement() reads the distance; bounds from the measured + definition points.
### Constructors (`doc.curves()`)
- **create_dimension_linear**(`first_point: [f64; 3], second_point: [f64; 3], definition_point: [f64; 3]`) -> `Dimension`
### Methods
- **measurement**(``) -> `f64` — query `GetDimensionMeasurement`
## `Entity` (acadrust `Insert`, collection `entities`)
Block reference. Place an existing BlockRecord by name; transform moves its insert point.
+21 -1
View File
@@ -277,6 +277,25 @@
],
"name": "Hatch"
},
{
"acadrust_variant": "Dimension",
"collection": "curves",
"handle": "Dimension",
"methods": [
{
"args": [],
"bulk": false,
"fixed": null,
"kind": "query",
"name": "measurement",
"op": null,
"query": "GetDimensionMeasurement",
"read_only": true,
"returns": "f64"
}
],
"name": "Dimension"
},
{
"acadrust_variant": "Insert",
"collection": "entities",
@@ -361,7 +380,8 @@
"Ray",
"XLine",
"Text",
"MText"
"MText",
"Dimension"
],
"root": "DocApi"
},
+3 -1
View File
@@ -7,7 +7,7 @@
use serde::{Deserialize, Serialize};
use crate::id::ObjectId;
use crate::ops::{BoolOp, Curve2Spec, EntitySpec, HatchSpec, InsertSpec, MTextSpec, PlacementSpec, SolidPrimitive, TextSpec, ViewportSpec};
use crate::ops::{BoolOp, Curve2Spec, DimensionSpec, EntitySpec, HatchSpec, InsertSpec, MTextSpec, PlacementSpec, SolidPrimitive, TextSpec, ViewportSpec};
/// A typed write operation (plan §5). Each variant is ONE atomic host call = one
/// undo step. Append new variants at the END only.
@@ -38,4 +38,6 @@ pub enum Operation {
SetTextContent { id: ObjectId, value: String },
// hatch
CreateHatch(HatchSpec),
// dimensions
CreateDimensionLinear(DimensionSpec),
}
+2
View File
@@ -23,4 +23,6 @@ pub enum Query {
GetTextContent { id: ObjectId },
/// The boundary loops of a Hatch (outer + islands) as polylines.
GetHatchBoundary { id: ObjectId },
/// The measured value of a Dimension (distance or angle).
GetDimensionMeasurement { id: ObjectId },
}
+10
View File
@@ -106,6 +106,15 @@ pub struct HatchSpec {
pub solid: bool,
}
/// Construction spec for a linear `DIMENSION`: the two measured points and the
/// dimension-line definition point.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DimensionSpec {
pub first_point: [f64; 3],
pub second_point: [f64; 3],
pub definition_point: [f64; 3],
}
/// A rigid similarity (rotation + translation + optional uniform scale/reflection)
/// as a plain-data mirror of `cadkernel::brep::transform`'s `Placement`
/// (non-serde). Columns are the new basis axes; `origin` is the translation.
@@ -159,6 +168,7 @@ impl crate::gen::Operation {
CreateMText(_) => "CreateMText",
SetTextContent { .. } => "SetTextContent",
CreateHatch(_) => "CreateHatch",
CreateDimensionLinear(_) => "CreateDimensionLinear",
}
}
}
+3
View File
@@ -47,6 +47,8 @@ pub enum QueryResult {
TextContent(String),
/// A hatch's boundary loops (outer + islands), each a closed polyline.
HatchBoundary(Vec<Vec<[f64; 2]>>),
/// A dimension's measured value (distance for linear/radius, degrees for angular).
DimensionMeasurement(f64),
}
/// Convenience: the query name for diagnostics.
@@ -62,6 +64,7 @@ impl crate::gen::Query {
GetGeometryRevision => "GetGeometryRevision",
GetTextContent { .. } => "GetTextContent",
GetHatchBoundary { .. } => "GetHatchBoundary",
GetDimensionMeasurement { .. } => "GetDimensionMeasurement",
}
}
}