Blender  V3.3
transform_convert_curve.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include "DNA_curve_types.h"
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "BLI_listbase.h"
13 #include "BLI_math.h"
14 
15 #include "BKE_context.h"
16 #include "BKE_curve.h"
17 
18 #include "transform.h"
19 #include "transform_snap.h"
20 
21 /* Own include. */
22 #include "transform_convert.h"
23 #include "transform_orientations.h"
24 
25 /* -------------------------------------------------------------------- */
33 static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const bool hide_handles)
34 {
35  int flag = 0;
36 
37  if (hide_handles) {
38  if (bezt->f2 & SELECT) {
39  flag = (1 << 0) | (1 << 1) | (1 << 2);
40  }
41  }
42  else {
43  flag = (((bezt->f1 & SELECT) ? (1 << 0) : 0) | ((bezt->f2 & SELECT) ? (1 << 1) : 0) |
44  ((bezt->f3 & SELECT) ? (1 << 2) : 0));
45  }
46 
47  /* Special case for auto & aligned handles:
48  * When a center point is being moved without the handles,
49  * leaving the handles stationary makes no sense and only causes strange behavior,
50  * where one handle is arbitrarily anchored, the other one is aligned and lengthened
51  * based on where the center point is moved. Also a bug when canceling, see: T52007.
52  *
53  * A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
54  * However that doesn't resolve odd behavior, so best transform the handles in this case.
55  */
56  if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) {
57  if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
58  flag = (1 << 0) | (1 << 1) | (1 << 2);
59  }
60  }
61 
62  return flag;
63 }
64 
66 {
67 
68 #define SEL_F1 (1 << 0)
69 #define SEL_F2 (1 << 1)
70 #define SEL_F3 (1 << 2)
71 
72  t->data_len_all = 0;
73 
74  /* Count control points (one per #BezTriple) if any number of handles are selected.
75  * Needed for #transform_around_single_fallback_ex. */
76  int data_len_all_pt = 0;
77 
79  Curve *cu = tc->obedit->data;
80  BLI_assert(cu->editnurb != NULL);
81  BezTriple *bezt;
82  BPoint *bp;
83  int a;
84  int count = 0, countsel = 0;
85  int count_pt = 0, countsel_pt = 0;
86  const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
87  const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
88  View3D *v3d = t->view;
89  short hide_handles = (v3d != NULL) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
90  false;
91 
92  /* count total of vertices, check identical as in 2nd loop for making transdata! */
93  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
94  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
95  if (nu->type == CU_BEZIER) {
96  for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
97  if (bezt->hide == 0) {
98  const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
99  if (bezt_tx & (SEL_F1 | SEL_F2 | SEL_F3)) {
100  if (bezt_tx & SEL_F1) {
101  countsel++;
102  }
103  if (bezt_tx & SEL_F2) {
104  countsel++;
105  }
106  if (bezt_tx & SEL_F3) {
107  countsel++;
108  }
109  countsel_pt++;
110  }
111  if (is_prop_edit) {
112  count += 3;
113  count_pt++;
114  }
115  }
116  }
117  }
118  else {
119  for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
120  if (bp->hide == 0) {
121  if (bp->f1 & SELECT) {
122  countsel++;
123  countsel_pt++;
124  }
125  if (is_prop_edit) {
126  count++;
127  count_pt++;
128  }
129  }
130  }
131  }
132  }
133 
134  /* Support other objects using PET to adjust these, unless connected is enabled. */
135  if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
136  tc->data_len = 0;
137  continue;
138  }
139 
140  int data_len_pt = 0;
141 
142  if (is_prop_edit) {
143  tc->data_len = count;
144  data_len_pt = count_pt;
145  }
146  else {
147  tc->data_len = countsel;
148  data_len_pt = countsel_pt;
149  }
150  tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)");
151 
152  t->data_len_all += tc->data_len;
153  data_len_all_pt += data_len_pt;
154  }
155 
156  transform_around_single_fallback_ex(t, data_len_all_pt);
157  t->data_len_all = -1;
158 
160  if (tc->data_len == 0) {
161  continue;
162  }
163 
164  Curve *cu = tc->obedit->data;
165  BezTriple *bezt;
166  BPoint *bp;
167  int a;
168  const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
169  View3D *v3d = t->view;
170  short hide_handles = (v3d != NULL) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
171  false;
172 
173  bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
175  float mtx[3][3], smtx[3][3];
176 
177  copy_m3_m4(mtx, tc->obedit->obmat);
179 
180  TransData *td = tc->data;
181  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
182  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
183  TransData *head, *tail;
184  head = tail = td;
185  bool has_any_selected = false;
186  if (nu->type == CU_BEZIER) {
187  for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
188  if (bezt->hide == 0) {
190  float axismtx[3][3];
191 
192  if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
193  float normal[3], plane[3];
194 
196  BKE_nurb_bezt_calc_plane(nu, bezt, plane);
197 
198  if (createSpaceNormalTangent(axismtx, normal, plane)) {
199  /* pass */
200  }
201  else {
203  axis_dominant_v3_to_m3(axismtx, normal);
204  invert_m3(axismtx);
205  }
206  }
207 
208  /* Elements that will be transform (not always a match to selection). */
209  const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
210  has_any_selected |= bezt_tx != 0;
211 
212  if (is_prop_edit || bezt_tx & SEL_F1) {
213  copy_v3_v3(td->iloc, bezt->vec[0]);
214  td->loc = bezt->vec[0];
215  copy_v3_v3(td->center,
216  bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
217  (bezt->f2 & SELECT)) ?
218  1 :
219  0]);
220  if (hide_handles) {
221  if (bezt->f2 & SELECT) {
222  td->flag = TD_SELECTED;
223  }
224  else {
225  td->flag = 0;
226  }
227  }
228  else {
229  if (bezt->f1 & SELECT) {
230  td->flag = TD_SELECTED;
231  }
232  else {
233  td->flag = 0;
234  }
235  }
236  td->ext = NULL;
237  td->val = NULL;
238 
239  hdata = initTransDataCurveHandles(td, bezt);
240 
241  copy_m3_m3(td->smtx, smtx);
242  copy_m3_m3(td->mtx, mtx);
243  if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
244  copy_m3_m3(td->axismtx, axismtx);
245  }
246 
247  td++;
248  tail++;
249  }
250 
251  /* This is the Curve Point, the other two are handles */
252  if (is_prop_edit || bezt_tx & SEL_F2) {
253  copy_v3_v3(td->iloc, bezt->vec[1]);
254  td->loc = bezt->vec[1];
255  copy_v3_v3(td->center, td->loc);
256  if (bezt->f2 & SELECT) {
257  td->flag = TD_SELECTED;
258  }
259  else {
260  td->flag = 0;
261  }
262  td->ext = NULL;
263 
264  /* TODO: make points scale. */
265  if (t->mode == TFM_CURVE_SHRINKFATTEN /* `|| t->mode == TFM_RESIZE` */) {
266  td->val = &(bezt->radius);
267  td->ival = bezt->radius;
268  }
269  else if (t->mode == TFM_TILT) {
270  td->val = &(bezt->tilt);
271  td->ival = bezt->tilt;
272  }
273  else {
274  td->val = NULL;
275  }
276 
277  copy_m3_m3(td->smtx, smtx);
278  copy_m3_m3(td->mtx, mtx);
279  if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
280  copy_m3_m3(td->axismtx, axismtx);
281  }
282 
283  if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) {
284  /* If the middle is selected but the sides aren't, this is needed. */
285  if (hdata == NULL) {
286  /* if the handle was not saved by the previous handle */
287  hdata = initTransDataCurveHandles(td, bezt);
288  }
289  }
290 
291  td++;
292  tail++;
293  }
294  if (is_prop_edit || bezt_tx & SEL_F3) {
295  copy_v3_v3(td->iloc, bezt->vec[2]);
296  td->loc = bezt->vec[2];
297  copy_v3_v3(td->center,
298  bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
299  (bezt->f2 & SELECT)) ?
300  1 :
301  2]);
302  if (hide_handles) {
303  if (bezt->f2 & SELECT) {
304  td->flag = TD_SELECTED;
305  }
306  else {
307  td->flag = 0;
308  }
309  }
310  else {
311  if (bezt->f3 & SELECT) {
312  td->flag = TD_SELECTED;
313  }
314  else {
315  td->flag = 0;
316  }
317  }
318  td->ext = NULL;
319  td->val = NULL;
320 
321  if (hdata == NULL) {
322  /* if the handle was not saved by the previous handle */
323  hdata = initTransDataCurveHandles(td, bezt);
324  }
325 
326  copy_m3_m3(td->smtx, smtx);
327  copy_m3_m3(td->mtx, mtx);
328  if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
329  copy_m3_m3(td->axismtx, axismtx);
330  }
331 
332  td++;
333  tail++;
334  }
335 
336  (void)hdata; /* quiet warning */
337  }
338  }
339  }
340  else {
341  for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
342  if (bp->hide == 0) {
343  if (is_prop_edit || (bp->f1 & SELECT)) {
344  copy_v3_v3(td->iloc, bp->vec);
345  td->loc = bp->vec;
346  copy_v3_v3(td->center, td->loc);
347  if (bp->f1 & SELECT) {
348  td->flag = TD_SELECTED;
349  has_any_selected |= true;
350  }
351  else {
352  td->flag = 0;
353  }
354  td->ext = NULL;
355 
356  if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_RESIZE)) {
357  td->val = &(bp->radius);
358  td->ival = bp->radius;
359  }
360  else {
361  td->val = &(bp->tilt);
362  td->ival = bp->tilt;
363  }
364 
365  copy_m3_m3(td->smtx, smtx);
366  copy_m3_m3(td->mtx, mtx);
367 
368  if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
369  if (nu->pntsv == 1) {
370  float normal[3], plane[3];
371 
373  BKE_nurb_bpoint_calc_plane(nu, bp, plane);
374 
375  if (createSpaceNormalTangent(td->axismtx, normal, plane)) {
376  /* pass */
377  }
378  else {
381  invert_m3(td->axismtx);
382  }
383  }
384  }
385 
386  td++;
387  tail++;
388  }
389  }
390  }
391  }
392  if (is_prop_edit && head != tail) {
393  tail -= 1;
394  if (!has_any_selected) {
395  for (td = head; td <= tail; td++) {
396  td->flag |= TD_NOTCONNECTED;
397  }
398  }
399  bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
400  calc_distanceCurveVerts(head, tail, cyclic);
401  }
402 
403  /* TODO: in the case of tilt and radius we can also avoid allocating the
404  * initTransDataCurveHandles but for now just don't change handle types */
405  if ((nu->type == CU_BEZIER) &&
407  /* sets the handles based on their selection,
408  * do this after the data is copied to the TransData */
409  BKE_nurb_handles_test(nu, !hide_handles, use_around_origins_for_handles_test);
410  }
411  }
412  }
413 #undef SEL_F1
414 #undef SEL_F2
415 #undef SEL_F3
416 }
417 
419 {
420  if (t->state != TRANS_CANCEL) {
422  }
423 
425  Curve *cu = tc->obedit->data;
426  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
427  Nurb *nu = nurbs->first;
428 
429  DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
430 
431  if (t->state == TRANS_CANCEL) {
432  while (nu) {
433  /* Can't do testhandlesNurb here, it messes up the h1 and h2 flags */
435  nu = nu->next;
436  }
437  }
438  else {
439  /* Apply clipping after so we never project past the clip plane T25423. */
441 
442  /* Normal updating. */
444  }
445  }
446 }
447 
451  /* flags */ (T_EDIT | T_POINTS),
452  /* createTransData */ createTransCurveVerts,
453  /* recalcData */ recalcData_curve,
454  /* special_aftertrans_update */ NULL,
455 };
void BKE_nurb_handles_calc(struct Nurb *nu)
Definition: curve.cc:3995
void BKE_nurb_bezt_calc_plane(struct Nurb *nu, struct BezTriple *bezt, float r_plane[3])
Definition: curve.cc:1074
struct ListBase * BKE_curve_editNurbs_get(struct Curve *cu)
Definition: curve.cc:426
void BKE_nurb_bpoint_calc_normal(struct Nurb *nu, struct BPoint *bp, float r_normal[3])
Definition: curve.cc:1111
void BKE_curve_dimension_update(struct Curve *cu)
Definition: curve.cc:465
void BKE_nurb_bezt_calc_normal(struct Nurb *nu, struct BezTriple *bezt, float r_normal[3])
void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, struct BPoint *bp, float r_plane[3])
Definition: curve.cc:1134
void BKE_nurb_handles_test(struct Nurb *nu, bool use_handles, bool use_around_local)
Definition: curve.cc:4105
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
Definition: math_geom.c:3527
void copy_m3_m3(float m1[3][3], const float m2[3][3])
Definition: math_matrix.c:71
void copy_m3_m4(float m1[3][3], const float m2[4][4])
Definition: math_matrix.c:87
#define PSEUDOINVERSE_EPSILON
bool invert_m3(float R[3][3])
Definition: math_matrix.c:1171
void pseudoinverse_m3_m3(float Ainv[3][3], const float A[3][3], float epsilon)
Definition: math_matrix.c:3107
MINLINE float normalize_v3(float r[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define UNUSED(x)
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ CU_BEZIER
@ CU_NURB_CYCLIC
@ HD_AUTO
@ HD_ALIGN
@ V3D_AROUND_LOCAL_ORIGINS
@ CURVE_HANDLE_NONE
@ TFM_RESIZE
Definition: ED_transform.h:32
@ TFM_CURVE_SHRINKFATTEN
Definition: ED_transform.h:46
@ TFM_TILT
Definition: ED_transform.h:38
@ TFM_DUMMY
Definition: ED_transform.h:29
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
#define SELECT
SyclQueue void void size_t num_bytes void
IconTextureDrawCall normal
int count
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
static unsigned a[3]
Definition: RandGen.cpp:78
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
short hide
uint8_t f1
float vec[4]
float radius
float tilt
uint8_t h1
uint8_t f3
float vec[3][3]
uint8_t f1
uint8_t f2
uint8_t h2
EditNurb * editnurb
void * first
Definition: DNA_listBase.h:31
struct Nurb * next
float smtx[3][3]
float axismtx[3][3]
float mtx[3][3]
TransDataExtension * ext
float * val
View3DOverlay overlay
void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
void transform_around_single_fallback_ex(TransInfo *t, int data_len_all)
bool transform_mode_use_local_origins(const TransInfo *t)
TransDataCurveHandleFlags * initTransDataCurveHandles(TransData *td, struct BezTriple *bezt)
conversion and adaptation of different datablocks to a common struct.
#define SEL_F1
#define SEL_F2
static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t)
static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const bool hide_handles)
TransConvertTypeInfo TransConvertType_Curve
#define SEL_F3
static void recalcData_curve(TransInfo *t)
@ TD_NOTCONNECTED
@ TD_SELECTED
bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
void applySnappingIndividual(TransInfo *t)