|
|
1.1 root 1: // SpaceView.m
2: //
3: // This class implements the flying starfield screen saver view.
4: //
5: // You may freely copy, distribute, and reuse the code in this example.
6: // NeXT disclaims any warranty of any kind, expressed or implied, as to its
7: // fitness for any particular use.
8:
9:
10: #import "SpaceView.h"
11: #import "Thinker.h"
12: #import "psfuncts.h"
13:
14: #import <dpsclient/wraps.h>
15: #import <appkit/NXImage.h>
16: #import <objc/zone.h>
17: #import <mach/mach.h>
18: #import <c.h>
19: #import <libc.h>
20: #import <math.h>
21:
22: #define PI (3.141592653589)
23:
24: @implementation SpaceView
25:
26: //takes theta and distance and stuffs it into x &y for *p
27: - convertToXY:(STAR *)p
28: {
29: p->draw->x = floor(bounds.size.width / 2 + (p->distance * cos(p-> theta)));
30: p->draw->y = floor(bounds.size.height / 2 + (p->distance * sin(p-> theta)));
31: return self;
32: }
33:
34:
35: - oneStep
36: {
37: int i, count, starsInArray = 0;
38: STAR *p;
39: NXPoint *t;
40:
41: if (nstars < NSTARS) [self addStar];
42:
43: for (i=0; i<nstars; i++)
44: {
45: p = &stars[i];
46: p->distance += p->delta;
47: p->delta *= p->ddelta;
48:
49: [self convertToXY:p];
50:
51: // only draw the star if it moved > 1 pixel
52: if (p->draw->x != p->erase->x ||
53: p->draw->y != p->erase->y)
54: {
55: BOOL mustErase = NO;
56: // add star to the erasure array
57: b[starsInArray] = *p->erase;
58: bc[starsInArray] = p->c;
59:
60: if (p->distance > p->changepoint[p->changemode])
61: {
62: (p->c)++; // increment character for next star size
63: (p->changemode)++;
64: }
65:
66: // clipping is off, so we must not draw outside view.
67: // replace stars that go too far...
68: if (p->draw->x < 0 ||
69: p->draw->y < 0 ||
70: p->draw->x + 7 > bounds.size.width ||
71: p->draw->y + 7 > bounds.size.height)
72: {
73: [self replaceStarAt:i];
74: mustErase = YES;
75: }
76:
77: w[starsInArray] = *p->draw;
78: wc[starsInArray] = p->c;
79:
80: if (mustErase || [self allowStars:p]) starsInArray++;
81:
82: t = p->draw; p->draw = p->erase; p->erase = t;
83: }
84: }
85:
86: bc[starsInArray] = wc[starsInArray] = 0; //null terminate string
87: if (starsInArray)
88: {
89: for (i=0; i<(starsInArray-1); i++)
90: {
91: bOffsets[i].x = b[i+1].x - b[i].x;
92: bOffsets[i].y = b[i+1].y - b[i].y;
93: wOffsets[i].x = w[i+1].x - w[i].x;
94: wOffsets[i].y = w[i+1].y - w[i].y;
95: }
96: bOffsets[i].x = bOffsets[i].y = wOffsets[i].x = wOffsets[i].y = 0;
97:
98: count = 0;
99: while (count < starsInArray)
100: { char tc;
101: int j;
102: // You get the best performance if you put out all the stars
103: // at once. This causes noticable flicker, so I put out
104: // 100 of the stars per iteration. This gives reasonable speed
105: // and flicker is hardly noticable. Besides, stars
106: // _should_ flicker a little...
107:
108: int t = (starsInArray - count);
109: i = (t < STARSPERIT)?t:STARSPERIT;
110: j = i + count;
111:
112: PSsetgray(NX_BLACK);
113: tc = bc[j]; bc[j] = 0;
114: PSWXYShow(b[count].x, b[count].y, &bc[count],
115: (float *)(&bOffsets[count].x), i*2);
116: bc[j] = tc;
117:
118: PSsetgray(NX_WHITE);
119: tc = wc[j]; wc[j] = 0;
120: PSWXYShow(w[count].x, w[count].y, &wc[count],
121: (float *)(&wOffsets[count].x), i*2);
122: wc[j] = tc;
123:
124: count += STARSPERIT;
125: }
126: }
127:
128: return self;
129: }
130:
131: // returns yes if the star is outside the avoidance rectangle
132: // this is really fast and loose but it works acceptibly well
133: // ps I could just use NXIntersectsRect() but I want to avoid
134: // trap overhead. Call me paranoid...
135: - (BOOL) allowStars:(const STAR *)p
136: {
137: // just return if voidRect not set
138: if ((!voidRect.size.width) ||
139: p->draw->x < voidRect.origin.x ||
140: p->draw->y < voidRect.origin.y ||
141: p->draw->x+7 > voidRect.origin.x+voidRect.size.width ||
142: p->draw->y+7 > voidRect.origin.y+voidRect.size.height ||
143:
144: p->erase->x < voidRect.origin. x ||
145: p->erase->y < voidRect.origin. y ||
146: p->erase->x+7 > voidRect.origin.x+voidRect.size.width ||
147: p->erase->y+7 > voidRect.origin.y+voidRect.size.height) return YES;
148:
149: return NO;
150: }
151:
152: - initFrame:(const NXRect *)frameRect
153: {
154: [super initFrame:frameRect];
155: [self allocateGState]; // For faster lock/unlockFocus
156: [self setClipping:NO]; // even faster...
157: [self setRadius];
158: loadPSProcedures();
159: PSWDefineFont("StarFont");
160:
161: return self;
162: }
163:
164: - drawSelf:(const NXRect *)rects :(int)rectCount
165: {
166: // this drawself doesn't really draw the view at all.
167: // in fact it just promotes the window to screen depth...
168:
169: NXRect t = {0,0,1,1};
170:
171: PSsetrgbcolor(1,0,0);
172: NXRectFill(&t); //yucky trick for window depth promotion!
173: PSsetgray(NX_BLACK); NXRectFill(&t);
174:
175: PSselectfont("StarFont", 1.0);
176:
177: return self;
178: }
179:
180: - sizeTo:(NXCoord)width :(NXCoord)height
181: {
182: [super sizeTo:width :height];
183:
184: if (oldSize.width != bounds.size.width ||
185: oldSize.height != bounds.size.height)
186: {
187: oldSize.width = bounds.size.width;
188: oldSize.height = bounds.size.height;
189: [self setRadius];
190: nstars = 0;
191: [self display];
192: }
193:
194: return self;
195: }
196:
197: // only call addStar if there is room in the stars array!
198: - addStar
199: {
200: [self replaceStarAt:nstars++];
201: return self;
202: }
203:
204: - replaceStarAt:(int)index
205: {
206: float dist, t;
207: int tries = 0;
208: STAR *p = &stars[index];
209: BOOL inBounds;
210:
211: p->draw = &p->r1;
212: p->erase = &p->r2;
213:
214:
215: do {
216: p->theta = randBetween(0,(2*PI));
217:
218: if (tries++ < 3) p->distance = randBetween(1, radius);
219: else p->distance = randBetween(1, p->distance);
220:
221: inBounds = YES;
222: [self convertToXY:p];
223:
224: if (p->draw->x < 0 || p->draw->y < 0 ||
225: p->draw->x + 7 > bounds.size.width ||
226: p->draw->y + 7 > bounds.size.height)
227: {
228: inBounds = NO;
229: }
230: } while (!inBounds);
231:
232: p->delta = (0.2);
233:
234: p->ddelta = randBetween(1.0, 1.1);
235:
236:
237:
238: t = randBetween(0, (0.42*radius));
239: dist = MAX(20,t);
240: p->changepoint[0] = p->distance + 5; // to b
241: p->changepoint[1] = p->changepoint[0] - 5 + dist + dist; // to c
242:
243:
244: p->changepoint[2] = p->changepoint[1] + dist; // to d
245: p->changepoint[3] = p->changepoint[2] + dist; // to e
246: p->changepoint[4] = p->changepoint[3] + dist; // to f
247: p->changepoint[5] = 100000; // never change to g
248:
249: p->changemode = 0;
250:
251: if ((++toggle) & 1) p->c = 'a';
252: else p->c = 'g';
253:
254: p->r2 = p->r1;
255:
256: return self;
257: }
258:
259: - setRadius
260: {
261: float x = bounds.size.width;
262: float y = bounds.size.height;
263: radius = (sqrt(x*x + y*y))/2;
264: return self;
265: }
266:
267: - (const char *)windowTitle
268: {
269: return "The Final Frontier";
270: }
271:
272: - setVoidRect:(const NXRect *)r
273: {
274: voidRect = *r;
275: return self;
276: }
277:
278: - didLockFocus
279: {
280: PSselectfont("StarFont", 1.0);
281: return self;
282: }
283:
284: - (BOOL)useBufferedWindow
285: { return NO;
286: }
287:
288: - inspector:sender
289: {
290: return [sender spaceInspector];
291: }
292:
293: - (BOOL)ignoreMouseMovement
294: { return NO;
295: }
296:
297: - inspectorWillBeRemoved
298: { return self; // just a prototype
299: }
300:
301: - inspectorInstalled
302: { return self; // just a prototype
303: }
304:
305: @end
306:
307:
308:
309: // this class is only used in the inspector, it animates
310: // when it draws itself.
311:
312: @implementation StaticSpaceView
313:
314: - drawSelf:(const NXRect *)rects :(int)rectCount
315: {
316: int i;
317:
318: if (!rects || !rectCount) return self;
319:
320: PSselectfont("StarFont", 1.0);
321:
322: PSsetgray(NX_BLACK);
323: NXRectFill(rects);
324:
325: for (i=0; i<20; i++)
326: {
327: [self oneStep];
328: [[self window] flushWindow];
329: NXPing();
330: }
331:
332: return self;
333: }
334:
335: - initFrame:(const NXRect *)frameRect
336: {
337: [super initFrame:frameRect];
338:
339: while (nstars < NSTARS) [self addStar];
340: return self;
341: }
342:
343: - sizeTo:(NXCoord)width :(NXCoord)height
344: {
345: [super sizeTo:width :height];
346:
347: nstars = 0;
348: while (nstars < NSTARS) [self addStar];
349: return self;
350: }
351:
352: @end
353:
354:
355:
356:
357:
358: @implementation View(nonretainedFillMethod)
359:
360: // I add this method as a category of View to be sure that all
361: // my views implement it. I really want to use nonretained windows
362: // but they are drawn via drawSelf at all kinds of goofy times. It
363: // seems like the kit kind of throws up its hands when it doesn't have
364: // a buffer to draw from, so you get a lot more drawSelfs than you need,
365: // and sometimes you don't get them when you really want them. I know
366: // when I need the background filled in black, so I factor that out of
367: // my drawSelf and then drawself only draws things that are already on
368: // screen so you don't see it happen. I will only call this method on
369: // a nonretained (and full screen) window.
370:
371: - fillBoundsWithBlack
372: {
373: if ([self canDraw])
374: {
375: [self lockFocus];
376: PSsetgray(NX_BLACK);
377: NXRectFill(&bounds);
378: [self unlockFocus];
379: }
380: return self;
381: }
382:
383: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.