2018-08-11 06:40:44 +02:00
/**************************************************************************/
/* transform_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# ifndef TRANSFORM_2D_H
# define TRANSFORM_2D_H
2022-02-04 13:28:02 +01:00
# include "core/math/math_funcs.h"
# include "core/math/rect2.h"
# include "core/math/vector2.h"
2022-02-04 15:35:14 +01:00
# include "core/templates/vector.h"
2022-02-04 13:28:02 +01:00
class String ;
2018-08-11 06:40:44 +02:00
2022-01-19 20:46:17 +01:00
struct _NO_DISCARD_ Transform2D {
2022-04-24 23:59:24 +02:00
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper":
// M = (columns[0][0] columns[1][0])
// (columns[0][1] columns[1][1])
// This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i].
// Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here.
2018-08-11 06:40:44 +02:00
// This requires additional care when working with explicit indices.
// See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading.
// Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down,
// and angle is measure from +X to +Y in a clockwise-fashion.
2022-04-24 23:59:24 +02:00
Vector2 columns [ 3 ] ;
2018-08-11 06:40:44 +02:00
2022-04-24 23:59:24 +02:00
_FORCE_INLINE_ real_t tdotx ( const Vector2 & v ) const { return columns [ 0 ] [ 0 ] * v . x + columns [ 1 ] [ 0 ] * v . y ; }
_FORCE_INLINE_ real_t tdoty ( const Vector2 & v ) const { return columns [ 0 ] [ 1 ] * v . x + columns [ 1 ] [ 1 ] * v . y ; }
2018-08-11 06:40:44 +02:00
2022-04-24 23:59:24 +02:00
const Vector2 & operator [ ] ( int p_idx ) const { return columns [ p_idx ] ; }
Vector2 & operator [ ] ( int p_idx ) { return columns [ p_idx ] ; }
2018-08-11 06:40:44 +02:00
void invert ( ) ;
Transform2D inverse ( ) const ;
void affine_invert ( ) ;
Transform2D affine_inverse ( ) const ;
2021-09-02 11:36:19 +02:00
void set_rotation ( const real_t p_rot ) ;
2018-08-11 06:40:44 +02:00
real_t get_rotation ( ) const ;
2020-05-01 18:38:04 +02:00
real_t get_skew ( ) const ;
2021-09-02 11:36:19 +02:00
void set_skew ( const real_t p_angle ) ;
_FORCE_INLINE_ void set_rotation_and_scale ( const real_t p_rot , const Size2 & p_scale ) ;
_FORCE_INLINE_ void set_rotation_scale_and_skew ( const real_t p_rot , const Size2 & p_scale , const real_t p_skew ) ;
2022-05-05 13:25:04 +02:00
void rotate ( const real_t p_angle ) ;
2018-08-11 06:40:44 +02:00
void scale ( const Size2 & p_scale ) ;
void scale_basis ( const Size2 & p_scale ) ;
2022-07-16 11:47:54 +02:00
void translate_local ( const real_t p_tx , const real_t p_ty ) ;
void translate_local ( const Vector2 & p_translation ) ;
2018-08-11 06:40:44 +02:00
2023-04-21 08:21:59 +02:00
real_t determinant ( ) const ;
2018-08-11 06:40:44 +02:00
Size2 get_scale ( ) const ;
2019-04-19 20:54:33 +02:00
void set_scale ( const Size2 & p_scale ) ;
2018-08-11 06:40:44 +02:00
2022-04-24 23:59:24 +02:00
_FORCE_INLINE_ const Vector2 & get_origin ( ) const { return columns [ 2 ] ; }
_FORCE_INLINE_ void set_origin ( const Vector2 & p_origin ) { columns [ 2 ] = p_origin ; }
2018-08-11 06:40:44 +02:00
2022-07-30 12:17:33 +02:00
Transform2D scaled ( const Size2 & p_scale ) const ;
Transform2D scaled_local ( const Size2 & p_scale ) const ;
Transform2D translated ( const Vector2 & p_offset ) const ;
2022-07-16 11:47:54 +02:00
Transform2D translated_local ( const Vector2 & p_offset ) const ;
2022-05-05 13:25:04 +02:00
Transform2D rotated ( const real_t p_angle ) const ;
2022-07-30 12:17:33 +02:00
Transform2D rotated_local ( const real_t p_angle ) const ;
2018-08-11 06:40:44 +02:00
Transform2D untranslated ( ) const ;
void orthonormalize ( ) ;
Transform2D orthonormalized ( ) const ;
2023-09-07 16:43:22 +02:00
bool is_conformal ( ) const ;
2019-10-14 22:33:45 +02:00
bool is_equal_approx ( const Transform2D & p_transform ) const ;
2022-08-11 10:12:27 +02:00
bool is_finite ( ) const ;
2018-08-11 06:40:44 +02:00
New and improved IK system for Skeleton2D
This PR and commit adds a new IK system for 2D with the Skeleton2D node
that adds several new IK solvers, a way to control bones in a Skeleton2D
node similar to that in Skeleton3D. It also adds additional changes
and functionality.
This work was sponsored by GSoC 2020 and TwistedTwigleg.
Full list of changes:
* Adds a SkeletonModifier2D resource
* This resource is the base where all IK code is written and executed
* Has a function for clamping angles, since it is so commonly used
* Modifiers are unique when duplicated so it works with instancing
* Adds a SkeletonModifierStack2D resource
* This resource manages a series of SkeletonModification2Ds
* This is what the Skeleton2D directly interfaces with to make IK possible
* Adds SkeletonModifier2D resources for LookAt, CCDIK, FABRIK, Jiggle, and TwoBoneIK
* Each modification is in its own file
* There is also a SkeletonModifier2D resource that acts as a stack for using multiple stacks together
* Adds a PhysicalBone2D node
* Works similar to the PhysicalBone3D node, but uses a RigidBody2D node
* Changes to Skeleton2D listed below:
* Skeleton2D now holds a single SkeletonModificationStack2D for IK
* Skeleton2D now has a local_pose_override, which overrides the Bone2D position similar to how the overrides work in Skeleton3D
* Changes to Bone2D listed below:
* The default_length property has been changed to length. Length is the length of the bone to its child bone node
* New bone_angle property, which is the angle the bone has to its first child bone node
* Bone2D caches its transform when not modified by IK for IK interpolation purposes
* Bone2D draws its own editor gizmo, though this is stated to change in the future
* Changes to CanvasItemEditor listed below:
* Bone2D gizmo drawing code removed
* The 2D IK code is removed. Now Bone2D is the only bone system for 2D
* Transform2D now has a looking_at function for rotating to face a position
* Two new node notifications: NOTIFICATION_EDITOR_PRE_SAVE and NOTIFICATION_EDITOR_POST_SAVE
* These notifications only are called in the editor right before and after saving a scene
* Needed for not saving the IK position when executing IK in the editor
* Documentation for all the changes listed above.
2020-08-03 20:02:24 +02:00
Transform2D looking_at ( const Vector2 & p_target ) const ;
2018-08-11 06:40:44 +02:00
bool operator = = ( const Transform2D & p_transform ) const ;
bool operator ! = ( const Transform2D & p_transform ) const ;
void operator * = ( const Transform2D & p_transform ) ;
Transform2D operator * ( const Transform2D & p_transform ) const ;
2021-06-16 03:15:38 +02:00
void operator * = ( const real_t p_val ) ;
Transform2D operator * ( const real_t p_val ) const ;
2018-08-11 06:40:44 +02:00
2021-09-02 11:36:19 +02:00
Transform2D interpolate_with ( const Transform2D & p_transform , const real_t p_c ) const ;
2018-08-11 06:40:44 +02:00
_FORCE_INLINE_ Vector2 basis_xform ( const Vector2 & p_vec ) const ;
_FORCE_INLINE_ Vector2 basis_xform_inv ( const Vector2 & p_vec ) const ;
_FORCE_INLINE_ Vector2 xform ( const Vector2 & p_vec ) const ;
_FORCE_INLINE_ Vector2 xform_inv ( const Vector2 & p_vec ) const ;
_FORCE_INLINE_ Rect2 xform ( const Rect2 & p_rect ) const ;
_FORCE_INLINE_ Rect2 xform_inv ( const Rect2 & p_rect ) const ;
2020-02-17 22:06:54 +01:00
_FORCE_INLINE_ Vector < Vector2 > xform ( const Vector < Vector2 > & p_array ) const ;
_FORCE_INLINE_ Vector < Vector2 > xform_inv ( const Vector < Vector2 > & p_array ) const ;
2018-08-11 06:40:44 +02:00
operator String ( ) const ;
2021-09-02 11:36:19 +02:00
Transform2D ( const real_t xx , const real_t xy , const real_t yx , const real_t yy , const real_t ox , const real_t oy ) {
2022-04-24 23:59:24 +02:00
columns [ 0 ] [ 0 ] = xx ;
columns [ 0 ] [ 1 ] = xy ;
columns [ 1 ] [ 0 ] = yx ;
columns [ 1 ] [ 1 ] = yy ;
columns [ 2 ] [ 0 ] = ox ;
columns [ 2 ] [ 1 ] = oy ;
2018-08-11 06:40:44 +02:00
}
2020-11-09 04:19:09 +01:00
Transform2D ( const Vector2 & p_x , const Vector2 & p_y , const Vector2 & p_origin ) {
2022-04-24 23:59:24 +02:00
columns [ 0 ] = p_x ;
columns [ 1 ] = p_y ;
columns [ 2 ] = p_origin ;
2020-11-09 04:19:09 +01:00
}
2021-09-02 11:36:19 +02:00
Transform2D ( const real_t p_rot , const Vector2 & p_pos ) ;
Transform2D ( const real_t p_rot , const Size2 & p_scale , const real_t p_skew , const Vector2 & p_pos ) ;
2018-08-11 06:40:44 +02:00
Transform2D ( ) {
2022-04-24 23:59:24 +02:00
columns [ 0 ] [ 0 ] = 1.0 ;
columns [ 1 ] [ 1 ] = 1.0 ;
2018-08-11 06:40:44 +02:00
}
} ;
Vector2 Transform2D : : basis_xform ( const Vector2 & p_vec ) const {
return Vector2 (
tdotx ( p_vec ) ,
tdoty ( p_vec ) ) ;
}
Vector2 Transform2D : : basis_xform_inv ( const Vector2 & p_vec ) const {
return Vector2 (
2022-04-24 23:59:24 +02:00
columns [ 0 ] . dot ( p_vec ) ,
columns [ 1 ] . dot ( p_vec ) ) ;
2018-08-11 06:40:44 +02:00
}
Vector2 Transform2D : : xform ( const Vector2 & p_vec ) const {
return Vector2 (
tdotx ( p_vec ) ,
tdoty ( p_vec ) ) +
2022-04-24 23:59:24 +02:00
columns [ 2 ] ;
2018-08-11 06:40:44 +02:00
}
2020-05-14 14:29:06 +02:00
2018-08-11 06:40:44 +02:00
Vector2 Transform2D : : xform_inv ( const Vector2 & p_vec ) const {
2022-04-24 23:59:24 +02:00
Vector2 v = p_vec - columns [ 2 ] ;
2018-08-11 06:40:44 +02:00
return Vector2 (
2022-04-24 23:59:24 +02:00
columns [ 0 ] . dot ( v ) ,
columns [ 1 ] . dot ( v ) ) ;
2018-08-11 06:40:44 +02:00
}
2020-05-14 14:29:06 +02:00
2018-08-11 06:40:44 +02:00
Rect2 Transform2D : : xform ( const Rect2 & p_rect ) const {
2022-04-24 23:59:24 +02:00
Vector2 x = columns [ 0 ] * p_rect . size . x ;
Vector2 y = columns [ 1 ] * p_rect . size . y ;
2018-08-11 06:40:44 +02:00
Vector2 pos = xform ( p_rect . position ) ;
Rect2 new_rect ;
new_rect . position = pos ;
new_rect . expand_to ( pos + x ) ;
new_rect . expand_to ( pos + y ) ;
new_rect . expand_to ( pos + x + y ) ;
return new_rect ;
}
2021-09-02 11:36:19 +02:00
void Transform2D : : set_rotation_and_scale ( const real_t p_rot , const Size2 & p_scale ) {
2022-04-24 23:59:24 +02:00
columns [ 0 ] [ 0 ] = Math : : cos ( p_rot ) * p_scale . x ;
columns [ 1 ] [ 1 ] = Math : : cos ( p_rot ) * p_scale . y ;
columns [ 1 ] [ 0 ] = - Math : : sin ( p_rot ) * p_scale . y ;
columns [ 0 ] [ 1 ] = Math : : sin ( p_rot ) * p_scale . x ;
2018-08-11 06:40:44 +02:00
}
2021-09-02 11:36:19 +02:00
void Transform2D : : set_rotation_scale_and_skew ( const real_t p_rot , const Size2 & p_scale , const real_t p_skew ) {
2022-04-24 23:59:24 +02:00
columns [ 0 ] [ 0 ] = Math : : cos ( p_rot ) * p_scale . x ;
columns [ 1 ] [ 1 ] = Math : : cos ( p_rot + p_skew ) * p_scale . y ;
columns [ 1 ] [ 0 ] = - Math : : sin ( p_rot + p_skew ) * p_scale . y ;
columns [ 0 ] [ 1 ] = Math : : sin ( p_rot ) * p_scale . x ;
2020-05-01 18:38:04 +02:00
}
2018-08-11 06:40:44 +02:00
Rect2 Transform2D : : xform_inv ( const Rect2 & p_rect ) const {
Vector2 ends [ 4 ] = {
xform_inv ( p_rect . position ) ,
xform_inv ( Vector2 ( p_rect . position . x , p_rect . position . y + p_rect . size . y ) ) ,
xform_inv ( Vector2 ( p_rect . position . x + p_rect . size . x , p_rect . position . y + p_rect . size . y ) ) ,
xform_inv ( Vector2 ( p_rect . position . x + p_rect . size . x , p_rect . position . y ) )
} ;
Rect2 new_rect ;
new_rect . position = ends [ 0 ] ;
new_rect . expand_to ( ends [ 1 ] ) ;
new_rect . expand_to ( ends [ 2 ] ) ;
new_rect . expand_to ( ends [ 3 ] ) ;
return new_rect ;
}
2020-02-17 22:06:54 +01:00
Vector < Vector2 > Transform2D : : xform ( const Vector < Vector2 > & p_array ) const {
Vector < Vector2 > array ;
2019-08-29 12:20:10 +02:00
array . resize ( p_array . size ( ) ) ;
2020-02-17 22:06:54 +01:00
const Vector2 * r = p_array . ptr ( ) ;
Vector2 * w = array . ptrw ( ) ;
2019-08-29 12:20:10 +02:00
for ( int i = 0 ; i < p_array . size ( ) ; + + i ) {
w [ i ] = xform ( r [ i ] ) ;
}
return array ;
}
2020-02-17 22:06:54 +01:00
Vector < Vector2 > Transform2D : : xform_inv ( const Vector < Vector2 > & p_array ) const {
Vector < Vector2 > array ;
2019-08-29 12:20:10 +02:00
array . resize ( p_array . size ( ) ) ;
2020-02-17 22:06:54 +01:00
const Vector2 * r = p_array . ptr ( ) ;
Vector2 * w = array . ptrw ( ) ;
2019-08-29 12:20:10 +02:00
for ( int i = 0 ; i < p_array . size ( ) ; + + i ) {
w [ i ] = xform_inv ( r [ i ] ) ;
}
return array ;
}
2018-08-11 06:40:44 +02:00
# endif // TRANSFORM_2D_H