#ifndef lint
static char *sccsid = "@(#)solid.c	1.2 (Steve Hill) 3/9/90";
#endif

/* solid.c
 *
 */

#include <stdio.h>

#include "basetype.h"
#include "malloc.h"
#include "cartesian.h"
#include "print.h"
#include "error.h"
#include "matrix.h"
#include "point.h"
#include "vector.h"
#include "indexlist.h"
#include "ray.h"
#include "quadric.h"
#include "colour.h"
#include "surface.h"
#include "bitmap.h"
#include "pattern.h"
#include "solid.h"
#include "bbox.h"


/* NewSolid
 *
 * Private function for creation of solids.
 */

static
solid_t *
NewSolid(type)
solid_type_t	type;
{
	solid_t	*solid;

	solid = (solid_t *) malloc(sizeof(solid_t));
	if (solid == SolidNull)
		FatalError(__FILE__, __LINE__, "NewSolid: Out of memory");

	solid->type    = type;
	solid->bounded = FALSE;
	solid->bbox    = BboxNull;

	return(solid);
}


/* SolidJoin
 *
 * Form the join of two solids.
 */

solid_t *
SolidJoin(solid1, solid2)
solid_t	*solid1, *solid2;
{
	solid_t	*solid;

	solid = NewSolid(JOIN);
	solid->body.join.left  = solid1;
	solid->body.join.right = solid2;

	return(solid);
}


/* SolidMeet
 *
 * Form the meet of two solids.
 */

solid_t *
SolidMeet(solid1, solid2)
solid_t	*solid1, *solid2;
{
	solid_t	*solid;

	solid = NewSolid(MEET);
	solid->body.meet.left  = solid1;
	solid->body.meet.right = solid2;

	return(solid);
}


/* Solid
 *
 * Construct an atomic solid.
 */

solid_t *
Solid(quadric, sort)
quadric_t	*quadric;
quadric_sort_t	sort;
{
	solid_t	*solid;

	solid = NewSolid(ATOM);
	solid->body.atom.sort    = sort;
	solid->body.atom.quadric = quadric;

	solid->body.atom.surface = SurfaceCopy(&default_surface);
	solid->body.atom.pattern = PatternNull;

	return(solid);
}


/* SolidNegate
 *
 * Negate a solid.  To negate an atom, flip the operation.
 * To negate joins or meets, negate the parts and change meets
 * to joins and v.v.
 */

solid_t *
SolidNegate(solid)
solid_t	*solid;
{
	solid_body_t	*body = &solid->body;

	switch (solid->type)
	{
	case ATOM:
		QuadricNegate(body->atom.quadric);
		break;

	case JOIN:
		solid->type = MEET;
		body->join.left  = SolidNegate(body->join.left);
		body->join.right = SolidNegate(body->join.right);
		break;

	case MEET:
		solid->type = JOIN;
		body->join.left  = SolidNegate(body->join.left);
		body->join.right = SolidNegate(body->join.right);
		break;

	default:
		FatalError(__FILE__, __LINE__, "Bad solid in SolidNegate");
	}
	return(solid);
}


/* SolidPaint
 *
 * Re-surface all of a solid.
 */

solid_t *
SolidPaint(solid, surface)
solid_t 	*solid;
surface_t	*surface;
{
	solid_body_t	*body;

	body = &solid->body;

	switch (solid->type)
	{
	case ATOM:
		if (body->atom.surface != SurfaceNull)
			SurfaceFree(body->atom.surface);
		body->atom.surface = SurfaceCopy(surface);
		break;

	case JOIN:
		SolidPaint(body->join.left, surface);
		SolidPaint(body->join.right, surface);
		break;

	case MEET:
		SolidPaint(body->meet.left, surface);
		SolidPaint(body->meet.right, surface);
		break;

	default:
		FatalError(__FILE__, __LINE__, "Bad solid in SolidPaint");
	}

	return(solid);
}


/* SolidPattern
 *
 * Give the whole of a solid a pattern.
 */

solid_t *
SolidPattern(solid, pattern)
solid_t 	*solid;
pattern_t	*pattern;
{
	solid_body_t	*body;

	body = &solid->body;

	switch (solid->type)
	{
	case ATOM:
		if (body->atom.pattern != PatternNull)
			PatternFree(body->atom.pattern);
		body->atom.pattern = PatternCopy(pattern);
		break;

	case JOIN:
		SolidPattern(body->join.left, pattern);
		SolidPattern(body->join.right, pattern);
		break;

	case MEET:
		SolidPattern(body->meet.left, pattern);
		SolidPattern(body->meet.right, pattern);
		break;

	default:
		FatalError(__FILE__, __LINE__, "Bad solid in SolidPattern");
	}

	return(solid);
}


/* SolidBbox
 *
 * Calculate bounding boxes in solid.
 */

solid_t *
SolidBbox(solid)
solid_t	*solid;
{
	bbox_t	*bbox;

	bbox = Bbox(REAL_ZERO, REAL_ZERO, REAL_ZERO,
		    REAL_ZERO, REAL_ZERO, REAL_ZERO);
	BboxCalculate(bbox, solid);

	return(solid);
}


/* SolidCopy
 *
 * Make a copy of a solid.
 */

solid_t *
SolidCopy(solid)
solid_t	*solid;
{
	solid_body_t	*body = &solid->body;
	solid_t		*new;

	switch (solid->type)
	{
	case ATOM:
		new = NewSolid(ATOM);
		new->body.atom.sort = body->atom.sort;
		new->body.atom.quadric = QuadricCopy(body->atom.quadric);
		new->body.atom.surface = SurfaceCopy(body->atom.surface);
		new->body.atom.pattern = PatternCopy(body->atom.pattern);
		break;

	case JOIN:
		new = SolidJoin(SolidCopy(body->join.left),
			        SolidCopy(body->join.right));
		break;

	case MEET:
		new = SolidMeet(SolidCopy(body->meet.left),
			        SolidCopy(body->meet.right));
		break;
	}
	new->bbox    = BboxCopy(solid->bbox);
	new->bounded = solid->bounded;
	return(new);
}


/* SolidPrint
 *
 * Print out a solid to a file.
 */

void
SolidPrint(file, solid)
FILE	*file;
solid_t	*solid;
{
	solid_body_t	*body = &solid->body;

	if (solid == SolidNull)
	{
		fprintf(file, "Null Solid\n");
		return;
	}

	switch (solid->type)
	{
	case ATOM:
		QuadricPrint(file, body->atom.quadric);
		SurfacePrint(file, body->atom.surface);
		if (body->atom.pattern != PatternNull)
			PatternPrint(file, body->atom.pattern);
		break;

	case JOIN:
		putchar('(');
		SolidPrint(file, body->join.left);
		puts("/\\");
		SolidPrint(file, body->join.right);
		putchar(')');
		break;

	case MEET:
		putchar('(');
		SolidPrint(file, body->meet.left);
		puts("\\/");
		SolidPrint(file, body->meet.right);
		putchar(')');
		break;

	default:
		FatalError(__FILE__, __LINE__, "Bad solid in SolidPrint");
	}
}

void
PatternCall(solid, point)
solid_t *solid;
point_t *point;
{
        pattern_t       *pattern;

        pattern = solid->body.atom.pattern;

        PointTransform(pattern->point, point, pattern->matrix);
        (*(pattern->function))(solid, pattern);
}
