/* expr.c Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr. This file is part of Sketch, a small, simple system for making 3d drawings with LaTeX and the PSTricks or TikZ package. Sketch 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; either version 3, or (at your option) any later version. Sketch 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 Sketch; see the file COPYING.txt. If not, see http://www.gnu.org/copyleft */ #include #include #include "expr.h" #include "error.h" #define F "%.3f" char *expr_val_type_str[] = { "float", "point", "vector", "transform", }; // set expression value to given type and value void set_float (EXPR_VAL * r, FLOAT val) { r->tag = E_FLOAT; r->val.flt = val; } void print_float (FILE * f, EXPR_VAL * val) { fprintf (f, F, val->val.flt); } void set_point (EXPR_VAL * r, POINT_3D val) { r->tag = E_POINT; copy_pt_3d (r->val.pt, val); } void print_point (FILE * f, EXPR_VAL * val) { FLOAT *p = val->val.pt; fprintf (f, "(" F "," F "," F ")", p[X], p[Y], p[Z]); } void set_vector (EXPR_VAL * r, VECTOR_3D val) { r->tag = E_VECTOR; copy_vec_3d (r->val.vec, val); } void print_vector (FILE * f, EXPR_VAL * val) { FLOAT *v = val->val.vec; fprintf (f, "[" F "," F "," F "]", v[X], v[Y], v[Z]); } void set_transform (EXPR_VAL * r, TRANSFORM val) { r->tag = E_TRANSFORM; copy_transform (r->val.xf, val); } void print_transform (FILE * f, EXPR_VAL * val) { FLOAT *xf = val->val.xf; int i, j; fprintf (f, "["); for (i = 0; i < 4; i++) { fprintf (f, "["); for (j = 0; j < 16; j += 4) fprintf (f, "%s" F, (j == 0) ? "" : ",", xf[i + j]); fprintf (f, "]"); } fprintf (f, "]"); } // coerce an expression value to given type // generate error message if it can't be done void coerce_to_float (EXPR_VAL * r, FLOAT * val, SRC_LINE line) { if (r->tag == E_FLOAT) { *val = r->val.flt; } else { *val = 0; err (line, "expected float, found %s", expr_val_type_str[r->tag]); } } void coerce_to_point (EXPR_VAL * r, POINT_3D val, SRC_LINE line) { if (r->tag == E_POINT) { copy_pt_3d (val, r->val.pt); } else { val[X] = val[Y] = val[Z] = 0; err (line, "expected point, found %s", expr_val_type_str[r->tag]); } } void coerce_to_vector (EXPR_VAL * r, VECTOR_3D val, SRC_LINE line) { if (r->tag == E_VECTOR) { copy_vec_3d (val, r->val.vec); } else { val[X] = val[Y] = val[Z] = 0; err (line, "expected vector, found %s", expr_val_type_str[r->tag]); } } void coerce_to_transform (EXPR_VAL * r, TRANSFORM val, SRC_LINE line) { if (r->tag == E_TRANSFORM) { copy_transform (val, r->val.xf); } else { set_ident (val); err (line, "expected transform, found %s", expr_val_type_str[r->tag]); } } typedef void (*PRINT_FUNC) (FILE *, EXPR_VAL *); static PRINT_FUNC print_expr_val_tbl[] = { print_float, print_point, print_vector, print_transform, }; void print_expr_val (FILE * f, EXPR_VAL * r) { (*print_expr_val_tbl[r->tag]) (f, r); } #define HASH(A, B) (((A) << 2) | (B)) void do_add (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, a->val.flt + b->val.flt); break; case HASH (E_POINT, E_VECTOR): r->tag = E_POINT; add_vec_to_pt_3d (r->val.pt, a->val.pt, b->val.vec); break; case HASH (E_VECTOR, E_POINT): r->tag = E_POINT; add_vec_to_pt_3d (r->val.pt, b->val.pt, a->val.vec); break; case HASH (E_VECTOR, E_VECTOR): r->tag = E_VECTOR; add_vecs_3d (r->val.vec, a->val.vec, b->val.vec); break; default: err (line, "operands of + (types %s and %s) cannot be added", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_sub (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, a->val.flt - b->val.flt); break; case HASH (E_POINT, E_POINT): r->tag = E_VECTOR; sub_pts_3d (r->val.vec, a->val.pt, b->val.pt); break; case HASH (E_POINT, E_VECTOR): r->tag = E_POINT; add_scaled_vec_to_pt_3d (r->val.pt, a->val.pt, b->val.vec, -1); break; case HASH (E_VECTOR, E_VECTOR): r->tag = E_VECTOR; sub_vecs_3d (r->val.vec, a->val.vec, b->val.vec); break; default: err (line, "operands of - (types %s and %s) cannot be subtracted", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_mul (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, a->val.flt * b->val.flt); break; case HASH (E_VECTOR, E_FLOAT): r->tag = E_VECTOR; scale_vec_3d (r->val.vec, a->val.vec, b->val.flt); break; case HASH (E_FLOAT, E_VECTOR): r->tag = E_VECTOR; scale_vec_3d (r->val.vec, b->val.vec, a->val.flt); break; case HASH (E_VECTOR, E_VECTOR): r->tag = E_VECTOR; cross (r->val.vec, a->val.vec, b->val.vec); break; case HASH (E_TRANSFORM, E_TRANSFORM): r->tag = E_TRANSFORM; compose (r->val.xf, a->val.xf, b->val.xf); break; case HASH (E_TRANSFORM, E_POINT): r->tag = E_POINT; transform_pt_3d (r->val.pt, a->val.xf, b->val.pt); break; case HASH (E_TRANSFORM, E_VECTOR): r->tag = E_VECTOR; transform_vec_3d (r->val.vec, a->val.xf, b->val.vec); break; default: err (line, "operands of * (types %s and %s) cannot be multiplied", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_thn (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_TRANSFORM, E_TRANSFORM): r->tag = E_TRANSFORM; compose (r->val.xf, b->val.xf, a->val.xf); break; case HASH (E_POINT, E_TRANSFORM): r->tag = E_POINT; transform_pt_3d (r->val.pt, b->val.xf, a->val.pt); break; case HASH (E_VECTOR, E_TRANSFORM): r->tag = E_VECTOR; transform_vec_3d (r->val.vec, b->val.xf, a->val.vec); break; default: err (line, "operands of 'then' (types %s and %s) cannot be multiplied", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } static FLOAT safe_dvd (FLOAT a, FLOAT b, SRC_LINE line) { if (-FLOAT_EPS < b && b < FLOAT_EPS) { err (line, "attempt to divide " F " by zero", a); return 0; } return a / b; } void do_dvd (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, safe_dvd (a->val.flt, b->val.flt, line)); break; case HASH (E_VECTOR, E_FLOAT): r->tag = E_VECTOR; scale_vec_3d (r->val.vec, a->val.vec, safe_dvd (1, b->val.flt, line)); break; case HASH (E_FLOAT, E_VECTOR): r->tag = E_VECTOR; scale_vec_3d (r->val.vec, b->val.vec, safe_dvd (1, a->val.flt, line)); break; default: err (line, "operands of / (types %s and %s) cannot be divided", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_dot (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_VECTOR, E_VECTOR): r->tag = E_FLOAT; r->val.flt = dot_3d (a->val.vec, b->val.vec); break; case HASH (E_FLOAT, E_FLOAT): case HASH (E_VECTOR, E_FLOAT): case HASH (E_FLOAT, E_VECTOR): case HASH (E_TRANSFORM, E_TRANSFORM): case HASH (E_TRANSFORM, E_POINT): case HASH (E_TRANSFORM, E_VECTOR): do_mul (r, a, b, line); break; default: err (line, "operands of dot (types %s and %s) cannot be multiplied", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_index (EXPR_VAL * r, EXPR_VAL * a, int index, SRC_LINE line) { switch (a->tag) { case E_VECTOR: set_float (r, a->val.vec[index]); break; case E_POINT: set_float (r, a->val.pt[index]); break; default: err (line, "operand of 'index is a %s and should be a point or a vector", expr_val_type_str[a->tag]); set_float (r, 0); break; } } void do_inverse (TRANSFORM inv, TRANSFORM xf, SRC_LINE line) { FLOAT det; invert (inv, &det, xf, 1e-4); if (det == 0) { err (line, "inverse of singular transform"); set_ident (inv); } } // put a^n into r; r and a can't both be the same storage // exploits a^(2n) = (a^n)^2 to reduce work void do_transform_power (TRANSFORM r, TRANSFORM a, int n, SRC_LINE line) { if (n < 0) { TRANSFORM inv; do_inverse (inv, a, line); do_transform_power (r, inv, -n, line); } else if (n == 0) { set_ident (r); } else { int m = (int) bit (30); while ((m & n) == 0) m >>= 1; copy_transform (r, a); for (m >>= 1; m; m >>= 1) { compose (r, r, r); if (m & n) compose (r, r, a); } } } int to_integer (FLOAT x, int *n) { double frac_part, int_part; frac_part = modf (x, &int_part); if (-1e9 <= int_part && int_part <= 1e9) { *n = (int) int_part; return 1; } return 0; } void do_pwr (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { TRANSFORM xf_pwr; int n; switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, pow (a->val.flt, b->val.flt)); break; case HASH (E_TRANSFORM, E_FLOAT): if (to_integer (b->val.flt, &n)) { do_transform_power (xf_pwr, a->val.xf, n, line); } else { err (line, "transform power out of domain (integer -1e9..1e9)"); set_ident (xf_pwr); } set_transform (r, xf_pwr); break; default: err (line, "operands of ^ (types %s and %s) must be type float", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); set_float (r, 0); break; } } void do_mag (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { switch (a->tag) { case E_FLOAT: set_float (r, a->val.flt >= 0 ? a->val.flt : -a->val.flt); break; case E_VECTOR: set_float (r, length_vec_3d (a->val.vec)); break; default: err (line, "operand of magnitude operator (type %s) must be vector", expr_val_type_str[a->tag]); *r = *a; break; } } void do_neg (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { switch (a->tag) { case E_FLOAT: set_float (r, -a->val.flt); break; case E_VECTOR: r->tag = E_VECTOR; negate_vec_3d (r->val.vec, a->val.vec); break; default: err (line, "operand of unary minus (type %s) cannot be negated", expr_val_type_str[a->tag]); *r = *a; break; } } void do_unit (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { if (a->tag == E_VECTOR) { r->tag = E_VECTOR; find_unit_vec_3d (r->val.vec, a->val.vec); } else { static VECTOR_3D k = { 0, 0, 1 }; err (line, "operand of unit operator (type %s) must be vector", expr_val_type_str[a->tag]); set_vector (r, k); } } void do_sqrt (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { switch (a->tag) { case E_FLOAT: if (a->val.flt < 0) err (line, "square root of negative number"); set_float (r, sqrt (a->val.flt)); break; default: err (line, "operand of sqrt (type %s) must be float", expr_val_type_str[a->tag]); break; } } void do_sin (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { switch (a->tag) { case E_FLOAT: set_float (r, sin ((PI / 180) * a->val.flt)); break; default: err (line, "operand of sin (type %s) must be float", expr_val_type_str[a->tag]); break; } } void do_cos (EXPR_VAL * r, EXPR_VAL * a, SRC_LINE line) { switch (a->tag) { case E_FLOAT: set_float (r, cos ((PI / 180) * a->val.flt)); break; default: err (line, "operand of cos (type %s) must be float", expr_val_type_str[a->tag]); break; } } void do_atan2 (EXPR_VAL * r, EXPR_VAL * a, EXPR_VAL * b, SRC_LINE line) { switch (HASH (a->tag, b->tag)) { case HASH (E_FLOAT, E_FLOAT): set_float (r, (180 / PI) * atan2 (a->val.flt, b->val.flt)); break; default: err (line, "operands of atan2 (types %s, %s) must be float", expr_val_type_str[a->tag], expr_val_type_str[b->tag]); break; } }