//////////////////////////////////////
// START of File: CO1_PSER.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/co1_pser.hpp> 
            // relative to the IDE's (global) include path: ~/cfrz

//
//=========================================================
// Implement the Chart Framework "3D Point Series" class.  
//=========================================================
//

cf_3DPointSeries
    ::cf_3DPointSeries()
        : cf_Virt3DObj(),    // Default C-tor:
            cf_3dpRenderAttribs_()
                // The render-attrib point is a default 3D-point at (0,0,0)
                // Note that we will set THIS point to auto-scale below...
{
    // Init the "Geometry State -- we need to:
    //      Init the Tick-Marking/Data Range info:
    //      Init the Virtual/Scaling Range info:
    //      Init the Geometry (Translation/Scaling) State:
    //      Init the Projection (Screen Coordinate) State:
    this->dDataXMax = this->dDataYMax = this->dDataZMax = 0.0;
    this->dDataXMin = this->dDataYMin = this->dDataZMin = 0.0;
                                    // Data Range info init'ed.
    this->dVirtXMax = this->dVirtYMax = this->dVirtZMax = 0.0;
    this->dVirtXMin = this->dVirtYMin = this->dVirtZMin = 0.0;
                                    // Virt/Scale Range info init'ed.
    this->dTransX = this->dTransY = this->dTransZ = 0.0;
    this->dScaleFacX = this->dScaleFacY = this->dScaleFacZ = 1.0;
    this->dGeomXMax = this->dGeomYMax = this->dGeomZMax = 0.0;
    this->dGeomXMin = this->dGeomYMin = this->dGeomZMin = 0.0;
                                    // Geometry State init'ed.
    this->dScreenXMax = this->dScreenYMax = 0.0;
    this->dScreenXMin = this->dScreenYMin = 0.0;
                                    // Projection State init'ed.

    // Now -- init the dynamic array of points:
    this->da_p_cf_3dpVertices.growBy( 25 );
    this->iNumPoints = 0;

    // Make sure the display attribute point WILL auto-scale!
    cf_3dpRenderAttribs_.preferAutoScale( TRUE );

}   // end of default c-tor


//
// Std c-tor lets user assign display attribs
//
cf_3DPointSeries
    ::cf_3DPointSeries( cf_DispAttribs *p_attrToDrawWith )
        : cf_Virt3DObj( p_attrToDrawWith ),
            cf_3dpRenderAttribs_()
                // The render-attrib point is a default 3D-point at (0,0,0)
                // Note that we will set THIS point to auto-scale below...
{
    // Init the "Geometry State -- we need to:
    //      Init the Tick-Marking/Data Range info:
    //      Init the Virtual/Scaling Range info:
    //      Init the Geometry (Translation/Scaling) State:
    //      Init the Projection (Screen Coordinate) State:
    this->dDataXMax = this->dDataYMax = this->dDataZMax = 0.0;
    this->dDataXMin = this->dDataYMin = this->dDataZMin = 0.0;
                                    // Data Range info init'ed.
    this->dVirtXMax = this->dVirtYMax = this->dVirtZMax = 0.0;
    this->dVirtXMin = this->dVirtYMin = this->dVirtZMin = 0.0;
                                    // Virt/Scale Range info init'ed.
    this->dTransX = this->dTransY = this->dTransZ = 0.0;
    this->dScaleFacX = this->dScaleFacY = this->dScaleFacZ = 1.0;
    this->dGeomXMax = this->dGeomYMax = this->dGeomZMax = 0.0;
    this->dGeomXMin = this->dGeomYMin = this->dGeomZMin = 0.0;
                                    // Geometry State init'ed.
    this->dScreenXMax = this->dScreenYMax = 0.0;
    this->dScreenXMin = this->dScreenYMin = 0.0;
                                    // Projection State init'ed.

    // Now -- init the dynamic array of points:
    this->da_p_cf_3dpVertices.growBy( 25 );
    this->iNumPoints = 0;

    // Make sure the display attribute point WILL auto-scale!
    cf_3dpRenderAttribs_.preferAutoScale( TRUE );

}   // End of std c-tor


cf_3DPointSeries
    ::~cf_3DPointSeries()
{
    for ( int i = 1; i <= this->iNumPoints; i++ )
        delete ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] );
    return;
}



cf_3DPointSeries&
    cf_3DPointSeries
        ::operator=( const cf_3DPointSeries& cr_cf_3dpsSource )
{
    // Copy the essential base member var values:
    cf_Virt3DObj::operator=( cr_cf_3dpsSource );

    // Copy the Drawing Attribute Point:
    this->cf_3dpRenderAttribs_   = cr_cf_3dpsSource.cf_3dpRenderAttribs_;

    // Copy the Geometry State info:
    this->dDataXMax     = cr_cf_3dpsSource.dDataXMax;
    this->dDataYMax     = cr_cf_3dpsSource.dDataYMax;
    this->dDataZMax     = cr_cf_3dpsSource.dDataZMax;
    this->dDataXMin     = cr_cf_3dpsSource.dDataXMin;
    this->dDataYMin     = cr_cf_3dpsSource.dDataYMin;
    this->dDataZMin     = cr_cf_3dpsSource.dDataZMin;
                        // Data Range info copied.
    this->dVirtXMax     = cr_cf_3dpsSource.dVirtXMax;
    this->dVirtYMax     = cr_cf_3dpsSource.dVirtYMax;
    this->dVirtZMax     = cr_cf_3dpsSource.dVirtZMax;
    this->dVirtXMin     = cr_cf_3dpsSource.dVirtXMin;
    this->dVirtYMin     = cr_cf_3dpsSource.dVirtYMin;
    this->dVirtZMin     = cr_cf_3dpsSource.dVirtZMin;
                        // Virt/Scale Range info copied.

    this->dTransX       = cr_cf_3dpsSource.dTransX;
    this->dTransY       = cr_cf_3dpsSource.dTransY;
    this->dTransZ       = cr_cf_3dpsSource.dTransZ;

    this->dScaleFacX    = cr_cf_3dpsSource.dScaleFacX;
    this->dScaleFacY    = cr_cf_3dpsSource.dScaleFacY;
    this->dScaleFacZ    = cr_cf_3dpsSource.dScaleFacZ;

    this->dGeomXMax     = cr_cf_3dpsSource.dGeomXMax;
    this->dGeomYMax     = cr_cf_3dpsSource.dGeomYMax;
    this->dGeomZMax     = cr_cf_3dpsSource.dGeomZMax;
    this->dGeomXMin     = cr_cf_3dpsSource.dGeomXMin;
    this->dGeomYMin     = cr_cf_3dpsSource.dGeomYMin;
    this->dGeomZMin     = cr_cf_3dpsSource.dGeomZMin;
                        // Geometry state copied.

    this->dScreenXMax   = cr_cf_3dpsSource.dScreenXMax;
    this->dScreenYMax   = cr_cf_3dpsSource.dScreenYMax;
    this->dScreenXMin   = cr_cf_3dpsSource.dScreenXMin;
    this->dScreenYMin   = cr_cf_3dpsSource.dScreenYMin;
                        // Projection State copied.

    //---------------------------------------------------------
    // Content-Copy the reference's dynamic array of 3D-Points:
    //---------------------------------------------------------
    int i;
    // First, get rid of any previous local 3D-point allocations:
    for ( i = 1; i <= this->iNumPoints; i++ )
        delete ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] );
    this->iNumPoints = 0;

    // NOW we can content-copy...
    cf_3DPoint  *p_cf_3dpSrc = NULL;
            //...the referenced dynamic array of 3D-points to...
    cf_3DPoint  *p_cf_3dpDest = NULL;
            //...our local dyna array void pointers cast into 3D-points...

    for ( i = 1; i <= cr_cf_3dpsSource.iNumPoints; i++ )
    {
        // src is the referenced dyna-array...
        p_cf_3dpSrc = ( (cf_3DPoint*) ((cf_3DPointSeries&)cr_cf_3dpsSource)
                                               .da_p_cf_3dpVertices[ i ] );
        // dest will be new-ed...
        p_cf_3dpDest = new cf_3DPoint();
        this->da_p_cf_3dpVertices[ i ] = p_cf_3dpDest;
            //...into our local dyna-array of voids cast to 3D-points;

        // so now we can de-ref and copy each pointer.
        *p_cf_3dpDest = *p_cf_3dpSrc;
    }

    // Be sure to keep track of what we've allocated!
    this->iNumPoints = cr_cf_3dpsSource.iNumPoints;

    return *this;

}   // end of cf_3DPointSeries::operator=( cf_3DPointSeries& )


cf_3DPoint*
    cf_3DPointSeries
        ::accessPoint( int iIndex )
{
    cf_3DPoint  *p_cf_3dpReturnVal = NULL;
    if (( iIndex > 0 ) && ( iIndex <=this->iNumPoints ) )
        p_cf_3dpReturnVal = ( (cf_3DPoint*)
                                this->da_p_cf_3dpVertices[ iIndex ] );
    return p_cf_3dpReturnVal;
}


void
    cf_3DPointSeries
        ::addPoint( cf_3DPoint *p_cf_3dpNewPoint )
{
    // ADD this ALREADY NEW()-ed 3D Point into our DYNAMIC ARRAY:
    this->iNumPoints++;
    this->da_p_cf_3dpVertices[ this->iNumPoints ] = p_cf_3dpNewPoint;

    // Set the new point's drawing attributes to ALWAYS match
    // this cf_3DPointSeries's point drawing attributes:
    p_cf_3dpNewPoint->dispMarkerShape( cf_3dpRenderAttribs_.dispMarkerShape() );
    p_cf_3dpNewPoint->dispMarkerSize( cf_3dpRenderAttribs_.dispMarkerSize() );

    // Makes the new point's disp-attributes INACTIVE...
    //...the master display attribute point will control these attribs!
    p_cf_3dpNewPoint->dispIgnorePushPen( TRUE );
    p_cf_3dpNewPoint->dispIgnorePushBrush( TRUE );
                    // The point will NOT need to push a pen/brush!
    p_cf_3dpNewPoint->preferAutoScale( FALSE );
                    // The point will NOT need to auto-scale!

    // See if we should update the Data Range coords:
    if ( this->iNumPoints == 1 )
    {
        this->dDataXMax = p_cf_3dpNewPoint->dataXMax();
        this->dDataXMin = p_cf_3dpNewPoint->dataXMin();

        this->dDataYMax = p_cf_3dpNewPoint->dataYMax();
        this->dDataYMin = p_cf_3dpNewPoint->dataYMin();

        this->dDataZMax = p_cf_3dpNewPoint->dataZMax();
        this->dDataZMin = p_cf_3dpNewPoint->dataZMin();
    }
    else
    {
        double  dDataValue;
                    // Our comparison value -- Note that for
                    // "points", XMax = XMin, etc...

        dDataValue = p_cf_3dpNewPoint->dataXMax();
        if ( dDataValue > this->dDataXMax )
                this->dDataXMax = dDataValue;
        if ( dDataValue < this->dDataXMin )
                this->dDataXMin = dDataValue;

        dDataValue = p_cf_3dpNewPoint->dataYMax();
        if ( dDataValue > this->dDataYMax )
                this->dDataYMax = dDataValue;
        if ( dDataValue < this->dDataYMin )
                this->dDataYMin = dDataValue;

        dDataValue = p_cf_3dpNewPoint->dataZMax();
        if ( dDataValue > this->dDataZMax )
                this->dDataZMax = dDataValue;
        if ( dDataValue < this->dDataZMin )
                this->dDataZMin = dDataValue;
    }


    // See if we should update the Virtual Max/Min coords:
    if ( this->iNumPoints == 1 )
    {
        this->dVirtXMax = p_cf_3dpNewPoint->virtXMax();
        this->dVirtXMin = p_cf_3dpNewPoint->virtXMin();

        this->dVirtYMax = p_cf_3dpNewPoint->virtYMax();
        this->dVirtYMin = p_cf_3dpNewPoint->virtYMin();

        this->dVirtZMax = p_cf_3dpNewPoint->virtZMax();
        this->dVirtZMin = p_cf_3dpNewPoint->virtZMin();
    }
    else
    {
        double  dVirtValue;
                    // Our comparison value -- Note that for
                    // "points", XMax = XMin, etc...

        dVirtValue = p_cf_3dpNewPoint->virtXMax();
        if ( dVirtValue > this->dVirtXMax )
                this->dVirtXMax = dVirtValue;
        if ( dVirtValue < this->dVirtXMin )
                this->dVirtXMin = dVirtValue;

        dVirtValue = p_cf_3dpNewPoint->virtYMax();
        if ( dVirtValue > this->dVirtYMax )
                this->dVirtYMax = dVirtValue;
        if ( dVirtValue < this->dVirtYMin )
                this->dVirtYMin = dVirtValue;

        dVirtValue = p_cf_3dpNewPoint->virtZMax();
        if ( dVirtValue > this->dVirtZMax )
                this->dVirtZMax = dVirtValue;
        if ( dVirtValue < this->dVirtZMin )
                this->dVirtZMin = dVirtValue;
    }

    return;

}   // end of cf_3DPointSeries::addPoint( cf_3DPoint* )


void
    cf_3DPointSeries
        ::addPoint( const cf_3DPoint &cr_cf_3dpNewPoint )
        //
        // Note that this member method is virtual, and
        // is meant to be over-ridden by derived objects...
        //
{
    // ADD a "new()-ed" POINTER onto the DYNAMIC ARRAY:
    this->iNumPoints++;
    this->da_p_cf_3dpVertices[ this->iNumPoints ] = new cf_3DPoint();

    // COPY the VALUE of the point param into the NEW 3D Point:
    cf_3DPoint  *p_cf_3dpNewPoint = ( (cf_3DPoint*)
                        this->da_p_cf_3dpVertices[ this->iNumPoints ] );
    *(p_cf_3dpNewPoint) = cr_cf_3dpNewPoint;

    // Set the new point's drawing attributes to ALWAYS match
    // this cf_3DPointSeries's point drawing attributes:
    p_cf_3dpNewPoint->dispMarkerShape( cf_3dpRenderAttribs_.dispMarkerShape() );
    p_cf_3dpNewPoint->dispMarkerSize( cf_3dpRenderAttribs_.dispMarkerSize() );

    // Makes the new point's disp-attributes INACTIVE...
    //...the master display attribute point will control these attribs!
    p_cf_3dpNewPoint->dispIgnorePushPen( TRUE );
    p_cf_3dpNewPoint->dispIgnorePushBrush( TRUE );
                    // The point will NOT need to push a pen/brush!
    p_cf_3dpNewPoint->preferAutoScale( FALSE );
                    // The point will NOT need to auto-scale!

    // See if we should update the Data Range coords:
    if ( this->iNumPoints == 1 )
    {
        this->dDataXMax = p_cf_3dpNewPoint->dataXMax();
        this->dDataXMin = p_cf_3dpNewPoint->dataXMin();

        this->dDataYMax = p_cf_3dpNewPoint->dataYMax();
        this->dDataYMin = p_cf_3dpNewPoint->dataYMin();

        this->dDataZMax = p_cf_3dpNewPoint->dataZMax();
        this->dDataZMin = p_cf_3dpNewPoint->dataZMin();
    }
    else
    {
        double  dDataValue;
                    // Our comparison value -- Note that for
                    // "points", XMax = XMin, etc...

        dDataValue = p_cf_3dpNewPoint->dataXMax();
        if ( dDataValue > this->dDataXMax )
                this->dDataXMax = dDataValue;
        if ( dDataValue < this->dDataXMin )
                this->dDataXMin = dDataValue;

        dDataValue = p_cf_3dpNewPoint->dataYMax();
        if ( dDataValue > this->dDataYMax )
                this->dDataYMax = dDataValue;
        if ( dDataValue < this->dDataYMin )
                this->dDataYMin = dDataValue;

        dDataValue = p_cf_3dpNewPoint->dataZMax();
        if ( dDataValue > this->dDataZMax )
                this->dDataZMax = dDataValue;
        if ( dDataValue < this->dDataZMin )
                this->dDataZMin = dDataValue;
    }


    // See if we should update the Virtual Max/Min coords:
    if ( this->iNumPoints == 1 )
    {
        this->dVirtXMax = p_cf_3dpNewPoint->virtXMax();
        this->dVirtXMin = p_cf_3dpNewPoint->virtXMin();

        this->dVirtYMax = p_cf_3dpNewPoint->virtYMax();
        this->dVirtYMin = p_cf_3dpNewPoint->virtYMin();

        this->dVirtZMax = p_cf_3dpNewPoint->virtZMax();
        this->dVirtZMin = p_cf_3dpNewPoint->virtZMin();
    }
    else
    {
        double  dVirtValue;
                    // Our comparison value -- Note that for
                    // "points", XMax = XMin, etc...

        dVirtValue = p_cf_3dpNewPoint->virtXMax();
        if ( dVirtValue > this->dVirtXMax )
                this->dVirtXMax = dVirtValue;
        if ( dVirtValue < this->dVirtXMin )
                this->dVirtXMin = dVirtValue;

        dVirtValue = p_cf_3dpNewPoint->virtYMax();
        if ( dVirtValue > this->dVirtYMax )
                this->dVirtYMax = dVirtValue;
        if ( dVirtValue < this->dVirtYMin )
                this->dVirtYMin = dVirtValue;

        dVirtValue = p_cf_3dpNewPoint->virtZMax();
        if ( dVirtValue > this->dVirtZMax )
                this->dVirtZMax = dVirtValue;
        if ( dVirtValue < this->dVirtZMin )
                this->dVirtZMin = dVirtValue;
    }

    return;

}   // end of cf_3DPointSeries::addPoint( cf_3DPoint& )


cf_3DPoint*
    cf_3DPointSeries
        ::attribPoint()
            { return( &(this->cf_3dpRenderAttribs_ ) ); }


cf_3DPoint
    cf_3DPointSeries
        ::getPoint( int iIndex )
        { return *( (cf_3DPoint*) this->da_p_cf_3dpVertices[ iIndex ]); }




void
    cf_3DPointSeries
        ::applyDispMetrics( cf_Kernel *p_cf_kUseThisKernel )
{
    // Simply pass the update signal along to our attribute point:
    { this->cf_3dpRenderAttribs_.applyDispMetrics( p_cf_kUseThisKernel ); }

    // Make a note that we have at least made ONE synchronization...
    cf_Virt3DObj::applyDispMetrics( p_cf_kUseThisKernel );
}




void
    cf_3DPointSeries
        ::applyGeomXform( cf_Kernel  *p_cf_kUseThisKernel )
{
    // Apply the xform to the point vertices:
    for ( int i = 1; i <= this->iNumPoints; i++ )
        ( (cf_3DPoint*) da_p_cf_3dpVertices[ i ] )
                            ->applyGeomXform( p_cf_kUseThisKernel );

    // Re-evaluate GEOMETRIC XYZ Min/Max values:
    cf_3DPoint *p_cf_3dpDefault = ( (cf_3DPoint*) da_p_cf_3dpVertices[ 1 ] );

    this->dGeomXMax = p_cf_3dpDefault->xformXMax();
    this->dGeomYMax = p_cf_3dpDefault->xformYMax();
    this->dGeomZMax = p_cf_3dpDefault->xformZMax();
    this->dGeomXMin = p_cf_3dpDefault->xformXMin();
    this->dGeomYMin = p_cf_3dpDefault->xformYMin();
    this->dGeomZMin = p_cf_3dpDefault->xformZMin();
                        // Preliminary candidates for Min/Max vals!...

    double  dXValMin, dYValMin, dZValMin;
    double  dXValMax, dYValMax, dZValMax;
                // Used to re-eval XYZ Min/Max values:

    for ( int j = 2; j <= this->iNumPoints; j++ )
    {
        cf_3DPoint *p_cf_3dpTestVal
                    = ( (cf_3DPoint*) da_p_cf_3dpVertices[ j ] );

        // Note that we are dealing with "dimensionless"
        // points -- for any given SINGLE point, min==max!
        dXValMin = p_cf_3dpTestVal->xformXMin();
        dYValMin = p_cf_3dpTestVal->xformYMin();
        dZValMin = p_cf_3dpTestVal->xformZMin();

        dXValMax = p_cf_3dpTestVal->xformXMax();
        dYValMax = p_cf_3dpTestVal->xformYMax();
        dZValMax = p_cf_3dpTestVal->xformZMax();

        if ( dXValMax > this->dGeomXMax ) this->dGeomXMax = dXValMax;
        if ( dXValMin < this->dGeomXMin ) this->dGeomXMin = dXValMin;

        if ( dYValMax > this->dGeomYMax ) this->dGeomYMax = dYValMax;
        if ( dYValMin < this->dGeomYMin ) this->dGeomYMin = dYValMin;

        if ( dZValMax > this->dGeomZMax ) this->dGeomZMax = dZValMax;
        if ( dZValMin < this->dGeomZMin ) this->dGeomZMin = dZValMin;
    }

    // Calc center of polyline/polygon XYZ extents for DSORTs
    this->dGeomXCtr = ( this->dGeomXMax - this->dGeomXMin ) / 2.0 + this->dGeomXMin;
    this->dGeomYCtr = ( this->dGeomYMax - this->dGeomYMin ) / 2.0 + this->dGeomYMin;
    this->dGeomZCtr = ( this->dGeomZMax - this->dGeomZMin ) / 2.0 + this->dGeomZMin;
    return;

}   // end of cf_3DPointSeries::applyGeomXform( cf_Kernel* )



void
    cf_3DPointSeries
        ::applyProjXform( cf_Kernel *p_cf_kUseThisKernel )
{
    cf_2DPoint  cf_2dpTestVal;
    double      dXValue, dYValue;
                // Used to re-eval XYZ Min/Max values:

    // Apply the xform to the polyline vertices:
    for ( int i = 1; i <= this->iNumPoints; i++ )
        ( (cf_3DPoint*) da_p_cf_3dpVertices[ i ] )
                        ->applyProjXform( p_cf_kUseThisKernel );

    // Re-evaluate SCREEN XY Min/Max values:
    this->dScreenXMax = this->dScreenYMax = 0.0;
    this->dScreenXMin = this->dScreenYMin = 0.0;

    for ( int j = 1; j <= this->iNumPoints; j++ )
    {
        cf_2dpTestVal // consider each 3D-Point's 2D-Screen-Point...
            = ( (cf_3DPoint*) da_p_cf_3dpVertices[ j ] )->screenPoint();

        dXValue = (double) cf_2dpTestVal.x();
        dYValue = (double) cf_2dpTestVal.y();

        if ( dXValue > this->dScreenXMax )  this->dScreenXMax = dXValue;
        if ( dXValue < this->dScreenXMin )  this->dScreenXMin = dXValue;

        if ( dYValue > this->dScreenYMax )  this->dScreenYMax = dYValue;
        if ( dYValue < this->dScreenYMin )  this->dScreenYMin = dYValue;
    }

    // Let's see if we FORGOT to apply any kernel display-metric info:
    cf_Virt3DObj::applyDispMetrics( p_cf_kUseThisKernel );

}   // end of cf_3DPointSeries::applyProjXform( cf_Kernel* )


//
// SCALE the cf_3DPointSeries's VIRTUAL Coordinates.
//
void
    cf_3DPointSeries
        ::applyScaleOp( double dXScaleFactor,
                        double dYScaleFactor,
                        double dZScaleFactor )
{
    // In order to "properly" scale this object, we
    // need to translate it back to it's original coords.
    double  dSaveXTrans = this->dTransX;
    double  dSaveYTrans = this->dTransY;
    double  dSaveZTrans = this->dTransZ;

    this->applyTranslateOp( -dSaveXTrans,
                            -dSaveYTrans,
                            -dSaveZTrans );

    // Apply the scale factors to the XYZ coords of
    // the cf_Virt3DObj's components.
    for ( int i = 1; i <= this->iNumPoints; i++ )
        ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] )
                                    ->applyScaleOp( dXScaleFactor,
                                                    dYScaleFactor,
                                                    dZScaleFactor );
    // Update this cf_Virt3DObj's "history":
    this->dScaleFacX = this->dScaleFacX * dXScaleFactor;
    this->dScaleFacY = this->dScaleFacY * dYScaleFactor;
    this->dScaleFacZ = this->dScaleFacZ * dZScaleFactor;

    this->dVirtXMax = this->dVirtXMax * dXScaleFactor;
    this->dVirtXMin = this->dVirtXMin * dXScaleFactor;
    this->dVirtYMax = this->dVirtYMax * dYScaleFactor;
    this->dVirtYMin = this->dVirtYMin * dYScaleFactor;
    this->dVirtZMax = this->dVirtZMax * dZScaleFactor;
    this->dVirtZMin = this->dVirtZMin * dZScaleFactor;

    // Now put our re-scaled cf_Virt3DObj "back" where it was:
    this->applyTranslateOp( dSaveXTrans, dSaveYTrans, dSaveZTrans );
    return;

}   // end of cf_3DPointSeries::applyScaleOp(...)



//
// TRANSLATE the cf_3DPointSeries's VIRTUAL Coordinates.
//
void
    cf_3DPointSeries
        ::applyTranslateOp( double  dXTrans,
                            double  dYTrans,
                            double  dZTrans  )
{
    //
    // Apply the xlations to the XYZ coords of the polyline's points.
    //
    for ( int i = 1; i <= this->iNumPoints; i++ )
        ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] )
                                      ->applyTranslateOp(   dXTrans,
                                                            dYTrans,
                                                            dZTrans );
    // Update our translation counters, so we can always
    // reverse-calc the Initial XYZ coords of the polyline.
    this->dTransX = this->dTransX + dXTrans;
    this->dTransY = this->dTransY + dYTrans;
    this->dTransZ = this->dTransZ + dZTrans;

    this->dVirtXMax = this->dVirtXMax + dXTrans;
    this->dVirtXMin = this->dVirtXMin + dXTrans;
    this->dVirtYMax = this->dVirtYMax + dYTrans;
    this->dVirtYMin = this->dVirtYMin + dYTrans;
    this->dVirtZMax = this->dVirtZMax + dZTrans;
    this->dVirtZMin = this->dVirtZMin + dZTrans;

    return;

}   // end of cf_3DPointSeries::applyTranslateOp(...)


//
// Get Data Dimension (e.g. for Tick Marking Ops) info:
//
double
    cf_3DPointSeries
        ::dataXMin()
            const
                { return this->dDataXMin; }

double
    cf_3DPointSeries
        ::dataXMax()
            const
                { return this->dDataXMax; }

double
    cf_3DPointSeries
        ::dataYMin()
            const
                { return this->dDataYMin; }

double
    cf_3DPointSeries
        ::dataYMax()
            const
                { return this->dDataYMax; }

double
    cf_3DPointSeries
        ::dataZMin()
            const
                { return this->dDataZMin; }

double
    cf_3DPointSeries
        ::dataZMax()
            const
                { return this->dDataZMax; }


//
// DRAW this cf_3DPointSeries on a bf_Display.
//
void
    cf_3DPointSeries
        ::draw2D( bf_Display *p_bf_dspOutputDisplay )
            { this->draw3D( p_bf_dspOutputDisplay ); }


void
    cf_3DPointSeries
        ::draw3D( bf_Display *p_bf_dspOutputDisplay )
{
    //
    // We'll use the cf_3DPoint::draw3D() member method
    // which ASSUMES we have pushed on the appropriate
    // pen and brush to draw with:
    //

    // New and Push a display pen and brush!
    this->dispPushPenAttribs( p_bf_dspOutputDisplay );
    this->dispPushBrushAttribs( p_bf_dspOutputDisplay );

    // Draw the individual data points/markers (which all
    // should be set to IGNORE their own attribs:
    eDrawShape  edsCurrShape = this->cf_3dpRenderAttribs_.dispMarkerShape();
    int         iCurrSize = this->cf_3dpRenderAttribs_.dispMarkerSize();

    for ( int i = 1; i <= this->iNumPoints; i++ )
        ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] )
                                ->drawFixed( p_bf_dspOutputDisplay,
                                             iCurrSize, edsCurrShape );

    // Pop and delete the pen and brush!
    this->dispPopBrushAttribs( p_bf_dspOutputDisplay );
    this->dispPopPenAttribs( p_bf_dspOutputDisplay );
    return;

}   // end of cf_3DPointSeries::draw3D( bf_Display* )



int
    cf_3DPointSeries
        ::numPoints()
            { return this->iNumPoints; }

void
    cf_3DPointSeries
        ::reset()
{
    // Get rid of any point allocations:
    for ( int i = 1; i <= this->iNumPoints; i++ )
        delete ( (cf_3DPoint*) this->da_p_cf_3dpVertices[ i ] );
    this->iNumPoints = 0;

    // Reset the "Geometry State" -- we need to: (1) re-Init
    // the Data Range info; (2) re-Init the Virtual Range info;
    // and (3) PRESERVE the Geometry (xlation/scale) State:
    this->dDataXMax = this->dDataYMax = this->dDataZMax = 0.0;
    this->dDataXMin = this->dDataYMin = this->dDataZMin = 0.0;
                                    // Data Range info reset.
    this->dVirtXMax = this->dVirtYMax = this->dVirtZMax = 0.0;
    this->dVirtXMin = this->dVirtYMin = this->dVirtZMin = 0.0;
                                    // Virt/Scale Range info reset.

    // But PRESERVE the Scaling/Translation State...
    //this->dTransX = this->dTransY = this->dTransZ = 0.0;
    //this->dScaleFacX = this->dScaleFacY = this->dScaleFacZ = 1.0;

    this->dGeomXMax = this->dGeomYMax = this->dGeomZMax = 0.0;
    this->dGeomXMin = this->dGeomYMin = this->dGeomZMin = 0.0;
                                    // Geometry State reset.
    this->dScreenXMax = this->dScreenYMax = 0.0;
    this->dScreenXMin = this->dScreenYMin = 0.0;
                                    // Projection State reset.
    return;
}


double
    cf_3DPointSeries
        ::scaleX()
            { return this->dScaleFacX; }

double
    cf_3DPointSeries
        ::scaleY()
            { return this->dScaleFacY; }

double
    cf_3DPointSeries
        ::scaleZ()
            { return this->dScaleFacZ; }



//
// Get Screen Projection Info:
//
double
    cf_3DPointSeries
        ::screenXMin()
            { return this->dScreenXMin; }

double
    cf_3DPointSeries
        ::screenXMax()
            { return this->dScreenXMax; }

double
    cf_3DPointSeries
        ::screenYMin()
            { return this->dScreenYMin; }

double
    cf_3DPointSeries
        ::screenYMax()
            { return this->dScreenYMax; }


double
    cf_3DPointSeries
        ::transX()
            { return this->dTransX; }

double
    cf_3DPointSeries
        ::transY()
            { return this->dTransY; }

double
    cf_3DPointSeries
        ::transZ()
            { return this->dTransZ; }


//
// Get Virtual Dimension (e.g. for Scaling Ops) info:
//
double
    cf_3DPointSeries
        ::virtXMin()
            const
                { return this->dVirtXMin; }

double
    cf_3DPointSeries
        ::virtXMax()
            const
                { return this->dVirtXMax; }

double
    cf_3DPointSeries
        ::virtYMin()
            const
                { return this->dVirtYMin; }

double
    cf_3DPointSeries
        ::virtYMax()
            const
                { return this->dVirtYMax; }

double
    cf_3DPointSeries
        ::virtZMin()
            const
                { return this->dVirtZMin; }

double
    cf_3DPointSeries
        ::virtZMax()
            const
                { return this->dVirtZMax; }

//
// Get HSE (Hidden-line/Surface Elimination) info:
//
double
    cf_3DPointSeries
        ::xformXMin()
            const
            { return this->dGeomXMin; }

double
    cf_3DPointSeries
        ::xformXMax()
            const
            { return this->dGeomXMax; }

double
    cf_3DPointSeries
        ::xformXCtr()
            const
            { return this->dGeomXCtr; }

double
    cf_3DPointSeries
        ::xformYMin()
            const
            { return this->dGeomYMin; }

double
    cf_3DPointSeries
        ::xformYMax()
            const
            { return this->dGeomYMax; }

double
    cf_3DPointSeries
        ::xformYCtr()
            const
            { return this->dGeomYCtr; }

double
    cf_3DPointSeries
        ::xformZMin()
            const
            { return this->dGeomZMin; }

double
    cf_3DPointSeries
        ::xformZMax()
            const
            { return this->dGeomZMax; }

double
    cf_3DPointSeries
        ::xformZCtr()
            const
            { return this->dGeomZCtr; }



//
// End of cf_3DPointSeries Implementation.
// 

//////////////////////////////////////
// END of File: CO1_PSER.CPP
//////////////////////////////////////

