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