SpacePlate

Struct SpacePlate 

Source
pub struct SpacePlate { /* private fields */ }
Expand description

The persistent floor-plate topology. See the module docs.

Implementations§

Source§

impl SpacePlate

Source

pub fn prune_orphans(&mut self) -> usize

Remove all orphaned cruft the wall arrangement leaves behind: dangling spur walls (degree-1 chains), isolated vertices, and redundant collinear degree-2 nodes. Idempotent, and never changes a room’s area (spurs bound no room; collinear dissolve only straightens a node already on its chord). Returns how many topology elements were pruned.

Source

pub fn remove_edge( &mut self, edge: HalfEdgeId, ) -> Result<Vec<FacePatch>, EditError>

Remove the wall edge, choosing the right semantics from its two incident faces, and auto-clean the orphans it leaves:

  • room ↔ room → union the two rooms (merge_faces);
  • bridge (same face both sides) → delete it, give the cycle it cut off its own face (split_bridge_cycles), then prune_orphans;
  • outer ↔ outer → delete it + prune_orphans;
  • room ↔ outer (a real enclosing wall) → BordersExterior (don’t open a room).
Source§

impl SpacePlate

Source

pub fn build(segments: &[InputSegment], options: BuildOptions) -> Self

Build the plate from tagged wall-axis segments.

Pipeline (ported from auto-space-detect.ts, provenance added):

  1. snap endpoints onto a spatial-hash grid,
  2. snap dangling endpoints onto nearby edge interiors (T-junctions),
  3. resolve interior crossings, splitting both hosts,
  4. dedupe undirected edges,
  5. build the half-edge graph with angle-sorted vertex fans,
  6. assign next/prev by the leftmost-turn rule and walk faces,
  7. flag CW cycles as exterior and drop sub-min_area rooms.
Source

pub fn build_from_wall_rects( rects: &[[[f64; 2]; 4]], options: BuildOptions, ) -> Self

FACE-BASED build → an editable CENTRELINE plate sitting on the wall axes.

Two stages. (1) DETECT: arrange the wall footprint rectangle edges and keep each bounded face whose centroid lies OUTSIDE every rectangle — i.e. a GAP between walls (not a wall interior or a junction overlap). This locates the rooms accurately, with the room boundary literally on the rendered wall faces (no centroid-derived-centreline drift). (2) LIFT: take each gap room’s wall-AXIS outline (the net gap offset out by ½ thickness, corners re-intersected) and arrange THOSE edges into the returned plate.

The returned plate is therefore a normal centreline plate whose room outlines ARE the wall axes and whose vertices ARE the displayed nodes — so every editing op (drag / split / merge / dissolve) acts directly on what the user sees, with no axis-vs-face offset. Each axis edge carries its wall’s half-thickness, so net_outline recovers the inner (net) and outer (gross) faces. Adjacent rooms’ shared wall maps to one shared centreline edge.

Source

pub fn drag_vertex( &mut self, v: VertexId, x: f64, y: f64, ) -> Result<Vec<FacePatch>, EditError>

Move v to (x, y). Topology is untouched; every incident face is updated by construction (they all reference this one vertex). Returns a patch per incident room face — the same call updates a room and its neighbour across a shared wall.

Source

pub fn split_face( &mut self, face: FaceId, va: VertexId, vb: VertexId, source_element: Option<u32>, ) -> Result<Vec<FacePatch>, EditError>

Subdivide face by inserting a partition edge between two of its vertices. Returns patches for the kept face and the new face.

The new edge carries source_element (None = a brand-new partition the user drew, which the bake step materialises as a fresh wall or a virtual boundary).

Source

pub fn split_edge( &mut self, edge: HalfEdgeId, x: f64, y: f64, ) -> Result<VertexId, EditError>

Insert a new vertex at (x, y) on the undirected edge edge (and its twin), subdividing it. No face is created — both incident faces simply gain a boundary vertex. Returns the new vertex so the caller can use it as a split_face endpoint. Pass a point on the edge segment to keep areas unchanged (the caller projects the click onto the edge).

This is what lets the user add a node where there wasn’t a corner, so a partition can start/end mid-wall rather than only at existing vertices.

Source

pub fn merge_faces( &mut self, edge: HalfEdgeId, ) -> Result<Vec<FacePatch>, EditError>

Remove the shared edge edge (and its twin), unioning the two rooms it separated into one. Returns a patch for the surviving face.

Source

pub fn dissolve_vertex( &mut self, v: VertexId, ) -> Result<Vec<FacePatch>, EditError>

Dissolve a degree-2 vertex v, welding its two incident edges into one straight edge between its neighbours — the inverse of split_edge, and the “delete this corner / errant node” affordance. Both incident faces lose a boundary vertex; if v was a real corner the faces change shape (the edge becomes the straight chord A→B), which is the intended edit. Returns a patch per incident room face.

Rejects:

  • a vertex whose degree isn’t exactly 2 — a wall junction or dangling tip has no unambiguous edge pair to merge (VertexNotDissolvable);
  • a node where the wall thickness changes: the welded edge carries ONE half_thickness for both twins, so a 200 mm wall meeting a 150 mm one end-to-end has no single value to carry (VertexNotDissolvable);
  • a weld whose two neighbours are already directly joined, which would make a parallel edge / collapse a triangle to a digon (DegenerateCut).
Source

pub fn add_face( &mut self, points: &[[f64; 2]], source_element: Option<u32>, ) -> Result<FacePatch, EditError>

Author a brand-new room from a closed ring of points — the “draw a room” affordance. The polygon becomes its own connected component: a CCW interior room face plus the CW exterior face bounding it. It does NOT merge into existing topology (there’s no arrangement overlay), so drawing over an existing room leaves two independent components — the documented prototype limitation.

points is the ring with no repeated closing vertex; winding is normalised to CCW. Rejects a ring that is too short, self-intersecting, or near-zero area (InvalidPolygon). Returns the new room’s patch.

Source

pub fn neighbor_across(&self, edge: HalfEdgeId) -> Option<FaceId>

The face on the far side of edge — its twin’s face. O(1). This is the “who’s my neighbour across this wall” query the UX leans on.

Source

pub fn rooms(&self) -> impl Iterator<Item = FaceId> + '_

Every live interior (room) face. In the FACE-BASED build (wall_rects non-empty) a bounded face is a room only if it’s a gap between walls — its centroid lies outside every wall rectangle, so wall interiors and junction overlaps are excluded.

Source

pub fn face_outline(&self, face: FaceId) -> Vec<[f64; 2]>

CCW outline of a face (no repeated closing vertex).

Source

pub fn face_area(&self, face: FaceId) -> f64

Absolute area of a face.

Source

pub fn net_outline(&self, face: FaceId, inset: bool) -> Vec<[f64; 2]>

The room’s outline offset to a wall boundary face rather than the centreline: each boundary edge is moved perpendicular by its own wall’s half-thickness — inward for the net (inner) face, outward for the gross (outer) face — then adjacent offset lines are re-intersected for the corners. Because every half-edge carries its source wall’s thickness, this needs no fuzzy edge↔wall matching (unlike the TS offsetRoomFootprint).

An edge shared with another room (its twin bounds a room, not the exterior) is pinned to the centreline in outward mode so it can’t push into the neighbour. Falls back to the unchanged centreline outline when no offset applies, a corner is degenerate, or an inset would invert the polygon — so the result is always a sane ring.

Source

pub fn gap_boundary(&self, face: FaceId, factor: f64) -> Vec<[f64; 2]>

FACE-BASED boundary of a gap room: the gap outline IS the net (inner-face) area, so this pushes every edge OUTWARD (into the wall, away from the room) by factor × the source wall's half-thickness, then re-intersects corners. factor = 0 → net (the gap itself); 1 → the wall axis / centre line (½ thickness — where the editable node sits, on the wall mid); 2 → the gross outer face (full thickness). No shared-edge pinning: two rooms across a wall correctly meet at the mid axis and overlap into it for gross.

Falls back to the un-offset net ring when the offset cannot be built; try_gap_boundary is the same computation with that case as None.

Source

pub fn bounding_elements(&self, face: FaceId) -> Vec<(HalfEdgeId, Option<u32>)>

The bounding half-edges of a face paired with the IFC element each came from — the raw material for IfcRelSpaceBoundary at bake.

Source

pub fn set_face_height( &mut self, face: FaceId, floor_z: f64, ceiling_z: f64, non_planar_ceiling: bool, )

Set the floor / ceiling planes of a face (the vertical dimension that turns a 2D face into a prismatic space at bake).

Source

pub fn vertex_position(&self, v: VertexId) -> Option<[f64; 2]>

Source

pub fn room_count(&self) -> usize

Count of live rooms — handy for tests / UI summaries.

Source

pub fn find_vertex_near(&self, x: f64, y: f64, tol: f64) -> Option<VertexId>

Nearest live vertex to (x, y) within tol, for hit-testing a drag target from the TS side. Linear scan — a floor plate is small.

Source

pub fn room_patches(&self) -> Vec<FacePatch>

Snapshot every live room as a patch (outline + area + simple flag) — for bulk render or seeding a fresh TS mirror after a rebuild.

Trait Implementations§

Source§

impl Clone for SpacePlate

Source§

fn clone(&self) -> SpacePlate

Returns a duplicate of the value. Read more
1.0.0 · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for SpacePlate

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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
§

impl<U> As for U

§

fn as_<T>(self) -> T
where T: CastFrom<U>,

Casts self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. 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.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
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, 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.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more