diff --git a/src/main/java/org/apache/commons/math/geometry/partitioning/Point.java b/src/main/java/org/apache/commons/math/geometry/Space.java similarity index 59% rename from src/main/java/org/apache/commons/math/geometry/partitioning/Point.java rename to src/main/java/org/apache/commons/math/geometry/Space.java index ad9d3daea..8e5c32dee 100644 --- a/src/main/java/org/apache/commons/math/geometry/partitioning/Point.java +++ b/src/main/java/org/apache/commons/math/geometry/Space.java @@ -14,16 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.math.geometry.partitioning; +package org.apache.commons.math.geometry; -/** This interface represents a generic point to be used in a space partition. - *

Points are completely virtual entities with no specification at - * all, so this class is essentially a marker interface with no - * methods. This allows to perform partition in traditional euclidean - * n-dimensions spaces, but also in more exotic universes like for - * example the surface of the unit sphere.

- * @version $Revision$ $Date$ +import java.io.Serializable; + +/** This interface represents a generic space, with affine and vectorial counterparts. + * @version $Id:$ + * @see Vector + * @since 3.0 */ -public interface Point { - // nothing here, this is only a marker interface +public interface Space extends Serializable { + + /** Get the dimension of the space. + * @return dimension of the space + */ + int getDimension(); + + /** Get the n-1 dimension subspace of this space. + * @return n-1 dimension sub-space of this space + * @see #getDimension() + */ + Space getSubSpace(); + } diff --git a/src/main/java/org/apache/commons/math/geometry/Vector.java b/src/main/java/org/apache/commons/math/geometry/Vector.java new file mode 100644 index 000000000..5bb392c5f --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/Vector.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry; + +import java.io.Serializable; +import java.text.NumberFormat; + +/** This interface represents a generic vector in a vectorial space or a point in an affine space. + * @version $Id:$ + * @see Space + * @see Vector + * @since 3.0 + */ +public interface Vector extends Serializable { + + /** Get the space to which the vector belongs. + * @return containing space + */ + Space getSpace(); + + /** Get the null vector of the vetorial space or origin point of the affine space. + * @return null vector of the vetorial space or origin point of the affine space + */ + Vector getZero(); + + /** Get the L1 norm for the vector. + * @return L1 norm for the vector + */ + double getNorm1(); + + /** Get the L2 norm for the vector. + * @return Euclidean norm for the vector + */ + double getNorm(); + + /** Get the square of the norm for the vector. + * @return square of the Euclidean norm for the vector + */ + double getNormSq(); + + /** Get the L norm for the vector. + * @return L norm for the vector + */ + double getNormInf(); + + /** Add a vector to the instance. + * @param v vector to add + * @return a new vector + */ + Vector add(Vector v); + + /** Add a scaled vector to the instance. + * @param factor scale factor to apply to v before adding it + * @param v vector to add + * @return a new vector + */ + Vector add(double factor, Vector v); + + /** Subtract a vector from the instance. + * @param v vector to subtract + * @return a new vector + */ + Vector subtract(Vector v); + + /** Subtract a scaled vector from the instance. + * @param factor scale factor to apply to v before subtracting it + * @param v vector to subtract + * @return a new vector + */ + Vector subtract(double factor, Vector v); + + /** + * Returns true if any coordinate of this vector is NaN; false otherwise + * @return true if any coordinate of this vector is NaN; false otherwise + */ + boolean isNaN(); + + /** + * Returns true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + * @return true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + */ + boolean isInfinite(); + + /** Compute the distance between the instance and another vector according to the L1 norm. + *

Calling this method is equivalent to calling: + * q.subtract(p).getNorm1() except that no intermediate + * vector is built

+ * @param v second vector + * @return the distance between the instance and p according to the L1 norm + */ + double distance1(Vector v); + + /** Compute the distance between the instance and another vector according to the L2 norm. + *

Calling this method is equivalent to calling: + * q.subtract(p).getNorm() except that no intermediate + * vector is built

+ * @param v second vector + * @return the distance between the instance and p according to the L2 norm + */ + double distance(Vector v); + + /** Compute the distance between the instance and another vector according to the L norm. + *

Calling this method is equivalent to calling: + * q.subtract(p).getNormInf() except that no intermediate + * vector is built

+ * @param v second vector + * @return the distance between the instance and p according to the L norm + */ + double distanceInf(Vector v); + + /** Compute the square of the distance between the instance and another vector. + *

Calling this method is equivalent to calling: + * q.subtract(p).getNormSq() except that no intermediate + * vector is built

+ * @param v second vector + * @return the square of the distance between the instance and p + */ + double distanceSq(Vector v); + + /** Get a string representation of this vector. + * @param format the custom format for components. + */ + public String toString(final NumberFormat format); + +} diff --git a/src/main/java/org/apache/commons/math/geometry/VectorFormat.java b/src/main/java/org/apache/commons/math/geometry/VectorFormat.java new file mode 100644 index 000000000..27ededa4c --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/VectorFormat.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.commons.math.util.CompositeFormat; +import org.apache.commons.math.exception.MathParseException; + +/** + * Formats a vector in components list format "{x; y; ...}". + *

The prefix and suffix "{" and "}" and the separator "; " can be replaced by + * any user-defined strings. The number format for components can be configured.

+ *

White space is ignored at parse time, even if it is in the prefix, suffix + * or separator specifications. So even if the default separator does include a space + * character that is used at format time, both input string "{1;1;1}" and + * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be + * returned. In the second case, however, the parse position after parsing will be + * just after the closing curly brace, i.e. just before the trailing space.

+ * + * @version $Id:$ + * @since 3.0 + */ +public abstract class VectorFormat { + + /** The default prefix: "{". */ + public static final String DEFAULT_PREFIX = "{"; + + /** The default suffix: "}". */ + public static final String DEFAULT_SUFFIX = "}"; + + /** The default separator: ", ". */ + public static final String DEFAULT_SEPARATOR = "; "; + + /** Prefix. */ + private final String prefix; + + /** Suffix. */ + private final String suffix; + + /** Separator. */ + private final String separator; + + /** Trimmed prefix. */ + private final String trimmedPrefix; + + /** Trimmed suffix. */ + private final String trimmedSuffix; + + /** Trimmed separator. */ + private final String trimmedSeparator; + + /** The format used for components. */ + private final NumberFormat format; + + /** + * Create an instance with default settings. + *

The instance uses the default prefix, suffix and separator: + * "{", "}", and "; " and the default number format for components.

+ */ + protected VectorFormat() { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, + CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with a custom number format for components. + * @param format the custom format for components. + */ + protected VectorFormat(final NumberFormat format) { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + } + + /** + * Create an instance with custom prefix, suffix and separator. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + */ + protected VectorFormat(final String prefix, final String suffix, + final String separator) { + this(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with custom prefix, suffix, separator and format + * for components. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + * @param format the custom format for components. + */ + protected VectorFormat(final String prefix, final String suffix, + final String separator, final NumberFormat format) { + this.prefix = prefix; + this.suffix = suffix; + this.separator = separator; + trimmedPrefix = prefix.trim(); + trimmedSuffix = suffix.trim(); + trimmedSeparator = separator.trim(); + this.format = format; + } + + /** + * Get the set of locales for which point/vector formats are available. + *

This is the same set as the {@link NumberFormat} set.

+ * @return available point/vector format locales. + */ + public static Locale[] getAvailableLocales() { + return NumberFormat.getAvailableLocales(); + } + + /** + * Get the format prefix. + * @return format prefix. + */ + public String getPrefix() { + return prefix; + } + + /** + * Get the format suffix. + * @return format suffix. + */ + public String getSuffix() { + return suffix; + } + + /** + * Get the format separator between components. + * @return format separator. + */ + public String getSeparator() { + return separator; + } + + /** + * Get the components format. + * @return components format. + */ + public NumberFormat getFormat() { + return format; + } + + /** + * Formats a {@link Vector} object to produce a string. + * @param vector the object to format. + * @return a formatted string. + */ + public String format(Vector vector) { + return format(vector, new StringBuffer(), new FieldPosition(0)).toString(); + } + + /** + * Formats a {@link Vector} object to produce a string. + * @param vector the object to format. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + */ + public abstract StringBuffer format(Vector vector, + StringBuffer toAppendTo, FieldPosition pos); + + /** + * Formats the coordinates of a {@link Vector} to produce a string. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @param coordinates coordinates of the object to format. + * @return the value passed in as toAppendTo. + */ + protected StringBuffer format(StringBuffer toAppendTo, FieldPosition pos, + double ... coordinates) { + + pos.setBeginIndex(0); + pos.setEndIndex(0); + + // format prefix + toAppendTo.append(prefix); + + // format components + for (int i = 0; i < coordinates.length; ++i) { + if (i > 0) { + toAppendTo.append(separator); + } + CompositeFormat.formatDouble(coordinates[i], format, toAppendTo, pos); + } + + // format suffix + toAppendTo.append(suffix); + + return toAppendTo; + + } + + /** + * Parses a string to produce a {@link Vector} object. + * @param source the string to parse + * @return the parsed {@link Vector} object. + * @throws MathParseException if the beginning of the specified string + * cannot be parsed. + */ + public abstract Vector parse(String source); + + /** + * Parses a string to produce a {@link Vector} object. + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the parsed {@link Vector} object. + */ + public abstract Vector parse(String source, ParsePosition pos); + + /** + * Parses a string to produce an array of coordinates. + * @param dimension dimension of the space + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return coordinates array. + */ + protected double[] parseCoordinates(int dimension, String source, ParsePosition pos) { + + int initialIndex = pos.getIndex(); + double[] coordinates = new double[dimension]; + + // parse prefix + CompositeFormat.parseAndIgnoreWhitespace(source, pos); + if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) { + return null; + } + + for (int i = 0; i < dimension; ++i) { + + // skip whitespace + CompositeFormat.parseAndIgnoreWhitespace(source, pos); + + // parse separator + if (i > 0) { + if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) { + return null; + } + } + + // skip whitespace + CompositeFormat.parseAndIgnoreWhitespace(source, pos); + + // parse coordinate + Number c = CompositeFormat.parseNumber(source, format, pos); + if (c == null) { + // invalid coordinate + // set index back to initial, error index should already be set + pos.setIndex(initialIndex); + return null; + } + + // store coordinate + coordinates[i] = c.doubleValue(); + + } + + // parse suffix + CompositeFormat.parseAndIgnoreWhitespace(source, pos); + if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) { + return null; + } + + return coordinates; + + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Euclidean1D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Euclidean1D.java new file mode 100644 index 000000000..5d20506ca --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Euclidean1D.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.oned; + +import java.io.Serializable; + +import org.apache.commons.math.exception.MathUnsupportedOperationException; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.geometry.Space; + +/** + * This class implements a one-dimensional space. + * @version $Id:$ + * @since 3.0 + */ +public class Euclidean1D implements Serializable, Space { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -1178039568877797126L; + + /** Private constructor for the singleton. + */ + private Euclidean1D() { + } + + /** Get the unique instance. + * @return the unique instance + */ + public static Euclidean1D getInstance() { + return LazyHolder.INSTANCE; + } + + /** {@inheritDoc} */ + public int getDimension() { + return 1; + } + + /** {@inheritDoc} + *

+ * As the 1-dimension Euclidean space does not have proper sub-spaces, + * this method always throws a {@link MathUnsupportedOperationException} + *

+ * @return nothing + * @throws MathUnsupportedOperationException in all cases + */ + public Space getSubSpace() throws MathUnsupportedOperationException { + throw new MathUnsupportedOperationException(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1); + } + + // CHECKSTYLE: stop HideUtilityClassConstructor + /** Holder for the instance. + *

We use here the Initialization On Demand Holder Idiom.

+ */ + private static class LazyHolder { + /** Cached field instance. */ + private static final Euclidean1D INSTANCE = new Euclidean1D(); + } + // CHECKSTYLE: resume HideUtilityClassConstructor + + /** Handle deserialization of the singleton. + * @return the singleton instance + */ + private Object readResolve() { + // return the singleton instance + return LazyHolder.INSTANCE; + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Point1D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Point1D.java deleted file mode 100644 index 6db1a96b2..000000000 --- a/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Point1D.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.oned; - -import org.apache.commons.math.geometry.partitioning.Point; - -/** This class represents a 1D point. - *

Instances of this class are guaranteed to be immutable.

- * @version $Revision$ $Date$ - */ -public class Point1D implements Point { - - /** Point at 0.0 abscissa. */ - public static final Point1D ZERO = new Point1D(0.0); - - /** Point at 1.0 abscissa. */ - public static final Point1D ONE = new Point1D(1.0); - - /** Point at undefined (NaN) abscissa. */ - public static final Point1D UNDEFINED = new Point1D(Double.NaN); - - /** Abscissa of the point. */ - private double x; - - /** Simple constructor. - * @param x abscissa of the point - */ - public Point1D(final double x) { - this.x = x; - } - - /** Get the abscissa of the point. - * @return abscissa of the point - */ - public double getAbscissa() { - return x; - } - -} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1D.java new file mode 100644 index 000000000..e8aaa76b3 --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1D.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.oned; + +import java.text.NumberFormat; + +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.Space; +import org.apache.commons.math.util.FastMath; +import org.apache.commons.math.util.MathUtils; + +/** This class represents a 1D vector. + *

Instances of this class are guaranteed to be immutable.

+ * @version $Id:$ + * @since 3.0 + */ +public class Vector1D implements Vector { + + /** Origin (coordinates: 0). */ + public static final Vector1D ZERO = new Vector1D(0.0); + + /** Unit (coordinates: 1). */ + public static final Vector1D ONE = new Vector1D(1.0); + + // CHECKSTYLE: stop ConstantName + /** A vector with all coordinates set to NaN. */ + public static final Vector1D NaN = new Vector1D(Double.NaN); + // CHECKSTYLE: resume ConstantName + + /** A vector with all coordinates set to positive infinity. */ + public static final Vector1D POSITIVE_INFINITY = + new Vector1D(Double.POSITIVE_INFINITY); + + /** A vector with all coordinates set to negative infinity. */ + public static final Vector1D NEGATIVE_INFINITY = + new Vector1D(Double.NEGATIVE_INFINITY); + + /** Serializable UID. */ + private static final long serialVersionUID = 7556674948671647925L; + + /** Abscissa. */ + private final double x; + + /** Simple constructor. + * Build a vector from its coordinates + * @param x abscissa + * @see #getX() + */ + public Vector1D(double x) { + this.x = x; + } + + /** Multiplicative constructor + * Build a vector from another one and a scale factor. + * The vector built will be a * u + * @param a scale factor + * @param u base (unscaled) vector + */ + public Vector1D(double a, Vector1D u) { + this.x = a * u.x; + } + + /** Linear constructor + * Build a vector from two other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + */ + public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2) { + this.x = a1 * u1.x + a2 * u2.x; + } + + /** Linear constructor + * Build a vector from three other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + */ + public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2, + double a3, Vector1D u3) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x; + } + + /** Linear constructor + * Build a vector from four other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + * @param a4 fourth scale factor + * @param u4 fourth base (unscaled) vector + */ + public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2, + double a3, Vector1D u3, double a4, Vector1D u4) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x; + } + + /** Get the abscissa of the vector. + * @return abscissa of the vector + * @see #Vector1D(double) + */ + public double getX() { + return x; + } + + /** {@inheritDoc} */ + public Space getSpace() { + return Euclidean1D.getInstance(); + } + + /** {@inheritDoc} */ + public Vector1D getZero() { + return ZERO; + } + + /** {@inheritDoc} */ + public double getNorm1() { + return FastMath.abs(x); + } + + /** {@inheritDoc} */ + public double getNorm() { + return FastMath.abs(x); + } + + /** {@inheritDoc} */ + public double getNormSq() { + return x * x; + } + + /** {@inheritDoc} */ + public double getNormInf() { + return FastMath.abs(x); + } + + /** {@inheritDoc} */ + public Vector1D add(Vector v) { + Vector1D v1 = (Vector1D) v; + return new Vector1D(x + v1.getX()); + } + + /** {@inheritDoc} */ + public Vector1D add(double factor, Vector v) { + Vector1D v1 = (Vector1D) v; + return new Vector1D(x + factor * v1.getX()); + } + + /** {@inheritDoc} */ + public Vector1D subtract(Vector p) { + Vector1D p3 = (Vector1D) p; + return new Vector1D(x - p3.x); + } + + /** {@inheritDoc} */ + public Vector1D subtract(double factor, Vector v) { + Vector1D v1 = (Vector1D) v; + return new Vector1D(x - factor * v1.getX()); + } + + /** {@inheritDoc} */ + public boolean isNaN() { + return Double.isNaN(x); + } + + /** {@inheritDoc} */ + public boolean isInfinite() { + return !isNaN() && Double.isInfinite(x); + } + + /** {@inheritDoc} */ + public double distance1(Vector p) { + Vector1D p3 = (Vector1D) p; + final double dx = FastMath.abs(p3.x - x); + return dx; + } + + /** {@inheritDoc} */ + public double distance(Vector p) { + Vector1D p3 = (Vector1D) p; + final double dx = p3.x - x; + return FastMath.abs(dx); + } + + /** {@inheritDoc} */ + public double distanceInf(Vector p) { + Vector1D p3 = (Vector1D) p; + final double dx = FastMath.abs(p3.x - x); + return dx; + } + + /** {@inheritDoc} */ + public double distanceSq(Vector p) { + Vector1D p3 = (Vector1D) p; + final double dx = p3.x - x; + return dx * dx; + } + + /** Compute the distance between two vectors according to the L2 norm. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNorm() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the distance between p1 and p2 according to the L2 norm + */ + public static double distance(Vector1D p1, Vector1D p2) { + return p1.distance(p2); + } + + /** Compute the distance between two vectors according to the L norm. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNormInf() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the distance between p1 and p2 according to the L norm + */ + public static double distanceInf(Vector1D p1, Vector1D p2) { + return p1.distanceInf(p2); + } + + /** Compute the square of the distance between two vectors. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNormSq() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the square of the distance between p1 and p2 + */ + public static double distanceSq(Vector1D p1, Vector1D p2) { + return p1.distanceSq(p2); + } + + /** + * Test for the equality of two 1D vectors. + *

+ * If all coordinates of two 1D vectors are exactly the same, and none are + * Double.NaN, the two 1D vectors are considered to be equal. + *

+ *

+ * NaN coordinates are considered to affect globally the vector + * and be equals to each other - i.e, if either (or all) coordinates of the + * 1D vector are equal to Double.NaN, the 1D vector is equal to + * {@link #NaN}. + *

+ * + * @param other Object to test for equality to this + * @return true if two 1D vector objects are equal, false if + * object is null, not an instance of Vector1D, or + * not equal to this Vector1D instance + * + */ + @Override + public boolean equals(Object other) { + + if (this == other) { + return true; + } + + if (other instanceof Vector1D) { + final Vector1D rhs = (Vector1D)other; + if (rhs.isNaN()) { + return this.isNaN(); + } + + return x == rhs.x; + } + return false; + } + + /** + * Get a hashCode for the 1D vector. + *

+ * All NaN values have the same hash code.

+ * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + if (isNaN()) { + return 7785; + } + return 997 * MathUtils.hash(x); + } + + /** Get a string representation of this vector. + * @return a string representation of this vector + */ + @Override + public String toString() { + return Vector1DFormat.getInstance().format(this); + } + + /** {@inheritDoc} */ + public String toString(final NumberFormat format) { + return new Vector1DFormat(format).format(this); + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1DFormat.java b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1DFormat.java new file mode 100644 index 000000000..8d8335e9e --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/oned/Vector1DFormat.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.oned; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.commons.math.exception.MathParseException; +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.VectorFormat; +import org.apache.commons.math.util.CompositeFormat; + +/** + * Formats a 1D vector in components list format "{x}". + *

The prefix and suffix "{" and "}" can be replaced by + * any user-defined strings. The number format for components can be configured.

+ *

White space is ignored at parse time, even if it is in the prefix, suffix + * or separator specifications. So even if the default separator does include a space + * character that is used at format time, both input string "{1}" and + * " { 1 } " will be parsed without error and the same vector will be + * returned. In the second case, however, the parse position after parsing will be + * just after the closing curly brace, i.e. just before the trailing space.

+ * + * @version $Id:$ + * @since 3.0 + */ +public class Vector1DFormat extends VectorFormat { + + /** + * Create an instance with default settings. + *

The instance uses the default prefix, suffix and separator: + * "{", "}", and "; " and the default number format for components.

+ */ + public Vector1DFormat() { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, + CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with a custom number format for components. + * @param format the custom format for components. + */ + public Vector1DFormat(final NumberFormat format) { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + } + + /** + * Create an instance with custom prefix, suffix and separator. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + */ + public Vector1DFormat(final String prefix, final String suffix) { + super(prefix, suffix, DEFAULT_SEPARATOR, CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with custom prefix, suffix, separator and format + * for components. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param format the custom format for components. + */ + public Vector1DFormat(final String prefix, final String suffix, + final NumberFormat format) { + super(prefix, suffix, DEFAULT_SEPARATOR, format); + } + + /** + * Returns the default 1D vector format for the current locale. + * @return the default 1D vector format. + */ + public static Vector1DFormat getInstance() { + return getInstance(Locale.getDefault()); + } + + /** + * Returns the default 1D vector format for the given locale. + * @param locale the specific locale used by the format. + * @return the 1D vector format specific to the given locale. + */ + public static Vector1DFormat getInstance(final Locale locale) { + return new Vector1DFormat(CompositeFormat.getDefaultNumberFormat(locale)); + } + + /** {@inheritDoc} */ + public StringBuffer format(final Vector vector, final StringBuffer toAppendTo, + final FieldPosition pos) { + final Vector1D p1 = (Vector1D) vector; + return format(toAppendTo, pos, p1.getX()); + } + + /** {@inheritDoc} */ + public Vector1D parse(final String source) { + ParsePosition parsePosition = new ParsePosition(0); + Vector1D result = parse(source, parsePosition); + if (parsePosition.getIndex() == 0) { + throw new MathParseException(source, + parsePosition.getErrorIndex(), + Vector1D.class); + } + return result; + } + + /** {@inheritDoc} */ + public Vector1D parse(final String source, final ParsePosition pos) { + final double[] coordinates = parseCoordinates(1, source, pos); + if (coordinates == null) { + return null; + } + return new Vector1D(coordinates[0]); + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Euclidean3D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Euclidean3D.java new file mode 100644 index 000000000..69f080733 --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Euclidean3D.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.threed; + +import java.io.Serializable; + +import org.apache.commons.math.geometry.Space; +import org.apache.commons.math.geometry.euclidean.twod.Euclidean2D; + +/** + * This class implements a three-dimensional space. + * @version $Id:$ + * @since 3.0 + */ +public class Euclidean3D implements Serializable, Space { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 6249091865814886817L; + + /** Private constructor for the singleton. + */ + private Euclidean3D() { + } + + /** Get the unique instance. + * @return the unique instance + */ + public static Euclidean3D getInstance() { + return LazyHolder.INSTANCE; + } + + /** {@inheritDoc} */ + public int getDimension() { + return 3; + } + + /** {@inheritDoc} */ + public Euclidean2D getSubSpace() { + return Euclidean2D.getInstance(); + } + + // CHECKSTYLE: stop HideUtilityClassConstructor + /** Holder for the instance. + *

We use here the Initialization On Demand Holder Idiom.

+ */ + private static class LazyHolder { + /** Cached field instance. */ + private static final Euclidean3D INSTANCE = new Euclidean3D(); + } + // CHECKSTYLE: resume HideUtilityClassConstructor + + /** Handle deserialization of the singleton. + * @return the singleton instance + */ + private Object readResolve() { + // return the singleton instance + return LazyHolder.INSTANCE; + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Point3D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Point3D.java deleted file mode 100644 index acdeb61e7..000000000 --- a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Point3D.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.threed; - -import org.apache.commons.math.geometry.partitioning.Point; - -/** This class represents a 3D point. - *

Instances of this class are guaranteed to be immutable.

- * @version $Revision$ $Date$ - */ -public class Point3D extends Vector3D implements Point { - - /** Point at undefined (NaN) coordinates. */ - public static final Point3D UNDEFINED = new Point3D(Double.NaN, Double.NaN, Double.NaN); - - /** Serializable UID. */ - private static final long serialVersionUID = 9128130934224884451L; - - /** Simple constructor. - * Build a vector from its coordinates - * @param x abscissa - * @param y ordinate - * @param z height - * @see #getX() - * @see #getY() - * @see #getZ() - */ - public Point3D(final double x, final double y, final double z) { - super(x, y, z); - } - - /** Simple constructor. - * Build a vector from its azimuthal coordinates - * @param alpha azimuth (α) around Z - * (0 is +X, π/2 is +Y, π is -X and 3π/2 is -Y) - * @param delta elevation (δ) above (XY) plane, from -π/2 to +π/2 - * @see #getAlpha() - * @see #getDelta() - */ - public Point3D(final double alpha, final double delta) { - super(alpha, delta); - } - - /** Multiplicative constructor - * Build a vector from another one and a scale factor. - * The vector built will be a * u - * @param a scale factor - * @param u base (unscaled) vector - */ - public Point3D(final double a, final Vector3D u) { - super(a, u); - } - - /** Linear constructor - * Build a vector from two other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - */ - public Point3D(final double a1, final Vector3D u1, final double a2, final Vector3D u2) { - super(a1, u1, a2, u2); - } - - /** Linear constructor - * Build a vector from three other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - * @param a3 third scale factor - * @param u3 third base (unscaled) vector - */ - public Point3D(final double a1, final Vector3D u1, final double a2, final Vector3D u2, - final double a3, final Vector3D u3) { - super(a1, u1, a2, u2, a3, u3); - } - - /** Linear constructor - * Build a vector from four other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - * @param a3 third scale factor - * @param u3 third base (unscaled) vector - * @param a4 fourth scale factor - * @param u4 fourth base (unscaled) vector - */ - public Point3D(final double a1, final Vector3D u1, final double a2, final Vector3D u2, - final double a3, final Vector3D u3, final double a4, final Vector3D u4) { - super(a1, u1, a2, u2, a3, u3, a4, u4); - } - -} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3D.java index d9be49073..b20999b30 100644 --- a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3D.java +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3D.java @@ -18,545 +18,564 @@ package org.apache.commons.math.geometry.euclidean.threed; import java.io.Serializable; +import java.text.NumberFormat; import org.apache.commons.math.exception.MathArithmeticException; import org.apache.commons.math.exception.util.LocalizedFormats; -import org.apache.commons.math.util.MathUtils; +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.Space; import org.apache.commons.math.util.FastMath; +import org.apache.commons.math.util.MathUtils; /** * This class implements vectors in a three-dimensional space. *

Instance of this class are guaranteed to be immutable.

- * @version $Revision$ $Date$ + * @version $Id$ * @since 1.2 */ -public class Vector3D implements Serializable { - /** Null vector (coordinates: 0, 0, 0). */ - public static final Vector3D ZERO = new Vector3D(0, 0, 0); +public class Vector3D implements Serializable, Vector { - /** First canonical vector (coordinates: 1, 0, 0). */ - public static final Vector3D PLUS_I = new Vector3D(1, 0, 0); + /** Serializable version id. */ + private static final long serialVersionUID = 1313493323784566947L; - /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */ - public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0); + /** Null vector (coordinates: 0, 0, 0). */ + public static final Vector3D ZERO = new Vector3D(0, 0, 0); - /** Second canonical vector (coordinates: 0, 1, 0). */ - public static final Vector3D PLUS_J = new Vector3D(0, 1, 0); + /** First canonical vector (coordinates: 1, 0, 0). */ + public static final Vector3D PLUS_I = new Vector3D(1, 0, 0); - /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */ - public static final Vector3D MINUS_J = new Vector3D(0, -1, 0); + /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */ + public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0); - /** Third canonical vector (coordinates: 0, 0, 1). */ - public static final Vector3D PLUS_K = new Vector3D(0, 0, 1); + /** Second canonical vector (coordinates: 0, 1, 0). */ + public static final Vector3D PLUS_J = new Vector3D(0, 1, 0); - /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */ - public static final Vector3D MINUS_K = new Vector3D(0, 0, -1); + /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */ + public static final Vector3D MINUS_J = new Vector3D(0, -1, 0); - // CHECKSTYLE: stop ConstantName - /** A vector with all coordinates set to NaN. */ - public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN); - // CHECKSTYLE: resume ConstantName + /** Third canonical vector (coordinates: 0, 0, 1). */ + public static final Vector3D PLUS_K = new Vector3D(0, 0, 1); - /** A vector with all coordinates set to positive infinity. */ - public static final Vector3D POSITIVE_INFINITY = - new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */ + public static final Vector3D MINUS_K = new Vector3D(0, 0, -1); - /** A vector with all coordinates set to negative infinity. */ - public static final Vector3D NEGATIVE_INFINITY = - new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + // CHECKSTYLE: stop ConstantName + /** A vector with all coordinates set to NaN. */ + public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN); + // CHECKSTYLE: resume ConstantName - /** Default format. */ - private static final Vector3DFormat DEFAULT_FORMAT = - Vector3DFormat.getInstance(); + /** A vector with all coordinates set to positive infinity. */ + public static final Vector3D POSITIVE_INFINITY = + new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); - /** Serializable version identifier. */ - private static final long serialVersionUID = 5133268763396045979L; + /** A vector with all coordinates set to negative infinity. */ + public static final Vector3D NEGATIVE_INFINITY = + new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); - /** Abscissa. */ - private final double x; + /** Serializable version identifier. */ + + /** Abscissa. */ + private final double x; - /** Ordinate. */ - private final double y; + /** Ordinate. */ + private final double y; - /** Height. */ - private final double z; + /** Height. */ + private final double z; - /** Simple constructor. - * Build a vector from its coordinates - * @param x abscissa - * @param y ordinate - * @param z height - * @see #getX() - * @see #getY() - * @see #getZ() - */ - public Vector3D(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - /** Simple constructor. - * Build a vector from its azimuthal coordinates - * @param alpha azimuth (α) around Z - * (0 is +X, π/2 is +Y, π is -X and 3π/2 is -Y) - * @param delta elevation (δ) above (XY) plane, from -π/2 to +π/2 - * @see #getAlpha() - * @see #getDelta() - */ - public Vector3D(double alpha, double delta) { - double cosDelta = FastMath.cos(delta); - this.x = FastMath.cos(alpha) * cosDelta; - this.y = FastMath.sin(alpha) * cosDelta; - this.z = FastMath.sin(delta); - } - - /** Multiplicative constructor - * Build a vector from another one and a scale factor. - * The vector built will be a * u - * @param a scale factor - * @param u base (unscaled) vector - */ - public Vector3D(double a, Vector3D u) { - this.x = a * u.x; - this.y = a * u.y; - this.z = a * u.z; - } - - /** Linear constructor - * Build a vector from two other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - */ - public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2) { - this.x = a1 * u1.x + a2 * u2.x; - this.y = a1 * u1.y + a2 * u2.y; - this.z = a1 * u1.z + a2 * u2.z; - } - - /** Linear constructor - * Build a vector from three other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - * @param a3 third scale factor - * @param u3 third base (unscaled) vector - */ - public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, - double a3, Vector3D u3) { - this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x; - this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y; - this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z; - } - - /** Linear constructor - * Build a vector from four other ones and corresponding scale factors. - * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 - * @param a1 first scale factor - * @param u1 first base (unscaled) vector - * @param a2 second scale factor - * @param u2 second base (unscaled) vector - * @param a3 third scale factor - * @param u3 third base (unscaled) vector - * @param a4 fourth scale factor - * @param u4 fourth base (unscaled) vector - */ - public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, - double a3, Vector3D u3, double a4, Vector3D u4) { - this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x; - this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y; - this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z + a4 * u4.z; - } - - /** Get the abscissa of the vector. - * @return abscissa of the vector - * @see #Vector3D(double, double, double) - */ - public double getX() { - return x; - } - - /** Get the ordinate of the vector. - * @return ordinate of the vector - * @see #Vector3D(double, double, double) - */ - public double getY() { - return y; - } - - /** Get the height of the vector. - * @return height of the vector - * @see #Vector3D(double, double, double) - */ - public double getZ() { - return z; - } - - /** Get the L1 norm for the vector. - * @return L1 norm for the vector - */ - public double getNorm1() { - return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z); - } - - /** Get the L2 norm for the vector. - * @return euclidean norm for the vector - */ - public double getNorm() { - return FastMath.sqrt (x * x + y * y + z * z); - } - - /** Get the square of the norm for the vector. - * @return square of the euclidean norm for the vector - */ - public double getNormSq() { - return x * x + y * y + z * z; - } - - /** Get the L norm for the vector. - * @return L norm for the vector - */ - public double getNormInf() { - return FastMath.max(FastMath.max(FastMath.abs(x), FastMath.abs(y)), FastMath.abs(z)); - } - - /** Get the azimuth of the vector. - * @return azimuth (α) of the vector, between -π and +π - * @see #Vector3D(double, double) - */ - public double getAlpha() { - return FastMath.atan2(y, x); - } - - /** Get the elevation of the vector. - * @return elevation (δ) of the vector, between -π/2 and +π/2 - * @see #Vector3D(double, double) - */ - public double getDelta() { - return FastMath.asin(z / getNorm()); - } - - /** Add a vector to the instance. - * @param v vector to add - * @return a new vector - */ - public Vector3D add(Vector3D v) { - return new Vector3D(x + v.x, y + v.y, z + v.z); - } - - /** Add a scaled vector to the instance. - * @param factor scale factor to apply to v before adding it - * @param v vector to add - * @return a new vector - */ - public Vector3D add(double factor, Vector3D v) { - return new Vector3D(x + factor * v.x, y + factor * v.y, z + factor * v.z); - } - - /** Subtract a vector from the instance. - * @param v vector to subtract - * @return a new vector - */ - public Vector3D subtract(Vector3D v) { - return new Vector3D(x - v.x, y - v.y, z - v.z); - } - - /** Subtract a scaled vector from the instance. - * @param factor scale factor to apply to v before subtracting it - * @param v vector to subtract - * @return a new vector - */ - public Vector3D subtract(double factor, Vector3D v) { - return new Vector3D(x - factor * v.x, y - factor * v.y, z - factor * v.z); - } - - /** Get a normalized vector aligned with the instance. - * @return a new normalized vector - * @exception ArithmeticException if the norm is zero - */ - public Vector3D normalize() { - double s = getNorm(); - if (s == 0) { - throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR); - } - return scalarMultiply(1 / s); - } - - /** Get a vector orthogonal to the instance. - *

There are an infinite number of normalized vectors orthogonal - * to the instance. This method picks up one of them almost - * arbitrarily. It is useful when one needs to compute a reference - * frame with one of the axes in a predefined direction. The - * following example shows how to build a frame having the k axis - * aligned with the known vector u : - *


-   *   Vector3D k = u.normalize();
-   *   Vector3D i = k.orthogonal();
-   *   Vector3D j = Vector3D.crossProduct(k, i);
-   * 

- * @return a new normalized vector orthogonal to the instance - * @exception ArithmeticException if the norm of the instance is null - */ - public Vector3D orthogonal() { - - double threshold = 0.6 * getNorm(); - if (threshold == 0) { - throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); + /** Simple constructor. + * Build a vector from its coordinates + * @param x abscissa + * @param y ordinate + * @param z height + * @see #getX() + * @see #getY() + * @see #getZ() + */ + public Vector3D(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; } - if ((x >= -threshold) && (x <= threshold)) { - double inverse = 1 / FastMath.sqrt(y * y + z * z); - return new Vector3D(0, inverse * z, -inverse * y); - } else if ((y >= -threshold) && (y <= threshold)) { - double inverse = 1 / FastMath.sqrt(x * x + z * z); - return new Vector3D(-inverse * z, 0, inverse * x); - } - double inverse = 1 / FastMath.sqrt(x * x + y * y); - return new Vector3D(inverse * y, -inverse * x, 0); - - } - - /** Compute the angular separation between two vectors. - *

This method computes the angular separation between two - * vectors using the dot product for well separated vectors and the - * cross product for almost aligned vectors. This allows to have a - * good accuracy in all cases, even for vectors very close to each - * other.

- * @param v1 first vector - * @param v2 second vector - * @return angular separation between v1 and v2 - * @exception ArithmeticException if either vector has a null norm - */ - public static double angle(Vector3D v1, Vector3D v2) { - - double normProduct = v1.getNorm() * v2.getNorm(); - if (normProduct == 0) { - throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); + /** Simple constructor. + * Build a vector from its azimuthal coordinates + * @param alpha azimuth (α) around Z + * (0 is +X, π/2 is +Y, π is -X and 3π/2 is -Y) + * @param delta elevation (δ) above (XY) plane, from -π/2 to +π/2 + * @see #getAlpha() + * @see #getDelta() + */ + public Vector3D(double alpha, double delta) { + double cosDelta = FastMath.cos(delta); + this.x = FastMath.cos(alpha) * cosDelta; + this.y = FastMath.sin(alpha) * cosDelta; + this.z = FastMath.sin(delta); } - double dot = dotProduct(v1, v2); - double threshold = normProduct * 0.9999; - if ((dot < -threshold) || (dot > threshold)) { - // the vectors are almost aligned, compute using the sine - Vector3D v3 = crossProduct(v1, v2); - if (dot >= 0) { - return FastMath.asin(v3.getNorm() / normProduct); - } - return FastMath.PI - FastMath.asin(v3.getNorm() / normProduct); + /** Multiplicative constructor + * Build a vector from another one and a scale factor. + * The vector built will be a * u + * @param a scale factor + * @param u base (unscaled) vector + */ + public Vector3D(double a, Vector3D u) { + this.x = a * u.x; + this.y = a * u.y; + this.z = a * u.z; } - // the vectors are sufficiently separated to use the cosine - return FastMath.acos(dot / normProduct); - - } - - /** Get the opposite of the instance. - * @return a new vector which is opposite to the instance - */ - public Vector3D negate() { - return new Vector3D(-x, -y, -z); - } - - /** Multiply the instance by a scalar - * @param a scalar - * @return a new vector - */ - public Vector3D scalarMultiply(double a) { - return new Vector3D(a * x, a * y, a * z); - } - - /** - * Returns true if any coordinate of this vector is NaN; false otherwise - * @return true if any coordinate of this vector is NaN; false otherwise - */ - public boolean isNaN() { - return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z); - } - - /** - * Returns true if any coordinate of this vector is infinite and none are NaN; - * false otherwise - * @return true if any coordinate of this vector is infinite and none are NaN; - * false otherwise - */ - public boolean isInfinite() { - return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z)); - } - - /** - * Test for the equality of two 3D vectors. - *

- * If all coordinates of two 3D vectors are exactly the same, and none are - * Double.NaN, the two 3D vectors are considered to be equal. - *

- *

- * NaN coordinates are considered to affect globally the vector - * and be equals to each other - i.e, if either (or all) coordinates of the - * 3D vector are equal to Double.NaN, the 3D vector is equal to - * {@link #NaN}. - *

- * - * @param other Object to test for equality to this - * @return true if two 3D vector objects are equal, false if - * object is null, not an instance of Vector3D, or - * not equal to this Vector3D instance - * - */ - @Override - public boolean equals(Object other) { - - if (this == other) { - return true; + /** Linear constructor + * Build a vector from two other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + */ + public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2) { + this.x = a1 * u1.x + a2 * u2.x; + this.y = a1 * u1.y + a2 * u2.y; + this.z = a1 * u1.z + a2 * u2.z; } - if (other instanceof Vector3D) { - final Vector3D rhs = (Vector3D)other; - if (rhs.isNaN()) { - return this.isNaN(); - } - - return (x == rhs.x) && (y == rhs.y) && (z == rhs.z); + /** Linear constructor + * Build a vector from three other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + */ + public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, + double a3, Vector3D u3) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x; + this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y; + this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z; } - return false; - } - /** - * Get a hashCode for the 3D vector. - *

- * All NaN values have the same hash code.

- * - * @return a hash code value for this object - */ - @Override - public int hashCode() { - if (isNaN()) { - return 8; - } - return 31 * (23 * MathUtils.hash(x) + 19 * MathUtils.hash(y) + MathUtils.hash(z)); - } + /** Linear constructor + * Build a vector from four other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + * @param a4 fourth scale factor + * @param u4 fourth base (unscaled) vector + */ + public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, + double a3, Vector3D u3, double a4, Vector3D u4) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x; + this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y; + this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z + a4 * u4.z; + } - /** Compute the dot-product of two vectors. - * @param v1 first vector - * @param v2 second vector - * @return the dot product v1.v2 - */ - public static double dotProduct(Vector3D v1, Vector3D v2) { - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; - } + /** Get the abscissa of the vector. + * @return abscissa of the vector + * @see #Vector3D(double, double, double) + */ + public double getX() { + return x; + } - /** Compute the cross-product of two vectors. - * @param v1 first vector - * @param v2 second vector - * @return the cross product v1 ^ v2 as a new Vector - */ - public static Vector3D crossProduct(final Vector3D v1, final Vector3D v2) { + /** Get the ordinate of the vector. + * @return ordinate of the vector + * @see #Vector3D(double, double, double) + */ + public double getY() { + return y; + } - final double n1 = v1.getNormSq(); - final double n2 = v2.getNormSq(); - if ((n1 * n2) < MathUtils.SAFE_MIN) { - return ZERO; - } + /** Get the height of the vector. + * @return height of the vector + * @see #Vector3D(double, double, double) + */ + public double getZ() { + return z; + } - // rescale both vectors without losing precision, - // to ensure their norm are the same order of magnitude - final int deltaExp = (FastMath.getExponent(n1) - FastMath.getExponent(n2)) / 4; - final double x1 = FastMath.scalb(v1.x, -deltaExp); - final double y1 = FastMath.scalb(v1.y, -deltaExp); - final double z1 = FastMath.scalb(v1.z, -deltaExp); - final double x2 = FastMath.scalb(v2.x, deltaExp); - final double y2 = FastMath.scalb(v2.y, deltaExp); - final double z2 = FastMath.scalb(v2.z, deltaExp); + /** {@inheritDoc} */ + public Space getSpace() { + return Euclidean3D.getInstance(); + } - // we reduce cancellation errors by preconditioning, - // we replace v1 by v3 = v1 - rho v2 with rho chosen in order to compute - // v3 without loss of precision. See Kahan lecture - // "Computing Cross-Products and Rotations in 2- and 3-Dimensional Euclidean Spaces" - // available at http://www.cs.berkeley.edu/~wkahan/MathH110/Cross.pdf + /** {@inheritDoc} */ + public Vector3D getZero() { + return ZERO; + } - // compute rho as an 8 bits approximation of v1.v2 / v2.v2 - final double ratio = (x1 * x2 + y1 * y2 + z1 * z2) / FastMath.scalb(n2, 2 * deltaExp); - final double rho = FastMath.rint(256 * ratio) / 256; + /** {@inheritDoc} */ + public double getNorm1() { + return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z); + } - final double x3 = x1 - rho * x2; - final double y3 = y1 - rho * y2; - final double z3 = z1 - rho * z2; + /** {@inheritDoc} */ + public double getNorm() { + return FastMath.sqrt (x * x + y * y + z * z); + } - // compute cross product from v3 and v2 instead of v1 and v2 - return new Vector3D(y3 * z2 - z3 * y2, z3 * x2 - x3 * z2, x3 * y2 - y3 * x2); + /** {@inheritDoc} */ + public double getNormSq() { + return x * x + y * y + z * z; + } - } + /** {@inheritDoc} */ + public double getNormInf() { + return FastMath.max(FastMath.max(FastMath.abs(x), FastMath.abs(y)), FastMath.abs(z)); + } - /** Compute the distance between two vectors according to the L1 norm. - *

Calling this method is equivalent to calling: - * v1.subtract(v2).getNorm1() except that no intermediate - * vector is built

- * @param v1 first vector - * @param v2 second vector - * @return the distance between v1 and v2 according to the L1 norm - */ - public static double distance1(Vector3D v1, Vector3D v2) { - final double dx = FastMath.abs(v2.x - v1.x); - final double dy = FastMath.abs(v2.y - v1.y); - final double dz = FastMath.abs(v2.z - v1.z); - return dx + dy + dz; - } + /** Get the azimuth of the vector. + * @return azimuth (α) of the vector, between -π and +π + * @see #Vector3D(double, double) + */ + public double getAlpha() { + return FastMath.atan2(y, x); + } - /** Compute the distance between two vectors according to the L2 norm. - *

Calling this method is equivalent to calling: - * v1.subtract(v2).getNorm() except that no intermediate - * vector is built

- * @param v1 first vector - * @param v2 second vector - * @return the distance between v1 and v2 according to the L2 norm - */ - public static double distance(Vector3D v1, Vector3D v2) { - final double dx = v2.x - v1.x; - final double dy = v2.y - v1.y; - final double dz = v2.z - v1.z; - return FastMath.sqrt(dx * dx + dy * dy + dz * dz); - } + /** Get the elevation of the vector. + * @return elevation (δ) of the vector, between -π/2 and +π/2 + * @see #Vector3D(double, double) + */ + public double getDelta() { + return FastMath.asin(z / getNorm()); + } - /** Compute the distance between two vectors according to the L norm. - *

Calling this method is equivalent to calling: - * v1.subtract(v2).getNormInf() except that no intermediate - * vector is built

- * @param v1 first vector - * @param v2 second vector - * @return the distance between v1 and v2 according to the L norm - */ - public static double distanceInf(Vector3D v1, Vector3D v2) { - final double dx = FastMath.abs(v2.x - v1.x); - final double dy = FastMath.abs(v2.y - v1.y); - final double dz = FastMath.abs(v2.z - v1.z); - return FastMath.max(FastMath.max(dx, dy), dz); - } + /** {@inheritDoc} */ + public Vector3D add(final Vector v) { + final Vector3D v3 = (Vector3D) v; + return new Vector3D(x + v3.x, y + v3.y, z + v3.z); + } - /** Compute the square of the distance between two vectors. - *

Calling this method is equivalent to calling: - * v1.subtract(v2).getNormSq() except that no intermediate - * vector is built

- * @param v1 first vector - * @param v2 second vector - * @return the square of the distance between v1 and v2 - */ - public static double distanceSq(Vector3D v1, Vector3D v2) { - final double dx = v2.x - v1.x; - final double dy = v2.y - v1.y; - final double dz = v2.z - v1.z; - return dx * dx + dy * dy + dz * dz; - } + /** {@inheritDoc} */ + public Vector3D add(double factor, final Vector v) { + final Vector3D v3 = (Vector3D) v; + return new Vector3D(x + factor * v3.x, y + factor * v3.y, z + factor * v3.z); + } - /** Get a string representation of this vector. - * @return a string representation of this vector - */ - @Override - public String toString() { - return DEFAULT_FORMAT.format(this); - } + /** {@inheritDoc} */ + public Vector3D subtract(final Vector v) { + final Vector3D v3 = (Vector3D) v; + return new Vector3D(x - v3.x, y - v3.y, z - v3.z); + } + + /** {@inheritDoc} */ + public Vector3D subtract(final double factor, final Vector v) { + final Vector3D v3 = (Vector3D) v; + return new Vector3D(x - factor * v3.x, y - factor * v3.y, z - factor * v3.z); + } + + /** {@inheritDoc} */ + public Vector3D normalize() { + double s = getNorm(); + if (s == 0) { + throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR); + } + return scalarMultiply(1 / s); + } + + /** Get a vector orthogonal to the instance. + *

There are an infinite number of normalized vectors orthogonal + * to the instance. This method picks up one of them almost + * arbitrarily. It is useful when one needs to compute a reference + * frame with one of the axes in a predefined direction. The + * following example shows how to build a frame having the k axis + * aligned with the known vector u : + *


+     *   Vector3D k = u.normalize();
+     *   Vector3D i = k.orthogonal();
+     *   Vector3D j = Vector3D.crossProduct(k, i);
+     * 

+ * @return a new normalized vector orthogonal to the instance + * @exception MathArithmeticException if the norm of the instance is null + */ + public Vector3D orthogonal() { + + double threshold = 0.6 * getNorm(); + if (threshold == 0) { + throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); + } + + if ((x >= -threshold) && (x <= threshold)) { + double inverse = 1 / FastMath.sqrt(y * y + z * z); + return new Vector3D(0, inverse * z, -inverse * y); + } else if ((y >= -threshold) && (y <= threshold)) { + double inverse = 1 / FastMath.sqrt(x * x + z * z); + return new Vector3D(-inverse * z, 0, inverse * x); + } + double inverse = 1 / FastMath.sqrt(x * x + y * y); + return new Vector3D(inverse * y, -inverse * x, 0); + + } + + /** Compute the angular separation between two vectors. + *

This method computes the angular separation between two + * vectors using the dot product for well separated vectors and the + * cross product for almost aligned vectors. This allows to have a + * good accuracy in all cases, even for vectors very close to each + * other.

+ * @param v1 first vector + * @param v2 second vector + * @return angular separation between v1 and v2 + * @exception MathArithmeticException if either vector has a null norm + */ + public static double angle(Vector3D v1, Vector3D v2) { + + double normProduct = v1.getNorm() * v2.getNorm(); + if (normProduct == 0) { + throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); + } + + double dot = dotProduct(v1, v2); + double threshold = normProduct * 0.9999; + if ((dot < -threshold) || (dot > threshold)) { + // the vectors are almost aligned, compute using the sine + Vector3D v3 = crossProduct(v1, v2); + if (dot >= 0) { + return FastMath.asin(v3.getNorm() / normProduct); + } + return FastMath.PI - FastMath.asin(v3.getNorm() / normProduct); + } + + // the vectors are sufficiently separated to use the cosine + return FastMath.acos(dot / normProduct); + + } + + /** {@inheritDoc} */ + public Vector3D negate() { + return new Vector3D(-x, -y, -z); + } + + /** {@inheritDoc} */ + public Vector3D scalarMultiply(double a) { + return new Vector3D(a * x, a * y, a * z); + } + + /** {@inheritDoc} */ + public boolean isNaN() { + return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z); + } + + /** {@inheritDoc} */ + public boolean isInfinite() { + return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z)); + } + + /** + * Test for the equality of two 3D vectors. + *

+ * If all coordinates of two 3D vectors are exactly the same, and none are + * Double.NaN, the two 3D vectors are considered to be equal. + *

+ *

+ * NaN coordinates are considered to affect globally the vector + * and be equals to each other - i.e, if either (or all) coordinates of the + * 3D vector are equal to Double.NaN, the 3D vector is equal to + * {@link #NaN}. + *

+ * + * @param other Object to test for equality to this + * @return true if two 3D vector objects are equal, false if + * object is null, not an instance of Vector3D, or + * not equal to this Vector3D instance + * + */ + @Override + public boolean equals(Object other) { + + if (this == other) { + return true; + } + + if (other instanceof Vector3D) { + final Vector3D rhs = (Vector3D)other; + if (rhs.isNaN()) { + return this.isNaN(); + } + + return (x == rhs.x) && (y == rhs.y) && (z == rhs.z); + } + return false; + } + + /** + * Get a hashCode for the 3D vector. + *

+ * All NaN values have the same hash code.

+ * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + if (isNaN()) { + return 642; + } + return 643 * (164 * MathUtils.hash(x) + 3 * MathUtils.hash(y) + MathUtils.hash(z)); + } + + /** {@inheritDoc} */ + public double dotProduct(final Vector v) { + final Vector3D v3 = (Vector3D) v; + return x * v3.x + y * v3.y + z * v3.z; + } + + /** {@inheritDoc} */ + public Vector3D crossProduct(final Vector v) { + final Vector3D v3 = (Vector3D) v; + + final double n1 = getNormSq(); + final double n2 = v.getNormSq(); + if ((n1 * n2) < MathUtils.SAFE_MIN) { + return ZERO; + } + + // rescale both vectors without losing precision, + // to ensure their norm are the same order of magnitude + final int deltaExp = (FastMath.getExponent(n1) - FastMath.getExponent(n2)) / 4; + final double x1 = FastMath.scalb(x, -deltaExp); + final double y1 = FastMath.scalb(y, -deltaExp); + final double z1 = FastMath.scalb(z, -deltaExp); + final double x2 = FastMath.scalb(v3.x, deltaExp); + final double y2 = FastMath.scalb(v3.y, deltaExp); + final double z2 = FastMath.scalb(v3.z, deltaExp); + + // we reduce cancellation errors by preconditioning, + // we replace v1 by v3 = v1 - rho v2 with rho chosen in order to compute + // v3 without loss of precision. See Kahan lecture + // "Computing Cross-Products and Rotations in 2- and 3-Dimensional Euclidean Spaces" + // available at http://www.cs.berkeley.edu/~wkahan/MathH110/Cross.pdf + + // compute rho as an 8 bits approximation of v1.v2 / v2.v2 + final double ratio = (x1 * x2 + y1 * y2 + z1 * z2) / FastMath.scalb(n2, 2 * deltaExp); + final double rho = FastMath.rint(256 * ratio) / 256; + + final double x3 = x1 - rho * x2; + final double y3 = y1 - rho * y2; + final double z3 = z1 - rho * z2; + + // compute cross product from v3 and v2 instead of v1 and v2 + return new Vector3D(y3 * z2 - z3 * y2, z3 * x2 - x3 * z2, x3 * y2 - y3 * x2); + + } + + /** {@inheritDoc} */ + public double distance1(Vector v) { + final Vector3D v3 = (Vector3D) v; + final double dx = FastMath.abs(v3.x - x); + final double dy = FastMath.abs(v3.y - y); + final double dz = FastMath.abs(v3.z - z); + return dx + dy + dz; + } + + /** {@inheritDoc} */ + public double distance(Vector v) { + final Vector3D v3 = (Vector3D) v; + final double dx = v3.x - x; + final double dy = v3.y - y; + final double dz = v3.z - z; + return FastMath.sqrt(dx * dx + dy * dy + dz * dz); + } + + /** {@inheritDoc} */ + public double distanceInf(Vector v) { + final Vector3D v3 = (Vector3D) v; + final double dx = FastMath.abs(v3.x - x); + final double dy = FastMath.abs(v3.y - y); + final double dz = FastMath.abs(v3.z - z); + return FastMath.max(FastMath.max(dx, dy), dz); + } + + /** {@inheritDoc} */ + public double distanceSq(Vector v) { + final Vector3D v3 = (Vector3D) v; + final double dx = v3.x - x; + final double dy = v3.y - y; + final double dz = v3.z - z; + return dx * dx + dy * dy + dz * dz; + } + + /** Compute the dot-product of two vectors. + * @param v1 first vector + * @param v2 second vector + * @return the dot product v1.v2 + */ + public static double dotProduct(Vector3D v1, Vector3D v2) { + return v1.dotProduct(v2); + } + + /** Compute the cross-product of two vectors. + * @param v1 first vector + * @param v2 second vector + * @return the cross product v1 ^ v2 as a new Vector + */ + public static Vector3D crossProduct(final Vector3D v1, final Vector3D v2) { + return v1.crossProduct(v2); + } + + /** Compute the distance between two vectors according to the L1 norm. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNorm1() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the distance between v1 and v2 according to the L1 norm + */ + public static double distance1(Vector3D v1, Vector3D v2) { + return v1.distance1(v2); + } + + /** Compute the distance between two vectors according to the L2 norm. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNorm() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the distance between v1 and v2 according to the L2 norm + */ + public static double distance(Vector3D v1, Vector3D v2) { + return v1.distance(v2); + } + + /** Compute the distance between two vectors according to the L norm. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNormInf() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the distance between v1 and v2 according to the L norm + */ + public static double distanceInf(Vector3D v1, Vector3D v2) { + return v1.distanceInf(v2); + } + + /** Compute the square of the distance between two vectors. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNormSq() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the square of the distance between v1 and v2 + */ + public static double distanceSq(Vector3D v1, Vector3D v2) { + return v1.distanceSq(v2); + } + + /** Get a string representation of this vector. + * @return a string representation of this vector + */ + @Override + public String toString() { + return Vector3DFormat.getInstance().format(this); + } + + /** {@inheritDoc} */ + public String toString(final NumberFormat format) { + return new Vector3DFormat(format).format(this); + } } diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3DFormat.java b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3DFormat.java index 685aeb6e2..2bf1f4826 100644 --- a/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3DFormat.java +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Vector3DFormat.java @@ -22,8 +22,10 @@ import java.text.NumberFormat; import java.text.ParsePosition; import java.util.Locale; -import org.apache.commons.math.util.CompositeFormat; import org.apache.commons.math.exception.MathParseException; +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.VectorFormat; +import org.apache.commons.math.util.CompositeFormat; /** * Formats a 3D vector in components list format "{x; y; z}". @@ -36,31 +38,9 @@ import org.apache.commons.math.exception.MathParseException; * returned. In the second case, however, the parse position after parsing will be * just after the closing curly brace, i.e. just before the trailing space.

* - * @version $Revision$ $Date$ + * @version $Id:$ */ -public class Vector3DFormat { - /** Serializable version identifier */ - private static final long serialVersionUID = -5447606608652576301L; - /** The default prefix: "{". */ - private static final String DEFAULT_PREFIX = "{"; - /** The default suffix: "}". */ - private static final String DEFAULT_SUFFIX = "}"; - /** The default separator: ", ". */ - private static final String DEFAULT_SEPARATOR = "; "; - /** Prefix. */ - private final String prefix; - /** Suffix. */ - private final String suffix; - /** Separator. */ - private final String separator; - /** Trimmed prefix. */ - private final String trimmedPrefix; - /** Trimmed suffix. */ - private final String trimmedSuffix; - /** Trimmed separator. */ - private final String trimmedSeparator; - /** The format used for components. */ - private final NumberFormat format; +public class Vector3DFormat extends VectorFormat { /** * Create an instance with default settings. @@ -68,8 +48,8 @@ public class Vector3DFormat { * "{", "}", and "; " and the default number format for components.

*/ public Vector3DFormat() { - this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, - CompositeFormat.getDefaultNumberFormat()); + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, + CompositeFormat.getDefaultNumberFormat()); } /** @@ -77,7 +57,7 @@ public class Vector3DFormat { * @param format the custom format for components. */ public Vector3DFormat(final NumberFormat format) { - this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); } /** @@ -87,8 +67,8 @@ public class Vector3DFormat { * @param separator separator to use instead of the default "; " */ public Vector3DFormat(final String prefix, final String suffix, - final String separator) { - this(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat()); + final String separator) { + super(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat()); } /** @@ -100,55 +80,8 @@ public class Vector3DFormat { * @param format the custom format for components. */ public Vector3DFormat(final String prefix, final String suffix, - final String separator, final NumberFormat format) { - this.prefix = prefix; - this.suffix = suffix; - this.separator = separator; - trimmedPrefix = prefix.trim(); - trimmedSuffix = suffix.trim(); - trimmedSeparator = separator.trim(); - this.format = format; - } - - /** - * Get the set of locales for which 3D vectors formats are available. - *

This is the same set as the {@link NumberFormat} set.

- * @return available 3D vector format locales. - */ - public static Locale[] getAvailableLocales() { - return NumberFormat.getAvailableLocales(); - } - - /** - * Get the format prefix. - * @return format prefix. - */ - public String getPrefix() { - return prefix; - } - - /** - * Get the format suffix. - * @return format suffix. - */ - public String getSuffix() { - return suffix; - } - - /** - * Get the format separator between components. - * @return format separator. - */ - public String getSeparator() { - return separator; - } - - /** - * Get the components format. - * @return components format. - */ - public NumberFormat getFormat() { - return format; + final String separator, final NumberFormat format) { + super(prefix, suffix, separator, format); } /** @@ -168,16 +101,6 @@ public class Vector3DFormat { return new Vector3DFormat(CompositeFormat.getDefaultNumberFormat(locale)); } - /** - * This method calls {@link #format(Vector3D,StringBuffer,FieldPosition)}. - * - * @param v Vector3D object to format. - * @return a formatted vector. - */ - public String format(Vector3D v) { - return format(v, new StringBuffer(), new FieldPosition(0)).toString(); - } - /** * Formats a {@link Vector3D} object to produce a string. * @param vector the object to format. @@ -186,26 +109,10 @@ public class Vector3DFormat { * offsets of the alignment field * @return the value passed in as toAppendTo. */ - public StringBuffer format(Vector3D vector, StringBuffer toAppendTo, - FieldPosition pos) { - - pos.setBeginIndex(0); - pos.setEndIndex(0); - - // format prefix - toAppendTo.append(prefix); - - // format components - CompositeFormat.formatDouble(vector.getX(), format, toAppendTo, pos); - toAppendTo.append(separator); - CompositeFormat.formatDouble(vector.getY(), format, toAppendTo, pos); - toAppendTo.append(separator); - CompositeFormat.formatDouble(vector.getZ(), format, toAppendTo, pos); - - // format suffix - toAppendTo.append(suffix); - - return toAppendTo; + public StringBuffer format(final Vector vector, final StringBuffer toAppendTo, + final FieldPosition pos) { + final Vector3D v3 = (Vector3D) vector; + return format(toAppendTo, pos, v3.getX(), v3.getY(), v3.getZ()); } /** @@ -215,7 +122,7 @@ public class Vector3DFormat { * @throws MathParseException if the beginning of the specified string * cannot be parsed. */ - public Vector3D parse(String source) { + public Vector3D parse(final String source) { ParsePosition parsePosition = new ParsePosition(0); Vector3D result = parse(source, parsePosition); if (parsePosition.getIndex() == 0) { @@ -232,59 +139,12 @@ public class Vector3DFormat { * @param pos input/ouput parsing parameter. * @return the parsed {@link Vector3D} object. */ - public Vector3D parse(String source, ParsePosition pos) { - int initialIndex = pos.getIndex(); - - // parse prefix - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) { + public Vector3D parse(final String source, final ParsePosition pos) { + final double[] coordinates = parseCoordinates(3, source, pos); + if (coordinates == null) { return null; } - - // parse X component - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - Number x = CompositeFormat.parseNumber(source, format, pos); - if (x == null) { - // invalid abscissa - // set index back to initial, error index should already be set - pos.setIndex(initialIndex); - return null; - } - - // parse Y component - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) { - return null; - } - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - Number y = CompositeFormat.parseNumber(source, format, pos); - if (y == null) { - // invalid ordinate - // set index back to initial, error index should already be set - pos.setIndex(initialIndex); - return null; - } - - // parse Z component - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) { - return null; - } - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - Number z = CompositeFormat.parseNumber(source, format, pos); - if (z == null) { - // invalid height - // set index back to initial, error index should already be set - pos.setIndex(initialIndex); - return null; - } - - // parse suffix - CompositeFormat.parseAndIgnoreWhitespace(source, pos); - if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) { - return null; - } - - return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue()); + return new Vector3D(coordinates[0], coordinates[1], coordinates[2]); } + } diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Euclidean2D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Euclidean2D.java new file mode 100644 index 000000000..d19dfe808 --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Euclidean2D.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.twod; + +import java.io.Serializable; + +import org.apache.commons.math.geometry.Space; +import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D; + +/** + * This class implements a three-dimensional space. + * @version $Id:$ + * @since 3.0 + */ +public class Euclidean2D implements Serializable, Space { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 4793432849757649566L; + + /** Private constructor for the singleton. + */ + private Euclidean2D() { + } + + /** Get the unique instance. + * @return the unique instance + */ + public static Euclidean2D getInstance() { + return LazyHolder.INSTANCE; + } + + /** {@inheritDoc} */ + public int getDimension() { + return 2; + } + + /** {@inheritDoc} */ + public Euclidean1D getSubSpace() { + return Euclidean1D.getInstance(); + } + + // CHECKSTYLE: stop HideUtilityClassConstructor + /** Holder for the instance. + *

We use here the Initialization On Demand Holder Idiom.

+ */ + private static class LazyHolder { + /** Cached field instance. */ + private static final Euclidean2D INSTANCE = new Euclidean2D(); + } + // CHECKSTYLE: resume HideUtilityClassConstructor + + /** Handle deserialization of the singleton. + * @return the singleton instance + */ + private Object readResolve() { + // return the singleton instance + return LazyHolder.INSTANCE; + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Point2D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Point2D.java deleted file mode 100644 index bf94cedcf..000000000 --- a/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Point2D.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.twod; - -import org.apache.commons.math.geometry.partitioning.Point; -import org.apache.commons.math.geometry.partitioning.SubSpace; - -/** This class represents a 2D point. - *

Instances of this class are guaranteed to be immutable.

- * @version $Revision$ $Date$ - */ -public class Point2D extends java.awt.geom.Point2D.Double implements Point, SubSpace { - - /** Point at undefined (NaN) coordinates. */ - public static final Point2D UNDEFINED = new Point2D(java.lang.Double.NaN, java.lang.Double.NaN); - - /** Serializable UID. */ - private static final long serialVersionUID = 8883702098988517151L; - - /** Build a point with default coordinates. - */ - public Point2D() { - } - - /** Build a point from its coordinates. - * @param x abscissa - * @param y ordinate - */ - public Point2D(final double x, final double y) { - super(x, y); - } - - /** Build a point from a java awt point. - * @param point java awt point - */ - public Point2D(final java.awt.geom.Point2D.Double point) { - super(point.x, point.y); - } - - /** Transform a 2D space point into a sub-space point. - * @param point 2D point of the space - * @return always return null - * @see #toSpace - */ - public Point toSubSpace(final Point point) { - return null; - } - - /** Transform a sub-space point into a space point. - * @param point ignored parameter - * @return always return the instance - * @see #toSubSpace - */ - public Point toSpace(final Point point) { - return this; - } - -} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2D.java b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2D.java new file mode 100644 index 000000000..d6cbf9d42 --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2D.java @@ -0,0 +1,346 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.twod; + +import java.text.NumberFormat; + +import org.apache.commons.math.geometry.Space; +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.util.FastMath; +import org.apache.commons.math.util.MathUtils; + +/** This class represents a 2D vector. + *

Instances of this class are guaranteed to be immutable.

+ * @version $Id:$ + * @since 3.0 + */ +public class Vector2D implements Vector { + + /** Origin (coordinates: 0, 0). */ + public static final Vector2D ZERO = new Vector2D(0, 0); + + // CHECKSTYLE: stop ConstantName + /** A vector with all coordinates set to NaN. */ + public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN); + // CHECKSTYLE: resume ConstantName + + /** A vector with all coordinates set to positive infinity. */ + public static final Vector2D POSITIVE_INFINITY = + new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + + /** A vector with all coordinates set to negative infinity. */ + public static final Vector2D NEGATIVE_INFINITY = + new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + + /** Serializable UID. */ + private static final long serialVersionUID = 266938651998679754L; + + /** Abscissa. */ + private final double x; + + /** Ordinate. */ + private final double y; + + /** Simple constructor. + * Build a vector from its coordinates + * @param x abscissa + * @param y ordinate + * @see #getX() + * @see #getY() + */ + public Vector2D(double x, double y) { + this.x = x; + this.y = y; + } + + /** Multiplicative constructor + * Build a vector from another one and a scale factor. + * The vector built will be a * u + * @param a scale factor + * @param u base (unscaled) vector + */ + public Vector2D(double a, Vector2D u) { + this.x = a * u.x; + this.y = a * u.y; + } + + /** Linear constructor + * Build a vector from two other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + */ + public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) { + this.x = a1 * u1.x + a2 * u2.x; + this.y = a1 * u1.y + a2 * u2.y; + } + + /** Linear constructor + * Build a vector from three other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + */ + public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2, + double a3, Vector2D u3) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x; + this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y; + } + + /** Linear constructor + * Build a vector from four other ones and corresponding scale factors. + * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 + * @param a1 first scale factor + * @param u1 first base (unscaled) vector + * @param a2 second scale factor + * @param u2 second base (unscaled) vector + * @param a3 third scale factor + * @param u3 third base (unscaled) vector + * @param a4 fourth scale factor + * @param u4 fourth base (unscaled) vector + */ + public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2, + double a3, Vector2D u3, double a4, Vector2D u4) { + this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x; + this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y; + } + + /** Get the abscissa of the vector. + * @return abscissa of the vector + * @see #Vector2D(double, double) + */ + public double getX() { + return x; + } + + /** Get the ordinate of the vector. + * @return ordinate of the vector + * @see #Vector2D(double, double) + */ + public double getY() { + return y; + } + + /** {@inheritDoc} */ + public Space getSpace() { + return Euclidean2D.getInstance(); + } + + /** {@inheritDoc} */ + public Vector2D getZero() { + return ZERO; + } + + /** {@inheritDoc} */ + public Vector2D toVector() { + return new Vector2D(x, y); + } + + /** {@inheritDoc} */ + public double getNorm1() { + return FastMath.abs(x) + FastMath.abs(y); + } + + /** {@inheritDoc} */ + public double getNorm() { + return FastMath.sqrt (x * x + y * y); + } + + /** {@inheritDoc} */ + public double getNormSq() { + return x * x + y * y; + } + + /** {@inheritDoc} */ + public double getNormInf() { + return FastMath.max(FastMath.abs(x), FastMath.abs(y)); + } + + /** {@inheritDoc} */ + public Vector2D add(Vector v) { + Vector2D v2 = (Vector2D) v; + return new Vector2D(x + v2.getX(), y + v2.getY()); + } + + /** {@inheritDoc} */ + public Vector2D add(double factor, Vector v) { + Vector2D v2 = (Vector2D) v; + return new Vector2D(x + factor * v2.getX(), y + factor * v2.getY()); + } + + /** {@inheritDoc} */ + public Vector2D subtract(Vector p) { + Vector2D p3 = (Vector2D) p; + return new Vector2D(x - p3.x, y - p3.y); + } + + /** {@inheritDoc} */ + public Vector2D subtract(double factor, Vector v) { + Vector2D v2 = (Vector2D) v; + return new Vector2D(x - factor * v2.getX(), y - factor * v2.getY()); + } + + /** {@inheritDoc} */ + public boolean isNaN() { + return Double.isNaN(x) || Double.isNaN(y); + } + + /** {@inheritDoc} */ + public boolean isInfinite() { + return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y)); + } + + /** {@inheritDoc} */ + public double distance1(Vector p) { + Vector2D p3 = (Vector2D) p; + final double dx = FastMath.abs(p3.x - x); + final double dy = FastMath.abs(p3.y - y); + return dx + dy; + } + + /** {@inheritDoc} */ + public double distance(Vector p) { + Vector2D p3 = (Vector2D) p; + final double dx = p3.x - x; + final double dy = p3.y - y; + return FastMath.sqrt(dx * dx + dy * dy); + } + + /** {@inheritDoc} */ + public double distanceInf(Vector p) { + Vector2D p3 = (Vector2D) p; + final double dx = FastMath.abs(p3.x - x); + final double dy = FastMath.abs(p3.y - y); + return FastMath.max(dx, dy); + } + + /** {@inheritDoc} */ + public double distanceSq(Vector p) { + Vector2D p3 = (Vector2D) p; + final double dx = p3.x - x; + final double dy = p3.y - y; + return dx * dx + dy * dy; + } + + /** Compute the distance between two vectors according to the L2 norm. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNorm() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the distance between p1 and p2 according to the L2 norm + */ + public static double distance(Vector2D p1, Vector2D p2) { + return p1.distance(p2); + } + + /** Compute the distance between two vectors according to the L norm. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNormInf() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the distance between p1 and p2 according to the L norm + */ + public static double distanceInf(Vector2D p1, Vector2D p2) { + return p1.distanceInf(p2); + } + + /** Compute the square of the distance between two vectors. + *

Calling this method is equivalent to calling: + * p1.subtract(p2).getNormSq() except that no intermediate + * vector is built

+ * @param p1 first vector + * @param p2 second vector + * @return the square of the distance between p1 and p2 + */ + public static double distanceSq(Vector2D p1, Vector2D p2) { + return p1.distanceSq(p2); + } + + /** + * Test for the equality of two 2D vectors. + *

+ * If all coordinates of two 2D vectors are exactly the same, and none are + * Double.NaN, the two 2D vectors are considered to be equal. + *

+ *

+ * NaN coordinates are considered to affect globally the vector + * and be equals to each other - i.e, if either (or all) coordinates of the + * 2D vector are equal to Double.NaN, the 2D vector is equal to + * {@link #NaN}. + *

+ * + * @param other Object to test for equality to this + * @return true if two 2D vector objects are equal, false if + * object is null, not an instance of Vector2D, or + * not equal to this Vector2D instance + * + */ + @Override + public boolean equals(Object other) { + + if (this == other) { + return true; + } + + if (other instanceof Vector2D) { + final Vector2D rhs = (Vector2D)other; + if (rhs.isNaN()) { + return this.isNaN(); + } + + return (x == rhs.x) && (y == rhs.y); + } + return false; + } + + /** + * Get a hashCode for the 2D vector. + *

+ * All NaN values have the same hash code.

+ * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + if (isNaN()) { + return 542; + } + return 122 * (76 * MathUtils.hash(x) + MathUtils.hash(y)); + } + + /** Get a string representation of this vector. + * @return a string representation of this vector + */ + @Override + public String toString() { + return Vector2DFormat.getInstance().format(this); + } + + /** {@inheritDoc} */ + public String toString(final NumberFormat format) { + return new Vector2DFormat(format).format(this); + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2DFormat.java b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2DFormat.java new file mode 100644 index 000000000..ccc558cbe --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/euclidean/twod/Vector2DFormat.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.euclidean.twod; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.commons.math.exception.MathParseException; +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.VectorFormat; +import org.apache.commons.math.util.CompositeFormat; + +/** + * Formats a 2D vector in components list format "{x; y}". + *

The prefix and suffix "{" and "}" and the separator "; " can be replaced by + * any user-defined strings. The number format for components can be configured.

+ *

White space is ignored at parse time, even if it is in the prefix, suffix + * or separator specifications. So even if the default separator does include a space + * character that is used at format time, both input string "{1;1}" and + * " { 1 ; 1 } " will be parsed without error and the same vector will be + * returned. In the second case, however, the parse position after parsing will be + * just after the closing curly brace, i.e. just before the trailing space.

+ * + * @version $Id:$ + * @since 3.0 + */ +public class Vector2DFormat extends VectorFormat { + + /** + * Create an instance with default settings. + *

The instance uses the default prefix, suffix and separator: + * "{", "}", and "; " and the default number format for components.

+ */ + public Vector2DFormat() { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, + CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with a custom number format for components. + * @param format the custom format for components. + */ + public Vector2DFormat(final NumberFormat format) { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + } + + /** + * Create an instance with custom prefix, suffix and separator. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + */ + public Vector2DFormat(final String prefix, final String suffix, + final String separator) { + super(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat()); + } + + /** + * Create an instance with custom prefix, suffix, separator and format + * for components. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + * @param format the custom format for components. + */ + public Vector2DFormat(final String prefix, final String suffix, + final String separator, final NumberFormat format) { + super(prefix, suffix, separator, format); + } + + /** + * Returns the default 2D vector format for the current locale. + * @return the default 2D vector format. + */ + public static Vector2DFormat getInstance() { + return getInstance(Locale.getDefault()); + } + + /** + * Returns the default 2D vector format for the given locale. + * @param locale the specific locale used by the format. + * @return the 2D vector format specific to the given locale. + */ + public static Vector2DFormat getInstance(final Locale locale) { + return new Vector2DFormat(CompositeFormat.getDefaultNumberFormat(locale)); + } + + /** {@inheritDoc} */ + public StringBuffer format(final Vector vector, final StringBuffer toAppendTo, + final FieldPosition pos) { + final Vector2D p2 = (Vector2D) vector; + return format(toAppendTo, pos, p2.getX(), p2.getY()); + } + + /** {@inheritDoc} */ + public Vector2D parse(final String source) { + ParsePosition parsePosition = new ParsePosition(0); + Vector2D result = parse(source, parsePosition); + if (parsePosition.getIndex() == 0) { + throw new MathParseException(source, + parsePosition.getErrorIndex(), + Vector2D.class); + } + return result; + } + + /** {@inheritDoc} */ + public Vector2D parse(final String source, final ParsePosition pos) { + final double[] coordinates = parseCoordinates(2, source, pos); + if (coordinates == null) { + return null; + } + return new Vector2D(coordinates[0], coordinates[1]); + } + +} diff --git a/src/main/java/org/apache/commons/math/geometry/package.html b/src/main/java/org/apache/commons/math/geometry/package.html new file mode 100644 index 000000000..b15c1ed96 --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/package.html @@ -0,0 +1,25 @@ + + + + +

+This package is the top level package for geometry. It provides only a few interfaces +related to vectorial/affine spaces that are implemented in sub-packages. +

+ + diff --git a/src/main/java/org/apache/commons/math/geometry/partitioning/Embedding.java b/src/main/java/org/apache/commons/math/geometry/partitioning/Embedding.java new file mode 100644 index 000000000..d0ec9239a --- /dev/null +++ b/src/main/java/org/apache/commons/math/geometry/partitioning/Embedding.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.math.geometry.partitioning; + +import org.apache.commons.math.geometry.Vector; +import org.apache.commons.math.geometry.Space; + +/** This interface defines mappers between a space and one of its sub-spaces. + + *

Sub-spaces are the lower dimensions subsets of a n-dimensions + * space. The (n-1)-dimension sub-spaces are specific sub-spaces known + * as {@link Hyperplane hyperplanes}. This interface can be used regardless + * of the dimensions differences. As an example, {@link + * org.apache.commons.math.geometry.euclidean.threed.Line Line} in 3D + * implements Embedding<{@link + * org.apache.commons.math.geometry.euclidean.threed.Vector3D Vector3D}, {link + * org.apache.commons.math.geometry.euclidean.oned.Vector1D Vector1D>, i.e. it + * maps directly dimensions 3 and 1.

+ + *

In the 3D euclidean space, hyperplanes are 2D planes, and the 1D + * sub-spaces are lines.

+ + * @param Type of the embedding space. + * @param Type of the embedded sub-space. + + * @see Hyperplane + * @version $Id:$ + * @since 3.0 + */ +public interface Embedding { + + /** Transform a space point into a sub-space point. + * @param point n-dimension point of the space + * @return (n-1)-dimension point of the sub-space corresponding to + * the specified space point + * @see #toSpace + */ + Vector toSubSpace(Vector point); + + /** Transform a sub-space point into a space point. + * @param point (n-1)-dimension point of the sub-space + * @return n-dimension point of the space corresponding to the + * specified sub-space point + * @see #toSubSpace + */ + Vector toSpace(Vector point); + +} diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index a5c5f28e4..1d52bd93b 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -52,6 +52,9 @@ The type attribute can be add,update,fix,remove. If the output is not quite correct, check for invisible trailing spaces! --> + + Added a consistent classes hierarchy for Euclidean spaces in dimension 1, 2 and 3. + Improved javadoc for FastMath explaining the overhead at class loading and the targeted use cases.