1use std::fmt::{Display, Write};
2use std::ops::MulAssign;
3
4use euclid::default::Point2D;
5use euclid::Trig;
6use num_traits::{Float, FromPrimitive};
7use points_on_curve::{curve_to_bezier, points_on_bezier_curves};
8use svgtypes::PathSegment;
9
10use crate::core::{
11 Drawable,
12 FillStyle,
13 OpSet,
14 OpSetType,
15 OpType,
16 Options,
17 OptionsBuilder,
18 PathInfo,
19 _c,
20};
21use crate::geometry::{convert_bezier_quadratic_to_cubic, BezierQuadratic};
22use crate::points_on_path::{points_on_path, points_on_segments};
23use crate::renderer::{
24 bezier_cubic,
25 bezier_quadratic,
26 curve,
27 ellipse_with_params,
28 generate_ellipse_params,
29 line,
30 linear_path,
31 pattern_fill_arc,
32 pattern_fill_polygons,
33 rectangle,
34 solid_fill_polygon,
35 svg_path,
36 svg_segments,
37};
38
39pub struct Generator {
40 default_options: Options,
41}
42
43impl Default for Generator {
44 fn default() -> Self {
45 Self {
46 default_options: OptionsBuilder::default()
47 .seed(345_u64)
48 .build()
49 .expect("failed to build default options"),
50 }
51 }
52}
53
54impl Generator {
55 fn new(options: Options) -> Self {
56 Generator { default_options: options }
57 }
58
59 fn d<T, F>(&self, name: T, op_sets: &[OpSet<F>], options: &Option<Options>) -> Drawable<F>
60 where
61 T: Into<String>,
62 F: Float + Trig + FromPrimitive,
63 {
64 Drawable {
65 shape: name.into(),
66 options: options
67 .clone()
68 .unwrap_or_else(|| self.default_options.clone()),
69 sets: Vec::from_iter(op_sets.iter().cloned()),
70 }
71 }
72
73 pub fn line<F>(&self, x1: F, y1: F, x2: F, y2: F, options: &Option<Options>) -> Drawable<F>
74 where
75 F: Float + Trig + FromPrimitive,
76 {
77 self.d(
78 "line",
79 &[line(
80 x1,
81 y1,
82 x2,
83 y2,
84 &mut options
85 .clone()
86 .unwrap_or_else(|| self.default_options.clone()),
87 )],
88 options,
89 )
90 }
91
92 pub fn rectangle<F>(
93 &self,
94 x: F,
95 y: F,
96 width: F,
97 height: F,
98 options: &Option<Options>,
99 ) -> Drawable<F>
100 where
101 F: Float + Trig + FromPrimitive,
102 {
103 let mut paths = vec![];
104 let mut options = options
105 .clone()
106 .unwrap_or_else(|| self.default_options.clone());
107 let outline = rectangle(x, y, width, height, &mut options);
108 if options.fill.is_some() {
109 let points = vec![
110 Point2D::new(x, y),
111 Point2D::new(x + width, y),
112 Point2D::new(x + width, y + height),
113 Point2D::new(x, y + height),
114 ];
115 if options.fill_style == Some(FillStyle::Solid) {
116 paths.push(solid_fill_polygon(&vec![points], &mut options));
117 } else {
118 paths.push(pattern_fill_polygons(vec![points], &mut options));
119 }
120 }
121 if options.stroke.is_some() {
122 paths.push(outline);
123 }
124
125 self.d("rectangle", &paths, &Some(options))
126 }
127
128 pub fn ellipse<F>(
129 &self,
130 x: F,
131 y: F,
132 width: F,
133 height: F,
134 options: &Option<Options>,
135 ) -> Drawable<F>
136 where
137 F: Float + Trig + FromPrimitive,
138 {
139 let mut paths = vec![];
140 let mut options = options
141 .clone()
142 .unwrap_or_else(|| self.default_options.clone());
143 let ellipse_params = generate_ellipse_params(width, height, &mut options);
144 let ellipse_response = ellipse_with_params(x, y, &mut options, &ellipse_params);
145 if options.fill.is_some() {
146 if options.fill_style == Some(FillStyle::Solid) {
147 let mut shape = ellipse_with_params(x, y, &mut options, &ellipse_params).opset;
148 shape.op_set_type = OpSetType::FillPath;
149 paths.push(shape);
150 } else {
151 paths.push(pattern_fill_polygons(
152 vec![ellipse_response.estimated_points],
153 &mut options,
154 ));
155 }
156 }
157 if options.stroke.is_some() {
158 paths.push(ellipse_response.opset);
159 }
160 self.d("ellipse", &paths, &Some(options))
161 }
162
163 pub fn circle<F>(&self, x: F, y: F, diameter: F, options: &Option<Options>) -> Drawable<F>
164 where
165 F: Float + Trig + FromPrimitive,
166 {
167 let mut shape = self.ellipse(x, y, diameter, diameter, options);
168 shape.shape = "circle".into();
169 shape
170 }
171
172 pub fn linear_path<F>(
173 &self,
174 points: &[Point2D<F>],
175 close: bool,
176 options: &Option<Options>,
177 ) -> Drawable<F>
178 where
179 F: Float + Trig + FromPrimitive,
180 {
181 let mut options = options
182 .clone()
183 .unwrap_or_else(|| self.default_options.clone());
184 self.d(
185 "linear_path",
186 &[linear_path(points, close, &mut options)],
187 &Some(options),
188 )
189 }
190
191 pub fn arc<F>(
192 &self,
193 x: F,
194 y: F,
195 width: F,
196 height: F,
197 start: F,
198 stop: F,
199 closed: bool,
200 options: &Option<Options>,
201 ) -> Drawable<F>
202 where
203 F: Float + Trig + FromPrimitive,
204 {
205 let mut options = options
206 .clone()
207 .unwrap_or_else(|| self.default_options.clone());
208 let mut paths = vec![];
209 let outline =
210 crate::renderer::arc(x, y, width, height, start, stop, closed, true, &mut options);
211 if closed && options.fill.is_some() {
212 if options.fill_style == Some(FillStyle::Solid) {
213 options.disable_multi_stroke = Some(true);
214 let mut shape = crate::renderer::arc(
215 x,
216 y,
217 width,
218 height,
219 start,
220 stop,
221 true,
222 false,
223 &mut options,
224 );
225 shape.op_set_type = OpSetType::FillPath;
226 paths.push(shape);
227 } else {
228 paths.push(pattern_fill_arc(
229 x,
230 y,
231 width,
232 height,
233 start,
234 stop,
235 &mut options,
236 ));
237 }
238 }
239 if options.stroke.is_some() {
240 paths.push(outline);
241 }
242 self.d("arc", &paths, &Some(options))
243 }
244
245 pub fn bezier_quadratic<F>(
246 &self,
247 start: Point2D<F>,
248 cp: Point2D<F>,
249 end: Point2D<F>,
250 options: &Option<Options>,
251 ) -> Drawable<F>
252 where
253 F: Float + Trig + FromPrimitive + MulAssign + Display,
254 {
255 let mut paths = vec![];
256 let mut options = options
257 .clone()
258 .unwrap_or_else(|| self.default_options.clone());
259
260 let outline = bezier_quadratic(start, cp, end, &mut options);
261
262 if options.fill.is_some() {
263 let cubic = convert_bezier_quadratic_to_cubic(BezierQuadratic { start, cp, end });
265 let crv = vec![cubic.start, cubic.cp1, cubic.cp2, cubic.end];
266
267 let poly_points = points_on_bezier_curves(
268 &crv,
269 _c(10.0),
270 Some(_c::<F>(1.0) + _c::<F>(options.roughness.unwrap_or(0.0)) / _c(2.0)),
271 );
272 if options.fill_style == Some(FillStyle::Solid) {
273 paths.push(solid_fill_polygon(&vec![poly_points], &mut options));
274 } else {
275 paths.push(pattern_fill_polygons(&mut vec![poly_points], &mut options));
276 }
277 }
278
279 if options.stroke.is_some() {
280 paths.push(outline);
281 }
282
283 self.d("curve", &paths, &Some(options))
284 }
285
286 pub fn bezier_cubic<F>(
287 &self,
288 start: Point2D<F>,
289 cp1: Point2D<F>,
290 cp2: Point2D<F>,
291 end: Point2D<F>,
292 options: &Option<Options>,
293 ) -> Drawable<F>
294 where
295 F: Float + Trig + FromPrimitive + MulAssign + Display,
296 {
297 let mut paths = vec![];
298 let mut options = options
299 .clone()
300 .unwrap_or_else(|| self.default_options.clone());
301
302 let outline = bezier_cubic(start, cp1, cp2, end, &mut options);
303
304 if options.fill.is_some() {
305 let crv = vec![start, cp1, cp2, end];
306
307 let poly_points = points_on_bezier_curves(
308 &crv,
309 _c(10.0),
310 Some(_c::<F>(1.0) + _c::<F>(options.roughness.unwrap_or(0.0)) / _c(2.0)),
311 );
312 if options.fill_style == Some(FillStyle::Solid) {
313 paths.push(solid_fill_polygon(&vec![poly_points], &mut options));
314 } else {
315 paths.push(pattern_fill_polygons(&mut vec![poly_points], &mut options));
316 }
317 }
318
319 if options.stroke.is_some() {
320 paths.push(outline);
321 }
322
323 self.d("curve", &paths, &Some(options))
324 }
325
326 pub fn curve<F>(&self, points: &[Point2D<F>], options: &Option<Options>) -> Drawable<F>
327 where
328 F: Float + Trig + FromPrimitive + MulAssign + Display,
329 {
330 let mut paths = vec![];
331 let mut options = options
332 .clone()
333 .unwrap_or_else(|| self.default_options.clone());
334 let outline = curve(points, &mut options);
335 if options.fill.is_some() && points.len() >= 3 {
336 let curve = curve_to_bezier(points, _c(0.0));
337 if let Some(crv) = curve {
338 let poly_points = points_on_bezier_curves(
339 &crv,
340 _c(10.0),
341 Some(_c::<F>(1.0) + _c::<F>(options.roughness.unwrap_or(0.0)) / _c(2.0)),
342 );
343 if options.fill_style == Some(FillStyle::Solid) {
344 paths.push(solid_fill_polygon(&vec![poly_points], &mut options));
345 } else {
346 paths.push(pattern_fill_polygons(&mut vec![poly_points], &mut options));
347 }
348 }
349 }
350
351 if options.stroke.is_some() {
352 paths.push(outline);
353 }
354
355 self.d("curve", &paths, &Some(options))
356 }
357
358 pub fn polygon<F>(&self, points: &[Point2D<F>], options: &Option<Options>) -> Drawable<F>
359 where
360 F: Float + Trig + FromPrimitive + MulAssign + Display,
361 {
362 let mut options = options
363 .clone()
364 .unwrap_or_else(|| self.default_options.clone());
365 let mut paths = vec![];
366 let outline = linear_path(points, true, &mut options);
367 if options.fill.is_some() {
368 if options.fill_style == Some(FillStyle::Solid) {
369 paths.push(solid_fill_polygon(&vec![points.to_vec()], &mut options));
370 } else {
371 paths.push(pattern_fill_polygons(
372 &mut vec![points.to_vec()],
373 &mut options,
374 ));
375 }
376 }
377 if options.stroke.is_some() {
378 paths.push(outline);
379 }
380 self.d("polygon", &paths, &Some(options))
381 }
382
383 pub fn path<F>(&self, d: String, options: &Option<Options>) -> Drawable<F>
384 where
385 F: Float + Trig + FromPrimitive + MulAssign + Display,
386 {
387 let mut options = options.clone().unwrap_or(self.default_options.clone());
388 let mut paths = vec![];
389 if d.is_empty() {
390 self.d("path", &paths, &Some(options))
391 } else {
392 let simplified = options.simplification.map(|a| a < 1.0).unwrap_or(false);
393 let distance = if simplified {
394 _c::<F>(4.0) - _c::<F>(4.0) * _c::<F>(options.simplification.unwrap())
395 } else {
396 (_c::<F>(1.0) + _c::<F>(options.roughness.unwrap_or(1.0))) / _c::<F>(2.0)
397 };
398
399 let sets = points_on_path(d.clone(), Some(_c(1.0)), Some(distance));
400 if options.fill.is_some() {
401 if options.fill_style == Some(FillStyle::Solid) {
402 paths.push(solid_fill_polygon(&sets, &mut options));
403 } else {
404 paths.push(pattern_fill_polygons(sets.clone(), &mut options));
405 }
406 }
407
408 if options.stroke.is_some() {
409 if simplified {
410 sets.iter()
411 .for_each(|s| paths.push(linear_path(s, false, &mut options)));
412 } else {
413 paths.push(svg_path(d, &mut options));
414 }
415 }
416
417 self.d("path", &paths, &Some(options))
418 }
419 }
420
421 pub fn path_from_segments<F>(
422 &self,
423 segments: Vec<PathSegment>,
424 options: &Option<Options>,
425 ) -> Drawable<F>
426 where
427 F: Float + Trig + FromPrimitive + MulAssign + Display,
428 {
429 let mut options = options.clone().unwrap_or(self.default_options.clone());
430 let mut paths = vec![];
431 if segments.is_empty() {
432 self.d("path", &paths, &Some(options))
433 } else {
434 let simplified = options.simplification.map(|a| a < 1.0).unwrap_or(false);
435 let distance = if simplified {
436 _c::<F>(4.0) - _c::<F>(4.0) * _c::<F>(options.simplification.unwrap())
437 } else {
438 (_c::<F>(1.0) + _c::<F>(options.roughness.unwrap_or(1.0))) / _c::<F>(2.0)
439 };
440
441 let sets = points_on_segments(segments.clone(), Some(_c(1.0)), Some(distance));
442 if options.fill.is_some() {
443 if options.fill_style == Some(FillStyle::Solid) {
444 paths.push(solid_fill_polygon(&sets, &mut options));
445 } else {
446 paths.push(pattern_fill_polygons(sets.clone(), &mut options));
447 }
448 }
449
450 if options.stroke.is_some() {
451 if simplified {
452 sets.iter()
453 .for_each(|s| paths.push(linear_path(s, false, &mut options)));
454 } else {
455 paths.push(svg_segments(segments, &mut options));
456 }
457 }
458
459 self.d("path", &paths, &Some(options))
460 }
461 }
462
463 pub fn ops_to_path<F>(mut drawing: OpSet<F>, fixed_decimals: Option<u32>) -> String
464 where
465 F: Float + FromPrimitive + Trig + Display,
466 {
467 let mut path = String::new();
468
469 for item in drawing.ops.iter_mut() {
470 if let Some(fd) = fixed_decimals {
471 let pow: u32 = 10u32.pow(fd);
472 item.data.iter_mut().for_each(|p| {
473 *p = (*p * F::from(pow).unwrap()).round() / F::from(pow).unwrap();
474 });
475 }
476
477 match item.op {
478 OpType::Move => {
479 write!(&mut path, "L{} {} ", item.data[0], item.data[1])
480 .expect("Failed to write path string");
481 }
482 OpType::BCurveTo => {
483 write!(
484 &mut path,
485 "C{} {}, {} {}, {} {} ",
486 item.data[0],
487 item.data[1],
488 item.data[2],
489 item.data[3],
490 item.data[4],
491 item.data[5]
492 )
493 .expect("Failed to write path string");
494 }
495 OpType::LineTo => {
496 write!(&mut path, "L{} {}, ", item.data[0], item.data[1])
497 .expect("Failed to write path string");
498 }
499 }
500 }
501
502 path
503 }
504
505 pub fn to_paths<F>(drawable: Drawable<F>) -> Vec<PathInfo>
506 where
507 F: Float + FromPrimitive + Trig + Display,
508 {
509 let sets = drawable.sets;
510 let o = drawable.options;
511 let mut path_infos = vec![];
512 for drawing in sets.iter() {
513 let path_info = match drawing.op_set_type {
514 OpSetType::Path => PathInfo {
515 d: Self::ops_to_path(drawing.clone(), None),
516 stroke: o.stroke,
517 stroke_width: o.stroke_width,
518 fill: None,
519 },
520 OpSetType::FillPath => PathInfo {
521 d: Self::ops_to_path(drawing.clone(), None),
522 stroke: None,
523 stroke_width: Some(0.0f32),
524 fill: o.fill,
525 },
526 OpSetType::FillSketch => {
527 let fill_weight = if o.fill_weight.unwrap_or(0.0) < 0.0 {
528 o.stroke_width.unwrap_or(0.0) / 2.0
529 } else {
530 o.fill_weight.unwrap_or(0.0)
531 };
532 PathInfo {
533 d: Self::ops_to_path(drawing.clone(), None),
534 stroke: o.fill,
535 stroke_width: Some(fill_weight),
536 fill: None,
537 }
538 }
539 };
540 path_infos.push(path_info);
541 }
542 path_infos
543 }
544}