Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_clip / tracking_ops_orient.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2016 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spclip
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_constraint_types.h"
27 #include "DNA_object_types.h" /* SELECT */
28 #include "DNA_screen_types.h"
29 #include "DNA_space_types.h"
30
31 #include "BLI_utildefines.h"
32 #include "BLI_math.h"
33
34 #include "BKE_context.h"
35 #include "BKE_constraint.h"
36 #include "BKE_tracking.h"
37 #include "BKE_layer.h"
38 #include "BKE_object.h"
39 #include "BKE_report.h"
40
41 #include "DEG_depsgraph.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_clip.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "clip_intern.h"
52
53 /********************** set origin operator *********************/
54
55 static Object *get_camera_with_movieclip(Scene *scene, MovieClip *clip)
56 {
57   Object *camera = scene->camera;
58
59   if (camera != NULL && BKE_object_movieclip_get(scene, camera, false) == clip) {
60     return camera;
61   }
62
63   FOREACH_SCENE_OBJECT_BEGIN (scene, ob) {
64     if (ob->type == OB_CAMERA) {
65       if (BKE_object_movieclip_get(scene, ob, false) == clip) {
66         camera = ob;
67         break;
68       }
69     }
70   }
71   FOREACH_SCENE_OBJECT_END;
72
73   return camera;
74 }
75
76 static Object *get_orientation_object(bContext *C)
77 {
78   Scene *scene = CTX_data_scene(C);
79   ViewLayer *view_layer = CTX_data_view_layer(C);
80   SpaceClip *sc = CTX_wm_space_clip(C);
81   MovieClip *clip = ED_space_clip_get_clip(sc);
82   MovieTracking *tracking = &clip->tracking;
83   MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
84   Object *object = NULL;
85
86   if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
87     object = get_camera_with_movieclip(scene, clip);
88   }
89   else {
90     object = OBACT(view_layer);
91   }
92
93   if (object != NULL && object->parent != NULL) {
94     object = object->parent;
95   }
96
97   return object;
98 }
99
100 static bool set_orientation_poll(bContext *C)
101 {
102   SpaceClip *sc = CTX_wm_space_clip(C);
103   if (sc != NULL) {
104     ViewLayer *view_layer = CTX_data_view_layer(C);
105     MovieClip *clip = ED_space_clip_get_clip(sc);
106     if (clip != NULL) {
107       MovieTracking *tracking = &clip->tracking;
108       MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
109       if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
110         return true;
111       }
112       else {
113         return OBACT(view_layer) != NULL;
114       }
115     }
116   }
117   return false;
118 }
119
120 static int count_selected_bundles(bContext *C)
121 {
122   SpaceClip *sc = CTX_wm_space_clip(C);
123   MovieClip *clip = ED_space_clip_get_clip(sc);
124   ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
125   int tot = 0;
126   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
127     if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) {
128       tot++;
129     }
130   }
131   return tot;
132 }
133
134 static void object_solver_inverted_matrix(Scene *scene, Object *ob, float invmat[4][4])
135 {
136   bool found = false;
137   for (bConstraint *con = ob->constraints.first; con != NULL; con = con->next) {
138     const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
139     if (cti == NULL) {
140       continue;
141     }
142     if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
143       bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data;
144       if (!found) {
145         Object *cam = data->camera ? data->camera : scene->camera;
146         BKE_object_where_is_calc_mat4(cam, invmat);
147       }
148       mul_m4_m4m4(invmat, invmat, data->invmat);
149       found = true;
150     }
151   }
152   if (found) {
153     invert_m4(invmat);
154   }
155   else {
156     unit_m4(invmat);
157   }
158 }
159
160 static Object *object_solver_camera(Scene *scene, Object *ob)
161 {
162   for (bConstraint *con = ob->constraints.first; con != NULL; con = con->next) {
163     const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
164     if (cti == NULL) {
165       continue;
166     }
167     if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
168       bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data;
169       return (data->camera != NULL) ? data->camera : scene->camera;
170     }
171   }
172   return NULL;
173 }
174
175 static int set_origin_exec(bContext *C, wmOperator *op)
176 {
177   SpaceClip *sc = CTX_wm_space_clip(C);
178   MovieClip *clip = ED_space_clip_get_clip(sc);
179   MovieTracking *tracking = &clip->tracking;
180   Scene *scene = CTX_data_scene(C);
181   Object *camera = get_camera_with_movieclip(scene, clip);
182   int selected_count = count_selected_bundles(C);
183
184   if (selected_count == 0) {
185     BKE_report(op->reports,
186                RPT_ERROR,
187                "At least one track with bundle should be selected to "
188                "define origin position");
189
190     return OPERATOR_CANCELLED;
191   }
192
193   Object *object = get_orientation_object(C);
194   if (object == NULL) {
195     BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
196     return OPERATOR_CANCELLED;
197   }
198
199   MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
200   ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
201
202   float median[3] = {0.0f, 0.0f, 0.0f};
203   zero_v3(median);
204   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
205     if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) {
206       add_v3_v3(median, track->bundle_pos);
207     }
208   }
209   mul_v3_fl(median, 1.0f / selected_count);
210
211   float mat[4][4], vec[3];
212   BKE_tracking_get_camera_object_matrix(scene, camera, mat);
213   mul_v3_m4v3(vec, mat, median);
214
215   if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
216     sub_v3_v3(object->loc, vec);
217   }
218   else {
219     object_solver_inverted_matrix(scene, object, mat);
220     mul_v3_m4v3(vec, mat, vec);
221     copy_v3_v3(object->loc, vec);
222   }
223
224   DEG_id_tag_update(&clip->id, 0);
225   DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM);
226
227   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
228   WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
229
230   return OPERATOR_FINISHED;
231 }
232
233 void CLIP_OT_set_origin(wmOperatorType *ot)
234 {
235   /* identifiers */
236   ot->name = "Set Origin";
237   ot->description =
238       "Set active marker as origin by moving camera (or its parent if present) in 3D space";
239   ot->idname = "CLIP_OT_set_origin";
240
241   /* api callbacks */
242   ot->exec = set_origin_exec;
243   ot->poll = set_orientation_poll;
244
245   /* flags */
246   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
247
248   /* properties */
249   RNA_def_boolean(
250       ot->srna, "use_median", 0, "Use Median", "Set origin to median point of selected bundles");
251 }
252
253 /********************** set floor operator *********************/
254
255 static void set_axis(Scene *scene,
256                      Object *ob,
257                      MovieClip *clip,
258                      MovieTrackingObject *tracking_object,
259                      MovieTrackingTrack *track,
260                      char axis)
261 {
262   Object *camera = get_camera_with_movieclip(scene, clip);
263   const bool is_camera = (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0;
264   bool flip = false;
265   float mat[4][4], vec[3], obmat[4][4], dvec[3];
266
267   BKE_object_to_mat4(ob, obmat);
268
269   BKE_tracking_get_camera_object_matrix(scene, camera, mat);
270   mul_v3_m4v3(vec, mat, track->bundle_pos);
271   copy_v3_v3(dvec, vec);
272
273   if (!is_camera) {
274     float imat[4][4];
275
276     object_solver_inverted_matrix(scene, ob, imat);
277     mul_v3_m4v3(vec, imat, vec);
278
279     invert_m4_m4(imat, obmat);
280     mul_v3_m4v3(dvec, imat, vec);
281
282     sub_v3_v3(vec, obmat[3]);
283   }
284
285   if (len_squared_v2(vec) < (1e-3f * 1e-3f)) {
286     return;
287   }
288
289   unit_m4(mat);
290
291   if (axis == 'X') {
292     if (fabsf(dvec[1]) < 1e-3f) {
293       flip = true;
294
295       mat[0][0] = -1.0f;
296       mat[0][1] = 0.0f;
297       mat[0][2] = 0.0f;
298       mat[1][0] = 0.0f;
299       mat[1][1] = -1.0f;
300       mat[1][2] = 0.0f;
301       mat[2][0] = 0.0f;
302       mat[2][1] = 0.0f;
303       mat[2][2] = 1.0f;
304     }
305     else {
306       copy_v3_v3(mat[0], vec);
307
308       if (is_camera || fabsf(vec[2]) < 1e-3f) {
309         mat[0][2] = 0.0f;
310         mat[2][0] = 0.0f;
311         mat[2][1] = 0.0f;
312         mat[2][2] = 1.0f;
313         cross_v3_v3v3(mat[1], mat[2], mat[0]);
314       }
315       else {
316         vec[2] = 0.0f;
317
318         cross_v3_v3v3(mat[1], mat[0], vec);
319         cross_v3_v3v3(mat[2], mat[0], mat[1]);
320       }
321     }
322   }
323   else {
324     if (fabsf(dvec[0]) < 1e-3f) {
325       flip = true;
326
327       mat[0][0] = -1.0f;
328       mat[0][1] = 0.0f;
329       mat[0][2] = 0.0f;
330       mat[1][0] = 0.0f;
331       mat[1][1] = -1.0f;
332       mat[1][2] = 0.0f;
333       mat[2][0] = 0.0f;
334       mat[2][1] = 0.0f;
335       mat[2][2] = 1.0f;
336     }
337     else {
338       copy_v3_v3(mat[1], vec);
339
340       if (is_camera || fabsf(vec[2]) < 1e-3f) {
341         mat[1][2] = 0.0f;
342         mat[2][0] = 0.0f;
343         mat[2][1] = 0.0f;
344         mat[2][2] = 1.0f;
345         cross_v3_v3v3(mat[0], mat[1], mat[2]);
346       }
347       else {
348         vec[2] = 0.0f;
349
350         cross_v3_v3v3(mat[0], vec, mat[1]);
351         cross_v3_v3v3(mat[2], mat[0], mat[1]);
352       }
353     }
354   }
355
356   normalize_v3(mat[0]);
357   normalize_v3(mat[1]);
358   normalize_v3(mat[2]);
359
360   if (is_camera) {
361     invert_m4(mat);
362
363     mul_m4_m4m4(mat, mat, obmat);
364   }
365   else {
366     if (!flip) {
367       float lmat[4][4], ilmat[4][4], rmat[3][3];
368
369       BKE_object_rot_to_mat3(ob, rmat, true);
370       invert_m3(rmat);
371       mul_m4_m4m3(mat, mat, rmat);
372
373       unit_m4(lmat);
374       copy_v3_v3(lmat[3], obmat[3]);
375       invert_m4_m4(ilmat, lmat);
376
377       mul_m4_series(mat, lmat, mat, ilmat, obmat);
378     }
379     else {
380       mul_m4_m4m4(mat, obmat, mat);
381     }
382   }
383
384   BKE_object_apply_mat4(ob, mat, 0, 0);
385 }
386
387 static int set_plane_exec(bContext *C, wmOperator *op)
388 {
389   SpaceClip *sc = CTX_wm_space_clip(C);
390   MovieClip *clip = ED_space_clip_get_clip(sc);
391   Scene *scene = CTX_data_scene(C);
392   MovieTracking *tracking = &clip->tracking;
393   MovieTrackingObject *tracking_object;
394   MovieTrackingTrack *track, *axis_track = NULL, *act_track;
395   ListBase *tracksbase;
396   Object *object;
397   Object *camera = get_camera_with_movieclip(scene, clip);
398   Depsgraph *depsgraph = CTX_data_depsgraph(C);
399   int tot = 0;
400   float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3] = {0.0f, 0.0f, 0.0f};
401   int plane = RNA_enum_get(op->ptr, "plane");
402   float rot[4][4] = {
403       {0.0f, 0.0f, -1.0f, 0.0f},
404       {0.0f, 1.0f, 0.0f, 0.0f},
405       {1.0f, 0.0f, 0.0f, 0.0f},
406       {0.0f, 0.0f, 0.0f, 1.0f},
407   }; /* 90 degrees Y-axis rotation matrix */
408
409   if (count_selected_bundles(C) != 3) {
410     BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor");
411
412     return OPERATOR_CANCELLED;
413   }
414
415   tracking_object = BKE_tracking_object_get_active(tracking);
416   tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
417   act_track = BKE_tracking_track_get_active(tracking);
418
419   object = get_orientation_object(C);
420   if (object == NULL) {
421     BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
422     return OPERATOR_CANCELLED;
423   }
424
425   BKE_tracking_get_camera_object_matrix(scene, camera, mat);
426
427   /* Get 3 bundles to use as reference. */
428   track = tracksbase->first;
429   while (track && tot < 3) {
430     if (track->flag & TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) {
431       mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
432       if (tot == 0 || track == act_track) {
433         copy_v3_v3(orig, vec[tot]);
434       }
435       else {
436         axis_track = track;
437       }
438       tot++;
439     }
440     track = track->next;
441   }
442
443   sub_v3_v3(vec[1], vec[0]);
444   sub_v3_v3(vec[2], vec[0]);
445
446   /* Construct ortho-normal basis. */
447   unit_m4(mat);
448   if (plane == 0) { /* floor */
449     cross_v3_v3v3(mat[0], vec[1], vec[2]);
450     copy_v3_v3(mat[1], vec[1]);
451     cross_v3_v3v3(mat[2], mat[0], mat[1]);
452   }
453   else if (plane == 1) { /* wall */
454     cross_v3_v3v3(mat[2], vec[1], vec[2]);
455     copy_v3_v3(mat[1], vec[1]);
456     cross_v3_v3v3(mat[0], mat[1], mat[2]);
457   }
458
459   normalize_v3(mat[0]);
460   normalize_v3(mat[1]);
461   normalize_v3(mat[2]);
462
463   /* Move to origin point. */
464   mat[3][0] = orig[0];
465   mat[3][1] = orig[1];
466   mat[3][2] = orig[2];
467
468   if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
469     invert_m4(mat);
470
471     BKE_object_to_mat4(object, obmat);
472     mul_m4_m4m4(mat, mat, obmat);
473     mul_m4_m4m4(newmat, rot, mat);
474     BKE_object_apply_mat4(object, newmat, 0, 0);
475
476     /* Make camera have positive z-coordinate. */
477     if (object->loc[2] < 0) {
478       invert_m4(rot);
479       mul_m4_m4m4(newmat, rot, mat);
480       BKE_object_apply_mat4(object, newmat, 0, 0);
481     }
482   }
483   else {
484     BKE_object_apply_mat4(object, mat, 0, 0);
485   }
486
487   BKE_object_where_is_calc(depsgraph, scene, object);
488   set_axis(scene, object, clip, tracking_object, axis_track, 'X');
489
490   DEG_id_tag_update(&clip->id, 0);
491   DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM);
492
493   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
494   WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
495
496   return OPERATOR_FINISHED;
497 }
498
499 void CLIP_OT_set_plane(wmOperatorType *ot)
500 {
501   static const EnumPropertyItem plane_items[] = {
502       {0, "FLOOR", 0, "Floor", "Set floor plane"},
503       {1, "WALL", 0, "Wall", "Set wall plane"},
504       {0, NULL, 0, NULL, NULL},
505   };
506
507   /* identifiers */
508   ot->name = "Set Plane";
509   ot->description =
510       "Set plane based on 3 selected bundles by moving camera "
511       "(or its parent if present) in 3D space";
512   ot->idname = "CLIP_OT_set_plane";
513
514   /* api callbacks */
515   ot->exec = set_plane_exec;
516   ot->poll = set_orientation_poll;
517
518   /* flags */
519   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
520
521   /* properties */
522   RNA_def_enum(ot->srna, "plane", plane_items, 0, "Plane", "Plane to be used for orientation");
523 }
524
525 /********************** set axis operator *********************/
526
527 static int set_axis_exec(bContext *C, wmOperator *op)
528 {
529   SpaceClip *sc = CTX_wm_space_clip(C);
530   MovieClip *clip = ED_space_clip_get_clip(sc);
531   MovieTracking *tracking = &clip->tracking;
532   MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
533   Scene *scene = CTX_data_scene(C);
534   Object *object;
535   int axis = RNA_enum_get(op->ptr, "axis");
536
537   if (count_selected_bundles(C) != 1) {
538     BKE_report(
539         op->reports, RPT_ERROR, "Single track with bundle should be selected to define axis");
540     return OPERATOR_CANCELLED;
541   }
542
543   object = get_orientation_object(C);
544   if (object == NULL) {
545     BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
546     return OPERATOR_CANCELLED;
547   }
548
549   ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
550   MovieTrackingTrack *track = tracksbase->first;
551   while (track) {
552     if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) {
553       break;
554     }
555     track = track->next;
556   }
557
558   set_axis(scene, object, clip, tracking_object, track, axis == 0 ? 'X' : 'Y');
559
560   DEG_id_tag_update(&clip->id, 0);
561   DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM);
562
563   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
564   WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
565
566   return OPERATOR_FINISHED;
567 }
568
569 void CLIP_OT_set_axis(wmOperatorType *ot)
570 {
571   static const EnumPropertyItem axis_actions[] = {
572       {0, "X", 0, "X", "Align bundle align X axis"},
573       {1, "Y", 0, "Y", "Align bundle align Y axis"},
574       {0, NULL, 0, NULL, NULL},
575   };
576
577   /* identifiers */
578   ot->name = "Set Axis";
579   ot->description =
580       "Set direction of scene axis rotating camera "
581       "(or its parent if present) and assume selected track "
582       "lies on real axis, joining it with the origin";
583   ot->idname = "CLIP_OT_set_axis";
584
585   /* api callbacks */
586   ot->exec = set_axis_exec;
587   ot->poll = set_orientation_poll;
588
589   /* flags */
590   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
591
592   /* properties */
593   RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", "Axis to use to align bundle along");
594 }
595
596 /********************** set scale operator *********************/
597
598 static int do_set_scale(bContext *C, wmOperator *op, bool scale_solution, bool apply_scale)
599 {
600   SpaceClip *sc = CTX_wm_space_clip(C);
601   MovieClip *clip = ED_space_clip_get_clip(sc);
602   MovieTracking *tracking = &clip->tracking;
603   MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
604   MovieTrackingTrack *track;
605   Scene *scene = CTX_data_scene(C);
606   Object *object = NULL;
607   Object *camera = get_camera_with_movieclip(scene, clip);
608   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
609   int tot = 0;
610   float vec[2][3], mat[4][4], scale;
611   float dist = RNA_float_get(op->ptr, "distance");
612
613   if (count_selected_bundles(C) != 2) {
614     BKE_report(op->reports, RPT_ERROR, "Two tracks with bundles should be selected to set scale");
615     return OPERATOR_CANCELLED;
616   }
617
618   if (!scale_solution && !apply_scale) {
619     object = get_orientation_object(C);
620     if (object == NULL) {
621       BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
622       return OPERATOR_CANCELLED;
623     }
624   }
625
626   BKE_tracking_get_camera_object_matrix(scene, camera, mat);
627
628   track = tracksbase->first;
629   while (track) {
630     if (TRACK_VIEW_SELECTED(sc, track)) {
631       mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
632       tot++;
633     }
634     track = track->next;
635   }
636
637   sub_v3_v3(vec[0], vec[1]);
638
639   if (len_v3(vec[0]) > 1e-5f) {
640     scale = dist / len_v3(vec[0]);
641     if (apply_scale) {
642       /* Apply scale on reconstructed scene itself. */
643       MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(
644           tracking);
645       MovieReconstructedCamera *reconstructed_cameras;
646       int i;
647
648       for (track = tracksbase->first; track; track = track->next) {
649         mul_v3_fl(track->bundle_pos, scale);
650       }
651
652       reconstructed_cameras = reconstruction->cameras;
653       for (i = 0; i < reconstruction->camnr; i++) {
654         mul_v3_fl(reconstructed_cameras[i].mat[3], scale);
655       }
656
657       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
658       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
659     }
660     else {
661       if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
662         mul_v3_fl(object->scale, scale);
663         mul_v3_fl(object->loc, scale);
664       }
665       else if (!scale_solution) {
666         Object *solver_camera = object_solver_camera(scene, object);
667
668         object->scale[0] = object->scale[1] = object->scale[2] = 1.0f / scale;
669
670         if (solver_camera) {
671           object->scale[0] /= solver_camera->scale[0];
672           object->scale[1] /= solver_camera->scale[1];
673           object->scale[2] /= solver_camera->scale[2];
674         }
675       }
676       else {
677         tracking_object->scale = scale;
678       }
679
680       DEG_id_tag_update(&clip->id, 0);
681
682       if (object) {
683         DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM);
684       }
685
686       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
687       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
688     }
689   }
690
691   return OPERATOR_FINISHED;
692 }
693
694 static int set_scale_exec(bContext *C, wmOperator *op)
695 {
696   return do_set_scale(C, op, false, false);
697 }
698
699 static int set_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
700 {
701   SpaceClip *sc = CTX_wm_space_clip(C);
702   MovieClip *clip = ED_space_clip_get_clip(sc);
703
704   if (!RNA_struct_property_is_set(op->ptr, "distance")) {
705     RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
706   }
707
708   return set_scale_exec(C, op);
709 }
710
711 void CLIP_OT_set_scale(wmOperatorType *ot)
712 {
713   /* identifiers */
714   ot->name = "Set Scale";
715   ot->description = "Set scale of scene by scaling camera (or its parent if present)";
716   ot->idname = "CLIP_OT_set_scale";
717
718   /* api callbacks */
719   ot->exec = set_scale_exec;
720   ot->invoke = set_scale_invoke;
721   ot->poll = set_orientation_poll;
722
723   /* flags */
724   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
725
726   /* properties */
727   RNA_def_float(ot->srna,
728                 "distance",
729                 0.0f,
730                 -FLT_MAX,
731                 FLT_MAX,
732                 "Distance",
733                 "Distance between selected tracks",
734                 -100.0f,
735                 100.0f);
736 }
737
738 /********************** set solution scale operator *********************/
739
740 static bool set_solution_scale_poll(bContext *C)
741 {
742   SpaceClip *sc = CTX_wm_space_clip(C);
743   if (sc != NULL) {
744     MovieClip *clip = ED_space_clip_get_clip(sc);
745     if (clip != NULL) {
746       MovieTracking *tracking = &clip->tracking;
747       MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
748       return (tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0;
749     }
750   }
751   return false;
752 }
753
754 static int set_solution_scale_exec(bContext *C, wmOperator *op)
755 {
756   return do_set_scale(C, op, true, false);
757 }
758
759 static int set_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
760 {
761   SpaceClip *sc = CTX_wm_space_clip(C);
762   MovieClip *clip = ED_space_clip_get_clip(sc);
763
764   if (!RNA_struct_property_is_set(op->ptr, "distance")) {
765     RNA_float_set(op->ptr, "distance", clip->tracking.settings.object_distance);
766   }
767
768   return set_solution_scale_exec(C, op);
769 }
770
771 void CLIP_OT_set_solution_scale(wmOperatorType *ot)
772 {
773   /* identifiers */
774   ot->name = "Set Solution Scale";
775   ot->description =
776       "Set object solution scale using distance between "
777       "two selected tracks";
778   ot->idname = "CLIP_OT_set_solution_scale";
779
780   /* api callbacks */
781   ot->exec = set_solution_scale_exec;
782   ot->invoke = set_solution_scale_invoke;
783   ot->poll = set_solution_scale_poll;
784
785   /* flags */
786   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
787
788   /* properties */
789   RNA_def_float(ot->srna,
790                 "distance",
791                 0.0f,
792                 -FLT_MAX,
793                 FLT_MAX,
794                 "Distance",
795                 "Distance between selected tracks",
796                 -100.0f,
797                 100.0f);
798 }
799
800 /********************** apply solution scale operator *********************/
801
802 static bool apply_solution_scale_poll(bContext *C)
803 {
804   SpaceClip *sc = CTX_wm_space_clip(C);
805   if (sc != NULL) {
806     MovieClip *clip = ED_space_clip_get_clip(sc);
807     if (clip != NULL) {
808       MovieTracking *tracking = &clip->tracking;
809       MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
810       return (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0;
811     }
812   }
813   return 0;
814 }
815
816 static int apply_solution_scale_exec(bContext *C, wmOperator *op)
817 {
818   return do_set_scale(C, op, false, true);
819 }
820
821 static int apply_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
822 {
823   SpaceClip *sc = CTX_wm_space_clip(C);
824   MovieClip *clip = ED_space_clip_get_clip(sc);
825   if (!RNA_struct_property_is_set(op->ptr, "distance")) {
826     RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
827   }
828   return apply_solution_scale_exec(C, op);
829 }
830
831 void CLIP_OT_apply_solution_scale(wmOperatorType *ot)
832 {
833   /* identifiers */
834   ot->name = "Apply Solution Scale";
835   ot->description =
836       "Apply scale on solution itself to make distance between "
837       "selected tracks equals to desired";
838   ot->idname = "CLIP_OT_apply_solution_scale";
839
840   /* api callbacks */
841   ot->exec = apply_solution_scale_exec;
842   ot->invoke = apply_solution_scale_invoke;
843   ot->poll = apply_solution_scale_poll;
844
845   /* flags */
846   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
847
848   /* properties */
849   RNA_def_float(ot->srna,
850                 "distance",
851                 0.0f,
852                 -FLT_MAX,
853                 FLT_MAX,
854                 "Distance",
855                 "Distance between selected tracks",
856                 -100.0f,
857                 100.0f);
858 }