[comp.graphics] The ray tracer I wrote.

kyriazis@rpics (George Kyriazis) (09/07/88)

	Since I had too many requests for my ray tracer, I am posting it
here.  Hope that will help people that can't get mail to me.  I finally
had the source put so people can read it, but it's not definite that it'll
stay where it is.  Right now it is on life.pawl.rpi.edu (128.113.10.2) in
pub/ray.  Can you please comment back on it ?  Thanks.

So, here it is:



# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by pawl23.pawl.rpi.edu!kyriazis on Thu Sep  1 22:08:36 EDT 1988
# Contents:  README Makefile ray.h vector.h bg.c initialize.c intersect.c
#	main.c readfile.c shade.c trace.c vector.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'

	Here is a simple ray tracing program developed here at RPI.  It
incorporates shadows, reflections and refraction together with one
non-directional light source.  The light source can diffuse on the surfaces
and can also give specular reflections.  The illumination model used is
by Phong.  The only objects supported right now are spheres, but the data
structure can be easily expanded to incorporate more objects.
	Here is a list of the files, and what does each file contain:

bg.c:		bgcolor()	evaluates the background color for a given ray.

initialize.c:	initialize()	does some useful setup.

intersect.c:	sphere()	Intersection routine with a sphere.
		intersect()	Main intersection routine  (calls sphere() ).

main.c:		main()		Main body of the program.

readfile.c:	readfile()	Reads in the input data.

shade.c:	shadow()        Calculates the existance of a shadow ray.
		reflect()	Find the reflection vector.
		refract()	Find the refraction vector.
		shade()		Calculate Phong's shading function.

trace.c:	trace()		Trace a single ray.
		raytrace()	Ray trace the whole picture.

vector.c:	vadd()		vector addition.
		vsub()		vector subtraction.
		vneg()		vector negation.
		svproduct()	scalar - vector product.
		vdot()		dot product.
		vcross()	cross product.
		norm()		normalize a vector.

ray.h:		Include file for every file used in the raytracer.

vector.h:	Include file for every file using vectors.



	The ray tracer is written so it can be easily understood (at least
that version), and it is fully commented.  Nevertheless, probably it won't
be understood by a newcomer.  

	The format of the input file is as follows:

light
nos
x y z r [ambient] [diff] [spec] refl r g b refr r g b width index

where:

light		x y z components of the light source.
nos		num,ber of spheres
[ambient]	r g b components of ambient
[diff]		r g b components of diffuse
[spec]		r g b components of specular
refl r g b	reflection ratio and color of the reflection
refr r g b	refraction ratio and color of the refraction
width		specular width exponent
index		index of refraction

	The format of the output file is simple.  In the beginning there are
2 integers (that can be read with fread() on a SUN) showing xsize and ysize
of the picture.  After that follow the pixels in scan-line order.  Each pixel
uses 3 bytes (one for red, green and blue), totalling 16777216 colors.  You
can change the format of that file to tailor your needs.  It can be done
easily by changing the funcion raytrace() in file trace.c


	Can you please inform me with any bugs that the program might have
or any features that you want the upcoming versions to have.  This software
was written by me, and the subsequent version will probably by produced
by other members of the RPI chapter of the ACM.
						Good luck!


	George Kyriazis
	kyriazis@turing.cs.rpi.edu
@//E*O*F README//
chmod u=rw,g=rw,o=rw README
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
SRC=vector.c readfile.c main.c trace.c intersect.c initialize.c shade.c bg.c

OBJ=vector.o readfile.o main.o trace.o intersect.o initialize.o shade.o bg.o

HDR=ray.h

CFLAGS=-O2

ray:	$(OBJ)
	cc $(CFLAGS) $(OBJ) -o ray -lm

clean:
	rm -f ray $(OBJ)

$(OBJ): $(HDR)
@//E*O*F Makefile//
chmod u=rw,g=rw,o=rw Makefile
 
echo x - ray.h
sed 's/^@//' > "ray.h" <<'@//E*O*F ray.h//'
/*
 * include file for the ray-tracer
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#ifndef	NULL
#define	NULL 0
#endif

#ifndef	FALSE
#define	FALSE	0
#endif

#ifndef	TRUE
#define	TRUE	!FALSE
#endif

#define	MAXLEVEL	3	/* maximum recursion level */

struct	vector	{double x,y,z;};

/* the color structure */
struct	color	{
		double	r,g,b;
		};

/* the object structure */
struct	obj	{
		struct	vector	center;
		double	radius;
		double	reflection;	/* precentage reflection */
		double	refraction;	/* percentage refraction */
		struct	color	refl_color;	/* reflective color */
		struct	color	refr_color;	/* refractive color */
		struct	color	ambient;	/* ambient color */
		struct	color	diffuse;		/* diffuse color */
		struct	color	specular;	/* specular color */
		double	width;		/* specular width factor */
		double	index;		/* index of refraction */
		};

/* light source */
struct	vector	light;

/* number of spheres */
int	noo;

/* the array of spheres */
struct	obj	*obj;

/* resolution */
int	xres, yres;

/* the ray structure */
struct	ray	{
		struct	vector	pos;	/* origin */
		struct	vector	dir;	/* direction */
		struct	obj	*obj;	/* where the ray comes from */
		};

/* the intersection structure */
struct	intersect	{
		struct	obj	*obj;	/* which object */
		double	t;		/* where in the ray */
		struct	vector	n;	/* the normal at that point */
		};

/* some statistics variables */
int	raycount;		/* total number of rays */
int	shadowcount;		/* total number of shadow rays traced */
int	reflectcount;		/* total number of reflected rays */
int	refractcount;		/* total number of refracted rays */
int	intersectcount;		/* total number of object intersections */
int	objtestcount;		/* total number of intersection tests */

int	rayline;		/* rays / line */
int	shadowline;		/* shadow rays / line */
int	reflectline;		/* reflected rays / line */
int	refractline;		/* refracted rays / line */
int	intersectline;		/* object intersections / line */
int	objtestline;		/* object intersection tests / line */
@//E*O*F ray.h//
chmod u=rw,g=rw,o=rw ray.h
 
echo x - vector.h
sed 's/^@//' > "vector.h" <<'@//E*O*F vector.h//'

/*
 * definitions for the vector routines used in other files
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

struct	vector	vadd();
struct	vector	vsub();
struct	vector	vneg();
struct	vector	svproduct();
double	vdot();
struct	vector	vcross();
struct	vector	norm();


@//E*O*F vector.h//
chmod u=rw,g=rw,o=rw vector.h
 
echo x - bg.c
sed 's/^@//' > "bg.c" <<'@//E*O*F bg.c//'

/*
 * the background color.
 * can be changed to have any kind of background texturing.  (hint hint)
 */

/* 
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM.
 */

#include "ray.h"

struct	color	bgcolor(r)
struct	ray	r;
{
	struct	color	c;

/* if it is above the y-axis just give a grey value */
/* if below give a gradually whiter color           */
	if(r.dir.y > 0)
		c.r = c.g = c.b = 0.2;
	else
		c.r = c.g = c.b = 0.2 - 0.8 * r.dir.y;

	return c;
}
@//E*O*F bg.c//
chmod u=rw,g=rw,o=rw bg.c
 
echo x - initialize.c
sed 's/^@//' > "initialize.c" <<'@//E*O*F initialize.c//'

/*
 * just to initialize some stuff
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"ray.h"

initialize()
{
/* for the moment just initialize the statistics */
	raycount = rayline = 0;
	shadowcount = shadowline = 0;
	reflectcount = reflectline = 0;
	refractcount = refractline = 0;
	intersectcount = intersectline = 0;
	objtestcount = objtestline = 0;
}
@//E*O*F initialize.c//
chmod u=rw,g=rw,o=rw initialize.c
 
echo x - intersect.c
sed 's/^@//' > "intersect.c" <<'@//E*O*F intersect.c//'

/*
 * Intersection routines
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"vector.h"
#include	"ray.h"
#include	<math.h>

/* 
 * Intersect ray with sphere
 */
struct	intersect	sphere(obj, r)
struct	obj	*obj;
struct	ray	r;
{
	struct	vector	v;
	struct	vector	n;	/* normal vector */
	struct	intersect	i;
	double	b, c, d;
	double	sol1, sol2;

	i.obj = NULL;

	v = vsub( r.pos, obj->center );
	b = 2 * vdot( r.dir, v );
	c = vdot(v, v) - obj->radius;

	d = b * b - 4 * c;
	if( d < 0 )
		return i;
	d = sqrt(d);
	sol1 = ( -b + d ) / 2;
	sol2 = ( -b - d ) / 2;
	if( sol1 <= 0 )
		sol1 = sol2;
	if( sol2 <= 0 )
		sol2 = sol1;
	i.t = (sol1 < sol2) ? sol1 : sol2 ;
/* if intersection is behind eye */
	if(i.t <= 0)
		return i;
	i.obj = obj;	

/* calculate the normal.  It is just the direction of the radius */
	n = vsub( vadd( r.pos, svproduct(i.t, r.dir) ), obj->center);
	i.n = norm(n);

	return i;
}


struct	intersect	intersect(r)
struct	ray	r;
{
	int	i;
	struct	intersect	inter, intermin;

	intermin.obj = NULL;

	for(i = 0; i < noo; i++) {
		objtestline++;
		inter = sphere( &obj[i], r);
/* update the minimum intersection distance if the new intersection  */
/* exists (ray intersects the object), and the intersection distance */
/* is smaller that the one logged and also the object intersected is */
/* not the object that the ray is originating from.                  */
		if( inter.obj &&
			( !intermin.obj || 
				(inter.t < intermin.t) ) &&
			( inter.obj != r.obj ) )
			intermin = inter;
	}

	return intermin;
}

@//E*O*F intersect.c//
chmod u=rw,g=rw,o=rw intersect.c
 
echo x - main.c
sed 's/^@//' > "main.c" <<'@//E*O*F main.c//'

/*
 * main subroutine call for the ray-tracer
 *	Use:  % ray scenename resolution
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"ray.h"
#include	<stdio.h>

main(argc, argv)
int	argc;
char	**argv;
{

	if(argc != 4) {
		fprintf(stderr,"Usage: %s scenename resolution outfile\n",
			argv[0]);
		exit(1);
	}

	xres = yres = atoi(argv[2]);

	readfile(argv[1]);
	initialize();
	raytrace(argv[3]);
}

@//E*O*F main.c//
chmod u=rw,g=rw,o=rw main.c
 
echo x - readfile.c
sed 's/^@//' > "readfile.c" <<'@//E*O*F readfile.c//'

/*
 * read the input file of the ray-tracer
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	<stdio.h>
#include	"ray.h"

readfile(fname)
char	*fname;
{
	FILE	*f;
	int	i;

	f = fopen(fname,"r");
	if(f == NULL) {
		perror("fopen");
		exit(1);
	}

/* file format is:
 *	<x y z> light source position
 *	number or spheres
 * 	<x y z r [ambient] [diff] [spec] refl r g b refr r g b width index > 
 *		for every sphere
 */

	fscanf(f, "%lf %lf %lf", &light.x, &light.y, &light.z);

	fscanf(f, "%d", &noo);

	obj = (struct obj *)malloc(noo * sizeof(struct obj) );
	if(obj == NULL) {
		perror("malloc");
		exit(1);
	}

	for(i = 0; i < noo; i++) {
		fscanf(f, "%lf %lf %lf %lf", &obj[i].center.x,
			&obj[i].center.y,
			&obj[i].center.z,
			&obj[i].radius );
		fscanf(f, "%lf %lf %lf", &obj[i].ambient.r,
			&obj[i].ambient.g,
			&obj[i].ambient.b);
		fscanf(f, "%lf %lf %lf", &obj[i].diffuse.r,
			&obj[i].diffuse.g,
			&obj[i].diffuse.b);
		fscanf(f, "%lf %lf %lf", &obj[i].specular.r,
			&obj[i].specular.g,
			&obj[i].specular.b);
		fscanf(f, "%lf %lf %lf %lf", &obj[i].reflection,
			&obj[i].refl_color.r,
			&obj[i].refl_color.g,
			&obj[i].refl_color.b);
		fscanf(f, "%lf %lf %lf %lf", &obj[i].refraction,
			&obj[i].refr_color.r,
			&obj[i].refr_color.g,
			&obj[i].refr_color.b);
		fscanf(f, "%lf %lf", &obj[i].width, &obj[i].index);
	}
}
@//E*O*F readfile.c//
chmod u=rw,g=rw,o=rw readfile.c
 
echo x - shade.c
sed 's/^@//' > "shade.c" <<'@//E*O*F shade.c//'
/*
 * here is the shading function
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"ray.h"
#include	"vector.h"
#include	<math.h>

struct	color	trace();
struct	intersect	intersect();

/*
 * Shadow check.  Check if the ray from the intersection point to the
 * light, intersection any objects inbetween.  If it does, we have a
 * shadow.
 * One improvement that can be made is to check the transparencies of all
 * the intersecting objects and make the shadow have a different intensity
 * depending on the transparency of the objects
 */
int	shadow(r)
struct	ray	r;
{
	struct	intersect	i;
/* to have a shadow, the light ray must intersect an object */
	i = intersect(r);

	if( i.obj == NULL )
		return FALSE;
	else
		return (r.obj != i.obj);
}

/*
 * calculate the reflection vector.
 * D. Rogers: "Procedural elements for computer graphics". 5-12. p. 367
 */
struct	vector	reflect(n,v1)
struct	vector	n, v1;
{
	struct	vector	v2;

	v2 = vsub( v1, svproduct( 2 * vdot(n, v1), n) );


	return v2;
}

/*
 * calculate the refracted vector.
 * D. Rogers: "Procedural elements for computer graphics". 5-12. p. 367
 */
struct	vector	refract(n, v1, index)
struct	vector	n, v1;
double	index;
{
	double	p, t;
	struct	vector	v2;

	v2.x = 0.; v2.y = 0.; v2.z = 0.;

	p = vdot(n, v1);
	if(p < 0) {
		t = 1 - ( 1 - p*p ) / ( index*index );
		if( t <= 0 )
			return v2;
		t = -p/index - sqrt(t);
	} else {
		index = 1 / index;
		t = 1 - ( 1 - p*p ) / ( index*index );
		if( t <= 0 )
			return v2;
		t = -p/index + sqrt(t);
	}

	v2 = vadd( svproduct(1/index, v1), svproduct(t, n) );

	return v2;
}

/*
 * the actual shading function.  Recursively calls trace()
 */
struct	color	shade(i, r, n)
struct	intersect	i;
struct	ray	r;
int	n;		/* recursion level */
{
	struct	color	col;
	struct	vector	pt;
	struct	ray	shadow_ray;
	struct	vector	ldir;
	int	shad;
	double	ldot;
	double	spec;
	struct	vector	rr;	/* reflected light direction */
	struct	ray	reflected, refracted;	/* refl, refr directions */
	struct	color	c;

/* if the recursion level has been exceeded, return peacefully*/
	if(n > MAXLEVEL) {
		col.r = col.g = col.b = 0.;
		return col;
	}

/* initially get the ambient color */
	col = i.obj->ambient;

/* first calculate the intersection point */
	pt = vadd( r.pos, svproduct(i.t, r.dir) );

/* for the light source first get the vector from it to the intersection */
	ldir = norm( vsub(pt, light) );
/* then calc the dot product between the light direction and the normal   */
/* negative because the light direction is reverse (comes from the light) */
	ldot = -vdot(i.n, ldir);
/* and find if that shadow ray is stopped by an object */
	shadow_ray.pos = light;
	shadow_ray.dir = ldir;
	shadow_ray.obj = i.obj;
	shad = shadow(shadow_ray);
	if( (ldot>0) && !shad ) {
/* statistics */
		shadowline++;
/* add some diffuse color. */
		col.r += ldot * i.obj->diffuse.r;
		col.g += ldot * i.obj->diffuse.g;
		col.b += ldot * i.obj->diffuse.b;
/* now calc the specular color */
/* first calculate the reflected light vector */
		rr = reflect(i.n, ldir);
/* then take the dot of the reflected ray with the viewing direction */
/* that is the specular component. The minus is there for obvious reasons */
		spec = -vdot(rr, r.dir);
/* remember the specular width factor?  We use it here! */
		spec = pow(spec, i.obj->width);
		col.r += spec * i.obj->specular.r;
		col.g += spec * i.obj->specular.g;
		col.b += spec * i.obj->specular.b;
	}

/* setup the reflected ray */
	reflected.pos = pt;
	reflected.dir = reflect(i.n, r.dir);
	reflected.obj = i.obj;
/* calculate the reflection */
	reflectline++;
	c = trace(reflected, n+1);
	col.r += c.r * i.obj->refl_color.r * i.obj->reflection;
	col.g += c.g * i.obj->refl_color.g * i.obj->reflection;
	col.b += c.b * i.obj->refl_color.b * i.obj->reflection;
/* now setup the refracted ray */
	refracted.pos = pt;
	refracted.dir = refract(i.n, r.dir, i.obj->index);
	refracted.obj = i.obj;
	if( (refracted.dir.x == 0.) &&
	    (refracted.dir.y == 0.) &&
	    (refracted.dir.z == 0.) )
		return col;
/* and calculate the reflection */
	refractline++;
	c = trace(refracted, n+1);
	col.r += c.r * i.obj->refr_color.r * i.obj->refraction;
	col.g += c.g * i.obj->refr_color.g * i.obj->refraction;
	col.b += c.b * i.obj->refr_color.b * i.obj->refraction;

	return col;
}
@//E*O*F shade.c//
chmod u=rw,g=rw,o=rw shade.c
 
echo x - trace.c
sed 's/^@//' > "trace.c" <<'@//E*O*F trace.c//'

/* 
 * This is the actual ray-tracing part
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"ray.h"
#include	"vector.h"
#include	<stdio.h>

struct	intersect	intersect();
struct	color	shade();
struct	color	bgcolor();

struct	color	trace(r, n)
struct	ray	r;
int	n;		/* recursion level */
{
	struct	intersect	inter;
	struct	color	col;
	double	m;

/* update stats */
	rayline++;
/* check for intersection with the object */
	inter = intersect(r);
/* if no intersection, return some background color */
	if( inter.obj == NULL )
		return bgcolor(r);
/* more stats */
	intersectline++;
/* else calculate the shading function there */
	col = shade(inter, r, n);
/* if the color > 1, that means that the components are too big. Normalize. */
	m = col.r;
	if( col.g > m )
		m = col.g;
	if( col.b > m )
		m = col.b;
	if( m > 1 ) {
/* overflow condition */
		printf("Pixel normalized:%lf %lf %lf\n", col.r, col.g, col.b);
		col.r /= m;
		col.g /= m;
		col.b /= m;
	}
	return col;
}


raytrace(fname)
char	*fname;
{
	int	x, y, x1, y1;
	double	xr, yr, xstep, ystep;
	struct	vector	hor, ver, eye_dir;
	struct	color	col;
	struct	ray	ray;
	int	r, g, b;
	double	m;
	FILE	*f;

	f = fopen(fname, "w");
	if(f == NULL) {
		perror("fopen");
		exit(1);
	}

	hor.x = 1.; hor.y = 0.; hor.z = 0.;
	ver.x = 0.; ver.y = 1.; ver.z = 0.;
	eye_dir.x = 0.; eye_dir.y = 0.; eye_dir.z = 1.;

	ray.pos.x = 0.; ray.pos.y = 0.; ray.pos.z = 0.;
	ray.obj = NULL;		/* not coming from an object */

	fwrite(&xres, sizeof(int), 1, f);
	fwrite(&yres, sizeof(int), 1, f);

	yr = 1.;
	xstep = 2. / xres; ystep = 2. / yres;
	for(y = 0; y < yres;y++) {
		xr = -1.;
		for(x = 0; x < xres; x++) {
			ray.dir = vadd( svproduct(xr, hor),
				svproduct(yr, ver) );
			ray.dir = norm( vadd( ray.dir, eye_dir) );
			col = trace(ray, 0);
		/* calc the integer value to be put to the file */
			r = col.r * 255;
			g = col.g * 255;
			b = col.b * 255;
			putc(r,f);
			putc(g,f);
			putc(b,f);

			xr += xstep;
		}
		yr -= ystep;
		printf("Finished scanline %d. r:%.2lf/%d sh:%d rl:%d rr:%d int:%d\n",
			y, (double)rayline / yres,  rayline, shadowline,
			reflectline, refractline, intersectline);
	/* update statistics */
		raycount += rayline; rayline = 0;
		shadowcount += shadowline; shadowline = 0;
		reflectcount += reflectline; reflectline = 0;
		refractcount += refractline; refractline = 0;
		intersectcount += intersectline; intersectline = 0;
		objtestcount += objtestline; objtestline = 0;
	}
	printf("\nTotal number of rays traced: %d\n", raycount);
	printf("Total number of not shadowed intersections: %d\n", shadowcount);
	printf("Total reflected rays traced: %d\n", reflectcount);
	printf("Total refracted rays traced: %d\n", refractcount);
	printf("Total object intersections: %d\n", intersectcount);
	printf("Total objects tested: %d\n", objtestcount);
}
@//E*O*F trace.c//
chmod u=rw,g=rw,o=rw trace.c
 
echo x - vector.c
sed 's/^@//' > "vector.c" <<'@//E*O*F vector.c//'

/*
 *	vector operations 
 */

/*
 *	(c) 1988 by George Kyriazis
 *	and the RPI - ACM
 */

#include	"ray.h"
#include	<math.h>

struct	vector	vadd(a,b)
struct	vector	a,b;
{
	struct	vector	c;

	c.x=a.x+b.x;
	c.y=a.y+b.y;
	c.z=a.z+b.z;

	return c;
}

struct	vector	vsub(a,b)
struct	vector	a,b;
{
	struct	vector	c;

	c.x=a.x-b.x;
	c.y=a.y-b.y;
	c.z=a.z-b.z;

	return c;
}

struct	vector	vneg(a)
struct	vector	a;
{
	struct	vector	b;

	b.x= -a.x;
	b.y= -a.y;
	b.z= -a.z;

	return b;
}

struct	vector	svproduct(k,a)
double	k;
struct	vector	a;
{
	a.x*=k;
	a.y*=k;
	a.z*=k;

	return a;
}
 
double	vdot(a,b)
struct	vector	a,b;
{
	return (a.x*b.x+a.y*b.y+a.z*b.z);
}

struct	vector	vcross(a,b)
struct	vector	a,b;
{
	struct	vector	c;

	c.x=a.y*b.z-b.y*a.z;
	c.y=b.x*a.z-a.x*b.z;
	c.z=a.x*b.y-b.x*a.y;

	return c;
}

struct	vector	norm(a)
struct	vector	a;
{
	double len;
	struct	vector	b;

	len=sqrt(a.x*a.x+a.y*a.y+a.z*a.z);
	b.x=a.x/len;
	b.y=a.y/len;
	b.z=a.z/len;

	return	b;
}
@//E*O*F vector.c//
chmod u=rw,g=rw,o=rw vector.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      81     453    2832 README
      15      33     265 Makefile
      85     339    2018 ray.h
      19      46     284 vector.h
      27      92     458 bg.c
      22      66     381 initialize.c
      82     293    1549 intersect.c
      32      61     423 main.c
      67     175    1366 readfile.c
     172     720    4312 shade.c
     120     456    2843 trace.c
      91     133     969 vector.c
     813    2867   17700 total
!!!
wc  README Makefile ray.h vector.h bg.c initialize.c intersect.c main.c readfile.c shade.c trace.c vector.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

  George Kyriazis
  kyriazis@turing.cs.rpi.edu
------------------------------