1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::{
    options::{Options, StrokeOptions},
    tess, PolyBuilder,
};
use gee::{Point, Rect};

#[derive(Clone, Debug)]
pub enum ControlPoint {
    Quadratic(Point),
    Cubic(Point, Point),
}

#[derive(Clone, Debug)]
pub struct BezierSegment {
    end: Point,
    ctrl: ControlPoint,
}

impl BezierSegment {
    pub fn new(end: Point, ctrl: ControlPoint) -> Self {
        Self { end, ctrl }
    }

    pub fn quadratic(end: Point, ctrl: Point) -> Self {
        Self::new(end, ControlPoint::Quadratic(ctrl))
    }

    pub fn cubic(end: Point, ctrl1: Point, ctrl2: Point) -> Self {
        Self::new(end, ControlPoint::Cubic(ctrl1, ctrl2))
    }
}

#[derive(Clone, Debug, Default)]
pub struct BezierBuilder {
    start: Point,
    segments: Vec<BezierSegment>,
    open: bool,
    options: Options,
}

impl BezierBuilder {
    pub fn new(start: Point) -> Self {
        Self::default().with_start(start)
    }

    pub fn from_bezier_segment(start: Point, segment: BezierSegment) -> Self {
        Self::new(start).with_bezier_segment(segment)
    }

    pub fn from_bezier_segments(
        start: Point,
        segments: impl IntoIterator<Item = BezierSegment>,
    ) -> Self {
        Self::new(start).with_bezier_segments(segments)
    }

    pub fn from_quadratic_segment(start: Point, end: Point, ctrl: Point) -> Self {
        Self::from_bezier_segment(start, BezierSegment::quadratic(end, ctrl))
    }

    pub fn from_cubic_segment(start: Point, end: Point, ctrl1: Point, ctrl2: Point) -> Self {
        Self::from_bezier_segment(start, BezierSegment::cubic(end, ctrl1, ctrl2))
    }

    pub fn with_start(mut self, start: Point) -> Self {
        self.start = start;
        self
    }

    pub fn with_bezier_segment(mut self, segment: BezierSegment) -> Self {
        self.segments.push(segment);
        self
    }

    pub fn with_bezier_segments(
        mut self,
        segments: impl IntoIterator<Item = BezierSegment>,
    ) -> Self {
        self.segments.extend(segments);
        self
    }

    pub fn with_quadratic_segment(self, end: Point, ctrl: Point) -> Self {
        self.with_bezier_segment(BezierSegment::quadratic(end, ctrl))
    }

    pub fn with_cubic_segment(self, end: Point, ctrl1: Point, ctrl2: Point) -> Self {
        self.with_bezier_segment(BezierSegment::cubic(end, ctrl1, ctrl2))
    }

    pub fn with_stroke(mut self, stroke_width: f32, open: bool) -> Self {
        self.open = open;
        self._with_stroke(stroke_width)
    }

    pub fn with_stroke_open(self, stroke_width: f32) -> Self {
        self.with_stroke(stroke_width, true)
    }

    pub fn with_stroke_closed(self, stroke_width: f32) -> Self {
        self.with_stroke(stroke_width, false)
    }

    pub fn with_stroke_opts(mut self, stroke_options: StrokeOptions, open: bool) -> Self {
        self.open = open;
        self._with_stroke_opts(stroke_options)
    }

    pub fn with_stroke_opts_open(self, stroke_options: StrokeOptions) -> Self {
        self.with_stroke_opts(stroke_options, true)
    }

    pub fn with_stroke_opts_closed(self, stroke_options: StrokeOptions) -> Self {
        self.with_stroke_opts(stroke_options, false)
    }

    stroke!(private);

    fill!();

    build!();
}

impl PolyBuilder for BezierBuilder {
    fn options(&self) -> &Options {
        &self.options
    }

    fn bounding_rect(&self) -> Rect {
        todo!("bézier curves can't be filled")
    }

    fn build<B: tess::path::traits::PathBuilder>(self, builder: &mut B) {
        builder.begin(self.start.into());
        for segment in self.segments {
            match segment.ctrl {
                ControlPoint::Quadratic(ctrl) => {
                    builder.quadratic_bezier_to(ctrl.into(), segment.end.into());
                }
                ControlPoint::Cubic(ctrl1, ctrl2) => {
                    builder.cubic_bezier_to(ctrl1.into(), ctrl2.into(), segment.end.into());
                }
            }
        }
        builder.end(!self.open);
    }
}