1
- use crate :: consts:: DRAG_THRESHOLD ;
2
1
use crate :: document:: DocumentMessageHandler ;
3
2
use crate :: frontend:: utility_types:: MouseCursorIcon ;
4
3
use crate :: input:: keyboard:: { Key , MouseMotion } ;
@@ -8,11 +7,14 @@ use crate::message_prelude::*;
8
7
use crate :: misc:: { HintData , HintGroup , HintInfo , KeysGroup } ;
9
8
use crate :: viewport_tools:: snapping:: SnapHandler ;
10
9
use crate :: viewport_tools:: tool:: { DocumentToolData , Fsm , ToolActionHandlerData } ;
10
+ use crate :: viewport_tools:: vector_editor:: shape_editor:: ShapeEditor ;
11
+ use crate :: viewport_tools:: vector_editor:: vector_shape:: VectorShape ;
11
12
12
13
use graphene:: layers:: style;
13
14
use graphene:: Operation ;
14
15
15
16
use glam:: { DAffine2 , DVec2 } ;
17
+ use kurbo:: { PathEl , Point } ;
16
18
use serde:: { Deserialize , Serialize } ;
17
19
18
20
#[ derive( Default ) ]
@@ -38,6 +40,8 @@ impl Default for PenOptions {
38
40
pub enum PenMessage {
39
41
// Standard messages
40
42
#[ remain:: unsorted]
43
+ DocumentIsDirty ,
44
+ #[ remain:: unsorted]
41
45
Abort ,
42
46
43
47
// Tool-specific messages
@@ -111,7 +115,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Pen {
111
115
112
116
match self . fsm_state {
113
117
Ready => actions ! ( PenMessageDiscriminant ; Undo , DragStart , DragStop , Confirm , Abort ) ,
114
- Drawing => actions ! ( PenMessageDiscriminant ; DragStop , PointerMove , Confirm , Abort ) ,
118
+ Drawing => actions ! ( PenMessageDiscriminant ; DragStart , DragStop , PointerMove , Confirm , Abort ) ,
115
119
}
116
120
}
117
121
}
@@ -123,11 +127,12 @@ impl Default for PenToolFsmState {
123
127
}
124
128
#[ derive( Clone , Debug , Default ) ]
125
129
struct PenToolData {
126
- points : Vec < DVec2 > ,
127
- next_point : DVec2 ,
128
130
weight : u32 ,
129
131
path : Option < Vec < LayerId > > ,
132
+ curve_shape : VectorShape ,
133
+ bez_path : Vec < PathEl > ,
130
134
snap_handler : SnapHandler ,
135
+ shape_editor : ShapeEditor ,
131
136
}
132
137
133
138
impl Fsm for PenToolFsmState {
@@ -151,67 +156,92 @@ impl Fsm for PenToolFsmState {
151
156
152
157
if let ToolMessage :: Pen ( event) = event {
153
158
match ( self , event) {
159
+ ( _, DocumentIsDirty ) => {
160
+ data. shape_editor . update_shapes ( document, responses) ;
161
+ self
162
+ }
154
163
( Ready , DragStart ) => {
155
164
responses. push_back ( DocumentMessage :: StartTransaction . into ( ) ) ;
156
165
responses. push_back ( DocumentMessage :: DeselectAllLayers . into ( ) ) ;
157
- data. path = Some ( document. get_path_for_new_layer ( ) ) ;
158
166
167
+ // Create a new layer and prep snap system
168
+ data. path = Some ( document. get_path_for_new_layer ( ) ) ;
159
169
data. snap_handler . start_snap ( document, document. bounding_boxes ( None , None ) , true , true ) ;
160
170
let snapped_position = data. snap_handler . snap_position ( responses, input. viewport_bounds . size ( ) , document, input. mouse . position ) ;
161
171
162
- let pos = transform. inverse ( ) . transform_point2 ( snapped_position) ;
163
-
164
- data. points . push ( pos) ;
165
- data. next_point = pos;
166
-
172
+ // Get the position and set properties
173
+ let start_position = transform. inverse ( ) . transform_point2 ( snapped_position) ;
167
174
data. weight = tool_options. line_weight ;
168
175
169
- responses. push_back ( add_polyline ( data, tool_data, true ) ) ;
176
+ // Create the initial shape with a bez_path (only contains a moveto initially)
177
+ if let Some ( layer_path) = & data. path {
178
+ data. bez_path = start_bez_path ( start_position) ;
179
+ responses. push_back (
180
+ Operation :: AddShape {
181
+ path : layer_path. clone ( ) ,
182
+ transform : transform. to_cols_array ( ) ,
183
+ insert_index : -1 ,
184
+ bez_path : data. bez_path . clone ( ) . into_iter ( ) . collect ( ) ,
185
+ style : style:: PathStyle :: new ( Some ( style:: Stroke :: new ( tool_data. primary_color , data. weight as f32 ) ) , None ) ,
186
+ closed : false ,
187
+ }
188
+ . into ( ) ,
189
+ ) ;
190
+ }
170
191
192
+ add_to_curve ( data, input, transform, document, responses) ;
193
+ Drawing
194
+ }
195
+ ( Drawing , DragStart ) => {
196
+ add_to_curve ( data, input, transform, document, responses) ;
171
197
Drawing
172
198
}
173
199
( Drawing , DragStop ) => {
174
- let snapped_position = data . snap_handler . snap_position ( responses , input . viewport_bounds . size ( ) , document , input . mouse . position ) ;
175
- let pos = transform . inverse ( ) . transform_point2 ( snapped_position ) ;
200
+ // Deselect everything (this means we are no longer dragging the handle)
201
+ data . shape_editor . deselect_all ( responses ) ;
176
202
177
- if let Some ( last_pos) = data. points . last ( ) {
178
- if last_pos. distance ( pos) > DRAG_THRESHOLD {
179
- data. points . push ( pos) ;
180
- data. next_point = pos;
181
- }
203
+ // Reselect the last point
204
+ if let Some ( last_anchor) = data. shape_editor . select_last_anchor ( ) {
205
+ last_anchor. select_point ( 0 , true , responses) ;
182
206
}
183
207
184
- responses. push_back ( remove_preview ( data) ) ;
185
- responses. push_back ( add_polyline ( data, tool_data, true ) ) ;
186
-
187
208
Drawing
188
209
}
189
210
( Drawing , PointerMove ) => {
190
211
let snapped_position = data. snap_handler . snap_position ( responses, input. viewport_bounds . size ( ) , document, input. mouse . position ) ;
191
- let pos = transform. inverse ( ) . transform_point2 ( snapped_position) ;
192
- data. next_point = pos;
193
-
194
- responses. push_back ( remove_preview ( data) ) ;
195
- responses. push_back ( add_polyline ( data, tool_data, true ) ) ;
212
+ //data.shape_editor.update_shapes(document, responses);
213
+ data. shape_editor . move_selected_points ( snapped_position, false , responses) ;
196
214
197
215
Drawing
198
216
}
199
217
( Drawing , Confirm ) | ( Drawing , Abort ) => {
200
- if data. points . len ( ) >= 2 {
218
+ // Add a curve to the path
219
+ if let Some ( layer_path) = & data. path {
220
+ remove_curve_from_end ( & mut data. bez_path ) ;
221
+ responses. push_back ( apply_bez_path ( layer_path. clone ( ) , data. bez_path . clone ( ) , transform) ) ;
222
+ }
223
+
224
+ // Cleanup, we are either canceling or finished drawing
225
+ if data. bez_path . len ( ) >= 2 {
201
226
responses. push_back ( DocumentMessage :: DeselectAllLayers . into ( ) ) ;
202
- responses. push_back ( remove_preview ( data) ) ;
203
- responses. push_back ( add_polyline ( data, tool_data, false ) ) ;
204
227
responses. push_back ( DocumentMessage :: CommitTransaction . into ( ) ) ;
205
228
} else {
206
229
responses. push_back ( DocumentMessage :: AbortTransaction . into ( ) ) ;
207
230
}
208
231
232
+ data. shape_editor . remove_overlays ( responses) ;
233
+ data. shape_editor . clear_shapes_to_modify ( ) ;
234
+
209
235
data. path = None ;
210
- data. points . clear ( ) ;
211
236
data. snap_handler . cleanup ( responses) ;
212
237
213
238
Ready
214
239
}
240
+ ( _, Abort ) => {
241
+ data. shape_editor . remove_overlays ( responses) ;
242
+ data. shape_editor . clear_shapes_to_modify ( ) ;
243
+ Ready
244
+ }
215
245
_ => self ,
216
246
}
217
247
} else {
@@ -251,22 +281,70 @@ impl Fsm for PenToolFsmState {
251
281
}
252
282
}
253
283
254
- fn remove_preview ( data : & PenToolData ) -> Message {
255
- Operation :: DeleteLayer { path : data. path . clone ( ) . unwrap ( ) } . into ( )
256
- }
284
+ // Add to the curve and select the second anchor of the last point and the newly added anchor point
285
+ fn add_to_curve ( data : & mut PenToolData , input : & InputPreprocessorMessageHandler , transform : DAffine2 , document : & DocumentMessageHandler , responses : & mut VecDeque < Message > ) {
286
+ // We need to make sure we have the most up-to-date bez_path
287
+ // Would like to remove this hack eventually
288
+ if !data. shape_editor . shapes_to_modify . is_empty ( ) {
289
+ // Hacky way of saving the curve changes
290
+ data. bez_path = data. shape_editor . shapes_to_modify [ 0 ] . bez_path . elements ( ) . to_vec ( ) ;
291
+ }
292
+
293
+ // Setup our position params
294
+ let snapped_position = data. snap_handler . snap_position ( responses, input. viewport_bounds . size ( ) , document, input. mouse . position ) ;
295
+ let position = transform. inverse ( ) . transform_point2 ( snapped_position) ;
296
+
297
+ // Add a curve to the path
298
+ if let Some ( layer_path) = & data. path {
299
+ add_curve_to_end ( position, & mut data. bez_path ) ;
300
+ responses. push_back ( apply_bez_path ( layer_path. clone ( ) , data. bez_path . clone ( ) , transform) ) ;
301
+
302
+ // Clear previous overlays
303
+ data. shape_editor . remove_overlays ( responses) ;
304
+
305
+ // Create a new shape from the updated bez_path
306
+ let bez_path = data. bez_path . clone ( ) . into_iter ( ) . collect ( ) ;
307
+ data. curve_shape = VectorShape :: new ( layer_path. to_vec ( ) , transform, & bez_path, false , responses) ;
308
+ data. shape_editor . set_shapes_to_modify ( vec ! [ data. curve_shape. clone( ) ] ) ;
309
+
310
+ // Select the second to last segment's handle
311
+ data. shape_editor . set_shape_selected ( 0 ) ;
312
+ let handle_element = data. shape_editor . select_nth_anchor ( 0 , -2 ) ;
313
+ handle_element. select_point ( 2 , true , responses) ;
257
314
258
- fn add_polyline ( data : & PenToolData , tool_data : & DocumentToolData , show_preview : bool ) -> Message {
259
- let mut points: Vec < ( f64 , f64 ) > = data. points . iter ( ) . map ( |p| ( p. x , p. y ) ) . collect ( ) ;
260
- if show_preview {
261
- points. push ( ( data. next_point . x , data. next_point . y ) )
315
+ // Select the last segment's anchor point
316
+ if let Some ( last_anchor) = data. shape_editor . select_last_anchor ( ) {
317
+ last_anchor. select_point ( 0 , true , responses) ;
318
+ }
319
+ data. shape_editor . set_selected_mirror_options ( true , true ) ;
262
320
}
321
+ }
322
+
323
+ // Create the initial moveto for the bez_path
324
+ fn start_bez_path ( start_position : DVec2 ) -> Vec < PathEl > {
325
+ vec ! [ PathEl :: MoveTo ( Point {
326
+ x: start_position. x,
327
+ y: start_position. y,
328
+ } ) ]
329
+ }
330
+
331
+ // Add a curve to the bez_path
332
+ fn add_curve_to_end ( point : DVec2 , bez_path : & mut Vec < PathEl > ) {
333
+ let point = Point { x : point. x , y : point. y } ;
334
+ bez_path. push ( PathEl :: CurveTo ( point, point, point) ) ;
335
+ }
336
+
337
+ // Add a curve to the bez_path
338
+ fn remove_curve_from_end ( bez_path : & mut Vec < PathEl > ) {
339
+ bez_path. pop ( ) ;
340
+ }
263
341
264
- Operation :: AddPolyline {
265
- path : data . path . clone ( ) . unwrap ( ) ,
266
- insert_index : - 1 ,
267
- transform : DAffine2 :: IDENTITY . to_cols_array ( ) ,
268
- points ,
269
- style : style :: PathStyle :: new ( Some ( style :: Stroke :: new ( tool_data . primary_color , data . weight as f32 ) ) , None ) ,
342
+ // Apply the bez_path to the shape in the viewport
343
+ fn apply_bez_path ( layer_path : Vec < LayerId > , bez_path : Vec < PathEl > , transform : DAffine2 ) -> Message {
344
+ Operation :: SetShapePathInViewport {
345
+ path : layer_path ,
346
+ bez_path : bez_path . into_iter ( ) . collect ( ) ,
347
+ transform : transform . to_cols_array ( ) ,
270
348
}
271
349
. into ( )
272
350
}
0 commit comments