|
|
1.1 root 1: /*
2: The animation guts from a freely distributable X program:
3: xsperm.c
4: Drew Olbrich, Febrary 1991
5: Note -- This code originally served as a demonstration
6: of how to do animation under X. The "guts" of the program
7: which draws the sperm are consequently located in one huge
8: chunk in the update_display() routine, and can be easily
9: cut out.
10:
11: The animation function wrapped in a NeXTstep View subclass by Ali Ozer, May 91
12: Very minor changes so this thing works as a screen saver module by sam streeper,
13: August 91
14: The "oneStep" method computes new locations.
15: */
16:
17: #import "SpermView.h"
18: #import "Thinker.h"
19: #import <appkit/appkit.h>
20:
21: #define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
22: #define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))
23:
24: #define VEC_SET(x, a, b) x[0] = a, x[1] = b
25: #define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
26: #define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
27: #define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
28: #define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
29: #define VEC_MULT(x, a) x[0] *= a, x[1] *= a
30: #define VEC_DIV(x, a) x[0] /= a, x[1] /= a
31: #define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
32: #define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }
33:
34: #define MINRAD 0.1
35: #define RADSTEP 2.0
36: #define MAXRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP)
37: #define INITRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP)
38:
39: // RANDINT(n) returns an integer 0..n-1
40: // RANDFLOAT(f) returns a float [0..f] (inclusive on both ends)
41:
42: #define RANDINT(n) (random() % (n))
43: #define RANDFLOAT(f) (((f) * (float)(random() & 0x0ffff)) / (float)0x0ffff)
44:
45: @implementation SpermView
46:
47: - initFrame:(const NXRect *)rect
48: {
49: [super initFrame:rect];
50:
51: [self allocateGState]; // For faster lock/unlockFocus
52:
53: dir = 1.0;
54: rad = INITRAD;
55: [self getSpermCount];
56: [self getLineWidth];
57: [self getUseColor];
58: [inspectorPanel display];
59:
60: color = NX_COLORWHITE;
61: alreadyInitialized = NO;
62: randCount1 = 100;
63: randCount2 = 200;
64:
65: uPath = newUserPath();
66:
67: return self;
68: }
69:
70: - (void)initializeLine:(int)i
71: {
72: double angle = RANDFLOAT(10.0) + 5.0;
73: prevX[i][0] = x[i][0] = (double) (RANDINT((int)NX_WIDTH(&bounds)));
74: prevX[i][1] = x[i][1] = (double) (RANDINT((int)NX_HEIGHT(&bounds)));
75: v[i][0] = RANDFLOAT(2.0) - 1.0;
76: v[i][1] = RANDFLOAT(2.0) - 1.0;
77: sine[i] = sin(angle*M_PI/180.0);
78: cosine[i] = cos(angle*M_PI/180.0);
79: vel[i] = RANDFLOAT(4.0) + 4.0;
80: VEC_NORM(v[i]);
81: }
82:
83: - (void)getFocusFromEvent:(NXEvent *)event
84: {
85: NXPoint loc = event->location;
86: [self convertPoint:&loc fromView:nil];
87: mouse[0] = loc.x;
88: mouse[1] = loc.y;
89: }
90:
91: - (BOOL)acceptsFirstMouse
92: { return YES;
93: }
94:
95: - mouseDown:(NXEvent *)event
96: {
97: [self getFocusFromEvent:event];
98: return self;
99: }
100:
101: - effectOne
102: {
103: VECTOR y;
104: int i;
105:
106: dir *= -1.0;
107: for (i = 0; i < MAXCOUNT; i++) {
108: VEC_COPY(y, v[i]);
109: if (dir == -1.0) {
110: v[i][0] = y[1];
111: v[i][1] = -y[0];
112: } else {
113: v[i][0] = -y[1];
114: v[i][1] = y[0];
115: }
116: }
117: return self;
118: }
119:
120: - effectTwo
121: {
122: int i;
123: for (i = 0; i < MAXCOUNT; i++) {
124: v[i][0] = -v[i][0];
125: }
126: return self;
127: }
128:
129: - effectThree
130: {
131: int i;
132: for (i = 0; i < MAXCOUNT; i++) {
133: v[i][1] = -v[i][1];
134: }
135: return self;
136: }
137:
138: - effectFour
139: {
140: [self effectTwo];
141: [self effectThree];
142: return self;
143: }
144:
145: - effectFive
146: {
147: [self effectOne];
148: [self effectFour];
149: return self;
150: }
151:
152: - effectSix
153: {
154: rad = MIN(rad * RADSTEP, MAXRAD);
155: return self;
156: }
157:
158: - effectSeven
159: {
160: rad = MAX(rad / RADSTEP, MINRAD);
161: return self;
162: }
163:
164: - doEffectNumber:(int)val
165: {
166: switch (val) {
167: case 0: [self effectOne]; break;
168: case 1: [self effectTwo]; break;
169: case 2: [self effectThree]; break;
170: case 3: [self effectFour]; break;
171: case 4: [self effectFive]; break;
172: case 5: [self effectSix]; break;
173: case 6: [self effectSeven]; break;
174: default: break;
175: }
176: return self;
177: }
178:
179: - oneStep
180: {
181: int i, cnt;
182: POINT lLeft, uRight;
183: NXRect eraseRect;
184:
185: uRight[0] = lLeft[0] = x[0][0];
186: uRight[1] = lLeft[1] = x[0][1];
187:
188: for (i = 0; i < count; i++) {
189: VECTOR w, y;
190: POINT p;
191: double r;
192:
193: for (cnt = 0; cnt < 2; cnt++) {
194: if (prevX[i][cnt] < lLeft[cnt]) lLeft[cnt] = prevX[i][cnt];
195: else if (prevX[i][cnt] > uRight[cnt]) uRight[cnt] = prevX[i][cnt];
196: if (x[i][cnt] < lLeft[cnt]) lLeft[cnt] = x[i][cnt];
197: else if (x[i][cnt] > uRight[cnt]) uRight[cnt] = x[i][cnt];
198: }
199:
200: prevX[i][0] = x[i][0]; /* old location */
201: prevX[i][1] = x[i][1];
202:
203: VEC_SUB(w, x[i], mouse);
204: VEC_NORM(w);
205: VEC_COPY(y, w);
206: w[0] = y[0]*cosine[i] - dir*y[1]*sine[i];
207: w[1] = y[1]*cosine[i] + dir*y[0]*sine[i];
208: VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w);
209:
210: VEC_SUB(w, p, x[i]);
211: r = VEC_LEN(w);
212: VEC_DIV(w, r);
213:
214: VEC_ADDS(v[i], v[i], 1.0, w);
215:
216: VEC_NORM(v[i]);
217: VEC_MULT(v[i], vel[i]);
218:
219: VEC_ADD(x[i], x[i], v[i]);
220:
221: }
222:
223: NXSetRect (&eraseRect, lLeft[0], lLeft[1], uRight[0]-lLeft[0], uRight[1]-lLeft[1]);
224: NXInsetRect (&eraseRect, -1.0-lineWidth, -1.0-lineWidth);
225: PSsetgray(0);
226: NXRectFill(&eraseRect);
227:
228: [self drawPath];
229:
230: if (--randCount1 < 0)
231: {
232: randCount1 = RANDINT(700);
233: mouse[0] = randBetween(0,bounds.size.width);
234: mouse[1] = randBetween(0,bounds.size.height);
235: }
236: if (--randCount2 < 0)
237: {
238: randCount2 = RANDINT(600);
239: [self doEffectNumber:(randCount2 % 7)];
240: }
241: return self;
242: }
243:
244: // Modify "orig" by upto plus or minus "by" keeping it in the specified range...
245:
246: static float randMod(float orig, float by, float min, float max)
247: {
248: orig = orig + RANDFLOAT(by * 2.0) - by;
249: return (orig < min) ? min : ((orig > max) ? max : orig);
250: }
251:
252: - drawPath
253: {
254: int cnt;
255:
256: PSsetlinewidth (lineWidth);
257: if (useColors) {
258: color = NXConvertRGBToColor(randMod(NXRedComponent(color), 0.05, 0.0, 1.0),
259: randMod(NXGreenComponent(color), 0.05, 0.0, 1.0),
260: randMod(NXBlueComponent(color), 0.05, 0.0, 1.0));
261: }
262: else color = NX_COLORWHITE;
263:
264: NXSetColor (color);
265:
266: beginUserPath(uPath, NO);
267: for (cnt = 0; cnt < count; cnt++) {
268: UPmoveto(uPath, (float)prevX[cnt][0], (float)prevX[cnt][1]);
269: UPlineto(uPath, (float)x[cnt][0], (float)x[cnt][1]);
270: }
271: closePath(uPath);
272: endUserPath(uPath, dps_ustroke);
273: sendUserPath(uPath);
274:
275: return self;
276: }
277:
278: - drawSelf:(const NXRect *)rects :(int)rectCount
279: {
280: if (!rects || !rectCount) return self;
281:
282: PSsetgray(NX_BLACK);
283: NXRectFill(rects);
284: [self drawPath];
285: return self;
286: }
287:
288: - newWindow
289: {
290: mouse[0] = randBetween(0,bounds.size.width);
291: mouse[1] = randBetween(0,bounds.size.height);
292:
293: return self;
294: }
295:
296: - free
297: {
298: freeUserPath(uPath);
299: return [super free];
300: }
301:
302: - setNumLines:sender
303: {
304: int i;
305: int oldCount = count;
306: char str[100];
307:
308: // set the number of lines
309: count = MIN(MAXCOUNT, MAX([sender intValue], 1));
310:
311: // initialize velocities & such
312: for (i = oldCount; i < count; i++) {
313: [self initializeLine:i];
314: }
315:
316: [self display];
317:
318: sprintf(str,"%d", count);
319: NXWriteDefault([NXApp appName], "SpermViewCount", str);
320:
321: return self;
322: }
323:
324: - getSpermCount
325: {
326: const char *ptr;
327: int val;
328:
329: [spermCountSlider setMinValue: 10];
330: [spermCountSlider setMaxValue: MAXCOUNT];
331:
332: ptr = NXGetDefaultValue([NXApp appName], "SpermViewCount");
333: if (ptr)
334: {
335: sscanf(ptr,"%d",&val);
336: if (val >= 10 && val <= MAXCOUNT) count = val;
337: else count = MAXCOUNT;
338: }
339: else count = MAXCOUNT;
340:
341: return self;
342: }
343:
344: - setUseColor:sender
345: {
346: useColors = [sender state];
347:
348: if (useColors)
349: NXWriteDefault([NXApp appName], "SpermViewColor", "Yes");
350: else
351: NXRemoveDefault([NXApp appName], "SpermViewColor");
352:
353: return self;
354: }
355:
356: - getUseColor
357: {
358: const char *ptr;
359:
360: ptr = NXGetDefaultValue([NXApp appName], "SpermViewColor");
361:
362: if (!ptr || !strcmp(ptr,"No")) useColors = NO;
363: else useColors = YES;
364:
365: return self;
366: }
367:
368: - setLineWidth:sender
369: {
370: char str[50];
371:
372: lineWidth = MAX([sender floatValue], 0.0);
373: sprintf(str,"%5.1f", lineWidth);
374: NXWriteDefault([NXApp appName], "SpermViewWidth", str);
375:
376: return self;
377: }
378:
379: - getLineWidth
380: {
381: const char *ptr;
382: float val;
383:
384: [spermWidthSlider setMinValue: 0];
385: [spermWidthSlider setMaxValue: 8];
386:
387: ptr = NXGetDefaultValue([NXApp appName], "SpermViewWidth");
388: if (ptr)
389: {
390: sscanf(ptr,"%f",&val);
391: if (val >= 0 && val <= 8) lineWidth = val;
392: else lineWidth = 0;
393: }
394: else lineWidth = 0;
395:
396: return self;
397: }
398:
399: - sizeTo:(NXCoord)width :(NXCoord)height
400: {
401: [super sizeTo:width :height];
402:
403: if (!alreadyInitialized)
404: { int i;
405: mouse[0] = NX_MIDX(&bounds);
406: mouse[1] = NX_MIDY(&bounds);
407:
408: for (i = 0; i < MAXCOUNT; i++) {
409: [self initializeLine:i];
410: }
411: alreadyInitialized = YES;
412: }
413:
414: [self newWindow];
415: return self;
416: }
417:
418: - (const char *)windowTitle
419: { return "Sperm";
420: }
421:
422: - (BOOL) useBufferedWindow;
423: { return YES;
424: }
425:
426:
427: - inspector:sender
428: {
429: char buf[MAXPATHLEN];
430:
431: if (!inspectorPanel)
432: {
433: [NXBundle getPath:buf forResource:"sperm" ofType:"nib" inDirectory:[sender moduleDirectory:"Sperm"] withVersion:0];
434: [NXApp loadNibFile:buf owner:self withNames:NO];
435:
436: [spermCountSlider setIntValue:count];
437: [spermWidthSlider setFloatValue:lineWidth];
438: [colorButton setState: (useColors ? 1:0)];
439: }
440: return inspectorPanel;
441: }
442:
443: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.