This is SpermViewPart.m in view mode; [Download] [Up]
/* The animation guts from a freely distributable X program: xsperm.c Drew Olbrich, Febrary 1991 Note -- This code originally served as a demonstration of how to do animation under X. The "guts" of the program which draws the sperm are consequently located in one huge chunk in the update_display() routine, and can be easily cut out. The animation function wrapped in a NeXTstep View subclass by Ali Ozer, May 91 Very minor changes so this thing works as a screen saver module by sam streeper, August 91 The "oneStep" method computes new locations. */ #import "SpermViewPart.h" #import "Thinker.h" #import <math.h> #import <libc.h> #import <dpsclient/wraps.h> // For PS and DPS function prototypes #import <appkit/color.h> // For color stuff #import <appkit/nextstd.h> // For MIN MAX #import <appkit/Button.h> // intValue, etc #import <appkit/Application.h> // For NX_BASETHRESHOLD and peek event #import <appkit/Window.h> #define COUNT 200 #define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1]) #define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1])) #define VEC_SET(x, a, b) x[0] = a, x[1] = b #define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1] #define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1] #define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1] #define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1] #define VEC_MULT(x, a) x[0] *= a, x[1] *= a #define VEC_DIV(x, a) x[0] /= a, x[1] /= a #define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1] #define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); } #define MINRAD 0.1 #define RADSTEP 2.0 #define MAXRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP) #define INITRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP) // RANDINT(n) returns an integer 0..n-1 // RANDFLOAT(f) returns a float [0..f] (inclusive on both ends) #define RANDINT(n) (random() % (n)) #define RANDFLOAT(f) (((f) * (float)(random() & 0x0ffff)) / (float)0x0ffff) @implementation SpermView - initFrame:(const NXRect *)rect { id retVal = [super initFrame:rect]; [self allocateGState]; // For faster lock/unlockFocus dir = 1.0; rad = INITRAD; count = 200; useColors = NO; dontErase = NO; color = NX_COLORWHITE; alreadyInitialized = NO; randCount1 = 100; randCount2 = 200; uPath = newUserPath(); return retVal; } - (void)initializeLine:(int)i { double angle = RANDFLOAT(10.0) + 5.0; prevX[i][0] = x[i][0] = (double) (RANDINT((int)NX_WIDTH(&bounds))); prevX[i][1] = x[i][1] = (double) (RANDINT((int)NX_HEIGHT(&bounds))); v[i][0] = RANDFLOAT(2.0) - 1.0; v[i][1] = RANDFLOAT(2.0) - 1.0; sine[i] = sin(angle*M_PI/180.0); cosine[i] = cos(angle*M_PI/180.0); vel[i] = RANDFLOAT(4.0) + 4.0; VEC_NORM(v[i]); } - (void)getFocusFromEvent:(NXEvent *)event { NXPoint loc = event->location; [self convertPoint:&loc fromView:nil]; mouse[0] = loc.x; mouse[1] = loc.y; } - (BOOL)acceptsFirstMouse { return YES; } - mouseDown:(NXEvent *)event { [self getFocusFromEvent:event]; return self; } - effectOne { VECTOR y; int i; dir *= -1.0; for (i = 0; i < COUNT; i++) { VEC_COPY(y, v[i]); if (dir == -1.0) { v[i][0] = y[1]; v[i][1] = -y[0]; } else { v[i][0] = -y[1]; v[i][1] = y[0]; } } return self; } - effectTwo { int i; for (i = 0; i < COUNT; i++) { v[i][0] = -v[i][0]; } return self; } - effectThree { int i; for (i = 0; i < COUNT; i++) { v[i][1] = -v[i][1]; } return self; } - effectFour { [self effectTwo]; [self effectThree]; return self; } - effectFive { [self effectOne]; [self effectFour]; return self; } - effectSix { rad = MIN(rad * RADSTEP, MAXRAD); return self; } - effectSeven { rad = MAX(rad / RADSTEP, MINRAD); return self; } - doEffectNumber:(int)val { switch (val) { case 0: [self effectOne]; break; case 1: [self effectTwo]; break; case 2: [self effectThree]; break; case 3: [self effectFour]; break; case 4: [self effectFive]; break; case 5: [self effectSix]; break; case 6: [self effectSeven]; break; default: break; } return self; } - oneStep { int i, cnt; POINT lLeft, uRight; NXRect eraseRect; uRight[0] = lLeft[0] = x[0][0]; uRight[1] = lLeft[1] = x[0][1]; for (i = 0; i < COUNT; i++) { VECTOR w, y; POINT p; double r; if (!dontErase) { for (cnt = 0; cnt < 2; cnt++) { if (prevX[i][cnt] < lLeft[cnt]) lLeft[cnt] = prevX[i][cnt]; else if (prevX[i][cnt] > uRight[cnt]) uRight[cnt] = prevX[i][cnt]; if (x[i][cnt] < lLeft[cnt]) lLeft[cnt] = x[i][cnt]; else if (x[i][cnt] > uRight[cnt]) uRight[cnt] = x[i][cnt]; } } prevX[i][0] = x[i][0]; /* old location */ prevX[i][1] = x[i][1]; VEC_SUB(w, x[i], mouse); VEC_NORM(w); VEC_COPY(y, w); w[0] = y[0]*cosine[i] - dir*y[1]*sine[i]; w[1] = y[1]*cosine[i] + dir*y[0]*sine[i]; VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w); VEC_SUB(w, p, x[i]); r = VEC_LEN(w); VEC_DIV(w, r); VEC_ADDS(v[i], v[i], 1.0, w); VEC_NORM(v[i]); VEC_MULT(v[i], vel[i]); VEC_ADD(x[i], x[i], v[i]); } if (!dontErase) { NXSetRect (&eraseRect, lLeft[0], lLeft[1], uRight[0]-lLeft[0], uRight[1]-lLeft[1]); NXInsetRect (&eraseRect, -1.0-lineWidth, -1.0-lineWidth); PSsetgray(0); NXRectFill(&eraseRect); } [self drawPath]; if (--randCount1 < 0) { randCount1 = RANDINT(700); mouse[0] = randBetween(0,bounds.size.width); mouse[1] = randBetween(0,bounds.size.height); } if (--randCount2 < 0) { randCount2 = RANDINT(600); [self doEffectNumber:(randCount2 % 7)]; } return self; } // Modify "orig" by upto plus or minus "by" keeping it in the specified range... static float randMod(float orig, float by, float min, float max) { orig = orig + RANDFLOAT(by * 2.0) - by; return (orig < min) ? min : ((orig > max) ? max : orig); } - drawPath { int cnt; PSsetlinewidth (lineWidth); if (useColors) { color = NXConvertRGBToColor(randMod(NXRedComponent(color), 0.05, 0.0, 1.0), randMod(NXGreenComponent(color), 0.05, 0.0, 1.0), randMod(NXBlueComponent(color), 0.05, 0.0, 1.0)); } NXSetColor (color); beginUserPath(uPath, NO); for (cnt = 0; cnt < COUNT; cnt++) { UPmoveto(uPath, (float)prevX[cnt][0], (float)prevX[cnt][1]); UPlineto(uPath, (float)x[cnt][0], (float)x[cnt][1]); } closePath(uPath); endUserPath(uPath, dps_ustroke); sendUserPath(uPath); return self; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (!rects || !rectCount) return self; PSsetgray(NX_BLACK); NXRectFill(rects); [self drawPath]; return self; } - newWindow { mouse[0] = randBetween(0,bounds.size.width); mouse[1] = randBetween(0,bounds.size.height); return self; } - free { freeUserPath(uPath); return [super free]; } - setNumLines:(int)val { int i; int oldCount = count; // set the number of lines count = MIN(MAXCOUNT, MAX(val, 1)); // initialize velocities & such for (i = oldCount; i < count; i++) { [self initializeLine:i]; } [self display]; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { [super sizeTo:width :height]; if (!alreadyInitialized) { int i; mouse[0] = NX_MIDX(&bounds); mouse[1] = NX_MIDY(&bounds); for (i = 0; i < COUNT; i++) { [self initializeLine:i]; } alreadyInitialized = YES; } [self newWindow]; return self; } - (const char *)windowTitle { return "Sperm"; } - (BOOL) useBufferedWindow; { return YES; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.