From efb1f7d76b63d56fc0c097d13b4b6d7b91250611 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Fri, 1 May 2020 13:38:04 -0300 Subject: [PATCH] Implement Skew in Node2D Skew is x-axis only, because it must be bidirectionally convertible to a 2x3 matrix, but you can subtract it to the rotation to get the effect on y-axis --- core/math/transform_2d.cpp | 12 +++++++++++ core/math/transform_2d.h | 11 ++++++++++ scene/2d/node_2d.cpp | 42 ++++++++++++++++++++++++++++++++++++-- scene/2d/node_2d.h | 5 +++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 97a9216a5ac..ed95baa233c 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -70,6 +70,18 @@ void Transform2D::rotate(real_t p_phi) { *this = Transform2D(p_phi, Vector2()) * (*this); } +real_t Transform2D::get_skew() const { + + real_t det = basis_determinant(); + return Math::acos(elements[0].normalized().dot(SGN(det) * elements[1].normalized())) - Math_PI * 0.5; +} + +void Transform2D::set_skew(float p_angle) { + + real_t det = basis_determinant(); + elements[1] = SGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length(); +} + real_t Transform2D::get_rotation() const { real_t det = basis_determinant(); Transform2D m = orthonormalized(); diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index fa43762aa4b..459ceed7a95 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -70,7 +70,10 @@ struct Transform2D { void set_rotation(real_t p_rot); real_t get_rotation() const; + real_t get_skew() const; + void set_skew(float p_angle); _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); + _FORCE_INLINE_ void set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew); void rotate(real_t p_phi); void scale(const Size2 &p_scale); @@ -184,6 +187,14 @@ void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { elements[0][1] = Math::sin(p_rot) * p_scale.x; } +void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew) { + + elements[0][0] = Math::cos(p_rot) * p_scale.x; + elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; + elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; + elements[0][1] = Math::sin(p_rot) * p_scale.x; +} + Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { Vector2 ends[4] = { diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index ac8a77b6cb3..1ea51be1482 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -42,6 +42,7 @@ Dictionary Node2D::_edit_get_state() const { state["position"] = get_position(); state["rotation"] = get_rotation(); state["scale"] = get_scale(); + state["skew"] = get_skew(); return state; } @@ -51,11 +52,14 @@ void Node2D::_edit_set_state(const Dictionary &p_state) { pos = p_state["position"]; angle = p_state["rotation"]; _scale = p_state["scale"]; + skew = p_state["skew"]; _update_transform(); _change_notify("rotation"); _change_notify("rotation_degrees"); _change_notify("scale"); + _change_notify("skew"); + _change_notify("skew_degrees"); _change_notify("position"); } @@ -111,7 +115,7 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset; Transform2D postxf; - postxf.set_rotation_and_scale(angle, _scale); + postxf.set_rotation_scale_and_skew(angle, _scale, skew); new_pos = postxf.xform(new_pos); pos += new_pos; @@ -128,12 +132,13 @@ void Node2D::_update_xform_values() { pos = _mat.elements[2]; angle = _mat.get_rotation(); _scale = _mat.get_scale(); + skew = _mat.get_skew(); _xform_dirty = false; } void Node2D::_update_transform() { - _mat.set_rotation_and_scale(angle, _scale); + _mat.set_rotation_scale_and_skew(angle, _scale, skew); _mat.elements[2] = pos; RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat); @@ -163,11 +168,26 @@ void Node2D::set_rotation(float p_radians) { _change_notify("rotation_degrees"); } +void Node2D::set_skew(float p_radians) { + + if (_xform_dirty) + ((Node2D *)this)->_update_xform_values(); + skew = p_radians; + _update_transform(); + _change_notify("skew"); + _change_notify("skew_degrees"); +} + void Node2D::set_rotation_degrees(float p_degrees) { set_rotation(Math::deg2rad(p_degrees)); } +void Node2D::set_skew_degrees(float p_degrees) { + + set_skew(Math::deg2rad(p_degrees)); +} + void Node2D::set_scale(const Size2 &p_scale) { if (_xform_dirty) @@ -196,11 +216,22 @@ float Node2D::get_rotation() const { return angle; } +float Node2D::get_skew() const { + if (_xform_dirty) + ((Node2D *)this)->_update_xform_values(); + + return skew; +} + float Node2D::get_rotation_degrees() const { return Math::rad2deg(get_rotation()); } +float Node2D::get_skew_degrees() const { + + return Math::rad2deg(get_skew()); +} Size2 Node2D::get_scale() const { if (_xform_dirty) ((Node2D *)this)->_update_xform_values(); @@ -398,11 +429,15 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation); ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Node2D::set_rotation_degrees); + ClassDB::bind_method(D_METHOD("set_skew", "radians"), &Node2D::set_skew); + ClassDB::bind_method(D_METHOD("set_skew_degrees", "degrees"), &Node2D::set_skew_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node2D::set_scale); ClassDB::bind_method(D_METHOD("get_position"), &Node2D::get_position); ClassDB::bind_method(D_METHOD("get_rotation"), &Node2D::get_rotation); ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node2D::get_rotation_degrees); + ClassDB::bind_method(D_METHOD("get_skew"), &Node2D::get_skew); + ClassDB::bind_method(D_METHOD("get_skew_degrees"), &Node2D::get_skew_degrees); ClassDB::bind_method(D_METHOD("get_scale"), &Node2D::get_scale); ClassDB::bind_method(D_METHOD("rotate", "radians"), &Node2D::rotate); @@ -443,6 +478,8 @@ void Node2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_skew", "get_skew"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew_degrees", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1", PROPERTY_USAGE_EDITOR), "set_skew_degrees", "get_skew_degrees"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", 0), "set_transform", "get_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); @@ -460,6 +497,7 @@ Node2D::Node2D() { angle = 0; _scale = Vector2(1, 1); + skew = 0; _xform_dirty = false; z_index = 0; z_relative = true; diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index abed05ed0c6..0afec36254e 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -40,6 +40,7 @@ class Node2D : public CanvasItem { Point2 pos; float angle; Size2 _scale; + float skew; int z_index; bool z_relative; @@ -75,6 +76,8 @@ public: void set_position(const Point2 &p_pos); void set_rotation(float p_radians); void set_rotation_degrees(float p_degrees); + void set_skew(float p_radians); + void set_skew_degrees(float p_radians); void set_scale(const Size2 &p_scale); void rotate(float p_radians); @@ -86,7 +89,9 @@ public: Point2 get_position() const; float get_rotation() const; + float get_skew() const; float get_rotation_degrees() const; + float get_skew_degrees() const; Size2 get_scale() const; Point2 get_global_position() const;