This is SpinViewPart.m in view mode; [Download] [Up]
#import "SpinView.h"
#import <libc.h>
#import <math.h>
#import <sys/time.h>
#import <defaults/defaults.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
#import <appkit/Slider.h>
/**********************************************************************/
#define PI ( 3.1415926 )
#define DEGtoRAD(deg) ( deg * PI/180.0 )
#define LEFT ( 100 )
#define RIGHT ( 101 )
#define UP ( 102 )
#define DOWN ( 103 )
/**********************************************************************/
// Looks like a set of defaults to me, how 'bout you?
static NXDefaultsVector SpinDefaults =
{
{ "Radius", "104" },
{ "TailLength", "95" },
{ "XIncrement", "1.0" },
{ "YIncrement", "2.5" },
{ "AngleIncrement", "38" },
{ "NumberOfSpinners", "4" },
{ NULL },
};
/**********************************************************************/
// userPathData holds four endpoint coordinates for each line to draw.
// The endpoint coordinates are calculated on the fly, in drawSpinners:.
// userPathOps holds a moveto/lineto pair for each line that can be drawn.
// Both of these are passed to DPSDoUserPath(), along with the other
// neccessary arguments. See the method drawSpinners: for the call.
static float userPathData[ 4*MAXNUMSPINS ];
static char userPathOps[ ] =
{
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto,
dps_moveto, dps_lineto
};
static float Sines[ 91 ]; // Table of sines. Filled in initFrame:
static float SinRad[ 91 ]; // Table of (sines * current radius).
// Filled in setStartPoints.
static char tnum[ 20 ]; // Used to modify defaults, if they
// need to be adjusted in the database.
/**********************************************************************/
@implementation SpinView
/**********************************************************************/
- (const char *) windowTitle
{
return ( const char * ) "Larrys Wild Spinners";
}
/**********************************************************************/
- ( void ) getAngleInc
{
// Gets the current angle increment from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXANGLEINC and MINANGLEINC. If the value is out of range it
// is reset to the nearest inbounds value.
CurAngleInc = atoi( NXGetDefaultValue( OWNER, "AngleIncrement" ) );
if( CurAngleInc > MAXANGLEINC )
{
sprintf( tnum, "%3d", MAXANGLEINC );
NXWriteDefault( OWNER, "AngleIncrement", tnum );
CurAngleInc = MAXANGLEINC;
}
else if( CurAngleInc < MINANGLEINC )
{
sprintf( tnum, "%3d", MINANGLEINC );
NXWriteDefault( OWNER, "AngleIncrement", tnum );
CurAngleInc = MINANGLEINC;
}
}
/**********************************************************************/
- ( void ) getNumSpinners
{
// Gets the current number of spinners from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXNUMSPINS and MINNUMSPINS. If the value is out of range it
// is reset to the nearest inbounds value.
CurNumSpins = atoi( NXGetDefaultValue( OWNER, "NumberOfSpinners" ) );
if( CurNumSpins > MAXNUMSPINS )
{
sprintf( tnum, "%2d", MAXNUMSPINS );
NXWriteDefault( OWNER, "NumberOfSpinners", tnum );
CurNumSpins = MAXNUMSPINS;
}
else if( CurNumSpins < MINNUMSPINS )
{
sprintf( tnum, "%2d", MINNUMSPINS );
NXWriteDefault( OWNER, "NumberOfSpinners", tnum );
CurNumSpins = MINNUMSPINS;
}
}
/**********************************************************************/
- ( void ) getRadius
{
// Gets the current circle radius from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXRADIUS and MINRADIUS. If the value is out of range it
// is reset to the nearest inbounds value.
CurRadius = atoi( NXGetDefaultValue( OWNER, "Radius" ) );
if( CurRadius > MAXRADIUS )
{
sprintf( tnum, "%3d", MAXRADIUS );
NXWriteDefault( OWNER, "Radius", tnum );
CurRadius = MAXRADIUS;
}
else if( CurRadius < MINRADIUS )
{
sprintf( tnum, "%3d", MINRADIUS );
NXWriteDefault( OWNER, "Radius", tnum );
CurRadius = MINRADIUS;
}
}
/**********************************************************************/
- ( void ) getTailLength
{
// Gets the current tail length from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXTAILLEN and MINTAILLEN. If the value is out of range it
// is reset to the nearest inbounds value.
CurTailLen = atoi( NXGetDefaultValue( OWNER, "TailLength" ) );
if( CurTailLen > MAXTAILLEN )
{
sprintf( tnum, "%3d", MAXTAILLEN );
NXWriteDefault( OWNER, "TailLength", tnum );
CurTailLen = MAXTAILLEN;
}
else if( CurTailLen < MINTAILLEN )
{
sprintf( tnum, "%3d", MINTAILLEN );
NXWriteDefault( OWNER, "TailLength", tnum );
CurTailLen = MINTAILLEN;
}
}
/**********************************************************************/
- ( void ) getXIncrement
{
// Gets the current X increment from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXXINC and MINXINC. If the value is out of range it
// is reset to the nearest inbounds value.
CurXInc = atof( NXGetDefaultValue( OWNER, "XIncrement" ) );
if( CurXInc > MAXXINC )
{
sprintf( tnum, "%7.3f", MAXXINC );
NXWriteDefault( OWNER, "XIncrement", tnum );
CurXInc = MAXXINC;
}
else if( CurXInc < MINXINC )
{
sprintf( tnum, "%7.3f", MINXINC );
NXWriteDefault( OWNER, "XIncrement", tnum );
CurXInc = MINXINC;
}
}
/**********************************************************************/
- ( void ) getYIncrement
{
// Gets the current Y increment from the defaults database.
// Checks to make sure the value found is within the range delimited
// by MAXYINC and MINYINC. If the value is out of range it
// is reset to the nearest inbounds value.
CurYInc = atof( NXGetDefaultValue( OWNER, "YIncrement" ) );
if( CurYInc > MAXYINC )
{
sprintf( tnum, "%7.3f", MAXYINC );
NXWriteDefault( OWNER, "YIncrement", tnum );
CurYInc = MAXYINC;
}
else if( CurYInc < MINYINC )
{
sprintf( tnum, "%7.3f", MINYINC );
NXWriteDefault( OWNER, "YIncrement", tnum );
CurYInc = MINYINC;
}
}
/**********************************************************************/
- initFrame : ( const NXRect * ) frameRect
{
// How many miscellaneous setup things can you
// put into a single method, geez!
struct timeval tp;
struct timezone tzp;
int i;
[ super initFrame : frameRect ];
[ self setOpaque : YES ];
[ self setClipping : NO ];
NXRegisterDefaults( OWNER, SpinDefaults );
[ self getNumSpinners ];
[ self getRadius ];
[ self getAngleInc ];
[ self getTailLength ];
[ self getXIncrement ];
[ self getYIncrement ];
PSsetlinewidth( 0.15 );
gettimeofday( &tp, &tzp );
srandom( ( int ) tp.tv_sec + tp.tv_usec );
for( i = 0; i < 91; ++i )
Sines[ i ] = sin( DEGtoRAD(i) ); // Fill in sines table.
[ self resetSpinners ];
return self;
}
/**********************************************************************/
- newWindow
{
[ self resetSpinners ];
return self;
}
/**********************************************************************/
- sizeTo : ( NXCoord ) width : ( NXCoord ) height
{
[ super sizeTo : width : height ];
[ self resetSpinners ];
return self;
}
/**********************************************************************/
- ( void ) setStartPoints
{
int i;
for( i = 0; i < CurNumSpins; ++i )
{
headSpin[i].x = tailSpin[i].x = random() % ( (int) bounds.size.width);
headSpin[i].y = tailSpin[i].y = random() % ( (int) bounds.size.height);
}
// Reset the color cycle.
r = 0;
g = ( 2 * PI ) / 3;
// Everytime the current radius changes the SinRad[] table and the
// bounding box need to be updated. These should probably be done
// in the setRadius: method, but it's so fast I don't think it matters.
for( i = 0; i < 91; ++i )
SinRad[ i ] = CurRadius * Sines[ i ];
// Setting the bounding box this way gives the spinners a more consistent
// feel. When there is only one spinner displayed the speed of the spinner
// appears near the speed of having 20 spinners on the screen. Using a
// consistent bounding box has a lot to do with it.
// The 30 is a fudge factor, I guess I should figure out the real value
// at some point.
Bbox[ 0 ] = bounds.origin.x - ( CurRadius + 30 );
Bbox[ 1 ] = bounds.origin.y - ( CurRadius + 30 );
Bbox[ 2 ] = bounds.origin.x + bounds.size.width + ( CurRadius + 30 );
Bbox[ 3 ] = bounds.origin.y + bounds.size.height + ( CurRadius + 30 );
}
/**********************************************************************/
- ( void ) resetSpinners
{
// This method realigns the head and tail spinner data structures.
// Each time a control is adjusted this method is called. It is much
// simpler to reset everything after a control is adjusted than
// it is to figure out how to align things on the fly.
SPINNER *hs;
SPINNER *ts;
int iMod2;
int i;
[ self setStartPoints ];
hs = headSpin; // Moving pointers through the arrays is a lot faster.
ts = tailSpin;
for( i = 0; i < CurNumSpins; ++i, ++hs, ++ts )
{
hs->a = ts->a = random() % 360;
hs->ix = ts->ix = CurXInc;
hs->iy = ts->iy = CurYInc;
iMod2 = i % 2;
hs->dx = ts->dx = ( ( iMod2 ) ? RIGHT : LEFT );
hs->dy = ts->dy = ( ( iMod2 ) ? DOWN : UP );
}
if( tailSlider )
CurTailLen = [ tailSlider intValue ];
}
/**********************************************************************/
- ( void ) moveSpinner : ( SPINNER * ) spin
{
spin->a = ( spin->a + CurAngleInc ) % 360;
if( spin->x >= bounds.size.width )
spin->dx = LEFT;
else if( spin->x <= 0 )
spin->dx = RIGHT;
spin->x += ( spin->dx == RIGHT ) ? spin->ix : -spin->ix;
if( spin->y >= bounds.size.height )
spin->dy = DOWN;
else if( spin->y <= 0 )
spin->dy = UP;
spin->y += ( spin->dy == UP ) ? spin->iy : -spin->iy;
}
/**********************************************************************/
- ( void ) drawSpinners : ( SPINNER * ) spin
{
// I know this looks a little complicated, but it really
// just builds a user path. It fills the userPathData[] array
// with enough endpoints to draw CurNumSpins lines. Once userPathData[]
// is filled its passed to DPSDoUserPath(), along with the count of
// points in the array.
float offset1;
float offset2;
int i, j;
for( i = 0, j = 0; i < CurNumSpins; ++i, ++spin )
{
if( spin->a >= 270 )
{
offset1 = -SinRad[ 360 - spin->a ];
offset2 = SinRad[ spin->a - 270 ];
}
else if( spin->a >= 180 )
{
offset1 = -SinRad[ spin->a - 180 ];
offset2 = -SinRad[ 270 - spin->a ];
}
else if( spin->a >= 90 )
{
offset1 = SinRad[ 180 - spin->a ];
offset2 = -SinRad[ spin->a - 90 ];
}
else
{
offset1 = SinRad[ spin->a ];
offset2 = SinRad[ 90 - spin->a ];
}
userPathData[ j++ ] = spin->x + offset2;
userPathData[ j++ ] = spin->y + offset1;
userPathData[ j++ ] = spin->x - offset2;
userPathData[ j++ ] = spin->y - offset1;
[ self moveSpinner : spin ];
}
DPSDoUserPath( userPathData, j, dps_float, userPathOps,
CurNumSpins*2, Bbox, dps_ustroke );
}
/**********************************************************************/
- oneStep
{
if( CurTailLen-- <= 0 )
{
PSsetrgbcolor( 0.0, 0.0, 0.0 );
[ self drawSpinners : tailSpin ];
}
// PSsetgray( NX_WHITE ); // Used only for testing.
r -= ( r > 2 * PI ) ? 2 * PI : -0.008;
g -= ( g > 2 * PI ) ? 2 * PI : -0.02;
b -= ( b > 2 * PI ) ? 2 * PI : -0.01;
PSsetrgbcolor( ( cos( r ) + 1.0 ) * .5,
( cos( g ) + 1.0 ) * .5,
( cos( b ) + 1.0 ) * .5 );
[ self drawSpinners : headSpin ];
return self;
}
/**********************************************************************/
- drawSelf : ( NXRect * ) rect : ( int ) count
{
if (!rect || !count)
return self;
PSsetgray( NX_BLACK );
NXRectFill( rect );
return self;
}
/**********************************************************************/
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.