[comp.graphics] Sperm Demo

po0o+@andrew.cmu.edu (Paul Andrew Olbrich) (04/03/91)

Try this!

-------
/*
	glsperm.c
	Drew Olbrich, February 1991

	Compile with:

	cc -O sperm.c -o sperm -lgl_s -lm
*/

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

#include "gl.h"
#include "device.h"

typedef double POINT[2];
typedef double VECTOR[2];

#define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
#define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))

#define VEC_SET(x, a, b) x[0] = a, x[1] = b
#define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
#define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
#define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
#define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
#define VEC_MULT(x, a) x[0] *= a, x[1] *= a
#define VEC_DIV(x, a) x[0] /= a, x[1] /= a
#define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
#define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }

#define COUNT	200

#define WIN_X	1280
#define WIN_Y	1024

typedef struct {
  POINT x;
  VECTOR v;
  double sine, cosine;
  double vel;
} SPERM;

SPERM sperm[COUNT];

double rad_scale = 3.5;
int index = 0;

double frand()
{
  return (rand() % 16384)/16384.0;
}

init_display()
{
  ginit();
  prefsize(WIN_X, WIN_Y);
  prefposition(0, WIN_X - 1, 0, WIN_Y - 1);
  foreground();
  winopen("sperm");
  doublebuffer();
  linewidth(3);
  gconfig();
}

init_sperm()
{
  int i;
  double angle;

  for (i = 0; i < COUNT; i++)
  {
    sperm[i].x[0] = frand()*WIN_X;
    sperm[i].x[1] = frand()*WIN_Y;
    VEC_SET(sperm[i].v, 0.0, 0.0);
    angle = frand()*10.0 + 5.0;
    sperm[i].sine = sin(angle*M_PI/180.0);
    sperm[i].cosine = cos(angle*M_PI/180.0);
    sperm[i].vel = frand()*4.0 + 4.0;
  }
}

dynamics()
{
  int i;
  VECTOR w, old_w;
  POINT p;
  double r, dot, temp;
  int org_x, org_y;
  VECTOR origin, mouse;
  int x1, y1, x2, y2;
  VECTOR target;

  getorigin(&org_x, &org_y);
  VEC_SET(origin, (double) org_x, (double) org_y);
  mouse[0] = getvaluator(MOUSEX);
  mouse[1] = getvaluator(MOUSEY);
  VEC_SUB(mouse, mouse, origin);

  for (i = 0; i < COUNT; i++)
  {
    x1 = (int) sperm[i].x[0];
    y1 = (int) sperm[i].x[1];

    VEC_COPY(target, mouse);
    
    VEC_SUB(w, sperm[i].x, target);
    VEC_NORM(w);
    VEC_COPY(old_w, w);
    w[0] = old_w[0]*sperm[i].cosine - old_w[1]*sperm[i].sine;
    w[1] = old_w[1]*sperm[i].cosine + old_w[0]*sperm[i].sine;
    VEC_ADDS(p, target, rad_scale*(160.0 - sperm[i].vel*20.0), w);

    VEC_SUB(w, p, sperm[i].x);
    VEC_NORM(w);
    VEC_ADDS(sperm[i].v, sperm[i].v, 1.0, w);
 
    VEC_NORM(sperm[i].v);
    VEC_MULT(sperm[i].v, sperm[i].vel);

    VEC_ADD(sperm[i].x, sperm[i].x, sperm[i].v);

    x2 = (int) sperm[i].x[0];
    y2 = (int) sperm[i].x[1];

    color(BLACK);
    move2i(x1, y1);
    draw2i(x2, y2);
  }
}

main_loop()
{
  while (!getbutton(RIGHTMOUSE))
  {
    color(WHITE);
    clear();
    dynamics();
    swapbuffers();
  }
}

main()
{
  init_display();
  init_sperm();
  main_loop();
  gexit();
}

po0o+@andrew.cmu.edu (Paul Andrew Olbrich) (04/03/91)

Try this!

---------
/*
	xsperm.c
	Drew Olbrich, Febrary 1991

	Compile with:

	cc -o xsperm xsperm.c -lm -lX11

	Note --  This code originally served as a demonstration
	of how to do animation under X.  The "guts" of the program
	which draws the sperm are consequently located in one huge
	chunk in the update_display() routine, and can be easily
	cut out.

	-----

	This program demonstrates using X windows to poll mouse events
	(movement and button clicks) and draw moving images on the 
	screen in real time.

	To prevent flicker, new frames are created off-screen in a
	frame buffer and then copied to the window.

	The main functions are:

	init_display() - initializes the X display
	close_display()	- close the display when we're done
	update_display() - draw the next frame and display it
	event_loop() - handle mouse events

	The upper left corner of the window is coordinate (0, 0).

	This demo will probably only work on a monochrome display.
	Try clicking the buttons!  (The right button kills the
	program.)
*/

#include <stdio.h>
#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

typedef double POINT[2];
typedef double VECTOR[2];

#define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
#define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))

#define VEC_SET(x, a, b) x[0] = a, x[1] = b
#define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
#define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
#define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
#define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
#define VEC_MULT(x, a) x[0] *= a, x[1] *= a
#define VEC_DIV(x, a) x[0] /= a, x[1] /= a
#define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
#define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }

#define WIN_TITLE	"xsperm"/* title to put on the window */
#define SCR_WIDTH	640	/* the size of my window */
#define SCR_HEIGHT	480

Display *dpy;			/* a reference to my display */
Window win;			/* a reference to my window */
GC gc_normal;			/* used for drawing black on white */
GC gc_inverse;			/* used for drawing white on black */

Pixmap buffer;			/* the frame buffer */
POINT mouse;			/* current mouse coordinates */
int pressed[3] = { 0, 0, 0 };	/* which buttons are currently being pressed */

#define BUTTON_LEFT	0	/* button definitions */	
#define BUTTON_CENTER	1
#define BUTTON_RIGHT	2

/*
	error() - generic error message handler
*/

error(message)
char *message;
{
  fprintf(stderr, "xloop: %s\n", message);
  exit(-1);
}

/*
	init_display() - initialize the X display
*/

init_display()
{
  int scn;			/* a reference to the screen */
  XSetWindowAttributes attr;	/* storage for window attributes */
  XGCValues gcvalues;		/* storage for "graphics context" */
  XSizeHints hints;		/* storage for "window hints" */
  
  dpy = XOpenDisplay(NULL);	/* connect with the X window system */
  if (dpy == NULL) 
    error("Could not open display.");

  /* create the window */
  scn = DefaultScreen(dpy);
  win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
			    100, 100, SCR_WIDTH, SCR_HEIGHT, 	
			    1, BlackPixel(dpy, scn), WhitePixel(dpy, scn));

  /* set up "window hints" so that we won't be allowed to
     resize the window while it's running */
  hints.flags = PSize | PMinSize | PMaxSize;
  hints.width = hints.min_width = hints.max_width = SCR_WIDTH;
  hints.height = hints.min_height = hints.max_height = SCR_HEIGHT;
  XSetStandardProperties(dpy, win, WIN_TITLE, WIN_TITLE, None, 
			 NULL, 0, &hints);

  /* tell X which events we'd like to be aware of */
  attr.event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
  XChangeWindowAttributes(dpy, win, CWEventMask, &attr);

  /* draw the new window on the screen */
  XMapWindow(dpy, win);

  /* set up a "graphics context" for drawing black on white */
  gcvalues.foreground = BlackPixel(dpy, scn);
  gcvalues.background = WhitePixel(dpy, scn);
  gc_normal = XCreateGC(dpy, win, GCForeground | GCBackground, &gcvalues);

  /* set up a "graphics context" for drawing white on black */
  gcvalues.foreground = WhitePixel(dpy, scn);
  gcvalues.background = BlackPixel(dpy, scn);
  gc_inverse = XCreateGC(dpy, win, GCForeground | GCBackground, &gcvalues);

  /* create the frame buffer */
  buffer = XCreatePixmap(dpy, win, SCR_WIDTH, SCR_HEIGHT, 
			 DefaultDepth(dpy, scn));
  if (buffer == NULL)
    error("Can't create frame buffer.");
}

/*
	close_display() - tell the X server that we're done
*/
				
close_display()
{
  XFreePixmap(dpy, buffer);	/* free the memory we used first */
  XDestroyWindow(dpy, win);	/* get rid of the window */
  XCloseDisplay(dpy);
}

/*
	dummy_predicate() - dummy event predicate

	This function is used by 'XCheckIfEvent()' below to determine
	which events we want to handle.  However, we always want to 
	handle every event, so we will always return 1 (true).
*/

int dummy_predicate(display, event, arg)
Display *display;
XEvent *event;
char *arg;
{
  return 1;
}

/*
	update_display() - draw the next frame

	The idea here is to clear the frame buffer, draw lines on it,
	and then copy it to the screen.

	If instead, we just cleared the screen and drew the lines there,
	we'd get a lot of flicker.  That would look pretty bad.
*/

update_display()
{
  /* clear the frame buffer by drawing a white rectangle */
  XFillRectangle(dpy, buffer, gc_inverse, 0, 0, SCR_WIDTH, SCR_HEIGHT);

  /* draw stuff in the frame buffer  */


/***********************************************************
    This is just an icky hacked up example routine for
    the sake of demonstration.  You will want to delete
    everything from here down to the next big comment.
    However, note the use of 'XDrawLine()' down near
    the bottom.
***********************************************************/

  {
#define COUNT	300
    int i, dx, dy;
    static int init = 0;
    static POINT x[COUNT];
    static VECTOR v[COUNT];
    static double sine[COUNT];
    static double cosine[COUNT];
    static double vel[COUNT];
    int x1, y1, x2, y2;
    static double rad = 1.0;
    static double dir = 1.0;

    if (!init)
    {	
      init = 1;
      for (i = 0; i < COUNT; i++)
      {
	double angle;

	x[i][0] = (double) (random() % SCR_WIDTH);
	x[i][1] = (double) (random() % SCR_HEIGHT);
	v[i][0] = (double) (random() % 2000)/1000.0 - 1.0;
	v[i][1] = (double) (random() % 2000)/1000.0 - 1.0;	
	angle = (random() % 1000)/1000.0*10.0 + 5.0;
	sine[i] = sin(angle*M_PI/180.0);
	cosine[i] = cos(angle*M_PI/180.0);
	vel[i] = (random() % 1000)/1000.0*4.0 + 4.0;
	VEC_NORM(v[i]);
      }
    }      

    if (pressed[BUTTON_LEFT])
    {
      rad = rad == 1.0 ? 3.0 : 1.0;
      pressed[BUTTON_LEFT] = 0;
    }
    if (pressed[BUTTON_CENTER])
    {
      VECTOR y;
      dir *= -1.0;
      pressed[BUTTON_CENTER] = 0;
      for (i = 0; i < COUNT; i++)
      {
	VEC_COPY(y, v[i]);
	if (dir == -1.0)
	{
	  v[i][0] = y[1];
	  v[i][1] = -y[0];
	}
	else
	{
	  v[i][0] = -y[1];
	  v[i][1] = y[0];
	}
      }
    }

    for (i = 0; i < COUNT; i++)
    {
      x1 = (int) x[i][0];	/* old location */
      y1 = (int) x[i][1];

      {
	VECTOR w, y;
	POINT p;
	double r, dot;
	double temp;
	
	VEC_SUB(w, x[i], mouse);
	VEC_NORM(w);
	VEC_COPY(y, w);
	w[0] = y[0]*cosine[i] - dir*y[1]*sine[i];
	w[1] = y[1]*cosine[i] + dir*y[0]*sine[i];
	VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w);

	VEC_SUB(w, p, x[i]);
	r = VEC_LEN(w);
	VEC_DIV(w, r);

	VEC_ADDS(v[i], v[i], 1.0, w);
      }

      VEC_NORM(v[i]);
      VEC_MULT(v[i], vel[i]);

      VEC_ADD(x[i], x[i], v[i]);

      x2 = (int) x[i][0];	/* new location */
      y2 = (int) x[i][1];

      XDrawLine(dpy, buffer, gc_normal, x1, y1, x2, y2);
    }
  }

/***********************************************************
               End of icky example routine!
***********************************************************/

  /* copy the finished frame buffer to the window */
  XCopyArea(dpy, buffer, win, gc_normal, 0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0);
}

/*
	event_loop() - handle mouse events 
*/

event_loop()
{
  int done = 0;
  XEvent event;

  while (!done)
  {
    update_display();		/* draw the next frame */
    XSync(dpy, False);		/* keep everything synchronized */

    /* handle all events waiting in the event queue */
    while (XCheckIfEvent(dpy, &event, dummy_predicate, ""))
      switch (event.type) {
        case MotionNotify :	/* the mouse moved */
	  mouse[0] = event.xmotion.x;
	  mouse[1] = event.xmotion.y;
	  break;
	case ButtonPress :	/* a button was pressed down */
	  pressed[event.xbutton.button - 1] = 1;
	  break;
	case ButtonRelease :	/* a button was released */
	  pressed[event.xbutton.button - 1] = 0;
	  break;
      }

    if (pressed[BUTTON_RIGHT])	/* stop if the right button is pressed */
      done = 1;
  }
}

/*
	main()
*/

main()
{
  init_display();
  event_loop();
  close_display();
}

will@rins.ryukoku.ac.jp (will) (04/03/91)

	Compiled and ran with no prob. on my sparc, sun4, and mips computers.
	w/color.


							will.....