Skip to content

Commit eb0d54e

Browse files
committed
initial commit
1 parent 4a213de commit eb0d54e

File tree

4 files changed

+447
-0
lines changed

4 files changed

+447
-0
lines changed

eclipse_gosu.rb

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# Visualizer to show sun and moon positions, give a certain time and position on
4+
# the earth.
5+
#
6+
# Uses "gosu" so you will need to install the following libraries:
7+
# * gosu (brew install gosu on macos)
8+
# * sdl2 (brew install sdl2 on macos)
9+
#
10+
# Uses gems:
11+
# * suncalc
12+
# * gosu
13+
14+
# The moon is the brighter of the two bodies. The sun is the dimmer of the two.
15+
#
16+
# The green line is the horizon and will move up and down depending on the time
17+
# of the year.
18+
#
19+
# Default starting position on the earth is Grand Teton National Park, which
20+
# will be in the path of totality for the 2017 Eclipse. Default time is the
21+
# projected time of totality for that location.
22+
#
23+
# You can move through time with the left and right arrows. By default, right
24+
# arrow will move you 1 hour ahead, left will move you one hour behind. You can
25+
# change the movement speed using the up and down arrows. Your current movement
26+
# speed is shown in the window title bar.
27+
#
28+
# The "r" key will set the time to be sunrise of the current day.
29+
#
30+
# The "s" key will set the time to be sunset of the current day.
31+
#
32+
# Escape will exit.
33+
#
34+
# You can change the position on earth by changing the value of `@coords`
35+
# defined inthe Eclipse#initialize method. Insert your prefered Lat/Long
36+
# coordinates in decimal format.
37+
#
38+
# You can also change the default start time by changing `@current_time`
39+
# defined in the Eclipse#initialize method. You can also uses the SunCalc gem
40+
# to set the time to be sunrise, sunset, mid-day, etc.
41+
42+
43+
require 'suncalc'
44+
require 'time'
45+
46+
require 'gosu'
47+
# brew install gosu
48+
# brew install sdl2
49+
50+
# https://gist.github.com/jlnr/661266
51+
class Circle
52+
attr_reader :columns, :rows
53+
54+
def initialize radius, color = 255
55+
@columns = @rows = radius * 2
56+
lower_half = (0...radius).map do |y|
57+
x = Math.sqrt(radius**2 - y**2).round
58+
right_half = "#{color.chr * x}#{0.chr * (radius - x)}"
59+
"#{right_half.reverse}#{right_half}"
60+
end.join
61+
@blob = lower_half.reverse + lower_half
62+
@blob.gsub!(/./) { |alpha| "#{255.chr}#{255.chr}#{255.chr}#{alpha}"}
63+
end
64+
65+
def to_blob
66+
@blob
67+
end
68+
end
69+
70+
class Eclipse < Gosu::Window
71+
MOVEMENTS_MODES = [
72+
# name, times, change in seconds * times
73+
['1 Minute', 1, 60],
74+
['1 Hour', 60, 60],
75+
['1 Day', 24, 3600],
76+
['30 Days', 30, 86400],
77+
['365 Days', 365, 86400]
78+
]
79+
80+
def initialize
81+
@width = 1024
82+
@height = 746
83+
super @width, @height
84+
self.caption = "Eclipse"
85+
86+
@moon_d = 24
87+
@moon = Gosu::Image.new(self, Circle.new(@moon_d, 255), false)
88+
@sun_d = 25
89+
@sun = Gosu::Image.new(self, Circle.new(@sun_d, 128), false)
90+
@you_d = 10
91+
@you = Gosu::Image.new(self, Circle.new(@you_d, 200), false)
92+
93+
# @coords = [40.768860, -111.893273] # Salt Lake City, Utah
94+
@coords = [43.833333, -110.700833] # grand teton NP, 11:36am
95+
96+
@current_time = Time.parse('2017-08-21 10:36:00') # grand teton NP totality, in MST/MDT time
97+
# @current_time = SunCalc.get_times(Time.now, @coords.first, @coords.last)[:solar_noon]
98+
# @current_time = SunCalc.get_times(Time.now, @coords.first, @coords.last)[:sunrise]
99+
# @current_time = SunCalc.get_times(Time.now, @coords.first, @coords.last)[:sunset]
100+
# @current_time = SunCalc.get_times(Time.parse('June 21, 2018'), @coords.first, @coords.last)[:sunrise]
101+
102+
# Can use one of: [:solar_noon, :nadir, :sunrise, :sunset, :sunrise_end, :sunset_start, :dawn, :dusk, :nautical_dawn, :nautical_dusk, :night_end, :night, :golden_hour_end, :golden_hour]
103+
104+
@current_movement_mode = 1
105+
106+
update_coords
107+
update_horizon_y
108+
puts 'update_horizon_y'
109+
110+
@counter = 0
111+
end
112+
113+
def button_down(id)
114+
return if @moving
115+
case id
116+
when Gosu::Button::KbRight
117+
@moving = true
118+
puts "Forward in time #{MOVEMENTS_MODES[@current_movement_mode][0]}"
119+
@counter = MOVEMENTS_MODES[@current_movement_mode][1]
120+
when Gosu::Button::KbLeft
121+
@moving = true
122+
puts "Backward in time #{MOVEMENTS_MODES[@current_movement_mode][0]}"
123+
@counter = -MOVEMENTS_MODES[@current_movement_mode][1]
124+
when Gosu::Button::KbUp
125+
@current_movement_mode += 1
126+
@current_movement_mode = 0 if @current_movement_mode > MOVEMENTS_MODES.length-1
127+
when Gosu::Button::KbDown
128+
@current_movement_mode -= 1
129+
@current_movement_mode = MOVEMENTS_MODES.length-1 if @current_movement_mode < 0
130+
when Gosu::Button::KbR
131+
@current_time = get_times[:sunrise]
132+
when Gosu::Button::KbS
133+
@current_time = get_times[:sunset]
134+
when Gosu::Button::KbEscape
135+
exit
136+
end
137+
end
138+
139+
def get_times
140+
SunCalc.get_times(@current_time, @coords.first, @coords.last)
141+
end
142+
143+
# Sets horizon Y coordiate to be same as suns Y position at sunrise
144+
def update_horizon_y
145+
sunrise_time = get_times[:sunrise]
146+
147+
sunrise_coords = SunCalc.get_position(sunrise_time, @coords.first, @coords.last)
148+
149+
@horizon_y = map_range([-180, +180], [@height-@sun_d/2, @sun_d], sph2cart(sunrise_coords[:azimuth], sunrise_coords[:altitude])[:y]).to_i
150+
end
151+
152+
def update_coords
153+
@sun_coords = SunCalc.get_position(@current_time, @coords.first, @coords.last)
154+
@moon_coords = SunCalc.get_moon_position(@current_time, @coords.first, @coords.last)
155+
end
156+
157+
def update
158+
if @counter != 0
159+
update_horizon_y
160+
update_caption
161+
end
162+
163+
if @counter < 0
164+
@current_time -= MOVEMENTS_MODES[@current_movement_mode][2]
165+
@counter += 1
166+
elsif @counter > 0
167+
@current_time += MOVEMENTS_MODES[@current_movement_mode][2]
168+
@counter -= 1
169+
elsif @counter == 0 && @moving
170+
update_horizon_y
171+
@moving = false
172+
end
173+
174+
update_coords
175+
end
176+
177+
def draw
178+
# draw horizon
179+
draw_line(0, @horizon_y, Gosu::Color::GREEN, @width, @horizon_y, Gosu::Color::GREEN)
180+
181+
# draw "you"
182+
@you.draw(@width/2, @horizon_y-@you_d, 0)
183+
184+
# Draw sun sphere - the dimmer of the two
185+
@sun.draw(
186+
map_range([-180, +180], [@width-@sun_d/2, @sun_d], sph2cart(@sun_coords[:azimuth], @sun_coords[:altitude])[:x]).to_i-(@sun_d/2),
187+
map_range([-180, +180], [@height-@sun_d/2, @sun_d], sph2cart(@sun_coords[:azimuth], @sun_coords[:altitude])[:y]).to_i-(@sun_d/2),
188+
0
189+
)
190+
191+
# Draw moon sphere - the brighter of the two
192+
@moon.draw(
193+
map_range([-180, +180], [@width-@moon_d/2, @moon_d], sph2cart(@moon_coords[:azimuth], @moon_coords[:altitude])[:x]).to_i-(@moon_d/2),
194+
map_range([-180, +180], [@height-@moon_d/2, @moon_d], sph2cart(@moon_coords[:azimuth], @moon_coords[:altitude])[:y]).to_i-(@moon_d/2),
195+
0
196+
)
197+
198+
unless @moving
199+
# To prevent needless rapid redrawing if the screen isn't changing
200+
sleep(0.25)
201+
update_caption
202+
end
203+
end
204+
205+
def update_caption
206+
self.caption = "#{(@current_time)} - #{MOVEMENTS_MODES[@current_movement_mode][0]}"
207+
end
208+
209+
# Maps a number from one numeric range to another. In this case we need to be
210+
# able to map the coordinates of the sun/moon in to the coordinate range of
211+
# the Gosu window.
212+
def map_range(a, b, s)
213+
af, al, bf, bl = a.first, a.last, b.first, b.last
214+
bf + (s - af)*(bl - bf).quo(al - af)
215+
end
216+
217+
# Convert Spherical coordinates (azimuth/elevation) in to cartesian (x/y) coordinates
218+
# https://www.mathworks.com/help/matlab/ref/sph2cart.html?requestedDomain=www.mathworks.com#input_argument_d0e929631
219+
def sph2cart(azimuth, elevation)
220+
{
221+
# x: 180 * Math.cos(elevation) * Math.cos(azimuth),
222+
# y: 180 * Math.cos(elevation) * Math.sin(azimuth)
223+
x: 180 * Math.cos(elevation) * Math.sin(azimuth),
224+
y: 180 * Math.cos(elevation) * Math.cos(azimuth)
225+
226+
}
227+
end
228+
229+
end
230+
231+
Eclipse.new.show

0 commit comments

Comments
 (0)