rootvg_tessellation/path/
builder.rs1use super::{ArcPath, EllipticalArcPath, Path};
6
7use rootvg_core::math::{Angle, Point, Size};
8
9use lyon::geom;
10use lyon::math;
11use lyon::path::builder::{self, SvgPathBuilder};
12
13pub struct PathBuilder {
17 pub raw: builder::WithSvg<lyon::path::path::BuilderImpl>,
18}
19
20impl PathBuilder {
21 pub fn new() -> Self {
23 Self {
24 raw: lyon::path::Path::builder().with_svg(),
25 }
26 }
27
28 pub fn move_to(mut self, point: Point) -> Self {
30 self.raw.move_to(math::Point::new(point.x, point.y));
31 self
32 }
33
34 pub fn line_to(mut self, point: Point) -> Self {
37 self.raw.line_to(math::Point::new(point.x, point.y));
38 self
39 }
40
41 pub fn arc(self, arc: ArcPath) -> Self {
44 self.ellipse(arc.into())
45 }
46
47 pub fn arc_to(mut self, a: Point, b: Point, radius: f32) -> Self {
61 let start = self.raw.current_position();
62 let mid = math::Point::new(a.x, a.y);
63 let end = math::Point::new(b.x, b.y);
64
65 if start == mid || mid == end || radius == 0.0 {
66 let _ = self.raw.line_to(mid);
67 return self;
68 }
69
70 let double_area =
71 start.x * (mid.y - end.y) + mid.x * (end.y - start.y) + end.x * (start.y - mid.y);
72
73 if double_area == 0.0 {
74 let _ = self.raw.line_to(mid);
75 return self;
76 }
77
78 let to_start = (start - mid).normalize();
79 let to_end = (end - mid).normalize();
80
81 let inner_angle = to_start.dot(to_end).acos();
82
83 let origin_angle = inner_angle / 2.0;
84
85 let origin_adjacent = radius / origin_angle.tan();
86
87 let arc_start = mid + to_start * origin_adjacent;
88 let arc_end = mid + to_end * origin_adjacent;
89
90 let sweep = to_start.cross(to_end) < 0.0;
91
92 let _ = self.raw.line_to(arc_start);
93
94 self.raw.arc_to(
95 math::Vector::new(radius, radius),
96 math::Angle::radians(0.0),
97 lyon::path::ArcFlags {
98 large_arc: false,
99 sweep,
100 },
101 arc_end,
102 );
103
104 self
105 }
106
107 pub fn ellipse(mut self, arc: EllipticalArcPath) -> Self {
109 let arc = geom::Arc {
110 center: math::Point::new(arc.center.x, arc.center.y),
111 radii: math::Vector::new(arc.radii.x, arc.radii.y),
112 x_rotation: math::Angle::radians(arc.rotation.radians),
113 start_angle: math::Angle::radians(arc.start_angle.radians),
114 sweep_angle: math::Angle::radians((arc.end_angle - arc.start_angle).radians),
115 };
116
117 let _ = self.raw.move_to(arc.sample(0.0));
118
119 arc.for_each_quadratic_bezier(&mut |curve| {
120 let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
121 });
122
123 self
124 }
125
126 pub fn bezier_curve_to(mut self, control_a: Point, control_b: Point, to: Point) -> Self {
129 let _ = self.raw.cubic_bezier_to(
130 math::Point::new(control_a.x, control_a.y),
131 math::Point::new(control_b.x, control_b.y),
132 math::Point::new(to.x, to.y),
133 );
134 self
135 }
136
137 pub fn quadratic_curve_to(mut self, control: Point, to: Point) -> Self {
140 let _ = self.raw.quadratic_bezier_to(
141 math::Point::new(control.x, control.y),
142 math::Point::new(to.x, to.y),
143 );
144 self
145 }
146
147 pub fn rectangle(self, top_left: Point, size: Size) -> Self {
150 self.move_to(top_left)
151 .line_to(Point::new(top_left.x + size.width, top_left.y))
152 .line_to(Point::new(
153 top_left.x + size.width,
154 top_left.y + size.height,
155 ))
156 .line_to(Point::new(top_left.x, top_left.y + size.height))
157 .close()
158 }
159
160 pub fn circle(self, center: Point, radius: f32) -> Self {
163 self.arc(ArcPath {
164 center,
165 radius,
166 start_angle: Angle { radians: 0.0 },
167 end_angle: Angle {
168 radians: 2.0 * std::f32::consts::PI,
169 },
170 })
171 }
172
173 pub fn close(mut self) -> Self {
176 self.raw.close();
177 self
178 }
179
180 pub fn build(self) -> Path {
182 Path {
183 raw: self.raw.build(),
184 }
185 }
186}
187
188impl Default for PathBuilder {
189 fn default() -> Self {
190 Self::new()
191 }
192}