home *** CD-ROM | disk | FTP | other *** search
- /*
- * LinesView.m, a small sample view for showing timed entries & userpaths.
- * Author: Ali T. Ozer, NeXT Computer, Inc.
- * Written March 19, 1989.
- * Updated for 2.0 Oct 16, 1990 by Jayson Adams to use UserPath.[hm].
- * Updated for 3.0 March 24, 1992 by Ali Ozer
- *
- * You may freely copy, distribute and reuse the code in this example.
- * NeXT disclaims any warranty of any kind, expressed or implied, as to its
- * fitness for any particular use.
- *
- * LinesView draws a number of connected lines whose endpoints bounce around
- * randomly within the bounds of the view. The endpoints are stored in
- * an data array which is passed to PostScript as a user path. The
- * animation is performed by calling the "animate" method as often as
- * possible through a timed entry.
- */
-
- #import <appkit/appkit.h>
- #import <libc.h> // For random(), etc...
- #import <dpsclient/wraps.h> // For PS and DPS function prototypes
- #import "LinesView.h"
-
- #define RANDINT(n) (random() % (n+1)) // Return random integer 0..n
-
- #define XVEL corners[count].xVel // Some slimy shortcuts, asuuming we're
- #define YVEL corners[count].yVel // using "count" as corner counter.
- #define XLOC corners[count].xLoc
- #define YLOC corners[count].yLoc
-
- #define MAXVEL 12 // Maximum velocity of corners
-
-
- @implementation LinesView
-
- - initFrame:(NXRect *)rect
- {
- [super initFrame:rect];
-
- /* create a user path */
- userPath = newUserPath();
-
- running = NO;
-
- return self;
- }
-
- - free
- {
- /* be sure to stop the timed entry */
- if (running) {
- DPSRemoveTimedEntry(linesTimedEntry);
- }
- freeUserPath(userPath);
-
- return [super free];
- }
-
- void DrawIt(DPSTimedEntry te, double timeNow, void *data)
- {
- /* we set data to self so we can call this method from the timed entry */
- [(id)data animate];
- }
-
- - toggleRun:sender
- {
- /* start or stop the timed entry (we're called by a two-state button) */
- if (running) {
- DPSRemoveTimedEntry(linesTimedEntry);
- running = NO;
- } else {
- /* Call the DrawIt() function as often as possible... */
- linesTime!0@ry = DPSAddTimedEntry(0.0, &DrawIt, self,
- NX_BASETHRESHOLD);
- running = YES;
- }
-
- return self;
- }
-
- /*
- * This method should be connected to a UI object capable of generating
- * int numbers. Note that to successfully detect the initial value of this
- * slider as set through IB, we also declare an outlet named "numberOfCorners,"
- * and connect it to this UI object. Thus this method (setNumberOfCorners:)
- * gets called when the .nib is being loaded, and we can detect the
- * initial value of the slider. Because at initialization time the window
- * isn't up yet, we won't really update the display at that time, even though
- * display is called below.
- */
- - setNumberOfCorners:sender
- {
- int count;
- int oldNumCorners = numCorners;
-
- /* set the number of corners based on the "corners" slider */
- numCorners = MIN(MAXNUMCORNERS, MAX([sender intValue], MINNUMCORNERS));
-
- /* set the new corner starting positions & velocities */
- for (count = oldNumCorners; count < numCorners; count++) {
- XLOC = (int)(bounds.size.width) / 2;
- YLOC = (int)(bounds.size.height) / 2;
- XVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
- YVEL = (RANDINT(4) ? 1 : -1) * (1 + RANDINT(MAXVEL/2));
- }
- [self display];
-
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- int count;
-
- /* fill with the background color */
- NXEraseRect(&bounds);
-
- PSsetgray(NX_BLACK);
- PSsetlinewidth(0.0);
-
- /* "plot" the points */
- beginUserPath(userPath, NO);
- for (count = 0; count < numCorners; count++) {
- if (count) {
- UPlineto(userPath, XLOC, YLOC);
- } else {
- UPmoveto(userPath, XLOC, YLOC);
- }
- }
- closePath(userPath);
-
- /* draw it */
- endUserPath(userPath, dps_ustroke);
- sendUserPath(userPath);
-
- return self;
- }
-
- - animate
- /*
- * Lines is an unusual animation program in that it runs untimed; that is,
- * it runs as fast as the CPU will allow, and it doesn't care that on faster
- * CPUs the animation will run faster. An animation or game application
- * will usually want to limit to frame rate to a value (for instance, 30
- * frames a second), and on hardware not capable of that rate, end up doing
- * the best it can. Such an application would also look at the time
- * that actually passed between frames and increment!0A animation or game play
- * accordingly. (See the sources to the BreakApp example on how it does it
- * animation timing. BreakApp also allocates a graphic state for its view so
- * that the lock/unlockFocus is faster.)
- *
- * Lines accomplishes its goal of running as fast as possible by creating
- * a timed entry with a 0.0 second period. This means that the timed entry
- * will fire and this method (animate) will be called as soon as possible.
- * To make things even faster, we stay in this method until some event comes
- * along. Staying in this method has the advantage that we do not need to
- * lock or unlockFocus every frame. Of course, this only works as desired
- * if the timed entry was placed with a period of 0.0 seconds.
- *
- * If an event comes along, we return from this method and process the event.
- * Then, unless the user stopped the animation, the timed entry brings us
- * right back in to continue with the animation.
- *
- * Lines uses a buffered output window as a means to fake double-buffered
- * animation. The current frame is drawn directly into the window. However,
- * because the window is buffered, the drawing goes to the backing store,
- * and not the screen. Only when the frame is complete does Lines flush the
- * window contents to the screen; this process is fast and provides a
- * flicker-free update. The next frame is then drawn into the backing store,
- * and the cycle continues.
- */
- {
- int count;
- NXEvent dummyEvent; // For peeking at the event queue.
-
- [self lockFocus];
-
- do {
- /* move all the corners... */
- for (count = 0; count < numCorners; count++) {
- XLOC += XVEL;
- YLOC += YVEL;
-
- /*
- * Detect collision with sides; if we collide, bounce back in some
- * random fashion.
- */
- if (XLOC >= bounds.size.width) {
- XLOC = bounds.size.width-1;
- XVEL = -1-RANDINT(MAXVEL);
- } else if (XLOC < bounds.origin.x) {
- XLOC = bounds.origin.x;
- XVEL = 1+RANDINT(MAXVEL);
- }
- if (YLOC >= bounds.size.height) {
- YLOC = bounds.size.height-1;
- YVEL = -1-RANDINT(MAXVEL);
- } else if (YLOC < bounds.origin.y) {
- YLOC = bounds.origin.y;
- YVEL = 1+RANDINT(MAXVEL);
- }
- }
-
- /* draw our path and flush to the screen */
- [self drawSelf:&bounds :1];
- [window flushWindow];
-
- } while ([NXApp peekNextEvent:NX_ALLEVENTS
- into:&dummyEven!0B waitFor:0.0
- threshold:NX_BASETHRESHOLD] == NULL);
-
- [self unlockFocus];
-
- return self;
- }
-
- @end
-