summaryrefslogtreecommitdiffstats
path: root/libdimension/dimension/math
diff options
context:
space:
mode:
Diffstat (limited to 'libdimension/dimension/math')
-rw-r--r--libdimension/dimension/math/aabb.h129
-rw-r--r--libdimension/dimension/math/matrix.h193
-rw-r--r--libdimension/dimension/math/ray.h83
-rw-r--r--libdimension/dimension/math/scalar.h103
-rw-r--r--libdimension/dimension/math/vector.h183
5 files changed, 691 insertions, 0 deletions
diff --git a/libdimension/dimension/math/aabb.h b/libdimension/dimension/math/aabb.h
new file mode 100644
index 0000000..14cc575
--- /dev/null
+++ b/libdimension/dimension/math/aabb.h
@@ -0,0 +1,129 @@
+/*************************************************************************
+ * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Axis-aligned bounding boxes.
+ */
+
+#ifndef DMNSN_MATH_H
+#error "Please include <dimension/math.h> instead of this header directly."
+#endif
+
+/** An axis-aligned bounding box. */
+typedef struct dmnsn_aabb {
+ dmnsn_vector min; /**< The coordinate-wise minimum extent of the box. */
+ dmnsn_vector max; /**< The coordinate-wise maximum extent of the box. */
+} dmnsn_aabb;
+
+/** A standard format string for bounding boxes. */
+#define DMNSN_AABB_FORMAT "(<%g, %g, %g> ==> <%g, %g, %g>)"
+/** The appropriate arguements to printf() a bounding box. */
+#define DMNSN_AABB_PRINTF(box) \
+ DMNSN_VECTOR_PRINTF((box).min), DMNSN_VECTOR_PRINTF((box).max)
+
+/**
+ * Construct a new bounding box.
+ * @param[in] min The minimal extent of the bounding box.
+ * @param[in] max The maximal extent of the bounding box.
+ * @return The new bounding box.
+ */
+DMNSN_INLINE dmnsn_aabb
+dmnsn_new_aabb(dmnsn_vector min, dmnsn_vector max)
+{
+ dmnsn_aabb box = { min, max };
+ return box;
+}
+
+/** Return the bounding box which contains nothing. */
+DMNSN_INLINE dmnsn_aabb
+dmnsn_zero_aabb(void)
+{
+ dmnsn_aabb box = {
+ { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY },
+ { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY }
+ };
+ return box;
+}
+
+/** Return the bounding box which contains everything. */
+DMNSN_INLINE dmnsn_aabb
+dmnsn_infinite_aabb(void)
+{
+ dmnsn_aabb box = {
+ { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY },
+ { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY }
+ };
+ return box;
+}
+
+/**
+ * Construct a new symmetric bounding box.
+ * @param[in] r The extent of the bounding box from the origin.
+ * @return The new bounding box.
+ */
+DMNSN_INLINE dmnsn_aabb
+dmnsn_symmetric_aabb(dmnsn_vector r)
+{
+ dmnsn_vector minus_r = dmnsn_vector_negate(r);
+ dmnsn_aabb box = {
+ dmnsn_vector_min(r, minus_r),
+ dmnsn_vector_max(r, minus_r)
+ };
+ return box;
+}
+
+/** Return whether \p p is within the axis-aligned bounding box. */
+DMNSN_INLINE bool
+dmnsn_aabb_contains(dmnsn_aabb box, dmnsn_vector p)
+{
+ return (p.x >= box.min.x && p.y >= box.min.y && p.z >= box.min.z)
+ && (p.x <= box.max.x && p.y <= box.max.y && p.z <= box.max.z);
+}
+
+/** Return whether a bounding box is infinite. */
+DMNSN_INLINE bool
+dmnsn_aabb_is_infinite(dmnsn_aabb box)
+{
+ return box.min.x == -DMNSN_INFINITY;
+}
+
+/**
+ * Expand a bounding box to contain a point
+ * @param[in] box The bounding box to expand.
+ * @param[in] point The point to swallow.
+ * @return The expanded bounding box.
+ */
+DMNSN_INLINE dmnsn_aabb
+dmnsn_aabb_swallow(dmnsn_aabb box, dmnsn_vector point)
+{
+ dmnsn_aabb ret = {
+ dmnsn_vector_min(box.min, point),
+ dmnsn_vector_max(box.max, point)
+ };
+ return ret;
+}
+
+/** Return whether a bounding box has any NaN components. */
+DMNSN_INLINE bool
+dmnsn_aabb_isnan(dmnsn_aabb box)
+{
+ return dmnsn_vector_isnan(box.min) || dmnsn_vector_isnan(box.max);
+}
diff --git a/libdimension/dimension/math/matrix.h b/libdimension/dimension/math/matrix.h
new file mode 100644
index 0000000..7471bf5
--- /dev/null
+++ b/libdimension/dimension/math/matrix.h
@@ -0,0 +1,193 @@
+/*************************************************************************
+ * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Affine transformation matrices.
+ */
+
+#ifndef DMNSN_MATH_H
+#error "Please include <dimension/math.h> instead of this header directly."
+#endif
+
+/** A 4x4 affine transformation matrix, with implied [0 0 0 1] bottom row. */
+typedef struct dmnsn_matrix {
+ double n[3][4]; /**< The matrix elements in row-major order. */
+} dmnsn_matrix;
+
+/** A standard format string for matricies. */
+#define DMNSN_MATRIX_FORMAT \
+ "[%g\t%g\t%g\t%g]\n" \
+ "[%g\t%g\t%g\t%g]\n" \
+ "[%g\t%g\t%g\t%g]\n" \
+ "[%g\t%g\t%g\t%g]"
+/** The appropriate arguements to printf() a matrix. */
+#define DMNSN_MATRIX_PRINTF(m) \
+ (m).n[0][0], (m).n[0][1], (m).n[0][2], (m).n[0][3], \
+ (m).n[1][0], (m).n[1][1], (m).n[1][2], (m).n[1][3], \
+ (m).n[2][0], (m).n[2][1], (m).n[2][2], (m).n[2][3], \
+ 0.0, 0.0, 0.0, 1.0
+
+/** Construct a new transformation matrix. */
+DMNSN_INLINE dmnsn_matrix
+dmnsn_new_matrix(double a0, double a1, double a2, double a3,
+ double b0, double b1, double b2, double b3,
+ double c0, double c1, double c2, double c3)
+{
+ dmnsn_matrix m = { { { a0, a1, a2, a3 },
+ { b0, b1, b2, b3 },
+ { c0, c1, c2, c3 } } };
+ return m;
+}
+
+/** Construct a new transformation matrix from column vectors. */
+DMNSN_INLINE dmnsn_matrix
+dmnsn_new_matrix4(dmnsn_vector a, dmnsn_vector b, dmnsn_vector c,
+ dmnsn_vector d)
+{
+ dmnsn_matrix m = { { { a.x, b.x, c.x, d.x },
+ { a.y, b.y, c.y, d.y },
+ { a.z, b.z, c.z, d.z } } };
+ return m;
+}
+
+/** Extract column vectors from a matrix. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_matrix_column(dmnsn_matrix M, unsigned int i)
+{
+ return dmnsn_new_vector(M.n[0][i], M.n[1][i], M.n[2][i]);
+}
+
+/** Return the identity matrix. */
+dmnsn_matrix dmnsn_identity_matrix(void);
+
+/**
+ * A scale transformation.
+ * @param[in] s A vector with components representing the scaling factor in
+ * each axis.
+ * @return The transformation matrix.
+ */
+dmnsn_matrix dmnsn_scale_matrix(dmnsn_vector s);
+/**
+ * A translation.
+ * @param[in] d The vector to translate by.
+ * @return The transformation matrix.
+ */
+dmnsn_matrix dmnsn_translation_matrix(dmnsn_vector d);
+/**
+ * A left-handed rotation.
+ * @param[in] theta A vector representing an axis and angle.
+ * @f$ axis = \vec{\theta}/|\vec{\theta}| @f$,
+ * @f$ angle = |\vec{\theta}| @f$
+ * @return The transformation matrix.
+ */
+dmnsn_matrix dmnsn_rotation_matrix(dmnsn_vector theta);
+
+/**
+ * An alignment matrix.
+ * @param[in] from The initial vector.
+ * @param[in] to The desired direction.
+ * @param[in] axis1 The first axis about which to rotate.
+ * @param[in] axis2 The second axis about which to rotate.
+ * @return A transformation matrix that will rotate \p from to \p to.
+ */
+dmnsn_matrix dmnsn_alignment_matrix(dmnsn_vector from, dmnsn_vector to,
+ dmnsn_vector axis1, dmnsn_vector axis2);
+
+/** Invert a matrix. */
+dmnsn_matrix dmnsn_matrix_inverse(dmnsn_matrix A);
+
+/** Multiply two matricies. */
+dmnsn_matrix dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs);
+
+/** Transform a point by a matrix. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_transform_point(dmnsn_matrix T, dmnsn_vector v)
+{
+ /* 9 multiplications, 9 additions */
+ dmnsn_vector r;
+ r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z + T.n[0][3];
+ r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z + T.n[1][3];
+ r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z + T.n[2][3];
+ return r;
+}
+
+/** Transform a direction by a matrix. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_transform_direction(dmnsn_matrix T, dmnsn_vector v)
+{
+ /* 9 multiplications, 6 additions */
+ dmnsn_vector r;
+ r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z;
+ r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z;
+ r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z;
+ return r;
+}
+
+/**
+ * Transform a pseudovector by a matrix.
+ * @param[in] Tinv The inverse of the transformation matrix.
+ * @param[in] v The pseudovector to transform
+ * @return The transformed pseudovector.
+ */
+DMNSN_INLINE dmnsn_vector
+dmnsn_transform_normal(dmnsn_matrix Tinv, dmnsn_vector v)
+{
+ /* Multiply by the transpose of the inverse
+ (9 multiplications, 6 additions) */
+ dmnsn_vector r;
+ r.x = Tinv.n[0][0]*v.x + Tinv.n[1][0]*v.y + Tinv.n[2][0]*v.z;
+ r.y = Tinv.n[0][1]*v.x + Tinv.n[1][1]*v.y + Tinv.n[2][1]*v.z;
+ r.z = Tinv.n[0][2]*v.x + Tinv.n[1][2]*v.y + Tinv.n[2][2]*v.z;
+ return r;
+}
+
+/**
+ * Transform a ray by a matrix.
+ * \f$ n' = T(l.\vec{x_0} + l.\vec{n}) - T(l.\vec{x_0}) \f$,
+ * \f$ \vec{x_0}' = T(l.\vec{x_0}) \f$
+ */
+DMNSN_INLINE dmnsn_ray
+dmnsn_transform_ray(dmnsn_matrix T, dmnsn_ray l)
+{
+ /* 18 multiplications, 15 additions */
+ dmnsn_ray ret;
+ ret.x0 = dmnsn_transform_point(T, l.x0);
+ ret.n = dmnsn_transform_direction(T, l.n);
+ return ret;
+}
+
+/** Transform a bounding box by a matrix. */
+dmnsn_aabb dmnsn_transform_aabb(dmnsn_matrix T, dmnsn_aabb box);
+
+/** Return whether a matrix contains any NaN components. */
+DMNSN_INLINE bool
+dmnsn_matrix_isnan(dmnsn_matrix m)
+{
+ size_t i, j;
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 4; ++j) {
+ if (dmnsn_isnan(m.n[i][j])) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/libdimension/dimension/math/ray.h b/libdimension/dimension/math/ray.h
new file mode 100644
index 0000000..1dd98f7
--- /dev/null
+++ b/libdimension/dimension/math/ray.h
@@ -0,0 +1,83 @@
+/*************************************************************************
+ * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Lines in 3-D space.
+ */
+
+#ifndef DMNSN_MATH_H
+#error "Please include <dimension/math.h> instead of this header directly."
+#endif
+
+/** A line, or ray. */
+typedef struct dmnsn_ray {
+ dmnsn_vector x0; /**< The origin of the ray. */
+ dmnsn_vector n; /**< The direction of the ray. */
+} dmnsn_ray;
+
+/** A standard format string for rays. */
+#define DMNSN_RAY_FORMAT "(<%g, %g, %g> + t*<%g, %g, %g>)"
+/** The appropriate arguements to printf() a ray. */
+#define DMNSN_RAY_PRINTF(l) \
+ DMNSN_VECTOR_PRINTF((l).x0), DMNSN_VECTOR_PRINTF((l).n)
+
+/**
+ * Construct a new ray.
+ * @param[in] x0 The origin of the ray.
+ * @param[in] n The direction of the ray.
+ * @return The new ray.
+ */
+DMNSN_INLINE dmnsn_ray
+dmnsn_new_ray(dmnsn_vector x0, dmnsn_vector n)
+{
+ dmnsn_ray l = { x0, n };
+ return l;
+}
+
+/**
+ * Return the point at \p t on a ray.
+ * The point is defined by \f$ l.\vec{x_0} + t \cdot l.\vec{n} \f$
+ */
+DMNSN_INLINE dmnsn_vector
+dmnsn_ray_point(dmnsn_ray l, double t)
+{
+ return dmnsn_vector_add(l.x0, dmnsn_vector_mul(t, l.n));
+}
+
+/** Add epsilon*l.n to l.x0, to avoid self-intersections. */
+DMNSN_INLINE dmnsn_ray
+dmnsn_ray_add_epsilon(dmnsn_ray l)
+{
+ return dmnsn_new_ray(
+ dmnsn_vector_add(
+ l.x0,
+ dmnsn_vector_mul(1.0e3*dmnsn_epsilon, l.n)
+ ),
+ l.n
+ );
+}
+
+/** Return whether a ray contains any NaN entries. */
+DMNSN_INLINE bool
+dmnsn_ray_isnan(dmnsn_ray l)
+{
+ return dmnsn_vector_isnan(l.x0) || dmnsn_vector_isnan(l.n);
+}
diff --git a/libdimension/dimension/math/scalar.h b/libdimension/dimension/math/scalar.h
new file mode 100644
index 0000000..3887c14
--- /dev/null
+++ b/libdimension/dimension/math/scalar.h
@@ -0,0 +1,103 @@
+/*************************************************************************
+ * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Mathematical functions of one variable.
+ */
+
+#ifndef DMNSN_MATH_H
+#error "Please include <dimension/math.h> instead of this header directly."
+#endif
+
+#include <math.h>
+#include <stdbool.h>
+
+/** The smallest value considered non-zero by some numerical algorithms. */
+#define dmnsn_epsilon 1.0e-10
+
+/**
+ * @def DMNSN_INFINITY
+ * Expands to floating-point infinity.
+ */
+#if defined(INFINITY) || DMNSN_C99
+ #define DMNSN_INFINITY INFINITY
+#else
+ #define DMNSN_INFINITY HUGE_VAL
+#endif
+
+/** Find the minimum of two values. */
+DMNSN_INLINE double
+dmnsn_min(double a, double b)
+{
+ return a < b ? a : b;
+}
+
+/** Find the maximum of two values. */
+DMNSN_INLINE double
+dmnsn_max(double a, double b)
+{
+ return a > b ? a : b;
+}
+
+/** Clamp a value to an interval. */
+DMNSN_INLINE double
+dmnsn_clamp(double n, double min, double max)
+{
+ return dmnsn_min(dmnsn_max(n, min), max);
+}
+
+/** Convert degrees to radians. */
+DMNSN_INLINE double
+dmnsn_radians(double degrees)
+{
+ return degrees*(atan(1.0)/45.0);
+}
+
+/** Convert radians to degrees. */
+DMNSN_INLINE double
+dmnsn_degrees(double radians)
+{
+ return radians*(45.0/atan(1.0));
+}
+
+/** Signum function: return the sign of a value. */
+DMNSN_INLINE int
+dmnsn_sgn(double n)
+{
+ if (n > 0.0) {
+ return 1;
+ } else if (n < 0.0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/** Return whether a value is NaN. */
+DMNSN_INLINE bool
+dmnsn_isnan(double n)
+{
+#if DMNSN_C99
+ return isnan(n);
+#else
+ return n != n;
+#endif
+}
diff --git a/libdimension/dimension/math/vector.h b/libdimension/dimension/math/vector.h
new file mode 100644
index 0000000..8eacee9
--- /dev/null
+++ b/libdimension/dimension/math/vector.h
@@ -0,0 +1,183 @@
+/*************************************************************************
+ * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Vectors in 3-D space.
+ */
+
+#ifndef DMNSN_MATH_H
+#error "Please include <dimension/math.h> instead of this header directly."
+#endif
+
+#include <math.h>
+#include <stdbool.h>
+
+/** A vector in 3 dimensions. */
+typedef struct dmnsn_vector {
+ double x; /**< The x component. */
+ double y; /**< The y component. */
+ double z; /**< The z component. */
+} dmnsn_vector;
+
+/** A standard format string for vectors. */
+#define DMNSN_VECTOR_FORMAT "<%g, %g, %g>"
+/** The appropriate arguements to printf() a vector. */
+#define DMNSN_VECTOR_PRINTF(v) (v).x, (v).y, (v).z
+
+/* Constants */
+
+/** The zero vector. */
+static const dmnsn_vector dmnsn_zero = { 0.0, 0.0, 0.0 };
+/** The x vector. */
+static const dmnsn_vector dmnsn_x = { 1.0, 0.0, 0.0 };
+/** The y vector. */
+static const dmnsn_vector dmnsn_y = { 0.0, 1.0, 0.0 };
+/** The z vector. */
+static const dmnsn_vector dmnsn_z = { 0.0, 0.0, 1.0 };
+
+/* Shorthand for vector construction */
+
+/** Construct a new vector. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_new_vector(double x, double y, double z)
+{
+ dmnsn_vector v = { x, y, z };
+ return v;
+}
+
+/* Vector arithmetic */
+
+/** Negate a vector. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_negate(dmnsn_vector rhs)
+{
+ /* 3 negations */
+ dmnsn_vector v = { -rhs.x, -rhs.y, -rhs.z };
+ return v;
+}
+
+/** Add two vectors. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs)
+{
+ /* 3 additions */
+ dmnsn_vector v = { lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z };
+ return v;
+}
+
+/** Subtract two vectors. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs)
+{
+ /* 3 additions */
+ dmnsn_vector v = { lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z };
+ return v;
+}
+
+/** Multiply a vector by a scalar. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_mul(double lhs, dmnsn_vector rhs)
+{
+ /* 3 multiplications */
+ dmnsn_vector v = { lhs*rhs.x, lhs*rhs.y, lhs*rhs.z };
+ return v;
+}
+
+/** Divide a vector by a scalar. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_div(dmnsn_vector lhs, double rhs)
+{
+ /* 3 divisions */
+ dmnsn_vector v = { lhs.x/rhs, lhs.y/rhs, lhs.z/rhs };
+ return v;
+}
+
+/** Return the dot product of two vectors. */
+DMNSN_INLINE double
+dmnsn_vector_dot(dmnsn_vector lhs, dmnsn_vector rhs)
+{
+ /* 3 multiplications, 2 additions */
+ return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
+}
+
+/** Return the cross product of two vectors. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs)
+{
+ /* 6 multiplications, 3 additions */
+ dmnsn_vector v = { lhs.y*rhs.z - lhs.z*rhs.y,
+ lhs.z*rhs.x - lhs.x*rhs.z,
+ lhs.x*rhs.y - lhs.y*rhs.x };
+ return v;
+}
+
+/** Return the projection of \p u onto \p d. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_proj(dmnsn_vector u, dmnsn_vector d)
+{
+ /* 1 division, 9 multiplications, 4 additions */
+ return dmnsn_vector_mul(dmnsn_vector_dot(u, d)/dmnsn_vector_dot(d, d), d);
+}
+
+/** Return the magnitude of a vector. */
+DMNSN_INLINE double
+dmnsn_vector_norm(dmnsn_vector n)
+{
+ /* 1 sqrt, 3 multiplications, 2 additions */
+ return sqrt(dmnsn_vector_dot(n, n));
+}
+
+/** Return the direction of a vector. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_normalized(dmnsn_vector n)
+{
+ /* 1 sqrt, 3 divisions, 3 multiplications, 2 additions */
+ return dmnsn_vector_div(n, dmnsn_vector_norm(n));
+}
+
+/** Return the component-wise minimum of two vectors. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_min(dmnsn_vector a, dmnsn_vector b)
+{
+ return dmnsn_new_vector(
+ dmnsn_min(a.x, b.x),
+ dmnsn_min(a.y, b.y),
+ dmnsn_min(a.z, b.z)
+ );
+}
+
+/** Return the component-wise maximum of two vectors. */
+DMNSN_INLINE dmnsn_vector
+dmnsn_vector_max(dmnsn_vector a, dmnsn_vector b)
+{
+ return dmnsn_new_vector(
+ dmnsn_max(a.x, b.x),
+ dmnsn_max(a.y, b.y),
+ dmnsn_max(a.z, b.z)
+ );
+}
+
+/** Return whether a vector contains any NaN components. */
+DMNSN_INLINE bool
+dmnsn_vector_isnan(dmnsn_vector v)
+{
+ return dmnsn_isnan(v.x) || dmnsn_isnan(v.y) || dmnsn_isnan(v.z);
+}