/* vector.h: vectoren */

#ifndef _VECTOR_H_
#define _VECTOR_H_

#include <math.h>
#include "Float.h"

/* vector structuur */
typedef struct VECTOR {
	float x, y, z;
} VECTOR;

typedef struct VEC2D {
	float 	u, v;
} VEC2D;

/* vult x, y, en z component van vector in */
#define VECTORSET(v, a, b, c)	{(v).x = a; (v).y = b; (v).z = c;}

/* twee vectoren zijn gelijk als alle componenten gelijk zijn */
#define VECTOREQUAL(v, w)	(FLOATEQUAL((v).x, (w).x) && \
				 FLOATEQUAL((v).y, (w).y) && \
				 FLOATEQUAL((v).z, (w).z))

/* verschil van vectoren: d = a-b */
#define VECTORSUBSTRACT(a, b, d) {(d).x = (a).x - (b).x; \
				  (d).y = (a).y - (b).y; \
				  (d).z = (a).z - (b).z;}

/* verschil van vectoren: d = a-s.b */
#define VECTORSUBSTRACTSCALED(a, s, b, d){(d).x = (a).x - (s) * (b).x; \
					  (d).y = (a).y - (s) * (b).y; \
				          (d).z = (a).z - (s) * (b).z;}

/* som van vectoren: d = a+b */
#define VECTORSUM(a, b, d) 	 {(d).x = (a).x + (b).x; \
				  (d).y = (a).y + (b).y; \
				  (d).z = (a).z + (b).z;}

/* som van vectoren: d = a+s.b */
#define VECTORSUMSCALED(a, s, b, d) 	 {(d).x = (a).x + (s) * (b).x; \
					  (d).y = (a).y + (s) * (b).y; \
				          (d).z = (a).z + (s) * (b).z;}

/* scalair product van vectoren: a.b */
#define VECTORDOTPRODUCT(a, b) 	((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)

/* vectornorm in het kwadraat: scalair product met zichzelf */
#define VECTORNORM2(v)		  ((v).x * (v).x + (v).y * (v).y + (v).z * (v).z)	 	

/* vectornorm: wortel uit het kwadraat ervan */
#define VECTORNORM(v)		  (float)sqrt((double)VECTORNORM2(v))

/*vector scaleren d = s.v (s is een reel getal) */
#define VECTORSCALE(s, v, d)	  {(d).x = (s) * (v).x; \
				   (d).y = (s) * (v).y; \
				   (d).z = (s) * (v).z;}

/*vector scaleren d = (1/s).v (s is een reel getal) Oppassen als s nul is! */
#define VECTORSCALEINVERSE(s, v, d)	  {float _is_ = ((s) < -EPSILON || (s) > EPSILON ? 1./(s) : 1.); \
						   (d).x = _is_ * (v).x; \
						   (d).y = _is_ * (v).y; \
						   (d).z = _is_ * (v).z;}

/* vector normalizeren: scaleren met de inverse van de norm */
#define VECTORNORMALIZE(v)	{float _norm_; _norm_ = VECTORNORM(v); \
				 VECTORSCALEINVERSE(_norm_, v, v);}

/* inproduct van vectoren - loopt fout als d=a of d=b !! */
#define VECTORCROSSPRODUCT(a, b, d)	{(d).x = (a).y * (b).z - (a).z * (b).y; \
					 (d).y = (a).z * (b).x - (a).x * (b).z; \
					 (d).z = (a).x * (b).y - (a).y * (b).x;}

/* normaal construeren op een veelhoek bepaald door de drie gegeven 
 * vectoren */
#define NORMAL(v1, v2, v3, n)		{VECTOR _d1_, _d2_; \
					 VECTORSUBSTRACT(v2, v1, _d1_); \
					 VECTORSUBSTRACT(v3, v2, _d2_); \
					 VECTORCROSSPROD(_d1_, _d2_, n); \
					 VECTORNORMALIZE(n); }

/* combineer twee vectoren: d = a.v + b.w */
#define VECTORCOMB2(a, v, b, w, d)	{(d).x = (a) * (v).x + (b) * (w).x; \
				 (d).y = (a) * (v).y + (b) * (w).y; \
				 (d).z = (a) * (v).z + (b) * (w).z;}

/* combineer drie vectoren: d = o + a.v + b.w */
#define VECTORCOMB3(o, a, v, b, w, d)	{(d).x = (o).x + (a) * (v).x + (b) * (w).x; \
				 (d).y = (o).y + (a) * (v).y + (b) * (w).y; \
				 (d).z = (o).z + (a) * (v).z + (b) * (w).z;}

/* combineer drie vectoren: d = a*X + b*Y + c*Z */
#define VECTORCOORD(a, X, b, Y, c, Z, d) {(d).x = (a) * (X).x + (b) * (Y).x + (c) * (Z).x; \
					  (d).y = (a) * (X).y + (b) * (Y).y + (c) * (Z).y; \
					  (d).z = (a) * (X).z + (b) * (Y).z + (c) * (Z).z;}

/* macro to compute the cross product of v2-v1 and v0-v1 */
#define VECTORCROSSPRODDIFF(v0, v1, v2, res) {VECTOR _D1, _D2;			\
	VECTORSUBSTRACT(v2, v1, _D1);						\
	VECTORSUBSTRACT(v0, v1, _D2);						\
	VECTORCROSSPRODUCT(_D1, _D2, res)					\
				      }

#include <stdio.h>
#define VectorPrint(fp, v) fprintf(fp, "%g %g %g", (v).x, (v).y, (v).z)

/* geheugenbeheerroutines voor bvb lijsten en arrays van vectoren */

/* creeert een vector met gegeven componenten */
extern VECTOR *VectorCreate(float x, float y, float z);

/* vernietigt een vector */
extern void VectorDestroy(VECTOR *vector);

/* copieert de vector en geeft een wijzer naar de gecreeerde copij terug */
extern VECTOR *VectorCopy(VECTOR *vector);

/* geeft de lengte van de vector terug */
extern float VectorNormalize(VECTOR *vector);

#define XNORMAL 0
#define YNORMAL 1
#define ZNORMAL 2
#define VECTORINDEXNAME(index)	((index) == XNORMAL ? "XNORMAL" :	\
				 ((index) == YNORMAL ? "YNORMAL" : "ZNORMAL"))

#define VECTORPROJECT(r, p, i)	{switch(i) { \
				case XNORMAL: \
					r.u = (p).y; \
					r.v = (p).z; \
					break; \
				case YNORMAL: \
					r.u = (p).x; \
					r.v = (p).z; \
					break; \
				case ZNORMAL: \
					r.u = (p).x; \
					r.v = (p).y; \
					break; \
  				} }

/* midden van twee punten */
#define MIDPOINT(p1, p2, m)	{ 	\
	(m).x = 0.5 * ((p1).x + (p2).x);	\
	(m).y = 0.5 * ((p1).y + (p2).y);	\
	(m).z = 0.5 * ((p1).z + (p2).z);	\
				}

/* midden van vier punten */
#define MIDPOINT4(p1, p2, p3, p4, m)	{ 	\
	(m).x = 0.25 * ((p1).x + (p2).x + (p3).x + (p4).x);	\
	(m).y = 0.25 * ((p1).y + (p2).y + (p3).y + (p4).y);	\
	(m).z = 0.25 * ((p1).z + (p2).z + (p3).z + (p4).z);	\
				}

/* som van vier vectoren */
#define VECTORSUM4(v1, v2, v3, v4, s)	{ 		\
	(s).x = (v1).x + (v2).x + (v3).x + (v4).x;	\
	(s).y = (v1).y + (v2).y + (v3).y + (v4).y;	\
	(s).z = (v1).z + (v2).z + (v3).z + (v4).z;	\
				}


/* Point IN Triangle: barycentric parametrisation */
#define PINT(v0, v1, v2, u, v, p)     {				\
	(p).x = (v0).x + (u) * ((v1).x - (v0).x) + (v) * ((v2).x - (v0).x);	\
	(p).y = (v0).y + (u) * ((v1).y - (v0).y) + (v) * ((v2).y - (v0).y);	\
	(p).z = (v0).z + (u) * ((v1).z - (v0).z) + (v) * ((v2).z - (v0).z);	\
				      }

/* Point IN Quadrilateral: bilinear parametrisation */
#define PINQ(v0, v1 ,v2 ,v3, u, v, p)	{	\
        float _c=(u)*(v), _b=(u)-_c, _d=(v)-_c;	\
	(p).x = (v0).x + (_b) * ((v1).x - (v0).x) + (_c) * ((v2).x - (v0).x)+ (_d) * ((v3).x - (v0).x);	\
	(p).y = (v0).y + (_b) * ((v1).y - (v0).y) + (_c) * ((v2).y - (v0).y)+ (_d) * ((v3).y - (v0).y);	\
	(p).z = (v0).z + (_b) * ((v1).z - (v0).z) + (_c) * ((v2).z - (v0).z)+ (_d) * ((v3).z - (v0).z);	\
					};

/*
 * Find the "dominant" part of the vector (eg patch-normal).  This
 * is used to turn the point-in-polygon test into a 2D problem.
 */
extern int VectorDominantCoord(VECTOR *v);


#endif _VECTOR_H_
