(********************************************************************)
(*                                                                  *)
(*  vector3d.s7i  3D vector support library                         *)
(*  Copyright (C) 2008  Thomas Mertes                               *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  3D vectors represented with [[float]] values for the three dimensions.
 *  3D vector literals do not exist.
 *)
const type: vector3d is new struct
    var float: x is 0.0;
    var float: y is 0.0;
    var float: z is 0.0;
  end struct;


(**
 *  Create a 3D vector from its x, y and z values.
 *  @return the created 3D vector.
 *)
const func vector3d: vector3d (in float: x, in float: y, in float: z) is func
  result
    var vector3d: vect is vector3d.value;
  begin
    vect.x := x;
    vect.y := y;
    vect.z := z;
  end func;


(**
 *  Check if two 3D vectors are equal.
 *  @return TRUE if both 3D vectors are equal,
 *          FALSE otherwise.
 *)
const func boolean: (in vector3d: a) = (in vector3d: b) is
  return a.x = b.x and a.y = b.y and a.z = b.z;


(**
 *  Check if two 3D vectors are not equal.
 *  @return FALSE if both 3D vectors are equal,
 *          TRUE otherwise.
 *)
const func boolean: (in vector3d: a) <> (in vector3d: b) is
  return a.x <> b.x or a.y <> b.y or a.z <> b.z;


(**
 *  Minus sign, negate a 3D vector.
 *  @return the negated value of the 3D vector.
 *)
const func vector3d: - (in vector3d: a) is func
  result
    var vector3d: negated is vector3d.value;
  begin
    negated.x := -a.x;
    negated.y := -a.y;
    negated.z := -a.z;
  end func;


(**
 *  Add two 3D vectors.
 *  @return the sum of the two 3D vectors.
 *)
const func vector3d: (in vector3d: a) + (in vector3d: b) is func
  result
    var vector3d: sum is vector3d.value;
  begin
    sum.x := a.x + b.x;
    sum.y := a.y + b.y;
    sum.z := a.z + b.z;
  end func;


(**
 *  Subtract two 3D vectors.
 *  @return the difference of the two 3D vectors.
 *)
const func vector3d: (in vector3d: a) - (in vector3d: b) is func
  result
    var vector3d: difference is vector3d.value;
  begin
    difference.x := a.x - b.x;
    difference.y := a.y - b.y;
    difference.z := a.z - b.z;
  end func;


(**
 *  Multiply a 3D vector with a [[float]] number.
 *  @return the product of the multiplication.
 *)
const func vector3d: (in vector3d: v) * (in float: num) is func
  result
    var vector3d: product is vector3d.value;
  begin
    product.x := v.x * num;
    product.y := v.y * num;
    product.z := v.z * num;
  end func;


(**
 *  Divide a 3D vector by a [[float]] number.
 *  @return the quotient of the division.
 *)
const func vector3d: (in vector3d: v) / (in float: num) is func
  result
    var vector3d: quotient is vector3d.value;
  begin
    quotient.x := v.x / num;
    quotient.y := v.y / num;
    quotient.z := v.z / num;
  end func;


(**
 *  Increment a 3D vector by a [[float]] delta.
 *)
const proc: (inout vector3d: vect) +:= (in float: delta) is func
  begin
    vect.x +:= delta;
    vect.y +:= delta;
    vect.z +:= delta;
  end func;


(**
 *  Decrement a 3D vector by a [[float]] delta.
 *)
const proc: (inout vector3d: vect) -:= (in float: delta) is func
  begin
    vect.x -:= delta;
    vect.y -:= delta;
    vect.z -:= delta;
  end func;


(**
 *  Multiply a 3D vector by a [[float]] factor and assign the result back.
 *)
const proc: (inout vector3d: vect) *:= (in float: number) is func
  begin
    vect.x *:= number;
    vect.y *:= number;
    vect.z *:= number;
  end func;


(**
 *  Divide a 3D vector by a [[float]] factor and assign the result back.
 *)
const proc: (inout vector3d: vect) /:= (in float: number) is func
  begin
    vect.x /:= number;
    vect.y /:= number;
    vect.z /:= number;
  end func;


(**
 *  Compute the absolute value of a 3D vector.
 *  @return the absolute value.
 *)
const func float: abs (in vector3d: v) is
  return sqrt(v.x ** 2 + v.y ** 2 + v.z ** 2);


(**
 *  Compute the square of the absolute value of a 3D vector.
 *  @return the square of the absolute value.
 *)
const func float: sqrAbs (in vector3d: v) is
  return v.x ** 2 + v.y ** 2 + v.z ** 2;


(**
 *  Inner product of two 3D vectors.
 *  @return the inner product of the two 3D vectors.
 *)
const func float: dot (in vector3d: a, in vector3d: b) is
  return a.x * b.x + a.y * b.y + a.z * b.z;


(**
 *  Cross product of two 3D vectors.
 *  @return the cross product of the two 3D vectors.
 *)
const func vector3d: cross (in vector3d: a, in vector3d: b) is func
  result
    var vector3d: product is vector3d.value;
  begin
    product.x := a.y * b.z - a.z * b.y;
    product.y := a.z * b.x - a.x * b.z;
    product.z := a.x * b.y - a.y * b.x;
  end func;


(**
 *  Reflect the vector ''vect'' at a surface defined by ''normal''.
 *  The surface is representing by its normal at the intersection point.
 *  @return the reflected vector.
 *)
const func vector3d: reflect (in vector3d: vect, in vector3d: normal) is
  return vect - normal * dot(normal, vect) * 2.0;


(**
 *  Unit vector of a given 3D vector.
 *  @return the unit vector of the given 3D vector.
 *)
const func vector3d: unitVector (in vector3d: v) is func
  result
    var vector3d: unitVector is vector3d.value;
  local
    var float: length is 0.0;
  begin
    length := abs(v);
    unitVector.x := v.x / length;
    unitVector.y := v.y / length;
    unitVector.z := v.z / length;
  end func;


(**
 *  Compare two 3D vectors in lexicographic order.
 *  @return -1, 0 or 1 if the first argument is considered to be
 *          respectively less than, equal to, or greater than the
 *          second.
 *)
const func integer: compare (in vector3d: vect1, in vector3d: vect2) is func
  result
    var integer: signumValue is 0;
  begin
    signumValue := compare(vect1.x, vect2.x);
    if signumValue = 0 then
      signumValue := compare(vect1.y, vect2.y);
      if signumValue = 0 then
        signumValue := compare(vect1.z, vect2.z);
      end if;
    end if;
  end func;


(**
 *  Compute the hash value of a 3D vector.
 *  @return the hash value.
 *)
const func integer: hashCode (in vector3d: vect) is
  return hashCode(vect.x) mod 16#40000000 +
         hashCode(vect.y) mod 16#40000000 +
         hashCode(vect.z) mod 16#40000000;


(**
 *  Convert a 3D vector to a [[string]].
 *  @return the string result of the conversion.
 *)
const func string: str (in vector3d: a) is
  return "(" <& a.x digits 2 <& ", " <& a.y digits 2 <& ", " <& a.z digits 2 <& ")";


enable_output(vector3d);