//      Routines for setting up world coordinates of the current viewport.
//
// Copyright (C) 2004-2014 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; either version 2 of the License, or
// (at your option) any later version.
//
// 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
//

#include "plplotP.h"

#define  dtr    0.01745329252

//--------------------------------------------------------------------------
// void plwind()
//
// Set up world coordinates of the viewport boundaries (2d plots).
//--------------------------------------------------------------------------

void
c_plwind( PLFLT xmin, PLFLT xmax, PLFLT ymin, PLFLT ymax )
{
    PLFLT    dx, dy, mmxmi, mmxma, mmymi, mmyma;
    PLFLT    xvpwxmin, xvpwxmax, xvpwymin, xvpwymax;
    PLWindow w;

    if ( plsc->level < 2 )
    {
        plabort( "plwind: Please set up viewport first" );
        return;
    }

// Best to just warn and recover on bounds errors

    if ( xmin == xmax )
    {
        plwarn( "plwind: Invalid window limits in x." );
        xmin--; xmax++;
    }
    if ( ymin == ymax )
    {
        plwarn( "plwind: Invalid window limits in y." );
        ymin--; ymax++;
    }

    plsc->vpwxmi = xmin;
    plsc->vpwxma = xmax;
    plsc->vpwymi = ymin;
    plsc->vpwyma = ymax;

// The true plot window is made slightly larger than requested so that
// the end limits will be on the graph
// Get the (slightly extended) window limits.
    plP_xgvpw( &xvpwxmin, &xvpwxmax, &xvpwymin, &xvpwymax );

// Compute the scaling between coordinate systems

    dx = xvpwxmax - xvpwxmin;
    dy = xvpwymax - xvpwymin;

    plsc->wpxscl = ( plsc->vppxma - plsc->vppxmi ) / dx;
    plsc->wpxoff = ( xmax * plsc->vppxmi - xmin * plsc->vppxma ) / dx;
    plsc->wpyscl = ( plsc->vppyma - plsc->vppymi ) / dy;
    plsc->wpyoff = ( ymax * plsc->vppymi - ymin * plsc->vppyma ) / dy;

    mmxmi = plP_dcmmx( plsc->vpdxmi );
    mmxma = plP_dcmmx( plsc->vpdxma );
    mmymi = plP_dcmmy( plsc->vpdymi );
    mmyma = plP_dcmmy( plsc->vpdyma );

// Set transformation variables for world coordinates to mm

    plsc->wmxscl = ( mmxma - mmxmi ) / dx;
    plsc->wmxoff = ( xmax * mmxmi - xmin * mmxma ) / dx;
    plsc->wmyscl = ( mmyma - mmymi ) / dy;
    plsc->wmyoff = ( ymax * mmymi - ymin * mmyma ) / dy;

// Set transformation variables for world coordinates to device coords

    plsc->wdxscl = plsc->wmxscl * plsc->xpmm / ( plsc->phyxma - plsc->phyxmi );
    plsc->wdxoff = plsc->wmxoff * plsc->xpmm / ( plsc->phyxma - plsc->phyxmi );
    plsc->wdyscl = plsc->wmyscl * plsc->ypmm / ( plsc->phyyma - plsc->phyymi );
    plsc->wdyoff = plsc->wmyoff * plsc->ypmm / ( plsc->phyyma - plsc->phyymi );

// Register plot window attributes

    w.dxmi = plsc->vpdxmi;
    w.dxma = plsc->vpdxma;
    w.dymi = plsc->vpdymi;
    w.dyma = plsc->vpdyma;

    w.wxmi = xvpwxmin;
    w.wxma = xvpwxmax;
    w.wymi = xvpwymin;
    w.wyma = xvpwymax;

    plP_swin( &w );

// Go to level 3

    plsc->level = 3;
}

//--------------------------------------------------------------------------
// void plw3d()
//
// Set up a window for three-dimensional plotting. The data are mapped
// into a box with world coordinate size "basex" by "basey" by "height",
// with the base being symmetrically positioned about zero. Thus
// the mapping between data 3-d and world 3-d coordinates is given by:
//
//   x = xmin   =>   wx = -0.5*basex
//   x = xmax   =>   wx =  0.5*basex
//   y = ymin   =>   wy = -0.5*basey
//   y = ymax   =>   wy =  0.5*basey
//   z = zmin   =>   wz =  0.0
//   z = zmax   =>   wz =  height
//
// The world coordinate box is then viewed from position "alt"-"az",
// measured in degrees. For proper operation, 0 <= alt <= 90 degrees,
// but az can be any value.
//--------------------------------------------------------------------------

void
c_plw3d( PLFLT basex, PLFLT basey, PLFLT height, PLFLT xmin,
         PLFLT xmax, PLFLT ymin, PLFLT ymax, PLFLT zmin,
         PLFLT zmax, PLFLT alt, PLFLT az )
{
    PLFLT xmin_adjusted, xmax_adjusted, ymin_adjusted, ymax_adjusted, zmin_adjusted, zmax_adjusted, d;
    PLFLT cx, cy, saz, caz, salt, calt, zscale;

    if ( plsc->level < 3 )
    {
        plabort( "plw3d: Please set up 2-d window first" );
        return;
    }
    if ( basex <= 0.0 || basey <= 0.0 || height <= 0.0 )
    {
        plabort( "plw3d: Invalid world coordinate boxsize" );
        return;
    }
    if ( xmin == xmax || ymin == ymax || zmin == zmax )
    {
        plabort( "plw3d: Invalid axis range" );
        return;
    }
    if ( alt < 0.0 || alt > 90.0 )
    {
        plabort( "plw3d: Altitude must be between 0 and 90 degrees" );
        return;
    }

    d             = 1.0e-5 * ( xmax - xmin );
    xmax_adjusted = xmax + d;
    xmin_adjusted = xmin - d;
    d             = 1.0e-5 * ( ymax - ymin );
    ymax_adjusted = ymax + d;
    ymin_adjusted = ymin - d;
    d             = 1.0e-5 * ( zmax - zmin );
    zmax_adjusted = zmax + d;
    zmin_adjusted = zmin - d;
    cx            = basex / ( xmax_adjusted - xmin_adjusted );
    cy            = basey / ( ymax_adjusted - ymin_adjusted );
    zscale        = height / ( zmax_adjusted - zmin_adjusted );
    saz           = sin( dtr * az );
    caz           = cos( dtr * az );
    salt          = sin( dtr * alt );
    calt          = cos( dtr * alt );

    plsc->domxmi = xmin_adjusted;
    plsc->domxma = xmax_adjusted;
    plsc->domymi = ymin_adjusted;
    plsc->domyma = ymax_adjusted;
    plsc->zzscl  = zscale;
    plsc->ranmi  = zmin_adjusted;
    plsc->ranma  = zmax_adjusted;

    plsc->base3x = basex;
    plsc->base3y = basey;
    plsc->basecx = 0.5 * ( xmin_adjusted + xmax_adjusted );
    plsc->basecy = 0.5 * ( ymin_adjusted + ymax_adjusted );
// Mathematical explanation of the 3 transformations of coordinates:
// (I) Scaling:
//     x' = cx*(x-x_mid) = cx*(x-plsc->basecx)
//     y' = cy*(y-y_mid) = cy*(y-plsc->basecy)
//     z' = zscale*(z-zmin_adjusted) = zscale*(z-plsc->ranmi)
// (II) Rotation about z' axis clockwise by the angle of the azimut when
//      looking from the top in a right-handed coordinate system.
//     x''          x'
//     y'' =  M_1 * y'
//     z''          z'
//    where the rotation matrix M_1 (see any mathematical physics book such
//    as Mathematical Methods in the Physical Sciences by Boas) is
//    caz          -saz       0
//    saz           caz       0
//     0             0        1
// (III) Rotation about x'' axis by 90 deg - alt to bring z''' axis
//      coincident with line of sight and x''' and y''' corresponding to
//      x and y coordinates in the 2D plane of the plot.
//     x'''          x''
//     y''' =  M_2 * y''
//     z'''          z''
//    where the rotation matrix M_2 is
//     1            0         0
//     0           salt      calt
//     0          -calt      salt
// Note
//     x'''          x'
//     y''' =  M *   y'
//     z'''          z'
// where M = M_2*M_1 is given by
//          caz      -saz     0
//     salt*saz  salt*caz    calt
//    -calt*saz -calt*caz    salt
// plP_w3wcx and plP_w3wcy take the combination of the plsc->basecx,
// plsc->basecy, plsc->ranmi, plsc->cxx, plsc->cxy, plsc->cyx, plsc->cyy, and
// plsc->cyz data stored here to implement the combination of the 3
// transformations to determine x''' and y''' from x, y, and z.
//
    plsc->cxx = cx * caz;
    plsc->cxy = -cy * saz;
    plsc->cyx = cx * saz * salt;
    plsc->cyy = cy * caz * salt;
    plsc->cyz = zscale * calt;
    plsc->czx = -cx * calt * saz;
    plsc->czy = -cy * calt * caz;
    plsc->czz = zscale * salt;
}
