406 lines
13 KiB
Rust
406 lines
13 KiB
Rust
/// Tool system for the toolbar
|
|
///
|
|
/// Defines the available drawing/editing tools
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
use vello::kurbo::Point;
|
|
|
|
/// Drawing and editing tools
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum Tool {
|
|
// ── Vector / shared tools ──────────────────────────────────────────────
|
|
/// Selection tool - select and move objects
|
|
Select,
|
|
/// Draw/Brush tool - freehand drawing (vector) / paintbrush (raster)
|
|
Draw,
|
|
/// Transform tool - scale, rotate, skew
|
|
Transform,
|
|
/// Rectangle shape tool
|
|
Rectangle,
|
|
/// Ellipse/Circle shape tool
|
|
Ellipse,
|
|
/// Paint bucket - fill areas with color
|
|
PaintBucket,
|
|
/// Eyedropper - pick colors from the canvas
|
|
Eyedropper,
|
|
/// Line tool - draw straight lines
|
|
Line,
|
|
/// Polygon tool - draw polygons
|
|
Polygon,
|
|
/// Bezier edit tool - edit bezier curve control points
|
|
BezierEdit,
|
|
/// Text tool - add and edit text
|
|
Text,
|
|
/// Region select tool - select sub-regions of shapes by clipping
|
|
RegionSelect,
|
|
/// Split tool - split audio/video clips at a point
|
|
Split,
|
|
// ── Raster brush tools ────────────────────────────────────────────────
|
|
/// Pencil tool - hard-edged raster brush
|
|
Pencil,
|
|
/// Pen tool - pressure-sensitive raster pen
|
|
Pen,
|
|
/// Airbrush tool - soft spray raster brush
|
|
Airbrush,
|
|
/// Erase tool - erase raster pixels
|
|
Erase,
|
|
/// Smudge tool - smudge/blend raster pixels
|
|
Smudge,
|
|
/// Clone Stamp - copy pixels from a source point
|
|
CloneStamp,
|
|
/// Healing Brush - content-aware pixel repair
|
|
HealingBrush,
|
|
/// Pattern Stamp - paint with a repeating pattern
|
|
PatternStamp,
|
|
/// Dodge/Burn - lighten or darken pixels
|
|
DodgeBurn,
|
|
/// Sponge - saturate or desaturate pixels
|
|
Sponge,
|
|
/// Blur/Sharpen - blur or sharpen pixel regions
|
|
BlurSharpen,
|
|
// ── Raster fill / shape ───────────────────────────────────────────────
|
|
/// Gradient tool - fill with a gradient
|
|
Gradient,
|
|
/// Custom Shape tool - draw from a shape library
|
|
CustomShape,
|
|
// ── Raster selection tools ────────────────────────────────────────────
|
|
/// Elliptical marquee selection
|
|
SelectEllipse,
|
|
/// Lasso select tool - freehand / polygonal / magnetic selection
|
|
SelectLasso,
|
|
/// Magic Wand - select by colour similarity
|
|
MagicWand,
|
|
/// Quick Select - brush-based smart selection
|
|
QuickSelect,
|
|
// ── Raster transform tools ────────────────────────────────────────────
|
|
/// Warp / perspective transform
|
|
Warp,
|
|
/// Liquify - freeform pixel warping
|
|
Liquify,
|
|
}
|
|
|
|
/// Region select mode
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum RegionSelectMode {
|
|
/// Rectangular region selection
|
|
Rectangle,
|
|
/// Freehand lasso region selection
|
|
Lasso,
|
|
}
|
|
|
|
impl Default for RegionSelectMode {
|
|
fn default() -> Self {
|
|
Self::Rectangle
|
|
}
|
|
}
|
|
|
|
/// Lasso selection sub-mode
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum LassoMode {
|
|
/// Freehand lasso (existing, implemented)
|
|
Freehand,
|
|
/// Click-to-place polygonal lasso
|
|
Polygonal,
|
|
/// Magnetically snaps to edges
|
|
Magnetic,
|
|
}
|
|
|
|
impl Default for LassoMode {
|
|
fn default() -> Self {
|
|
Self::Freehand
|
|
}
|
|
}
|
|
|
|
/// Tool state tracking for interactive operations
|
|
#[derive(Debug, Clone)]
|
|
pub enum ToolState {
|
|
/// Tool is idle (no operation in progress)
|
|
Idle,
|
|
|
|
/// Drawing a freehand path
|
|
DrawingPath {
|
|
points: Vec<Point>,
|
|
simplify_mode: SimplifyMode,
|
|
},
|
|
|
|
/// Drawing a raster paint stroke
|
|
DrawingRasterStroke {
|
|
points: Vec<crate::raster_layer::StrokePoint>,
|
|
},
|
|
|
|
/// Drawing a freehand lasso selection on a raster layer
|
|
DrawingRasterLasso {
|
|
points: Vec<(i32, i32)>,
|
|
},
|
|
|
|
/// Drawing a rectangular marquee selection on a raster layer
|
|
DrawingRasterMarquee {
|
|
start: (i32, i32),
|
|
current: (i32, i32),
|
|
},
|
|
|
|
/// Moving an existing raster selection (and its floating pixels, if any).
|
|
MovingRasterSelection {
|
|
/// Canvas position of the pointer at the last processed event, used to
|
|
/// compute per-frame deltas.
|
|
last: (i32, i32),
|
|
},
|
|
|
|
/// Dragging selected objects
|
|
DraggingSelection {
|
|
start_pos: Point,
|
|
start_mouse: Point,
|
|
original_positions: HashMap<Uuid, Point>,
|
|
},
|
|
|
|
/// Creating a marquee selection rectangle
|
|
MarqueeSelecting {
|
|
start: Point,
|
|
current: Point,
|
|
},
|
|
|
|
/// Creating a rectangle shape
|
|
CreatingRectangle {
|
|
start_point: Point, // Starting point (corner or center depending on modifiers)
|
|
current_point: Point, // Current mouse position
|
|
centered: bool, // If true, start_point is center; if false, it's a corner
|
|
constrain_square: bool, // If true, constrain to square (equal width/height)
|
|
},
|
|
|
|
/// Creating an ellipse shape
|
|
CreatingEllipse {
|
|
start_point: Point, // Starting point (center or corner depending on modifiers)
|
|
current_point: Point, // Current mouse position
|
|
corner_mode: bool, // If true, start is corner; if false, start is center
|
|
constrain_circle: bool, // If true, constrain to circle (equal radii)
|
|
},
|
|
|
|
/// Transforming selected objects (scale, rotate)
|
|
Transforming {
|
|
mode: TransformMode,
|
|
original_transforms: HashMap<Uuid, crate::object::Transform>,
|
|
pivot: Point,
|
|
start_mouse: Point, // Mouse position when transform started
|
|
current_mouse: Point, // Current mouse position during drag
|
|
original_bbox: vello::kurbo::Rect, // Bounding box at start of transform (fixed)
|
|
},
|
|
|
|
/// Creating a line
|
|
CreatingLine {
|
|
start_point: Point, // Starting point of the line
|
|
current_point: Point, // Current mouse position (end point)
|
|
},
|
|
|
|
/// Creating a polygon
|
|
CreatingPolygon {
|
|
center: Point, // Center point of the polygon
|
|
current_point: Point, // Current mouse position (determines radius)
|
|
num_sides: u32, // Number of sides (from properties, default 5)
|
|
},
|
|
|
|
/// Editing a vertex (dragging it and connected edges)
|
|
EditingVertex {
|
|
vertex_id: crate::dcel::VertexId,
|
|
connected_edges: Vec<crate::dcel::EdgeId>, // edges to update when vertex moves
|
|
},
|
|
|
|
/// Editing a curve (reshaping with moldCurve algorithm)
|
|
EditingCurve {
|
|
edge_id: crate::dcel::EdgeId,
|
|
original_curve: vello::kurbo::CubicBez,
|
|
start_mouse: Point,
|
|
parameter_t: f64,
|
|
},
|
|
|
|
/// Pending curve interaction: click selects edge, drag starts curve editing
|
|
PendingCurveInteraction {
|
|
edge_id: crate::dcel::EdgeId,
|
|
parameter_t: f64,
|
|
start_mouse: Point,
|
|
},
|
|
|
|
/// Drawing a region selection rectangle
|
|
RegionSelectingRect {
|
|
start: Point,
|
|
current: Point,
|
|
},
|
|
|
|
/// Drawing a freehand lasso region selection
|
|
RegionSelectingLasso {
|
|
points: Vec<Point>,
|
|
},
|
|
|
|
/// Editing a control point (BezierEdit tool only)
|
|
EditingControlPoint {
|
|
edge_id: crate::dcel::EdgeId,
|
|
point_index: u8, // 1 or 2 (p1 or p2 of the cubic bezier)
|
|
original_curve: vello::kurbo::CubicBez,
|
|
start_pos: Point,
|
|
},
|
|
}
|
|
|
|
/// Path simplification mode for the draw tool
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum SimplifyMode {
|
|
/// Ramer-Douglas-Peucker corner detection
|
|
Corners,
|
|
/// Schneider curve fitting for smooth curves
|
|
Smooth,
|
|
/// No simplification (use raw points)
|
|
Verbatim,
|
|
}
|
|
|
|
/// Transform mode for the transform tool
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum TransformMode {
|
|
/// Scale from a corner
|
|
ScaleCorner { origin: Point },
|
|
/// Scale along an edge
|
|
ScaleEdge { axis: Axis, origin: Point },
|
|
/// Rotate around a pivot
|
|
Rotate { center: Point },
|
|
/// Skew along an edge
|
|
Skew { axis: Axis, origin: Point },
|
|
}
|
|
|
|
/// Axis for edge scaling
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Axis {
|
|
Horizontal,
|
|
Vertical,
|
|
}
|
|
|
|
impl Default for ToolState {
|
|
fn default() -> Self {
|
|
Self::Idle
|
|
}
|
|
}
|
|
|
|
impl Tool {
|
|
/// Get display name for the tool
|
|
pub fn display_name(self) -> &'static str {
|
|
match self {
|
|
Tool::Select => "Select",
|
|
Tool::Draw => "Brush",
|
|
Tool::Transform => "Transform",
|
|
Tool::Rectangle => "Rectangle",
|
|
Tool::Ellipse => "Ellipse",
|
|
Tool::PaintBucket => "Paint Bucket",
|
|
Tool::Eyedropper => "Eyedropper",
|
|
Tool::Line => "Line",
|
|
Tool::Polygon => "Polygon",
|
|
Tool::BezierEdit => "Bezier Edit",
|
|
Tool::Text => "Text",
|
|
Tool::RegionSelect => "Region Select",
|
|
Tool::Split => "Split",
|
|
Tool::Pencil => "Pencil",
|
|
Tool::Pen => "Pen",
|
|
Tool::Airbrush => "Airbrush",
|
|
Tool::Erase => "Eraser",
|
|
Tool::Smudge => "Smudge",
|
|
Tool::CloneStamp => "Clone Stamp",
|
|
Tool::HealingBrush => "Healing Brush",
|
|
Tool::PatternStamp => "Pattern Stamp",
|
|
Tool::DodgeBurn => "Dodge / Burn",
|
|
Tool::Sponge => "Sponge",
|
|
Tool::BlurSharpen => "Blur / Sharpen",
|
|
Tool::Gradient => "Gradient",
|
|
Tool::CustomShape => "Custom Shape",
|
|
Tool::SelectEllipse => "Elliptical Select",
|
|
Tool::SelectLasso => "Lasso Select",
|
|
Tool::MagicWand => "Magic Wand",
|
|
Tool::QuickSelect => "Quick Select",
|
|
Tool::Warp => "Warp",
|
|
Tool::Liquify => "Liquify",
|
|
}
|
|
}
|
|
|
|
/// Get SVG icon file name for the tool
|
|
pub fn icon_file(self) -> &'static str {
|
|
match self {
|
|
Tool::Select => "select.svg",
|
|
Tool::Draw => "draw.svg",
|
|
Tool::Transform => "transform.svg",
|
|
Tool::Rectangle => "rectangle.svg",
|
|
Tool::Ellipse => "ellipse.svg",
|
|
Tool::PaintBucket => "paint_bucket.svg",
|
|
Tool::Eyedropper => "eyedropper.svg",
|
|
Tool::Line => "line.svg",
|
|
Tool::Polygon => "polygon.svg",
|
|
Tool::BezierEdit => "bezier_edit.svg",
|
|
Tool::Text => "text.svg",
|
|
Tool::RegionSelect => "region_select.svg",
|
|
Tool::Split => "split.svg",
|
|
Tool::Erase => "erase.svg",
|
|
Tool::Smudge => "smudge.svg",
|
|
Tool::SelectLasso => "lasso.svg",
|
|
// Not yet implemented — use the placeholder icon
|
|
Tool::Pencil
|
|
| Tool::Pen
|
|
| Tool::Airbrush
|
|
| Tool::CloneStamp
|
|
| Tool::HealingBrush
|
|
| Tool::PatternStamp
|
|
| Tool::DodgeBurn
|
|
| Tool::Sponge
|
|
| Tool::BlurSharpen
|
|
| Tool::Gradient
|
|
| Tool::CustomShape
|
|
| Tool::SelectEllipse
|
|
| Tool::MagicWand
|
|
| Tool::QuickSelect
|
|
| Tool::Warp
|
|
| Tool::Liquify => "todo.svg",
|
|
}
|
|
}
|
|
|
|
/// Get all vector-layer tools (the full drawing toolset)
|
|
pub fn all() -> &'static [Tool] {
|
|
&[
|
|
Tool::Select,
|
|
Tool::Draw,
|
|
Tool::Transform,
|
|
Tool::Rectangle,
|
|
Tool::Ellipse,
|
|
Tool::PaintBucket,
|
|
Tool::Eyedropper,
|
|
Tool::Line,
|
|
Tool::Polygon,
|
|
Tool::BezierEdit,
|
|
Tool::Text,
|
|
Tool::RegionSelect,
|
|
]
|
|
}
|
|
|
|
/// Get the tools available for a given layer type
|
|
pub fn for_layer_type(layer_type: Option<crate::layer::LayerType>) -> &'static [Tool] {
|
|
use crate::layer::LayerType;
|
|
match layer_type {
|
|
None | Some(LayerType::Vector) => Tool::all(),
|
|
Some(LayerType::Audio) | Some(LayerType::Video) => &[Tool::Select, Tool::Split],
|
|
Some(LayerType::Raster) => &[
|
|
// Brush tools
|
|
Tool::Draw, Tool::Pencil, Tool::Pen, Tool::Airbrush,
|
|
Tool::Erase, Tool::Smudge,
|
|
Tool::CloneStamp, Tool::HealingBrush, Tool::PatternStamp,
|
|
Tool::DodgeBurn, Tool::Sponge, Tool::BlurSharpen,
|
|
// Fill / shape
|
|
Tool::PaintBucket, Tool::Gradient,
|
|
Tool::Rectangle, Tool::Ellipse, Tool::Polygon, Tool::Line, Tool::CustomShape,
|
|
// Selection
|
|
Tool::Select, Tool::SelectLasso,
|
|
Tool::MagicWand, Tool::QuickSelect,
|
|
// Transform
|
|
Tool::Transform, Tool::Warp, Tool::Liquify,
|
|
// Utility
|
|
Tool::Eyedropper,
|
|
],
|
|
_ => &[Tool::Select],
|
|
}
|
|
}
|
|
|
|
}
|