[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//
//  Project:   Talina Gaming System (TgS) (∂)
//  File:      TgS Collision - Cylinder-Cylinder.cpp
//  Author:    Andrew Aye (EMail: andrew.aye@gmail.com, Web: http://www.andrewaye.com) 
//  Version:   3.11
//
// ------------------------------------------------------------------------------------------------------------------------------ //
//
//  Copyright: © 2002-2008, Andrew Aye.  All Rights Reserved.
//
//  This software is free for non-commercial use. Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met: 
//    Redistributions of source code must retain this copyright notice, this list of conditions and the following disclaimers. 
//    Redistributions in binary form must reproduce this copyright notice, this list of conditions and the following
//      disclaimers in the documentation and other materials provided with the distribution. 
//
//  Neither the names of the copyright owner nor the names of its contributors may be used to endorse or promote products derived
//  from this software without specific prior written permission. 
//
//  The intellectual property rights of the algorithms used reside with Andrew Aye.  You may not use this software, in whole or
//  in part, in support of any commercial product without the express written consent of the author.
//
//  There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //





namespace TGS { // START TGS ///////////////////////////////////////////////////////////////////////////////////////////////////////
namespace COL { // START COL ///////////////////////////////////////////////////////////////////////////////////////////////////////

// ============================================================================================================================== //

// ---- F_Axis_Seperation ------------------------------------------------------------------------------------------------------- //
// Input:  tgCY0, tgCY1: Cylinder primitives
// Output: tgAxS: Structure holds the resulting axis separation information necessary to create a contact set.
// Return: False if a separating axis exists, true otherwise
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgBOOL F_Axis_Seperation( PC_(AXIS_RESULT,DIM) ptgAxS, CR_(CYLINDER,DIM) tgCY0, CR_(CYLINDER,DIM) tgCY1 )
{
    TgASSERT( tgCY0.Is_Valid() && tgCY1.Is_Valid() )

    //  To figure out all the possible axes of separation, it is necessary only to imagine the infinite set of vectors that are
    // orthogonal to the cylinder surface.  Once conceived, it becomes obvious to separate the cylinder into three conceptual
    // parts.  The body is the main tube section of the cylinder.  The caps are the planar terminal part of the cylinder and the
    // rims are where the planar terminal part of the cylinder and the tube intersect.  The body and the caps have obvious and
    // well defined orthogonal vectors sets.  In the case of the caps, the set has only one member - the normal defining the plane.
    // For the body, the set is infinite - defined as those vectors in the cylinder cross-sectional plane that are directed
    // radially.  The rim set is composed of a locus forming a quarter torus.  Most of these axes are invalid, and its a matter
    // of choosing the right ones - and preferably a finite set in computational amount of time.  The correct axes can be found
    // using the following methods:
    // 
    // Cap-Rim      All cases should be found by the axial test
    // Cap-Cap      All cases should be found by the axial test
    // Cap-Body     All cases should be found by the axial test, if not the minimal distance vector will catch it.
    // 
    // Rim-Body     Minimal distance vector, projected onto the cylinder plane, will find all such cases.
    // Rim-Rim      The intersection of the two sets of normal rim vectors should be unique and is the test case.
    // 
    // Body-Body    Minimal distance vector will find all such cases.
    // 

    TYPE                                tyTest;

    // Construct the difference vector between the two origins and calculate the reference frame projections.

    C_(VECTOR,DIM)                      tvDS = MATH::F_SUB( tgCY1.Query_Origin(), tgCY0.Query_Origin() );

    const TYPE                          tyDS_A0 = MATH::F_DOT(tvDS,tgCY0.Query_AxisUnit());
    const TYPE                          tyDS_A1 = MATH::F_DOT(tvDS,tgCY1.Query_AxisUnit());

    ptgAxS->m_tyDepth = LIMITS<TYPE>::MAX;

    const TYPE                          tyABS_A0_A1 = P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgCY1.Query_AxisUnit()) );
    const TYPE                          tyABS_A0xA1 = P::SQRT( TYPE(1.0) - MIN( TYPE(1.0), tyABS_A0_A1*tyABS_A0_A1 ) );

    // == Axis Separation Tests =================================================================================================

    // -- Axis: Cylinder #0 Axis -------------------------------

    tyTest = (tgCY0.Query_Extent() + tgCY1.Query_Radius()*tyABS_A0xA1 + tgCY1.Query_Extent()*tyABS_A0_A1) - P::ABS( tyDS_A0 );

    if (tyTest <= TYPE(0.0))
    {
        return (TgFALSE);
    };

    if (tyTest < ptgAxS->m_tyDepth)
    {
        ptgAxS->m_tvNormal = tyDS_A0 > TYPE(0.0) ? tgCY0.Query_AxisUnit() : MATH::F_NEG( tgCY0.Query_AxisUnit() );
        ptgAxS->m_tyDepth = tyTest;
        ptgAxS->m_iAxis = 0;
    };

    // == Cylinder Parallel Test =============================================================

    const TYPE                          tyRadSum = tgCY0.Query_Radius() + tgCY1.Query_Radius();

    if (Near_One( tyABS_A0_A1 ) || Near_Zero( tyABS_A0xA1 ))
    {
        // -- Axis: Perpendicular to Common Cylinder Axis ----------

        tyTest = MATH::F_LSQ( tvDS ) - tyDS_A0*tyDS_A0;
        
        if (tyTest > tyRadSum*tyRadSum)
        {
            return (TgFALSE);
        };
        
        tyTest = tyRadSum - P::SQRT( tyTest );
        
        if (tyTest < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvNormal = MATH::F_SUB( tvDS, MATH::F_MUL( tyDS_A0, tgCY0.Query_AxisUnit() ) );
            ptgAxS->m_tyDepth = tyTest;
            ptgAxS->m_iAxis = 4;

            MATH::F_NORM( ptgAxS->m_tvNormal );
        };

        return (TgTRUE);
    };

    // -- Axis: Cylinder #1 Axis -------------------------------

    tyTest = (tgCY1.Query_Extent() + tgCY0.Query_Radius()*tyABS_A0xA1 + tgCY0.Query_Extent()*tyABS_A0_A1) - P::ABS( tyDS_A1 );

    if (tyTest <= TYPE(0.0))
    {
        return (TgFALSE);
    };

    if (tyTest < ptgAxS->m_tyDepth)
    {
        ptgAxS->m_tvNormal = tyDS_A1 > TYPE(0.0) ? MATH::F_NEG( tgCY1.Query_AxisUnit() ) : tgCY1.Query_AxisUnit();
        ptgAxS->m_tyDepth = tyTest;
        ptgAxS->m_iAxis = 2;
    };

    // =======================================================================================

    CR_(SEGMENT,DIM)                    tgSG_CY0 = tgCY0.Query_Segment();
    CR_(SEGMENT,DIM)                    tgSG_CY1 = tgCY1.Query_Segment();

    TYPE                                tyCA0,tyCA1;
    TYPE                                tyT0,tyT1, tyT2,tyT3;

    const TYPE                          tyDistSq = F_ClosestSq( &tyCA0,&tyCA1, tgSG_CY0, tgSG_CY1 );
    
    if (tyDistSq > tyRadSum*tyRadSum)
    {
        return (TgFALSE);
    };
    
    C_(VECTOR,DIM)                      tvK14 = MATH::F_MUL( tyCA0, tgSG_CY0.Query_DirN() );
    C_(VECTOR,DIM)                      tvK15 = MATH::F_MUL( tyCA1, tgSG_CY1.Query_DirN() );
    C_(VECTOR,DIM)                      tvMin_CY0 = MATH::F_ADD( tgSG_CY0.Query_Origin(), tvK14 );
    C_(VECTOR,DIM)                      tvMin_CY1 = MATH::F_ADD( tgSG_CY1.Query_Origin(), tvK15 );
    C_(VECTOR,DIM)                      tvMinDist = MATH::F_SUB( tvMin_CY1, tvMin_CY0 );
    C_(VECTOR,DIM)                      tvMinDirN = MATH::F_SUB( tvMin_CY1, tvMin_CY0 );

    MATH::F_NORM( tvMinDirN );

    C_TgBOOL                            bCapContact0 = Near_One( tyCA0 ) || Near_Zero( tyCA0 );
    C_TgBOOL                            bCapContact1 = Near_One( tyCA1 ) || Near_Zero( tyCA1 );

    // -- Axis: Between Points of Closest Proximity ------------

    if (!bCapContact0 && !bCapContact1)
    {
        tgCY0.Project( &tyT0,&tyT1, tvMinDirN );
        tgCY1.Project( &tyT2,&tyT3, tvMinDirN );

        if (tyT1 - tyT2 < TYPE(0.0) || tyT3 - tyT0 < TYPE(0.0))
        {
            return (TgFALSE);
        };

        tyTest = tyT2 - tyT1;

        if (tyTest < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvPoint = MATH::F_SUB( tvMin_CY1, MATH::F_MUL( tgCY1.Query_Radius(), tvMinDirN ) );
            ptgAxS->m_tvNormal = tvMinDirN;
            ptgAxS->m_tyDepth = tyTest;
            ptgAxS->m_iAxis = 5;
        };
    };

    if (bCapContact0) // If at a cylinder termini, a better axis may be the minimal direction projected on the cylinder plane.
    {
        // -- Axis: Perpendicular to Axis of Cylinder 0 ------------
        // Example: Rim to Cylinder contact.

        const TYPE                          tyK00 = MATH::F_DOT( tvMinDist, tgCY0.Query_BasisUnit0() );
        const TYPE                          tyK01 = MATH::F_DOT( tvMinDist, tgCY0.Query_BasisUnit1() );
        C_(VECTOR,DIM)                      tvK00 = MATH::F_MUL( tyK00, tgCY0.Query_BasisUnit0() );
        C_(VECTOR,DIM)                      tvK01 = MATH::F_MUL( tyK01, tgCY0.Query_BasisUnit1() );
        C_(VECTOR,DIM)                      tvX00 = MATH::F_NORM( MATH::F_ADD( tvK00, tvK01 ) );

        const TYPE                          tyABS_Ax_A1 = P::ABS( MATH::F_DOT( tvX00, tgCY1.Query_AxisUnit() ) );
        const TYPE                          tyAx_C10 = MATH::F_DOT( tvX00, tgCY1.Query_BasisUnit0() );
        const TYPE                          tyAx_C11 = MATH::F_DOT( tvX00, tgCY1.Query_BasisUnit1() );

        const TYPE                          tyK02 = P::SQRT( tyAx_C10*tyAx_C10 + tyAx_C11*tyAx_C11 );
        const TYPE                          tyK03 = tgCY0.Query_Radius() + tyABS_Ax_A1*tgCY1.Query_Extent();
        const TYPE                          tyK04 = (tyK03 + tyK02*tgCY1.Query_Radius()) - P::ABS( MATH::F_DOT( tvX00, tvDS ) );

        if (tyK04 <= TYPE(0.0))
        {
            return (TgFALSE);
        };

        if (tyK04 < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvNormal = tvX00;
            ptgAxS->m_tyDepth = tyK04;
            ptgAxS->m_iAxis = 6;
        };
    };

    if (bCapContact1) // If at a cylinder termini, a better axis may be the minimal direction projected on the cylinder plane.
    {
        // -- Axis: Perpendicular to Axis of Cylinder 1 ------------
        // Example: Rim to Cylinder contact.

        const TYPE                          tyK06 = MATH::F_DOT( tvMinDist, tgCY1.Query_BasisUnit0() );
        const TYPE                          tyK07 = MATH::F_DOT( tvMinDist, tgCY1.Query_BasisUnit1() );
        C_(VECTOR,DIM)                      tvK06 = MATH::F_MUL( tyK06, tgCY1.Query_BasisUnit0() );
        C_(VECTOR,DIM)                      tvK07 = MATH::F_MUL( tyK07, tgCY1.Query_BasisUnit1() );
        C_(VECTOR,DIM)                      tvX10 = MATH::F_NORM( MATH::F_ADD( tvK06, tvK07 ) );

        const TYPE                          tyABS_Ax_A0 = P::ABS( MATH::F_DOT( tvX10, tgCY0.Query_AxisUnit() ) );
        const TYPE                          tyAx_C00 = MATH::F_DOT( tvX10, tgCY0.Query_BasisUnit0() );
        const TYPE                          tyAx_C01 = MATH::F_DOT( tvX10, tgCY0.Query_BasisUnit1() );

        const TYPE                          tyK08 = P::SQRT( tyAx_C00*tyAx_C00 + tyAx_C01*tyAx_C01 );
        const TYPE                          tyK09 = tgCY1.Query_Radius() + tyABS_Ax_A0*tgCY0.Query_Extent();
        const TYPE                          tyK10 = (tyK09 + tyK08*tgCY1.Query_Radius()) - P::ABS( MATH::F_DOT( tvX10, tvDS ) );

        if (tyK10 <= TYPE(0.0))
        {
            return (TgFALSE);
        };

        if (tyK10 < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvNormal = tvX10;
            ptgAxS->m_tyDepth = tyK10;
            ptgAxS->m_iAxis = 7;
        };
    };

    if (!bCapContact0 || !bCapContact1 || MATH::F_LSQ( tvDS ) < LIMITS<TYPE>::ROOTEPSILON)
    {
        return (TgTRUE);
    };

    // Construct the line of intersection between the two cylinder cap planes.

    const TYPE                          tyA0_A1 = MATH::F_DOT( tgCY0.Query_AxisUnit(), tgCY1.Query_AxisUnit() );
    const TYPE                          tyA = TYPE(1.0) - tyA0_A1*tyA0_A1;

    if (tyA <= LIMITS<TYPE>::ROOTEPSILON)
    {
        return (TgTRUE); //« Make sure the two planes are not co-planar.
    };

    const TYPE                          tyInvA = TYPE(1.0) / tyA;
    const TYPE                          tyD_CY0 = MATH::F_DOT( tgCY0.Query_AxisUnit(), tvMin_CY0 );
    const TYPE                          tyD_CY1 = MATH::F_DOT( tgCY1.Query_AxisUnit(), tvMin_CY1 );

    tyT0 = (tyD_CY0 - tyD_CY1*tyA0_A1) * tyInvA;
    tyT1 = (tyD_CY1 - tyD_CY0*tyA0_A1) * tyInvA;

    T_(VECTOR,DIM)                      tvD0, tvTest, tvT0,tvT1;

    // F_Clip the common line of intersection to the interior of the cylinder tube space.

    T_(LINE,DIM)                        tgLN0;

    tgLN0.Set_Origin( MATH::F_ADD( MATH::F_MUL( tyT0, tgCY0.Query_AxisUnit() ), MATH::F_MUL( tyT1, tgCY1.Query_AxisUnit() ) ) );
    tgLN0.Set_DirN( MATH::F_CX( tgCY0.Query_AxisUnit(), tgCY1.Query_AxisUnit() ) );

    // F_Clip the line to cylinder 0 and find the contact point

    F_Clip( &tyT0,&tyT1, static_cast<CR_(TUBE,DIM)>(tgCY0), tgLN0 );

    tvT0 = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT0, tgLN0.Query_DirN() ) );

    if (MATH::F_LSQ( MATH::F_SUB( tvT0, tvMin_CY1 ) ) >= tgCY1.Query_RadiusSq())
    {
        tvT0 = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT1, tgLN0.Query_DirN() ) );

        if (MATH::F_LSQ( MATH::F_SUB( tvT0, tvMin_CY1 ) ) >= tgCY1.Query_RadiusSq())
        {
            COUT_NOOBJ(
                WARNING, TgT("%-16.16s(%-48.48s): [CY][CY] Not 100% that this type of thing should happen - record for test case.\n"),
                TgT("Collision"), TgT("F_Axis_Seperation")
            );
            return (TgFALSE);
        };
    }
    else
    {
        tvTest = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT1, tgLN0.Query_DirN() ) );

        if (MATH::F_LSQ( MATH::F_SUB( tvTest, tvMin_CY1 ) ) <= tgCY1.Query_RadiusSq())
        {
            // If the entire segment is contained the best axis will be axial.
            return (TgTRUE);
        };
    };

    // F_Clip the line to cylinder 1 and find the contact point

    F_Clip( &tyT0,&tyT1, static_cast<CR_(TUBE,DIM)>(tgCY1), tgLN0 );

    tvT1 = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT0, tgLN0.Query_DirN() ) );

    if (MATH::F_LSQ( MATH::F_SUB( tvT1, tvMin_CY1 ) ) >= tgCY0.Query_RadiusSq())
    {
        tvT1 = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT1, tgLN0.Query_DirN() ) );

        if (MATH::F_LSQ( MATH::F_SUB( tvT1, tvMin_CY1 ) ) >= tgCY0.Query_RadiusSq())
        {
            COUT_NOOBJ(
                WARNING, TgT("%-16.16s(%-48.48s): [CY][CY] Not 100% that this type of thing should happen - record for test case.\n"),
                TgT("Collision"), TgT("F_Axis_Seperation")
            );
            return (TgFALSE);
        };
    }
    else
    {
        tvTest = MATH::F_ADD( tgLN0.Query_Origin(), MATH::F_MUL( tyT1, tgLN0.Query_DirN() ) );

        if (MATH::F_LSQ( MATH::F_SUB( tvTest, tvMin_CY1 ) ) <= tgCY0.Query_RadiusSq())
        {
            // If the entire segment is contained the best axis will be axial.
            return (TgTRUE);
        };
    };

    T_(VECTOR,DIM)                      tvD1;

    // Construct the tangent vectors, create a mutual orthogonal vector (cross-product) - this is the separating plane normal.
    // Essentially the construction is finding the (possibly unique) vector that exists in the intersection of the two sets
    // of possible rim normals.

    tvD0 = MATH::F_CX( MATH::F_SUB( tvT0, tvMin_CY0 ), tgCY0.Query_AxisUnit() ); //« Tangent vector at rim on Cylinder 0
    tvD1 = MATH::F_CX( MATH::F_SUB( tvT0, tvMin_CY1 ), tgCY1.Query_AxisUnit() ); //« Tangent vector at rim on Cylinder 1

    if (MATH::F_UCX( &tvTest, tvD0,tvD1 ) > LIMITS<TYPE>::ROOTEPSILON)
    {
        tgCY0.Project( &tyT0,&tyT1, tvTest );
        tgCY1.Project( &tyT2,&tyT3, tvTest );

        tyT1 -= tyT2;
        tyT3 -= tyT0;

        if (tyT1 < TYPE(0.0) || tyT3 < TYPE(0.0))
        {
            return (TgFALSE);
        };

        tyTest = -TgMIN( tyT1, tyT3 );

        if (tyTest < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvPoint = tvT0;
            ptgAxS->m_tvNormal = MATH::F_MUL( P::FSEL( tyT1 - tyT3, TYPE(-1.0), TYPE(1.0) ), tvTest );
            ptgAxS->m_tyDepth = tyTest;
            ptgAxS->m_iAxis = 8;
        };
    };

    tvD0 = MATH::F_CX( MATH::F_SUB( tvT1, tvMin_CY0 ), tgCY0.Query_AxisUnit() ); //« Tangent vector at rim on Cylinder 0
    tvD1 = MATH::F_CX( MATH::F_SUB( tvT1, tvMin_CY1 ), tgCY1.Query_AxisUnit() ); //« Tangent vector at rim on Cylinder 1

    if (MATH::F_UCX( &tvTest, tvD0,tvD1 ) > LIMITS<TYPE>::ROOTEPSILON)
    {
        tgCY0.Project( &tyT0,&tyT1, tvTest );
        tgCY1.Project( &tyT2,&tyT3, tvTest );

        tyT1 -= tyT2;
        tyT3 -= tyT0;

        if (tyT1 < TYPE(0.0) || tyT3 < TYPE(0.0))
        {
            return (TgFALSE);
        };

        tyTest = -TgMIN( tyT1, tyT3 );

        if (tyTest < ptgAxS->m_tyDepth)
        {
            ptgAxS->m_tvPoint = tvT1;
            ptgAxS->m_tvNormal = MATH::F_MUL( P::FSEL( tyT1 - tyT3, TYPE(-1.0), TYPE(1.0) ), tvTest );
            ptgAxS->m_tyDepth = tyTest;
            ptgAxS->m_iAxis = 8;
        };
    };

    return (TgTRUE);
};

template TgBOOL F_Axis_Seperation( PC_TgF4AXIS_RESULT, CR_TgF4CYLINDER, CR_TgF4CYLINDER );


// ============================================================================================================================== //

// ---- F_Internal_CapCap ------------------------------------------------------------------------------------------------------- //
//  -- Internal Function -- (Helper to create contact points between caps of two cylinders)
// Input:  tvNormal: The axis of contact being used.
// Input:  tgEL0: The Elliptical projection of the cylinder cap onto the plane defined by the axis of contact
// Input:  tgCY0: Cylinder primitive
// Input:  tgCY1: Cylinder primitive - contact points are generated on this primitive
// Output: aF_Contact: An array of contact points to return for processing that will project cylinder 1 out of cylinder 0.
// Output: nuiPoint: The number of valid points in the array
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM> TgFORCEINLINE
TgRESULT F_Internal_CapCap(
    T_(CONTACT,DIM) aF_Contact[4], TgUINT32& nuiPoint, M_(VECTOR,DIM) tvNormal, CR_(ELLIPSE,DIM) tgEL1, CR_(CYLINDER,DIM) tgCY0, CR_(CYLINDER,DIM) tgCY1
) {
    const TYPE                          tyDN0 = P::FSEL( MATH::F_DOT(tvNormal,tgCY0.Query_AxisUnit()), TYPE(1.0), TYPE(-1.0) );
    const TYPE                          tyDN1 = P::FSEL( MATH::F_DOT(tvNormal,tgCY1.Query_AxisUnit()), TYPE(-1.0), TYPE(1.0) );
    C_(VECTOR,DIM)                      tvC0 = MATH::F_ADD( tgCY0.Query_Origin(), MATH::F_MUL( tyDN0, tgCY0.Query_HalfAxis() ) );
    C_(VECTOR,DIM)                      tvC1 = MATH::F_ADD( tgCY1.Query_Origin(), MATH::F_MUL( tyDN1, tgCY1.Query_HalfAxis() ) );

    T_(VECTOR,DIM)                      tvX10,tvX11, tvTest;
    TYPE                                tyT0,tyT1, tyTest;

    nuiPoint = 0;

    // Create a reference frame for cylinder one based on the direction of deepest penetration.  To determine this reference frame
    // the primary axis of the second cylinder is projectd down onto the cross-sectional plane of the first cylinder. It is the
    // angle of the second cylinder that will give us the direction of maximum penetration.  Once that has been determined that
    // direction now has to be taken back into the second cylinder's reference frame to determine the result basis.

    const TYPE                          tyK0 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit0(), tgCY1.Query_AxisUnit() );
    const TYPE                          tyK1 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit1(), tgCY1.Query_AxisUnit() );
    C_(VECTOR,DIM)                      tvK0 = MATH::F_MUL( tyK0, tgCY0.Query_BasisUnit0() );
    C_(VECTOR,DIM)                      tvK1 = MATH::F_MUL( tyK1, tgCY0.Query_BasisUnit1() );

    tvX10 = MATH::F_NORM( &tyTest, MATH::F_ADD( tvK0, tvK1 ) );

    if (tyTest <= LIMITS<TYPE>::EPSILON)
    {
        tvX10 = tgCY1.Query_BasisUnit0();
        tvX11 = tgCY1.Query_BasisUnit1();
    }
    else
    {
        MATH::F_UCX( &tvX11, tvX10, tgCY1.Query_AxisUnit() );
        MATH::F_UCX( &tvX10, tvX11, tgCY1.Query_AxisUnit() );
    };

    // Use the reference frame of cylinder 1 to create the point of deepest penetration.

    tvTest = MATH::F_MUL( tgCY1.Query_Radius(), tvX10 );

    if (TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, MATH::F_SUB( tvC1, tvTest ), MATH::F_MUL( TYPE(2.0), tvTest ) ) >= 0)
    {
        tvTest = MATH::F_ADD( tvC1, MATH::F_MUL( TYPE(2.0)*tyT1 - TYPE(1.0), tvTest ) );
        tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

        if (tyTest > TYPE(0.0))
        {
            aF_Contact[nuiPoint].m_tyDepth = tyTest;
            aF_Contact[nuiPoint++].m_tvPos = tvTest;
        };
    };

    //  At issue is the fact that the elliptical intersection routine requires the solution of a quartic equation which are known
    // to be very unstable.  Specifically, as the ellipse ratios become closer to that of a full circle the intersection routine
    // will quickly become degenerate.  Therefore, a heuristic value to determine when to use the elliptical routine over assuming
    // that both caps are co-planar has to be used.  Keep in mind the the math will be distinctly less prone to these problems on
    // the 64bit Power architectures used in the Xbox360 and PS3.  However, the calculations will have to be kept in the floating
    // point unit and not vectorized.

    T_(VECTOR,DIM)                      tvT0, tvT1;
    const TYPE                          tyABS_A0_A1 = P::ABS( MATH::F_DOT( tgCY0.Query_AxisUnit(), tgCY1.Query_AxisUnit() ) );
    TgRESULT                            tgResult = TgE_NOINTERSECT;

    if (tyABS_A0_A1 >= KF32_SQRT1_2)
    {
        if (Near_Zero( tgEL1.Query_Major_Radius() - tgEL1.Query_Minor_Radius() ))
        {
            // Use the circle routine

            T_(CIRCLE,DIM)                      tgCI0, tgCI1;

            tgCI0.Set( tgCY0.Query_BasisUnit0(), tgCY0.Query_AxisUnit(), tgCY0.Query_BasisUnit1(), tvC0, tgCY0.Query_Radius() );
            tgCI1.Set( tgCY1.Query_BasisUnit0(), tgCY1.Query_AxisUnit(), tgCY1.Query_BasisUnit1(), tvC1, tgCY1.Query_Radius() );

            tgResult = F_Contact_Intersect2D( &tvT0,&tvT1, tgCI0,tgCI1 );
        }
        else
        {
            // Use the elliptical routine

            T_(ELLIPSE,DIM)                     tgEL0;

            tgEL0.Set(
                tvC0, tgCY0.Query_BasisUnit0(), tgCY0.Query_AxisUnit(), tgCY0.Query_BasisUnit1(),
                tgCY0.Query_Radius(), tgCY0.Query_Radius()
            );

            tgResult = F_Contact_Intersect2D( &tvT0,&tvT1, tgEL0,tgEL1 );
        };
    };

    if (TgE_NOINTERSECT == tgResult)
    {
        // Used the reference frame of cylinder 0 to create three symmetrical contacts around the rim/base.

        C_(VECTOR,DIM)                      tvK0 = MATH::F_ADD( tvX10, MATH::F_MUL( KF32_SQRT3, tvX11 ) );

        tvTest = MATH::F_MUL( tgCY1.Query_Radius()*TYPE(-0.5), tvK0 );

        if (TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tvC1,tvTest ) >= 0)
        {
            tvTest = MATH::F_ADD( tvC1, MATH::F_MUL( tyT1, tvTest ) );
            tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

            if (tyTest > TYPE(0.0))
            {
                aF_Contact[nuiPoint].m_tyDepth = tyTest;
                aF_Contact[nuiPoint++].m_tvPos = tvTest;
            };
        };

        C_(VECTOR,DIM)                      tvK1 = MATH::F_SUB( tvX10, MATH::F_MUL( KF32_SQRT3, tvX11 ) );

        tvTest = MATH::F_MUL( tgCY1.Query_Radius()*TYPE(-0.5), tvK1 );

        if (TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tvC1,tvTest ) >= 0)
        {
            tvTest = MATH::F_ADD( tvC1, MATH::F_MUL( tyT1, tvTest ) );
            tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

            if (tyTest > TYPE(0.0))
            {
                aF_Contact[nuiPoint].m_tyDepth = tyTest;
                aF_Contact[nuiPoint++].m_tvPos = tvTest;
            };
        };

        return (0 != nuiPoint ? TgS_OK : TgE_NOINTERSECT);
    }

    if (tgResult < 0)
    {
        TgASSERT_MSG( TgFALSE, TgT("F_Internal_CapCap: No intersection points found between the two caps.") );
        return (TgE_FAIL);
    };

    // Use tvTo,tvT1, and bisect them and create points on other side of the intersection space.

    const TYPE                          tyFactor = -tyDN1 / tyABS_A0_A1;

    tyTest = tyFactor*(MATH::F_DOT( MATH::F_SUB( tvT0, tvC1 ), tgCY1.Query_AxisUnit() ));

    if (tyTest > TYPE(0.0))
    {
        aF_Contact[nuiPoint].m_tyDepth = tyTest;
        aF_Contact[nuiPoint++].m_tvPos = MATH::F_SUB( tvT0, MATH::F_MUL( tyDN0*tyTest, tgCY0.Query_AxisUnit() ) );
    };

    tyTest = tyFactor*(MATH::F_DOT( MATH::F_SUB( tvT1, tvC1 ), tgCY1.Query_AxisUnit() ));

    if (tyTest > TYPE(0.0))
    {
        aF_Contact[nuiPoint].m_tyDepth = tyTest;
        aF_Contact[nuiPoint++].m_tvPos = MATH::F_SUB( tvT1, MATH::F_MUL( tyDN0*tyTest, tgCY0.Query_AxisUnit() ) );
    };

    tvTest = MATH::F_NORM( MATH::F_SUB( MATH::F_MUL( TYPE(0.5), MATH::F_ADD( tvT0, tvT1 ) ), tvC1 ) );
    tvTest = MATH::F_ADD( MATH::F_MUL( tvTest, tgCY1.Query_Radius() ), tvC1 );
    tyTest = MATH::F_DOT( MATH::F_SUB( tvTest, tvC1 ), tgCY1.Query_AxisUnit() )*tyFactor;

    if (tyTest > TYPE(0.0))
    {
        aF_Contact[nuiPoint].m_tyDepth = tyTest;
        aF_Contact[nuiPoint++].m_tvPos = MATH::F_SUB( tvTest, MATH::F_MUL( tyDN0*tyTest, tgCY0.Query_AxisUnit() ) );
    };

    return (TgS_OK);
};


// ============================================================================================================================== //

// ---- F_Internal_CapContained ------------------------------------------------------------------------------------------------- //
//  -- Internal Function -- (Helper to create contact points between caps of two cylinders, given one is entirely contained)
// Input:  tvNormal: The axis of contact being used.
// Input:  tgCY0: Cylinder primitive
// Input:  tgCY1: Cylinder primitive - contact points are generated on this primitive
// Output: aF_Contact: An array of contact points to return for processing
// Output: nuiPoint: The number of valid points in the array
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM> TgFORCEINLINE
TgRESULT F_Internal_CapContained( // Cylinder 1 is known to be nested inside cylinder 0
    T_(CONTACT,DIM) aF_Contact[4], TgUINT32 &nuiPoint, M_(VECTOR,DIM) tvNormal, CR_(CYLINDER,DIM) tgCY0, CR_(CYLINDER,DIM) tgCY1
) {
    const TYPE                          tyDN0 = P::FSEL( MATH::F_DOT(tvNormal,tgCY0.Query_AxisUnit()), TYPE(1.0), TYPE(-1.0) );
    const TYPE                          tyDN1 = P::FSEL( MATH::F_DOT(tvNormal,tgCY1.Query_AxisUnit()), TYPE(-1.0), TYPE(1.0) );
    C_(VECTOR,DIM)                      tvC0 = MATH::F_ADD( tgCY0.Query_Origin(), MATH::F_MUL( tyDN0, tgCY0.Query_HalfAxis() ) );
    C_(VECTOR,DIM)                      tvC1 = MATH::F_ADD( tgCY1.Query_Origin(), MATH::F_MUL( tyDN1, tgCY1.Query_HalfAxis() ) );

    T_(VECTOR,DIM)                      tvX10,tvX11, tvTest;
    TYPE                                tyTest;

    // Create a reference frame for cylinder one based on the direction of deepest penetration.  To determine this reference frame
    // the primary axis of the first cylinder is projectd down onto the cross-sectional plane of the second cylinder.

    const TYPE                          tyK0 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit0(), tgCY1.Query_AxisUnit() );
    const TYPE                          tyK1 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit1(), tgCY1.Query_AxisUnit() );
    C_(VECTOR,DIM)                      tvK0 = MATH::F_MUL( tyK0, tgCY0.Query_BasisUnit0() );
    C_(VECTOR,DIM)                      tvK1 = MATH::F_MUL( tyK1, tgCY0.Query_BasisUnit1() );

    tvX10 = MATH::F_NORM( &tyTest, MATH::F_ADD( tvK0, tvK1 ) );

    if (tyTest <= LIMITS<TYPE>::EPSILON)
    {
        tvX10 = tgCY1.Query_BasisUnit0();
        tvX11 = tgCY1.Query_BasisUnit1();
    }
    else
    {
        MATH::F_UCX( &tvX11, tvX10, tgCY1.Query_AxisUnit() );
    };

    // Used the reference frame of cylinder 1 to create three symmetrical contacts around the rim/base.

    tvTest = MATH::F_ADD( tvC1,  MATH::F_MUL( tgCY1.Query_Radius(), tvX10 ) );
    tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

    if (tyTest < TYPE(0.0))
    {
        TgASSERT_MSG( TgFALSE, TgT("F_Internal_CapContained: Axis determined a fully contained cylinder but found no contacts.") );
        return (TgE_FAIL);
    };

    aF_Contact[0].m_tyDepth = tyTest;
    aF_Contact[0].m_tvPos = tvTest;
    nuiPoint = 1;

    C_(VECTOR,DIM)                      tvK2 = MATH::F_ADD( tvX10, MATH::F_MUL( KF32_SQRT3, tvX11 ) );

    tvTest = MATH::F_ADD( tvC1, MATH::F_MUL( tgCY1.Query_Radius()*TYPE(-0.5), tvK2 ) );
    tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

    if (tyTest > TYPE(0.0))
    {
        aF_Contact[nuiPoint].m_tvPos = tvTest;
        aF_Contact[nuiPoint].m_tyDepth = tyTest;
        ++nuiPoint;
    };

    C_(VECTOR,DIM)                      tvK3 = MATH::F_SUB( tvX10, MATH::F_MUL( KF32_SQRT3, tvX11 ) );

    tvTest = MATH::F_ADD( tvC1, MATH::F_MUL( tgCY1.Query_Radius()*TYPE(-0.5), tvK3 ) );
    tyTest = MATH::F_DOT( MATH::F_SUB( tvC0, tvTest ), tvNormal );

    if (tyTest > TYPE(0.0))
    {
        aF_Contact[nuiPoint].m_tvPos = tvTest;
        aF_Contact[nuiPoint].m_tyDepth = tyTest;
        ++nuiPoint;
    };

    return (TgS_OK);
};


// ============================================================================================================================== //

// ---- F_Internal_AxisCap ------------------------------------------------------------------------------------------------------ //
//  -- Internal Function -- (Helper to create contact points between the tube and cap of two cylinders)
// Input:  tvNormal: The axis of contact being used.
// Input:  tgCY0: Cylinder primitive
// Input:  tgCY1: Cylinder primitive - contact points are generated on this primitive
// Output: aF_Contact: An array of contact points to return for processing
// Output: nuiPoint: The number of valid points in the array
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM> TgFORCEINLINE
TgRESULT F_Internal_AxisCap(
    T_(CONTACT,DIM) aF_Contact[4], TgUINT32 &nuiPoint, C_(VECTOR,DIM) tvNormal, CR_(CYLINDER,DIM) tgCY0, CR_(CYLINDER,DIM) tgCY1
) {
    const TYPE                          tyDN0 = P::FSEL( MATH::F_DOT(tvNormal,tgCY0.Query_AxisUnit()), TYPE(-1.0), TYPE(1.0) );
    const TYPE                          tyDN1 = P::FSEL( MATH::F_DOT(tvNormal,tgCY1.Query_AxisUnit()), TYPE(1.0), TYPE(-1.0) );
    C_(VECTOR,DIM)                      tvC0 = MATH::F_ADD( tgCY0.Query_Origin(), MATH::F_MUL( tyDN0, tgCY0.Query_HalfAxis() ) );
    C_(VECTOR,DIM)                      tvC1 = MATH::F_ADD( tgCY1.Query_Origin(), MATH::F_MUL( tyDN1, tgCY1.Query_HalfAxis() ) );

    CR_(SEGMENT,DIM)                    tgSG_CY0 = tgCY0.Query_Segment();

    T_(VECTOR,DIM)                      tvX10,tvX11;
    TYPE                                tyT0,tyT1;

    // Create a reference frame for cylinder one based on the direction of deepest penetration.

    const TYPE                          tyK0 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit0(), tgCY1.Query_AxisUnit() );
    const TYPE                          tyK1 = -tyDN1*MATH::F_DOT( tgCY0.Query_BasisUnit1(), tgCY1.Query_AxisUnit() );
    C_(VECTOR,DIM)                      tvK0 = MATH::F_MUL( tyK0, tgCY0.Query_BasisUnit0() );
    C_(VECTOR,DIM)                      tvK1 = MATH::F_MUL( tyK1, tgCY0.Query_BasisUnit1() );

    tvX10 = MATH::F_NORM( MATH::F_ADD( tvK0, tvK1 ) );
    MATH::F_UCX( &tvX11, tvX10, tgCY0.Query_AxisUnit() );
    TgASSERT_MSG( Near_One( MATH::F_LSQ( tvX10 ) ), TgT("F_Internal_AxisCap: Degenerate basis set formed.") );

    // Create a segment along the length of the cylinder, in the direction of deepest penetration.

    T_(VECTOR,DIM)                      tvS0 = MATH::F_ADD( tgSG_CY0.Query_Origin(), MATH::F_MUL( tgCY0.Query_Radius(), tvX10 ) );
    T_(VECTOR,DIM)                      tvD0 = tgSG_CY0.Query_DirN();

    if (TTgCLP_TULN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, static_cast<CR_(TUBE,DIM)>(tgCY1), tvS0, tvD0 ) < 0)
    {
        return (TgE_NOINTERSECT);
    };

    T_(PLANE,DIM)                       tgPN0;

    tgPN0.Set( tvNormal, tvC1 );

    tvS0 = MATH::F_MUL( tvS0, tyT0 );
    tvD0 = MATH::F_MUL( tvD0, tyT1 - tyT0 );

    if (TTgCLP_PNLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgPN0, tvS0, tvD0 ) < 0)
    {
        return (TgE_NOINTERSECT);
    };

    // Segment was clipped to the cylinder surface.  Thus, the resulting point must be contained inside or on the cylinder.

    TYPE                                tyDH0, tyDH1;
    T_(VECTOR,DIM)                      tvT0, tvT1;

    tvT0 = MATH::F_ADD( tvS0, MATH::F_MUL( tyT0, tgSG_CY0.Query_DirN() ) );
    tyDH0 = MATH::F_DOT( MATH::F_SUB( tvT0, tvC1 ), tvNormal );

    tvT1 = MATH::F_ADD( tvS0, MATH::F_MUL( tyT1, tgSG_CY0.Query_DirN() ) );
    tyDH1 = MATH::F_DOT( MATH::F_SUB( tvT1, tvC1 ), tvNormal );

    aF_Contact[0].m_tvPos = tyDH0 - tyDH1 > TYPE(0.0) ? tvT0 : tvT1;
    aF_Contact[0].m_tyDepth = P::FSEL( tyDH0 - tyDH1, tyDH0, tyDH1 );
    aF_Contact[1].m_tvPos = tyDH0 - tyDH1 > TYPE(0.0) ? tvT1 : tvT0;
    aF_Contact[1].m_tyDepth = P::FSEL( tyDH0 - tyDH1, tyDH1, tyDH0 );

    // Always create the point of largest penetration but only use the second point if it would generate a counter-moment.

    nuiPoint = !Near_Zero( tyT0 - tyT1 ) && ((tyT0 - TYPE(0.5) < TYPE(0.0)) ^ (tyT1 - TYPE(0.5) < TYPE(0.0))) ? 2 : 1;

    return (TgS_OK);
};


// ============================================================================================================================== //

// ---- F_Contact_Penetrate ----------------------------------------------------------------------------------------------------- //
// Input:  tgPacket: The current series of contact points for this query-series, and contact generation parameters.
// Input:  tgCY0: Cylinder primitive
// Input:  tgCY1: Cylinder primitive - contact points are generated on this primitive
// Output: tgPacket: Points of penetration between the two primitives are added to it
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Contact_Penetrate( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(CYLINDER,DIM) tgCY0, CR_(CYLINDER,DIM) tgCY1 )
{
    TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) ))
    TgASSERT(tgCY0.Is_Valid() && tgCY1.Is_Valid())

    if (0 == ptgPacket->m_niMaxContact || ptgPacket->m_niContact >= ptgPacket->m_niMaxContact || NULL == ptgPacket->m_ptgContact)
    {
        return (TgE_FAIL);
    };

    // Find the minimal axis of separation, or return if the primitives are not in contact.

    T_(AXIS_RESULT,DIM)                 tgAxS;

    if (!F_Axis_Seperation( &tgAxS, tgCY0,tgCY1 ))
    {
        return (TgE_NOINTERSECT);
    };

    TgASSERT( Near_One( MATH::F_LSQ(tgAxS.m_tvNormal) ) && tgAxS.m_tyDepth >= TYPE(0.0) )

    // == Contact Generation ==================================================================================================== //

    P_(CONTACT,DIM)                     ptgContact;

    T_(CONTACT,DIM)                     aF_Contact[4];
    TgUINT32                            nuiPoint = 0;

    C_(VECTOR,DIM)                      tvDS = MATH::F_SUB( tgCY1.Query_Origin(), tgCY0.Query_Origin() );
    const TYPE                          tyDS_A0 = MATH::F_DOT(tvDS,tgCY0.Query_AxisUnit());
    const TYPE                          tyDS_A1 = MATH::F_DOT(tvDS,tgCY1.Query_AxisUnit());

    switch (tgAxS.m_iAxis)
    {
        case 0: { // -- Axis: Cylinder #0 Axis -------------------------------

            T_(ELLIPSE,DIM)                     tgEL1;

            if (Is_Cap_Contained( tgEL1, tgCY0,tyDS_A0,tgCY1,tyDS_A1 ))
            {
                if (P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgCY1.Query_AxisUnit()) ) < KF32_SQRT1_2)
                {
                    if (F_Internal_AxisCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY0,tgCY1 ) >= 0)
                    {
                        break;
                    };
                }
                else if (F_Internal_CapContained( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY0,tgCY1 ) >= 0)
                {
                    break;
                };
            }
            else
            {
                // The contact normal is the primary axis of cylinder zero.  Now we need to determine if the case to be generated
                // has the ends touching or an end and side touching.

                if (P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgCY1.Query_AxisUnit()) ) < KF32_SQRT1_2)
                {
                    if (F_Internal_AxisCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY0, tgCY1 ) >= 0)
                    {
                        break;
                    };
                }
                else if (F_Internal_CapCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgEL1, tgCY0,tgCY1 ) >= 0)
                {
                    break;
                };
            };

            return (TgE_NOINTERSECT);
        };

        case 2: { // -- Axis: Cylinder #1 Axis -------------------------------

            T_(ELLIPSE,DIM)                     tgEL0;

            if (Is_Cap_Contained( tgEL0, tgCY1,-tyDS_A1,tgCY0,-tyDS_A0 ))
            {
                if (P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgCY1.Query_AxisUnit()) ) < KF32_SQRT1_2)
                {
                    C_TgRESULT tgResult = F_Internal_AxisCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY1,tgCY0 );

                    if (tgResult >= 0)
                    {
                        // Since we swapped the primitives and normal during the contact generation we now need to take the points and
                        // reverse them back out so they are being generated on the correect geometry in the correct direction.
                        for (TgUINT32 uiIndex = 0; uiIndex < nuiPoint; ++uiIndex)
                        {
                            C_(VECTOR,DIM) tvK20 = MATH::F_MUL( aF_Contact[uiIndex].m_tyDepth, tgAxS.m_tvNormal );
                            aF_Contact[uiIndex].m_tvPos = MATH::F_SUB( aF_Contact[uiIndex].m_tvPos, tvK20 );
                        };
                        break;
                    };
                }
                else
                {
                    C_TgRESULT tgResult = F_Internal_CapContained( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY1,tgCY0 );

                    if (tgResult >= 0)
                    {
                        // Since we swapped the primitives and normal during the contact generation we now need to take the points and
                        // reverse them back out so they are being generated on the correect geometry in the correct direction.
                        for (TgUINT32 uiIndex = 0; uiIndex < nuiPoint; ++uiIndex)
                        {
                            C_(VECTOR,DIM) tvK21 = MATH::F_MUL( aF_Contact[uiIndex].m_tyDepth, tgAxS.m_tvNormal );
                            aF_Contact[uiIndex].m_tvPos = MATH::F_ADD( aF_Contact[uiIndex].m_tvPos, tvK21 );
                        };
                        tgAxS.m_tvNormal = MATH::F_NEG( tgAxS.m_tvNormal );
                        break;
                    };
                };
            }
            else
            {
                if (P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgCY1.Query_AxisUnit()) ) < KF32_SQRT1_2)
                {
                    C_TgRESULT tgResult = F_Internal_AxisCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgCY0,tgCY1 );

                    if (tgResult >= 0)
                    {
                        // Since we swapped the primitives and normal during the contact generation we now need to take the points and
                        // reverse them back out so they are being generated on the correect geometry in the correct direction.
                        for (TgUINT32 uiIndex = 0; uiIndex < nuiPoint; ++uiIndex)
                        {
                            C_(VECTOR,DIM) tvK22 = MATH::F_MUL( aF_Contact[uiIndex].m_tyDepth, tgAxS.m_tvNormal );
                            aF_Contact[uiIndex].m_tvPos = MATH::F_SUB( aF_Contact[uiIndex].m_tvPos, tvK22 );
                        };
                        break;
                    };
                }

                C_TgRESULT tgResult = F_Internal_CapCap( aF_Contact, nuiPoint, tgAxS.m_tvNormal, tgEL0, tgCY0,tgCY1 );

                if (tgResult >= 0)
                {
                    // Since we swapped the primitives and normal during the contact generation we now need to take the points and
                    // reverse them back out so they are being generated on the correect geometry in the correct direction.
                    for (TgUINT32 uiIndex = 0; uiIndex < nuiPoint; ++uiIndex)
                    {
                        C_(VECTOR,DIM) tvK23 = MATH::F_MUL( aF_Contact[uiIndex].m_tyDepth, tgAxS.m_tvNormal );
                        aF_Contact[uiIndex].m_tvPos = MATH::F_SUB( aF_Contact[uiIndex].m_tvPos, tvK23 );
                    };
                    break;
                };
            };

            return (TgE_NOINTERSECT);
        };

        case 4: { // -- Axis: Perpendicular to Common Cylinder Axis ----------

            C_(VECTOR,DIM)                      tvDS = MATH::F_SUB( tgCY0.Query_Origin(), tgCY1.Query_Origin() );
            const TYPE                          tyDS_A1 = MATH::F_DOT( tvDS, tgCY1.Query_AxisUnit() );

            const TYPE                          tyMinY = TgMAX( -tgCY1.Query_Extent(), tyDS_A1 - tgCY0.Query_Extent() );
            const TYPE                          tyMaxY = TgMIN(  tgCY1.Query_Extent(), tyDS_A1 + tgCY0.Query_Extent() );
            P_(CONTACT,DIM)                     ptgContact;

            if (!Near_Zero(tyMinY - tyMaxY))
            {
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

                C_(VECTOR,DIM)                      tvK30 = MATH::F_MUL( tyMinY, tgCY1.Query_AxisUnit() );
                C_(VECTOR,DIM)                      tvK31 = MATH::F_MUL( tgCY1.Query_Radius(), tgAxS.m_tvNormal );

                ptgContact->m_tvPos = MATH::F_SUB( MATH::F_ADD( tgCY1.Query_Origin(), tvK30 ), tvK31 );
                ptgContact->m_tvNormal = tgAxS.m_tvNormal;
                ptgContact->m_tyT0 = TYPE(0.0);
                ptgContact->m_tyDepth = tgAxS.m_tyDepth;

                ++ptgPacket->m_niContact;

                if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
                {
                    return (TgS_MAXCONTACTS);
                };
            };

            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            C_(VECTOR,DIM)                      tvK32 = MATH::F_MUL( tyMaxY, tgCY1.Query_AxisUnit() );
            C_(VECTOR,DIM)                      tvK33 = MATH::F_MUL( tgCY1.Query_Radius(), tgAxS.m_tvNormal );

            ptgContact->m_tvPos = MATH::F_SUB( MATH::F_ADD( tgCY1.Query_Origin(), tvK32 ), tvK33 );
            ptgContact->m_tvNormal = tgAxS.m_tvNormal;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tgAxS.m_tyDepth;

            ++ptgPacket->m_niContact;

            return (TgS_OK);
        };

        case 8:   // -- Axis: Contact Between the Two Cylinder Rims ----------
        case 5: { // -- Axis: Between Points of Closest Proximity ------------

            //  Since this axis is only generated when the points of closest proximity do not lie at the termini of the respective
            // cylinder axes, the resulting direction vector is known to be orthogonal to both axes.  (Proof by Observation:
            // the minimal distance would be along a mutually perpendicular vector, which is known to exist since the lines are
            // also known to be skew.)

            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            ptgContact->m_tvPos = tgAxS.m_tvPoint;
            ptgContact->m_tvNormal = tgAxS.m_tvNormal;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tgAxS.m_tyDepth;

            ++ptgPacket->m_niContact;

            return (TgS_OK);
        };

        case 6: { // -- Axis: Perpendicular to Axis of Cylinder 0 ------------

            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            ptgContact->m_tvPos = tgCY1.Calc_Support_Point( MATH::F_NEG( tgAxS.m_tvNormal ) );
            ptgContact->m_tvNormal = tgAxS.m_tvNormal;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tgAxS.m_tyDepth;

            ++ptgPacket->m_niContact;

            return (TgS_OK);
        };

        case 7: { // -- Axis: Perpendicular to Axis of Cylinder 1 ------------

            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            C_(VECTOR,DIM)                      tvK34 = MATH::F_MUL( tgAxS.m_tyDepth, tgAxS.m_tvNormal );

            ptgContact->m_tvPos = MATH::F_SUB( tgCY0.Calc_Support_Point( tgAxS.m_tvNormal ), tvK34 );
            ptgContact->m_tvNormal = tgAxS.m_tvNormal;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tgAxS.m_tyDepth;

            ++ptgPacket->m_niContact;

            return (TgS_OK);
        };
    };

    C_TgUINT32 nuiMax = TgMIN(nuiPoint,(TgUINT32)(ptgPacket->m_niMaxContact - ptgPacket->m_niContact));

    for (TgUINT32 iIdx = 0; iIdx < nuiMax; ++iIdx)
    {
        ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

        ptgContact->m_tvPos = aF_Contact[iIdx].m_tvPos;
        ptgContact->m_tvNormal = tgAxS.m_tvNormal;
        ptgContact->m_tyT0 = TYPE(0.0);
        ptgContact->m_tyDepth = aF_Contact[iIdx].m_tyDepth;

        ++ptgPacket->m_niContact;
    };

    return (nuiMax != nuiPoint ? TgS_MAXCONTACTS : TgS_OK);
};

template TgRESULT F_Contact_Penetrate( PC_TgF4CONTACT_PACKET, CR_TgF4CYLINDER, CR_TgF4CYLINDER );


// ============================================================================================================================== //

}; // END COL //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}; // END TGS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[an error occurred while processing this directive]