[comp.sys.sgi] chains dangling from cursor

architec@cutmcvax.cs.curtin.edu.au (Phil Dench ) (01/02/91)

Ever wondered what to do about all those idle CPU cycles?

Below is a little prog that draws chains hanging off the
cursor (one chain per process). There are command line args
to control length of chain and refresh rate. And there are
several parameters within the code (eg elasticity) that can be 
fiddled with.

If I get around to it, future version will have optional
spiders hanging off the end that stick to walls and start
climbing. I may add wind too; the chains get a bit boring
if you dont move the cursor around in this version.

Just stow the window if you want to use all those CPU
cycles for some real work.

Phil

------8<-----------8<-----------8<-----------8<-----------8<-----------8<-----

/* $Header: /usr/people/envy/architec/src/chain/RCS/chain.c,v 1.2 91/01/02 14:52:51 architec Exp $ */

/****************************************************************************\
*                                                                            *
*                                                                            *
*                          Chain hanging off cursor                          *
*                                                                            *
*                                                                            *
*                           Original SDL code from                           *
*                                                                            *
*                           Alias/2 Version 2.4.1                            *
*            Scene Descriptiopn Language (SDL) Programming Manual            *
*                     Introduction to Dynamics Tutorial                      *
*                            Alias Research Inc.                             *
*                                                                            *
*                                                                            *
*                            Coerced into C/gl by                            *
*                                                                            *
*                                 Phil Dench                                 *
*                     architec@cutmcvax.cs.curtin.edu.au                     *
*                                                                            *
*                                                                            *
\****************************************************************************/

/*
 * $Log:	chain.c,v $
 * Revision 1.2  91/01/02  14:52:51  architec
 * clean up before release
 * 
 * Revision 1.1  91/01/02  14:06:18  architec
 * Initial revision
 * 
 */

#include <stdio.h>
#include <math.h>

#include <gl.h>
#include <device.h>

static char 	* prog_name;

static Boolean	sleep_at_start;
static int		refresh_rate;
static int 		num_part;

#define 		RADIUS	1.0
#define 		SCALE 	20.0

static void usage()
{
	fprintf( stderr, "usage: %s [-s] [-r<num>] [-n<num>]\n", prog_name);
}

static void help()
{
	fprintf( stderr, "\n");
	usage();
	fprintf( stderr, "\n");
	fprintf( stderr, " where ...\n");
	fprintf( stderr, "\n");
	fprintf( stderr, "    -s         : sleep at start\n");
	fprintf( stderr, "    -r<num>    : refresh rate          [6]\n");
	fprintf( stderr, "    -n<num>    : number of chain links [30]\n");
	fprintf( stderr, "\n");
}

static void parse_command_line( argc, argv)
	int argc;
	char ** argv;
{
	int arg;

	prog_name = argv[ 0];

	sleep_at_start = FALSE;
	refresh_rate = 6;
	num_part = 30;

	for( arg = 1; arg < argc; arg++)
	{
		if( argv[ arg][ 0] == '-')
		{
			switch( argv[ arg][ 1])
			{
				case 'n':
					num_part = atoi( argv[ arg] + 2);
				break;

				case 'r':
					refresh_rate = atoi( argv[ arg] + 2);
					if( refresh_rate < 2)
					{
						refresh_rate = 2;
					}
				break;

				case 's':
					sleep_at_start = TRUE;
				break;

				case 'h':
					help();
					exit( 1);
				break;

				default:
					usage();
					exit( 1);
				break;
			}
		}
		else
		{
			
		}
	}
}

static void open_graphics()
{
	/*
	noborder();
	prefposition( 0, 1, 0, 1);
	*/

	/*
	foreground();
	*/

	(void) winopen( "cursor");

	color( BLACK);
	clear();

	fullscrn();

	ortho2( 0.0, 1280.0 / SCALE, 0.0, 1024.0 / SCALE);

	drawmode( PUPDRAW);
}

static void draw_chain( num_part, x, y, old_x, old_y)
	int		num_part;
	double 	* x,
			* y,
			* old_x,
			* old_y;
{
	int part;

	/* Loop for each object outputting the geometry */

	color( PUP_CLEAR);

	move( old_x[ 0], old_y[ 0], 0.0);
	for( part = 1; part < num_part - 1; part++)
	{
		draw( old_x[ part], old_y[ part], 0.0);
	}

	color( PUP_WHITE);

	move( old_x[ 0] = x[ 0], old_y[ 0] = y[ 0], 0.0);
	for( part = 1; part < num_part - 1; part++)
	{
		draw( old_x[ part] = x[ part], old_y[ part] = y[ part], 0.0);
	}
}

static void calc_chain( num_part, x, y, vx, vy, mass)
	int 	num_part;
	double	* x,
			* y,
			* vx,
			* vy,
			* mass;
{
	double forcex[ 64], forcey[ 64];

	/* Define the control parameters for the particles and the springs */

	static int numint = 10;

	static double radius = RADIUS;

	static double timescale = 2.1;
	static double gravity = 0.125 / 30.0;
	static double ymax = 1024.0 / SCALE;
	static double ymin = 0.0;
	static double xmax = 1280.0 / SCALE;
	static double xmin = 0.0;
	static double elasticity = 0.95;
	static double floor_damping = 0.1;
	static double air_damping = 0.999;
	static double bond = 4.0;
	static double damping = 4.0;
	static double bend = 0.15;
	static double bend_damping = 0.15;

	double rad2 = radius * radius;
	double floor_thickness = radius / 2.0;

	double a = bond;
	double b = bond / (radius * radius);

	double dt = timescale * 1.0 / numint;
	double dt2 = 0.5 * dt * dt;

	int sub;
	int part;

	/* Loop for each subinterval of time */

	for( sub = 0; sub < numint; sub++)
	{
		for( part = 0; part < num_part; part++)
		{
			forcex[ part] = 0.0;
			forcey[ part] = 0.0;
		}

		/* Loop for each part to compute the effect of the neighbour forces */

		for( part = 0; part < num_part - 1; part++)
		{
			double dx, dy, r2;
			int other;

			other = part + 1;

			/* Compute the interparticle attraction */
				
			dx = x[ part] - x[ other];
			dy = y[ part] - y[ other];
				
			r2 = dx * dx + dy * dy;

			/* original code  - causes chain to stretch - pd
			if( r2 < 4.0 * rad2)
			*/
			{
				double r, r3, r5;
				double f, scf;
				double dvx, dvy;

				r = sqrt( r2);
				if( r < 0.0001) r = 0.0001;
								
				/* Compute the attraction-repulsion term */
					
				r3 = r * r2;
				r5 = r3 * r2;					

				/* the original calc - causes chain to stretch - pd
				scf = (radius - 0.5 * r) / radius;
				*/

				scf = 0.5;

				dvx = vx[ part] - vx[ other];
				dvy = vy[ part] - vy[ other];
				
				/* the original calc - causes chain to stretch - pd
				f = ((a / r5 - b / r3) - damping *
						 (dvx * dx + dvy * dy) / r2) * scf;
				*/

				f = ((a / r2 - b / r) - damping *
						 (dvx * dx + dvy * dy) / r2) * scf;
				
				forcex[ part] = forcex[ part] + f * dx;
				forcey[ part] = forcey[ part] + f * dy;

				forcex[ other] = forcex[ other] - f * dx;
				forcey[ other] = forcey[ other] - f * dy;

				/* Compute the floor drag-factor term */

				if( y[ part] < ymin + floor_thickness)
				{
					double atn;

					atn = floor_damping * (ymin + floor_thickness -
										 y[ part]) / (floor_thickness);
					forcex[ part] = forcex[ part] - atn * vx[ part];
					forcey[ part] = forcey[ part] - atn * vy[ part];
				}
			}
		}

		/* Loop for each particle computing the torques */
		
		for( part = 1; part < num_part - 1; part++)
		{
			int last, next;
			double xmean, ymean;
			double dx, dy;
			double dxb, dyb;
			double dvx, dvy;
			double vxmean, vymean;
			double d;

			last = part - 1;
			next = part + 1;
			xmean = (x[ last] + x[ part] + x[ next]) / 3.0;
			ymean = (y[ last] + y[ part] + y[ next]) / 3.0;
			dx = x[ part] - xmean;
			dy = y[ part] - ymean;
			d = sqrt( dx * dx + dy * dy);
			dxb = dx * bend;
			dyb = dy * bend;
			
			vxmean = (vx[ last] + vx[ part] + vx[ next]) / 3.0;
			vymean = (vy[ last] + vy[ part] + vy[ next]) / 3.0;
			dvx = vx[ part] - vxmean;
			dvy = vy[ part] - vymean;

			dxb = dxb + bend_damping * dvx;
			dyb = dyb + bend_damping * dvy;
			
			forcex[ part] = forcex[ part] - dxb;
			forcey[ part] = forcey[ part] - dyb;

			dxb = dxb * 0.5;
			dyb = dyb * 0.5;

			forcex[ last] = forcex[ last] + dxb;
			forcey[ last] = forcey[ last] + dyb;

			forcex[ next] = forcex[ next] + dxb;
			forcey[ next] = forcey[ next] + dyb;
		}	
		
		/* Loop for each particle applying the forces to the position */

		for( part = 0; part < num_part; part++)
		{
			double ax, ay;

			ax = forcex[ part] / mass[ part];
			ay = - gravity + forcey[ part] / mass[ part];

			/*
			if( part > 0 || frame > 900)
			*/
			if( part > 0)
			{			
				x[ part] = x[ part] + vx[ part] * dt + ax * dt2;
				y[ part] = y[ part] + vy[ part] * dt + ay * dt2;
				
				vx[ part] = vx[ part] + ax * dt;
				vy[ part] = vy[ part] + ay * dt;

				vx[ part] *= air_damping;
				vy[ part] *= air_damping;
			}
			else
			{
				x[ part] = getvaluator( MOUSEX) / SCALE;
				y[ part] = getvaluator( MOUSEY) / SCALE;
			}

			/* Bounce off the floor */
			
			if( y[ part] > ymax)
			{
				y[ part] = ymax + (ymax - y[ part]) * elasticity;
				vy[ part] = -vy[ part] * elasticity;
			}

			if( y[ part] < ymin)
			{
				y[ part] = ymin + (ymin - y[ part]) * elasticity;
				vy[ part] = -vy[ part] * elasticity;
			}

			if( x[ part] > xmax)
			{
				x[ part] = xmax + (xmax - x[ part]) * elasticity;
				vx[ part] = -vx[ part] * elasticity;
			}
			if( x[ part] < xmin)
			{
				x[ part] = xmin + (xmin - x[ part]) * elasticity;
				vx[ part] = -vx[ part] * elasticity;
			}
		}
	}
}

int main( argc, argv)
	int 	argc;
	char 	** argv;
{
	/* Simulate interparticle attraction for several bodies */

	/* Define the arrays to store the particle descriptions */

	double x[ 64], y[ 64], vx[ 64], vy[ 64], mass[ 64];
	double old_x[ 64], old_y[ 64];

	int part;

	Boolean paused = FALSE;
	Boolean quit = FALSE;

	parse_command_line( argc, argv);

	/* sleep for a bit */
	if( sleep_at_start)
	{
		sleep( 120);
	}

	open_graphics();

	old_x[ 0] = x[ 0] = getvaluator( MOUSEX) / SCALE;
	old_y[ 0] = y[ 0] = getvaluator( MOUSEY) / SCALE;

	for( part = 1; part < num_part; part++)
	{
		old_x[ part] = x[ part] = x[ part - 1] + RADIUS;
		old_y[ part] = y[ part] = y[ part - 1];
		vx[ part] = 0.0;
		vy[ part] = 0.0;
		mass[ part] = 1.0;
	}

	qdevice( TIMER0);
	noise( TIMER0, refresh_rate);

	qdevice( WINFREEZE);
	qdevice( WINTHAW);
	qdevice( WINQUIT);

	while( !quit)
	{
		long dev;
		short val;

		switch( dev = qread( &val))
		{
			case TIMER0:
				if( !paused && !( getbutton( MENUBUTTON)))
				{
					calc_chain( num_part, x, y, vx, vy, mass);

					if( !( getbutton( MENUBUTTON)))
					{
						draw_chain( num_part, x, y, old_x, old_y);
					}
				}
			break;

			case WINTHAW:
				paused = FALSE;
			break;

			case WINFREEZE:
				paused = TRUE;
				color( PUP_CLEAR);
				clear();
			break;

			case WINQUIT:
				quit = TRUE;
				color( PUP_CLEAR);
				clear();
			break;

			case REDRAW:
				endfullscrn();
				drawmode( NORMALDRAW);
				color( BLACK);
				clear();

				fullscrn();
				ortho2( 0.0, 1280.0 / SCALE, 0.0, 1024.0 / SCALE);
				drawmode( PUPDRAW);
			break;
		}
	}

	return( 0);
}