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, ¶m1, cbdata);
193 if (node->child2) so_eval_traverse(node->child2, ¶m2, cbdata);
194 if (node->child3) so_eval_traverse(node->child3, ¶m3, 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, ¶m1, cbdata);
385 so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3,
386 ¶m2, cbdata);
387 result->value = param2.value;
388 break;
389 case ID_VEC_COND:
390 so_eval_traverse(node->child1, ¶m1, cbdata);
391 so_eval_traverse(param1.trueorfalse ? node->child2 : node->child3,
392 ¶m2, 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, ¶m1, cbdata);
403 cbdata->writefieldcb(node->child1->regname, ¶m1.value,
404 node->child1->regidx, cbdata->userdata);
405 break;
406 case ID_ASSIGN_VEC:
407 so_eval_traverse(node->child2, ¶m1, 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