summaryrefslogtreecommitdiff
path: root/graphics/sketch/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/sketch/expr.c')
-rw-r--r--graphics/sketch/expr.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/graphics/sketch/expr.c b/graphics/sketch/expr.c
new file mode 100644
index 0000000000..a61b771dd8
--- /dev/null
+++ b/graphics/sketch/expr.c
@@ -0,0 +1,581 @@
+/* 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 <stdio.h>
+#include <math.h>
+#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;
+ }
+}