Blender V5.0
wm_gesture_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14#include "MEM_guardedalloc.h"
15
16#include <fmt/format.h>
17
18#include <algorithm>
19
20#include "DNA_space_types.h"
22
23#include "BLI_math_rotation.h"
24#include "BLI_math_vector.h"
25#include "BLI_math_vector.hh"
27#include "BLI_rect.h"
28
29#include "BKE_context.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "wm.hh"
35#include "wm_event_types.hh"
36
37#include "ED_screen.hh"
38#include "ED_select_utils.hh"
39
40#include "RNA_access.hh"
41
42using blender::Array;
43using blender::float2;
44using blender::int2;
45
46/* -------------------------------------------------------------------- */
56
58{
59 wmWindow *win = CTX_wm_window(C);
60 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
61
62 WM_gesture_end(win, gesture); /* Frees gesture itself, and unregisters from window. */
63 op->customdata = nullptr;
64
66
67 if (RNA_struct_find_property(op->ptr, "cursor")) {
69 }
70}
71
72static void gesture_modal_state_to_operator(wmOperator *op, int modal_state)
73{
74 PropertyRNA *prop;
75
76 switch (modal_state) {
79 if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
80 RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_DESELECT));
81 }
82 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
84 op->ptr, prop, (modal_state == GESTURE_MODAL_DESELECT) ? SEL_OP_SUB : SEL_OP_ADD);
85 }
86 break;
89 if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
90 RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_OUT));
91 }
92 break;
93 }
94}
95
97{
98 PropertyRNA *prop;
99
100 if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
101 if (RNA_property_is_set(op->ptr, prop)) {
102 return RNA_property_boolean_get(op->ptr, prop) ? GESTURE_MODAL_DESELECT :
104 }
105 }
106 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
107 if (RNA_property_is_set(op->ptr, prop)) {
108 return RNA_property_enum_get(op->ptr, prop) == SEL_OP_SUB ? GESTURE_MODAL_DESELECT :
110 }
111 }
112 if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
113 if (RNA_property_is_set(op->ptr, prop)) {
115 }
116 }
117 return GESTURE_MODAL_NOP;
118}
119
121
122/* -------------------------------------------------------------------- */
132
134{
135 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
136 const rcti *rect = static_cast<const rcti *>(gesture->customdata);
137
138 if (rect->xmin == rect->xmax || rect->ymin == rect->ymax) {
139 return false;
140 }
141
142 /* Operator arguments and storage. */
143 RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
144 RNA_int_set(op->ptr, "ymin", min_ii(rect->ymin, rect->ymax));
145 RNA_int_set(op->ptr, "xmax", max_ii(rect->xmin, rect->xmax));
146 RNA_int_set(op->ptr, "ymax", max_ii(rect->ymin, rect->ymax));
147
148 return true;
149}
150
152{
153 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
154
155 if (!gesture_box_apply_rect(op)) {
156 return false;
157 }
158
159 if (gesture->wait_for_input) {
161 }
162
163 const wmOperatorStatus retval = op->type->exec(C, op);
164 OPERATOR_RETVAL_CHECK(retval);
165
166 return (retval & OPERATOR_FINISHED) ? true : false;
167}
168
170{
171 wmWindow *win = CTX_wm_window(C);
172 const ARegion *region = CTX_wm_region(C);
173 const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
174 RNA_boolean_get(op->ptr, "wait_for_input");
175
176 if (wait_for_input) {
177 op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT);
178 }
179 else {
180 op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_RECT);
181 }
182
183 {
184 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
185 gesture->wait_for_input = wait_for_input;
186 }
187
188 /* Add modal handler. */
190
192
194}
195
197{
198 wmWindow *win = CTX_wm_window(C);
199 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
200 rcti *rect = static_cast<rcti *>(gesture->customdata);
201
202 if (event->type == EVT_MODAL_MAP) {
203 switch (event->val) {
204 case GESTURE_MODAL_MOVE: {
205 gesture->move = !gesture->move;
206 break;
207 }
208 case GESTURE_MODAL_BEGIN: {
209 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
210 gesture->is_active = true;
212 }
213 break;
214 }
217 case GESTURE_MODAL_IN:
218 case GESTURE_MODAL_OUT: {
219 if (gesture->wait_for_input) {
220 gesture->modal_state = event->val;
221 }
222 if (gesture_box_apply(C, op)) {
223 gesture_modal_end(C, op);
224 return OPERATOR_FINISHED;
225 }
226 gesture_modal_end(C, op);
227 return OPERATOR_CANCELLED;
228 }
230 gesture_modal_end(C, op);
231 return OPERATOR_CANCELLED;
232 }
233 }
234 }
235 else {
236 switch (event->type) {
237 case MOUSEMOVE: {
238 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
239 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
240 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
241 }
242 else if (gesture->move) {
244 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
245 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
246 }
247 else {
248 rect->xmax = event->xy[0] - gesture->winrct.xmin;
249 rect->ymax = event->xy[1] - gesture->winrct.ymin;
250 }
252
254
255 break;
256 }
257#ifdef WITH_INPUT_NDOF
258 case NDOF_MOTION: {
260 }
261#endif
262
263 default:
264#if 0 /* This allows view navigation, keep disabled as it's too unpredictable. */
266#endif
267 break;
268 }
269 }
270
271 gesture->is_active_prev = gesture->is_active;
273}
274
279
281
282/* -------------------------------------------------------------------- */
290
291static void gesture_circle_apply(bContext *C, wmOperator *op);
292
294{
295 wmWindow *win = CTX_wm_window(C);
296 const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
297 RNA_boolean_get(op->ptr, "wait_for_input");
298
300 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
301 rcti *rect = static_cast<rcti *>(gesture->customdata);
302
303 /* Default or previously stored value. */
304 rect->xmax = RNA_int_get(op->ptr, "radius");
305
306 gesture->wait_for_input = wait_for_input;
307
308 /* Starting with the mode starts immediately,
309 * like having 'wait_for_input' disabled (some tools use this). */
310 if (gesture->wait_for_input == false) {
311 gesture->is_active = true;
313 gesture->is_active_prev = true;
314 }
315
316 /* Add modal handler. */
318
320
322}
323
325{
326 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
327 const rcti *rect = static_cast<const rcti *>(gesture->customdata);
328
329 if (gesture->wait_for_input && (gesture->modal_state == GESTURE_MODAL_NOP)) {
330 return;
331 }
332
333 /* Operator arguments and storage. */
334 RNA_int_set(op->ptr, "x", rect->xmin);
335 RNA_int_set(op->ptr, "y", rect->ymin);
336 RNA_int_set(op->ptr, "radius", rect->xmax);
337
338 /* When 'wait_for_input' is false,
339 * use properties to get the selection state (typically tool settings).
340 * This is done so executing as a mode can select & de-select, see: #58594. */
341 if (gesture->wait_for_input) {
343 }
344
345 if (op->type->exec) {
346 const wmOperatorStatus retval = op->type->exec(C, op);
347 OPERATOR_RETVAL_CHECK(retval);
348 }
349}
350
352{
353 wmWindow *win = CTX_wm_window(C);
354 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
355 rcti *rect = static_cast<rcti *>(gesture->customdata);
356
357 if (event->type == MOUSEMOVE) {
358
359 rect->xmin = event->xy[0] - gesture->winrct.xmin;
360 rect->ymin = event->xy[1] - gesture->winrct.ymin;
361
363
364 if (gesture->is_active) {
366 }
367 }
368 else if (event->type == EVT_MODAL_MAP) {
369 bool is_circle_size = false;
370 bool is_finished = false;
371 float fac;
372
373 switch (event->val) {
375 fac = 0.3f * (event->xy[1] - event->prev_xy[1]);
376 if (fac > 0) {
377 rect->xmax += ceil(fac);
378 }
379 else {
380 rect->xmax += floor(fac);
381 }
382 rect->xmax = std::max(rect->xmax, 1);
383 is_circle_size = true;
384 break;
386 rect->xmax += 2 + rect->xmax / 10;
387 is_circle_size = true;
388 break;
390 rect->xmax -= 2 + rect->xmax / 10;
391 rect->xmax = std::max(rect->xmax, 1);
392 is_circle_size = true;
393 break;
396 case GESTURE_MODAL_NOP: {
397 if (gesture->wait_for_input) {
398 gesture->modal_state = event->val;
399 }
400 if (event->val == GESTURE_MODAL_NOP) {
401 /* Single action, click-drag & release to exit. */
402 if (gesture->wait_for_input == false) {
403 is_finished = true;
404 }
405 }
406 else {
407 /* Apply first click. */
408 gesture->is_active = true;
411 }
412 break;
413 }
416 is_finished = true;
417 }
418
419 if (is_finished) {
420 gesture_modal_end(C, op);
421 return OPERATOR_FINISHED; /* Use finish or we don't get an undo. */
422 }
423
424 if (is_circle_size) {
426
427 /* So next use remembers last seen size, even if we didn't apply it. */
428 RNA_int_set(op->ptr, "radius", rect->xmax);
429 }
430 }
431#ifdef WITH_INPUT_NDOF
432 else if (event->type == NDOF_MOTION) {
434 }
435#endif
436
437#if 0
438 /* Allow view navigation??? */
439 /* NOTE: this gives issues:
440 * 1) other modal ops run on top (box select),
441 * 2) middle-mouse is used now 3) tablet/trackpad? */
442 else {
444 }
445#endif
446
447 gesture->is_active_prev = gesture->is_active;
449}
450
455
456#if 0
457/* Template to copy from. */
458void WM_OT_circle_gesture(wmOperatorType *ot)
459{
460 ot->name = "Circle Gesture";
461 ot->idname = "WM_OT_circle_gesture";
462 ot->description = "Enter rotate mode with a circular gesture";
463
467
468 /* Properties. */
470}
471#endif
472
474
475/* -------------------------------------------------------------------- */
483
485{
486 wmWindow *win = CTX_wm_window(C);
487 PropertyRNA *prop;
488
490 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
491 gesture->use_smooth = RNA_boolean_get(op->ptr, "use_smooth_stroke");
492
493 /* Add modal handler. */
495
497
498 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
500 }
501
503}
504
506{
507 wmWindow *win = CTX_wm_window(C);
508 PropertyRNA *prop;
509
511 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
512 if ((prop = RNA_struct_find_property(op->ptr, "use_smooth_stroke"))) {
513 gesture->use_smooth = RNA_property_boolean_get(op->ptr, prop);
514 }
515
516 /* Add modal handler. */
518
520
521 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
523 }
524
526}
527
529{
531 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
532 PointerRNA itemptr;
533 float loc[2];
534 int i;
535 const float *lasso = static_cast<const float *>(gesture->customdata);
536
537 /* Operator storage as path. */
538
539 RNA_collection_clear(op->ptr, "path");
540 for (i = 0; i < gesture->points; i++, lasso += 2) {
541 loc[0] = lasso[0];
542 loc[1] = lasso[1];
543 RNA_collection_add(op->ptr, "path", &itemptr);
544 RNA_float_set_array(&itemptr, "loc", loc);
545 }
546
547 gesture_modal_end(C, op);
548
549 if (op->type->exec) {
550 retval = op->type->exec(C, op);
551 OPERATOR_RETVAL_CHECK(retval);
552 }
553
554 return retval;
555}
556
558{
559 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
560 const float factor = gesture->use_smooth ? RNA_float_get(op->ptr, "smooth_stroke_factor") : 0.0f;
561 const int radius = gesture->use_smooth ? RNA_int_get(op->ptr, "smooth_stroke_radius") : 0;
562
563 if (event->type == EVT_MODAL_MAP) {
564 switch (event->val) {
565 case GESTURE_MODAL_MOVE: {
566 gesture->move = !gesture->move;
567 break;
568 }
569 }
570 }
571 else {
572 switch (event->type) {
573 case MOUSEMOVE:
574 case INBETWEEN_MOUSEMOVE: {
576 gesture->mval = int2((event->xy[0] - gesture->winrct.xmin),
577 (event->xy[1] - gesture->winrct.ymin));
578
579 if (gesture->points == gesture->points_alloc) {
580 gesture->points_alloc *= 2;
581 gesture->customdata = MEM_reallocN(gesture->customdata,
582 sizeof(float[2]) * gesture->points_alloc);
583 }
584
585 {
586 float (*lasso)[2] = static_cast<float (*)[2]>(gesture->customdata);
587 const float2 current_mouse_position = float2(gesture->mval);
588 const float2 last_position(lasso[gesture->points - 1][0], lasso[gesture->points - 1][1]);
589
590 const float2 delta = current_mouse_position - last_position;
591 const float dist_squared = blender::math::length_squared(delta);
592
593 /* Move the lasso. */
594 if (gesture->move) {
595 for (int i = 0; i < gesture->points; i++) {
596 lasso[i][0] += delta.x;
597 lasso[i][1] += delta.y;
598 }
599 }
600 else if (gesture->use_smooth) {
601 if (dist_squared > square_f(radius)) {
603 current_mouse_position, last_position, factor);
604
605 lasso[gesture->points][0] = result.x;
606 lasso[gesture->points][1] = result.y;
607 gesture->points++;
608 }
609 }
610 else if (dist_squared > pow2f(2.0f * UI_SCALE_FAC)) {
611 /* Make a simple distance check to get a smoother lasso even if smoothing isn't enabled
612 * add only when at least 2 pixels between this and previous location. */
613 lasso[gesture->points][0] = gesture->mval.x;
614 lasso[gesture->points][1] = gesture->mval.y;
615 gesture->points++;
616 }
617 }
618 break;
619 }
620 case LEFTMOUSE:
621 case MIDDLEMOUSE:
622 case RIGHTMOUSE: {
623 if (event->val == KM_RELEASE) { /* Key release. */
624 return gesture_lasso_apply(C, op);
625 }
626 break;
627 }
628 case EVT_ESCKEY: {
629 gesture_modal_end(C, op);
630 return OPERATOR_CANCELLED;
631 }
632 default: {
633 break;
634 }
635 }
636 }
637
638 gesture->is_active_prev = gesture->is_active;
640}
641
643{
644 return WM_gesture_lasso_modal(C, op, event);
645}
646
651
656
658{
659 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
660 BLI_assert(prop != nullptr);
661 if (!prop) {
662 return {};
663 }
664 const int len = RNA_property_collection_length(op->ptr, prop);
665 if (len == 0) {
666 return {};
667 }
668
669 int i = 0;
670 Array<int2> mcoords(len);
671
672 RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
673 float loc[2];
674 RNA_float_get_array(&itemptr, "loc", loc);
675 mcoords[i] = int2(loc[0], loc[1]);
676 i++;
677 }
679
680 return mcoords;
681}
682
683#if 0
684/* Template to copy from. */
685
686static wmOperatorStatus gesture_lasso_exec(bContext *C, wmOperator *op)
687{
688 RNA_BEGIN (op->ptr, itemptr, "path") {
689 float loc[2];
690
691 RNA_float_get_array(&itemptr, "loc", loc);
692 printf("Location: %f %f\n", loc[0], loc[1]);
693 }
694 RNA_END;
695
696 return OPERATOR_FINISHED;
697}
698
699void WM_OT_lasso_gesture(wmOperatorType *ot)
700{
701 PropertyRNA *prop;
702
703 ot->name = "Lasso Gesture";
704 ot->idname = "WM_OT_lasso_gesture";
705 ot->description = "Draw a shape defined by the cursor";
706
707 ot->invoke = WM_gesture_lasso_invoke;
708 ot->modal = WM_gesture_lasso_modal;
709 ot->exec = gesture_lasso_exec;
710
712
714
715 prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
716 RNA_def_property_struct_runtime(ot->srna, prop, &RNA_OperatorMousePath);
717}
718#endif
719
721
722/* -------------------------------------------------------------------- */
730{
731 wmWindow *win = CTX_wm_window(C);
732 PropertyRNA *prop;
733
735
736 /* add modal handler */
738
740
741 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
743 }
744
746}
747
748/* Calculates the number of valid points in a polyline gesture where
749 * a duplicated end point is invalid for submission */
750static int gesture_polyline_valid_points(const wmGesture &wmGesture, const bool is_click_submitted)
751{
753
754 const int num_points = wmGesture.points;
755 if (is_click_submitted) {
756 return num_points;
757 }
758
759 short (*points)[2] = static_cast<short int (*)[2]>(wmGesture.customdata);
760
761 const short prev_x = points[num_points - 1][0];
762 const short prev_y = points[num_points - 1][1];
763
764 return (wmGesture.mval.x == prev_x && wmGesture.mval.y == prev_y) ? num_points : num_points + 1;
765}
766
775static bool gesture_polyline_can_apply(const wmGesture &wmGesture, const bool is_click_submitted)
776{
777 if (wmGesture.points < 2) {
778 return false;
779 }
780
781 const int valid_points = gesture_polyline_valid_points(wmGesture, is_click_submitted);
782 if (valid_points <= 2) {
783 return false;
784 }
785
786 return true;
787}
788
790 wmOperator *op,
791 const bool is_click_submitted)
792{
793 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
794 BLI_assert(gesture_polyline_can_apply(*gesture, is_click_submitted));
795
796 const int valid_points = gesture_polyline_valid_points(*gesture, is_click_submitted);
797 const short *border = static_cast<const short int *>(gesture->customdata);
798
799 PointerRNA itemptr;
800 float loc[2];
801 RNA_collection_clear(op->ptr, "path");
802 for (int i = 0; i < gesture->points; i++, border += 2) {
803 loc[0] = border[0];
804 loc[1] = border[1];
805 RNA_collection_add(op->ptr, "path", &itemptr);
806 RNA_float_set_array(&itemptr, "loc", loc);
807 }
808 if (valid_points > gesture->points) {
809 loc[0] = gesture->mval.x;
810 loc[1] = gesture->mval.y;
811 RNA_collection_add(op->ptr, "path", &itemptr);
812 RNA_float_set_array(&itemptr, "loc", loc);
813 }
814
815 gesture_modal_end(C, op);
816
818 if (op->type->exec) {
819 retval = op->type->exec(C, op);
820 OPERATOR_RETVAL_CHECK(retval);
821 }
822
823 return retval;
824}
825
827{
828 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
829
830 if (event->type == EVT_MODAL_MAP) {
831 switch (event->val) {
833 gesture->move = !gesture->move;
834 break;
837 short (*border)[2] = static_cast<short int (*)[2]>(gesture->customdata);
838 const short prev_x = border[gesture->points - 1][0];
839 const short prev_y = border[gesture->points - 1][1];
840
841 if (gesture->mval.x == prev_x && gesture->mval.y == prev_y) {
842 break;
843 }
844
845 const float2 cur(gesture->mval);
846 const float2 orig(border[0][0], border[0][1]);
847
848 const float dist = len_v2v2(cur, orig);
849
851 gesture_polyline_can_apply(*gesture, true))
852 {
853 return gesture_polyline_apply(C, op, true);
854 }
855
856 gesture->points++;
857 border[gesture->points - 1][0] = gesture->mval.x;
858 border[gesture->points - 1][1] = gesture->mval.y;
859 break;
860 }
862 if (gesture_polyline_can_apply(*gesture, false)) {
863 return gesture_polyline_apply(C, op, false);
864 }
865 break;
867 gesture_modal_end(C, op);
868 return OPERATOR_CANCELLED;
869 }
870 }
871 else {
872 switch (event->type) {
873 case MOUSEMOVE:
874 case INBETWEEN_MOUSEMOVE: {
876 gesture->mval = int2((event->xy[0] - gesture->winrct.xmin),
877 (event->xy[1] - gesture->winrct.ymin));
878 if (gesture->points == gesture->points_alloc) {
879 gesture->points_alloc *= 2;
880 gesture->customdata = MEM_reallocN(gesture->customdata,
881 sizeof(short[2]) * gesture->points_alloc);
882 }
883 short (*border)[2] = static_cast<short int (*)[2]>(gesture->customdata);
884
885 /* move the lasso */
886 if (gesture->move) {
887 const int dx = gesture->mval.x - border[gesture->points - 1][0];
888 const int dy = gesture->mval.y - border[gesture->points - 1][1];
889
890 for (int i = 0; i < gesture->points; i++) {
891 border[i][0] += dx;
892 border[i][1] += dy;
893 }
894 }
895 break;
896 }
897 default: {
898 break;
899 }
900 }
901 }
902
903 gesture->is_active_prev = gesture->is_active;
905}
906
911
912/* template to copy from */
913#if 0
914static wmOperatorStatus gesture_polyline_exec(bContext *C, wmOperator *op)
915{
916 RNA_BEGIN (op->ptr, itemptr, "path") {
917 float loc[2];
918
919 RNA_float_get_array(&itemptr, "loc", loc);
920 printf("Location: %f %f\n", loc[0], loc[1]);
921 }
922 RNA_END;
923
924 return OPERATOR_FINISHED;
925}
926
927void WM_OT_polyline_gesture(wmOperatorType *ot)
928{
929 PropertyRNA *prop;
930
931 ot->name = "Polyline Gesture";
932 ot->idname = "WM_OT_polyline_gesture";
933 ot->description = "Outline a selection area with each mouse click";
934
937 ot->exec = gesture_polyline_exec;
938
940
942}
943#endif
944
946
947/* -------------------------------------------------------------------- */
959
960struct SnapAngle {
963};
964
965static SnapAngle get_snap_angle(const ScrArea &area, const ToolSettings &tool_settings)
966{
967 SnapAngle snap_angle;
968 if (area.spacetype == SPACE_VIEW3D) {
969 snap_angle.increment = tool_settings.snap_angle_increment_3d;
970 snap_angle.precise_increment = tool_settings.snap_angle_increment_3d_precision;
971 }
972 else {
973 snap_angle.increment = tool_settings.snap_angle_increment_2d;
974 snap_angle.precise_increment = tool_settings.snap_angle_increment_2d_precision;
975 }
976
977 return snap_angle;
978}
979
981{
982 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
983 const rcti *rect = static_cast<const rcti *>(gesture->customdata);
984
985 if (rect->xmin == rect->xmax && rect->ymin == rect->ymax) {
986 return false;
987 }
988
989 /* Operator arguments and storage. */
990 RNA_int_set(op->ptr, "xstart", rect->xmin);
991 RNA_int_set(op->ptr, "ystart", rect->ymin);
992 RNA_int_set(op->ptr, "xend", rect->xmax);
993 RNA_int_set(op->ptr, "yend", rect->ymax);
994 RNA_boolean_set(op->ptr, "flip", gesture->use_flip);
995
996 if (op->type->exec) {
997 const wmOperatorStatus retval = op->type->exec(C, op);
998 OPERATOR_RETVAL_CHECK(retval);
999 }
1000
1001 return true;
1002}
1003
1005{
1006 wmWindow *win = CTX_wm_window(C);
1007 PropertyRNA *prop;
1008
1010
1012 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1013 gesture->is_active = true;
1014 }
1015
1016 /* Add modal handler. */
1018
1020
1021 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
1023 }
1024
1026}
1028 wmOperator *op,
1029 const wmEvent *event)
1030{
1032 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1033 gesture->draw_active_side = true;
1034 gesture->use_flip = false;
1036}
1037
1038static void wm_gesture_straightline_do_angle_snap(rcti *rect, float snap_angle)
1039{
1040 const float line_start[2] = {float(rect->xmin), float(rect->ymin)};
1041 const float line_end[2] = {float(rect->xmax), float(rect->ymax)};
1042 const float x_axis[2] = {1.0f, 0.0f};
1043
1044 float line_direction[2];
1045 sub_v2_v2v2(line_direction, line_end, line_start);
1046 const float line_length = normalize_v2(line_direction);
1047
1048 const float current_angle = angle_signed_v2v2(x_axis, line_direction);
1049 const float adjusted_angle = current_angle + (snap_angle / 2.0f);
1050 const float angle_snapped = -floorf(adjusted_angle / snap_angle) * snap_angle;
1051
1052 float line_snapped_end[2];
1053 rotate_v2_v2fl(line_snapped_end, x_axis, angle_snapped);
1054 mul_v2_fl(line_snapped_end, line_length);
1055 add_v2_v2(line_snapped_end, line_start);
1056
1057 rect->xmax = int(line_snapped_end[0]);
1058 rect->ymax = int(line_snapped_end[1]);
1059
1060 /* Check whether `angle_snapped` is a multiple of 45 degrees, if so ensure X and Y directions
1061 * are the same length (there could be an off-by-one due to rounding error). */
1062 const float fract_45 = fractf(angle_snapped / DEG2RADF(45.0f));
1063 const float fract_90 = fractf(angle_snapped / DEG2RADF(90.0f));
1064 /* Check if it's a multiple of 45 but not 90 degrees. */
1065 if ((compare_ff(fract_45, 0.0f, 1e-6) || compare_ff(fabsf(fract_45), 1.0f, 1e-6)) &&
1066 !(compare_ff(fract_90, 0.0f, 1e-6) || compare_ff(fabsf(fract_90), 1.0f, 1e-6)))
1067 {
1068 int xlen = abs(rect->xmax - rect->xmin);
1069 int ylen = rect->ymax - rect->ymin;
1070 if (abs(ylen) != xlen) {
1071 ylen = xlen * (ylen >= 0 ? 1 : -1);
1072 rect->ymax = rect->ymin + ylen;
1073 }
1074 }
1075}
1076
1078{
1079 const Scene *scene = CTX_data_scene(C);
1080 const ScrArea *area = CTX_wm_area(C);
1081 const SnapAngle snap_angle = get_snap_angle(*area, *scene->toolsettings);
1082
1083 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1084 wmWindow *win = CTX_wm_window(C);
1085 rcti *rect = static_cast<rcti *>(gesture->customdata);
1086
1087 if (event->type == EVT_MODAL_MAP) {
1088 switch (event->val) {
1089 case GESTURE_MODAL_MOVE: {
1090 gesture->move = !gesture->move;
1091 break;
1092 }
1093 case GESTURE_MODAL_BEGIN: {
1094 if (gesture->is_active == false) {
1095 gesture->is_active = true;
1097 }
1098 break;
1099 }
1100 case GESTURE_MODAL_SNAP: {
1101 /* Toggle snapping on/off. */
1102 gesture->use_snap = !gesture->use_snap;
1103 break;
1104 }
1105 case GESTURE_MODAL_FLIP: {
1106 /* Toggle flipping on/off. */
1107 gesture->use_flip = !gesture->use_flip;
1110 break;
1111 }
1112 case GESTURE_MODAL_SELECT: {
1113 if (gesture_straightline_apply(C, op)) {
1114 gesture_modal_end(C, op);
1115 return OPERATOR_FINISHED;
1116 }
1117 gesture_modal_end(C, op);
1118 return OPERATOR_CANCELLED;
1119 }
1120 case GESTURE_MODAL_CANCEL: {
1121 gesture_modal_end(C, op);
1122 return OPERATOR_CANCELLED;
1123 }
1124 }
1125 }
1126 else {
1127 switch (event->type) {
1128 case MOUSEMOVE: {
1129 if (gesture->is_active == false) {
1130 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
1131 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
1132 }
1133 else if (gesture->move) {
1134 BLI_rcti_translate(rect,
1135 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
1136 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
1138 }
1139 else {
1140 rect->xmax = event->xy[0] - gesture->winrct.xmin;
1141 rect->ymax = event->xy[1] - gesture->winrct.ymin;
1143 }
1144
1145 if (gesture->use_snap) {
1148 }
1149
1151
1152 break;
1153 }
1154 default: {
1155 break;
1156 }
1157 }
1158 }
1159
1160 gesture->is_active_prev = gesture->is_active;
1162}
1163
1165 wmOperator *op,
1166 const wmEvent *event)
1167{
1168 const Scene *scene = CTX_data_scene(C);
1169 const ScrArea *area = CTX_wm_area(C);
1170 const SnapAngle snap_angle = get_snap_angle(*area, *scene->toolsettings);
1171
1172 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1173 wmWindow *win = CTX_wm_window(C);
1174 rcti *rect = static_cast<rcti *>(gesture->customdata);
1175
1176 if (event->type == EVT_MODAL_MAP) {
1177 switch (event->val) {
1178 case GESTURE_MODAL_MOVE: {
1179 gesture->move = !gesture->move;
1180 break;
1181 }
1182 case GESTURE_MODAL_BEGIN: {
1183 if (gesture->is_active == false) {
1184 gesture->is_active = true;
1186 }
1187 break;
1188 }
1189 case GESTURE_MODAL_SNAP: {
1190 /* Toggle snapping on/off. */
1191 gesture->use_snap = !gesture->use_snap;
1192 break;
1193 }
1194 case GESTURE_MODAL_FLIP: {
1195 /* Toggle flip on/off. */
1196 gesture->use_flip = !gesture->use_flip;
1198 break;
1199 }
1202 case GESTURE_MODAL_IN:
1203 case GESTURE_MODAL_OUT: {
1204 if (gesture->wait_for_input) {
1205 gesture->modal_state = event->val;
1206 }
1207 if (gesture_straightline_apply(C, op)) {
1208 gesture_modal_end(C, op);
1209 return OPERATOR_FINISHED;
1210 }
1211 gesture_modal_end(C, op);
1212 return OPERATOR_CANCELLED;
1213 }
1214 case GESTURE_MODAL_CANCEL: {
1215 gesture_modal_end(C, op);
1216 return OPERATOR_CANCELLED;
1217 }
1218 }
1219 }
1220 else {
1221 switch (event->type) {
1222 case MOUSEMOVE: {
1223 if (gesture->is_active == false) {
1224 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
1225 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
1226 }
1227 else if (gesture->move) {
1228 BLI_rcti_translate(rect,
1229 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
1230 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
1231 }
1232 else {
1233 rect->xmax = event->xy[0] - gesture->winrct.xmin;
1234 rect->ymax = event->xy[1] - gesture->winrct.ymin;
1235 }
1236
1237 if (gesture->use_snap) {
1239 }
1240
1242
1243 break;
1244 }
1245 default: {
1246 break;
1247 }
1248 }
1249 }
1250
1251 gesture->is_active_prev = gesture->is_active;
1253}
1254
1259
1260#if 0
1261/* Template to copy from. */
1262void WM_OT_straightline_gesture(wmOperatorType *ot)
1263{
1264 PropertyRNA *prop;
1265
1266 ot->name = "Straight Line Gesture";
1267 ot->idname = "WM_OT_straightline_gesture";
1268 ot->description = "Draw a straight line defined by the cursor";
1269
1272 ot->exec = gesture_straightline_exec;
1273
1274 ot->poll = WM_operator_winactive;
1275
1277}
1278#endif
1279
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int min_ii(int a, int b)
MINLINE float pow2f(float x)
MINLINE int max_ii(int a, int b)
MINLINE float square_f(float a)
MINLINE int compare_ff(float a, float b, float max_diff)
#define DEG2RADF(_deg)
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void add_v2_v2(float r[2], const float a[2])
float angle_signed_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float n[2])
void rotate_v2_v2fl(float r[2], const float p[2], float angle)
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.cc:566
#define UNUSED_FUNCTION(x)
@ SPACE_VIEW3D
#define UI_SCALE_FAC
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define OPERATOR_RETVAL_CHECK(ret)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
@ SEL_OP_ADD
@ SEL_OP_SUB
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
#define RNA_PROP_END
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
#define RNA_PROP_BEGIN(sptr, itemptr, prop)
@ PROP_COLLECTION
Definition RNA_types.hh:168
@ PROP_NONE
Definition RNA_types.hh:233
#define C
Definition RandGen.cpp:29
#define WM_GESTURE_RECT
Definition WM_types.hh:602
#define WM_GESTURE_STRAIGHTLINE
Definition WM_types.hh:606
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
#define WM_GESTURE_LINES
Definition WM_types.hh:601
#define WM_GESTURE_LASSO
Definition WM_types.hh:604
#define WM_GESTURE_CIRCLE
Definition WM_types.hh:605
#define WM_GESTURE_CROSS_RECT
Definition WM_types.hh:603
#define WM_GESTURE_POLYLINE
Definition WM_types.hh:607
return true
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
nullptr float
#define printf(...)
#define abs
#define floor
#define ceil
T length_squared(const VecBase< T, Size > &a)
T interpolate(const T &a, const T &b, const FactorT &t)
constexpr float POLYLINE_CLICK_RADIUS
Definition WM_types.hh:597
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define floorf
#define fabsf
#define fractf
void RNA_collection_clear(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
int RNA_int_get(PointerRNA *ptr, const char *name)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
void RNA_def_property_struct_runtime(StructOrFunctionRNA *cont, PropertyRNA *prop, StructRNA *type)
PropertyRNA * RNA_def_property(StructOrFunctionRNA *cont_, const char *identifier, int type, int subtype)
struct ToolSettings * toolsettings
float precise_increment
float snap_angle_increment_3d_precision
float snap_angle_increment_2d_precision
float snap_angle_increment_3d
float snap_angle_increment_2d
float x
float y
int ymin
int ymax
int xmin
int xmax
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
uint wait_for_input
Definition WM_types.hh:643
uint use_snap
Definition WM_types.hh:648
uint is_active_prev
Definition WM_types.hh:641
int modal_state
Definition WM_types.hh:628
void * customdata
Definition WM_types.hh:663
int points_alloc
Definition WM_types.hh:627
uint move
Definition WM_types.hh:645
bool draw_active_side
Definition WM_types.hh:630
uint use_flip
Definition WM_types.hh:651
uint is_active
Definition WM_types.hh:639
rcti winrct
Definition WM_types.hh:623
uint use_smooth
Definition WM_types.hh:654
blender::int2 mval
Definition WM_types.hh:632
wmOperatorStatus(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1049
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
@ RIGHTMOUSE
@ EVT_MODAL_MAP
@ MOUSEMOVE
@ LEFTMOUSE
@ NDOF_MOTION
@ MIDDLEMOUSE
@ EVT_ESCKEY
@ INBETWEEN_MOUSEMOVE
@ GESTURE_MODAL_CIRCLE_SIZE
@ GESTURE_MODAL_OUT
@ GESTURE_MODAL_SNAP
@ GESTURE_MODAL_CIRCLE_ADD
@ GESTURE_MODAL_CANCEL
@ GESTURE_MODAL_CIRCLE_SUB
@ GESTURE_MODAL_MOVE
@ GESTURE_MODAL_NOP
@ GESTURE_MODAL_DESELECT
@ GESTURE_MODAL_CONFIRM
@ GESTURE_MODAL_IN
@ GESTURE_MODAL_FLIP
@ GESTURE_MODAL_BEGIN
@ GESTURE_MODAL_SELECT
wmOperatorType * ot
Definition wm_files.cc:4237
wmGesture * WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent *event, int type)
Definition wm_gesture.cc:39
void wm_gesture_tag_redraw(wmWindow *win)
void WM_gesture_end(wmWindow *win, wmGesture *gesture)
Definition wm_gesture.cc:99
static int UNUSED_FUNCTION gesture_modal_state_from_operator(wmOperator *op)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
static SnapAngle get_snap_angle(const ScrArea &area, const ToolSettings &tool_settings)
static bool gesture_box_apply(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_polyline_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus gesture_lasso_apply(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void gesture_modal_end(bContext *C, wmOperator *op)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_polyline_cancel(bContext *C, wmOperator *op)
void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool gesture_polyline_can_apply(const wmGesture &wmGesture, const bool is_click_submitted)
static bool gesture_straightline_apply(bContext *C, wmOperator *op)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_straightline_active_side_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void gesture_circle_apply(bContext *C, wmOperator *op)
static void wm_gesture_straightline_do_angle_snap(rcti *rect, float snap_angle)
wmOperatorStatus WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool gesture_box_apply_rect(wmOperator *op)
wmOperatorStatus WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void gesture_modal_state_to_operator(wmOperator *op, int modal_state)
static int gesture_polyline_valid_points(const wmGesture &wmGesture, const bool is_click_submitted)
wmOperatorStatus WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus gesture_polyline_apply(bContext *C, wmOperator *op, const bool is_click_submitted)
wmOperatorStatus WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
void WM_operator_properties_gesture_polyline(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
bool WM_operator_winactive(bContext *C)