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