//////////////////////////////////////
// START of File: CE1_CCOR.CPP
//////////////////////////////////////

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

// Include this source file's header file:
#include	<cf_api/bf_defs.h>
#include    <cf_api/ce1_ccor.hpp>

// Specify any other file usage:
#include    <math.h>
            // We'll use fabs() functionality below...

cf_eng_CartXYZ
        ::cf_eng_CartXYZ
            (   cf_ChartType    enumChartType,
                cf_DispMode     enumDispMode )

    :   cf_ChartEngine( enumChartType,
                        enumDispMode )

{
    // The "base" engine class uses an origin at the
    // "center" of the 3D Box. These "cartesian XYZ" engines
    // use an origin at the minimal X/Y/Z coords of the 3D-Box:
    measureVirtOrigin();

    // Init the Kernel to a favorable state for cartesian
    // chart views -- like an XY Plot with a little depth:
    preferPerspective( 0.0, FALSE, FALSE );
    preferViewAngle( 20, -30, FALSE, FALSE );
    preferProportions(  1.0,    // Height-to-Width ratio
                        0.5,    // Depth-to-Height ratio
                        TRUE ); // and auto-configure:
}

cf_eng_CartXYZ
    ::~cf_eng_CartXYZ() 
        {}




void
cf_eng_CartXYZ::applyDataUpdate
                ( cf_ChartModel *p_cf_cmodlDataSource )
{
    // Reset the current data-state of the chart-engine:
    cf_ChartEngine::applyDataUpdate( p_cf_cmodlDataSource );
                    // ...by using the base-class shared-functionality..

    // Generate a set of "new" Chart Engine Objects:
    cf_IndexMode    enumCurrIndexMode
                    = p_cf_cmodlDataSource->dataIndexMethod();

    switch ( enumCurrIndexMode )
    {
        case    CF_INDEX_INT_I:

                // Generate Virtual Objects AT Integer I indices:
                updateYValsAtIntIs( p_cf_cmodlDataSource );
                break;

        case    CF_INDEX_NAT_N:
        case    CF_INDEX_USR_X:
        default:
                // Generate Virtual Objects ON "Natural N" indices:
                updateYValsOnNatNs( p_cf_cmodlDataSource );
                break;
    }

    // Generate domain and range axis grid/tick/series objects:
    updateAxisGridTickSeries( p_cf_cmodlDataSource );

    // Generate range axis buffering (by adding "invisible" 3D-points):
    updateAxisRangeBufferPoints( p_cf_cmodlDataSource );
    
}   // end of cf_eng_CartXYZ::applyDataUpdate(...)




void
    cf_eng_CartXYZ
        ::draw2D( bf_Display *p_bf_dspFromChartView ) 
{
    // Render the 3D Box using the base class:
    cf_ChartEngine::draw2D( p_bf_dspFromChartView );
    // Process the Face Component List ( tick marks, etc):
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                                ->draw2D( p_bf_dspFromChartView );
    // Process the Face Object List ( control lines, etc):
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                                ->draw2D( p_bf_dspFromChartView );
    // Process the Space Object List ( ribbons, etc):
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                                ->draw2D( p_bf_dspFromChartView );
    // Process the Chart Marker List ( ribbons, etc):
    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
                                ->draw2D( p_bf_dspFromChartView );
}



void
    cf_eng_CartXYZ
        ::draw3D( bf_Display *p_bf_dspFromChartView ) 
{
    // Render the 3D Box using the base class:
    cf_ChartEngine::draw3D( p_bf_dspFromChartView );

    // Process the Face Component List ( tick marks, etc):
    for ( i = 1; i <= iNumCompoObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objCompoObjects_[ i ] )
                                ->draw3D( p_bf_dspFromChartView );
    // Process the Face Object List ( control lines, etc):
    for ( i = 1; i <= iNumFaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objFaceObjects_[ i ] )
                                ->draw3D( p_bf_dspFromChartView );
    // Process the Space Object List ( ribbons, etc):
    for ( i = 1; i <= iNumSpaceObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objSpaceObjects_[ i ] )
                                ->draw3D( p_bf_dspFromChartView );
    // Process the Chart Marker List ( ribbons, etc):
    for ( i = 1; i <= iNumMarkerObjects_; i++ )
        ( (cf_Virt3DObj*) da_p_cf_objMarkerObjects_[ i ] )
                                ->draw3D( p_bf_dspFromChartView );
}



void
    cf_eng_CartXYZ
        ::initGridTickSeries()
{
    // Activate a Target "Domain" Axis:
    cf_3DGridTickSeries *p_cf_3dgtsTargetDomain = axis_X_BottomFace();
    p_cf_3dgtsTargetDomain->on();
    p_cf_3dgtsTargetDomain->useDomainItems( TRUE );
    p_cf_3dgtsTargetDomain->freqTextsToTicks( 1 );
    p_cf_3dgtsTargetDomain->freqGridsToTicks( 1 );

    // Activate a Target "Range" Axis:
    cf_3DGridTickSeries *p_cf_3dgtsTargetRange = axis_Y_LeftFace();
    p_cf_3dgtsTargetRange->on();
    p_cf_3dgtsTargetRange->useDataRange( TRUE );
    p_cf_3dgtsTargetRange->useRangeValues( TRUE );
                        // We want to tick across the data -- AND --
                        // have user range ticks show up as well...

    p_cf_3dgtsTargetRange->freqTextsToTicks( 2 );
    p_cf_3dgtsTargetRange->freqGridsToTicks( 2 );
}




void
cf_eng_CartXYZ::measureVirtMinMax()
{
    // Declare vars used to Determine the Min/Max X, Y, & Z
    // DATA vals in this cf_ChartModel by "scanning over" the data
    // values of this cf_ChartModel's objects:
    double  dXMin = p_cf_kEnginesKernel_->getXMin();
    double  dYMin = p_cf_kEnginesKernel_->getYMin();
    double  dZMin = p_cf_kEnginesKernel_->getZMin();

    double  dXMax = p_cf_kEnginesKernel_->getXMax();
    double  dYMax = p_cf_kEnginesKernel_->getYMax();
    double  dZMax = p_cf_kEnginesKernel_->getZMax();
            // Before we start our scan, set some harmless
            // default.  If we have no data objects to scan,
            // these will remain the min/max values...

    //---------------------------------------------------------
    // Otherwise, start by scanning the chart space objects:
    //---------------------------------------------------------
    cf_Virt3DObj    *p_cf_objDefault = NULL;
    cf_Virt3DObj    *p_cf_objTestVal = NULL;

    if ( iNumSpaceObjects_ >= 1 )
    {
        p_cf_objDefault = ( (cf_Virt3DObj*)
                            da_p_cf_objSpaceObjects_[ 1 ] );

        dXMin = p_cf_objDefault->virtXMin();
        dYMin = p_cf_objDefault->virtYMin();
        dZMin = p_cf_objDefault->virtZMin();

        dXMax = p_cf_objDefault->virtXMax();
        dYMax = p_cf_objDefault->virtYMax();
        dZMax = p_cf_objDefault->virtZMax();
    }   // Min/Max vals DEFAULT to the Min/Max of the 1st space object.

    for ( i = 2; i <= iNumSpaceObjects_; i++ )
    {
        p_cf_objTestVal = ( (cf_Virt3DObj*)
                            da_p_cf_objSpaceObjects_[ i ] );

        // Look for an X or Y or Z Minima
        if ( p_cf_objTestVal->virtXMin() < dXMin )
                dXMin = p_cf_objTestVal->virtXMin();
        if ( p_cf_objTestVal->virtYMin() < dYMin )
                dYMin = p_cf_objTestVal->virtYMin();
        if ( p_cf_objTestVal->virtZMin() < dZMin )
                dZMin = p_cf_objTestVal->virtZMin();

        // Look for an X or Y or Z Maxima
        if ( p_cf_objTestVal->virtXMax() > dXMax )
                dXMax = p_cf_objTestVal->virtXMax();
        if ( p_cf_objTestVal->virtYMax() > dYMax )
                dYMax = p_cf_objTestVal->virtYMax();
        if ( p_cf_objTestVal->virtZMax() > dZMax )
                dZMax = p_cf_objTestVal->virtZMax();

    }   // End of scanning the SpaceObjects for Data Min/Max vals.

    //----------------------------------------------
    // Next, scan the chart face objects:
    //----------------------------------------------
    for ( i = 1; i <= iNumFaceObjects_; i++ )
    {
        // Is the first face object the SOLE object in this engine?
        if ( i == 1 )
        {   // IF there are no space objects to used for default values:
            if ( ( iNumSpaceObjects_ < 1 ) && ( iNumFaceObjects_ >= 1 ) )
            {   // ...let the Min & Max vals be initially set to
                // the Min & Max of the 1st face object:
                p_cf_objDefault = ( (cf_Virt3DObj*)
                                    da_p_cf_objFaceObjects_[ 1 ] );

                dXMin = p_cf_objDefault->virtXMin();
                dYMin = p_cf_objDefault->virtYMin();
                dZMin = p_cf_objDefault->virtZMin();

                dXMax = p_cf_objDefault->virtXMax();
                dYMax = p_cf_objDefault->virtYMax();
                dZMax = p_cf_objDefault->virtZMax();
            }
        }   // end of special consideration for first face object.

        p_cf_objTestVal = ( (cf_Virt3DObj*)
                            da_p_cf_objFaceObjects_[ i ] );

        // Otherwise, continue to look for X or Y or Z Minima
        if ( p_cf_objTestVal->virtXMin() < dXMin )
                dXMin = p_cf_objTestVal->virtXMin();
        if ( p_cf_objTestVal->virtYMin() < dYMin )
                dYMin = p_cf_objTestVal->virtYMin();
        if ( p_cf_objTestVal->virtZMin() < dZMin )
                dZMin = p_cf_objTestVal->virtZMin();

        // ...and X or Y or Z Maxima
        if ( p_cf_objTestVal->virtXMax() > dXMax )
                dXMax = p_cf_objTestVal->virtXMax();
        if ( p_cf_objTestVal->virtYMax() > dYMax )
                dYMax = p_cf_objTestVal->virtYMax();
        if ( p_cf_objTestVal->virtZMax() > dZMax )
                dZMax = p_cf_objTestVal->virtZMax();

    }   // End of scanning the FaceObject array for Data Min/Max vals.

    //-------------------------------------------------------
    // After the scan(s), update this cf_ChartModel Engine's
    // X, Y & Z Min/Max "virtual" DATA Vars:
    //-------------------------------------------------------
    dVirtXRangeMin_ = dXMin;
    dVirtXRangeMax_ = dXMax;
    dVirtYRangeMin_ = dYMin;
    dVirtYRangeMax_ = dYMax;
    dVirtZRangeMin_ = dZMin;
    dVirtZRangeMax_ = dZMax;
        // This is the FIRST approximation...  Now make updates...
        // We'll move from scanning the actual virtual data
        // to determining the total RANGE of the virtual data...

    // Next approx. in/ex-cludes origins, -- as appropriate...
    // Start by considering the X-Origin:
    BOOL    bIncludeXAxisOrigin = FALSE;    // Tentatively...

    if ( ( dChartXRangeMin_ <= 0.0 ) && ( dChartXRangeMax_ >= 0.0 ) )
        bIncludeXAxisOrigin = TRUE;

    if ( bIncludeXAxisOrigin == TRUE )
    {   // Deal with virtual data sets that lie
        // entirely RIGHT OF the X-Origin...
        if ( dChartXRangeMin_ >= 0.0 )
            // By updating the Virt-Range-Min to be ZERO -- if needed.
            if ( dChartXOrigin_ < dVirtXRangeMin_ )
                dVirtXRangeMin_ = dChartXOrigin_;
        // Deal with virtual data that lies RIGHT OF the X-Origin...
        if ( dChartXRangeMax_ <= 0.0 )
            // By updating the Virt-Range-Max to be ZERO -- if needed.
            if ( dChartXOrigin_ > dVirtXRangeMax_ )
                dVirtXRangeMax_ = dChartXOrigin_;
    }

    // Next consider the Y-Origin:
    BOOL    bIncludeYAxisOrigin = FALSE;    // Tentatively...

    if ( ( dChartYRangeMin_ <= 0.0 ) && ( dChartYRangeMax_ >= 0.0 ) )
        bIncludeYAxisOrigin = TRUE;

    if ( bIncludeYAxisOrigin == TRUE )
    {   // Deal with virtual data that lies ABOVE the Y-Origin...
        if ( dChartYRangeMin_ >= 0.0 )
            // By updating the Virt-Range-Min to be ZERO -- if needed.
            if ( dChartYOrigin_ < dVirtYRangeMin_ )
                dVirtYRangeMin_ = dChartYOrigin_;
        // Deal with virtual data that lies BELOW the Y-Origin...
        if ( dChartYRangeMax_ <= 0.0 )
            // By updating the Virt-Range-Max to be ZERO -- if needed.
            if ( dChartYOrigin_ > dVirtYRangeMax_ )
                dVirtYRangeMax_ = dChartYOrigin_;
    }

    // Finally consider the Z-Origin:
    BOOL    bIncludeZAxisOrigin = FALSE;    // Tentatively...

    if ( ( dChartZRangeMin_ <= 0.0 ) && ( dChartZRangeMax_ >= 0.0 ) )
        bIncludeZAxisOrigin = TRUE;

    if ( bIncludeZAxisOrigin == TRUE )
    {   // Deal with virtual data that lies ABOVE the Y-Origin...
        if ( dChartZRangeMin_ >= 0.0 )
            // By updating the Virt-Range-Min to be ZERO -- if needed.
            if ( dChartZOrigin_ < dVirtZRangeMin_ )
                dVirtZRangeMin_ = dChartZOrigin_;
        // Deal with virtual data that lies BELOW the Y-Origin...
        if ( dChartZRangeMax_ <= 0.0 )
            // By updating the Virt-Range-Max to be ZERO -- if needed.
            if ( dChartZOrigin_ > dVirtZRangeMax_ )
                dVirtZRangeMax_ = dChartZOrigin_;
    }

    return; // VIRTUAL data/ranges of this engine have now been set,
            // with consideration to data and origin inclusions.

}   // end of cf_eng_CartXYZ::measureVirtMinMax(...)

   


void
cf_eng_CartXYZ::measureVirtOrigin()
{
    // 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 );
    }

}   // end of cf_eng_CartXYZ::measureVirtOrigin(...)



// END of cf_eng_CartXYZ Implementation

//////////////////////////////////////
// END of File: CE1_CCOR.CPP
//////////////////////////////////////





