/*
 *	Euler - a numerical lab
 *
 *	platform : neutral
 *
 *	file : graphics.h -- portable advanced graphics
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>

#include "sysdep.h"
#include "output.h"
#include "funcs.h"
#include "graphics.h"
#include "meta.h"
#include "express.h"
#include "matrix.h"


static double x_min=-1,x_max=1,y_min=-1,y_max=1,meshfactor=1;
static int dgrid=0;

static int upperclipc=0,upperclipr=0,lowerclipc=1023,lowerclipr=1023;
static int upperc=10,upperr=30,lowerc=1010,lowerr=1010;
static int connected[4]={1,1,1,1};
static int tconnected[3]={1,0,0};
static int keepsquare=0;

static int linetype=line_solid,linecolor=1,lines=1,holding=0,framecolor=3,
	wirecolor=2,textcolor=2,markertype=marker_cross,scaling=1,
	newframe,twosides=1,linewidth=1,densitycolor=1,antialiasing=0,
	bartype=bar_framed,barcolor=3;

static double distance=7,tele=1.5,a_left=0.5,a_up=0.5;

static double scrcol (double x)
{	return (upperc+(x-x_min)/(x_max-x_min)*(lowerc-upperc));
}

static double scrrow (double y)
{	return (lowerr-(y-y_min)/(y_max-y_min)*(lowerr-upperr));
}

static void frame (void)
{	gline(upperc,upperr,upperc,lowerr,framecolor,line_solid,1);
	gline(upperc,lowerr,lowerc,lowerr,framecolor,line_solid,1);
	gline(lowerc,lowerr,lowerc,upperr,framecolor,line_solid,1);
	gline(lowerc,upperr,upperc,upperr,framecolor,line_solid,1);
	newframe=0;
}

static void plot_vector (double *x, double *y, int n, int m)
/***** plot_vector
	plots n pairs (x,y).
*****/
{	double c0,r0,c1,r1;
	int i;
	if (n<=0) return;
	if (lines)
	{	c0=scrcol(*x); r0=scrrow(*y);
		if (m) gmarker(c0,r0,linecolor,markertype);
		if (n==1) gline(c0,r0,c0,r0,linecolor,linetype,linewidth);
		for (i=1; i<n; i++)
		{	x++; y++;
			c1=scrcol(*x); r1=scrrow(*y);
			if (m) gmarker(c1,r1,linecolor,markertype);
			else gline(c0,r0,c1,r1,linecolor,linetype,linewidth);
			c0=c1; r0=r1;
		}
	}
}

static void do_plot (header *hdx, header *hdy, int m)
{	int cx,rx,cy,ry,i,ix,iy;
	double *x,*y,h;
	getmatrix(hdx,&rx,&cx,&x); getmatrix(hdy,&ry,&cy,&y);
	if (cx!=cy || (rx>1 && ry!=rx))
	{	error=22; output("Plot columns must agree!\n");
		return;
	}
	if (scaling)
	{	minmax(x,(long)cx*rx,&x_min,&x_max,&ix,&iy);
		minmax(y,(long)cy*ry,&y_min,&y_max,&ix,&iy);
	}
	if (x_max>DBL_MAX) x_max=DBL_MAX;
	if (x_min<-DBL_MAX) x_min=-DBL_MAX;
	if (y_max>DBL_MAX) y_max=DBL_MAX;
	if (y_min<-DBL_MAX) y_min=-DBL_MAX;
	if (x_min>=x_max) x_min=x_max+1;
	if (y_min>=y_max) y_min=y_max+1;
	if (keepsquare)
	{	if (x_max-x_min>y_max-y_min)
		{   h=(y_max+y_min)/2;
			y_max=h+(x_max-x_min)/2;
			y_min=h-(x_max-x_min)/2;
		}
		else
		{   h=(x_max+x_min)/2;
			x_max=h+(y_max-y_min)/2;
			x_min=h-(y_max-y_min)/2;
		}
	}
	graphic_mode();
	if (!holding) { gclear(); }
	if (!holding || newframe) frame();
	for (i=0; i<ry; i++)
	{	plot_vector(mat(x,cx,(i>=rx)?rx-1:i,0),mat(y,cy,i,0),cx,m);
		if (test_key()==escape) break;
	}
	gflush();
}

void mplot (header *hd)
{	header *hd1=0,*st=hd,*result;
	double *x;
	hd=getvalue(hd); if (error) return;
	if (hd) /* parameters given */
	{	if (hd->type!=s_matrix && hd->type!=s_real)
		{	error=21;
			output("Plot needs a real vector or matrix!\n");
			return;
		}
		hd1=next_param(st);
		if (hd1) hd1=getvalue(hd1); if (error) return;
		if (hd1->type!=s_matrix && hd1->type!=s_real)
		{	error=11001; output("Wrong arguments for plot!\n");
			return;
		}
	}
	do_plot(hd,hd1,0);
	result=new_matrix(1,4,""); if (error) return;
	x=matrixof(result);
	*x++=x_min; *x++=x_max; *x++=y_min; *x=y_max;
	moveresult(st,result);
}

void mplotarea (header *hd)
{	header *hd1=0,*st=hd,*result;
	double *x,*y,h;
	int cx,rx,cy,ry,ix,iy;
	hd=getvalue(hd); if (error) return;
	if (hd) /* parameters given */
	{	if (hd->type!=s_matrix && hd->type!=s_real)
		{	error=21;
			output("Plot needs a real vector or matrix!\n");
			return;
		}
		hd1=next_param(st);
		if (hd1) hd1=getvalue(hd1); if (error) return;
		if (hd1->type!=s_matrix && hd1->type!=s_real)
		{	error=11000; output("Wrong arguments for plotarea!\n"); return;
		}
	}
	getmatrix(hd,&rx,&cx,&x); getmatrix(hd1,&ry,&cy,&y);
	if (cx!=cy || (rx>1 && ry!=rx))
	{	error=22; output("Plot columns must agree!\n");
		return;
	}
	if (scaling)
	{	minmax(x,(long)cx*rx,&x_min,&x_max,&ix,&iy);
		minmax(y,(long)cy*ry,&y_min,&y_max,&ix,&iy);
		if (x_min==x_max) x_max=x_min+1;
		if (y_min==y_max) y_max=y_min+1;
		scaling=0;
	}
	if (keepsquare)
	{	if (x_max-x_min>y_max-y_min)
		{   h=(y_max+y_min)/2;
			y_max=h+(x_max-x_min)/2;
			y_min=h-(x_max-x_min)/2;
		}
		else
		{   h=(x_max+x_min)/2;
			x_max=h+(y_max-y_min)/2;
			x_min=h-(y_max-y_min)/2;
		}
	}
	result=new_matrix(1,4,""); if (error) return;
	x=matrixof(result);
	*x++=x_min; *x++=x_max; *x++=y_min; *x++=y_max;
	moveresult(st,result);
}

void mpixel (header *hd)
{	double x,y;
	hd=new_matrix(1,2,""); if (error) return;
	getpixelsize(&x,&y);
	x*=(x_max-x_min)/(lowerc-upperc);
	y*=(y_max-y_min)/(lowerr-upperr);
	*(matrixof(hd))=x; *(matrixof(hd)+1)=y;
}

void mmark (header *hd)
{	header *hd1,*st=hd,*result;
	double *x;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix && hd->type!=s_real)
	{	error=21; output("Mark needs a vector or matrix!\n");
		return;
	}
	hd1=next_param(st);
	if (hd1) hd1=getvalue(hd1); if (error) return;
	if (hd1->type!=s_matrix && hd->type!=s_real)
	{	error=-1; output("Illegal arguments for mark!\n"); return;
	}
	do_plot(hd,hd1,1);
	result=new_matrix(1,4,""); if (error) return;
	x=matrixof(result);
	*x++=x_min; *x++=x_max; *x++=y_min; *x=y_max;
	moveresult(st,result);
	gflush();
}

void ghold (void)
/**** hold
	toggles holding of the current plot.
****/
{	static int oldhold=-1;
	scan_space();
	if (!strncmp(next,"off",3))
	{	oldhold=-1; holding=0; next+=3;
	}
	else if (!strncmp(next,"on",2))
	{	oldhold=-1; holding=1; next+=2;
	}
	else
	{	if (oldhold!=-1) {	holding=oldhold; oldhold=-1; }
		else { oldhold=holding; holding=1; }
	}
	scaling=!holding;
}

void show_graphics (void)
{	int scan;
	graphic_mode(); wait_key(&scan); text_mode();
}

void mmesh (header *hd)
{	double *screen_col,*screen_row;
	long col,size;
	double *m,ymin,ymax,xxscale,xyscale,yxscale,yyscale;
	int imin,imax,c,r,i,j;
	double cc[8];
	header *st=hd;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->c<2 || dimsof(hd)->r<2)
	{	output("Illegal parameter for mesh!\n"); error=80; return;
	}
	getmatrix(hd,&r,&c,&m); col=r;
	minmax(m,(long)c*r,&ymin,&ymax,&imin,&imax);
	if (ymin==ymax) ymax=ymin+1;
	size=(long)c*r*sizeof(double);
	screen_col=(double *)newram;
	screen_row=(double *)(newram+size);
	if (!freeram(2*size))
	{	output("Out of memory in mesh!\n");
		error=85; return;
	}

	xxscale=0.6*(lowerc-upperc)/c; xyscale=0.4*(lowerc-upperc)/r;
	yxscale=0.2*(lowerr-upperr)/r;
	yyscale=0.8*meshfactor*(lowerr-upperr)/(ymax-ymin);
	for (i=0; i<c; i++)
		for (j=0; j<r; j++)
		{	screen_col[col*i+j]=upperc+(xxscale*i+xyscale*j);
			screen_row[col*i+j]=lowerr-(yxscale*j+
				yyscale*(*mat(m,c,j,i)-ymin));
			if (test_key()==escape)
			{	output("User interrupted!\n");
				error=1; return;
			}
		}
	graphic_mode();
	if (!holding) gclear();
	for (i=0; i<c-1; i++)
		for (j=r-2; j>=0; j--)
		{  	cc[4]=( screen_col[col*i+j] + screen_col[col*(i+1)+j] +
		   	     screen_col[col*(i+1)+j+1] + screen_col[col*i+j+1])/4;
			cc[5]=( screen_row[col*i+j] + screen_row[col*(i+1)+j] +
			     screen_row[col*(i+1)+j+1] + screen_row[col*i+j+1])/4;
			cc[0]=screen_col[col*(i+1)+j+1]; cc[1]=screen_row[col*(i+1)+j+1];
			cc[2]=screen_col[col*i+j+1]; cc[3]=screen_row[col*i+1+j];
			if (twosides &&
				((cc[2]-cc[0])*(cc[5]-cc[1])-
					(cc[3]-cc[1])*(cc[4]-cc[0]))>0
				)
				gfill(cc,fill_filled,3,tconnected);
			else gfill(cc,fill_blank,3,tconnected);
			cc[0]=screen_col[col*i+j+1]; cc[1]=screen_row[col*i+j+1];
			cc[2]=screen_col[col*i+j]; cc[3]=screen_row[col*i+j];
			if (twosides &&
				((cc[2]-cc[0])*(cc[5]-cc[1])-
					(cc[3]-cc[1])*(cc[4]-cc[0]))>0
				)
			gfill(cc,fill_filled,3,tconnected);
			else gfill(cc,fill_blank,3,tconnected);
			cc[0]=screen_col[col*(i+1)+j]; cc[1]=screen_row[col*(i+1)+j];
			cc[2]=screen_col[col*(i+1)+j+1]; cc[3]=screen_row[col*(i+1)+j+1];
			if (twosides &&
				((cc[2]-cc[0])*(cc[5]-cc[1])-
					(cc[3]-cc[1])*(cc[4]-cc[0]))>0
				)
			gfill(cc,fill_filled,3,tconnected);
			else gfill(cc,fill_blank,3,tconnected);
			cc[0]=screen_col[col*i+j]; cc[1]=screen_row[col*i+j];
			cc[2]=screen_col[col*(i+1)+j]; cc[3]=screen_row[col*(i+1)+j];
			if (twosides &&
				((cc[2]-cc[0])*(cc[5]-cc[1])-
					(cc[3]-cc[1])*(cc[4]-cc[0]))>0
				)
				gfill(cc,fill_filled,3,tconnected);
			else gfill(cc,fill_blank,3,tconnected);
			if (test_key()==escape)
			{	output("User interrupted!\n");
				error=1; gflush(); return;
			}
		}
	hd=new_matrix(1,2,"");
	*matrixof(hd)=ymin; *(matrixof(hd)+1)=ymax;
	moveresult(st,hd);
	gflush();
}

void mmeshflat (header *hd)
{	double *m,ymin,ymax,xxscale,xyscale,yxscale,yyscale;
	int imin,imax,c,r,i,j,c1;
	double cc[8];
	double sc,sr;
	header *st=hd;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->c<1 || dimsof(hd)->r<1)
	{	output("Illegal parameter for meshbar!\n"); error=80; return;
	}
	getmatrix(hd,&r,&c,&m);
	minmax(m,(long)c*r,&ymin,&ymax,&imin,&imax);
	if (ymin==ymax) ymax=ymin+1;
	if (c>r) c1=c;
	else c1=r;

	xxscale=0.6*(lowerc-upperc)/c1;
	xyscale=0.4*(lowerc-upperc)/c1;
	yxscale=0.2*(lowerr-upperr)/c1;
	yyscale=0.8*meshfactor*(lowerr-upperr)/(ymax-ymin);
	graphic_mode();
	if (!holding) gclear();
	for (i=0; i<c; i++)
		for (j=r-1; j>=0; j--)
		{   sc=upperc+(xxscale*i+xyscale*j);
			sr=lowerr-(yxscale*j+yyscale*(*mat(m,c,j,i)-ymin));
			cc[0]=sc; cc[1]=sr;
			cc[2]=sc+xxscale; cc[3]=sr;
			cc[4]=sc+xxscale+xyscale; cc[5]=sr-yxscale;
			cc[6]=sc+xyscale; cc[7]=sr-yxscale;
			gfill(cc,fill_filled,4,connected);
			cc[0]=sc; cc[1]=sr;
			cc[2]=sc+xxscale; cc[3]=sr;
			cc[4]=sc+xxscale; cc[5]=lowerr-yxscale*j;
			cc[6]=sc; cc[7]=lowerr-yxscale*j;
			gfill(cc,fill_filled,4,connected);
			cc[0]=sc+xxscale; cc[1]=sr;
			cc[2]=sc+xxscale+xyscale; cc[3]=sr-yxscale;
			cc[4]=sc+xxscale+xyscale; cc[5]=lowerr-yxscale*(j+1);
			cc[6]=sc+xxscale; cc[7]=lowerr-yxscale*j;
			gfill(cc,fill_filled,4,connected);
			if (test_key()==escape)
			{	output("User interrupted!\n");
				error=1; gflush(); return;
			}
		}
	hd=new_matrix(1,2,"");
	*matrixof(hd)=ymin; *(matrixof(hd)+1)=ymax;
	moveresult(st,hd);
	gflush();
}

static double cos_up,sin_up,cos_left,sin_left;

static void turn (double *x, double *y, double cs, double sn)
{	double h;
	h=*x*cs-*y*sn;
	*y=*x*sn+*y*cs;
	*x=h;
}

static double project (double x, double y, double z, double *c, double *r)
/***** project
	3D-projection onto the screen.
*****/
{	turn(&y,&x,cos_left,sin_left);
	turn(&y,&z,cos_up,sin_up);
	if (y<-0.9*distance) y=-0.9*distance;
	x/=(y+distance); z/=(y+distance);
	*c=((upperc+lowerc)/2+(lowerc-upperc)/2*x*tele);
//	*r=1024-((upperr+lowerr)/2+(lowerr-upperr)/2*z*tele); //buggy
	*r=((upperr+lowerr)/2-(lowerr-upperr)/2*z*tele);
	return y;
}

void mproject (header *hd)
{	long col;
	double *mx,*my,*mz,*screen_col,*screen_row;
	int c,r,i,j;
	header *st=hd,*hd1,*hd2,*result1,*result2;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix)
	{	output("Illegal parameter for project!\n"); error=82; return;
	}
	getmatrix(hd,&r,&c,&mx); col=c;
	hd1=next_param(st); hd2=next_param(hd1);
	hd1=getvalue(hd1); hd2=getvalue(hd2);
	if (error) return;
	if (hd1->type!=s_matrix || hd2->type!=s_matrix ||
		dimsof(hd1)->r!=r || dimsof(hd2)->r!=r ||
		dimsof(hd1)->c!=c || dimsof(hd2)->c!=c)
	{	output("Matrix dimensions for project must agree!\n");
		error=83; return;
	}
	my=matrixof(hd1); mz=matrixof(hd2);
	result1=new_matrix(r,c,""); if (error) return;
	result2=new_matrix(r,c,""); if (error) return;
	screen_col=matrixof(result1);
	screen_row=matrixof(result2);
	
	cos_left=cos(a_left); sin_left=sin(a_left);
	cos_up=cos(a_up); sin_up=sin(a_up);
	for (i=0; i<r; i++)
		for (j=0; j<c; j++)
		{	project(*mat(mx,c,i,j),*mat(my,c,i,j),*mat(mz,c,i,j),
				&screen_col[col*i+j],&screen_row[col*i+j]);
			if (test_key()==escape)
			{   output("User interrupted!\n");
				error=1; return;
			}
		}
	moveresult(st,result1);
	moveresult(nextof(st),result2);
}

typedef struct { int i,j; double z; } recttyp;

static int compare (const recttyp **r1, const recttyp **r2)
{	if ((*r1)->z > (*r2)->z) return -1;
	else if ((*r1)->z < (*r2)->z) return 1;
	else return 0;
}

void mwire (header *hd)
{	double *screen_col,*screen_row;
	long col,size;
	double *mx,*my,*mz;
	int c,r,i,j,cx,cy,cz,rx,ry,rz;
	header *st=hd,*hd1,*hd2;
	hd1=next_param(st); hd2=next_param(hd1);
	hd=getvalue(hd);
	hd1=getvalue(hd1); hd2=getvalue(hd2);
	if (error) return;
	if (!isreal(hd) || !isreal(hd1) || !isreal(hd2))
		wrong_arg_in("wire");
	getmatrix(hd,&rx,&cx,&mx);
	getmatrix(hd1,&ry,&cy,&my);
	getmatrix(hd2,&rz,&cz,&mz);
	r=rx; if (ry>r) r=ry; if (rz>r) r=rz;
	c=cx; if (cy>c) c=cy; if (cz>c) r=rz;
	col=c;
	if (r<1 || c<1) wrong_arg_in("wire");
	if ((rx>1 && rx!=r) || (ry>1 && ry!=r) || (rz>1 && rz!=r)
		|| (cx>1 && cx!=c) || (cy>1 && cy!=c) || (cz>1 && cz!=c))
		wrong_arg_in("wire");
	size=(long)c*r*sizeof(double);
	screen_col=(double *)newram;
	screen_row=(double *)(newram+size);
	if (!freeram(2*size))
	{	output("Out of memory in wire!\n");
		error=85; return;
	}

	cos_left=cos(a_left); sin_left=sin(a_left);
	cos_up=cos(a_up); sin_up=sin(a_up);
	for (i=0; i<r; i++)
		for (j=0; j<c; j++)
		{	project(
				*mat(mx,cx,(rx==1)?0:i,(cx==1)?0:j),
				*mat(my,cy,(ry==1)?0:i,(cy==1)?0:j),
				*mat(mz,cz,(rz==1)?0:i,(cz==1)?0:j),
				&screen_col[col*i+j],&screen_row[col*i+j]);
			if (test_key()==escape)
			{   output("User interrupted!\n");
				error=1; return;
			}
		}
	graphic_mode();
	if (!holding) gclear();
	if (c>1) for (i=0; i<r; i++)
	{	for (j=0; j<c-1; j++)
		{	gline(screen_col[col*i+j],screen_row[col*i+j],
				screen_col[col*i+j+1],screen_row[col*i+j+1],
				linecolor,linetype,linewidth);
		}
	}
	if (r>2) for (j=0; j<c; j++)
	{	for (i=0; i<r-1; i++)
		{	gline(screen_col[col*i+j],screen_row[col*i+j],
				screen_col[col*(i+1)+j],screen_row[col*(i+1)+j],
				wirecolor,linetype,linewidth);
		}
	}
	hd=new_real(0.0,"");
	moveresult(st,hd);
	gflush();
}

typedef struct { int i,j,type; double z; } triangletyp;

static int comparetriangle (const triangletyp **r1, const triangletyp **r2)
{	if ((*r1)->z > (*r2)->z) return -1;
	else if ((*r1)->z < (*r2)->z) return 1;
	else return 0;
}

void msolid (header *hd)
{	double *screen_col,*screen_row,*screen_z;
	unsigned long col,size,n,ind;
	double *mx,*my,*mz,z;
	int c,r,i,j,rx,cx,ry,cy,rz,cz;
	double cc[8];
    int connect1[]={1,0,1};
	header *st=hd,*hd1,*hd2;
	triangletyp *trp;
	triangletyp **trpp,**trps;
	hd=getvalue(hd);
	hd1=next_param(st); hd2=next_param(hd1);
	hd1=getvalue(hd1); hd2=getvalue(hd2);
	if (error) return;
	if (!isreal(hd) || !isreal(hd1) || !isreal(hd2))
		wrong_arg_in("solid");
	getmatrix(hd,&rx,&cx,&mx);
	getmatrix(hd1,&ry,&cy,&my);
	getmatrix(hd2,&rz,&cz,&mz);
	r=rx; if (ry>r) r=ry; if (rz>r) r=rz;
	c=cx; if (cy>c) c=cy; if (cz>c) r=rz;
	col=c;
	if (r<2 || c<2) wrong_arg_in("solid");
	if ((rx>1 && rx!=r) || (ry>1 && ry!=r) || (rz>1 && rz!=r)
		|| (cx>1 && cx!=c) || (cy>1 && cy!=c) || (cz>1 && cz!=c))
		wrong_arg_in("solid");
	size=(long)c*r*sizeof(double);
	n=(long)(r-1)*(c-1);
	screen_col=(double *)newram;
	screen_row=(double *)(newram+size);
    screen_z=(double *)(newram+2*size);
	trp=(triangletyp *)(newram+3*size);
	trpp=trps=(triangletyp **)(newram+3*size+2*n*sizeof(triangletyp));
	if (!freeram(3*size+(sizeof(triangletyp)+sizeof(triangletyp *))*2*n))
	{	output("Out of memory in solid!\n");
		error=85; return;
	}
	cos_left=cos(a_left); sin_left=sin(a_left);
	cos_up=cos(a_up); sin_up=sin(a_up);
	for (i=0; i<r; i++)
		for (j=0; j<c; j++)
		{	screen_z[col*i+j]=project(
				*mat(mx,cx,(rx==1)?0:i,(cx==1)?0:j),
				*mat(my,cy,(ry==1)?0:i,(cy==1)?0:j),
				*mat(mz,cz,(rz==1)?0:i,(cz==1)?0:j),
				&screen_col[col*i+j],&screen_row[col*i+j]);
			if (test_key()==escape)
			{   output("User interrupted");
				error=1; return;
			}
		}
	for (i=0; i<r-1; i++)
		for (j=0; j<c-1; j++)
		{   z=(screen_z[col*i+j]+screen_z[col*(i+1)+j]
        		+screen_z[col*i+j+1])/3;
        	trp->i=i; trp->j=j; trp->type=0; trp->z=z;
			*trpp++=trp; trp++;
            z=(screen_z[col*(i+1)+(j+1)]+screen_z[col*(i+1)+j]
        		+screen_z[col*i+j+1])/3;
        	trp->i=i; trp->j=j; trp->type=1; trp->z=z;
			*trpp++=trp; trp++;
			if (test_key()==escape)
			{   output("User interrupted");
				error=1; return;
			}
		}
	qsort(trps,2*n,sizeof(triangletyp *),
		(int (*)(const void *, const void *))comparetriangle);
	graphic_mode();
	if (!holding) gclear();
	trpp=trps;
	for (ind=0; ind<2*n; ind++)
	{	i=(*trpp)->i; j=(*trpp)->j;
    	if ((*trpp)->type==0)
		{	cc[0]=screen_col[col*i+j];
        	cc[1]=screen_row[col*i+j];
			cc[2]=screen_col[col*(i+1)+j]; cc[3]=screen_row[col*(i+1)+j];
			cc[4]=screen_col[col*i+(j+1)]; cc[5]=screen_row[col*i+(j+1)];
			if (!twosides ||
				((cc[2]-cc[0])*(cc[5]-cc[1])-(cc[3]-cc[1])*(cc[4]-cc[0]))>0)
				gfill(cc,fill_blank,3,connect1);
			else
    	    	gfill(cc,fill_filled,3,connect1);
        }
        else
		{	cc[0]=screen_col[col*(i+1)+(j+1)];
        	cc[1]=screen_row[col*(i+1)+(j+1)];
			cc[2]=screen_col[col*(i+1)+j]; cc[3]=screen_row[col*(i+1)+j];
			cc[4]=screen_col[col*i+(j+1)]; cc[5]=screen_row[col*i+(j+1)];
			if (!twosides ||
				((cc[2]-cc[0])*(cc[5]-cc[1])-(cc[3]-cc[1])*(cc[4]-cc[0]))<0)
				gfill(cc,fill_blank,3,connect1);
			else
    	    	gfill(cc,fill_filled,3,connect1);
        }
		if (test_key()==escape)
		{   output("User interrupted!\n");
			error=1; gflush(); return;
		}
		trpp++;
	}
	hd=new_real(0.0,"");
	moveresult(st,hd);
	gflush();
}

void msolidh (header *hd)
{	double *screen_col,*screen_row;
	unsigned long col,size,n,ind;
	double *mx,*my,*mz,*mh,z;
	int c,r,i,j,rx,cx,ry,cy,rz,cz,rh,ch;
	double cc[8];
	header *st=hd,*hd1,*hd2,*hd3;
	recttyp *rectp;
	recttyp **rectpp,**rectps;
	hd=getvalue(hd);
	hd1=next_param(st); hd2=next_param(hd1); hd3=next_param(hd2);
	hd1=getvalue(hd1); hd2=getvalue(hd2); hd3=getvalue(hd3);
	if (!isreal(hd) || !isreal(hd1) || !isreal(hd2) || !isreal(hd3))
		wrong_arg_in("solidhue");
	getmatrix(hd,&rx,&cx,&mx);
	getmatrix(hd1,&ry,&cy,&my);
	getmatrix(hd2,&rz,&cz,&mz);
	getmatrix(hd3,&rh,&ch,&mh);
	r=rx; if (ry>r) r=ry; if (rz>r) r=rz; if (rh>r) r=rh;
	c=cx; if (cy>c) c=cy; if (cz>c) r=rz; if (ch>c) ch=c;
	col=c;
	if (r<2 || c<2) wrong_arg_in("solidhue");
	if ((rx>1 && rx!=r) || (ry>1 && ry!=r) || (rz>1 && rz!=r)
		|| (cx>1 && cx!=c) || (cy>1 && cy!=c) || (cz>1 && cz!=c)
		|| (rh>1 && rh!=r) || (ch>1 && ch!=c))
		wrong_arg_in("solidhue");
	size=(long)c*r*sizeof(double);
	n=(long)(r-1)*(c-1);
	screen_col=(double *)newram;
	screen_row=(double *)(newram+size);
	rectp=(recttyp *)(newram+2*size);
	rectpp=rectps=(recttyp **)(newram+2*size+n*sizeof(recttyp));
	if (!freeram(2*size+(sizeof(recttyp)+sizeof(recttyp *))*n))
	{	output("Out of memory in solidhue!\n");
		error=85; return;
	}
	cos_left=cos(a_left); sin_left=sin(a_left);
	cos_up=cos(a_up); sin_up=sin(a_up);
	for (i=0; i<r; i++)
		for (j=0; j<c; j++)
		{	z=project(
				*mat(mx,cx,(rx==1)?0:i,(cx==1)?0:j),
				*mat(my,cy,(ry==1)?0:i,(cy==1)?0:j),
				*mat(mz,cz,(rz==1)?0:i,(cz==1)?0:j),
				&screen_col[col*i+j],&screen_row[col*i+j]);
			if (i<r-1 && j<c-1)
			{	rectp->i=i; rectp->j=j; rectp->z=z;
				*rectpp++=rectp; rectp++;
			}
			if (test_key()==escape)
			{	output("User interrupted!\n");
				error=1; return;
			}
		}
	qsort(rectps,n,sizeof(recttyp *),
		(int (*)(const void *,const void *))compare);
	graphic_mode();
	if (!holding) gclear();
	rectpp=rectps;
	for (ind=0; ind<n; ind++)
	{	i=(*rectpp)->i; j=(*rectpp)->j;
		cc[0]=screen_col[col*i+j]; cc[1]=screen_row[col*i+j];
		cc[2]=screen_col[col*(i+1)+j]; cc[3]=screen_row[col*(i+1)+j];
		cc[4]=screen_col[col*(i+1)+j+1]; cc[5]=screen_row[col*(i+1)+(j+1)];
		cc[6]=screen_col[col*i+j+1]; cc[7]=screen_row[col*i+j+1];
		gfillh(cc,
			*mat(mh,ch,(rh==1)?0:i,(ch==1)?0:j),
			densitycolor,dgrid);
		if (test_key()==escape)
		{	output("User interrupted!\n");
			error=1; gflush(); return;
		}
		rectpp++;
	}
	hd=new_real(0.0,"");
	moveresult(st,hd);
	gflush();
}

void msolid1 (header *hd)
{	double *screen_col,*screen_row;
	unsigned long col,size,n,ind;
	double *mx,*my,*mz,z,*mult;
	int c,r,i,j,multc,multr,multi,multn,norectp=0;
	double cc[8];
	header *st=hd,*hd1,*hd2,*hdmult;
	recttyp *rectp;
	recttyp **rectpp,**rectps;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r<2 || dimsof(hd)->c<2)
	{	output("Illegal parameter for solid!\n"); error=82; return;
	}
	getmatrix(hd,&r,&c,&mx); col=c;
	hd1=next_param(st); hd2=next_param(hd1); hdmult=next_param(hd2);
	hd1=getvalue(hd1); hd2=getvalue(hd2); hdmult=getvalue(hdmult);
	if (error) return;
	if (hd1->type!=s_matrix || hd2->type!=s_matrix ||
		dimsof(hd1)->r!=r || dimsof(hd2)->r!=r ||
		dimsof(hd1)->c!=c || dimsof(hd2)->c!=c)
	{	output("Matrix dimensions for solid must agree!\n");
		error=83; return;
	}
	if (hdmult->type!=s_real &&
		(hdmult->type!=s_matrix || dimsof(hdmult)->r!=1
			|| dimsof(hdmult)->c<1)
		)
	{	output("4th parameter for solid must be a real vector!\n");
		error=83; return;
	}
	my=matrixof(hd1); mz=matrixof(hd2);
	getmatrix(hdmult,&multr,&multc,&mult);
	multn=0; multi=(int)(*mult)-1;
	size=(long)c*r*sizeof(double);
	n=(long)(r-1)*(c-1);
	screen_col=(double *)newram;
	screen_row=(double *)(newram+size);
	rectp=(recttyp *)(newram+2*size);
	rectpp=rectps=(recttyp **)(newram+2*size+n*sizeof(recttyp));
	if (!freeram(2*size+(sizeof(recttyp)+sizeof(recttyp *))*n))
	{	output("Out of memory in solid!\n");
		error=85; return;
	}	
	cos_left=cos(a_left); sin_left=sin(a_left);
	cos_up=cos(a_up); sin_up=sin(a_up);
	for (i=0; i<r; i++)
	{	if (multi==i)
		{	mult++; multn++;
			if (multn>=multc) multi=-1;
			else multi=(int)(*mult)-1;
			n-=c-1;
			norectp=1;
		}
		for (j=0; j<c; j++)
		{	z=project(*mat(mx,c,i,j),*mat(my,c,i,j),*mat(mz,c,i,j),
				&screen_col[col*i+j],&screen_row[col*i+j]);
			if (i<r-1 && j<c-1 && !norectp)
			{	rectp->i=i; rectp->j=j; rectp->z=z;
				*rectpp++=rectp; rectp++;
			}
			if (test_key()==escape)
			{   output("User interrupted!\n");
				error=1; return;
			}
		}
		norectp=0;
	}
	qsort(rectps,n,sizeof(recttyp *),
		(int (*)(const void *,const void *))compare);
	graphic_mode();
	if (!holding) gclear();
	rectpp=rectps;
	for (ind=0; ind<n; ind++)
	{	i=(*rectpp)->i; j=(*rectpp)->j;
		cc[0]=screen_col[col*i+j]; cc[1]=screen_row[col*i+j];
		cc[2]=screen_col[col*(i+1)+j]; cc[3]=screen_row[col*(i+1)+j];
		cc[4]=screen_col[col*(i+1)+j+1]; cc[5]=screen_row[col*(i+1)+(j+1)];
		cc[6]=screen_col[col*i+j+1]; cc[7]=screen_row[col*i+j+1];
		if (!twosides ||
			((cc[2]-cc[0])*(cc[5]-cc[1])-(cc[3]-cc[1])*(cc[4]-cc[0]))>0)
			gfill(cc,fill_blank,4,connected);
		else gfill(cc,fill_filled,4,connected);
		if (test_key()==escape)
		{	output("User interrupted!\n");
			error=1; gflush(); return;
		}
		rectpp++;
	}
	hd=new_real(0.0,"");
	moveresult(st,hd);
	gflush();
}

static void hcontour (double val, int n, int m, 
	double x[], double r[], double c[])
/**** hcontour
	helping function to contour.
****/
{	double f1,f2;
	if ((val>=x[n] && val<=x[n+1]) || (val>=x[n+1] && val<=x[n]))
	if ((val>=x[m] && val<=x[m+1]) || (val>=x[m+1] && val<=x[m]))
	{	if (x[n+1]==x[n]) f1=0;
		else f1=(val-x[n])/(x[n+1]-x[n]);
		if (x[m+1]==x[m]) f2=0;
		else f2=(val-x[m])/(x[m+1]-x[m]);
		gline((c[n]+f1*(c[n+1]-c[n])),
			(r[n]+f1*(r[n+1]-r[n])),
			(c[m]+f2*(c[m+1]-c[m])),
			(r[m]+f2*(r[m+1]-r[m])),
			linecolor,line_solid,linewidth);
	}
}

static void contour (double x[], int i, int j, int rows, int cols, 
	double v[], int nv)
/***** contour
	x1 is lower left edge, x2 upper left, x3 upper right, x4 lower
	right value at a square. 
	does contour plot of the nv values in v.
	r and c is needed to compute the position of the square.
*****/
{	int k,n,m;
	double sr[5],sc[5];
	double val;
	sr[4]=sr[0]=sr[3]=(lowerr-((long)i*(lowerr-upperr))/cols);
	sr[1]=sr[2]=(lowerr-((long)(i+1)*(lowerr-upperr))/cols);
	sc[4]=sc[0]=sc[1]=(upperc+((long)j*(lowerc-upperc))/rows);
	sc[2]=sc[3]=(upperc+((long)(j+1)*(lowerc-upperc))/rows);
	for (k=0; k<nv; k++)
	{	val=v[k];
		for (n=0; n<3; n++)
			for (m=n+1; m<4; m++)
				hcontour(val,n,m,x,sr,sc);
	}
}

void mcontour (header *hd)
/***** mcontour
	contour plot with matrix and vector intput.
*****/
{	header *st=hd,*result,*hd1;
	double *m,*mv,x[5];
	int r,c,cv,dummy,i,j;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->c<2 || dimsof(hd)->r<2)
	{	output("Contour needs a real matrix!\n"); error=81; return;
	}
	hd1=next_param(st); if (error) return;
	hd1=getvalue(hd1); if (error) return;
	if (hd1->type!=s_real)
		if (hd1->type!=s_matrix || dimsof(hd1)->r!=1)
		{	output("Second parameter of contour must be a vector!\n");
			error=82; return;
		}
	getmatrix(hd,&r,&c,&m); getmatrix(hd1,&dummy,&cv,&mv);
	graphic_mode();
	if (!holding) gclear();
	frame();
	for (i=0; i<r-1; i++)
	{	for (j=0; j<c-1; j++)
		{	x[0]=*mat(m,c,i,j); x[1]=*mat(m,c,i+1,j);
			x[2]=*mat(m,c,i+1,j+1); x[3]=*mat(m,c,i,j+1);
			x[4]=x[0];
			contour(x,i,j,c-1,r-1,mv,cv);
		}
		if (test_key()==escape)
		{	output("User interrupted!\n");
			error=1; gflush(); return;
		}
	}
	result=new_real(cv,"");
	moveresult(st,result);
	gflush();
}

void mdensity (header *hd)
/***** mcontour
	density plot with matrix input.
*****/
{	header *st=hd,*result;
	double *m,x,deltax,deltay;
	int r,c,i,j;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->c<2 || dimsof(hd)->r<2)
	{	output("Density needs a real matrix!\n"); error=81; return;
	}
	getmatrix(hd,&r,&c,&m);
	graphic_mode();
	if (!holding) gclear();
	if (antialiasing) {
		deltax=(double)(lowerc-upperc)/(c-1);
		deltay=(double)(lowerr-upperr)/(r-1);
		for (i=0; i<r-1; i++)
		{	for (j=0; j<c-1; j++)
			{
				x=((*mat(m,c,i,j))+(*mat(m,c,i+1,j))+(*mat(m,c,i+1,j+1))+(*mat(m,c,i,j+1)))/4;
				gbar(
					upperc+j*deltax,lowerr-(i+1)*deltay,
					upperc+(j+1)*deltax,lowerr-i*deltay,
					x,densitycolor,dgrid);
			}
			if (test_key()==escape)
			{   output("User interrupted!\n");
				error=1; gflush(); return;
			}
		}
	} else {
		deltax=(double)(lowerc-upperc)/(c);
		deltay=(double)(lowerr-upperr)/(r);
		for (i=0; i<r; i++)
		{	for (j=0; j<c; j++)
			{
				gbar(
					upperc+j*deltax,lowerr-(i+1)*deltay,
					upperc+(j+1)*deltax,lowerr-i*deltay,
					*mat(m,c,i,j),densitycolor,dgrid);
			}
			if (test_key()==escape)
			{   output("User interrupted!\n");
				error=1; gflush(); return;
			}
		}
	}
	result=new_real(0,"");
	moveresult(st,result);
	gflush();
}

void mview (header *hd)
{	double *m;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=4)
	{	output("Arguments for view are [dist tele alpha beta]!\n");
		error=90; return;
	}
	m=matrixof(hd);
	distance=*m++;
	tele=*m++;
	a_left=*m++;
	a_up=*m;
}

void mwindow (header *hd)
{	double *m;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=4)
	{	output("Arguments for window are [c0 r0 c1 r1]!\n");
		error=90; return;
	}
	m=matrixof(hd);
	upperc=(int)(*m++);
	upperr=(int)(*m++);
	lowerc=(int)(*m++);
	lowerr=(int)(*m);
	if (lowerr<upperr) lowerr=upperr+1;
	if (lowerc<upperc) lowerc=upperc+1;
	newframe=1; scaling=1;
}

void mclip (header *hd)
{	double *m;
	header *stack=hd, *oldclip;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=4)
	{	output("Arguments for clip are [c0 r0 c1 r1]!\n");
		error=90; return;
	}
	oldclip = new_matrix(1,4,""); if (error) return;
	m=matrixof(oldclip);
	*m++=upperclipc;
	*m++=upperclipr;
	*m++=lowerclipc;
	*m=lowerclipr;
	m=matrixof(hd);
	upperclipc=(int)(*m++);
	upperclipr=(int)(*m++);
	lowerclipc=(int)(*m++);
	lowerclipr=(int)(*m);
	if (upperclipc<0) upperclipc = 0;
	if (upperclipr<0) upperclipr = 0;
	if (lowerclipc>1023) lowerclipc = 1023;
	if (lowerclipc>1023) lowerclipc = 1023;
	if (lowerclipr<upperclipr) lowerclipr=upperclipr+1;
	if (lowerclipc<upperclipc) lowerclipc=upperclipc+1;
	gclip(upperclipc,upperclipr,lowerclipc,lowerclipr);
	moveresult(stack,oldclip);
}

void mclip0 (header *hd)
{	double *m;
	hd=new_matrix(1,4,""); if (error) return;
	m=matrixof(hd);
	*m++=upperclipc;
	*m++=upperclipr;
	*m++=lowerclipc;
	*m=lowerclipr;
}

void mpswindow (header *hd)
{	double *m;
	double c,r;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=2)
	{	output("Arguments for pswindow are [c r]!\n");
		error=90; return;
	}
	m=matrixof(hd);
	c=*m++;
	r=*m;
	if (c<1) c=1;
	if (r<1) r=1;
    pswindow(c,r);
}

void mcolor (header *hd)
{   header *st=hd;
	int old=linecolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for color must be real!\n");
		error=90; return;
	}
	linecolor=(int)*realof(hd);
	moveresult(st,new_real(old,""));
}

void mfcolor (header *hd)
{   header *st=hd;
	int old=framecolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for framecolor must be real!\n");
		error=90; return;
	}
	framecolor=(int)*realof(hd);
	if (framecolor<0 || framecolor>=16) framecolor=1;
	moveresult(st,new_real(old,""));
}

int fillcolor1=11, fillcolor2=3;

void mfillcolor (header *hd)
{   header *st=hd,*res;
	int o1=fillcolor1,o2=fillcolor2;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=2)
	{	output("Argument for fillcolor must be a 1x2 vector!\n");
		error=90; return;
	}
	fillcolor1=(int)*matrixof(hd);
	fillcolor2=(int)*(matrixof(hd)+1);
	if (fillcolor1<0 || fillcolor1>15) fillcolor1=0;
	if (fillcolor2<0 || fillcolor2>15) fillcolor2=0;
	res=new_matrix(1,2,""); if (error) return;
	*matrixof(res)=o1; *(matrixof(res)+1)=o2;
	moveresult(st,res);
}

int markerfactor=100;

void mmarkersize (header *hd)
{   header *st=hd;
	int old,markersize;
	if (markerfactor>0) old=1024/markerfactor;
	else old=0;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for markersize must be integer!\n");
		error=90; return;
	}
	markersize=(int)*realof(hd);
	moveresult(st,new_real(old,""));
	if (markersize>0) markerfactor=1024/markersize;
	else markerfactor=0;
}

void mwcolor (header *hd)
{   header *st=hd;
	int old=wirecolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for wirecolor must be integer!\n");
		error=90; return;
	}
	wirecolor=(int)*realof(hd);
	if (wirecolor<0 || wirecolor>=16) wirecolor=1;
	moveresult(st,new_real(old,""));
}

void mtcolor (header *hd)
{   header *st=hd;
	int old=textcolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for textcolor must be integer!\n");
		error=90; return;
	}
	textcolor=(int)*realof(hd);
	if (textcolor<0 || textcolor>=16) textcolor=1;
	moveresult(st,new_real(old,""));
}

void mdcolor (header *hd)
{   header *st=hd;
	int old=densitycolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for densitycolor must be integer!\n");
		error=90; return;
	}
	densitycolor=(int)*realof(hd);
	if (densitycolor<0 || densitycolor>=16) densitycolor=1;
	moveresult(st,new_real(old,""));
}

void mdgrid (header *hd)
{   header *st=hd;
	int old=dgrid;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for densitygrid must be integer!\n");
		error=90; return;
	}
	dgrid=(int)*realof(hd);
	moveresult(st,new_real(old,""));
}

void mstyle (header *hd)
{	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_string)
	{	output("Argument style must be a string!\n");
		error=90; return;
	}
	if (!strcmp(stringof(hd),"i")) linetype=line_none;
	else if (!strcmp(stringof(hd),"-")) linetype=line_solid;
	else if (!strcmp(stringof(hd),".")) linetype=line_dotted;
	else if (!strcmp(stringof(hd),"--")) linetype=line_dashed;
	else if (!strcmp(stringof(hd),"->")) linetype=line_arrow;
	else if (!strcmp(stringof(hd),"mx")) markertype=marker_cross;
	else if (!strcmp(stringof(hd),"mo")) markertype=marker_circle;
	else if (!strcmp(stringof(hd),"m<>")) markertype=marker_diamond;
	else if (!strcmp(stringof(hd),"m.")) markertype=marker_dot;
	else if (!strcmp(stringof(hd),"m+")) markertype=marker_plus;
	else if (!strcmp(stringof(hd),"m[]")) markertype=marker_square;
	else if (!strcmp(stringof(hd),"m*")) markertype=marker_star;
	else if (!strcmp(stringof(hd),"b/")) bartype=bar_diagonal1;
	else if (!strcmp(stringof(hd),"b\\")) bartype=bar_diagonal2;
	else if (!strcmp(stringof(hd),"bO")) bartype=bar_frame;
	else if (!strcmp(stringof(hd),"b#")) bartype=bar_solid;
	else if (!strcmp(stringof(hd),"bO#")) bartype=bar_framed;
	else if (!strcmp(stringof(hd),"b#O")) bartype=bar_framed;
	else if (!strcmp(stringof(hd),"b|")) bartype=bar_vhatch;
	else if (!strcmp(stringof(hd),"b-")) bartype=bar_hhatch;
	else if (!strcmp(stringof(hd),"b\\/")) bartype=bar_cross;
	else if (!strcmp(stringof(hd),"b/\\")) bartype=bar_cross;
	else
	{
		markertype=marker_cross; linetype=line_solid;
		bartype=bar_framed;
	}
}

void mmstyle (header *hd)
{	header *st=hd,*res;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_string)
	{	output("Argument for markerstyle must be a string!\n");
		error=90; return;
	}
	switch (markertype)
	{	case marker_cross : res=new_string("x",8,""); break;
		case marker_circle : res=new_string("o",8,""); break;
		case marker_diamond : res=new_string("<>",8,""); break;
		case marker_dot : res=new_string(".",8,""); break;
		case marker_plus : res=new_string("+",8,""); break;
		case marker_square : res=new_string("[]",8,""); break;
		case marker_star : res=new_string("*",8,""); break;
		default : res=new_string("",8,"");
	}
	if (!strcmp(stringof(hd),"x")) markertype=marker_cross;
	else if (!strcmp(stringof(hd),"o")) markertype=marker_circle;
	else if (!strcmp(stringof(hd),"<>")) markertype=marker_diamond;
	else if (!strcmp(stringof(hd),".")) markertype=marker_dot;
	else if (!strcmp(stringof(hd),"+")) markertype=marker_plus;
	else if (!strcmp(stringof(hd),"[]")) markertype=marker_square;
	else if (!strcmp(stringof(hd),"*")) markertype=marker_star;
	moveresult(st,res);
}

void mbarstyle (header *hd)
{	header *st=hd,*res;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_string)
	{	output("Argument for barstyle must be a string!\n");
		error=90; return;
	}
	switch (bartype)
	{	case bar_solid : res=new_string("#",8,""); break;
		case bar_framed : res=new_string("#O",8,""); break;
		case bar_frame : res=new_string("O",8,""); break;
		case bar_vhatch : res=new_string("|",8,""); break;
		case bar_hhatch : res=new_string("-",8,""); break;
		case bar_diagonal1 : res=new_string("/",8,""); break;
		case bar_diagonal2 : res=new_string("\\",8,""); break;
		case bar_cross : res=new_string("\\/",8,""); break;
		default : res=new_string("",8,"");
	}
	if (!strcmp(stringof(hd),"/")) bartype=bar_diagonal1;
	else if (!strcmp(stringof(hd),"\\")) bartype=bar_diagonal2;
	else if (!strcmp(stringof(hd),"O")) bartype=bar_frame;
	else if (!strcmp(stringof(hd),"#")) bartype=bar_solid;
	else if (!strcmp(stringof(hd),"O#")) bartype=bar_framed;
	else if (!strcmp(stringof(hd),"#O")) bartype=bar_framed;
	else if (!strcmp(stringof(hd),"|")) bartype=bar_vhatch;
	else if (!strcmp(stringof(hd),"-")) bartype=bar_hhatch;
	else if (!strcmp(stringof(hd),"\\/")) bartype=bar_cross;
	else if (!strcmp(stringof(hd),"/\\")) bartype=bar_cross;
	moveresult(st,res);
}

void mlstyle (header *hd)
{	header *st=hd,*res;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_string)
	{	output("Argument for linestyle must be string!\n");
		error=90; return;
	}
	switch (linetype)
	{	case line_none : res=new_string("i",8,""); break;
		case line_solid : res=new_string("-",8,""); break;
		case line_dotted : res=new_string(".",8,""); break;
		case line_dashed : res=new_string("--",8,""); break;
		case line_arrow : res=new_string("->",8,""); break;
		default : res=new_string("",8,"");
	}
	if (!strcmp(stringof(hd),"i")) linetype=line_none;
	else if (!strcmp(stringof(hd),"-")) linetype=line_solid;
	else if (!strcmp(stringof(hd),".")) linetype=line_dotted;
	else if (!strcmp(stringof(hd),"--")) linetype=line_dashed;
	else if (!strcmp(stringof(hd),"->")) linetype=line_arrow;
	moveresult(st,res);
}

void mlinew (header *hd)
{	header *st=hd,*res;
	int h,old=linewidth;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for linewidth must be a real!");
		error=90; return;
	}
	if ((h=(int)*realof(hd))!=0) linewidth=h;
	res=new_real(old,"");
	moveresult(st,res);
}

static void mtext1 (header *hd, int flag)
{	header *hd1;
	hd1=next_param(hd);
	hd=getvalue(hd); if (error) return;
	if (hd1)  hd1=getvalue(hd1); if (error) return;
	if (hd->type!=s_string || hd1->type!=s_matrix ||
		dimsof(hd1)->r!=1 || dimsof(hd1)->c!=2)
	{	output("Need a string and a vector [x y]!\n");
		error=91; return;
	}
	graphic_mode();
	gtext((int)*matrixof(hd1),(int)*(matrixof(hd1)+1),
		stringof(hd),textcolor,flag);
	gflush();
}

void mctext (header *hd)
{   mtext1(hd,1);
}

void mrtext (header *hd)
{   mtext1(hd,2);
}

void mtext (header *hd)
{   mtext1(hd,0);
}

static void mvtext1 (header *hd, int flag)
{	header *hd1;
	hd1=next_param(hd);
	hd=getvalue(hd); if (error) return;
	if (hd1)  hd1=getvalue(hd1); if (error) return;
	if (hd->type!=s_string || hd1->type!=s_matrix ||
		dimsof(hd1)->r!=1 || dimsof(hd1)->c!=2)
	{	output("Need a string and a vector [x y]!\n");
		error=91; return;
	}
	graphic_mode();
	gvtext((int)*matrixof(hd1),(int)*(matrixof(hd1)+1),
		stringof(hd),textcolor,flag);
	gflush();
}

void mvtext (header *hd)
{	mvtext1(hd,0);
}

void mvctext (header *hd)
{	mvtext1(hd,1);
}

void mvrtext (header *hd)
{	mvtext1(hd,2);
}

static void mvutext1 (header *hd, int flag)
{	header *hd1;
	hd1=next_param(hd);
	hd=getvalue(hd); if (error) return;
	if (hd1)  hd1=getvalue(hd1); if (error) return;
	if (hd->type!=s_string || hd1->type!=s_matrix ||
		dimsof(hd1)->r!=1 || dimsof(hd1)->c!=2)
	{	output("Need a string and a vector [x y]!\n");
		error=91; return;
	}
	graphic_mode();
	gvutext((int)*matrixof(hd1),(int)*(matrixof(hd1)+1),
		stringof(hd),textcolor,flag);
	gflush();
}

void mvutext (header *hd)
{	mvutext1(hd,0);
}

void mvcutext (header *hd)
{	mvutext1(hd,1);
}

void mvrutext (header *hd)
{	mvutext1(hd,2);
}

void mbar (header *hd)
{	header *st=hd,*result;
	double *m,x,y,w,h;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=4)
	{	output("Bar needs a 1x4 vector!\n");
		error=1; return;
	}
	m=matrixof(hd);
	x=*m; y=*(m+1); w=*(m+2); h=*(m+3);
	w+=x; h+=y;
	graphic_mode();
	gbar1(x,y,w,h,barcolor,bartype);
	result=new_string("",2,"");
	moveresult(st,result);
}

void mbarcolor (header *hd)
{   header *st=hd;
	int old=barcolor;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for textcolor must be integer!\n");
		error=90; return;
	}
	barcolor=(int)*realof(hd);
	if (barcolor<0 || barcolor>=16) barcolor=3;
	moveresult(st,new_real(old,""));
}

void msetplot (header *hd)
{	header *st=hd,*result;
	double *m;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_matrix || dimsof(hd)->r!=1 || dimsof(hd)->c!=4)
	{	output("Setplot needs a 1x4 vector!\n");
		error=2200; return;
	}
	result=new_matrix(1,4,""); if (error) return;
	m=matrixof(result);
	*m++=x_min; *m++=x_max; *m++=y_min; *m=y_max;
	m=matrixof(hd);
	x_min=*m++; x_max=*m++; y_min=*m++; y_max=*m;
	if (x_max>DBL_MAX) x_max=DBL_MAX;
	if (x_min<-DBL_MAX) x_min=-DBL_MAX;
	if (y_max>DBL_MAX) y_max=DBL_MAX;
	if (y_min<-DBL_MAX) y_min=-DBL_MAX;
	if (x_min>=x_max) x_min=x_max+1;
	if (y_min>=y_max) y_min=y_max+1;
	moveresult(st,result);
	scaling=0;
}

void mholding (header *hd)
{	header *st=hd,*result;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Holding needs a 1 or 0!\n");
		error=2201; return;
	}
	result=new_real(holding,"");
	holding=(*realof(hd)!=0.0); scaling=!holding;
	moveresult(st,result);
}

void mkeepsquare (header *hd)
{	header *st=hd,*result;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Keepsquare needs a 1 or 0!\n");
		error=2201; return;
	}
	result=new_real(keepsquare,"");
	keepsquare=(*realof(hd)!=0.0);
	moveresult(st,result);
}

void mscaling (header *hd)
{	header *st=hd,*result;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Scaling needs a 1 or 0!\n");
		error=2201; return;
	}
	result=new_real(scaling,"");
	scaling=(*realof(hd)!=0.0);
	moveresult(st,result);
}

void mtwosides (header *hd)
{	header *st=hd,*result;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Twosides needs a 1 or 0!\n");
		error=2201; return;
	}
	result=new_real(twosides,"");
	twosides=(*realof(hd)!=0.0);
	moveresult(st,result);
}

void mscale (header *hd)
{	hd=getvalue(hd);
	if (hd->type!=s_real)
	{	output("Scale needs a real!\n"); error=150; return;
	}
	gscale(*realof(hd));
}

void mmeshfactor (header *hd)
{	double oldfactor=meshfactor;
	hd=getvalue(hd);
	if (hd->type!=s_real)
	{	output("Meshfactor needs a real!\n"); error=150; return;
	}
	meshfactor=*realof(hd);
	if (meshfactor<0) meshfactor=0;
	if (meshfactor>1) meshfactor=1;
	*realof(hd)=oldfactor;
}

void mtextsize (header *hd)
{	header *result;
	result=new_matrix(1,2,""); if (error) return;
	*matrixof(result)=getmetacharwidth();
	*(matrixof(result)+1)=getmetacharheight();
}

void mmouse (header *hd)
{	header *result;
	double c,r;
	double *m;
	graphic_mode();
	mouse(&c,&r);
	if (c<0 && r<0) new_real(0,"");
	else
	{	result=new_matrix(1,2,""); if (error) return;
		m=matrixof(result);
		*m++=x_min+(c-upperc)/(double)(lowerc-upperc)*(x_max-x_min);
		*m=y_max-(r-upperr)/(double)(lowerr-upperr)*(y_max-y_min);
	}
}

void mholding0 (header *hd)
{	new_real(holding,"");
}

void mplot1 (header *hd)
{	header *result;
	double *x;
	result=new_matrix(1,4,""); if (error) return;
	x=matrixof(result);
	*x++=x_min; *x++=x_max; *x++=y_min; *x=y_max;
}

void mview0 (header *hd)
{	header *result;
	double *m;
	result=new_matrix(1,4,""); if (error) return;
	m=matrixof(result);
	*m++=distance;
	*m++=tele;
	*m++=a_left;
	*m=a_up;
}

void mwindow0 (header *hd)
{	double *m;
	hd=new_matrix(1,4,""); if (error) return;
	m=matrixof(hd);
	*m++=upperc;
	*m++=upperr;
	*m++=lowerc;
	*m=lowerr;
}

void mframe (header *hd)
{	graphic_mode();
	frame();
	new_real(0,"");
}

void mantialiasing (header *hd)
{   header *st=hd;
	int old=antialiasing;
	hd=getvalue(hd); if (error) return;
	if (hd->type!=s_real)
	{	output("Argument for antialiasing must be real!\n");
		error=90; return;
	}
	antialiasing=(int)*realof(hd);
	moveresult(st,new_real(old,""));
}

void mantialiasing0 (header *hd)
{
	new_real(antialiasing,"");
}
