Ejemplo n.º 1
0
 /**
  * Interpolates between this vector and the destination. Offsets that are not between zero and one
  * are handled specially:
  *
  * <ul>
  *   <li>If {@code offset <= 0}, a copy of {@code src} is returned
  *   <li>If {@code offset >= 1}, a copy of {@code dest} is returned
  * </ul>
  *
  * This special behavior allows clients to reliably detect when interpolation is complete.
  *
  * @param src the starting vector
  * @param dest the ending vector
  * @param offset the percentage of distance between the specified points
  * @return a mutable vector that lies between src and dest
  */
 public static Vector3f interpolated(Vector3f src, Vector3f dest, final float offset) {
   if (src == null) {
     throw new NullPointerException("src must not be null");
   }
   if (dest == null) {
     throw new NullPointerException("dest must not be null");
   }
   if (offset <= 0f) {
     return src.toMutable();
   }
   if (offset >= 1f) {
     return dest.toMutable();
   }
   return mutable(
       src.x + (dest.x - src.x) * offset,
       src.y + (dest.y - src.y) * offset,
       src.z + (dest.z - src.z) * offset);
 }
Ejemplo n.º 2
0
/**
 * A {@link Vector3} in {@code float} precision. Be aware that while this class implements {@link
 * #equals(Object)} appropriately, it may yield unexpected results due to the inherent imprecision
 * of floating-point values.
 *
 * @author Aaron Faanes
 * @see Vector3d
 * @see Vector3i
 */
public final class Vector3f extends AbstractVector3<Vector3f> {

  public static Vector3f mutable() {
    return mutable(0);
  }

  public static Vector3f frozen() {
    return frozen(0);
  }

  /**
   * Create a mutable {@link Vector3f} using the specified value for all axes.
   *
   * @param v the value used for all axes
   * @return a mutable {@code Vector3f}
   * @throw {@link IllegalArgumentException} if {@code v} is {@code NaN}
   */
  public static Vector3f mutable(float v) {
    return Vector3f.mutable(v, v, v);
  }

  /**
   * Create a frozen {@link Vector3f} using the specified value for all axes.
   *
   * @param v the value used for all axes
   * @return a frozen {@code Vector3f}
   * @throw {@link IllegalArgumentException} if {@code v} is {@code NaN}
   */
  public static Vector3f frozen(float v) {
    return Vector3f.mutable(v, v, v);
  }

  public static Vector3f mutable(final float x, final float y, final float z) {
    return new Vector3f(true, x, y, z);
  }

  public static Vector3f mutable(final float x, final float y) {
    return mutable(x, y, 0);
  }

  public static Vector3f frozen(final float x, final float y, final float z) {
    return new Vector3f(false, x, y, z);
  }

  public static Vector3f frozen(final float x, final float y) {
    return frozen(x, y, 0);
  }

  public static Vector3f mutable(Vector3i vector) {
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    return new Vector3f(true, vector.x(), vector.y(), vector.z());
  }

  public static Vector3f frozen(Vector3i vector) {
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    return new Vector3f(false, vector.x(), vector.y(), vector.z());
  }

  public static Vector3f mutable(Vector3d vector) {
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    return new Vector3f(true, (float) vector.x(), (float) vector.y(), (float) vector.z());
  }

  public static Vector3f frozen(Vector3d vector) {
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    return new Vector3f(false, (float) vector.x(), (float) vector.y(), (float) vector.z());
  }

  public static Vector3f mutable(Point point) {
    return Vector3f.mutable(point.x, point.y, 0);
  }

  public static Vector3f frozen(Point point) {
    return Vector3f.frozen(point.x, point.y, 0);
  }

  public static Vector3f mutable(Dimension dimension) {
    return Vector3f.mutable(dimension.width, dimension.height, 0);
  }

  public static Vector3f frozen(Dimension dimension) {
    return Vector3f.frozen(dimension.width, dimension.height, 0);
  }

  public static Vector3d mutable(Point2D.Float point) {
    return Vector3d.mutable(point.x, point.y, 0);
  }

  public static Vector3d frozen(Point2D.Float point) {
    return Vector3d.frozen(point.x, point.y, 0);
  }

  /**
   * Interpolates between this vector and the destination. Offsets that are not between zero and one
   * are handled specially:
   *
   * <ul>
   *   <li>If {@code offset <= 0}, a copy of {@code src} is returned
   *   <li>If {@code offset >= 1}, a copy of {@code dest} is returned
   * </ul>
   *
   * This special behavior allows clients to reliably detect when interpolation is complete.
   *
   * @param src the starting vector
   * @param dest the ending vector
   * @param offset the percentage of distance between the specified points
   * @return a mutable vector that lies between src and dest
   */
  public static Vector3f interpolated(Vector3f src, Vector3f dest, final float offset) {
    if (src == null) {
      throw new NullPointerException("src must not be null");
    }
    if (dest == null) {
      throw new NullPointerException("dest must not be null");
    }
    if (offset <= 0f) {
      return src.toMutable();
    }
    if (offset >= 1f) {
      return dest.toMutable();
    }
    return mutable(
        src.x + (dest.x - src.x) * offset,
        src.y + (dest.y - src.y) * offset,
        src.z + (dest.z - src.z) * offset);
  }

  private static final Vector3f ORIGIN = Vector3f.frozen(0);

  /**
   * Returns a frozen vector at the origin.
   *
   * @return a frozen vector at the origin.
   */
  public static Vector3f origin() {
    return ORIGIN;
  }

  /**
   * Return a frozen vector with values of 1 at the specified axes. This is normally used to create
   * unit vectors, but {@code axis} values of multiple axes are allowed.
   *
   * @param axis the axes with values of 1
   * @return a frozen unit vector
   */
  public static Vector3f unit(Axis axis) {
    return origin().copy().set(axis, 1).toFrozen();
  }

  private static final Vector3f UP = Vector3f.frozen(0, 1, 0);

  /**
   * Returns a frozen vector that points up the y axis.
   *
   * @return a frozen vector with components {@code (0, 1, 0)}
   */
  public static Vector3f up() {
    return UP;
  }

  private static final Vector3f FORWARD = Vector3f.frozen(0, 0, -1);

  /**
   * Returns a frozen vector that points down the z axis.
   *
   * @return a frozen vector with components {@code (0, 0, -1)}
   */
  public static Vector3f forward() {
    return FORWARD;
  }

  private static final Vector3f LEFT = Vector3f.frozen(-1, 0, 0);

  /**
   * Returns a frozen vector that points down the negative x axis.
   *
   * @return a frozen vector with components {@code (-1, 0, 0)}
   */
  public static final Vector3f left() {
    return LEFT;
  }

  private static final Vector3f RIGHT = Vector3f.frozen(1, 0, 0);

  /**
   * Returns a frozen vector that points down the positive x axis.
   *
   * @return a frozen vector with components {@code (1, 0, 0)}
   */
  public static Vector3f right() {
    return RIGHT;
  }

  private static final Vector3f DOWN = UP.toMutable().negate().toFrozen();

  /**
   * Returns a frozen vector that points down the negative Y axis.
   *
   * @return a frozen vector with components {@code (0, -1, 0)}
   */
  public static final Vector3f down() {
    return DOWN;
  }

  private float z;
  private float y;
  private float x;

  /**
   * Constructs a vector using the specified coordinates. There are no restrictions on the values of
   * these points except that none of them can be {@code NaN}.
   *
   * @param mutable whether this vector can be directly modified
   * @param x the x-coordinate of this vector
   * @param y the y-coordinate of this vector
   * @param z the z-coordinate of this vector
   * @throws IllegalArgumentException if any coordinate is {@code NaN}
   */
  private Vector3f(final boolean mutable, final float x, final float y, final float z) {
    super(mutable);
    if (java.lang.Float.isNaN(x)) {
      throw new IllegalArgumentException("x is NaN");
    }
    if (java.lang.Float.isNaN(y)) {
      throw new IllegalArgumentException("y is NaN");
    }
    if (java.lang.Float.isNaN(z)) {
      throw new IllegalArgumentException("z is NaN");
    }
    this.x = x;
    this.y = y;
    this.z = z;
  }

  /**
   * Returns the x-coordinate of this vector.
   *
   * @return the x-coordinate of this vector
   */
  public float x() {
    return this.x;
  }

  /**
   * Sets the x position to the specified value.
   *
   * @param value the new x value
   * @return the old x value
   */
  public float setX(float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (Float.isNaN(value)) {
      throw new IllegalArgumentException("value must not be NaN");
    }
    float old = this.x;
    this.x = value;
    return old;
  }

  /**
   * Add the specified x value to this vector.
   *
   * @param offset the value to add
   * @return the old x value
   */
  public float addX(float offset) {
    return this.setX(this.x() + offset);
  }

  /**
   * Subtract the specified value from this vector's X axis.
   *
   * @param offset the value to subtract
   * @return the old value at the X axis
   * @see #subtractedX(int)
   * @throw UnsupportedOperationException if this vector is not mutable
   * @throw IllegalArgumentException if {@code offset} is NaN
   */
  public float subtractX(float offset) {
    return this.setX(this.x() - offset);
  }

  /**
   * Multiply the specified x value of this vector.
   *
   * @param factor the factor of multiplication
   * @return the old x value
   */
  public float multiplyX(double factor) {
    return this.setX((float) (this.x() * factor));
  }

  public float divideX(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setX((float) (this.x() / denominator));
  }

  public float moduloX(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setX((float) (this.x() % denominator));
  }

  /**
   * Returns the y-coordinate of this vector.
   *
   * @return the y-coordinate of this vector
   */
  public float y() {
    return this.y;
  }

  /**
   * Sets the y position to the specified value.
   *
   * @param value the new y value
   * @return the old y value
   */
  public float setY(float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (Float.isNaN(value)) {
      throw new IllegalArgumentException("value must not be NaN");
    }
    float old = this.y;
    this.y = value;
    return old;
  }

  /**
   * Add the specified y value to this vector.
   *
   * @param offset the value to add
   * @return the old y value
   */
  public float addY(float offset) {
    return this.setY(this.y() + offset);
  }

  /**
   * Subtract the specified value from this vector's Y axis.
   *
   * @param offset the value to subtract
   * @return the old value at the Y axis
   * @see #subtractedY(int)
   * @throw UnsupportedOperationException if this vector is not mutable
   */
  public float subtractY(float offset) {
    return this.setY(this.y() - offset);
  }

  /**
   * Multiply the specified y value of this vector.
   *
   * @param factor the factor of multiplication
   * @return the old y value
   */
  public float multiplyY(double factor) {
    return this.setY((float) (this.y() * factor));
  }

  public float divideY(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setY((float) (this.y() / denominator));
  }

  public float moduloY(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setY((float) (this.y() % denominator));
  }

  /**
   * Returns the z-coordinate of this vector.
   *
   * @return the z-coordinate of this vector
   */
  public float z() {
    return this.z;
  }

  /**
   * Sets the z position to the specified value.
   *
   * @param value the new z value
   * @return the old z value
   */
  public float setZ(float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (Float.isNaN(value)) {
      throw new IllegalArgumentException("value must not be NaN");
    }
    float old = this.z;
    this.z = value;
    return old;
  }

  /**
   * Add the specified z value to this vector.
   *
   * @param offset the value to add
   * @return the old z value
   */
  public float addZ(float offset) {
    return this.setZ(this.z() + offset);
  }

  /**
   * Subtract the specified value from this vector's Z axis.
   *
   * @param offset the value to subtract
   * @return the old value at the Z axis
   * @see #subtractedZ(int)
   * @throw UnsupportedOperationException if this vector is not mutable
   * @throw IllegalArgumentException if {@code offset} is NaN
   */
  public float subtractZ(float offset) {
    return this.setZ(this.z() - offset);
  }

  /**
   * Multiply the specified z value of this vector.
   *
   * @param factor the factor of multiplication
   * @return the old z value
   */
  public float multiplyZ(double factor) {
    return this.setZ((float) (this.z() * factor));
  }

  public float divideZ(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setZ((float) (this.z() / denominator));
  }

  public float moduloZ(double denominator) {
    if (Double.isNaN(denominator)) {
      throw new IllegalArgumentException("denominator must not be NaN");
    }
    return this.setZ((float) (this.z() % denominator));
  }

  @Override
  public Vector3f set(Vector3f vector) {
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    this.setX(vector.x());
    this.setY(vector.y());
    this.setZ(vector.z());
    return this;
  }

  /**
   * Sets all of this vector's values to the specified value.
   *
   * @param value the value that will be used
   * @return {@code this}
   */
  public Vector3f set(float value) {
    this.setX(value);
    this.setY(value);
    this.setZ(value);
    return this;
  }

  /**
   * Sets the x and y components to the specified values.
   *
   * @param x the new x value
   * @param y the new y value
   * @return {@code this}
   */
  public Vector3f set(float x, float y) {
    this.setX(x);
    this.setY(y);
    return this;
  }

  /**
   * Sets all of this vector's values to the specified values.
   *
   * @param x the new x value
   * @param y the new y value
   * @param z the new z value
   * @return {@code this}
   * @throw IllegalArgumentException if any value is NaN. All values are checked before any are
   *     used.
   */
  public Vector3f set(float x, float y, float z) {
    if (Float.isNaN(x)) {
      throw new IllegalArgumentException("x must not be NaN");
    }
    if (Float.isNaN(y)) {
      throw new IllegalArgumentException("y must not be NaN");
    }
    if (Float.isNaN(z)) {
      throw new IllegalArgumentException("z must not be NaN");
    }
    this.setX(x);
    this.setY(y);
    this.setZ(z);
    return this;
  }

  @Override
  public Vector3f set(Axis axis, Vector3f vector) {
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    switch (axis) {
      case X:
        this.setX(vector.x());
        return this;
      case Y:
        this.setY(vector.y());
        return this;
      case Z:
        this.setZ(vector.z());
        return this;
      case XY:
        this.setX(vector.x());
        this.setY(vector.y());
        return this;
      case XZ:
        this.setX(vector.x());
        this.setZ(vector.z());
        return this;
      case YZ:
        this.setY(vector.y());
        this.setZ(vector.z());
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  /**
   * Sets values at the specified axes to the specified value.
   *
   * @param axis the axes that will be modified
   * @param value the added value
   * @return {@code this}
   */
  public Vector3f set(Axis axis, float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    switch (axis) {
      case X:
        this.setX(value);
        return this;
      case Y:
        this.setY(value);
        return this;
      case Z:
        this.setZ(value);
        return this;
      case XY:
        this.setX(value);
        this.setY(value);
        return this;
      case XZ:
        this.setX(value);
        this.setZ(value);
        return this;
      case YZ:
        this.setY(value);
        this.setZ(value);
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f add(Vector3f vector) {
    this.addX(vector.x());
    this.addY(vector.y());
    this.addZ(vector.z());
    return this;
  }

  /**
   * Adds the specified value to all of this vector's values.
   *
   * @param value the value that will be used
   * @return {@code this}
   */
  public Vector3f add(float value) {
    this.addX(value);
    this.addY(value);
    this.addZ(value);
    return this;
  }

  @Override
  public Vector3f add(Axis axis, Vector3f vector) {
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    switch (axis) {
      case X:
        this.addX(vector.x());
        return this;
      case Y:
        this.addY(vector.y());
        return this;
      case Z:
        this.addZ(vector.z());
        return this;
      case XY:
        this.addX(vector.x());
        this.addY(vector.y());
        return this;
      case XZ:
        this.addX(vector.x());
        this.addZ(vector.z());
        return this;
      case YZ:
        this.addY(vector.y());
        this.addZ(vector.z());
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  /**
   * Adds the specified value to the specified axes.
   *
   * @param axis the axes that will be modified
   * @param value the added value
   * @return {@code this}
   */
  public Vector3f add(Axis axis, float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    switch (axis) {
      case X:
        this.addX(value);
        return this;
      case Y:
        this.addY(value);
        return this;
      case Z:
        this.addZ(value);
        return this;
      case XY:
        this.addX(value);
        this.addY(value);
        return this;
      case XZ:
        this.addX(value);
        this.addZ(value);
        return this;
      case YZ:
        this.addY(value);
        this.addZ(value);
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f subtract(Vector3f vector) {
    this.subtractX(vector.x());
    this.subtractY(vector.y());
    this.subtractZ(vector.z());
    return this;
  }

  /**
   * Subtracts the specified value from each of this vector's values.
   *
   * @param value the value that will be used
   * @return {@code this}
   */
  public Vector3f subtract(float value) {
    this.subtractX(value);
    this.subtractY(value);
    this.subtractZ(value);
    return this;
  }

  @Override
  public Vector3f subtract(Axis axis, Vector3f vector) {
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    switch (axis) {
      case X:
        this.subtractX(vector.x());
        return this;
      case Y:
        this.subtractY(vector.y());
        return this;
      case Z:
        this.subtractZ(vector.z());
        return this;
      case XY:
        this.subtractX(vector.x());
        this.subtractY(vector.y());
        return this;
      case XZ:
        this.subtractX(vector.x());
        this.subtractZ(vector.z());
        return this;
      case YZ:
        this.subtractY(vector.y());
        this.subtractZ(vector.z());
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  /**
   * Subtracts the specified value from the specified axes.
   *
   * @param axis the axes that will be modified
   * @param value the subtracted value
   * @return {@code this}
   */
  public Vector3f subtract(Axis axis, float value) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    switch (axis) {
      case X:
        this.subtractX(value);
        return this;
      case Y:
        this.subtractY(value);
        return this;
      case Z:
        this.subtractZ(value);
        return this;
      case XY:
        this.subtractX(value);
        this.subtractY(value);
        return this;
      case XZ:
        this.subtractX(value);
        this.subtractZ(value);
        return this;
      case YZ:
        this.subtractY(value);
        this.subtractZ(value);
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f multiply(Vector3f vector) {
    this.multiplyX(vector.x());
    this.multiplyY(vector.y());
    this.multiplyZ(vector.z());
    return this;
  }

  @Override
  public Vector3f multiply(double factor) {
    this.multiplyX(factor);
    this.multiplyY(factor);
    this.multiplyZ(factor);
    return this;
  }

  @Override
  public Vector3f multiply(double x, double y, double z) {
    this.multiplyX(x);
    this.multiplyY(y);
    this.multiplyZ(z);
    return this;
  }

  @Override
  public Vector3f multiply(Axis axis, Vector3f vector) {
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    if (vector == null) {
      throw new NullPointerException("vector must not be null");
    }
    switch (axis) {
      case X:
        this.multiplyX(vector.x());
        return this;
      case Y:
        this.multiplyY(vector.y());
        return this;
      case Z:
        this.multiplyZ(vector.z());
        return this;
      case XY:
        this.multiplyX(vector.x());
        this.multiplyY(vector.y());
        return this;
      case XZ:
        this.multiplyX(vector.x());
        this.multiplyZ(vector.z());
        return this;
      case YZ:
        this.multiplyY(vector.y());
        this.multiplyZ(vector.z());
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f multiply(Axis axis, double factor) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    switch (axis) {
      case X:
        this.multiplyX(factor);
        return this;
      case Y:
        this.multiplyY(factor);
        return this;
      case Z:
        this.multiplyZ(factor);
        return this;
      case XY:
        this.multiplyX(factor);
        this.multiplyY(factor);
        return this;
      case XZ:
        this.multiplyX(factor);
        this.multiplyZ(factor);
        return this;
      case YZ:
        this.multiplyY(factor);
        this.multiplyZ(factor);
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f divide(Vector3f vector) {
    return this.divide(vector.x(), vector.y(), vector.z());
  }

  @Override
  public Vector3f divide(double x, double y, double z) {
    this.divideX(x);
    this.divideY(y);
    this.divideZ(z);
    return this;
  }

  @Override
  public double length() {
    return Math.sqrt(Math.pow(this.x(), 2) + Math.pow(this.y(), 2) + Math.pow(this.z(), 2));
  }

  public float area() {
    return this.x * this.y;
  }

  public float volume() {
    return this.x * this.y * this.z;
  }

  @Override
  public Vector3f normalize() {
    float len = (float) this.length();
    return this.set(this.x() / len, this.y() / len, this.z() / len);
  }

  @Override
  public Vector3f reciprocal() {
    this.setX(1 / this.x());
    this.setY(1 / this.y());
    this.setZ(1 / this.z());
    return this;
  }

  @Override
  public Vector3f reciprocal(Axis axis) {
    if (!this.isMutable()) {
      throw new UnsupportedOperationException("vector is not mutable");
    }
    if (axis == null) {
      throw new NullPointerException("Axis must not be null");
    }
    switch (axis) {
      case X:
        this.setX(1 / this.x());
        return this;
      case Y:
        this.setY(1 / this.y());
        return this;
      case Z:
        this.setZ(1 / this.z());
        return this;
      case XY:
        this.setX(1 / this.x());
        this.setY(1 / this.y());
        return this;
      case XZ:
        this.setX(1 / this.x());
        this.setZ(1 / this.z());
        return this;
      case YZ:
        this.setY(1 / this.y());
        this.setZ(1 / this.z());
        return this;
    }
    throw new IllegalArgumentException("Axis is invalid");
  }

  @Override
  public Vector3f interpolate(Vector3f dest, float offset) {
    if (dest == null) {
      throw new NullPointerException("dest must not be null");
    }
    if (offset >= 1f) {
      this.set(dest);
    } else if (offset >= 0f) {
      this.x += (dest.x - this.x) * offset;
      this.y += (dest.y - this.y) * offset;
      this.z += (dest.z - this.z) * offset;
    }
    return this;
  }

  @Override
  public Vector3f cross(Vector3f other) {
    return this.set(
        this.y() * other.z() - other.y() * this.z(),
        -this.x() * other.z() + other.x() * this.z(),
        this.x() * other.y() - other.x() * this.y());
  }

  @Override
  public Vector3f clear() {
    return this.set(0f);
  }

  @Override
  public Vector3f clear(Axis axis) {
    return this.set(axis, 0f);
  }

  @Override
  public Dimension toDimension() {
    return new Dimension((int) x, (int) y);
  }

  @Override
  public Point toPoint() {
    return new Point((int) x, (int) y);
  }

  public Point2D.Float toPoint2D() {
    return new Point2D.Float(x, y);
  }

  @Override
  public Vector3f toMutable() {
    return Vector3f.mutable(x, y, z);
  }

  @Override
  public Vector3f toFrozen() {
    if (!this.isMutable()) {
      return this;
    }
    return Vector3f.frozen(x, y, z);
  }

  @Override
  public Vector3f getThis() {
    return this;
  }

  @Override
  public boolean at(Vector3f vector) {
    if (vector == null) {
      return false;
    }
    return this.x() == vector.x() && this.y() == vector.y() && this.z() == vector.z();
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof Vector3f)) {
      return false;
    }
    final Vector3f other = (Vector3f) obj;
    if (this.isMutable() != other.isMutable()) {
      return false;
    }
    if (this.x() != other.x()) {
      return false;
    }
    if (this.y() != other.y()) {
      return false;
    }
    if (this.z() != other.z()) {
      return false;
    }
    return true;
  }

  @Override
  public int hashCode() {
    int result = 11;
    result = 31 * result + (this.isMutable() ? 1 : 0);
    result = 31 * result + java.lang.Float.floatToIntBits(this.x());
    result = 31 * result + java.lang.Float.floatToIntBits(this.y());
    result = 31 * result + java.lang.Float.floatToIntBits(this.z());
    return result;
  }

  @Override
  public String toString() {
    return String.format(
        "Vector3f[%s (%f, %f, %f)]",
        this.isMutable() ? "mutable" : "frozen", this.x(), this.y(), this.z());
  }
}