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"
41 #include "DNA_workspace_types.h"
44 #include "BLI_listbase.h"
45 #include "BLI_string.h"
46 #include "BLI_string_utils.h"
47 #include "BLI_utildefines.h"
49 #include "BKE_action.h"
50 #include "BKE_curve.h"
51 #include "BKE_context.h"
52 #include "BKE_editmesh.h"
53 #include "BKE_report.h"
55 #include "BKE_screen.h"
56 #include "BKE_workspace.h"
58 #include "BLT_translation.h"
60 #include "ED_armature.h"
62 #include "transform.h"
64 /* *********************** TransSpace ************************** */
66 void BIF_clearTransformOrientation(bContext *C)
68 WorkSpace *workspace = CTX_wm_workspace(C);
69 ListBase *transform_orientations = BKE_workspace_transform_orientations_get(workspace);
70 View3D *v3d = CTX_wm_view3d(C);
72 BLI_freelistN(transform_orientations);
74 // Need to loop over all view3d
75 if (v3d && v3d->twmode == V3D_MANIP_CUSTOM) {
76 v3d->twmode = V3D_MANIP_GLOBAL; /* fallback to global */
77 v3d->custom_orientation_index = -1;
81 static TransformOrientation *findOrientationName(ListBase *lb, const char *name)
83 return BLI_findstring(lb, name, offsetof(TransformOrientation, name));
86 static bool uniqueOrientationNameCheck(void *arg, const char *name)
88 return findOrientationName((ListBase *)arg, name) != NULL;
91 static void uniqueOrientationName(ListBase *lb, char *name)
93 BLI_uniquename_cb(uniqueOrientationNameCheck, lb, CTX_DATA_(BLT_I18NCONTEXT_ID_SCENE, "Space"), '.', name,
94 sizeof(((TransformOrientation *)NULL)->name));
97 static TransformOrientation *createViewSpace(bContext *C, ReportList *UNUSED(reports),
98 const char *name, const bool overwrite)
100 RegionView3D *rv3d = CTX_wm_region_view3d(C);
106 copy_m3_m4(mat, rv3d->viewinv);
110 View3D *v3d = CTX_wm_view3d(C);
111 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
112 /* If an object is used as camera, then this space is the same as object space! */
113 name = v3d->camera->id.name + 2;
116 name = "Custom View";
120 return addMatrixSpace(C, mat, name, overwrite);
123 static TransformOrientation *createObjectSpace(bContext *C, ReportList *UNUSED(reports),
124 const char *name, const bool overwrite)
126 Base *base = CTX_data_active_base(C);
135 copy_m3_m4(mat, ob->obmat);
138 /* use object name if no name is given */
140 name = ob->id.name + 2;
143 return addMatrixSpace(C, mat, name, overwrite);
146 static TransformOrientation *createBoneSpace(bContext *C, ReportList *reports,
147 const char *name, const bool overwrite)
150 float normal[3], plane[3];
152 getTransformOrientation(C, normal, plane);
154 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
155 BKE_reports_prepend(reports, "Cannot use zero-length bone");
163 return addMatrixSpace(C, mat, name, overwrite);
166 static TransformOrientation *createCurveSpace(bContext *C, ReportList *reports,
167 const char *name, const bool overwrite)
170 float normal[3], plane[3];
172 getTransformOrientation(C, normal, plane);
174 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
175 BKE_reports_prepend(reports, "Cannot use zero-length curve");
183 return addMatrixSpace(C, mat, name, overwrite);
187 static TransformOrientation *createMeshSpace(bContext *C, ReportList *reports,
188 const char *name, const bool overwrite)
191 float normal[3], plane[3];
194 type = getTransformOrientation(C, normal, plane);
197 case ORIENTATION_VERT:
198 if (createSpaceNormal(mat, normal) == 0) {
199 BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal");
207 case ORIENTATION_EDGE:
208 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
209 BKE_reports_prepend(reports, "Cannot use zero-length edge");
217 case ORIENTATION_FACE:
218 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
219 BKE_reports_prepend(reports, "Cannot use zero-area face");
231 return addMatrixSpace(C, mat, name, overwrite);
234 bool createSpaceNormal(float mat[3][3], const float normal[3])
236 float tangent[3] = {0.0f, 0.0f, 1.0f};
238 copy_v3_v3(mat[2], normal);
239 if (normalize_v3(mat[2]) == 0.0f) {
240 return false; /* error return */
243 cross_v3_v3v3(mat[0], mat[2], tangent);
244 if (is_zero_v3(mat[0])) {
246 tangent[1] = tangent[2] = 0.0f;
247 cross_v3_v3v3(mat[0], tangent, mat[2]);
250 cross_v3_v3v3(mat[1], mat[2], mat[0]);
258 * \note To recreate an orientation from the matrix:
259 * - (plane == mat[1])
260 * - (normal == mat[2])
262 bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
264 if (normalize_v3_v3(mat[2], normal) == 0.0f) {
265 return false; /* error return */
268 /* negate so we can use values from the matrix as input */
269 negate_v3_v3(mat[1], tangent);
270 /* preempt zero length tangent from causing trouble */
271 if (is_zero_v3(mat[1])) {
275 cross_v3_v3v3(mat[0], mat[2], mat[1]);
276 if (normalize_v3(mat[0]) == 0.0f) {
277 return false; /* error return */
280 cross_v3_v3v3(mat[1], mat[2], mat[0]);
281 normalize_v3(mat[1]);
283 /* final matrix must be normalized, do inline */
284 // normalize_m3(mat);
289 void BIF_createTransformOrientation(bContext *C, ReportList *reports,
290 const char *name, const bool use_view,
291 const bool activate, const bool overwrite)
293 TransformOrientation *ts = NULL;
296 ts = createViewSpace(C, reports, name, overwrite);
299 Object *obedit = CTX_data_edit_object(C);
300 Object *ob = CTX_data_active_object(C);
302 if (obedit->type == OB_MESH)
303 ts = createMeshSpace(C, reports, name, overwrite);
304 else if (obedit->type == OB_ARMATURE)
305 ts = createBoneSpace(C, reports, name, overwrite);
306 else if (obedit->type == OB_CURVE)
307 ts = createCurveSpace(C, reports, name, overwrite);
309 else if (ob && (ob->mode & OB_MODE_POSE)) {
310 ts = createBoneSpace(C, reports, name, overwrite);
313 ts = createObjectSpace(C, reports, name, overwrite);
317 if (activate && ts != NULL) {
318 BIF_selectTransformOrientation(C, ts);
322 TransformOrientation *addMatrixSpace(bContext *C, float mat[3][3],
323 const char *name, const bool overwrite)
325 TransformOrientation *ts = NULL;
326 WorkSpace *workspace = CTX_wm_workspace(C);
327 ListBase *transform_orientations = BKE_workspace_transform_orientations_get(workspace);
328 char name_unique[sizeof(ts->name)];
331 ts = findOrientationName(transform_orientations, name);
334 BLI_strncpy(name_unique, name, sizeof(name_unique));
335 uniqueOrientationName(transform_orientations, name_unique);
339 /* if not, create a new one */
341 ts = MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix");
342 BLI_addtail(transform_orientations, ts);
343 BLI_strncpy(ts->name, name, sizeof(ts->name));
346 /* copy matrix into transform space */
347 copy_m3_m3(ts->mat, mat);
352 void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target)
354 BKE_workspace_transform_orientation_remove(CTX_wm_workspace(C), target);
357 void BIF_removeTransformOrientationIndex(bContext *C, int index)
359 TransformOrientation *target = BKE_workspace_transform_orientation_find(CTX_wm_workspace(C), index);
360 BIF_removeTransformOrientation(C, target);
363 void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target)
365 int index = BKE_workspace_transform_orientation_get_index(CTX_wm_workspace(C), target);
366 View3D *v3d = CTX_wm_view3d(C);
368 BLI_assert(index != -1);
370 v3d->twmode = V3D_MANIP_CUSTOM;
371 v3d->custom_orientation_index = index;
375 * Activate a transform orientation in a 3D view based on an enum value.
377 * \param orientation: If this is #V3D_MANIP_CUSTOM or greater, the custom transform orientation
378 * with index \a orientation - #V3D_MANIP_CUSTOM gets activated.
380 void BIF_selectTransformOrientationValue(View3D *v3d, int orientation)
382 const bool is_custom = orientation >= V3D_MANIP_CUSTOM;
384 v3d->twmode = is_custom ? V3D_MANIP_CUSTOM : orientation;
385 v3d->custom_orientation_index = is_custom ? (orientation - V3D_MANIP_CUSTOM) : -1;
388 int BIF_countTransformOrientation(const bContext *C)
390 WorkSpace *workspace = CTX_wm_workspace(C);
391 ListBase *transform_orientations = BKE_workspace_transform_orientations_get(workspace);
392 return BLI_listbase_count(transform_orientations);
395 bool applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name)
398 BLI_strncpy(r_name, ts->name, MAX_NAME);
400 copy_m3_m3(r_mat, ts->mat);
405 static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it)
411 for (bone = lb->first; bone; bone = bone->next) {
412 bone->flag &= ~BONE_TRANSFORM;
415 if (bone->layer & arm->layer) {
416 if (bone->flag & BONE_SELECTED) {
417 bone->flag |= BONE_TRANSFORM;
420 /* no transform on children if one parent bone is selected */
425 total += count_bone_select(arm, &bone->childbase, do_next);
431 void initTransformOrientation(bContext *C, TransInfo *t)
433 Object *ob = CTX_data_active_object(C);
434 Object *obedit = CTX_data_active_object(C);
436 switch (t->current_orientation) {
437 case V3D_MANIP_GLOBAL:
438 unit_m3(t->spacemtx);
439 BLI_strncpy(t->spacename, IFACE_("global"), sizeof(t->spacename));
442 case V3D_MANIP_GIMBAL:
443 unit_m3(t->spacemtx);
444 if (ob && gimbal_axis(ob, t->spacemtx)) {
445 BLI_strncpy(t->spacename, IFACE_("gimbal"), sizeof(t->spacename));
448 ATTR_FALLTHROUGH; /* no gimbal fallthrough to normal */
449 case V3D_MANIP_NORMAL:
450 if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
451 BLI_strncpy(t->spacename, IFACE_("normal"), sizeof(t->spacename));
452 ED_getTransformOrientationMatrix(C, t->spacemtx, t->around);
455 ATTR_FALLTHROUGH; /* we define 'normal' as 'local' in Object mode */
456 case V3D_MANIP_LOCAL:
457 BLI_strncpy(t->spacename, IFACE_("local"), sizeof(t->spacename));
460 copy_m3_m4(t->spacemtx, ob->obmat);
461 normalize_m3(t->spacemtx);
464 unit_m3(t->spacemtx);
470 if ((t->spacetype == SPACE_VIEW3D) &&
471 (t->ar->regiontype == RGN_TYPE_WINDOW))
473 RegionView3D *rv3d = t->ar->regiondata;
476 BLI_strncpy(t->spacename, IFACE_("view"), sizeof(t->spacename));
477 copy_m3_m4(mat, rv3d->viewinv);
479 copy_m3_m3(t->spacemtx, mat);
482 unit_m3(t->spacemtx);
485 case V3D_MANIP_CUSTOM:
486 BLI_strncpy(t->spacename, t->custom_orientation->name, sizeof(t->spacename));
488 if (applyTransformOrientation(t->custom_orientation, t->spacemtx, t->spacename)) {
492 unit_m3(t->spacemtx);
499 * utility function - get first n, selected vert/edge/faces
501 static unsigned int bm_mesh_elems_select_get_n__internal(
502 BMesh *bm, BMElem **elems, const unsigned int n,
503 const BMIterType itype, const char htype)
509 BLI_assert(ELEM(htype, BM_VERT, BM_EDGE, BM_FACE));
510 BLI_assert(ELEM(itype, BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH));
512 if (!BLI_listbase_is_empty(&bm->selected)) {
514 BMEditSelection *ese;
516 for (ese = bm->selected.last; ese; ese = ese->prev) {
517 /* shouldn't need this check */
518 if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
520 /* only use contiguous selection */
521 if (ese->htype != htype) {
526 elems[i++] = ese->ele;
545 BM_ITER_MESH (ele, &iter, bm, itype) {
546 BLI_assert(ele->head.htype == htype);
547 if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
558 static unsigned int bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n)
560 return bm_mesh_elems_select_get_n__internal(
561 bm, (BMElem **)elems, min_ii(n, bm->totvertsel),
562 BM_VERTS_OF_MESH, BM_VERT);
564 static unsigned int bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const unsigned int n)
566 return bm_mesh_elems_select_get_n__internal(
567 bm, (BMElem **)elems, min_ii(n, bm->totedgesel),
568 BM_EDGES_OF_MESH, BM_EDGE);
571 static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n)
573 return bm_mesh_elems_select_get_n__internal(
574 bm, (BMElem **)elems, min_ii(n, bm->totfacesel),
575 BM_FACES_OF_MESH, BM_FACE);
579 int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3], const short around)
581 SceneLayer *sl = CTX_data_scene_layer(C);
582 Object *obedit = CTX_data_edit_object(C);
584 Object *ob = OBACT_NEW;
585 int result = ORIENTATION_NONE;
586 const bool activeOnly = (around == V3D_AROUND_ACTIVE);
592 float imat[3][3], mat[3][3];
594 /* we need the transpose of the inverse for a normal... */
595 copy_m3_m4(imat, ob->obmat);
597 invert_m3_m3(mat, imat);
602 if (ob->type == OB_MESH) {
603 BMEditMesh *em = BKE_editmesh_from_object(ob);
605 float vec[3] = {0, 0, 0};
607 /* USE LAST SELECTED WITH ACTIVE */
608 if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
609 BM_editselection_normal(&ese, normal);
610 BM_editselection_plane(&ese, plane);
614 result = ORIENTATION_VERT;
617 result = ORIENTATION_EDGE;
620 result = ORIENTATION_FACE;
625 if (em->bm->totfacesel >= 1) {
629 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
630 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
631 BM_face_calc_tangent_auto(efa, vec);
632 add_v3_v3(normal, efa->no);
633 add_v3_v3(plane, vec);
637 result = ORIENTATION_FACE;
639 else if (em->bm->totvertsel == 3) {
642 if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) {
646 normal_tri_v3(normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
648 /* check if the normal is pointing opposite to vert normals */
649 no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0];
650 no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1];
651 no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2];
652 if (dot_v3v3(no_test, normal) < 0.0f) {
656 if (em->bm->totedgesel >= 1) {
657 /* find an edge thats apart of v_tri (no need to search all edges) */
661 for (j = 0; j < 3; j++) {
662 BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]);
663 if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) {
664 const float e_test_length = BM_edge_calc_length_squared(e_test);
665 if ((e == NULL) || (e_length < e_test_length)) {
667 e_length = e_test_length;
675 if (BM_edge_is_boundary(e)) {
676 BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]);
682 sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co);
685 BM_vert_tri_calc_tangent_edge(v_tri, plane);
692 result = ORIENTATION_FACE;
694 else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) {
695 BMVert *v_pair[2] = {NULL, NULL};
698 if (em->bm->totedgesel == 1) {
699 if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) {
705 BLI_assert(em->bm->totvertsel == 2);
706 bm_mesh_verts_select_get_n(em->bm, v_pair, 2);
709 /* should never fail */
710 if (LIKELY(v_pair[0] && v_pair[1])) {
711 bool v_pair_swap = false;
715 * - Edges and vert-pairs treated the same way.
716 * - Point the Y axis along the edge vector (towards the active vertex).
717 * - Point the Z axis outwards (the same direction as the normals).
719 * \note Z points outwards - along the normal.
720 * take care making changes here, see: T38592, T43708
723 /* be deterministic where possible and ensure v_pair[0] is active */
724 if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) {
727 else if (eed && BM_edge_is_boundary(eed)) {
728 /* predictable direction for boundary edges */
729 if (eed->l->v != v_pair[0]) {
735 SWAP(BMVert *, v_pair[0], v_pair[1]);
738 add_v3_v3v3(normal, v_pair[0]->no, v_pair[1]->no);
739 sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co);
740 /* flip the plane normal so we point outwards */
744 result = ORIENTATION_EDGE;
746 else if (em->bm->totvertsel == 1) {
749 if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) {
750 copy_v3_v3(normal, v->no);
753 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
754 bool v_pair_swap = false;
755 BMVert *v_pair[2] = {BM_edge_other_vert(e_pair[0], v), BM_edge_other_vert(e_pair[1], v)};
756 float dir_pair[2][3];
758 if (BM_edge_is_boundary(e_pair[0])) {
759 if (e_pair[0]->l->v != v) {
764 if (BM_edge_calc_length_squared(e_pair[0]) < BM_edge_calc_length_squared(e_pair[1])) {
770 SWAP(BMVert *, v_pair[0], v_pair[1]);
773 sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co);
774 sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co);
775 normalize_v3(dir_pair[0]);
776 normalize_v3(dir_pair[1]);
778 add_v3_v3v3(plane, dir_pair[0], dir_pair[1]);
782 if (is_zero_v3(plane)) {
783 result = ORIENTATION_VERT;
786 result = ORIENTATION_EDGE;
789 else if (em->bm->totvertsel > 3) {
795 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
796 if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
797 add_v3_v3(normal, v->no);
800 normalize_v3(normal);
801 result = ORIENTATION_VERT;
805 /* not needed but this matches 2.68 and older behavior */
809 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
810 Curve *cu = obedit->data;
813 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
815 void *vert_act = NULL;
816 if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) {
817 if (nu->type == CU_BEZIER) {
818 BezTriple *bezt = vert_act;
819 BKE_nurb_bezt_calc_normal(nu, bezt, normal);
820 BKE_nurb_bezt_calc_plane(nu, bezt, plane);
823 BPoint *bp = vert_act;
824 BKE_nurb_bpoint_calc_normal(nu, bp, normal);
825 BKE_nurb_bpoint_calc_plane(nu, bp, plane);
829 const bool use_handle = (cu->drawflag & CU_HIDE_HANDLES) == 0;
831 for (nu = nurbs->first; nu; nu = nu->next) {
832 /* only bezier has a normal */
833 if (nu->type == CU_BEZIER) {
834 BezTriple *bezt = nu->bezt;
839 #define SEL_F1 (1 << 0)
840 #define SEL_F2 (1 << 1)
841 #define SEL_F3 (1 << 2)
844 if (bezt->f1 & SELECT) flag |= SEL_F1;
845 if (bezt->f2 & SELECT) flag |= SEL_F2;
846 if (bezt->f3 & SELECT) flag |= SEL_F3;
849 flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0;
855 if ((around == V3D_AROUND_LOCAL_ORIGINS) ||
856 ELEM(flag, SEL_F2, SEL_F1 | SEL_F3, SEL_F1 | SEL_F2 | SEL_F3))
858 BKE_nurb_bezt_calc_normal(nu, bezt, tvec);
859 add_v3_v3(normal, tvec);
862 /* ignore bezt->f2 in this case */
864 sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]);
866 add_v3_v3(normal, tvec);
869 sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]);
871 add_v3_v3(normal, tvec);
875 BKE_nurb_bezt_calc_plane(nu, bezt, tvec);
876 add_v3_v3(plane, tvec);
886 else if (nu->bp && (nu->pntsv == 1)) {
890 if (bp->f1 & SELECT) {
893 BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp);
894 BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp);
896 const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT);
897 const bool is_next_sel = bp_next && (bp_next->f1 & SELECT);
898 if (is_prev_sel == false && is_next_sel == false) {
899 /* Isolated, add based on surrounding */
900 BKE_nurb_bpoint_calc_normal(nu, bp, tvec);
901 add_v3_v3(normal, tvec);
903 else if (is_next_sel) {
904 /* A segment, add the edge normal */
905 sub_v3_v3v3(tvec, bp->vec, bp_next->vec );
907 add_v3_v3(normal, tvec);
910 BKE_nurb_bpoint_calc_plane(nu, bp, tvec);
911 add_v3_v3(plane, tvec);
919 if (!is_zero_v3(normal)) {
920 result = ORIENTATION_FACE;
923 else if (obedit->type == OB_MBALL) {
924 MetaBall *mb = obedit->data;
929 if (activeOnly && (ml = mb->lastelem)) {
930 quat_to_mat3(tmat, ml->quat);
931 add_v3_v3(normal, tmat[2]);
932 add_v3_v3(plane, tmat[1]);
936 for (ml = mb->editelems->first; ml; ml = ml->next) {
937 if (ml->flag & SELECT) {
938 quat_to_mat3(tmat, ml->quat);
939 add_v3_v3(normal, tmat[2]);
940 add_v3_v3(plane, tmat[1]);
947 if (!is_zero_v3(plane)) {
948 result = ORIENTATION_FACE;
952 else if (obedit->type == OB_ARMATURE) {
953 bArmature *arm = obedit->data;
958 if (activeOnly && (ebone = arm->act_edbone)) {
959 ED_armature_ebone_to_mat3(ebone, tmat);
960 add_v3_v3(normal, tmat[2]);
961 add_v3_v3(plane, tmat[1]);
965 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
966 if (arm->layer & ebone->layer) {
967 if (ebone->flag & BONE_SELECTED) {
968 ED_armature_ebone_to_mat3(ebone, tmat);
969 add_v3_v3(normal, tmat[2]);
970 add_v3_v3(plane, tmat[1]);
978 if (!is_zero_v3(plane)) {
979 result = ORIENTATION_EDGE;
984 /* Vectors from edges don't need the special transpose inverse multiplication */
985 if (result == ORIENTATION_EDGE) {
988 mul_mat3_m4_v3(ob->obmat, normal);
989 mul_mat3_m4_v3(ob->obmat, plane);
991 /* align normal to edge direction (so normal is perpendicular to the plane).
992 * 'ORIENTATION_EDGE' will do the other way around.
993 * This has to be done **after** applying obmat, see T45775! */
994 project_v3_v3v3(tvec, normal, plane);
995 sub_v3_v3(normal, tvec);
998 mul_m3_v3(mat, normal);
999 mul_m3_v3(mat, plane);
1002 else if (ob && (ob->mode & OB_MODE_POSE)) {
1003 bArmature *arm = ob->data;
1004 bPoseChannel *pchan;
1005 float imat[3][3], mat[3][3];
1008 if (activeOnly && (pchan = BKE_pose_channel_active(ob))) {
1009 add_v3_v3(normal, pchan->pose_mat[2]);
1010 add_v3_v3(plane, pchan->pose_mat[1]);
1016 totsel = count_bone_select(arm, &arm->bonebase, true);
1018 /* use channels to get stats */
1019 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
1020 if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) {
1021 add_v3_v3(normal, pchan->pose_mat[2]);
1022 add_v3_v3(plane, pchan->pose_mat[1]);
1029 /* use for both active & all */
1031 /* we need the transpose of the inverse for a normal... */
1032 copy_m3_m4(imat, ob->obmat);
1034 invert_m3_m3(mat, imat);
1036 mul_m3_v3(mat, normal);
1037 mul_m3_v3(mat, plane);
1039 result = ORIENTATION_EDGE;
1042 else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
1046 /* we need the one selected object, if its not active */
1049 if (base && ((base->flag & BASE_SELECTED) != 0)) {
1053 /* first selected */
1055 for (base = sl->object_bases.first; base; base = base->next) {
1056 if (TESTBASELIB_NEW(base)) {
1064 copy_v3_v3(normal, ob->obmat[2]);
1065 copy_v3_v3(plane, ob->obmat[1]);
1067 result = ORIENTATION_NORMAL;
1073 int getTransformOrientation(const bContext *C, float normal[3], float plane[3])
1075 /* dummy value, not V3D_AROUND_ACTIVE and not V3D_AROUND_LOCAL_ORIGINS */
1076 short around = V3D_AROUND_CENTER_BOUNDS;
1078 return getTransformOrientation_ex(C, normal, plane, around);
1081 void ED_getTransformOrientationMatrix(const bContext *C, float orientation_mat[3][3], const short around)
1083 float normal[3] = {0.0, 0.0, 0.0};
1084 float plane[3] = {0.0, 0.0, 0.0};
1088 type = getTransformOrientation_ex(C, normal, plane, around);
1091 case ORIENTATION_NORMAL:
1092 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1093 type = ORIENTATION_NONE;
1096 case ORIENTATION_VERT:
1097 if (createSpaceNormal(orientation_mat, normal) == 0) {
1098 type = ORIENTATION_NONE;
1101 case ORIENTATION_EDGE:
1102 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1103 type = ORIENTATION_NONE;
1106 case ORIENTATION_FACE:
1107 if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) {
1108 type = ORIENTATION_NONE;
1113 if (type == ORIENTATION_NONE) {
1114 unit_m3(orientation_mat);