From e8d72dd72f96cc78c255b5a997944b9b02adc921 Mon Sep 17 00:00:00 2001 From: yeqing Date: Mon, 2 Mar 2026 14:48:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=A4=9A=E8=BE=B9?= =?UTF-8?q?=E5=BD=A2=E5=A1=AB=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mscanvas.rs | 202 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 173 insertions(+), 29 deletions(-) diff --git a/src/mscanvas.rs b/src/mscanvas.rs index 6c92108..0c653b6 100644 --- a/src/mscanvas.rs +++ b/src/mscanvas.rs @@ -1,4 +1,4 @@ -use iced::Point; +use iced::{Color, Point}; use rand::prelude::*; #[derive(Debug, Clone, Copy, PartialEq)] @@ -35,6 +35,65 @@ impl MSColor { b: 255, a: 255, }; + + pub const RED: MSColor = MSColor { + r: 255, + g: 0, + b: 0, + a: 255, + }; + + pub const GREEN: MSColor = MSColor { + r: 0, + g: 255, + b: 0, + a: 255, + }; + + pub const BLUE: MSColor = MSColor { + r: 0, + g: 0, + b: 255, + a: 255, + }; + + pub fn into_color(self) -> Color { + Color::from_rgba8( + self.r, + self.g, + self.b, + (self.a / 255) as f32, + ) + } +} + +#[derive(Clone, Copy, PartialEq)] +struct Edge { + y_max: i32, // 边的最大 y(不包含) + x: f32, // 当前扫描线 y 处的 x 值 + dx_dy: f32, // 1 / slope = Δx / Δy +} + +impl Edge { + fn new(p1: Point, p2: Point) -> Option { + // 确保 p1.y <= p2.y + let (top, bottom) = if p1.y < p2.y { (p1, p2) } else { (p2, p1) }; + + // 忽略水平边(不会贡献交点) + if (top.y - bottom.y).abs() < f32::EPSILON { + return None; + } + + let dx = (bottom.x - top.x); + let dy = (bottom.y - top.y); + let dx_dy = dx / dy; + + Some(Edge { + y_max: bottom.y as i32, + x: top.x, + dx_dy, + }) + } } pub struct Path2D { @@ -61,7 +120,8 @@ pub struct MSCanvas { pixels_bak: Vec, /// 当前笔画颜色 - color: MSColor, + foreground_color: MSColor, + background_color: MSColor, line_width: i32, @@ -76,14 +136,19 @@ impl MSCanvas { height, pixels: vec![255; (width * height * 4) as usize], pixels_bak: Vec::new(), - color: MSColor::BLACK, + foreground_color: MSColor::BLACK, + background_color: MSColor::WHITE, line_width: 1, path2d: Path2D::new(), } } - pub fn set_color(&mut self, color: MSColor) { - self.color = color; + pub fn set_foreground_color(&mut self, color: MSColor) { + self.foreground_color = color; + } + + pub fn set_background_color(&mut self, color: MSColor) { + self.background_color = color; } pub fn set_line_width(&mut self, line_width: i32) { @@ -168,10 +233,10 @@ impl MSCanvas { // 写入 RGBA 数据 // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 - self.pixels[index] = self.color.r; // R - self.pixels[index + 1] = self.color.g; // G - self.pixels[index + 2] = self.color.b; // B - self.pixels[index + 3] = self.color.a; // A + self.pixels[index] = self.foreground_color.r; // R + self.pixels[index + 1] = self.foreground_color.g; // G + self.pixels[index + 2] = self.foreground_color.b; // B + self.pixels[index + 3] = self.foreground_color.a; // A } pub fn draw_pixel_color_at(&mut self, point: Point, color: MSColor) { @@ -203,13 +268,17 @@ impl MSCanvas { // 写入 RGBA 数据 // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 - self.pixels[index] = self.color.r; // R - self.pixels[index + 1] = self.color.g; // G - self.pixels[index + 2] = self.color.b; // B - self.pixels[index + 3] = self.color.a; // A + self.pixels[index] = self.foreground_color.r; // R + self.pixels[index + 1] = self.foreground_color.g; // G + self.pixels[index + 2] = self.foreground_color.b; // B + self.pixels[index + 3] = self.foreground_color.a; // A } pub fn draw_pixel_row(&mut self, xs: i32, xe: i32, y: i32) { + self.draw_pixel_row_color(xs, xe, y, self.foreground_color); + } + + pub fn draw_pixel_row_color(&mut self, xs: i32, xe: i32, y: i32, color: MSColor) { if y < 0 || y >= self.height { return; } @@ -222,10 +291,10 @@ impl MSCanvas { // 写入 RGBA 数据 // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 - self.pixels[index] = self.color.r; // R - self.pixels[index + 1] = self.color.g; // G - self.pixels[index + 2] = self.color.b; // B - self.pixels[index + 3] = self.color.a; // A + self.pixels[index] = color.r; // R + self.pixels[index + 1] = color.g; // G + self.pixels[index + 2] = color.b; // B + self.pixels[index + 3] = color.a; // A } } @@ -421,7 +490,7 @@ impl MSCanvas { let start_x = begin.x as i32; let start_y = begin.y as i32; let target_color = self.pixel_at(start_x, start_y); - if target_color == self.color { + if target_color == self.foreground_color { return (0, 0); } @@ -465,7 +534,7 @@ impl MSCanvas { } let target_color = self.pixel_at(start_x, start_y); - if target_color == (self.color) { + if target_color == (self.foreground_color) { return (0, 0); } @@ -609,10 +678,10 @@ impl MSCanvas { let max_sq = max_r * max_r; let color = MSColor::new( - (255 + self.color.r) / 2, - self.color.g, - self.color.b, - self.color.a, + (255 + self.foreground_color.r) / 2, + self.foreground_color.g, + self.foreground_color.b, + self.foreground_color.a, ); let center_x = center.x as i32; @@ -648,12 +717,20 @@ impl MSCanvas { } } - pub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32) { + fn fill_rect_color(&mut self, x: i32, y: i32, width: i32, height: i32, color: MSColor) { for yi in y..(y + height) { - self.draw_pixel_row(x, x + width, yi); + self.draw_pixel_row_color(x, x + width, yi, color); } } + fn fill_rect_foreground_color(&mut self, x: i32, y: i32, width: i32, height: i32) { + self.fill_rect_color(x, y, width, height, self.foreground_color); + } + + pub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32) { + self.fill_rect_color(x, y, width, height, self.background_color) + } + pub fn round_rect(&mut self, x: f32, y: f32, width: f32, height: f32, radius: f32) { if (width as i32) < 2 * self.line_width || (height as i32) < 2 * self.line_width @@ -664,25 +741,25 @@ impl MSCanvas { return; } - self.fill_rect( + self.fill_rect_foreground_color( (x + radius) as i32, y as i32, (width - 2.0 * radius) as i32, self.line_width, ); - self.fill_rect( + self.fill_rect_foreground_color( (x + radius) as i32, (y + height) as i32 - self.line_width, (width - 2.0 * radius) as i32, self.line_width, ); - self.fill_rect( + self.fill_rect_foreground_color( x as i32, (y + radius) as i32, self.line_width, (height - 2.0 * radius) as i32, ); - self.fill_rect( + self.fill_rect_foreground_color( (x + width) as i32 - self.line_width, (y + radius) as i32, self.line_width, @@ -913,6 +990,68 @@ impl MSCanvas { } } + /// 填充一个简单多边形(支持凹多边形) + pub fn fill_polygon(&mut self, points: &[Point]) { + if points.len() < 3 { + return; + } + + // 计算 y 范围 + let min_y = points.iter().map(|p| p.y as i32).min().unwrap_or(0); + let max_y = points.iter().map(|p| p.y as i32).max().unwrap_or(0); + + // 构建边表(Edge Table):按 ymin 分组 + let mut edge_table: Vec> = vec![Vec::new(); (max_y - min_y + 1) as usize]; + + for i in 0..points.len() { + let p1 = points[i]; + let p2 = points[(i + 1) % points.len()]; + if let Some(edge) = Edge::new(p1, p2) { + let idx = (edge.x as i32).min(max_y).max(min_y); // 实际应使用 top.y + // 正确做法:使用 top.y 作为插入位置 + let top_y = p1.y.min(p2.y) as i32; + if top_y >= min_y && top_y <= max_y { + edge_table[(top_y - min_y) as usize].push(edge); + } + } + } + + // 活动边表(Active Edge List),按 x 排序 + let mut active_edges: Vec = Vec::new(); + + // 从 min_y 到 max_y 扫描每一行 + for y in min_y..=max_y { + let y_idx = (y - min_y) as usize; + + // 1. 添加新边(y == ymin 的边) + if y_idx < edge_table.len() { + active_edges.append(&mut edge_table[y_idx]); + } + + // 2. 移除 y >= y_max 的边 + active_edges.retain(|e| y < e.y_max); + + // 3. 按 x 排序 + active_edges.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal)); + + // 4. 成对填充像素 + for i in (0..active_edges.len()).step_by(2) { + if i + 1 < active_edges.len() { + let x_start = active_edges[i].x.ceil() as i32; + let x_end = active_edges[i + 1].x.floor() as i32; + for x in x_start..=x_end { + self.draw_pixel_color_at(Point::new(x as f32, y as f32), self.background_color); + } + } + } + + // 5. 更新活动边的 x(为下一行准备) + for edge in &mut active_edges { + edge.x += edge.dx_dy; + } + } + } + pub fn begin_path(&mut self) { self.path2d = Path2D::new(); } @@ -943,6 +1082,11 @@ impl MSCanvas { self.draw_line_with_circle_brush(points[i], points[i + 1]); } } + + pub fn fill(&mut self) { + let points = self.path2d.points.clone(); + self.fill_polygon(&points[..]); + } } fn point_muln(point: Point, t: f32) -> Point {