home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / Lines / LinesView.m < prev    next >
Text File  |  1992-03-24  |  7KB  |  218 lines

  1. /*
  2.  * LinesView.m, a small sample view for showing timed entries & userpaths.
  3.  * Author: Ali T. Ozer, NeXT Computer, Inc.
  4.  * Written March 19, 1989.
  5.  * Updated for 2.0 Oct 16, 1990 by Jayson Adams to use UserPath.[hm].
  6.  * Updated for 3.0 March 24, 1992 by Ali Ozer
  7.  *
  8.  * You may freely copy, distribute and reuse the code in this example.
  9.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  10.  * fitness for any particular use.
  11.  *
  12.  * LinesView draws a number of connected lines whose endpoints bounce around
  13.  * randomly within the bounds of the view. The endpoints are stored in
  14.  * an data array which is passed to PostScript as a user path. The
  15.  * animation is performed by calling the "animate" method as often as
  16.  * possible through a timed entry.
  17.  */
  18.  
  19. #import <appkit/appkit.h>
  20. #import <libc.h>                // For random(), etc...
  21. #import <dpsclient/wraps.h>    // For PS and DPS function prototypes
  22. #import "LinesView.h"
  23.  
  24. #define RANDINT(n) (random() % (n+1))        // Return random integer 0..n
  25.  
  26. #define XVEL corners[count].xVel  // Some slimy shortcuts, asuuming we're
  27. #define YVEL corners[count].yVel  // using "count" as corner counter.
  28. #define XLOC corners[count].xLoc
  29. #define YLOC corners[count].yLoc
  30.  
  31. #define MAXVEL 12        // Maximum velocity of corners
  32.  
  33.  
  34. @implementation LinesView
  35.  
  36. - initFrame:(NXRect *)rect
  37. {
  38.     [super initFrame:rect];
  39.  
  40.   /* create a user path */
  41.     userPath = newUserPath();
  42.     
  43.     running = NO;
  44.  
  45.     return self;
  46. }
  47.  
  48. - free
  49. {
  50.   /* be sure to stop the timed entry */
  51.     if (running) {
  52.     DPSRemoveTimedEntry(linesTimedEntry);
  53.     }
  54.     freeUserPath(userPath);
  55.     
  56.     return [super free];
  57. }
  58.  
  59. void DrawIt(DPSTimedEntry te, double timeNow, void *data)
  60. {
  61.   /* we set data to self so we can call this method from the timed entry */
  62.     [(id)data animate];
  63. }
  64.  
  65. - toggleRun:sender
  66. {
  67.   /* start or stop the timed entry (we're called by a two-state button) */
  68.     if (running) {
  69.     DPSRemoveTimedEntry(linesTimedEntry);
  70.     running = NO;
  71.     } else {
  72.       /* Call the DrawIt() function as often as possible... */
  73.     linesTime!0@ry = DPSAddTimedEntry(0.0, &DrawIt, self,
  74.                        NX_BASETHRESHOLD);
  75.     running = YES;
  76.     }
  77.  
  78.     return self;
  79. }
  80.  
  81. /*
  82.  * This method should be connected to a UI object capable of generating
  83.  * int numbers. Note that to successfully detect the initial value of this
  84.  * slider as set through IB, we also declare an outlet named "numberOfCorners,"
  85.  * and connect it to this UI object. Thus this method (setNumberOfCorners:)
  86.  * gets called when the .nib is being loaded, and we can detect the
  87.  * initial value of the slider.  Because at initialization time the window
  88.  * isn't up yet, we won't really update the display at that time, even though
  89.  * display is called below.
  90.  */
  91. - setNumberOfCorners:sender
  92. {
  93.     int       count;
  94.     int    oldNumCorners = numCorners;
  95.  
  96.   /* set the number of corners based on the "corners" slider */
  97.     numCorners = MIN(MAXNUMCORNERS, MAX([sender intValue], MINNUMCORNERS));
  98.  
  99.   /* set the new corner starting positions & velocities */
  100.     for (count = oldNumCorners; count < numCorners; count++) {
  101.       XLOC = (int)(bounds.size.width) / 2;      
  102.       YLOC = (int)(bounds.size.height) / 2;      
  103.       XVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
  104.       YVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
  105.     }
  106.     [self display];
  107.  
  108.     return self;
  109. }
  110.  
  111. - drawSelf:(const NXRect *)rects :(int)rectCount
  112. {
  113.     int      count;
  114.     
  115.   /* fill with the background color */
  116.     NXEraseRect(&bounds);
  117.     
  118.     PSsetgray(NX_BLACK);
  119.     PSsetlinewidth(0.0);
  120.  
  121.   /* "plot" the points */
  122.     beginUserPath(userPath, NO);
  123.     for (count = 0; count < numCorners; count++) {
  124.     if (count) {
  125.         UPlineto(userPath, XLOC, YLOC);
  126.     } else {
  127.         UPmoveto(userPath, XLOC, YLOC);
  128.     }
  129.     }
  130.     closePath(userPath);
  131.     
  132.   /* draw it */
  133.     endUserPath(userPath, dps_ustroke);
  134.     sendUserPath(userPath);
  135.     
  136.     return self;
  137. }
  138.  
  139. - animate
  140. /*
  141.  * Lines is an unusual animation program in that it runs untimed; that is,
  142.  * it runs as fast as the CPU will allow, and it doesn't care that on faster
  143.  * CPUs the animation will run faster. An animation or game application
  144.  * will usually want to limit to frame rate to a value (for instance, 30
  145.  * frames a second), and on hardware not capable of that rate, end up doing
  146.  * the best it can. Such an application would also look at the time
  147.  * that actually passed between frames and increment!0A animation or game play
  148.  * accordingly. (See the sources to the BreakApp example on how it does it
  149.  * animation timing. BreakApp also allocates a graphic state for its view so
  150.  * that the lock/unlockFocus is faster.)
  151.  *
  152.  * Lines accomplishes its goal of running as fast as possible by creating
  153.  * a timed entry with a 0.0 second period. This means that the timed entry
  154.  * will fire and this method (animate) will be called as soon as possible.
  155.  * To make things even faster, we stay in this method until some event comes
  156.  * along. Staying in this method has the advantage that we do not need to
  157.  * lock or unlockFocus every frame. Of course, this only works as desired
  158.  * if the timed entry was placed with a period of 0.0 seconds.
  159.  * 
  160.  * If an event comes along, we return from this method and process the event.
  161.  * Then, unless the user stopped the animation, the timed entry brings us
  162.  * right back in to continue with the animation.
  163.  *
  164.  * Lines uses a buffered output window as a means to fake double-buffered
  165.  * animation. The current frame is drawn directly into the window. However,
  166.  * because the window is buffered, the drawing goes to the backing store,
  167.  * and not the screen.  Only when the frame is complete does Lines flush the
  168.  * window contents to the screen; this process is fast and provides a
  169.  * flicker-free update.  The next frame is then drawn into the backing store,
  170.  * and the cycle continues.
  171.  */
  172. {
  173.     int count;  
  174.     NXEvent dummyEvent;  // For peeking at the event queue. 
  175.     
  176.     [self lockFocus];
  177.     
  178.     do {
  179.       /* move all the corners... */
  180.     for (count = 0; count < numCorners; count++) {
  181.         XLOC += XVEL;
  182.         YLOC += YVEL;
  183.  
  184.       /*
  185.        * Detect collision with sides; if we collide, bounce back in some
  186.        * random fashion.
  187.        */
  188.         if (XLOC >= bounds.size.width) {
  189.         XLOC = bounds.size.width-1;
  190.         XVEL = -1-RANDINT(MAXVEL);
  191.         } else if (XLOC < bounds.origin.x) {
  192.         XLOC = bounds.origin.x;
  193.         XVEL = 1+RANDINT(MAXVEL);
  194.         }
  195.         if (YLOC >= bounds.size.height) {
  196.         YLOC = bounds.size.height-1;
  197.         YVEL = -1-RANDINT(MAXVEL);
  198.         } else if (YLOC < bounds.origin.y) {
  199.         YLOC = bounds.origin.y;
  200.         YVEL = 1+RANDINT(MAXVEL);
  201.         }
  202.     }
  203.  
  204.       /* draw our path and flush to the screen */
  205.     [self drawSelf:&bounds :1];
  206.     [window flushWindow];
  207.     
  208.     } while ([NXApp peekNextEvent:NX_ALLEVENTS
  209.             into:&dummyEven!0B        waitFor:0.0
  210.             threshold:NX_BASETHRESHOLD] == NULL);
  211.  
  212.     [self unlockFocus];
  213.  
  214.     return self;
  215. }
  216.  
  217. @end
  218.