all_is_cubes_base/math/
lines.rs

1//! Wireframe representations of shapes for debugging.
2
3use crate::math::{FreePoint, Rgba};
4use crate::util::MapExtend;
5
6// -------------------------------------------------------------------------------------------------
7
8/// Represent objects as line drawings, or wireframes.
9pub trait Wireframe {
10    /// Represent this object as a line drawing, or wireframe,
11    /// by producing line segments to be drawn.
12    ///
13    /// Design note: This method accepts a destination to write to, rather than returning an
14    /// iterator, because if it did return an iterator, it would be difficult to compose in
15    /// ways like allocating a temporary `Wireframe` and delegating to that, if it borrowed
16    /// its input, and would risk composing a very large yet unnecessary iterator struct
17    /// if it owned its input.
18    /// This way, composition is simply calling further functions.
19    ///
20    /// (If Rust gains stable [generator coroutines], we might be able to revisit that decision.)
21    ///
22    /// [generator coroutines]: https://doc.rust-lang.org/std/iter/macro.iter.html
23    fn wireframe_points<E: Extend<[Vertex; 2]>>(&self, output: &mut E);
24}
25
26impl<T: Wireframe> Wireframe for Option<T> {
27    #[allow(clippy::missing_inline_in_public_items)]
28    fn wireframe_points<E: Extend<[Vertex; 2]>>(&self, output: &mut E) {
29        if let Some(value) = self {
30            value.wireframe_points(output)
31        }
32    }
33}
34
35/// One end of a line to be drawn.
36///
37/// These are the output of [`Wireframe::wireframe_points()`].
38#[derive(Clone, Copy, Debug, PartialEq)]
39#[expect(clippy::exhaustive_structs)]
40pub struct Vertex {
41    /// Position of the vertex.
42    pub position: FreePoint,
43
44    /// Color in which to draw the line.
45    ///
46    /// If [`None`], a color set by the context/parent should be used instead.
47    ///
48    /// If the ends of a line are different colors, color should be interpolated along
49    /// the line.
50    pub color: Option<Rgba>,
51}
52
53impl From<FreePoint> for Vertex {
54    #[inline]
55    fn from(position: FreePoint) -> Self {
56        Self {
57            position,
58            color: None,
59        }
60    }
61}
62
63// -------------------------------------------------------------------------------------------------
64
65/// Transform an array of vertices to be interpreted as a line loop.
66/// That is, `[a, b, c]` is transformed to `[[a, b], [b, c], [c, a]]`.
67#[doc(hidden)] // for implementors’ use; not sure if good API
68#[allow(clippy::missing_inline_in_public_items)] // already generic
69pub fn line_loop<const N: usize>(vertices: [Vertex; N]) -> impl Iterator<Item = [Vertex; 2]> {
70    (0..N).map(move |i| [vertices[i], vertices[(i + 1).rem_euclid(N)]])
71}
72
73/// Add color to all vertices that don't have it.
74#[inline]
75pub fn colorize(output: &mut impl Extend<[Vertex; 2]>, color: Rgba) -> impl Extend<[Vertex; 2]> {
76    MapExtend::new(output, move |vertices: [Vertex; 2]| {
77        vertices.map(|mut vertex| {
78            vertex.color.get_or_insert(color);
79            vertex
80        })
81    })
82}