This is NewSpaceView.m in view mode; [Download] [Up]
// NewSpaceView.m // // This class implements the flying starfield screen saver view. // This is mostly the code from SpaceView in BackSpace // // Hacked by R.S. Brown for added functionality #import "NewSpaceView.h" #import "Thinker.h" #import "psfuncts.h" #import "spaceFuncts.h" #import <dpsclient/wraps.h> #import <appkit/NXImage.h> #import <objc/zone.h> #import <mach/mach.h> #import <c.h> #import <libc.h> #import <math.h> #import "StarShipView.h" #define PI (3.141592653589) #define SCREENMOVE 1000 //how often screen moves #define LIGHTMOVE 10 //how often lights move @implementation NewSpaceView + initialize { if ( self == [NewSpaceView class] ) { // load PostScript procedures SpaceView needs // must be careful these are loaded into the correct context loadPSProcedures(); } return self; } //takes theta and distance and stuffs it into x &y for *p - convertToXY:(STAR *)p { p->draw->x = floor(bounds.size.width / 2 + (p->distance * cos(p-> theta))); p->draw->y = floor(bounds.size.height / 2 + (p->distance * sin(p-> theta))); return self; } - oneStep { int i, stopTest, count; int starsInBArray = 0; int starsInWArray = 0; STAR *p; NXPoint *t; float adjustedStarSpeed; STAR test; NXPoint testP; if (nstars < NSTARS && !hidden) [self addStar]; stopTest = 0; for (i=0; i<nstars; i++) { p = &stars[i]; p->distance += (p->delta); if(!stopping) adjustedStarSpeed = starSpeed; else adjustedStarSpeed = 1.0; p->delta = p->delta * p->ddelta * stopDelta * adjustedStarSpeed; if(p->delta < .01){ // this must be less than starting p>delta stopTest++; p->delta = 0.0; } [self convertToXY:p]; // only draw the star if it moved > 1 pixel or stopped if (p->draw->x != p->erase->x || p->draw->y != p->erase->y || stopped){ BOOL mustErase = NO; // add star to the erasure array b[starsInBArray] = *p->erase; bc[starsInBArray] = p->c; if (p->distance > p->changepoint[p->changemode]){ (p->c)++; // increment character for next star size (p->changemode)++; } // clipping is off, so we must not draw outside view. // replace stars that go too far... if (p->draw->x < smallScreenRect.origin.x+3 || p->draw->y < smallScreenRect.origin.y+3 || p->draw->x+7 > smallScreenRect.size.width + smallScreenRect.origin.x - 3 || p->draw->y+7 > smallScreenRect.size.height + smallScreenRect.origin.y - 3){ [self replaceStarAt:i]; //printf("replaced star at %d\n",i); mustErase = YES; } w[starsInWArray] = *p->draw; wc[starsInWArray] = p->c; if(mustErase){ testP.x = b[starsInBArray].x; testP.y = b[starsInBArray].y; test.erase = &testP; if ([self allowBStars:&test])starsInBArray++; } else if ([self allowBStars:p])starsInBArray++; if ([self allowWStars:p]) starsInWArray++; t = p->draw; p->draw = p->erase; p->erase = t; } } bc[starsInBArray] = wc[starsInWArray] = 0; //null terminate string if (starsInBArray){ for (i=0; i<(starsInBArray-1); i++){ bOffsets[i].x = b[i+1].x - b[i].x; bOffsets[i].y = b[i+1].y - b[i].y; } bOffsets[i].x = bOffsets[i].y = 0; count = 0; while (count < starsInBArray){ char tc; int j; // You get the best performance if you put out all the stars // at once. This causes noticable flicker, so I put out // 100 of the stars per iteration. This gives reasonable speed // and flicker is hardly noticable. Besides, stars // _should_ flicker a little... int t = (starsInBArray - count); i = (t < STARSPERIT)?t:STARSPERIT; j = i + count; PSsetgray(NX_BLACK); tc = bc[j]; bc[j] = 0; PSWXYShow(b[count].x, b[count].y, &bc[count], (float *)(&bOffsets[count].x), i*2); bc[j] = tc; count += STARSPERIT; } } if (starsInWArray){ for (i=0; i<(starsInWArray-1); i++){ wOffsets[i].x = w[i+1].x - w[i].x; wOffsets[i].y = w[i+1].y - w[i].y; } wOffsets[i].x = wOffsets[i].y = 0; count = 0; while (count < starsInWArray){ char tc; int j; // You get the best performance if you put out all the stars // at once. This causes noticable flicker, so I put out // 100 of the stars per iteration. This gives reasonable speed // and flicker is hardly noticable. Besides, stars // _should_ flicker a little... int t = (starsInWArray - count); i = (t < STARSPERIT)?t:STARSPERIT; j = i + count; PSsetgray(NX_WHITE); tc = wc[j]; wc[j] = 0; PSWXYShow(w[count].x, w[count].y, &wc[count], (float *)(&wOffsets[count].x), i*2); wc[j] = tc; count += STARSPERIT; } } if((stopTest >= (nstars - 1)) && stopping && !stopped){ stopped = YES; [bodyController starsStopped]; } screenChangeTime++; if(screenChangeTime > SCREENMOVE){ [self drawScreen]; [starShip viewScreenResized:&smallScreenRect]; } lightChangeTime++; if(lightChangeTime > LIGHTMOVE){ lightChangeTime = 0; [self drawScreenLights]; } return self; } // make the actual image 8 pixels bigger in each direction // then make the avoidance rect -8 pixels to width and height of image //for example // if the image you want to avoid is 5 pixels square // make image 21 X 21 with a black background and //center the actual image. make the avoidance rect //0,0,13,13 //The reason for this is that the stars are a max of //7 X 7 pixels and drawn from lower left corner - (BOOL) allowWStars:(const STAR *)p { // avoidance rectangles are set by message from body objects int ii; NXRect avoid; BOOL allow; allow = YES; // return yes if point is outside of all avoidance rectangles for(ii=0;ii < [avoidStorage count];ii++){ avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid; // just return if voidRect not set if ((!avoid.size.width) || p->draw->x < avoid.origin.x || p->draw->y < avoid.origin.y || p->draw->x > avoid.origin.x+avoid.size.width || p->draw->y > avoid.origin.y+avoid.size.height){ } else allow = NO; } return allow; } //check the ones that need erasing //returns yes if outside avoidance rect - (BOOL) allowBStars:(const STAR *)p { int ii; NXRect avoid; BOOL allow; allow = YES; // return yes if point is outside of all avoidance rectangles for(ii=0;ii < [avoidStorage count];ii++){ avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid; // just return if voidRect not set if ((!avoid.size.width) || p->erase->x < avoid.origin.x || p->erase->y < avoid.origin.y || p->erase->x > avoid.origin.x+avoid.size.width || p->erase->y > avoid.origin.y+avoid.size.height){ } else allow = NO; } return allow; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; // these were in original code - I don't know if they do anything //[self allocateGState]; // For faster lock/unlockFocus //[self setClipping:NO]; // even faster... loadPSProcedures(); loadSpaceProcedures(); PSWDefineFont("StarFont"); PSselectfont("StarFont", 1.0); // not really needed // for completeness - view gets resized right away by sizeTo method [self setSmallScreenRect:frameRect]; [self setRadius]; screenChangeTime = 0; screenResizeLimit = 0; screenResizeDir = 1; lightChangeTime = 0; lightLimit = 16; stopDelta = 1.0; stopped = NO; stopping = NO; hidden = NO; lightInc = floor(smallScreenRect.size.width/60); lights.l_x_pos = smallScreenRect.size.width/2 - 4 + smallScreenRect.origin.x; lights.r_x_pos = smallScreenRect.size.width/2 + 4 + smallScreenRect.origin.x; lights.y_pos = floor(smallScreenRect.origin.y/2); [self setStarsStopped]; //set stars stopped at the beginning return self; } - drawSelf:(const NXRect *)rects :(int)rectCount { // this drawself doesn't really draw the view at all. // in fact it just promotes the window to screen depth... NXRect t = {0,0,1,1}; PSsetrgbcolor(.333,.222,.111); NXRectFill(&t); //yucky trick for window depth promotion! PSsetgray(NX_BLACK); NXRectFill(&t); PSselectfont("StarFont", 1.0); return self; } - setSmallScreenRect:(const NXRect *)newRect; { //sets variable to bounds of the small window in the viewscreen rect smallScreenRect.size.width = newRect->size.width; smallScreenRect.size.height = newRect->size.height; smallScreenRect.origin.x = newRect->origin.x; smallScreenRect.origin.y = newRect->origin.y; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { NXSize delta; [super sizeTo:width :height]; delta.width = floor(width * .15); delta.height = floor(height * .15); smallScreenRect.size.width = width - delta.width; smallScreenRect.size.height = height - delta.height; smallScreenRect.origin.x = (delta.width / 2); smallScreenRect.origin.y = (delta.height / 2); screenChangeTime = SCREENMOVE; //put them on screen lightChangeTime = LIGHTMOVE; lightLimit = 16; lightInc = floor(smallScreenRect.size.width/60); lights.l_x_pos = smallScreenRect.size.width/2 - 4 + smallScreenRect.origin.x; lights.r_x_pos = smallScreenRect.size.width/2 + 4 + smallScreenRect.origin.x; lights.y_pos = floor(smallScreenRect.origin.y/2); if (oldSize.width != bounds.size.width || oldSize.height != bounds.size.height) { oldSize.width = bounds.size.width; oldSize.height = bounds.size.height; nstars = 0; [self setRadius]; [self display]; [starShip viewScreenResized:&smallScreenRect]; } return self; } // only call addStar if there is room in the stars array! - addStar { [self replaceStarAt:nstars++]; return self; } - replaceStarAt:(int)index { float dist, t; int tries = 0; STAR *p = &stars[index]; BOOL inBounds; p->draw = &p->r1; p->erase = &p->r2; do { p->theta = randBetween(0,(2*PI)); if (tries++ < 3) p->distance = randBetween(1, radius); else p->distance = randBetween(1, p->distance); inBounds = YES; [self convertToXY:p]; if (p->draw->x < smallScreenRect.origin.x+3 || p->draw->y < smallScreenRect.origin.y+3 || p->draw->x + 7 > smallScreenRect.size.width + smallScreenRect.origin.x - 3 || p->draw->y + 7 > smallScreenRect.size.height + smallScreenRect.origin.y - 3) { inBounds = NO; } } while (!inBounds); //take out if(stopping){ p->delta = 0.0; p->ddelta = 1.0; } else{ p->delta = .2; //p->ddelta = randBetween(1.0, 1.1); p->ddelta = randBetween(1.0, 1.05); } t = randBetween(0, (0.42*radius)); dist = MAX(20,t); p->changepoint[0] = p->distance + 5; // to b p->changepoint[1] = p->changepoint[0] - 5 + dist + dist; // to c p->changepoint[2] = p->changepoint[1] + dist; // to d p->changepoint[3] = p->changepoint[2] + dist; // to e p->changepoint[4] = p->changepoint[3] + dist; // to f p->changepoint[5] = 100000; // never change to g p->changemode = 0; if ((++toggle) & 1) p->c = 'a'; else p->c = 'g'; p->r2 = p->r1; return self; } - drawScreen { screenChangeTime = 0; PSscreenBorder(smallScreenRect.origin.x, smallScreenRect.origin.y, smallScreenRect.size.width + smallScreenRect.origin.x, smallScreenRect.size.height + smallScreenRect.origin.y, smallScreenRect.origin.y-2.5,5.0,0.0); if(screenResizeDir == 1){ smallScreenRect.size.width++; smallScreenRect.size.height++; smallScreenRect.origin.x--; smallScreenRect.origin.y--; } else{ smallScreenRect.size.width--; smallScreenRect.size.height--; smallScreenRect.origin.x++; smallScreenRect.origin.y++; } PSscreenBorder(smallScreenRect.origin.x, smallScreenRect.origin.y, smallScreenRect.size.width + smallScreenRect.origin.x, smallScreenRect.size.height + smallScreenRect.origin.y, smallScreenRect.origin.y-2.5,5.0,1.0); screenResizeLimit++; if(screenResizeLimit > 7){ if(screenResizeDir) screenResizeDir = 0; else screenResizeDir = 1; //reverse the values screenResizeLimit = 0; } return self; } - drawScreenLights { float red,green,blue; PSscreenLights(lights.l_x_pos,lights.r_x_pos, lights.y_pos,0.0,0.0,0.0); lights.l_x_pos -= lightInc; lights.r_x_pos += lightInc; lightLimit++; if(lightLimit > 15){ lightLimit = 0; lights.l_x_pos = smallScreenRect.size.width/2 - 4 + smallScreenRect.origin.x; lights.r_x_pos = smallScreenRect.size.width/2 + 4 + smallScreenRect.origin.x; lights.y_pos = smallScreenRect.origin.y/2; } red = randBetween(0.1,1.0); //make sure never black green = randBetween(0.0,1.0); blue = randBetween(0.0,1.0); PSscreenLights(lights.l_x_pos,lights.r_x_pos, lights.y_pos,red,green,blue); return self; } //this causes stars to slow down and stop - continue being displayed - stopStars { // needs to be < 1 so that the stars stop eventually // this will control rate of stoppage stopDelta = 0.90; stopping = YES; return self; } // causes stars to disappear altogether - //only works if stars are stopped //send a setStarsStopped message first - hideStars { [self setStarsStopped]; PSsetgray(0); NXRectFill(&smallScreenRect); nstars = 0; hidden = YES; return self; } - startStars { STAR *p; int ii; hidden = NO; for (ii=0; ii < NSTARS; ii++){ p = &stars[ii]; p->delta = 0.2; } stopDelta = 1.0; stopping = NO; stopped = NO; return self; } - (BOOL)isStopped { return stopped; } - (BOOL)isStopping { return stopping; } - setRadius { float x = smallScreenRect.size.width; float y = smallScreenRect.size.height; radius = (sqrt(x*x + y*y))/2; return self; } - (const char *)windowTitle { return "The Final Frontier"; } - setAvoidRect:(Storage *)storage { avoidStorage = storage; return self; } - didLockFocus { PSselectfont("StarFont", 1.0); return self; } - (BOOL)useBufferedWindow { return NO; } - inspector:sender { return [sender spaceInspector]; } - (BOOL)ignoreMouseMovement { return NO; } - inspectorWillBeRemoved { return self; // just a prototype } - inspectorInstalled { return self; // just a prototype } - setStarShipOutlet:(id)outlet { starShip = outlet; return self; } - setBodyControllerOutlet:(id)outlet { bodyController = outlet; return self; } - setStarSpeed:sender { starSpeed = [sender floatValue]; return self; } //this sets stars instantly stopped - setStarsStopped { STAR *p; int ii; nstars = 0; for(ii=0;ii < NSTARS;ii++){ [self addStar]; p = &stars[ii]; p->delta = 0.0; } stopping = YES; stopped = YES; return self; } @end @implementation View(nonretainedFillMethod) // I add this method as a category of View to be sure that all // my views implement it. I really want to use nonretained windows // but they are drawn via drawSelf at all kinds of goofy times. It // seems like the kit kind of throws up its hands when it doesn't have // a buffer to draw from, so you get a lot more drawSelfs than you need, // and sometimes you don't get them when you really want them. I know // when I need the background filled in black, so I factor that out of // my drawSelf and then drawself only draws things that are already on // screen so you don't see it happen. I will only call this method on // a nonretained (and full screen) window. - fillBoundsWithBlack { if ([self canDraw]) { [self lockFocus]; PSsetgray(NX_BLACK); NXRectFill(&bounds); [self unlockFocus]; } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.