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.