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.