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