Files
2026-08-20 20:28:40 +01:00

1687 lines
47 KiB
C

/*
* Argyll Color Management System
* Multi-dimensional regularized splines data fitter
*
* Author: Graeme W. Gill
* Date: 30/1/00
*
* Copyright 1996 - 2004 Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*/
/* Version that a combination of relaxation and conjugate gradient */
/* solution techniques. */
/* TTBD:
To save space, the full columns of A should only be allocated when needed ?
(Does this actually save much space for a realistic data sample set ?)
Get rid of error() calls - return status instead.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <time.h>
#include "rspl_imp.h"
#include "numlib.h"
#include "counters.h" /* Counter macros */
#undef DEBUG
#undef DEBUGLU /* Debug fwd interpolation */
#undef VERBOSE
#undef NEVER
#define ALWAYS
#ifdef DEBUGLU
# define DEBLU(xxxx) printf xxxx
char *icmPiv(int di, int *p);
char *icmPdv(int di, double *p);
char *icmPfv(int di, float *p);
#else
# define DEBLU(xxxx)
#endif
#undef USING_INTERP_NL /* [und] */
/* Implemeted in this file: */
rspl *new_rspl(int flags, int di, int fdi);
static void free_rspl(rspl *s);
static void init_grid(rspl *s);
static void free_grid(rspl *s);
static void get_in_range(rspl *s, double *min, double *max);
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);
#ifdef USING_INTERP_NL
static int interp_rspl_nl(rspl *s, co *p);
#endif
static double interp1_rspl(rspl *s, double in);
int is_mono(rspl *s);
static int set_rspl(rspl *s, int flags, void *cbctx,
void (*func)(void *cbctx, double *out, double *in),
datai glow, datai ghigh, int gres[MXDI], datao vlow, datao vhigh);
static int re_set_rspl(struct _rspl *s, int flags, void *cbntx,
void (*func)(void *cbntx, double *out, double *in));
static void scan_rspl(struct _rspl *s, int flags, void *cbntx,
void (*func)(void *cbntx, double *out, double *in));
static int tune_value(struct _rspl *s, co *p);
static void filter_rspl(struct _rspl *s, int flags, void *cbctx,
void (*func)(void *cbntx, float **out, double *in, int cvi));
extern int add_rspl(rspl *s, int flags, co *d, int dno);
extern void init_data(rspl *s);
extern void free_data(rspl *s);
/* Implemented in rev.c: */
void init_rev(rspl *s);
void free_rev(rspl *s);
/* Implemented in gam.c: */
void init_gam(rspl *s);
void free_gam(rspl *s);
/* Implemented in spline.c: */
void init_spline(rspl *s);
void free_spline(rspl *s);
/* Implemented in opt.c: */
int opt_rspl_imp(struct _rspl *s, int flags, int tdi, int adi, double **vdata,
double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
void *fdata, datai glow, datai ghigh, int gres[MXDI], datao vlow, datao vhigh);
/* Convention is to use:
i to index grid points u.a
n to index data points d.a
e to index position dimension di
f to index output function dimension fdi
j misc and cube corners
k misc
*/
/* ================================ */
/* Allocate an empty rspl object. */
rspl *
new_rspl(
int flags,
int di,
int fdi
) {
rspl *s;
#ifdef DEBUG
fprintf(stderr,"new_rspl with flags 0x%x, di %d, fdi %d\n",flags,di,fdi);
#endif
/* Allocate a structure */
if ((s = (rspl *) calloc(1, sizeof(rspl))) == NULL)
error("rspl: malloc failed - main structure");
/* Set our fundamental parameters */
if (di < 1 || di > MXDI)
error("rspl: can't handle input dimension %d",di);
s->di = di;
if (fdi < 1 || fdi > MXDO)
error("rspl: can't handle output dimension %d",fdi);
s->fdi = fdi;
/* And appropriate flags */
if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
s->verbose = 1;
if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
s->verbose = 0;
/* Allocate space for cube offset arrays */
s->g.hi = s->g.a_hi;
s->g.fhi = s->g.a_fhi;
if ((1 << di) > DEF2MXDI) {
if ((s->g.hi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
error("rspl malloc failed - hi[]");
if ((s->g.fhi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
error("rspl malloc failed - fhi[]");
}
/* Init sub sections */
init_data(s);
init_grid(s);
init_rev(s);
init_gam(s);
init_spline(s);
if (flags & RSPL_FASTREVSETUP)
s->rev.fastsetup = 1;
else
s->rev.fastsetup = 0;
/* Set pointers to methods in this file */
s->del = free_rspl;
s->interp = interp_rspl_sx; /* Default to simplex interp */
#ifdef USING_INTERP_NL
# pragma message("!!!!!!!!! USING_INTERP_NL defined !!!!!!!!!!!")
s->interp = interp_rspl_nl;
#endif
s->interp1 = interp1_rspl;
s->part_interp = part_interp_rspl_sx;
s->set_rspl = set_rspl;
s->scan_rspl = scan_rspl;
s->re_set_rspl = re_set_rspl;
s->tune_value = tune_value;
s->opt_rspl = opt_rspl_imp;
s->filter_rspl = filter_rspl;
s->get_in_range = get_in_range;
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;
return s;
}
/* Free the rspl and all its contents */
static void free_rspl(rspl *s) {
int e;
/* Free everying contained */
free_data(s); /* Free any scattered data */
free_rev(s); /* Free any reverse lookup data */
free_gam(s); /* Free any grid data */
free_grid(s); /* Free any grid data */
/* Free spline interpolation data ~~~~ */
/* Free structure */
for (e = 0; e < s->di; e++) {
if (s->g.ipos[e] != NULL)
free(s->g.ipos[e]);
}
if (s->g.hi != s->g.a_hi) {
free(s->g.hi);
free(s->g.fhi);
}
free((void *) s);
}
/* ======================================================== */
/* Allocate rspl grid data, and initialise grid associated stuff */
void
alloc_grid(rspl *s) {
int di = s->di, fdi = s->fdi;
int e,g,i;
int gno; /* Number of points in grid */
ECOUNT(gc, MXDIDO, di, 0, s->g.res, 0);/* coordinates */
float *gp; /* Grid point pointer */
#ifdef DEBUG
fprintf(stderr,"rspl allocating grid res %s\n",icmPiv(di, s->g.res));
#endif
/* Compute total number of elements in the grid */
for (gno = 1, e = 0; e < di; gno *= s->g.res[e], e++)
;
s->g.no = gno;
s->g.pss = fdi+G_XTRA; /* float for each output value + nme + flags */
/* Compute index coordinate increments into linear grid for each dimension */
/* ie. 1, gres, gres^2, gres^3 */
for (s->g.ci[0] = 1, e = 1; e < di; e++)
s->g.ci[e] = s->g.ci[e-1] * s->g.res[e-1]; /* In grid points */
for (e = 0; e < di; e++)
s->g.fci[e] = s->g.ci[e] * s->g.pss; /* In floats */
/* Compute index offsets from base of cube to other corners. */
for (s->g.hi[0] = e = 0, g = 1; e < di; g *= 2, e++) {
for (i = 0; i < g; i++)
s->g.hi[g+i] = s->g.hi[i] + s->g.ci[e]; /* In grid points */
}
/* same as hi, but in floats */
for (i = 0; i < (1 << di); i++)
s->g.fhi[i] = s->g.hi[i] * s->g.pss; /* In floats */
/* Allocate space for grid */
if ((s->g.alloc = (float *) malloc(sizeof(float) * gno * s->g.pss)) == NULL)
error("rspl malloc failed - grid points");
s->g.a = s->g.alloc + G_XTRA; /* make -1 be nme, and -2 be (unsigned int) flags */
/* Set initial value of cell touch count */
s->g.touch = 0;
/* Init near edge flags, and touched flag */
EC_INIT(gc);
for (i = 0, gp = s->g.a; !EC_DONE(gc); i++, gp += s->g.pss) {
gp[-1] = L_UNINIT; /* Init Ink limit function value to -1e38 */
I_FL(gp); /* Init all flags to zero */
for (e = 0; e < di; e++) {
int e1,e2;
e1 = gc[e]; /* Dist to bottom edge */
e2 = (s->g.res[e]-1) - gc[e]; /* Dist to top edge */
if (e2 < e1) { /* Top edge is closer */
if (e2 > 2)
e2 = 2; /* Max dist = 2 */
S_FL(gp,e,e2); /* Set flag value */
} else { /* Bot edge is closer */
if (e1 > 2)
e1 = 2; /* Max dist = 2 */
S_FL(gp,e,e1 | 0x4); /* Set flag value */
}
}
TOUCHF(gp) = 0;
EC_INC(gc);
}
s->g.limitv_cached = 0; /* No limit values are current cached */
}
/* Init grid related elements of rspl */
static void
init_grid(rspl *s) {
s->g.alloc = NULL;
}
/* Free the grid allocation */
static void
free_grid(rspl *s) {
if (s->g.alloc != NULL)
free((void *)s->g.alloc);
}
/* ============================================ */
/* Return the range of possible input values that the grid can represent */
static void
get_in_range(
rspl *s, /* Grid to search */
double *min, double *max /* Return min/max values */
) {
int e;
for (e = 0; e < s->di; e++) {
min[e] = s->g.l[e];
max[e] = s->g.h[e];
}
}
/* ============================================ */
/* Discover the range of possible output values */
static void
get_out_range(
rspl *s, /* Grid to search */
double *min, double *max /* Return min/max values */
) {
float *gp,*ep; /* Grid pointer */
int f;
if (s->g.fminmax_valid == 0) { /* Not valid, so compute it */
for (f = 0; f < s->fdi; f++) {
s->g.fmin[f] = 1e30;
s->g.fmax[f] = -1e30;
s->g.fminx[f] = -1;
s->g.fmaxx[f] = -1;
}
/* Scan the Grid points 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 (f = 0; f < s->fdi; f++) {
if (s->g.fmin[f] > gp[f]) {
s->g.fmin[f] = gp[f];
s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
}
if (s->g.fmax[f] < gp[f]) {
s->g.fmax[f] = gp[f];
s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
}
}
}
/* Compute overall output scale */
for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
double tt = s->g.fmax[f] - s->g.fmin[f];
s->g.fscale += tt * tt;
}
s->g.fscale = sqrt(s->g.fscale);
s->g.fminmax_valid = 1; /* Now is valid */
}
for (f = 0; f < s->fdi; f++) {
if (min != NULL)
min[f] = s->g.fmin[f];
if (max != NULL)
max[f] = s->g.fmax[f];
}
}
/* ============================================ */
/* return the grid index of the grid values at the min & max output values */
static void get_out_range_points(rspl *s, int *minp, int *maxp) {
int f;
if (s->g.fminmax_valid == 0) /* Not valid, so compute it */
get_out_range(s, NULL, NULL);
for (f = 0; f < s->fdi; f++) {
if (minp != NULL)
minp[f] = s->g.fminx[f];
if (maxp != NULL)
maxp[f] = s->g.fmaxx[f];
}
}
/* ============================================ */
/* Discover the csale of the output values */
static double
get_out_scale(rspl *s) {
if (s->g.fminmax_valid == 0) /* Not valid, so compute it */
get_out_range(s, NULL, NULL);
return s->g.fscale;
}
/* ============================================ */
/* Return the next touched flag count value. */
/* Whenever this rolls over, all the flags in the grid array will be reset. */
/* */
/* The touch flag is a way of some grid accessor (ie. rev()) making sure */
/* that it doesn't access cells more than once for a particular operation, */
/* without sorting a list of items to be accessed, or having to reset a */
/* binary flag on all the cells for each operation. */
/* If the value of a cells TOUCHF() is less than s->g.touch, then it hasn't */
/* been accessed yet. Once the cell has been acessed, then TOUCHF() should */
/* be set to s->g.touch. After 2^32 operations, the cell touch flags will */
/* have been set to values between 0 and 2^32-1, so it is time to reset */
/* all the flags. For that reason, the following method should be used */
/* to get the next touch generation value at the start of each operation. */
static unsigned int
get_next_touch(
rspl *s
) {
unsigned int tg;
float *gp,*ep; /* Grid pointer */
if ((tg = ++s->g.touch) == 0) {
/* We have to reset all the cell flags to zero before we roll over */
for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
TOUCHF(gp) = 0;
}
tg = ++s->g.touch; /* return 1 */
}
return tg;
}
/* ============================================ */
/* 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(
rspl *s
) {
if (s->di <= MXRI && s->fdi <= MXRO)
return 1;
return 0;
}
/* ============================================ */
/* Do a forward interpolation using an simplex interpolation method. */
/* Return 0 if OK, 1 if input was clipped to grid */
/* Return 0 on success, 1 if clipping occured, 2 on other error */
// ~~999
//int rspldb = 0;
static int interp_rspl_sx(
rspl *s,
co *p /* Input value and returned function value */
) {
int e, di = s->di;
int f, fdi = s->fdi;
double we[MXDI]; /* Coordinate offset within the grid cell */
int si[MXDI]; /* we[] Sort index, [0] = smallest */
float *gp; /* Pointer to grid cube base */
int rv = 0; /* Register clip */
/* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
DEBLU(("In %s\n", icmPdv(di, p->p)));
/* Figure out which grid cell the point falls into */
{
gp = s->g.a; /* Base of grid array */
for (e = 0; e < di; e++) {
int gres_1 = s->g.res[e]-1;
double pe, t;
int mi;
pe = p->p[e];
if (pe < s->g.l[e]) { /* Clip to grid */
pe = s->g.l[e];
rv = 1;
}
if (pe > s->g.h[e]) {
pe = s->g.h[e];
rv = 1;
}
t = (pe - s->g.l[e])/s->g.w[e];
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
else if (mi >= gres_1)
mi = gres_1-1;
gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
we[e] = t - (double)mi; /* 1.0 - weight */
//if (rspldb && di == 3) printf("~1 e = %d, ix = %d, we = %f\n", e, mi, we[e]);
}
DEBLU(("ix %d, we %s\n", (int)(gp - s->g.a)/s->g.pss, icmPdv(di, p->p)));
}
/* Do selection sort on coordinates */
{
for (e = 0; e < di; e++)
si[e] = e; /* Initial unsorted indexes */
for (e = 0; e < (di-1); e++) {
double cosn;
cosn = we[si[e]]; /* Current smallest value */
for (f = e+1; f < di; f++) { /* Check against rest */
int tt;
tt = si[f];
if (cosn > we[tt]) {
si[f] = si[e]; /* Exchange */
si[e] = tt;
cosn = we[tt];
}
}
}
}
DEBLU(("si[] = %s\n", icmPiv(di, si)));
/* Now compute the weightings, simplex vertices and output values */
{
double w; /* Current vertex weight */
w = 1.0 - we[si[di-1]]; /* Vertex at base of cell */
for (f = 0; f < fdi; f++)
p->v[f] = w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
for (e = di-1; e > 0; e--) { /* Middle vertices */
w = we[si[e]] - we[si[e-1]];
gp += s->g.fci[si[e]]; /* Move to top of cell in next largest dimension */
for (f = 0; f < fdi; f++)
p->v[f] += w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
}
w = we[si[0]];
gp += s->g.fci[si[0]]; /* Far corner from base of cell */
for (f = 0; f < fdi; f++)
p->v[f] += w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
DEBLU(("Outval %s\n", icmPdv(fdi, p->v)));
}
return rv;
}
/* ============================================ */
/* Just interpolate channel 0 */
/* If the input is outside the grid range, it will */
/* be clamped to the nearest grid point. */
static double interp1_rspl(rspl *t, double in) {
co p = { 0 };
p.p[0]= in;
t->interp(t, &p);
return p.v[0];
}
/* ============================================ */
/* Do forward (partial) interpolation to allow input & output curves to be applied, */
/* and allow input delta E to be estimated from output delta E. */
/* Call with input value in p1[0].p[], */
/* In order smallest to largest weight: */
/* Return di+1 vertex values in p1[]].v[] and */
/* 0-1 sub-cell weight values as (p1[].p[0] - p1[].p[1]). */
/* Optionally in input channel order: */
/* Returns di+1 partial derivatives + base value in p2[].v[], */
/* with matching weight values for each in p2[].p[0] (last weight = 1)*/
/* Return 0 if OK, 1 if input was clipped to grid */
static int part_interp_rspl_sx(
struct _rspl *s, /* this */
co *p1,
co *p2 /* optional - return partial derivatives for each input channel */
) {
int e, di = s->di;
int f, fdi = s->fdi;
double we[MXDI]; /* Coordinate offset within the grid cell */
int si[MXDI]; /* we[] Sort index, [0] = smallest */
float *gp; /* Pointer to grid cube base */
int rv = 0; /* Register clip */
/* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
/* Figure out which grid cell the point falls into */
{
gp = s->g.a; /* Base of grid array */
for (e = 0; e < di; e++) {
int gres_1 = s->g.res[e]-1;
double pe, t;
int mi;
pe = p1[0].p[e];
if (pe < s->g.l[e]) { /* Clip to grid */
pe = s->g.l[e];
rv = 1;
}
if (pe > s->g.h[e]) {
pe = s->g.h[e];
rv = 1;
}
t = (pe - s->g.l[e])/s->g.w[e];
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
else if (mi >= gres_1)
mi = gres_1-1;
gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
we[e] = t - (double)mi; /* 1.0 - weight */
}
}
/* Do selection sort on coordinates */
{
for (e = 0; e < di; e++)
si[e] = e; /* Initial unsorted indexes */
for (e = 0; e < (di-1); e++) {
double cosn;
cosn = we[si[e]]; /* Current smallest value */
for (f = e+1; f < di; f++) { /* Check against rest */
int tt;
tt = si[f];
if (cosn > we[tt]) {
si[f] = si[e]; /* Exchange */
si[e] = tt;
cosn = we[tt];
}
}
}
}
/* Now compute the vertex values that correspond */
/* to the input faction weightings + fixed value */
/* Scale the slopes + weights to make slopes */
/* valid as partial derivative of input values */
{
p1[di].p[0] = 1.0;
p1[di].p[1] = we[si[di-1]]; /* Vertex at base of cell */
for (f = 0; f < fdi; f++)
p1[di].v[f] = gp[f];
if (p2 != NULL) {
for (f = 0; f < fdi; f++)
p2[di].v[f] = gp[f]; /* Constant term @ vertex base */
p2[di].p[0] = 1.0;
}
for (e = di-1; e >= 0; e--) { /* Middle vertices to far vertex from base */
int ee = si[e];
float *lgp = gp; /* Last gp[] */
gp += s->g.fci[ee]; /* Move to top of cell in next largest dimension */
p1[e].p[0] = we[si[e]];
p1[e].p[1] = e > 0 ? we[si[e-1]] : 0.0;
for (f = 0; f < fdi; f++)
p1[e].v[f] = gp[f];
if (p2 != NULL) {
for (f = 0; f < fdi; f++)
p2[ee].v[f] = (gp[f] - lgp[f]) / s->g.w[ee];
p2[ee].p[0] = we[ee] * s->g.w[ee];
}
}
}
return rv;
}
#ifdef NEVER
/* Test out part_interp_rspl_sx() */
/* Designed to test with a CMYK->Lab lookup */
static int interp_rspl_sx(
rspl *s,
co *p /* Input value and returned function value */
) {
int rv, rv2;
int e, f, m;
co p1[MXDI+1];
co p2[MXDI+1];
co p3;
double v1[MXDO];
double v2[MXDO];
for (e = 0; e < s->di; e++) {
p1[0].p[e] = p->p[e];
p3.p[e] = p->p[e];
}
rv = _interp_rspl_sx(s, p);
if ((s->di != 4 || s->fdi != 3)
&& (s->di != 3 || s->fdi != 4))
return rv;
rv2 = part_interp_rspl_sx(s, p1, p2);
/* Check interpolation values returned in p1 and p2 form */
for (f = 0; f < s->fdi; f++)
v1[f] = v2[f] = 0.0;
for (e = 0; e <= s->di; e++) {
for (f = 0; f < s->fdi; f++) {
/* We could converts p1[].p[0] and p1[].p[1] through sub curve lookup, */
/* and p1[].v[] though inverse output curve lookup, */
/* then convert v1[] through output curve lookup. */
v1[f] += p1[e].v[f] * (p1[e].p[0] - p1[e].p[1]);
/* v2 is using base + partial derivatives */
v2[f] += p2[e].v[f] * p2[e].p[0];
}
}
if (s->di == 4) {
printf("~1 %f %f %f %f ->\n",p->p[0], p->p[1], p->p[2], p->p[3]);
printf("~1 ref %d -> %f %f %f\n", rv, p->v[0], p->v[1], p->v[2]);
printf("~1 check1 %d -> %f %f %f\n", rv2, v1[0], v1[1], v1[2]);
printf("~1 check2 %d -> %f %f %f\n", rv2, v2[0], v2[1], v2[2]);
} else {
printf("~1 %f %f %f ->\n",p->p[0], p->p[1], p->p[2]);
printf("~1 ref %d -> %f %f %f %f\n", rv, p->v[0], p->v[1], p->v[2], p->v[3]);
printf("~1 check1 %d -> %f %f %f %f\n", rv2, v1[0], v1[1], v1[2], v1[3]);
printf("~1 check2 %d -> %f %f %f %f\n", rv2, v2[0], v2[1], v2[2], v2[3]);
}
/* Check partial derivs in p2 */
for (m = 0; m < s->di; m++) {
p3.p[m] += 1e-5;
_interp_rspl_sx(s, &p3);
for (f = 0; f < s->fdi; f++)
p3.v[f] = (p3.v[f] - p->v[f])/1e-5;
if (s->di == 4) {
printf("~1 deriv %d:\n", m);
printf("~1 ref del %f %f %f\n", p3.v[0], p3.v[1], p3.v[2]);
printf("~1 check del %f %f %f\n", p2[m].v[0], p2[m].v[1], p2[m].v[2]);
} else {
printf("~1 deriv %d:\n", m);
printf("~1 ref del %f %f %f %f\n", p3.v[0], p3.v[1], p3.v[2], p3.v[3]);
printf("~1 check del %f %f %f %f\n", p2[m].v[0], p2[m].v[1], p2[m].v[2], p2[m].v[3]);
}
p3.p[m] -= 1e-5;
}
return rv;
}
#endif
/* ============================================ */
#ifdef USING_INTERP_NL
/* Alternate, not currently used */
/* Do a forward interpolation using an n-linear method. */
/* Return 0 if OK, 1 if input was clipped to grid */
/* Alternative to interp_rspl_sx */
static int interp_rspl_nl(
rspl *s,
co *p /* Input value and returned function value */
) {
int e, di = s->di;
int f, fdi = s->fdi;
double we[MXDI]; /* 1.0 - Weight in each dimension */
double *gw; /* weight for each grid cube corner */
double a_gw[DEF2MXDI]; /* Default space for gw */
float *gp; /* Pointer to grid cube base */
int rv = 0;
gw = a_gw;
if ((1 << di) > DEF2MXDI) {
if ((gw = (double *) malloc(sizeof(double) * (1 << di))) == NULL)
error("rspl malloc failed - interp_rspl_nl");
}
/* Figure out which grid cell the point falls into */
{
gp = s->g.a; /* Base of grid array */
for (e = 0; e < di; e++) {
int gres_1 = s->g.res[e]-1;
double pe, t;
int mi;
pe = p->p[e];
if (pe < s->g.l[e]) { /* Clip to grid */
pe = s->g.l[e];
rv = 1;
}
if (pe > s->g.h[e]) {
pe = s->g.h[e];
rv = 1;
}
t = (pe - s->g.l[e])/s->g.w[e];
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
else if (mi >= gres_1)
mi = gres_1-1;
gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
we[e] = t - (double)mi; /* 1.0 - weight */
}
}
/* Compute corner weights needed for interpolation */
{
int i, g;
gw[0] = 1.0;
for (e = 0, g = 1; e < di; g *= 2, e++) {
for (i = 0; i < g; i++) {
gw[g+i] = gw[i] * we[e];
gw[i] *= (1.0 - we[e]);
}
}
}
/* Now compute the output values */
{
int i;
double w = gw[0];
float *d = gp + s->g.fhi[0];
for (f = 0; f < fdi; f++) /* Base of cube */
p->v[f] = w * d[f];
for (i = 1; i < (1 << di); i++) { /* For all other corners of cube */
double w = gw[i]; /* Strength reduce */
float *d = gp + s->g.fhi[i];
for (f = 0; f < fdi; f++)
p->v[f] += w * d[f];
}
}
if (gw != a_gw)
free(gw);
return rv;
}
#endif /* USING_INTERP_NL */
/* ============================================ */
/* Non-mono calculations */
/* Compute non-monotonicity factor for each grid point, and */
/* return non-zero if the overall grid is monotonic. */
/* (Note that this is not a true non-monotonicity test. */
/* A true test has to deal with PCS combination values.) */
int
is_mono(
rspl *s
) {
int f;
int di = s->di;
int fdi = s->fdi;
int *fci = s->g.fci; /* Strength reduction */
float *gp, *ep;
double mcinc = MCINC/(s->g.mres-1); /* Scaled version of MCINC */
double min = 1e20; /* Minimum clearance found */
/* Find the minimum step between grid points */
for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
for (f = 0; f < fdi; f++) {
int e;
double e1,e2; /* Smallest/largest surrounting point */
double u; /* Current output value we are considering */
double ce; /* nm error */
/* Find smallest and largest surrounding points */
/* In +/- 1 dimension directions */
e1 = 1e20; e2 = -1e20;
for (e = 0; e < di; e++) {
int dof; /* Double offset */
float vv;
if ((G_FL(gp,e) & 3) < 1)
break; /* Skip to next grid point if on edge */
dof = fci[e];
vv = gp[f + dof];
if (vv < e1)
e1 = vv;
if (vv > e2)
e2 = vv;
vv = gp[f - dof];
if (vv < e1)
e1 = vv;
if (vv > e2)
e2 = vv;
}
if (e < di) /* We broke because we are on the edge */
continue;
u = gp[f];
e1 = u - e1;
e2 = e2 - u;
ce = (e1 < e2 ? e1 : e2); /* Smallest step */
if (ce < min) /* Current smallest step */
min = ce;
}
}
//if (min < mcinc) printf("~1 is_mono failed by %e < %e\n",min,mcinc);
return min < mcinc;
}
/* ============================================ */
/* Initialize the grid from a provided function. By default the grid */
/* values are set to exactly the value returned by func(), unless the */
/* RSPL_SET_APXLS flag is set, in which case an attempt is made to have */
/* the grid points represent a least squares aproximation to the underlying */
/* surface, by using extra samples in the middle of grid cells. */
/* RSPL_SET_APXLS tends to improve the fit to the underlying function. */
/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]), */
/* but if RSPL_SET_APXLS is set, the grid index will be the base of */
/* the cell the center point is sampled from every second sample. */
/* Return non-monotonic status */
static int set_rspl(
struct _rspl *s,/* this */
int flags, /* Combination of flags */
void *cbctx, /* Opaque function context */
void (*func)(void *cbctx, double *out, double *in), /* Function to set from */
datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
int gres[MXDI], /* 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 e, f, j;
rpsh counter; /* Pseudo-hilbert counter */
int gc[MXDI]; /* Grid index value */
float *gp; /* Pointer to grid data */
float *cc = NULL; /* Pointer to cell center data */
double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
double ov[MXDO];
if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
s->verbose = 1;
if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
s->verbose = 0;
/* transfer desired grid range to structure */
s->g.mres = 1.0;
s->g.bres = 0;
for (e = 0; e < s->di; e++) {
if (gres[e] < 2)
error("rspl: grid res must be >= 2!");
s->g.res[e] = gres[e]; /* record the desired resolution of the grid */
s->g.mres *= gres[e];
if (gres[e] > s->g.bres) {
s->g.bres = gres[e];
s->g.brix = e;
}
if (glow == NULL)
s->g.l[e] = 0.0;
else
s->g.l[e] = glow[e];
if (ghigh == NULL)
s->g.h[e] = 1.0;
else
s->g.h[e] = ghigh[e];
/* compute width of each grid cell */
s->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(s->g.res[e]-1);
/* ?? Should h be recomputed as (l + gres-1) * w ?? */
}
s->g.mres = pow(s->g.mres, 1.0/e); /* geometric mean */
/* record low and width data normalizing factors */
for (f = 0; f < s->fdi; f++) {
if (vlow == NULL)
s->d.vl[f] = 0.0;
else
s->d.vl[f] = vlow[f];
if (vhigh == NULL)
s->d.vw[f] = 1.0 - s->d.vl[f];
else
s->d.vw[f] = vhigh[f] - s->d.vl[f];
}
/* Allocate the grid data */
alloc_grid(s);
/* Allocate space for cell center value lookup */
if (flags & RSPL_SET_APXLS) {
if ((cc = (float *)malloc(sizeof(float) * s->g.no * s->fdi)) == NULL)
error("rspl malloc failed - center cell points");
}
/* Reset output min/max */
for (f = 0; f < s->fdi; f++) {
s->g.fmin[f] = 1e30;
s->g.fmax[f] = -1e30;
s->g.fminx[f] = -1;
s->g.fmaxx[f] = -1;
}
/* Set the grid points value from the provided function */
/* To make this clut function cache friendly, we use the pseudo-hilbert */
/* count sequence. This keeps each point close to the last in the */
/* multi-dimensional space. */
rpsh_init(&counter, s->di, (unsigned int *)gres, gc); /* Initialise counter */
for (;;) {
/* 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 += gc[e] * s->g.fci[e]; /* Grid value pointer */
iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
*((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
}
/* Apply incolor -> outcolor function we want to represent */
func(cbctx, ov, iv);
for (f = 0; f < s->fdi; f++) { /* Output chans */
gp[f] = (float)ov[f]; /* Set output value */
if (s->g.fmin[f] > gp[f]) {
s->g.fmin[f] = gp[f];
s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
}
if (s->g.fmax[f] < gp[f]) {
s->g.fmax[f] = gp[f];
s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
}
}
/* For RSPL_SET_APXLS, get the center of the cell values as well. */
if (cc != NULL) {
float *ccp;
ccp = cc;
for (e = 0; e < s->di; e++) { /* Input tables */
if (gc[e] >= (gres[e]-1))
break; /* No center for outer row */
iv[e] = s->g.l[e] + (gc[e] + 0.5) * s->g.w[e]; /* Input sample values */
ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
}
if (e >= s->di) { /* Not outer row */
/* Apply incolor -> outcolor function we want to represent */
func(cbctx, ov, iv);
for (f = 0; f < s->fdi; f++) { /* Output chans */
ccp[f] = (float)ov[f]; /* Set output value */
}
}
}
/* Increment counter */
if (rpsh_inc(&counter, gc))
break;
}
/* For RSPL_SET_APXLS, deal with cell center value, aproximate least squares adjustment */
if (cc != NULL) {
int ee;
double cw = 1.0/(double)(1 << s->di); /* Weight for each cube corner */
float *ccp;
for (e = 0; e < s->di; e++)
gc[e] = 0; /* init coords */
/* Compute linear interpolated error to actual cell center value */
for (ee = 0; ee < s->di;) {
gp = s->g.a; /* Base of grid data */
ccp = cc; /* Base of center data */
for (e = 0; e < s->di; e++) { /* Input tables */
gp += gc[e] * s->g.fci[e]; /* Grid value pointer */
ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
}
for (f = 0; f < s->fdi; f++) { /* Output chans */
double sum = 0.0;
for (j = 0; j < (1 << s->di); j++) /* For corners of cube */
sum += (gp + s->g.fhi[j])[f];
sum *= cw; /* Interpolated value */
ccp[f] -= sum; /* Correction to actual value */
/* Average half the error to cube corners */
ccp[f] *= 0.5 * cw; /* Distribution fraction */
}
/* Increment coord */
for (ee = 0; ee < s->di; ee++) {
if (++gc[ee] < (gres[ee]-1)) /* Don't go through upper edge */
break; /* No carry */
gc[ee] = 0;
}
}
for (e = 0; e < s->di; e++)
gc[e] = 0; /* init coords */
/* Distribute the center error to the cell corners */
for (ee = 0; ee < s->di;) {
gp = s->g.a; /* Base of grid data */
ccp = cc; /* Base of center data */
for (e = 0; e < s->di; e++) { /* Input tables */
gp += gc[e] * s->g.fci[e]; /* Grid value pointer */
ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
}
for (j = 0; j < (1 << s->di); j++) { /* For corners of cube */
double sc = 1.0; /* Scale factor for non-edge nodes */
/* Don't distribute error to edge nodes since there may */
/* an expectation that they have precicely set values */
/* (ie. white and black points) */
for (e = 0; e < s->di; e++) {
if ((gc[e] == 0 && (j & (1 << e)) == 0)
|| (gc[e] == ((gres[e]-2)) && (j & (1 << e)) != 0))
sc *= 0.0;
}
for (f = 0; f < s->fdi; f++) { /* Output chans */
double vv;
vv = (gp + s->g.fhi[j])[f]; /* Current value */
vv += sc * cc[f]; /* Correction */
(gp + s->g.fhi[j])[f] = vv;
if (s->g.fmin[f] > vv) {
s->g.fmin[f] = vv;
s->g.fminx[f] = (gp + s->g.fhi[j] - s->g.a)/s->g.pss;
}
if (s->g.fmax[f] < vv) {
s->g.fmax[f] = vv;
s->g.fmaxx[f] = (gp + s->g.fhi[j] - s->g.a)/s->g.pss;
}
}
}
/* Increment coord */
for (ee = 0; ee < s->di; ee++) {
if (++gc[ee] < (gres[ee]-1)) /* Don't go through upper edge */
break; /* No carry */
gc[ee] = 0;
}
}
free((void *)cc);
}
/* Compute overall output scale */
for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
double tt = s->g.fmax[f] - s->g.fmin[f];
s->g.fscale += tt * tt;
}
s->g.fscale = sqrt(s->g.fscale);
s->g.fminmax_valid = 1; /* Now is valid */
/* Return non-mono check */
return is_mono(s);
}
/* ============================================ */
/* Scan or change each grid point in the rspl. */
static int scan_set_rspl(
struct _rspl *s, /* this */
int flags, /* Combination of flags */
void *cbctx, /* Opaque function context */
void (*func)(void *cbntx, double *out, double *in), /* Function to get/set from */
int change /* Flag - nz means change values, 0 means scan values */
) {
int e, f;
rpsh counter; /* Pseudo-hilbert counter */
int gc[MXDI]; /* Grid index value */
float *gp; /* Pointer to grid data */
double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
double ov[MXDO];
if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
s->verbose = 1;
if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
s->verbose = 0;
if (change) {
/* Reset output min/max */
for (f = 0; f < s->fdi; f++) {
s->g.fmin[f] = 1e30;
s->g.fmax[f] = -1e30;
s->g.fminx[f] = -1;
s->g.fmaxx[f] = -1;
}
}
/* Set the grid points value from the provided function */
/* Give the function both the grid position and the existing output values */
/* To make this clut function cache friendly, we use the pseudo-hilbert */
/* count sequence. This keeps each point close to the last in the */
/* multi-dimensional space. */
rpsh_init(&counter, s->di, (unsigned int *)s->g.res, gc); /* Initialise counter */
for (;;) {
/* 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 */
*((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
}
for (f = 0; f < s->fdi; f++) /* Output chans */
ov[f] = gp[f];
/* Let function scan the input and output values, or */
/* Apply incolor -> outcolor, or oldoutcolor->outcolor function we want to represent */
func(cbctx, ov, iv);
if (change) { /* Put new output values back */
for (f = 0; f < s->fdi; f++) { /* Output chans */
gp[f] = (float)ov[f];
if (s->g.fmin[f] > gp[f]) {
s->g.fmin[f] = gp[f];
s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
}
if (s->g.fmax[f] < gp[f]) {
s->g.fmax[f] = gp[f];
s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
}
}
}
/* Increment counter */
if (rpsh_inc(&counter, gc))
break;
}
if (change == 0) {
return 0;
}
/* Compute overall output scale */
for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
double tt = s->g.fmax[f] - s->g.fmin[f];
s->g.fscale += tt * tt;
}
s->g.fscale = sqrt(s->g.fscale);
s->g.fminmax_valid = 1; /* Now is valid */
/* Invalidate various things */
free_data(s); /* Free any scattered data */
free_rev(s); /* Free any reverse lookup data */
/* Return non-mono check */
return is_mono(s);
}
/* Re-initialize the grid from existing grid values, and the provided function */
/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]) */
/* Return non-monotonic status. We assume that the ouput scale factors don't change. */
static int re_set_rspl(
struct _rspl *s, /* this */
int flags, /* Combination of flags */
void *cbctx, /* Opaque function context */
void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
) {
return scan_set_rspl(s, flags, cbctx, func, 1);
}
/* Scan the rspl grid point locations and values. Grid index values are */
/* supplied "under" in[] *((int*)&iv[-e-1]) */
static void scan_rspl(
struct _rspl *s, /* this */
int flags, /* Combination of flags */
void *cbctx, /* Opaque function context */
void (*func)(void *cbntx, double *out, double *in) /* Function to get from */
) {
scan_set_rspl(s, flags, cbctx, func, 0);
}
/* ============================================ */
/* Tune a single value for simplex interpolation. */
/* Return 0 on success, 1 if input clipping occured, 2 if output clipping occured */
static int tune_value (
struct _rspl *s, /* Pointer to Lut object */
co *p /* Target value */
) {
int e, di = s->di;
int f, fdi = s->fdi;
double we[MXDI]; /* Coordinate offset within the grid cell */
int si[MXDI]; /* we[] Sort index, [0] = smallest */
float *gp; /* Pointer to grid cube base */
int rv = 0; /* Register clip */
/* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
DEBLU(("In %s\n", icmPdv(di, p->p)));
/* Figure out which grid cell the point falls into */
{
gp = s->g.a; /* Base of grid array */
for (e = 0; e < di; e++) {
int gres_1 = s->g.res[e]-1;
double pe, t;
int mi;
pe = p->p[e];
if (pe < s->g.l[e]) { /* Clip to grid */
pe = s->g.l[e];
rv = 1;
}
if (pe > s->g.h[e]) {
pe = s->g.h[e];
rv = 1;
}
t = (pe - s->g.l[e])/s->g.w[e];
mi = (int)floor(t); /* Grid coordinate */
if (mi < 0) /* Limit to valid cube base index range */
mi = 0;
else if (mi >= gres_1)
mi = gres_1-1;
gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
we[e] = t - (double)mi; /* 1.0 - weight */
//if (rspldb && di == 3) printf("~1 e = %d, ix = %d, we = %f\n", e, mi, we[e]);
}
DEBLU(("ix %d, we %s\n", (int)(gp - s->g.a)/s->g.pss, icmPdv(di, p->p)));
}
/* Do selection sort on coordinates */
{
for (e = 0; e < di; e++)
si[e] = e; /* Initial unsorted indexes */
for (e = 0; e < (di-1); e++) {
double cosn;
cosn = we[si[e]]; /* Current smallest value */
for (f = e+1; f < di; f++) { /* Check against rest */
int tt;
tt = si[f];
if (cosn > we[tt]) {
si[f] = si[e]; /* Exchange */
si[e] = tt;
cosn = we[tt];
}
}
}
}
DEBLU(("si[] = %s\n", icmPiv(di, si)));
/* Now compute the weightings, simplex vertices and output values */
{
double w, ww = 0.0; /* Current vertex weight, sum of weights squared */
double cout[MXDO]; /* Current output value */
float *ogp = gp; /* Pointer to grid cube base */
w = 1.0 - we[si[di-1]]; /* Vertex at base of cell */
ww += w * w; /* Sum of weights squared */
for (f = 0; f < fdi; f++)
cout[f] = w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
for (e = di-1; e > 0; e--) { /* Middle vertices */
w = we[si[e]] - we[si[e-1]];
ww += w * w; /* Sum of weights squared */
gp += s->g.fci[si[e]]; /* Move to top of cell in next largest dimension */
for (f = 0; f < fdi; f++)
cout[f] += w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
}
w = we[si[0]];
ww += w * w; /* Sum of weights squared */
gp += s->g.fci[si[0]]; /* Far corner from base of cell */
for (f = 0; f < fdi; f++)
cout[f] += w * gp[f];
DEBLU(("ix %d: w %f * val %s\n", (int)(gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
DEBLU(("cout %s\n", icmPdv(fdi, cout)));
/* We distribute the correction needed in proportion to the */
/* interpolation weighting, so the biggest correction is to the */
/* closest vertex. */
for (f = 0; f < fdi; f++)
cout[f] = (p->v[f] - cout[f])/ww; /* Amount to distribute */
gp = ogp;
w = 1.0 - we[si[di-1]]; /* Vertex at base of cell */
for (f = 0; f < fdi; f++) {
gp[f] += w * cout[f]; /* Apply correction */
if (gp[f] < s->g.fmin[f]) {
gp[f] = s->g.fmin[f];
rv |= 2;
} else if (gp[f] > s->g.fmax[f]) {
gp[f] = s->g.fmax[f];
rv |= 2;
}
}
for (e = di-1; e > 0; e--) { /* Middle vertices */
w = we[si[e]] - we[si[e-1]];
gp += s->g.fci[si[e]]; /* Move to top of cell in next largest dimension */
for (f = 0; f < fdi; f++) {
gp[f] += w * cout[f]; /* Apply correction */
if (gp[f] < s->g.fmin[f]) {
gp[f] = s->g.fmin[f];
rv |= 2;
} else if (gp[f] > s->g.fmax[f]) {
gp[f] = s->g.fmax[f];
rv |= 2;
}
}
}
w = we[si[0]];
gp += s->g.fci[si[0]]; /* Far corner from base of cell */
for (f = 0; f < fdi; f++) {
gp[f] += w * cout[f]; /* Apply correction */
if (gp[f] < s->g.fmin[f]) {
gp[f] = s->g.fmin[f];
rv |= 2;
} else if (gp[f] > s->g.fmax[f]) {
gp[f] = s->g.fmax[f];
rv |= 2;
}
}
}
return rv;
}
/* ============================================ */
/* Allow the grid values to be filtered. */
/* For each grid value, provide the input value and */
/* pointers to all the output values in a 3^di grid around */
/* the output value. Pointers will be NULL if neigbour is outside */
/* the grid. cvi is the index of the output value. */
/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]) */
/* After all the grid values have been done, they will be updated */
/* with their new values. */
static void filter_rspl(
struct _rspl *s, /* this */
int flags, /* Combination of flags */
void *cbctx, /* Opaque function context */
void (*func)(void *cbntx, float **out, double *in, int cvi) /* Function to set from */
) {
int e, f;
ECOUNT(gc, MXDIDO, s->di, 0, s->g.res, 0); /* coordinates */
DCOUNT(cc, MXDIDO, s->di, -1, -1, 2); /* Surrounding cube counter */
float *gp, *ep; /* Pointer to grid data */
float *tarry, *tp; /* Temporary array of values */
double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
int cvi; /* Center value index = 3^di-1)/2 */
int pow3di = 1;
float **svals; /* Pointer to surrounding output values */
float *a_svals[DEF3MXDI];/* default allocation for svals */
if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
s->verbose = 1;
if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
s->verbose = 0;
/* Allocate svals array */
svals = a_svals;
for (e = 0; e < s->di; e++)
pow3di *= 3;
if (pow3di > DEF3MXDI) {
if ((svals = (float **) malloc(sizeof(float *) * pow3di)) == NULL)
error("rspl malloc failed - filter_rspl");
}
/* Compute the center value index */
for (cvi = 1, e = 0; e < s->di; e++)
cvi *= 3;
cvi = (cvi-1)/2;
/* Allocate a temporary array for the new output values */
if ((tarry = (float *)malloc(sizeof(float) * s->g.no * s->fdi)) == NULL) {
if (svals != a_svals)
free(svals);
error("rspl malloc failed - filter_rspl array");
}
/* Set the grid points value from the provided function */
/* Give the function both the grid position and the existing output values */
/* in the 3x3 surrounding grid */
EC_INIT(gc);
for (tp = tarry; !EC_DONE(gc); tp += s->fdi) {
int i;
/* Compute grid pointer and input sample values */
for (e = 0; e < s->di; e++) {
iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
*((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
}
/* Set pointers to 3x3 surrounders */
DC_INIT(cc)
for (i = 0; !DC_DONE(cc); i++ ) {
float *sp = s->g.a;
for (e = 0; e < s->di; e++) { /* Input tables */
int j;
j = gc[e] + cc[e];
if (j < 0 || j >= s->g.res[e]) {
sp = NULL; /* outside grid */
break;
}
sp += s->g.fci[e] * j; /* Compute pointer to surrounder */
}
svals[i] = sp;
DC_INC(cc);
}
for (f = 0; f < s->fdi; f++) /* Set default no change new values */
tp[f] = svals[cvi][f];
svals[cvi] = tp; /* Make sure output value goes into temp array */
/* Apply incolor -> outcolor, or oldoutcolor->outcolor function we want to represent */
func(cbctx, svals, iv, cvi);
EC_INC(gc);
}
/* Reset output min/max */
for (f = 0; f < s->fdi; f++) {
s->g.fmin[f] = 1e30;
s->g.fmax[f] = -1e30;
s->g.fminx[f] = -1;
s->g.fmaxx[f] = -1;
}
/* Now update all the values */
for (tp = tarry, gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss;
gp < ep; gp += s->g.pss, tp += s->fdi) {
for (f = 0; f < s->fdi; f++) /* Output chans */
gp[f] = tp[f];
for (f = 0; f < s->fdi; f++) { /* Output chans */
if (s->g.fmin[f] > gp[f]) {
s->g.fmin[f] = gp[f];
s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
}
if (s->g.fmax[f] < gp[f]) {
s->g.fmax[f] = gp[f];
s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
}
}
}
/* Compute overall output scale */
for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
double tt = s->g.fmax[f] - s->g.fmin[f];
s->g.fscale += tt * tt;
}
s->g.fscale = sqrt(s->g.fscale);
s->g.fminmax_valid = 1; /* Now is valid */
if (svals != a_svals)
free(svals);
free(tarry);
/* Invalidate various things */
free_data(s); /* Free any scattered data */
free_rev(s); /* Free any reverse lookup data */
}
/* =============================================== */
/* Utility function */
/* Pseudo - Hilbert count sequencer */
/* Initialise, returns total usable count */
unsigned rpsh_init(
rpsh *p, /* Pointer to structure to initialise */
int di, /* Dimensionality */
unsigned int *res, /* Size per coordinate */
int co[] /* Coordinates to initialise (May be NULL) */
) {
int e;
p->di = di;
p->tbits = 0;
for (e = 0; e < di; e++) {
p->res[e] = res[e];
/* Compute bits */
for (p->bits[e] = 0; (1u << p->bits[e]) < res[e]; p->bits[e]++)
;
p->tbits += p->bits[e];
}
/* Compute the total count mask */
p->tmask = ((((unsigned)1) << p->tbits)-1);
/* Compute usable count */
p->count = 1;
for (e = 0; e < di; e++)
p->count *= res[e];
/* Reset the counter */
p->ix = 0;
if (co != NULL) {
for (e = 0; e < di; e++)
co[e] = 0;
}
return p->count;
}
/* Reset the counter */
void rpsh_reset(
rpsh *p /* Pointer to structure */
) {
p->ix = 0;
}
/* Increment pseudo-hilbert coordinates */
/* Return non-zero if count rolls over to 0 */
int rpsh_inc(
rpsh *p, /* Pointer to structure */
int coa[] /* Coordinates to return */
) {
int di = p->di;
int e;
do {
unsigned int b, tb;
int gix; /* Gray code index */
p->ix = (p->ix + 1) & p->tmask;
gix = p->ix ^ (p->ix >> 1); /* Convert to gray code index */
for (e = 0; e < di; e++)
coa[e] = 0;
for (b = tb = 0; tb < p->tbits ; b++) { /* Distribute bits */
if (b & 1) {
for (e = di-1; e >= 0; e--) { /* In reverse coord order */
if (b < p->bits[e]) {
coa[e] |= (gix & 1) << b; /* ls bits of gix */
gix >>= 1;
tb++;
}
}
} else {
for (e = 0; e < di; e++) { /* In normal coord order */
if (b < p->bits[e]) {
coa[e] |= (gix & 1) << b; /* ls bits of gix */
gix >>= 1;
tb++;
}
}
}
}
/* Convert from Gray to binary coordinates */
for (e = 0; e < di; e++) {
unsigned sh, tv;
for(sh = 1, tv = coa[e];; sh <<= 1) {
unsigned ptv = tv;
tv ^= (tv >> sh);
if (ptv <= 1 || sh == 16)
break;
}
if (tv >= p->res[e]) /* Dumbo filter - increment again if outside cube range */
break;
coa[e] = tv;
}
} while (e < di);
return (p->ix == 0);
}
/* =============================================== */