pub struct Model { /* private fields */ }Expand description
A document: topology, placements, geometry, and where every entity came from.
Implementations§
Source§impl Model
impl Model
Sourcepub fn with_tolerances(tolerances: Tolerances) -> Self
pub fn with_tolerances(tolerances: Tolerances) -> Self
An empty model at a given unit scale.
A document has a scale, and it is the document’s rather than each
call’s: a model authored in metres does not become a model in
millimetres because one caller passed the default. Algorithms still take
a Tolerances argument (that is deliberate, since a caller may want
to work coarser or finer than the document’s own setting for one
operation), but the document now says what it was built at, so a
mismatch is visible rather than assumed away.
Sourcepub const fn tolerances(&self) -> Tolerances
pub const fn tolerances(&self) -> Tolerances
The tolerances this document was built at.
Sourcepub fn from_parts(parts: ModelParts) -> OgeomResult<Self>
pub fn from_parts(parts: ModelParts) -> OgeomResult<Self>
Assemble a model from parts read back from a file.
The one way into a Model that does not go through its builders, and
it exists for one reason: a builder mints an identity for every node
it makes (docs/DATA_MODEL.md §8). A document rebuilt through the
builders is therefore a different document from the one that was
written (every EntityId renumbered, every provenance record
replaced by a fresh Primitive one), and every reference into it,
which is the thing provenance exists to keep alive, is dead. Reading a
file has to reproduce the document it describes, identities and all.
This is not a hole in “the builder is the sole mutation path”. Nothing here mutates an existing model; it assembles a new one, and it checks the structural invariants the builders check before handing it back, so a corrupt file is an error, not a model that answers wrongly.
§Errors
OgeomError::Dangling if a node names a
child, a datum, a piece of geometry or an identity that is not there;
OgeomError::Construction if a node’s
data does not match its kind, or a child is of the wrong kind for its
parent.
Sourcepub fn absorb(
&mut self,
parts: ModelParts,
roots: &[Shape],
) -> OgeomResult<Absorbed>
pub fn absorb( &mut self, parts: ModelParts, roots: &[Shape], ) -> OgeomResult<Absorbed>
Absorb another document’s parts into this model.
Model::from_parts lands a document in a fresh model; this lands
one in a model that already has things in it: the operation behind
bringing a serialized tool body into a live document so a boolean can
use it. Everything the parts carry is appended: nodes, datums,
geometry, provenance and identities all keep their relative structure,
shifted past what the model already holds. The result behaves exactly
as if it had been built here, because after the shift it is
indistinguishable from having been.
roots are the shapes the source document named, in the unbound state
a reader leaves them; they come back bound to this model. The returned
Absorbed::entities table says where every source identity landed,
which is what a caller holding references against the source document
resolves them through.
Three deliberate refusals, each an error rather than a guess:
- Units. A document authored at another scale is refused, not rescaled; rescaling is a real feature with real decisions in it, and silently absorbing metres into millimetres is a wrong model.
- Bound handles. Parts whose handles already name an arena did not come from a reader; absorbing them would alias whatever those handles meant elsewhere. Serialization is the one road in.
- A model with holes. Absorbing appends by offset, which is only sound while the target’s arenas have only ever been appended to. Nothing in this crate removes, so this cannot trigger today; it is checked so a future that removes gets an error, not aliasing.
The current operation is left alone: absorb mints no identities, it
transplants a table, and the absorbed provenance keeps its source
OpIds verbatim, meaningful in the source document’s rebuild, kept
because renumbering them would orphan the source’s own references.
§Errors
OgeomError::Construction for
the three refusals above;
OgeomError::Dangling if the parts
do not describe themselves: a handle that does not resolve, a
derivation from an identity no entry issued, a root naming a node that
is not there.
On an error past the up-front gates, the model’s prior contents are untouched and still fully usable: an absorbed subgraph is self-contained (its shifted handles cannot reach below the append line), and no identity is committed until every check has passed. What a failed absorb can leave behind is unreachable appended entries, which cost memory and mean nothing.
Sourcepub fn bind(&self, shape: &Shape) -> OgeomResult<Shape>
pub fn bind(&self, shape: &Shape) -> OgeomResult<Shape>
Bind an unscoped shape to this model.
A handle rebuilt by a reader names no arena, so it resolves nowhere until it is told which document it belongs to. This is how a reader says so, and it verifies the answer, so a file naming a node that is not there is an error rather than a shape that fails mysteriously later.
It will not re-home a shape that already belongs to another model. That is exactly the mistake scoping exists to catch, and quietly relabelling it would hand back a shape that resolves and answers about the wrong entity.
§Errors
OgeomError::Construction if the shape
already belongs to a different model;
OgeomError::Dangling if it does not resolve
here once bound.
Sourcepub fn bind_location(&self, location: &Location) -> OgeomResult<Location>
pub fn bind_location(&self, location: &Location) -> OgeomResult<Location>
Bind a bare location to this model’s datum store.
The persistence path’s sibling of Model::bind: a location read
from a file names datum handles that are unscoped until the store
that holds them exists. Binding checks them too: a chain naming a
datum not in this model is an error here rather than wherever it is
first resolved.
§Errors
OgeomError::Dangling if the chain
names a datum not in this model.
Sourcepub const fn begin_operation(&mut self) -> OpId
pub const fn begin_operation(&mut self) -> OpId
Begin a new operation, and return its identifier.
Every node created from here on is attributed to it until the next call.
The counter is deterministic (the third operation in a rebuild is
OpId(3) every time), which is what lets provenance survive a parameter
change (docs/DATA_MODEL.md §8).
Sourcepub const fn current_operation(&self) -> OpId
pub const fn current_operation(&self) -> OpId
The operation nodes are currently attributed to.
Sourcepub fn identity_of(&self, shape: &Shape) -> Option<EntityId>
pub fn identity_of(&self, shape: &Shape) -> Option<EntityId>
The stable identity of a shape’s node.
Distinct from its arena handle: the handle says where the data is and dies when the shape is rebuilt, while this says what the entity is and survives.
Sourcepub fn provenance_of(&self, shape: &Shape) -> Option<&Provenance>
pub fn provenance_of(&self, shape: &Shape) -> Option<&Provenance>
Where a shape’s node came from.
Sourcepub const fn provenance(&self) -> &ProvenanceTable
pub const fn provenance(&self) -> &ProvenanceTable
The provenance table.
Sourcepub fn roots_of(&self, shape: &Shape) -> Vec<EntityId>
pub fn roots_of(&self, shape: &Shape) -> Vec<EntityId>
Trace a shape back to the entities it ultimately came from.
How a reference into a rebuilt model is resolved: find what the user originally picked, then find what that became.
Sourcepub fn shape_of(&self, id: EntityId) -> Option<Shape>
pub fn shape_of(&self, id: EntityId) -> Option<Shape>
The shape carrying a given identity, if this document has one.
The inverse of Model::identity_of, and the answer to “I kept a
reference and the document has been saved and reloaded since”. A raw
Shape cannot survive that: the reloaded document is a new set of
arenas and Model::bind refuses a handle from another one, on
purpose. An EntityId can, because it names what the entity is
rather than where it sits (docs/DATA_MODEL.md §8), and that is the
whole reason it exists.
Returns the shape in its default placement and orientation. A caller that wants a particular occurrence explores from here.
Sourcepub fn set_derived(
&mut self,
shape: &Shape,
from: &[Shape],
role: Role,
) -> OgeomResult<EntityId>
pub fn set_derived( &mut self, shape: &Shape, from: &[Shape], role: Role, ) -> OgeomResult<EntityId>
Record that a node was derived from other entities.
Overwrites the Primitive attribution a builder assigns by default.
An operation that splits or reshapes existing topology calls this, and
what it records is what a later rebuild will match against.
§Errors
OgeomError::Dangling if the shape does not
resolve in this model.
Sourcepub const fn datums(&self) -> &DatumStore
pub const fn datums(&self) -> &DatumStore
The placement datums.
Sourcepub const fn geometry(&self) -> &GeometryStore
pub const fn geometry(&self) -> &GeometryStore
The geometry.
Sourcepub const fn geometry_mut(&mut self) -> &mut GeometryStore
pub const fn geometry_mut(&mut self) -> &mut GeometryStore
Mutable access to the geometry, for adding curves and surfaces.
Sourcepub fn add_datum(&mut self, transform: Transform) -> DatumId
pub fn add_datum(&mut self, transform: Transform) -> DatumId
Intern a transform for use in placements.
Sourcepub fn node_by_id(&self, id: TShapeId) -> Option<&TShape>
pub fn node_by_id(&self, id: TShapeId) -> Option<&TShape>
The node behind a handle.
Sourcepub fn node_mut(&mut self, shape: &Shape) -> Option<&mut TShape>
pub fn node_mut(&mut self, shape: &Shape) -> Option<&mut TShape>
Mutable access to the node behind a shape’s handle.
For attaching geometry to an entity that already exists: a pcurve joining an edge to a face it has just come to bound. Structural change still goes through the builders; this reaches the node’s data, which no invariant here constrains on its own.
Sourcepub fn kind_of(&self, shape: &Shape) -> OgeomResult<ShapeType>
pub fn kind_of(&self, shape: &Shape) -> OgeomResult<ShapeType>
What kind of shape this is.
§Errors
OgeomError::Dangling if the handle does not
resolve in this model.
Sourcepub fn tolerance_of(&self, shape: &Shape) -> OgeomResult<Option<Tolerance>>
pub fn tolerance_of(&self, shape: &Shape) -> OgeomResult<Option<Tolerance>>
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Number of topology nodes.
Sourcepub fn nodes(&self) -> impl Iterator<Item = (TShapeId, &TShape)>
pub fn nodes(&self) -> impl Iterator<Item = (TShapeId, &TShape)>
Every topology node, with its handle, in arena order.
For writing a document out. Traversal from a root shape reaches only what that root bounds; a document is everything in it.
Sourcepub fn identities(&self) -> impl Iterator<Item = (TShapeId, EntityId)>
pub fn identities(&self) -> impl Iterator<Item = (TShapeId, EntityId)>
Every node that has been given an identity, with it.
Sourcepub fn add_vertex(&mut self, data: VertexData) -> Shape
pub fn add_vertex(&mut self, data: VertexData) -> Shape
Add a vertex.
Sourcepub fn add_point(&mut self, point: Point) -> Shape
pub fn add_point(&mut self, point: Point) -> Shape
Add a vertex at point with the minimum tolerance.
Sourcepub fn add_edge(
&mut self,
data: EdgeData,
bounds: &[Shape],
) -> OgeomResult<Shape>
pub fn add_edge( &mut self, data: EdgeData, bounds: &[Shape], ) -> OgeomResult<Shape>
Add an edge bounded by the given vertices.
The vertices are the edge’s ends, in order. A closed edge (a full circle) names the same vertex twice rather than once, so that walking its boundary yields a start and an end as every other edge does.
§Errors
OgeomError::Construction if a bound is
not a vertex, or if there are more than two;
OgeomError::Invariant if a vertex’s
tolerance is tighter than the edge’s, breaking the containment rule.
Sourcepub fn add_wire(&mut self, edges: &[Shape]) -> OgeomResult<Shape>
pub fn add_wire(&mut self, edges: &[Shape]) -> OgeomResult<Shape>
Add a wire from a sequence of edges.
§Errors
OgeomError::Construction if a child is
not an edge, or the wire is empty.
Sourcepub fn add_face(
&mut self,
data: FaceData,
wires: &[Shape],
) -> OgeomResult<Shape>
pub fn add_face( &mut self, data: FaceData, wires: &[Shape], ) -> OgeomResult<Shape>
Add a face bounded by the given wires.
A face with no wires covers its surface’s whole domain, and is recorded as naturally restricted.
§Errors
OgeomError::Construction if a bound is
not a wire; OgeomError::Invariant if the
containment rule is broken.
Sourcepub fn add_shell(&mut self, faces: &[Shape]) -> OgeomResult<Shape>
pub fn add_shell(&mut self, faces: &[Shape]) -> OgeomResult<Shape>
Add a shell from a set of faces.
§Errors
OgeomError::Construction if a child is
not a face, or the shell is empty.
Sourcepub fn add_solid(&mut self, shells: &[Shape]) -> OgeomResult<Shape>
pub fn add_solid(&mut self, shells: &[Shape]) -> OgeomResult<Shape>
Add a solid bounded by the given shells.
§Errors
OgeomError::Construction if a child is
not a shell, or the solid is empty.
Sourcepub fn add_compsolid(&mut self, solids: &[Shape]) -> OgeomResult<Shape>
pub fn add_compsolid(&mut self, solids: &[Shape]) -> OgeomResult<Shape>
Add a compsolid from solids sharing faces.
§Errors
OgeomError::Construction if a child is
not a solid, or it is empty.
Sourcepub fn add_compound(&mut self, shapes: &[Shape]) -> OgeomResult<Shape>
pub fn add_compound(&mut self, shapes: &[Shape]) -> OgeomResult<Shape>
Add a compound of arbitrary shapes.
The one container with no type constraint; that is what a compound is for. It may be empty, since an empty result is a legitimate answer from a boolean and needs somewhere to live.
§Errors
OgeomError::Dangling if a child does not
resolve in this model.
Sourcepub fn children_of(&self, shape: &Shape) -> OgeomResult<Vec<Shape>>
pub fn children_of(&self, shape: &Shape) -> OgeomResult<Vec<Shape>>
The direct children of a shape, with this shape’s placement and orientation composed onto each.
The single most important method on the model, and the reason traversal is correct by default rather than by discipline: a child’s placement in the world is its parent’s composed with its own, and its orientation is its parent’s composed with its own. Returning raw children would leave every caller to remember both, and the failure is silent: face normals that flip inconsistently, sub-shapes drawn at the origin.
§Errors
OgeomError::Dangling if the shape does not
resolve in this model.
Sourcepub fn ordered_children_of(&self, shape: &Shape) -> OgeomResult<Vec<Shape>>
pub fn ordered_children_of(&self, shape: &Shape) -> OgeomResult<Vec<Shape>>
A shape’s children in traversal order.
The same shapes as Model::children_of, but with the list reversed
when the parent is reversed. Order carries meaning for a wire (its
edges run head to tail), and reversing a wire has to reverse the walk as
well as each edge, or consecutive edges stop sharing a vertex and the
boundary comes apart. For a shell or a solid the order means nothing and
the reversal is invisible.
Model::children_of stays the raw accessor: it returns what is
stored, which is what a rebuild or a comparison wants.
§Errors
OgeomError::Dangling if the shape does not
resolve in this model.
Sourcepub fn widen(&mut self, shape: &Shape, to: Tolerance) -> OgeomResult<()>
pub fn widen(&mut self, shape: &Shape, to: Tolerance) -> OgeomResult<()>
Widen a shape’s tolerance, and every sub-shape’s with it.
The cascade is the point. The containment rule is transitive: a face’s edges must be no tighter than the face, and those edges’ vertices no tighter than the edges. Widening only one level leaves the rule broken two levels down, where nothing will notice until a containment test quietly answers about geometry that does not meet.
Tolerances only ever grow, so this is the sanctioned repair: raise what bounds, never lower what is bounded.
§Errors
OgeomError::Dangling if the shape, or
anything below it, does not resolve in this model.
Sourcepub fn check_tolerances(&self, root: &Shape) -> OgeomResult<()>
pub fn check_tolerances(&self, root: &Shape) -> OgeomResult<()>
Verify the containment rule across a whole shape tree.
docs/DATA_MODEL.md §5. Walks parent to child and checks that whatever
bounds is no tighter than what it bounds.
§Errors
OgeomError::Invariant at the first
violation, naming the two shape types involved.
Sourcepub fn same_position(
&self,
a: &Shape,
b: &Shape,
tol: Tolerances,
) -> OgeomResult<bool>
pub fn same_position( &self, a: &Shape, b: &Shape, tol: Tolerances, ) -> OgeomResult<bool>
Whether two shapes coincide in position, comparing composed transforms.
§Errors
Sourcepub fn placed(&mut self, shape: &Shape, transform: Transform) -> Shape
pub fn placed(&mut self, shape: &Shape, transform: Transform) -> Shape
A shape placed by an additional transform.
Interns the transform and composes it onto the shape’s placement, so the underlying node (and all its geometry) is shared rather than copied. Placing ten thousand instances of a part costs ten thousand short chains and one copy of the geometry.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Model
impl RefUnwindSafe for Model
impl Send for Model
impl Sync for Model
impl Unpin for Model
impl UnsafeUnpin for Model
impl UnwindSafe for Model
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.