forked from 1b-agent/opencadstudio-upstream
ocs_doc_api: Phase 2c-iii — 2-line angular dimension (all dimension sub-types done)
- CreateDimensionAngular2Ln(DimensionAngularSpec) op (append-only). - backend add_dimension_angular2ln (DimensionAngular2Ln::new computes angle). - facade create_dimension_angular2ln; spec constructor; api_reference + binding_schema regenerated; mocks + baseline. - IMPLEMENTATION.md: fix stale Status line (all 5 phases complete, current test counts); mark all dimension sub-types done. - Test phase2ciii_dimension_angular2ln (90-degree 2-line angle) -> 616 host lib + 3 consistency green.
This commit is contained in:
1 parent
240831edcd
commit
612e8e13e0
12 files changed
+82
-7
No files matched your search
@@ -49,11 +49,11 @@ Phases are **spec-additive**: each is a `[[family]]`/`[[family.method]]` block i
|
||||
`ARCHITECTURE.md` § Workflow). No protocol change; new `Operation`/`Query`
|
||||
variants append at enum end.
|
||||
|
||||
**Status:** Phase 0 (supported-family methods), Phase 2a (full 2D curves),
|
||||
Phase 3 (block references), Phase 4 (viewports), Phase 5 (media read-mostly) are
|
||||
**done** (all tested; 601 host lib + 8 crate unit tests green). Remaining sub-items:
|
||||
Phase 2b (annotations/hatch/dimension typed ops), attributes + nested traversal
|
||||
(P3), set_view + view query (P4), typed media ops (P5).
|
||||
**Status:** all five phases complete — Phase 0 (supported-family methods), Phase 2
|
||||
(full 2D curves + annotations incl. dimension sub-types), Phase 3 (block references
|
||||
+ attributes + traversal + attribute definitions), Phase 4 (viewports + set_view +
|
||||
view query), Phase 5 (media read-mostly + typed raster image + typed table). All
|
||||
tested: 615 host lib + 8 crate unit + 3 spec↔wire consistency tests green.
|
||||
|
||||
### Phase 2 — full 2D + annotations
|
||||
- **Done (2a):** typed curve families `ArcCurve`/`Ellipse`/`Spline`/`Ray`/`XLine` —
|
||||
@@ -73,8 +73,10 @@ Phase 2b (annotations/hatch/dimension typed ops), attributes + nested traversal
|
||||
(`GetDimensionMeasurement`), bounds. Covered by `phase2c_*` test.
|
||||
- **Done (2c-ii, sub-types):** `create_dimension_radius` / `create_dimension_diameter`
|
||||
(radial chord) and `create_dimension_angular` (vertex + 2 legs) → radial/degrees
|
||||
measurements. Covered by `phase2cii_*` test. (`Angular2Ln` variant handled in
|
||||
`measurement`; a dedicated constructor is a later refinement.)
|
||||
measurements. Covered by `phase2cii_*` test.
|
||||
- **Done (2c-iii):** `create_dimension_angular2ln(vertex, first, second, arc_location)`
|
||||
→ `CreateDimensionAngular2Ln` (2-line angular; `DimensionAngular2Ln::new` computes
|
||||
the angle). Covered by `phase2ciii_*` test. All documented dimension sub-types done.
|
||||
- **Unblocked:** `add_vertex` (polyline) and sweep profiles (Line/Circle/Arc/LwPolyline)
|
||||
are supported (Phase 0).
|
||||
|
||||
|
||||
@@ -206,6 +206,11 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
Ok(self.alloc("AttributeDefinition"))
|
||||
}
|
||||
fn add_dimension_angular2ln(&mut self, _spec: &ocs_doc_api::ops::DimensionAngularSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
self.dimension_measurements.insert(id, 90.0);
|
||||
Ok(id)
|
||||
}
|
||||
fn add_table(&mut self, spec: &ocs_doc_api::ops::TableSpec) -> ApiResult<ObjectId> {
|
||||
let cols = spec.data.first().map(|r| r.len()).unwrap_or(0);
|
||||
if spec.data.is_empty() || cols == 0 || spec.data.iter().any(|r| r.len() != cols) {
|
||||
|
||||
@@ -346,6 +346,11 @@ name = "create_dimension_angular"
|
||||
args = [{ name = "vertex", ty = "[f64; 3]" }, { name = "first_point", ty = "[f64; 3]" }, { name = "second_point", ty = "[f64; 3]" }, { name = "arc_location", ty = "[f64; 3]" }]
|
||||
op = "CreateDimensionAngular"
|
||||
|
||||
[[family.constructor]]
|
||||
name = "create_dimension_angular2ln"
|
||||
args = [{ name = "vertex", ty = "[f64; 3]" }, { name = "first_point", ty = "[f64; 3]" }, { name = "second_point", ty = "[f64; 3]" }, { name = "arc_location", ty = "[f64; 3]" }]
|
||||
op = "CreateDimensionAngular2Ln"
|
||||
|
||||
[[family.method]]
|
||||
name = "measurement"
|
||||
kind = "query"
|
||||
|
||||
@@ -78,6 +78,9 @@ pub trait DocApiBackend {
|
||||
/// Add a `TABLE` (rows × columns grid); returns the fresh `ObjectId`.
|
||||
fn add_table(&mut self, spec: &crate::ops::TableSpec) -> ApiResult<ObjectId>;
|
||||
|
||||
/// Add a 2-line angular `DIMENSION`; returns the fresh `ObjectId`.
|
||||
fn add_dimension_angular2ln(&mut self, spec: &crate::ops::DimensionAngularSpec) -> ApiResult<ObjectId>;
|
||||
|
||||
/// A dimension's measured value (distance for linear/radius, degrees for angular).
|
||||
fn dimension_measurement(&self, id: ObjectId) -> ApiResult<f64>;
|
||||
|
||||
|
||||
@@ -304,6 +304,12 @@ pub fn apply_op<B: DocApiBackend>(b: &mut B, op: Operation) -> ApiResult<Receipt
|
||||
b.finalize_op();
|
||||
OpOutcome::NewId(id)
|
||||
}
|
||||
Operation::CreateDimensionAngular2Ln(spec) => {
|
||||
b.push_undo(name);
|
||||
let id = b.add_dimension_angular2ln(spec)?;
|
||||
b.finalize_op();
|
||||
OpOutcome::NewId(id)
|
||||
}
|
||||
};
|
||||
Ok(Receipt {
|
||||
outcome: Some(outcome),
|
||||
|
||||
@@ -539,6 +539,14 @@ impl CurveCollection {
|
||||
Ok(Entity::new(self.session.clone(), id))
|
||||
}
|
||||
|
||||
/// A 2-line angular DIMENSION: angle between lines (vertex→first) and (vertex→second).
|
||||
pub fn create_dimension_angular2ln(&self, vertex: [f64; 3], first_point: [f64; 3], second_point: [f64; 3], arc_location: [f64; 3]) -> ApiResult<Dimension> {
|
||||
let receipt = self.session.apply_op(Operation::CreateDimensionAngular2Ln(crate::ops::DimensionAngularSpec {
|
||||
vertex, first_point, second_point, arc_location,
|
||||
}))?;
|
||||
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))
|
||||
}
|
||||
/// A radial DIMENSION for the circle centered at `center` through `point`.
|
||||
pub fn create_dimension_radius(&self, center: [f64; 3], point: [f64; 3]) -> ApiResult<Dimension> {
|
||||
self.create_dim_radial(Operation::CreateDimensionRadius, center, point)
|
||||
|
||||
@@ -136,6 +136,7 @@ Linear dimension between two measured points. measurement() reads the distance;
|
||||
- **create_dimension_radius**(center: [f64; 3], point: [f64; 3]) -> `Dimension` — `CreateDimensionRadius`
|
||||
- **create_dimension_diameter**(center: [f64; 3], point: [f64; 3]) -> `Dimension` — `CreateDimensionDiameter`
|
||||
- **create_dimension_angular**(vertex: [f64; 3], first_point: [f64; 3], second_point: [f64; 3], arc_location: [f64; 3]) -> `Dimension` — `CreateDimensionAngular`
|
||||
- **create_dimension_angular2ln**(vertex: [f64; 3], first_point: [f64; 3], second_point: [f64; 3], arc_location: [f64; 3]) -> `Dimension` — `CreateDimensionAngular2Ln`
|
||||
|
||||
### Methods
|
||||
|
||||
|
||||
@@ -56,4 +56,6 @@ pub enum Operation {
|
||||
CreateAttributeDefinition(AttributeDefinitionSpec),
|
||||
// tables
|
||||
CreateTable(TableSpec),
|
||||
// dimension sub-types (2-line angular)
|
||||
CreateDimensionAngular2Ln(DimensionAngularSpec),
|
||||
}
|
||||
@@ -233,6 +233,7 @@ impl crate::gen::Operation {
|
||||
CreateDimensionAngular(_) => "CreateDimensionAngular",
|
||||
CreateAttributeDefinition(_) => "CreateAttributeDefinition",
|
||||
CreateTable(_) => "CreateTable",
|
||||
CreateDimensionAngular2Ln(_) => "CreateDimensionAngular2Ln",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,11 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
Ok(self.alloc("AttributeDefinition"))
|
||||
}
|
||||
fn add_dimension_angular2ln(&mut self, _spec: &ocs_doc_api::ops::DimensionAngularSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
self.dimension_measurements.insert(id, 90.0); // mock: 90 degrees
|
||||
Ok(id)
|
||||
}
|
||||
fn add_table(&mut self, spec: &ocs_doc_api::ops::TableSpec) -> ApiResult<ObjectId> {
|
||||
let cols = spec.data.first().map(|r| r.len()).unwrap_or(0);
|
||||
if spec.data.is_empty() || cols == 0 || spec.data.iter().any(|r| r.len() != cols) {
|
||||
|
||||
@@ -71,6 +71,7 @@ fn operation_variants() -> Vec<String> {
|
||||
tag: String::new(), prompt: String::new(), default_value: String::new(), insertion_point: [0.0; 3], height: 0.0, rotation: 0.0,
|
||||
}),
|
||||
Operation::CreateTable(ocs_doc_api::ops::TableSpec { insertion_point: [0.0; 3], data: vec![] }),
|
||||
Operation::CreateDimensionAngular2Ln(ocs_doc_api::ops::DimensionAngularSpec { vertex: [0.0; 3], first_point: [0.0; 3], second_point: [0.0; 3], arc_location: [0.0; 3] }),
|
||||
];
|
||||
// Derive the variant names by serializing a probe of each and reading the
|
||||
// bincode variant index -> name map is not available; instead match by
|
||||
@@ -165,6 +166,7 @@ const OPERATION_BASELINE: &[&str] = &[
|
||||
"CreateDimensionAngular",
|
||||
"CreateAttributeDefinition",
|
||||
"CreateTable",
|
||||
"CreateDimensionAngular2Ln",
|
||||
];
|
||||
|
||||
/// Recorded baseline of the `Query` variant order.
|
||||
|
||||
@@ -306,6 +306,16 @@ impl DocApiBackend for HostSession<'_> {
|
||||
Ok(handle_to_obj(handle))
|
||||
}
|
||||
|
||||
fn add_dimension_angular2ln(&mut self, spec: &ocs_doc_api::ops::DimensionAngularSpec) -> ApiResult<ObjectId> {
|
||||
use acadrust::entities::{Dimension, DimensionAngular2Ln};
|
||||
use acadrust::types::Vector3;
|
||||
let v3 = |p: [f64; 3]| Vector3::new(p[0], p[1], p[2]);
|
||||
let mut dim = DimensionAngular2Ln::new(v3(spec.vertex), v3(spec.first_point), v3(spec.second_point));
|
||||
dim.dimension_arc = v3(spec.arc_location);
|
||||
let handle = self.scene_mut().add_entity(EntityType::Dimension(Dimension::Angular2Ln(dim)));
|
||||
Ok(handle_to_obj(handle))
|
||||
}
|
||||
|
||||
fn add_table(&mut self, spec: &ocs_doc_api::ops::TableSpec) -> ApiResult<ObjectId> {
|
||||
use acadrust::entities::Table;
|
||||
use acadrust::types::Vector3;
|
||||
@@ -1234,6 +1244,31 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 2c-iii: 2-line angular dimension ────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn phase2ciii_dimension_angular2ln() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let mut host = HostSession::new(&mut app, 0);
|
||||
// Angle between line (0,0,0)->(10,0,0) and (0,0,0)->(0,10,0) = 90 degrees.
|
||||
let ang = new_id(&dispatch(&mut host, DocApiEnvelope::op(Operation::CreateDimensionAngular2Ln(
|
||||
ocs_doc_api::ops::DimensionAngularSpec {
|
||||
vertex: [0.0; 3], first_point: [10.0, 0.0, 0.0], second_point: [0.0, 10.0, 0.0], arc_location: [5.0, 5.0, 0.0],
|
||||
}))).unwrap());
|
||||
let receipt = dispatch(&mut host, DocApiEnvelope::queries(vec![
|
||||
Query::GetDimensionMeasurement { id: ang },
|
||||
Query::GetEntity { id: ang },
|
||||
])).unwrap();
|
||||
match &receipt.query_results[0] {
|
||||
QueryResult::DimensionMeasurement(v) => assert!((*v - 90.0).abs() < 0.1, "angle {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:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 5 remaining: typed raster image create ─────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user