Skip to main content

Roof

Struct Roof 

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

A roof over a floor plan.

Build one with Roof::new for a hip, Roof::mansard for two pitches, or Roof::with_profile for either — and for the truncated versions of both, over a constrained skeleton.

Vertices are indexed by RoofVertexId, and the first Skeleton::node_count of those stand directly over the skeleton nodes of the same number, so the two structures stay in step and provenance survives into 3D.

§Why every panel is flat

A panel is the region swept by one wall’s wavefront, so every point on it is offset away from that wall’s supporting line — an affine function of position. Each band of a Profile is affine in that distance, so the composition is affine too, and the panel is a plane.

That is why a Profile::Mansard’s break has to cut panels rather than merely bend them: the profile is only affine within a band, so a panel spanning the break would be a fold, not a plane. Cut at the break, both halves are planes again.

Planarity is therefore guaranteed by construction rather than fitted, and the crate’s tests re-derive every corner’s height from its wall to check it.

§Examples

use straight_skeleton::{skeleton, Point, Polygon, Roof};

// An L-shaped house.
let plan = Polygon::from_outer(&[
    Point::new(0, 0),
    Point::new(160, 0),
    Point::new(160, 70),
    Point::new(70, 70),
    Point::new(70, 150),
    Point::new(0, 150),
])?;

let roof = Roof::new(&skeleton(&plan)?, 0.6)?;

// Six walls, six panels.
assert_eq!(roof.panels().len(), 6);

// Every panel knows which wall it rises from.
for (i, panel) in roof.panels().iter().enumerate() {
    assert_eq!(panel.wall().unwrap().0 as usize, i);
}

// Eaves sit at zero; nothing is below them.
assert!(roof.verts().iter().all(|v| v.position.z >= 0));

Implementations§

Source§

impl Roof

Source

pub fn new(skeleton: &Skeleton, pitch: f32) -> Result<Roof, RoofError>

Raises a hip roof over a skeleton.

pitch is rise over run: 1.0 gives 45°, 0.5 gives a shallower roof half as tall, 0.0 gives a flat one.

Shorthand for Roof::with_profile with a Profile::Hip. Use Roof::mansard for two pitches with a break between them, and see Roof::with_profile for what a constrained skeleton does here.

§Errors

As Roof::with_profile.

§Examples
use straight_skeleton::{skeleton, skeleton_constrained, Point, Polygon, Roof, RoofError};

let plan = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(80, 0), Point::new(80, 80), Point::new(0, 80),
])?;
let skel = skeleton(&plan)?;

// A square plan gives a pyramid: its apex is 40 in from every wall.
assert_eq!(Roof::new(&skel, 1.0)?.ridge_height(), 40);
assert_eq!(Roof::new(&skel, 0.5)?.ridge_height(), 20);
assert_eq!(Roof::new(&skel, 0.0)?.ridge_height(), 0);

// A pitch that would push the apex past i16 is refused, not clamped.
assert!(matches!(
    Roof::new(&skel, 1000.0),
    Err(RoofError::HeightOverflow { .. })
));

// Stopping every wall at 10 cuts the apex off, leaving a flat.
let truncated = skeleton_constrained(&plan, &[10.0; 4])?;
let roof = Roof::new(&truncated, 1.0)?;
assert_eq!(roof.ridge_height(), 10);
assert_eq!(roof.flat().count(), 1);
Source

pub fn mansard( skeleton: &Skeleton, lower_pitch: f32, break_offset: f32, upper_pitch: f32, ) -> Result<Roof, RoofError>

Raises a mansard roof: steep to break_offset, shallow above it.

Shorthand for Roof::with_profile with a Profile::Mansard. See there for what a mansard is and why the skeleton underneath is the same one a hip roof uses.

§Errors

As Roof::with_profile.

§Examples
use straight_skeleton::{skeleton, Point, Polygon, Roof};

// A 120 x 80 plan. Its ridge is 40 in from the long walls.
let plan = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(120, 0), Point::new(120, 80), Point::new(0, 80),
])?;
let skel = skeleton(&plan)?;

// Steep (2:1) for the first 10, then shallow (1:4) to the ridge.
let roof = Roof::mansard(&skel, 2.0, 10.0, 0.25)?;

// 10 * 2 = 20 at the kerb, then 30 more of run at 0.25 = 7.5 -> 28.
assert_eq!(roof.ridge_height(), 28);

// Each of the four walls now carries two panels rather than one: the
// steep skirt, and the shallow slope above it.
assert_eq!(roof.panels().len(), 8);
assert_eq!(roof.panels_of(straight_skeleton::EdgeId(0)).count(), 2);

// A hip roof of the same plan is much taller for the same lower pitch.
assert_eq!(Roof::new(&skel, 2.0)?.ridge_height(), 80);
Source

pub fn with_profile( skeleton: &Skeleton, profile: Profile, ) -> Result<Roof, RoofError>

Raises a roof over a skeleton, with any Profile.

§Constrained skeletons

A skeleton_constrained with one uniform limit gives a truncated roof: the slopes rise to the limit and stop, and the residual the wavefront stopped as becomes a PanelKind::Flat on top. That works with any profile, so a truncated mansard is steep, then shallow, then flat.

Uneven limits are refused — see RoofError::UnevenLimits, which explains why there is no such roof rather than merely no implementation.

§Errors
§Examples
use straight_skeleton::{skeleton_constrained, PanelKind, Point, Polygon, Profile, Roof};

let plan = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(100, 0), Point::new(100, 100), Point::new(0, 100),
])?;

// Every wall stopped at 20: a hip roof with its apex cut off.
let skel = skeleton_constrained(&plan, &[20.0; 4])?;
let roof = Roof::with_profile(&skel, Profile::Hip { pitch: 0.5 })?;

// Four slopes, and the flat they stop at.
assert_eq!(roof.panels().len(), 5);
assert_eq!(roof.flat().count(), 1);

// The flat stands at 20 * 0.5, and that is the top of the roof.
assert_eq!(roof.ridge_height(), 10);
Source

pub fn verts(&self) -> &[RoofVertex]

Every corner of the roof, indexed by RoofVertexId.

Source

pub fn panels(&self) -> &[Panel]

Every panel: the slopes in wall order, then any flats.

Source

pub fn profile(&self) -> Profile

The profile this roof was raised with.

Source

pub fn vertex(&self, v: RoofVertexId) -> &RoofVertex

A corner of the roof.

§Panics

Panics if v does not belong to this roof.

Source

pub fn vertex_at(&self, n: NodeId) -> &RoofVertex

The corner standing over a given skeleton node.

§Panics

Panics if n does not belong to the skeleton this roof was built from.

Source

pub fn panels_of(&self, wall: EdgeId) -> impl Iterator<Item = &Panel> + '_

The panels rising from a given wall, from the eaves up.

A Profile::Hip gives exactly one; a Profile::Mansard gives two where its break crosses the panel, and one where it does not reach.

§Examples
use straight_skeleton::{skeleton, EdgeId, Point, Polygon, Roof};

let plan = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(120, 0), Point::new(120, 80), Point::new(0, 80),
])?;
let skel = skeleton(&plan)?;

assert_eq!(Roof::new(&skel, 0.5)?.panels_of(EdgeId(0)).count(), 1);
assert_eq!(Roof::mansard(&skel, 2.0, 10.0, 0.25)?.panels_of(EdgeId(0)).count(), 2);
Source

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

The flat panels, if this roof is truncated. Empty otherwise.

More than one only when the flat has a hole in it — see PanelKind::Flat.

Source

pub fn ridge_height(&self) -> i16

The height of the highest point: the ridge, or a pyramid’s apex.

§Examples
use straight_skeleton::{skeleton, Point, Polygon, Roof};

// A 120-wide, 80-deep plan. The ridge runs down the middle of the long
// axis, 40 in from each long wall, so at pitch 0.5 it stands 20 high.
let plan = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(120, 0), Point::new(120, 80), Point::new(0, 80),
])?;
assert_eq!(Roof::new(&skeleton(&plan)?, 0.5)?.ridge_height(), 20);
Source

pub fn outline(&self, panel: &Panel) -> Vec<Point3>

The corners of one panel, as positions.

§Panics

Panics if panel does not belong to this roof.

Trait Implementations§

Source§

impl Clone for Roof

Source§

fn clone(&self) -> Roof

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 Roof

Source§

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

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

impl<'de> Deserialize<'de> for Roof

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Roof

Source§

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

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

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

Inequality operator !=. Read more
Source§

impl Serialize for Roof

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Roof

Auto Trait Implementations§

§

impl Freeze for Roof

§

impl RefUnwindSafe for Roof

§

impl Send for Roof

§

impl Sync for Roof

§

impl Unpin for Roof

§

impl UnsafeUnpin for Roof

§

impl UnwindSafe for Roof

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> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

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