Blender V4.3
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
9#include "MEM_guardedalloc.h"
10
11#include "DNA_movieclip_types.h"
12#include "DNA_scene_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#include "BLI_utildefines.h"
22
23#include "BKE_context.hh"
24#include "BKE_tracking.h"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "ED_clip.hh"
30#include "ED_select_utils.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34
35#include "UI_view2d.hh"
36
37#include "DEG_depsgraph.hh"
38
39#include "clip_intern.hh" /* own include */
40#include "tracking_ops_intern.hh" /* own include */
41
42using blender::Array;
43using blender::int2;
44using blender::Span;
45
46namespace math = blender::math;
47
48/* -------------------------------------------------------------------- */
53{
54 PointTrackPick pick = {nullptr};
55
56 pick.area = TRACK_AREA_NONE;
58 pick.corner_index = -1;
60
61 return pick;
62}
63
64static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2])
65{
66 add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]);
67}
68
69static void slide_marker_tilt_slider(const float marker_pos[2],
70 const float pattern_corners[4][2],
71 float r_slider[2])
72{
73 slide_marker_tilt_slider_relative(pattern_corners, r_slider);
74 add_v2_v2(r_slider, marker_pos);
75}
76
77static float mouse_to_slide_zone_distance_squared(const float co[2],
78 const float slide_zone[2],
79 int width,
80 int height)
81{
82 const float pixel_co[2] = {co[0] * width, co[1] * height},
83 pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
84 return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
85}
86
88 const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
89{
90 float side_zone[2];
91 if (corner == 0) {
92 side_zone[0] = marker->pos[0] + marker->search_max[0];
93 side_zone[1] = marker->pos[1] + marker->search_min[1];
94 }
95 else {
96 side_zone[0] = marker->pos[0] + marker->search_min[0];
97 side_zone[1] = marker->pos[1] + marker->search_max[1];
98 }
99 return mouse_to_slide_zone_distance_squared(co, side_zone, width, height);
100}
101
103 const MovieTrackingMarker *marker, const float co[2], int width, int height, int *r_corner)
104{
105 float min_distance_squared = FLT_MAX;
106 for (int i = 0; i < 4; i++) {
107 float corner_co[2];
108 add_v2_v2v2(corner_co, marker->pattern_corners[i], marker->pos);
109 float distance_squared = mouse_to_slide_zone_distance_squared(co, corner_co, width, height);
110 if (distance_squared < min_distance_squared) {
111 min_distance_squared = distance_squared;
112 *r_corner = i;
113 }
114 }
115 return min_distance_squared;
116}
117
119 const MovieTrackingMarker *marker,
120 const float co[2],
121 int width,
122 int height)
123{
124 float pos[2];
125 add_v2_v2v2(pos, marker->pos, track->offset);
126 return mouse_to_slide_zone_distance_squared(co, pos, width, height);
127}
128
130 const float co[2],
131 int width,
132 int height)
133{
134 float slider[2];
135 slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider);
136 return mouse_to_slide_zone_distance_squared(co, slider, width, height);
137}
138
140 const float corners_offset[2],
141 const float corners[4][2],
142 int width,
143 int height)
144{
145 const float co_px[2] = {co[0] * width, co[1] * height};
146
147 float prev_corner_co_px[2];
148 add_v2_v2v2(prev_corner_co_px, corners_offset, corners[3]);
149 prev_corner_co_px[0] *= width;
150 prev_corner_co_px[1] *= height;
151
152 float min_distance_squared = FLT_MAX;
153
154 for (int i = 0; i < 4; ++i) {
155 float corner_co_px[2];
156 add_v2_v2v2(corner_co_px, corners_offset, corners[i]);
157 corner_co_px[0] *= width;
158 corner_co_px[1] *= height;
159
160 const float distance_squared = dist_squared_to_line_segment_v2(
161 co_px, corner_co_px, prev_corner_co_px);
162
163 if (distance_squared < min_distance_squared) {
164 min_distance_squared = distance_squared;
165 }
166
167 copy_v2_v2(prev_corner_co_px, corner_co_px);
168 }
169
170 return min_distance_squared;
171}
172
174 const float co[2],
175 int width,
176 int height)
177{
179 co, marker->pos, marker->pattern_corners, width, height);
180}
181
183 const float co[2],
184 int width,
185 int height)
186{
187 const float corners[4][2] = {
188 {marker->search_min[0], marker->search_min[1]},
189 {marker->search_max[0], marker->search_min[1]},
190 {marker->search_max[0], marker->search_max[1]},
191 {marker->search_min[0], marker->search_max[1]},
192 };
193
194 return mouse_to_closest_corners_edge_distance_squared(co, marker->pos, corners, width, height);
195}
196
198 bContext *C,
199 const float co[2])
200{
201 SpaceClip *space_clip = CTX_wm_space_clip(C);
202
203 int width, height;
204 ED_space_clip_get_size(space_clip, &width, &height);
205 if (width == 0 || height == 0) {
207 }
208
209 MovieClip *clip = ED_space_clip_get_clip(space_clip);
210 MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
211
212 const float distance_tolerance_px_squared = math::square(12.0f / space_clip->zoom *
214 const bool are_disabled_markers_visible = (space_clip->flag & SC_HIDE_DISABLED) == 0;
215 const int framenr = ED_space_clip_get_clip_frame_number(space_clip);
216
218
219 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
220 const bool is_track_selected = TRACK_VIEW_SELECTED(space_clip, track);
221
222 if (options->selected_only && !is_track_selected) {
223 continue;
224 }
225 if (options->unlocked_only && (track->flag & TRACK_LOCKED)) {
226 continue;
227 }
228
229 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
230 const bool is_marker_enabled = ((marker->flag & MARKER_DISABLED) == 0);
231
232 if (!is_marker_enabled) {
233 if (options->enabled_only) {
234 /* Disabled marker is requested to not be in the pick result, so skip it. */
235 continue;
236 }
237
238 /* See whether the disabled marker is visible.
239 *
240 * If the clip editor is not hiding disabled markers, then all disabled markers are visible.
241 * Otherwise only disabled marker of the active track is visible. */
242 if (!are_disabled_markers_visible && track != tracking_object->active_track) {
243 continue;
244 }
245 }
246
247 float distance_squared;
248
249 /* Initialize the current pick with the offset point of the track. */
251 current_pick.track = track;
252 current_pick.marker = marker;
253 current_pick.area = TRACK_AREA_POINT;
255 track, marker, co, width, height);
256
257 /* If search area is visible, check how close to its sliding zones mouse is.
258 * NOTE: The search area is only visible for selected tracks. */
259 if (is_track_selected && (space_clip->flag & SC_SHOW_MARKER_SEARCH)) {
260 distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
261 if (distance_squared < current_pick.distance_px_squared) {
262 current_pick.area = TRACK_AREA_SEARCH;
264 current_pick.distance_px_squared = distance_squared;
265 }
266
267 distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
268 if (distance_squared < current_pick.distance_px_squared) {
269 current_pick.area = TRACK_AREA_SEARCH;
271 current_pick.distance_px_squared = distance_squared;
272 }
273 }
274
275 /* If pattern area is visible, check which corner is closest to the mouse. */
276 if (space_clip->flag & SC_SHOW_MARKER_PATTERN) {
277 int current_corner = -1;
279 marker, co, width, height, &current_corner);
280 if (distance_squared < current_pick.distance_px_squared) {
281 current_pick.area = TRACK_AREA_PAT;
283 current_pick.corner_index = current_corner;
284 current_pick.distance_px_squared = distance_squared;
285 }
286
287 /* Here we also check whether the mouse is actually closer to the widget which controls scale
288 * and tilt.
289 * NOTE: The tilt control is only visible for selected tracks. */
290 if (is_track_selected) {
291 distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
292 if (distance_squared < current_pick.distance_px_squared) {
293 current_pick.area = TRACK_AREA_PAT;
295 current_pick.distance_px_squared = distance_squared;
296 }
297 }
298 }
299
300 /* Whenever a manipulation "widgets" are not within distance tolerance test the edges as well.
301 * This allows to pick tracks by clicking on the pattern/search areas edges but prefer to use
302 * more actionable "widget" for sliding. */
303 if (current_pick.distance_px_squared > distance_tolerance_px_squared) {
304 if (is_track_selected && (space_clip->flag & SC_SHOW_MARKER_SEARCH)) {
306 marker, co, width, height);
307 if (distance_squared < current_pick.distance_px_squared) {
308 current_pick.area = TRACK_AREA_SEARCH;
310 current_pick.distance_px_squared = distance_squared;
311 }
312 }
313
314 if (space_clip->flag & SC_SHOW_MARKER_PATTERN) {
316 marker, co, width, height);
317 if (distance_squared < current_pick.distance_px_squared) {
318 current_pick.area = TRACK_AREA_PAT;
320 current_pick.distance_px_squared = distance_squared;
321 }
322 }
323 }
324
325 if (current_pick.distance_px_squared < pick.distance_px_squared) {
326 pick = current_pick;
327 }
328 }
329
330 if (pick.distance_px_squared > distance_tolerance_px_squared) {
332 }
333
334 return pick;
335}
336
338 const PointTrackPick *pick)
339{
340 if (pick->track == nullptr) {
341 return false;
342 }
343
344 BLI_assert(pick->marker != nullptr);
345
346 if (!TRACK_VIEW_SELECTED(space_clip, pick->track)) {
347 return false;
348 }
349
350 if (pick->track->flag & TRACK_LOCKED) {
351 return false;
352 }
353 if (pick->marker->flag & MARKER_DISABLED) {
354 return false;
355 }
356
358}
359
362/* -------------------------------------------------------------------- */
367{
368 PlaneTrackPick result = {nullptr};
369
370 result.corner_index = -1;
371 result.distance_px_squared = FLT_MAX;
372
373 return result;
374}
375
376static float mouse_to_plane_slide_zone_distance_squared(const float co[2],
377 const float slide_zone[2],
378 int width,
379 int height)
380{
381 const float pixel_co[2] = {co[0] * width, co[1] * height};
382 const float pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
383 return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
384}
385
387 bContext *C,
388 const float co[2])
389{
390 SpaceClip *space_clip = CTX_wm_space_clip(C);
391
392 int width, height;
393 ED_space_clip_get_size(space_clip, &width, &height);
394 if (width == 0 || height == 0) {
396 }
397
398 MovieClip *clip = ED_space_clip_get_clip(space_clip);
399 MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
400 const int framenr = ED_space_clip_get_clip_frame_number(space_clip);
401
402 const float distance_tolerance_px_squared = math::square(12.0f / space_clip->zoom *
405
406 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
407 if (options->selected_only && !PLANE_TRACK_VIEW_SELECTED(plane_track)) {
408 continue;
409 }
410
411 MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr);
412
414 current_pick.plane_track = plane_track;
415 current_pick.plane_marker = plane_marker;
416
417 for (int i = 0; i < 4; i++) {
418 const float distance_squared = mouse_to_plane_slide_zone_distance_squared(
419 co, plane_marker->corners[i], width, height);
420
421 if (distance_squared < current_pick.distance_px_squared) {
422 current_pick.corner_index = i;
423 current_pick.distance_px_squared = distance_squared;
424 }
425 }
426
427 if (current_pick.distance_px_squared > distance_tolerance_px_squared) {
428 const float zero_offset[2] = {0.0f, 0.0f};
429 const float distance_squared = mouse_to_closest_corners_edge_distance_squared(
430 co, zero_offset, plane_marker->corners, width, height);
431 if (distance_squared < current_pick.distance_px_squared) {
432 current_pick.corner_index = -1;
433 current_pick.distance_px_squared = distance_squared;
434 }
435 }
436
437 if (current_pick.distance_px_squared < pick.distance_px_squared) {
438 pick = current_pick;
439 }
440 }
441
442 if (pick.distance_px_squared > distance_tolerance_px_squared) {
444 }
445
446 return pick;
447}
448
450{
451 if (pick->plane_track == nullptr) {
452 return false;
453 }
454
455 BLI_assert(pick->plane_marker != nullptr);
456
458 return false;
459 }
460
461 return pick->corner_index != -1;
462}
463
466/* -------------------------------------------------------------------- */
471{
473
475 result.plane_track_pick = plane_track_pick_make_null();
476
477 return result;
478}
479
481 const PointTrackPick *point_track_pick,
482 const PlaneTrackPick *plane_track_pick)
483{
484 /* Simple case: one of the pick results is empty, so prefer the other one. */
485 if (point_track_pick->track == nullptr) {
486 return false;
487 }
488 if (plane_track_pick->plane_track == nullptr) {
489 return true;
490 }
491
492 SpaceClip *space_clip = CTX_wm_space_clip(C);
493
494 /* If one of the picks can be slid prefer it. */
495 const bool can_slide_point_track = ed_tracking_point_track_pick_can_slide(space_clip,
496 point_track_pick);
497 const bool can_slide_plane_track = ed_tracking_plane_track_pick_can_slide(plane_track_pick);
498 if (can_slide_point_track && !can_slide_plane_track) {
499 return true;
500 }
501 else if (!can_slide_point_track && can_slide_plane_track) {
502 return false;
503 }
504
505 /* Prefer the closest pick. */
506 if (point_track_pick->distance_px_squared > plane_track_pick->distance_px_squared) {
507 return false;
508 }
509 return true;
510}
511
513 bContext *C,
514 const float co[2])
515{
516 TrackingPick pick;
517
520
523 }
524 else {
526 }
527
528 return pick;
529}
530
533/********************** mouse select operator *********************/
534
536{
537 LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_base) {
539 }
540}
541
543{
544 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) {
545 plane_track->flag &= ~SELECT;
546 }
547}
548
549static bool select_poll(bContext *C)
550{
552
553 if (sc) {
554 return sc->clip && sc->view == SC_VIEW_CLIP;
555 }
556
557 return false;
558}
559
560static int select_exec(bContext *C, wmOperator *op)
561{
564 MovieTracking *tracking = &clip->tracking;
565 MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
566 const bool extend = RNA_boolean_get(op->ptr, "extend");
567 const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
568
569 float co[2];
570 RNA_float_get_array(op->ptr, "location", co);
571
573 const TrackingPick pick = ed_tracking_pick_closest(&options, C, co);
574
575 /* Special code which allows to slide a marker which belongs to currently selected but not yet
576 * active track. If such track is found activate it and return pass-though so that marker slide
577 * operator can be used immediately after.
578 * This logic makes it convenient to slide markers when left mouse selection is used. Without it
579 * selection will be lost which causes inconvenience for the VFX artist. */
580 if (!extend && ed_tracking_pick_can_slide(sc, &pick)) {
581 if (pick.point_track_pick.track != nullptr) {
582 tracking_object->active_track = pick.point_track_pick.track;
583 tracking_object->active_plane_track = nullptr;
584 }
585 else {
586 tracking_object->active_track = nullptr;
587 tracking_object->active_plane_track = pick.plane_track_pick.plane_track;
588 }
589
592
594 }
595
596 ClipViewLockState lock_state;
597 ED_clip_view_lock_state_store(C, &lock_state);
598
599 if (pick.point_track_pick.track != nullptr) {
600 if (!extend) {
602 }
603
605 int area = pick.point_track_pick.area;
606
607 if (!extend || !TRACK_VIEW_SELECTED(sc, track)) {
608 area = TRACK_AREA_ALL;
609 }
610
611 if (extend && TRACK_AREA_SELECTED(track, area)) {
612 if (track == tracking_object->active_track) {
613 BKE_tracking_track_deselect(track, area);
614 }
615 else {
616 tracking_object->active_track = track;
617 tracking_object->active_plane_track = nullptr;
618 }
619 }
620 else {
621 if (area == TRACK_AREA_POINT) {
622 area = TRACK_AREA_ALL;
623 }
624
625 BKE_tracking_track_select(&tracking_object->tracks, track, area, extend);
626 tracking_object->active_track = track;
627 tracking_object->active_plane_track = nullptr;
628 }
629 }
630 else if (pick.plane_track_pick.plane_track != nullptr) {
631 if (!extend) {
632 ed_tracking_deselect_all_tracks(&tracking_object->tracks);
633 }
634
636
637 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
638 if (extend) {
639 plane_track->flag &= ~SELECT;
640 }
641 }
642 else {
643 plane_track->flag |= SELECT;
644 }
645
646 tracking_object->active_track = nullptr;
647 tracking_object->active_plane_track = plane_track;
648 }
649 else if (deselect_all) {
650 ed_tracking_deselect_all_tracks(&tracking_object->tracks);
652 }
653
655
657
660
661 /* Pass-through + finished to allow tweak to transform. */
663}
664
665static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
666{
668 ARegion *region = CTX_wm_region(C);
669
670 float co[2];
671 ED_clip_mouse_pos(sc, region, event->mval, co);
672 RNA_float_set_array(op->ptr, "location", co);
673
674 return select_exec(C, op);
675}
676
678{
679 /* identifiers */
680 ot->name = "Select";
681 ot->description = "Select tracking markers";
682 ot->idname = "CLIP_OT_select";
683
684 /* api callbacks */
688
689 /* flags */
691
692 /* properties */
693 PropertyRNA *prop;
694 prop = RNA_def_boolean(ot->srna,
695 "extend",
696 false,
697 "Extend",
698 "Extend selection rather than clearing the existing selection");
700 prop = RNA_def_boolean(ot->srna,
701 "deselect_all",
702 false,
703 "Deselect On Nothing",
704 "Deselect all when nothing under the cursor");
706
708 ot->srna,
709 "location",
710 2,
711 nullptr,
712 -FLT_MAX,
713 FLT_MAX,
714 "Location",
715 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
716 -100.0f,
717 100.0f);
718}
719
721{
722 /* To avoid conflicts with mask select deselect all in empty space. */
723 return select_poll(C);
724}
725
726/********************** box select operator *********************/
727
729{
731 ARegion *region = CTX_wm_region(C);
732
734 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
735 rcti rect;
736 rctf rectf;
737 bool changed = false;
738 int framenr = ED_space_clip_get_clip_frame_number(sc);
739
740 /* get rectangle from operator */
742
743 ED_clip_point_stable_pos(sc, region, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
744 ED_clip_point_stable_pos(sc, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
745
746 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
747 const bool select = (sel_op != SEL_OP_SUB);
748 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
749 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
750 changed = true;
751 }
752
753 /* do actual selection */
754 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
755 if (track->flag & TRACK_HIDDEN) {
756 continue;
757 }
758
759 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
760
761 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker)) {
762 if (BLI_rctf_isect_pt_v(&rectf, marker->pos)) {
763 if (select) {
765 }
766 else {
768 }
769 }
770 changed = true;
771 }
772 }
773
774 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
775 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
776 continue;
777 }
778
779 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
780 framenr);
781
782 for (int i = 0; i < 4; i++) {
783 if (BLI_rctf_isect_pt_v(&rectf, plane_marker->corners[i])) {
784 if (select) {
785 plane_track->flag |= SELECT;
786 }
787 else {
788 plane_track->flag &= ~SELECT;
789 }
790 }
791 }
792 changed = true;
793 }
794
795 if (changed) {
796 BKE_tracking_dopesheet_tag_update(&clip->tracking);
797
800
801 return OPERATOR_FINISHED;
802 }
803
804 return OPERATOR_CANCELLED;
805}
806
808{
809 /* identifiers */
810 ot->name = "Box Select";
811 ot->description = "Select markers using box selection";
812 ot->idname = "CLIP_OT_select_box";
813
814 /* api callbacks */
819
820 /* flags */
822
823 /* properties */
826}
827
828/********************** lasso select operator *********************/
829
830static int do_lasso_select_marker(bContext *C, const Span<int2> mcoords, bool select)
831{
833 ARegion *region = CTX_wm_region(C);
834
836 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
837 rcti rect;
838 bool changed = false;
839 const int framenr = ED_space_clip_get_clip_frame_number(sc);
840
841 /* get rectangle from operator */
842 BLI_lasso_boundbox(&rect, mcoords);
843
844 /* do actual selection */
845 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
846 if (track->flag & TRACK_HIDDEN) {
847 continue;
848 }
849
850 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
851
852 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker)) {
853 float screen_co[2];
854
855 /* marker in screen coords */
856 ED_clip_point_stable_pos__reverse(sc, region, marker->pos, screen_co);
857
858 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
859 BLI_lasso_is_point_inside(mcoords, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
860 {
861 if (select) {
863 }
864 else {
866 }
867 }
868
869 changed = true;
870 }
871 }
872
873 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
874 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
875 continue;
876 }
877
878 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
879 framenr);
880
881 for (int i = 0; i < 4; i++) {
882 float screen_co[2];
883
884 /* marker in screen coords */
885 ED_clip_point_stable_pos__reverse(sc, region, plane_marker->corners[i], screen_co);
886
887 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
888 BLI_lasso_is_point_inside(mcoords, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
889 {
890 if (select) {
891 plane_track->flag |= SELECT;
892 }
893 else {
894 plane_track->flag &= ~SELECT;
895 }
896 }
897 }
898
899 changed = true;
900 }
901
902 if (changed) {
903 BKE_tracking_dopesheet_tag_update(&clip->tracking);
904
907 }
908
909 return changed;
910}
911
913{
914 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
915
916 if (mcoords.is_empty()) {
918 }
919
920 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
921 const bool select = (sel_op != SEL_OP_SUB);
922 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
924 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
925 }
926
927 do_lasso_select_marker(C, mcoords, select);
928
929 return OPERATOR_FINISHED;
930}
931
933{
934 /* identifiers */
935 ot->name = "Lasso Select";
936 ot->description = "Select markers using lasso selection";
937 ot->idname = "CLIP_OT_select_lasso";
938
939 /* api callbacks */
945
946 /* flags */
948
949 /* properties */
952}
953
954/********************** circle select operator *********************/
955
956static int point_inside_ellipse(const float point[2],
957 const float offset[2],
958 const float ellipse[2])
959{
960 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
961 float x, y;
962
963 x = (point[0] - offset[0]) * ellipse[0];
964 y = (point[1] - offset[1]) * ellipse[1];
965
966 return x * x + y * y < 1.0f;
967}
968
970 const float offset[2],
971 const float ellipse[2])
972{
973 return point_inside_ellipse(marker->pos, offset, ellipse);
974}
975
977{
979 ARegion *region = CTX_wm_region(C);
980
982 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
983 int width, height;
984 bool changed = false;
985 float zoomx, zoomy, offset[2], ellipse[2];
986 int framenr = ED_space_clip_get_clip_frame_number(sc);
987
988 /* get operator properties */
989 const int x = RNA_int_get(op->ptr, "x");
990 const int y = RNA_int_get(op->ptr, "y");
991 const int radius = RNA_int_get(op->ptr, "radius");
992
993 const eSelectOp sel_op = ED_select_op_modal(
994 eSelectOp(RNA_enum_get(op->ptr, "mode")),
995 WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
996 const bool select = (sel_op != SEL_OP_SUB);
997 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
998 ED_clip_select_all(sc, SEL_DESELECT, nullptr);
999 changed = true;
1000 }
1001
1002 /* compute ellipse and position in unified coordinates */
1003 ED_space_clip_get_size(sc, &width, &height);
1004 ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
1005
1006 ellipse[0] = width * zoomx / radius;
1007 ellipse[1] = height * zoomy / radius;
1008
1009 ED_clip_point_stable_pos(sc, region, x, y, &offset[0], &offset[1]);
1010
1011 /* do selection */
1012 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
1013 if (track->flag & TRACK_HIDDEN) {
1014 continue;
1015 }
1016
1017 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1018
1019 if (ED_space_clip_marker_is_visible(sc, tracking_object, track, marker) &&
1020 marker_inside_ellipse(marker, offset, ellipse))
1021 {
1022 if (select) {
1024 }
1025 else {
1027 }
1028 changed = true;
1029 }
1030 }
1031
1032 LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking_object->plane_tracks) {
1033 if (plane_track->flag & PLANE_TRACK_HIDDEN) {
1034 continue;
1035 }
1036
1037 const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
1038 framenr);
1039
1040 for (int i = 0; i < 4; i++) {
1041 if (point_inside_ellipse(plane_marker->corners[i], offset, ellipse)) {
1042 if (select) {
1043 plane_track->flag |= SELECT;
1044 }
1045 else {
1046 plane_track->flag &= ~SELECT;
1047 }
1048 }
1049 }
1050
1051 changed = true;
1052 }
1053
1054 if (changed) {
1055 BKE_tracking_dopesheet_tag_update(&clip->tracking);
1056
1059
1060 return OPERATOR_FINISHED;
1061 }
1062
1063 return OPERATOR_CANCELLED;
1064}
1065
1067{
1068 /* identifiers */
1069 ot->name = "Circle Select";
1070 ot->description = "Select markers using circle selection";
1071 ot->idname = "CLIP_OT_select_circle";
1072
1073 /* api callbacks */
1079
1080 /* flags */
1082
1083 /* properties */
1086}
1087
1088/********************** select all operator *********************/
1089
1091{
1094 MovieTracking *tracking = &clip->tracking;
1095
1096 const int action = RNA_enum_get(op->ptr, "action");
1097
1098 ClipViewLockState lock_state;
1099 ED_clip_view_lock_state_store(C, &lock_state);
1100
1101 bool has_selection = false;
1102 ED_clip_select_all(sc, action, &has_selection);
1103
1104 if (has_selection) {
1106 }
1107
1109
1112
1113 return OPERATOR_FINISHED;
1114}
1115
1117{
1118 /* identifiers */
1119 ot->name = "(De)select All";
1120 ot->description = "Change selection of all tracking markers";
1121 ot->idname = "CLIP_OT_select_all";
1122
1123 /* api callbacks */
1126
1127 /* flags */
1129
1131}
1132
1133/********************** select grouped operator *********************/
1134
1136{
1139 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
1140 const int group = RNA_enum_get(op->ptr, "group");
1141 const int framenr = ED_space_clip_get_clip_frame_number(sc);
1142
1143 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
1144 bool ok = false;
1145
1146 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1147
1148 if (group == 0) { /* Keyframed */
1149 ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED) == 0;
1150 }
1151 else if (group == 1) { /* Estimated */
1152 ok = marker->framenr != framenr;
1153 }
1154 else if (group == 2) { /* tracked */
1155 ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED);
1156 }
1157 else if (group == 3) { /* locked */
1158 ok = track->flag & TRACK_LOCKED;
1159 }
1160 else if (group == 4) { /* disabled */
1161 ok = marker->flag & MARKER_DISABLED;
1162 }
1163 else if (group == 5) { /* color */
1164 const MovieTrackingTrack *act_track = tracking_object->active_track;
1165
1166 if (act_track) {
1167 ok = (track->flag & TRACK_CUSTOMCOLOR) == (act_track->flag & TRACK_CUSTOMCOLOR);
1168
1169 if (ok && track->flag & TRACK_CUSTOMCOLOR) {
1170 ok = equals_v3v3(track->color, act_track->color);
1171 }
1172 }
1173 }
1174 else if (group == 6) { /* failed */
1175 ok = (track->flag & TRACK_HAS_BUNDLE) == 0;
1176 }
1177
1178 if (ok) {
1179 track->flag |= SELECT;
1180 if (sc->flag & SC_SHOW_MARKER_PATTERN) {
1181 track->pat_flag |= SELECT;
1182 }
1183 if (sc->flag & SC_SHOW_MARKER_SEARCH) {
1184 track->search_flag |= SELECT;
1185 }
1186 }
1187 }
1188
1189 BKE_tracking_dopesheet_tag_update(&clip->tracking);
1190
1193
1194 return OPERATOR_FINISHED;
1195}
1196
1198{
1199 static const EnumPropertyItem select_group_items[] = {
1200 {0, "KEYFRAMED", 0, "Keyframed Tracks", "Select all keyframed tracks"},
1201 {1, "ESTIMATED", 0, "Estimated Tracks", "Select all estimated tracks"},
1202 {2, "TRACKED", 0, "Tracked Tracks", "Select all tracked tracks"},
1203 {3, "LOCKED", 0, "Locked Tracks", "Select all locked tracks"},
1204 {4, "DISABLED", 0, "Disabled Tracks", "Select all disabled tracks"},
1205 {5,
1206 "COLOR",
1207 0,
1208 "Tracks with Same Color",
1209 "Select all tracks with same color as active track"},
1210 {6, "FAILED", 0, "Failed Tracks", "Select all tracks which failed to be reconstructed"},
1211 {0, nullptr, 0, nullptr, nullptr},
1212 };
1213
1214 /* identifiers */
1215 ot->name = "Select Grouped";
1216 ot->description = "Select all tracks from specified group";
1217 ot->idname = "CLIP_OT_select_grouped";
1218
1219 /* api callbacks */
1222
1223 /* flags */
1225
1226 /* properties */
1228 "group",
1229 select_group_items,
1231 "Action",
1232 "Clear action to execute");
1233}
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:3411
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:50
#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:289
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:1068
@ SC_VIEW_CLIP
@ SC_HIDE_DISABLED
@ SC_SHOW_MARKER_SEARCH
@ SC_SHOW_MARKER_PATTERN
@ TRACK_CUSTOMCOLOR
@ TRACK_HIDDEN
@ TRACK_LOCKED
@ TRACK_HAS_BUNDLE
@ PLANE_TRACK_HIDDEN
@ MARKER_TRACKED
@ MARKER_DISABLED
#define UI_SCALE_FAC
@ 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 ED_select_op_modal(eSelectOp sel_op, bool is_first)
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
@ SEL_DESELECT
eSelectOp
@ SEL_OP_SUB
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DISPLAY
Definition WM_types.hh:458
#define NC_MOVIECLIP
Definition WM_types.hh:364
#define ND_SELECT
Definition WM_types.hh:474
bool is_empty() const
Definition BLI_array.hh:253
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
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
T square(const T &a)
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
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:728
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
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
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
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct PointerRNA * ptr
@ 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 int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float mouse_to_offset_distance_squared(const MovieTrackingTrack *track, const MovieTrackingMarker *marker, const float co[2], int width, int height)
static float mouse_to_closest_search_edge_distance_squared(const MovieTrackingMarker *marker, const float co[2], int width, int height)
static int select_exec(bContext *C, wmOperator *op)
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 int select_all_exec(bContext *C, wmOperator *op)
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 int box_select_exec(bContext *C, wmOperator *op)
static float mouse_to_search_corner_distance_squared(const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
static int clip_lasso_select_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)
static int circle_select_exec(bContext *C, wmOperator *op)
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)
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 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 int select_grouped_exec(bContext *C, wmOperator *op)
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:4125
bool WM_gesture_is_modal_first(const wmGesture *gesture)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
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)
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)