/* * 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 #include #include #include #include #include #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 verticies 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 verticies 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; }