/* Steven Andrews, 8/25/02 */
/* See documentation called Plot3D doc */
/* Copyright 2003 by Steven Andrews.  Permission is granted
   for non-commercial use of and modifications to the code. */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "DiskIO.h"
#include "Plot.h"
#include "Plot3D.h"
#include "Rn.h"

float *p2o3D,*p2s3D,*work3D1,*work3D2;
int stereo3D;
float *penpos3Ds;
float dist3D,sep3D;


int Init3DPlot(int stereo,float dist,float sep) {
	float alpha;

	p2o3D=allocM(3,3);
	p2s3D=allocM(3,3);
	work3D1=allocM(3,3);
	work3D2=allocM(3,3);
	penpos3Ds=allocV(2);
	if(!p2o3D||!p2s3D||!work3D1||!work3D2||!penpos3Ds) return 1;
	InitPlot();
	stereo3D=stereo;
	dist3D=dist;
	sep3D=sep;
	if(stereo) SetScales(-1,3,-1,1);
	else SetScales(-1,1,-1,1);
	setstdM(p2o3D,3,3,1);
	setstdM(p2s3D,3,3,1);
	if(stereo) {
		alpha=atan(sep/(2*dist));
		Rotate3D(29,2*alpha);
		setstdM(p2s3D,3,3,1);
		Rotate3D(28,alpha); }
	setstdV(penpos3Ds,2,0);
	return 0; }

Pt3Dptr Pt3DAlloc() {
	Pt3Dptr pt;

	pt=(Pt3Dptr) malloc(sizeof(struct Point3D));
	if(!pt) return NULL;
	pt->r[0]=pt->r[1]=pt->r[2]=0;
	pt->p[0]=pt->p[1]=pt->p[2]=0;
	pt->o[0]=pt->o[1]=pt->o[2]=0;
	pt->s[0]=pt->s[1]=pt->s[2]=0;
	pt->r2p=NULL;
	return pt; }

void Pt3DFree(Pt3Dptr pt) {
	if(pt) free(pt);
	return; }

void PlotPt3D(Pt3Dptr pt) {
	PlotPt(pt->o[0],pt->o[1]);
	if(stereo3D) {
		PlotPt(pt->s[0]+2,pt->s[1]);
		penpos3Ds[0]=pt->s[0]+2;
		penpos3Ds[1]=pt->s[1];
		PlotMove(pt->o[0],pt->o[1]); }
	return; }

void PlotLine3D(Pt3Dptr pt) {
	PlotLine(pt->o[0],pt->o[1]);
	if(stereo3D) {
		PlotMove(penpos3Ds[0],penpos3Ds[1]);
		PlotLine(pt->s[0]+2,pt->s[1]);
		penpos3Ds[0]=pt->s[0]+2;
		penpos3Ds[1]=pt->s[1];
		PlotMove(pt->o[0],pt->o[1]); }
	return; }

void PlotStr3D(Pt3Dptr pt,char *s) {
	PlotStr(pt->o[0],pt->o[1],s);
	if(stereo3D) {
		PlotStr(pt->s[0]+2,pt->s[1],s);
		penpos3Ds[0]=pt->s[0]+2;
		penpos3Ds[1]=pt->s[1];
		PlotMove(pt->o[0],pt->o[1]); }
	return; }

void Rotate3D(char c,float alpha) {
	float *m,*temp;

	if(!c) return;
	m=work3D1;
	setstdM(m,3,3,1);
	if(c==31) {m[4]=m[8]=cos(alpha);m[5]=-(m[7]=sin(alpha)); }
	else if(c==30) {m[4]=m[8]=cos(alpha);m[5]=-(m[7]=-sin(alpha)); }
	else if(c==28) {m[0]=m[8]=cos(alpha);m[2]=-(m[6]=sin(alpha)); }
	else if(c==29) {m[0]=m[8]=cos(alpha);m[2]=-(m[6]=-sin(alpha)); }
	else if(c=='.') {m[0]=m[4]=cos(alpha);m[1]=-(m[3]=sin(alpha)); }
	else if(c=='/') {m[0]=m[4]=cos(alpha);m[1]=-(m[3]=-sin(alpha)); }
	else return;
	dotMM(m,p2o3D,work3D2,3,3,3);
	temp=p2o3D;p2o3D=work3D2;work3D2=temp;
	if(stereo3D) {
		dotMM(m,p2s3D,work3D2,3,3,3);
		temp=p2s3D;p2s3D=work3D2;work3D2=temp; }
	return;	}

void UpdatePt3D(Pt3Dptr pt,int internal) {
	if(internal&&pt->r2p) dotVM(pt->r,pt->r2p,pt->p,3,3);
	dotMV(p2o3D,pt->p,pt->o,3,3);
	pt->o[0]*=dist3D/(dist3D-pt->o[2]);
	pt->o[1]*=dist3D/(dist3D-pt->o[2]);
	if(stereo3D) {
		dotMV(p2s3D,pt->p,pt->s,3,3);
		pt->s[0]*=dist3D/(dist3D-pt->s[2]);
		pt->s[1]*=dist3D/(dist3D-pt->s[2]); }
	return; }


Draw3Dptr Draw3DAlloc(int max,float *r2p) {
	Draw3Dptr dr;
	Pt3Dptr pt;
	int i;
	
	dr=(Draw3Dptr) malloc(sizeof(struct Drawing3D));
	if(!dr) return NULL;
	dr->max=max;
	dr->n=0;
	dr->pt=NULL;
	dr->col=NULL;
	dr->line=NULL;
	dr->r2p=r2p;
	dr->pt=(Pt3Dptr *) calloc(sizeof(Pt3Dptr),max);
	if(dr->pt) for(i=0;i<max;i++) dr->pt[i]=NULL;
	dr->col=(char *) calloc(sizeof(char),max);
	dr->line=(int *) calloc(sizeof(int),max);
	if(!dr->pt||!dr->col||!dr->line) {Draw3DFree(dr);return NULL;}
	for(i=0;i<max;i++) {
		pt=dr->pt[i]=Pt3DAlloc();
		if(!pt) {Draw3DFree(dr);return NULL;}
		pt->r[0]=pt->r[1]=pt->r[2]=0;
		pt->p[0]=pt->p[1]=pt->p[2]=0;
		pt->o[0]=pt->o[1]=pt->o[2]=0;
		pt->s[0]=pt->s[1]=pt->s[2]=0;
		pt->r2p=r2p;
		dr->col[i]='0';
		dr->line[i]=0; }
	return dr; }

void Draw3DFree(Draw3Dptr dr) {
	int i;

	if(!dr) return;
	if(dr->pt) for(i=0;i<dr->max;i++) Pt3DFree(dr->pt[i]);	
	if(dr->pt) free(dr->pt);
	if(dr->col) free(dr->col);
	if(dr->line) free(dr->line);
	free(dr);
	return; }

int DrawingPt3D(Draw3Dptr dr,float x,float y,float z,int rel,int line,int col) {
	int n;
	Pt3Dptr pt;

	if(dr->n==dr->max) return 1;
	n=dr->n++;
	pt=dr->pt[n];
	if(rel) {pt->r[0]=x;pt->r[1]=y;pt->r[2]=z;}
	else {pt->p[0]=x;pt->p[1]=y;pt->p[2]=z;}
	pt->r2p=dr->r2p;
	dr->col[n]=col;
	dr->line[n]=line;
	return; }

void PlotDraw3D(Draw3Dptr dr) {
	int i;

	for(i=0;i<dr->n;i++) {
		SetColor(dr->col[i]);
		if(dr->line[i]) PlotLine3D(dr->pt[i]);
		else PlotPt3D(dr->pt[i]); }
	return; }

void UpdateDraw3D(Draw3Dptr dr,int internal) {
	int i;
	
	for(i=0;i<dr->n;i++) UpdatePt3D(dr->pt[i],internal);
	return; }

float *Draw3DOut(Draw3Dptr dr,char c) {
	int i,j;
	float *a;
	
	a=allocM(dr->n,3);
	if(!a) return NULL;
	for(i=0;i<dr->n;i++)
		for(j=0;j<3;j++)
			if(c=='r') a[3*i+j]=dr->pt[i]->r[j];
			else if(c=='p') a[3*i+j]=dr->pt[i]->p[j];
			else if(c=='s') a[3*i+j]=dr->pt[i]->s[j];
			else a[3*i+j]=dr->pt[i]->o[j];
	return a; }

int Draw3D2ps(Draw3Dptr dr,int zdiv,char *name,int append) {
	FILE *fptr;
	char str[256];
	Pt3Dptr *pt;
	int nmfre,er,i,div,divcur;
	float zmin,zmax;
	
	er=GetName(&name,&nmfre);
	if(er) return er;
	if(!strcmp(name,"stdout")) fptr=stdout;
	else if(append==1) fptr=fopen(name,"a");
	else if(append==2) fptr=fopen(name,"w");
	else {
		fptr=fopen(name,"r");
		if(fptr) {
			fclose(fptr);
			fprintf(stderr,"File exists. Do you want to overwrite it? ");
			scanf("%s",str);
			if(str[0]!='y'&&str[0]!='Y') {
				if(nmfre) free(name);
				return 2;}}
		fptr=fopen(name,"w");}
	if(nmfre) free(name);
	if(!fptr) return 3;

	pt=dr->pt;
/*	zmin=zmax=pt[0]->o[2];
	for(i=0;i<dr->n;i++) {
		if(pt[i]->o[2]<zmin) zmin=pt[i]->o[2];
		else if(pt[i]->o[2]>zmax) zmax=pt[i]->o[2]; }*/
	zmin=-1;
	zmax=1;

	fprintf(fptr,"%!\n");
	fprintf(fptr,"%%%% Postscript file produced by Plot3D.c\n");
	fprintf(fptr,"/unit {250 mul} def\n");
	div=divcur=floor((pt[0]->o[2]-zmin)/(zmax-zmin)*zdiv);
	if(div<0) div=0;
	else if(div>=zdiv) div=zdiv-1;
	fprintf(fptr,"newpath\n");
	fprintf(fptr,"%f setlinewidth\n",0.5*(1.0-1.0*div/zdiv)-0.3);
	fprintf(fptr,"%f unit %f unit moveto\n",3+pt[0]->o[0],6+pt[0]->o[1]);
	for(i=1;i<dr->n;i++) {
		div=floor((zmax-pt[i]->o[2])/(zmax-zmin)*zdiv);
		if(div<0) div=0;
		else if(div>=zdiv) div=zdiv-1;
		if(div==divcur&&dr->line[i])
			fprintf(fptr,"%f unit %f unit lineto\n",3+pt[i]->o[0],6+pt[i]->o[1]);
		else {
			if(dr->line[i])
				fprintf(fptr,"%f unit %f unit lineto\n",3+pt[i]->o[0],6+pt[i]->o[1]);
			fprintf(fptr,"stroke\n");
			fprintf(fptr,"newpath\n");
			fprintf(fptr,"%f setlinewidth\n",(1.0-1.0*div/zdiv)-0.3);
			divcur=div;
			fprintf(fptr,"%f unit %f unit moveto\n",3+pt[i]->o[0],6+pt[i]->o[1]); }}
	fprintf(fptr,"stroke\n");
	fprintf(fptr,"showpage\n");
	if(fptr!=stdout) fclose(fptr);
	return er; }








