This is NekoView.m in view mode; [Download] [Up]
/* Generated by Interface Builder */
#import "NekoView.h"
// based on xneko by Masayuki Koba
// This module is really, truly hideous because I chose to preserve the
// original code to a large extent rather than reimplementing the state
// machine in a sane fashion.
#import <libc.h>
#import <math.h>
#import <appkit/defaults.h>
#import <appkit/graphics.h>
#import <appkit/Application.h>
#import <appkit/NXImage.h>
#import <dpsclient/wraps.h>
const NXCoord Even[]={
Mati2, // STATE_STOP
Jare2, // STATE_JARE
Kaki1, // STATE_KAKI
Mati3, // STATE_AKUBI
Sleep1, // STATE_SLEEP
Awake, // STATE_AWAKE
Up1, // STATE_U_MOVE
Down1, // STATE_D_MOVE
Left1, // STATE_L_MOVE
Right1, // STATE_R_MOVE
UpLeft1, // STATE_UL_MOVE
UpRight1, // STATE_UR_MOVE
DownLeft1, // STATE_DL_MOVE
DownRight1, // STATE_DR_MOVE
UpTogi1, // STATE_U_TOGI
DownTogi1, // STATE_D_TOGI
LeftTogi1, // STATE_L_TOGI
RightTogi1 // STATE_R_TOGI
};
const NXCoord Odd[]={
Mati2, // STATE_STOP
Mati2, // STATE_JARE
Kaki2, // STATE_KAKI
Mati3, // STATE_AKUBI
Sleep2, // STATE_SLEEP
Awake, // STATE_AWAKE
Up2, // STATE_U_MOVE
Down2, // STATE_D_MOVE
Left2, // STATE_L_MOVE
Right2, // STATE_R_MOVE
UpLeft2, // STATE_UL_MOVE
UpRight2, // STATE_UR_MOVE
DownLeft2, // STATE_DL_MOVE
DownRight2, // STATE_DR_MOVE
UpTogi2, // STATE_U_TOGI
DownTogi2, // STATE_D_TOGI
LeftTogi2, // STATE_L_TOGI
RightTogi2 // STATE_R_TOGI
};
#define PI_PER8 ((double)M_PI/(double)8)
static double SinPiPer8Times3;
static double SinPiPer8;
@implementation NekoView
void inline TickCount(NekoView *self)
{
if ( ++(self->NekoTickCount) >= MAX_TICK ) {
self->NekoTickCount = 0;
}
if ( self->NekoTickCount % 2 == 0 ) {
if ( self->NekoStateCount < MAX_TICK ) {
self->NekoStateCount++;
}
}
}
void inline SetNekoState(NekoView *self, int SetValue)
{
self->NekoTickCount=0;
self->NekoStateCount=0;
self->NekoState=SetValue;
}
void DrawNeko(NekoView *self, NXCoord DrawIcon)
{
self->NekoLastIcon.origin.x=DrawIcon;
if ([self isAutodisplay]) {
[self lockFocus];
if (NX_X(&self->NekoPos)!=self->NekoLastXY.x||
NX_Y(&self->NekoPos)!=self->NekoLastXY.y) {
const NXRect zero={ { Space, 0.0 },
{ (NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT } };
self->NekoLastXY.y+=(NXCoord)BITMAP_HEIGHT; // X lossage
[self->bitmaps composite:NX_SOVER fromRect:&zero
toPoint:&self->NekoLastXY];
}
self->NekoPos.origin.y+=(NXCoord)BITMAP_HEIGHT; // X lossage
[self->bitmaps composite:NX_SOVER fromRect:&self->NekoLastIcon
toPoint:&self->NekoPos.origin];
self->NekoPos.origin.y-=(NXCoord)BITMAP_HEIGHT;
PSflushgraphics();
[self unlockFocus];
}
else [self setNeedsDisplay:YES];
self->NekoLastXY=self->NekoPos.origin;
}
void NekoDirection(NekoView *self)
{
int NewState;
double LargeX, LargeY;
double Length;
double SinTheta;
if ( self->NekoMoveDx == 0 && self->NekoMoveDy == 0 ) {
NewState = STATE_STOP;
} else {
LargeX = (double)self->NekoMoveDx;
LargeY = (double)(-self->NekoMoveDy);
Length = sqrt( LargeX * LargeX + LargeY * LargeY );
SinTheta = LargeY / Length;
if ( self->NekoMoveDx > 0 ) {
if ( SinTheta > SinPiPer8Times3 ) {
NewState = STATE_U_MOVE;
} else if ( ( SinTheta <= SinPiPer8Times3 )
&& ( SinTheta > SinPiPer8 ) ) {
NewState = STATE_UR_MOVE;
} else if ( ( SinTheta <= SinPiPer8 )
&& ( SinTheta > -( SinPiPer8 ) ) ) {
NewState = STATE_R_MOVE;
} else if ( ( SinTheta <= -( SinPiPer8 ) )
&& ( SinTheta > -( SinPiPer8Times3 ) ) ) {
NewState = STATE_DR_MOVE;
} else {
NewState = STATE_D_MOVE;
}
} else {
if ( SinTheta > SinPiPer8Times3 ) {
NewState = STATE_U_MOVE;
} else if ( ( SinTheta <= SinPiPer8Times3 )
&& ( SinTheta > SinPiPer8 ) ) {
NewState = STATE_UL_MOVE;
} else if ( ( SinTheta <= SinPiPer8 )
&& ( SinTheta > -( SinPiPer8 ) ) ) {
NewState = STATE_L_MOVE;
} else if ( ( SinTheta <= -( SinPiPer8 ) )
&& ( SinTheta > -( SinPiPer8Times3 ) ) ) {
NewState = STATE_DL_MOVE;
} else {
NewState = STATE_D_MOVE;
}
}
}
if ( self->NekoState != NewState ) {
SetNekoState(self, NewState);
}
}
BOOL IsWindowOver(NekoView *self)
{
BOOL ReturnValue = NO;
if ( NX_Y(&self->NekoPos) <= 0.0 ) {
self->NekoPos.origin.y = 0.0;
ReturnValue = YES;
} else if ( NX_Y(&self->NekoPos) >=
NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) {
self->NekoPos.origin.y = NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT;
ReturnValue = YES;
}
if ( NX_X(&self->NekoPos) <= 0.0 ) {
self->NekoPos.origin.x = 0;
ReturnValue = YES;
} else if ( NX_X(&self->NekoPos) >=
NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) {
self->NekoPos.origin.x = NX_WIDTH(&self->bounds) - BITMAP_WIDTH;
ReturnValue = YES;
}
return( YES );
}
BOOL IsNekoDontMove(NekoView *self)
{
if ( NX_X(&self->NekoPos) == self->NekoLastXY.x &&
NX_Y(&self->NekoPos) == self->NekoLastXY.y ) {
return( YES );
} else {
return( NO );
}
}
BOOL IsNekoMoveStart(NekoView *self)
{
if ( ( self->PrevMouseX >= self->MouseX - IDLE_SPACE
&& self->PrevMouseX <= self->MouseX + IDLE_SPACE ) &&
( self->PrevMouseY >= self->MouseY - IDLE_SPACE
&& self->PrevMouseY <= self->MouseY + IDLE_SPACE ) ) {
return( NO );
} else {
return( YES );
}
}
void CalcDxDy(NekoView *self)
{
NXPoint RelativeXY;
double LargeX, LargeY;
double DoubleLength, Length;
[self->window getMouseLocation:&RelativeXY];
[self convertPoint:&RelativeXY fromView:nil];
self->PrevMouseX = self->MouseX;
self->PrevMouseY = self->MouseY;
self->MouseX = (int)RelativeXY.x;
self->MouseY = (int)RelativeXY.y;
LargeX = (double)( self->MouseX - NX_X(&self->NekoPos) - BITMAP_WIDTH/2 );
LargeY = (double)( self->MouseY - NX_Y(&self->NekoPos) - BITMAP_HEIGHT );
DoubleLength = LargeX * LargeX + LargeY * LargeY;
if ( DoubleLength != (double)0 ) {
Length = sqrt( DoubleLength );
if ( Length <= self->NekoSpeed ) {
self->NekoMoveDx = (int)LargeX;
self->NekoMoveDy = (int)LargeY;
} else {
self->NekoMoveDx = (int)( ( self->NekoSpeed * LargeX ) / Length );
self->NekoMoveDy = (int)( ( self->NekoSpeed * LargeY ) / Length );
}
} else {
self->NekoMoveDx = self->NekoMoveDy = 0;
}
}
void NekoThinkDraw(DPSTimedEntry te, double now, void *userData) {
NekoView *self=(NekoView *)userData;
CalcDxDy(self);
if ( self->NekoState != STATE_SLEEP )
DrawNeko(self, self->NekoTickCount % 2 == 0 ?
Even[self->NekoState] : Odd[self->NekoState]);
else DrawNeko(self, self->NekoTickCount % 8 <= 3 ?
Even[self->NekoState] :Odd[self->NekoState]);
TickCount(self);
switch ( self->NekoState ) {
case STATE_STOP:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
if ( self->NekoStateCount < TIME_STOP ) {
break;
}
if ( self->NekoMoveDx < 0 && NX_X(&self->NekoPos) <= 0 ) {
SetNekoState(self, STATE_L_TOGI );
} else if ( self->NekoMoveDx > 0 &&
NX_X(&self->NekoPos) >= NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) {
SetNekoState(self, STATE_R_TOGI );
} else if ( self->NekoMoveDy < 0 && NX_Y(&self->NekoPos) <= 0 ) {
SetNekoState(self, STATE_U_TOGI );
} else if ( self->NekoMoveDy > 0 &&
NX_Y(&self->NekoPos) >=
NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) {
SetNekoState(self, STATE_D_TOGI );
} else {
SetNekoState(self, STATE_JARE );
}
break;
case STATE_JARE:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
if ( self->NekoStateCount < TIME_JARE ) {
break;
}
SetNekoState(self, STATE_KAKI );
break;
case STATE_KAKI:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
if ( self->NekoStateCount < TIME_KAKI ) {
break;
}
SetNekoState(self, STATE_AKUBI );
break;
case STATE_AKUBI:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
if ( self->NekoStateCount < TIME_AKUBI ) {
break;
}
SetNekoState(self, STATE_SLEEP );
break;
case STATE_SLEEP:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
break;
case STATE_AWAKE:
if ( self->NekoStateCount < TIME_AWAKE ) {
break;
}
NekoDirection(self);
break;
case STATE_U_MOVE:
case STATE_D_MOVE:
case STATE_L_MOVE:
case STATE_R_MOVE:
case STATE_UL_MOVE:
case STATE_UR_MOVE:
case STATE_DL_MOVE:
case STATE_DR_MOVE:
self->NekoPos.origin.x += self->NekoMoveDx;
self->NekoPos.origin.y += self->NekoMoveDy;
NekoDirection(self);
if ( IsWindowOver(self) ) {
if ( IsNekoDontMove(self) ) {
SetNekoState(self, STATE_STOP );
}
}
break;
case STATE_U_TOGI:
case STATE_D_TOGI:
case STATE_L_TOGI:
case STATE_R_TOGI:
if ( IsNekoMoveStart(self) ) {
SetNekoState(self, STATE_AWAKE );
break;
}
if ( self->NekoStateCount < TIME_TOGI ) {
break;
}
SetNekoState(self, STATE_KAKI );
break;
default:
/* Internal Error */
SetNekoState(self, STATE_STOP );
break;
}
}
void NekoAdjust(NekoView *self)
{
if ( NX_X(&self->NekoPos) < 0 ) {
self->NekoPos.origin.x = 0;
} else if ( NX_X(&self->NekoPos) >
NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) {
self->NekoPos.origin.x = NX_WIDTH(&self->bounds) - BITMAP_WIDTH;
}
if ( NX_Y(&self->NekoPos) < 0 ) {
self->NekoPos.origin.y = 0;
} else if ( NX_Y(&self->NekoPos) >
NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) {
self->NekoPos.origin.y = NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT;
}
}
static id Nlist;
+ initialize
{
SinPiPer8Times3=sin(PI_PER8*(double)3);
SinPiPer8=sin(PI_PER8);
[self setVersion:1];
Nlist=[[List alloc] initCount:1];
return self;
}
+ shouldRun:(BOOL)flag
{
[Nlist makeObjectsPerform:flag ? @selector(start) : @selector(stop)];
return self;
}
- setBitmaps:anObject
{
bitmaps=anObject;
return self;
}
- bitmaps
{
return bitmaps;
}
- (int)start
{
if (!teNum&&[window isVisible]) teNum=DPSAddTimedEntry(
atof(NXGetDefaultValue([NXApp appName], "Interval"))/1000000.0,
NekoThinkDraw, (void *)self, NX_BASETHRESHOLD);
return 0;
}
- (int)stop
{
if (teNum) {
DPSRemoveTimedEntry(teNum);
teNum=0;
}
return 0;
}
- initFrame:(const NXRect *)frameRect tora:(BOOL)flag
{
[super initFrame:frameRect];
[self setFlipped:YES]; // I *hate* X!!!
[self setClipping:NO];
[self setOpaque:YES];
NXSetRect(&NekoPos,
(NX_WIDTH(frameRect)-(NXCoord)(BITMAP_WIDTH/2))/2.0,
(NX_HEIGHT(frameRect)-(NXCoord)(BITMAP_HEIGHT/2))/2.0,
(NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT);
NekoLastXY=NekoPos.origin;
bitmaps=[NXImage findImageNamed:flag ? "tora.tiff" : "bitmaps.tiff"];
NXSetRect(&NekoLastIcon, Even[STATE_STOP], 0.0,
(NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT);
NekoSpeed=atof(NXGetDefaultValue([NXApp appName], "Speed"));
SetNekoState(self, STATE_STOP);
[Nlist addObject:self];
return [self allocateGState];
}
- initFrame:(const NXRect *)frameRect
{
return [self initFrame:frameRect tora:NO];
}
- free
{
[self stop];
[Nlist removeObject:self];
return [super free];
}
- (BOOL)acceptsFirstMouse;
{
return YES;
}
- mouseDown:(NXEvent *)theEvent
{
return [window dragFrom:theEvent->location.x:theEvent->location.y
eventNum:theEvent->data.mouse.eventNum];
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
if (rectCount==3) {
NXEraseRect(&rects[1]);
NXEraseRect(&rects[2]);
if (!NXIntersectsRect(&rects[1], &NekoPos)&&
!NXIntersectsRect(&rects[2], &NekoPos)) return self;
}
else NXEraseRect(rects);
NekoPos.origin.y+=BITMAP_HEIGHT; // X lossage
[bitmaps composite:NX_SOVER fromRect:&NekoLastIcon
toPoint:&NekoPos.origin];
NekoPos.origin.y-=BITMAP_HEIGHT;
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width:height];
NekoAdjust(self);
return self;
}
- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteTypes(stream, "!iiiiiii", &teNum, &NekoTickCount, &NekoStateCount,
&NekoState, &MouseX, &MouseY, &PrevMouseX, &PrevMouseY);
NXWriteRect(stream, &NekoPos);
NXWriteTypes(stream, "ii", &NekoMoveDx, &NekoMoveDy);
NXWritePoint(stream, &NekoLastXY);
NXWriteRect(stream, &NekoLastIcon);
NXWriteType(stream, "d", &NekoSpeed);
NXWriteObjectReference(stream, bitmaps);
return self;
}
- read:(NXTypedStream *)stream
{
[super read:stream];
NXReadTypes(stream, "!iiiiiii", &teNum, &NekoTickCount, &NekoStateCount,
&NekoState, &MouseX, &MouseY, &PrevMouseX, &PrevMouseY);
teNum=0;
NXReadRect(stream, &NekoPos);
NXReadTypes(stream, "ii", &NekoMoveDx, &NekoMoveDy);
NXReadPoint(stream, &NekoLastXY);
NXReadRect(stream, &NekoLastIcon);
NXReadType(stream, "d", &NekoSpeed);
bitmaps=NXReadObject(stream);
return self;
}
- awake
{
[super awake];
if (!bitmaps)
bitmaps=[NXImage findImageNamed:"bitmaps.tiff"]; // sigh
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.