gardesk/garshot / 6f5395e

Browse files

annotate: add tool state machine

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
6f5395e01973f5f085cd511744e856ae0ac95ef9
Parents
283a088
Tree
66b0323

1 changed file

StatusFile+-
A garshot/src/annotate/state.rs 214 0
garshot/src/annotate/state.rsadded
@@ -0,0 +1,214 @@
1
+//! Annotation state machine and core types.
2
+
3
+use gartk_core::Color;
4
+
5
+/// Tool type for annotation.
6
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7
+pub enum ToolType {
8
+    #[default]
9
+    Arrow,
10
+    Brush,
11
+    Line,
12
+    Rectangle,
13
+    Ellipse,
14
+    Text,
15
+    Blur,
16
+    Highlight,
17
+}
18
+
19
+impl ToolType {
20
+    /// Get the keyboard shortcut for this tool.
21
+    pub fn shortcut(&self) -> char {
22
+        match self {
23
+            ToolType::Brush => 'b',
24
+            ToolType::Line => 'l',
25
+            ToolType::Arrow => 'a',
26
+            ToolType::Rectangle => 'r',
27
+            ToolType::Ellipse => 'e',
28
+            ToolType::Text => 't',
29
+            ToolType::Blur => 'x',
30
+            ToolType::Highlight => 'h',
31
+        }
32
+    }
33
+
34
+    /// Get tool type from keyboard shortcut.
35
+    pub fn from_shortcut(c: char) -> Option<Self> {
36
+        match c.to_ascii_lowercase() {
37
+            'b' => Some(ToolType::Brush),
38
+            'l' => Some(ToolType::Line),
39
+            'a' => Some(ToolType::Arrow),
40
+            'r' => Some(ToolType::Rectangle),
41
+            'e' => Some(ToolType::Ellipse),
42
+            't' => Some(ToolType::Text),
43
+            'x' => Some(ToolType::Blur),
44
+            'h' => Some(ToolType::Highlight),
45
+            _ => None,
46
+        }
47
+    }
48
+
49
+    /// Get display name for this tool.
50
+    pub fn name(&self) -> &'static str {
51
+        match self {
52
+            ToolType::Brush => "Brush",
53
+            ToolType::Line => "Line",
54
+            ToolType::Arrow => "Arrow",
55
+            ToolType::Rectangle => "Rectangle",
56
+            ToolType::Ellipse => "Ellipse",
57
+            ToolType::Text => "Text",
58
+            ToolType::Blur => "Blur",
59
+            ToolType::Highlight => "Highlight",
60
+        }
61
+    }
62
+
63
+    /// Get all tool types in order.
64
+    pub fn all() -> &'static [ToolType] {
65
+        &[
66
+            ToolType::Brush,
67
+            ToolType::Line,
68
+            ToolType::Arrow,
69
+            ToolType::Rectangle,
70
+            ToolType::Ellipse,
71
+            ToolType::Text,
72
+            ToolType::Blur,
73
+            ToolType::Highlight,
74
+        ]
75
+    }
76
+}
77
+
78
+/// Tool properties for drawing.
79
+#[derive(Debug, Clone)]
80
+pub struct ToolProperties {
81
+    /// Stroke/fill color.
82
+    pub color: Color,
83
+    /// Line width in pixels.
84
+    pub line_width: f64,
85
+    /// Whether to fill shapes (vs stroke only).
86
+    pub fill: bool,
87
+    /// Blur radius for blur tool.
88
+    pub blur_radius: usize,
89
+    /// Font size for text tool.
90
+    pub font_size: f64,
91
+}
92
+
93
+impl Default for ToolProperties {
94
+    fn default() -> Self {
95
+        Self {
96
+            color: Color::new(1.0, 0.4, 0.0, 1.0), // Orange (#ff6600)
97
+            line_width: 3.0,
98
+            fill: false,
99
+            blur_radius: 15,
100
+            font_size: 16.0,
101
+        }
102
+    }
103
+}
104
+
105
+/// Preset colors for quick selection (keys 1-9).
106
+const PRESET_COLORS: [Color; 9] = [
107
+    Color::new(1.0, 0.0, 0.0, 1.0),   // 1: Red
108
+    Color::new(1.0, 0.4, 0.0, 1.0),   // 2: Orange
109
+    Color::new(1.0, 1.0, 0.0, 1.0),   // 3: Yellow
110
+    Color::new(0.0, 1.0, 0.0, 1.0),   // 4: Green
111
+    Color::new(0.0, 1.0, 1.0, 1.0),   // 5: Cyan
112
+    Color::new(0.0, 0.0, 1.0, 1.0),   // 6: Blue
113
+    Color::new(1.0, 0.0, 1.0, 1.0),   // 7: Magenta
114
+    Color::new(1.0, 1.0, 1.0, 1.0),   // 8: White
115
+    Color::new(0.0, 0.0, 0.0, 1.0),   // 9: Black
116
+];
117
+
118
+impl ToolProperties {
119
+    /// Preset colors for quick selection (keys 1-9).
120
+    pub fn preset_colors() -> &'static [Color] {
121
+        &PRESET_COLORS
122
+    }
123
+
124
+    /// Increase line width.
125
+    pub fn increase_line_width(&mut self) {
126
+        self.line_width = (self.line_width + 1.0).min(50.0);
127
+    }
128
+
129
+    /// Decrease line width.
130
+    pub fn decrease_line_width(&mut self) {
131
+        self.line_width = (self.line_width - 1.0).max(1.0);
132
+    }
133
+}
134
+
135
+/// Result of annotation mode.
136
+#[derive(Debug)]
137
+pub enum AnnotationResult {
138
+    /// User saved the annotated image.
139
+    Save {
140
+        /// Annotated image data (RGBA).
141
+        data: Vec<u8>,
142
+        /// Image width.
143
+        width: u32,
144
+        /// Image height.
145
+        height: u32,
146
+    },
147
+    /// User cancelled annotation.
148
+    Cancel,
149
+}
150
+
151
+/// Main annotation state machine.
152
+pub struct AnnotationState {
153
+    /// Current tool.
154
+    pub current_tool: ToolType,
155
+    /// Tool properties.
156
+    pub properties: ToolProperties,
157
+    /// Whether annotation is finished.
158
+    finished: bool,
159
+    /// Result when finished.
160
+    result: Option<AnnotationResult>,
161
+}
162
+
163
+impl AnnotationState {
164
+    /// Create new annotation state.
165
+    pub fn new() -> Self {
166
+        Self {
167
+            current_tool: ToolType::Arrow,
168
+            properties: ToolProperties::default(),
169
+            finished: false,
170
+            result: None,
171
+        }
172
+    }
173
+
174
+    /// Check if annotation mode is finished.
175
+    pub fn is_finished(&self) -> bool {
176
+        self.finished
177
+    }
178
+
179
+    /// Get the result (consumes the state).
180
+    pub fn take_result(&mut self) -> Option<AnnotationResult> {
181
+        self.result.take()
182
+    }
183
+
184
+    /// Select a tool.
185
+    pub fn select_tool(&mut self, tool: ToolType) {
186
+        self.current_tool = tool;
187
+    }
188
+
189
+    /// Set color from preset index (0-8).
190
+    pub fn set_color_preset(&mut self, index: usize) {
191
+        let colors = ToolProperties::preset_colors();
192
+        if index < colors.len() {
193
+            self.properties.color = colors[index];
194
+        }
195
+    }
196
+
197
+    /// Mark as finished with save result.
198
+    pub fn finish_save(&mut self, data: Vec<u8>, width: u32, height: u32) {
199
+        self.finished = true;
200
+        self.result = Some(AnnotationResult::Save { data, width, height });
201
+    }
202
+
203
+    /// Mark as finished with cancel result.
204
+    pub fn finish_cancel(&mut self) {
205
+        self.finished = true;
206
+        self.result = Some(AnnotationResult::Cancel);
207
+    }
208
+}
209
+
210
+impl Default for AnnotationState {
211
+    fn default() -> Self {
212
+        Self::new()
213
+    }
214
+}