astromapper 1.0.2

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.
@@ -0,0 +1,33 @@
1
+ module Astromapper
2
+ module Builder
3
+ class Sector < Astromapper::Builder::Base
4
+ def constitute
5
+ @volumes = []
6
+ 40.times do |r|
7
+ 32.times do |c|
8
+ next unless has_system?
9
+ v = Volume.new(c+1,r+1)
10
+ @volumes << v unless v.empty?
11
+ end
12
+ end
13
+ self
14
+ end
15
+ def has_system?
16
+ case
17
+ when config['density'] == 'extra_galactic' then (1.d100 <= 1)
18
+ when config['density'] == 'rift' then (1.d100 <= 3)
19
+ when config['density'] == 'sparse' then (1.d100 <= 17)
20
+ when config['density'] == 'scattered' then (1.d100 <= 33)
21
+ when config['density'] == 'dense' then (1.d100 <= 66)
22
+ when config['density'] == 'cluster' then (1.d100 <= 83)
23
+ when config['density'] == 'core' then (1.d100 <= 91)
24
+ else (d100 <= 50) # Standard
25
+ end
26
+ end
27
+ def to_file
28
+ file = Astromapper.output_file('sector')
29
+ File.open(file,'w').write(@volumes.map{|v| v.to_ascii}.join("\n"))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,198 @@
1
+ module Astromapper
2
+ module Builder
3
+ class Star < Astromapper::Builder::Base
4
+ attr_accessor :star_size, :mass, :bode_constant, :biozone, :type_dm, :size_dm, :orbits, :primary, :orbit, :id, :volume, :world, :companions
5
+ # @@stars = {}
6
+ STAR_CHART = {
7
+ #type => 0)example, 1)temp, 2)lux, 3)mass, 4)radius
8
+ 'B0' => ['Becrux', 30000, 16000, 16.0, 5.70],
9
+ 'B2' => ['Spica', 22000, 8300, 10.5, 5.10],
10
+ 'B5' => ['Achernar', 15000, 750, 5.40, 3.70],
11
+ 'B8' => ['Rigel', 12500, 130, 3.50, 2.70],
12
+ 'A0' => ['Sirius A', 9500, 63, 2.60, 2.30],
13
+ 'A2' => ['Fomalhaut', 9000, 40, 2.20, 2.00],
14
+ 'A5' => ['Altair', 8700, 24, 1.90, 1.80],
15
+ 'F0' => ['Gamma Virginis', 7400, 9.0, 1.60, 1.50],
16
+ 'F2' => ['.', 7100, 6.3, 1.50, 1.30],
17
+ 'F5' => ['Procyon A', 6400, 4.0, 1.35, 1.20],
18
+ 'G0' => ['Alpha Centauri A', 5900, 1.45, 1.08, 1.05],
19
+ 'G2' => ['The Sun', 5800, 1.00, 1.00, 1.00],
20
+ 'G5' => ['Mu Cassiopeiae', 5600, 0.70, 0.95, 0.91],
21
+ 'G8' => ['Tau Ceti', 5300, 0.44, 0.85, 0.87],
22
+ 'K0' => ['Pollux', 5100, 0.36, 0.83, 0.83],
23
+ 'K2' => ['Epsilon Eridani', 4830, 0.28, 0.78, 0.79],
24
+ 'K5' => ['Alpha Centauri B', 4370, 0.18, 0.68, 0.74],
25
+ 'M0' => ['Gliese 185', 3670, 0.075, 0.47, 0.63],
26
+ 'M2' => ['Lalande 21185', 3400, 0.03, 0.33, 0.36],
27
+ 'M4' => ['Ross 128', 3200, 0.0005, 0.20, 0.21],
28
+ 'M6' => ['Wolf 359', 3000, 0.0002, 0.10, 0.12]
29
+ }
30
+ INNER_LIMIT = {
31
+ 'O' => [ 16, 13, 10 ],
32
+ 'B' => [ 10, 6.3, 5.0, 4.0, 3.8, 0.6, 0],
33
+ 'A' => [ 4, 1, 0.4, 0, 0, 0, 0],
34
+ 'F' => [ 4, 1, 0.3, 0.1, 0, 0, 0],
35
+ 'G' => [ 3.1, 1, 0.3, 0.1, 0, 0, 0],
36
+ 'K' => [ 2.5, 1, 0.3, 0.1, 0, 0, 0],
37
+ 'M' => [ 2, 1, 0.3, 0.1, 0, 0, 0],
38
+ 'D' => [ 0 ],
39
+ }
40
+ BIOZONE = {
41
+ 'O' => [ [790,1190], [630,950], [500,750] ],
42
+ 'B' => [ [500,700], [320,480], [250,375], [200,300], [180,270], [30,45] ],
43
+ 'A' => [ [200,300], [50,75], [20,30], [5.0,7.5], [4.0,6.0], [3.1,4.7] ],
44
+ 'F' => [ [200,300], [50,75], [13,19], [2.5,3.7], [2.0,3.0], [1.6,2.4], [0.5,0.8] ],
45
+ 'G' => [ [200,300], [50,75], [13,19], [2.5,3.7], [2.0,3.0], [1.6,2.4], [0.5,0.8] ],
46
+ 'K' => [ [125,190], [50,75], [13,19], [4.0,5.9], [1.0,1.5], [0.5,0.6], [0.2,0.3] ],
47
+ 'M' => [ [100,150], [50,76], [16,24], [5.0,7.5], [0,0], [0.1,0.2], [0.1,0.1] ],
48
+ 'D' => [ [0.03, 0.03] ],
49
+ }
50
+ SPECTRAL = {
51
+ 'O' => [9],
52
+ 'B' => [0,2,5,8],
53
+ 'A' => [0,2,5],
54
+ 'F' => [0,2,5],
55
+ 'G' => [0,2,5,8],
56
+ 'K' => [0,2,5],
57
+ 'M' => [0,2,4,6]
58
+ }
59
+ MASS = {
60
+ 'O' => [70, 60, 0, 0, 50, 0 ],
61
+ 'B' => [50, 40, 35, 30, 20, 10],
62
+ 'A' => [30, 16, 10, 6, 4, 3],
63
+ 'F' => [15, 13, 8, 2.5, 2.2, 1.9],
64
+ 'G' => [12, 10, 6, 2.7, 1.8, 1.1, 0.8],
65
+ 'K' => [15, 12, 6, 3, 2.3, 0.9, 0.5],
66
+ 'M' => [20, 16, 8, 4, 0.3, 0.2],
67
+ 'D' => [0.8,0.8,0.8,0.8,0.8,0.8,]
68
+ }
69
+ COMPANION_SEPARATION = [[0.05]*2, [0.5]*3, [2.0]*2, [10.0]*3, [50.0] * 10].flatten
70
+ BODE_RATIO = [[0.3] * 4, [0.35] * 3, [0.4] * 4].flatten
71
+ def initialize(volume, primary=nil,ternary=0)
72
+ @volume = volume
73
+ @primary = primary
74
+ @orbits = []
75
+ @companions = []
76
+ @world = nil
77
+
78
+ @type_dm = 0
79
+ @size_dm = 0
80
+ @has_gg = false
81
+
82
+ if primary.nil?
83
+ @orbit = 0
84
+ @type_dm = (toss(2,0) + @volume.star_dm ).max(12)
85
+ @size_dm = (toss(2,0) + 0 ).max(12)
86
+ @star_type = %w{B B A M M M M M K G F F F}[@type_dm]
87
+ @star_size = %w{0 1 2 3 4 5 5 5 5 5 5 6 500}[@size_dm].to_i
88
+ else
89
+ separation = (toss(2,0) * COMPANION_SEPARATION[toss(3) + (4 * ternary) - 2]).round(2) # Gurps Space 4e p.105
90
+
91
+ @orbit = au_to_orbit(separation) - 1
92
+ @star_type = %w{X B A F F G G K K M M M M}[(toss(2,0) + primary.type_dm).max(12)]
93
+ @star_size = %w{0 1 2 3 4 500 500 5 5 6 500 500 500 500}[(toss(2,0) + primary.size_dm).max(12)].to_i
94
+ end
95
+ @spectral = @star_type + SPECTRAL[@star_type].sample.to_s
96
+ @star_size ||= 500
97
+
98
+ @bode_constant = (@star_type=='M' and @star_size==5) ? 0.2 : BODE_RATIO[toss]
99
+
100
+ if @star_size == 500
101
+ @star_subtype = (true) ? 'B' : @star_type
102
+ @star_type = 'D'
103
+ end
104
+
105
+ dm = 0
106
+ dm += 4 if @star_size == 3
107
+ dm += 8 if @star_size < 3
108
+ dm -= 4 if @star_type == 'M'
109
+ dm -= 2 if @star_type == 'K'
110
+
111
+ # Populate Orbits
112
+ (toss(2,0) + dm).whole.times do |i|
113
+ @orbits << Orbit.new(self,i).populate unless orbit_to_au(i) > outer_limit
114
+ @world = @orbits.last if @orbits.last.is_a?(World)
115
+ end
116
+ @world.gas_giant = (@orbits.map{|o| o.kid}.include?('G')) ? 'G' : '.' unless @world.nil?
117
+ prune!
118
+ end
119
+ def prune! # Ensure last orbits are not empty.
120
+ @orbits.each_index { |x| @orbits[x] = Orbit.new(self,x) if @orbits[x].nil?}
121
+ c = @orbits
122
+ # exit
123
+ tk = false
124
+ @orbits = @orbits.sort{|b,a| a.orbit_number <=> b.orbit_number}.map {|o| tk = true unless (o.kid == '.' or tk); o if tk }.reverse.compact
125
+ # @orbits.each_index { |x| @orbits[x] = Orbit.new(self,x) if @orbits[x].nil?}
126
+
127
+ return if @orbits.size < 2
128
+ @orbits.length.times do |i|
129
+ @orbits[i].orbit_number = i
130
+ @orbits[i].au = self.orbit_to_au(i)
131
+ end
132
+
133
+ end
134
+ def orbit_to_au(o)
135
+ inner_limit + (self.bode_constant * (2 ** o)).round(1)
136
+ end
137
+ def au_to_orbit(au)
138
+ constant = (@primary.nil?) ? @bode_constant : @primary.bode_constant
139
+ (Math.log(au / constant) / Math.log(2) ).round(2).abs - inner_limit
140
+ end
141
+ def companions=(star)
142
+ orbit = star.orbit.abs
143
+ companion = Companion.new(self, orbit, star)
144
+
145
+ # Gurps Space 4e p.107 - Clear Forbidden orbits
146
+ inner = au_to_orbit(companion.au * 0.67).floor
147
+ outer = au_to_orbit(companion.au * 3).ceil
148
+ @forbidden = (inner .. outer)
149
+ @forbidden.each { |x| @orbits[x] = nil }
150
+ @orbits[orbit - 1] = companion
151
+ @companions << star
152
+ prune!
153
+ end
154
+ def to_s; kid; end
155
+ def kid; 'C'; end
156
+ def radius; (155000 * Math.sqrt(luminosity)) ** 2; end # Gurps Space 4e p. 104
157
+ def snow_line; 4.85 * Math.sqrt(luminosity); end # Gurps Space 4e p. 106
158
+ def outer_limit; 40 * mass; end # Gurps Space 4e p. 107
159
+ def orbits_to_ascii
160
+ return '' if @orbits.empty?
161
+ "\n" + @orbits.map{|o| o.to_ascii}.join("\n") + "\n"
162
+ end
163
+ def crib
164
+ stars = [classification]
165
+ @companions.each { |s| stars << s.classification }
166
+ "%-17s %-16s" % [stars.join('/'), @orbits.map{|o| o.kid}.join('')]
167
+ end
168
+ def to_ascii
169
+ classification
170
+ end
171
+ def classification
172
+ return @star_type + @star_subtype if (@star_type == 'D')
173
+ "#{@spectral}#{@star_size.roman}"
174
+ end
175
+ def world?
176
+ return @orbits.join('').include?('W')
177
+ return false
178
+ end
179
+ def column; @volume.column; end
180
+ def row; @volume.row; end
181
+ def sector; @volume.sector; end
182
+ def location; @volume.location; end
183
+ def type; @star_type; end
184
+ def type=(s); @star_type = s; end
185
+ def size; @star_size; end
186
+ def size=(s); @star_size = s; end
187
+ def inner_limit; limit; end
188
+ def limit
189
+ return 0 if @star_size.nil?
190
+ INNER_LIMIT[@star_type][@star_size % 10]
191
+ end
192
+ def biozone; BIOZONE[@star_type][@star_size % 10] or []; end
193
+ def luminosity; STAR_CHART[@spectral][2]; end
194
+ def temperature; @temperature = STAR_CHART[@spectral][1].around(20) if @temperature.nil?; end
195
+ def mass; MASS[@star_type][@star_size] || 0.3; end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,38 @@
1
+ module Astromapper
2
+ module Builder
3
+ class Volume < Astromapper::Builder::Base
4
+
5
+ attr_accessor :gas_giant, :name
6
+
7
+ def initialize(c,r)
8
+ @name = (config['named']) ? Astromapper.names.sample : "%02d%02d" % [c,r]
9
+ @column = c
10
+ @row = r
11
+ @star = Star.new(self)
12
+ [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2][toss(2,0)].times do |i|
13
+ @star.companions = Star.new(self, @star,i)
14
+ end
15
+ end
16
+
17
+ def star_dm
18
+ return 0 if @atmo.nil? or @popx.nil?
19
+ ((4..9).include?(@atmo) or @popx > 7) ? 4 : 0
20
+ end
21
+
22
+ def to_ascii
23
+ w = @star.world
24
+ sumy = "%s %s %s %s %s\t%-15s\t%-8s\t%s\t%s" % [location, w.uwp, w.temp, w.bases, w.travel_code, w.trade_codes.join(','), w.factions.join(','), @star.crib, @name]
25
+ sumy += @star.orbits_to_ascii
26
+ return sumy
27
+ end
28
+
29
+ def empty?
30
+ return true if @star.world.nil? or @star.world.empty? or !@star.world?
31
+ end
32
+
33
+ def location
34
+ "%02d%02d" % [@column,@row]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,60 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'thor'
3
+ require 'Astromapper/version'
4
+ module Astromapper
5
+ class Cli < Thor
6
+ FORMATS = %w[pdf draft proof html epub mobi txt]
7
+ check_unknown_options!
8
+
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+ def initialize(args = [], options = {}, config = {})
13
+ # if (config[:current_task] || config[:current_command]).name == "new" && args.empty?
14
+ # raise Error, "The e-Book path is required. For details run: Astromapper help new"
15
+ # end
16
+ super
17
+ end
18
+
19
+ desc "new", "Create Astromapper Directory"
20
+ map %w(-n new -c) => :create
21
+ def create(path)
22
+ say "Voices of billions cry out in terror at the creation of '#{path}'"
23
+ generator = Generator.new
24
+ generator.destination_root = path.squish.gsub(' ','-')
25
+ generator.invoke_all
26
+ end
27
+
28
+ desc "build", "Generate a map of {sector / domain}"
29
+ map %w{-b --build generate} => :build
30
+ def build(type='sector')
31
+ say "Building #{type}: #{config['name'].inspect}"
32
+ Astromapper::Exporter.run(root_dir, options)
33
+ end
34
+
35
+ desc "svg", "Convert ASCII output to SVG"
36
+ map %w{-s --svg} => :svg
37
+ def svg
38
+ source = Astromapper.output_file('sector')
39
+ say "Converting #{source} to SVG"
40
+ s = Svg.new(source)
41
+ s.convert
42
+ say "SVG available at #{Astromapper.output_file('svg')}"
43
+ end
44
+
45
+ desc "version", "Prints the Astromapper's version information"
46
+ map %w(-v --version) => :version
47
+ def version
48
+ say "Astromapper version #{Astromapper::VERSION}"
49
+ end
50
+
51
+ private
52
+ def config
53
+ # YAML.load_file(config_path).with_indifferent_access
54
+ Astromapper.config
55
+ end
56
+ def root_dir
57
+ @root ||= Pathname.new(Dir.pwd)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,64 @@
1
+ module Astromapper
2
+ class Exporter
3
+ def self.run(root_dir, options)
4
+ exporter = new(root_dir, options)
5
+ exporter.export!
6
+ end
7
+
8
+ attr_accessor :root_dir
9
+ attr_accessor :options
10
+
11
+ def initialize(root_dir, options)
12
+ @root_dir = root_dir
13
+ @options = options
14
+ end
15
+
16
+ def ui
17
+ @ui ||= Thor::Base.shell.new
18
+ end
19
+
20
+ def export!
21
+ helper = root_dir.join("config/helper.rb")
22
+ load(helper) if helper.exist?
23
+ exported = []
24
+
25
+ sector = Builder::Sector.constitute(root_dir)
26
+
27
+ sector.to_file
28
+
29
+ # export_pdf = [nil, "pdf"].include?(options[:only])
30
+ # export_html = [nil, "html", "mobi", "epub"].include?(options[:only])
31
+ # export_epub = [nil, "mobi", "epub"].include?(options[:only])
32
+ # export_mobi = [nil, "mobi"].include?(options[:only])
33
+ # export_txt = [nil, "txt"].include?(options[:only])
34
+
35
+ # exported = []
36
+ # exported << Parser::PDF.parse(root_dir) if export_pdf && Dependency.xelatex?# && Dependency.prince?
37
+ # exported << Parser::HTML.parse(root_dir) if export_html
38
+ # epub_done = Parser::Epub.parse(root_dir) if export_epub
39
+ # exported << epub_done
40
+ # exported << Parser::Mobi.parse(root_dir) if export_mobi && epub_done && Dependency.kindlegen?
41
+ # exported << Parser::Txt.parse(root_dir) if export_txt && Dependency.html2text?
42
+
43
+ if exported.all?
44
+ color = :green
45
+ message = options[:auto] ? "exported!" : "** Map has been exported"
46
+
47
+ # Notifier.notify(
48
+ # # :image => Astromapper::ROOT.join("templates/ebook.png"),
49
+ # :title => "Astromapper",
50
+ # :message => "Your \"#{config[:title]}\" map has been exported!"
51
+ # )
52
+ else
53
+ color = :red
54
+ message = options[:auto] ? "could not be exported!" : "** e-book couldn't be exported"
55
+ end
56
+
57
+ ui.say message, color
58
+ end
59
+
60
+ def config
61
+ Astromapper.config(root_dir)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def roll(n=1)
3
+ n.times.map{ self.rotate!; self.first }.inject{|s,x| s + x}
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Float
2
+ def tweak
3
+ self.round(2)
4
+ end
5
+ end
@@ -0,0 +1,44 @@
1
+ class Integer
2
+ def dn(n)
3
+ (1..self).inject(0) { |a, e| a + rand(n) + 1 }
4
+ end
5
+ def d3
6
+ dn(3)
7
+ end
8
+ def d6
9
+ dn(6)
10
+ end
11
+ def d100
12
+ dn(100)
13
+ end
14
+ def hexd
15
+ return 'F' if self > 15
16
+ self.whole.to_s(16).upcase
17
+ end
18
+ def whole
19
+ return 0 if self < 0
20
+ return self
21
+ end
22
+ def natural
23
+ return 1 if self < 1
24
+ return self
25
+ end
26
+ def roman
27
+ return 'D' if self ==500
28
+ return %w{Ia Ib II III IV V VI VII VIII IX X}[self]
29
+ end
30
+ def max(n)
31
+ return n if self > n
32
+ return self
33
+ end
34
+ def min(n)
35
+ return n if self < n
36
+ return self
37
+ end
38
+ def tweak
39
+ return self
40
+ end
41
+ def to_string
42
+ return self.tweak
43
+ end
44
+ end