/*
	3d Surface plotting with hidden line removal
*/
#include "general.h"
#include "gsurface.h"
#include "vdevice.h"
#if (defined DJ || defined EMXOS2)              /* a.r. */
	#define unix
	extern int d_move_really(float fx, float fy);
#endif

#ifdef unix
	#define huge
#endif
#ifdef VAXC
	#define huge
#endif

#define TESTING true

int initminmax(void);
int init_user(void);
int nice_ticks(float *dticks, float *gmin,float *gmax , float *t1,float *tn);
int grid_back(int nx, int ny, float z1, float z2);
int skirt(float huge *z,int ix1, int iy1, float minz);
int find_splits(int nx, int ny, int *splitx, int *splity);
int fxy_polar(float dx,float dy,float *radius,float *angle);
int fpolar_xy(float r, float angle, float *dx, float *dy);
int draw_zaxis(struct axis_struct *ax, int nx, int ny, float minz, float maxz);
int draw_axis(struct axis_struct *ax, int nx, int ny, float minz, float maxz);
int hide(float huge *z,int nx, int ny, float minz, float maxz, struct surface_struct *sff);
int touser3(float x, float y, float z, float *uux, float *uuy, float *uuz);
int touser(float x, float y, float z, float *uux, float *uuy);
int matmove(float i[4][4],float x, float y, float z);
int seth2(int rx1, int ry1, float rz1, int rx2, int ry2, float rz2);
int matscale(float i[4][4],float x, float y, float z);
int matun(float m[4][4]);
int matmul(float a[4][4],float b[4][4]);
int matrx(float i[4][4], float angle);
int matry(float i[4][4], float angle);
int matrz(float i[4][4], float angle);
int vector(int x1, float y1, int x2, float y2);
int draw_riselines(int nx, int ny,float minz, float maxz);
int move3d(float x, float y, float z);
int line3d(float x, float y, float z);
int cube(float x, float y, float z1, float z2);
int horizon(float huge *z,int ix1,int iy1,int ix2,int iy2);
int horizon2(float huge *z,int ix1,int iy1,int ix2,int iy2);
int horizonv(float huge *z,int ix1,int iy1,int ix2,int iy2);
int horizonv2(float huge *z,int ix1,int iy1,int ix2,int iy2);
int hclipvec(int x1, float y1, int x2, float y2, int sethi);
int hclipvec2(int x1, float y1, int x2, float y2, int sethi);
int clipline(float x1, float y1, float z1, float x2, float y2, float z2);
int draw_markers(int nx, int ny);
int matshow(char *s, float m[4][4]);
int show_horizon();
float base;
extern int ngerror;
extern int batch_only;
FILE *fp;
float image[4][4];
static double min_zed;
init_user()
{
	int i,j;
	matun(image);
}
float smin_x,smax_x,smin_y,smax_y,smin_z,smax_z;
initminmax()
{
	smin_x = 10e10; smax_x = -10e10;
	smin_y = 10e10; smax_y = -10e10;
	smin_z = 10e10; smax_z = -10e10;
}
int setaminmax(float x, float *x1, float *x2);
setaminmax(float x, float *x1, float *x2) 
{
	if (x < *x1) *x1 = x;
	if (x > *x2) *x2 = x;
}
int setminmax(float x,float y,float z);
setminmax(float x,float y,float z)
{
	setaminmax(x,&smin_x,&smax_x);
	setaminmax(y,&smin_y,&smax_y);
	setaminmax(z,&smin_z,&smax_z);
}
#define RERR (0.0001)         /* Rounding error */
int MAXH;
#define deg(d) ((d)*3.14159254/180)
float *h,*h2;         /* h2 is the underneath */
float eye_x,eye_y,eye_depth;
float vdist=0; /* 1=eye touching front of picture, infinity=  cube back */
float map_mul,map_sub;
float maxdepth;
float xmargin = 2;
float ymargin = 1.5;
int doy=true; 
int dox=true;  /* false */
int nnx;
int vsign=1;
#define returng {v_close(); return;}
static struct surface_struct sf;
hide(float huge *z,int nx, int ny, float minz, float maxz, struct surface_struct *sff)
{
	int i,j,x,y,d;
	float r,angle;
	float width,height;
	float ux,uy,y1,y2,step,v,uz,scalex,scaley,ux3,uy3,ux2,uy2;
	static char dummy[90];
	int lasta, isleft, splitx, splity;
	int x1,x2;        
	sf = *sff; /* Make a local copy of all the paramters */
	init_user();
	eye_x = sf.eye_x;
	eye_y = sf.eye_y;
	vdist = sf.vdist;
	base = sf.screenx;
	xmargin = sf.screenx*2/10.0;
	ymargin = sf.screeny*1.5/10.0;
	width = sf.screenx-.5;
	height = sf.screeny-.5;
	if (sf.title!=NULL) height = height -.7;
	dox = sf.xlines_on;
	doy = sf.ylines_on;
	MAXH = sf.maxh;

	if (MAXH==0) MAXH = 1000;
	h = malloc(MAXH*sizeof(float));
	h2 = malloc(MAXH*sizeof(float));
	if (h==NULL || h2==NULL) {
		gprint("Was not able to allocate horizon arrays %d \n",MAXH);
		return;
	}
	if (sf.zaxis.min != sf.zaxis.max) {
		minz = sf.zaxis.min;
		maxz = sf.zaxis.max;
	}
	min_zed  = minz;
	if (ngerror>0) {
		gprint("Press return to continue\n");
		if (batch_only)  gets(dummy); else text_inkey();
		ngerror = 0;
	}
	v_open(sf.screenx,sf.screeny);
	nnx = nx;        /* save dimensions for use in subroutine */
	vsign = 1; /* doing top half */
	for (i=0; i<MAXH ;i++) h[i] = 0.0;  /* zero horizon */
	maxdepth = 0;
	
	/* make it a 10x10x10 cube */
	matmove(image,0.0,0.0,-minz); /* so 0,0,minz = 0,0,0 */
	if (sf.sizez==0) sf.sizez = (sf.sizex+sf.sizey)/2.0;
	matscale(image,sf.sizex/(float) nx,sf.sizey/(float) ny,sf.sizez/(maxz-minz));
			/* positive z comes towards the viewer */

	/* 60 50 20 , 80 0 0 */
	matrx(image,deg(sf.xrotate)); /*  clockwise holding right hand side */
	matry(image,deg(sf.yrotate)); /* clockwise holding top */
	matrz(image,deg(sf.zrotate)); /* clockwise holding front */

	/* now rotate cube so line from 0,0,0 --> 0,0,1 is virtical on screen */
	touser(0.0,0.0,0.0,&ux,&uy);
	touser(0.0,0.0,1.0,&ux3,&uy3);
	fxy_polar(ux3-ux,uy3-uy,&r,&angle);
	matrz(image,deg(-90.0+angle)); /* clockwise holding front */
		
	/* Normalize image, shift to touch x=0, y=0, z=0 */
	initminmax();
	for (x=0; x<nx; x+=nx-1) {
	 for (y=0; y<ny; y+=ny-1) {
		touser3(x,y,minz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
		touser3(x,y,maxz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
	 }
	}

	scalex = 1;
	scaley = 1;
	if (smax_x>width) scalex = width/(smax_x-smin_x);
	if (smax_y>height) scaley = height/(smax_y-smin_y);
	if (scalex<scaley) scaley = scalex;

	image[0][3] = image[0][3] - smin_x + xmargin/scaley;
	image[1][3] = image[1][3] - smin_y + ymargin/scaley;
	image[2][3] = image[2][3] - smax_z;
	
		initminmax();
	for (x=0; x<nx; x+=nx-1) {
	 for (y=0; y<ny; y+=ny-1) {
		touser3(x,y,minz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
		touser3(x,y,maxz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
	 }
	}
	
	scalex = 1;
	scaley = 1;
	if (smax_x>width) scalex = width/smax_x;
	if (smax_y>height) scaley = height/smax_y;
	if (scalex<scaley) scaley = scalex;
	matscale(image,scaley,scaley,scaley);        

	initminmax();
	for (x=0; x<nx; x+=nx-1) {
	 for (y=0; y<ny; y+=ny-1) {
		touser3(x,y,minz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
		touser3(x,y,maxz,&ux,&uy,&uz);
		setminmax(ux,uy,uz);
	 }
	}

	
	/* Deepest point will be at   z==-(smax_z-smin_z) */
	maxdepth = smin_z;
	
	v_move(eye_x,eye_y);
	v_set_hei(.3);

	/* decide on mapping of ux onto h[] -------------------------- */
#define maph(ux) (((ux)-map_sub)*map_mul)
#define unmaph(ux) (((ux)/map_mul)+map_sub)
	
	x1 = 10000;
	x2 = -10000;
	for (x=0; x<nx; x+=nx-1) {
	 for (y=0; y<ny; y+=ny-1) {
		 touser(x,y,minz,&ux,&uy);
		if (ux<x1) x1 = ux;
		if (ux>x2) x2 = ux;
		 touser(x,y,maxz,&ux,&uy);
		if (ux<x1) x1 = ux;
		if (ux>x2) x2 = ux;
	 }
	}
	/* map x1 -- x2  onto 2 -- 998 */
	x1--; x2++;
	map_sub = x1;
	map_mul = (MAXH-100)/((double) x2-x1);
	/* ----------------------------------------------------  */

	/* if the closest line of constant X is not one of the edges
	   then split object at EYE(X) 
	*/

	find_splits(nx,ny,&splitx,&splity);

	/* if ux of nx,0,0 is less than ux of 0,ny,0 then */
	/* eye is closer to XAXIS then*/ 
	/*   (lines of constant x will be horizontal */
	/*   process lines of constant X in usual order, front to back*/
	/*   After each x line, draw lines of constant y between lastx and nextx */
	/* else, do y first and x betweens.*/
	
	touser(nx,0,0,&ux,&uy);
	touser(0,ny,0,&ux2,&uy);


	/* Now draw bottom half */
	v_color(sf.bot_color);
	v_lstyle(sf.bot_lstyle);
	vsign = -1;        /* reverse tests for bottom half */
	for (i=0; i<MAXH ;i++) h2[i] = 9999.0;  /* zero horizon */

	if (sf.bot_on && !sf.skirt_on) {
	touser(nx,0,0,&ux,&uy);
	touser(0,ny,0,&ux2,&uy);
	if (ux < ux2) {
	 for (x=nx-1; x>=0; x--) {
	  if (abortkey()) returng;
	  if (dox) for (y=splity; y>0; y--) horizonv2(z,x,y,x,y-1); /* x */
	  if (dox) for (y=splity+1; y<ny; y++) if (y>0) horizonv2(z,x,y-1,x,y);
	  for (y=splity; y>=0; y--) if (x>0) if (doy) horizonv2(z,x,y,x-1,y);
	  for (y=splity+1; y<ny; y++) if (x>0) if (doy) horizonv2(z,x,y,x-1,y);
	 }                
	} else {
	 for (y=0; y<ny; y++) {
	  if (abortkey()) returng;
	  if (doy) for (x=splitx; x>0; x--) horizonv2(z,x,y,x-1,y); /* y */
	  if (doy) for (x=splitx+1; x<nx; x++) if (x>0) horizonv2(z,x-1,y,x,y);
	  for (x=splitx; x>=0; x--) if (y<ny-1) if (dox) horizonv2(z,x,y,x,y+1);
	  for (x=splitx+1; x<nx; x++) if (y<ny-1) if (dox) horizonv2(z,x,y,x,y+1);
	 }                
	}
	} /* if bot_on a&& !skirt_on */


/* should clip object nicely at zmin,zmax,  do this inside vector */
	vsign = 1;        /* normal tests for top half */
	v_color(sf.top_color);
	v_lstyle(sf.top_lstyle);
	if (sf.top_on) {
	if (ux < ux2) {
	 /* Go from x = splitx to zero, then splitx+1 to nx-1 */
	 for (x=nx-1; x>=0; x--) {
	  if (abortkey()) returng;
	  if (dox) for (y=splity; y>0; y--) horizonv(z,x,y,x,y-1); /* X */
	  if (dox) for (y=splity+1; y<ny; y++) if (y>0) horizonv(z,x,y-1,x,y); /* Constant X  (this does join, should be earlier )(*/
	  for (y=splity; y>=0; y--) if (x>0) if (doy) horizonv(z,x,y,x-1,y); /* Constant Y */
	  for (y=splity+1; y<ny; y++) if (x>0) if (doy) horizonv(z,x,y,x-1,y); /* Constant Y */
	 }                
	} else {
	 /* Go from y = splity to zero, then splity+1 to ny-1 */
	 for (y=0; y<ny; y++) { 
	  if (abortkey()) returng;
	  if (doy) for (x=splitx; x>0; x--) horizonv(z,x,y,x-1,y); /* Constant y */
	  if (doy) for (x=splitx+1; x<nx; x++) if (x>0) horizonv(z,x-1,y,x,y); /* Constant y  (this does join, should be first )(*/
	  for (x=splitx; x>=0; x--) if (y<ny-1) if (dox) horizonv(z,x,y,x,y+1); /* Constant x */
	  for (x=splitx+1; x<nx; x++) if (y<ny-1) if (dox) horizonv(z,x,y,x,y+1); /* Constant x */
	 }                
	}
	} /* sf.top_on */


	v_color(sf.top_color);
	v_lstyle(sf.top_lstyle);
	if (sf.skirt_on) { /* set h2 to bottom of top surface */
		for (x=nx-1; x>0; x--) seth2(x,0,z[x],x-1,0,z[x-1]);
		for (y=0; y<ny-1; y++) seth2(nx-1,y,z[nx-1+y* (int32) nx],nx-1,y+1,z[nx-1+(y+1)* (int32) nx]);
	}
/*        show_horizon(); */
	if (sf.skirt_on) {
	  for (y=splity; y>=0; y--) skirt(z,nx-1,y,minz);
	  for (y=splity+1; y<ny; y++) skirt(z,nx-1,y,minz);
	  for (x=splitx; x>=0; x--) skirt(z,x,0,minz);
	  for (x=splitx+1; x<nx; x++) skirt(z,x,0,minz);
	}
	  if (abortkey()) returng;
	if (sf.skirt_on) { /* set h2 to bottom edge of box */
		for (x=nx-1; x>=0; x--) seth2(x,0,minz,x-1,0,minz);
		for (y= -1; y<ny; y++) seth2(nx-1,y,minz,nx-1,y+1,minz);
	}
	/* now define h2[] if bot wasn't drawn */
	if (!sf.bot_on && !sf.skirt_on) {
		for (x=nx-1; x>0; x--) seth2(x,0,z[x],x-1,0,z[x-1]);
		for (y=0; y<ny-1; y++) seth2(nx-1,y,z[nx-1+y* (int32) nx],nx-1,y+1,z[nx-1+(y+1)* (int32) nx]);
	}

	/* Draw the unit cube for reference   -------------------------- */
	if (sf.cube_on) {
		cube(nx-1,ny-1,minz,maxz);
	}

	draw_maintitle();

/* now lets try and draw the axes */
	draw_axis(&sf.xaxis,nx,ny,minz,maxz);
	draw_axis(&sf.yaxis,nx,ny,minz,maxz);
	draw_zaxis(&sf.zaxis,nx,ny,minz,maxz);

/* The back, right and bottom grid lines */
	grid_back(nx,ny,minz,maxz);

	draw_markers(nx,ny);
	draw_riselines(nx,ny,minz,maxz);

	free(h); free(h2);
	v_close();
}
#define DVAL(a,b) if ((a)==0) a = (b)
draw_maintitle()
{
	v_set_just("BC");
	if (sf.title==NULL) return;
	v_color(sf.title_color);
	DVAL(sf.title_hei,base/30.0);
	v_set_hei(sf.title_hei);
	v_move(sf.screenx/2.0,sf.screeny-sf.title_hei+sf.title_dist);
	v_text(sf.title);
}
draw_axis(struct axis_struct *ax, int nx, int ny, float minz, float maxz)
{
	float ux,uy,ux2,uy2,ux3,uy3,r,a,ta,r2,x,xx,t1,tn;
	char buff[80];
	if (ax->type>1) return;
	if (!ax->on) return;
	if (ax->type==0) {
		touser(0,0,minz,&ux,&uy);
		touser(nx-1,0,minz,&ux2,&uy2);
	} else {
		touser(nx-1,0,minz,&ux,&uy);
		touser(nx-1,ny-1,minz,&ux2,&uy2);
	}
	v_color(ax->color);
	if (!sf.cube_on) {v_move(ux,uy); v_line(ux2,uy2);}
	fxy_polar(ux2-ux,uy2-uy,&r,&a);
	ta = a ;
	a = a - 90;
	if (ax->ticklen == 0) ax->ticklen = base*.001;
	r = ax->ticklen;
	r2 = ax->ticklen+base*.02+ax->dist;
	fpolar_xy(r,a,&ux2,&uy2);
	fpolar_xy(r2,a,&ux3,&uy3);
	
	DVAL(ax->hei,base/60.0);
	v_set_hei(ax->hei);
	v_set_just("TC");

	nice_ticks(&ax->step, &ax->min, &ax->max, &t1, &tn);

	for (x=t1; x<=.00001+ax->max; x+=ax->step) {
		if (ax->type==0) {
			xx =  (nx-1)*(x-ax->min)/(ax->max - ax->min);
			touser(xx,0,minz,&ux,&uy);
		} else {
			xx =  (ny-1)*(x-ax->min)/(ax->max - ax->min);
			touser(nx-1,xx,minz,&ux,&uy);
		}
		v_move(ux,uy);
		v_line(ux+ux2,uy+uy2);
		v_move(ux+ux3,uy+uy3);
		if (fabs(x)<(.0001*ax->step)) x = 0;
		sprintf(buff,"%g",x);
		v_rotate(ta);
		if (ax->nolast && x> (ax->max - .5*(ax->step))) /*  */ ;
		else if (ax->nofirst && x==t1) /* do nothing */;
		else v_text(buff);
		v_rotate(-ta);
	}
	v_set_just("TC");
	/* Now draw the title for this axis */
	if (ax->title==NULL) return;
	v_color(ax->title_color);
	DVAL(ax->title_hei,base/40.0);
	v_set_hei(ax->title_hei);
	if (ax->type==0) {
		touser((nx-1)/2.0,0,minz,&ux,&uy);
	} else {
		touser(nx-1,(ny-1)/2.0,minz,&ux,&uy);
	}
	DVAL(ax->title_dist,base/17.0);
	r = ax->title_dist;
	fpolar_xy(r,a,&ux2,&uy2);
	v_move(ux+ux2,uy+uy2);
	v_rotate(ta);
	v_text(ax->title);
	v_rotate(-ta);
	
}
draw_zaxis(struct axis_struct *ax, int nx, int ny, float minz, float maxz)
{
	float ux,uy,ux2,uy2,ux3,uy3,r,a,ta,r2,x,xx,t1,tn;
	char buff[80];

	if (!ax->on) return;
	touser(0,0,minz,&ux,&uy);
	touser(0,0,maxz,&ux2,&uy2);
	v_color(ax->color);
	if (!sf.cube_on) {v_move(ux,uy); v_line(ux2,uy2);}
	fxy_polar(ux2-ux,uy2-uy,&r,&a);
	ta = a ;
	a = a + 90;
	if (ax->ticklen == 0) ax->ticklen = base*.001;
	r = ax->ticklen;
	r2 = ax->ticklen+base*.02+ax->dist;
	fpolar_xy(r,a,&ux2,&uy2);
	fpolar_xy(r2,a,&ux3,&uy3);
	DVAL(ax->hei,base/60.0);
	v_set_hei(ax->hei);
	v_set_just("RC");
	nice_ticks(&ax->step, &ax->min, &ax->max, &t1, &tn);
	for (x=t1; x<=.0001+ax->max; x+=ax->step) {
		touser(0,0,x,&ux,&uy);
		v_move(ux,uy);
		v_line(ux+ux2,uy+uy2);
		v_move(ux+ux3,uy+uy3);
		if (fabs(x)<(.0001*ax->step)) x = 0;
		sprintf(buff,"%g",x);
		v_text(buff);
	}
	v_set_just("BC");
	/* Now draw the title for this axis */
	if (ax->title==NULL) return;
	v_color(ax->title_color);
	DVAL(ax->title_hei,base/40.0);
	v_set_hei(ax->title_hei);
	touser(0,0,(maxz-minz)/2.0+minz,&ux,&uy);
	DVAL(ax->title_dist,base/17.0);
	r = ax->title_dist;
	fpolar_xy(r,a,&ux2,&uy2);
	v_move(ux+ux2,uy+uy2);
	v_rotate(a-90);
	v_text(ax->title);
	v_rotate(-a+90);
}
move3d(float x, float y, float z)
{
	float ux,uy;
	touser(x,y,z,&ux,&uy);
	v_move(ux,uy);
       #if (defined DJ || defined EMXOS2)   /* a.r. for surface markers */
	d_move_really(ux,uy);
       #endif
}
line3d(float x, float y, float z)
{
	float ux,uy;
	touser(x,y,z,&ux,&uy);
	v_line(ux,uy);
}
show_horizon()
{
	int i;
	FILE *f;
	v_color("RED");
	v_move(unmaph(0),h[0]);
	for (i=0;i<900;i++) {
		v_line(unmaph(i),h[i]);
	}
	v_color("BLUE");
	v_move(unmaph(0),h2[0]);
	for (i=0;i<900;i++) {
		v_line(unmaph(i),h2[i]);
	}

}
skirt(float huge *z,int ix1, int iy1, float minz)
{
	float ux,uy,v;
	int i,x1,x2;
	float y1,y2,step;

	/*
	touser(ix1,iy1,z[ix1+iy1* (int32) nnx],&ux,&y1);
	x1 = maph(ux);
	touser(ix1,iy1,minz,&ux,&y2);
	x2 = maph(ux);
	vector(x1,y1,x2,y2);
	*/

	clipline(ix1,iy1,z[ix1+iy1* (int32) nnx],ix1,iy1,minz);
}
horizonv(float huge *z,int ix1,int iy1,int ix2,int iy2)
{
	float ux,uy;
	int x1,x2,putback=false;
	float y1,y2;

	if (sf.zcolour[0]!=0) {
	if (z[ix1+iy1* (int32) nnx] <= min_zed  ||
	 z[ix2+iy2* (int32) nnx] <= min_zed) {
		putback = true;
		v_color(sf.zcolour);
	}}


	touser(ix1,iy1,z[ix1+iy1* (int32) nnx],&ux,&y1);
	x1 = maph(ux);
	touser(ix2,iy2,z[ix2+iy2* (int32) nnx],&ux,&y2);
	x2 = maph(ux);
	hclipvec(x1,y1,x2,y2,true);

	if (putback) {
		putback = true;
		v_color(sf.bot_color);
	}

}
int doclipping=true;
clipline(float x1, float y1, float z1, float x2, float y2, float z2)
{
	float ux,uy,ux2,uy2;
	int ix1,ix2;

	touser(x1,y1,z1,&ux,&uy);
	touser(x2,y2,z2,&ux2,&uy2);
	if (!doclipping) { v_move(ux,uy); v_line(ux2,uy2); return;}
	ix1 = maph(ux); ix2 = maph(ux2);

/*        printf("hclipvec %d %g  %d %g \n",ix1,uy,ix2,uy2); scr_getch(); */
	if (abs(ix1-ix2)==1) if (fabs(uy2-uy)>.3) ix1 = ix2;
	hclipvec(ix1,uy,ix2,uy2,false);
	hclipvec2(ix1,uy,ix2,uy2,false);
}
horizonv2(float huge *z,int ix1,int iy1,int ix2,int iy2)
{
	float ux,uy;
	int x1,x2;
	float y1,y2;

	touser(ix1,iy1,z[ix1+iy1* (int32) nnx],&ux,&y1);
	x1 = maph(ux);
	touser(ix2,iy2,z[ix2+iy2* (int32) nnx],&ux,&y2);
	x2 = maph(ux);
	hclipvec2(x1,y1,x2,y2,true);
}

hclipvec(int x1, float y1, int x2, float y2, int sethi)
{
	float v,sy,step;
	int i,sd,sx,visible;

       if (x1==x2) {
	/* printf("VEC %d %g   %d %g h=%g\n",x1,y1,x2,y2,h[x1]); scr_getch();
	 */        if (y2<y1) {v = y1; y1 = y2; y2 = v;}
		if (h[x1]<y2) {
			if (h[x1]>y1) y1 = h[x1];
			vector(x1,y1,x2,y2);
			if (sethi) h[x1] = y2;
		}
		return;
       }
       step = (y2-y1)/(x2-x1);
       sd = -1;
       if (x1<x2) sd = 1;
       step = step*sd;
       visible = false;
       for (i=x1, v=y1; sd*i <= sd*x2; i+=sd, v+=step) {
	 if (visible) {
	    if (h[i]>v) {
		if (sethi) vector(sx,sy,i-sd,v-step);
		else if (fabs(h[i]-v)<.5) { vector(sx,sy,i,h[i]);}    /* i-sd,v) */
		else vector(sx,sy,i-sd,v-step);
		visible = false;
	    } else if (sethi) h[i] = v;
	 } else {
	    if (h[i]<=v+.0001) {
		sx = i; sy = v; visible = true;
		if (!sethi) {if (i!=x1) if (fabs(v-h[i])<.5) sy = h[i];}
		if (sethi) h[i] = v;
	    }
	 }
       }
       /* draw end part of line */
       if (visible) vector(sx,sy,x2,y2);
}
seth2(int rx1, int ry1, float rz1, int rx2, int ry2, float rz2)
{
	float vx1,vx2,y1,y2;
	int x1,x2;
	float v,sy,step;
	int i,sd,sx,visible;

     /*        printf("seth2 %d %d %g, %d %d %g \n",rx1,ry1,rz1,rx2,ry2,rz2);
	scr_getch(); */
	touser(rx1,ry1,rz1,&vx1,&y1);
	touser(rx2,ry2,rz2,&vx2,&y2);
	x1 = maph(vx1); x2 = maph(vx2);
	if (x1<0) x1 = 0;
	if (x2<0) x2 = 0;
	if (x1>MAXH) x1 = MAXH-1;
	if (x2>MAXH) x2 = MAXH-1;

       if (x1==x2) {
		if (y2>y1) {v = y1; y1 = y2; y2 = v;}
		if (h2[x1]>y2) h2[x1] = y2;
		return;
       }
       step = (y2-y1)/(x2-x1);
       sd = -1;
       if (x1<x2) sd = 1;
       step = step*sd;
       visible = false;
       for (i=x1, v=y1; sd*i <= sd*x2; i+=sd, v+=step) {
		if (h2[i]>v) h2[i] = v;
       }
}
hclipvec2(int x1, float y1, int x2, float y2, int sethi)
{
	float v,sy,step;
	int i,sd,sx,visible;

       if (x1==x2) {
/*        printf("VEC2 %d %g   %d %g h=%g ",x1,y1,x2,y2,h2[x1]); scr_getch();*/
		if (y2>y1) {v = y1; y1 = y2; y2 = v;}
		if (h2[x1]>y2) {
			if (h2[x1]<y1) y1 = h2[x1];
			vector(x1,y1,x2,y2);
			if (sethi) h2[x1] = y2;
		}
/*                scr_getch(); printf("x \n"); scr_getch();                        */
		return;
       }
       step = (y2-y1)/(x2-x1);
       sd = -1;
       if (x1<x2) sd = 1;
       step = step*sd;
       visible = false;
       for (i=x1, v=y1; sd*i <= sd*x2; i+=sd, v+=step) {
		if (visible) {
			if (h2[i]<v) {
				if (sethi)  vector(sx,sy,i-sd,v-step);
				else  if (fabs(h2[i]-v)<0.5) { vector(sx,sy,i,h2[i]);}
				else  vector(sx,sy,i-sd,v-step);
				visible = false;
			} else if (sethi) h2[i] = v;
		} else {
			if (h2[i]>=v-.0001) {
				sx = i; sy = v; visible = true;
				if (!sethi) {if (i!=x1)  if (fabs(v-h2[i])<.5) sy = h2[i];}
				if (sethi) h2[i] = v;
			}
		}
       }
       /* draw end part of line */
       if (visible) vector(sx,sy,x2,y2);
}
horizon(float huge *z,int ix1,int iy1,int ix2,int iy2)
{
	float ux,uy,v;
	int i,x1,x2;
	float y1,y2,step;
	touser(ix1,iy1,z[ix1+iy1* (int32) nnx],&ux,&y1);
	x1 = maph(ux);
	touser(ix2,iy2,z[ix2+iy2* (int32) nnx],&ux,&y2);
	x2 = maph(ux);

	if (h[x2]-RERR<=y2 && h[x1]-RERR<=y1) {
		vector(x1,y1,x2,y2);
		return;
	}
	if (h[x2]-RERR<=y2 || h[x1]-RERR<=y1) {
	  if (h[x1]-RERR>y1) {
		if (x1==x2) {
			vector(x1,h[x1],x2,y2);
			return;
		}
		step = (y2-y1)/(x2-x1);
		if (x1<x2) {
		  for (i=x1, v=y1; i<=x2; i++, v+=step) {
			if (h[i]<=v) {
				vector(i,v,x2,y2);
				return;
			}
		  }
		} else {
		  for (i=x1, v=y1; i>=x2; i--, v-=step) {
			if (h[i]<=v) {
				vector(i,v,x2,y2);
				return;
			}
		  }
		}
	  } else {
		if (x1==x2) {
			vector(x1,y1,x2,h[x2]);
			return;
		}
		step = (y2-y1)/(x2-x1);
		if (x1<x2) {
		  for (i=x2, v=y2; i>=x1; i--, v-=step) {
			if (h[i]<=v) {
				vector(x1,y1,i,v);
				return;
			}
		  }
		} else {
		  for (i=x2, v=y2; i<=x1; i++, v+=step) {
			if (h[i]<=v) {
				vector(x1,y1,i,v);
				return;
			}
		  }
		}
	  }
	} 
}
horizon2(float huge *z,int ix1,int iy1,int ix2,int iy2)
{
	float ux,uy,v;
	int i,x1,x2;
	float y1,y2,step;
	touser(ix1,iy1,z[ix1+iy1* (int32) nnx],&ux,&y1);
	x1 = maph(ux);
	touser(ix2,iy2,z[ix2+iy2* (int32) nnx],&ux,&y2);
	x2 = maph(ux);

	if (h[x2]+RERR>=y2 && h[x1]+RERR>=y1) {
		vector(x1,y1,x2,y2);
		return;
	}
	if (h[x2]+RERR>=y2 || h[x1]+RERR>=y1) {
	  if (h[x1]+RERR<y1) {
		if (x1==x2) {
			vector(x1,h[x1],x2,y2);
			return;
		}
		step = (y2-y1)/(x2-x1);
		if (x1<x2) {
		  for (i=x1, v=y1; i<=x2; i++, v+=step) {
			if (h[i]>=v) {
				vector(i,v,x2,y2);
				return;
			}
		  }
		} else {
		  for (i=x1, v=y1; i>=x2; i--, v-=step) {
			if (h[i]>=v) {
				vector(i,v,x2,y2);
				return;
			}
		  }
		}
	  } else {
		if (x1==x2) {
			vector(x1,y1,x2,h[x2]);
			return;
		}
		step = (y2-y1)/(x2-x1);
		if (x1<x2) {
		  for (i=x2, v=y2; i>=x1; i--, v-=step) {
			if (h[i]>=v) {
				vector(x1,y1,i,v);
				return;
			}
		  }
		} else {
		  for (i=x2, v=y2; i<=x1; i++, v+=step) {
			if (h[i]>=v) {
				vector(x1,y1,i,v);
				return;
			}
		  }
		}
	  }
	} 
}
vector(int x1, float y1, int x2, float y2)
{
	int i;
	static int savex;
	static float savey;
	float step,v;

	if (x2<0 || x1<0) {gprint("Less than zero \n"); exit(1);}
      /*        if (savex!=x1 || savey!=y1) { */
		v_move(unmaph(x1),y1);
/*        }                                       */
	v_line(unmaph(x2),y2);
	savex = x2;
	savey = y2;
}
vectorzz(int x1, float y1, int x2, float y2)
{
	int i;
	static int savex;
	static float savey;
	float step,v;

	if (x2<0 || x1<0) {gprint("Less than zero \n"); exit(1);}
	if (x1==x2) {
		if (vsign*h[x1]<vsign*y1) h[x1] = y1;
		if (vsign*h[x2]<vsign*y2) h[x2] = y2;
	} else {
		step = (y2-y1)/(x2-x1);
		if (x1<x2) {
		  for (i=x1, v=y1; i<=x2; i++, v+=step) {
			/* printf("! seth i %d v %g  step %g \n",i,v,step); */
			if (vsign*h[i] < vsign*v) h[i] = v;
		  }
		} else {
		  for (i=x1, v=y1; i>=x2; i--, v-=step) {
			/* printf("! seth i %d v %g  step %g \n",i,v,step); */
			if (vsign*h[i] < vsign*v) h[i] = v;
		  }
		}
	}
	if (savex!=x1 || savey!=y1) {
		v_move(unmaph(x1),y1);
	}
	v_line(unmaph(x2),y2);
	savex = x2;
	savey = y2;
}
touser(float x, float y, float z, float *uux, float *uuy)
{
	float uz,ux,uy,p,q;
	ux = image[0][0]*x + image[0][1]*y + image[0][2]*z + image[0][3];
	uy = image[1][0]*x + image[1][1]*y + image[1][2]*z + image[1][3];
	uz = image[2][0]*x + image[2][1]*y + image[2][2]*z + image[2][3];

	ux -= eye_x;
	uy -= eye_y;
	if (maxdepth!=0) {
		p = vdist;                 /* almost touching infinity */
		q = uz/maxdepth; /* z is negative at deep end , zero at top */
		ux = ux - ux*p*q/(1-p+p*q);
		uy = uy - uy*p*q/(1-p+p*q);
/* (my old transformation)
		ux = uz*(-ux/eye_depth)+ux;
		uy = uz*(-uy/eye_depth)+uy;
*/
	}
	*uux = ux + eye_x;
	*uuy = uy + eye_y;
}
touser3(float x, float y, float z, float *uux, float *uuy, float *uuz)
{
	float uz,ux,uy;
	ux = image[0][0]*x + image[0][1]*y + image[0][2]*z + image[0][3];
	uy = image[1][0]*x + image[1][1]*y + image[1][2]*z + image[1][3];
	uz = image[2][0]*x + image[2][1]*y + image[2][2]*z + image[2][3];

	ux -= eye_x;
	uy -= eye_y;
	*uux = ux + eye_x;
	*uuy = uy + eye_y;
	*uuz = uz;
}
matscale(float i[4][4],float x, float y, float z)
{
	
	static float c[4][4];
	c[0][0] = x;
	c[1][1] = y;
	c[2][2] = z;
	c[3][3] = 1;
	matmul(i,c);
}
matmove(float i[4][4],float x, float y, float z)
{
	static float c[4][4];
	c[0][0] = 1;
	c[1][1] = 1;
	c[2][2] = 1;
	c[3][3] = 1;
	c[0][3] = x;
	c[1][3] = y;
	c[2][3] = z;
	matmul(i,c);
}
matmul(float a[4][4],float b[4][4])                /* a = a * b */
{
	static float c[4][4],tot;
	int y,xb,x;
	for (y=0;y<4;y++) {
	  for (xb=0;xb<4;xb++) {
		tot = 0;
		for (x=0;x<4;x++) tot += a[x][y] * b[xb][x];
		c[xb][y] = tot;
	  }
	}
	memcpy(a,c,4*4*sizeof(float));
}
matun(float m[4][4])
{
	int i,j;
	for (i=0;i<4;i++) for (j=0;j<4;j++) m[i][j] = 0;
	for (i=0;i<4;i++) m[i][i] = 1;
}
matrz(float i[4][4], float angle)
{
	/* Rotate around the Z axis */

	float m[4][4];
	matun(m);
	m[0][0] = cos(angle);
	m[1][1] = m[0][0];
	m[0][1] = sin(angle);
	m[1][0] = -m[0][1];
	matmul(i,m);
}
matry(float i[4][4], float angle)
{
	/* Rotate around the y axis */

	float m[4][4];
	matun(m);
	m[0][0] = cos(angle);
	m[2][2] = m[0][0];
	m[2][0] = sin(angle);
	m[0][2] = -m[2][0];
	matmul(i,m);
}
matrx(float i[4][4], float angle)
{
	/* Rotate around the x axis */

	float m[4][4];
	matun(m);
	m[1][1] = cos(angle);
	m[2][2] = m[1][1];
	m[1][2] = sin(angle);
	m[2][1] = -m[1][2];
	matmul(i,m);
}
#define SX(x) ((nx-1)*(x-sf.xaxis.min)/(sf.xaxis.max-sf.xaxis.min))
#define SY(y) ((ny-1)*(y-sf.yaxis.min)/(sf.yaxis.max-sf.yaxis.min))
grid_back(int nx, int ny, float z1, float z2)
{
	float x,y,z;
	v_color(sf.back_color);
	v_lstyle(sf.back_lstyle);
	doclipping = sf.back_hidden;
	if (sf.back_ystep>0) for (y=sf.yaxis.min; y<=sf.yaxis.max+.00001; y+=sf.back_ystep) {
		clipline(0,SY(y),z1,0,SY(y),z2);
	}
	if (sf.back_zstep>0) for (z=z1; z<=z2;  z+=sf.back_zstep) {
	  if (abortkey()) return;
		clipline(0,0,z,0,ny-1,z);
	}

	v_color(sf.right_color);
	v_lstyle(sf.right_lstyle);
	doclipping = sf.right_hidden;
	if (sf.right_xstep>0) for (x=sf.xaxis.min; x<=sf.xaxis.max+.00001; x+=sf.right_xstep) {
	  if (abortkey()) return;
		clipline(SX(x),ny-1,z1,SX(x),ny-1,z2);
	}
	if (sf.right_zstep>0) for (z=z1; z<=z2;  z+=sf.right_zstep) {
	  if (abortkey()) return;
		clipline(0,ny-1,z,nx-1,ny-1,z);
	}


	v_color(sf.base_color);
	v_lstyle(sf.base_lstyle);
	doclipping = sf.base_hidden;
	if (sf.base_xstep>0) for (x=sf.xaxis.min; x<=sf.xaxis.max+.00001; x+=sf.base_xstep) {
		clipline(SX(x),0,z1,SX(x),ny-1,z1);
	}
	if (sf.base_ystep>0) for (y=sf.yaxis.min; y<=sf.yaxis.max+.00001; y+=sf.base_ystep) {
		clipline(0,SY(y),z1,nx-1,SY(y),z1);
	}
}
draw_markers(int nx, int ny)
{
	float *pnt;
	int i;
	pnt = sf.pntxyz;
	if (*sf.marker == 0) return;
	v_color(sf.marker_color);
	DVAL(sf.marker_hei,base/60.0);
	v_set_hei(sf.marker_hei);
	for (i=0; i<sf.npnts; i+=3) {
		move3d(SX(pnt[i]),SY(pnt[i+1]),pnt[i+2]);
		v_marker(sf.marker);
	}
}
draw_riselines(int nx, int ny,float minz, float maxz)
{
	float *pnt=sf.pntxyz;
	int i;

	if (sf.riselines!=0) {
	 v_color(sf.riselines_color);
	 v_lstyle(sf.riselines_lstyle);
	 for (i=0; i<sf.npnts; i+=3) {
		move3d(SX(pnt[i]),SY(pnt[i+1]),pnt[i+2]);
		line3d(SX(pnt[i]),SY(pnt[i+1]),maxz);
	 }
	}

	if (sf.droplines==0) return;
	v_color(sf.droplines_color);
	v_lstyle(sf.droplines_lstyle);
	for (i=0; i<sf.npnts; i+=3) {
		move3d(SX(pnt[i]),SY(pnt[i+1]),pnt[i+2]);
		line3d(SX(pnt[i]),SY(pnt[i+1]),minz);
	}
}
cube(float x, float y, float z1, float z2)
{
	if (sf.cube_hidden_on) doclipping = true;
	else doclipping = false;
	v_color(sf.cube_color);
	v_lstyle(sf.cube_lstyle);

	clipline(x,y,z1,0,y,z1);
	clipline(0,y,z1,0,0,z1);

	clipline(0,0,z1,0,0,z2);
	clipline(0,0,z2,0,y,z2);
	clipline(0,y,z2,0,y,z1);
	clipline(0,y,z2,x,y,z2);
	clipline(x,y,z2,x,y,z1);

	doclipping = false;
	clipline(0,0,z1,x,0,z1);
	clipline(x,0,z1,x,y,z1);

	if (sf.cube_front_on) {
		clipline(0,0,z2,x,0,z2);
		clipline(x,0,z2,x,0,z1);
		clipline(x,0,z2,x,y,z2);
	}
/*

	move3d(0,0,z1);
	v_color(sf.cube_color);
	v_lstyle(sf.cube_lstyle);
	line3d(x,0,z1);
	line3d(x,y,z1);
	line3d(0,y,z1);
	line3d(0,0,z1);

	line3d(0,0,z2);
	line3d(0,y,z2);
	line3d(0,y,z1);
	move3d(0,y,z2);
	line3d(x,y,z2);
	line3d(x,y,z1);
	if (sf.cube_front_on) {
		move3d(0,0,z2);
		line3d(x,0,z2);
		line3d(x,0,z1);
		move3d(x,0,z2);
		line3d(x,y,z2);
	}
	*/
}
matshow(char *s, float m[4][4])
{
	int i,j;
	printf("\n! Matrix {%s} \n",s);
	for (i=0;i<4;i++)
		printf("!        %f %f %f %f\n",m[0][i],m[1][i],m[2][i],m[3][i]);

}
#define pi (3.141592654)
find_splits(int nx, int ny, int *splitx, int *splity)
{
	int lasta,i,isleft;
	float ux,uy,ux2,uy2,angle,r;        

	lasta = 999;
	*splity = -1;
	*splitx = nx-1;
	for (i = 0; i<ny; i++) {
		touser(nx-1,i,0,&ux,&uy);
		touser(0,i,0,&ux2,&uy2);
		fxy_polar(ux2-ux,uy2-uy,&r,&angle);
		if (angle<90) isleft = true;
		if (angle>=90) isleft = false;
		if (lasta==999) lasta = isleft;
		if (lasta!=isleft) {
			*splity = i-1;
		}
		lasta = isleft;
	}
	lasta=999;
	for (i = 0; i<nx; i++) {
		touser(i,0,0,&ux,&uy);
		touser(i,ny-1,0,&ux2,&uy2);
		fxy_polar(ux2-ux,uy2-uy,&r,&angle);
		if (angle<90) isleft = true;
		if (angle>=90) isleft = false;
		if (lasta==999) lasta = isleft;
		if (lasta!=isleft) {
			*splitx = i-1;
		}
		lasta = isleft;
	}
}
nice_ticks(float *dticks, float *gmin,float *gmax , float *t1,float *tn)
{
	float delta,st,expnt,n;
	int ni;

	delta = *gmax-*gmin;
	if (delta==0) {gprint("Axis range error min=%g max=%g \n",*gmin,*gmax);
		*gmax = *gmin+10;
		delta = 10;
	}
	st = delta/10;
	expnt = floor(log10(st));
	n = st/pow(10,expnt);
	if (n>5)
		ni = 10;
	else if (n>2)
		ni = 5;
	else if (n>1)
		ni = 2;
	else
		ni = 1;
	if (*dticks==0) *dticks = ni * pow(10,expnt);
	if (*gmin - (delta/1000) <=  floor( *gmin/ *dticks) * *dticks)
		*t1 = *gmin;
	else
		*t1 = (floor(*gmin/ *dticks) * *dticks ) + *dticks;

	*tn = *gmax;
	if (( floor( *gmax/ *dticks) * *dticks) < (*gmax - (delta/1000) ))
		*tn = floor(*gmax/ *dticks ) * *dticks;
}
