/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ #include "evaluator.h" #include #include #include #include #include /* NULL */ #include /* FLT_EPSILON */ /* * create node, initialize all values to default */ static so_eval_node * create_node(int id) { so_eval_node *node = (so_eval_node*) malloc(sizeof(so_eval_node)); node->id = id; node->child1 = NULL; node->child2 = NULL; node->child3 = NULL; node->regidx = -1; node->regname[0] = 'x'; node->regname[1] = 0; node->value = 0.0f; return node; } /* * convenience method that creates a unary node */ so_eval_node * so_eval_create_unary(int id, so_eval_node *topnode) { so_eval_node *node = create_node(id); node->child1 = topnode; return node; } /* * convenience method that creates a binary node */ so_eval_node * so_eval_create_binary(int id, so_eval_node *lhs, so_eval_node *rhs) { so_eval_node *node = create_node(id); node->child1 = lhs; node->child2 = rhs; return node; } /* * convenience method that creates a ternary node */ so_eval_node * so_eval_create_ternary(int id, so_eval_node *cond, so_eval_node *branch1, so_eval_node *branch2) { so_eval_node *node = create_node(id); node->child1 = cond; node->child2 = branch1; node->child3 = branch2; return node; } /* * creates a node that "references" a field (register) in the SoCalculator node. */ so_eval_node * so_eval_create_reg(const char *regname) { so_eval_node *node = NULL; int idx; /* find where to look for field name (upper case means vectors) */ if (regname[0] == 't' || regname[0] == 'o') { idx = 1; } else idx = 0; if (regname[idx] >= 'a' && regname[idx] <= 'h') { node = create_node(ID_FLT_REG); } else if (regname[idx] >= 'A' && regname[idx] <= 'H') { node = create_node(ID_VEC_REG); } else { assert(0 && "whoa!"); /* the lexical scanner should have stopped this */ } if (node) { node->regname[0] = regname[0]; node->regname[1] = regname[1]; node->regname[2] = 0; } return node; } /* * creates a node that references a component in a SoCalculator vector field. */ so_eval_node * so_eval_create_reg_comp(const char *regname, int index) { so_eval_node *node = create_node(ID_VEC_REG_COMP); node->regname[0] = regname[0]; node->regname[1] = regname[1]; node->regname[2] = 0; node->regidx = index; return node; } /* * creates a node that holds a float value. */ so_eval_node * so_eval_create_flt_val(float val) { so_eval_node *node = create_node(ID_VALUE); node->value = val; return node; } /* * used for returning values from the traverse method. */ typedef union { int trueorfalse; float value; float vec[3]; } so_eval_param; /* * clamp-function */ static float clamp(float val, float minval, float maxval) { if (val <= minval) return minval; else if (val >= maxval) return maxval; return val; } /* * returns the dot product of the two vectors. */ static float dot_product(float *v0, float *v1) { return v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2]; } /* * treverses (evaluates) the tree structure. */ void so_eval_traverse(so_eval_node *node, so_eval_param *result, const so_eval_cbdata *cbdata) { so_eval_param param1, param2, param3; if (node->id != ID_FLT_COND && node->id != ID_VEC_COND && node->id != ID_ASSIGN_FLT && node->id != ID_ASSIGN_VEC) { if (node->child1) so_eval_traverse(node->child1, ¶m1, cbdata); if (node->child2) so_eval_traverse(node->child2, ¶m2, cbdata); if (node->child3) so_eval_traverse(node->child3, ¶m3, cbdata); } switch (node->id) { case ID_ADD: result->value = param1.value + param2.value; break; case ID_ADD_VEC: result->vec[0] = param1.vec[0] + param2.vec[0]; result->vec[1] = param1.vec[1] + param2.vec[1]; result->vec[2] = param1.vec[2] + param2.vec[2]; break; case ID_SUB: result->value = param1.value - param2.value; break; case ID_SUB_VEC: result->vec[0] = param1.vec[0] - param2.vec[0]; result->vec[1] = param1.vec[1] - param2.vec[1]; result->vec[2] = param1.vec[2] - param2.vec[2]; break; case ID_MUL: result->value = param1.value * param2.value; break; case ID_DIV: if (param2.value == 0.0f) { result->value = param1.value / FLT_EPSILON; /* FIXME: is this ok? */ } else { result->value = param1.value / param2.value; } break; case ID_FMOD: if (param2.value != 0.0f) { result->value = (float) fmod(param1.value, param2.value); } else result->value = 0.0f; break; case ID_NEG: result->value = - param1.value; break; case ID_NEG_VEC: result->vec[0] = -param1.vec[0]; result->vec[1] = -param1.vec[1]; result->vec[2] = -param1.vec[2]; break; case ID_AND: result->trueorfalse = param1.trueorfalse && param2.trueorfalse; break; case ID_OR: result->trueorfalse = param1.trueorfalse || param2.trueorfalse; break; case ID_LEQ: result->trueorfalse = param1.value <= param2.value; break; case ID_GEQ: result->trueorfalse = param1.value >= param2.value; break; case ID_EQ: result->trueorfalse = param1.value == param2.value; break; case ID_NEQ: result->trueorfalse = param1.value != param2.value; break; case ID_COS: result->value = (float)cos(param1.value); break; case ID_SIN: result->value = (float)sin(param1.value); break; case ID_TAN: result->value = (float)tan(param1.value); break; case ID_ACOS: result->value = (float)acos(clamp(param1.value, -1.0f, 1.0f)); break; case ID_ASIN: result->value = (float)asin(clamp(param1.value, -1.0f, 1.0f)); break; case ID_ATAN: result->value = (float)atan(param1.value); break; case ID_ATAN2: if (param2.value == 0.0) { result->value = (float) (param1.value >= 0.0f ? M_PI * 0.5 : - M_PI * 0.5); } else { result->value = (float)atan2(param1.value, param2.value); } break; case ID_COSH: result->value = (float) cosh(param1.value); break; case ID_SINH: result->value = (float) sinh(param1.value); break; case ID_TANH: result->value = (float) tanh(param1.value); break; case ID_SQRT: result->value = param1.value > 0.0f ? (float) sqrt(param1.value) : 0.0f; break; case ID_EXP: result->value = (float) exp(param1.value); break; case ID_LOG: /* as value gets close to 0, the log2 goes towards -128 */ result->value = param1.value <= 0.0f ? -128.0f : (float) log(param1.value); break; case ID_LOG10: /* as value gets close to 0, the log10 goes towards -38 */ result->value = param1.value <= 0.0f ? -38.0f : (float)log10(param1.value); break; case ID_CEIL: result->value = (float) ceil(param1.value); break; case ID_FLOOR: result->value = (float) floor(param1.value); break; case ID_FABS: result->value = (float) fabs(param1.value); break; case ID_RAND: result->value = ((float)rand()) / ((float)RAND_MAX); /* [0, 1] */ result->value *= param1.value; /* [0, arg] */ break; case ID_CROSS: result->vec[0] = param1.vec[1]*param2.vec[2] - param1.vec[2]*param2.vec[1]; result->vec[1] = param1.vec[2]*param2.vec[0] - param1.vec[0]*param2.vec[2]; result->vec[2] = param1.vec[0]*param2.vec[1] - param1.vec[1]*param2.vec[0]; break; case ID_DOT: result->value = dot_product(param1.vec, param2.vec); break; case ID_LEN: result->value = (float)sqrt(dot_product(param1.vec, param1.vec)); break; case ID_NORMALIZE: { float len = (float) sqrt(dot_product(param1.vec, param1.vec)); if (len > 0.0f) { result->vec[0] = param1.vec[0] / len; result->vec[1] = param1.vec[1] / len; result->vec[2] = param1.vec[2] / len; } else { result->vec[0] = result->vec[1] = result->vec[2] = 0.0f; } } break; case ID_TEST_FLT: result->trueorfalse = param1.value != 0.0f; break; case ID_TEST_VEC: result->trueorfalse = param1.vec[0] != 0.0f || param1.vec[1] != 0.0f || param1.vec[2] != 0.0f; break; case ID_VEC3F: result->vec[0] = param1.value; result->vec[1] = param2.value; result->vec[2] = param3.value; break; case ID_FLT_REG: cbdata->readfieldcb(node->regname, &result->value, cbdata->userdata); break; case ID_VEC_REG: cbdata->readfieldcb(node->regname, result->vec, cbdata->userdata); break; case ID_VEC_REG_COMP: { float tmp[3]; assert(node->regidx >= 0 && node->regidx <= 2); cbdata->readfieldcb(node->regname, tmp, cbdata->userdata); result->value = tmp[node->regidx]; } break; case ID_FLT_COND: so_eval_traverse(node->child1, ¶m1, cbdata); so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3, ¶m2, cbdata); result->value = param2.value; break; case ID_VEC_COND: so_eval_traverse(node->child1, ¶m1, cbdata); so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3, ¶m2, cbdata); result->vec[0] = param2.vec[0]; result->vec[1] = param2.vec[1]; result->vec[2] = param2.vec[2]; break; case ID_VALUE: result->value = node->value; break; case ID_ASSIGN_FLT: /* this is safe, since regidx always will be -1 for other than vector components */ so_eval_traverse(node->child2, ¶m1, cbdata); cbdata->writefieldcb(node->child1->regname, ¶m1.value, node->child1->regidx, cbdata->userdata); break; case ID_ASSIGN_VEC: so_eval_traverse(node->child2, ¶m1, cbdata); cbdata->writefieldcb(node->child1->regname, param1.vec, -1, cbdata->userdata); break; case ID_NOT: result->trueorfalse = ! param1.trueorfalse; break; case ID_LT: result->trueorfalse = param1.value < param2.value; break; case ID_GT: result->trueorfalse = param1.value > param2.value; break; case ID_POW: if (param1.value == 0.0f) result->value = 0.0f; else if (param1.value > 0.0f) { result->value = (float) pow(param1.value, param2.value); } else { /* param1.value < 0.0, param2.value must be an integral value */ result->value = (float) pow(param1.value, floor(param2.value + 0.5)); } break; case ID_MUL_VEC_FLT: result->vec[0] = param1.vec[0] * param2.value; result->vec[1] = param1.vec[1] * param2.value; result->vec[2] = param1.vec[2] * param2.value; break; case ID_DIV_VEC_FLT: { float div = param2.value; if (div == 0.0f) div = FLT_EPSILON; result->vec[0] = param1.vec[0] / div; result->vec[1] = param1.vec[1] / div; result->vec[2] = param1.vec[2] / div; } break; case ID_SEPARATOR: /* do nothing, both children have been traversed */ break; default: assert(0 && "Whoops. Unknown node id!\n"); break; } } /* * evaluates the tree structure */ void so_eval_evaluate(so_eval_node *node, const so_eval_cbdata *cbdata) { so_eval_param dummy; if (node == NULL) return; so_eval_traverse(node, &dummy, cbdata); } void so_eval_delete(so_eval_node *node) { if (node != NULL) { if (node->child1) so_eval_delete(node->child1); if (node->child2) so_eval_delete(node->child2); if (node->child3) so_eval_delete(node->child3); free(node); } }