Blender V5.0
tracking_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "DNA_movieclip_types.h"
12#include "DNA_userdef_types.h"
13
14#include "BLI_lasso_2d.hh"
15#include "BLI_listbase.h"
16#include "BLI_math_base.hh"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
20#include "BLI_rect.h"
21
22#include "BKE_context.hh"
23#include "BKE_tracking.h"
24
25#include "WM_api.hh"
26#include "WM_types.hh"
27
28#include "ED_clip.hh"
29#include "ED_select_utils.hh"
30
31#include "RNA_access.hh"
32#include "RNA_define.hh"
33
34#include "UI_view2d.hh"
35
36#include "DEG_depsgraph.hh"
37
38#include "clip_intern.hh" /* own include */
39#include "tracking_ops_intern.hh" /* own include */
40
41using blender::Array;
42using blender::int2;
43using blender::Span;
44
45namespace math = blender::math;
46
47/* -------------------------------------------------------------------- */
50
52{
53 PointTrackPick pick = {nullptr};
54
55 pick.area = TRACK_AREA_NONE;
57 pick.corner_index = -1;
59
60 return pick;
61}
62
63static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2])
64{
65 add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]);
66}
67
68static void slide_marker_tilt_slider(const float marker_pos[2],
69 const float pattern_corners[4][2],
70 float r_slider[2])
71{
72 slide_marker_tilt_slider_relative(pattern_corners, r_slider);
73 add_v2_v2(r_slider, marker_pos);
74}
75
76static float mouse_to_slide_zone_distance_squared(const float co[2],
77 const float slide_zone[2],
78 int width,
79 int height)
80{
81 const float pixel_co[2] = {co[0] * width, co[1] * height},
82 pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
83 return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
84}
85
87 const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
88{
89 float side_zone[2];
90 if (corner == 0) {
91 side_zone[0] = marker->pos[0] + marker->search_max[0];
92 side_zone[1] = marker->pos[1] + marker->search_min[1];
93 }
94 else {
95 side_zone[0] = marker->pos[0] + marker->search_min[0];
96 side_zone[1] = marker->pos[1] + marker->search_max[1];
97 }
98 return mouse_to_slide_zone_distance_squared(co, side_zone, width, height);
99}
100
102 const MovieTrackingMarker *marker, const float co[2], int width, int height, int *r_corner)
103{
104 float min_distance_squared = FLT_MAX;
105 for (int i = 0; i < 4; i++) {
106 float corner_co[2];
107 add_v2_v2v2(corner_co, marker->pattern_corners[i], marker->pos);
108 float distance_squared = mouse_to_slide_zone_distance_squared(co, corner_co, width, height);
109 if (distance_squared < min_distance_squared) {
110 min_distance_squared = distance_squared;
111 *r_corner = i;
112 }
113 }
114 return min_distance_squared;
115}
116
118 const MovieTrackingMarker *marker,
119 const float co[2],
120 int width,
121 int height)
122{
123 float pos[2];
124 add_v2_v2v2(pos, marker->pos, track->offset);
125 return mouse_to_slide_zone_distance_squared(co, pos, width, height);
126}
127
129 const float co[2],
130 int width,
131 int height)
132{
133 float slider[2];
134 slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider);
135 return mouse_to_slide_zone_distance_squared(co, slider, width, height);
136}
137
139 const float corners_offset[2],
140 const float corners[4][2],
141 int width,
142 int height)
143{
144 const float co_px[2] = {co[0] * width, co[1] * height};
145
146 float prev_corner_co_px[2];
147 add_v2_v2v2(prev_corner_co_px, corners_offset, corners[3]);
148 prev_corner_co_px[0] *= width;
149 prev_corner_co_px[1] *= height;
150
151 float min_distance_squared = FLT_MAX;
152
153 for (int i = 0; i < 4; ++i) {
154 float corner_co_px[2];
155 add_v2_v2v2(corner_co_px, corners_offset, corners[i]);
156 corner_co_px[0] *= width;
157 corner_co_px[1] *= height;
158
159 const float distance_squared = dist_squared_to_line_segment_v2(
160 co_px, corner_co_px, prev_corner_co_px);
161
162 min_distance_squared = std::min(distance_squared, min_distance_squared);
163
164 copy_v2_v2(prev_corner_co_px, corner_co_px);
165 }
166
167 return min_distance_squared;
168}
169
171 const float co[2],
172 int width,
173 int height)
174{
176 co, marker->pos, marker->pattern_corners, width, height);
177}
178
180 const float co[2],
181 int width,
182 int height)
183{
184 const float corners[4][2] = {
185 {marker->search_min[0], marker->search_min[1]},
186 {marker->search_max[0], marker->search_min[1]},
187 {marker->search_max[0], marker->search_max[1]},
188 {marker->search_min[0], marker->search_max[1]},
189 };
190
191 return mouse_to_closest_corners_edge_distance_squared(co, marker->pos, corners, width, height);
192}
193
195 bContext *C,
196 const float co[2])
197{
198 SpaceClip *space_clip = CTX_wm_space_clip(C);
199
200 int width, height;
201 ED_space_clip_get_size(space_clip, &width, &height);
202 if (width == 0 || height == 0) {
204 }
205
206 MovieClip *clip = ED_space_clip_get_clip(space_clip);
208
209 const float distance_tolerance_px_squared = math::square(12.0f / space_clip->zoom *
211 const bool are_disabled_markers_visible = (space_clip->flag & SC_HIDE_DISABLED) == 0;
212 const int framenr = ED_space_clip_get_clip_frame_number(space_clip);
213
215
216 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
217 const bool is_track_selected = TRACK_VIEW_SELECTED(space_clip, track);
218
219 if (options->selected_only && !is_track_selected) {
220 continue;
221 }
222 if (options->unlocked_only && (track->flag & TRACK_LOCKED)) {
223 continue;
224 }
225
226 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
227 const bool is_marker_enabled = ((marker->flag & MARKER_DISABLED) == 0);
228
229 if (!is_marker_enabled) {
230 if (options->enabled_only) {
231 /* Disabled marker is requested to not be in the pick result, so skip it. */
232 continue;
233 }
234
235 /* See whether the disabled marker is visible.
236 *
237 * If the clip editor is not hiding disabled markers, then all disabled markers are visible.
238 * Otherwise only disabled marker of the active track is visible. */
239 if (!are_disabled_markers_visible && track != tracking_object->active_track) {
240 continue;
241 }
242 }
243
244 float distance_squared;
245
246 /* Initialize the current pick with the offset point of the track. */
248 current_pick.track = track;
249 current_pick.marker = marker;
250 current_pick.area = TRACK_AREA_POINT;
252 track, marker, co, width, height);
253
254 /* If search area is visible, check how close to its sliding zones mouse is.
255 * NOTE: The search area is only visible for selected tracks. */
256 if (is_track_selected && (space_clip->flag & SC_SHOW_MARKER_SEARCH)) {
257 distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
258 if (distance_squared < current_pick.distance_px_squared) {
259 current_pick.area = TRACK_AREA_SEARCH;
261 current_pick.distance_px_squared = distance_squared;
262 }
263
264 distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
265 if (distance_squared < current_pick.distance_px_squared) {
266 current_pick.area = TRACK_AREA_SEARCH;
268 current_pick.distance_px_squared = distance_squared;
269 }
270 }
271
272 /* If pattern area is visible, check which corner is closest to the mouse. */
273 if (space_clip->flag & SC_SHOW_MARKER_PATTERN) {
274 int current_corner = -1;
276 marker, co, width, height, &current_corner);
277 if (distance_squared < current_pick.distance_px_squared) {
278 current_pick.area = TRACK_AREA_PAT;
280 current_pick.corner_index = current_corner;
281 current_pick.distance_px_squared = distance_squared;
282 }
283
284 /* Here we also check whether the mouse is actually closer to the widget which controls scale
285 * and tilt.
286 * NOTE: The tilt control is only visible for selected tracks. */
287 if (is_track_selected) {
288 distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
289 if (distance_squared < current_pick.distance_px_squared) {
290 current_pick.area = TRACK_AREA_PAT;
292 current_pick.distance_px_squared = distance_squared;
293 }
294 }
295 }
296
297 /* Whenever a manipulation "widgets" are not within distance tolerance test the edges as well.
298 * This allows to pick tracks by clicking on the pattern/search areas edges but prefer to use
299 * more actionable "widget" for sliding. */
300 if (current_pick.distance_px_squared > distance_tolerance_px_squared) {
301 if (is_track_selected && (space_clip->flag & SC_SHOW_MARKER_SEARCH)) {
303 marker, co, width, height);
304 if (distance_squared < current_pick.distance_px_squared) {
305 current_pick.area = TRACK_AREA_SEARCH;
307 current_pick.distance_px_squared = distance_squared;
308 }
309 }
310
311 if (space_clip->flag & SC_SHOW_MARKER_PATTERN) {
313 marker, co, width, height);
314 if (distance_squared < current_pick.distance_px_squared) {
315 current_pick.area = TRACK_AREA_PAT;
317 current_pick.distance_px_squared = distance_squared;
318 }
319 }
320 }
321
322 if (current_pick.distance_px_squared < pick.distance_px_squared) {
323 pick = current_pick;
324 }
325 }
326
327 if (pick.distance_px_squared > distance_tolerance_px_squared) {
329 }
330
331 return pick;
332}
333
335 const PointTrackPick *pick)
336{
337 if (pick->track == nullptr) {
338 return false;
339 }
340
341 BLI_assert(pick->marker != nullptr);
342
343 if (!TRACK_VIEW_SELECTED(space_clip, pick->track)) {
344 return false;
345 }
346
347 if (pick->track->flag & TRACK_LOCKED) {
348 return false;
349 }
350 if (pick->marker->flag & MARKER_DISABLED) {
351 return false;
352 }
353
355}
356
358
359/* -------------------------------------------------------------------- */
362
364{
365 PlaneTrackPick result = {nullptr};
366
367 result.corner_index = -1;
368 result.distance_px_squared = FLT_MAX;
369
370 return result;
371}
372
373static float mouse_to_plane_slide_zone_distance_squared(const float co[2],
374 const float slide_zone[2],
375 int width,
376 int height)
377{
378 const float pixel_co[2] = {co[0] * width, co[1] * height};
379 const float pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
380 return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
381}
382
384 bContext *C,
385 const float co[2])
386{
387 SpaceClip *space_clip = CTX_wm_space_clip(C);
388
389 int width, height;
390 ED_space_clip_get_size(space_clip, &width, &height);
391 if (width == 0 || height == 0) {
393 }
394
395 MovieClip *clip = ED_space_clip_get_clip(space_clip);
397 const int framenr = ED_space_clip_get_clip_frame_number(space_clip);
398
399 const float distance_tolerance_px_squared = math::square(12.0f / space_clip->zoom *
402
403 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
404 if (options->selected_only && !PLANE_TRACK_VIEW_SELECTED(plane_track)) {
405 continue;
406 }
407
408 MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr);
409
411 current_pick.plane_track = plane_track;
412 current_pick.plane_marker = plane_marker;
413
414 for (int i = 0; i < 4; i++) {
415 const float distance_squared = mouse_to_plane_slide_zone_distance_squared(
416 co, plane_marker->corners[i], width, height);
417
418 if (distance_squared < current_pick.distance_px_squared) {
419 current_pick.corner_index = i;
420 current_pick.distance_px_squared = distance_squared;
421 }
422 }
423
424 if (current_pick.distance_px_squared > distance_tolerance_px_squared) {
425 const float zero_offset[2] = {0.0f, 0.0f};
426 const float distance_squared = mouse_to_closest_corners_edge_distance_squared(
427 co, zero_offset, plane_marker->corners, width, height);
428 if (distance_squared < current_pick.distance_px_squared) {
429 current_pick.corner_index = -1;
430 current_pick.distance_px_squared = distance_squared;
431 }
432 }
433
434 if (current_pick.distance_px_squared < pick.distance_px_squared) {
435 pick = current_pick;
436 }
437 }
438
439 if (pick.distance_px_squared > distance_tolerance_px_squared) {
441 }
442
443 return pick;
444}
445
447{
448 if (pick->plane_track == nullptr) {
449 return false;
450 }
451
452 BLI_assert(pick->plane_marker != nullptr);
453
455 return false;
456 }
457
458 return pick->corner_index != -1;
459}
460
462
463/* -------------------------------------------------------------------- */
466
468{
470
471 result.point_track_pick = point_track_pick_make_null();
472 result.plane_track_pick = plane_track_pick_make_null();
473
474 return result;
475}
476
478 const PointTrackPick *point_track_pick,
479 const PlaneTrackPick *plane_track_pick)
480{
481 /* Simple case: one of the pick results is empty, so prefer the other one. */
482 if (point_track_pick->track == nullptr) {
483 return false;
484 }
485 if (plane_track_pick->plane_track == nullptr) {
486 return true;
487 }
488
489 SpaceClip *space_clip = CTX_wm_space_clip(C);
490
491 /* If one of the picks can be slid prefer it. */
492 const bool can_slide_point_track = ed_tracking_point_track_pick_can_slide(space_clip,
493 point_track_pick);
494 const bool can_slide_plane_track = ed_tracking_plane_track_pick_can_slide(plane_track_pick);
495 if (can_slide_point_track && !can_slide_plane_track) {
496 return true;
497 }
498 if (!can_slide_point_track && can_slide_plane_track) {
499 return false;
500 }
501
502 /* Prefer the closest pick. */
503 if (point_track_pick->distance_px_squared > plane_track_pick->distance_px_squared) {
504 return false;
505 }
506 return true;
507}
508
527
529
530/********************** mouse select operator *********************/
531
533{
534 LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_base) {
536 }
537}
538
540{
541 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) {
542 plane_track->flag &= ~SELECT;
543 }
544}
545
546static bool select_poll(bContext *C)
547{
549
550 if (sc) {
551 return sc->clip && sc->view == SC_VIEW_CLIP;
552 }
553
554 return false;
555}
556
558{
561 MovieTracking *tracking = &clip->tracking;
562 MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
563 const bool extend = RNA_boolean_get(op->ptr, "extend");
564 const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
565
566 float co[2];
567 RNA_float_get_array(op->ptr, "location", co);
568
571
572 /* Special code which allows to slide a marker which belongs to currently selected but not yet
573 * active track. If such track is found activate it and return pass-though so that marker slide
574 * operator can be used immediately after.
575 * This logic makes it convenient to slide markers when left mouse selection is used. Without it
576 * selection will be lost which causes inconvenience for the VFX artist. */
577 if (!extend && ed_tracking_pick_can_slide(sc, &pick)) {
578 if (pick.point_track_pick.track != nullptr) {
579 tracking_object->active_track = pick.point_track_pick.track;
580 tracking_object->active_plane_track = nullptr;
581 }
582 else {
583 tracking_object->active_track = nullptr;
584 tracking_object->active_plane_track = pick.plane_track_pick.plane_track;
585 }
586
589
591 }
592
593 ClipViewLockState lock_state;
594 ED_clip_view_lock_state_store(C, &lock_state);
595
596 if (pick.point_track_pick.track != nullptr) {
597 if (!extend) {
599 }
600
602 int area = pick.point_track_pick.area;
603
604 if (!extend || !TRACK_VIEW_SELECTED(sc, track)) {
605 area = TRACK_AREA_ALL;
606 }
607
608 if (extend && TRACK_AREA_SELECTED(track, area)) {
609 if (track == tracking_object->active_track) {
610 BKE_tracking_track_deselect(track, area);
611 }
612 else {
613 tracking_object->active_track = track;
614 tracking_object->active_plane_track = nullptr;
615 }
616 }
617 else {
618 if (area == TRACK_AREA_POINT) {
619 area = TRACK_AREA_ALL;
620 }
621
622 BKE_tracking_track_select(&tracking_object->tracks, track, area, extend);
623 tracking_object->active_track = track;
624 tracking_object->active_plane_track = nullptr;
625 }
626 }
627 else if (pick.plane_track_pick.plane_track != nullptr) {
628 if (!extend) {
629 ed_tracking_deselect_all_tracks(&tracking_object->tracks);
630 }
631
633
634 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
635 if (extend) {
636 plane_track->flag &= ~SELECT;
637 }
638 }
639 else {
640 plane_track->flag |= SELECT;
641 }
642
643 tracking_object->active_track = nullptr;
644 tracking_object->active_plane_track = plane_track;
645 }
646 else if (deselect_all) {
647 ed_tracking_deselect_all_tracks(&tracking_object->tracks);
649 }
650
652
654
657
658 /* Pass-through + finished to allow tweak to transform. */
660}
661
663{
665 ARegion *region = CTX_wm_region(C);
666
667 float co[2];
668 ED_clip_mouse_pos(sc, region, event->mval, co);
669 RNA_float_set_array(op->ptr, "location", co);
670
671 return select_exec(C, op);
672}
673
675{
676 /* identifiers */
677 ot->name = "Select";
678 ot->description = "Select tracking markers";
679 ot->idname = "CLIP_OT_select";
680
681 /* API callbacks. */
682 ot->exec = select_exec;
683 ot->invoke = select_invoke;
684 ot->poll = select_poll;
685
686 /* flags */
687 ot->flag = OPTYPE_UNDO;
688
689 /* properties */
690 PropertyRNA *prop;
691 prop = RNA_def_boolean(ot->srna,
692 "extend",
693 false,
694 "Extend",
695 "Extend selection rather than clearing the existing selection");
697 prop = RNA_def_boolean(ot->srna,
698 "deselect_all",
699 false,
700 "Deselect On Nothing",
701 "Deselect all when nothing under the cursor");
703
705 ot->srna,
706 "location",
707 2,
708 nullptr,
709 -FLT_MAX,
710 FLT_MAX,
711 "Location",
712 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
713 -100.0f,
714 100.0f);
715}
716
718{
719 /* To avoid conflicts with mask select deselect all in empty space. */
720 return select_poll(C);
721}
722
723/********************** box select operator *********************/
724
726{
728 ARegion *region = CTX_wm_region(C);
729
731 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
732 rcti rect;
733 rctf rectf;
734 bool changed = false;
735 int framenr = ED_space_clip_get_clip_frame_number(sc);
736
737 /* get rectangle from operator */
739
740 ED_clip_point_stable_pos(sc, region, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
741 ED_clip_point_stable_pos(sc, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
742
743 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
744 const bool select = (sel_op != SEL_OP_SUB);
745 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
746 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
747 changed = true;
748 }
749
750 /* do actual selection */
751 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
752 if (track->flag & TRACK_HIDDEN) {
753 continue;
754 }
755
756 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
757
758 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker)) {
759 if (BLI_rctf_isect_pt_v(&rectf, marker->pos)) {
760 if (select) {
762 }
763 else {
765 }
766 }
767 changed = true;
768 }
769 }
770
771 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
772 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
773 continue;
774 }
775
776 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
777 framenr);
778
779 for (int i = 0; i < 4; i++) {
780 if (BLI_rctf_isect_pt_v(&rectf, plane_marker->corners[i])) {
781 if (select) {
782 plane_track->flag |= SELECT;
783 }
784 else {
785 plane_track->flag &= ~SELECT;
786 }
787 }
788 }
789 changed = true;
790 }
791
792 if (changed) {
794
797
798 return OPERATOR_FINISHED;
799 }
800
801 return OPERATOR_CANCELLED;
802}
803
805{
806 /* identifiers */
807 ot->name = "Box Select";
808 ot->description = "Select markers using box selection";
809 ot->idname = "CLIP_OT_select_box";
810
811 /* API callbacks. */
812 ot->invoke = WM_gesture_box_invoke;
813 ot->exec = box_select_exec;
814 ot->modal = WM_gesture_box_modal;
816
817 /* flags */
818 ot->flag = OPTYPE_UNDO;
819
820 /* properties */
823}
824
825/********************** lasso select operator *********************/
826
827static int do_lasso_select_marker(bContext *C, const Span<int2> mcoords, bool select)
828{
830 ARegion *region = CTX_wm_region(C);
831
833 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
834 rcti rect;
835 bool changed = false;
836 const int framenr = ED_space_clip_get_clip_frame_number(sc);
837
838 /* get rectangle from operator */
839 BLI_lasso_boundbox(&rect, mcoords);
840
841 /* do actual selection */
842 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
843 if (track->flag & TRACK_HIDDEN) {
844 continue;
845 }
846
847 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
848
849 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker)) {
850 float screen_co[2];
851
852 /* marker in screen coords */
853 ED_clip_point_stable_pos__reverse(sc, region, marker->pos, screen_co);
854
855 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
856 BLI_lasso_is_point_inside(mcoords, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
857 {
858 if (select) {
860 }
861 else {
863 }
864 }
865
866 changed = true;
867 }
868 }
869
870 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
871 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
872 continue;
873 }
874
875 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
876 framenr);
877
878 for (int i = 0; i < 4; i++) {
879 float screen_co[2];
880
881 /* marker in screen coords */
882 ED_clip_point_stable_pos__reverse(sc, region, plane_marker->corners[i], screen_co);
883
884 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
885 BLI_lasso_is_point_inside(mcoords, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
886 {
887 if (select) {
888 plane_track->flag |= SELECT;
889 }
890 else {
891 plane_track->flag &= ~SELECT;
892 }
893 }
894 }
895
896 changed = true;
897 }
898
899 if (changed) {
901
904 }
905
906 return changed;
907}
908
910{
911 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
912
913 if (mcoords.is_empty()) {
915 }
916
917 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
918 const bool select = (sel_op != SEL_OP_SUB);
919 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
921 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
922 }
923
925
926 return OPERATOR_FINISHED;
927}
928
930{
931 /* identifiers */
932 ot->name = "Lasso Select";
933 ot->description = "Select markers using lasso selection";
934 ot->idname = "CLIP_OT_select_lasso";
935
936 /* API callbacks. */
937 ot->invoke = WM_gesture_lasso_invoke;
938 ot->modal = WM_gesture_lasso_modal;
941 ot->cancel = WM_gesture_lasso_cancel;
942
943 /* flags */
945
946 /* properties */
949}
950
951/********************** circle select operator *********************/
952
953static int point_inside_ellipse(const float point[2],
954 const float offset[2],
955 const float ellipse[2])
956{
957 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
958 float x, y;
959
960 x = (point[0] - offset[0]) * ellipse[0];
961 y = (point[1] - offset[1]) * ellipse[1];
962
963 return x * x + y * y < 1.0f;
964}
965
967 const float offset[2],
968 const float ellipse[2])
969{
970 return point_inside_ellipse(marker->pos, offset, ellipse);
971}
972
974{
976 ARegion *region = CTX_wm_region(C);
977
979 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
980 int width, height;
981 bool changed = false;
982 float zoomx, zoomy, offset[2], ellipse[2];
983 int framenr = ED_space_clip_get_clip_frame_number(sc);
984
985 /* get operator properties */
986 const int x = RNA_int_get(op->ptr, "x");
987 const int y = RNA_int_get(op->ptr, "y");
988 const int radius = RNA_int_get(op->ptr, "radius");
989
990 const eSelectOp sel_op = ED_select_op_modal(
991 eSelectOp(RNA_enum_get(op->ptr, "mode")),
992 WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
993 const bool select = (sel_op != SEL_OP_SUB);
994 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
995 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
996 changed = true;
997 }
998
999 /* compute ellipse and position in unified coordinates */
1000 ED_space_clip_get_size(sc, &width, &height);
1001 ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
1002
1003 ellipse[0] = width * zoomx / radius;
1004 ellipse[1] = height * zoomy / radius;
1005
1006 ED_clip_point_stable_pos(sc, region, x, y, &offset[0], &offset[1]);
1007
1008 /* do selection */
1009 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
1010 if (track->flag & TRACK_HIDDEN) {
1011 continue;
1012 }
1013
1014 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1015
1016 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker) &&
1017 marker_inside_ellipse(marker, offset, ellipse))
1018 {
1019 if (select) {
1021 }
1022 else {
1024 }
1025 changed = true;
1026 }
1027 }
1028
1029 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
1030 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
1031 continue;
1032 }
1033
1034 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
1035 framenr);
1036
1037 for (int i = 0; i < 4; i++) {
1038 if (point_inside_ellipse(plane_marker->corners[i], offset, ellipse)) {
1039 if (select) {
1040 plane_track->flag |= SELECT;
1041 }
1042 else {
1043 plane_track->flag &= ~SELECT;
1044 }
1045 }
1046 }
1047
1048 changed = true;
1049 }
1050
1051 if (changed) {
1053
1056
1057 return OPERATOR_FINISHED;
1058 }
1059
1060 return OPERATOR_CANCELLED;
1061}
1062
1064{
1065 /* identifiers */
1066 ot->name = "Circle Select";
1067 ot->description = "Select markers using circle selection";
1068 ot->idname = "CLIP_OT_select_circle";
1069
1070 /* API callbacks. */
1071 ot->invoke = WM_gesture_circle_invoke;
1072 ot->modal = WM_gesture_circle_modal;
1073 ot->exec = circle_select_exec;
1075 ot->get_name = ED_select_circle_get_name;
1076
1077 /* flags */
1078 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1079
1080 /* properties */
1083}
1084
1085/********************** select all operator *********************/
1086
1088{
1091 MovieTracking *tracking = &clip->tracking;
1092
1093 const int action = RNA_enum_get(op->ptr, "action");
1094
1095 ClipViewLockState lock_state;
1096 ED_clip_view_lock_state_store(C, &lock_state);
1097
1098 bool has_selection = false;
1099 ED_clip_select_all(sc, action, &has_selection);
1100
1101 if (has_selection) {
1103 }
1104
1106
1109
1110 return OPERATOR_FINISHED;
1111}
1112
1114{
1115 /* identifiers */
1116 ot->name = "(De)select All";
1117 ot->description = "Change selection of all tracking markers";
1118 ot->idname = "CLIP_OT_select_all";
1119
1120 /* API callbacks. */
1121 ot->exec = select_all_exec;
1123
1124 /* flags */
1125 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1126
1128}
1129
1130/********************** select grouped operator *********************/
1131
1133{
1136 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
1137 const int group = RNA_enum_get(op->ptr, "group");
1138 const int framenr = ED_space_clip_get_clip_frame_number(sc);
1139
1140 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
1141 bool ok = false;
1142
1143 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1144
1145 if (group == 0) { /* Keyframed */
1146 ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED) == 0;
1147 }
1148 else if (group == 1) { /* Estimated */
1149 ok = marker->framenr != framenr;
1150 }
1151 else if (group == 2) { /* tracked */
1152 ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED);
1153 }
1154 else if (group == 3) { /* locked */
1155 ok = track->flag & TRACK_LOCKED;
1156 }
1157 else if (group == 4) { /* disabled */
1158 ok = marker->flag & MARKER_DISABLED;
1159 }
1160 else if (group == 5) { /* color */
1161 const MovieTrackingTrack *act_track = tracking_object->active_track;
1162
1163 if (act_track) {
1164 ok = (track->flag & TRACK_CUSTOMCOLOR) == (act_track->flag & TRACK_CUSTOMCOLOR);
1165
1166 if (ok && track->flag & TRACK_CUSTOMCOLOR) {
1167 ok = equals_v3v3(track->color, act_track->color);
1168 }
1169 }
1170 }
1171 else if (group == 6) { /* failed */
1172 ok = (track->flag & TRACK_HAS_BUNDLE) == 0;
1173 }
1174
1175 if (ok) {
1176 track->flag |= SELECT;
1177 if (sc->flag & SC_SHOW_MARKER_PATTERN) {
1178 track->pat_flag |= SELECT;
1179 }
1180 if (sc->flag & SC_SHOW_MARKER_SEARCH) {
1181 track->search_flag |= SELECT;
1182 }
1183 }
1184 }
1185
1187
1190
1191 return OPERATOR_FINISHED;
1192}
1193
1195{
1196 static const EnumPropertyItem select_group_items[] = {
1197 {0, "KEYFRAMED", 0, "Keyframed Tracks", "Select all keyframed tracks"},
1198 {1, "ESTIMATED", 0, "Estimated Tracks", "Select all estimated tracks"},
1199 {2, "TRACKED", 0, "Tracked Tracks", "Select all tracked tracks"},
1200 {3, "LOCKED", 0, "Locked Tracks", "Select all locked tracks"},
1201 {4, "DISABLED", 0, "Disabled Tracks", "Select all disabled tracks"},
1202 {5,
1203 "COLOR",
1204 0,
1205 "Tracks with Same Color",
1206 "Select all tracks with same color as active track"},
1207 {6, "FAILED", 0, "Failed Tracks", "Select all tracks which failed to be reconstructed"},
1208 {0, nullptr, 0, nullptr, nullptr},
1209 };
1210
1211 /* identifiers */
1212 ot->name = "Select Grouped";
1213 ot->description = "Select all tracks from specified group";
1214 ot->idname = "CLIP_OT_select_grouped";
1215
1216 /* API callbacks. */
1217 ot->exec = select_grouped_exec;
1219
1220 /* flags */
1221 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1222
1223 /* properties */
1224 RNA_def_enum(ot->srna,
1225 "group",
1226 select_group_items,
1228 "Action",
1229 "Clear action to execute");
1230}
SpaceClip * CTX_wm_space_clip(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void BKE_tracking_track_deselect(struct MovieTrackingTrack *track, int area)
Definition tracking.cc:1217
void BKE_tracking_track_flag_clear(struct MovieTrackingTrack *track, int area, int flag)
Definition tracking.cc:692
#define PLANE_TRACK_VIEW_SELECTED(plane_track)
@ TRACK_AREA_POINT
@ TRACK_AREA_ALL
@ TRACK_AREA_PAT
@ TRACK_AREA_SEARCH
@ TRACK_AREA_NONE
void BKE_tracking_track_flag_set(struct MovieTrackingTrack *track, int area, int flag)
Definition tracking.cc:675
struct MovieTrackingMarker * BKE_tracking_marker_get(struct MovieTrackingTrack *track, int framenr)
Definition tracking.cc:1358
struct MovieTrackingObject * BKE_tracking_object_get_active(const struct MovieTracking *tracking)
void BKE_tracking_dopesheet_tag_update(struct MovieTracking *tracking)
Definition tracking.cc:3439
void BKE_tracking_track_select(struct ListBase *tracksbase, struct MovieTrackingTrack *track, int area, bool extend)
Definition tracking.cc:1190
@ TRACK_CLEAR_REMAINED
#define TRACK_VIEW_SELECTED(sc, track)
struct MovieTrackingPlaneMarker * BKE_tracking_plane_marker_get(struct MovieTrackingPlaneTrack *plane_track, int framenr)
Definition tracking.cc:1791
#define TRACK_AREA_SELECTED(track, area)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
#define LISTBASE_FOREACH(type, var, list)
MINLINE float square_f(float a)
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ SC_VIEW_CLIP
@ SC_HIDE_DISABLED
@ SC_SHOW_MARKER_SEARCH
@ SC_SHOW_MARKER_PATTERN
#define UI_SCALE_FAC
@ TRACK_CUSTOMCOLOR
@ TRACK_HIDDEN
@ TRACK_LOCKED
@ TRACK_HAS_BUNDLE
@ MARKER_TRACKED
@ MARKER_DISABLED
@ PLANE_TRACK_HIDDEN
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void ED_clip_view_lock_state_restore_no_jump(const bContext *C, const ClipViewLockState *state)
void ED_clip_point_stable_pos(const SpaceClip *sc, const ARegion *region, float x, float y, float *xr, float *yr)
MovieClip * ED_space_clip_get_clip(const SpaceClip *sc)
void ED_clip_mouse_pos(const SpaceClip *sc, const ARegion *region, const int mval[2], float r_co[2])
void ED_space_clip_get_size(const SpaceClip *sc, int *r_width, int *r_height)
void ED_space_clip_get_zoom(const SpaceClip *sc, const ARegion *region, float *r_zoomx, float *r_zoomy)
int ED_space_clip_get_clip_frame_number(const SpaceClip *sc)
void ED_clip_view_lock_state_store(const bContext *C, ClipViewLockState *state)
void ED_clip_select_all(const SpaceClip *sc, int action, bool *r_has_selection)
bool ED_space_clip_tracking_poll(bContext *C)
void ED_clip_point_stable_pos__reverse(const SpaceClip *sc, const ARegion *region, const float co[2], float r_co[2])
the reverse of ED_clip_point_stable_pos(), gets the marker region coords. better name here?...
eSelectOp
@ SEL_OP_SUB
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
@ SEL_DESELECT
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DISPLAY
Definition WM_types.hh:491
#define NC_MOVIECLIP
Definition WM_types.hh:397
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
bool is_empty() const
Definition BLI_array.hh:264
BLI_INLINE bool ED_space_clip_marker_is_visible(const SpaceClip *space_clip, const MovieTrackingObject *tracking_object, const MovieTrackingTrack *track, const MovieTrackingMarker *marker)
#define SELECT
CCL_NAMESPACE_BEGIN struct Options options
uint pos
#define select(A, B, C)
static wmOperatorStatus box_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
static wmOperatorStatus circle_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus clip_lasso_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
T square(const T &a)
VecBase< int32_t, 2 > int2
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
#define FLT_MAX
Definition stdcycles.h:14
struct MovieTracking tracking
MovieTrackingPlaneTrack * active_plane_track
MovieTrackingTrack * active_track
MovieTrackingPlaneTrack * plane_track
MovieTrackingPlaneMarker * plane_marker
MovieTrackingMarker * marker
eTrackPickAreaDetail area_detail
MovieTrackingTrack * track
struct MovieClip * clip
PlaneTrackPick plane_track_pick
PointTrackPick point_track_pick
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
int mval[2]
Definition WM_types.hh:763
struct PointerRNA * ptr
i
Definition text_draw.cc:230
@ TRACK_PICK_AREA_DETAIL_EDGE
@ TRACK_PICK_AREA_DETAIL_POSITION
@ TRACK_PICK_AREA_DETAIL_TILT_SIZE
@ TRACK_PICK_AREA_DETAIL_NONE
@ TRACK_PICK_AREA_DETAIL_SIZE
@ TRACK_PICK_AREA_DETAIL_OFFSET
BLI_INLINE bool ed_tracking_pick_can_slide(const SpaceClip *space_clip, const TrackingPick *pick)
BLI_INLINE TrackPickOptions ed_tracking_pick_options_defaults()
static wmOperatorStatus box_select_exec(bContext *C, wmOperator *op)
static float mouse_to_offset_distance_squared(const MovieTrackingTrack *track, const MovieTrackingMarker *marker, const float co[2], int width, int height)
static wmOperatorStatus select_grouped_exec(bContext *C, wmOperator *op)
static float mouse_to_closest_search_edge_distance_squared(const MovieTrackingMarker *marker, const float co[2], int width, int height)
static float mouse_to_slide_zone_distance_squared(const float co[2], const float slide_zone[2], int width, int height)
BLI_INLINE PlaneTrackPick plane_track_pick_make_null()
TrackingPick ed_tracking_pick_closest(const TrackPickOptions *options, bContext *C, const float co[2])
static float mouse_to_closest_pattern_edge_distance_squared(const MovieTrackingMarker *marker, const float co[2], int width, int height)
static float mouse_to_plane_slide_zone_distance_squared(const float co[2], const float slide_zone[2], int width, int height)
static float mouse_to_closest_pattern_corner_distance_squared(const MovieTrackingMarker *marker, const float co[2], int width, int height, int *r_corner)
void CLIP_OT_select_circle(wmOperatorType *ot)
void ed_tracking_deselect_all_tracks(ListBase *tracks_base)
static float mouse_to_search_corner_distance_squared(const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
static wmOperatorStatus select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
static float mouse_to_closest_corners_edge_distance_squared(const float co[2], const float corners_offset[2], const float corners[4][2], int width, int height)
static int do_lasso_select_marker(bContext *C, const Span< int2 > mcoords, bool select)
void CLIP_OT_select_lasso(wmOperatorType *ot)
void CLIP_OT_select_all(wmOperatorType *ot)
bool ED_clip_can_select(bContext *C)
static bool tracking_should_prefer_point_track(bContext *C, const PointTrackPick *point_track_pick, const PlaneTrackPick *plane_track_pick)
void CLIP_OT_select_box(wmOperatorType *ot)
static wmOperatorStatus circle_select_exec(bContext *C, wmOperator *op)
BLI_INLINE TrackingPick tracking_pick_make_null()
static int point_inside_ellipse(const float point[2], const float offset[2], const float ellipse[2])
static wmOperatorStatus clip_lasso_select_exec(bContext *C, wmOperator *op)
static int marker_inside_ellipse(const MovieTrackingMarker *marker, const float offset[2], const float ellipse[2])
bool ed_tracking_plane_track_pick_can_slide(const PlaneTrackPick *pick)
static float mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker, const float co[2], int width, int height)
void CLIP_OT_select_grouped(wmOperatorType *ot)
PlaneTrackPick ed_tracking_pick_plane_track(const TrackPickOptions *options, bContext *C, const float co[2])
BLI_INLINE PointTrackPick point_track_pick_make_null()
static wmOperatorStatus select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CLIP_OT_select(wmOperatorType *ot)
void ed_tracking_deselect_all_plane_tracks(ListBase *plane_tracks_base)
static void slide_marker_tilt_slider(const float marker_pos[2], const float pattern_corners[4][2], float r_slider[2])
PointTrackPick ed_tracking_pick_point_track(const TrackPickOptions *options, bContext *C, const float co[2])
bool ed_tracking_point_track_pick_can_slide(const SpaceClip *space_clip, const PointTrackPick *pick)
static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2])
static bool select_poll(bContext *C)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
bool WM_gesture_is_modal_first(const wmGesture *gesture)
wmOperatorStatus WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_border_to_rcti(wmOperator *op, rcti *r_rect)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)