/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "gtypes.h" #include "util/math_fixed.h" //! @addtogroup Graphics //! @{ //! @addtogroup GraphicsTransforms Transformation Matrices //! \brief Types for creating transformation matrices and utility functions to manipulate and apply //! the transformations. //! //! @{ ////////////////////////////////////// /// Creating Transforms ////////////////////////////////////// //! Convenience macro for GTransformNumber equal to 0 #define GTransformNumberZero ((GTransformNumber){ .integer = 0, .fraction = 0 }) //! Convenience macro for GTransformNumber equal to 1 #define GTransformNumberOne ((GTransformNumber){ .integer = 1, .fraction = 0 }) //! Convenience macro to convert from a number (i.e. char, int, float, etc.) to GTransformNumber //! @param x The number to convert #define GTransformNumberFromNumber(x) \ ((GTransformNumber){ .raw_value = (int32_t)((x)*(GTransformNumberOne.raw_value)) }) //! This macro returns the transformation matrix for the corresponding input coefficients. //! Below is the equivalent resulting matrix: //! t = [ a b 0 ] //! [ c d 0 ] //! [ tx ty 1 ] //! @param a Coefficient corresponding to X scale (type is GTransformNumber) //! @param b Coefficient corresponding to X shear (type is GTransformNumber) //! @param c Coefficient corresponding to Y shear (type is GTransformNumber) //! @param d Coefficient corresponding to Y scale (type is GTransformNumber) //! @param tx Coefficient corresponding to X translation (type is GTransformNumber) //! @param ty Coefficient corresponding to Y translation (type is GTransformNumber) #define GTransform(a, b, c, d, tx, ty) (GTransform) { (a), (b), (c), (d), (tx), (ty) } //! @param a Coefficient corresponding to X scale (type is char, int, float, etc) //! @param b Coefficient corresponding to X shear (type is char, int, float, etc) //! @param c Coefficient corresponding to Y shear (type is char, int, float, etc) //! @param d Coefficient corresponding to Y scale (type is char, int, float, etc) //! @param tx Coefficient corresponding to X translation (type is char, int, float, etc) //! @param ty Coefficient corresponding to Y translation (type is char, int, float, etc) #define GTransformFromNumbers(a, b, c, d, tx, ty) GTransform(GTransformNumberFromNumber(a), \ GTransformNumberFromNumber(b), \ GTransformNumberFromNumber(c), \ GTransformNumberFromNumber(d), \ GTransformNumberFromNumber(tx), \ GTransformNumberFromNumber(ty)) //! This macro returns the identity transformation matrix. //! Below is the equivalent resulting matrix: //! t = [ 1 0 0 ] //! [ 0 1 0 ] //! [ 0 0 1 ] #define GTransformIdentity() \ (GTransform) { .a = GTransformNumberOne, \ .b = GTransformNumberZero, \ .c = GTransformNumberZero, \ .d = GTransformNumberOne, \ .tx = GTransformNumberZero, \ .ty = GTransformNumberZero } //! This macro returns a scaling transformation matrix for the corresponding input coefficients. //! Below is the equivalent resulting matrix: //! t = [ sx 0 0 ] //! [ 0 sy 0 ] //! [ 0 0 1 ] //! @param sx X scaling factor (type is GTransformNumber) //! @param sy Y scaling factor (type is GTransformNumber) #define GTransformScale(sx, sy) \ (GTransform) { .a = sx, \ .b = GTransformNumberZero, \ .c = GTransformNumberZero, \ .d = sy, \ .tx = GTransformNumberZero, \ .ty = GTransformNumberZero } //! @param sx X scaling factor (type is char, int, float, etc) //! @param sy Y scaling factor (type is char, int, float, etc) #define GTransformScaleFromNumber(sx, sy) \ GTransformScale(GTransformNumberFromNumber(sx), GTransformNumberFromNumber(sy)) //! This macro returns a translation transformation matrix for the corresponding input coefficients. //! Below is the equivalent resulting matrix: //! t = [ 1 0 0 ] //! [ 0 1 0 ] //! [ tx ty 1 ] //! @param tx_v X translation factor (type is GTransformNumber) //! @param ty_v Y translation factor (type is GTransformNumber) #define GTransformTranslation(tx_v, ty_v) \ (GTransform) { .a = GTransformNumberOne, \ .b = GTransformNumberZero, \ .c = GTransformNumberZero, \ .d = GTransformNumberOne, \ .tx = tx_v, \ .ty = ty_v } //! @param tx_v X translation factor (type is char, int, float, etc) //! @param ty_v Y translation factor (type is char, int, float, etc) #define GTransformTranslationFromNumber(tx, ty) \ GTransformTranslation(GTransformNumberFromNumber(tx), GTransformNumberFromNumber(ty)) //! @internal //! Function that returns the rotation matrix as defined below by GTransformRotation GTransform gtransform_init_rotation(int32_t angle); //! This macro returns the transformation matrix for the corresponding rotation angle. //! Below is the equivalent resulting matrix: //! t = [ cos(angle) -sin(angle) 0 ] //! [ sin(angle) cos(angle) 0 ] //! [ 0 0 1 ] // //! The input angle corresponds to the rotation angle applied during transformation. //! If this angle is set to 0, then the identity matrix is returned. //! @param angle Rotation angle to apply (type is in same format as trig angle 0..TRIG_MAX_ANGLE) #define GTransformRotation(angle) gtransform_init_rotation(angle) ////////////////////////////////////// /// Evaluating Transforms ////////////////////////////////////// //! Returns whether the input matrix is an identity matrix or not //! @param t Pointer to transformation matrix to test //! @return True if input matrix is identity; False if NULL or not identity. bool gtransform_is_identity(const GTransform * const t); //! Returns whether the input matrix is strictly a scaling matrix //! @param t Pointer to transformation matrix to test //! @return True if input matrix is only scaling X or Y; False if NULL or other coefficients set. bool gtransform_is_only_scale(const GTransform * const t); //! Returns whether the input matrix is strictly a translation matrix //! @param t Pointer to transformation matrix to test //! @return True if input matrix is only translating X or Y; False if NULL or other //! coefficients set. bool gtransform_is_only_translation(const GTransform * const t); //! Returns whether the input matrix has coefficients b and c set to 0. //! This does not check whether any other coefficients are set or not. //! @param t Pointer to transformation matrix to test //! @return True if input matrix is only scaling or translating X or Y; False if NULL or other. //! coefficients set. bool gtransform_is_only_scale_or_translation(const GTransform * const t); //! Returns true if the two matrices are equal; false otherwise //! Returns false if either parameter is NULL //! @param t1 Pointer to first transformation matrix //! @param t2 Pointer to second transformation matrix //! @return True if both matrices are equal; False if any are NULL or if not equal. bool gtransform_is_equal(const GTransform * const t1, const GTransform * const t2); ////////////////////////////////////// /// Modifying Transforms ////////////////////////////////////// //! Concatenates two transformation matrices and returns the resulting matrix in t1 //! The operation performed is t_new = t1*t2. This order is not commutative so be careful //! when concatenating the matrices. //! Note t_new can safely be be the same pointer as t1 or t2. //! @param t_new Pointer to destination transformation matrix //! @param t1 Pointer to transformation matrix to concatenate with t2 where t_new = t1*t2 //! @param t2 Pointer to transformation matrix to concatenate with t1 where t_new = t1*t2 void gtransform_concat(GTransform *t_new, const GTransform *t1, const GTransform * t2); //! Updates the input transformation matrix by applying a translation. //! This results in applying the following matrix below (i.e. t_new = t_scale*t): //! t_scale = [ sx 0 0 ] //! [ 0 sy 0 ] //! [ 0 0 1 ] //! Note t_new can safely be be the same pointer as t. //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be scaled //! @param sx X scaling factor //! @param sy Y scaling factor void gtransform_scale(GTransform *t_new, GTransform *t, GTransformNumber sx, GTransformNumber sy); //! Similar to gtransform_scale but with native number types (i.e. char, int, float, etc) //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be scaled //! @param sx X scaling factor (type is char, int, float, etc) //! @param sy Y scaling factor (type is char, int, float, etc) #define gtransform_scale_number(t_new, t, sx, sy) \ gtransform_scale(t_new, t, \ GTransformNumberFromNumber(sx), GTransformNumberFromNumber(sy)) //! Updates the input transformation matrix by applying a translation. //! This results in applying the following matrix below (i.e. t_new = t_translation*t): //! t_translation = [ 1 0 0 ] //! [ 0 1 0 ] //! [ tx ty 1 ] //! Note t_new can safely be be the same pointer as t. //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be translated //! @param tx X translation factor //! @param ty Y translation factor void gtransform_translate(GTransform *t_new, GTransform *t, GTransformNumber tx, GTransformNumber ty); //! Similar to gtransform_translate but with native number types (i.e. char, int, float, etc) //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be translated //! @param tx X translation factor (type is char, int, float, etc) //! @param ty Y translation factor (type is char, int, float, etc) #define gtransform_translate_number(t_new, t, tx, ty) \ gtransform_translate(t_new, t, \ GTransformNumberFromNumber(tx), GTransformNumberFromNumber(ty)) //! Updates the input transformation matrix by applying a rotation of angle degrees. //! This results in applying the following matrix below (i.e. t_new = tr*t): //! tr = [ cos(angle) -sin(angle) 0 ] //! [ sin(angle) cos(angle) 0 ] //! [ 0 0 1 ] //! Note t_new can safely be be the same pointer as t. //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be rotated //! @param angle Rotation angle to apply (type is in same format as trig angle 0..TRIG_MAX_ANGLE) void gtransform_rotate(GTransform *t_new, GTransform *t, int32_t angle); //! Returns the inversion of a given transformation matrix t in t_new. //! Function returns true if operation is successful; false if the matrix cannot be inverted //! If the matrix cannot be inverted, then the contents of t will be copied to t_new. //! Note t_new can safely be be the same pointer as t. //! @param t_new Pointer to destination transformation matrix //! @param t Pointer to transformation matrix that will be inverted //! @return True if inversion of input t matrix exists; False otherwise or if t is NULL. bool gtransform_invert(GTransform *t_new, GTransform *t); ////////////////////////////////////// /// Applying Transformations ////////////////////////////////////// //! Transforms a single GPoint (x,y) based on the transformation matrix //! @param point GPoint to be transformed //! @param t Pointer to transformation matrix to apply to the GPoint //! @return GPointPrecise after transforming the GPoint; if t is NULL then just convert the //! GPoint to a GPointPrecise. GPointPrecise gpoint_transform(GPoint point, const GTransform * const t); //! Transforms a single GVector (dx,dy) based on the transformation matrix //! @param point GVector to be transformed //! @param t Pointer to transformation matrix to apply to the GVector //! @return GVectorPrecise after transforming the GVector; if t is NULL then just convert the //! GVector to a GVectorPrecise. GVectorPrecise gvector_transform(GVector vector, const GTransform * const t); //! @} // end addtogroup GraphicsTransforms //! @} // end addtogroup Graphics