drafftink_render/
renderer.rs

1//! Renderer trait abstraction.
2
3use kurbo::{Affine, Rect, Size};
4use peniko::Color;
5use drafftink_core::canvas::Canvas;
6use drafftink_core::shapes::Shape;
7use drafftink_core::snap::SnapTarget;
8use thiserror::Error;
9
10/// Renderer errors.
11#[derive(Debug, Error)]
12pub enum RendererError {
13    #[error("Initialization failed: {0}")]
14    InitFailed(String),
15    #[error("Render failed: {0}")]
16    RenderFailed(String),
17    #[error("Surface error: {0}")]
18    Surface(String),
19}
20
21/// Result type for renderer operations.
22#[allow(dead_code)]
23pub type RenderResult<T> = Result<T, RendererError>;
24
25/// Grid display style.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27pub enum GridStyle {
28    /// No grid (plain white background).
29    None,
30    /// Full grid lines.
31    #[default]
32    Lines,
33    /// Only corner crosses (+).
34    CrossPlus,
35    /// Only corner dots (.).
36    Dots,
37}
38
39impl GridStyle {
40    /// Cycle to the next grid style.
41    pub fn next(self) -> Self {
42        match self {
43            GridStyle::None => GridStyle::Lines,
44            GridStyle::Lines => GridStyle::CrossPlus,
45            GridStyle::CrossPlus => GridStyle::Dots,
46            GridStyle::Dots => GridStyle::None,
47        }
48    }
49
50    /// Get display name for this grid style.
51    pub fn name(self) -> &'static str {
52        match self {
53            GridStyle::None => "None",
54            GridStyle::Lines => "Lines",
55            GridStyle::CrossPlus => "Crosses",
56            GridStyle::Dots => "Dots",
57        }
58    }
59}
60
61/// Angle snap visualization info.
62#[derive(Debug, Clone, Copy)]
63pub struct AngleSnapInfo {
64    /// Start point of the line being drawn.
65    pub start_point: kurbo::Point,
66    /// Current end point (snapped).
67    pub end_point: kurbo::Point,
68    /// Snapped angle in degrees.
69    pub angle_degrees: f64,
70    /// Whether angle snapping is active.
71    pub is_snapped: bool,
72}
73
74/// Context for a single render frame.
75pub struct RenderContext<'a> {
76    /// The canvas to render.
77    pub canvas: &'a Canvas,
78    /// Viewport size in physical pixels.
79    pub viewport_size: Size,
80    /// Device pixel ratio (for HiDPI).
81    pub scale_factor: f64,
82    /// Background color.
83    pub background_color: Color,
84    /// Grid display style.
85    pub grid_style: GridStyle,
86    /// Selection highlight color.
87    pub selection_color: Color,
88    /// Selection rectangle (marquee) in world coordinates.
89    pub selection_rect: Option<Rect>,
90    /// Shape ID currently being edited (skip rendering in build_scene).
91    pub editing_shape_id: Option<drafftink_core::shapes::ShapeId>,
92    /// Snap point for rendering guides (in world coordinates).
93    pub snap_point: Option<kurbo::Point>,
94    /// Angle snap visualization info (for lines/arrows).
95    pub angle_snap_info: Option<AngleSnapInfo>,
96    /// Nearby snap targets to highlight (when shape snapping is enabled).
97    pub nearby_snap_targets: Vec<SnapTarget>,
98}
99
100impl<'a> RenderContext<'a> {
101    /// Create a new render context.
102    pub fn new(canvas: &'a Canvas, viewport_size: Size) -> Self {
103        Self {
104            canvas,
105            viewport_size,
106            scale_factor: 1.0,
107            background_color: Color::from_rgba8(250, 250, 250, 255),
108            grid_style: GridStyle::Lines,
109            selection_color: Color::from_rgba8(59, 130, 246, 255), // Blue
110            selection_rect: None,
111            editing_shape_id: None,
112            snap_point: None,
113            angle_snap_info: None,
114            nearby_snap_targets: Vec::new(),
115        }
116    }
117
118    /// Set the scale factor for HiDPI.
119    pub fn with_scale_factor(mut self, scale_factor: f64) -> Self {
120        self.scale_factor = scale_factor;
121        self
122    }
123
124    /// Set the background color.
125    pub fn with_background(mut self, color: Color) -> Self {
126        self.background_color = color;
127        self
128    }
129
130    /// Set the grid style.
131    pub fn with_grid(mut self, style: GridStyle) -> Self {
132        self.grid_style = style;
133        self
134    }
135
136    /// Set the selection rectangle.
137    pub fn with_selection_rect(mut self, rect: Option<Rect>) -> Self {
138        self.selection_rect = rect;
139        self
140    }
141
142    /// Set the shape ID being edited (will be skipped in build_scene).
143    pub fn with_editing_shape(mut self, shape_id: Option<drafftink_core::shapes::ShapeId>) -> Self {
144        self.editing_shape_id = shape_id;
145        self
146    }
147
148    /// Set the snap point for rendering guides.
149    pub fn with_snap_point(mut self, point: Option<kurbo::Point>) -> Self {
150        self.snap_point = point;
151        self
152    }
153
154    /// Set the angle snap info for rendering angle guides.
155    pub fn with_angle_snap(mut self, info: Option<AngleSnapInfo>) -> Self {
156        self.angle_snap_info = info;
157        self
158    }
159
160    /// Set nearby snap targets to highlight.
161    pub fn with_snap_targets(mut self, targets: Vec<SnapTarget>) -> Self {
162        self.nearby_snap_targets = targets;
163        self
164    }
165}
166
167/// Trait for rendering backends.
168///
169/// Implementations can use Vello, wgpu directly, or other rendering engines.
170pub trait Renderer: Send + Sync {
171    /// Build the scene/command buffer for a frame.
172    ///
173    /// This method is called once per frame and should prepare all drawing commands.
174    fn build_scene(&mut self, ctx: &RenderContext);
175
176    /// Get the background color (for clearing).
177    fn background_color(&self, ctx: &RenderContext) -> Color {
178        ctx.background_color
179    }
180}
181
182/// Helper trait for shape rendering (used internally by renderers).
183#[allow(dead_code)]
184pub trait ShapeRenderer {
185    /// Render a shape with the given transform and style.
186    fn render_shape(&mut self, shape: &Shape, transform: Affine, selected: bool);
187
188    /// Render a grid pattern.
189    fn render_grid(&mut self, viewport: Rect, transform: Affine, grid_size: f64);
190
191    /// Render selection handles for a shape.
192    fn render_selection_handles(&mut self, bounds: Rect, transform: Affine);
193}