quad_sphere 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +18 -0
- data/README.md +153 -0
- data/examples/binning.rb +44 -0
- data/examples/distort_closure.rb +108 -0
- data/examples/gl1.rb +164 -0
- data/examples/gl2.rb +182 -0
- data/examples/grid.rb +69 -0
- data/lib/quad_sphere/csc.rb +1 -1
- data/lib/quad_sphere/version.rb +5 -0
- data/quad_sphere.gemspec +30 -0
- data/test/test_quad_sphere.rb +860 -0
- metadata +39 -40
data/examples/gl2.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# A shaded view of the CSC projection. You need OpenGL for this.
|
2
|
+
|
3
|
+
require 'quad_sphere/csc'
|
4
|
+
require 'opengl'
|
5
|
+
|
6
|
+
class CSCShadedApp
|
7
|
+
|
8
|
+
WINDOW_SIZE = 480
|
9
|
+
|
10
|
+
# How many subdivisions of each cube face. Can be raised for
|
11
|
+
# smoother shading, at the cost of model complexity.
|
12
|
+
FACE_SUBDIV = 10
|
13
|
+
|
14
|
+
def run
|
15
|
+
@ang1 = 0
|
16
|
+
@ang2 = 0
|
17
|
+
@ang3 = 0
|
18
|
+
|
19
|
+
glutInit
|
20
|
+
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
|
21
|
+
glutInitWindowSize(WINDOW_SIZE, WINDOW_SIZE)
|
22
|
+
glutCreateWindow('CSC')
|
23
|
+
|
24
|
+
glEnable(GL_LIGHTING)
|
25
|
+
glEnable(GL_LIGHT0)
|
26
|
+
glEnable(GL_DEPTH_TEST)
|
27
|
+
glEnable(GL_CULL_FACE)
|
28
|
+
glFrontFace(GL_CW)
|
29
|
+
glClearColor(0.5, 0.5, 0.5, 0.0)
|
30
|
+
setup_model
|
31
|
+
|
32
|
+
glutDisplayFunc(method :display)
|
33
|
+
glutReshapeFunc(method :reshape)
|
34
|
+
glutKeyboardFunc(method :keyboard)
|
35
|
+
|
36
|
+
# And run.
|
37
|
+
glutMainLoop()
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def setup_model
|
43
|
+
glGenLists(1)
|
44
|
+
glNewList(1, GL_COMPILE)
|
45
|
+
|
46
|
+
# The cube faces, and the colour we want each:
|
47
|
+
faces = [ [ QuadSphere::TOP_FACE, [ 0.9, 0.25, 0.25, 1.0 ] ], # red
|
48
|
+
[ QuadSphere::FRONT_FACE, [ 0.25, 0.25, 0.9, 1.0 ] ], # blue
|
49
|
+
[ QuadSphere::EAST_FACE, [ 0.25, 0.9, 0.25, 1.0 ] ], # green
|
50
|
+
[ QuadSphere::BACK_FACE, [ 0.9, 0.9, 0.25, 1.0 ] ], # yellow
|
51
|
+
[ QuadSphere::WEST_FACE, [ 0.25, 0.9, 0.9, 1.0 ] ], #cyan
|
52
|
+
[ QuadSphere::BOTTOM_FACE, [ 0.9, 0.25, 0.9, 1.0 ] ] ] #magenta
|
53
|
+
|
54
|
+
faces.each do |face, colour|
|
55
|
+
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colour);
|
56
|
+
# Calculate all the vertices we'll use for this face...
|
57
|
+
vertices = grid(face, FACE_SUBDIV)
|
58
|
+
# ... and arrange them in triangle strips:
|
59
|
+
mesh2strips(FACE_SUBDIV, vertices)
|
60
|
+
end
|
61
|
+
|
62
|
+
glEndList
|
63
|
+
end
|
64
|
+
|
65
|
+
def reshape(w, h)
|
66
|
+
glViewport(0, 0, w, h)
|
67
|
+
glMatrixMode(GL_PROJECTION)
|
68
|
+
glLoadIdentity()
|
69
|
+
gluPerspective(45.0, w.to_f / h.to_f, 1.0, 20.0)
|
70
|
+
glMatrixMode(GL_MODELVIEW)
|
71
|
+
glLoadIdentity()
|
72
|
+
gluLookAt(3.2,0,0, 0,0,0, 0,0,1)
|
73
|
+
|
74
|
+
# Add a light.
|
75
|
+
glPushMatrix()
|
76
|
+
glTranslate(2.5,-0.8,0.8);
|
77
|
+
glLightfv(GL_LIGHT0, GL_POSITION, [0, 0, 0, 1])
|
78
|
+
glPopMatrix()
|
79
|
+
end
|
80
|
+
|
81
|
+
def display
|
82
|
+
# XXX - maybe it'd be more efficient to only clear the depth
|
83
|
+
# buffer if we moved the model or camera?
|
84
|
+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
85
|
+
|
86
|
+
# Rotate and draw the model.
|
87
|
+
glPushMatrix()
|
88
|
+
glRotate(@ang1, 0, 0, 1)
|
89
|
+
glRotate(@ang2, 1, 0, 0)
|
90
|
+
glRotate(@ang3, 0, 1, 0)
|
91
|
+
glCallList(1)
|
92
|
+
glPopMatrix()
|
93
|
+
|
94
|
+
# Done.
|
95
|
+
glutSwapBuffers()
|
96
|
+
end
|
97
|
+
|
98
|
+
def keyboard(key, x, y)
|
99
|
+
case (key)
|
100
|
+
when ?s
|
101
|
+
@ang1 = (@ang1 + 5) % 360
|
102
|
+
glutPostRedisplay()
|
103
|
+
when ?a
|
104
|
+
@ang1 = (@ang1 - 5) % 360
|
105
|
+
glutPostRedisplay()
|
106
|
+
when ?w
|
107
|
+
@ang2 = (@ang2 + 5) % 360
|
108
|
+
glutPostRedisplay()
|
109
|
+
when ?q
|
110
|
+
@ang2 = (@ang2 - 5) % 360
|
111
|
+
glutPostRedisplay()
|
112
|
+
when ?x
|
113
|
+
@ang3 = (@ang3 + 5) % 360
|
114
|
+
glutPostRedisplay()
|
115
|
+
when ?z
|
116
|
+
@ang3 = (@ang3 - 5) % 360
|
117
|
+
glutPostRedisplay()
|
118
|
+
when ?r
|
119
|
+
@ang1 = @ang2 = @ang3 = 0
|
120
|
+
glutPostRedisplay()
|
121
|
+
when ?\e, ?Q
|
122
|
+
exit(0)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create a NxN grid of points on a face of the cube. Note that this
|
127
|
+
# will generate (N+1)*(N+1) points.
|
128
|
+
#
|
129
|
+
# Each point is projected on the sphere and stored in an array. Note
|
130
|
+
# that all these are points on the unit sphere, and so their distance
|
131
|
+
# to the origin is 1, and so each point can be used as its own normal.
|
132
|
+
def grid(face, n)
|
133
|
+
dx = 2.0/n
|
134
|
+
dy = 2.0/n
|
135
|
+
a = Array.new
|
136
|
+
n += 1
|
137
|
+
|
138
|
+
n.times do |j|
|
139
|
+
y = -1.0 + j*dy
|
140
|
+
n.times do |i|
|
141
|
+
x = -1.0 + i*dx
|
142
|
+
lon, lat = QuadSphere::CSC.inverse(face, x, y)
|
143
|
+
sx = Math::cos(lat) * Math::cos(lon)
|
144
|
+
sy = Math::cos(lat) * Math::sin(lon)
|
145
|
+
sz = Math::sin(lat)
|
146
|
+
a << [sx,sy,sz]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
a
|
151
|
+
end
|
152
|
+
|
153
|
+
# p grid(0, 3)
|
154
|
+
#
|
155
|
+
# Create triangle strips to represent a NxN mesh. The given array
|
156
|
+
# should then contain (N+1)**2 points, arranged as N+1 rows of N+1
|
157
|
+
# points.
|
158
|
+
|
159
|
+
def mesh2strips(n,a)
|
160
|
+
dx = 2.0/n
|
161
|
+
dy = 2.0/n
|
162
|
+
row = n+1
|
163
|
+
|
164
|
+
n.times do |j|
|
165
|
+
glBegin(GL_TRIANGLE_STRIP)
|
166
|
+
rowi = j*row
|
167
|
+
row.times do |x|
|
168
|
+
add_vertex(a[rowi+x])
|
169
|
+
add_vertex(a[rowi+row+x])
|
170
|
+
end
|
171
|
+
glEnd
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def add_vertex(v)
|
176
|
+
glNormal3fv(v)
|
177
|
+
glVertex3fv(v)
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
CSCShadedApp.new.run
|
data/examples/grid.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'quad_sphere/csc'
|
4
|
+
require 'chunky_png'
|
5
|
+
|
6
|
+
# Not the best way to draw a grid — we repaint a lot of pixels. But
|
7
|
+
# it's compact and hopefully clear.
|
8
|
+
|
9
|
+
# The size of each cube face, and some colours:
|
10
|
+
size = 99
|
11
|
+
colour = ChunkyPNG::Color.html_color(:grey)
|
12
|
+
background = ChunkyPNG::Color::WHITE
|
13
|
+
face_border = ChunkyPNG::Color::BLACK
|
14
|
+
|
15
|
+
# This is where we'll place each face, the usual cross.
|
16
|
+
faces = {
|
17
|
+
QuadSphere::TOP_FACE => [ 2*size, 0 ],
|
18
|
+
QuadSphere::BACK_FACE => [ 0, size ],
|
19
|
+
QuadSphere::WEST_FACE => [ size, size ],
|
20
|
+
QuadSphere::FRONT_FACE => [ 2*size, size ],
|
21
|
+
QuadSphere::EAST_FACE => [ 3*size, size ],
|
22
|
+
QuadSphere::BOTTOM_FACE => [ 2*size, 2*size ]
|
23
|
+
}
|
24
|
+
|
25
|
+
image = ChunkyPNG::Image.new(4*size+1, 3*size+1, ChunkyPNG::Color::TRANSPARENT)
|
26
|
+
|
27
|
+
# The longest distance on a cube face is the diagonal, which is of
|
28
|
+
# length d=size*√2. This is an angle of π/2 on the sphere. We split
|
29
|
+
# π/2 over d to get a small angle Δ that, along the diagonal, would
|
30
|
+
# map to pixels at unit distance from each other, and we use Δ as
|
31
|
+
# "angular resolution" everywhere, because this will keep our lines
|
32
|
+
# continuous. This is wasteful: Δ could be larger everywhere else; we
|
33
|
+
# repaint a lot of pixels. But hey, tis just an example.
|
34
|
+
delta = Math::PI/(2*size*Math::sqrt(2))
|
35
|
+
|
36
|
+
# And this is a lambda to paint a point by latitude and longitude.
|
37
|
+
# We'll do this a lot.
|
38
|
+
plot = lambda do |lon,lat|
|
39
|
+
face, x, y = QuadSphere::CSC.forward(lon, lat)
|
40
|
+
x = (size*(x+1)/2).floor
|
41
|
+
y = (size*(y+1)/2).floor
|
42
|
+
offx, offy = faces[face]
|
43
|
+
image[x+offx,y+offy] = colour
|
44
|
+
end
|
45
|
+
|
46
|
+
# To work. Draw a rectangle around each face.
|
47
|
+
faces.each_pair do |face, offsets|
|
48
|
+
off_x, off_y = offsets
|
49
|
+
image.rect(off_x, off_y, off_x+size, off_y+size, face_border, background)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Draw the meridians.
|
53
|
+
36.times do |meridian|
|
54
|
+
lon = meridian*Math::PI/18
|
55
|
+
(-Math::PI/2).step(Math::PI/2, delta) do |lat|
|
56
|
+
plot.call(lon,lat)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Draw the parallels.
|
61
|
+
18.times do |parallel|
|
62
|
+
lat = -Math::PI/2 + parallel*Math::PI/18
|
63
|
+
(-Math::PI).step(Math::PI, delta) do |lon|
|
64
|
+
plot.call(lon,lat)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# There done, save our masterpiece.
|
69
|
+
image.save('grid.png', :best_compression)
|
data/lib/quad_sphere/csc.rb
CHANGED
@@ -165,7 +165,7 @@ module QuadSphere
|
|
165
165
|
# approximation there, don't quote me on those figures). So aye,
|
166
166
|
# we could use more precision.
|
167
167
|
#
|
168
|
-
# There is code in the +
|
168
|
+
# There is code in the +examples+ directory to compute the error
|
169
169
|
# distribution, and even generate a picture showing the areas of
|
170
170
|
# low and high error on the plane. Peek there if you're curious.
|
171
171
|
#
|
data/quad_sphere.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding: utf-8; mode: ruby -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'quad_sphere'
|
5
|
+
s.version = '1.0.0'
|
6
|
+
s.date = '2013-02-04'
|
7
|
+
s.summary = 'Quadrilateralised spherical cube projection'
|
8
|
+
s.description = <<END
|
9
|
+
An implementation of the quadrilateralised spherical cube, an
|
10
|
+
approximately equal-area projection of the sphere onto the faces of a
|
11
|
+
cube. It is useful for storing data collected on a spherical surface,
|
12
|
+
and for general mapmaking.
|
13
|
+
END
|
14
|
+
s.homepage = 'https://github.com/crinc/QuadSphere'
|
15
|
+
s.author = 'Cesar Rincon'
|
16
|
+
s.email = 'crincon@gmail.com'
|
17
|
+
s.files = [ 'README.md',
|
18
|
+
'COPYING',
|
19
|
+
'quad_sphere.gemspec',
|
20
|
+
'examples/binning.rb',
|
21
|
+
'examples/distort_closure.rb',
|
22
|
+
'examples/gl1.rb',
|
23
|
+
'examples/gl2.rb',
|
24
|
+
'examples/grid.rb',
|
25
|
+
'lib/quad_sphere.rb',
|
26
|
+
'lib/quad_sphere/tangential.rb',
|
27
|
+
'lib/quad_sphere/csc.rb',
|
28
|
+
'lib/quad_sphere/version.rb' ]
|
29
|
+
s.test_files = [ 'test/test_quad_sphere.rb' ]
|
30
|
+
end
|
@@ -0,0 +1,860 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'quad_sphere'
|
5
|
+
require 'quad_sphere/tangential'
|
6
|
+
require 'quad_sphere/csc'
|
7
|
+
|
8
|
+
module MappingTests
|
9
|
+
# Some constants to save typing (not that I typed all this heh)
|
10
|
+
PI = Math::PI
|
11
|
+
|
12
|
+
TOP_FACE = QuadSphere::TOP_FACE
|
13
|
+
FRONT_FACE = QuadSphere::FRONT_FACE
|
14
|
+
EAST_FACE = QuadSphere::EAST_FACE
|
15
|
+
BACK_FACE = QuadSphere::BACK_FACE
|
16
|
+
WEST_FACE = QuadSphere::WEST_FACE
|
17
|
+
BOTTOM_FACE = QuadSphere::BOTTOM_FACE
|
18
|
+
|
19
|
+
# This is the floating point round-off error expected to be
|
20
|
+
# introduced by our computations. Most of our tangential
|
21
|
+
# projections are exact to the unit roundoff given by Ruby,
|
22
|
+
# actually. It's only a couple that are above one epsilon. But in
|
23
|
+
# any case, just take from this the tangential projection is
|
24
|
+
# accurate to 15 digits...
|
25
|
+
#
|
26
|
+
# Float::EPSILON = 2.220446049250313e-16 on my MacBook, ruby
|
27
|
+
# 1.9.3p374 (2013-01-15 revision 38858) [x86_64-darwin11.4.2]
|
28
|
+
DELTA = 2*Float::EPSILON
|
29
|
+
|
30
|
+
# This is a small value we use to test rollover to the neighbouring
|
31
|
+
# face, in the forward projection.
|
32
|
+
NUDGE = Float::EPSILON
|
33
|
+
|
34
|
+
# Picture a sphere of unit radius inscribed in a cube, with the
|
35
|
+
# centre of the sphere at the origin of the cartesian space, and
|
36
|
+
# with the cube oriented so that each coordinate axis passes through
|
37
|
+
# the centres of two opposite cube faces.
|
38
|
+
#
|
39
|
+
# The sides of this cube are then of length 2 (twice the sphere's
|
40
|
+
# radius), thus the diagonal of the cube is of length 2*sqrt(3). It
|
41
|
+
# follows that, at each corner of the cube, the distance from the
|
42
|
+
# corner vertex to the horizontal plane is 1 (half the length of a
|
43
|
+
# cube side), and the distance from the vertex to the origin is
|
44
|
+
# sqrt(3) (half the length of the cube diagonal). It further
|
45
|
+
# follows that the sine of the elevation angle to the corners of the
|
46
|
+
# cube is 1/sqrt(3).
|
47
|
+
#
|
48
|
+
# So the four northern corners of the cube are at latitude
|
49
|
+
# θc = arcsin(1/sqrt(3)); and the southern corners at latitude -θc.
|
50
|
+
#
|
51
|
+
# I don't know is there is a way to write this angle in terms of π
|
52
|
+
# or roots or something. Would be cute.
|
53
|
+
THETA_C = Math::asin(1/Math::sqrt(3))
|
54
|
+
|
55
|
+
def test_forward_mapping
|
56
|
+
p = self.projection
|
57
|
+
|
58
|
+
# We'll test the mappings of 26 specific points around the sphere.
|
59
|
+
|
60
|
+
# First, the one north pole.
|
61
|
+
|
62
|
+
# 1. θ = π/2 (90°N) should map to the centre point of the top
|
63
|
+
# face. φ is irrelevant here.
|
64
|
+
face, x, y = p.forward(0.0, PI/2)
|
65
|
+
assert_equal(TOP_FACE, face)
|
66
|
+
assert_in_delta( 0.0, x, DELTA)
|
67
|
+
assert_in_delta( 0.0, y, DELTA)
|
68
|
+
|
69
|
+
# Then the four middle points of the edges of the top cube face.
|
70
|
+
# These are all at θ = π/4 (45°N).
|
71
|
+
|
72
|
+
# 2. φ = -π/2, θ = π/4 (45°N 90°W) should map to the left point of
|
73
|
+
# the top face, or the up point of the west face. When
|
74
|
+
# coordinates are given exactly like that, this implementation
|
75
|
+
# maps to the west face.
|
76
|
+
face, x, y = p.forward(-PI/2, PI/4)
|
77
|
+
assert_equal(WEST_FACE, face)
|
78
|
+
assert_in_delta( 0.0, x, DELTA)
|
79
|
+
assert_in_delta( 1.0, y, DELTA)
|
80
|
+
# A nudge northward, though, and we're on the top face.
|
81
|
+
face, x, y = p.forward(-PI/2, PI/4+NUDGE)
|
82
|
+
assert_equal(TOP_FACE, face)
|
83
|
+
assert_in_delta(-1.0+NUDGE, x, DELTA)
|
84
|
+
assert_in_delta( 0.0, y, DELTA)
|
85
|
+
|
86
|
+
# 3. φ = 0, θ = π/4 (45°N 0°) should map to the down point of the
|
87
|
+
# top face, or the up point of the front face. When coordinates
|
88
|
+
# are given exactly like that, this implementation maps to the
|
89
|
+
# front face.
|
90
|
+
face, x, y = p.forward(0.0, PI/4)
|
91
|
+
assert_equal(FRONT_FACE, face)
|
92
|
+
assert_in_delta( 0.0, x, DELTA)
|
93
|
+
assert_in_delta( 1.0, y, DELTA)
|
94
|
+
# A nudge northward, though, and we're on the top face.
|
95
|
+
face, x, y = p.forward(0.0, PI/4+NUDGE)
|
96
|
+
assert_equal(TOP_FACE, face)
|
97
|
+
assert_in_delta( 0.0, x, DELTA)
|
98
|
+
assert_in_delta(-1.0+NUDGE, y, DELTA)
|
99
|
+
|
100
|
+
# 4. φ = π/2, θ = π/4 (45°N 90°E) should map to the right point of
|
101
|
+
# the top face, or the up point of the east face. When
|
102
|
+
# coordinates are given exactly like that, this implementation
|
103
|
+
# maps to the east face.
|
104
|
+
face, x, y = p.forward(PI/2, PI/4)
|
105
|
+
assert_equal(EAST_FACE, face)
|
106
|
+
assert_in_delta( 0.0, x, DELTA)
|
107
|
+
assert_in_delta( 1.0, y, DELTA)
|
108
|
+
# A nudge northward, though, and we're on the top face.
|
109
|
+
face, x, y = p.forward(PI/2, PI/4+NUDGE)
|
110
|
+
assert_equal(TOP_FACE, face)
|
111
|
+
assert_in_delta( 1.0-NUDGE, x, DELTA)
|
112
|
+
assert_in_delta( 0.0, y, DELTA)
|
113
|
+
|
114
|
+
# 5. φ = π, θ = π/4 (45°N 180°) should map to the up point of the
|
115
|
+
# top face, or the up point of the back face. When coordinates
|
116
|
+
# are given exactly like that, this implementation maps to the
|
117
|
+
# back face.
|
118
|
+
face, x, y = p.forward(PI, PI/4)
|
119
|
+
assert_equal(BACK_FACE, face)
|
120
|
+
assert_in_delta( 0.0, x, DELTA)
|
121
|
+
assert_in_delta( 1.0, y, DELTA)
|
122
|
+
# A nudge northward, though, and we're on the top face.
|
123
|
+
face, x, y = p.forward(PI, PI/4+NUDGE)
|
124
|
+
assert_equal(TOP_FACE, face)
|
125
|
+
assert_in_delta( 0.0, x, DELTA)
|
126
|
+
assert_in_delta( 1.0-NUDGE, y, DELTA)
|
127
|
+
|
128
|
+
# Then the four corners of the top cube face. These are all at
|
129
|
+
# θ = θc = arcsin(1/√3) -- about 35.26°N.
|
130
|
+
|
131
|
+
# 6. φ = -3π/4, θ = θc (35.26°N 135°W) should map to the up-left
|
132
|
+
# point of the top face, or the up-right point of the back face,
|
133
|
+
# or the up-left point of the west face. When coordinates are
|
134
|
+
# given exactly like that, this implementation maps to the top
|
135
|
+
# face.
|
136
|
+
face, x, y = p.forward(-3*PI/4, THETA_C)
|
137
|
+
assert_equal(TOP_FACE, face)
|
138
|
+
assert_in_delta(-1.0, x, DELTA)
|
139
|
+
assert_in_delta( 1.0, y, DELTA)
|
140
|
+
# A nudge westward, though, and we're on the back face.
|
141
|
+
face, x, y = p.forward(-3*PI/4-2*NUDGE, THETA_C)
|
142
|
+
assert_equal(BACK_FACE, face)
|
143
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
144
|
+
assert_in_delta( 1.0, y, DELTA)
|
145
|
+
# Or a nudge eastward, and we're on the west face.
|
146
|
+
face, x, y = p.forward(-3*PI/4+2*NUDGE, THETA_C)
|
147
|
+
assert_equal(WEST_FACE, face)
|
148
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
149
|
+
assert_in_delta( 1.0, y, DELTA)
|
150
|
+
|
151
|
+
# 7. φ = -π/4, θ = θc (35.26°N 45°W) should map to the down-left
|
152
|
+
# point of the top face, or the up-left point of the front face,
|
153
|
+
# or the up-right point of the west face. When coordinates are
|
154
|
+
# given exactly like that, this implementation maps to the top
|
155
|
+
# face.
|
156
|
+
face, x, y = p.forward(-PI/4, THETA_C)
|
157
|
+
assert_equal(TOP_FACE, face)
|
158
|
+
assert_in_delta(-1.0, x, DELTA)
|
159
|
+
assert_in_delta(-1.0, y, DELTA)
|
160
|
+
# A nudge westward, though, and we're on the west face.
|
161
|
+
face, x, y = p.forward(-PI/4-2*NUDGE, THETA_C)
|
162
|
+
assert_equal(WEST_FACE, face)
|
163
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
164
|
+
assert_in_delta( 1.0, y, DELTA)
|
165
|
+
# Or a nudge eastward, and we're on the front face.
|
166
|
+
face, x, y = p.forward(-PI/4+2*NUDGE, THETA_C)
|
167
|
+
assert_equal(FRONT_FACE, face)
|
168
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
169
|
+
assert_in_delta( 1.0, y, DELTA)
|
170
|
+
|
171
|
+
# 8. φ = π/4, θ = θc (35.26°N 45°E) should map to the down-right
|
172
|
+
# point of the top face, or the up-right point of the front face,
|
173
|
+
# or the up-left point of the east face. When coordinates are
|
174
|
+
# given exactly like that, this implementation maps to the top
|
175
|
+
# face.
|
176
|
+
face, x, y = p.forward(PI/4, THETA_C)
|
177
|
+
assert_equal(TOP_FACE, face)
|
178
|
+
assert_in_delta( 1.0, x, DELTA)
|
179
|
+
assert_in_delta(-1.0, y, DELTA)
|
180
|
+
# A nudge westward, though, and we're on the front face.
|
181
|
+
face, x, y = p.forward(PI/4-2*NUDGE, THETA_C)
|
182
|
+
assert_equal(FRONT_FACE, face)
|
183
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
184
|
+
assert_in_delta( 1.0, y, DELTA)
|
185
|
+
# Or a nudge eastward, and we're on the east face.
|
186
|
+
face, x, y = p.forward(PI/4+2*NUDGE, THETA_C)
|
187
|
+
assert_equal(EAST_FACE, face)
|
188
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
189
|
+
assert_in_delta( 1.0, y, DELTA)
|
190
|
+
|
191
|
+
# 9. φ = 3π/4, θ = θc (35.26°N 135°E) should map to the up-right
|
192
|
+
# point of the top face, or the up-right point of the east face,
|
193
|
+
# or the up-left point of the back face. When coordinates are
|
194
|
+
# given exactly like that, this implementation maps to the top
|
195
|
+
# face.
|
196
|
+
face, x, y = p.forward(3*PI/4, THETA_C)
|
197
|
+
assert_equal(TOP_FACE, face)
|
198
|
+
assert_in_delta( 1.0, x, DELTA)
|
199
|
+
assert_in_delta( 1.0, y, DELTA)
|
200
|
+
# A nudge westward, though, and we're on the east face.
|
201
|
+
face, x, y = p.forward(3*PI/4-2*NUDGE, THETA_C)
|
202
|
+
assert_equal(EAST_FACE, face)
|
203
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
204
|
+
assert_in_delta( 1.0, y, DELTA)
|
205
|
+
# Or a nudge eastward, and we're on the back face.
|
206
|
+
face, x, y = p.forward(3*PI/4+2*NUDGE, THETA_C)
|
207
|
+
assert_equal(BACK_FACE, face)
|
208
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
209
|
+
assert_in_delta( 1.0, y, DELTA)
|
210
|
+
|
211
|
+
# Then the "equatorial belt", eight points, being the four centres
|
212
|
+
# of the front, east, back and west faces, and the the midpoints
|
213
|
+
# of the vertical edges of these faces. These eight points are at
|
214
|
+
# θ = zero, of course.
|
215
|
+
|
216
|
+
# 10. φ = -3π/4, θ = 0 (0° 135°W) should map to the right point of
|
217
|
+
# the back face, or the left point of the west face. When
|
218
|
+
# coordinates are given exactly like that, this implementation
|
219
|
+
# maps to the west face.
|
220
|
+
face, x, y = p.forward(-3*PI/4, 0.0)
|
221
|
+
assert_equal(WEST_FACE, face)
|
222
|
+
assert_in_delta(-1.0, x, DELTA)
|
223
|
+
assert_in_delta( 0.0, y, DELTA)
|
224
|
+
# A nudge westward, though, and we're on the back face.
|
225
|
+
face, x, y = p.forward(-3*PI/4-2*NUDGE, 0.0)
|
226
|
+
assert_equal(BACK_FACE, face)
|
227
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
228
|
+
assert_in_delta( 0.0, y, DELTA)
|
229
|
+
|
230
|
+
# 11. φ = -π/2, θ = 0 (0° 90°W) should map to the centre point of
|
231
|
+
# the west face.
|
232
|
+
face, x, y = p.forward(-PI/2, 0.0)
|
233
|
+
assert_equal(WEST_FACE, face)
|
234
|
+
assert_in_delta( 0.0, x, DELTA)
|
235
|
+
assert_in_delta( 0.0, y, DELTA)
|
236
|
+
|
237
|
+
# 12. φ = -π/4, θ = 0 (0° 45°W) should map to the left point of
|
238
|
+
# the front face, or the right point of the west face. When
|
239
|
+
# coordinates are given exactly like that, this implementation
|
240
|
+
# maps to the front face.
|
241
|
+
face, x, y = p.forward(-PI/4, 0.0)
|
242
|
+
assert_equal(FRONT_FACE, face)
|
243
|
+
assert_in_delta(-1.0, x, DELTA)
|
244
|
+
assert_in_delta( 0.0, y, DELTA)
|
245
|
+
# A nudge westward, though, and we're on the XXX face.
|
246
|
+
face, x, y = p.forward(-PI/4-2*NUDGE, 0.0)
|
247
|
+
assert_equal(WEST_FACE, face)
|
248
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
249
|
+
assert_in_delta( 0.0, y, DELTA)
|
250
|
+
|
251
|
+
# 13. φ = 0, θ = 0 (0° 0°) should map to the centre point of the
|
252
|
+
# front face.
|
253
|
+
face, x, y = p.forward(0.0, 0.0)
|
254
|
+
assert_equal(FRONT_FACE, face)
|
255
|
+
assert_in_delta( 0.0, x, DELTA)
|
256
|
+
assert_in_delta( 0.0, y, DELTA)
|
257
|
+
|
258
|
+
# 14. φ = π/4, θ = 0 (0° 45°E) should map to the right point of
|
259
|
+
# the front face, or the left point of the east face. When
|
260
|
+
# coordinates are given exactly like that, this implementation
|
261
|
+
# maps to the front face.
|
262
|
+
face, x, y = p.forward(PI/4, 0.0)
|
263
|
+
assert_equal(FRONT_FACE, face)
|
264
|
+
assert_in_delta( 1.0, x, DELTA)
|
265
|
+
assert_in_delta( 0.0, y, DELTA)
|
266
|
+
# A nudge eastward, though, and we're on the east face.
|
267
|
+
face, x, y = p.forward(PI/4+2*NUDGE, 0.0)
|
268
|
+
assert_equal(EAST_FACE, face)
|
269
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
270
|
+
assert_in_delta( 0.0, y, DELTA)
|
271
|
+
|
272
|
+
# 15. φ = π/2, θ = 0 (0° 90°E) should map to the centre point of
|
273
|
+
# the east face.
|
274
|
+
face, x, y = p.forward(PI/2, 0.0)
|
275
|
+
assert_equal(EAST_FACE, face)
|
276
|
+
assert_in_delta( 0.0, x, DELTA)
|
277
|
+
assert_in_delta( 0.0, y, DELTA)
|
278
|
+
|
279
|
+
# 16. φ = 3π/4, θ = 0 (0° 135°E) should map to the right point of
|
280
|
+
# the east face, or the left point of the back face. When
|
281
|
+
# coordinates are given exactly like that, this implementation
|
282
|
+
# maps to the east face.
|
283
|
+
face, x, y = p.forward(3*PI/4, 0.0)
|
284
|
+
assert_equal(EAST_FACE, face)
|
285
|
+
assert_in_delta( 1.0, x, DELTA)
|
286
|
+
assert_in_delta( 0.0, y, DELTA)
|
287
|
+
# A nudge eastward, though, and we're on the back face.
|
288
|
+
face, x, y = p.forward(3*PI/4+2*NUDGE, 0.0)
|
289
|
+
assert_equal(BACK_FACE, face)
|
290
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
291
|
+
assert_in_delta( 0.0, y, DELTA)
|
292
|
+
|
293
|
+
# 17. φ = π, θ = 0 (0° 180°) should map to the centre point of the
|
294
|
+
# back face.
|
295
|
+
face, x, y = p.forward(PI, 0.0)
|
296
|
+
assert_equal(BACK_FACE, face)
|
297
|
+
assert_in_delta( 0.0, x, DELTA)
|
298
|
+
assert_in_delta( 0.0, y, DELTA)
|
299
|
+
|
300
|
+
# Then the four corners of the bottom cube face. These are all at
|
301
|
+
# θ = -θc = -arcsin(1/√3) -- about 35.26°S.
|
302
|
+
|
303
|
+
# 18. φ = -3π/4, θ = -θc (35.26°S 135°W) should map to the
|
304
|
+
# down-right point of the back face, or the down-left point of the
|
305
|
+
# west face, or the down-left point of the bottom face. When
|
306
|
+
# coordinates are given exactly like that, this implementation
|
307
|
+
# maps to the bottom face.
|
308
|
+
face, x, y = p.forward(-3*PI/4, -THETA_C)
|
309
|
+
assert_equal(BOTTOM_FACE, face)
|
310
|
+
assert_in_delta(-1.0, x, DELTA)
|
311
|
+
assert_in_delta(-1.0, y, DELTA)
|
312
|
+
# A nudge westward, though, and we're on the back face.
|
313
|
+
face, x, y = p.forward(-3*PI/4-2*NUDGE, -THETA_C)
|
314
|
+
assert_equal(BACK_FACE, face)
|
315
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
316
|
+
assert_in_delta(-1.0, y, DELTA)
|
317
|
+
# Or a nudge eastward, and we're on the west face.
|
318
|
+
face, x, y = p.forward(-3*PI/4+2*NUDGE, -THETA_C)
|
319
|
+
assert_equal(WEST_FACE, face)
|
320
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
321
|
+
assert_in_delta(-1.0, y, DELTA)
|
322
|
+
|
323
|
+
# 19. φ = -π/4, θ = -θc (35.26°S 45°W) should map to the down-left
|
324
|
+
# point of the front face, or the down-right point of the west
|
325
|
+
# face, or the up-left point of the bottom face. When coordinates
|
326
|
+
# are given exactly like that, this implementation maps to the
|
327
|
+
# bottom face.
|
328
|
+
face, x, y = p.forward(-PI/4, -THETA_C)
|
329
|
+
assert_equal(BOTTOM_FACE, face)
|
330
|
+
assert_in_delta(-1.0, x, DELTA)
|
331
|
+
assert_in_delta( 1.0, y, DELTA)
|
332
|
+
# A nudge westward, though, and we're on the west face.
|
333
|
+
face, x, y = p.forward(-PI/4-2*NUDGE, -THETA_C)
|
334
|
+
assert_equal(WEST_FACE, face)
|
335
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
336
|
+
assert_in_delta(-1.0, y, DELTA)
|
337
|
+
# Or a nudge eastward, and we're on the front face.
|
338
|
+
face, x, y = p.forward(-PI/4+2*NUDGE, -THETA_C)
|
339
|
+
assert_equal(FRONT_FACE, face)
|
340
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
341
|
+
assert_in_delta(-1.0, y, DELTA)
|
342
|
+
|
343
|
+
# 20. φ = π/4, θ = -θc (35.26°S 45°E) should map to the down-right
|
344
|
+
# point of the front face, or the down-left point of the east
|
345
|
+
# face, or the up-right point of the bottom face. When
|
346
|
+
# coordinates are given exactly like that, this implementation
|
347
|
+
# maps to the bottom face.
|
348
|
+
face, x, y = p.forward(PI/4, -THETA_C)
|
349
|
+
assert_equal(BOTTOM_FACE, face)
|
350
|
+
assert_in_delta( 1.0, x, DELTA)
|
351
|
+
assert_in_delta( 1.0, y, DELTA)
|
352
|
+
# A nudge westward, though, and we're on the front face.
|
353
|
+
face, x, y = p.forward(PI/4-2*NUDGE, -THETA_C)
|
354
|
+
assert_equal(FRONT_FACE, face)
|
355
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
356
|
+
assert_in_delta(-1.0, y, DELTA)
|
357
|
+
# Or a nudge eastward, and we're on the east face.
|
358
|
+
face, x, y = p.forward(PI/4+2*NUDGE, -THETA_C)
|
359
|
+
assert_equal(EAST_FACE, face)
|
360
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
361
|
+
assert_in_delta(-1.0, y, DELTA)
|
362
|
+
|
363
|
+
# 21. φ = 3π/4, θ = -θc (35.26°S 135°E) should map to the
|
364
|
+
# down-right point of the east face, or the down-left point of the
|
365
|
+
# back face, or the down-right point of the bottom face. When
|
366
|
+
# coordinates are given exactly like that, this implementation
|
367
|
+
# maps to the bottom face.
|
368
|
+
face, x, y = p.forward(3*PI/4, -THETA_C)
|
369
|
+
assert_equal(BOTTOM_FACE, face)
|
370
|
+
assert_in_delta( 1.0, x, DELTA)
|
371
|
+
assert_in_delta(-1.0, y, DELTA)
|
372
|
+
# A nudge westward, though, and we're on the east face.
|
373
|
+
face, x, y = p.forward(3*PI/4-2*NUDGE, -THETA_C)
|
374
|
+
assert_equal(EAST_FACE, face)
|
375
|
+
assert_in_delta( 1.0-3*NUDGE, x, DELTA)
|
376
|
+
assert_in_delta(-1.0, y, DELTA)
|
377
|
+
# Or a nudge eastward, and we're on the back face.
|
378
|
+
face, x, y = p.forward(3*PI/4+2*NUDGE, -THETA_C)
|
379
|
+
assert_equal(BACK_FACE, face)
|
380
|
+
assert_in_delta(-1.0+3*NUDGE, x, DELTA)
|
381
|
+
assert_in_delta(-1.0, y, DELTA)
|
382
|
+
|
383
|
+
# Then the four middle points of the edges of the bottom cube
|
384
|
+
# face. These are all at θ = -π/4 (45°S).
|
385
|
+
|
386
|
+
# 22. φ = -π/2, θ = -π/4 (45°S 90°W) should map to the down point
|
387
|
+
# of the west face, or the left point of the bottom face. When
|
388
|
+
# coordinates are given exactly like that, this implementation
|
389
|
+
# maps to the west face.
|
390
|
+
face, x, y = p.forward(-PI/2, -PI/4)
|
391
|
+
assert_equal(WEST_FACE, face)
|
392
|
+
assert_in_delta( 0.0, x, DELTA)
|
393
|
+
assert_in_delta(-1.0, y, DELTA)
|
394
|
+
# A nudge southward, though, and we're on the bottom face.
|
395
|
+
face, x, y = p.forward(-PI/2, -PI/4-NUDGE)
|
396
|
+
assert_equal(BOTTOM_FACE, face)
|
397
|
+
assert_in_delta(-1.0+NUDGE, x, DELTA)
|
398
|
+
assert_in_delta( 0.0, y, DELTA)
|
399
|
+
|
400
|
+
# 23. φ = 0, θ = -π/4 (45°S 0°) should map to the down point of
|
401
|
+
# the front face, or the up point of the bottom face. When
|
402
|
+
# coordinates are given exactly like that, this implementation
|
403
|
+
# maps to the FRONT face.
|
404
|
+
face, x, y = p.forward(0.0, -PI/4)
|
405
|
+
assert_equal(FRONT_FACE, face)
|
406
|
+
assert_in_delta( 0.0, x, DELTA)
|
407
|
+
assert_in_delta(-1.0, y, DELTA)
|
408
|
+
# A nudge southward, though, and we're on the bottom face.
|
409
|
+
face, x, y = p.forward(0.0, -PI/4-NUDGE)
|
410
|
+
assert_equal(BOTTOM_FACE, face)
|
411
|
+
assert_in_delta( 0.0, x, DELTA)
|
412
|
+
assert_in_delta( 1.0-NUDGE, y, DELTA)
|
413
|
+
|
414
|
+
# 24. φ = π/2, θ = -π/4 (45°S 90°E) should map to the down point
|
415
|
+
# of the east face, or the right point of the bottom face. When
|
416
|
+
# coordinates are given exactly like that, this implementation
|
417
|
+
# maps to the east face.
|
418
|
+
face, x, y = p.forward(PI/2, -PI/4)
|
419
|
+
assert_equal(EAST_FACE, face)
|
420
|
+
assert_in_delta( 0.0, x, DELTA)
|
421
|
+
assert_in_delta(-1.0, y, DELTA)
|
422
|
+
# A nudge southward, though, and we're on the bottom face.
|
423
|
+
face, x, y = p.forward(PI/2, -PI/4-NUDGE)
|
424
|
+
assert_equal(BOTTOM_FACE, face)
|
425
|
+
assert_in_delta( 1.0-NUDGE, x, DELTA)
|
426
|
+
assert_in_delta( 0.0, y, DELTA)
|
427
|
+
|
428
|
+
# 25. φ = π, θ = -π/4 (45°S 180°) should map to the down point of
|
429
|
+
# the back face, or the down point of the bottom face. When
|
430
|
+
# coordinates are given exactly like that, this implementation
|
431
|
+
# maps to the back face.
|
432
|
+
face, x, y = p.forward(PI, -PI/4)
|
433
|
+
assert_equal(BACK_FACE, face)
|
434
|
+
assert_in_delta( 0.0, x, DELTA)
|
435
|
+
assert_in_delta(-1.0, y, DELTA)
|
436
|
+
# A nudge southward, though, and we're on the bottom face.
|
437
|
+
face, x, y = p.forward(PI, -PI/4-NUDGE)
|
438
|
+
assert_equal(BOTTOM_FACE, face)
|
439
|
+
assert_in_delta( 0.0, x, DELTA)
|
440
|
+
assert_in_delta(-1.0+NUDGE, y, DELTA)
|
441
|
+
|
442
|
+
# Finally, the one south pole.
|
443
|
+
|
444
|
+
# 26. θ = -π/2 (90°S ) should map to the centre point of the
|
445
|
+
# bottom face. φ is irrelevant here.
|
446
|
+
|
447
|
+
face, x, y = p.forward(0.0, -PI/2)
|
448
|
+
assert_equal(BOTTOM_FACE, face)
|
449
|
+
assert_in_delta( 0.0, x, DELTA)
|
450
|
+
assert_in_delta( 0.0, y, DELTA)
|
451
|
+
|
452
|
+
end # test_forward_mapping
|
453
|
+
|
454
|
+
|
455
|
+
def test_inverse_mapping
|
456
|
+
p = self.projection
|
457
|
+
|
458
|
+
# We'll test nine points on each cube face: the four corners, the
|
459
|
+
# four centres of each edge, and the centre of the face. Please
|
460
|
+
# note that, except for the face centres, the mappings are not
|
461
|
+
# necessarily reversible. This is because, for any point that is
|
462
|
+
# shared amongst two or three faces, the forward projection may
|
463
|
+
# choose to return a mapping to any of the shared faces. See all
|
464
|
+
# those nudges in the forward mapping tests above.
|
465
|
+
|
466
|
+
# 1. The top face.
|
467
|
+
|
468
|
+
# The up-left point of the top face should map to
|
469
|
+
# φ = -3π/4, θ = θc.
|
470
|
+
phi, theta = p.inverse(TOP_FACE, -1.0, 1.0)
|
471
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
472
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
473
|
+
|
474
|
+
# The up point of the top face should map to
|
475
|
+
# φ = π, θ = π/4.
|
476
|
+
phi, theta = p.inverse(TOP_FACE, 0.0, 1.0)
|
477
|
+
assert_in_delta(PI, phi, DELTA)
|
478
|
+
assert_in_delta(PI/4, theta, DELTA)
|
479
|
+
|
480
|
+
# The up-right point of the top face should map to
|
481
|
+
# φ = 3π/4, θ = θc.
|
482
|
+
phi, theta = p.inverse(TOP_FACE, 1.0, 1.0)
|
483
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
484
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
485
|
+
|
486
|
+
# The left point of the top face should map to
|
487
|
+
# φ = -π/2, θ = π/4.
|
488
|
+
phi, theta = p.inverse(TOP_FACE, -1.0, 0.0)
|
489
|
+
assert_in_delta(-PI/2, phi, DELTA)
|
490
|
+
assert_in_delta(PI/4, theta, DELTA)
|
491
|
+
|
492
|
+
# The centre point of the top face should map to
|
493
|
+
# θ = π/2, with φ being irrelevant.
|
494
|
+
phi, theta = p.inverse(TOP_FACE, 0.0, 0.0)
|
495
|
+
assert_in_delta(PI/2, theta, DELTA)
|
496
|
+
# So here, any value is correct. This implementation returns
|
497
|
+
# PI, though, for some reason, and we'll watch for changes.
|
498
|
+
assert_in_delta(PI, phi, DELTA)
|
499
|
+
|
500
|
+
# The right point of the top face should map to
|
501
|
+
# φ = π/2, θ = π/4.
|
502
|
+
phi, theta = p.inverse(TOP_FACE, 1.0, 0.0)
|
503
|
+
assert_in_delta(PI/2, phi, DELTA)
|
504
|
+
assert_in_delta(PI/4, theta, DELTA)
|
505
|
+
|
506
|
+
# The down-left point of the top face should map to
|
507
|
+
# φ = -π/4, θ = θc.
|
508
|
+
phi, theta = p.inverse(TOP_FACE, -1.0, -1.0)
|
509
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
510
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
511
|
+
|
512
|
+
# The down point of the top face should map to
|
513
|
+
# φ = 0, θ = π/4.
|
514
|
+
phi, theta = p.inverse(TOP_FACE, 0.0, -1.0)
|
515
|
+
assert_in_delta(0.0, phi, DELTA)
|
516
|
+
assert_in_delta(PI/4, theta, DELTA)
|
517
|
+
|
518
|
+
# The down-right point of the top face should map to
|
519
|
+
# φ = π/4, θ = θc.
|
520
|
+
phi, theta = p.inverse(TOP_FACE, 1.0, -1.0)
|
521
|
+
assert_in_delta(PI/4, phi, DELTA)
|
522
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
523
|
+
|
524
|
+
# 2. The front face.
|
525
|
+
|
526
|
+
# The up-left point of the front face should map to
|
527
|
+
# φ = -π/4, θ = θc.
|
528
|
+
phi, theta = p.inverse(FRONT_FACE, -1.0, 1.0)
|
529
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
530
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
531
|
+
|
532
|
+
# The up point of the front face should map to
|
533
|
+
# φ = 0, θ = π/4.
|
534
|
+
phi, theta = p.inverse(FRONT_FACE, 0.0, 1.0)
|
535
|
+
assert_in_delta(0.0, phi, DELTA)
|
536
|
+
assert_in_delta(PI/4, theta, DELTA)
|
537
|
+
|
538
|
+
# The up-right point of the front face should map to
|
539
|
+
# φ = π/4, θ = θc.
|
540
|
+
phi, theta = p.inverse(FRONT_FACE, 1.0, 1.0)
|
541
|
+
assert_in_delta(PI/4, phi, DELTA)
|
542
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
543
|
+
|
544
|
+
# The left point of the front face should map to
|
545
|
+
# φ = -π/4, θ = 0.
|
546
|
+
phi, theta = p.inverse(FRONT_FACE, -1.0, 0.0)
|
547
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
548
|
+
assert_in_delta(0.0, theta, DELTA)
|
549
|
+
|
550
|
+
# The centre point of the front face should map to
|
551
|
+
# φ = 0, θ = 0.
|
552
|
+
phi, theta = p.inverse(FRONT_FACE, 0.0, 0.0)
|
553
|
+
assert_in_delta(0.0, phi, DELTA)
|
554
|
+
assert_in_delta(0.0, theta, DELTA)
|
555
|
+
|
556
|
+
# The right point of the front face should map to
|
557
|
+
# φ = π/4, θ = 0.
|
558
|
+
phi, theta = p.inverse(FRONT_FACE, 1.0, 0.0)
|
559
|
+
assert_in_delta(PI/4, phi, DELTA)
|
560
|
+
assert_in_delta(0.0, theta, DELTA)
|
561
|
+
|
562
|
+
# The down-left point of the front face should map to
|
563
|
+
# φ = -π/4, θ = -θc.
|
564
|
+
phi, theta = p.inverse(FRONT_FACE, -1.0, -1.0)
|
565
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
566
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
567
|
+
|
568
|
+
# The down point of the front face should map to
|
569
|
+
# φ = 0, θ = -π/4.
|
570
|
+
phi, theta = p.inverse(FRONT_FACE, 0.0, -1.0)
|
571
|
+
assert_in_delta(0.0, phi, DELTA)
|
572
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
573
|
+
|
574
|
+
# The down-right point of the front face should map to
|
575
|
+
# φ = π/4, θ = -θc.
|
576
|
+
phi, theta = p.inverse(FRONT_FACE, 1.0, -1.0)
|
577
|
+
assert_in_delta(PI/4, phi, DELTA)
|
578
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
579
|
+
|
580
|
+
# 3. The east face.
|
581
|
+
|
582
|
+
# The up-left point of the east face should map to
|
583
|
+
# φ = π/4, θ = θc.
|
584
|
+
phi, theta = p.inverse(EAST_FACE, -1.0, 1.0)
|
585
|
+
assert_in_delta(PI/4, phi, DELTA)
|
586
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
587
|
+
|
588
|
+
# The up point of the east face should map to
|
589
|
+
# φ = π/2, θ = π/4.
|
590
|
+
phi, theta = p.inverse(EAST_FACE, 0.0, 1.0)
|
591
|
+
assert_in_delta(PI/2, phi, DELTA)
|
592
|
+
assert_in_delta(PI/4, theta, DELTA)
|
593
|
+
|
594
|
+
# The up-right point of the east face should map to
|
595
|
+
# φ = 3π/4, θ = θc.
|
596
|
+
phi, theta = p.inverse(EAST_FACE, 1.0, 1.0)
|
597
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
598
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
599
|
+
|
600
|
+
# The left point of the east face should map to
|
601
|
+
# φ = π/4, θ = 0.
|
602
|
+
phi, theta = p.inverse(EAST_FACE, -1.0, 0.0)
|
603
|
+
assert_in_delta(PI/4, phi, DELTA)
|
604
|
+
assert_in_delta(0.0, theta, DELTA)
|
605
|
+
|
606
|
+
# The centre point of the east face should map to
|
607
|
+
# φ = π/2, θ = 0.
|
608
|
+
phi, theta = p.inverse(EAST_FACE, 0.0, 0.0)
|
609
|
+
assert_in_delta(PI/2, phi, DELTA)
|
610
|
+
assert_in_delta(0.0, theta, DELTA)
|
611
|
+
|
612
|
+
# The right point of the east face should map to
|
613
|
+
# φ = 3π/4, θ = 0.
|
614
|
+
phi, theta = p.inverse(EAST_FACE, 1.0, 0.0)
|
615
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
616
|
+
assert_in_delta(0.0, theta, DELTA)
|
617
|
+
|
618
|
+
# The down-left point of the east face should map to
|
619
|
+
# φ = π/4, θ = -θc.
|
620
|
+
phi, theta = p.inverse(EAST_FACE, -1.0, -1.0)
|
621
|
+
assert_in_delta(PI/4, phi, DELTA)
|
622
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
623
|
+
|
624
|
+
# The down point of the east face should map to
|
625
|
+
# φ = π/2, θ = -π/4.
|
626
|
+
phi, theta = p.inverse(EAST_FACE, 0.0, -1.0)
|
627
|
+
assert_in_delta(PI/2, phi, DELTA)
|
628
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
629
|
+
|
630
|
+
# The down-right point of the east face should map to
|
631
|
+
# φ = 3π/4, θ = -θc.
|
632
|
+
phi, theta = p.inverse(EAST_FACE, 1.0, -1.0)
|
633
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
634
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
635
|
+
|
636
|
+
# 4. The back face.
|
637
|
+
|
638
|
+
# The up-left point of the back face should map to
|
639
|
+
# φ = 3π/4, θ = θc.
|
640
|
+
phi, theta = p.inverse(BACK_FACE, -1.0, 1.0)
|
641
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
642
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
643
|
+
|
644
|
+
# The up point of the back face should map to
|
645
|
+
# φ = π, θ = π/4.
|
646
|
+
# Note the implementation returns φ = -π, which is also correct.
|
647
|
+
phi, theta = p.inverse(BACK_FACE, 0.0, 1.0)
|
648
|
+
assert_in_delta(-PI, phi, DELTA)
|
649
|
+
assert_in_delta(PI/4, theta, DELTA)
|
650
|
+
|
651
|
+
# The up-right point of the back face should map to
|
652
|
+
# φ = -3π/4, θ = θc.
|
653
|
+
phi, theta = p.inverse(BACK_FACE, 1.0, 1.0)
|
654
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
655
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
656
|
+
|
657
|
+
# The left point of the back face should map to
|
658
|
+
# φ = 3π/4, θ = 0.
|
659
|
+
phi, theta = p.inverse(BACK_FACE, -1.0, 0.0)
|
660
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
661
|
+
assert_in_delta(0.0, theta, DELTA)
|
662
|
+
|
663
|
+
# The centre point of the back face should map to
|
664
|
+
# φ = π, θ = 0.
|
665
|
+
# Note the implementation returns φ = -π, which is also correct.
|
666
|
+
phi, theta = p.inverse(BACK_FACE, 0.0, 0.0)
|
667
|
+
assert_in_delta(-PI, phi, DELTA)
|
668
|
+
assert_in_delta(0.0, theta, DELTA)
|
669
|
+
|
670
|
+
# The right point of the back face should map to
|
671
|
+
# φ = -3π/4, θ = 0.
|
672
|
+
phi, theta = p.inverse(BACK_FACE, 1.0, 0.0)
|
673
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
674
|
+
assert_in_delta(0.0, theta, DELTA)
|
675
|
+
|
676
|
+
# The down-left point of the back face should map to
|
677
|
+
# φ = 3π/4, θ = -θc.
|
678
|
+
phi, theta = p.inverse(BACK_FACE, -1.0, -1.0)
|
679
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
680
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
681
|
+
|
682
|
+
# The down point of the back face should map to
|
683
|
+
# φ = π, θ = -π/4.
|
684
|
+
# Note the implementation returns φ = -π, which is also correct.
|
685
|
+
phi, theta = p.inverse(BACK_FACE, 0.0, -1.0)
|
686
|
+
assert_in_delta(-PI, phi, DELTA)
|
687
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
688
|
+
|
689
|
+
# The down-right point of the back face should map to
|
690
|
+
# φ = -3π/4, θ = -θc.
|
691
|
+
phi, theta = p.inverse(BACK_FACE, 1.0, -1.0)
|
692
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
693
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
694
|
+
|
695
|
+
# 5. The west face.
|
696
|
+
|
697
|
+
# The up-left point of the west face should map to
|
698
|
+
# φ = -3π/4, θ = θc.
|
699
|
+
phi, theta = p.inverse(WEST_FACE, -1.0, 1.0)
|
700
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
701
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
702
|
+
|
703
|
+
# The up point of the west face should map to
|
704
|
+
# φ = -π/2, θ = π/4.
|
705
|
+
phi, theta = p.inverse(WEST_FACE, 0.0, 1.0)
|
706
|
+
assert_in_delta(-PI/2, phi, DELTA)
|
707
|
+
assert_in_delta(PI/4, theta, DELTA)
|
708
|
+
|
709
|
+
# The up-right point of the west face should map to
|
710
|
+
# φ = -π/4, θ = θc.
|
711
|
+
phi, theta = p.inverse(WEST_FACE, 1.0, 1.0)
|
712
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
713
|
+
assert_in_delta(THETA_C, theta, DELTA)
|
714
|
+
|
715
|
+
# The left point of the west face should map to
|
716
|
+
# φ = -3π/4, θ = 0.
|
717
|
+
phi, theta = p.inverse(WEST_FACE, -1.0, 0.0)
|
718
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
719
|
+
assert_in_delta(0.0, theta, DELTA)
|
720
|
+
|
721
|
+
# The centre point of the west face should map to
|
722
|
+
# φ = -π/2, θ = 0.
|
723
|
+
phi, theta = p.inverse(WEST_FACE, 0.0, 0.0)
|
724
|
+
assert_in_delta(-PI/2, phi, DELTA)
|
725
|
+
assert_in_delta(0.0, theta, DELTA)
|
726
|
+
|
727
|
+
# The right point of the west face should map to
|
728
|
+
# φ = -π/4, θ = 0.
|
729
|
+
phi, theta = p.inverse(WEST_FACE, 1.0, 0.0)
|
730
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
731
|
+
assert_in_delta(0.0, theta, DELTA)
|
732
|
+
|
733
|
+
# The down-left point of the west face should map to
|
734
|
+
# φ = -3π/4, θ = -θc.
|
735
|
+
phi, theta = p.inverse(WEST_FACE, -1.0, -1.0)
|
736
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
737
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
738
|
+
|
739
|
+
# The down point of the west face should map to
|
740
|
+
# φ = -π/2, θ = -π/4.
|
741
|
+
phi, theta = p.inverse(WEST_FACE, 0.0, -1.0)
|
742
|
+
assert_in_delta(-PI/2, phi, DELTA)
|
743
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
744
|
+
|
745
|
+
# The down-right point of the west face should map to
|
746
|
+
# φ = -π/4, θ = -θc.
|
747
|
+
phi, theta = p.inverse(WEST_FACE, 1.0, -1.0)
|
748
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
749
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
750
|
+
|
751
|
+
# 6. The bottom face.
|
752
|
+
|
753
|
+
# The up-left point of the bottom face should map to
|
754
|
+
# φ = -π/4, θ = -θc.
|
755
|
+
phi, theta = p.inverse(BOTTOM_FACE, -1.0, 1.0)
|
756
|
+
assert_in_delta(-PI/4, phi, DELTA)
|
757
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
758
|
+
|
759
|
+
# The up point of the bottom face should map to
|
760
|
+
# φ = 0, θ = -π/4.
|
761
|
+
phi, theta = p.inverse(BOTTOM_FACE, 0.0, 1.0)
|
762
|
+
assert_in_delta(0.0, phi, DELTA)
|
763
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
764
|
+
|
765
|
+
# The up-right point of the bottom face should map to
|
766
|
+
# φ = π/4, θ = -θc.
|
767
|
+
phi, theta = p.inverse(BOTTOM_FACE, 1.0, 1.0)
|
768
|
+
assert_in_delta(PI/4, phi, DELTA)
|
769
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
770
|
+
|
771
|
+
# The left point of the bottom face should map to
|
772
|
+
# φ = -π/2, θ = -π/4.
|
773
|
+
phi, theta = p.inverse(BOTTOM_FACE, -1.0, 0.0)
|
774
|
+
assert_in_delta(-PI/2, phi, DELTA)
|
775
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
776
|
+
|
777
|
+
# The centre point of the bottom face should map to
|
778
|
+
# θ = -π/2, with φ being irrelevant.
|
779
|
+
phi, theta = p.inverse(BOTTOM_FACE, 0.0, 0.0)
|
780
|
+
assert_in_delta(-PI/2, theta, DELTA)
|
781
|
+
# So here, any value is correct. This implementation returns
|
782
|
+
# zero, though, for some reason. And we'll watch for changes.
|
783
|
+
assert_in_delta(0.0, phi, DELTA)
|
784
|
+
|
785
|
+
# The right point of the bottom face should map to
|
786
|
+
# φ = π/2, θ = -π/4.
|
787
|
+
phi, theta = p.inverse(BOTTOM_FACE, 1.0, 0.0)
|
788
|
+
assert_in_delta(PI/2, phi, DELTA)
|
789
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
790
|
+
|
791
|
+
# The down-left point of the bottom face should map to
|
792
|
+
# φ = -3π/4, θ = -θc.
|
793
|
+
phi, theta = p.inverse(BOTTOM_FACE, -1.0, -1.0)
|
794
|
+
assert_in_delta(-3*PI/4, phi, DELTA)
|
795
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
796
|
+
|
797
|
+
# The down point of the bottom face should map to
|
798
|
+
# φ = π, θ = -π/4.
|
799
|
+
phi, theta = p.inverse(BOTTOM_FACE, 0.0, -1.0)
|
800
|
+
assert_in_delta(PI, phi, DELTA)
|
801
|
+
assert_in_delta(-PI/4, theta, DELTA)
|
802
|
+
|
803
|
+
# The down-right point of the bottom face should map to
|
804
|
+
# φ = 3π/4, θ = -θc.
|
805
|
+
phi, theta = p.inverse(BOTTOM_FACE, 1.0, -1.0)
|
806
|
+
assert_in_delta(3*PI/4, phi, DELTA)
|
807
|
+
assert_in_delta(-THETA_C, theta, DELTA)
|
808
|
+
|
809
|
+
end # test_inverse_mapping
|
810
|
+
|
811
|
+
end # module MappingTests
|
812
|
+
|
813
|
+
class TangentialMappingTest < Test::Unit::TestCase
|
814
|
+
include MappingTests
|
815
|
+
|
816
|
+
def projection
|
817
|
+
QuadSphere::Tangential
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
class CSCMappingTest < Test::Unit::TestCase
|
822
|
+
include MappingTests
|
823
|
+
|
824
|
+
def projection
|
825
|
+
QuadSphere::CSC
|
826
|
+
end
|
827
|
+
|
828
|
+
def test_distortion_closure
|
829
|
+
expected_error = 2.4e-4
|
830
|
+
|
831
|
+
(-0.99).step(0.99, 0.01) do |psi|
|
832
|
+
(-0.99).step(0.99, 0.01) do |chi|
|
833
|
+
x = projection.forward_distort(chi, psi)
|
834
|
+
y = projection.forward_distort(psi, chi)
|
835
|
+
chi1 = projection.inverse_distort(x,y)
|
836
|
+
assert_in_delta(0.0, chi1-chi, expected_error)
|
837
|
+
psi1 = projection.inverse_distort(y,x)
|
838
|
+
error = Math::sqrt((chi1-chi)**2 + (psi1-psi)**2)
|
839
|
+
assert_in_delta(0.0, error, expected_error)
|
840
|
+
end
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
def test_closure
|
845
|
+
error = 3.0e-4
|
846
|
+
|
847
|
+
100.times do |row|
|
848
|
+
100.times do |col|
|
849
|
+
x = 0.005+col/100.0
|
850
|
+
y = 0.005+row/100.0
|
851
|
+
phi, theta = projection.inverse(WEST_FACE, x, y)
|
852
|
+
f1, x1, y1 = projection.forward(phi, theta)
|
853
|
+
assert_equal(WEST_FACE, f1)
|
854
|
+
assert_in_delta(x, x1, error)
|
855
|
+
assert_in_delta(y, y1, error)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|