summaryrefslogtreecommitdiff
path: root/rspl/rev.c
diff options
context:
space:
mode:
Diffstat (limited to 'rspl/rev.c')
-rw-r--r--rspl/rev.c8763
1 files changed, 7369 insertions, 1394 deletions
diff --git a/rspl/rev.c b/rspl/rev.c
index 1dbf6cc..f2f717b 100644
--- a/rspl/rev.c
+++ b/rspl/rev.c
@@ -1,4 +1,7 @@
+/* First cut at lchw weighted. Problems with list size, memory use and */
+/* performance. Version uses direct bwd cell nnrev[] creation */
+
/*
* Argyll Color Correction System
* Multi-dimensional regularized spline data structure
@@ -19,27 +22,13 @@
/* TTBD:
- Should fix the clipping case so that a direction weighting
- funtion can be applied. This should be used just like
- the perceptual case to increase L* constance for dark
- colors. This would entail large scale changes though,
- since a lot of code assumes minimal euclidean distance
- goal, from the cell selection structure [ See fill_nncell(),
- fill_nncell() and users of calc_fwd_nn_cell_list() ] and
- the within cell computation [ ie. See nnearest_clip_solve(),
- clipn_setsort() etc. ]
- XYZ PCS couldn't work with a simple weighting - it would have
- to be a position dependent weighting.
- The SVD least squares computation case makes this hard to change ?
- Would have to feed in a weighting function, or can it be general ?
- -
- Can this be solved some other way, ie. by using gamut
- mapping type look up ? Problem is precision.
- -
- Vector clip could be used (if intent can be turned
- into computable vector clip direction), but it is slow,
- because it search all cells from source until it
- hits surface.
+ Add option/function to return a gamut surface triangle list
+ based on the rev setup thinned vertex list.
+ Need to add code to convert over ink edges to triangles
+ and then shadow test them though.
+
+ XYZ PCS doesn't work with a LCh weighting, although this is
+ no an issue when xicc uses separate Jab rspl for clip case (CAM CLIP).
Allow function callback to set auxiliary values for
flag RSPL_AUXLOCUS.
@@ -59,7 +48,7 @@
Basic function requirements: exact, auxil, locus, clip
- Fwd cell - reverse cell list lookup
+ Fwd cell - fxcell list lookup
Basic layout di -> fdi + auxils + ink limit
@@ -119,8 +108,13 @@
//#include "dmalloc.h"
//#undef DMALLOC_GLOBALS
-#undef DEBUG1 /* Higher level code */
-#undef DEBUG2 /* Lower level code */
+#define DOSORT /* [def] Cell sort for better speed */
+
+#undef REVTABLESTATS /* [und] Reverse table stats */
+#undef REVVRML /* [und] Reverse table plots */
+
+#undef DEBUG1 /* [und] Higher level code */
+#undef DEBUG2 /* [und] Lower level code */
/* Debug memory usage accounting */
#ifdef NEVER
@@ -151,8 +145,6 @@ int thissz, lastsz = -1;
/* Set STATS in rev.h */
-#define DOSORT /* Cell sort */
-
/* Print a vectors value */
#define DBGVI(text, dim, out, vec, end) \
{ int pveci; \
@@ -175,6 +167,23 @@ int thissz, lastsz = -1;
printf(end); \
}
+#if defined(DEBUG1) || defined(DEBUG2)
+# define REVTABLESTATS /* [und] Reverse table stats */
+#endif
+
+#ifdef REVTABLESTATS
+#pragma message("!!!!!!!!! REVTABLESTATS set in rspl/rev.c !!!!!!!!!!!")
+#endif
+
+#ifdef REVVRML
+#pragma message("!!!!!!!!! REVVRML set in rspl/rev.c !!!!!!!!!!!")
+#include "vrml.h"
+#endif
+
+#ifdef CHECK_NNLU
+#pragma message("!!!!!!!!! CHECK_NNLU set in rspl/rspl.h !!!!!!!!!!!")
+#endif
+
/* Do an arbitrary printf */
#define DBGI(text) printf text ;
@@ -209,7 +218,7 @@ int thissz, lastsz = -1;
#endif
/* Debug string routines */
-static char *pcellorange(cell *c);
+static char *pcellorange(fxcell *c);
/* Convention is to use:
i to index grid points u.a
@@ -225,9 +234,9 @@ static char *pcellorange(cell *c);
static void make_rev(rspl *s);
static void init_revaccell(rspl *s);
-static cell *get_rcell(schbase *b, int ix, int force);
-static void uncache_rcell(revcache *r, cell *cp);
-#define unget_rcell(r, cp) uncache_rcell(r, cp) /* These are the same */
+static fxcell *get_fxcell(schbase *b, int ix, int force);
+static void uncache_fxcell(revcache *r, fxcell *cp);
+#define unget_fxcell(r, cp) uncache_fxcell(r, cp) /* These are the same */
static void invalidate_revaccell(rspl *s);
static int decrease_revcache(revcache *rc);
@@ -285,7 +294,7 @@ static void rev_reduce_cache(size_t size) {
ram += rsi->sz;
if (size > ram)
- error("rev_reduce_cache: run out of rev virtual memory!");
+ error("rev_reduce_cache: run out of rev virtual memory! (want %d, got %d)",size,ram);
//printf("~1 size = %d, g_test_ram = %d\n",size,g_test_ram);
//printf("~1 rev: Reducing cache because alloc of %d bytes failed. Reduced from %d to %d MB\n",
@@ -304,12 +313,13 @@ static void rev_reduce_cache(size_t size) {
}
//printf("~1 rev instance ram = %d MB\n",rsi->sz/1000000);
}
-//fprintf(stdout, "%c~~1 There %s %d rev cache instance%s with %d Mbytes limit\n",
-// cr_char,
-// g_no_rev_cache_instances > 1 ? "are" : "is",
-// g_no_rev_cache_instances,
-// g_no_rev_cache_instances > 1 ? "s" : "",
-// ram/1000000);
+ if (g_rev_instances != NULL && g_rev_instances->sb->s->verbose)
+ printf("%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
+ cr_char,
+ g_no_rev_cache_instances > 1 ? "are" : "is",
+ g_no_rev_cache_instances,
+ g_no_rev_cache_instances > 1 ? "s" : "",
+ (unsigned long)ram/1000000);
}
/* Check that the requested allocation plus 20 M Bytes */
@@ -403,7 +413,8 @@ rev_set_limit_rspl(
) {
schbase *b;
- DBG(("rev: setting ink limit function 0x%x and limit %f\n",limit,limitv));
+ DBG(("rev: setting ink limit function %p and limit %f\n",limit,limitv));
+
/* This is a restricted size function */
if (s->di > MXRI)
error("rspl: rev_set_limit can't handle di = %d",s->di);
@@ -447,22 +458,58 @@ rev_get_limit_rspl(
}
}
+/* Set the RSPL_NEARCLIP LCh weightings. */
+/* Will only work with L*a*b* like output spaces. */
+/* Calling this will clear the reverse interpolaton cache. */
+static void rev_set_lchw(
+ struct _rspl *s, /* this */
+ double lchw[MXRO] /* Weighting */
+) {
+ int f;
+
+ DBG(("rev: setting LCH weightings %f %f %f \n",lchw[0], lchw[1], lchw[2]));
+
+ /* This is a restricted size function */
+ if (s->di > MXRI)
+ error("rspl: rev_set_lchw can't handle di = %d",s->di);
+ if (s->fdi > MXRO || s->fdi != 3)
+ error("rspl: rev_set_lchw can't handle fdi = %d",s->fdi);
+
+ s->rev.lchweighted = 1;
+ for (f = 0; f < s->fdi; f++) {
+ s->rev.lchw[f] = lchw[f];
+ s->rev.lchw_sq[f] = s->rev.lchw[f] * s->rev.lchw[f];
+ }
+ s->rev.lchw_chsq = s->rev.lchw_sq[1] - s->rev.lchw_sq[2]; /* C - H squared weight */
+
+ if (s->rev.inited) { /* If cache and acceleration has been allocated */
+ invalidate_revaccell(s); /* Invalidate the reverse cache */
+ }
+}
+
#define RSPL_CERTAIN 0x80000000 /* WILLCLIP hint is certain */
#define RSPL_WILLCLIP2 (RSPL_CERTAIN | RSPL_WILLCLIP) /* Clipping will certainly be needed */
+#ifdef CHECK_NNLU
+static void check_nn(rspl *s, double *oval, co *cpp);
+static void print_nnck(rspl *s);
+#endif
+
/* Do reverse interpolation given target output values and (optional) auxiliary target */
/* input values. Return number of results and clipping flag. If return value == mxsoln, */
/* then there might be more results. The target values returned will correspond to the */
/* actual (posssibly clipped) point. The return value is the number of solutions + */
/* a clipped flag. Properly set hint flags improve performance, but a correct result should */
/* be returned if the RSPL_NEARCLIP is set, even if they are not set correctly. */
+/* If RSPL_NONNSETUP is set, then rev.fastsetup will be set for this call, avoiding */
+/* initialization of the nngrid if RSPL_NEARCLIP hasn't been used before. */
static int
rev_interp_rspl(
rspl *s, /* this */
int flags, /* Hint flag */
int mxsoln, /* Maximum number of solutions allowed for */
int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
- double cdir[MXRO], /* Clip vector direction wrt to cpp[0].v and length - NULL if not used */
+ double cdir[MXRO], /* Clip vector direction and length - NULL if not used */
co *cpp /* Given target output space value in cpp[0].v[] + */
/* target input space auxiliaries in cpp[0].p[], return */
/* input space solutions in cpp[0..retval-1].p[], and */
@@ -473,6 +520,7 @@ rev_interp_rspl(
schbase *b = NULL; /* Base search information */
double auxv[MXRI]; /* Locus proportional auxiliary values */
int didclip = 0; /* flag - set if we clipped the target */
+ int fastsetup; /* fastsetup on entry */
DBGV(("\nrev interp called with out targets", fdi, " %f", cpp[0].v, "\n"));
@@ -496,7 +544,11 @@ rev_interp_rspl(
DBG(("di = %d, fdi = %d\n",di, fdi));
DBG(("flags = 0x%x\n",flags));
- mxsoln &= RSPL_NOSOLNS; /* Prevent silliness */
+ fastsetup = s->rev.fastsetup; /* fastsetup on entry */
+ if (flags & RSPL_NONNSETUP) /* Avoid triggering nnsetup on this call */
+ s->rev.fastsetup = 1;
+
+ mxsoln &= RSPL_NOSOLNS; /* Prevent silliness */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Auxiliary is proportion of locus, so we need to find locus extent */
@@ -550,7 +602,7 @@ rev_interp_rspl(
b = init_search(s, flags, cpp[0].p, auxm, cpp[0].v, cdir, cpp, mxsoln, exact);
else
adjust_search(s, flags, auxv, exact); /* Using proportion of locus aux */
-
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* If hinted that we will not need to clip, look for exact solution. */
if (!(flags & RSPL_WILLCLIP)) {
@@ -570,7 +622,7 @@ rev_interp_rspl(
/* Setup, sort and search the list */
search_list(b, rip, s->get_next_touch(s));
} else {
- DBG(("Got NULL list (point outside range) for first exact reverse cell\n"));
+ DBG(("Got NULL list (point outside range) for first exact fxcell\n"));
}
/* If we selected exact aux, but failed to find a solution, relax expectation */
@@ -587,7 +639,7 @@ rev_interp_rspl(
/* Setup, sort and search the list */
search_list(b, rip, s->get_next_touch(s));
} else {
- DBG(("Got NULL list (point outside range) for nearest search reverse cell\n"));
+ DBG(("Got NULL list (point outside range) for nearest search fxcell\n"));
}
}
}
@@ -595,6 +647,13 @@ rev_interp_rspl(
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* If the exact search failed, and we should look for a nearest solution */
if (b->nsoln == 0 && (flags & RSPL_NEARCLIP)) {
+#ifdef CHECK_NNLU
+ int f, fdi = s->fdi;
+ double oval[MXRO]; /* Save the input target value for check_nn() */
+
+ for (f = 0; f < fdi; f++)
+ oval[f] = cpp[0].v[f];
+#endif
DBG(("Trying nearest search\n"));
#ifdef STATS
@@ -603,10 +662,6 @@ rev_interp_rspl(
/* We get returned a list of cube base indexes of all cubes that have */
/* the closest valid vertex value to the target value. */
- /* (This may not result in the true closest point if the geometry of */
- /* the vertex values is localy non-smooth or self intersecting, */
- /* but seems to return a good result in most realistic situations ?) */
-
adjust_search(s, flags, NULL, clipn);
/* Get list of cells enclosing nearest vertex */
@@ -616,8 +671,12 @@ rev_interp_rspl(
DBG(("Got NULL list! (point inside gamut \?\?) for nearest search\n"));
}
- if (b->nsoln > 0)
+ if (b->nsoln > 0) {
didclip = RSPL_DIDCLIP;
+#ifdef CHECK_NNLU
+ check_nn(s, oval, cpp); /* Run diagnostic to check sanity of result */
+#endif
+ }
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -625,7 +684,7 @@ rev_interp_rspl(
if (b->nsoln == 0 && b->canvecclip) {
/* Find clipping solution in vector direction */
line ln; /* Structure to hold line context */
- unsigned int tcount; /* grid touch count for this opperation */
+ unsigned int tcount; /* grid touch count for this operation */
DBG(("Starting a clipping vector search now!!\n"));
@@ -641,7 +700,7 @@ rev_interp_rspl(
//~~1 HACK!!! should be <= 1.0 !!!
for (; ln.t <= 2.0; rip = next_line_cell(&ln)) {
if (rip == NULL) {
- DBG(("Got NULL list for this reverse cell\n"));
+ DBG(("Got NULL list for this fxcell\n"));
continue;
}
@@ -685,7 +744,7 @@ rev_interp_rspl(
/* Setup, sort and search the list */
search_list(b, rip, s->get_next_touch(s));
} else {
- DBG(("Got NULL list (point outside range) for first exact reverse cell\n"));
+ DBG(("Got NULL list (point outside range) for first exact fxcell\n"));
}
/* If we selected exact aux, but failed to find a solution, relax expectation */
@@ -702,7 +761,7 @@ rev_interp_rspl(
/* Setup, sort and search the list */
search_list(b, rip, s->get_next_touch(s));
} else {
- DBG(("Got NULL list (point outside range) for nearest search reverse cell\n"));
+ DBG(("Got NULL list (point outside range) for nearest search fxcell\n"));
}
}
@@ -729,6 +788,8 @@ rev_interp_rspl(
}
DBG(("rev interp returning %d solutions%s\n",b->nsoln, didclip ? " [clip]" : ""));
+ s->rev.fastsetup = fastsetup; /* retore fastsetup state */
+
return b->nsoln | didclip;
}
@@ -958,23 +1019,23 @@ rev_locus_rspl(
/* ------------------------------------------------ */
/* subroutines of top level reverse lookup routine */
-static int exact_setsort(schbase *b, cell *c);
+static int exact_setsort(schbase *b, fxcell *c);
static int exact_compute(schbase *b, simplex *x);
-static int auxil_setsort(schbase *b, cell *c);
-static int auxil_check(schbase *b, cell *c);
+static int auxil_setsort(schbase *b, fxcell *c);
+static int auxil_check(schbase *b, fxcell *c);
static int auxil_compute(schbase *b, simplex *x);
-static int locus_setsort(schbase *b, cell *c);
-static int locus_check(schbase *b, cell *c);
+static int locus_setsort(schbase *b, fxcell *c);
+static int locus_check(schbase *b, fxcell *c);
static int locus_compute(schbase *b, simplex *x);
-static int clipv_setsort(schbase *b, cell *c);
-static int clipv_check(schbase *b, cell *c);
+static int clipv_setsort(schbase *b, fxcell *c);
+static int clipv_check(schbase *b, fxcell *c);
static int clipv_compute(schbase *b, simplex *x);
-static int clipn_setsort(schbase *b, cell *c);
-static int clipn_check(schbase *b, cell *c);
+static int clipn_setsort(schbase *b, fxcell *c);
+static int clipn_check(schbase *b, fxcell *c);
static int clipn_compute(schbase *b, simplex *x);
/* Allocate the search base structure */
@@ -985,8 +1046,8 @@ alloc_sb(rspl *s) {
error("rspl malloc failed - rev.sb structure");
INCSZ(s, sizeof(schbase));
- b->s = s; /* rsp */
- b->pauxcell = /* Previous solution cell indexes */
+ b->s = s; /* rsp */
+ b->pauxcell = /* Previous solution cell indexes */
b->plmaxcell =
b->plmincell = -1;
@@ -1010,7 +1071,7 @@ init_search(
int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
/* Locus search will search for max/min of first valid auxlilary */
double *v, /* Output value target, NULL if none */
- double *cdir, /* Clip vector direction, NULL if none */
+ double *cdir, /* Clip vector direction/LCh weighting, NULL if none */
co *cpp, /* Array that hold solutions, NULL if none. */
int mxsoln, /* Maximum number of solutions allowed for */
enum ops op /* Type of reverse search operation requested */
@@ -1019,15 +1080,14 @@ init_search(
int e, di = s->di;
int f, fdi = s->fdi;
- DBG(("Initializing search\n"));
+ DBG(("Initializing search di %d fdi %d\n",s->di,s->fdi));
if (s->rev.inited == 0) /* Compute reverse info if it doesn't exist */
make_rev(s);
/* If first time initialisation (Fourth section init) */
- if ((b = s->rev.sb) == NULL) {
+ if ((b = s->rev.sb) == NULL)
b = alloc_sb(s);
- }
/* Init some basic search info */
b->op = op; /* operation */
@@ -1065,7 +1125,7 @@ init_search(
/* Figure out if the clip direction is meaningfull */
/* Check that the clip vector makes sense */
- if (cdir != NULL) { /* Clip vector is specified */
+ if (!(flags & RSPL_NEARCLIP) && cdir != NULL) { /* Clip vector is specified */
double ss;
for (ss = 0.0, f = 0; f < fdi; f++) {
double tt = cdir[f];
@@ -1272,9 +1332,9 @@ set_search_limit(
b = alloc_sb(s);
}
- s->limitf = limitf; /* Input limit function */
- s->lcntx = lcntx; /* Context passed to limit() */
- s->limitv= INKSCALE * limitv; /* Context passed to values not to be exceedded by limit() */
+ s->limitf = limitf; /* Input limit function */
+ s->lcntx = lcntx; /* Context passed to limit() */
+ s->limitv = INKSCALE * limitv; /* Context passed to values not to be exceeded by limit() */
if (limitf != NULL) {
s->limiten = 1; /* enable limiting by default */
} else
@@ -1309,7 +1369,7 @@ schbase *b /* Base search information */
/* Sorted cell list */
if (b->lclistz > 0) {
free(b->lclist);
- DECSZ(b->s, b->lclistz * sizeof(cell *));
+ DECSZ(b->s, b->lclistz * sizeof(fxcell *));
b->lclist = NULL;
b->lclistz = 0;
}
@@ -1350,12 +1410,14 @@ calc_fwd_cell_list(
}
rpp += mi * s->rev.coi[f]; /* Accumulate reverse grid pointer */
}
+ s->rev.sb->rix = rpp - s->rev.rev; /* Set diagnostic value */
+
if (*rpp == NULL)
return NULL;
return (*rpp) + 3;
}
-void alloc_simplexes(cell *c, int nsdi);
+void alloc_simplexes(fxcell *c, int nsdi);
/* Given a pointer to a list of fwd cells, cull cells that */
/* cannot contain or improve the solution, sort the list, */
@@ -1369,7 +1431,7 @@ unsigned int tcount /* grid touch count for this operation */
rspl *s = b->s;
int nsdi;
int i;
- int nilist; /* Number in cell list */
+ int nilist; /* Number in cell list */
unsigned int stouch; /* Simplex touch count */
DBG(("search_list called\n"));
@@ -1381,31 +1443,31 @@ unsigned int tcount /* grid touch count for this operation */
if (b->lclistz > 0) { /* Free old space before allocating new */
free(b->lclist);
- DECSZ(b->s, b->lclistz * sizeof(cell *));
+ DECSZ(b->s, b->lclistz * sizeof(fxcell *));
}
b->lclistz = 0;
/* Allocate enough space for all the candidate cells */
- if ((b->lclist = (cell **)rev_malloc(s, rip[-3] * sizeof(cell *))) == NULL)
+ if ((b->lclist = (fxcell **)rev_malloc(s, rip[-3] * sizeof(fxcell *))) == NULL)
error("rev: malloc failed - candidate cell list, count %d",rip[-3]);
b->lclistz = rip[-3]; /* Current allocated space */
- INCSZ(b->s, b->lclistz * sizeof(cell *));
+ INCSZ(b->s, b->lclistz * sizeof(fxcell *));
}
/* Get the next simplex touch count, so that we don't search shared */
/* face simplexes more than once in this pass through the cells. */
if ((stouch = ++s->rev.stouch) == 0) { /* If touch count rolls over */
- cell *cp;
+ fxcell *cp;
stouch = s->rev.stouch = 1;
- DBG(("touch has rolled over, resetting it\n"));
/* For all of the cells */
+ DBG(("touch has rolled over, resetting it\n"));
for (cp = s->rev.cache->mrubot; cp != NULL; cp = cp->mruup) {
int nsdi;
if (cp->s == NULL) /* Cell has never been used */
continue;
- /* For all the simplexes in the cell */
+ /* For all the simplexes in the fxcell */
for (nsdi = 0; nsdi <= s->di; nsdi++) {
if (cp->sx[nsdi] != NULL) {
int si;
@@ -1419,20 +1481,20 @@ unsigned int tcount /* grid touch count for this operation */
}
/* For each chunk of the list that we can fit in the rcache: */
- for(; *rip != -1;) {
+ for (; *rip != -1;) {
/* Go through all the candidate fwd cells, and build up the list of search cells */
- for(nilist = 0; *rip != -1; rip++) {
+ for (nilist = 0; *rip != -1; rip++) {
int ix = *rip; /* Fwd cell index */
float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
- cell *c;
+ fxcell *c;
if (TOUCHF(fcb) >= tcount) { /* If we have visited this cell before */
DBG((" Already touched cell index %d\n",ix));
continue;
}
/* Get pointers to cells from cache, and lock it in the cache */
- if ((c = get_rcell(b, ix, nilist == 0 ? 1 : 0)) == NULL) {
+ if ((c = get_fxcell(b, ix, nilist == 0 ? 1 : 0)) == NULL) {
static int warned = 0;
if (!warned) {
warning("%cWarning - Reverse Cell Cache exausted, processing in chunks",cr_char);
@@ -1442,7 +1504,7 @@ unsigned int tcount /* grid touch count for this operation */
if (nilist == 0) {
/* This should never happen, because nz force should prevent it */
revcache *rc = s->rev.cache;
- cell *cp;
+ fxcell *cp;
int nunlk = 0;
/* Double check that there are no unlocked cells */
for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup) {
@@ -1463,7 +1525,7 @@ unsigned int tcount /* grid touch count for this operation */
/* Check mandatory conditions, and compute search key */
if (!b->setsort(b, c)) {
DBG(("cell %d rejected from list\n",ix));
- unget_rcell(s->rev.cache, c);
+ unget_fxcell(s->rev.cache, c);
continue;
}
DBG(("cell %d accepted into list\n",ix));
@@ -1483,7 +1545,7 @@ unsigned int tcount /* grid touch count for this operation */
{ /* Special case, adjust sort values */
double min = INF_DIST, max = -INF_DIST;
for (i = 0; i < nilist; i++) {
- cell *c = b->lclist[i];
+ fxcell *c = b->lclist[i];
if (c->sort < min)
min = c->sort;
if (c->sort > max)
@@ -1492,7 +1554,7 @@ unsigned int tcount /* grid touch count for this operation */
max = min + max; /* Total of min/max */
min = 0.5 * max; /* Average sort value */
for (i = 0; i < nilist; i++) {
- cell *c = b->lclist[i];
+ fxcell *c = b->lclist[i];
if (c->ix == b->plmincell || c->ix == b->plmaxcell) {
c->sort = -1.0; /* Put previous solution cells at head of list */
} else if (c->sort > min) {
@@ -1505,7 +1567,7 @@ unsigned int tcount /* grid touch count for this operation */
case clipv:
case clipn:
#define HEAP_COMPARE(A,B) (A->sort < B->sort)
- HEAPSORT(cell *,b->lclist, nilist)
+ HEAPSORT(fxcell *,b->lclist, nilist)
#undef HEAP_COMPARE
break;
default:
@@ -1531,7 +1593,7 @@ unsigned int tcount /* grid touch count for this operation */
/* For each cell in the list */
for (i = 0; i < nilist; i++) {
- cell *c = b->lclist[i];
+ fxcell *c = b->lclist[i];
#ifdef STATS
s->rev.st[b->op].csearched++;
@@ -1593,8 +1655,8 @@ unsigned int tcount /* grid touch count for this operation */
nsdi++; /* Continue through increasing sub-simplex dimenionality */
} /* until we get to the top. */
}
- /* Unlock the cache cell now that we're done with it */
- unget_rcell(s->rev.cache, b->lclist[i]);
+ /* Unlock the fxcell now that we're done with it */
+ unget_fxcell(s->rev.cache, b->lclist[i]);
} /* Next cell */
} /* Next chunk */
@@ -1651,16 +1713,16 @@ init_line(
}
DBGV(("current line cell = ", fdi, " %d", l->ci, "")); DBG((", t = %f, nvalid = %d\n",l->t,nvalid));
#ifdef DEBUG
-{
-int ii;
-double tt;
-printf("Current cell = ");
-for (ii = 0; ii < fdi; ii++) {
- tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
- printf(" %f - %f",tt,tt+s->rev.gw[ii]);
-}
-printf("\n");
-}
+ {
+ int ii;
+ double tt;
+ printf("Current cell = ");
+ for (ii = 0; ii < fdi; ii++) {
+ tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
+ printf(" %f - %f",tt,tt+s->rev.gw[ii]);
+ }
+ printf("\n");
+ }
#endif /* DEBUG */
if (nvalid)
return NULL;
@@ -1701,19 +1763,19 @@ next_line_cell(
DBGV(("current line cell =", fdi, " %d", l->ci, "")); DBG((", t = %f\n",l->t));
#ifdef DEBUG
-{
-int ii;
-double tt;
-printf("Current cell = ");
-for (ii = 0; ii < fdi; ii++) {
- tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
- printf(" %f - %f",tt,tt+s->rev.gw[ii]);
-}
-printf("\n");
-}
+ {
+ int ii;
+ double tt;
+ printf("Current cell = ");
+ for (ii = 0; ii < fdi; ii++) {
+ tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
+ printf(" %f - %f",tt,tt+s->rev.gw[ii]);
+ }
+ printf("\n");
+ }
#endif /* DEBUG */
- /* Compute reverse cell index */
+ /* Compute fxcell index */
for (rpp = s->rev.rev, f = 0; f < fdi; f++) {
if (l->ci[f] < 0 || l->ci[f] > rgres_1) { /* If outside valid reverse range */
DBG(("Outside list on dim %d, 0 <= %d <= %d\n", f, l->ci[f],rgres_1));
@@ -1729,164 +1791,599 @@ printf("\n");
/* ------------------------------------- */
/* Clip nearest support. */
-/* Track candidate cells nearest and furthest */
-struct _nncell_nf{
- double n, f;
-}; typedef struct _nncell_nf nncell_nf;
+/* Weighted distance function macro: */
+
+#define LCHW_SQ(fname, arg2type) \
+ \
+static double fname(rspl *s, double in1[MXDO], arg2type in2[MXDO]) { \
+ int f, fdi = s->fdi; \
+ double tt, rr = 0.0; \
+ \
+ /* Fall back */ \
+ if (!s->rev.lchweighted || fdi < 3) { \
+ for (f = 0; f < fdi; f++) { \
+ tt = in1[f] - (double)in2[f]; \
+ rr += tt * tt; \
+ } \
+ return rr; \
+ } \
+ \
+ { \
+ double dxsq = 0.0, dchsq; \
+ double dlsq, dcsq, dhsq; \
+ double dc, c1, c2; \
+ \
+ /* Compute delta L squared and delta E squared */ \
+ { \
+ double dl, da, db; \
+ dl = in1[0] - (double)in2[0]; \
+ da = in1[1] - (double)in2[1]; \
+ db = in1[2] - (double)in2[2]; \
+ \
+ dlsq = dl * dl; /* dl squared */ \
+ dchsq = da * da + db * db; \
+ } \
+ \
+ /* Add any extra dims */ \
+ for (f = 3; f < fdi; f++) { \
+ tt = in1[f] - (double)in2[f]; \
+ dxsq += tt * tt; \
+ } \
+ \
+ /* compute delta chromanance squared */ \
+ { \
+ /* Compute chromanance for the two colors */ \
+ c1 = sqrt(in1[1] * in1[1] + in1[2] * in1[2]); \
+ c2 = sqrt((double)in2[1] * (double)in2[1] + (double)in2[2] * (double)in2[2]); \
+ \
+ dc = c1 - c2; \
+ dcsq = dc * dc; \
+ } \
+ \
+ /* Compute delta hue squared */ \
+ /* (Hue is simply the orthogonal delta to chromanance in the a*b* plane) */ \
+ if ((dhsq = dchsq - dcsq) < 0.0) \
+ dhsq = 0.0; \
+ \
+ /* Compute weighted error squared */ \
+ rr = dxsq + s->rev.lchw_sq[0] * dlsq + s->rev.lchw_sq[1] * dcsq + s->rev.lchw_sq[2] * dhsq; \
+ \
+ return rr; \
+ } \
+}
-/* Given and empty nnrev index, create a list of */
-/* the forward cells that may contain the nearest value by */
-/* using and exaustive search. This is used for faststart. */
-static void fill_nncell(
- rspl *s,
- int *co, /* Integer coords of cell to be filled */
- int ix /* Index of cell to be filled */
-) {
+/* Compute weighted LCh output distance squared. */
+/* Weighting is to L,C,h, delta's squared - double[], double[] version */
+LCHW_SQ(lchw_sq, double)
+
+/* Weighting is to L,C,h, delta's squared - double[], float[] version */
+LCHW_SQ(lchw_sq_f, float)
+
+/* Notes:
+
+ Estimation accuracy is hobbled by 100% at HWEIGHT 1.0
+ compare to pure euclidean estimate, due to the conservative
+ maxDlc maxDh of points in group, but this reduces at larger
+ HWEIGHT's. The handicap also decreases quickly with tighter
+ group size, since C variation is diminished.
+
+ The handicap limits filtering efficiency for large group to group,
+ so ideally group size shouldn't be larger than about 10 DE in diameter.
+
+ It's not clear if any better approach is possible.
+*/
+
+#define NN_GCMIN (1e-6)
+
+/* Create a nn group. */
+/* If G != NULL, use it as group center rather than computing from members. */
+static void nn_grpinit(rspl *s, nn_grp *p, double **pnts, int npnts, double *G) {
+ int f, ee, ff, fdi = s->fdi;
int i;
- int e, di = s->di;
- int f, fdi = s->fdi;
- double cc[MXDO]; /* Cell center */
- double rr = 0.0; /* Cell radius */
- int **rpp, *rp;
- int gno = s->g.no;
- float *gp; /* Pointer to fwd grid points */
- nncell_nf *nf; /* cloase and far distances corresponding to list */
- double clfu = 1e38; /* closest furthest distance in list */
+ double *min[MXRO], *max[MXRO]; /* Pointers to points with min/max values */
+ double rad, radsq = -1.0; /* Span/radius squared */
+ int spf;
+ double dxsq = 0.0, desq, dchsq, dlcsq;
+ double dlsq, dcsq, dhsq;
+ double dc, c1, c2;
+ double c, minc = 1e200, maxc = -1.0;
+
+ if (G != NULL) {
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] = G[f];
+
+ if (fdi >= 3) {
+ /* Track minimum and maximum member C squared */
+ for (i = 0; i < npnts; i++) {
+ c = pnts[i][1] * pnts[i][1] + pnts[i][2] * pnts[i][2];
+ if (c < minc)
+ minc = c;
+ if (c > maxc)
+ maxc = c;
+ }
+ }
+
+ } else if (npnts <= 2) {
+
+ /* Compute center as simple average */
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] = 0.0;
- rpp = s->rev.nnrev + ix;
- rp = *rpp;
+ for (i = 0; i < npnts; i++) {
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] += pnts[i][f];
+
+ if (fdi >= 3) {
+ /* Track minimum and maximum member C squared */
+ c = pnts[i][1] * pnts[i][1] + pnts[i][2] * pnts[i][2];
+ if (c < minc)
+ minc = c;
+ if (c > maxc)
+ maxc = c;
+ }
+ }
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] *= 1.0/(double)npnts;
+
+ } else {
+ /* We establish a center point in un-weighted space, because this is */
+ /* what's needed for in-gamut work, and is computationally faster */
+ /* and easier than attempting it using weighted space. */
+
+ /* Find verticies of cell that have min and max values in output space */
+ for (f = 0; f < fdi; f++)
+ min[f] = max[f] = NULL;
+
+ for (ee = 0; ee < npnts; ee++) {
+ double *vp = pnts[ee];
+ for (f = 0; f < fdi; f++) {
+ if (min[f] == NULL || min[f][f] > vp[f])
+ min[f] = vp;
+ if (max[f] == NULL || max[f][f] < vp[f])
+ max[f] = vp;
+ }
+ }
+
+ /* Find the pair of points with the largest span (diameter) in output space */
+ for (ff = 0; ff < fdi; ff++) {
+ double ss;
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt;
+ tt = max[ff][f] - min[ff][f];
+ ss += tt * tt;
+ }
+ if (ss > radsq) {
+ radsq = ss;
+ spf = ff; /* Output dimension max was in */
+ }
+ }
+
+ /* Set initial bounding sphere */
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] = (max[spf][f] + min[spf][f])/2.0;
+ radsq /= 4.0; /* diam^2 -> rad^2 */
+ rad = sqrt(radsq);
+
+ /* Go though all the points again, expanding sphere if necessary */
+ for (ee = 0; ee < npnts; ee++) {
+ double ss;
+ double *vp = pnts[ee];
+
+ /* Compute distance squared of point to bounding shere */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = vp[f] - p->bcent[f];
+ ss += tt * tt;
+ }
+ if (ss > radsq) {
+ double tt;
+ /* DBG(("Expanding bounding sphere by %f\n",sqrt(ss) - rad)); */
+
+ ss = sqrt(ss) + EPS; /* Radius to point */
+ rad = (rad + ss)/2.0;
+ radsq = rad * rad;
+ tt = ss - rad;
+ for (f = 0; f < fdi; f++)
+ p->bcent[f] = (rad * p->bcent[f] + tt * vp[f])/ss;
+ } else {
+ /* DBG(("Bounding sphere encloses by %f\n",rad - sqrt(ss))); */
+ }
+ }
+ if (fdi >= 3) {
+ /* Establish the minimum and maximum member C squared */
+ for (ee = 0; ee < npnts; ee++) {
+ c = pnts[ee][1] * pnts[ee][1] + pnts[ee][2] * pnts[ee][2];
+ if (c < minc)
+ minc = c;
+ if (c > maxc)
+ maxc = c;
+ }
+ }
+ }
+
+ p->brad = p->bradsq = -1.0;
+ p->maxDlc = -1.0;
+ p->maxDh = p->maxDh_ = -1.0;
+ p->sratio = 1.0;
+ p->Wsratio = s->rev.lchw_sq[2];
+ p->bratio = 1.0;
+ p->Wbratio = s->rev.lchw_sq[2];
+ p->Gc = p->Gc_ = NN_GCMIN;
+
+ /* No weighting */
+ if (!s->rev.lchweighted || fdi < 3) {
+
+ for (i = 0; i < npnts; i++) {
+ desq = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = p->bcent[f] - pnts[i][f];
+ desq += tt * tt;
+ }
+ /* Track maximum euclidean distance */
+ if (desq > p->bradsq)
+ p->bradsq = desq;
+ }
+ p->brad = sqrt(p->bradsq); /* Distance rather than squared */
+
+ /* Weighted */
+ } else {
+ double maxde = -1.0;
+
+ /* Locate member maximum deltaLC and deltaH */
+ for (i = 0; i < npnts; i++) {
+
+ /* Compute delta L squared and delta E squared */
+ {
+ double dl, dasq, dbsq;
+ dl = p->bcent[0] - pnts[i][0];
+ dlsq = dl * dl; /* dl squared */
+ dasq = p->bcent[1] - pnts[i][1];
+ dasq *= dasq;
+ dbsq = p->bcent[2] - pnts[i][2];
+ dbsq *= dbsq;
+
+ dchsq = dasq + dbsq;
+ desq = dlsq + dchsq;
+ }
+
+ /* Add any extra dims */
+ for (f = 3; f < fdi; f++) {
+ double tt = p->bcent[f] - pnts[i][f];
+ dxsq += tt * tt;
+ }
+ desq += dxsq;
+
+ /* Track maximum euclidean distance too */
+ if (desq > p->bradsq)
+ p->bradsq = desq;
+
+ /* compute delta chromanance squared */
+ {
+ /* Compute chromanance of member to group center */
+ c1 = sqrt(p->bcent[1] * p->bcent[1] + p->bcent[2] * p->bcent[2]);
+ c2 = sqrt(pnts[i][1] * pnts[i][1] + pnts[i][2] * pnts[i][2]);
+
+ dc = c1 - c2;
+ dcsq = dc * dc;
+ }
+
+ /* Compute delta hue squared */
+ /* (Hue is simply the orthogonal delta to chromanance in the a*b* plane) */
+ if ((dhsq = dchsq - dcsq) < 0.0)
+ dhsq = 0.0;
+
+ /* Weighted delta extra + luminance + chromanance squared */
+ dlcsq = dxsq + s->rev.lchw_sq[0] * dlsq + s->rev.lchw_sq[1] * dcsq;
+
+ /* Using maxDlc & maxDh is an absolute worst case, but */
+ /* using a more exact approximation to the worst point */
+ /* for a given hue correction factor, doesn't seem to help */
+ /* for HWEIGHT > 1.5 */
+
+ /* Track maximum weighted deltaLC squared */
+ if (dlcsq > p->maxDlc)
+ p->maxDlc = dlcsq;
+
+ /* Track maximum deltaH squared */
+ if (dhsq > p->maxDh)
+ p->maxDh = dhsq;
+ }
+ p->brad = sqrt(p->bradsq); /* Euclidean distance rather than squared */
+ p->maxDh_ = sqrt(p->maxDh);
+
+ /* Pre-calculate center C squared */
+ p->Gc = p->bcent[1] * p->bcent[1] + p->bcent[2] * p->bcent[2];
+ if (p->Gc < NN_GCMIN)
+ p->Gc = NN_GCMIN;
+ p->Gc_ = sqrt(p->Gc);
+
+ /* Calculate hue scale down factor for Group center to smallest member C */
+ /* (This is used to scale point/center to center distance) */
+ if (minc < p->Gc) {
+ p->sratio = sqrt(minc/p->Gc);
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ p->Wsratio = (s->rev.lchw_sq[2] - 1.0) * p->sratio + 1.0;
+ else
+ p->Wsratio = s->rev.lchw_sq[2] * p->sratio;
+ }
+
+ /* Calculate hue scale up factor for Group center to largest member C */
+ /* (This is used to scale point/center to center distance) */
+ /* (For group target, multiply group ->bratio values ??) */
+ if (maxc > p->Gc) {
+ p->bratio = sqrt(maxc/p->Gc);
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ p->Wbratio = (s->rev.lchw_sq[2] - 1.0) * p->bratio + 1.0;
+ else
+ p->Wbratio = s->rev.lchw_sq[2] * p->bratio;
+ }
+ }
+}
+
+/* Return nz if point is within euclidean bounding sphere. */
+/* Also return distance squared in *dist if non-NULL */
+static int nn_insphere(rspl *s, double *dist, nn_grp *p, double *src) {
+ int f, fdi = s->fdi;
+ double desq = 0.0;
- /* Compute the center location and radius of the target cell */
for (f = 0; f < fdi; f++) {
- cc[f] = s->rev.gw[f] * (co[f] + 0.5) + s->rev.gl[f];
- rr += 0.25 * s->rev.gw[f] * s->rev.gw[f];
+ double tt = p->bcent[f] - src[f];
+ desq += tt * tt;
}
- rr = sqrt(rr);
-//printf("~1 fill_nncell() cell ix %d, coord %d %d %d, cent %f %f %f, rad %f\n",
-//ix, co[0], co[1], co[2], cc[0], cc[1], cc[2], rr);
-//printf("~1 total of %d fwd cells\n",gno);
- /* For all the forward cells: */
- for (gp = s->g.a, i = 0; i < gno; gp += s->g.pss, i++) {
- int ee;
- int uil; /* One is under the ink limit */
- double dn, df; /* Nearest and farthest distance of fwd cell values */
+ if (dist != NULL)
+ *dist = desq;
- /* Skip cubes that are on the outside edge of the grid */
- for (e = 0; e < di; e++) {
- if(G_FL(gp, e) == 0) /* At the top edge */
- break;
+ return desq <= p->bradsq;
+}
+
+/* Estimate possible smallest weighted distance of point to group. */
+/* If lgst != NULL, also return the estimated largest possible distance. */
+static double nn_pntgrp_est(rspl *s, double *lgst, nn_grp *p, double *src) {
+ int f, fdi = s->fdi;
+ double dxsq = 0.0, desq, dchsq;
+ double dlsq, dcsq, dhsq;
+ double dc, c1, c2;
+ double Tc; /* Point chromanance squared */
+ double sGrr; /* Min Point to group center diatance squared */
+ double bGrr; /* Max Point to group center diatance squared */
+ double rr; /* Largest member distance squared */
+ double sdist; /* Min. estimated distance squared */
+ double bdist; /* Max.. estimated distance squared */
+ double aratio = 1.0;
+
+ /* If not using LCh weighted distances */
+ if (!s->rev.lchweighted || fdi < 3) {
+
+ desq = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = p->bcent[f] - src[f];
+ desq += tt * tt;
}
- if (e < di) { /* Top edge - skip this cube */
- continue;
+
+ /* Return largest possible distance */
+ if (lgst != NULL) {
+ bdist = sqrt(desq) + p->brad + EPS;
+ *lgst = bdist;
}
- /* Compute the closest and furthest distances of nodes of current cell */
- dn = 1e38, df = 0.0;
- for (uil = ee = 0; ee < (1 << di); ee++) { /* For all grid points in the cube */
- double r;
- float *gt = gp + s->g.fhi[ee]; /* Pointer to cube vertex */
-
- if (!s->limiten || gt[-1] <= s->limitv)
- uil = 1;
+ /* Return min possible distance */
+ sdist = sqrt(desq) - p->brad - EPS;
+ if (sdist < 0.0)
+ sdist = 0.0;
+ return sdist;
- /* Update bounding box for this grid point */
- for (r = 0.0, f = 0; f < fdi; f++) {
- double tt = cc[f] - (double)gt[f];
- r += tt * tt;
- }
-//printf("~1 grid location %f %f %f rad %f\n",gt[0],gt[1],gt[2],sqrt(r));
- if (r < dn)
- dn = r;
- if (r > df)
- df = r;
- }
- /* Skip any fwd cells that are over the ink limit */
- if (!uil)
- continue;
+ /* We're using LCh weighting, so we need to do some adjustments */
+ } else {
+ /* Compute components of weighted distance of point */
+ /* to group center. */
+ {
+ double dl, dasq, dbsq;
+ dl = p->bcent[0] - src[0];
+ dlsq = dl * dl; /* dl squared */
+ dasq = p->bcent[1] - src[1];
+ dasq *= dasq;
+ dbsq = p->bcent[2] - src[2];
+ dbsq *= dbsq;
+
+ dchsq = dasq + dbsq;
+ }
+
+ /* Compute any extra dims */
+ for (f = 3; f < fdi; f++) {
+ double tt = p->bcent[f] - src[f];
+ dxsq += tt * tt;
+ }
+
+ /* compute delta chromanance squared of target to group center */
+ {
+ /* Compute delta chromanance between target point and group center */
+ c1 = p->Gc_;
+ c2 = Tc = src[1] * src[1] + src[2] * src[2];
+ c2 = sqrt(c2);
+ dc = c1 - c2;
+ dcsq = dc * dc;
+ }
+
+ /* Compute delta hue squared of target point to group center */
+ /* (Hue is simply the orthogonal delta to chromanance in the a*b* plane) */
+ if ((dhsq = dchsq - dcsq) < 0.0)
+ dhsq = 0.0;
+
+ /* Weighted values of L and C delta's */
+ dlsq *= s->rev.lchw_sq[0];
+ dcsq *= s->rev.lchw_sq[1];
+
+ /* Most distant member hue delta adjustment factor */
+ aratio = s->rev.lchw_sq[2];
+ if (Tc > p->Gc) {
+ aratio = sqrt(Tc/p->Gc);
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ aratio = (s->rev.lchw_sq[2] - 1.0) * aratio + 1.0;
+ else
+ aratio = s->rev.lchw_sq[2] * aratio;
+ }
- dn = sqrt(dn) - rr;
- df = sqrt(df) + rr;
+ /* Adjusted maximum member distance to group center */
+ rr = sqrt(p->maxDlc + aratio * p->maxDh);
-//printf("~1 checking cell %d, near %f, far %f\n",i,dn,df);
+ /* Return max. possible distance squared */
+ if (lgst != NULL) {
- /* Skip any that have a closest distance larger that the lists */
- /* closest furthest distance. */
- if (dn > clfu) {
-//printf("~1 skipping cell %d, near %f, far %f clfu %f\n",i,dn,df,clfu);
- continue;
+ /* Adjusted weighted max. distance squared of target to group center */
+ bGrr = dxsq + dlsq + dcsq + dhsq * p->Wbratio;
+
+ /* max. possible distance of target to most distant member */
+ bdist = sqrt(bGrr) + rr + EPS;
+ *lgst = bdist;
}
-//printf("~1 adding cell %d\n",i);
- if (rp == NULL) {
- if ((nf = (nncell_nf *) rev_malloc(s, 6 * sizeof(nncell_nf))) == NULL)
- error("rspl malloc failed - nncell_nf list");
- INCSZ(s, 6 * sizeof(nncell_nf));
- if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
- error("rspl malloc failed - rev.grid entry");
- INCSZ(s, 6 * sizeof(int));
- *rpp = rp;
- rp[0] = 6; /* Allocation */
- rp[1] = 4; /* Next empty cell */
- rp[2] = 1; /* Reference count */
- rp[3] = i;
- nf[3].n = dn;
- nf[3].f = df;
- rp[4] = -1;
- } else {
- int z = rp[1], ll = rp[0];
- if (z >= (ll-1)) { /* Not enough space */
- INCSZ(s, ll * sizeof(nncell_nf));
- INCSZ(s, ll * sizeof(int));
- ll *= 2;
- if ((nf = (nncell_nf *) rev_realloc(s, nf, sizeof(nncell_nf) * ll)) == NULL)
- error("rspl realloc failed - nncell_nf list");
- if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
- error("rspl realloc failed - rev.grid entry");
- *rpp = rp;
- rp[0] = ll;
- }
- rp[z] = i;
- nf[z].n = dn;
- nf[z++].f = df;
- rp[z] = -1;
- rp[1] = z;
- }
-
- if (df < clfu)
- clfu = df;
- }
-//printf("~1 Current list is:\n");
-//for (e = 3; rp[e] != -1; e++)
-//printf(" %d: Cell %d near %f far %f\n",e,rp[e],nf[e].n,nf[e].f);
-
- /* Now filter out any cells that have a closest point that is further than */
- /* closest furthest point */
- {
- int z, w, ll = rp[0];
+ /* Adjusted weighted min. distance squared of target to group center */
+ sGrr = dxsq + dlsq + dcsq + dhsq * p->Wsratio;
- /* For all the cells in the current list: */
- for (w = z = 3; rp[z] != -1; z++) {
+ /* min. possible distance of target to most distant member */
+ sdist = sqrt(sGrr) - rr - EPS;
+ if (sdist < 0.0)
+ sdist = 0.0;
- /* If the new cell nearest is greater than the existing cell closest, */
- /* then don't omit existing cell from the list. */
- if (clfu >= nf[z].n) {
- rp[w] = rp[z];
- nf[w].n = nf[z].n;
- nf[w].f = nf[z].f;
- w++;
- }
-//else printf("~1 deleting cell %d because %f >= %f\n",rp[z],clfu, nf[z].f);
+ return sdist;
+ }
+}
+
+/* Estimate possible smallest weighted distance of group to group. */
+/* If lgst != NULL, also return the estimated largest possible distance. */
+static double nn_grpgrp_est(rspl *s, double *lgst, nn_grp *p1, nn_grp *p2) {
+ int f, fdi = s->fdi;
+ double dxsq = 0.0, desq, dchsq;
+ double dlsq, dcsq, dhsq;
+ double dc, c1, c2;
+ double sGrr; /* Min Point to group center diatance squared */
+ double bGrr; /* Max Point to group center diatance squared */
+ double rr1, rr2; /* Largest member distance squared */
+ double sdist; /* Min. estimated distance squared */
+ double bdist; /* Max.. estimated distance squared */
+ double aratio1 = 1.0, aratio2 = 1.0;
+
+ /* If not using LCh weighted distances */
+ if (!s->rev.lchweighted || fdi < 3) {
+
+ desq = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = p1->bcent[f] - p2->bcent[f];
+ desq += tt * tt;
+ }
+
+ /* Return largest possible distance */
+ if (lgst != NULL) {
+ bdist = sqrt(desq) + p1->brad + p2->brad + EPS;
+ *lgst = bdist;
}
- rp[w] = rp[z];
+
+ /* Return min possible distance */
+ sdist = sqrt(desq) - p1->brad - p2->brad - EPS;
+ if (sdist < 0.0)
+ sdist = 0.0;
+ return sdist;
+
+ /* We're using LCh weighting, so we need to do some adjustments */
+ } else {
+ double Wratio;
+
+ /* Compute components of weighted distance of group center */
+ /* to group center. */
+ {
+ double dl, dasq, dbsq;
+ dl = p1->bcent[0] - p2->bcent[0];
+ dlsq = dl * dl; /* dl squared */
+ dasq = p1->bcent[1] - p2->bcent[1];
+ dasq *= dasq;
+ dbsq = p1->bcent[2] - p2->bcent[2];
+ dbsq *= dbsq;
+
+ dchsq = dasq + dbsq;
+ }
+
+ /* Compute any extra dims */
+ for (f = 3; f < fdi; f++) {
+ double tt = p1->bcent[f] - p2->bcent[f];
+ dxsq += tt * tt;
+ }
+
+ /* compute delta chromanance squared of point to group center */
+ {
+ /* Compute delta chromanance group centers */
+ c1 = p1->Gc_;
+ c2 = p2->Gc_;
+ dc = c1 - c2;
+ dcsq = dc * dc;
+ }
+
+ /* Compute delta hue squared of group centers */
+ /* (Hue is simply the orthogonal delta to chromanance in the a*b* plane) */
+ if ((dhsq = dchsq - dcsq) < 0.0)
+ dhsq = 0.0;
+
+ /* Weighted values of L and C delta's */
+ dlsq *= s->rev.lchw_sq[0];
+ dcsq *= s->rev.lchw_sq[1];
+
+ /* Most distant member hue delta adjustment factor */
+ aratio1 = aratio2 = s->rev.lchw_sq[2];
+
+ if ((p1->Gc_ + p1->maxDh) > p2->Gc_) {
+ aratio2 = (p1->Gc_ + p1->maxDh)/p2->Gc_;
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ aratio2 = (s->rev.lchw_sq[2] - 1.0) * aratio2 + 1.0;
+ else
+ aratio2 = s->rev.lchw_sq[2] * aratio2;
+
+ }
+ if ((p2->Gc_ + p2->maxDh) > p1->Gc_) {
+ aratio1 = (p2->Gc_ + p2->maxDh)/p1->Gc_;
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ aratio1 = (s->rev.lchw_sq[2] - 1.0) * aratio1 + 1.0;
+ else
+ aratio1 = s->rev.lchw_sq[2] * aratio1;
+ }
+
+ /* Adjusted maximum member distance to group center */
+ rr1 = sqrt(p1->maxDlc + aratio1 * p1->maxDh);
+ rr2 = sqrt(p2->maxDlc + aratio2 * p2->maxDh);
+
+ /* Returne max. possible distance squared */
+ if (lgst != NULL) {
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ Wratio = (s->rev.lchw_sq[2] - 1.0) * p1->bratio * p2->bratio + 1.0;
+ else
+ Wratio = s->rev.lchw_sq[2] * p1->bratio * p2->bratio;
+
+ /* Adjusted weighted max. distance squared of group centers */
+ bGrr = dxsq + dlsq + dcsq + dhsq * Wratio;
+
+ /* max. possible distance of target to most distant member */
+ bdist = sqrt(bGrr) + rr1 + rr2 + EPS;
+ *lgst = bdist;
+ }
+
+ if (s->rev.lchw_sq[2] > 1.0) /* Slightly improves filter ratio */
+ Wratio = (s->rev.lchw_sq[2] - 1.0) * p1->sratio * p2->sratio + 1.0;
+ else
+ Wratio = s->rev.lchw_sq[2] * p1->sratio * p2->sratio;
+
+ /* Adjusted weighted min. distance squared of group centers */
+ sGrr = dxsq + dlsq + dcsq + dhsq * Wratio;
+
+ /* min. possible distance of target to most distant member */
+ sdist = sqrt(sGrr) - rr1 - rr2 - EPS;
+ if (sdist < 0.0)
+ sdist = 0.0;
+
+ return sdist;
}
-//printf("~1 Current list is:\n");
-//for (e = 3; rp[e] != -1; e++)
-//printf(" %d: Cell %d near %f far %f\n",e,rp[e],nf[e].n,nf[e].f);
- free(nf);
-//printf("~1 Done\n");
}
+/* ------------------------------------------------------------ */
+static void fill_nncell(rspl *s, int *co, int ix);
+
/* Return the pointer to the list of nearest fwd cells given */
/* the target output values. The pointer will be to the first */
/* index in the list (ie. list address + 3) */
@@ -1907,21 +2404,39 @@ calc_fwd_nn_cell_list(
for (ix = 0, f = 0; f < fdi; f++) {
double t = (v[f] - s->rev.gl[f])/s->rev.gw[f];
mi[f] = (int)floor(t); /* Grid coordinate */
- if (mi[f] < 0) /* Clip to reverse range, so we always return a result */
+ if (mi[f] < 0) /* Clip to reverse range, so we always return a result */
mi[f] = 0;
else if (mi[f] > rgres_1)
mi[f] = rgres_1;
ix += mi[f] * s->rev.coi[f]; /* Accumulate reverse grid index */
}
+ s->rev.sb->rix = ix; /* Set diagnostic value */
+
rpp = s->rev.nnrev + ix;
if (*rpp == NULL) {
if (s->rev.fastsetup)
- fill_nncell(s, mi, ix);
+ fill_nncell(s, mi, ix); /* Fill on-demand */
if (*rpp == NULL)
rpp = s->rev.rev + ix; /* fall back to in-gamut lookup */
}
- if (*rpp == NULL)
+ if (*rpp == NULL) {
+#ifdef CHECK_NNLU
+ printf("Got NULL list for nearest search, targ %s,\n coord %s, rix %d\n", debPdv(fdi,v),debPiv(fdi,mi),ix);
+ if (ix < 0 || ix >= s->rev.no)
+ printf("Index is outside range 0 .. %d\n",s->rev.no-1);
+ else {
+ if (s->rev.nnrev[ix] == NULL)
+ printf(" nnrev = NULL\n");
+ else
+ printf(" nnrev length = %d\n",s->rev.nnrev[ix][1]-3);
+ if (s->rev.rev[ix] == NULL)
+ printf(" rev = NULL\n");
+ else
+ printf(" rev = length = %d\n",s->rev.rev[ix][1]-3);
+ }
+#endif
return NULL;
+ }
return (*rpp) + 3;
}
@@ -1932,6 +2447,7 @@ static int add_lu_svd(simplex *x);
static int add_locus(schbase *b, simplex *x);
static int add_auxil_lu_svd(schbase *b, simplex *x);
static int within_simplex(simplex *x, double *p);
+static int within_simplex_limit(simplex *x, double *p);
static void simplex_to_abs(simplex *x, double *in, double *out);
static int auxil_solve(schbase *b, simplex *x, double *xp);
@@ -1939,7 +2455,7 @@ static int auxil_solve(schbase *b, simplex *x, double *xp);
/* ---------------------- */
/* Exact search functions */
/* Return non-zero if cell is acceptable */
-static int exact_setsort(schbase *b, cell *c) {
+static int exact_setsort(schbase *b, fxcell *c) {
rspl *s = b->s;
int f, fdi = s->fdi;
double ss;
@@ -1948,11 +2464,11 @@ static int exact_setsort(schbase *b, cell *c) {
/* Check that the target lies within the cell bounding sphere */
for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = c->bcent[f] - b->v[f];
+ double tt = c->g.bcent[f] - b->v[f];
ss += tt * tt;
}
- if (ss > c->bradsq) {
- DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ if (ss > c->g.bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",debPdv(fdi,b->v),debPdv(fdi,c->g.bcent),sqrt(c->g.bradsq)));
return 0;
}
@@ -2019,7 +2535,7 @@ static int exact_compute(schbase *b, simplex *x) {
/* Compute the solution (in simplex space) */
lu_backsub(x->d_u, sdi, (int *)x->d_w, xp);
- /* Check that the solution is within the simplex */
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, xp)) == 0) {
DBG(("Solution rejected because not in simplex\n"));
return 0;
@@ -2068,7 +2584,7 @@ static int exact_compute(schbase *b, simplex *x) {
/* -------------------------- */
/* Auxiliary search functions */
-static int auxil_setsort(schbase *b, cell *c) {
+static int auxil_setsort(schbase *b, fxcell *c) {
rspl *s = b->s;
int f, fdi = b->s->fdi;
int ee, ixc = b->ixc;
@@ -2082,11 +2598,11 @@ static int auxil_setsort(schbase *b, cell *c) {
/* Check that the target lies within the cell bounding sphere */
for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = c->bcent[f] - b->v[f];
+ double tt = c->g.bcent[f] - b->v[f];
ss += tt * tt;
}
- if (ss > c->bradsq) {
- DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ if (ss > c->g.bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",debPdv(fdi,b->v),debPdv(fdi,c->g.bcent),sqrt(c->g.bradsq)));
return 0;
}
@@ -2132,7 +2648,7 @@ static int auxil_setsort(schbase *b, cell *c) {
}
/* Re-check whether it's worth searching cell */
-static int auxil_check(schbase *b, cell *c) {
+static int auxil_check(schbase *b, fxcell *c) {
int ee, ixc = b->ixc, nabove;
DBG(("Reverse auxiliary search, re-check cell\n"));
@@ -2184,11 +2700,11 @@ static int auxil_compute(schbase *b, simplex *x) {
for (f = 0; f <= x->sdi; f++)
sum += x->vix[f];
printf("Simplex of cell ix %d, sum 0x%x, sdi = %d, efdi = %d\n",x->ix, sum, x->sdi, x->efdi);
- printf("Target val %s\n",icmPdv(fdi, b->v));
+ printf("Target val %s\n",debPdv(fdi, b->v));
for (f = 0; f <= x->sdi; f++) {
int ix = x->vix[f], i;
float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
- printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,icmPfv(fdi, fcb));
+ printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,debPfv(fdi, fcb));
}
}
#endif
@@ -2242,7 +2758,7 @@ static int auxil_compute(schbase *b, simplex *x) {
/* Convert solution from simplex relative to absolute space */
simplex_to_abs(x, p, xp);
- DBG(("Got solution at %s\n", icmPdv(di,p)));
+ DBG(("Got solution at %s\n", debPdv(di,p)));
//printf("~~ soln = %f %f %f %f\n",p[0],p[1],p[2],p[3]);
//printf("~~ About to compute auxil distance\n");
@@ -2290,7 +2806,7 @@ static int auxil_compute(schbase *b, simplex *x) {
/* ------------------------------------ */
/* Locus range search functions */
-static int locus_setsort(schbase *b, cell *c) {
+static int locus_setsort(schbase *b, fxcell *c) {
rspl *s = b->s;
int f, fdi = s->fdi;
int lxi = b->lxi; /* Auxiliary we are finding min/max of */
@@ -2307,11 +2823,11 @@ static int locus_setsort(schbase *b, cell *c) {
/* Check that the target lies within the cell bounding sphere */
for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = c->bcent[f] - b->v[f];
+ double tt = c->g.bcent[f] - b->v[f];
ss += tt * tt;
}
- if (ss > c->bradsq) {
- DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ if (ss > c->g.bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",debPdv(fdi,b->v),debPdv(fdi,c->g.bcent),sqrt(c->g.bradsq)));
return 0;
}
@@ -2338,7 +2854,7 @@ static int locus_setsort(schbase *b, cell *c) {
}
/* Re-check whether it's worth searching simplexes */
-static int locus_check(schbase *b, cell *c) {
+static int locus_check(schbase *b, fxcell *c) {
int lxi = b->lxi; /* Auxiliary we are finding min/max of */
int ixc = b->ixc;
@@ -2372,12 +2888,12 @@ static int locus_compute(schbase *b, simplex *x) {
for (f = 0; f <= x->sdi; f++)
sum += x->vix[f];
printf("Simplex of cell ix %d, sum 0x%x, sdi = %d, efdi = %d\n",x->ix, sum, x->sdi, x->efdi);
- printf("Target val %s\n",icmPdv(fdi, b->v));
+ printf("Target val %s\n",debPdv(fdi, b->v));
for (f = 0; f <= x->sdi; f++) {
int ix = x->vix[f], i;
float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
double v[MXDO];
- printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,icmPfv(fdi, fcb));
+ printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,debPfv(fdi, fcb));
}
}
#endif
@@ -2419,7 +2935,7 @@ static int locus_compute(schbase *b, simplex *x) {
/* ------------------- */
/* Vector clipping search functions */
-static int clipv_setsort(schbase *b, cell *c) {
+static int clipv_setsort(schbase *b, fxcell *c) {
rspl *s = b->s;
int f, fdi = s->fdi;
double ss, dp;
@@ -2431,7 +2947,7 @@ static int clipv_setsort(schbase *b, cell *c) {
/* First compute dot product cdir . (bcent - v) */
/* == distance to center of sphere in direction of clip vector */
for (dp = 0.0, f = 0; f < fdi; f++) {
- dp += b->ncdir[f] * (c->bcent[f] - b->v[f]);
+ dp += b->ncdir[f] * (c->g.bcent[f] - b->v[f]);
}
if (s->limiten != 0 && c->limmin > s->limitv) {
@@ -2442,12 +2958,12 @@ static int clipv_setsort(schbase *b, cell *c) {
//printf("~~ dot product = %f\n",dp);
/* Now compute closest distance to sphere center */
for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = b->v[f] + dp * b->ncdir[f] - c->bcent[f];
+ double tt = b->v[f] + dp * b->ncdir[f] - c->g.bcent[f];
ss += tt * tt;
}
//printf("~~ distance to sphere center = %f\n",sqrt(ss));
- if (ss > c->bradsq) {
+ if (ss > c->g.bradsq) {
DBG(("Cell is rejected - wrong direction or bounding sphere\n"));
return 0;
}
@@ -2462,7 +2978,7 @@ static int clipv_setsort(schbase *b, cell *c) {
/* because we assume that nothing will set a small cdist */
/* before the search commences (unlike auxil). */
/* Note that line search loop exits on finding any solution. */
-static int clipv_check(schbase *b, cell *c) {
+static int clipv_check(schbase *b, fxcell *c) {
DBG(("Reverse clipping re-check\n"));
@@ -2471,12 +2987,12 @@ static int clipv_check(schbase *b, cell *c) {
double dist;
/* Compute a conservative "best possible solution clip distance" */
for (dist = 0.0, f = 0; f < fdi ; f++) {
- double tt = (c->bcent[f] - b->v[f]);
+ double tt = (c->g.bcent[f] - b->v[f]);
dist += tt * tt;
}
dist = sqrt(dist); /* Target distance to bounding */
- if (dist >= (c->brad + b->cdist)) { /* Equal or worse clip solution */
+ if (dist >= (c->g.brad + b->cdist)) { /* Equal or worse clip solution */
DBG(("Cell best possible solution worse than current\n"));
return 0;
}
@@ -2533,86 +3049,107 @@ static int clipv_compute(schbase *b, simplex *x) {
}
/* ------------------- */
-/* Nearest clipping search functions */
-static int clipn_setsort(schbase *b, cell *c) {
+/* Nearest clipping search functions. */
+/* We use weighted distances if lchweighted. */
+static int clipn_setsort(schbase *b, fxcell *c) {
rspl *s = b->s;
int f, fdi = s->fdi;
double ss;
- DBG(("Reverse nearest clipping search evaluate cell\n"));
+ DBG(("Reverse nearest clipping search evaluate fwd cell ix %d\n",c->ix));
+//if (b->rix == 7135) printf("Reverse nearest clipping search evaluate fwd cell ix %d\n",c->ix);
- /* Compute a conservative "best possible solution clip distance" */
- for (ss = 0.0, f = 0; f < fdi ; f++) {
- double tt = (c->bcent[f] - b->v[f]);
- ss += tt * tt;
- }
- ss = sqrt(ss); /* Target distance to bounding sphere */
- ss -= c->brad;
- if (ss < 0.0)
- ss = 0.0;
+ /* Compute an estimated weighted clip distance from target point to this fxcell */
+ ss = nn_pntgrp_est(s, NULL, &c->g, b->v);
/* Check that the cell could possibly improve the solution */
if (b->cdist < INF_DIST) { /* If some clip solution has been found */
if (ss >= b->cdist) { /* Equal or worse clip solution */
DBG(("Cell best possible solution worse than current\n"));
+
+//if (b->rix == 7135) {
+// printf("Cell best possible solution worse than current\n");
+// printf("current dist %f, best to fwd %f\n",b->cdist,ss);
+//}
return 0;
}
}
if (s->limiten != 0 && c->limmin > s->limitv) {
DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+//if (b->rix == 7135) printf("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv);
return 0;
}
- c->sort = ss; /* May be -ve if beyond clip target point ? */
+ c->sort = ss;
- DBG(("Cell is accepted\n"));
+ DBG(("Cell is accepted (%f < %f)\n",ss,b->cdist));
+//if (b->rix == 7135) printf("Cell is accepted (%f < %f)\n",ss,b->cdist);
return 1;
}
/* Clipping check functions */
-static int clipn_check(schbase *b, cell *c) {
+static int clipn_check(schbase *b, fxcell *c) {
- DBG(("Reverse nearest clipping re-check\n"));
+ DBG(("Reverse nearest clipping re-check fwd cell ix %d\n",c->ix));
+//if (b->rix == 7135) printf("Reverse nearest clipping re-check fwd cell ix %d\n",c->ix);
if (b->cdist < INF_DIST) { /* If some clip solution has been found */
/* re-use sort value, best possible distance to solution */
if (c->sort >= b->cdist) { /* Equal or worse clip solution */
- DBG(("Cell best possible solution worse than current\n"));
+ DBG(("Cell best possible solution now worse than current\n"));
+//if (b->rix == 7135) {
+// printf("Cell best possible solution now worse than current\n");
+// printf("current dist %f, best to fwd %f\n",b->cdist,c->sort);
+//}
return 0;
}
}
DBG(("Cell is still ok\n"));
+//if (b->rix == 7135) printf("Cell is still ok\n");
return 1;
}
+static int lchw_nnearest_clip_solve(schbase *b, simplex *x, double *xp, double *xv, double *err);
static int nnearest_clip_solve(schbase *b, simplex *x, double *xp, double *xv, double *err);
/* Compute a clip solution */
static int clipn_compute(schbase *b, simplex *x) {
rspl *s = b->s;
int f, fdi = s->fdi;
- datai p; /* Input space solution */
- datao v; /* Output space solution */
- double err; /* output error of solution */
- int wsrv; /* Within simplex return value */
+ datai p; /* Simplex input space solution */
+ datao v; /* Output space solution */
+ double err; /* output error of solution */
+ int wsrv; /* Within simplex return value */
- DBG(("Clipn: computing possible solution simplex %d, sdi = %d, efdi = %d\n",x->si,x->sdi,x->efdi));
+ DBG(("Clipn: computing possible solution cell %d, simplex %d, sdi = %d, efdi = %d\n",x->ix,x->si,x->sdi,x->efdi));
+//if (b->rix == 7135) printf("Clipn: computing possible solution cell %d, simplex %d, sdi = %d, efdi = %d\n",x->ix,x->si,x->sdi,x->efdi);
/* Compute a solution value */
- if ((wsrv = nnearest_clip_solve(b, x, p, v, &err)) == 0) {
- DBG(("Doesn't contain a solution\n"));
- return 0;
+ if (s->rev.lchweighted) {
+ if ((wsrv = lchw_nnearest_clip_solve(b, x, p, v, &err)) == 0) {
+ DBG(("Doesn't contain a solution\n"));
+//if (b->rix == 7135) printf("Doesn't contain a solution\n");
+ return 0;
+ }
+ } else {
+ if ((wsrv = nnearest_clip_solve(b, x, p, v, &err)) == 0) {
+ DBG(("Doesn't contain a solution\n"));
+//if (b->rix == 7135) printf("Doesn't contain a solution\n");
+ return 0;
+ }
}
/* We want the smallest clip error */
if (err >= b->cdist) { /* Equal or worse clip solution */
- DBG(("better solution has been found before\n"));
+ DBG(("better solution has been found before (%f < %f)\n",b->cdist,err));
+//if (b->rix == 7135) printf("better solution has been found before (%f < %f)\n",b->cdist,err);
return 0;
}
- DBG(("######## Accepting new clipn solution with error %f\n",err));
+ DBG(("######## Accepting new clipn solution with error %f (replaces %f)\n",err,b->cdist));
+//if (b->rix == 7135) printf("######## Accepting new clipn solution with error %f (replaces %f)\n",err,b->cdist);
simplex_to_abs(x, b->cpp[0].p, p); /* Convert to abs. space & copy */
@@ -2646,7 +3183,7 @@ double *xp /* Return solution xp[sdi] */
int f, efdi = x->efdi;
int dof = sdi-efdi; /* Degree of freedom of simplex locus */
int *icomb = x->psxi->icomb; /* abs -> simplex coordinate translation */
- double auxt[MXRI]; /* Simplex relative auxiliary targets */
+ double auxt[MXRI]; /* Simplex relative auxiliary targets */
double bb[MXRI];
int wsrv; /* Within simplex return value */
@@ -2667,8 +3204,9 @@ double *xp /* Return solution xp[sdi] */
/* Compute the solution (in simplex space) */
lu_backsub(x->d_u, sdi, (int *)x->d_w, xp);
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, xp)) != 0) {
- DBG(("Got solution at %s\n", icmPdv(sdi,xp)));
+ DBG(("Got solution at %s\n", debPdv(sdi,xp)));
return wsrv; /* OK, got solution */
}
@@ -2708,8 +3246,9 @@ double *xp /* Return solution xp[sdi] */
for (e = 0; e < sdi; e++) {
xp[e] = x->lo_bd[e] + tt * x->lo_l[e][0];
}
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, xp)) != 0) {
- DBG(("Got solution %s\n",icmPdv(di,xp)));
+ DBG(("Got solution %s\n",debPdv(di,xp)));
return wsrv; /* OK, got solution */
}
DBG(("No solution (not within simplex)\n"));
@@ -2755,8 +3294,9 @@ double *xp /* Return solution xp[sdi] */
xp[e] = x->lo_bd[e] + tt;
}
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, xp)) != 0) {
- DBG(("Got solution %s\n",icmPdv(di,xp)));
+ DBG(("Got solution %s\n",debPdv(di,xp)));
return wsrv; /* OK, got solution */
}
DBG(("No solution (not within simplex)\n"));
@@ -2793,7 +3333,7 @@ simplex *x
/* Compute the solution (in simplex space) */
lu_backsub(x->d_u, sdi, (int *)x->d_w, pp);
- /* Check that the solution is within the simplex */
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, pp)) != 0) {
double xval;
int lxi = b->lxi; /* Auxiliary we are finding min/max of (Abs space) */
@@ -2913,11 +3453,11 @@ double *err /* Output error distance at solution point */
return 0; /* No solution */
}
- /* Check that the solution is within the simplex */
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, tb)) != 0) {
double dist; /* distance to clip target */
- DBG(("Got solution within simplex %s\n", icmPdv(sdi,tb)));
+ DBG(("Got solution within simplex %s\n", debPdv(sdi,tb)));
/* Compute the output space solution point */
for (f = 0; f < fdi; f++) {
@@ -2932,6 +3472,9 @@ double *err /* Output error distance at solution point */
for (e = 0; e < sdi; e++)
xp[e] = tb[e];
+ // ~~~ are we properly checking if the intersection is
+ // ~~~ backwards rather than forwards in the line direction ?
+
/* Compute distance to clip target */
for (dist = 0.0, f = 0; f < fdi ; f++) {
double tt = (b->v[f] - xv[f]);
@@ -2952,7 +3495,7 @@ double *err /* Output error distance at solution point */
/* - - - - - - - - - - - - - - - - - - - - - - - */
/* Find the point on the simplexes valid surface, that is closest */
-/* to the target output value. */
+/* to the target output value, for the linear (unweighted) case. */
/* We expect to be given a sub simplex with sdi = fdi-1, and efdi = fdi */
/* or a limit sub-simplex with sdi = fdi, and efdi = fdi+1 */
/* Return zero if solution canot be calculated, */
@@ -2963,7 +3506,7 @@ schbase *b,
simplex *x,
double *xp, /* Return solution (simplex parameter space) */
double *xv, /* Return solution (output space) */
-double *err /* Output error distance at solution point */
+double *err /* Output error (weighted) distance at solution point */
) {
rspl *s = b->s;
int e, sdi = x->sdi;
@@ -2977,14 +3520,17 @@ double *err /* Output error distance at solution point */
if (sdi == 0) { /* Solution is vertex */
wsrv = 1;
for (f = 0; f < efdi; f++)
- xv[f] = x->v[sdi][f]; /* Copy vertex value */
- if (x->v[sdi][fdi] > s->limitv) {
+ xv[f] = x->v[0][f]; /* Copy vertex value */
+ if (x->v[0][fdi] > s->limitv) {
if (s->limiten) /* Needed when limiten == 0 */
return 0; /* Over ink limit - no good */
wsrv = 2; /* Would be over */
}
- DBG(("Got assumed vertex solution\n"));
+ DBG(("Got assumed vertex solution (vtx ix %d)\n",x->vix[0]));
+
+ /* General linear nearest solver */
} else {
+
#ifdef NEVER /* Don't specialise ink limit version - use INKSCALE fudge instead */
if (!(x->flags & SPLX_CLIPSX)) { /* Not an ink limited plane simplex */
@@ -3002,22 +3548,24 @@ double *err /* Output error distance at solution point */
/* Find least squares solution */
svdbacksub(x->d_u, x->d_w, x->d_v, tb, tb, efdi, sdi);
- /* Check that the solution is within the simplex */
+ /* Check that the solution is within the simplex & meets ink limit */
if ((wsrv = within_simplex(x, tb)) == 0) {
DBG(("Nearest clip solution not in simplex\n"));
return 0; /* No solution */
}
- DBG(("Got solution within simplex %s\n",icmPdv(sdi,tb)));
+ DBG(("Got solution within simplex %s\n",debPdv(sdi,tb)));
+//if (b->rix == 7135) printf("Got solution within simplex params %s\n",debPdv(sdi,tb));
+//if (b->rix == 7135) printf(" verticies ix %s\n",debPiv(sdi+1,x->vix));
/* Compute the output space solution point */
for (f = 0; f < fdi; f++) {
double tt = 0.0;
- for (e = 0; e < sdi; e++) {
+ for (e = 0; e < sdi; e++)
tt += (x->v[e][f] - x->v[e+1][f]) * tb[e];
- }
xv[f] = tt + x->v[sdi][f];
}
+//if (b->rix == 7135) printf("Computed Got simplex solution %s\n",debPdv(fdi,xv));
#ifdef NEVER /* ~~1 Haven't figured out equations to make this a special case. */
/* Content to use INKSCALE fudge and rely on SVD least squares. */
} else {
@@ -3031,21 +3579,22 @@ double *err /* Output error distance at solution point */
}
#endif
+
+ /* Copy to return array */
+ for (e = 0; e < sdi; e++)
+ xp[e] = tb[e];
}
- /* Copy to return array */
- for (e = 0; e < sdi; e++)
- xp[e] = tb[e];
+ /* Compute weighted distance to clip target */
+ dist = sqrt(lchw_sq(s, b->v, xv));
+
+//if (b->rix == 7135 && dist < b->cdist) {
+// printf("Got dist %f from %s -> %s with weight %d, %s\n", dist,debPdv(fdi,b->v),debPdv(fdi,xv),s->rev.lchweighted,debPdv(fdi,s->rev.lchw)); }
- /* Compute distance to clip target */
- for (dist = 0.0, f = 0; f < fdi ; f++) {
- double tt = (b->v[f] - xv[f]);
- dist += tt * tt;
- }
DBGV(("Nearest clip output soln: ",fdi," %f", xv, "\n"));
- /* Return the solution in xp[]m xv[] and *err */
- *err = sqrt(dist);
+ /* Return the solution in xp[], xv[] and *err */
+ *err = dist;
DBG(("Nearest clip returning a solution with error %f\n",*err));
return wsrv;
@@ -3054,7 +3603,7 @@ double *err /* Output error distance at solution point */
#ifdef NEVER
/* Utility to convert an implicit ink limit plane equation */
-/* (held at the end of the simplex output value equations), */
+/* held at the end of the simplex output value equations), */
/* into a parameterized surface equation. */
static void
compute_param_limit_surface(
@@ -3149,7 +3698,869 @@ double de[MXRO] /* Delta */
#endif
+/* -------------------------------------------------------- */
+static int lchw_edge_solve(rspl *s, double *vv, double *p, double *vt, double v[MXRI+1][MXRO+1]);
+static int lchw_tri_solve(rspl *s, double *vv, double *p, double *vt, double v[MXRI+1][MXRO+1]);
+
+/* Find the point on the simplexes valid surface, that is closest */
+/* to the target output value, for the LCh weighted case. */
+/* We use Newton itteration to solve this for the 1D (line) and 2D (triangle) */
+/* cases, and explicitly decode the ink limit surfaces back to point, line */
+/* and triangled cases. */
+/* We expect to be given a sub simplex with sdi = 0..2, and efdi = fdi */
+/* or a limit sub-simplex with sdi = 1..3, and efdi = fdi+1 */
+/* We bail with an assert if we get more than 2D to solve. */
+/* Return zero if solution canot be calculated, */
+/* return 1 normally, 2 if solution would be above the (disabled) ink limit */
+static int
+lchw_nnearest_clip_solve(
+schbase *b,
+simplex *x,
+double *xp, /* Return solution (simplex parameter space) */
+double *xv, /* Return solution (output space) */
+double *err /* Output error (weighted) distance at solution point */
+) {
+ rspl *s = b->s;
+ int e, ee, sdi = x->sdi;
+ int f, fdi = s->fdi, efdi = x->efdi;
+ double tb[MXRO]; /* RHS & Parameter solution */
+ double dist; /* distance to clip target */
+ int wsrv = 0; /* Within simplex return value */
+
+ DBG(("LChw nearest clip solution called, cell %d, splx %d\n", x->ix, x->si));
+
+ /* - - - - - - - */
+ if (sdi == 0) { /* Solution is vertex */
+ wsrv = 1;
+ for (f = 0; f < efdi; f++)
+ xv[f] = x->v[0][f]; /* Copy vertex value */
+ if (x->v[0][fdi] > s->limitv) {
+ if (s->limiten) /* Needed when limiten == 0 */
+ return 0; /* Over ink limit - no good */
+ wsrv = 2; /* Would be over */
+ }
+ DBG(("Got assumed vertex solution (vtx ix %d)\n",x->vix[0]));
+
+ /* - - - - - - - */
+ /* Ink limit simplex case */
+ } else if (efdi == (fdi+1)) {
+
+ /* Convert line into vertex and return it */
+ if (sdi == 1) {
+ wsrv = 1;
+
+ /* Ink limit plane point along line */
+ xp[0] = (s->limitv - x->v[1][fdi])/(x->v[0][fdi] - x->v[1][fdi]);
+
+ /* Output value at that point */
+ for (f = 0; f < fdi; f++)
+ xv[f] = (x->v[0][f] - x->v[1][f]) * xp[0] + x->v[1][f];
+
+ DBG(("Got ink limit point on edge\n"));
+
+ /* Turn triangle into line and solve line. */
+ } else if (sdi == 2) {
+ int pos = 0, neg = 0;
+ int ix[MXRI+1]; /* Odd index and the two other indexes */
+ double p[MXRI+1], pp[MXRI+1];
+ double v[MXRI+1][MXRO+1];
+
+ /* Count ink limit signs of vertexes */
+ for (e = 0; e <= sdi; e++) {
+ ix[e] = e;
+ if (x->v[e][fdi] > s->limitv)
+ pos++;
+ else
+ neg++;
+ }
+
+ /* We expect one vertex to be on the other side of the */
+ /* ink limit to the two others. */
+ if (pos == 0 || neg == 0)
+ error("Ink limit tri doesn't have one opposite sign");
+
+ /* Make the first ix be the odd one */
+ if (pos == 1) {
+ if (x->v[0][fdi] <= s->limitv) {
+ if (x->v[1][fdi] > s->limitv) {
+ ix[0] = 1;
+ ix[1] = 0;
+ } else {
+ ix[0] = 2;
+ ix[2] = 0;
+ }
+ }
+ } else {
+ if (x->v[0][fdi] > s->limitv) {
+ if (x->v[1][fdi] <= s->limitv) {
+ ix[0] = 1;
+ ix[1] = 0;
+ } else {
+ ix[0] = 2;
+ ix[2] = 0;
+ }
+ }
+ }
+
+ /* Compute the points on the two edges that cross the ink limit. */
+ /* i.e. for edges ix 0..1 & 0..2 */
+ pp[0] = (s->limitv - x->v[ix[1]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[1]][fdi]);
+ pp[1] = (s->limitv - x->v[ix[2]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[2]][fdi]);
+ for (f = 0; f < fdi; f++) {
+ v[0][f] = (x->v[ix[0]][f] - x->v[ix[1]][f]) * pp[0] + x->v[ix[1]][f];
+ v[1][f] = (x->v[ix[0]][f] - x->v[ix[2]][f]) * pp[1] + x->v[ix[2]][f];
+ }
+
+ /* Solve it */
+ if ((wsrv = lchw_edge_solve(s, xv, p, b->v, v)) != 0) {
+
+ /* Figure out the solution simplex coords */
+ /* (p is weighting of lower indexes vertex) */
+
+ /* Convert solution simplex coords into baricentric weighting */
+ p[1] = 1.0 - p[0];
+
+ /* Sum baricentric weightings for each vertex */
+ for (e = 0; e <= sdi; e++)
+ xp[e] = 0.0;
+
+ xp[ix[0]] += pp[0] * p[0];
+ xp[ix[1]] += (1.0 - pp[0]) * p[0];
+ xp[ix[0]] += pp[1] * p[1];
+ xp[ix[2]] += (1.0 - pp[1]) * p[1];
+
+ /* Convert back to simplex coords */
+ xp[1] = 1.0 - xp[2];
+ xp[0] = xp[0];
+
+ DBG(("Got ink limit edge in triangle\n"));
+ }
+
+ /* Turn tetrahedron into one or two triangles */
+ /* and solve triangles. */
+ } else if (sdi == 3) {
+ int pos = 0, neg = 0;
+ int ix[MXRI+1]; /* Odd index and the three other indexes or 2 + 2 */
+ double p[MXRI+1], pp[MXRI+1];
+ double v[MXRI+1][MXRO+1];
+
+ /* Count ink limit signs of vertexes */
+ for (e = 0; e <= sdi; e++) {
+ ix[e] = e;
+ if (x->v[e][fdi] > s->limitv)
+ pos++;
+ else
+ neg++;
+ }
+
+ /* We expect one or two vertexes t be on the other side of the */
+ /* ink limit to the two others. */
+ if (pos == 0 || neg == 0)
+ error("Ink limit tetrahedron doesn't have one opposite sign");
+
+ /* If we can decompose this into a single triangle */
+ if (pos == 1 || neg == 1) {
+
+ /* Make the first ix be the odd one */
+ for (e = 0; e <= sdi; e++) {
+ if ((pos == 1 && x->v[e][fdi] > s->limitv)
+ || (neg == 1 && x->v[e][fdi] <= s->limitv)) {
+ int tt = ix[0];
+ ix[0] = e;
+ ix[e] = tt;
+ break;
+ }
+ }
+
+ /* Compute the points on the three edges that cross the ink limit. */
+ /* i.e. for edges ix 0..1, 0..2 & 0..3 */
+ pp[0] = (s->limitv - x->v[ix[1]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[1]][fdi]);
+ pp[1] = (s->limitv - x->v[ix[2]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[2]][fdi]);
+ pp[2] = (s->limitv - x->v[ix[3]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[3]][fdi]);
+ for (f = 0; f < fdi; f++) {
+ v[0][f] = (x->v[ix[0]][f] - x->v[ix[1]][f]) * pp[0] + x->v[ix[1]][f];
+ v[1][f] = (x->v[ix[0]][f] - x->v[ix[2]][f]) * pp[1] + x->v[ix[2]][f];
+ v[2][f] = (x->v[ix[0]][f] - x->v[ix[3]][f]) * pp[2] + x->v[ix[3]][f];
+ }
+
+ /* Solve it */
+ if ((wsrv = lchw_tri_solve(s, xv, p, b->v, v)) != 0) {
+
+ /* Figure out the solution simplex coords */
+ /* (p is weighting of lower indexes vertex) */
+
+ /* Convert solution simplex coords into baricentric weighting */
+ p[2] = 1.0 - p[1];
+ p[1] = p[1] - p[0];
+ p[0] = p[0];
+
+ /* Sum baricentric weightings for each vertex */
+ for (e = 0; e <= sdi; e++)
+ xp[e] = 0.0;
+
+ xp[ix[0]] += pp[0] * p[0];
+ xp[ix[1]] += (1.0 - pp[0]) * p[0];
+ xp[ix[0]] += pp[1] * p[1];
+ xp[ix[2]] += (1.0 - pp[1]) * p[1];
+ xp[ix[0]] += pp[2] * p[2];
+ xp[ix[3]] += (1.0 - pp[2]) * p[2];
+
+ /* Convert back to simplex coords */
+ xp[2] = 1.0 - xp[3];
+ xp[1] = xp[1] + xp[0];
+ xp[0] = xp[0];
+
+ DBG(("Got single ink limit triangle in tetrahedron\n"));
+ }
+
+ /* We need to decompose this into two triangles */
+ } else {
+ int wsrv2 = 0;
+ double dist2;
+ double xv2[MXRO]; /* 2nd triangle solution */
+
+ /* Make the first two ix's be the same, leaving second two the same. */
+ for (e = 1; e <= sdi; e++) {
+ if (x->v[0][fdi] > s->limitv && x->v[e][fdi] > s->limitv) {
+ int tt = ix[1];
+ ix[1] = e;
+ ix[e] = tt;
+ break;
+ }
+ }
+
+ /* We choose disjoint vertex pairs as the common edge of the two */
+ /* triangles, and then use each of the remaining pairs to form */
+ /* the other edges. */
+ /* i.e. common edge 0..2 + 1..3, then add 0..3 then 1..2 */
+ pp[0] = (s->limitv - x->v[ix[2]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[2]][fdi]);
+ pp[1] = (s->limitv - x->v[ix[3]][fdi])/(x->v[ix[1]][fdi] - x->v[ix[3]][fdi]);
+ pp[2] = (s->limitv - x->v[ix[3]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[3]][fdi]);
+ for (f = 0; f < fdi; f++) {
+ v[0][f] = (x->v[ix[0]][f] - x->v[ix[2]][f]) * pp[0] + x->v[ix[2]][f];
+ v[1][f] = (x->v[ix[1]][f] - x->v[ix[3]][f]) * pp[1] + x->v[ix[3]][f];
+ v[2][f] = (x->v[ix[0]][f] - x->v[ix[3]][f]) * pp[2] + x->v[ix[3]][f];
+ }
+
+ /* Solve first one */
+ if ((wsrv = lchw_tri_solve(s, xv, p, b->v, v)) != 0) {
+
+ dist = sqrt(lchw_sq(s, b->v, xv));
+
+ /* Figure out the solution simplex coords */
+ /* (p is weighting of lower indexes vertex) */
+
+ /* Convert solution simplex coords into baricentric weighting */
+ p[2] = 1.0 - p[1];
+ p[1] = p[1] - p[0];
+ p[0] = p[0];
+
+ /* Sum baricentric weightings for each vertex */
+ for (e = 0; e <= sdi; e++)
+ xp[e] = 0.0;
+
+ xp[ix[0]] += pp[0] * p[0];
+ xp[ix[2]] += (1.0 - pp[0]) * p[0];
+ xp[ix[1]] += pp[1] * p[1];
+ xp[ix[3]] += (1.0 - pp[1]) * p[1];
+ xp[ix[0]] += pp[2] * p[2];
+ xp[ix[3]] += (1.0 - pp[2]) * p[2];
+
+ /* Convert back to simplex coords */
+ xp[2] = 1.0 - xp[3];
+ xp[1] = xp[1] + xp[0];
+ xp[0] = xp[0];
+ }
+
+ /* Setup other triangle, 0..2 + 1..3, with 1..2 */
+ pp[0] = (s->limitv - x->v[ix[2]][fdi])/(x->v[ix[0]][fdi] - x->v[ix[2]][fdi]);
+ pp[1] = (s->limitv - x->v[ix[3]][fdi])/(x->v[ix[1]][fdi] - x->v[ix[3]][fdi]);
+ pp[2] = (s->limitv - x->v[ix[2]][fdi])/(x->v[ix[1]][fdi] - x->v[ix[2]][fdi]);
+ for (f = 0; f < fdi; f++) {
+ v[0][f] = (x->v[ix[0]][f] - x->v[ix[2]][f]) * pp[0] + x->v[ix[2]][f];
+ v[1][f] = (x->v[ix[1]][f] - x->v[ix[3]][f]) * pp[1] + x->v[ix[3]][f];
+ v[2][f] = (x->v[ix[1]][f] - x->v[ix[2]][f]) * pp[2] + x->v[ix[2]][f];
+ }
+
+ /* Solve second triangle */
+ if ((wsrv2 = lchw_tri_solve(s, xv2, p, b->v, v)) != 0) {
+
+ dist2 = sqrt(lchw_sq(s, b->v, xv));
+
+ /* Use this second solution */
+ if (wsrv == 0 || dist2 < dist) {
+
+ dist = dist2;
+
+ /* Figure out the solution simplex coords */
+ /* (p is weighting of lower indexes vertex) */
+
+ /* Convert solution simplex coords into baricentric weighting */
+ p[2] = 1.0 - p[1];
+ p[1] = p[1] - p[0];
+ p[0] = p[0];
+
+ /* Sum baricentric weightings for each vertex */
+ for (e = 0; e <= sdi; e++)
+ xp[e] = 0.0;
+
+ xp[ix[0]] += pp[0] * p[0];
+ xp[ix[2]] += (1.0 - pp[0]) * p[0];
+ xp[ix[1]] += pp[1] * p[1];
+ xp[ix[3]] += (1.0 - pp[1]) * p[1];
+ xp[ix[1]] += pp[2] * p[2];
+ xp[ix[2]] += (1.0 - pp[2]) * p[2];
+
+ /* Convert back to simplex coords */
+ xp[2] = 1.0 - xp[3];
+ xp[1] = xp[1] + xp[0];
+ xp[0] = xp[0];
+
+ for (f = 0; f < fdi; f++)
+ xv[f] = xv2[f];
+
+ } else {
+ wsrv2 = 0;
+ }
+ }
+
+#ifdef DEBUG
+ if (wsrv2)
+ DBG(("Got second ink limit triangle in tetrahedron\n"));
+ else if (wsrv)
+ DBG(("Got first ink limit triangle in tetrahedron\n"));
+#endif
+ *err = dist;
+ return wsrv;
+ }
+ } else {
+ error("rev: lchw_nnearest_clip_solve sdi = %d\n",sdi);
+ }
+
+ /* All solutions computed on the ink limit surface */
+ /* are assumed to be valid */
+
+ /* - - - - - - - */
+ /* Non-ink limit simplex case */
+ } else {
+
+ /* Line */
+ if (sdi == 1) {
+ wsrv = lchw_edge_solve(s, xv, xp, b->v, x->v);
+
+ DBG(("Got line solution\n"));
+
+ /* Triangle */
+ } else if (sdi == 2) {
+ wsrv = lchw_tri_solve(s, xv, xp, b->v, x->v);
+
+ DBG(("Got triangle solution\n"));
+
+ /* Oops */
+ } else {
+ error("rev: lchw_nnearest_clip_solve sdi = %d\n",sdi);
+ }
+
+ /* Check that the result is within the ink limit */
+ if (wsrv != 0)
+ wsrv = within_simplex_limit(x, xp);
+ }
+
+ if (wsrv == 0)
+ return wsrv;
+
+ /* Compute weighted distance to clip target */
+ dist = sqrt(lchw_sq(s, b->v, xv));
+
+ DBGV(("LChw nearest clip output soln: ",fdi," %f", xv, "\n"));
+
+ /* Return the solution in xp[], xv[] and *err */
+ *err = dist;
+
+ DBG(("LChw nearest clip returning a solution with error %f\n",*err));
+
+#ifdef NEVER
+ {
+ double chxv[MXRO];
+
+ printf("LChw nearest clip returning a solution with error %f\n",dist);
+
+ printf("Solution (sx in) %s -> out %s\n", debPdv(sdi, xp), debPdv(fdi, xv));
+
+ if (dist < b->cdist) { /* Equal or worse clip solution */
+ printf("Will be new best solution\n");
+ }
+
+ /* Check the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++)
+ tt += (x->v[e][f] - x->v[e+1][f]) * xp[e];
+ chxv[f] = tt + x->v[sdi][f];
+ }
+ for (f = 0; f < fdi; f++) {
+ if (fabs(chxv[f] - xv[f]) > 1e-3) {
+ break;
+ }
+ }
+ if (f < fdi)
+ printf(" ###### Check of out failed: %s\n", debPdv(fdi, chxv));
+ }
+#endif
+
+ return wsrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Edge lchw Newton itteration code */
+
+#ifdef NEVER /* Not actually used here */
+/* return weighted delta squared for target to edge at param value p */
+static double lchw_edge_sq(rspl *s, double *vt, double v[MXRI+1][MXRO+1], double p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double dlsq; /* Delta L squared */
+ double da, db, dchsq; /* Delta CH squared */
+ double ct, cv, dc, dcsq; /* Delta C squared */
+ double lcomp, chcomp, ccomp;
+ double de;
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++)
+ vv[f] = (v[0][f] - v[1][f]) * p + v[1][f];
+
+ /* Delta L component */
+ dlsq = vv[0] - vt[0];
+ dlsq = dlsq * dlsq;
+ lcomp = s->rev.lchw_sq[0] * dlsq;
+
+ /* Delta CH component */
+ da = vv[1] - vt[1];
+ db = vv[2] - vt[2];
+ dchsq = da * da + db * db;
+ chcomp = s->rev.lchw_sq[2] * dchsq;
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = ct - cv;
+ dcsq = dc * dc;
+
+ ccomp = s->rev.lchw_chsq * dcsq; /* w = cw - hw because dh = dch - dc */
+
+ de = lcomp + chcomp + ccomp;
+
+ return de;
+}
+#endif /* NEVER */
+
+/* return weighted 1st derivativ of delta squared for target to edge at param value p */
+static double lchw_edge_Dp_sq(rspl *s, double *vt, double v[MXRI+1][MXRO+1], double p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double Dvv[MXRO]; /* Derivative wrt p of vv */
+ double dl, Ddlsq; /* Delta L squared */
+ double da, Ddasq, db, Ddbsq, Ddchsq; /* Delta CH squared */
+ double ct, cv, Dcv, dc, Ddc, Dvv1sq, Dvv2sq, Ddcsq; /* Delta C squared */
+ double Dlcomp, Dchcomp, Dccomp;
+ double Dde;
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (v[0][f] - v[1][f]) * p + v[1][f];
+ Dvv[f] = v[0][f] - v[1][f];
+ }
+
+ /* Delta L component */
+ dl = vv[0] - vt[0];
+ Ddlsq = 2.0 * dl * Dvv[0];
+ Dlcomp = s->rev.lchw_sq[0] * Ddlsq;
+
+ /* Delta CH component */
+ da = vv[1] - vt[1];
+ db = vv[2] - vt[2];
+ Ddasq = 2.0 * da * Dvv[1];
+ Ddbsq = 2.0 * db * Dvv[2];
+ Ddchsq = Ddasq + Ddbsq;
+ Dchcomp = s->rev.lchw_sq[2] * Ddchsq;
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = cv - ct;
+ Dvv1sq = 2.0 * vv[1] * Dvv[1];
+ Dvv2sq = 2.0 * vv[2] * Dvv[2];
+ Dcv = 0.5/cv * (Dvv1sq + Dvv2sq);
+ Ddcsq = 2.0 * dc * Dcv;
+ Dccomp = s->rev.lchw_chsq * Ddcsq;
+
+ Dde = Dlcomp + Dchcomp + Dccomp;
+
+ return Dde;
+}
+
+/* return weighted 2nd derivative of delta squared for target to edge at param value p */
+static double lchw_edge_DDp_sq(rspl *s, double *vt, double v[MXRI+1][MXRO+1], double p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double Dvv[MXRO]; /* Derivative wrt p of vv */
+ double DDvvsq[MXRO]; /* 2nd Derivative wrt p of vv */
+ double DDdchsq;
+ double ct, cv, Dcv, DDcv, dc, Dvv1sq, Dvv2sq, DDdcsq;
+ double DDlcomp, DDchcomp, DDccomp;
+ double DDde;
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (v[0][f] - v[1][f]) * p + v[1][f];
+ Dvv[f] = v[0][f] - v[1][f];
+ DDvvsq[f] = 2.0 * Dvv[f] * Dvv[f];
+ }
+
+ /* Delta L component */
+ DDlcomp = s->rev.lchw_sq[0] * DDvvsq[0];
+
+ /* Delta CH component */
+ DDdchsq = DDvvsq[1] + DDvvsq[2];
+ DDchcomp = s->rev.lchw_sq[2] * DDdchsq;
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = cv - ct;
+ Dvv1sq = 2.0 * vv[1] * Dvv[1];
+ Dvv2sq = 2.0 * vv[2] * Dvv[2];
+
+ Dcv = 0.5/cv * (Dvv1sq + Dvv2sq);
+ DDcv = -0.5/(cv * cv) * Dcv * (Dvv1sq + Dvv2sq) + 0.5/cv * (DDvvsq[1] + DDvvsq[2]);
+
+ DDdcsq = 2.0 * (Dcv * Dcv + dc * DDcv);
+ DDccomp = s->rev.lchw_chsq * DDdcsq;
+
+ DDde = DDlcomp + DDchcomp + DDccomp;
+
+ return DDde;
+}
+
+/* Solve for an edge. Return nz of solution. */
+static int lchw_edge_solve(rspl *s, double *vv, double *p, double *vt, double v[MXRI+1][MXRO+1]) {
+ int i, f, fdi = s->fdi;
+ double pp, ee, dedp;
+ double e0, e1;
+
+ /* Decide whether there is a solution on this edge. */
+ /* This is reliable, and saves any itters in the loop. */
+ e0 = lchw_edge_Dp_sq(s, vt, v, 0.0);
+ e1 = lchw_edge_Dp_sq(s, vt, v, 1.0);
+
+ if ((e0 < 0.0 && e1 < 0.0)
+ || (e0 > 0.0 && e1 > 0.0)) {
+ return 0;
+ }
+
+ pp = 0.5;
+ for (i = 0; i < 30; i++) {
+ ee = lchw_edge_Dp_sq(s, vt, v, pp);
+ dedp = lchw_edge_DDp_sq(s, vt, v, pp);
+ pp -= ee/dedp;
+
+ if (fabs(ee) < 1e-6)
+ break;
+ }
+ ee = lchw_edge_Dp_sq(s, vt, v, pp);
+
+ if (fabs(ee) > 1e-6 || pp < -EPS || pp > (1.0 + EPS)) {
+ return 0;
+ }
+
+ /* Return solution (output space) */
+ for (f = 0; f < fdi; f++)
+ vv[f] = (v[0][f] - v[1][f]) * pp + v[1][f];
+
+ /* Return solution (simplex parameter space) */
+ *p = pp;
+
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Triangle lchw Newton itteration code */
+
+/* return weighted delta squared for target to triangle at param values p */
+/* [ 0 <= p0 <= p1 <= 1 ] */
+static double lchw_tri_sq(rspl *s, double *vt, double v[MXRI+1][MXRO+1], double *p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double dlsq; /* Delta L squared */
+ double da, db, dchsq; /* Delta CH squared */
+ double ct, cv, dc, dcsq; /* Delta C squared */
+ double lcomp, chcomp, ccomp;
+ double de;
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++)
+ vv[f] = (v[0][f] - v[1][f]) * p[0]
+ + (v[1][f] - v[2][f]) * p[1]
+ + v[2][f];
+
+ /* Delta L component */
+ dlsq = vv[0] - vt[0];
+ dlsq = dlsq * dlsq;
+ lcomp = s->rev.lchw_sq[0] * dlsq;
+
+ /* Delta CH component */
+ da = vv[1] - vt[1];
+ db = vv[2] - vt[2];
+ dchsq = da * da + db * db;
+ chcomp = s->rev.lchw_sq[2] * dchsq;
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = ct - cv;
+ dcsq = dc * dc;
+
+ ccomp = s->rev.lchw_chsq * dcsq; /* w = cw - hw because dh = dch - dc */
+
+ de = lcomp + chcomp + ccomp;
+
+ return de;
+}
+
+/* return weighted two 1st derivativ of delta squared for target to edge at param value p */
+static void lchw_tri_Dp_sq(rspl *s, double Dde[2], double *vt, double v[MXRI+1][MXRO+1], double *p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double Dvv[2][MXRO]; /* Derivative wrt p of vv */
+ double dl, Ddl[2], Ddlsq[2]; /* Delta L squared */
+ double da, Ddasq[2], db, Ddbsq[2], Ddchsq[2]; /* Delta CH squared */
+ double ct, cv, Dcv[2], dc, Dvv1sq[2], Dvv2sq[2], Ddcsq[2]; /* Delta C squared */
+ double Dlcomp[2], Dchcomp[2], Dccomp[2];
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (v[0][f] - v[1][f]) * p[0]
+ + (v[1][f] - v[2][f]) * p[1]
+ + v[2][f];
+ Dvv[0][f] = v[0][f] - v[1][f];
+ Dvv[1][f] = v[1][f] - v[2][f];
+ }
+
+ /* Delta L component */
+ dl = vv[0] - vt[0];
+ Ddlsq[0] = 2.0 * dl * Dvv[0][0];
+ Ddlsq[1] = 2.0 * dl * Dvv[1][0];
+ Dlcomp[0] = s->rev.lchw_sq[0] * Ddlsq[0];
+ Dlcomp[1] = s->rev.lchw_sq[0] * Ddlsq[1];
+
+ /* Delta CH component */
+ da = vv[1] - vt[1];
+ db = vv[2] - vt[2];
+ Ddasq[0] = 2.0 * da * Dvv[0][1];
+ Ddasq[1] = 2.0 * da * Dvv[1][1];
+ Ddbsq[0] = 2.0 * db * Dvv[0][2];
+ Ddbsq[1] = 2.0 * db * Dvv[1][2];
+ Ddchsq[0] = Ddasq[0] + Ddbsq[0];
+ Ddchsq[1] = Ddasq[1] + Ddbsq[1];
+ Dchcomp[0] = s->rev.lchw_sq[2] * Ddchsq[0];
+ Dchcomp[1] = s->rev.lchw_sq[2] * Ddchsq[1];
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = cv - ct;
+ Dvv1sq[0] = 2.0 * vv[1] * Dvv[0][1];
+ Dvv1sq[1] = 2.0 * vv[1] * Dvv[1][1];
+ Dvv2sq[0] = 2.0 * vv[2] * Dvv[0][2];
+ Dvv2sq[1] = 2.0 * vv[2] * Dvv[1][2];
+ Dcv[0] = 0.5/cv * (Dvv1sq[0] + Dvv2sq[0]);
+ Dcv[1] = 0.5/cv * (Dvv1sq[1] + Dvv2sq[1]);
+ Ddcsq[0] = 2.0 * dc * Dcv[0];
+ Ddcsq[1] = 2.0 * dc * Dcv[1];
+ Dccomp[0] = s->rev.lchw_chsq * Ddcsq[0];
+ Dccomp[1] = s->rev.lchw_chsq * Ddcsq[1];
+
+ Dde[0] = Dlcomp[0] + Dchcomp[0] + Dccomp[0];
+ Dde[1] = Dlcomp[1] + Dchcomp[1] + Dccomp[1];
+}
+
+/* return weighted four 2nd derivatives of delta squared for target to edge at param value p */
+/* ([first][second]) */
+static void lchw_tri_DDp_sq(rspl *s, double DDde[2][2], double *vt, double v[MXRI+1][MXRO+1], double *p) {
+ int f, fdi = s->fdi;
+ double vv[MXRO]; /* Point at parameter location */
+ double Dvv[2][MXRO]; /* Derivative wrt p of vv */
+ double DDvvsq[2][2][MXRO]; /* 2nd Derivative wrt p of vv */
+ double DDdchsq[2][2]; /* Delta CH squared */
+ double ct, cv, Dcv[2], DDcv[2][2], dc, Dvv1sq[2], Dvv2sq[2], DDdcsq[2][2];
+ double DDlcomp[2][2], DDchcomp[2][2], DDccomp[2][2];
+
+ /* Due to comutivity, [0][1] == [1][0], so we omit */
+ /* those redundant calculations. */
+
+ /* Compute point at parameter location */
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (v[0][f] - v[1][f]) * p[0]
+ + (v[1][f] - v[2][f]) * p[1]
+ + v[2][f];
+ Dvv[0][f] = v[0][f] - v[1][f];
+ Dvv[1][f] = v[1][f] - v[2][f];
+
+ DDvvsq[0][0][f] = 2.0 * Dvv[0][f] * Dvv[0][f];
+ DDvvsq[1][0][f] = 2.0 * Dvv[1][f] * Dvv[0][f];
+// DDvvsq[0][1][f] = 2.0 * Dvv[0][f] * Dvv[1][f];
+ DDvvsq[1][1][f] = 2.0 * Dvv[1][f] * Dvv[1][f];
+ }
+
+ /* Delta L component */
+ DDlcomp[0][0] = s->rev.lchw_sq[0] * DDvvsq[0][0][0];
+ DDlcomp[1][0] = s->rev.lchw_sq[0] * DDvvsq[1][0][0];
+// DDlcomp[0][1] = s->rev.lchw_sq[0] * DDvvsq[0][1][0];
+ DDlcomp[1][1] = s->rev.lchw_sq[0] * DDvvsq[1][1][0];
+
+ /* Delta CH component */
+ DDdchsq[0][0] = DDvvsq[0][0][1] + DDvvsq[0][0][2];
+ DDdchsq[1][0] = DDvvsq[1][0][1] + DDvvsq[1][0][2];
+// DDdchsq[0][1] = DDvvsq[0][1][1] + DDvvsq[0][1][2];
+ DDdchsq[1][1] = DDvvsq[1][1][1] + DDvvsq[1][1][2];
+
+ DDchcomp[0][0] = s->rev.lchw_sq[2] * DDdchsq[0][0];
+ DDchcomp[1][0] = s->rev.lchw_sq[2] * DDdchsq[1][0];
+// DDchcomp[0][1] = s->rev.lchw_sq[2] * DDdchsq[0][1];
+ DDchcomp[1][1] = s->rev.lchw_sq[2] * DDdchsq[1][1];
+
+ /* Compute chromanance for the two colors */
+ ct = sqrt(vt[1] * vt[1] + vt[2] * vt[2]);
+ cv = sqrt(vv[1] * vv[1] + vv[2] * vv[2]);
+ dc = cv - ct;
+
+ Dvv1sq[0] = 2.0 * vv[1] * Dvv[0][1];
+ Dvv1sq[1] = 2.0 * vv[1] * Dvv[1][1];
+
+ Dvv2sq[0] = 2.0 * vv[2] * Dvv[0][2];
+ Dvv2sq[1] = 2.0 * vv[2] * Dvv[1][2];
+
+ Dcv[0] = 0.5/cv * (Dvv1sq[0] + Dvv2sq[0]);
+ Dcv[1] = 0.5/cv * (Dvv1sq[1] + Dvv2sq[1]);
+
+
+ DDcv[0][0] = -0.5/(cv * cv) * Dcv[0] * (Dvv1sq[0] + Dvv2sq[0])
+ + 0.5/cv * (DDvvsq[0][0][1] + DDvvsq[0][0][2]);
+
+ DDcv[1][0] = -0.5/(cv * cv) * Dcv[0] * (Dvv1sq[1] + Dvv2sq[1])
+ + 0.5/cv * (DDvvsq[1][0][1] + DDvvsq[1][0][2]);
+
+// DDcv[0][1] = -0.5/(cv * cv) * Dcv[1] * (Dvv1sq[0] + Dvv2sq[0])
+// + 0.5/cv * (DDvvsq[0][1][1] + DDvvsq[0][1][2]);
+
+ DDcv[1][1] = -0.5/(cv * cv) * Dcv[1] * (Dvv1sq[1] + Dvv2sq[1])
+ + 0.5/cv * (DDvvsq[1][1][1] + DDvvsq[1][1][2]);
+
+ DDdcsq[0][0] = 2.0 * (Dcv[0] * Dcv[0] + dc * DDcv[0][0]);
+ DDdcsq[1][0] = 2.0 * (Dcv[1] * Dcv[0] + dc * DDcv[1][0]);
+// DDdcsq[0][1] = 2.0 * (Dcv[0] * Dcv[1] + dc * DDcv[0][1]);
+ DDdcsq[1][1] = 2.0 * (Dcv[1] * Dcv[1] + dc * DDcv[1][1]);
+
+ DDccomp[0][0] = s->rev.lchw_chsq * DDdcsq[0][0];
+ DDccomp[1][0] = s->rev.lchw_chsq * DDdcsq[1][0];
+// DDccomp[0][1] = s->rev.lchw_chsq * DDdcsq[0][1];
+ DDccomp[1][1] = s->rev.lchw_chsq * DDdcsq[1][1];
+
+ DDde[0][0] = DDlcomp[0][0] + DDchcomp[0][0] + DDccomp[0][0];
+ DDde[1][0] = DDlcomp[1][0] + DDchcomp[1][0] + DDccomp[1][0];
+// DDde[0][1] = DDlcomp[0][1] + DDchcomp[0][1] + DDccomp[0][1];
+ DDde[0][1] = DDde[1][0];
+ DDde[1][1] = DDlcomp[1][1] + DDchcomp[1][1] + DDccomp[1][1];
+}
+
+
+/* Solve for a triangle face. Return nz of solution. */
+static int lchw_tri_solve(rspl *s, double *vv, double *p, double *vt, double v[MXRI+1][MXRO+1]) {
+ int f, fdi = s->fdi;
+ int i, j, k;
+ double pp[2], ee[2], dedp[2][2];
+ int ff1 = 0, ff2 = 0, fit = -1;
+
+ /* Decide whether there is a solution in this triangle */
+ j = k = 0;
+ pp[0] = 0.0; pp[1] = 0.0;
+ lchw_tri_Dp_sq(s, ee, vt, v, pp);
+ if (ee[0] < 0.0) j++;
+ if (ee[1] < 0.0) k++;
+
+ pp[0] = 0.0; pp[1] = 1.0;
+ lchw_tri_Dp_sq(s, ee, vt, v, pp);
+ if (ee[0] < 0.0) j++;
+ if (ee[1] < 0.0) k++;
+
+ if (j != 1 || k != 1) {
+ pp[0] = 1.0; pp[1] = 1.0;
+ lchw_tri_Dp_sq(s, ee, vt, v, pp);
+ if (ee[0] < 0.0) j++;
+ if (ee[1] < 0.0) k++;
+
+ /* Making this || filters out lots more for an avg itter of 0.74, */
+ /* but has a failure rate of 1 in 50000. */
+ /* This less stringent filter has an avg itter of 2.0 and 0 failure rate. */
+ if ((j == 0 || j == 3) && (k == 0 || k == 3)) {
+ return 0;
+ }
+ }
+
+ pp[0] = 0.3333; pp[1] = 0.6667;
+
+ for (i = 0; i < 30; i++) {
+ double det;
+
+ lchw_tri_Dp_sq(s, ee, vt, v, pp);
+ lchw_tri_DDp_sq(s, dedp, vt, v, pp);
+
+ /* Correct the point using inverse of dedp */
+ det = (dedp[0][0] * dedp[1][1] - dedp[0][1] * dedp[1][0]);
+ if (fabs(det) < 1e-20)
+ break; /* Hmm. */
+
+ det = 1.0/det;
+ pp[0] -= det * ( dedp[1][1] * ee[0] - dedp[0][1] * ee[1]);
+ pp[1] -= det * (-dedp[1][0] * ee[0] + dedp[0][0] * ee[1]);
+
+ /* If we're sufficiently close to zero point */
+ if (fabs(ee[0]) < 1e-6 && fabs(ee[1]) < 1e-6)
+ break;
+
+#ifdef NEVER
+#define THR 0.25
+ /* If we're too far out of bounds, give up */
+ /* (Speeds things up by about 40% at the cost of failing */
+ /* some that would suceed.) */
+ if (i >= 2 && (pp[0] < -THR || pp[0] > (1.0 + THR) || pp[1] < -THR || pp[1] > (1.0 + THR)
+ || pp[1] < (pp[0]-THR))) {
+ return 0;
+ }
+#undef THR
+#endif
+ }
+
+ lchw_tri_Dp_sq(s, ee, vt, v, pp);
+
+ if (fabs(ee[0]) > 1e-6 || fabs(ee[1]) > 1e-6
+ || pp[0] < -EPS || pp[1] < (pp[0]-EPS) || pp[1] > (1.0 + EPS)) {
+ return 0;
+ }
+
+ /* Return solution (output space) */
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (v[0][f] - v[1][f]) * pp[0]
+ + (v[1][f] - v[2][f]) * pp[1]
+ + v[2][f];
+ }
+ /* Return solution (simplex parameter space) */
+ p[0] = pp[0];
+ p[1] = pp[1];
+
+ return 1;
+}
/* -------------------------------------------------------- */
/* Cell/simplex object lower level code */
@@ -3209,17 +4620,16 @@ rspl *s
/* Cell code */
-static void free_cell_contents(cell *c);
-static cell *cache_rcell(revcache *r, int ix, int force);
-static void uncache_rcell(revcache *r, cell *cp);
+static void free_cell_contents(fxcell *c);
+static fxcell *cache_fxcell(revcache *r, int ix, int force);
+static void uncache_fxcell(revcache *r, fxcell *cp);
-/* Return a pointer to an appropriate reverse cell */
-/* cache structure. None of the sub simplex lists will */
-/* be initialised. */
-/* NOTE: must unget_cell() (== uncache_rcell()) when cell */
+/* Return a pointer to an appropriate fxcell cache structure. */
+/* None of the sub simplex lists will be initialised. */
+/* NOTE: must unget_cell() (== uncache_fxcell()) when fxcell */
/* is no longer needed */
/* Return NULL if we ran out of room in the cache. */
-static cell *get_rcell(
+static fxcell *get_fxcell(
schbase *b, /* Base search information */
int ix, /* fwd index of cell */
int force /* if nz, force memory allocation, so that we have at least one cell */
@@ -3228,9 +4638,9 @@ int force /* if nz, force memory allocation, so that we have at least one cell
int ee, e, di = s->di;
int p2di = (1<<di);
int ff, f, fdi = s->fdi;
- cell *c;
+ fxcell *c;
- c = cache_rcell(s->rev.cache, ix, force); /* Fetch it from the cache and lock it */
+ c = cache_fxcell(s->rev.cache, ix, force); /* Fetch it from the cache and lock it */
if (c == NULL)
return NULL;
@@ -3282,76 +4692,15 @@ int force /* if nz, force memory allocation, so that we have at least one cell
}
}
- /* Compute the output bounding sphere for fast rejection testing */
+ /* Compute the output bounding group for fast rejection testing */
{
- double *min[MXRO], *max[MXRO]; /* Pointers to points with min/max values */
- double radsq = -1.0; /* Span/radius squared */
- double rad;
- int spf = 0;
-
- /* Find verticies of cell that have min and max values in output space */
- for (f = 0; f < fdi; f++)
- min[f] = max[f] = NULL;
+ double *vp[POW2MXRI];
- for (ee = 0; ee < p2di; ee++) {
- double *vp = c->v[ee];
- for (f = 0; f < fdi; f++) {
- if (min[f] == NULL || min[f][f] > vp[f])
- min[f] = vp;
- if (max[f] == NULL || max[f][f] < vp[f])
- max[f] = vp;
- }
- }
-
- /* Find the pair of points with the largest span (diameter) in output space */
- for (ff = 0; ff < fdi; ff++) {
- double ss;
- for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt;
- tt = max[ff][f] - min[ff][f];
- ss += tt * tt;
- }
- if (ss > radsq) {
- radsq = ss;
- spf = ff; /* Output dimension max was in */
- }
- }
+ /* Make array of pointers to double vectors */
+ for (ee = 0; ee < p2di; ee++)
+ vp[ee] = c->v[ee];
- /* Set initial bounding sphere */
- for (f = 0; f < fdi; f++) {
- c->bcent[f] = (max[spf][f] + min[spf][f])/2.0;
- }
- radsq /= 4.0; /* diam^2 -> rad^2 */
- c->bradsq = radsq;
- rad = c->brad = sqrt(radsq);
-
- /* Go though all the points again, expanding sphere if necessary */
- for (ee = 0; ee < p2di; ee++) {
- double ss;
- double *vp = c->v[ee];
-
- /* Compute distance squared of point to bounding shere */
- for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = vp[f] - c->bcent[f];
- ss += tt * tt;
- }
- if (ss > radsq) {
- double tt;
- /* DBG(("Expanding bounding sphere by %f\n",sqrt(ss) - rad)); */
-
- ss = sqrt(ss) + EPS; /* Radius to point */
- rad = (rad + ss)/2.0;
- c->bradsq = radsq = rad * rad;
- tt = ss - rad;
- for (f = 0; f < fdi; f++) {
- c->bcent[f] = (rad * c->bcent[f] + tt * vp[f])/ss;
- }
-
- } else {
- /* DBG(("Bounding sphere encloses by %f\n",rad - sqrt(ss))); */
- }
- }
- c->bradsq += EPS;
+ nn_grpinit(s, &c->g, vp, p2di, NULL);
}
c->flags = CELL_FLAG_1;
}
@@ -3359,7 +4708,7 @@ int force /* if nz, force memory allocation, so that we have at least one cell
return c;
}
-void free_simplex_info(cell *c, int dof);
+void free_simplex_info(fxcell *c, int dof);
/* Free up any allocated simplexes in a cell, */
/* and set the pointers to NULL. */
@@ -3367,7 +4716,7 @@ void free_simplex_info(cell *c, int dof);
/* the cache index or unthrheaded from the mru list). */
static void
free_cell_contents(
-cell *c
+fxcell *c
) {
int nsdi;
@@ -3392,7 +4741,6 @@ int primes[] = {
853,
1489,
3373,
- 3373,
6863,
12919,
23333,
@@ -3400,6 +4748,9 @@ int primes[] = {
97849,
146221,
254941,
+ 407843,
+ 756869,
+ 999983,
-1
};
@@ -3419,7 +4770,7 @@ unsigned int simplex_hash(revcache *rc, int sdi, int efdi, int *vix) {
/* Allocate and do the basic initialisation for a DOF list of simplexes */
void alloc_simplexes(
-cell *c,
+fxcell *c,
int nsdi /* Non limited sub simplex dimensionality */
) {
rspl *s = c->s;
@@ -3497,7 +4848,7 @@ int nsdi /* Non limited sub simplex dimensionality */
//if ((max - min) > EPS) printf("~1 Found simplex sdi %d, efdi %d, min = %f, max = %f, limitv = %f\n", sdi, efdi, min,max,s->limitv);
if (isclip) { /* Limit clipped simplex */
/* (Make sure it straddles the limit boundary) */
- if (max < s->limitv || min > s->limitv)
+ if (max <= s->limitv || min > s->limitv)
continue; /* Discard this simplex - it can't straddle the ink limit */
//printf("~1 using sub simplex sdi %d, efdi %d, min = %f, max = %f, limitv = %f\n", sdi, efdi, min,max,s->limitv);
} else {
@@ -3514,7 +4865,7 @@ int nsdi /* Non limited sub simplex dimensionality */
/* Allocate space for all the DOF simplexes that will be used */
if (so > 0) {
if ((c->sx[nsdi] = (simplex **) rev_calloc(s, so, sizeof(simplex *))) == NULL)
- error("rspl malloc failed - reverse cell simplexes - list of pointers");
+ error("rspl malloc failed - fxcell simplexes - list of pointers");
INCSZ(s, so * sizeof(simplex *));
}
@@ -3552,7 +4903,7 @@ int nsdi /* Non limited sub simplex dimensionality */
x = c->sx[nsdi][so];
- /* If this is a shared simplex, see if we already have it in another cell */
+ /* If this is a shared face simplex, see if we already have it in another fxcell */
if (x == NULL && psxi->face) {
unsigned int hash;
//printf("~1 looking for existing simplex nsdi = %d\n",nsdi);
@@ -3576,7 +4927,7 @@ int nsdi /* Non limited sub simplex dimensionality */
/* Doesn't already exist */
if (x == NULL) {
if ((x = (simplex *) rev_calloc(s, 1, sizeof(simplex))) == NULL)
- error("rspl malloc failed - reverse cell simplexes - base simplex %d bytes",sizeof(simplex));
+ error("rspl malloc failed - fxcell simplexes - base simplex %d bytes",sizeof(simplex));
INCSZ(s, sizeof(simplex));
x->refcount = 1;
x->touch = s->rev.stouch-1;
@@ -3638,7 +4989,7 @@ int nsdi /* Non limited sub simplex dimensionality */
x->aloc2 = x->aloc5 = NULL; /* Matrix allocations not done yet */
- /* Add it to the face shared simplex hash index */
+ /* Add it to the shared face simplex hash index */
if (x->psxi->face) {
unsigned int hash;
int i;
@@ -3701,7 +5052,7 @@ int nsdi /* Non limited sub simplex dimensionality */
/* Free up any allocated for a list of sub-simplexes */
void
free_simplex_info(
-cell *c,
+fxcell *c,
int nsdi /* non limit sub simplex dimensionaity */
) {
int si, sxno = c->sxno[nsdi]; /* Number of simplexes */
@@ -3792,7 +5143,6 @@ simplex *x, /* Simplex */
double *p /* Input coords in simplex space */
) {
rspl *s = x->s;
- schbase *b = s->rev.sb;
int fdi = s->fdi;
int e, sdi = x->sdi; /* simplex dimensionality */
double cp, lp;
@@ -3839,6 +5189,61 @@ double *p /* Input coords in simplex space */
return rv;
}
+/* Check that an input space vector of a simplex meets the ink limit. */
+/* Return zero if outside the simplex, */
+/* 1 normally if within the simplex, */
+/* and 2 if it would be over the ink limit if limit was enabled. */
+/* This is the same as within_simplex() but only checks the ink limit. */
+static int
+within_simplex_limit(
+simplex *x, /* Simplex */
+double *p /* Input coords in simplex space */
+) {
+ rspl *s = x->s;
+ int fdi = s->fdi;
+ int e, sdi = x->sdi; /* simplex dimensionality */
+ int rv = 1;
+
+ /* Compute limit using interp. - assume simplex would have been trivially rejected */
+ if (s->limitf != NULL) {
+ double sum = 0.0; /* Might be over the limit */
+ for (e = 0; e < sdi; e++)
+ sum += p[e] * (x->v[e][fdi] - x->v[e+1][fdi]);
+ sum += x->v[sdi][fdi];
+ if (sum > s->limitv) {
+ if (s->limiten != 0)
+ return 0; /* Exceeds ink limit */
+ else
+ rv = 2; /* would have exceeded limit */
+ }
+ }
+ return rv;
+}
+
+/* Similar check to within_simplex(), but with explicit simplex definition */
+/* and no ink limit check. Returns 0 if outside, 1 if within */
+static int
+simple_within_simplex(
+double v[MXRI+1][MXRO], /* Vertex values */
+double *p, /* Input coords in simplex space */
+int sdi /* input dimensionality of simplex */
+) {
+ int e;
+ double cp, lp;
+
+ /* Check we are within baricentric limits */
+ for (lp = 0.0, e = 0; e < sdi; e++) {
+ cp = p[e];
+ if ((cp+EPS) < lp) /* Outside baricentric or not in correct */
+ return 0; /* order for this simplex */
+ lp = cp;
+ }
+ if ((1.0+EPS) < lp) /* outside baricentric range */
+ return 0;
+
+ return 1;
+}
+
/* Convert vector from simplex space to absolute cartesian space */
static void simplex_to_abs(
simplex *x,
@@ -3868,23 +5273,29 @@ double *in /* Input in simplex space */
/* with CLIPSX sub-simplexes. */
/* Note that no line equation values are returned if fdi = 1, */
/* since there is no such thing as an implicit line equation. */
+/* (Re-usable version for lines in general) */
static void
-init_line_eq(
-schbase *b,
+init_line_eq_imp(
+rspl *s,
+schbase *b, /* to set cdir, may be NULL if not needed. */
+double ***pcla, /* pointer to clip vector LHS implicit equation matrix */
+double clb[MXRO+1], /* Clip vector RHS implicit equation vector */
double st[MXRO], /* Start point */
-double de[MXRO] /* Delta */
+double de[MXRO], /* Delta */
+int inkeq /* nz to add ink limit target equation if s->limitf != NULL */
) {
- rspl *s = b->s;
int ff, f, fdi = s->fdi;
int i, p;
double lgst;
+ double **cla = *pcla;
DBG(("Computing clipping line implicit equation, dim = %d\n", fdi));
/* Pick a pivot element */
for (lgst = -1.0, p = -1, f = 0; f < fdi; f++) {
double tt = de[f];
- b->cdir[f] = tt; /* Stash this away */
+ if (b != NULL)
+ b->cdir[f] = tt; /* Stash this away */
tt = fabs(tt);
if (tt > lgst) {
lgst = tt;
@@ -3894,8 +5305,10 @@ double de[MXRO] /* Delta */
if (p < 0) /* Shouldn't happen */
error("rspl rev, internal, trying to cope with zero length clip line\n");
- if (b->cla == NULL)
- b->cla = dmatrix(0, fdi-1, 0, fdi); /* Allow for ink limit supliment */
+ if (cla == NULL) {
+ cla = dmatrix(0, fdi-1, 0, fdi); /* Allow for ink limit supliment */
+ *pcla = cla;
+ }
for (i = ff = 0; ff < fdi; ff++) { /* For the input rows */
if (ff == p) {
@@ -3903,28 +5316,28 @@ double de[MXRO] /* Delta */
}
for (f = 0; f < fdi; f++) { /* For input & output columns */
if (f == p) {
- b->cla[i][f] = -de[ff]; /* Last column is -ve delta value */
+ cla[i][f] = -de[ff]; /* Last column is -ve delta value */
} else if (f == ff) {
- b->cla[i][f] = de[p]; /* Diagonal is pivot value */
+ cla[i][f] = de[p]; /* Diagonal is pivot value */
} else {
- b->cla[i][f] = 0.0; /* Else zero */
+ cla[i][f] = 0.0; /* Else zero */
}
}
- b->clb[i] = de[p] * st[ff] - de[ff] * st[p];
+ clb[i] = de[p] * st[ff] - de[ff] * st[p];
i++;
}
/* Add ink limit target equation - */
/* interpolated ink value == target */
- if (s->limitf != NULL) {
+ if (inkeq && s->limitf != NULL) {
for (i = 0; i < (fdi-1); i++)
- b->cla[i][fdi] = 0.0;
+ cla[i][fdi] = 0.0;
for (f = 0; f < fdi; f++)
- b->cla[fdi-1][f] = 0.0;
+ cla[fdi-1][f] = 0.0;
- b->cla[fdi-1][fdi] = 1.0;
- b->clb[fdi-1] = s->limitv;
+ cla[fdi-1][fdi] = 1.0;
+ clb[fdi-1] = s->limitv;
}
#ifdef NEVER
@@ -3941,9 +5354,9 @@ double de[MXRO] /* Delta */
for (ff = 0; ff < (fdi-1); ff++) {
v[ff] = 0.0;
for (f = 0; f < fdi; f++) {
- v[ff] += b->cla[ff][f] * pnt[f];
+ v[ff] += cla[ff][f] * pnt[f];
}
- v[ff] -= b->clb[ff];
+ v[ff] -= clb[ff];
if (v[ff] < 0.0)
v[ff] = -v[ff];
if (v[ff] > 0.000001) {
@@ -3957,6 +5370,18 @@ double de[MXRO] /* Delta */
}
+/* Version of above used to set vector clipping line up */
+static void
+init_line_eq(
+schbase *b,
+double st[MXRO], /* Start point */
+double de[MXRO] /* Delta */
+) {
+ DBG(("Computing clipping line implicit equation, dim = %d\n", b->s->fdi));
+
+ init_line_eq_imp(b->s, b, &b->cla, b->clb, st, de, 1);
+}
+
/* - - - - - - */
/* Simpex solution info #2 */
@@ -3985,7 +5410,7 @@ add_lu_svd(simplex *x) {
+ sizeof(int) * sdi;
if ((x->aloc2 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
- error("rspl malloc failed - reverse cell sub-simplex matricies");
+ error("rspl malloc failed - fxcell sub-simplex matricies");
INCSZ(x->s, asize);
/* Allocate biggest to smallest (double, pointers, ints) */
@@ -4017,7 +5442,7 @@ add_lu_svd(simplex *x) {
+ sizeof(double *) * (efdi + 2 * sdi);
if ((x->aloc2 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
- error("rspl malloc failed - reverse cell sub-simplex matricies");
+ error("rspl malloc failed - fxcell sub-simplex matricies");
INCSZ(x->s, asize);
/* Allocate biggest to smallest (double, pointers, ints) */
@@ -4232,7 +5657,7 @@ simplex *x
+ sizeof(int) * dof;
if ((x->aloc5 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
- error("rspl malloc failed - reverse cell sub-simplex matricies");
+ error("rspl malloc failed - fxcell sub-simplex matricies");
INCSZ(x->s, asize);
/* Allocate biggest to smallest (double, pointers, ints) */
@@ -4261,7 +5686,7 @@ simplex *x
+ sizeof(double) * (dof * (naux + dof + 1));
if ((x->aloc5 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
- error("rspl malloc failed - reverse cell sub-simplex matricies");
+ error("rspl malloc failed - fxcell sub-simplex matricies");
INCSZ(x->s, asize);
/* Allocate biggest to smallest (double, pointers, ints) */
@@ -4368,7 +5793,7 @@ int sdi /* Sub-simplex dimensionality (range 0 - di) */
xip->sdi = sdi;
xip->nospx = nospx;
if ((xip->spxi = (psxinfo *) rev_calloc(s, nospx, sizeof(psxinfo))) == NULL)
- error("rspl malloc failed - reverse cell sub-simplex info array");
+ error("rspl malloc failed - fxcell sub-simplex info array");
INCSZ(s, nospx * sizeof(psxinfo));
DBG(("Number of subsimplex = %d\n",nospx));
@@ -4487,7 +5912,7 @@ ssxinfo *xip /* Pointer to sub-simplex info structure */
/* ====================================================== */
/* Reverse cell cache code */
-/* Allocate and initialise the reverse cell cache */
+/* Allocate and initialise the fxcell cache */
static revcache *
alloc_revcache(
rspl *s
@@ -4496,7 +5921,7 @@ rspl *s
DBG(("alloc_revcache called\n"));
if ((rc = (revcache *) rev_calloc(s, 1, sizeof(revcache))) == NULL)
- error("rspl malloc failed - reverse cell cache");
+ error("rspl malloc failed - fxcell cache");
INCSZ(s, sizeof(revcache));
rc->s = s; /* For stats */
@@ -4504,9 +5929,9 @@ rspl *s
/* Allocate an initial cell hash index */
rc->cell_hash_size = primes[0];
- if ((rc->hashtop = (cell **) rev_calloc(s, rc->cell_hash_size, sizeof(cell *))) == NULL)
- error("rspl malloc failed - reverse cell cache index");
- INCSZ(s, rc->cell_hash_size * sizeof(cell *));
+ if ((rc->hashtop = (fxcell **) rev_calloc(s, rc->cell_hash_size, sizeof(fxcell *))) == NULL)
+ error("rspl malloc failed - fxcell cache index");
+ INCSZ(s, rc->cell_hash_size * sizeof(fxcell *));
/* Allocate an initial simplex face match hash index */
rc->spx_hash_size = primes[0];
@@ -4518,23 +5943,23 @@ rspl *s
return rc;
}
-/* Free the reverse cell cache */
+/* Free the fxcell cache */
static void
free_revcache(revcache *rc) {
int i;
- cell *cp, *ncp;
+ fxcell *cp, *ncp;
/* Free any stuff allocated in the cell contents, and the cell itself. */
for (cp = rc->mrubot; cp != NULL; cp = ncp) {
ncp = cp->mruup;
free_cell_contents(cp);
free(cp);
- DECSZ(rc->s, sizeof(cell));
+ DECSZ(rc->s, sizeof(fxcell));
}
/* Free the hash indexes */
free(rc->hashtop);
- DECSZ(rc->s, rc->cell_hash_size * sizeof(cell *));
+ DECSZ(rc->s, rc->cell_hash_size * sizeof(fxcell *));
free(rc->spxhashtop);
DECSZ(rc->s, rc->spx_hash_size * sizeof(simplex *));
@@ -4548,7 +5973,7 @@ invalidate_revcache(
revcache *rc)
{
int i;
- cell *cp;
+ fxcell *cp;
rc->nunlocked = 0;
@@ -4574,23 +5999,23 @@ revcache *rc)
/* This may re-size the hash index too. */
/* Return the pointer to the new cell. */
/* (Note it's not our job here to honour the memory limit) */
-static cell *
+static fxcell *
increase_revcache(
revcache *rc
) {
- cell *nxcell; /* Newly allocated cell */
+ fxcell *nxcell; /* Newly allocated fxcell */
int i;
- DBG(("Adding another chunk of cells to cache\n"));
+// DBG(("Adding another cell to cache\n"));
#ifdef NEVER /* We may be called with force != 0 */
if (rc->s->rev.sz >= rc->s->rev.max_sz)
return NULL;
#endif
- if ((nxcell = (cell *) rev_calloc(rc->s, 1, sizeof(cell))) == NULL)
- error("rspl malloc failed - reverse cache cells");
- INCSZ(rc->s, sizeof(cell));
+ if ((nxcell = (fxcell *) rev_calloc(rc->s, 1, sizeof(fxcell))) == NULL)
+ error("rspl malloc failed - reverse fxcells");
+ INCSZ(rc->s, sizeof(fxcell));
nxcell->s = rc->s;
@@ -4605,7 +6030,7 @@ revcache *rc
rc->nacells++;
rc->nunlocked++;
- DBG(("cache is now %d cells\n",rc->nacells));
+// DBG(("cache is now %d cells\n",rc->nacells));
/* See if the hash index should be re-sized */
if (rc->nacells > (HASH_FILL_RATIO * rc->cell_hash_size)) {
@@ -4613,19 +6038,19 @@ revcache *rc
;
if (primes[i] > 0) {
int cell_hash_size = rc->cell_hash_size; /* Old */
- cell **hashtop = rc->hashtop;
+ fxcell **hashtop = rc->hashtop;
rc->cell_hash_size = primes[i];
DBG(("Increasing cell cache hash index to %d\n",cell_hash_size));
/* Allocate a new index */
- if ((rc->hashtop = (cell **) rev_calloc(rc->s, rc->cell_hash_size, sizeof(cell *))) == NULL)
- error("rspl malloc failed - reverse cell cache index");
- INCSZ(rc->s, rc->cell_hash_size * sizeof(cell *));
+ if ((rc->hashtop = (fxcell **) rev_calloc(rc->s, rc->cell_hash_size, sizeof(fxcell *))) == NULL)
+ error("rspl malloc failed - fxcell cache index");
+ INCSZ(rc->s, rc->cell_hash_size * sizeof(fxcell *));
/* Transfer all the cells to the new index */
for (i = 0; i < cell_hash_size; i++) {
- cell *c, *nc;
+ fxcell *c, *nc;
for (c = hashtop[i]; c != NULL; c = nc) {
int hash;
nc = c->hlink;
@@ -4637,7 +6062,7 @@ revcache *rc
/* Done with old index */
free(hashtop);
- DECSZ(rc->s, cell_hash_size * sizeof(cell *));
+ DECSZ(rc->s, cell_hash_size * sizeof(fxcell *));
}
}
@@ -4651,11 +6076,11 @@ revcache *rc /* Reverse cache structure */
) {
int hit = 0;
int hash;
- cell *cp;
+ fxcell *cp;
DBG(("Decreasing cell cache memory allocation by freeing a cell\n"));
- /* Use the least recently used unlocked cell */
+ /* Use the least recently used unlocked fxcell */
for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup)
;
@@ -4674,7 +6099,7 @@ revcache *rc /* Reverse cache structure */
if (rc->hashtop[hash] == cp) {
rc->hashtop[hash] = cp->hlink;
} else {
- cell *c;
+ fxcell *c;
for (c = rc->hashtop[hash]; c != NULL && c->hlink != cp; c = c->hlink)
;
if (c != NULL)
@@ -4692,30 +6117,30 @@ revcache *rc /* Reverse cache structure */
cp->mrudown->mruup = cp->mruup;
cp->mruup = cp->mrudown = NULL;
free(cp);
- DECSZ(rc->s, sizeof(cell));
+ DECSZ(rc->s, sizeof(fxcell));
rc->nacells--;
rc->nunlocked--;
- DBG(("Freed a rev cache cell\n"));
+ DBG(("Freed a rev fxcell\n"));
return 1;
}
-/* Return a pointer to an appropriate reverse cell */
-/* cache structure. cell->flags will be 0 if the cell */
+/* Return a pointer to an appropriate fxcell */
+/* cache structure. cell->flags will be 0 if the fxcell */
/* has been reallocated. cell contents will be 0 if */
/* never used before. */
/* The cell reference count is incremented, so that it */
/* can't be thrown out of the cache. The cell must be */
-/* released with uncache_rcell() when it's no longer needed. */
+/* released with uncache_fxcell() when it's no longer needed. */
/* return NULL if we ran out of room in the cache */
-static cell *cache_rcell(
+static fxcell *cache_fxcell(
revcache *rc, /* Reverse cache structure */
int ix, /* fwd index of cell */
-int force /* if nz, force memory allocation, so that we have at least one cell */
+int force /* if nz, force memory allocation, so that we have at least one fxcell */
) {
int hit = 0;
int hash;
- cell *cp;
+ fxcell *cp;
/* keep memory in check - fail if we're out of memory and can't free any */
/* (Doesn't matter if it might be a hit, it will get picked up the next time) */
@@ -4744,12 +6169,13 @@ int force /* if nz, force memory allocation, so that we have at least one cell
break;
}
}
- if (!hit) { /* No hit, use new cell or the least recently used cell */
+ if (!hit) { /* No hit, use new cell or the least recently used fxcell */
int ohash;
/* If we haven't used all our memory, or if we are forced and have */
- /* no cell we can re-use, then noallocate another cell */
- if (rc->s->rev.sz < rc->s->rev.max_sz || (force && rc->nunlocked == 0)) {
+ /* no cell we can re-use, then allocate another fxcell */
+ if (rc->s->rev.sz < rc->s->rev.max_sz
+ || (force && rc->nunlocked == 0)) {
cp = increase_revcache(rc);
hash = HASH(rc,ix); /* Re-compute hash in case hash size changed */
//printf("~1 using new cell\n");
@@ -4757,7 +6183,7 @@ int force /* if nz, force memory allocation, so that we have at least one cell
//printf("~1 memory limit has been reached, using old cell\n");
for (;;) {
- /* Use the least recently used unlocked cell */
+ /* Use the least recently used unlocked fxcell */
for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup)
;
@@ -4775,14 +6201,14 @@ int force /* if nz, force memory allocation, so that we have at least one cell
if (rc->hashtop[ohash] == cp) {
rc->hashtop[ohash] = cp->hlink;
} else {
- cell *c;
+ fxcell *c;
for (c = rc->hashtop[ohash]; c != NULL && c->hlink != cp; c = c->hlink)
;
if (c != NULL)
c->hlink = cp->hlink;
}
- /* If we're now under the memory limit, use this cell */
+ /* If we're now under the memory limit, use this fxcell */
if (rc->s->rev.sz < rc->s->rev.max_sz) {
break;
}
@@ -4800,7 +6226,7 @@ int force /* if nz, force memory allocation, so that we have at least one cell
cp->mrudown->mruup = cp->mruup;
cp->mruup = cp->mrudown = NULL;
free(cp);
- DECSZ(rc->s, sizeof(cell));
+ DECSZ(rc->s, sizeof(fxcell));
rc->nacells--;
rc->nunlocked--;
}
@@ -4843,9 +6269,9 @@ int force /* if nz, force memory allocation, so that we have at least one cell
/* Tell the cache that we aren't using this cell anymore, */
/* but to keep it in case it is needed again. */
-static void uncache_rcell(
+static void uncache_fxcell(
revcache *rc, /* Reverse cache structure */
-cell *cp
+fxcell *cp
) {
if (cp->refcount > 0) {
cp->refcount--;
@@ -4859,8 +6285,14 @@ cell *cp
/* ====================================================== */
/* Reverse rspl setup functions */
+static void del_bxcell(rspl *s, bxcell *bx);
+static void free_sharelist(rspl *s);
+static void free_indexlist(rspl *s, int **rp);
+static void free_surfhash(rspl *s, int del);
+static void free_surflist(rspl *s);
+
/* Called by rspl initialisation */
-/* Note that reverse cell lookup tables are not */
+/* Note that fxcell lookup tables are not */
/* allocated & created until the first call */
/* to a reverse interpolation function. */
void
@@ -4885,6 +6317,7 @@ init_rev(rspl *s) {
/* Methods */
s->rev_set_limit = rev_set_limit_rspl;
s->rev_get_limit = rev_get_limit_rspl;
+ s->rev_set_lchw = rev_set_lchw;
s->rev_interp = rev_interp_rspl;
s->rev_locus = rev_locus_rspl;
s->rev_locus_segs = rev_locus_segs_rspl;
@@ -4947,13 +6380,15 @@ rspl *s /* Pointer to rspl grid */
/* Free up the Second section */
if (s->rev.nnrev != NULL) {
- /* Free arrays at grid points, taking care of reference count */
+
+ /* Free up nn list sharelist records - this will free and set */
+ /* any shared lists to NULL */
+ free_sharelist(s);
+
+ /* Free any remaining arrays at grid points */
for (rpp = s->rev.nnrev; rpp < (s->rev.nnrev + s->rev.no); rpp++) {
- if ((rp = *rpp) != NULL && --rp[2] <= 0) {
- DECSZ(s, rp[0] * sizeof(int));
- free(*rpp);
- *rpp = NULL;
- }
+ if (*rpp != NULL)
+ free_indexlist(s, rpp);
}
free(s->rev.nnrev);
DECSZ(s, s->rev.no * sizeof(int *));
@@ -4992,13 +6427,10 @@ rspl *s /* Pointer to rspl grid */
s->rev.rev_valid = 0;
if (s->rev.rev != NULL) {
- /* Free arrays at grid points, taking care of reference count */
+ /* Free arrays at grid points */
for (rpp = s->rev.rev; rpp < (s->rev.rev + s->rev.no); rpp++) {
- if ((rp = *rpp) != NULL && --rp[2] <= 0) {
- DECSZ(s, rp[0] * sizeof(int));
- free(*rpp);
- *rpp = NULL;
- }
+ if (*rpp != NULL)
+ free_indexlist(s, rpp);
}
free(s->rev.rev);
DECSZ(s, s->rev.no * sizeof(int *));
@@ -5016,111 +6448,2334 @@ rspl *s /* Pointer to rspl grid */
s->rev.no = 0;
s->rev.inited = 0;
}
+
+ /* Free up surface linked list and the bxcells in it. */
+ free_surflist(s);
+
+ /* Free up surface bxcell hash index */
+ free_surfhash(s, 0);
+
DBG(("rev allocation left after free = %d bytes\n",s->rev.sz));
+
+#ifdef CHECK_NNLU
+ print_nnck(s);
+#endif /* CHECK_NNLU */
}
+
+/* ========================================================== */
+/* reverse lookup acceleration structure initialisation code. */
+
+/* The reverse lookup relies on a search of the fwd interpolation tables.
+ To eliminate out of gamut points quickly, to provide a starting point for
+ the search, and to guarantee that all possible reverse solutions are discovered,
+ a spatial indexing structure is used to provide a list of starting candidate
+ forward cell indexes for a given output value. (rev.rev[])
+ The reverse structure contains two fdi dimensional bwd cell grids, each element of the
+ cell grid holding the indexes of the forward interpolation grid.
+ The rev[] grid holds fwd cell indexes which intersect that bwd cell's range of
+ output values. A rev[] cell will be empty if there is no potential exact solution.
+ The nnrev[] grid holds fwd cell indexes of those cells that may be the lch weighted
+ closest to that bwd cell.
+ The rev.nnrev[] array is almost a complement of the rev.rev[] array,
+ with the exception of any overlap near the gamut surface.
+ Since many of the nnrev[] bwd cells map to nearly the same surface region, many
+ of the fwd cell lists are shared.
+
+ When s->rev.fastsetup is set, then the rev.nnrev[] grid is left empty, and
+ any call for nn lookup is satisfied by filling the requisite rev.nnrev[] on-demand,
+ by an exaustive search of the surface bwd cells (rev.surflist)
+
+ Note that unlike the forward grid which is composed of verticies,
+ these rev lists are composed of fwd cells.
+
+ The nnrev[] setup code identifies possible surface bwd revp[] cells
+ by them being face neighbors of empty (out of gamut) bwd cells.
+ It then converts the vertexes of the fwd cell list into a vertex list,
+ and "thins" the list by deleting any vertex that is shaded by a triangle
+ that other vertexes are part of. This is done on a backward cell basis,
+ but includes vertexes of other possibly shadowed backward cells.
+
+ If ink limiting is being used, then over ink limit partners to
+ the vertexes are added in, and then the list of vertexes is
+ converted back into fwd cells in a way that ensures 2 dimensional
+ connectivity of the cells, while minimizing the number of
+ extra (non surface) vertexes implied by the fwd cells.
+
+ */
+
+/*
+ The gamut hull fwcell finding code is not robust - it assumes visiblity
+ of the surface from some center point(s).
+
+ Perfect gamut hull finding approach would be something like this:
+ (using vertex and triangle caching structures.)
+
+ Add all triangles on device gamut surface with at least
+ one vertex within ink limit.
+
+ Add all triangles that are part of a full di simplex
+ with at least one vertex within ink limit (and not on device gamut),
+ where all the other verticies of the simplex are on one
+ side of the triangle (non-monotonic surfaces).
+
+ Add all triangles on the ink limit plane.
+ (Will be 1 or more triangles per simplex that has
+ 1..di verticies that are over the ink limit.)
+
+ Check all triangles for instersection with each other.
+ Convert any such intersections into smaller, non-intersecting
+ triangles that share verticies along intersection line.
+
+ Delete triangles that have dangling edges (i.e. triangles that
+ have edges with odd number of associated triangles).
+ This is to eliminate "dangling" triangles. Should only be left
+ with "bubles" in surface after this ?
+
+ Bubbles join at edges where more than 2 triangles co-incide.
+ Can internal bubles be "un-stitched" if we can decide which
+ triangles are part of a bubble ????
+ i.e. use even/odd inside rule for points between
+ the triangles at the edge.
+
+ Delete all vertexes and associated triangles that are
+ inside the surface.
+ Will odd/even test work ? - i.e. from vertex of triangle,
+ is on surface if intersections in one direction are even, and
+ other direction are odd.
+
+ Or "point within odd number of tetrahedrons formed with point on surface" ?
+ - seems to be the same as the odd/even rule. Can't detect connectivity.
+
+ Or do this using a winding number algorithm
+ with signed crossings optimization ?
+ <Point in Polyhedron Testing Using Spherical Polygons, Graphics Gems V pp42>
+ But do we have to order triangles in a consistent direction ?
+ How to do this when more than 2 triangles meet at an adge ???
+ i.e. catch-22 - need to know which are inside triangles to
+ set edge direction, but need edge direction to detect inside-outside.
+
+*/
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#if defined(REVTABLESTATS) || defined(DEBUG)
+static int bxcount = 0;
+static int maxbxcount = 0;
+#endif
+
+static void add2indexlist(rspl *s, int **rpp, int ix, int shrec);
+static void comp_shadow_group(rspl *s, double *gcent, double *rgc, double *pcc,
+ double *pdw, double *gc, double (*v)[MXRO], int nverta);
+
+/* Allocate a new bx cell. */
+/* (Doesn't add to hash or list) */
+static bxcell *new_bxcell(
+ rspl *s,
+ int ix, /* rev[] index of cell being created */
+ int *gc, /* Coord of rev[] cell being created */
+ bxcell *ss, /* search starting bxcell to commence with, this cell if NULL */
+ double sdist, /* Est. distance from this cell to six */
+ char *vflag /* If non-NULL, create a super-cell if far from seed */
+) {
+ int f, fdi = s->fdi;
+ int i;
+ bxcell *bx = NULL;
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+
+//printf("~1 creating new bxcell with index %d\n",ix);
+ if ((bx = (bxcell *) rev_calloc(s, 1, sizeof(bxcell))) == NULL)
+ error("rspl malloc failed - rev bxcell structs");
+ INCSZ(s, sizeof(bxcell));
+
+ bx->ix = ix;
+ bx->tix = -1;
+ for (f = 0; f < fdi; f++)
+ bx->gc[f] = gc[f];
+ bx->ss = (ss == NULL) ? bx : ss;
+ bx->sdist = sdist;
+
+//printf("~1 new_bxcell ix %d, co %s, base %s\n",ix,debPiv(s->fdi, bx->gc),debPdv(s->fdi, vp[0]));
+
+ /* super-cell code (to speed filling) */
+ if (vflag != NULL && (vflag[ix] & 2) == 0 && ss != NULL) {
+ double codist = 0.0;
+
+ /* Compute distance of seed from this cell */
+ for (codist = 0.0, f = 0; f < fdi; f++) {
+ int tt = bx->gc[f] - ss->gc[f];
+ codist += tt * tt;
+ }
+ codist = sqrt(codist);
+
+//printf("~1 codist %f, codist/s->rev.res = %f\n",codist,codist/s->rev.res);
+ /* Create a super-cell if we are far enough from the seed. */
+ /* (this determines what portion of filling uses super-cells) */
+// if (codist >= 1.0 && (codist/s->rev.res) > 0.05)
+ if (codist >= 2.0)
+ {
+ int co[MXRO];
+ DCOUNT(ss, MXRO, s->fdi, -1, -1, 2);
+ double (*vp)[MXRO];
+ double **vpp;
+ int nverts;
+//printf("~1 creating super-cell for bx %d\n",ix);
+
+ /* Maximum number of verticies for all surrounders */
+ for (nverts = (1 << fdi), f = 0; f < fdi; f++)
+ nverts *= 3;
+
+ if ((vp = (double(*)[MXRO]) rev_calloc(s, nverts, sizeof(double) * MXRO)) == NULL)
+ error("rspl malloc failed - rev bxcell vertex list");
+ INCSZ(s, nverts * sizeof(double) * MXRO);
+
+ if ((vpp = (double **) rev_calloc(s, nverts, sizeof(double *))) == NULL)
+ error("rspl malloc failed - rev bxcell vertex list");
+ INCSZ(s, nverts * sizeof(double *));
+
+ /* Search around this cell for other cells to be filled */
+ i = 0;
+ DC_INIT(ss);
+ while (!DC_DONE(ss)) {
+ int nix = ix;
+ for (f = 0; f < fdi; f++) {
+ nix += ss[f] * s->rev.coi[f];
+ co[f] = bx->gc[f] + ss[f];
+ if (co[f] < 0 || co[f] >= s->rev.res)
+ break;
+ }
+
+ /* If within boundary and un-filled non-surface bxcell */
+ if (f >= fdi && (vflag[nix] & 0xf) == 0) {
+ add2indexlist(s, &bx->scell, nix, 0);
+ vflag[nix] = (vflag[nix] & ~0xf) | 1; /* Assume it's now on the seed list */
+
+ /* Create vertex locations for this bxcell */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ for (f = 0; f < fdi; f++)
+ vp[i][f] = (co[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ vpp[i] = vp[i];
+ DC_INC(cc);
+ i++;
+ }
+ }
+ DC_INC(ss);
+ }
+
+ /* Init the group boundary data */
+ nn_grpinit(s, &bx->g, vpp, i, NULL);
+
+ /* Compute the default shadowing test width and distance */
+ /* (Not actually used for super-cell ?) */
+ comp_shadow_group(s, s->rev.ocent, NULL, &bx->cc, &bx->dw, bx->g.bcent, vp, i);
+
+ free(vpp);
+ DECSZ(s, nverts * sizeof(double *));
+
+ free(vp);
+ DECSZ(s, nverts * sizeof(double) * MXRO);
+//printf(" - %d sub-cells\n",bx->scell[1]-3);
+ }
+ }
+
+ if (bx->scell == NULL) {
+ double vp[POW2MXRO][MXRO];
+ double *vpp[POW2MXRO];
+
+ /* Create vertex locations for this bxcell */
+ i = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ for (f = 0; f < fdi; f++)
+ vp[i][f] = (gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ vpp[i] = vp[i];
+ DC_INC(cc);
+ i++;
+ }
+
+ /* Init the group boundary data */
+ nn_grpinit(s, &bx->g, vpp, i, NULL);
+
+ /* Compute the default shadowing test width and distance */
+ comp_shadow_group(s, s->rev.ocent, NULL, &bx->cc, &bx->dw, bx->g.bcent, vp, 1 << fdi);
+ }
-#ifdef NEVER /* Test code */
-/* Reverse closest find using exaustive pseudo hilbert search */
-static void debug_find_closest_rev(
+//printf("~1 grp bcent %s, brad %f\n",debPdv(s->fdi, bx->g.bcent), bx->g.brad);
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ bxcount++;
+ if (bxcount > maxbxcount)
+ maxbxcount = bxcount;
+//printf("~1 now %d bxcells\n",bxcount);
+#endif
+ return bx;
+}
+
+/* Free a bxcell (up to caller to free bx->sl, remove from cache etc.) */
+/* We free the super-cell info. */
+static void del_bxcell(rspl *s, bxcell *bx) {
+ if (bx->scell != NULL) /* If this is a supercell */
+ free_indexlist(s, &bx->scell);
+ if (bx->dl != NULL) /* We have a deleted fwd vertex list */
+ free_indexlist(s, &bx->dl);
+ free(bx);
+ DECSZ(s, sizeof(bxcell));
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ bxcount--;
+//printf("~1 now %d bxcells\n",--bxcount);
+#endif
+}
+
+/* Allocate the surflist hash index */
+static void create_surfhash(rspl *s) {
+
+ s->rev.surf_hash_size = primes[2]; /* 1489 */
+ if ((s->rev.surfhash = (bxcell **) rev_calloc(s, s->rev.surf_hash_size, sizeof(bxcell *))) == NULL)
+ error("rspl malloc failed - reverse bxcell surface cache index");
+ INCSZ(s, s->rev.surf_hash_size * sizeof(bxcell *));
+}
+
+/* Add a bxcell to the surface hash list */
+static void add_bxcell_hash(rspl *s, bxcell *bx) {
+ unsigned int hash = 0;
+
+ hash = bx->ix % s->rev.surf_hash_size;
+ bx->hlink = s->rev.surfhash[hash];
+ s->rev.surfhash[hash] = bx;
+}
+
+/* Remove a bxcell from the surface hash list. */
+/* Doesn't delete the bxcell though. */
+static void rem_bxcell_hash(rspl *s, int ix) {
+ unsigned int hash = 0;
+ bxcell *bx = NULL, **pbx;
+
+ hash = ix % s->rev.surf_hash_size;
+
+ for (pbx = &s->rev.surfhash[hash], bx = *pbx; bx != NULL; pbx = &bx->hlink, bx = *pbx) {
+ if (bx->ix == ix) {
+ *pbx = bx->hlink;
+ return;
+ }
+ }
+}
+
+/* Fetch a surface bxcell from the surface hash list, given its index, or */
+/* Return NULL if none */
+static bxcell *get_surface_bxcell(rspl *s, int ix) {
+ unsigned int hash = 0;
+ bxcell *bx = NULL;
+
+ hash = ix % s->rev.surf_hash_size;
+
+ for (bx = s->rev.surfhash[hash]; bx != NULL; bx = bx->hlink) {
+ if (bx->ix == ix)
+ return bx;
+ }
+ return NULL;
+}
+
+/* Free up surface linked list and delete the bxcells. */
+/* (If we use this, don't use free_surfhash with del set.) */
+static void free_surflist(rspl *s) {
+
+ while (s->rev.surflist != NULL) {
+ bxcell *this = s->rev.surflist;
+ s->rev.surflist = s->rev.surflist->slist;
+ if (this->sl != NULL)
+ free_indexlist(s, &this->sl);
+ del_bxcell(s, this);
+ }
+}
+
+
+/* If del is set, free up all the bxcell cells in the hash index, */
+/* then free the surfhash itself. */
+/* (Use instead of surflist to manage allocation, */
+/* or to clean up hashlist after surflist has been freed.) */
+static void free_surfhash(rspl *s, int del) {
+
+ if (s->rev.surfhash != NULL) {
+ if (del) {
+ int i;
+ for (i = 0; i < s->rev.surf_hash_size; i++) {
+ bxcell *bx, *nbx;
+ for (bx = s->rev.surfhash[i]; bx != NULL; bx = nbx) {
+ nbx = bx->hlink;
+ if (bx->sl != NULL)
+ free_indexlist(s, &bx->sl);
+ del_bxcell(s, bx);
+ }
+ }
+ }
+ free(s->rev.surfhash);
+ DECSZ(s, s->rev.surf_hash_size * sizeof(bxcell *));
+ s->rev.surfhash = NULL;
+ s->rev.surf_hash_size = 0;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Structure to cache prime vertex information when filtering surface */
+/* cell vertex lists. */
+
+/* vertex status */
+typedef enum {
+ vtx_norm = 0, /* Normal vertex in primary bxcell */
+ vtx_sha = 1, /* Vertex has been shadowed */
+ vtx_del = 2, /* Vertex has been deleted because it's shadowed */
+ vtx_oil = 3 /* Vertex is over ink limit */
+} vstat;
+
+struct _vtxrec {
+ int ix; /* fwd index of vertex */
+ int cix; /* Cell index for this vertex */
+ double v[MXRO]; /* Output value of vertex */
+ double dist; /* Distance from center point squared */
+ int tcount; /* Touch count for converting to fwd cells */
+ int acount; /* Actual count for converting to fwd cells */
+
+ vstat status;
+ int tix; /* Target vertex when being created */
+
+ struct _vtxrec *hlink; /* Linked list of vtxrecs with same ix hash */
+ int rix; /* nnrev[] index vertex falls into */
+ int ival[MXRO]; /* nnrev[] coordinate rix */
+
+ char prim; /* nz when primary vertex of bx (not shadow bx) */
+ char cross; /* nz when part of suspected crossed triangle */
+ char pres; /* nz when preserved shadowed vertex from crossed triangle */
+
+ char tflag; /* nz when on tlist */
+ struct _vtxrec *tlist; /* Linked list of vertexes for nnrev[] cell/freelist */
+
+#ifdef REVVRML
+ int addvtx; /* Vertex that caused a bxcell to be added */
+ int vrmlix; /* Index for plotting */
+#endif
+
+}; typedef struct _vtxrec vtxrec;
+
+struct _vtxcache {
+ vtxrec *vtxlist; /* vertex list for soring/itterating selected nnrev cell. */
+ int nilist; /* Number of vertexes in the list */
+
+ int hash_size; /* Current size of vtxrec hash list */
+ vtxrec **hash; /* hash index list */
+
+ vtxrec *freelist; /* Unused vertex structures (to avoid memory allocs) */
+}; typedef struct _vtxcache vtxcache;
+
+
+/* Create the vertex list & hash */
+static void create_vtxrec_list(rspl *s, vtxcache *vc) {
+ vc->hash_size = primes[3]; /* 3373 */
+ if ((vc->hash = (vtxrec **) rev_calloc(s, vc->hash_size, sizeof(vtxrec *))) == NULL)
+ error("rspl malloc failed - vtxrec cache index");
+ INCSZ(s, vc->hash_size * sizeof(vtxrec *));
+ vc->vtxlist = NULL;
+ vc->nilist = 0;
+ vc->freelist = NULL;
+}
+
+/* Clear the vertex hash and list */
+static void clear_vtxrec_lists(rspl *s, vtxcache *vc) {
+ vtxrec *vp, *nvp;
+ int i;
+
+ /* Transfer all records in hash to freelist, */
+ /* and clear hash. */
+ for (i = 0; i < vc->hash_size; i++) {
+ for (vp = vc->hash[i]; vp != NULL; vp = nvp) {
+ nvp = vp->hlink;
+ vp->tlist = vc->freelist;
+ vc->freelist = vp;
+ }
+ vc->hash[i] = NULL;
+ }
+
+ vc->vtxlist = NULL;
+ vc->nilist = 0;
+}
+
+/* Free the vertex list & hash */
+static void free_vtxrec_list(rspl *s, vtxcache *vc) {
+ clear_vtxrec_lists(s, vc);
+
+ while (vc->freelist != NULL) {
+ vtxrec *this = vc->freelist;
+ vc->freelist = vc->freelist->tlist;
+ free(this);
+ DECSZ(s, sizeof(vtxrec));
+ }
+ free(vc->hash);
+ DECSZ(s, vc->hash_size * sizeof(vtxrec *));
+ vc->hash = NULL;
+ vc->hash_size = 0;
+}
+
+/* Add a vtxrec to the vertex hash list */
+static void add_vtxrec_hash(vtxcache *vc, vtxrec *vx) {
+ unsigned int hash = 0;
+
+ hash = vx->ix % vc->hash_size;
+ vx->hlink = vc->hash[hash];
+ vc->hash[hash] = vx;
+}
+
+/* Delete a vtxrec from the vertex hash list */
+/* (Assume it's not part of vtxlist!) */
+static void del_vtxrec_hash(vtxcache *vc, int ix) {
+ unsigned int hash = 0;
+ vtxrec *vx = NULL, **pvx;
+
+ hash = ix % vc->hash_size;
+
+ for (pvx = &vc->hash[hash], vx = *pvx; vx != NULL; pvx = &vx->hlink, vx = *pvx) {
+ if (vx->ix == ix) {
+ *pvx = vx->hlink;
+ vx->tlist = vc->freelist;
+ vc->freelist = vx;
+ vx->hlink = NULL;
+ return;
+ }
+ }
+}
+
+/* Fetch a surface vtxrec from the hash list, given its index */
+/* Return NULL if none */
+static vtxrec *get_vtxrec(vtxcache *vc, int ix) {
+ unsigned int hash = 0;
+ vtxrec *vx = NULL;
+
+ hash = ix % vc->hash_size;
+
+ for (vx = vc->hash[hash]; vx != NULL; vx = vx->hlink) {
+ if (vx->ix == ix)
+ return vx;
+ }
+ return NULL;
+}
+
+/* Create a new vtxrec or return the current one. */
+/* Allocates it, adds it to cache. */
+/* DOESN"T add it to vtxlist. */
+static vtxrec *new_vtxrec(
+ rspl *s,
+ vtxcache *vc,
+ int ix /* fwd index of vertex */
+) {
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ vtxrec *vx = NULL;
+ float *gp;
+ int rix;
+ int rgres_1 = s->rev.res -1; /* rgres -1 == maximum base coord value */
+
+ /* See if we've already got this vertex */
+ if ((vx = get_vtxrec(vc, ix)) != NULL)
+ return vx;
+
+ /* Fetch or allocate a new structure */
+ if (vc->freelist != NULL) { /* Grab one from free list */
+ vx = vc->freelist;
+ vc->freelist = vx->tlist;
+ memset((void *)vx, 0, sizeof(vtxrec));
+
+ } else {
+ if ((vx = (vtxrec *) rev_calloc(s, 1, sizeof(vtxrec))) == NULL)
+ error("rspl malloc failed - rev vtxrec structs");
+ INCSZ(s, sizeof(vtxrec));
+ }
+
+ /* Our fwd index */
+ vx->ix = ix;
+
+ /* Add it to the hash */
+ add_vtxrec_hash(vc, vx);
+
+ /* Fwd vertex array address */
+ gp = s->g.a + ix * s->g.pss;
+
+ /* Set cell index so that cell verticies don't exceed grid boundary */
+ vx->cix = ix;
+ for (e = 0; e < di; e++) {
+ if (G_FL(gp, e) == 0) /* At the top edge */
+ vx->cix -= s->g.ci[e]; /* Move cell base down a row */
+ }
+
+ /* Get the output value */
+ for (f = 0; f < fdi; f++)
+ vx->v[f] = gp[f];
+
+ /* Compute distance to overall center point squared */
+ vx->dist = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = gp[f] - s->rev.ocent[f];
+ vx->dist += tt * tt;
+ }
+
+ /* Figure the actual nncell it lands in */
+ for (rix = f = 0; f < fdi; f++) {
+ double t;
+ int mi;
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ t = (vx->v[f] - gl)/gw;
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi > rgres_1)
+ mi = rgres_1;
+ vx->ival[f] = mi;
+ rix += mi * s->rev.coi[f];
+ }
+ vx->rix = rix;
+
+ return vx;
+}
+
+/* Add a vertex to the list. */
+/* Don't add if already on list (if tflag set), or if shadowed) */
+/* set prim flag to value */
+static void add_vtxrec_list(vtxcache *vc, vtxrec *vx, int prim) {
+
+ vx->prim = (char)prim; /* Always set prim flag */
+
+ if (vx->tflag || vx->status != vtx_norm)
+ return;
+
+ vx->tlist = vc->vtxlist;
+ vc->vtxlist = vx;
+ vx->tflag = 1;
+ vc->nilist++;
+
+}
+
+int dumpvtxsort = 0;
+
+/* Sort the vertex linked list by dist. */
+/* Also reset the tflag */
+static void sort_vtxrec_list(rspl *s, vtxcache *vc) {
+ int i;
+ vtxrec **sort, *vx;
+
+ /* Create temporary array of pointers to vtxrec's in list */
+ if ((sort = (vtxrec **) rev_calloc(s, vc->nilist, sizeof(vtxrec *))) == NULL)
+ error("rspl malloc failed - rev vtxrec sort array");
+ INCSZ(s, vc->nilist * sizeof(vtxrec *));
+
+ for (i = 0, vx = vc->vtxlist; vx != NULL; vx = vx->tlist, i++)
+ sort[i] = vx;
+
+ /* Sort the list into ascending distance from center */
+#define HEAP_COMPARE(A,B) (A->dist < B->dist)
+ HEAPSORT(vtxrec *, sort, vc->nilist)
+#undef HEAP_COMPARE
+
+ /* Re-create the linked list in descending order */
+ vc->vtxlist = NULL;
+ for (i = 0; i < vc->nilist; i++) {
+ vx = sort[i];
+ vx->tlist = vc->vtxlist;
+ vc->vtxlist = vx;
+ vx->tflag = 0;
+ }
+
+ free(sort);
+ DECSZ(s, vc->nilist * sizeof(vtxrec *));
+
+#ifndef NEVER
+ if (dumpvtxsort) {
+ printf("sorted vertex list:\n");
+ for (i = 0, vx = vc->vtxlist; vx != NULL; vx = vx->tlist, i++)
+ printf("%d: ix %d, dist %f\n",i,vx->ix, sqrt(vx->dist));
+ }
+#endif
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Structure to cache surface triangle vertexes, to avoid repeated */
+/* shadowing test */
+
+struct _trirec{
+ int ix[3]; /* vertex indexes of triangle in simplex order */
+ struct _trirec *hlink; /* Linked list of triangles in hash/freelist */
+}; typedef struct _trirec trirec;
+
+typedef struct {
+ int hash_size; /* Current size of trirec hash list */
+ trirec **hash; /* hash index list */
+ trirec *freelist; /* Unused trirec structures (to avoid memory allocs) */
+} tricache;
+
+/* Create the tricache list & hash. */
+/* Set sm flag if we only want a small cache size */
+static void create_trirec(rspl *s, tricache *tc, int sm) {
+ if (sm)
+ tc->hash_size = primes[1]; /* 853 */
+ else
+ tc->hash_size = primes[5]; /* 12919 */
+ if ((tc->hash = (trirec **) rev_calloc(s, tc->hash_size, sizeof(trirec *))) == NULL)
+ error("rspl malloc failed - trirec cache index");
+ INCSZ(s, tc->hash_size * sizeof(trirec *));
+ tc->freelist = NULL;
+}
+
+/* Clear the trirec list & hash */
+static void clear_trirec(rspl *s, tricache *tc) {
+ int i;
+ trirec *tp, *ntp;
+
+ /* Transfer all records in hash to freelist, */
+ /* and clear hash. */
+ for (i = 0; i < tc->hash_size; i++) {
+ for (tp = tc->hash[i]; tp != NULL; tp = ntp) {
+ ntp = tp->hlink;
+ tp->hlink = tc->freelist;
+ tc->freelist = tp;
+ }
+ tc->hash[i] = NULL;
+ }
+}
+
+/* Free the triangle list & hash */
+static void free_trirec(rspl *s, tricache *tc) {
+ clear_trirec(s, tc);
+
+ while (tc->freelist != NULL) {
+ trirec *this = tc->freelist;
+ tc->freelist = tc->freelist->hlink;
+ free(this);
+ DECSZ(s, sizeof(trirec));
+ }
+ free(tc->hash);
+ DECSZ(s, tc->hash_size * sizeof(trirec *));
+ tc->hash = NULL;
+ tc->hash_size = 0;
+}
+
+/* Check if a triangle is in the cache. */
+/* return nz if it is, and z if it isn't, and add it. */
+static int check_trirec(rspl *s, tricache *tc, int *ix) {
+ int i;
+ unsigned int hash = 0;
+ trirec *tp = NULL;
+
+ hash = ix[0];
+ hash = hash * 17 + ix[1];
+ hash = hash * 17 + ix[2];
+ hash %= tc->hash_size;
+
+ for (tp = tc->hash[hash]; tp != NULL; tp = tp->hlink) {
+ if (tp->ix[0] == ix[0]
+ && tp->ix[1] == ix[1]
+ && tp->ix[2] == ix[2]) {
+//printf("check_trirec %d %d %d is in cache\n",ix[0], ix[1], ix[2]);
+ return 1;
+ }
+ }
+//printf("check_trirec %d %d %d NOT in cache\n",ix[0], ix[1], ix[2]);
+
+ /* Allocate a new structure */
+ if (tc->freelist != NULL) { /* Grab one from free list */
+ tp = tc->freelist;
+ tc->freelist = tp->hlink;
+ memset((void *)tp, 0, sizeof(trirec));
+
+ } else {
+ if ((tp = (trirec *) rev_calloc(s, 1, sizeof(trirec))) == NULL)
+ error("rspl malloc failed - rev trirec structs");
+ INCSZ(s, sizeof(trirec));
+ }
+
+ tp->ix[0] = ix[0];
+ tp->ix[1] = ix[1];
+ tp->ix[2] = ix[2];
+
+ /* add it into the hash */
+ tp->hlink = tc->hash[hash];
+ tc->hash[hash] = tp;
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Add another entry to an index/share list, taking care of any re-sizing */
+/* Set shlist if this is a sharer record */
+static void add2indexlist(rspl *s, int **rpp, int ix, int shrec) {
+ int *rp = *rpp;
+
+ if (rp == NULL) {
+ if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
+ error("rspl malloc failed - rev.grid list");
+ INCSZ(s, 6 * sizeof(int));
+ rp[0] = 6; /* Allocation */
+ rp[1] = 4; /* Next free Cell */
+ rp[2] = -1; /* share list index - default none */
+ rp[3] = ix; /* Index added to list */
+ rp[4] = -1; /* End of list marker */
+ *rpp = rp; /* Update pointer */
+ } else {
+ int z = rp[1], ll = rp[0];
+ if (z >= (ll-1)) { /* Not enough space */
+ if (!shrec && rp[2] != -1)
+ error("Re-allocating shared fwd index list");
+ INCSZ(s, ll * sizeof(int));
+ ll *= 2;
+ if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
+ error("rspl realloc failed - rev.grid list size %d",ll);
+ rp[0] = ll; /* Allocation */
+ *rpp = rp; /* Update pointer */
+ }
+ rp[z++] = ix; /* Index added to list */
+ rp[z] = -1; /* End of list marker */
+ rp[1] = z; /* Next free Cell */
+ }
+}
+
+/* Copy an index list (i.e. from nnrev[] to bxcell->sl) */
+static void copy_indexlist(rspl *s, int **dp, int *sp) {
+ if (sp == NULL)
+ *dp = NULL;
+ else {
+ int i;
+ if ((*dp = (int *) rev_malloc(s, sp[0] * sizeof(int))) == NULL)
+ error("rspl malloc failed - rev.grid list");
+ INCSZ(s, sp[0] * sizeof(int));
+ for (i = 0; i <= sp[1]; i++)
+ (*dp)[i] = sp[i];
+ (*dp)[2] = -1;
+ }
+}
+
+/* Free an index list, at set it to NULL */
+static void free_indexlist(rspl *s, int **rp) {
+ if (*rp != NULL) {
+ DECSZ(s, (*rp)[0] * sizeof(int));
+ free(*rp);
+ *rp = NULL;
+ }
+}
+
+/* Add a (fwd index list) sharer to share list. */
+/* Record will be created if list[2] == -1, */
+/* or incremented otherwise. */
+/* sharerix is the index of the cell sharing the *list */
+static void add2sharelist(rspl *s, int sharerix, int *list) {
+ int *sharerec = NULL;
+
+ /* Create a new record and add our (one) sharer to it */
+ if (list[2] == -1) {
+ if (s->rev.sharellen >= s->rev.sharelaloc) {
+ /* Allocate another sharelist entry */
+ INCSZ(s, (10 + s->rev.sharelaloc) * sizeof(int *));
+ s->rev.sharelaloc = 10 + 2 * s->rev.sharelaloc;
+ if ((s->rev.sharelist = (int **)rev_realloc(s, s->rev.sharelist,
+ s->rev.sharelaloc * sizeof(int *))) == NULL)
+ error("add2sharelist: realloc failed");
+ }
+ add2indexlist(s, &sharerec, sharerix, 1);
+ s->rev.sharelist[s->rev.sharellen] = sharerec;
+ list[2] = s->rev.sharellen;
+ s->rev.sharellen++;
+
+ /* Add the sharer to the existing sharer list */
+ } else {
+ if (list[2] >= s->rev.sharellen)
+ error("add2sharelist got list with sharelist index out of range");
+ sharerec = s->rev.sharelist[list[2]];
+ add2indexlist(s, &sharerec, sharerix, 1);
+ s->rev.sharelist[list[2]] = sharerec;
+ }
+}
+
+/* Return the sharer list for the given (fwd cell) list */
+/* Return NULL if not shared */
+static int *getsharelist(rspl *s, int *list) {
+ if (list[2] == -1)
+ return NULL;
+ if (list[2] >= s->rev.sharellen) {
+ error("getsharelist got list with sharelist index out of range (%d > %d)",list[2],s->rev.sharellen);
+ }
+ return s->rev.sharelist[list[2]];
+}
+
+/* Free all the sharelist and the shared nnrev[] fwd cell lists as well */
+static void free_sharelist(rspl *s) {
+ if (s->rev.sharelist != NULL) {
+ int i, j;
+ for (i = 0; i < s->rev.sharellen; i++) {
+ int *shrec = s->rev.sharelist[i];
+
+ /* Free the shared fwd cell list */
+ if (shrec[1] > 3) {
+ int *clist = s->rev.nnrev[shrec[3]];
+ DECSZ(s, clist[0] * sizeof (int));
+ free(clist);
+ }
+
+ /* Make sure freeing of s->rev.nnrev[] doesn't free them twice */
+ for (j = 3; shrec[j] != -1; j++)
+ s->rev.nnrev[shrec[j]] = NULL;
+
+ DECSZ(s, s->rev.sharelist[i][0] * sizeof (int));
+ free(s->rev.sharelist[i]);
+ }
+ DECSZ(s, s->rev.sharelaloc * sizeof(int *));
+ free(s->rev.sharelist);
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* For shadow bxcell testing, compute the delta distance */
+/* rev.ocent, and delta "width" between two vertex values. */
+
+/* Compute shadow testing group values */
+static void comp_shadow_group(
+ rspl *s,
+ double *gcent, /* In gamut center point to compute from */
+ double *rgc, /* Return group center if non-NULL */
+ double *pcc, /* Return distance of group center to gamut center */
+ double *pdw, /* Return width of furthest point from group center */
+ double *gc, /* if not-NULL, the group center */
+ double (*v)[MXRO], /* Input verticies */
+ int nvert /* Number of verticies */
+) {
+ double _gc[MXRO];
+ int i;
+ int f, fdi = s->fdi;
+ double cc; /* gamut center to group center */
+ double dw = -1.0; /* Largest goup vertex width */
+
+ /* if no group center given, compute one simply as an average */
+ /* (Used for triangle) */
+ if (gc == NULL) {
+ gc = _gc;
+ for (f = 0; f < fdi; f++)
+ gc[f] = 0.0;
+ for (i = 0; i < nvert; i++) {
+ for (f = 0; f < fdi; f++) {
+ gc[f] += v[i][f];
+ }
+ }
+ for (f = 0; f < fdi; f++)
+ gc[f] /= (double)nvert;
+ }
+
+ /* Return it if requested */
+ if (rgc != NULL) {
+ for (f = 0; f < fdi; f++)
+ rgc[f] = gc[f];
+ }
+
+ /* Compute distance from gamut center to group center */
+ for (cc = 0.0, f = 0; f < fdi; f++) {
+ double tt = gcent[f] - gc[f];
+ cc += tt * tt;
+ }
+ cc = sqrt(cc);
+
+ if (pcc != NULL)
+ *pcc = cc;
+
+ /* Compute width for each vertex, and track maximum */
+ for (i = 0; i < nvert; i++) {
+ double vlen, scale;
+ double sv[MXRO]; /* Vertex scaled to same distance as group center */
+ double w;
+
+ /* vertex length from gamut center */
+ for (vlen= 0.0, f = 0; f < fdi; f++) {
+ double tt = v[i][f] - gcent[f];
+ vlen += tt * tt;
+ }
+ vlen = sqrt(vlen);
+
+ if (vlen > 1e-6)
+ scale = cc/vlen;
+ else
+ scale = 1.0;
+
+ for (f = 0; f < fdi; f++)
+ sv[f] = (scale * (v[i][f] - gcent[f])) + gcent[f];
+
+ /* Distance from scaled vertex to group center */
+ for (w = 0.0, f = 0; f < fdi; f++) {
+ double tt = sv[f] - gc[f];
+ w += tt * tt;
+ }
+ if (w > dw)
+ dw = w;
+
+ }
+ dw = sqrt(dw);
+
+ if (pdw != NULL)
+ *pdw = dw;
+}
+
+/* Expand a bxcell's shadow testing group values based on it's vertex list */
+static void extend_bxcell_shadow_group(
+ rspl *s,
+ vtxcache *vc,
+ bxcell *bx
+) {
+ int *ip;
+ int f, fdi = s->fdi;
+ double dw;
+
+ if (bx->sl == NULL)
+ return;
+
+ /* Current dw squared */
+ dw = bx->dw * bx->dw;
+
+ /* Compute width for each vertex, and track maximum */
+ for (ip = bx->sl+3; *ip != -1; ip++) {
+ vtxrec *vx;
+ double vlen, scale;
+ double sv[MXRO]; /* Vertex scaled to same distance as group center */
+ double w;
+
+ if ((vx = get_vtxrec(vc, *ip)) == NULL)
+ continue;
+
+ /* vertex length from gamut center */
+ for (vlen= 0.0, f = 0; f < fdi; f++) {
+ double tt = vx->v[f] - s->rev.ocent[f];
+ vlen += tt * tt;
+ }
+ vlen = sqrt(vlen);
+
+ if (vlen > 1e-6)
+ scale = bx->cc/vlen;
+ else
+ scale = 1.0;
+
+ for (f = 0; f < fdi; f++)
+ sv[f] = (scale * (vx->v[f] - s->rev.ocent[f])) + s->rev.ocent[f];
+
+ /* Distance from scaled vertex to group center */
+ for (w = 0.0, f = 0; f < fdi; f++) {
+ double tt = sv[f] - bx->g.bcent[f];
+ w += tt * tt;
+ }
+ if (w > dw)
+ dw = w;
+
+ }
+ if (dw > bx->dw)
+ bx->dw = sqrt(dw);
+}
+
+/* Shadow group to group compare. Return nz if within range */
+static int shadow_group_group(
+ rspl *s,
+ double *gcent, /* Input gamut center point to compute from */
+ double *gc1, /* Reference group center point */
+ double cc1, /* Reference point cc value */
+ double dw1, /* Reference point dw value */
+ double *gc2, /* Comparison group center point */
+ double cc2, /* Comparison point cc value */
+ double dw2 /* Comparison point dw value */
+) {
+ int i;
+ int f, fdi = s->fdi;
+ double dot, scale;
+ double sv[MXRO]; /* Comparison group center scaled to same distance as ref center */
+ double w;
+
+ /* Compute dot product of cc1 and cc2 */
+ for (dot = 0.0, f = 0; f < fdi ; f++)
+ dot += (gc1[f] - gcent[f]) * (gc2[f] - gcent[f]);
+
+ /* If the groupls are not in the same direction, return false */
+ if (dot < 0.0)
+ return 0;
+
+ if (cc2 > 1e-6)
+ scale = cc1/cc2;
+ else
+ scale = 1.0;
+
+ for (f = 0; f < fdi; f++)
+ sv[f] = (scale * (gc2[f] - gcent[f])) + gcent[f];
+
+ /* Distance from scaled group center to ref. group center */
+ for (w = 0.0, f = 0; f < fdi; f++) {
+ double tt = sv[f] - gc1[f];
+ w += tt * tt;
+ }
+ w = sqrt(w);
+
+ if (w <= (dw1 + (scale * dw2) + EPS))
+ return 1;
+
+ return 0;
+}
+
+/* Shadow group to vertex compare. Return nz if within range */
+static int shadow_group_vertex(
+ rspl *s,
+ double *gcent, /* Input gamut center point to compute from */
+ double *gc1, /* Reference group center point */
+ double cc1, /* Reference point cc value */
+ double dw1, /* Reference point dw value */
+ double *v /* Comparison vertex location */
+) {
+ int i;
+ int f, fdi = s->fdi;
+ double vlen, dot, scale;
+ double sv[MXRO]; /* Vertex scaled to same distance as group center */
+ double w;
+
+ /* Compute dot product of cc1 and cc2 */
+ for (dot = 0.0, f = 0; f < fdi ; f++)
+
+ /* vertex length from center */
+ /* and dot product with group center vector */
+ for (vlen= 0.0, f = 0; f < fdi; f++) {
+ double tt = v[f] - gcent[f];
+ vlen += tt * tt;
+ dot += (gc1[f] - gcent[f]) * tt;
+ }
+
+ /* If the groupls are not in the same direction, return false */
+ if (dot < 0.0)
+ return 0;
+
+ vlen = sqrt(vlen);
+
+ if (vlen > 1e-6)
+ scale = cc1/vlen;
+ else
+ scale = 1.0;
+
+ for (f = 0; f < fdi; f++)
+ sv[f] = (scale * (v[f] - gcent[f])) + gcent[f];
+
+ /* Distance from scaled vertex to group center */
+ for (w = 0.0, f = 0; f < fdi; f++) {
+ double tt = sv[f] - gc1[f];
+ w += tt * tt;
+ }
+ w = sqrt(w);
+
+ if (w <= (dw1 + EPS))
+ return 1;
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Given a pointer to a bxcell, use the ->tlist to fill in the corresponding nnrev[]. */
+/* Expand and share lists with nearby nnrev[] cells if they are similar. */
+static void create_nnrev_list(
rspl *s,
-double *out,
-double *in
+bxcell *tx, /* Target nnrev[] cell */
+bxcell *ss, /* Head of solution list of surface nnrev[] cells */
+double emax /* smallest emax in solution list */
) {
- double best = 1e38;
- int e, f;
- rpsh counter; /* Pseudo-hilbert counter */
- int gc[MXDI]; /* Grid index value */
- double iv[MXDI];
- float *gp; /* Pointer to grid data */
+ int i, j;
+ bxcell *bx;
+ int *dp = NULL, *sp;
+ double *eminlist;
+ unsigned int hashk;
- rpsh_init(&counter, s->di, (unsigned int *)s->g.res, gc); /* Initialise counter */
- for (;;) {
- double dist;
+ DBG(("create_nnrev_list: di %d target cell ix %d co[] %s emax = %f\n",s->di, tx->ix,debPiv(s->fdi, tx->gc),emax));
- /* Compute grid pointer and input sample values */
- gp = s->g.a; /* Base of grid data */
- for (e = 0; e < s->di; e++) { /* Input tables */
- gp += s->g.fci[e] * gc[e]; /* Grid value pointer */
- iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ /* Update tx->ss and tx->sdist with best in tlist for future */
+ /* searches from this surface cell. */
+ tx->sdist = 1e200;
+ for (bx = ss; bx != NULL; bx = bx->tlist) {
+//printf("~1 checking ix %d\n",bx->ix);
+ if (bx->sdist < tx->emin) {
+ tx->ss = bx;
+ tx->sdist = bx->emin;
+ DBG((" Set target ss to ix %d, emin %f\n",tx->ss->ix, tx->sdist));
+//printf(" Set target ss to ix %d, emin %f\n",tx->ss->ix, tx->sdist);
}
+ }
+//printf("~1 set sdist\n");
- dist = 0.0;
- for (f = 0; f < s->fdi; f++) {
- double tt = in[f] - (double)gp[f];
- dist += tt * tt;
+#ifdef DEBUG2
+ {
+ int tot = 0;
+ printf(" Initial fwd list from following surface cells:\n");
+ for (bx = ss; bx != NULL; bx = bx->tlist) {
+ if (bx->emin <= emax) {
+ if (bx->sl == NULL)
+ error("rev create_nnrev_list: found empty surface bxcell");
+ printf(" ix %d co %s fwd count %d\n",bx->ix,debPiv(s->fdi, bx->gc),bx->sl[1]-3);
+ tot += bx->sl[1]-3;
+ }
}
- if (dist < best) {
- best = dist;
- for (e = 0; e < s->di; e++)
- out[e] = iv[e];
+ printf(" Total fwd cells = %d\n",tot);
+ }
+#endif
+
+ /* Create an initial list of fwd cells from all bxcells */
+ /* on the solution list that have ->emin <= emax */
+ for (bx = ss; bx != NULL; bx = bx->tlist) {
+//printf("~1 checking solution cell ix %d co %s\n",bx->ix,debPiv(s->fdi, bx->gc));
+ if (bx->emin <= emax) {
+//printf("~1 solution cell has emin %f < emax %f\n",bx->emin, emax);
+ sp = bx->sl;
+ if (sp == NULL)
+ error("rev create_nnrev_list: found empty surface bxcell %d",ss->ix);
+ for (sp += 3; *sp != -1; sp++)
+ add2indexlist(s, &dp, *sp, 0);
}
+ }
+
+ if (dp == NULL)
+ error("create_nnrev_list got NULL new list\n");
+
+#ifdef DEBUG2
+ printf(" Initial fwd list (length %d, alloc %d):\n",dp[1]-3,dp[0]);
+ for (i = 3; dp[i] != -1; i++)
+ printf(" %d: ix %d\n",i-3,dp[i]);
+#endif
+
+ /* Sort the list into ascending order */
+#define HEAP_COMPARE(A,B) (A < B)
+ HEAPSORT(int, dp + 3, dp[1]-3)
+#undef HEAP_COMPARE
- /* Increment counter */
- if (rpsh_inc(&counter, gc))
+#ifdef DEBUG2
+ printf(" After sorting:\n");
+ for (i = 3; dp[i] != -1; i++)
+ printf(" %d: ix %d\n",i-3,dp[i]);
+#endif
+
+ /* Delete any duplicates */
+ for (i = 3, j = i+1; ; j++) {
+ if (dp[i] != dp[j])
+ dp[++i] = dp[j];
+ if (dp[j] == -1)
break;
}
+ dp[1] = i;
+
+#ifdef DEBUG2
+ printf(" After de-duplication (length %d, alloc %d):\n",dp[1],dp[0]);
+ for (i = 3; dp[i] != -1; i++)
+ printf(" %d: ix %d\n",i-3,dp[i]);
+#endif
+
+ /* Filter fwd cells against emin/emax. */
+ /* (Don't bother for 1D, as there's no point in filling up the cache */
+ /* at this point, since 1D ins't participating in RAM management ?) */
+ if (s->fdi > 1) {
+ /* Allocate a temporary array to hold fwd cell emin */
+ if ((eminlist = (double *) rev_malloc(s, (dp[1]-3) * sizeof(double))) == NULL)
+ error("rspl malloc failed - rev create_nnrev_list emin array");
+ INCSZ(s, (dp[1]-3) * sizeof(double));
+
+ for (i = 0; i < (dp[1]-3); i++)
+ eminlist[i] = 1e200;
+
+ /* Get an fxcell for each fwd index, and compute emin & emax for this target. */
+ /* Tracl smallest maximum and record each fxcell emin */
+ emax = 1e200;
+ for (i = 3; dp[i] != -1; i++) {
+ fxcell *fc;
+ double em, ex;
+
+ fc = get_fxcell(s->rev.sb, dp[i], 1);
+
+ eminlist[i-3] = nn_grpgrp_est(s, &ex, &fc->g, &tx->g);
+ if (ex < emax)
+ emax = ex;
+
+ unget_fxcell(s->rev.cache, fc);
+ }
+
+#ifdef DEBUG2
+ printf(" Smallest emax = %f\n",emax);
+ for (i = 3; dp[i] != -1; i++)
+ printf(" %d: ix %d, emin %f\n",i-3,dp[i],eminlist[i-3]);
+#endif
+
+ /* Delete any fwd cells/indexes that have an emin > smallest emax */
+ for (i = j = 3; dp[j] != -1; j++) {
+ if (eminlist[j-3] <= emax)
+ dp[i++] = dp[j];
+ }
+ dp[i] = -1;
+ dp[1] = i;
+
+ free(eminlist);
+ DECSZ(s, sizeof(schbase));
+
+#ifdef DEBUG2
+ printf(" After removing too far cells (length %d, alloc %d):\n",i-3,dp[0]);
+ for (i = 3; dp[i] != -1; i++)
+ printf(" %d: ix %d\n",i-3,dp[i]);
+#endif
+ }
+
+ /* If the size of the list has reduced substatially, reclaim some memory */
+ if ((dp[1]+1) <= (dp[0]/2)) {
+ int ll = dp[0];
+ while (ll > (dp[1]+1))
+ ll /= 2;
+ ll *= 2;
+ DBG((" Reducing list allocation from %d to %d entries\n",dp[0],ll));
+ DECSZ(s, (dp[0] - ll) * sizeof(int));
+ if ((dp = (int *) rev_realloc(s, dp, sizeof(int) * ll)) == NULL)
+ error("rspl realloc failed - create_nnrev_list");
+ dp[0] = ll; /* New allocation */
+ }
+
+ /* Check if any neighbor lists are similar to the list we just created, */
+ /* so that we can merge similar lists, greatly reducing memory usage */
+ /* at the cost of slightly longer lists. */
+ /* Don't do this if this is a super-cell. */
+ /* [ This seems to increase nnrev fill time by about 5% ] */
+ if (tx->scell == NULL) {
+ DCOUNT(cc, MXRO, s->fdi, -1, -1, 2); /* bwd neighborhood offset counter */
+ int nn[MXRO];
+ int shlim, lnlim;
+ int sh, ln;
+ int bnix = -1, *blist = NULL, bwhgt = 0x7ffffff, bsh, bln;
+ int f, nix;
+
+ /* Set limits of an acceptable match at 2% short, 15% long */
+ /* This trades off list size against number of lists/memory */
+ /* i.e. a 10% rise in average list length for a 100 x reduction in */
+ /* number of lists. (vary lnlim for most effect) */
+ shlim = (2 * (dp[1]-3) + 50)/100;
+ lnlim = (15 * (dp[1]-3) + 50)/100;
+
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+
+ nix = tx->ix;
+ for (f = 0; f < s->fdi; f++) {
+ nn[f] = tx->gc[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= s->rev.res)
+ break; /* Out of bounds */
+ nix += cc[f] * s->rev.coi[f];
+ }
+ if (nix == tx->ix) /* Skip this cell */
+ goto next_neighbor;
+
+ /* If neighbor is in bounds and has a fwd cell list, */
+ /* check what sort of match it is to this list */
+ if (f >= s->fdi && s->rev.nnrev[nix] != NULL) {
+ int *np = s->rev.nnrev[nix];
+ int *shrecs = getsharelist(s, np);
+
+ if (shrecs != NULL) {
+ if (shrecs[2] == tx->ix) /* Already looked at this list */
+ goto next_neighbor;
+ shrecs[2] = tx->ix; /* Remember we've done this one */
+ }
+
+ /* See how much it is a super or sub-set */
+//printf("~1 checking ix %d against nix %d\n",tx->ix, nix);
+ if ((dp[1] - np[1]) > shlim
+ || (np[1] - dp[1]) > lnlim) {
+ goto next_neighbor; /* No possibility of being acceptable */
+ }
+
+ sh = ln = 0;
+ for (j = i = 3; dp[i] != -1 || np[j] != -1;) {
+
+//printf("1: dp[%d] %d - np[%d] %d\n",i,dp[i],j,np[j]);
+ while (np[j] != -1 && (dp[i] == -1 || dp[i] > np[j])) {
+ j++;
+ ln++;
+//printf("2: dp[%d] %d - np[%d] %d, ln %d\n",i,dp[i],j,np[j],ln);
+ if (ln > lnlim)
+ goto next_neighbor; /* No possibility of being acceptable */
+ }
+
+ while (dp[i] != -1 && (np[j] == -1 || dp[i] < np[j])) {
+ i++;
+ sh++;
+//printf("3: dp[%d] %d - np[%d] %d, sh %d\n",i,dp[i],j,np[j],sh);
+ if (sh > shlim)
+ goto next_neighbor; /* No possibility of being acceptable */
+ }
+
+ while (dp[i] != -1 && np[j] != -1 && dp[i] == np[j]) {
+ i++;
+ j++;
+//printf("4: dp[%d] %d - np[%d] %d\n",i,dp[i],j,np[j]);
+ }
+ }
+//printf("~1 len %d, short %d, long %d\n",dp[1]-3,sh,ln);
+
+ /* remember best similar list within our criteria */
+ if (sh <= shlim && ln <= lnlim) {
+ int whgt = 2 * sh + ln;
+ if (whgt < bwhgt) {
+ bnix = nix;
+ blist = np;
+ bwhgt = bwhgt;
+ bsh = sh;
+ bln = ln;
+ }
+ }
+ }
+ next_neighbor:;
+ DC_INC(cc);
+ }
+
+ /* Got a list we want to share with */
+ if (blist != NULL) {
+ int *shrecs = NULL;
+ int *exlist = NULL;
+
+ DBG((" Found similar existing list (short %d, long %d)\n",bsh,bln));
+
+#ifdef DEBUG2
+ printf(" Similar list (length %d, alloc %d):\n",blist[1]-3,blist[0]);
+// for (i = 3; blist[i] != -1; i++)
+// printf(" %d: ix %d\n",i-3,blist[i]);
+#endif
+ /* If the neighbor list is not a super-set */
+ if (bsh > 0) {
+
+ /* But new list is superset of neighbor list */
+ if (bln == 0) {
+ DBG((" Using new list to share\n"));
+
+ exlist = dp; /* Use our new list */
+ exlist[2] = blist[2]; /* Same sharers */
+ dp = NULL;
+
+ /* Free neighbor list */
+ free_indexlist(s, &blist);
+
+ /* Create superset list from new list and neighbor list */
+ } else {
+
+ DBG((" Creating superset list\n"));
+
+ for (j = i = 3; dp[i] != -1 || blist[j] != -1;) {
+
+ while (blist[j] != -1 && (dp[i] == -1 || dp[i] > blist[j])) {
+ add2indexlist(s, &exlist, blist[j], 0);
+ j++;
+ }
+
+ while (dp[i] != -1 && (blist[j] == -1 || dp[i] < blist[j])) {
+ add2indexlist(s, &exlist, dp[i], 0);
+ i++;
+ }
+
+ while (dp[i] != -1 && blist[j] != -1 && dp[i] == blist[j]) {
+ add2indexlist(s, &exlist, dp[i], 0);
+ i++;
+ j++;
+ }
+ }
+
+ exlist[2] = blist[2]; /* Same sharers */
+
+ /* Free neighbor list */
+ free_indexlist(s, &blist);
+
+ /* Done with list we created for this nnrev[] */
+ free_indexlist(s, &dp);
+ }
+
+ } else {
+ DBG((" Using existing list to share\n"));
+
+ exlist = blist; /* blist is already a super-set */
+ blist = NULL; /* Done with neighbor list */
+
+ /* Done with list we created for this nnrev[] */
+ free_indexlist(s, &dp);
+ }
+
+#ifdef DEBUG2
+ printf(" Superset list nnrev[%d] (length %d, alloc %d):\n",tx->ix,exlist[1]-3,exlist[0]);
+// for (i = 3; exlist[i] != -1; i++)
+// printf(" %d: ix %d\n",i-3,exlist[i]);
+#endif
+//if (s->fdi > 1 && (tx->ix == 19054 || tx->ix == 19055)) {
+//printf(" Superset list nnrev[%d] (length %d, alloc %d):\n",tx->ix,exlist[1]-3,exlist[0]);
+//for (i = 3; exlist[i] != -1; i++)
+// printf(" %d: ix %d\n",i-3,exlist[i]);
+//}
+
+ /* If this list has not been shared before, create share record for it */
+ if (getsharelist(s, exlist) == NULL)
+ add2sharelist(s, bnix, exlist);
+
+ /* Add this cell as a sharer */
+ add2sharelist(s, tx->ix, exlist);
+
+ /* Update pointers for all sharers of this (possibly new) list */
+ shrecs = getsharelist(s, exlist);
+//printf("Number shared now %d\n", shrecs[1]-3);
+ for (i = 3; shrecs[i] != -1; i++) {
+ s->rev.nnrev[shrecs[i]] = exlist;
+ }
+
+ } else {
+ DBG((" no matching existing list\n"));
+//printf(" no matching existing list\n");
+
+ /* Put list in place for target nnrev[]*/
+ s->rev.nnrev[tx->ix] = dp;
+ }
+ } else {
+
+ if (tx->scell != NULL) {
+
+ /* Put list in place for all nnrev[]'s covered by super-cell */
+ for (sp = tx->scell + 3; *sp != -1; sp++) {
+
+ /* Add this cell as a sharer */
+ add2sharelist(s, *sp, dp);
+
+ s->rev.nnrev[*sp] = dp;
+ }
+
+ } else {
+ /* Put list in place for target nnrev[]*/
+ s->rev.nnrev[tx->ix] = dp;
+ }
+ }
+
+ DBG(("create_nnrev_list done, total fwd cells = %d\n",s->rev.nnrev[tx->ix][1]-3));
}
-#endif /* NEVER */
-/* ========================================================== */
-/* reverse lookup acceleration structure initialisation code */
-
-/* The reverse lookup relies on a search of the fwd interpolation tables. */
-/* To eliminate out of gamut points quickly, to provide a starting point for */
-/* the search, and to guarantee that all possible reverse solutions are discovered, */
-/* a spatial indexing structure is used to provide a list of starting candidate */
-/* forward indexes for a given output value. (rev.rev[]) */
-/* The reverse structure contains an fdi dimensional cell grid, each element of the */
-/* cell grid holding the indexes of the forward interpolation grid, which intersect */
-/* that ranges of output values. A reverse cell will be empty if there is no */
-/* potential exact solution. */
-/* Note that unlike the forward grid which is composed of verticies, */
-/* this grid is composed of cells (there is an extra row allocated */
-/* during construction using verticies, that are not used when converted. */
-/* to cells) */
-/* For accelleration of the nearest lookup, a parallel reverse grid is */
-/* constructed that holds lists of forward grid cells that may hold the */
-/* nearest point within the gamut. These lists may be empty if we are within */
-/* gamut - ie. the rev.nnrev[] array is the complement of the rev.rev[] array. */
-/* During construction of rev.nnrev[], it is initially filled with lists for */
-/* the potential nearest cell list for each vertex (hence the extra rows allocated */
-/* for rev[] and nnrev[]), and these are then merged down to form the list */
-/* for each cell extent. The nnrev[] array is filled using a seed fill algorithm, */
-/* starting from the edges of the filled cells in rev[]. */
-/* Since many of the cells map to the same surface region, many of the fwd cell lists */
-/* are shared. */
-
-/* NOTE: that the nnrev accuracy doesn't seem as good as fill_nncell() ! */
-/* Could we fix this with better geometry calculations ??? */
-
-/* rev.nnrev[] cache entry record */
-struct _nncache{
- int min[MXRO]; /* bwd vertex extent covered by this list */
- int max[MXRO];
- int *rip; /* Fwd cell list */
- struct _nncache *next; /* Link list for this cache key */
-}; typedef struct _nncache nncache;
-
-/* Structure to hold prime seed vertex information */
-struct _primevx{
- int ix; /* Index of prime seed */
- int gc[MXRO]; /* coordinate of the prime seed vertex */
- struct _primevx *next; /* Linked list for final conversion */
- int *clist; /* Cell list generated for prime cell */
-}; typedef struct _primevx primevx;
-
-/* Structure to hold temporary nn reverse vertex propogation information */
-struct _propvx{
- int ix; /* Index of this secondary seed */
- int gc[MXRO]; /* coordinate of this secondary seed */
- int cix; /* Index of the closest surface nnrev vertex */
- double dsq; /* Distance to the closest point squared */
- int pass; /* Propogation pass */
- struct _propvx *next; /* Linked list for next seeds */
-}; typedef struct _propvx propvx;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* This is the routine used to fill nnrev[] cells on demand, */
+/* because s->rev.fastsetup is set. */
+/* This is similar to the code used in the normal case, except */
+/* we search the rev[] bxcell list rather than use the bxcell surface list */
+static void fill_nncell(
+ rspl *s,
+ int *co, /* Integer coords of cell to be filled */
+ int ix /* Index of cell to be filled */
+) {
+ int f, fdi = s->fdi;
+ DCOUNT(gg, MXRO, fdi, 0, 0, s->rev.res); /* search seed coordinate */
+ int i, six = -1, nn[MXRO];
+ double bdist = 1e200;
+ bxcell *tx, *ss;
+ DCOUNT(cc, MXRO, fdi, -1, -1, 2); /* bwd neighborhood offset counter */
+ int nix; /* Neighbor offset index */
+ bxcell *xlist = NULL; /* Linked list of cells being searched */
+ bxcell *xlistend = NULL; /* Last item on xlist */
+ bxcell *tlist; /* Linked list of cells being considered as soln. */
+ double emax; /* Current smallest estimated max weigted distance */
+
+ DBG(("fill_nncell: (triggered on-demand)\n"));
+
+ /* Allocate the bxcell hash index */
+ create_surfhash(s);
+
+ /* Locate a starting search cell. */
+ /* We use a simple full search of rev[] for the cell */
+ /* closest to our target. */
+ DC_INIT(gg);
+ for (i = 0; i < s->rev.no; i++) {
+ if (s->rev.rev[i] != NULL) {
+ double dist;
+ for (dist = 0.0, f = 0; f < fdi; f++) {
+ double tt = co[f] - gg[f];
+ dist += tt * tt;
+ }
+ if (dist < bdist) {
+ bdist = dist;
+ six = i;
+ for (f = 0; f < fdi; f++)
+ nn[f] = gg[f];
+ }
+ }
+ DC_INC(gg);
+ }
+ if (six < 0)
+ error("fill_nncell: rev[] is empty");
+
+ /* Create search seed cell */
+ ss = new_bxcell(s, six, nn, NULL, 0.0, NULL);
+ add_bxcell_hash(s, ss);
+
+ /* Create a target cell */
+ tx = new_bxcell(s, ix, co, ss, 0.0, NULL);
+ add_bxcell_hash(s, tx);
+
+ DBG((" Target ix = %d, co[] %s\n",ix,debPiv(fdi, tx->gc)));
+ DBG((" Search start ix = %d, co[] %s\n",six,debPiv(fdi, ss->gc)));
+//printf(" Target ix = %d, co[] %s\n",ix,debPiv(fdi, tx->gc));
+//printf(" Search start ix = %d, co[] %s\n",six,debPiv(fdi, ss->gc));
+
+ emax = 1e200; /* Smallest emax */
+ ss->tix = tx->ix; /* Mark this cell as being in search list */
+
+ /* Make start cell the only entry in the search list */
+ ss->xlist = NULL;
+ xlist = ss;
+ xlistend = ss;
+
+ /* Clear the solution list */
+ tlist = NULL;
+
+ /* While there are cells to search for solutions */
+ while (xlist != NULL) {
+ double em, ex;
+
+ ss = xlist; /* Remove next search cell from linked list */
+ xlist = xlist->xlist;
+
+ /* Check if this cell could be in solution */
+ em = nn_grpgrp_est(s, &ex, &tx->g, &ss->g);
+ ss->emin = em;
+
+ DBG(("Searching rev[%d] co %s, em %f, ex %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, ex));
+//printf("Searching rev[%d] co %s, em %f, ex %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, ex);
+
+ if (em < emax) { /* Yes */
+
+ /* Add it to the solution list */
+ ss->tlist = tlist;
+ tlist = ss;
+
+ // copy rev[] list to ss->sl
+ copy_indexlist(s, &ss->sl, s->rev.rev[ss->ix]);
+
+ DBG(("Adding %d to solution list\n",ss->ix));
+//printf("Adding %d to to solution list\n",ss->ix);
+
+ /* Update smallest maximum */
+ /* (Will cull existing bxcell solutions with emin > emax later) */
+ if (ex < emax)
+ emax = ex;
+
+ /* Explore all neighbours, and add any surface cells that haven't been */
+ /* searched for this target yet. */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ bxcell *nbx;
+
+ nix = ss->ix;
+ for (f = 0; f < fdi; f++) {
+ nn[f] = ss->gc[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= s->rev.res)
+ break; /* Out of bounds */
+ nix += cc[f] * s->rev.coi[f];
+ }
+ if (f < fdi || nix == ss->ix) {
+//printf("Rejecting search neigbor co %s because out of bounds or current cell\n",debPiv(s->fdi,nn));
+ goto next_neighbor;
+ }
+
+ /* Can only search within filled rev[] cells */
+ if (s->rev.rev[nix] == NULL) {
+ goto next_neighbor;
+ }
+
+ /* If neighbor is in bounds, and a surface bxcell*/
+ {
+ /* Make sure we have a bxcell for the neighbor */
+ if ((nbx = get_surface_bxcell(s, nix)) == NULL) {
+ nbx = new_bxcell(s, nix, nn, NULL, 0.0, NULL);
+ add_bxcell_hash(s, nbx);
+ }
+
+ /* If not already in search list */
+ if (nbx->tix != tx->ix) {
+// DBG(("Adding search neigbor nnrev[%d] co %s to search list\n",nbx->ix, debPiv(s->fdi, nbx->gc)));
+//printf("Adding search neigbor nnrev[%d] co %s to search list\n",nbx->ix, debPiv(s->fdi, nbx->gc));
+ /* Add neigbor to end of search list */
+ nbx->tix = tx->ix; /* Is now in search list */
+ nbx->xlist = NULL;
+ if (xlist == NULL)
+ xlist = nbx;
+ else
+ xlistend->xlist = nbx;
+ xlistend = nbx;
+ }
+//else
+//printf("Rejecting search neigbor nnrev[%d] co %s because already in list\n",nbx->ix, debPiv(s->fdi, nbx->gc));
+ }
+ next_neighbor:;
+ DC_INC(cc);
+ }
+ }
+//else
+//printf("Rejected rev[%d] co %s, because em %f >= emax %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, emax);
+ }
+
+ if (tlist == NULL)
+ error("fill_nncell: search for rev[] cells failed");
+
+//printf("Got solution list, filling in nnrev[] cell\n");
+ /* Create the nnrev[] list from the candidate bxcell solutions */
+ create_nnrev_list(s, tx, tlist, emax);
+
+//printf("nnrev[%d] list length = %d\n",tx->ix,s->rev.nnrev[tx->ix][1]-3);
+
+ /* Free up bxcell hash index and all bxcell's we've created */
+ free_surfhash(s, 1);
+
+ DBG(("fill_nncell done\n"));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Associated sub-simplex tables. For a given base vertex with a given fwd */
+/* access flags FLV(), create is a pointer to a list of sub-simplex verticies */
+/* offset from the base vertex that the base vertex is part of, in all possible */
+/* directions. We do all possible directions to make the bxcell triangle */
+/* search symetrical, and searching triangles using points outside */
+/* the bxcell list seems to actually speed it up (by culling more effectively) */
+
+typedef struct {
+ int pos; /* nz if this is a ssimplex that is only in the +ve direction */
+ int ee;
+ int goffs[MXDI+1]; /* Offsets to sub-simplex verticies within grid in simplex order. */
+} assinfo;
+
+typedef struct {
+ int sdi; /* Sub dimensionality */
+ int no; /* Number of sub-simplexes in list */
+ assinfo *ti; /* Per sub-simplex info array */
+} assdire;
+
+#if (FL_BITS != 3)
+#error FL_BITS is not 3!
+#endif
+
+/* init the triangle/edge directory list and related assinfo tables */
+/* sdi = 2 for triangles, 1 for edges */
+static void init_assdir(rspl *s, assdire **passdir, int sdi) {
+ assdire *assdir;
+ int i, j, k;
+ int e, ee, di = s->di;
+ int dirsize;
+ DCOUNT(cc, MXRI, di, -1, -1, 2); /* Clipping values for each dim */
+ ssxinfo *xip; /* Pointer to sub-simplex info structure */
+
+ DBG(("init_assdir called, di = %d\n",di));
+
+ dirsize = (1 << (FL_BITS * di));
+
+ if ((assdir = (assdire *) rev_calloc(s, dirsize, sizeof(assdire))) == NULL)
+ error("rspl malloc failed - assdir");
+ INCSZ(s, dirsize * sizeof(assdire));
+
+ assdir->sdi = sdi;
+ xip = &s->rev.sspxi[sdi];
+
+#ifdef NEVER
+ printf("simplex dim %d:\n",xip->sdi);
+ for (i = 0; i < xip->nospx; i++) {
+ printf("offs = %s\n", debPiv(sdi+1, xip->spxi[i].offs));
+ printf("goffs = %s\n", debPiv(sdi+1, xip->spxi[i].goffs));
+ }
+#endif
+
+ /* For each possible clip combination */
+ /* (where < 0 == clipping lower edge, > 0 == clipping upper edge */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ int trilaloc, trillen;
+ assinfo *trilist;
+
+ /* Start a new table, allocate the maximum possible number of entries. */
+ trilaloc = (1 << di) * xip->nospx;
+ if ((trilist = (assinfo *) rev_calloc(s, trilaloc, sizeof(assinfo))) == NULL)
+ error("rspl malloc failed - trilist");
+ INCSZ(s, trilaloc * sizeof(assinfo));
+ trillen = 0;
+
+ /* For all cube directions from base, 0 = +ve, 1 = -ve */
+ for (ee = 0; ee < (1<<di); ee++) {
+
+ /* For all the sub-simplexes in a cube */
+ for (i = 0; i < xip->nospx; i++) {
+ int gotbase = 0;
+
+ /* Offset the sub-simplex by the direction, and check that the */
+ /* base vertex is part of it. */
+ trilist[trillen].ee = ee;
+ trilist[trillen].pos = (ee == 0);
+ for (j = 0; j < (sdi+1); j++) {
+ trilist[trillen].goffs[j] = xip->spxi[i].goffs[j] - s->g.hi[ee];
+ if (trilist[trillen].goffs[j] == 0) /* Base vertex is present */
+ gotbase = 1;
+ }
+ if (!gotbase) {
+ continue;
+ }
+
+ /* See if the direction of each vertex of the sub-simplex is */
+ /* compatible with the clipping. */
+ for (j = 0; j < (sdi+1); j++) {
+ for (e = 0; e < di; e++) {
+ if (xip->spxi[i].offs[j] & (1<<e)) {
+
+ if ((cc[e] < 0 && (ee & (1<<e)) != 0)
+ || (cc[e] > 0 && (ee & (1<<e)) == 0)) {
+ break; /* not compatible */
+ }
+ }
+ }
+ if (e < di) { /* Not compatible */
+ break;
+ }
+ }
+ if (j < (sdi+1)) {
+ continue; /* Not compatible */
+ }
+
+ /* We end up with aliases due to the sspxi having all */
+ /* sub-simplexes within a cube, so see if we already */
+ /* created this one. */
+ for (k = 0; k < trillen; k++) {
+ for (j = 0; j < (sdi+1); j++) {
+ if (trilist[k].goffs[j] != trilist[trillen].goffs[j])
+ break;
+ }
+ if (j >= (sdi+1))
+ break; /* Redundant - don't add this point */
+ }
+ if (k < trillen) {
+ continue; /* Skip redundant combination */
+ }
+
+//printf(" Clip %s off %d tri %d goffs = %s\n", debPiv(di, cc), ee, trillen, debPiv(sdi+1, trilist[trillen].goffs));
+ trillen++;
+ }
+ }
+
+//printf("Got %d triangles for cc %s\n", trillen, debPiv(di, cc));
+
+ /* Add table to all matching combination of FLV() */
+ for (i = 0; i < dirsize; i++) {
+ for (e = 0; e < di; e++) {
+ int fl = (i >> (3 * e)) & 7;
+ if (! /* NOT: */
+ ((cc[e] > 0 && fl == 0) /* Top edge clip and on top edge */
+ || (cc[e] < 0 && fl == 4) /* Bottom edge clip and on bottom edge */
+ || (cc[e] == 0 && fl != 0 && fl != 4))) /* No clipping and in middle */
+ break; /* Not a match */
+ }
+ if (e >= di) { /* Table matches this FLV() */
+ assdir[i].no = trillen;
+ assdir[i].ti = trilist;
+ }
+ }
+ DC_INC(cc); /* Next clip combination */
+ }
+
+#ifdef NEVER
+ /* Check that there is a list for every flag value */
+ for (i = 0; i < dirsize; i++) {
+ if (assdir[i].no == 0)
+ error("init_assdir has fl %d entry with no sub-simplexes",i);
+ else
+ printf("fl %d has %d triangles\n",i,assdir[i].no);
+ }
+#endif
+
+ *passdir = assdir;
+}
+
+static void free_assdir(rspl *s, assdire *assdir) {
+ int i, j;
+ int e, ee, di = s->di;
+ int sdi = assdir->sdi;
+ int dirsize = (1 << (FL_BITS * di));
+ int trilaloc = (1 << di) * s->rev.sspxi[sdi].nospx;
+
+ for (i = 0; i < dirsize; i++) {
+ assinfo *trilist;
+
+ if ((trilist = assdir[i].ti) == NULL)
+ continue;
+
+ /* Free all aliases of list */
+ for (j = i; j < dirsize; j++) {
+ if (trilist == assdir[j].ti) {
+ assdir[j].ti = NULL;
+ }
+ }
+ free(trilist);
+ DECSZ(s, trilaloc * sizeof(assinfo));
+ }
+ free(assdir);
+ DECSZ(s, dirsize * sizeof(assdire));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Solve the 2x2 simultaneous linear equations A.X = B */
+static int solve_se_2x2(double **ta, double *tb) {
+ double b[2] = { tb[0], tb[1] };
+ double det;
+ int rv;
+
+ det = (ta[0][0] * ta[1][1] - ta[0][1] * ta[1][0]);
+
+ if (fabs(det) < 1e-20)
+ return 1;
+
+ det = 1.0/det;
+ tb[0] = det * ( ta[1][1] * b[0] - ta[0][1] * b[1]);
+ tb[1] = det * (-ta[1][0] * b[0] + ta[0][0] * b[1]);
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef CHECK_NNLU
+
+/* Debug code */
+
+static int debug(int ix) {
+ if (
+ (ix == 619 || ix == 618 || ix == 329)
+ || (ix == 330 || ix == 329 || ix == 312)
+ || (ix == 619 || ix == 329 || ix == 312)
+ || (ix == 329 || ix == 312 || ix == 23)
+ || (ix == 329 || ix == 23 || ix == 22)
+ || (ix == 40 || ix == 23 || ix == 22)
+ )
+ return 1;
+ return 0;
+}
+
+static int debug2(int *ix) {
+ if (
+ (ix[0] == 619 && ix[1] == 618 && ix[2] == 329)
+ || (ix[0] == 330 && ix[1] == 329 && ix[2] == 312)
+ || (ix[0] == 619 && ix[1] == 329 && ix[2] == 312)
+ || (ix[0] == 329 && ix[1] == 312 && ix[2] == 23)
+ || (ix[0] == 329 && ix[1] == 23 && ix[2] == 22)
+ || (ix[0] == 40 && ix[1] == 23 && ix[2] == 22)
+ )
+ return 1;
+ return 0;
+}
+
+#endif /* CHECK_NNLU */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#ifdef REVVRML /* Plotting routine declarations */
+
+static void plot_bxfwcells(rspl *s, int dobxcells, int dofwcells, int dofwlabels);
+
+static void plot_tri_check(rspl *s, int dobxcells, int dowait, bxcell *bx, int vtxix,
+ int trii, int triix[3], int nvtxix, int sorv, int wsrv, int shdwd,
+ double v[MXRI+1][MXRO], double de[MXRO], double pv[MXRO], double xv[MXRO]);
+
+static void plot_vtx_surface(rspl *s, int dovtxlabels, int dodeleted, int doadded,
+ int dopres, int dooil, int dobxcells, int dowait, vtxcache *vc, assdire *edgdir);
+
+static void plot_touched_bxcells(rspl *s, int bxix);
+
+static void plot_fxcell_surface(rspl *s, int dofclabels, int dobxcells, int dowait);
+
+#endif /* REVVRML */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ The basic strategy to thin the gamut surface as much as possible
+ to reduce the nnrev[] list size for best memory consuption and
+ rev lookup speed relies on being able to decide if a vertx
+ is inside or on the surface of the gamut. A simple and definitive
+ topological rule hasn't been forthcoming, so a simpler heursitic
+ of visiblilty from a singe internal "focal" point is currently used.
+ calc_ocent() attempts to choose a point with best visibilit of
+ all the gamut surfaces, since any self-shadowing results in
+ gamut surface holes.
+
+ Improvements would be to create per-axis mappings (and separate
+ the shadow vertex locations from the real ones) to re-shape
+ the gamut into a squere as much as possible.
+ Multiple external focal points could be used,
+ a vertex being shadowed only when it can't be "seen" by any
+ external focal point. It's hard to figure how to make the latter
+ fast enough to be useful though, unless some breakthrough
+ in the algorithm or spatial data structure can be developed.
+
+ The code is still slower than desired. A possible avenue for
+ improving the thinning would be to add an explicit triangle
+ structure (similar to gamut ?), add a suitable spatial
+ accelleration structure for shadow testing (BSP tree ??),
+ and build the gamut surface incrementally from existing
+ furthest points.
+*/
+
+/* Struct to slice locus points */
+struct _slpoint {
+ double v[MXRO]; /* Point location */
+ double rad; /* Distance from ccent */
+
+ double cvec[MXRO]; /* Vector from this point to ccent */
+ double len; /* Length of segment, -1 if no good */
+ double trad; /* Trial center point to this v[] radius */
+}; typedef struct _slpoint slpoint;
+
+/* Center finding context */
+struct _ocenctx {
+ rspl *s;
+ int ares; /* angle resolution */
+ slpoint *p[MXRO]; /* Slice locus points */
+ double ccent[MXRO]; /* Construction center point */
+ int debug;
+}; typedef struct _ocenctx ocenctx;
+
+/* Given a set of slice locus points and a proposed center point, */
+/* compute the weighted averag of the orthogonality of the point */
+/* to each locus line segment. (smaller is better) */
+static double aorthog(void *_ctx, double *cent) {
+ ocenctx *ctx = (ocenctx *) _ctx;
+ rspl *s = ctx->s;
+ int f, ff, fdi = s->fdi;
+ int aa, ares = ctx->ares;
+ double tcent[MXRO];
+ double ang, aang = 0.0;
+ int naang = 0;
+
+ if (ctx->debug) printf("aorthog called with cent %s\n",debPdv(fdi,cent));
+
+ for (ff = 0; ff < fdi; ff++) {
+ if (ctx->debug) printf(" Axis %d\n",ff);
+
+ for (f = 0; f < fdi; f++)
+ tcent[f] = cent[f];
+ /* Flatten the points to lie on the notional center */
+ tcent[ff] = ctx->ccent[ff];
+
+ for (aa = 0; aa < ares; aa++) {
+ double trad, nrad;
+ double cvec[MXRO], dot;
+
+ if (ctx->p[ff][aa].len < 0.0)
+ continue;
+
+ if (aa == 0) {
+ /* Compute normalize vector from cent to locus to this point */
+ trad = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = tcent[f] - ctx->p[ff][aa].v[f];
+ trad += tt * tt;
+ }
+ trad = sqrt(trad);
+ } else { /* Was computed by previous */
+ trad = ctx->p[ff][aa].trad;
+ }
+
+ /* Compute normalize vector from tcent to locus to next point */
+ nrad = 0.0;
+ for (f = 0; f < fdi; f++) {
+ cvec[f] = tcent[f] - ctx->p[ff][aa+1].v[f];
+ nrad += cvec[f] * cvec[f];
+ }
+ nrad = ctx->p[ff][aa+1].trad = sqrt(nrad);
+
+ /* Normalized difference in distance over length */
+ /* Compute dot product of cv and segment vector */
+ ang = fabs(trad - nrad)/ctx->p[ff][aa].len;
+
+ if (ctx->debug) printf(" aa %d: trad %f nrad %f, diff %f, len %f, ang %f\n",aa,trad,nrad,fabs(trad - nrad),ctx->p[ff][aa].len,ang);
+
+ /* Compute dot of next point vector from trial center */
+ /* with vector from construction center, to detect */
+ /* if the trial has wandered outside of gamut. */
+ dot = 0.0;
+ for (f = 0; f < fdi; f++)
+ dot += cvec[f] * ctx->p[ff][aa+1].cvec[f];
+
+ if (dot < 0.0) {
+ if (ctx->debug) printf(" dot is %f\n",dot);
+ ang = 50.0; /* Big value */
+ } else {
+ ang = pow(ang, 50.0); /* Weight high angles */
+ }
+
+ if (ang > aang)
+ aang = ang;
+
+ aang += ang;
+ naang++;
+ }
+ }
+ aang /= (double)naang;
+
+ if (ctx->debug) printf(" returning %f\n",aang);
+
+ return aang;
+}
+
+/* Determine a gamut center point, for surface triangle shadow testing. */
+/* We assume that rev[] has been setup. */
+/* The idea is to locate a point that best "sees" all internal */
+/* surface of the gamut. */
+static void calc_ocent(rspl *s) {
+ int i, j, aa, mm;
+ int f, ff, fdi = s->fdi;
+ int rgres = s->rev.res; /* number of bwd cells */
+ double minmax[2][MXRO][MXRO]; /* Range min/max points for each axis */
+ float *gp, *ep;
+ int midix[MXDO]; /* Middle rev[] index */
+ double mid[MXRO]; /* Middle of midix[] */
+ double ss[MXRO];
+ ocenctx ctx; /* Context */
+ double atanscale;
+
+ /* Scan the forward array for the min and max points of each axis */
+ for (f = 0; f < fdi; f++) {
+ minmax[0][f][f] = 1e30;
+ minmax[1][f][f] = -1e30;
+ }
+
+ /* Scan the Grid for min/max values */
+ for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
+ for (ff = 0; ff < fdi; ff++) {
+ if (minmax[0][ff][ff] > gp[ff]) {
+ for (f = 0; f < fdi; f++)
+ minmax[0][ff][f]= gp[f];
+ }
+ if (minmax[1][ff][ff] < gp[ff]) {
+ for (f = 0; f < fdi; f++)
+ minmax[1][ff][f] = gp[f];
+ }
+ }
+ }
+
+ if (fdi == 1) {
+ for (f = 0; f < fdi; f++)
+ s->rev.ocent[f] = 0.5 * (minmax[0][0][f] + minmax[1][0][f]);
+ DBG(("calc_ocent: got ocent = %s\n",debPdv(fdi,s->rev.ocent)));
+ return;
+ }
+
+ /* Aprox. mid point of gamut from average of min/max points */
+ for (f = 0; f < fdi; f++)
+ ctx.ccent[f] = 0.0;
+ for (ff = 0; ff < fdi; ff++) {
+ for (f = 0; f < fdi; f++) {
+ if (f == ff)
+ continue;
+ for (mm = 0; mm < 2; mm++)
+ ctx.ccent[f] += minmax[mm][ff][f];
+ }
+ }
+ for (f = 0; f < fdi; f++)
+ s->rev.ocent[f] = ctx.ccent[f] /= ((fdi-1) * 2.0);
+
+ DBG(("calc_ocent: ccent = %s\n",debPdv(fdi,ctx.ccent)));
+
+ /* If it's all to hard ... */
+ if (fdi != 3) {
+ return;
+ }
+
+ /* Index of data mid point in rev[] grid */
+ for (f = 0; f < fdi; f++) {
+ midix[f] = (int)((ctx.ccent[f] - s->rev.gl[f])/s->rev.gw[f] + 0.5);
+ mid[f] = (midix[f]+0.5) * s->rev.gw[f] + s->rev.gl[f];
+ }
+
+ /* Array for each slice values at angle (+ repeat at end) */
+ ctx.debug = 0;
+ ctx.s = s;
+ ctx.ares = 20;
+ atanscale = ctx.ares/(2.0 * DBL_PI);
+ for (ff = 0; ff < fdi; ff++) {
+ if ((ctx.p[ff] = (slpoint *)rev_calloc(s, ctx.ares+1,sizeof(slpoint))) == NULL)
+ error("rspl malloc failed - calc_ocent arrays");
+ INCSZ(s, (ctx.ares+1) * sizeof(slpoint));
+ }
+
+//printf("~1 locating center point\n");
+
+ /* Set initial radius values */
+ for (aa = 0; aa < ctx.ares; aa++) {
+ for (ff = 0; ff < fdi; ff++)
+ ctx.p[ff][aa].rad = -1.0;
+ }
+
+ /* Take three slices through the rev[] array, plotting */
+ /* the maximum circumference for the slice */
+
+ /* For the axis we're slicing */
+ for (ff = 0; ff < fdi; ff++) {
+ DCOUNT(cc, MXRO, 2, 0, 0, rgres); /* Counter through bwd cells */
+ double vv[MXRO];
+ int aa;
+
+//printf(" slice axis %d\n",ff);
+
+ /* Scan this slice of rev[] */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ int ix;
+ int slix[2]; /* Indexes in slice direction */
+ int *rp;
+ int co[MXRO];
+
+ /* Compute bx index */
+ ix = 0;
+ for (j = f = 0; f < fdi; f++) {
+ if (f == ff)
+ co[f] = midix[f];
+ else {
+ slix[j] = f;
+ co[f] = cc[j++];
+ }
+ ix += co[f] * s->rev.coi[f];
+ }
+//printf(" bx %d, %d ix %d\n",co[0],co[1],co[2],ix);
+
+ if (s->rev.rev[ix] == NULL) {
+//printf(" rev is empty\n");
+ goto next_bx;
+ }
+
+ /* For all the vertex values in bx rev[] */
+ for (rp = s->rev.rev[ix]+3; *rp != -1; rp++) {
+ float *fcb = s->g.a + *rp * s->g.pss;
+ double x, y, rad, ang;
+
+//printf(" vtx %d\n",*rp);
+ /* Ignore over ink limit values */
+ if (s->limiten && fcb[-1] > s->limitv)
+ continue;
+
+ /* Compute radius and normalize */
+ x = fcb[slix[0]] - ctx.ccent[slix[0]];
+ y = fcb[slix[1]] - ctx.ccent[slix[1]];
+ rad = sqrt(x * x + y * y);
+ if (rad < EPS)
+ continue;
+
+ /* Quantized angle this point is at */
+ ang = atanscale * atan2(y, x);
+ aa = (int)floor(ang);
+ if (aa < 0)
+ aa += ctx.ares;
+ if (aa >= ctx.ares)
+ aa -= ctx.ares;
+
+//printf(" slice %d vtx %f %f %f rad %f, ang %f aa %d\n", ff, fcb[0], fcb[1], fcb[2], rad, ang, aa);
+
+ if (rad > ctx.p[ff][aa].rad) {
+ ctx.p[ff][aa].rad = rad;
+
+ for (f = 0; f < fdi; f++) {
+ ctx.p[ff][aa].v[f] = fcb[f];
+ }
+ /* Flatten the points to lie on the notional center */
+ ctx.p[ff][aa].v[ff] = ctx.ccent[ff];
+ }
+ }
+ next_bx:;
+ DC_INC(cc);
+ }
+
+ /* Repeat first in extra at end */
+ ctx.p[ff][ctx.ares] = ctx.p[ff][0]; /* Structure copy */
+ }
+
+ /* Pre-compute point to point info to speed optimization */
+ for (ff = 0; ff < fdi; ff++) {
+ for (aa = 0; aa < ctx.ares; aa++) {
+ double len = 0.0;
+
+ for (f = 0; f < fdi; f++)
+ ctx.p[ff][aa+1].cvec[f] = ctx.ccent[f] - ctx.p[ff][aa].v[f];
+
+ for (f = 0; f < fdi; f++) {
+ double tt = ctx.p[ff][aa+1].v[f] - ctx.p[ff][aa].v[f];
+ len += tt * tt;
+ }
+ if (len < EPS)
+ ctx.p[ff][aa].len = -1.0;
+ else
+ ctx.p[ff][aa].len = sqrt(len);
+ }
+ }
+
+ /* Locate center point that maximised the orthogonallity to each */
+ /* slice segment. This should maximize visibility of the inner of the */
+ /* gamut surface, for shadow testing. */
+ for (f = 0; f < fdi; f++)
+ ss[f] = 5.0;
+
+ /* return 0 on sucess, 1 on failure due to excessive itterations */
+ if (powell(NULL, fdi, s->rev.ocent, ss, 1e-3, 500, aorthog, (void *)&ctx, NULL, NULL)) {
+ printf("calc_ocent powell failed\n");
+ for (f = 0; f < fdi; f++)
+ s->rev.ocent[f] = ctx.ccent[f];
+ }
+
+// ctx.debug = 1;
+// printf("Final angle = %f\n", aorthog(&ctx, ctx.ccent));
+
+#ifdef REVVRML /* Plotting routine declarations */
+ /* Diagnostic - dump the gamut slice locii */
+ {
+ vrml *wrl;
+ double grey[3] = { 0.5, 0.5, 0.5 };
+ double white[3] = { 1.0, 1.0, 1.0 };
+ double red[3] = { 0.8, 0.1, 0.1 };
+ double green[3] = { 0.1, 1.0, 0.1 };
+ double blue[3] = { 0.1, 0.1, 0.8 };
+ double *rgb[3] = { red, green, blue };
+
+ wrl = new_vrml("section", 0, vrml_lab);
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ /* Show vertex labels */
+ for (ff = 0; ff < fdi; ff++) {
+ char index[100];
+
+ for (aa = 0; aa < ctx.ares; aa++) {
+ if (ctx.p[ff][aa].rad > 0) {
+ sprintf(index, "%d:%d",ff,aa);
+ wrl->add_text(wrl, index, ctx.p[ff][aa].v, white, 1.0);
+ }
+ }
+ }
+
+ /* Axis we're slicing */
+ for (ff = 0; ff < fdi; ff++) {
+ int vix[100];
+
+ for (aa = 0; aa < ctx.ares; aa++) {
+ if (ctx.p[ff][aa].rad > 0)
+ vix[aa] = wrl->add_vertex(wrl, 0, ctx.p[ff][aa].v);
+ }
+ vix[aa] = vix[0];
+
+ for (aa = 0; aa < ctx.ares; aa++) {
+ if (ctx.p[ff][aa].rad > 0
+ && ctx.p[ff][aa+1].rad > 0)
+ wrl->add_col_line(wrl, 0, vix + aa, rgb[ff]);
+ }
+ }
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+ }
+#endif /* REVVRML */
+
+ /* Free up the context data */
+ for (ff = 0; ff < fdi; ff++) {
+ free(ctx.p[ff]);
+ DECSZ(s, (ctx.ares+1) * sizeof(slpoint));
+ }
+
+ DBG(("calc_ocent: got ocent = %s\n",debPdv(fdi,s->rev.ocent)));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Initialise the rev Second section acceleration information. */
/* This is called when it is discovered on a call that s->rev.rev_valid == 0 */
static void init_revaccell(
@@ -5132,39 +8787,101 @@ rspl *s
int fdi = s->fdi;
int gno = s->g.no;
int rgno = s->rev.no;
- int argres = s->rev.ares; /* Allocation rgres, = no bwd cells +1 */
- int rgres = s->rev.res; /* no bwd cells */
+ int rgres = s->rev.res; /* number of bwd cells */
int rgres_1 = rgres-1; /* rgres -1 == maximum base coord value */
schbase *b = s->rev.sb; /* Base search information */
- char *vflag = NULL; /* Per vertex flag used during construction of nnrev */
+ char *vflag = NULL; /* Per bwd vertex flag used during construction of nnrev */
+ /* 0 nnrev[] cell empty, not surface */
+ /* 1 nnrev[] done/don't fill, not surface */
+ /* 2 nnrev[] cell empty, on surface */
+ /* 3 nnrev[] done, on surface */
+ /* 1X nnrev[] contains ink limited fwcells */
+ /* Note that bit 1 can be set for cells that are not */
+ /* to be explored because they are in the gamut interior, */
+ /* and because they have already been added to the seedlist. */
float *gp; /* Pointer to fwd grid points */
- primevx *plist = NULL, *ptail = NULL; /* Prime seed list for last pass */
- propvx *alist = NULL; /* Linked list of active secondary seeds */
- propvx *nlist = NULL; /* Linked list of next secondary seeds */
- DCOUNT(gg, MXRO, fdi, 0, 0, argres);/* Track the prime seed coordinate */
- DCOUNT(cc, MXRO, fdi, -1, -1, 2); /* Neighborhood offset counter */
- int nn[MXRO]; /* Neighbor coordinate */
- int pass = 0; /* Propogation pass */
- int nncsize; /* Size of the rev.nnrev construction cache index */
- nncache **nnc; /* nn cache index, used during construction of nnrev */
- unsigned hashk; /* Hash key */
- nncache *ncp; /* Hash entry pointer */
- int nskcells = 0; /* Number of skiped cells (debug) */
-#ifdef DEBUG
- int cellinrevlist = 0;
- int fwdcells = 0;
+
+ DCOUNT(gg, MXRO, fdi, 0, 0, rgres); /* Track the prime seed coordinate */
+ int nn[MXRO]; /* bwd neighbor coordinate */
+
+ vtxcache vc; /* List + cache of vertexes being processed */
+ tricache tc; /* cache of surface triangles that have been processed */
+ tricache stc; /* small cache of surface triangles that have been processed */
+ bxcell *bx, *nbx, **pbx;
+ bxcell *xlist = NULL; /* Linked list of added surface bxcells */
+ assdire *tridir = NULL; /* Triangle tables */
+ assdire *edgdir = NULL; /* Edge tables */
+ double **cla = NULL; /* Line LHS implicit equation matrix [fdi][fdi+1] */
+ double *ta[MXRO], TA[MXRO][MXRO]; /* temp for intersection solving */
+
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ /* Some statistics */
+ unsigned long smsec;
+ int nskcells = 0; /* Number of skipped cells because over ink limit (debug) */
+ int nascells = 0; /* Number of added surface cells */
+ int nrscells = 0; /* Number of removed surface cells */
+ int naoulvtxs = 0; /* Number of added over ink limit vertexes */
+ int revcells = 0; /* Non-empty rev[] cells */
+ int revcelldepth = 0; /* Sum of rev[] list lengths */
+ int ingamutcells = 0; /* No of rev[] cells not on surface */
+ int surfcells = 0; /* No. surface cells */
+ int emptycells = 0; /* No. empty cells */
+ int nnrevcells = 0; /* Non-empty nnrev[] cells */
+ int nnrevcellsearch = 0; /* Sum of number of surface cells searched */
+ int nnsinglefill = 0; /* Number of nnrev[] cells seeded singly */
+ int nnsuperfill = 0; /* Number of nnrev[] cells seeded using supercell */
+ int nnrevcelldepth = 0; /* Sum of nnrev[] list lengths */
+ int nnmxrevcelldepth = 0; /* Maximum nnrev[] list lengths */
+ int nnrevshare = 0; /* Sum of nnrev[] list reference counts */
#endif
DBG(("init_revaccell called, di = %d, fdi = %d, mgres = %d\n",di,fdi,(int)s->g.mres));
- if (!s->rev.fastsetup) {
- /* Temporary per bwd vertex/cell flag */
- if ((vflag = (char *) calloc(rgno, sizeof(char))) == NULL)
- error("rspl malloc failed - rev.vflag points");
- INCSZ(s, rgno * sizeof(char));
+ if (fdi > 1 && s->verbose)
+ fprintf(stdout, "%cInitializing nnrev arrays...\n",cr_char);
+
+ /* Add this instance into memory management */
+ if (s->rev.rev_valid == 0 && di > 1) {
+ rev_struct *rsi;
+ size_t ram_portion = g_avail_ram;
+
+ /* Add into linked list */
+ s->rev.next = g_rev_instances;
+ g_rev_instances = &s->rev;
+
+ /* Aportion the memory, and reduce cache if it is over new limit. */
+ g_no_rev_cache_instances++;
+ ram_portion /= g_no_rev_cache_instances;
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next) {
+ revcache *rc = rsi->cache;
+
+ rsi->max_sz = ram_portion;
+ while (rc->nunlocked > 0 && rsi->sz > rsi->max_sz) {
+ if (decrease_revcache(rc) == 0)
+ break;
+ }
+//printf("~1 rev instance ram = %d MB\n",rsi->sz/1000000);
+ }
+
+ if (s->verbose)
+ fprintf(stdout, "%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
+ cr_char,
+ g_no_rev_cache_instances > 1 ? "are" : "is",
+ g_no_rev_cache_instances,
+ g_no_rev_cache_instances > 1 ? "s" : "",
+ (unsigned long)ram_portion/1000000);
}
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ smsec = msec_time();
+#endif
+
+ /* Temporary per bwd vertex/cell flag for nn setup */
+ if ((vflag = (char *) rev_calloc(s, rgno, sizeof(char))) == NULL)
+ error("rspl malloc failed - rev.vflag points");
+ INCSZ(s, rgno * sizeof(char));
+
/*
* The rev[] and nnrev[] grids contain pointers to lists of grid cube base indexes.
* If the pointer is NULL, then there are no base indexes in that list.
@@ -5196,44 +8913,64 @@ rspl *s
}
/* We then fill in the in-gamut reverse grid lookups, */
- /* and identify nnrev prime seed verticies */
+ /* and identify nnrev prime seed verticies to put in the surface bxcells. */
DBG(("filling in rev.rev[] grid\n"));
/* To create rev.rev[], for all fwd grid points, form the cube with that */
/* point at its base, and determine the bounding box of the output values */
- /* that could intersect that cube. */
- /* As a start for creating rev.nnrevp[], flag which bwd verticies are */
- /* covered by the fwd grid output range. */
+ /* that could intersect that fwd cube. Add that fwd index to the lists of */
+ /* of all bwd cells that the bounding box intersects. */
+ /* As a start for creating surface bxcell list, flag which bwd verticies */
+ /* are covered by the fwd grid output range. */
+
+ /* Pre-marking device edge rev cells creates many more initial cells, */
+ /* but avoids having to discover them with multiple passes ? */
for (gp = s->g.a, i = 0; i < gno; gp += s->g.pss, i++) {
datao min, max;
int imin[MXRO], imax[MXRO], gc[MXRO];
+ int edge = 0; /* This fwd cell contains a device edge */
int uil; /* One is under the ink limit */
+ int oil; /* One is over the ink limit */
//printf("~1 i = %d/%d\n",i,gno);
/* Skip grid points on the upper edge of the grid, since there */
/* is no further grid point to form a cube range with. */
for (e = 0; e < di; e++) {
- if(G_FL(gp, e) == 0) /* At the top edge */
+ int flags = G_FL(gp, e);
+
+ if (flags == 0) /* At the top edge */
break;
+
+ /* If we at the bottom edge, or one away from top edge */
+ if (flags == 4 || flags == 1)
+ edge = 1; /* This fwd cell is on device gamut edge */
}
if (e < di) { /* Top edge - skip this cube */
+//printf("~1 skipping base vertex %d on top edge\n",i);
continue;
}
+//printf("~1 adding to rev[]\n");
+
/* Find the output value bounding box values for this grid cell */
- uil = 0;
+ /* Start with base vertex */
+ uil = oil = 0;
for (f = 0; f < fdi; f++) /* Init output min/max */
min[f] = max[f] = gp[f];
- if (b == NULL || !s->limiten || gp[-1] <= s->limitv)
+ if (!s->limiten || gp[-1] <= s->limitv)
uil = 1;
+ else
+ edge = oil = 1; /* May be stradling ink limit edge */
- /* For all other grid points in the cube */
+ /* Then add all other fwd cube verticies */
for (ee = 1; ee < (1 << di); ee++) {
float *gt = gp + s->g.fhi[ee]; /* Pointer to cube vertex */
- if (b == NULL || !s->limiten || gt[-1] <= s->limitv)
+ if (!s->limiten || gt[-1] <= s->limitv)
uil = 1;
+ else
+ edge = oil = 1;
/* Update bounding box for this grid point */
for (f = 0; f < fdi; f++) {
@@ -5244,26 +8981,28 @@ rspl *s
}
}
- /* Skip any fwd cells that are over the ink limit */
+ /* Skip any fwd cells that have every vertex over the ink limit */
if (!uil) {
+#if defined(REVTABLESTATS) || defined(DEBUG)
nskcells++;
+#endif
continue;
}
- /* Figure out intersection range in reverse grid */
+ /* Figure out intersection range in bwd cell grid */
for (f = 0; f < fdi; f++) {
double t;
int mi;
double gw = s->rev.gw[f];
double gl = s->rev.gl[f];
- t = (min[f] - gl)/gw;
+ t = (min[f] - gl - EPS)/gw;
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
else if (mi > rgres_1)
mi = rgres_1;
imin[f] = mi;
- t = (max[f] - gl)/gw;
+ t = (max[f] - gl + EPS)/gw;
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
@@ -5272,51 +9011,51 @@ rspl *s
imax[f] = mi;
}
-//printf("Scanning over grid:\n");
+//printf(" Scanning over bwd cell range grid:\n");
//for (f = 0; f < fdi; f++)
-//printf("Min[%d] = %d -> Max[%d] = %d\n",f,imin[f],f,imax[f]);
+//printf(" Min[%d] = %d -> Max[%d] = %d\n",f,imin[f],f,imax[f]);
/* Now create forward index and vector with all the reverse grid cells */
for (f = 0; f < fdi; f++)
gc[f] = imin[f]; /* init coords */
- for (f = 0; f < fdi;) { /* For all of intersect cube */
- int **rpp, *rp;
+ /* Until increment at bottom carries */
+ for (f = 0; f < fdi;) { /* For all of intersect bwd cube */
+ int **rpp;
+ char *vflagp;
- /* Compute pointer to grid cell */
- for (rpp = s->rev.rev, f = 0; f < fdi; f++)
- rpp += gc[f] * s->rev.coi[f];
- rp = *rpp;
+ /* Compute pointer to bwd grid cell and vflag[] */
+ for (rpp = s->rev.rev, vflagp = vflag, f = 0; f < fdi; f++) {
+ int inc = gc[f] * s->rev.coi[f];
+ rpp += inc;
+ vflagp += inc;
+ }
-//printf("Currently at grid:\n");
-//for (f = 0; f < fdi; f++)
-//printf("gc[%d] = %d\n",f,gc[f]);
-
- if (rp == NULL) {
- if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
- error("rspl malloc failed - rev.grid entry");
- INCSZ(s, 6 * sizeof(int));
- *rpp = rp;
- rp[0] = 6; /* Allocation */
- rp[1] = 4; /* Next free Cell */
- rp[2] = 1; /* Reference count */
- rp[3] = i;
- rp[4] = -1; /* End marker */
- } else {
- int z = rp[1], ll = rp[0];
- if (z >= (ll-1)) { /* Not enough space */
- INCSZ(s, ll * sizeof(int));
- ll *= 2;
- if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
- error("rspl realloc failed - rev.grid entry");
- *rpp = rp;
- rp[0] = ll;
- }
- rp[z++] = i;
- rp[z] = -1;
- rp[1] = z;
+#undef PRE_LOAD_SURFACE /* [und] Makes it slower ? */
+#ifdef PRE_LOAD_SURFACE /* Pre-load device edge cells */
+ if (edge) {
+ *vflagp = 2; /* This is definitely a gamut surface bwd cell */
+ /* and so nnrev[] needs to be filled */
+ } else
+#endif
+ if (*vflagp == 0) {
+ *vflagp = 1; /* This is possibly not a surface bwd cell, */
+ /* and otherwise is an inside gamut bwd cell */
}
- /* Increment index */
+
+ if (oil)
+ *vflagp |= 0x10; /* Contains over ink limit vertexes */
+
+//printf("seting vflag[%d] to surface done (%x)\n",vflagp-vflag,*vflagp);
+
+//printf(" Currently at grid ix %d, (vflag = %x) adding fwd %d:\n",vflagp-vflag,*vflagp,i);
+//for (f = 0; f < fdi; f++)
+//printf(" gc[%d] = %d\n",f,gc[f]);
+
+ /* Add fwd cells to rev[] list */
+ add2indexlist(s, rpp, i, 0);
+
+ /* Increment index up to and including imax[] */
for (f = 0; f < fdi; f++) {
gc[f]++;
if (gc[f] <= imax[f])
@@ -5324,811 +9063,2107 @@ rspl *s
gc[f] = imin[f];
}
} /* Next reverse grid point in intersecting cube */
+ } /* Next base grid point */
- if (s->rev.fastsetup)
- continue; /* Skip nnrev setup */
+ DBG(("We skipped %d cells that were over the limit\n",nskcells));
+#ifdef CHECK_NNLU
+ if (fdi > 1) {
+ /* Check that every flagged rev[] cell is filled */
+ printf("Checking all %d flagged rev[] cells are filled\n",rgno);
+ for (i = 0; i < rgno; i++) {
+ if ( (vflag[i] & 1) != 0
+ && (s->rev.rev[i] == NULL || s->rev.rev[i][1] == 3)) {
+ printf("Found empty rev[%d] ?:\n",i);
+ printf(" vflag %x\n",vflag[i]);
+ if (s->rev.rev[i] == NULL)
+ printf(" rev = NULL\n");
+ else
+ printf(" rev = length = %d\n",s->rev.rev[i][1]-3);
+ }
+ }
+ }
+#endif /* CHECK_NNLU */
- /* Now also register which grid points are in-gamut and are part of cells */
- /* than have a rev.rev[] list. */
+ /* If doing fast setup, then this is all we need. */
+ if (s->rev.fastsetup) {
- /* Figure out intersection range in reverse nn (construction) vertex grid */
- /* This range may be empty if a grid isn't stradled by the fwd cell output */
- /* range. */
- for (f = 0; f < fdi; f++) {
- double t;
- int mi;
- double gw = s->rev.gw[f];
- double gl = s->rev.gl[f];
- t = (min[f] - gl)/gw;
- mi = (int)ceil(t); /* Grid coordinate */
- if (mi < 0) /* Limit to valid cube base index range */
- mi = 0;
- else if (mi >= argres)
- mi = rgres;
- imin[f] = mi;
- t = (max[f] - gl)/gw;
- mi = (int)floor(t); /* Grid coordinate */
- if (mi < 0) /* Limit to valid cube base index range */
- mi = 0;
- else if (mi >= argres)
- mi = rgres;
- imax[f] = mi;
- if (imax[f] < imin[f])
- break; /* Doesn't straddle any verticies */
+ /* Free up flag array used for construction */
+ if (vflag != NULL) {
+ DECSZ(s, rgno * sizeof(char));
+ free(vflag);
}
- if (f >= fdi) { /* There are seed verticies to mark */
+ s->rev.rev_valid = 1;
+
+ if (fdi > 1 && s->verbose)
+ fprintf(stdout, "%cFast nnrev initialization done\n",cr_char);
+
+ DBG(("init_revaccell fastsetup finished\n"));
+
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ printf("Fastsetup took %f seconds\n",0.001 * (msec_time()-smsec));
+#endif
+
+ return;
+ }
+
+ /* Rough outline of overall nn setup process:
+
+ Fill rev[] array by scanning fwd cells.
+
+ Locating initial surface bwd cells.
+
+ loop:
+ Fill empty surface cells from rev[] list and convert to vertexes.
+
+ (In two phases, first just against primary bx, second with all
+ shadowed bx's:)
+
+ Test all triangles against all vertexes and mark those that are shadowed.
+
+ Remove vertexes from bx if they have been deleted, but leave
+ them in the vertex cache for testing against.
+
+ If any vertexes of a bx land outside it in a bx that is not
+ part of the surface list, add that bx to the surface list and
+ mark it for processing.
+
+ Track which bx cells shadow newly added bx cells,
+ so that new bx cells get tested against all their shadowers,
+ as well as being used to test against their shadowees.
+
+ Locate and preserve all overlapping surface triangles.
+
+ Delete any shadowed vertexes, and remove any empty bxcells.
+
+ Add extra over ink limit vertexes.
+
+ Convert vertexes back to minimum number of fwd cubes.
+
+ */
+
+ calc_ocent(s);
+
+ /* Locate and process the surface bxcells and fill the nnrev array if we */
+ /* are not doing a fast setup. (fastsetup will instead fill the nnrev[] array */
+ /* on demand, by searching the rev[] array.) */
+ DBG(("Identifying surface rev cells\n"));
+
+ /* Allocate the surflist hash index. */
+ /* (Note that we track bxcells in the surface list rather */
+ /* than the hash list, in this context.) */
+ create_surfhash(s);
+
+ /* Locate surface reverse cells */
+ DC_INIT(gg);
+ for (i = 0; i < rgno; i++) {
+
+ if ((vflag[i] & 0xf) == 1) { /* if filled rev[] cell but not surface */
+ char *vflagp;
-//printf("~1 marking prime seed vertex %d\n",i);
+ /* Check face neighbors */
+ int cc[MXDO]; /* Neigbor offset counter */
- /* Mark an initial seed point nnrev vertex, and */
- /* create a surface point propogation record for it */
+ /* Check if any of the face neigbors of this bwd cell are empty. */
+ /* If so, mark it as a surface cell. */
+ /* [This won't detect all surface nncells, but will hit most of them */
+ /* without including too many false ones. The vertex filter code */
+ /* should discover any surface nncells that are missed.] */
for (f = 0; f < fdi; f++)
- gc[f] = imin[f]; /* init coords */
+ cc[f] = gg[f];
+ vflagp = vflag + i;
- for (f = 0; f < fdi;) { /* For all of intersect cube */
- int **rpp, *rp;
- char *fpp;
-
- /* Compute pointer to grid cell */
- for (rpp = s->rev.nnrev, fpp = vflag, f = 0; f < fdi; f++) {
- int inc = gc[f] * s->rev.coi[f];
- rpp += inc;
- fpp += inc;
- }
- rp = *rpp;
+ for (ff = 0; ff < (fdi << 1); ff++) {
+ f = ff >> 1;
- *fpp = 3; /* Initial seed point */
-
- /* Increment index */
- for (f = 0; f < fdi; f++) {
- gc[f]++;
- if (gc[f] <= imax[f])
- break; /* No carry */
- gc[f] = imin[f];
+ cc[f] += (ff & 1) ? 1 : -1;
+ vflagp += (ff & 1) ? s->rev.coi[f] : -s->rev.coi[f];
+
+
+ /* Out of bounds or empty */
+ if (cc[f] < 0 || cc[f] >= rgres || ((*vflagp & 0xf) == 0)) {
+ vflag[i] = (vflag[i] & ~0xf) | 2; /* Convert this one to empty surface cell */
+//printf("seting vflag[%d] to surface cell (%x)\n",i,vflag[i]);
+
+ /* Add a bxcell to surf hash. Initial status = bx_uninit */
+ if ((bx = get_surface_bxcell(s, i)) == NULL) {
+ /* Since it's a surface point, the seeding point is itself (NULL). */
+ bx = new_bxcell(s, i, gg, NULL, 0.0, NULL);
+ add_bxcell_hash(s, bx);
+
+ /* Add to surface linked list */
+ bx->slist = s->rev.surflist;
+ s->rev.surflist = bx;
+//printf("~1 adding nnrev[%d] to surface list\n",bx->ix);
+ }
+ break;
}
+
+ cc[f] -= (ff & 1) ? 1 : -1;
+ vflagp -= (ff & 1) ? s->rev.coi[f] : -s->rev.coi[f];
+ }
+ }
+#ifdef PRE_LOAD_SURFACE
+ else if ((vflag[i] & 0xf) == 2) { /* Pre-marked surface rev cell */
+
+ /* Add a bxcell to surf hash. Initial status = bx_uninit */
+ if ((bx = get_surface_bxcell(s, i)) == NULL) {
+ /* Since it's a surface point, the seeding point is itself (NULL). */
+ bx = new_bxcell(s, i, gg, NULL, 0.0, NULL);
+ add_bxcell_hash(s, bx);
+
+ /* Add to surface linked list */
+ bx->slist = s->rev.surflist;
+ s->rev.surflist = bx;
+//printf("~1 adding pre-marked nnrev[%d] to surface list\n",bx->ix);
}
}
- } /* Next base grid point */
+#endif /* PRE_LOAD_SURFACE */
- DBG(("We skipped %d cells that were over the limit\n",nskcells));
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (vflag[i] & 2)
+ surfcells++;
+ else if ((vflag[i] & 0xf) != 0)
+ ingamutcells++;
+ else
+ emptycells++;
- /* Setup the nnrev array if we are not doing a fast setup. */
- /* (fastsetup will instead fill the nnrev array on demand, */
- /* using an exaustive search.) */
- if (!s->rev.fastsetup) {
+ if (s->rev.rev[i] != NULL) {
+ revcells++;
+ revcelldepth += s->rev.rev[i][1]-3;
+ }
+#endif
+ DC_INC(gg);
+ }
- /* The next step is to use all the prime seed grid points to set and propogate */
- /* the index of the closest fwd vertex through the revnn[] array. */
- /* (This doesn't work perfectly. Sometimes a vertex is not linked to it's closest */
- /* prime. I'm not sure if this is due to a bug here, or is a quirk of geometry */
- /* that a prime that is closest to a vertex isn't closest for any of its neighbors.) */
- DBG(("filling in rev.nnrev[] grid\n"));
+ if (di < 2)
+ {
+ /* Create surface fwd cell list */
+ DBG(("create surface fwd cell lists\n"));
- /* For all the primary seed points */
- DC_INIT(gg);
- for (i = 0; i < rgno; i++) {
- int **rpp;
- primevx *prime= NULL; /* prime cell information structure */
+ /* For each rev[] containing fwd cells, */
+ /* copy the cells to the corresponding surface bxcel cell */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int *crp, *rp;
+
+ if ((crp = s->rev.rev[bx->ix]) == NULL)
+ error("Surface list bxcell ix %d has no vertexes",bx->ix);
- if (vflag[i] != 3) { /* Not a prime seed point */
- goto next_seed_point;
+ /* For each fwd cell in surface rev[] */
+ for (rp = crp+3; *rp != -1; rp++) {
+ add2indexlist(s, &bx->sl, *rp, 0);
}
+ }
+
+ } else {
+
+#ifdef REVVRML
+ /* Plot the initial surface bxcells & their fwd cells. */
+ /* Rev cells? Fwd cells? Fwd cell base indexs? */
+ if (0) plot_bxfwcells(s, 0, 1, 0);
+#endif /* REVVRML */
+
+ /* per reverse cell vertex cache */
+ create_vtxrec_list(s, &vc);
+
+ /* per reverse cell surface triangle cache */
+ create_trirec(s, &tc, 0);
+
+ /* small per reverse cell surface triangle cache */
+ create_trirec(s, &stc, 1);
+
+ /* create associated sub-simplex (triangle) lookup table */
+ init_assdir(s, &tridir, 2);
+
+ /* create associated sub-simplex (edge) lookup table */
+ init_assdir(s, &edgdir, 1);
+
+ /* Process surface bxcells */
+ /* (Maintain current list of vtxrec's for all vertexes) */
- rpp = s->rev.nnrev + i;
+ /* Setup temporary matrix */
+ for (f = 0; f < 2; f++)
+ ta[f] = TA[f];
+
+ /* - - - - - - - - - - - - - - */
+ /* fill, thin and add, until */
+ /* there is no more work to do. */
+ for (;;) {
+ int phase;
+ int morevtxadded = 0;
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ unsigned long lmsec = msec_time();
+ int thcount = 0, rethcount = 0;
+
+// printf("At top of gamut surface loop\n");
+#endif
-//printf("~1 potential rev.nnrev[] prime seed %d, about to scan neibors\n",i);
- /* For all the neigbors of this seed */
- DC_INIT(cc);
- while (!DC_DONE(cc)) {
- propvx *prop; /* neighor cell propogation structure */
- int nix = 0; /* Neighbor cell index */
- char *fpp = vflag;
- int **nrpp = s->rev.nnrev;
- double dsq;
+ /* For each surface bxcell, convert the corresponding */
+ /* rev[] fwd cubes into vertices. */
+ /* (Musk keep bxcells even if none of their verticies */
+ /* are physically in them, so that those verticies get thinned. */
+ /* could only remove them if vertex was not in any surface cell ?) */
+ for (pbx = &s->rev.surflist, bx = *pbx; bx != NULL; bx = nbx) {
+ int *crp, *rp, *nrp;
+ vtxrec *vx;
+
+ nbx = bx->slist;
- for (f = 0; f < fdi; f++) {
- nn[f] = gg[f] + cc[f];
- if (nn[f] < 0 || nn[f] >= argres)
- break; /* Out of bounds */
- nix += nn[f] * s->rev.coi[f];
+ if (bx->status != bx_uninit) {
+ pbx = &bx->slist;
+ continue;
}
- fpp = vflag + nix;
- /* If neighbor out of bounds, or is a prime seed point, skip it */
- if (f < fdi || *fpp == 3) {
- goto next_neighbor;
- }
+ if ((crp = s->rev.rev[bx->ix]) == NULL)
+ error("Surface list bxcell ix %d has no vertexes",bx->ix);
+
+//printf("Initializing bxcell %d with vertexes\n",bx->ix);
+ /* For each fwd cell in surface rev[] */
+ for (rp = crp+3; *rp != -1; rp++) {
+
+//adding cube %d to bx %d\n",*rp, bx->ix);
+ /* For each vertex of cube */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = *rp + s->g.hi[ee];
+ float *fcb = s->g.a + vix * s->g.pss; /* Pointer to base float of fwd cell */
+ vtxrec *vx;
+
+//printf("~1 adding cube %d vtx %d to bx %d\n",*rp, vix, bx->ix);
-//printf("~1 identified prime seed %d with neighbor %d\n",i,nix);
- /* We now know that this prime seed will propogate, */
- /* so get/create the temporary information record for it */
- if (prime == NULL) {
+ /* Don't add over ink limit vertexes */
+ /* (we'll re-add them in later) */
+ if (s->limiten && fcb[-1] > s->limitv) {
+//printf("Skipping vtx %d because over ink limit\n",vix);
+ continue;
+ }
+
+ if ((vx = get_vtxrec(&vc, vix)) != NULL) {
+ if (vx->rix == bx->ix) {
+//printf("Already have vertex %d in bx %d\n",vx->ix,vx->rix);
+ }
- /* If this prime seed hasn't be setup before */
- if (*rpp != NULL) {
- prime = *((primevx **)rpp);
- } else {
- /* Allocate a primevx if there isn't one */
- if ((prime = (primevx *) calloc(1, sizeof(primevx))) == NULL)
- error("rspl malloc failed - rev.nnrev prime info structs");
- *((primevx **)rpp) = prime;
- prime->ix = i;
- for (f = 0; f < fdi; f++)
- prime->gc[f] = gg[f];
-//if (fdi > 1) printf("~1 setting prime %d, gc = %d, %d, %d\n", i, prime->gc[0], prime->gc[1], prime->gc[2]);
+ /* Skip vertexes that we've already added to this bxcell */
+ if (vx->tix == bx->ix) {
+//printf("Skipping vtx %d because alread in bx %d\n",vix,bx->ix);
+ continue;
+ }
+ } else {
+ /* Create new vertex */
+ vx = new_vtxrec(s, &vc, vix);
+ vx->tix = bx->ix; /* Added to this bx */
+//printf("Create vtx %d for bx %d (actually in bx %d)\n",vix,bx->ix,vx->rix);
+ }
+
+ /* Add vertex to bxcell sl list */
+ add2indexlist(s, &bx->sl, vix, 0);
+
+ if (vx->rix == bx->ix) {
+//printf("Added vertex %d is in this bx %d\n",vx->ix,vx->rix);
+ } else {
+//printf("Added vertex %d is in different bx %d to this one %d\n",vx->ix,vx->rix,bx->ix);
+ }
}
}
+ /* Expand a bxcell's shadow testing group values based on it's vertex list */
+ /* so that shadow testing works correctly for vertexes that don't */
+ /* actually lie within the bxcell. (Note that in fact the triangle */
+ /* testing creates triangles that are mode of vertexes that may not */
+ /* be in this bx's list, so the shadow size doesn't accturatly reprsent */
+ /* the possible shadow area. It's not clear what consequences this has, */
+ /* if any. If we extanded the group to cover this, we would need to have ) */
+ /* two groups, a shadower group including those vertexes, and a shadowee */
+ /* goup for just those vertexes that are part of the bx. */
+ extend_bxcell_shadow_group(s, &vc, bx);
+ bx->status = bx_filled;
+ pbx = &bx->slist;
+ morevtxadded = 1;
+ }
+
+ DBG(("thinning surface vertex lists and converting to cells\n"));
- /* Pointer to nnrev vertex neighbor point */
- nrpp = s->rev.nnrev + nix;
+ /* (Sorting bxcells doesn't seem to make any performace difference.) */
- /* Compute the distance squared from this prime seed to this neighbor */
- for (dsq = 0.0, f = 0; f < fdi; f++) {
- double tt = (gg[f] - nn[f]) * s->rev.gw[f];
- dsq += tt * tt;
- }
+ for (phase = 0; phase < 2; phase++) {
+
+//printf("Phase %d\n",phase);
+
+ /* For each surface bxcell, form triangles from vertexes */
+ /* and mark as shadowed and other vertexes that are in the */
+ /* triangles shadow. */
+ /* rev[] fwd cubes into vertices. */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int sdi = 2; /* sub-simplexes are triangles */
+ double clb[MXRO+1]; /* Line RHS implicit equation vector [fdi+1] */
+ int *crp, *rp, *nrp;
+ vtxrec *vx, *nvx;
+ int aftercount; /* vertex count after thinning */
- /* Get or allocate a prop structure for it */
- if (*nrpp != NULL) {
- prop = *((propvx **)nrpp);
- if ((dsq + 1e-6) < prop->dsq) { /* This prime is closer than previous */
- prop->cix = i; /* The index of the closest prime */
- prop->dsq = dsq; /* Distance squared to closest prime */
+ if (bx->status != bx_filled && bx->status != bx_rethinnd) {
+//printf("~1 skipping bx %d because status = %d\n",bx->ix,bx->status);
+ continue;
}
- } else {
- if ((prop = (propvx *) calloc(1, sizeof(propvx))) == NULL)
- error("rspl malloc failed - rev.nnrev propogation structs");
- *((propvx **)nrpp) = prop;
- prop->ix = nix;
- for (f = 0; f < fdi; f++)
- prop->gc[f] = nn[f]; /* This neighbors coord */
- prop->cix = i;
- prop->dsq = dsq;
- prop->pass = pass;
- prop->next = nlist; /* Add new seed to list of next seeds */
- nlist = prop;
- *fpp = 1;
+//printf("~1 checking bx %d\n",bx->ix);
- }
- next_neighbor:;
- DC_INC(cc);
- }
+ /* Only do first pass through primary alone if never thinned before */
+ if (phase == 0 && bx->status == bx_rethinnd) {
+ continue;
+ }
- next_seed_point:;
- DC_INC(gg);
- }
+ /* If this bxcell is empty (because all it's vertexes are shadowed ?) */
+ if (bx->sl == NULL || bx->sl[1] == 3) {
+//printf("~1 skipping nnrev[%d] because it's empty\n",bx->ix);
+ continue;
+ }
+//printf("Thinning bxcell %d\n",bx->ix);
+ /* Create nnrev[] shadowing linked list. nnrev[] cells who's shadow in */
+ /* the direction of rev.ocent[] touches another nnrev[], add that nnrev[] */
+ /* to their shadow list. This allows us to filter vertexes in other */
+ /* nnrev[] cells from triangles above them */
+ bx->wlist = NULL;
+
+ /* Only go through all shadowed bxcells once primary has been */
+ /* thinned alone */
+ if (phase == 1) {
+
+ /* Use just extra list for re-thinning, for 10% speed advantage. */
+ if (bx->status == bx_rethinnd && xlist != NULL) {
+//printf("Adding shadows to bxcell %d from xlist\n",bx->ix);
+ for (nbx = xlist; nbx != NULL; nbx = nbx->xlist) {
+
+ if (nbx->status == bx_uninit) /* Newly added cells (shouldn't happen) */
+ break;
-//printf("~1 about to propogate secondary seeds\n");
- /* Now we propogate the secondary seed points until there are no more left */
- while(nlist != NULL) {
- propvx *next;
- propvx *tlp;
+ if (nbx == bx)
+ continue;
+
+ /* If any of bx is further from nbx and their bounding */
+ /* cylinders overlap in perspective from rev.ocenter, */
+ /* assume nbx is a shadow */
+ if (shadow_group_group(s, s->rev.ocent, bx->g.bcent, bx->cc, bx->dw,
+ nbx->g.bcent, nbx->cc, nbx->dw)) {
+ nbx->wlist = bx->wlist;
+ bx->wlist = nbx;
+//printf("~1 adding shadow nnrev[%d] from xlist\n",nbx->ix);
+ }
+ }
+ } else {
- if ((pass += 2) < 0)
- error("Assert rev: excessive propogation passes");
-//printf("~1 about to do a round of propogation pass %d\n",(pass+2)/2);
+//printf("Adding shadows to bxcell %d from surflist\n",bx->ix);
+ for (nbx = s->rev.surflist; nbx != NULL; nbx = nbx->slist) {
- /* Mark all seed points on the current list with pass-1 */
- for (tlp = nlist; tlp != NULL; tlp = tlp->next) {
- *(vflag + tlp->ix) = 2;
- tlp->pass = pass-1;
- }
+//printf("Considering bx %d for shadow list\n",nbx->ix);
+ if (nbx->status == bx_uninit) /* Newly added cells (shouldn't happen) */
+ break;
- /* Go through each secondary seed in the active list, propogating them */
- for (alist = nlist, nlist = NULL; alist != NULL; alist = next) {
- int **rpp;
- primevx *prime= NULL; /* prime cell information structure */
+ if (nbx == bx)
+ continue;
+
+ /* If any of bx is further from nbx and their bounding */
+ /* cylinders overlap in perspective from rev.ocenter, */
+ /* assume nbx is a shadow */
+ if (shadow_group_group(s, s->rev.ocent, bx->g.bcent, bx->cc, bx->dw,
+ nbx->g.bcent, nbx->cc, nbx->dw))
+ {
+//printf("Added bx %d for shadow list, prim bx %d\n",nbx->ix,bx->ix);
+ nbx->wlist = bx->wlist;
+ bx->wlist = nbx;
+ }
+ }
+ }
+ } /* if phase == 1 */
- next = alist->next; /* Next unless we re-insert one */
-
- /* Grab this seed points coodinate and index */
- for (i = f = 0; f < fdi; f++) {
- gg[f] = alist->gc[f];
- i += gg[f] * s->rev.coi[f];
- }
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (bx->status == bx_rethinnd)
+ rethcount++;
+ else
+ thcount++;
+#endif
-//printf("\n~1 propogating from seed %d\n",i);
- /* rpp = s->rev.nnrev + i; */
-
- /* Grab the corresponding prime seed information record */
- prime = *((primevx **)(s->rev.nnrev + alist->cix));
-
- /* For all the neigbors of this seed */
- DC_INIT(cc);
- while (!DC_DONE(cc)) {
- propvx *prop; /* neighor cell propogation structure */
- int nix; /* Neighbor cell index */
- char *fpp = vflag;
- int **nrpp = s->rev.nnrev;
- double dsq;
-
- for (nix = f = 0; f < fdi; f++) {
- nn[f] = gg[f] + cc[f];
- if (nn[f] < 0 || nn[f] >= argres)
- break; /* Out of bounds */
- nix += nn[f] * s->rev.coi[f];
+ /* Abort doing this cell until all its shadowees are filled */
+ /* (Shouldn't happen ?) */
+ if (nbx != NULL) {
+//printf("Skipping thinning of bx %d because newly added bx %d is in surfce list\n",bx->ix,nbx->ix);
+ continue;
}
- fpp = vflag + nix;
-//printf("~1 neighbor ix %d, flag %d\n",nix,*fpp);
-
- /* If neighbor out of bounds, current vertex or prime, skip it */
- if (f < fdi || i == nix || *fpp >= 3) {
-//printf("~1 skipping neighbour %d\n",nix);
- goto next_neighbor2;
+
+ /* Be able to detect triangles already tested */
+ /* from this shadowing bxcell. */
+ clear_trirec(s, &tc);
+
+ /* Put just primary and shadows on vx->tlist */
+ vc.vtxlist = NULL;
+ vc.nilist = 0;
+
+ /* Add all the secondary bxcell vertexes to the vtxlist */
+ for (nbx = bx->wlist; nbx != NULL; nbx = nbx->wlist) {
+//printf("Adding bx %d verticies\n",nbx->ix);
+ for (rp = nbx->sl+3; *rp != -1; rp++) {
+
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ error("Failed to find vertex %s in cache",*rp);
+
+//printf("Checking ix %d from bx %d\n",vx->ix,nbx->ix);
+ /* Check vertex falls within shadow of main bx */
+ /* (just checking non-deleted vertexes (triangles) */
+ /* improves speed by 20%, but we end up with stray fwd cells */
+ /* and some holes, because crossed triangles vertexes get */
+ /* marked deleted ??) */
+ if (
+// vx->status == vtx_norm &&
+ shadow_group_vertex(s, s->rev.ocent, bx->g.bcent, bx->cc, bx->dw,
+ vx->v)) {
+ add_vtxrec_list(&vc, vx, 0); /* Add if not deleted */
+//printf(" Added ix %d from bx %d\n",vx->ix,nbx->ix);
+ }
+//else
+//printf(" Not added ix %d from bx %d because no within prim bx %d\n",vx->ix,nbx->ix,bx->ix);
+ }
}
- /* Pointer to nnrev vertex neighbor point */
- nrpp = s->rev.nnrev + nix;
-
- /* Compute the distance squared from the prime seed to this neighbor */
- for (dsq = 0.0, f = 0; f < fdi; f++) {
- double tt = (prime->gc[f] - nn[f]) * s->rev.gw[f];
- dsq += tt * tt;
- }
+ /* Add all the primary bxcell verticies to the list, and */
+ /* mark them (override shadow mark) */
+//printf("Adding bx %d verticies\n",bx->ix);
+ for (rp = bx->sl+3; *rp != -1; rp++) {
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ error("Failed to find vertex %s in cache",*rp);
- /* Get or allocate a prop structure for it */
- if (*nrpp != NULL) {
- prop = *((propvx **)nrpp);
-//if (prop->ix != nix) error ("Assert: prop index %d doesn't match index %d",prop->ix, nix);
-
- if ((dsq + 1e-6) < prop->dsq) { /* This prime is closer than previous */
-//printf("~1 updating %d to prime %d, dsq = %f from %f\n",nix, prime->ix, dsq, prop->dsq);
- prop->cix = prime->ix; /* The index of the new closest prime */
- prop->dsq = dsq; /* Distance squared to closest prime */
- /* If this is a vertex from previous pass that has changed, */
- /* and it's not ahead of us in the current list, */
- /* put it next on the current list. */
- if (*fpp == 2 && prop->pass != (pass-1)) {
-//printf("~1 re-shedule %d (%d) for next propogate\n",nix,prop->ix);
-//if (next == NULL)
-//printf("Before insert, next = NULL\n");
-//else
-//printf("Before insert, next = %d\n",next->ix);
- prop->pass = pass-1; /* Re-shedule once only */
- prop->next = next;
- next = prop;
- }
+ if (vx->status == vtx_norm &&
+ shadow_group_vertex(s, s->rev.ocent, bx->g.bcent, bx->cc, bx->dw, vx->v)) {
+ add_vtxrec_list(&vc, vx, 1); /* Add if not hidden/deleted */
}
- } else {
- if ((prop = (propvx *) calloc(1, sizeof(propvx))) == NULL)
- error("rspl malloc failed - rev.nnrev propogation structs");
- *((propvx **)nrpp) = prop;
- prop->ix = nix;
- for (f = 0; f < fdi; f++)
- prop->gc[f] = nn[f]; /* This neighbors coord */
- prop->cix = prime->ix;
- prop->dsq = dsq;
-//printf("~1 propogating to new, %d, dsq = %f, prime %d\n",nix, dsq, prime->ix);
- prop->pass = pass;
- prop->next = nlist; /* Add new seed to list of next seeds */
- nlist = prop;
- *fpp = 1;
}
- next_neighbor2:;
- DC_INC(cc);
- }
- alist->pass = pass;
- }
- }
+ aftercount = vc.nilist;
-#ifdef DEBUG
- DBG(("checking that every vertex is now touched\n"));
- for (i = 0; i < rgno; i++) {
- if (vflag[i] < 2) {
- printf("~1 problem: vertex %d flag = %d\n",i, vflag[i]);
- }
- if (vflag[i] == 2 && *(s->rev.nnrev + i) == NULL) {
- printf("~1 problem: vertex %d flag = %d and struct = NULL\n",i, vflag[i]);
- }
- }
-#endif /* DEBUG */
+ /* sort vertexes by decending distance to center point */
+ /* (and also reset list tflag) */
+ sort_vtxrec_list(s, &vc);
+
+ /* For vertexes of this bxcell and shadowers, */
+ /* in order from largst to smallest distance from center. */
+ for (vx = vc.vtxlist; vx != NULL; vx = vx->tlist) {
+ float *vp; /* Vertex being tested */
+ int fl;
+ assdire *tri; /* Triangle table */
+
+//printf("~1 checking against vtx %d\n",vx->ix);
+
+ /* Only check triangles using verticies of the primary bxcell, */
+ /* not shadow bx's. */
+ if (!vx->prim)
+ continue;
-#ifdef NEVER /* Check that all cells are closest to their primes than any other */
-DC_INIT(gg);
-for (i = 0; i < rgno; i++) { /* For all the verticies */
- if (vflag[i] == 2) {
- propvx *prop = (propvx *) *(s->rev.nnrev + i);
- for (j = 0; j < rgno; j++) { /* For all the primes */
- if (vflag[j] == 3) {
- primevx *prime = (primevx *) *(s->rev.nnrev + j);
- double dsq;
- if (prime == NULL)
- continue;
- for (dsq = 0.0, f = 0; f < fdi; f++) {
- double tt = (prime->gc[f] - prop->gc[f]) * s->rev.gw[f];
- dsq += tt * tt;
- }
- if ((dsq + 1e-6) < prop->dsq) {
- warning("~1 vertex %d prime %d, dsq = %f, is closer to prime %d, dsq %f\n", i,prop->cix, prop->dsq, j, dsq);
- /* See if any of the neighbors have the closer prime */
- DC_INIT(cc); /* For all the neigbors of this seed */
- while (!DC_DONE(cc)) {
- propvx *nprop; /* neighor cell propogation structure */
- int nix; /* Neighbor cell index */
- char *fpp = vflag;
- int **nrpp = s->rev.nnrev;
- double dsq;
-
- for (nix = f = 0; f < fdi; f++) {
- nn[f] = gg[f] + cc[f];
- if (nn[f] < 0 || nn[f] >= argres)
- break; /* Out of bounds */
- nix += nn[f] * s->rev.coi[f];
+//printf("~1 doing vertex %d at %s dist %f\n",vx->ix, debPdv(fdi,vx->v), sqrt(vx->dist));
+
+ vp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(vp); /* Edge flags for this vertex */
+
+ tri = tridir + fl;
+//printf("~1 fl %d = 0o%o, no triangles %d\n",fl, fl, tri->no);
+
+ /* For all possible triangles that use this vertex */
+ for (i = 0; i < tridir[fl].no; i++) {
+ int triix[3];
+ vtxrec *trivx[3];
+ double v[MXRI+1][MXRO]; /* Triangle vertex values */
+ double gc[MXRO], cc, dw; /* Triangle shadow group info. */
+ int ntvsh = 0; /* Number of triangle verticies shadowed */
+ double bdist = -1.0;
+
+ /* Get triangle verticy values */
+ for (e = 0; e <= sdi; e++) {
+ triix[e] = vx->ix + tri->ti[i].goffs[e];
+
+ if ((trivx[e] = get_vtxrec(&vc, triix[e])) == NULL)
+ break; /* Vertex doesn't exist in our set */
+
+ if (trivx[e]->status != vtx_norm)
+ ntvsh++;
+
+ if (trivx[e]->dist > bdist)
+ bdist = trivx[e]->dist;
}
- fpp = vflag + nix;
-//printf("~1 neighbor ix %d, flag %d\n",nix,*fpp);
+//printf("~1 tri %d: vtxs %s goffs %s\n",i, debPiv(di,triix), debPiv(sdi+1, tri->ti[i].goffs));
+
+ /* Don't test against triangle unless all vertexes */
+ /* are in current surface, and whole triangle is visible. */
+ if (e <= sdi || ntvsh >= 3)
+ continue;
+
+ /* If triangle has been done before for this bxcell, skip it. */
+ if (check_trirec(s, &tc, triix))
+ continue;
- /* If neighbor out of bounds, current vertex or prime, skip it */
- if (f < fdi || i == nix || *fpp != 2) {
-//printf("~1 skipping neighbour %d\n",nix);
- goto next_neighbor3;
+ for (e = 0; e <= sdi; e++) {
+ for (f = 0; f < fdi; f++)
+ v[e][f] = trivx[e]->v[f];
}
- /* Pointer to nnrev vertex neighbor point */
- nrpp = s->rev.nnrev + nix;
- if ((nprop = *((propvx **)nrpp)) != NULL) {
-//printf("~1 neighbor %d %d %d has prime %d dsq %f\n",cc[0],cc[1],cc[2],nprop->cix,nprop->dsq);
- if (nprop->cix == j) {
-//warning("~1 but neighbor has this prime point!\n");
+ /* Compute shadow group params of triangle for quick vertex test */
+ comp_shadow_group(s, s->rev.ocent, gc, &cc, &dw, NULL, v, sdi+1);
+
+ /* For all vertexes */
+ for (nvx = vc.vtxlist; nvx != NULL; nvx = nvx->tlist) {
+ double pv[MXRO]; /* Vertex being tested */
+ double de[MXRO]; /* Line delta */
+ double tb[MXRI]; /* Solution point in input space */
+ double xv[MXRO]; /* Solution point in output space */
+ int g, sorv, wsrv; /* Solved & within simplex return value */
+ double dist; /* distance to line origin */
+ double dot; /* dot product of solution to line */
+ int shdwd; /* whether vertex is shadowed */
+
+ /* If vertex is above triangle, it can't be shadowed */
+ if (nvx->dist > bdist)
+ continue;
+
+ /* If this other vertex has already been deleted, skip it */
+ if (nvx->status != vtx_norm)
+ continue;
+
+ /* If this other vertex is part of the triangle, skip it */
+ if (nvx->ix == triix[0]
+ || nvx->ix == triix[1]
+ || nvx->ix == triix[2]) {
+ continue;
+ }
+
+//printf("~1 checking vertex %d against tri %s\n",nvx->ix,debPiv(3,triix));
+
+ /* Do quick check against triangle */
+ if (!shadow_group_vertex(s,
+ s->rev.ocent, gc, cc, dw, nvx->v)) {
+//printf("~1 shadow group check shows no intersection\n");
+ continue;
+ }
+
+//printf("~1 checking vertex %d at %s dist %f\n",nvx->ix, debPdv(fdi,nvx->v), sqrt(nvx->dist));
+
+ /* Compute intersection: */
+ shdwd = wsrv = 0;
+
+ /* Compute line delta */
+ vp = s->g.a + nvx->ix * s->g.pss;
+ for (f = 0; f < fdi; f++) {
+ pv[f] = vp[f];
+ de[f] = pv[f] - s->rev.ocent[f];
+ }
+
+ /* Setup line cla and clb */
+ init_line_eq_imp(s, NULL, &cla, clb, s->rev.ocent, de, 0);
+
+ /* Solve line/triangle intersection using same */
+ /* method as vnearest_clip_solve(). */
+
+ /* LHS: ta[sdi][sdi] = cla[sdi][fdi] * vv[fdi][sdi] */
+ /* RHS: tb[sdi] = clb[sdi] - cla[sdi][fdi] * vv_di[fdi] */
+ for (f = 0; f < sdi; f++) {
+ double tt;
+ for (e = 0; e < sdi; e++) {
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * (v[e][g] - v[e+1][g]);
+ ta[f][e] = tt;
+ }
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * v[sdi][g];
+ tb[f] = clb[f] - tt;
+ }
+
+ /* Compute the solution */
+ /* (Solve the simultaneous linear equations A.X = B) */
+// sorv = !solve_se(ta, tb, sdi);
+ sorv = !solve_se_2x2(ta, tb); /* Saves a few % only */
+
+ /* If it was solved */
+ if (sorv) {
+
+ /* Check that the solution is within the simplex & ink limit */
+ if ((wsrv = simple_within_simplex(v, tb, sdi)) != 0) {
+
+ /* Compute the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++)
+ tt += (v[e][f] - v[e+1][f]) * tb[e];
+ xv[f] = tt + v[sdi][f];
+ }
+
+ /* Compute distance to gamut center squared, */
+ /* as well as the dot product */
+ for (dot = dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (xv[f] - s->rev.ocent[f]);
+ dist += tt * tt;
+ dot += de[f] * tt;
+ }
+//printf("~1 intersection at %s dist %f\n", debPdv(fdi,xv), sqrt(dist));
+
+ /* If intersection distance is greater than vertex distance, */
+ /* delete the vertex */
+ if (dot > 0.0 && dist > (nvx->dist + EPS)) {
+ shdwd = 1;
+ nvx->status = vtx_sha; /* Shadowed */
+ aftercount--;
+//printf("~1 deleting vx %d\n",nvx->ix);
+ }
+ }
+ }
+//if (!sorv) printf("~1 solve failed\n");
+//if (sorv && !wsrv) printf("~1 %d not within simplex, tb = %s\n",nvx->ix, debPdv(sdi,tb));
+//if (sorv && wsrv && shdwd) printf("~1 tri %s deleting vertex %d\n",debPiv(3,triix), nvx->ix);
+
+#ifdef REVVRML
+ /* Plot vertex & triangle check setup & solution */
+ /* + the primary and shadow bxcells. */
+ /* Plot prim & shadow bxcell cells ? Wait for user press ? */
+ if (0 && phase && shdwd) plot_tri_check(s, 1, 1,
+ bx, vx->ix, i, triix, nvx->ix, sorv, wsrv, shdwd, v, de, pv, xv);
+#endif /* REVVRML */
+
+ } /* Next other vertex */
+ } /* Next triangle */
+ } /* Next main vertex */
+
+ if (phase == 1)
+ bx->status = bx_thinned;
+//printf("Thinned vertexes in bx %d from %d to %d (%d)\n",bx->ix, vc.nilist,aftercount, vc.nilist-aftercount);
+ } /* Next surface bx cell */
+ /* Done with lists */
+ vc.vtxlist = NULL;
+ vc.nilist = 0;
+ xlist = NULL;
+
+ DBG(("deleting verticies in all bxcells\n"));
+
+ /* The thinning may have deleted verticies from bxcell's that */
+ /* were not involved in the thinning, so go though all bxcells */
+ /* to do deletions. Look also for any needed additional surface bxcells. */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int beforecount, aftercount;
+ vtxrec *nvx;
+ int *crp, *rp, *nrp;
+
+ if (bx->status == bx_uninit)
+ continue;
+
+#ifdef REVVRML
+ bx->debug = 0; /* Not an addition */
+#endif
+
+ beforecount = bx->sl[1]-3;
+
+#undef DELETE_SHAD /* [und] try deleting shadowed vertexes with no un-shadowed neighbors. */
+ /* Seems to actually slow things down though ? */
+
+ /* Delete all the marked vertexes from bxcell list */
+ for (nrp = rp = bx->sl+3; *rp != -1; rp++) {
+ vtxrec *vx;
+#ifdef DELETE_SHAD
+ int nshad = 0, nnshad = 0;
+#endif
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ continue; /* Already deleted */
+
+#ifdef REVVRML
+ vx->addvtx = 0;
+#endif
+
+#ifdef DELETE_SHAD
+ /* Check all of its neighbor vertexes, to see if */
+ /* it's safe to actually delete them. */
+ if (vx->status >= vtx_sha) { /* vertex to delete ? */
+ float *vp;
+ int fl;
+ assdire *edg; /* Edge table */
+
+//printf("Checking vx %d neighbors\n",vx->ix);
+ vp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(vp); /* Edge flags for this vertex */
+ edg = edgdir + fl;
+
+ /* For all possible edges that use this vertex */
+ for (i = 0; i < edgdir[fl].no; i++) {
+ int eix;
+
+ /* Edge vertex index number of other vertex */
+ if (edg->ti[i].goffs[0] != 0)
+ eix = vx->ix + edg->ti[i].goffs[0];
+ else
+ eix = vx->ix + edg->ti[i].goffs[1];
+
+ if ((nvx = get_vtxrec(&vc, eix)) != NULL) {
+//printf("vx %d neighbor vx %d status %d\n",vx->ix,nvx->ix,nvx->status);
+ if (nvx->status >= vtx_sha) {
+ nshad++;
+ } else {
+ nnshad++;
+ }
}
}
- next_neighbor3:;
- DC_INC(cc);
}
-// prop->cix = j; /* Fix it and see what happens */
-// prop->dsq = dsq;
- }
- }
+//printf("vx %d nshad %d nnshad %d\n",vx->ix);
+#endif /* DELETE_SHAD */
+
+ /* Keep un-shadowed vertexes and */
+ /* shadowes ones that have non-shadows neigbors */
+ if (vx->status == vtx_norm
+#ifdef DELETE_SHAD
+ || vx->status >= vtx_sha && nnshad != 0
+#endif
+ ) {
+ *nrp++ = *rp;
+//printf("~1 leaving vtx %d status %d in bxcell %d list\n",vx->ix,vx->status,bx->ix);
+
+ if (phase == 1) {
+#ifndef NEVER /* Do additions */
+ /* If vertex doesn't land in a surface bxcell, */
+ /* create a new surface bxcell for it. */
+ if (vx->status == vtx_norm // ????
+ && (vflag[vx->rix] & 2) == 0) {
+ bxcell *nx;
+
+//if (get_surface_bxcell(s, vx->rix) != NULL)
+//error("new addition bx %d is already surface cell!\n",vx->rix);
+
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ nascells++;
+#endif
+ /* Since it's a surface point, the seeding point is itself (NULL). */
+ nx = new_bxcell(s, vx->rix, vx->ival, NULL, 0.0, NULL);
+ add_bxcell_hash(s, nx);
+ /* Convert to empty surface cell */
+ vflag[nx->ix] = (vflag[nx->ix] & ~0xf) | 2;
+
+ /* Add to surface linked list */
+ nx->slist = s->rev.surflist;
+ s->rev.surflist = nx;
+
+ /* Add to additions list */
+ nx->xlist = xlist;
+ xlist = nx;
+//printf("Added bxcell %d, status %d due to vx %d status %d\n",nx->ix, nx->status,vx->ix,vx->status);
+
+#ifdef REVVRML
+ vx->addvtx = 1; /* Cause of added bxcell */
+ nx->debug = 1; /* Mark added bxcells */
+#endif
+ }
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ /* Keep addvtx flag straight */
+ else if (vx->status == vtx_norm) {
+ bxcell *nx;
+ if ((nx = get_surface_bxcell(s, vx->rix)) != NULL) {
+ if (nx->status == bx_uninit) /* Must be just added */
+ vx->addvtx = 1; /* Cause of added bxcell */
+ }
+ }
+#endif
+ }
+#endif /* Do additions */
+ /* Omit vertex from bx list, and mark it as deleted, */
+ /* and remove it if it has no un-shadowed neighbors */
+ } else {
+ vx->status = vtx_del;
+//printf("~1 marking vtx %d status %d nnshad %d deleted bxcell %d list\n",vx->ix,vx->status,nnshad,bx->ix);
+#ifdef DELETE_SHAD
+ /* Remove it from cache if all its neighbors are */
+ /* shadowed too. */
+ if (nnshad == 0) {
+//printf("~1 deleting vtx %d\n",vx->ix);
+ del_vtxrec_hash(&vc, vx->ix);
+ if (get_vtxrec(&vc, vx->ix) != NULL)
+ error("get_vtxrec suceeded after del_vtxrec_hash!");
+ }
+#else /* !DELETE_SHAD */
+ /* Keep track of deleted verticies that are in this bx, */
+ /* so we can add back in crossing triangle vertexes */
+ add2indexlist(s, &bx->dl, vx->ix, 0);
+#endif /* !DELETE_SHAD */
+ }
+ } /* Next vertex in bx's list */
+ *nrp = -1;
+ bx->sl[1] = nrp - bx->sl;
+
+//aftercount = bx->sl[1]-3;
+//if (beforecount != 0 && aftercount < beforecount) printf("Reduced bx from %d to %d verticies\n",beforecount,aftercount);
+ } /* Next bx */
+ } /* Next phase */
+
+#ifdef REVVRML
+ /* Main summary plot at each thinning round */
+ /* Vtx ix tag ? Deleted vtxs ? Added vtxs ? Preserved vtxs ? oil ? bxcells ? Wait ? */
+ if (0) plot_vtx_surface(s, 0, 0, 1, 0, 0, 0, 1, &vc, edgdir);
+#endif /* REVVRML */
+
+ if (xlist == NULL) {
+ break; /* No added surface cells */
}
- }
- DC_INC(gg);
-}
-#endif /* NEVER */
+ DBG(("reseting shadowers of new bxcells\n"));
+ /* Locate all the bxcells that shadow the added bxcells, */
+ /* and revert status to rethinned. */
+ for (bx = xlist; bx != NULL; bx = bx->xlist) {
+
+#ifdef REVVRML
+ for (nbx = s->rev.surflist; nbx != NULL; nbx = nbx->slist)
+ nbx->debug = 0;
+#endif
- DBG(("about to do convert vertex values to cell lists\n"));
- /* Setup a cache for the fwd cell lists created, so that we can */
- /* avoid the list creation and memory allocation for identical lists */
- nncsize = s->rev.ares * s->rev.ares;
- if ((nnc = (nncache **) calloc(nncsize, sizeof(nncache *))) == NULL)
- error("rspl malloc failed - rev.nnc cache entries");
+ /* Locate the nnrev[] bxcells that shadow this added bxcell */
+ bx->wlist = NULL; /* For debug */
+ for (nbx = s->rev.surflist; nbx != NULL; nbx = nbx->slist) {
- /* Now convert the nnrev secondary vertex points to pointers to fwd cell lists */
- /* Do this in order, so that we don't need the verticies after */
- /* they are converted to cell lists. */
- DC_INIT(gg);
- for (i = 0; i < rgno; i++) {
- int **rpp, *rp;
- propvx *prop = NULL; /* vertex information structure */
- primevx *prime= NULL; /* prime cell information structure */
- int imin[MXRO], imax[MXRO]; /* Prime vertex range for each axis */
- double rmin[MXRO], rmax[MXRO]; /* Float prime vertex value range */
- unsigned int tcount; /* grid touch count for this opperation */
- datao min, max; /* Fwd cell output range */
- int lpix; /* Last prime index seen */
-
-//if (fdi > 1) printf("~1 converting vertex %d\n",i);
-//if (fdi > 1) printf("~1 coord %d %d %d\n",gg[0],gg[1],gg[2]);
-
- rpp = s->rev.nnrev + i;
- if (vflag[i] == 3) { /* Cell base is prime */
- prime = (primevx *) *rpp;
-
- if (prime != NULL) { /* It's a propogating prime */
- /* Add prime to the end of the ptime linked list */
- prime->next = NULL;
- if (plist == NULL) {
- plist = ptail = prime;
- } else {
- ptail->next = prime;
- ptail = prime;
- }
- }
- } else if (vflag[i] == 2) { /* Cell base is secondary */
- prop = (propvx *)*rpp;
- } else { /* Hmm */
- /* This seems to happen if the space explored is not really 3D ? */
- if (s->rev.primsecwarn == 0) {
- warning("rev: bwd vertex %d is not prime or secondary (vflag = %d)"
- "(Check that your measurement data is sane!)",i,vflag[i]);
- s->rev.primsecwarn = 1;
- }
- fill_nncell(s, gg, i); /* Is this valid to do ?? */
- continue;
- }
-
- /* Setup to scan of cube corners, and check that base is within cube grid */
- for (f = 0; f < fdi; f++) {
- if (gg[f] > rgres_1) { /* Vertex outside bwd cell range, */
- if (prop != NULL && prime == NULL) {
- free(prop);
- *rpp = NULL;
+ if (
+#ifdef REVVRML
+ (nbx->status != bx_thinned && nbx->status != bx_filled) // Show all
+#else
+ (nbx->status != bx_thinned)
+#endif
+ || nbx == bx
+ || nbx->sl == NULL
+ || nbx->sl[1] == 3)
+ continue;
+
+ /* If any of nbx is further from bx and their bounding cylinders */
+ /* overlap in perspective from rev.ocenter, assume nbx is a shadower. */
+ if (shadow_group_group(s, s->rev.ocent, nbx->g.bcent, nbx->cc, nbx->dw,
+ bx->g.bcent, bx->cc, bx->dw)) {
+ nbx->status = bx_rethinnd;
+
+#ifdef REVVRML
+ bx->debug = 1; /* rethinned bx */
+ nbx->debug = 2; /* added bx */
+ nbx->wlist = bx->wlist; /* For debug */
+ bx->wlist = nbx;
+#endif
+//printf("~1 marking bxcell %d as un-thinned due to added bxcell %d\n",nbx->ix, bx->ix);
}
-//printf("~1 done vertex %d because its out of cell bounds\n",i);
- goto next_vertex;
}
- imin[f] = 0x7fffffff;
- imax[f] = -1;
- }
- /* For all the vertex points in the nnrev cube starting at this base (i), */
- /* Check if any of them are secondary seed points */
- for (ff = 0; ff < (1 << fdi); ff++) {
- if (vflag[i + s->rev.hoi[ff]] == 2)
- break;
+#ifdef REVVRML
+ /* Plot bxcells touched by added cell */
+ if (0) plot_touched_bxcells(s, bx->ix);
+#endif /* VRML */
}
- /* If not a cell that we want to create a nearest fwd cell list for */
- if (ff >= (1 << fdi)) {
- /* Don't free anything, because we leave a prime in place, */
- /* and it can't be a prop. */
- goto next_vertex;
- }
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ printf(" %d bxcells thinned, %d re-thinned\n",thcount,rethcount);
+ printf("Loop took %f seconds\n",0.001 * (msec_time()-lmsec));
+#endif
+ } /* Loop until done */
- /* For all the vertex points in the nnrev cube starting at this base (i), */
- /* accumulate the range they cover */
- lpix = -1;
- for (f = 0; f < fdi; f++) {
- imin[f] = 0x7fffffff;
- imax[f] = -1;
- }
- for (ff = 0; ff < (1 << fdi); ff++) {
- int ii = i + s->rev.hoi[ff]; /* cube vertex index */
- primevx *tprime= NULL;
-
- /* Grab vertex info and corresponding prime vertex info */
- if (vflag[ii] == 3) { /* Corner is a prime */
- tprime = (primevx *) *(s->rev.nnrev + ii); /* Use itself */
- if (tprime == NULL)
- continue; /* Not a propogated in-gamut vertex */
- } else if (vflag[ii] == 2) {
- propvx *tprop = (propvx *) *(s->rev.nnrev + ii); /* Use propogated prime */
- tprime = (primevx *) *(s->rev.nnrev + tprop->cix);
- } else {
- continue; /* Hmm */
- }
- if (tprime->ix == lpix)
- continue; /* Don't waste time */
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ printf("Thinning took %f seconds\n",0.001 * (msec_time()-smsec));
+#endif
-//if (fdi > 1) printf("~1 corner %d, ix %d, prime %d gc = %d, %d, %d\n", ff, ii, tprime->ix, tprime->gc[0], tprime->gc[1], tprime->gc[2]);
+ /* = = = = = = = = = = = = = = = = = = */
+ DBG(("Preserving overlapping triangles\n"));
+ {
+#ifdef REVTABLESTATS
+ int notverts = 0; /* Number of possible crossed triangles/test verticies */
+ int nopreserved = 0; /* Number of verticies preseved for crossied triangles */
+ unsigned long lmsec = msec_time();
+#endif
+ int sdi = 2; /* sub-simplexes are triangles */
+ int k, jj;
+ vtxrec *vx;
+
+ /* Struct to hold test vertex locations */
+ struct _tvxrec {
+ double v[MXRO];
+ double dist; /* Distance from center point squared */
+ int ix[MXRO+1]; /* Indexes of the triangle verticies */
+ int shad; /* Test result */
+ struct _tvxrec *tlist;
+ }; typedef struct _tvxrec tvxrec;
+ tvxrec *tlist = NULL, *ftlist = NULL, *tvx, *ntvx;
+ int nitlist = 0;
+
+ /* For each surface bxcell, form triangles from vertexes */
+ /* and detect possible crossed triangles */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int sdi = 2; /* sub-simplexes are triangles */
+ double clb[MXRO+1]; /* Line RHS implicit equation vector [fdi+1] */
+ int *crp, *rp, *nrp;
+ vtxrec *vx, *nvx;
+ int aftercount; /* vertex count after thinning */
+
+ /* Skip cell if empty */
+ if (bx->sl == NULL || bx->sl[1] == 3)
+ continue;
+
+ /* Put the testing triangle verticies on the vtxlist */
+ vc.vtxlist = NULL;
+ vc.nilist = 0;
+
+ /* Be able to detect triangles already tested */
+ /* from this shadowing bxcell. */
+ clear_trirec(s, &tc);
+
+ /* See whether to add cell verticies to the list. */
+ for (rp = bx->sl+3; *rp != -1; rp++) {
+ assdire *tri; /* Triangle table */
+ float *fp;
+ int fl;
+ int added = 0;
+
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ error("Failed to find vertex %s in cache",*rp);
+
+ if (vx->status != vtx_norm) // ???
+ continue;
- /* Update bounding box for this prime grid point */
- for (f = 0; f < fdi; f++) {
- if (tprime->gc[f] < imin[f])
- imin[f] = tprime->gc[f];
- if (tprime->gc[f] > imax[f])
- imax[f] = tprime->gc[f];
- }
- lpix = tprime->ix;
- }
+ fp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(fp); /* Edge flags for this vertex */
+ tri = tridir + fl;
-//if (fdi > 1) printf("~1 prime vertex index range = %d - %d, %d - %d, %d - %d\n", imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+ /* For all +ve triangles that use this vertex */
+ for (k = 0; k < tridir[fl].no; k++) {
+ int triix[MXRI+1];
+ vtxrec *trivx[3];
+ int ntvsh = 0; /* Number of verticies shadowed */
+ int nntvsh = 0; /* Number of verticies not shadowed */
- /* See if a list matching this range is in the cache */
- hashk = 0;
- for (hashk = f = 0; f < fdi; f++)
- hashk = hashk * 97 + imin[f] + 43 * (imax[f] - imin[f]);
- hashk = hashk % nncsize;
-//if (fdi > 1) printf("~1 hashk = %d from %d - %d %d - %d %d - %d\n", hashk, imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+//printf("~1 tri %d: goffs = %s\n", k, debPiv(sdi+1, tri->ti[k].goffs));
- /* See if we can locate an existing list for this range */
- for (ncp = nnc[hashk]; ncp != NULL; ncp = ncp->next) {
-//if (fdi > 1) printf("~1 checking %d - %d %d - %d %d - %d\n", ncp->min[0], ncp->max[0], ncp->min[1], ncp->max[1], ncp->min[2], ncp->max[2]);
- for (f = 0; f < fdi; f++) {
- if (ncp->min[f] != imin[f]
- || ncp->max[f] != imax[f]) {
-//if (fdi > 1) printf("~1 not a match\n");
- break;
+ /* Triangle vertex index numbers */
+ for (j = 0; j <= sdi; j++) {
+ triix[j] = vx->ix + tri->ti[k].goffs[j];
+
+ if ((trivx[j] = get_vtxrec(&vc, triix[j])) == NULL) {
+ break; /* Vertex doesn't exist */
+ }
+ if (trivx[j]->status != vtx_norm)
+ ntvsh++;
+ else
+ nntvsh++;
+ }
+
+ /* If a vertex isn't valid, or all vertexes are shadowed or not shadowed */
+ if (j <= sdi
+ || ntvsh == (sdi+1)
+ || nntvsh == (sdi+1)) {
+//printf("~1 vtx missing %d, ntvsh %d, nntvxsh %d\n",j <= sdi, ntvsh, nntvsh);
+ continue; /* Skip this triangle */
+ }
+
+ /* If triangle has been done before for this bxcell, skip it. */
+ if (check_trirec(s, &tc, triix)) {
+ continue;
+ }
+
+ /* We've decided to add triangle and test vertex */
+ if (!added) {
+ add_vtxrec_list(&vc, vx, 1); /* Add vertex to list to test against */
+ added = 1;
+ }
+
+ /* Create or re-use test vertex */
+ if (ftlist != NULL) { /* Grab one from free list */
+ tvx = ftlist;
+ ftlist = tvx->tlist;
+ memset((void *)tvx, 0, sizeof(tvxrec));
+
+ } else {
+ if ((tvx = (tvxrec *) rev_calloc(s, 1, sizeof(tvxrec))) == NULL)
+ error("rspl malloc failed - rev tvxrec structs");
+ INCSZ(s, sizeof(tvxrec));
+ }
+
+ tvx->tlist = tlist;
+ tlist = tvx;
+ nitlist++;
+
+ for (f = 0; f < fdi; f++)
+ tvx->v[f] = 0.0;
+
+ for (j = 0; j <= sdi; j++) {
+ if (trivx[j]->status == vtx_norm) {
+ for (f = 0; f < fdi; f++)
+ tvx->v[f] += 0.95/nntvsh * trivx[j]->v[f];
+ } else {
+ for (f = 0; f < fdi; f++)
+ tvx->v[f] += 0.05/ntvsh * trivx[j]->v[f];
+ trivx[j]->cross = 1; /* For diagnostics */
+ }
+ }
+
+ /* Compute distance of test vertex to overall center point squared */
+ tvx->dist = 0.0;
+ for (f = 0; f < fdi; f++) {
+ double tt = tvx->v[f] - s->rev.ocent[f];
+ tvx->dist += tt * tt;
+ }
+
+ /* Note the triangles vertexes indexes */
+ for (j = 0; j <= sdi; j++)
+ tvx->ix[j] = trivx[j]->ix;
+#ifdef REVTABLESTATS
+ notverts++;
+#endif
}
}
- if (f >= fdi) {
-//if (fdi > 1) printf("~1 got a match\n");
- break; /* Found a matching cache entry */
- }
- }
- if (ncp != NULL) {
- rp = ncp->rip;
-//if (fdi > 1) printf("~1 got cache hit hashk = %d, with ref count %d\n\n",hashk, rp[1]);
- rp[2]++; /* Increase reference count */
+ /* Do a first pass for each test vertex, testing against */
+ /* just the triangles that are associated with it's triangle. */
+ /* (This quickly culls the test vertex list size, greatly */
+ /* reducing the time taken in the second pass */
- } else {
- /* This section seems to be the most time consuming part of the nnrev setup. */
+ /* For each test vertex */
+ for (tvx = tlist; tvx != NULL; tvx = tvx->tlist) {
+ double pv[MXRO]; /* Vertex being tested */
+ double de[MXRO]; /* Line delta */
- /* Allocate a cache entry and place it */
- if ((ncp = (nncache *)calloc(1, sizeof(nncache))) == NULL)
- error("rspl malloc failed - rev.nn cach record");
+ clear_trirec(s, &stc);
- for (f = 0; f < fdi; f++) {
- ncp->min[f] = imin[f];
- ncp->max[f] = imax[f];
- }
- ncp->next = nnc[hashk];
- nnc[hashk] = ncp;
+ /* Compute line delta */
+ for (f = 0; f < fdi; f++) {
+ pv[f] = tvx->v[f];
+ de[f] = pv[f] - s->rev.ocent[f];
+ }
- /* Convert the nn destination vertex range into an output value range. */
- for (f = 0; f < fdi; f++) {
- double gw = s->rev.gw[f];
- double gl = s->rev.gl[f];
- rmin[f] = gl + imin[f] * gw;
- rmax[f] = gl + imax[f] * gw;
- }
+ /* Setup line cla and clb */
+ init_line_eq_imp(s, NULL, &cla, clb, s->rev.ocent, de, 0);
+
+ /* For each vertex of the test vertex triangle */
+ for (jj = 0; jj <= sdi; jj++) {
+ assdire *tri; /* Triangle table */
+ float *fp;
+ int fl;
+
+ if ((vx = get_vtxrec(&vc, tvx->ix[jj])) == NULL)
+ error("rev crossing test - failed to get vertex");
+
+ if (vx->status != vtx_norm)
+ continue;
+
+ fp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(fp); /* Edge flags for this vertex */
+ tri = tridir + fl;
+
+ /* For all +ve triangles that use this vertex */
+ for (k = 0; k < tridir[fl].no; k++) {
+ int triix[MXRI+1];
+ vtxrec *trivx[MXRI+1];
+ double v[MXRI+1][MXRO]; /* Triangle vertex values */
+ double gc[MXRO], cc, dw; /* Triangle shadow group info. */
+ int ntvsh = 0; /* Number of verticies shadowed */
+ double bdist = -1.0;
+ double tb[MXRI]; /* Solution point in input space */
+ double xv[MXRO]; /* Solution point in output space */
+ int g, sorv, wsrv; /* Solved & within simplex return value */
+ double dist; /* distance to line origin */
+ double dot; /* dot product of solution to line */
+
+//printf("~1 tri %d: goffs = %s\n", i, debPiv(sdi+1, tri->ti[k].goffs));
+
+ /* Triangle vertex index numbers */
+ triix[0] = vx->ix + tri->ti[k].goffs[0];
+ triix[1] = vx->ix + tri->ti[k].goffs[1];
+ triix[2] = vx->ix + tri->ti[k].goffs[2];
+
+ /* If triangle has been done before for this tvx, skip it. */
+ if (check_trirec(s, &stc, triix)) {
+ continue;
+ }
+
+ /* Triangle vertex index numbers */
+ for (j = 0; j <= sdi; j++) {
+// triix[j] = vx->ix + tri->ti[k].goffs[j];
- /* Do any adjustment of the range needed to acount for the inacuracies */
- /* caused by the vertex quantization. */
- /* (I don't really understand the need for the extra avggw expansion, */
- /* but there are artefacts without this. This size of this sampling */
- /* expansion has a great effect on the performance.) */
+ if ((trivx[j] = get_vtxrec(&vc, triix[j])) == NULL) {
+ break; /* Vertex doesn't exist */
+ }
+ if (trivx[j]->status != vtx_norm)
+ ntvsh++;
+
+ if (trivx[j]->dist > bdist)
+ bdist = trivx[j]->dist;
+ }
+
+ /* If vertex is above triangle, it can't be shadowed */
+ if (tvx->dist > bdist)
+ continue;
+
+ /* If a vertex isn't valid, or all vertexes are shadowed */
+ if (j <= sdi
+ || ntvsh >= (sdi+1)) {
+ continue; /* Skip this triangle */
+ }
+
+ /* If this triangle is the test vertex triangle, skip it */
+ if (tvx->ix[0] == triix[0]
+ && tvx->ix[1] == triix[1]
+ && tvx->ix[2] == triix[2]) {
+ continue;
+ }
+
+ for (j = 0; j <= sdi; j++) {
+ for (f = 0; f < fdi; f++)
+ v[j][f] = trivx[j]->v[f];
+ }
+
+ /* Compute shadow group params of triangle for quick vertex test */
+ comp_shadow_group(s, s->rev.ocent, gc, &cc, &dw, NULL, v, sdi+1);
+
+ /* Do quick check against triangle */
+ if (!shadow_group_vertex(s,
+ s->rev.ocent, gc, cc, dw, tvx->v)) {
+ continue;
+ }
+
+//printf("~1 checking vertex %d at %s dist %f\n",tvx->ix, debPdv(fdi,tvx->v), sqrt(tvx->dist));
+ /* Compute intersection: */
+ wsrv = 0;
+
+ /* Solve line/triangle intersection using same */
+ /* method as vnearest_clip_solve(). */
+
+ /* LHS: ta[sdi][sdi] = cla[sdi][fdi] * vv[fdi][sdi] */
+ /* RHS: tb[sdi] = clb[sdi] - cla[sdi][fdi] * vv_di[fdi] */
+ for (f = 0; f < sdi; f++) {
+ double tt;
+ for (e = 0; e < sdi; e++) {
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * (v[e][g] - v[e+1][g]);
+ ta[f][e] = tt;
+ }
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * v[sdi][g];
+ tb[f] = clb[f] - tt;
+ }
+
+ /* Compute the solution */
+ /* (Solve the simultaneous linear equations A.X = B) */
+// sorv = !solve_se(ta, tb, sdi);
+ sorv = !solve_se_2x2(ta, tb); /* Saves a few % only */
+
+ if (!sorv)
+ continue;
+
+ /* Check that the solution is within the simplex & meets ink limit */
+ if ((wsrv = simple_within_simplex(v, tb, sdi)) != 0) {
+
+ /* Compute the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++)
+ tt += (v[e][f] - v[e+1][f]) * tb[e];
+ xv[f] = tt + v[sdi][f];
+ }
+
+ /* Compute distance to gamut center squared, */
+ /* as well as the dot product */
+ for (dot = dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (xv[f] - s->rev.ocent[f]);
+ dist += tt * tt;
+ dot += de[f] * tt;
+ }
+
+ /* If intersection distance is greater than vertex distance, */
+ /* mark the test vertex as shadowed (== crossed triangle */
+ /* is shadowed) */
+ if (dot > 0.0 && dist > (tvx->dist + EPS)) {
+ tvx->shad = 1;
+ goto next_tvx;
+ }
+ }
+ } /* Next associated triangle */
+ } /* Next vertex of test triangle */
+ next_tvx:;
+ } /* Next test vertex */
+
+ /* Delete shadowed tvx, and sort remaining tlist by distance so */
+ /* that we have a better chance of shadowing it early ? */
{
- double avggw = 0.0;
- for (f = 0; f < fdi; f++)
- avggw += s->rev.gw[f];
- avggw /= (double)fdi;
- for (f = 0; f < fdi; f++) { /* Quantizing range plus extra */
- double gw = s->rev.gw[f];
- rmin[f] -= (0.5 * gw + 0.99 * avggw);
- rmax[f] += (0.5 * gw + 0.99 * avggw);
+ int i;
+ tvxrec **sort, *vx, *nvx;
+
+ /* Create temporary array of pointers to tvxrec's in list */
+ if ((sort = (tvxrec **) rev_calloc(s, nitlist, sizeof(tvxrec *))) == NULL)
+ error("rspl malloc failed - rev tvxrec sort array");
+ INCSZ(s, nitlist * sizeof(tvxrec *));
+
+ for (i = 0, vx = tlist; vx != NULL; vx = nvx) {
+ nvx = vx->tlist;
+ if (!vx->shad) {
+ sort[i++] = vx;
+ } else {
+ /* Put deleted tvxrec on the free list to re-use */
+ vx->tlist = ftlist;
+ ftlist = vx;
+ }
}
- }
-//if (fdi > 1) printf("~1 prime vertex value adjusted range = %f - %f, %f - %f, %f - %fn", rmin[0], rmax[0], rmin[1], rmax[1], rmin[2], rmax[2]);
+ nitlist = i;
+
+ /* Sort the list into ascending distance from center */
+#define HEAP_COMPARE(A,B) (A->dist < B->dist)
+ HEAPSORT(tvxrec *, sort, nitlist)
+#undef HEAP_COMPARE
+
+ /* Re-create the linked list in descending order */
+ tlist = NULL;
+ for (i = 0; i < nitlist; i++) {
+ vx = sort[i];
+ vx->tlist = tlist;
+ tlist = vx;
+ }
+
+ free(sort);
+ DECSZ(s, nitlist * sizeof(tvxrec *));
- /* computue the rev.rev cell grid range we will need to cover to */
- /* get all the cell output ranges that could touch our nn reverse range */
- for (f = 0; f < fdi; f++) {
- double gw = s->rev.gw[f];
- double gl = s->rev.gl[f];
- imin[f] = (int)floor((rmin[f] - gl)/gw);
- if (imin[f] < 0)
- imin[f] = 0;
- else if (imin[f] > rgres_1)
- imin[f] = rgres_1;
- imax[f] = (int)floor((rmax[f] - gl)/gw);
- if (imax[f] < 0)
- imax[f] = 0;
- else if (imax[f] > rgres_1)
- imax[f] = rgres_1;
- cc[f] = imin[f]; /* Set grid starting value */
+#ifdef NEVER
+ printf("sorted test vertex list:\n");
+ for (i = 0, vx = tlist; vx != NULL; vx = vx->tlist, i++)
+ printf("%d: ix %s dist %f\n",i,debPiv(3,vx->ix), sqrt(vx->dist));
+#endif
}
- tcount = s->get_next_touch(s); /* Get next grid touched generation count */
-//if (fdi > 1) printf("~1 Cells to scan = %d - %d, %d - %d, %d - %d\n", imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+ /* Be able to detect triangles already tested */
+ /* from this shadowing bxcell. */
+ clear_trirec(s, &tc);
+
+ /* sort vertexes by descending distance to center point */
+ /* (and also reset list tflag), to detect shadowing early */
+ sort_vtxrec_list(s, &vc);
+
+ /* Check if the test points are shadowed by any triangle */
+ for (vx = vc.vtxlist; vx != NULL; vx = vx->tlist) {
+ assdire *tri; /* Triangle table */
+ float *fp;
+ int fl;
- rp = NULL; /* We always allocate a new list initially */
- for (f = 0; f < fdi;) { /* For all the cells in the min/max range */
- int ii;
- int **nrpp, *nrp; /* Pointer to base of cell list, entry 0 = allocated space */
+ if (vx->status != vtx_norm) // ???
+ continue;
- /* Get pointer to rev.rev[] cell list */
- for (nrpp = s->rev.rev, f = 0; f < fdi; f++)
- nrpp += cc[f] * s->rev.coi[f];
+ fp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(fp); /* Edge flags for this vertex */
+ tri = tridir + fl;
- if ((nrp = *nrpp) == NULL)
- goto next_range_list; /* This rev.rev[] cell is empty */
+ /* For all +ve triangles that use this vertex */
+ for (k = 0; k < tridir[fl].no; k++) {
+ int triix[MXRI+1];
+ vtxrec *trivx[MXRI+1];
+ double v[MXRI+1][MXRO]; /* Triangle vertex values */
+ double gc[MXRO], cc, dw; /* Triangle shadow group info. */
+ int ntvsh = 0; /* Number of verticies shadowed */
+ double bdist = -1.0;
+//printf("~1 tri %d: goffs = %s\n", i, debPiv(sdi+1, tri->ti[k].goffs));
-//if (fdi > 1) printf("~1 adding list from cell %d, list length %d\n",nrpp - s->rev.rev, nrp[0]);
- /* For all the fwd cells in the rev.rev[] list */
- for(nrp += 3; *nrp != -1; nrp++) {
- int ix = *nrp; /* Fwd cell index */
- float *fcb = s->g.a + ix * s->g.pss; /* Pntr to base float of fwd cell */
+ /* Triangle details */
+ for (j = 0; j <= sdi; j++) {
+ triix[j] = vx->ix + tri->ti[k].goffs[j];
- if (TOUCHF(fcb) >= tcount) { /* If we seen visited this fwd cell before */
-//if (fdi > 1) printf("~1 skipping cell %d because we alread have it\n",ix);
+ if ((trivx[j] = get_vtxrec(&vc, triix[j])) == NULL) {
+ break; /* Vertex doesn't exist */
+ }
+ if (trivx[j]->status != vtx_norm)
+ ntvsh++;
+
+ if (trivx[j]->dist > bdist)
+ bdist = trivx[j]->dist;
+ }
+
+ /* If a vertex isn't valid, or all vertexes are shadowed */
+ if (j <= sdi
+ || ntvsh >= (sdi+1)) {
+ continue; /* Skip this triangle */
+ }
+
+ /* If triangle has been done before for this bxcell, skip it. */
+ if (check_trirec(s, &tc, triix)) {
continue;
}
- TOUCHF(fcb) = tcount; /* Touch it so we will skip it next time */
- /* Compute the range of output values this cell covers */
- for (f = 0; f < fdi; f++) /* Init output min/max */
- min[f] = max[f] = fcb[f];
+ for (j = 0; j <= sdi; j++) {
+ for (f = 0; f < fdi; f++)
+ v[j][f] = trivx[j]->v[f];
+ }
- /* For all other grid points in the fwd cell cube */
- for (ee = 1; ee < (1 << di); ee++) {
- float *gt = fcb + s->g.fhi[ee]; /* Pointer to cube vertex */
-
- /* Update bounding box for this grid point */
+ /* Compute shadow group params of triangle for quick vertex test */
+ comp_shadow_group(s, s->rev.ocent, gc, &cc, &dw, NULL, v, sdi+1);
+
+ /* For all test vertexes */
+ for (tvx = tlist; tvx != NULL; tvx = tvx->tlist) {
+ double pv[MXRO]; /* Vertex being tested */
+ double de[MXRO]; /* Line delta */
+ double tb[MXRI]; /* Solution point in input space */
+ double xv[MXRO]; /* Solution point in output space */
+ int g, sorv, wsrv; /* Solved & within simplex return value */
+ double dist; /* distance to line origin */
+ double dot; /* dot product of solution to line */
+
+ /* If vertex is above triangle, it can't be shadowed */
+ if (tvx->dist > bdist)
+ continue;
+
+ /* If we have already determined this one is shadowed */
+ if (tvx->shad)
+ continue;
+
+ /* If this vertex for this triangle, skip it */
+ if (tvx->ix[0] == triix[0]
+ && tvx->ix[1] == triix[1]
+ && tvx->ix[2] == triix[2]) {
+ continue;
+ }
+
+ /* Do quick check against triangle */
+ if (!shadow_group_vertex(s, s->rev.ocent, gc, cc, dw, tvx->v))
+ continue;
+//printf("~1 checking vertex %d at %s dist %f\n",tvx->ix, debPdv(fdi,tvx->v), sqrt(tvx->dist));
+ /* Compute intersection: */
+ wsrv = 0;
+
+ /* Compute line delta */
for (f = 0; f < fdi; f++) {
- if (min[f] > gt[f])
- min[f] = gt[f];
- if (max[f] < gt[f])
- max[f] = gt[f];
+ pv[f] = tvx->v[f];
+ de[f] = pv[f] - s->rev.ocent[f];
}
- }
-//if (fdi > 1) printf("~1 cell %d range = %f - %f, %f - %f, %f - %f\n", ix, min[0], max[0], min[1], max[1], min[2], max[2]);
+ /* Setup line cla and clb */
+ init_line_eq_imp(s, NULL, &cla, clb, s->rev.ocent, de, 0);
- /* See if this fwd cell output values overlaps our region of interest */
- for (f = 0; f < fdi; f++) {
- if (min[f] > rmax[f]
- || max[f] < rmin[f]) {
- break; /* Doesn't overlap */
+ /* Solve line/triangle intersection using same */
+ /* method as vnearest_clip_solve(). */
+
+ /* LHS: ta[sdi][sdi] = cla[sdi][fdi] * vv[fdi][sdi] */
+ /* RHS: tb[sdi] = clb[sdi] - cla[sdi][fdi] * vv_di[fdi] */
+ for (f = 0; f < sdi; f++) {
+ double tt;
+ for (e = 0; e < sdi; e++) {
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * (v[e][g] - v[e+1][g]);
+ ta[f][e] = tt;
+ }
+ for (tt = 0.0, g = 0; g < fdi; g++)
+ tt += cla[f][g] * v[sdi][g];
+ tb[f] = clb[f] - tt;
}
- }
+
+ /* Compute the solution */
+ /* (Solve the simultaneous linear equations A.X = B) */
+// sorv = !solve_se(ta, tb, sdi);
+ sorv = !solve_se_2x2(ta, tb); /* Saves a few % only */
- if (f < fdi) {
-//if (fdi > 1) printf("~1 skipping cell %d because we doesn't overlap\n",ix);
- continue; /* It doesn't overlap */
+ /* If it was solved */
+ if (sorv) {
+
+ /* Check that the solution is within the simplex & ink limit */
+ if ((wsrv = simple_within_simplex(v, tb, sdi)) != 0) {
+
+ /* Compute the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++)
+ tt += (v[e][f] - v[e+1][f]) * tb[e];
+ xv[f] = tt + v[sdi][f];
+ }
+
+ /* Compute distance to gamut center squared, */
+ /* as well as the dot product */
+ for (dot = dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (xv[f] - s->rev.ocent[f]);
+ dist += tt * tt;
+ dot += de[f] * tt;
+ }
+//printf("~1 intersection at %s dist %f\n", debPdv(fdi,xv), sqrt(dist));
+
+ /* If intersection distance is greater than vertex distance, */
+ /* mark the test vertex as shadowed (== crossed triangle */
+ /* is shadowed) */
+ if (dot > 0.0 && dist > (tvx->dist + EPS)) {
+ tvx->shad = 1;
+ }
+ }
+ }
+ } /* Next test vertex */
+ } /* Next triangle from vertex */
+ } /* Next vertex */
+
+ /* Go through test vertex results, and if it is un-shadowed, */
+ /* mark all the corresponding triangle vertexes as un-shadowed. */
+ /* For all test vertexes */
+ for (tvx = tlist; tvx != NULL; tvx = ntvx) {
+ ntvx = tvx->tlist;
+
+ /* If the test point wasn't shadowed, assume it */
+ /* is part of the gamut surface, and mark all its */
+ /* vertexes as valid. */
+ if (!tvx->shad) {
+ for (j = 0; j <= sdi; j++) {
+ if ((vx = get_vtxrec(&vc, tvx->ix[j])) == NULL)
+ error("rev - failed to locate vertex %d\n",tvx->ix[j]);
+
+ if (vx->status != vtx_norm) {
+ vx->pres = 1; /* Don't treat it as deleted */
+ }
}
-
-//if (fdi > 1) printf("~1 adding fwd index %d to list\n",ix);
-//if (fdi > 1) printf("~1 cell %d range = %f - %f, %f - %f, %f - %f\n", ix, min[0], max[0], min[1], max[1], min[2], max[2]);
-#ifdef DEBUG
- fwdcells++;
-#endif
- /* It does, add it to our new list */
- if (rp == NULL) {
- if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
- error("rspl malloc failed - rev.nngrid entry");
- INCSZ(s, 6 * sizeof(int));
- rp[0] = 6; /* Allocation */
- rp[1] = 4; /* Next free Cell */
- rp[2] = 1; /* reference count */
- rp[3] = ix;
- rp[4] = -1;
+ }
+ /* Put all the tvxrec's on the free list to re-use */
+ tvx->tlist = ftlist;
+ ftlist = tvx;
+ }
+ tlist = NULL;
+ nitlist = 0;
+
+ /* If the preseved vertexes have been deleted from the bx list, */
+ /* add them back in again */
+ if (bx->dl != NULL) {
+ for (nrp = rp = bx->dl+3; *rp != -1; rp++) {
+ vtxrec *vx;
+
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ continue; /* Hmm. */
+
+ /* If preserved, transfer it to the active bx list */
+ if (vx->pres) {
+ add2indexlist(s, &bx->sl, *rp, 0);
+
+ /* Leave it in deleted list */
} else {
- int z = rp[1], ll = rp[0];
- if (z >= (ll-1)) { /* Not enough space */
- INCSZ(s, ll * sizeof(int));
- ll *= 2;
- if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
- error("rspl realloc failed - rev.grid entry");
- rp[0] = ll;
- }
- rp[z++] = ix;
- rp[z] = -1;
- rp[1] = z;
+ *nrp++ = *rp;
}
- } /* Next fwd cell in list */
-
- /* Increment index */
- next_range_list:;
- for (f = 0; f < fdi; f++) {
- if (++cc[f] <= imax[f])
- break; /* No carry */
- cc[f] = imin[f];
}
+ *nrp = -1;
+ bx->dl[1] = nrp - bx->dl;
+
+ /* We don't need the deleted list now */
+ free_indexlist(s, &bx->dl);
}
- ncp->rip = rp; /* record nnrev cell in cache */
-#ifdef DEBUG
- cellinrevlist++;
-#endif
-//if (fdi > 1) printf("~1 adding cache entry with hashk = %d\n\n",hashk);
+ } /* Next bxcell */
+
+ /* Free up tvxrec's */
+ while (ftlist != NULL) {
+ tvxrec *this = ftlist;
+ ftlist = ftlist->tlist;
+ free(this);
+ DECSZ(s, sizeof(tvxrec));
}
- /* Put the resulting list in place */
- if (prime != NULL)
- prime->clist = rp; /* Save it untill we get rid of the primes */
- else
- *rpp = rp;
+#ifdef REVTABLESTATS
+ /* Count the number of preserved vertexes */
+ for (i = 0; i < vc.hash_size; i++) {
+ for (vx = vc.hash[i]; vx != NULL; vx = vx->hlink) {
+ if (vx->pres)
+ nopreserved++;
+ }
+ }
+ printf("%d crossed triangles tested\n",notverts);
+ printf("%d hidden verticies retained for crossed triangles\n",nopreserved);
+ printf("Took %f secs to preserving crossing triangless\n",0.001 * (msec_time()-lmsec));
+#endif
+ } /* End of preserve shadowed triangles */
-//if (*rpp == NULL) printf("~1 problem: we ended up with no list or prime struct at cell %d\n",i);
+ /* = = = = = = = = = = = = = = = = = = */
+ /* Delete any shadowed vertexes, and remove any empty bxcells. */
+ for (pbx = &s->rev.surflist, bx = *pbx; bx != NULL; bx = nbx) {
+ int *rp, *nrp;
-#ifdef NEVER
-/* Sanity check the list, to see if the list cells corner contain an output value */
-/* that is at least closer to the target than the prime. */
-if (prop != NULL) {
- int *tp = rp;
- double bdist = 1e60;
- double bdist2 = 1e60;
- double vx[MXRO]; /* Vertex location */
- double px[MXRO]; /* Prime location */
- double cl[MXRO]; /* Closest output value from list */
- double acl[MXRO]; /* Absolute closest output value */
- double dst; /* Distance to prime */
- int ti;
-
- primevx *prm = (primevx *) *(s->rev.nnrev + prop->cix);
- for (f = 0; f < fdi; f++) {
- double gw = s->rev.gw[f];
- double gl = s->rev.gl[f];
- vx[f] = gl + prop->gc[f] * gw;
- px[f] = gl + prm->gc[f] * gw;
- }
+ /* Delete all the shadowed or delted vertexes from bxcell list, */
+ /* unless they are preserved because they are part of a crossed triangle. */
+ for (nrp = rp = bx->sl+3; *rp != -1; rp++) {
+ vtxrec *vx;
- for(tp++; *tp != -1; tp++) {
- int ix = *tp; /* Fwd cell index */
- float *fcb = s->g.a + ix * s->g.pss; /* Pntr to base float of fwd cell */
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ continue; /* Hmm. */
- for (ee = 0; ee < (1 << di); ee++) {
- double ss;
- float *gt = fcb + s->g.fhi[ee]; /* Pointer to cube vertex */
-
- for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = vx[f] - gt[f];
- ss += tt * tt;
+ /* Keep all the un-shadowed or preserved vertexes */
+ if (vx->status == vtx_norm
+ || vx->pres) {
+ *nrp++ = *rp;
+ } else {
+ del_vtxrec_hash(&vc, vx->ix);
+ }
}
- if (ss < bdist) {
- bdist = ss;
- for (f = 0; f < fdi; f++)
- cl[f] = gt[f];
+ *nrp = -1;
+ bx->sl[1] = nrp - bx->sl;
+
+ if (bx->sl == NULL /* Missing or empty fwd index list */
+ || bx->sl[1] == 3
+ ) {
+ /* Remove it from vflag array */
+ if (s->rev.rev[bx->ix] != NULL) {
+ vflag[bx->ix] = (vflag[bx->ix] & ~0xf) | 1; /* Not surface and done */
+ } else {
+ vflag[bx->ix] = (vflag[bx->ix] & ~0xf) | 0; /* Not surface and empty */
+ }
+
+ /* Remove it from hash */
+ rem_bxcell_hash(s, bx->ix);
+
+ /* Free fwd index list (none are shared at this point) */
+ if (bx->sl != NULL)
+ free_indexlist(s, &bx->sl);
+
+ /* Remove it from surface list */
+ *pbx = nbx = bx->slist;
+
+ /* Free it */
+ del_bxcell(s, bx);
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ nrscells++;
+#endif
+
+ } else { /* Move on to next */
+ pbx = &bx->slist;
+ nbx = bx->slist;
}
}
- }
- bdist = sqrt(bdist);
- dst = sqrt(prop->dsq);
- /* Lookup best distance to any output value */
- if (dst < bdist) {
- float *gt;
- for (ti = 0, gt = s->g.a; ti < s->g.no; ti++, gt += s->g.pss) {
- double ss;
-
- for (ss = 0.0, f = 0; f < fdi; f++) {
- double tt = vx[f] - gt[f];
- ss += tt * tt;
- }
- if (ss < bdist2) {
- bdist2 = ss;
- for (f = 0; f < fdi; f++)
- acl[f] = gt[f];
+ /* Add extra over ink limit vertexes. */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int sdi = 1; /* sub-simplexes are edges */
+ int *crp, *rp, *nrp;
+ int ttouch;
+ vtxrec *vx, *nvx;
+
+ /* Add over ink limit vertexes, so that fwd cells will straddle */
+ /* the ink limit boundary. */
+ /* Do this by checking all vertexes edge neighbors, */
+ /* and adding any that are over the ink limit. */
+ /* (Only do this for bx cells that are known to contain */
+ /* over ink limit verticies.) */
+ if (s->limiten && vflag[bx->ix] & 0x10) {
+ int *rp;
+//printf("~1 ink limitin is enabled bx %d\n", bx->ix);
+
+ for (rp = bx->sl+3; *rp != -1; rp++) {
+ float *vp, *evp;
+ int fl;
+ assdire *edg; /* Edge table */
+
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ continue; /* Hmm. */
+
+ /* Don't do this for preserved or oil vertexes */
+ if (vx->status != vtx_norm)
+ continue;
+
+ vp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(vp); /* Edge flags for this vertex */
+ edg = edgdir + fl;
+
+#ifdef CHECK_NNLU
+ if (vp[-1] > s->limitv)
+ error("Thinned vertex %d is over ink limit!",vx->ix);
+#endif
+
+//printf("~1 fl %d = 0o%o, no edges %d\n",fl, fl, edg->no);
+
+ /* For all possible edges that use this vertex */
+ for (i = 0; i < edgdir[fl].no; i++) {
+ int eix;
+
+//printf("~1 edg %d: goffs = %s\n", i, debPiv(sdi+1, edg->ti[i].goffs));
+
+ /* Edge vertex index number of other vertex */
+ if (edg->ti[i].goffs[0] != 0)
+ eix = vx->ix + edg->ti[i].goffs[0];
+ else
+ eix = vx->ix + edg->ti[i].goffs[1];
+
+ evp = s->g.a + eix * s->g.pss; /* Other vertex in fwd grid */
+
+//printf(" Checking edge %d (%f) -> %d (%f)\n", vx->ix, vp[-1], eix, evp[-1]);
+
+ /* If over limit, add it to the list */
+ if (evp[-1] > s->limitv) {
+//printf("~1 added over ink limit vertex %d\n",eix);
+
+ if (get_vtxrec(&vc, eix) != NULL)
+ continue; /* Added by another bx */
+ nvx = new_vtxrec(s, &vc, eix);
+ nvx->status = vtx_oil;
+ add2indexlist(s, &bx->sl, eix, 0);
+ }
+ }
+ }
}
}
- }
- bdist2 = sqrt(bdist2);
- if (dst < bdist) {
- printf("~1 vertex %d has worse distance to values than prime\n",i);
- printf("~1 vertex loc %f %f %f\n", vx[0], vx[1], vx[2]);
- printf("~1 prime loc %f %f %f, dist %f\n", px[0], px[1], px[2],dst);
- printf("~1 closest loc %f %f %f, dist %f\n", cl[0], cl[1], cl[2],bdist);
- printf("~1 abs clst loc %f %f %f, dist %f\n", acl[0], acl[1], acl[2], bdist2);
+#ifdef REVTABLESTATS
+ /* Count the number of over ink limit vertexes */
+ for (i = 0; i < vc.hash_size; i++) {
+ vtxrec *vx;
+ for (vx = vc.hash[i]; vx != NULL; vx = vx->hlink) {
+ if (vx->status == vtx_oil)
+ naoulvtxs++;
+ }
}
-}
-#endif // NEVER
+#endif
+
+#ifdef REVVRML
+ /* Plot final vertex surface before converting to fwcells */
+ /* Vtx ix tag ? Deleted vtxs ? Added vtxs ? Preserved vtxs ? oil vtxs ? bxcells ? Wait ? */
+ if (1) plot_vtx_surface(s, 0, 0, 0, 1, 1, 0, 0, &vc, edgdir);
+#endif /* REVVRML */
+
+ /* Convert vertexes to cube lists */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int sdi = 1; /* sub-simplexes are edges */
+ int *crp, *rp, *nrp;
+ int ttouch;
+ vtxrec *vx, *nvx;
+
+ /* If there are no vertexes left (i.e. they have all been deleted) */
+ /* Don't try and convert to fwd cells. */
+ if (bx->sl == NULL || bx->sl[1] == 3) {
+ bx->status = bx_conv;
+ continue;
+ }
+
+ /* Create cach list of vxrec's for just this nnrev[] */
+ clear_vtxrec_lists(s, &vc);
- if (prop != NULL && prime == NULL) {
- free(prop);
+ /* Add all this bxcell verticies to cache and list */
+ for (rp = bx->sl+3; *rp != -1; rp++) {
+ vx = new_vtxrec(s, &vc, *rp);
+ add_vtxrec_list(&vc, vx, 0);
}
- next_vertex:;
- DC_INC(gg);
+ /* Convert fwd index list into fwd cells list. Do this in */
+ /* a way that minimizes the number of cells needed while still */
+ /* ensuring that there is 2 dimensional connectivity for all the vertexes. */
+
+ /* Count number of touches if we add a cube for each prime vertex */
+//printf("~1 counting number of touches\n");
+ crp = bx->sl;
+ i = 0;
+ for (rp = crp+3; *rp != -1; rp++) {
+ vtxrec *vx;
+
+ if ((vx = get_vtxrec(&vc, *rp)) == NULL)
+ error("get_vtxrec() failed on surface vtx");
+
+ i++;
+
+ /* For each vertex of cube placed at vx->cix */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = vx->cix + s->g.hi[ee];
+ vtxrec *nx;
+
+ if ((nx = get_vtxrec(&vc, vix)) != NULL)
+ vx->tcount++;
+ }
+ }
+//printf("there were %d vertexes",i);
+
+//printf("~1 adding cells in order of touch count\n");
+ /* Add cells in order of touch count, i.e. from most necessary */
+ /* to least necessary. Allow a maximum touch of 4, to ensure */
+ /* 2 dimensional connectivity of the fwd cells */
+ nrp = NULL;
+ i = 0;
+ for (ttouch = 1; ; ttouch++) {
+ int more = 0;
+//printf("~1 ttouch = %d\n",ttouch);
+ for (rp = crp+3; *rp != -1; rp++) {
+ vtxrec *vx = get_vtxrec(&vc, *rp);
+
+ if (vx->tcount == 0)
+ continue;
+
+ more = 1;
+ if (vx->tcount > ttouch)
+ continue;
+
+ /* For each cube vertex placed at vx->cix */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = vx->cix + s->g.hi[ee];
+ vtxrec *nx;
+
+ /* Track touch count on creating cells, and */
+ /* clear vertexes that have reached 4, */
+ /* so that they don't get any more */
+ if ((nx = get_vtxrec(&vc, vix)) != NULL) {
+//printf("bx %d, adding fwcell vertex %d for vertex %d\n",bx->ix,vix,*rp);
+ vx->acount++;
+ if (vx->acount >= 4)
+ vx->tcount = 0;
+ }
+ }
+ i++;
+ add2indexlist(s, &nrp, vx->cix, 0);
+ }
+ if (!more)
+ break;
+ }
+//printf(", now %d fwdcells\n",i);
+//printf("~1 replacing vertex list with cell list\n");
+
+ if (nrp == NULL)
+ error("Surface list bxcell ix %d has no fwd cells",bx->ix);
+
+ /* Replace vertex list with cell list */
+ free_indexlist(s, &bx->sl);
+ bx->sl = nrp;
+
+ if (bx->sl == NULL)
+ error("Surcface cell nnrev[%d] is empty!\n",bx->ix);
+ bx->status = bx_conv;
+ }
+
+ if (cla != NULL)
+ free_dmatrix(cla, 0, fdi-1, 0, fdi);
+ free_trirec(s, &stc);
+ free_trirec(s, &tc);
+ free_vtxrec_list(s, &vc);
+ free_assdir(s, edgdir);
+ free_assdir(s, tridir);
+ }
+
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (fdi > 1) {
+ bxcell *bx;
+ int surfcelldepth = 0, surfcells = 0;
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ if (bx->sl == NULL
+ || bx->sl[1] == 3)
+ continue;
+ surfcells++;
+ surfcelldepth += bx->sl[1]-3;
}
- DBG(("freeing up the prime seed structurs\n"));
- /* Finaly convert all the prime verticies to cell lists */
- /* Free up all the prime seed structures */
- for (;plist != NULL; ) {
- primevx *prime, *next = plist->next;
- int **rpp;
+ printf("%d/%d surface cells\n",surfcells,rgno);
+ printf("%d/%d non-surface cells\n",ingamutcells,rgno);
+ printf("%d/%d empty cells\n",emptycells,rgno);
+ printf("%d/%d used cells in rev[]\n",revcells,rgno);
+ printf("%f average rev[] list length\n",(double)revcelldepth/(double)revcells);
+ printf("%f average nnrev[] surface list length\n",(double)surfcelldepth/(double)surfcells);
+ printf("%d added surface cells\n",nascells);
+ printf("%d removed surface cells\n",nrscells);
+ printf("%d added over ink limit vertexes\n",naoulvtxs);
+ }
+#endif
- rpp = s->rev.nnrev + plist->ix;
- if ((prime = (primevx *)(*rpp)) != NULL) {
- if (prime->clist != NULL) /* There is a nn list for this cell */
- *rpp = prime->clist;
+#ifdef REVVRML
+ /* Plot the thinned surface fwd cells */
+ /* fwd cell base ix's ? bxcells ? Wait ? */
+ if (1 && fdi > 1) plot_fxcell_surface(s, 0, 0, 0);
+#endif /* REVVRML */
+
+ /* Fill the non-surface nnrev array from the surface list. */
+ {
+ bxcell *seedlist = NULL; /* Linked list of active seeds */
+ bxcell *seedlistend = NULL; /* Last item on seedlist */
+ bxcell *xlist = NULL; /* Linked list of cells being searched */
+ bxcell *xlistend = NULL; /* Last item on xlist */
+ bxcell *tlist; /* Linked list of cells being considered as soln. */
+ double emax; /* Current smallest estimated max weigted distance */
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ unsigned long smsec = msec_time();
+#endif
+
+ DBG(("Filling in rev.nnrev[] grid\n"));
+
+ /* Start the seeding of the nnrev[] array with all the surface cells */
+ {
+ bxcell *ss;
+
+ for (ss = s->rev.surflist; ss != NULL; ss = ss->slist) {
+ /* Add to end of seedlist */
+ ss->flist = NULL;
+ if (seedlist == NULL)
+ seedlist = ss;
else
- *rpp = NULL;
- free(prime);
- } else {
- error("assert, prime cell %d was empty",plist->ix);
+ seedlistend->flist = ss;
+ seedlistend = ss;
+
+ vflag[ss->ix] |= 1; /* They are on seed list, so will be filled */
}
- plist = next;
}
-#ifdef DEBUG
- DBG(("sanity check that all rev accell cells are filled\n"));
- DC_INIT(gg);
- for (i = 0; i < rgno; i++) {
- for (f = 0; f < fdi; f++) {
- if (gg[f] > rgres_1) { /* Vertex outside bwd cell range, */
- goto next_vertex3;
+ /* While there are nnrev[] cells to fill */
+ while (seedlist != NULL) {
+ DCOUNT(cc, MXRO, fdi, -1, -1, 2); /* bwd neighborhood offset counter */
+ int nix; /* Neighbor offset index */
+ bxcell *ss, *tx;
+
+ tx = seedlist; /* Remove target cell from front of seed list */
+ seedlist = tx->flist;
+
+ if (s->rev.nnrev[tx->ix] != NULL)
+ error("nncel[%d] in seed list is not empty\n",tx->ix);
+
+#ifdef CHECK_NNLU
+ if (tx->ss == NULL || (vflag[tx->ss->ix] & 2) == 0 ) {
+ if (tx->ss == NULL)
+ printf("nnrev[%d] has NULL seed\n",tx->ix);
+ else
+ printf("nnrev[%d] has seed %d with flag %x != 3\n",tx->ix,tx->ss->ix, vflag[tx->ss->ix]);
+ }
+#endif
+
+ DBG(("Doing nnrev[%d] vflag %x co %s\n",tx->ix, vflag[tx->ix], debPiv(s->fdi, tx->gc)));
+//printf("Doing nnrev[%d] vflag %x co %s\n",tx->ix, vflag[tx->ix], debPiv(s->fdi, tx->gc));
+
+ emax = 1e200; /* Smallest emax */
+ ss = tx->ss; /* Search start cell */
+ ss->tix = tx->ix; /* Mark this cell as being in search list */
+
+ /* Make start cell the only entry in the search list */
+ ss->xlist = NULL;
+ xlist = ss;
+ xlistend = ss;
+
+ /* Clear the solution list */
+ tlist = NULL;
+
+ /* Note that filling an nnrev[] cell using a seeded search may miss fw cells */
+ /* that should be in it, if they are in physically dis-continuous locations */
+ /* due to gamut hull convexity. LCh weighting will reduce this somewhat, and */
+ /* discontinuity is rarely a desired characteristic of a color conversion, so */
+ /* we are ignoring this issue for now. */
+
+ /* While there are cells to search for solutions */
+ while (xlist != NULL) {
+ double em, ex;
+
+ ss = xlist; /* Remove next search cell from linked list */
+ xlist = xlist->xlist;
+
+ /* Check if this cell could be in solution */
+ em = nn_grpgrp_est(s, &ex, &tx->g, &ss->g);
+ ss->emin = em;
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ nnrevcellsearch++;
+#endif
+
+ DBG(("Searching rev[%d] co %s, em %f, ex %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, ex));
+//printf("Searching rev[%d] co %s, em %f, ex %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, ex);
+
+ if (em < emax) { /* Yes */
+
+ /* Add it to the solution list */
+ ss->tlist = tlist;
+ tlist = ss;
+
+ DBG(("Adding it to solution list\n"));
+
+ /* Update smallest maximum */
+ /* (Will cull existing bxcell solutions with emin > emax later) */
+ if (ex < emax)
+ emax = ex;
+
+ /* Explore all neighbours, and add any surface cells that haven't been */
+ /* searched for this target yet. */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ bxcell *nbx;
+
+ nix = ss->ix;
+ for (f = 0; f < fdi; f++) {
+ nn[f] = ss->gc[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= rgres)
+ break; /* Out of bounds */
+ nix += cc[f] * s->rev.coi[f];
+ }
+ if (f < fdi || nix == ss->ix) {
+//printf("Rejecting search neigbor co %s because out of bounds or current cell\n",debPiv(s->fdi,cc));
+ goto next_neighbor;
+ }
+
+ /* We only search surface bxcells */
+ if ((vflag[nix] & 2) == 0) {
+//printf("Rejecting search neigbor nnrev[%d] co %s because flags = %x\n",nix, debPiv(s->fdi, cc),vflag[nix]);
+ goto next_neighbor;
+ }
+
+ /* If neighbor is in bounds, and a surface bxcell*/
+ {
+
+ /* Expect all all surface bxcells to be in cache */
+ if ((nbx = get_surface_bxcell(s, nix)) == NULL)
+ error("rspl rev get_surface_bxcell %d failed",nix);
+
+ /* If not already in search list */
+ if (nbx->tix != tx->ix) {
+// DBG(("Adding search neigbor nnrev[%d] co %s to search list\n",nbx->ix, debPiv(s->fdi, nbx->gc)));
+//printf("Adding search neigbor nnrev[%d] co %s to search list\n",nbx->ix, debPiv(s->fdi, nbx->gc));
+ /* Add neigbor to end of search list */
+ nbx->tix = tx->ix; /* Is now in search list */
+ nbx->xlist = NULL;
+ if (xlist == NULL)
+ xlist = nbx;
+ else
+ xlistend->xlist = nbx;
+ xlistend = nbx;
+ }
+//else
+//printf("Rejecting search neigbor nnrev[%d] co %s because already in list\n",nbx->ix, debPiv(s->fdi, nbx->gc));
+ }
+ next_neighbor:;
+ DC_INC(cc);
+ }
+ }
+//else
+//printf("Rejected rev[%d] co %s, because em %f >= emax %f\n",ss->ix, debPiv(s->fdi, ss->gc), em, emax);
+ }
+
+ /* Create the nnrev[] list from the candidate bxcell solutions */
+ if (tlist != NULL) {
+ create_nnrev_list(s, tx, tlist, emax);
+ }
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ nnrevcells++;
+ nnrevcelldepth += s->rev.nnrev[tx->ix][1]-3;
+ if (s->rev.nnrev[tx->ix][1]-3 > nnmxrevcelldepth)
+ nnmxrevcelldepth = s->rev.nnrev[tx->ix][1]-3;
+#endif
+
+ /* If this was a super-cell, explore the 2nd row around this cell, */
+ /* and locate any cells not on the seeding list */
+ if (tx->scell != NULL) {
+ DCOUNT(sc, MXRO, fdi, -3, -3, 4);
+ DC_INIT(sc);
+ while (!DC_DONE(sc)) {
+ int co[MXRO];
+ int ok = 0;
+ int nix = tx->ix;
+
+ for (f = 0; f < fdi; f++) {
+ co[f] = tx->gc[f] + sc[f];
+ if (co[f] < 0 || co[f] >= s->rev.res)
+ break;
+ nix += sc[f] * s->rev.coi[f];
+ if (sc[f] == -3 || sc[f] == 3)
+ ok = 1; /* Just surface of +/- 2 */
+ }
+ if (!ok && sc[0] == -2)
+ sc[0] = 2; /* Skip center */
+
+ /* Put this cell on list and stop searching. */
+ if (f >= fdi && (vflag[nix] & 1) == 0) {
+
+ if ((vflag[nix] & 2) != 0) { /* If un-filled surface bxcell */
+ /* Get surface bxcell from cache index for seed */
+ if ((ss = get_surface_bxcell(s, nix)) == NULL)
+ error("rspl rev get_surface_bxcell %d failed #2, vflag = %x",nix,vflag[nix]);
+//printf("Fetched surface bxcell seed %d vflag %x\n",ss->ix, vflag[ss->ix]);
+ } else { /* If un-filled nnrev */
+ if (get_surface_bxcell(s, nix) != NULL)
+ error("vflag[%d] = %x, but cell is in surface list hash\n");
+
+ /* Create new temporary (non-surface) bxcell seed. */
+ /* If we are sufficiently far from the seed point, */
+ /* a super-cell to improve seeding performance will be created. */
+ ss = new_bxcell(s, nix, co, tx->ss, tx->sdist, vflag);
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (tx->scell != NULL)
+// nnsuperfill += tx->scell[3]-3;
+ nnsuperfill++;
+ else
+ nnsinglefill++;
+#endif
+//printf("Created temporary seed bxcell %d vflag %x\n",ss->ix, vflag[ss->ix]);
+ }
+ DBG(("Adding seed neighbor nnrev[%d] vflag %x co %s to seed list\n",ss->ix, vflag[ss->ix], debPiv(s->fdi, ss->gc)));
+//printf("Adding seed neighbor nnrev[%d] vflag %x co %s to seed list\n",ss->ix, vflag[ss->ix], debPiv(s->fdi, ss->gc));
+
+ /* Add to end of seedlist */
+ ss->flist = NULL;
+ if (seedlist == NULL)
+ seedlist = ss;
+ else
+ seedlistend->flist = ss;
+ seedlistend = ss;
+ vflag[ss->ix] |= 1; /* This is on seed list, so will be filled */
+ }
+ DC_INC(sc);
}
+ } else {
+ /* Explore neighbours, and add any nnrev[] cells that haven't been */
+ /* put on the seed list yet. */
+ for (f = 0; f < fdi; f++)
+ cc[f] = tx->gc[f];
+ nix = tx->ix;
+
+ for (ff = 0; ff < (fdi << 1); ff++) {
+ f = ff >> 1; /* Dimension being explored */
+
+ cc[f] += (ff & 1) ? 1 : -1;
+ nix += (ff & 1) ? s->rev.coi[f] : -s->rev.coi[f];
+
+ /* If found unfilled nnrev[] cell */
+ if (cc[f] >= 0 && cc[f] < rgres && (vflag[nix] & 1) == 0) {
+
+ if ((vflag[nix] & 2) != 0) { /* If un-filled surface bxcell */
+ /* Get surface bxcell from cache index for seed */
+ if ((ss = get_surface_bxcell(s, nix)) == NULL)
+ error("rspl rev get_surface_bxcell %d failed #2, vflag = %x",nix,vflag[nix]);
+//printf("Fetched surface bxcell seed %d vflag %x\n",ss->ix, vflag[ss->ix]);
+ } else { /* If un-filled nnrev */
+ if (get_surface_bxcell(s, nix) != NULL)
+ error("vflag[%d] = %x, but cell is in surface list hash\n");
+
+ /* Create new temporary (non-surface) bxcell seed. */
+ /* If we are sufficiently far from the seed point, */
+ /* a super-cell to improve seeding performance will be created. */
+ ss = new_bxcell(s, nix, cc, tx->ss, tx->sdist, vflag);
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (tx->scell != NULL)
+// nnsuperfill += tx->scell[3]-3;
+ nnsuperfill++;
+ else {
+ nnsinglefill++;
+ }
+#endif
+//printf("Created temporary seed bxcell %d vflag %x\n",ss->ix, vflag[ss->ix]);
+ }
+ DBG(("Adding seed neighbor nnrev[%d] vflag %x co %s to seed list\n",ss->ix, vflag[ss->ix], debPiv(s->fdi, ss->gc)));
+//printf("Adding seed neighbor nnrev[%d] vflag %x co %s to seed list\n",ss->ix, vflag[ss->ix], debPiv(s->fdi, ss->gc));
+
+ /* Add to end of seedlist */
+ ss->flist = NULL;
+ if (seedlist == NULL)
+ seedlist = ss;
+ else
+ seedlistend->flist = ss;
+ seedlistend = ss;
+ vflag[ss->ix] |= 1; /* This is on seed list, so will be filled */
+ }
+
+ cc[f] -= (ff & 1) ? 1 : -1;
+ nix -= (ff & 1) ? s->rev.coi[f] : -s->rev.coi[f];
+ }
}
- if (*(s->rev.nnrev + i) == NULL
- && *(s->rev.rev + i) == NULL) {
-// printf("~1 warning, cell %d [ %d %d %d] has a NULL list\n",i, gg[0],gg[1],gg[2]);
- error("cell %d has a NULL list\n",i);
+ /* if this is a temporary bxcell (i.e. not a surface bxcell), */
+ /* we can now free it */
+ if ((vflag[tx->ix] & 2) == 0) {
+//printf("Done with non-surface bxcell %d vflag %x\n",tx->ix,vflag[tx->ix]);
+ del_bxcell(s, tx);
}
- next_vertex3:;
- DC_INC(gg);
}
-#endif /* DEBUG */
+ /* We've done the nnrev[] setup */
+ DBG(("rev.nnrev[] grid done - cleaning up\n"));
+
+#ifdef CHECK_NNLU
+ if (fdi > 1) {
+ /* Check that every nnrev[] cell is filled */
+ printf("Checking all %d nnrev[] cells are filled\n",rgno);
+ for (i = 0; i < rgno; i++) {
+ if ( ((vflag[i] & 2) != 0 || s->rev.rev[i] == NULL || s->rev.rev[i][1] == 3)
+ && (s->rev.nnrev[i] == NULL || s->rev.nnrev[i][1] == 3)) {
+ printf("Found empty nnrev[%d] ?:\n",i);
+ printf(" vflag %x\n",vflag[i]);
+ if (s->rev.nnrev[i] == NULL)
+ printf(" nnrev = NULL\n");
+ else
+ printf(" nnrev length = %d\n",s->rev.nnrev[i][1]-3);
+ if (s->rev.rev[i] == NULL)
+ printf(" rev = NULL\n");
+ else
+ printf(" rev = length = %d\n",s->rev.rev[i][1]-3);
+ }
+ }
+ }
+#endif /* CHECK_NNLU */
/* Free up flag array used for construction */
if (vflag != NULL) {
@@ -6136,58 +11171,37 @@ if (prop != NULL) {
free(vflag);
}
- /* Free up nn list cache indexing structure used in construction */
- if (nnc != 0) {
- for (i = 0; i < nncsize; i++) {
- nncache *nncp;
- /* Run through linked list freeing entries */
- for (ncp = nnc[i]; ncp != NULL; ncp = nncp) {
- nncp = ncp->next;
- free(ncp);
- }
- }
- free(nnc);
- nnc = NULL;
- }
- }
-
- if (s->rev.rev_valid == 0 && di > 1) {
- rev_struct *rsi;
- size_t ram_portion = g_avail_ram;
-
- /* Add into linked list */
- s->rev.next = g_rev_instances;
- g_rev_instances = &s->rev;
-
- /* Aportion the memory, and reduce cache if it is over new limit. */
- g_no_rev_cache_instances++;
- ram_portion /= g_no_rev_cache_instances;
- for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next) {
- revcache *rc = rsi->cache;
+#ifndef CHECK_NNLU
+ /* Free up surface linked list and delete the bxcells. */
+ free_surflist(s);
+#endif
- rsi->max_sz = ram_portion;
- while (rc->nunlocked > 0 && rsi->sz > rsi->max_sz) {
- if (decrease_revcache(rc) == 0)
- break;
- }
-//printf("~1 rev instance ram = %d MB\n",rsi->sz/1000000);
+ /* Free up surface bxcell hash index */
+ free_surfhash(s, 0);
+
+#if defined(REVTABLESTATS) || defined(DEBUG)
+ if (fdi > 1) {
+ nnrevshare = nnrevcells;
+ for (i = 0; i < s->rev.sharellen; i++)
+ nnrevshare += (s->rev.sharelist[i][1]-4) * (s->rev.sharelist[i][1]-3);
+
+ printf("%d/%d used cells in nnrev list\n",nnrevcells,rgno);
+ printf("%f average cells searched\n",(double)nnrevcellsearch/(double)nnrevcells);
+ printf("%d max bxcells used\n",maxbxcount);
+ printf("%.1f%% super-cell filled\n",100.0 * nnsuperfill/(nnsuperfill+nnsinglefill));
+ printf("%f average list length\n",(double)nnrevcelldepth/(double)nnrevcells);
+ printf("%d max list length\n",nnmxrevcelldepth);
+ printf("%f average shared lists\n",(double)nnrevshare/(double)nnrevcells);
+ printf("Took %f seconds\n",0.001 * (msec_time()-smsec));
+ printf("Overall took %f seconds\n",0.001 * (msec_time()-smsec));
}
-
- if (s->verbose)
- fprintf(stdout, "%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
- cr_char,
- g_no_rev_cache_instances > 1 ? "are" : "is",
- g_no_rev_cache_instances,
- g_no_rev_cache_instances > 1 ? "s" : "",
- (unsigned long)ram_portion/1000000);
+#endif
}
+
s->rev.rev_valid = 1;
-#ifdef DEBUG
- if (fdi > 1) printf("%d cells in rev nn list\n",cellinrevlist);
- if (fdi > 1) printf("%d fwd cells in rev nn list\n",fwdcells);
- if (cellinrevlist > 1) printf("Avg list size = %f\n",(double)fwdcells/cellinrevlist);
-#endif
+ if (fdi > 1 && s->verbose)
+ fprintf(stdout, "%cnnrev initialization done\n",cr_char);
DBG(("init_revaccell finished\n"));
}
@@ -6205,20 +11219,19 @@ rspl *s /* Pointer to rspl grid */
/* Free up the contents of rev.rev[] and rev.nnrev[] */
if (s->rev.rev != NULL) {
for (rpp = s->rev.rev; rpp < (s->rev.rev + s->rev.no); rpp++) {
- if ((rp = *rpp) != NULL && --rp[2] <= 0) {
- DECSZ(s, rp[0] * sizeof(int));
- free(*rpp);
- *rpp = NULL;
- }
+ if (*rpp != NULL)
+ free_indexlist(s, rpp);
}
}
if (s->rev.nnrev != NULL) {
+
+ /* Free up nn list sharelist records - this will free and set */
+ /* any shared lists to NULL */
+ free_sharelist(s);
+
for (rpp = s->rev.nnrev; rpp < (s->rev.nnrev + s->rev.no); rpp++) {
- if ((rp = *rpp) != NULL && --rp[2] <= 0) {
- DECSZ(s, rp[0] * sizeof(int));
- free(*rpp);
- *rpp = NULL;
- }
+ if (*rpp != NULL)
+ free_indexlist(s, rpp);
}
}
@@ -6253,6 +11266,274 @@ rspl *s /* Pointer to rspl grid */
s->rev.rev_valid = 0;
}
+#ifdef CHECK_NNLU
+/* ====================================================== */
+
+/* Used exautive searches to check that nn lookup found a good solution */
+static void check_nn(
+rspl *s,
+double *oval, /* Un-clipped output target value */
+co *cpp /* Clipped output space value in cpp[0].v[] */
+ /* nn solution in cpp[0].p[] */
+) {
+ int i, j; /* Index of fwd grid point */
+ int e, f, ee, ff;
+ int di = s->di;
+ int fdi = s->fdi;
+ int gno = s->g.no;
+ int good = 1;
+ int found = 0;
+ int printed = 0;
+
+ ECOUNT(gc, MXRI, di, 0, s->g.res, 0);/* coordinates */
+ float *gp; /* Pointer to grid data */
+ double iv[MXDI];
+ double ov[MXDO];
+ double chov[MXDO], de;
+
+ int bix = -1;
+ double bdist = 1e200;
+ double biv[MXDI];
+ double bov[MXDO];
+ int six = -1;
+ double sdist = 1e200;
+ double siv[MXDI];
+ double sov[MXDO];
+
+ double odelta;
+ double idelta;
+ double fsdelta;
+ double sodelta;
+ double sidelta;
+
+ s->rev.cknn_no++;
+
+ /* Compute the given solutions de */
+ de = sqrt(lchw_sq(s, oval, cpp[0].v));
+
+ /* Go through every fwd vertex looking for closest and 2nd closest */
+ EC_INIT(gc);
+ for (gp = s->g.a, i = 0; i < gno; gp += s->g.pss, i++) {
+ double dist;
+
+ if (s->limiten && gp[-1] > s->limitv) {
+ EC_INC(gc);
+ continue; /* Over the ink limit */
+ }
+
+ for (f = 0; f < fdi; f++)
+ ov[f] = gp[f];
+
+ dist = lchw_sq(s, oval, ov);
+
+ if (dist < bdist) {
+ six = bix;
+ bix = i;
+ for (e = 0; e < s->di; e++) {
+ siv[e] = biv[e];
+ biv[e] = s->g.l[e] + gc[e] * s->g.w[e];
+ }
+ for (f = 0; f < fdi; f++) {
+ sov[f] = bov[f];
+ bov[f] = ov[f];
+ }
+ sdist = bdist;
+ bdist = dist;
+
+ } else if (dist < sdist) {
+ six = i;
+ for (e = 0; e < s->di; e++)
+ siv[e] = s->g.l[e] + gc[e] * s->g.w[e];
+ for (f = 0; f < fdi; f++)
+ sov[f] = ov[f];
+ sdist = dist;
+
+ }
+ EC_INC(gc);
+ }
+
+ /* What is magnitude of target match ? */
+ odelta = sqrt(lchw_sq(s, bov, oval));
+
+ /* What is magnitude of solution match */
+ idelta = 0.0;
+ for (e = 0; e < s->di; e++) {
+ double tt = biv[e] - cpp[0].p[e];
+ idelta += tt * tt;
+ }
+ idelta = sqrt(idelta);
+
+ /* What is scale of solution from closest to 2nd closest ? */
+ fsdelta = 0.0;
+ for (e = 0; e < s->di; e++) {
+ double tt = biv[e] - siv[e];
+ fsdelta += tt * tt;
+ }
+ fsdelta = sqrt(fsdelta);
+
+ /* What is magnitude of target match to secondary ? */
+ sodelta = sqrt(lchw_sq(s, sov, oval));
+
+ /* What is magnitude of solution match to secondary ?*/
+ sidelta = 0.0;
+ for (e = 0; e < s->di; e++) {
+ double tt = siv[e] - cpp[0].p[e];
+ sidelta += tt * tt;
+ }
+ sidelta = sqrt(sidelta);
+
+ /* If our exaustive search is better than the nn solution: */
+ if (odelta < (de - 1e-6)) {
+ double dde = de - odelta;
+ if (dde > s->rev.cknn_we)
+ s->rev.cknn_we = dde;
+ s->rev.cknn_noerrs++;
+ good = 0;
+ printf("check_nn: target %s\n",debPdv(s->fdi,oval));
+ printf("check_nn: cliped to %s, de %f\n",debPdv(s->di,cpp[0].v),de);
+ printf("check_nn: solution %s\n",debPdv(s->di,cpp[0].p));
+ printf("check_nn: check target %s, de %f\n",debPdv(s->fdi, bov),odelta);
+ printf("check_nn: check solution %s, de %f @ix %d\n",debPdv(s->di, biv),idelta,bix);
+ printf("check_nn: check 2nd target %s, de %f\n",debPdv(s->fdi, sov),sodelta);
+ printf("check_nn: check 2nd solution %s, de %f @ ix %d\n",debPdv(s->di, siv),sidelta,six);
+ printf("check_nn: excess delta %f\n",dde);
+ printf("check_nn: first-second delta %f\n",fsdelta);
+ if (six >= 0 && (de - sodelta) > 1e-6) {
+ printf("check_nn: beyond 2nd best by %f!\n",de-sodelta);
+ s->rev.cknn_nobsb++;
+ }
+ printed = 1;
+ }
+
+ /* Search surface nnrev cells, to make sure our best is in it somewhere */
+ if (s->rev.surflist != NULL) {
+ bxcell *ss;
+
+ for (ss = s->rev.surflist; ss != NULL; ss = ss->slist) {
+ int *flist = ss->sl; /* List of fwd cells */
+
+ if (flist == NULL)
+ error("surflist nnrev[%d] is empty!",ss->ix);
+
+ /* For each forward cell */
+ for (flist += 3; *flist != -1; flist++) {
+ /* For each cube vertex */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = *flist + s->g.hi[ee];
+ if (vix == bix) {
+ found = 1;
+ if (!good)
+ printf("check_nn: found best vertex in surf nnrev[%d] fwd %d \n",ss->ix,*flist);
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ int rgno = s->rev.no;
+ int **rpp;
+ int revfound = 0;
+
+ s->rev.cknn_nonis++;
+ if (good) {
+ printf("check_nn: target %s\n",debPdv(s->fdi,oval));
+ printf("check_nn: cliped to %s, de %f\n",debPdv(s->di,cpp[0].v),de);
+ printf("check_nn: solution %s\n",debPdv(s->di,cpp[0].p));
+ printf("check_nn: check target %s, de %f\n",debPdv(s->fdi, bov),odelta);
+ printf("check_nn: check solution %s, de %f\n",debPdv(s->di, biv),idelta);
+ printf("check_nn: result is OK\n");
+ }
+ if (s->rev.surflist == NULL) {
+ printf("check_nn: No surface list to check against\n");
+ } else {
+ printf("check_nn: DIDN'T find best vertex %d in nnrev[] surface list\n",bix);
+ }
+ printed = 1;
+
+ /* See where it is in the rev[] list, and what the corresponding nnrev[] */
+ /* looks like */
+ for (rpp = s->rev.rev, i = 0; i < rgno; rpp++, i++) {
+ int *flist = *rpp;
+
+ if (flist == NULL)
+ continue;
+
+ /* For each forward cell */
+ for (flist += 3; *flist != -1; flist++) {
+ /* For each cube vertex */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = *flist + s->g.hi[ee];
+ if (vix == bix) {
+ revfound = 1;
+ printf("check_nn: found best vertex in rev[%d] fwd %d",i,*flist);
+ if (s->rev.nnrev[i] != NULL)
+ printf(" - cspndg. nnrev has list\n");
+ else
+ printf(" - cspndg. nnrev is empty\n");
+ break;
+ }
+ }
+ }
+ }
+ if (!revfound) {
+ printf("check_nn: DIDN'T find best vertex %d in rev list\n",bix);
+ }
+ }
+ }
+
+ /* Check if the nnrev[] cell for this target has the fwd cell */
+ if (s->rev.surflist != NULL && (!good || !found)) {
+ int mi[MXDO];
+ int rgres_1 = s->rev.res - 1;
+ int ix, *flist;
+ int found2 = 0;
+
+ for (ix = 0, f = 0; f < fdi; f++) {
+ double t = (oval[f] - s->rev.gl[f])/s->rev.gw[f];
+ mi[f] = (int)floor(t); /* Grid coordinate */
+ if (mi[f] < 0) /* Clip to reverse range, so we always return a result */
+ mi[f] = 0;
+ else if (mi[f] > rgres_1)
+ mi[f] = rgres_1;
+ ix += mi[f] * s->rev.coi[f]; /* Accumulate reverse grid index */
+ }
+ flist = s->rev.nnrev[ix];
+
+ if (flist != NULL) {
+ /* For each forward cell */
+ for (flist += 3; *flist != -1; flist++) {
+ /* For each cube vertex */
+ for (ee = 0; ee < (1<<di); ee++) {
+ int vix = *flist + s->g.hi[ee];
+ if (vix == bix) {
+ found2 = 1;
+ printf("check_nn: found best vertex %d in expected nnrev[%d], fwd %d\n",bix,ix,*flist);
+ printed = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (!found2) {
+ printf("check_nn: DIDN'T find best vertex %d in expected nnrev[%d] list\n",bix,ix);
+ printed = 1;
+ }
+ }
+ if (printed)
+ printf("\n");
+}
+
+static void print_nnck(rspl *s) {
+ printf("check_nn di %d fdi %d checked %d lookups:\n",s->di,s->fdi,s->rev.cknn_no);
+ printf("check_nn got %d not as good as best vertex\n",s->rev.cknn_noerrs);
+ printf("check_nn got %d not as good as 2nd best vertex\n",s->rev.cknn_nobsb);
+ printf("check_nn got %d not in surface list\n",s->rev.cknn_nonis);
+ printf("check_nn got %f worst excess de\n",s->rev.cknn_we);
+ printf("\n");
+}
+
+#endif /* CHECK_NNLU */
/* ====================================================== */
/* Initialise the rev First section, basic information that doesn't change */
@@ -6265,8 +11546,7 @@ rspl *s
int di = s->di;
int fdi = s->fdi;
int rgno, gno = s->g.no;
- int argres; /* Allocation rgres, = no cells +1 */
- int rgres;
+ int rgres; /* bwd cell grid (rev[], nnrev[]) resolution */
int rgres_1; /* rgres -1 == maximum base coord value */
datao rgmin, rgmax;
@@ -6318,26 +11598,23 @@ rspl *s
if ((rgres = (int) gresmul * s->g.mres) < 4)
rgres = 4;
}
- argres = rgres+1;
- s->rev.ares = argres; /* == number of verticies per side, used for construction */
s->rev.res = rgres; /* == number of cells per side */
rgres_1 = rgres-1;
- /* Number of elements in the rev.grid, including construction extra rows */
- for (rgno = 1, f = 0; f < fdi; f++, rgno *= argres);
+ /* Number of elements in the rev.grid */
+ for (rgno = 1, f = 0; f < fdi; f++, rgno *= rgres);
s->rev.no = rgno;
-//printf("~1 argres = %d\n",argres);
+//printf("~1 rgres = %d\n",rgres);
/* Compute coordinate increments */
s->rev.coi[0] = 1;
//printf("~1 coi[0] = %d\n",s->rev.coi[0]);
for (f = 1; f < fdi; f++) {
- s->rev.coi[f] = s->rev.coi[f-1] * argres;
+ s->rev.coi[f] = s->rev.coi[f-1] * rgres;
//printf("~1 coi[%d] = %d\n",f,s->rev.coi[f]);
}
/* Compute index offsets from base of cube to other corners. */
-
for (s->rev.hoi[0] = f = 0, j = 1; f < fdi; j *= 2, f++) {
for (i = 0; i < j; i++)
s->rev.hoi[j+i] = s->rev.hoi[i] + s->rev.coi[f]; /* In grid points */
@@ -6361,7 +11638,6 @@ rspl *s
INCSZ(s, rgno * sizeof(int *));
s->rev.inited = 1;
-
s->rev.stouch = 1;
DBG(("make_rev_one finished\n"));
@@ -6370,7 +11646,7 @@ rspl *s
/* ====================================================== */
/* First section of rev_struct init. */
-/* Initialise the reverse cell cache, sub simplex information */
+/* Initialise the fxcell cache, sub simplex information */
/* and reverse lookup acceleration structures. */
/* This is called by a reverse interpolation call */
/* that discovers that the reverse index list haven't */
@@ -6563,8 +11839,8 @@ rspl *s
#if defined(DEBUG1) || defined(DEBUG2)
-/* Utility - return a string containing a cells output value range */
-static char *pcellorange(cell *c) {
+/* Utility - return a string containing a fwd cells output value range */
+static char *pcellorange(fxcell *c) {
static char buf[5][300];
static ix = 0;
char *bp;
@@ -6615,8 +11891,707 @@ static char *pcellorange(cell *c) {
#define DBGV(xxx)
#define DBG(xxx)
+#ifdef REVVRML
+/* ====================================================== */
+/* VRML diagnostic output functions */
+
+/* Plot the initial surface rev cells */
+static void plot_bxfwcells(
+rspl *s,
+int dobxcells, /* Plot rev cells */
+int dofwcells, /* Plot fwd cells */
+int dofwlabels /* Plot fwd cell base indexs */
+) {
+ int i, j; /* Index of fwd grid point */
+ int e, f, ee, ff;
+ int di = s->di;
+ int fdi = s->fdi;
+ bxcell *bx;
+ vrml *wrl;
+ double grey[3] = { 0.5, 0.5, 0.5 };
+ double white[3] = { 1.0, 1.0, 1.0 };
+
+ wrl = new_vrml("raw_bxfwcells", 0, vrml_lab);
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ if (dofwlabels) {
+ /* Put text for every base cube index */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int vix[POW2MXRI];
+ int *crp, *rp;
+
+ crp = s->rev.rev[bx->ix];
+
+ for (rp = crp+3; *rp != -1; rp++) {
+ int ix = *rp;
+ char index[100];
+ double vv[MXRI];
+ int off = 0; // 0 .. 7, choose cube vertex
+ float *fcb = s->g.a + (ix + s->g.hi[off]) * s->g.pss;
+
+ for (e = 0; e < di; e++)
+ vv[e] = fcb[e];
+ sprintf(index, "%d",ix);
+ wrl->add_text(wrl, index, vv, white, 0.3);
+ }
+ }
+ }
+
+ if (dobxcells) {
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int vix[POW2MXRO];
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+ int *crp, *rp;
+
+ /* Plot bxcell's */
+ i = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ double vv[MXRO];
+ for (f = 0; f < fdi; f++)
+ vv[f] = (bx->gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ vix[i] = wrl->add_vertex(wrl, 0, vv);
+ DC_INC(cc);
+ i++;
+ }
+
+ /* For each vertex */
+ for (i = 0; i < (1 << fdi); i++) {
+ int lix[2];
+
+ lix[0] = vix[i];
+
+ /* for each dimension */
+ for (j = 0; j < fdi; j++) {
+ if (i & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[i | (1 << j)];
+ if (dofwcells)
+ wrl->add_col_line(wrl, 0, lix, grey);
+ else
+ wrl->add_line(wrl, 0, lix);
+ }
+ }
+ }
+ }
+
+ if (dofwcells) {
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int vix[POW2MXRI];
+ int *crp, *rp;
+
+ /* Add fwd cells */
+ crp = s->rev.rev[bx->ix];
+ for (rp = crp+3; *rp != -1; rp++) {
+ float *fcb = s->g.a + *rp * s->g.pss;
+
+ /* Skip grid base points on the upper edge of the grid */
+ for (e = 0; e < di; e++) {
+ if (G_FL(fcb, e) == 0) /* At the top edge */
+ break;
+ }
+ if (e < di) {
+ printf("Fwd cell base index %d is on upper edge!\n",*rp);
+ continue;
+ }
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ double vv[MXRI];
+ int ix = *rp + s->g.hi[i];
+ fcb = s->g.a + ix * s->g.pss;
+
+ if (!s->limiten || fcb[-1] <= s->limitv)
+ break;
+ }
+ /* Skip any cubes that a completely over the ink limit */
+ if (i >= (1<<di))
+ continue;
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ double vv[MXRI];
+ int ix = *rp + s->g.hi[i];
+ fcb = s->g.a + ix * s->g.pss;
+
+ for (e = 0; e < di; e++)
+ vv[e] = fcb[e];
+ vix[i] = wrl->add_vertex(wrl, 1, vv);
+ }
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ int lix[2];
+
+ lix[0] = vix[i];
+
+ /* for each dimension */
+ for (j = 0; j < di; j++) {
+ if (i & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[i | (1 << j)];
+ wrl->add_line(wrl, 1, lix);
+ }
+ }
+ }
+ }
+ }
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ wrl->make_lines_vc(wrl, 1, 0.0);
+
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+}
+
+/* Plot vertex & triangle check setup & solution */
+/* + the primary and shadow bxcells. */
+static void plot_tri_check(
+rspl *s,
+int dobxcells, /* Plot prim & shadow bxcell cells */
+int dowait, /* Wait for the user to hit return */
+bxcell *bx, /* First bx cell (if dobxcells set) */
+int vtxix, /* triangle base vertex index (-1 if not applicable) */
+int trii, /* Triangle eneration */
+int triix[3], /* Triangle indexes */
+int nvtxix, /* test point vertex index number (may be -1 if not vtxrec) */
+int sorv, /* Intersection was solved ? */
+int wsrv, /* Within simplex ? */
+int shdwd, /* Vertex is shadowed ? */
+double v[MXRI+1][MXRO], /* Triangle vertex values */
+double de[MXRO], /* Line delta */
+double pv[MXRO], /* Vertex being tested */
+double xv[MXRO] /* Intersection point */
+) {
+ int j;
+ int e, f, ee, ff;
+ int di = s->di;
+ int fdi = s->fdi;
+ vrml *wrl;
+ bxcell *vbx;
+ int first = 1;
+ int ii, vix[POW2MXRO], lix[3];
+ double vv[MXRO];
+ double white[3] = { 1.0, 1.0, 1.0 };
+ double grey[3] = { 0.5, 0.5, 0.5 };
+ double green[3] = { 0.1, 1.0, 0.1 };
+ double red[3] = { 0.8, 0.1, 0.1 };
+ double blue[3] = { 0.1, 0.1, 0.8 };
+ double yellow[3] = { 0.8, 0.8, 0.1 };
+
+ wrl = new_vrml("tri_check", 0, vrml_lab);
+
+ /* Gamut center point marker */
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ /* point being tested marker */
+ wrl->add_marker(wrl, pv, shdwd ? red : blue, 0.5);
+
+ /* Intersection point */
+ if (wsrv)
+ wrl->add_marker(wrl, xv, blue, 0.2);
+
+ /* Line from center through point being tested */
+ lix[0] = wrl->add_vertex(wrl, 0, s->rev.ocent);
+ for (ii = 0; ii < fdi; ii++)
+ vv[ii] = s->rev.ocent[ii] + 10.0 * de[ii];
+ lix[1] = wrl->add_vertex(wrl, 0, vv);
+ wrl->add_col_line(wrl, 0, lix, grey);
+
+ /* Triangle */
+ lix[0] = wrl->add_vertex(wrl, 1, v[0]);
+ lix[1] = wrl->add_vertex(wrl, 1, v[1]);
+ lix[2] = wrl->add_vertex(wrl, 1, v[2]);
+ wrl->add_col_triangle(wrl, 1, lix, green);
+ /* And again to get both faces */
+ lix[0] = wrl->add_vertex(wrl, 1, v[0]);
+ lix[1] = wrl->add_vertex(wrl, 1, v[2]);
+ lix[2] = wrl->add_vertex(wrl, 1, v[1]);
+ wrl->add_col_triangle(wrl, 1, lix, green);
+
+ if (dobxcells) {
+//printf(" bx = %p\n",bx);
+ for (vbx = bx; vbx != NULL; vbx = vbx->wlist) {
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+ int *crp, *rp;
+
+//printf(" vrml adding bxcell %d\n",vbx->ix);
+ /* Plot bxcell's */
+ ii = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ for (f = 0; f < fdi; f++)
+ vv[f] = (vbx->gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+//printf(" vrml vtx %d from %s\n",vix[i], debPdv(3,vv));
+ vix[ii] = wrl->add_vertex(wrl, 0, vv);
+ DC_INC(cc);
+ ii++;
+ }
+
+ /* For each vertex */
+ for (ii = 0; ii < (1 << fdi); ii++) {
+
+ lix[0] = vix[ii];
+
+ /* for each dimension */
+ for (j = 0; j < fdi; j++) {
+ if (ii & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[ii | (1 << j)];
+//printf(" vrml line from vtx %d - %d\n",lix[0],lix[1]);
+ wrl->add_col_line(wrl, 0, lix, first ? white : red);
+ }
+ }
+ first = 0;
+ }
+ }
+
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ wrl->make_triangles(wrl, 1, 0.0, NULL);
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+
+ printf(" Solved %s, Within triang %s, shadowed %s\n", sorv ? "true" : "false", wsrv ? "true" : "false", shdwd ? "true" : "false");
+ printf("Testing against tri %d %d %d\n", triix[0], triix[1], triix[2]);
+
+ printf(" bx %d vtx %d tri %d checking nvx %d, hit return key:\n",bx->ix, vtxix, trii, nvtxix);
+ if (dowait) {
+ printf(" hit return key to continue:\n");
+ getchar();
+ }
+}
+
+/* Main summary plot at each thinning round and at end. */
+/* Show vertex surface & optional added or deleted vertexes, */
+/* + optional bxcells. */
+static void plot_vtx_surface(
+rspl *s,
+int dovtxlabels, /* Show vertex index numbers */
+int dodeleted, /* Show deleted vertexes */
+int doadded, /* Show added vertexes */
+int dopres, /* Show preserved vertexes */
+int dooil, /* Show over ink limit vertexes */
+int dobxcells, /* Show bxcells */
+int dowait, /* Wait for a return key */
+vtxcache *vc, /* Vertexes */
+assdire *edgdir /* Edge lookup for vertex */
+) {
+ vtxrec *vx, *nvx;
+ int i, j;
+ int f, fdi = s->fdi;
+ vrml *wrl;
+ double grey[3] = { 0.5, 0.5, 0.5 };
+ double red[3] = { 0.8, 0.1, 0.1 };
+ double green[3] = { 0.2, 0.8, 0.2 };
+ double blue[3] = { 0.2, 0.2, 0.8 };
+ double white[3] = { 0.8, 0.8, 0.8 };
+ double magenta[3] = { 0.8, 0.2, 0.8 };
+ double cyan[3] = { 0.0, 1.0, 1.0 };
+ double yellow[3] = { 1.0, 1.0, 0.0 };
+ bxcell *vbx;
+
+ if (dopres)
+ wrl = new_vrml("final_surface", 0, vrml_lab);
+ else
+ wrl = new_vrml("thinned_surface", 0, vrml_lab);
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ if (dovtxlabels) {
+ for (i = 0; i < vc->hash_size; i++) {
+ for (vx = vc->hash[i]; vx != NULL; vx = vx->hlink) {
+ char index[100];
+
+ if (vx->status == vtx_norm
+ || (dodeleted && (vx->status == vtx_sha || vx->status == vtx_del))
+ || (doadded && vx->addvtx)
+ || (dopres && vx->pres)
+ || (dooil && vx->status == vtx_oil)) {
+ sprintf(index, "%d",vx->ix);
+ wrl->add_text(wrl, index, vx->v, cyan, 0.3);
+ }
+ }
+ }
+ }
+
+ /* Go through the vertex hash to set every vertex value */
+ for (i = 0; i < vc->hash_size; i++) {
+ for (vx = vc->hash[i]; vx != NULL; vx = vx->hlink) {
+
+ if (vx->status != vtx_norm && vx->addvtx)
+ error ("Found vertex that is both deleted and cause of added bxcell");
+
+ if (doadded && vx->addvtx) /* Cause of added bxcell */
+ vx->vrmlix = wrl->add_col_vertex(wrl, 0, vx->v, green);
+ else if (dopres && vx->pres) /* Preserved vertex */
+ vx->vrmlix = wrl->add_col_vertex(wrl, 0, vx->v, yellow);
+ else if (dodeleted && (vx->status == vtx_sha || vx->status == vtx_del))
+ vx->vrmlix = wrl->add_col_vertex(wrl, 0, vx->v, red);
+ else if (dooil && vx->status == vtx_oil)
+ vx->vrmlix = wrl->add_col_vertex(wrl, 0, vx->v, blue);
+ else if (vx->status == vtx_norm)
+ vx->vrmlix = wrl->add_col_vertex(wrl, 0, vx->v, white);
+ }
+ }
+
+ /* Go through them again to get every line they are part of */
+ for (i = 0; i < vc->hash_size; i++) {
+ for (vx = vc->hash[i]; vx != NULL; vx = vx->hlink) {
+ assdire *edg; /* Edge table */
+ float *fp;
+ int fl;
+ int pline = 0; /* Plotted at least 1 line */
+ int lix[2];
+
+ fp = s->g.a + vx->ix * s->g.pss; /* This vertex in fwd grid */
+ fl = FLV(fp); /* Edge flags for this vertex */
+ edg = edgdir + fl;
+
+ /* For all possible edges that use this vertex */
+ for (j = 0; j < edgdir[fl].no; j++) {
+ int fix;
+ int eix;
+
+ /* Index of first vertex of the line */
+ fix = vx->ix + edg->ti[j].goffs[0];
+
+ /* Index number of vertex other than the one we got it from */
+ if (edg->ti[j].goffs[0] != 0)
+ eix = vx->ix + edg->ti[j].goffs[0];
+ else
+ eix = vx->ix + edg->ti[j].goffs[1];
+
+ if ((nvx = get_vtxrec(vc, eix)) != NULL) {
+ if ( (vx->status == vtx_norm
+ || (dodeleted && (vx->status == vtx_sha || vx->status == vtx_del))
+ || (doadded && vx->addvtx)
+ || (dopres && vx->pres)
+ || (dooil && vx->status == vtx_oil))
+ && (nvx->status == vtx_norm
+ || (dodeleted && (nvx->status == vtx_sha || nvx->status == vtx_del))
+ || (doadded && nvx->addvtx)
+ || (dopres && nvx->pres)
+ || (dooil && nvx->status == vtx_oil))) {
+
+ pline = 1; /* Will/would plot this */
+
+ /* Only plot the line once though */
+ if (fix == vx->ix) {
+ lix[0] = vx->vrmlix;
+ lix[1] = nvx->vrmlix;
+ wrl->add_line(wrl, 0, lix);
+ }
+ }
+ }
+ }
+
+ /* we have an orphan vertex */
+ if (pline == 0
+ && (dodeleted || vx->status == vtx_norm)
+ && (doadded || !vx->addvtx)) {
+ double vv[MXRO], off = 0.15, *col;
+
+ if (doadded && vx->addvtx) /* Cause of added bxcell */
+ col = green;
+ else if (dopres && vx->pres) /* Preserved vertex */
+ col = yellow;
+ else if (dodeleted && vx->status != vtx_norm)
+ col = red;
+ else if (dooil && vx->status == vtx_oil)
+ col = blue;
+ else if (vx->status == vtx_norm)
+ col = white;
+
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] + off;
+ lix[0] = wrl->add_vertex(wrl, 2, vv);
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] - off;
+ lix[1] = wrl->add_vertex(wrl, 2, vv);
+ wrl->add_col_line(wrl, 2, lix, col);
+
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] + ((f & 1) ? off : -off);
+ lix[0] = wrl->add_vertex(wrl, 2, vv);
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] - ((f & 1) ? off : -off);
+ lix[1] = wrl->add_vertex(wrl, 2, vv);
+ wrl->add_col_line(wrl, 2, lix, col);
+
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] + ((f & 2) ? off : -off);
+ lix[0] = wrl->add_vertex(wrl, 2, vv);
+ for (f = 0; f < fdi; f++)
+ vv[f] = vx->v[f] - ((f & 2) ? off : -off);
+ lix[1] = wrl->add_vertex(wrl, 2, vv);
+ wrl->add_col_line(wrl, 2, lix, col);
+ }
+ }
+ }
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ wrl->make_lines_vc(wrl, 2, 0.0);
+
+ /* Plot surface cells */
+ if (dobxcells) {
+ for (vbx = s->rev.surflist; vbx != NULL; vbx = vbx->slist) {
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+ int *crp, *rp, ii;
+ double vv[MXRO];
+ int vix[POW2MXRO], lix[2];
+
+ ii = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (vbx->gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ vv[f] += d_rand(-0.05, 0.05);
+ }
+ vix[ii] = wrl->add_vertex(wrl, 1, vv);
+ DC_INC(cc);
+ ii++;
+ }
+
+ for (ii = 0; ii < (1 << fdi); ii++) {
+ lix[0] = vix[ii];
+
+ /* for each dimension */
+ for (j = 0; j < fdi; j++) {
+ if (ii & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[ii | (1 << j)];
+ if (vbx->debug) { /* Added bxcell */
+ wrl->add_col_line(wrl, 1, lix, magenta);
+ } else { /* Existing bxcell */
+ wrl->add_col_line(wrl, 1, lix, grey);
+ }
+ }
+ }
+ }
+ wrl->make_lines_vc(wrl, 1, 0.0);
+ }
+
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+ if (dowait) {
+ printf(" Thinned vertexes surface: Hit return to continue\n");
+ getchar();
+ }
+}
+
+/* Plot bxcells touched by added cell */
+static void plot_touched_bxcells(
+rspl *s,
+int bxix /* Index of bx cell causing touches */
+) {
+ int j, f, fdi = s->fdi;
+ vrml *wrl;
+ bxcell *vbx;
+ int first = 1;
+ int ii, vix[POW2MXRO], lix[3];
+ double vv[MXRO];
+ double green[3] = { 0.1, 0.6, 0.1 };
+ double white[3] = { 1.0, 1.0, 1.0 };
+ double red[3] = { 0.8, 0.1, 0.1 };
+
+ wrl = new_vrml("add_touch_bxcells", 0, vrml_lab);
+
+ /* Gamut center point marker */
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ for (vbx = s->rev.surflist; vbx != NULL; vbx = vbx->slist) {
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+ int *crp, *rp;
+
+ /* Plot bxcell's */
+ ii = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ for (f = 0; f < fdi; f++) {
+ vv[f] = (vbx->gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ if (vbx->debug == 2)
+ vv[f] += 0.05;
+ else if (vbx->debug == 1)
+ vv[f] -= 0.05;
+ }
+ vix[ii] = wrl->add_vertex(wrl, 0, vv);
+ DC_INC(cc);
+ ii++;
+ }
+ /* For each vertex */
+ for (ii = 0; ii < (1 << fdi); ii++) {
+
+ lix[0] = vix[ii];
+
+ /* for each dimension */
+ for (j = 0; j < fdi; j++) {
+ if (ii & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[ii | (1 << j)];
+ wrl->add_col_line(wrl, 0, lix,
+ vbx->debug == 2 ? white : vbx->debug == 1 ? red : green);
+ }
+ }
+ }
+
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+
+ printf(" Touched bx cells for bx %d: Hit return to continue\n",bxix);
+ getchar();
+}
+
+/* Plot the thinned surface fwd cells */
+static void plot_fxcell_surface(
+rspl *s,
+int dofclabels, /* Show fwd cell base indexes */
+int dobxcells, /* Show bxcells */
+int dowait /* Wait for a return key */
+) {
+ bxcell *bx;
+ int i, j;
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ vrml *wrl;
+ double grey[3] = { 0.5, 0.5, 0.5 };
+ double white[3] = { 1.0, 1.0, 1.0 };
+
+ wrl = new_vrml("thinned_fwcells", 0, vrml_lab);
+ wrl->add_marker(wrl, s->rev.ocent, NULL, 1.0);
+
+ if (dofclabels) {
+ /* Put text for every base cube index */
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ int vix[POW2MXRI];
+ int *crp, *rp;
+
+ crp = bx->sl;
+
+ for (rp = crp+3; *rp != -1; rp++) {
+ int ix = *rp;
+ char index[100];
+ double vv[MXRI];
+ int off = 0; // 0 .. 7, choose cube vertex
+ float *fcb = s->g.a + (ix + s->g.hi[off]) * s->g.pss;
+
+ for (e = 0; e < di; e++)
+ vv[e] = fcb[e];
+ sprintf(index, "%d",ix + s->g.hi[off]);
+ wrl->add_text(wrl, index, vv, white, 0.3);
+ }
+ }
+ }
+
+ for (bx = s->rev.surflist; bx != NULL; bx = bx->slist) {
+ DCOUNT(cc, MXRO, fdi, 0, 0, 2); /* Vertex counter */
+ int vix[POW2MXRI];
+ int *crp, *rp;
+
+ if (dobxcells) {
+ /* Plot bxcell's */
+ i = 0;
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ double vv[MXRO];
+ for (f = 0; f < fdi; f++)
+ vv[f] = (bx->gc[f] + cc[f]) * s->rev.gw[f] + s->rev.gl[f];
+ vix[i] = wrl->add_vertex(wrl, 1, vv);
+ DC_INC(cc);
+ i++;
+ }
+
+ /* For each vertex */
+ for (i = 0; i < (1 << fdi); i++) {
+ int lix[2];
+
+ lix[0] = vix[i];
+
+ /* for each dimension */
+ for (j = 0; j < fdi; j++) {
+ if (i & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[i | (1 << j)];
+ wrl->add_col_line(wrl, 1, lix, white);
+ }
+ }
+ }
+
+ crp = bx->sl;
+
+ for (rp = crp+3; *rp != -1; rp++) {
+ float *fcb = s->g.a + *rp * s->g.pss;
+
+ /* Skip grid base points on the upper edge of the grid */
+ for (e = 0; e < di; e++) {
+ if (G_FL(fcb, e) == 0) /* At the top edge */
+ break;
+ }
+ if (e < di) {
+ printf("Fwd cell base index %d is on upper edge!\n",*rp);
+ continue;
+ }
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ double vv[MXRI];
+ int ix = *rp + s->g.hi[i];
+ float *fcb = s->g.a + ix * s->g.pss;
+
+ if (!s->limiten || fcb[-1] <= s->limitv)
+ break;
+ }
+ /* Skip any cubes that a completely over the ink limit */
+ if (i >= (1<<di))
+ continue;
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ double vv[MXRI];
+ int ix = *rp + s->g.hi[i];
+ float *fcb = s->g.a + ix * s->g.pss;
+
+ for (e = 0; e < di; e++)
+ vv[e] = fcb[e];
+ vix[i] = wrl->add_vertex(wrl, 0, vv);
+ }
+
+ /* For each vertex of cube */
+ for (i = 0; i < (1<<di); i++) {
+ int lix[2];
+
+ lix[0] = vix[i];
+
+ /* for each dimension */
+ for (j = 0; j < di; j++) {
+ if (i & (1<<j))
+ continue; /* Would go outside cube */
+
+ lix[1] = vix[i | (1<<j)];
+ wrl->add_line(wrl, 0, lix);
+ }
+ }
+ }
+ }
+ if (dobxcells)
+ wrl->make_lines_vc(wrl, 1, 0.0);
+ wrl->make_lines_vc(wrl, 0, 0.0);
+ printf("Created %s\n",wrl->name);
+ wrl->del(wrl);
+
+ if (dowait) {
+ printf(" Thinned fwd cell surface: Hit return to continue\n");
+ getchar();
+ }
+}
+
+/* ====================================================== */
+#endif /* REVVRML */