imageproc/drawing/
bezier.rs1use crate::definitions::Image;
2use crate::drawing::line::draw_line_segment_mut;
3use crate::drawing::Canvas;
4use image::{GenericImage, ImageBuffer};
5use std::f32;
6use std::i32;
7
8#[must_use = "the function does not modify the original image"]
12pub fn draw_cubic_bezier_curve<I>(
13 image: &I,
14 start: (f32, f32),
15 end: (f32, f32),
16 control_a: (f32, f32),
17 control_b: (f32, f32),
18 color: I::Pixel,
19) -> Image<I::Pixel>
20where
21 I: GenericImage,
22{
23 let mut out = ImageBuffer::new(image.width(), image.height());
24 out.copy_from(image, 0, 0).unwrap();
25 draw_cubic_bezier_curve_mut(&mut out, start, end, control_a, control_b, color);
26 out
27}
28
29pub fn draw_cubic_bezier_curve_mut<C>(
33 canvas: &mut C,
34 start: (f32, f32),
35 end: (f32, f32),
36 control_a: (f32, f32),
37 control_b: (f32, f32),
38 color: C::Pixel,
39) where
40 C: Canvas,
41{
42 let cubic_bezier_curve = |t: f32| {
44 let t2 = t * t;
45 let t3 = t2 * t;
46 let mt = 1.0 - t;
47 let mt2 = mt * mt;
48 let mt3 = mt2 * mt;
49 let x = (start.0 * mt3)
50 + (3.0 * control_a.0 * mt2 * t)
51 + (3.0 * control_b.0 * mt * t2)
52 + (end.0 * t3);
53 let y = (start.1 * mt3)
54 + (3.0 * control_a.1 * mt2 * t)
55 + (3.0 * control_b.1 * mt * t2)
56 + (end.1 * t3);
57 (x.round(), y.round()) };
59
60 let distance = |point_a: (f32, f32), point_b: (f32, f32)| {
61 ((point_a.0 - point_b.0).powi(2) + (point_a.1 - point_b.1).powi(2)).sqrt()
62 };
63
64 let curve_length_bound: f32 =
66 distance(start, control_a) + distance(control_a, control_b) + distance(control_b, end);
67
68 let num_segments: i32 = ((curve_length_bound.powi(2) + 800.0).sqrt() / 8.0) as i32;
70
71 let t_interval = 1f32 / (num_segments as f32);
73 let mut t1 = 0f32;
74 for i in 0..num_segments {
75 let t2 = (i as f32 + 1.0) * t_interval;
76 draw_line_segment_mut(
77 canvas,
78 cubic_bezier_curve(t1),
79 cubic_bezier_curve(t2),
80 color,
81 );
82 t1 = t2;
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use image::{GrayImage, Luma};
89
90 macro_rules! bench_cubic_bezier_curve {
91 ($name:ident, $start:expr, $end:expr, $control_a:expr, $control_b:expr) => {
92 #[bench]
93 fn $name(b: &mut test::Bencher) {
94 use super::draw_cubic_bezier_curve_mut;
95
96 let mut image = GrayImage::new(500, 500);
97 let color = Luma([50u8]);
98 b.iter(|| {
99 draw_cubic_bezier_curve_mut(
100 &mut image, $start, $end, $control_a, $control_b, color,
101 );
102 test::black_box(&image);
103 });
104 }
105 };
106 }
107
108 bench_cubic_bezier_curve!(
109 bench_draw_cubic_bezier_curve_short,
110 (100.0, 100.0),
111 (130.0, 130.0),
112 (110.0, 100.0),
113 (120.0, 130.0)
114 );
115
116 bench_cubic_bezier_curve!(
117 bench_draw_cubic_bezier_curve_long,
118 (100.0, 100.0),
119 (400.0, 400.0),
120 (500.0, 0.0),
121 (0.0, 500.0)
122 );
123}