home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / RatMandelbrot / Source / DrawView.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  15.1 KB  |  552 lines

  1. /* DrawView.m  part of RatMandel 
  2.  * draws the Mandelbrot set of rational quadratic functions 
  3.  * Author: William Gilbert
  4.  * Written: May 1991 and last modified Dec 1992 
  5.  */
  6.  
  7. #import "DrawView.h"
  8.  
  9. #define PRINTED_SIZE 5.6                // size of printed window in inches
  10. #define LINE_WIDTH_FACTOR 1.1            // fudge factor to avoid aliasing
  11. #define INF 10e20
  12. #define A_PLANE 0
  13. #define A_INVERSE_PLANE 1
  14. #define B_PLANE 2
  15.  
  16. @implementation DrawView
  17.  
  18. - appDidInit:sender
  19. {
  20.     [notesText setOpaque:YES];
  21.     [notesText setMonoFont:NO];
  22.     [notesText setFont:[Font newFont:"Ohlfs" size:11.0]];
  23.     [self presetRing:sender];
  24.     [self getParameters:&displayplane xr:&xr yr:&yr xs:&xs ys:&ys 
  25.         xmin:&xmin ymin:&ymin xmax:&xmax ymax:&ymax 
  26.         iterates:&iterates cycle:&cycle close:&close];
  27.     [[PrintPanel new] setAccessoryView:accessoryBox];
  28.     lineCompleted = 0;
  29.     return self;
  30. }
  31.  
  32. - start:sender
  33. {
  34.     running = YES;
  35.     [self display];
  36.     return self;
  37. }
  38.  
  39. - clearNotes:sender
  40. {
  41.     [notesText selectAll:nil];
  42.     [notesText delete:nil];
  43.     return self;
  44. }
  45.  
  46. - lastCoords:sender
  47. {
  48.     [self setParameters:displayplane xr:xr yr:yr xs:xs ys:ys 
  49.         xmin:xmin ymin:ymin xmax:xmax ymax:ymax 
  50.         iterates:iterates cycle:cycle close:close];
  51.     [self clearNotes:sender];
  52.       [self lockFocus];
  53.     PSnewinstance();
  54.      [self unlockFocus];
  55.     return self;
  56. }
  57.  
  58. - presetMandelbrot:sender
  59.  {
  60.     [notesText setText:"\n Mandelbrot Set\n ==============\n The critical point -1 is also fixed.\n F(z) is conjugate to\n     z -> z^2 + c\n\n This is not the usual\nview of the Mandelbrot\nset.  See Mandelbrot\nPlate 189.\n"];
  61.     [self setParameters:A_PLANE xr:-1.0 yr:0.0 xs:2.0 ys:0.0 
  62.         xmin:-2.0 ymin:-3.0 xmax:4.0 ymax:3.0  
  63.         iterates:50 cycle:1 close:0.01];
  64.     [self parametersChanged:sender];
  65.     return self;
  66.  }
  67.  
  68. - presetSnowshoe:sender
  69.  {
  70.     [notesText setText:"\n Snowshoe\n ========\n F(z) is conjugate to\n   z -> (1/z^2) + c\n\n See J. Milnor, \"Remarks on Quadratic Rational Maps\" Fig 13.\n\n\n F:1 -> -1\n"];
  71.     [self setParameters:A_PLANE xr:-1.0 yr:0.0 xs:-2.0 ys:0.0 
  72.         xmin:-8.2 ymin:-4.7 xmax:1.2 ymax:4.7 
  73.         iterates:1000 cycle:30 close:0.001];
  74.     [self parametersChanged:sender];
  75.     return self;
  76.  }
  77.  
  78. - presetSpyglass:sender
  79.  {
  80.     [notesText setText:"\n Spyglass\n ========\n"];
  81.     [self setParameters:A_PLANE xr:-1.0 yr:0.4 xs:2.0 ys:0.0 
  82.         xmin:-2.0 ymin:-3.0 xmax:4.0 ymax:3.0 
  83.         iterates:50 cycle:1 close:0.01];
  84.     [self parametersChanged:sender];
  85.     return self;
  86.  }
  87.  
  88. - presetWink:sender
  89.  {
  90.     [notesText setText:"\n Wink\n ====\n"];
  91.     [self setParameters:A_PLANE xr:-1.0 yr:0.0 xs:1.7 ys:0.3 
  92.         xmin:-2.0 ymin:-3.0 xmax:4.0 ymax:3.0 
  93.         iterates:200 cycle:1 close:0.01];
  94.     [self parametersChanged:sender];
  95.     return self;
  96.  }
  97.  
  98. - presetTieAndCane:sender
  99.  {
  100.     [notesText setText:"\n TieAndCane\n ==========\n"];
  101.     [self setParameters:B_PLANE xr:-1.0 yr:0.0 xs:50.0 ys:200.0 
  102.         xmin:-2.0 ymin:-2.0 xmax:2.0 ymax:2.0 
  103.         iterates:1000 cycle:1 close:0.01];
  104.     [self parametersChanged:sender];
  105.     return self;
  106.  }
  107.  
  108. - presetNapoleon:sender
  109.  {
  110.     [notesText setText:"\n Napoleon\n ========\n"];
  111.     [self setParameters:A_PLANE xr:-1.0 yr:0.0 xs:3.0 ys:0.0 
  112.         xmin:-3.0 ymin:-4.0 xmax:5.0 ymax:4.0 
  113.         iterates:100 cycle:1 close:0.01];
  114.     [self parametersChanged:sender];
  115.     return self;
  116.  }
  117.  
  118. - presetSperm:sender
  119.  {
  120.     [notesText setText:"\n Sperm\n =====\n"];
  121.     [self setParameters:A_PLANE xr:0.3 yr:-0.4 xs:1.2 ys:0.3 
  122.         xmin:-9.0 ymin:-16.0 xmax:9.0 ymax:2.0 
  123.         iterates:200 cycle:1 close:0.1];
  124.     [self parametersChanged:sender];
  125.     return self;
  126.  }
  127.  
  128. - presetRing:sender
  129.  {
  130.     [notesText setText:"\n Ring\n ====\n"];
  131.     [self setParameters:B_PLANE xr:0.3 yr:-0.4 xs:1.2 ys:0.3 
  132.         xmin:-4.6 ymin:-4.6 xmax:2.1 ymax:2.1 
  133.         iterates:200 cycle:1 close:0.1];
  134.     [self parametersChanged:sender];
  135.     return self;
  136.  }
  137.  
  138. - presetBloodClot:sender
  139.  {
  140.     [notesText setText:"\n BloodClot\n =========\n Same as Alien, but\nshowing the a-plane\ninstead of the\n(1/a)-plane.\n"];
  141.     [self setParameters:A_PLANE xr:0.0 yr:0.0 xs:0.0 ys:0.0 
  142.         xmin:-1.25 ymin:0.1 xmax:1.25 ymax:2.6 
  143.         iterates:500 cycle:1 close:0.15];
  144.     [self parametersChanged:sender];
  145.     return self;
  146.  }
  147.  
  148. - presetAlien:sender
  149.  {
  150.     [notesText setText:"\n Alien\n =====\n See Madelbrot Plate X and Milnor Fig 12.\n\n The actual Mandelbrot set is obtained by setting close = 0.001; see Yin p. 146.\n"];
  151.     [self setParameters:A_INVERSE_PLANE xr:0.0 yr:0.0 xs:0.0 ys:0.0 
  152.         xmin:-1.02 ymin:-1.02 xmax:1.02 ymax:1.02 
  153.         iterates:1000 cycle:1 close:0.1];
  154.     [self parametersChanged:sender];
  155.     return self;
  156.  }
  157.  
  158. - presetLunar:sender
  159.  {
  160.     [notesText setText:"\n Lunar\n =====\n Same as Alien with cycle = 10\n"];
  161.     [self setParameters:A_INVERSE_PLANE xr:0.0 yr:0.0 xs:0.0 ys:0.0 
  162.         xmin:-1.02 ymin:-1.02 xmax:1.02 ymax:1.02 
  163.         iterates:1000 cycle:10 close:0.05];
  164.     [self parametersChanged:sender];
  165.     return self;
  166.  }
  167.  
  168. - presetScallops:sender
  169.  {
  170.     [notesText setText:"\n Scallops\n ========\n See J. Milnor, \"Remarks on Quadratic Rational Maps\" Fig 11.\n\n\nF:-1 -> 0 -> inf(fixed)\n"];
  171.     [self setParameters:A_INVERSE_PLANE xr:0.0 yr:0.0 xs:2.0 ys:0.0 
  172.         xmin:-1.02 ymin:-1.02 xmax:1.02 ymax:1.02 
  173.         iterates:1000 cycle:1 close:0.1];
  174.     [self parametersChanged:sender];
  175.     return self;
  176.  }
  177.  
  178. - (BOOL) continue
  179.  // Ignores all mousedown events until Stop is pressed
  180. {
  181.     NXEvent    *peekedEvent;
  182.     
  183.     peekedEvent = [NXApp peekAndGetNextEvent:NX_MOUSEDOWNMASK];
  184.     if (peekedEvent IS NULL)
  185.         return YES;
  186.     else if (NXPointInRect(&(peekedEvent->location), &stopRect))
  187.     {
  188.         running = NO;
  189.         return NO;
  190.     }
  191.     else
  192.         return YES;
  193. }
  194.  
  195. - drawSelf: (NXRect *)rect : (int) count
  196.  {
  197.     NXRect  frameRect;
  198.     int p, q,                 // pixel coords
  199.         pmax, qmax,         // pixels in screen
  200.         pstart;                // start of line to draw
  201.     float x, y,                // actual coords of point
  202.         dx, dy,                // distance between pixels
  203.         oldCol, newCol,        // colours of adjacent pixels
  204.         printerDot,            // distance between printer dots 
  205.         lineWidth,            // width 1 on printer is PRINTER_PIXEL_FACTOR
  206.         qHeight;            // vertical position of line to be drawn
  207.  
  208.     [self getParameters:&displayplane xr:&xr yr:&yr xs:&xs ys:&ys 
  209.         xmin:&xmin ymin:&ymin xmax:&xmax ymax:&ymax 
  210.         iterates:&iterates cycle:&cycle close:&close];
  211.     sqclose = SQ(close);
  212.     invclose = 1/sqclose;
  213.     if (lineCompleted IS 0) NXEraseRect(&bounds);
  214.     [self getFrame:&frameRect];
  215.     if ((NXDrawingStatus IS NX_DRAWING) AND running)
  216.     {
  217.         [stopButton getFrame:&stopRect];
  218.         pmax = (int) frameRect.size.width;
  219.         qmax = (int) frameRect.size.height;
  220.         dx = (xmax - xmin)/pmax;
  221.         dy = (ymax - ymin)/qmax;
  222.         PSsetlinewidth(1.0);
  223.          for (q = lineCompleted, y = ymin + lineCompleted*dy; 
  224.             ((q <= qmax) AND running); q++, y += dy)
  225.         {
  226.             for (p = 0, pstart = 0, x = xmin, oldCol = NX_WHITE;
  227.                             ((p <= pmax) AND [self continue]); p++, x += dx)
  228.             {
  229.                 newCol = [self pixelCol:x :y];
  230.                 if (newCol IS_NOT oldCol)
  231.                 {
  232.                     if (oldCol IS_NOT NX_WHITE)
  233.                         [self drawline:oldCol :pstart :(p-1) :q];
  234.                     pstart = p;
  235.                     oldCol = newCol;
  236.                 }
  237.             }
  238.             if (oldCol IS_NOT NX_WHITE)
  239.                 [self drawline:oldCol :pstart :p :q];
  240.             [window flushWindow];
  241.         }
  242.         if (q < qmax)
  243.         {
  244.             lineCompleted = q-1;
  245.             [startButton setTitle:"Cont"];
  246.         }
  247.         else
  248.         {
  249.             lineCompleted = 0;
  250.             [startButton setTitle:"Start"];
  251.         }
  252.     }
  253.     else if (NXDrawingStatus IS NX_PRINTING)
  254.     {
  255.         NXEraseRect(&bounds);
  256.         pixelSize = 1 +  [[printerPopUpButton target]      
  257.                                 indexOfItem:[printerPopUpButton title]];
  258.         pmax = (int) ((atoi(NXGetDefaultValue("System", "PrinterResolution")))
  259.                                  *PRINTED_SIZE/pixelSize);
  260.         qmax = pmax;
  261.         lineWidth = frameRect.size.width/pmax;
  262.         printerDot = lineWidth/pixelSize;
  263.         dx = (xmax - xmin)/pmax;
  264.         dy = (ymax - ymin)/qmax;
  265.         if (pixelSize > 1)
  266.             PSsetlinewidth(lineWidth*LINE_WIDTH_FACTOR);
  267.         else
  268.             PSsetlinewidth(0);
  269.          for (q = 0, y = ymin; (q <= qmax); q++, y += dy)
  270.         {
  271.             qHeight = (q + 0.5)*lineWidth;
  272.             for (p = 0, pstart = 0, x = xmin, oldCol = NX_WHITE; p <= pmax;
  273.                     p++, x += dx)
  274.             {
  275.                 newCol = [self pixelCol:x :y];
  276.                 if (newCol IS_NOT oldCol)
  277.                 {
  278.                     if (oldCol IS_NOT NX_WHITE)
  279.                         [self drawline :oldCol :(pstart*lineWidth) 
  280.                                         :(p*lineWidth - printerDot) :qHeight];
  281.                     pstart = p;
  282.                     oldCol = newCol;
  283.                 }
  284.             }
  285.             if (oldCol IS_NOT NX_WHITE)
  286.                 [self drawline:oldCol :(pstart*lineWidth) :(p*lineWidth)
  287.                                                                      :qHeight];
  288.             [window flushWindow];
  289.         }
  290.     }        
  291.     return self;
  292.  }
  293.  
  294. - (void) drawline:(float) col :(float) p1 :(float) p2 :(float) q
  295. {
  296.     PSnewpath();
  297.     PSmoveto(p1, q);
  298.     PSlineto(p2, q);
  299.     PSsetgray(col);
  300.     PSstroke();
  301. }
  302.  
  303. - (float) pixelCol:(float) x :(float) y
  304. {
  305.     int power = 0;
  306.     BOOL separate = YES;        // images of critical points are separate
  307.     double x1, y1, x2, y2, xx, yy, xa = 0.0, ya = 0.0, xb = 0.0, yb = 0.0,
  308.         x0, y0, norma, norm0, norm1, norm2;
  309.     
  310.     switch (displayplane) 
  311.     {
  312.         case A_PLANE:
  313.             xa = x;
  314.             ya = y;  
  315.             xb = xr*xa - yr*ya + xs;
  316.             yb = xr*ya + yr*xa + ys;
  317.             break;
  318.         case A_INVERSE_PLANE:
  319.             norma = x*x + y*y;
  320.             if (norma > 0.0)
  321.             {
  322.                 xa = x/norma;
  323.                 ya = -y/norma;
  324.             }
  325.             xb = xr*xa - yr*ya + xs;
  326.             yb = xr*ya + yr*xa + ys;
  327.             break;
  328.         case B_PLANE:
  329.             xb = x;
  330.             yb = y;  
  331.             xa = xr*xb - yr*yb + xs;
  332.             ya = xr*yb + yr*xb + ys;
  333.             break;
  334.     }
  335.     norma = xa*xa + ya*ya;
  336.     if (norma IS 0.0)
  337.         return NX_WHITE;
  338.     else
  339.     {                                // F(z) = (1/a)(z + 1/z + b)
  340.         x1 = 1.0;
  341.         y1 = 0.0;
  342.         x2 = -1.0;
  343.         y2 = 0.0;
  344.         x0 = x1;
  345.         y0 = y1;
  346.         norm0 = x0*x0 + y0*y0;
  347.         norm1 = x1*x1 + y1*y1;
  348.         norm2 = x2*x2 + y2*y2;
  349.         while (separate AND (power < iterates))
  350.         {
  351.             ++power;
  352.             if (norm1 IS 0.0)
  353.             {
  354.                 x1 = INF;
  355.                 y1 = INF;    
  356.             }
  357.             else
  358.             {
  359.                 xx = x1 + (x1/norm1) + xb;
  360.                 yy = y1 - (y1/norm1) + yb;
  361.                 x1 = ((xx*xa + yy*ya)/norma);
  362.                 y1 = ((yy*xa - xx*ya)/norma);
  363.             }
  364.             if (norm2 IS 0.0)
  365.             {
  366.                 x2 = INF;
  367.                 y2 = INF;    
  368.             }
  369.             else
  370.             {
  371.                 xx = x2 + (x2/norm2) + xb;
  372.                 yy = y2 - (y2/norm2) + yb;
  373.                 x2 = ((xx*xa + yy*ya)/norma);
  374.                 y2 = ((yy*xa - xx*ya)/norma);
  375.             }
  376.             if ((power MOD cycle) IS 0)
  377.             {
  378.                 x0 = x1;
  379.                 y0 = y1;
  380.                 norm0 = x0*x0 + y0*y0;
  381.             }
  382.             norm1 = x1*x1 + y1*y1;
  383.             norm2 = x2*x2 + y2*y2;
  384.             if ((SQ(x0 - x2) + SQ(y0 - y2) < sqclose) OR 
  385.                     ((norm0 > invclose) AND (norm2 > invclose)))
  386.                 separate = FALSE;
  387.         }
  388.         if (power < iterates)
  389.             return NX_WHITE;
  390.         else
  391.             return NX_BLACK;
  392.     }
  393. }
  394.  
  395. - setParameters:(int)plane 
  396.     xr:(float)paramsxr yr:(float)paramsyr 
  397.     xs:(float)paramsxs ys:(float)paramsys     
  398.     xmin:(float)paramsxmin ymin:(float)paramsymin 
  399.     xmax:(float)paramsxmax ymax:(float)paramsymax 
  400.     iterates:(int)paramsiterates cycle:(int)paramscycle 
  401.     close:(float)paramsclose    
  402.  {
  403.     switch (plane)
  404.     {
  405.         case A_PLANE:
  406.             [planePopUpButton setTitle:
  407.              " The  a-plane section, when  b = r a + s,  of"];
  408.             break;
  409.         case A_INVERSE_PLANE:
  410.             [planePopUpButton setTitle:
  411.             " The  (1/a)-plane section, when  b = r a + s,  of"];
  412.             break;
  413.         case B_PLANE:
  414.             [planePopUpButton setTitle:
  415.             " The  b-plane section, when  a = r b + s,  of"];
  416.             break;
  417.     }
  418.     [paramsForm setFloatValue:paramsxr at:0];
  419.     [paramsForm setFloatValue:paramsyr at:1];
  420.     [paramsForm setFloatValue:paramsxs at:2];
  421.     [paramsForm setFloatValue:paramsys at:3];
  422.     [paramsForm setFloatValue:paramsxmin at:4];
  423.     [paramsForm setFloatValue:paramsymin at:5];
  424.     [paramsForm setFloatValue:paramsxmax at:6];
  425.     [paramsForm setFloatValue:paramsymax at:7];
  426.     [paramsForm setIntValue:paramsiterates at:8];
  427.     [paramsForm setIntValue:paramscycle at:9];
  428.     [paramsForm setFloatValue:paramsclose at:10];
  429.     return self;
  430.  }
  431.  
  432. - getParameters:(int *)plane 
  433.     xr:(float *)paramsxr yr:(float *)paramsyr 
  434.     xs:(float *)paramsxs  ys:(float *)paramsys 
  435.     xmin:(float *)paramsxmin ymin:(float *)paramsymin
  436.     xmax:(float *)paramsxmax ymax:(float *)paramsymax 
  437.     iterates:(int *)paramsiterates cycle:(int *)paramscycle 
  438.     close:(float *)paramsclose
  439.  {
  440.      if (([paramsForm floatValueAt:0] IS_NOT xr) OR
  441.         ([paramsForm floatValueAt:1] IS_NOT yr) OR
  442.         ([paramsForm floatValueAt:2] IS_NOT xs) OR
  443.         ([paramsForm floatValueAt:3] IS_NOT ys) OR
  444.         ([paramsForm floatValueAt:4] IS_NOT xmin) OR
  445.         ([paramsForm floatValueAt:5] IS_NOT ymin) OR
  446.         ([paramsForm floatValueAt:6] IS_NOT xmax) OR
  447.         ([paramsForm floatValueAt:7] IS_NOT ymax) OR
  448.         ([paramsForm floatValueAt:8] IS_NOT iterates) OR
  449.         ([paramsForm floatValueAt:9] IS_NOT cycle) OR
  450.         ([paramsForm floatValueAt:10] IS_NOT close))
  451.     {
  452.         [self parametersChanged:self];
  453.     }
  454.     *plane = [[planePopUpButton target] indexOfItem:[planePopUpButton title]];
  455.     *paramsxr = [paramsForm floatValueAt:0];
  456.     *paramsyr = [paramsForm floatValueAt:1];
  457.     *paramsxs = [paramsForm floatValueAt:2];
  458.     *paramsys = [paramsForm floatValueAt:3];
  459.     *paramsxmin = [paramsForm floatValueAt:4];
  460.     *paramsymin = [paramsForm floatValueAt:5];
  461.     *paramsxmax = [paramsForm floatValueAt:6];
  462.     *paramsymax = [paramsForm floatValueAt:7];
  463.     *paramsiterates = [paramsForm intValueAt:8];
  464.     *paramscycle = [paramsForm intValueAt:9];
  465.     *paramsclose = [paramsForm floatValueAt:10];
  466.     return self;
  467.  }
  468.  
  469. - parametersChanged:sender
  470. {
  471.     lineCompleted = 0;
  472.     [startButton setTitle:"Start"];
  473.     if (NXDrawingStatus IS NX_DRAWING)
  474.     {
  475.           [self lockFocus];
  476.         PSnewinstance();
  477.          [self unlockFocus];
  478.     }
  479.     return self;
  480. }
  481.  
  482. - mouseDown:(NXEvent *)theEvent
  483.  // Draws a box on the screen to produce new view coordinates
  484. {
  485.   NXRect boxRect;
  486.   NXPoint centerPoint, currentPoint;
  487.   NXEvent *nextEvent;
  488.   int oldMask,
  489.       boxSides[] = {NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN,
  490.                   NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN};
  491.   float aspectRatio,
  492.       boxGrays[] = {NX_BLACK, NX_BLACK, NX_BLACK, NX_BLACK, 
  493.                   NX_WHITE, NX_WHITE, NX_WHITE, NX_WHITE};
  494.   
  495.   [window makeFirstResponder:self];
  496.   oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  497.   aspectRatio = bounds.size.height/bounds.size.width;
  498.   centerPoint = theEvent->location;
  499.   [self convertPoint:¢erPoint fromView:nil];
  500.   NXSetRect(&boxRect,centerPoint.x,centerPoint.y,0.0,0.0);
  501.   [self lockFocus];
  502.   nextEvent = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  503.   while (nextEvent->type IS NX_MOUSEDRAGGED)
  504.   { 
  505.       currentPoint = nextEvent->location;
  506.     [self convertPoint:¤tPoint fromView:nil];
  507.     boxRect.size.width = 2*(currentPoint.x - centerPoint.x);
  508.     boxRect.size.height = 2*(currentPoint.y - centerPoint.y);
  509.     if (boxRect.size.width < 0) 
  510.         boxRect.size.width = -boxRect.size.width;
  511.     if (boxRect.size.height < 0) 
  512.         boxRect.size.height = -boxRect.size.height;
  513.     if (boxRect.size.height > boxRect.size.width*aspectRatio) 
  514.         boxRect.size.height = boxRect.size.width*aspectRatio;
  515.     else 
  516.         boxRect.size.width = boxRect.size.height/aspectRatio;
  517.     boxRect.origin.x = centerPoint.x - .5*boxRect.size.width;
  518.     boxRect.origin.y = centerPoint.y - .5*boxRect.size.height;
  519.     PSnewinstance();
  520.     PSsetinstance(YES);
  521.     NXDrawTiledRects(&boxRect, (NXRect *)0, boxSides, boxGrays, 8);
  522.     PSsetinstance(NO);
  523.       nextEvent = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  524.   }
  525.   [self unlockFocus];
  526.   [window setEventMask:oldMask];
  527.   if ((boxRect.size.width > 0) AND (boxRect.size.height > 0)) 
  528.   {
  529.     [paramsForm setFloatValue:xmin + (xmax - xmin)*
  530.         (boxRect.origin.x - bounds.origin.x)/bounds.size.width at:4];
  531.     [paramsForm setFloatValue:ymin + (ymax - ymin)*
  532.         (boxRect.origin.y - bounds.origin.y)/bounds.size.height at:5];
  533.     [paramsForm setFloatValue:xmin + (xmax - xmin)*
  534.         (boxRect.origin.x + boxRect.size.width - bounds.origin.x) 
  535.         /bounds.size.width at:6];
  536.     [paramsForm setFloatValue:ymin + (ymax - ymin)*
  537.         (boxRect.origin.y + boxRect.size.height - bounds.origin.y)
  538.         /bounds.size.height at:7];
  539.     lineCompleted = 0;
  540.     [startButton setTitle:"Start"];
  541.   }
  542.   else
  543.   {
  544.       [self lockFocus];
  545.     PSnewinstance();
  546.      [self unlockFocus];
  547.   }
  548.   return self;
  549. }
  550.  
  551. @end
  552.