This is SwarmView.m in view mode; [Download] [Up]
/*- ** XSwarm ** ====== ** ** Purpose: Display a swarm of bees chasing a wasp. ** ** Features: uses only integer math ** has no redeeming social value ** ** Comments: Most of this program is greatly rewritten by me, but the ** initial math and update stuff is left over from the older ** program. Much of the NeXT related stuff is from LizardSaver. ** ** Created: by Jeff Butterworth on 7/11/90 ** butterwo@cs.unc.edu ** ** NeXTPoRT: Kurt Werle ** frsvnsvn!kurt@crash.cts.com ** ** Updated: by Kurt Werle on 10/28/91 Mod Dec 16, 1991, Scott Byer - Changed the way drawing was done to use wraps and encoded user paths. Separated variable initialization code from variable allocation code. Added 'stages' at beginning of oneStep. */ /* Standard Includes */ #import "SwarmView.h" #import "SwarmWraps.c" #import <appkit/NXImage.h> #import <appkit/Panel.h> // for NXRunAlertPanel() #import <appkit/Control.h> #import <dpsclient/wraps.h> #import <dpsclient/dpsclient.h> #import <libc.h> #import <stdlib.h> @implementation SwarmView /* Single step the wasp and the bees. Called continuously by BackSpace as long as the animation is needed. */ - oneStep { register int xCd, yCd, beeNum, xVel, yVel; int curWX, curWY; /* Wasp position and velocity. */ /* Clear the BBOX before all position calculations. */ RESET_BBOX; /* If the wasp is the cursor, set it. Otherwise, figure out the change in velocity, and then add the velocity into the current position. */ if (cursorwasp) { float mouseX, mouseY; PScurrentmouse([window windowNum], &mouseX, &mouseY); /* Age the old position and check against the bounding box. */ xCd = OWX(0); yCd = OWY(0); BUMP_BBOX(xCd, yCd); WX(1) = xCd; WY(1) = yCd; /* Get the new position. */ xCd = (int) mouseX; yCd = (int) mouseY; BUMP_BBOX(xCd, yCd); WX(0) = xCd; WY(0) = yCd; } else { /* Accellerate the wasp and check against the wasp speed limits. */ xVel = WXV + RAND(WASPACC); if (xVel > WASPVEL) xVel = WASPVEL; else if (xVel < -WASPVEL) xVel = -WASPVEL; yVel = WYV + RAND(WASPACC); if (yVel > WASPVEL) yVel = WASPVEL; else if (yVel < -WASPVEL) yVel = -WASPVEL; /* Move the wasp, aging the array at the same time, and doing bounds checking. */ xCd = OWX(0); yCd = OWY(0); WX(1) = xCd; WY(1) = yCd; BUMP_BBOX(xCd, yCd); xCd += xVel; if (xCd < BORDER) xVel = abs(xVel); else if (xCd > VIEW_WIDTH - BORDER) xVel = -abs(xVel); yCd += yVel; if (yCd < BORDER) yVel = abs(yVel); else if (yCd > VIEW_HEIGHT - BORDER) yVel = -abs(yVel); /* Dump the positions and velocities out into their proper places. */ WX(0) = xCd; WY(0) = yCd; WXV = xVel; WYV = yVel; BUMP_BBOX(xCd, yCd); } /* Store the wasp position in simple variables as loop invariants. */ curWX = WX(1); curWY = WY(1); /* Bee position and velocity. */ /* Blip the velocity of some random bees, to keep things interesting. By doing the blipping here, their velocities still get adjusted later, making the 'blipping' appear natural and smooth. */ for ( beeNum = 0 ; beeNum < (bees / 20) ; beeNum++ ) { xv[random() % bees] += RAND(BEEVEL); yv[random() % bees] += RAND(BEEVEL); } /* Each bee must have it's velocity and position adjusted. Loop through the bees, doing the processing for each. */ for ( beeNum = 1 ; beeNum < bees ; beeNum++ ) { int dist, distX, distY; /* Grab the current bee's position - used for calculating new velocity, new position, and aging the current position. */ xCd = OX(0, beeNum); yCd = OY(0, beeNum); /* Age the array right away. */ X(1, beeNum) = xCd; Y(1, beeNum) = yCd; BUMP_BBOX(xCd, yCd); /* Calculate the acceleration of each be based on it's relative position to the wasp. Approximate the distance of this bee from the previous position of the wasp, and use that to accelerate the bee in the right direction at it's meximum velocity. */ distX = curWX - xCd; distY = curWY - yCd; dist = abs(distX) + abs(distY); if (dist == 0) dist = 1; /* Calculate the new velocities and do velocity checking. */ xVel = xv[beeNum] + (distX * BEEACC) / dist; if (xVel > BEEVEL) xVel = BEEVEL; if (xVel < -BEEVEL) xVel = -BEEVEL; yVel = yv[beeNum] + (distY * BEEACC) / dist; if (yVel > BEEVEL) yVel = BEEVEL; if (yVel < -BEEVEL) yVel = -BEEVEL; /* Move the bee, aging it at the same time - no bounds checking... */ xCd += xVel; X(0, beeNum) = xCd; yCd += yVel; Y(0, beeNum) = yCd; BUMP_BBOX(xCd, yCd); /* Dump the velocities into their arrays */ xv[beeNum] = xVel; yv[beeNum] = yVel; } /* Display. */ /* If you thought that the structure that the calculations were being done in was wierd, here is why. For the type of drawing we are doing, encoded user paths are the fastest way to get it done. Well, the arrangement of the coordinates is set up so I can just hand off the calculation array and bounding box and operand array straight off. The operand array is never touched after initialization - the coordinates are always to be eaten by a moveto followed by a lineto. NOTE: If you ever think about going to floating point calculations here, don't. Go to 16.16 fixed point instead - the user path stuff will eat that up faster than integers, even. */ PSsetgray(1.0); DPSDoUserPath(DRAW_BASE, (4 * bees), dps_long, ops, (2 * bees), &drawBBox[0], dps_ustroke); PSsetgray(0.0); DPSDoUserPath(ERASE_BASE, (4 * bees), dps_long, ops, (2 * bees), &eraseBBox[0], dps_ustroke); /* Copy the coordinates just used for drawing into the ones to be used next time for erasing. */ COPY_CDS; COPY_BBOX; return self; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; [self initSwarmView]; [inspectorPanel display]; return self; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (!rects || !rectCount) { return self; } PSsetgray(0.0); NXRectFill(rects); return self; } - initSwarmView { register int time; /* Get the random number generator ready. */ srandom(getpid()); bees = BEES; /* Initial number of bees. */ cursorwasp = NO; /* Use the cursor for the wasp position. */ /* Initialize the pointers - done once. */ drawCds = &spaceOne; eraseCds = &spaceTwo; drawBBox = &bboxOne; eraseBBox = &bboxTwo; /* Initialize the operator array - done only once. */ for ( time = 0 ; time < COORDS * 2 ; ) { ops[time++] = dps_moveto; ops[time++] = dps_lineto; } /* Initialize the values. */ [self initValues]; return self; } - initValues { register int beeNum, xVal, yVal; RESET_BBOX; /* Initialize the wasp location and velocity. */ xVal = BORDER + random() % (int) (VIEW_WIDTH - 2*BORDER); WX(0) = xVal; WX(1) = xVal; yVal = BORDER + random() % (int) (VIEW_HEIGHT - 2*BORDER); WY(0) = yVal; WY(1) = yVal; BUMP_BBOX(xVal, yVal); WXV = 0; WYV = 0; /* Initialize the bee locations and velocities. */ for ( beeNum = 1 ; beeNum <= MAXBEES ; beeNum++ ) { xVal = random() % VIEW_WIDTH; X(0,beeNum) = xVal; X(1,beeNum) = xVal; yVal = random() % VIEW_HEIGHT; Y(0,beeNum) = yVal; Y(1,beeNum) = yVal; BUMP_BBOX(xVal, yVal); xv[beeNum] = RAND(BEEVEL); yv[beeNum] = RAND(BEEVEL); } /* Copy the draw coordinates to the erase coordinates for real. */ { register int *from = (int *)drawCds, *to = (int *)eraseCds, cnt; for (cnt = 0 ; cnt < COORDS_SIZE ; cnt++) *to++ = *from++; from = (int *)drawBBox; to = (int *)eraseBBox; for (cnt = 0 ; cnt < BBOX_SIZE ; cnt++ ) *to++ = *from++; } return self; } /* Preferences panel methods. */ - setNumberBees: sender { bees = [sender intValue]; [slider setIntValue:bees]; [beeText setIntValue:bees]; /* Bump it one - location zero is the wasp, so go from 1 to <request>+1 */ bees++; [self initValues]; [self lockFocus]; PSsetgray(0.0); NXRectFill(&bounds); [self unlockFocus]; return self; } - setFollow: sender { cursorwasp = [sender intValue]; return self; } - inspector:sender { char buf[MAXPATHLEN]; if (!inspectorPanel) { sprintf(buf,"%s/Swarm.nib",[sender moduleDirectory:"Swarm"]); [NXApp loadNibFile:buf owner:self withNames:NO]; } return inspectorPanel; } - (BOOL)ignoreMouseMovement { return cursorwasp; } - (const char *)windowTitle { return "Swarm"; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.