geometry.rs

  1use super::scene::{Path, PathVertex};
  2use crate::{color::Color, json::ToJson};
  3pub use pathfinder_geometry::*;
  4use rect::RectF;
  5use serde::{Deserialize, Deserializer};
  6use serde_json::json;
  7use vector::{vec2f, Vector2F};
  8
  9pub struct PathBuilder {
 10    vertices: Vec<PathVertex>,
 11    start: Vector2F,
 12    current: Vector2F,
 13    contour_count: usize,
 14    bounds: RectF,
 15}
 16
 17enum PathVertexKind {
 18    Solid,
 19    Quadratic,
 20}
 21
 22impl Default for PathBuilder {
 23    fn default() -> Self {
 24        PathBuilder::new()
 25    }
 26}
 27
 28impl PathBuilder {
 29    pub fn new() -> Self {
 30        Self {
 31            vertices: Vec::new(),
 32            start: vec2f(0., 0.),
 33            current: vec2f(0., 0.),
 34            contour_count: 0,
 35            bounds: RectF::default(),
 36        }
 37    }
 38
 39    pub fn reset(&mut self, point: Vector2F) {
 40        self.vertices.clear();
 41        self.start = point;
 42        self.current = point;
 43        self.contour_count = 0;
 44    }
 45
 46    pub fn line_to(&mut self, point: Vector2F) {
 47        self.contour_count += 1;
 48        if self.contour_count > 1 {
 49            self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
 50        }
 51
 52        self.current = point;
 53    }
 54
 55    pub fn curve_to(&mut self, point: Vector2F, ctrl: Vector2F) {
 56        self.contour_count += 1;
 57        if self.contour_count > 1 {
 58            self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
 59        }
 60
 61        self.push_triangle(self.current, ctrl, point, PathVertexKind::Quadratic);
 62        self.current = point;
 63    }
 64
 65    pub fn build(mut self, color: Color, clip_bounds: Option<RectF>) -> Path {
 66        if let Some(clip_bounds) = clip_bounds {
 67            self.bounds = self.bounds.intersection(clip_bounds).unwrap_or_default();
 68        }
 69        Path {
 70            bounds: self.bounds,
 71            color,
 72            vertices: self.vertices,
 73        }
 74    }
 75
 76    fn push_triangle(&mut self, a: Vector2F, b: Vector2F, c: Vector2F, kind: PathVertexKind) {
 77        if self.vertices.is_empty() {
 78            self.bounds = RectF::new(a, Vector2F::zero());
 79        }
 80        self.bounds = self.bounds.union_point(a).union_point(b).union_point(c);
 81
 82        match kind {
 83            PathVertexKind::Solid => {
 84                self.vertices.push(PathVertex {
 85                    xy_position: a,
 86                    st_position: vec2f(0., 1.),
 87                });
 88                self.vertices.push(PathVertex {
 89                    xy_position: b,
 90                    st_position: vec2f(0., 1.),
 91                });
 92                self.vertices.push(PathVertex {
 93                    xy_position: c,
 94                    st_position: vec2f(0., 1.),
 95                });
 96            }
 97            PathVertexKind::Quadratic => {
 98                self.vertices.push(PathVertex {
 99                    xy_position: a,
100                    st_position: vec2f(0., 0.),
101                });
102                self.vertices.push(PathVertex {
103                    xy_position: b,
104                    st_position: vec2f(0.5, 0.),
105                });
106                self.vertices.push(PathVertex {
107                    xy_position: c,
108                    st_position: vec2f(1., 1.),
109                });
110            }
111        }
112    }
113}
114
115pub fn deserialize_vec2f<'de, D>(deserializer: D) -> Result<Vector2F, D::Error>
116where
117    D: Deserializer<'de>,
118{
119    let [x, y]: [f32; 2] = Deserialize::deserialize(deserializer)?;
120    Ok(vec2f(x, y))
121}
122
123impl ToJson for Vector2F {
124    fn to_json(&self) -> serde_json::Value {
125        json!([self.x(), self.y()])
126    }
127}
128
129impl ToJson for RectF {
130    fn to_json(&self) -> serde_json::Value {
131        json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
132    }
133}
134
135#[derive(Clone)]
136pub struct Point<T> {
137    pub x: T,
138    pub y: T,
139}
140
141impl<T> Into<taffy::geometry::Point<T>> for Point<T> {
142    fn into(self) -> taffy::geometry::Point<T> {
143        taffy::geometry::Point {
144            x: self.x,
145            y: self.y,
146        }
147    }
148}
149
150#[derive(Clone)]
151pub struct Size<T> {
152    pub width: T,
153    pub height: T,
154}
155
156impl<S, T> From<taffy::geometry::Size<S>> for Size<T>
157where
158    S: Into<T>,
159{
160    fn from(value: taffy::geometry::Size<S>) -> Self {
161        Self {
162            width: value.width.into(),
163            height: value.height.into(),
164        }
165    }
166}
167
168impl<S, T> Into<taffy::geometry::Size<S>> for Size<T>
169where
170    T: Into<S>,
171{
172    fn into(self) -> taffy::geometry::Size<S> {
173        taffy::geometry::Size {
174            width: self.width.into(),
175            height: self.height.into(),
176        }
177    }
178}
179
180impl Size<DefinedLength> {
181    pub const fn zero() -> Self {
182        Self {
183            width: DefinedLength::Pixels(0.),
184            height: DefinedLength::Pixels(0.),
185        }
186    }
187
188    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
189        taffy::geometry::Size {
190            width: self.width.to_taffy(rem_size),
191            height: self.height.to_taffy(rem_size),
192        }
193    }
194}
195
196impl Size<Length> {
197    pub const fn auto() -> Self {
198        Self {
199            width: Length::Auto,
200            height: Length::Auto,
201        }
202    }
203
204    pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
205        &self,
206        rem_size: f32,
207    ) -> taffy::geometry::Size<T> {
208        taffy::geometry::Size {
209            width: self.width.to_taffy(rem_size).into(),
210            height: self.height.to_taffy(rem_size).into(),
211        }
212    }
213}
214
215#[derive(Clone)]
216pub struct Edges<T> {
217    pub top: T,
218    pub right: T,
219    pub bottom: T,
220    pub left: T,
221}
222
223impl Edges<DefinedLength> {
224    pub const fn zero() -> Self {
225        Self {
226            top: DefinedLength::Pixels(0.0),
227            right: DefinedLength::Pixels(0.0),
228            bottom: DefinedLength::Pixels(0.0),
229            left: DefinedLength::Pixels(0.0),
230        }
231    }
232
233    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
234        taffy::geometry::Rect {
235            top: self.top.to_taffy(rem_size),
236            right: self.right.to_taffy(rem_size),
237            bottom: self.bottom.to_taffy(rem_size),
238            left: self.left.to_taffy(rem_size),
239        }
240    }
241}
242
243impl Edges<Length> {
244    pub const fn auto() -> Self {
245        Self {
246            top: Length::Auto,
247            right: Length::Auto,
248            bottom: Length::Auto,
249            left: Length::Auto,
250        }
251    }
252
253    pub const fn zero() -> Self {
254        Self {
255            top: Length::Defined(DefinedLength::Pixels(0.0)),
256            right: Length::Defined(DefinedLength::Pixels(0.0)),
257            bottom: Length::Defined(DefinedLength::Pixels(0.0)),
258            left: Length::Defined(DefinedLength::Pixels(0.0)),
259        }
260    }
261
262    pub fn to_taffy(
263        &self,
264        rem_size: f32,
265    ) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
266        taffy::geometry::Rect {
267            top: self.top.to_taffy(rem_size),
268            right: self.right.to_taffy(rem_size),
269            bottom: self.bottom.to_taffy(rem_size),
270            left: self.left.to_taffy(rem_size),
271        }
272    }
273}
274
275/// A non-auto length that can be defined in pixels, rems, or percent of parent.
276#[derive(Clone, Copy)]
277pub enum DefinedLength {
278    Pixels(f32),
279    Rems(f32),
280    Percent(f32), // 0. - 100.
281}
282
283impl DefinedLength {
284    fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
285        match self {
286            DefinedLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
287            DefinedLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
288            DefinedLength::Percent(percent) => {
289                taffy::style::LengthPercentage::Percent(*percent / 100.)
290            }
291        }
292    }
293}
294
295/// A length that can be defined in pixels, rems, percent of parent, or auto.
296#[derive(Clone, Copy)]
297pub enum Length {
298    Defined(DefinedLength),
299    Auto,
300}
301
302pub fn auto() -> Length {
303    Length::Auto
304}
305
306pub fn percent(percent: f32) -> DefinedLength {
307    DefinedLength::Percent(percent)
308}
309
310pub fn rems(rems: f32) -> DefinedLength {
311    DefinedLength::Rems(rems)
312}
313
314pub fn pixels(pixels: f32) -> DefinedLength {
315    DefinedLength::Pixels(pixels)
316}
317
318impl Length {
319    pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
320        match self {
321            Length::Defined(length) => length.to_taffy(rem_size).into(),
322            Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
323        }
324    }
325}
326
327impl From<DefinedLength> for Length {
328    fn from(value: DefinedLength) -> Self {
329        Length::Defined(value)
330    }
331}