1use crate::{
2 options::{Options, StrokeOptions},
3 tess, PolyBuilder,
4};
5use gee::{Point, Rect};
6
7#[derive(Clone, Debug)]
8pub enum ControlPoint {
9 Quadratic(Point),
10 Cubic(Point, Point),
11}
12
13#[derive(Clone, Debug)]
14pub struct BezierSegment {
15 end: Point,
16 ctrl: ControlPoint,
17}
18
19impl BezierSegment {
20 pub fn new(end: Point, ctrl: ControlPoint) -> Self {
21 Self { end, ctrl }
22 }
23
24 pub fn quadratic(end: Point, ctrl: Point) -> Self {
25 Self::new(end, ControlPoint::Quadratic(ctrl))
26 }
27
28 pub fn cubic(end: Point, ctrl1: Point, ctrl2: Point) -> Self {
29 Self::new(end, ControlPoint::Cubic(ctrl1, ctrl2))
30 }
31}
32
33#[derive(Clone, Debug, Default)]
34pub struct BezierBuilder {
35 start: Point,
36 segments: Vec<BezierSegment>,
37 open: bool,
38 options: Options,
39}
40
41impl BezierBuilder {
42 pub fn new(start: Point) -> Self {
43 Self::default().with_start(start)
44 }
45
46 pub fn from_bezier_segment(start: Point, segment: BezierSegment) -> Self {
47 Self::new(start).with_bezier_segment(segment)
48 }
49
50 pub fn from_bezier_segments(
51 start: Point,
52 segments: impl IntoIterator<Item = BezierSegment>,
53 ) -> Self {
54 Self::new(start).with_bezier_segments(segments)
55 }
56
57 pub fn from_quadratic_segment(start: Point, end: Point, ctrl: Point) -> Self {
58 Self::from_bezier_segment(start, BezierSegment::quadratic(end, ctrl))
59 }
60
61 pub fn from_cubic_segment(start: Point, end: Point, ctrl1: Point, ctrl2: Point) -> Self {
62 Self::from_bezier_segment(start, BezierSegment::cubic(end, ctrl1, ctrl2))
63 }
64
65 pub fn with_start(mut self, start: Point) -> Self {
66 self.start = start;
67 self
68 }
69
70 pub fn with_bezier_segment(mut self, segment: BezierSegment) -> Self {
71 self.segments.push(segment);
72 self
73 }
74
75 pub fn with_bezier_segments(
76 mut self,
77 segments: impl IntoIterator<Item = BezierSegment>,
78 ) -> Self {
79 self.segments.extend(segments);
80 self
81 }
82
83 pub fn with_quadratic_segment(self, end: Point, ctrl: Point) -> Self {
84 self.with_bezier_segment(BezierSegment::quadratic(end, ctrl))
85 }
86
87 pub fn with_cubic_segment(self, end: Point, ctrl1: Point, ctrl2: Point) -> Self {
88 self.with_bezier_segment(BezierSegment::cubic(end, ctrl1, ctrl2))
89 }
90
91 pub fn with_stroke(mut self, stroke_width: f32, open: bool) -> Self {
92 self.open = open;
93 self._with_stroke(stroke_width)
94 }
95
96 pub fn with_stroke_open(self, stroke_width: f32) -> Self {
97 self.with_stroke(stroke_width, true)
98 }
99
100 pub fn with_stroke_closed(self, stroke_width: f32) -> Self {
101 self.with_stroke(stroke_width, false)
102 }
103
104 pub fn with_stroke_opts(mut self, stroke_options: StrokeOptions, open: bool) -> Self {
105 self.open = open;
106 self._with_stroke_opts(stroke_options)
107 }
108
109 pub fn with_stroke_opts_open(self, stroke_options: StrokeOptions) -> Self {
110 self.with_stroke_opts(stroke_options, true)
111 }
112
113 pub fn with_stroke_opts_closed(self, stroke_options: StrokeOptions) -> Self {
114 self.with_stroke_opts(stroke_options, false)
115 }
116
117 stroke!(private);
118
119 fill!();
120
121 build!();
122}
123
124impl PolyBuilder for BezierBuilder {
125 fn options(&self) -> &Options {
126 &self.options
127 }
128
129 fn bounding_rect(&self) -> Rect {
130 todo!("bézier curves can't be filled")
131 }
132
133 fn build<B: tess::path::traits::PathBuilder>(self, builder: &mut B) {
134 builder.begin(self.start.into());
135 for segment in self.segments {
136 match segment.ctrl {
137 ControlPoint::Quadratic(ctrl) => {
138 builder.quadratic_bezier_to(ctrl.into(), segment.end.into());
139 }
140 ControlPoint::Cubic(ctrl1, ctrl2) => {
141 builder.cubic_bezier_to(ctrl1.into(), ctrl2.into(), segment.end.into());
142 }
143 }
144 }
145 builder.end(!self.open);
146 }
147}