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}