blob: 89a2322014585a3d9ca17b4af1ff62f35da63c94 [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.geometry;
/**
* An S2Point represents a point on the unit sphere as a 3D vector. Usually
* points are normalized to be unit length, but some methods do not require
* this.
*
*/
public strictfp class S2Point implements Comparable<S2Point> {
// coordinates of the points
final double x;
final double y;
final double z;
public S2Point() {
x = y = z = 0;
}
public S2Point(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public static S2Point minus(S2Point p1, S2Point p2) {
return sub(p1, p2);
}
public static S2Point neg(S2Point p) {
return new S2Point(-p.x, -p.y, -p.z);
}
public double norm2() {
return x * x + y * y + z * z;
}
public double norm() {
return Math.sqrt(norm2());
}
public static S2Point crossProd(final S2Point p1, final S2Point p2) {
return new S2Point(
p1.y * p2.z - p1.z * p2.y, p1.z * p2.x - p1.x * p2.z, p1.x * p2.y - p1.y * p2.x);
}
public static S2Point add(final S2Point p1, final S2Point p2) {
return new S2Point(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
}
public static S2Point sub(final S2Point p1, final S2Point p2) {
return new S2Point(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
}
public double dotProd(S2Point that) {
return this.x * that.x + this.y * that.y + this.z * that.z;
}
public static S2Point mul(final S2Point p, double m) {
return new S2Point(m * p.x, m * p.y, m * p.z);
}
public static S2Point div(final S2Point p, double m) {
return new S2Point(p.x / m, p.y / m, p.z / m);
}
/** return a vector orthogonal to this one */
public S2Point ortho() {
int k = largestAbsComponent();
S2Point temp;
if (k == 1) {
temp = new S2Point(1, 0, 0);
} else if (k == 2) {
temp = new S2Point(0, 1, 0);
} else {
temp = new S2Point(0, 0, 1);
}
return S2Point.normalize(crossProd(this, temp));
}
/** Return the index of the largest component fabs */
public int largestAbsComponent() {
S2Point temp = fabs(this);
if (temp.x > temp.y) {
if (temp.x > temp.z) {
return 0;
} else {
return 2;
}
} else {
if (temp.y > temp.z) {
return 1;
} else {
return 2;
}
}
}
public static S2Point fabs(S2Point p) {
return new S2Point(Math.abs(p.x), Math.abs(p.y), Math.abs(p.z));
}
public static S2Point normalize(S2Point p) {
double norm = p.norm();
if (norm != 0) {
norm = 1.0 / norm;
}
return S2Point.mul(p, norm);
}
public double get(int axis) {
return (axis == 0) ? x : (axis == 1) ? y : z;
}
/** Return the angle between two vectors in radians */
public double angle(S2Point va) {
return Math.atan2(crossProd(this, va).norm(), this.dotProd(va));
}
/**
* Compare two vectors, return true if all their components are within a
* difference of margin.
*/
boolean aequal(S2Point that, double margin) {
return (Math.abs(x - that.x) < margin) && (Math.abs(y - that.y) < margin)
&& (Math.abs(z - that.z) < margin);
}
@Override
public boolean equals(Object that) {
if (!(that instanceof S2Point)) {
return false;
}
S2Point thatPoint = (S2Point) that;
return this.x == thatPoint.x && this.y == thatPoint.y && this.z == thatPoint.z;
}
public boolean lessThan(S2Point vb) {
if (x < vb.x) {
return true;
}
if (vb.x < x) {
return false;
}
if (y < vb.y) {
return true;
}
if (vb.y < y) {
return false;
}
if (z < vb.z) {
return true;
}
return false;
}
// Required for Comparable
@Override
public int compareTo(S2Point other) {
return (lessThan(other) ? -1 : (equals(other) ? 0 : 1));
}
@Override
public String toString() {
return "(" + x + ", " + y + ", " + z + ")";
}
public String toDegreesString() {
S2LatLng s2LatLng = new S2LatLng(this);
return "(" + Double.toString(s2LatLng.latDegrees()) + ", "
+ Double.toString(s2LatLng.lngDegrees()) + ")";
}
/**
* Calcualates hashcode based on stored coordinates. Since we want +0.0 and
* -0.0 to be treated the same, we ignore the sign of the coordinates.
*/
@Override
public int hashCode() {
long value = 17;
value += 37 * value + Double.doubleToLongBits(Math.abs(x));
value += 37 * value + Double.doubleToLongBits(Math.abs(y));
value += 37 * value + Double.doubleToLongBits(Math.abs(z));
return (int) (value ^ (value >>> 32));
}
}