ftp.nice.ch/pub/next/science/mathematics/HippoDraw.2.0.s.tar.gz#/HippoDraw/Hippo.bproj/Plot.m

This is Plot.m in view mode; [Download] [Up]

/* Plot.m  by Jonas Karlsson, July 1990 
 *
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University. All Rights Reserved.
 */

#import "Draw.subproj/draw.h"
#import "Plot.h"

const char Plot_h_rcsid[] = PLOT_H_ID;
const char Plot_m_rcsid[] = "$Id: Plot.m,v 2.54.2.5 1994/02/08 20:30:15 rensing Exp $";


#import "HGraphicView.h"
#import "HTuple.h"
#import <float.h>

#define INDEX_VERSION 2
#define INVERTCUT_VERSION 3
#define FUNCLIST_VERSION 4
#define CURRENT_VERSION FUNCLIST_VERSION

@implementation Plot

static unsigned int plotNum = 0;

static NXAtom namePlot() 
{
    char buffer[256];
    
    sprintf( buffer, "HippoPlot.%u", plotNum++);
    return NXUniqueString(buffer);
}
    
static void setMarginRect( NXRect *margins, NXRect *bounds, int grid,
    int topFlag, int leftFlag)
{
    float xOffset=0.0, yOffset=0.0;
    
    switch (topFlag) {
    case 0:			/* Top */
    	yOffset = 0.11 * bounds->size.height;
        break;
    case 1:			/* Middle */
    	yOffset = 0.07 * bounds->size.height;
        break;
    case 2:			/* Bottom */
    	yOffset = 0.04 * bounds->size.height;
        break;
    }

    switch (leftFlag) {
    case 0:			/* Left */
    	xOffset = 0.18 * bounds->size.width;
        break;
    case 1:			/* Middle */
    	xOffset = 0.11 * bounds->size.width;
        break;
    case 2:			/* Right */
    	xOffset = 0.04 * bounds->size.width;
        break;
    }

    NXSetRect (margins, xOffset, yOffset,
	bounds->size.width * 0.80, bounds->size.height * 0.82);

    if ( margins->size.width/grid > 3.001 ) {
        margins->origin.x = bounds->origin.x
	              + MAX( grid*floor( margins->origin.x/grid ), grid );
	margins->size.width = grid*floor( margins->size.width/grid );
    } else {
        margins->origin.x += bounds->origin.x;
    }
    if ( margins->size.height/grid > 3.001) {
        margins->origin.y = bounds->origin.y
	               + MAX( grid*floor( margins->origin.y/grid ), grid );
	margins->size.height = grid*floor( margins->size.height/grid );
    } else {
        margins->origin.y += bounds->origin.y;
    }
    
}
+ initialize
{
    [self setVersion:CURRENT_VERSION];
    return self;
}
- init
{
    NXColor	color = NX_COLORCLEAR;
    [super init ];
    uniqueName = namePlot();
    graphicView = NULL;
    hTuple = NULL;
    disp = NULL;
    
    [self setFillColor:&color];
    cutHistFlag = NO;
    
    return self;
}

- initPlotWithTuple: (HTuple*)ht Type: (graphtype_t)type
{
     [self init];
    
     disp = h_newDisp(type);
     [self replaceTupleWith: ht];

     return self;
}

- (const char *)name
{
    return uniqueName;
}
- setDispType:(graphtype_t *) type
{
    graphtype_t         oldtype;

    oldtype = h_getDispType(disp);
    if (oldtype == *type)
	return self;

    h_setDispType(disp, *type);
    if ((oldtype == HISTOGRAM) && (*type != HISTOGRAM)) {
	int i = h_getBinding(disp, XAXIS);
	int dim = h_getNtDim(h_getNtuple(disp));
	i = MIN(i+1, dim-1);
	if (h_getBinding(disp, YAXIS) < 0) {
	    h_bind(disp, YAXIS, i);
	}
	i = MIN(i+1, dim-1);
	if (h_getBinding(disp, ZAXIS) < 0) {
	    h_bind(disp, ZAXIS, i);
	}
	[self NameAxisY:"%y"];
	[self NameAxisZ:"%z"];
    }
    switch (*type) {
    case SCATTERPLOT:
	h_setDrawType(disp, POINT);
	break;
    case LEGOPLOT:
	disp->doMesh = 1;
	break;
    case XYPLOT:
    case STRIPCHART:
	h_setDrawType(disp, POINT);
	break;
    case HISTOGRAM:
	h_setDrawType(disp, BOX);
	[self NameAxisY:"Entries / %dx bin"];
    default:
	break;
    }

    return self;
}

- setGraphicView:anObject
{
    graphicView = anObject;
    return self;
}
- graphicView
{
    return graphicView;
}
- setInspector:(id)anInspector
{
	inspector = anInspector;
	return self;
	
}
- inspector
{
	return inspector;
}
- setHTuple: ht withDisplay:(display) d1
{
    NXRect bbox;
    
    disp = d1;
    [self replaceTupleWith:ht];
    h_getDrawRect(disp, (rectangle *) &bbox);
    [self setBounds: &bbox];
    return self;
}
- setRefFlag:(BOOL) flag
{
    h_setNtByRef( disp, flag, NULL );
    return self;
}

- setFitted:(BOOL) flag
{
    isFitted = flag;
    return self;
}

- (BOOL)isFitted
{
    return isFitted;
}

- setFixBinsFlag:(BOOL)flag
{
    fixBinsFlag = flag;
    h_setFixedBins( disp, fixBinsFlag );
    return self;
}
- setRefFilename:(const char *)filename
{
	if (!filename) return self;    /* if filename is not legal, return */
    if ( !reffilename ) {
        NX_ZONEMALLOC( [self zone], reffilename, char, strlen(filename)+1 );
    } else {
        NX_ZONEREALLOC( [self zone], reffilename, char, strlen(filename)+1 );
    }
    strcpy( reffilename, filename );
    return self;
}
- changeRefFileNameIfValid:(const char *)filename
{
    if ( !hTuple ) {
        return self;
    }
    if ( [hTuple isFakeFilename] ) {
	if ( !reffilename ) {
	    NX_ZONEMALLOC( [self zone], reffilename, char,
	                   strlen(filename)+1 );
	} else {
	    NX_ZONEREALLOC( [self zone], reffilename, char,
	                    strlen(filename)+1 );
	}
	strcpy( reffilename, filename );
	[hTuple setFilename:filename];
    }
    return self;
}
- replaceTupleWith: ht
{
    ntuple	tuple;
    
    hTuple = ht;
    tuple = (hTuple != nil)  ? [hTuple ntuple] : NULL ;
    h_bindNtuple(disp, tuple );
    if ( !tuple ) return self;
    [self setRefFilename:[hTuple filename]];
    [self setRefFlag:[hTuple isRef]];
    ntindex = [hTuple index];
    isFitted = NO;
    return self;
}
- replace:oldTuple with:newTuple
{
    if ( oldTuple == hTuple ) {
	[self replaceTupleWith:newTuple];
    }
    return self;
} 
- closeTuple
{
    ntuple	tuple;
    
    refFlag = [hTuple isRef];
    fixBinsFlag = h_getFixedBins(disp);
    h_setFixedBins(disp, YES);
    hTuple = nil;
    tuple = NULL;
    h_bindNtuple(disp, tuple );
    return self;
}
- bindReference
{
    if ( reffilename == NULL ) return self;
    if ( !hTuple ) {
	hTuple = [ graphicView hTupleForFile:reffilename index:ntindex];
    }
    if ( disp == NULL ) return self;
    [self replaceTupleWith:hTuple];
    if ( !hTuple ) return self;
    
    h_setFixedBins( disp, fixBinsFlag );
    return self;
}

- hTuple
{
    return hTuple;
}
- (ntuple) ntuple
{
    return [hTuple ntuple];
}
- addHTupleToList:tlist
{
    if ( hTuple ) {
	[tlist addObjectIfAbsent:hTuple];
    }
    return self;
}

- addPlotToList:list
{
    [list addObjectIfAbsent:self];
    return self;
}
- (display) histDisplay
{
    return disp;
}

- setTitle:(char *) title
{
  h_setTitle(disp, title);

  return self;
}
- (const char *) title
{
    return h_getTitle( disp );
}
- NameAxisX:(const char *)AxisName
{
    h_setAxisLabel(disp, XAXIS, AxisName);
    return self;
}
- (const char *) axisLabelX
{
    return h_getAxisLabel(disp, XAXIS );
}
- NameAxisY:(const char *)AxisName
{
    h_setAxisLabel(disp, YAXIS, AxisName);
    return self;
}
- (const char *) axisLabelY
{
    return h_getAxisLabel(disp, YAXIS );
}
- NameAxisZ:(const char *)AxisName
{
    h_setAxisLabel(disp, ZAXIS, AxisName);
    return self;
}
- (const char *) axisLabelZ
{
    return h_getAxisLabel(disp, YAXIS );
}
- NameAxisW:(const char *)AxisName
{
    h_setAxisLabel(disp, WEIGHT, AxisName);
    return self;
}

- NameAxisXE:(const char *)AxisName
{
    h_setAxisLabel(disp, XERROR, AxisName);
    return self;
}

- NameAxisYE:(const char *)AxisName
{
    h_setAxisLabel(disp, YERROR, AxisName);
    return self;
}

- bindAxisX:(int *)dataDim
{
    if ( [self isCutPlot] ) return self;
    
    h_bind(disp, XAXIS, *dataDim );
    isFitted = NO;
    return self;
}
- bindAxisY:(int *)dataDim
{
    h_bind(disp, YAXIS, *dataDim );
    isFitted = NO;
    return self;
}
- bindAxisZ:(int *)dataDim
{
    h_bind(disp, ZAXIS, *dataDim );
    isFitted = NO;
    return self;
}
- bindAxisW:(int *)dataDim
{
    h_bind(disp, WEIGHT, *dataDim );
    isFitted = NO;
    return self;
}

- bindAxisXE:(int *)dataDim
{
    h_bind(disp, XERROR, *dataDim );
    isFitted = NO;
    return self;
}

- bindAxisYE:(int *)dataDim
{
    h_bind(disp, YERROR, *dataDim );
    isFitted = NO;
    return self;
}

- setLogScale:(binding_t *)axis to:(int *)yesOrNo
{	
    h_setLogAxis( disp, *axis, *yesOrNo );
    return self;
}
- (BOOL) isLogScaleX
{
    BOOL	val;
    
    val = h_getLogAxis(disp, XAXIS);
    return val;
}
- (BOOL) isLogScaleY
{
    BOOL	val;
    
    val = h_getLogAxis(disp, YAXIS);
    return val;
}
- (BOOL) isLogScaleZ
{
    BOOL	val;
    
    val = h_getLogAxis(disp, ZAXIS);
    return val;
}
- setTitlesFlag:(const int *)yesOrNo
{
    h_setDrawTitles( disp, *yesOrNo);
    return self;
}
- (int) titlesFlag
{
    return h_getDrawTitles(disp);
}

- setAxesFlag:(int *)yesOrNo
{
    h_setDrawAxes( disp, *yesOrNo );
    return self;
}
- (int) axesFlag
{
    return h_getDrawAxes( disp );
}
- setAutoScale:(binding_t *)axis to:(const int *)yesOrNo
{	
     h_setAutoScale(disp, *axis, *yesOrNo);
     return self;
}
- getRangeForAxisX:(NXPoint *)p
{
    h_getRange(disp, XAXIS, &p->x, &p->y );
    return self;
}
- getRangeForAxisY:(NXPoint *)p
{
    h_getRange(disp, YAXIS, &p->x, &p->y );
    return self;
}
- setRange:(binding_t *)axis to:(const NXPoint *)p
{
    h_setRange(disp, *axis, p->x, p->y );
    isFitted = NO;
    return self;
}

- setRangeAndNbins:(binding_t *)axis to:(const NXPoint *)p
{
    float	low, high, width;
    int		nbins;
        
    width = h_getBinWidth(disp, *axis);
    nbins = (p->y - p->x) / width;
    low = p->x;
    high = low + nbins * width;
    h_setRange(disp, *axis, low, high);
    h_setBinNum(disp, *axis, nbins);
    isFitted = NO;
    return self;
}

- getRangeForAxisZ:(NXPoint *)p
{
    h_getRange(disp, ZAXIS, &p->x, &p->y );
    return self;
}
- getRangeForAxis:(binding_t) axis low:(float *) xl high:(float *) xh
{
    h_getRange(disp, axis, xl, xh );
    return self;
}
- setRangesFrom:plot
{
    display 		plotdisp;
    float 		low, high;

    graphtype_t	plot_t;
    float	plot_w, my_w;
    
    if ( plot == self ) return self;

    plotdisp = [plot histDisplay];
    h_getRange(plotdisp, XAXIS, &low, &high );
    h_setRange(disp, XAXIS, low, high );
    h_getRange(plotdisp, YAXIS, &low, &high );

    /*
     * normalize for histograms only
     */
    plot_t = h_getDispType( plotdisp);
    if ( h_getDispType(disp) == HISTOGRAM && 
                      plot_t == HISTOGRAM    ) {
    	plot_w = h_getBinWidth( plotdisp, XAXIS );
    	my_w = h_getBinWidth(disp, XAXIS);
    	low *= (my_w/plot_w);
    	high *= (my_w/plot_w);
    }

    h_setRange(disp, YAXIS, low, high );
    isFitted = NO;

    return self;
}
- setRangesAndBinsFrom:plot
{
    display 		plotdisp;
    float		low, high, width;
    int			nbins;

    graphtype_t	plot_t;
    float	plot_w, my_w;
    
    if ( plot == self ) return self;

    plotdisp = [plot histDisplay];
    h_getRange(plotdisp, XAXIS, &low, &high );
    if ( h_getDispType(disp) == HISTOGRAM) { 
    	width = h_getBinWidth(disp, XAXIS);
    	nbins = (high - low) / width;
    	high = low + nbins * width;
    	h_setBinNum(disp, XAXIS, nbins);
    }
    h_setRange(disp, XAXIS, low, high);

    h_getRange(plotdisp, YAXIS, &low, &high );
    /*
     * normalize for histograms only
     */
    plot_t = h_getDispType( plotdisp);
    if ( h_getDispType(disp) == HISTOGRAM && 
                      plot_t == HISTOGRAM    ) {
    	plot_w = h_getBinWidth( plotdisp, XAXIS );
    	my_w = h_getBinWidth(disp, XAXIS);
    	low *= (my_w/plot_w);
    	high *= (my_w/plot_w);
    }

    h_setRange(disp, YAXIS, low, high );
    isFitted = NO;

    return self;
}
- getRangeForAxisY:(NXPoint *)range normalizedTo:plot
{
    display     plotdisp;
    float	scale;
    
    h_getRange(disp, YAXIS, &range->x, &range->y );
    plotdisp = [plot histDisplay];
    if ( h_getDispType(disp) != HISTOGRAM || 
         h_getDispType(plotdisp) != HISTOGRAM    ) {
        return self;
    }
    scale = h_getBinWidth(plotdisp, XAXIS) / h_getBinWidth(disp, XAXIS);
    range->x *= scale;
    range->y *= scale;
    return self;
}
- (float)widthForAxis:(binding_t) axis
{
    return h_getBinWidth(disp, axis);
}
- setNumBins:(binding_t *) axis to:(const int *)n
{
    h_setBinNum( disp, *axis, *n );
    h_setAutoScale( disp, *axis, 0 );
    isFitted = NO;
    return self;
}

- (int)numBinsForAxis:(binding_t) axis
{
    return h_getBinNum(disp, axis);
}

- setLineStyle:(linestyle_t *) style
{
    h_setLineStyle(disp, *style);
    return self;
}

- getLineStyle:(linestyle_t *) style
{
        *style=h_getLineStyle(disp);
	return self;
} 

- setPlotSym:(plotsymbol_t *) plotsymbol
{
    h_setPlotSym(disp, *plotsymbol);
    return self;
}

- getPlotSym:(plotsymbol_t *) plotsymbol
{
        *plotsymbol=h_getPlotSym(disp);
	return self;
} 
- setSymSize:(float *) plotsymsize
{
    h_setSymSize(disp, *plotsymsize);
    return self;
}
- getSymSize:(float *) plotsymsize
{
        *plotsymsize=h_getSymSize(disp);
	return self;
} 
- setDrawType:(drawtype_t *) type
{
    h_setDrawType(disp, *type);
    return self;
}
- getDrawType:(drawtype_t *) type
{
	*type = h_getDrawType(disp);
	return self;
}
- setColorType:(const int *)onOff
{
     int i;
     
     if ( *onOff )
	  h_orDrawType(disp, COLOR);
     else
     {
	  i = h_getDrawType(disp);
	  i = i & (~COLOR);
	  h_setDrawType(disp, i);
     }
     return self;
}

- setTickLocation: (binding_t *) axis to:(plotloc_t *) loc
{
    h_setTickLocation(disp, *axis, *loc );
    return self;
}
- setScaleLocation: (binding_t *) axis to:(plotloc_t *) loc
{
    h_setScaleLocation(disp, *axis, *loc );
    return self;
}
- setLabelLocation: (binding_t *) axis to:(plotloc_t *) loc
{
    h_setLabelLocation(disp, *axis, *loc );
    return self;
}
- setScaleFontSize: (binding_t *) axis to:(float *) fontSize
{
    h_setScaleFontSize( disp, *axis, *fontSize );
    return self;
}
- setTickLength: (binding_t *) axis to:(float *) len
{
    h_setTickLength( disp, *axis, *len );
    return self;
}

- draw
{
    	NXRect margins;
        int   grid, topFlag=0, leftFlag=0;
		
    	if (bounds.size.width < 1.0 || bounds.size.height < 1.0)
		return self;

	if ([self fill]) {
	    PSgsave();
	    [self setFillColor];
	    NXRectFill(&bounds);
	    PSgrestore();
	}
	if (!gFlags.nooutline) {
	    [self setLineColor];
	}
   	if ( (NXDrawingStatus == NX_PRINTING) && (linewidth < 0.15) ) {
    		PSsetlinewidth(0.15); 	/* in case going to Linotronic (R) */
    	}
    
    	h_setDrawRect (disp, (rectangle *) &bounds );
	if ( graphicView && [graphicView gridIsEnabled] ) { 
	    grid = [graphicView gridSpacing];
	} else {
	    grid = 1;
	}

    	if (disp->xAxis.flags.labelLocation & PLOTTOP) topFlag += 1;
    	if (disp->xAxis.flags.scaleLocation & PLOTTOP) topFlag += 1;
    	if (disp->yAxis.flags.labelLocation & PLOTRIGHT) leftFlag += 1;
    	if (disp->yAxis.flags.scaleLocation & PLOTRIGHT) leftFlag += 1;
    	setMarginRect(&margins, &bounds, grid, topFlag, leftFlag);

    	h_setMarginRect (disp, (rectangle *) &margins);

 /*
 * do shading in case of cut plot. Must do after plotting, since h_plot
 *  sets up margins, autoscales, etc.
 * h_shade will keep shading inside bounds of plot.
 */
	if ( cutHistFlag && (disp != NULL) && (cutParms.cutFunc != NULL) )
	{
	     switch (cutParms.cutCode)
	     {
	     case 0:
		  h_shade(disp, -FLT_MAX, cutParms.cutValue1);
		  break;
	     case 1:
		  h_shade(disp, cutParms.cutValue1, FLT_MAX);
		  break;
	     case 2:
		  h_shade(disp, cutParms.cutValue1, cutParms.cutValue2);
		  break;
	     case 3:
		  h_shade(disp, -FLT_MAX, cutParms.cutValue1);
		  h_shade(disp, cutParms.cutValue2, FLT_MAX);
		  break;
	     }		  
	}

 /*
  * draw plot last, so shading doesn't cover it.
  */
    	h_plot(disp);

    	return self;
}
- print
{
    h_print(disp);
    return self;
}

- setBounds:(const NXRect *)aRect
{
    NXRect margins;
    int		grid, topFlag = 0, leftFlag=0;
    
    bounds = *aRect;
    h_setDrawRect(disp, (rectangle *)  &bounds);
    grid = [graphicView gridSpacing];

    if (disp->xAxis.flags.labelLocation & PLOTTOP) topFlag += 1;
    if (disp->xAxis.flags.scaleLocation & PLOTTOP) topFlag += 1;
    if (disp->yAxis.flags.labelLocation & PLOTRIGHT) leftFlag += 1;
    if (disp->yAxis.flags.scaleLocation & PLOTRIGHT) leftFlag += 1;
    setMarginRect(&margins, &bounds, grid, topFlag, leftFlag);

    h_setMarginRect (disp, (rectangle *) &margins);
    return self;
}


- sizeToNaturalAspectRatio
{
    NXRect	defaultBox;
    
    [graphicView calcDefaultPlotSize:&defaultBox];
    bounds.origin.y += ( NX_HEIGHT(&bounds) - NX_HEIGHT(&defaultBox) );
    bounds.size.height = NX_HEIGHT(&defaultBox);
    bounds.size.width = NX_WIDTH(&defaultBox);
    [self setBounds:&bounds];
    return self;
}

/* Methods supporting Archiving and de-Archiving */
- write:(NXTypedStream *) ts
{
    cutStorElem		*cut;
    dependStorElem	*old;
    display             dlist[] = {NULL, NULL};
    char		*data;
    int			len, lenref;
    int			i;

    [super write:ts];

 /* change some flags for storage to stream */
    if ( hTuple) {
	refFlag = [hTuple isRef];
    }
    fixBinsFlag = h_getFixedBins(disp);
    h_setFixedBins(disp, YES);
    h_setNtByRef(disp, YES, reffilename );
    lenref = strlen(reffilename);
 /* write display to buffer */
    len = h_dispSize( disp );
    NX_ZONEMALLOC( [self zone], data, char, len );
    dlist[0] = disp;
    if ( h_writeMem( data, len, dlist, NULL ) ) {
        printf( "Could not write buffer\n" );
	return self;
    }

 /* restore display in case we are doing copy */
    h_setNtByRef( disp, refFlag, NULL );
    h_setFixedBins( disp, fixBinsFlag );
        
 /* Now archive Plot object to typed stream */
 
    NXWriteTypes( ts, "ccii",    &refFlag, &fixBinsFlag, &len, &lenref );
    NXWriteType(  ts, "*",       &reffilename );
    if ( [Plot version] >= INDEX_VERSION ) {
        NXWriteType( ts, "i", &ntindex );
        NXWriteObjectReference(ts, hTuple);
    }
    NXWriteArray( ts, "c",       len, data );
    NXWriteType(  ts, "c",       &cutHistFlag);
    if ( cutHistFlag ) {
	len = strlen( cutParms.cutFunc );
        NXWriteType( ts, "i",       &len );
	NXWriteType( ts, "{*iffi}", &cutParms ); 
	NXWriteType( ts, "i",       &cutNumber);
    }
    
    i = [cutPlotStor count];
    NXWriteType( ts, "i", &i );
    while ( i-- ) {
        cut = [cutPlotStor elementAt:i];
	NXWriteObjectReference(ts, cut->plot );
	if ( [Plot version] >= INDEX_VERSION ) {
	    cut->name = [cut->plot name];
	    NXWriteType( ts, "%", &cut->name );
	    if ( [Plot version] >= INVERTCUT_VERSION ) {
	        NXWriteType( ts, "c", &cut->invertFlag );
	    }
	}
    }
    if ( dependStor ) {
        i = [dependStor count];
    } else {
        i = 0;
    }
    NXWriteType( ts, "i", &i);
    while ( i-- ) {
        old = [dependStor elementAt:i];
	NXWriteObjectReference( ts, old->plot);
	if ( [Plot version] >= INDEX_VERSION ) {
	    old->name = [old->plot name];
	    NXWriteType( ts, "%", &old->name );
	}
    }
    free( data );    
    
    if ( [Plot version] >= FUNCLIST_VERSION ) {
        NXWriteObject( ts, funcList );
    }
    return self;
}
- read:(NXTypedStream *) ts
{
    cutStorElem		cut;
    dependStorElem	new;
    ntuple		*ntlist;
    display		*dlist;
    char		*data;
    int			len, lenref;
    int			i, count;
    
    [super read:ts];
    uniqueName = namePlot();
    NXReadTypes( ts, "ccii",    &refFlag, &fixBinsFlag, &len, &lenref);
    NX_ZONEMALLOC( [self zone], data, char, len );
    NXReadType(  ts, "*",       &reffilename);
    if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
        NXReadType( ts, "i", &ntindex );
        hTuple = NXReadObject( ts );
	hTuple = nil;	/* will bind by reference later */
    } else {
        ntindex = 0;
	hTuple = nil;
    }
    NXReadArray( ts, "c",       len, data );
    h_readMem( data, len,  &dlist, &ntlist );
    NX_FREE( data );
    disp = dlist[0];
    if ( !hTuple ) disp->tuple = NULL;    /* shouldn't happen */
    NXReadType(  ts, "c",      &cutHistFlag );
    if ( cutHistFlag ) {
        NXReadType( ts, "i",       &len );
	NX_ZONEMALLOC( [self zone], cutParms.cutFunc, char, len+1 );
	NXReadType( ts, "{*iffi}", &cutParms ); 
	NXReadType( ts, "i",       &cutNumber);
    }
    NXReadType(  ts, "i",       &count );
    if ( count ) {
        cutPlotStor = [[Storage allocFromZone:[self zone]] initCount:0
    			             elementSize:sizeof(cutStorElem)
			             description:"@%*"];
    }
    for ( i = 0; i < count; i++ ) {
        cut.plot = NXReadObject( ts );
	if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
	    NXReadType( ts, "%", &cut.name );
	    if ( NXTypedStreamClassVersion(ts, "Plot") >= INVERTCUT_VERSION ) {
		NXReadType( ts, "c", &cut.invertFlag );
	    }
	} else {
	    cut.name = NXUniqueString("HippoPlot.NULL" );
	    cut.invertFlag = FALSE;
	}
	if ( cut.plot ) {
	    cut.name = [cut.plot name];
	}
	[cutPlotStor addElement:&cut.plot];
    }
    NXReadType( ts, "i", &count );
    if ( count ) {
        dependStor = [[Storage allocFromZone:[self zone]] initCount:0
    			             elementSize:sizeof(dependStorElem)
			             description:"@%"];
    }
    for ( i = 0; i < count; i++ ) {
        new.plot = NXReadObject( ts );
	if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
	     NXReadType( ts, "%", &new.name );
	} else {
	    new.name = NXUniqueString("HippoPlot.NULL" );
	}
	if ( new.plot ) {
	    new.name = [new.plot name];
	}
        [dependStor addElement:&new];
    }
    
    if ( NXTypedStreamClassVersion(ts, "Plot") >= FUNCLIST_VERSION ) {
        funcList = NXReadObject( ts );
    }
    
    return self;
}
- free
{
    [self removeAllCuts];
    NX_FREE(reffilename);
    [cutPlotStor free];
    NX_FREE(cutParms.cutFunc);
  /* disp should be attached, but it may not be */
    if ( disp != NULL ) h_freeDisp(disp);
    return [super free];
}

- wasRemovedFrom: gv
{
     if (cutHistFlag && [gv isKindOf:[HGraphicView class]])
     {
	  [[gv cutList] removeObject: self];
     }
     return [super wasRemovedFrom: gv];
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.