//////////////////////////////////////
// START of File: C1_KERNL.CPP
//////////////////////////////////////
                  
//===================================================================
//
// COPYRIGHT NOTICE:  This code is protected by Copyright!
//
// Copyright (c) 1994, 1995, 1996, 1997 by DPC Technology.
// All rights reserved.
//
// See files "cf_copyr.h" and "cf_licen.h" for details of
// copyright and license specifications, and contact information.
//
//===================================================================

// Include this source file's header file:
#include	<cf_api/bf_defs.h>
#include    <cf_api/c1_kernl.hpp>
            // relative to the IDE's (global) include path: ~/cfrz

// Include any other neccessary definitions:
#include    <math.h>
            // We'll need the std math lib for geometric functions.
            // Also for asserts that use ::fabs() functions.

#include    <stdlib.h>
            // We'll need this for the ::abs() function -- go figure!

//============================================================
// Implement Chart Framework Kernel Class class cf_Kernel.
// Note that complete implementation is version-dependent
// and is completed in ONE of two source files:
// "c2_cmodl.cpp" or "c3_cmodl.cpp"
//============================================================
cf_Kernel
    ::cf_Kernel(    double  dXDimension,
                    double  dYDimension,
                    double  dZDimension  )

    :   // No specific derivation from a base class

        // However, let's insure SPECIFIC instantiation(s):
        cf_2drDispAreaEntire_( 0, 0, 0, 0 ),
        cf_2drDispAreaMargin_( 0, 0, 0, 0 ),

        cf_2drProjLeftFaceArea_( 0, 0, 0, 0 ),
        cf_2drProjBackFaceArea_( 0, 0, 0, 0 ),
        cf_2drProjBottomFaceArea_( 0, 0, 0, 0 )

{
    // Init the Geometry and Projection Matrices:
    int i, j;

    for ( i = 0; i < 4; i++ )
        for ( j = 0; j < 4; j++ )
            this->cf_4by4mGeomMatrix[ i ][ j ] = 0.0;
            // The geometric transformation matrix transforms logical
            // world 3D coords according to rotation and perspective.

    for ( i = 0; i < 4; i++ )
        for ( j = 0; j < 2; j++ )
            this->cf_4by2mProjMatrix[ i ][ j ] = 0.0;
            // The projection transformation matrix transforms
            // projected 3D points to 2D screen coords.

    //-------------------------------------------------------------
    // Perform modular init opss with the "prefer" methods, but
    // do NOT force a geometry-matrix calc op until we are ready
    // (so last param of these calls will be FALSE):
    //-------------------------------------------------------------
    /*===========   To comply with this Spec:   ==============

                              7---------6
                             /|        /|
                            / |       / |
                  +Y       4---------5  |
                           |  3======|==2           -Z
                  y=0      | //      | //
                           |//       |//          z=0
                  -Y       0=========1
                                                +Z
                          -X   x=0   +X

    ==========================================================*/
    this->preferDimensions( dXDimension, dYDimension, dZDimension, FALSE );
    this->preferViewAngle(  CF_DEFAULT_X_ANGLE, CF_DEFAULT_Y_ANGLE, FALSE );
    this->preferPerspective( 0.0, FALSE );

    // Set default margins, which will affect upcoming
    // projection matrix calculations...
    iLeftMarginTwips_ = CF_DEFAULT_LEFT_MARGIN;
    iRightMarginTwips_ = CF_DEFAULT_RIGHT_MARGIN;
    iTopMarginTwips_ = CF_DEFAULT_TOP_MARGIN;
    iBottomMarginTwips_ = CF_DEFAULT_BOTTOM_MARGIN;
    this->bIsZoomModeOn = FALSE;

    // NOW -- calc the Geometry Matrix, which is dependent on
    // perspective value and view angles.  The Projection
    // Matrix will have to wait until we have a device context...
    this->calcGeomMatrix();

}   // end of c-tor


cf_Kernel
    ::~cf_Kernel()
        {}



cf_Kernel&
    cf_Kernel
        ::operator=( const cf_Kernel& cr_cf_kUserVal )
{
    // Copy the essential kernel object member vars:
    this->dXRotAngle = cr_cf_kUserVal.dXRotAngle;
    this->dYRotAngle = cr_cf_kUserVal.dYRotAngle;
    this->dZRotAngle = cr_cf_kUserVal.dZRotAngle;
                // These keep track of the 3D Display view angle.

    this->dPRatio = cr_cf_kUserVal.dPRatio;
                // Keep track of the 3D Display perspective view factors.
                // Note that the "Perspective" Value is calculated from
                // this "PRatio", which  is the user's chosen percent
                // value for the perspective value.

    iLeftMarginTwips_ = cr_cf_kUserVal.iLeftMarginTwips_;
    iRightMarginTwips_ = cr_cf_kUserVal.iRightMarginTwips_;
    iTopMarginTwips_ = cr_cf_kUserVal.iTopMarginTwips_;
    iBottomMarginTwips_ = cr_cf_kUserVal.iBottomMarginTwips_;
                // Keep track of Margin offsets due to additional
                // text spacing that will be outside the borders of
                // the 3D box -- These are LOGICAL units of Twips --
                // where one logical unit == 1/20th of a point, and
                // there are 72 points/inch :

    this->bIsZoomModeOn = cr_cf_kUserVal.bIsZoomModeOn;
    this->iSavedTopMargin = cr_cf_kUserVal.iSavedTopMargin;
    this->iSavedBottomMargin = cr_cf_kUserVal.iSavedBottomMargin;
    this->iSavedLeftMargin = cr_cf_kUserVal.iSavedLeftMargin;
    this->iSavedRightMargin = cr_cf_kUserVal.iSavedRightMargin;
                // Copy state vars for "zoom-mode" (zero margins).
                // Note that this neccessitates saving the existing
                // chart margin params, so we can restore them.

    // Copy the Geometry and Projection Matrices:
    int i, j;
    for ( i = 0; i < 4; i++ )
        for ( j = 0; j < 4; j++ )
            this->cf_4by4mGeomMatrix[ i ][ j ]
                    = cr_cf_kUserVal.cf_4by4mGeomMatrix[ i ][ j ];
                    // The geometric transformation matrix transforms
                    // logical world 3D coords according to rotation
                    // and perspective.

    for ( i = 0; i < 4; i++ )
        for ( j = 0; j < 2; j++ )
            this->cf_4by2mProjMatrix[ i ][ j ]
                    = cr_cf_kUserVal.cf_4by2mProjMatrix[ i ][ j ];
                    // The projection transformation matrix transforms
                    // projected 3D points to 2D screen coords.)

    for ( i = 0; i <= 7; i++ )
    {
        // Copy the INITIAL (data) world coords of the kernel's
        // 3D-bounding-box which encloses our 3D charting area.
        this->a_d3DBoxInitXCoord[ i ] = cr_cf_kUserVal.a_d3DBoxInitXCoord[ i ];
        this->a_d3DBoxInitYCoord[ i ] = cr_cf_kUserVal.a_d3DBoxInitYCoord[ i ];
        this->a_d3DBoxInitZCoord[ i ] = cr_cf_kUserVal.a_d3DBoxInitZCoord[ i ];

        // Copy the TRANSFORMED (virtual) world coords of 3D-bounding-box:
        this->a_d3DBoxXformXCoord[ i ] = cr_cf_kUserVal.a_d3DBoxXformXCoord[ i ];
        this->a_d3DBoxXformYCoord[ i ] = cr_cf_kUserVal.a_d3DBoxXformYCoord[ i ];
        this->a_d3DBoxXformZCoord[ i ] = cr_cf_kUserVal.a_d3DBoxXformZCoord[ i ];

        // Copy the PROJECTED coords of the kernel's 3D-bounding box:
        this->a_d3DBoxProjXCoord[ i ] = cr_cf_kUserVal.a_d3DBoxProjXCoord[ i ];
        this->a_d3DBoxProjYCoord[ i ] = cr_cf_kUserVal.a_d3DBoxProjYCoord[ i ];

        // Copy the projected SCREED coords of the kernel's TWO-D-bounding-box"
            // bounding box which encloses our 3D charting area.
        this->a_cf_2dp3DBoxProjScreenPoints[ i ]
            = (cf_2DPoint&) cr_cf_kUserVal.a_cf_2dp3DBoxProjScreenPoints[ i ];
    }

    // Copy the virtual coords of the kernel's 3D-box-origin.
    cr_cf_kUserVal.copy3DBoxOrigin(     &this->dChartBoxXOrigin,
                                        &this->dChartBoxYOrigin,
                                        &this->dChartBoxZOrigin );

    // Copy the virtual coords of the HSE (Hidden Surface Elimination) point.
    cr_cf_kUserVal.copyHseRefPointCoords(   &this->dDsortRefPointX,
                                            &this->dDsortRefPointY,
                                            &this->dDsortRefPointZ );

    // Copy the virtual coords of the BitmapWall wall's origin.
    cr_cf_kUserVal.copyBitmapWallOrigin(  &this->dBitmapWallXOrigin,
                                            &this->dBitmapWallYOrigin,
                                            &this->dBitmapWallZOrigin );
    // Copy the kernel dimension coords
    this->dXMin = cr_cf_kUserVal.dXMin;
    this->dXMax = cr_cf_kUserVal.dXMax;
    this->dYMin = cr_cf_kUserVal.dYMin;
    this->dYMax = cr_cf_kUserVal.dYMax;
    this->dZMin = cr_cf_kUserVal.dZMin;
    this->dZMax = cr_cf_kUserVal.dZMax;

    // Copy the calculated projection rectangles:
    this->cf_2drDispAreaEntire_  = cr_cf_kUserVal.cf_2drDispAreaEntire_;
    this->cf_2drDispAreaMargin_   = cr_cf_kUserVal.cf_2drDispAreaMargin_;
    this->cf_2drProjLeftFaceArea_   = cr_cf_kUserVal.cf_2drProjLeftFaceArea_;
    this->cf_2drProjBackFaceArea_   = cr_cf_kUserVal.cf_2drProjBackFaceArea_;
    this->cf_2drProjBottomFaceArea_ = cr_cf_kUserVal.cf_2drProjBottomFaceArea_;

    return *this;

}   // end of cf_Kernel::operator=( const cf_Kernel& cr_cf_kUserVal )


void
    cf_Kernel
        ::applyGeomXform(   double  *a_dInitXCoords,
                            double  *a_dInitYCoords,
                            double  *a_dInitZCoords,
                            int     iNumXYZPoints )
//-------------------------------------------------------------------
// Transform a set of 3D points via the Rotation & Perspective
// Geometry matrix.  This is a PRIVATE cf_Kernel function.
//
// We perform the following matrix multiplication:
//
//      ( adInitXCoords, adInitYCoords, adInitZCoords )
//                  * ( cf_4by4mGeomMatrix )
//
// and store the result BACK in
//          ( adInitXCoords, adInitYCoords, adInitZCoords ).
//
// iNumXYZPoints is the number of points in the data array.
//
// Note that the 3 coordinate arrays MUST be of equal length.
//-------------------------------------------------------------------
{
    double dTempXCoord, dTempYCoord, dTempZCoord;
    double dProjectionRatio;

    // Xform the set of points as follows:
    for ( int i = 0; i < iNumXYZPoints; i++ )
    {
        // Perform the matrix multiplication:
        dTempXCoord = cf_4by4mGeomMatrix[ 0 ][ 0 ] * a_dInitXCoords[ i ]
                    + cf_4by4mGeomMatrix[ 1 ][ 0 ] * a_dInitYCoords[ i ]
                    + cf_4by4mGeomMatrix[ 2 ][ 0 ] * a_dInitZCoords[ i ];

        dTempYCoord = cf_4by4mGeomMatrix[ 0 ][ 1 ] * a_dInitXCoords[ i ]
                    + cf_4by4mGeomMatrix[ 1 ][ 1 ] * a_dInitYCoords[ i ]
                    + cf_4by4mGeomMatrix[ 2 ][ 1 ] * a_dInitZCoords[ i ];

        dTempZCoord = cf_4by4mGeomMatrix[ 0 ][ 2 ] * a_dInitXCoords[ i ]
                    + cf_4by4mGeomMatrix[ 1 ][ 2 ] * a_dInitYCoords[ i ]
                    + cf_4by4mGeomMatrix[ 2 ][ 2 ] * a_dInitZCoords[ i ];

        dProjectionRatio = cf_4by4mGeomMatrix[ 0 ][ 3 ] * a_dInitXCoords[ i ]
                         + cf_4by4mGeomMatrix[ 1 ][ 3 ] * a_dInitYCoords[ i ]
                         + cf_4by4mGeomMatrix[ 2 ][ 3 ] * a_dInitZCoords[ i ]
                         + cf_4by4mGeomMatrix[ 3 ][ 3 ];

        // Perform the projection:
        #ifdef CF_ASSERT_DVALS
        assert( ::fabs( dProjectionRatio ) > CF_NUM_ZERO_IF_LT );
        #endif
        if ( ::fabs( dProjectionRatio ) > CF_NUM_ZERO_IF_LT )
        {
        		a_dInitXCoords[ i ] = dTempXCoord / dProjectionRatio;
        		a_dInitYCoords[ i ] = dTempYCoord / dProjectionRatio;
        }
        else
        {
        		a_dInitXCoords[ i ] = dTempXCoord;
        		a_dInitYCoords[ i ] = dTempYCoord;
        }
        a_dInitZCoords[ i ] = dTempZCoord;
    }
}   // end of cf_Kernel::applyGeomXform()


void
    cf_Kernel
        ::applyProjXform(   double      *a_dXformedXCoords,
                            double      *a_dXformedYCoords,
                            cf_2DPoint  *a_cf_2dpScreenCoords,
                            int         iNumXYZPoints )
//-------------------------------------------------------------------------
// THis PRIVATE cf_Kernel method transforms a set of 3D points via the
// Screen Projection Matrix via the following matrix multiplication:
//      ( adXformedXCoords, adXformedYCoords, adXformedZCoords )
//                      * ( cf_4by2mProjMatrix )
//
// ...and then store the resulting XY screen coords into the array
// a_cf_2dpScreenCoordPoints. iNumXYZPoints specifiec the number of
// points in the data array to process.  Note that the 3 coordinate
// arrays, and the 2D Point array, MUST be of equal length.
//-------------------------------------------------------------------------
{
    // Xform the set of points as follows:
    for ( int i = 0; i < iNumXYZPoints; i++ )
    {
        a_cf_2dpScreenCoords[ i ].x()
            = (unsigned int) ( cf_4by2mProjMatrix[ 0 ][ 0 ]
                    * a_dXformedXCoords[ i ] + cf_4by2mProjMatrix[ 3 ][ 0 ] );

        a_cf_2dpScreenCoords[ i ].y()
            = (unsigned int) ( cf_4by2mProjMatrix[ 1 ][ 1 ]
                    * a_dXformedYCoords[ i ] + cf_4by2mProjMatrix[ 3 ][ 1 ] );
    }
}   // end of cf_Kernel::applyProjXform( ... )




//=================================================================
//=================================================================
//
// NOTE that the chart-kernel member methods:
//
//  int
//      cf_Kernel
//        ::calcGeometry()
//
//  int
//      cf_Kernel
//        ::calcGeomPerspective()
//
//
// Are implemented in ONE of these SEPARATE source files --
// (depending upon the version of the ChartFolio Framework):
// "c2_kernl.cpp", or "c3_kernl.cpp".
//
//=================================================================
//=================================================================


void
    cf_Kernel
        ::calcProjMatrix()
//-------------------------------------------------------------
//
// Calculate the Projection Transformation Matrix (for a set
// of points in 3D world coordinates to map into 2D screen
// coordinates) onto the 2D window.
//
//-------------------------------------------------------------
{
    double  dMinX, dMaxX, dMinY, dMaxY;
                // Mins & Maxes for the Virtual 3D Box.

    double  d3DRangeX, d3DRangeY;
    double  d2DRangeX, d2DRangeY;
                // Range values for the 3D Box, as well
                // as the 2D projection plane.

    double  d2D3D_XRangeRatio,
                d2D3D_YRangeRatio,
                d2D3DProjectionRatio;
                // Ratios between the 2D and 3D Ranges,
                // One of which will be chosen to serve
                // as the overall 2D3D Projection Ratio.

    double  d3DXOffset, d3DYOffset;
                // If the 3D Box is not symmetric about
                // the 3D origin, (i.e. when the 3D Box
                // "sticks out too far" on one side),
                // we will need to calc 3D Offsets
                // neccessary for compensating for asymmetric
                // positive and negative ranges

    int         iProjOriginX,
                iProjOriginY;
                // We need to match up the center of the
                // screen projection with the center
                // of the geometric xform.

    int         i;  // Std. ICV.

    // Given the transformed (and updated coordinates) from the PREVIOUS
    // this->calcGeomMatrix() call, (and there had BETTER have been one!)...
    // ...Find the min & max for the TRANSFORMED 3D Box X & Y coordinates:

    // Update Min/Max Limit Values:
    dMinX   = a_d3DBoxInitXCoord[ 0 ];
    dMaxX   = a_d3DBoxInitXCoord[ 1 ];

    dMinY   = a_d3DBoxInitYCoord[ 0 ];
    dMaxY   = a_d3DBoxInitYCoord[ 4 ];  // ...w/ initial default vals:

    if ( this->bIsZoomModeOn == TRUE )
    //
    // In Normal (Non-"Zoom") Mode, we use the INITIAL
    // (i.e. virtual, untransformed) coords to search for
    // min/max vals to serve as the X & Y 3D ranges.
    //
    // "Zoom" Mode chooses the TRANSFORMED coords, which
    // makes the 3D Box always "snug up against" the closest
    // set of parallel boundaries (i.e. the horizontal
    // (top & bottom) margins, or the vertical (left & right)
    // margins)...
    //
    {
        for ( i = 0; i < 8; i++ )
        {
            // Determine Min/Max Xs:
            if ( a_d3DBoxXformXCoord[ i ] < dMinX )
                dMinX = a_d3DBoxXformXCoord[ i ];

            if ( a_d3DBoxXformXCoord[ i ] > dMaxX )
                dMaxX = a_d3DBoxXformXCoord[ i ];

            // Determine Min/Max Ys:
            if ( a_d3DBoxXformYCoord[ i ] < dMinY )
                dMinY = a_d3DBoxXformYCoord[ i ];

            if ( a_d3DBoxXformYCoord[ i ] > dMaxY )
                dMaxY = a_d3DBoxXformYCoord[ i ];
        }
    }
    else    // we're in non-zoom mode:
    {
        for ( i = 0; i < 8; i++ )
        {
            if ( a_d3DBoxInitXCoord[ i ] < dMinX )
                dMinX = a_d3DBoxInitXCoord[ i ];

            if ( a_d3DBoxInitXCoord[ i ] > dMaxX )
                dMaxX = a_d3DBoxInitXCoord[ i ];

            if ( a_d3DBoxInitYCoord[ i ] < dMinY )
                dMinY = a_d3DBoxInitYCoord[ i ];

            if ( a_d3DBoxInitYCoord[ i ] > dMaxY )
                dMaxY = a_d3DBoxInitYCoord[ i ];
        }
    }   /*
        =============   As per this Spec:   ==============

                          7---------6
                         /|        /|
                        / |       / |
              +Y       4---------5  |
                       |  3======|==2           -Z
              y=0      | //      | //
                       |//       |//          z=0
              -Y       0=========1
                                            +Z
                      -X   x=0   +X

        ==================================================
        */

    // Calc the 3D X and Y ranges:
    d3DRangeX = ::fabs( dMaxX - dMinX );
    d3DRangeY = ::fabs( dMaxY - dMinY );

    // In Normal (Non-"Zoom") Mode, calc BOTH 3D X *and* Y
    // ranges to be the hypotenuse of the X & Y 3D ranges.
    //
    // "Zoom" Mode chooses the LARGER of the two to act
    // as the 3D Range, which makes the 3D Box always "snug
    // up against" the closest set of parallel boundaries
    // (i.e. the horizontal (top & bottom) margins, or
    // the vertical (left & right) margins)...
    if ( this->bIsZoomModeOn != TRUE )
    {
        double  d3DRangeXSq = d3DRangeX * d3DRangeX;
        double  d3DRangeYSq = d3DRangeY * d3DRangeY;
        d3DRangeX = d3DRangeY = sqrt( ::fabs( d3DRangeXSq + d3DRangeYSq ) );
                        // Look!  It's a "hippopotamus"!...
                        // ... I mean... a hypotenuse... oh, WHATEVER!
    }

    // Calc 3D Offsets neccessary for compensating for asymmetric
    // positive and negative ranges (i.e. when the 3D Box "sticks
    // out too far" on one side).
    d3DXOffset = (dMinX + dMaxX) / 2.0;
    d3DYOffset = (dMinY + dMaxY) / 2.0;

    // Get the 2D X and Y Ranges:
    d2DRangeX = cf_2drDispAreaMargin_.width();
    d2DRangeY = cf_2drDispAreaMargin_.height();

    // We don't want our 2D Range to go negative -- this "flips"
    // our chart projection:
    if ( d2DRangeX < 0.0 ) d2DRangeX = 0.0;
    if ( d2DRangeY < 0.0 ) d2DRangeY = 0.0;

    // The LESSER 2D/3D Range Ratio serves as the d2D3DProjectionRatio;:
    #ifdef CF_ASSERT_DVALS
    assert( ::fabs( d3DRangeX ) > CF_NUM_ZERO_IF_LT );
    assert( ::fabs( d3DRangeY ) > CF_NUM_ZERO_IF_LT );
    #endif
    d2D3D_XRangeRatio = d2DRangeX / d3DRangeX;
    d2D3D_YRangeRatio = d2DRangeY / d3DRangeY;

    if ( d2D3D_XRangeRatio < d2D3D_YRangeRatio )
        d2D3DProjectionRatio = d2D3D_XRangeRatio;
    else
        d2D3DProjectionRatio = d2D3D_YRangeRatio;


    // Calc the Projection Center Point -- with offset values
    // based on the projections of the 3D Offsets calc'ed above:
    int iProjOriginOffsetX, iProjOriginOffsetY;

    iProjOriginOffsetX  = (int) (d3DXOffset * d2D3DProjectionRatio );
    iProjOriginOffsetY  = (int) (d3DYOffset * d2D3DProjectionRatio );

    iProjOriginX = (int)( ( cf_2drDispAreaMargin_.right()
                            + cf_2drDispAreaMargin_.left()) / 2 )
                                - iProjOriginOffsetX;

    iProjOriginY = (int)( ( cf_2drDispAreaMargin_.bottom()
                            + cf_2drDispAreaMargin_.top()) / 2 )
                                + iProjOriginOffsetY;
                    // NB: "+" and "-" directly above are VERY important!

    // We don't want our 2D Projection Point to go negative -- this
    // moves our chart projection out of the projection window:
    if ( iProjOriginX < 0 ) iProjOriginX = 0;
    if ( iProjOriginY < 0 ) iProjOriginY = 0;

    // Set up the transformation array:
    cf_4by2mProjMatrix[ 0 ][ 0 ] = d2D3DProjectionRatio;
    cf_4by2mProjMatrix[ 1 ][ 0 ] = 0.0;
    cf_4by2mProjMatrix[ 2 ][ 0 ] = 0.0;
    cf_4by2mProjMatrix[ 3 ][ 0 ] = (double) iProjOriginX;

    cf_4by2mProjMatrix[ 0 ][ 1 ] = 0.0;
    cf_4by2mProjMatrix[ 1 ][ 1 ] = -d2D3DProjectionRatio;
    cf_4by2mProjMatrix[ 2 ][ 1 ] = 0.0;
    cf_4by2mProjMatrix[ 3 ][ 1 ] = (double) iProjOriginY;

    // Go ahead and (re)apply the Projection Transform Matrix to the
    // screen coordinates for the 3D bounding box:
    for ( i = 0; i < 8; i++ )
    {
        a_d3DBoxProjXCoord[ i ] = a_d3DBoxXformXCoord[ i ];
        a_d3DBoxProjYCoord[ i ] = a_d3DBoxXformYCoord[ i ];
            // Reset the projected coordinates to the initial ones
            // to avoid  xforming already xformed coords!...
    }

    this->applyProjXform(   a_d3DBoxProjXCoord,
                            a_d3DBoxProjYCoord,
                            a_cf_2dp3DBoxProjScreenPoints,
                            8 );
                            // Xformed coords of 3D box now ready for use,
                            // as needed by other cf_Kernel components...

}   // end of cf_Kernel::calcProjMatrix( ... )


cf_2DRect
    cf_Kernel
        ::calcProjRectAreaFaceCoords(   double  *a_d4XCoords,
                                        double  *a_d4YCoords )
//----------------------------------------------------------------------
// We need to know how big the PROJECTED 3D Box faces are in
// order to properly scale fonts, as well as the BitmapWall bitmap.
// All three of the calcProjRectAreaFaceBack(), etc., members use this
// method, which makes the assumption that the context of the 4 points
// represented by the parameters passed is a rectangle, projected
// counter-clockwise.
//----------------------------------------------------------------------
{
    int     iRightEdgeLength, iLeftEdgeLength,
            iTopEdgeLength, iBottomEdgeLength;
            // The right, left, top, and bottom PROJECTED edge
            // lengths, respectively.  This is in accordance with
            // our vertex, edge, and Wall numbering convention.

    int     iProjectedWidth, iProjectedHeight;
            // The LARGER of the projection width edges becomes the
            // projection width, and so with the heights.  These are
            // used to construct the final cf_2DRect return value.

    int     iEdgeDeltaX, iEdgeDeltaY;
            // When calculating a projected edge length, an edge is
            // the hypotenuese of a deltaX, deltaY triangle. These
            // vars will be cast (as intermediate results) into
            // (double)s to work as params to the sqrt() calls below.

    cf_2DPoint a_cf_2dp4ScreenPoints[ 4 ];
                        // Holds result of proj calculation.

    // Apply a Projection xform to the (context-dependent) face points:
    this->applyProjXform( a_d4XCoords, a_d4YCoords, a_cf_2dp4ScreenPoints, 4 );
        //...note dependency on a successfully completed calcProjMatrix(...)

    // Calculate the PROJECTED width and height of the BitmapWall
    // bitmap. We're gonna hafta go thru some elementary trig here...
    //
    // The edges are numbered according to our established convention.
    // Note that this means that AE (a_cf_2dp4ScreenPoints Element) 0
    // is 3DCV (3D Cube Vertex) 0, AE 1 is 3DCV 3, AE 2 is 3DCV 7,
    // and AE 3 is 3DCV 4.

    // Calc the LEFT Edge projected Height:
    iEdgeDeltaX =  ::abs( a_cf_2dp4ScreenPoints[ 3 ].x()
                                - a_cf_2dp4ScreenPoints[ 0 ].x() );
    iEdgeDeltaY =  ::abs( a_cf_2dp4ScreenPoints[ 3 ].y()
                                - a_cf_2dp4ScreenPoints[ 0 ].y() );
    iLeftEdgeLength
        = (int) sqrt( (double) ::abs( iEdgeDeltaX * iEdgeDeltaX
                                    + iEdgeDeltaY * iEdgeDeltaY ) );

    // Calc the RIGHT Edge projected Height:
    iEdgeDeltaX =  ::abs( a_cf_2dp4ScreenPoints[ 2 ].x()
                                - a_cf_2dp4ScreenPoints[ 1 ].x() );
    iEdgeDeltaY =  ::abs( a_cf_2dp4ScreenPoints[ 2 ].y()
                                - a_cf_2dp4ScreenPoints[ 1 ].y() );
    iRightEdgeLength
        = (int) sqrt( (double) ::abs( iEdgeDeltaX * iEdgeDeltaX
                                    + iEdgeDeltaY * iEdgeDeltaY ) );

    // The bigger of the two is the PROJECTED face's HEIGHT.
    if ( iLeftEdgeLength >= iRightEdgeLength )
        iProjectedHeight = iLeftEdgeLength;
    else
        iProjectedHeight = iRightEdgeLength;

    // Calc the Bottom Edge projected Width:
    iEdgeDeltaX =  ::abs( a_cf_2dp4ScreenPoints[ 0 ].x()
                                    - a_cf_2dp4ScreenPoints[ 1 ].x() );
    iEdgeDeltaY =  ::abs( a_cf_2dp4ScreenPoints[ 0 ].y()
                                    - a_cf_2dp4ScreenPoints[ 1 ].y() );
    iBottomEdgeLength
        = (int) sqrt( (double) ::abs( iEdgeDeltaX * iEdgeDeltaX
                                    + iEdgeDeltaY * iEdgeDeltaY ) );

    // Calc the Top Edge projected Width:
    iEdgeDeltaX =  ::abs( a_cf_2dp4ScreenPoints[ 3 ].x()
                                    - a_cf_2dp4ScreenPoints[ 2 ].x() );
    iEdgeDeltaY =  ::abs( a_cf_2dp4ScreenPoints[ 3 ].y()
                                    - a_cf_2dp4ScreenPoints[ 2 ].y() );
    iTopEdgeLength
        =  (int) sqrt( (double) ::abs( iEdgeDeltaX * iEdgeDeltaX
                                    + iEdgeDeltaY * iEdgeDeltaY ) );

    // The bigger of the two is the PROJECTED face's WIDTH.
    if ( iTopEdgeLength >= iBottomEdgeLength )
        iProjectedWidth = iTopEdgeLength;
    else
        iProjectedWidth = iBottomEdgeLength;

    // Return the area we just calc'ed -- we need to do a two-stage return...
	cf_2DRect cf_2drReturnVal(  0, 0, // First specify Top Left X/Y...
                                // ...then specify Bottom Right X/Y
                                iProjectedWidth, iProjectedHeight );
	return( cf_2drReturnVal );
        // ... because some compilers (e.g. HPUX) complain of
        // constructors inside of return-statement param-lists.

}   // end of cf_Kernel::calcProjRectAreaFaceCoords()



void
    cf_Kernel
        ::calcProjRectAreaFaceBack()
{
    double a_d4XCoords[ 4 ]; double a_d4YCoords[ 4 ]; double a_d4ZCoords[ 4 ];
    this->copyBackFaceCoords( a_d4XCoords, a_d4YCoords, a_d4ZCoords );
    cf_2drProjBackFaceArea_
        = this->calcProjRectAreaFaceCoords( a_d4XCoords, a_d4YCoords );
}


void
    cf_Kernel
        ::calcProjRectAreaFaceBottom()
{
    double a_d4XCoords[ 4 ]; double a_d4YCoords[ 4 ]; double a_d4ZCoords[ 4 ];
    this->copyBottomFaceCoords( a_d4XCoords, a_d4YCoords, a_d4ZCoords );
    cf_2drProjBottomFaceArea_
        = this->calcProjRectAreaFaceCoords( a_d4XCoords, a_d4YCoords );
}


void
    cf_Kernel
        ::calcProjRectAreaFaceLeft()
{
    double a_d4XCoords[ 4 ]; double a_d4YCoords[ 4 ]; double a_d4ZCoords[ 4 ];
    this->copyLeftFaceCoords( a_d4XCoords, a_d4YCoords, a_d4ZCoords );
    cf_2drProjLeftFaceArea_
        = this->calcProjRectAreaFaceCoords( a_d4XCoords, a_d4YCoords );
}


//-----------------------------------------------------------
// Copy "3D Box" coordinates to user classes who need them:
//-----------------------------------------------------------
void
    cf_Kernel
        ::copy3DBoxCoords(  double  *a_d8BoxXCoords,
                            double  *a_d8BoxYCoords,
                            double  *a_d8BoxZCoords )
        const
{
    for ( int i = 0; i < 8; i++ )
    {
            *a_d8BoxXCoords++ = a_d3DBoxInitXCoord[ i ];
            *a_d8BoxYCoords++ = a_d3DBoxInitYCoord[ i ];
            *a_d8BoxZCoords++ = a_d3DBoxInitZCoord[ i ];
    }
}

void
    cf_Kernel
        ::copy3DBoxOrigin(  double  *dXCoord,
                            double  *dYCoord,
                            double  *dZCoord )
        const

{
    *dXCoord = dChartBoxXOrigin;
    *dYCoord = dChartBoxYOrigin;
    *dZCoord = dChartBoxZOrigin;
}


void
    cf_Kernel
        ::copyBackFaceCoords(   double  *a_d4FaceXCoords,
                                double  *a_d4FaceYCoords,
                                double  *a_d4FaceZCoords )
        const
{
    // First Back Face Point is a_d3DBoxInitXCoord[ 3 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 3 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 3 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 3 ];

    // Second Back Face Point is a_d3DBoxInitXCoord[ 2 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 2 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 2 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 2 ];

    // Third Back Face Point is a_d3DBoxInitXCoord[ 6 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 6 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 6 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 6 ];

    // Fourth Back Face Point is a_d3DBoxInitXCoord[ 7 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 7 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 7 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 7 ];
}


void
    cf_Kernel
        ::copyBottomFaceCoords( double  *a_d4FaceXCoords,
                                double  *a_d4FaceYCoords,
                                double  *a_d4FaceZCoords )
        const
{
    // Take advantage of the fact that the Bottom Face points
    // are in the array indices 0, 1, 2, 3:
    for ( int i = 0; i < 4; i++ )
    {
        *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ i ];
        *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ i ];
        *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ i ];

    }
}


void
    cf_Kernel
        ::copyBitmapWallOrigin(   double  *dXCoord,
                                double  *dYCoord,
                                double  *dZCoord  )
        const
{
    *dXCoord = dBitmapWallXOrigin;
    *dYCoord = dBitmapWallYOrigin;
    *dZCoord = dBitmapWallZOrigin;
}


void
    cf_Kernel
        ::copyHseRefPointCoords(    double  *dXCoord,
                                    double  *dYCoord,
                                    double  *dZCoord )
        const
{
    *dXCoord = dDsortRefPointX;
    *dYCoord = dDsortRefPointY;
    *dZCoord = dDsortRefPointZ;
    return;
}



void
    cf_Kernel
        ::copyLeftFaceCoords(   double  *a_d4FaceXCoords,
                                double  *a_d4FaceYCoords,
                                double  *a_d4FaceZCoords )
        const
{
    // First Left Face Point is a_d3DBoxInitXCoord[ 0 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 0 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 0 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 0 ];

    // Second Left Face Point is a_d3DBoxInitXCoord[ 3 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 3 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 3 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 3 ];

    // Third Left Face Point is a_d3DBoxInitXCoord[ 7 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 7 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 7 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 7 ];

    // Fourth Left Face Point is a_d3DBoxInitXCoord[ 4 ]:
    *a_d4FaceXCoords++ = a_d3DBoxInitXCoord[ 4 ];
    *a_d4FaceYCoords++ = a_d3DBoxInitYCoord[ 4 ];
    *a_d4FaceZCoords++ = a_d3DBoxInitZCoord[ 4 ];
}



double  cf_Kernel::dsortRefX()  { return this->dDsortRefPointX; }
double  cf_Kernel::dsortRefY()  { return this->dDsortRefPointY; }
double  cf_Kernel::dsortRefZ()  { return this->dDsortRefPointZ; }

int cf_Kernel::getBottomMarginTwips()   { return iBottomMarginTwips_; }
int cf_Kernel::getLeftMarginTwips()     { return iLeftMarginTwips_; }
int cf_Kernel::getRightMarginTwips()    { return iRightMarginTwips_; }
int cf_Kernel::getTopMarginTwips()      { return iTopMarginTwips_; }

double  cf_Kernel::getXAngle()      { return dXRotAngle; }
double  cf_Kernel::getYAngle()      { return dYRotAngle; }
double  cf_Kernel::getZAngle()      { return dZRotAngle; }

double  cf_Kernel::getPerspectiveRatio() { return dPRatio; }

double  cf_Kernel::getXDim()    { return( this->dXMax - this->dXMin ); }
double  cf_Kernel::getXMin()    { return this->dXMin; }
double  cf_Kernel::getXMax()    { return this->dXMax; }

double  cf_Kernel::getYDim()    { return( this->dYMax - this->dYMin ); }
double  cf_Kernel::getYMin()    { return this->dYMin; }
double  cf_Kernel::getYMax()    { return this->dYMax; }

double  cf_Kernel::getZDim()    { return( this->dZMax - this->dZMin ); }
double  cf_Kernel::getZMin()    { return this->dZMin; }
double  cf_Kernel::getZMax()    { return this->dZMax; }


void
    cf_Kernel
        ::measureDispRectEntire( bf_Display *p_bf_dspFromChartView )
{
    // Measure the display area into our rectangle:
    p_bf_dspFromChartView->dsp_getVisible( cf_2drDispAreaEntire_ );
            // Our measured 2D-Rectangle is now ready for reference:
}

void
    cf_Kernel
        ::measureDispRectMargin( bf_Display *p_bf_dspFromChartView )
{
    // Measure the display area into our rectangle:
    p_bf_dspFromChartView->dsp_getVisible( cf_2drDispAreaMargin_ );

    // Convert this rectangle from DEVICE coords to LOGICAL coords
    p_bf_dspFromChartView->dsp_devToLog( cf_2drDispAreaMargin_ );
                                    //...Logical Units in TWIPS.

    // Adjust the dimensions of rectangle for MARGINS:
    cf_2drDispAreaMargin_.left() = iLeftMarginTwips_;
    cf_2drDispAreaMargin_.top() = iTopMarginTwips_;

    cf_2drDispAreaMargin_.right() -= iRightMarginTwips_;
    cf_2drDispAreaMargin_.bottom() -= iBottomMarginTwips_;
                // Note that since Twip-Ys are in the reverse (negative)
                // direction from Pixel-Ys, we may have to adjust our calcs.

    // Convert this Margin Rectangle back to DEVICE coordinates
    p_bf_dspFromChartView->dsp_logToDev( cf_2drDispAreaMargin_ );
                           // Margin Rectangle is now in PIXELS.
}

//---------------------------------------------------------
// Geomeric "objects" will want to REFERENCE the geom/proj
// matrices, because it's MUCH faster than COPYING them!
//---------------------------------------------------------
const CF_4BY4_MATRIX&
    cf_Kernel
        ::referToGeomMatrix()
            const
    { return ( (const CF_4BY4_MATRIX&)(this->cf_4by4mGeomMatrix[ 0 ][ 0 ]) ); }

const CF_4BY2_MATRIX&
    cf_Kernel
        ::referToProjMatrix()
            const
    { return ( (const CF_4BY2_MATRIX&)(this->cf_4by2mProjMatrix[ 0 ][ 0 ]) ); }


//---------------------------------------------------------
// Provide user acccess to caluclated display projection
// rectangles via constant references:
//---------------------------------------------------------
const cf_2DRect&
    cf_Kernel
        ::referToDispRectEntire()
            const
    { return cf_2drDispAreaEntire_; }

const cf_2DRect&
    cf_Kernel
        ::referToDispRectMargin()
            const
    { return cf_2drDispAreaMargin_; }


const cf_2DRect&
    cf_Kernel
        ::referToProjRectFaceLeft()
            const
    { return cf_2drProjLeftFaceArea_; }

const cf_2DRect&
    cf_Kernel
        ::referToProjRectFaceBack()
            const
    { return cf_2drProjBackFaceArea_; }

const cf_2DRect&
    cf_Kernel
        ::referToProjRectFaceBottom()
            const
    { return cf_2drProjBottomFaceArea_; }




void
    cf_Kernel
        ::preferDimensions( 	double	dXDimension,
							    double	dYDimension,
							    double	dZDimension,
                                BOOL    bRecalcGeomMatrix )
//================================================================
// Set up the 3D box without any transformations, i.e.,
// (re)set the (private data) world coordinate box vertices
// to "enclose" (or bound) the run-time data values.
//
// The box is placed such that its centroid is the origin of
// the 3D space.
//================================================================
        /*
        =============   As per this Spec:   ==============

                          7---------6
                         /|        /|
                        / |       / |
              +Y       4---------5  |
                       |  3======|==2           -Z
              y=0      | //      | //
                       |//       |//          z=0
              -Y       0=========1
                                            +Z
                      -X   x=0   +X

        ==================================================
        */
{
    a_d3DBoxInitXCoord[ 1 ] = a_d3DBoxInitXCoord[ 2 ]
        = a_d3DBoxInitXCoord[ 5 ] = a_d3DBoxInitXCoord[ 6 ]
                = this->dXMax = dXDimension / 2.0;

    a_d3DBoxInitXCoord[ 0 ] = a_d3DBoxInitXCoord[ 3 ]
        = a_d3DBoxInitXCoord[ 4 ] = a_d3DBoxInitXCoord[ 7 ]
                = this->dXMin = - a_d3DBoxInitXCoord[ 1 ];

    a_d3DBoxInitYCoord[ 4 ] = a_d3DBoxInitYCoord[ 5 ]
        = a_d3DBoxInitYCoord[ 6 ] = a_d3DBoxInitYCoord[ 7 ]
                = this->dYMax = dYDimension / 2.0;

    a_d3DBoxInitYCoord[ 0 ] = a_d3DBoxInitYCoord[ 1 ]
        = a_d3DBoxInitYCoord[ 2 ] = a_d3DBoxInitYCoord[ 3 ]
                = this->dYMin = - a_d3DBoxInitYCoord[ 4 ];

    a_d3DBoxInitZCoord[ 0 ] = a_d3DBoxInitZCoord[ 1 ]
        = a_d3DBoxInitZCoord[ 4 ] = a_d3DBoxInitZCoord[ 5 ]
                = this->dZMax = dZDimension / 2.0;

    a_d3DBoxInitZCoord[ 2 ] = a_d3DBoxInitZCoord[ 3 ]
        = a_d3DBoxInitZCoord[ 6 ] = a_d3DBoxInitZCoord[ 7 ]
                = this->dZMin = - a_d3DBoxInitZCoord[ 0 ];

    this->dBitmapWallXOrigin = a_d3DBoxInitXCoord[ 4 ];
    this->dBitmapWallYOrigin = a_d3DBoxInitYCoord[ 4 ];
    this->dBitmapWallZOrigin = a_d3DBoxInitZCoord[ 4 ];

    this->dChartBoxXOrigin = a_d3DBoxInitXCoord[ 3 ];
    this->dChartBoxYOrigin = a_d3DBoxInitYCoord[ 3 ];
    this->dChartBoxZOrigin = a_d3DBoxInitZCoord[ 3 ];

    // Define the HSE/DSORT Ref Point:
    this->dDsortRefPointX = 0.0;
    this->dDsortRefPointY = 0.0;
    this->dDsortRefPointZ = this->dZMax * 2;

    // Recalc the geom matrix ONLY if the user specifically requests so!
    if ( bRecalcGeomMatrix == TRUE ) this->calcGeomMatrix();
}




void
cf_Kernel::preferMarginTop
        (   int     iMarginInTwips,
            BOOL    bRecalcProjMatrix )
{
    iTopMarginTwips_ = iMarginInTwips;
    if ( bRecalcProjMatrix == TRUE ) this->calcProjMatrix();
}

void
cf_Kernel::preferMarginBottom
        (   int     iMarginInTwips,
            BOOL    bRecalcProjMatrix )
{
    iBottomMarginTwips_ = iMarginInTwips;
    if ( bRecalcProjMatrix == TRUE ) this->calcProjMatrix();
}

void
cf_Kernel::preferMarginLeft
        (   int     iMarginInTwips,
            BOOL    bRecalcProjMatrix )
{
    iLeftMarginTwips_ = iMarginInTwips;
    if ( bRecalcProjMatrix == TRUE ) this->calcProjMatrix();
}

void
cf_Kernel::preferMarginRight
        (   int     iMarginInTwips,
            BOOL    bRecalcProjMatrix )
{
    iRightMarginTwips_ = iMarginInTwips;
    if ( bRecalcProjMatrix == TRUE ) this->calcProjMatrix();
}



void
cf_Kernel::preferPerspective
        (   double  dSetPercentPerspective,
            BOOL    bRecalcGeomMatrix )
{
    // Update the perspective -- IF it makes sense:
    if ( dSetPercentPerspective > CF_MAX_P_RATIO_PERCENT )
        this->dPRatio = CF_MAX_P_RATIO_PERCENT;
    else
        this->dPRatio = dSetPercentPerspective;

    // Recalc the geom matrix ONLY if the user specifically requests so!
    if ( bRecalcGeomMatrix == TRUE ) this->calcGeomMatrix();
}



void
cf_Kernel::preferViewAngle
            (   double  dSetXAngle,
                double  dSetYAngle,
                BOOL    bRecalcGeomMatrix )
{
    // Correct for bogus user parameter values:
    if ( dSetXAngle < 0.0 ) dSetXAngle = 0.0;
    if ( dSetXAngle > 90.0 ) dSetXAngle = 90.0;

    if ( dSetYAngle > 0.0 ) dSetYAngle = 0.0;
    if ( dSetYAngle < -90.0 ) dSetYAngle = -90.0;

    // Update the view-angle -- IF it makes sense:
    this->dXRotAngle = dSetXAngle;
    this->dYRotAngle = dSetYAngle;
    this->dZRotAngle = CF_DEFAULT_Z_ANGLE;

    // Recalc the geom matrix ONLY if the user specifically requests so!
    if ( bRecalcGeomMatrix == TRUE ) this->calcGeomMatrix();
}


BOOL
    cf_Kernel
        ::setZoomMode()
            { return this->bIsZoomModeOn; }


void
    cf_Kernel
        ::setZoomMode( BOOL bNewState )
{
    if ( bNewState == TRUE )
    {
        // Protect against multiple calls of ::setZoomMode( TRUE )
        // wiping out the saved margins state of this kernel:
        if ( !this->bIsZoomModeOn )
        {
            this->iSavedTopMargin = iTopMarginTwips_;
            this->iSavedBottomMargin = iBottomMarginTwips_;
            this->iSavedLeftMargin = iLeftMarginTwips_;
            this->iSavedRightMargin = iRightMarginTwips_;
        }

        iTopMarginTwips_   = CF_DEFAULT_TOP_MARGIN_ZOOM;
        iBottomMarginTwips_ = CF_DEFAULT_BOTTOM_MARGIN_ZOOM;
        iLeftMarginTwips_  = CF_DEFAULT_LEFT_MARGIN_ZOOM;
        iRightMarginTwips_ = CF_DEFAULT_RIGHT_MARGIN_ZOOM;
    }
    else
    {
        iTopMarginTwips_ = this->iSavedTopMargin;
        iBottomMarginTwips_ = this->iSavedBottomMargin;
        iLeftMarginTwips_ = this->iSavedLeftMargin;
        iRightMarginTwips_ = this->iSavedRightMargin;
    }

    this->bIsZoomModeOn = bNewState;

    return;
}

void
    cf_Kernel
        ::updateDisplayMetrics( bf_Display *p_bf_dspHasChanged )
{
    //----------------------------------------------------------------
    // Note that the owner/parent chart-control/view MUST have
    // established a valid (and locked/mutexed) device-context...
    //----------------------------------------------------------------

    // (Re)Measure the display-device's ENTIRE area...
    this->measureDispRectEntire( p_bf_dspHasChanged );

    //...and our display-margin which we will use...
    this->measureDispRectMargin( p_bf_dspHasChanged );

    //...to calculate our projection matrix...
    this->calcProjMatrix();

    //...then go ahead and figure the 2D-projection sizes...
    this->calcProjRectAreaFaceBack();
    this->calcProjRectAreaFaceLeft();
    this->calcProjRectAreaFaceBottom();
        //...of the 3 important faces of the chart.

    //----------------------------------------------------------------
    // ...and will be responsible for releasing any mutex/lock
    // of the display/device-context *after* this method is finished!
    //----------------------------------------------------------------
}


//
// End of cf_Kernel Implementation.
//

//////////////////////////////////////
// END of File: C1_KERNL.CPP
//////////////////////////////////////



