telescope-term 0.7 → 1.1

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/telescope +472 -606
  3. metadata +13 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 408d3c474e9618022c328cdf98a68d2c0d1922951ab422229158ed8ca882d022
4
- data.tar.gz: 84f8679e4d86425f948c0a314ab5e7a8bedef62af0c170dafe3370d3f0317551
3
+ metadata.gz: b9d7390a8fecd0f7985cb6c6396edc3611f51daa61afc810e4636487345dda37
4
+ data.tar.gz: 525cc188610ac7dc332850e26b95fedada8c77acb6bddb498e260c4b5769e26f
5
5
  SHA512:
6
- metadata.gz: 3f0150614eeb44975ea19fd3bd61a7caf43349f5d9c7de50e6d174780c589926476ad77408cfa1cb9a4ae873dcd832994a18725b252a7aee4b5f2f575a5ba5d7
7
- data.tar.gz: 3ed63758d445eeaf494c14ebdc13543c1c2060a798230f7b16e55101beb7c777187e29c8c0e17e3e5a0ddeefb3cc3d0ca4688b98b375fe1faecb9e7e67635cec
6
+ metadata.gz: 15fb388da4c9bdced12b57cfdce35d466b19df4fc4ffe6ab1597bfeb248c66f05bddc2e1b79454833289e2dd1680a9fd45cf55b2afb6021d9c94c23df6646ea1
7
+ data.tar.gz: 78e47762e001126575ad985841efd7fc73c17f8b6cf710fc11616a8f8bad902e744754e2b2439c59e2ced2421a97e87ec784254ee6ff7e84686c7fd1401ff41d
data/bin/telescope CHANGED
@@ -1,637 +1,503 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
- @help = <<HELPTEXT
4
- WELCOME TO TELESCOPE - A TERMINAL/CONSOLE PROGRAM FOR THE AMATEUR ASTRONOMER.
5
- The top panel lists your telescopes with eyepieces in the lower panel. Add a telescope by pressing 't' and fill in the name, the
6
- apperature (APP) and the focal length (FL) in the "command line" at the bottom and press ENTER. Add an eyepiece with 'e' and enter
7
- a name, the focal length (FL) and apparent field of view (AFOV) and press ENTER. Select items by using the TAB/UP/DOWN cursor keys.
8
- Press ENTER on a selected item to change the values in the command line. Delete an item by pressing 'D'. Move an with PgUP/PgDown keys.
9
- Sort telescopes with 'T' (toggles sorting by the first thre columns; Name, APP, FL). Sort eyepieces in the same way with the 'E' key.
10
- Tag items with the SPACE key (untag all with 'u'), then press 'o' to create an observation log file (content shown in the lower panel).
11
- Refresh all panels with the 'r' key. Escape the selected panels or the command line by pressing 'Ctrl-G'. Quit via 'q' (or 'Q' if you
12
- don't want to save your edits since last session). Save a backup session with 'b' and load a saved backup session with the 'B' key.
13
- Telescope and eyepiece data is saved in the file '.telescope' in your home directory (backup file is '.telescope.bu').
14
-
15
- LIST OF TELESCOPE ABBREVIATIONS: LIST OF EYEPIECE ABBREVIATIONS:
16
- APP, FL, F/? = Apperature and Focal Length (millimeters) and F-ratio FL = Focal Length (in millimeters)
17
- <MAG = Maximum magnitude visible AFOV = Apparent Field Of View
18
- xEYE = Light gathering compared to the human eye xMAGN = Magnification (with that telescope)
19
- MINx, MAXx = Minimum and maximum usable magnification FOV = True Field Of View (deg/min/sec)
20
- XPUP = Exit pupil (in millimeters)
21
- Recommended magnifications for objects:
22
- *FIELD = star fields, GX/NEB = galaxies/nebulae, PL/GCL = planets/globular clusters
23
- PLd/2* = planet details/double stars, TGHT2* = tight double stars
24
- DL/RC-SEP = Minimum separation, Dawes limit and Rayleigh limit
25
- MOON, SUN = Minimum feature resolved on the Moon (meters) & Sun (kilometers)
26
- HELPTEXT
27
- begin # BASIC SETUP
28
- if `tput cols`.to_i < 140
29
- puts "You must run Telescope with a minimum tarminal width of 140 chracters."
3
+
4
+ # SCRIPT INFO {{{1
5
+ # Name: Telescope - a tool for amateur astronomers
6
+ # Language: Pure Ruby, best viewed in VIM
7
+ # Author: Geir Isene <g@isene.com>
8
+ # Web_site: http://isene.com/
9
+ # Github: https://github.com/isene/telescope
10
+ # License: I release all copyright claims. This code is in the public domain.
11
+ # Permission is granted to use, copy modify, distribute, and sell
12
+ # this software for any purpose. I make no guarantee about the
13
+ # suitability of this software for any purpose and I am not liable
14
+ # for any damages resulting from its use. Further, I am under no
15
+ # obligation to maintain or extend this software. It is provided
16
+ # on an 'as is' basis without any expressed or implied warranty.
17
+ # Docs: Apart from the extensive documentation found on Github, you can
18
+ # get a great understanding of the code itself by simply sending
19
+ # or pasting this whole file into you favorite AI for coding with
20
+ # a prompt like this: "Help me understand every part of this code".
21
+
22
+ # SETUP {{{1
23
+ begin
24
+ require 'rcurses'
25
+ class Object
26
+ include Rcurses
27
+ include Rcurses::Input
30
28
  end
31
- require 'io/console'
32
- require 'date'
33
- require 'curses'
34
- include Curses
35
-
36
- Curses.init_screen
37
- Curses.start_color
38
- Curses.curs_set(0)
39
- Curses.noecho
40
- Curses.cbreak
41
- Curses.stdscr.keypad = true
29
+ rescue StandardError => e
30
+ puts 'Telescope is built using rcurses (https://github.com/isene/rcurses). Install rcurses to run Telescope.'
31
+ exit 1
32
+ end
33
+ Pane = Rcurses::Pane
34
+ require 'date'
35
+
36
+ # Persistence paths
37
+ SAVE = File.join(Dir.home, '.telescope')
38
+
39
+ # HELP TEXT {{{1
40
+ HELP1 = " WELCOME TO THE TERMINAL TELESCOPE APPLICATION\n".b + '
41
+ This can help structure your telescopes and eyepieces.
42
+ It will calculate properties of your scopes and EPs.
43
+ Scope+EP combination properties are also calculated.
44
+
45
+ Anything you like to have changed or bugs fixed,
46
+ create an issue: https://github.com/isene/telescope
47
+ ...or drop me an e-mail: g@isene.com.
48
+
49
+ Press any key to show more help text.
50
+ '
51
+
52
+ HELP2 = " TERMININAL TELESCOPE APPLICATION\n\n Keys and their actions\n".b + '
53
+ t Add telescope (name,app,fl)
54
+ e Add eyepiece (name,fl,afov)
55
+ ENTER Edit selected
56
+ TAB Switch panels
57
+ UP/DOWN Move cursor
58
+ Shift-UP Move item up
59
+ Shift-DOWN Move item down
60
+ HOME/END Jump to start/end
61
+ o Toggle order by Telescope APP and Eyepiece FL
62
+ SPACE Tag/untag
63
+ u Untag all
64
+ Ctrl-o Create observation log with tagged equipment
65
+ D Delete item
66
+ r Refresh all panes
67
+ q/Q Quit (save/no save)
68
+ ? Help'.fg(230)
42
69
 
70
+ HELP3 = " Abbreviations and their meaning\n".b + '
71
+ APP = Telescope apperature (in millimeters)
72
+ TFL = Telescope focal length (in millimeters)
73
+ F/? = Telescope focal ratio
74
+ <MGN = Magnitude limit (dimmest object visible)
75
+ xEYE = Times eye light gathering
76
+ MINx = Minimum magnification
77
+ MAXx = Maximum magnification
78
+ SEP-R = Rayleigh separation criterion
79
+ SEP-D = Dawes separation limit
80
+ *FLD = EP for star fields
81
+ GLXY = EP for galaxies & nebulae
82
+ PLNT = EP for planets, globular clusters
83
+ DBL* = EP for double stars & planetary details
84
+ >2*< = EP for tight double stars
85
+ MOON = Smallest detail visible on Moon
86
+ SUN = Smallest detail visible on Sun
87
+
88
+ FL = EP focal length (in millimeters)
89
+ AFOV = EP apparent field of view
90
+ MAGX = Magnification (w/selected telescope)
91
+ TFOV = True field of view (w/selected telescope)
92
+ PPL = Exit pupil (w/selected telescope)
93
+ 2BLW = With a 2xBarlow (magnification, then rest)'.fg(229)
94
+
95
+
96
+ # INITIALIZATION {{{1
97
+ # Data stores
98
+ @ts = [] # Telescopes: [name, app, fl]
99
+ @ep = [] # Eyepieces: [name, fl, afov]
100
+ @tstag = [] # Telescope tags
101
+ @eptag = [] # Eyepiece tags
102
+ @cursor_ts = 0 # Telescope cursor index
103
+ @cursor_ep = 0 # Eyepiece cursor index
104
+
105
+ # Load saved data if present
106
+ SAVE = File.join(Dir.home, '.telescope') # Persistence path
107
+
108
+ if File.exist?(SAVE)
109
+ load SAVE # expects plaintext: @ts = [...] and @ep = [...]
110
+ else
43
111
  @ts = []
44
112
  @ep = []
45
-
46
- @tsmark = false
47
- @epmark = false
113
+ end
48
114
 
49
- @tstag = []
50
- @eptag = []
115
+ @ts_unsorted = @ts.dup
116
+ @ep_unsorted = @ep.dup
51
117
 
52
- @t_sort = false
53
- @e_sort = false
54
- end
55
- if File.exist?(Dir.home+'/.telescope')
56
- load(Dir.home+'/.telescope')
57
- end
58
- class Numeric # NUMERIC CLASS EXTENSION
59
- def deg
60
- self * Math::PI / 180
61
- end
62
- def rad
63
- self * 180 / Math::PI
64
- end
65
- def dec2
66
- "%.2f" % self
67
- end
118
+ # Initialize tag arrays to match loaded data
119
+ @tstag = Array.new(@ts.size, false)
120
+ @eptag = Array.new(@ep.size, false)
121
+
122
+ # CLASS EXTENSIONS {{{1
123
+ # Numeric formatting helper
124
+ class Numeric
125
+ def deg; self * Math::PI / 180; end
126
+ def rad; self * 180 / Math::PI; end
127
+ def rts(n); format("%.#{n}f", self) end
68
128
  end
69
- class Curses::Window # CLASS EXTENSION
70
- # General extensions (see https://github.com/isene/Ruby-Curses-Class-Extension)
71
- attr_accessor :color, :fg, :bg, :attr, :update
72
- # Set self.color for an already defined color pair such as: init_pair(1, 255, 3)
73
- # The color pair is defined like this: init_pair(index, foreground, background)
74
- # self.fg is set for the foreground color (and is used if self.color is not set)
75
- # self.bg is set for the background color (and is used if self.color is not set)
76
- # self.attr is set for text attributes like Curses::A_BOLD
77
- def clr # Clears the whole window
78
- self.setpos(0, 0)
79
- self.maxy.times {self.deleteln()}
80
- self.refresh
81
- self.setpos(0, 0)
82
- end
83
- def clr_to_cur_line
84
- l = self.cury
85
- self.setpos(0, 0)
86
- l.times {self.deleteln()}
87
- self.refresh
88
- end
89
- def clr_from_cur_line
90
- l = self.cury
91
- (self.maxy - l).times {self.deleteln()}
92
- self.refresh
93
- self.setpos(l, 0)
94
- end
95
- def fill # Fill window with color as set by self.color (or self.bg if not set)
96
- self.setpos(0, 0)
97
- self.fill_from_cur_pos
98
- end
99
- def fill_to_cur_pos # Fills the window up to current line
100
- x = self.curx
101
- y = self.cury
102
- self.setpos(0, 0)
103
- blank = " " * self.maxx
104
- if self.color == nil
105
- self.bg = 0 if self.bg == nil
106
- self.fg = 255 if self.fg == nil
107
- init_pair(self.fg, self.fg, self.bg)
108
- y.times {self.attron(color_pair(self.fg)) {self << blank}}
109
- else
110
- y.times {self.attron(color_pair(self.color)) {self << blank}}
111
- end
112
- self.refresh
113
- self.setpos(y, x)
114
- end
115
- def fill_from_cur_pos # Fills the rest of the window from current line
116
- x = self.curx
117
- y = self.cury
118
- self.setpos(y, 0)
119
- blank = " " * self.maxx
120
- if self.color == nil
121
- self.bg = 0 if self.bg == nil
122
- self.fg = 255 if self.fg == nil
123
- init_pair(self.fg, self.fg, self.bg)
124
- self.maxy.times {self.attron(color_pair(self.fg)) {self << blank}}
125
- else
126
- self.maxy.times {self.attron(color_pair(self.color)) {self << blank}}
127
- end
128
- self.refresh
129
- self.setpos(y, x)
130
- end
131
- def p(text) # Puts text to window
132
- self.attr = 0 if self.attr == nil
133
- if self.color == nil
134
- self.bg = 0 if self.bg == nil
135
- self.fg = 255 if self.fg == nil
136
- init_pair(self.fg, self.fg, self.bg)
137
- self.attron(color_pair(self.fg) | self.attr) { self << text }
138
- else
139
- self.attron(color_pair(self.color) | self.attr) { self << text }
140
- end
141
- self.refresh
142
- end
143
- def pclr(text) # Puts text to window and clears the rest of the window
144
- self.p(text)
145
- self.clr_from_cur_line
146
- end
147
- def paclr(fg, bg, attr, text) # Puts text to window with full set of attributes and clears rest of window
148
- self.paclr(fg, bg, attr, text)
149
- self.clr_from_cur_line
150
- end
151
- def pa(fg, bg, attr, text) # Puts text to window with full set of attributes
152
- self.fg = fg
153
- self.bg = bg
154
- self.attr = attr
155
- init_pair(self.fg, self.fg, self.bg)
156
- self.attron(color_pair(self.fg) | self.attr) { self << text }
157
- self.refresh
129
+
130
+ # Pane class extension
131
+ module Rcurses
132
+ class Pane
133
+ attr_accessor :index
158
134
  end
159
135
  end
160
- def getchr # PROCESS KEY PRESSES
161
- c = STDIN.getch(min: 0, time: 1)
162
- case c
163
- when "\e" # ANSI escape sequences
164
- case $stdin.getc
165
- when '[' # CSI
166
- case $stdin.getc
167
- when 'A' then chr = "UP"
168
- when 'B' then chr = "DOWN"
169
- when 'C' then chr = "RIGHT"
170
- when 'D' then chr = "LEFT"
171
- when 'Z' then chr = "S-TAB"
172
- when '2' then chr = "INS" ; STDIN.getc
173
- when '3' then chr = "DEL" ; STDIN.getc
174
- when '5' then chr = "PgUP" ; STDIN.getc
175
- when '6' then chr = "PgDOWN" ; STDIN.getc
176
- when '7' then chr = "HOME" ; STDIN.getc
177
- when '8' then chr = "END" ; STDIN.getc
178
- end
179
- end
180
- when "", "" then chr = "BACK"
181
- when "" then chr = "WBACK"
182
- when "" then chr = "LDEL"
183
- when "" then chr = "C-T"
184
- when "" then chr = "C-G"
185
- when "\r" then chr = "ENTER"
186
- when "\t" then chr = "TAB"
187
- when /./ then chr = c
188
- end
189
- return chr
136
+
137
+ # OPTICAL FORMULAS {{{1
138
+ def tfr(app, tfl); (tfl.to_f / app); end
139
+ def mlim(app); (5 * Math::log(app / 10, 10) + 7.5); end
140
+ def xeye(app); (app.to_f ** 2 / 49); end
141
+ def minx(app, tfl); (tfl / (7 * tfr(app, tfl))); end
142
+ def mine(app, tfl); (7 * tfr(app, tfl)); end
143
+ def maxx(app); (2 * app.to_f); end
144
+ def maxe(app, tfl); (tfl / maxx(app)); end
145
+ def sepr(app); (3600.0 * Math::asin(671E-6 / app).rad); end
146
+ def sepd(app); (115.824 / app); end
147
+ def e_st(app, tfl); (6.4 * tfl / app); end
148
+ def e_gx(app, tfl); (3.6 * tfl / app); end
149
+ def e_pl(app, tfl); (2.1 * tfl / app); end
150
+ def e_2s(app, tfl); (1.3 * tfl / app); end
151
+ def e_t2(app, tfl); (0.7 * tfl / app); end
152
+ def moon(tfl); (384E6*Math::tan((115.824.deg / tfl)/360)); end
153
+ def sun(tfl); (moon(tfl) / 2.5668); end
154
+ def tfov(tfl, epfl, afov); (afov.to_f / magx(tfl, epfl)); end
155
+ def pupl(app, tfl, epfl); (app.to_f / magx(tfl, epfl)); end
156
+ def magx(tfl, epfl); (tfl.to_f / epfl); end
157
+
158
+ # FUNCTIONS {{{1
159
+ def refresh_all #{{{2
160
+ Rcurses.clear_screen
161
+ @pTS.border = false
162
+ @pEP.border = false
163
+ @focus.border = true
164
+ @pTS.full_refresh
165
+ @pTSh.full_refresh
166
+ @pEP.full_refresh
167
+ @pEPh.full_refresh
168
+ @pST.refresh
190
169
  end
191
- def main_getkey # GET KEY FROM USER
192
- chr = getchr
193
- case chr
194
- when '?' # Show helptext in lower panel
195
- @w_ep.clr
196
- @w_ep.pa(249, 0, 0, @help)
197
- @w_ep.p("\n ...Press any key to continue")
198
- getch
199
- when 'TAB' # Move between the panels
200
- if @tsmark
201
- @tsmark = false
202
- @epmark = 0
203
- elsif @epmark
204
- @epmark = false
205
- @tsmark = false
206
- else
207
- @tsmark = 0
208
- @epmark = false
209
- end
210
- when 'C-G' # Escape from panel selections or command line
211
- @epmark = false
212
- @tsmark = false
213
- when 'ENTER' # Edit selected item in the command line (or refresh panels)
214
- if @tsmark
215
- out = "#{@ts[@tsmark][0]}, #{@ts[@tsmark][1]}, #{@ts[@tsmark][2]}"
216
- ret = w_cm_getstr("", out).split(",")
217
- return if ret.length != 3
218
- ret[1] = ret[1].to_i
219
- ret[2] = ret[2].to_i
220
- @ts[@tsmark] = ret
221
- elsif @epmark
222
- out = "#{@ep[@epmark][0]}, #{@ep[@epmark][1]}, #{@ep[@epmark][2]}"
223
- ret = w_cm_getstr("", out).split(",")
224
- return if ret.length != 3
225
- ret[1] = ret[1].to_f
226
- ret[2] = ret[2].to_i
227
- @ep[@epmark] = ret
228
- end
229
- when 't' # Add a telescope
230
- return if @ts.length == 5
231
- ret = w_cm_getstr("", "Telescope, App, FL").split(",")
232
- return if ret.length != 3
233
- ret[1] = ret[1].to_i
234
- ret[2] = ret[2].to_i
235
- ret[1] = 1 if ret[1] == 0
236
- ret[2] = 1 if ret[2] == 0
237
- @ts[@ts.length] = ret
238
- when 'e' # Add an eyepiece
239
- ret = w_cm_getstr("", "Eyepiece, FL, AFOV").split(",")
240
- return if ret.length != 3
241
- ret[1] = ret[1].to_f
242
- ret[2] = ret[2].to_i
243
- ret[1] = 1 if ret[1] == 0
244
- ret[2] = 1 if ret[2] == 0
245
- @ep[@ep.length] = ret
246
- when 'D' # Delete selected item (telescope or eyepiece)
247
- if @tsmark
248
- @ts.delete_at(@tsmark)
249
- @tsmark -= 1
250
- elsif @epmark
251
- @ep.delete_at(@epmark)
252
- @epmark -= 1
253
- end
254
- when 'T' # Sort telescopes by next column (Name, APP, FL)
255
- if @t_sort == false or @t_sort == 2
256
- @t_sort = 0
257
- else
258
- @t_sort += 1
259
- end
260
- @ts = @ts.sort {|a,b| b[@t_sort] <=> a[@t_sort]}
261
- when 'E' # Sort eyepiece by next column (Name, FL, AFOV)
262
- if @e_sort == false or @e_sort == 2
263
- @e_sort = 0
264
- else
265
- @e_sort += 1
266
- end
267
- @ep = @ep.sort {|a,b| b[@e_sort] <=> a[@e_sort]}
268
- when 'UP' # Move to one item up
269
- if @tsmark
270
- if @tsmark == 0
271
- @tsmark = false
272
- else
273
- @tsmark -= 1
274
- end
275
- elsif @epmark
276
- if @epmark == 0
277
- @epmark = false
278
- @tsmark = @ts.length - 1
279
- else
280
- @epmark -= 1
281
- end
282
- else
283
- @epmark = @ep.length - 1
284
- end
285
- when 'DOWN' # Move to one item down
286
- if @tsmark
287
- if @tsmark == @ts.length - 1
288
- @tsmark = false
289
- @epmark = 0
290
- else
291
- @tsmark += 1
292
- end
293
- elsif @epmark
294
- if @epmark == @ep.length - 1
295
- @epmark = false
296
- else
297
- @epmark += 1
298
- end
299
- else
300
- @tsmark = 0
301
- end
302
- when 'PgUP' # Move selected item up by one
303
- if @tsmark
304
- t = @ts.delete_at(@tsmark)
305
- @tsmark -= 1 unless @tsmark == 0
306
- @ts.insert(@tsmark, t)
307
- elsif @epmark
308
- e = @ep.delete_at(@epmark)
309
- @epmark -= 1 unless @epmark == 0
310
- @ep.insert(@epmark, e)
311
- end
312
- when 'PgDOWN' # Move selected item by one down
313
- if @tsmark
314
- t = @ts.delete_at(@tsmark)
315
- @tsmark += 1 unless @tsmark == @ts.length
316
- @ts.insert(@tsmark, t)
317
- elsif @epmark
318
- e = @ep.delete_at(@epmark)
319
- @epmark += 1 unless @epmark == @ep.length
320
- @ep.insert(@epmark, e)
321
- end
322
- when 'HOME' # Jump to first item in the panel
323
- if @tsmark
324
- @tsmark = 0
325
- elsif @epmark
326
- @epmark = 0
327
- end
328
- when 'END' # Move to last item in the panel
329
- if @tsmark
330
- @tsmark = @ts.length - 1
331
- elsif @epmark
332
- @epmark = @ep.length - 1
333
- end
334
- when ' ' # Tag selected item to be used in observation file/log
335
- if @tsmark
336
- @tstag.include?(@tsmark) ? @tstag.delete(@tsmark) : @tstag.push(@tsmark)
337
- @tsmark += 1 unless @tsmark == @ts.length - 1
338
- elsif @epmark
339
- @eptag.include?(@epmark) ? @eptag.delete(@epmark) : @eptag.push(@epmark)
340
- @epmark += 1 unless @epmark == @ep.length - 1
341
- end
342
- when 'u' # Untag all tagget items
343
- @tstag.clear
344
- @eptag.clear
345
- when 'o' # Create observation file/log and show content in lower panel
346
- observe
347
- when 'b' # Create backup file (~/.telescope.bu) with current items
348
- File.write(Dir.home+'/.telescope.bu',"@ts = #{@ts}\n@ep = #{@ep}")
349
- when 'B' # Read items from backup file
350
- if File.exist?(Dir.home+'/.telescope.bu')
351
- load(Dir.home+'/.telescope.bu')
352
- end
353
- when 'r' # Hard refres panels
354
- @break = true
355
- when 'q' # Exit after saving items to ~/.telescope
356
- File.write(Dir.home+'/.telescope',"@ts = #{@ts}\n@ep = #{@ep}")
357
- exit 0
358
- when 'Q' # Exit without saving items
359
- exit 0
360
- else
170
+
171
+ def render_ts #{{{2
172
+ @pTS.clear
173
+ @ts.each_with_index do |t, i|
174
+ @pTS.text += "\n"
175
+ name = t[0]
176
+ app = t[1].to_i
177
+ tfl = t[2].to_i
178
+ tag_ts = @tstag[i] ? ' ' + '▐'.b.fg(46) : ' '
179
+ txt = tag_ts
180
+ txt += name.to_s.ljust(18)
181
+ txt += app.to_s.rjust(7)
182
+ txt += tfl.to_s.rjust(8)
183
+ txt += tfr(app, tfl).rts(1).rjust(6)
184
+ txt += mlim(app).rts(1).rjust(6)
185
+ txt += xeye(app).rts(0).rjust(6)
186
+ min = mine(app, tfl).rts(0)
187
+ min += "(" + minx(app, tfl).rts(0) + "x)"
188
+ txt += min.rjust(10)
189
+ max = maxe(app, tfl).rts(0)
190
+ max += "(" + maxx(app).rts(0) + "x)"
191
+ txt += max.rjust(9)
192
+ txt += sepr(app).rts(2).rjust(7) + '"'
193
+ txt += sepd(app).rts(2).rjust(6) + '"'
194
+ txt += e_st(app, tfl).rts(0).rjust(7)
195
+ txt += e_gx(app, tfl).rts(0).rjust(6)
196
+ txt += e_pl(app, tfl).rts(0).rjust(6)
197
+ txt += e_2s(app, tfl).rts(0).rjust(6)
198
+ txt += e_t2(app, tfl).rts(0).rjust(6)
199
+ txt += moon(tfl).rts(0).rjust(7)
200
+ txt += sun(tfl).rts(0).rjust(6)
201
+ txt[0] = '→' if i == @pTS.index
202
+ # ANSI safe padding
203
+ pad = @pTS.w - Rcurses.display_width(txt.pure)
204
+ pad = 0 if pad.negative?
205
+ txt += ' ' * pad
206
+ txt = txt.bg(234) if i == @pTS.index
207
+ txt = txt.fg(248) if i != @pTS.index
208
+ @pTS.text += txt
361
209
  end
210
+ @pTS.refresh
211
+ @pTSh.full_refresh
362
212
  end
363
- def observe
364
- @tstag = @tstag.sort
365
- @eptag = @eptag.sort
366
- date = DateTime.now.strftime "%Y-%m-%d"
367
- file = Dir.home + "/" + date + "_observation.txt"
368
- obs = "Observation file: #{file}\n"
369
- obs += "Observation date:\n\n"
370
- obs += "This file lists the intended equipment for the observation date.\n"
371
- obs += "Reference observations with telescope letter and eyepiece numbers (like A2, B1, etc.)\n\n"
372
- obs += "─" * 100 + "\n"
373
- enum = "ABCDE"
374
- @tstag.each_with_index do |t, i|
375
- d = @ts[t][1]
376
- f = @ts[t][2]
377
- obs += "Telescope (#{enum[i]}): " + @ts[t][0].ljust(15) + " ("
378
- obs += d.to_s + "mm/" + f.to_s + "mm f/" + (f/d.to_f).truncate(1).to_s + ")"
379
- mag = (5 * Math::log(d/10, 10) + 7.5).truncate(1).to_s
380
- obs += " Max MAG: " + mag
381
- sepd = (115.824/d).truncate(2).to_s
382
- sepr = (3600*Math::asin(671E-6/d).rad).truncate(2).to_s
383
- obs += " Min SEP: " + sepd + "/" + sepr + "\n"
384
- end
385
- obs += "No telescope(s) chosen for the observation\n" if @tstag.empty?
386
- obs += "─" * 100 + "\n"
387
- @eptag.each_with_index do |e, i|
388
- m = @ep[e][1]
389
- a = @ep[e][2]
390
- obs += "Eyepiece (#{i+1}): " + @ep[e][0].ljust(15) + " ("
391
- obs += m.to_s.rjust(4) + "mm/" + a.to_s.rjust(3) + "°) "
392
- @tstag.each_with_index do |t, j|
393
- d = @ts[t][1]
394
- f = @ts[t][2]
395
- obs += enum[j] + ": "
396
- mag = (f.to_f/m)
397
- obs += mag.truncate(1).to_s.rjust(5) + "x ("
398
- fov = a/mag
399
- deg = fov.to_i
400
- mins = ((fov - fov.to_i) * 60)
401
- min = mins.to_i
402
- sec = ((mins - min) * 60).to_i
403
- deg == 0 ? dgo = " " : dgo = deg.to_s + "°"
404
- mno = min.to_s.rjust(2, " ") + "'"
405
- sco = sec.to_s.rjust(2, " ") + "\""
406
- obs += (dgo + mno + sco) + ") "
407
- end
408
- obs += "\n"
409
- end
410
- obs += "No eyepiece(s) chosen for the observation\n" if @eptag.empty?
411
- obs += "─" * 100 + "\n\n"
412
- obs += "Object: Equipment: Observation:\n" * 8
413
- @w_ep.clr
414
- @w_ep.pa(255, 0, 0, obs)
415
- @w_ep.p("\n...Press any key to continue")
416
- File.write(file, obs)
417
- getch
213
+
214
+ def ep_nice(app, tfl, e) #{{{2
215
+ r = (tfl / app)
216
+ out = ' '
217
+ out += (e/6 > r ? ' ✓'.fg(112) : ' ✗'.fg(208))
218
+ out += (e/3 > r && e/6 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
219
+ out += (e/1.5 > r && e/3 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
220
+ out += (e >= r && e/1.5 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
221
+ out += (e < r ? ' ✓'.fg(112) : ' ✗'.fg(208))
222
+ out
418
223
  end
419
224
 
420
- # TELESCOPE FUNCTIONS (top window, w_ts)
421
- def w_ts_show
422
- @w_ts.setpos(0,0)
423
- heading = " TELESCOPES APP(mm) FL(mm) F/? <MAG xEYE MINx MAXx *FIELD GX/NEB PL/GCL PLd/2* TGHT2* DL-SEP RC-SEP MOON SUN"
424
- heading += " " * (@w_ts.maxx - heading.length).abs
425
- @w_ts.pa(255, 94, Curses::A_BOLD, heading)
426
- @w_ts.fg = 15
427
- @w_ts.bg = 0
428
- @ts.each_with_index do |scope, i|
429
- name = scope[0][0...18]
430
- d = scope[1]
431
- f = scope[2]
432
- out = " " + name.ljust(18)
433
- out += d.to_s.rjust(8)
434
- out += f.to_s.rjust(8)
435
- attr = Curses::A_BOLD
436
- attr = attr | Curses::A_REVERSE if @tsmark == i
437
- attr = attr | Curses::A_UNDERLINE if @tstag.include?(i)
438
- @w_ts.pa(254, 0, attr, out) # Basic info (Name, APP, FL)
439
- out = (f.to_f/d.to_f).truncate(1).to_s.rjust(6)
440
- @w_ts.pa(254, 0, attr, out) # F/?
441
- out = (5 * Math::log(d/10, 10) + 7.5).truncate(1).to_s.rjust(6)
442
- @w_ts.pa(229, 0, attr, out) # <MAG
443
- out = (d**2/49).to_i.to_s.rjust(6)
444
- @w_ts.pa(229, 0, attr, out) # xEYE
445
- out = magx(d, f, 1/7.to_f)
446
- @w_ts.pa(157, 0, attr, out) # MINx
447
- out = magx(d, f, 2)
448
- @w_ts.pa(157, 0, attr, out) # MAXx
449
- out = magx(d, f, 1/6.4)
450
- @w_ts.pa(195, 0, attr, out) # *FIELD
451
- out = magx(d, f, 1/3.6)
452
- @w_ts.pa(195, 0, attr, out) # GX/NEB
453
- out = magx(d, f, 1/2.1)
454
- @w_ts.pa(195, 0, attr, out) # PL/GCL
455
- out = magx(d, f, 1/1.3)
456
- @w_ts.pa(195, 0, attr, out) # PLd/2*
457
- out = magx(d, f, 1/0.7)
458
- @w_ts.pa(195, 0, attr, out) # TGHT2*
459
- out = (115.824/d).truncate(2).dec2.to_s.rjust(7)
460
- @w_ts.pa(219, 0, attr, out) # DL-SEP
461
- out = (3600*Math::asin(671E-6/d).rad).truncate(2).dec2.to_s.rjust(8)
462
- @w_ts.pa(219, 0, attr, out) # RC-SEP
463
- moon = (384E6*Math::tan((115.824.deg/d)/3600))
464
- out = moon.to_i.to_s.rjust(6) + "m"
465
- @w_ts.pa(225, 0, attr, out) # MOON
466
- out = (moon/2.5668).to_i.to_s.rjust(5) + "km"
467
- @w_ts.pa(225, 0, attr, out) # SUN
468
- @w_ts.p("\n")
225
+ def render_ep #{{{2
226
+ app = @ts[@pTS.index][1].to_f
227
+ tfl = @ts[@pTS.index][2].to_f
228
+ @pEP.text = "\n"
229
+ @ep.each_with_index do |e, i|
230
+ name = e[0]
231
+ epfl = e[1].to_f
232
+ afov = e[2].to_f
233
+ tag_ep = @eptag[i] ? ' ' + '▐'.b.fg(46) : ' '
234
+ txt = tag_ep
235
+ txt += name.to_s.ljust(18)
236
+ txt += epfl.to_s.rjust(7)
237
+ txt += afov.to_s.rjust(8)
238
+ txt += magx(tfl, epfl).rts(0).rjust(8) + 'x'
239
+ fov = tfov(tfl, epfl, afov)
240
+ deg = fov.to_i
241
+ min = ((fov - deg) * 60).to_i
242
+ sec = ((fov - deg - min/60.0) * 3600).to_i
243
+ min_s = format('%02d', min)
244
+ sec_s = format('%02d', sec)
245
+ dms = "#{deg}°#{min_s}'#{sec_s}\""
246
+ txt += dms.rjust(11)
247
+ txt += pupl(app, tfl, epfl).rts(1).rjust(6)
248
+ txt += (magx(tfl, epfl) * 2).rts(0).rjust(8) + 'x'
249
+ tfov2 = tfov(tfl, epfl, afov) / 2.0
250
+ deg2 = tfov2.to_i
251
+ min2 = ((tfov2 - deg2) * 60).to_i
252
+ sec2 = ((tfov2 - deg2 - min2/60.0) * 3600).to_i
253
+ min2_s = format('%02d', min2)
254
+ sec2_s = format('%02d', sec2)
255
+ dms2 = "#{deg2}°#{min2_s}'#{sec2_s}\""
256
+ txt += dms2.rjust(11)
257
+ txt += (pupl(app, tfl, epfl) / 2).rts(1).rjust(6)
258
+ txt = txt.fg(248) if i != @pEP.index
259
+ txt += ep_nice(app, tfl, epfl)
260
+ txt[0] = '→' if i == @pEP.index
261
+ # ANSI safe padding
262
+ pad = @pEP.w - Rcurses.display_width(txt.pure)
263
+ pad = 0 if pad.negative?
264
+ txt += ' ' * pad
265
+ txt = txt.bg(234) if i == @pEP.index
266
+ @pEP.text += txt
469
267
  end
470
- @w_ts.clr_from_cur_line
471
- end
472
- def magx(d, f, r)
473
- m = d * r
474
- e = f / m
475
- return (m.to_i.to_s + "(" + e.to_i.to_s + ")").rjust(8)
268
+ @pEP.refresh
269
+ @pEPh.full_refresh
476
270
  end
477
271
 
478
- # EYEPIECE FUNCTIONS (middle window, w_ep)
479
- def w_ep_show
480
- @w_ep.setpos(0,0)
481
- scopes = 5
482
- heading = " ".rjust(35)
483
- @w_ep.pa(231, 240, 0, heading)
484
- @ts.each do |scope|
485
- @w_ep.pa(231, 240, Curses::A_BOLD, " ")
486
- heading = scope[0].ljust(22)
487
- @w_ep.pa(172, 240, Curses::A_BOLD, heading)
488
- end
489
- heading = " " * (@w_ep.maxx - @w_ep.curx)
490
- @w_ep.p(heading)
491
- heading = " EYEPIECES FL(mm) AFOV "
492
- heading += "│ xMAGN FOV(dms) XPUP " * @ts.length
493
- heading += " " * (@w_ep.maxx - heading.length).abs
494
- @w_ep.pa(231, 240, Curses::A_BOLD, heading)
495
- @w_ep.fg = 15
496
- @w_ep.bg = 0
497
- @ep.each_with_index do |ep, i|
498
- name = ep[0][0...18]
499
- m = ep[1].truncate(1)
500
- a = ep[2]
501
- out = " " + name.ljust(18)
502
- out += m.to_s.rjust(8)
503
- out += a.to_s.rjust(6) + "°"
504
- attr = Curses::A_BOLD
505
- attr = attr | Curses::A_REVERSE if @epmark == i
506
- attr = attr | Curses::A_UNDERLINE if @eptag.include?(i)
507
- @w_ep.pa(253, 0, attr, out)
508
- @ts.each do |scope|
509
- d = scope[1]
510
- f = scope[2]
511
- mag = (f.to_f/m)
512
- @w_ep.pa(254, 0, attr, " │")
513
- out = mag.truncate(1).to_s.rjust(6)
514
- @w_ep.pa(156, 0, attr, out)
515
- fov = a/mag
516
- deg = fov.to_i
517
- mins = ((fov - fov.to_i) * 60)
518
- min = mins.to_i
519
- sec = ((mins - min) * 60).to_i
520
- deg == 0 ? dgo = " " : dgo = deg.to_s + "°"
521
- mno = min.to_s.rjust(2, " ") + "'"
522
- sco = sec.to_s.rjust(2, " ") + "\""
523
- out = (dgo + mno + sco).rjust(10)
524
- @w_ep.pa(222, 0, attr, out)
525
- out = (d/mag).truncate(1).to_s.rjust(6)
526
- @w_ep.pa(209, 0, attr, out)
272
+ def observe #{{{2
273
+ # Prepare file header
274
+ date = Date.today.iso8601
275
+ file = File.join(Dir.home, "#{date}_observation.txt")
276
+
277
+ obs = +"Observation file: #{file}\n"
278
+ obs << "Observation date: #{date}\n\n"
279
+ obs << "This file lists the intended equipment for the observation date.\n"
280
+ obs << "Reference observations with telescope letter and eyepiece numbers (like A2, B1, etc.)\n\n"
281
+ obs << ("─" * 100) << "\n"
282
+
283
+ enum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
284
+
285
+ # Collect and print selected telescopes
286
+ sel_ts = @tstag.each_index.select { |i| @tstag[i] }
287
+ if sel_ts.empty?
288
+ obs << "No telescope(s) chosen for the observation\n"
289
+ else
290
+ sel_ts.each_with_index do |ts_idx, idx|
291
+ name, app_str, tfl_str = @ts[ts_idx]
292
+ app, tfl = app_str.to_f, tfl_str.to_f
293
+
294
+ obs << format(
295
+ "Telescope (%s): %-15s (%3.0fmm/%4.0fmm f/%.1f)",
296
+ enum[idx], name, app, tfl, tfr(app, tfl)
297
+ )
298
+ obs << format(" Max MAG: %.1f", mlim(app))
299
+ obs << format(" Min SEP: %.2f/%.2f\n", sepd(app), sepr(app))
527
300
  end
528
- @w_ep.p("\n")
529
301
  end
530
- @w_ep.clr_from_cur_line
531
- end
532
302
 
533
- # COMMAND FUNCTIONS (bottom window, w_cm)
534
- def w_cm_show
535
- @w_cm.fill
536
- @w_cm.p(" Telescope v0.2 - By Geir Isene (https://isene.com) - press '?' to display the help text")
537
- end
538
- def w_cm_getstr(pretext, text) # A SIMPLE READLINE-LIKE ROUTINE
539
- Curses.curs_set(1)
540
- Curses.echo
541
- pos = text.length
542
- chr = ""
543
- while chr != "ENTER"
544
- @w_cm.setpos(0,0)
545
- init_pair(250, 250, 238)
546
- text += " " * (@w_cm.maxx - text.length) if text.length < @w_cm.maxx
547
- @w_cm.attron(color_pair(250)) { @w_cm << pretext + text }
548
- @w_cm.setpos(0,pretext.length + pos)
549
- @w_cm.refresh
550
- chr = getchr
551
- if chr == "C-G"
552
- Curses.curs_set(0)
553
- Curses.noecho
554
- @w_cm.update = true
555
- return ""
556
- end
557
- case chr
558
- when 'RIGHT'
559
- pos += 1 unless pos > text.length
560
- when 'LEFT'
561
- pos -= 1 unless pos == 0
562
- when 'HOME'
563
- pos = 0
564
- when 'END'
565
- pos = text.length
566
- when 'DEL'
567
- text[pos] = ""
568
- when 'BACK'
569
- unless pos == 0
570
- pos -= 1
571
- text[pos] = ""
572
- end
573
- when 'WBACK'
574
- unless pos == 0
575
- until text[pos - 1] == " " or pos == 0
576
- pos -= 1
577
- text[pos] = ""
578
- end
579
- if text[pos - 1] == " "
580
- pos -= 1
581
- text[pos] = ""
582
- end
303
+ obs << ("─" * 100) << "\n"
304
+
305
+ # Collect and print selected eyepieces (with per‐telescope TFOV)
306
+ sel_ep = @eptag.each_index.select { |i| @eptag[i] }
307
+ if sel_ep.empty?
308
+ obs << "No eyepiece(s) chosen for the observation\n"
309
+ else
310
+ sel_ep.each_with_index do |ep_idx, ep_num|
311
+ name, epfl_str, afov_str = @ep[ep_idx]
312
+ epfl, afov = epfl_str.to_f, afov_str.to_f
313
+
314
+ obs << format(
315
+ "Eyepiece (%d): %-15s (%4.0fmm/%3.0f°) ",
316
+ ep_num+1, name, epfl, afov
317
+ )
318
+
319
+ sel_ts.each_with_index do |ts_idx, ts_num|
320
+ app, tfl = @ts[ts_idx][1].to_f, @ts[ts_idx][2].to_f
321
+ mag = magx(tfl, epfl)
322
+ tf = tfov(tfl, epfl, afov)
323
+
324
+ # Break TFOV into deg, min, sec
325
+ deg = tf.to_i
326
+ min = ((tf - deg)*60).to_i
327
+ sec = ((tf - deg - min/60.0)*3600).to_i
328
+ angle = format(
329
+ "%2s%2d'%2d\"",
330
+ (deg.zero? ? " " : "#{deg}°"),
331
+ min, sec
332
+ )
333
+ obs << format(
334
+ "%s:%5.1fx (%s) ",
335
+ enum[ts_num], mag, angle
336
+ )
583
337
  end
584
- when 'LDEL'
585
- text = ""
586
- pos = 0
587
- when /^.$/
588
- text.insert(pos,chr)
589
- pos += 1
338
+
339
+ obs << "\n"
590
340
  end
591
341
  end
592
- curstr = text
593
- Curses.curs_set(0)
594
- Curses.noecho
595
- return curstr
342
+
343
+ obs << ("─" * 100) << "\n\n"
344
+ obs << ("Object: Equipment: Observation:\n" * 8)
345
+ @pObs.clear
346
+ @pObs.say(obs)
347
+ @pST.say(" Press any key to continue")
348
+ File.write(file, obs)
349
+ getchr
596
350
  end
597
351
 
598
- # MAIN PROGRAM
599
- loop do # OUTER LOOP - (catching refreshes via 'r')
600
- @break = false # Initialize @break variable (set if user hits 'r')
601
- begin # Create the four windows/panels
602
- if Curses.stdscr.maxx < 140
603
- break
604
- end
605
- Curses.stdscr.bg = 236
606
- Curses.stdscr.fg = 236
607
- Curses.stdscr.fill
608
- maxx = Curses.cols
609
- maxy = Curses.lines
610
- # Curses::Window.new(h,w,y,x)
611
- @w_ts = Curses::Window.new(7, maxx - 2, 1, 1)
612
- @w_ep = Curses::Window.new(maxy - 10, maxx - 2, 8, 1)
613
- @w_cm = Curses::Window.new(1, maxx, maxy - 1, 0)
614
- @w_ts.fg, @w_ts.bg = 15, 0
615
- @w_ep.fg, @w_ep.bg = 255, 232
616
- @w_cm.fg, @w_cm.bg = 233, 246
617
- @w_ts.clr
618
- @w_ep.clr
619
- @w_cm.fill
620
- loop do # INNER, CORE LOOP
621
- w_ts_show
622
- w_ep_show
623
- w_cm_show
624
- main_getkey # Get key from user
625
- break if @break # Break to outer loop, redrawing windows, if user hit 'r'
626
- if Curses.cols != maxx or Curses.lines != maxy # break on terminal resize
627
- close_screen
628
- puts " You must run Telescope with a minimum tarminal width of 140 chracters."
629
- break
630
- end
352
+ # PANE SETUP {{{1
353
+ # Top telescopes, eyepieces below, status at bottom
354
+ @max_h, @max_w = IO.console.winsize
355
+ @pTS = Pane.new( 2, 2, @max_w - 2, 8, nil, nil)
356
+ @pTSh = Pane.new( 2, 2, @max_w - 2, 1, 255, "00524b")
357
+ @pTSa = Pane.new( 1, @max_h, @max_w, 1, 255, "00524b")
358
+ @pEP = Pane.new( 2, 11, @max_w - 2, @max_h - 12, nil, nil)
359
+ @pEPh = Pane.new( 2, 11, @max_w - 2, 1, 255, "4c3c1d")
360
+ @pEPa = Pane.new( 1, @max_h, @max_w, 1, 255, "4c3c1d")
361
+ @pST = Pane.new( 1, @max_h, @max_w, 1, 255, 236)
362
+
363
+ @pHlp = Pane.new( @max_w/2 - 30, @max_h/2 - 13, 60, 26, 252, 233)
364
+ @pObs = Pane.new( 8, 8, @max_w - 16, @max_h - 16, 231, 233)
365
+
366
+ @pTSh.text = ' TELESCOPES APP(mm) FL(mm) F/? <MGN xEYE MINx MAXx SEP-R SEP-D *FLD GLXY PLNT DBL* >2*< MOON SUN'.b
367
+ @pEPh.text = ' EYEPIECES FL(mm) AFOV'.b
368
+ @pEPh.text += ' MAGX TFOV PPL 2blw tfov ppl'.b.i.fg("00827b")
369
+ @pEPh.text += ' *FLD GLXY PLNT DBL* >2*<'.bg("4c3c1d")
370
+ @pST.text = ' t/e = Add telescope/eyepiece, ENTER = Edit item, q/Q = Quit, ? = Help'
371
+ @pHlp.border = true
372
+ @pObs.border = true
373
+
374
+ @pTSa.record = true
375
+ @pEPa.record = true
376
+ @pTS.index = 0
377
+ @pEP.index = 0
378
+ @sort_ts = false
379
+ @sort_ep = false
380
+ @ts_unsorted = @ts.dup
381
+ @ep_unsorted = @ep.dup
382
+ @focus = @pTS
383
+ @current = @ts
384
+
385
+ # TRAP WIN SIZE CHANGE {{{1
386
+ Signal.trap('WINCH') do
387
+ @h, @w = IO.console.winsize
388
+ refresh_all
389
+ end
390
+
391
+ # MAIN LOOP {{{1
392
+ refresh_all
393
+ loop do
394
+ render_ts
395
+ render_ep
396
+ @pST.text = ' t/e = Add telescope/eyepiece, ENTER = Edit item, q/Q = Quit, ? = Help'
397
+ @pST.full_refresh
398
+ ch = getchr
399
+ case ch
400
+ when 'q'
401
+ File.write(SAVE, "@ts = #{@ts.inspect}\n@ep = #{@ep.inspect}\n") && exit
402
+ when 'Q'; exit
403
+ when '?'
404
+ @pHlp.full_refresh
405
+ @pHlp.say(HELP1)
406
+ getchr
407
+ @pHlp.say(HELP2)
408
+ getchr
409
+ @pHlp.say(HELP3)
410
+ getchr
411
+ refresh_all
412
+ when 'r'
413
+ refresh_all
414
+ when 't'
415
+ inp=@pTSa.ask('name, app, fl: ','').split(', ')
416
+ next unless inp.size == 3
417
+ @ts<<inp
418
+ @tstag<<false
419
+ @ts_unsorted << inp # keep master list updated
420
+ @current = @ts
421
+ when 'e'
422
+ inp=@pEPa.ask('name, fl, afov: ','').split(', ')
423
+ next unless inp.size == 3
424
+ @ep<<inp
425
+ @eptag<<false
426
+ @ep_unsorted << inp # keep master list updated
427
+ @current = @ep
428
+ when 'ENTER'
429
+ val = @current[@focus.index].join(', ')
430
+ arr=@pST.ask('Edit: ', val).split(', '); next unless arr.size==3
431
+ @current[@focus.index] = arr
432
+ when 'D'
433
+ if @current.equal?(@ts)
434
+ removed = @ts.delete_at(@focus.index)
435
+ @ts_unsorted.delete(removed)
436
+ @tstag.delete_at(@focus.index)
437
+ else
438
+ removed = @ep.delete_at(@focus.index)
439
+ @ep_unsorted.delete(removed)
440
+ @eptag.delete_at(@focus.index)
441
+ end
442
+ when 'TAB'
443
+ @focus = (@focus == @pTS ? @pEP : @pTS)
444
+ @current = (@current == @ts ? @ep : @ts)
445
+ @pTS.border = false
446
+ @pEP.border = false
447
+ @focus.border = true
448
+ @pTS.border_refresh
449
+ @pEP.border_refresh
450
+ when 'UP'
451
+ @focus.index -= 1
452
+ @focus.index = @current.length - 1 if @focus.index < 0
453
+ when 'DOWN'
454
+ @focus.index += 1
455
+ @focus.index = 0 if @focus.index > @current.length - 1
456
+ @focus == :ts ? @cursor_ts = 0 : @cursor_ep = 0
457
+ when 'HOME'
458
+ @focus.index = 0
459
+ when 'END'
460
+ @focus.index = @current.length - 1
461
+ when 'S-UP'
462
+ @current.insert([@focus.index - 1, 0].max, @current.delete_at(@focus.index))
463
+ @focus.index -= 1 if @focus.index != 0
464
+ when 'S-DOWN'
465
+ @current.insert([@focus.index + 1, @current.size - 1].min, @current.delete_at(@focus.index))
466
+ @focus.index += 1 if @focus.index != @current.size - 1
467
+ when 'o' # Toggle-sort the currently focused list
468
+ if @current.equal?(@ts) # toggling telescopes
469
+ @sort_ts = !@sort_ts
470
+ @ts = if @sort_ts
471
+ @ts_unsorted.sort_by { |t| t[1].to_i }
472
+ else
473
+ @ts_unsorted.dup
474
+ end
475
+ @current = @ts
476
+ @pTS.index = [@pTS.index, @ts.size - 1].min
477
+ else # toggling eyepieces
478
+ @sort_ep = !@sort_ep
479
+ @ep = if @sort_ep
480
+ @ep_unsorted.sort_by { |e| e[1].to_i }
481
+ else
482
+ @ep_unsorted.dup
483
+ end
484
+ @current = @ep
485
+ @pEP.index = [@pEP.index, @ep.size - 1].min
631
486
  end
632
- ensure # On exit: close curses, clear terminal
633
- close_screen
487
+ when ' ' # SPACE: tag/untag current entry
488
+ if @current.equal?(@ts)
489
+ idx = @pTS.index
490
+ @tstag[idx] = !@tstag[idx]
491
+ else
492
+ idx = @pEP.index
493
+ @eptag[idx] = !@eptag[idx]
494
+ end
495
+ when 'u'
496
+ @tstag.fill(false)
497
+ @eptag.fill(false)
498
+ when 'C-O'
499
+ observe
634
500
  end
635
501
  end
636
502
 
637
- # vim: set sw=2 sts=2 et fdm=syntax fdn=2 fcs=fold\:\ :
503
+ # vim: set sw=2 sts=2 et filetype=ruby fdm=marker fdn=2 fcs=fold\:\ :
metadata CHANGED
@@ -1,40 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telescope-term
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.7'
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-05 00:00:00.000000000 Z
11
+ date: 2025-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: curses
14
+ name: rcurses
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 1.3.2
19
+ version: '3.5'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '1.3'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 1.3.2
26
+ version: '3.5'
33
27
  description: 'With this program you can list your telescopes and eyepieces and get
34
28
  a set of calculations done for each scope and for the combination of scope and eyepiece.
35
- Easy interface. Run the program, then hit ''?'' to show the help file. New in v0.6.0:
36
- Added sorting of telescopes (via ''T'') and eyepieces (''E''). New in 1.7: Highlighted
37
- all of current selection for better readability.'
29
+ Easy interface. Run the program, then hit ''?'' to show the help file. Version 1.0:
30
+ A full rewrite using the rcurses library (https://github.com/isene/rcurses) - lots
31
+ of improvements. 1.1: Removed tty startup/exit codes as rcurses now handles that.'
38
32
  email: g@isene.com
39
33
  executables:
40
34
  - telescope
@@ -47,7 +41,7 @@ licenses:
47
41
  - Unlicense
48
42
  metadata:
49
43
  source_code_uri: https://github.com/isene/telescope
50
- post_install_message:
44
+ post_install_message:
51
45
  rdoc_options: []
52
46
  require_paths:
53
47
  - lib
@@ -62,8 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
56
  - !ruby/object:Gem::Version
63
57
  version: '0'
64
58
  requirements: []
65
- rubygems_version: 3.1.2
66
- signing_key:
59
+ rubygems_version: 3.4.20
60
+ signing_key:
67
61
  specification_version: 4
68
- summary: Terminal program to aid the amateur astronomer.
62
+ summary: Terminal program for the amateur astronomer.
69
63
  test_files: []