//
// Copyright 2016 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
////////////////////////////////////////////////////////////////////////
// This file is generated by a script.  Do not edit directly.  Edit the
// matrix4.template.h file to make changes.

{% extends "matrix.template.h" %}

{% block includes %}
#include "pxr/base/gf/homogeneous.h"
#include "pxr/base/gf/limits.h"
#include "pxr/base/gf/math.h"
#include "pxr/base/gf/vec3{{ SCL[0] }}.h"
{% endblock %}

{% block forwardDeclarations %}
{% if SCL == 'double' -%}
class GfRotation;
{% endif %}
class GfMatrix3{{ SCL[0] }};
{% endblock %}

{% block classDocs %}
/// <h3>3D Transformations</h3>
///
/// The following methods interpret a {{ MAT }} as a 3D
/// transformation: SetRotate(), SetScale(), SetTranslate(), SetLookAt(),
/// Factor(), ExtractTranslation(), ExtractRotation(), Transform(), TransformDir().
/// By convention, vectors are treated primarily as row vectors,
/// implying the following:
/// \li Transformation matrices are organized to deal with row
///        vectors, not column vectors. For example, the last row of a matrix
///        contains the translation amounts.
/// \li Each of the Set() methods below completely rewrites the
///        matrix; for example, SetTranslate() yields a matrix
///        which does nothing but translate.
/// \li When multiplying two transformation matrices, the matrix
///        on the left applies a more local transformation to a row
///        vector. For example, if R represents a rotation
///        matrix and T represents a translation matrix, the
///        product R*T will rotate a row vector, then translate
///        it.
{% endblock classDocs %}

{% block customConstructors %}
{% for S in SCALARS %}
    /// Constructor.  Initialize the matrix from {{ DIM }} row vectors of
    /// {{ S }}.  Each vector is expected to length {{ DIM }}.  If it is too
    /// big, only the first {{ DIM }} items will be used.  If it is too small,
    /// uninitialized elements will be filled in with the
    /// corresponding elements from an identity matrix.
    ///
    GF_API
    explicit {{ MAT }}(const std::vector<{{ S }}>& r0,
                        const std::vector<{{ S }}>& r1,
                        const std::vector<{{ S }}>& r2,
                        const std::vector<{{ S }}>& r3);

{% endfor %}
{% if SCL == 'double' %}
    /// Constructor. Initializes a transformation matrix to perform the
    /// indicated rotation and translation.
    GF_API
    {{ MAT }}(const GfRotation& rotate,
               const GfVec3{{ SCL[0] }}& translate);

{% endif %}
    /// Constructor. Initializes a transformation matrix to perform the
    /// indicated rotation and translation.
    GF_API
    {{ MAT }}(const GfMatrix3{{ SCL[0] }}& rotmx,
               const GfVec3{{ SCL[0] }}& translate);
{% endblock customConstructors %}

{% block customFunctions %}
    /// Sets a row of the matrix from a Vec3.
    /// The fourth element of the row is ignored.
    void SetRow3(int i, const GfVec3{{ SCL[0] }} & v) {
        _mtx[i][0] = v[0];
        _mtx[i][1] = v[1];
        _mtx[i][2] = v[2];
    }

    /// Gets a row of the matrix as a Vec3.
    GfVec3{{ SCL[0] }} GetRow3(int i) const {
        return GfVec3{{ SCL[0] }}(_mtx[i][0], _mtx[i][1], _mtx[i][2]);
    }

    /// Returns the determinant of the upper 3x3 matrix. This method is useful
    /// when the matrix describes a linear transformation such as a rotation or
    /// scale because the other values in the 4x4 matrix are not important.
    double GetDeterminant3() const {
        return _GetDeterminant3(0, 1, 2, 0, 1, 2);
    }

    /// Returns true, if the row vectors of the upper 3x3 matrix form an
    /// orthogonal basis. Note they do not have to be unit length for this
    /// test to return true.
    bool HasOrthogonalRows3() const {
        // XXX Should add GfAreOrthogonal(v0, v1, v2) (which also
        //     GfRotation::Decompose() could use).
        GfVec3{{ SCL[0] }} axis0(GetRow3(0)), axis1(GetRow3(1)), axis2(GetRow3(2));
        return (GfAbs(GfDot(axis0, axis1)) < GF_MIN_ORTHO_TOLERANCE && 
                GfAbs(GfDot(axis0, axis2)) < GF_MIN_ORTHO_TOLERANCE && 
                GfAbs(GfDot(axis1, axis2)) < GF_MIN_ORTHO_TOLERANCE);
    }

    /// Makes the matrix orthonormal in place. This is an iterative method
    /// that is much more stable than the previous cross/cross method.  If the
    /// iterative method does not converge, a warning is issued.
    ///
    /// Returns true if the iteration converged, false otherwise.  Leaves any
    /// translation part of the matrix unchanged.  If \a issueWarning is true,
    /// this method will issue a warning if the iteration does not converge,
    /// otherwise it will be silent.
    GF_API
    bool Orthonormalize(bool issueWarning=true);

    /// Returns an orthonormalized copy of the matrix.
    GF_API
    {{ MAT }} GetOrthonormalized(bool issueWarning=true) const;

    /// Returns the sign of the determinant of the upper 3x3 matrix, i.e. 1
    /// for a right-handed matrix, -1 for a left-handed matrix, and 0 for a
    /// singular matrix.
    GF_API
    double GetHandedness() const;

    /// Returns true if the vectors in the upper 3x3 matrix form a
    /// right-handed coordinate system.
    bool IsRightHanded() const {
        return GetHandedness() == 1.0;
    }

    /// Returns true if the vectors in the upper 3x3 matrix form a left-handed
    /// coordinate system.
    bool IsLeftHanded() const {
        return GetHandedness() == -1.0;
    }
{% endblock customFunctions %}

{% block customXformFunctions %}
    /// Sets matrix to specify a uniform scaling by \e scaleFactor.
    GF_API
    {{ MAT }}& SetScale({{ SCL }} scaleFactor);

    /// Returns the matrix with any scaling or shearing removed,
    /// leaving only the rotation and translation.
    /// If the matrix cannot be decomposed, returns the original matrix.
    GF_API
    {{ MAT }} RemoveScaleShear() const;
{% if SCL == 'double' %}

    /// \name 3D Transformation Utilities
    /// @{

    /// Sets the matrix to specify a rotation equivalent to \e rot,
    /// and clears the translation.
    GF_API
    {{ MAT }}& SetRotate(const GfRotation &rot);

    /// Sets the matrix to specify a rotation equivalent to \e rot,
    /// without clearing the translation.
    GF_API
    {{ MAT }}& SetRotateOnly(const GfRotation &rot);

    /// Sets the matrix to specify a rotation equivalent to \e mx,
    /// and clears the translation.
    GF_API
    {{ MAT }}& SetRotate(const GfMatrix3d &mx);

    /// Sets the matrix to specify a rotation equivalent to \e mx,
    /// without clearing the translation.
    GF_API
    {{ MAT }}& SetRotateOnly(const GfMatrix3d &mx);

    /// Sets the matrix to specify a nonuniform scaling in x, y, and z by
    /// the factors in vector \e scaleFactors.
    GF_API
    {{ MAT }}& SetScale(const GfVec3d &scaleFactors);

    /// Sets matrix to specify a translation by the vector \e trans,
    /// and clears the rotation.
    GF_API
    {{ MAT }}& SetTranslate(const GfVec3d &trans);

    /// Sets matrix to specify a translation by the vector \e trans,
    /// without clearing the rotation.
    GF_API
    {{ MAT }}& SetTranslateOnly(const GfVec3d &t);

    /// Sets matrix to specify a rotation by \e rotate and a
    /// translation by \e translate.
    GF_API
    {{ MAT }}& SetTransform(const GfRotation& rotate,
                             const GfVec3d& translate);

    /// Sets matrix to specify a rotation by \e rotmx and a
    /// translation by \e translate.
    GF_API
    {{ MAT }}& SetTransform(const GfMatrix3d& rotmx,
                             const GfVec3d& translate);

    /// Sets the matrix to specify a viewing matrix from parameters
    /// similar to those used by <c>gluLookAt(3G)</c>. \e eyePoint
    /// represents the eye point in world space. \e centerPoint
    /// represents the world-space center of attention. \e upDirection
    /// is a vector indicating which way is up.
    GF_API
    {{ MAT }}& SetLookAt(const GfVec3d &eyePoint,
                          const GfVec3d &centerPoint,
                          const GfVec3d &upDirection);

    /// Sets the matrix to specify a viewing matrix from a world-space
    /// \e eyePoint and a world-space rotation that rigidly rotates the
    /// orientation from its canonical frame, which is defined to be
    /// looking along the <c>-z</c> axis with the <c>+y</c> axis as the up
    /// direction.
    GF_API
    {{ MAT }}& SetLookAt(const GfVec3d &eyePoint,
                          const GfRotation &orientation);

    /// Factors the matrix into 5 components:
    /// \li <c>\e M = r * s * -r * u * t</c>
    /// where
    /// \li \e t is a translation.
    /// \li \e u and \e r are rotations, and \e -r is the transpose
    ///     (inverse) of \e r. The \e u matrix may contain shear
    ///     information.
    /// \li \e s is a scale.
    /// Any projection information could be returned in matrix \e p,
    /// but currently p is never modified.
    ///
    /// Returns \c false if the matrix is singular (as determined by \e eps).
    /// In that case, any zero scales in \e s are clamped to \e eps
    /// to allow computation of \e u.
    GF_API
    bool Factor({{ MAT }}* r, GfVec3d* s, {{ MAT }}* u,
                GfVec3d* t, {{ MAT }}* p,
                double eps = GF_MIN_VECTOR_LENGTH) const;

    /// Returns the translation part of the matrix, defined as the first three
    /// elements of the last row.
    GfVec3d ExtractTranslation() const {
        return GfVec3d(_mtx[3][0], _mtx[3][1], _mtx[3][2]);
    }

    /// Returns the rotation corresponding to this matrix. This works well
    /// only if the matrix represents a rotation.
    ///
    /// For good results, consider calling Orthonormalize() before calling
    /// this method.
    GF_API
    GfRotation ExtractRotation() const;

    /// Decompose the rotation corresponding to this matrix about 3 orthogonal
    /// axes.  If the axes are not orthogonal, warnings will be spewed.
    ///
    /// This is a convenience method that is equivalent to calling
    /// ExtractRotation().Decompose().
    GF_API
    GfVec3d DecomposeRotation(const GfVec3d &axis0,
                              const GfVec3d &axis1,
                              const GfVec3d &axis2) const;

    /// Returns the rotation corresponding to this matrix. This works well
    /// only if the matrix represents a rotation.
    ///
    /// For good results, consider calling Orthonormalize() before calling
    /// this method.
    GF_API
    GfMatrix3d ExtractRotationMatrix() const;

    /// Transforms the row vector \e vec by the matrix, returning the result.
    /// This treats the vector as a 4-component vector whose fourth component
    /// is 1.
    GfVec3d Transform(const GfVec3d &vec) const {
        return GfProject(GfVec4d(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }} + _mtx[3][0],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }} + _mtx[3][1],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }} + _mtx[3][2],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][3]", sep=" + ", num=3) }} + _mtx[3][3]));
    }

    /// Transforms the row vector \e vec by the matrix, returning the result.
    /// This treats the vector as a 4-component vector whose fourth component
    /// is 1. This is an overloaded method; it differs from the other version
    /// in that it returns a different value type.
    GfVec3f Transform(const GfVec3f &vec) const {
        return GfVec3f(GfProject(GfVec4d(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }} + _mtx[3][0],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }} + _mtx[3][1],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }} + _mtx[3][2],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][3]", sep=" + ", num=3) }} + _mtx[3][3])));
    }

    /// Transforms row vector \e vec by the matrix, returning the result. This
    /// treats the vector as a direction vector, so the translation
    /// information in the matrix is ignored. That is, it treats the vector as
    /// a 4-component vector whose fourth component is 0.
    GfVec3d TransformDir(const GfVec3d &vec) const {
        return GfVec3d(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }},
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }},
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }});
    }

    /// Transforms row vector \e vec by the matrix, returning the result. This
    /// treats the vector as a direction vector, so the translation
    /// information in the matrix is ignored. That is, it treats the vector as
    /// a 4-component vector whose fourth component is 0.  This is an
    /// overloaded method; it differs from the other version in that it
    /// returns a different value type.
    GfVec3f TransformDir(const GfVec3f &vec) const {
        return GfVec3f(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }},
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }},
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }});
    }

    /// Transforms the row vector \e vec by the matrix, returning the result.
    /// This treats the vector as a 4-component vector whose fourth component
    /// is 1 and ignores the fourth column of the matrix (i.e. assumes it is
    /// (0, 0, 0, 1)).
    GfVec3d TransformAffine(const GfVec3d &vec) const {
        return GfVec3d(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }} + _mtx[3][0],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }} + _mtx[3][1],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }} + _mtx[3][2]);
    }

    /// Transforms the row vector \e vec by the matrix, returning the result.
    /// This treats the vector as a 4-component vector whose fourth component
    /// is 1 and ignores the fourth column of the matrix (i.e. assumes it is
    /// (0, 0, 0, 1)).
    GfVec3f TransformAffine(const GfVec3f &vec) const {
        return GfVec3f(
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][0]", sep=" + ", num=3) }} + _mtx[3][0],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][1]", sep=" + ", num=3) }} + _mtx[3][1],
            {{ LIST("vec[%(i)s] * _mtx[%(i)s][2]", sep=" + ", num=3) }} + _mtx[3][2]);
    }
    /// @}
{% endif %}

private:
    /// Returns the determinant of the 3x3 submatrix specified by the three
    /// given row and column indices (0-3 for each).
    GF_API
    double _GetDeterminant3(size_t row1, size_t row2, size_t row3,
       size_t col1, size_t col2, size_t col3) const;
{% if SCL == 'double' %}

    /// Diagonalizes the upper 3x3 matrix of a matrix known to be symmetric.
    void _Jacobi3(GfVec3d *eigenvalues, GfVec3d eigenvectors[3]) const;
{% endif %}
{% endblock customXformFunctions %}
