| /* |
| * 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)); |
| } |
| } |