ftp.nice.ch/pub/next/graphics/3d/3DViewer.s.tar.gz#/3DViewer/CubeView.m

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

/* Generated by Interface Builder */

#import "CubeView.h"
#import "a3DViewerView.h"
#import "dotAt.h"
#import <stdlib.h>
#import <strings.h>
#import <stdio.h>
#import <dpsclient/wraps.h>
#import <dpsclient/dpsNeXT.h>
#import <appkit/graphics.h>
#import <appkit/Control.h>
#import <appkit/Form.h>
#import <appkit/OpenPanel.h>
#import <appkit/Pasteboard.h>
#import <appkit/Application.h>
#import <appkit/Font.h>
#import <appkit/Slider.h>
#import <soundkit/Sound.h>
#import <math.h>

#define MAXLINE 1000
#define MAXNUM  10000
#define rad_to_deg  (180./M_PI)

@implementation CubeView

+ newFrame:(const NXRect *) frameRect
{
  self = [super newFrame: frameRect];
  [self initStatus];
  return self;
}

+ new;
{
  self = [super new];
  [self initStatus];
  return self;
}

- (id)class
{
    return [myController class];
}

- (const char *)name
{
    return [myController name];
}

- (int)tag
{
    return tag;
}

- (id)controllerObj
{
    return myController;
}

- setController:(id)ctrlr
{
    myController = ctrlr;
    [soundButton getFrame:&sndBtnRect];
    [soundButton removeFromSuperview];
    return self;
}

- setmachHeader:(struct mach_header *)mH
{
    machHeader = mH;
    return self;
}

static char  angle_buffer[20];

- (void)showError:(char *)errorMessage
{
  NXRunAlertPanel("Error", errorMessage, "OK", NULL, NULL);
}

- (void)initStatus
{
  toshow = (datapoints **) calloc(2000, sizeof(datapoints *));
  max_path = 4000;
  path = (float *) calloc(2000, sizeof(float));
  dot_offset[0] = 2.5 * 2 / bounds.size.width;
  dot_offset[1] = 2.5 * 2 / bounds.size.height;
  [self setDrawSize:(NXCoord) 2:(NXCoord) 2];
  [self setDrawOrigin:(NXCoord) -1:(NXCoord) -1];
//showCube = YES;
  showCube = NO;
  showAxes = YES;
//PSonly = NO;
  PSonly = YES;
  vm = [AzimuthMat new];
  openReq = [OpenPanel new];
				// establish the ops array and
				// bounding box for DPSDoUserPath()
  ops[0] = dps_moveto; ops[1] = 32 + 15; ops[2] = dps_lineto;
  boundingBox[0] = bounds.origin.x;
  boundingBox[1] = bounds.origin.y;
  boundingBox[2] = bounds.origin.x + bounds.size.width;
  boundingBox[3] = bounds.origin.y + bounds.size.height;
				// create the template plotting dot
  dot = [[NXImage alloc] initFromSection:"cubedot.tiff"];
// dot = [Bitmap newFromMachO:"cubedot.tiff"];
// dotgstate = [[dot window] gState];
				// force a rescaling
  [self reScale:self];
  tag = 63757374;		// hex ascii for "cust"
}

- (void)printPSCode:sender
{
  PSonly = YES;
  [self display];
  [super printPSCode:self];
  PSonly = NO;
  [self display];
}

- copy:sender
{
  id pb = [Pasteboard new];
  NXStream  *st;
  char      *data;
  int       length;
  int       maxLength;
  
  PSonly = YES;
  [self display];
  [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
  st = NXOpenMemory( NULL, 0, NX_WRITEONLY );
  [self copyPSCodeInside:NULL to:st];
  NXGetMemoryBuffer( st, &data, &length, &maxLength );
  [pb writeType:NXPostScriptPboardType data:data length:length];
  NXCloseMemory( st, NX_FREEBUFFER );
  PSonly = NO;
  [self display];
  return self; 
}


-(BOOL)acceptsFirstResponder
{				/* make this view accept first */
	return YES;		/* responder so it will understand copy */
}

-resignFirstResponder
{
	return self;
}

- setAngleDisplay:anObject
{
  AngleDisplay = anObject;
  return self;
}

- setDistanceSlider:anObject
{
  DistanceSlider = anObject;
  return self;
}

- setThetaSlider:anObject
{
  ThetaSlider = anObject;
  return self;
}

- setPhiSlider:anObject
{
  PhiSlider = anObject;
  return self;
}

- setSoundButton:anObject
{
    soundButton = anObject;
    return self;
}

- Reset:sender
{
  [vm setTheta:0.];
  [ThetaSlider setFloatValue:0.];
  [vm setPhi:0.];
  [PhiSlider setFloatValue:0.];
  [vm setdist:2.0];
  [DistanceSlider setFloatValue:-1/2.];
  [self ShowAngles:sender];
  [self display];
  return self;
}

- (void)ShowAngles:sender
{
  sprintf(angle_buffer, "%6.2f", rad_to_deg*[vm getTheta:self]);
  [AngleDisplay setStringValue:angle_buffer at:0];
  sprintf(angle_buffer, "%6.2f", -rad_to_deg*[vm getPhi:self]); 
  [AngleDisplay setStringValue:angle_buffer at:1];
  sprintf(angle_buffer, "%6.2f", [vm getdist:self]);
  [AngleDisplay setStringValue:angle_buffer at:2];
}

- (float)readPhi
{
    return -rad_to_deg*[vm getPhi:self];
}

- (float)readTheta
{
    return rad_to_deg*[vm getTheta:self];
}

- (float)readInvdist
{
    return [vm getdist:self];
}

- (BOOL)writePhi:(float)phi
{
    float  angle;
    double min,max;
    BOOL   correct=NO;
    angle = phi/rad_to_deg;
    if( angle > (max = [PhiSlider maxValue]) ) {angle = max;correct=YES;}
    if( angle < (min = [PhiSlider minValue]) ) {angle = min;correct=YES;} 
    [vm setPhi:-angle];
    [PhiSlider setFloatValue:angle];
    [self display];
    return correct;
}

- (BOOL)writeTheta:(float)tht
{
    float  angle;
    double min,max;
    BOOL   correct=NO;
    angle = tht/rad_to_deg;
    if( angle > (max = [ThetaSlider maxValue]) ) {angle = max;correct=YES;}
    if( angle < (min = [ThetaSlider minValue]) ) {angle = min;correct=YES;} 
    [vm setTheta:angle];
    [ThetaSlider setFloatValue:angle];
    [self display];
    return correct;
}

- (BOOL)writeInvdist:(float)invd
{
    float  id;
    double min,max;
    BOOL   correct=NO;
    id = -1./invd;
    if( id > (max = [DistanceSlider maxValue]) ) {id = max;correct=YES;}
    if( id < (min = [DistanceSlider minValue]) ) {id = min;correct=YES;}
    id = -1./id;
    [vm setdist:id];
    [DistanceSlider setFloatValue:-1./id];
    [self display];
    return correct;
}

- (void)setViewParameters
{
    [self writePhi:currentPhi];
    [self writeTheta:currentTheta];
    [self writeInvdist:currentDist];
}
    
- setTheta:sender
{
  [vm setTheta:[sender floatValue]];
  [self display];
  [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
  return self;
}

- setPhi:sender
{
  [vm setPhi:-[sender floatValue]];
  [self display];
  [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
  return self;
}

- setdist:sender
{
  float dist;

  dist = [sender floatValue];
  if (dist < 1.8) {
    [self showError: "Distance must be greater than 1.8"];
    return self;
  }
  [vm setdist:dist];
  [DistanceSlider setFloatValue:-1./dist];
  [self display];
  return self;
}

- setinvdist:sender
{
  [vm setinvdist:-[sender floatValue]];
  [self display];
  [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
  return self;
}

- setTheta_degrees:sender
{
  float angle = [sender floatValue]/rad_to_deg;

  [vm setTheta:angle];
  [ThetaSlider setFloatValue:angle];
  [self display];
  return self;
}

- setPhi_degrees:sender
{
  float angle = [sender floatValue]/rad_to_deg;

  [vm setPhi:-angle];
  [PhiSlider setFloatValue:angle];
  [self display];
  return self;
}

- toggleCube:sender
{
  showCube = showCube ? NO : YES;
  [self display];
  [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
  return self;
}

- toggleAxes:sender
{
  showAxes = showAxes ? NO : YES;
  [self display];
  [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
  return self;
}

- reScale:sender
{
  float maxval[3], minval[3], limits[6], *fpt;
  datapoints **all_lines = toshow, *thisline;
  short int i, count;

  if (*all_lines == (datapoints *) NULL) { /* no data - set to defaults */
    limits[0] = limits[1] = limits[2] = 0.;
    limits[3] = limits[4] = limits[5] = 1.;
    [self setlimits:limits];
    return self;
  }
  for(i = 0; i < 3; i++)
    minval[i] = maxval[i] = *((*all_lines)->displayed[i]);
  while (thisline = *all_lines++) {
    for(i = 0; i < 3; i++) {
      count = thisline->npts;
      fpt = thisline->displayed[i];
      while (count--) {
	if (*fpt < minval[i]) minval[i] = *fpt;
	if (*fpt > maxval[i]) maxval[i] = *fpt;
	fpt++;
      }
    }
  }
  for(i = 0; i < 3; i++) {
    if (minval[i] >= maxval[i]) minval[i] = maxval[i] - 1;
    limits[i] = minval[i] - 0.035*(maxval[i] - minval[i]);
    limits[i + 3] = maxval[i] + 0.035*(maxval[i] - minval[i]);
  }
  [self setlimits:limits];
  return self;
}
 
- (void)setViewSound:(id)sndObj
{
    if( !viewSound ) viewSound = [Sound new];
    [viewSound copySound:sndObj];
    [self showSoundButton];
    [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
}

- (void)deleteViewSound
{
    if( viewSound ) {
       [viewSound free];
       viewSound = 0x0;
       [[self window] disableFlushWindow];
       [soundButton removeFromSuperview];
       [[self window] display];
       [[self window] reenableFlushWindow];
       [[self window] flushWindow];
       [myController perform:@selector(resetFirstResponder:) with:self
                                  afterDelay:1 cancelPrevious:YES];
   }
}

- (void)showSoundButton
{
    [[self window] disableFlushWindow];
    [[self superview] addSubview:soundButton];
    [soundButton moveTo:sndBtnRect.origin.x :sndBtnRect.origin.y];
    [[self window] display];
    [[self window] reenableFlushWindow];
    [[self window] flushWindow];
}

- playSound:sender
{
    [viewSound play];
    return self;
}

- (char *)currentDataFile
{
    return dataFileName;
}

- openData:sender
{
  const char *fileName;
  const char *const ext[2] = {"3d", NULL};

  if([openReq runModalForTypes:ext] && (fileName = [openReq filename])) {
     [self readData:fileName];
     if( dataFileName ) free(dataFileName);
     dataFileName = malloc(BUFSIZ);
     strcpy(dataFileName,fileName);
  }
  else
    [self showError:"No file chosen or could not open file"];
  return self;
}

static void float_copy (register int n, register float * to, register int
		   to_inc, register float * from, register int from_inc)
{
  if (n > 0)
    while (n--) {
      *to = *from;
      to += to_inc;
      from += from_inc;
    }
}

- (BOOL) readData:(const char *)filename
{
  FILE *input;
  float *scratch, *fpt;
  char thisline[MAXLINE], *cpt, c;
  long int count, ncols, nchars, nlines, expected_cols;
  datapoints **all_data = toshow, *thesedata;

  scratch = (float *) calloc(MAXNUM, sizeof(float));
  if((input = fopen(filename, "r")) == NULL) {
    sprintf(thisline, "Unable to open file %s", filename);
    [self showError:thisline];
    return NO;
  }
				/* release existing data */
  while ((thesedata = *all_data) != (datapoints *) NULL) {
    free(thesedata->displayed);
    count = 0;
    while(thesedata->all[count] != (float *) NULL) {
      free(thesedata->all[count]);
      count++;
    }
    free(thesedata->all);
    free(thesedata);
    *all_data++ = (datapoints *) NULL;
  }
  all_data = toshow;		/* read new data */
  fgets(thisline, MAXLINE, input);
  while (!feof(input)) {
    if(!sscanf(thisline, " %1s ", &c)) {
      sprintf(thisline, "Unable to read a specification from %s", filename);
      [self showError:thisline];
      return NO;
    }
    switch(c) {
    case 'N': case 'n': case 'L': case 'l': case 'P': case 'p': case 'B':
    case 'b':
      fgets(thisline, MAXLINE, input); 	/* load a new line */
      break;
    case 'C': case 'c':			/* next line contains an RGB color */
      fgets(thisline, MAXLINE, input);
      break;
    default:
      sprintf(thisline, "the beginning of %s doesn't look like a specification",
	      filename);
      [self showError:thisline];
      return NO;
    }
    cpt = thisline; fpt = scratch; ncols = 0;
    while(sscanf(cpt, "%f%n", fpt, &nchars) == 1) { /* removed space bewteen %f %n */
      cpt += nchars;
      ncols++;
      fpt++;
    }
    if (ncols == 0) {
      sprintf(thisline, "First line of %s does not contain numeric data",
	      filename);
      [self showError:thisline];
      return NO;
    }
				/* digest the rest of the lines */
    expected_cols = ncols; nlines = 1;
    while(fgets(thisline, MAXLINE, input) != NULL) {
      cpt = thisline; ncols = 0;
      while(sscanf(cpt, "%f%n", fpt, &nchars) == 1) { /* removed space bewteen %f %n */
	cpt += nchars;
	ncols++;
	fpt++;
      }
      if (!ncols) break;
      nlines++;
      if (ncols != expected_cols) {
	sprintf(thisline,
		"Expected %d values but got %d values at line %d\n",
		expected_cols, ncols, nlines);
	[self showError:thisline];
	return NO;
      }
    }				/* create the datapoints structure and */
				/* store the values */
    thesedata = *all_data++ = (datapoints *) malloc(sizeof(datapoints));
    switch(c) {
    case 'N': case 'n':
      thesedata->type = NEITHER;
      break;
    case 'L': case 'l':
      thesedata->type = LINES;
      break;
    case 'P': case 'p':
      thesedata->type = POINTS;
      break;
    case 'B': case 'b':
      thesedata->type = BOTH;
      break;
    case 'C': case 'c':
      thesedata->type = COLOR;
      break;
    }
    thesedata->all = (float **)calloc((size_t) expected_cols + 1,
			      (size_t) sizeof(float *));
    for(ncols = 0; ncols < expected_cols; ncols++) {
      thesedata->all[ncols] = (float *)calloc((size_t) nlines, sizeof(float));
      float_copy(nlines, thesedata->all[ncols], 1, scratch + ncols,
		 expected_cols);
    }
    thesedata->all[expected_cols] = (float *)NULL;
				/* make sure path is large enough */
    if (2*nlines > max_path)
      realloc(path, (2*nlines)*sizeof(float));
    thesedata->npts = nlines;
    memmove(thesedata->displayed = (float **) calloc(3, sizeof(float *)),
	    thesedata->all, 3 * sizeof(float *));	  
  }

  [self reScale:self];
  free(scratch);
  fclose(input);
  [self display];
  return YES;
}


static int x_seq[] = {0,3,3,0,0,0,3,3,0,0,3,3,3,3,0,0};
static int y_seq[] = {1,1,4,4,1,1,1,4,4,1,1,1,4,4,4,4};
static int z_seq[] = {2,2,2,2,2,5,5,5,5,5,5,2,2,5,5,2};

-setlimits:(float *)newlimits
{
  int i, n, tickdir;
  float tick, width;

  for (n = 0; n < 16; n++) {	/* create cube */
    cube[0][n] = newlimits[x_seq[n]];
    cube[1][n] = newlimits[y_seq[n]];
    cube[2][n] = newlimits[z_seq[n]];
  }
  for (n = 0; n < 54; n++) {	/* create axes */
    axes[0][n] = newlimits[0];
    axes[1][n] = newlimits[1];
    axes[2][n] = newlimits[2];
  }
  for (i = 0; i < 3; i++) {
    tickdir = (i == 0) ? 1 : 0;
    tick = 0.03 * (newlimits[tickdir + 3] - newlimits[tickdir]);
    width = newlimits[i + 3] - newlimits[i];
    n = 18 * i;
    axes[i][n+1] = axes[i][n+2] = axes[i][n+3] = axes[i][n+4] =
      newlimits[i] + 0.25 * width;
    axes[i][n+5] = axes[i][n+6] = axes[i][n+7] = axes[i][n+8] =
      newlimits[i] + 0.5 * width;
    axes[i][n+9] = axes[i][n+10] = axes[i][n+11] = axes[i][n+12] =
      newlimits[i] + 0.75 * width;
    axes[i][n+13] = axes[i][n+17] = newlimits[i] + 0.87 * width;
    axes[i][n+14] = axes[i][n+16] = newlimits[i] + 0.84 * width;
    axes[i][n+15] = newlimits[i] + width;
    axes[tickdir][n+2] = axes[tickdir][n+6] = axes[tickdir][n+10] =
      axes[tickdir][n+14] = newlimits[tickdir] + tick;
    axes[tickdir][n+3] = axes[tickdir][n+7] = axes[tickdir][n+11] =
      axes[tickdir][n+16] = newlimits[tickdir] - tick;
  }
  [vm setlimits:newlimits];
  [self Reset:self];
  return self;
}

- drawSelf:(NXRect*)r :(int)c
{
  NXColor  lineColor;
  datapoints **all_lines = toshow, *thisline;
  
  NXEraseRect(&bounds);
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (showCube) {
    ops[1] = 32 + 16 - 1;
    DPSDoUserPath ([vm as_DPSpath :16 :cube[0] :cube[1] :cube[2] :path],
		   32, dps_float, ops, 3, boundingBox, dps_ustroke);
  }
  if (showAxes) {
    ops[1] = 32 + 54 - 1;
    DPSDoUserPath ([vm as_DPSpath :54 :axes[0] :axes[1] :axes[2] :path],
		   108, dps_float, ops, 3, boundingBox, dps_ustroke);
  }
  while((thisline = *all_lines++) != (datapoints *) NULL) {
    if (thisline->type & 0x4) { 		/* set the RGB color */
      lineColor = NXConvertRGBToColor(*(thisline->displayed)[0],
                                      *(thisline->displayed)[1],
				      *(thisline->displayed)[2]);
      NXSetColor(lineColor);
      continue;
    }
    if( thisline->type & 0x1 && thisline->npts > 1 ) { /* draw the line */
      ops[1] = thisline->npts + 32 - 1;
      DPSDoUserPath([vm as_DPSpath :thisline->npts
		     :(thisline->displayed)[0] :(thisline->displayed)[1]
		     :(thisline->displayed)[2] :path],
		    2*(thisline->npts), dps_float,
		    ops, 3, boundingBox, dps_ustroke);
    }
    if (thisline->type & 0x2) {			/* draw points */
      if (PSonly)
	dotAtPSonly([vm as_DPSpath :thisline->npts
		     :(thisline->displayed)[0] :(thisline->displayed)[1]
		     :(thisline->displayed)[2] :path],
		    2 *(thisline->npts), *dot_offset);
      else
	dotAt([vm as_DPSpath :thisline->npts
	       :(thisline->displayed)[0] :(thisline->displayed)[1]
	       :(thisline->displayed)[2] :path offset:dot_offset],
	      2 * (thisline->npts), dotgstate);
    }
  }
  return self;
}

- (BOOL)acceptsFirstMouse
{
    return YES;
}

- mouseDown:(NXEvent *)e
{
    if( e->data.mouse.click == 2 ) {
       [myController inspectCustomComponent];
       return self;
    }

    if( e->flags & NX_COMMANDMASK ) {
       [self toggleCube:self];
    }
    else if (e->flags & NX_ALTERNATEMASK ) {
       [self toggleAxes:self];
    } else {
       [myController refreshInspectorPanel];
    }
    return [super mouseDown:e];
}

- free
{
  int  count;
  datapoints **all_data = toshow, *thesedata;
  
  while ((thesedata = *all_data) != (datapoints *) NULL) {
    free(thesedata->displayed);
    count = 0;
    while(thesedata->all[count] != (float *) NULL) {
      free(thesedata->all[count]);
      count++;
    }
    free(thesedata->all);
    free(thesedata);
    *all_data++ = (datapoints *) NULL;
  }
  free(toshow);
  if( viewSound ) [viewSound free];
  if( dataFileName ) free(dataFileName);
  return [super free];
}

- write:(NXTypedStream *)stream
{
    int  cols, nstruct, ns;
    datapoints **all_data = toshow, *thesedata;
    
    [super write:stream];
    NXWriteType(stream, "i", &max_path);
    nstruct = 0;
    while((thesedata = *all_data++) != (datapoints *) NULL) nstruct++;
    NXWriteType(stream, "i", &nstruct);
    all_data = toshow;
    for( ns=0; ns<nstruct; ns++) {
       thesedata = *all_data++;
       NXWriteTypes(stream, "ii", &thesedata->npts, &thesedata->type);
       cols = 0;
       while( thesedata->all[cols] != (float *) NULL) {
	  NXWriteArray(stream, "f", thesedata->npts, *(&thesedata->all[cols]));
          cols++;
       }
    }
    NXWriteObject(stream, AngleDisplay);
    NXWriteObject(stream, PhiSlider);
    currentPhi = -rad_to_deg * [vm getPhi:self];
    NXWriteObject(stream, ThetaSlider);
    currentTheta = rad_to_deg * [vm getTheta:self];
    NXWriteObject(stream, DistanceSlider);
    currentDist = [vm getdist:self];
    NXWriteTypes(stream, "fff", &currentPhi, &currentTheta, &currentDist);
    NXWriteObject(stream, soundButton);
    NXWriteRect(stream, &sndBtnRect);
    NXWriteObject(stream, viewSound);
    NXWriteTypes(stream, "ccc", &showCube, &showAxes, &PSonly);
    NXWriteType(stream, "*", &dataFileName);
    NXWriteObject(stream, myController);
    return self;
}

- read:(NXTypedStream *)stream
{
    int  cols, nstruct, ns;
    datapoints **all_data, *thesedata;
    int  ncols = 3;
    
    [super read:stream];
    NXReadType(stream, "i", &max_path);
    NXReadType(stream, "i", &nstruct);
    toshow = (datapoints **) calloc(1000, sizeof(datapoints *));
    all_data = toshow;
    for( ns=0; ns<nstruct; ns++) {
       thesedata = *all_data++ = (datapoints *) malloc(sizeof(datapoints));
       NXReadTypes(stream, "ii", &thesedata->npts, &thesedata->type);
       thesedata->all = (float **)calloc((size_t) ncols+1, (size_t) sizeof(float *));
       for( cols=0; cols<ncols; cols++ ) {
          thesedata->all[cols] = (float *)calloc((size_t) thesedata->npts, sizeof(float));
	  NXReadArray(stream, "f", thesedata->npts, *(&thesedata->all[cols]));
       }
       thesedata->all[ncols] = (float *)NULL;
       memmove(thesedata->displayed = (float **) calloc(3, sizeof(float *)),
	    thesedata->all, 3 * sizeof(float *));
    }
    AngleDisplay = NXReadObject(stream);
    PhiSlider = NXReadObject(stream);
    ThetaSlider = NXReadObject(stream);
    DistanceSlider = NXReadObject(stream);
    NXReadTypes(stream, "fff", &currentPhi, &currentTheta, &currentDist);
    soundButton = NXReadObject(stream);
    NXReadRect(stream, &sndBtnRect);
    viewSound = NXReadObject(stream);
    NXReadTypes(stream, "ccc", &showCube, &showAxes, &PSonly);
    NXReadType(stream, "*", &dataFileName);
    myController = NXReadObject(stream);
    return self;
}

- awake
{
    [super awake];
    [myController setViews:self];
    path = (float *) calloc(2000, sizeof(float));
    dot_offset[0] = 2.5 * 2 / bounds.size.width;
    dot_offset[1] = 2.5 * 2 / bounds.size.height;
    [self setDrawSize:(NXCoord) 2:(NXCoord) 2];
    [self setDrawOrigin:(NXCoord) -1:(NXCoord) -1];
    showCube = NO;
    showAxes = YES;
    PSonly = YES;
    ops[0] = dps_moveto; ops[1] = 32 + 15; ops[2] = dps_lineto;
    boundingBox[0] = bounds.origin.x;
    boundingBox[1] = bounds.origin.y;
    boundingBox[2] = bounds.origin.x + bounds.size.width;
    boundingBox[3] = bounds.origin.y + bounds.size.height;
    vm = [AzimuthMat new];
    openReq = [OpenPanel new];
    [self reScale:self];
    tag = 63757374;		// hex ascii for "cust"
    [self setViewParameters];
    return self;
}

@end

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