001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.euclidean.oned; 018 019import java.util.Comparator; 020import java.util.function.UnaryOperator; 021 022import org.apache.commons.geometry.core.internal.SimpleTupleFormat; 023import org.apache.commons.geometry.euclidean.EuclideanVector; 024import org.apache.commons.geometry.euclidean.EuclideanVectorSum; 025import org.apache.commons.geometry.euclidean.internal.Vectors; 026import org.apache.commons.numbers.core.Precision; 027 028/** This class represents vectors and points in one-dimensional Euclidean space. 029 * Instances of this class are guaranteed to be immutable. 030 */ 031public class Vector1D extends EuclideanVector<Vector1D> { 032 033 /** Zero vector (coordinates: 0). */ 034 public static final Vector1D ZERO = new Vector1D(0.0); 035 036 /** A vector with all coordinates set to NaN. */ 037 public static final Vector1D NaN = new Vector1D(Double.NaN); 038 039 /** A vector with all coordinates set to positive infinity. */ 040 public static final Vector1D POSITIVE_INFINITY = 041 new Vector1D(Double.POSITIVE_INFINITY); 042 043 /** A vector with all coordinates set to negative infinity. */ 044 public static final Vector1D NEGATIVE_INFINITY = 045 new Vector1D(Double.NEGATIVE_INFINITY); 046 047 /** Comparator that sorts vectors in component-wise ascending order. 048 * Vectors are only considered equal if their coordinates match exactly. 049 * Null arguments are evaluated as being greater than non-null arguments. 050 */ 051 public static final Comparator<Vector1D> COORDINATE_ASCENDING_ORDER = (a, b) -> { 052 int cmp = 0; 053 054 if (a != null && b != null) { 055 cmp = Double.compare(a.getX(), b.getX()); 056 } else if (a != null) { 057 cmp = -1; 058 } else if (b != null) { 059 cmp = 1; 060 } 061 062 return cmp; 063 }; 064 065 /** Abscissa (coordinate value). */ 066 private final double x; 067 068 /** Simple constructor. 069 * @param x abscissa (coordinate value) 070 */ 071 private Vector1D(final double x) { 072 this.x = x; 073 } 074 075 /** 076 * Returns the abscissa (coordinate value) of the instance. 077 * @return the abscissa value 078 */ 079 public double getX() { 080 return x; 081 } 082 083 /** {@inheritDoc} */ 084 @Override 085 public int getDimension() { 086 return 1; 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 public boolean isNaN() { 092 return Double.isNaN(x); 093 } 094 095 /** {@inheritDoc} */ 096 @Override 097 public boolean isInfinite() { 098 return !isNaN() && Double.isInfinite(x); 099 } 100 101 /** {@inheritDoc} */ 102 @Override 103 public boolean isFinite() { 104 return Double.isFinite(x); 105 } 106 107 /** {@inheritDoc} */ 108 @Override 109 public Vector1D vectorTo(final Vector1D v) { 110 return v.subtract(this); 111 } 112 113 /** {@inheritDoc} */ 114 @Override 115 public Unit directionTo(final Vector1D v) { 116 return vectorTo(v).normalize(); 117 } 118 119 /** {@inheritDoc} */ 120 @Override 121 public Vector1D lerp(final Vector1D p, final double t) { 122 return Sum.create() 123 .addScaled(1.0 - t, this) 124 .addScaled(t, p).get(); 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public Vector1D getZero() { 130 return ZERO; 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public double norm() { 136 return Vectors.norm(x); 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public double normSq() { 142 return Vectors.normSq(x); 143 } 144 145 /** {@inheritDoc} */ 146 @Override 147 public Vector1D withNorm(final double magnitude) { 148 getCheckedNorm(); // validate our norm value 149 return (x > 0.0) ? new Vector1D(magnitude) : new Vector1D(-magnitude); 150 } 151 152 /** {@inheritDoc} */ 153 @Override 154 public Vector1D add(final Vector1D v) { 155 return new Vector1D(x + v.x); 156 } 157 158 /** {@inheritDoc} */ 159 @Override 160 public Vector1D add(final double factor, final Vector1D v) { 161 return new Vector1D(x + (factor * v.x)); 162 } 163 164 /** {@inheritDoc} */ 165 @Override 166 public Vector1D subtract(final Vector1D v) { 167 return new Vector1D(x - v.x); 168 } 169 170 /** {@inheritDoc} */ 171 @Override 172 public Vector1D subtract(final double factor, final Vector1D v) { 173 return new Vector1D(x - (factor * v.x)); 174 } 175 176 /** {@inheritDoc} */ 177 @Override 178 public Vector1D negate() { 179 return new Vector1D(-x); 180 } 181 182 /** {@inheritDoc} */ 183 @Override 184 public Unit normalize() { 185 return Unit.from(x); 186 } 187 188 /** {@inheritDoc} */ 189 @Override 190 public Unit normalizeOrNull() { 191 return Unit.tryCreateNormalized(x, false); 192 } 193 194 /** {@inheritDoc} */ 195 @Override 196 public Vector1D multiply(final double a) { 197 return new Vector1D(a * x); 198 } 199 200 /** {@inheritDoc} */ 201 @Override 202 public double distance(final Vector1D v) { 203 return Vectors.norm(x - v.x); 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public double distanceSq(final Vector1D v) { 209 return Vectors.normSq(x - v.x); 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public double dot(final Vector1D v) { 215 return x * v.x; 216 } 217 218 /** {@inheritDoc} 219 * <p>For the one-dimensional case, this method returns 0 if the vector x values have 220 * the same sign and {@code pi} if they are opposite.</p> 221 */ 222 @Override 223 public double angle(final Vector1D v) { 224 // validate the norm values 225 getCheckedNorm(); 226 v.getCheckedNorm(); 227 228 final double sig1 = Math.signum(x); 229 final double sig2 = Math.signum(v.x); 230 231 // the angle is 0 if the x value signs are the same and pi if not 232 return (sig1 == sig2) ? 0.0 : Math.PI; 233 } 234 235 /** Convenience method to apply a function to this vector. This 236 * can be used to transform the vector inline with other methods. 237 * @param fn the function to apply 238 * @return the transformed vector 239 */ 240 public Vector1D transform(final UnaryOperator<Vector1D> fn) { 241 return fn.apply(this); 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 public boolean eq(final Vector1D vec, final Precision.DoubleEquivalence precision) { 247 return precision.eq(x, vec.x); 248 } 249 250 /** 251 * Get a hashCode for the vector. 252 * <p>All NaN values have the same hash code.</p> 253 * 254 * @return a hash code value for this object 255 */ 256 @Override 257 public int hashCode() { 258 if (isNaN()) { 259 return 857; 260 } 261 return 403 * Double.hashCode(x); 262 } 263 264 /** 265 * Test for the equality of two vectors. 266 * <p> 267 * If all coordinates of two vectors are exactly the same, and none are 268 * <code>Double.NaN</code>, the two vectors are considered to be equal. 269 * </p> 270 * <p> 271 * <code>NaN</code> coordinates are considered to globally affect the vector 272 * and be equal to each other - i.e, if either (or all) coordinates of the 273 * vector are equal to <code>Double.NaN</code>, the vector is equal to 274 * {@link #NaN}. 275 * </p> 276 * 277 * @param other Object to test for equality to this 278 * @return true if two vector objects are equal, false if 279 * object is null, not an instance of Vector1D, or 280 * not equal to this Vector1D instance 281 * 282 */ 283 @Override 284 public boolean equals(final Object other) { 285 if (this == other) { 286 return true; 287 } 288 if (other instanceof Vector1D) { 289 final Vector1D rhs = (Vector1D) other; 290 if (rhs.isNaN()) { 291 return this.isNaN(); 292 } 293 294 return Double.compare(x, rhs.x) == 0; 295 } 296 return false; 297 } 298 299 /** {@inheritDoc} */ 300 @Override 301 public String toString() { 302 return SimpleTupleFormat.getDefault().format(x); 303 } 304 305 /** Returns a vector with the given coordinate value. 306 * @param x vector coordinate 307 * @return vector instance 308 */ 309 public static Vector1D of(final double x) { 310 return new Vector1D(x); 311 } 312 313 /** Parses the given string and returns a new vector instance. The expected string 314 * format is the same as that returned by {@link #toString()}. 315 * @param str the string to parse 316 * @return vector instance represented by the string 317 * @throws IllegalArgumentException if the given string has an invalid format 318 */ 319 public static Vector1D parse(final String str) { 320 return SimpleTupleFormat.getDefault().parse(str, Vector1D::new); 321 } 322 323 /** 324 * Represent unit vectors. 325 * This allows optimizations to be performed for certain operations. 326 */ 327 public static final class Unit extends Vector1D { 328 /** Unit vector (coordinates: 1). */ 329 public static final Unit PLUS = new Unit(1d); 330 /** Negation of unit vector (coordinates: -1). */ 331 public static final Unit MINUS = new Unit(-1d); 332 333 /** Simple constructor. Callers are responsible for ensuring that the given 334 * values represent a normalized vector. 335 * @param x abscissa (first coordinate value) 336 */ 337 private Unit(final double x) { 338 super(x); 339 } 340 341 /** {@inheritDoc} */ 342 @Override 343 public double norm() { 344 return 1; 345 } 346 347 /** {@inheritDoc} */ 348 @Override 349 public double normSq() { 350 return 1; 351 } 352 353 /** {@inheritDoc} */ 354 @Override 355 public Unit normalize() { 356 return this; 357 } 358 359 /** {@inheritDoc} */ 360 @Override 361 public Unit normalizeOrNull() { 362 return this; 363 } 364 365 /** {@inheritDoc} */ 366 @Override 367 public Vector1D withNorm(final double mag) { 368 return multiply(mag); 369 } 370 371 /** {@inheritDoc} */ 372 @Override 373 public Vector1D negate() { 374 return this == PLUS ? MINUS : PLUS; 375 } 376 377 /** Create a normalized vector. 378 * @param x Vector coordinate. 379 * @return a vector whose norm is 1. 380 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite 381 */ 382 public static Unit from(final double x) { 383 return tryCreateNormalized(x, true); 384 } 385 386 /** Create a normalized vector. 387 * @param v Vector. 388 * @return a vector whose norm is 1. 389 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite 390 */ 391 public static Unit from(final Vector1D v) { 392 return v instanceof Unit ? 393 (Unit) v : 394 from(v.getX()); 395 } 396 397 /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure} 398 * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null 399 * is returned. 400 * @param x x coordinate 401 * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created 402 * @return normalized vector or null if one cannot be created and {@code throwOnFailure} 403 * is false 404 * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite 405 */ 406 private static Unit tryCreateNormalized(final double x, final boolean throwOnFailure) { 407 final double norm = Vectors.norm(x); 408 409 if (Vectors.isRealNonZero(norm)) { 410 return x > 0 ? PLUS : MINUS; 411 } else if (throwOnFailure) { 412 throw Vectors.illegalNorm(norm); 413 } 414 return null; 415 } 416 } 417 418 /** Class used to create high-accuracy sums of vectors. Each vector component is 419 * summed using an instance of {@link org.apache.commons.numbers.core.Sum}. 420 * 421 * <p>This class is mutable and not thread-safe. 422 * @see org.apache.commons.numbers.core.Sum 423 */ 424 public static final class Sum extends EuclideanVectorSum<Vector1D> { 425 426 /** X component sum. */ 427 private final org.apache.commons.numbers.core.Sum xsum; 428 429 /** Construct a new instance with the given initial value. 430 * @param initial initial value 431 */ 432 Sum(final Vector1D initial) { 433 this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x); 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public Sum add(final Vector1D vec) { 439 xsum.add(vec.x); 440 return this; 441 } 442 443 /** {@inheritDoc} */ 444 @Override 445 public Sum addScaled(final double scale, final Vector1D vec) { 446 xsum.addProduct(scale, vec.x); 447 return this; 448 } 449 450 /** {@inheritDoc} */ 451 @Override 452 public Vector1D get() { 453 return Vector1D.of(xsum.getAsDouble()); 454 } 455 456 /** Create a new instance with an initial value set to the {@link Vector1D#ZERO zero vector}. 457 * @return new instance set to zero 458 */ 459 public static Sum create() { 460 return new Sum(Vector1D.ZERO); 461 } 462 463 /** Construct a new instance with an initial value set to the argument. 464 * @param initial initial sum value 465 * @return new instance 466 */ 467 public static Sum of(final Vector1D initial) { 468 return new Sum(initial); 469 } 470 471 /** Construct a new instance from multiple values. 472 * @param first first vector 473 * @param more additional vectors 474 * @return new instance 475 */ 476 public static Sum of(final Vector1D first, final Vector1D... more) { 477 final Sum s = new Sum(first); 478 for (final Vector1D v : more) { 479 s.add(v); 480 } 481 return s; 482 } 483 } 484}