diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index c419749c26..635f2a530c 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -22,7 +22,7 @@ #include "GLMHelpers.h" /**jsdoc - * A 3-dimensional vector. + * A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. * * @typedef {object} Vec3 * @property {number} x - X-coordinate of the vector. @@ -31,7 +31,7 @@ */ /**jsdoc - * A color vector. + * A color vector. See also the {@link Vec3(0)|Vec3} object. * * @typedef {object} Vec3Color * @property {number} x - Red component value. Integer in the range 0 - 255. @@ -39,6 +39,51 @@ * @property {number} z - Blue component value. Integer in the range 0 - 255. */ +/**jsdoc + * The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed + * Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. + * High Fidelity coordinate system + * + * @namespace Vec3 + * @variation 0 + * @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. + */ + /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API class Vec3 : public QObject, protected QScriptable { Q_OBJECT @@ -63,27 +108,301 @@ class Vec3 : public QObject, protected QScriptable { Q_PROPERTY(glm::vec3 FRONT READ FRONT CONSTANT) public slots: + + /**jsdoc + * Calculate 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 + * Calculate 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 + * Calculate 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 + * Multiply 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 + * Multiply 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 + * Multiply 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 + * Rotate 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 + * Calculate 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 + * Calculate 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 + * Calculate 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 + * Calculate 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 + * Calculate 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, with a positive sign if the + * rotation axis aligns with the reference vector (has a positive dot product) otherwise a negative sign. + * @example Compare Vec3.angle() 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 + * Normalize 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 + * Calculate 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 in the range 0.0 to 1.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 + * Print to the program log a text label followed by a 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 + * Test 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 + * Test 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 the 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 + * Calculate 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 scale. + * @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 + * Calculate 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 scale. + * @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 + * Calculate 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. + * 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 + * Calculate 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: