Blender V4.3
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
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
48
49#include "SEQ_select.hh"
50
51#include "transform.hh"
53
54/* *********************** TransSpace ************************** */
55
57{
58 Scene *scene = CTX_data_scene(C);
59 ListBase *transform_orientations = &scene->transform_spaces;
60
61 BLI_freelistN(transform_orientations);
62
63 for (int i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
64 TransformOrientationSlot *orient_slot = &scene->orientation_slots[i];
65 if (orient_slot->type == V3D_ORIENT_CUSTOM) {
66 orient_slot->type = V3D_ORIENT_GLOBAL; /* Fallback to global. */
67 orient_slot->index_custom = -1;
68 }
69 }
70}
71
73{
74 return static_cast<TransformOrientation *>(
76}
77
78static bool uniqueOrientationNameCheck(void *arg, const char *name)
79{
80 return findOrientationName((ListBase *)arg, name) != nullptr;
81}
82
83static void uniqueOrientationName(ListBase *lb, char *name)
84{
86 lb,
88 '.',
89 name,
91}
92
94 ReportList * /*reports*/,
95 const char *name,
96 const bool overwrite)
97{
99 float mat[3][3];
100
101 if (!rv3d) {
102 return nullptr;
103 }
104
105 copy_m3_m4(mat, rv3d->viewinv);
106 normalize_m3(mat);
107
108 if (name[0] == 0) {
109 View3D *v3d = CTX_wm_view3d(C);
110 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
111 /* If an object is used as camera, then this space is the same as object space! */
112 name = v3d->camera->id.name + 2;
113 }
114 else {
115 name = DATA_("Custom View");
116 }
117 }
118
119 return addMatrixSpace(C, mat, name, overwrite);
120}
121
123 ReportList * /*reports*/,
124 const char *name,
125 const bool overwrite)
126{
127 Base *base = CTX_data_active_base(C);
128 Object *ob;
129 float mat[3][3];
130
131 if (base == nullptr) {
132 return nullptr;
133 }
134
135 ob = base->object;
136
137 copy_m3_m4(mat, ob->object_to_world().ptr());
138 normalize_m3(mat);
139
140 /* Use object name if no name is given. */
141 if (name[0] == 0) {
142 name = ob->id.name + 2;
143 }
144
145 return addMatrixSpace(C, mat, name, overwrite);
146}
147
149 ReportList *reports,
150 const char *name,
151 const bool overwrite)
152{
153 float mat[3][3];
154 float normal[3], plane[3];
155
156 getTransformOrientation(C, normal, plane);
157
158 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
159 BKE_reports_prepend(reports, "Cannot use zero-length bone");
160 return nullptr;
161 }
162
163 if (name[0] == 0) {
164 name = DATA_("Bone");
165 }
166
167 return addMatrixSpace(C, mat, name, overwrite);
168}
169
171 ReportList *reports,
172 const char *name,
173 const bool overwrite)
174{
175 float mat[3][3];
176 float normal[3], plane[3];
177
178 getTransformOrientation(C, normal, plane);
179
180 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
181 BKE_reports_prepend(reports, "Cannot use zero-length curve");
182 return nullptr;
183 }
184
185 if (name[0] == 0) {
186 name = DATA_("Curve");
187 }
188
189 return addMatrixSpace(C, mat, name, overwrite);
190}
191
193 ReportList *reports,
194 const char *name,
195 const bool overwrite)
196{
197 float mat[3][3];
198 float normal[3], plane[3];
199 int type;
200
201 type = getTransformOrientation(C, normal, plane);
202
203 switch (type) {
204 case ORIENTATION_VERT:
205 if (createSpaceNormal(mat, normal) == 0) {
206 BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal");
207 return nullptr;
208 }
209
210 if (name[0] == 0) {
211 name = DATA_("Vertex");
212 }
213 break;
214 case ORIENTATION_EDGE:
215 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
216 BKE_reports_prepend(reports, "Cannot use zero-length edge");
217 return nullptr;
218 }
219
220 if (name[0] == 0) {
221 name = DATA_("Edge");
222 }
223 break;
224 case ORIENTATION_FACE:
225 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
226 BKE_reports_prepend(reports, "Cannot use zero-area face");
227 return nullptr;
228 }
229
230 if (name[0] == 0) {
231 name = DATA_("Face");
232 }
233 break;
234 default:
235 return nullptr;
236 }
237
238 return addMatrixSpace(C, mat, name, overwrite);
239}
240
241static bool test_rotmode_euler(short rotmode)
242{
243 return ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT) ? false : true;
244}
245
249static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
250{
251 /* X/Y are arbitrary axes, most importantly Z is the axis of rotation. */
252
253 float cross_vec[3];
254 float quat[4];
255
256 /* This is an un-scientific method to get a vector to cross with XYZ intentionally YZX. */
257 cross_vec[0] = axis[1];
258 cross_vec[1] = axis[2];
259 cross_vec[2] = axis[0];
260
261 /* X-axis. */
262 cross_v3_v3v3(gmat[0], cross_vec, axis);
263 normalize_v3(gmat[0]);
264 axis_angle_to_quat(quat, axis, angle);
265 mul_qt_v3(quat, gmat[0]);
266
267 /* Y-axis. */
268 axis_angle_to_quat(quat, axis, M_PI_2);
269 copy_v3_v3(gmat[1], gmat[0]);
270 mul_qt_v3(quat, gmat[1]);
271
272 /* Z-axis. */
273 copy_v3_v3(gmat[2], axis);
274
275 normalize_m3(gmat);
276}
277
278bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
279{
280 float mat[3][3], tmat[3][3], obmat[3][3];
281 if (test_rotmode_euler(pchan->rotmode)) {
282 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
283 }
284 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
285 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
286 }
287 else { /* Quaternion. */
288 return false;
289 }
290
291 /* Apply bone transformation. */
292 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
293
294 if (pchan->parent) {
295 float parent_mat[3][3];
296
297 copy_m3_m4(parent_mat,
298 (pchan->bone->flag & BONE_HINGE) ? pchan->parent->bone->arm_mat :
299 pchan->parent->pose_mat);
300 mul_m3_m3m3(mat, parent_mat, tmat);
301
302 /* Needed if object transformation isn't identity. */
303 copy_m3_m4(obmat, ob->object_to_world().ptr());
304 mul_m3_m3m3(gmat, obmat, mat);
305 }
306 else {
307 /* Needed if object transformation isn't identity. */
308 copy_m3_m4(obmat, ob->object_to_world().ptr());
309 mul_m3_m3m3(gmat, obmat, tmat);
310 }
311
312 normalize_m3(gmat);
313 return true;
314}
315
316bool gimbal_axis_object(Object *ob, float gmat[3][3])
317{
318 if (test_rotmode_euler(ob->rotmode)) {
319 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
320 }
321 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
323 }
324 else { /* Quaternion. */
325 return false;
326 }
327
328 if (ob->parent) {
329 float parent_mat[3][3];
330 copy_m3_m4(parent_mat, ob->parent->object_to_world().ptr());
331 normalize_m3(parent_mat);
332 mul_m3_m3m3(gmat, parent_mat, gmat);
333 }
334 return true;
335}
336
338 const float x[3],
339 const float y[3],
340 const float z[3])
341{
342 bool is_zero[3] = {true, true, true};
343 zero_m3(mat);
344 if (x) {
345 is_zero[0] = normalize_v3_v3(mat[0], x) == 0.0f;
346 }
347 if (y) {
348 is_zero[1] = normalize_v3_v3(mat[1], y) == 0.0f;
349 }
350 if (z) {
351 is_zero[2] = normalize_v3_v3(mat[2], z) == 0.0f;
352 }
353
354 int zero_axis = is_zero[0] + is_zero[1] + is_zero[2];
355 if (zero_axis == 0) {
356 return true;
357 }
358
359 if (zero_axis == 1) {
360 int axis = is_zero[0] ? 0 : is_zero[1] ? 1 : 2;
361 cross_v3_v3v3(mat[axis], mat[(axis + 1) % 3], mat[(axis + 2) % 3]);
362 if (normalize_v3(mat[axis]) != 0.0f) {
363 return true;
364 }
365 }
366 else if (zero_axis == 2) {
367 int axis, a, b;
368 axis = !is_zero[0] ? 0 : !is_zero[1] ? 1 : 2;
369 a = (axis + 1) % 3;
370 b = (axis + 2) % 3;
371
372 mat[a][a] = 1.0f;
373 mat[b][b] = 1.0f;
374 project_plane_v3_v3v3(mat[a], mat[a], mat[axis]);
375 project_plane_v3_v3v3(mat[b], mat[b], mat[axis]);
376 if ((normalize_v3(mat[a]) != 0.0f) && (normalize_v3(mat[b]) != 0.0f)) {
377 return true;
378 }
379 }
380
381 unit_m3(mat);
382 return false;
383}
384
385bool createSpaceNormal(float mat[3][3], const float normal[3])
386{
387 float tangent[3] = {0.0f, 0.0f, 1.0f};
388
389 copy_v3_v3(mat[2], normal);
390 if (normalize_v3(mat[2]) == 0.0f) {
391 return false; /* Error return. */
392 }
393
394 cross_v3_v3v3(mat[0], mat[2], tangent);
395 if (is_zero_v3(mat[0])) {
396 tangent[0] = 1.0f;
397 tangent[1] = tangent[2] = 0.0f;
398 cross_v3_v3v3(mat[0], tangent, mat[2]);
399 }
400
401 cross_v3_v3v3(mat[1], mat[2], mat[0]);
402
403 normalize_m3(mat);
404
405 return true;
406}
407
408bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
409{
410 if (normalize_v3_v3(mat[2], normal) == 0.0f) {
411 return false; /* Error return. */
412 }
413
414 /* Negate so we can use values from the matrix as input. */
415 negate_v3_v3(mat[1], tangent);
416 /* Preempt zero length tangent from causing trouble. */
417 if (is_zero_v3(mat[1])) {
418 mat[1][2] = 1.0f;
419 }
420
421 cross_v3_v3v3(mat[0], mat[2], mat[1]);
422 if (normalize_v3(mat[0]) == 0.0f) {
423 return false; /* Error return. */
424 }
425
426 cross_v3_v3v3(mat[1], mat[2], mat[0]);
427 normalize_v3(mat[1]);
428
429 /* Final matrix must be normalized, do inline. */
430 // normalize_m3(mat);
431
432 return true;
433}
434
436 ReportList *reports,
437 const char *name,
438 const bool use_view,
439 const bool activate,
440 const bool overwrite)
441{
442 TransformOrientation *ts = nullptr;
443
444 if (use_view) {
445 ts = createViewSpace(C, reports, name, overwrite);
446 }
447 else {
448 Object *obedit = CTX_data_edit_object(C);
450 if (obedit) {
451 if (obedit->type == OB_MESH) {
452 ts = createMeshSpace(C, reports, name, overwrite);
453 }
454 else if (obedit->type == OB_ARMATURE) {
455 ts = createBoneSpace(C, reports, name, overwrite);
456 }
457 else if (obedit->type == OB_CURVES_LEGACY) {
458 ts = createCurveSpace(C, reports, name, overwrite);
459 }
460 }
461 else if (ob && (ob->mode & OB_MODE_POSE)) {
462 ts = createBoneSpace(C, reports, name, overwrite);
463 }
464 else {
465 ts = createObjectSpace(C, reports, name, overwrite);
466 }
467 }
468
469 if (activate && ts != nullptr) {
471 }
472 return (ts != nullptr);
473}
474
476 float mat[3][3],
477 const char *name,
478 const bool overwrite)
479{
480 TransformOrientation *ts = nullptr;
481 Scene *scene = CTX_data_scene(C);
482 ListBase *transform_orientations = &scene->transform_spaces;
483 char name_unique[sizeof(ts->name)];
484
485 if (overwrite) {
486 ts = findOrientationName(transform_orientations, name);
487 }
488 else {
489 STRNCPY(name_unique, name);
490 uniqueOrientationName(transform_orientations, name_unique);
491 name = name_unique;
492 }
493
494 /* If not, create a new one. */
495 if (ts == nullptr) {
496 ts = static_cast<TransformOrientation *>(
497 MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix"));
498 BLI_addtail(transform_orientations, ts);
499 STRNCPY(ts->name, name);
500 }
501
502 /* Copy matrix into transform space. */
503 copy_m3_m3(ts->mat, mat);
504
505 return ts;
506}
507
512
518
520{
521 Scene *scene = CTX_data_scene(C);
522 int index = BKE_scene_transform_orientation_get_index(scene, target);
523
524 BLI_assert(index != -1);
525
526 scene->orientation_slots[SCE_ORIENT_DEFAULT].type = V3D_ORIENT_CUSTOM;
527 scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom = index;
528}
529
531{
532 Scene *scene = CTX_data_scene(C);
533 ListBase *transform_orientations = &scene->transform_spaces;
534 return BLI_listbase_count(transform_orientations);
535}
536
537void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
538{
539 if (r_name) {
540 BLI_strncpy(r_name, ts->name, MAX_NAME);
541 }
542 copy_m3_m3(r_mat, ts->mat);
543}
544
545/* Updates all `BONE_TRANSFORM` flags.
546 * Returns total number of bones with `BONE_TRANSFORM`.
547 * NOTE: `transform_convert_pose_transflags_update` has a similar logic. */
549 ListBase *lb,
550 const bool do_it)
551{
552 bool do_next;
553 int total = 0;
554
555 LISTBASE_FOREACH (Bone *, bone, lb) {
556 bone->flag &= ~BONE_TRANSFORM;
557 do_next = do_it;
558 if (do_it) {
559 if (ANIM_bone_in_visible_collection(arm, bone)) {
560 if (bone->flag & BONE_SELECTED) {
561 bone->flag |= BONE_TRANSFORM;
562 total++;
563
564 /* No transform on children if one parent bone is selected. */
565 do_next = false;
566 }
567 }
568 }
569 total += armature_bone_transflags_update_recursive(arm, &bone->childbase, do_next);
570 }
571
572 return total;
573}
574
575void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3])
576{
577 ARegion *region = CTX_wm_region(C);
578 Scene *scene = CTX_data_scene(C);
579 ViewLayer *view_layer = CTX_data_view_layer(C);
580 Object *obedit = CTX_data_edit_object(C);
581 View3D *v3d = CTX_wm_view3d(C);
582 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
583 BKE_view_layer_synced_ensure(scene, view_layer);
585 const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
586 const int pivot_point = scene->toolsettings->transform_pivot_point;
587
589 scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, r_mat);
590}
591
592static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
593{
594 bPoseChannel *active_pchan = BKE_pose_channel_active(ob, false);
595
596 /* Check if target bone is a child. */
597 if (active_pchan && active_pchan->parent) {
598 /* For child, show parent local regardless if "local location" is set for parent bone. */
600 float ob_orientations_mat[3][3];
601 transform_orientations_create_from_axis(ob_orientations_mat,
602 UNPACK3(ob->object_to_world().ptr()));
603 mul_m3_m3_pre(r_mat, ob_orientations_mat);
604 return;
605 }
606
607 /* For root, use local transform of armature object. */
608 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
609}
610
611static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
612{
613 /* If object has parent, then orient to parent. */
614 if (ob->parent) {
615 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->parent->object_to_world().ptr()));
616 }
617 else {
618 /* If object doesn't have parent, then orient to world. */
619 unit_m3(r_mat);
620 }
621}
622
624 ViewLayer *view_layer,
625 const View3D *v3d,
626 const RegionView3D *rv3d,
627 Object *ob,
628 Object *obedit,
629 const short orientation_index,
630 const int pivot_point,
631 float r_mat[3][3])
632{
633 switch (orientation_index) {
634 case V3D_ORIENT_GIMBAL: {
635
636 if (ob) {
637 if (ob->mode & OB_MODE_POSE) {
639 if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) {
640 break;
641 }
642 }
643 else {
644 if (gimbal_axis_object(ob, r_mat)) {
645 break;
646 }
647 }
648 }
649 /* If not gimbal, fall through to normal. */
651 }
652 case V3D_ORIENT_PARENT: {
653 if (ob) {
654 if (ob->mode & OB_MODE_POSE) {
656 break;
657 }
659 break;
660 }
661 /* No break; we define 'parent' as 'normal' otherwise. */
663 }
664 case V3D_ORIENT_NORMAL: {
665 if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
666 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
667 break;
668 }
669 /* No break we define 'normal' as 'local' in Object mode. */
671 }
672 case V3D_ORIENT_LOCAL: {
673 if (ob) {
674 if (ob->mode & OB_MODE_POSE) {
675 /* Each bone moves on its own local axis, but to avoid confusion,
676 * use the active bone's axis for display #33575, this works as expected on a single
677 * bone and users who select many bones will understand what's going on and what local
678 * means when they start transforming. */
679 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
680 }
681 else {
682 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
683 }
684 break;
685 }
686 /* If not local, fall through to global. */
688 }
689 case V3D_ORIENT_GLOBAL: {
690 unit_m3(r_mat);
691 break;
692 }
693 case V3D_ORIENT_VIEW: {
694 if (rv3d != nullptr) {
695 copy_m3_m4(r_mat, rv3d->viewinv);
696 normalize_m3(r_mat);
697 }
698 else {
699 unit_m3(r_mat);
700 }
701 break;
702 }
703 case V3D_ORIENT_CURSOR: {
704 copy_m3_m3(r_mat, scene->cursor.matrix<blender::float3x3>().ptr());
705 break;
706 }
708 /* Do nothing. */;
709 break;
710 }
712 default: {
713 BLI_assert(orientation_index >= V3D_ORIENT_CUSTOM);
714 int orientation_index_custom = orientation_index - V3D_ORIENT_CUSTOM;
716 scene, orientation_index_custom);
717 applyTransformOrientation(custom_orientation, r_mat, nullptr);
718 break;
719 }
720 }
721
722 return orientation_index;
723}
724
726 TransInfo *t,
727 short orient_index,
728 const float custom[3][3],
729 float r_spacemtx[3][3])
730{
731 if (orient_index == V3D_ORIENT_CUSTOM_MATRIX) {
732 copy_m3_m3(r_spacemtx, custom);
734 }
735
737 Scene *scene = t->scene;
738 Sequence *seq = SEQ_select_active_get(scene);
739 if (seq && seq->strip->transform && orient_index == V3D_ORIENT_LOCAL) {
740 axis_angle_to_mat3_single(r_spacemtx, 'Z', seq->strip->transform->rotation);
741 return orient_index;
742 }
743 }
744
746 Object *obedit = CTX_data_edit_object(C);
747 Scene *scene = t->scene;
748 View3D *v3d = nullptr;
749 RegionView3D *rv3d = nullptr;
750
751 if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
752 v3d = static_cast<View3D *>(t->view);
753 rv3d = static_cast<RegionView3D *>(t->region->regiondata);
754
755 if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
757 if (ob_armature) {
758 /* The armature matrix is used for GIMBAL, NORMAL and LOCAL orientations. */
759 ob = ob_armature;
760 }
761 }
762 }
763
764 short r_orient_index = ED_transform_calc_orientation_from_type_ex(
765 scene, t->view_layer, v3d, rv3d, ob, obedit, orient_index, t->around, r_spacemtx);
766
767 if (rv3d && (t->options & CTX_PAINT_CURVE)) {
768 /* Screen space in the 3d region. */
769 if (r_orient_index == V3D_ORIENT_VIEW) {
770 unit_m3(r_spacemtx);
771 }
772 else {
773 mul_m3_m4m3(r_spacemtx, rv3d->viewmat, r_spacemtx);
774 normalize_m3(r_spacemtx);
775 }
776 }
777
778 return r_orient_index;
779}
780
781const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)
782{
783 switch (orient_type) {
785 return RPT_("global");
787 return RPT_("gimbal");
789 return RPT_("normal");
790 case V3D_ORIENT_LOCAL:
791 return RPT_("local");
792 case V3D_ORIENT_VIEW:
793 return RPT_("view");
795 return RPT_("cursor");
797 return RPT_("parent");
799 return RPT_("custom");
801 default:
802 BLI_assert(orient_type >= V3D_ORIENT_CUSTOM);
804 t->scene, orient_type - V3D_ORIENT_CUSTOM);
805 return ts->name;
806 }
807}
808
809void transform_orientations_current_set(TransInfo *t, const short orient_index)
810{
811 const short orientation = t->orient[orient_index].type;
812 const char *spacename = transform_orientations_spacename_get(t, orientation);
813
814 STRNCPY(t->spacename, spacename);
815 copy_m3_m3(t->spacemtx, t->orient[orient_index].matrix);
817 t->orient_curr = eTOType(orient_index);
818}
819
824 BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
825{
826 BMIter iter;
827 BMElem *ele;
828 uint i;
829
832
834 /* Quick check. */
835 i = 0;
837 /* Shouldn't need this check. */
838 if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
839
840 /* Only use contiguous selection. */
841 if (ese->htype != htype) {
842 i = 0;
843 break;
844 }
845
846 elems[i++] = ese->ele;
847 if (n == i) {
848 break;
849 }
850 }
851 else {
852 BLI_assert(0);
853 }
854 }
855
856 if (i == 0) {
857 /* Pass. */
858 }
859 else if (i == n) {
860 return i;
861 }
862 }
863
864 i = 0;
865 BM_ITER_MESH (ele, &iter, bm, itype) {
866 BLI_assert(ele->head.htype == htype);
868 elems[i++] = ele;
869 if (n == i) {
870 break;
871 }
872 }
873 }
874
875 return i;
876}
877
888#if 0
889static uint bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const uint n)
890{
893}
894#endif
895
897 ViewLayer *view_layer,
898 const View3D *v3d,
899 Object *ob,
900 Object *obedit,
901 float normal[3],
902 float plane[3],
903 const short around)
904{
905 int result = ORIENTATION_NONE;
906 const bool activeOnly = (around == V3D_AROUND_ACTIVE);
907
908 zero_v3(normal);
909 zero_v3(plane);
910
911 if (obedit) {
912 float imat[3][3], mat[3][3];
913
914 /* We need the transpose of the inverse for a normal... */
915 copy_m3_m4(imat, ob->object_to_world().ptr());
916
917 invert_m3_m3(mat, imat);
918 transpose_m3(mat);
919
920 ob = obedit;
921
922 if (ob->type == OB_MESH) {
924 BMEditSelection ese;
925 float vec[3] = {0, 0, 0};
926
927 /* Use last selected with active. */
928 if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
929 BM_editselection_normal(&ese, normal);
930 BM_editselection_plane(&ese, plane);
931
932 switch (ese.htype) {
933 case BM_VERT:
934 result = ORIENTATION_VERT;
935 break;
936 case BM_EDGE:
937 result = ORIENTATION_EDGE;
938 break;
939 case BM_FACE:
940 result = ORIENTATION_FACE;
941 break;
942 }
943 }
944 else {
945 if (em->bm->totfacesel >= 1) {
946 BMFace *efa;
947 BMIter iter;
948
949 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
952 add_v3_v3(normal, efa->no);
953 add_v3_v3(plane, vec);
954 }
955 }
956
957 result = ORIENTATION_FACE;
958 }
959 else if (em->bm->totvertsel == 3) {
960 BMVert *v_tri[3];
961
962 if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) {
963 BMEdge *e = nullptr;
964 float no_test[3];
965
966 normal_tri_v3(normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
967
968 /* Check if the normal is pointing opposite to vert normals. */
969 no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0];
970 no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1];
971 no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2];
972 if (dot_v3v3(no_test, normal) < 0.0f) {
973 negate_v3(normal);
974 }
975
976 if (em->bm->totedgesel >= 1) {
977 /* Find an edge that's a part of v_tri (no need to search all edges). */
978 float e_length;
979 int j;
980
981 for (j = 0; j < 3; j++) {
982 BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]);
983 if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) {
984 const float e_test_length = BM_edge_calc_length_squared(e_test);
985 if ((e == nullptr) || (e_length < e_test_length)) {
986 e = e_test;
987 e_length = e_test_length;
988 }
989 }
990 }
991 }
992
993 if (e) {
994 BMVert *v_pair[2];
995 if (BM_edge_is_boundary(e)) {
996 BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]);
997 }
998 else {
999 v_pair[0] = e->v1;
1000 v_pair[1] = e->v2;
1001 }
1002 sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co);
1003 }
1004 else {
1005 BM_vert_tri_calc_tangent_edge(v_tri, plane);
1006 }
1007 }
1008 else {
1009 BLI_assert(0);
1010 }
1011
1012 result = ORIENTATION_FACE;
1013 }
1014 else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) {
1015 BMVert *v_pair[2] = {nullptr, nullptr};
1016 BMEdge *eed = nullptr;
1017
1018 if (em->bm->totedgesel == 1) {
1019 if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) {
1020 v_pair[0] = eed->v1;
1021 v_pair[1] = eed->v2;
1022 }
1023 }
1024 else {
1025 BLI_assert(em->bm->totvertsel == 2);
1026 bm_mesh_verts_select_get_n(em->bm, v_pair, 2);
1027 }
1028
1029 /* Should never fail. */
1030 if (LIKELY(v_pair[0] && v_pair[1])) {
1031 bool v_pair_swap = false;
1043 /* Be deterministic where possible and ensure `v_pair[0]` is active. */
1044 if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) {
1045 v_pair_swap = true;
1046 }
1047 else if (eed && BM_edge_is_boundary(eed)) {
1048 /* Predictable direction for boundary edges. */
1049 if (eed->l->v != v_pair[0]) {
1050 v_pair_swap = true;
1051 }
1052 }
1053
1054 if (v_pair_swap) {
1055 std::swap(v_pair[0], v_pair[1]);
1056 }
1057
1058 add_v3_v3v3(normal, v_pair[1]->no, v_pair[0]->no);
1059 sub_v3_v3v3(plane, v_pair[1]->co, v_pair[0]->co);
1060
1061 if (normalize_v3(plane) != 0.0f) {
1062 /* For edges it'd important the resulting matrix can rotate around the edge,
1063 * project onto the plane so we can use a fallback value. */
1064 project_plane_normalized_v3_v3v3(normal, normal, plane);
1065 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
1066 /* In the case the normal and plane are aligned,
1067 * use a fallback normal which is orthogonal to the plane. */
1068 ortho_v3_v3(normal, plane);
1069 }
1070 }
1071 }
1072
1073 result = ORIENTATION_EDGE;
1074 }
1075 else if (em->bm->totvertsel == 1) {
1076 BMVert *v = nullptr;
1077
1078 if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) {
1079 copy_v3_v3(normal, v->no);
1080 BMEdge *e_pair[2];
1081
1082 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
1083 bool v_pair_swap = false;
1084 BMVert *v_pair[2] = {
1085 BM_edge_other_vert(e_pair[0], v),
1086 BM_edge_other_vert(e_pair[1], v),
1087 };
1088 float dir_pair[2][3];
1089
1090 if (BM_edge_is_boundary(e_pair[0])) {
1091 if (e_pair[0]->l->v != v) {
1092 v_pair_swap = true;
1093 }
1094 }
1095 else {
1096 if (BM_edge_calc_length_squared(e_pair[0]) <
1097 BM_edge_calc_length_squared(e_pair[1]))
1098 {
1099 v_pair_swap = true;
1100 }
1101 }
1102
1103 if (v_pair_swap) {
1104 std::swap(v_pair[0], v_pair[1]);
1105 }
1106
1107 sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co);
1108 sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co);
1109 normalize_v3(dir_pair[0]);
1110 normalize_v3(dir_pair[1]);
1111
1112 add_v3_v3v3(plane, dir_pair[0], dir_pair[1]);
1113 }
1114 }
1115
1116 result = is_zero_v3(plane) ? ORIENTATION_VERT : ORIENTATION_EDGE;
1117 }
1118 else if (em->bm->totvertsel > 3) {
1119 BMIter iter;
1120 BMVert *v;
1121
1122 zero_v3(normal);
1123
1124 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1126 add_v3_v3(normal, v->no);
1127 }
1128 }
1129 normalize_v3(normal);
1130 result = ORIENTATION_VERT;
1131 }
1132 }
1133
1134 /* Not needed but this matches 2.68 and older behavior. */
1135 negate_v3(plane);
1136
1137 } /* End edit-mesh. */
1138 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
1139 Curve *cu = static_cast<Curve *>(obedit->data);
1140 Nurb *nu = nullptr;
1141 int a;
1142 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1143
1144 void *vert_act = nullptr;
1145 if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) {
1146 if (nu->type == CU_BEZIER) {
1147 BezTriple *bezt = static_cast<BezTriple *>(vert_act);
1148 BKE_nurb_bezt_calc_normal(nu, bezt, normal);
1149 BKE_nurb_bezt_calc_plane(nu, bezt, plane);
1150 }
1151 else {
1152 BPoint *bp = static_cast<BPoint *>(vert_act);
1153 BKE_nurb_bpoint_calc_normal(nu, bp, normal);
1154 BKE_nurb_bpoint_calc_plane(nu, bp, plane);
1155 }
1156 }
1157 else {
1158 const bool use_handle = v3d->overlay.handle_display != CURVE_HANDLE_NONE;
1159
1160 for (nu = static_cast<Nurb *>(nurbs->first); nu; nu = nu->next) {
1161 /* Only bezier has a normal. */
1162 if (nu->type == CU_BEZIER) {
1163 BezTriple *bezt = nu->bezt;
1164 a = nu->pntsu;
1165 while (a--) {
1166 short flag = 0;
1167
1168#define SEL_F1 (1 << 0)
1169#define SEL_F2 (1 << 1)
1170#define SEL_F3 (1 << 2)
1171
1172 if (use_handle) {
1173 if (bezt->f1 & SELECT) {
1174 flag |= SEL_F1;
1175 }
1176 if (bezt->f2 & SELECT) {
1177 flag |= SEL_F2;
1178 }
1179 if (bezt->f3 & SELECT) {
1180 flag |= SEL_F3;
1181 }
1182 }
1183 else {
1184 flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0;
1185 }
1186
1187 /* Exception. */
1188 if (flag) {
1189 float tvec[3];
1190 if ((around == V3D_AROUND_LOCAL_ORIGINS) ||
1192 {
1193 BKE_nurb_bezt_calc_normal(nu, bezt, tvec);
1194 add_v3_v3(normal, tvec);
1195 }
1196 else {
1197 /* Ignore `bezt->f2` in this case. */
1198 if (flag & SEL_F1) {
1199 sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]);
1200 normalize_v3(tvec);
1201 add_v3_v3(normal, tvec);
1202 }
1203 if (flag & SEL_F3) {
1204 sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]);
1205 normalize_v3(tvec);
1206 add_v3_v3(normal, tvec);
1207 }
1208 }
1209
1210 BKE_nurb_bezt_calc_plane(nu, bezt, tvec);
1211 add_v3_v3(plane, tvec);
1212 }
1213
1214#undef SEL_F1
1215#undef SEL_F2
1216#undef SEL_F3
1217
1218 bezt++;
1219 }
1220 }
1221 else if (nu->bp && (nu->pntsv == 1)) {
1222 BPoint *bp = nu->bp;
1223 a = nu->pntsu;
1224 while (a--) {
1225 if (bp->f1 & SELECT) {
1226 float tvec[3];
1227
1228 BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp);
1229 BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp);
1230
1231 const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT);
1232 const bool is_next_sel = bp_next && (bp_next->f1 & SELECT);
1233 if (is_prev_sel == false && is_next_sel == false) {
1234 /* Isolated, add based on surrounding. */
1235 BKE_nurb_bpoint_calc_normal(nu, bp, tvec);
1236 add_v3_v3(normal, tvec);
1237 }
1238 else if (is_next_sel) {
1239 /* A segment, add the edge normal. */
1240 sub_v3_v3v3(tvec, bp->vec, bp_next->vec);
1241 normalize_v3(tvec);
1242 add_v3_v3(normal, tvec);
1243 }
1244
1245 BKE_nurb_bpoint_calc_plane(nu, bp, tvec);
1246 add_v3_v3(plane, tvec);
1247 }
1248 bp++;
1249 }
1250 }
1251 }
1252 }
1253
1254 if (!is_zero_v3(normal)) {
1255 result = ORIENTATION_FACE;
1256 }
1257 }
1258 else if (obedit->type == OB_MBALL) {
1259 MetaBall *mb = static_cast<MetaBall *>(obedit->data);
1260 MetaElem *ml;
1261 bool ok = false;
1262 float tmat[3][3];
1263
1264 if (activeOnly && (ml = mb->lastelem)) {
1265 quat_to_mat3(tmat, ml->quat);
1266 add_v3_v3(normal, tmat[2]);
1267 add_v3_v3(plane, tmat[1]);
1268 ok = true;
1269 }
1270 else {
1271 LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
1272 if (ml->flag & SELECT) {
1273 quat_to_mat3(tmat, ml->quat);
1274 add_v3_v3(normal, tmat[2]);
1275 add_v3_v3(plane, tmat[1]);
1276 ok = true;
1277 }
1278 }
1279 }
1280
1281 if (ok) {
1282 if (!is_zero_v3(plane)) {
1283 result = ORIENTATION_FACE;
1284 }
1285 }
1286 }
1287 else if (obedit->type == OB_ARMATURE) {
1288 bArmature *arm = static_cast<bArmature *>(obedit->data);
1289 EditBone *ebone;
1290 bool ok = false;
1291 float tmat[3][3];
1292
1293 if (activeOnly && (ebone = arm->act_edbone)) {
1294 ED_armature_ebone_to_mat3(ebone, tmat);
1295 add_v3_v3(normal, tmat[2]);
1296 add_v3_v3(plane, tmat[1]);
1297 ok = true;
1298 }
1299 else {
1300 /* When we only have the root/tip are selected. */
1301 bool fallback_ok = false;
1302 float fallback_normal[3];
1303 float fallback_plane[3];
1304
1305 zero_v3(fallback_normal);
1306 zero_v3(fallback_plane);
1307
1308 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1309 if (ANIM_bonecoll_is_visible_editbone(arm, ebone)) {
1310 if (ebone->flag & BONE_SELECTED) {
1311 ED_armature_ebone_to_mat3(ebone, tmat);
1312 add_v3_v3(normal, tmat[2]);
1313 add_v3_v3(plane, tmat[1]);
1314 ok = true;
1315 }
1316 else if ((ok == false) && ((ebone->flag & BONE_TIPSEL) ||
1317 ((ebone->flag & BONE_ROOTSEL) &&
1318 (ebone->parent && ebone->flag & BONE_CONNECTED) == false)))
1319 {
1320 ED_armature_ebone_to_mat3(ebone, tmat);
1321 add_v3_v3(fallback_normal, tmat[2]);
1322 add_v3_v3(fallback_plane, tmat[1]);
1323 fallback_ok = true;
1324 }
1325 }
1326 }
1327 if ((ok == false) && fallback_ok) {
1328 ok = true;
1329 copy_v3_v3(normal, fallback_normal);
1330 copy_v3_v3(plane, fallback_plane);
1331 }
1332 }
1333
1334 if (ok) {
1335 if (!is_zero_v3(plane)) {
1336 result = ORIENTATION_EDGE;
1337 }
1338 }
1339 }
1340
1341 /* Vectors from edges don't need the special transpose inverse multiplication. */
1342 if (result == ORIENTATION_EDGE) {
1343 float tvec[3];
1344
1345 mul_mat3_m4_v3(ob->object_to_world().ptr(), normal);
1346 mul_mat3_m4_v3(ob->object_to_world().ptr(), plane);
1347
1348 /* Align normal to edge direction (so normal is perpendicular to the plane).
1349 * 'ORIENTATION_EDGE' will do the other way around.
1350 * This has to be done **after** applying obmat, see #45775! */
1351 project_v3_v3v3(tvec, normal, plane);
1352 sub_v3_v3(normal, tvec);
1353 }
1354 else {
1355 mul_m3_v3(mat, normal);
1356 mul_m3_v3(mat, plane);
1357 }
1358 }
1359 else if (ob && (ob->mode & OB_MODE_POSE)) {
1360 bArmature *arm = static_cast<bArmature *>(ob->data);
1361 bPoseChannel *pchan;
1362 float imat[3][3], mat[3][3];
1363 bool ok = false;
1364
1365 if (activeOnly && (pchan = BKE_pose_channel_active_if_bonecoll_visible(ob))) {
1366 add_v3_v3(normal, pchan->pose_mat[2]);
1367 add_v3_v3(plane, pchan->pose_mat[1]);
1368 ok = true;
1369 }
1370 else {
1371 int transformed_len;
1372 transformed_len = armature_bone_transflags_update_recursive(arm, &arm->bonebase, true);
1373 if (transformed_len) {
1374 /* Use channels to get stats. */
1375 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
1376 if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) {
1377 add_v3_v3(normal, pchan->pose_mat[2]);
1378 add_v3_v3(plane, pchan->pose_mat[1]);
1379 }
1380 }
1381 ok = true;
1382 }
1383 }
1384
1385 /* Use for both active & all. */
1386 if (ok) {
1387 /* We need the transpose of the inverse for a normal. */
1388 copy_m3_m4(imat, ob->object_to_world().ptr());
1389
1390 invert_m3_m3(mat, imat);
1391 transpose_m3(mat);
1392 mul_m3_v3(mat, normal);
1393 mul_m3_v3(mat, plane);
1394
1395 result = ORIENTATION_EDGE;
1396 }
1397 }
1398 else {
1399 /* We need the one selected object, if its not active. */
1400 if (ob != nullptr) {
1401 bool ok = false;
1402 if (activeOnly || (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
1403 /* Ignore selection state. */
1404 ok = true;
1405 }
1406 else {
1407 BKE_view_layer_synced_ensure(scene, view_layer);
1408 Base *base = BKE_view_layer_base_find(view_layer, ob);
1409 if (UNLIKELY(base == nullptr)) {
1410 /* This is very unlikely, if it happens allow the value to be set since the caller
1411 * may have taken the object from outside this view-layer. */
1412 ok = true;
1413 }
1414 else if (BASE_SELECTED(v3d, base)) {
1415 ok = true;
1416 }
1417 }
1418
1419 if (ok) {
1420 copy_v3_v3(normal, ob->object_to_world().ptr()[2]);
1421 copy_v3_v3(plane, ob->object_to_world().ptr()[1]);
1422 }
1423 }
1424 result = ORIENTATION_NORMAL;
1425 }
1426
1427 return result;
1428}
1429
1430int getTransformOrientation(const bContext *C, float normal[3], float plane[3])
1431{
1432 Object *obact = CTX_data_active_object(C);
1433 Object *obedit = CTX_data_edit_object(C);
1434
1435 /* Dummy value, not #V3D_AROUND_ACTIVE and not #V3D_AROUND_LOCAL_ORIGINS. */
1436 short around = V3D_AROUND_CENTER_BOUNDS;
1437
1438 const Scene *scene = CTX_data_scene(C);
1439 ViewLayer *view_layer = CTX_data_view_layer(C);
1440 View3D *v3d = CTX_wm_view3d(C);
1441
1442 return getTransformOrientation_ex(scene, view_layer, v3d, obact, obedit, normal, plane, around);
1443}
1444
1446 ViewLayer *view_layer,
1447 const View3D *v3d,
1448 Object *ob,
1449 Object *obedit,
1450 const short around,
1451 float r_orientation_mat[3][3])
1452{
1453 float normal[3] = {0.0, 0.0, 0.0};
1454 float plane[3] = {0.0, 0.0, 0.0};
1455
1456 int type;
1457
1458 type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, normal, plane, around);
1459
1460 /* Fallback, when the plane can't be calculated. */
1461 if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) {
1462 type = ORIENTATION_VERT;
1463 }
1464
1465 switch (type) {
1466 case ORIENTATION_NORMAL:
1467 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1468 type = ORIENTATION_NONE;
1469 }
1470 break;
1471 case ORIENTATION_VERT:
1472 if (createSpaceNormal(r_orientation_mat, normal) == 0) {
1473 type = ORIENTATION_NONE;
1474 }
1475 break;
1476 case ORIENTATION_EDGE:
1477 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1478 type = ORIENTATION_NONE;
1479 }
1480 break;
1481 case ORIENTATION_FACE:
1482 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1483 type = ORIENTATION_NONE;
1484 }
1485 break;
1486 default:
1488 break;
1489 }
1490
1491 if (type == ORIENTATION_NONE) {
1492 unit_m3(r_orientation_mat);
1493 }
1494}
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
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:1081
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:398
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1006
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5038
BPoint * BKE_nurb_bpoint_get_prev(Nurb *nu, BPoint *bp)
Definition curve.cc:984
BPoint * BKE_nurb_bpoint_get_next(Nurb *nu, BPoint *bp)
Definition curve.cc:941
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1058
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1021
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
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:205
int BKE_scene_transform_orientation_get_index(const Scene *scene, const TransformOrientation *orientation)
Definition scene.cc:3491
void BKE_scene_transform_orientation_remove(Scene *scene, TransformOrientation *orientation)
Definition scene.cc:3466
TransformOrientation * BKE_scene_transform_orientation_find(const Scene *scene, int index)
Definition scene.cc:3486
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2386
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
#define M_PI_2
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
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])
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 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])
#define STRNCPY(dst, src)
Definition BLI_string.h:593
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t void BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(1
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 MAX_NAME
Definition DNA_defs.h:50
#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
@ SCE_ORIENT_DEFAULT
#define BASE_SELECTED(v3d, base)
@ RGN_TYPE_WINDOW
@ SPACE_SEQ
@ SPACE_VIEW3D
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_LOCAL_ORIGINS
@ CURVE_HANDLE_NONE
@ RV3D_CAMOB
@ 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
Read Guarded memory(de)allocation.
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
ATTR_WARN_UNUSED_RESULT BMesh const char itype
ATTR_WARN_UNUSED_RESULT BMesh * bm
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_edge(BMVert *verts[3], float r_tangent[3])
void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3])
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
float BM_edge_calc_length_squared(const BMEdge *e)
bool BM_vert_edge_pair(BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_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
local_group_size(16, 16) .push_constant(Type b
#define SELECT
#define offsetof(t, d)
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline bool is_zero(const float2 a)
Sequence * SEQ_select_active_get(const Scene *scene)
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
ListBase selected
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:425
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]
StripTransform * transform
struct TransInfo::@565 orient[3]
short around
Definition transform.hh:580
void * view
Definition transform.hh:647
char spacetype
Definition transform.hh:582
float spacemtx_inv[3][3]
Definition transform.hh:593
Scene * scene
Definition transform.hh:654
eTOType orient_curr
Definition transform.hh:613
ViewLayer * view_layer
Definition transform.hh:655
ARegion * region
Definition transform.hh:652
char spacename[64]
Definition transform.hh:595
float matrix[3][3]
Definition transform.hh:610
float spacemtx[3][3]
Definition transform.hh:592
eTContext options
Definition transform.hh:521
short type
Definition transform.hh:609
View3DOverlay overlay
struct Object * camera
struct EditBone * act_edbone
ListBase * edbo
struct Bone * bone
struct bPoseChannel * parent
float pose_mat[4][4]
ListBase chanbase
const c_style_mat & ptr() const
@ CTX_PAINT_CURVE
Definition transform.hh:72
@ CTX_SEQUENCER_IMAGE
Definition transform.hh:75
eTOType
Definition transform.hh:232
Object * transform_object_deform_pose_armature_get(const TransInfo *t, Object *ob)
bool transform_orientations_create_from_axis(float mat[3][3], const float x[3], const float y[3], const float z[3])
static bool test_rotmode_euler(short rotmode)
bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
static TransformOrientation * createObjectSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
void transform_orientations_current_set(TransInfo *t, const short orient_index)
void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
bool createSpaceNormal(float mat[3][3], const float normal[3])
static uint bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const uint n)
static uint bm_mesh_elems_select_get_n__internal(BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
void BIF_clearTransformOrientation(bContext *C)
int getTransformOrientation(const bContext *C, float normal[3], float plane[3])
static TransformOrientation * createViewSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
void BIF_removeTransformOrientationIndex(bContext *C, int index)
void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3])
static bool uniqueOrientationNameCheck(void *arg, const char *name)
void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target)
#define SEL_F1
#define SEL_F2
static TransformOrientation * createBoneSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
bool gimbal_axis_object(Object *ob, float gmat[3][3])
static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
static TransformOrientation * findOrientationName(ListBase *lb, const char *name)
int getTransformOrientation_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, float normal[3], float plane[3], const short around)
TransformOrientation * addMatrixSpace(bContext *C, float mat[3][3], const char *name, const bool overwrite)
static TransformOrientation * createCurveSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
static TransformOrientation * createMeshSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
const char * transform_orientations_spacename_get(TransInfo *t, const short orient_type)
int BIF_countTransformOrientation(const bContext *C)
bool BIF_createTransformOrientation(bContext *C, ReportList *reports, const char *name, const bool use_view, const bool activate, const bool overwrite)
static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target)
static void uniqueOrientationName(ListBase *lb, char *name)
#define SEL_F3
static int armature_bone_transflags_update_recursive(bArmature *arm, ListBase *lb, const bool do_it)
void ED_getTransformOrientationMatrix(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, const short around, float r_orientation_mat[3][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)
bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
short ED_transform_calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, const short orientation_index, const int pivot_point, float r_mat[3][3])
@ ORIENTATION_NORMAL
#define ORIENTATION_USE_PLANE(ty)
uint8_t flag
Definition wm_window.cc:138