1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package s2
16
17 import (
18 "math"
19 "testing"
20
21 "github.com/golang/geo/r3"
22 "github.com/golang/geo/s1"
23 )
24
25 func TestOriginPoint(t *testing.T) {
26 if math.Abs(OriginPoint().Norm()-1) > 1e-15 {
27 t.Errorf("Origin point norm = %v, want 1", OriginPoint().Norm())
28 }
29
30
31
32
33
34
35
36 p := Point{r3.Vector{-0.01, 0.01 * stToUV(2.0/3), 1}}
37 if !p.ApproxEqual(OriginPoint()) {
38 t.Errorf("Origin point should fall in the Siberian Sea, but does not.")
39 }
40
41
42 if dist := math.Acos(OriginPoint().Z) * earthRadiusKm; dist <= 50 {
43 t.Errorf("Origin point is to close to the North Pole. Got %v, want >= 50km", dist)
44 }
45 }
46
47 func TestPointCross(t *testing.T) {
48 tests := []struct {
49 p1x, p1y, p1z, p2x, p2y, p2z, norm float64
50 }{
51 {1, 0, 0, 1, 0, 0, 1},
52 {1, 0, 0, 0, 1, 0, 2},
53 {0, 1, 0, 1, 0, 0, 2},
54 {1, 2, 3, -4, 5, -6, 2 * math.Sqrt(934)},
55 }
56 for _, test := range tests {
57 p1 := Point{r3.Vector{test.p1x, test.p1y, test.p1z}}
58 p2 := Point{r3.Vector{test.p2x, test.p2y, test.p2z}}
59 result := p1.PointCross(p2)
60 if !float64Eq(result.Norm(), test.norm) {
61 t.Errorf("|%v ⨯ %v| = %v, want %v", p1, p2, result.Norm(), test.norm)
62 }
63 if x := result.Dot(p1.Vector); !float64Eq(x, 0) {
64 t.Errorf("|(%v ⨯ %v) · %v| = %v, want 0", p1, p2, p1, x)
65 }
66 if x := result.Dot(p2.Vector); !float64Eq(x, 0) {
67 t.Errorf("|(%v ⨯ %v) · %v| = %v, want 0", p1, p2, p2, x)
68 }
69 }
70 }
71
72 func TestPointDistance(t *testing.T) {
73 tests := []struct {
74 x1, y1, z1 float64
75 x2, y2, z2 float64
76 want float64
77 }{
78 {1, 0, 0, 1, 0, 0, 0},
79 {1, 0, 0, 0, 1, 0, math.Pi / 2},
80 {1, 0, 0, 0, 1, 1, math.Pi / 2},
81 {1, 0, 0, -1, 0, 0, math.Pi},
82 {1, 2, 3, 2, 3, -1, 1.2055891055045298},
83 }
84 for _, test := range tests {
85 p1 := Point{r3.Vector{test.x1, test.y1, test.z1}}
86 p2 := Point{r3.Vector{test.x2, test.y2, test.z2}}
87 if a := p1.Distance(p2).Radians(); !float64Eq(a, test.want) {
88 t.Errorf("%v.Distance(%v) = %v, want %v", p1, p2, a, test.want)
89 }
90 if a := p2.Distance(p1).Radians(); !float64Eq(a, test.want) {
91 t.Errorf("%v.Distance(%v) = %v, want %v", p2, p1, a, test.want)
92 }
93 }
94 }
95
96 func TestChordAngleBetweenPoints(t *testing.T) {
97 for iter := 0; iter < 10; iter++ {
98 m := randomFrame()
99 x := m.col(0)
100 y := m.col(1)
101 z := m.col(2)
102
103 if got := ChordAngleBetweenPoints(z, z).Angle(); got != 0 {
104 t.Errorf("ChordAngleBetweenPoints(%v, %v) = %v, want 0", z, z, got)
105 }
106 if got, want := ChordAngleBetweenPoints(Point{z.Mul(-1)}, z).Angle().Radians(), math.Pi; !float64Near(got, want, 1e-7) {
107 t.Errorf("ChordAngleBetweenPoints(%v, %v) = %v, want %v", z.Mul(-1), z, got, want)
108 }
109 if got, want := ChordAngleBetweenPoints(x, z).Angle().Radians(), math.Pi/2; !float64Eq(got, want) {
110 t.Errorf("ChordAngleBetweenPoints(%v, %v) = %v, want %v", x, z, got, want)
111 }
112 w := Point{y.Add(z.Vector).Normalize()}
113 if got, want := ChordAngleBetweenPoints(w, z).Angle().Radians(), math.Pi/4; !float64Eq(got, want) {
114 t.Errorf("ChordAngleBetweenPoints(%v, %v) = %v, want %v", w, z, got, want)
115 }
116 }
117 }
118
119 func TestPointApproxEqual(t *testing.T) {
120 tests := []struct {
121 x1, y1, z1 float64
122 x2, y2, z2 float64
123 want bool
124 }{
125 {1, 0, 0, 1, 0, 0, true},
126 {1, 0, 0, 0, 1, 0, false},
127 {1, 0, 0, 0, 1, 1, false},
128 {1, 0, 0, -1, 0, 0, false},
129 {1, 2, 3, 2, 3, -1, false},
130 {1, 0, 0, 1 * (1 + epsilon), 0, 0, true},
131 {1, 0, 0, 1 * (1 - epsilon), 0, 0, true},
132 {1, 0, 0, 1 + epsilon, 0, 0, true},
133 {1, 0, 0, 1 - epsilon, 0, 0, true},
134 {1, 0, 0, 1, epsilon, 0, true},
135 {1, 0, 0, 1, epsilon, epsilon, false},
136 {1, epsilon, 0, 1, -epsilon, epsilon, false},
137 }
138 for _, test := range tests {
139 p1 := Point{r3.Vector{test.x1, test.y1, test.z1}}
140 p2 := Point{r3.Vector{test.x2, test.y2, test.z2}}
141 if got := p1.ApproxEqual(p2); got != test.want {
142 t.Errorf("%v.ApproxEqual(%v), got %v want %v", p1, p2, got, test.want)
143 }
144 }
145 }
146
147 func TestPointOrtho(t *testing.T) {
148 tests := []struct {
149 have Point
150 want Point
151 }{
152
153
154 {
155 have: Point{r3.Vector{X: 1, Y: 0, Z: 0}},
156 want: Point{r3.Vector{X: 0, Y: -0.999985955295886075333556, Z: 0.005299925563068195837058}},
157 },
158 {
159 have: Point{r3.Vector{X: 0, Y: 1, Z: 0}},
160 want: Point{r3.Vector{X: 0.004569952278750987959000, Y: 0.0, Z: -0.999989557713564125585037}},
161 },
162 {
163 have: Point{r3.Vector{X: 0, Y: 0, Z: 1}},
164 want: Point{r3.Vector{X: -0.999928007775066962636856, Y: 0.011999136093300803371231, Z: 0}},
165 },
166
167
168 {
169 have: Point{r3.Vector{X: 1, Y: 1, Z: 1}},
170 want: Point{r3.Vector{X: -0.709740689278763769998193, Y: 0.005297583276916723732386, Z: 0.704443106001847008101890}},
171 },
172 {
173 have: Point{r3.Vector{X: 3, Y: -2, Z: 0.4}},
174 want: Point{r3.Vector{X: -0.555687999915428054720223, Y: -0.831317152491703792449584, Z: 0.011074236907191168863274}},
175 },
176 {
177 have: Point{r3.Vector{X: 0.012, Y: 0.0053, Z: 0.00457}},
178 want: Point{r3.Vector{X: 0.404015523469256565558538, Y: -0.914752128609637393807930, Z: 0}},
179 },
180 }
181
182 for _, test := range tests {
183 got := Ortho(test.have)
184
185 if got != test.want {
186 t.Errorf("Ortho(%v) = %v, want %v", test.have, got, test.want)
187 }
188
189
190 if !float64Eq(test.have.Dot(got.Vector), 0) {
191 t.Errorf("%v = not orthogonal to %v.Ortho()", test.have, got)
192 }
193
194 if !got.IsUnit() {
195 t.Errorf("%v should be unit length, but is not", got)
196 }
197 }
198 }
199
200 func TestPointRegularPoints(t *testing.T) {
201
202 const epsilon = 1e-13
203 center := PointFromLatLng(LatLngFromDegrees(80, 135))
204 radius := s1.Degree * 20
205 pts := regularPoints(center, radius, 4)
206
207 if len(pts) != 4 {
208 t.Errorf("regularPoints with 4 vertices should have 4 vertices, got %d", len(pts))
209 }
210
211 lls := []LatLng{
212 LatLngFromPoint(pts[0]),
213 LatLngFromPoint(pts[1]),
214 LatLngFromPoint(pts[2]),
215 LatLngFromPoint(pts[3]),
216 }
217 cll := LatLngFromPoint(center)
218
219
220 wantDist := 20.0
221 for i, ll := range lls {
222 if got := cll.Distance(ll).Degrees(); !float64Near(got, wantDist, epsilon) {
223 t.Errorf("Vertex %d distance from center = %v, want %v", i, got, wantDist)
224 }
225 }
226
227
228 wantAngle := math.Pi / 2
229 for i := 0; i < len(pts); i++ {
230
231 v0, v1, v2 := pts[(4+i+1)%4], pts[(4+i)%4], pts[(4+i-1)%4]
232 if got := float64(v0.Sub(v1.Vector).Angle(v2.Sub(v1.Vector))); !float64Eq(got, wantAngle) {
233 t.Errorf("(%v-%v).Angle(%v-%v) = %v, want %v", v0, v1, v1, v2, got, wantAngle)
234 }
235 }
236
237
238 wantLength := 27.990890717782829
239 for i := 0; i < len(lls); i++ {
240 ll1, ll2 := lls[i], lls[(i+1)%4]
241 if got := ll1.Distance(ll2).Degrees(); !float64Near(got, wantLength, epsilon) {
242 t.Errorf("%v.Distance(%v) = %v, want %v", ll1, ll2, got, wantLength)
243 }
244 }
245
246
247
248 if got, want := lls[0].Lat.Degrees(), 62.162880741097204; !float64Near(got, want, epsilon) {
249 t.Errorf("%v.Lat = %v, want %v", lls[0], got, want)
250 }
251 if got, want := lls[0].Lng.Degrees(), 103.11051028343407; !float64Near(got, want, epsilon) {
252 t.Errorf("%v.Lng = %v, want %v", lls[0], got, want)
253 }
254 }
255
256 func TestPointRegion(t *testing.T) {
257 p := Point{r3.Vector{1, 0, 0}}
258 r := Point{r3.Vector{1, 0, 0}}
259 if !r.Contains(p) {
260 t.Errorf("%v.Contains(%v) = false, want true", r, p)
261 }
262 if !r.ContainsPoint(p) {
263 t.Errorf("%v.ContainsPoint(%v) = false, want true", r, p)
264 }
265 if !r.Contains(r) {
266 t.Errorf("%v.Contains(%v) = false, want true", r, r)
267 }
268 if !r.ContainsPoint(r) {
269 t.Errorf("%v.ContainsPoint(%v) = false, want true", r, r)
270 }
271 if s := (Point{r3.Vector{1, 0, 1}}); r.Contains(s) {
272 t.Errorf("%v.Contains(%v) = true, want false", r, s)
273 }
274 if got, want := r.CapBound(), CapFromPoint(p); !got.ApproxEqual(want) {
275 t.Errorf("%v.CapBound() = %v, want %v", r, got, want)
276 }
277 if got, want := r.RectBound(), RectFromLatLng(LatLngFromPoint(p)); !rectsApproxEqual(got, want, epsilon, epsilon) {
278 t.Errorf("%v.RectBound() = %v, want %v", r, got, want)
279 }
280
281
282 cell := CellFromPoint(p)
283 if r.ContainsCell(cell) {
284 t.Errorf("%v.ContainsCell(%v) = true, want false", r, cell)
285 }
286 if !r.IntersectsCell(cell) {
287 t.Errorf("%v.IntersectsCell(%v) = false, want true", r, cell)
288 }
289
290 }
291
292 func TestPointRotate(t *testing.T) {
293 for iter := 0; iter < 1000; iter++ {
294 axis := randomPoint()
295 target := randomPoint()
296
297 distance := s1.Angle(math.Pi * math.Pow(1e-15, randomFloat64()))
298
299 if oneIn(5) {
300 distance = math.Pi - distance
301 }
302 p := InterpolateAtDistance(distance, axis, target)
303
304 angle := s1.Angle(2 * math.Pi * math.Pow(1e-15, randomFloat64()))
305 if oneIn(3) {
306 angle = -angle
307 }
308 if oneIn(10) {
309 angle = 0
310 }
311
312 got := Rotate(p, axis, angle)
313
314 if !got.IsUnit() {
315 t.Errorf("%v should be unit length", got)
316 }
317
318
319 const maxPositionError = 1e-15
320 if (got.Distance(axis) - p.Distance(axis)).Abs().Radians() > maxPositionError {
321 t.Errorf("rotated point %v should be same distance as %v, got %v, want %v", got, p, got.Distance(axis), p.Distance(axis))
322 }
323
324
325
326
327 axisDistance := p.Cross(axis.Vector).Norm()
328 maxRotationError := 0.0
329 if axisDistance < maxPositionError {
330 maxRotationError = 2 * math.Pi
331 } else {
332 maxRotationError = math.Asin(maxPositionError / axisDistance)
333 }
334 actualRotation := TurnAngle(p, axis, got) + math.Pi
335 rotationError := math.Remainder((angle - actualRotation).Radians(), 2*math.Pi)
336 if rotationError > maxRotationError {
337 t.Errorf("rotational angle of %v = %v, want %v", got, actualRotation, angle)
338 }
339 }
340 }
341
View as plain text