%%%-------------------------------------------------------------------
%%% erlrithmetician - linear algebra library for Erlang
%%% Copyright (C) 2026 E. G. Bland
%%%
%%% This program is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program. If not, see <https://www.gnu.org/licenses/>.
%%%-------------------------------------------------------------------
-module(transform3d).
-export([rotate_xy/1, rotate_xz/1, rotate_yz/1, rotate_axis/2, scale/3, translate/3]).
-spec rotate_xy(number()) -> mat:mat(float()).
-spec rotate_xz(number()) -> mat:mat(float()).
-spec rotate_yz(number()) -> mat:mat(float()).
-spec rotate_axis(vec:vec(number()), number()) -> mat:mat(float()).
-spec scale(number(), number(), number()) -> mat:mat(number()).
-spec translate(number(), number(), number()) -> mat:mat(number()).
rotate_xy(Theta) ->
Cos = math:cos(Theta),
Sin = math:sin(Theta),
mat:new(array:from_list([vec:new3(Cos, Sin, 0), vec:new3(-Sin, Cos, 0), vec:new3(0, 0, 1)])).
rotate_xz(Theta) ->
Cos = math:cos(Theta),
Sin = math:sin(Theta),
mat:new(array:from_list([vec:new3(Cos, 0, -Sin), vec:new3(0, 1, 0), vec:new3(Sin, 0, Cos)])).
rotate_yz(Theta) ->
Cos = math:cos(Theta),
Sin = math:sin(Theta),
mat:new(array:from_list([vec:new3(1, 0, 0), vec:new3(0, Cos, Sin), vec:new3(0, -Sin, Cos)])).
rotate_axis(Axis, Theta) ->
Dim = vec:dim(Axis),
case Dim of
3 ->
AxisNorm = vec:normalise(Axis),
X = vec:x(AxisNorm),
Y = vec:y(AxisNorm),
Z = vec:z(AxisNorm),
Cos = math:cos(Theta),
Sin = math:sin(Theta),
mat:new(array:from_list([
vec:new3(
Cos + (1 - Cos) * X * X,
(1 - Cos) * X * Y + Sin * Z,
(1 - Cos) * X * Z - Sin * Y
),
vec:new3(
(1 - Cos) * X * Y - Sin * Z,
Cos + (1 - Cos) * Y * Y,
(1 - Cos) * Y * Z + Sin * X
),
vec:new3(
(1 - Cos) * X * Z + Sin * Y,
(1 - Cos) * Y * Z - Sin * X,
Cos + (1 - Cos) * Z * Z
)
]));
_ -> error({ axis_not_3d, { axis_dim, Dim } })
end.
scale(Sx, Sy, Sz) ->
mat:new(array:from_list([vec:new3(Sx, 0, 0), vec:new3(0, Sy, 0), vec:new3(0, 0, Sz)])).
translate(Tx, Ty, Tz) ->
mat:new(array:from_list([vec:new4(1, 0, 0, 0), vec:new4(0, 1, 0, 0), vec:new4(0, 0, 1, 0), vec:new4(Tx, Ty, Tz, 1)])).