Lightningbeam/lightningbeam-ui/lightningbeam-core/src/tool.rs

270 lines
7.8 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 {
/// Selection tool - select and move objects
Select,
/// Draw/Pen tool - freehand drawing
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,
}
/// 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
}
}
/// 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,
},
/// 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 => "Draw",
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",
}
}
/// 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",
}
}
/// Get all available tools
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 keyboard shortcut hint
pub fn shortcut_hint(self) -> &'static str {
match self {
Tool::Select => "V",
Tool::Draw => "P",
Tool::Transform => "Q",
Tool::Rectangle => "R",
Tool::Ellipse => "E",
Tool::PaintBucket => "B",
Tool::Eyedropper => "I",
Tool::Line => "L",
Tool::Polygon => "G",
Tool::BezierEdit => "A",
Tool::Text => "T",
Tool::RegionSelect => "S",
}
}
}