feat: 实现多边形功能

This commit is contained in:
2026-02-28 18:29:32 +08:00
parent 25c906278c
commit 9337f15da1
2 changed files with 153 additions and 13 deletions

View File

@@ -37,6 +37,20 @@ impl MSColor {
};
}
pub struct Path2D {
points: Vec<Point>,
}
impl Path2D {
pub fn new() -> Self {
Self { points: vec![] }
}
pub fn push(&mut self, point: Point) {
self.points.push(point);
}
}
pub struct MSCanvas {
width: i32,
height: i32,
@@ -50,6 +64,8 @@ pub struct MSCanvas {
color: MSColor,
line_width: i32,
path2d: Path2D,
}
#[allow(unused)]
@@ -62,6 +78,7 @@ impl MSCanvas {
pixels_bak: Vec::new(),
color: MSColor::BLACK,
line_width: 1,
path2d: Path2D::new(),
}
}
@@ -635,6 +652,37 @@ impl MSCanvas {
let points = vec![begin, p1, p2, end];
self.bezier(points);
}
pub fn begin_path(&mut self) {
self.path2d = Path2D::new();
}
pub fn close_path(&mut self) {
let points = &self.path2d.points;
if points.len() < 3 {
return;
}
self.path2d.push(points[0]);
}
pub fn move_to(&mut self, point: Point) {
self.path2d.push(point);
}
pub fn line_to(&mut self, point: Point) {
self.path2d.push(point);
}
pub fn stroke(&mut self) {
let mut points = self.path2d.points.clone();
if points.len() < 2 {
return;
}
// 连线
for i in 0..(points.len() - 1) {
self.draw_line_with_circle_brush(points[i], points[i + 1]);
}
}
}
fn point_muln(point: Point, t: f32) -> Point {

View File

@@ -53,6 +53,7 @@ enum Message {
MousePressed(Point),
MouseReleased(Point),
MouseMoved(Point),
MouseDoubleClick(Point),
Clear,
SavePNG,
@@ -94,6 +95,34 @@ enum Tool {
Count,
}
impl std::fmt::Display for Tool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Tool::FreeFormSelect => "FreeFormSelect",
Tool::Select => "Select",
Tool::Eraser => "Eraser",
Tool::FillWithColor => "FillWithColor",
Tool::PickColor => "PickColor",
Tool::Magnifier => "Magnifier",
Tool::Pencil => "Pencil",
Tool::Brush => "Brush",
Tool::Airbrush => "Airbrush",
Tool::Text => "Text",
Tool::Line => "Line",
Tool::Curve => "Curve",
Tool::Rectangle => "Rectangle",
Tool::Polygon => "Polygon",
Tool::Ellipse => "Ellipse",
Tool::RoundedRectangle => "RoundedRectangle",
Tool::Count => "Count",
}
)
}
}
impl From<usize> for Tool {
fn from(tool: usize) -> Self {
match tool {
@@ -219,6 +248,8 @@ struct PaintApp {
is_controlling: bool,
control_points: Vec<Point>,
is_path_beginning: bool,
/// 用于显示的图像句柄缓存
/// 每次像素变化后需要重新生成
image_handle: image::Handle,
@@ -249,6 +280,7 @@ impl PaintApp {
end_point: Point::ORIGIN,
is_controlling: false,
control_points: Vec::with_capacity(2),
is_path_beginning: false,
image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels),
dirty: false,
config,
@@ -266,7 +298,8 @@ impl PaintApp {
let canvas_area = mouse_area(image_widget)
.on_press(|pos| Message::MousePressed(pos)) // 占位,实际逻辑在 on_drag 或自定义
.on_release(|pos| Message::MouseReleased(pos))
.on_move(|pos| Message::MouseMoved(pos));
.on_move(|pos| Message::MouseMoved(pos))
.on_double_click(|pos| Message::MouseDoubleClick(pos));
// 注意mouse_area 的 on_move 给出的坐标通常是相对于 widget 左上角的,这正是我们需要的!
let canvas_area = container(canvas_area)
.width(Length::Fill)
@@ -293,7 +326,10 @@ impl PaintApp {
.on_press(Message::ClickTool(Tool::from(i + 1)));
columns.push(row![btn1, btn2].into());
}
let tool_config_area = container("").width(90).height(200).style(|theme: &Theme| {
let tool_config_area = container(text(format!("{}", self.tool_selected)))
.width(90)
.height(200)
.style(|theme: &Theme| {
let palette = theme.extended_palette();
container::Style {
@@ -408,6 +444,9 @@ impl PaintApp {
Tool::Rectangle => {
self.update_with_rectangle(message);
}
Tool::Polygon => {
self.update_with_polygon(message);
}
_ => {}
}
@@ -712,6 +751,49 @@ impl PaintApp {
}
}
pub fn update_with_polygon(&mut self, message: Message) {
match message {
Message::MousePressed(pos) => {
if self.is_path_beginning {
self.is_drawing = true;
self.canvas.restore_pixels();
self.canvas.stroke();
self.canvas
.draw_line_with_circle_brush(self.begin_point, pos);
self.dirty = true;
} else {
self.is_path_beginning = true;
self.is_drawing = true;
self.canvas.save_pixels();
self.begin_point = pos;
self.canvas.begin_path();
self.canvas.move_to(pos);
}
}
Message::MouseReleased(pos) => {
self.canvas.line_to(pos);
self.begin_point = pos;
}
Message::MouseMoved(pos) => {
if self.is_drawing {
self.canvas.restore_pixels();
self.canvas.stroke();
self.canvas
.draw_line_with_circle_brush(self.begin_point, pos);
self.dirty = true;
}
}
Message::MouseDoubleClick(_pos) => {
self.canvas.close_path();
self.canvas.stroke();
self.is_drawing = false;
self.is_path_beginning = false;
self.dirty = true;
}
_ => {}
}
}
// endregion
pub fn update_tool_states(&mut self, tool: Tool) {
@@ -719,12 +801,22 @@ impl PaintApp {
if idx >= self.tool_states.len() {
return;
}
if self.tool_selected == Tool::Polygon {
// 切换到其他工具,闭合路径
self.canvas.close_path();
self.canvas.stroke();
self.dirty = true;
}
let old_value = self.tool_states[idx];
for i in 0..(Tool::Count as usize) {
self.tool_states[i] = false;
}
self.tool_states[idx] = !old_value;
self.tool_selected = idx.into();
self.is_drawing = false;
self.is_path_beginning = false;
}
/// 将原始字节转换为 Iced 的图像句柄