/* Steven Andrews, version 1 written 10/31/90; modified 8/6/96	*/
/* Modified to work with MetroWerks C and on a color computer 9/9/98	*/
/* See documentation called Plot doc */
/* Copyright 2003 by Steven Andrews.  Permission is granted
   for non-commercial use of and modifications to the code. */

#include <Types.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Menus.h>
#include <Windows.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <OSUtils.h>
#include <ToolUtils.h>
#include <SegLoad.h>
#include <Sound.h>

#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <math.h>
#include "Plot.h"
#include "random.h"
#include "string2.h"

float xmin=-10.0,xmax=10.0,ymin=-10.0,ymax=10.0;
WindowPtr output;
Rect wBounds;

void InitPlot() {
	#if TARGET_API_MAC_CARBON
		InitCursor();
	#else
		OSErr error;
		SysEnvRec theWorld;
		
		error=SysEnvirons(1,&theWorld);
		if(theWorld.hasColorQD==false)	{
			SysBeep(50);
			ExitToShell(); }
		InitGraf(&qd.thePort);
		InitFonts();
		InitWindows();
		InitMenus();
		TEInit();
		InitDialogs(nil);
		InitCursor();
	#endif
	return; }

void EndPlot() {
	EndUpdate(output); }

void MakeWindow(float left,float right,float top,float bottom) {
	#if TARGET_API_MAC_CARBON
		BitMap theScreenBits;
	#endif
	Rect r;
	
	#if TARGET_API_MAC_CARBON
		GetQDGlobalsScreenBits(&theScreenBits);
		r=theScreenBits.bounds;
	#else
		r=qd.screenBits.bounds;
	#endif
	wBounds.left=(r.right-r.left)*left+r.left;
	wBounds.right=(r.right-r.left)*right+r.left;
	wBounds.top=(r.bottom-r.top)*top+r.top;
	wBounds.bottom=(r.bottom-r.top)*bottom+r.top;
	output=NewCWindow(nil,&wBounds,"\pOutput",true,documentProc,(WindowPtr)(-1),false,0);
	#if TARGET_API_MAC_CARBON
		SetPortWindowPort(output);
	#else
		SetPort(output);
	#endif
	BeginUpdate(output);
	return; }

void KillWindow() {
	DisposeWindow(output); }

void InvalPlot() {
	struct Rect r;

	#if TARGET_API_MAC_CARBON
		GetWindowPortBounds(output,&r);
		InvalWindowRect(output,&r);
	#else
		r=(*qd.thePort).portRect;
		InvalRect(&r);
	#endif
	return; }

void SetScales(float xa,float xb,float ya,float yb) {
	if(xa>=-FLT_MAX&&xa<=FLT_MAX) xmin=xa;
	if(xb>=-FLT_MAX&&xb<=FLT_MAX) xmax=xb;
	if(ya>=-FLT_MAX&&ya<=FLT_MAX) ymin=ya;
	if(yb>=-FLT_MAX&&yb<=FLT_MAX) ymax=yb; }

void GetScales(float *xa,float *xb,float *ya,float *yb) {
	*xa=xmin;
	*xb=xmax;
	*ya=ymin;
	*yb=ymax; }

void ScalePtrs(float **xa,float **xb,float **ya,float **yb) {
	*xa=&xmin;
	*xb=&xmax;
	*ya=&ymin;
	*yb=&ymax; }

void SetColor(char c) {
	RGBColor x;
	
	if(c=='A'||c=='a')	{x.red=0x7000;x.green=0xdb00;x.blue=0x9300;}							/* aqua			*/
	else if(c=='B'||c=='b'||c=='6')	{x.red=0x0000;x.green=0x0000;x.blue=0xffff;}	/* blue			*/
	else if(c=='C'||c=='c')	{x.red=0x0000;x.green=0xffff;x.blue=0xffff;}					/* cyan			*/
	else if(c=='D'||c=='d')	{x.red=0x8000;x.green=0x0000;x.blue=0x0000;}					/* dark red	*/
	else if(c=='E'||c=='e'||c=='8')	{x.red=0x8000;x.green=0x8000;x.blue=0x8000;}	/* grey			*/
	else if(c=='F'||c=='f')	{x.red=0xff00;x.green=0x6e00;x.blue=0xc700;}					/* fuchsia	*/
	else if(c=='G'||c=='g')	{x.red=0x0000;x.green=0xffff;x.blue=0x0000;}					/* green		*/
	else if(c=='H'||c=='h'||c=='5')	{x.red=0x0000;x.green=0x8000;x.blue=0x0000;}	/* hunter		*/
	else if(c=='I'||c=='i')	{x.red=0xdb00;x.green=0x7000;x.blue=0xdb00;}					/* indigo		*/
	else if(c=='J'||c=='j')	{x.red=0x8000;x.green=0x8000;x.blue=0x0000;}					/* olive		*/
	else if(c=='K'||c=='k'||c=='0')	{x.red=0x0000;x.green=0x0000;x.blue=0x0000;}	/* black		*/
	else if(c=='L'||c=='l')	{x.red=0x3200;x.green=0xcd00;x.blue=0x3200;}					/* lime			*/
	else if(c=='M'||c=='m')	{x.red=0xffff;x.green=0x0000;x.blue=0xffff;}					/* magenta	*/
	else if(c=='N'||c=='n')	{x.red=0x0000;x.green=0x0000;x.blue=0x8000;}					/* navy			*/
	else if(c=='O'||c=='o'||c=='3')	{x.red=0xffff;x.green=0xaf00;x.blue=0x0000;}	/* orange		*/
	else if(c=='P'||c=='p')	{x.red=0x8000;x.green=0x0000;x.blue=0x8000;}					/* purple		*/
	else if(c=='Q'||c=='q')	{x.red=0xd900;x.green=0xd900;x.blue=0xf300;}					/* quartz		*/
	else if(c=='R'||c=='r'||c=='2')	{x.red=0xffff;x.green=0x0000;x.blue=0x0000;}	/* red			*/
	else if(c=='S'||c=='s')	{x.red=0x8e00;x.green=0xdb00;x.blue=0x2300;}					/* sienna		*/
	else if(c=='T'||c=='t')	{x.red=0x0000;x.green=0x8000;x.blue=0x8000;}					/* teal			*/
	else if(c=='U'||c=='u')	{x.red=0x2000;x.green=0x0000;x.blue=0x2000;}					/* ultraviolet */
	else if(c=='V'||c=='v'||c=='7')	{x.red=0xee00;x.green=0x8200;x.blue=0xee00;}	/* violet		*/
	else if(c=='W'||c=='w'||c=='9')	{x.red=0xffff;x.green=0xffff;x.blue=0xffff;}	/* white		*/
	else if(c=='X'||c=='x')	{x.red=intrand(0xffff);x.green=intrand(0xffff);x.blue=intrand(0xffff);}	/* random	*/
	else if(c=='Y'||c=='y'||c=='4')	{x.red=0xffff;x.green=0xffff;x.blue=0x0000;}	/* yellow		*/
	else if(c=='Z'||c=='z')	{x.red=intrand(2)*0xffff;x.green=intrand(2)*0xffff;x.blue=intrand(2)*0xffff;}	/* random	*/
	else if(c=='1')	{x.red=0x4000;x.green=0x4000;x.blue=0x2000;}									/* brown		*/
	else if(c=='-')	{x.red=0xc000;x.green=0xc000;x.blue=0xc000;}									/* silver		*/
	else if(c=='+')	{x.red=0xffff;x.green=0xd700;x.blue=0x2000;}									/* gold		*/
	else {x.red=0x0000;x.green=0x0000;x.blue=0x0000;}															/* black		*/
	RGBForeColor(&x); }

void SetPenSize(int w,int h) {
	PenSize(w,h);
	return; }

void ToPixel(float x,float y,int *a,int *b) {
	#if TARGET_API_MAC_CARBON
		struct Rect r;
		
		GetWindowPortBounds(output,&r);
		x=(x-xmin)/(xmax-xmin)*(r.right-r.left)+r.left;
		y=(y-ymin)/(ymax-ymin)*(r.top-r.bottom)+r.bottom;
	#else
		x=(x-xmin)/(xmax-xmin)*((*qd.thePort).portRect.right-(*qd.thePort).portRect.left)+(*qd.thePort).portRect.left;
		y=(y-ymin)/(ymax-ymin)*((*qd.thePort).portRect.top-(*qd.thePort).portRect.bottom)+(*qd.thePort).portRect.bottom;
	#endif
	*a=(x>32767)?32767:((x<-32767)?-32767:x);
	*b=(y>32767)?32767:((y<-32767)?-32767:y);
	return; }

int ToPoint(int a,int b,float *x,float *y) {
	#if TARGET_API_MAC_CARBON
		struct Rect r;
		
		GetWindowPortBounds(output,&r);
		*x=(a-r.left)*(xmax-xmin)/(r.right-r.left)+xmin;
		*y=(b-r.bottom)*(ymax-ymin)/(r.top-r.bottom)+ymin;
	#else
		*x=(a-(*qd.thePort).portRect.left)*(xmax-xmin)/((*qd.thePort).portRect.right-(*qd.thePort).portRect.left)+xmin;
		*y=(b-(*qd.thePort).portRect.bottom)*(ymax-ymin)/((*qd.thePort).portRect.top-(*qd.thePort).portRect.bottom)+ymin;
	#endif
	if(*x<xmin||*x>xmax||*y<ymin||*y>ymax) return 0;
	else return 1; }

void DrawAxes() {
	int a=0,b=0;
	
	ToPixel(xmin,0,&a,&b);
	MoveTo(a,b);
	ToPixel(xmax,0,&a,&b);
	LineTo(a,b);
	ToPixel(0,ymin,&a,&b);
	MoveTo(a,b);
	ToPixel(0,ymax,&a,&b);
	LineTo(a,b); }

void DrawMarks(float dx,float dy,int lo,int hi) {
	float t,f1,f2;
	int a,b;

	dx=fabs(dx);
	dy=fabs(dy);
	f1=dx*floor((xmin<xmax?xmin:xmax)/dx);
	f2=dx*ceil((xmin<xmax?xmax:xmin)/dx);
	for(t=f1;t<=f2;t+=dx)	{
		ToPixel(t,0,&a,&b);
		MoveTo(a,b-hi);
		LineTo(a,b+lo); }
	f1=dy*floor((ymin<ymax?ymin:ymax)/dy);
	f2=dy*ceil((ymin<ymax?ymax:ymin)/dy);
	for(t=f1;t<=f2;t+=dy)	{
		ToPixel(0,t,&a,&b);
		MoveTo(a-lo,b);
		LineTo(a+hi,b); }
	return; }

void PlotClear() {
	int a,b,c,d;
	Rect r;
	
	ToPixel(xmin,ymin,&a,&b);
	ToPixel(xmax,ymax,&c,&d);
	r.top=d;
	r.left=a;
	r.bottom=b;
	r.right=c;
	EraseRect(&r); }

void PlotMove(float x,float y) {
	int a,b;

	ToPixel(x,y,&a,&b);
	MoveTo(a,b); }

void PlotPt(float x,float y) {
	int a,b;
	
	ToPixel(x,y,&a,&b);
	MoveTo(a,b);
	LineTo(a,b); }

void PlotLine(float x,float y) {
	int a,b;
	
	ToPixel(x,y,&a,&b);
	LineTo(a,b); }

void PlotStr(float x,float y,char *s) {
	int a,b;
	unsigned char *s2;
	
	ToPixel(x,y,&a,&b);
	MoveTo(a,b);

	s2=PascalString(s);
	if(s2)	{
		TextSize(9);
		DrawString(s2);
		free(s2); }
	return; }

void ShowLimits() {
	int a,b,i;
	char *s;
	unsigned char *ps;
	
	s=EmptyString();
	if(!s) return;
	i=0;
	s[i++]='(';
	i+=sprintf(s+i,"%1.2f",xmin);
	s[i++]=',';
	i+=sprintf(s+i,"%1.2f",ymin);
	s[i++]=')';
	ps=PascalString(s);
	if(ps)	{
		TextSize(9);
		ToPixel(xmin,ymin,&a,&b);
		a+=10;
		b-=10;
		MoveTo(a,b);
		DrawString(ps);
		free(ps);	}
	i=0;
	s[i++]='(';
	i+=sprintf(s+i,"%1.2f",xmax);
	s[i++]=',';
	i+=sprintf(s+i,"%1.2f",ymax);
	s[i++]=')';
	s[i++]='\0';
	ps=PascalString(s);
	if(ps)	{
		ToPixel(xmax,ymax,&a,&b);
		a-=6*i;
		b+=20;
		MoveTo(a,b);
		DrawString(ps);
		free(ps); }
	free(s);	
	return; }

void PlotFn(float (*fn)(float),float dx) {
	int a,b;
	float x;
	
	ToPixel(xmin,(*fn)(xmin),&a,&b);
	MoveTo(a,b);
	for(x=xmin;x<xmax;x+=dx) {
		ToPixel(x,(*fn)(x),&a,&b);
		LineTo(a,b); }
	return; }

void PlotInt(float (*fn)(float),float x0,float dx) {
	float x,y;
	int a,b;
	
	y=0;
	ToPixel(x0,0,&a,&b);
	MoveTo(a,b);
	for(x=x0-dx;x>=xmin;x-=dx)	{
		y-=(*fn)(x+dx/2)*dx;
		ToPixel(x,y,&a,&b);
		LineTo(a,b); }
	y=0;
	ToPixel(x0,0,&a,&b);
	MoveTo(a,b);
	for(x=x0+dx;x<=xmax;x+=dx) {
		y+=(*fn)(x-dx/2)*dx;
		ToPixel(x,y,&a,&b);
		LineTo(a,b); }
	return; }

void PlotData(float *data,int n,float xmin,float xmax,int style) {
	int i,a,b,b0;
	float x;
	
	// style 1 is bars, 2 is dots, 3 is line
	
	ToPixel(xmin,data[0],&a,&b);
	MoveTo(a,b);
	for(i=0;i<n;i++)	{
		x=(xmax-xmin)*i/(n-1)+xmin;
		ToPixel(x,0,&a,&b0);
		ToPixel(x,data[i],&a,&b);
		switch(style)	{
			case 1:
				MoveTo(a,b0);
				break;
			case 2:
				MoveTo(a,b);
				break;
			case 3:
				break; }
		LineTo(a,b); }
	return; }

void PlotData2(float *data,float *x,int n,int style) {
	int i,a,b,b0;

	// style 1 is bars, 2 is dots, 3 is line

	ToPixel(x[0],data[0],&a,&b);
	MoveTo(a,b);
	for(i=0;i<n;i++)	{
		ToPixel(x[i],0,&a,&b0);
		ToPixel(x[i],data[i],&a,&b);
		switch(style)	{
			case 1:
				MoveTo(a,b0);
				break;
			case 2:
				MoveTo(a,b);
				break;
			case 3:
				break; }
		LineTo(a,b); }
	return; }

void PlotData3(float *x,float *y,int *ct,int col) {
	int i,j,a,b;
	char c;
	Rect r;

	c='a';
	for(j=0;j<col;j++) {
		if(j==0) SetColor('2');
		else if(j==1) SetColor('5');
		else if(j==2) SetColor('6');
		else SetColor(c++);
		for(i=0;i<ct[j];i++) {
			ToPixel(x[col*i+j],y[col*i+j],&a,&b);
			r.left=a;
			r.top=b;
			r.right=a+10;
			r.bottom=b+10;
			PaintOval(&r); }}
	return; }
	