From 25c906278c5150b5a1d0cd0d90c8c58b37d18df7 Mon Sep 17 00:00:00 2001 From: yeqing Date: Sat, 28 Feb 2026 16:17:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=9B=B2=E7=BA=BF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mscanvas.rs | 44 ++++++++++++++++++++++++++++++ src/paint.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/src/mscanvas.rs b/src/mscanvas.rs index db823be..597401a 100644 --- a/src/mscanvas.rs +++ b/src/mscanvas.rs @@ -599,4 +599,48 @@ impl MSCanvas { } } } + + fn recursive_bezier(&mut self, control_points: &[Point], t: f32) -> Point { + // Implement de Casteljau's algorithm + let n = control_points.len(); + if n == 1 { + return control_points[0]; + } + let mut new_points: Vec = Vec::with_capacity(n - 1); + for i in 0..(n - 1) { + let p0 = control_points[i]; + let p1 = control_points[i + 1]; + let point = point_add(point_muln(p0, 1.0 - t), point_muln(p1, t)); + new_points.push(point); + } + self.recursive_bezier(&new_points[..], t) + } + + pub fn bezier(&mut self, control_points: Vec) { + // Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's recursive Bezier algorithm. + let mut t = 0.0; + while t <= 1.0 { + let point = self.recursive_bezier(&control_points, t); + self.brush_circle(point); + t += 0.001; + } + } + + pub fn quadratic_bezier(&mut self, begin: Point, end: Point, p1: Point) { + let points = vec![begin, p1, end]; + self.bezier(points); + } + + pub fn cubic_bezier(&mut self, begin: Point, end: Point, p1: Point, p2: Point) { + let points = vec![begin, p1, p2, end]; + self.bezier(points); + } +} + +fn point_muln(point: Point, t: f32) -> Point { + Point::new(point.x * t, point.y * t) +} + +fn point_add(p1: Point, p2: Point) -> Point { + Point::new(p1.x + p2.x, p1.y + p2.y) } diff --git a/src/paint.rs b/src/paint.rs index df666e8..8bb7a4d 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -207,16 +207,23 @@ struct PaintApp { canvas: MSCanvas, - // 是否正在绘制 + /// 是否正在绘制 is_drawing: bool, + /// pencil brush line curve rectangle begin_point: Point, + /// curve + end_point: Point, - // 用于显示的图像句柄缓存 - // 每次像素变化后需要重新生成 + /// 贝塞尔曲线控制 + is_controlling: bool, + control_points: Vec, + + /// 用于显示的图像句柄缓存 + /// 每次像素变化后需要重新生成 image_handle: image::Handle, - // 标记像素是否被修改,用于优化图像句柄的生成 + /// 标记像素是否被修改,用于优化图像句柄的生成 dirty: bool, config: Config, @@ -239,6 +246,9 @@ impl PaintApp { canvas, is_drawing: false, begin_point: Point::ORIGIN, + end_point: Point::ORIGIN, + is_controlling: false, + control_points: Vec::with_capacity(2), image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels), dirty: false, config, @@ -392,6 +402,9 @@ impl PaintApp { Tool::Line => { self.update_with_line(message); } + Tool::Curve => { + self.update_with_curve(message); + } Tool::Rectangle => { self.update_with_rectangle(message); } @@ -471,7 +484,7 @@ impl PaintApp { _ => None, }); let tick = time::every(milliseconds(50)).map(Message::Tick); - return Subscription::batch(vec![ev, tick]); + Subscription::batch(vec![ev, tick]) } // endregion @@ -629,6 +642,54 @@ impl PaintApp { } } + pub fn update_with_curve(&mut self, message: Message) { + match message { + Message::MousePressed(pos) => { + if self.is_controlling { + if self.control_points.len() == 0 { + self.canvas.restore_pixels(); + self.canvas + .quadratic_bezier(self.begin_point, self.end_point, pos); + self.control_points.push(pos); + } else { + self.canvas.restore_pixels(); + self.canvas.cubic_bezier( + self.begin_point, + self.end_point, + self.control_points[0], + pos, + ); + self.control_points.push(pos); + } + self.dirty = true; + } else { + self.is_drawing = true; + self.canvas.save_pixels(); + self.begin_point = pos; + } + } + Message::MouseReleased(pos) => { + if self.control_points.len() == 0 { + self.is_drawing = false; + self.end_point = pos; + self.is_controlling = true; + } else if self.control_points.len() == 2 { + self.control_points.clear(); + self.is_controlling = false; + } + } + Message::MouseMoved(pos) => { + if self.is_drawing { + self.canvas.restore_pixels(); + self.canvas + .draw_line_with_circle_brush(self.begin_point, pos); + self.dirty = true; + } + } + _ => {} + } + } + pub fn update_with_rectangle(&mut self, message: Message) { match message { Message::MousePressed(pos) => {