animate/canvas/rough/
renderer.rs

1use super::{get_filler, Op, OpSet, OpSetType, Random, RoughOptions};
2use crate::{OpType, Point};
3use std::f64::consts::PI;
4// import { RenderHelper } from './fillers/filler-interface.js';
5// import { parsePath, normalize, absolutize } from 'path-data-parser';
6
7pub struct EllipseResult {
8    pub opset: OpSet,
9    pub estimated_points: Vec<Point<f64>>,
10}
11
12pub struct EllipseParams {
13    pub rx: f64,
14    pub ry: f64,
15    pub increment: f64,
16}
17
18pub struct Renderer;
19// let helper: RenderHelper = {
20//   randOffset,
21//   randOffsetWithRange,
22//   ellipse,
23//   doubleLineOps: doubleLineFillOps
24// };
25
26impl Renderer {
27    pub fn line(x1: f64, y1: f64, x2: f64, y2: f64, options: &RoughOptions) -> OpSet {
28        OpSet {
29            kind: OpSetType::Path,
30            ops: Renderer::_double_line(x1, y1, x2, y2, options, false),
31            size: None,
32            path: None,
33        }
34    }
35
36    pub fn linear_path(points: &Vec<Point<f64>>, close: bool, options: &RoughOptions) -> OpSet {
37        let len = points.len();
38        if len > 2 {
39            let mut ops: Vec<Op> = Vec::new();
40            for idx in 0..len - 1 {
41                let first = points.get(idx).unwrap();
42                let next = points.get(idx + 1).unwrap();
43                let mut op2 =
44                    Renderer::_double_line(first.x, first.y, next.x, next.y, options, false);
45                ops.append(op2.as_mut());
46            }
47
48            if close {
49                let first = points.first().unwrap();
50                let last = points.last().unwrap();
51                let mut op2 =
52                    Renderer::_double_line(last.x, last.y, first.x, first.y, options, false);
53                ops.append(op2.as_mut());
54            }
55
56            return OpSet {
57                kind: OpSetType::Path,
58                ops,
59                size: None,
60                path: None,
61            };
62        } else if len == 2 {
63            let first = points.first().unwrap();
64            let last = points.last().unwrap();
65            return Renderer::line(first.x, first.y, last.x, last.y, options);
66        }
67
68        OpSet {
69            kind: OpSetType::Path,
70            ops: Vec::new(),
71            size: None,
72            path: None,
73        }
74    }
75
76    pub fn polygon(points: &Vec<Point<f64>>, options: &RoughOptions) -> OpSet {
77        Renderer::linear_path(points, true, options)
78    }
79
80    pub fn rectangle(x: f64, y: f64, width: f64, height: f64, options: &RoughOptions) -> OpSet {
81        let points: Vec<Point<f64>> = vec![
82            Point::new(x, y),
83            Point::new(x + width, y),
84            Point::new(x + width, y + height),
85            Point::new(x, y + height),
86        ];
87
88        Renderer::polygon(&points, options)
89    }
90
91    pub fn curve(points: &Vec<Point<f64>>, options: &RoughOptions) -> OpSet {
92        let mut o1 =
93            Renderer::_curve_with_offset(points, 1.0 * (1.0 + options.roughness * 0.2), options);
94        if !options.disable_multi_stroke {
95            let mut o2 = Renderer::_curve_with_offset(
96                points,
97                1.5 * (1.0 + options.roughness * 0.22),
98                options, //Renderer::clone_options_alter_seed(options),
99            );
100            o1.append(o2.as_mut());
101        }
102
103        OpSet {
104            kind: OpSetType::Path,
105            ops: o1,
106            size: None,
107            path: None,
108        }
109    }
110
111    pub fn ellipse(x: f64, y: f64, width: f64, height: f64, options: &RoughOptions) -> OpSet {
112        let params = Renderer::generate_ellipse_params(width, height, options);
113        Renderer::ellipse_with_params(x, y, options, &params).opset
114    }
115
116    pub fn generate_ellipse_params(width: f64, height: f64, options: &RoughOptions) -> EllipseParams {
117        let psq =
118            (PI * 2.0 * (((width / 2.0).powi(2) + (height / 2.0).powi(2)) / 2.0).sqrt()).sqrt();
119        let step_count = options.curve_step_count.max(options.curve_step_count / 200_f64.sqrt()) * psq;
120        let increment = (PI * 2.0) / step_count;
121        let mut rx = (width / 2.0).abs();
122        let mut ry = (height / 2.0).abs();
123        let curve_fit_randomness = 1.0 - options.curve_fitting;
124        rx += Renderer::_offset_opt(rx * curve_fit_randomness, options, 1.0);
125        ry += Renderer::_offset_opt(ry * curve_fit_randomness, options, 1.0);
126        EllipseParams { increment, rx, ry }
127    }
128
129    pub fn ellipse_with_params(
130        x: f64,
131        y: f64,
132        o: &RoughOptions,
133        ellipse_params: &EllipseParams,
134    ) -> EllipseResult {
135        let (ap1, cp1) = Renderer::_compute_ellipse_points(
136            ellipse_params.increment,
137            x,
138            y,
139            ellipse_params.rx,
140            ellipse_params.ry,
141            1.0,
142            ellipse_params.increment
143                * Renderer::_offset(0.1, Renderer::_offset(0.4, 1.0, o, 1.0), o, 1.0),
144            o,
145        );
146
147        let mut o1 = Renderer::_curve(ap1, None, o);
148        if !o.disable_multi_stroke {
149            let (ap2, _) = Renderer::_compute_ellipse_points(
150                ellipse_params.increment,
151                x,
152                y,
153                ellipse_params.rx,
154                ellipse_params.ry,
155                1.5,
156                0.0,
157                o,
158            );
159            let mut o2 = Renderer::_curve(ap2, None, o);
160            o1.append(o2.as_mut());
161        }
162
163        EllipseResult {
164            estimated_points: cp1,
165            opset: OpSet {
166                kind: OpSetType::Path,
167                ops: o1,
168                size: None,
169                path: None,
170            },
171        }
172    }
173
174    pub fn arc(
175        x: f64,
176        y: f64,
177        width: f64,
178        height: f64,
179        start: f64,
180        stop: f64,
181        closed: bool,
182        rough_closure: bool,
183        options: &RoughOptions,
184    ) -> OpSet {
185        let cx = x;
186        let cy = y;
187        let rx =
188            (width / 2.0).abs() + Renderer::_offset_opt((width / 2.0).abs() * 0.01, options, 1.0);
189        let ry =
190            (height / 2.0).abs() + Renderer::_offset_opt((height / 2.0).abs() * 0.01, options, 1.0);
191        let mut strt = start;
192        let mut stp = stop;
193        while strt < 0.0 {
194            strt += PI * 2.0;
195            stp += PI * 2.0;
196        }
197
198        if (stp - strt) > PI * 2.0 {
199            strt = 0.0;
200            stp = PI * 2.0;
201        }
202
203        let ellipse_inc = PI * 2.0 / options.curve_step_count;
204        let arc_inc = (ellipse_inc / 2.0).min((stp - strt) / 2.0);
205        let mut ops = Renderer::_arc(arc_inc, cx, cy, rx, ry, strt, stp, 1.0, options);
206
207        if !options.disable_multi_stroke {
208            // println!("RENDERER ARC DISABLE MULTI STROKE");
209            let mut o2 = Renderer::_arc(arc_inc, cx, cy, rx, ry, strt, stp, 1.5, options);
210            ops.append(o2.as_mut());
211        }
212
213        if closed {
214            // println!("RENDERER ARC CLOSED");
215            if rough_closure {
216                let mut op2 = Renderer::_double_line(
217                    cx,
218                    cy,
219                    cx + rx * strt.cos(),
220                    cy + ry * strt.sin(),
221                    options,
222                    false,
223                );
224                ops.append(op2.as_mut());
225
226                let mut op2 = Renderer::_double_line(
227                    cx,
228                    cy,
229                    cx + rx * stp.cos(),
230                    cy + ry * stp.sin(),
231                    options,
232                    false,
233                );
234                ops.append(op2.as_mut());
235            } else {
236                ops.push(Op {
237                    op: OpType::LineTo,
238                    data: vec![cx, cy],
239                });
240                ops.push(Op {
241                    op: OpType::LineTo,
242                    data: vec![cx + rx * strt.cos(), cy + ry * strt.sin()],
243                });
244            }
245        }
246
247        OpSet {
248            kind: OpSetType::Path,
249            ops,
250            size: None,
251            path: None,
252        }
253    }
254
255    // pub fn svg_path(path: String, o: &RoughOptions) -> OpSet {
256    //     let segments = normalize(absolutize(parsePath(path)));
257    //     let ops: Op[] = [];
258    //     let first: Point = [0, 0];
259    //     let current: Point = [0, 0];
260    //     for (let { key, data } of segments) {
261    //       switch (key) {
262    //         case 'M': {
263    //           let ro = 1 * (o.maxRandomnessOffset || 0);
264    //           ops.push({ op: 'move', data: data.map((d) => d + _offset_opt(ro, o)) });
265    //           current = [data[0], data[1]];
266    //           first = [data[0], data[1]];
267    //           break;
268    //         }
269    //         case 'L':
270    //           ops.push(..._doubleLine(current[0], current[1], data[0], data[1], o));
271    //           current = [data[0], data[1]];
272    //           break;
273    //         case 'C': {
274    //           let [x1, y1, x2, y2, x, y] = data;
275    //           ops.push(..._bezierTo(x1, y1, x2, y2, x, y, current, o));
276    //           current = [x, y];
277    //           break;
278    //         }
279    //         case 'Z':
280    //           ops.push(..._doubleLine(current[0], current[1], first[0], first[1], o));
281    //           current = [first[0], first[1]];
282    //           break;
283    //       }
284    //     }
285    //     OpSet {
286    //         kind: OpSetType::Path,
287    //         ops,
288    //         size: None,
289    //         path: None,
290    //     }
291    // }
292
293    // Fills
294    pub fn solid_fill_polygon(points: &Vec<Point<f64>>, options: &RoughOptions) -> OpSet {
295        let mut ops: Vec<Op> = Vec::new();
296        let len = points.len();
297        if len > 0 {
298            let offset = options.max_randomness_offset;
299            if len > 2 {
300                let first = points.first().unwrap();
301                ops.push(Op {
302                    op: OpType::Move,
303                    data: vec![
304                        first.x + Renderer::_offset_opt(offset, options, 1.0),
305                        first.y + Renderer::_offset_opt(offset, options, 1.0),
306                    ],
307                });
308
309                for i in 1..len {
310                    let pt = points.get(i).unwrap();
311                    ops.push(Op {
312                        op: OpType::LineTo,
313                        data: vec![
314                            pt.x + Renderer::_offset_opt(offset, options, 1.0),
315                            pt.y + Renderer::_offset_opt(offset, options, 1.0),
316                        ],
317                    });
318                }
319            }
320        }
321        OpSet {
322            kind: OpSetType::FillPath,
323            ops,
324            size: None,
325            path: None,
326        }
327    }
328
329    pub fn pattern_fill_polygon(points: &Vec<Point<f64>>, options: &RoughOptions) -> OpSet {
330        // get_filler(o, helper).fill_polygon(points, o)
331        unimplemented!()
332    }
333
334    pub fn pattern_fill_arc(
335        x: f64,
336        y: f64,
337        width: f64,
338        height: f64,
339        start: f64,
340        stop: f64,
341        o: &RoughOptions,
342    ) -> OpSet {
343        let cx = x;
344        let cy = y;
345        let mut rx = (width / 2.0).abs();
346        let mut ry = (height / 2.0).abs();
347        rx += Renderer::_offset_opt(rx * 0.01, o, 1.0);
348        ry += Renderer::_offset_opt(ry * 0.01, o, 1.0);
349        let mut strt = start;
350        let mut stp = stop;
351        while strt < 0.0 {
352            strt += PI * 2.0;
353            stp += PI * 2.0;
354        }
355
356        if (stp - strt) > PI * 2.0 {
357            strt = 0.0;
358            stp = PI * 2.0;
359        }
360
361        let increment = (stp - strt) / o.curve_step_count;
362        let mut points: Vec<Point<f64>> = Vec::new();
363
364        let mut angle = strt;
365        while angle <= stp {
366            points.push(Point::new(cx + rx * angle.cos(), cy + ry * angle.sin()));
367            angle = angle + increment
368        }
369
370        points.push(Point::new(cx + rx * stp.cos(), cy + ry * stp.sin()));
371        points.push(Point::new(cx, cy));
372        Renderer::pattern_fill_polygon(&points, o)
373    }
374
375    pub fn rand_offset(x: f64, o: &RoughOptions) -> f64 {
376        Renderer::_offset_opt(x, o, 1.0)
377    }
378
379    pub fn rand_offset_with_range(min: f64, max: f64, o: &RoughOptions) -> f64 {
380        Renderer::_offset(min, max, o, 1.0)
381    }
382
383    pub fn double_line_fill_ops(x1: f64, y1: f64, x2: f64, y2: f64, o: &RoughOptions) -> Vec<Op> {
384        Renderer::_double_line(x1, y1, x2, y2, o, true)
385    }
386
387    fn randomize(ops: &RoughOptions) -> f64 {
388        // if (!ops.randomizer) {
389        //   ops.randomizer = new Random(ops.seed || 0);
390        // }
391        // ops.randomizer.next()
392        rand::random()
393    }
394
395    fn _offset(min: f64, max: f64, ops: &RoughOptions, roughness_gain: f64) -> f64 {
396        // roughness_gain: i32 = 1
397        ops.roughness * roughness_gain * ((Renderer::randomize(ops) * (max - min)) + min)
398    }
399
400    fn _offset_opt(x: f64, ops: &RoughOptions, roughness_gain: f64) -> f64 {
401        // roughness_gain: i32 = 1
402        Renderer::_offset(-x, x, ops, roughness_gain)
403    }
404
405    fn _double_line(
406        x1: f64,
407        y1: f64,
408        x2: f64,
409        y2: f64,
410        o: &RoughOptions,
411        filling: bool,
412    ) -> Vec<Op> {
413        // // filling: bool = false
414        let single_stroke = if filling {
415            o.disable_multi_stroke_fill
416        } else {
417            o.disable_multi_stroke
418        };
419
420        let mut result = Renderer::_line(x1, y1, x2, y2, o, true, false);
421        if !single_stroke {
422            let mut second = Renderer::_line(x1, y1, x2, y2, o, true, true);
423            result.append(second.as_mut());
424        }
425
426        result
427    }
428
429    fn _line(
430        x1: f64,
431        y1: f64,
432        x2: f64,
433        y2: f64,
434        o: &RoughOptions,
435        ismove: bool,
436        overlay: bool,
437    ) -> Vec<Op> {
438        let length_sq = (x1 - x2).powi(2) + (y1 - y2).powi(2);
439        let length = length_sq.sqrt();
440
441        // println!("LINE >>> {}:{} {}:{} length {}", x1, y1, x2, y2, length);
442        let roughness_gain = if length < 200.0 {
443            1.0
444        } else if length > 500.0 {
445            0.4
446        } else {
447            -0.0016668 * length + 1.233334
448        };
449
450        let mut offset = o.max_randomness_offset;
451        if (offset * offset * 100.0) > length_sq {
452            offset = length / 10.0;
453        }
454        let half_offset = offset / 2.0;
455        let diverge_point = 0.2 + Renderer::randomize(o) as f64 * 0.2;
456
457        let mut mid_disp_x = o.bowing * o.max_randomness_offset * (y2 - y1) / 200.0;
458        let mut mid_disp_y = o.bowing * o.max_randomness_offset * (x1 - x2) / 200.0;
459        mid_disp_x = Renderer::_offset_opt(mid_disp_x, o, roughness_gain);
460        mid_disp_y = Renderer::_offset_opt(mid_disp_y, o, roughness_gain);
461        let mut ops: Vec<Op> = Vec::new();
462
463        if ismove {
464            if overlay {
465                ops.push(Op {
466                    op: OpType::Move,
467                    data: vec![
468                        x1 + Renderer::_offset_opt(half_offset, o, roughness_gain),
469                        y1 + Renderer::_offset_opt(half_offset, o, roughness_gain),
470                    ],
471                });
472            } else {
473                ops.push(Op {
474                    op: OpType::Move,
475                    data: vec![
476                        x1 + Renderer::_offset_opt(offset, o, roughness_gain),
477                        y1 + Renderer::_offset_opt(offset, o, roughness_gain),
478                    ],
479                });
480            }
481        }
482
483        if overlay {
484            ops.push(Op {
485                op: OpType::BCurveTo,
486                data: vec![
487                    mid_disp_x
488                        + x1
489                        + (x2 - x1) * diverge_point
490                        + Renderer::_offset_opt(half_offset, o, roughness_gain),
491                    mid_disp_y
492                        + y1
493                        + (y2 - y1) * diverge_point
494                        + Renderer::_offset_opt(half_offset, o, roughness_gain),
495                    mid_disp_x
496                        + x1
497                        + 2.0 * (x2 - x1) * diverge_point
498                        + Renderer::_offset_opt(half_offset, o, roughness_gain),
499                    mid_disp_y
500                        + y1
501                        + 2.0 * (y2 - y1) * diverge_point
502                        + Renderer::_offset_opt(half_offset, o, roughness_gain),
503                    x2 + Renderer::_offset_opt(half_offset, o, roughness_gain),
504                    y2 + Renderer::_offset_opt(half_offset, o, roughness_gain),
505                ],
506            });
507        } else {
508            ops.push(Op {
509                op: OpType::BCurveTo,
510                data: vec![
511                    mid_disp_x
512                        + x1
513                        + (x2 - x1) * diverge_point
514                        + Renderer::_offset_opt(offset, o, roughness_gain),
515                    mid_disp_y
516                        + y1
517                        + (y2 - y1) * diverge_point
518                        + Renderer::_offset_opt(offset, o, roughness_gain),
519                    mid_disp_x
520                        + x1
521                        + 2.0 * (x2 - x1) * diverge_point
522                        + Renderer::_offset_opt(offset, o, roughness_gain),
523                    mid_disp_y
524                        + y1
525                        + 2.0 * (y2 - y1) * diverge_point
526                        + Renderer::_offset_opt(offset, o, roughness_gain),
527                    x2 + Renderer::_offset_opt(offset, o, roughness_gain),
528                    y2 + Renderer::_offset_opt(offset, o, roughness_gain),
529                ],
530            });
531        }
532        ops
533    }
534
535    fn _curve_with_offset(
536        points: &Vec<Point<f64>>,
537        offset: f64,
538        options: &RoughOptions,
539    ) -> Vec<Op> {
540        let mut ps: Vec<Point<f64>> = Vec::new();
541        let mut iter = points.iter();
542
543        let first = iter.next().unwrap();
544        ps.push(Point::new(
545            first.x + Renderer::_offset_opt(offset, options, 1.0),
546            first.y + Renderer::_offset_opt(offset, options, 1.0),
547        ));
548
549        ps.push(Point::new(
550            first.x + Renderer::_offset_opt(offset, options, 1.0),
551            first.y + Renderer::_offset_opt(offset, options, 1.0),
552        ));
553
554        let mut idx: usize = 1;
555        let last_idx = points.len() - 1;
556        // from second item
557        for point in iter {
558            ps.push(Point::new(
559                point.x + Renderer::_offset_opt(offset, options, 1.0),
560                point.y + Renderer::_offset_opt(offset, options, 1.0),
561            ));
562
563            if idx == last_idx {
564                ps.push(Point::new(
565                    point.x + Renderer::_offset_opt(offset, options, 1.0),
566                    point.y + Renderer::_offset_opt(offset, options, 1.0),
567                ));
568            }
569            idx += 1;
570        }
571
572        Renderer::_curve(ps, None, options)
573    }
574
575    fn _curve(
576        points: Vec<Point<f64>>,
577        close_point: Option<Point<f64>>,
578        o: &RoughOptions,
579    ) -> Vec<Op> {
580        let len = points.len();
581        let mut ops: Vec<Op> = Vec::new();
582        if len > 3 {
583            let s = 1.0 - o.curve_tightness;
584            let pt = points.get(1).unwrap();
585            ops.push(Op {
586                op: OpType::Move,
587                data: vec![pt.x, pt.y],
588            });
589
590            let mut i: usize = 1;
591            while (i + 2) < len {
592                let prev = points.get(i - 1).unwrap();
593                let current = points.get(i).unwrap();
594                let next = points.get(i + 1).unwrap();
595                let next_one = points.get(i + 2).unwrap();
596
597                let b0 = Point::new(current.x, current.y);
598                let b1 = Point::new(
599                    current.x + (s * next.x - s * prev.x) / 6.0,
600                    current.y + (s * next.y - s * prev.y) / 6.0,
601                );
602                let b2 = Point::new(
603                    next.x + (s * current.x - s * next_one.x) / 6.0,
604                    next.y + (s * current.y - s * next_one.y) / 6.0,
605                );
606                let b3 = Point::new(next.x, next.y);
607
608                ops.push(Op {
609                    op: OpType::BCurveTo,
610                    data: vec![b1.x, b1.y, b2.x, b2.y, b3.x, b3.y],
611                });
612                i += 1;
613            }
614            if let Some(close_point) = close_point {
615                let ro = o.max_randomness_offset;
616                ops.push(Op {
617                    op: OpType::LineTo,
618                    data: vec![
619                        close_point.x + Renderer::_offset_opt(ro, o, 1.0),
620                        close_point.y + Renderer::_offset_opt(ro, o, 1.0),
621                    ],
622                });
623            }
624        } else if len == 3 {
625            let mid = points.get(1).unwrap();
626            ops.push(Op {
627                op: OpType::Move,
628                data: vec![mid.x, mid.y],
629            });
630            let last = points.last().unwrap();
631            ops.push(Op {
632                op: OpType::BCurveTo,
633                data: vec![mid.x, mid.y, last.x, last.y, last.x, last.y],
634            });
635        } else if len == 2 {
636            let first = points.get(0).unwrap();
637            let second = points.get(1).unwrap();
638            let mut append = Renderer::_double_line(first.x, first.y, second.x, second.y, o, false);
639            ops.append(append.as_mut());
640        }
641        ops
642    }
643
644    fn _compute_ellipse_points(
645        increment: f64,
646        cx: f64,
647        cy: f64,
648        rx: f64,
649        ry: f64,
650        offset: f64,
651        overlap: f64,
652        o: &RoughOptions,
653    ) -> (Vec<Point<f64>>, Vec<Point<f64>>) {
654        let mut core_points: Vec<Point<f64>> = Vec::new();
655        let mut all_points: Vec<Point<f64>> = Vec::new();
656
657        let rad_offset = Renderer::_offset_opt(0.5, o, 1.0) - PI / 2.0;
658
659        all_points.push(Point::new(
660            Renderer::_offset_opt(offset, o, 1.0) + cx + 0.9 * rx * (rad_offset - increment).cos(),
661            Renderer::_offset_opt(offset, o, 1.0) + cy + 0.9 * ry * (rad_offset - increment).sin(),
662        ));
663
664        let mut angle = rad_offset;
665        while angle < PI * 2.0 + rad_offset - 0.01 {
666            let p = Point::new(
667                Renderer::_offset_opt(offset, o, 1.0) + cx + rx * angle.cos(),
668                Renderer::_offset_opt(offset, o, 1.0) + cy + ry * angle.sin(),
669            );
670            core_points.push(p);
671            all_points.push(p);
672
673            angle = angle + increment;
674        }
675
676        all_points.push(Point::new(
677            Renderer::_offset_opt(offset, o, 1.0)
678                + cx
679                + rx * (rad_offset + PI * 2.0 + overlap * 0.5).cos(),
680            Renderer::_offset_opt(offset, o, 1.0)
681                + cy
682                + ry * (rad_offset + PI * 2.0 + overlap * 0.5).sin(),
683        ));
684
685        all_points.push(Point::new(
686            Renderer::_offset_opt(offset, o, 1.0) + cx + 0.98 * rx * (rad_offset + overlap).cos(),
687            Renderer::_offset_opt(offset, o, 1.0) + cy + 0.98 * ry * (rad_offset + overlap).sin(),
688        ));
689
690        all_points.push(Point::new(
691            Renderer::_offset_opt(offset, o, 1.0)
692                + cx
693                + 0.9 * rx * (rad_offset + overlap * 0.5).cos(),
694            Renderer::_offset_opt(offset, o, 1.0)
695                + cy
696                + 0.9 * ry * (rad_offset + overlap * 0.5).sin(),
697        ));
698
699        (all_points, core_points)
700    }
701
702    fn _arc(
703        increment: f64,
704        cx: f64,
705        cy: f64,
706        rx: f64,
707        ry: f64,
708        strt: f64,
709        stp: f64,
710        offset: f64,
711        o: &RoughOptions,
712    ) -> Vec<Op> {
713        // println!("RENDERER _ARC");
714
715        let rad_offset = strt + Renderer::_offset_opt(0.1, o, 1.0);
716        let mut points: Vec<Point<f64>> = Vec::new();
717        points.push(Point::new(
718            Renderer::_offset_opt(offset, o, 1.0) + cx + 0.9 * rx * (rad_offset - increment).cos(),
719            Renderer::_offset_opt(offset, o, 1.0) + cy + 0.9 * ry * (rad_offset - increment).sin(),
720        ));
721
722        let mut angle = rad_offset;
723        while angle <= stp {
724            points.push(Point::new(
725                Renderer::_offset_opt(offset, o, 1.0) + cx + rx * angle.cos(),
726                Renderer::_offset_opt(offset, o, 1.0) + cy + ry * angle.sin(),
727            ));
728            angle = angle + increment;
729        }
730
731        points.push(Point::new(cx + rx * stp.cos(), cy + ry * stp.sin()));
732        points.push(Point::new(cx + rx * stp.cos(), cy + ry * stp.sin()));
733
734        Renderer::_curve(points, None, o)
735    }
736
737    fn _bezier_to(
738        x1: f64,
739        y1: f64,
740        x2: f64,
741        y2: f64,
742        x: f64,
743        y: f64,
744        current: Point<f64>,
745        options: &RoughOptions,
746    ) -> Vec<Op> {
747        let mut ops: Vec<Op> = Vec::new();
748        let ros = vec![
749            if options.max_randomness_offset != 0.0 {
750                options.max_randomness_offset
751            } else {
752                1.0
753            },
754            if options.max_randomness_offset != 0.0 {
755                options.max_randomness_offset + 0.3
756            } else {
757                1.3
758            },
759        ];
760
761        let iterations = if options.disable_multi_stroke { 1 } else { 2 };
762
763        for i in 0..iterations {
764            if i == 0 {
765                ops.push(Op {
766                    op: OpType::Move,
767                    data: vec![current.x, current.y],
768                });
769            } else {
770                ops.push(Op {
771                    op: OpType::Move,
772                    data: vec![
773                        current.x + Renderer::_offset_opt(ros[0], options, 1.0),
774                        current.y + Renderer::_offset_opt(ros[0], options, 1.0),
775                    ],
776                });
777            }
778
779            let fp = Point::new(
780                x + Renderer::_offset_opt(ros[i], options, 1.0),
781                y + Renderer::_offset_opt(ros[i], options, 1.0),
782            );
783
784            ops.push(Op {
785                op: OpType::BCurveTo,
786                data: vec![
787                    x1 + Renderer::_offset_opt(ros[i], options, 1.0),
788                    y1 + Renderer::_offset_opt(ros[i], options, 1.0),
789                    x2 + Renderer::_offset_opt(ros[i], options, 1.0),
790                    y2 + Renderer::_offset_opt(ros[i], options, 1.0),
791                    fp.x,
792                    fp.y,
793                ],
794            });
795        }
796        ops
797    }
798}