[comp.sys.sgi] Fun dynamic sketching program for IRISes.

paul@manray.asd.sgi.com (Paul Haeberli) (02/12/91)

Here's a little program that uses a very simple dynamics model
to draw caligraphic strokes. Please give it a try if you have
an IRIS workstation.

paul haeberli
paul@sgi.com
415-962-3665

/* 
 *	dynadraw -
 *		Use a simple dynamics model to create caligraphic
 *	strokes.
 *
 *				Paul Haeberli - 1989
 *	to compile on an IRIS:
 *		cc -O dynadraw.c -o dynadraw -lgl_s -lm
 *
 *	leftmouse - used for drawing
 *	middlemouse - clears page
 *	rightmouse - menu.
 *
 *	uparrow - wider strokes
 *	downarrow - narrower strokes
 *		
 */
#include "gl.h"
#include "device.h"
#include "math.h"
#include "stdio.h"
#include "sys/types.h"
#include "sys/times.h"
#include "sys/param.h"

#define SLIDERHIGH	(15)
#define SLIDERLEFT	(200)
#define TIMESLICE	(0.005)
#define MAXPOLYS	(20000)
#define SHRINK

typedef struct filter {
     float curx, cury;
     float velx, vely, vel;
     float accx, accy, acc;
     float angx, angy;
     float mass, drag;
     float lastx, lasty;
     int fixedangle;
} filter;

float initwidth = 1.5;
float width, shrink;
float odelx, odely;
float curmass, curdrag;
float polyverts[4*2*MAXPOLYS];
int npolys;
long xsize, ysize;
long xorg, yorg;
filter mouse;

float flerp();
float fgetmousex();
float fgetmousey();
float gettime();
unsigned long getltime();

filtersetpos(f,x,y)
filter *f;
float x, y;
{
    f->curx = x;
    f->cury = y;
    f->lastx = x;
    f->lasty = y;
    f->velx = 0.0;
    f->vely = 0.0;
    f->accx = 0.0;
    f->accy = 0.0;
}

filterapply(f,mx,my)
filter *f;
float mx, my;
{
    float mass, drag;
    float fx, fy, force;

/* calculate mass and drag */
    mass = flerp(1.0,160.0,curmass);
    drag = flerp(0.00,0.5,curdrag*curdrag);

/* calculate force and acceleration */
    fx = mx-f->curx;
    fy = my-f->cury;
    f->acc = sqrt(fx*fx+fy*fy);
    if(f->acc<0.000001)
	return 0;
    f->accx = fx/mass;
    f->accy = fy/mass;

/* calculate new velocity */
    f->velx += f->accx;
    f->vely += f->accy;
    f->vel = sqrt(f->velx*f->velx+f->vely*f->vely);
    f->angx = -f->vely;
    f->angy = f->velx;
    if(f->vel<0.000001) 
 	return 0;

/* calculate angle of drawing tool */
    f->angx /= f->vel;
    f->angy /= f->vel;
    if(f->fixedangle) {
	f->angx = 0.6;
	f->angy = 0.2;
    }

/* apply drag */
    f->velx = f->velx*(1.0-drag);
    f->vely = f->vely*(1.0-drag);

/* update position */
    f->lastx = f->curx;
    f->lasty = f->cury;
    f->curx = f->curx+f->velx;
    f->cury = f->cury+f->vely;
    return 1;
}

float paramval()
{
    float p;

    p = (float)(getmousex()-SLIDERLEFT)/(xsize-SLIDERLEFT);
    if(p<0.0)
	return 0.0;
    if(p>1.0)
	return 1.0;
    return p;
}

main()
{
    short val;
    int menu, pres;
    float p, mx, my;

    curmass = 0.5;
    curdrag = 0.15;
    prefsize(1000,800);
    initbuzz();
    winopen("dynadraw");
    glcompat(GLC_OLDPOLYGON,1);
    subpixel(1);
    RGBmode();
    gconfig();
    qdevice(LEFTMOUSE);
    qdevice(MIDDLEMOUSE);
    qdevice(MENUBUTTON);
    qdevice(RIGHTSHIFTKEY); 
    qdevice(UPARROWKEY); 
    qdevice(DOWNARROWKEY); 
    qdevice(RIGHTSHIFTKEY); 
    makeframe();
    menu = defpup("calligraphy %t|toggle line style|save ps|save image");
    width = initwidth;
    mouse.fixedangle = 1;
    while(1) {
	switch(qread(&val)) {
	    case REDRAW:
		makeframe();
		break;
	    case MIDDLEMOUSE:
		if(val) 
		    clearscreen();
		break;
	    case UPARROWKEY:
		if(val) 
		    initwidth *= 1.414213;
		width = initwidth;
		break;
	    case DOWNARROWKEY:
		if(val) 
		    initwidth /= 1.414213;
		width = initwidth;
		break;
	    case MENUBUTTON:
		if(val) {
		    switch(dopup(menu)) {
			case 1: 
			    mouse.fixedangle = 1-mouse.fixedangle;
			    break;
			case 2: 
			    savepolys();
			    break;
			case 3: 
			    savewindow("drag.rgb");
			    break;
		    }
		}
		break;
	    case LEFTMOUSE:
		if(val) {
		    my = getmousey();
		    if(my>0*SLIDERHIGH && my<2*SLIDERHIGH) {
			if(my>SLIDERHIGH) {
			    while(getbutton(LEFTMOUSE)) {
				p = paramval();
				if(p != curmass) {
				    curmass = p;
				    showsettings();
				}
			    }
			} else {
			    while(getbutton(LEFTMOUSE)) {
				p = paramval();
				if(p != curdrag) {
				    curdrag = p;
				    showsettings();
				}
			    }
			}
		    } else {
			mx = 1.25*fgetmousex();
			my = fgetmousey();
			filtersetpos(&mouse,mx,my);
			odelx = 0.0;
			odely = 0.0;
			shrink = 1.0;
			while(getbutton(LEFTMOUSE)) {
			    mx = 1.25*fgetmousex();
			    my = fgetmousey();
			    if(filterapply(&mouse,mx,my)) {
				drawsegment(&mouse);
				RGBcolor(0,0,0);
				buzz();
				if(getbutton(RIGHTSHIFTKEY)) 
				    shrink = shrink*0.98;
			    }
			}
		    }
		}
		break;
	}
    }
}

makeframe()
{
    reshapeviewport();
    getsize(&xsize,&ysize);
    getorigin(&xorg,&yorg);
    clearscreen();
}

clearscreen()
{
    int x, y;

    ortho2(0.0,1.25,0.0,1.0);
    RGBcolor(200,200,200);
    setpattern(0);
    clear();
    npolys = 0;
    showsettings();
    RGBcolor(0,0,0);
}

showsettings()
{
    char str[256];
    int xpos;

    ortho2(-0.5,xsize-0.5,-0.5,ysize-0.5);
    RGBcolor(200,200,200);
    rectfi(0,0,xsize,2*SLIDERHIGH);
    RGBcolor(0,0,0);
    sprintf(str,"Mass %g",curmass); 
    cmov2i(20,3+1*SLIDERHIGH);
    charstr(str);
    sprintf(str,"Drag %g",curdrag); 
    cmov2i(20,3+0*SLIDERHIGH);
    charstr(str);
    move2i(SLIDERLEFT,0);
    draw2i(SLIDERLEFT,2*SLIDERHIGH);
    move2i(0,1*SLIDERHIGH);
    draw2i(xsize,1*SLIDERHIGH);
    move2i(0,2*SLIDERHIGH);
    draw2i(xsize,2*SLIDERHIGH);
    RGBcolor(255,0,0);
    xpos = SLIDERLEFT+curmass*(xsize-SLIDERLEFT);
    rectfi(xpos,1*SLIDERHIGH,xpos+4,2*SLIDERHIGH);
    xpos = SLIDERLEFT+curdrag*(xsize-SLIDERLEFT);
    rectfi(xpos,0*SLIDERHIGH,xpos+4,1*SLIDERHIGH);
    ortho2(0.0,1.25,0.0,1.0);
}

drawsegment(f)
filter *f;
{
    float delx, dely; 
    float wid, *fptr;
    float px, py, nx, ny;

    wid = 0.04-f->vel;
    wid = wid*width;
    if(wid<0.00001)
    	wid = 0.00001;
    wid *= shrink;
    delx = f->angx*wid;
    dely = f->angy*wid;

    RGBcolor(0,0,0);
    px = f->lastx;
    py = f->lasty;
    nx = f->curx;
    ny = f->cury;

    fptr = polyverts+8*npolys;
    bgnpolygon();
    fptr[0] = px+odelx;
    fptr[1] = py+odely;
    v2f(fptr);
    fptr += 2;
    fptr[0] = px-odelx;
    fptr[1] = py-odely;
    v2f(fptr);
    fptr += 2;
    fptr[0] = nx-delx;
    fptr[1] = ny-dely;
    v2f(fptr);
    fptr += 2;
    fptr[0] = nx+delx;
    fptr[1] = ny+dely;
    v2f(fptr);
    fptr += 2;
    endpolygon();
    npolys++;
    if(npolys>=MAXPOLYS) {
	printf("out of polys - increase the define MAXPOLYS\n");
	exit(1);
    }
#define DOLINES
#ifdef DOLINES
    fptr -= 8;
    bgnclosedline();
    v2f(fptr);
    fptr += 2;
    v2f(fptr);
    fptr += 2;
    v2f(fptr);
    fptr += 2;
    v2f(fptr);
    fptr += 2;
    endclosedline();
#endif
    odelx = delx;
    odely = dely;
}

int buzztemp, buzzmax;

buzz()
{
    int i;

    for(i=0; i<buzzmax; i++) 
	buzztemp++;
}

initbuzz()
{
    long t0, t1;

    buzzmax = 1000000;
    sginap(10);
    t0 = getltime();
    buzz();
    t1 = getltime();
    buzzmax = TIMESLICE*(100.0*1000000.0)/(t1-t0);
}

savepolys()
{
    FILE *of;
    int i;
    float *fptr;

    of = fopen("/tmp/drag.ps","w");
    fprintf(of,"%%!\n");
    fprintf(of,"%f %f translate\n",0.25*72,(11.0-0.75)*72);
    fprintf(of,"-90 rotate\n");
    fprintf(of,"%f %f scale\n",8.0*72,8.0*72);
    fprintf(of,"0.0 setgray\n");
    printf("npolys %d\n",npolys);
    fptr = polyverts;
    for(i=0; i<npolys; i++) {
	fprintf(of,"newpath\n");
	fprintf(of,"%f %f moveto\n",fptr[0],fptr[1]);
	fptr+=2;
	fprintf(of,"%f %f lineto\n",fptr[0],fptr[1]);
	fptr+=2;
	fprintf(of,"%f %f lineto\n",fptr[0],fptr[1]);
	fptr+=2;
	fprintf(of,"%f %f lineto\n",fptr[0],fptr[1]);
	fptr+=2;
	fprintf(of,"closepath\n");
	fprintf(of,"fill\n");
    }
    fprintf(of,"showpage\n");
    fclose(of);
    fprintf(stderr,"PostScript saved to /tmp/drag.ps\n");
}

/*
 *	winlib routines follow
 *
 */
float fgetmousex()
{
    return ((float)getvaluator(MOUSEX)-xorg)/(float)xsize;
}

float fgetmousey()
{
    return ((float)getvaluator(MOUSEY)-yorg)/(float)ysize;
}

int getmousex()
{
    return getvaluator(MOUSEX)-xorg;
}

int getmousey()
{
    return getvaluator(MOUSEY)-yorg;
}

float flerp(f0,f1,p)
float f0, f1, p; 
{
    return ((f0*(1.0-p))+(f1*p));
}

savewindow(name)
char *name;
{
    char cmd[256];
    long xorg, yorg;
    long xsize, ysize;

    getorigin(&xorg,&yorg);
    getsize(&xsize,&ysize);
    sprintf(cmd,"scrsave %s %d %d %d %d\n",name,xorg,xorg+xsize-1,yorg,yorg+ysize-1);
    system(cmd);
}

unsigned long getltime()
{
    struct tms ct;

    return times(&ct);
}

pdc@sgi.com (Paul Close) (02/14/91)

In article <85040@sgi.sgi.com> paul@manray.asd.sgi.com (Paul Haeberli) writes:
>Here's a little program that uses a very simple dynamics model
>to draw caligraphic strokes. Please give it a try if you have
>an IRIS workstation.

This is indeed a *highly cool* program!  If however, you have 8-bit graphics
on your PI, you'll want to apply the following unoffical patch (the line
numbers might be off because I didn't strip the headers):

*** dynadraw.c.old	Wed Feb 13 09:38:06 1991
--- dynadraw.c	Wed Feb 13 09:39:56 1991
***************
*** 164,170 ****
      winopen("dynadraw");
      glcompat(GLC_OLDPOLYGON,1);
      subpixel(1);
!     RGBmode();
      gconfig();
      qdevice(LEFTMOUSE);
      qdevice(MIDDLEMOUSE);
--- 164,170 ----
      winopen("dynadraw");
      glcompat(GLC_OLDPOLYGON,1);
      subpixel(1);
!     shademodel(FLAT);
      gconfig();
      qdevice(LEFTMOUSE);
      qdevice(MIDDLEMOUSE);
***************
*** 244,250 ****
  			    my = fgetmousey();
  			    if(filterapply(&mouse,mx,my)) {
  				drawsegment(&mouse);
! 				RGBcolor(0,0,0);
  				buzz();
  				if(getbutton(RIGHTSHIFTKEY)) 
  				    shrink = shrink*0.98;
--- 244,250 ----
  			    my = fgetmousey();
  			    if(filterapply(&mouse,mx,my)) {
  				drawsegment(&mouse);
! 				color(0);
  				buzz();
  				if(getbutton(RIGHTSHIFTKEY)) 
  				    shrink = shrink*0.98;
***************
*** 270,281 ****
      int x, y;
  
      ortho2(0.0,1.25,0.0,1.0);
!     RGBcolor(200,200,200);
      setpattern(0);
      clear();
      npolys = 0;
      showsettings();
!     RGBcolor(0,0,0);
  }
  
  showsettings()
--- 270,281 ----
      int x, y;
  
      ortho2(0.0,1.25,0.0,1.0);
!     color(51);
      setpattern(0);
      clear();
      npolys = 0;
      showsettings();
!     color(0);
  }
  
  showsettings()
***************
*** 284,292 ****
      int xpos;
  
      ortho2(-0.5,xsize-0.5,-0.5,ysize-0.5);
!     RGBcolor(200,200,200);
      rectfi(0,0,xsize,2*SLIDERHIGH);
!     RGBcolor(0,0,0);
      sprintf(str,"Mass %g",curmass); 
      cmov2i(20,3+1*SLIDERHIGH);
      charstr(str);
--- 284,292 ----
      int xpos;
  
      ortho2(-0.5,xsize-0.5,-0.5,ysize-0.5);
!     color(51);
      rectfi(0,0,xsize,2*SLIDERHIGH);
!     color(0);
      sprintf(str,"Mass %g",curmass); 
      cmov2i(20,3+1*SLIDERHIGH);
      charstr(str);
***************
*** 299,305 ****
      draw2i(xsize,1*SLIDERHIGH);
      move2i(0,2*SLIDERHIGH);
      draw2i(xsize,2*SLIDERHIGH);
!     RGBcolor(255,0,0);
      xpos = SLIDERLEFT+curmass*(xsize-SLIDERLEFT);
      rectfi(xpos,1*SLIDERHIGH,xpos+4,2*SLIDERHIGH);
      xpos = SLIDERLEFT+curdrag*(xsize-SLIDERLEFT);
--- 299,305 ----
      draw2i(xsize,1*SLIDERHIGH);
      move2i(0,2*SLIDERHIGH);
      draw2i(xsize,2*SLIDERHIGH);
!     color(1);
      xpos = SLIDERLEFT+curmass*(xsize-SLIDERLEFT);
      rectfi(xpos,1*SLIDERHIGH,xpos+4,2*SLIDERHIGH);
      xpos = SLIDERLEFT+curdrag*(xsize-SLIDERLEFT);
***************
*** 322,328 ****
      delx = f->angx*wid;
      dely = f->angy*wid;
  
!     RGBcolor(0,0,0);
      px = f->lastx;
      py = f->lasty;
      nx = f->curx;
--- 322,328 ----
      delx = f->angx*wid;
      dely = f->angy*wid;
  
!     color(0);
      px = f->lastx;
      py = f->lasty;
      nx = f->curx;
--
Paul Close	     pdc@sgi.com	   ...!{ames, decwrl, ucbvax}!sgi!pdc

	It is pitch dark.  You are likely to be eaten by a grue.

naughton@wind.Eng.Sun.COM (Patrick Naughton) (02/17/91)

In article <85040@sgi.sgi.com>, paul@manray.asd.sgi.com (Paul Haeberli) writes:
|> Here's a little program that uses a very simple dynamics model
|> to draw caligraphic strokes. Please give it a try if you have
|> an IRIS workstation.
|> 
|> paul haeberli
|> paul@sgi.com
|> 415-962-3665
|>
|> ... dynadraw.c deleted ...

Here's another set of X11 compatibility routines for us Paul Haeberli
fans without IRISes...

compile it like this:

	cc -O dynadraw.c glx.c -o dynadraw -lm -lX


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	glx.c
#	gl.h
#	device.h
# This archive created: Sat Feb 16 23:30:16 1991
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'glx.c'
then
	echo shar: "will not over-write existing file 'glx.c'"
else
cat << \SHAR_EOF > 'glx.c'
#ident "@(#)glx.c	1.1 91/02/16 GL"
/*-
 * glx.c - simple replacements for SGI GL functions for X11.
 *
 * Copyright (c) 1991 by Patrick J. Naughton
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind. The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof. In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Comments and additions should be sent to the author:
 *
 * Patrick J. Naughton
 * Sun Microsystems
 * 2550 Garcia Ave, MS 10-20
 * Mountain View, CA 94043
 * (415) 336-1080
 *
 */

#include <stdio.h>
#include <sys/times.h>
#include <string.h>
#include <strings.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/bitmaps/gray1>
#include "gl.h"

#define POLYBATCH 40
#define FONTNAME "bembo-bold-24"

static Display *dsp;
static int  screen;
static Colormap cmap;
static GC   gc;
static Window win;
static Window root;
static int  winw;
static int  winh;
static int  cpx;
static int  cpy;
static XPoint *poly = 0;
static int  curpoly;
static int  npoly;
static XFontStruct *textfont;
static XFontStruct *font;
static XCharStruct fontextent;
static int  fontascent;
static int  fontdescent;
static int  fontheight;
static int  menux;
static int  menuy;
static long black;
static long white;
static float xoff;
static float xscale;
static float yoff;
static float yscale;
static Pixmap stipple;
static int  mousex;
static int  mousey;

void
prefsize(w, h)
{
    winw = w;
    winh = h;
}

void
winopen(name)
    char       *name;
{
    XSetWindowAttributes xswa;
    XWMHints    xwmh;
    int         tmp;
    int         mask;

    dsp = XOpenDisplay(0);
    screen = DefaultScreen(dsp);
    cmap = DefaultColormap(dsp, screen);
    gc = DefaultGC(dsp, screen);
    root = RootWindow(dsp, screen);
    black = BlackPixel(dsp, screen);
    white = WhitePixel(dsp, screen);
    stipple = XCreatePixmapFromBitmapData(dsp, root,
				       gray1_bits, gray1_width, gray1_height,
					  black, white, 1);
    XSetStipple(dsp, gc, stipple);
    font = XLoadQueryFont(dsp, "fixed");
    textfont = XLoadQueryFont(dsp, FONTNAME);
    if (!textfont)
	textfont = font;
    XSetFont(dsp, gc, textfont->fid);
    XQueryTextExtents(dsp, textfont->fid, "Q", 1,
		      &tmp, &fontascent, &fontdescent, &fontextent);
    fontheight = fontascent + fontdescent + 1;

    xswa.event_mask = ExposureMask | KeyPressMask
	| ButtonPressMask | Button2MotionMask;
    mask = CWEventMask;
    win = XCreateWindow(dsp, root, 0, 0, winw, winh, 0,
			CopyFromParent, InputOutput, CopyFromParent,
			mask, &xswa);
    xwmh.flags = InputHint;
    xwmh.input = True;
    XChangeProperty(dsp, win, XA_WM_HINTS, XA_WM_HINTS, 32,
       PropModeReplace, (unsigned char *) &xwmh, sizeof(xwmh) / sizeof(int));
    XStoreName(dsp, win, name);
    XMapWindow(dsp, win);
    while (1) {
	XEvent      ev;
	XNextEvent(dsp, &ev);
	if (ev.type == Expose)
	    break;
    }
}

/* this is a hack to make Sun's times return the time value just like SYSV */

#undef times
long
mytimes(ct)
    struct tms *ct;
{
    times(ct);
    return ct->tms_utime;
}

void
RGBcolor(r, g, b)
{
    XColor      color;

    color.pixel = 0;
    color.red = r << 8;
    color.green = g << 8;
    color.blue = b << 8;
    color.flags = DoRed | DoGreen | DoBlue;
    XAllocColor(dsp, cmap, &color);
    XSetForeground(dsp, gc, color.pixel);
}

void
clear()
{
    XFillRectangle(dsp, win, gc, 0, 0, winw, winh);
}

void
pnt2i(x, y)
    int         x;
    int         y;
{
    XDrawPoint(dsp, win, gc, x, winh - y);
}

int
getvaluator(which)
    int         which;
{
    switch (which) {
    case MOUSEX:
	return mousex;
    case MOUSEY:
	return winh - mousey;
    default:
	return 0;
    }
}

int
getbutton(which)
    int         which;
{
    Window      qroot;
    Window      qwin;
    int         rootx;
    int         rooty;
    int         winx;
    int         winy;
    static int  mask;

    XQueryPointer(dsp, win, &qroot, &qwin, &rootx, &rooty, &winx, &winy, &mask);
    switch (which) {
    case LEFTMOUSE:
	mousex = winx;
	mousey = winy;
	return (mask & Button1Mask);
    case RIGHTSHIFTKEY:
	return (mask & ShiftMask);
    default:
	return 0;
    }
}

int
qread(val)
    short      *val;
{
    XEvent      ev;
    *val = 1;
    XNextEvent(dsp, &ev);
    switch (ev.type) {
    case Expose:
	if (((XExposeEvent *) & ev)->window == win)
	    return REDRAW;
	break;
    case KeyPress:
	{
	    XKeyEvent  *xke = (XKeyEvent *) & ev;
	    KeySym      keysym = XLookupKeysym(xke, 0);
	    switch (keysym) {
	    case XK_Up:
		return UPARROWKEY;
	    case XK_Down:
		return DOWNARROWKEY;
	    default:
		break;
	    }
	}
	break;
    case ButtonPress:
	{
	    XButtonEvent *xbe = (XButtonEvent *) & ev;
	    switch (xbe->button) {
	    case Button1:
		mousex = xbe->x;
		mousey = xbe->y;
		return LEFTMOUSE;
	    case Button2:
		return MIDDLEMOUSE;
	    case Button3:
		menux = xbe->x_root;
		menuy = xbe->y_root;
		return MENUBUTTON;
	    default:
		break;
	    }
	}
	break;
    case MotionNotify:
	break;
    }
}

void
getorigin(xo, yo)
    int        *xo;
    int        *yo;
{
    *xo = 0;
    *yo = 0;
}

void
getsize(xs, ys)
    int        *xs;
    int        *ys;
{
    *xs = winw;
    *ys = winh;
}

void
move2i(x, y)
    int         x,
                y;
{
    cpx = x;
    cpy = y;
}

/* NB: what's the difference between cmov2i and move2i??? */
void
cmov2i(x, y)
    int         x,
                y;
{
    cpx = x;
    cpy = y;
}

void
draw2i(x, y)
    int         x,
                y;
{
    XDrawLine(dsp, win, gc, cpx, winh - cpy, x, winh - y);
    cpx = x;
    cpy = y;
}

void
rectfi(x1, y1, x2, y2)
    int         x1;
    int         y1;
    int         x2;
    int         y2;
{
    XFillRectangle(dsp, win, gc, x1, winh - y2, x2 - x1, y2 - y1);
}

void
charstr(str)
    char       *str;
{
    XSetFont(dsp, gc, font->fid);
    XDrawString(dsp, win, gc, cpx, winh - cpy, str, strlen(str));
    XSetFont(dsp, gc, textfont->fid);
}

void
sginap(len)
    int         len;
{
    usleep(len);
}

void
bgnpolygon()
{
    if (poly == 0) {
	poly = (XPoint *) malloc(POLYBATCH * sizeof(XPoint));
	npoly = POLYBATCH;
    }
    curpoly = 0;
}

static void
addpoint(x, y)
    int         x;
    int         y;
{
    if (curpoly >= npoly) {
	npoly += POLYBATCH;
	poly = (XPoint *) realloc(poly, npoly * sizeof(XPoint));
    }
    poly[curpoly].x = x;
    poly[curpoly].y = y;
    curpoly++;
}

void
v2f(p)
    float      *p;
{
    addpoint((int) ((xoff + p[0]) * xscale), winh - (int) ((yoff + p[1]) * yscale));
}

void
endpolygon()
{
    XFillPolygon(dsp, win, gc, poly, curpoly, Nonconvex, CoordModeOrigin);
}

void
bgnclosedline()
{
    if (poly == 0) {
	poly = (XPoint *) malloc(POLYBATCH * sizeof(XPoint));
	npoly = POLYBATCH;
    }
    curpoly = 0;
}

void
endclosedline()
{
    addpoint(poly[0].x, poly[0].y);
    XDrawLines(dsp, win, gc, poly, curpoly, CoordModeOrigin);
}

void
ortho2(xmin, xmax, ymin, ymax)
    float       xmin;
    float       xmax;
    float       ymin;
    float       ymax;
{
    xoff = xmin;
    xscale = winw / (xmax - xmin);
    yoff = ymin;
    yscale = winh / (ymax - ymin);
}


/*
 * these routines are a cheesy ten minute hack to do menus with GL's API.
 */

#define MAXMENUS 10
#define MAXMENUITEMS 10
typedef struct {
    char       *title;
    int         titlewidth;
    int         nitems;
    char       *item[MAXMENUITEMS];
    Window      win;
    int         w;
    int         h;
}           MenuStruct;
MenuStruct  menu[10];
int         nummenus = 0;

#define MENUPADW 20
#define MENUPADH 4
#define MENUPADTITLE 8
#define SHADE 16

int
defpup(s)
    char       *s;
{
    int         n = nummenus++;
    char       *val = strtok(s, "|");
    int         w;
    int         mask;
    int         maxw;
    XSetWindowAttributes xswa;
    char       *p;

    if (nummenus >= MAXMENUS) {
	fprintf(stderr, "too many menus\n");
	exit(1);
    }
    p = rindex(val, '%');
    *p = 0;			/* nuke the %t */
    menu[n].title = strdup(val);
    menu[n].titlewidth = maxw = XTextWidth(textfont, val, strlen(val));
    while (val = strtok((char *) 0, "|")) {
	menu[n].item[menu[n].nitems++] = strdup(val);
	w = XTextWidth(textfont, val, strlen(val));
	if (w > maxw)
	    maxw = w;
	if (menu[n].nitems >= MAXMENUITEMS) {
	    fprintf(stderr, "too many menu items\n");
	    exit(1);
	}
    }
    menu[n].w = maxw + 2 * MENUPADW;
    menu[n].h = (menu[n].nitems + 1) * (fontheight + MENUPADH) + MENUPADTITLE;
    xswa.background_pixmap = None;
    xswa.save_under = True;
    xswa.event_mask = ExposureMask;
    xswa.override_redirect = True;
    mask = CWBackPixmap | CWEventMask | CWSaveUnder | CWOverrideRedirect;
    menu[n].win = XCreateWindow(dsp, root,
				0, 0, menu[n].w + SHADE, menu[n].h + SHADE, 0,
				CopyFromParent, InputOutput, CopyFromParent,
				mask, &xswa);
    return n;
}


static void
displayitem(menu, i, fg, bg)
    MenuStruct *menu;
    int         i;
    long        fg;
    long        bg;
{
    XSetForeground(dsp, gc, bg);
    XFillRectangle(dsp, menu->win, gc,
		   1, MENUPADTITLE + (fontheight + MENUPADH) * i,
		   menu->w - 1, (fontheight + MENUPADH));
    XSetForeground(dsp, gc, fg);
    XDrawString(dsp, menu->win, gc,
	   MENUPADW, MENUPADTITLE + (fontheight + MENUPADH) * i + fontascent,
		menu->item[i - 1], strlen(menu->item[i - 1]));
}

void
XDrawOString(d, w, g, x, y, s, n)
{
    XSetForeground(dsp, gc, black);
    XDrawString(d, w, g, x - 1, y - 1, s, n);
    XDrawString(d, w, g, x, y - 1, s, n);
    XDrawString(d, w, g, x + 1, y - 1, s, n);
    XDrawString(d, w, g, x - 1, y, s, n);
    XDrawString(d, w, g, x + 1, y, s, n);
    XDrawString(d, w, g, x - 1, y + 1, s, n);
    XDrawString(d, w, g, x, y + 1, s, n);
    XDrawString(d, w, g, x + 1, y + 1, s, n);
    XSetForeground(dsp, gc, white);
    XDrawString(d, w, g, x, y, s, n);
    XSetForeground(dsp, gc, black);
}

int
dopup(n)
    int         n;
{
    int         menuval = 0;
    int         oldmenuval = 0;
    int         i;
    XEvent      ev;
    Window      r;
    Window      w;
    int         rx;
    int         ry;
    int         wx;
    int         wy;
    int         mask;

    XMoveWindow(dsp, menu[n].win,
		menux - 2, menuy - fontheight - MENUPADH - MENUPADTITLE / 2);
    XMapRaised(dsp, menu[n].win);

    do {
	XNextEvent(dsp, &ev);
    } while (ev.type != Expose);

    XSetForeground(dsp, gc, white);
    XFillRectangle(dsp, menu[n].win, gc, 0, 0, menu[n].w, menu[n].h);
    XSetForeground(dsp, gc, black);
    XDrawRectangle(dsp, menu[n].win, gc, 0, 0, menu[n].w, menu[n].h);
    XSetFillStyle(dsp, gc, FillStippled);
    XFillRectangle(dsp, menu[n].win, gc, menu[n].w, SHADE, SHADE, menu[n].h);
    XFillRectangle(dsp, menu[n].win, gc, SHADE, menu[n].h, menu[n].w - SHADE, SHADE);
    XSetFillStyle(dsp, gc, FillSolid);
    XDrawOString(dsp, menu[n].win, gc,
		 (menu[n].w - menu[n].titlewidth) / 2, MENUPADH + fontascent,
		 menu[n].title, strlen(menu[n].title));
    XDrawRectangle(dsp, menu[n].win, gc, 2, 2,
		menu[n].w - 4, MENUPADH + fontheight + MENUPADTITLE / 2 - 4);
    XDrawLine(dsp, menu[n].win, gc, 0, MENUPADH + fontheight + MENUPADTITLE / 2,
	      menu[n].w, MENUPADH + fontheight + MENUPADTITLE / 2);
    for (i = 1; i <= menu[n].nitems; i++)
	displayitem(&menu[n], i, black, white);
    do {
	XQueryPointer(dsp, menu[n].win, &r, &w, &rx, &ry, &wx, &wy, &mask);

	if (wx > 0 && wx < menu[n].w &&
		wy > MENUPADTITLE + fontheight + MENUPADH &&
	wy < MENUPADTITLE + (fontheight + MENUPADH) * (1 + menu[n].nitems)) {
	    menuval = (wy - MENUPADTITLE) / (fontheight + MENUPADH);

	    if (oldmenuval != menuval) {
		if (oldmenuval)
		    displayitem(&menu[n], oldmenuval, black, white);
		displayitem(&menu[n], menuval, white, black);
		oldmenuval = menuval;
	    }
	} else if (oldmenuval) {
	    displayitem(&menu[n], oldmenuval, black, white);
	    oldmenuval = menuval = 0;
	}
    } while (mask & Button3Mask);
    XUnmapWindow(dsp, menu[n].win);
    return menuval;
}

void
RGBmode()
{
/* NOP */
}

void
gconfig()
{
/* NOP */
}

void
glcompat()
{
/* NOP */
}

void
subpixel()
{
/* NOP */
}

void
setpattern()
{
/* NOP */
}

void
reshapeviewport()
{
/* NOP */
}

void
qdevice(mask)
    int         mask;
{
/* NOP */
}
SHAR_EOF
fi
if test -f 'gl.h'
then
	echo shar: "will not over-write existing file 'gl.h'"
else
cat << \SHAR_EOF > 'gl.h'
#ident "@(#)gl.h	1.1 91/02/16 GL"
/*
 * gl.h - the beginnings of the defines to keep SGI GL programs happy.
 */

extern long mytimes();

#define REDRAW 0
#define UPARROWKEY 100
#define DOWNARROWKEY 101
#define RIGHTSHIFTKEY 103
#define LEFTMOUSE 200
#define MIDDLEMOUSE 201
#define MENUBUTTON 202
#define MOUSEX 0
#define MOUSEY 1
#define times mytimes

#define GLC_OLDPOLYGON 0
SHAR_EOF
fi
if test -f 'device.h'
then
	echo shar: "will not over-write existing file 'device.h'"
else
cat << \SHAR_EOF > 'device.h'
#ident "@(#)device.h	1.1 91/02/16 GL"
/*
 * device.h - empty file to keep SGI GL programs happy.
 */
SHAR_EOF
fi
exit 0
#	End of shell archive

-- 
    ______________________________________________________________________
    Patrick J. Naughton				   email: naughton@sun.com
    Sun Laboratories				   voice: (415) 336 - 1080