geometry.rs

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