//--------------------------------------------------------------------------
// Copyright (C) 2004  Andrew Ross
// Copyright (C) 2004  Alan W. Irwin
//
// This file is part of PLplot.
//
// PLplot is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// PLplot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with PLplot; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
// Implementation of PLplot example 16 in C++.
//--------------------------------------------------------------------------

#include "plc++demos.h"

#ifdef PL_USE_NAMESPACE
using namespace std;
#endif

class x16 {
public:
    x16( int, const char ** );

private:
    // Class data
    plstream *pls;

    // number of shade levels, x data points, y data points,
    // and perimeter points.
    static int           ns;
    static int           nx;
    static int           ny;
    static int           exclude;
    static int           colorbar;
    const static int     PERIMETERPTS;
public:
    static PLFLT         tr[];

    static PLOptionTable options[];
    static const char    *notes[];
};

int       x16::      ns       = 20;
int       x16::      nx       = 35;
int       x16::      ny       = 46;
int       x16::      exclude  = 0;
int       x16::      colorbar = 1;
const int x16::PERIMETERPTS   = 100;

PLFLT     x16::    tr[] = { 2. / ( nx - 1 ), 0.0, -1.0, 0.0, 2. / ( ny - 1 ), -1.0 };


// Options data structure definition.

PLOptionTable x16::options[] = {
    {
        "exclude",              // Turns on page showing exclusion
        NULL,
        NULL,
        &exclude,
        PL_OPT_BOOL,
        "-exclude",
        "Plot the \"exclusion\" page."
    },
    {
        "ns",                   // Number of shade levels
        NULL,
        NULL,
        &ns,
        PL_OPT_INT,
        "-ns levels",
        "Sets number of shade levels"
    },
    {
        "nx",                   // Number of data points in x
        NULL,
        NULL,
        &nx,
        PL_OPT_INT,
        "-nx xpts",
        "Sets number of data points in x"
    },
    {
        "ny",                   // Number of data points in y
        NULL,
        NULL,
        &ny,
        PL_OPT_INT,
        "-ny ypts",
        "Sets number of data points in y"
    },
    {
        NULL,                   // option
        NULL,                   // handler
        NULL,                   // client data
        NULL,                   // address of variable to set
        0,                      // mode flag
        NULL,                   // short syntax
        NULL
    }                           // long syntax
};

const char    *x16::  notes[] = {
    "To get smoother color variation, increase ns, nx, and ny.  To get faster",
    "response (especially on a serial link), decrease them.  A decent but quick",
    "test results from ns around 5 and nx, ny around 25.",
    NULL
};

// Transformation function

static void mypltr( PLFLT x, PLFLT y, PLFLT *tx, PLFLT *ty, void * /* pltr_data */ )
{
    *tx = x16::tr[0] * x + x16::tr[1] * y + x16::tr[2];
    *ty = x16::tr[3] * x + x16::tr[4] * y + x16::tr[5];
}

PLINT zdefined( PLFLT x, PLFLT y )
{
    PLFLT z = sqrt( x * x + y * y );

    return z < 0.4 || z > 0.6;
}

x16::x16( int argc, const char ** argv )
{
    int         i, j;
    PLFLT       x, y, argx, argy, distort, r, t, zmin, zmax;
    PLFLT       *px  = new PLFLT[PERIMETERPTS];
    PLFLT       *py  = new PLFLT[PERIMETERPTS];
    PLFLT       *xg1 = new PLFLT[nx];
    PLFLT       *yg1 = new PLFLT[ny];
    PLFLT       **z;
    PLFLT       **w;
    PLcGrid     cgrid1;
    PLcGrid2    cgrid2;
    PLFLT       *clevel    = new PLFLT[ns];
    PLFLT       *shedge    = new PLFLT[ns + 1];
    const int   cont_color = 0;
    const PLFLT fill_width = 2.0, cont_width = 0.0;
    PLFLT       colorbar_width, colorbar_height;

#define NUM_AXES    1
    PLINT      n_axis_opts  = NUM_AXES;
    const char *axis_opts[] = {
        "bcvtm",
    };
    PLINT      num_values[NUM_AXES];
    PLFLT      *values[NUM_AXES];
    PLFLT      axis_ticks[NUM_AXES] = {
        0.0,
    };
    PLINT      axis_subticks[NUM_AXES] = {
        0,
    };
#define NUM_LABELS    1
    PLINT      n_labels     = NUM_LABELS;
    PLINT      label_opts[] = {
        PL_COLORBAR_LABEL_BOTTOM,
    };
    const char *labels[] = {
        "Magnitude",
    };

    // plplot initialization

    pls = new plstream();

    // Parse and process command line arguments.
    pls->MergeOpts( options, "x16c options", notes );
    pls->parseopts( &argc, argv, PL_PARSE_FULL );

    // Load colour palettes
    pls->spal0( "cmap0_black_on_white.pal" );
    pls->spal1( "cmap1_gray.pal", true );

    // Reduce colors in cmap 0 so that cmap 1 is useful on a
    //16-color display
    pls->scmap0n( 3 );


    // Initialize PLplot.
    pls->init();

    pls->Alloc2dGrid( &z, nx, ny );
    pls->Alloc2dGrid( &w, nx, ny );

    // Set up data array

    for ( i = 0; i < nx; i++ )
    {
        x = (PLFLT) ( i - ( nx / 2 ) ) / (PLFLT) ( nx / 2 );
        for ( j = 0; j < ny; j++ )
        {
            y = (PLFLT) ( j - ( ny / 2 ) ) / (PLFLT) ( ny / 2 ) - 1.0;

            z[i][j] = -sin( 7. * x ) * cos( 7. * y ) + x * x - y * y;
            w[i][j] = -cos( 7. * x ) * sin( 7. * y ) + 2 * x * y;
        }
    }

    pls->MinMax2dGrid( z, nx, ny, &zmax, &zmin );

    for ( i = 0; i < ns; i++ )
        clevel[i] = zmin + ( zmax - zmin ) * ( i + 0.5 ) / (PLFLT) ns;

    for ( i = 0; i < ns + 1; i++ )
        shedge[i] = zmin + ( zmax - zmin ) * (PLFLT) i / (PLFLT) ns;

// Set up coordinate grids

    cgrid1.xg = xg1;
    cgrid1.yg = yg1;
    cgrid1.nx = nx;
    cgrid1.ny = ny;

    pls->Alloc2dGrid( &cgrid2.xg, nx, ny );
    pls->Alloc2dGrid( &cgrid2.yg, nx, ny );
    cgrid2.nx = nx;
    cgrid2.ny = ny;

    for ( i = 0; i < nx; i++ )
    {
        for ( j = 0; j < ny; j++ )
        {
            mypltr( (PLFLT) i, (PLFLT) j, &x, &y, NULL );

            argx    = x * M_PI / 2;
            argy    = y * M_PI / 2;
            distort = 0.4;

            cgrid1.xg[i] = x + distort * cos( argx );
            cgrid1.yg[j] = y - distort * cos( argy );

            cgrid2.xg[i][j] = x + distort * cos( argx ) * cos( argy );
            cgrid2.yg[i][j] = y - distort * cos( argx ) * cos( argy );
        }
    }

    // Plot using identity transform

    pls->adv( 0 );
    pls->vpor( 0.1, 0.9, 0.1, 0.9 );
    pls->wind( -1.0, 1.0, -1.0, 1.0 );

    pls->psty( 0 );

    pls->shades( z, nx, ny, NULL, -1., 1., -1., 1.,
        shedge, ns + 1, fill_width,
        cont_color, cont_width,
        plcallback::fill, true, NULL, NULL );

    if ( colorbar )
    {
        // Smaller text
        pls->schr( 0.0, 0.75 );
        // Small ticks on the vertical axis
        pls->smaj( 0.0, 0.5 );
        pls->smin( 0.0, 0.5 );

        num_values[0] = ns + 1;
        values[0]     = shedge;
        pls->colorbar( &colorbar_width, &colorbar_height,
            PL_COLORBAR_SHADE | PL_COLORBAR_SHADE_LABEL, 0,
            0.005, 0.0, 0.0375, 0.875, 0, 1, 1, 0.0, 0.0,
            cont_color, cont_width,
            n_labels, label_opts, labels,
            n_axis_opts, axis_opts,
            axis_ticks, axis_subticks,
            num_values, (const PLFLT * const *) values );

        // Reset text and tick sizes
        pls->schr( 0.0, 1.0 );
        pls->smaj( 0.0, 1.0 );
        pls->smin( 0.0, 1.0 );
    }

    pls->col0( 1 );
    pls->box( "bcnst", 0.0, 0, "bcnstv", 0.0, 0 );
    pls->col0( 2 );
    pls->lab( "distance", "altitude", "Bogon density" );

    // Plot using 1d coordinate transform

    pls->spal0( "cmap0_black_on_white.pal" );
    pls->spal1( "cmap1_blue_yellow.pal", true );
    pls->scmap0n( 3 );

    pls->adv( 0 );
    pls->vpor( 0.1, 0.9, 0.1, 0.9 );
    pls->wind( -1.0, 1.0, -1.0, 1.0 );

    pls->psty( 0 );

    pls->shades( z, nx, ny, NULL, -1., 1., -1., 1.,
        shedge, ns + 1, fill_width,
        cont_color, cont_width,
        plcallback::fill, true, plcallback::tr1, (void *) &cgrid1 );

    if ( colorbar )
    {
        // Smaller text
        pls->schr( 0.0, 0.75 );
        // Small ticks on the vertical axis
        pls->smaj( 0.0, 0.5 );
        pls->smin( 0.0, 0.5 );

        num_values[0] = ns + 1;
        values[0]     = shedge;
        pls->colorbar( &colorbar_width, &colorbar_height,
            PL_COLORBAR_SHADE | PL_COLORBAR_SHADE_LABEL, 0,
            0.005, 0.0, 0.0375, 0.875, 0, 1, 1, 0.0, 0.0,
            cont_color, cont_width,
            n_labels, label_opts, labels,
            n_axis_opts, axis_opts,
            axis_ticks, axis_subticks,
            num_values, (const PLFLT * const *) values );

        // Reset text and tick sizes
        pls->schr( 0.0, 1.0 );
        pls->smaj( 0.0, 1.0 );
        pls->smin( 0.0, 1.0 );
    }

    pls->col0( 1 );
    pls->box( "bcnst", 0.0, 0, "bcnstv", 0.0, 0 );
    pls->col0( 2 );

    pls->lab( "distance", "altitude", "Bogon density" );

    // Plot using 2d coordinate transform

    pls->spal0( "cmap0_black_on_white.pal" );
    pls->spal1( "cmap1_blue_red.pal", true );
    pls->scmap0n( 3 );

    pls->adv( 0 );
    pls->vpor( 0.1, 0.9, 0.1, 0.9 );
    pls->wind( -1.0, 1.0, -1.0, 1.0 );

    pls->psty( 0 );

    pls->shades( z, nx, ny, NULL, -1., 1., -1., 1.,
        shedge, ns + 1, fill_width,
        cont_color, cont_width,
        plcallback::fill, false, plcallback::tr2, (void *) &cgrid2 );

    if ( colorbar )
    {
        // Smaller text
        pls->schr( 0.0, 0.75 );
        // Small ticks on the vertical axis
        pls->smaj( 0.0, 0.5 );
        pls->smin( 0.0, 0.5 );

        num_values[0] = ns + 1;
        values[0]     = shedge;
        pls->colorbar( &colorbar_width, &colorbar_height,
            PL_COLORBAR_SHADE | PL_COLORBAR_SHADE_LABEL, 0,
            0.005, 0.0, 0.0375, 0.875, 0, 1, 1, 0.0, 0.0,
            cont_color, cont_width,
            n_labels, label_opts, labels,
            n_axis_opts, axis_opts,
            axis_ticks, axis_subticks,
            num_values, (const PLFLT * const *) values );

        // Reset text and tick sizes
        pls->schr( 0.0, 1.0 );
        pls->smaj( 0.0, 1.0 );
        pls->smin( 0.0, 1.0 );
    }

    pls->col0( 1 );
    pls->box( "bcnst", 0.0, 0, "bcnstv", 0.0, 0 );
    pls->col0( 2 );
    pls->cont( w, nx, ny, 1, nx, 1, ny, clevel, ns, plcallback::tr2, (void *) &cgrid2 );

    pls->lab( "distance", "altitude", "Bogon density, with streamlines" );

    // Plot using 2d coordinate transform

    pls->spal0( "" );
    pls->spal1( "", true );
    pls->scmap0n( 3 );

    pls->adv( 0 );
    pls->vpor( 0.1, 0.9, 0.1, 0.9 );
    pls->wind( -1.0, 1.0, -1.0, 1.0 );

    pls->psty( 0 );

    pls->shades( z, nx, ny, NULL, -1., 1., -1., 1.,
        shedge, ns + 1, fill_width,
        2, 3.0,
        plcallback::fill, false, plcallback::tr2, (void *) &cgrid2 );

    if ( colorbar )
    {
        // Smaller text
        pls->schr( 0.0, 0.75 );
        // Small ticks on the vertical axis
        pls->smaj( 0.0, 0.5 );
        pls->smin( 0.0, 0.5 );

        num_values[0] = ns + 1;
        values[0]     = shedge;
        pls->colorbar( &colorbar_width, &colorbar_height,
            PL_COLORBAR_SHADE | PL_COLORBAR_SHADE_LABEL, 0,
            0.005, 0.0, 0.0375, 0.875, 0, 1, 1, 0.0, 0.0,
            2, 3,
            n_labels, label_opts, labels,
            n_axis_opts, axis_opts,
            axis_ticks, axis_subticks,
            num_values, (const PLFLT * const *) values );

        // Reset text and tick sizes
        pls->schr( 0.0, 1.0 );
        pls->smaj( 0.0, 1.0 );
        pls->smin( 0.0, 1.0 );
    }

    pls->col0( 1 );
    pls->box( "bcnst", 0.0, 0, "bcnstv", 0.0, 0 );
    pls->col0( 2 );

    pls->lab( "distance", "altitude", "Bogon density" );

    // Note this exclusion API will probably change
    // Plot using 2d coordinate transform and exclusion

    if ( exclude )
    {
        pls->spal0( "cmap0_black_on_white.pal" );
        pls->spal1( "cmap1_gray.pal", true );
        pls->scmap0n( 3 );

        pls->adv( 0 );
        pls->vpor( 0.1, 0.9, 0.1, 0.9 );
        pls->wind( -1.0, 1.0, -1.0, 1.0 );

        pls->psty( 0 );

        pls->shades( z, nx, ny, zdefined, -1., 1., -1., 1.,
            shedge, ns + 1, fill_width,
            cont_color, cont_width,
            plcallback::fill, false, plcallback::tr2, (void *) &cgrid2 );

        pls->col0( 1 );
        pls->box( "bcnst", 0.0, 0, "bcnstv", 0.0, 0 );

        pls->lab( "distance", "altitude", "Bogon density with exclusion" );
    }

    //Example with polar coordinates.

    pls->spal0( "cmap0_black_on_white.pal" );
    pls->spal1( "cmap1_gray.pal", true );
    pls->scmap0n( 3 );

    pls->adv( 0 );
    pls->vpor( .1, .9, .1, .9 );
    pls->wind( -1., 1., -1., 1. );

    pls->psty( 0 );

    // Build new coordinate matrices.

    for ( i = 0; i < nx; i++ )
    {
        r = ( (PLFLT) i ) / ( nx - 1 );
        for ( j = 0; j < ny; j++ )
        {
            t = ( 2. * M_PI / ( ny - 1. ) ) * j;
            cgrid2.xg[i][j] = r * cos( t );
            cgrid2.yg[i][j] = r * sin( t );
            z[i][j]         = exp( -r * r ) * cos( 5. * M_PI * r ) * cos( 5. * t );
        }
    }

    //Need a new shedge to go along with the new data set.

    pls->MinMax2dGrid( z, nx, ny, &zmax, &zmin );

    for ( i = 0; i < ns + 1; i++ )
        shedge[i] = zmin + ( zmax - zmin ) * (PLFLT) i / (PLFLT) ns;

    pls->shades( z, nx, ny, NULL, -1., 1., -1., 1.,
        shedge, ns + 1, fill_width,
        cont_color, cont_width,
        plcallback::fill, false, plcallback::tr2, (void *) &cgrid2 );

    if ( colorbar )
    {
        // Smaller text
        pls->schr( 0.0, 0.75 );
        // Small ticks on the vertical axis
        pls->smaj( 0.0, 0.5 );
        pls->smin( 0.0, 0.5 );

        num_values[0] = ns + 1;
        values[0]     = shedge;
        pls->colorbar( &colorbar_width, &colorbar_height,
            PL_COLORBAR_SHADE | PL_COLORBAR_SHADE_LABEL, 0,
            0.005, 0.0, 0.0375, 0.875, 0, 1, 1, 0.0, 0.0,
            cont_color, cont_width,
            n_labels, label_opts, labels,
            n_axis_opts, axis_opts,
            axis_ticks, axis_subticks,
            num_values, (const PLFLT * const *) values );

        // Reset text and tick sizes
        pls->schr( 0.0, 1.0 );
        pls->smaj( 0.0, 1.0 );
        pls->smin( 0.0, 1.0 );
    }

    // Now we can draw the perimeter.  (If do before, shade stuff may overlap.)
    for ( i = 0; i < PERIMETERPTS; i++ )
    {
        t     = ( 2. * M_PI / ( PERIMETERPTS - 1 ) ) * (PLFLT) i;
        px[i] = cos( t );
        py[i] = sin( t );
    }
    pls->col0( 1 );
    pls->line( PERIMETERPTS, px, py );

    // And label the plot.

    pls->col0( 2 );
    pls->lab( "", "", "Tokamak Bogon Instability" );

    // Clean up

    // pls->end();

    pls->Free2dGrid( z, nx, ny );
    pls->Free2dGrid( w, nx, ny );
    pls->Free2dGrid( cgrid2.xg, nx, ny );
    pls->Free2dGrid( cgrid2.yg, nx, ny );

    delete[] px;
    delete[] py;
    delete[] xg1;
    delete[] yg1;
    delete[] clevel;
    delete[] shedge;
    delete pls;
}

int main( int argc, const char ** argv )
{
    x16 *x = new x16( argc, argv );

    delete x;
}


//--------------------------------------------------------------------------
//                              End of x16.cc
//--------------------------------------------------------------------------
