home *** CD-ROM | disk | FTP | other *** search
- /*
- * algebraic.c -- handle higher order surfaces.
- *
- * Copyright (c) by Han-Wen Nienhuys, 1993
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
-
- extern struct methods my_methods;
-
-
- /* the polystack with polynomials */
- PRIVATE spoly *polystack = NULL;
- PRIVATE double *dpolystack = NULL;
-
- PRIVATE int polystackp, /* and it's pointer */
- maxpolystack;
-
- PRIVATE vector raypos, /* current ray.pos */
- raydir; /* and ray.dir */
-
- PRIVATE void do_poly_op(struct poly_instruction *instr);
- PRIVATE void do_poly_diff_op(struct poly_instruction *instr);
-
- PRIVATE void print_polystack(void);
- PRIVATE void print_instr(struct poly_instruction *instr);
- PRIVATE void print_diffstack(void);
-
- /* execute an instruction */
- PRIVATE void
- exec(struct poly_instruction *instr)
- {
- switch (instr->type) {
- case POLY_CONS: /* push a constant on the stack */
- polystack[polystackp].coef[0] =
- instr->data.factor;
- polystack[polystackp].ord = 0;
- polystackp++;
- break;
- case POLY_OP: /* evaluate operator. */
- do_poly_op(instr);
- break;
- case POLY_X: /* push X on stack */
- polystack[polystackp].coef[0] =
- raypos.x;
- if ((polystack[polystackp].coef[1] = raydir.x))
- polystack[polystackp].ord = 1;
- else
- polystack[polystackp].ord = 0;
-
- polystackp++;
- break;
- case POLY_Y: /* push Y on stack */
- polystack[polystackp].coef[0] = raypos.y;
- if ((polystack[polystackp].coef[1] = raydir.y))
- polystack[polystackp].ord = 1;
- else
- polystack[polystackp].ord = 0;
- polystackp++;
- break;
- case POLY_Z: /* and Z */
- polystack[polystackp].coef[0] = raypos.z;
- if ((polystack[polystackp].coef[1] = raydir.z))
- polystack[polystackp].ord = 1;
- else
- polystack[polystackp].ord = 0;
- polystackp++;
- break;
- default:
- assert(FALSE);
-
- }
- }
-
-
- /* do an operation on the stack */
- PRIVATE void
- do_poly_op(struct poly_instruction *instr)
- {
- spoly temp;
- int exponent;
-
- switch (instr->data.op) {
- case 'n': /* negate */
- poly_neg(polystack + polystackp - 1,
- polystack + polystackp - 1);
- break;
- case '-': /* substract */
- poly_sub(polystack + polystackp - 2,
- polystack + polystackp - 2, polystack + polystackp - 1);
- polystackp--;
-
- break;
- case '+': /* add */
- poly_add(polystack + polystackp - 2, polystack + polystackp - 2,
- polystack + polystackp - 1);
- polystackp--;
- break;
- case '*': /* multiply */
- poly_multiply(&temp, polystack + polystackp - 2,
- polystack + polystackp - 1);
- poly_copy(polystack + polystackp - 2, &temp);
- polystackp--;
- break;
- case '^': /* power */
- exponent = (int) polystack[polystackp - 1].coef[0];
- poly_power(&temp, exponent, polystack + polystackp - 2);
- poly_copy(polystack + polystackp - 2, &temp);
- polystackp--;
- break;
- }
- }
-
- /* calculate a polynomial from its code */
- PRIVATE void
- calc_poly(spoly *dst, struct poly_instruction *instr, int
- len)
- {
- int i;
-
- /* init stack */
- polystackp = 0;
-
- /* execute poly instructions to form polynomial */
- for (i = 0; i < len; i++) {
- exec(instr + i);
- }
-
- assert(polystackp == 1);
-
- poly_copy(dst, polystack);
-
- }
-
- /* zapp */
- PRIVATE void
- free_algebraic(object *o)
- {
- struct algebraic_data *a = o->data.algebraic;
-
- free(a->code);
- free((void *) a);
- o->type = NOSHAPE;
- }
-
- /* evaluate the polynomial in X */
- PRIVATE bool
- inside_algebraic(object *o, vector x)
- {
- spoly tmp;
-
- raypos = x;
- setvector(raydir, 0, 0, 0);
-
- calc_poly(&tmp, o->data.algebraic->code, o->data.algebraic->codesize);
- poly_clean(&tmp);
-
- assert(tmp.ord == 0);
-
- return (tmp.coef[0] > 0) ^ o->inverted;
- }
-
- /* debugging stuff */
- PRIVATE void
- print_algebraic(object *o)
- {
-
- #ifdef DEBUG
- int i;
- struct algebraic_data *a;
-
- a = o->data.algebraic;
-
-
- printf("\t%s%s", (a->isclosed) ? "closed " : "", (a->usesturm) ? "sturm solver" : "");
- printf("code: \n[");
- for (i = 0; i < a->codesize; i++)
- print_instr(a->code + i);
- printf("]\n");
- #endif
- }
-
- /* just the intersections. */
- PRIVATE int
- intersect_algebraic(struct algebraic_data *t, struct ray r,
- double *rhits, bool chkall)
- {
- spoly tosolve;
- int n;
-
-
- my_methods.test++;
-
- /* back to object coords */
- raypos = r.pos;
- raydir = r.dir;
-
- /* construct polynomial */
- calc_poly(&tosolve, t->code, t->codesize);
-
- /* and solve it */
- if (t->usesturm)
- n = sturm_solve_rt_poly(&tosolve, rhits, chkall);
- else
- n = solve_rt_poly(&tosolve, rhits, chkall);
-
- if (n)
- my_methods.hit++;
-
- return n;
- }
-
- /*
- * find each and every intersection with a polysurface, and stuff them
- * into a queue
- */
- PRIVATE bool
- all_algebraic_intersections(dqueue * q, object *o, struct ray *r,
- int flags, bool *isinside)
- {
- int n,
- j;
- double inter[MAX_ORDER];
- dqueue q_ent;
- struct ray localray;
- bool loc_ins;
-
-
- localray = *r;
- transform_ray(&localray, o);
-
- n = intersect_algebraic(o->data.algebraic, localray, inter, flags & CHKALL);
-
-
- if (flags & CHKINSIDE) {
- if (o->data.algebraic->isclosed) /* closed surface */
- loc_ins = *isinside = (n % 2) ^ o->inverted;
- else {
- /* not closed, then use brute force */
- vector sample;
-
- svproduct(sample, 2 * tolerance, r->dir);
- vadd(sample, sample, r->pos);
- loc_ins = *isinside = inside_algebraic(o, sample);
- }
- }
- if (!n)
- return FALSE;
-
-
- if (flags & CHKALL && n > 1)/* check everything? */
- for (j = 0; j < n; j++) {
- q_ent.t = inter[j];
- q_ent.obj = o;
- q_ent.entering = (loc_ins = !loc_ins);
- add_to_queue(q, q_ent);
- } else {
-
- /* record only one hit. */
-
- q_ent.t = inter[0];
- q_ent.obj = o;
- q_ent.entering = (loc_ins = !loc_ins);
- add_to_queue(q, q_ent);
- }
-
- return TRUE;
- }
-
-
-
- PRIVATE vector normal_loc;
- PRIVATE int diff_to;
-
-
- /* handle instructions, for derivative */
- PRIVATE void
- exec_diff(struct poly_instruction *instr)
- {
- switch (instr->type) {
- case POLY_OP:
- do_poly_diff_op(instr);
- break;
- case POLY_X:
- dpolystack[polystackp] = normal_loc.x;
- dpolystack[polystackp + 1] = (diff_to == POLY_X);
- polystackp += 2;
- break;
- case POLY_Y:
- dpolystack[polystackp] = normal_loc.y;
- dpolystack[polystackp + 1] = (diff_to == POLY_Y);
- polystackp += 2;
- break;
- case POLY_Z:
- dpolystack[polystackp] = normal_loc.z;
- dpolystack[polystackp + 1] = (diff_to == POLY_Z);
- polystackp += 2;
- break;
- case POLY_CONS:
- dpolystack[polystackp] = instr->data.factor;
- dpolystack[polystackp + 1] = 0;
- polystackp += 2;
- break;
- default:
- assert(FALSE);
- }
- }
-
-
- /*
- * calculate the resulting polynomial for p1 OP p2, and it's derivative.
- */
- PRIVATE void
- do_poly_diff_op(struct poly_instruction *instr)
- {
- double p,
- Dp;
-
- switch (instr->data.op) {
- case '+':
- polystackp -= 2;
- dpolystack[polystackp - 2] = dpolystack[polystackp - 2]
- + dpolystack[polystackp];
-
- /* (p1+p2)' = p1' + p2' */
- dpolystack[polystackp - 1] = dpolystack[polystackp - 1]
- + dpolystack[polystackp + 1];
- break;
-
- case '-':
- polystackp -= 2;
- dpolystack[polystackp - 2] = dpolystack[polystackp - 2]
- - dpolystack[polystackp];
-
- /* (p1-p2)' = p1' - p2' */
- dpolystack[polystackp - 1] = dpolystack[polystackp - 1]
- - dpolystack[polystackp + 1];
- break;
-
- case '*':
-
- polystackp -= 2;
- p = dpolystack[polystackp - 2] * dpolystack[polystackp];
-
- /* (p1*p2)' = p1'*p2 + p2'*p1 */
- Dp = dpolystack[polystackp - 2] * dpolystack[polystackp + 1] +
- dpolystack[polystackp - 1] * dpolystack[polystackp];
- dpolystack[polystackp - 2] = p;
- dpolystack[polystackp - 1] = Dp;
- break;
- case '^':
-
- polystackp -= 2;
- p = pow(dpolystack[polystackp - 2], dpolystack[polystackp]);
-
- /* (p^a)' = a * p^(a-1) p' */
- Dp = pow(dpolystack[polystackp - 2], dpolystack[polystackp] - 1.0)
- * dpolystack[polystackp - 1] * dpolystack[polystackp];
- dpolystack[polystackp - 2] = p;
- dpolystack[polystackp - 1] = Dp;
- break;
- case 'n':
-
- dpolystack[polystackp - 2] = -dpolystack[polystackp - 2];
-
- /* (-p)' = - p' */
- dpolystack[polystackp - 1] = -dpolystack[polystackp - 1];
- break;
- default:
- assert(FALSE);
- }
- }
-
-
- /*
- * calculate normal. calculate the gradient for each component
- */
- PRIVATE double
- calc_normal(struct poly_instruction *p, int size)
- {
- int i;
- double retval;
-
- polystackp = 0;
-
- /* calcalute the gradient. */
- for (i = 0; i < size; i++) {
- exec_diff(p + i);
- }
- assert(polystackp <= 2);
-
-
- retval = dpolystack[1];
-
- return retval;
- }
-
- /*
- * returns the normal to the algebraic
- */
- PRIVATE vector
- algebraic_normal(struct intersect i, vector loc)
- {
- vector n;
- struct algebraic_data *a;
-
- a = i.q_ent.obj->data.algebraic;
-
- /* back to object coords */
- normal_loc = mvproduct(*i.q_ent.obj->inv_trans, loc);
-
- diff_to = POLY_X;
- n.x = calc_normal(a->code, a->codesize);
- diff_to = POLY_Y;
- n.y = calc_normal(a->code, a->codesize);
- diff_to = POLY_Z;
- n.z = calc_normal(a->code, a->codesize);
-
- n = transform_normal(*i.q_ent.obj->inv_trans, n);
- norm(n, n);
-
- return n;
- }
-
- /* initialize a algebraic_data struct */
- PRIVATE void
- init_algebraic(struct algebraic_data *t)
- {
- t->code = NULL;
- t->codesize = 0;
-
- t->usesturm = FALSE;
- t->isclosed = FALSE;
- }
-
- /* alloc and init algebraic */
- PRIVATE struct algebraic_data *
- get_new_algebraic(void)
- {
- struct algebraic_data *a;
-
- a = ALLOC(struct algebraic_data);
-
- CHECK_MEM(a, my_methods.name);
- init_algebraic(a);
-
- return a;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_algebraic(object *dst, object *src)
- {
- struct algebraic_data *s,
- *d;
-
- assert(dst != NULL && src != NULL);
-
- /* alloc a new one */
- if (dst->type != ALGEBRAIC) {
- dst->data.algebraic = get_new_algebraic();
- dst->type = ALGEBRAIC;
- }
- d = dst->data.algebraic;
- s = src->data.algebraic;
- *d = *s;
-
-
- /* alloc new code */
- d->code = malloc(sizeof(struct poly_instruction) * s->codesize);
-
- CHECK_MEM(d->code, "polycode");
- memcpy(d->code, s->code, s->codesize * sizeof(struct poly_instruction));
- }
-
- PRIVATE void
- precompute_algebraic(object *o)
- {
- struct algebraic_data *a;
-
- a = o->data.algebraic;
-
- if (a->codesize > maxpolystack) {
- maxpolystack = a->codesize + 1;
- polystack = realloc(polystack, maxpolystack * sizeof(spoly));
-
- CHECK_MEM(polystack, "polystack");
- dpolystack = realloc(dpolystack, 2 * maxpolystack * sizeof(double));
-
- CHECK_MEM(dpolystack, "dpolystack");
- }
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
-
- PRIVATE struct methods my_methods =
- {
- all_algebraic_intersections,
- algebraic_normal,
- copy_algebraic,
- inside_algebraic,
- generic_rotate_object,
- generic_translate_object,
- generic_scale_object,
- free_algebraic,
- print_algebraic,
- precompute_algebraic,
- "algebraic",
- };
-
- /* alloc/init */
- PUBLIC object *
- get_new_algebraic_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.algebraic = get_new_algebraic();
- o->methods = &my_methods;
- o->type = ALGEBRAIC;
-
- return o;
- }
-
- #ifdef DEBUG
-
- /* dump stack */
- PRIVATE void
- print_polystack(void)
- {
- int i;
-
- printf("\n----\n");
- for (i = 0; i < polystackp; i++) {
- printf("%d : ", i);
- print_poly(polystack + i);
- }
- }
-
- /* print out the stack for calculating the derivative */
- PRIVATE void
- print_diffstack(void)
- {
- int i;
-
- printf("\n----\n");
- for (i = 0; i < polystackp; i += 2) {
- printf("%d : %f, %f\n", i / 2, dpolystack[i], dpolystack[i + 1]);
- }
- }
-
- /* print instruction */
- PRIVATE void
- print_instr(struct poly_instruction *instr)
- {
- switch (instr->type) {
- case POLY_CONS: /* push a constant on the stack */
- printf("c %f ", instr->data.factor);
- break;
- case POLY_OP: /* evaluate operator. */
- printf("op %c ", instr->data.op);
- break;
- case POLY_X: /* push X on stack */
- printf("X ");
- break;
- case POLY_Y: /* push Y on stack */
- printf("Y ");
- break;
- case POLY_Z: /* and Z */
- printf("Z ");
- break;
- default:
- assert(FALSE);
- }
- }
-
- #endif
-
- /* eof */
-