Cleanup: use 'e' prefix for enum typedefs
[blender-staging.git] / source / blender / editors / transform / transform_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): Martin Poirier
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/transform/transform_snap.c
29  *  \ingroup edtransform
30  */
31
32 #include <stdlib.h>
33 #include <math.h>
34 #include <float.h>
35 #include <stdio.h>
36
37 #include "PIL_time.h"
38
39 #include "DNA_scene_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_meshdata_types.h"  /* Temporary, for snapping to other unselected meshes */
42 #include "DNA_node_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_view3d_types.h"
46 #include "DNA_windowmanager_types.h"
47
48 #include "BLI_math.h"
49 #include "BLI_blenlib.h"
50 #include "BLI_utildefines.h"
51
52 #include "BIF_gl.h"
53
54 #include "BKE_DerivedMesh.h"
55 #include "BKE_global.h"
56 #include "BKE_object.h"
57 #include "BKE_anim.h"  /* for duplis */
58 #include "BKE_context.h"
59 #include "BKE_editmesh.h"
60 #include "BKE_sequencer.h"
61 #include "BKE_main.h"
62
63 #include "RNA_access.h"
64
65 #include "WM_types.h"
66
67 #include "ED_image.h"
68 #include "ED_node.h"
69 #include "ED_uvedit.h"
70 #include "ED_view3d.h"
71 #include "ED_transform_snap_object_context.h"
72
73 #include "UI_resources.h"
74 #include "UI_view2d.h"
75
76 #include "MEM_guardedalloc.h"
77
78 #include "transform.h"
79
80 /* this should be passed as an arg for use in snap functions */
81 #undef BASACT
82
83 /* use half of flt-max so we can scale up without an exception */
84
85 /********************* PROTOTYPES ***********************/
86
87 static void setSnappingCallback(TransInfo *t);
88
89 static void ApplySnapTranslation(TransInfo *t, float vec[3]);
90 static void ApplySnapRotation(TransInfo *t, float *vec);
91 static void ApplySnapResize(TransInfo *t, float vec[2]);
92
93 /* static void CalcSnapGrid(TransInfo *t, float *vec); */
94 static void CalcSnapGeometry(TransInfo *t, float *vec);
95
96 static void TargetSnapMedian(TransInfo *t);
97 static void TargetSnapCenter(TransInfo *t);
98 static void TargetSnapClosest(TransInfo *t);
99 static void TargetSnapActive(TransInfo *t);
100
101 static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]);
102 static float TranslationBetween(TransInfo *t, const float p1[3], const float p2[3]);
103 static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3]);
104
105
106 /****************** IMPLEMENTATIONS *********************/
107
108 static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select);
109 static NodeBorder snapNodeBorder(int snap_node_mode);
110
111 #if 0
112 int BIF_snappingSupported(Object *obedit)
113 {
114         int status = 0;
115         
116         /* only support object mesh, armature, curves */
117         if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
118                 status = 1;
119         }
120         
121         return status;
122 }
123 #endif
124
125 bool validSnap(TransInfo *t)
126 {
127         return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) ||
128                (t->tsnap.status & (MULTI_POINTS | TARGET_INIT)) == (MULTI_POINTS | TARGET_INIT);
129 }
130
131 bool activeSnap(TransInfo *t)
132 {
133         return ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP) ||
134                ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT);
135 }
136
137 void drawSnapping(const struct bContext *C, TransInfo *t)
138 {
139         unsigned char col[4], selectedCol[4], activeCol[4];
140         
141         if (!activeSnap(t))
142                 return;
143         
144         UI_GetThemeColor3ubv(TH_TRANSFORM, col);
145         col[3] = 128;
146         
147         UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
148         selectedCol[3] = 128;
149         
150         UI_GetThemeColor3ubv(TH_ACTIVE, activeCol);
151         activeCol[3] = 192;
152         
153         if (t->spacetype == SPACE_VIEW3D) {
154                 if (validSnap(t)) {
155                         TransSnapPoint *p;
156                         View3D *v3d = CTX_wm_view3d(C);
157                         RegionView3D *rv3d = CTX_wm_region_view3d(C);
158                         float imat[4][4];
159                         float size;
160                         
161                         glDisable(GL_DEPTH_TEST);
162                         
163                         size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
164                         
165                         invert_m4_m4(imat, rv3d->viewmat);
166                         
167                         for (p = t->tsnap.points.first; p; p = p->next) {
168                                 if (p == t->tsnap.selectedPoint) {
169                                         glColor4ubv(selectedCol);
170                                 }
171                                 else {
172                                         glColor4ubv(col);
173                                 }
174                                 
175                                 drawcircball(GL_LINE_LOOP, p->co, ED_view3d_pixel_size(rv3d, p->co) * size * 0.75f, imat);
176                         }
177                         
178                         if (t->tsnap.status & POINT_INIT) {
179                                 glColor4ubv(activeCol);
180                                 
181                                 drawcircball(GL_LINE_LOOP, t->tsnap.snapPoint, ED_view3d_pixel_size(rv3d, t->tsnap.snapPoint) * size, imat);
182                         }
183                         
184                         /* draw normal if needed */
185                         if (usingSnappingNormal(t) && validSnappingNormal(t)) {
186                                 glColor4ubv(activeCol);
187                                 
188                                 glBegin(GL_LINES);
189                                 glVertex3f(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
190                                 glVertex3f(t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0],
191                                            t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1],
192                                            t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]);
193                                 glEnd();
194                         }
195                         
196                         if (v3d->zbuf)
197                                 glEnable(GL_DEPTH_TEST);
198                 }
199         }
200         else if (t->spacetype == SPACE_IMAGE) {
201                 if (validSnap(t)) {
202                         /* This will not draw, and Im nor sure why - campbell */
203 #if 0
204                         float xuser_asp, yuser_asp;
205                         int wi, hi;
206                         float w, h;
207                         
208                         calc_image_view(G.sima, 'f');   // float
209                         myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
210                         glLoadIdentity();
211                         
212                         ED_space_image_get_aspect(t->sa->spacedata.first, &xuser_aspx, &yuser_asp);
213                         ED_space_image_width(t->sa->spacedata.first, &wi, &hi);
214                         w = (((float)wi) / IMG_SIZE_FALLBACK) * G.sima->zoom * xuser_asp;
215                         h = (((float)hi) / IMG_SIZE_FALLBACK) * G.sima->zoom * yuser_asp;
216                         
217                         cpack(0xFFFFFF);
218                         glTranslate2fv(t->tsnap.snapPoint);
219                         
220                         //glRectf(0, 0, 1, 1);
221                         
222                         setlinestyle(0);
223                         cpack(0x0);
224                         fdrawline(-0.020 / w, 0, -0.1 / w, 0);
225                         fdrawline(0.1 / w, 0, 0.020 / w, 0);
226                         fdrawline(0, -0.020 / h, 0, -0.1 / h);
227                         fdrawline(0, 0.1 / h, 0, 0.020 / h);
228                         
229                         glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f);
230                         setlinestyle(0);
231 #endif
232                 }
233         }
234         else if (t->spacetype == SPACE_NODE) {
235                 if (validSnap(t)) {
236                         ARegion *ar = CTX_wm_region(C);
237                         TransSnapPoint *p;
238                         float size;
239                         
240                         size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
241                         
242                         glEnable(GL_BLEND);
243                         
244                         for (p = t->tsnap.points.first; p; p = p->next) {
245                                 if (p == t->tsnap.selectedPoint) {
246                                         glColor4ubv(selectedCol);
247                                 }
248                                 else {
249                                         glColor4ubv(col);
250                                 }
251                                 
252                                 ED_node_draw_snap(&ar->v2d, p->co, size, 0);
253                         }
254                         
255                         if (t->tsnap.status & POINT_INIT) {
256                                 glColor4ubv(activeCol);
257                                 
258                                 ED_node_draw_snap(&ar->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder);
259                         }
260                         
261                         glDisable(GL_BLEND);
262                 }
263         }
264 }
265
266 eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
267 {
268         eRedrawFlag status = TREDRAW_NOTHING;
269
270 #if 0 // XXX need a proper selector for all snap mode
271         if (BIF_snappingSupported(t->obedit) && event->type == TABKEY && event->shift) {
272                 /* toggle snap and reinit */
273                 t->settings->snap_flag ^= SCE_SNAP;
274                 initSnapping(t, NULL);
275                 status = TREDRAW_HARD;
276         }
277 #endif
278         if (event->type == MOUSEMOVE) {
279                 status |= updateSelectedSnapPoint(t);
280         }
281         
282         return status;
283 }
284
285 void applyProject(TransInfo *t)
286 {
287         /* XXX FLICKER IN OBJECT MODE */
288         if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) {
289                 TransData *td = t->data;
290                 float tvec[3];
291                 float imat[4][4];
292                 int i;
293         
294                 if (t->flag & (T_EDIT | T_POSE)) {
295                         Object *ob = t->obedit ? t->obedit : t->poseobj;
296                         invert_m4_m4(imat, ob->obmat);
297                 }
298
299                 for (i = 0; i < t->total; i++, td++) {
300                         float iloc[3], loc[3], no[3];
301                         float mval_fl[2];
302                         float dist_px = TRANSFORM_DIST_MAX_PX;
303                         
304                         if (td->flag & TD_NOACTION)
305                                 break;
306                         
307                         if (td->flag & TD_SKIP)
308                                 continue;
309
310                         if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
311                                 continue;
312                         
313                         copy_v3_v3(iloc, td->loc);
314                         if (t->flag & (T_EDIT | T_POSE)) {
315                                 Object *ob = t->obedit ? t->obedit : t->poseobj;
316                                 mul_m4_v3(ob->obmat, iloc);
317                         }
318                         else if (t->flag & T_OBJECT) {
319                                 /* TODO(sergey): Ideally force update is not needed here. */
320                                 td->ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
321                                 BKE_object_handle_update(G.main->eval_ctx, t->scene, td->ob);
322                                 copy_v3_v3(iloc, td->ob->obmat[3]);
323                         }
324                         
325                         if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
326                                 if (snapObjectsTransform(
327                                         t, mval_fl, &dist_px,
328                                         loc, no))
329                                 {
330 //                                      if (t->flag & (T_EDIT|T_POSE)) {
331 //                                              mul_m4_v3(imat, loc);
332 //                                      }
333
334                                         sub_v3_v3v3(tvec, loc, iloc);
335
336                                         mul_m3_v3(td->smtx, tvec);
337
338                                         add_v3_v3(td->loc, tvec);
339
340                                         if (t->tsnap.align && (t->flag & T_OBJECT)) {
341                                                 /* handle alignment as well */
342                                                 const float *original_normal;
343                                                 float mat[3][3];
344
345                                                 /* In pose mode, we want to align normals with Y axis of bones... */
346                                                 original_normal = td->axismtx[2];
347
348                                                 rotation_between_vecs_to_mat3(mat, original_normal, no);
349
350                                                 transform_data_ext_rotate(td, mat, true);
351
352                                                 /* TODO support constraints for rotation too? see ElementRotation */
353                                         }
354                                 }
355                         }
356                         
357                         //XXX constraintTransLim(t, td);
358                 }
359         }
360 }
361
362 void applyGridAbsolute(TransInfo *t)
363 {
364         float grid_size = 0.0f;
365         GearsType grid_action;
366         TransData *td;
367         float (*obmat)[4] = NULL;
368         bool use_obmat = false;
369         int i;
370         
371         if (!(activeSnap(t) && (ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID))))
372                 return;
373         
374         grid_action = BIG_GEARS;
375         if (t->modifiers & MOD_PRECISION)
376                 grid_action = SMALL_GEARS;
377         
378         switch (grid_action) {
379                 case NO_GEARS: grid_size = t->snap_spatial[0]; break;
380                 case BIG_GEARS: grid_size = t->snap_spatial[1]; break;
381                 case SMALL_GEARS: grid_size = t->snap_spatial[2]; break;
382         }
383         /* early exit on unusable grid size */
384         if (grid_size == 0.0f)
385                 return;
386         
387         if (t->flag & (T_EDIT | T_POSE)) {
388                 Object *ob = t->obedit ? t->obedit : t->poseobj;
389                 obmat = ob->obmat;
390                 use_obmat = true;
391         }
392         
393         for (i = 0, td = t->data; i < t->total; i++, td++) {
394                 float iloc[3], loc[3], tvec[3];
395                 
396                 if (td->flag & TD_NOACTION)
397                         break;
398                 
399                 if (td->flag & TD_SKIP)
400                         continue;
401                 
402                 if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
403                         continue;
404                 
405                 copy_v3_v3(iloc, td->loc);
406                 if (use_obmat) {
407                         mul_m4_v3(obmat, iloc);
408                 }
409                 else if (t->flag & T_OBJECT) {
410                         td->ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
411                         BKE_object_handle_update(G.main->eval_ctx, t->scene, td->ob);
412                         copy_v3_v3(iloc, td->ob->obmat[3]);
413                 }
414                 
415                 mul_v3_v3fl(loc, iloc, 1.0f / grid_size);
416                 loc[0] = roundf(loc[0]);
417                 loc[1] = roundf(loc[1]);
418                 loc[2] = roundf(loc[2]);
419                 mul_v3_fl(loc, grid_size);
420
421                 sub_v3_v3v3(tvec, loc, iloc);
422                 mul_m3_v3(td->smtx, tvec);
423                 add_v3_v3(td->loc, tvec);
424         }
425 }
426
427 void applySnapping(TransInfo *t, float *vec)
428 {
429         /* project is not applied this way */
430         if (t->tsnap.project)
431                 return;
432         
433         if (t->tsnap.status & SNAP_FORCED) {
434                 t->tsnap.targetSnap(t);
435         
436                 t->tsnap.applySnap(t, vec);
437         }
438         else if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) && activeSnap(t)) {
439                 double current = PIL_check_seconds_timer();
440                 
441                 // Time base quirky code to go around findnearest slowness
442                 /* !TODO! add exception for object mode, no need to slow it down then */
443                 if (current - t->tsnap.last >= 0.01) {
444                         t->tsnap.calcSnap(t, vec);
445                         t->tsnap.targetSnap(t);
446         
447                         t->tsnap.last = current;
448                 }
449                 if (validSnap(t)) {
450                         t->tsnap.applySnap(t, vec);
451                 }
452         }
453 }
454
455 void resetSnapping(TransInfo *t)
456 {
457         t->tsnap.status = 0;
458         t->tsnap.align = false;
459         t->tsnap.project = 0;
460         t->tsnap.mode = 0;
461         t->tsnap.modeSelect = 0;
462         t->tsnap.target = 0;
463         t->tsnap.last = 0;
464         t->tsnap.applySnap = NULL;
465
466         t->tsnap.snapNormal[0] = 0;
467         t->tsnap.snapNormal[1] = 0;
468         t->tsnap.snapNormal[2] = 0;
469         
470         t->tsnap.snapNodeBorder = 0;
471 }
472
473 bool usingSnappingNormal(TransInfo *t)
474 {
475         return t->tsnap.align;
476 }
477
478 bool validSnappingNormal(TransInfo *t)
479 {
480         if (validSnap(t)) {
481                 if (!is_zero_v3(t->tsnap.snapNormal)) {
482                         return true;
483                 }
484         }
485         
486         return false;
487 }
488
489 static bool bm_edge_is_snap_target(BMEdge *e, void *UNUSED(user_data))
490 {
491         if (BM_elem_flag_test(e, BM_ELEM_SELECT | BM_ELEM_HIDDEN) ||
492             BM_elem_flag_test(e->v1, BM_ELEM_SELECT) ||
493             BM_elem_flag_test(e->v2, BM_ELEM_SELECT))
494         {
495                 return false;
496         }
497
498         return true;
499 }
500
501 static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data))
502 {
503         if (BM_elem_flag_test(f, BM_ELEM_SELECT | BM_ELEM_HIDDEN)) {
504                 return false;
505         }
506
507         BMLoop *l_iter, *l_first;
508         l_iter = l_first = BM_FACE_FIRST_LOOP(f);
509         do {
510                 if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
511                         return false;
512                 }
513         } while ((l_iter = l_iter->next) != l_first);
514
515         return true;
516 }
517
518 static void initSnappingMode(TransInfo *t)
519 {
520         ToolSettings *ts = t->settings;
521         Object *obedit = t->obedit;
522         Scene *scene = t->scene;
523         Base *base_act = scene->basact;
524
525         if (t->spacetype == SPACE_NODE) {
526                 /* force project off when not supported */
527                 t->tsnap.project = 0;
528                 
529                 t->tsnap.mode = ts->snap_node_mode;
530         }
531         else if (t->spacetype == SPACE_IMAGE) {
532                 /* force project off when not supported */
533                 t->tsnap.project = 0;
534                 
535                 t->tsnap.mode = ts->snap_uv_mode;
536         }
537         else {
538                 /* force project off when not supported */
539                 if (ts->snap_mode != SCE_SNAP_MODE_FACE)
540                         t->tsnap.project = 0;
541                 
542                 t->tsnap.mode = ts->snap_mode;
543         }
544
545         if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) &&  /* Only 3D view or UV */
546             (t->flag & T_CAMERA) == 0)  /* Not with camera selected in camera view */
547         {
548                 setSnappingCallback(t);
549
550                 /* Edit mode */
551                 if (t->tsnap.applySnap != NULL && // A snapping function actually exist
552                     (obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs
553                 {
554                         /* Exclude editmesh if using proportional edit */
555                         if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
556                                 t->tsnap.modeSelect = SNAP_NOT_ACTIVE;
557                         }
558                         else {
559                                 t->tsnap.modeSelect = t->tsnap.snap_self ? SNAP_ALL : SNAP_NOT_ACTIVE;
560                         }
561                 }
562                 /* Particles edit mode*/
563                 else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
564                          (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT))
565                 {
566                         t->tsnap.modeSelect = SNAP_ALL;
567                 }
568                 /* Object mode */
569                 else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
570                          (obedit == NULL) ) // Object Mode
571                 {
572                         /* In "Edit Strokes" mode, Snap tool can perform snap to selected or active objects (see T49632)
573                          * TODO: perform self snap in gpencil_strokes */
574                         t->tsnap.modeSelect = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_ALL : SNAP_NOT_SELECTED;
575                 }
576                 else {
577                         /* Grid if snap is not possible */
578                         t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
579                 }
580         }
581         else if (t->spacetype == SPACE_NODE) {
582                 setSnappingCallback(t);
583                 
584                 if (t->tsnap.applySnap != NULL) {
585                         t->tsnap.modeSelect = SNAP_NOT_SELECTED;
586                 }
587                 else {
588                         /* Grid if snap is not possible */
589                         t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
590                 }
591         }
592         else if (t->spacetype == SPACE_SEQ) {
593                 /* We do our own snapping currently, so nothing here */
594                 t->tsnap.mode = SCE_SNAP_MODE_GRID;  /* Dummy, should we rather add a NOP mode? */
595         }
596         else {
597                 /* Always grid outside of 3D view */
598                 t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
599         }
600
601         if (t->spacetype == SPACE_VIEW3D) {
602                 if (t->tsnap.object_context == NULL) {
603                         t->tsnap.object_context = ED_transform_snap_object_context_create_view3d(
604                                 G.main, t->scene, 0,
605                                 t->ar, t->view);
606
607                         ED_transform_snap_object_context_set_editmesh_callbacks(
608                                 t->tsnap.object_context,
609                                 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
610                                 bm_edge_is_snap_target,
611                                 bm_face_is_snap_target,
612                                 SET_UINT_IN_POINTER((BM_ELEM_SELECT | BM_ELEM_HIDDEN)));
613                 }
614         }
615 }
616
617 void initSnapping(TransInfo *t, wmOperator *op)
618 {
619         ToolSettings *ts = t->settings;
620         short snap_target = t->settings->snap_target;
621         
622         resetSnapping(t);
623         
624         /* if snap property exists */
625         if (op && RNA_struct_find_property(op->ptr, "snap") && RNA_struct_property_is_set(op->ptr, "snap")) {
626                 if (RNA_boolean_get(op->ptr, "snap")) {
627                         t->modifiers |= MOD_SNAP;
628
629                         if (RNA_struct_property_is_set(op->ptr, "snap_target")) {
630                                 snap_target = RNA_enum_get(op->ptr, "snap_target");
631                         }
632                         
633                         if (RNA_struct_property_is_set(op->ptr, "snap_point")) {
634                                 RNA_float_get_array(op->ptr, "snap_point", t->tsnap.snapPoint);
635                                 t->tsnap.status |= SNAP_FORCED | POINT_INIT;
636                         }
637                         
638                         /* snap align only defined in specific cases */
639                         if (RNA_struct_find_property(op->ptr, "snap_align")) {
640                                 t->tsnap.align = RNA_boolean_get(op->ptr, "snap_align");
641                                 RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal);
642                                 normalize_v3(t->tsnap.snapNormal);
643                         }
644
645                         if (RNA_struct_find_property(op->ptr, "use_snap_project")) {
646                                 t->tsnap.project = RNA_boolean_get(op->ptr, "use_snap_project");
647                         }
648
649                         if (RNA_struct_find_property(op->ptr, "use_snap_self")) {
650                                 t->tsnap.snap_self = RNA_boolean_get(op->ptr, "use_snap_self");
651                         }
652                 }
653         }
654         /* use scene defaults only when transform is modal */
655         else if (t->flag & T_MODAL) {
656                 if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) {
657                         if (ts->snap_flag & SCE_SNAP) {
658                                 t->modifiers |= MOD_SNAP;
659                         }
660
661                         t->tsnap.align = ((t->settings->snap_flag & SCE_SNAP_ROTATE) != 0);
662                         t->tsnap.project = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0);
663                         t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0);
664                         t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0);
665                 }
666
667                 /* for now only 3d view (others can be added if we want) */
668                 if (t->spacetype == SPACE_VIEW3D) {
669                         t->tsnap.snap_spatial_grid = ((t->settings->snap_flag & SCE_SNAP_ABS_GRID) != 0);
670                 }
671         }
672         
673         t->tsnap.target = snap_target;
674
675         initSnappingMode(t);
676 }
677
678 void freeSnapping(TransInfo *t)
679 {
680         if (t->tsnap.object_context) {
681                 ED_transform_snap_object_context_destroy(t->tsnap.object_context);
682                 t->tsnap.object_context = NULL;
683         }
684 }
685
686 static void setSnappingCallback(TransInfo *t)
687 {
688         t->tsnap.calcSnap = CalcSnapGeometry;
689
690         switch (t->tsnap.target) {
691                 case SCE_SNAP_TARGET_CLOSEST:
692                         t->tsnap.targetSnap = TargetSnapClosest;
693                         break;
694                 case SCE_SNAP_TARGET_CENTER:
695                         t->tsnap.targetSnap = TargetSnapCenter;
696                         break;
697                 case SCE_SNAP_TARGET_MEDIAN:
698                         t->tsnap.targetSnap = TargetSnapMedian;
699                         break;
700                 case SCE_SNAP_TARGET_ACTIVE:
701                         t->tsnap.targetSnap = TargetSnapActive;
702                         break;
703
704         }
705
706         switch (t->mode) {
707                 case TFM_TRANSLATION:
708                         t->tsnap.applySnap = ApplySnapTranslation;
709                         t->tsnap.distance = TranslationBetween;
710                         break;
711                 case TFM_ROTATION:
712                         t->tsnap.applySnap = ApplySnapRotation;
713                         t->tsnap.distance = RotationBetween;
714
715                         // Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead
716                         if (t->tsnap.target == SCE_SNAP_TARGET_CENTER) {
717                                 t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
718                                 t->tsnap.targetSnap = TargetSnapMedian;
719                         }
720                         break;
721                 case TFM_RESIZE:
722                         t->tsnap.applySnap = ApplySnapResize;
723                         t->tsnap.distance = ResizeBetween;
724
725                         // Can't do TARGET_CENTER with resize, use TARGET_MEDIAN instead
726                         if (t->tsnap.target == SCE_SNAP_TARGET_CENTER) {
727                                 t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
728                                 t->tsnap.targetSnap = TargetSnapMedian;
729                         }
730                         break;
731                 default:
732                         t->tsnap.applySnap = NULL;
733                         break;
734         }
735 }
736
737 void addSnapPoint(TransInfo *t)
738 {
739         /* Currently only 3D viewport works for snapping points. */
740         if (t->tsnap.status & POINT_INIT && t->spacetype == SPACE_VIEW3D) {
741                 TransSnapPoint *p = MEM_callocN(sizeof(TransSnapPoint), "SnapPoint");
742
743                 t->tsnap.selectedPoint = p;
744
745                 copy_v3_v3(p->co, t->tsnap.snapPoint);
746
747                 BLI_addtail(&t->tsnap.points, p);
748
749                 t->tsnap.status |= MULTI_POINTS;
750         }
751 }
752
753 eRedrawFlag updateSelectedSnapPoint(TransInfo *t)
754 {
755         eRedrawFlag status = TREDRAW_NOTHING;
756
757         if (t->tsnap.status & MULTI_POINTS) {
758                 TransSnapPoint *p, *closest_p = NULL;
759                 float dist_min_sq = TRANSFORM_SNAP_MAX_PX;
760                 const float mval_fl[2] = {t->mval[0], t->mval[1]};
761                 float screen_loc[2];
762
763                 for (p = t->tsnap.points.first; p; p = p->next) {
764                         float dist_sq;
765
766                         if (ED_view3d_project_float_global(t->ar, p->co, screen_loc, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) {
767                                 continue;
768                         }
769
770                         dist_sq = len_squared_v2v2(mval_fl, screen_loc);
771
772                         if (dist_sq < dist_min_sq) {
773                                 closest_p = p;
774                                 dist_min_sq = dist_sq;
775                         }
776                 }
777
778                 if (closest_p) {
779                         if (t->tsnap.selectedPoint != closest_p) {
780                                 status = TREDRAW_HARD;
781                         }
782
783                         t->tsnap.selectedPoint = closest_p;
784                 }
785         }
786
787         return status;
788 }
789
790 void removeSnapPoint(TransInfo *t)
791 {
792         if (t->tsnap.status & MULTI_POINTS) {
793                 updateSelectedSnapPoint(t);
794
795                 if (t->tsnap.selectedPoint) {
796                         BLI_freelinkN(&t->tsnap.points, t->tsnap.selectedPoint);
797
798                         if (BLI_listbase_is_empty(&t->tsnap.points)) {
799                                 t->tsnap.status &= ~MULTI_POINTS;
800                         }
801
802                         t->tsnap.selectedPoint = NULL;
803                 }
804
805         }
806 }
807
808 void getSnapPoint(TransInfo *t, float vec[3])
809 {
810         if (t->tsnap.points.first) {
811                 TransSnapPoint *p;
812                 int total = 0;
813
814                 vec[0] = vec[1] = vec[2] = 0;
815
816                 for (p = t->tsnap.points.first; p; p = p->next, total++) {
817                         add_v3_v3(vec, p->co);
818                 }
819
820                 if (t->tsnap.status & POINT_INIT) {
821                         add_v3_v3(vec, t->tsnap.snapPoint);
822                         total++;
823                 }
824
825                 mul_v3_fl(vec, 1.0f / total);
826         }
827         else {
828                 copy_v3_v3(vec, t->tsnap.snapPoint);
829         }
830 }
831
832 /********************** APPLY **************************/
833
834 static void ApplySnapTranslation(TransInfo *t, float vec[3])
835 {
836         float point[3];
837         getSnapPoint(t, point);
838
839         if (t->spacetype == SPACE_NODE) {
840                 char border = t->tsnap.snapNodeBorder;
841                 if (border & (NODE_LEFT | NODE_RIGHT))
842                         vec[0] = point[0] - t->tsnap.snapTarget[0];
843                 if (border & (NODE_BOTTOM | NODE_TOP))
844                         vec[1] = point[1] - t->tsnap.snapTarget[1];
845         }
846         else {
847                 if (t->spacetype == SPACE_VIEW3D) {
848                         if (t->options & CTX_PAINT_CURVE) {
849                                 if (ED_view3d_project_float_global(t->ar, point, point, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) {
850                                         zero_v3(point);  /* no good answer here... */
851                                 }
852                         }
853                 }
854
855                 sub_v3_v3v3(vec, point, t->tsnap.snapTarget);
856         }
857 }
858
859 static void ApplySnapRotation(TransInfo *t, float *value)
860 {
861         float point[3];
862         getSnapPoint(t, point);
863
864         float dist = RotationBetween(t, t->tsnap.snapTarget, point);
865         *value = dist;
866 }
867
868 static void ApplySnapResize(TransInfo *t, float vec[3])
869 {
870         float point[3];
871         getSnapPoint(t, point);
872
873         float dist = ResizeBetween(t, t->tsnap.snapTarget, point);
874         copy_v3_fl(vec, dist);
875 }
876
877 /********************** DISTANCE **************************/
878
879 static float TranslationBetween(TransInfo *UNUSED(t), const float p1[3], const float p2[3])
880 {
881         return len_squared_v3v3(p1, p2);
882 }
883
884 static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
885 {
886         float angle, start[3], end[3];
887
888         sub_v3_v3v3(start, p1, t->center_global);
889         sub_v3_v3v3(end,   p2, t->center_global);
890                 
891         // Angle around a constraint axis (error prone, will need debug)
892         if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
893                 float axis[3], tmp[3];
894                 
895                 t->con.applyRot(t, NULL, axis, NULL);
896
897                 project_v3_v3v3(tmp, end, axis);
898                 sub_v3_v3v3(end, end, tmp);
899                 
900                 project_v3_v3v3(tmp, start, axis);
901                 sub_v3_v3v3(start, start, tmp);
902                 
903                 normalize_v3(end);
904                 normalize_v3(start);
905                 
906                 cross_v3_v3v3(tmp, start, end);
907                 
908                 if (dot_v3v3(tmp, axis) < 0.0f)
909                         angle = -acosf(dot_v3v3(start, end));
910                 else
911                         angle = acosf(dot_v3v3(start, end));
912         }
913         else {
914                 float mtx[3][3];
915                 
916                 copy_m3_m4(mtx, t->viewmat);
917
918                 mul_m3_v3(mtx, end);
919                 mul_m3_v3(mtx, start);
920                 
921                 angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]);
922         }
923         
924         if (angle > (float)M_PI) {
925                 angle = angle - 2 * (float)M_PI;
926         }
927         else if (angle < -((float)M_PI)) {
928                 angle = 2.0f * (float)M_PI + angle;
929         }
930         
931         return angle;
932 }
933
934 static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
935 {
936         float d1[3], d2[3], len_d1;
937
938         sub_v3_v3v3(d1, p1, t->center_global);
939         sub_v3_v3v3(d2, p2, t->center_global);
940
941         if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
942                 mul_m3_v3(t->con.pmtx, d1);
943                 mul_m3_v3(t->con.pmtx, d2);
944         }
945
946         project_v3_v3v3(d1, d1, d2);
947         
948         len_d1 = len_v3(d1);
949
950         /* Use 'invalid' dist when `center == p1` (after projecting),
951          * in this case scale will _never_ move the point in relation to the center,
952          * so it makes no sense to take it into account when scaling. see: T46503 */
953         return len_d1 != 0.0f ? len_v3(d2) / len_d1 : TRANSFORM_DIST_INVALID;
954 }
955
956 /********************** CALC **************************/
957
958 static void UNUSED_FUNCTION(CalcSnapGrid) (TransInfo *t, float *UNUSED(vec))
959 {
960         snapGridIncrementAction(t, t->tsnap.snapPoint, BIG_GEARS);
961 }
962
963 static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
964 {
965         if (t->spacetype == SPACE_VIEW3D) {
966                 float loc[3];
967                 float no[3];
968                 float mval[2];
969                 bool found = false;
970                 float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
971                 
972                 mval[0] = t->mval[0];
973                 mval[1] = t->mval[1];
974                 
975                 if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) {
976                         found = peelObjectsTransform(
977                                 t, mval,
978                                 (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0,
979                                 loc, no, NULL);
980                 }
981                 else {
982                         zero_v3(no);  /* objects won't set this */
983                         found = snapObjectsTransform(
984                                 t, mval, &dist_px,
985                                 loc, no);
986                 }
987                 
988                 if (found == true) {
989                         copy_v3_v3(t->tsnap.snapPoint, loc);
990                         copy_v3_v3(t->tsnap.snapNormal, no);
991
992                         t->tsnap.status |=  POINT_INIT;
993                 }
994                 else {
995                         t->tsnap.status &= ~POINT_INIT;
996                 }
997         }
998         else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type == OB_MESH) {
999                 /* same as above but for UV's */
1000                 Image *ima = ED_space_image(t->sa->spacedata.first);
1001                 float co[2];
1002                 
1003                 UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]);
1004
1005                 if (ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint)) {
1006                         t->tsnap.snapPoint[0] *= t->aspect[0];
1007                         t->tsnap.snapPoint[1] *= t->aspect[1];
1008
1009                         t->tsnap.status |=  POINT_INIT;
1010                 }
1011                 else {
1012                         t->tsnap.status &= ~POINT_INIT;
1013                 }
1014         }
1015         else if (t->spacetype == SPACE_NODE) {
1016                 float loc[2];
1017                 float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
1018                 char node_border;
1019                 
1020                 if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) {
1021                         copy_v2_v2(t->tsnap.snapPoint, loc);
1022                         t->tsnap.snapNodeBorder = node_border;
1023                         
1024                         t->tsnap.status |=  POINT_INIT;
1025                 }
1026                 else {
1027                         t->tsnap.status &= ~POINT_INIT;
1028                 }
1029         }
1030 }
1031
1032 /********************** TARGET **************************/
1033
1034 static void TargetSnapOffset(TransInfo *t, TransData *td)
1035 {
1036         if (t->spacetype == SPACE_NODE && td != NULL) {
1037                 bNode *node = td->extra;
1038                 char border = t->tsnap.snapNodeBorder;
1039                 float width  = BLI_rctf_size_x(&node->totr);
1040                 float height = BLI_rctf_size_y(&node->totr);
1041                 
1042 #ifdef USE_NODE_CENTER
1043                 if (border & NODE_LEFT)
1044                         t->tsnap.snapTarget[0] -= 0.5f * width;
1045                 if (border & NODE_RIGHT)
1046                         t->tsnap.snapTarget[0] += 0.5f * width;
1047                 if (border & NODE_BOTTOM)
1048                         t->tsnap.snapTarget[1] -= 0.5f * height;
1049                 if (border & NODE_TOP)
1050                         t->tsnap.snapTarget[1] += 0.5f * height;
1051 #else
1052                 if (border & NODE_LEFT)
1053                         t->tsnap.snapTarget[0] -= 0.0f;
1054                 if (border & NODE_RIGHT)
1055                         t->tsnap.snapTarget[0] += width;
1056                 if (border & NODE_BOTTOM)
1057                         t->tsnap.snapTarget[1] -= height;
1058                 if (border & NODE_TOP)
1059                         t->tsnap.snapTarget[1] += 0.0f;
1060 #endif
1061         }
1062 }
1063
1064 static void TargetSnapCenter(TransInfo *t)
1065 {
1066         /* Only need to calculate once */
1067         if ((t->tsnap.status & TARGET_INIT) == 0) {
1068                 copy_v3_v3(t->tsnap.snapTarget, t->center_global);
1069                 TargetSnapOffset(t, NULL);
1070                 
1071                 t->tsnap.status |= TARGET_INIT;
1072         }
1073 }
1074
1075 static void TargetSnapActive(TransInfo *t)
1076 {
1077         /* Only need to calculate once */
1078         if ((t->tsnap.status & TARGET_INIT) == 0) {
1079                 if (calculateCenterActive(t, true, t->tsnap.snapTarget)) {
1080                         if (t->flag & (T_EDIT | T_POSE)) {
1081                                 Object *ob = t->obedit ? t->obedit : t->poseobj;
1082                                 mul_m4_v3(ob->obmat, t->tsnap.snapTarget);
1083                         }
1084
1085                         TargetSnapOffset(t, NULL);
1086
1087                         t->tsnap.status |= TARGET_INIT;
1088                 }
1089                 /* No active, default to median */
1090                 else {
1091                         t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
1092                         t->tsnap.targetSnap = TargetSnapMedian;
1093                         TargetSnapMedian(t);
1094                 }
1095         }
1096 }
1097
1098 static void TargetSnapMedian(TransInfo *t)
1099 {
1100         // Only need to calculate once
1101         if ((t->tsnap.status & TARGET_INIT) == 0) {
1102                 TransData *td = NULL;
1103                 int i;
1104
1105                 t->tsnap.snapTarget[0] = 0;
1106                 t->tsnap.snapTarget[1] = 0;
1107                 t->tsnap.snapTarget[2] = 0;
1108                 
1109                 for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
1110                         add_v3_v3(t->tsnap.snapTarget, td->center);
1111                 }
1112                 
1113                 mul_v3_fl(t->tsnap.snapTarget, 1.0 / i);
1114                 
1115                 if (t->flag & (T_EDIT | T_POSE)) {
1116                         Object *ob = t->obedit ? t->obedit : t->poseobj;
1117                         mul_m4_v3(ob->obmat, t->tsnap.snapTarget);
1118                 }
1119                 
1120                 TargetSnapOffset(t, NULL);
1121                 
1122                 t->tsnap.status |= TARGET_INIT;
1123         }
1124 }
1125
1126 static void TargetSnapClosest(TransInfo *t)
1127 {
1128         // Only valid if a snap point has been selected
1129         if (t->tsnap.status & POINT_INIT) {
1130                 float dist_closest = 0.0f;
1131                 TransData *closest = NULL, *td = NULL;
1132                 
1133                 /* Object mode */
1134                 if (t->flag & T_OBJECT) {
1135                         int i;
1136                         for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
1137                                 struct BoundBox *bb = BKE_object_boundbox_get(td->ob);
1138                                 
1139                                 /* use boundbox if possible */
1140                                 if (bb) {
1141                                         int j;
1142                                         
1143                                         for (j = 0; j < 8; j++) {
1144                                                 float loc[3];
1145                                                 float dist;
1146                                                 
1147                                                 copy_v3_v3(loc, bb->vec[j]);
1148                                                 mul_m4_v3(td->ext->obmat, loc);
1149                                                 
1150                                                 dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
1151
1152                                                 if ((dist != TRANSFORM_DIST_INVALID) &&
1153                                                     (closest == NULL || fabsf(dist) < fabsf(dist_closest)))
1154                                                 {
1155                                                         copy_v3_v3(t->tsnap.snapTarget, loc);
1156                                                         closest = td;
1157                                                         dist_closest = dist;
1158                                                 }
1159                                         }
1160                                 }
1161                                 /* use element center otherwise */
1162                                 else {
1163                                         float loc[3];
1164                                         float dist;
1165                                         
1166                                         copy_v3_v3(loc, td->center);
1167                                         
1168                                         dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
1169
1170                                         if ((dist != TRANSFORM_DIST_INVALID) &&
1171                                             (closest == NULL || fabsf(dist) < fabsf(dist_closest)))
1172                                         {
1173                                                 copy_v3_v3(t->tsnap.snapTarget, loc);
1174                                                 closest = td;
1175                                         }
1176                                 }
1177                         }
1178                 }
1179                 else {
1180                         int i;
1181                         for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
1182                                 float loc[3];
1183                                 float dist;
1184                                 
1185                                 copy_v3_v3(loc, td->center);
1186                                 
1187                                 if (t->flag & (T_EDIT | T_POSE)) {
1188                                         Object *ob = t->obedit ? t->obedit : t->poseobj;
1189                                         mul_m4_v3(ob->obmat, loc);
1190                                 }
1191                                 
1192                                 dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
1193                                 
1194                                 if ((dist != TRANSFORM_DIST_INVALID) &&
1195                                     (closest == NULL || fabsf(dist) < fabsf(dist_closest)))
1196                                 {
1197                                         copy_v3_v3(t->tsnap.snapTarget, loc);
1198                                         closest = td;
1199                                         dist_closest = dist;
1200                                 }
1201                         }
1202                 }
1203                 
1204                 TargetSnapOffset(t, closest);
1205                 
1206                 t->tsnap.status |= TARGET_INIT;
1207         }
1208 }
1209
1210 bool snapObjectsTransform(
1211         TransInfo *t, const float mval[2],
1212         float *dist_px,
1213         float r_loc[3], float r_no[3])
1214 {
1215         return ED_transform_snap_object_project_view3d_ex(
1216                 t->tsnap.object_context,
1217                 t->scene->toolsettings->snap_mode,
1218                 &(const struct SnapObjectParams){
1219                     .snap_select = t->tsnap.modeSelect,
1220                     .use_object_edit_cage = (t->flag & T_EDIT) != 0,
1221                 },
1222                 mval, dist_px, NULL,
1223                 r_loc, r_no, NULL);
1224 }
1225
1226
1227 /******************** PEELING *********************************/
1228
1229 bool peelObjectsSnapContext(
1230         SnapObjectContext *sctx,
1231         const float mval[2],
1232         const struct SnapObjectParams *params,
1233         const bool use_peel_object,
1234         /* return args */
1235         float r_loc[3], float r_no[3], float *r_thickness)
1236 {
1237         ListBase depths_peel = {0};
1238         ED_transform_snap_object_project_all_view3d_ex(
1239                 sctx,
1240                 params,
1241                 mval, -1.0f, false,
1242                 &depths_peel);
1243
1244         if (!BLI_listbase_is_empty(&depths_peel)) {
1245                 /* At the moment we only use the hits of the first object */
1246                 struct SnapObjectHitDepth *hit_min = depths_peel.first;
1247                 for (struct SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) {
1248                         if (iter->depth < hit_min->depth) {
1249                                 hit_min = iter;
1250                         }
1251                 }
1252                 struct SnapObjectHitDepth *hit_max = NULL;
1253
1254                 if (use_peel_object) {
1255                         /* if peeling objects, take the first and last from each object */
1256                         hit_max = hit_min;
1257                         for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) {
1258                                 if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) {
1259                                         hit_max = iter;
1260                                 }
1261                         }
1262                 }
1263                 else {
1264                         /* otherwise, pair first with second and so on */
1265                         for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) {
1266                                 if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) {
1267                                         if (hit_max == NULL) {
1268                                                 hit_max = iter;
1269                                         }
1270                                         else if (iter->depth < hit_max->depth) {
1271                                                 hit_max = iter;
1272                                         }
1273                                 }
1274                         }
1275                         /* in this case has only one hit. treat as raycast */
1276                         if (hit_max == NULL) {
1277                                 hit_max = hit_min;
1278                         }
1279                 }
1280
1281                 mid_v3_v3v3(r_loc, hit_min->co, hit_max->co);
1282
1283                 if (r_thickness) {
1284                         *r_thickness = hit_max->depth - hit_min->depth;
1285                 }
1286
1287                 /* XXX, is there a correct normal in this case ???, for now just z up */
1288                 r_no[0] = 0.0;
1289                 r_no[1] = 0.0;
1290                 r_no[2] = 1.0;
1291
1292                 BLI_freelistN(&depths_peel);
1293                 return true;
1294         }
1295         return false;
1296 }
1297
1298 bool peelObjectsTransform(
1299         TransInfo *t,
1300         const float mval[2],
1301         const bool use_peel_object,
1302         /* return args */
1303         float r_loc[3], float r_no[3], float *r_thickness)
1304 {
1305         return peelObjectsSnapContext(
1306                 t->tsnap.object_context,
1307                 mval,
1308                 &(const struct SnapObjectParams){
1309                     .snap_select = t->tsnap.modeSelect,
1310                     .use_object_edit_cage = (t->flag & T_EDIT) != 0,
1311                 },
1312                 use_peel_object,
1313                 r_loc, r_no, r_thickness);
1314 }
1315
1316 /******************** NODES ***********************************/
1317
1318 static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select)
1319 {
1320         /* node is use for snapping only if a) snap mode matches and b) node is inside the view */
1321         return ((snap_select == SNAP_NOT_SELECTED && !(node->flag & NODE_SELECT)) ||
1322                 (snap_select == SNAP_ALL          && !(node->flag & NODE_ACTIVE))) &&
1323                 (node->totr.xmin < v2d->cur.xmax && node->totr.xmax > v2d->cur.xmin &&
1324                  node->totr.ymin < v2d->cur.ymax && node->totr.ymax > v2d->cur.ymin);
1325 }
1326
1327 static NodeBorder snapNodeBorder(int snap_node_mode)
1328 {
1329         switch (snap_node_mode) {
1330                 case SCE_SNAP_MODE_NODE_X:
1331                         return NODE_LEFT | NODE_RIGHT;
1332                 case SCE_SNAP_MODE_NODE_Y:
1333                         return NODE_TOP | NODE_BOTTOM;
1334                 case SCE_SNAP_MODE_NODE_XY:
1335                         return NODE_LEFT | NODE_RIGHT | NODE_TOP | NODE_BOTTOM;
1336         }
1337         return 0;
1338 }
1339
1340 static bool snapNode(
1341         ToolSettings *ts, SpaceNode *UNUSED(snode), ARegion *ar, bNode *node, const int mval[2],
1342         float r_loc[2], float *r_dist_px, char *r_node_border)
1343 {
1344         View2D *v2d = &ar->v2d;
1345         NodeBorder border = snapNodeBorder(ts->snap_node_mode);
1346         bool retval = false;
1347         rcti totr;
1348         int new_dist;
1349         
1350         UI_view2d_view_to_region_rcti(v2d, &node->totr, &totr);
1351         
1352         if (border & NODE_LEFT) {
1353                 new_dist = abs(totr.xmin - mval[0]);
1354                 if (new_dist < *r_dist_px) {
1355                         UI_view2d_region_to_view(v2d, totr.xmin, mval[1], &r_loc[0], &r_loc[1]);
1356                         *r_dist_px = new_dist;
1357                         *r_node_border = NODE_LEFT;
1358                         retval = true;
1359                 }
1360         }
1361         
1362         if (border & NODE_RIGHT) {
1363                 new_dist = abs(totr.xmax - mval[0]);
1364                 if (new_dist < *r_dist_px) {
1365                         UI_view2d_region_to_view(v2d, totr.xmax, mval[1], &r_loc[0], &r_loc[1]);
1366                         *r_dist_px = new_dist;
1367                         *r_node_border = NODE_RIGHT;
1368                         retval = true;
1369                 }
1370         }
1371         
1372         if (border & NODE_BOTTOM) {
1373                 new_dist = abs(totr.ymin - mval[1]);
1374                 if (new_dist < *r_dist_px) {
1375                         UI_view2d_region_to_view(v2d, mval[0], totr.ymin, &r_loc[0], &r_loc[1]);
1376                         *r_dist_px = new_dist;
1377                         *r_node_border = NODE_BOTTOM;
1378                         retval = true;
1379                 }
1380         }
1381         
1382         if (border & NODE_TOP) {
1383                 new_dist = abs(totr.ymax - mval[1]);
1384                 if (new_dist < *r_dist_px) {
1385                         UI_view2d_region_to_view(v2d, mval[0], totr.ymax, &r_loc[0], &r_loc[1]);
1386                         *r_dist_px = new_dist;
1387                         *r_node_border = NODE_TOP;
1388                         retval = true;
1389                 }
1390         }
1391         
1392         return retval;
1393 }
1394
1395 static bool snapNodes(
1396         ToolSettings *ts, SpaceNode *snode, ARegion *ar,
1397         const int mval[2], eSnapSelect snap_select,
1398         float r_loc[2], float *r_dist_px, char *r_node_border)
1399 {
1400         bNodeTree *ntree = snode->edittree;
1401         bNode *node;
1402         bool retval = false;
1403         
1404         *r_node_border = 0;
1405         
1406         for (node = ntree->nodes.first; node; node = node->next) {
1407                 if (snapNodeTest(&ar->v2d, node, snap_select)) {
1408                         retval |= snapNode(ts, snode, ar, node, mval, r_loc, r_dist_px, r_node_border);
1409                 }
1410         }
1411         
1412         return retval;
1413 }
1414
1415 bool snapNodesTransform(
1416         TransInfo *t, const int mval[2],
1417         float r_loc[2], float *r_dist_px, char *r_node_border)
1418 {
1419         return snapNodes(
1420                 t->settings, t->sa->spacedata.first, t->ar, mval, t->tsnap.modeSelect,
1421                 r_loc, r_dist_px, r_node_border);
1422 }
1423
1424 /*================================================================*/
1425
1426 static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action);
1427
1428
1429 void snapGridIncrementAction(TransInfo *t, float *val, GearsType action)
1430 {
1431         float fac[3];
1432
1433         fac[NO_GEARS]    = t->snap[0];
1434         fac[BIG_GEARS]   = t->snap[1];
1435         fac[SMALL_GEARS] = t->snap[2];
1436         
1437         applyGridIncrement(t, val, t->idx_max, fac, action);
1438 }
1439
1440
1441 void snapGridIncrement(TransInfo *t, float *val)
1442 {
1443         GearsType action;
1444
1445         /* only do something if using absolute or incremental grid snapping */
1446         if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID))
1447                 return;
1448
1449         action = activeSnap(t) ? BIG_GEARS : NO_GEARS;
1450
1451         if (action == BIG_GEARS && (t->modifiers & MOD_PRECISION)) {
1452                 action = SMALL_GEARS;
1453         }
1454
1455         snapGridIncrementAction(t, val, action);
1456 }
1457
1458 void snapSequenceBounds(TransInfo *t, const int mval[2])
1459 {
1460         float xmouse, ymouse;
1461         int frame;
1462         int mframe;
1463         TransSeq *ts = t->custom.type.data;
1464         /* reuse increment, strictly speaking could be another snap mode, but leave as is */
1465         if (!(t->modifiers & MOD_SNAP_INVERT))
1466                 return;
1467
1468         /* convert to frame range */
1469         UI_view2d_region_to_view(&t->ar->v2d, mval[0], mval[1], &xmouse, &ymouse);
1470         mframe = round_fl_to_int(xmouse);
1471         /* now find the closest sequence */
1472         frame = BKE_sequencer_find_next_prev_edit(t->scene, mframe, SEQ_SIDE_BOTH, true, false, true);
1473
1474         if (!ts->snap_left)
1475                 frame = frame - (ts->max - ts->min);
1476
1477         t->values[0] = frame - ts->min;
1478 }
1479
1480 static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action)
1481 {
1482         float asp_local[3] = {1, 1, 1};
1483         const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION);
1484         const float *asp = use_aspect ? t->aspect : asp_local;
1485         int i;
1486
1487         BLI_assert(ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID));
1488         BLI_assert(max_index <= 2);
1489
1490         /* Early bailing out if no need to snap */
1491         if (fac[action] == 0.0f) {
1492                 return;
1493         }
1494
1495         if (use_aspect) {
1496                 /* custom aspect for fcurve */
1497                 if (t->spacetype == SPACE_IPO) {
1498                         View2D *v2d = &t->ar->v2d;
1499                         View2DGrid *grid;
1500                         SpaceIpo *sipo = t->sa->spacedata.first;
1501                         int unity = V2D_UNIT_VALUES;
1502                         int unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE;
1503
1504                         /* grid */
1505                         grid = UI_view2d_grid_calc(t->scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, t->ar->winx, t->ar->winy);
1506
1507                         UI_view2d_grid_size(grid, &asp_local[0], &asp_local[1]);
1508                         UI_view2d_grid_free(grid);
1509
1510                         asp = asp_local;
1511                 }
1512         }
1513
1514         /* absolute snapping on grid based on global center */
1515         if ((t->tsnap.snap_spatial_grid) && (t->mode == TFM_TRANSLATION)) {
1516                 const float *center_global = t->center_global;
1517
1518                 /* use a fallback for cursor selection,
1519                  * this isn't useful as a global center for absolute grid snapping
1520                  * since its not based on the position of the selection. */
1521                 if (t->around == V3D_AROUND_CURSOR) {
1522                         const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEAN);
1523                         center_global = cd->global;
1524                 }
1525
1526                 for (i = 0; i <= max_index; i++) {
1527                         /* do not let unconstrained axis jump to absolute grid increments */
1528                         if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) {
1529                                 const float iter_fac = fac[action] * asp[i];
1530                                 val[i] = iter_fac * roundf((val[i] + center_global[i]) / iter_fac) - center_global[i];
1531                         }
1532                 }
1533         }
1534         else {
1535                 /* relative snapping in fixed increments */
1536                 for (i = 0; i <= max_index; i++) {
1537                         const float iter_fac = fac[action] * asp[i];
1538                         val[i] = iter_fac * roundf(val[i] / iter_fac);
1539                 }
1540         }
1541 }