/* DensView.m  Copyright 1992 Steve Ludtke */

/* This object works by taking passed data and rendering it into an NXImage */
/* which is then composited to the screen as necessary. When the view is    */
/* resized, the user coordinate system is reset to unit size. Areas can be  */ 
/* selected by dragging with the mouse. A zoomTo:::: message will be sent to*/ 
/* the delegate with the resulting area. */

#import "Plot3DView.h"
#import "DensView.h"

#import <stdio.h>
#import <string.h>
#import <libc.h>
#import <math.h>
#import <appkit/NXImage.h>
#import <appkit/OpenPanel.h>
#import <appkit/Slider.h>
#import <dpsclient/psops.h>
#import <appkit/Application.h>

char hex[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };

extern id NXApp;

@implementation DensView

-initFrame:(NXRect *)myrect
{
[super initFrame:myrect];
[self setDrawSize:1.0 :1.0];	/* set size and origin so view is a "unit" */
[self setDrawOrigin:0.0 :0.0];	/* coordinate system */
point.y=point.x=0.0;		/* origin of image when composited */
buf=NULL;
data=NULL;
image=nil;			/* new image is created by -setData::::: */
return self;
}

-superviewSizeChanged:(const NXSize *)oldsize
{
[super superviewSizeChanged:oldsize];
[self setDrawSize:1.0 :1.0];			/* fix drawing size */
[self setData:nx :ny :data :minZ :maxZ];	/* re-render NXImage */
return self;
}

/* essentially all this method has to do is composite the NXImage to the */
/* screen. */
-drawSelf:(NXRect *)rects :(int)rectCount
{

if (buf==NULL||data==NULL||image==nil) {
	PSsetgray(NX_BLACK);
	NXRectFill(&bounds);
	return(self);
}

[image composite:NX_COPY toPoint:&point];

return self;
}

/* This method receives data and generates the NXImage */
-setData:(int)Nx :(int)Ny :(float *)Data :(float)MinZ :(float)MaxZ
{
NXStream *str;
static char s[220];
int i,n,d;

/* Make sure our postscript buffer is big enough */
if ((Nx*Ny)>bufs) {
	if (buf!=NULL) free(buf);
	buf=malloc(Nx*Ny*2+Nx*Ny/20+400);
	bufs=Nx*Ny;
}
data=Data;
nx=Nx;
ny=Ny;
minZ=MinZ;
maxZ=MaxZ;

/* Since everything conforms properly, it would be quite easy to generate */
/* an EPS file from this data ... */
sprintf(s,"%%!PS-Adobe-2.0 EPSF-2.0\n%%%%Origin:0 0\n%%%%BoundingBox: 0 0 %f %f\n%%%%EndComments\n%f %f scale\n/picstr 1 string def\ngsave\n",frame.size.width,frame.size.height,frame.size.width,frame.size.height);
strcpy(buf,s);
sprintf(s,"%d %d 8 [%d 0 0 %d 0 0] {currentfile picstr readhexstring pop} image\n",nx,ny,nx,ny);
strcat(buf,s);
i=strlen(buf);
for (n=0; n<(nx*ny); n++) {
        if (n%100==0) buf[i++]='\n';
        d=255*(data[n]-minZ)/(maxZ-minZ);
        if (d>255) d=255;
        if (d<0) d=0;
        buf[i++]=hex[d>>4];
        buf[i++]=hex[d&15];
}
buf[i]=0;
sprintf(s,"\ngrestore\n");
strcat(buf,s);

/* send the postscript we generated to an NXImage. I know this is a messy */
/* way of doing things, but it's leftover from a previous incarnation of  */
/* this object and I didn't feel like rewriting it ... */
str=NXOpenMemory(buf,strlen(buf),NX_READONLY);
if (image!=nil) [image free];
image=[NXImage alloc];
image=[image initFromStream:str];
NXClose(str);
/* This is how it used to work */
/*DPSWritePostScript(DPSGetCurrentContext(), buf, i);*/

[self display];
return self;
}

/* Allows the user to select an area of the plot to be passed to the */
/* delegate via a zoomTo:::: message. */
-mouseDown:(NXEvent *)oevent 
{
int oldMask,loop=1;
NXEvent *event,evs;
float f,dash[2] = { SS,SS };

evs=*oevent;
oevent=&evs;
[self convertPoint:&oevent->location fromView:nil];

oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];

while (loop) {
    event = [NXApp getNextEvent:(NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK)];
    [self convertPoint:&event->location fromView:nil];

        switch (event->type) {
        case NX_LMOUSEUP:
                loop = 0;
		if (event->location.x<oevent->location.x) {
			f=event->location.x;
			event->location.x=oevent->location.x; 
			oevent->location.x=f; 
		}
		if (event->location.y<oevent->location.y) {
			f=event->location.y;
			event->location.y=oevent->location.y; 
			oevent->location.y=f; 
		}
		if (event->location.x-oevent->location.x<.02) break;
		if (event->location.y-oevent->location.y<.02) break;
		[delegate zoomTo:oevent->location.x :oevent->location.y :event->location.x :event->location.y];
            break;
        case NX_LMOUSEDRAGGED:
		[self lockFocus];
		PSsetlinewidth(0.0);
		[image composite:NX_COPY toPoint:&point];

		PSmoveto(oevent->location.x,oevent->location.y);
		PSlineto(event->location.x,oevent->location.y);
		PSlineto(event->location.x,event->location.y);
		PSlineto(oevent->location.x,event->location.y);
		PSlineto(oevent->location.x,oevent->location.y);

		PSsetgray(1.0);
		PSsetdash(dash,2,0.0);
		PSgsave();
		PSstroke();
		PSgrestore();

		PSsetgray(0.0);
		PSsetdash(dash,2,SS);
		PSstroke();

		[self unlockFocus];
		[window flushWindow];
	    break;
	}
    
}
[window setEventMask:oldMask];
return self;
}

-(int)acceptsFirstMouse {
return (YES);
}
@end
