
/*	Program By Fred Richards, Copyright 1990.	*/
/*							*/
/*	This software is provided free of charge	*/
/*	and without warranty.  The source code may	*/
/*	be distributed freely provided this copyright	*/
/*	notice remains in tact.				*/


#import "Plot.h"
#import "PlotView.h"
#import	<stdlib.h>
#import <string.h>


@implementation Plot

// These 'set***' methods attach this to the objects in the Window

- setCanvas:anObject
{
    canvas = anObject;
    return self;
}

- setXMin:anObject
{
    xMin = anObject;
    return self;
}

- setYMax:anObject
{
    yMax = anObject;
    return self;
}

- setYMin:anObject
{
    yMin = anObject;
    return self;
}

- setXMax:anObject
{
    xMax = anObject;
    return self;
}

- setPlotBox:anObject
{
    plotBox = anObject;
    return self;
}


// These methods return the values of the instance variables

- (BOOL )border:sender {	return border;	}

- (BOOL )axes:sender {	return axes;	}

- (BOOL )lines:sender {	return lines;	}

- (int )style:sender {	return style;	}

- (NXPoint *)data {	return data;	}

- (int )dSize {		return dCount;	}

- (NXCoord )xmin:sender {    return min.x; }

- (NXCoord )xmax:sender {    return max.x; }

- (NXCoord )ymin:sender {    return min.y; }

- (NXCoord )ymax:sender {    return max.y; }

- (char *)fileName:sender; {	return fileName; }

// These set*** methods set the instance variables from the control-
// panel objects.

- setLines:sender
{
    lines = [sender state];
    return self;
}

- setAxes:sender
{
    axes = [sender state];
    return self;
}

- setBorder:sender
{
    border = [sender state];
    return self;
}

- setStyle:sender
{
    style = [sender selectedRow];
    return self;
}

- setMinmax:sender
{
    minmax = [sender selectedCol];
    if (minmax)  {
      [self getXmin:self];	[self getXmax:self];
      [self getYmin:self];	[self getYmax:self];
    } else  {
      min.x = dMin.x;	max.x = dMax.x;
      min.y = dMin.y;	max.y = dMax.y;
      [self resetXmin:self];	[self resetXmax:self];
      [self resetYmin:self];	[self resetYmax:self];
    }
    return self;
}


// read the min or max value from the control panel

- getXmin:sender
{
    min.x = [xMin floatValueAt:0];
    return self;
}

- getXmax:sender
{
    max.x = [xMax floatValueAt:0];
    return self;
}

- getYmin:sender
{
    min.y = [yMin floatValueAt:0];
    return self;
}

- getYmax:sender
{
    max.y = [yMax floatValueAt:0];
    return self;
}


// reset the data limits shown in the control panel

- resetXmin:sender
{
    [xMin setFloatValue:(float )min.x at:0];
    return self;
}

- resetXmax:sender
{
    [xMax setFloatValue:(float )max.x at:0];
    return self;
}

- resetYmin:sender
{
    [yMin setFloatValue:(float )min.y at:0];
    return self;
}

- resetYmax:sender
{
    [yMax setFloatValue:(float )max.y at:0];
    return self;
}

// Change the Plot title in the window to reflect
// the data-file name

- setBoxTitle:(char *)aTitle
{
    [plotBox setTitle:(const char *)aTitle];
    return self;
}


// Get a fileName:  this will be the PostScript file where the
// PlotView will write its code if a 'Save' is requested from the main menu

- setFileName:(char *)aPSfile
{
    if (fileName)
      free(fileName);
    fileName = calloc(strlen(aPSfile)+1, sizeof(char));
    strcpy(fileName, aPSfile);
    return self;
}


// This method sets things up so that PlotView:drawSelf
// can do all the drawing work.

- drawPlot:sender
{
    if (!data)
      return self;

    if ([self setupBorder:self])
      [plotBox display];
    return self;
}

// Allocate enough memory and read the data points

- (int )readDataFrom:(FILE *)aDataStream
{
    double	x, y;
    int		i;

    /* First read through the file to see how big it is */
    
    i = 0;
    while (fscanf(aDataStream, "%lf %lf\n", &x, &y) == 2)
      i++;

    /* If it was empty, then don't discard the old data */

    if (i == 0)
      return 0;

    /* Now read the data into memory */
    
    dCount = i;

    if (data)
      free(data);
    data = (NXPoint *)calloc(dCount, sizeof(NXPoint));
    rewind(aDataStream);
    for (i = 0; i < dCount; i++)  {
      fscanf(aDataStream, "%lf %lf\n", &x, &y);
      data[i].x = (float )x;
      data[i].y = (float )y;
    }
    return dCount;
}


// Go through the data set and find values for dMin.x, dMax.x, dMin.y, dMax.y

- findMinAndMax
{
    if (data)  {
      int	i;
      dMin.x = dMax.x = data[0].x;
      dMin.y = dMax.y = data[0].y;
      for (i = 1; i < dCount; i++)  {
	if (data[i].x < dMin.x)
	  dMin.x = data[i].x;
	else if (data[i].x > dMax.x)
	  dMax.x = data[i].x;
	if (data[i].y < dMin.y)
	  dMin.y = data[i].y;
	else if (data[i].y > dMax.y)
	  dMax.y = data[i].y;
      }
    }
    return self;
}
	

// This message sends the appropriate min and max to the PlotView

- (BOOL )setupBorder:sender
{
    NXSize	newSize;

    /* Start by setting up the right scale */

    if (minmax)  {
      [self getXmin:self];	[self getXmax:self];
      [self getYmin:self];	[self getYmax:self];
    } else  {
      min.x = dMin.x;	max.x = dMax.x;
      min.y = dMin.y;	max.y = dMax.y;
      [self resetXmin:self];	[self resetXmax:self];
      [self resetYmin:self];	[self resetYmax:self];
    }

    newSize.width = max.x - min.x;
    newSize.height = max.y - min.y;
    if (newSize.width == 0 || newSize.height == 0)  {
      NXRunAlertPanel("Plot", "The graph width and height must be > 0",
		      "OK", NULL, NULL, NULL);
      return NO;
    }
    [canvas setDrawSize:(newSize.width + 0.2*newSize.width)
	:(newSize.height + 0.2*newSize.height)];

    [canvas setDrawOrigin:(min.x - 0.1*newSize.width)
	:(min.y - 0.1*newSize.height)];

    return YES;
}

// Use the OpenPanel object to get a fileName

- open:sender
{
    char const	*fileTypes[2] = {0,0};	// this type is all ASCII files
    char	fname[1024];

    id openPanel = [OpenPanel new];
    if ([openPanel runModalForTypes:fileTypes])  {
      strncpy(fname, [openPanel filename], 1024);
      [self openFile:fname];
    }
    return self;
}

- openFile:(char *)aDataFile
{
    FILE	*dataStream;
    char	psFile[1024];

    if ((dataStream = fopen(aDataFile, "r")) == NULL)  {
      NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, aDataFile);
      return self;
    }

    if ([self readDataFrom:dataStream] == 0)  {
      NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
		      NULL, NULL, aDataFile);
      fclose(dataStream);
      return self;
    }
    fclose(dataStream);

    [self findMinAndMax];
    [self setBoxTitle:aDataFile];
    [self drawPlot:self] ;
    
    return self;
}


// Tell the PlotView to write its PostScript code to fileName

- save:sender
{
    if (fileName)
      [canvas savePSCode:fileName];
    else
      [self saveAs:sender];

    return self;
}

// Get a fileName and then tell the PlotView to save
// its PostScript code there

- saveAs:sender
{
    id savePanel = [SavePanel new];

    if (fileName)
      free(fileName);

    [savePanel setRequiredFileType:"ps"];
    if ([savePanel runModal])  {
      fileName = calloc(strlen([savePanel filename])+1, sizeof(char));
      strcpy(fileName, [savePanel filename]);
      [self save:sender];
    }

    return self;
}


@end
