This is LinesView.m in view mode; [Download] [Up]
/* * 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:(const 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... */ linesTimedEntry = DPSAddTimedEntry(0.01, &DrawIt, self, NX_BASETHRESHOLD); running = YES; } return self; } - off { if (running) { DPSRemoveTimedEntry(linesTimedEntry); running = NO; } 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 { NXRect r2; int count; /* fill with the background color */ [[self superview] getBounds:&r2]; NXDrawWhiteBezel(&bounds, &r2); // 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 the 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. if (!running) return nil; [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:&dummyEvent waitFor:0.0 threshold:NX_BASETHRESHOLD] == NULL); [self unlockFocus]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.