Blender V5.0
interface_template_matrix.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <fmt/format.h>
10
11#include "BKE_unit.hh"
12
13#include "BLI_math_matrix.h"
14#include "BLI_math_rotation.h"
15
16#include "BLT_translation.hh"
17
18#include "RNA_access.hh"
19#include "RNA_enum_types.hh"
20
21#include "interface_intern.hh"
22
25
26/* Format translation/rotation value as a string based on Blender unit settings. */
27static std::string format_unit_value(float value, PropertySubType subtype, uiLayout *layout)
28{
29 const UnitSettings *unit = layout->block()->unit;
30 const int unit_type = RNA_SUBTYPE_UNIT(subtype);
31
32 /* Change negative zero to regular zero, without altering anything else. */
33 value += +0.0f;
34 double value_scaled = BKE_unit_value_scale(*unit, unit_type, value);
35
36 char new_str[UI_MAX_DRAW_STR];
38 sizeof(new_str),
39 value_scaled,
41 RNA_SUBTYPE_UNIT_VALUE(unit_type),
42 *unit,
43 true);
44 return std::string(new_str);
45}
46
47/* Format unitless value as a string. */
48static std::string format_coefficient(float value)
49{
50 /* Change negative zero to regular zero, without altering anything else. */
51 value += +0.0f;
52 /* Same precision that we use in `Object.scale`. */
53 const int RNA_SCALE_PREC_DEFAULT = 3;
54 return fmt::format("{:.{}f}", value, RNA_SCALE_PREC_DEFAULT);
55}
56
57/* Static variable to store rotation mode button state at runtime.
58 * Defaults to XYZ Euler. */
60
61static void rotation_mode_menu_callback(bContext *, uiLayout *layout, void *)
62{
65 const int yco = -1.5f * UI_UNIT_Y;
66 const int width = 9 * UI_UNIT_X;
67 uiBut *but = uiDefButI(layout->block(),
69 0,
70 IFACE_(mode_info.name),
71 0,
72 yco,
73 width / 2,
76 i,
77 i,
78 TIP_(mode_info.description));
80 if (i == rotation_mode_index) {
82 }
83 }
84}
85
87{
88 /* Matrix template UI is mirroring Object's Transform UI for better UX. */
89 uiLayout *row, *col;
90 uiLayout *layout_ = &layout.box();
91
92 float m4[4][4];
93 RNA_property_float_get_array(&ptr, &prop, &m4[0][0]);
94
95 /* Show a warning as a matrix with a shear cannot be represented fully
96 * by a decomposition.
97 * Use the 3x3 matrix, as shear in the 4x4 homogeneous matrix
98 * is expected due to the translation component. */
99 float m3[3][3];
100 copy_m3_m4(m3, m4);
101 if (!is_orthogonal_m3(m3)) {
102 layout_->label(RPT_("Matrix has a shear"), ICON_ERROR);
103 }
104
105 float loc[3], quat[4], size[3];
106 mat4_decompose(loc, quat, size, m4);
107
108 /* Translation. */
109 col = &layout_->column(true);
110 col->use_property_split_set(true);
111
112 row = &col->row(true);
113 uiItemL_respect_property_split(row, IFACE_("Location X"), ICON_NONE);
114 row->label(format_unit_value(loc[0], PROP_TRANSLATION, layout_), ICON_NONE);
115
116 row = &col->row(true);
117 uiItemL_respect_property_split(row, IFACE_("Y"), ICON_NONE);
118 row->label(format_unit_value(loc[1], PROP_TRANSLATION, layout_), ICON_NONE);
119
120 row = &col->row(true);
121 uiItemL_respect_property_split(row, IFACE_("Z"), ICON_NONE);
122 row->label(format_unit_value(loc[2], PROP_TRANSLATION, layout_), ICON_NONE);
123
124 /* Rotation. */
125 float eul[3], axis[3];
126 float angle;
128 col = &layout_->column(true);
129 col->use_property_split_set(true);
130
131 if (mode_info.value == ROT_MODE_QUAT) {
132 row = &col->row(true);
133 uiItemL_respect_property_split(row, IFACE_("Rotation W"), ICON_NONE);
134 row->label(format_coefficient(quat[0]), ICON_NONE);
135
136 row = &col->row(true);
137 uiItemL_respect_property_split(row, IFACE_("X"), ICON_NONE);
138 row->label(format_coefficient(quat[1]), ICON_NONE);
139
140 row = &col->row(true);
141 uiItemL_respect_property_split(row, IFACE_("Y"), ICON_NONE);
142 row->label(format_coefficient(quat[2]), ICON_NONE);
143
144 row = &col->row(true);
145 uiItemL_respect_property_split(row, IFACE_("Z"), ICON_NONE);
146 row->label(format_coefficient(quat[3]), ICON_NONE);
147 }
148 else if (mode_info.value == ROT_MODE_AXISANGLE) {
149 quat_to_axis_angle(axis, &angle, quat);
150
151 row = &col->row(true);
152 uiItemL_respect_property_split(row, IFACE_("Rotation W"), ICON_NONE);
153 row->label(format_unit_value(angle, PROP_ANGLE, layout_), ICON_NONE);
154
155 row = &col->row(true);
156 uiItemL_respect_property_split(row, IFACE_("X"), ICON_NONE);
157 row->label(format_coefficient(axis[0]), ICON_NONE);
158
159 row = &col->row(true);
160 uiItemL_respect_property_split(row, IFACE_("Y"), ICON_NONE);
161 row->label(format_coefficient(axis[1]), ICON_NONE);
162
163 row = &col->row(true);
164 uiItemL_respect_property_split(row, IFACE_("Z"), ICON_NONE);
165 row->label(format_coefficient(axis[2]), ICON_NONE);
166 }
167 else { /* Euler modes. */
168 quat_to_eulO(eul, mode_info.value, quat);
169
170 row = &col->row(true);
171 uiItemL_respect_property_split(row, IFACE_("Rotation X"), ICON_NONE);
172 row->label(format_unit_value(eul[0], PROP_ANGLE, layout_), ICON_NONE);
173
174 row = &col->row(true);
175 uiItemL_respect_property_split(row, IFACE_("Y"), ICON_NONE);
176 row->label(format_unit_value(eul[1], PROP_ANGLE, layout_), ICON_NONE);
177
178 row = &col->row(true);
179 uiItemL_respect_property_split(row, IFACE_("Z"), ICON_NONE);
180 row->label(format_unit_value(eul[2], PROP_ANGLE, layout_), ICON_NONE);
181 }
182
183 /* Mirror RNA enum property dropdown UI - with menu triangle an dropdown items. */
184 row = &layout_->row(true);
185 uiItemL_respect_property_split(row, IFACE_("Mode"), ICON_NONE);
186 uiBlock *block = row->block();
187 uiBut *but = uiDefMenuBut(block,
189 nullptr,
190 IFACE_(mode_info.name),
191 0,
192 0,
193 UI_UNIT_X * 10,
194 UI_UNIT_Y,
195 TIP_("Rotation mode.\n\nOnly affects the way "
196 "rotation is displayed, rotation itself is unaffected."));
198
199 /* Scale. */
200 col = &layout_->column(true);
201 col->use_property_split_set(true);
202
203 row = &col->row(true);
204 uiItemL_respect_property_split(row, IFACE_("Scale X"), ICON_NONE);
205 row->label(format_coefficient(size[0]), ICON_NONE);
206
207 row = &col->row(true);
208 uiItemL_respect_property_split(row, IFACE_("Y"), ICON_NONE);
209 row->label(format_coefficient(size[1]), ICON_NONE);
210
211 row = &col->row(true);
212 uiItemL_respect_property_split(row, IFACE_("Z"), ICON_NONE);
213 row->label(format_coefficient(size[2]), ICON_NONE);
214}
215
216void uiTemplateMatrix(uiLayout *layout, PointerRNA *ptr, const StringRefNull propname)
217{
218 PropertyRNA *prop = RNA_struct_find_property(ptr, propname.c_str());
219
220 if (!prop || RNA_property_type(prop) != PROP_FLOAT ||
222 {
223 RNA_warning("4x4 Matrix property not found: %s.%s",
225 propname.c_str());
226 return;
227 }
228 draw_matrix_template(*layout, *ptr, *prop);
229}
size_t BKE_unit_value_as_string(char *str, int str_maxncpy, double value, int prec, int type, const UnitSettings &settings, bool pad)
Definition unit.cc:1882
double BKE_unit_value_scale(const UnitSettings &settings, int unit_type, double value)
Definition unit.cc:1907
bool is_orthogonal_m3(const float m[3][3])
void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat[4][4])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void quat_to_eulO(float e[3], short order, const float q[4])
void quat_to_axis_angle(float axis[3], float *angle, const float q[4])
#define RPT_(msgid)
#define TIP_(msgid)
#define IFACE_(msgid)
@ ROT_MODE_QUAT
@ ROT_MODE_AXISANGLE
@ ROT_MODE_EUL
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define RNA_warning(format,...)
#define RNA_SUBTYPE_UNIT_VALUE(subtype)
Definition RNA_types.hh:220
@ PROP_FLOAT
Definition RNA_types.hh:164
#define RNA_TRANSLATION_PREC_DEFAULT
Definition RNA_types.hh:224
#define RNA_SUBTYPE_UNIT(subtype)
Definition RNA_types.hh:218
PropertySubType
Definition RNA_types.hh:232
@ PROP_MATRIX
Definition RNA_types.hh:265
@ PROP_ANGLE
Definition RNA_types.hh:252
@ PROP_TRANSLATION
Definition RNA_types.hh:261
uiBut * uiDefButI(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_disable(uiBut *but, int flag)
#define UI_UNIT_Y
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
@ UI_BUT_UNDO
void UI_but_type_set_menu_from_pulldown(uiBut *but)
#define UI_UNIT_X
void UI_but_flag_enable(uiBut *but, int flag)
uiLayout * uiItemL_respect_property_split(uiLayout *layout, blender::StringRef text, int icon)
#define UI_MAX_DRAW_STR
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr const char * c_str() const
uint col
@ UI_SELECT_DRAW
void uiTemplateMatrix(uiLayout *layout, PointerRNA *ptr, const StringRefNull propname)
static std::string format_unit_value(float value, PropertySubType subtype, uiLayout *layout)
static void rotation_mode_menu_callback(bContext *, uiLayout *layout, void *)
static int rotation_mode_index
static std::string format_coefficient(float value)
static void draw_matrix_template(uiLayout &layout, PointerRNA &ptr, PropertyRNA &prop)
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
uint RNA_enum_items_count(const EnumPropertyItem *item)
PropertyType RNA_property_type(PropertyRNA *prop)
const char * RNA_struct_identifier(const StructRNA *type)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
const EnumPropertyItem rna_enum_object_rotation_mode_items[]
const char * name
Definition RNA_types.hh:661
const char * description
Definition RNA_types.hh:663
const UnitSettings * unit
uiBlock * block() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
uiLayout & box()
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238