mcsdf/
shape.rs

1use super::geometry::{Curve, Line, Rect};
2use super::texture::{TextureView, TextureViewAllocator};
3use std::f32;
4use std::iter::FromIterator;
5
6pub struct Shape {
7    segments: Vec<ShapeSegment>,
8}
9
10impl Shape {
11    pub fn new(segments: Vec<ShapeSegment>) -> Self {
12        Self { segments }
13    }
14
15    pub fn get_segments(&self) -> &[ShapeSegment] {
16        &self.segments
17    }
18}
19
20#[derive(Clone, Copy)]
21pub enum ShapeSegment {
22    Line { line: Line, mask: u8 },
23    Curve { curve: Curve, mask: u8 },
24    End { clock_wise: bool },
25}
26
27impl ShapeSegment {
28    pub fn bounding_box(&self) -> Option<Rect<f32>> {
29        match self {
30            ShapeSegment::Line { line, .. } => Some(line.bounding_box()),
31            ShapeSegment::Curve { curve, .. } => Some(curve.bounding_box()),
32            ShapeSegment::End { .. } => None,
33        }
34    }
35}
36
37pub struct AllocatedShape {
38    pub shape: Shape,
39    pub shape_bb: Rect<f32>,
40    pub texture_view: TextureView,
41    pub max_distance: f32,
42}
43
44impl AllocatedShape {
45    pub fn new(
46        shape: Shape,
47        texture_allocator: &mut TextureViewAllocator,
48        max_distance: f32,
49    ) -> Option<Self> {
50        let mut max_bb: Option<Rect<f32>> = None;
51        for segment in &shape.segments {
52            if let Some(bb) = segment.bounding_box() {
53                if let Some(ref mut max_bb) = max_bb {
54                    max_bb.min.x = max_bb.min.x.min(bb.min.x);
55                    max_bb.min.y = max_bb.min.y.min(bb.min.y);
56                    max_bb.max.x = max_bb.max.x.max(bb.max.x);
57                    max_bb.max.y = max_bb.max.y.max(bb.max.y);
58                } else {
59                    max_bb = Some(bb);
60                }
61            }
62        }
63
64        let mut max_bb = max_bb?;
65        max_bb.min.x -= max_distance;
66        max_bb.min.y -= max_distance;
67        max_bb.max.x += max_distance;
68        max_bb.max.y += max_distance;
69
70        let texture_view = texture_allocator
71            .allocate(max_bb.width().ceil() as u32, max_bb.height().ceil() as u32)?;
72
73        Some(Self {
74            shape,
75            shape_bb: max_bb,
76            texture_view,
77            max_distance,
78        })
79    }
80}
81
82pub enum Segment {
83    Start { count: usize },
84    Line { line: Line },
85    Curve { curve: Curve },
86}
87
88impl<'a> FromIterator<Segment> for Shape {
89    fn from_iter<T: IntoIterator<Item = Segment>>(segments: T) -> Self {
90        let mut shape_segments = Vec::new();
91        let mut area = 0.0;
92        let mut mask = 0;
93        let mut remaining_segments = 0;
94
95        fn next_mask(mask: u8, remaining_segments: usize) -> u8 {
96            match mask {
97                0b110 => 0b011,
98                0b011 => 0b101,
99                _ => if remaining_segments == 0 {
100                    0b011
101                } else {
102                    0b110
103                },
104            }
105        };
106
107        let mut iter = segments.into_iter();
108        while let Some(segment) = iter.next() {
109            match segment {
110                Segment::Start { count } => {
111                    remaining_segments = count;
112                    mask = 0;
113                    area = 0.0;
114                }
115                Segment::Line { line } => {
116                    area += line.area();
117                    remaining_segments -= 1;
118                    mask = next_mask(mask, remaining_segments);
119                    shape_segments.push(ShapeSegment::Line { line, mask });
120                }
121                Segment::Curve { curve } => {
122                    area += curve.area();
123                    remaining_segments -= 1;
124                    mask = next_mask(mask, remaining_segments);
125                    shape_segments.push(ShapeSegment::Curve { curve, mask });
126                }
127            }
128
129            if remaining_segments == 0 {
130                shape_segments.push(ShapeSegment::End {
131                    clock_wise: area < 0.0,
132                });
133            }
134        }
135
136        Shape::new(shape_segments)
137    }
138}