/* Vector manipulation code V1.71 16/06/07
   Copyright 2008 Jeffrey Lee
   This file is part of WOUM.
   WOUM is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   WOUM 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 General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with WOUM.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _VEC_H
#define _VEC_H

#include "fixmath.h"

typedef struct {
	float x,y,z;
} vec;

typedef struct {
	f1616 x,y,z;
} vec16;

typedef struct {
	f248 x,y,z;
} vec24;

typedef struct {
	f2012 x,y,z;
} vec20;

typedef struct {
	f4816 x,y,z;
} vec48;

typedef struct { /* Note: No code exists for this type! */
	int x,y,z;
} vecint;

/* Add one vector to another: a=a+b */
extern void vec_add(vec *a,const vec *b);
extern void vec16_add(vec16 *a,const vec16 *b);
extern void vec24_add(vec24 *a,const vec24 *b);
extern void vec20_add(vec20 *a,const vec20 *b);
extern void vec48_add(vec48 *a,const vec48 *b);

/* Subtract one vector from another: a=a-b */
extern void vec_sub(vec *a,const vec *b);
extern void vec16_sub(vec16 *a,const vec16 *b);
extern void vec24_sub(vec24 *a,const vec24 *b);
extern void vec20_sub(vec20 *a,const vec20 *b);
extern void vec48_sub(vec48 *a,const vec48 *b);

/* Negate a vector: a.x=-a.x, a.y=-a.y, a.z=-a.z */
extern void vec_neg(vec *a);
extern void vec16_neg(vec16 *a);
extern void vec24_neg(vec24 *a);
extern void vec20_neg(vec20 *a);
extern void vec48_neg(vec48 *a);

/* Multiply one vector by another: a.x=a.x*b.x a.y=a.y*b.y, a.z=a.z*b.z */
extern void vec_mul(vec *a,const vec *b);
extern void vec16_mul(vec16 *a,const vec16 *b);
extern void vec24_mul(vec24 *a,const vec24 *b);
extern void vec20_mul(vec20 *a,const vec20 *b);
extern void vec48_mul(vec48 *a,const vec48 *b);

/* Divide one vector by another: a.x=a.x/b.x a.y=a.y/b.y a.z=a.z/b.z */
extern void vec_div(vec *a,const vec *b);
extern void vec16_div(vec16 *a,const vec16 *b);
extern void vec24_div(vec24 *a,const vec24 *b);
extern void vec20_div(vec20 *a,const vec20 *b);
extern void vec48_div(vec48 *a,const vec48 *b);

/* Multiply one vector by a unit: a.x=a.x*b, a.y=a.y*b, a.z=a.z*b */
extern void vec_mul2(vec *a,float b);
extern void vec16_mul2(vec16 *a,f1616 b);
extern void vec24_mul2(vec24 *a,f248 b);
extern void vec20_mul2(vec20 *a,f2012 b);
extern void vec48_mul2(vec48 *a,f4816 b);

/* Divide one vector by a unit: a.x=a.x/b, a.y=a.y/b, a.z=a.z/b */
extern void vec_div2(vec *a,float b);
extern void vec16_div2(vec16 *a,f1616 b);
extern void vec24_div2(vec24 *a,f248 b);
extern void vec20_div2(vec20 *a,f2012 b);
extern void vec48_div2(vec48 *a,f4816 b);

/* Divide a unit by a vector: b.x=a/b.x, b.y=a/b.y, b.z=a/b.z */
extern void vec_div3(float a,vec *b);
extern void vec16_div3(f1616 a,vec16 *b);
extern void vec24_div3(f248 a,vec24 *b);
extern void vec20_div3(f2012 a,vec20 *b);
extern void vec48_div3(f4816 a,vec48 *b);

/* Return the length of a vector */
extern float vec_len(const vec *v);
extern f1616 vec16_len(const vec16 *v);
extern f248 vec24_len(const vec24 *v);
extern f2012 vec20_len(const vec20 *v);
extern f4816 vec48_len(const vec48 *v);

/* Scale the vector so it's length is 1 */
extern void vec_normalize(vec *v);
extern void vec16_normalize(vec16 *v);
extern void vec24_normalize(vec24 *v);
extern void vec20_normalize(vec20 *v);
extern void vec48_normalize(vec48 *v);

/* Return the dot product of both vectors: a.x*b.x + a.y*b.y + a.z*b.z */
extern float vec_dotprod(const vec *a,const vec *b);
extern f1616 vec16_dotprod(const vec16 *a,const vec16 *b);
extern f248 vec24_dotprod(const vec24 *a,const vec24 *b);
extern f2012 vec20_dotprod(const vec20 *a,const vec20 *b);
extern f4816 vec48_dotprod(const vec48 *a,const vec48 *b);

/* Set c to the cross product of a and b */
extern void vec_crossprod(const vec *a,const vec *b,vec *c);
extern void vec16_crossprod(const vec16 *a,const vec16 *b,vec16 *c);
extern void vec24_crossprod(const vec24 *a,const vec24 *b,vec24 *c);
extern void vec20_crossprod(const vec20 *a,const vec20 *b,vec20 *c);
extern void vec48_corssprod(const vec48 *a,const vec48 *b,vec48 *c);

/* 'move' vector p to x location tx by adding on some multiple of d
   recdx should be 1/d.x
   Returns the distance moved (i.e. multiple of d used) */ 
extern float vec_movetox(vec *p,const vec *d,float tx,float recdx);
extern f1616 vec16_movetox(vec16 *p,const vec16 *d,f1616 tx,f1616 recdx);
extern f248 vec24_movetox(vec24 *p,const vec24 *d,f248 tx,f248 recdx);
extern f2012 vec20_movetox(vec20 *p,const vec20 *d,f2012 tx,f2012 recdx);
extern f4816 vec48_movetox(vec48 *p,const vec48 *d,f4816 tx,f4816 recdx);

/* 'move' vector p to y location ty by adding on some multiple of d
   recdy should be 1/d.y
   Returns the distance moved (i.e. multiple of d used) */ 
extern float vec_movetoy(vec *p,const vec *d,float ty,float recdy);
extern f1616 vec16_movetoy(vec16 *p,const vec16 *d,f1616 ty,f1616 recdy);
extern f248 vec24_movetoy(vec24 *p,const vec24 *d,f248 ty,f248 recdy);
extern f2012 vec20_movetoy(vec20 *p,const vec20 *d,f2012 ty,f2012 recdy);
extern f4816 vec48_movetoy(vec48 *p,const vec48 *d,f4816 ty,f4816 recdy);

/* 'move' vector p to z location tz by adding on some multiple of d
   recdz should be 1/d.z
   Returns the distance moved (i.e. multiple of d used) */ 
extern float vec_movetoz(vec *p,const vec *d,float tz,float recdz);
extern f1616 vec16_movetoz(vec16 *p,const vec16 *d,f1616 tz,f1616 recdz);
extern f248 vec24_movetoz(vec24 *p,const vec24 *d,f248 tz,f248 recdz);
extern f2012 vec20_movetoz(vec20 *p,const vec20 *d,f2012 tz,f2012 recdz);
extern f4816 vec48_movetoz(vec48 *p,const vec48 *d,f4816 tz,f4816 recdz);

/* Rotate v about the x axis by the angle a (in degrees) */
extern void vec_rotatex(vec *v,float a);
extern void vec16_rotatex(vec16 *v,f1616 a);
extern void vec24_rotatex(vec24 *v,f248 a);
extern void vec20_rotatex(vec20 *v,f2012 a);
extern void vec48_rotatex(vec48 *v,f4816 a);

/* Rotate v about the y axis by the angle a (in degrees) */
extern void vec_rotatey(vec *v,float a);
extern void vec16_rotatey(vec16 *v,f1616 a);
extern void vec24_rotatey(vec24 *v,f248 a);
extern void vec20_rotatey(vec20 *v,f2012 a);
extern void vec48_rotatey(vec48 *v,f4816 a);

/* Rotate v about the z axis by the angle a (in degrees) */
extern void vec_rotatez(vec *v,float a);
extern void vec16_rotatez(vec16 *v,f1616 a);
extern void vec24_rotatez(vec24 *v,f248 a);
extern void vec20_rotatez(vec20 *v,f2012 a);
extern void vec48_rotatez(vec48 *v,f4816 a);

/* Translate the vectors v1,v2,v3 into a plane; *p will be filled with the x,y,z
   coefficients (A, B and C) (the plane normal), and the offset D will be
   returned such that:
   Ax+By+Cz+D=0
   for all points on the plane. The normal is 0,0,0 for colinear points.
*/
extern float vec_toplane(const vec *v1,const vec *v2,const vec *v3,vec *p);
extern f1616 vec16_toplane(const vec16 *v1,const vec16 *v2,const vec16 *v3,vec16 *p);
extern f248 vec24_toplane(const vec24 *v1,const vec24 *v2,const vec24 *v3,vec24 *p);
extern f2012 vec20_toplane(const vec20 *v1,const vec20 *v2,const vec20 *v3,vec20 *p);
extern f4816 vec48_toplane(const vec48 *v1,const vec48 *v2,const vec48 *v3,vec48 *p);

/* Faster (probably), more accurate moveto* routines
   Destination value must be between vectors a and b (which are points on the line, as opposed to a point and a delta).
   c (which can be a, b or something else) is overwritten with the new value
*/
extern void vec_fmovetox(const vec *a,const vec *b,float dest,vec *c);
extern void vec_fmovetoy(const vec *a,const vec *b,float dest,vec *c);
extern void vec_fmovetoz(const vec *a,const vec *b,float dest,vec *c);
#define vec16_fmovetox(a,b,dest,c) gen_movetox(a,b,dest,c)
#define vec16_fmovetoy(a,b,dest,c) gen_movetoy(a,b,dest,c)
#define vec16_fmovetoz(a,b,dest,c) gen_movetoz(a,b,dest,c)
#define vec24_fmovetox(a,b,dest,c) gen_movetox(a,b,dest,c)
#define vec24_fmovetoy(a,b,dest,c) gen_movetoy(a,b,dest,c)
#define vec24_fmovetoz(a,b,dest,c) gen_movetoz(a,b,dest,c)
#define vec20_fmovetox(a,b,dest,c) gen_movetox(a,b,dest,c)
#define vec20_fmovetoy(a,b,dest,c) gen_movetoy(a,b,dest,c)
#define vec20_fmovetoz(a,b,dest,c) gen_movetoz(a,b,dest,c)
#define vec48_fmovetox(a,b,dest,c) genll_movetox(a,b,dest,c)
#define vec48_fmovetoy(a,b,dest,c) genll_movetoy(a,b,dest,c)
#define vec48_fmovetoz(a,b,dest,c) genll_movetoz(a,b,dest,c)

/* Fast perspective moveto* routines
   Destination value must be between vectors a and b
   c (which can be a, b, or something else) is overwritten with the new value (which will have c.x==c.z or c.y==c.z (or -c.x==c.z or -c.y==c.z if the negation flag is set)
*/
extern void vec_pmovetox(const vec *a,const vec *b,int neg,vec *c);
extern void vec_pmovetoy(const vec *a,const vec *b,int neg,vec *c);
#define vec16_pmovetox(a,b,neg,c) gen_pmovetox(a,b,neg,c)
#define vec16_pmovetoy(a,b,neg,c) gen_pmovetoy(a,b,neg,c)
#define vec24_pmovetox(a,b,neg,c) gen_pmovetox(a,b,neg,c)
#define vec24_pmovetoy(a,b,neg,c) gen_pmovetoy(a,b,neg,c)
#define vec20_pmovetox(a,b,neg,c) gen_pmovetox(a,b,neg,c)
#define vec20_pmovetoy(a,b,neg,c) gen_pmovetoy(a,b,neg,c)
#define vec48_pmovetox(a,b,neg,c) genll_pmovetox(a,b,neg,c)
#define vec48_pmovetoy(a,b,neg,c) genll_pmovetoy(a,b,neg,c)

#endif
