/* Surface
 * (C) Copyright 1995 by Ashton Mason (amason@cs.uct.ac.za)
 *
 * Permission to use, modify, copy and distribute this source code for
 * any purpose and without fee is granted, provided that this copyright
 * notice appears in all copies and supporting documentation, and that
 * credit is given where due. This source code is provided "as is" with
 * no express or implied warranty.
 */


#include "headers.h"


	// constructor


value::value(void)
{
}


	// constructor


value::value(vector val)
{
  type = v3d;
  value3d = val;
}


	// constructor


value::value(float val)
{
  type = v1d;
  value1d = val;
}


	// output a value


int value::output(char *name)
{
  printf("%s: ", name);
  switch (type)
  {
    case v1d:
      printf("%.2f\n", value1d);
      break;

    case v3d:
      printf("<%.2f,%.2f,%.2f>\n", value3d.x, value3d.y, value3d.z);
      break;

    default:
      error("unknown type - program error");
      return 0;
  }

  return 1;
}


	// add two values
        // works for any combination of input types


value operator+(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d + b.value1d;
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d = b.value3d + a.value1d;
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d = a.value3d + b.value1d;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d = a.value3d + b.value3d;
      return result;
  }

  return result;
}


	// subtract two values


value operator-(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d - b.value1d;
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      vector zero(0.0, 0.0, 0.0);
      result.value3d = (zero - b.value3d) + a.value1d;
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d = a.value3d - b.value1d;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d = a.value3d - b.value3d;
      return result;
  }

  return result;
}


	// multiply two values


value operator*(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d * b.value1d;
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d = b.value3d * a.value1d;
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d = a.value3d * b.value1d;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d = a.value3d * b.value3d;
      return result;
  }

  return result;
}


	// divide two values


value operator/(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d / b.value1d;
      return result;

    case 5:				// v1d, v3d
      error("can't divide a number by a vector");

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d = a.value3d / b.value1d;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d = a.value3d / b.value3d;
      return result;
  }

  return result;
}


	// dot product of two values


value dotval(value &a, value &b)
{
  value result;

  if (a.type != v3d || b.type != v3d)
  {
   error("operands of dot product must be vectors");
  }

  result.type = v1d;
  result.value1d = dot(a.value3d, b.value3d);

  return result;
}


	// cross product of two values


value crossval(value &a, value &b)
{
  value result;

  if (a.type != v3d || b.type != v3d)
  {
   error("operands of cross product must be vectors");
  }

  result.type = v3d;

  	// doing cross product ourselves - vector.h may be other-handed

  result.value3d.x = a.value3d.z * b.value3d.y - a.value3d.y * b.value3d.z;
  result.value3d.y = a.value3d.x * b.value3d.z - a.value3d.z * b.value3d.x;
  result.value3d.z = a.value3d.y * b.value3d.x - a.value3d.x * b.value3d.y;

  return result;
}


	// sin of a value


value sinval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = sin(a.value3d.x);
    result.value3d.y = sin(a.value3d.y);
    result.value3d.z = sin(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = sin(a.value1d);
  return result;
}


	// cos of a value


value cosval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = cos(a.value3d.x);
    result.value3d.y = cos(a.value3d.y);
    result.value3d.z = cos(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = cos(a.value1d);
  return result;
}


	// tan of a value


value tanval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = tan(a.value3d.x);
    result.value3d.y = tan(a.value3d.y);
    result.value3d.z = tan(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = tan(a.value1d);
  return result;
}


	// asin of a value


value asinval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = asin(a.value3d.x);
    result.value3d.y = asin(a.value3d.y);
    result.value3d.z = asin(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = asin(a.value1d);
  return result;
}


	// acos of a value


value acosval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = acos(a.value3d.x);
    result.value3d.y = acos(a.value3d.y);
    result.value3d.z = acos(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = acos(a.value1d);
  return result;
}


	// atan of a value


value atanval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = atan(a.value3d.x);
    result.value3d.y = atan(a.value3d.y);
    result.value3d.z = atan(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = atan(a.value1d);
  return result;
}


	// create new vector with supplied coords


value create(value &arg1, value &arg2, value &arg3)
{
  value result;

  result.type = v3d;

  if (arg1.type != v1d || arg2.type != v1d || arg3.type != v1d)
  {
    error("3 numbers expected as arguments of create");
  }

  result.value3d.set(arg1.value1d, arg2.value1d, arg3.value1d);
  return result;
}


value operator==(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d == b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d == b.value3d.x &&
                        a.value1d == b.value3d.y &&
			a.value1d == b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x == b.value1d &&
                        a.value3d.y == b.value1d &&
			a.value3d.z == b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x == b.value3d.x &&
                        a.value3d.y == b.value3d.y &&
			a.value3d.z == b.value3d.z);
      return result;
  }

  return result;
}


value operator!=(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d != b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d != b.value3d.x ||
                        a.value1d != b.value3d.y ||
			a.value1d != b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x != b.value1d ||
                        a.value3d.y != b.value1d ||
			a.value3d.z != b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x != b.value3d.x ||
                        a.value3d.y != b.value3d.y ||
			a.value3d.z != b.value3d.z);
      return result;
  }

  return result;
}


	// less than for values


value operator<(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d < b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d < b.value3d.x &&
                        a.value1d < b.value3d.y &&
			a.value1d < b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x < b.value1d &&
                        a.value3d.y < b.value1d &&
			a.value3d.z < b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x < b.value3d.x &&
                        a.value3d.y < b.value3d.y &&
			a.value3d.z < b.value3d.z);
      return result;
  }

  return result;
}


value operator>(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d > b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d > b.value3d.x &&
                        a.value1d > b.value3d.y &&
			a.value1d > b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x > b.value1d &&
                        a.value3d.y > b.value1d &&
			a.value3d.z > b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x > b.value3d.x &&
                        a.value3d.y > b.value3d.y &&
			a.value3d.z > b.value3d.z);
      return result;
  }

  return result;
}


value operator<=(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d <= b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d <= b.value3d.x &&
                        a.value1d <= b.value3d.y &&
			a.value1d <= b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x <= b.value1d &&
                        a.value3d.y <= b.value1d &&
			a.value3d.z <= b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x <= b.value3d.x &&
                        a.value3d.y <= b.value3d.y &&
			a.value3d.z <= b.value3d.z);
      return result;
  }

  return result;
}


value operator>=(value &a, value &b)
{
  value result;
  result.type = v1d;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.value1d = (a.value1d >= b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.value1d = (a.value1d >= b.value3d.x &&
                        a.value1d >= b.value3d.y &&
			a.value1d >= b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.value1d = (a.value3d.x >= b.value1d &&
                        a.value3d.y >= b.value1d &&
			a.value3d.z >= b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.value1d = (a.value3d.x >= b.value3d.x &&
                        a.value3d.y >= b.value3d.y &&
			a.value3d.z >= b.value3d.z);
      return result;
  }

  return result;
}


value rem(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = fmod(a.value1d, b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d.x = fmod(a.value1d, b.value3d.x);
      result.value3d.y = fmod(a.value1d, b.value3d.y);
      result.value3d.z = fmod(a.value1d, b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d.x = fmod(a.value3d.x, b.value1d);
      result.value3d.y = fmod(a.value3d.y, b.value1d);
      result.value3d.z = fmod(a.value3d.z, b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d.x = fmod(a.value3d.x, b.value3d.x);
      result.value3d.y = fmod(a.value3d.y, b.value3d.y);
      result.value3d.z = fmod(a.value3d.z, b.value3d.z);
      return result;
  }

  return result;
}


	// checks a value for being non zero


int trueval(value &condition)
{
  if (condition.type == v3d)
  {
    error("single value expected for argument 1 of if");
  }

  return (!ZEROISH(condition.value1d));
}


value operator&&(value &a, value &b)
{
  value result;

  if (a.type != v1d || b.type != v1d)
  {
    error("single valued arguments expected for and");
  }

  result.type = v1d;
  result.value1d = (float) (!ZEROISH(a.value1d) && !ZEROISH(b.value1d));

  return result;
}


value operator||(value &a, value &b)
{
  value result;

  if (a.type != v1d || b.type != v1d)
  {
    error("single valued arguments expected for or");
  }

  result.type = v1d;
  result.value1d = (float) (!ZEROISH(a.value1d) || !ZEROISH(b.value1d));

  return result;
}


value operator!(value &a)
{
  value result;

  if (a.type != v1d)
  {
    error("single valued argument expected for not");
  }

  result.type = v1d;
  result.value1d = (float) ZEROISH(a.value1d);

  return result;
}


value squareval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = a.value3d.x * a.value3d.x;
    result.value3d.y = a.value3d.y * a.value3d.y;
    result.value3d.z = a.value3d.z * a.value3d.z;

    return result;
  }

  result.type = v1d;
  result.value1d = a.value1d * a.value1d;

  return result;
}


value sqrtval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = sqrt(a.value3d.x);
    result.value3d.y = sqrt(a.value3d.y);
    result.value3d.z = sqrt(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = sqrt(a.value1d);
  return result;
}


value lengthval(value &a)
{
  value result;

  if (a.type != v3d)
  {
    error("vector expected as argument for length");
  }

  result.type = v1d;
  result.value1d = a.value3d.length();

  return result;
}


value maxval(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d > b.value1d ? a.value1d : b.value1d;
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d.x = a.value1d > b.value3d.x ? a.value1d : b.value3d.x;
      result.value3d.y = a.value1d > b.value3d.y ? a.value1d : b.value3d.y;
      result.value3d.z = a.value1d > b.value3d.z ? a.value1d : b.value3d.z;
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d.x = b.value1d > a.value3d.x ? b.value1d : a.value3d.x;
      result.value3d.y = b.value1d > a.value3d.y ? b.value1d : a.value3d.y;
      result.value3d.z = b.value1d > a.value3d.z ? b.value1d : a.value3d.z;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d.x = a.value3d.x > b.value3d.x ? a.value3d.x : b.value3d.x;
      result.value3d.y = a.value3d.y > b.value3d.y ? a.value3d.y : b.value3d.y;
      result.value3d.z = a.value3d.z > b.value3d.z ? a.value3d.z : b.value3d.z;
      return result;
  }

  return result;
}


value minval(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = a.value1d < b.value1d ? a.value1d : b.value1d;
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d.x = a.value1d < b.value3d.x ? a.value1d : b.value3d.x;
      result.value3d.y = a.value1d < b.value3d.y ? a.value1d : b.value3d.y;
      result.value3d.z = a.value1d < b.value3d.z ? a.value1d : b.value3d.z;
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d.x = b.value1d < a.value3d.x ? b.value1d : a.value3d.x;
      result.value3d.y = b.value1d < a.value3d.y ? b.value1d : a.value3d.y;
      result.value3d.z = b.value1d < a.value3d.z ? b.value1d : a.value3d.z;
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d.x = a.value3d.x < b.value3d.x ? a.value3d.x : b.value3d.x;
      result.value3d.y = a.value3d.y < b.value3d.y ? a.value3d.y : b.value3d.y;
      result.value3d.z = a.value3d.z < b.value3d.z ? a.value3d.z : b.value3d.z;
      return result;
  }

  return result;
}


value logval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = log(a.value3d.x);
    result.value3d.y = log(a.value3d.y);
    result.value3d.z = log(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = log(a.value1d);
  return result;
}


value powval(value &a, value &b)
{
  value result;

  switch ((a.type << 1) + b.type)
  {
    case 3: 				// both v1d
      result.type = v1d;
      result.value1d = pow(a.value1d, b.value1d);
      return result;

    case 5:				// v1d, v3d
      result.type = v3d;
      result.value3d.x = pow(a.value1d, b.value3d.x);
      result.value3d.y = pow(a.value1d, b.value3d.y);
      result.value3d.z = pow(a.value1d, b.value3d.z);
      return result;

    case 7:				// v3d, v1d
      result.type = v3d;
      result.value3d.x = pow(a.value3d.x, b.value1d);
      result.value3d.y = pow(a.value3d.y, b.value1d);
      result.value3d.z = pow(a.value3d.z, b.value1d);
      return result;

    case 9:				// v3d, v3d
      result.type = v3d;
      result.value3d.x = pow(a.value3d.x, b.value3d.x);
      result.value3d.y = pow(a.value3d.y, b.value3d.y);
      result.value3d.z = pow(a.value3d.z, b.value3d.z);
      return result;
  }

  return result;
}


value unitval(value &a)
{
  value result;

  if (a.type != v3d)
  {
    error("vector expected as argument for unit");
  }

  result.type = v3d;
  result.value3d = a.value3d;
  result.value3d.normalize();
  return result;
}


value floorval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = floor(a.value3d.x);
    result.value3d.y = floor(a.value3d.y);
    result.value3d.z = floor(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = floor(a.value1d);
  return result;
}


value ceilval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = ceil(a.value3d.x);
    result.value3d.y = ceil(a.value3d.y);
    result.value3d.z = ceil(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = ceil(a.value1d);
  return result;
}


value absval(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v3d;
    result.value3d.x = fabs(a.value3d.x);
    result.value3d.y = fabs(a.value3d.y);
    result.value3d.z = fabs(a.value3d.z);

    return result;
  }

  result.type = v1d;
  result.value1d = fabs(a.value1d);
  return result;
}


value xcoord(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v1d;
    result.value1d = a.value3d.x;
    return result;
  }

  error("vector expected as argument to xcoord");
  return result;
}


value ycoord(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v1d;
    result.value1d = a.value3d.y;
    return result;
  }

  error("vector expected as argument to ycoord");
  return result;
}


value zcoord(value &a)
{
  value result;

  if (a.type == v3d)
  {
    result.type = v1d;
    result.value1d = a.value3d.z;
    return result;
  }

  error("vector expected as argument to zcoord");
  return result;
}


