//////////////////////////////////////
// START of File: C1_CMODL.CPP
//////////////////////////////////////

//-------------------------------------------------------------------
// COPYRIGHT NOTICE:  This code is protected by Copyright!
//
// Copyright (c) 1994 through 1997 by DPC Technology Corporation.
// All rights reserved.
//
// See files "cf_copyr.txt" and "cf_licen.txt" for details of
// copyright and license specifications, and contact information.
//-------------------------------------------------------------------

#include	<cf_api/bf_defs.h>
#include    <cf_api/c1_cmodl.hpp>

#include    <math.h>
            // for floor() and ceil() calls below...

#define     C1_CMODL_DGROU_GROWBY       16
            // Dynamic Array of Data Groups "Grow-By" val


//============================================================
// Implement class cf_ChartModel.  Note that complete
// implementation is version-dependent and is completed in
// ONE of two source files: "c2_cmodl.cpp" or "c3_cmodl.cpp"
//============================================================
cf_ChartModel::cf_ChartModel
            (   cf_ChartView    *p_bf_cviewOwner,
                cf_IndexMode    enumIndexMode )

    :   p_cf_dgGroupsList_( NULL ),
        p_cf_dgDomainItems_( NULL ),
        p_cf_dgRangeValues_( NULL ),

        p_cf_dgLabelTitleMain_( NULL ),
        p_cf_dgLabelTitleSub_( NULL ),

        p_cf_dgLabelAxisXBottom_( NULL ),
        p_cf_dgLabelAxisXBack_( NULL ),
        p_cf_dgLabelAxisYLeft_( NULL ),
        p_cf_dgLabelAxisYBack_( NULL ),
        p_cf_dgLabelAxisZBottom_( NULL ),
        p_cf_dgLabelAxisZBack_( NULL ),
        p_cf_dgLabelAxisPolarFloor_( NULL ),

        p_cf_dgAxisXBottom_( NULL ),
        p_cf_dgAxisXBack_( NULL ),
        p_cf_dgAxisYLeft_( NULL ),
        p_cf_dgAxisYBack_( NULL ),
        p_cf_dgAxisZBottom_( NULL ),
        p_cf_dgAxisZBack_( NULL ),
        p_cf_dgAxisPolarFloor_( NULL ),

        p_cf_engChartEngine_( NULL )
{
    //--------------------------------------------------------------
    // Keep track of who created us and what our index-mode is:
    //--------------------------------------------------------------
    p_bf_cviewParentView_       = p_bf_cviewOwner;
    cf_enumCurrentIndexMode_    = enumIndexMode;

    //--------------------------------------------------------------
    // Load the chart palettes BEFORE any activateGroupUserData( ) calls!
    // to init our set of default attributes (for rendering, etc)
    //--------------------------------------------------------------
    paletteColors(  CF_PALETTE_01, CF_PALETTE_02, CF_PALETTE_03,
                    CF_PALETTE_04, CF_PALETTE_05, CF_PALETTE_06,
                    CF_PALETTE_07, CF_PALETTE_08, CF_PALETTE_09,
                    CF_PALETTE_10, CF_PALETTE_11, CF_PALETTE_12,
                    CF_PALETTE_13, CF_PALETTE_14, CF_PALETTE_15,
                    CF_PALETTE_16 );  // from bf_Color's header file.

    paletteMarkers2D3D( FALSE,      // No 2D Markers
                        FALSE,      // No 3D Markers
                        FALSE );    // And DON'T re-apply!
    palettePenStyles( CF_PS_SOLID, CF_PS_DASH, CF_PS_DOT );
    palettePenWidth( 1 );
    paletteBrushHatches( CF_BHAT_NONE, CF_BHAT_VERT,  CF_BHAT_HORZ );

    //--------------------------------------------------------------
    // Since the Chart Framework is Data-Driven, and "Data-Model
    // Centric", we MUST init the Chart's user data value storage
    // (that engines, grid/tick/series, etc. will be referencing)!
    //--------------------------------------------------------------
    da_p_cf_dgDataGroups_.growBy( C1_CMODL_DGROU_GROWBY );
    iNumAllocatedGroups_ = 0;
    iMaxGroupIndex_ = 0;
    iMinGroupIndex_ = 0;
    iActiveGroupIndex_ = 0;
        // Just for now.  We'll call this->activateGroupUserData( 1 ) below...

    p_cf_dgGroupsList_ = new cf_DataGroup( "Groups" );
    p_cf_dgDomainItems_ = new cf_DataGroup( "Domain" );
    p_cf_dgRangeValues_ = new cf_DataGroup( "Range" );

    da_p_cf_dgLegendMarkers_.growBy( C1_CMODL_DGROU_GROWBY );
    iNumAllocatedLegendMarkers_ = 0;

    //---------------------------------------------
    // Init Data/Range/Origin Inclusion Treatment:
    //---------------------------------------------
    bDataRangeInclOrigAlways_ = FALSE;
    dNumDataDeltasForOrigInclus_ = 1.0;

    //--------------------------------------------
    // Init Data-Range Calculation modifier-vars:
    //--------------------------------------------
    dBufferRangeMinLimitBy_ = dBufferRangeMaxLimitBy_ = 0.0;
    bRangeMinLimitUsesFloor_ = bRangeMaxLimitUsesCeil_ = FALSE;

    dCandidateRangeMaxLimit_ = dCandidateRangeMinLimit_ = 0.0;
    bRangeMaxLimitUsesCand_ = bRangeMinLimitUsesCand_ = FALSE;

    //----------------------------------------------
    // Avoid any "ambiguous" state by setting up at
    // least ONE "default" user data group:
    //----------------------------------------------
    this->activateGroupUserData( 1 );
    bUpdatedChartData_ = FALSE;
            // but note that we HAVE NOT added any data yet...

}



cf_ChartModel
    ::~cf_ChartModel()
{
    delete p_cf_dgGroupsList_;
    delete p_cf_dgDomainItems_;
    delete p_cf_dgRangeValues_;

    for ( i = 1; i <= iNumAllocatedGroups_; i++ )
        delete ( (cf_DataGroup*) da_p_cf_dgDataGroups_[ i ] );

    // Our title/axis data-groups either were allocated or are == NULL...
    delete  p_cf_dgLabelTitleMain_;
    delete  p_cf_dgLabelTitleSub_;
    delete  p_cf_dgLabelAxisXBottom_;
    delete  p_cf_dgLabelAxisXBack_;
    delete  p_cf_dgLabelAxisYLeft_;
    delete  p_cf_dgLabelAxisYBack_;
    delete  p_cf_dgLabelAxisZBottom_;
    delete  p_cf_dgLabelAxisZBack_;
    delete  p_cf_dgLabelAxisPolarFloor_;

    delete  p_cf_dgAxisXBottom_;
    delete  p_cf_dgAxisXBack_;
    delete  p_cf_dgAxisYLeft_;
    delete  p_cf_dgAxisYBack_;
    delete  p_cf_dgAxisZBottom_;
    delete  p_cf_dgAxisZBack_;
    delete  p_cf_dgAxisPolarFloor_;
            //... and in any case safe to do a C++ delete op.

    for ( i = 1; i <= iNumAllocatedLegendMarkers_; i++ )
        delete ( (cf_DataGroup*) da_p_cf_dgLegendMarkers_[ i ] );
}


//----------------------------------------------------
// Allow users to access the implicit "domain",
// "range", and "group-list" data-groups this
// model constructs from user data.
//----------------------------------------------------
cf_DataGroup*
cf_ChartModel::accessGroupsList()
{
    return p_cf_dgGroupsList_;
}

cf_DataGroup*
cf_ChartModel::accessDomainItems()
{
    return p_cf_dgDomainItems_;
}


cf_DataGroup*
cf_ChartModel::accessRangeValues()
{
    return p_cf_dgRangeValues_;
}


//--------------------------------------------------
// Users interact with (e.g. add data to)
// a pointer to the current "active" group
// in a "dynamic array" of data groups.
//--------------------------------------------------
cf_DataGroup*
cf_ChartModel::activateGroupAxis
            ( cf_ChartAxis  enumChartAxisSelector )
{
    // Either create a new group, or point to an existing group:
    cf_DataGroup    *p_cf_dgActivated = NULL;
    switch ( enumChartAxisSelector )
    {
        case    CF_AXIS_X_BOTTOM:
                if ( p_cf_dgAxisXBottom_ == NULL )
                    p_cf_dgAxisXBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisXBottom_;
                break;

        case    CF_AXIS_X_BACK:
                if ( p_cf_dgAxisXBack_ == NULL )
                    p_cf_dgAxisXBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisXBack_;
                break;

        case    CF_AXIS_Y_LEFT:
                if ( p_cf_dgAxisYLeft_ == NULL )
                    p_cf_dgAxisYLeft_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisYLeft_;
                break;

        case    CF_AXIS_Y_BACK:
                if ( p_cf_dgAxisYBack_ == NULL )
                    p_cf_dgAxisYBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisYBack_;
                break;

        case    CF_AXIS_Z_BOTTOM:
                if ( p_cf_dgAxisZBottom_ == NULL )
                    p_cf_dgAxisZBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisZBottom_;
                break;

        case    CF_AXIS_Z_LEFT:
                if ( p_cf_dgAxisZBack_ == NULL )
                    p_cf_dgAxisZBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisZBack_;
                break;

        case    CF_AXIS_POLAR_FLOOR:
                if ( p_cf_dgAxisPolarFloor_ == NULL )
                    p_cf_dgAxisPolarFloor_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisPolarFloor_;
                break;

        default:
                if ( p_cf_dgAxisXBottom_ == NULL )
                    p_cf_dgAxisXBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgAxisXBottom_;
                break;
    }

    // If the group has not been used before, give it default attrobutes:
    if ( p_cf_dgActivated->active() == FALSE )
    {
        // Then activate this new group!...
        p_cf_dgActivated->on();

        // Setup default display attributes for the new group:
        cf_DispAttribs  *p_cf_datbNewGroup = p_cf_dgActivated->dispAttribs();
        p_cf_datbNewGroup->colorPen( CF_BLACK );
        p_cf_datbNewGroup->colorBrush( CF_BLACK );

        // Setup default text attributes for the new group:
        cf_TextAttribs  *p_cf_tatbNewGroup = p_cf_dgActivated->textAttribs();
        switch ( enumChartAxisSelector )
        {
            case    CF_AXIS_X_BOTTOM:
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_X_BACK:
                    p_cf_tatbNewGroup->shiftVertUp();
                    break;

            case    CF_AXIS_Y_BACK:
                    p_cf_tatbNewGroup->shiftHorzRight();
                    break;

            case    CF_AXIS_Y_LEFT:
                    p_cf_tatbNewGroup->shiftHorzLeft();
                    break;

            case    CF_AXIS_Z_BOTTOM:
                    p_cf_tatbNewGroup->shiftHorzRight();
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_Z_LEFT:
                    p_cf_tatbNewGroup->shiftHorzLeft();
                    p_cf_tatbNewGroup->shiftVertUp();
                    break;

            case    CF_AXIS_POLAR_FLOOR:
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_UNDEFINED:
            case    CF_AXIS_CUSTOM:
            default:
                    break;
        }

    }

    return( p_cf_dgActivated );
}



cf_DataGroup*
cf_ChartModel::activateGroupAxisLabel
            ( cf_ChartAxis enumChartAxisSelector )
{
    // Either create a new group, or point to an existing group:
    cf_DataGroup    *p_cf_dgActivated = NULL;
    switch ( enumChartAxisSelector )
    {
        case    CF_AXIS_X_BOTTOM:
                if ( p_cf_dgLabelAxisXBottom_ == NULL )
                    p_cf_dgLabelAxisXBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisXBottom_;
                break;

        case    CF_AXIS_X_BACK:
                if ( p_cf_dgLabelAxisXBack_ == NULL )
                    p_cf_dgLabelAxisXBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisXBack_;
                break;

        case    CF_AXIS_Y_LEFT:
                if ( p_cf_dgLabelAxisYLeft_ == NULL )
                    p_cf_dgLabelAxisYLeft_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisYLeft_;
                break;

        case    CF_AXIS_Y_BACK:
                if ( p_cf_dgLabelAxisYBack_ == NULL )
                    p_cf_dgLabelAxisYBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisYBack_;
                break;

        case    CF_AXIS_Z_BOTTOM:
                if ( p_cf_dgLabelAxisZBottom_ == NULL )
                    p_cf_dgLabelAxisZBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisZBottom_;
                break;

        case    CF_AXIS_Z_LEFT:
                if ( p_cf_dgLabelAxisZBack_ == NULL )
                    p_cf_dgLabelAxisZBack_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisZBack_;
                break;

        case    CF_AXIS_POLAR_FLOOR:
                if ( p_cf_dgLabelAxisPolarFloor_ == NULL )
                    p_cf_dgLabelAxisPolarFloor_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisPolarFloor_;
                break;

        default:
                if ( p_cf_dgLabelAxisXBottom_ == NULL )
                    p_cf_dgLabelAxisXBottom_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelAxisXBottom_;
                break;
    }

    // If the group has not been used before, give it default attrobutes:
    if ( p_cf_dgActivated->active() == FALSE )
    {
        // Then activate this new group!...
        p_cf_dgActivated->on();

        // Setup default display attributes for the new group:
        cf_DispAttribs  *p_cf_datbNewGroup = p_cf_dgActivated->dispAttribs();
        p_cf_datbNewGroup->colorPen( CF_BLACK );
        p_cf_datbNewGroup->colorBrush( CF_BLACK );

        // Setup default text attributes for the new group:
        cf_TextAttribs  *p_cf_tatbNewGroup = p_cf_dgActivated->textAttribs();
        switch ( enumChartAxisSelector )
        {
            case    CF_AXIS_X_BOTTOM:
                    p_cf_tatbNewGroup->shiftHorzRight();
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_X_BACK:
                    p_cf_tatbNewGroup->shiftHorzLeft();
                    p_cf_tatbNewGroup->shiftVertUp();
                    break;

            case    CF_AXIS_Y_BACK:
                    p_cf_tatbNewGroup->shiftHorzRight();
                    p_cf_tatbNewGroup->shiftVertDown();
                    p_cf_tatbNewGroup->angleRotationPrefer( -90 );
                    break;

            case    CF_AXIS_Y_LEFT:
                    p_cf_tatbNewGroup->shiftHorzLeft();
                    p_cf_tatbNewGroup->shiftVertUp();
                    p_cf_tatbNewGroup->angleRotationPrefer( 90 );
                    break;

            case    CF_AXIS_Z_BOTTOM:
                    p_cf_tatbNewGroup->shiftHorzRight();
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_Z_LEFT:
                    p_cf_tatbNewGroup->shiftHorzLeft();
                    p_cf_tatbNewGroup->shiftVertUp();
                    break;

            case    CF_AXIS_POLAR_FLOOR:
                    p_cf_tatbNewGroup->shiftVertDown();
                    break;

            case    CF_AXIS_UNDEFINED:
            case    CF_AXIS_CUSTOM:
            default:
                    break;
        }

    }

    return( p_cf_dgActivated );
}


cf_DataGroup*
cf_ChartModel::activateGroupLegend
            (  int   iUserLegendNumber )
{
    // Make a TENTATIVE Index Assignment:
    int iValidateUserIndex = iUserLegendNumber;

    // Correct Bogus Index Values that are TOO LOW:
    if ( iValidateUserIndex < 1 ) iValidateUserIndex = 1;

    // "Fill-In" Index Values that are TOO HIGH:
    if ( iValidateUserIndex > iNumAllocatedLegendMarkers_ )
    {
        int iBackFillStartingAt = iNumAllocatedLegendMarkers_ + 1;

        for ( i = iBackFillStartingAt; i <= iValidateUserIndex;  i++ )
        {
            // Back-Fill missing dynamic array ptr vals!...
            cf_DataGroup *p_cf_dgBackFill = new cf_DataGroup();
            p_cf_dgBackFill->labelNew( "Un-assigned Legend!" );

            //...and take responsibility for it...
            da_p_cf_dgLegendMarkers_[ i ] = p_cf_dgBackFill;
            iNumAllocatedLegendMarkers_++;
        }
    }

    // Get the address of this (newly OR re) activated data group:
    cf_DataGroup    *p_cf_dgActivated = ( (cf_DataGroup*)
                    da_p_cf_dgLegendMarkers_[ iValidateUserIndex ] );

    // Has this group been used before?
    if ( p_cf_dgActivated->active() == FALSE )
    {
        // Then activate this new group!...
        p_cf_dgActivated->on();

        // Now set up DEFAULT Display attributes from the set
        // of Attribute "Palettes" this Chart Model maintains:
        cf_DispAttribs  *p_cf_datbNewGroup
                        = p_cf_dgActivated->dispAttribs();

        p_cf_datbNewGroup->colorPen( CF_BLACK );// a good default choice
        p_cf_datbNewGroup->colorBrush( paletteColor( iValidateUserIndex ) );
        p_cf_datbNewGroup->stylePen( palettePenStyle( 1 ) );
                            // which gets mod'ed to a palette-selection.
        p_cf_datbNewGroup->markWhen2D( TRUE );
        p_cf_datbNewGroup->markWhen3D( TRUE );
        p_cf_datbNewGroup->stylePenWidth( palettePenWidth() );

                    // 2D/3D marking mode (for poly-lines, etc) now set.
    }

    // Return the address of this current active group:
    return  p_cf_dgActivated;
            // Which users can use as a pointer l-value:
}

cf_DataGroup*
cf_ChartModel::activateGroupTitle
            ( cf_ChartTitle  enumChartTitleSelector )
{
    cf_DataGroup    *p_cf_dgActivated = NULL;

    switch ( enumChartTitleSelector )
    {
        case    CF_TITLE_SUB:
                if ( p_cf_dgLabelTitleSub_ == NULL )
                    p_cf_dgLabelTitleSub_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelTitleSub_;
                break;

        case    CF_TITLE_MAIN:
        default:
                if ( p_cf_dgLabelTitleMain_ == NULL )
                    p_cf_dgLabelTitleMain_ = new cf_DataGroup();
                p_cf_dgActivated = p_cf_dgLabelTitleMain_;
                break;
    }

    if ( p_cf_dgActivated->active() == FALSE )
    {
        // Then activate this new group!...
        p_cf_dgActivated->on();

        // Setup default display attributes for the new group:
        cf_DispAttribs  *p_cf_datbNewGroup = p_cf_dgActivated->dispAttribs();
        p_cf_datbNewGroup->colorPen( CF_BLACK );
        p_cf_datbNewGroup->colorBrush( CF_BLACK );
            // Note: the *Brush* color will become the *text* color!
    }

    return( p_cf_dgActivated );
}




cf_DataGroup*
cf_ChartModel::activateGroupUserData( int  iUserGroupIndex )
{
    // Make a TENTATIVE Index Assignment:
    int iValidateUserIndex = iUserGroupIndex;

    // Correct Bogus Index Values that are TOO LOW:
    if ( iValidateUserIndex < 1 ) iValidateUserIndex = 1;

    // "Fill-In" Index Values that are TOO HIGH:
    if ( iValidateUserIndex > iMaxGroupIndex_ )
    {
        cf_DataGroup    *p_cf_dgBackFill = NULL;
                        // We'll new() Data Groups to "Back Fill"
                        // the "missing" dynamic array ptr vals!...

        for ( i = ( iMaxGroupIndex_ + 1 ); i <= iValidateUserIndex;  i++ )
        {   // Make a new "INACTIVE" data-group...
            p_cf_dgBackFill = new cf_DataGroup();
                 //...with "DEFAULT" display-attribs
            da_p_cf_dgDataGroups_[ i ] = p_cf_dgBackFill;
                //...take responsibility for it...
            iNumAllocatedGroups_++;
        }       //...and account for this new allocation.
    }

    // OK, we've got a validated index..
    iActiveGroupIndex_ = iValidateUserIndex;
                // into our dynamic array of POINTERS to Data Groups

    // Get the address of this (newly OR re) activated data group:
    cf_DataGroup    *p_cf_dgActivated = ( (cf_DataGroup*)
                    da_p_cf_dgDataGroups_[ iActiveGroupIndex_ ] );

    // Has this group been used before?
    if ( p_cf_dgActivated->active() == FALSE )
    {
        // Then activate this new group!...
        p_cf_dgActivated->on();

        // Now set up DEFAULT Display attributes from the set
        // of Attribute "Palettes" this Chart Model maintains:
        cf_DispAttribs  *p_cf_datbNewGroup = p_cf_dgActivated->dispAttribs();

        p_cf_datbNewGroup->colorPen( CF_BLACK );// a good default choice
        p_cf_datbNewGroup->colorBrush( paletteColor( iActiveGroupIndex_ ) );
                    // which gets mod'ed to a palette color selection.
        p_cf_datbNewGroup->markWhen2D( paletteMarkers2D3D( 2 ) );
        p_cf_datbNewGroup->markWhen3D( paletteMarkers2D3D( 3 ) );
                    // 2D/3D marking mode (for poly-lines, etc) now set.
    }

    // Update Max Group Index Val?
    if ( iActiveGroupIndex_ > iMaxGroupIndex_ )
        iMaxGroupIndex_ = iActiveGroupIndex_;

    // Update Min Group Index Val?
    if ( iMinGroupIndex_ == 0 )
        iMinGroupIndex_ = iActiveGroupIndex_;
    else
        if ( iActiveGroupIndex_ < iMinGroupIndex_ )
            iMaxGroupIndex_ = iActiveGroupIndex_;

    // Return the address of this current active group:
    return  p_cf_dgActivated;
            // Which users can use as a pointer l-value:

}   // end of cf_ChartModel::activateGroupUserData( ... )


cf_ChartEngine*
cf_ChartModel::activeEngine()
{
    return( p_cf_engChartEngine_ );
}


BOOL
cf_ChartModel::activeEngine
            ( cf_ChartEngine  *p_cf_engToUseOurData )
{
    // Don't step into this if something is FUBAR!
    if ( p_cf_engToUseOurData == NULL ) return FALSE;

    p_cf_engChartEngine_ = p_cf_engToUseOurData;

    return TRUE;
}



cf_ChartView*
cf_ChartModel::activeView()
{
    return this->p_bf_cviewParentView_;
}



//==========================================================
// Add a (y) data item to the current "activated" (z) group.
//==========================================================
cf_XYZ*
    cf_ChartModel
        ::addDataYVal( double      dUserYVal,
                       const char* a_cDomainItemLabel,
                       const char* a_cRangeValueLabel )
{
    cf_XYZ  *p_cf_xyzReturnVal = NULL;

    switch ( cf_enumCurrentIndexMode_ )
    {
        case  CF_INDEX_INT_I:
             p_cf_xyzReturnVal
                = this->addDataYValIndexIntI( dUserYVal,
                                              a_cDomainItemLabel,
                                              a_cRangeValueLabel );
            break;

        case CF_INDEX_NAT_N:
        case CF_INDEX_USR_X:
        default:
             p_cf_xyzReturnVal
                = this->addDataYValIndexNatN( dUserYVal,
                                              a_cDomainItemLabel,
                                              a_cRangeValueLabel );
            break;

    }

    return p_cf_xyzReturnVal;
}


//============================================================
// Add a (x,y) data item to the current "activated" (z) group.
//============================================================
cf_XYZ*
    cf_ChartModel
        ::addDataXYVal( double      dUserXVal,
                        double      dUserYVal,
                        const char* a_cDomainItemLabel,
                        const char* a_cRangeValueLabel )
{
    // Use the functionality of the general method:
    return( this->addDataXYZVal( dUserXVal, dUserYVal,
                                 iActiveGroupIndex_,
                                 a_cDomainItemLabel,
                                 a_cRangeValueLabel ) );
        // User can continue to operate on the return val...
        // Note that bUpdatedChartData_ now == TRUE;
}


//===========================================================
// Add a (x,y,z) data item to the current "activated" group.
//===========================================================
cf_XYZ*
    cf_ChartModel
        ::addDataXYZVal( double      dUserXVal,
                         double      dUserYVal,
                         double      dUserZVal,
                         const char* a_cDomainItemLabel,
                         const char* a_cRangeValueLabel )
{
    // Note that the user has assumed responsibility for auto-indexing:
    cf_enumCurrentIndexMode_  = CF_INDEX_USR_X;

    // Add the user value to the ACTIVE data group as a new XYZ object:
    cf_XYZ* p_cf_xyzNewUserVal
            = ( (cf_DataGroup*)
                    da_p_cf_dgDataGroups_[ iActiveGroupIndex_ ] )
                                ->addXYZVal(    dUserXVal,
                                                dUserYVal,
                                                dUserZVal );
    // Consider adding a Domain ITEM Label:
    if ( a_cDomainItemLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzDomainItem
                = p_cf_dgDomainItems_->addXYZVal( dUserXVal, 0.0, 0.0 );
        p_cf_xyzDomainItem->labelNew( a_cDomainItemLabel );
    }


    // Also consider adding a Range VALUE Label:
    if ( a_cRangeValueLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzRangeValue
                = p_cf_dgRangeValues_->addXYZVal( 0.0, dUserYVal, 0.0 );
        p_cf_xyzRangeValue->labelNew( a_cRangeValueLabel );
    }

    // Wrap things up:
    bUpdatedChartData_ = TRUE;
            // If the chart engine asks, tell it we have new data...
    return  p_cf_xyzNewUserVal;
            // Lets the user continue to operate on the new object...
}



//-----------------------------------------------------------
// Adds user's Y-val with an X-Index equal to
// subsequent Natural Numbers (1, 2, 3, .. N).
// Z-index generated from current "activateGroupUserData()" call.
//-----------------------------------------------------------
cf_XYZ*
    cf_ChartModel
        ::addDataYValIndexNatN( double  dUserYVal,
                                const char* a_cDomainItemLabel,
                                const char* a_cRangeValueLabel )
{
    // Add the user value to the ACTIVE data group as a new XYZ object:
    int iNewGroupCount  = ( (cf_DataGroup*) da_p_cf_dgDataGroups_
                                [ iActiveGroupIndex_ ] )->numItems() + 1;
    cf_XYZ* p_cf_xyzNewUserVal
            = ( (cf_DataGroup*)da_p_cf_dgDataGroups_[ iActiveGroupIndex_ ] )
                                        ->addXYZVal(    iNewGroupCount,
                                                        dUserYVal,
                                                        iActiveGroupIndex_ );
    // Add a Domain-ITEM Label?
    if ( a_cDomainItemLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzDomainItem
                = p_cf_dgDomainItems_->addXYZVal( iNewGroupCount, 0.0, 0.0 );
        p_cf_xyzDomainItem->labelNew( a_cDomainItemLabel );
        p_cf_xyzNewUserVal->labelUsing( p_cf_xyzDomainItem->labelIs() );
        // The user's new XYZ item will also use the domain-item-label.
    }
    else
    {
        int iDomainItemCount = this->p_cf_dgDomainItems_->numItems();
        if ( iDomainItemCount < iNewGroupCount )
            ( p_cf_dgDomainItems_
                ->addXYZVal( iNewGroupCount,
                             0.0, 0.0 ) )->labelNew(((double) iNewGroupCount));
    }

    // Also consider adding a Range VALUE Label:
    if ( a_cRangeValueLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzRangeValue
                = p_cf_dgRangeValues_->addXYZVal(   iNewGroupCount,
                                                    dUserYVal, 0.0 );
        p_cf_xyzRangeValue->labelNew( a_cRangeValueLabel );
        p_cf_xyzNewUserVal->labelUsing( p_cf_xyzRangeValue->labelIs() );
        // The user's new XYZ item will also use the range-value-label.
    }

    // If the chart engine asks, tell it we have new data...
    bUpdatedChartData_ = TRUE;
    return  p_cf_xyzNewUserVal;
            // Lets the user continue to operate on the new object...

}   // end of cf_ChartModel::addDataYValIndexNatN( YVal... )


//-----------------------------------------------------------
// Adds user's Y-val with an X-Index equal to
// subsequent Integer Numbers (0, 1, 2, .. I)
// Z-index generated from current "activateGroupUserData()" call.
//-----------------------------------------------------------
cf_XYZ*
    cf_ChartModel
        ::addDataYValIndexIntI( double  dUserYVal,
                                const char* a_cDomainItemLabel,
                                const char* a_cRangeValueLabel )
{
    // Add the user value to the data group as a new XYZ object:
    int iCurrGroupCount = ( ( cf_DataGroup*) da_p_cf_dgDataGroups_
                                    [ iActiveGroupIndex_ ] )->numItems();
    cf_XYZ* p_cf_xyzNewUserVal
            = ( (cf_DataGroup*)da_p_cf_dgDataGroups_[ iActiveGroupIndex_ ] )
                                        ->addXYZVal(    iCurrGroupCount,
                                                        dUserYVal,
                                                        iActiveGroupIndex_ );
    // Add a Domain-ITEM Label?
    if ( a_cDomainItemLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzDomainItem
                = p_cf_dgDomainItems_->addXYZVal( iCurrGroupCount, 0.0, 0.0 );
        p_cf_xyzDomainItem->labelNew( a_cDomainItemLabel );
        p_cf_xyzNewUserVal->labelUsing( p_cf_xyzDomainItem->labelIs() );
        // The user's new XYZ item will also use the domain-item-label.
    }
    else
    {
        int iDomainItemCount = this->p_cf_dgDomainItems_->numItems();
        if ( iCurrGroupCount >= iDomainItemCount )
            ( p_cf_dgDomainItems_
                ->addXYZVal( iCurrGroupCount,
                             0.0, 0.0 ) )->labelNew(((double)iCurrGroupCount));
    }

    // Also consider adding a Range VALUE Label:
    if ( a_cRangeValueLabel != NULL )
    {
        cf_XYZ  *p_cf_xyzRangeValue
                = p_cf_dgRangeValues_->addXYZVal(   iCurrGroupCount,
                                                    dUserYVal, 0.0 );
        p_cf_xyzRangeValue->labelNew( a_cRangeValueLabel );
        p_cf_xyzNewUserVal->labelUsing( p_cf_xyzRangeValue->labelIs() );
        // The user's new XYZ item will also use the range-value-label.
    }

    // If the chart engine asks, tell it we have new data...
    bUpdatedChartData_ = TRUE;
    return  p_cf_xyzNewUserVal;
            // Lets the user continue to operate on the new object...

}   // end of cf_ChartModel::addDataYValIndexIntI( YVal... )



//------------------------------------------------------------
// Add an int-indexed label to the "domain axis item" group:
//------------------------------------------------------------
cf_XYZ*
cf_ChartModel::addLabelDomainItem
            (   double      dDomainItemIndex,
                const char  *p_strDomainItemLabel )
{
    cf_XYZ  *p_cf_xyzDomainItem
            = p_cf_dgDomainItems_->addXYZVal(   dDomainItemIndex,
                                                0.0, iActiveGroupIndex_ );
    p_cf_xyzDomainItem->labelNew( p_strDomainItemLabel );
    return  p_cf_xyzDomainItem;
}

cf_XYZ*
cf_ChartModel::addLabelDomainItem
            (   double      dDomainItemIndex,
                double      dValueForLabel )
{
    cf_XYZ  *p_cf_xyzDomainItem
            = p_cf_dgDomainItems_->addXYZVal(   dDomainItemIndex,
                                                0.0, iActiveGroupIndex_ );
    p_cf_xyzDomainItem->labelNew( dValueForLabel );
    return  p_cf_xyzDomainItem;
}


cf_XYZ*
cf_ChartModel::addLabelGroupsList
            (   double      dGroupsListIndex,
                const char  *p_strGroupsListLabel )
{
    double dXIndexVal = (double)( p_cf_dgGroupsList_->numItems() + 1 );
    cf_XYZ  *p_cf_xyzGroupsList
            = p_cf_dgGroupsList_->addXYZVal( dXIndexVal, 0.0, dGroupsListIndex );
    p_cf_xyzGroupsList->labelNew( p_strGroupsListLabel );
    return  p_cf_xyzGroupsList;
}


cf_XYZ*
cf_ChartModel::addLabelGroupsList
            (   double      dGroupsListIndex,
                double      dValueForLabel )
{
    double dXIndexVal = (double)( p_cf_dgGroupsList_->numItems() + 1 );
    cf_XYZ  *p_cf_xyzGroupsList
            = p_cf_dgGroupsList_->addXYZVal( dXIndexVal, 0.0, dGroupsListIndex );
    p_cf_xyzGroupsList->labelNew( dValueForLabel );
    return  p_cf_xyzGroupsList;
}

//------------------------------------------------------------
// Add an user-specified label to the "range values" group:
//------------------------------------------------------------
cf_XYZ*
    cf_ChartModel
        ::addLabelRangeValue(   double  dRangeValueIndex,
                                const char* a_cRangeValueLabel )
{
    cf_XYZ  *p_cf_xyzRangeValue
            = p_cf_dgRangeValues_->addXYZVal(  0.0, dRangeValueIndex,
                                            iActiveGroupIndex_ );
    p_cf_xyzRangeValue->labelNew( a_cRangeValueLabel );
    return  p_cf_xyzRangeValue;
}

cf_XYZ*
    cf_ChartModel
        ::addLabelRangeValue(   double  dRangeValueIndex,
                                double  dValueForLabel )
{
    cf_XYZ  *p_cf_xyzRangeValue
            = p_cf_dgRangeValues_->addXYZVal(  0.0, dRangeValueIndex,
                                            iActiveGroupIndex_ );
    p_cf_xyzRangeValue->labelNew( dValueForLabel );
    return  p_cf_xyzRangeValue;
}


int
    cf_ChartModel
        ::dataGroupActive()
            { return iActiveGroupIndex_; }
int
    cf_ChartModel
        ::dataGroupCount()
            { return iNumAllocatedGroups_; }
int
    cf_ChartModel
        ::dataGroupsStartAt()
            { return iMinGroupIndex_; }
int
    cf_ChartModel
        ::dataGroupsEndAt()
            { return iMaxGroupIndex_; }


cf_DataGroup*
    cf_ChartModel
        ::dataGroupsReset()
{
    // First reset our axis-label data groups:
    p_cf_dgGroupsList_->reset();
    p_cf_dgGroupsList_->labelNew( "Groups" );

    p_cf_dgDomainItems_->reset();
    p_cf_dgDomainItems_->labelNew( "Domain" );

    p_cf_dgRangeValues_->reset();
    p_cf_dgRangeValues_->labelNew( "Range" );

    // Loose any data-groups new()ed by user "activate" calls:
    for ( i = 1; i <= iNumAllocatedGroups_; i++ )
            delete ( (cf_DataGroup*) da_p_cf_dgDataGroups_[ i ] );

    iNumAllocatedGroups_ = 0;
    iMaxGroupIndex_ = 0;
    iMinGroupIndex_ = 0;
    iActiveGroupIndex_ = 0;     // Just for now -- we'll now
                                // IMMEDIATELY activate group one!

    // Avoid any "ambiguous" state by setting up
    // at least ONE "default" user data group...
    cf_DataGroup *p_cf_dgReturnVal = this->activateGroupUserData( 1 );

    //...and note that we have deleted our current data set!
    bUpdatedChartData_ = TRUE;

    return p_cf_dgReturnVal;

}   // end of cf_ChartModel::dataGroupsReset();




//===========================================
// Get/set Index Method IDs as follows:
//===========================================
cf_IndexMode
cf_ChartModel::dataIndexMethod()
{
    return( cf_enumCurrentIndexMode_ );
}


BOOL
cf_ChartModel::dataIndexMethod
    (    cf_IndexMode    enumNewIndexMode,
         BOOL            bForceEngineUpdate )
{
    BOOL    bStatus = FALSE;
    cf_enumCurrentIndexMode_ = enumNewIndexMode;

    int iNumOrigDomainItems = p_cf_dgDomainItems_->numItems();

    if ( enumNewIndexMode == CF_INDEX_USR_X )
    {
        p_cf_dgDomainItems_->reset();
        cf_DataGroup  *p_cf_dgAccessGroup = (cf_DataGroup*) da_p_cf_dgDataGroups_[ 1 ];
        int iNumGroupItems = p_cf_dgAccessGroup->numItems();

        for ( i = 1; i <= iNumGroupItems; i++ )
        {
            cf_XYZ  *p_cf_xyzGetX = p_cf_dgAccessGroup->activateXYZValAt( i );
            double  dXVal = p_cf_xyzGetX->x();
            ( p_cf_dgDomainItems_->addXYZVal( p_cf_xyzGetX->x(), 0.0, 0.0 ) )->labelNew( dXVal );
        }
    }
    else if ( enumNewIndexMode == CF_INDEX_INT_I )
    {
        p_cf_dgDomainItems_->reset();
        for ( i = 1; i <= iNumOrigDomainItems; i++ )
            ( p_cf_dgDomainItems_->addXYZVal( ( i - 1 ), 0.0, 0.0 ) )->labelNew( ((double) i - 1 ) );
    }
    else // i.e. ( enumNewIndexMode == CF_INDEX_NAT_N )
    {
        p_cf_dgDomainItems_->reset();
        for ( i = 1; i <= iNumOrigDomainItems; i++ )
            ( p_cf_dgDomainItems_->addXYZVal( i, 0.0, 0.0 ) )->labelNew( ((double) i ) );
    }

    // Should we re-generate a new set of chart-engine
    // objects to reflect this new data-state?
    if ( bForceEngineUpdate == TRUE )
    {
        if ( p_cf_engChartEngine_ != NULL )
        {
            p_cf_engChartEngine_->applyDataUpdate( this );
            p_cf_engChartEngine_->autoConfigure();
            p_cf_engChartEngine_->applyGeomXform();
            p_cf_engChartEngine_->applyProjXform();
            p_cf_engChartEngine_->applyDispMetrics();
            bStatus = TRUE;
        }
    }

    return( bStatus );
}




//----------------------------------------------------------
// Methods to deal with User Data-Range Buffering:
//----------------------------------------------------------
double
    cf_ChartModel
        ::dataRangeInclMaxBuffer()
            { return dBufferRangeMaxLimitBy_; }
void
    cf_ChartModel
        ::dataRangeInclMaxBuffer( double dAddOntoCurrentRange )
            { dBufferRangeMaxLimitBy_ = dAddOntoCurrentRange; }
            // Get/set extra "margin" onto the user-data-range.
            // Subject to range's "use-max-ceiling" state (below).
            // C-tor inits this buffer value to zero.
double
    cf_ChartModel
        ::dataRangeInclMaxCandidate()
            { return dCandidateRangeMaxLimit_; }
void
    cf_ChartModel
        ::dataRangeInclMaxCandidate( double dPossibleMaxVal )
            {   dCandidateRangeMaxLimit_ = dPossibleMaxVal;
                bRangeMaxLimitUsesCand_ = TRUE; }
            // Get/set a "candidate" max value for user-data-range.
            // Subject to range's "max-buffer" state (above).
            // Subject to range's "use-max-ceiling" state (below).
BOOL
    cf_ChartModel
        ::dataRangeInclMaxCandidateInUse()
            { return bRangeMaxLimitUsesCand_; }
void
    cf_ChartModel
        ::dataRangeInclMaxCandidateInUse( BOOL bNewState )
            { bRangeMaxLimitUsesCand_ = bNewState; }
            // Turn consideration of any candidate min value for
            // the user data-range ON/OFF.  C-tor inits to FALSE.
            // Calls to dataRangeInclMinCandidate( double );
            // (immediately above) reset this state to TRUE!
BOOL
    cf_ChartModel
        ::dataRangeInclMaxCeiling()
            { return bRangeMaxLimitUsesCeil_; }
void
    cf_ChartModel
        ::dataRangeInclMaxCeiling( BOOL bNewState )
            { bRangeMaxLimitUsesCeil_ = bNewState; }
            // If set TRUE, uses math.h ceil() to round user data
            // ranges UP to an integer value.  C-tor inits to FALSE.
double
    cf_ChartModel
        ::dataRangeInclMinBuffer()
            { return dBufferRangeMinLimitBy_; }
void
    cf_ChartModel
        ::dataRangeInclMinBuffer( double dAddOntoCurrentRange )
            { dBufferRangeMinLimitBy_ = dAddOntoCurrentRange; }
            // Get/set extra "margin" onto the user-data-range.
            // Subject to range's "use-min-floor" state (below).
            // C-tor inits this buffer value to zero.
double
    cf_ChartModel
        ::dataRangeInclMinCandidate()
            { return dCandidateRangeMinLimit_; }
void
    cf_ChartModel
        ::dataRangeInclMinCandidate( double dPossibleMinVal )
            {   dCandidateRangeMinLimit_ = dPossibleMinVal;
                bRangeMinLimitUsesCand_ = TRUE; }
            // Get/set a "candidate" min value for user-data-range.
            // Subject to range's "min-buffer" state (above).
            // Subject to range's "use-min-floor" state (below).
BOOL
    cf_ChartModel
        ::dataRangeInclMinCandidateInUse()
            { return bRangeMinLimitUsesCand_; }
void
    cf_ChartModel
        ::dataRangeInclMinCandidateInUse( BOOL bNewState )
            { bRangeMinLimitUsesCand_ = bNewState; }
            // Turn consideration of any candidate min value for
            // the user data-range ON/OFF.  C-tor inits to FALSE.
            // Calls to dataRangeInclMinCandidate( double );
            // (immediately above) reset this state to TRUE!
BOOL
    cf_ChartModel
        ::dataRangeInclMinFloor()
            { return bRangeMinLimitUsesFloor_; }
void
    cf_ChartModel
        ::dataRangeInclMinFloor( BOOL bNewState )
            { bRangeMinLimitUsesFloor_ = bNewState; }
            // If set TRUE, uses math.h floor() to round user data
            // ranges DOWN to an integer value.  C-tor inits to FALSE.


//----------------------------------------------------------
// Methods to deal with Range/Origin Inclusion/Exclusion:
//----------------------------------------------------------
BOOL
    cf_ChartModel
        ::dataRangeInclOrigAlways()
            { return bDataRangeInclOrigAlways_; }
void
    cf_ChartModel
        ::dataRangeInclOrigAlways( BOOL bNewState )
            { bDataRangeInclOrigAlways_ = bNewState; }

double
    cf_ChartModel
        ::dataRangeInclOrigFromDelta()
            { return dNumDataDeltasForOrigInclus_; }
void
    cf_ChartModel
        ::dataRangeInclOrigFromDelta( double dNumDataDeltasAway )
            { dNumDataDeltasForOrigInclus_ = dNumDataDeltasAway; }


//-----------------------------------------------------
// Query the active data groups for min/max/range
// values.  For use in engine axis/tick ops, etc...
//-----------------------------------------------------
double
cf_ChartModel::dataXItemsMax()
{
    double  dReturnVal = 0.0;

    if ( cf_enumCurrentIndexMode_  == CF_INDEX_USR_X )
    {
        dReturnVal = p_cf_dgDomainItems_->dataXMax();
        for ( i = iMinGroupIndex_; i <= iMaxGroupIndex_; i++ )
        {
            double dTestVal = ( (cf_DataGroup*)
                                da_p_cf_dgDataGroups_[ i ] )->dataXMax();
            if ( dTestVal > dReturnVal ) dReturnVal = dTestVal;
        }
    }
    else if ( cf_enumCurrentIndexMode_  == CF_INDEX_INT_I )
        //dReturnVal = ( p_cf_dgDomainItems_->numItems() );
        dReturnVal = ( p_cf_dgDomainItems_->numItems() - 1 );
    else // ( cf_enumCurrentIndexMode_  == CF_INDEX_NAT_N )
        dReturnVal = ( p_cf_dgDomainItems_->numItems() + 1  );

    return dReturnVal;
}


double
cf_ChartModel::dataXItemsMin()
{
    double  dReturnVal = 0.0;

    if ( cf_enumCurrentIndexMode_  == CF_INDEX_USR_X )
    {
        dReturnVal = p_cf_dgDomainItems_->dataXMin();
        for ( i = iMinGroupIndex_; i <= iMaxGroupIndex_; i++ )
        {
            double dTestVal = ( (cf_DataGroup*)
                                da_p_cf_dgDataGroups_[ i ] )->dataXMin();
            if ( dTestVal < dReturnVal ) dReturnVal = dTestVal;
        }
    }
    if ( cf_enumCurrentIndexMode_ == CF_INDEX_INT_I )
        dReturnVal = 0.0;
    else // i.e. ( cf_enumCurrentIndexMode_  == CF_INDEX_NAT_N )
        dReturnVal = 0.0;   // Note != 1.0 !!!

    return dReturnVal;
}


double
cf_ChartModel::dataXItemsExtent()
{
    return ( this->dataXItemsMax() - this->dataXItemsMin() );
}

double
    cf_ChartModel
        ::dataYValuesMax()
{
    // First look for the Max:
    double  dTestVal = 0.0;
    double  dReturnVal = p_cf_dgRangeValues_->dataYMax();
            // First *candidate* is the data-range group max value

    for ( i = iMinGroupIndex_; i <= iMaxGroupIndex_; i++ )
    {
        dTestVal = ( (cf_DataGroup*)
                     da_p_cf_dgDataGroups_[ i ] )->dataYMax();
        if ( dTestVal > dReturnVal ) dReturnVal = dTestVal;
    }                     //...OK, we've got our Max value.

    // Now consider what to do if this Max value is NEGATIVE:
    if ( dReturnVal < 0.0 )
    {    // Did the user FORCE the Max Value to be zero?
        if ( bDataRangeInclOrigAlways_ == TRUE ) dReturnVal = 0.0;
        else // Consider origin inclusion w.r.t delta of the data range.
        {    // To get the delta, we'll first need to find the Min...
            double dMinVal = ( (cf_DataGroup*)
                                da_p_cf_dgDataGroups_
                                        [ iMinGroupIndex_ ] )->dataYMin();
            for ( i = iMinGroupIndex_ + 1; i <= iMaxGroupIndex_; i++ )
            {
                dTestVal = ( (cf_DataGroup*)
                             da_p_cf_dgDataGroups_[ i ] )->dataYMin();
                if ( dTestVal < dMinVal ) dMinVal = dTestVal;
            }               //...OK, we've got our Min value.

            double  dDataDeltaLimit = ( dReturnVal - dMinVal )
                                    * dNumDataDeltasForOrigInclus_;
            // Compare the data delta against our negative Max value...
            if ( dDataDeltaLimit >= -dReturnVal ) dReturnVal = 0.0;
        }           //...and bump the negative Max value up to zero
    }   //...negative dMax values now dealt with.

    // Now consider any USER-specified candidates for the max:
    if ( bRangeMaxLimitUsesCand_ == TRUE )
        if ( dCandidateRangeMaxLimit_ > dReturnVal )
            dReturnVal = dCandidateRangeMaxLimit_;

    // Add any current amount of range buffering:
    dReturnVal += dBufferRangeMaxLimitBy_;
    if ( bRangeMaxLimitUsesCeil_ ) dReturnVal = ceil( dReturnVal );

    return dReturnVal;
}

double
    cf_ChartModel
        ::dataYValuesMin()
{
    // First look for the Min:
    double  dTestVal;
    double  dReturnVal
            = ( (cf_DataGroup*)
                da_p_cf_dgDataGroups_[ iMinGroupIndex_ ] )->dataYMin();
            // First *candidate* is the data-range group min value.
    dTestVal = dReturnVal;  // Synchronize values prior to testing!

    for ( i = ( iMinGroupIndex_ + 1 ); i <= iMaxGroupIndex_; i++ )
    {
        dTestVal = ( (cf_DataGroup*)
                     da_p_cf_dgDataGroups_[ i ] )->dataYMin();
        if ( dTestVal < dReturnVal ) dReturnVal = dTestVal;
    }                     //...OK, we've got our Min value.

    // Now consider what to do if this Min value is POSITIVE:
    if ( dReturnVal > 0.0 )
    {    // Did the user FORCE the Min Value to be zero?
        if ( bDataRangeInclOrigAlways_ == TRUE ) dReturnVal = 0.0;
        else // Consider origin inclusion w.r.t delta of the data range.
        {    // To get the delta, we'll first need to find the Max...
            double dMaxVal = ( (cf_DataGroup*)
                                da_p_cf_dgDataGroups_
                                        [ iMinGroupIndex_ ] )->dataYMax();
            for ( i = iMinGroupIndex_ + 1; i <= iMaxGroupIndex_; i++ )
            {
                dTestVal = ( (cf_DataGroup*)
                             da_p_cf_dgDataGroups_[ i ] )->dataYMax();
                if ( dTestVal > dMaxVal ) dMaxVal = dTestVal;
            }               //...OK, we've got our Max value.

            double  dDataDeltaLimit = ( dMaxVal - dReturnVal )
                                    * dNumDataDeltasForOrigInclus_;
            // Compare the data delta against our positive Min value...
            if ( dDataDeltaLimit >= dReturnVal ) dReturnVal = 0.0;
        }           //...and bump the positive Min value down to zero
    }   //...positive dMin values now dealt with.

    // Now consider any USER-specified candidates for the min:
    if ( bRangeMinLimitUsesCand_ == TRUE )
        if ( dCandidateRangeMinLimit_ < dReturnVal )
            dReturnVal = dCandidateRangeMinLimit_;

    // Add any current amount of range buffering:
    dReturnVal -= dBufferRangeMinLimitBy_;
    if ( bRangeMinLimitUsesFloor_ ) dReturnVal = floor( dReturnVal );

    return dReturnVal;
}

double
    cf_ChartModel
        ::dataYValuesExtent()
            { return ( this->dataYValuesMax() - this->dataYValuesMin() ); }

double
    cf_ChartModel
        ::dataYValSumsMax()
{
    double  dTestVal = 0.0;
    double  dReturnVal = p_cf_dgRangeValues_->dataYMax();
            // First *candidate* is the data-range group max value

    // Determine the number of domain items we will sample:
    int iNumItemsInGroup = 0;
    int iMaxNumItemsInAnyGroup = 0;
    for ( i = iMinGroupIndex_; i <= iMaxGroupIndex_; i++ )
    {
        iNumItemsInGroup = ( (cf_DataGroup*)
                             da_p_cf_dgDataGroups_[ i ] )->numItems();
        if ( iNumItemsInGroup > iMaxNumItemsInAnyGroup )
            iMaxNumItemsInAnyGroup = iNumItemsInGroup;
    }

    // Cycle through the domain item indexes and sum the range vals:
    cf_DataGroup    *p_cf_dgSample = NULL;
    cf_XYZ          *p_cf_xyzSample = NULL;
    double      dSampleAtIndex = 0.0;
    double      dSumAtIndex = 0.0;
    for ( j = 1; j <= iMaxNumItemsInAnyGroup; j++ )
    {
        dSumAtIndex = 0.0;
        for ( k = iMinGroupIndex_; k <= iMaxGroupIndex_; k++ )
        {
            p_cf_dgSample = this->activateGroupUserData( k );

            // Protect ourselves from bogus access requests:
            if ( p_cf_dgSample->numItems() >= j )
            {
                // Sample the item:
                p_cf_xyzSample = p_cf_dgSample->activateXYZValAt( j );
                dSampleAtIndex = p_cf_xyzSample->y();

                // Will the sample contribute to the sum?
                if ( dSampleAtIndex > 0 )
                    dSumAtIndex += dSampleAtIndex;
            }
        }
        // Hey!  Is this a max sum value?
        if ( dSumAtIndex > dReturnVal )
            dReturnVal = dSumAtIndex;
    }

    // Now consider any USER-specified candidates for the max:
    if ( bRangeMaxLimitUsesCand_ == TRUE )
        if ( dCandidateRangeMaxLimit_ > dReturnVal )
            dReturnVal = dCandidateRangeMaxLimit_;

    // Add any current amount of range buffering:
    dReturnVal += dBufferRangeMaxLimitBy_;
    if ( bRangeMaxLimitUsesCeil_ ) dReturnVal = ceil( dReturnVal );

    return dReturnVal;
}


double
    cf_ChartModel
        ::dataYValSumsMin()
{
    double  dTestVal = 0.0;
    double  dReturnVal = p_cf_dgRangeValues_->dataYMin();
            // First *candidate* is the data-range group min value

    // Determine the number of domain items we will sample:
    int iNumItemsInGroup = 0;
    int iMaxNumItemsInAnyGroup = 0;
    for ( i = iMinGroupIndex_; i <= iMaxGroupIndex_; i++ )
    {
        iNumItemsInGroup = ( (cf_DataGroup*)
                             da_p_cf_dgDataGroups_[ i ] )->numItems();
        if ( iNumItemsInGroup > iMaxNumItemsInAnyGroup )
            iMaxNumItemsInAnyGroup = iNumItemsInGroup;
    }

    // Cycle through the domain item indexes and sum the range vals:
    cf_DataGroup    *p_cf_dgSample = NULL;
    cf_XYZ          *p_cf_xyzSample = NULL;
    double      dSampleAtIndex = 0.0;
    double      dSumAtIndex = 0.0;
    for ( j = 1; j <= iMaxNumItemsInAnyGroup; j++ )
    {
        dSumAtIndex = 0.0;
        for ( k = iMinGroupIndex_; k <= iMaxGroupIndex_; k++ )
        {
            p_cf_dgSample = this->activateGroupUserData( k );

            // Protect ourselves from bogus access requests:
            if ( p_cf_dgSample->numItems() >= j )
            {
                // Sample the item:
                p_cf_xyzSample = p_cf_dgSample->activateXYZValAt( j );
                dSampleAtIndex = p_cf_xyzSample->y();

                // Will the sample contribute to the sum?
                if ( dSampleAtIndex < 0 )
                    dSumAtIndex += dSampleAtIndex;
            }
        }
        // Hey!  Is this a min sum value?
        if ( dSumAtIndex < dReturnVal )
            dReturnVal = dSumAtIndex;
    }

    // Now consider any USER-specified candidates for the min:
    if ( bRangeMinLimitUsesCand_ == TRUE )
        if ( dCandidateRangeMinLimit_ < dReturnVal )
            dReturnVal = dCandidateRangeMinLimit_;

    // Add any current amount of range buffering:
    dReturnVal -= dBufferRangeMinLimitBy_;
    if ( bRangeMinLimitUsesFloor_ ) dReturnVal = floor( dReturnVal );

    return dReturnVal;
}

double
    cf_ChartModel
        ::dataYValSumsExtent()
        { return ( this->dataYValSumsMax() - this->dataYValSumsMin() ); }

double
    cf_ChartModel
        ::dataZGroupsMax()
{
/*
    double  dTestVal = 0.0;
    double  dReturnVal = ( (cf_DataGroup*)
                            da_p_cf_dgDataGroups_
                                    [ iMinGroupIndex_ ] )->dataZMax();

    for ( i = iMinGroupIndex_ + 1; i <= iMaxGroupIndex_; i++ )
    {
        dTestVal = ( (cf_DataGroup*)
                     da_p_cf_dgDataGroups_[ i ] )->dataZMax();
        if ( dTestVal > dReturnVal ) dReturnVal = dTestVal;
    }
    return dReturnVal;
 */
 return( (double)( iMaxGroupIndex_ ) );
}

double
    cf_ChartModel
        ::dataZGroupsMin()
{
/*
    double  dTestVal = 0.0;
    double  dReturnVal = ( (cf_DataGroup*)
                           da_p_cf_dgDataGroups_
                                    [ iMinGroupIndex_ ] )->dataZMin();

    for ( i = iMinGroupIndex_ + 1; i <= iMaxGroupIndex_; i++ )
    {
        dTestVal = ( (cf_DataGroup*)
                     da_p_cf_dgDataGroups_[ i ] )->dataZMin();
        if ( dTestVal < dReturnVal ) dReturnVal = dTestVal;
    }
    return dReturnVal;
 */
 //return( (double)( iMinGroupIndex_ ) );
 return 0.0;
}

double
    cf_ChartModel
        ::dataZGroupsExtent()
            { return ( this->dataZGroupsMax() - this->dataZGroupsMin() ); }






cf_DataGroup*
cf_ChartModel::labelAxis
                (   const char      *p_strNewLabel,
                    cf_ChartAxis    enumChartAxisSelector,
                    double          dPercentFromXCenterCoord,
                    double          dPercentFromYCenterCoord,
                    double          dPercentFromZCenterCoord )
{
    cf_DataGroup  *p_cf_dgTargetGroup
                    = activateGroupAxisLabel( enumChartAxisSelector );

    // Setup the data defining the text-marker's head/tail/text points:
    p_cf_dgTargetGroup->reset();
    for ( int i = 1; i <= 3; i++ )
        p_cf_dgTargetGroup->addXYZVal(  dPercentFromXCenterCoord,
                                        dPercentFromYCenterCoord,
                                        dPercentFromZCenterCoord );

    // Setup default display attributes for the new group:
    p_cf_dgTargetGroup->labelNew( p_strNewLabel );
            // Note: the *Brush* color will become the *text* color!

    // If the chart engine asks, tell it we have new data...
    bUpdatedChartData_ = TRUE;

    return( p_cf_dgTargetGroup );
}



cf_DataGroup*
cf_ChartModel::labelAxisData( cf_ChartAxis   enumChartAxisSelector )
{
    cf_DataGroup  *p_cf_dgResult
                    = activateGroupAxisLabel( enumChartAxisSelector );

    return( p_cf_dgResult );
}


/*
BOOL
cf_ChartModel::labelAxisIs( cf_ChartAxis   enumChartAxisSelector )
{
    BOOL    bReturnVal = FALSE;
    switch ( enumChartAxisSelector )
    {
        case    CF_AXIS_X_BOTTOM:
                if ( p_cf_dgLabelAxisXBottom_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_X_BACK:
                if ( p_cf_dgLabelAxisXBack_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_Y_LEFT:
                if ( p_cf_dgLabelAxisYLeft_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_Y_BACK:
                if ( p_cf_dgLabelAxisYBack_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_Z_BOTTOM:
                if ( p_cf_dgLabelAxisZBottom_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_Z_LEFT:
                if ( p_cf_dgLabelAxisZBack_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_AXIS_POLAR_FLOOR:
                if ( p_cf_dgLabelAxisPolarFloor_ != NULL ) bReturnVal = TRUE;
                break;

        default:
                break;
    }
    return( bReturnVal );
}
*/



void
cf_ChartModel::labelAxesReset()
{
    // Our axis-label data-groups either were allocated or are == NULL...
    delete  p_cf_dgLabelAxisXBottom_;
    delete  p_cf_dgLabelAxisXBack_;
    delete  p_cf_dgLabelAxisYLeft_;
    delete  p_cf_dgLabelAxisYBack_;
    delete  p_cf_dgLabelAxisZBottom_;
    delete  p_cf_dgLabelAxisZBack_;
    delete  p_cf_dgLabelAxisPolarFloor_;
            //... and in any case safe to do a C++ delete op.

    p_cf_dgLabelAxisXBottom_ = NULL;
    p_cf_dgLabelAxisXBack_ = NULL;
    p_cf_dgLabelAxisYLeft_ = NULL;
    p_cf_dgLabelAxisYBack_ = NULL;
    p_cf_dgLabelAxisZBottom_ = NULL;
    p_cf_dgLabelAxisZBack_ = NULL;
    p_cf_dgLabelAxisPolarFloor_ = NULL;
        // These are now reset to a testable state.
}



cf_DataGroup*
cf_ChartModel::labelLegendData
                ( int iAtIndex )
{
    cf_DataGroup  *p_cf_dgReturnVal = activateGroupLegend( iAtIndex );
    return( p_cf_dgReturnVal );
}


cf_DataGroup*
cf_ChartModel::labelLegendNew
                (   const char  *p_strLegendLabel,
                    double      dPercentFromXCenterCoord,
                    double      dPercentFromYCenterCoord,
                    double      dPercentFromZCenterCoord )
{
    // Set up a new data-group to hold our legend-data:
    cf_DataGroup *p_cf_dgNewLegend = new cf_DataGroup( p_strLegendLabel );
    iNumAllocatedLegendMarkers_ ++;
    da_p_cf_dgLegendMarkers_[ iNumAllocatedLegendMarkers_ ] = p_cf_dgNewLegend;

    // Setup display-attribs for this new group:
    cf_DispAttribs  *p_cf_datbNewLegend = p_cf_dgNewLegend->dispAttribs();
    p_cf_datbNewLegend->colorPen( CF_BLACK );// a good default choice
    p_cf_datbNewLegend->colorBrush( paletteColor( iNumAllocatedLegendMarkers_ ));

    cf_TextAttribs  *p_cf_tatbNewLegend = p_cf_dgNewLegend->textAttribs();
    p_cf_tatbNewLegend->shiftHorzRight();
    p_cf_tatbNewLegend->colorTextFore( p_cf_datbNewLegend->colorBrush() );
                // Note: the *Brush* color will become the *text* color!

    // Setup the data defining the text-marker's head/tail/text points:
    for ( int i = 1; i <= 3; i++ )
        p_cf_dgNewLegend->addXYZVal(    dPercentFromXCenterCoord,
                                        dPercentFromYCenterCoord,
                                        dPercentFromZCenterCoord );

    // If the chart engine asks, tell it we have new data...
    bUpdatedChartData_ = TRUE;

    return( p_cf_dgNewLegend );
}


int
cf_ChartModel::labelLegendCount()
{
    return( iNumAllocatedLegendMarkers_ );
}


void
cf_ChartModel::labelLegendsReset()
{
    for ( i = 1; i <= iNumAllocatedLegendMarkers_; i++ )
        delete ( (cf_DataGroup*) da_p_cf_dgLegendMarkers_[ i ] );

    iNumAllocatedLegendMarkers_ = 0;
}



cf_DataGroup*
cf_ChartModel::labelTitle
                (   const char      *p_strNewLabel,
                    cf_ChartTitle   enumChartTitleSelector,
                    double          dPercentFromXCenterCoord,
                    double          dPercentFromYCenterCoord,
                    double          dPercentFromZCenterCoord )
{
    cf_DataGroup  *p_cf_dgTargetGroup
                    = activateGroupTitle( enumChartTitleSelector );

    // Setup the data defining the text-marker's head/tail/text points:
    p_cf_dgTargetGroup->reset();
    for ( int i = 1; i <= 3; i++ )
        p_cf_dgTargetGroup->addXYZVal(  dPercentFromXCenterCoord,
                                        dPercentFromYCenterCoord,
                                        dPercentFromZCenterCoord );

    // Setup default display-attribs for this new group:
    p_cf_dgTargetGroup->labelNew( p_strNewLabel );
            // Note: the *Brush* color will become the *text* color!

    // If the chart engine asks, tell it we have new data...
    bUpdatedChartData_ = TRUE;

    return( p_cf_dgTargetGroup );
}



cf_DataGroup*
cf_ChartModel::labelTitleData( cf_ChartTitle   enumChartTitleSelector )
{
    cf_DataGroup  *p_cf_dgResult
                    = activateGroupTitle( enumChartTitleSelector );
    return( p_cf_dgResult );
}



/*
BOOL
cf_ChartModel::labelTitleIs( cf_ChartTitle enumChartTitleSelector )
{
    BOOL    bReturnVal = FALSE;
    switch ( enumChartTitleSelector )
    {
        case    CF_TITLE_SUB:
                if ( p_cf_dgLabelTitleSub_ != NULL ) bReturnVal = TRUE;
                break;

        case    CF_TITLE_MAIN:
                if ( p_cf_dgLabelTitleMain_ != NULL ) bReturnVal = TRUE;
                break;

        default:
                break;
    }
    return( bReturnVal );
}
*/


void
cf_ChartModel::labelTitlesReset()
{
    delete p_cf_dgLabelTitleMain_;
    p_cf_dgLabelTitleMain_ = NULL;

    delete p_cf_dgLabelTitleSub_;
    p_cf_dgLabelTitleSub_ = NULL;
}






//
//=================================================================
//
// NOTE that the chart-model member method:
//
//  void
//      cf_ChartModel
//          ::modelEngine(  int     iChartTypeID,
//                          int     iIndexMethodID,
//                          BOOL    bDeleteOldEngine )
//
// Is implemented in ONE of these SEPARATE source files --
// (depending upon the version of the ChartFolio Framework):
// "c2_cmodl.cpp", or "c3_cmodl.cpp".
//
//=================================================================
//



bf_Color
    cf_ChartModel
        ::paletteColor( int iModIntoAvailColors )
{
    bf_Color   bf_clrReturnThisVal;

    if ( iModIntoAvailColors > 0 )
        // Return the color value...
        bf_clrReturnThisVal
            = a_bf_clrChartPalette_
                //...at this index...
                [ (iModIntoAvailColors - 1) % C1_CMODL_NUM_PALETTE_COLORS ];
    else
        bf_clrReturnThisVal =  CF_BLACK;

    return bf_clrReturnThisVal;
}


void
    cf_ChartModel
        ::paletteColor( int     iModIntoAvailColors,
                        bf_Color   bf_clrNew )
{
    // Screen for bogus index values:
    if ( iModIntoAvailColors > 0 )
        a_bf_clrChartPalette_
            // The color at this index...
            [ (iModIntoAvailColors - 1) % C1_CMODL_NUM_PALETTE_COLORS ]
                    = bf_clrNew;
                    // gets updated w/ this value.
}


void
    cf_ChartModel
        ::paletteColors(    bf_Color bf_clr01,
                            bf_Color bf_clr02,
                            bf_Color bf_clr03,
                            bf_Color bf_clr04,
                            bf_Color bf_clr05,
                            bf_Color bf_clr06,
                            bf_Color bf_clr07,
                            bf_Color bf_clr08,
                            bf_Color bf_clr09,
                            bf_Color bf_clr10,
                            bf_Color bf_clr11,
                            bf_Color bf_clr12,
                            bf_Color bf_clr13,
                            bf_Color bf_clr14,
                            bf_Color bf_clr15,
                            bf_Color bf_clr16 )
{
    a_bf_clrChartPalette_[ 0 ] = bf_clr01;
    a_bf_clrChartPalette_[ 1 ] = bf_clr02;
    a_bf_clrChartPalette_[ 2 ] = bf_clr03;
    a_bf_clrChartPalette_[ 3 ] = bf_clr04;
    a_bf_clrChartPalette_[ 4 ] = bf_clr05;
    a_bf_clrChartPalette_[ 5 ] = bf_clr06;
    a_bf_clrChartPalette_[ 6 ] = bf_clr07;
    a_bf_clrChartPalette_[ 7 ] = bf_clr08;
    a_bf_clrChartPalette_[ 8 ] = bf_clr09;
    a_bf_clrChartPalette_[ 9 ] = bf_clr10;
    a_bf_clrChartPalette_[ 10 ] = bf_clr11;
    a_bf_clrChartPalette_[ 11 ] = bf_clr12;
    a_bf_clrChartPalette_[ 12 ] = bf_clr13;
    a_bf_clrChartPalette_[ 13 ] = bf_clr14;
    a_bf_clrChartPalette_[ 14 ] = bf_clr15;
    a_bf_clrChartPalette_[ 15 ] = bf_clr16;
    // See the hpp file for default init settings...
}



bf_BrushHatch
    cf_ChartModel
        ::paletteBrushHatch( int iModIntoAvailHatches )
{
    bf_BrushHatch  bf_bhatReturnThisVal;

    if ( iModIntoAvailHatches > 0 )
        // Return the brush-hatch value...
        bf_bhatReturnThisVal
            = a_bf_bhatChartPalette_
                //...at this index...
                [ (iModIntoAvailHatches - 1) % C1_CMODL_NUM_BRUSH_HATCHES ];
    else
        bf_bhatReturnThisVal = CF_BHAT_NONE; // try to do no harm...

    return bf_bhatReturnThisVal;
}


void
    cf_ChartModel
        ::paletteBrushHatch(    int     iModIntoAvailHatches,
                                bf_BrushHatch  bf_bhatNew )
{
    // Screen for bogus index values:
    if ( iModIntoAvailHatches > 0 )
        a_bf_bhatChartPalette_
            // The brush-hatch at this index...
            [ (iModIntoAvailHatches - 1) % C1_CMODL_NUM_BRUSH_HATCHES ]
                = bf_bhatNew;
                // gets updated w/ this value.
}


void
    cf_ChartModel
        ::paletteBrushHatches(  bf_BrushHatch bf_bhat01,
                                bf_BrushHatch bf_bhat02,
                                bf_BrushHatch bf_bhat03 )
{
    a_bf_bhatChartPalette_[ 0 ] = bf_bhat01;
    a_bf_bhatChartPalette_[ 1 ] = bf_bhat02;
    a_bf_bhatChartPalette_[ 2 ] = bf_bhat03;
    // See the hpp file for default init settings...
}



//------------------------------------------------------------
// Manipulate 2D/3D Point-Marker Render Attribs of this chart:
//------------------------------------------------------------
BOOL
    cf_ChartModel
        ::paletteMarkers2D3D(   int     i2For2D_3For3D )
{
    BOOL    bStatus = FALSE;

    if ( i2For2D_3For3D == 2 )
        if ( bDraw2DPointMarkers_ == TRUE )
            bStatus = TRUE;

    else if ( i2For2D_3For3D == 3 )
        if ( bDraw3DPointMarkers_ == TRUE )
            bStatus = TRUE;

    return bStatus;
}


void
    cf_ChartModel
        ::paletteMarkers2D3D(   BOOL    bDisp2DMarkers,
                                BOOL    bDisp3DMarkers,
                                BOOL    bResetCurrentGroups )
{
    // Keep track of what we're doing:
    bDraw2DPointMarkers_ = bDisp2DMarkers;
    bDraw3DPointMarkers_ = bDisp3DMarkers;

    // Now update the active groups:
    if ( bResetCurrentGroups == TRUE )
    {
        cf_DispAttribs      *p_cf_datbCurrGroups = NULL;
        for ( i = 1; i <= iNumAllocatedGroups_; i++ )
        {
            p_cf_datbCurrGroups = ( (cf_DataGroup*)
                        da_p_cf_dgDataGroups_[ i ] )->dispAttribs();
            p_cf_datbCurrGroups->markWhen2D( bDisp2DMarkers );
            p_cf_datbCurrGroups->markWhen3D( bDisp3DMarkers );
        }
    }
}



bf_PenStyle
cf_ChartModel::palettePenStyle
            ( int iModIntoAvailStyles )
{
    bf_PenStyle    bf_pstyReturnThisVal;

    if ( iModIntoAvailStyles > 0 )
        // Return the pen-style value...
        bf_pstyReturnThisVal
            = a_bf_pstyChartPalette_
                //...at this index...
                [ (iModIntoAvailStyles - 1) % C1_CMODL_NUM_PEN_STYLES ];
    else
        bf_pstyReturnThisVal = CF_PS_SOLID;  // try to do no harm...

    return bf_pstyReturnThisVal;
}


void
cf_ChartModel::palettePenStyle
            (   int             iModIntoAvailStyles,
                bf_PenStyle     bf_pstyNew )
{
    // Screen for bogus index values:
    if ( iModIntoAvailStyles > 0 )
        a_bf_pstyChartPalette_
            // The pen-style at this index...
            [ (iModIntoAvailStyles - 1) % C1_CMODL_NUM_PEN_STYLES ]
                = bf_pstyNew;
                // ...gets updated w/ this value.
}


void
cf_ChartModel::palettePenStyles
            (   bf_PenStyle bf_psty01,
                bf_PenStyle bf_psty02,
                bf_PenStyle bf_psty03 )
{
    a_bf_pstyChartPalette_[ 0 ] = bf_psty01;
    a_bf_pstyChartPalette_[ 1 ] = bf_psty02;
    a_bf_pstyChartPalette_[ 2 ] = bf_psty03;
    // See the hpp file for default init settings...
}



int
cf_ChartModel::palettePenWidth()
{
    return( iDefaultPenWidth_ );
}

void
cf_ChartModel::palettePenWidth( int iNewWidth )
{
    iDefaultPenWidth_ = iNewWidth;
}





BOOL
cf_ChartModel::updateNeededIs()
{
    return( bUpdatedChartData_ );
}



void
cf_ChartModel::updateViewingEngines
            (   BOOL    bForceDataUpdate,
                BOOL    bForceAutoConfig,
                BOOL    bForceGeomXform,
                BOOL    bForceProjXform,
                BOOL    bForceDispMetrics )
{
    // Protect ourselves from bogosities:
    if ( p_cf_engChartEngine_ == NULL ) return;

    // Do we need to re-generate a new set of chart-engine objects?
    BOOL    bPerformDataUpdate = FALSE;
    if ( bForceDataUpdate == TRUE )  bPerformDataUpdate = TRUE;
    if ( bUpdatedChartData_ == TRUE )  bPerformDataUpdate = TRUE;

    if ( bPerformDataUpdate == TRUE )
    {   //...then do so...
        p_cf_engChartEngine_->applyDataUpdate( this );
        bUpdatedChartData_ = FALSE;
    }

    // Do we need to re-(auto)configure the engine and its objects?
    if ( ( bPerformDataUpdate == TRUE ) || ( bForceAutoConfig == TRUE ) )
        p_cf_engChartEngine_->autoConfigure();

    // Were Geom/Proj/Metric xforms called for, or are they now implied?
    if ( ( bPerformDataUpdate == TRUE ) || ( bForceGeomXform == TRUE ) )
        p_cf_engChartEngine_->applyGeomXform();

    if ( ( bPerformDataUpdate == TRUE ) || ( bForceProjXform == TRUE ) )
        p_cf_engChartEngine_->applyProjXform();

    if ( ( bPerformDataUpdate == TRUE ) || ( bForceDispMetrics == TRUE ) )
        p_cf_engChartEngine_->applyDispMetrics();
}


//
// END of cf_ChartModel Implementation!
//

//////////////////////////////////////
// END of File: C1_CMODL.CPP
//////////////////////////////////////






