zeno/
path_data.rs
1use super::command::{Command, PointsCommands, Verb};
4use super::geometry::{Point, Transform};
5use super::path_builder::PathBuilder;
6use super::segment::segments;
7use super::svg_parser::SvgCommands;
8
9#[cfg(feature = "eval")]
10use super::stroke::stroke_into;
11
12#[cfg(feature = "eval")]
13use super::style::*;
14
15#[cfg(feature = "eval")]
16use super::geometry::{Bounds, BoundsBuilder};
17
18#[cfg(feature = "eval")]
19use super::path_builder::TransformSink;
20
21use crate::lib::Vec;
22
23pub trait PathData {
86 type Commands: Iterator<Item = Command> + Clone;
88
89 fn commands(&self) -> Self::Commands;
91
92 fn copy_to(&self, sink: &mut impl PathBuilder) {
94 for cmd in self.commands() {
95 use Command::*;
96 match cmd {
97 MoveTo(p) => sink.move_to(p),
98 LineTo(p) => sink.line_to(p),
99 QuadTo(c, p) => sink.quad_to(c, p),
100 CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p),
101 Close => sink.close(),
102 };
103 }
104 }
105}
106
107pub fn length(data: impl PathData, transform: Option<Transform>) -> f32 {
109 let data = data.commands();
110 let mut length = 0.;
111 if let Some(transform) = transform {
112 for s in segments(data.map(|cmd| cmd.transform(&transform)), false) {
113 length += s.length();
114 }
115 } else {
116 for s in segments(data, false) {
117 length += s.length();
118 }
119 }
120 length
121}
122
123#[cfg(feature = "eval")]
125pub fn bounds<'a>(
126 data: impl PathData,
127 style: impl Into<Style<'a>>,
128 transform: Option<Transform>,
129) -> Bounds {
130 let style = style.into();
131 let mut bounds = BoundsBuilder::new();
132 apply(data, style, transform, &mut bounds);
133 bounds.build()
134}
135
136#[cfg(feature = "eval")]
139pub fn apply<'a>(
140 data: impl PathData,
141 style: impl Into<Style<'a>>,
142 transform: Option<Transform>,
143 sink: &mut impl PathBuilder,
144) -> Fill {
145 let style = style.into();
146 match style {
147 Style::Fill(fill) => {
148 if let Some(transform) = transform {
149 let mut transform_sink = TransformSink { sink, transform };
150 data.copy_to(&mut transform_sink);
151 fill
152 } else {
153 data.copy_to(sink);
154 fill
155 }
156 }
157 Style::Stroke(stroke) => {
158 if let Some(transform) = transform {
159 if stroke.scale {
160 let mut transform_sink = TransformSink { sink, transform };
161 stroke_into(data.commands(), &stroke, &mut transform_sink);
162 } else {
163 stroke_into(
164 data.commands().map(|cmd| cmd.transform(&transform)),
165 &stroke,
166 sink,
167 );
168 }
169 } else {
170 stroke_into(data.commands(), &stroke, sink);
171 }
172 Fill::NonZero
173 }
174 }
175}
176
177impl<T> PathData for &'_ T
178where
179 T: PathData,
180{
181 type Commands = T::Commands;
182
183 fn commands(&self) -> Self::Commands {
184 T::commands(*self)
185 }
186
187 #[inline(always)]
188 fn copy_to(&self, sink: &mut impl PathBuilder) {
189 T::copy_to(*self, sink)
190 }
191}
192
193impl<'a> PathData for &'a str {
194 type Commands = SvgCommands<'a>;
195
196 fn commands(&self) -> Self::Commands {
197 SvgCommands::new(self)
198 }
199}
200
201impl<'a> PathData for (&'a [Point], &'a [Verb]) {
202 type Commands = PointsCommands<'a>;
203
204 fn commands(&self) -> Self::Commands {
205 PointsCommands::new(self.0, self.1)
206 }
207
208 #[inline(always)]
209 fn copy_to(&self, sink: &mut impl PathBuilder) {
210 self.commands().copy_to(sink);
211 }
212}
213
214impl<'a> PathData for &'a [Command] {
215 type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
216
217 fn commands(&self) -> Self::Commands {
218 self.iter().copied()
219 }
220}
221
222impl<'a> PathData for &'a Vec<Command> {
223 type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
224
225 fn commands(&self) -> Self::Commands {
226 self.iter().copied()
227 }
228}