ocs_doc_api: Phase 0 + Phase 2a (sweep profiles, non-solid transform, full 2D curves)

Phase 0 (outstanding supported-family methods):
- add_vertex on LwPolyline (insert vertex, out-of-range validation).
- extrude / revolve from profiles (entity_to_profile_curves: Line/Circle/Arc/
  LwPolyline straight-segment conversion -> geom2d::Curve -> brep::extrude/revolve).
- non-solid transform + transform_many via acadrust geometry
  (transform_entity_geometry for Line/Circle/Arc/Point/LwPolyline).
- ensure_transformable broadened accordingly (all-or-nothing pre-validation).

Phase 2a (full 2D curve families): ArcCurve/Ellipse/Spline/Ray/XLine.
- Curve2Spec extended (Arc/Ellipse/Spline/Ray/XLine) + create_arc/create_ellipse/
  create_spline/create_ray/create_xline facade constructors + handle types.
- entity_kind_name, entity_bounds (Arc coarse, Ellipse major-axis, Spline bbox;
  Ray/XLine unbounded), transform_entity_geometry, ensure_transformable extended.
- spec/entities.toml families; api_reference.md + binding_schema.json regenerated.

Tests: +6 doc_api integration tests (phase0_*, phase2_*) -> 598 host lib green.
IMPLEMENTATION.md updated (Phase 0 done, Phase 2a done / 2b remaining).
This commit is contained in:
Sebastian committed 2026-09-06 00:51:39 +02:00
1 parent e41086b058
commit d46351cdb3
8 files changed
+647 -47

No files matched your search

+23 -16
View File
@@ -11,8 +11,9 @@ content).
| Capability | Method / op | Notes |
|---|---|---|
| Primitives | `create_cuboid` / `create_sphere` / `create_cylinder` / `create_cone` / `create_torus` / `create_wedge` | `CreateSolid(SolidPrimitive)` → `brep::make::*` |
| Sweep | `extrude(profile, dir)` / `revolve(profile, pivot, axis, angle)` | `Extrude`/`Revolve` → `brep::extrude`/`brep::revolve`; profile = Line/Circle/Arc/LwPolyline |
| Booleans | `intersect` / `union` / `subtract` | `SolidBoolean{op, a, b, erase_sources:true}` → `brep::combine` |
| Transform | `transform(placement)` (in-place) | `Transform{id, placement}` → `brep::transform`; solids only |
| Transform | `transform(placement)` (in-place) | `Transform{id, placement}` → `brep::transform`; solids |
| Bounds | `bounds()` | `GetBounds` → `brep::body_bounds` (lift-on-miss) |
| Volume | `volume()` | `GetVolume` → `meshes.metrics` cache, mesh-divergence fallback |
| Centroid | `centroid()` | `GetCentroid` → `meshes.metrics` cache, mesh-divergence fallback |
@@ -21,17 +22,19 @@ content).
### Minimal 2D (phase-1 set, used as profiles/inputs)
| Capability | Method / op | Notes |
|---|---|---|
| Line | `create_line` → bounds | `CreateCurve(Line)` |
| Circle | `create_circle` → bounds | `CreateCurve(Circle)` |
| Polyline | `create_polyline` → bounds | `CreateCurve(Polyline)` |
| Point | `create_point` / `create_points` (bulk) | `CreateCurve(Point)` / `CreateMany` |
| Line | `create_line` → bounds, transform | `CreateCurve(Line)` |
| Circle | `create_circle` → bounds, transform | `CreateCurve(Circle)` |
| Arc | (create via host) → transform, profile | usable as sweep profile |
| Polyline | `create_polyline` → bounds, transform, `add_vertex` | `CreateCurve(Polyline)`; `AddVertex` inserts a vertex |
| Point | `create_point` / `create_points` (bulk) → transform | `CreateCurve(Point)` / `CreateMany` |
| Transform (2D) | `transform(placement)` / `transform_many` | `Line`/`Circle`/`Arc`/`Point`/`LwPolyline` via acadrust |
### Cross-cutting
| Capability | Method | Notes |
|---|---|---|
| Lookup / downcast | `entities().get(id)` → `Entity`; `as_solid()` | generic, any family |
| Bulk create | `create_points(&[..])`, `CreateMany` | all-or-nothing, one undo step |
| Bulk transform | `transform_many(&ids, placement)` | all-or-nothing, solids only (v1) |
| Bulk transform | `transform_many(&ids, placement)` | all-or-nothing, solids + 2D families |
| Bulk delete | `delete_many(&ids)` | all-or-nothing; `OpGroup::compensate` uses it |
| Read batch | `query_batch(\|q\| …)` | read-only, one round-trip, no bump |
| Revision guard | `revision()` / `assert_revision(rev)` | read-modify-write guard |
@@ -47,12 +50,15 @@ Phases are **spec-additive**: each is a `[[family]]`/`[[family.method]]` block i
variants append at enum end.
### Phase 2 — full 2D + annotations
- **Entities:** ellipse, spline, xline/ray, arc (typed), text, mtext, dimension,
hatch.
- **Methods:** boundary/vertex actions (`add_vertex` on polyline — currently
`Unsupported`), hatch boundary query, text content get/set.
- **Blocked by:** `AddVertex` backend impl (currently returns `Unsupported`);
curve → `geom2d::Curve` conversion for non-trivial curves.
- **Done (2a):** typed curve families `ArcCurve`/`Ellipse`/`Spline`/`Ray`/`XLine` —
`create_arc`/`create_ellipse`/`create_spline`/`create_ray`/`create_xline`, bounds
(Arc coarse full-circle, Ellipse major-axis radius, Spline control-point bbox;
Ray/XLine unbounded → `Unsupported`), and transform (all five + the phase-1 set)
via acadrust. Covered by `phase2_*` tests.
- **Remaining (2b):** annotations `Text`/`MText` (create + content get/set), `Hatch`
(create + boundary query), `Dimension` (typed sub-types).
- **Unblocked:** `add_vertex` (polyline) and sweep profiles (Line/Circle/Arc/LwPolyline)
are supported (Phase 0).
### Phase 3 — containers
- **Entities:** insert/block references, attribute definitions/entities.
@@ -75,13 +81,14 @@ variants append at enum end.
| Method | Family | Status | Blocker |
|---|---|---|---|
| `extrude(profile, dir)` | Solid | stub | `profile_curves` backend returns `Unsupported`; needs entity → `geom2d::Curve` conversion |
| `revolve(profile, pivot, axis, angle)` | Solid | stub | same `profile_curves` blocker |
| `loft(sections)` | Solid | not started | multi-profile kernel op + spec |
| `add_vertex(at, pt)` | Polyline | stub | backend `add_vertex` returns `Unsupported` |
| `transform` (non-solid) | 2D entities | `Unsupported` | per-family transform via acadrust (not kernel) |
| Bulge-arc polyline profiles | Solid sweep | partial | `entity_to_profile_curves` emits straight segments; bulge arcs need arc-segment conversion |
| `GetCentroid`/`GetVolume` accuracy | Solid | mesh approx | render-mesh LOD tolerance vs query tolerance |
**Completed (Phase 0):** `extrude`, `revolve` (profiles: Line/Circle/Arc/LwPolyline),
`add_vertex` (polyline), non-solid `transform` + `transform_many` (Line/Circle/Arc/
Point/LwPolyline). Covered by `phase0_*` tests in `src/app/doc_api.rs`.
## Design invariants to preserve when extending
- Per-op autocommit; **no multi-op write transactions**.
+60 -3
View File
@@ -62,13 +62,11 @@ fixed = { primitive = "Wedge" }
name = "extrude"
args = [{ name = "profile", ty = "EntityRef" }, { name = "direction", ty = "[f64; 3]" }]
op = "Extrude"
status = "unsupported"
[[family.constructor]]
name = "revolve"
args = [{ name = "profile", ty = "EntityRef" }, { name = "pivot", ty = "[f64; 3]" }, { name = "axis", ty = "[f64; 3]" }, { name = "angle", ty = "f64" }]
op = "Revolve"
status = "unsupported"
[[family.method]]
name = "intersect"
@@ -158,7 +156,6 @@ kind = "op"
op = "AddVertex"
args = [{ name = "at", ty = "usize" }, { name = "point", ty = "[f64; 3]" }]
returns = "()"
status = "unsupported"
[[family]]
name = "Point"
@@ -180,6 +177,66 @@ fixed = { curve = "Point" }
bulk = true
returns = "Vec<Point>"
[[family]]
name = "Arc"
acadrust_variant = "Arc"
handle = "ArcCurve"
collection = "curves"
[[family.constructor]]
name = "create_arc"
args = [{ name = "centre", ty = "[f64; 3]" }, { name = "radius", ty = "f64" }, { name = "start_angle", ty = "f64" }, { name = "end_angle", ty = "f64" }]
op = "CreateCurve"
fixed = { curve = "Arc" }
[[family]]
name = "Ellipse"
acadrust_variant = "Ellipse"
handle = "Ellipse"
collection = "curves"
[[family.constructor]]
name = "create_ellipse"
args = [{ name = "centre", ty = "[f64; 3]" }, { name = "major_axis", ty = "[f64; 3]" }, { name = "ratio", ty = "f64" }, { name = "start", ty = "f64" }, { name = "end", ty = "f64" }]
op = "CreateCurve"
fixed = { curve = "Ellipse" }
[[family]]
name = "Spline"
acadrust_variant = "Spline"
handle = "Spline"
collection = "curves"
[[family.constructor]]
name = "create_spline"
args = [{ name = "degree", ty = "i32" }, { name = "control_points", ty = "&[[f64; 3]]" }, { name = "knots", ty = "&[f64]" }, { name = "weights", ty = "&[f64]" }]
op = "CreateCurve"
fixed = { curve = "Spline" }
[[family]]
name = "Ray"
acadrust_variant = "Ray"
handle = "Ray"
collection = "curves"
[[family.constructor]]
name = "create_ray"
args = [{ name = "origin", ty = "[f64; 3]" }, { name = "direction", ty = "[f64; 3]" }]
op = "CreateCurve"
fixed = { curve = "Ray" }
[[family]]
name = "XLine"
acadrust_variant = "XLine"
handle = "XLine"
collection = "curves"
[[family.constructor]]
name = "create_xline"
args = [{ name = "origin", ty = "[f64; 3]" }, { name = "direction", ty = "[f64; 3]" }]
op = "CreateCurve"
fixed = { curve = "XLine" }
# ============================================================================
# Cross-cutting ops (generic handles + bulk) — documented in build.rs
# ============================================================================
+30
View File
@@ -262,6 +262,11 @@ handle!(Line);
handle!(Circle);
handle!(Polyline);
handle!(Point);
handle!(ArcCurve);
handle!(Ellipse);
handle!(Spline);
handle!(Ray);
handle!(XLine);
impl Entity {
pub fn view(&self) -> ApiResult<EntityView> {
@@ -412,6 +417,31 @@ impl CurveCollection {
let (s, id) = self.create_curve(Curve2Spec::Point { position })?;
Ok(Point::new(s, id))
}
pub fn create_arc(&self, centre: [f64; 3], radius: f64, start_angle: f64, end_angle: f64) -> ApiResult<ArcCurve> {
let (s, id) = self.create_curve(Curve2Spec::Arc { centre, radius, start_angle, end_angle })?;
Ok(ArcCurve::new(s, id))
}
pub fn create_ellipse(&self, centre: [f64; 3], major_axis: [f64; 3], ratio: f64, start: f64, end: f64) -> ApiResult<Ellipse> {
let (s, id) = self.create_curve(Curve2Spec::Ellipse { centre, major_axis, ratio, start, end })?;
Ok(Ellipse::new(s, id))
}
pub fn create_spline(&self, degree: i32, control_points: &[[f64; 3]], knots: &[f64], weights: &[f64]) -> ApiResult<Spline> {
let (s, id) = self.create_curve(Curve2Spec::Spline {
degree,
control_points: control_points.to_vec(),
knots: knots.to_vec(),
weights: weights.to_vec(),
})?;
Ok(Spline::new(s, id))
}
pub fn create_ray(&self, origin: [f64; 3], direction: [f64; 3]) -> ApiResult<Ray> {
let (s, id) = self.create_curve(Curve2Spec::Ray { origin, direction })?;
Ok(Ray::new(s, id))
}
pub fn create_xline(&self, origin: [f64; 3], direction: [f64; 3]) -> ApiResult<XLine> {
let (s, id) = self.create_curve(Curve2Spec::XLine { origin, direction })?;
Ok(XLine::new(s, id))
}
/// Bulk-create many points in ONE op (plan §5.3): all-or-nothing, one undo step.
pub fn create_points(&self, positions: &[[f64; 3]]) -> ApiResult<Vec<Point>> {
let specs: Vec<crate::ops::EntitySpec> = positions
+33 -3
View File
@@ -16,8 +16,8 @@ B-rep solid. Host resolves bodies via the solid_models cache (lift-on-miss from
- **create_cone**(`base: [f64; 3], radius: f64, height: f64`) -> `Solid`
- **create_torus**(`centre: [f64; 3], major_radius: f64, minor_radius: f64`) -> `Solid`
- **create_wedge**(`origin: [f64; 3], size: [f64; 3]`) -> `Solid`
- **extrude**(`profile: EntityRef, direction: [f64; 3]`) -> `Solid` — **not yet supported** (returns `ApiError::Unsupported`)
- **revolve**(`profile: EntityRef, pivot: [f64; 3], axis: [f64; 3], angle: f64`) -> `Solid` — **not yet supported** (returns `ApiError::Unsupported`)
- **extrude**(`profile: EntityRef, direction: [f64; 3]`) -> `Solid`
- **revolve**(`profile: EntityRef, pivot: [f64; 3], axis: [f64; 3], angle: f64`) -> `Solid`
### Methods
@@ -48,7 +48,7 @@ B-rep solid. Host resolves bodies via the solid_models cache (lift-on-miss from
### Methods
- **add_vertex**(`at: usize, point: [f64; 3]`) -> `()` — op `AddVertex` — **not yet supported** (returns `ApiError::Unsupported`)
- **add_vertex**(`at: usize, point: [f64; 3]`) -> `()` — op `AddVertex`
## `Point` (acadrust `Point`, collection `curves`)
@@ -57,6 +57,36 @@ B-rep solid. Host resolves bodies via the solid_models cache (lift-on-miss from
- **create_point**(`position: [f64; 3]`) -> `Point`
- **create_points**(`positions: &[[f64; 3]]`) -> `Vec<Point>` *(bulk op)*
## `ArcCurve` (acadrust `Arc`, collection `curves`)
### Constructors (`doc.curves()`)
- **create_arc**(`centre: [f64; 3], radius: f64, start_angle: f64, end_angle: f64`) -> `ArcCurve`
## `Ellipse` (acadrust `Ellipse`, collection `curves`)
### Constructors (`doc.curves()`)
- **create_ellipse**(`centre: [f64; 3], major_axis: [f64; 3], ratio: f64, start: f64, end: f64`) -> `Ellipse`
## `Spline` (acadrust `Spline`, collection `curves`)
### Constructors (`doc.curves()`)
- **create_spline**(`degree: i32, control_points: &[[f64; 3]], knots: &[f64], weights: &[f64]`) -> `Spline`
## `Ray` (acadrust `Ray`, collection `curves`)
### Constructors (`doc.curves()`)
- **create_ray**(`origin: [f64; 3], direction: [f64; 3]`) -> `Ray`
## `XLine` (acadrust `XLine`, collection `curves`)
### Constructors (`doc.curves()`)
- **create_xline**(`origin: [f64; 3], direction: [f64; 3]`) -> `XLine`
## Generic methods (every handle)
Every typed handle (`Solid`, `Line`, `Circle`, `Polyline`, `Point`, `Entity`) is a
@@ -152,6 +152,41 @@
"handle": "Point",
"methods": [],
"name": "Point"
},
{
"acadrust_variant": "Arc",
"collection": "curves",
"handle": "ArcCurve",
"methods": [],
"name": "Arc"
},
{
"acadrust_variant": "Ellipse",
"collection": "curves",
"handle": "Ellipse",
"methods": [],
"name": "Ellipse"
},
{
"acadrust_variant": "Spline",
"collection": "curves",
"handle": "Spline",
"methods": [],
"name": "Spline"
},
{
"acadrust_variant": "Ray",
"collection": "curves",
"handle": "Ray",
"methods": [],
"name": "Ray"
},
{
"acadrust_variant": "XLine",
"collection": "curves",
"handle": "XLine",
"methods": [],
"name": "XLine"
}
],
"layouts": {
+11
View File
@@ -34,6 +34,17 @@ pub enum Curve2Spec {
Circle { centre: [f64; 3], radius: f64 },
Polyline { points: Vec<[f64; 3]>, closed: bool },
Point { position: [f64; 3] },
/// Circular arc, counter-clockwise from `start_angle` to `end_angle` (radians).
Arc { centre: [f64; 3], radius: f64, start_angle: f64, end_angle: f64 },
/// Ellipse: `major_axis` is the major-axis endpoint relative to `centre`;
/// `ratio` = minor/major. `start`/`end` parameters 0..2π (full ellipse = 0..2π).
Ellipse { centre: [f64; 3], major_axis: [f64; 3], ratio: f64, start: f64, end: f64 },
/// NURBS spline (control points + knots + weights; `degree` typically 3).
Spline { degree: i32, control_points: Vec<[f64; 3]>, knots: Vec<f64>, weights: Vec<f64> },
/// A ray from `origin` along `direction` (bounded at origin only).
Ray { origin: [f64; 3], direction: [f64; 3] },
/// An infinite construction line through `origin` along `direction`.
XLine { origin: [f64; 3], direction: [f64; 3] },
}
/// A generic entity-construction payload used by the bulk op