// // Vec3.h // libraries/script-engine/src // // Created by Brad Hefta-Gaub on 1/29/14. // Copyright 2014 High Fidelity, Inc. // Copyright 2023 Overte e.V. // // Scriptable Vec3 class library. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // /// @addtogroup ScriptEngine /// @{ #pragma once #ifndef hifi_Vec3_h #define hifi_Vec3_h #include #include #include "GLMHelpers.h" #include "Scriptable.h" /*@jsdoc * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. Overte uses a * right-handed Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. * Overte coordinate system * * @namespace Vec3 * @variation 0 * * @hifi-interface * @hifi-client-entity * @hifi-avatar * @hifi-server-entity * @hifi-assignment-client * * @property {Vec3} UNIT_X - { x: 1, y: 0, z: 0 } : Unit vector in the x-axis direction. Read-only. * @property {Vec3} UNIT_Y - { x: 0, y: 1, z: 0 } : Unit vector in the y-axis direction. Read-only. * @property {Vec3} UNIT_Z - { x: 0, y: 0, z: 1 } : Unit vector in the z-axis direction. Read-only. * @property {Vec3} UNIT_NEG_X - { x: -1, y: 0, z: 0 } : Unit vector in the negative x-axis direction. * Read-only. * @property {Vec3} UNIT_NEG_Y - { x: 0, y: -1, z: 0 } : Unit vector in the negative y-axis direction. * Read-only. * @property {Vec3} UNIT_NEG_Z - { x: 0, y: 0, z: -1 } : Unit vector in the negative z-axis direction. * Read-only. * @property {Vec3} UNIT_XY - { x: 0.707107, y: 0.707107, z: 0 } : Unit vector in the direction of the diagonal * between the x and y axes. Read-only. * @property {Vec3} UNIT_XZ - { x: 0.707107, y: 0, z: 0.707107 } : Unit vector in the direction of the diagonal * between the x and z axes. Read-only. * @property {Vec3} UNIT_YZ - { x: 0, y: 0.707107, z: 0.707107 } : Unit vector in the direction of the diagonal * between the y and z axes. Read-only. * @property {Vec3} UNIT_XYZ - { x: 0.577350, y: 0.577350, z: 0.577350 } : Unit vector in the direction of the * diagonal between the x, y, and z axes. Read-only. * @property {Vec3} FLOAT_MAX - { x: 3.402823e+38, y: 3.402823e+38, z: 3.402823e+38 } : Vector with all axis * values set to the maximum floating point value. Read-only. * @property {Vec3} FLOAT_MIN - { x: -3.402823e+38, y: -3.402823e+38, z: -3.402823e+38 } : Vector with all axis * values set to the negative of the maximum floating point value. Read-only. * @property {Vec3} ZERO - { x: 0, y: 0, z: 0 } : Vector with all axis values set to 0. * Read-only. * @property {Vec3} ONE - { x: 1, y: 1, z: 1 } : Vector with all axis values set to 1. * Read-only. * @property {Vec3} TWO - { x: 2, y: 2, z: 2 } : Vector with all axis values set to 2. * Read-only. * @property {Vec3} HALF - { x: 0.5, y: 0.5, z: 0.5 } : Vector with all axis values set to 0.5. * Read-only. * @property {Vec3} RIGHT - { x: 1, y: 0, z: 0 } : Unit vector in the "right" direction. Synonym for * UNIT_X. Read-only. * @property {Vec3} UP - { x: 0, y: 1, z: 0 } : Unit vector in the "up" direction. Synonym for * UNIT_Y. Read-only. * @property {Vec3} FRONT - { x: 0, y: 0, z: -1 } : Unit vector in the "front" direction. Synonym for * UNIT_NEG_Z. Read-only. */ /// Provides the Vec3 scripting interface class Vec3 : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Z READ UNIT_Z CONSTANT) Q_PROPERTY(glm::vec3 UNIT_NEG_X READ UNIT_NEG_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_NEG_Y READ UNIT_NEG_Y CONSTANT) Q_PROPERTY(glm::vec3 UNIT_NEG_Z READ UNIT_NEG_Z CONSTANT) Q_PROPERTY(glm::vec3 UNIT_XY READ UNIT_XY CONSTANT) Q_PROPERTY(glm::vec3 UNIT_XZ READ UNIT_XZ CONSTANT) Q_PROPERTY(glm::vec3 UNIT_YZ READ UNIT_YZ CONSTANT) Q_PROPERTY(glm::vec3 UNIT_XYZ READ UNIT_XYZ CONSTANT) Q_PROPERTY(glm::vec3 FLOAT_MAX READ FLOAT_MAX CONSTANT) Q_PROPERTY(glm::vec3 FLOAT_MIN READ FLOAT_MIN CONSTANT) Q_PROPERTY(glm::vec3 ZERO READ ZERO CONSTANT) Q_PROPERTY(glm::vec3 ONE READ ONE CONSTANT) Q_PROPERTY(glm::vec3 TWO READ TWO CONSTANT) Q_PROPERTY(glm::vec3 HALF READ HALF CONSTANT) Q_PROPERTY(glm::vec3 RIGHT READ RIGHT CONSTANT) Q_PROPERTY(glm::vec3 UP READ UP CONSTANT) Q_PROPERTY(glm::vec3 FRONT READ FRONT CONSTANT) public slots: /*@jsdoc * Calculates the reflection of a vector in a plane. * @function Vec3(0).reflect * @param {Vec3} v - The vector to reflect. * @param {Vec3} normal - The normal of the plane. * @returns {Vec3} The vector reflected in the plane given by the normal. * @example Reflect a vector in the x-z plane. * var v = { x: 1, y: 2, z: 3 }; * var normal = Vec3.UNIT_Y; * var reflected = Vec3.reflect(v, normal); * print(JSON.stringify(reflected)); // {"x":1,"y":-2,"z":3} */ glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } /*@jsdoc * Calculates the cross product of two vectors. * @function Vec3(0).cross * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {Vec3} The cross product of v1 and v2. * @example The cross product of x and y unit vectors is the z unit vector. * var v1 = { x: 1, y: 0, z: 0 }; * var v2 = { x: 0, y: 1, z: 0 }; * var crossProduct = Vec3.cross(v1, v2); * print(JSON.stringify(crossProduct)); // {"x":0,"y":0,"z":1} */ glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1, v2); } /*@jsdoc * Calculates the dot product of two vectors. * @function Vec3(0).dot * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {number} The dot product of v1 and v2. * @example The dot product of vectors at right angles is 0. * var v1 = { x: 1, y: 0, z: 0 }; * var v2 = { x: 0, y: 1, z: 0 }; * var dotProduct = Vec3.dot(v1, v2); * print(dotProduct); // 0 */ float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } /*@jsdoc * Multiplies a vector by a scale factor. * @function Vec3(0).multiply * @param {Vec3} v - The vector. * @param {number} scale - The scale factor. * @returns {Vec3} The vector with each ordinate value multiplied by the scale. */ glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } /*@jsdoc * Multiplies a vector by a scale factor. * @function Vec3(0).multiply * @param {number} scale - The scale factor. * @param {Vec3} v - The vector. * @returns {Vec3} The vector with each ordinate value multiplied by the scale. */ glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } /*@jsdoc * Multiplies two vectors. * @function Vec3(0).multiplyVbyV * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {Vec3} A vector formed by multiplying the ordinates of two vectors: { x: v1.x * v2.x, y: v1.y * v2.y, * z: v1.z * v2.z }. * @example Multiply two vectors. * var v1 = { x: 1, y: 2, z: 3 }; * var v2 = { x: 1, y: 2, z: 3 }; * var multiplied = Vec3.multiplyVbyV(v1, v2); * print(JSON.stringify(multiplied)); // {"x":1,"y":4,"z":9} */ glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; } /*@jsdoc * Rotates a vector. * @function Vec3(0).multiplyQbyV * @param {Quat} q - The rotation to apply. * @param {Vec3} v - The vector to rotate. * @returns {Vec3} v rotated by q. * @example Rotate the negative z-axis by 90 degrees about the x-axis. * var v = Vec3.UNIT_NEG_Z; * var q = Quat.fromPitchYawRollDegrees(90, 0, 0); * var result = Vec3.multiplyQbyV(q, v); * print(JSON.stringify(result)); // {"x":0,"y":1.000,"z":1.19e-7} */ glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } /*@jsdoc * Calculates the sum of two vectors. * @function Vec3(0).sum * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {Vec3} The sum of the two vectors. */ glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } /*@jsdoc * Calculates one vector subtracted from another. * @function Vec3(0).subtract * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {Vec3} The second vector subtracted from the first. */ glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } /*@jsdoc * Calculates the length of a vector * @function Vec3(0).length * @param {Vec3} v - The vector. * @returns {number} The length of the vector. */ float length(const glm::vec3& v) { return glm::length(v); } /*@jsdoc * Calculates the distance between two points. * @function Vec3(0).distance * @param {Vec3} p1 - The first point. * @param {Vec3} p2 - The second point. * @returns {number} The distance between the two points. * @example The distance between two points is aways positive. * var p1 = { x: 0, y: 0, z: 0 }; * var p2 = { x: 0, y: 0, z: 10 }; * var distance = Vec3.distance(p1, p2); * print(distance); // 10 * * p2 = { x: 0, y: 0, z: -10 }; * distance = Vec3.distance(p1, p2); * print(distance); // 10 */ float distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } /*@jsdoc * Calculates the angle of rotation from one vector onto another, with the sign depending on a reference vector. * @function Vec3(0).orientedAngle * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @param {Vec3} ref - Reference vector. * @returns {number} The angle of rotation from the first vector to the second, in degrees. The value is positive if the * rotation axis aligns with the reference vector (has a positive dot product), otherwise the value is negative. * @example Compare Vec3.getAngle() and Vec3.orientedAngle(). * var v1 = { x: 5, y: 0, z: 0 }; * var v2 = { x: 5, y: 0, z: 5 }; * * var angle = Vec3.getAngle(v1, v2); * print(angle * 180 / Math.PI); // 45 * * print(Vec3.orientedAngle(v1, v2, Vec3.UNIT_Y)); // -45 * print(Vec3.orientedAngle(v1, v2, Vec3.UNIT_NEG_Y)); // 45 * print(Vec3.orientedAngle(v1, v2, { x: 1, y: 2, z: -1 })); // -45 * print(Vec3.orientedAngle(v1, v2, { x: 1, y: -2, z: -1 })); // 45 */ float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); /*@jsdoc * Normalizes a vector so that its length is 1. * @function Vec3(0).normalize * @param {Vec3} v - The vector to normalize. * @returns {Vec3} The vector normalized to have a length of 1. * @example Normalize a vector. * var v = { x: 10, y: 10, z: 0 }; * var normalized = Vec3.normalize(v); * print(JSON.stringify(normalized)); // {"x":0.7071,"y":0.7071,"z":0} * print(Vec3.length(normalized)); // 1 */ glm::vec3 normalize(const glm::vec3& v) { return glm::normalize(v); }; /*@jsdoc * Calculates a linear interpolation between two vectors. * @function Vec3(0).mix * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @param {number} factor - The interpolation factor, range 0.01.0. * @returns {Vec3} The linear interpolation between the two vectors: (1 - factor) * v1 + factor * v2. * @example Linear interpolation between two vectors. * var v1 = { x: 10, y: 0, z: 0 }; * var v2 = { x: 0, y: 10, z: 0 }; * var interpolated = Vec3.mix(v1, v2, 0.75); // 1/4 of v1 and 3/4 of v2. * print(JSON.stringify(interpolated)); // {"x":2.5,"y":7.5","z":0} */ glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m) { return glm::mix(v1, v2, m); } /*@jsdoc * Prints the vector to the program log, as a text label followed by the vector value. * @function Vec3(0).print * @param {string} label - The label to print. * @param {Vec3} v - The vector value to print. * @example Two ways of printing a label and vector value. * var v = { x: 1, y: 2, z: 3 }; * Vec3.print("Vector: ", v); // dvec3(1.000000, 2.000000, 3.000000) * print("Vector: " + JSON.stringify(v)); // {"x":1,"y":2,"z":3} */ void print(const QString& label, const glm::vec3& v); /*@jsdoc * Tests whether two vectors are equal. *

Note: The vectors must be exactly equal in order for true to be returned; it is often * better to use {@link Vec3(0).withinEpsilon|withinEpsilon}.

* @function Vec3(0).equal * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {boolean} true if the two vectors are exactly equal, otherwise false. * @example Vectors are only equal if exactly the same. * var v1 = { x: 10, y: 10, z: 10 }; * var v2 = { x: 10, y: 10, z: 10 }; * * var equal = Vec3.equal(v1, v2); * print(equal); // true * * v2 = { x: 10, y: 10, z: 10.0005 }; * equal = Vec3.equal(v1, v2); * print(equal); // false */ bool equal(const glm::vec3& v1, const glm::vec3& v2) { return v1 == v2; } /*@jsdoc * Tests whether two vectors are equal within a tolerance. *

Note: It is often better to use this function than {@link Vec3(0).equal|equal}.

* @function Vec3(0).withinEpsilon * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @param {number} epsilon - The maximum distance between the two vectors. * @returns {boolean} true if the distance between the points represented by the vectors is less than or equal * to epsilon, otherwise false. * @example Testing vectors for near equality. * var v1 = { x: 10, y: 10, z: 10 }; * var v2 = { x: 10, y: 10, z: 10.0005 }; * * var equal = Vec3.equal(v1, v2); * print(equal); // false * * equal = Vec3.withinEpsilon(v1, v2, 0.001); * print(equal); // true */ bool withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon); /*@jsdoc * Calculates polar coordinates (elevation, azimuth, radius) that transform the unit z-axis vector onto a point. * @function Vec3(0).toPolar * @param {Vec3} p - The point to calculate the polar coordinates for. * @returns {Vec3} Vector of polar coordinates for the point: x elevation rotation about the x-axis in * radians, y azimuth rotation about the y-axis in radians, and z radius. * @example Polar coordinates for a point. * var v = { x: 5, y: 2.5, z: 5 }; * var polar = Vec3.toPolar(v); * print("Elevation: " + polar.x * 180 / Math.PI); // -19.471 * print("Azimuth: " + polar.y * 180 / Math.PI); // 45 * print("Radius: " + polar.z); // 7.5 */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation glm::vec3 toPolar(const glm::vec3& v); /*@jsdoc * Calculates the coordinates of a point from polar coordinate transformation of the unit z-axis vector. * @function Vec3(0).fromPolar * @param {Vec3} polar - The polar coordinates of a point: x elevation rotation about the x-axis in radians, * y azimuth rotation about the y-axis in radians, and z radius. * @returns {Vec3} The coordinates of the point. * @example Polar coordinates to Cartesian. * var polar = { x: -19.471 * Math.PI / 180, y: 45 * Math.PI / 180, z: 7.5 }; * var p = Vec3.fromPolar(polar); * print(JSON.stringify(p)); // {"x":5,"y":2.5,"z":5} */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation glm::vec3 fromPolar(const glm::vec3& polar); /*@jsdoc * Calculates the unit vector corresponding to polar coordinates elevation and azimuth transformation of the unit z-axis * vector. * @function Vec3(0).fromPolar * @param {number} elevation - Rotation about the x-axis, in radians. * @param {number} azimuth - Rotation about the y-axis, in radians. * @returns {Vec3} Unit vector for the direction specified by elevation and azimuth. * @example Polar coordinates to Cartesian coordinates. * var elevation = -19.471 * Math.PI / 180; * var rotation = 45 * Math.PI / 180; * var p = Vec3.fromPolar(elevation, rotation); * print(JSON.stringify(p)); // {"x":0.667,"y":0.333,"z":0.667} * p = Vec3.multiply(7.5, p); * print(JSON.stringify(p)); // {"x":5,"y":2.5,"z":5} */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation glm::vec3 fromPolar(float elevation, float azimuth); /*@jsdoc * Calculates the angle between two vectors. * @function Vec3(0).getAngle * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {number} The angle between the two vectors, in radians. * @example Calculate the angle between two vectors. * var v1 = { x: 10, y: 0, z: 0 }; * var v2 = { x: 0, y: 0, z: 10 }; * var angle = Vec3.getAngle(v1, v2); * print(angle * 180 / Math.PI); // 90 */ float getAngle(const glm::vec3& v1, const glm::vec3& v2); private: const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } const glm::vec3& UNIT_NEG_X() { return Vectors::UNIT_NEG_X; } const glm::vec3& UNIT_NEG_Y() { return Vectors::UNIT_NEG_Y; } const glm::vec3& UNIT_NEG_Z() { return Vectors::UNIT_NEG_Z; } const glm::vec3& UNIT_XY() { return Vectors::UNIT_XY; } const glm::vec3& UNIT_XZ() { return Vectors::UNIT_XZ; } const glm::vec3& UNIT_YZ() { return Vectors::UNIT_YZ; } const glm::vec3& UNIT_XYZ() { return Vectors::UNIT_XYZ; } const glm::vec3& FLOAT_MAX() { return Vectors::MAX; } const glm::vec3& FLOAT_MIN() { return Vectors::MIN; } const glm::vec3& ZERO() { return Vectors::ZERO; } const glm::vec3& ONE() { return Vectors::ONE; } const glm::vec3& TWO() { return Vectors::TWO; } const glm::vec3& HALF() { return Vectors::HALF; } const glm::vec3& RIGHT() { return Vectors::RIGHT; } const glm::vec3& UP() { return Vectors::UP; } const glm::vec3& FRONT() { return Vectors::FRONT; } public: virtual ~Vec3(); }; #endif // hifi_Vec3_h /// @}