/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.arakhne.afc.math.geometry.d3; import org.eclipse.xtext.xbase.lib.Inline; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.math.MathConstants; import org.arakhne.afc.math.MathUtil; import org.arakhne.afc.math.geometry.coordinatesystem.CoordinateSystem3D; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** 3D Vector. * * @param <RV> is the type of vector that can be returned by this tuple. * @param <RP> is the type of point that can be returned by this tuple. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public interface Vector3D<RV extends Vector3D<? super RV, ? super RP>, RP extends Point3D<? super RP, ? super RV>> extends Tuple3D<RV> { /** * Replies if the vector is a unit vector. * * <p>Due to the precision on floating-point computations, the test of unit-vector * must consider that the norm of the given vector is approximatively equal * to 1. The precision (i.e. the number of significant decimals) is given * by {@link MathConstants#UNIT_VECTOR_EPSILON}. * * @param x is the X coordinate of the vector. * @param y is the Y coordinate of the vector. * @param z is the Z coordinate of the vector. * @return <code>true</code> if the two given vectors are colinear. * @see MathUtil#isEpsilonEqual(double, double, double) * @see MathConstants#UNIT_VECTOR_EPSILON * @see #isUnitVector(double, double, double, double) */ @Pure @Inline(value = "(Vector3D.isUnitVector($1, $2, $3, MathConstants.UNIT_VECTOR_EPSILON))", imported = {Vector3D.class, MathConstants.class}) static boolean isUnitVector(double x, double y, double z) { return isUnitVector(x, y, z, MathConstants.UNIT_VECTOR_EPSILON); } /** * Replies if the vector is a unit vector. * * <p>Due to the precision on floating-point computations, the test of unit-vector * must consider that the norm of the given vector is approximatively equal * to 1. The precision (i.e. the number of significant decimals) is given * by <code>epsilon</code>. * * @param x is the X coordinate of the vector. * @param y is the Y coordinate of the vector. * @param z is the Z coordinate of the vector. * @param epsilon the precision distance to assumed for equality. * @return <code>true</code> if the two given vectors are colinear. * @since 13.0 * @see MathUtil#isEpsilonEqual(double, double, double) * @see #isUnitVector(double, double, double) */ @Pure @Inline(value = "(MathUtil.isEpsilonEqual($1 * $1 + $2 * $2 + $3 * $3, 1., $4))", imported = {MathUtil.class}) static boolean isUnitVector(double x, double y, double z, double epsilon) { return MathUtil.isEpsilonEqual(x * x + y * y + z * z, 1., epsilon); } /** Replies if this first is a unit vector. * A unit vector has a length equal to 1. * * <p>This function approximates the test on the length of the vector. * This approximation could be based on {@link MathUtil#isEpsilonEqual(double, double)}. * * @return <code>true</code> if the vector has a length equal to 1. * <code>false</code> otherwise. */ @Pure default boolean isUnitVector() { return isUnitVector(getX(), getY(), getZ()); } /** Compute the determinant of three vectors. * * @param x1 * is the X coordinate of the first vector * @param y1 * is the Y coordinate of the first vector * @param z1 * is the Z coordinate of the first vector * @param x2 * is the X coordinate of the second vector * @param y2 * is the Y coordinate of the second vector * @param z2 * is the Z coordinate of the second vector * @param x3 * is the X coordinate of the third vector * @param y3 * is the Y coordinate of the third vector * @param z3 * is the Z coordinate of the third vector * @return the determinant * @see #perpProduct(double, double, double, double, double, double) */ @Pure @SuppressWarnings("checkstyle:parameternumber") static double determinant(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3) { return x1 * (y2 * z3 - y3 * z2) + x2 * (y3 * z1 - y1 * z3) + x3 * (y1 * z2 - y2 * z1); } /** Compute the determinant of two vectors. * * <p><pre><code>det(X1,X2) = |X1|.|X2|.sin(a)</code></pre> * where <code>X1</code> and <code>X2</code> are two vectors * and <code>a</code> is the angle between <code>X1</code> * and <code>X2</code>. * * @param x1 * is the X coordinate of the first vector * @param y1 * is the Y coordinate of the first vector * @param z1 * is the Z coordinate of the first vector * @param x2 * is the X coordinate of the second vector * @param y2 * is the Y coordinate of the second vector * @param z2 * is the Z coordinate of the second vector * @return the determinant * @see #determinant(double, double, double, double, double, double, double, double, double) */ @Pure static double perpProduct(double x1, double y1, double z1, double x2, double y2, double z2) { /* First method: * * det(A,B) = |A|.|B|.sin(theta) * A x B = |A|.|B|.sin(theta).N, where N is the unit vector * A x B = det(A,B).N * A x B = [ y1*z2 - z1*y2 ] = det(A,B).N * [ z1*x2 - x1*z2 ] * [ x1*y2 - y1*x2 ] * det(A,B) = sum(A x B) * * Second method: * * det(A,B) = det( [ x1 x2 1 ] * [ y1 y2 1 ] * [ z1 z2 1 ] ) * det(A,B) = x1*y2*1 + y1*z2*1 + z1*x2*1 - 1*y2*z1 - 1*z2*x1 - 1*x2*y1 */ return x1 * y2 + y1 * z2 + z1 * x2 - y2 * z1 - z2 * x1 - x2 * y1; } /** * Replies if two vectors are colinear. * * <p>This function uses the test {@link MathUtil#isEpsilonZero(double)}. * * @param x1 * is the X coordinate of the first vector * @param y1 * is the Y coordinate of the first vector * @param z1 * is the Z coordinate of the first vector * @param x2 * is the X coordinate of the second vector * @param y2 * is the Y coordinate of the second vector * @param z2 * is the Z coordinate of the second vector * @return <code>true</code> if the two given vectors are colinear. * @since 3.0 * @see MathUtil#isEpsilonZero(double) */ @Pure static boolean isCollinearVectors(double x1, double y1, double z1, double x2, double y2, double z2) { // Cross product final double cx = y1 * z2 - z1 * y2; final double cy = z1 * x2 - x1 * z2; final double cz = x1 * y2 - y1 * x2; return MathUtil.isEpsilonZero(cx * cx + cy * cy + cz * cz); } /** Compute the dot product of two vectors. * * @param x1 x coordinate of the first vector. * @param y1 y coordinate of the first vector. * @param z1 y coordinate of the first vector. * @param x2 x coordinate of the second vector. * @param y2 y coordinate of the second vector. * @param z2 z coordinate of the second vector. * @return the dot product. */ @Pure @Inline(value = "($1) * ($4) + ($2) * ($5) + ($3) * ($6)") static double dotProduct(double x1, double y1, double z1, double x2, double y2, double z2) { return x1 * x2 + y1 * y2 + z1 * z2; } /** * Computes the cross product of the vectors v1 and v2. * This function uses the * {@link #crossProductLeftHand(double, double, double, double, double, double, Vector3D) left-handed cross product} * if the default coordinate system is left-handed. Otherwise, it uses the * {@link #crossProductRightHand(double, double, double, double, double, double, Vector3D) right-handed cross product}. * The default coordinate system is given by * {@link CoordinateSystem3D#getDefaultCoordinateSystem()}. * * <img src="doc-files/left_handed_cross_product.png" alt="[Left-Handed Cross Product]"> * <img src="doc-files/left_handed_cross_product.png" alt="[Right-Handed Cross Product]"> * * @param x1 x coordinate of the vector v1. * @param y1 y coordinate of the vector v1. * @param z1 z coordinate of the vector v1. * @param x2 x coordinate of the vector v2. * @param y2 y coordinate of the vector v2. * @param z2 z coordinate of the vector v2. * @param result is the result of the cross product. */ static void crossProduct( double x1, double y1, double z1, double x2, double y2, double z2, Vector3D<?, ?> result) { crossProduct(x1, y1, z1, x2, y2, z2, CoordinateSystem3D.getDefaultCoordinateSystem(), result); } /** * Computes the cross product of the vectors v1 and v2. * This function uses the * {@link #crossProductLeftHand(double, double, double, double, double, double, Vector3D) left-handed cross product} * if the given coordinate system is left-handed. Otherwise, it uses the * {@link #crossProductRightHand(double, double, double, double, double, double, Vector3D) right-handed cross product}. * * <img src="doc-files/left_handed_cross_product.png" alt="[Left-Handed Cross Product]"> * <img src="doc-files/left_handed_cross_product.png" alt="[Right-Handed Cross Product]"> * * @param x1 x coordinate of the vector v1. * @param y1 y coordinate of the vector v1. * @param z1 z coordinate of the vector v1. * @param x2 x coordinate of the vector v2. * @param y2 y coordinate of the vector v2. * @param z2 z coordinate of the vector v2. * @param system the coordinate system to consider for computing the cross product. * @param result is the result of the cross product. */ static void crossProduct( double x1, double y1, double z1, double x2, double y2, double z2, CoordinateSystem3D system, Vector3D<?, ?> result) { if (system.isLeftHanded()) { crossProductLeftHand( x1, y1, z1, x2, y2, z2, result); } else { crossProductRightHand( x1, y1, z1, x2, y2, z2, result); } } /** * Computes the cross product of the vectors v1 and v2 * as if the vectors are inside a left-hand coordinate system. * * <img src="doc-files/left_handed_cross_product.png"> * * @param x1 x coordinate of the vector v1. * @param y1 y coordinate of the vector v1. * @param z1 z coordinate of the vector v1. * @param x2 x coordinate of the vector v2. * @param y2 y coordinate of the vector v2. * @param z2 z coordinate of the vector v2. * @param result is the result of the cross product. */ static void crossProductLeftHand( double x1, double y1, double z1, double x2, double y2, double z2, Vector3D<?, ?> result) { final double x = y2 * z1 - z2 * y1; final double y = z2 * x1 - x2 * z1; final double z = x2 * y1 - y2 * x1; result.set(x, y, z); } /** * Computes the cross product of the vectors v1 and v2 * as if the vectors are inside a right-hand coordinate system. * * <img src="doc-files/right_handed_cross_product.png"> * * @param x1 x coordinate of the vector v1. * @param y1 y coordinate of the vector v1. * @param z1 z coordinate of the vector v1. * @param x2 x coordinate of the vector v2. * @param y2 y coordinate of the vector v2. * @param z2 z coordinate of the vector v2. * @param result is the result of the cross product. */ static void crossProductRightHand( double x1, double y1, double z1, double x2, double y2, double z2, Vector3D<?, ?> result) { final double x = y1 * z2 - z1 * y2; final double y = z1 * x2 - x1 * z2; final double z = x1 * y2 - y1 * x2; result.set(x, y, z); } /** * Compute the signed angle between two vectors. * * @param x1 * is the X coordinate of the first vector * @param y1 * is the Y coordinate of the first vector * @param z1 * is the Z coordinate of the first vector * @param x2 * is the X coordinate of the second vector * @param y2 * is the Y coordinate of the second vector * @param z2 * is the Z coordinate of the second vector * @return the angle between <code>-PI</code> and <code>PI</code>. */ @Pure static double signedAngle(double x1, double y1, double z1, double x2, double y2, double z2) { final double lengths = Math.sqrt(x1 * x1 + y1 * y1 + z1 * z1) * Math.sqrt(x2 * x2 + y2 * y2 + z2 * z2); if (lengths == 0.) { return Double.NaN; } // First method // Angle // A . B = |A|.|B|.cos(theta) double dot = dotProduct(x1, y1, z1, x2, y2, z2) / lengths; if (dot < -1.0) { dot = -1.0; } if (dot > 1.0) { dot = 1.0; } double angle = Math.acos(dot); // On which side of A, B is located? if ((dot > -1) && (dot < 1)) { // det(A,B) = |A|.|B|.sin(theta) dot = perpProduct(x1, y1, z1, x2, y2, z2) / lengths; if (dot < 0) { angle = -angle; } } return angle; } /** Compute a signed angle between this vector and the given vector. * * <p>The signed angle between this vector and the given {@code vector} * is the rotation angle to apply to this vector * to be colinear to the given {@code vector} and pointing the * same demi-plane. It means that the angle replied * by this function is be negative if the rotation * to apply is clockwise, and positive if * the rotation is counterclockwise. * * <p>The value replied by {@link #angle(Vector3D)} * is the absolute value of the vlaue replied by this * function. * * @param vector is the vector to reach. * @return the rotation angle to turn this vector to reach * {@code v}. */ @Pure default double signedAngle(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return signedAngle(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ()); } /** * Sets the value of this tuple to the sum of tuples t1 and t2. * @param vector1 the first tuple * @param vector2 the second tuple */ default void add(Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(vector1.getX() + vector2.getX(), vector1.getY() + vector2.getY(), vector1.getZ() + vector2.getZ()); } /** * Sets the value of this tuple to the sum of itself and t1. * @param vector the other tuple */ default void add(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(getX() + vector.getX(), getY() + vector.getY(), getZ() + vector.getZ()); } /** * Sets the value of this tuple to the scalar multiplication * of tuple t1 plus tuple t2 (this = s*t1 + t2). * @param scale the scalar value * @param vector1 the tuple to be multipled * @param vector2 the tuple to be added */ default void scaleAdd(int scale, Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(scale * vector1.getX() + vector2.getX(), scale * vector1.getY() + vector2.getY(), scale * vector1.getZ() + vector2.getY()); } /** * Sets the value of this tuple to the scalar multiplication * of tuple t1 plus tuple t2 (this = s*t1 + t2). * @param scale the scalar value * @param vector1 the tuple to be multipled * @param vector2 the tuple to be added */ default void scaleAdd(double scale, Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(scale * vector1.getX() + vector2.getX(), scale * vector1.getY() + vector2.getY(), scale * vector1.getZ() + vector2.getY()); } /** * Sets the value of this tuple to the scalar multiplication * of itself and then adds tuple t1 (this = s*this + t1). * @param scale the scalar value * @param vector the tuple to be added */ default void scaleAdd(int scale, Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(scale * getX() + vector.getX(), scale * getY() + vector.getY(), scale * getZ() + vector.getZ()); } /** * Sets the value of this tuple to the scalar multiplication * of itself and then adds tuple t1 (this = s*this + t1). * @param scale the scalar value * @param vector the tuple to be added */ default void scaleAdd(double scale, Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(scale * getX() + vector.getX(), scale * getY() + vector.getY(), scale * getZ() + vector.getZ()); } /** * Sets the value of this tuple to the difference * of tuples t1 and t2 (this = t1 - t2). * @param vector1 the first tuple * @param vector2 the second tuple */ default void sub(Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(vector1.getX() - vector2.getX(), vector1.getY() - vector2.getY(), vector1.getZ() - vector2.getZ()); } /** * Sets the value of this tuple to the difference * of tuples t1 and t2 (this = t1 - t2). * @param point1 the first tuple * @param point2 the second tuple */ default void sub(Point3D<?, ?> point1, Point3D<?, ?> point2) { assert point1 != null : AssertMessages.notNullParameter(0); assert point2 != null : AssertMessages.notNullParameter(1); set(point1.getX() - point2.getX(), point1.getY() - point2.getY(), point1.getZ() - point2.getZ()); } /** * Sets the value of this tuple to the difference * of itself and t1 (this = this - t1). * @param vector the other tuple */ default void sub(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(getX() - vector.getX(), getY() - vector.getY(), getZ() - vector.getZ()); } /** Compute the power of this vector. * * <p>If the power is even, the result is a scalar. * If the power is odd, the result is a vector. * * @param power the power factor. * @return the power of this vector. * @see "http://www.euclideanspace.com/maths/algebra/vectors/vecAlgebra/powers/index.htm" */ @Pure default PowerResult<RV> power(int power) { final boolean isEven = power % 2 == 0; final int evenPower; if (isEven) { evenPower = power / 2; } else { evenPower = MathUtil.sign(power) * (Math.abs(power) - 1) / 2; } final double x = getX(); final double y = getY(); final double z = getZ(); final double dot = dotProduct(x, y, z, x, y, z); final double resultForEven = Math.pow(dot, evenPower); if (isEven) { return new PowerResult<>(resultForEven); } final RV r = getGeomFactory().newVector(getX() * resultForEven, getY() * resultForEven, getZ() * resultForEven); return new PowerResult<>(r); } /** * Computes the dot product of the this vector and vector v1. * @param vector the other vector * @return the dot product. */ @Pure default double dot(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return dotProduct(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ()); } /** Compute the determinant of two vectors. * * <pre><code>det(this, V) = |this|.|V|.sin(a)</code></pre> * where <code>this</code> and <code>V</code> are two vectors * and <code>a</code> is the angle between <code>this</code> * and <code>V</code>. * * @param vector the vector. * @return the perp product. */ @Pure default double perp(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return perpProduct(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ()); } /** * Computes the cross product of the this vector and vector v1. * The coordinate system's standard depends on the underlying * implementation of the API. * One of {@link #crossLeftHand(Vector3D)} or {@link #crossRightHand(Vector3D)} * will be invoked by this function. * * <p><img src="doc-files/left_handed_cross_product.png" alt="[Left-Handed Cross Product]"> * * <p><img src="doc-files/right_handed_cross_product.png" alt="[Right-Handed Cross Product]"> * * @param vector the other vector. * @return the cross product. * @see #crossLeftHand(Vector3D) * @see #crossRightHand(Vector3D) */ @Pure default RV cross(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); final RV result = getGeomFactory().newVector(); crossProduct(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ(), result); return result; } /** * Computes the cross product of the vectors v1 and v2 and * put the result in this vector. * The coordinate system's standard depends on the underlying * implementation of the API. * One of {@link #crossLeftHand(Vector3D, Vector3D)} or * {@link #crossRightHand(Vector3D, Vector3D)} * will be invoked by this function. * * <p><img src="doc-files/left_handed_cross_product.png" alt="[Left-Handed Cross Product]"> * * <p><img src="doc-files/right_handed_cross_product.png" alt="[Right-Handed Cross Product]"> * * @param vector1 the left operand. * @param vector2 the right operand. * @see #crossLeftHand(Vector3D, Vector3D) * @see #crossRightHand(Vector3D, Vector3D) */ default void cross(Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); crossProduct(vector1.getX(), vector1.getY(), vector1.getZ(), vector2.getX(), vector2.getY(), vector2.getZ(), this); } /** * Computes the cross product of the this vector and vector v1 * as if the vectors are inside a left-hand coordinate system. * * <p><img alt="" src="doc-files/left_handed_cross_product.png"> * * @param vector the other vector * @return the dot product. */ @Pure default RV crossLeftHand(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); final RV result = getGeomFactory().newVector(); crossProductLeftHand(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ(), result); return result; } /** * Computes the cross product of the vectors v1 and v2 * as if the vectors are inside a left-hand coordinate system; * and put the result in this vector. * * <p><img alt="" src="doc-files/left_handed_cross_product.png"> * * @param vector1 the left operand. * @param vector2 the right operand. */ default void crossLeftHand(Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); crossProductLeftHand(vector1.getX(), vector1.getY(), vector1.getZ(), vector2.getX(), vector2.getY(), vector2.getZ(), this); } /** * Computes the cross product of the this vector and vector v1 * as if the vectors are inside a left-hand coordinate system. * * <p><img alt="" src="doc-files/right_handed_cross_product.png"> * * @param vector the other vector * @return the dot product. */ @Pure default Vector3D<?, ?> crossRightHand(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); final RV result = getGeomFactory().newVector(); crossProductRightHand(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ(), result); return result; } /** * Computes the cross product of the vectors v1 and v2 * as if the vectors are inside a left-hand coordinate system; * and put the result in this vector. * * <p><img alt="" src="doc-files/right_handed_cross_product.png"> * * @param vector1 the left operand * @param vector2 the right operand */ default void crossRightHand(Vector3D<?, ?> vector1, Vector3D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); crossProductRightHand(vector1.getX(), vector1.getY(), vector1.getZ(), vector2.getX(), vector2.getY(), vector2.getZ(), this); } /** * Returns the length of this vector. * @return the length of this vector */ @Pure default double getLength() { final double x = getX(); final double y = getY(); final double z = getZ(); return Math.sqrt(x * x + y * y + z * z); } /** * Returns the squared length of this vector. * @return the squared length of this vector */ @Pure default double getLengthSquared() { final double x = getX(); final double y = getY(); final double z = getZ(); return x * x + y * y + z * z; } /** * Sets the value of this vector to the normalization of vector v1. * @param vector the un-normalized vector */ default void normalize(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); final double x = vector.getX(); final double y = vector.getY(); final double z = vector.getZ(); double sqlength = x * x + y * y + z * z; if (sqlength != 0.) { sqlength = Math.sqrt(sqlength); set(x / sqlength, y / sqlength, z / sqlength); } else { set(0, 0, 0); } } /** * Normalizes this vector in place. */ default void normalize() { final double x = getX(); final double y = getY(); final double z = getZ(); double sqlength = x * x + y * y + z * z; if (sqlength != 0.) { sqlength = Math.sqrt(sqlength); set(x / sqlength, y / sqlength, z / sqlength); } else { set(0, 0, 0); } } /** * Returns the angle in radians between this vector and the vector * parameter; the return value is constrained to the range [0, PI]. * @param v1 the other vector * @return the angle in radians in the range [0, PI] */ @Pure default double angle(Vector3D<?, ?> v1) { double vDot = this.dot(v1) / (this.getLength() * v1.getLength()); if (vDot < -1.) { vDot = -1.; } if (vDot > 1.) { vDot = 1.; } return Math.acos(vDot); } /** Turn this vector about the given rotation angle. * * @param axis is the axis of rotation. * @param angle is the rotation angle in radians. */ default void turnVector(Vector3D<?, ?> axis, double angle) { final Transform3D mat = new Transform3D(); mat.setRotation(getGeomFactory().newQuaternion(axis, angle)); mat.transform(this); } /** Replies if this vector is colinear to the given vector. * * @param vector the vector * @return <code>true</code> if the vectors are colinear.. */ @Pure default boolean isColinear(Vector3D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return isCollinearVectors(getX(), getY(), getZ(), vector.getX(), vector.getY(), vector.getZ()); } /** Change the length of the vector. * The direction of the vector is unchanged. * * @param newLength - the new length. */ default void setLength(double newLength) { final double l = getLength(); if (l != 0.) { final double f = newLength / l; set(getX() * f, getY() * f, getZ() * f); } else { set(newLength, 0, 0); } } /** Replies the unit vector of this vector. * * @return the unit vector of this vector. */ @Pure default RV toUnitVector() { final double length = getLength(); if (length == 0.) { return getGeomFactory().newVector(); } return getGeomFactory().newVector(getX() / length, getY() / length, getZ() / length); } /** Replies a vector of the given length that is colinear to this vector. * * @param length the length of the new vector. * @return the colinear vector. */ @Pure default RV toColinearVector(double length) { assert length >= 0. : AssertMessages.positiveOrZeroParameter(); final double len = getLength(); if (len != 0.) { final double x = (length * getX()) / len; final double y = (length * getY()) / len; final double z = (length * getZ()) / len; return getGeomFactory().newVector(x, y, z); } return getGeomFactory().newVector(); } /** Replies an unmodifiable copy of this vector. * * @return an unmodifiable copy. */ @Pure UnmodifiableVector3D<RV, RP> toUnmodifiable(); /** Replies the geometry factory associated to this point. * * @return the factory. */ @Pure GeomFactory3D<RV, RP> getGeomFactory(); /** Add a vector to this vector: {@code this += v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector * @see #add(Vector3D) */ default void operator_add(Vector3D<?, ?> v) { add(v); } /** Substract a vector to this vector: {@code this -= v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector * @see #sub(Vector3D) */ default void operator_remove(Vector3D<?, ?> v) { sub(v); } /** Dot product: {@code this * v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector * @return the result. * @see #dot(Vector3D) */ @Pure default double operator_multiply(Vector3D<?, ?> v) { return dot(v); } /** Scale this vector: {@code this * f} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param scale the scaling factor. * @return the scaled vector. * @see #scale(double) */ @Pure default RV operator_multiply(double scale) { return getGeomFactory().newVector(getX() * scale, getY() * scale, getZ() * scale); } /** Replies if this vector and the given vector are equal: {@code this == v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return test result. * @see #equals(Tuple3D) */ @Pure default boolean operator_equals(Tuple3D<?> v) { return equals(v); } /** Replies if this vector and the given vector are different: {@code this != v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return test result. * @see #equals(Tuple3D) */ @Pure default boolean operator_notEquals(Tuple3D<?> v) { return !equals(v); } /** Replies if the absolute angle between this and v: {@code this .. b} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return the signed angle. * @see #angle(Vector3D) */ @Pure default double operator_upTo(Vector3D<?, ?> v) { return angle(v); } /** Replies the signed angle from this to v: {@code this >.. v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return the signed angle. * @see #signedAngle(Vector3D) */ @Pure default double operator_greaterThanDoubleDot(Vector3D<?, ?> v) { return signedAngle(v); } /** Replies the signed angle from v to this: {@code this ..< v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return the signed angle. * @see #signedAngle(Vector3D) */ @Pure default double operator_doubleDotLessThan(Vector3D<?, ?> v) { return -signedAngle(v); } /** Subtract a vector to this vector: {@code this - v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector * @return the result. * @see #sub(Vector3D) */ @Pure default RV operator_minus(Vector3D<?, ?> v) { assert v != null : AssertMessages.notNullParameter(); return getGeomFactory().newVector(getX() - v.getX(), getY() - v.getY(), getZ() - v.getZ()); } /** Negation of this vector: {@code -this} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @return the result. * @see #negate(Tuple3D) */ @Pure default RV operator_minus() { return getGeomFactory().newVector(-getX(), -getY(), -getZ()); } /** Scale this vector: {@code this / f}. * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param scale the scaling factor * @return the scaled vector. */ @Pure default RV operator_divide(double scale) { return getGeomFactory().newVector(getX() / scale, getY() / scale, getZ() / scale); } /** If this vector is epsilon equal to zero then reply v else reply this: {@code this ?: v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector. * @return the vector. */ @Pure default Vector3D<? extends RV, ? extends RP> operator_elvis(Vector3D<? extends RV, ? extends RP> v) { if (MathUtil.isEpsilonZero(getX()) && MathUtil.isEpsilonZero(getY()) && MathUtil.isEpsilonZero(getZ())) { return v; } return this; } /** Sum of this vector and the given vector: {@code this + v} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the vector * @return the result. * @see #add(Vector3D, Vector3D) */ @Pure default RV operator_plus(Vector3D<?, ?> v) { assert v != null : AssertMessages.notNullParameter(); return getGeomFactory().newVector(getX() + v.getX(), getY() + v.getY(), getZ() + v.getZ()); } /** Add this vector to a point: {@code this + p} * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param point the point. * @return the result. * @see Point3D#add(Vector3D, Point3D) */ @Pure default RP operator_plus(Point3D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); return getGeomFactory().newPoint(getX() + point.getX(), getY() + point.getY(), getZ() + point.getZ()); } /** Perp product of this vector and the given vector: {@code this ** v}. * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param v the other vector. * @return the result. * @see #perp(Vector3D) */ @Pure default double operator_power(Vector3D<?, ?> v) { return perp(v); } /** Compute the power of this vector: {@code this ** n}. * * <p>If the power is even, the result is a scalar. * If the power is odd, the result is a vector. * * <p>This function is an implementation of the "-" operator for * the languages that defined or based on the * <a href="https://www.eclipse.org/Xtext/">Xtext framework</a>. * * @param power the power factor. * @return the power of this vector. * @see #power(int) * @see "http://www.euclideanspace.com/maths/algebra/vectors/vecAlgebra/powers/index.htm" */ @Pure default PowerResult<RV> operator_power(int power) { return power(power); } /** Result of the power of a Vector3D. * * @param <T> the type of the vector. * @author $Author: tpiotrow$ * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ final class PowerResult<T extends Vector3D<? super T, ?>> { private final double scalar; private final T vector; /** Construct a result for even power. * * @param scalar the scalar result. */ PowerResult(double scalar) { this.scalar = scalar; this.vector = null; } /** Construct a result for the odd power. * * @param vector the vector result. */ PowerResult(T vector) { assert vector != null : AssertMessages.notNullParameter(); this.scalar = Double.NaN; this.vector = vector; } @Pure @Override public String toString() { if (this.vector != null) { return this.vector.toString(); } return Double.toString(this.scalar); } private boolean isSameScalar(Number number) { return number.equals(Double.valueOf(this.scalar)); } private boolean isSameVector(Vector3D<?, ?> vector) { if (this.vector == vector) { return true; } if (this.vector != null) { return this.vector.equals((Vector3D<?, ?>) vector); } return false; } @Override @Pure public boolean equals(Object obj) { if (obj instanceof PowerResult<?>) { if (this == obj) { return true; } final PowerResult<?> result = (PowerResult<?>) obj; if (result.vector != null) { return isSameVector(result.vector); } return isSameScalar(result.scalar); } if (obj instanceof Vector3D<?, ?>) { return isSameVector((Vector3D<?, ?>) obj); } if (obj instanceof Number) { return isSameScalar((Number) obj); } return false; } @Override @Pure public int hashCode() { long bits = 1; bits = 31 * bits + Double.doubleToLongBits(this.scalar); bits = 31 * bits + ((this.vector == null) ? 0 : this.vector.hashCode()); final int b = (int) bits; return b ^ (b >> 31); } /** Replies the scalar result. * * @return the scalar result. */ @Pure public double getScalar() { return this.scalar; } /** Replies the vector result. * * @return the vector result. */ @Pure public T getVector() { return this.vector; } /** Replies if the result is vectorial. * * @return <code>true</code> if the result is vectorial. <code>false</code> * if the result if scalar. */ @Pure public boolean isVectorial() { return this.vector != null; } } }