33use lambda_platform:: rand:: get_uniformly_random_floats_between;
44
55use super :: vector:: Vector ;
6+ use crate :: {
7+ assert_approximately_equal,
8+ math:: turns_to_radians,
9+ } ;
610
711// -------------------------------- MATRIX -------------------------------------
812
@@ -77,6 +81,70 @@ pub fn translation_matrix<
7781 return result;
7882}
7983
84+ /// Rotates the input matrix by the given number of turns around the given axis.
85+ /// The axis must be a unit vector and the turns must be in the range [0, 1).
86+ /// The rotation is counter-clockwise when looking down the axis.
87+ pub fn rotate_matrix <
88+ InputVector : Vector < Scalar = f32 > ,
89+ ResultingVector : Vector < Scalar = f32 > ,
90+ OutputMatrix : Matrix < ResultingVector > + Default + Clone ,
91+ > (
92+ matrix_to_rotate : OutputMatrix ,
93+ axis_to_rotate : InputVector ,
94+ angle_in_turns : f32 ,
95+ ) -> OutputMatrix {
96+ let ( rows, columns) = matrix_to_rotate. size ( ) ;
97+ assert_eq ! ( rows, columns, "Matrix must be square" ) ;
98+ assert_eq ! ( rows, 4 , "Matrix must be 4x4" ) ;
99+ assert_eq ! (
100+ axis_to_rotate. size( ) ,
101+ 3 ,
102+ "Axis vector must have 3 elements (x, y, z)"
103+ ) ;
104+
105+ // Convert the angle from turns to radians
106+ let angle_in_radians = turns_to_radians ( angle_in_turns) ;
107+ let cosine_of_angle = angle_in_radians. cos ( ) ;
108+ let sin_of_angle = -angle_in_radians. sin ( ) ;
109+
110+ let t = 1.0 - cosine_of_angle;
111+ let x = axis_to_rotate. at ( 0 ) ;
112+ let y = axis_to_rotate. at ( 1 ) ;
113+ let z = axis_to_rotate. at ( 2 ) ;
114+
115+ let mut rotation_matrix = OutputMatrix :: default ( ) ;
116+
117+ let rotation = [
118+ [
119+ t * x * x + cosine_of_angle,
120+ t * x * y - ( sin_of_angle * z) ,
121+ t * x * z + ( sin_of_angle * y) ,
122+ 0.0 ,
123+ ] ,
124+ [
125+ t * x * y + ( sin_of_angle * z) ,
126+ t * y * y + cosine_of_angle,
127+ t * y * z - ( sin_of_angle * x) ,
128+ 0.0 ,
129+ ] ,
130+ [
131+ t * x * z - ( sin_of_angle * y) ,
132+ t * y * z - ( sin_of_angle * x) ,
133+ t * z * z + cosine_of_angle,
134+ 0.0 ,
135+ ] ,
136+ [ 0.0 , 0.0 , 0.0 , 1.0 ] ,
137+ ] ;
138+
139+ for i in 0 ..rows {
140+ for j in 0 ..columns {
141+ rotation_matrix. update ( i, j, rotation[ i] [ j] ) ;
142+ }
143+ }
144+
145+ return matrix_to_rotate. multiply ( & rotation_matrix) ;
146+ }
147+
80148/// Creates a 4x4 perspective matrix given the fov in turns (unit between
81149/// 0..2pi radians), aspect ratio, near clipping plane (also known as z_near),
82150/// and far clipping plane (also known as z_far). Enforces that the matrix being
@@ -98,7 +166,7 @@ pub fn perspective_matrix<
98166 "Matrix must be square to be a perspective matrix"
99167 ) ;
100168 debug_assert_eq ! ( rows, 4 , "Matrix must be 4x4 to be a perspective matrix" ) ;
101- let fov_in_radians = fov * std :: f32 :: consts :: PI * 2.0 ;
169+ let fov_in_radians = turns_to_radians ( fov) ;
102170 let f = 1.0 / ( fov_in_radians / 2.0 ) . tan ( ) ;
103171 let range = near_clipping_plane - far_clipping_plane;
104172
@@ -115,9 +183,70 @@ pub fn perspective_matrix<
115183 return result;
116184}
117185
186+ pub fn zeroed_matrix <
187+ V : Vector < Scalar = f32 > ,
188+ MatrixLike : Matrix < V > + Default ,
189+ > (
190+ rows : usize ,
191+ columns : usize ,
192+ ) -> MatrixLike {
193+ let mut result = MatrixLike :: default ( ) ;
194+ for i in 0 ..rows {
195+ for j in 0 ..columns {
196+ result. update ( i, j, 0.0 ) ;
197+ }
198+ }
199+ return result;
200+ }
201+
202+ /// Creates a new matrix with the given number of rows and columns, and fills it
203+ /// with the given value.
204+ pub fn filled_matrix <
205+ V : Vector < Scalar = f32 > ,
206+ MatrixLike : Matrix < V > + Default ,
207+ > (
208+ rows : usize ,
209+ columns : usize ,
210+ value : V :: Scalar ,
211+ ) -> MatrixLike {
212+ let mut result = MatrixLike :: default ( ) ;
213+ for i in 0 ..rows {
214+ for j in 0 ..columns {
215+ result. update ( i, j, value) ;
216+ }
217+ }
218+ return result;
219+ }
220+
221+ pub fn identity_matrix <
222+ V : Vector < Scalar = f32 > ,
223+ MatrixLike : Matrix < V > + Default ,
224+ > (
225+ rows : usize ,
226+ columns : usize ,
227+ ) -> MatrixLike {
228+ assert_eq ! (
229+ rows, columns,
230+ "Matrix must be square to be an identity matrix"
231+ ) ;
232+ let mut result = MatrixLike :: default ( ) ;
233+ for i in 0 ..rows {
234+ for j in 0 ..columns {
235+ if i == j {
236+ result. update ( i, j, 1.0 ) ;
237+ } else {
238+ result. update ( i, j, 0.0 ) ;
239+ }
240+ }
241+ }
242+ return result;
243+ }
244+
118245// -------------------------- ARRAY IMPLEMENTATION -----------------------------
119246
120- /// Matrix implementations for arrays backed by vectors.
247+ /// Matrix implementations for arrays of f32 arrays. Including the trait Matrix into
248+ /// your code will allow you to use these function implementation for any array
249+ /// of f32 arrays.
121250impl < Array , V > Matrix < V > for Array
122251where
123252 Array : AsMut < [ V ] > + AsRef < [ V ] > + Default ,
@@ -244,11 +373,16 @@ where
244373mod tests {
245374
246375 use super :: {
376+ filled_matrix,
247377 perspective_matrix,
378+ rotate_matrix,
248379 submatrix,
249380 Matrix ,
250381 } ;
251- use crate :: math:: matrix:: translation_matrix;
382+ use crate :: math:: {
383+ matrix:: translation_matrix,
384+ turns_to_radians,
385+ } ;
252386
253387 #[ test]
254388 fn square_matrix_add ( ) {
@@ -336,8 +470,8 @@ mod tests {
336470 perspective_matrix ( 1.0 / 4.0 , 1.0 , 1.0 , 0.0 ) ;
337471
338472 // Compute the field of view values used by the perspective matrix by hand.
339- let fov_radians = ( 1.0 / 4.0 ) * std :: f32 :: consts :: PI * 2.0 ;
340- let f = 1.0 / ( fov_radians as f32 / 2.0 ) . tan ( ) ;
473+ let fov_radians = turns_to_radians ( 1.0 / 4.0 ) ;
474+ let f = 1.0 / ( fov_radians / 2.0 ) . tan ( ) ;
341475
342476 let expected: [ [ f32 ; 4 ] ; 4 ] = [
343477 [ f, 0.0 , 0.0 , 0.0 ] ,
@@ -348,4 +482,36 @@ mod tests {
348482
349483 assert_eq ! ( perspective, expected) ;
350484 }
485+
486+ #[ test]
487+ fn rotate_matrices ( ) {
488+ let rotation_matrix: [ [ f32 ; 4 ] ; 4 ] = filled_matrix ( 4 , 4 , 1.0 ) ;
489+ let rotated_matrix = rotate_matrix ( rotation_matrix, [ 0.0 , 0.0 , 1.0 ] , 0.0 ) ;
490+ assert_eq ! ( rotated_matrix, rotation_matrix) ;
491+
492+ let matrix = [
493+ [ 1.0 , 2.0 , 3.0 , 4.0 ] ,
494+ [ 5.0 , 6.0 , 7.0 , 8.0 ] ,
495+ [ 9.0 , 10.0 , 11.0 , 12.0 ] ,
496+ [ 13.0 , 14.0 , 15.0 , 16.0 ] ,
497+ ] ;
498+ let rotated = rotate_matrix ( matrix, [ 0.0 , 1.0 , 0.0 ] , 0.25 ) ;
499+ let expected = [
500+ [ 3.0 , 1.9999999 , -1.0000001 , 4.0 ] ,
501+ [ 7.0 , 5.9999995 , -5.0000005 , 8.0 ] ,
502+ [ 11.0 , 9.999999 , -9.000001 , 12.0 ] ,
503+ [ 14.999999 , 13.999999 , -13.000001 , 16.0 ] ,
504+ ] ;
505+
506+ println ! ( "rotated: {:?}" , rotated) ;
507+ for i in 0 ..4 {
508+ for j in 0 ..4 {
509+ crate :: assert_approximately_equal!(
510+ rotated. at( i, j) ,
511+ expected. at( i, j) ,
512+ 0.1
513+ ) ;
514+ }
515+ }
516+ }
351517}
0 commit comments