//////////////////////////////////////
// START of File: CO1_POIN.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_poin.hpp>

#include	<math.h>    // for fabs() calls below...

//
// Implement the cf_Virt3DObj-derived "cf_3DPoint" class.
// All functions not implemented here are implemented
// in the *.hpp file.
//
cf_3DPoint
    ::cf_3DPoint()
        :cf_Virt3DObj()     // Default c-tor

{
    // Data and Virt and Geom Values:
    this->dDataX    = this->dVirtX = this->dXformX = 0.0;
    this->dDataY    = this->dVirtY = this->dXformY = 0.0;
    this->dDataZ    = this->dVirtZ = this->dXformZ = 0.0;
    this->bSkipGeomXforms = FALSE;
    this->cf_2dpScreenCoord.setZero();

    // Display Attribs:
    this->edsPointShape = edsCircle;
    this->iMarkerRadialSize_ = CO1_POIN_DEFAULT_FIXED;

    this->dAutoScalePct = CO1_POIN_DEFAULT_SCALE;
    this->bAutoScale = FALSE;

}   // end of default c-tor


cf_3DPoint
    ::cf_3DPoint(   double  dInitX,
                    double  dInitY,
                    double  dInitZ )
        :cf_Virt3DObj()

{
    // Data and Virt and Geom Values:
    this->dDataX = this->dVirtX = this->dXformX = dInitX;
    this->dDataY = this->dVirtY = this->dXformY = dInitY;
    this->dDataZ = this->dVirtZ = this->dXformZ = dInitZ;
    this->bSkipGeomXforms = FALSE;
    this->cf_2dpScreenCoord.setZero();

    // Display Attribs:
    this->edsPointShape = edsCircle;
    this->iMarkerRadialSize_ = CO1_POIN_DEFAULT_FIXED;

    this->dAutoScalePct = CO1_POIN_DEFAULT_SCALE;
    this->bAutoScale = FALSE;

}   // end of parametrized c-tor w/ default colors for most uses...



//
// Virtual d-tor:
//
cf_3DPoint
    ::~cf_3DPoint()
        {}



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

    // Copy the derived member var values:
    this->dDataX    = cr_cf_3dpSource.dDataX;
    this->dDataY    = cr_cf_3dpSource.dDataY;
    this->dDataZ    = cr_cf_3dpSource.dDataZ;

    this->dVirtX    = cr_cf_3dpSource.dVirtX;
    this->dVirtY    = cr_cf_3dpSource.dVirtY;
    this->dVirtZ    = cr_cf_3dpSource.dVirtZ;

    this->dXformX   = cr_cf_3dpSource.dXformX;
    this->dXformY   = cr_cf_3dpSource.dXformY;
    this->dXformZ   = cr_cf_3dpSource.dXformZ;

    this->bSkipGeomXforms = cr_cf_3dpSource.bSkipGeomXforms;

    this->cf_2dpScreenCoord = (cf_2DPoint&)
                                cr_cf_3dpSource.cf_2dpScreenCoord;

    this->edsPointShape     = cr_cf_3dpSource.edsPointShape;
    this->iMarkerRadialSize_         = cr_cf_3dpSource.iMarkerRadialSize_;

    this->dAutoScalePct     = cr_cf_3dpSource.dAutoScalePct;
    this->bAutoScale        = cr_cf_3dpSource.bAutoScale;
                            // Derived class member var values:

    return *this;

}   // end of cf_3DPoint::operator=( cf_3DPoint cf_3dpUserVal )



void
    cf_3DPoint
        ::applyDispMetrics( cf_Kernel *p_cf_kUseThisKernel )
//---------------------------------------------------------------------
// Called by the parent chart-engine configuration in response to
// display context changes (e.g. re-size events) imposed by the
// chart-engine's parent chart-model/chart-view.  This implementation
// over-rides the pure virtual declaration of the base class to
// re-size the point's marker proportionately w.r.t the kernels'
// latest measurement of the display area's (new?) size.
//---------------------------------------------------------------------
{
    // If we don't belong here -- let's scram!
    if ( this->bAutoScale == FALSE ) return;
        // But... if we DO have business here,
        // let's update the Display Marker Size:

    // Reference the last display area measured as a rectangle:
    cf_2DRect cf_2drCurrDsp //= p_cf_kUseThisKernel->referToDispRectMargin();
                            = p_cf_kUseThisKernel->referToProjRectFaceBack();

    // Compare to yield our minimal constraining display dimension:
    int iMinDimension = cf_2drCurrDsp.width();  // tentatively!...
    if ( cf_2drCurrDsp.height() < iMinDimension )
        iMinDimension = cf_2drCurrDsp.height();

    // Calc our re-size from the minimum constraining dimension:
    double  dMinDimension = ( (double) iMinDimension );
    double  dDrawSize = ( ( this->dAutoScalePct * dMinDimension ) / 200.0 );
    this->iMarkerRadialSize_ = ((int) dDrawSize );

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

}   // end of cf_3DPoint::applyDispMetrics( cf_Kernel* )



void
    cf_3DPoint
        ::applyGeomXform( cf_Kernel *p_cf_kUseThisKernel )
{
    if ( this->bSkipGeomXforms == TRUE )
    {
        this->dXformX = this->dVirtX;
        this->dXformY = this->dVirtY;
        this->dXformZ = this->dVirtZ;
    }
    else    // Do a "regular" geom/xform:
    {
        // Using the active kernel's geometry matrix...
        const CF_4BY4_MATRIX    &r_cf_4b4mForGeomXform
                                = p_cf_kUseThisKernel->referToGeomMatrix();

        // ...perform the matrix multiplication:
        double  dTempXCoord = r_cf_4b4mForGeomXform[ 0 ][ 0 ] * dVirtX
                            + r_cf_4b4mForGeomXform[ 1 ][ 0 ] * dVirtY
                            + r_cf_4b4mForGeomXform[ 2 ][ 0 ] * dVirtZ;

        double  dTempYCoord = r_cf_4b4mForGeomXform[ 0 ][ 1 ] * dVirtX
                            + r_cf_4b4mForGeomXform[ 1 ][ 1 ] * dVirtY
                            + r_cf_4b4mForGeomXform[ 2 ][ 1 ] * dVirtZ;

        double  dTempZCoord = r_cf_4b4mForGeomXform[ 0 ][ 2 ] * dVirtX
                            + r_cf_4b4mForGeomXform[ 1 ][ 2 ] * dVirtY
                            + r_cf_4b4mForGeomXform[ 2 ][ 2 ] * dVirtZ;

        double  dProjectionRatio = r_cf_4b4mForGeomXform[ 0 ][ 3 ] * dVirtX
                                    + r_cf_4b4mForGeomXform[ 1 ][ 3 ] * dVirtY
                                    + r_cf_4b4mForGeomXform[ 2 ][ 3 ] * dVirtZ
                                    + r_cf_4b4mForGeomXform[ 3 ][ 3 ];

        // Perform the projection:
        #ifdef  CF_ASSERT_DVALS
        assert( fabs( dProjectionRatio ) > CF_NUM_ZERO_IF_LT );
        #endif
        if ( fabs( dProjectionRatio ) > CF_NUM_ZERO_IF_LT )
        {
        		this->dXformX = dTempXCoord / dProjectionRatio;
        		this->dXformY = dTempYCoord / dProjectionRatio;
        }
        else
        {
        		this->dXformX = dTempXCoord;
        		this->dXformY = dTempYCoord;
        }
        this->dXformZ = dTempZCoord;

    }   // OK, "regular" geom/xform is complete

    return;

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


void
    cf_3DPoint
        ::applyProjXform( cf_Kernel *p_cf_kUseThisKernel )
{
    // Use the active kernel's projection matrix...
    const CF_4BY2_MATRIX    &r_cf_4b2mForProjXform
                            = p_cf_kUseThisKernel->referToProjMatrix();

    // ...to yield our screen coords:
    this->cf_2dpScreenCoord.x()
        = (unsigned int) ( r_cf_4b2mForProjXform[ 0 ][ 0 ]
                            * dXformX
                            + r_cf_4b2mForProjXform[ 3 ][ 0 ] );

    this->cf_2dpScreenCoord.y()
        = (unsigned int) ( r_cf_4b2mForProjXform[ 1 ][ 1 ]
                            * dXformY
                            + r_cf_4b2mForProjXform[ 3 ][ 1 ] );

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

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




//
// Apply transform-independent geometric operations on a cf_3DPoint.
//
// E.g., pictCharts may want to SCALE cf_3DPoints, (or their user
// classes such as pictPolylines, pictPolygons, etc.) to
// proportion cf_Virt3DObjs for optimal chart dimensioning, etc.
//
void
    cf_3DPoint
        ::applyScaleOp( double dXScaleFactor,
                        double dYScaleFactor,
                        double dZScaleFactor ) 
{
    this->dVirtX = this->dVirtX * dXScaleFactor;
    this->dVirtY = this->dVirtY * dYScaleFactor;
    this->dVirtZ = this->dVirtZ * dZScaleFactor;
}




//
// Apply transform-independent geometric operations on the cf_3DPoint.
//
// E.g., pictCharts typictally want to TRANSLATE cf_3DPoints, (or 
// their user classes such as pictPolylines, pictPolygons, 
// etc.) to match the objects(s) to their relative chart
// origin.
//
void
    cf_3DPoint
        ::applyTranslateOp( double dXTrans,
                            double dYTrans,
                            double dZTrans ) 
{
    this->dVirtX = this->dVirtX + dXTrans;
    this->dVirtY = this->dVirtY + dYTrans;
    this->dVirtZ = this->dVirtZ + dZTrans;
}



//
// Assign & Read the DATA 3D coords -- Note that any 
// (re)assignment of the initial coords also re-assigns
// the virtual coords:
//
//
// Read the DATA 3D coords -- what the user stuffed in for "data"
// values.
//
double
    cf_3DPoint
        ::dataX() 
            const   
        { return this->dDataX; }

double
    cf_3DPoint
        ::dataY()   
            const   
        { return this->dDataY; }

double
    cf_3DPoint
        ::dataZ()   
            const   
        { return this->dDataZ; }


void
    cf_3DPoint
        ::dataX( double dNewX )
    { this->dDataX = this->dVirtX = this->dXformX = dNewX; }


void
    cf_3DPoint
        ::dataY( double dNewY )
    { this->dDataY = this->dVirtY = this->dXformY = dNewY; }


void
    cf_3DPoint
        ::dataZ( double dNewZ )
    { this->dDataZ = this->dVirtZ = this->dXformZ = dNewZ; }


void
    cf_3DPoint
        ::dataXYZ(  double  dNewX, 
                    double  dNewY, 
                    double  dNewZ ) 
{   this->dDataX = this->dVirtX = this->dXformX = dNewX;
        this->dDataY = this->dVirtY = this->dXformY = dNewY;
            this->dDataZ = this->dVirtZ = this->dXformZ = dNewZ; }


double
    cf_3DPoint
        ::dataXMin() 
            const   
        { return this->dDataX; }

double
    cf_3DPoint
        ::dataXMax() 
            const
        { return this->dDataX; }
            
            
double
    cf_3DPoint
        ::dataYMin()    
            const   
        { return this->dDataY; }

double  
    cf_3DPoint
        ::dataYMax()    
            const   
        { return this->dDataY; }
            
double
    cf_3DPoint
        ::dataZMin()    
            const   
        { return this->dDataZ; }

double
    cf_3DPoint
        ::dataZMax()    
            const   
        { return this->dDataZ; }
    

eDrawShape
    cf_3DPoint
        ::dispMarkerShape()
            { return this->edsPointShape; }
void
    cf_3DPoint
        ::dispMarkerShape( const eDrawShape &cr_edsNewShape )
            { this->edsPointShape = cr_edsNewShape; }
                    
int
    cf_3DPoint
        ::dispMarkerSize() 
            { return this->iMarkerRadialSize_; }
void
    cf_3DPoint
        ::dispMarkerSize( int iNewSize ) 
            { this->iMarkerRadialSize_ = iNewSize; }



//
// DRAW this cf_3DPoint on a bf_Display.
//
void
    cf_3DPoint
        ::draw2D( bf_Display *p_bf_dspOutputDisplay )
{
    if ( bDispVisible_ == FALSE ) return;

    //--------------------------------------------------------
    // If we've been set up to use our OWN attributes,
    // Let's push them onto the display and make note of it:
    //--------------------------------------------------------
    BOOL    bPushedPen = FALSE;
    BOOL    bPushedBrush = FALSE;

    if ( this->bIgnorePushPen_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushPenAttribs( p_bf_dspOutputDisplay );
            bPushedPen = TRUE;
        }   // Pen now pushed and noted.

    if ( this->bIgnorePushBrush_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushBrushAttribs( p_bf_dspOutputDisplay );
            bPushedBrush = TRUE;
        }   // Brush now pushed and noted.

    //---------------------------------------------------------
    // Now render a point "Marker" with the current pen/brush:
    //---------------------------------------------------------
    p_bf_dspOutputDisplay->dsp_circle( this->cf_2dpScreenCoord,
                                        this->iMarkerRadialSize_ );

    //-----------------------------------------
    // Then render the actual data "Point":
    //-----------------------------------------
    bf_Color   bf_clrPoint = CF_BLACK;
                // Start with a decent default...

    if ( bWereDispAttribsSet_ == TRUE )
        // ... but try to pick up the "current" PEN color.
        bf_clrPoint = p_cf_datbForDrawOps_->colorPen();

    p_bf_dspOutputDisplay->dsp_pixel(  this->cf_2dpScreenCoord.x(),
                                        this->cf_2dpScreenCoord.y(),
                                        bf_clrPoint );
                                        // point now rendered.

    //------------------------------------------------------
    // If we pushed any pens/brushes we better pop 'em!
    //------------------------------------------------------
    if ( bPushedBrush == TRUE )
            this->dispPopBrushAttribs( p_bf_dspOutputDisplay );
    if ( bPushedPen == TRUE )
        this->dispPopPenAttribs( p_bf_dspOutputDisplay );

    return;
}


//
// DRAW this cf_3DPoint on a bf_Display.
//
void
    cf_3DPoint
        ::draw3D( bf_Display *p_bf_dspOutputDisplay )
{
    if ( bDispVisible_ == FALSE ) return;

    //--------------------------------------------------------
    // If we've been set up to use our OWN attributes,
    // Let's push them onto the display and make note of it:
    //--------------------------------------------------------
    BOOL    bPushedPen = FALSE;
    BOOL    bPushedBrush = FALSE;

    if ( this->bIgnorePushPen_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushPenAttribs( p_bf_dspOutputDisplay );
            bPushedPen = TRUE;
        }   // Pen now pushed and noted.

    if ( this->bIgnorePushBrush_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushBrushAttribs( p_bf_dspOutputDisplay );
            bPushedBrush = TRUE;
        }   // Brush now pushed and noted.

    //---------------------------------------------------------
    // Now render a point "Marker" with the current pen/brush:
    //---------------------------------------------------------
    p_bf_dspOutputDisplay->dsp_circle( this->cf_2dpScreenCoord,
                                        this->iMarkerRadialSize_ );

    //-----------------------------------------
    // Then render the actual data "Point":
    //-----------------------------------------
    bf_Color   bf_clrPoint = CF_BLACK;
                // Start with a decent default...

    if ( bWereDispAttribsSet_ == TRUE )
        // ... but try to pick up the "current" PEN color.
        bf_clrPoint = p_cf_datbForDrawOps_->colorPen();

    p_bf_dspOutputDisplay->dsp_pixel(  this->cf_2dpScreenCoord.x(),
                                        this->cf_2dpScreenCoord.y(),
                                        bf_clrPoint );
                                        // point now rendered.

    //------------------------------------------------------
    // If we pushed any pens/brushes we better pop 'em!
    //------------------------------------------------------
    if ( bPushedBrush == TRUE )
            this->dispPopBrushAttribs( p_bf_dspOutputDisplay );
    if ( bPushedPen == TRUE )
        this->dispPopPenAttribs( p_bf_dspOutputDisplay );
    return;

}   // end of cf_3DPoint::draw3D(...)


void
    cf_3DPoint
        ::drawFixed(    bf_Display  *p_bf_dspOutputDisplay,
                        int         iMarkerRadialSize,
                        eDrawShape  /*edsMarkerShape*/ )
{
    if ( bDispVisible_ == FALSE ) return;

    //--------------------------------------------------------
    // If we've been set up to use our OWN attributes,
    // Let's push them onto the display and make note of it:
    //--------------------------------------------------------
    BOOL    bPushedPen = FALSE;
    BOOL    bPushedBrush = FALSE;

    if ( this->bIgnorePushPen_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushPenAttribs( p_bf_dspOutputDisplay );
            bPushedPen = TRUE;
        }   // Pen now pushed and noted.

    if ( this->bIgnorePushBrush_ == FALSE )
        if ( bWereDispAttribsSet_ == TRUE )
        {
            this->dispPushBrushAttribs( p_bf_dspOutputDisplay );
            bPushedBrush = TRUE;
        }   // Brush now pushed and noted.

    //---------------------------------------------------------
    // Now render a point "Marker" with the current pen/brush:
    //---------------------------------------------------------
    p_bf_dspOutputDisplay->dsp_circle(  this->cf_2dpScreenCoord,
                                        iMarkerRadialSize );

    //-----------------------------------------
    // Then render the actual data "Point":
    //-----------------------------------------
    bf_Color   bf_clrPoint = CF_BLACK;
                // Start with a decent default...

    if ( bWereDispAttribsSet_ == TRUE )
        // ... but try to pick up the "current" PEN color.
        bf_clrPoint = p_cf_datbForDrawOps_->colorPen();

    p_bf_dspOutputDisplay->dsp_pixel(   this->cf_2dpScreenCoord.x(),
                                        this->cf_2dpScreenCoord.y(),
                                        bf_clrPoint );
                                        // point now rendered.

    //------------------------------------------------------
    // If we pushed any pens/brushes we better pop 'em!
    //------------------------------------------------------
    if ( bPushedBrush == TRUE )
            this->dispPopBrushAttribs( p_bf_dspOutputDisplay );
    if ( bPushedPen == TRUE )
        this->dispPopPenAttribs( p_bf_dspOutputDisplay );
    return;

}   // end of cf_3DPoint::drawFixed(...)


void
    cf_3DPoint
        ::preferAutoScale( BOOL bNewState )
{
    if ( ( this->bAutoScale = bNewState ) == FALSE )
            this->iMarkerRadialSize_ = CO1_POIN_DEFAULT_FIXED;
}

BOOL
    cf_3DPoint
        ::preferAutoScaleIs()
            { return this->bAutoScale; }


void
    cf_3DPoint
        ::preferAutoScalePct( double  dPercentOfMinDispDimension )
{
    this->dAutoScalePct = dPercentOfMinDispDimension;
    this->bAutoScale = TRUE;
}

double
    cf_3DPoint
        ::preferAutoScalePctIs()
            { return this->dAutoScalePct; }


// Makes Geom/Xform Coords always == Virt Coords.
BOOL
    cf_3DPoint
        ::skipGeomXforms()
            { return this->bSkipGeomXforms; }

void
    cf_3DPoint
        ::skipGeomXforms( BOOL bNewState )
            { this->bSkipGeomXforms = bNewState; }



//
// Access the results of the projection op:
//
cf_2DPoint 
    cf_3DPoint
        ::screenPoint() 
            { return this->cf_2dpScreenCoord; }

int 
    cf_3DPoint
        ::screenX() 
            { return this->cf_2dpScreenCoord.x(); }

int
    cf_3DPoint
        ::screenY()
            { return this->cf_2dpScreenCoord.y(); }


//--------------------------------------------------------------------------
// Read the VIRTUAL 3D coords -- Remember, the only things that can ASSIGN
// the virtual 3D coords are geometric ops like scale() and translate():
//--------------------------------------------------------------------------
double
    cf_3DPoint
        ::virtX()
            const   
        { return this->dVirtX; }

double
    cf_3DPoint
        ::virtY()   
            const   
        { return this->dVirtY; }

double
    cf_3DPoint
        ::virtZ()   
            const   
        { return this->dVirtZ; }


double
    cf_3DPoint
        ::virtXMin()    
            const   
        { return this->dVirtX; }

double
    cf_3DPoint
        ::virtXMax()    
            const   
        { return this->dVirtX; }
            
double
    cf_3DPoint
        ::virtYMin()    
            const   
        { return this->dVirtY; }

double
    cf_3DPoint
        ::virtYMax()    
            const   
        { return this->dVirtY; }
            
double
    cf_3DPoint
        ::virtZMin()    
            const   
        { return this->dVirtZ; }

double
    cf_3DPoint
        ::virtZMax()    
            const
        { return this->dVirtZ; }


//
// Read the TRANSFORMED 3D coords:
//
double
    cf_3DPoint
        ::xformX()  
            const   
        { return this->dXformX; }

double
    cf_3DPoint
        ::xformY()  
            const   
        { return this->dXformY; }

double
    cf_3DPoint
        ::xformZ()  
            const   
        { return this->dXformZ; }


double
    cf_3DPoint
        ::xformXMin()   
            const
        { return this->dXformX; }

double
    cf_3DPoint
        ::xformXMax()   
            const   
        { return this->dXformX; }
            
double
    cf_3DPoint
        ::xformYMin()   
            const   
        { return this->dXformY; }

double
    cf_3DPoint
        ::xformYMax()   
            const
        { return this->dXformY; }
            
double
    cf_3DPoint
        ::xformZMin()   
            const
        { return this->dXformZ; }

double
    cf_3DPoint
        ::xformZMax()
            const
        { return this->dXformZ; }

//
// END of cf_3DPoint Implementation.
//


//////////////////////////////////////
// END of File: CO1_POIN.CPP
//////////////////////////////////////


