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