diff options
Diffstat (limited to 'rspl')
-rw-r--r-- | rspl/Jamfile | 11 | ||||
-rw-r--r-- | rspl/rev.c | 8763 | ||||
-rw-r--r-- | rspl/rev.h | 222 | ||||
-rw-r--r-- | rspl/revbench.c | 4 | ||||
-rw-r--r-- | rspl/rspl.c | 8 | ||||
-rw-r--r-- | rspl/rspl.h | 46 | ||||
-rw-r--r-- | rspl/rspl1.c | 83 | ||||
-rw-r--r-- | rspl/rspl1.h | 32 | ||||
-rw-r--r-- | rspl/scat.c | 15 | ||||
-rw-r--r-- | rspl/tnd.c | 4 |
10 files changed, 7701 insertions, 1487 deletions
diff --git a/rspl/Jamfile b/rspl/Jamfile index 37c2c37..0d93acc 100644 --- a/rspl/Jamfile +++ b/rspl/Jamfile @@ -3,8 +3,8 @@ # Optimization and Debug flags -PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on -#PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags +#PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on +PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags #PREF_CCFLAGS += $(CCPROFFLAG) ; # Profile flags #PREF_LINKFLAGS += $(LINKPROFFLAG) ; # Profile flags #PREF_CCFLAGS += $(CCHEAPDEBUG) ; # Heap Debugging flags @@ -59,9 +59,14 @@ if $(BUILD_TESTS) { # Test code -if $(HOME) = "d:\\usr\\graeme" && $(PWD) = "/src/argyll/rspl" { +if ( $(HOME) = "D:\\usr\\graeme" && $(PWD) = "/src/argyll/rspl" ) + || ( $(HOME) = "/Users/graeme" && $(PWD) = "/Users/graeme/src/argyll/rspl" ) { # Main tt : tt.c : : ../xicc : : : ../plot/libvrml ../icc/libicc ; + Main lchw : lchw.c : : : ../h ../numlib ../icc ../plot : : ../plot/libplot ../icc/libicc ../numlib/libui ../numlib/libnum ; + Main lchw_solve : lchw_solve.c : : : ../h ../numlib ../icc ../plot : : ../plot/libplot ../icc/libicc ../numlib/libui ../numlib/libnum ; + Main lchw_deriv : lchw_deriv.c : : : ../h ../numlib ../icc ../plot : : ../plot/libplot ../icc/libicc ../numlib/libui ../numlib/libnum ; + Main lchw_2deriv : lchw_2deriv.c : : : ../h ../numlib ../icc ../plot : : ../plot/libplot ../icc/libicc ../numlib/libui ../numlib/libnum ; Main crossv : crossv.c : : : ../numlib ../plot : : ../plot/libplot ../numlib/libui ../numlib/libnum ; } @@ -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 */ @@ -37,21 +37,22 @@ * with a cartesian coordinate, but the parameter order corresponds with * the baricentric order. * - * For example, given cartesian coordinates D0, D1, D2 - * these should be sorted from smallest to largest, thereby - * choosing a particular simplex within a cube, and allowing - * a correspondence to the parameter coordinates, ie: + * For example, given cartesian sub-coordinates D0, D1, D2 + * into a (3D) forward interpolation cube, these should be sorted + * from smallest to largest, thereby choosing a particular + * simplex within a cube, and allowing a correspondence to + * the parameter coordinates, ie: * - * D1 D0 D2 Smallest -> Largest cartesian sort - * P2 P1 P0 Corresponding Parameter coordinates (note reverse order!) + * D2 D0 D1 Smallest -> Largest cartesian sort + * P0 P1 P2 Corresponding Parameter coordinates * - * B0 = P0 Conversion to Baricentric coordinates + * B0 = P0 Conversion to Baricentric weighting/coordinates * B1 = P1 - P0 * B2 = P2 - P1 * B3 = 1 - P2 * - * The vertex values directly correspond to Baricentric coordinates, - * giving the usual interpolation equation of: + * The 4 (tetrahedron) vertex values directly correspond to Baricentric + * weighting/coordinates, giving the usual interpolation equation of: * * VV0 * B0 * + VV1 * B1 @@ -66,21 +67,42 @@ * + VV2 - VV3 * P2 * + VV3 * + * Note that withing the simplex, 0 <= P0 && P0 <= P1 && P1 <= P2 && P2 <= 1 + * * It is this which is used in rev.c for solving the reverse * interpolation problem. */ -/* A structure to hold per simplex coordinate and vertex mapping */ +/* - - - - - - - - - - - - - - - - - - - - - */ +/* Group size information for nn LCh weighted quick accept/reject testing */ +typedef struct { + double bcent[MXRO]; /* Group center location in output space */ + double brad; /* Output value bounding shere radius */ + double bradsq; /* Output value bounding shere radius squared */ + + double maxDlc; /* Maximum members weighted delta LC (+extra dims) squared */ + double maxDh; /* Maximum members delta h squared */ + double maxDh_; /* Maximum members delta h (not squared) */ + double sratio; /* Minimum members C ratio to Gc squared */ + double bratio; /* Maximum members C ratio to Gc squared */ + double Wsratio; /* Minimum members C ratio to Gc squared - pre-weighted */ + double Wbratio; /* Maximum members C ratio to Gc squared - pre-weighted */ + double Gc; /* Group center Chrominance squared */ + double Gc_; /* Group center Chrominance (not squared) */ +} nn_grp; + +/* - - - - - - - - - - - - - - - - - - - - - */ +/* A structure to hold per simplex coordinate and vertex mapping for ssxinfo. */ /* This is relative to the construction cube. A face sub-simplex */ /* that is common between cubes, will have a different psxinfo */ /* depending on which cube created it. */ typedef struct { int face; /* Flag, nz if simplex lies on cube surface */ - int icomb[MXDI]; /* Index by Absolute[di] -> Simplex Parameter[sdi], */ + int icomb[MXDI]; /* icomb[] specifies the equation to convert simplex space */ + /* coordinates back into cartesian space. */ + /* Index by Absolute[di] -> Simplex Parameter[sdi], */ /* -1 == value 0, -2 == value 1 */ /* Note that many Abs can map to one Param to form a sum. */ - /* icomb[] specifies the equation to convert simplex space */ - /* coordinates back into cartesian space. */ int offs[MXDI+1]; /* Offsets to simplex verticies within cube == bit per dim */ int goffs[MXDI+1]; /* Offsets to simplex verticies within grid */ int foffs[MXDI+1]; /* Fwd grid floating offsets to simplex verticies from cube base */ @@ -97,9 +119,9 @@ typedef struct { /* - - - - - - - - - - - - - - - - - - - - - */ /* NOTE :- This should really be re-arranged to be per-sub-simplex caching, */ -/* rather than cell caching. Rather than stash the simplex info in the cells, */ +/* rather than fxcell caching. Rather than stash the simplex info in the fxcells, */ /* create a separate cache or some other way of sharing the common simplexes. */ -/* The code that ignores common face simplexes in cells could then be removed. */ +/* The code that ignores common face simplexes in fxcells could then be removed (?). */ /* Simplex definition. Each top level fwd interpolation cell, */ /* is decomposed into sub-simplexes. Sub-simplexes are of equal or */ @@ -111,12 +133,12 @@ struct _simplex { int si; /* Diagnostic - simplex number within level */ int sdi; /* Sub-simplex dimensionality */ int efdi; /* Effective fdi. This will be = fdi for a non clip */ - /* plane simplex, and fdi+1 for a clip plane simplex */ - /* The DOF (Degress of freedom) withing this simplex = sdi - efdi */ + /* plane simplex, and fdi+1 for a clip plane simplex. */ + /* The DOF (Degress of freedom) within this simplex = sdi - efdi */ psxinfo *psxi; /* Generic per simplex info (construction cube relative) */ int vix[MXRI+1]; /* fwd cell vertex indexes of this simplex [sdi+1] */ - /* This is a universal identification of this simplex */ + /* This is a universal identification of this simplex. */ struct _simplex *hlink; /* Link to other cells with this hash */ unsigned int touch; /* Last touch count. */ short flags; /* Various flags */ @@ -130,7 +152,6 @@ struct _simplex { #define SPLX_FLAG_5 0x40 /* auxiliary lu/svd initialised */ #define SPLX_FLAG_5F 0x80 /* auxiliary lu/svd init. failed */ - #define SPLX_FLAGS (SPLX_FLAG_1 | SPLX_FLAG_2 | SPLX_FLAG_2F \ | SPLX_FLAG_4 | SPLX_FLAG_5 | SPLX_FLAG_5F) @@ -151,7 +172,7 @@ struct _simplex { double min[MXRO+1], max[MXRO+1]; /* Simplex vertex output space [fdi+1] and */ /* ink limit bounding values at minmax[fdi] */ - /* If sdi == efdi, this holds the LU decomposition */ + /* If sdi == efdi, this holds the LU decomposition, */ /* else this holds the SVD and solution locus info */ char *aloc2; /* Memory allocation for #2 & #4 */ @@ -187,27 +208,31 @@ struct _simplex { }; typedef struct _simplex simplex; -/* A candidate search cell (cell cache entry structure) */ -struct _cell { +/* A candidate search (fwd) fxcell (cell cache entry structure) */ +struct _fxcell { struct _rspl *s; /* Pointer to parent rspl */ /* Cache information */ - int ix; /* Fwd cell index */ - struct _cell *hlink; /* Link to other cells with this hash */ - struct _cell *mrudown; /* Links to next most recently used cell */ - struct _cell *mruup; + int ix; /* Corresponding fwd cell index */ + struct _fxcell *hlink; /* Link to other cells with this hash */ + struct _fxcell *mrudown;/* Links to next most recently used fxcell */ + struct _fxcell *mruup; int refcount; /* Reference count */ - int flags; /* Non-zero if the cell has been initialised */ -#define CELL_FLAG_1 0x01 /* Basic initialisation */ + int flags; /* Non-zero if the fxcell has been initialised */ +#define CELL_FLAG_1 0x01 /* Basic initialisation, including nn_grp */ #define CELL_FLAG_2 0x02 /* Simplex information initialised */ /* Use information */ double sort; /* Sort key */ + double limmin, limmax; /* limit() min/max for this fxcell */ - double limmin, limmax; /* limit() min/max for this cell */ - double bcent[MXRO]; /* Output value bounding shere center */ - double brad; /* Output value bounding shere radius */ - double bradsq; /* Output value bounding shere radius squared */ + /* Quick nn distance information */ + nn_grp g; + +// double bcent[MXRO]; /* Output value bounding shere center */ +// double brad; /* Output value bounding shere radius */ +// double bradsq; /* Output value bounding shere radius squared */ +// double wbrad; /* Output value weighted bounding shere radius */ double p[POW2MXRI][MXRI]; /* Vertex input positions for this cube. */ /* Copied to x->pmin/pmax[] & ink limit */ @@ -215,18 +240,61 @@ struct _cell { double v[POW2MXRI][MXRO+1]; /* Vertex data for this cube. Copied to x->v[] */ /* v[][fdi] is the ink limit values, if relevant */ - simplex **sx[MXRI+1]; /* Lists of simplexes that make up this cell. */ + simplex **sx[MXRI+1]; /* Lists of simplexes that make up this fxcell. */ /* Each list indexed by the non-limited simplex */ /* dimensionality (similar to sspxi[]) */ /* Each list will be NULL if it hasn't been created yet */ int sxno[MXRI+1]; /* Corresponding count of each list */ -}; typedef struct _cell cell; +}; typedef struct _fxcell fxcell; + +/* surface bxcell sl status */ +typedef enum { + bx_uninit = 0, /* sl is not initialised */ + bx_filled = 1, /* sl has been filled with initial fwd cell vertexes */ + bx_rethinnd = 2, /* sl vertexes need to be re-thinned */ + bx_thinned = 3, /* sl vertexes have been thinned */ + bx_conv = 4 /* sl vertexes have been converted to fwd cell indexes */ +} bxstat; + +/* Structure to hold bwd cell information for surface list, and also */ +/* for seed fill bwd cell propogation. (Cells on surface will have two */ +/* of these) */ +struct _bxcell{ + int ix; /* nnrev[] index of this bwd cell */ + int gc[MXRO]; /* coordinate of this bwd cell */ + nn_grp g; /* Group nn calculation info */ + struct _bxcell *ss; /* bwd surface cell to start search from */ + double sdist; /* Est. wtd distance from this cell to ss */ + int tix; /* target rev[] index being filled (visited check) */ + + bxstat status; /* State of sl list */ + int *sl; /* fwd vertex seed list for surface bxcells */ + int *dl; /* deleted fwd vertex list for this bxcell */ + + int *scell; /* If non-NULL, this is a (non-surface) */ + /* seeding super bxcell, and scell contains */ + /* the list of bxcells covered */ + + struct _bxcell *slist; /* Linked list of all surface bxcells */ + struct _bxcell *hlink; /* Linked list of surface bxcells with same ix hash */ + struct _bxcell *xlist; /* Linked list of surface exploration search region */ + double emin; /* estimated minimum wtd distance of this cell in current search */ + struct _bxcell *tlist; /* Linked list of solution surface cells for current search. */ + + struct _bxcell *flist; /* Linked list for nnrev[] fill seeds */ + + double cc; /* Distance of group from gamut center */ + double dw; /* Delta width from ocent of furthest point */ + struct _bxcell *wlist; /* Linked list for shadow bxcells during thinning */ + + int debug; /* debug flag - for VRML tagging */ +}; typedef struct _bxcell bxcell; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Enough space is needed to cache all the cells/simplexes */ +/* Enough space is needed to cache all the fxcells/simplexes */ /* for a full aux. locus, or the query will be processed in */ /* several "chunks" and will be slower. */ /* This sets the basic memory usage of the rev code. */ @@ -239,20 +307,20 @@ struct _cell { /* rev as a fraction of the System RAM. */ #define HASH_FILL_RATIO 3 /* 3 Ratio of entries to hash size */ -/* The structure where cells are allocated and cached. */ +/* The structure where fxcells and simplexes are allocated and cached. */ -/* Holds the cell and simplex match cache specific information */ +/* Holds the fxcell and simplex match cache specific information */ typedef struct { struct _rspl *s; /* Pointer to parent rspl */ int nacells; /* Number of allocated cells */ int nunlocked; /* Number of unlocked cells that could be freed */ int cell_hash_size; /* Current size of cell hash list */ - cell **hashtop; /* Top of hash list [cell_hash_size] */ - cell *mrutop, *mrubot; /* Top and bottom pointers of mru list */ - /* that tracks allocated cells */ + fxcell **hashtop; /* Top of hash list [cell_hash_size] */ + fxcell *mrutop, *mrubot; /* Top and bottom pointers of mru list */ + /* that tracks allocated fxcells */ - int spx_hash_size; /* Current size of simplex hash list */ - simplex **spxhashtop; /* Face simplex hash index list */ + int spx_hash_size; /* Current size of shared face simplex hash list */ + simplex **spxhashtop; /* Shared face simplex hash index list */ int nspx; /* Number of simplexes in hash list */ } revcache; @@ -296,8 +364,8 @@ struct _schbase { int ixc; /* Cube index of corner that holds maximum input values */ int snsdi, ensdi; /* Start and end extra sub-simplex dimensionality */ - int (*setsort)(struct _schbase *b, cell *c); /* Function to triage & set cube sort index */ - int (*check)(struct _schbase *b, cell *c); /* Function to recheck cube worth keeping */ + int (*setsort)(struct _schbase *b, fxcell *c); /* Function to triage & set cube sort index */ + int (*check)(struct _schbase *b, fxcell *c); /* Function to recheck cube worth keeping */ int (*compute)(struct _schbase *b, simplex *x); /* Function to compute a simplex solution */ double v[MXRO+1]; /* Target output value, + ink limit */ @@ -314,7 +382,7 @@ struct _schbase { double ncdir[MXRO]; /* Normalised clip vector */ double **cla; /* Clip vector LHS implicit equation matrix [fdi][fdi+1] (inc. ink tgt.) */ double clb[MXRO+1]; /* Clip vector RHS implicit equation vector [fdi+1] (inc. ink tgt.) */ - double cdist; /* Best clip locus distance found (aim is min +ve) */ + double cdist; /* Best clip locus distance found (aim is min +ve) :- weighted for nn */ int iclip; /* NZ if result is above (disabled) ink limit */ int mxsoln; /* Maximum number of solutions that we want */ @@ -328,20 +396,17 @@ struct _schbase { int axislz; /* Space allocated to axisl[] */ axisec *axisl; /* Auxiliary intersections */ - int lclistz; /* Allocated space to cell sort list */ - cell **lclist; /* Sorted list of pointers to candidate cells */ + int lclistz; /* Allocated space to fxcell sort list */ + fxcell **lclist; /* Sorted list of pointers to candidate fxcells */ - int pauxcell; /* Indexe of previous call solution cell, -1 if not relevant */ - int plmaxcell; /* Indexe of previous call solution cell, -1 if not relevant */ - int plmincell; /* Indexe of previous call solution cell, -1 if not relevant */ + int pauxcell; /* Indexe of previous call solution fxcell, -1 if not relevant */ + int plmaxcell; /* Indexe of previous call solution fxcell, -1 if not relevant */ + int plmincell; /* Indexe of previous call solution fxcell, -1 if not relevant */ int lsxfilt; /* Allocated space of simplex filter list */ - char *sxfilt; /* Flag for simplexes that should be in a cell */ + char *sxfilt; /* Flag for simplexes that should be in an fxcell */ - double crad; /* nn current radius distance */ - double bw; /* nn current window distance */ - int wex[MXRO * 2]; /* nn current window edge indexes */ - double wed[MXRO * 2]; /* nn current window edge distances */ + int rix; /* Diagnostic - rev[] or nnrev[] index for this point */ }; typedef struct _schbase schbase; @@ -376,7 +441,12 @@ struct _rev_struct { /* All other sections depend on this. */ int fastsetup; /* Flag - NZ if fast setup at cost of slow throughput */ - struct _rev_struct *next; /* Linked list of instances sharing memory */ + int lchweighted; /* Non-zero if nearest search is LCh weighted */ + double lchw[MXRO]; /* LCh nearest weighting */ + double lchw_sq[MXRO]; /* LCh nearest weighting squared */ + double lchw_chsq; /* lchw_sq[1] - lchw_sq[2] */ + + struct _rev_struct *next; /* Linked list of global instances sharing memory */ size_t max_sz; /* Maximum size permitted */ size_t sz; /* Total memory current allocated by rev */ @@ -385,32 +455,38 @@ struct _rev_struct { #endif /* Reverse grid lookup information */ - int ares; /* Reverse grid allocated resolution, = res + 1, */ - /* allows for extra row used during construction */ int res; /* Reverse grid resolution == ncells on a side */ int no; /* Total number of points in reverse grid = rev.ares ^ fdi */ int coi[MXRO]; /* Coordinate increments for each dimension */ int hoi[1 << MXRO]; /* Coordinate increments for progress through cube */ - datao gl,gh,gw; /* Reverse grid low, high, grid cell width */ + datao gl,gh,gw; /* Reverse grid low, high, grid bwd cell width */ /* Second section, accelleration information that changes with ink limit. */ int rev_valid; /* nz if this information in rev[] and nnrev[] is valid */ int **rev; /* Exact reverse lookup starting list. */ - /* rev.no pointers to lists of fwd grid indexes. */ - /* First int is allocation length */ - /* Second int is reference count */ - /* Then follows cube indexes */ - /* Last int is -1 */ int **nnrev; /* Nearest reverse lookup starting list. */ - /* rev.no pointers to lists of fwd grid indexes. */ + /* These lists are of fwd grid indexes. */ /* [0] is allocation length */ - /* [1] is the next free entry index */ - /* [2] is reference count */ + /* [1] is the next free entry index (length + 3, not counting -1) */ + /* [2] is index into share lists, -1 if not shared. */ /* Then follows cube indexes */ - /* The last entry is marked with -1 */ + /* Last entry is marked with -1 */ + + double ocent[MXRO]; /* rev cell gamut "center" point for thinning and shadow testing. */ + + bxcell *surflist; /* Linked list of rev[] bwd cells that contain gamut surface fwd cells. */ + /* Used to speed up fill_nncell() when rev.fastsetup is set, else NULL */ + int surf_hash_size; /* Current size of bxcell hash list */ + bxcell **surfhash; /* bxcell hash index list */ + + int **sharelist; /* Array of pointers to shared (fwd grid list sharer) records. */ + /* Each record is same format as rev[]/nnrev[], except */ + /* [2] is used to detect scanning the same list. */ + int sharellen; /* Size of sharelist */ + int sharelaloc; /* Allocation size of sharelist */ /* Third section */ - revcache *cache; /* Where cells are allocated and cached */ + revcache *cache; /* Where fxcells and simplexes are allocated and cached */ /* Sub-dimension simplex information */ ssxinfo sspxi[MXRI+1];/* One per sub dimenstionality at offset sdi */ @@ -425,6 +501,14 @@ struct _rev_struct { int primsecwarn; /* Not primary or secondary warning has been issued */ +#ifdef CHECK_NNLU +int cknn_no; /* Number checked */ +double cknn_we; /* Worst DE */ +int cknn_noerrs; /* Number not as good as closet vertex */ +int cknn_nobsb; /* Number not as good as second closest vertex */ +int cknn_nonis; /* Number not in surface */ +#endif /* CHECK_NNLU */ + }; typedef struct _rev_struct rev_struct; diff --git a/rspl/revbench.c b/rspl/revbench.c index 81e668e..f61ff2e 100644 --- a/rspl/revbench.c +++ b/rspl/revbench.c @@ -27,6 +27,7 @@ #undef DOLIMIT /* Define to have ink limit */ #define LIMITVAL 2.5 /* Total ink limit sum */ #undef DOCHECK +#define TESTNN /* Test Nearest Clipping */ #define SHOW_OUTPUT /* Define for printf on each conversion */ @@ -217,6 +218,9 @@ main(int argc, char *argv[]) { stime = clock(); +#ifdef TESTNN + flags |= RSPL_NEARCLIP; +#endif /* Itterate though the grid */ for (ops = 0;; ops++) { int r; diff --git a/rspl/rspl.c b/rspl/rspl.c index 431a7e3..db63a47 100644 --- a/rspl/rspl.c +++ b/rspl/rspl.c @@ -60,6 +60,7 @@ static void get_out_range(rspl *s, double *min, double *max); static void get_out_range_points(rspl *s, int *minp, int *maxp); static double get_out_scale(rspl *s); static unsigned int get_next_touch(rspl *s); +static int *get_res(rspl *s); static int within_restrictedsize(rspl *s); static int interp_rspl_sx(rspl *s, co *pp); static int part_interp_rspl_sx(rspl *s, co *p1, co *p2); @@ -180,6 +181,7 @@ printf("!!!! rspl.c using interp_rspl_nl !!!!"); s->get_out_range = get_out_range; s->get_out_range_points = get_out_range_points; s->get_out_scale = get_out_scale; + s->get_res = get_res; s->get_next_touch = get_next_touch; s->within_restrictedsize = within_restrictedsize; @@ -416,6 +418,12 @@ rspl *s } /* ============================================ */ +/* Return a pointer to the resolution array */ +static int *get_res(rspl *s) { + return s->g.res; +} + +/* ============================================ */ /* Return non-zero if this rspl can be */ /* used with Restricted Size functions. */ static int within_restrictedsize( diff --git a/rspl/rspl.h b/rspl/rspl.h index ae0a165..7d096f9 100644 --- a/rspl/rspl.h +++ b/rspl/rspl.h @@ -19,6 +19,9 @@ /** Configuration **/ +#undef CHECK_NNLU /* [und] Check nn lookup results against exaustive searches */ + /* as well as other asserts. */ + /** General Limits **/ #define MXDI 10 /* Maximum input dimensionality */ @@ -43,6 +46,7 @@ #define POW2MXRI 16 /* 2 ^ MXRI */ #define POW3MXRI 81 /* 3 ^ MXRI */ #define HACOMPS ((POW3MXRI + 2 * MXRI + 1)/2) /* Maximum number of array components */ +#define POW2MXRO 1024 /* 2 ^ MXRO */ #if MXRI > MXRO /* Maximum of either RI or RO */ # define MXRIRO MXRI @@ -175,7 +179,7 @@ struct _rspl { /* Array is res[] ^ di entries float[fdi+G_XTRA], offset by G_XTRA */ /* (But is expanded when spline interpolaton is active) */ /* float[-1] contains the ink limit function value, L_UNINIT if not initd */ - /* float[-2] contains the edge flag values, 2 bits per in dim. */ + /* float[-2] contains the edge flag values, 3 bits per in dim. */ /* float[-3] contains the touched flag generation count. */ /* (k value for non-linear fit would be another entry.) */ /* Flag values are 3 bits for each dimension. Bits 1,0 form */ @@ -188,9 +192,15 @@ struct _rspl { /* Uninitialised limit value */ #define L_UNINIT ((float)-1e38) +#define FL_BITS 3 /* flag bits per dimension */ /* Macros to access flags. Arguments are a pointer to base grid point and */ /* Flag value is distance from edge in bottom 2 bits, values 0, 1 or 2 maximum. */ - /* bit 2 is set if the distance is to the lower edge. */ + /* bit 2 is set if the distance is to the lower edge. ie: */ + /* 0 = at top edge */ + /* 1 = next to top edge */ + /* 2, 6 = not at or next to any edge */ + /* 4 = at bottom edge */ + /* 5 = next to bottom edge */ #define FLV(fp) (*((unsigned int *)((fp)-2))) /* Init the flag values to 0 */ #define I_FL(fp) (FLV(fp) = 0) @@ -407,8 +417,8 @@ struct _rspl { void (*scan_rspl)( struct _rspl *s, /* this */ - int flags, /* Combination of flags (not used) */ - void *cbntx, /* Opaque function context */ + int flags, /* Combination of flags (not used) */ + void *cbntx, /* Opaque function context */ void (*func)(void *cbntx, double *out, double *in) /* Function that gets given values */ ); @@ -442,8 +452,8 @@ struct _rspl { void (*filter_rspl)( struct _rspl *s, /* this */ - int flags, /* Combination of flags (not used) */ - void *cbntx, /* Opaque function context */ + int flags, /* Combination of flags (not used) */ + void *cbntx, /* Opaque function context */ void (*func)(void *cbntx, float **out, double *in, int cvi) /* Function to set from */ ); @@ -510,17 +520,26 @@ struct _rspl { double *limitv /* Return limit value */ ); + /* Set the RSPL_NEARCLIP LCh weightings. */ + /* Will only work with L*a*b* like output spaces. */ + /* Calling this will clear the reverse interpolaton cache. */ + void (*rev_set_lchw)( + struct _rspl *s, /* this */ + double lchw[MXRO] /* Weighting */ + ); + /* Possible reverse hint flags */ -#define RSPL_WILLCLIP 0x0001 /* Hint that clipping will be needed */ -#define RSPL_EXACTAUX 0x0002 /* Hint that auxiliary target will be matched exactly */ -#define RSPL_MAXAUX 0x0004 /* If not possible to match exactly, return the */ +#define RSPL_WILLCLIP 0x0001 /* Hint that clipping will be needed */ +#define RSPL_EXACTAUX 0x0002 /* Hint that auxiliary target will be matched exactly */ +#define RSPL_MAXAUX 0x0004 /* If not possible to match exactly, return the */ /* closest value larger than the target, rather than */ /* absolute closest. */ -#define RSPL_AUXLOCUS 0x0008 /* Auxiliary target is proportion of locus, not */ +#define RSPL_AUXLOCUS 0x0008 /* Auxiliary target is proportion of locus, not */ /* absolute. Implies EXACTAUX hint. */ -#define RSPL_NEARCLIP 0x0010 /* If clipping occurs, return the nearest solution, */ +#define RSPL_NEARCLIP 0x0010 /* If clipping occurs, return the nearest solution, */ /* rather than the one in the clip direction. */ - +#define RSPL_NONNSETUP 0x0020 /* Sets RSPL_FASTREVSETUP flag, which avoids NN grid */ + /* setup if this is the first call using RSPL_NEARCLIP. */ /* Return value masks */ #define RSPL_DIDCLIP 0x8000 /* If this bit is set, at least one soln. and clipping occured */ #define RSPL_NOSOLNS 0x7fff /* And return value with this mask to get number of solutions */ @@ -587,6 +606,9 @@ struct _rspl { # define wvals ad##jw + /* Return a pointer to the resolution array */ + int *(*get_res)(struct _rspl *s); + /* Return non-zero if this rspl can be */ /* used with Restricted Size functions. */ int (*within_restrictedsize)( diff --git a/rspl/rspl1.c b/rspl/rspl1.c index dc3588b..3996269 100644 --- a/rspl/rspl1.c +++ b/rspl/rspl1.c @@ -1,5 +1,5 @@ - /* Single dimension regularized spline data structure */ +/* Single dimension regularized spline data structure */ /* * Argyll Color Correction System @@ -339,6 +339,11 @@ static int fit_rspl( smooth, avgdev, ipos); } +/* Return a pointer to the resolution array */ +static int *get_res(rspl *s) { + return &s->nig; +} + /* Initialise the regular spline from scattered data with weights */ /* Return nz on error */ static int @@ -361,6 +366,80 @@ fit_rspl_w( smooth, avgdev, ipos); } +/* Initialize the grid from a provided function. */ +/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]) */ +static int set_rspl( + struct _rspl *s,/* this */ + int flags, /* (Not used) */ + void *cbctx, /* Opaque function context */ + void (*func)(void *cbctx, double *out, double *in), /* Function to set from */ + datai glow, /* Grid low scale, NULL = default 0.0 */ + datai ghigh, /* Grid high scale, NULL = default 1.0 */ + int *gres, /* Spline grid resolution for each dimension */ + datao vlow, /* Data value low normalize, NULL = default 0.0 */ + datao vhigh /* Data value high normalize, NULL = default 1.0 */ +) { + int n; + double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */ + double ov[MXDO]; + + DBGF((DBGA, "rspl1:set_rspl() callen")); + + /* Allocate space for interpolation grid */ + s->nig = *gres; + + if ((s->x = dvector(0, s->nig)) == NULL) { + DBGF((DBGA, "rspl1:Malloc of vector x failed\n")); + return 1; + } + + s->xl = s->gl = glow != NULL ? *glow : 0.0; + s->xh = s->gh = ghigh != NULL ? *ghigh : 1.0; + + /* Set the input scaling */ + s->gw = (s->gh - s->gl)/(double)(s->nig-1); + + /* Set the default output scaling */ + s->vl = vlow != NULL ? *vlow : 0.0; + s->vw = ((vhigh != NULL ? *vhigh : 1.0) - s->vl); + + DBGF((DBGA, "rspl1:gl %f, gh %f, gw %f, vl %f, vw %f\n",s->gl,s->gh,s->gw,s->vl,s->vw)); + + /* Lookup the values at the grid points */ + for (n = 0; n < s->nig; n++) { + double vv; + + /* Compute grid pointer and input sample values */ + iv[0] = s->gl + n * s->gw; /* Input sample values */ + *((int *)&iv[-1-1]) = n; /* Trick to supply grid index in iv[] */ + + /* Apply incolor -> outcolor function we want to represent */ + func(cbctx, ov, iv); + + s->x[n] = (float)ov[0]; /* Set unscaled output value */ + + if (s->x[n] < s->dl) + s->dl = s->x[n]; + if (s->x[n] > s->dh) + s->dh = s->x[n]; + } + + /* Adjust output scaling */ + s->vw += s->vl; /* Convert to high */ + if (s->dl < s->vl) + s->vl = s->dl; + if (s->dh < s->vw) + s->vw = s->dh; + s->vw -= s->vl; /* Convert to width */ + + /* Apply scaling to data */ + for (n = 0; n < s->nig; n++) { + s->x[n] = (s->x[n] - s->vl)/s->vw; + } + + return 0; +} + /* Construct an empty rspl1 */ /* Return NULL if something goes wrong. */ rspl *new_rspl(int flags, int di, int fdi) { @@ -380,6 +459,8 @@ rspl *new_rspl(int flags, int di, int fdi) { t->interp = interp; t->fit_rspl = fit_rspl; t->fit_rspl_w = fit_rspl_w; + t->set_rspl = set_rspl; + t->get_res = get_res; t->del = del_rspl; return t; diff --git a/rspl/rspl1.h b/rspl/rspl1.h index 747abd5..63be428 100644 --- a/rspl/rspl1.h +++ b/rspl/rspl1.h @@ -1,7 +1,7 @@ #ifndef _RSPL1_H_ - /* Single dimension regularized spline data structure */ +/* Single dimension regularized spline data structure */ /* * Argyll Color Correction System @@ -18,10 +18,22 @@ * */ +/* + * Might be nice to add support for simple rev lookup, so that + * standalone xcal can use it ?? + */ + + #ifdef __cplusplus extern "C" { #endif +/* Make up for possible lack of rspl.h */ +#ifndef MXDI +# define MXDI 10 /* Maximum input dimensionality */ +# define MXDO 10 /* Maximum output dimensionality (Is not fully tested!!!) */ +#endif + /* General data point position/value structure */ typedef double datai[1]; typedef double datao[1]; @@ -94,12 +106,30 @@ struct _rspl { double **ipos /* (not used) */ ); + /* Initialize the grid from a provided function. */ + /* Grid index values are supplied "under" in[] at *((int*)&in[-e-1]) */ + int + (*set_rspl)( + struct _rspl *s, /* this */ + int flags, /* (Not used) */ + void *cbntx, /* Opaque function context */ + void (*func)(void *cbntx, double *out, double *in), /* Function to set from */ + datai glow, /* Grid low scale, NULL = default 0.0 */ + datai ghigh, /* Grid high scale, NULL = default 1.0 */ + int gres[MXDI], /* Spline grid resolution */ + datao vlow, /* Data value low normalize, NULL = default 0.0 */ + datao vhigh /* Data value high normalize - NULL = default 1.0 */ + ); + /* Do forward interpolation */ /* Return 0 if OK, 1 if input was clipped to grid */ int (*interp)( struct _rspl *s, /* this */ co *p); /* Input and output values */ + /* Return a pointer to the resolution array */ + int *(*get_res)(struct _rspl *s); + }; typedef struct _rspl rspl; /* Create a new, empty rspl object */ diff --git a/rspl/scat.c b/rspl/scat.c index f1535fa..f94e074 100644 --- a/rspl/scat.c +++ b/rspl/scat.c @@ -72,7 +72,7 @@ a curvature error). The default assumption is that the grid resolution is set - to matche the input data range for that dimension, eg. if + to match the input data range for that dimension, eg. if a sub range of input space is all that is needed, then a smaller grid resolution can/should be used if smoothness is expected to remain symetric in relation to the input @@ -129,13 +129,17 @@ /* algorithm parameters [Release defaults] */ #define INCURVEADJ /* [Defined] Adjust smoothness criteria for input curve grid spacing */ -#undef SMOOTH2 /* [Undef] INCOMPLETE Use 3nd order smoothness rather than curvature. */ +#undef SMOOTH2 /* [Undef] INCOMPLETE - would be nice to finish this to help XYZ! */ + /* Use 3nd order smoothness rather than curvature. */ /* 2nd order is optimal about 2.5 x lower than 3rd order, */ /* so an even split between 3rd:2nd would be 1.0 0.4, */ /* a 9:1 split would be 0.9 0.04 */ /* This also disables the incorrect scaling of smoothness with */ /* output range */ #undef AUTOSM /* [Undef] INCOMPLETE Support auto smoothing using LOOCV */ + /* - started implementing this using shadow grid map of */ + /* smoothness (see see mgtmp *sm), then switch to */ + /* Leave One Out Cross Validation (LOOCV) idea. */ # define CW2 0.9 # define CW ((1.0 - CW2) * 0.4) @@ -275,6 +279,7 @@ struct _loocv { /* in the list in the cell. -1 for no more data */ double *sm; /* smoothness map grid data values in log space, 0.0 for none */ + /* (Not fully implemented, and being superceeded) */ double **As; /* A matrix of smoothness vertex weights */ double *bs; /* b vector for RHS of smoothness equation */ @@ -1725,7 +1730,7 @@ print_smsens(mgtmp *m) { of any sum term that does not have the grid point in question in it will have a partial derivative of zero, each row equation consists of just those terms that have that grid points value in it, - with the vast majoroty of the sum terms omitted. + with the vast majority of the sum terms omitted. */ @@ -1871,7 +1876,7 @@ mgtmp *sm /* Optional smoothing map for ausm mode */ /* The ipos[] factor is to allow for the possibility that the */ /* grid spacing may be non-uniform in the colorspace where the */ /* function being modelled is smooth. Our curvature computation */ - /* needs to make allowsance for this fact in computing the */ + /* needs to make allowance for this fact in computing the */ /* node value differences that equate to zero curvature. */ /* The old curvature fixed grid spacing equation was: @@ -2078,7 +2083,7 @@ mgtmp *sm /* Optional smoothing map for ausm mode */ } /* We setup the equation to be solved for each grid point. */ - /* Each grid point participates in foure curvature equations, */ + /* Each grid point participates in four curvature equations, */ /* one centered on the grid line below, one that it's the center of, */ /* one centered on the grid line above, and one centered on the */ /* grid line two above. The equation setup is for the differential */ @@ -22,7 +22,7 @@ #include "rspl.h" #include "numlib.h" #include "tiffio.h" -#include "plot.h" +//#include "plot.h" #ifdef NEVER FILE *verbose_out = stdout; @@ -217,7 +217,7 @@ int main(int argc, char *argv[]) { cvec[3] = 0.0 - tp[0].v[3]; /* Do reverse interpolation ~~~1 */ - if ((r = rss->rev_interp(rss, 0, NIP, auxm, NULL /*cvec*/, tp)) > 0) { + if ((r = rss->rev_interp(rss, 0, NIP, auxm, NULL /*cvec/LChW*/, tp)) > 0) { printf("Total of %d Results\n",r); for (i = 0; i < r; i++) printf("Result %d = %f, %f, %f, %f\n",i, tp[i].p[0],tp[i].p[1],tp[i].p[2],tp[i].p[3]); |