Skip to main content

Model

Struct Model 

Source
pub struct Model<S: Scalar> {
    pub vertices: HashMap<VertexId, Vertex<S>>,
    pub edges: HashMap<EdgeId, Edge<S>>,
    pub coedges: HashMap<CoedgeId, Coedge<S>>,
    pub faces: HashMap<FaceId, Face<S>>,
    pub shells: HashMap<ShellId, Shell>,
    pub solids: HashMap<SolidId, Solid>,
    /* 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

V - E + F - L = 2 * (S - 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), S = #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.

Fields§

§vertices: HashMap<VertexId, Vertex<S>>§edges: HashMap<EdgeId, Edge<S>>§coedges: HashMap<CoedgeId, Coedge<S>>§faces: HashMap<FaceId, Face<S>>§shells: HashMap<ShellId, Shell>§solids: HashMap<SolidId, Solid>

Implementations§

Source§

impl<S: Scalar> Model<S>

Source

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

Replace the solids consumed by one new solid of a single shell made of keep, which must be faces of those solids. Every other face they 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.

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 solid in the model are none of this operation’s business.

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<S: Scalar> Model<S>

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<S: Scalar> Model<S>

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<S: Scalar> Model<S>

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<S: Scalar> Model<S>

Source

pub fn splice_edge_into_face( &mut self, edge_id: EdgeId, face_id: FaceId, max_nodes: usize, min_subdivision_size: S, ) -> 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.

Unlike the mer/mekr Euler operators — which mint their own brand-new edge, and so can’t be used for an edge that already exists because two faces are meant to share it (the whole point of imprinting an intersection curve) — this rewires loops around an edge it’s handed.

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]). The middle cases are the same restructurings mer/mekr perform; a spur changes no connectivity at all, being “wire” topology rather than a real trim boundary.

Source§

impl<S: Scalar> Model<S>

Source

pub fn split_edge_at_vertex( &mut self, edge_id: EdgeId, edge_t: S, vertex_id: VertexId, max_nodes: usize, min_subdivision_size: S, ) -> 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<S: Scalar> Model<S>

Source

pub fn add_vertex_coedge( self: &mut Model<S>, after: CoedgeId, vertex: VertexId, pcurve: NurbCurve2D<S>, ) -> GeopResult<CoedgeId>

Splice a single degenerate coedge — backed directly by vertex (see CoedgeGeometry::Vertex), not a real edge — into after’s own loop, right after it. pcurve must both start and end at vertex’s own position, as mapped through after’s face’s surface (it’s meant to sweep some parameter-space range while sitting at that one 3-D point the whole way, e.g. tracing a pole row’s full angular span).

Unlike every other euler operator here, this isn’t one: it doesn’t touch V, E, or F (vertex already exists, and no edge is created), so there’s no Euler–Poincaré invariant for it to need to preserve — it’s pure loop bookkeeping, letting a face’s own boundary legitimately pass through an already-shared vertex without requiring a real (and, for a single-face-only detour, otherwise unpaired) edge to carry a pcurve.

Source§

impl<S: Scalar> Model<S>

Source

pub fn kef( self: &mut Model<S>, edge: EdgeId, killed_face: FaceId, ) -> GeopResult<()>

Kill the edge added by mef, merging killed_face back into the face on the other side of edge: the absorbed face’s ring is spliced back into the surviving face’s ring (undoing the split), and killed_face is removed from the model and from its shell’s face list. edge’s two coedges must currently lie on two different faces, one of which is killed_face.

Source§

impl<S: Scalar> Model<S>

Source

pub fn kemr( self: &mut Model<S>, ca_id: CoedgeId, cb_id: CoedgeId, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn ker( self: &mut Model<S>, coedge_backward: CoedgeId, coedge_forward: CoedgeId, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn kill_vertex_coedge( self: &mut Model<S>, coedge: CoedgeId, ) -> GeopResult<()>

Undo Model::add_vertex_coedge: splice coedge back out of its loop. coedge must be Vertex-backed (i.e. one add_vertex_coedge itself returned) — nothing is removed from V, E, or F here either, matching add_vertex_coedge’s own no-op effect on them.

Source§

impl<S: Scalar> Model<S>

Source

pub fn kve( self: &mut Model<S>, c_out: CoedgeId, c_in: CoedgeId, vertex: VertexId, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn kvfs(self: &mut Model<S>, solid: SolidId) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn kvr( self: &mut Model<S>, face_id: FaceId, vertex: VertexId, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn mef( self: &mut Model<S>, coedge1: CoedgeId, coedge2: CoedgeId, curve: NurbCurve3D<S>, pcurve: NurbCurve2D<S>, pcurve_reversed: NurbCurve2D<S>, new_surface: NurbSurface3D<S>, ) -> GeopResult<(EdgeId, FaceId, CoedgeId, CoedgeId)>

Source§

impl<S: Scalar> Model<S>

Source

pub fn mekr( self: &mut Model<S>, coedge1: CoedgeId, coedge2: CoedgeId, curve: NurbCurve3D<S>, pcurve: NurbCurve2D<S>, ) -> GeopResult<(EdgeId, CoedgeId, CoedgeId)>

Source§

impl<S: Scalar> Model<S>

Source

pub fn mer( self: &mut Model<S>, coedge1: CoedgeId, coedge2: CoedgeId, curve: NurbCurve3D<S>, pcurve: NurbCurve2D<S>, pcurve_reversed: NurbCurve2D<S>, existing_face_id: FaceId, ) -> GeopResult<(EdgeId, CoedgeId, CoedgeId)>

Source§

impl<S: Scalar> Model<S>

Source

pub fn mve( self: &mut Model<S>, coedge: CoedgeId, curve: NurbCurve3D<S>, pcurve: NurbCurve2D<S>, pcurve_reversed: NurbCurve2D<S>, p: Vector3<S>, ) -> GeopResult<(VertexId, CoedgeId, CoedgeId, EdgeId)>

Source

pub fn mve_from_vertex( self: &mut Model<S>, face_id: FaceId, vertex: VertexId, curve: NurbCurve3D<S>, pcurve: NurbCurve2D<S>, pcurve_reversed: NurbCurve2D<S>, p: Vector3<S>, ) -> GeopResult<(VertexId, CoedgeId, CoedgeId, EdgeId)>

Source§

impl<S: Scalar> Model<S>

Source

pub fn mvfs( self: &mut Model<S>, point: Vector3<S>, ) -> (VertexId, FaceId, SolidId)

Source§

impl<S: Scalar> Model<S>

Source

pub fn mvr( self: &mut Model<S>, face_id: FaceId, point: Vector3<S>, ) -> GeopResult<VertexId>

Source§

impl<S: Scalar> Model<S>

Source

pub fn replace_face( self: &mut Model<S>, face_id: FaceId, surface: NurbSurface3D<S>, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

pub fn replace_pcurve( self: &mut Model<S>, coedge_id: CoedgeId, pcurve: NurbCurve2D<S>, ) -> GeopResult<()>

Source§

impl<S: Scalar> Model<S>

Source

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

Source

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

Source

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

Source

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

Source

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

Source

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

Source§

impl<S: Scalar> Model<S>

Source

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

Source

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

Source

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

Source

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

Source

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

Source

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

Source

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

Source

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

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 coedge_start_vertex_id(&self, coedge: CoedgeId) -> GeopResult<VertexId>

Source

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

Source

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

Source

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

Source§

impl<S: Scalar> Model<S>

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.

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).

Source

pub fn iter_solid_vertices( &self, solid_id: SolidId, ) -> GeopResult<impl Iterator<Item = VertexId> + '_>

The distinct vertices referenced by solid_id’s faces, as an iterator borrowing self — see [SolidVertices]’s own doc comment.

Source

pub fn iter_solid_edges( &self, solid_id: SolidId, ) -> GeopResult<impl Iterator<Item = EdgeId> + '_>

The distinct edges referenced by solid_id’s faces, as an iterator borrowing self — see [SolidVertices]’s own doc comment.

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 (see kef) 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§

impl<S: Scalar> Model<S>

Source

pub fn new() -> Self

Trait Implementations§

Source§

impl<S: Clone + Scalar> Clone for Model<S>

Source§

fn clone(&self) -> Model<S>

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<S: Scalar> Display for Model<S>

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<S> Freeze for Model<S>

§

impl<S> RefUnwindSafe for Model<S>
where S: RefUnwindSafe,

§

impl<S> Send for Model<S>

§

impl<S> Sync for Model<S>

§

impl<S> Unpin for Model<S>
where S: Unpin,

§

impl<S> UnsafeUnpin for Model<S>

§

impl<S> UnwindSafe for Model<S>
where S: UnwindSafe,

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.