////////////////////////////////////////
// START of File: C1_ENGIN.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	<cf_api/bf_defs.h>
#include    <cf_api/c1_engin.hpp>
#include    <math.h>

//==========================================
// Implement the BASE class cf_ChartEngine:
//==========================================
cf_ChartEngine::cf_ChartEngine
                (   cf_ChartType    enumChartType,
                    cf_DispMode     enumDispMode )

    :   // Insure *known* init-states of important engine-components:
        cf_3dpEngineCentroid_( 0.0, 0.0, 0.0 ),
        cf_3dpEngObjVirtMin_( 0.0, 0.0, 0.0 ),
        cf_3dpEngObjVirtMax_( 0.0, 0.0, 0.0 ),

        p_cf_3dgtsXonBackFace_( NULL ),
        p_cf_3dgtsXonBottomFace_( NULL ),
        p_cf_3dgtsYonLeftFace_( NULL ),
        p_cf_3dgtsYonBackFace_( NULL ),
        p_cf_3dgtsZonLeftFace_( NULL ),
        p_cf_3dgtsZonBottomFace_( NULL ),
        p_cf_3dgtsPolarOnFloor_( NULL )
{
    // We'll first need to make a geometry-kernel for ourselves:
    p_cf_kEnginesKernel_ = new cf_Kernel();
    bWasEnginesKernelNewed_ = TRUE;
    p_cf_kEnginesKernel_->copy3DBoxOrigin(  &dChartXOrigin_,
                                            &dChartYOrigin_,
                                            &dChartZOrigin_ );
    measureVirtOrigin();

    // Initialize our "chart-behavior/typing" variables:
    cf_enumChartEngineType_ = enumChartType;
    cf_enumCurrentDisplayMode_ = enumDispMode;
        // Because Chart-Typing can be VERY important, it should
        // preceed ANY chart-type and/or index-method dependent configs!

    //----------------------------------------
    // Perform other modular initializations:
    //----------------------------------------
    this->initObjectDynamicStorage();
            // Init the dynamic storage for the chart's objects -- in this
            // implementation, Dynamic Arrays of pointers to cf_Virt3DObjs
            // -- or really, objects *derived* from cf_Virt3DObjs...

    this->init3DBoxETC();
            // Init the "3D Box" to the current kernel dimensions!

    this->initDrawingAttribs();
            // New and Init the chart/engine's object's drawing attribs.
            // Note that we do this *after* we have the 3D-Box established!

    this->initGridTickSeries();
    this->initTextMarkers();
            // Make these all explicitly and *test-ably* NULL!
            // NB!  Order is important here -- these depend on
            // the 3D-Box setup done immediately ablve!

    //-----------------------------------------------------------
    // Finally, make sure that we can track our *first* draw op,
    // so we can gather any neccessary display-metrics, etc.
    //-----------------------------------------------------------
    bHasEverRendered_ = FALSE;
            // ...will be reset to TRUE upon first draw() op.
}


cf_ChartEngine
    ::~cf_ChartEngine()
{
    if ( bWasEnginesKernelNewed_ == TRUE ) delete p_cf_kEnginesKernel_;

    // Delete ANYTHING we new'ed -- but be CAREFUL !!!
    if ( p_cf_3dbmpProjectedBitmap_ != NULL )
                    delete p_cf_3dbmpProjectedBitmap_;
                    // from when we made a chart bitmap wall...

    // We new()-ed our 3d Box display attribs, so loose 'em
    if ( p_cf_datbBackFace_ != NULL )
                    delete p_cf_datbBackFace_;
    if ( p_cf_datbLeftFace_ != NULL )
                    delete p_cf_datbLeftFace_;
    if ( p_cf_datbBottomFace_ != NULL )
                    delete p_cf_datbBottomFace_;

    // NOW... the heavy stuff!  Delete the pointer allocations
    // of any dynamic engine objects we are responsible for!
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        if ( da_p_cf_objCompoObjects_[ i ] != NULL )
            delete ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] );

    for ( i = 1; i <= iNumFaceObjects_; i++ )
        if ( da_p_cf_objFaceObjects_[ i ] != NULL )
            delete ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] );

    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        if ( da_p_cf_objSpaceObjects_[ i ] != NULL )
            delete ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] );

    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        if ( da_p_cf_objMarkerObjects_[ i ] != NULL )
            delete ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] );

    // Well... if we haven't crashed (grin) -- we're DONE!
    return;
}



cf_Kernel*
cf_ChartEngine::activeKernel()
{
    return p_cf_kEnginesKernel_;
}


BOOL
cf_ChartEngine::activeKernel
            (   cf_Kernel   *p_cf_kNewKernel,
                BOOL        bDeleteOldKernelNow,
                BOOL        bDeleteNewKernelAtDtor )
{
    BOOL        bStatus = FALSE;    // tentatively (and pessimistically)...
    cf_Kernel   *p_cf_kOldKernel = p_cf_kEnginesKernel_;

    if ( p_cf_kNewKernel != NULL )
    {
        // Point AWAY from the original kernel:
        p_cf_kEnginesKernel_ = p_cf_kNewKernel;

        // Should we DELETE the old kernel?
        if ( bDeleteOldKernelNow == TRUE )
            if ( bWasEnginesKernelNewed_ == TRUE )
                delete p_cf_kOldKernel;

        // Are we responsible for cleaning up this NEW kernel?
        bWasEnginesKernelNewed_ = bDeleteNewKernelAtDtor;

        // Notify any other kernel-dependent objects we control!...
        if ( p_cf_3dbmpProjectedBitmap_ != NULL )
            p_cf_3dbmpProjectedBitmap_->setKernel( p_cf_kEnginesKernel_ );

        // Notify the caller that we did our duty...
        bStatus = TRUE;
    }
    return( bStatus );
}


void
    cf_ChartEngine
        ::addCompoObject( cf_Virt3DObj *p_cf_objAddThisObject )
{
    p_cf_objAddThisObject->applyTranslateOp(    dChartXOrigin_,
                                                dChartYOrigin_,
                                                dChartZOrigin_  );
    iNumCompoObjects_++;
    da_p_cf_objCompoObjects_[ iNumCompoObjects_ ] = p_cf_objAddThisObject;
    return;
}   // end of cf_ChartEngine::addCompoObject( cf_Virt3DObj* )



void
    cf_ChartEngine
        ::addFaceObject( cf_Virt3DObj *p_cf_objAddThisObject )
{
    p_cf_objAddThisObject->applyTranslateOp(    dChartXOrigin_,
                                                dChartYOrigin_,
                                                dChartZOrigin_  );
    iNumFaceObjects_++;
    da_p_cf_objFaceObjects_[ iNumFaceObjects_ ] = p_cf_objAddThisObject;
    return;
}   // end of cf_ChartEngine::addFaceObject( cf_Virt3DObj* )


void
    cf_ChartEngine
        ::addSpaceObject( cf_Virt3DObj *p_cf_objAddThisObject )
{
    p_cf_objAddThisObject->applyTranslateOp(    dChartXOrigin_,
                                                dChartYOrigin_,
                                                dChartZOrigin_  );
    iNumSpaceObjects_++;
    da_p_cf_objSpaceObjects_[ iNumSpaceObjects_ ] = p_cf_objAddThisObject;
    return;
}   // end of cf_ChartEngine::addSpaceObject( cf_Virt3DObj* )


void
    cf_ChartEngine
        ::addMarkerObject( cf_Virt3DObj *p_cf_objAddThisObject )
{
    p_cf_objAddThisObject->applyTranslateOp(    dChartXOrigin_,
                                                dChartYOrigin_,
                                                dChartZOrigin_  );
    iNumMarkerObjects_++;
    da_p_cf_objMarkerObjects_[ iNumMarkerObjects_ ] = p_cf_objAddThisObject;
    return;
}   // end of cf_ChartEngine::addMarkerObject( cf_Virt3DObj* )



void
cf_ChartEngine::applyDataUpdate
                ( cf_ChartModel *p_cf_cmodlDataSource )
//--------------------------------------------------------------------------
// This BASE update-method implements modular functionality that is almost
// ALWAYS called from other DERIVED update-methods (based on type ID, etc.).
//--------------------------------------------------------------------------
{
    // Get rid of any previously allocated set(s) of chart-objects:
    resetEngineObjects();

    // Reset our virtual scaling metrics:
    measureDataMinMax( p_cf_cmodlDataSource );
    measureVirtMinMax();

    markerMgrTitlesAxes()->applyDataUpdate( p_cf_cmodlDataSource );
        // the main/sub/axis-title marker-manager resets, then auto-generates.

    markerMgrUserLegends()->applyDataUpdate( p_cf_cmodlDataSource );
        // the user-legend marker-manager resets, then auto-generates.
}


//==========================================================================
// Use the Given Chart Framework Kernel to apply various transformation(s)
// to this cf_ChartEngine and its components (by using our engine-kernel):
//==========================================================================
void
cf_ChartEngine::applyDispMetrics()
{
    // Update the Chart Faces:
    cf_3dpgLeftFace_.applyDispMetrics( p_cf_kEnginesKernel_ );
    cf_3dpgBackFace_.applyDispMetrics( p_cf_kEnginesKernel_ );
    cf_3dpgBottomFace_.applyDispMetrics( p_cf_kEnginesKernel_ );

    // Update the Face Component List ( tick marks, etc):
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                            ->applyDispMetrics( p_cf_kEnginesKernel_ );

    // Update the Face Object List ( control lines, etc):
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                            ->applyDispMetrics( p_cf_kEnginesKernel_ );

    // Update the Space Object List ( ribbons, etc):
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                            ->applyDispMetrics( p_cf_kEnginesKernel_ );

    // Update the Marker Object List:
    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
                            ->applyDispMetrics( p_cf_kEnginesKernel_ );

    // Note it's NOT neccessary to update the invis-Min/Max Tracking points:
}


void
cf_ChartEngine::applyGeomXform()
{
    // Process the Chart Faces:
    cf_3dpgLeftFace_.applyGeomXform( p_cf_kEnginesKernel_ );
    cf_3dpgBackFace_.applyGeomXform( p_cf_kEnginesKernel_ );
    cf_3dpgBottomFace_.applyGeomXform( p_cf_kEnginesKernel_ );

    // Process the Face Component List ( tick marks, etc):
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                            ->applyGeomXform( p_cf_kEnginesKernel_ );

    // Process the Face Object List ( control lines, etc):
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                            ->applyGeomXform( p_cf_kEnginesKernel_ );

    // Process the Space Object List ( ribbons, etc):
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                            ->applyGeomXform( p_cf_kEnginesKernel_ );

    // Process the Marker Object List ( XY Text Markers, etc):
    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
                    ->applyGeomXform( p_cf_kEnginesKernel_ );

    // Process the invisible virtual/data-limit points:
    cf_3dpEngineCentroid_.applyGeomXform( p_cf_kEnginesKernel_ );
    cf_3dpEngObjVirtMin_.applyGeomXform( p_cf_kEnginesKernel_ );
    cf_3dpEngObjVirtMax_.applyGeomXform( p_cf_kEnginesKernel_ );

}   // end of cf_ChartEngine::applyGeomXform(...);


void
cf_ChartEngine::applyProjXform()
{
    // Project the Chart Faces:
    cf_3dpgLeftFace_.applyProjXform( p_cf_kEnginesKernel_ );
    cf_3dpgBackFace_.applyProjXform( p_cf_kEnginesKernel_ );
    cf_3dpgBottomFace_.applyProjXform( p_cf_kEnginesKernel_ );

    // Project the Face Component List ( tick marks, etc):
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                            ->applyProjXform( p_cf_kEnginesKernel_ );

    // Project the Face Object List ( control lines, etc):
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                            ->applyProjXform( p_cf_kEnginesKernel_ );

    // Project the Space Object List ( ribbons, etc):
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                            ->applyProjXform( p_cf_kEnginesKernel_ );

    // Project the Marker Object List:
    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
                            ->applyProjXform( p_cf_kEnginesKernel_ );

    // Process the invisible virtual/data-limit points:
    cf_3dpEngineCentroid_.applyProjXform( p_cf_kEnginesKernel_ );
    cf_3dpEngObjVirtMin_.applyProjXform( p_cf_kEnginesKernel_ );
    cf_3dpEngObjVirtMax_.applyProjXform( p_cf_kEnginesKernel_ );

}   // end of cf_ChartEngine::applyProjXform(...);



void
cf_ChartEngine::applyVirtScalings()
{
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                    ->applyScaleOp( dChartObjectXScaleFactor_,
                                    dChartObjectYScaleFactor_,
                                    dChartObjectZScaleFactor_ );
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                    ->applyScaleOp( dChartObjectXScaleFactor_,
                                    dChartObjectYScaleFactor_,
                                    dChartObjectZScaleFactor_ );
}



void
cf_ChartEngine::applyVirtTranslations()
{
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                ->applyTranslateOp( dChartObjectXTransFactor_,
                                    dChartObjectYTransFactor_, 
                                    dChartObjectZTransFactor_ ); 
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                ->applyTranslateOp( dChartObjectXTransFactor_,
                                    dChartObjectYTransFactor_, 
                                    dChartObjectZTransFactor_ ); 
}


cf_DispAttribs*
cf_ChartEngine::attribChartFaceDisp( cf_ChartFace enumChartFaceSelector )
{
    cf_DispAttribs  *p_cf_datbRetVal = p_cf_datbBackFace_;

    if ( enumChartFaceSelector == CF_FACE_LEFT )
        p_cf_datbRetVal = p_cf_datbLeftFace_;
    else if ( enumChartFaceSelector == CF_FACE_BOTTOM )
        p_cf_datbRetVal = p_cf_datbBottomFace_;

    return( p_cf_datbRetVal );
}


//--------------------------------------------------------------------------
// This member method modularizes the calculation of the  essential chart
// dynamic scaling parameter via the other chart engine member methods...
//--------------------------------------------------------------------------
void
cf_ChartEngine::autoConfigure()
{
    // Set up the 3D Box's "reference points" -- Notice that
    // we MAY use the methods of the *derived* engine-objects!...
    measureVirtOrigin();
    config3DBoxETC();

    // Compute and Apply any neccessary
    // Object Scalings & Translations:
    measureVirtMinMax();
    calcVirtScaleFactors();
    calcVirtTransFactors();   // ...computed!
    applyVirtScalings();
    applyVirtTranslations();  // ...applied!
}



cf_3DGridTickSeries*
cf_ChartEngine::axis_X_BackFace()
{
    return( p_cf_3dgtsXonBackFace_ );
}


cf_3DGridTickSeries*
cf_ChartEngine::axis_X_BottomFace()
{
    return( p_cf_3dgtsXonBottomFace_ );
}



cf_3DGridTickSeries*
cf_ChartEngine::axis_Y_LeftFace()
{
    return( p_cf_3dgtsYonLeftFace_ );
}



cf_3DGridTickSeries*
cf_ChartEngine::axis_Y_BackFace()
{
    return( p_cf_3dgtsYonBackFace_ );
}


cf_3DGridTickSeries*
cf_ChartEngine::axis_Z_LeftFace()
{
    return( p_cf_3dgtsZonLeftFace_ );
}



cf_3DGridTickSeries*
cf_ChartEngine::axis_Z_BottomFace()
{
    return( p_cf_3dgtsZonBottomFace_ );
}



cf_3DGridTickSeries*
cf_ChartEngine::axis_Polar_Floor()
{
    return( p_cf_3dgtsPolarOnFloor_ );
}



//
// Manipulate the Chart's Bitmap Wall Source File:
//
BOOL
    cf_ChartEngine
        ::bitmapChartWallFile() 
            { return p_cf_3dbmpProjectedBitmap_->isFileSet(); }

void
    cf_ChartEngine
        ::bitmapChartWallFileName( const char *a_cFileNameString )
            { p_cf_3dbmpProjectedBitmap_
                        ->sourceFileName( a_cFileNameString ); }    

const char*  
    cf_ChartEngine
        ::bitmapChartWallFileName()
            { return p_cf_3dbmpProjectedBitmap_->sourceFileName(); }

//
// Manipulate the Chart's Bitmap Background Wall Paper Source File:
//
BOOL
    cf_ChartEngine
        ::bitmapWallPaperFile() 
{
    if ( cf_strWallPaperFile_.cf_strlen() == 0 )
        return FALSE;
    else
        return TRUE;
}

void
    cf_ChartEngine
        ::bitmapWallPaperFileName( const char *a_cFileNameString )
            { cf_strWallPaperFile_ = a_cFileNameString; }

const char*
    cf_ChartEngine
        ::bitmapWallPaperFileName()
            { return cf_strWallPaperFile_; }



void
cf_ChartEngine::calcVirtScaleFactors()
{
    // Once we have MAX & MIN values established, (from this 
    // chart/engine measureVirtMinMax() member method) we can
    // calc the DELTA values, and thence the Range Ratios and
    // resulting Scale Factors...
    double  dVirtDeltaX = dVirtXRangeMax_ - dVirtXRangeMin_; 
    double  dVirtDeltaY = dVirtYRangeMax_ - dVirtYRangeMin_;
    double  dVirtDeltaZ = dVirtZRangeMax_ - dVirtZRangeMin_; 

    dChartObjectXScaleFactor_ = 1.0;   // Unless updated below...
    if ( fabs( dVirtDeltaX ) > CF_NUM_ZERO_IF_LT )
            dChartObjectXScaleFactor_ = d3DBoxXDim_ / dVirtDeltaX;

    dChartObjectYScaleFactor_ = 1.0;   // ditto...
    if ( fabs( dVirtDeltaY ) > CF_NUM_ZERO_IF_LT )
            dChartObjectYScaleFactor_ = d3DBoxYDim_ / dVirtDeltaY;

    dChartObjectZScaleFactor_ = 1.0;   // and ditto...
    if ( fabs( dVirtDeltaZ ) > CF_NUM_ZERO_IF_LT )
            dChartObjectZScaleFactor_ = d3DBoxZDim_ / dVirtDeltaZ;
}



void
cf_ChartEngine::calcVirtTransFactors()
{
    dChartObjectXTransFactor_
    = ( dChartXOrigin_ - dVirtXRangeMin_ ) * dChartObjectXScaleFactor_;

    dChartObjectYTransFactor_ 
    = ( dChartYOrigin_ - dVirtYRangeMin_ ) * dChartObjectYScaleFactor_;

    dChartObjectZTransFactor_ 
    = ( dChartZOrigin_ - dVirtZRangeMin_ ) * dChartObjectZScaleFactor_;
}




cf_ChartType
cf_ChartEngine::chartType()
{
    return( cf_enumChartEngineType_ );
}



BOOL
cf_ChartEngine::chartTypeBarColStackIs()
{
    BOOL    bReturnVal = FALSE;
    if ( cf_enumChartEngineType_ == CF_TYPE_BAR )    bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_COLUMN ) bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_STACK )  bReturnVal = TRUE;
    return( bReturnVal );
}

BOOL
cf_ChartEngine::chartTypeCartesianIs()
{
    BOOL    bReturnVal = FALSE;
    if ( cf_enumChartEngineType_ == CF_TYPE_PSER )   bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_LINE )   bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_AREA )   bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_BAR )    bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_COLUMN ) bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_STACK )  bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_RIBBON ) bReturnVal = TRUE;
    return( bReturnVal );
}

BOOL
cf_ChartEngine::chartTypePolarIs()
{
    BOOL    bReturnVal = FALSE;
    if ( cf_enumChartEngineType_ == CF_TYPE_PIE )    bReturnVal = TRUE;
    if ( cf_enumChartEngineType_ == CF_TYPE_XPIE )   bReturnVal = TRUE;
    return( bReturnVal );
}


//
// This private member modularizes the engine calculations:
//
// If we change coordinates within the chart, or change the
// dimensions of the kernel, we should recalc the chart.
//
void
cf_ChartEngine::config3DBoxETC()
{
    // Make a record of the LAST set of 3D Box dimensions
    // for upcoming rescaling ops on chart components:
    double dLastXDim = d3DBoxXDim_;
    double dLastYDim = d3DBoxYDim_;
    double dLastZDim = d3DBoxZDim_;

    // RESET vars which hold the kernel's CURRENT 3D Box Coords:
    d3DBoxXDim_ = p_cf_kEnginesKernel_->getXDim();
    d3DBoxYDim_ = p_cf_kEnginesKernel_->getYDim();
    d3DBoxZDim_ = p_cf_kEnginesKernel_->getZDim();

    // Calc local re-scale-ing factors to apply to the chart components:
    double  dCXSF = 1.0;    // CXSF = Component X Scale Factor
    double  dCYSF = 1.0;    // CYSF = Component Y Scale Factor
    double  dCZSF = 1.0;    // CZSF = Component Z Scale Factor
    BOOL    bUpdateScaleFacs = FALSE;

    if ( d3DBoxXDim_ != dLastXDim )
    {
        bUpdateScaleFacs = TRUE;
        // Avoid numeric overflows caused by small divisors:
        // Was originally: "if ( dLastXDim != 0.0 )"
        if ( fabs( dLastXDim ) > CF_NUM_ZERO_IF_LT )
            dCXSF = d3DBoxXDim_ / dLastXDim;
    }
    if ( d3DBoxYDim_ != dLastYDim )
    {
        bUpdateScaleFacs = TRUE;
        if ( fabs( dLastYDim ) > CF_NUM_ZERO_IF_LT ) // ditto as above...
            dCYSF = d3DBoxYDim_ / dLastYDim;// Otherwise = 1.0 per above.
    }
    if ( d3DBoxZDim_ != dLastZDim )
    {
        bUpdateScaleFacs = TRUE;
        if ( fabs( dLastZDim ) > CF_NUM_ZERO_IF_LT ) // ditto as above...
            dCZSF = d3DBoxZDim_ / dLastZDim;// Otherwise = 1.0 per above.
    }

    //--------------------------------------------------
    // Rescale Chart Components (e.g. tick marks, etc)
    // to match the new 3D Box dimensions:
    //--------------------------------------------------
    if ( bUpdateScaleFacs == TRUE )
    {
        // First re-scale the 3D Box Faces:
        cf_3dpgLeftFace_.applyScaleOp( dCXSF, dCYSF, dCZSF );
        cf_3dpgBackFace_.applyScaleOp( dCXSF, dCYSF, dCZSF );
        cf_3dpgBottomFace_.applyScaleOp( dCXSF, dCYSF, dCZSF );

        // Then re-scale other components, such as tick marks, etc:
//        for ( i = 1; i <= iNumCompoObjects_; i++ )
//            ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
//                            ->applyScaleOp( dCXSF, dCYSF, dCZSF );

//        for ( i = 1; i <= iNumMarkerObjects_; i++ )
//            ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
//                            ->applyScaleOp( dCXSF, dCYSF, dCZSF );
    }

}   // end of cf_ChartEngine::config3DBoxETC()




cf_DispMode
cf_ChartEngine::disp2D3DMode()
{
    return( cf_enumCurrentDisplayMode_ );
}


BOOL
cf_ChartEngine::disp2D3DMode
            ( cf_DispMode enumNewDispMode )
{
    BOOL    bStatus = FALSE;
    BOOL    bNeedToReConfig = FALSE;
            // Which can happen, for instance, under repeated
            // (i.e. redundant) calls to the same display-mode:

    if ( enumNewDispMode == CF_DISP_2D )
    {   // The caller must want to go from 3D-mode into 2D-Mode:
        if ( cf_enumCurrentDisplayMode_ == CF_DISP_3D ) bNeedToReConfig = TRUE;

        // Save current view params for subsequent recall:
        dSaveXAngle_ = p_cf_kEnginesKernel_->getXAngle();
        dSaveYAngle_ = p_cf_kEnginesKernel_->getYAngle();
        dSaveZAngle_ = p_cf_kEnginesKernel_->getZAngle();
        dSavePRatio_ = p_cf_kEnginesKernel_->getPerspectiveRatio();

        // Make the switch
        p_cf_kEnginesKernel_->preferViewAngle( 0.0, 0.0, FALSE );
        p_cf_kEnginesKernel_->preferPerspective( 0.0, TRUE );

        // Record a successful operation:
        cf_enumCurrentDisplayMode_ = CF_DISP_2D;
        bStatus = TRUE;
    }

    if ( enumNewDispMode == CF_DISP_3D )
    {   // The caller must want to go from 2D into 3D-Mode:
        if ( cf_enumCurrentDisplayMode_ == CF_DISP_2D ) bNeedToReConfig = TRUE;

        // Restore any saved view params as the current state:
        p_cf_kEnginesKernel_
                ->preferViewAngle( dSaveXAngle_, dSaveYAngle_, FALSE );
        p_cf_kEnginesKernel_->preferPerspective( dSavePRatio_, TRUE );

        // Record a successful operation:
        cf_enumCurrentDisplayMode_ = CF_DISP_3D;
        bStatus = TRUE;
    }

    // Do we need to force a geometry update to occur?
    if ( bNeedToReConfig == TRUE )
    {
        autoConfigure();
        applyGeomXform();
    }

    return( bStatus );

}   // end of cf_ChartEngine::disp2D3DMode( cf_DispMode enumNewDispMode )


void
cf_ChartEngine::dispBitmapChartWall
                ( BOOL bNewState )
{
    bDoWeDrawBitmapWall_ = bNewState;
}

BOOL
cf_ChartEngine::dispBitmapChartWall()
{
    return bDoWeDrawBitmapWall_;
}

BOOL
cf_ChartEngine::dispBitmapWallPaper()
{
    return bDoWeDrawWallPaper_;
}

void
cf_ChartEngine::dispBitmapWallPaper
                ( BOOL bNewState )
{
    bDoWeDrawWallPaper_ = bNewState;
}


//-------------------------------------------------
// Get/Set the main chart-engine color-attributes:
//-------------------------------------------------
bf_Color
cf_ChartEngine::dispColorBackground()
{
    return bf_clrCurrentBackground_;
}


void
cf_ChartEngine::dispColorBackground
            ( const bf_Color &cr_bf_clrNew )
{
    // Keep track of this latest state-change:
    bf_clrCurrentBackground_ = cr_bf_clrNew;

    // Proceed to update the Engine's Grid/Tick Mark Series:
    if ( p_cf_3dgtsXonBackFace_ != NULL )
        p_cf_3dgtsXonBackFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsXonBottomFace_ != NULL )
        p_cf_3dgtsXonBottomFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsYonLeftFace_ != NULL )
        p_cf_3dgtsYonLeftFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsYonBackFace_ != NULL )
        p_cf_3dgtsYonBackFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsZonLeftFace_ != NULL )
        p_cf_3dgtsZonLeftFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsZonBottomFace_ != NULL )
        p_cf_3dgtsZonBottomFace_->colorBack( cr_bf_clrNew );

    if ( p_cf_3dgtsPolarOnFloor_ != NULL )
        p_cf_3dgtsPolarOnFloor_->colorBack( cr_bf_clrNew );

    // Also update the Engine's Title-Marker-Manager:
    if ( p_cf_tmmTitlesAxes_ != NULL )
        p_cf_tmmTitlesAxes_->colorBack( cr_bf_clrNew );

    // Also update the Engine's "User-Legend" 3D-Text-Marker-Series:
    if ( p_cf_3dtmsUserLegends_ != NULL )
        p_cf_3dtmsUserLegends_->colorBack( cr_bf_clrNew );
}


bf_Color
cf_ChartEngine::dispColorFaceBack()
{
    return p_cf_datbBackFace_->colorBrush();
}


void
cf_ChartEngine::dispColorFaceBack
            ( const bf_Color &cr_bf_clrNew )
{
    p_cf_datbBackFace_->colorBrush( cr_bf_clrNew );
}


bf_Color
cf_ChartEngine::dispColorFaceBottom()
{
    return p_cf_datbBottomFace_->colorBrush();
}

void
cf_ChartEngine::dispColorFaceBottom
            ( const bf_Color &cr_bf_clrNew )
{
    p_cf_datbBottomFace_->colorBrush( cr_bf_clrNew );
}

bf_Color
cf_ChartEngine::dispColorFaceLeft()
{
    return p_cf_datbLeftFace_->colorBrush();
}

void
cf_ChartEngine::dispColorFaceLeft
            ( const bf_Color &cr_bf_clrNew )
{
    p_cf_datbLeftFace_->colorBrush( cr_bf_clrNew );
}


bf_Color
cf_ChartEngine::dispColorFaceLines()
{
    return p_cf_datbBackFace_->colorPen();
}

void
cf_ChartEngine::dispColorFaceLines( const bf_Color &cr_bf_clrNew )
{
    p_cf_datbBackFace_->colorPen( cr_bf_clrNew );
    p_cf_datbBottomFace_->colorPen( cr_bf_clrNew );
    p_cf_datbLeftFace_->colorPen( cr_bf_clrNew );
}




void
    cf_ChartEngine
        ::dispFaceBack( BOOL bNewState )
            { bDoWeDrawBackFace_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispFaceBack()
            { return bDoWeDrawBackFace_; }
void
    cf_ChartEngine
        ::dispFaceBottom( BOOL bNewState )
            { bDoWeDrawBottomFace_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispFaceBottom()
            { return bDoWeDrawBottomFace_; }
void
    cf_ChartEngine
        ::dispFaceLeft( BOOL bNewState )
            { bDoWeDrawLeftFace_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispFaceLeft()
            { return bDoWeDrawLeftFace_; }


void
    cf_ChartEngine
        ::dispForceBackBorder( BOOL bNewState )
            { bForceRenderOfBackBorder_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispForceBackBorder()
            { return bForceRenderOfBackBorder_; }

void
    cf_ChartEngine
        ::dispForceBackColor( BOOL bNewState )
            { bForceRenderOfBackColorRect_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispForceBackColor()
            { return bForceRenderOfBackColorRect_; }



void
cf_ChartEngine::dispVirtCentroid
                ( BOOL bNewState )
{
    cf_3dpEngineCentroid_.dispVisible( bNewState );
}


void
cf_ChartEngine::dispVirtObjMaxPoint
                ( BOOL bNewState )
{
    cf_3dpEngObjVirtMax_.dispVisible( bNewState );
}

void
cf_ChartEngine::dispVirtObjMinPoint
                ( BOOL bNewState )
{
    cf_3dpEngObjVirtMin_.dispVisible( bNewState );
}



void
    cf_ChartEngine
        ::dispWireFrameMode( BOOL   bNewState )
            { bInWireFrameMode_ = bNewState; }
BOOL
    cf_ChartEngine
        ::dispWireFrameMode()
            { return bInWireFrameMode_; }

void
    cf_ChartEngine
        ::dispZoomMode( BOOL bNewState )
            { p_cf_kEnginesKernel_->setZoomMode( bNewState ); }

BOOL
    cf_ChartEngine
        ::dispZoomMode()
            { return p_cf_kEnginesKernel_->setZoomMode(); }


void
    cf_ChartEngine
        ::draw( bf_Display *p_bf_dspOutputDisplay )
{
    //----------------------------------------------------------------
    // Note that the owner/parent chart-control/view MUST have
    // established a valid (and locked/mutexed) device-context...
    // ...and will be responsible for releasing any mutex/lock
    // of the display/device-context *after* this method is finished!
    //----------------------------------------------------------------

    //***********************************************
    // If this is the FIRST time we are rendering...
    //***********************************************
    if ( bHasEverRendered_ == FALSE )
    {   // ...let's make SURE our objects are synched with the display!
        this->applyDispMetrics();
        bHasEverRendered_ = TRUE;
    }

    //----------------------------------------------------------
    // If we are not in "wire-frame" mode" we will see if we
    // need to render any display "background" items, such as:
    //  (1) a bitmap for background "wall paper",
    //  (2) a FORCED display-background filled-rectangle or,
    //  (3) a FORCED display-background hollow-border polyline.
    //----------------------------------------------------------
    if ( bInWireFrameMode_ == FALSE )
    {
        // Measure the current display, because almost anything
        // we do here will want to know these dimensions...
        cf_2DRect  cf_2drDisplaySize;
        p_bf_dspOutputDisplay->dsp_getVisible( cf_2drDisplaySize );
                    // Our "background" rectangle is now defined...

        // Shall we render a "background" bitmap?
        if ( bDoWeDrawWallPaper_ == TRUE )
        {
            // If we've GOT one, then load it and render it
            if ( bitmapWallPaperFile() == TRUE )
            {
                int iWidth = cf_2drDisplaySize.width();
                int iHeight = cf_2drDisplaySize.height();

                p_bf_2dbmpWallPaper_
                    = new bf_2DBitmap( p_bf_dspOutputDisplay,
                                        cf_strWallPaperFile_ );
                // Show it!...
                p_bf_dspOutputDisplay
                    ->dsp_bitmap(   p_bf_2dbmpWallPaper_,
                                    cf_2DPoint( 0,0 ),// placement
                                    cf_2DRect(  0, 0, // Size to top/left  X/Y,
                                                iWidth,
                                                iHeight) );
                                                // Size to Bottom Right X/Y
                //...then Blow it!
                delete p_bf_2dbmpWallPaper_;
                p_bf_2dbmpWallPaper_ = NULL;
                        // reset to a measurable state...

            }  // End of screen for existance of bitmap file.
        }   // End of screen for bitmap wallpaper to be drawn.

        // Force the rendering of a background color-filled rectangle?
        else if ( bForceRenderOfBackColorRect_ == TRUE )
                // (only if we haven't already rendered a bitmap.)
        {
            //...our background rect is already defined... so render it!
//            p_bf_dspOutputDisplay->dsp_rectangle( cf_2drDisplaySize );
        }

        // Force the rendering of a background non-filled polyline?
        if ( bForceRenderOfBackBorder_ == TRUE )
        {
            // We need to tweak the bottom-right dimension of the
            // background rect to avoid clipping!
            int iRight = cf_2drDisplaySize.right() - 1;
            int iBottom = cf_2drDisplaySize.bottom() - 1;
            cf_2DRect  cf_2drRenderSize( 0, 0, iRight, iBottom );

//            p_bf_dspOutputDisplay->dsp_polyline( cf_2drRenderSize );
        }

    }   // End of screen for wire-frame mode.

    //===============================================================
    // Now let the rest of the engine components do their tricks !!!
    //===============================================================
    if ( cf_enumCurrentDisplayMode_ == CF_DISP_3D )
        draw3D( p_bf_dspOutputDisplay );
    else
        draw2D( p_bf_dspOutputDisplay );

    if ( cf_3dpEngineCentroid_.dispVisibleIs() == TRUE )
        cf_3dpEngineCentroid_.draw3D( p_bf_dspOutputDisplay );

    if ( cf_3dpEngObjVirtMin_.dispVisibleIs() == TRUE )
        cf_3dpEngObjVirtMin_.draw3D( p_bf_dspOutputDisplay );

    if ( cf_3dpEngObjVirtMax_.dispVisibleIs() == TRUE )
        cf_3dpEngObjVirtMax_.draw3D( p_bf_dspOutputDisplay );

    return;

}   // end of cf_ChartEngine::draw( bf_Display * )



void
    cf_ChartEngine
        ::draw2D( bf_Display *p_bf_dspOutputDisplay )
{
    //
    // If we're in Wire Frame Mode -- Do minimal draw ops,
    // and Bail Out ASAP!
    //
    if ( bInWireFrameMode_ == TRUE )
    {
        if ( bDoWeDrawLeftFace_ == TRUE )
            cf_3dpgLeftFace_.drawBorder( p_bf_dspOutputDisplay );
        if ( bDoWeDrawBackFace_ == TRUE )
            cf_3dpgBackFace_.drawBorder( p_bf_dspOutputDisplay );
        if ( bDoWeDrawBottomFace_ == TRUE )
            cf_3dpgBottomFace_.drawBorder( p_bf_dspOutputDisplay );
    }
    else
    {
        //
        // Draw The Chart's faces -- giving the user something
        // to look at whilst any derived classes do their rendering...
        //
        if ( bDoWeDrawLeftFace_ == TRUE )
            cf_3dpgLeftFace_.draw3D( p_bf_dspOutputDisplay );
        if ( bDoWeDrawBackFace_ == TRUE ) 
            cf_3dpgBackFace_.draw3D( p_bf_dspOutputDisplay );
        if ( bDoWeDrawBottomFace_ ==  TRUE )
            cf_3dpgBottomFace_.draw3D( p_bf_dspOutputDisplay );
    }

    return;

}   // end of cf_ChartEngine::draw2D(...);


void
    cf_ChartEngine
        ::draw3D( bf_Display *p_bf_dspOutputDisplay )
{
    //
    // Avoid duplicating code by using existing functionality...
    //
    cf_ChartEngine::draw2D( p_bf_dspOutputDisplay );

    //
    // If we're in 3D Mode, we might also want
    // to draw the ChartBitmapWall wall:
    //
    if ( bDoWeDrawBitmapWall_ == TRUE )
    {
        if ( p_cf_3dbmpProjectedBitmap_->isFileSet() == TRUE )
        {
            p_cf_3dbmpProjectedBitmap_
                                ->draw3D( p_bf_dspOutputDisplay );
            cf_3dpgLeftFace_.drawBorder( p_bf_dspOutputDisplay );
        }
    }
    return;

}   // end of cf_ChartEngine::draw3D(...);



//----------------------------------------------------
// Protected virtual member function called by c-tor.
// Inits chart "3D Box" Geometry objects/member vars:
//----------------------------------------------------
void
cf_ChartEngine::init3DBoxETC()
{
    double      a_d4Xs[ 4 ];
    double      a_d4Ys[ 4 ];
    double      a_d4Zs[ 4 ];
    cf_3DPoint  *p_cf_3dpNew = NULL;
                // Used to generate the faces of the 3D Box,

    // Set up the LEFT Face:
    p_cf_kEnginesKernel_->copyLeftFaceCoords( a_d4Xs, a_d4Ys, a_d4Zs );
    for ( i = 0; i < 4; i++ )
    {
        p_cf_3dpNew = new cf_3DPoint( a_d4Xs[i], a_d4Ys[i], a_d4Zs[i] );
        cf_3dpgLeftFace_.addPoint( p_cf_3dpNew );
    }
    cf_3dpgLeftFace_.applyGeomXform( p_cf_kEnginesKernel_ );
                            // Projection xform will wait until AFTER
                            // we know projection state and params...

    // Set up the BACK Face:
    p_cf_kEnginesKernel_->copyBackFaceCoords( a_d4Xs, a_d4Ys, a_d4Zs );
    for ( i = 0; i < 4; i++ )
    {
        p_cf_3dpNew = new cf_3DPoint( a_d4Xs[i], a_d4Ys[i], a_d4Zs[i] );
        cf_3dpgBackFace_.addPoint( p_cf_3dpNew );
    }
    cf_3dpgBackFace_.applyGeomXform( p_cf_kEnginesKernel_ );
                            // As above, Proj/xform waits for later...

    // Set up the BOTTOM Face:
    p_cf_kEnginesKernel_->copyBottomFaceCoords( a_d4Xs, a_d4Ys, a_d4Zs );
    for ( i = 0; i < 4; i++ )
    {
        p_cf_3dpNew = new cf_3DPoint( a_d4Xs[i], a_d4Ys[i], a_d4Zs[i] );
        cf_3dpgBottomFace_.addPoint( p_cf_3dpNew );
    }
    cf_3dpgBottomFace_.applyGeomXform( p_cf_kEnginesKernel_ );
                            // As above, Proj/xform waits for later...

    // Now init members vars which hold the kernel's 3D Box XYZ
    // Min Max Coords -- for later use in re-proportion ops, etc:
    d3DBoxXDim_ = p_cf_kEnginesKernel_->getXDim();
    d3DBoxYDim_ = p_cf_kEnginesKernel_->getYDim();
    d3DBoxZDim_ = p_cf_kEnginesKernel_->getZDim();

    // Save current view params for subsequent recall:
    dSaveXAngle_ = p_cf_kEnginesKernel_->getXAngle();
    dSaveYAngle_ = p_cf_kEnginesKernel_->getYAngle();
    dSaveZAngle_ = p_cf_kEnginesKernel_->getZAngle();
    dSavePRatio_ = p_cf_kEnginesKernel_->getPerspectiveRatio();

    return; // back to the c-tor...

}   // end of cf_ChartEngine::init3DBoxETC()


void
cf_ChartEngine::initDrawingAttribs()
// Protected virtual member function called by c-tor
{
    cf_3dpEngineCentroid_.dispVisible( FALSE );

    cf_3dpEngObjVirtMin_.dispVisible( FALSE );
    cf_3dpEngObjVirtMax_.dispVisible( FALSE );

    // Init overall Draw-Mode attribs:
    bInWireFrameMode_              = FALSE;

    bForceRenderOfBackColorRect_   = FALSE;
    bForceRenderOfBackBorder_      = FALSE;

    bDoWeDrawLeftFace_             = TRUE;
    bDoWeDrawBackFace_             = TRUE;
    bDoWeDrawBottomFace_           = TRUE;

    // Allocate and Init the 3D Box Face Display Attribs:
    p_cf_datbBackFace_ = new cf_DispAttribs();
    p_cf_datbBackFace_->stylePenWidth( 1 );
    p_cf_datbBackFace_->colorBrush( CF_LITE_GRAY );
    cf_3dpgBackFace_.dispAttribsPointTo( p_cf_datbBackFace_ );

    p_cf_datbBottomFace_ = new cf_DispAttribs();
    p_cf_datbBottomFace_->stylePenWidth( 1 );
    p_cf_datbBottomFace_->colorBrush( CF_DARK_GRAY );
    cf_3dpgBottomFace_.dispAttribsPointTo( p_cf_datbBottomFace_ );

    p_cf_datbLeftFace_ = new cf_DispAttribs();
    p_cf_datbLeftFace_->stylePenWidth( 1 );
    p_cf_datbLeftFace_->colorBrush( CF_LITE_GRAY );
    cf_3dpgLeftFace_.dispAttribsPointTo( p_cf_datbLeftFace_ );

    // Default is NOT to display background bitmaps:
    cf_strWallPaperFile_ = "";
    bDoWeDrawWallPaper_ = FALSE;
    p_bf_2dbmpWallPaper_ = NULL;  // a measurable state!...

    // Default is NOT to display chart 3D bitmap/wall(s):
    bDoWeDrawBitmapWall_ = FALSE;
    // But we will NEW the pointer -- for now...
    p_cf_3dbmpProjectedBitmap_ = new cf_3DBitmap( p_cf_kEnginesKernel_ );
                            // NB: The 3D Bitmap Wall source bitmap file name
                            // is kept INSIDE the cf_3dbmpProjectedBitmap object.
    return;

}   // end of cf_ChartEngine::initDrawingAttribs()



void
cf_ChartEngine::initGridTickSeries()
// Protected virtual member function called by c-tor to init
// the chart-engine's collection of default grid/tick mark series.
{
    p_cf_3dgtsXonBackFace_ = new cf_3DGTS_AutoMarkBackFaceX( this );
    addCompoObject( p_cf_3dgtsXonBackFace_ );
    p_cf_3dgtsXonBackFace_->off();

    p_cf_3dgtsXonBottomFace_ = new cf_3DGTS_AutoMarkBottomFaceX( this );
    addCompoObject( p_cf_3dgtsXonBottomFace_ );
    p_cf_3dgtsXonBottomFace_->off();

    p_cf_3dgtsYonLeftFace_ = new cf_3DGTS_AutoMarkLeftFaceY( this );
    addCompoObject( p_cf_3dgtsYonLeftFace_ );
    p_cf_3dgtsYonLeftFace_->off();

    p_cf_3dgtsYonBackFace_ = new cf_3DGTS_AutoMarkBackFaceY( this );
    addCompoObject( p_cf_3dgtsYonBackFace_ );
    p_cf_3dgtsYonBackFace_->off();

    p_cf_3dgtsZonLeftFace_ = new cf_3DGTS_AutoMarkLeftFaceZ( this );
    addCompoObject( p_cf_3dgtsZonLeftFace_ );
    p_cf_3dgtsZonLeftFace_->off();

    p_cf_3dgtsZonBottomFace_ = new cf_3DGTS_AutoMarkBottomFaceZ( this );
    addCompoObject( p_cf_3dgtsZonBottomFace_ );
    p_cf_3dgtsZonBottomFace_->off();

    p_cf_3dgtsPolarOnFloor_ = new cf_3DGTS_AutoMarkPolarFloor( this );
    addCompoObject( p_cf_3dgtsPolarOnFloor_ );
    p_cf_3dgtsPolarOnFloor_->off();


    // Measure the Data-Range extending on the Y-Axis...
    switch ( cf_enumChartEngineType_  ) // ...based on the Chart-Type:
    {
        case    CF_TYPE_BAR:
                // X-Axes are ranges:
                p_cf_3dgtsXonBackFace_->useDataRange( TRUE );
                p_cf_3dgtsXonBackFace_->useRangeValues( TRUE );

                p_cf_3dgtsXonBottomFace_->useDataRange( TRUE );
                p_cf_3dgtsXonBottomFace_->useRangeValues( TRUE );

                // Y-Axes are domains:
                p_cf_3dgtsYonLeftFace_->useDomainItems( TRUE );
                p_cf_3dgtsYonBackFace_->useDomainItems( TRUE );

                // Z-Axes and Polar-Axes are data-groups:
                p_cf_3dgtsZonLeftFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsZonBottomFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsPolarOnFloor_->useNoUpdateSource();
                break;

        case    CF_TYPE_STACK:
                // X-Axes are domains:
                p_cf_3dgtsXonBackFace_->useDomainItems( TRUE );
                p_cf_3dgtsXonBottomFace_->useDomainItems( TRUE );

                // Y-Axes are ranges *sums*:
                p_cf_3dgtsYonLeftFace_->useDataRangeSums( TRUE );
                p_cf_3dgtsYonLeftFace_->useRangeValSums( TRUE );

                p_cf_3dgtsYonBackFace_->useDataRangeSums( TRUE );
                p_cf_3dgtsYonBackFace_->useRangeValSums( TRUE );

                // Z-Axes and Polar-Axes are data-groups:
                p_cf_3dgtsZonLeftFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsZonBottomFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsPolarOnFloor_->useNoUpdateSource();
                break;

        case    CF_TYPE_COLUMN:
        case    CF_TYPE_PSER:
        case    CF_TYPE_LINE:
        case    CF_TYPE_AREA:
                // X-Axes are domains:
                p_cf_3dgtsXonBackFace_->useDomainItems( TRUE );
                p_cf_3dgtsXonBottomFace_->useDomainItems( TRUE );

                // Y-Axes are ranges:
                p_cf_3dgtsYonLeftFace_->useDataRange( TRUE );
                p_cf_3dgtsYonLeftFace_->useRangeValues( TRUE );

                p_cf_3dgtsYonBackFace_->useDataRange( TRUE );
                p_cf_3dgtsYonBackFace_->useRangeValues( TRUE );

                // Z-Axes and Polar-Axes are data-groups:
                p_cf_3dgtsZonLeftFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsZonBottomFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsPolarOnFloor_->useNoUpdateSource();
                break;

        case    CF_TYPE_PIE:
        case    CF_TYPE_XPIE:
                // Polar-Axes are data-groups:
                p_cf_3dgtsPolarOnFloor_->useDataGroupsList( TRUE );

                // Everything else is off:
                p_cf_3dgtsXonBackFace_->useNoUpdateSource();
                p_cf_3dgtsXonBottomFace_->useNoUpdateSource();

                p_cf_3dgtsYonLeftFace_->useNoUpdateSource();
                p_cf_3dgtsYonLeftFace_->useNoUpdateSource();
                p_cf_3dgtsYonBackFace_->useNoUpdateSource();
                p_cf_3dgtsYonBackFace_->useNoUpdateSource();

                p_cf_3dgtsZonLeftFace_->useNoUpdateSource();
                p_cf_3dgtsZonBottomFace_->useNoUpdateSource();
                break;

        default:
                // X-Axes are domains:
                p_cf_3dgtsXonBackFace_->useDomainItems( TRUE );
                p_cf_3dgtsXonBottomFace_->useDomainItems( TRUE );

                // Y-Axes are ranges:
                p_cf_3dgtsYonLeftFace_->useDataRange( TRUE );
                p_cf_3dgtsYonLeftFace_->useRangeValues( TRUE );

                p_cf_3dgtsYonBackFace_->useDataRange( TRUE );
                p_cf_3dgtsYonBackFace_->useRangeValues( TRUE );

                // Z-Axes and Polar-Axes are data-groups:
                p_cf_3dgtsZonLeftFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsZonBottomFace_->useDataGroupsList( TRUE );
                p_cf_3dgtsPolarOnFloor_->useNoUpdateSource();
                break;
    }
}



void
cf_ChartEngine::initObjectDynamicStorage()
// Protected virtual member function called by c-tor to init
// the engine's dynamic arrays of pointers to chart-objects.
{
    iNumCompoObjects_ = 0;
    da_p_cf_objCompoObjects_.growBy( 10 );

    iNumFaceObjects_ = 0;
    da_p_cf_objFaceObjects_.growBy( 100 );

    iNumSpaceObjects_ = 0;
    da_p_cf_objSpaceObjects_.growBy( 100 );

    iNumMarkerObjects_ = 0;
    da_p_cf_objMarkerObjects_.growBy( 100 );
}


void
cf_ChartEngine::initTextMarkers()
{
    p_cf_tmmTitlesAxes_  = NULL;
    p_cf_3dtmsUserLegends_  = NULL;
}


cf_TitleMarkerMgr*
    cf_ChartEngine
        ::markerMgrTitlesAxes()
{
    if ( p_cf_tmmTitlesAxes_ == NULL )
    {   // Which it WILL the first time an access op is run!...
        p_cf_tmmTitlesAxes_ = new cf_TitleMarkerMgr( this );
        addMarkerObject( p_cf_tmmTitlesAxes_ );
    }
    return p_cf_tmmTitlesAxes_;
}           // which SHOULD be A-OK to go with!...


cf_3DTextMarkerSeries*
    cf_ChartEngine
        ::markerMgrUserLegends()
{
    if ( p_cf_3dtmsUserLegends_ == NULL )
    {   // Which it WILL the first time an access op is run!...
        p_cf_3dtmsUserLegends_ = new cf_3DTextMarkerSeries( this );
        addMarkerObject( p_cf_3dtmsUserLegends_ );
    }
    return p_cf_3dtmsUserLegends_;
}           // which SHOULD be A-OK to go with!...



void
cf_ChartEngine::measureDataMinMax
                ( cf_ChartModel *p_cf_cmodlDataSource )
{
    // Measure the Data-Range extending on the X-Axis...
    switch ( cf_enumChartEngineType_  ) // ...based on the Chart-Type:
    {
        case    CF_TYPE_BAR:
                dChartXRangeMin_ = p_cf_cmodlDataSource->dataYValuesMin();
                dChartXRangeMax_ = p_cf_cmodlDataSource->dataYValuesMax();
                break;

        case    CF_TYPE_COLUMN:
        case    CF_TYPE_STACK:
        case    CF_TYPE_PSER:
        case    CF_TYPE_LINE:
        case    CF_TYPE_AREA:
        case    CF_TYPE_PIE:
        case    CF_TYPE_XPIE:
        default:
                dChartXRangeMin_ = p_cf_cmodlDataSource->dataXItemsMin();
                dChartXRangeMax_ = p_cf_cmodlDataSource->dataXItemsMax();
                if ( p_cf_cmodlDataSource->dataIndexMethod() == CF_INDEX_INT_I )
                    if ( chartTypeBarColStackIs() == TRUE )
                    {
                        dChartXRangeMin_ = 0.0;
                        dChartXRangeMax_ += 1.0;
                    }
                break;
    }

/*
    if ( p_cf_cmodlDataSource->dataIndexMethod() == CF_INDEX_USR_X )
        if ( chartTypeBarColStackIs() == TRUE )
            dChartXRangeMin_ = 0.0;
 */



    // Measure the Data-Range extending on the Y-Axis...
    switch ( cf_enumChartEngineType_  ) // ...based on the Chart-Type:
    {
        case    CF_TYPE_BAR:
                dChartYRangeMin_ = p_cf_cmodlDataSource->dataXItemsMin();
                dChartYRangeMax_ = p_cf_cmodlDataSource->dataXItemsMax();
                if ( p_cf_cmodlDataSource->dataIndexMethod() == CF_INDEX_INT_I )
                {
                    dChartYRangeMin_ = 0.0;
                    dChartYRangeMax_ += 1.0;
                }
                break;

        case    CF_TYPE_STACK:
                dChartYRangeMin_ = p_cf_cmodlDataSource->dataYValSumsMin();
                dChartYRangeMax_ = p_cf_cmodlDataSource->dataYValSumsMax();
                break;

        case    CF_TYPE_COLUMN:
        case    CF_TYPE_PSER:
        case    CF_TYPE_LINE:
        case    CF_TYPE_AREA:
        case    CF_TYPE_PIE:
        case    CF_TYPE_XPIE:
        default:
                dChartYRangeMin_ = p_cf_cmodlDataSource->dataYValuesMin();
                dChartYRangeMax_ = p_cf_cmodlDataSource->dataYValuesMax();
                break;
    }

    // Measure the Z-Axis Data-Range...
    dChartZRangeMin_ = p_cf_cmodlDataSource->dataZGroupsMin();
    dChartZRangeMax_ = p_cf_cmodlDataSource->dataZGroupsMax();
    if (    ( cf_enumChartEngineType_ == CF_TYPE_BAR )
        ||  ( cf_enumChartEngineType_ == CF_TYPE_COLUMN )
        ||  ( cf_enumChartEngineType_ == CF_TYPE_STACK )
        ||  ( cf_enumChartEngineType_ == CF_TYPE_RIBBON ) )
        dChartZRangeMax_ = ( dChartZRangeMax_ * 2.0 ) - 1.0;
        // ...and compensate for certain chart-types:
}


double
cf_ChartEngine::measuredDataXRangeMin()
                { return dChartXRangeMin_; }

double
cf_ChartEngine::measuredDataXRangeMax()
                { return dChartXRangeMax_; }

double
cf_ChartEngine::measuredDataYRangeMin()
                { return dChartYRangeMin_; }

double
cf_ChartEngine::measuredDataYRangeMax()
                { return dChartYRangeMax_; }

double
cf_ChartEngine::measuredDataZRangeMin()
                { return dChartZRangeMin_; }

double
cf_ChartEngine::measuredDataZRangeMax()
                { return dChartZRangeMax_; }




void
cf_ChartEngine::measureVirtMinMax()
{
    dVirtXRangeMin_ = p_cf_kEnginesKernel_->getXMin();
    dVirtYRangeMin_ = p_cf_kEnginesKernel_->getYMin();
    dVirtZRangeMin_ = p_cf_kEnginesKernel_->getZMin();

    dVirtXRangeMax_ = p_cf_kEnginesKernel_->getXMax();
    dVirtYRangeMax_ = p_cf_kEnginesKernel_->getYMax();
    dVirtZRangeMax_ = p_cf_kEnginesKernel_->getZMax();

}


void
cf_ChartEngine::measureVirtOrigin()
{
    // This BASE method sets a default engine-origin
    // as the MIN X/Y/Z of the kernel's virtual 3D Box:
//    p_cf_kEnginesKernel_->copy3DBoxOrigin(  &dChartXOrigin_,
//                                            &dChartYOrigin_,
//                                            &dChartZOrigin_ );

    // BEFORE we configure new Chart Origins, we NEED to calculate
    // a translation/correction for current Chart Objects to line
    // up with the new origin:
    double  dOldXOrigin = dChartXOrigin_;
    double  dOldYOrigin = dChartYOrigin_;
    double  dOldZOrigin = dChartZOrigin_;

    // Update our relative chart origin w.r.t. the kernel's
    // absolute origin & dimensions:
    dChartXOrigin_ = p_cf_kEnginesKernel_->getXMin();
    dChartYOrigin_ = p_cf_kEnginesKernel_->getYMin();
    dChartZOrigin_ = p_cf_kEnginesKernel_->getZMin();

    // See if it's neccessary to make any adjustments:
    BOOL    bIsTransNeccessary = FALSE; // For now...

    double  dXlateAlongX = dChartXOrigin_ - dOldXOrigin;
    double  dXlateAlongY = dChartYOrigin_ - dOldYOrigin;
    double  dXlateAlongZ = dChartZOrigin_ - dOldZOrigin;

    if ( dXlateAlongX != 0.0 
                        || dXlateAlongY != 0.0 
                                        || dXlateAlongZ != 0.0 )
            bIsTransNeccessary = TRUE;

    if ( bIsTransNeccessary == TRUE )
    {   // Apply neccessary translation corrections to chart objects:
        for ( i = 1; i <= iNumCompoObjects_; i++ )
            ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                        ->applyTranslateOp( dXlateAlongX,
                                            dXlateAlongY,
                                            dXlateAlongZ );
        for ( i = 1; i <= iNumFaceObjects_; i++ )
            ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                        ->applyTranslateOp( dXlateAlongX,
                                            dXlateAlongY,
                                            dXlateAlongZ );
        for ( i = 1; i <= iNumSpaceObjects_; i++ )
            ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                        ->applyTranslateOp( dXlateAlongX,
                                            dXlateAlongY,
                                            dXlateAlongZ );
    }


}


double
cf_ChartEngine::measuredVirtXOrigin()
                { return dChartXOrigin_; }
double
cf_ChartEngine::measuredVirtYOrigin()
                { return dChartYOrigin_; }
double
cf_ChartEngine::measuredVirtZOrigin()
                { return dChartZOrigin_; }


double
cf_ChartEngine::measuredVirtXRangeMin()
                { return dVirtXRangeMin_; }

double
cf_ChartEngine::measuredVirtXRangeMax()
                { return dVirtXRangeMax_; }

double
cf_ChartEngine::measuredVirtYRangeMin()
                { return dVirtYRangeMin_; }

double
cf_ChartEngine::measuredVirtYRangeMax()
                { return dVirtYRangeMax_; }

double
cf_ChartEngine::measuredVirtZRangeMin()
                { return dVirtZRangeMin_; }

double
cf_ChartEngine::measuredVirtZRangeMax()
                { return dVirtZRangeMax_; }





//----------------------------------------------------------------
// Allow user classes to over-ride any default positioning of
// a 3D Point and specify a new position as an X/Y/Z percentage 
// away from the CENTER of the 3D Box of the chart.  Positive
// OR negative percentages are allowed.
//----------------------------------------------------------------
BOOL
    cf_ChartEngine
        ::positPointAt( cf_3DPoint  *p_cf_3dpTargetPoint,
                        double      dPctWidthFromCtr,
                        double      dPctHeightFromCtr,
                        double      dPctDepthFromCtr,
                        BOOL        bCorrectOriginOffset )
{
    BOOL bStatus = FALSE;   // Init to ERROR state to be safe...

    if ( p_cf_3dpTargetPoint != NULL )
    {
        // Measure the Current Chart's 3D Box Height and Width:
        double  dChartXMax = p_cf_kEnginesKernel_->getXMax();
        double  dChartYMax = p_cf_kEnginesKernel_->getYMax();
        double  dChartZMax = p_cf_kEnginesKernel_->getZMax();

        // Calc XY placement values from the user's percent param:
        double dNewX = dChartXMax * dPctWidthFromCtr / 100.0;
        double dNewY = dChartYMax * dPctHeightFromCtr / 100.0;
        double dNewZ = dChartZMax * dPctDepthFromCtr / 100.0;
        
        // Re-position and transform the point:
        p_cf_3dpTargetPoint->dataXYZ( dNewX, dNewY, dNewZ );

        if ( bCorrectOriginOffset == TRUE )
            p_cf_3dpTargetPoint
                ->applyTranslateOp( - dChartXOrigin_,
                                    - dChartYOrigin_,
                                    - dChartZOrigin_ );
                                    // point now re-positioned...

        p_cf_3dpTargetPoint->applyGeomXform( p_cf_kEnginesKernel_ );
        p_cf_3dpTargetPoint->applyProjXform( p_cf_kEnginesKernel_ );
                                    // point now transformed!

        bStatus = TRUE;
    }
    return bStatus;

}   // end of cf_ChartEngine::positPointAt( point, 3 XYZ params )




void
cf_ChartEngine::preferMarginTop
            ( int iMarginInTwips )
{
    p_cf_kEnginesKernel_->preferMarginTop( iMarginInTwips, TRUE );
    applyProjXform();
}

void
cf_ChartEngine::preferMarginBottom
            ( int iMarginInTwips )
{
    p_cf_kEnginesKernel_->preferMarginBottom( iMarginInTwips, TRUE );
    applyProjXform();
}

void
cf_ChartEngine::preferMarginLeft
            ( int iMarginInTwips )
{
    p_cf_kEnginesKernel_->preferMarginLeft( iMarginInTwips, TRUE );
    applyProjXform();
}

void
    cf_ChartEngine
        ::preferMarginRight( int iMarginInTwips )
{
    p_cf_kEnginesKernel_->preferMarginRight( iMarginInTwips, TRUE );
    applyProjXform();
}


void
cf_ChartEngine::preferMargins
            (   int     iTopMarginInTwips,
                int     iBottomMarginInTwips,
                int     iLeftMarginInTwips,
                int     iRightMarginInTwips )
{
    p_cf_kEnginesKernel_->preferMarginTop( iTopMarginInTwips, FALSE );
    p_cf_kEnginesKernel_->preferMarginBottom( iBottomMarginInTwips, FALSE );
    p_cf_kEnginesKernel_->preferMarginLeft( iLeftMarginInTwips, FALSE );
    p_cf_kEnginesKernel_->preferMarginRight( iRightMarginInTwips, TRUE );
    applyProjXform();
}


void
cf_ChartEngine::preferPerspective
                (   double  dPercentPerspective,
                    BOOL    bReCalcKernelGeom,
                    BOOL    bApplyGeomXform     )
{
    p_cf_kEnginesKernel_->preferPerspective(    dPercentPerspective,
                                                bReCalcKernelGeom  );
    if ( bApplyGeomXform ) applyGeomXform();
}

void
    cf_ChartEngine
        ::preferPerspectiveMax()
{
    p_cf_kEnginesKernel_->preferPerspective( CF_MAX_P_RATIO_PERCENT/100.0, TRUE );
    applyGeomXform();
}

void
    cf_ChartEngine
        ::preferPerspectiveNormal()
{
    p_cf_kEnginesKernel_->preferPerspective( CF_NORMAL_P_RATIO_PERCENT/100.0, TRUE );
    applyGeomXform();
}

void
    cf_ChartEngine
        ::preferPerspectiveZero()
{
    p_cf_kEnginesKernel_->preferPerspective( CF_ZERO_P_RATIO_PERCENT/100.0, TRUE );
    applyGeomXform();
}


void
    cf_ChartEngine
        ::preferProportions(    double  dPrefHWPercentRatio,
                                double  dPrefDHPercentRatio,
                                BOOL    bPerformAutoConfigure )
//--------------------------------------------------------------------
// Set PREFERRED (i.e. you might NOT get it IF it's gonna make
// a problem!) 3D Box Proportions -- HW is Height/Width Ratio,
// and DH is Depth/Height Ratio:
//--------------------------------------------------------------------
{
    dPrefChartHWRatio_ = dPrefHWPercentRatio;
    dPrefChartDHRatio_ = dPrefDHPercentRatio;

    // Re-set the preferred Y and Z dimensions of the kernel
    // from the proportion ratios applied to the X dimension.
    double  dPrefChartXDim  = p_cf_kEnginesKernel_->getXMax() * 2.0;
                            // ...from simple 3D Box symmetry.

    double  dPrefChartYDim  = dPrefChartXDim * dPrefChartHWRatio_;
                            // I.e. via the Height-to-Width Ratio.

    double  dPrefChartZDim  = dPrefChartYDim * dPrefChartDHRatio_;
                            // I.e. via the Depth-to-Height Ratio.

    p_cf_kEnginesKernel_->preferDimensions( dPrefChartXDim,
                                            dPrefChartYDim,
                                            dPrefChartZDim,
                                            TRUE  ); // re-calc geom-matrix!

    if ( bPerformAutoConfigure == TRUE ) autoConfigure();
}


void
cf_ChartEngine::preferViewAngle
                (   double  dSetXAngle,
                    double  dSetYAngle,
                    BOOL    bReCalcKernelGeom,
                    BOOL    bApplyGeomXform  )
{
    p_cf_kEnginesKernel_->preferViewAngle(  dSetXAngle,
                                            dSetYAngle,
                                            bReCalcKernelGeom );

    dSaveXAngle_ = p_cf_kEnginesKernel_->getXAngle();
    dSaveYAngle_ = p_cf_kEnginesKernel_->getYAngle();

    if ( bApplyGeomXform ) applyGeomXform();
}


void
cf_ChartEngine::resetEngineObjects()
{
    // Get rid of any FACE objects we generated!
    if ( iNumFaceObjects_ >= 1 )
    {
        for ( i = 1; i <= iNumFaceObjects_; i++ )
            if ( da_p_cf_objFaceObjects_[ i ] != NULL )
                delete ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] );
        iNumFaceObjects_ = 0;
    }

    // Get rid of any SPACE objects we generated!
    if ( iNumSpaceObjects_ >= 1 )
    {
        for ( i = 1; i <= iNumSpaceObjects_; i++ )
            if ( da_p_cf_objSpaceObjects_[ i ] != NULL )
                delete ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] );
        iNumSpaceObjects_ = 0;
    }

    return;

}   // end of cf_ChartEngine::resetEngineObjects()



void
cf_ChartEngine::updateAxisGridTickSeries
                ( cf_ChartModel *p_cf_cmodlDataSource )
{
    // Are we going to make POLAR or CARTESIAN grid/ticks?
    if ( chartTypePolarIs() == TRUE )
    {
        if ( p_cf_3dgtsPolarOnFloor_->isActive() == TRUE )
            p_cf_3dgtsPolarOnFloor_->applyDataUpdate( p_cf_cmodlDataSource );
            // Polar-Coord circular-axis now updated...
    }
    else    // We are a Cartesian chart!
    {
        // Update the Bottom and left-face X-axes...
        if ( p_cf_3dgtsXonBottomFace_->isActive() == TRUE )
            p_cf_3dgtsXonBottomFace_->applyDataUpdate( p_cf_cmodlDataSource );

        if ( p_cf_3dgtsXonBackFace_->isActive() == TRUE )
            p_cf_3dgtsXonBackFace_->applyDataUpdate( p_cf_cmodlDataSource );

        // Update the Left and back-face Y-axes...
        if ( p_cf_3dgtsYonLeftFace_->isActive() == TRUE )
            p_cf_3dgtsYonLeftFace_->applyDataUpdate( p_cf_cmodlDataSource );

        if ( p_cf_3dgtsYonBackFace_->isActive() == TRUE )
            p_cf_3dgtsYonBackFace_->applyDataUpdate( p_cf_cmodlDataSource );

        // Consider whether or not we are in 2D Mode BEFORE letting
        // the Z-Axis Grid/Tick Mark Series mark the Z-Axis Ranges:
        if ( cf_enumCurrentDisplayMode_ == CF_DISP_3D )
        {
            if ( p_cf_3dgtsZonLeftFace_->isActive() == TRUE )
                p_cf_3dgtsZonLeftFace_->applyDataUpdate( p_cf_cmodlDataSource );

            if ( p_cf_3dgtsZonBottomFace_->isActive() == TRUE )
                p_cf_3dgtsZonBottomFace_->applyDataUpdate( p_cf_cmodlDataSource );
                // Left and bottom-face Z-axes now updated...
        }

    }   // End of cartesian grid/tick-update

}   // end of cf_ChatEngine::updateAxisGridTickSeries()




void
cf_ChartEngine::updateAxisRangeBufferPoints
                ( cf_ChartModel *  /* p_cf_cmodlDataSource */ )
{
    double      dXAxisMin = dChartXRangeMin_;
    double      dXAxisMax = dChartXRangeMax_;
    double      dYAxisMin = dChartYRangeMin_;
    double      dYAxisMax = dChartYRangeMax_;
    cf_3DPoint  *p_cf_3dpAxisMinMax;

    p_cf_3dpAxisMinMax = new cf_3DPoint( dXAxisMin, dYAxisMin, 0.0 );
    p_cf_3dpAxisMinMax->dispVisible( FALSE );
    addFaceObject( p_cf_3dpAxisMinMax );

    p_cf_3dpAxisMinMax = new cf_3DPoint( dXAxisMax, dYAxisMax, 0.0 );
    p_cf_3dpAxisMinMax->dispVisible( FALSE );
    addFaceObject( p_cf_3dpAxisMinMax );
}

void
cf_ChartEngine::updateYValsOnNatNs
                ( cf_ChartModel *p_cf_cmodlDataSource )
{}

void
cf_ChartEngine::updateYValsAtIntIs
                ( cf_ChartModel *p_cf_cmodlDataSource )
{}


//
// End of class cf_ChartEngine Implementation!
//


//////////////////////////////////////
// END of File: C1_ENGIN.CPP
//////////////////////////////////////





