1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #include "engines/evaluator.h"
34 #include <Inventor/C/basic.h>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stddef.h> /* NULL */
39 #include <float.h> /* FLT_EPSILON */
40 
41 /*
42  * create node, initialize all values to default
43  */
44 static so_eval_node *
create_node(int id)45 create_node(int id)
46 {
47   so_eval_node *node = (so_eval_node*) malloc(sizeof(so_eval_node));
48   node->id = id;
49   node->child1 = NULL;
50   node->child2 = NULL;
51   node->child3 = NULL;
52   node->regidx = -1;
53   node->regname[0] = 'x';
54   node->regname[1] = 0;
55   node->value = 0.0f;
56   return node;
57 }
58 
59 /*
60  * convenience method that creates a unary node
61  */
62 so_eval_node *
so_eval_create_unary(int id,so_eval_node * topnode)63 so_eval_create_unary(int id, so_eval_node *topnode)
64 {
65   so_eval_node *node = create_node(id);
66   node->child1 = topnode;
67   return node;
68 }
69 
70 /*
71  * convenience method that creates a binary node
72  */
73 so_eval_node *
so_eval_create_binary(int id,so_eval_node * lhs,so_eval_node * rhs)74 so_eval_create_binary(int id, so_eval_node *lhs, so_eval_node *rhs)
75 {
76   so_eval_node *node = create_node(id);
77   node->child1 = lhs;
78   node->child2 = rhs;
79   return node;
80 }
81 
82 /*
83  * convenience method that creates a ternary node
84  */
85 so_eval_node *
so_eval_create_ternary(int id,so_eval_node * cond,so_eval_node * branch1,so_eval_node * branch2)86 so_eval_create_ternary(int id, so_eval_node *cond, so_eval_node *branch1,
87                        so_eval_node *branch2)
88 {
89   so_eval_node *node = create_node(id);
90   node->child1 = cond;
91   node->child2 = branch1;
92   node->child3 = branch2;
93   return node;
94 }
95 
96 /*
97  * creates a node that "references" a field (register) in the SoCalculator node.
98  */
99 so_eval_node *
so_eval_create_reg(const char * regname)100 so_eval_create_reg(const char *regname)
101 {
102   so_eval_node *node = NULL;
103   int idx;
104 
105   /* find where to look for field name (upper case means vectors) */
106   if (regname[0] == 't' || regname[0] == 'o') {
107     idx = 1;
108   }
109   else idx = 0;
110 
111   if (regname[idx] >= 'a' && regname[idx] <= 'h') {
112     node = create_node(ID_FLT_REG);
113   }
114   else if (regname[idx] >= 'A' && regname[idx] <= 'H') {
115     node = create_node(ID_VEC_REG);
116   }
117   else {
118     assert(0 && "whoa!"); /* the lexical scanner should have stopped this */
119   }
120   if (node) {
121     node->regname[0] = regname[0];
122     node->regname[1] = regname[1];
123     node->regname[2] = 0;
124   }
125   return node;
126 }
127 
128 /*
129  * creates a node that references a component in a SoCalculator vector field.
130  */
131 so_eval_node *
so_eval_create_reg_comp(const char * regname,int index)132 so_eval_create_reg_comp(const char *regname, int index)
133 {
134   so_eval_node *node = create_node(ID_VEC_REG_COMP);
135   node->regname[0] = regname[0];
136   node->regname[1] = regname[1];
137   node->regname[2] = 0;
138   node->regidx = index;
139   return node;
140 }
141 
142 /*
143  * creates a node that holds a float value.
144  */
145 so_eval_node *
so_eval_create_flt_val(float val)146 so_eval_create_flt_val(float val)
147 {
148   so_eval_node *node = create_node(ID_VALUE);
149   node->value = val;
150   return node;
151 }
152 
153 /*
154  * used for returning values from the traverse method.
155  */
156 typedef union {
157   int trueorfalse;
158   float value;
159   float vec[3];
160 } so_eval_param;
161 
162 /*
163  * clamp-function
164  */
165 static float
clamp(float val,float minval,float maxval)166 clamp(float val, float minval, float maxval)
167 {
168   if (val <= minval) return minval;
169   else if (val >= maxval) return maxval;
170   return val;
171 }
172 
173 /*
174  * returns the dot product of the two vectors.
175  */
176 static float
dot_product(float * v0,float * v1)177 dot_product(float *v0, float *v1)
178 {
179   return v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2];
180 }
181 
182 /*
183  * treverses (evaluates) the tree structure.
184  */
185 void
so_eval_traverse(so_eval_node * node,so_eval_param * result,const so_eval_cbdata * cbdata)186 so_eval_traverse(so_eval_node *node, so_eval_param *result, const so_eval_cbdata *cbdata)
187 {
188   so_eval_param param1, param2, param3;
189 
190   if (node->id != ID_FLT_COND && node->id != ID_VEC_COND &&
191       node->id != ID_ASSIGN_FLT && node->id != ID_ASSIGN_VEC) {
192     if (node->child1) so_eval_traverse(node->child1, &param1, cbdata);
193     if (node->child2) so_eval_traverse(node->child2, &param2, cbdata);
194     if (node->child3) so_eval_traverse(node->child3, &param3, cbdata);
195   }
196 
197   switch (node->id) {
198   case ID_ADD:
199     result->value = param1.value + param2.value;
200     break;
201   case ID_ADD_VEC:
202     result->vec[0] = param1.vec[0] + param2.vec[0];
203     result->vec[1] = param1.vec[1] + param2.vec[1];
204     result->vec[2] = param1.vec[2] + param2.vec[2];
205     break;
206   case ID_SUB:
207     result->value = param1.value - param2.value;
208     break;
209   case ID_SUB_VEC:
210     result->vec[0] = param1.vec[0] - param2.vec[0];
211     result->vec[1] = param1.vec[1] - param2.vec[1];
212     result->vec[2] = param1.vec[2] - param2.vec[2];
213     break;
214   case ID_MUL:
215     result->value = param1.value * param2.value;
216     break;
217   case ID_DIV:
218     /* FIXME: shouldn't just silently pad over this, but rather signal
219        an error. perhaps also set result->value to NaN or inf?
220        -mortene. */
221     if (param2.value == 0.0f) {
222       result->value = param1.value / FLT_EPSILON; /* FIXME: is this ok? */
223     }
224     else {
225       result->value = param1.value / param2.value;
226     }
227     break;
228   case ID_FMOD:
229     /* FIXME: shouldn't just silently pad over this, but rather signal
230        an error. perhaps also set result->value to NaN or inf?
231        -mortene. */
232     if (param2.value != 0.0f) {
233       result->value = (float) fmod(param1.value, param2.value);
234     }
235     else result->value = 0.0f;
236     break;
237   case ID_NEG:
238     result->value = - param1.value;
239     break;
240   case ID_NEG_VEC:
241     result->vec[0] = -param1.vec[0];
242     result->vec[1] = -param1.vec[1];
243     result->vec[2] = -param1.vec[2];
244     break;
245   case ID_AND:
246     result->trueorfalse = param1.trueorfalse && param2.trueorfalse;
247     break;
248   case ID_OR:
249     result->trueorfalse = param1.trueorfalse || param2.trueorfalse;
250     break;
251   case ID_LEQ:
252     result->trueorfalse = param1.value <= param2.value;
253     break;
254   case ID_GEQ:
255     result->trueorfalse = param1.value >= param2.value;
256     break;
257   case ID_EQ:
258     result->trueorfalse = param1.value == param2.value;
259     break;
260   case ID_NEQ:
261     result->trueorfalse = param1.value != param2.value;
262     break;
263   case ID_COS:
264     result->value = (float)cos(param1.value);
265     break;
266   case ID_SIN:
267     result->value = (float)sin(param1.value);
268     break;
269   case ID_TAN:
270     result->value = (float)tan(param1.value);
271     break;
272   case ID_ACOS:
273     result->value = (float)acos(clamp(param1.value, -1.0f, 1.0f));
274     break;
275   case ID_ASIN:
276     result->value = (float)asin(clamp(param1.value, -1.0f, 1.0f));
277     break;
278   case ID_ATAN:
279     result->value = (float)atan(param1.value);
280     break;
281   case ID_ATAN2:
282     /* FIXME: shouldn't just silently pad over this, but rather signal
283        an error. perhaps also set result->value to NaN or inf?
284        -mortene. */
285     if (param2.value == 0.0) {
286       result->value = (float) (param1.value >= 0.0f ? M_PI * 0.5 : - M_PI * 0.5);
287     }
288     else {
289       result->value = (float)atan2(param1.value, param2.value);
290     }
291     break;
292   case ID_COSH:
293     result->value = (float) cosh(param1.value);
294     break;
295   case ID_SINH:
296     result->value = (float) sinh(param1.value);
297     break;
298   case ID_TANH:
299     result->value = (float) tanh(param1.value);
300     break;
301   case ID_SQRT:
302     result->value = param1.value > 0.0f ? (float) sqrt(param1.value) : 0.0f;
303     break;
304   case ID_EXP:
305     result->value = (float) exp(param1.value);
306     break;
307   case ID_LOG:
308     /* as value gets close to 0, the log2 goes towards -128 */
309     result->value = param1.value <= 0.0f ? -128.0f : (float) log(param1.value);
310     break;
311   case ID_LOG10:
312     /* as value gets close to 0, the log10 goes towards -38 */
313     result->value = param1.value <= 0.0f ? -38.0f : (float)log10(param1.value);
314     break;
315   case ID_CEIL:
316     result->value = (float) ceil(param1.value);
317     break;
318   case ID_FLOOR:
319     result->value = (float) floor(param1.value);
320     break;
321   case ID_FABS:
322     result->value = (float) fabs(param1.value);
323     break;
324   case ID_RAND:
325     result->value = ((float)rand()) / ((float)RAND_MAX); /* [0, 1] */
326     result->value *= param1.value; /* [0, arg] */
327     break;
328   case ID_CROSS:
329     result->vec[0] = param1.vec[1]*param2.vec[2] - param1.vec[2]*param2.vec[1];
330     result->vec[1] = param1.vec[2]*param2.vec[0] - param1.vec[0]*param2.vec[2];
331     result->vec[2] = param1.vec[0]*param2.vec[1] - param1.vec[1]*param2.vec[0];
332     break;
333   case ID_DOT:
334     result->value = dot_product(param1.vec, param2.vec);
335     break;
336   case ID_LEN:
337     result->value = (float)sqrt(dot_product(param1.vec, param1.vec));
338     break;
339   case ID_NORMALIZE:
340     {
341       float len = (float) sqrt(dot_product(param1.vec, param1.vec));
342       /* FIXME: shouldn't just silently pad over this, but rather
343          signal an error. perhaps also set result->vec to NaNs or inf?
344          -mortene. */
345       if (len > 0.0f) {
346         result->vec[0] = param1.vec[0] / len;
347         result->vec[1] = param1.vec[1] / len;
348         result->vec[2] = param1.vec[2] / len;
349       }
350       else {
351         result->vec[0] = result->vec[1] = result->vec[2] = 0.0f;
352       }
353     }
354     break;
355   case ID_TEST_FLT:
356     result->trueorfalse = param1.value != 0.0f;
357     break;
358   case ID_TEST_VEC:
359     result->trueorfalse =
360       param1.vec[0] != 0.0f ||
361       param1.vec[1] != 0.0f ||
362       param1.vec[2] != 0.0f;
363     break;
364   case ID_VEC3F:
365     result->vec[0] = param1.value;
366     result->vec[1] = param2.value;
367     result->vec[2] = param3.value;
368     break;
369   case ID_FLT_REG:
370     cbdata->readfieldcb(node->regname, &result->value, cbdata->userdata);
371     break;
372   case ID_VEC_REG:
373     cbdata->readfieldcb(node->regname, result->vec, cbdata->userdata);
374     break;
375   case ID_VEC_REG_COMP:
376     {
377       float tmp[3];
378       assert(node->regidx >= 0 && node->regidx <= 2);
379       cbdata->readfieldcb(node->regname, tmp, cbdata->userdata);
380       result->value = tmp[node->regidx];
381     }
382     break;
383   case ID_FLT_COND:
384     so_eval_traverse(node->child1, &param1, cbdata);
385     so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3,
386                      &param2, cbdata);
387     result->value = param2.value;
388     break;
389   case ID_VEC_COND:
390     so_eval_traverse(node->child1, &param1, cbdata);
391     so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3,
392                      &param2, cbdata);
393     result->vec[0] = param2.vec[0];
394     result->vec[1] = param2.vec[1];
395     result->vec[2] = param2.vec[2];
396     break;
397   case ID_VALUE:
398     result->value = node->value;
399     break;
400   case ID_ASSIGN_FLT:
401     /* this is safe, since regidx always will be -1 for other than vector components */
402     so_eval_traverse(node->child2, &param1, cbdata);
403     cbdata->writefieldcb(node->child1->regname, &param1.value,
404                          node->child1->regidx, cbdata->userdata);
405     break;
406   case ID_ASSIGN_VEC:
407     so_eval_traverse(node->child2, &param1, cbdata);
408     cbdata->writefieldcb(node->child1->regname, param1.vec, -1, cbdata->userdata);
409     break;
410   case ID_NOT:
411     result->trueorfalse = ! param1.trueorfalse;
412     break;
413   case ID_LT:
414     result->trueorfalse = param1.value < param2.value;
415     break;
416   case ID_GT:
417     result->trueorfalse = param1.value > param2.value;
418     break;
419   case ID_POW:
420     /* FIXME: shouldn't just silently pad over this, but rather signal
421        an error. perhaps also set result->value to NaN or inf?
422        -mortene. */
423     if (param1.value == 0.0f) result->value = 0.0f;
424     else if (param1.value > 0.0f) {
425       result->value = (float) pow(param1.value, param2.value);
426     }
427     else { /* param1.value < 0.0, param2.value must be an integral value */
428       result->value = (float) pow(param1.value, floor(param2.value + 0.5));
429     }
430     break;
431   case ID_MUL_VEC_FLT:
432     result->vec[0] = param1.vec[0] * param2.value;
433     result->vec[1] = param1.vec[1] * param2.value;
434     result->vec[2] = param1.vec[2] * param2.value;
435     break;
436   case ID_DIV_VEC_FLT:
437     {
438       float div = param2.value;
439       /* FIXME: shouldn't just silently pad over this, but rather signal
440          an error. perhaps also set result->vec to NaNs or inf?
441          -mortene. */
442       if (div == 0.0f) div = FLT_EPSILON;
443       result->vec[0] = param1.vec[0] / div;
444       result->vec[1] = param1.vec[1] / div;
445       result->vec[2] = param1.vec[2] / div;
446     }
447     break;
448   case ID_SEPARATOR:
449     /* do nothing, both children have been traversed */
450     break;
451   default:
452     assert(0 && "Whoops. Unknown node id!\n");
453     break;
454   }
455 }
456 
457 /*
458  * evaluates the tree structure
459  */
460 void
so_eval_evaluate(so_eval_node * node,const so_eval_cbdata * cbdata)461 so_eval_evaluate(so_eval_node *node, const so_eval_cbdata *cbdata)
462 {
463   so_eval_param dummy;
464   if (node == NULL) return;
465   so_eval_traverse(node, &dummy, cbdata);
466 }
467 
468 
469 void
so_eval_delete(so_eval_node * node)470 so_eval_delete(so_eval_node *node)
471 {
472   if (node != NULL) {
473     if (node->child1) so_eval_delete(node->child1);
474     if (node->child2) so_eval_delete(node->child2);
475     if (node->child3) so_eval_delete(node->child3);
476     free(node);
477   }
478 }
479