diff options
-rw-r--r-- | core/math/quaternion.cpp | 18 | ||||
-rw-r--r-- | core/math/quaternion.h | 4 | ||||
-rw-r--r-- | core/variant/variant.h | 1 | ||||
-rw-r--r-- | core/variant/variant_call.cpp | 2 | ||||
-rw-r--r-- | doc/classes/ColorPicker.xml | 3 | ||||
-rw-r--r-- | doc/classes/Node3D.xml | 14 | ||||
-rw-r--r-- | doc/classes/Quaternion.xml | 3 | ||||
-rw-r--r-- | editor/editor_properties.cpp | 2 | ||||
-rw-r--r-- | editor/plugins/script_editor_plugin.cpp | 2 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs | 4 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.cpp | 3 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.h | 1 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 4 | ||||
-rw-r--r-- | scene/3d/node_3d.cpp | 23 | ||||
-rw-r--r-- | scene/3d/node_3d.h | 14 | ||||
-rw-r--r-- | scene/gui/color_mode.cpp | 8 | ||||
-rw-r--r-- | scene/resources/default_theme/color_picker_hue.svg | 1 | ||||
-rw-r--r-- | scene/resources/default_theme/default_theme.cpp | 55 | ||||
-rw-r--r-- | tests/core/math/test_quaternion.h | 27 |
19 files changed, 108 insertions, 81 deletions
diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp index 42b61f6dfe..942a0b766e 100644 --- a/core/math/quaternion.cpp +++ b/core/math/quaternion.cpp @@ -38,25 +38,11 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const { return Math::acos(CLAMP(d * d * 2 - 1, -1, 1)); } -// get_euler_xyz returns a vector containing the Euler angles in the format -// (ax,ay,az), where ax is the angle of rotation around x axis, -// and similar for other axes. -// This implementation uses XYZ convention (Z is the first rotation). -Vector3 Quaternion::get_euler_xyz() const { - Basis m(*this); - return m.get_euler(EulerOrder::XYZ); -} - -// get_euler_yxz returns a vector containing the Euler angles in the format -// (ax,ay,az), where ax is the angle of rotation around x axis, -// and similar for other axes. -// This implementation uses YXZ convention (Z is the first rotation). -Vector3 Quaternion::get_euler_yxz() const { +Vector3 Quaternion::get_euler(EulerOrder p_order) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized."); #endif - Basis m(*this); - return m.get_euler(EulerOrder::YXZ); + return Basis(*this).get_euler(p_order); } void Quaternion::operator*=(const Quaternion &p_q) { diff --git a/core/math/quaternion.h b/core/math/quaternion.h index 7497f1643e..c5af2121d9 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -66,9 +66,7 @@ struct _NO_DISCARD_ Quaternion { _FORCE_INLINE_ real_t dot(const Quaternion &p_q) const; real_t angle_to(const Quaternion &p_to) const; - Vector3 get_euler_xyz() const; - Vector3 get_euler_yxz() const; - Vector3 get_euler() const { return get_euler_yxz(); }; + Vector3 get_euler(EulerOrder p_order = EulerOrder::YXZ) const; static Quaternion from_euler(const Vector3 &p_euler); Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const; diff --git a/core/variant/variant.h b/core/variant/variant.h index b0738e7d44..c5be609184 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -490,6 +490,7 @@ public: } // Only enum classes that need to be bound need this to be defined. + VARIANT_ENUM_CLASS_CONSTRUCTOR(EulerOrder) VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyAxis) VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyButton) VARIANT_ENUM_CLASS_CONSTRUCTOR(Key) diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index f32233d808..7da46a2d05 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1805,7 +1805,7 @@ static void _register_variant_builtin_methods() { bind_method(Quaternion, slerpni, sarray("to", "weight"), varray()); bind_method(Quaternion, spherical_cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray()); bind_method(Quaternion, spherical_cubic_interpolate_in_time, sarray("b", "pre_a", "post_b", "weight", "b_t", "pre_a_t", "post_b_t"), varray()); - bind_method(Quaternion, get_euler, sarray(), varray()); + bind_method(Quaternion, get_euler, sarray("order"), varray((int64_t)EulerOrder::YXZ)); bind_static_method(Quaternion, from_euler, sarray("euler"), varray()); bind_method(Quaternion, get_axis, sarray(), varray()); bind_method(Quaternion, get_angle, sarray(), varray()); diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index cd1d0b016d..7b71dbc362 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -151,6 +151,9 @@ <theme_item name="color_hue" data_type="icon" type="Texture2D"> Custom texture for the hue selection slider on the right. </theme_item> + <theme_item name="color_okhsl_hue" data_type="icon" type="Texture2D"> + Custom texture for the H slider in the OKHSL color mode. + </theme_item> <theme_item name="expanded_arrow" data_type="icon" type="Texture2D"> The icon for color preset drop down menu when expanded. </theme_item> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index bc2570b183..b6024b1887 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -290,7 +290,7 @@ <member name="rotation_edit_mode" type="int" setter="set_rotation_edit_mode" getter="get_rotation_edit_mode" enum="Node3D.RotationEditMode" default="0"> Specify how rotation (and scale) will be presented in the editor. </member> - <member name="rotation_order" type="int" setter="set_rotation_order" getter="get_rotation_order" enum="Node3D.RotationOrder" default="2"> + <member name="rotation_order" type="int" setter="set_rotation_order" getter="get_rotation_order" enum="EulerOrder" default="2"> Specify the axis rotation order of the [member rotation] property. The final orientation is constructed by rotating the Euler angles in the order specified by this property. </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)"> @@ -342,17 +342,5 @@ </constant> <constant name="ROTATION_EDIT_MODE_BASIS" value="2" enum="RotationEditMode"> </constant> - <constant name="ROTATION_ORDER_XYZ" value="0" enum="RotationOrder"> - </constant> - <constant name="ROTATION_ORDER_XZY" value="1" enum="RotationOrder"> - </constant> - <constant name="ROTATION_ORDER_YXZ" value="2" enum="RotationOrder"> - </constant> - <constant name="ROTATION_ORDER_YZX" value="3" enum="RotationOrder"> - </constant> - <constant name="ROTATION_ORDER_ZXY" value="4" enum="RotationOrder"> - </constant> - <constant name="ROTATION_ORDER_ZYX" value="5" enum="RotationOrder"> - </constant> </constants> </class> diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 08a9231751..ac2eda9f28 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -99,8 +99,9 @@ </method> <method name="get_euler" qualifiers="const"> <return type="Vector3" /> + <param index="0" name="order" type="int" default="2" /> <description> - Returns Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last) corresponding to the rotation represented by the unit quaternion. Returned vector contains the rotation angles in the format (X angle, Y angle, Z angle). + Returns the quaternion's rotation in the form of Euler angles. The Euler order depends on the [param order] parameter, for example using the YXZ convention: since this method decomposes, first Z, then X, and Y last. See the [enum EulerOrder] enum for possible values. The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle). </description> </method> <method name="inverse" qualifiers="const"> diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 115fda6ca0..d76dce0c1d 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2725,7 +2725,7 @@ void EditorPropertyQuaternion::update_property() { spin[2]->set_value(val.z); spin[3]->set_value(val.w); if (!is_grabbing_euler()) { - Vector3 v = val.normalized().get_euler_yxz(); + Vector3 v = val.normalized().get_euler(); edit_euler.x = Math::rad_to_deg(v.x); edit_euler.y = Math::rad_to_deg(v.y); edit_euler.z = Math::rad_to_deg(v.z); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e4f0192c64..4ff3919e9b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3892,7 +3892,7 @@ ScriptEditor::ScriptEditor() { vbc->add_child(disk_changed_list); disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); - disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts)); + disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts).bind(false)); disk_changed->set_ok_button_text(TTR("Reload")); disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 5dd629aeb0..f01f0be985 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -312,7 +312,7 @@ namespace Godot /// the rotation angles in the format (X angle, Y angle, Z angle). /// </summary> /// <returns>The Euler angle representation of this quaternion.</returns> - public Vector3 GetEuler() + public Vector3 GetEuler(EulerOrder order = EulerOrder.Yxz) { #if DEBUG if (!IsNormalized()) @@ -321,7 +321,7 @@ namespace Godot } #endif var basis = new Basis(this); - return basis.GetEuler(); + return basis.GetEuler(order); } /// <summary> diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 820f53d5d6..f477787771 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -3414,7 +3414,7 @@ bool DisplayServerX11::mouse_process_popups() { XWindowAttributes root_attrs; XGetWindowAttributes(x11_display, root, &root_attrs); Vector2i pos = Vector2i(root_attrs.x + root_x, root_attrs.y + root_y); - if ((pos != last_mouse_monitor_pos) || (mask != last_mouse_monitor_mask)) { + if (mask != last_mouse_monitor_mask) { if (((mask & Button1Mask) || (mask & Button2Mask) || (mask & Button3Mask) || (mask & Button4Mask) || (mask & Button5Mask))) { List<WindowID>::Element *C = nullptr; List<WindowID>::Element *E = popup_list.back(); @@ -3440,7 +3440,6 @@ bool DisplayServerX11::mouse_process_popups() { } } last_mouse_monitor_mask = mask; - last_mouse_monitor_pos = pos; } } return closed; diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 861eced88b..4be8c3a534 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -169,7 +169,6 @@ class DisplayServerX11 : public DisplayServer { HashMap<WindowID, WindowData> windows; unsigned int last_mouse_monitor_mask = 0; - Vector2i last_mouse_monitor_pos; uint64_t time_since_popup = 0; List<WindowID> popup_list; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 9d0a2578fb..d6ee712a31 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2449,6 +2449,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA window_mouseover_id = INVALID_WINDOW_ID; _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + } else if (window_mouseover_id != INVALID_WINDOW_ID) { + // This is reached during drag and drop, after dropping in a different window. + // Once-off notification, must call again. + track_mouse_leave_event(windows[window_mouseover_id].hWnd); } } break; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index e2d68c36ff..1327bdd6e9 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -392,27 +392,25 @@ Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const { return data.rotation_edit_mode; } -void Node3D::set_rotation_order(RotationOrder p_order) { - EulerOrder order = EulerOrder(p_order); - - if (data.euler_rotation_order == order) { +void Node3D::set_rotation_order(EulerOrder p_order) { + if (data.euler_rotation_order == p_order) { return; } - ERR_FAIL_INDEX(int32_t(order), 6); + ERR_FAIL_INDEX(int32_t(p_order), 6); bool transform_changed = false; if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); } else if (data.dirty & DIRTY_LOCAL_TRANSFORM) { - data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(order); + data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(p_order); transform_changed = true; } else { data.dirty |= DIRTY_LOCAL_TRANSFORM; transform_changed = true; } - data.euler_rotation_order = order; + data.euler_rotation_order = p_order; if (transform_changed) { _propagate_transform_changed(this); @@ -423,8 +421,8 @@ void Node3D::set_rotation_order(RotationOrder p_order) { notify_property_list_changed(); // Will change the rotation property. } -Node3D::RotationOrder Node3D::get_rotation_order() const { - return RotationOrder(data.euler_rotation_order); +EulerOrder Node3D::get_rotation_order() const { + return data.euler_rotation_order; } void Node3D::set_rotation(const Vector3 &p_euler_rad) { @@ -1042,13 +1040,6 @@ void Node3D::_bind_methods() { BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION); BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_BASIS); - BIND_ENUM_CONSTANT(ROTATION_ORDER_XYZ); - BIND_ENUM_CONSTANT(ROTATION_ORDER_XZY); - BIND_ENUM_CONSTANT(ROTATION_ORDER_YXZ); - BIND_ENUM_CONSTANT(ROTATION_ORDER_YZX); - BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY); - BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX); - ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index f6868209bb..76c1e3e22c 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -61,15 +61,6 @@ public: ROTATION_EDIT_MODE_BASIS, }; - enum RotationOrder { - ROTATION_ORDER_XYZ, - ROTATION_ORDER_XZY, - ROTATION_ORDER_YXZ, - ROTATION_ORDER_YZX, - ROTATION_ORDER_ZXY, - ROTATION_ORDER_ZYX - }; - private: // For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaternion/Scale and Euler Rotation/Scale. // Transform and Quaternion are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand). @@ -179,7 +170,7 @@ public: void set_rotation_edit_mode(RotationEditMode p_mode); RotationEditMode get_rotation_edit_mode() const; - void set_rotation_order(RotationOrder p_order); + void set_rotation_order(EulerOrder p_order); void set_rotation(const Vector3 &p_euler_rad); void set_scale(const Vector3 &p_scale); @@ -188,7 +179,7 @@ public: Vector3 get_position() const; - RotationOrder get_rotation_order() const; + EulerOrder get_rotation_order() const; Vector3 get_rotation() const; Vector3 get_scale() const; @@ -277,6 +268,5 @@ public: }; VARIANT_ENUM_CAST(Node3D::RotationEditMode) -VARIANT_ENUM_CAST(Node3D::RotationOrder) #endif // NODE_3D_H diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp index a063cd344a..308fe057c5 100644 --- a/scene/gui/color_mode.cpp +++ b/scene/gui/color_mode.cpp @@ -158,8 +158,7 @@ void ColorModeHSV::slider_draw(int p_which) { right_color.a = 1; } else if (p_which == 0) { Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); - slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); - slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false); + slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false); return; } else { Color s_col; @@ -289,9 +288,8 @@ void ColorModeOKHSL::slider_draw(int p_which) { const real_t margin = 16 * color_picker->get_theme_default_base_scale(); if (p_which == 0) { // H - Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); - slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); - slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false); + Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_okhsl_hue"), SNAME("ColorPicker")); + slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false); return; } diff --git a/scene/resources/default_theme/color_picker_hue.svg b/scene/resources/default_theme/color_picker_hue.svg deleted file mode 100644 index ff75d5eb9e..0000000000 --- a/scene/resources/default_theme/color_picker_hue.svg +++ /dev/null @@ -1 +0,0 @@ -<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 1 256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(0 256 -256 0 0 0)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#f00"/><stop offset=".04" stop-color="#ff4000"/><stop offset=".08" stop-color="#ff8000"/><stop offset=".17" stop-color="#ff0"/><stop offset=".25" stop-color="#80ff00"/><stop offset=".33" stop-color="#0f0"/><stop offset=".42" stop-color="#00ff80"/><stop offset=".5" stop-color="#0ff"/><stop offset=".58" stop-color="#0080ff"/><stop offset=".63" stop-color="#0040ff"/><stop offset=".67" stop-color="#00f"/><stop offset=".75" stop-color="#8000ff"/><stop offset=".83" stop-color="#f0f"/><stop offset=".92" stop-color="#ff0080"/><stop offset="1" stop-color="#f00"/></linearGradient><path d="m0 0h1v256h-1z" fill="url(#a)"/></svg> diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index aa271e6f4b..894936acd7 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -887,12 +887,65 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("shape_rect", "ColorPicker", icons["picker_shape_rectangle"]); theme->set_icon("shape_rect_wheel", "ColorPicker", icons["picker_shape_rectangle_wheel"]); theme->set_icon("add_preset", "ColorPicker", icons["add"]); - theme->set_icon("color_hue", "ColorPicker", icons["color_picker_hue"]); theme->set_icon("sample_bg", "ColorPicker", icons["mini_checkerboard"]); theme->set_icon("overbright_indicator", "ColorPicker", icons["color_picker_overbright"]); theme->set_icon("bar_arrow", "ColorPicker", icons["color_picker_bar_arrow"]); theme->set_icon("picker_cursor", "ColorPicker", icons["color_picker_cursor"]); + { + const int precision = 7; + + Ref<Gradient> hue_gradient; + hue_gradient.instantiate(); + PackedFloat32Array offsets; + offsets.resize(precision); + PackedColorArray colors; + colors.resize(precision); + + for (int i = 0; i < precision; i++) { + float h = i / float(precision - 1); + offsets.write[i] = h; + colors.write[i] = Color::from_hsv(h, 1, 1); + } + hue_gradient->set_offsets(offsets); + hue_gradient->set_colors(colors); + + Ref<GradientTexture2D> hue_texture; + hue_texture.instantiate(); + hue_texture->set_width(800); + hue_texture->set_height(6); + hue_texture->set_gradient(hue_gradient); + + theme->set_icon("color_hue", "ColorPicker", hue_texture); + } + + { + const int precision = 7; + + Ref<Gradient> hue_gradient; + hue_gradient.instantiate(); + PackedFloat32Array offsets; + offsets.resize(precision); + PackedColorArray colors; + colors.resize(precision); + + for (int i = 0; i < precision; i++) { + float h = i / float(precision - 1); + offsets.write[i] = h; + colors.write[i] = Color::from_ok_hsl(h, 1, 0.5); + } + hue_gradient->set_offsets(offsets); + hue_gradient->set_colors(colors); + + Ref<GradientTexture2D> hue_texture; + hue_texture.instantiate(); + hue_texture->set_width(800); + hue_texture->set_height(6); + hue_texture->set_gradient(hue_gradient); + + theme->set_icon("color_okhsl_hue", "ColorPicker", hue_texture); + } + // ColorPickerButton theme->set_icon("bg", "ColorPickerButton", icons["mini_checkerboard"]); diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h index 909fc2499f..c3ae322991 100644 --- a/tests/core/math/test_quaternion.h +++ b/tests/core/math/test_quaternion.h @@ -169,10 +169,9 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") { Vector3 euler_r(0.0, 0.0, roll); Quaternion q_r = Quaternion::from_euler(euler_r); - // Roll-Z is followed by Pitch-X. - Quaternion check_xz = q_p * q_r; - // Then Yaw-Y follows both. - Quaternion check_yxz = q_y * check_xz; + // Instrinsically, Yaw-Y then Pitch-X then Roll-Z. + // Extrinsically, Roll-Z is followed by Pitch-X, then Yaw-Y. + Quaternion check_yxz = q_y * q_p * q_r; // Test construction from YXZ Euler angles. Vector3 euler_yxz(pitch, yaw, roll); @@ -182,8 +181,9 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") { CHECK(q[2] == doctest::Approx(check_yxz[2])); CHECK(q[3] == doctest::Approx(check_yxz[3])); - // Sneak in a test of is_equal_approx. CHECK(q.is_equal_approx(check_yxz)); + CHECK(q.get_euler().is_equal_approx(euler_yxz)); + CHECK(check_yxz.get_euler().is_equal_approx(euler_yxz)); } TEST_CASE("[Quaternion] Construct Basis Euler") { @@ -235,6 +235,23 @@ TEST_CASE("[Quaternion] Construct Basis Axes") { CHECK(q[3] == doctest::Approx(0.8582598)); } +TEST_CASE("[Quaternion] Get Euler Orders") { + double x = Math::deg_to_rad(30.0); + double y = Math::deg_to_rad(45.0); + double z = Math::deg_to_rad(10.0); + Vector3 euler(x, y, z); + for (int i = 0; i < 6; i++) { + EulerOrder order = (EulerOrder)i; + Basis basis = Basis::from_euler(euler, order); + Quaternion q = Quaternion(basis); + Vector3 check = q.get_euler(order); + CHECK_MESSAGE(check.is_equal_approx(euler), + "Quaternion get_euler method should return the original angles."); + CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)), + "Quaternion get_euler method should behave the same as Basis get_euler."); + } +} + TEST_CASE("[Quaternion] Product (book)") { // Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108. Quaternion p(1.0, -2.0, 1.0, 3.0); |