Skip to main content

Skeleton

Struct Skeleton 

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

The straight skeleton of a Polygon.

A skeleton is a planar graph of Nodes joined by Arcs. Build one with skeleton or skeleton_constrained.

§Examples

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

// A 10x10 square's skeleton is an X: four boundary nodes, one centre node,
// four arcs running corner to centre.
let square = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(10, 0), Point::new(10, 10), Point::new(0, 10),
])?;
let skel = skeleton(&square)?;

assert_eq!(skel.node_count(), 5);
assert_eq!(skel.arc_count(), 4);

// The interior node is the centre, at offset 5 (half the width).
let centre = skel.nodes().iter().find(|n| !n.is_boundary()).unwrap();
assert_eq!(centre.position, Point::new(5, 5));
assert!((centre.offset - 5.0).abs() < 1e-4);

// ...and it is equidistant from all four input edges.
assert_eq!(centre.sources.len(), 4);

Implementations§

Source§

impl Skeleton

Source

pub fn nodes(&self) -> &[Node]

All nodes.

Source

pub fn arcs(&self) -> &[Arc]

All arcs.

Source

pub fn residual(&self) -> &[ResidualLoop]

The wavefront loops that stopped rather than collapsing — the offset polygon a constrained skeleton leaves behind.

Empty for a plain skeleton, whose wavefront always shrinks away to nothing. Non-empty only where skeleton_constrained’s limits bound hard enough to stop a whole loop, and then there is one entry per loop still standing: the outer offset outline, plus one around each hole that survived.

This is the other half of a constrained result. The arcs are the stubs reaching in from the boundary; this is the outline they stop on. See ResidualLoop for why it is not made of arcs.

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

// An L-shape, every wall stopped at 20.
let l = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(200, 0), Point::new(200, 100),
    Point::new(100, 100), Point::new(100, 200), Point::new(0, 200),
])?;
let skel = skeleton_constrained(&l, &[20.0; 6])?;

// What is left is the same L, 20 in from every wall.
assert_eq!(skel.residual().len(), 1);
let mut corners: Vec<Point> =
    skel.residual()[0].nodes.iter().map(|&n| skel.node(n).position).collect();
corners.sort();
assert_eq!(corners, vec![
    Point::new(20, 20), Point::new(20, 180), Point::new(80, 80),
    Point::new(80, 180), Point::new(180, 20), Point::new(180, 80),
]);
Source

pub fn node_count(&self) -> usize

Number of nodes.

Source

pub fn arc_count(&self) -> usize

Number of arcs.

Source

pub fn node(&self, n: NodeId) -> &Node

Looks up a node.

§Panics

Panics if n does not belong to this skeleton.

Source

pub fn arc(&self, a: ArcId) -> &Arc

Looks up an arc.

§Panics

Panics if a does not belong to this skeleton.

Source

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

Iterates node ids.

Source

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

Iterates arc ids.

Source

pub fn arcs_at(&self, n: NodeId) -> &[ArcId]

The arcs incident to a node.

§Panics

Panics if n does not belong to this skeleton.

Source

pub fn arc_segment(&self, a: ArcId) -> (Point, Point)

The arc’s two endpoints, as positions.

§Panics

Panics if a does not belong to this skeleton.

Source

pub fn closest_input_edges(&self, a: ArcId) -> [EdgeId; 2]

The two input edges a given arc came from.

Every point along the arc is equidistant from these two edges’ supporting lines, and the arc separates their two faces. See Arc for why this is exact rather than a search, and Node::sources for why “came from” is more accurate than “closest to”.

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

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

// Each of the square's four arcs bisects two adjacent input edges.
for a in skel.arc_ids() {
    let [e0, e1] = skel.closest_input_edges(a);
    assert_ne!(e0, e1);
}
§Panics

Panics if a does not belong to this skeleton.

Source

pub fn closest_input_edges_to_node(&self, n: NodeId) -> &[EdgeId]

The input edges a given node came from.

See Node::sources, which this returns.

§Panics

Panics if n does not belong to this skeleton.

Source

pub fn max_offset(&self) -> f32

The largest offset reached by any node: the radius of the largest disc that fits inside the polygon.

For a roof, this is the ridge height. Returns 0 for an empty skeleton.

Source

pub fn boundary_node(&self, v: VertexId) -> Option<NodeId>

The boundary node sitting on a given input vertex.

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

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

let n = skel.boundary_node(VertexId(2)).unwrap();
assert_eq!(skel.node(n).position, Point::new(10, 10));
Source

pub fn face(&self, e: EdgeId) -> Option<Vec<NodeId>>

The face of an input edge: the closed region the wavefront of that edge swept out, as a loop of nodes.

Every input edge has exactly one face, and the faces tile the polygon. The returned loop starts with the edge’s own two endpoints, then follows the skeleton arcs that bound the face back around. Each face is planar when nodes are lifted to z = offset, which is exactly why a straight skeleton builds roofs: one face is one roof plane. See the roof example.

Returns None if the face cannot be walked, which should not happen for a skeleton of a valid polygon.

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

// Each of a square's four edges has a triangular face running to the
// centre.
let square = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(10, 0), Point::new(10, 10), Point::new(0, 10),
])?;
let skel = skeleton(&square)?;

let face = skel.face(EdgeId(0)).unwrap();
assert_eq!(face.len(), 3);

// A rectangle's long edges get quadrilateral faces, because the ridge
// gives them a fourth corner.
let rect = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(20, 0), Point::new(20, 10), Point::new(0, 10),
])?;
let skel = skeleton(&rect)?;
assert_eq!(skel.face(EdgeId(0)).unwrap().len(), 4); // long edge
assert_eq!(skel.face(EdgeId(1)).unwrap().len(), 3); // short edge
Source

pub fn input_edge_count(&self) -> usize

How many input edges the polygon had.

Each one owns exactly one face, so this is also the number of faces.

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

let square = Polygon::from_outer(&[
    Point::new(0, 0), Point::new(10, 0), Point::new(10, 10), Point::new(0, 10),
])?;
assert_eq!(skeleton(&square)?.input_edge_count(), 4);
Source

pub fn faces(&self) -> Option<Vec<Vec<NodeId>>>

Every input edge’s face, in edge order.

Returns None if any face cannot be walked.

Trait Implementations§

Source§

impl Clone for Skeleton

Source§

fn clone(&self) -> Skeleton

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 Skeleton

Source§

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

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

impl Default for Skeleton

Source§

fn default() -> Skeleton

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

impl<'de> Deserialize<'de> for Skeleton

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 Skeleton

Source§

fn eq(&self, other: &Skeleton) -> 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 Skeleton

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 Skeleton

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