2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * Contributor(s): Martin Poirier
20 * ***** END GPL LICENSE BLOCK *****
23 /** \file blender/editors/transform/transform_orientations.c
24 * \ingroup edtransform
31 #include "MEM_guardedalloc.h"
33 #include "DNA_armature_types.h"
34 #include "DNA_curve_types.h"
35 #include "DNA_meta_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_scene_types.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_view3d_types.h"
43 #include "BLI_listbase.h"
44 #include "BLI_string.h"
45 #include "BLI_string_utils.h"
46 #include "BLI_utildefines.h"
48 #include "BKE_action.h"
49 #include "BKE_curve.h"
50 #include "BKE_context.h"
51 #include "BKE_editmesh.h"
52 #include "BKE_report.h"
54 #include "BKE_screen.h"
56 #include "BLT_translation.h"
58 #include "ED_armature.h"
60 #include "transform.h"
62 /* *********************** TransSpace ************************** */
64 void BIF_clearTransformOrientation(bContext *C)
66 View3D *v3d = CTX_wm_view3d(C);
68 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
69 BLI_freelistN(transform_spaces);
71 // Need to loop over all view3d
72 if (v3d && v3d->twmode >= V3D_MANIP_CUSTOM) {
73 v3d->twmode = V3D_MANIP_GLOBAL; /* fallback to global */
77 static TransformOrientation *findOrientationName(ListBase *lb, const char *name)
79 return BLI_findstring(lb, name, offsetof(TransformOrientation, name));
82 static bool uniqueOrientationNameCheck(void *arg, const char *name)
84 return findOrientationName((ListBase *)arg, name) != NULL;
87 static void uniqueOrientationName(ListBase *lb, char *name)
89 BLI_uniquename_cb(uniqueOrientationNameCheck, lb, CTX_DATA_(BLT_I18NCONTEXT_ID_SCENE, "Space"), '.', name,
90 sizeof(((TransformOrientation *)NULL)->name));
93 static TransformOrientation *createViewSpace(bContext *C, ReportList *UNUSED(reports),
94 const char *name, const bool overwrite)
96 RegionView3D *rv3d = CTX_wm_region_view3d(C);
102 copy_m3_m4(mat, rv3d->viewinv);
106 View3D *v3d = CTX_wm_view3d(C);
107 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
108 /* If an object is used as camera, then this space is the same as object space! */
109 name = v3d->camera->id.name + 2;
112 name = "Custom View";
116 return addMatrixSpace(C, mat, name, overwrite);
119 static TransformOrientation *createObjectSpace(bContext *C, ReportList *UNUSED(reports),
120 const char *name, const bool overwrite)
122 Base *base = CTX_data_active_base(C);
131 copy_m3_m4(mat, ob->obmat);
134 /* use object name if no name is given */
136 name = ob->id.name + 2;
139 return addMatrixSpace(C, mat, name, overwrite);
142 static TransformOrientation *createBoneSpace(bContext *C, ReportList *reports,
143 const char *name, const bool overwrite)
146 float normal[3], plane[3];
148 getTransformOrientation(C, normal, plane);
150 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
151 BKE_reports_prepend(reports, "Cannot use zero-length bone");
159 return addMatrixSpace(C, mat, name, overwrite);
162 static TransformOrientation *createCurveSpace(bContext *C, ReportList *reports,
163 const char *name, const bool overwrite)
166 float normal[3], plane[3];
168 getTransformOrientation(C, normal, plane);
170 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
171 BKE_reports_prepend(reports, "Cannot use zero-length curve");
179 return addMatrixSpace(C, mat, name, overwrite);
183 static TransformOrientation *createMeshSpace(bContext *C, ReportList *reports,
184 const char *name, const bool overwrite)
187 float normal[3], plane[3];
190 type = getTransformOrientation(C, normal, plane);
193 case ORIENTATION_VERT:
194 if (createSpaceNormal(mat, normal) == 0) {
195 BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal");
203 case ORIENTATION_EDGE:
204 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
205 BKE_reports_prepend(reports, "Cannot use zero-length edge");
213 case ORIENTATION_FACE:
214 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
215 BKE_reports_prepend(reports, "Cannot use zero-area face");
227 return addMatrixSpace(C, mat, name, overwrite);
230 bool createSpaceNormal(float mat[3][3], const float normal[3])
232 float tangent[3] = {0.0f, 0.0f, 1.0f};
234 copy_v3_v3(mat[2], normal);
235 if (normalize_v3(mat[2]) == 0.0f) {
236 return false; /* error return */
239 cross_v3_v3v3(mat[0], mat[2], tangent);
240 if (is_zero_v3(mat[0])) {
242 tangent[1] = tangent[2] = 0.0f;
243 cross_v3_v3v3(mat[0], tangent, mat[2]);
246 cross_v3_v3v3(mat[1], mat[2], mat[0]);
254 * \note To recreate an orientation from the matrix:
255 * - (plane == mat[1])
256 * - (normal == mat[2])
258 bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
260 if (normalize_v3_v3(mat[2], normal) == 0.0f) {
261 return false; /* error return */
264 /* negate so we can use values from the matrix as input */
265 negate_v3_v3(mat[1], tangent);
266 /* preempt zero length tangent from causing trouble */
267 if (is_zero_v3(mat[1])) {
271 cross_v3_v3v3(mat[0], mat[2], mat[1]);
272 if (normalize_v3(mat[0]) == 0.0f) {
273 return false; /* error return */
276 cross_v3_v3v3(mat[1], mat[2], mat[0]);
277 normalize_v3(mat[1]);
279 /* final matrix must be normalized, do inline */
280 // normalize_m3(mat);
285 void BIF_createTransformOrientation(bContext *C, ReportList *reports,
286 const char *name, const bool use_view,
287 const bool activate, const bool overwrite)
289 TransformOrientation *ts = NULL;
292 ts = createViewSpace(C, reports, name, overwrite);
295 Object *obedit = CTX_data_edit_object(C);
296 Object *ob = CTX_data_active_object(C);
298 if (obedit->type == OB_MESH)
299 ts = createMeshSpace(C, reports, name, overwrite);
300 else if (obedit->type == OB_ARMATURE)
301 ts = createBoneSpace(C, reports, name, overwrite);
302 else if (obedit->type == OB_CURVE)
303 ts = createCurveSpace(C, reports, name, overwrite);
305 else if (ob && (ob->mode & OB_MODE_POSE)) {
306 ts = createBoneSpace(C, reports, name, overwrite);
309 ts = createObjectSpace(C, reports, name, overwrite);
313 if (activate && ts != NULL) {
314 BIF_selectTransformOrientation(C, ts);
318 TransformOrientation *addMatrixSpace(bContext *C, float mat[3][3],
319 const char *name, const bool overwrite)
321 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
322 TransformOrientation *ts = NULL;
323 char name_unique[sizeof(ts->name)];
326 ts = findOrientationName(transform_spaces, name);
329 BLI_strncpy(name_unique, name, sizeof(name_unique));
330 uniqueOrientationName(transform_spaces, name_unique);
334 /* if not, create a new one */
336 ts = MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix");
337 BLI_addtail(transform_spaces, ts);
338 BLI_strncpy(ts->name, name, sizeof(ts->name));
341 /* copy matrix into transform space */
342 copy_m3_m3(ts->mat, mat);
347 void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target)
349 Scene *scene = CTX_data_scene(C);
350 ListBase *transform_spaces = &scene->transform_spaces;
351 const int i = BLI_findindex(transform_spaces, target);
354 Main *bmain = CTX_data_main(C);
355 BKE_screen_view3d_main_twmode_remove(&bmain->screen, scene, i);
356 BLI_freelinkN(transform_spaces, target);
360 void BIF_removeTransformOrientationIndex(bContext *C, int index)
362 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
363 TransformOrientation *ts = BLI_findlink(transform_spaces, index);
366 BIF_removeTransformOrientation(C, ts);
370 void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target)
372 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
373 const int i = BLI_findindex(transform_spaces, target);
376 View3D *v3d = CTX_wm_view3d(C);
377 v3d->twmode = V3D_MANIP_CUSTOM + i;
381 void BIF_selectTransformOrientationValue(bContext *C, int orientation)
383 View3D *v3d = CTX_wm_view3d(C);
384 if (v3d) /* currently using generic poll */
385 v3d->twmode = orientation;
388 int BIF_countTransformOrientation(const bContext *C)
390 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
391 return BLI_listbase_count(transform_spaces);
394 bool applyTransformOrientation(const bContext *C, float mat[3][3], char *r_name, int index)
396 ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces;
397 TransformOrientation *ts = BLI_findlink(transform_spaces, index);
399 BLI_assert(index >= 0);
403 BLI_strncpy(r_name, ts->name, MAX_NAME);
406 copy_m3_m3(mat, ts->mat);
410 /* invalid index, can happen sometimes */
415 static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it)
421 for (bone = lb->first; bone; bone = bone->next) {
422 bone->flag &= ~BONE_TRANSFORM;
425 if (bone->layer & arm->layer) {
426 if (bone->flag & BONE_SELECTED) {
427 bone->flag |= BONE_TRANSFORM;
430 /* no transform on children if one parent bone is selected */
435 total += count_bone_select(arm, &bone->childbase, do_next);
441 void initTransformOrientation(bContext *C, TransInfo *t)
443 Object *ob = CTX_data_active_object(C);
444 Object *obedit = CTX_data_active_object(C);
446 switch (t->current_orientation) {
447 case V3D_MANIP_GLOBAL:
448 unit_m3(t->spacemtx);
449 BLI_strncpy(t->spacename, IFACE_("global"), sizeof(t->spacename));
452 case V3D_MANIP_GIMBAL:
453 unit_m3(t->spacemtx);
454 if (ob && gimbal_axis(ob, t->spacemtx)) {
455 BLI_strncpy(t->spacename, IFACE_("gimbal"), sizeof(t->spacename));
458 ATTR_FALLTHROUGH; /* no gimbal fallthrough to normal */
459 case V3D_MANIP_NORMAL:
460 if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
461 BLI_strncpy(t->spacename, IFACE_("normal"), sizeof(t->spacename));
462 ED_getTransformOrientationMatrix(C, t->spacemtx, t->around);
465 ATTR_FALLTHROUGH; /* we define 'normal' as 'local' in Object mode */
466 case V3D_MANIP_LOCAL:
467 BLI_strncpy(t->spacename, IFACE_("local"), sizeof(t->spacename));
470 copy_m3_m4(t->spacemtx, ob->obmat);
471 normalize_m3(t->spacemtx);
474 unit_m3(t->spacemtx);
480 if ((t->spacetype == SPACE_VIEW3D) &&
481 (t->ar->regiontype == RGN_TYPE_WINDOW))
483 RegionView3D *rv3d = t->ar->regiondata;
486 BLI_strncpy(t->spacename, IFACE_("view"), sizeof(t->spacename));
487 copy_m3_m4(mat, rv3d->viewinv);
489 copy_m3_m3(t->spacemtx, mat);
492 unit_m3(t->spacemtx);
495 default: /* V3D_MANIP_CUSTOM */
496 if (applyTransformOrientation(C, t->spacemtx, t->spacename, t->current_orientation - V3D_MANIP_CUSTOM)) {
500 unit_m3(t->spacemtx);
507 * utility function - get first n, selected vert/edge/faces
509 static unsigned int bm_mesh_elems_select_get_n__internal(
510 BMesh *bm, BMElem **elems, const unsigned int n,
511 const BMIterType itype, const char htype)
517 BLI_assert(ELEM(htype, BM_VERT, BM_EDGE, BM_FACE));
518 BLI_assert(ELEM(itype, BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH));
520 if (!BLI_listbase_is_empty(&bm->selected)) {
522 BMEditSelection *ese;
524 for (ese = bm->selected.last; ese; ese = ese->prev) {
525 /* shouldn't need this check */
526 if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
528 /* only use contiguous selection */
529 if (ese->htype != htype) {
534 elems[i++] = ese->ele;
553 BM_ITER_MESH (ele, &iter, bm, itype) {
554 BLI_assert(ele->head.htype == htype);
555 if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
566 static unsigned int bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n)
568 return bm_mesh_elems_select_get_n__internal(
569 bm, (BMElem **)elems, min_ii(n, bm->totvertsel),
570 BM_VERTS_OF_MESH, BM_VERT);
572 static unsigned int bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const unsigned int n)
574 return bm_mesh_elems_select_get_n__internal(
575 bm, (BMElem **)elems, min_ii(n, bm->totedgesel),
576 BM_EDGES_OF_MESH, BM_EDGE);
579 static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n)
581 return bm_mesh_elems_select_get_n__internal(
582 bm, (BMElem **)elems, min_ii(n, bm->totfacesel),
583 BM_FACES_OF_MESH, BM_FACE);
587 int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3], const short around)
589 Scene *scene = CTX_data_scene(C);
590 Object *obedit = CTX_data_edit_object(C);
593 int result = ORIENTATION_NONE;
594 const bool activeOnly = (around == V3D_AROUND_ACTIVE);
600 float imat[3][3], mat[3][3];
602 /* we need the transpose of the inverse for a normal... */
603 copy_m3_m4(imat, ob->obmat);
605 invert_m3_m3(mat, imat);
610 if (ob->type == OB_MESH) {
611 BMEditMesh *em = BKE_editmesh_from_object(ob);
613 float vec[3] = {0, 0, 0};
615 /* USE LAST SELECTED WITH ACTIVE */
616 if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
617 BM_editselection_normal(&ese, normal);
618 BM_editselection_plane(&ese, plane);
622 result = ORIENTATION_VERT;
625 result = ORIENTATION_EDGE;
628 result = ORIENTATION_FACE;
633 if (em->bm->totfacesel >= 1) {
637 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
638 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
639 BM_face_calc_tangent_auto(efa, vec);
640 add_v3_v3(normal, efa->no);
641 add_v3_v3(plane, vec);
645 result = ORIENTATION_FACE;
647 else if (em->bm->totvertsel == 3) {
650 if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) {
654 normal_tri_v3(normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
656 /* check if the normal is pointing opposite to vert normals */
657 no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0];
658 no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1];
659 no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2];
660 if (dot_v3v3(no_test, normal) < 0.0f) {
664 if (em->bm->totedgesel >= 1) {
665 /* find an edge thats apart of v_tri (no need to search all edges) */
669 for (j = 0; j < 3; j++) {
670 BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]);
671 if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) {
672 const float e_test_length = BM_edge_calc_length_squared(e_test);
673 if ((e == NULL) || (e_length < e_test_length)) {
675 e_length = e_test_length;
683 if (BM_edge_is_boundary(e)) {
684 BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]);
690 sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co);
693 BM_vert_tri_calc_tangent_edge(v_tri, plane);
700 result = ORIENTATION_FACE;
702 else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) {
703 BMVert *v_pair[2] = {NULL, NULL};
706 if (em->bm->totedgesel == 1) {
707 if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) {
713 BLI_assert(em->bm->totvertsel == 2);
714 bm_mesh_verts_select_get_n(em->bm, v_pair, 2);
717 /* should never fail */
718 if (LIKELY(v_pair[0] && v_pair[1])) {
719 bool v_pair_swap = false;
723 * - Edges and vert-pairs treated the same way.
724 * - Point the Y axis along the edge vector (towards the active vertex).
725 * - Point the Z axis outwards (the same direction as the normals).
727 * \note Z points outwards - along the normal.
728 * take care making changes here, see: T38592, T43708
731 /* be deterministic where possible and ensure v_pair[0] is active */
732 if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) {
735 else if (eed && BM_edge_is_boundary(eed)) {
736 /* predictable direction for boundary edges */
737 if (eed->l->v != v_pair[0]) {
743 SWAP(BMVert *, v_pair[0], v_pair[1]);
746 add_v3_v3v3(normal, v_pair[1]->no, v_pair[0]->no);
747 sub_v3_v3v3(plane, v_pair[1]->co, v_pair[0]->co);
749 if (normalize_v3(plane) != 0.0f) {
750 /* For edges it'd important the resulting matrix can rotate around the edge,
751 * project onto the plane so we can use a fallback value. */
752 project_plane_normalized_v3_v3v3(normal, normal, plane);
753 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
754 /* in the case the normal and plane are aligned,
755 * use a fallback normal which is orthogonal to the plane. */
756 ortho_v3_v3(normal, plane);
761 result = ORIENTATION_EDGE;
763 else if (em->bm->totvertsel == 1) {
766 if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) {
767 copy_v3_v3(normal, v->no);
770 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
771 bool v_pair_swap = false;
772 BMVert *v_pair[2] = {BM_edge_other_vert(e_pair[0], v), BM_edge_other_vert(e_pair[1], v)};
773 float dir_pair[2][3];
775 if (BM_edge_is_boundary(e_pair[0])) {
776 if (e_pair[0]->l->v != v) {
781 if (BM_edge_calc_length_squared(e_pair[0]) < BM_edge_calc_length_squared(e_pair[1])) {
787 SWAP(BMVert *, v_pair[0], v_pair[1]);
790 sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co);
791 sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co);
792 normalize_v3(dir_pair[0]);
793 normalize_v3(dir_pair[1]);
795 add_v3_v3v3(plane, dir_pair[0], dir_pair[1]);
799 if (is_zero_v3(plane)) {
800 result = ORIENTATION_VERT;
803 result = ORIENTATION_EDGE;
806 else if (em->bm->totvertsel > 3) {
812 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
813 if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
814 add_v3_v3(normal, v->no);
817 normalize_v3(normal);
818 result = ORIENTATION_VERT;
822 /* not needed but this matches 2.68 and older behavior */
826 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
827 Curve *cu = obedit->data;
830 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
832 void *vert_act = NULL;
833 if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) {
834 if (nu->type == CU_BEZIER) {
835 BezTriple *bezt = vert_act;
836 BKE_nurb_bezt_calc_normal(nu, bezt, normal);
837 BKE_nurb_bezt_calc_plane(nu, bezt, plane);
840 BPoint *bp = vert_act;
841 BKE_nurb_bpoint_calc_normal(nu, bp, normal);
842 BKE_nurb_bpoint_calc_plane(nu, bp, plane);
846 const bool use_handle = (cu->drawflag & CU_HIDE_HANDLES) == 0;
848 for (nu = nurbs->first; nu; nu = nu->next) {
849 /* only bezier has a normal */
850 if (nu->type == CU_BEZIER) {
851 BezTriple *bezt = nu->bezt;
856 #define SEL_F1 (1 << 0)
857 #define SEL_F2 (1 << 1)
858 #define SEL_F3 (1 << 2)
861 if (bezt->f1 & SELECT) flag |= SEL_F1;
862 if (bezt->f2 & SELECT) flag |= SEL_F2;
863 if (bezt->f3 & SELECT) flag |= SEL_F3;
866 flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0;
872 if ((around == V3D_AROUND_LOCAL_ORIGINS) ||
873 ELEM(flag, SEL_F2, SEL_F1 | SEL_F3, SEL_F1 | SEL_F2 | SEL_F3))
875 BKE_nurb_bezt_calc_normal(nu, bezt, tvec);
876 add_v3_v3(normal, tvec);
879 /* ignore bezt->f2 in this case */
881 sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]);
883 add_v3_v3(normal, tvec);
886 sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]);
888 add_v3_v3(normal, tvec);
892 BKE_nurb_bezt_calc_plane(nu, bezt, tvec);
893 add_v3_v3(plane, tvec);
903 else if (nu->bp && (nu->pntsv == 1)) {
907 if (bp->f1 & SELECT) {
910 BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp);
911 BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp);
913 const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT);
914 const bool is_next_sel = bp_next && (bp_next->f1 & SELECT);
915 if (is_prev_sel == false && is_next_sel == false) {
916 /* Isolated, add based on surrounding */
917 BKE_nurb_bpoint_calc_normal(nu, bp, tvec);
918 add_v3_v3(normal, tvec);
920 else if (is_next_sel) {
921 /* A segment, add the edge normal */
922 sub_v3_v3v3(tvec, bp->vec, bp_next->vec);
924 add_v3_v3(normal, tvec);
927 BKE_nurb_bpoint_calc_plane(nu, bp, tvec);
928 add_v3_v3(plane, tvec);
936 if (!is_zero_v3(normal)) {
937 result = ORIENTATION_FACE;
940 else if (obedit->type == OB_MBALL) {
941 MetaBall *mb = obedit->data;
946 if (activeOnly && (ml = mb->lastelem)) {
947 quat_to_mat3(tmat, ml->quat);
948 add_v3_v3(normal, tmat[2]);
949 add_v3_v3(plane, tmat[1]);
953 for (ml = mb->editelems->first; ml; ml = ml->next) {
954 if (ml->flag & SELECT) {
955 quat_to_mat3(tmat, ml->quat);
956 add_v3_v3(normal, tmat[2]);
957 add_v3_v3(plane, tmat[1]);
964 if (!is_zero_v3(plane)) {
965 result = ORIENTATION_FACE;
969 else if (obedit->type == OB_ARMATURE) {
970 bArmature *arm = obedit->data;
975 if (activeOnly && (ebone = arm->act_edbone)) {
976 ED_armature_ebone_to_mat3(ebone, tmat);
977 add_v3_v3(normal, tmat[2]);
978 add_v3_v3(plane, tmat[1]);
982 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
983 if (arm->layer & ebone->layer) {
984 if (ebone->flag & BONE_SELECTED) {
985 ED_armature_ebone_to_mat3(ebone, tmat);
986 add_v3_v3(normal, tmat[2]);
987 add_v3_v3(plane, tmat[1]);
995 if (!is_zero_v3(plane)) {
996 result = ORIENTATION_EDGE;
1001 /* Vectors from edges don't need the special transpose inverse multiplication */
1002 if (result == ORIENTATION_EDGE) {
1005 mul_mat3_m4_v3(ob->obmat, normal);
1006 mul_mat3_m4_v3(ob->obmat, plane);
1008 /* align normal to edge direction (so normal is perpendicular to the plane).
1009 * 'ORIENTATION_EDGE' will do the other way around.
1010 * This has to be done **after** applying obmat, see T45775! */
1011 project_v3_v3v3(tvec, normal, plane);
1012 sub_v3_v3(normal, tvec);
1015 mul_m3_v3(mat, normal);
1016 mul_m3_v3(mat, plane);
1019 else if (ob && (ob->mode & OB_MODE_POSE)) {
1020 bArmature *arm = ob->data;
1021 bPoseChannel *pchan;
1022 float imat[3][3], mat[3][3];
1025 if (activeOnly && (pchan = BKE_pose_channel_active(ob))) {
1026 add_v3_v3(normal, pchan->pose_mat[2]);
1027 add_v3_v3(plane, pchan->pose_mat[1]);
1033 totsel = count_bone_select(arm, &arm->bonebase, true);
1035 /* use channels to get stats */
1036 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
1037 if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) {
1038 add_v3_v3(normal, pchan->pose_mat[2]);
1039 add_v3_v3(plane, pchan->pose_mat[1]);
1046 /* use for both active & all */
1048 /* we need the transpose of the inverse for a normal... */
1049 copy_m3_m4(imat, ob->obmat);
1051 invert_m3_m3(mat, imat);
1053 mul_m3_v3(mat, normal);
1054 mul_m3_v3(mat, plane);
1056 result = ORIENTATION_EDGE;
1059 else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
1063 /* we need the one selected object, if its not active */
1064 View3D *v3d = CTX_wm_view3d(C);
1066 if (ob && (ob->flag & SELECT)) {
1070 /* first selected */
1072 for (base = scene->base.first; base; base = base->next) {
1073 if (TESTBASELIB(v3d, base)) {
1081 copy_v3_v3(normal, ob->obmat[2]);
1082 copy_v3_v3(plane, ob->obmat[1]);
1084 result = ORIENTATION_NORMAL;
1090 int getTransformOrientation(const bContext *C, float normal[3], float plane[3])
1092 /* dummy value, not V3D_AROUND_ACTIVE and not V3D_AROUND_LOCAL_ORIGINS */
1093 short around = V3D_AROUND_CENTER_BOUNDS;
1095 return getTransformOrientation_ex(C, normal, plane, around);
1098 void ED_getTransformOrientationMatrix(const bContext *C, float orientation_mat[3][3], const short around)
1100 float normal[3] = {0.0, 0.0, 0.0};
1101 float plane[3] = {0.0, 0.0, 0.0};
1105 type = getTransformOrientation_ex(C, normal, plane, around);
1108 case ORIENTATION_NORMAL:
1109 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1110 type = ORIENTATION_NONE;
1113 case ORIENTATION_VERT:
1114 if (createSpaceNormal(orientation_mat, normal) == 0) {
1115 type = ORIENTATION_NONE;
1118 case ORIENTATION_EDGE:
1119 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1120 type = ORIENTATION_NONE;
1123 case ORIENTATION_FACE:
1124 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1125 type = ORIENTATION_NONE;
1130 if (type == ORIENTATION_NONE) {
1131 unit_m3(orientation_mat);