|
|
1.1 ! root 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: linesTimedEntry = 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 the 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:&dummyEvent ! 210: waitFor:0.0 ! 211: threshold:NX_BASETHRESHOLD] == NULL); ! 212: ! 213: [self unlockFocus]; ! 214: ! 215: return self; ! 216: } ! 217: ! 218: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.