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