Blender V4.3
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#include <fmt/format.h>
16
17#include "DNA_space_types.h"
19
20#include "BLI_math_base.hh"
21#include "BLI_math_rotation.h"
22#include "BLI_math_vector.h"
23#include "BLI_math_vector.hh"
25#include "BLI_rect.h"
26
27#include "BLT_translation.hh"
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/* -------------------------------------------------------------------- */
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
122/* -------------------------------------------------------------------- */
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 int retval;
156
157 if (!gesture_box_apply_rect(op)) {
158 return false;
159 }
160
161 if (gesture->wait_for_input) {
163 }
164
165 retval = op->type->exec(C, op);
166 OPERATOR_RETVAL_CHECK(retval);
167
168 return (retval & OPERATOR_FINISHED) ? true : false;
169}
170
172{
173 wmWindow *win = CTX_wm_window(C);
174 const ARegion *region = CTX_wm_region(C);
175 const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
176 RNA_boolean_get(op->ptr, "wait_for_input");
177
178 if (wait_for_input) {
179 op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT);
180 }
181 else {
182 op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_RECT);
183 }
184
185 {
186 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
187 gesture->wait_for_input = wait_for_input;
188 }
189
190 /* Add modal handler. */
192
194
196}
197
199{
200 wmWindow *win = CTX_wm_window(C);
201 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
202 rcti *rect = static_cast<rcti *>(gesture->customdata);
203
204 if (event->type == EVT_MODAL_MAP) {
205 switch (event->val) {
206 case GESTURE_MODAL_MOVE: {
207 gesture->move = !gesture->move;
208 break;
209 }
210 case GESTURE_MODAL_BEGIN: {
211 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
212 gesture->is_active = true;
214 }
215 break;
216 }
219 case GESTURE_MODAL_IN:
220 case GESTURE_MODAL_OUT: {
221 if (gesture->wait_for_input) {
222 gesture->modal_state = event->val;
223 }
224 if (gesture_box_apply(C, op)) {
225 gesture_modal_end(C, op);
226 return OPERATOR_FINISHED;
227 }
228 gesture_modal_end(C, op);
229 return OPERATOR_CANCELLED;
230 }
232 gesture_modal_end(C, op);
233 return OPERATOR_CANCELLED;
234 }
235 }
236 }
237 else {
238 switch (event->type) {
239 case MOUSEMOVE: {
240 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
241 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
242 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
243 }
244 else if (gesture->move) {
246 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
247 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
248 }
249 else {
250 rect->xmax = event->xy[0] - gesture->winrct.xmin;
251 rect->ymax = event->xy[1] - gesture->winrct.ymin;
252 }
254
256
257 break;
258 }
259#ifdef WITH_INPUT_NDOF
260 case NDOF_MOTION: {
262 }
263#endif
264
265#if 0 /* This allows view navigation, keep disabled as it's too unpredictable. */
266 default:
268#endif
269 }
270 }
271
272 gesture->is_active_prev = gesture->is_active;
274}
275
277{
278 gesture_modal_end(C, op);
279}
280
283/* -------------------------------------------------------------------- */
292static void gesture_circle_apply(bContext *C, wmOperator *op);
293
295{
296 wmWindow *win = CTX_wm_window(C);
297 const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
298 RNA_boolean_get(op->ptr, "wait_for_input");
299
301 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
302 rcti *rect = static_cast<rcti *>(gesture->customdata);
303
304 /* Default or previously stored value. */
305 rect->xmax = RNA_int_get(op->ptr, "radius");
306
307 gesture->wait_for_input = wait_for_input;
308
309 /* Starting with the mode starts immediately,
310 * like having 'wait_for_input' disabled (some tools use this). */
311 if (gesture->wait_for_input == false) {
312 gesture->is_active = true;
314 gesture->is_active_prev = true;
315 }
316
317 /* Add modal handler. */
319
321
323}
324
326{
327 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
328 const rcti *rect = static_cast<const rcti *>(gesture->customdata);
329
330 if (gesture->wait_for_input && (gesture->modal_state == GESTURE_MODAL_NOP)) {
331 return;
332 }
333
334 /* Operator arguments and storage. */
335 RNA_int_set(op->ptr, "x", rect->xmin);
336 RNA_int_set(op->ptr, "y", rect->ymin);
337 RNA_int_set(op->ptr, "radius", rect->xmax);
338
339 /* When 'wait_for_input' is false,
340 * use properties to get the selection state (typically tool settings).
341 * This is done so executing as a mode can select & de-select, see: #58594. */
342 if (gesture->wait_for_input) {
344 }
345
346 if (op->type->exec) {
347 int retval;
348 retval = op->type->exec(C, op);
349 OPERATOR_RETVAL_CHECK(retval);
350 }
351}
352
354{
355 wmWindow *win = CTX_wm_window(C);
356 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
357 rcti *rect = static_cast<rcti *>(gesture->customdata);
358
359 if (event->type == MOUSEMOVE) {
360
361 rect->xmin = event->xy[0] - gesture->winrct.xmin;
362 rect->ymin = event->xy[1] - gesture->winrct.ymin;
363
365
366 if (gesture->is_active) {
368 }
369 }
370 else if (event->type == EVT_MODAL_MAP) {
371 bool is_circle_size = false;
372 bool is_finished = false;
373 float fac;
374
375 switch (event->val) {
377 fac = 0.3f * (event->xy[1] - event->prev_xy[1]);
378 if (fac > 0) {
379 rect->xmax += ceil(fac);
380 }
381 else {
382 rect->xmax += floor(fac);
383 }
384 if (rect->xmax < 1) {
385 rect->xmax = 1;
386 }
387 is_circle_size = true;
388 break;
390 rect->xmax += 2 + rect->xmax / 10;
391 is_circle_size = true;
392 break;
394 rect->xmax -= 2 + rect->xmax / 10;
395 if (rect->xmax < 1) {
396 rect->xmax = 1;
397 }
398 is_circle_size = true;
399 break;
402 case GESTURE_MODAL_NOP: {
403 if (gesture->wait_for_input) {
404 gesture->modal_state = event->val;
405 }
406 if (event->val == GESTURE_MODAL_NOP) {
407 /* Single action, click-drag & release to exit. */
408 if (gesture->wait_for_input == false) {
409 is_finished = true;
410 }
411 }
412 else {
413 /* Apply first click. */
414 gesture->is_active = true;
417 }
418 break;
419 }
422 is_finished = true;
423 }
424
425 if (is_finished) {
426 gesture_modal_end(C, op);
427 return OPERATOR_FINISHED; /* Use finish or we don't get an undo. */
428 }
429
430 if (is_circle_size) {
432
433 /* So next use remembers last seen size, even if we didn't apply it. */
434 RNA_int_set(op->ptr, "radius", rect->xmax);
435 }
436 }
437#ifdef WITH_INPUT_NDOF
438 else if (event->type == NDOF_MOTION) {
440 }
441#endif
442
443#if 0
444 /* Allow view navigation??? */
445 /* NOTE: this gives issues:
446 * 1) other modal ops run on top (box select),
447 * 2) middle-mouse is used now 3) tablet/trackpad? */
448 else {
450 }
451#endif
452
453 gesture->is_active_prev = gesture->is_active;
455}
456
461
462#if 0
463/* Template to copy from. */
464void WM_OT_circle_gesture(wmOperatorType *ot)
465{
466 ot->name = "Circle Gesture";
467 ot->idname = "WM_OT_circle_gesture";
468 ot->description = "Enter rotate mode with a circular gesture";
469
473
474 /* Properties. */
476}
477#endif
478
481/* -------------------------------------------------------------------- */
491{
492 wmWindow *win = CTX_wm_window(C);
493 PropertyRNA *prop;
494
496 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
497 gesture->use_smooth = RNA_boolean_get(op->ptr, "use_smooth_stroke");
498
499 /* Add modal handler. */
501
503
504 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
506 }
507
509}
510
512{
513 wmWindow *win = CTX_wm_window(C);
514 PropertyRNA *prop;
515
517 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
518 if ((prop = RNA_struct_find_property(op->ptr, "use_smooth_stroke"))) {
519 gesture->use_smooth = RNA_property_boolean_get(op->ptr, prop);
520 }
521
522 /* Add modal handler. */
524
526
527 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
529 }
530
532}
533
535{
536 int retval = OPERATOR_FINISHED;
537 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
538 PointerRNA itemptr;
539 float loc[2];
540 int i;
541 const float *lasso = static_cast<const float *>(gesture->customdata);
542
543 /* Operator storage as path. */
544
545 RNA_collection_clear(op->ptr, "path");
546 for (i = 0; i < gesture->points; i++, lasso += 2) {
547 loc[0] = lasso[0];
548 loc[1] = lasso[1];
549 RNA_collection_add(op->ptr, "path", &itemptr);
550 RNA_float_set_array(&itemptr, "loc", loc);
551 }
552
553 gesture_modal_end(C, op);
554
555 if (op->type->exec) {
556 retval = op->type->exec(C, op);
557 OPERATOR_RETVAL_CHECK(retval);
558 }
559
560 return retval;
561}
562
564{
565 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
566 const float factor = gesture->use_smooth ? RNA_float_get(op->ptr, "smooth_stroke_factor") : 0.0f;
567 const int radius = gesture->use_smooth ? RNA_int_get(op->ptr, "smooth_stroke_radius") : 0;
568
569 if (event->type == EVT_MODAL_MAP) {
570 switch (event->val) {
571 case GESTURE_MODAL_MOVE: {
572 gesture->move = !gesture->move;
573 break;
574 }
575 }
576 }
577 else {
578 switch (event->type) {
579 case MOUSEMOVE:
580 case INBETWEEN_MOUSEMOVE: {
582 gesture->mval = int2((event->xy[0] - gesture->winrct.xmin),
583 (event->xy[1] - gesture->winrct.ymin));
584
585 if (gesture->points == gesture->points_alloc) {
586 gesture->points_alloc *= 2;
587 gesture->customdata = MEM_reallocN(gesture->customdata,
588 sizeof(float[2]) * gesture->points_alloc);
589 }
590
591 {
592 float(*lasso)[2] = static_cast<float(*)[2]>(gesture->customdata);
593 const float2 current_mouse_position = float2(gesture->mval);
594 const float2 last_position(lasso[gesture->points - 1][0], lasso[gesture->points - 1][1]);
595
596 const float2 delta = current_mouse_position - last_position;
597 const float dist_squared = blender::math::length_squared(delta);
598
599 /* Move the lasso. */
600 if (gesture->move) {
601 for (int i = 0; i < gesture->points; i++) {
602 lasso[i][0] += delta.x;
603 lasso[i][1] += delta.y;
604 }
605 }
606 else if (gesture->use_smooth) {
607 if (dist_squared > square_f(radius)) {
609 current_mouse_position, last_position, factor);
610
611 lasso[gesture->points][0] = result.x;
612 lasso[gesture->points][1] = result.y;
613 gesture->points++;
614 }
615 }
616 else if (dist_squared > pow2f(2.0f * UI_SCALE_FAC)) {
617 /* Make a simple distance check to get a smoother lasso even if smoothing isn't enabled
618 * add only when at least 2 pixels between this and previous location. */
619 lasso[gesture->points][0] = gesture->mval.x;
620 lasso[gesture->points][1] = gesture->mval.y;
621 gesture->points++;
622 }
623 }
624 break;
625 }
626 case LEFTMOUSE:
627 case MIDDLEMOUSE:
628 case RIGHTMOUSE: {
629 if (event->val == KM_RELEASE) { /* Key release. */
630 return gesture_lasso_apply(C, op);
631 }
632 break;
633 }
634 case EVT_ESCKEY: {
635 gesture_modal_end(C, op);
636 return OPERATOR_CANCELLED;
637 }
638 }
639 }
640
641 gesture->is_active_prev = gesture->is_active;
643}
644
646{
647 return WM_gesture_lasso_modal(C, op, event);
648}
649
654
659
661{
662 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
663 BLI_assert(prop != nullptr);
664 if (!prop) {
665 return {};
666 }
667 const int len = RNA_property_collection_length(op->ptr, prop);
668 if (len == 0) {
669 return {};
670 }
671
672 int i = 0;
673 Array<int2> mcoords(len);
674
675 RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
676 float loc[2];
677 RNA_float_get_array(&itemptr, "loc", loc);
678 mcoords[i] = int2(loc[0], loc[1]);
679 i++;
680 }
682
683 return mcoords;
684}
685
686#if 0
687/* Template to copy from. */
688
689static int gesture_lasso_exec(bContext *C, wmOperator *op)
690{
691 RNA_BEGIN (op->ptr, itemptr, "path") {
692 float loc[2];
693
694 RNA_float_get_array(&itemptr, "loc", loc);
695 printf("Location: %f %f\n", loc[0], loc[1]);
696 }
697 RNA_END;
698
699 return OPERATOR_FINISHED;
700}
701
702void WM_OT_lasso_gesture(wmOperatorType *ot)
703{
704 PropertyRNA *prop;
705
706 ot->name = "Lasso Gesture";
707 ot->idname = "WM_OT_lasso_gesture";
708 ot->description = "Draw a shape defined by the cursor";
709
712 ot->exec = gesture_lasso_exec;
713
715
717
719 RNA_def_property_struct_runtime(ot->srna, prop, &RNA_OperatorMousePath);
720}
721#endif
722
725/* -------------------------------------------------------------------- */
733{
734 wmWindow *win = CTX_wm_window(C);
735 PropertyRNA *prop;
736
738
739 /* add modal handler */
741
743
744 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
746 }
747
749}
750
751/* Calculates the number of valid points in a polyline gesture where
752 * a duplicated end point is invalid for submission */
753static int gesture_polyline_valid_points(const wmGesture &wmGesture, const bool is_click_submitted)
754{
756
757 const int num_points = wmGesture.points;
758 if (is_click_submitted) {
759 return num_points;
760 }
761
762 short(*points)[2] = static_cast<short int(*)[2]>(wmGesture.customdata);
763
764 const short prev_x = points[num_points - 1][0];
765 const short prev_y = points[num_points - 1][1];
766
767 return (wmGesture.mval.x == prev_x && wmGesture.mval.y == prev_y) ? num_points : num_points + 1;
768}
769
778static bool gesture_polyline_can_apply(const wmGesture &wmGesture, const bool is_click_submitted)
779{
780 if (wmGesture.points < 2) {
781 return false;
782 }
783
784 const int valid_points = gesture_polyline_valid_points(wmGesture, is_click_submitted);
785 if (valid_points <= 2) {
786 return false;
787 }
788
789 return true;
790}
791
792static int gesture_polyline_apply(bContext *C, wmOperator *op, const bool is_click_submitted)
793{
794 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
795 BLI_assert(gesture_polyline_can_apply(*gesture, is_click_submitted));
796
797 const int valid_points = gesture_polyline_valid_points(*gesture, is_click_submitted);
798 const short *border = static_cast<const short int *>(gesture->customdata);
799
800 PointerRNA itemptr;
801 float loc[2];
802 RNA_collection_clear(op->ptr, "path");
803 for (int i = 0; i < gesture->points; i++, border += 2) {
804 loc[0] = border[0];
805 loc[1] = border[1];
806 RNA_collection_add(op->ptr, "path", &itemptr);
807 RNA_float_set_array(&itemptr, "loc", loc);
808 }
809 if (valid_points > gesture->points) {
810 loc[0] = gesture->mval.x;
811 loc[1] = gesture->mval.y;
812 RNA_collection_add(op->ptr, "path", &itemptr);
813 RNA_float_set_array(&itemptr, "loc", loc);
814 }
815
816 gesture_modal_end(C, op);
817
818 int retval = OPERATOR_FINISHED;
819 if (op->type->exec) {
820 retval = op->type->exec(C, op);
821 OPERATOR_RETVAL_CHECK(retval);
822 }
823
824 return retval;
825}
826
828{
829 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
830
831 if (event->type == EVT_MODAL_MAP) {
832 switch (event->val) {
834 gesture->move = !gesture->move;
835 break;
838 short(*border)[2] = static_cast<short int(*)[2]>(gesture->customdata);
839 const short prev_x = border[gesture->points - 1][0];
840 const short prev_y = border[gesture->points - 1][1];
841
842 if (gesture->mval.x == prev_x && gesture->mval.y == prev_y) {
843 break;
844 }
845
846 const float2 cur(gesture->mval);
847 const float2 orig(border[0][0], border[0][1]);
848
849 const float dist = len_v2v2(cur, orig);
850
852 gesture_polyline_can_apply(*gesture, true))
853 {
854 return gesture_polyline_apply(C, op, true);
855 }
856
857 gesture->points++;
858 border[gesture->points - 1][0] = gesture->mval.x;
859 border[gesture->points - 1][1] = gesture->mval.y;
860 break;
861 }
863 if (gesture_polyline_can_apply(*gesture, false)) {
864 return gesture_polyline_apply(C, op, false);
865 }
866 break;
868 gesture_modal_end(C, op);
869 return OPERATOR_CANCELLED;
870 }
871 }
872 else {
873 switch (event->type) {
874 case MOUSEMOVE:
875 case INBETWEEN_MOUSEMOVE: {
877 gesture->mval = int2((event->xy[0] - gesture->winrct.xmin),
878 (event->xy[1] - gesture->winrct.ymin));
879 if (gesture->points == gesture->points_alloc) {
880 gesture->points_alloc *= 2;
881 gesture->customdata = MEM_reallocN(gesture->customdata,
882 sizeof(short[2]) * gesture->points_alloc);
883 }
884 short(*border)[2] = static_cast<short int(*)[2]>(gesture->customdata);
885
886 /* move the lasso */
887 if (gesture->move) {
888 const int dx = gesture->mval.x - border[gesture->points - 1][0];
889 const int dy = gesture->mval.y - border[gesture->points - 1][1];
890
891 for (int i = 0; i < gesture->points; i++) {
892 border[i][0] += dx;
893 border[i][1] += dy;
894 }
895 }
896 break;
897 }
898 }
899 }
900
901 gesture->is_active_prev = gesture->is_active;
903}
904
909
910/* template to copy from */
911#if 0
912static int gesture_polyline_exec(bContext *C, wmOperator *op)
913{
914 RNA_BEGIN (op->ptr, itemptr, "path") {
915 float loc[2];
916
917 RNA_float_get_array(&itemptr, "loc", loc);
918 printf("Location: %f %f\n", loc[0], loc[1]);
919 }
920 RNA_END;
921
922 return OPERATOR_FINISHED;
923}
924
925void WM_OT_polyline_gesture(wmOperatorType *ot)
926{
927 PropertyRNA *prop;
928
929 ot->name = "Polyline Gesture";
930 ot->idname = "WM_OT_polyline_gesture";
931 ot->description = "Outline a selection area with each mouse click";
932
935 ot->exec = gesture_polyline_exec;
936
938
940}
941#endif
942
945/* -------------------------------------------------------------------- */
958struct SnapAngle {
961};
962
963static SnapAngle get_snap_angle(const ScrArea &area, const ToolSettings &tool_settings)
964{
965 SnapAngle snap_angle;
966 if (area.spacetype == SPACE_VIEW3D) {
967 snap_angle.increment = tool_settings.snap_angle_increment_3d;
968 snap_angle.precise_increment = tool_settings.snap_angle_increment_3d_precision;
969 }
970 else {
971 snap_angle.increment = tool_settings.snap_angle_increment_2d;
972 snap_angle.precise_increment = tool_settings.snap_angle_increment_2d_precision;
973 }
974
975 return snap_angle;
976}
977
979{
980 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
981 const rcti *rect = static_cast<const rcti *>(gesture->customdata);
982
983 if (rect->xmin == rect->xmax && rect->ymin == rect->ymax) {
984 return false;
985 }
986
987 /* Operator arguments and storage. */
988 RNA_int_set(op->ptr, "xstart", rect->xmin);
989 RNA_int_set(op->ptr, "ystart", rect->ymin);
990 RNA_int_set(op->ptr, "xend", rect->xmax);
991 RNA_int_set(op->ptr, "yend", rect->ymax);
992 RNA_boolean_set(op->ptr, "flip", gesture->use_flip);
993
994 if (op->type->exec) {
995 int retval = op->type->exec(C, op);
996 OPERATOR_RETVAL_CHECK(retval);
997 }
998
999 return true;
1000}
1001
1003{
1004 wmWindow *win = CTX_wm_window(C);
1005 PropertyRNA *prop;
1006
1008
1010 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1011 gesture->is_active = true;
1012 }
1013
1014 /* Add modal handler. */
1016
1018
1019 if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
1021 }
1022
1024}
1026{
1027 WM_gesture_straightline_invoke(C, op, event);
1028 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1029 gesture->draw_active_side = true;
1030 gesture->use_flip = false;
1032}
1033
1034static void wm_gesture_straightline_do_angle_snap(rcti *rect, float snap_angle)
1035{
1036 const float line_start[2] = {float(rect->xmin), float(rect->ymin)};
1037 const float line_end[2] = {float(rect->xmax), float(rect->ymax)};
1038 const float x_axis[2] = {1.0f, 0.0f};
1039
1040 float line_direction[2];
1041 sub_v2_v2v2(line_direction, line_end, line_start);
1042 const float line_length = normalize_v2(line_direction);
1043
1044 const float current_angle = angle_signed_v2v2(x_axis, line_direction);
1045 const float adjusted_angle = current_angle + (snap_angle / 2.0f);
1046 const float angle_snapped = -floorf(adjusted_angle / snap_angle) * snap_angle;
1047
1048 float line_snapped_end[2];
1049 rotate_v2_v2fl(line_snapped_end, x_axis, angle_snapped);
1050 mul_v2_fl(line_snapped_end, line_length);
1051 add_v2_v2(line_snapped_end, line_start);
1052
1053 rect->xmax = int(line_snapped_end[0]);
1054 rect->ymax = int(line_snapped_end[1]);
1055
1056 /* Check whether `angle_snapped` is a multiple of 45 degrees, if so ensure X and Y directions
1057 * are the same length (there could be an off-by-one due to rounding error). */
1058 const float fract_45 = fractf(angle_snapped / DEG2RADF(45.0f));
1059 const float fract_90 = fractf(angle_snapped / DEG2RADF(90.0f));
1060 /* Check if it's a multiple of 45 but not 90 degrees. */
1061 if ((compare_ff(fract_45, 0.0f, 1e-6) || compare_ff(fabsf(fract_45), 1.0f, 1e-6)) &&
1062 (!(compare_ff(fract_90, 0.0f, 1e-6) || compare_ff(fabsf(fract_90), 1.0f, 1e-6))))
1063 {
1064 int xlen = abs(rect->xmax - rect->xmin);
1065 int ylen = rect->ymax - rect->ymin;
1066 if (abs(ylen) != xlen) {
1067 ylen = xlen * (ylen >= 0 ? 1 : -1);
1068 rect->ymax = rect->ymin + ylen;
1069 }
1070 }
1071}
1072
1074{
1075 const Scene *scene = CTX_data_scene(C);
1076 const ScrArea *area = CTX_wm_area(C);
1077 const SnapAngle snap_angle = get_snap_angle(*area, *scene->toolsettings);
1078
1079 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1080 wmWindow *win = CTX_wm_window(C);
1081 rcti *rect = static_cast<rcti *>(gesture->customdata);
1082
1083 if (event->type == EVT_MODAL_MAP) {
1084 switch (event->val) {
1085 case GESTURE_MODAL_MOVE: {
1086 gesture->move = !gesture->move;
1087 break;
1088 }
1089 case GESTURE_MODAL_BEGIN: {
1090 if (gesture->is_active == false) {
1091 gesture->is_active = true;
1093 }
1094 break;
1095 }
1096 case GESTURE_MODAL_SNAP: {
1097 /* Toggle snapping on/off. */
1098 gesture->use_snap = !gesture->use_snap;
1099 break;
1100 }
1101 case GESTURE_MODAL_FLIP: {
1102 /* Toggle flipping on/off. */
1103 gesture->use_flip = !gesture->use_flip;
1106 break;
1107 }
1108 case GESTURE_MODAL_SELECT: {
1109 if (gesture_straightline_apply(C, op)) {
1110 gesture_modal_end(C, op);
1111 return OPERATOR_FINISHED;
1112 }
1113 gesture_modal_end(C, op);
1114 return OPERATOR_CANCELLED;
1115 }
1116 case GESTURE_MODAL_CANCEL: {
1117 gesture_modal_end(C, op);
1118 return OPERATOR_CANCELLED;
1119 }
1120 }
1121 }
1122 else {
1123 switch (event->type) {
1124 case MOUSEMOVE: {
1125 if (gesture->is_active == false) {
1126 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
1127 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
1128 }
1129 else if (gesture->move) {
1130 BLI_rcti_translate(rect,
1131 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
1132 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
1134 }
1135 else {
1136 rect->xmax = event->xy[0] - gesture->winrct.xmin;
1137 rect->ymax = event->xy[1] - gesture->winrct.ymin;
1139 }
1140
1141 if (gesture->use_snap) {
1144 }
1145
1147
1148 break;
1149 }
1150 }
1151 }
1152
1153 gesture->is_active_prev = gesture->is_active;
1155}
1156
1158{
1159 const Scene *scene = CTX_data_scene(C);
1160 const ScrArea *area = CTX_wm_area(C);
1161 const SnapAngle snap_angle = get_snap_angle(*area, *scene->toolsettings);
1162
1163 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
1164 wmWindow *win = CTX_wm_window(C);
1165 rcti *rect = static_cast<rcti *>(gesture->customdata);
1166
1167 if (event->type == EVT_MODAL_MAP) {
1168 switch (event->val) {
1169 case GESTURE_MODAL_MOVE: {
1170 gesture->move = !gesture->move;
1171 break;
1172 }
1173 case GESTURE_MODAL_BEGIN: {
1174 if (gesture->is_active == false) {
1175 gesture->is_active = true;
1177 }
1178 break;
1179 }
1180 case GESTURE_MODAL_SNAP: {
1181 /* Toggle snapping on/off. */
1182 gesture->use_snap = !gesture->use_snap;
1183 break;
1184 }
1185 case GESTURE_MODAL_FLIP: {
1186 /* Toggle flip on/off. */
1187 gesture->use_flip = !gesture->use_flip;
1189 break;
1190 }
1193 case GESTURE_MODAL_IN:
1194 case GESTURE_MODAL_OUT: {
1195 if (gesture->wait_for_input) {
1196 gesture->modal_state = event->val;
1197 }
1198 if (gesture_straightline_apply(C, op)) {
1199 gesture_modal_end(C, op);
1200 return OPERATOR_FINISHED;
1201 }
1202 gesture_modal_end(C, op);
1203 return OPERATOR_CANCELLED;
1204 }
1205 case GESTURE_MODAL_CANCEL: {
1206 gesture_modal_end(C, op);
1207 return OPERATOR_CANCELLED;
1208 }
1209 }
1210 }
1211 else {
1212 switch (event->type) {
1213 case MOUSEMOVE: {
1214 if (gesture->is_active == false) {
1215 rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin;
1216 rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin;
1217 }
1218 else if (gesture->move) {
1219 BLI_rcti_translate(rect,
1220 (event->xy[0] - gesture->winrct.xmin) - rect->xmax,
1221 (event->xy[1] - gesture->winrct.ymin) - rect->ymax);
1222 }
1223 else {
1224 rect->xmax = event->xy[0] - gesture->winrct.xmin;
1225 rect->ymax = event->xy[1] - gesture->winrct.ymin;
1226 }
1227
1228 if (gesture->use_snap) {
1230 }
1231
1233
1234 break;
1235 }
1236 }
1237 }
1238
1239 gesture->is_active_prev = gesture->is_active;
1241}
1242
1247
1248#if 0
1249/* Template to copy from. */
1250void WM_OT_straightline_gesture(wmOperatorType *ot)
1251{
1252 PropertyRNA *prop;
1253
1254 ot->name = "Straight Line Gesture";
1255 ot->idname = "WM_OT_straightline_gesture";
1256 ot->description = "Draw a straight line defined by the cursor";
1257
1260 ot->exec = gesture_straightline_exec;
1261
1263
1265}
1266#endif
1267
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:50
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.c:560
#define UNUSED_FUNCTION(x)
@ SPACE_VIEW3D
#define UI_SCALE_FAC
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define OPERATOR_RETVAL_CHECK(ret)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:708
@ 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:71
@ PROP_NONE
Definition RNA_types.hh:136
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
#define WM_GESTURE_RECT
Definition WM_types.hh:568
#define WM_GESTURE_STRAIGHTLINE
Definition WM_types.hh:572
@ KM_RELEASE
Definition WM_types.hh:285
#define WM_GESTURE_LINES
Definition WM_types.hh:567
#define WM_GESTURE_LASSO
Definition WM_types.hh:570
#define WM_GESTURE_CIRCLE
Definition WM_types.hh:571
#define WM_GESTURE_CROSS_RECT
Definition WM_types.hh:569
#define WM_GESTURE_POLYLINE
Definition WM_types.hh:573
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define printf
#define floorf(x)
#define fabsf(x)
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
MINLINE float fractf(float a)
ccl_device_inline float2 floor(const float2 a)
ccl_device_inline float3 ceil(const float3 a)
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:563
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)
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
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
short type
Definition WM_types.hh:722
uint wait_for_input
Definition WM_types.hh:609
uint use_snap
Definition WM_types.hh:614
uint is_active_prev
Definition WM_types.hh:607
int modal_state
Definition WM_types.hh:594
void * customdata
Definition WM_types.hh:629
int points_alloc
Definition WM_types.hh:593
uint move
Definition WM_types.hh:611
bool draw_active_side
Definition WM_types.hh:596
uint use_flip
Definition WM_types.hh:617
uint is_active
Definition WM_types.hh:605
rcti winrct
Definition WM_types.hh:589
uint use_smooth
Definition WM_types.hh:620
blender::int2 mval
Definition WM_types.hh:598
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct wmOperatorType * type
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
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)
@ 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
@ RIGHTMOUSE
@ EVT_MODAL_MAP
@ MOUSEMOVE
@ LEFTMOUSE
@ NDOF_MOTION
@ MIDDLEMOUSE
@ EVT_ESCKEY
@ INBETWEEN_MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4125
wmGesture * WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent *event, int type)
Definition wm_gesture.cc:37
void wm_gesture_tag_redraw(wmWindow *win)
void WM_gesture_end(wmWindow *win, wmGesture *gesture)
Definition wm_gesture.cc:98
static int UNUSED_FUNCTION gesture_modal_state_from_operator(wmOperator *op)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static SnapAngle get_snap_angle(const ScrArea &area, const ToolSettings &tool_settings)
static bool gesture_box_apply(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lines_modal(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)
void WM_gesture_polyline_cancel(bContext *C, wmOperator *op)
void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_polyline_modal(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)
static void gesture_circle_apply(bContext *C, wmOperator *op)
static void wm_gesture_straightline_do_angle_snap(rcti *rect, float snap_angle)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int gesture_polyline_apply(bContext *C, wmOperator *op, const bool is_click_submitted)
static bool gesture_box_apply_rect(wmOperator *op)
int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
int WM_gesture_lasso_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)
static int gesture_lasso_apply(bContext *C, wmOperator *op)
int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_active_side_invoke(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)