home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Between Heaven & Hell 2
/
BetweenHeavenHell.cdr
/
300
/
249
/
spline.arc
/
SPLINE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-06-16
|
17KB
|
600 lines
/* spline - interpolate points in a tabulated function
Usage...
spline [file][options]
options are:
-a [step [start]] automatic abscissas
-b break interpolation after each label
-c general curve
-i file interpolate at x values given in file. Only
1st number on each line of file is used -
rest of line is ignored. Blank lines and
lines starting with ';' are ignored.
-n num interpolate over num intervals
-q increase intervals fourfold
-t num tension in interpolating curve
-x [min [max]] interpolate from min to max
-xl take logs of x values before interpolating
-yl take logs of y values before interpolating
-zl take logs of z values before interpolating
-3 3D function or curve
Notes...
This program fits a smooth curve through a given set of points,
using the splines under tension introduced by Schweikert [J.
Math. and Physics v 45 pp 312-317 (1966)]. They are similar
to cubic splines, but are less likely to introduce spurious
inflection points.
History...
15 Jun 87 echoes command line to output file.
-- ver 2.1 --
25 Nov 86 3D
-- ver 2.0 --
16 Oct 86 accepts desired abscissas from file.
-- ver 1.4 --
7 Jun 86 echoes a 2-point line.
3 Jun 86 -s switch allows specified slopes at ends.
2 Jun 86 Ver 1.3: Fixed several embarrassing bugs in -b option.
-q switch quadruples number of intervals.
6 Apr 86 -b switch breaks interpolation at labels.
21 Sep 85 Added -xl and -yl switches for taking logs
23 Nov 85 Passing lines starting with ';' unchanged, otherwise
ignoring them.
Bugs...
Should have -e command for evenly spaced abscissas.
-q switch should accept argument for desired fractional
increase in # points.
Should accept the output file as an optional second argument.
Author...
Copyright (c) 1985, 1986 James R. Van Zandt (jrv@mitre-bedford)
All rights reserved.
This program may be copied for personal, non-profit use only.
Based on algorithms by A. K. Cline [Communications of the ACM
v 17 n 4 pp 218-223 (Apr 74)].
*/
#include <stdio.h>
#include <math.h>
#define VERSION "2.1"
char *RIGHT= "Copyright (c) 1985, 1986 James R. Van Zandt";
#define DESMET /* this enables echoing of command line to output */
#define ENTRIES 200
#define MAXLABELS 50
#define BUFSIZE 200
double *x, *y, *z, *temp, *xp, *yp, *zp, *path;
double slp1=0.,slpn=0.,sigma=1.;
double abscissa=0.;
double abscissa_step=1.;
double slope1=0.,slopen=0.; /* slopes supplied by user */
double xmin=0.;
double xmax=0.;
double curv2(), mylog();
int abscissa_arguments=0;
int automatic_abscissas=0;
int breaking=0; /* nonzero if breaking at labels */
int curve=0; /* nonzero if general curve permitted */
int debugging=0;
int given_abscissas=0; /* nonzero if abscissas are given in a file */
int labels=0; /* number of labels in input data */
int quadruple=0; /* nonzero if user asking for 4 times the resolution */
int slopes=0; /* nonzero if slopes supplied by user */
int threed=0; /* nonzero if 3D curve (x, y, and z given for each point) */
int xlog=0; /* nonzero if taking log of x values */
int ylog=0; /* nonzero if taking log of y values */
int zlog=0; /* nonzero if taking log of z values */
int x_arguments=0;
int n; /* number of entries in x, y, yp, and temp */
int index_array[MAXLABELS]; /* indexes into x and y */
int *p_data=index_array;
int total=100;
FILE *file, *afile;
char buf[BUFSIZE];
char *label_array[MAXLABELS];
char **p_text=label_array;
char newline[]="\n", nothing[]="", *leadin;
main(argc,argv) int argc; char **argv;
{ int nn,origin;
read_data(argc,argv);
if(breaking && labels)
{origin=0;
while(labels--)
{p_data[0] -= origin;
nn=p_data[0]+1;
if(quadruple) total=nn*4-3;
if(nn) curv0(nn,total);
origin += nn;
path += nn;
x += nn;
y += nn;
p_data++;
p_text++;
}
}
else
{if(quadruple) total=n*4-3;
curv0(n,total);
}
}
curv0(n,requested)
int n,requested;
{ int i, j, each, stop, seg=0, regressing=0;
double s, ds, xx, yy, zz;
char *t;
if(n<3)
{for (j=0; j<n; j++)
{xx=x[n-1]; if(xlog) xx=exp(xx);
yy=y[n-1]; if(ylog) yy=exp(yy);
printf("%g %g ",xx,yy);
if(threed)
{zz=z[n-1]; if(zlog) zz=exp(zz);
printf("%g ",zz);
}
if(j==p_data[seg]) puts(p_text[seg++]);
putchar('\n');
}
return;
}
for(i=1; i<n; i++) if(path[i]<=path[i-1]) regressing++;
if (regressing)
{fprintf(stderr,"ERROR: independent variable not strictly increasing\n");
return;
}
if (curve && (xmin<0. || xmax>1.))
{fprintf(stderr,"ERROR: independent variable not in range 0 to 1\n");
return;
}
if(curve)
{curv1(path,x,xp,n); curv1(path,y,yp,n);
if(threed) curv1(path,z,zp,n);
}
else
{slp1=slope1; slpn=slopen; curv1(x,y,yp,n);
if(threed)
{slp1=slpn=0.; curv1(x,z,zp,n);
}
}
s=path[0];
seg=0;
if(requested<n) requested=n;
if(given_abscissas) /* abscissas specified in a file */
{leadin=nothing;
while(fgets(buf,BUFSIZE,afile))
{if(buf[0]==';') continue; /* skip comment */
t=buf;
while(*t && isspace(*t)) t++;
if(*t == 0) continue; /* skip blank lines */
buf[strlen(buf)-1]=0; /* zero out the line feed */
sscanf(buf,"%F",&s);
if(xlog) s=mylog(s);
if(s<path[0] || path[n-1]<s) continue; /* ignore outlying points */
value(s, &xx, &yy, &zz, n);
if(threed) printf("%s%g %g %g", leadin, xx, yy, zz);
else printf("%s%g %g", leadin, xx, yy);
leadin=newline;
}
if(breaking && labels) printf(" %s\n",p_text[0]);
else printf(" \"\"\n");
rewind(afile);
}
else if(x_arguments==2) /* specific range was requested */
{ds=(xmax-xmin)/requested;
if(curve)
{ds*=(path[n-1]-path[0]);
s=xmin*(path[n-1]-path[0])+path[0];
}
else s=xmin;
for(i=requested+1; i; i--)
{value(s,&xx,&yy,&zz,n);
if(threed) printf("%g %g %g ", xx, yy, zz);
else printf("%g %g ", xx, yy);
if(j==p_data[seg]) puts(p_text[seg++]);
putchar('\n');
s+=ds;
}
}
else /* spline for entire curve was requested */
{for (i=j=0; j<n-1; j++)
{stop=requested*(j+1)/(n-1);
ds=(path[j+1]-path[j])/(stop-i);
for ( ; i<stop; i++)
{value(s,&xx,&yy,&zz,n);
if(threed) printf("%g %g %g ", xx, yy, zz);
else printf("%g %g ",xx,yy);
if(j==p_data[seg]) puts(p_text[seg++]);
putchar('\n');
s+=ds;
}
}
xx=x[n-1]; if(xlog) xx=exp(xx);
yy=y[n-1]; if(ylog) yy=exp(yy);
if(threed)
{zz=z[n-1]; if(zlog) zz=exp(zz);
printf("%g %g %g ", xx, yy, zz);
}
else printf("%g %g ",xx,yy);
if(j==p_data[seg]) puts(p_text[seg++]);
putchar('\n');
}
}
value(s,q,r,v,n) double s,*q,*r,*v; int n;
{ if(curve)
{*q=curv2(path,x,xp,s,n);
*r=curv2(path,y,yp,s,n);
if(threed) *v=curv2(path,z,zp,s,n);
}
else
{*q=s;
*r=curv2(x,y,yp,s,n);
if(threed) *v=curv2(x,z,zp,s,n);
}
if(xlog) *q=exp(*q);
if(ylog) *r=exp(*r);
if(zlog) *v=exp(*v);
}
read_data(argc,argv) int argc; char **argv;
{ int i, j, length, skipping;
double xx, yy, zz, ss, d, *pd, sum;
char *s,*t;
#ifdef DESMET
fprint_cmd(stdout, "; spline %s\n");
#endif
x=path=malloc(ENTRIES*sizeof(double));
y=malloc(ENTRIES*sizeof(double));
if(x==0 || y==0) {fprintf(stderr,"can\'t allocate buffer"); exit();}
if(argc>1 && streq(argv[1],"?")) help();
if(argc<=1 || *argv[1]=='-') file=stdin;
else
{if(argc>1)
{file=fopen(argv[1],"r");
if(file==0) {printf("file %s not found\n",argv[1]); exit();}
argc--; argv++;
}
else help();
}
argc--; argv++;
while(argc>0)
{i=get_parameter(argc,argv);
argv=argv+i; argc=argc-i;
}
if(threed)
{z=malloc(ENTRIES*sizeof(double));
if(z==0) {fprintf(stderr,"can\'t allocate buffer"); exit();}
}
if(sigma<=0.)
{fprintf(stderr,"ERROR: tension must be positive\n");
exit(1);
}
if(slopes && curve)
{fprintf(stderr,"ERROR: slopes can\'t be specified for general curve\n");
exit(1);
}
sigma *= -1.;
if(xlog && !curve)
{if(x_arguments>1) xmax=mylog(xmax);
if(x_arguments>=1) xmin=mylog(xmin);
}
if(automatic_abscissas && abscissa_arguments<2 && x_arguments>=1)
abscissa=xmin;
p_data[0]=-1;
skipping=2;
if(automatic_abscissas) skipping--;
if(threed) skipping++;
i=0;
while(i<ENTRIES)
{if(fgets(buf,BUFSIZE,file)==0)break;
t=buf;
while(*t && isspace(*t)) t++;
if(*t == 0) continue; /* skip blank lines */
buf[strlen(buf)-1]=0; /* zero out the line feed */
if(buf[0]==';') {printf("%s\n",buf); continue;} /* skip comment */
if(automatic_abscissas)
{x[i]=abscissa;
abscissa+=abscissa_step;
if(threed) sscanf(buf,"%F %F",&y[i],&z[i]);
else sscanf(buf,"%F",&y[i]);
if(ylog) y[i]=mylog(y[i]);
if(zlog) z[i]=mylog(z[i]);
}
else
{if(threed) sscanf(buf,"%F %F %F",&x[i],&y[i],&z[i]);
else sscanf(buf,"%F %F",&x[i],&y[i]);
if(xlog) x[i]=mylog(x[i]);
if(ylog) y[i]=mylog(y[i]);
if(zlog) z[i]=mylog(z[i]);
}
s=buf; /* start looking for label */
for (j=skipping; j--; )
{while(*s==' ')s++; /* skip a number */
while(*s && (*s!=' '))s++;
}
while(*s==' ')s++;
if((length=strlen(s))&&(labels<MAXLABELS))
{if(*s=='\"') /* label in quotes */
{t=s+1;
while(*t && (*t!='\"')) t++;
t++;
}
else /* label without quotes */
{t=s;
while(*t && (*t!=' '))t++;
}
*t=0;
length=t-s;
p_data[labels]=i;
p_text[labels]=malloc(length+1);
if(p_text[labels]) strcpy(p_text[labels++],s);
}
i++;
}
n=i;
if(breaking && (!labels || p_data[labels-1]!=n-1))
{p_data[labels]=i-1;
if(p_text[labels]=malloc(1)) *p_text[labels++]=0;
}
yp=malloc(n*sizeof(double));
temp=malloc(n*sizeof(double));
if(temp==0 || yp==0)
{fprintf(stderr,"can\'t allocate buffer"); exit();
}
if(threed)
{zp=malloc(n*sizeof(double));
if(zp==0) {fprintf(stderr,"can\'t allocate buffer"); exit();}
}
if(curve)
{xp=malloc(n*sizeof(double));
path=malloc(n*sizeof(double));
if(xp==0|| path==0) {fprintf(stderr,"can\'t allocate buffer"); exit();}
path[0]=sum=0.;
if(threed)
for (i=1; i<n; i++)
{xx=x[i]-x[i-1];
yy=y[i]-y[i-1];
zz=z[i]-z[i-1];
path[i]=(sum+=sqrt(xx*xx + yy*yy + zz*zz));
}
else
for (i=1; i<n; i++)
{xx=x[i]-x[i-1];
yy=y[i]-y[i-1];
path[i]=(sum+=sqrt(xx*xx + yy*yy));
}
/* for(i=0; i<n; i++)
printf("path[%d]=%g x[%d]=%g \n",i,path[i],i,x[i]); */
}
}
/* get_parameter - process one command line option
(return # parameters used) */
get_parameter(argc,argv) int argc; char **argv;
{ int i;
if(streq(*argv,"-a"))
{i=get_double(argc,argv,2,&abscissa_step,&abscissa,&abscissa);
abscissa_arguments=i-1;
automatic_abscissas=1;
return i;
}
else if(streq(*argv,"-b")) {breaking=1; return 1;}
else if(streq(*argv,"-c")) {curve=1; return 1;}
else if(streq(*argv,"-d")) {debugging=1; return 1;}
else if(streq(*argv,"-i"))
{if(argc>1)
{afile=fopen(argv[1],"r");
if(afile==0)
{fwrite(stderr,"can\'t open abscissa file %s",argv[1]);
exit(1);
}
given_abscissas=1;
}
return 2;
}
else if(streq(*argv,"-n"))
{if((argc>1) && numeric(argv[1])) total=atoi(argv[1]);
return 2;
}
else if(streq(*argv,"-q")) {quadruple=1; return 1;}
else if(streq(*argv,"-t"))
{return(get_double(argc,argv,1,&sigma,&abscissa,&abscissa));
}
else if(streq(*argv,"-s"))
{slopes++;
return(get_double(argc,argv,2,&slope1,&slopen,&slopen));
}
else if(streq(*argv,"-x"))
{i=get_double(argc,argv,2,&xmin,&xmax,&xmax);
x_arguments=i-1;
return i;
}
else if(streq(*argv,"-xl")) {xlog++; return 1;}
else if(streq(*argv,"-yl")) {ylog++; return 1;}
else if(streq(*argv,"-zl")) {zlog++; threed++; return 1;}
else if(streq(*argv,"-3")) {threed++; return 1;}
else gripe(argv);
}
get_double(argc,argv,permitted,a,b,c)
int argc,permitted; char **argv; double *a,*b,*c;
{ int i=1;
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *a=atof(argv[i++]);
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *b=atof(argv[i++]);
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *c=atof(argv[i++]);
return i;
}
int streq(a,b) char *a,*b;
{ while(*a)
{if(*a!=*b)return 0;
a++; b++;
}
return 1;
}
gripe_arg(s) char *s;
{ fprintf(stderr,"argument missing for switch %s",s);
help();
}
gripe(argv) char **argv;
{ fprintf(stderr,*argv); fprintf(stderr," isn\'t a legal argument \n\n");
help();
}
help()
{ fprintf(stderr,"spline version %s",VERSION);
fprintf(stderr,"\nusage: spline [file][options]\n");
fprintf(stderr,"options are:\n");
fprintf(stderr," -a [step [start]] automatic abscissas \n");
fprintf(stderr," -b break interpolation after each label \n");
fprintf(stderr," -c general curve \n");
fprintf(stderr," -i file interpolate at abscissas from the file\n");
fprintf(stderr," -n num interpolate over num intervals \n");
fprintf(stderr," -q increase intervals fourfold \n");
fprintf(stderr," -s [num [num]] specify slopes at beginning and end \n");
fprintf(stderr," -t num tension in interpolating curve \n");
fprintf(stderr," -x [min [max]] interpolate from min to max \n");
fprintf(stderr," -xl take logs of x values before interpolating \n");
fprintf(stderr," -yl take logs of y values before interpolating \n");
fprintf(stderr," -zl take logs of z values before interpolating \n");
fprintf(stderr," (implies -3)\n");
fprintf(stderr," -3 3D curve: x, y, and z given for each points\n");
exit();
}
numeric(s) char *s;
{ char c;
while(c=*s++)
{if((c<='9' && c>='0') || c=='+' || c=='-' || c=='.') continue;
return 0;
}
return 1;
}
curv1(x,y,yp,n) double *x,*y,*yp; int n;
{ int i;
double c1,c2,c3,deln,delnm1,delnn,dels,delx1,delx2,delx12;
double diag1,diag2,diagin,dx1,dx2,exps;
double sigmap,sinhs,sinhin,slpp1,slppn,spdiag;
delx1=x[1] - x[0];
dx1=(y[1] - y[0])/delx1;
if(slopes) {slpp1=slp1; slppn=slpn;}
else
{if(n!=2)
{delx2= x[2] - x[1];
delx12= x[2] - x[0];
c1= -(delx12 + delx1)/delx12/delx1;
c2= delx12/delx1/delx2;
c3= -delx1/delx12/delx2;
slpp1= c1*y[0] + c2*y[1] + c3*y[2];
deln= x[n-1] - x[n-2];
delnm1= x[n-2] - x[n-3];
delnn= x[n-1] - x[n-3];
c1= (delnn + deln)/delnn/deln;
c2= -delnn/deln/delnm1;
c3= deln/delnn/delnm1;
slppn= c3*y[n-3] + c2*y[n-2] + c1*y[n-1];
}
else yp[0]=yp[1]=0.;
}
/* denormalize tension factor */
sigmap=fabs(sigma)*(n-1)/(x[n-1]-x[0]);
/* set up right hand side and tridiagonal system for
yp and perform forward elimination */
dels=sigmap*delx1;
exps=exp(dels);
sinhs=.5*(exps-1./exps);
sinhin=1./(delx1*sinhs);
diag1=sinhin*(dels*.5*(exps+1./exps)-sinhs);
diagin=1./diag1;
yp[0]=diagin*(dx1-slpp1);
spdiag=sinhin*(sinhs-dels);
temp[0]=diagin*spdiag;
if(n!=2)
{for(i=1; i<=n-2; i++)
{delx2=x[i+1]-x[i];
dx2=(y[i+1]-y[i])/delx2;
dels=sigmap*delx2;
exps=exp(dels);
sinhs=.5*(exps-1./exps);
sinhin=1./(delx2*sinhs);
diag2=sinhin*(dels*(.5*(exps+1./exps))-sinhs);
diagin=1./(diag1+diag2-spdiag*temp[i-1]);
yp[i]=diagin*(dx2-dx1-spdiag*yp[i-1]);
spdiag=sinhin*(sinhs-dels);
temp[i]=diagin*spdiag;
dx1=dx2;
diag1=diag2;
}
}
diagin=1./(diag1-spdiag*temp[n-2]);
yp[n-1]=diagin*(slppn-dx2-spdiag*yp[n-2]);
/* perform back substitution */
for (i=n-2; i>=0; i--) yp[i] -= temp[i]*yp[i+1];
}
double curv2(x,y,yp,t,n) double *x,*y,*yp,t; int n;
{ int i,j;
static int i1=1;
double del1,del2,dels,exps,exps1,s,sigmap,sinhd1,sinhd2,sinhs;
s=x[n-1]-x[0];
sigmap=fabs(sigma)*(n-1)/s;
i=i1;
if(i>n) i=1; /* want: x[i-1] <= t < x[i], 0 < i <= n */
while(i<n && t>=x[i]) i++;
while(i>1 && x[i-1]>t) i--;
i1=i;
del1=t-x[i-1];
del2=x[i]-t;
dels=x[i] - x[i-1];
exps1=exp(sigmap*del1); sinhd1=.5*(exps1-1./exps1);
exps= exp(sigmap*del2); sinhd2=.5*(exps-1./exps);
exps=exps1*exps; sinhs=.5*(exps-1./exps);
return ((yp[i]*sinhd1 + yp[i-1]*sinhd2)/sinhs +
((y[i] - yp[i])*del1 + (y[i-1] - yp[i-1])*del2)/dels);
}
double mylog(x) double x;
{ if(x>0.) return log(x);
fprintf(stderr,"can%'t take log of nonpositive number");
exit(1);
return 0.;
}
#ifdef DESMET
/* write out the command line */
fprint_cmd(fp, format)
FILE *fp; /* pointer to output file */
char *format; /* desired print format, possibly including program name */
{ char buf[129];
int i=0;
_lmove(129, 130, _showcs()-0x10, &buf, _showds());
buf[128]=13;
while(buf[i]!=13) i++;
buf[i]=0;
fprintf(fp, format, buf);
}
#endif