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}