This is SchoolView.m in view mode; [Download] [Up]
/********************************************************************** SchoolView.m Author: David C. "Slam" Lambert Date: 1 February, 1993 **********************************************************************/ #import <math.h> #import <string.h> #import <memory.h> #import <appkit/Font.h> #import <appkit/View.h> #import <appkit/Button.h> #import <appkit/NXImage.h> #import <appkit/TextField.h> #import <appkit/NXColorWell.h> #import <defaults/defaults.h> #import <dpsclient/wraps.h> #import "Thinker.h" #import "School.h" #import "SchoolView.h" #ifndef FONT_SIZE #define FONT_SIZE (20.0) #endif static NXDefaultsVector SchoolViewDefaults = { { "SchoolCount", "40" }, { "SchoolDistExp", "3.0" }, { "SchoolMomentum", "0.05" }, { "SchoolMinRadius", "60.0" }, { "SchoolMaxVelocity", "18.0" }, { "SchoolMinVelocity", "0.0" }, { "SchoolAccLimit", "5.0" }, { "SchoolAvoidFact", "40" }, { "SchoolMatchFact", "0.7" }, { "SchoolCenterFact", "13" }, { "SchoolTargetFact", "10" }, { "SchoolFollowsMouse", "0" }, { "SchoolChangeGoalFreq", "20"}, { NULL }, }; static float uniform(long seed) { long k; long z; static long s1; static long s2; if (seed != 0) { s1 = seed; s2 = ~seed; } k = s1 / 53668; s1 = 40014 * (s1 - k * 53668) - k * 12211; if (s1 < 0) s1 = s1 + 2147483563; k = s2 / 52774; s2 = 40692 * (s2 - k * 52774) - k * 3791; if (s2 < 0) s2 = s2 + 2147483399; z = s1 - s2; if (z < 1) z = z + 2147483562; return(((float)z * 4.656613e-10)); } @implementation SchoolView + initialize { NXRegisterDefaults([NXApp appName], SchoolViewDefaults); return self; } - getDefaults { const char *theName = [NXApp appName]; schoolCount = atoi(NXGetDefaultValue(theName, "SchoolCount")); maxVel = atof(NXGetDefaultValue(theName, "SchoolMaxVelocity")); minVel = atof(NXGetDefaultValue(theName, "SchoolMinVelocity")); distExp = atof(NXGetDefaultValue(theName, "SchoolDistExp")); momentum = atof(NXGetDefaultValue(theName, "SchoolMomentum")); accLimit = atof(NXGetDefaultValue(theName, "SchoolAccLimit")); avoidFact = atof(NXGetDefaultValue(theName, "SchoolAvoidFact")); matchFact = atof(NXGetDefaultValue(theName, "SchoolMatchFact")); minRadius = atof(NXGetDefaultValue(theName, "SchoolMinRadius")); centerFact = atof(NXGetDefaultValue(theName, "SchoolCenterFact")); targetFact = atof(NXGetDefaultValue(theName, "SchoolTargetFact")); followMouse = (BOOL)atoi(NXGetDefaultValue(theName, "SchoolFollowsMouse")); goalChgFreq = atoi(NXGetDefaultValue(theName, "SchoolChangeGoalFreq")); return self; } - writeDefaults { char string[100]; const char *theName = [NXApp appName]; sprintf(string, "%d", schoolCount); NXWriteDefault(theName, "SchoolCount", string); sprintf(string, "%.4f", maxVel); NXWriteDefault(theName, "SchoolMaxVelocity", string); sprintf(string, "%.4f", avoidFact); NXWriteDefault(theName, "SchoolAvoidFact", string); sprintf(string, "%d", followMouse); NXWriteDefault(theName, "SchoolFollowsMouse", string); return self; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; [self setOpaque:YES]; [self setClipping:NO]; uniform(time(NULL)); theSchools = (BOID *)malloc(0); oldString = malloc(0); schoolString = malloc(0); coords = (float *)malloc(0); oldCoords = (float *)malloc(0); [self getDefaults]; [self allocateGState]; [self setValues]; [self setup]; PSWDavesDefFont("Fish"); PSselectfont("Fish", FONT_SIZE); return self; } - free { free(theSchools); free(oldString); free(schoolString); free(coords); free(oldCoords); [targetImage free]; return [super free]; } - setValues { [countField setIntValue:schoolCount]; [countSlider setIntValue:schoolCount]; [vMaxField setIntValue:maxVel]; [vMaxSlider setIntValue:maxVel]; [avoidField setIntValue:avoidFact]; [avoidSlider setIntValue:avoidFact]; [followSwitch setState:followMouse]; return self; } - awakeFromNib { [self setValues]; targetImage = [NXImage findImageNamed:"Target"]; distComp = FONT_SIZE * 0.707; return self; } - takeValues:sender { schoolCount = [countSlider floatValue]; maxVel = [vMaxSlider floatValue]; avoidFact = [avoidSlider floatValue]; followMouse = [followSwitch state]; targetFact = (followMouse) ? 200.0 : 10.0; [countField setIntValue:schoolCount]; [vMaxField setIntValue:maxVel]; [avoidField setIntValue:avoidFact]; [self setup]; [self writeDefaults]; return self; } - setup { int i; int j; BOID *b; theSchools = realloc(theSchools, sizeof(BOID) * schoolCount); oldString = (char *)realloc(oldString, sizeof(char) * (schoolCount + 1)); schoolString = (char *)realloc(schoolString, sizeof(char) * (schoolCount + 1)); coords = (float *)realloc(coords, sizeof(float) * schoolCount * 2); oldCoords = (float *)realloc(oldCoords, sizeof(float) * schoolCount * 2); bzero(coords, sizeof(float) * schoolCount * 2); bzero(oldCoords, sizeof(float) * schoolCount * 2); memset(oldString, 'A', schoolCount); memset(schoolString, '0', schoolCount); oldString[schoolCount] = schoolString[schoolCount] = '\0'; for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2) { BOID_X(b) = NX_X(&frame) + uniform(0) * NX_WIDTH(&frame); BOID_Y(b) = NX_Y(&frame) + uniform(0) * NX_HEIGHT(&frame); BOID_XVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel; BOID_YVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel; BOID_XACC(b) = BOID_YACC(b) = 0.0; if (i > 0) { coords[j] = (BOID_X(b) - BOID_X(b-1)); coords[j+1] = (BOID_Y(b) - BOID_Y(b-1)); } } goalPoint.x = NX_MIDX(&frame); goalPoint.y = NX_MIDY(&frame); if ([[self window] canStoreColor]) hasColor = YES; [self display]; return self; } - inspector:sender { char buf[1024]; if (!thePanel) { sprintf(buf, "%s/SchoolView.nib", [sender moduleDirectory:"School"]); [NXApp loadNibFile:buf owner:self withNames:NO]; } [self getDefaults]; return thePanel; } - (BOOL)ignoreMouseMovement { return followMouse; } - computeAccelerations { int i; int j; float cAx; float cAy; float aVx; float aVy; float dist; float aMag; float xDiff; float yDiff; float adjDist; float adjDistSum; BOID *b0; BOID *b1; NXPoint tmpPoint; static unsigned counter; adjDist = 0; if (window != nil && followMouse) { [window getMouseLocation:&tmpPoint]; if (NXMouseInRect(&tmpPoint, &frame, NO)) { goalPoint = tmpPoint; goalPoint.x -= 10.0; goalPoint.y -= 16.0; } } else if (!((++counter) % goalChgFreq)) { goalPoint.x = NX_MIDX(&frame) + (uniform(0)-0.5) * 0.45 * NX_WIDTH(&frame); goalPoint.y = NX_MIDY(&frame) + (uniform(0)-0.5) * 0.45 * NX_HEIGHT(&frame); } /* other school avoidance */ for(i = 0, b0 = theSchools; i < schoolCount; i++, b0++) { adjDistSum = 0.0; cAx = cAy = aVx = aVy = 0.0; BOID_XACC(b0) = BOID_YACC(b0) = 0.0; for(j = 0, b1 = theSchools; j < schoolCount; j++, b1++) { if (b1 == b0) continue; xDiff = XDIFF(b0, b1); yDiff = YDIFF(b0, b1); if (xDiff > NX_MIDX(&frame)) xDiff = NX_MAXX(&frame) - xDiff; if (yDiff > NX_MIDY(&frame)) yDiff = NX_MAXY(&frame) - yDiff; dist = NORM(xDiff, yDiff) - distComp; if (dist > minRadius) continue; else if (dist <= 0.0) dist = MIN_DIST; adjDist = dist * dist; adjDistSum += (1.0 / adjDist); xDiff /= adjDist; yDiff /= adjDist; cAx -= xDiff; cAy -= yDiff; BOID_XACC(b0) += xDiff; BOID_YACC(b0) += yDiff; aVx += (BOID_XVEL(b1) / adjDist); aVy += (BOID_YVEL(b1) / adjDist); } xDiff = goalPoint.x - BOID_X(b0); yDiff = goalPoint.y - BOID_Y(b0); BOID_XACC(b0) *= avoidFact; BOID_YACC(b0) *= avoidFact; aMag = NORM(BOID_XACC(b0), BOID_YACC(b0)); /* velocity matching */ if (adjDistSum != 0.0 && aMag < accLimit) { aVx /= adjDistSum; aVy /= adjDistSum; BOID_XACC(b0) += ((aVx - BOID_XVEL(b0)) * matchFact); BOID_YACC(b0) += ((aVy - BOID_YVEL(b0)) * matchFact); aMag = NORM(BOID_XACC(b0), BOID_YACC(b0)); /* flock centering */ if (aMag < accLimit) { BOID_XACC(b0) += cAx * centerFact; BOID_YACC(b0) += cAy * centerFact; aMag = NORM(BOID_XACC(b0), BOID_YACC(b0)); /* target attraction */ if (aMag < accLimit) { BOID_XACC(b0) += xDiff * targetFact / adjDist; BOID_YACC(b0) += yDiff * targetFact / adjDist; } } } BOID_XACC(b0) += (uniform(0)-0.5); BOID_YACC(b0) += (uniform(0)-0.5); aMag = NORM(BOID_XACC(b0), BOID_YACC(b0)); if (aMag > accLimit) { BOID_XACC(b0) *= sqrt(accLimit/aMag); BOID_YACC(b0) *= sqrt(accLimit/aMag); } } return self; } - (BOOL)useBufferedWindow { return NO; } - didLockFocus { PSselectfont("Fish", FONT_SIZE); return self; } - oneStep { int i; int j; int index; float avgIndex; BOID *b; float vMag; float oldX0 = BOID_X(theSchools); float oldY0 = BOID_Y(theSchools); NXColor theColor; static float hue; bcopy(coords, oldCoords, sizeof(float)*schoolCount*2); bcopy(schoolString, oldString, sizeof(char)*schoolCount); PSnewinstance(); PSsetinstance(YES); if (followMouse) [targetImage composite:NX_COPY toPoint:&goalPoint]; PSsetinstance(NO); avgIndex = 0; for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2) { /* apply accelerations */ BOID_XVEL(b) = BOID_XACC(b) + (1.0 + momentum) * BOID_XVEL(b); BOID_YVEL(b) = BOID_YACC(b) + (1.0 + momentum) * BOID_YVEL(b); vMag = 1.0e-6 + NORM(BOID_XVEL(b), BOID_YVEL(b)); if (vMag > maxVel) { BOID_XVEL(b) *= (maxVel/vMag); BOID_YVEL(b) *= (maxVel/vMag); } else if (vMag < minVel) { BOID_XVEL(b) *= (minVel/vMag); BOID_XVEL(b) *= (minVel/vMag); } /* apply movements */ BOID_X(b) += BOID_XVEL(b); BOID_X(b) = (BOID_X(b) > NX_MAXX(&frame)) ? 0.0 : BOID_X(b); BOID_X(b) = (BOID_X(b) < NX_X(&frame)) ? NX_MAXX(&frame) : BOID_X(b); BOID_Y(b) += BOID_YVEL(b); BOID_Y(b) = (BOID_Y(b) > NX_MAXY(&frame)) ? 0.0 : BOID_Y(b); BOID_Y(b) = (BOID_Y(b) < NX_Y(&frame)) ? NX_MAXY(&frame) : BOID_Y(b); if (i > 0) { coords[j] = (BOID_X(b) - BOID_X(b-1)); coords[j+1] = (BOID_Y(b) - BOID_Y(b-1)); } if (fabs(fabs(BOID_XVEL(b))-fabs(BOID_YVEL(b))) < maxVel/2.0) { if (BOID_XVEL(b) < 0.0) index = (BOID_YVEL(b) > 0.0) ? '1' : '2'; else index = (BOID_YVEL(b) > 0.0) ? '4' : '3'; } else if (fabs(BOID_XVEL(b)) > fabs(BOID_YVEL(b))) index = (BOID_XVEL(b) < 0.0) ? 'l' : 'r'; else index = (BOID_YVEL(b) > 0.0) ? 'u' : 'd'; schoolString[i] = index; } NXSetColor(NX_COLORBLACK); PSWDavesXYShow(oldX0, oldY0, oldString, oldCoords, schoolCount*2); if (hasColor) { theColor = NXConvertHSBToColor(hue/360.0, 1.0, 1.0); hue = (hue < 360.0) ? hue+1.0 : 0.0; NXSetColor(theColor); } else NXSetColor(NX_COLORWHITE); PSWDavesXYShow(BOID_X(theSchools), BOID_Y(theSchools), schoolString, coords, schoolCount*2); [self computeAccelerations]; return self; } - (const char *)windowTitle { return "School by David Lambert"; } - sizeTo:(NXCoord)width :(NXCoord)height { [super sizeTo:width :height]; [self setup]; return self; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (!rects || !rectCount) return self; PSselectfont("Fish", FONT_SIZE); NXSetColor(NX_COLORBLACK); NXRectFill(rects); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.