
/* 
 * Compute the intersection volume of two gamuts. 
 *
 * Author:  Graeme W. Gill
 * Date:    2008/1/7
 * Version: 1.00
 *
 * Copyright 2008 Graeme W. Gill
 * All rights reserved.
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */

/*
 * TTBD:
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include <math.h>
#include "icc.h"
#include "numlib.h"
#include "gamut.h"

/* Compute a triangles area */
static double tri_area(
double v1[3],
double v2[3],
double v3[3]) {
	int i, j;
	double sp, ss[3];		/* Triangle side lengths */
	double area;			/* Area of this triangle */
	double *vv[3];			/* Pointers to vertexes */

	vv[0] = v1;
	vv[1] = v2;
	vv[2] = v3;
	/* Compute the full triangles area */
	for (i = 0; i < 3; i++) {	/* For each edge */
		for (ss[i] = 0.0, j = 0; j < 3; j++) {
			double dd = vv[i][j] - vv[(i+1) % 3][j];
			ss[i] += dd * dd;
		}
		ss[i] = sqrt(ss[i]);
	}
	sp = 0.5 * (ss[0] + ss[1] + ss[2]);		/* semi-perimeter */
	area = sqrt(fabs(sp * (sp - ss[0]) * (sp - ss[1]) * (sp - ss[2]))); /* Area of triangle */

	return area;
}

/* See if the given edge intersects a given triangle. */
/* Return 1 if it does, 0 if it doesn't */
static int edge_tri_isect(
gamut *s,		/* Gamut the triangle is in */
double *ip,		/* return intersection point */
gtri *t,		/* Triangle in question */
gedge *e		/* edge to test (may be from another gamut) */
) {
	double rv;			/* Axis parameter value */
	double gv[3];		/* Grey axis vector */
	double ival[3];		/* Intersection value */
	double den;
	int j;

	gv[0] = e->v[1]->p[0] - e->v[0]->p[0];
	gv[1] = e->v[1]->p[1] - e->v[0]->p[1];
	gv[2] = e->v[1]->p[2] - e->v[0]->p[2];

	den = t->pe[0] * gv[0] + t->pe[1] * gv[1] + t->pe[2] * gv[2];
	if (fabs(den) < 1e-10) {
		return 0;
	}

	/* Compute the intersection of the grey axis vector with the triangle plane */
	rv = -(t->pe[0] * e->v[0]->p[0]
	     + t->pe[1] * e->v[0]->p[1]
	     + t->pe[2] * e->v[0]->p[2]
	     + t->pe[3])/den;

	/* Compute the actual intersection point */
	ival[0] = e->v[0]->p[0] + rv * gv[0];
	ival[1] = e->v[0]->p[1] + rv * gv[1];
	ival[2] = e->v[0]->p[2] + rv * gv[2];

	/* Check if the intersection is on the edge */
	if (rv < 0.0 || rv > 1.0)
		return 0;

	/* Check if the intersection point is within the triangle */
	for (j = 0; j < 3; j++) {
		double ds;
		ds = t->ee[j][0] * (ival[0] - s->cent[0])	/* Convert to relative for edge check */
		   + t->ee[j][1] * (ival[1] - s->cent[1])
	       + t->ee[j][2] * (ival[2] - s->cent[2])
		   + t->ee[j][3];
		if (ds > 1e-8) {
			return 0;		/* Not within triangle */
		}
	}

	/* Got an intersection point */
	ip[0] = ival[0];
	ip[1] = ival[1];
	ip[2] = ival[2];

	return 1;
}


/* Return the total volume of the gamut */
/* Return -1.0 if incompatible gamuts */
double isect_volume(
gamut *s1,
gamut *s2
) {
	int i, j, k;
	gtri *tp1, *tp2;		/* Triangle pointer */
	double vol;		/* Gamut volume */

	if (s1->compatible(s1, s2) == 0)
		return -1.0;

	if IS_LIST_EMPTY(s1->tris)
		s1->triangulate(s1);
	if IS_LIST_EMPTY(s2->tris)
		s2->triangulate(s2);

	vol = 0.0;

	/* For first gamut then second gamut */
	for (k = 0; k < 2; k++) {

		if (k == 1) {			/* Swap the two gamuts roles */
			gamut *st = s1;
			s1 = s2;
			s2 = st;
printf("~1 doing second gamut inside first\n");
		} else {
printf("~1 doing first gamut inside second\n");
		}

		/* Compute the area of each triangle in the list that is within, */
		/* the other gamut, and accumulate the gamut volume. */
		tp1 = s1->tris;
		FOR_ALL_ITEMS(gtri, tp1) {
			double sp, ss[3];		/* Triangle side lengths */
			double area;			/* Area of this triangle */
			double dp;				/* Dot product of point in triangle and normal */
			int inout[3];			/* 0 = inside, 1 = outside */
			int nout;				/* Number that are out */ 
			
printf("~1 doing triangle %d from %s gamut\n",tp1->n,k == 0 ? "first" : "second");
			/* See how many vertices in the triangle are contained within */
			/* the other gamut. */
			nout = 0;
			for (i = 0; i < 3; i++) {	/* For each vertex */
				double pl;
				pl = s2->nradial(s2, NULL, tp1->v[i]->p);

				/* We add a slight hysterysis to avoid issues */
				/* with identical triangles in two gamuts. */
				if ((k == 0 && pl > (1.0 + 1e-10))
				 || (k == 1 && pl > (1.0 - 1e-10))) {
					nout++;
					inout[i] = 1;
				} else
					inout[i] = 0;
			}

printf("~1 vertices outside = %d\n",nout);

			/* If none are in, skip this triangle */
			if (nout == 3)
				continue;

			/* Compute the full triangles area */
			area = tri_area(tp1->v[0]->p, tp1->v[1]->p, tp1->v[2]->p);
printf("~1 full triangle area = %f\n",area);

			/* If the triangle is not completely in, locate all the intersections */
			/* between it and triangles in the other gamut */
			if (nout != 0) {
				gvert *opv;			/* Pointer to the one "in" or "out" vertex */
				double parea = 0.0;	/* Total partial area */

				/* Locate the odd point out of the three */
				if (nout == 2) {		/* Look for the one "in" point */
					for (j = 0; j < 3; j++) {
						if (inout[j] == 0)
							break;
					}
				} else {				/* Look for the one "out" point */
					for (j = 0; j < 3; j++) {
						if (inout[j] == 1)
							break;
					}
				}
				opv = tp1->v[j];

				tp2 = s2->tris;
				FOR_ALL_ITEMS(gtri, tp2) {		/* Other gamut triangles */
					double isps[2][3];			/* Intersection npoints */
					int nisps;					/* Number of intersection points */
					double isp[3];				/* New intersection point */
					int kk;
		
					/* Do a min/max intersection elimination test */
					for (i = 0; i < 3; i++) {
						if (tp2->mix[1][i] < tp1->mix[0][i]
						 || tp2->mix[0][i] > tp1->mix[1][i])
							break;			/* min/max don't overlap */
					}
					if (i < 3)
						continue;			/* Skip this triangle, it can't intersect */

//printf("~1 located possible intersecting triangle %d\n",tp2->n);

					/* Locate intersection of all sides of one triangle with */
					/* the plane of the other. Keep the two points of */
					/* intersection that lie within the triangles. */
					nisps = 0;
//printf("~1 initial nisps = %d\n",nisps);
					for (kk = 0; kk < 2; kk++) {
						gamut *ts;					/* Triangle gamut */
						gtri *tpa;					/* Triangle pointer */
						gtri *tpb;					/* Other triangle pointer */
						if (kk == 0) {
							ts = s1; 
							tpa = tp1;
							tpb = tp2;
						} else {
							ts = s2; 
							tpa = tp2;
							tpb = tp1;
						}

						/* For each edge */
						for (j = 0; j < 3; j++) {
							if (edge_tri_isect(ts, isp, tpa, tpb->e[j]) != 0) {
//printf("~1 isect %f %f %f\n", isp[0],isp[1],isp[2]);
								if (nisps < 2) {
//printf("~1 added at %d\n",nisps);
									icmAry2Ary(isps[nisps], isp);
									nisps++;
								} else {	/* Figure which one to replace */
									int xx;
									/* Replace the one closest to the new one, */
									/* if the new one is further from the other one */

									if (icmNorm33sq(isps[0], isp) < icmNorm33sq(isps[1], isp))
										xx = 0;
									else
										xx = 1;

									if (icmNorm33sq(isps[xx ^ 1], isp)
									  > icmNorm33sq(isps[xx ^ 1], isps[xx])) {
//printf("~1 replaced %d\n",xx);
										icmAry2Ary(isps[xx], isp);
									}
								}
							}
						}
					}
					if (nisps == 0) {
//printf("~1 no intersection\n");
						continue;

					} else if (nisps == 2)  {
						double sarea;

printf("~1 sub triangle =\n");
printf("~1 com %f %f %f\n", opv->p[0], opv->p[1], opv->p[2]);
printf("~1 1st %f %f %f\n", isps[0][0], isps[0][1], isps[0][2]);
printf("~1 2nd %f %f %f\n", isps[1][0], isps[1][1], isps[1][2]);

						/* Accumulate area of these two points + odd point */
						sarea = tri_area(opv->p, isps[0], isps[1]);
printf("~1 located intersecting triangle %d\n",tp2->n);
printf("~1 got sub area %f\n",sarea);
						parea += sarea;
					} else {	/* Hmm */
printf("~1 unexpectedly got %d intersection points in triangle\n",nisps);
					}
				} END_FOR_ALL_ITEMS(tp2);
				
				if (nout == 2) {		/* One "in" point */
					area = parea;
				} else {				/* One "out" point */
					area = area - parea;
				}
printf("~1 partial area = %f\n",area);
			}

			/* Dot product between first vertex in triangle and the unit normal vector */
			dp = tp1->v[0]->p[0] * tp1->pe[0]
			   + tp1->v[0]->p[1] * tp1->pe[1]
			   + tp1->v[0]->p[2] * tp1->pe[2];

printf("~1 vector volume = %f\n",dp * area);
			/* Accumulate gamut volume */
			vol += dp * area;

		} END_FOR_ALL_ITEMS(tp1);
	}

printf("~1 volume sum = %f\n",vol);
	vol = fabs(vol)/3.0;

printf("~1 final volume = %f\n",vol);
	return vol;
}


