/***********************************************************************
*
*       ELMER, A Computational Fluid Dynamics Program.
*
*       Copyright 1st April 1995 - , Center for Scientific Computing,
*                                    Finland.
*
*       All rights reserved. No part of this program may be used,
*       reproduced or transmitted in any form or by any means
*       without the written permission of CSC.
*
*                Address: Center for Scientific Computing
*                         Tietotie 6, P.O. BOX 405
*                         02101 Espoo, Finland
*                         Tel.     +358 0 457 2001
*                         Telefax: +358 0 457 2302
*                         EMail:   Jari.Jarvinen@csc.fi
************************************************************************/

/***********************************************************************
Program:    ELMER Front 
Module:     ecif_boundbox.cpp
Language:   C++
Date:       01.10.98
Version:    1.00
Author(s):  Martti Verho
Revisions:  

Abstract:   Implementation

************************************************************************/

#include <algorithm>

#include "ecif_boundbox.h"
#include "ecif_geometry.h"

BoundBox::BoundBox()
{
  x1 = y1 = z1 = MAX_RANGE;
  x2 = y2 = z2 = MIN_RANGE;
}
 

BoundBox::BoundBox(double min_value, double max_value)
{
  x1 = y1 = z1 = min_value;
  x2 = y2 = z2 = max_value;
}

BoundBox::BoundBox(RangeVector rv)
{
  //x-range
  if (rv[0] <= rv[1]) { 
    x1=rv[0];
    x2=rv[1];
  }
  else {
    x1=rv[1];
    x2=rv[0];
  }
  //y-range
  if (rv[2] <= rv[3]) {
    y1=rv[2];
    y2=rv[3];
  }
  else {
    y1=rv[3];
    y2=rv[2];
  }
  //z-range
  if (rv[4] <= rv[5]) {
    z1=rv[4];
    z2=rv[5];
  }
  else {
    z1=rv[5];
    z2=rv[4];
  }
}


bool 
BoundBox::contains(BoundBox* other_box)
{
  //If this-box contains *other_box*
  return ( x1 <= (other_box->x1 + GAP_TOLERANCE) &&
           x2 >= (other_box->x2 - GAP_TOLERANCE) &&
           y1 <= (other_box->y1 + GAP_TOLERANCE) && 
           y2 >= (other_box->y2 - GAP_TOLERANCE) &&
           z1 <= (other_box->z1 + GAP_TOLERANCE) &&
           z2 >= (other_box->z2 - GAP_TOLERANCE) );
}


//Extend range by point
void
BoundBox::extendByPoint(GcPoint* point)
{
  Point3 p;
  
  point->getPoint(p);
  
  extendByPoint(p);
}


//Extend range by point
void
BoundBox::extendByPoint(Point3 p)
{
  if ( x1 > p[0] )
    x1 = p[0];

  if ( x2 < p[0] )
    x2 = p[0];

  if ( y1 > p[1] )
    y1 = p[1];

  if ( y2 < p[1] )
    y2 = p[1];

  if ( z1 > p[2] )
    z1 = p[2];

  if ( z2 < p[2] )
    z2 = p[2];
}


//Extend range by new coordinates
//Note: argument *rv* must be ordered properly!
void
BoundBox::extendByRange(RangeVector rv)
{
  // Update maximum-box values
  setMaximumPair(x1, x2, rv[0], rv[1]);
  setMaximumPair(y1, y2, rv[2], rv[3]);
  setMaximumPair(z1, z2, rv[4], rv[5]);
}


// NOTE! Works currently only for 2D!!
// Method gets a bounding box axis as corner values.
// Corners are numbered ccw starting from the near sw-corner.
// z=0: 1,2,3,4; z=1: 5,6,7,8
//
void
BoundBox::getBoxAxis(int crn1, int crn2, double* start, double* end1, double* end2)
{
  // Deafaults for 2D
  start[2] = end1[2] = 0.0;
  end2[0] = end2[1] = end2[2] = 0.0;

  // Start corner
  switch (crn1) {
  case 1:
    start[0] = x1; start[1] = y1; break;
  case 2:
    start[0] = x1; start[1] = y2; break;
  case 3:
    start[0] = x2; start[1] = y2; break;
  case 4:
    start[0] = x2; start[1] = y1; break;
  }
  // End corner
  switch (crn2) {
  case 1:
    end1[0] = x1; end1[1] = y1; break;
  case 2:
    end1[0] = x1; end1[1] = y2; break;
  case 3:
    end1[0] = x2; end1[1] = y2; break;
  case 4:
    end1[0] = x2; end1[1] = y1; break;
  }
}


// Get box xyz-dimensions
void
BoundBox::getSize(double& dim_x, double& dim_y, double& dim_z)
{
  dim_x = x2 - x1;
  dim_y = y2 - y1;
  dim_z = z2 - z1;
}


void
BoundBox::getRangeVector(RangeVector rv)
{
  rv[0] = x1; rv[1] = x2;
  rv[2] = y1; rv[3] = y2;
  rv[4] = z1; rv[5] = z2;
}


ostream& 
BoundBox::output(ostream& out) 
{
  out << "(" << x1 << ", " << x2 << ") ";
  out << "(" << y1 << ", " << y2 << ") ";
  out << "(" << z1 << ", " << z2 << ") ";
  
  return out;
}


bool 
BoundBox::overlap(BoundBox* other_box)
{
  using namespace std;

  double xd = min(x2, other_box->x2) - max(x1, other_box->x1);
  double yd = min(y2, other_box->y2) - max(y1, other_box->y1);
  double zd = min(z2, other_box->z2) - max(z1, other_box->z1);

  double mindif = min(min(xd,yd),zd);
  double maxdif = max(max(xd,yd),zd);

  return ( (mindif > -1 * GAP_TOLERANCE)  && (maxdif > GAP_TOLERANCE) );

}


// Restrict minimum-pair box by new coordinates
// This type of box is maintaining two smallest values
// for vertex-values for each coordinate
// First of values in each pair is always the smaller.
void
BoundBox::restrictByRange(RangeVector rv)
{ 
  // Update box-values.
  setMinimumPair(x1, x2, rv[0], rv[1]);
  setMinimumPair(y1, y2, rv[2], rv[3]);
  setMinimumPair(z1, z2, rv[4], rv[5]);
}


// NOTE: We suppouse that c1,c2 is ordered pair!!!
void
BoundBox::setMaximumPair( double& c1, double& c2, double r1, double r2)
{
  if ( isGreater(r1, r2) ) {
    double tmp = r1;
    r1 = r2;
    r2 = tmp;
  }

  // Lower value
  if ( r1 != -NSVD && r1 != NSVD && r1 < c1) 
    c1 = r1;

  // Upper value
  if ( r2 != -NSVD && r2 != NSVD && r2 > c2) 
    c2 = r2;
}


// NOTE: We suppouse that c1,c2 is ordered pair!!!
void
BoundBox::setMinimumPair(double& c1, double& c2, double r1, double r2)
{
  if ( isGreater(r1, r2) ) {
    double tmp = r1;
    r1 = r2;
    r2 = tmp;
  }

  // At first we select the lowest value.
  double tmp = r1;
  if ( isEqual(c1,NSVD) || isLess(r1,c1) )  {
    tmp = c1;
    c1 = r1;
  }

  // If c1 was changed and it originally was unitialized
  // we have to change the value of *tmp*
  if ( isEqual(tmp,NSVD) ) {
    tmp = r2;
  }

  // Now we have to select the lowest of the remaining three, such
  // that it is different from *c1*.
  if ( !isEqual(tmp,c1) && isLess(tmp,c2) ) {
    c2 = tmp;
  }

  if ( !isEqual(r2,c1) && isLess(r2,c2) ) {
    c2 = r2;
  }
}
