Browse Source

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
Juan Linietsky 5 years ago
parent
commit
efb1f7d76b
4 changed files with 68 additions and 2 deletions
  1. 12 0
      core/math/transform_2d.cpp
  2. 11 0
      core/math/transform_2d.h
  3. 40 2
      scene/2d/node_2d.cpp
  4. 5 0
      scene/2d/node_2d.h

+ 12 - 0
core/math/transform_2d.cpp

@@ -70,6 +70,18 @@ void Transform2D::rotate(real_t p_phi) {
 	*this = Transform2D(p_phi, Vector2()) * (*this);
 	*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 Transform2D::get_rotation() const {
 	real_t det = basis_determinant();
 	real_t det = basis_determinant();
 	Transform2D m = orthonormalized();
 	Transform2D m = orthonormalized();

+ 11 - 0
core/math/transform_2d.h

@@ -70,7 +70,10 @@ struct Transform2D {
 
 
 	void set_rotation(real_t p_rot);
 	void set_rotation(real_t p_rot);
 	real_t get_rotation() const;
 	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_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 rotate(real_t p_phi);
 
 
 	void scale(const Size2 &p_scale);
 	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;
 	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 {
 Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const {
 
 
 	Vector2 ends[4] = {
 	Vector2 ends[4] = {

+ 40 - 2
scene/2d/node_2d.cpp

@@ -42,6 +42,7 @@ Dictionary Node2D::_edit_get_state() const {
 	state["position"] = get_position();
 	state["position"] = get_position();
 	state["rotation"] = get_rotation();
 	state["rotation"] = get_rotation();
 	state["scale"] = get_scale();
 	state["scale"] = get_scale();
+	state["skew"] = get_skew();
 
 
 	return state;
 	return state;
 }
 }
@@ -51,11 +52,14 @@ void Node2D::_edit_set_state(const Dictionary &p_state) {
 	pos = p_state["position"];
 	pos = p_state["position"];
 	angle = p_state["rotation"];
 	angle = p_state["rotation"];
 	_scale = p_state["scale"];
 	_scale = p_state["scale"];
+	skew = p_state["skew"];
 
 
 	_update_transform();
 	_update_transform();
 	_change_notify("rotation");
 	_change_notify("rotation");
 	_change_notify("rotation_degrees");
 	_change_notify("rotation_degrees");
 	_change_notify("scale");
 	_change_notify("scale");
+	_change_notify("skew");
+	_change_notify("skew_degrees");
 	_change_notify("position");
 	_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;
 	Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset;
 
 
 	Transform2D postxf;
 	Transform2D postxf;
-	postxf.set_rotation_and_scale(angle, _scale);
+	postxf.set_rotation_scale_and_skew(angle, _scale, skew);
 	new_pos = postxf.xform(new_pos);
 	new_pos = postxf.xform(new_pos);
 
 
 	pos += new_pos;
 	pos += new_pos;
@@ -128,12 +132,13 @@ void Node2D::_update_xform_values() {
 	pos = _mat.elements[2];
 	pos = _mat.elements[2];
 	angle = _mat.get_rotation();
 	angle = _mat.get_rotation();
 	_scale = _mat.get_scale();
 	_scale = _mat.get_scale();
+	skew = _mat.get_skew();
 	_xform_dirty = false;
 	_xform_dirty = false;
 }
 }
 
 
 void Node2D::_update_transform() {
 void Node2D::_update_transform() {
 
 
-	_mat.set_rotation_and_scale(angle, _scale);
+	_mat.set_rotation_scale_and_skew(angle, _scale, skew);
 	_mat.elements[2] = pos;
 	_mat.elements[2] = pos;
 
 
 	RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
 	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");
 	_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) {
 void Node2D::set_rotation_degrees(float p_degrees) {
 
 
 	set_rotation(Math::deg2rad(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) {
 void Node2D::set_scale(const Size2 &p_scale) {
 
 
 	if (_xform_dirty)
 	if (_xform_dirty)
@@ -196,11 +216,22 @@ float Node2D::get_rotation() const {
 	return angle;
 	return angle;
 }
 }
 
 
+float Node2D::get_skew() const {
+	if (_xform_dirty)
+		((Node2D *)this)->_update_xform_values();
+
+	return skew;
+}
+
 float Node2D::get_rotation_degrees() const {
 float Node2D::get_rotation_degrees() const {
 
 
 	return Math::rad2deg(get_rotation());
 	return Math::rad2deg(get_rotation());
 }
 }
 
 
+float Node2D::get_skew_degrees() const {
+
+	return Math::rad2deg(get_skew());
+}
 Size2 Node2D::get_scale() const {
 Size2 Node2D::get_scale() const {
 	if (_xform_dirty)
 	if (_xform_dirty)
 		((Node2D *)this)->_update_xform_values();
 		((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_position", "position"), &Node2D::set_position);
 	ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation);
 	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_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("set_scale", "scale"), &Node2D::set_scale);
 
 
 	ClassDB::bind_method(D_METHOD("get_position"), &Node2D::get_position);
 	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"), &Node2D::get_rotation);
 	ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node2D::get_rotation_degrees);
 	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("get_scale"), &Node2D::get_scale);
 
 
 	ClassDB::bind_method(D_METHOD("rotate", "radians"), &Node2D::rotate);
 	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", 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::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::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::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");
 	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;
 	angle = 0;
 	_scale = Vector2(1, 1);
 	_scale = Vector2(1, 1);
+	skew = 0;
 	_xform_dirty = false;
 	_xform_dirty = false;
 	z_index = 0;
 	z_index = 0;
 	z_relative = true;
 	z_relative = true;

+ 5 - 0
scene/2d/node_2d.h

@@ -40,6 +40,7 @@ class Node2D : public CanvasItem {
 	Point2 pos;
 	Point2 pos;
 	float angle;
 	float angle;
 	Size2 _scale;
 	Size2 _scale;
+	float skew;
 	int z_index;
 	int z_index;
 	bool z_relative;
 	bool z_relative;
 
 
@@ -75,6 +76,8 @@ public:
 	void set_position(const Point2 &p_pos);
 	void set_position(const Point2 &p_pos);
 	void set_rotation(float p_radians);
 	void set_rotation(float p_radians);
 	void set_rotation_degrees(float p_degrees);
 	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 set_scale(const Size2 &p_scale);
 
 
 	void rotate(float p_radians);
 	void rotate(float p_radians);
@@ -86,7 +89,9 @@ public:
 
 
 	Point2 get_position() const;
 	Point2 get_position() const;
 	float get_rotation() const;
 	float get_rotation() const;
+	float get_skew() const;
 	float get_rotation_degrees() const;
 	float get_rotation_degrees() const;
+	float get_skew_degrees() const;
 	Size2 get_scale() const;
 	Size2 get_scale() const;
 
 
 	Point2 get_global_position() const;
 	Point2 get_global_position() const;