General improvements on API

This commit is contained in:
Sebastian committed 2026-09-09 08:02:35 +02:00
1 parent 56e09cd3f5
commit 55f61a4327
30 files changed
+1820 -2565

No files matched your search

+31 -136
View File
@@ -8,11 +8,11 @@ use std::collections::HashMap;
use ocs_doc_api::backend::{DocApiBackend, KernelBody};
use ocs_doc_api::{
Aabb, ApiError, ApiResult, Curve2Spec, EntityView, GeometryErrorKind, GeometryRevision,
ObjectId, PlacementSpec,
Aabb, ApiError, ApiResult, Curve2Spec, EntityView, GeometryErrorKind, GeometryRevision, ObjectId,
PlacementSpec,
};
#[derive(Default, Clone)]
#[derive(Default)]
pub struct MockBackend {
next_id: u64,
revision: u64,
@@ -68,10 +68,7 @@ fn curve_bounds(spec: &Curve2Spec) -> Aabb {
min: [centre[0] - radius, centre[1] - radius, centre[2]],
max: [centre[0] + radius, centre[1] + radius, centre[2]],
},
Curve2Spec::Point { position } => Aabb {
min: *position,
max: *position,
},
Curve2Spec::Point { position } => Aabb { min: *position, max: *position },
Curve2Spec::Polyline { points, .. } => {
let mut min = [f64::INFINITY; 3];
let mut max = [f64::NEG_INFINITY; 3];
@@ -87,17 +84,9 @@ fn curve_bounds(spec: &Curve2Spec) -> Aabb {
min: [centre[0] - radius, centre[1] - radius, centre[2]],
max: [centre[0] + radius, centre[1] + radius, centre[2]],
},
Curve2Spec::Ellipse {
centre, major_axis, ..
} => {
let r = (major_axis[0] * major_axis[0]
+ major_axis[1] * major_axis[1]
+ major_axis[2] * major_axis[2])
.sqrt();
Aabb {
min: [centre[0] - r, centre[1] - r, centre[2] - r],
max: [centre[0] + r, centre[1] + r, centre[2] + r],
}
Curve2Spec::Ellipse { centre, major_axis, .. } => {
let r = (major_axis[0] * major_axis[0] + major_axis[1] * major_axis[1] + major_axis[2] * major_axis[2]).sqrt();
Aabb { min: [centre[0] - r, centre[1] - r, centre[2] - r], max: [centre[0] + r, centre[1] + r, centre[2] + r] }
}
Curve2Spec::Spline { control_points, .. } => {
let mut min = [f64::INFINITY; 3];
@@ -110,10 +99,7 @@ fn curve_bounds(spec: &Curve2Spec) -> Aabb {
}
Aabb { min, max }
}
Curve2Spec::Ray { origin, .. } | Curve2Spec::XLine { origin, .. } => Aabb {
min: *origin,
max: *origin,
},
Curve2Spec::Ray { origin, .. } | Curve2Spec::XLine { origin, .. } => Aabb { min: *origin, max: *origin },
}
}
@@ -132,35 +118,6 @@ impl DocApiBackend for MockBackend {
}
Ok(())
}
fn create_many(&mut self, specs: &[ocs_doc_api::EntitySpec]) -> ApiResult<Vec<ObjectId>> {
let mut next = self.clone();
let ids = specs
.iter()
.map(|spec| match spec {
ocs_doc_api::EntitySpec::Solid(spec) => {
next.store_solid(&ocs_doc_api::geom::make_solid(spec)?)
}
ocs_doc_api::EntitySpec::Curve(spec) => {
ocs_doc_api::convert::curve_spec_to_entity(spec)?;
next.add_curve(spec)
}
})
.collect::<ApiResult<Vec<_>>>()?;
next.push_undo("CreateMany");
next.finalize_op();
*self = next;
Ok(ids)
}
fn transform_many(&mut self, ids: &[ObjectId], placement: &PlacementSpec) -> ApiResult<()> {
let mut next = self.clone();
for &id in ids {
next.transform_entity(id, placement)?;
}
next.push_undo("TransformMany");
next.finalize_op();
*self = next;
Ok(())
}
fn add_curve(&mut self, spec: &Curve2Spec) -> ApiResult<ObjectId> {
let kind = match spec {
Curve2Spec::Line { .. } => "Line",
@@ -185,8 +142,7 @@ impl DocApiBackend for MockBackend {
}
fn add_viewport(&mut self, spec: &ocs_doc_api::ops::ViewportSpec) -> ApiResult<ObjectId> {
let id = self.alloc("Viewport");
self.viewport_views
.insert(id, (spec.view_target, spec.view_height));
self.viewport_views.insert(id, (spec.view_target, spec.view_height));
Ok(id)
}
fn add_text(&mut self, spec: &ocs_doc_api::ops::TextSpec) -> ApiResult<ObjectId> {
@@ -208,27 +164,17 @@ impl DocApiBackend for MockBackend {
}
}
fn text_content(&self, id: ObjectId) -> ApiResult<String> {
self.text_values
.get(&id)
.cloned()
.ok_or(ApiError::UnknownId(id))
self.text_values.get(&id).cloned().ok_or(ApiError::UnknownId(id))
}
fn add_hatch(&mut self, spec: &ocs_doc_api::ops::HatchSpec) -> ApiResult<ObjectId> {
let id = self.alloc("Hatch");
self.hatch_boundaries
.insert(id, vec![spec.boundary.clone()]);
self.hatch_boundaries.insert(id, vec![spec.boundary.clone()]);
Ok(id)
}
fn hatch_boundary(&self, id: ObjectId) -> ApiResult<Vec<Vec<[f64; 2]>>> {
self.hatch_boundaries
.get(&id)
.cloned()
.ok_or(ApiError::UnknownId(id))
self.hatch_boundaries.get(&id).cloned().ok_or(ApiError::UnknownId(id))
}
fn add_dimension_linear(
&mut self,
spec: &ocs_doc_api::ops::DimensionSpec,
) -> ApiResult<ObjectId> {
fn add_dimension_linear(&mut self, spec: &ocs_doc_api::ops::DimensionSpec) -> ApiResult<ObjectId> {
let id = self.alloc("Dimension");
let d = ((spec.first_point[0] - spec.second_point[0]).powi(2)
+ (spec.first_point[1] - spec.second_point[1]).powi(2)
@@ -237,54 +183,30 @@ impl DocApiBackend for MockBackend {
self.dimension_measurements.insert(id, d);
Ok(id)
}
fn add_dimension_radius(
&mut self,
spec: &ocs_doc_api::ops::DimensionRadialSpec,
) -> ApiResult<ObjectId> {
fn add_dimension_radius(&mut self, spec: &ocs_doc_api::ops::DimensionRadialSpec) -> ApiResult<ObjectId> {
let id = self.alloc("Dimension");
let d = ((spec.center[0] - spec.point[0]).powi(2)
+ (spec.center[1] - spec.point[1]).powi(2)
+ (spec.center[2] - spec.point[2]).powi(2))
.sqrt();
let d = ((spec.center[0] - spec.point[0]).powi(2) + (spec.center[1] - spec.point[1]).powi(2) + (spec.center[2] - spec.point[2]).powi(2)).sqrt();
self.dimension_measurements.insert(id, d);
Ok(id)
}
fn add_dimension_diameter(
&mut self,
spec: &ocs_doc_api::ops::DimensionRadialSpec,
) -> ApiResult<ObjectId> {
fn add_dimension_diameter(&mut self, spec: &ocs_doc_api::ops::DimensionRadialSpec) -> ApiResult<ObjectId> {
self.add_dimension_radius(spec)
}
fn add_dimension_angular(
&mut self,
_spec: &ocs_doc_api::ops::DimensionAngularSpec,
) -> ApiResult<ObjectId> {
fn add_dimension_angular(&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 dimension_measurement(&self, id: ObjectId) -> ApiResult<f64> {
self.dimension_measurements
.get(&id)
.copied()
.ok_or(ApiError::UnknownId(id))
self.dimension_measurements.get(&id).copied().ok_or(ApiError::UnknownId(id))
}
fn add_attribute_definition(
&mut self,
spec: &ocs_doc_api::ops::AttributeDefinitionSpec,
) -> ApiResult<ObjectId> {
fn add_attribute_definition(&mut self, spec: &ocs_doc_api::ops::AttributeDefinitionSpec) -> ApiResult<ObjectId> {
if spec.tag.is_empty() {
return Err(ApiError::validation(
"CreateAttributeDefinition",
"empty tag",
));
return Err(ApiError::validation("CreateAttributeDefinition", "empty tag"));
}
Ok(self.alloc("AttributeDefinition"))
}
fn add_dimension_angular2ln(
&mut self,
_spec: &ocs_doc_api::ops::DimensionAngularSpec,
) -> ApiResult<ObjectId> {
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)
@@ -292,10 +214,7 @@ impl DocApiBackend for MockBackend {
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) {
return Err(ApiError::validation(
"CreateTable",
"table needs a non-empty rectangular grid",
));
return Err(ApiError::validation("CreateTable", "table needs a non-empty rectangular grid"));
}
Ok(self.alloc("Table"))
}
@@ -312,24 +231,15 @@ impl DocApiBackend for MockBackend {
Ok(())
}
fn attributes(&self, id: ObjectId) -> ApiResult<Vec<(String, String)>> {
self.attributes_store
.get(&id)
.cloned()
.ok_or(ApiError::UnknownId(id))
self.attributes_store.get(&id).cloned().ok_or(ApiError::UnknownId(id))
}
fn block_entities(&self, _block_name: &str) -> ApiResult<Vec<EntityView>> {
Ok(Vec::new())
}
fn add_raster_image(
&mut self,
_spec: &ocs_doc_api::ops::RasterImageSpec,
) -> ApiResult<ObjectId> {
fn add_raster_image(&mut self, _spec: &ocs_doc_api::ops::RasterImageSpec) -> ApiResult<ObjectId> {
Ok(self.alloc("RasterImage"))
}
fn loft(
&mut self,
sections: &[(cadkernel::space::Plane, Vec<cadkernel::geom2d::Curve>)],
) -> ApiResult<ObjectId> {
fn loft(&mut self, sections: &[Vec<cadkernel::geom2d::Curve>]) -> ApiResult<ObjectId> {
if sections.len() < 2 {
return Err(ApiError::validation("Loft", "loft needs >= 2 profiles"));
}
@@ -337,12 +247,7 @@ impl DocApiBackend for MockBackend {
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::InvalidInput, "make cuboid"))?;
self.store_solid(&body)
}
fn set_viewport_view(
&mut self,
id: ObjectId,
view_target: [f64; 3],
view_height: f64,
) -> ApiResult<()> {
fn set_viewport_view(&mut self, id: ObjectId, view_target: [f64; 3], view_height: f64) -> ApiResult<()> {
if !self.entity_exists(id) {
return Err(ApiError::UnknownId(id));
}
@@ -350,10 +255,7 @@ impl DocApiBackend for MockBackend {
Ok(())
}
fn viewport_view(&self, id: ObjectId) -> ApiResult<([f64; 3], f64)> {
self.viewport_views
.get(&id)
.copied()
.ok_or(ApiError::UnknownId(id))
self.viewport_views.get(&id).copied().ok_or(ApiError::UnknownId(id))
}
fn add_vertex(&mut self, id: ObjectId, _at: usize, _point: [f64; 3]) -> ApiResult<()> {
if self.entity_exists(id) {
@@ -369,10 +271,7 @@ impl DocApiBackend for MockBackend {
fn get_entity(&mut self, id: ObjectId) -> ApiResult<EntityView> {
let kind = self.kinds.get(&id).ok_or(ApiError::UnknownId(id))?.clone();
let bounds = self.bodies.get(&id).and_then(|b| {
cadkernel::brep::body_bounds(b).map(|bb| Aabb {
min: bb.min,
max: bb.max,
})
cadkernel::brep::body_bounds(b).map(|bb| Aabb { min: bb.min, max: bb.max })
});
Ok(EntityView { id, kind, bounds })
}
@@ -387,9 +286,8 @@ impl DocApiBackend for MockBackend {
z_axis: placement.z_axis,
origin: placement.origin,
};
let moved = cadkernel::brep::transform(&body, &place).ok_or_else(|| {
ApiError::geometry(GeometryErrorKind::InvalidInput, "transform failed")
})?;
let moved = cadkernel::brep::transform(&body, &place)
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::InvalidInput, "transform failed"))?;
self.bodies.insert(id, moved);
}
Ok(())
@@ -402,10 +300,7 @@ impl DocApiBackend for MockBackend {
if let Some(body) = self.bodies.get(&id) {
let bb = cadkernel::brep::body_bounds(body)
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::Empty, "no bounds"))?;
return Ok(Aabb {
min: bb.min,
max: bb.max,
});
return Ok(Aabb { min: bb.min, max: bb.max });
}
if let Some(spec) = self.curves.get(&id) {
return Ok(curve_bounds(spec));