This is PlanetView.m in view mode; [Download] [Up]
#import <appkit/NXImage.h>
#import <appkit/Panel.h>
#import <appkit/appkit.h>
#import <dpsclient/wraps.h>
#import <libc.h>
#import <math.h>
#import <appkit/Slider.h>
#import "PlanetView.h"
#import "Thinker.h"
#import "PlanetWraps.h"
@implementation PlanetView
- createUniverse
{
int i;
float dx, xsum=0, dy, ysum=0;
numplanets = initialNumplanets;
for (i = 0; i < initialNumplanets; i++)
[self newPlanet:&myplanets[i] :bounds.size.width: bounds.size.height];
for (i = 0; i < initialNumplanets-1; i++)
{
dx = randBetween(-3, 3);
dy = randBetween(-3, 3);
myplanets[i].xv = dx;
myplanets[i].yv = dy;
xsum+=dx;
ysum+=dy;
}
// zero out the energy in the universe
myplanets[i].xv = -xsum;
myplanets[i].yv = -ysum;
iterationCount = 0;
return self;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
PLinitPlanetDefs();
initialNumplanets = PLANETS;
gfactor = 1;
myplanets = (aplanet *) malloc(MAXPLANETS * sizeof(aplanet));
[self createUniverse];
return self;
}
- setGFactor:sender
{
gfactor = [sender floatValue];
return self;
}
- oneStep
{
int i, j;
double xfactor, yfactor;
if (++iterationCount > MAXITERATIONS)
{
[self createUniverse];
[self display];
iterationCount = 0;
}
for (i=0; i < numplanets; i++)
{
// erase each
PSsetgray(0);
PLdrawPlanet(myplanets[i].x, myplanets[i].y,
SPACEDIST * sqrt(myplanets[i].gravity));
// move each
myplanets[i].x += myplanets[i].xv;
myplanets[i].y += myplanets[i].yv;
[self constrainPosition:i];
// draw each
PSsetgray(1);
PLdrawPlanet(myplanets[i].x, myplanets[i].y,
SPACEDIST * sqrt(myplanets[i].gravity));
[[self window] flushWindow];
// perturb each
for (j = i + 1; j < numplanets; j++)
{
float dist = sqrt(SQR(deltaX(i, j)) + SQR(deltaY(i, j))) / SPACEDIST;
myplanets[i].xv += (xfactor = deltaX(i, j) /
(SQR(dist) * dist)) * myplanets[j].gravity * gfactor;
myplanets[i].yv += (yfactor = deltaY(i, j) /
(SQR(dist) * dist)) * myplanets[j].gravity * gfactor;
myplanets[j].xv -= xfactor * myplanets[i].gravity * gfactor;
myplanets[j].yv -= yfactor * myplanets[i].gravity * gfactor;
// check for collisions
if (dist * SPACEDIST < sqrt(myplanets[i].gravity)
+ sqrt(myplanets[j].gravity))
{
[self merge:i :j];
continue;
}
}
}
return self;
}
- constrainPosition:(int)i
{
if (myplanets[i].x < bounds.origin.x - 20)
{
myplanets[i].x = bounds.origin.x + bounds.size.width + 10;
myplanets[i].xv *= 0.95;
}
else if (myplanets[i].x > bounds.origin.x + bounds.size.width + 20)
{
myplanets[i].x = bounds.origin.x - 10;
myplanets[i].xv *= 0.95;
}
if (myplanets[i].y < bounds.origin.y - 20)
{
myplanets[i].y = bounds.origin.y + bounds.size.height + 10;
myplanets[i].yv *= 0.95;
}
else if (myplanets[i].y > bounds.origin.y + bounds.size.height + 20)
{
myplanets[i].y = bounds.origin.y - 10;
myplanets[i].yv *= 0.95;
}
return self;
}
- constrainGravity:(int)i
{
if (myplanets[i].gravity < 1) myplanets[i].gravity = 1;
else if (myplanets[i].gravity > MAXGRAVITY) myplanets[i].gravity = MAXGRAVITY;
return self;
}
- setNumberPlanets:sender
{
initialNumplanets = numplanets = [sender intValue];
[numplanetTextField setIntValue:numplanets];
[self createUniverse];
[self display];
return self;
}
- merge:(int)planetA :(int)planetB
{
float tgrav = myplanets[planetA].gravity + myplanets[planetB].gravity;
PSsetgray(0);
PLdrawPlanet(myplanets[planetA].x, myplanets[planetA].y,
SPACEDIST * sqrt(myplanets[planetA].gravity));
myplanets[planetA].xv = (myplanets[planetA].xv * myplanets[planetA].gravity +
myplanets[planetB].xv * myplanets[planetB].gravity) / tgrav;
myplanets[planetA].yv = (myplanets[planetA].yv * myplanets[planetA].gravity +
myplanets[planetB].yv * myplanets[planetB].gravity) / tgrav;
myplanets[planetA].gravity = tgrav;
[self constrainGravity:planetA];
PSsetgray(0);
PLdrawPlanet(myplanets[planetB].x, myplanets[planetB].y,
SPACEDIST * sqrt(myplanets[planetB].gravity));
PSsetgray(1);
PLdrawPlanet(myplanets[planetA].x, myplanets[planetA].y,
SPACEDIST * sqrt(myplanets[planetA].gravity));
[[self window] flushWindow];
myplanets[planetB] = myplanets[--numplanets];
if ((myplanets[planetA].gravity > ((initialNumplanets * DEFAULTGRAV) * .65))
|| ((randBetween(0,1) <= 0.15) &&
(myplanets[planetA].gravity > ((initialNumplanets * DEFAULTGRAV) * .33))))
[self blowPlanet:planetA];
if (numplanets == 1) iterationCount = MAXITERATIONS - 200;
return self;
}
- newPlanet:(aplanet *) newplanet :(float)width :(float)height
{
newplanet->x = randBetween(0,width);
newplanet->y = randBetween(0, height);
newplanet->gravity = DEFAULTGRAV;
newplanet->xv = 0;
newplanet->yv = 0;
return self;
}
- blowPlanet:(int)planetNum
{
int i, newplanets, newplanetnum;
double newxd, newyd, newradius;
float oldgrav = myplanets[planetNum].gravity;
float xc, yc;
newplanets = MIN(oldgrav, MAXPLANETS-numplanets) - 1;
newradius = newplanets * 1.5;
newplanetnum = MIN((numplanets + newplanets), MAXPLANETS);
for (i = numplanets; i < newplanetnum; i++)
{
myplanets[i] = myplanets[planetNum];
newxd = (xc = sin(PI * 2 * (i - numplanets) / (newplanets))) * newradius;
newyd = (yc = cos(PI * 2 * (i - numplanets) / (newplanets))) * newradius;
myplanets[i].x += newxd;
myplanets[i].y += newyd;
myplanets[i].gravity = oldgrav / (newplanets+1);
myplanets[i].xv += xc * SPACEDIST * randBetween(2.85,3.5);
myplanets[i].yv += yc * SPACEDIST * randBetween(2.85,3.5);
[self constrainGravity:i];
}
PSsetgray(0);
PLdrawPlanet(myplanets[planetNum].x, myplanets[planetNum].y,
SPACEDIST * sqrt(myplanets[planetNum].gravity));
myplanets[planetNum].gravity = oldgrav / (newplanets+1);
[self constrainGravity:planetNum];
numplanets = newplanetnum;
return self;
}
- (const char *)windowTitle
{ return "Orbital Simulation";
}
- inspector:sender
{
char buf[MAXPATHLEN];
if (!inspectorPanel)
{
sprintf(buf,"%s/planet.nib",[sender moduleDirectory:"Planet"]);
[NXApp loadNibFile:buf owner:self withNames:NO];
[numplanetSlider setIntValue:numplanets];
[numplanetTextField setIntValue:numplanets];
}
return inspectorPanel;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
[self createUniverse];
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
if (!rects || !rectCount) return self;
PSsetgray(NX_BLACK);
NXRectFill(rects);
return self;
}
- (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.