home *** CD-ROM | disk | FTP | other *** search
/ Los Alamos National Laboratory / LANL_CD.ISO / software / medview / custom / source / 3dcustom / cubeview.m < prev    next >
Encoding:
Text File  |  1992-02-12  |  21.0 KB  |  821 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "CubeView.h"
  5. #import "a3DViewerView.h"
  6. #import "dotAt.h"
  7. #import <stdlib.h>
  8. #import <strings.h>
  9. #import <stdio.h>
  10. #import <dpsclient/wraps.h>
  11. #import <dpsclient/dpsNeXT.h>
  12. #import <appkit/graphics.h>
  13. #import <appkit/Control.h>
  14. #import <appkit/Form.h>
  15. #import <appkit/OpenPanel.h>
  16. #import <appkit/Pasteboard.h>
  17. #import <appkit/Application.h>
  18. #import <appkit/Font.h>
  19. #import <appkit/Slider.h>
  20. #import <soundkit/Sound.h>
  21. #import <math.h>
  22.  
  23. #define MAXLINE 1000
  24. #define MAXNUM  10000
  25. #define rad_to_deg  (180./M_PI)
  26.  
  27. @implementation CubeView
  28.  
  29. + newFrame:(const NXRect *) frameRect
  30. {
  31.   self = [super newFrame: frameRect];
  32.   [self initStatus];
  33.   return self;
  34. }
  35.  
  36. + new;
  37. {
  38.   self = [super new];
  39.   [self initStatus];
  40.   return self;
  41. }
  42.  
  43. - (id)class
  44. {
  45.     return [myController class];
  46. }
  47.  
  48. - (const char *)name
  49. {
  50.     return [myController name];
  51. }
  52.  
  53. - (int)tag
  54. {
  55.     return tag;
  56. }
  57.  
  58. - (id)controllerObj
  59. {
  60.     return myController;
  61. }
  62.  
  63. - setController:(id)ctrlr
  64. {
  65.     myController = ctrlr;
  66.     [soundButton getFrame:&sndBtnRect];
  67.     [soundButton removeFromSuperview];
  68.     return self;
  69. }
  70.  
  71. - setmachHeader:(struct mach_header *)mH
  72. {
  73.     machHeader = mH;
  74.     return self;
  75. }
  76.  
  77. static char  angle_buffer[20];
  78.  
  79. - (void)showError:(char *)errorMessage
  80. {
  81.   NXRunAlertPanel("Error", errorMessage, "OK", NULL, NULL);
  82. }
  83.  
  84. - (void)initStatus
  85. {
  86.   toshow = (datapoints **) calloc(2000, sizeof(datapoints *));
  87.   max_path = 4000;
  88.   path = (float *) calloc(2000, sizeof(float));
  89.   dot_offset[0] = 2.5 * 2 / bounds.size.width;
  90.   dot_offset[1] = 2.5 * 2 / bounds.size.height;
  91.   [self setDrawSize:(NXCoord) 2:(NXCoord) 2];
  92.   [self setDrawOrigin:(NXCoord) -1:(NXCoord) -1];
  93. //showCube = YES;
  94.   showCube = NO;
  95.   showAxes = YES;
  96. //PSonly = NO;
  97.   PSonly = YES;
  98.   vm = [AzimuthMat new];
  99.   openReq = [OpenPanel new];
  100.                 // establish the ops array and
  101.                 // bounding box for DPSDoUserPath()
  102.   ops[0] = dps_moveto; ops[1] = 32 + 15; ops[2] = dps_lineto;
  103.   boundingBox[0] = bounds.origin.x;
  104.   boundingBox[1] = bounds.origin.y;
  105.   boundingBox[2] = bounds.origin.x + bounds.size.width;
  106.   boundingBox[3] = bounds.origin.y + bounds.size.height;
  107.                 // create the template plotting dot
  108.   dot = [[NXImage alloc] initFromSection:"cubedot.tiff"];
  109. // dot = [Bitmap newFromMachO:"cubedot.tiff"];
  110. // dotgstate = [[dot window] gState];
  111.                 // force a rescaling
  112.   [self reScale:self];
  113.   tag = 63757374;        // hex ascii for "cust"
  114. }
  115.  
  116. - (void)printPSCode:sender
  117. {
  118.   PSonly = YES;
  119.   [self display];
  120.   [super printPSCode:self];
  121.   PSonly = NO;
  122.   [self display];
  123. }
  124.  
  125. - copy:sender
  126. {
  127.   id pb = [Pasteboard new];
  128.   NXStream  *st;
  129.   char      *data;
  130.   int       length;
  131.   int       maxLength;
  132.   
  133.   PSonly = YES;
  134.   [self display];
  135.   [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
  136.   st = NXOpenMemory( NULL, 0, NX_WRITEONLY );
  137.   [self copyPSCodeInside:NULL to:st];
  138.   NXGetMemoryBuffer( st, &data, &length, &maxLength );
  139.   [pb writeType:NXPostScriptPboardType data:data length:length];
  140.   NXCloseMemory( st, NX_FREEBUFFER );
  141.   PSonly = NO;
  142.   [self display];
  143.   return self; 
  144. }
  145.  
  146.  
  147. -(BOOL)acceptsFirstResponder
  148. {                /* make this view accept first */
  149.     return YES;        /* responder so it will understand copy */
  150. }
  151.  
  152. -resignFirstResponder
  153. {
  154.     return self;
  155. }
  156.  
  157. - setAngleDisplay:anObject
  158. {
  159.   AngleDisplay = anObject;
  160.   return self;
  161. }
  162.  
  163. - setDistanceSlider:anObject
  164. {
  165.   DistanceSlider = anObject;
  166.   return self;
  167. }
  168.  
  169. - setThetaSlider:anObject
  170. {
  171.   ThetaSlider = anObject;
  172.   return self;
  173. }
  174.  
  175. - setPhiSlider:anObject
  176. {
  177.   PhiSlider = anObject;
  178.   return self;
  179. }
  180.  
  181. - setSoundButton:anObject
  182. {
  183.     soundButton = anObject;
  184.     return self;
  185. }
  186.  
  187. - Reset:sender
  188. {
  189.   [vm setTheta:0.];
  190.   [ThetaSlider setFloatValue:0.];
  191.   [vm setPhi:0.];
  192.   [PhiSlider setFloatValue:0.];
  193.   [vm setdist:2.0];
  194.   [DistanceSlider setFloatValue:-1/2.];
  195.   [self ShowAngles:sender];
  196.   [self display];
  197.   return self;
  198. }
  199.  
  200. - (void)ShowAngles:sender
  201. {
  202.   sprintf(angle_buffer, "%6.2f", rad_to_deg*[vm getTheta:self]);
  203.   [AngleDisplay setStringValue:angle_buffer at:0];
  204.   sprintf(angle_buffer, "%6.2f", -rad_to_deg*[vm getPhi:self]); 
  205.   [AngleDisplay setStringValue:angle_buffer at:1];
  206.   sprintf(angle_buffer, "%6.2f", [vm getdist:self]);
  207.   [AngleDisplay setStringValue:angle_buffer at:2];
  208. }
  209.  
  210. - (float)readPhi
  211. {
  212.     return -rad_to_deg*[vm getPhi:self];
  213. }
  214.  
  215. - (float)readTheta
  216. {
  217.     return rad_to_deg*[vm getTheta:self];
  218. }
  219.  
  220. - (float)readInvdist
  221. {
  222.     return [vm getdist:self];
  223. }
  224.  
  225. - (BOOL)writePhi:(float)phi
  226. {
  227.     float  angle;
  228.     double min,max;
  229.     BOOL   correct=NO;
  230.     angle = phi/rad_to_deg;
  231.     if( angle > (max = [PhiSlider maxValue]) ) {angle = max;correct=YES;}
  232.     if( angle < (min = [PhiSlider minValue]) ) {angle = min;correct=YES;} 
  233.     [vm setPhi:-angle];
  234.     [PhiSlider setFloatValue:angle];
  235.     [self display];
  236.     return correct;
  237. }
  238.  
  239. - (BOOL)writeTheta:(float)tht
  240. {
  241.     float  angle;
  242.     double min,max;
  243.     BOOL   correct=NO;
  244.     angle = tht/rad_to_deg;
  245.     if( angle > (max = [ThetaSlider maxValue]) ) {angle = max;correct=YES;}
  246.     if( angle < (min = [ThetaSlider minValue]) ) {angle = min;correct=YES;} 
  247.     [vm setTheta:angle];
  248.     [ThetaSlider setFloatValue:angle];
  249.     [self display];
  250.     return correct;
  251. }
  252.  
  253. - (BOOL)writeInvdist:(float)invd
  254. {
  255.     float  id;
  256.     double min,max;
  257.     BOOL   correct=NO;
  258.     id = -1./invd;
  259.     if( id > (max = [DistanceSlider maxValue]) ) {id = max;correct=YES;}
  260.     if( id < (min = [DistanceSlider minValue]) ) {id = min;correct=YES;}
  261.     id = -1./id;
  262.     [vm setdist:id];
  263.     [DistanceSlider setFloatValue:-1./id];
  264.     [self display];
  265.     return correct;
  266. }
  267.  
  268. - (void)setViewParameters
  269. {
  270.     [self writePhi:currentPhi];
  271.     [self writeTheta:currentTheta];
  272.     [self writeInvdist:currentDist];
  273. }
  274.     
  275. - setTheta:sender
  276. {
  277.   [vm setTheta:[sender floatValue]];
  278.   [self display];
  279.   [myController perform:@selector(resetFirstResponder:) with:self
  280.                                   afterDelay:1 cancelPrevious:YES];
  281.   return self;
  282. }
  283.  
  284. - setPhi:sender
  285. {
  286.   [vm setPhi:-[sender floatValue]];
  287.   [self display];
  288.   [myController perform:@selector(resetFirstResponder:) with:self
  289.                                   afterDelay:1 cancelPrevious:YES];
  290.   return self;
  291. }
  292.  
  293. - setdist:sender
  294. {
  295.   float dist;
  296.  
  297.   dist = [sender floatValue];
  298.   if (dist < 1.8) {
  299.     [self showError: "Distance must be greater than 1.8"];
  300.     return self;
  301.   }
  302.   [vm setdist:dist];
  303.   [DistanceSlider setFloatValue:-1./dist];
  304.   [self display];
  305.   return self;
  306. }
  307.  
  308. - setinvdist:sender
  309. {
  310.   [vm setinvdist:-[sender floatValue]];
  311.   [self display];
  312.   [myController perform:@selector(resetFirstResponder:) with:self
  313.                                   afterDelay:1 cancelPrevious:YES];
  314.   return self;
  315. }
  316.  
  317. - setTheta_degrees:sender
  318. {
  319.   float angle = [sender floatValue]/rad_to_deg;
  320.  
  321.   [vm setTheta:angle];
  322.   [ThetaSlider setFloatValue:angle];
  323.   [self display];
  324.   return self;
  325. }
  326.  
  327. - setPhi_degrees:sender
  328. {
  329.   float angle = [sender floatValue]/rad_to_deg;
  330.  
  331.   [vm setPhi:-angle];
  332.   [PhiSlider setFloatValue:angle];
  333.   [self display];
  334.   return self;
  335. }
  336.  
  337. - toggleCube:sender
  338. {
  339.   showCube = showCube ? NO : YES;
  340.   [self display];
  341.   [myController perform:@selector(resetFirstResponder:) with:self
  342.                                   afterDelay:1 cancelPrevious:YES];
  343.   return self;
  344. }
  345.  
  346. - toggleAxes:sender
  347. {
  348.   showAxes = showAxes ? NO : YES;
  349.   [self display];
  350.   [myController perform:@selector(resetFirstResponder:) with:self
  351.                                   afterDelay:1 cancelPrevious:YES];
  352.   return self;
  353. }
  354.  
  355. - reScale:sender
  356. {
  357.   float maxval[3], minval[3], limits[6], *fpt;
  358.   datapoints **all_lines = toshow, *thisline;
  359.   short int i, count;
  360.  
  361.   if (*all_lines == (datapoints *) NULL) { /* no data - set to defaults */
  362.     limits[0] = limits[1] = limits[2] = 0.;
  363.     limits[3] = limits[4] = limits[5] = 1.;
  364.     [self setlimits:limits];
  365.     return self;
  366.   }
  367.   for(i = 0; i < 3; i++)
  368.     minval[i] = maxval[i] = *((*all_lines)->displayed[i]);
  369.   while (thisline = *all_lines++) {
  370.     for(i = 0; i < 3; i++) {
  371.       count = thisline->npts;
  372.       fpt = thisline->displayed[i];
  373.       while (count--) {
  374.     if (*fpt < minval[i]) minval[i] = *fpt;
  375.     if (*fpt > maxval[i]) maxval[i] = *fpt;
  376.     fpt++;
  377.       }
  378.     }
  379.   }
  380.   for(i = 0; i < 3; i++) {
  381.     if (minval[i] >= maxval[i]) minval[i] = maxval[i] - 1;
  382.     limits[i] = minval[i] - 0.035*(maxval[i] - minval[i]);
  383.     limits[i + 3] = maxval[i] + 0.035*(maxval[i] - minval[i]);
  384.   }
  385.   [self setlimits:limits];
  386.   return self;
  387. }
  388.  
  389. - (void)setViewSound:(id)sndObj
  390. {
  391.     if( !viewSound ) viewSound = [Sound new];
  392.     [viewSound copySound:sndObj];
  393.     [self showSoundButton];
  394.     [myController perform:@selector(resetFirstResponder:) with:self
  395.                                   afterDelay:1 cancelPrevious:YES];
  396. }
  397.  
  398. - (void)deleteViewSound
  399. {
  400.     if( viewSound ) {
  401.        [viewSound free];
  402.        viewSound = 0x0;
  403.        [[self window] disableFlushWindow];
  404.        [soundButton removeFromSuperview];
  405.        [[self window] display];
  406.        [[self window] reenableFlushWindow];
  407.        [[self window] flushWindow];
  408.        [myController perform:@selector(resetFirstResponder:) with:self
  409.                                   afterDelay:1 cancelPrevious:YES];
  410.    }
  411. }
  412.  
  413. - (void)showSoundButton
  414. {
  415.     [[self window] disableFlushWindow];
  416.     [[self superview] addSubview:soundButton];
  417.     [soundButton moveTo:sndBtnRect.origin.x :sndBtnRect.origin.y];
  418.     [[self window] display];
  419.     [[self window] reenableFlushWindow];
  420.     [[self window] flushWindow];
  421. }
  422.  
  423. - playSound:sender
  424. {
  425.     [viewSound play];
  426.     return self;
  427. }
  428.  
  429. - (char *)currentDataFile
  430. {
  431.     return dataFileName;
  432. }
  433.  
  434. - openData:sender
  435. {
  436.   const char *fileName;
  437.   const char *const ext[2] = {"3d", NULL};
  438.  
  439.   if([openReq runModalForTypes:ext] && (fileName = [openReq filename])) {
  440.      [self readData:fileName];
  441.      if( dataFileName ) free(dataFileName);
  442.      dataFileName = malloc(BUFSIZ);
  443.      strcpy(dataFileName,fileName);
  444.   }
  445.   else
  446.     [self showError:"No file chosen or could not open file"];
  447.   return self;
  448. }
  449.  
  450. static void float_copy (register int n, register float * to, register int
  451.            to_inc, register float * from, register int from_inc)
  452. {
  453.   if (n > 0)
  454.     while (n--) {
  455.       *to = *from;
  456.       to += to_inc;
  457.       from += from_inc;
  458.     }
  459. }
  460.  
  461. - (BOOL) readData:(const char *)filename
  462. {
  463.   FILE *input;
  464.   float *scratch, *fpt;
  465.   char thisline[MAXLINE], *cpt, c;
  466.   long int count, ncols, nchars, nlines, expected_cols;
  467.   datapoints **all_data = toshow, *thesedata;
  468.  
  469.   scratch = (float *) calloc(MAXNUM, sizeof(float));
  470.   if((input = fopen(filename, "r")) == NULL) {
  471.     sprintf(thisline, "Unable to open file %s", filename);
  472.     [self showError:thisline];
  473.     return NO;
  474.   }
  475.                 /* release existing data */
  476.   while ((thesedata = *all_data) != (datapoints *) NULL) {
  477.     free(thesedata->displayed);
  478.     count = 0;
  479.     while(thesedata->all[count] != (float *) NULL) {
  480.       free(thesedata->all[count]);
  481.       count++;
  482.     }
  483.     free(thesedata->all);
  484.     free(thesedata);
  485.     *all_data++ = (datapoints *) NULL;
  486.   }
  487.   all_data = toshow;        /* read new data */
  488.   fgets(thisline, MAXLINE, input);
  489.   while (!feof(input)) {
  490.     if(!sscanf(thisline, " %1s ", &c)) {
  491.       sprintf(thisline, "Unable to read a specification from %s", filename);
  492.       [self showError:thisline];
  493.       return NO;
  494.     }
  495.     switch(c) {
  496.     case 'N': case 'n': case 'L': case 'l': case 'P': case 'p': case 'B':
  497.     case 'b':
  498.       fgets(thisline, MAXLINE, input);     /* load a new line */
  499.       break;
  500.     case 'C': case 'c':            /* next line contains an RGB color */
  501.       fgets(thisline, MAXLINE, input);
  502.       break;
  503.     default:
  504.       sprintf(thisline, "the beginning of %s doesn't look like a specification",
  505.           filename);
  506.       [self showError:thisline];
  507.       return NO;
  508.     }
  509.     cpt = thisline; fpt = scratch; ncols = 0;
  510.     while(sscanf(cpt, "%f%n", fpt, &nchars) == 1) { /* removed space bewteen %f %n */
  511.       cpt += nchars;
  512.       ncols++;
  513.       fpt++;
  514.     }
  515.     if (ncols == 0) {
  516.       sprintf(thisline, "First line of %s does not contain numeric data",
  517.           filename);
  518.       [self showError:thisline];
  519.       return NO;
  520.     }
  521.                 /* digest the rest of the lines */
  522.     expected_cols = ncols; nlines = 1;
  523.     while(fgets(thisline, MAXLINE, input) != NULL) {
  524.       cpt = thisline; ncols = 0;
  525.       while(sscanf(cpt, "%f%n", fpt, &nchars) == 1) { /* removed space bewteen %f %n */
  526.     cpt += nchars;
  527.     ncols++;
  528.     fpt++;
  529.       }
  530.       if (!ncols) break;
  531.       nlines++;
  532.       if (ncols != expected_cols) {
  533.     sprintf(thisline,
  534.         "Expected %d values but got %d values at line %d\n",
  535.         expected_cols, ncols, nlines);
  536.     [self showError:thisline];
  537.     return NO;
  538.       }
  539.     }                /* create the datapoints structure and */
  540.                 /* store the values */
  541.     thesedata = *all_data++ = (datapoints *) malloc(sizeof(datapoints));
  542.     switch(c) {
  543.     case 'N': case 'n':
  544.       thesedata->type = NEITHER;
  545.       break;
  546.     case 'L': case 'l':
  547.       thesedata->type = LINES;
  548.       break;
  549.     case 'P': case 'p':
  550.       thesedata->type = POINTS;
  551.       break;
  552.     case 'B': case 'b':
  553.       thesedata->type = BOTH;
  554.       break;
  555.     case 'C': case 'c':
  556.       thesedata->type = COLOR;
  557.       break;
  558.     }
  559.     thesedata->all = (float **)calloc((size_t) expected_cols + 1,
  560.                   (size_t) sizeof(float *));
  561.     for(ncols = 0; ncols < expected_cols; ncols++) {
  562.       thesedata->all[ncols] = (float *)calloc((size_t) nlines, sizeof(float));
  563.       float_copy(nlines, thesedata->all[ncols], 1, scratch + ncols,
  564.          expected_cols);
  565.     }
  566.     thesedata->all[expected_cols] = (float *)NULL;
  567.                 /* make sure path is large enough */
  568.     if (2*nlines > max_path)
  569.       realloc(path, (2*nlines)*sizeof(float));
  570.     thesedata->npts = nlines;
  571.     memmove(thesedata->displayed = (float **) calloc(3, sizeof(float *)),
  572.         thesedata->all, 3 * sizeof(float *));      
  573.   }
  574.  
  575.   [self reScale:self];
  576.   free(scratch);
  577.   fclose(input);
  578.   [self display];
  579.   return YES;
  580. }
  581.  
  582.  
  583. static int x_seq[] = {0,3,3,0,0,0,3,3,0,0,3,3,3,3,0,0};
  584. static int y_seq[] = {1,1,4,4,1,1,1,4,4,1,1,1,4,4,4,4};
  585. static int z_seq[] = {2,2,2,2,2,5,5,5,5,5,5,2,2,5,5,2};
  586.  
  587. -setlimits:(float *)newlimits
  588. {
  589.   int i, n, tickdir;
  590.   float tick, width;
  591.  
  592.   for (n = 0; n < 16; n++) {    /* create cube */
  593.     cube[0][n] = newlimits[x_seq[n]];
  594.     cube[1][n] = newlimits[y_seq[n]];
  595.     cube[2][n] = newlimits[z_seq[n]];
  596.   }
  597.   for (n = 0; n < 54; n++) {    /* create axes */
  598.     axes[0][n] = newlimits[0];
  599.     axes[1][n] = newlimits[1];
  600.     axes[2][n] = newlimits[2];
  601.   }
  602.   for (i = 0; i < 3; i++) {
  603.     tickdir = (i == 0) ? 1 : 0;
  604.     tick = 0.03 * (newlimits[tickdir + 3] - newlimits[tickdir]);
  605.     width = newlimits[i + 3] - newlimits[i];
  606.     n = 18 * i;
  607.     axes[i][n+1] = axes[i][n+2] = axes[i][n+3] = axes[i][n+4] =
  608.       newlimits[i] + 0.25 * width;
  609.     axes[i][n+5] = axes[i][n+6] = axes[i][n+7] = axes[i][n+8] =
  610.       newlimits[i] + 0.5 * width;
  611.     axes[i][n+9] = axes[i][n+10] = axes[i][n+11] = axes[i][n+12] =
  612.       newlimits[i] + 0.75 * width;
  613.     axes[i][n+13] = axes[i][n+17] = newlimits[i] + 0.87 * width;
  614.     axes[i][n+14] = axes[i][n+16] = newlimits[i] + 0.84 * width;
  615.     axes[i][n+15] = newlimits[i] + width;
  616.     axes[tickdir][n+2] = axes[tickdir][n+6] = axes[tickdir][n+10] =
  617.       axes[tickdir][n+14] = newlimits[tickdir] + tick;
  618.     axes[tickdir][n+3] = axes[tickdir][n+7] = axes[tickdir][n+11] =
  619.       axes[tickdir][n+16] = newlimits[tickdir] - tick;
  620.   }
  621.   [vm setlimits:newlimits];
  622.   [self Reset:self];
  623.   return self;
  624. }
  625.  
  626. - drawSelf:(NXRect*)r :(int)c
  627. {
  628.   NXColor  lineColor;
  629.   datapoints **all_lines = toshow, *thisline;
  630.   
  631.   NXEraseRect(&bounds);
  632.   PSsetgray(NX_BLACK);
  633.   PSsetlinewidth(0.0);
  634.   if (showCube) {
  635.     ops[1] = 32 + 16 - 1;
  636.     DPSDoUserPath ([vm as_DPSpath :16 :cube[0] :cube[1] :cube[2] :path],
  637.            32, dps_float, ops, 3, boundingBox, dps_ustroke);
  638.   }
  639.   if (showAxes) {
  640.     ops[1] = 32 + 54 - 1;
  641.     DPSDoUserPath ([vm as_DPSpath :54 :axes[0] :axes[1] :axes[2] :path],
  642.            108, dps_float, ops, 3, boundingBox, dps_ustroke);
  643.   }
  644.   while((thisline = *all_lines++) != (datapoints *) NULL) {
  645.     if (thisline->type & 0x4) {         /* set the RGB color */
  646.       lineColor = NXConvertRGBToColor(*(thisline->displayed)[0],
  647.                                       *(thisline->displayed)[1],
  648.                       *(thisline->displayed)[2]);
  649.       NXSetColor(lineColor);
  650.       continue;
  651.     }
  652.     if( thisline->type & 0x1 && thisline->npts > 1 ) { /* draw the line */
  653.       ops[1] = thisline->npts + 32 - 1;
  654.       DPSDoUserPath([vm as_DPSpath :thisline->npts
  655.              :(thisline->displayed)[0] :(thisline->displayed)[1]
  656.              :(thisline->displayed)[2] :path],
  657.             2*(thisline->npts), dps_float,
  658.             ops, 3, boundingBox, dps_ustroke);
  659.     }
  660.     if (thisline->type & 0x2) {            /* draw points */
  661.       if (PSonly)
  662.     dotAtPSonly([vm as_DPSpath :thisline->npts
  663.              :(thisline->displayed)[0] :(thisline->displayed)[1]
  664.              :(thisline->displayed)[2] :path],
  665.             2 *(thisline->npts), *dot_offset);
  666.       else
  667.     dotAt([vm as_DPSpath :thisline->npts
  668.            :(thisline->displayed)[0] :(thisline->displayed)[1]
  669.            :(thisline->displayed)[2] :path offset:dot_offset],
  670.           2 * (thisline->npts), dotgstate);
  671.     }
  672.   }
  673.   return self;
  674. }
  675.  
  676. - (BOOL)acceptsFirstMouse
  677. {
  678.     return YES;
  679. }
  680.  
  681. - mouseDown:(NXEvent *)e
  682. {
  683.     if( e->data.mouse.click == 2 ) {
  684.        [myController inspectCustomComponent];
  685.        return self;
  686.     }
  687.  
  688.     if( e->flags & NX_COMMANDMASK ) {
  689.        [self toggleCube:self];
  690.     }
  691.     else if (e->flags & NX_ALTERNATEMASK ) {
  692.        [self toggleAxes:self];
  693.     } else {
  694.        [myController refreshInspectorPanel];
  695.     }
  696.     return [super mouseDown:e];
  697. }
  698.  
  699. - free
  700. {
  701.   int  count;
  702.   datapoints **all_data = toshow, *thesedata;
  703.   
  704.   while ((thesedata = *all_data) != (datapoints *) NULL) {
  705.     free(thesedata->displayed);
  706.     count = 0;
  707.     while(thesedata->all[count] != (float *) NULL) {
  708.       free(thesedata->all[count]);
  709.       count++;
  710.     }
  711.     free(thesedata->all);
  712.     free(thesedata);
  713.     *all_data++ = (datapoints *) NULL;
  714.   }
  715.   free(toshow);
  716.   if( viewSound ) [viewSound free];
  717.   if( dataFileName ) free(dataFileName);
  718.   return [super free];
  719. }
  720.  
  721. - write:(NXTypedStream *)stream
  722. {
  723.     int  cols, nstruct, ns;
  724.     datapoints **all_data = toshow, *thesedata;
  725.     
  726.     [super write:stream];
  727.     NXWriteType(stream, "i", &max_path);
  728.     nstruct = 0;
  729.     while((thesedata = *all_data++) != (datapoints *) NULL) nstruct++;
  730.     NXWriteType(stream, "i", &nstruct);
  731.     all_data = toshow;
  732.     for( ns=0; ns<nstruct; ns++) {
  733.        thesedata = *all_data++;
  734.        NXWriteTypes(stream, "ii", &thesedata->npts, &thesedata->type);
  735.        cols = 0;
  736.        while( thesedata->all[cols] != (float *) NULL) {
  737.       NXWriteArray(stream, "f", thesedata->npts, *(&thesedata->all[cols]));
  738.           cols++;
  739.        }
  740.     }
  741.     NXWriteObject(stream, AngleDisplay);
  742.     NXWriteObject(stream, PhiSlider);
  743.     currentPhi = -rad_to_deg * [vm getPhi:self];
  744.     NXWriteObject(stream, ThetaSlider);
  745.     currentTheta = rad_to_deg * [vm getTheta:self];
  746.     NXWriteObject(stream, DistanceSlider);
  747.     currentDist = [vm getdist:self];
  748.     NXWriteTypes(stream, "fff", ¤tPhi, ¤tTheta, ¤tDist);
  749.     NXWriteObject(stream, soundButton);
  750.     NXWriteRect(stream, &sndBtnRect);
  751.     NXWriteObject(stream, viewSound);
  752.     NXWriteTypes(stream, "ccc", &showCube, &showAxes, &PSonly);
  753.     NXWriteType(stream, "*", &dataFileName);
  754.     NXWriteObject(stream, myController);
  755.     return self;
  756. }
  757.  
  758. - read:(NXTypedStream *)stream
  759. {
  760.     int  cols, nstruct, ns;
  761.     datapoints **all_data, *thesedata;
  762.     int  ncols = 3;
  763.     
  764.     [super read:stream];
  765.     NXReadType(stream, "i", &max_path);
  766.     NXReadType(stream, "i", &nstruct);
  767.     toshow = (datapoints **) calloc(1000, sizeof(datapoints *));
  768.     all_data = toshow;
  769.     for( ns=0; ns<nstruct; ns++) {
  770.        thesedata = *all_data++ = (datapoints *) malloc(sizeof(datapoints));
  771.        NXReadTypes(stream, "ii", &thesedata->npts, &thesedata->type);
  772.        thesedata->all = (float **)calloc((size_t) ncols+1, (size_t) sizeof(float *));
  773.        for( cols=0; cols<ncols; cols++ ) {
  774.           thesedata->all[cols] = (float *)calloc((size_t) thesedata->npts, sizeof(float));
  775.       NXReadArray(stream, "f", thesedata->npts, *(&thesedata->all[cols]));
  776.        }
  777.        thesedata->all[ncols] = (float *)NULL;
  778.        memmove(thesedata->displayed = (float **) calloc(3, sizeof(float *)),
  779.         thesedata->all, 3 * sizeof(float *));
  780.     }
  781.     AngleDisplay = NXReadObject(stream);
  782.     PhiSlider = NXReadObject(stream);
  783.     ThetaSlider = NXReadObject(stream);
  784.     DistanceSlider = NXReadObject(stream);
  785.     NXReadTypes(stream, "fff", ¤tPhi, ¤tTheta, ¤tDist);
  786.     soundButton = NXReadObject(stream);
  787.     NXReadRect(stream, &sndBtnRect);
  788.     viewSound = NXReadObject(stream);
  789.     NXReadTypes(stream, "ccc", &showCube, &showAxes, &PSonly);
  790.     NXReadType(stream, "*", &dataFileName);
  791.     myController = NXReadObject(stream);
  792.     return self;
  793. }
  794.  
  795. - awake
  796. {
  797.     [super awake];
  798.     [myController setViews:self];
  799.     path = (float *) calloc(2000, sizeof(float));
  800.     dot_offset[0] = 2.5 * 2 / bounds.size.width;
  801.     dot_offset[1] = 2.5 * 2 / bounds.size.height;
  802.     [self setDrawSize:(NXCoord) 2:(NXCoord) 2];
  803.     [self setDrawOrigin:(NXCoord) -1:(NXCoord) -1];
  804.     showCube = NO;
  805.     showAxes = YES;
  806.     PSonly = YES;
  807.     ops[0] = dps_moveto; ops[1] = 32 + 15; ops[2] = dps_lineto;
  808.     boundingBox[0] = bounds.origin.x;
  809.     boundingBox[1] = bounds.origin.y;
  810.     boundingBox[2] = bounds.origin.x + bounds.size.width;
  811.     boundingBox[3] = bounds.origin.y + bounds.size.height;
  812.     vm = [AzimuthMat new];
  813.     openReq = [OpenPanel new];
  814.     [self reScale:self];
  815.     tag = 63757374;        // hex ascii for "cust"
  816.     [self setViewParameters];
  817.     return self;
  818. }
  819.  
  820. @end
  821.