Skip to main content

Model

Struct Model 

Source
pub struct Model {
    pub vertices: OrdMap<VertexId, Vertex>,
    pub edges: OrdMap<EdgeId, Edge>,
    pub coedges: OrdMap<CoedgeId, Coedge>,
    pub faces: OrdMap<FaceId, Face>,
    pub shells: OrdMap<ShellId, Shell>,
    pub solids: OrdMap<SolidId, Solid>,
    pub wires: OrdMap<WireId, Wire>,
    /* private fields */
}
Expand description

The top-level boundary-representation model.

Vertex, Edge, Coedge, Face, Shell and Solid are the entities genuinely shared by reference (an edge by its two coedges via opposite, a vertex by every incident edge, and so on) — these live here in arenas and are referenced by stable typed IDs. Their geometry (a Curve3 per Edge, a Curve2 pcurve per Coedge, a NurbSurface per Face) is owned 1:1 by that entity directly rather than through another arena, since nothing ever needs to reference it independently. A loop has no entity of its own: each entry of Face::boundaries just anchors a CoedgeId whose next/prev cycle traces the whole loop.

Hierarchy (each arrow means “references one or more”):

Solid ──▶ Shell(s) ──▶ Face(s) [+Surface] ──▶ Coedge(s) [+Curve2] ──▶ Edge [+Curve3] ──▶ Vertex
Wire ──▶ Edge(s) [+Curve3], Vertex(es)

A Wire holds edges and vertices that bound no face: curves in space. V - E + F - L = 2 * (Scalar - G), E = 2C for a single-shell solid with genus G, where V = #vertices, E = #edges, F = #faces, L = #hole loops (i.e. Σ (face.boundaries.len() - 1), not counting each face’s mandatory outer loop), Scalar = #shells, and G = genus. Euler’s formula generalizes to multiple shells and/or genus > 0.

Creation (insert_*), lookup (get_*), and iteration (iterate_*) methods each live in their own private submodule (model::create, model::get, model::iterate) — all still just plain inherent Model methods from the outside.

The arenas are ordered by id, which is the order of creation: every walk over a model’s entities — validation, a boolean’s classification, the remesh’s pairing of faces — sees them in the same order on every run. A HashMap iterates in an order seeded afresh in each process, and a validation failure that depended on which face pair was checked first appeared and disappeared between runs.

Fields§

§vertices: OrdMap<VertexId, Vertex>§edges: OrdMap<EdgeId, Edge>§coedges: OrdMap<CoedgeId, Coedge>§faces: OrdMap<FaceId, Face>§shells: OrdMap<ShellId, Shell>§solids: OrdMap<SolidId, Solid>§wires: OrdMap<WireId, Wire>

Edges and vertices bounding no face (see Wire).

Implementations§

Source§

impl Model

Source

pub fn build_body(&mut self, spec: BodySpec) -> GeopResult<BuiltBody>

Adds the body spec describes, once BodySpec::check has found it consistent — nothing is added otherwise. One without faces is added as a Wire.

Source

pub fn body_spec( &self, faces: &[FaceId], solid: bool, ) -> GeopResult<(BodySpec, SpecSources)>

faces described as a body of one shell — a solid’s if solid — with every vertex and edge they use, copied: building it makes a copy of them, sharing nothing with the originals. Also says where in the model each entity of the description came from.

Source§

impl Model

Source

pub fn assemble_solid( &mut self, consumed: &[Body], keep: &[FaceId], ) -> GeopResult<Option<SolidId>>

Replace the bodies consumed by one new solid bounded by keep, which must be faces of those bodies: a shell for each connected set of them, faces sharing an edge being connected. A boolean that cuts a bar in two, or leaves a void inside a block, so keeps two shells, each a closed surface of its own (see Solid). Every other face the bodies owned is deleted, and so is everything no face reaches any more.

What a boolean does last, once it has decided which faces survive. Ok(None) (and no solid) when keep is empty: the result is empty, which is an answer rather than a failure — and also how a body is deleted outright.

Deletion is by reachability rather than by tracking what was split or re-homed on the way here, so no dangling id can be left behind — and it only ever deletes from what consumed owned: faces of any other body in the model are none of this operation’s business.

Source

pub fn assemble_sheet( &mut self, consumed: &[Body], keep: &[FaceId], ) -> GeopResult<Option<ShellId>>

Like Model::assemble_solid, but keep becomes a sheet — faces standing on their own, in one shell whether connected or not — rather than a solid: what is left of a sheet once it is trimmed.

Source

pub fn merge_solids(&mut self, into: SolidId, from: SolidId) -> GeopResult<()>

Move every shell of from into into, deleting from: one solid of both bodies. Only valid for bodies that don’t touch — nothing is intersected, so two overlapping ones would make a solid whose shells cross; combining those is a boolean union’s job.

Source§

impl Model

Source

pub fn merge_edge( &mut self, edge_into_id: EdgeId, edge_deleted_id: EdgeId, reversed: bool, ) -> GeopResult<()>

Merges edge_deleted_id into edge_into_id: repoints every coedge tracing the deleted edge to the surviving one, then drops the now-unreferenced edge. reversed says whether the deleted edge ran start<->end the other way round relative to the surviving one — if so, every repointed coedge’s sense is flipped so it keeps tracing the same physical direction it always did.

TODO: edge_into_id‘s own curve currently stays exactly as it was — it should instead be widened (unioned) to certainly contain the deleted edge’s curve too, so the kept edge’s geometry honestly reflects both original edges’ combined tolerance instead of silently favoring whichever one happened to survive.

Source§

impl Model

Source

pub fn merge_vertex( &mut self, vertex_into_id: VertexId, vertex_deleted_id: VertexId, ) -> GeopResult<()>

Merges vertex_deleted_id into vertex_into_id: unions their points into vertex_into_id, repoints every edge/coedge/boundary reference from vertex_deleted_id to vertex_into_id, then drops the now -unreferenced vertex.

Source§

impl Model

Source

pub fn reverse_face(&mut self, face_id: FaceId) -> GeopResult<()>

Turn face_id’s material side around, so its normal points the other way.

Orientation in this kernel lives in the surface’s parametrization — there is no flag on a face — so flipping it means mirroring the surface’s u (see geop_core_geometry::nurb_surface::NurbSurface::reverse_u) and applying the identical mirror to every pcurve drawn on it. The domain is unchanged by the mirror, so the trim loops still describe the same region of the same surface; only Su x Sv reverses.

Deliberately touches nothing but this face’s surface and its own pcurves. The 3-D curves, the edges, the vertices and every coedge on the other side of those edges are untouched, which is what lets a boolean flip one operand’s faces while they stay glued to the other’s along shared edges.

Source§

impl Model

Source

pub fn splice_edge_into_face( &mut self, edge_id: EdgeId, face_id: FaceId, max_nodes: usize, min_subdivision_size: Scalar, ) -> GeopResult<Option<FaceId>>

Splice an already-existing edge_id into face_id’s boundary structure, as a forward/reversed coedge pair whose pcurves it fits onto the face’s own surface.

The edge already exists because two faces are meant to share it (the whole point of imprinting an intersection curve), so this rewires loops around the edge it’s handed rather than making one.

What it does depends on how much of the edge’s own topology the face already knows about, which is the only thing that determines what a correct result even looks like:

edge’s start/end vertex on this face’s boundaryresult
neithera new self-contained ring floating inside the face — a hole
exactly onea spur (out along the edge and back) inserted into that vertex’s own loop
both, same holethat hole is divided into two holes
both, two different holesthose holes are merged into one
both, a hole and the outer loopthe hole is absorbed into the outer loop
both, the outer loopthe face is split into two faces

Splitting the face, and dividing a hole (whose material side becomes a face of its own), are the cases that create a face; its id is returned, None otherwise. The face split is the only one that has to reclassify the old face’s holes — each now lies inside exactly one of the two halves (see [split_face]). A spur changes no connectivity at all, being “wire” topology rather than a real trim boundary.

Source§

impl Model

Source

pub fn split_edge_at_vertex( &mut self, edge_id: EdgeId, edge_t: Scalar, vertex_id: VertexId, max_nodes: usize, min_subdivision_size: Scalar, ) -> GeopResult<EdgeId>

Splits edge_id at edge_t into two edges joined at the pre-existing vertex_id (which must already coincide with edge.curve.evaluate(edge_t)), and splits every coedge tracing edge_id to match. edge_id itself keeps the first (start -> vertex) segment; the new edge holding the second (vertex -> end) segment, in the same direction, is returned.

max_nodes/min_subdivision_size bound the BFS search used to locate each coedge’s own pcurve parameter for the split (see curve_could_contain).

Source§

impl Model

Source

pub fn transform_body( &mut self, body: impl Into<Body>, map: &dyn Deformation, ) -> GeopResult<()>

Moves the whole of body by map in place — a rigid motion, a translation, a mirror, a scaling (geop_core_math::primitives::Motion) or a deformation about an axis (geop_core_geometry::deform::AxialMap): every vertex, every edge’s curve and every face’s surface. The pcurves stay as they are: every map keeps the surfaces’ parametrization, so each pcurve still traces the same patch, now moved. Nothing is created or deleted, and the body stays connected exactly as it was.

A map made of pieces moves each edge and face by the piece it lies in, asking for a point of it (the curve’s middle, a point inside the face) only where its control points straddle two pieces: the body has to be divided first where it crosses from one to the next.

A mirror turns every surface’s normal into the body (see geop_core_geometry::nurb_surface::NurbSurface::transform), so it also turns every face around (Model::reverse_face): a mirrored solid is a valid solid again, its normals pointing out. Each edge is shared by two faces of the body, both turned, so their coedges stay opposite.

A translation moves every point by one addition per coordinate; a rotation or a mirror encloses its result honestly, widening every point by what its rounded matrix leaves open.

Source§

impl Model

Source

pub fn face_area(&self, face: FaceId) -> GeopResult<(Scalar, bool)>

The area of the face face, and whether its quadrature converged.

Source

pub fn solid_area(&self, solid: SolidId) -> GeopResult<(Scalar, bool)>

The area of the boundary of solid: the sum of its faces’ areas, and whether every face’s quadrature converged.

Source

pub fn mass_properties(&self, solid: SolidId) -> GeopResult<MassProperties>

The mass properties of solid, of density one: what its shape alone decides, and a material scales (see MassProperties::with_density) — so they can be kept for a shape, whatever it is made of.

Fails for a solid whose faces do not enclose a volume definitely greater than zero — its faces pointing into the material, or its volume not resolved by the quadrature — naming it.

Source§

impl Model

Source

pub fn insert_vertex(&mut self, v: Vertex) -> VertexId

Source

pub fn insert_edge(&mut self, e: Edge) -> EdgeId

Source

pub fn insert_coedge(&mut self, c: Coedge) -> CoedgeId

Source

pub fn insert_face(&mut self, f: Face) -> FaceId

Source

pub fn insert_shell(&mut self, s: Shell) -> ShellId

Source

pub fn insert_solid(&mut self, s: Solid) -> SolidId

Source

pub fn insert_wire(&mut self, w: Wire) -> WireId

Source§

impl Model

Source

pub fn get_vertex(&self, id: VertexId) -> GeopResult<&Vertex>

Source

pub fn get_vertex_mut(&mut self, id: VertexId) -> GeopResult<&mut Vertex>

Source

pub fn get_edge(&self, id: EdgeId) -> GeopResult<&Edge>

Source

pub fn get_edge_mut(&mut self, id: EdgeId) -> GeopResult<&mut Edge>

Source

pub fn get_coedge(&self, id: CoedgeId) -> GeopResult<&Coedge>

Source

pub fn get_coedge_mut(&mut self, id: CoedgeId) -> GeopResult<&mut Coedge>

Source

pub fn get_face(&self, id: FaceId) -> GeopResult<&Face>

Source

pub fn get_face_mut(&mut self, id: FaceId) -> GeopResult<&mut Face>

Source

pub fn get_shell(&self, id: ShellId) -> GeopResult<&Shell>

Source

pub fn get_shell_mut(&mut self, id: ShellId) -> GeopResult<&mut Shell>

Source

pub fn get_solid(&self, id: SolidId) -> GeopResult<&Solid>

Source

pub fn get_solid_mut(&mut self, id: SolidId) -> GeopResult<&mut Solid>

Source

pub fn wire_of_edge(&self, edge: EdgeId) -> Option<WireId>

The wire edge is an edge of, if any.

Source

pub fn coedge_start_vertex_id(&self, coedge: CoedgeId) -> GeopResult<VertexId>

Source

pub fn coedge_start_vertex(&self, coedge: CoedgeId) -> GeopResult<&Vertex>

Source

pub fn coedge_end_vertex_id(&self, coedge: CoedgeId) -> GeopResult<VertexId>

Source

pub fn coedge_end_vertex(&self, coedge: CoedgeId) -> GeopResult<&Vertex>

Source

pub fn normal_at_vertex( &self, face: FaceId, vertex: VertexId, ) -> GeopResult<Vector3>

The unit normal of face’s surface at its own vertex vertex: at the (u, v) its loop gives the vertex — where a coedge of it starting there starts — not at one searched for by the vertex’s point. Where the patch’s parametrization degenerates at the vertex — a SubD patch whose two sides leave its corner along one line — a whole valley of (u, v) lies within a search’s reach of the point, and the search runs out of nodes; the loop knows which (u, v) it is. An error if no coedge of the face starts there.

Source§

impl Model

Source

pub fn coedges_of_edge(&self, edge: EdgeId) -> Vec<CoedgeId>

The (unordered) coedges referencing a given edge. An edge shared by a single manifold face pair has exactly two.

Source

pub fn iterate_loop_coedges( &self, anchor: CoedgeId, ) -> impl Iterator<Item = CoedgeId> + '_

Every coedge of the loop anchored at anchor, in traversal order, following next back around to anchor. Does not itself bound how many coedges it yields: a corrupted next chain that never returns to anchor makes this iterate forever, which callers checking that invariant (see validation::two_way_references) detect with a .take(n) of their own.

Source

pub fn face_corners( &self, face_id: FaceId, ) -> GeopResult<Vec<Vec<(VertexId, [f64; 3])>>>

The corners of face_id’s boundaries, outer loop first, each its vertex and where it is — what an error shows of a face, to tell which curves were imprinted into it, by vertices a named model can name (names_mentioned).

Source

pub fn iterate_face_coedges( &self, face_id: FaceId, ) -> impl Iterator<Item = CoedgeId> + '_

Every coedge of every boundary loop of face_id (its outer loop and any holes) — a bare Vertex boundary contributes none.

Source

pub fn face_edges(&self, face_id: FaceId) -> impl Iterator<Item = EdgeId> + '_

The edge of every edge-backed coedge of face_id’s boundaries, in loop order, outer loop first: a seam the face runs along twice comes twice.

Source

pub fn edges_at_vertex(&self, vertex: VertexId) -> Vec<EdgeId>

The edges ending at vertex, sorted by id.

Source

pub fn faces_at_vertex(&self, vertex: VertexId) -> GeopResult<Vec<FaceId>>

The faces whose boundaries run through vertex (along an edge starting there), sorted by id.

Source

pub fn faces_of_edge(&self, edge: EdgeId) -> Option<[FaceId; 2]>

The two faces of edge: the one its forward coedge bounds (on the edge’s left) first, the one running against it second. None unless the edge joins two faces.

Source

pub fn iter_body_vertices( &self, body: impl Into<Body>, ) -> GeopResult<impl Iterator<Item = VertexId> + '_>

The distinct vertices referenced by body’s faces. Borrows self for as long as it lives, so a caller holding one cannot also call a &mut self method like merge_vertex on the same model: the borrow checker, not the caller, keeps it from going stale.

Source

pub fn iter_body_edges( &self, body: impl Into<Body>, ) -> GeopResult<impl Iterator<Item = EdgeId> + '_>

The distinct edges referenced by body’s faces, borrowing self like Self::iter_body_vertices.

Source

pub fn find_boundary_containing( &self, face_id: FaceId, coedge: CoedgeId, ) -> GeopResult<BoundaryIndex>

Which of face_id’s boundaries contains coedge — its outer loop or one of its holes — found by traversing each Loop boundary’s .next chain. A face’s boundaries are disjoint loops, so at most one can contain any given coedge.

Returns BoundaryIndex rather than a bare position, because callers invariably need to know whether they found the outer loop: joining two holes, joining a hole to the outer loop, and joining two points of the outer loop are three different restructurings (see Model::splice_edge_into_face).

Source

pub fn remove_boundary( &mut self, face_id: FaceId, index: BoundaryIndex, ) -> GeopResult<()>

Drop the hole index names from face_id.

Errors on BoundaryIndex::Outer: a face without an outer boundary is not a face. An operation that genuinely consumes a face’s outer loop is deleting the face, and must say so rather than leaving one behind with nothing bounding it.

Source

pub fn solid_faces(&self, solid_id: SolidId) -> GeopResult<Vec<FaceId>>

Every FaceId across every shell of solid_id.

Source

pub fn body_faces(&self, body: impl Into<Body>) -> GeopResult<Vec<FaceId>>

Every FaceId of body: across every shell of a solid, or of the one shell a sheet is.

Source

pub fn solid_of_face(&self, face_id: FaceId) -> GeopResult<Option<SolidId>>

The solid face_id is part of; None for a face of a sheet.

Source

pub fn body_of_face(&self, face_id: FaceId) -> GeopResult<Body>

The body face_id is part of: its shell’s solid, or its shell, if that is a sheet.

Source§

impl Model

Source

pub fn new() -> Self

Source

pub fn next_id(&self) -> u64

The id the next entity it creates gets: what a model written to be read back as it was keeps with its entities (Model::with_next_id).

Source

pub fn with_next_id(self, next_id: u64) -> Self

It, creating entities from the id next_id on: a model read back as it was written, its entities set as they were. next_id is past every id it holds, as Model::next_id gave it.

Source§

impl Model

Source

pub fn wire_chains(&self, wire: WireId) -> GeopResult<Vec<Vec<(EdgeId, Sense)>>>

The edges of the wire wire in chains: each a run of edges, in order, each starting where the one before ends, with the sense it runs in along its chain — open between two vertices where the run does not go on through (an end, or a branch), or closed. Ordered by the smallest edge id each holds, which follows the order the edges were made in.

Trait Implementations§

Source§

impl Clone for Model

Source§

fn clone(&self) -> Model

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Model

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Model

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Display for Model

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Model

Source§

fn eq(&self, other: &Model) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Model

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.