This is SpaceView.m in view mode; [Download] [Up]
// SpaceView.m
//
// This class implements the flying starfield screen saver view.
//
// You may freely copy, distribute, and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.
#import "SpaceView.h"
#import "psfuncts.h"
#import <appkit/Application.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>
#define PI (3.141592653589)
// used in producing timed events
static void AnimateSpace();
static void AnimateSpaceIT();
// This should return a float between 0 and 1
float frandom()
{
float val = (random() & 0x7fffffff);
val /= 0x7fffffff;
return val;
}
float randBetween(float a, float b)
{
float val, scale, t;
if (a > b)
{ t = a; a = b; b = t;
}
scale = (b-a);
val = scale * frandom();
return (a + val);
}
@implementation SpaceView
//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, count, starsInArray = 0;
STAR *p;
NXPoint *t;
if (nstars < NSTARS) [self addStar];
for (i=0; i<nstars; i++)
{
p = &stars[i];
p->distance += p->delta;
p->delta *= p->ddelta;
[self convertToXY:p];
// only draw the star if it moved > 1 pixel
if (p->draw->x != p->erase->x ||
p->draw->y != p->erase->y)
{
BOOL mustErase = NO;
// add star to the erasure array
b[starsInArray] = *p->erase;
bc[starsInArray] = 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 < 0 ||
p->draw->y < 0 ||
p->draw->x + 7 > bounds.size.width ||
p->draw->y + 7 > bounds.size.height)
{
[self replaceStarAt:i];
mustErase = YES;
}
w[starsInArray] = *p->draw;
wc[starsInArray] = p->c;
if (mustErase || [self allowStars:p]) starsInArray++;
t = p->draw; p->draw = p->erase; p->erase = t;
}
}
bc[starsInArray] = wc[starsInArray] = 0; //null terminate string
if (starsInArray)
{
for (i=0; i<(starsInArray-1); i++)
{
bOffsets[i].x = b[i+1].x - b[i].x;
bOffsets[i].y = b[i+1].y - b[i].y;
wOffsets[i].x = w[i+1].x - w[i].x;
wOffsets[i].y = w[i+1].y - w[i].y;
}
bOffsets[i].x = bOffsets[i].y = wOffsets[i].x = wOffsets[i].y = 0;
count = 0;
while (count < starsInArray)
{ 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 = (starsInArray - 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;
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;
}
}
return self;
}
// returns yes if the star is outside the avoidance rectangle
// this is really fast and loose but it works acceptibly well
// ps I could just use NXIntersectsRect() but I want to avoid
// trap overhead. Call me paranoid...
- (BOOL) allowStars:(const STAR *)p
{
// just return if voidRect not set
if ((!voidRect.size.width) ||
p->draw->x < voidRect.origin.x ||
p->draw->y < voidRect.origin.y ||
p->draw->x+7 > voidRect.origin.x+voidRect.size.width ||
p->draw->y+7 > voidRect.origin.y+voidRect.size.height ||
p->erase->x < voidRect.origin. x ||
p->erase->y < voidRect.origin. y ||
p->erase->x+7 > voidRect.origin.x+voidRect.size.width ||
p->erase->y+7 > voidRect.origin.y+voidRect.size.height) return YES;
return NO;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
[self allocateGState]; // For faster lock/unlockFocus
[self setClipping:NO]; // even faster...
[self setRadius];
loadPSProcedures();
PSWDefineFont("StarFont");
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(1,0,0);
NXRectFill(&t); //yucky trick for window depth promotion!
PSsetgray(NX_BLACK); NXRectFill(&t);
PSselectfont("StarFont", 1.0);
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
if (oldSize.width != bounds.size.width ||
oldSize.height != bounds.size.height)
{
oldSize.width = bounds.size.width;
oldSize.height = bounds.size.height;
[self setRadius];
nstars = 0;
[self display];
}
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 < 0 || p->draw->y < 0 ||
p->draw->x + 7 > bounds.size.width ||
p->draw->y + 7 > bounds.size.height)
{
inBounds = NO;
}
} while (!inBounds);
p->delta = (0.1);
p->ddelta = randBetween(1.0, 1.1);
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;
}
- setRadius
{
float x = bounds.size.width;
float y = bounds.size.height;
radius = (sqrt(x*x + y*y))/2;
return self;
}
- (const char *)windowTitle
{
return "The Final Frontier";
}
- setVoidRect:(const NXRect *)r
{
voidRect = *r;
return self;
}
- didLockFocus
{
PSselectfont("StarFont", 1.0);
return self;
}
- (BOOL)useBufferedWindow
{ return NO;
}
- inspector:sender
{
[self display];
return self;
}
- (BOOL)ignoreMouseMovement
{ return NO;
}
- inspectorWillBeRemoved
{ return self; // just a prototype
}
- inspectorInstalled
{ return self; // just a prototype
}
//*****************************************************************************
//
// circular clipping path
//
//*****************************************************************************
- clipToFrame:(const NXRect *)frameRect
{
float x, y, cradius;
// Center the circle and pick an appropriate radius
x = frameRect->origin.x + frameRect->size.width/2.0;
y = frameRect->origin.y + frameRect->size.height/2.0;
cradius = frameRect->size.height/2.0;
// Create a circular clipping path
PSnewpath();
PSarc(x, y, cradius, 0.0, 360.0);
PSclosepath();
PSclip();
return self;
}
@end
// this class is only used in the inspector, it animates
// when it draws itself.
@implementation StaticSpaceView
- drawSelf:(const NXRect *)rects :(int)rectCount
{
int i;
if (!rects || !rectCount) return self;
PSselectfont("StarFont", 1.0);
PSsetgray(NX_BLACK);
NXRectFill(rects);
for (i=0; i<20; i++)
{
[self oneStep];
[[self window] flushWindow];
NXPing();
}
return self;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
[super setClipping:YES];
while (nstars < NSTARS) [self addStar];
if(!aniSpaceTag)
aniSpaceTag = DPSAddTimedEntry( // register function Animate
0.08, // to be called every period of
(DPSTimedEntryProc)AnimateSpace, // arg0
(id)self,
NX_BASETHRESHOLD);
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
nstars = 0;
while (nstars < NSTARS) [self addStar];
return self;
}
//************************************************************************
//
// remove the timed entry when we exit
//
//************************************************************************
- free
{
if (aniSpaceTag)
DPSRemoveTimedEntry (aniSpaceTag);
aniSpaceTag = 0;
return [super free];
}
@end
//************************************************************************
//
// This fucntion is registered by DPSaddtimedentry.
// It is subsequently called every period t as registered
// in arg 0 of DPSaddtimedentry.
//
//************************************************************************
static void AnimateSpace(DPSTimedEntry time_tag, double now, id self)
{
[self display];
}
@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
#import "InactivityTimer.h"
#import <appkit/Application.h>
@implementation StaticSpaceITimerView
//************************************************************************
//
// sent to us after nib objects are unarchived and init'd
//
//************************************************************************
- awakeFromNib
{
[secTillTimeoutField setIntValue:10];
return self;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
[super setClipping:YES];
while (nstars < NSTARS2) [self addStar];
if(!aniSpaceITimerTag)
aniSpaceITimerTag = DPSAddTimedEntry( // register function Animate
0.08, // to be called every period of
(DPSTimedEntryProc)AnimateSpaceIT, // arg0
(id)self,
NX_BASETHRESHOLD);
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
int i;
if (!rects || !rectCount) return self;
PSselectfont("StarFont", 1.0);
PSsetgray(NX_BLACK);
NXRectFill(rects);
for (i=0; i<20; i++)
{
[self oneStep];
[[self window] flushWindow];
NXPing();
}
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
nstars = 0;
while (nstars < NSTARS2) [self addStar];
return self;
}
//************************************************************************
//
// remove the timed entry when we exit
//
//************************************************************************
- removeTimedEntry
{
if (aniSpaceITimerTag)
DPSRemoveTimedEntry (aniSpaceITimerTag);
aniSpaceITimerTag = 0;
return self;
}
//************************************************************************
//
// countdown to inactivity timeout
//
//************************************************************************
- countdown
{
time(<ime); // Get time and place in time_t
if(ltime > preltime)
{
if((seconds = [secTillTimeoutField intValue]) == 0)
[[window delegate] continue:self];
else
[secTillTimeoutField setIntValue:(seconds - 1)];
[secTillTimeoutField display];
preltime = ltime;
}
return self;
}
@end
//************************************************************************
//
// This fucntion is registered by DPSaddtimedentry.
// It is subsequently called every period t as registered
// in arg 0 of DPSaddtimedentry.
//
//************************************************************************
static void AnimateSpaceIT(DPSTimedEntry time_tag, double now, id self)
{
[self countdown];
[self display];
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.