package org.osm2world.core.math; import static java.lang.Math.*; import java.util.List; import com.google.common.base.Function; import com.google.common.collect.Lists; /** * immutable two-dimensional vector with x and z component */ public class VectorXZ implements Vector3D { public final double x; public final double z; @Override public double getX() { return x; } @Override public double getY() { return 0; } @Override public double getZ() { return z; } public VectorXZ(double x, double z) { this.x = x; this.z = z; } public double length() { return Math.sqrt(x*x + z*z); } public double lengthSquared() { return x*x + z*z; } public VectorXZ normalize() { double length = length(); return new VectorXZ(x / length, z / length); } /** * adds the parameter to this vector and returns the result */ public VectorXZ add(VectorXZ other) { return new VectorXZ(x + other.x, z + other.z); } /** * subtracts the parameter from this vector and returns the result */ public VectorXZ subtract(VectorXZ other) { return new VectorXZ(x - other.x, z - other.z); } public VectorXZ mult(double scalar) { return new VectorXZ(x*scalar, z*scalar); } public VectorXZ invert() { return new VectorXZ(-x, -z); } public double dot(VectorXZ other) { return this.x * other.x + this.z * other.z; } /** * returns the vector that would result from calculating the * cross product of this vector (normalized and extended * to three dimensions) and (0,1,0). * * It's the vector that, seen from "above", points to the right * side of this vector and is orthogonal to it. * * The resulting vector's length is 1. */ public VectorXZ rightNormal() { double length = length(); return new VectorXZ(z / length, -(x / length)); } public double distanceTo(VectorXZ other) { return distance(this, other); } /** * gets this vector's angle relative to (0,1). * Inverse of {@link #fromAngle(double)}. * * @return angle in radians */ public double angle() { if (x == 0 && z == 0) { return 0; } else { VectorXZ normalized = this.normalize(); if (x >= 0) { return acos(normalized.dot(Z_UNIT)); } else { return 2*PI - acos(normalized.dot(Z_UNIT)); } } } /** * @see #angle() */ public double angleTo(VectorXZ other) { return other.subtract(this).angle(); } @Override public String toString() { return "(" + x + "," + z + ")"; } public static final VectorXZ NULL_VECTOR = new VectorXZ(0, 0); public static final VectorXZ X_UNIT = new VectorXZ(1, 0); public static final VectorXZ Z_UNIT = new VectorXZ(0, 1); public VectorXYZ xyz(double y) { return new VectorXYZ(x, y, z); } @Override public boolean equals(Object obj) { if (!(obj instanceof VectorXZ)) { return false; } VectorXZ other = (VectorXZ) obj; return x == other.x && z == other.z; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(x); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(z); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } //TODO: angle bisection as method in this class /** * returns a unit vector based on an angular direction. * For example, * angle 0 creates vector (0,1), * angle PI/2 creates vector (1,0). * * @param directionRad direction angle in radians */ public static VectorXZ fromAngle(double directionRad) { return new VectorXZ( sin(directionRad), cos(directionRad)); } /** * returns the angle between two direction vectors * @return angle as radians, in range 0 to PI */ public static double angleBetween(VectorXZ v1, VectorXZ v2) { double rawAngle = abs(v1.angle() - v2.angle()); if (rawAngle < PI) { return rawAngle; } else if (rawAngle == PI) { return PI; } else { return PI - (rawAngle % PI); } } public static final double distance(VectorXZ v1, VectorXZ v2) { //SUGGEST (performance): don't create temporary vector return (v2.subtract(v1)).length(); } public static final double distanceSquared(VectorXZ v1, VectorXZ v2) { //SUGGEST (performance): don't create temporary vector return (v2.subtract(v1)).lengthSquared(); } public static final List<VectorXYZ> listXYZ(List<VectorXZ> vs, final double y) { return Lists.transform(vs, VectorXZ.xyzFunction(y)); } public static final Function<VectorXZ, VectorXYZ> xyzFunction(final double y) { return new Function<VectorXZ, VectorXYZ>() { @Override public VectorXYZ apply(VectorXZ v) { return v.xyz(y); } }; } }