ocs_doc_api: fix review findings (layouts, Helix bounds, points_aabb, mass-properties budget, docs)

- layouts.json: append the 6 missing ops (CreateDimensionRadius/Diameter/Angular,
  CreateAttributeDefinition, CreateTable, CreateDimensionAngular2Ln); regenerate
  binding_schema.json + api_reference.md wire vocabulary (all 28 ops).
- Helix bounds: bound the evaluated spline control polygon (was a degenerate
  single-start-point AABB).
- points_aabb: empty -> Geometry::Empty('entity has no points to bound'), not
  UnknownId (the entity exists; don't misclassify as stale/deleted).
- mass_properties: accuracy-first fine tessellation + graceful degradation to the
  approximate meshes.metrics value when the cold-tess budget is exhausted (large
  distinct-solid batches no longer hard-fail).
- IMPLEMENTATION.md: Volume/Centroid rows + accuracy paragraph + test count (618)
  consistent with the final design.
- 618 host lib + 8 crate + 3 consistency green.
This commit is contained in:
Sebastian committed 2026-09-06 14:23:00 +02:00
1 parent 28c8e0ef1a
commit ac54d6079b
6 files changed
+52 -16

No files matched your search

+9 -1
View File
@@ -633,10 +633,18 @@ impl HostSession<'_> {
}
// Accuracy path: fine tessellation so the divergence volume/centroid
// converge to near-analytic (the render-mesh metrics cache is the coarse
// LOD for display, NOT for query accuracy). Bounded by the cold-tess budget;
// display LOD, NOT for query accuracy). Bounded by the cold-tess budget;
// memoized per (handle, epoch) so repeated queries on the same solid are O(1).
const COLD_TESS_BUDGET: usize = 256;
if self.cold_tess_used >= COLD_TESS_BUDGET {
// Budget exhausted: serve a documented-approximate value from the render
// mesh metrics cache rather than failing (large distinct-solid batches
// degrade gracefully instead of erroring). Accuracy-first up to the budget.
if let Some(metrics) = self.scene().meshes.get(&handle).map(|m| m.metrics) {
if metrics.volume.abs() > 0.0 {
return Ok((metrics.volume, metrics.centroid));
}
}
return Err(ApiError::Unsupported(
"volume/centroid cold-tessellation budget exceeded for this session; query fewer distinct solids".into(),
));
+14 -5
View File
@@ -5,7 +5,7 @@
use acadrust::entities::{Arc, Circle, Ellipse, Line, LwPolyline, LwVertex, Point, Ray, Spline, XLine};
use acadrust::types::{Vector2, Vector3};
use acadrust::EntityType;
use ocs_doc_api::{Aabb, ApiError, ApiResult, Curve2Spec, ObjectId};
use ocs_doc_api::{Aabb, ApiError, ApiResult, Curve2Spec, GeometryErrorKind, ObjectId};
/// Convert a profile entity (Line/Circle/Arc/LwPolyline) to `geom2d::Curve`s for
/// sweep ops (`Extrude`/`Revolve`). Z is dropped (profiles lie on the XY plane).
@@ -481,17 +481,26 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
id,
)?,
EntityType::MLine(m) => points_aabb(m.vertices.iter().map(|v| &v.position).collect(), id)?,
EntityType::Helix(h) => points_aabb([&h.start_point].into_iter().collect(), id)?,
EntityType::Helix(h) => {
// Bound the evaluated spline's control polygon (conservative); the
// single start_point alone would be a degenerate zero-size box.
if h.spline.control_points.is_empty() {
points_aabb([&h.start_point].into_iter().collect(), id)?
} else {
points_aabb(h.spline.control_points.iter().collect(), id)?
}
}
// Ray/XLine are unbounded by definition — no finite bounds.
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
};
Ok(bb)
}
/// AABB over a set of 3D points. `UnknownId` if empty (no points to bound).
fn points_aabb(pts: Vec<&Vector3>, id: ObjectId) -> ApiResult<Aabb> {
/// AABB over a set of 3D points. A geometry-Empty error if there are no points
/// to bound (the entity exists — do NOT misclassify as a stale/deleted id).
fn points_aabb(pts: Vec<&Vector3>, _id: ObjectId) -> ApiResult<Aabb> {
if pts.is_empty() {
return Err(ApiError::UnknownId(id));
return Err(ApiError::geometry(GeometryErrorKind::Empty, "entity has no points to bound"));
}
let mut min = [f64::INFINITY; 3];
let mut max = [f64::NEG_INFINITY; 3];