roughr/
generator.rs

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            // The fill algorithms expect at least 4 points of a cubic curve, else they panic
264            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}