@@ -25,6 +25,7 @@ pub struct Path {
25
25
#[ impl_message( Message , ToolMessage , Path ) ]
26
26
#[ derive( PartialEq , Clone , Debug , Hash , Serialize , Deserialize ) ]
27
27
pub enum PathMessage {
28
+ MouseDown ,
28
29
RedrawOverlay ,
29
30
Abort ,
30
31
}
@@ -33,12 +34,15 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Path {
33
34
fn process_action ( & mut self , action : ToolMessage , data : ToolActionHandlerData < ' a > , responses : & mut VecDeque < Message > ) {
34
35
self . fsm_state = self . fsm_state . transition ( action, data. 0 , data. 1 , & mut self . data , data. 2 , responses) ;
35
36
}
36
- fn actions ( & self ) -> ActionList {
37
- use PathToolFsmState :: * ;
38
- match self . fsm_state {
39
- Ready => actions ! ( PathMessageDiscriminant ; ) ,
40
- }
41
- }
37
+
38
+ // different actions depending on state may be wanted:
39
+ // fn actions(&self) -> ActionList {
40
+ // use PathToolFsmState::*;
41
+ // match self.fsm_state {
42
+ // Ready => actions!(PathMessageDiscriminant; MouseDown),
43
+ // }
44
+ // }
45
+ advertise_actions ! ( PathMessageDiscriminant ; MouseDown ) ;
42
46
}
43
47
44
48
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -71,7 +75,7 @@ impl Fsm for PathToolFsmState {
71
75
document : & DocumentMessageHandler ,
72
76
_tool_data : & DocumentToolData ,
73
77
data : & mut Self :: ToolData ,
74
- _input : & InputPreprocessor ,
78
+ input : & InputPreprocessor ,
75
79
responses : & mut VecDeque < Message > ,
76
80
) -> Self {
77
81
if let ToolMessage :: Path ( event) = event {
@@ -141,30 +145,28 @@ impl Fsm for PathToolFsmState {
141
145
142
146
// Draw the draggable square points on the end of every line segment or bezier curve segment
143
147
for anchor in anchors {
144
- let marker = data. anchor_marker_pool [ anchor_i] . clone ( ) ;
145
-
146
148
let scale = DVec2 :: splat ( VECTOR_MANIPULATOR_ANCHOR_MARKER_SIZE ) ;
147
149
let angle = 0. ;
148
150
let translation = ( anchor - ( scale / 2. ) + BIAS ) . round ( ) ;
149
151
let transform = DAffine2 :: from_scale_angle_translation ( scale, angle, translation) . to_cols_array ( ) ;
150
152
153
+ let marker = & data. anchor_marker_pool [ anchor_i] ;
151
154
responses. push_back ( Operation :: SetLayerTransformInViewport { path : marker. clone ( ) , transform } . into ( ) ) ;
152
- responses. push_back ( Operation :: SetLayerVisibility { path : marker, visible : true } . into ( ) ) ;
153
-
155
+ responses. push_back ( Operation :: SetLayerVisibility { path : marker. clone ( ) , visible : true } . into ( ) ) ;
154
156
anchor_i += 1 ;
155
157
}
156
158
157
159
// Draw the draggable handle for cubic and quadratic bezier segments
158
160
for handle in handles {
159
- let marker = data. handle_marker_pool [ handle_i] . clone ( ) ;
161
+ let marker = & data. handle_marker_pool [ handle_i] ;
160
162
161
163
let scale = DVec2 :: splat ( VECTOR_MANIPULATOR_ANCHOR_MARKER_SIZE ) ;
162
164
let angle = 0. ;
163
165
let translation = ( handle - ( scale / 2. ) + BIAS ) . round ( ) ;
164
166
let transform = DAffine2 :: from_scale_angle_translation ( scale, angle, translation) . to_cols_array ( ) ;
165
167
166
168
responses. push_back ( Operation :: SetLayerTransformInViewport { path : marker. clone ( ) , transform } . into ( ) ) ;
167
- responses. push_back ( Operation :: SetLayerVisibility { path : marker, visible : true } . into ( ) ) ;
169
+ responses. push_back ( Operation :: SetLayerVisibility { path : marker. clone ( ) , visible : true } . into ( ) ) ;
168
170
169
171
handle_i += 1 ;
170
172
}
@@ -191,6 +193,114 @@ impl Fsm for PathToolFsmState {
191
193
192
194
self
193
195
}
196
+ ( _, MouseDown ) => {
197
+ // todo: DRY refactor (this arm is very similar to the (_, RedrawOverlay) arm)
198
+ // WIP: selecting control point working
199
+ // next: correctly modifying path
200
+ // to test: E, make ellipse, A, click control point, see it change color
201
+ // todo: use cargo clippy?
202
+
203
+ let mouse_pos = input. mouse . position ;
204
+ let mut points = Vec :: new ( ) ;
205
+
206
+ let ( mut anchor_i, mut handle_i, _line_i, mut shape_i) = ( 0 , 0 , 0 , 0 ) ;
207
+ let shapes_to_draw = document. selected_layers_vector_points ( ) ;
208
+ let ( total_anchors, total_handles, _total_anchor_handle_lines) = calculate_total_overlays_per_type ( & shapes_to_draw) ;
209
+ grow_overlay_pool_entries ( & mut data. anchor_marker_pool , total_anchors, add_anchor_marker, responses) ;
210
+ grow_overlay_pool_entries ( & mut data. handle_marker_pool , total_handles, add_handle_marker, responses) ;
211
+
212
+ #[ derive( Debug ) ]
213
+ enum PointRef {
214
+ Anchor ( usize ) ,
215
+ Handle ( usize ) ,
216
+ }
217
+ #[ derive( Debug ) ]
218
+ struct Point ( DVec2 , PointRef , f64 ) ;
219
+
220
+ // todo: use const?
221
+ // use crate::consts::SELECTION_TOLERANCE;
222
+ // let tolerance = DVec2::splat(SELECTION_TOLERANCE);
223
+ // let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
224
+ let select_threshold = 6. ;
225
+ let select_threshold_squared = select_threshold * select_threshold;
226
+
227
+ for shape_to_draw in & shapes_to_draw {
228
+ let shape_layer_path = & data. shape_outline_pool [ shape_i] ;
229
+ shape_i += 1 ;
230
+
231
+ let bez = {
232
+ use kurbo:: PathEl ;
233
+ let mut bez: Vec < _ > = shape_to_draw. path . clone ( ) . into_iter ( ) . collect ( ) ;
234
+ for a in & mut bez {
235
+ match a {
236
+ PathEl :: LineTo ( p) => {
237
+ p. x += 5. ;
238
+ }
239
+ _ => { }
240
+ }
241
+ }
242
+ let bez: BezPath = bez. into_iter ( ) . collect ( ) ;
243
+ bez
244
+ } ;
245
+
246
+ // todo: change path correctly
247
+ responses. push_back (
248
+ Operation :: SetShapePathInViewport {
249
+ path : shape_layer_path. clone ( ) ,
250
+ bez_path : bez,
251
+ transform : shape_to_draw. transform . to_cols_array ( ) ,
252
+ }
253
+ . into ( ) ,
254
+ ) ;
255
+
256
+ for segment in & shape_to_draw. segments {
257
+ // TODO: We draw each anchor point twice because segment has it on both ends, fix this
258
+ // TODO: DRY, see above
259
+ let ( anchors, handles, _anchor_handle_lines) = match segment {
260
+ VectorManipulatorSegment :: Line ( a1, a2) => ( vec ! [ * a1, * a2] , vec ! [ ] , vec ! [ ] ) ,
261
+ VectorManipulatorSegment :: Quad ( a1, h1, a2) => ( vec ! [ * a1, * a2] , vec ! [ * h1] , vec ! [ ( * h1, * a1) ] ) ,
262
+ VectorManipulatorSegment :: Cubic ( a1, h1, h2, a2) => ( vec ! [ * a1, * a2] , vec ! [ * h1, * h2] , vec ! [ ( * h1, * a1) , ( * h2, * a2) ] ) ,
263
+ } ;
264
+
265
+ for anchor in anchors {
266
+ let d2 = mouse_pos. distance_squared ( anchor. into ( ) ) ;
267
+ if d2 < select_threshold_squared {
268
+ points. push ( Point ( anchor. clone ( ) , PointRef :: Anchor ( anchor_i) , d2) ) ;
269
+ }
270
+ anchor_i += 1 ;
271
+ }
272
+ for handle in handles {
273
+ let d2 = mouse_pos. distance_squared ( handle. into ( ) ) ;
274
+ if d2 < select_threshold_squared {
275
+ points. push ( Point ( handle. clone ( ) , PointRef :: Handle ( handle_i) , d2) ) ;
276
+ }
277
+ handle_i += 1 ;
278
+ }
279
+ }
280
+ }
281
+
282
+ points. sort_by ( |a, b| a. 2 . partial_cmp ( & b. 2 ) . unwrap_or ( std:: cmp:: Ordering :: Equal ) ) ;
283
+ let closest_point_within_click_threshold = points. first ( ) ;
284
+ // log::debug!("PATH TOOL: {:?} {:?}", mouse_pos, points);
285
+
286
+ if let Some ( point) = closest_point_within_click_threshold {
287
+ let path = match point. 1 {
288
+ PointRef :: Anchor ( i) => data. anchor_marker_pool [ i] . clone ( ) ,
289
+ PointRef :: Handle ( i) => data. handle_marker_pool [ i] . clone ( ) ,
290
+ } ;
291
+ // todo: use Operation::SetShapePathInViewport instead
292
+ // currently using SetLayerFill just to show some effect
293
+ responses. push_back (
294
+ Operation :: SetLayerFill {
295
+ path,
296
+ color : Color :: from_rgb8 ( if mouse_pos. x > 500. { 0 } else { 255 } , if mouse_pos. x > 500. { 255 } else { 0 } , 0 ) ,
297
+ }
298
+ . into ( ) ,
299
+ ) ;
300
+ }
301
+
302
+ self
303
+ }
194
304
( _, Abort ) => {
195
305
// Destory the overlay layer pools
196
306
while let Some ( layer) = data. anchor_marker_pool . pop ( ) {
0 commit comments