WM: refactor gestures for use as tools
[blender.git] / source / blender / editors / space_view3d / view3d_snap.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_view3d/view3d_snap.c
29  *  \ingroup spview3d
30  */
31
32
33 #include "DNA_armature_types.h"
34 #include "DNA_object_types.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_utildefines.h"
38 #include "BLI_math.h"
39
40 #include "BKE_action.h"
41 #include "BKE_armature.h"
42 #include "BKE_context.h"
43 #include "BKE_depsgraph.h"
44 #include "BKE_main.h"
45 #include "BKE_mball.h"
46 #include "BKE_object.h"
47 #include "BKE_report.h"
48 #include "BKE_tracking.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55
56 #include "ED_object.h"
57 #include "ED_transverts.h"
58 #include "ED_keyframing.h"
59 #include "ED_screen.h"
60
61 #include "view3d_intern.h"
62
63 static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]);
64 static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]);
65
66
67 /* *********************** operators ******************** */
68
69 static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
70 {
71         Object *obedit = CTX_data_edit_object(C);
72         Scene *scene = CTX_data_scene(C);
73         RegionView3D *rv3d = CTX_wm_region_data(C);
74         TransVertStore tvs = {NULL};
75         TransVert *tv;
76         float gridf, imat[3][3], bmat[3][3], vec[3];
77         int a;
78
79         gridf = rv3d->gridview;
80
81         if (obedit) {
82                 if (ED_transverts_check_obedit(obedit))
83                         ED_transverts_create_from_obedit(&tvs, obedit, 0);
84                 if (tvs.transverts_tot == 0)
85                         return OPERATOR_CANCELLED;
86
87                 copy_m3_m4(bmat, obedit->obmat);
88                 invert_m3_m3(imat, bmat);
89                 
90                 tv = tvs.transverts;
91                 for (a = 0; a < tvs.transverts_tot; a++, tv++) {
92                         copy_v3_v3(vec, tv->loc);
93                         mul_m3_v3(bmat, vec);
94                         add_v3_v3(vec, obedit->obmat[3]);
95                         vec[0] = gridf * floorf(0.5f + vec[0] / gridf);
96                         vec[1] = gridf * floorf(0.5f + vec[1] / gridf);
97                         vec[2] = gridf * floorf(0.5f + vec[2] / gridf);
98                         sub_v3_v3(vec, obedit->obmat[3]);
99                         
100                         mul_m3_v3(imat, vec);
101                         copy_v3_v3(tv->loc, vec);
102                 }
103                 
104                 ED_transverts_update_obedit(&tvs, obedit);
105                 ED_transverts_free(&tvs);
106         }
107         else {
108                 struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
109
110                 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
111                 {
112                         if (ob->mode & OB_MODE_POSE) {
113                                 bPoseChannel *pchan;
114                                 bArmature *arm = ob->data;
115                                 
116                                 invert_m4_m4(ob->imat, ob->obmat);
117                                 
118                                 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
119                                         if (pchan->bone->flag & BONE_SELECTED) {
120                                                 if (pchan->bone->layer & arm->layer) {
121                                                         if ((pchan->bone->flag & BONE_CONNECTED) == 0) {
122                                                                 float nLoc[3];
123                                                                 
124                                                                 /* get nearest grid point to snap to */
125                                                                 copy_v3_v3(nLoc, pchan->pose_mat[3]);
126                                                                 /* We must operate in world space! */
127                                                                 mul_m4_v3(ob->obmat, nLoc);
128                                                                 vec[0] = gridf * floorf(0.5f + nLoc[0] / gridf);
129                                                                 vec[1] = gridf * floorf(0.5f + nLoc[1] / gridf);
130                                                                 vec[2] = gridf * floorf(0.5f + nLoc[2] / gridf);
131                                                                 /* Back in object space... */
132                                                                 mul_m4_v3(ob->imat, vec);
133                                                                 
134                                                                 /* Get location of grid point in pose space. */
135                                                                 BKE_armature_loc_pose_to_bone(pchan, vec, vec);
136                                                                 
137                                                                 /* adjust location */
138                                                                 if ((pchan->protectflag & OB_LOCK_LOCX) == 0)
139                                                                         pchan->loc[0] = vec[0];
140                                                                 if ((pchan->protectflag & OB_LOCK_LOCY) == 0)
141                                                                         pchan->loc[1] = vec[1];
142                                                                 if ((pchan->protectflag & OB_LOCK_LOCZ) == 0)
143                                                                         pchan->loc[2] = vec[2];
144
145                                                                 /* auto-keyframing */
146                                                                 ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
147                                                         }
148                                                         /* if the bone has a parent and is connected to the parent,
149                                                          * don't do anything - will break chain unless we do auto-ik.
150                                                          */
151                                                 }
152                                         }
153                                 }
154                                 ob->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK);
155                                 
156                                 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
157                         }
158                         else {
159                                 vec[0] = -ob->obmat[3][0] + gridf * floorf(0.5f + ob->obmat[3][0] / gridf);
160                                 vec[1] = -ob->obmat[3][1] + gridf * floorf(0.5f + ob->obmat[3][1] / gridf);
161                                 vec[2] = -ob->obmat[3][2] + gridf * floorf(0.5f + ob->obmat[3][2] / gridf);
162                                 
163                                 if (ob->parent) {
164                                         float originmat[3][3];
165                                         BKE_object_where_is_calc_ex(scene, NULL, ob, originmat);
166                                         
167                                         invert_m3_m3(imat, originmat);
168                                         mul_m3_v3(imat, vec);
169                                 }
170                                 if ((ob->protectflag & OB_LOCK_LOCX) == 0)
171                                         ob->loc[0] += vec[0];
172                                 if ((ob->protectflag & OB_LOCK_LOCY) == 0)
173                                         ob->loc[1] += vec[1];
174                                 if ((ob->protectflag & OB_LOCK_LOCZ) == 0)
175                                         ob->loc[2] += vec[2];
176                                 
177                                 /* auto-keyframing */
178                                 ED_autokeyframe_object(C, scene, ob, ks);
179
180                                 DAG_id_tag_update(&ob->id, OB_RECALC_OB);
181                         }
182                 }
183                 CTX_DATA_END;
184         }
185
186         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
187         
188         return OPERATOR_FINISHED;
189 }
190
191 void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot)
192 {
193         /* identifiers */
194         ot->name = "Snap Selection to Grid";
195         ot->description = "Snap selected item(s) to nearest grid division";
196         ot->idname = "VIEW3D_OT_snap_selected_to_grid";
197         
198         /* api callbacks */
199         ot->exec = snap_sel_to_grid_exec;
200         ot->poll = ED_operator_region_view3d_active;
201         
202         /* flags */
203         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
204 }
205
206 /* *************************************************** */
207
208 static int snap_selected_to_location(bContext *C, const float snap_target_global[3], const bool use_offset)
209 {
210         Scene *scene = CTX_data_scene(C);
211         Object *obedit = CTX_data_edit_object(C);
212         Object *obact = CTX_data_active_object(C);
213         View3D *v3d = CTX_wm_view3d(C);
214         TransVertStore tvs = {NULL};
215         TransVert *tv;
216         float imat[3][3], bmat[3][3];
217         float center_global[3];
218         float offset_global[3];
219         int a;
220
221         if (use_offset) {
222                 if ((v3d && v3d->around == V3D_AROUND_ACTIVE) &&
223                     snap_calc_active_center(C, true, center_global))
224                 {
225                         /* pass */
226                 }
227                 else {
228                         snap_curs_to_sel_ex(C, center_global);
229                 }
230                 sub_v3_v3v3(offset_global, snap_target_global, center_global);
231         }
232
233         if (obedit) {
234                 float snap_target_local[3];
235                 
236                 if (ED_transverts_check_obedit(obedit))
237                         ED_transverts_create_from_obedit(&tvs, obedit, 0);
238                 if (tvs.transverts_tot == 0)
239                         return OPERATOR_CANCELLED;
240
241                 copy_m3_m4(bmat, obedit->obmat);
242                 invert_m3_m3(imat, bmat);
243                 
244                 /* get the cursor in object space */
245                 sub_v3_v3v3(snap_target_local, snap_target_global, obedit->obmat[3]);
246                 mul_m3_v3(imat, snap_target_local);
247
248                 if (use_offset) {
249                         float offset_local[3];
250
251                         mul_v3_m3v3(offset_local, imat, offset_global);
252
253                         tv = tvs.transverts;
254                         for (a = 0; a < tvs.transverts_tot; a++, tv++) {
255                                 add_v3_v3(tv->loc, offset_local);
256                         }
257                 }
258                 else {
259                         tv = tvs.transverts;
260                         for (a = 0; a < tvs.transverts_tot; a++, tv++) {
261                                 copy_v3_v3(tv->loc, snap_target_local);
262                         }
263                 }
264                 
265                 ED_transverts_update_obedit(&tvs, obedit);
266                 ED_transverts_free(&tvs);
267         }
268         else if (obact && (obact->mode & OB_MODE_POSE)) {
269                 struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
270
271                 bPoseChannel *pchan;
272                 bArmature *arm = obact->data;
273                 float snap_target_local[3];
274
275                 invert_m4_m4(obact->imat, obact->obmat);
276                 mul_v3_m4v3(snap_target_local, obact->imat, snap_target_global);
277
278                 for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
279                         if ((pchan->bone->flag & BONE_SELECTED) &&
280                             (PBONE_VISIBLE(arm, pchan->bone)) &&
281                             /* if the bone has a parent and is connected to the parent,
282                              * don't do anything - will break chain unless we do auto-ik.
283                              */
284                             (pchan->bone->flag & BONE_CONNECTED) == 0)
285                         {
286                                 pchan->bone->flag |= BONE_TRANSFORM;
287                         }
288                         else {
289                                 pchan->bone->flag &= ~BONE_TRANSFORM;
290                         }
291                 }
292
293                 for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
294                         if ((pchan->bone->flag & BONE_TRANSFORM) &&
295                             /* check that our parents not transformed (if we have one) */
296                             ((pchan->bone->parent &&
297                               BKE_armature_bone_flag_test_recursive(pchan->bone->parent, BONE_TRANSFORM)) == 0))
298                         {
299                                 /* Get position in pchan (pose) space. */
300                                 float cursor_pose[3];
301
302                                 if (use_offset) {
303                                         mul_v3_m4v3(cursor_pose, obact->obmat, pchan->pose_mat[3]);
304                                         add_v3_v3(cursor_pose, offset_global);
305
306                                         mul_m4_v3(obact->imat, cursor_pose);
307                                         BKE_armature_loc_pose_to_bone(pchan, cursor_pose, cursor_pose);
308                                 }
309                                 else {
310                                         BKE_armature_loc_pose_to_bone(pchan, snap_target_local, cursor_pose);
311                                 }
312
313                                 /* copy new position */
314                                 if ((pchan->protectflag & OB_LOCK_LOCX) == 0)
315                                         pchan->loc[0] = cursor_pose[0];
316                                 if ((pchan->protectflag & OB_LOCK_LOCY) == 0)
317                                         pchan->loc[1] = cursor_pose[1];
318                                 if ((pchan->protectflag & OB_LOCK_LOCZ) == 0)
319                                         pchan->loc[2] = cursor_pose[2];
320
321                                 /* auto-keyframing */
322                                 ED_autokeyframe_pchan(C, scene, obact, pchan, ks);
323                         }
324                 }
325
326                 for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
327                         pchan->bone->flag &= ~BONE_TRANSFORM;
328                 }
329
330                 obact->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK);
331
332                 DAG_id_tag_update(&obact->id, OB_RECALC_DATA);
333         }
334         else {
335                 struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
336                 Main *bmain = CTX_data_main(C);
337
338                 ListBase ctx_data_list;
339                 CollectionPointerLink *ctx_ob;
340                 Object *ob;
341
342                 CTX_data_selected_editable_objects(C, &ctx_data_list);
343
344                 /* reset flags */
345                 for (ob = bmain->object.first; ob; ob = ob->id.next) {
346                         ob->flag &= ~OB_DONE;
347                 }
348
349                 /* tag objects we're transforming */
350                 for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) {
351                         ob = ctx_ob->ptr.data;
352                         ob->flag |= OB_DONE;
353                 }
354
355                 for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) {
356                         ob = ctx_ob->ptr.data;
357
358                         if ((ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) == 0) {
359
360                                 float cursor_parent[3];  /* parent-relative */
361
362                                 if (use_offset) {
363                                         add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global);
364                                 }
365                                 else {
366                                         copy_v3_v3(cursor_parent, snap_target_global);
367                                 }
368
369                                 sub_v3_v3(cursor_parent, ob->obmat[3]);
370
371                                 if (ob->parent) {
372                                         float originmat[3][3];
373                                         BKE_object_where_is_calc_ex(scene, NULL, ob, originmat);
374
375                                         invert_m3_m3(imat, originmat);
376                                         mul_m3_v3(imat, cursor_parent);
377                                 }
378                                 if ((ob->protectflag & OB_LOCK_LOCX) == 0)
379                                         ob->loc[0] += cursor_parent[0];
380                                 if ((ob->protectflag & OB_LOCK_LOCY) == 0)
381                                         ob->loc[1] += cursor_parent[1];
382                                 if ((ob->protectflag & OB_LOCK_LOCZ) == 0)
383                                         ob->loc[2] += cursor_parent[2];
384
385                                 /* auto-keyframing */
386                                 ED_autokeyframe_object(C, scene, ob, ks);
387
388                                 DAG_id_tag_update(&ob->id, OB_RECALC_OB);
389                         }
390                 }
391
392                 BLI_freelistN(&ctx_data_list);
393         }
394
395         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
396         
397         return OPERATOR_FINISHED;
398 }
399
400 static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op)
401 {
402         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
403
404         Scene *scene = CTX_data_scene(C);
405         View3D *v3d = CTX_wm_view3d(C);
406
407         const float *snap_target_global = ED_view3d_cursor3d_get(scene, v3d);
408
409         return snap_selected_to_location(C, snap_target_global, use_offset);
410 }
411
412 void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot)
413 {
414         /* identifiers */
415         ot->name = "Snap Selection to Cursor";
416         ot->description = "Snap selected item(s) to cursor";
417         ot->idname = "VIEW3D_OT_snap_selected_to_cursor";
418         
419         /* api callbacks */
420         ot->exec = snap_selected_to_cursor_exec;
421         ot->poll = ED_operator_view3d_active;
422         
423         /* flags */
424         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
425
426         /* rna */
427         RNA_def_boolean(ot->srna, "use_offset", 1, "Offset", "");
428 }
429
430 static int snap_selected_to_active_exec(bContext *C, wmOperator *op)
431 {
432         float snap_target_global[3];
433
434         if (snap_calc_active_center(C, false, snap_target_global) == false) {
435                 BKE_report(op->reports, RPT_ERROR, "No active element found!");
436                 return OPERATOR_CANCELLED;
437         }
438
439         return snap_selected_to_location(C, snap_target_global, false);
440 }
441
442 void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot)
443 {
444         /* identifiers */
445         ot->name = "Snap Selection to Active";
446         ot->description = "Snap selected item(s) to the active item";
447         ot->idname = "VIEW3D_OT_snap_selected_to_active";
448
449         /* api callbacks */
450         ot->exec = snap_selected_to_active_exec;
451         ot->poll = ED_operator_view3d_active;
452
453         /* flags */
454         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
455 }
456
457
458 /* *************************************************** */
459
460 static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
461 {
462         Scene *scene = CTX_data_scene(C);
463         RegionView3D *rv3d = CTX_wm_region_data(C);
464         View3D *v3d = CTX_wm_view3d(C);
465         float gridf, *curs;
466
467         gridf = rv3d->gridview;
468         curs = ED_view3d_cursor3d_get(scene, v3d);
469
470         curs[0] = gridf * floorf(0.5f + curs[0] / gridf);
471         curs[1] = gridf * floorf(0.5f + curs[1] / gridf);
472         curs[2] = gridf * floorf(0.5f + curs[2] / gridf);
473         
474         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);  /* hrm */
475
476         return OPERATOR_FINISHED;
477 }
478
479 void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot)
480 {
481         /* identifiers */
482         ot->name = "Snap Cursor to Grid";
483         ot->description = "Snap cursor to nearest grid division";
484         ot->idname = "VIEW3D_OT_snap_cursor_to_grid";
485         
486         /* api callbacks */
487         ot->exec = snap_curs_to_grid_exec;
488         ot->poll = ED_operator_region_view3d_active;
489         
490         /* flags */
491         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
492 }
493
494 /* **************************************************** */
495
496 static void bundle_midpoint(Scene *scene, Object *ob, float vec[3])
497 {
498         MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
499         MovieTracking *tracking;
500         MovieTrackingObject *object;
501         bool ok = false;
502         float min[3], max[3], mat[4][4], pos[3], cammat[4][4];
503
504         if (!clip)
505                 return;
506
507         tracking = &clip->tracking;
508
509         copy_m4_m4(cammat, ob->obmat);
510
511         BKE_tracking_get_camera_object_matrix(scene, ob, mat);
512
513         INIT_MINMAX(min, max);
514
515         for (object = tracking->objects.first; object; object = object->next) {
516                 ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
517                 MovieTrackingTrack *track = tracksbase->first;
518                 float obmat[4][4];
519
520                 if (object->flag & TRACKING_OBJECT_CAMERA) {
521                         copy_m4_m4(obmat, mat);
522                 }
523                 else {
524                         float imat[4][4];
525
526                         BKE_tracking_camera_get_reconstructed_interpolate(tracking, object, scene->r.cfra, imat);
527                         invert_m4(imat);
528
529                         mul_m4_m4m4(obmat, cammat, imat);
530                 }
531
532                 while (track) {
533                         if ((track->flag & TRACK_HAS_BUNDLE) && TRACK_SELECTED(track)) {
534                                 ok = 1;
535                                 mul_v3_m4v3(pos, obmat, track->bundle_pos);
536                                 minmax_v3v3_v3(min, max, pos);
537                         }
538
539                         track = track->next;
540                 }
541         }
542
543         if (ok) {
544                 mid_v3_v3v3(vec, min, max);
545         }
546 }
547
548 static bool snap_curs_to_sel_ex(bContext *C, float cursor[3])
549 {
550         Object *obedit = CTX_data_edit_object(C);
551         Scene *scene = CTX_data_scene(C);
552         View3D *v3d = CTX_wm_view3d(C);
553         TransVertStore tvs = {NULL};
554         TransVert *tv;
555         float bmat[3][3], vec[3], min[3], max[3], centroid[3];
556         int count, a;
557
558         count = 0;
559         INIT_MINMAX(min, max);
560         zero_v3(centroid);
561
562         if (obedit) {
563
564                 if (ED_transverts_check_obedit(obedit))
565                         ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_SKIP_HANDLES);
566
567                 if (tvs.transverts_tot == 0) {
568                         return false;
569                 }
570
571                 copy_m3_m4(bmat, obedit->obmat);
572                 
573                 tv = tvs.transverts;
574                 for (a = 0; a < tvs.transverts_tot; a++, tv++) {
575                         copy_v3_v3(vec, tv->loc);
576                         mul_m3_v3(bmat, vec);
577                         add_v3_v3(vec, obedit->obmat[3]);
578                         add_v3_v3(centroid, vec);
579                         minmax_v3v3_v3(min, max, vec);
580                 }
581                 
582                 if (v3d->around == V3D_AROUND_CENTER_MEAN) {
583                         mul_v3_fl(centroid, 1.0f / (float)tvs.transverts_tot);
584                         copy_v3_v3(cursor, centroid);
585                 }
586                 else {
587                         mid_v3_v3v3(cursor, min, max);
588                 }
589
590                 ED_transverts_free(&tvs);
591         }
592         else {
593                 Object *obact = CTX_data_active_object(C);
594                 
595                 if (obact && (obact->mode & OB_MODE_POSE)) {
596                         bArmature *arm = obact->data;
597                         bPoseChannel *pchan;
598                         for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
599                                 if (arm->layer & pchan->bone->layer) {
600                                         if (pchan->bone->flag & BONE_SELECTED) {
601                                                 copy_v3_v3(vec, pchan->pose_head);
602                                                 mul_m4_v3(obact->obmat, vec);
603                                                 add_v3_v3(centroid, vec);
604                                                 minmax_v3v3_v3(min, max, vec);
605                                                 count++;
606                                         }
607                                 }
608                         }
609                 }
610                 else {
611                         CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
612                         {
613                                 copy_v3_v3(vec, ob->obmat[3]);
614
615                                 /* special case for camera -- snap to bundles */
616                                 if (ob->type == OB_CAMERA) {
617                                         /* snap to bundles should happen only when bundles are visible */
618                                         if (v3d->flag2 & V3D_SHOW_RECONSTRUCTION) {
619                                                 bundle_midpoint(scene, ob, vec);
620                                         }
621                                 }
622
623                                 add_v3_v3(centroid, vec);
624                                 minmax_v3v3_v3(min, max, vec);
625                                 count++;
626                         }
627                         CTX_DATA_END;
628                 }
629
630                 if (count == 0) {
631                         return false;
632                 }
633
634                 if (v3d->around == V3D_AROUND_CENTER_MEAN) {
635                         mul_v3_fl(centroid, 1.0f / (float)count);
636                         copy_v3_v3(cursor, centroid);
637                 }
638                 else {
639                         mid_v3_v3v3(cursor, min, max);
640                 }
641         }
642         return true;
643 }
644
645 static int snap_curs_to_sel_exec(bContext *C, wmOperator *UNUSED(op))
646 {
647         Scene *scene = CTX_data_scene(C);
648         View3D *v3d = CTX_wm_view3d(C);
649         float *curs;
650
651         curs = ED_view3d_cursor3d_get(scene, v3d);
652
653         if (snap_curs_to_sel_ex(C, curs)) {
654                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
655
656                 return OPERATOR_FINISHED;
657         }
658         else {
659                 return OPERATOR_CANCELLED;
660         }
661 }
662
663 void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot)
664 {
665         /* identifiers */
666         ot->name = "Snap Cursor to Selected";
667         ot->description = "Snap cursor to center of selected item(s)";
668         ot->idname = "VIEW3D_OT_snap_cursor_to_selected";
669         
670         /* api callbacks */
671         ot->exec = snap_curs_to_sel_exec;
672         ot->poll = ED_operator_view3d_active;
673         
674         /* flags */
675         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
676 }
677
678 /* ********************************************** */
679
680 /* this could be exported to be a generic function
681  * see: calculateCenterActive */
682
683 static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3])
684 {
685         Object *obedit = CTX_data_edit_object(C);
686
687         if (obedit) {
688                 if (ED_object_editmode_calc_active_center(obedit, select_only, r_center)) {
689                         mul_m4_v3(obedit->obmat, r_center);
690                         return true;
691                 }
692         }
693         else {
694                 Object *ob = CTX_data_active_object(C);
695
696                 if (ob) {
697                         if (ob->mode & OB_MODE_POSE) {
698                                 bPoseChannel *pchan = BKE_pose_channel_active(ob);
699                                 if (pchan) {
700                                         if (!select_only || (pchan->bone->flag & BONE_SELECTED)) {
701                                                 copy_v3_v3(r_center, pchan->pose_head);
702                                                 mul_m4_v3(ob->obmat, r_center);
703                                                 return true;
704                                         }
705                                 }
706                         }
707                         else {
708                                 if (!select_only || (ob->flag & SELECT)) {
709                                         copy_v3_v3(r_center, ob->obmat[3]);
710                                         return true;
711                                 }
712                         }
713                 }
714         }
715
716         return false;
717 }
718
719 static int snap_curs_to_active_exec(bContext *C, wmOperator *UNUSED(op))
720 {
721         Scene *scene = CTX_data_scene(C);
722         View3D *v3d = CTX_wm_view3d(C);
723         float *curs;
724         
725         curs = ED_view3d_cursor3d_get(scene, v3d);
726
727         if (snap_calc_active_center(C, false, curs)) {
728                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
729                 return OPERATOR_FINISHED;
730         }
731         else {
732                 return OPERATOR_CANCELLED;
733         }
734 }
735
736 void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot)
737 {
738         /* identifiers */
739         ot->name = "Snap Cursor to Active";
740         ot->description = "Snap cursor to active item";
741         ot->idname = "VIEW3D_OT_snap_cursor_to_active";
742         
743         /* api callbacks */
744         ot->exec = snap_curs_to_active_exec;
745         ot->poll = ED_operator_view3d_active;
746         
747         /* flags */
748         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
749 }
750
751 /* **************************************************** */
752 /*New Code - Snap Cursor to Center -*/
753 static int snap_curs_to_center_exec(bContext *C, wmOperator *UNUSED(op))
754 {
755         Scene *scene = CTX_data_scene(C);
756         View3D *v3d = CTX_wm_view3d(C);
757         float *curs;
758         curs = ED_view3d_cursor3d_get(scene, v3d);
759
760         zero_v3(curs);
761         
762         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
763         
764         return OPERATOR_FINISHED;
765 }
766
767 void VIEW3D_OT_snap_cursor_to_center(wmOperatorType *ot)
768 {
769         /* identifiers */
770         ot->name = "Snap Cursor to Center";
771         ot->description = "Snap cursor to the Center";
772         ot->idname = "VIEW3D_OT_snap_cursor_to_center";
773         
774         /* api callbacks */
775         ot->exec = snap_curs_to_center_exec;
776         ot->poll = ED_operator_view3d_active;
777         
778         /* flags */
779         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
780 }
781
782 /* **************************************************** */
783
784
785 bool ED_view3d_minmax_verts(Object *obedit, float min[3], float max[3])
786 {
787         TransVertStore tvs = {NULL};
788         TransVert *tv;
789         float centroid[3], vec[3], bmat[3][3];
790         int a;
791
792         /* metaballs are an exception */
793         if (obedit->type == OB_MBALL) {
794                 float ob_min[3], ob_max[3];
795                 bool changed;
796
797                 changed = BKE_mball_minmax_ex(obedit->data, ob_min, ob_max, obedit->obmat, SELECT);
798                 if (changed) {
799                         minmax_v3v3_v3(min, max, ob_min);
800                         minmax_v3v3_v3(min, max, ob_max);
801                 }
802                 return changed;
803         }
804
805         if (ED_transverts_check_obedit(obedit))
806                 ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS);
807         
808         if (tvs.transverts_tot == 0)
809                 return false;
810
811         copy_m3_m4(bmat, obedit->obmat);
812         
813         tv = tvs.transverts;
814         for (a = 0; a < tvs.transverts_tot; a++, tv++) {
815                 copy_v3_v3(vec, (tv->flag & TX_VERT_USE_MAPLOC) ? tv->maploc : tv->loc);
816                 mul_m3_v3(bmat, vec);
817                 add_v3_v3(vec, obedit->obmat[3]);
818                 add_v3_v3(centroid, vec);
819                 minmax_v3v3_v3(min, max, vec);
820         }
821         
822         ED_transverts_free(&tvs);
823         
824         return true;
825 }