1 /* 2 * Copyright 2006 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.common.geometry; 17 18 /** 19 * An S2Point represents a point on the unit sphere as a 3D vector. Usually 20 * points are normalized to be unit length, but some methods do not require 21 * this. 22 * 23 */ 24 public strictfp class S2Point implements Comparable<S2Point> { 25 // coordinates of the points 26 final double x; 27 final double y; 28 final double z; 29 S2Point()30 public S2Point() { 31 x = y = z = 0; 32 } 33 S2Point(double x, double y, double z)34 public S2Point(double x, double y, double z) { 35 this.x = x; 36 this.y = y; 37 this.z = z; 38 } 39 minus(S2Point p1, S2Point p2)40 public static S2Point minus(S2Point p1, S2Point p2) { 41 return sub(p1, p2); 42 } 43 neg(S2Point p)44 public static S2Point neg(S2Point p) { 45 return new S2Point(-p.x, -p.y, -p.z); 46 } 47 norm2()48 public double norm2() { 49 return x * x + y * y + z * z; 50 } 51 norm()52 public double norm() { 53 return Math.sqrt(norm2()); 54 } 55 crossProd(final S2Point p1, final S2Point p2)56 public static S2Point crossProd(final S2Point p1, final S2Point p2) { 57 return new S2Point( 58 p1.y * p2.z - p1.z * p2.y, p1.z * p2.x - p1.x * p2.z, p1.x * p2.y - p1.y * p2.x); 59 } 60 add(final S2Point p1, final S2Point p2)61 public static S2Point add(final S2Point p1, final S2Point p2) { 62 return new S2Point(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); 63 } 64 sub(final S2Point p1, final S2Point p2)65 public static S2Point sub(final S2Point p1, final S2Point p2) { 66 return new S2Point(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); 67 } 68 dotProd(S2Point that)69 public double dotProd(S2Point that) { 70 return this.x * that.x + this.y * that.y + this.z * that.z; 71 } 72 mul(final S2Point p, double m)73 public static S2Point mul(final S2Point p, double m) { 74 return new S2Point(m * p.x, m * p.y, m * p.z); 75 } 76 div(final S2Point p, double m)77 public static S2Point div(final S2Point p, double m) { 78 return new S2Point(p.x / m, p.y / m, p.z / m); 79 } 80 81 /** return a vector orthogonal to this one */ ortho()82 public S2Point ortho() { 83 int k = largestAbsComponent(); 84 S2Point temp; 85 if (k == 1) { 86 temp = new S2Point(1, 0, 0); 87 } else if (k == 2) { 88 temp = new S2Point(0, 1, 0); 89 } else { 90 temp = new S2Point(0, 0, 1); 91 } 92 return S2Point.normalize(crossProd(this, temp)); 93 } 94 95 /** Return the index of the largest component fabs */ largestAbsComponent()96 public int largestAbsComponent() { 97 S2Point temp = fabs(this); 98 if (temp.x > temp.y) { 99 if (temp.x > temp.z) { 100 return 0; 101 } else { 102 return 2; 103 } 104 } else { 105 if (temp.y > temp.z) { 106 return 1; 107 } else { 108 return 2; 109 } 110 } 111 } 112 fabs(S2Point p)113 public static S2Point fabs(S2Point p) { 114 return new S2Point(Math.abs(p.x), Math.abs(p.y), Math.abs(p.z)); 115 } 116 normalize(S2Point p)117 public static S2Point normalize(S2Point p) { 118 double norm = p.norm(); 119 if (norm != 0) { 120 norm = 1.0 / norm; 121 } 122 return S2Point.mul(p, norm); 123 } 124 get(int axis)125 public double get(int axis) { 126 return (axis == 0) ? x : (axis == 1) ? y : z; 127 } 128 129 /** Return the angle between two vectors in radians */ angle(S2Point va)130 public double angle(S2Point va) { 131 return Math.atan2(crossProd(this, va).norm(), this.dotProd(va)); 132 } 133 134 /** 135 * Compare two vectors, return true if all their components are within a 136 * difference of margin. 137 */ aequal(S2Point that, double margin)138 boolean aequal(S2Point that, double margin) { 139 return (Math.abs(x - that.x) < margin) && (Math.abs(y - that.y) < margin) 140 && (Math.abs(z - that.z) < margin); 141 } 142 143 @Override equals(Object that)144 public boolean equals(Object that) { 145 if (!(that instanceof S2Point)) { 146 return false; 147 } 148 S2Point thatPoint = (S2Point) that; 149 return this.x == thatPoint.x && this.y == thatPoint.y && this.z == thatPoint.z; 150 } 151 lessThan(S2Point vb)152 public boolean lessThan(S2Point vb) { 153 if (x < vb.x) { 154 return true; 155 } 156 if (vb.x < x) { 157 return false; 158 } 159 if (y < vb.y) { 160 return true; 161 } 162 if (vb.y < y) { 163 return false; 164 } 165 if (z < vb.z) { 166 return true; 167 } 168 return false; 169 } 170 171 // Required for Comparable 172 @Override compareTo(S2Point other)173 public int compareTo(S2Point other) { 174 return (lessThan(other) ? -1 : (equals(other) ? 0 : 1)); 175 } 176 177 @Override toString()178 public String toString() { 179 return "(" + x + ", " + y + ", " + z + ")"; 180 } 181 toDegreesString()182 public String toDegreesString() { 183 S2LatLng s2LatLng = new S2LatLng(this); 184 return "(" + Double.toString(s2LatLng.latDegrees()) + ", " 185 + Double.toString(s2LatLng.lngDegrees()) + ")"; 186 } 187 188 /** 189 * Calcualates hashcode based on stored coordinates. Since we want +0.0 and 190 * -0.0 to be treated the same, we ignore the sign of the coordinates. 191 */ 192 @Override hashCode()193 public int hashCode() { 194 long value = 17; 195 value += 37 * value + Double.doubleToLongBits(Math.abs(x)); 196 value += 37 * value + Double.doubleToLongBits(Math.abs(y)); 197 value += 37 * value + Double.doubleToLongBits(Math.abs(z)); 198 return (int) (value ^ (value >>> 32)); 199 } 200 } 201