//////////////////////////////////////
// START of File: CE2_STAC.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:
// and any additional neccessary include statements.
//
#include	<cf_api/bf_defs.h>
#include    <cf_api/ce2_stac.hpp> 

class       cf_XYZ;
class       cf_DataGroup;
#include    <cf_api/c1_dgrou.hpp>
            // We'll access the data groups/items in our Chart Model..

class       cf_3DPoint;
class       cf_3DPolygon;
#include    <cf_api/co1_pgon.hpp>
            // We'll build 2D Polygons for the 2D Stacks...
    
//
//==========================================
// Implement class cf_eng_Stack2D:
//==========================================
//
//---------------------------------
// Implement class cf_eng_Stack2D:
//---------------------------------
cf_eng_Stack2D::cf_eng_Stack2D()

    :   cf_eng_CartXYZ( CF_TYPE_STACK, CF_DISP_2D )

{
    disp2D3DMode( CF_DISP_2D );           // Make it a 2D Chart!...

    // The BASE chart engine class will have instantiated
    // ALL the possible Grid/Tick Series to NULL...
    // The DERIVED chart engine classes MUST set 
    // the series that they specifically want to be ON!...
    this->initGridTickSeries();
            // cf_engCartXYZ-derived charts can use either
            // the "default" XYZ/Cartesian method, which
            // activates the BOTTOM X-Axis as a "domain"
            // axis, and the LEFT Y-Axis as a "range over
            // chart model Y-Vals" axis, or define their
            // OWN version to over-ride the virtual XYZ/
            // Cartesian method...
}

cf_eng_Stack2D
    ::~cf_eng_Stack2D() 
        {}

void
    cf_eng_Stack2D
        ::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 );
                        // Set attributes for "auto-update" behavior

    // Activate a Target "Range" Axis:
    cf_3DGridTickSeries *p_cf_3dgtsTargetRange = axis_Y_LeftFace();
    p_cf_3dgtsTargetRange->on();
    p_cf_3dgtsTargetRange->useDataRangeSums( TRUE );
    p_cf_3dgtsTargetRange->useRangeValSums( 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 );
                        // Set attributes for "auto-update" behavior
}   





void    
cf_eng_Stack2D::updateYValsOnNatNs
                ( cf_ChartModel *p_cf_cmodlDataSource )
//
//==============================================
// OK -- the PLAN is the SAME as for 
// BAR Charts -- Just "turned sideways" !!!:
//==============================================
//
/*        

     (Range)
           |
           |   &
           |   &
           |   *
           |   *   &  
           |   +   *   
           |___+___+__[ ... ] ___(Domain)
           |   |   |           |
           0   1   2           N

        ( continued on Natural N Indices)
         
 */
{
    // We will pull data from the chart with these vars:
    cf_DataGroup    *p_cf_dgAccessGroup = NULL;
    cf_XYZ          *p_cf_xyzAccessItem = NULL;
    
    double      dUserYVal = 0.0; 
                    // We will sum individual user Y-vals...

    double      dUserYValSumPos = 0.0;
    double      dUserYValSumNeg = 0.0;
                    // ...into a running total keeping 
                    // track of pos/neg stack height...

    double      dUserYValYMax = 0.0; 
    double      dUserYValYMin = 0.0;
                    // ...that any individual Y val will
                    // get stacked onto OR below... 


    // We will generate "Virtual 2D Bar Points/Polygons" 
    // for each of the Chart's Data Items with these vars:
    cf_3DPoint      cf_3dpTemp( 0.0, 0.0, 0.0 );
    cf_3DPolygon    *p_cf_3dpgNewGon = NULL;
                    // After new()-ed and configured, this storage
                    // will get passed to our dyna-array of ptrs...

    // Count the number of Active Groups:
    int     iStartAt = p_cf_cmodlDataSource->dataGroupsStartAt();
    int     iEndAt = p_cf_cmodlDataSource->dataGroupsEndAt();
    int     iNumGroups = iEndAt - iStartAt;

    // Calc the space required for each item:
    double  dSpacePerItem = 1.0;
    double  dItemHalfSpace = dSpacePerItem / 2.0;
                // Note that we never div by zero!

    // Compute the max number of axis items to iterate over:
    int iNumActiveInGroup = 0;
    int iNumDomainAxisItems = 0;
    for ( i = iStartAt; i <= iEndAt; i++ )
    {
        p_cf_dgAccessGroup = p_cf_cmodlDataSource->activateGroupUserData( i );
        iNumActiveInGroup = p_cf_dgAccessGroup->numItems();
        if ( iNumActiveInGroup > iNumDomainAxisItems ) 
                iNumDomainAxisItems = iNumActiveInGroup;
    }           // We now have our iteration limit...

    //
    //--------------------------------------------------
    // Loop over each axis index which represents the
    // indexed values from our collection of groups:
    //--------------------------------------------------
    //
    double  dPlaceItemAt = 1.0 - dItemHalfSpace;
                // Set the spot to (start to) lay down items:
                
    // Loop over domain axis items to generate polygon stacks:
    for ( i = 1; i <= iNumDomainAxisItems; i++ )
    {
        // RESETing the column data sum for each new stack
        dUserYValSumPos = 0.0;
        dUserYValSumNeg = 0.0;
        dUserYVal = 0.0;

        // and for-loop over the groups to generate a stack
        for ( j = iStartAt; j <= iEndAt; j++ )  // (D-group indices)
        {
            // Get data values and construct 3D Points that
            // define bar/col/stack vertices:
            p_cf_dgAccessGroup  // Grab a group...
                = p_cf_cmodlDataSource->activateGroupUserData( j );
            p_cf_xyzAccessItem  // Then an item....
                = p_cf_dgAccessGroup->activateXYZValAt( i );
            dUserYVal = p_cf_xyzAccessItem->y();
                // ... whose Y value will define a "height"...

            // Keep track of whether our stack is growing + OR -:
            if ( dUserYVal >= 0.0 )
            {
                dUserYValYMax = dUserYVal + dUserYValSumPos; 
                dUserYValYMin = dUserYValSumPos; 

                // get set to (maybe) add the next stack item...
                dUserYValSumPos += dUserYVal;
            }
            else
            {
                dUserYValYMax = dUserYValSumNeg; 
                dUserYValYMin = dUserYVal + dUserYValSumNeg;
                                    // which will be a negative val

                // get set to (maybe) add the next stack item...
                dUserYValSumNeg += dUserYVal;
            }   // "top of stack" (may be pos OR neg) now incremented...

            // New a polygon -- note that we wait till AFTER we have
            // a data group activated so we can use VALID disp attribs!
            p_cf_3dpgNewGon 
                = new cf_3DPolygon( p_cf_dgAccessGroup->dispAttribs() );
                // (which will become a bar/col/stack/etc):
                
            // Construct the "leading edge":
            cf_3dpTemp.dataXYZ( dPlaceItemAt, 
                                dUserYValYMin, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the left base point is now defined...

            cf_3dpTemp.dataXYZ( dPlaceItemAt, 
                                dUserYValYMax, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the left data (top) point is now defined...

            // Construct the "trailing edge":
            cf_3dpTemp.dataXYZ( dPlaceItemAt + dSpacePerItem, 
                                dUserYValYMax, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the right data (top) point is now defined...

            cf_3dpTemp.dataXYZ( dPlaceItemAt + dSpacePerItem, 
                                dUserYValYMin, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the right base point is now defined...

            this->addSpaceObject( p_cf_3dpgNewGon );

        }   // end of for-loop over Data Groups

        // Place the NEXT stack along the domain axis:
        dPlaceItemAt += dSpacePerItem;
                        // bump along to the next position...

    }   // end of for-loop over iNumCurrentItems

    // Add an "invisible point" to make an appropriate margin/space:
    dPlaceItemAt = (double) iNumDomainAxisItems + 1.0;
    cf_3DPoint *p_cf_3dpAfterLastGon = new cf_3DPoint( dPlaceItemAt, 0.0, 0.0 );
    p_cf_3dpAfterLastGon->dispVisible( FALSE );
    this->addFaceObject( p_cf_3dpAfterLastGon );
        // makes a "trailing" margin matching the "leading" margin.

    return;
    
}   // end of cf_eng_Stack2D::updateYValsOnNatNs()



void    
cf_eng_Stack2D::updateYValsAtIntIs
                ( cf_ChartModel *p_cf_cmodlDataSource )
//
//==============================================
// OK -- the PLAN is the SAME as for 
// BAR Charts -- Just "turned sideways" !!!:
//==============================================
//
/*        
     (Range)
           |
           |&
           |&
           |*
           |* &  
           |+ *   
           |+_+__[ ... ] ___(Domain)
           | | |          |
           0 1 2          I  

        ( continued on Natural I Indices)

 */
{
    // We will pull data from the chart with these vars:
    cf_DataGroup    *p_cf_dgAccessGroup = NULL;
    cf_XYZ          *p_cf_xyzAccessItem = NULL;
    
    double      dUserYVal = 0.0; 
                    // We will sum individual user Y-vals...

    double      dUserYValSumPos = 0.0;
    double      dUserYValSumNeg = 0.0;
                    // ...into a running total keeping 
                    // track of pos/neg stack height...

    double      dUserYValYMax = 0.0; 
    double      dUserYValYMin = 0.0;
                    // ...that any individual Y val will
                    // get stacked onto OR below... 

    // We will generate "Virtual 2D Bar Points/Polygons" 
    // for each of the Chart's Data Items with these vars:
    cf_3DPoint      cf_3dpTemp( 0.0, 0.0, 0.0 );
    cf_3DPolygon    *p_cf_3dpgNewGon = NULL;
                    // After new()-ed and configured, this storage
                    // will get passed to our dyna-array of ptrs...

    // Count the number of Active Groups:
    int     iStartAt = p_cf_cmodlDataSource->dataGroupsStartAt();
    int     iEndAt = p_cf_cmodlDataSource->dataGroupsEndAt();
    int     iNumGroups = iEndAt - iStartAt;

    // Calc the space required for each item:
    double  dSpacePerItem = 1.0;
    double  dItemHalfSpace = dSpacePerItem / 2.0;
                // Note that we never div by zero!

    // Compute the max number of axis items to iterate over:
    int iNumActiveInGroup = 0;
    int iNumDomainAxisItems = 0;
    for ( i = iStartAt; i <= iEndAt; i++ )
    {
        p_cf_dgAccessGroup = p_cf_cmodlDataSource->activateGroupUserData( i );
        iNumActiveInGroup = p_cf_dgAccessGroup->numItems();
        if ( iNumActiveInGroup > iNumDomainAxisItems ) 
                iNumDomainAxisItems = iNumActiveInGroup;
    }           // We now have our iteration limit...

    //
    //--------------------------------------------------
    // Loop over each axis index which represents the
    // indexed values from our collection of groups:
    //--------------------------------------------------
    //
    double  dPlaceItemAt = 0.0;
                // Set the spot to (start to) lay down items:
                
    // Loop over domain axis items to generate polygon stacks:
    for ( i = 1; i <= iNumDomainAxisItems; i++ )
    {
        // RESETing the column data sum for each new stack
        dUserYValSumPos = 0.0;
        dUserYValSumNeg = 0.0;
        dUserYVal = 0.0;

        // and for-loop over the groups to generate a stack
        for ( j = iStartAt; j <= iEndAt; j++ )  // (D-group indices)
        {
            // Get data values and construct 3D Points that
            // define bar/col/stack vertices:
            p_cf_dgAccessGroup  // Grab a group...
                = p_cf_cmodlDataSource->activateGroupUserData( j );
            p_cf_xyzAccessItem  // Then an item....
                = p_cf_dgAccessGroup->activateXYZValAt( i );
            dUserYVal = p_cf_xyzAccessItem->y();
                // ... whose Y value will define a "length/height"...

            // Keep track of whether our stack is growing + OR -:
            if ( dUserYVal >= 0.0 )
            {
                dUserYValYMax = dUserYVal + dUserYValSumPos; 
                dUserYValYMin = dUserYValSumPos; 

                // get set to (maybe) add the next stack item...
                dUserYValSumPos += dUserYVal;
            }
            else
            {
                dUserYValYMax = dUserYValSumNeg; 
                dUserYValYMin = dUserYVal + dUserYValSumNeg;
                                    // which will be a negative val

                // get set to (maybe) add the next stack item...
                dUserYValSumNeg += dUserYVal;
            }   // "top of stack" (may be pos OR neg) now incremented...


            // New a polygon -- note that we wait till AFTER we have
            // a data group activated so we can use VALID disp attribs!
            p_cf_3dpgNewGon 
                = new cf_3DPolygon( p_cf_dgAccessGroup->dispAttribs() );
                // (which will become a bar/col/stack/etc):
                
            // Construct the "leading edge":
            cf_3dpTemp.dataXYZ( dPlaceItemAt, 
                                dUserYValYMin, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the left base point is now defined...

            cf_3dpTemp.dataXYZ( dPlaceItemAt, 
                                dUserYValYMax, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the left data (top) point is now defined...

            
            // Construct the "trailing edge":
            cf_3dpTemp.dataXYZ( dPlaceItemAt + dSpacePerItem, 
                                dUserYValYMax, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the right data (top) point is now defined...

            cf_3dpTemp.dataXYZ( dPlaceItemAt + dSpacePerItem, 
                                dUserYValYMin, 
                                0 );
            p_cf_3dpgNewGon->addPoint( cf_3dpTemp );
                    // the right base point is now defined...

            this->addSpaceObject( p_cf_3dpgNewGon );

        }   // end of for-loop over Data Groups

        // Get set to place the NEXT stack along the domain axis:
        dPlaceItemAt += dSpacePerItem;
                        // bump along to the next position...

    }   // end of for-loop over iNumCurrentItems

    return;

}   // end of cf_eng_Stack2D::updateYValsAtIntIs()

//
// END of class cf_eng_Stack2D Implementation
//

//////////////////////////////////////
// END of File: CE2_STAC.CPP
//////////////////////////////////////

