forked from 1b-agent/opencadstudio-upstream
Add ocs_doc_api: object-oriented CAD entity API (v2)
New crate crates/ocs_doc_api: object-centric facade (DocApi -> Document -> collections -> typed handles), transport-agnostic wire layer (DocApiEnvelope = single op | read-only query batch), per-op autocommit (no write transactions), bulk ops with upfront-validation all-or-nothing, structured ApiError, and build-time codegen (spec/entities.toml -> ops/query enums, api_reference.md, binding_schema.json). ocs_plugin_api: append-only PluginRequest::DocApiRequest / PluginResponse:: DocApiResponse (serde_bytes payload) + defaulted HostApi::doc_api_dispatch. Host: DocApiBackend impl for HostSession (src/app/doc_api.rs) mapping to scene primitives (solid_models lift-on-miss, register_prepared_solid_model, meshes metrics), entity conversions (doc_api_convert.rs), HostSession accessors + doc_api_dispatch override. Tests: 592 host lib (incl. 7 DocApi integration tests: create/boolean/query, unknown-id, transform-many all-or-nothing, query-batch cap, 2D/geometric/DWG roundtrips), 8 crate unit tests. Examples + README/ARCHITECTURE/IMPLEMENTATION docs.
This commit is contained in:
1 parent
9129c85f97
commit
e41086b058
44 files changed
+4837
-12
No files matched your search
@@ -0,0 +1,115 @@
|
||||
# ocs_doc_api — API reference (auto-generated)
|
||||
|
||||
Envelope version: `1`
|
||||
|
||||
Do NOT edit: regenerated by `build.rs` from `spec/entities.toml`.
|
||||
|
||||
## `Solid` (acadrust `Solid3D`, collection `solids`)
|
||||
|
||||
B-rep solid. Host resolves bodies via the solid_models cache (lift-on-miss from ACIS SAT).
|
||||
|
||||
### Constructors (`doc.solids()`)
|
||||
|
||||
- **create_cuboid**(`origin: [f64; 3], size: [f64; 3]`) -> `Solid`
|
||||
- **create_sphere**(`centre: [f64; 3], radius: f64`) -> `Solid`
|
||||
- **create_cylinder**(`base: [f64; 3], radius: f64, height: f64`) -> `Solid`
|
||||
- **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`)
|
||||
|
||||
### Methods
|
||||
|
||||
- **intersect**(`other: Solid`) -> `Solid` — op `SolidBoolean`
|
||||
- **union**(`other: Solid`) -> `Solid` — op `SolidBoolean`
|
||||
- **subtract**(`other: Solid`) -> `Solid` — op `SolidBoolean`
|
||||
- **volume**(``) -> `f64` — query `GetVolume`
|
||||
- **centroid**(``) -> `[f64; 3]` — query `GetCentroid`
|
||||
- **intersects**(`other: Solid`) -> `bool` — query `GetIntersects`
|
||||
|
||||
## `Line` (acadrust `Line`, collection `curves`)
|
||||
|
||||
### Constructors (`doc.curves()`)
|
||||
|
||||
- **create_line**(`start: [f64; 3], end: [f64; 3]`) -> `Line`
|
||||
|
||||
## `Circle` (acadrust `Circle`, collection `curves`)
|
||||
|
||||
### Constructors (`doc.curves()`)
|
||||
|
||||
- **create_circle**(`centre: [f64; 3], radius: f64`) -> `Circle`
|
||||
|
||||
## `Polyline` (acadrust `LwPolyline`, collection `curves`)
|
||||
|
||||
### Constructors (`doc.curves()`)
|
||||
|
||||
- **create_polyline**(`points: &[[f64; 3]], closed: bool`) -> `Polyline`
|
||||
|
||||
### Methods
|
||||
|
||||
- **add_vertex**(`at: usize, point: [f64; 3]`) -> `()` — op `AddVertex` — **not yet supported** (returns `ApiError::Unsupported`)
|
||||
|
||||
## `Point` (acadrust `Point`, collection `curves`)
|
||||
|
||||
### Constructors (`doc.curves()`)
|
||||
|
||||
- **create_point**(`position: [f64; 3]`) -> `Point`
|
||||
- **create_points**(`positions: &[[f64; 3]]`) -> `Vec<Point>` *(bulk op)*
|
||||
|
||||
## Generic methods (every handle)
|
||||
|
||||
Every typed handle (`Solid`, `Line`, `Circle`, `Polyline`, `Point`, `Entity`) is a
|
||||
`Copy`, `Send`, `Sync` token `{ ObjectId, session }` and supports:
|
||||
|
||||
- **`id()` -> `ObjectId`** — the stable entity identity.
|
||||
- **`bounds()` -> `Aabb`** — coarse bounding box (solids lift-on-miss; 2D entities
|
||||
computed; later families may be `Unsupported`).
|
||||
- **`transform(placement)`** — in-place rigid similarity (same `ObjectId`). **v1:
|
||||
solids only.**
|
||||
- **`delete()`** — remove the entity (one undo step).
|
||||
|
||||
`Entity` additionally has `view()` (id + kind + bounds) and `as_solid()` (typed
|
||||
downcast when `kind == "Solid3D"`).
|
||||
|
||||
## Collections & cross-cutting
|
||||
|
||||
- **`DocApi`** — root: `document(tab)`, `active_tab()`, `alive()`.
|
||||
- **`Document`** — `solids()` / `curves()` / `entities()` factories + lookup,
|
||||
`revision()`, `assert_revision(rev)` (read-guard), `query_batch(|q| …)` (read-only
|
||||
batch, one round-trip, **no revision bump**).
|
||||
- **`EntityCollection`** — `get(id) -> Entity`, `delete(id)`,
|
||||
`transform_many(&ids, placement)`, `delete_many(&ids)`.
|
||||
- **`OpGroup`** — best-effort client-side failure cleanup (`track` / `commit` /
|
||||
`compensate`). **Not** a transaction: no atomicity, no rollback.
|
||||
|
||||
## Bulk ops — single op, all-or-nothing, item-capped
|
||||
|
||||
`CreateMany` / `TransformMany` / `DeleteMany` are **single** write ops: one undo
|
||||
step, one publish, and **all-or-nothing via upfront validation** (no rollback).
|
||||
Item-capped at `BULK_ITEM_CAP = 100_000` to bound host execution. Use them for
|
||||
bulk data (N ≳ a few hundred); per-op autocommit is the right tool for
|
||||
modeling-scale work.
|
||||
|
||||
## Semantics
|
||||
|
||||
- **Per-op autocommit, no write transactions.** Every write op is ONE atomic host
|
||||
call = one undo step. Multi-op write transactions were removed (security/
|
||||
integrity review).
|
||||
- **Per-op atomicity.** Validate + compute geometry **before** any mutation; a
|
||||
failed op applies nothing and records no undo step. `erase_sources` booleans run
|
||||
only on success.
|
||||
- **Structured errors.** `ApiError::{Validation, Geometry, UnknownId, Unsupported,
|
||||
Transport}`; bulk validation names the failing index; a stale handle returns
|
||||
`UnknownId` (never a panic).
|
||||
|
||||
## Kernel mapping
|
||||
|
||||
| op | kernel call | returns |
|
||||
|---|---|---|
|
||||
| CreateSolid.Cuboid | `cadkernel::brep::make::cuboid` | Option<Body> |
|
||||
| CreateSolid.Sphere | `cadkernel::brep::make::sphere` | Option<Body> |
|
||||
| SolidBoolean | `cadkernel::brep::combine` | Result<Body, Snag> |
|
||||
| Transform | `cadkernel::brep::transform` | Option<Body> |
|
||||
| GetBounds | `cadkernel::brep::body_bounds` | Option<Aabb> |
|
||||
| GetVolume | `cadkernel::brep::mesh_body + divergence` | computed |
|
||||
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"envelope_version": 1,
|
||||
"families": [
|
||||
{
|
||||
"acadrust_variant": "Solid3D",
|
||||
"collection": "solids",
|
||||
"handle": "Solid",
|
||||
"methods": [
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "other",
|
||||
"ty": "Solid"
|
||||
}
|
||||
],
|
||||
"bulk": false,
|
||||
"fixed": {
|
||||
"erase_sources": true,
|
||||
"op": "Intersection"
|
||||
},
|
||||
"kind": "op",
|
||||
"name": "intersect",
|
||||
"op": "SolidBoolean",
|
||||
"query": null,
|
||||
"read_only": false,
|
||||
"returns": "Solid"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "other",
|
||||
"ty": "Solid"
|
||||
}
|
||||
],
|
||||
"bulk": false,
|
||||
"fixed": {
|
||||
"erase_sources": true,
|
||||
"op": "Union"
|
||||
},
|
||||
"kind": "op",
|
||||
"name": "union",
|
||||
"op": "SolidBoolean",
|
||||
"query": null,
|
||||
"read_only": false,
|
||||
"returns": "Solid"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "other",
|
||||
"ty": "Solid"
|
||||
}
|
||||
],
|
||||
"bulk": false,
|
||||
"fixed": {
|
||||
"erase_sources": true,
|
||||
"op": "Difference"
|
||||
},
|
||||
"kind": "op",
|
||||
"name": "subtract",
|
||||
"op": "SolidBoolean",
|
||||
"query": null,
|
||||
"read_only": false,
|
||||
"returns": "Solid"
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"bulk": false,
|
||||
"fixed": null,
|
||||
"kind": "query",
|
||||
"name": "volume",
|
||||
"op": null,
|
||||
"query": "GetVolume",
|
||||
"read_only": true,
|
||||
"returns": "f64"
|
||||
},
|
||||
{
|
||||
"args": [],
|
||||
"bulk": false,
|
||||
"fixed": null,
|
||||
"kind": "query",
|
||||
"name": "centroid",
|
||||
"op": null,
|
||||
"query": "GetCentroid",
|
||||
"read_only": true,
|
||||
"returns": "[f64; 3]"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "other",
|
||||
"ty": "Solid"
|
||||
}
|
||||
],
|
||||
"bulk": false,
|
||||
"fixed": null,
|
||||
"kind": "query",
|
||||
"name": "intersects",
|
||||
"op": null,
|
||||
"query": "GetIntersects",
|
||||
"read_only": true,
|
||||
"returns": "bool"
|
||||
}
|
||||
],
|
||||
"name": "Solid"
|
||||
},
|
||||
{
|
||||
"acadrust_variant": "Line",
|
||||
"collection": "curves",
|
||||
"handle": "Line",
|
||||
"methods": [],
|
||||
"name": "Line"
|
||||
},
|
||||
{
|
||||
"acadrust_variant": "Circle",
|
||||
"collection": "curves",
|
||||
"handle": "Circle",
|
||||
"methods": [],
|
||||
"name": "Circle"
|
||||
},
|
||||
{
|
||||
"acadrust_variant": "LwPolyline",
|
||||
"collection": "curves",
|
||||
"handle": "Polyline",
|
||||
"methods": [
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "at",
|
||||
"ty": "usize"
|
||||
},
|
||||
{
|
||||
"name": "point",
|
||||
"ty": "[f64; 3]"
|
||||
}
|
||||
],
|
||||
"bulk": false,
|
||||
"fixed": null,
|
||||
"kind": "op",
|
||||
"name": "add_vertex",
|
||||
"op": "AddVertex",
|
||||
"query": null,
|
||||
"read_only": false,
|
||||
"returns": "()"
|
||||
}
|
||||
],
|
||||
"name": "Polyline"
|
||||
},
|
||||
{
|
||||
"acadrust_variant": "Point",
|
||||
"collection": "curves",
|
||||
"handle": "Point",
|
||||
"methods": [],
|
||||
"name": "Point"
|
||||
}
|
||||
],
|
||||
"layouts": {
|
||||
"ObjectId": {
|
||||
"struct": [
|
||||
{
|
||||
"name": "0",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Operation": {
|
||||
"enum": [
|
||||
"CreateSolid(SolidPrimitive)",
|
||||
"CreateCurve(Curve2Spec)",
|
||||
"Extrude { profile: ObjectId, direction: [f64; 3] }",
|
||||
"Revolve { profile: ObjectId, axis: ([f64; 3], [f64; 3]), angle: f64 }",
|
||||
"Transform { id: ObjectId, placement: PlacementSpec }",
|
||||
"SolidBoolean { op: BoolOp, a: ObjectId, b: ObjectId, erase_sources: bool }",
|
||||
"AddVertex { id: ObjectId, at: usize, point: [f64; 3] }",
|
||||
"Delete { id: ObjectId }",
|
||||
"CreateMany(Vec<EntitySpec>)",
|
||||
"TransformMany { ids: Vec<ObjectId>, placement: PlacementSpec }",
|
||||
"DeleteMany(Vec<ObjectId>)"
|
||||
]
|
||||
},
|
||||
"Query": {
|
||||
"enum": [
|
||||
"GetEntity { id: ObjectId }",
|
||||
"GetBounds { id: ObjectId }",
|
||||
"GetCentroid { id: ObjectId }",
|
||||
"GetVolume { id: ObjectId }",
|
||||
"GetIntersects { a: ObjectId, b: ObjectId }",
|
||||
"GetGeometryRevision"
|
||||
]
|
||||
}
|
||||
},
|
||||
"object_model": {
|
||||
"collections": [
|
||||
"solids",
|
||||
"curves",
|
||||
"entities"
|
||||
],
|
||||
"document": "Document",
|
||||
"handles": [
|
||||
"Entity",
|
||||
"Solid",
|
||||
"Line",
|
||||
"Circle",
|
||||
"Polyline",
|
||||
"Point"
|
||||
],
|
||||
"root": "DocApi"
|
||||
},
|
||||
"schema": "ocs_doc_api/binding_schema"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"ObjectId": { "struct": [ { "name": "0", "type": "u64" } ] },
|
||||
"Operation": {
|
||||
"enum": [
|
||||
"CreateSolid(SolidPrimitive)",
|
||||
"CreateCurve(Curve2Spec)",
|
||||
"Extrude { profile: ObjectId, direction: [f64; 3] }",
|
||||
"Revolve { profile: ObjectId, axis: ([f64; 3], [f64; 3]), angle: f64 }",
|
||||
"Transform { id: ObjectId, placement: PlacementSpec }",
|
||||
"SolidBoolean { op: BoolOp, a: ObjectId, b: ObjectId, erase_sources: bool }",
|
||||
"AddVertex { id: ObjectId, at: usize, point: [f64; 3] }",
|
||||
"Delete { id: ObjectId }",
|
||||
"CreateMany(Vec<EntitySpec>)",
|
||||
"TransformMany { ids: Vec<ObjectId>, placement: PlacementSpec }",
|
||||
"DeleteMany(Vec<ObjectId>)"
|
||||
]
|
||||
},
|
||||
"Query": {
|
||||
"enum": [
|
||||
"GetEntity { id: ObjectId }",
|
||||
"GetBounds { id: ObjectId }",
|
||||
"GetCentroid { id: ObjectId }",
|
||||
"GetVolume { id: ObjectId }",
|
||||
"GetIntersects { a: ObjectId, b: ObjectId }",
|
||||
"GetGeometryRevision"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// GENERATED by build.rs from spec/entities.toml — do NOT edit by hand.
|
||||
// Committed snapshot for reviewability (plan §10); build.rs regenerates and CI
|
||||
// fails on drift (`--check`). Append new variants at the END only (bincode
|
||||
// discriminant stability, plan §7).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::id::ObjectId;
|
||||
use crate::ops::{BoolOp, Curve2Spec, EntitySpec, PlacementSpec, SolidPrimitive};
|
||||
|
||||
/// A typed write operation (plan §5). Each variant is ONE atomic host call = one
|
||||
/// undo step. Append new variants at the END only.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum Operation {
|
||||
// construction
|
||||
CreateSolid(SolidPrimitive),
|
||||
CreateCurve(Curve2Spec),
|
||||
Extrude { profile: ObjectId, direction: [f64; 3] },
|
||||
Revolve { profile: ObjectId, axis: ([f64; 3], [f64; 3]), angle: f64 },
|
||||
// modification
|
||||
Transform { id: ObjectId, placement: PlacementSpec },
|
||||
SolidBoolean { op: BoolOp, a: ObjectId, b: ObjectId, erase_sources: bool },
|
||||
AddVertex { id: ObjectId, at: usize, point: [f64; 3] },
|
||||
Delete { id: ObjectId },
|
||||
// Bulk ops (single op, upfront-validation all-or-nothing — NOT a transaction, §5.3)
|
||||
CreateMany(Vec<EntitySpec>),
|
||||
TransformMany { ids: Vec<ObjectId>, placement: PlacementSpec },
|
||||
DeleteMany(Vec<ObjectId>),
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// GENERATED by build.rs from spec/entities.toml — do NOT edit by hand.
|
||||
// Committed snapshot for reviewability (plan §10); build.rs regenerates and CI
|
||||
// fails on drift (`--check`). Append new variants at the END only (bincode
|
||||
// discriminant stability, plan §7).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::id::ObjectId;
|
||||
|
||||
/// A typed read query (plan §5). Read-only: no mutation, no undo, no revision
|
||||
/// bump. Safe to batch (`EnvelopeBody::Queries`). Append new variants at the END only.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum Query {
|
||||
GetEntity { id: ObjectId },
|
||||
GetBounds { id: ObjectId },
|
||||
GetCentroid { id: ObjectId },
|
||||
GetVolume { id: ObjectId },
|
||||
/// Whether two entities' bounds overlap (two-phase conditional modelling).
|
||||
GetIntersects { a: ObjectId, b: ObjectId },
|
||||
GetGeometryRevision,
|
||||
}
|
||||
Reference in new issue
Block a user