|
|
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.