# OSG matrix specific test procedures.
# These procedures are independent of the supplied matrix class type.
#
# Cnstr-Mat     Matrix constructors.        Matrix as result.
# Mat-Scalar    Matrix as input.            Scalar as result.
# Mat-Mat       Matrix as input.            Matrix as result.
# MatMat        Matrix and Matrix as input. No result.
# MatMat-Scalar Matrix and Matrix as input. Scalar as result.
# Mat-Vec       Matrix as input.            Vector as result.

package require tcl3d

source testUtil.tcl

proc PrintProjList { projList } {
    set valid  [lindex $projList 0]
    set left   [lindex $projList 1]
    set right  [lindex $projList 2]
    set bottom [lindex $projList 3]
    set top    [lindex $projList 4]
    set zNear  [lindex $projList 5]
    set zFar   [lindex $projList 6]
    if { $valid } {
        PN [format "(L/R: %.4f, %.4f) (B/T: %.4f, %.4f) (N/F: %.4f, %.4f)" \
                   $left $right $bottom $top $zNear $zFar]
    } else {
        PN "Not valid"
    }
}

proc PrintPerspList { perspList } {
    set valid  [lindex $perspList 0]
    set fovy   [lindex $perspList 1]
    set aspect [lindex $perspList 2]
    set zNear  [lindex $perspList 3]
    set zFar   [lindex $perspList 4]
    if { $valid } {
        PN [format "(F/A: %.4f, %.4f) (N/F: %.4f, %.4f)" \
                   $fovy $aspect $zNear $zFar]
    } else {
        PN "Not valid"
    }
}

# The wrapped OSG matrix classes.
set matAllClasses [list osg::Matrixf osg::Matrixd]

# Test procedure for the different types of Matrix constructors.
proc Cnstr-Mat { matType } {
    P "\nCnstr-Mat: $matType Functionality: Constructors"

    $matType mat1
    PN "Cnstr ()" ; PM mat1

    $matType mat2 [osg::Matrixf matf 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16]
    PN "Cnstr (Matrixf)" ; PM mat2

    $matType mat3 [osg::Matrixd matd 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16]
    PN "Cnstr (Matrixd)" ; PM mat3

    set fVec [tcl3dVectorFromArgs float 1 1 1 1  2 2 2 2  3 3 3 3  4 4 4 4]
    $matType mat4 $fVec
    PN "Cnstr (float *)" ; PM mat4

    set dVec [tcl3dVectorFromArgs double 1 1 1 1.1  2 2 2 2.2  3 3 3 3.3  4 4 4 4.4]
    $matType mat5 $dVec
    PN "Cnstr (double *)" ; PM mat5

    $matType mat6 [osg::Quat]
    PN "Cnstr (Quat)" ; PM mat6

    $matType mat7 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16
    PN "Cnstr (1 2 ...)" ; PM mat7

    # Static utility methods for creating matrices.
    set umat1 [${matType}_identity]
    PN "Method identity" ; PM $umat1

    set umat2 [${matType}_scale [osg::Vec3f vf1 1 2 3]]
    PN "Method scale Vec3f" ; PM $umat2

    set umat3 [${matType}_scale [osg::Vec3d vd1 1.1 2.1 3.1]]
    PN "Method scale Vec3d" ; PM $umat3

    set umat4 [${matType}_scale 1.2 2.2 3.2]
    PN "Method scale x y z" ; PM $umat4

    set umat5 [${matType}_translate [osg::Vec3f vf1 2 3 4]]
    PN "Method translate Vec3f" ; PM $umat5

    set umat6 [${matType}_translate [osg::Vec3d vd1 2.1 3.1 4.1]]
    PN "Method translate Vec3d" ; PM $umat6

    set umat7 [${matType}_translate 2.2 3.2 4.2]
    PN "Method translate x y z" ; PM $umat7

    set umat8 [${matType}_rotate [osg::Vec3f vf1 1 1 1] [osg::Vec3f vf2 1 2 3]]
    PN "Method rotate Vec3f Vec3f" ; PM $umat8

    set umat9 [${matType}_rotate [osg::Vec3d vd1 1 1 1] [osg::Vec3d vd2 1 2 3]]
    PN "Method rotate Vec3d Vec3d" ; PM $umat9

    set umat10 [${matType}_rotate 30 [osg::Vec3f vf1 1 1 1]]
    PN "Method rotate angle Vec3f" ; PM $umat10

    set umat11 [${matType}_rotate 30 [osg::Vec3d vd1 1 1 1]]
    PN "Method rotate angle Vec3d" ; PM $umat11

    set umat12 [${matType}_rotate 30 1 1 1]
    PN "Method rotate angle x y z" ; PM $umat12

    set umat13 [${matType}_rotate [osg::Quat]]
    PN "Method rotate Quat" ; PM $umat13

    set umat14 [${matType}_rotate 30 [osg::Vec3f vf1 1 0 0] \
                                  30 [osg::Vec3f vf2 0 1 0] \
                                  30 [osg::Vec3f vf3 0 0 1]]
    PN "Method rotate angle Vec3f angle Vec3f angle Vec3f" ; PM $umat14

    set umat15 [${matType}_rotate 30 [osg::Vec3d vd1 1 0 0] \
                                  30 [osg::Vec3d vd2 0 1 0] \
                                  30 [osg::Vec3d vd3 0 0 1]]
    PN "Method rotate angle Vec3d angle Vec3d angle Vec3d" ; PM $umat15

    set umat16 [${matType}_ortho -1 1 -2 2 0.1 100]
    PN "Method ortho left right bottom top zNear zFar" ; PM $umat16

    set umat17 [${matType}_ortho2D -1 1 -2 2]
    PN "Method ortho2D left right bottom top" ; PM $umat17

    set umat18 [${matType}_frustum -1 1 -2 2 0.1 100]
    PN "Method frustum left right bottom top zNear zFar" ; PM $umat18

    set umat19 [${matType}_perspective 45 1.5 0.1 100]
    PN "Method perspective fovy aspect zNear zFar" ; PM $umat19

    set umat20 [${matType}_lookAt [osg::Vec3f vf1 0 5 0] \
                                  [osg::Vec3f vf2 0 0 0] \
                                  [osg::Vec3f vf3 0 0 1]]
    PN "Method lookAt Vec3f Vec3f Vec3f" ; PM $umat20

    set umat21 [${matType}_lookAt [osg::Vec3d vd1 0 5 0] \
                                  [osg::Vec3d vd2 0 0 0] \
                                  [osg::Vec3d vd3 0 0 1]]
    PN "Method lookAt Vec3d Vec3d Vec3d" ; PM $umat21

    set umat22 [${matType}_inverse $umat21]
    PN "Method inverse Matrix" ; PM $umat22

    set umat23 [${matType}_orthoNormal $umat21]
    PN "Method orthoNormal Matrix" ; PM $umat23
}

# Test procedure for methods returning scalar information about a matrix
# without the need to supply parameters.
proc Mat-Scalar { matType mat } {
    P  "\nMat-Scalar: $matType Functionality: set Scalar \[\$In method\]"
    PN "In $matType" ; PM $mat
    foreach m { valid isNaN isIdentity } {
        set retVal [catch {eval $mat $m} result]
        PN "Method $m"
        if { $retVal == 0 } {
            P $result
        } else {
            P "Not supported"
        }
    }
    foreach m { ptr } {
        set retVal [catch {eval $mat $m} result]
        PN "Method $m"
        if { $retVal == 0 } {
            P [GetPointerType $result]
        } else {
            P "Not supported"
        }
    }
}

# Test procedure for methods returning a matrix generated
# by supplying a matrix.
proc Mat-Mat { matType mat } {
    P  "\nMat-Mat: $matType Functionality: set Matrix \[\$In method\]"
    PN "In $matType" ; PM $mat
    foreach m { copy } {
        set retVal [catch {eval $mat $m} result]
        PN "Method $m"
        if { $retVal == 0 } {
            PM "$result"
        } else {
            P "Not supported"
        }
    }
    return $result
}

# Test procedure for methods operating directly on a Matrix
# by supplying another Matrix.
proc MatMat { matType mat1 mat2 } {
    P "\nMatMat: $matType Functionality: \$In1 method \$In2"
    PN "In1 $matType" ; PM $mat1
    PN "In2 $matType" ; PM $mat2
    foreach m { mul mulSelf invert orthoNormalize preMult postMult } {
        set localMat [$mat1 copy]
        set retVal [catch {eval $localMat $m $mat2} result]
        PN "Method $m"
        if { $retVal == 0 } {
            PM "$localMat"
        } else {
            P "Not supported"
        }
        $localMat -delete
    }
    return $result
}

# Test procedure for methods returning a scalar generated
# by supplying two matrices.
proc MatMat-Scalar { matType mat1 mat2 } {
    P "\nMatMat-Scalar: $matType Functionality: set Scalar \[\$In1 method \$In2\]"
    PN "In1 $matType" ; PM $mat1
    PN "In2 $matType" ; PM $mat2
    foreach m { compare eq ne less } {
        set retVal [catch {eval $mat1 $m $mat2} result]
        PN "Method $m"
        if { $retVal == 0 } {
            P $result
        } else {
            P "Not supported"
        }
    }
    return $result
}

# Test procedure for methods not fitting into one of the above categories.
proc MatMisc { matType } {
    P "\nMatMisc: $matType Functionality: Miscellaneous"

    $matType thisMat

    thisMat set [osg::Matrixf matf 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16]
    PN "Method set Matrixf:" ; PM thisMat

    thisMat set [osg::Matrixd matd 1 2 3 4.1  5 6 7 8.1  9 10 11 12.1  13 14 15 16.1]
    PN "Method set Matrixd:" ; PM thisMat

    set fVec [tcl3dVectorFromArgs float 1 1 1 1  2 2 2 2  3 3 3 3  4 4 4 4]
    thisMat set $fVec
    PN "Method set float*" ; PM thisMat

    set dVec [tcl3dVectorFromArgs double 1 1 1 1.1  2 2 2 2.2  3 3 3 3.3  4 4 4 4.4]
    thisMat set $dVec
    PN "Method set double*" ; PM thisMat

    thisMat set 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16
    PN "Method set 1 2 ..." ; PM thisMat

    thisMat makeIdentity
    PN "Method makeIdentity" ; PM thisMat

    thisMat makeScale [osg::Vec3f vf1 1 2 3]
    PN "Method makeScale Vec3f" ; PM thisMat

    thisMat makeScale [osg::Vec3d vd1 1.1 2.1 3.1]
    PN "Method makeScale Vec3d" ; PM thisMat

    thisMat makeScale 1.2 2.2 3.2
    PN "Method makeScale x y z" ; PM thisMat

    thisMat makeTranslate [osg::Vec3f vf1 1 2 3]
    PN "Method makeTranslate Vec3f" ; PM thisMat

    thisMat makeTranslate [osg::Vec3d vd1 1.1 2.1 3.1]
    PN "Method makeTranslate Vec3d" ; PM thisMat

    thisMat makeTranslate 1.2 2.2 3.2
    PN "Method makeTranslate x y z" ; PM thisMat

    thisMat makeRotate [osg::Vec3f vf1 1 1 1] [osg::Vec3f vf2 1 2 3]
    PN "Method makeRotate Vec3f Vec3f" ; PM thisMat

    thisMat makeRotate [osg::Vec3d vd1 1 1 1] [osg::Vec3d vd2 1 2 3]
    PN "Method makeRotate Vec3d Vec3d" ; PM thisMat

    thisMat makeRotate 30 [osg::Vec3f vf1 1 1 1]
    PN "Method makeRotate angle Vec3f" ; PM thisMat

    thisMat makeRotate 30 [osg::Vec3d vd1 1 1 1]
    PN "Method makeRotate angle Vec3d" ; PM thisMat

    thisMat makeRotate 30 1 1 1
    PN "Method makeRotate angle x y z" ; PM thisMat

    thisMat makeRotate [osg::Quat]
    PN "Method makeRotate Quat" ; PM thisMat

    thisMat makeRotate 30 [osg::Vec3f vf1 1 0 0] \
                       30 [osg::Vec3f vf2 0 1 0] \
                       30 [osg::Vec3f vf3 0 0 1]
    PN "Method makeRotate angle Vec3f angle Vec3f angle Vec3f" ; PM thisMat

    thisMat makeRotate 30 [osg::Vec3d vd1 1 0 0] \
                       30 [osg::Vec3d vd2 0 1 0] \
                       30 [osg::Vec3d vd3 0 0 1]
    PN "Method makeRotate angle Vec3d angle Vec3d angle Vec3d" ; PM thisMat

    osg::Vec3f trans
    osg::Quat  rot
    osg::Vec3f scl
    osg::Quat  so
    thisMat decompose trans rot scl so
    PN "Method decompose Vec3f Quat Vec3f Quat" ; P ""
    PN "Translation"       ; PV trans
    PN "Rotation"          ; PQ rot
    PN "Scale"             ; PV scl
    PN "Scale Orientation" ; PQ so

    osg::Vec3d transd
    osg::Vec3d scld
    thisMat decompose transd rot scld so
    PN "Method decompose Vec3d Quat Vec3d Quat" ; P ""
    PN "Translation"       ; PV transd
    PN "Rotation"          ; PQ rot
    PN "Scale"             ; PV scld
    PN "Scale Orientation" ; PQ so

    thisMat makeOrtho -1 1 -2 2 0.1 100
    PN "Method makeOrtho left right bottom top zNear zFar" ; PM thisMat

    set orthoList [thisMat getOrtho]
    PN "Method getOrtho" ; PrintProjList $orthoList ; P ""

    thisMat makeOrtho2D -1 1 -2 2
    PN "Method makeOrtho2D left right bottom top" ; PM thisMat

    thisMat makeFrustum -1 1 -2 2 0.1 100
    PN "Method makeFrustum left right bottom top zNear zFar" ; PM thisMat

    set frustumList [thisMat getFrustum]
    PN "Method getFrustum" ; PrintProjList $frustumList ; P ""

    thisMat makePerspective 45 1.5 0.1 100
    PN "Method makePerspective fovy aspect zNear zFar" ; PM thisMat

    set perspList [thisMat getPerspective]
    PN "Method getPerspective" ; PrintPerspList $perspList ; P ""

    thisMat makeLookAt [osg::Vec3d vd1 0 5 0] \
                       [osg::Vec3d vd2 0 0 0] \
                       [osg::Vec3d vd3 0 0 1]
    PN "Method makeLookAt Vec3d Vec3d Vec3d" ; PM thisMat

    osg::Vec3f vf1
    osg::Vec3f vf2
    osg::Vec3f vf3
    thisMat getLookAt vf1 vf2 vf3 5
    PN "Method getLookAt Vec3f Vec3f Vec3f" ; P ""
    PN "Eye"    ; PV vf1
    PN "Center" ; PV vf2
    PN "Up"     ; PV vf3

    osg::Vec3d vd1
    osg::Vec3d vd2
    osg::Vec3d vd3
    thisMat getLookAt vd1 vd2 vd3 5
    PN "Method getLookAt Vec3d Vec3d Vec3d" ; P ""
    PN "Eye"    ; PV vd1
    PN "Center" ; PV vd2
    PN "Up"     ; PV vd3

    thisMat setRotate [osg::Quat]
    PN "Method setRotate Quat" ; PM thisMat

    set quat [thisMat getRotate]
    PN "Method getRotate" ; PQ $quat

    thisMat setTrans [osg::Vec3f vf1 1 2 3]
    PN "Method setTrans Vec3f" ; PM thisMat

    thisMat setTrans [osg::Vec3d vd1 1.1 2.1 3.1]
    PN "Method setTrans Vec3d" ; PM thisMat

    thisMat setTrans 1.2 2.2 3.2
    PN "Method setTrans x y z" ; PM thisMat

    set vecTrans [thisMat getTrans]
    PN "Method getTrans" ; PV $vecTrans

    set scale [thisMat getScale]
    PN "Method getScale" ; PV $scale

    set matScale [${matType}_scale 1.2 2.2 3.2]
    set matTrans [${matType}_translate 4.2 5.2 6.2]
    thisMat mult $matScale $matTrans
    PN "Method mult Matrix Matrix" ; PM thisMat
}

# Test procedure for methods returning a vector generated
# by supplying a matrix and a vector, i.e. applying a transformation.
proc VecTransform { matType mat } {
    P "\nVecTransform: $matType Functionality: Apply Transformation"
    PN "In1 $matType" ; PM $mat
    set vec(0) [osg::Vec3f v1 1 2 3]
    set vec(1) [osg::Vec3d v2 1 2 3]
    set vec(2) [osg::Vec4f v3 1 2 3 1]
    set vec(3) [osg::Vec4d v4 1 2 3 1]
    for { set i 0 } { $i < 4 } { incr i } {
        foreach m { mul preMult postMult } {
            set retVal [catch {eval $mat $m $vec($i)} result]
            PN "Method $m"
            if { $retVal == 0 } {
                PV $result
            } else {
                P "Not supported"
            }
        }
    }

    set result [${matType}_transform3x3 $vec(0) $mat]
    PN "Method transform3x3 Vec3f Matrix" ; PV $result
    set result [${matType}_transform3x3 $vec(1) $mat]
    PN "Method transform3x3 Vec3d Matrix" ; PV $result
    set result [${matType}_transform3x3 $mat $vec(0)]
    PN "Method transform3x3 Matrix Vec3f" ; PV $result
    set result [${matType}_transform3x3 $mat $vec(1)]
    PN "Method transform3x3 Matrix Vec3d" ; PV $result
    return $result
}

foreach matType $matAllClasses {
    $matType matId
    $matType matSample1 1 2 3 4  5 6 7 8  9 10 11 12  13 14 15 16
    $matType matSample2 1 0 0 0  0 0 1 0  0  1  0  0   2  3  4  5
    Cnstr-Mat      $matType
    Mat-Scalar     $matType matSample1
    Mat-Mat        $matType matSample1
    MatMat         $matType matId matSample2
    MatMat-Scalar  $matType matId matSample1
    MatMisc        $matType
    VecTransform   $matType matSample1
    matId      -delete
    matSample1 -delete
    matSample2 -delete
}

exit
