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