feat: 实现多边形功能
This commit is contained in:
@@ -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 {
|
pub struct MSCanvas {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
@@ -50,6 +64,8 @@ pub struct MSCanvas {
|
|||||||
color: MSColor,
|
color: MSColor,
|
||||||
|
|
||||||
line_width: i32,
|
line_width: i32,
|
||||||
|
|
||||||
|
path2d: Path2D,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -62,6 +78,7 @@ impl MSCanvas {
|
|||||||
pixels_bak: Vec::new(),
|
pixels_bak: Vec::new(),
|
||||||
color: MSColor::BLACK,
|
color: MSColor::BLACK,
|
||||||
line_width: 1,
|
line_width: 1,
|
||||||
|
path2d: Path2D::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,6 +652,37 @@ impl MSCanvas {
|
|||||||
let points = vec![begin, p1, p2, end];
|
let points = vec![begin, p1, p2, end];
|
||||||
self.bezier(points);
|
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 {
|
fn point_muln(point: Point, t: f32) -> Point {
|
||||||
|
|||||||
96
src/paint.rs
96
src/paint.rs
@@ -53,6 +53,7 @@ enum Message {
|
|||||||
MousePressed(Point),
|
MousePressed(Point),
|
||||||
MouseReleased(Point),
|
MouseReleased(Point),
|
||||||
MouseMoved(Point),
|
MouseMoved(Point),
|
||||||
|
MouseDoubleClick(Point),
|
||||||
|
|
||||||
Clear,
|
Clear,
|
||||||
SavePNG,
|
SavePNG,
|
||||||
@@ -94,6 +95,34 @@ enum Tool {
|
|||||||
Count,
|
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 {
|
impl From<usize> for Tool {
|
||||||
fn from(tool: usize) -> Self {
|
fn from(tool: usize) -> Self {
|
||||||
match tool {
|
match tool {
|
||||||
@@ -219,6 +248,8 @@ struct PaintApp {
|
|||||||
is_controlling: bool,
|
is_controlling: bool,
|
||||||
control_points: Vec<Point>,
|
control_points: Vec<Point>,
|
||||||
|
|
||||||
|
is_path_beginning: bool,
|
||||||
|
|
||||||
/// 用于显示的图像句柄缓存
|
/// 用于显示的图像句柄缓存
|
||||||
/// 每次像素变化后需要重新生成
|
/// 每次像素变化后需要重新生成
|
||||||
image_handle: image::Handle,
|
image_handle: image::Handle,
|
||||||
@@ -249,6 +280,7 @@ impl PaintApp {
|
|||||||
end_point: Point::ORIGIN,
|
end_point: Point::ORIGIN,
|
||||||
is_controlling: false,
|
is_controlling: false,
|
||||||
control_points: Vec::with_capacity(2),
|
control_points: Vec::with_capacity(2),
|
||||||
|
is_path_beginning: false,
|
||||||
image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels),
|
image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels),
|
||||||
dirty: false,
|
dirty: false,
|
||||||
config,
|
config,
|
||||||
@@ -266,7 +298,8 @@ impl PaintApp {
|
|||||||
let canvas_area = mouse_area(image_widget)
|
let canvas_area = mouse_area(image_widget)
|
||||||
.on_press(|pos| Message::MousePressed(pos)) // 占位,实际逻辑在 on_drag 或自定义
|
.on_press(|pos| Message::MousePressed(pos)) // 占位,实际逻辑在 on_drag 或自定义
|
||||||
.on_release(|pos| Message::MouseReleased(pos))
|
.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 左上角的,这正是我们需要的!
|
// 注意:mouse_area 的 on_move 给出的坐标通常是相对于 widget 左上角的,这正是我们需要的!
|
||||||
let canvas_area = container(canvas_area)
|
let canvas_area = container(canvas_area)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@@ -293,7 +326,10 @@ impl PaintApp {
|
|||||||
.on_press(Message::ClickTool(Tool::from(i + 1)));
|
.on_press(Message::ClickTool(Tool::from(i + 1)));
|
||||||
columns.push(row![btn1, btn2].into());
|
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();
|
let palette = theme.extended_palette();
|
||||||
|
|
||||||
container::Style {
|
container::Style {
|
||||||
@@ -408,6 +444,9 @@ impl PaintApp {
|
|||||||
Tool::Rectangle => {
|
Tool::Rectangle => {
|
||||||
self.update_with_rectangle(message);
|
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
|
// endregion
|
||||||
|
|
||||||
pub fn update_tool_states(&mut self, tool: Tool) {
|
pub fn update_tool_states(&mut self, tool: Tool) {
|
||||||
@@ -719,12 +801,22 @@ impl PaintApp {
|
|||||||
if idx >= self.tool_states.len() {
|
if idx >= self.tool_states.len() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if self.tool_selected == Tool::Polygon {
|
||||||
|
// 切换到其他工具,闭合路径
|
||||||
|
self.canvas.close_path();
|
||||||
|
self.canvas.stroke();
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
let old_value = self.tool_states[idx];
|
let old_value = self.tool_states[idx];
|
||||||
for i in 0..(Tool::Count as usize) {
|
for i in 0..(Tool::Count as usize) {
|
||||||
self.tool_states[i] = false;
|
self.tool_states[i] = false;
|
||||||
}
|
}
|
||||||
self.tool_states[idx] = !old_value;
|
self.tool_states[idx] = !old_value;
|
||||||
self.tool_selected = idx.into();
|
self.tool_selected = idx.into();
|
||||||
|
|
||||||
|
self.is_drawing = false;
|
||||||
|
self.is_path_beginning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 将原始字节转换为 Iced 的图像句柄
|
/// 将原始字节转换为 Iced 的图像句柄
|
||||||
|
|||||||
Reference in New Issue
Block a user