/* * $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.d2; import java.util.Objects; 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.extensions.xtext.Tuple2DExtensions; import org.arakhne.afc.math.geometry.coordinatesystem.CoordinateSystem2D; import org.arakhne.afc.vmutil.annotations.ScalaOperator; import org.arakhne.afc.vmutil.annotations.XtextOperator; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** 2D 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$ * @since 13.0 */ @SuppressWarnings("checkstyle:methodcount") public interface Vector2D<RV extends Vector2D<? super RV, ? super RP>, RP extends Point2D<? super RP, ? super RV>> extends Tuple2D<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. * @return <code>true</code> if the two given vectors are colinear. * @since 13.0 * @see MathUtil#isEpsilonEqual(double, double, double) * @see MathConstants#UNIT_VECTOR_EPSILON * @see #isUnitVector(double, double, double) */ @Pure @Inline(value = "Vector2D.isUnitVector(($1), ($2), MathConstants.UNIT_VECTOR_EPSILON)", imported = {Vector2D.class, MathConstants.class}) static boolean isUnitVector(double x, double y) { return isUnitVector(x, y, 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 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) */ @Pure @Inline(value = "MathUtil.isEpsilonEqual(($1) * ($1) + ($2) * ($2), 1., ($3))", imported = {MathUtil.class}) static boolean isUnitVector(double x, double y, double epsilon) { return MathUtil.isEpsilonEqual(x * x + y * y, 1., epsilon); } /** Replies if this vector is a unit vector. * A unit vector has a length equal to 1. * * @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()); } /** * Replies if the vectors are orthogonal vectors. * * <p>Due to the precision on floating-point computations, the test of orthogonality * is approximated. The default epsilon is {@link MathConstants#ORTHOGONAL_VECTOR_EPSILON}. * * @param x1 is the X coordinate of the first unit vector. * @param y1 is the Y coordinate of the first unit vector. * @param x2 is the X coordinate of the second unit vector. * @param y2 is the Y coordinate of the second unit vector. * @return <code>true</code> if the two given vectors are colinear. * @since 13.0 * @see MathUtil#isEpsilonEqual(double, double, double) * @see MathConstants#ORTHOGONAL_VECTOR_EPSILON * @see #isUnitVector(double, double, double) */ @Pure @Inline(value = "Vector2D.isOrthogonal(($1), ($2), ($3), ($4), MathConstants.ORTHOGONAL_VECTOR_EPSILON)", imported = {Vector2D.class, MathConstants.class}) static boolean isOrthogonal(double x1, double y1, double x2, double y2) { return isOrthogonal(x1, y1, x2, y2, MathConstants.ORTHOGONAL_VECTOR_EPSILON); } /** * Replies if the vectors are orthogonal vectors. * * @param x1 is the X coordinate of the first unit vector. * @param y1 is the Y coordinate of the first unit vector. * @param x2 is the X coordinate of the second unit vector. * @param y2 is the Y coordinate of the second unit vector. * @param epsilon the precision distance to assumed for equality. * @return <code>true</code> if the two given vectors are orthogonal. * @since 13.0 * @see #isOrthogonal(double, double, double, double) */ @Pure @Inline(value = "MathUtil.isEpsilonZero(Vector2D.dotProduct(($1), ($2), ($3), ($4)), ($5))", imported = {Vector2D.class, MathUtil.class}) static boolean isOrthogonal(double x1, double y1, double x2, double y2, double epsilon) { return MathUtil.isEpsilonZero(dotProduct(x1, y1, x2, y2), epsilon); } /** Replies if this vector is orthogonal to the given vector. * * @param vector the vector to compare to this vector. * @return <code>true</code> if the vectors are orthogonal. * <code>false</code> otherwise. */ @Pure default boolean isOrthogonal(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return isOrthogonal(getX(), getY(), vector.getX(), vector.getY()); } /** * Replies if two vectors are colinear. * * <p>This function uses the equal-to-zero test with the error {@link Math#ulp(double)}. * * @param x1 * is the X coordinate of the first vector * @param y1 * is the Y 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 * @return <code>true</code> if the two given vectors are colinear. * @since 13.0 * @see MathUtil#isEpsilonZero(double) */ @Pure @Inline(value = "MathUtil.isEpsilonZero(($1) * ($4) - ($3) * ($2))", imported = {MathUtil.class}) static boolean isCollinearVectors(double x1, double y1, double x2, double y2) { // Test if three points are colinears // iff det( [ x1 x2 ] // [ y1 y2 ] ) = 0 // Do not invoked MathUtil.determinant() to limit computation consumption. return MathUtil.isEpsilonZero(x1 * y2 - x2 * y1); } /** Compute the the perpendicular product of * the two vectors (aka. the determinant of two vectors). * * <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>. * * <p>Let consider that dotProduct projects the point (px,py) on the * Ox axis, and perpProduct projects the point (py,py) on the Oy * axis. Then: * <pre><code>perpProduct(ax, ay, px, py) = dotProduct(px, py, -ay, ax)</code></pre> * You could note that the semantics of the parameters differ:<ul> * <li><code>perpProduct(axisX, axisY, pointX, pointY)</code></li> * <li><code>dotProduct(pointX, pointY, axisX, axisY)</code></li> * </ul> * * @param x1 x coordinate of the first vector. * @param y1 y coordinate of the first vector. * @param x2 x coordinate of the second vector. * @param y2 y coordinate of the second vector. * @return the determinant */ @Pure @Inline(value = "($1) * ($4) - ($3) * ($2)") static double perpProduct(double x1, double y1, double x2, double y2) { return x1 * y2 - x2 * y1; } /** Compute the dot product of two vectors. * * <p>Let consider that dotProduct projects the point (px,py) on the * Ox axis, and perpProduct projects the point (py,py) on the Oy * axis. Then: * <pre><code>perpProduct(ax, ay, px, py) = dotProduct(px, py, -ay, ax)</code></pre> * You could note that the semantics of the parameters differ:<ul> * <li><code>perpProduct(axisX, axisY, pointX, pointY)</code></li> * <li><code>dotProduct(pointX, pointY, axisX, axisY)</code></li> * </ul> * * @param x1 x coordinate of the first vector. * @param y1 y coordinate of the first vector. * @param x2 x coordinate of the second vector. * @param y2 y coordinate of the second vector. * @return the dot product. */ @Pure @Inline(value = "($1) * ($3) + ($2) * ($4)") static double dotProduct(double x1, double y1, double x2, double y2) { return x1 * x2 + y1 * y2; } /** Replies if the vectors are defined in a counter-clockwise order. * * <p>The two vectors are defined in a counter-clockwise order if the sinus * from the first vector to the second vector is positive. * * <p>In other words, let the angle between the two vectors that * is replied by {@link #signedAngle(double, double, double, double)}. * The vectors are defined in an counter-clockwise order if the angle * is positive. * * @param x1 the first coordinate of the first vector. * @param y1 the second coordinate of the first vector. * @param x2 the first coordinate of the second vector. * @param y2 the second coordinate of the second vector. * @return <code>true</code> if the vectors are defined in a counter-clockwise order. * @see #signedAngle(double, double, double, double) * @see #perpProduct(double, double, double, double) */ @Pure @Inline("(($1) * ($4) - ($2) * ($3)) >= 0.") static boolean isCCW(double x1, double y1, double x2, double y2) { return (x1 * y2 - y1 * x2) >= 0.; } /** * Compute the signed angle between two vectors. * * @param x1 the first coordinate of the first vector. * @param y1 the second coordinate of the first vector. * @param x2 the first coordinate of the second vector. * @param y2 the second coordinate of the second vector. * @return the angle between <code>-PI</code> and <code>PI</code>. * @see #isCCW(double, double, double, double) */ @Pure static double signedAngle(double x1, double y1, double x2, double y2) { final double length1 = Math.sqrt(x1 * x1 + y1 * y1); final double length2 = Math.sqrt(x2 * x2 + y2 * y2); if ((length1 == 0.) || (length2 == 0.)) { return Double.NaN; } double cx1 = x1; double cy1 = y1; double cx2 = x2; double cy2 = y2; // A and B are normalized if (length1 != 1.) { cx1 /= length1; cy1 /= length1; } if (length2 != 1.) { cx2 /= length2; cy2 /= length2; } // Second method // A . B = |A|.|B|.cos(theta) = cos(theta) final double cos = cx1 * cx2 + cy1 * cy2; // A x B = |A|.|B|.sin(theta).N = sin(theta) (where N is the unit vector perpendicular to plane AB) final double sin = cx1 * cy2 - cy1 * cx2; return Math.atan2(sin, cos); } /** 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(Vector2D)} * 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(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return signedAngle(getX(), getY(), vector.getX(), vector.getY()); } /** Return the trigonometric angle of a vector. * The vector is from the first point to the * second point. * * <p>The trigonometric angle is the signed angle between * the vectors (1;0) and (x2-x1;y2-y1). * * @param x1 is the coordinate of the vector origin point. * @param y1 is the coordinate of the vector origin point. * @param x2 is the coordinate of the vector target point. * @param y2 is the coordinate of the vector target point. * @return the trigonometric angle in radians in [-PI;PI]. */ @Pure @Inline(value = "Vector2D.signedAngle(1, 0, ($3) - ($1), ($4) - ($2))", imported = {Vector2D.class}) static double angleOfVector(double x1, double y1, double x2, double y2) { return signedAngle( 1, 0, x2 - x1, y2 - y1); } /** Return the trigonometric angle of a vector. * The vector is from the first point to the * second point. * * <p>The trigonometric angle is the signed angle between * the vectors (1;0) and (x;y). * * @param x is the coordinate of the vector. * @param y is the coordinate of the vector. * @return the trigonometric angle in radians in [-PI;PI]. */ @Pure @Inline(value = "Vector2D.signedAngle(1, 0, ($1), ($2))", imported = {Vector2D.class}) static double angleOfVector(double x, double y) { return signedAngle( 1, 0, x, y); } /** * Sets the value of this tuple to the sum of tuples vector1 and vector2. * * @param vector1 the first tuple * @param vector2 the second tuple */ default void add(Vector2D<?, ?> vector1, Vector2D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(vector1.getX() + vector2.getX(), vector1.getY() + vector2.getY()); } /** * Sets the value of this tuple to the sum of itself and the given vector. * * @param vector the other tuple */ default void add(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(vector.getX() + getX(), vector.getY() + 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 t1 the tuple to be multipled * @param t2 the tuple to be added */ default void scaleAdd(int scale, Vector2D<?, ?> t1, Vector2D<?, ?> t2) { assert t1 != null : AssertMessages.notNullParameter(0); assert t2 != null : AssertMessages.notNullParameter(1); set(scale * t1.getX() + t2.getX(), scale * t1.getY() + t2.getY()); } /** * Sets the value of this tuple to the scalar multiplication * of the tuple vector1 plus the tuple vector2 (this = s*vector1 + vector2). * * @param scale the scalar value * @param vector1 the tuple to be multipled * @param vector2 the tuple to be added */ default void scaleAdd(double scale, Vector2D<?, ?> vector1, Vector2D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(scale * vector1.getX() + vector2.getX(), scale * vector1.getY() + vector2.getY()); } /** * Sets the value of this tuple to the scalar multiplication * of itself and then adds tuple vector (this = s*this + vector). * * @param scale the scalar value * @param vector the tuple to be added */ default void scaleAdd(int scale, Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(1); set(scale * getX() + vector.getX(), scale * getY() + vector.getY()); } /** * Sets the value of this tuple to the scalar multiplication * of itself and then adds tuple vector (this = s*this + vector). * * @param scale the scalar value * @param vector the tuple to be added */ default void scaleAdd(double scale, Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(1); set(scale * getX() + vector.getX(), scale * getY() + vector.getY()); } /** * Sets the value of this tuple to the difference * of tuples vector1 and vector2 (this = vector1 - vector2). * * @param vector1 the first tuple * @param vector2 the second tuple */ default void sub(Vector2D<?, ?> vector1, Vector2D<?, ?> vector2) { assert vector1 != null : AssertMessages.notNullParameter(0); assert vector2 != null : AssertMessages.notNullParameter(1); set(vector1.getX() - vector2.getX(), vector1.getY() - vector2.getY()); } /** * Sets the value of this tuple to the difference * of tuples point1 and point2 (this = point1 - point2). * * @param point1 the first tuple * @param point2 the second tuple */ default void sub(Point2D<?, ?> point1, Point2D<?, ?> point2) { assert point1 != null : AssertMessages.notNullParameter(0); assert point2 != null : AssertMessages.notNullParameter(1); set(point1.getX() - point2.getX(), point1.getY() - point2.getY()); } /** * Sets the value of this tuple to the difference * of itself and vector (this = this - vector). * * @param vector the other tuple */ default void sub(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); set(getX() - vector.getX(), getY() - vector.getY()); } /** 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 dot = dotProduct(x, y, x, y); final double resultForEven = Math.pow(dot, evenPower); if (isEven) { return new PowerResult<>(resultForEven); } final RV r = getGeomFactory().newVector(getX() * resultForEven, getY() * resultForEven); return new PowerResult<>(r); } /** * Computes the dot product of the this vector and the given vector. * * @param vector the other vector * @return the dot product. */ @Pure default double dot(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return dotProduct(getX(), getY(), vector.getX(), vector.getY()); } /** Compute the the perpendicular product of * the two vectors (aka. the determinant of two vectors). * * <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 vector the vertor. * @return the determinant */ @Pure default double perp(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); return perpProduct(getX(), getY(), vector.getX(), vector.getY()); } /** Change the coordinates of this vector to make it an orthogonal * vector to the original coordinates. */ default void makeOrthogonal() { set(-getY(), getX()); } /** Replies the orthogonal vector to this vector. * * @return the orthogonal vector. */ @Pure default RV toOrthogonalVector() { return getGeomFactory().newVector(-getY(), getX()); } /** 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; return getGeomFactory().newVector(x, y); } return getGeomFactory().newVector(); } /** * Returns the length of this vector. * * @return the length of this vector */ @Pure default double getLength() { final double x = getX(); final double y = getY(); return Math.sqrt(x * x + y * y); } /** * 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(); return x * x + y * y; } /** * Sets the value of this vector to the normalization of vector vector. * * @param vector the un-normalized vector */ default void normalize(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); final double x = vector.getX(); final double y = vector.getY(); double sqlength = x * x + y * y; if (sqlength != 0.) { sqlength = Math.sqrt(sqlength); set(x / sqlength, y / sqlength); } else { set(0, 0); } } /** * Normalizes this vector in place. * * <p>If the length of the vector is zero, x and y are set to zero. */ default void normalize() { final double x = getX(); final double y = getY(); double sqlength = x * x + y * y; if (sqlength != 1.) { if (sqlength != 0.) { sqlength = Math.sqrt(sqlength); set(x / sqlength, y / sqlength); } else { set(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 vector the other vector * @return the angle in radians in the range [0, PI] */ @Pure default double angle(Vector2D<?, ?> vector) { assert vector != null : AssertMessages.notNullParameter(); double vDot = dotProduct(getX(), getY(), vector.getX(), vector.getY()) / (getLength() * vector.getLength()); if (vDot < -1.) { vDot = -1.; } if (vDot > 1.) { vDot = 1.; } return Math.acos(vDot); } /** Turn this vector about the given rotation angle. * * <p>The rotation is done according to the trigonometric coordinate. * A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @deprecated since 13.0, {@link #turn(double)} */ @Deprecated default void turnVector(double angle) { turn(angle); } /** Turn this vector about the given rotation angle. * * <p>The rotation is done according to the trigonometric coordinate. * A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @see #turn(double, Vector2D) * @see #turnLeft(double) * @see #turnRight(double) */ default void turn(double angle) { turn(angle, this); } /** Turn the given vector about the given rotation angle, and set this * vector with the result. * * <p>The rotation is done according to the trigonometric coordinate. * A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @param vectorToTurn the vector to turn. * @see #turn(double) * @see #turnLeft(double) * @see #turnRight(double) */ default void turn(double angle, Vector2D<?, ?> vectorToTurn) { assert vectorToTurn != null : AssertMessages.notNullParameter(1); final double sin = Math.sin(angle); final double cos = Math.cos(angle); final double x = cos * vectorToTurn.getX() - sin * vectorToTurn.getY(); final double y = sin * vectorToTurn.getX() + cos * vectorToTurn.getY(); set(x, y); } /** Turn this vector on the left when the given rotation angle is positive. * * <p>A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @see CoordinateSystem2D * @see #turnLeft(double, Vector2D) * @see #turn(double) * @see #turnRight(double) */ default void turnLeft(double angle) { turnLeft(angle, this); } /** Turn the given vector on the left, and set this * vector with the result. * * <p>A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @param vectorToTurn the vector to turn. * @see CoordinateSystem2D * @see #turnLeft(double, Vector2D) * @see #turn(double) * @see #turnRight(double) */ default void turnLeft(double angle, Vector2D<?, ?> vectorToTurn) { final double sin = Math.sin(angle); final double cos = Math.cos(angle); final double x; final double y; if (CoordinateSystem2D.getDefaultCoordinateSystem().isRightHanded()) { x = cos * vectorToTurn.getX() - sin * vectorToTurn.getY(); y = sin * vectorToTurn.getX() + cos * vectorToTurn.getY(); } else { x = cos * vectorToTurn.getX() + sin * vectorToTurn.getY(); y = -sin * vectorToTurn.getX() + cos * vectorToTurn.getY(); } set(x, y); } /** Turn this vector on the right when the given rotation angle is positive. * * <p>A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @see CoordinateSystem2D * @see #turn(double) * @see #turnLeft(double) */ default void turnRight(double angle) { turnLeft(-angle, this); } /** Turn this vector on the right when the given rotation angle is positive. * * <p>A positive rotation angle corresponds to a left or right rotation * according to the current {@link CoordinateSystem2D}. * * @param angle is the rotation angle in radians. * @param vectorToTurn the vector to turn. * @see CoordinateSystem2D * @see #turn(double) * @see #turnLeft(double) */ default void turnRight(double angle, Vector2D<?, ?> vectorToTurn) { turnLeft(-angle, vectorToTurn); } /** Replies the orientation angle on a trigonometric circle * that is corresponding to the given direction of this vector. * * @return the angle on a trigonometric circle that is corresponding * to the given orientation vector. */ @Pure default double getOrientationAngle() { final double angle = Math.acos(getX()); if (getY() < 0f) { return MathConstants.TWO_PI - angle; } return angle; } /** 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); } else { set(newLength, 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); } /** Replies an unmodifiable copy of this vector. * * @return an unmodifiable copy. */ @Pure UnmodifiableVector2D<RV, RP> toUnmodifiable(); /** Replies the geometry factory associated to this point. * * @return the factory. */ @Pure GeomFactory2D<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(Vector2D) */ @XtextOperator("+=") default void operator_add(Vector2D<?, ?> 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(Vector2D) */ @XtextOperator("-=") default void operator_remove(Vector2D<?, ?> 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(Vector2D) */ @Pure @XtextOperator("*") default double operator_multiply(Vector2D<?, ?> v) { return dot(v); } /** Scale this vector: {@code this * factor}. * * <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>. * * <p>The operation {@code factor * this} is supported by {@link Tuple2DExtensions#operator_multiply(double, Vector2D)}. * * @param factor the scaling factor. * @return the scaled vector. * @see #scale(double) * @see Tuple2DExtensions#operator_multiply(double, Vector2D) */ @Pure @XtextOperator("*") default RV operator_multiply(double factor) { return getGeomFactory().newVector(getX() * factor, getY() * factor); } /** 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(Tuple2D) */ @Pure @XtextOperator("==") default boolean operator_equals(Tuple2D<?> 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(Tuple2D) */ @Pure @XtextOperator("!=") default boolean operator_notEquals(Tuple2D<?> 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(Vector2D) */ @Pure @XtextOperator("..") default double operator_upTo(Vector2D<?, ?> 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(Vector2D) */ @Pure @XtextOperator(">..") default double operator_greaterThanDoubleDot(Vector2D<?, ?> 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(Vector2D) */ @Pure @XtextOperator("..<") default double operator_doubleDotLessThan(Vector2D<?, ?> v) { return -signedAngle(v); } /** 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(Tuple2D) */ @Pure @XtextOperator("(-)") default RV operator_minus() { return getGeomFactory().newVector(-getX(), -getY()); } /** 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(Vector2D) */ @Pure @XtextOperator("-") default RV operator_minus(Vector2D<?, ?> v) { assert v != null : AssertMessages.notNullParameter(); return getGeomFactory().newVector(getX() - v.getX(), getY() - v.getY()); } /** Subtract a vector to this scalar: {@code this - scalar}. * * <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>. * * <p>The operation {@code scalar - this} is supported by {@link Tuple2DExtensions#operator_minus(double, Vector2D)}. * * @param scalar the scalar. * @return the result. * @see #sub(Vector2D) * @see Tuple2DExtensions#operator_minus(double, Vector2D) */ @Pure @XtextOperator("-") default RV operator_minus(double scalar) { return getGeomFactory().newVector(getX() - scalar, getY() - scalar); } /** Subtract a vector to this point: {@code this - point}. * * <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 #sub(Vector2D) */ @Pure @XtextOperator("-") default RP operator_minus(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); return getGeomFactory().newPoint(getX() - point.getX(), getY() - point.getY()); } /** Scale this vector: {@code this / factor}. * * <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>. * * <p>The operation {@code scalar / this} is supported by {@link Tuple2DExtensions#operator_divide(double, Vector2D)}. * * @param factor the scaling factor * @return the scaled vector. * @see Tuple2DExtensions#operator_divide(double, Vector2D) */ @Pure @XtextOperator("/") default RV operator_divide(double factor) { return getGeomFactory().newVector(getX() / factor, getY() / factor); } /** 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 @XtextOperator("?:") default Vector2D<? extends RV, ? extends RP> operator_elvis(Vector2D<? extends RV, ? extends RP> v) { if (MathUtil.isEpsilonZero(getX()) && MathUtil.isEpsilonZero(getY())) { 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(Vector2D, Vector2D) */ @Pure @XtextOperator("+") default RV operator_plus(Vector2D<?, ?> v) { assert v != null : AssertMessages.notNullParameter(); return getGeomFactory().newVector(getX() + v.getX(), getY() + v.getY()); } /** 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 pt the point. * @return the result. * @see Point2D#add(Vector2D, Point2D) */ @Pure @XtextOperator("+") default RP operator_plus(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); return getGeomFactory().newPoint(getX() + pt.getX(), getY() + pt.getY()); } /** Sum of this vector and the given scalar: {@code this + scalar}. * * <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>. * * <p>The operation {@code scalar + this} is supported by {@link Tuple2DExtensions#operator_plus(double, Vector2D)}. * * @param scalar the scalar. * @return the result. * @see #add(Vector2D, Vector2D) * @see Tuple2DExtensions#operator_plus(double, Vector2D) */ @Pure @XtextOperator("+") default RV operator_plus(double scalar) { return getGeomFactory().newVector(getX() + scalar, getY() + scalar); } /** 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(Vector2D) */ @Pure @XtextOperator("**") default double operator_power(Vector2D<?, ?> 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 @XtextOperator("**") default PowerResult<RV> operator_power(int power) { return power(power); } /** Dot product: {@code this * v}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param v the vector * @return the result. * @see #dot(Vector2D) */ @Pure @ScalaOperator("*") default double $times(Vector2D<?, ?> v) { return operator_multiply(v); } /** Scale this vector: {@code this * factor}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * <p>The operation {@code factor * this} is supported by * {@link org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$times(double, Vector2D)}. * * @param factor the scaling factor. * @return the scaled vector. * @see #scale(double) * @see org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$times(double, Vector2D) */ @Pure @ScalaOperator("*") default RV $times(double factor) { return operator_multiply(factor); } /** Negation of this vector: {@code -this}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @return the result. * @see #negate(Tuple2D) */ @Pure @ScalaOperator("(-)") default RV $minus() { return operator_minus(); } /** Subtract a vector to this vector: {@code this - v}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param v the vector * @return the result. * @see #sub(Vector2D) */ @Pure @ScalaOperator("-") default RV $minus(Vector2D<?, ?> v) { return operator_minus(v); } /** Subtract a vector to this scalar: {@code this - scalar}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * <p>The operation {@code scalar - this} is supported by * {@link org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$minus(double, Vector2D)}. * * @param scalar the scalar. * @return the result. * @see #sub(Vector2D) * @see org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$minus(double, Vector2D) */ @Pure @ScalaOperator("-") default RV $minus(double scalar) { return operator_minus(scalar); } /** Subtract a vector to this point: {@code this - point}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param point the point. * @return the result. * @see #sub(Vector2D) */ @Pure @ScalaOperator("-") default RP $minus(Point2D<?, ?> point) { return operator_minus(point); } /** Scale this vector: {@code this / factor}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * <p>The operation {@code scalar / this} is supported by * {@link org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$div(double, Vector2D)}. * * @param factor the scaling factor * @return the scaled vector. * @see org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$div(double, Vector2D) */ @Pure @ScalaOperator("/") default RV $div(double factor) { return operator_divide(factor); } /** Sum of this vector and the given vector: {@code this + v}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param v the vector * @return the result. * @see #add(Vector2D, Vector2D) */ @Pure @ScalaOperator("+") default RV $plus(Vector2D<?, ?> v) { return operator_plus(v); } /** Add this vector to a point: {@code this + p}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param pt the point. * @return the result. * @see Point2D#add(Vector2D, Point2D) */ @Pure @ScalaOperator("+") default RP $plus(Point2D<?, ?> pt) { return operator_plus(pt); } /** Sum of this vector and the given scalar: {@code this + scalar}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * <p>The operation {@code scalar + this} is supported by * {@link org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$plus(double, Vector2D)}. * * @param scalar the scalar. * @return the result. * @see #add(Vector2D, Vector2D) * @see org.arakhne.afc.math.extensions.scala.Tuple2DExtensions#$plus(double, Vector2D) */ @Pure @ScalaOperator("+") default RV $plus(double scalar) { return operator_plus(scalar); } /** Perp product of this vector and the given vector: {@code this ^ v}. * * <p>This function is an implementation of the operator for * the <a href="http://scala-lang.org/">Scala Language</a>. * * @param v the other vector. * @return the result. * @see #perp(Vector2D) */ @Pure @ScalaOperator("^") default double $up(Vector2D<?, ?> v) { return operator_power(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 <a href="http://scala-lang.org/">Scala Language</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 @ScalaOperator("^") default PowerResult<RV> $up(int power) { return operator_power(power); } /** Result of the power of a Vector2D. * * @param <T> the type of the vector. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ final class PowerResult<T extends Vector2D<? 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(Vector2D<?, ?> vector) { if (this.vector == vector) { return true; } if (this.vector != null) { return this.vector.equals((Vector2D<?, ?>) 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 Vector2D<?, ?>) { return isSameVector((Vector2D<?, ?>) obj); } if (obj instanceof Number) { return isSameScalar((Number) obj); } return false; } @Pure @Override public int hashCode() { long bits = 1; bits = 31 * bits + Double.hashCode(this.scalar); bits = 31 * bits + Objects.hashCode(this.vector); 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; } } }