//////////////////////////////////////
// START of File: CE1_PCOR.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_pcor.hpp>

//
// Include any additional classe defintions:
//
class       cf_2DPoint;
class       cf_2DRect;
#include    <cf_api/c0_dimen.hpp>

//
// Use this ctor to set up an "empty" cf_eng_Polar.
//
cf_eng_Polar::cf_eng_Polar
            (   cf_ChartType    enumChartType,
                cf_DispMode     enumDispMode )

    :   cf_ChartEngine( enumChartType,
                        enumDispMode )
{
    // Init the Kernel to a favorable state for polar chart views:
    this->preferViewAngle(  30.0,   // x-view angle -- up at 30 deg.
                            0.0,    // y-view angle -- head-on-front.
                            TRUE,
                            TRUE  );
    this->preferPerspectiveZero();  // Go for ISOMETRIC projection

    // Pies should be "squashed" a little flatter
    // than Cartesian/XYZ charts:
    this->preferProportions( 0.05,       // New height-to-width
                             1.0,        // New depth-to-height
                             TRUE );     // But do NOT auto-configure

    // Configure this derived chart's origins, etc:
    measureVirtOrigin();

    // The following lines can be uncommented for debugging:
    // (They set up marker point attributes for the XZ Plane)
    /*
    this->cf_3dpXZPlaneEllipseCenter.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseCenter.setColor( CF_BLACK );

    this->cf_3dpXZPlaneEllipseProjXMax.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseProjXMax.setColor( CF_RED );
    this->cf_3dpXZPlaneEllipseProjXMin.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseProjXMin.setColor( CF_RED );

    this->cf_3dpXZPlaneEllipseVirtXMax.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseVirtXMax.setColor( cf_objRANGE );
    this->cf_3dpXZPlaneEllipseVirtXMin.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseVirtXMin.setColor( cf_objRANGE );
    
    this->cf_3dpXZPlaneEllipseProjZMax.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseProjZMax.setColor( CF_DARK_BLUE );
    this->cf_3dpXZPlaneEllipseProjZMin.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseProjZMin.setColor( CF_DARK_BLUE );

    this->cf_3dpXZPlaneEllipseVirtZMax.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseVirtZMax.setColor( CF_MED_PURPLE );
    this->cf_3dpXZPlaneEllipseVirtZMin.set3DMarker( TRUE );
    this->cf_3dpXZPlaneEllipseVirtZMin.setColor( CF_MED_PURPLE );
    */
}


cf_eng_Polar
    ::~cf_eng_Polar() 
        {}


void
cf_eng_Polar::applyGeomXform()
{
    // First, use the capabilities of the base class:
    cf_ChartEngine::applyGeomXform();

    // Now, perform specific polar chart functionality...
    // Xform the center of the XZ Plane:
    this->cf_3dpXZPlaneEllipseCenter.applyGeomXform( p_cf_kEnginesKernel_ );

    // Xform the "virtual" minima/maxima:
    this->cf_3dpXZPlaneEllipseVirtXMax.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtXMin.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtZMax.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtZMin.applyGeomXform( p_cf_kEnginesKernel_ );

    // Counter any Y-Angle Rotation Effects
    // on the XZ plane (base) ellipse rectangle:
    double  dCurrXAngle = p_cf_kEnginesKernel_->getXAngle();
    double  dCurrYAngle = p_cf_kEnginesKernel_->getYAngle();
    double  dCurrPRatio = p_cf_kEnginesKernel_->getPerspectiveRatio();

    p_cf_kEnginesKernel_->preferViewAngle( dCurrXAngle, 0, TRUE );

    this->cf_3dpXZPlaneEllipseProjXMax.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjXMin.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjZMax.applyGeomXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjZMin.applyGeomXform( p_cf_kEnginesKernel_ );
            // Xforms the XZ plane ellipse marker points

    // Now restore the kernel's state:
    p_cf_kEnginesKernel_->preferViewAngle( dCurrXAngle, dCurrYAngle, TRUE);

}   // end of cf_eng_Polar::applyGeomXform()



void
cf_eng_Polar::applyProjXform()
{
    // First, use the capabilities of the base class:
    cf_ChartEngine::applyProjXform();

    // Now, perform specific polar chart functionality:
    this->cf_3dpXZPlaneEllipseProjXMax.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjXMin.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjZMax.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseProjZMin.applyProjXform( p_cf_kEnginesKernel_ );

    this->cf_3dpXZPlaneEllipseCenter.applyProjXform( p_cf_kEnginesKernel_ );

    this->cf_3dpXZPlaneEllipseVirtXMax.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtXMin.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtZMax.applyProjXform( p_cf_kEnginesKernel_ );
    this->cf_3dpXZPlaneEllipseVirtZMin.applyProjXform( p_cf_kEnginesKernel_ );
}            // XZ Plane xform completed


void
cf_eng_Polar::applyVirtScalings()
{
}

void
cf_eng_Polar::applyVirtTranslations()
{
}

void
cf_eng_Polar::calcVirtScaleFactors()
{
}

void
cf_eng_Polar::calcVirtTransFactors()
{
}



// If we change coordinates within the chart, or change the
// dimensions of the kernel, we should recalc the chart.
void
cf_eng_Polar::config3DBoxETC()
{
    // Use the functionality of the Base Class to
    // Configure the "standard" components of the 3D Box:
    cf_ChartEngine::config3DBoxETC();

    // Verify that we have a "proper" square to calc the
    // projected recangular boundary of our "polar pie"
    if ( d3DBoxXDim_ != d3DBoxZDim_ )
    // Whooooaw! Our "width" does not equal our "depth" !!!
    {
        // Alter the chart dimensions to correct the problem:
        d3DBoxZDim_ = d3DBoxXDim_;
        p_cf_kEnginesKernel_->preferDimensions( d3DBoxXDim_,    // as is...
                                                d3DBoxYDim_,    // unaltered
                                                d3DBoxZDim_,    // now = X!
                                                TRUE );

        // Update all the 3D Box dimension tracking vars:
        cf_ChartEngine::config3DBoxETC();

    }   // End of screen for non-square pie-circle
        // bounding-box bogosity.

    // Set up the XZ Plane ellipse tracking points by using
    // the bounding limits of the Pie's Base Ellipse Rectangle:
    // These points mark the "border of the pie"'s projected
    // ellipse boundaries, after correction for rotation, etc
    this->cf_3dpXZPlaneEllipseVirtXMax
                        .dataXYZ(   p_cf_kEnginesKernel_->getXMax(),
                                    dChartYOrigin_,
                                    dChartZOrigin_ );
    this->cf_3dpXZPlaneEllipseProjXMax
                        =( this->cf_3dpXZPlaneEllipseVirtXMax );

    this->cf_3dpXZPlaneEllipseVirtXMin
                        .dataXYZ(   p_cf_kEnginesKernel_->getXMin(),
                                    dChartYOrigin_,
                                    dChartZOrigin_ );
    this->cf_3dpXZPlaneEllipseProjXMin
                        =( this->cf_3dpXZPlaneEllipseVirtXMin );

    this->cf_3dpXZPlaneEllipseVirtZMax
                        .dataXYZ(   dChartXOrigin_,
                                    dChartYOrigin_,
                                    p_cf_kEnginesKernel_->getZMax() );
    this->cf_3dpXZPlaneEllipseProjZMax
                        =( this->cf_3dpXZPlaneEllipseVirtZMax );

    this->cf_3dpXZPlaneEllipseVirtZMin
                        .dataXYZ(   dChartXOrigin_,
                                    dChartYOrigin_,
                                    p_cf_kEnginesKernel_->getZMin() );
    this->cf_3dpXZPlaneEllipseProjZMin
                        =( this->cf_3dpXZPlaneEllipseVirtZMin );

    this->cf_3dpXZPlaneEllipseCenter
                        .dataXYZ(   dChartXOrigin_,
                                    dChartYOrigin_,
                                    dChartZOrigin_  );

}   // end of cf_eng_Polar::config3DBoxETC()




void
cf_eng_Polar::draw2D
            ( bf_Display *p_bf_dspFromChartView )
{
    cf_eng_Polar::draw3D( p_bf_dspFromChartView );
}


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

    // Now, perform derived functionality!  Use the Virtual Projection Points
    // to determine the boundaries of a rectangle that will define the XZ
    // projected ellipse on the XY Coordinate space of the current bf_Display:
    cf_2DPoint cf_2dpProjXMin = this->cf_3dpXZPlaneEllipseProjXMin.screenPoint();
    cf_2DPoint cf_2dpProjXMax = this->cf_3dpXZPlaneEllipseProjXMax.screenPoint();
    cf_2DPoint cf_2dpProjZMin = this->cf_3dpXZPlaneEllipseProjZMin.screenPoint();
    cf_2DPoint cf_2dpProjZMax = this->cf_3dpXZPlaneEllipseProjZMax.screenPoint();

    cf_2DPoint cf_2dpVirtXMax = this->cf_3dpXZPlaneEllipseVirtXMax.screenPoint();
    cf_2DPoint cf_2dpVirtZMax = this->cf_3dpXZPlaneEllipseVirtZMax.screenPoint();

    // Test and update these default Min/Max Projection boundaries:
    cf_2DPoint cf_2dpUpperLeft = cf_2dpProjXMin;
    cf_2dpUpperLeft = cf_2dpUpperLeft.yieldMin( cf_2dpProjXMax );
    cf_2dpUpperLeft = cf_2dpUpperLeft.yieldMin( cf_2dpProjZMin );
    cf_2dpUpperLeft = cf_2dpUpperLeft.yieldMin( cf_2dpProjZMax );

    cf_2DPoint cf_2dpLowerRight = cf_2dpProjXMin;
    cf_2dpLowerRight = cf_2dpLowerRight.yieldMax( cf_2dpProjXMax );
    cf_2dpLowerRight = cf_2dpLowerRight.yieldMax( cf_2dpProjZMin );
    cf_2dpLowerRight = cf_2dpLowerRight.yieldMax( cf_2dpProjZMax );

    cf_2dpLowerRight = cf_2dpLowerRight.yieldMax( cf_2dpVirtXMax );
    cf_2dpLowerRight = cf_2dpLowerRight.yieldMax( cf_2dpVirtZMax );

    cf_2DRect  cf_2drBoundingEllipseRect( cf_2dpUpperLeft,
                                                cf_2dpLowerRight );

    p_bf_dspFromChartView->dsp_ellipse(    cf_2dpUpperLeft,
                                            cf_2dpLowerRight );

    // For debugging purposes it may be useful to uncomment
    // these display lines below...
    /*
    this->cf_3dpXZPlaneEllipseProjXMax.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseProjXMin.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseProjZMax.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseProjZMin.draw3D( p_bf_dspFromChartView );

    this->cf_3dpXZPlaneEllipseVirtXMax.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseVirtXMin.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseVirtZMax.draw3D( p_bf_dspFromChartView );
    this->cf_3dpXZPlaneEllipseVirtZMin.draw3D( p_bf_dspFromChartView );

    this->cf_3dpXZPlaneEllipseCenter.draw3D( p_bf_dspFromChartView );

    p_zapp_dspTemp->dsp_polyline( cf_2drBoundingEllipseRect );
    */

    // 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 );

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



BOOL
cf_eng_Polar::disp2D3DMode
            ( cf_DispMode enumNewDispMode )
{
    BOOL    bStatus = FALSE;
    BOOL    bNeedToReConfig = FALSE;
            // Which can happen, for instance, under repeated
            // (i.e. redundant) calls to the same display-mode:

    if ( enumNewDispMode == CF_DISP_2D )
    {   // The caller must want to go from 3D-mode into 2D-Mode:
        if ( cf_enumCurrentDisplayMode_ == CF_DISP_3D ) bNeedToReConfig = TRUE;

        // Save current view params for subsequent recall:
        dSaveXAngle_ = p_cf_kEnginesKernel_->getXAngle();
        dSaveYAngle_ = p_cf_kEnginesKernel_->getYAngle();
        dSaveZAngle_ = p_cf_kEnginesKernel_->getZAngle();
        dSavePRatio_ = p_cf_kEnginesKernel_->getPerspectiveRatio();

        // Make the switch
        p_cf_kEnginesKernel_->preferViewAngle( 90.0, 0.0, FALSE  );
        p_cf_kEnginesKernel_->preferPerspective( 0.0, TRUE );

        // Record a successful operation:
        cf_enumCurrentDisplayMode_ = CF_DISP_2D;
        bStatus = TRUE;
    }

    if ( enumNewDispMode == CF_DISP_3D )
    {   // The caller must want to go from 2D into 3D-Mode:
        if ( cf_enumCurrentDisplayMode_ == CF_DISP_2D ) bNeedToReConfig = TRUE;

        // Restore any saved view params as the current state:
        p_cf_kEnginesKernel_
                ->preferViewAngle( dSaveXAngle_, dSaveYAngle_, FALSE );
        p_cf_kEnginesKernel_->preferPerspective( dSavePRatio_, TRUE );

        // Record a successful operation:
        cf_enumCurrentDisplayMode_ = CF_DISP_3D;
        bStatus = TRUE;
    }

    // Do we need to force a geometry update to occur?
    if ( bNeedToReConfig == TRUE )
    {
        autoConfigure();
        applyGeomXform();
    }

    return( bStatus );

}   // end of cf_eng_Polar::disp2D3DMode( cf_DispMode enumNewDispMode )


void
cf_eng_Polar::measureVirtOrigin()
{
    // For Polar Coordinate charts -- we need to over-ride
    // the base class functionality and set the chart's local
    // XYZ Origin to be the CENTER of the BOTTOM 3B Box Face:
    dChartXOrigin_ = 0.0;
    dChartYOrigin_ = p_cf_kEnginesKernel_->getYMin();
    dChartZOrigin_ = 0.0;
}

//
// END of cf_eng_Polar Implementation
//


//////////////////////////////////////
// END of File: CE1_PCOR.CPP
//////////////////////////////////////




