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