Blender V4.5
transform_orientations.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cctype>
10#include <cstddef>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_armature_types.h"
16#include "DNA_curve_types.h"
17#include "DNA_meta_types.h"
18#include "DNA_object_types.h"
19#include "DNA_scene_types.h"
20#include "DNA_screen_types.h"
21#include "DNA_sequence_types.h"
22#include "DNA_space_types.h"
23#include "DNA_view3d_types.h"
24
25#include "BLI_listbase.h"
26#include "BLI_math_geom.h"
27#include "BLI_math_matrix.h"
28#include "BLI_math_rotation.h"
29#include "BLI_math_vector.h"
30#include "BLI_string.h"
31#include "BLI_string_utils.hh"
32#include "BLI_utildefines.h"
33
34#include "BKE_action.hh"
35#include "BKE_armature.hh"
36#include "BKE_context.hh"
37#include "BKE_curve.hh"
38#include "BKE_editmesh.hh"
39#include "BKE_layer.hh"
40#include "BKE_report.hh"
41#include "BKE_scene.hh"
42
43#include "BLT_translation.hh"
44
45#include "ED_armature.hh"
46
47#include "ANIM_armature.hh"
49
50#include "SEQ_select.hh"
51
52#include "SEQ_transform.hh"
53#include "transform.hh"
55
56namespace blender::ed::transform {
57
58/* *********************** TransSpace ************************** */
59
61{
62 Scene *scene = CTX_data_scene(C);
63 ListBase *transform_orientations = &scene->transform_spaces;
64
65 BLI_freelistN(transform_orientations);
66
67 for (int i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
68 TransformOrientationSlot *orient_slot = &scene->orientation_slots[i];
69 if (orient_slot->type == V3D_ORIENT_CUSTOM) {
70 orient_slot->type = V3D_ORIENT_GLOBAL; /* Fallback to global. */
71 orient_slot->index_custom = -1;
72 }
73 }
74}
75
77{
78 return static_cast<TransformOrientation *>(
80}
81
82static void uniqueOrientationName(ListBase *lb, char *name)
83{
85 [&](const StringRefNull check_name) {
86 return findOrientationName(lb, check_name.c_str()) != nullptr;
87 },
89 '.',
90 name,
92}
93
95 ReportList * /*reports*/,
96 const char *name,
97 const bool overwrite)
98{
100 float mat[3][3];
101
102 if (!rv3d) {
103 return nullptr;
104 }
105
106 copy_m3_m4(mat, rv3d->viewinv);
107 normalize_m3(mat);
108
109 if (name[0] == 0) {
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;
114 }
115 else {
116 name = DATA_("Custom View");
117 }
118 }
119
120 return addMatrixSpace(C, mat, name, overwrite);
121}
122
124 ReportList * /*reports*/,
125 const char *name,
126 const bool overwrite)
127{
128 Base *base = CTX_data_active_base(C);
129 Object *ob;
130 float mat[3][3];
131
132 if (base == nullptr) {
133 return nullptr;
134 }
135
136 ob = base->object;
137
138 copy_m3_m4(mat, ob->object_to_world().ptr());
139 normalize_m3(mat);
140
141 /* Use object name if no name is given. */
142 if (name[0] == 0) {
143 name = ob->id.name + 2;
144 }
145
146 return addMatrixSpace(C, mat, name, overwrite);
147}
148
151 const char *name,
152 const bool overwrite)
153{
154 float mat[3][3];
155 float normal[3], plane[3];
156
157 getTransformOrientation(C, normal, plane);
158
159 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
160 BKE_reports_prepend(reports, "Cannot use zero-length bone");
161 return nullptr;
162 }
163
164 if (name[0] == 0) {
165 name = DATA_("Bone");
166 }
167
168 return addMatrixSpace(C, mat, name, overwrite);
169}
170
173 const char *name,
174 const bool overwrite)
175{
176 float mat[3][3];
177 float normal[3], plane[3];
178
179 getTransformOrientation(C, normal, plane);
180
181 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
182 BKE_reports_prepend(reports, "Cannot use zero-length curve");
183 return nullptr;
184 }
185
186 if (name[0] == 0) {
187 name = DATA_("Curve");
188 }
189
190 return addMatrixSpace(C, mat, name, overwrite);
191}
192
195 const char *name,
196 const bool overwrite)
197{
198 float mat[3][3];
199 float normal[3], plane[3];
200 int type;
201
202 type = getTransformOrientation(C, normal, plane);
203
204 switch (type) {
205 case ORIENTATION_VERT:
206 if (createSpaceNormal(mat, normal) == 0) {
207 BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal");
208 return nullptr;
209 }
210
211 if (name[0] == 0) {
212 name = DATA_("Vertex");
213 }
214 break;
215 case ORIENTATION_EDGE:
216 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
217 BKE_reports_prepend(reports, "Cannot use zero-length edge");
218 return nullptr;
219 }
220
221 if (name[0] == 0) {
222 name = DATA_("Edge");
223 }
224 break;
225 case ORIENTATION_FACE:
226 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
227 BKE_reports_prepend(reports, "Cannot use zero-area face");
228 return nullptr;
229 }
230
231 if (name[0] == 0) {
232 name = DATA_("Face");
233 }
234 break;
235 default:
236 return nullptr;
237 }
238
239 return addMatrixSpace(C, mat, name, overwrite);
240}
241
242static bool test_rotmode_euler(short rotmode)
243{
244 return ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT) ? false : true;
245}
246
250static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
251{
252 /* X/Y are arbitrary axes, most importantly Z is the axis of rotation. */
253
254 float cross_vec[3];
255 float quat[4];
256
257 /* This is an un-scientific method to get a vector to cross with XYZ intentionally YZX. */
258 cross_vec[0] = axis[1];
259 cross_vec[1] = axis[2];
260 cross_vec[2] = axis[0];
261
262 /* X-axis. */
263 cross_v3_v3v3(gmat[0], cross_vec, axis);
264 normalize_v3(gmat[0]);
265 axis_angle_to_quat(quat, axis, angle);
266 mul_qt_v3(quat, gmat[0]);
267
268 /* Y-axis. */
269 axis_angle_to_quat(quat, axis, M_PI_2);
270 copy_v3_v3(gmat[1], gmat[0]);
271 mul_qt_v3(quat, gmat[1]);
272
273 /* Z-axis. */
274 copy_v3_v3(gmat[2], axis);
275
276 normalize_m3(gmat);
277}
278
279bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
280{
281 float mat[3][3], tmat[3][3], obmat[3][3];
282 if (test_rotmode_euler(pchan->rotmode)) {
283 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
284 }
285 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
286 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
287 }
288 else { /* Quaternion. */
289 return false;
290 }
291
292 /* Apply bone transformation. */
293 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
294
295 if (pchan->parent) {
296 float parent_mat[3][3];
297
298 copy_m3_m4(parent_mat,
299 (pchan->bone->flag & BONE_HINGE) ? pchan->parent->bone->arm_mat :
300 pchan->parent->pose_mat);
301 mul_m3_m3m3(mat, parent_mat, tmat);
302
303 /* Needed if object transformation isn't identity. */
304 copy_m3_m4(obmat, ob->object_to_world().ptr());
305 mul_m3_m3m3(gmat, obmat, mat);
306 }
307 else {
308 /* Needed if object transformation isn't identity. */
309 copy_m3_m4(obmat, ob->object_to_world().ptr());
310 mul_m3_m3m3(gmat, obmat, tmat);
311 }
312
313 normalize_m3(gmat);
314 return true;
315}
316
317bool gimbal_axis_object(Object *ob, float gmat[3][3])
318{
319 if (test_rotmode_euler(ob->rotmode)) {
320 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
321 }
322 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
324 }
325 else { /* Quaternion. */
326 return false;
327 }
328
329 if (ob->parent) {
330 float parent_mat[3][3];
331 copy_m3_m4(parent_mat, ob->parent->object_to_world().ptr());
332 normalize_m3(parent_mat);
333 mul_m3_m3m3(gmat, parent_mat, gmat);
334 }
335 return true;
336}
337
339 const float x[3],
340 const float y[3],
341 const float z[3])
342{
343 bool is_zero[3] = {true, true, true};
344 zero_m3(mat);
345 if (x) {
346 is_zero[0] = normalize_v3_v3(mat[0], x) == 0.0f;
347 }
348 if (y) {
349 is_zero[1] = normalize_v3_v3(mat[1], y) == 0.0f;
350 }
351 if (z) {
352 is_zero[2] = normalize_v3_v3(mat[2], z) == 0.0f;
353 }
354
355 int zero_axis = is_zero[0] + is_zero[1] + is_zero[2];
356 if (zero_axis == 0) {
357 return true;
358 }
359
360 if (zero_axis == 1) {
361 int axis = is_zero[0] ? 0 : is_zero[1] ? 1 : 2;
362 cross_v3_v3v3(mat[axis], mat[(axis + 1) % 3], mat[(axis + 2) % 3]);
363 if (normalize_v3(mat[axis]) != 0.0f) {
364 return true;
365 }
366 }
367 else if (zero_axis == 2) {
368 int axis, a, b;
369 axis = !is_zero[0] ? 0 : !is_zero[1] ? 1 : 2;
370 a = (axis + 1) % 3;
371 b = (axis + 2) % 3;
372
373 mat[a][a] = 1.0f;
374 mat[b][b] = 1.0f;
375 project_plane_v3_v3v3(mat[a], mat[a], mat[axis]);
376 project_plane_v3_v3v3(mat[b], mat[b], mat[axis]);
377 if ((normalize_v3(mat[a]) != 0.0f) && (normalize_v3(mat[b]) != 0.0f)) {
378 return true;
379 }
380 }
381
382 unit_m3(mat);
383 return false;
384}
385
386bool createSpaceNormal(float mat[3][3], const float normal[3])
387{
388 float tangent[3] = {0.0f, 0.0f, 1.0f};
389
390 copy_v3_v3(mat[2], normal);
391 if (normalize_v3(mat[2]) == 0.0f) {
392 return false; /* Error return. */
393 }
394
395 cross_v3_v3v3(mat[0], mat[2], tangent);
396 if (is_zero_v3(mat[0])) {
397 tangent[0] = 1.0f;
398 tangent[1] = tangent[2] = 0.0f;
399 cross_v3_v3v3(mat[0], tangent, mat[2]);
400 }
401
402 cross_v3_v3v3(mat[1], mat[2], mat[0]);
403
404 normalize_m3(mat);
405
406 return true;
407}
408
409bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
410{
411 BLI_ASSERT_UNIT_V3(normal);
412 BLI_ASSERT_UNIT_V3(tangent);
413
414 if (UNLIKELY(is_zero_v3(normal))) {
415 /* Error return. */
416 return false;
417 }
418 copy_v3_v3(mat[2], normal);
419
420 /* Negate so we can use values from the matrix as input. */
421 negate_v3_v3(mat[1], tangent);
422
423 /* Preempt zero length tangent from causing trouble. */
424 if (UNLIKELY(is_zero_v3(mat[1]))) {
425 mat[1][2] = 1.0f;
426 }
427
428 cross_v3_v3v3(mat[0], mat[2], mat[1]);
429 if (UNLIKELY(normalize_v3(mat[0]) == 0.0f)) {
430 /* Error return from co-linear normal & tangent. */
431 return false;
432 }
433
434 /* Make the tangent orthogonal. */
435 cross_v3_v3v3(mat[1], mat[2], mat[0]);
436
437 if (UNLIKELY(normalize_v3(mat[1]) == 0.0f)) {
438 /* Error return as it's possible making the tangent orthogonal to the normal
439 * causes it to be zero length. */
440 return false;
441 }
442
443 /* Final matrix must be normalized, do inline. */
444 // normalize_m3(mat);
445
446 return true;
447}
448
450 const float normal[3],
451 const float tangent[3])
452{
453 if (createSpaceNormalTangent(mat, normal, tangent)) {
454 return;
455 }
456 if (!is_zero_v3(normal)) {
457 axis_dominant_v3_to_m3(mat, normal);
458 invert_m3(mat);
459 return;
460 }
461 /* Last resort. */
462 unit_m3(mat);
463}
464
467 const char *name,
468 const bool use_view,
469 const bool activate,
470 const bool overwrite)
471{
472 TransformOrientation *ts = nullptr;
473
474 if (use_view) {
475 ts = createViewSpace(C, reports, name, overwrite);
476 }
477 else {
478 Object *obedit = CTX_data_edit_object(C);
480 if (obedit) {
481 if (obedit->type == OB_MESH) {
482 ts = createMeshSpace(C, reports, name, overwrite);
483 }
484 else if (obedit->type == OB_ARMATURE) {
485 ts = createBoneSpace(C, reports, name, overwrite);
486 }
487 else if (obedit->type == OB_CURVES_LEGACY) {
488 ts = createCurveSpace(C, reports, name, overwrite);
489 }
490 }
491 else if (ob && (ob->mode & OB_MODE_POSE)) {
492 ts = createBoneSpace(C, reports, name, overwrite);
493 }
494 else {
495 ts = createObjectSpace(C, reports, name, overwrite);
496 }
497 }
498
499 if (activate && ts != nullptr) {
501 }
502 return (ts != nullptr);
503}
504
506 float mat[3][3],
507 const char *name,
508 const bool overwrite)
509{
510 TransformOrientation *ts = nullptr;
511 Scene *scene = CTX_data_scene(C);
512 ListBase *transform_orientations = &scene->transform_spaces;
513 char name_unique[sizeof(ts->name)];
514
515 if (overwrite) {
516 ts = findOrientationName(transform_orientations, name);
517 }
518 else {
519 STRNCPY(name_unique, name);
520 uniqueOrientationName(transform_orientations, name_unique);
521 name = name_unique;
522 }
523
524 /* If not, create a new one. */
525 if (ts == nullptr) {
526 ts = MEM_callocN<TransformOrientation>("UserTransSpace from matrix");
527 BLI_addtail(transform_orientations, ts);
528 STRNCPY(ts->name, name);
529 }
530
531 /* Copy matrix into transform space. */
532 copy_m3_m3(ts->mat, mat);
533
534 return ts;
535}
536
541
547
558
560{
561 Scene *scene = CTX_data_scene(C);
562 ListBase *transform_orientations = &scene->transform_spaces;
563 return BLI_listbase_count(transform_orientations);
564}
565
566void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
567{
568 if (r_name) {
569 BLI_strncpy(r_name, ts->name, MAX_NAME);
570 }
571 copy_m3_m3(r_mat, ts->mat);
572}
573
574/* Updates all `BONE_TRANSFORM` flags.
575 * Returns total number of bones with `BONE_TRANSFORM`.
576 * NOTE: `transform_convert_pose_transflags_update` has a similar logic. */
578 ListBase *lb,
579 const bool do_it)
580{
581 bool do_next;
582 int total = 0;
583
584 LISTBASE_FOREACH (Bone *, bone, lb) {
585 bone->flag &= ~BONE_TRANSFORM;
586 do_next = do_it;
587 if (do_it) {
588 if (ANIM_bone_in_visible_collection(arm, bone)) {
589 if (bone->flag & BONE_SELECTED) {
590 bone->flag |= BONE_TRANSFORM;
591 total++;
592
593 /* No transform on children if one parent bone is selected. */
594 do_next = false;
595 }
596 }
597 }
598 total += armature_bone_transflags_update_recursive(arm, &bone->childbase, do_next);
599 }
600
601 return total;
602}
603
604void calc_orientation_from_type(const bContext *C, float r_mat[3][3])
605{
606 ARegion *region = CTX_wm_region(C);
607 Scene *scene = CTX_data_scene(C);
608 ViewLayer *view_layer = CTX_data_view_layer(C);
609 Object *obedit = CTX_data_edit_object(C);
610 View3D *v3d = CTX_wm_view3d(C);
611 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
612 BKE_view_layer_synced_ensure(scene, view_layer);
614 const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
615 const int pivot_point = scene->toolsettings->transform_pivot_point;
616
618 scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, r_mat);
619}
620
621static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
622{
623 bPoseChannel *active_pchan = BKE_pose_channel_active(ob, false);
624
625 /* Check if target bone is a child. */
626 if (active_pchan && active_pchan->parent) {
627 /* For child, show parent local regardless if "local location" is set for parent bone. */
629 float ob_orientations_mat[3][3];
630 transform_orientations_create_from_axis(ob_orientations_mat,
631 UNPACK3(ob->object_to_world().ptr()));
632 mul_m3_m3_pre(r_mat, ob_orientations_mat);
633 return;
634 }
635
636 /* For root, use local transform of armature object. */
637 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
638}
639
640static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
641{
642 /* If object has parent, then orient to parent. */
643 if (ob->parent) {
644 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->parent->object_to_world().ptr()));
645 }
646 else {
647 /* If object doesn't have parent, then orient to world. */
648 unit_m3(r_mat);
649 }
650}
651
653 ViewLayer *view_layer,
654 const View3D *v3d,
655 const RegionView3D *rv3d,
656 Object *ob,
657 Object *obedit,
658 const short orientation_index,
659 const int pivot_point,
660 float r_mat[3][3])
661{
662 switch (orientation_index) {
663 case V3D_ORIENT_GIMBAL: {
664
665 if (ob) {
666 if (ob->mode & OB_MODE_POSE) {
668 if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) {
669 break;
670 }
671 }
672 else {
673 if (gimbal_axis_object(ob, r_mat)) {
674 break;
675 }
676 }
677 }
678 /* If not gimbal, fall through to normal. */
680 }
681 case V3D_ORIENT_PARENT: {
682 if (ob) {
683 if (ob->mode & OB_MODE_POSE) {
685 break;
686 }
688 break;
689 }
690 /* No break; we define 'parent' as 'normal' otherwise. */
692 }
693 case V3D_ORIENT_NORMAL: {
694 if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
695 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
696 break;
697 }
698 /* No break we define 'normal' as 'local' in Object mode. */
700 }
701 case V3D_ORIENT_LOCAL: {
702 if (ob) {
703 if (ob->mode & OB_MODE_POSE) {
704 /* Each bone moves on its own local axis, but to avoid confusion,
705 * use the active bone's axis for display #33575, this works as expected on a single
706 * bone and users who select many bones will understand what's going on and what local
707 * means when they start transforming. */
708 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
709 }
710 else {
711 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
712 }
713 break;
714 }
715 /* If not local, fall through to global. */
717 }
718 case V3D_ORIENT_GLOBAL: {
719 unit_m3(r_mat);
720 break;
721 }
722 case V3D_ORIENT_VIEW: {
723 if (rv3d != nullptr) {
724 copy_m3_m4(r_mat, rv3d->viewinv);
725 normalize_m3(r_mat);
726 }
727 else {
728 unit_m3(r_mat);
729 }
730 break;
731 }
732 case V3D_ORIENT_CURSOR: {
733 copy_m3_m3(r_mat, scene->cursor.matrix<float3x3>().ptr());
734 break;
735 }
737 /* Do nothing. */;
738 break;
739 }
741 default: {
742 BLI_assert(orientation_index >= V3D_ORIENT_CUSTOM);
743 int orientation_index_custom = orientation_index - V3D_ORIENT_CUSTOM;
745 scene, orientation_index_custom);
746 applyTransformOrientation(custom_orientation, r_mat, nullptr);
747 break;
748 }
749 }
750
751 return orientation_index;
752}
753
755 TransInfo *t,
756 short orient_index,
757 const float custom[3][3],
758 float r_spacemtx[3][3])
759{
760 if (orient_index == V3D_ORIENT_CUSTOM_MATRIX) {
761 copy_m3_m3(r_spacemtx, custom);
763 }
764
766 Scene *scene = t->scene;
767 Strip *strip = seq::select_active_get(scene);
768 if (strip && strip->data->transform && orient_index == V3D_ORIENT_LOCAL) {
771 r_spacemtx, 'Z', strip->data->transform->rotation * mirror[0] * mirror[1]);
772 return orient_index;
773 }
774 }
775
777 Object *obedit = CTX_data_edit_object(C);
778 Scene *scene = t->scene;
779 View3D *v3d = nullptr;
780 RegionView3D *rv3d = nullptr;
781
782 if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
783 v3d = static_cast<View3D *>(t->view);
784 rv3d = static_cast<RegionView3D *>(t->region->regiondata);
785
786 if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
788 if (ob_armature) {
789 /* The armature matrix is used for GIMBAL, NORMAL and LOCAL orientations. */
790 ob = ob_armature;
791 }
792 }
793 }
794
795 short r_orient_index = calc_orientation_from_type_ex(
796 scene, t->view_layer, v3d, rv3d, ob, obedit, orient_index, t->around, r_spacemtx);
797
798 if (rv3d && (t->options & CTX_PAINT_CURVE)) {
799 /* Screen space in the 3d region. */
800 if (r_orient_index == V3D_ORIENT_VIEW) {
801 unit_m3(r_spacemtx);
802 }
803 else {
804 mul_m3_m4m3(r_spacemtx, rv3d->viewmat, r_spacemtx);
805 normalize_m3(r_spacemtx);
806 }
807 }
808
809 return r_orient_index;
810}
811
812const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)
813{
814 switch (orient_type) {
816 return RPT_("global");
818 return RPT_("gimbal");
820 return RPT_("normal");
821 case V3D_ORIENT_LOCAL:
822 return RPT_("local");
823 case V3D_ORIENT_VIEW:
824 return RPT_("view");
826 return RPT_("cursor");
828 return RPT_("parent");
830 return RPT_("custom");
832 default:
833 BLI_assert(orient_type >= V3D_ORIENT_CUSTOM);
835 t->scene, orient_type - V3D_ORIENT_CUSTOM);
836 return ts->name;
837 }
838}
839
840void transform_orientations_current_set(TransInfo *t, const short orient_index)
841{
842 const short orientation = t->orient[orient_index].type;
843 const char *spacename = transform_orientations_spacename_get(t, orientation);
844
845 STRNCPY(t->spacename, spacename);
846 copy_m3_m3(t->spacemtx, t->orient[orient_index].matrix);
848 t->orient_curr = eTOType(orient_index);
849}
850
855 BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
856{
857 BMIter iter;
858 BMElem *ele;
859 uint i;
860
863
864 if (!BLI_listbase_is_empty(&bm->selected)) {
865 /* Quick check. */
866 i = 0;
867 LISTBASE_FOREACH_BACKWARD (BMEditSelection *, ese, &bm->selected) {
868 /* Shouldn't need this check. */
869 if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
870
871 /* Only use contiguous selection. */
872 if (ese->htype != htype) {
873 i = 0;
874 break;
875 }
876
877 elems[i++] = ese->ele;
878 if (n == i) {
879 break;
880 }
881 }
882 else {
883 BLI_assert(0);
884 }
885 }
886
887 if (i == 0) {
888 /* Pass. */
889 }
890 else if (i == n) {
891 return i;
892 }
893 }
894
895 i = 0;
896 BM_ITER_MESH (ele, &iter, bm, itype) {
897 BLI_assert(ele->head.htype == htype);
899 elems[i++] = ele;
900 if (n == i) {
901 break;
902 }
903 }
904 }
905
906 return i;
907}
908
910{
912 bm, (BMElem **)elems, min_ii(n, bm->totvertsel), BM_VERTS_OF_MESH, BM_VERT);
913}
915{
917 bm, (BMElem **)elems, min_ii(n, bm->totedgesel), BM_EDGES_OF_MESH, BM_EDGE);
918}
919#if 0
920static uint bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const uint n)
921{
923 bm, (BMElem **)elems, min_ii(n, bm->totfacesel), BM_FACES_OF_MESH, BM_FACE);
924}
925#endif
926
928 ViewLayer *view_layer,
929 const View3D *v3d,
930 Object *ob,
931 Object *obedit,
932 const short around,
933 float r_normal[3],
934 float r_plane[3])
935{
937 const bool activeOnly = (around == V3D_AROUND_ACTIVE);
938
939 zero_v3(r_normal);
940 zero_v3(r_plane);
941
942 if (obedit) {
943 float imat[3][3], mat[3][3];
944
945 /* We need the transpose of the inverse for a normal... */
946 copy_m3_m4(imat, ob->object_to_world().ptr());
947
948 invert_m3_m3(mat, imat);
949 transpose_m3(mat);
950
951 ob = obedit;
952
953 if (ob->type == OB_MESH) {
955 BMEditSelection ese;
956
957 /* Use last selected with active. */
958 if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
959 BM_editselection_normal(&ese, r_normal);
960 BM_editselection_plane(&ese, r_plane);
961
962 switch (ese.htype) {
963 case BM_VERT:
965 break;
966 case BM_EDGE:
968 break;
969 case BM_FACE:
971 break;
972 }
973 }
974 else {
975 if (em->bm->totfacesel >= 1) {
976 BMFace *efa;
977 BMIter iter;
978
979 float normal[3] = {0.0f};
980 float plane_pair[2][3] = {{0.0f}};
981 int face_count = 0;
982
983 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
985 float tangent_pair[2][3];
986 BM_face_calc_tangent_pair_auto(efa, tangent_pair[0], tangent_pair[1]);
987 add_v3_v3(normal, efa->no);
988 add_v3_v3(plane_pair[0], tangent_pair[0]);
989 add_v3_v3(plane_pair[1], tangent_pair[1]);
990 face_count++;
991 }
992 }
993
994 /* Pick the best plane (least likely to be co-linear),
995 * since this can result in failure to construct a usable matrix, see: #96535. */
996 int plane_index;
997 if (face_count == 1) {
998 /* Special case so a single face always matches
999 * the active-element orientation, see: #134948. */
1000 plane_index = 0;
1001 }
1002 else {
1003 float normal_unit[3];
1004 float plane_unit_pair[2][3], plane_ortho_pair[2][3];
1005
1006 normalize_v3_v3(normal_unit, normal);
1007 normalize_v3_v3(plane_unit_pair[0], plane_pair[0]);
1008 normalize_v3_v3(plane_unit_pair[1], plane_pair[1]);
1009
1010 cross_v3_v3v3(plane_ortho_pair[0], normal_unit, plane_unit_pair[0]);
1011 cross_v3_v3v3(plane_ortho_pair[1], normal_unit, plane_unit_pair[1]);
1012
1013 plane_index = (len_squared_v3(plane_ortho_pair[0]) >
1014 len_squared_v3(plane_ortho_pair[1])) ?
1015 0 :
1016 1;
1017 }
1018
1019 add_v3_v3(r_normal, normal);
1020 add_v3_v3(r_plane, plane_pair[plane_index]);
1021
1023 }
1024 else if (em->bm->totvertsel == 3) {
1025 BMVert *v_tri[3];
1026
1027 if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) {
1028 BMEdge *e = nullptr;
1029 float no_test[3];
1030
1031 normal_tri_v3(r_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
1032
1033 /* Check if the normal is pointing opposite to vert normals. */
1034 no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0];
1035 no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1];
1036 no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2];
1037 if (dot_v3v3(no_test, r_normal) < 0.0f) {
1038 negate_v3(r_normal);
1039 }
1040
1041 if (em->bm->totedgesel >= 1) {
1042 /* Find an edge that's a part of v_tri (no need to search all edges). */
1043 float e_length;
1044 int j;
1045
1046 for (j = 0; j < 3; j++) {
1047 BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]);
1048 if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) {
1049 const float e_test_length = BM_edge_calc_length_squared(e_test);
1050 if ((e == nullptr) || (e_length < e_test_length)) {
1051 e = e_test;
1052 e_length = e_test_length;
1053 }
1054 }
1055 }
1056 }
1057
1058 if (e) {
1059 BMVert *v_pair[2];
1060 if (BM_edge_is_boundary(e)) {
1061 BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]);
1062 }
1063 else {
1064 v_pair[0] = e->v1;
1065 v_pair[1] = e->v2;
1066 }
1067 sub_v3_v3v3(r_plane, v_pair[0]->co, v_pair[1]->co);
1068 }
1069 else {
1070 BM_vert_tri_calc_tangent_from_edge(v_tri, r_plane);
1071 }
1072 }
1073 else {
1074 BLI_assert(0);
1075 }
1076
1078 }
1079 else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) {
1080 BMVert *v_pair[2] = {nullptr, nullptr};
1081 BMEdge *eed = nullptr;
1082
1083 if (em->bm->totedgesel == 1) {
1084 if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) {
1085 v_pair[0] = eed->v1;
1086 v_pair[1] = eed->v2;
1087 }
1088 }
1089 else {
1090 BLI_assert(em->bm->totvertsel == 2);
1091 bm_mesh_verts_select_get_n(em->bm, v_pair, 2);
1092 }
1093
1094 /* Should never fail. */
1095 if (LIKELY(v_pair[0] && v_pair[1])) {
1096 bool v_pair_swap = false;
1107
1108 /* Be deterministic where possible and ensure `v_pair[0]` is active. */
1109 if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) {
1110 v_pair_swap = true;
1111 }
1112 else if (eed && BM_edge_is_boundary(eed)) {
1113 /* Predictable direction for boundary edges. */
1114 if (eed->l->v != v_pair[0]) {
1115 v_pair_swap = true;
1116 }
1117 }
1118
1119 if (v_pair_swap) {
1120 std::swap(v_pair[0], v_pair[1]);
1121 }
1122
1123 add_v3_v3v3(r_normal, v_pair[1]->no, v_pair[0]->no);
1124 sub_v3_v3v3(r_plane, v_pair[1]->co, v_pair[0]->co);
1125
1126 if (normalize_v3(r_plane) != 0.0f) {
1127 /* For edges it'd important the resulting matrix can rotate around the edge,
1128 * project onto the plane so we can use a fallback value. */
1129 project_plane_normalized_v3_v3v3(r_normal, r_normal, r_plane);
1130 if (UNLIKELY(normalize_v3(r_normal) == 0.0f)) {
1131 /* In the case the normal and plane are aligned,
1132 * use a fallback normal which is orthogonal to the plane. */
1133 ortho_v3_v3(r_normal, r_plane);
1134 }
1135 }
1136 }
1137
1139 }
1140 else if (em->bm->totvertsel == 1) {
1141 BMVert *v = nullptr;
1142
1143 if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) {
1144 copy_v3_v3(r_normal, v->no);
1145 BMEdge *e_pair[2];
1146
1147 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
1148 bool v_pair_swap = false;
1149 BMVert *v_pair[2] = {
1150 BM_edge_other_vert(e_pair[0], v),
1151 BM_edge_other_vert(e_pair[1], v),
1152 };
1153 float dir_pair[2][3];
1154
1155 if (BM_edge_is_boundary(e_pair[0])) {
1156 if (e_pair[0]->l->v != v) {
1157 v_pair_swap = true;
1158 }
1159 }
1160 else {
1161 if (BM_edge_calc_length_squared(e_pair[0]) <
1162 BM_edge_calc_length_squared(e_pair[1]))
1163 {
1164 v_pair_swap = true;
1165 }
1166 }
1167
1168 if (v_pair_swap) {
1169 std::swap(v_pair[0], v_pair[1]);
1170 }
1171
1172 sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co);
1173 sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co);
1174 normalize_v3(dir_pair[0]);
1175 normalize_v3(dir_pair[1]);
1176
1177 add_v3_v3v3(r_plane, dir_pair[0], dir_pair[1]);
1178 }
1179 }
1180
1182 }
1183 else if (em->bm->totvertsel > 3) {
1184 BMIter iter;
1185 BMVert *v;
1186
1187 zero_v3(r_normal);
1188
1189 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1191 add_v3_v3(r_normal, v->no);
1192 }
1193 }
1194 normalize_v3(r_normal);
1196 }
1197 }
1198
1199 /* Not needed but this matches 2.68 and older behavior. */
1200 negate_v3(r_plane);
1201
1202 } /* End edit-mesh. */
1203 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
1204 Curve *cu = static_cast<Curve *>(obedit->data);
1205 Nurb *nu = nullptr;
1206 int a;
1207 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1208
1209 void *vert_act = nullptr;
1210 if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) {
1211 if (nu->type == CU_BEZIER) {
1212 BezTriple *bezt = static_cast<BezTriple *>(vert_act);
1213 BKE_nurb_bezt_calc_normal(nu, bezt, r_normal);
1214 BKE_nurb_bezt_calc_plane(nu, bezt, r_plane);
1215 }
1216 else {
1217 BPoint *bp = static_cast<BPoint *>(vert_act);
1218 BKE_nurb_bpoint_calc_normal(nu, bp, r_normal);
1219 BKE_nurb_bpoint_calc_plane(nu, bp, r_plane);
1220 }
1221 }
1222 else {
1223 const bool use_handle = v3d ? (v3d->overlay.handle_display != CURVE_HANDLE_NONE) : true;
1224
1225 for (nu = static_cast<Nurb *>(nurbs->first); nu; nu = nu->next) {
1226 /* Only bezier has a normal. */
1227 if (nu->type == CU_BEZIER) {
1228 BezTriple *bezt = nu->bezt;
1229 a = nu->pntsu;
1230 while (a--) {
1231 short flag = 0;
1232
1233#define SEL_F1 (1 << 0)
1234#define SEL_F2 (1 << 1)
1235#define SEL_F3 (1 << 2)
1236
1237 if (use_handle) {
1238 if (bezt->f1 & SELECT) {
1239 flag |= SEL_F1;
1240 }
1241 if (bezt->f2 & SELECT) {
1242 flag |= SEL_F2;
1243 }
1244 if (bezt->f3 & SELECT) {
1245 flag |= SEL_F3;
1246 }
1247 }
1248 else {
1249 flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0;
1250 }
1251
1252 /* Exception. */
1253 if (flag) {
1254 float tvec[3];
1255 if ((around == V3D_AROUND_LOCAL_ORIGINS) ||
1257 {
1258 BKE_nurb_bezt_calc_normal(nu, bezt, tvec);
1259 add_v3_v3(r_normal, tvec);
1260 }
1261 else {
1262 /* Ignore `bezt->f2` in this case. */
1263 if (flag & SEL_F1) {
1264 sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]);
1265 normalize_v3(tvec);
1266 add_v3_v3(r_normal, tvec);
1267 }
1268 if (flag & SEL_F3) {
1269 sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]);
1270 normalize_v3(tvec);
1271 add_v3_v3(r_normal, tvec);
1272 }
1273 }
1274
1275 BKE_nurb_bezt_calc_plane(nu, bezt, tvec);
1276 add_v3_v3(r_plane, tvec);
1277 }
1278
1279#undef SEL_F1
1280#undef SEL_F2
1281#undef SEL_F3
1282
1283 bezt++;
1284 }
1285 }
1286 else if (nu->bp && (nu->pntsv == 1)) {
1287 BPoint *bp = nu->bp;
1288 a = nu->pntsu;
1289 while (a--) {
1290 if (bp->f1 & SELECT) {
1291 float tvec[3];
1292
1293 BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp);
1294 BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp);
1295
1296 const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT);
1297 const bool is_next_sel = bp_next && (bp_next->f1 & SELECT);
1298 if (is_prev_sel == false && is_next_sel == false) {
1299 /* Isolated, add based on surrounding. */
1300 BKE_nurb_bpoint_calc_normal(nu, bp, tvec);
1301 add_v3_v3(r_normal, tvec);
1302 }
1303 else if (is_next_sel) {
1304 /* A segment, add the edge normal. */
1305 sub_v3_v3v3(tvec, bp->vec, bp_next->vec);
1306 normalize_v3(tvec);
1307 add_v3_v3(r_normal, tvec);
1308 }
1309
1310 BKE_nurb_bpoint_calc_plane(nu, bp, tvec);
1311 add_v3_v3(r_plane, tvec);
1312 }
1313 bp++;
1314 }
1315 }
1316 }
1317 }
1318
1319 if (!is_zero_v3(r_normal)) {
1321 }
1322 }
1323 else if (obedit->type == OB_MBALL) {
1324 MetaBall *mb = static_cast<MetaBall *>(obedit->data);
1325 MetaElem *ml;
1326 bool ok = false;
1327 float tmat[3][3];
1328
1329 if (activeOnly && (ml = mb->lastelem)) {
1330 quat_to_mat3(tmat, ml->quat);
1331 add_v3_v3(r_normal, tmat[2]);
1332 add_v3_v3(r_plane, tmat[1]);
1333 ok = true;
1334 }
1335 else {
1336 LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
1337 if (ml->flag & SELECT) {
1338 quat_to_mat3(tmat, ml->quat);
1339 add_v3_v3(r_normal, tmat[2]);
1340 add_v3_v3(r_plane, tmat[1]);
1341 ok = true;
1342 }
1343 }
1344 }
1345
1346 if (ok) {
1347 if (!is_zero_v3(r_plane)) {
1349 }
1350 }
1351 }
1352 else if (obedit->type == OB_ARMATURE) {
1353 bArmature *arm = static_cast<bArmature *>(obedit->data);
1354 EditBone *ebone;
1355 bool ok = false;
1356 float tmat[3][3];
1357
1358 if (activeOnly && (ebone = arm->act_edbone)) {
1359 ED_armature_ebone_to_mat3(ebone, tmat);
1360 add_v3_v3(r_normal, tmat[2]);
1361 add_v3_v3(r_plane, tmat[1]);
1362 ok = true;
1363 }
1364 else {
1365 /* When we only have the root/tip are selected. */
1366 bool fallback_ok = false;
1367 float fallback_normal[3];
1368 float fallback_plane[3];
1369
1370 zero_v3(fallback_normal);
1371 zero_v3(fallback_plane);
1372
1373 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1375 if (ebone->flag & BONE_SELECTED) {
1376 ED_armature_ebone_to_mat3(ebone, tmat);
1377 add_v3_v3(r_normal, tmat[2]);
1378 add_v3_v3(r_plane, tmat[1]);
1379 ok = true;
1380 }
1381 else if ((ok == false) && ((ebone->flag & BONE_TIPSEL) ||
1382 ((ebone->flag & BONE_ROOTSEL) &&
1383 (ebone->parent && ebone->flag & BONE_CONNECTED) == false)))
1384 {
1385 ED_armature_ebone_to_mat3(ebone, tmat);
1386 add_v3_v3(fallback_normal, tmat[2]);
1387 add_v3_v3(fallback_plane, tmat[1]);
1388 fallback_ok = true;
1389 }
1390 }
1391 }
1392 if ((ok == false) && fallback_ok) {
1393 ok = true;
1394 copy_v3_v3(r_normal, fallback_normal);
1395 copy_v3_v3(r_plane, fallback_plane);
1396 }
1397 }
1398
1399 if (ok) {
1400 if (!is_zero_v3(r_plane)) {
1402 }
1403 }
1404 }
1405
1406 /* Vectors from edges don't need the special transpose inverse multiplication. */
1407 if (result == ORIENTATION_EDGE) {
1408 float tvec[3];
1409
1410 mul_mat3_m4_v3(ob->object_to_world().ptr(), r_normal);
1411 mul_mat3_m4_v3(ob->object_to_world().ptr(), r_plane);
1412
1413 /* Align normal to edge direction (so normal is perpendicular to the plane).
1414 * 'ORIENTATION_EDGE' will do the other way around.
1415 * This has to be done **after** applying obmat, see #45775! */
1416 project_v3_v3v3(tvec, r_normal, r_plane);
1417 sub_v3_v3(r_normal, tvec);
1418 }
1419 else {
1420 mul_m3_v3(mat, r_normal);
1421 mul_m3_v3(mat, r_plane);
1422 }
1423 }
1424 else if (ob && (ob->mode & OB_MODE_POSE)) {
1425 bArmature *arm = static_cast<bArmature *>(ob->data);
1426 bPoseChannel *pchan;
1427 float imat[3][3], mat[3][3];
1428 bool ok = false;
1429
1430 if (activeOnly && (pchan = BKE_pose_channel_active_if_bonecoll_visible(ob))) {
1431 add_v3_v3(r_normal, pchan->pose_mat[2]);
1432 add_v3_v3(r_plane, pchan->pose_mat[1]);
1433 ok = true;
1434 }
1435 else {
1436 int transformed_len;
1437 transformed_len = armature_bone_transflags_update_recursive(arm, &arm->bonebase, true);
1438 if (transformed_len) {
1439 /* Use channels to get stats. */
1440 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
1441 if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) {
1442 add_v3_v3(r_normal, pchan->pose_mat[2]);
1443 add_v3_v3(r_plane, pchan->pose_mat[1]);
1444 }
1445 }
1446 ok = true;
1447 }
1448 }
1449
1450 /* Use for both active & all. */
1451 if (ok) {
1452 /* We need the transpose of the inverse for a normal. */
1453 copy_m3_m4(imat, ob->object_to_world().ptr());
1454
1455 invert_m3_m3(mat, imat);
1456 transpose_m3(mat);
1457 mul_m3_v3(mat, r_normal);
1458 mul_m3_v3(mat, r_plane);
1459
1461 }
1462 }
1463 else {
1464 /* We need the one selected object, if its not active. */
1465 if (ob != nullptr) {
1466 bool ok = false;
1467 if (activeOnly || (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
1468 /* Ignore selection state. */
1469 ok = true;
1470 }
1471 else {
1472 BKE_view_layer_synced_ensure(scene, view_layer);
1473 Base *base = BKE_view_layer_base_find(view_layer, ob);
1474 if (UNLIKELY(base == nullptr)) {
1475 /* This is very unlikely, if it happens allow the value to be set since the caller
1476 * may have taken the object from outside this view-layer. */
1477 ok = true;
1478 }
1479 else if (BASE_SELECTED(v3d, base)) {
1480 ok = true;
1481 }
1482 }
1483
1484 if (ok) {
1485 copy_v3_v3(r_normal, ob->object_to_world().ptr()[2]);
1486 copy_v3_v3(r_plane, ob->object_to_world().ptr()[1]);
1487 }
1488 }
1490 }
1491
1492 normalize_v3(r_normal);
1493 normalize_v3(r_plane);
1494
1495 return result;
1496}
1497
1498int getTransformOrientation(const bContext *C, float r_normal[3], float r_plane[3])
1499{
1501 Object *obedit = CTX_data_edit_object(C);
1502
1503 /* Dummy value, not #V3D_AROUND_ACTIVE and not #V3D_AROUND_LOCAL_ORIGINS. */
1504 short around = V3D_AROUND_CENTER_BOUNDS;
1505
1506 const Scene *scene = CTX_data_scene(C);
1507 ViewLayer *view_layer = CTX_data_view_layer(C);
1508 View3D *v3d = CTX_wm_view3d(C);
1509
1511 scene, view_layer, v3d, obact, obedit, around, r_normal, r_plane);
1512}
1513
1515 ViewLayer *view_layer,
1516 const View3D *v3d,
1517 Object *ob,
1518 Object *obedit,
1519 const short around,
1520 float r_orientation_mat[3][3])
1521{
1522 float normal[3] = {0.0, 0.0, 0.0};
1523 float plane[3] = {0.0, 0.0, 0.0};
1524
1525 int type;
1526
1527 type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, around, normal, plane);
1528
1529 /* Fallback, when the plane can't be calculated. */
1530 if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) {
1531 type = ORIENTATION_VERT;
1532 }
1533
1534 switch (type) {
1535 case ORIENTATION_NORMAL:
1536 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1537 type = ORIENTATION_NONE;
1538 }
1539 break;
1540 case ORIENTATION_VERT:
1541 if (createSpaceNormal(r_orientation_mat, normal) == 0) {
1542 type = ORIENTATION_NONE;
1543 }
1544 break;
1545 case ORIENTATION_EDGE:
1546 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1547 type = ORIENTATION_NONE;
1548 }
1549 break;
1550 case ORIENTATION_FACE:
1551 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1552 type = ORIENTATION_NONE;
1553 }
1554 break;
1555 default:
1557 break;
1558 }
1559
1560 if (type == ORIENTATION_NONE) {
1561 unit_m3(r_orientation_mat);
1562 }
1563}
1564
1565} // namespace blender::ed::transform
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_active(Object *ob, bool check_bonecoll)
bPoseChannel * BKE_pose_channel_active_if_bonecoll_visible(Object *ob) ATTR_WARN_UNUSED_RESULT
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_nurb_bpoint_calc_plane(Nurb *nu, BPoint *bp, float r_plane[3])
Definition curve.cc:1086
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:423
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1011
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5035
BPoint * BKE_nurb_bpoint_get_prev(Nurb *nu, BPoint *bp)
Definition curve.cc:989
BPoint * BKE_nurb_bpoint_get_next(Nurb *nu, BPoint *bp)
Definition curve.cc:946
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1063
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1026
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void void BKE_reports_prepend(ReportList *reports, const char *prepend)
Definition report.cc:206
int BKE_scene_transform_orientation_get_index(const Scene *scene, const TransformOrientation *orientation)
Definition scene.cc:3541
void BKE_scene_transform_orientation_remove(Scene *scene, TransformOrientation *orientation)
Definition scene.cc:3516
TransformOrientation * BKE_scene_transform_orientation_find(const Scene *scene, int index)
Definition scene.cc:3536
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2436
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
#define BLI_ASSERT_UNIT_V3(v)
#define M_PI_2
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m3_m3_pre(float R[3][3], const float A[3][3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void zero_m3(float m[3][3])
void normalize_m3(float R[3][3]) ATTR_NONNULL()
void invert_m3_m3_safe_ortho(float inverse[3][3], const float mat[3][3])
void transpose_m3(float R[3][3])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
bool invert_m3(float mat[3][3])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
void mul_m3_m4m3(float R[3][3], const float A[4][4], const float B[3][3])
void eulO_to_gimbal_axis(float gmat[3][3], const float eul[3], short order)
void axis_angle_to_mat3_single(float R[3][3], char axis, float angle)
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void quat_to_mat3(float m[3][3], const float q[4])
void mul_qt_v3(const float q[4], float r[3])
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
void ortho_v3_v3(float out[3], const float v[3])
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
#define RPT_(msgid)
#define CTX_DATA_(context, msgid)
#define BLT_I18NCONTEXT_ID_SCENE
#define DATA_(msgid)
@ ROT_MODE_QUAT
@ ROT_MODE_AXISANGLE
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TRANSFORM
@ BONE_TIPSEL
@ BONE_CONNECTED
@ BONE_HINGE
@ CU_BEZIER
#define OB_MODE_ALL_PAINT
#define OB_MODE_ALL_WEIGHT_PAINT
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_MBALL
@ OB_SURF
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
#define BASE_SELECTED(v3d, base)
@ SCE_ORIENT_DEFAULT
@ RGN_TYPE_WINDOW
@ SPACE_SEQ
@ SPACE_VIEW3D
@ RV3D_CAMOB
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_LOCAL_ORIGINS
@ V3D_ORIENT_NORMAL
@ V3D_ORIENT_CUSTOM
@ V3D_ORIENT_GLOBAL
@ V3D_ORIENT_PARENT
@ V3D_ORIENT_CUSTOM_MATRIX
@ V3D_ORIENT_LOCAL
@ V3D_ORIENT_VIEW
@ V3D_ORIENT_CURSOR
@ V3D_ORIENT_GIMBAL
@ CURVE_HANDLE_NONE
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
ReportList * reports
Definition WM_types.hh:1025
void ED_armature_ebone_to_mat3(EditBone *ebone, float r_mat[3][3])
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
BMIterType
BMesh Iterators.
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
BMesh const char itype
BMVert * BM_mesh_active_vert_get(BMesh *bm)
void BM_editselection_plane(BMEditSelection *ese, float r_plane[3])
void BM_editselection_normal(BMEditSelection *ese, float r_normal[3])
bool BM_select_history_active_get(BMesh *bm, BMEditSelection *ese)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_vert_tri_calc_tangent_from_edge(BMVert *verts[3], float r_tangent[3])
void BM_face_calc_tangent_pair_auto(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
float BM_edge_calc_length_squared(const BMEdge *e)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_edge_pair(const BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void activate(bool forceActivation=false) const
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
constexpr const char * c_str() const
#define SEL_F1
Definition curve.cc:4015
#define SEL_F2
Definition curve.cc:4016
#define SEL_F3
Definition curve.cc:4017
#define SELECT
#define offsetof(t, d)
#define MAX_NAME
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline bool is_zero(const float2 a)
bool bone_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
void createSpaceNormalTangent_or_fallback(float mat[3][3], const float normal[3], const float tangent[3])
static bool test_rotmode_euler(short rotmode)
TransformOrientation * addMatrixSpace(bContext *C, float mat[3][3], const char *name, const bool overwrite)
static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
int getTransformOrientation(const bContext *C, float r_normal[3], float r_plane[3])
static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
bool BIF_createTransformOrientation(bContext *C, ReportList *reports, const char *name, bool use_view, bool activate, bool overwrite)
static TransformOrientation * createViewSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
static TransformOrientation * createMeshSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
int getTransformOrientation_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, const short around, float r_normal[3], float r_plane[3])
static int armature_bone_transflags_update_recursive(bArmature *arm, ListBase *lb, const bool do_it)
void BIF_clearTransformOrientation(bContext *C)
static TransformOrientation * createObjectSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
void BIF_removeTransformOrientationIndex(bContext *C, int index)
Object * transform_object_deform_pose_armature_get(const TransInfo *t, Object *ob)
bool createSpaceNormal(float mat[3][3], const float normal[3])
bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
static uint bm_mesh_elems_select_get_n__internal(BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
void calc_orientation_from_type(const bContext *C, float r_mat[3][3])
int BIF_countTransformOrientation(const bContext *C)
void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target)
bool transform_orientations_create_from_axis(float mat[3][3], const float x[3], const float y[3], const float z[3])
short transform_orientation_matrix_get(bContext *C, TransInfo *t, short orient_index, const float custom[3][3], float r_spacemtx[3][3])
static uint bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const uint n)
static TransformOrientation * createCurveSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
const char * transform_orientations_spacename_get(TransInfo *t, const short orient_type)
static TransformOrientation * createBoneSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
bool gimbal_axis_object(Object *ob, float gmat[3][3])
short calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, short orientation_index, int pivot_point, float r_mat[3][3])
static uint bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const uint n)
static TransformOrientation * findOrientationName(ListBase *lb, const char *name)
void ED_getTransformOrientationMatrix(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, short around, float r_orientation_mat[3][3])
static void uniqueOrientationName(ListBase *lb, char *name)
void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target)
void transform_orientations_current_set(TransInfo *t, const short orient_index)
float2 image_transform_mirror_factor_get(const Strip *strip)
Strip * select_active_get(const Scene *scene)
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
void * regiondata
BMVert * v1
BMVert * v2
struct BMLoop * l
BMHeader head
float no[3]
struct BMVert * v
float co[3]
float no[3]
int totfacesel
int totvertsel
int totedgesel
uint8_t f1
float vec[4]
struct Object * object
float vec[3][3]
float arm_mat[4][4]
float bone_mat[3][3]
EditBone * parent
char name[66]
Definition DNA_ID.h:415
void * first
MetaElem * lastelem
ListBase * editelems
float quat[4]
struct Nurb * next
short type
BezTriple * bezt
BPoint * bp
struct bPose * pose
float rot[3]
float rotAxis[3]
struct Object * parent
float viewmat[4][4]
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
TransformOrientationSlot orientation_slots[4]
ListBase transform_spaces
StripTransform * transform
StripData * data
View3DOverlay overlay
struct Object * camera
struct EditBone * act_edbone
ListBase * edbo
struct Bone * bone
struct bPoseChannel * parent
float pose_mat[4][4]
ListBase chanbase
struct blender::ed::transform::TransInfo::@233150133324350356161127343065016153253207070177 orient[3]
i
Definition text_draw.cc:230
#define ORIENTATION_USE_PLANE(ty)
uint8_t flag
Definition wm_window.cc:139