Skip to main content

roughr/
renderer.rs

1use std::borrow::BorrowMut;
2
3use euclid::default::Point2D;
4use euclid::{point2, Trig};
5use num_traits::{Float, FloatConst, FromPrimitive};
6use svg_path_ops::{absolutize, normalize};
7use svgtypes::{PathParser, PathSegment};
8
9use super::core::{Options, _c};
10use crate::core::{FillStyle, Op, OpSet, OpSetType, OpType, _cc};
11use crate::filler::get_filler;
12use crate::filler::FillerType::{
13    DashedFiller, DotFiller, HatchFiller, ScanLineHachure, ZigZagFiller, ZigZagLineFiller,
14};
15use crate::geometry::{convert_bezier_quadratic_to_cubic, BezierQuadratic};
16
17#[derive(PartialEq, Eq, Debug)]
18pub struct EllipseParams<F: Float> {
19    pub rx: F,
20    pub ry: F,
21    pub increment: F,
22}
23
24pub struct EllipseResult<F: Float + FromPrimitive + Trig> {
25    pub opset: OpSet<F>,
26    pub estimated_points: Vec<Point2D<F>>,
27}
28
29/// Constructs a line primitive that can be rendered into relevant context
30/// # Arguments
31/// * `x1` - Line start point x coordinate
32/// * `y1` - Line start point y coordinate
33/// * `x2` - Line end point x coordinate
34/// * `y2` - Line end point y coordinate
35/// * `o`  - Line generation options
36///
37/// # Example
38/// Note that result of this call is highly dependent on your selections of
39/// options and random number seed you use
40///
41/// ```rust
42/// use roughr::core::{OpSetType, OptionsBuilder};
43/// use roughr::renderer::line;
44///
45/// // Use a non-zero seed for reproducible output.
46/// let mut o = OptionsBuilder::default().seed(1_u64).build().unwrap();
47/// let result = line(0.0, 0.0, 1.0, 0.0, &mut o);
48/// assert_eq!(result.op_set_type, OpSetType::Path);
49/// assert_eq!(result.size, None);
50/// assert_eq!(result.path, None);
51/// assert_eq!(result.ops.len(), 4);
52/// ```
53pub fn line<F: Float + Trig + FromPrimitive>(
54    x1: F,
55    y1: F,
56    x2: F,
57    y2: F,
58    o: &mut Options,
59) -> OpSet<F> {
60    OpSet {
61        op_set_type: OpSetType::Path,
62        ops: _double_line(x1, y1, x2, y2, o, false),
63        size: None,
64        path: None,
65    }
66}
67
68/// Constructs a linear path with given points by connecting consecutive points
69/// with rough line primitives. This function is also used by other high level
70/// constructs such as rectangle and polygon. For two element point list
71/// it calls line function
72///
73/// # Arguments
74/// * `points` - 2D Points which forms the path. Consecutive points will be connected to each other
75/// * `close` - If algorithm should connect last point to first point with a line
76/// * `o` - Path generation options.
77///
78/// # Example
79/// Note that result of this call is highly dependent on your selections of
80/// options and random number seed you use
81///
82///```rust
83/// use euclid::point2;
84/// use roughr::core::{OpSetType, OptionsBuilder};
85/// use roughr::renderer::linear_path;
86///
87/// // Use a non-zero seed for reproducible output.
88/// let mut o = OptionsBuilder::default().seed(1_u64).build().unwrap();
89/// let result = linear_path(
90///     &[point2(0.0f32, 0.0), point2(0.0, 0.1), point2(1.0, 1.0)],
91///     false,
92///     &mut o,
93/// );
94/// assert_eq!(result.op_set_type, OpSetType::Path);
95/// assert_eq!(result.size, None);
96/// assert_eq!(result.path, None);
97/// ```
98pub fn linear_path<F: Float + Trig + FromPrimitive>(
99    points: &[Point2D<F>],
100    close: bool,
101    o: &mut Options,
102) -> OpSet<F> {
103    let len = points.len();
104    if len > 2 {
105        let mut ops: Vec<Op<F>> = Vec::new();
106        let mut i = 0;
107        while i < (len - 1) {
108            ops.append(&mut _double_line(
109                points[i].x,
110                points[i].y,
111                points[i + 1].x,
112                points[i + 1].y,
113                o,
114                false,
115            ));
116            i += 1;
117        }
118        if close {
119            ops.append(&mut _double_line(
120                points[len - 1].x,
121                points[len - 1].y,
122                points[0].x,
123                points[0].y,
124                o,
125                false,
126            ));
127        }
128        OpSet {
129            op_set_type: OpSetType::Path,
130            ops,
131            path: None,
132            size: None,
133        }
134    } else if len == 2 {
135        line(points[0].x, points[0].y, points[1].x, points[1].y, o)
136    } else {
137        OpSet {
138            op_set_type: OpSetType::Path,
139            ops: Vec::new(),
140            path: None,
141            size: None,
142        }
143    }
144}
145
146pub fn polygon<F: Float + Trig + FromPrimitive>(
147    points: &[Point2D<F>],
148    o: &mut Options,
149) -> OpSet<F> {
150    linear_path(points, true, o)
151}
152
153pub fn rectangle<F: Float + Trig + FromPrimitive>(
154    x: F,
155    y: F,
156    width: F,
157    height: F,
158    o: &mut Options,
159) -> OpSet<F> {
160    let points: Vec<Point2D<F>> = vec![
161        Point2D::new(x, y),
162        Point2D::new(x + width, y),
163        Point2D::new(x + width, y + height),
164        Point2D::new(x, y + height),
165    ];
166    polygon(&points, o)
167}
168
169pub fn bezier_quadratic<F: Float + Trig + FromPrimitive>(
170    start: Point2D<F>,
171    cp: Point2D<F>,
172    end: Point2D<F>,
173    o: &mut Options,
174) -> OpSet<F> {
175    let ops = _bezier_quadratic_to(cp.x, cp.y, end.x, end.y, &start, o);
176
177    OpSet {
178        op_set_type: OpSetType::Path,
179        ops,
180        path: None,
181        size: None,
182    }
183}
184
185pub fn bezier_cubic<F: Float + Trig + FromPrimitive>(
186    start: Point2D<F>,
187    cp1: Point2D<F>,
188    cp2: Point2D<F>,
189    end: Point2D<F>,
190    o: &mut Options,
191) -> OpSet<F> {
192    let ops = _bezier_to(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y, &start, o);
193
194    OpSet {
195        op_set_type: OpSetType::Path,
196        ops,
197        path: None,
198        size: None,
199    }
200}
201
202pub fn curve<F: Float + Trig + FromPrimitive>(points: &[Point2D<F>], o: &mut Options) -> OpSet<F> {
203    let mut o1 = _curve_with_offset(
204        points,
205        _c::<F>(1.0) * _c(1.0 + o.roughness.unwrap_or(0.0) * 0.2),
206        o,
207    );
208    if !o.disable_multi_stroke.unwrap_or(false) {
209        let mut o2 = _curve_with_offset(
210            points,
211            _c::<F>(1.5) * _c(1.0 + o.roughness.unwrap_or(0.0) * 0.22),
212            &mut clone_options_alter_seed(o),
213        );
214        o1.append(&mut o2);
215    }
216    OpSet {
217        op_set_type: OpSetType::Path,
218        ops: o1,
219        path: None,
220        size: None,
221    }
222}
223
224pub fn ellipse<F: Float + Trig + FromPrimitive>(
225    x: F,
226    y: F,
227    width: F,
228    height: F,
229    o: &mut Options,
230) -> OpSet<F> {
231    let params = generate_ellipse_params(width, height, o);
232    ellipse_with_params(x, y, o, &params).opset
233}
234
235pub fn generate_ellipse_params<F: Float + Trig + FromPrimitive>(
236    width: F,
237    height: F,
238    o: &mut Options,
239) -> EllipseParams<F> {
240    let psq: F = Float::sqrt(
241        _cc::<F>(std::f64::consts::PI * 2.0)
242            * Float::sqrt(
243                (Float::powi(width / _c(2.0), 2) + Float::powi(height / _c(2.0), 2)) / _c(2.0),
244            ),
245    );
246    let step_count: F = Float::ceil(Float::max(
247        _c(o.curve_step_count.unwrap_or(1.0)),
248        _c::<F>(o.curve_step_count.unwrap_or(1.0) / Float::sqrt(200.0)) * psq,
249    ));
250    let increment: F = _cc::<F>(std::f64::consts::PI * 2.0) / step_count;
251    let mut rx = Float::abs(width / _c(2.0));
252    let mut ry = Float::abs(height / _c(2.0));
253    let curve_fit_randomness: F = _c::<F>(1.0) - _c(o.curve_fitting.unwrap_or(0.0));
254    rx = rx + _offset_opt(rx * curve_fit_randomness, o, None);
255    ry = ry + _offset_opt(ry * curve_fit_randomness, o, None);
256    EllipseParams { increment, rx, ry }
257}
258
259pub fn ellipse_with_params<F: Float + Trig + FromPrimitive>(
260    x: F,
261    y: F,
262    o: &mut Options,
263    ellipse_params: &EllipseParams<F>,
264) -> EllipseResult<F> {
265    let ellipse_points = _compute_ellipse_points(
266        ellipse_params.increment,
267        x,
268        y,
269        ellipse_params.rx,
270        ellipse_params.ry,
271        _c(1.0),
272        ellipse_params.increment
273            * _offset(
274                _c(0.1),
275                _offset(_c::<F>(0.4), _c::<F>(1.0), o, None),
276                o,
277                None,
278            ),
279        o,
280    );
281    let ap1 = ellipse_points[0].clone();
282    let cp1 = ellipse_points[1].clone();
283    let mut o1 = _curve(&ap1, None, o);
284    if (!o.disable_multi_stroke.unwrap_or(false)) && (o.roughness.unwrap_or(0.0) != 0.0) {
285        let inner_ellipse_points = _compute_ellipse_points(
286            ellipse_params.increment,
287            x,
288            y,
289            ellipse_params.rx,
290            ellipse_params.ry,
291            _c::<F>(1.5),
292            _c::<F>(0.0),
293            o,
294        );
295        let ap2 = inner_ellipse_points[0].clone();
296        let _cp2 = inner_ellipse_points[1].clone();
297        let mut o2 = _curve(&ap2, None, o);
298        o1.append(&mut o2);
299    }
300    EllipseResult {
301        estimated_points: cp1,
302        opset: OpSet {
303            op_set_type: OpSetType::Path,
304            ops: o1,
305            size: None,
306            path: None,
307        },
308    }
309}
310
311#[allow(clippy::too_many_arguments)]
312pub fn arc<F: Float + Trig + FromPrimitive>(
313    x: F,
314    y: F,
315    width: F,
316    height: F,
317    start: F,
318    stop: F,
319    closed: bool,
320    rough_closure: bool,
321    o: &mut Options,
322) -> OpSet<F> {
323    let cx = x;
324    let cy = y;
325    let mut rx = Float::abs(width / _c(2.0));
326    let mut ry = Float::abs(height / _c(2.0));
327    rx = rx + _offset_opt(rx * _c(0.01), o, None);
328    ry = ry + _offset_opt(ry * _c(0.01), o, None);
329    let mut strt: F = start;
330    let mut stp: F = stop;
331    while strt < _c(0.0) {
332        strt = strt + _c(f32::PI() * 2.0);
333        stp = stp + _c(f32::PI() * 2.0);
334    }
335    if (stp - strt) > _c(f32::PI() * 2.0) {
336        strt = _c(0.0);
337        stp = _c(f32::PI() * 2.0);
338    }
339    let ellipse_inc: F = _c::<F>(f32::PI() * 2.0) / _c(o.curve_step_count.unwrap_or(1.0));
340    let arc_inc = Float::min(ellipse_inc / _c(2.0), (stp - strt) / _c(2.0));
341    let mut ops = _arc(arc_inc, cx, cy, rx, ry, strt, stp, _c(1.0), o);
342    if !o.disable_multi_stroke.unwrap_or(false) {
343        let mut o2 = _arc(arc_inc, cx, cy, rx, ry, strt, stp, _c(1.5), o);
344        ops.append(&mut o2);
345    }
346    if closed {
347        if rough_closure {
348            ops.append(&mut _double_line(
349                cx,
350                cy,
351                cx + rx * Float::cos(strt),
352                cy + ry * Float::sin(strt),
353                o,
354                false,
355            ));
356            ops.append(&mut _double_line(
357                cx,
358                cy,
359                cx + rx * Float::cos(stp),
360                cy + ry * Float::sin(stp),
361                o,
362                false,
363            ));
364        } else {
365            ops.push(Op {
366                op: OpType::LineTo,
367                data: vec![cx, cy],
368            });
369            ops.push(Op {
370                op: OpType::LineTo,
371                data: vec![cx + rx * Float::cos(strt), cy + ry * Float::sin(strt)],
372            });
373        }
374    }
375    OpSet {
376        op_set_type: OpSetType::Path,
377        ops,
378        path: None,
379        size: None,
380    }
381}
382
383pub fn solid_fill_polygon<F: Float + Trig + FromPrimitive>(
384    polygon_list: &Vec<Vec<Point2D<F>>>,
385    options: &mut Options,
386) -> OpSet<F> {
387    let mut ops = vec![];
388    for polygon in polygon_list {
389        if polygon.len() > 2 {
390            let rand_offset = _c(options.max_randomness_offset.unwrap_or(2.0));
391            polygon.iter().enumerate().for_each(|(ind, point)| {
392                if ind == 0 {
393                    ops.push(Op {
394                        op: OpType::Move,
395                        data: vec![
396                            point.x + _offset_opt(rand_offset, options, None),
397                            point.y + _offset_opt(rand_offset, options, None),
398                        ],
399                    });
400                } else {
401                    ops.push(Op {
402                        op: OpType::LineTo,
403                        data: vec![
404                            point.x + _offset_opt(rand_offset, options, None),
405                            point.y + _offset_opt(rand_offset, options, None),
406                        ],
407                    });
408                }
409            })
410        }
411    }
412    OpSet {
413        op_set_type: OpSetType::FillPath,
414        ops,
415        size: None,
416        path: None,
417    }
418}
419
420pub fn rand_offset<F: Float + Trig + FromPrimitive>(x: F, o: &mut Options) -> F {
421    _offset_opt(x, o, None)
422}
423
424pub fn rand_offset_with_range<F: Float + Trig + FromPrimitive>(
425    min: F,
426    max: F,
427    o: &mut Options,
428) -> F {
429    _offset(min, max, o, None)
430}
431
432pub fn double_line_fill_ops<F: Float + Trig + FromPrimitive>(
433    x1: F,
434    y1: F,
435    x2: F,
436    y2: F,
437    o: &mut Options,
438) -> Vec<Op<F>> {
439    _double_line(x1, y1, x2, y2, o, true)
440}
441
442fn clone_options_alter_seed(ops: &mut Options) -> Options {
443    // Match Rough.js `cloneOptionsAlterSeed` from `bin/renderer.js`:
444    // - clones the options object
445    // - clears `randomizer` so the clone creates its own PRNG stream
446    // - if `seed` is truthy (non-zero), increments it by 1
447    let mut result: Options = ops.clone();
448    result.randomizer = None;
449    if let Some(seed) = ops.seed {
450        if seed != 0 {
451            result.seed = Some(seed + 1);
452        }
453    }
454    result
455}
456
457fn _offset<F: Float + Trig + FromPrimitive>(
458    min: F,
459    max: F,
460    ops: &mut Options,
461    roughness_gain: Option<F>,
462) -> F {
463    let rg: F = roughness_gain.unwrap_or_else(|| _c(1.0));
464    _c::<F>(ops.roughness.unwrap_or(1.0)) * rg * ((_cc::<F>(ops.random()) * (max - min)) + min)
465}
466
467fn _offset_opt<F: Float + Trig + FromPrimitive>(
468    x: F,
469    ops: &mut Options,
470    roughness_gain: Option<F>,
471) -> F {
472    _offset(-x, x, ops, roughness_gain)
473}
474
475fn _line<F: Float + Trig + FromPrimitive>(
476    x1: F,
477    y1: F,
478    x2: F,
479    y2: F,
480    o: &mut Options,
481    mover: bool,
482    overlay: bool,
483) -> Vec<Op<F>> {
484    let length_sq = (x1 - x2).powi(2) + (y1 - y2).powi(2);
485    let length = length_sq.sqrt();
486    let roughness_gain;
487    if length < _c(200.0_f32) {
488        roughness_gain = _c(1.0);
489    } else if length > _c(500.0) {
490        roughness_gain = _c(0.4);
491    } else {
492        roughness_gain = _c::<F>(-0.0016668) * length + _c(1.233334);
493    }
494
495    let mut offset = _c(o.max_randomness_offset.unwrap_or(2.0));
496    if (offset * offset * _c(100.0)) > length_sq {
497        offset = length / _c(10.0);
498    }
499    let half_offset = offset / _c(2.0);
500    let diverge_point = _c::<F>(0.2) + _cc::<F>(o.random()) * _c(0.2);
501    let mut mid_disp_x =
502        _c::<F>(o.bowing.unwrap_or(1.0)) * _c(o.max_randomness_offset.unwrap_or(2.0)) * (y2 - y1)
503            / _c(200.0);
504    let mut mid_disp_y =
505        _c::<F>(o.bowing.unwrap_or(1.0)) * _c(o.max_randomness_offset.unwrap_or(2.0)) * (x1 - x2)
506            / _c(200.0);
507    mid_disp_x = _offset_opt(mid_disp_x, o, Some(roughness_gain));
508    mid_disp_y = _offset_opt(mid_disp_y, o, Some(roughness_gain));
509    let mut ops: Vec<Op<F>> = Vec::new();
510
511    let preserve_vertices = o.preserve_vertices.unwrap_or(false);
512    if mover {
513        if overlay {
514            ops.push(Op {
515                op: OpType::Move,
516                data: vec![
517                    x1 + if preserve_vertices {
518                        _c(0.0)
519                    } else {
520                        _offset_opt(half_offset, o, Some(roughness_gain))
521                    },
522                    y1 + if preserve_vertices {
523                        _c(0.0)
524                    } else {
525                        _offset_opt(half_offset, o, Some(roughness_gain))
526                    },
527                ],
528            });
529        } else {
530            ops.push(Op {
531                op: OpType::Move,
532                data: vec![
533                    x1 + if preserve_vertices {
534                        _c(0.0)
535                    } else {
536                        _offset_opt(offset, o, Some(roughness_gain))
537                    },
538                    y1 + if preserve_vertices {
539                        _c(0.0)
540                    } else {
541                        _offset_opt(offset, o, Some(roughness_gain))
542                    },
543                ],
544            });
545        }
546    }
547    if overlay {
548        ops.push(Op {
549            op: OpType::BCurveTo,
550            data: vec![
551                mid_disp_x
552                    + x1
553                    + (x2 - x1) * diverge_point
554                    + _offset_opt(half_offset, o, Some(roughness_gain)),
555                mid_disp_y
556                    + y1
557                    + (y2 - y1) * diverge_point
558                    + _offset_opt(half_offset, o, Some(roughness_gain)),
559                mid_disp_x
560                    + x1
561                    + _c::<F>(2.0) * (x2 - x1) * diverge_point
562                    + _offset_opt(half_offset, o, Some(roughness_gain)),
563                mid_disp_y
564                    + y1
565                    + _c::<F>(2.0) * (y2 - y1) * diverge_point
566                    + _offset_opt(half_offset, o, Some(roughness_gain)),
567                x2 + if preserve_vertices {
568                    _c(0.0)
569                } else {
570                    _offset_opt(half_offset, o, Some(roughness_gain))
571                },
572                y2 + if preserve_vertices {
573                    _c(0.0)
574                } else {
575                    _offset_opt(half_offset, o, Some(roughness_gain))
576                },
577            ],
578        });
579    } else {
580        ops.push(Op {
581            op: OpType::BCurveTo,
582            data: vec![
583                mid_disp_x
584                    + x1
585                    + (x2 - x1) * diverge_point
586                    + _offset_opt(offset, o, Some(roughness_gain)),
587                mid_disp_y
588                    + y1
589                    + (y2 - y1) * diverge_point
590                    + _offset_opt(offset, o, Some(roughness_gain)),
591                mid_disp_x
592                    + x1
593                    + _c::<F>(2.0) * (x2 - x1) * diverge_point
594                    + _offset_opt(offset, o, Some(roughness_gain)),
595                mid_disp_y
596                    + y1
597                    + _c::<F>(2.0) * (y2 - y1) * diverge_point
598                    + _offset_opt(offset, o, Some(roughness_gain)),
599                x2 + if preserve_vertices {
600                    _c(0.0)
601                } else {
602                    _offset_opt(offset, o, Some(roughness_gain))
603                },
604                y2 + if preserve_vertices {
605                    _c(0.0)
606                } else {
607                    _offset_opt(offset, o, Some(roughness_gain))
608                },
609            ],
610        });
611    }
612    ops
613}
614
615pub(crate) fn _double_line<F: Float + Trig + FromPrimitive>(
616    x1: F,
617    y1: F,
618    x2: F,
619    y2: F,
620    o: &mut Options,
621    filling: bool,
622) -> Vec<Op<F>> {
623    let single_stroke = if filling {
624        o.disable_multi_stroke_fill.unwrap_or(false)
625    } else {
626        o.disable_multi_stroke.unwrap_or(false)
627    };
628    let mut o1 = _line(x1, y1, x2, y2, o, true, false);
629    if single_stroke {
630        o1
631    } else {
632        let mut o2 = _line(x1, y1, x2, y2, o, true, true);
633        o1.append(&mut o2);
634        o1
635    }
636}
637
638pub(crate) fn _curve<F: Float + Trig + FromPrimitive>(
639    points: &[Point2D<F>],
640    close_point: Option<Point2D<F>>,
641    o: &mut Options,
642) -> Vec<Op<F>> {
643    let len = points.len();
644    let mut ops: Vec<Op<F>> = vec![];
645    if len > 3 {
646        let mut b: [[F; 2]; 4] = [[_c(0.0); 2]; 4];
647        let s: F = _c::<F>(1.0) - _c(o.curve_tightness.unwrap_or(0.0));
648        ops.push(Op {
649            op: OpType::Move,
650            data: vec![points[1].x, points[1].y],
651        });
652        let mut i = 1;
653        while (i + 2) < len {
654            let cached_vert_array = points[i];
655            b[0] = [cached_vert_array.x, cached_vert_array.y];
656            b[1] = [
657                cached_vert_array.x + (s * points[i + 1].x - s * points[i - 1].x) / _c(6.0),
658                cached_vert_array.y + (s * points[i + 1].y - s * points[i - 1].y) / _c(6.0),
659            ];
660            b[2] = [
661                points[i + 1].x + (s * points[i].x - s * points[i + 2].x) / _c(6.0),
662                points[i + 1].y + (s * points[i].y - s * points[i + 2].y) / _c(6.0),
663            ];
664            b[3] = [points[i + 1].x, points[i + 1].y];
665            ops.push(Op {
666                op: OpType::BCurveTo,
667                data: vec![b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]],
668            });
669            i += 1;
670        }
671        if let Some(cp) = close_point {
672            let ro = _c(o.max_randomness_offset.unwrap_or(2.0));
673            ops.push(Op {
674                op: OpType::LineTo,
675                data: vec![
676                    cp.x + _offset_opt(ro, o, None),
677                    cp.y + _offset_opt(ro, o, None),
678                ],
679            });
680        }
681    } else if len == 3 {
682        ops.push(Op {
683            op: OpType::Move,
684            data: vec![points[1].x, points[1].y],
685        });
686        ops.push(Op {
687            op: OpType::BCurveTo,
688            data: vec![
689                points[1].x,
690                points[1].y,
691                points[2].x,
692                points[2].y,
693                points[2].x,
694                points[2].y,
695            ],
696        });
697    } else if len == 2 {
698        ops.append(&mut _double_line(
699            points[0].x,
700            points[0].y,
701            points[1].x,
702            points[1].y,
703            o,
704            false,
705        ));
706    }
707    ops
708}
709
710fn _curve_with_offset<F: Float + Trig + FromPrimitive>(
711    points: &[Point2D<F>],
712    offset: F,
713    o: &mut Options,
714) -> Vec<Op<F>> {
715    let mut ps: Vec<Point2D<F>> = vec![
716        Point2D::new(
717            points[0].x + _offset_opt(offset, o, None),
718            points[0].y + _offset_opt(offset, o, None),
719        ),
720        Point2D::new(
721            points[0].x + _offset_opt(offset, o, None),
722            points[0].y + _offset_opt(offset, o, None),
723        ),
724    ];
725    let mut i = 1;
726    while i < points.len() {
727        ps.push(Point2D::new(
728            points[i].x + _offset_opt(offset, o, None),
729            points[i].y + _offset_opt(offset, o, None),
730        ));
731        if i == (points.len() - 1) {
732            ps.push(Point2D::new(
733                points[i].x + _offset_opt(offset, o, None),
734                points[i].y + _offset_opt(offset, o, None),
735            ));
736        }
737        i += 1;
738    }
739    _curve(&ps, None, o)
740}
741
742#[allow(clippy::too_many_arguments)]
743pub(crate) fn _compute_ellipse_points<F: Float + Trig + FromPrimitive>(
744    increment: F,
745    cx: F,
746    cy: F,
747    rx: F,
748    ry: F,
749    offset: F,
750    overlap: F,
751    o: &mut Options,
752) -> Vec<Vec<Point2D<F>>> {
753    let core_only = o.roughness.unwrap_or(0.0) == 0.0;
754    let mut core_points: Vec<Point2D<F>> = Vec::new();
755    let mut all_points: Vec<Point2D<F>> = Vec::new();
756
757    if core_only {
758        let increment_inner = increment / _c(4.0);
759        all_points.push(Point2D::new(
760            cx + rx * Float::cos(-increment_inner),
761            cy + ry * Float::sin(-increment_inner),
762        ));
763
764        let mut angle = _c(0.0);
765        while angle <= _cc::<F>(std::f64::consts::PI * 2.0) {
766            let p = Point2D::new(cx + rx * Float::cos(angle), cy + ry * Float::sin(angle));
767            core_points.push(p);
768            all_points.push(p);
769            angle = angle + increment_inner;
770        }
771        all_points.push(Point2D::new(
772            cx + rx * Float::cos(_c(0.0)),
773            cy + ry * Float::sin(_c(0.0)),
774        ));
775        all_points.push(Point2D::new(
776            cx + rx * Float::cos(increment_inner),
777            cy + ry * Float::sin(increment_inner),
778        ));
779    } else {
780        let rad_offset: F = _offset_opt::<F>(_c(0.5), o, None) - (_c::<F>(f32::PI()) / _c(2.0));
781        all_points.push(Point2D::new(
782            _offset_opt(offset, o, None)
783                + cx
784                + _c::<F>(0.9) * rx * Float::cos(rad_offset - increment),
785            _offset_opt(offset, o, None)
786                + cy
787                + _c::<F>(0.9) * ry * Float::sin(rad_offset - increment),
788        ));
789        let end_angle = _cc::<F>(std::f64::consts::PI * 2.0) + rad_offset - _c(0.01);
790        let mut angle = rad_offset;
791        while angle < end_angle {
792            let p = Point2D::new(
793                _offset_opt(offset, o, None) + cx + rx * Float::cos(angle),
794                _offset_opt(offset, o, None) + cy + ry * Float::sin(angle),
795            );
796            core_points.push(p);
797            all_points.push(p);
798            angle = angle + increment;
799        }
800
801        all_points.push(Point2D::new(
802            _offset_opt(offset, o, None)
803                + cx
804                + rx * Float::cos(
805                    rad_offset + _cc::<F>(std::f64::consts::PI * 2.0) + overlap * _c(0.5),
806                ),
807            _offset_opt(offset, o, None)
808                + cy
809                + ry * Float::sin(
810                    rad_offset + _cc::<F>(std::f64::consts::PI * 2.0) + overlap * _c(0.5),
811                ),
812        ));
813        all_points.push(Point2D::new(
814            _offset_opt(offset, o, None)
815                + cx
816                + _c::<F>(0.98) * rx * Float::cos(rad_offset + overlap),
817            _offset_opt(offset, o, None)
818                + cy
819                + _c::<F>(0.98) * ry * Float::sin(rad_offset + overlap),
820        ));
821        all_points.push(Point2D::new(
822            _offset_opt(offset, o, None)
823                + cx
824                + _c::<F>(0.9) * rx * Float::cos(rad_offset + overlap * _c(0.5)),
825            _offset_opt(offset, o, None)
826                + cy
827                + _c::<F>(0.9) * ry * Float::sin(rad_offset + overlap * _c(0.5)),
828        ));
829    }
830    vec![all_points, core_points]
831}
832
833#[allow(clippy::too_many_arguments)]
834fn _arc<F: Float + Trig + FromPrimitive>(
835    increment: F,
836    cx: F,
837    cy: F,
838    rx: F,
839    ry: F,
840    strt: F,
841    stp: F,
842    offset: F,
843    o: &mut Options,
844) -> Vec<Op<F>> {
845    let rad_offset = strt + _offset_opt(_c(0.1), o, None);
846    let mut points: Vec<Point2D<F>> = vec![Point2D::new(
847        _offset_opt(offset, o, None) + cx + _c::<F>(0.9) * rx * Float::cos(rad_offset - increment),
848        _offset_opt(offset, o, None) + cy + _c::<F>(0.9) * ry * Float::sin(rad_offset - increment),
849    )];
850    let mut angle = rad_offset;
851    while angle <= stp {
852        points.push(Point2D::new(
853            _offset_opt(offset, o, None) + cx + rx * Float::cos(angle),
854            _offset_opt(offset, o, None) + cy + ry * Float::sin(angle),
855        ));
856        angle = angle + increment;
857    }
858    points.push(Point2D::new(
859        cx + rx * Float::cos(stp),
860        cy + ry * Float::sin(stp),
861    ));
862    points.push(Point2D::new(
863        cx + rx * Float::cos(stp),
864        cy + ry * Float::sin(stp),
865    ));
866    _curve(&points, None, o)
867}
868
869fn _bezier_quadratic_to<F: Float + Trig + FromPrimitive>(
870    x1: F,
871    y1: F,
872    x: F,
873    y: F,
874    current: &Point2D<F>,
875    o: &mut Options,
876) -> Vec<Op<F>> {
877    // We simply convert the quadratic to a cubic bezier
878
879    let cubic = convert_bezier_quadratic_to_cubic(BezierQuadratic {
880        start: *current,
881        cp: Point2D::new(x1, y1),
882        end: Point2D::new(x, y),
883    });
884
885    _bezier_to(
886        cubic.cp1.x,
887        cubic.cp1.y,
888        cubic.cp2.x,
889        cubic.cp2.y,
890        cubic.end.x,
891        cubic.end.y,
892        &cubic.start,
893        o,
894    )
895}
896
897#[allow(clippy::too_many_arguments)]
898fn _bezier_to<F: Float + Trig + FromPrimitive>(
899    x1: F,
900    y1: F,
901    x2: F,
902    y2: F,
903    x: F,
904    y: F,
905    current: &Point2D<F>,
906    o: &mut Options,
907) -> Vec<Op<F>> {
908    let mut ops: Vec<Op<F>> = Vec::new();
909    let ros = [
910        _c(o.max_randomness_offset.unwrap_or(2.0)),
911        _c(o.max_randomness_offset.unwrap_or(2.0) + 0.3),
912    ];
913    let mut f: Point2D<F>;
914    let iterations = if o.disable_multi_stroke.unwrap_or(false) {
915        1
916    } else {
917        2
918    };
919    let preserve_vertices = o.preserve_vertices.unwrap_or(false);
920    let mut i = 0;
921    while i < iterations {
922        if i == 0 {
923            ops.push(Op {
924                op: OpType::Move,
925                data: vec![current.x, current.y],
926            });
927        } else {
928            ops.push(Op {
929                op: OpType::Move,
930                data: vec![
931                    current.x
932                        + (if preserve_vertices {
933                            _c(0.0)
934                        } else {
935                            _offset_opt(ros[0], o, None)
936                        }),
937                    current.y
938                        + (if preserve_vertices {
939                            _c(0.0)
940                        } else {
941                            _offset_opt(ros[0], o, None)
942                        }),
943                ],
944            });
945        }
946        f = if preserve_vertices {
947            Point2D::new(x, y)
948        } else {
949            Point2D::new(
950                x + _offset_opt(ros[i], o, None),
951                y + _offset_opt(ros[i], o, None),
952            )
953        };
954        ops.push(Op {
955            op: OpType::BCurveTo,
956            data: vec![
957                x1 + _offset_opt(ros[i], o, None),
958                y1 + _offset_opt(ros[i], o, None),
959                x2 + _offset_opt(ros[i], o, None),
960                y2 + _offset_opt(ros[i], o, None),
961                f.x,
962                f.y,
963            ],
964        });
965        i += 1;
966    }
967    ops
968}
969
970pub fn pattern_fill_polygons<F, P>(polygon_list: P, o: &mut Options) -> OpSet<F>
971where
972    F: Float + Trig + FromPrimitive,
973    P: BorrowMut<Vec<Vec<Point2D<F>>>>,
974{
975    let filler = if let Some(fill_style) = o.fill_style.as_ref() {
976        match fill_style {
977            FillStyle::Hachure => get_filler(ScanLineHachure),
978            FillStyle::Dashed => get_filler(DashedFiller),
979            FillStyle::Dots => get_filler(DotFiller),
980            FillStyle::CrossHatch => get_filler(HatchFiller),
981            FillStyle::ZigZag => get_filler(ZigZagFiller),
982            FillStyle::ZigZagLine => get_filler(ZigZagLineFiller),
983            _ => get_filler(ScanLineHachure),
984        }
985    } else {
986        get_filler(ScanLineHachure)
987    };
988    filler.fill_polygons(polygon_list, o)
989}
990
991pub fn pattern_fill_arc<F>(
992    x: F,
993    y: F,
994    width: F,
995    height: F,
996    start: F,
997    stop: F,
998    o: &mut Options,
999) -> OpSet<F>
1000where
1001    F: Float + FromPrimitive + Trig,
1002{
1003    let cx = x;
1004    let cy = y;
1005    let mut rx = F::abs(width / _c(2.0));
1006    let mut ry = F::abs(height / _c(2.0));
1007
1008    rx = rx + _offset_opt(rx * _c(0.01), o, None);
1009    ry = ry + _offset_opt(ry * _c(0.01), o, None);
1010
1011    let mut strt = start;
1012    let mut stp = stop;
1013    let two_pi = _c::<F>(f32::PI()) * _c::<F>(2.0);
1014
1015    while strt < _c(0.0) {
1016        strt = strt + two_pi;
1017        stp = stp + two_pi;
1018    }
1019
1020    if (stp - strt) > two_pi {
1021        strt = F::zero();
1022        stp = two_pi;
1023    }
1024
1025    let increment = (stp / strt) / o.curve_step_count.map(|a| _c(a)).unwrap_or_else(|| _c(1.0));
1026    let mut points: Vec<Point2D<F>> = vec![];
1027
1028    let mut angle = strt;
1029
1030    while angle <= stp {
1031        points.push(point2(
1032            cx + rx * Float::cos(angle),
1033            cy + ry * Float::sin(angle),
1034        ));
1035        angle = angle + increment;
1036    }
1037
1038    points.push(point2(cx + rx * Float::cos(stp), cy + ry * Float::sin(stp)));
1039    points.push(point2(cx, cy));
1040    pattern_fill_polygons(vec![points], o)
1041}
1042
1043pub fn svg_path<F>(path: String, o: &mut Options) -> OpSet<F>
1044where
1045    F: Float + FromPrimitive + Trig,
1046{
1047    let ops = vec![];
1048    let first = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1049    let current = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1050    let path_parser = PathParser::from(path.as_ref());
1051    let path_segments: Vec<PathSegment> = path_parser.flatten().collect();
1052    let normalized_segments = normalize(absolutize(path_segments.iter()));
1053
1054    opset_from_path(o, ops, first, current, normalized_segments)
1055}
1056
1057pub fn svg_segments<F>(path_segments: Vec<PathSegment>, o: &mut Options) -> OpSet<F>
1058where
1059    F: Float + FromPrimitive + Trig,
1060{
1061    let ops = vec![];
1062    let first = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1063    let current = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1064    let normalized_segments = normalize(absolutize(path_segments.iter()));
1065
1066    opset_from_path(o, ops, first, current, normalized_segments)
1067}
1068
1069pub fn svg_normalized_segments<F>(normalized_segments: &[PathSegment], o: &mut Options) -> OpSet<F>
1070where
1071    F: Float + FromPrimitive + Trig,
1072{
1073    let ops = vec![];
1074    let first = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1075    let current = Point2D::new(_c::<F>(0.0), _c::<F>(0.0));
1076
1077    opset_from_path(o, ops, first, current, normalized_segments.iter().cloned())
1078}
1079
1080fn opset_from_path<F>(
1081    o: &mut Options,
1082    mut ops: Vec<Op<F>>,
1083    mut first: euclid::Point2D<F, euclid::UnknownUnit>,
1084    mut current: euclid::Point2D<F, euclid::UnknownUnit>,
1085    normalized_segments: impl Iterator<Item = PathSegment>,
1086) -> OpSet<F>
1087where
1088    F: Float + FromPrimitive + Trig,
1089{
1090    for segment in normalized_segments {
1091        match segment {
1092            PathSegment::MoveTo { abs: true, x, y } => {
1093                // Rough.js `svgPath(...)` treats `M` as cursor state and does not emit an output op.
1094                // This also avoids consuming extra randomness that would shift subsequent
1095                // `divergePoint` values (critical even when `roughness = 0`).
1096                current = Point2D::new(_cc::<F>(x), _cc::<F>(y));
1097                first = Point2D::new(_cc::<F>(x), _cc::<F>(y));
1098            }
1099            PathSegment::LineTo { abs: true, x, y } => {
1100                ops.extend(_double_line(
1101                    current.x,
1102                    current.y,
1103                    _cc::<F>(x),
1104                    _cc::<F>(y),
1105                    o,
1106                    false,
1107                ));
1108                current = Point2D::new(_cc::<F>(x), _cc::<F>(y));
1109            }
1110            PathSegment::CurveTo {
1111                abs: true,
1112                x1,
1113                y1,
1114                x2,
1115                y2,
1116                x,
1117                y,
1118            } => {
1119                ops.extend(_bezier_to(
1120                    _cc::<F>(x1),
1121                    _cc::<F>(y1),
1122                    _cc::<F>(x2),
1123                    _cc::<F>(y2),
1124                    _cc::<F>(x),
1125                    _cc::<F>(y),
1126                    &current,
1127                    o,
1128                ));
1129                current = Point2D::new(_cc::<F>(x), _cc::<F>(y));
1130            }
1131            PathSegment::ClosePath { abs: true } => {
1132                ops.extend(_double_line(
1133                    current.x, current.y, first.x, first.y, o, false,
1134                ));
1135                current = Point2D::new(first.x, first.y);
1136            }
1137            _ => panic!("Unexpected segment type"),
1138        }
1139    }
1140    OpSet {
1141        op_set_type: OpSetType::Path,
1142        ops,
1143        size: None,
1144        path: None,
1145    }
1146}
1147
1148#[cfg(test)]
1149mod test {
1150    use euclid::point2;
1151    use plotlib::page::Page;
1152    use plotlib::repr::Plot;
1153    use plotlib::style::{PointMarker, PointStyle};
1154    use plotlib::view::ContinuousView;
1155
1156    use super::{EllipseParams, _compute_ellipse_points, _curve};
1157    use crate::core::{Op, OpSet, OpSetType, OpType, Options, OptionsBuilder};
1158
1159    fn get_default_options() -> Options {
1160        OptionsBuilder::default()
1161            .seed(345_u64)
1162            .build()
1163            .expect("failed to build default options")
1164    }
1165
1166    #[test]
1167    fn linear_path() {
1168        let result = super::linear_path(
1169            &[point2(0.0f32, 0.0), point2(0.0, 0.1), point2(1.0, 1.0)],
1170            false,
1171            &mut get_default_options(),
1172        );
1173        assert_eq!(result.op_set_type, OpSetType::Path);
1174        assert_eq!(
1175            result,
1176            OpSet {
1177                op_set_type: OpSetType::Path,
1178                ops: vec![
1179                    Op {
1180                        op: OpType::Move,
1181                        data: vec![-0.0012205532, 0.0027011754]
1182                    },
1183                    Op {
1184                        op: OpType::BCurveTo,
1185                        data: vec![
1186                            0.007769434,
1187                            0.026625521,
1188                            -0.006868569,
1189                            0.035775613,
1190                            -0.008950782,
1191                            0.09677874
1192                        ]
1193                    },
1194                    Op {
1195                        op: OpType::Move,
1196                        data: vec![0.0010519032, -0.0035673995]
1197                    },
1198                    Op {
1199                        op: OpType::BCurveTo,
1200                        data: vec![
1201                            -0.0034613428,
1202                            0.040271826,
1203                            -0.0024902155,
1204                            0.074226834,
1205                            -0.0011456441,
1206                            0.09860738
1207                        ]
1208                    },
1209                    Op {
1210                        op: OpType::Move,
1211                        data: vec![-0.123573944, 0.12996572]
1212                    },
1213                    Op {
1214                        op: OpType::BCurveTo,
1215                        data: vec![
1216                            0.19195092, 0.3300887, 0.6037904, 0.64465916, 0.98244137, 1.0048801
1217                        ]
1218                    },
1219                    Op {
1220                        op: OpType::Move,
1221                        data: vec![0.047007628, 0.116847344]
1222                    },
1223                    Op {
1224                        op: OpType::BCurveTo,
1225                        data: vec![
1226                            0.37384683, 0.40432873, 0.84561193, 0.77220255, 1.0250582, 0.96798605
1227                        ]
1228                    }
1229                ],
1230                size: None,
1231                path: None
1232            }
1233        );
1234    }
1235
1236    #[test]
1237    #[ignore = "failing due to randomness"]
1238    fn ellipse_with_params() {
1239        let expected_estimated_points = vec![
1240            point2(0.6818724507954145, -0.24215675845215262),
1241            point2(1.3682071413206485, 0.7930465114686116),
1242            point2(1.9097816708676238, 0.7671100939304721),
1243            point2(0.8360414855920169, 1.5122198080140175),
1244            point2(0.531355187897985, 0.4738367335276372),
1245            point2(1.111026909625053, 1.3449538537307408),
1246            point2(1.1040092949849214, 1.801902725957649),
1247            point2(0.4258957275631308, 1.2442749714336163),
1248            point2(0.5661545950654607, 0.6328000056262721),
1249        ];
1250
1251        let result = super::ellipse_with_params(
1252            0.1,
1253            0.1,
1254            &mut get_default_options(),
1255            &EllipseParams {
1256                rx: 0.486848765998615,
1257                ry: 0.4755334706420514,
1258                increment: 0.6981317007977318,
1259            },
1260        );
1261
1262        assert_eq!(expected_estimated_points, result.estimated_points);
1263    }
1264
1265    #[test]
1266    #[ignore = "failing due to randommness"]
1267    fn compute_ellipse_points() {
1268        let expected = vec![
1269            vec![
1270                point2(1.0710641633603797, 0.6343339196221436),
1271                point2(0.9888360341310736, 0.539884571860436),
1272                point2(1.0423582717058324, 0.48447611636004245),
1273                point2(1.1323647757131408, 0.48734422393942145),
1274                point2(1.097114022520837, 0.5024772415343248),
1275                point2(1.1983573886194598, 0.6344444071433158),
1276                point2(1.2951674832143851, 0.641832264291391),
1277                point2(1.3536023670520665, 0.6251662974163592),
1278                point2(1.2548224121208582, 0.6352429012560402),
1279                point2(1.3489034470987185, 0.6012739292011288),
1280                point2(1.4213037554602923, 0.6261652440563298),
1281                point2(1.4743145534688815, 0.7882156278963534),
1282                point2(1.4700412486188879, 0.8875515790754055),
1283                point2(1.4460278644836544, 0.8456185823210882),
1284                point2(1.4868741833172523, 0.9079833740096543),
1285                point2(1.4920518492387598, 0.9095078637143422),
1286                point2(1.5595453417691338, 0.9901532598343071),
1287                point2(1.5936742539308373, 1.0213282325299586),
1288                point2(1.58058656655406, 1.17305000017827),
1289                point2(1.4480254616492774, 1.0928279018210438),
1290                point2(1.4539640114348549, 1.144388265648967),
1291                point2(1.3648317202407696, 1.2212937832283584),
1292                point2(1.4733929772805416, 1.2083669884937012),
1293                point2(1.3608398097214693, 1.3207579529041924),
1294                point2(1.2912648851735424, 1.4205716705529399),
1295                point2(1.2046625302840053, 1.3826569437709715),
1296                point2(1.2570442078920254, 1.3410441079145428),
1297                point2(1.1830529369693072, 1.3820810903226886),
1298                point2(1.167072937591176, 1.4466053111301487),
1299                point2(1.0852661499741054, 1.55951044347548),
1300                point2(1.0494466853794846, 1.5479828315241733),
1301                point2(1.0033271419673007, 1.468194659125039),
1302                point2(0.9484890618160645, 1.4530640355956308),
1303                point2(0.9973592789218273, 1.45324593604413),
1304                point2(0.97187677594751, 1.5815631933148016),
1305                point2(0.8144204755613362, 1.3782837410393232),
1306                point2(0.7950961543969257, 1.444409277208105),
1307                point2(0.8249520184490917, 1.3374139622566115),
1308                point2(0.6758412677442227, 1.334436082917169),
1309                point2(0.64368867956175, 1.3618188433767497),
1310                point2(0.5445160170270017, 1.2507819758003385),
1311                point2(0.5261266184295889, 1.290024044761643),
1312                point2(0.502690056479149, 1.236879918084129),
1313                point2(0.5280669233268998, 1.1091277406960698),
1314                point2(0.4827538350322879, 1.1436496314081661),
1315                point2(0.5883382268183734, 1.175168641400803),
1316                point2(0.44736030622371087, 1.018503357084688),
1317                point2(0.5448981202541112, 0.9143727174667883),
1318                point2(0.4317760080261111, 1.051488996664834),
1319                point2(0.5085207904485967, 0.9331170328373988),
1320                point2(0.6001478439304737, 0.8979301783503268),
1321                point2(0.4373488434812126, 0.723669324069054),
1322                point2(0.48379460068391017, 0.6896668054813503),
1323                point2(0.5802149727260961, 0.6326489019654757),
1324                point2(0.5318481024591232, 0.6672519961193484),
1325                point2(0.6267954168946062, 0.6264453502200538),
1326                point2(0.7244414827901777, 0.6742999823788176),
1327                point2(0.7409838872007461, 0.5515230198623486),
1328                point2(0.7461775341290393, 0.6232380086449496),
1329                point2(0.9055915299113261, 0.5326254191949538),
1330                point2(0.9510466807539406, 0.49366667559390653),
1331                point2(0.8116223593436764, 0.4695463357704083),
1332                point2(0.8528118040757474, 0.4635000250267341),
1333                point2(0.9141212396595003, 0.40460067972212826),
1334                point2(1.003267583900141, 0.5351889587671019),
1335                point2(1.0320189898300267, 0.6060923051759772),
1336                point2(1.0784925820514744, 0.5016457530039365),
1337            ],
1338            vec![
1339                point2(0.9888360341310736, 0.539884571860436),
1340                point2(1.0423582717058324, 0.48447611636004245),
1341                point2(1.1323647757131408, 0.48734422393942145),
1342                point2(1.097114022520837, 0.5024772415343248),
1343                point2(1.1983573886194598, 0.6344444071433158),
1344                point2(1.2951674832143851, 0.641832264291391),
1345                point2(1.3536023670520665, 0.6251662974163592),
1346                point2(1.2548224121208582, 0.6352429012560402),
1347                point2(1.3489034470987185, 0.6012739292011288),
1348                point2(1.4213037554602923, 0.6261652440563298),
1349                point2(1.4743145534688815, 0.7882156278963534),
1350                point2(1.4700412486188879, 0.8875515790754055),
1351                point2(1.4460278644836544, 0.8456185823210882),
1352                point2(1.4868741833172523, 0.9079833740096543),
1353                point2(1.4920518492387598, 0.9095078637143422),
1354                point2(1.5595453417691338, 0.9901532598343071),
1355                point2(1.5936742539308373, 1.0213282325299586),
1356                point2(1.58058656655406, 1.17305000017827),
1357                point2(1.4480254616492774, 1.0928279018210438),
1358                point2(1.4539640114348549, 1.144388265648967),
1359                point2(1.3648317202407696, 1.2212937832283584),
1360                point2(1.4733929772805416, 1.2083669884937012),
1361                point2(1.3608398097214693, 1.3207579529041924),
1362                point2(1.2912648851735424, 1.4205716705529399),
1363                point2(1.2046625302840053, 1.3826569437709715),
1364                point2(1.2570442078920254, 1.3410441079145428),
1365                point2(1.1830529369693072, 1.3820810903226886),
1366                point2(1.167072937591176, 1.4466053111301487),
1367                point2(1.0852661499741054, 1.55951044347548),
1368                point2(1.0494466853794846, 1.5479828315241733),
1369                point2(1.0033271419673007, 1.468194659125039),
1370                point2(0.9484890618160645, 1.4530640355956308),
1371                point2(0.9973592789218273, 1.45324593604413),
1372                point2(0.97187677594751, 1.5815631933148016),
1373                point2(0.8144204755613362, 1.3782837410393232),
1374                point2(0.7950961543969257, 1.444409277208105),
1375                point2(0.8249520184490917, 1.3374139622566115),
1376                point2(0.6758412677442227, 1.334436082917169),
1377                point2(0.64368867956175, 1.3618188433767497),
1378                point2(0.5445160170270017, 1.2507819758003385),
1379                point2(0.5261266184295889, 1.290024044761643),
1380                point2(0.502690056479149, 1.236879918084129),
1381                point2(0.5280669233268998, 1.1091277406960698),
1382                point2(0.4827538350322879, 1.1436496314081661),
1383                point2(0.5883382268183734, 1.175168641400803),
1384                point2(0.44736030622371087, 1.018503357084688),
1385                point2(0.5448981202541112, 0.9143727174667883),
1386                point2(0.4317760080261111, 1.051488996664834),
1387                point2(0.5085207904485967, 0.9331170328373988),
1388                point2(0.6001478439304737, 0.8979301783503268),
1389                point2(0.4373488434812126, 0.723669324069054),
1390                point2(0.48379460068391017, 0.6896668054813503),
1391                point2(0.5802149727260961, 0.6326489019654757),
1392                point2(0.5318481024591232, 0.6672519961193484),
1393                point2(0.6267954168946062, 0.6264453502200538),
1394                point2(0.7244414827901777, 0.6742999823788176),
1395                point2(0.7409838872007461, 0.5515230198623486),
1396                point2(0.7461775341290393, 0.6232380086449496),
1397                point2(0.9055915299113261, 0.5326254191949538),
1398                point2(0.9510466807539406, 0.49366667559390653),
1399                point2(0.8116223593436764, 0.4695463357704083),
1400                point2(0.8528118040757474, 0.4635000250267341),
1401                point2(0.9141212396595003, 0.40460067972212826),
1402            ],
1403        ];
1404        let result = _compute_ellipse_points(
1405            0.1,
1406            1.0,
1407            1.0,
1408            0.5,
1409            0.5,
1410            0.1,
1411            0.1,
1412            &mut get_default_options(),
1413        );
1414        assert_eq!(expected, result);
1415    }
1416
1417    #[test]
1418    fn curve() {
1419        let result = _curve(
1420            &[
1421                point2(0.0, 0.0),
1422                point2(1.0, 1.0),
1423                point2(2.0, 0.0),
1424                point2(-1.0, -1.0),
1425            ],
1426            None,
1427            &mut get_default_options(),
1428        );
1429        assert_eq!(result[0].op, OpType::Move);
1430        assert_eq!(result[0].data, vec![1.0, 1.0]);
1431
1432        assert_eq!(result[1].op, OpType::BCurveTo);
1433        assert_eq!(
1434            result[1].data,
1435            vec![
1436                1.3333333333333333,
1437                1.0,
1438                2.3333333333333335,
1439                0.3333333333333333,
1440                2.0,
1441                0.0
1442            ]
1443        );
1444    }
1445
1446    #[test]
1447    #[ignore = "utility to see results quickly"]
1448    fn plot_points() {
1449        let data = vec![
1450            (1.0559477995009565, 0.6021961777759488),
1451            (0.9925497905143945, 0.4436148523483838),
1452            (1.1783256257253407, 0.5143362336768694),
1453            (1.208490397628349, 0.5745944499427847),
1454            (1.2711903714514319, 0.5701901786816395),
1455            (1.1974231651740772, 0.5696505646227608),
1456            (1.266815053466919, 0.5450282815494873),
1457            (1.4283771417586615, 0.6382465720026044),
1458            (1.4154905334465357, 0.7109067381405771),
1459            (1.4333920313802389, 0.8059906260263232),
1460            (1.5094667274959321, 0.7265860541520335),
1461            (1.400692088449572, 0.7835751135014755),
1462            (1.3881602391283323, 0.7755163633824922),
1463            (1.570385206729917, 0.8510533444105508),
1464            (1.5493770357747365, 1.0250335113190738),
1465            (1.510651107806883, 1.0837232261571872),
1466            (1.4775536326276126, 1.016950646519272),
1467            (1.5472535904647446, 1.1025497737242922),
1468            (1.399983805334271, 1.1307557954537981),
1469            (1.3612945701680008, 1.2623693228823314),
1470            (1.3404043926945617, 1.1635099938248215),
1471            (1.361444072889848, 1.3669009350459007),
1472            (1.3856729774849246, 1.3334358041468137),
1473            (1.4238836270255022, 1.3470401143733706),
1474            (1.3117443672910145, 1.3007103720810664),
1475            (1.2951811386649095, 1.413842218695549),
1476            (1.1332155971266886, 1.3564586452873857),
1477            (1.2083097488252306, 1.5340221616808116),
1478            (1.0881580052193756, 1.4263268611969555),
1479            (1.035233163501938, 1.580914582858814),
1480            (1.0786021335616458, 1.4201023026826818),
1481            (1.0116161297926778, 1.4140491306394047),
1482            (0.8765318057053879, 1.4359492914939993),
1483            (0.9399561543054671, 1.5660782762309609),
1484            (0.8375472416599303, 1.525744002191411),
1485            (0.8138957025941598, 1.499526147458222),
1486            (0.6692225625276738, 1.4230050653539723),
1487            (0.6445821561240486, 1.3465046022062919),
1488            (0.7468382746164379, 1.3061904618040936),
1489            (0.5422183692127689, 1.4253173885030197),
1490            (0.6535141358551948, 1.3706502636385975),
1491            (0.5394132023778615, 1.3237938582067676),
1492            (0.5609544663499307, 1.1661260280518218),
1493            (0.5071032508159938, 1.1407886339852356),
1494            (0.5720800099397795, 1.0384692384541154),
1495            (0.5507046722809901, 0.9777594942139937),
1496            (0.5080449523990171, 0.9942577887966262),
1497            (0.5885628279692711, 0.9426486291554865),
1498            (0.4977542840222783, 0.9482898228608775),
1499            (0.5144216046077197, 0.902002627557736),
1500            (0.6326671537040239, 0.8415207219207479),
1501            (0.5737651049885282, 0.7955447719947131),
1502            (0.5017586112800467, 0.8016467388837818),
1503            (0.6016973900071679, 0.6327656807099842),
1504            (0.6618602604154518, 0.5506023666758844),
1505            (0.6324945491128473, 0.5460241979809777),
1506            (0.8125244142495132, 0.6530224612358858),
1507            (0.7983569626413481, 0.6411210503669331),
1508            (0.7582913526129964, 0.6190096172157633),
1509            (0.7799420253058733, 0.5328746976861746),
1510            (0.9418801906688571, 0.4601256410807209),
1511            (1.0420025580486114, 0.5992707449732568),
1512            (0.9427185990787657, 0.5878683460934829),
1513            (1.0816303653623174, 0.5537733879903082),
1514            (1.159556236737222, 0.501976527225239),
1515            (1.0528934849778917, 0.6258578810541852),
1516            (1.1241549892963243, 0.6265235243673886),
1517        ];
1518        let data2 = vec![
1519            (0.9925497905143945, 0.4436148523483838),
1520            (1.1783256257253407, 0.5143362336768694),
1521            (1.208490397628349, 0.5745944499427847),
1522            (1.2711903714514319, 0.5701901786816395),
1523            (1.1974231651740772, 0.5696505646227608),
1524            (1.266815053466919, 0.5450282815494873),
1525            (1.4283771417586615, 0.6382465720026044),
1526            (1.4154905334465357, 0.7109067381405771),
1527            (1.4333920313802389, 0.8059906260263232),
1528            (1.5094667274959321, 0.7265860541520335),
1529            (1.400692088449572, 0.7835751135014755),
1530            (1.3881602391283323, 0.7755163633824922),
1531            (1.570385206729917, 0.8510533444105508),
1532            (1.5493770357747365, 1.0250335113190738),
1533            (1.510651107806883, 1.0837232261571872),
1534            (1.4775536326276126, 1.016950646519272),
1535            (1.5472535904647446, 1.1025497737242922),
1536            (1.399983805334271, 1.1307557954537981),
1537            (1.3612945701680008, 1.2623693228823314),
1538            (1.3404043926945617, 1.1635099938248215),
1539            (1.361444072889848, 1.3669009350459007),
1540            (1.3856729774849246, 1.3334358041468137),
1541            (1.4238836270255022, 1.3470401143733706),
1542            (1.3117443672910145, 1.3007103720810664),
1543            (1.2951811386649095, 1.413842218695549),
1544            (1.1332155971266886, 1.3564586452873857),
1545            (1.2083097488252306, 1.5340221616808116),
1546            (1.0881580052193756, 1.4263268611969555),
1547            (1.035233163501938, 1.580914582858814),
1548            (1.0786021335616458, 1.4201023026826818),
1549            (1.0116161297926778, 1.4140491306394047),
1550            (0.8765318057053879, 1.4359492914939993),
1551            (0.9399561543054671, 1.5660782762309609),
1552            (0.8375472416599303, 1.525744002191411),
1553            (0.8138957025941598, 1.499526147458222),
1554            (0.6692225625276738, 1.4230050653539723),
1555            (0.6445821561240486, 1.3465046022062919),
1556            (0.7468382746164379, 1.3061904618040936),
1557            (0.5422183692127689, 1.4253173885030197),
1558            (0.6535141358551948, 1.3706502636385975),
1559            (0.5394132023778615, 1.3237938582067676),
1560            (0.5609544663499307, 1.1661260280518218),
1561            (0.5071032508159938, 1.1407886339852356),
1562            (0.5720800099397795, 1.0384692384541154),
1563            (0.5507046722809901, 0.9777594942139937),
1564            (0.5080449523990171, 0.9942577887966262),
1565            (0.5885628279692711, 0.9426486291554865),
1566            (0.4977542840222783, 0.9482898228608775),
1567            (0.5144216046077197, 0.902002627557736),
1568            (0.6326671537040239, 0.8415207219207479),
1569            (0.5737651049885282, 0.7955447719947131),
1570            (0.5017586112800467, 0.8016467388837818),
1571            (0.6016973900071679, 0.6327656807099842),
1572            (0.6618602604154518, 0.5506023666758844),
1573            (0.6324945491128473, 0.5460241979809777),
1574            (0.8125244142495132, 0.6530224612358858),
1575            (0.7983569626413481, 0.6411210503669331),
1576            (0.7582913526129964, 0.6190096172157633),
1577            (0.7799420253058733, 0.5328746976861746),
1578            (0.9418801906688571, 0.4601256410807209),
1579            (1.0420025580486114, 0.5992707449732568),
1580            (0.9427185990787657, 0.5878683460934829),
1581            (1.0816303653623174, 0.5537733879903082),
1582        ];
1583
1584        // We create our scatter plot from the data
1585        let s1: Plot = Plot::new(data).point_style(
1586            PointStyle::new()
1587                .marker(PointMarker::Square) // setting the marker to be a square
1588                .colour("#DD3355")
1589                .size(1.0),
1590        ); // and a custom colour
1591
1592        // We can plot multiple data sets in the same view
1593        let s2: Plot = Plot::new(data2).point_style(
1594            PointStyle::new() // uses the default marker
1595                .colour("#35C788")
1596                .size(1.0),
1597        ); // and a different colour
1598
1599        // The 'view' describes what set of data is drawn
1600        let v = ContinuousView::new()
1601            .add(s1)
1602            .add(s2)
1603            .x_range(-5., 10.)
1604            .y_range(-2., 6.)
1605            .x_label("Some varying variable")
1606            .y_label("The response of something");
1607
1608        // A page with a single view is then saved to an SVG file
1609        Page::single(&v).save("scatter.svg").unwrap();
1610    }
1611}