This is WorldSpacePart.m in view mode; [Download] [Up]
#import "WorldSpacePart.h"
#import "SpaceView.h"
#import "Thinker.h"
#import <appkit/NXImage.h>
#import <math.h>
#import <libc.h>
#import <dpsclient/wraps.h>
#import <objc/List.h>
#import <defaults.h>
#import <appkit/Panel.h>
@implementation WorldSpaceView
#define ASSUMED_INTERVAL 35
#define WIDTH 100
#define HEIGHT 100
#define COUNT 36
#define ACCEL (-2)
#define REBOUND (-1.3)
// This screen height value is not critical, though it will be used
// to determine how high the ball can go
#define SCREEN_HEIGHT 832
#define LAUNCH_SPEED (sqrt(fabs(2*ACCEL*(SCREEN_HEIGHT - HEIGHT))))
#define REAL_LAUNCH_SPEEd (sqrt(fabs(2*accel*(viewHeight - HEIGHT))))
#define MIN_X_SPEED (3)
#define MAX_X_SPEED (6)
#define ABS_MAX_X_SPEED (6)
#define MAX_Y_SPEED (LAUNCH_SPEED + 20)
#define BUFFER_WIDTH (WIDTH + ABS_MAX_X_SPEED + 1)
#define BUFFER_HEIGHT (HEIGHT + MAX_Y_SPEED + 1)
- oneStep
{
NXRect black = {0,0,0,0};
BRECT new;
float scaledTime, calcYpos;
then = now;
now = currentTimeInMs();
/* calculate new ball x position */
xpos += [self timeCorrectedXSpeed];
if (xpos < 0) /* ball hit left edge */
{ xspeed = -xspeed;
if (viewWidth > WIDTH)
{ spinDir = -spinDir;
}
xpos = 0;
}
else if (xpos > (viewWidth - WIDTH)) /* ball hit right edge */
{ if (viewWidth > WIDTH)
{
xspeed = -[self getRandomXspeed];
[self checkXspeed:&xspeed];
xpos = (viewWidth - WIDTH);
}
else
{ xspeed = xpos = 0;
}
}
scaledTime = ((float)(now - then) / ASSUMED_INTERVAL);
if (scaledTime > 1) scaledTime = 1;
// calculate new ball vertical position
calcYpos = ypos + (scaledTime*yspeed) + ((accel * scaledTime * scaledTime)/2);
// change vertical ball speed to simulate gravity
yspeed += (accel * scaledTime);
if (calcYpos < (ypos - MAX_Y_SPEED)) calcYpos = ypos - MAX_Y_SPEED;
else if (calcYpos > (ypos + MAX_Y_SPEED)) calcYpos = ypos + MAX_Y_SPEED;
ypos = calcYpos;
if (yspeed < -MAX_Y_SPEED) yspeed = -MAX_Y_SPEED;
if (ypos <= 0) /* ball hit bottom of window */
{
ypos = 0;
if (viewHeight > HEIGHT) {
if (reboundMode == DECREASING) {
yspeed = lastLaunchSpeed = lastLaunchSpeed + rebound;
}
else {
yspeed = lastLaunchSpeed = lastLaunchSpeed - (2*rebound);
}
if (yspeed <= 0) {
yspeed = 0;
reboundMode = INCREASING; /* bounce height increases every b */
}
else if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED - (3*accel);
}
else yspeed = 0;
}
else if (ypos >= (viewHeight - HEIGHT)) /* ball hit top of window */
{ if (viewHeight > HEIGHT)
{
yspeed = accel;
ypos = (viewHeight - HEIGHT);
reboundMode = DECREASING; /* bounce height decreases every bounce */
spinDir = -spinDir;
}
else
{ yspeed = ypos = 0;
}
}
/* rotate the ball by selecting a new ball image to blit */
/* we have an image of the ball in 10 different stages of rotation */
[self incrementBallNumber];
new.l = floor(xpos);
new.b = floor(ypos);
new.r = new.l + WIDTH;
new.t = new.b + HEIGHT;
redrawTo.x = MIN(new.l, old.l);
redrawTo.y = MIN(new.b, old.b);
redraw.origin.x = 0;
redraw.origin.y = 0;
redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
black.size = redraw.size;
[buffer lockFocus];
PSsetgray(0);
NXRectFill(&black);
ballTo.x = new.l - redrawTo.x;
ballTo.y = new.b - redrawTo.y;
[[imageList objectAt:ballNum] composite:NX_SOVER toPoint:&ballTo];
[buffer unlockFocus];
[buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];
avoid.origin = redrawTo;
[mySpaceView setVoidRect:&avoid];
[mySpaceView oneStep];
old = new;
return self;
}
/* calculate a vertical launch speed which will get the ball almost to */
/* the top of the window before gravity pulls it back down. Little bit */
/* of physics lesson here... */
- newSpeed
{
lastLaunchSpeed = yspeed = REAL_LAUNCH_SPEEd;
if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED;
xpos = 0;
ypos = 0;
if (viewWidth <= WIDTH) xspeed = 0;
else xspeed = [self getRandomXspeed];
[self checkXspeed:&xspeed];
rebound = REBOUND;
return self;
}
static BOOL noAnimFile = 0;
- initFrame:(NXRect *)frameRect
{
char appDirectory[1024], *lastSlash;
const char *animSpeed, *animFile, *animName;
char animFrame[1024];
int i, f;
id local_image;
NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
[super initFrame:frameRect];
[self allocateGState]; // For faster lock/unlockFocus
[self setClipping:NO]; // even faster...
accel = ACCEL;
spinDir = 1;
mySpaceView = [[SpaceView alloc] initFrame:frameRect];
avoid.size.width = avoid.size.height = 100;
//in this case, I only need one buffer for several Views
if (!(buffer = [NXImage findImageNamed:"boinkBuffer"]))
{
buffer = [[NXImage alloc] initSize:&black.size];
[buffer setName:"boinkBuffer"];
}
if ([buffer lockFocus])
{
PSsetgray(0);
NXRectFill(&black);
[buffer unlockFocus];
}
// Get some of the defaults
animSpeed = NXGetDefaultValue([NXApp appName], "animSpeed");
if (animSpeed == NULL) framesPerSecond = 18;
else framesPerSecond = atoi(animSpeed);
// Find the animation file
animFile = NXGetDefaultValue([NXApp appName], "animFile");
if (animFile == NULL) {
strcpy(appDirectory, NXArgv[0]);
lastSlash = rindex(appDirectory, '/');
strcpy((lastSlash?lastSlash+1:appDirectory), "Globe.anim");
animFile = appDirectory;
}
animName = basename(animFile, ".anim");
imageList = [[List alloc] init];
// Construct the imageList
/* construct the image list */
for (i=0; ; i++) {
sprintf(animFrame, "%s/%s.%d.tiff", animFile, animName, i+1);
if ((f=open(animFrame, O_RDONLY)) < 0) break;
close(f);
// printf("%s\n", animFrame);
local_image = [[NXImage alloc] initFromFile:animFrame];
if (local_image == NULL) break; // never null, even if no file
[imageList addObject:local_image];
}
numberOfFrames = i;
ballNum = 0;
if (numberOfFrames == 0) {
if (!noAnimFile)
NXRunAlertPanel([NXApp appName], "Could not open %s",
NULL, NULL, NULL, animFile);
noAnimFile = 1;
return NULL;
}
// May something else that I forgot to do?
[self newViewSize];
return self;
}
- setAccel:(float)val
{
accel = val;
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
[mySpaceView sizeTo:width :height];
[self newViewSize];
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
if (!rects || !rectCount) return self;
//PSsetgray(0);
//NXRectFill(rects);
NXRectClip(rects);
return self;
}
- newViewSize
{
//this is called every time View size changes
// NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
then = now = currentTimeInMs();
if (oldSize.width == bounds.size.width &&
oldSize.height == bounds.size.height)
return self;
else
{
oldSize.width = bounds.size.width;
oldSize.height = bounds.size.height;
}
old.l = old.r = old.b = old.t = ballTo.x = ballTo.y = 0;
viewWidth = bounds.size.width;
viewHeight = bounds.size.height;
if (viewHeight > SCREEN_HEIGHT) viewHeight = SCREEN_HEIGHT;
[self newSpeed];
return self;
}
- incrementBallNumber
{
if (now > nextRotationTime)
{
ballNum += spinDir;
if (ballNum >= COUNT) ballNum = 0;
else if (ballNum < 0) ballNum = COUNT-1;
nextRotationTime = now + 1000/framesPerSecond;
}
return self;
}
- (float) getRandomXspeed
{
return randBetween(MIN_X_SPEED, MAX_X_SPEED);
}
- (float) timeCorrectedXSpeed
{
float ret = xspeed * ((float)(now - then) / ASSUMED_INTERVAL);
[self checkXspeed:&ret];
return ret;
}
- checkXspeed:(float *)speed
{
if (*speed > MAX_X_SPEED) *speed = MAX_X_SPEED;
else if (*speed < -MAX_X_SPEED) *speed = -MAX_X_SPEED;
return self;
}
- (const char *)windowTitle
{
return "The Earth As A Basketball";
}
- didLockFocus
{
[mySpaceView didLockFocus];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.