mooncats 0.1.2 → 1.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.
- checksums.yaml +4 -4
- data/Manifest.txt +13 -1
- data/README.md +19 -18
- data/Rakefile +1 -1
- data/config/cryptocats/000.txt +44 -0
- data/config/cryptocats/001.txt +44 -0
- data/config/cryptocats/002.txt +43 -0
- data/config/cryptocats/003.txt +43 -0
- data/config/v2/008.txt +23 -0
- data/config/v2/009.txt +20 -0
- data/config/v2/010.txt +29 -0
- data/config/v2/011.txt +26 -0
- data/config/v2/012.txt +22 -0
- data/config/v2/013.txt +20 -0
- data/config/v2/014.txt +27 -0
- data/config/v2/015.txt +29 -0
- data/lib/mooncats.rb +23 -5
- data/lib/mooncats/composite.rb +1 -1
- data/lib/mooncats/dataset.rb +11 -3
- data/lib/mooncats/design.rb +127 -0
- data/lib/mooncats/image.rb +86 -141
- data/lib/mooncats/structs.rb +111 -20
- data/lib/mooncats/version.rb +2 -2
- metadata +28 -5
- data/LICENSE.md +0 -116
data/config/v2/014.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
##############################
|
2
|
+
# design: 14 - Pouncing Smiling Tortie Left
|
3
|
+
# size: 17x22
|
4
|
+
###########################
|
5
|
+
|
6
|
+
0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0
|
7
|
+
0 0 1 2 1 0 0 0 1 3 1 0 0 0 0 0 0
|
8
|
+
0 0 1 4 2 1 1 1 3 4 1 0 0 0 0 0 0
|
9
|
+
0 1 1 2 2 2 3 3 3 3 1 1 0 0 0 0 0
|
10
|
+
0 1 2 2 2 2 2 3 3 3 3 1 0 0 0 0 0
|
11
|
+
0 1 2 5 1 2 2 5 1 3 3 1 0 0 0 0 0
|
12
|
+
0 1 2 2 2 2 2 2 3 3 3 1 0 0 0 0 0
|
13
|
+
0 1 2 2 1 2 1 2 1 3 3 1 0 0 0 0 0
|
14
|
+
0 1 2 2 2 1 2 1 3 3 3 1 0 0 0 0 0
|
15
|
+
0 0 1 2 2 2 2 2 3 3 1 1 0 0 0 0 0
|
16
|
+
1 1 3 1 1 1 1 1 1 1 3 1 1 1 0 0 0
|
17
|
+
1 4 3 3 3 3 3 3 3 3 3 3 1 3 1 1 0
|
18
|
+
1 1 1 3 3 1 1 1 2 3 3 3 1 1 3 1 0
|
19
|
+
0 0 1 1 3 1 4 2 2 2 3 3 3 1 3 1 1
|
20
|
+
0 0 0 1 3 3 1 1 2 2 3 3 3 1 1 3 1
|
21
|
+
0 0 0 1 3 4 4 4 3 3 3 3 3 1 1 3 1
|
22
|
+
0 0 0 1 1 4 4 4 3 1 1 1 2 1 2 2 1
|
23
|
+
0 0 0 0 1 4 4 4 3 1 3 2 2 1 2 1 1
|
24
|
+
0 0 0 0 1 2 4 4 3 3 3 2 2 2 1 1 0
|
25
|
+
0 0 0 0 1 2 2 1 1 3 3 2 1 1 1 0 0
|
26
|
+
0 0 0 0 1 1 4 1 1 1 1 2 1 0 0 0 0
|
27
|
+
0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 0 0
|
data/config/v2/015.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
##############################
|
2
|
+
# design: 15 - Stalking Smiling Tortie Left
|
3
|
+
# size: 20x21
|
4
|
+
###########################
|
5
|
+
|
6
|
+
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0
|
7
|
+
0 0 0 0 0 0 0 0 0 0 1 3 3 3 3 1 1 1 0 0
|
8
|
+
0 0 0 0 0 0 0 0 0 0 1 3 1 1 3 3 3 1 0 0
|
9
|
+
0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 2 1 0 0
|
10
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 1 0
|
11
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 1 0
|
12
|
+
0 0 1 1 0 0 0 0 1 1 0 0 0 1 1 1 1 1 1 0
|
13
|
+
0 1 2 1 0 0 0 1 3 1 0 0 1 1 2 2 2 2 1 1
|
14
|
+
1 2 4 2 1 1 1 3 4 3 1 1 1 3 3 2 2 2 3 1
|
15
|
+
1 2 2 2 2 3 3 3 3 3 1 1 3 3 3 3 3 3 3 1
|
16
|
+
1 2 2 2 2 2 3 3 3 3 1 3 3 3 3 3 3 3 3 1
|
17
|
+
1 2 5 1 2 2 5 1 3 3 1 3 3 3 3 3 3 3 1 1
|
18
|
+
1 2 2 2 2 2 2 3 3 3 1 3 3 3 3 3 3 3 1 0
|
19
|
+
1 2 2 1 2 1 2 1 3 3 1 2 2 2 3 4 3 3 1 0
|
20
|
+
1 2 2 2 1 2 1 3 3 3 1 2 2 4 4 4 3 3 1 0
|
21
|
+
0 1 2 2 2 2 2 3 3 1 1 2 4 4 4 1 3 3 3 1
|
22
|
+
0 0 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 2 3 1
|
23
|
+
0 1 1 3 3 3 2 2 2 2 1 1 1 3 3 1 1 2 1 1
|
24
|
+
1 1 3 3 3 1 2 2 1 1 1 1 4 3 1 1 2 2 1 0
|
25
|
+
1 4 3 1 1 1 4 1 1 0 0 1 1 1 1 2 2 1 1 0
|
26
|
+
1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0
|
27
|
+
|
28
|
+
|
29
|
+
|
data/lib/mooncats.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
## 3rd party
|
2
|
-
require '
|
2
|
+
require 'pixelart/base'
|
3
3
|
require 'csvreader'
|
4
4
|
|
5
5
|
|
6
6
|
## extra stdlibs
|
7
|
-
require 'fileutils'
|
8
7
|
require 'optparse'
|
9
8
|
|
10
9
|
|
@@ -13,6 +12,7 @@ require 'optparse'
|
|
13
12
|
require 'mooncats/version' # note: let version always go first
|
14
13
|
require 'mooncats/designs'
|
15
14
|
require 'mooncats/structs'
|
15
|
+
require 'mooncats/design'
|
16
16
|
require 'mooncats/image'
|
17
17
|
require 'mooncats/composite'
|
18
18
|
require 'mooncats/dataset'
|
@@ -63,15 +63,20 @@ module Mooncats
|
|
63
63
|
## note: cut-off optionial 0x
|
64
64
|
cat_id = cat_id[2..-1] if cat_id.start_with?( '0x')
|
65
65
|
|
66
|
+
cat = Image.generate( cat_id )
|
67
|
+
|
66
68
|
cat_name = "mooncat-#{cat_id}"
|
67
69
|
|
68
70
|
## if zoom - add x2,x4 or such
|
69
|
-
|
71
|
+
if opts[:zoom] != 1
|
72
|
+
cat = cat.zoom( opts[:zoom] )
|
73
|
+
cat_name << "_x#{opts[:zoom]}"
|
74
|
+
end
|
70
75
|
|
71
76
|
path = "#{opts[:outdir]}/#{cat_name}.png"
|
72
77
|
puts "==> (#{index+1}/#{args.size}) minting mooncat 0x#{cat_id}; writing to >#{path}<..."
|
73
78
|
|
74
|
-
|
79
|
+
cat.save( path )
|
75
80
|
end
|
76
81
|
|
77
82
|
puts "done"
|
@@ -82,11 +87,24 @@ module Mooncats
|
|
82
87
|
def self.main( args=ARGV )
|
83
88
|
Tool.new.run( args )
|
84
89
|
end
|
85
|
-
|
90
|
+
end ## module Mooncats
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
### add more built-in (load on demand) design series / collections
|
95
|
+
DESIGNS_V2 = Mooncats::DesignSeries.new( "#{Mooncats.root}/config/v2" )
|
96
|
+
DESIGNS_CRYPTOCATS = Mooncats::DesignSeries.new( "#{Mooncats.root}/config/cryptocats" )
|
86
97
|
|
87
98
|
|
88
99
|
### add some convenience shortcuts
|
89
100
|
MoonCats = Mooncats
|
90
101
|
|
91
102
|
|
103
|
+
|
104
|
+
|
105
|
+
###
|
106
|
+
# note: for convenience auto include Pixelart namespace!!! - why? why not?
|
107
|
+
include Pixelart
|
108
|
+
|
109
|
+
|
92
110
|
puts Mooncats.banner # say hello
|
data/lib/mooncats/composite.rb
CHANGED
@@ -10,7 +10,7 @@ CANVAS_HEIGHT = 24
|
|
10
10
|
def initialize( cols=100, rows=255 )
|
11
11
|
@composite = ChunkyPNG::Image.new( cols*CANVAS_WIDTH,
|
12
12
|
rows*CANVAS_HEIGHT,
|
13
|
-
ChunkyPNG::Color::
|
13
|
+
ChunkyPNG::Color::TRANSPARENT ) # why? why not? - use TRANSPARENT (is default?)
|
14
14
|
|
15
15
|
## todo/check - find a better name for cols/rows - why? why not?
|
16
16
|
@cols = cols
|
data/lib/mooncats/dataset.rb
CHANGED
@@ -31,9 +31,17 @@ module Mooncats
|
|
31
31
|
## note: skip all derived column from id e.g.
|
32
32
|
## - r,g,b, etc.
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
## add some more (extra) columns
|
35
|
+
mint = row['mint'].to_i
|
36
|
+
block = row['block'].to_i
|
37
|
+
## expected timestamp format like
|
38
|
+
## 2017-09-06 15:03:43 UTC
|
39
|
+
timestamp = DateTime.strptime( row['timestamp'], '%Y-%m-%d %H:%M:%S %z')
|
40
|
+
|
41
|
+
mooncats << Metadata.new( id,
|
42
|
+
mint: mint,
|
43
|
+
block: block,
|
44
|
+
timestamp: timestamp )
|
37
45
|
end
|
38
46
|
## print "\n" ## add progress
|
39
47
|
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
module Mooncats
|
3
|
+
|
4
|
+
|
5
|
+
class Design
|
6
|
+
def self.find( num ) ## pass in design index number (0 to 127)
|
7
|
+
## todo: add cache (memoize) - why? why not?
|
8
|
+
design = parse( DESIGNS[ num ] )
|
9
|
+
|
10
|
+
puts " design ##{num} (#{design.width}x#{design.height})"
|
11
|
+
## pp design.data
|
12
|
+
## puts
|
13
|
+
|
14
|
+
design
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def self.read( path )
|
19
|
+
text = File.open( path, 'r:utf-8') { |f| f.read }
|
20
|
+
parse( text )
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse( str )
|
24
|
+
## support original "classic" compact single-line format
|
25
|
+
## e.g. 00011111100000000.01113333310000000.13533333331110000....
|
26
|
+
## note: this format needs to get rotated by 90 degree!!!
|
27
|
+
if str.start_with?( /[0-5]+\.[0-5]+\.[0-5]+/ ) ## quick (and dirty) heuristic check
|
28
|
+
data = str.split('.')
|
29
|
+
|
30
|
+
## note: map colors encoded as a string to an array of integers - why? why not?
|
31
|
+
## e.g. "00011111133344411"
|
32
|
+
## =>
|
33
|
+
## [0,0,0,1,1,1,1,1,1,3,3,3,4,4,4,1,1]
|
34
|
+
data = data.map do |row|
|
35
|
+
row.chars.map do |color|
|
36
|
+
color.to_i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
data = data.transpose ## note: rotate by 90 degree!!!!!
|
40
|
+
else ## assume "modern" pixelart format
|
41
|
+
## todo/check: delegate to pixelart parse or such - why? why not?
|
42
|
+
|
43
|
+
data = []
|
44
|
+
str.each_line do |line|
|
45
|
+
line = line.strip
|
46
|
+
next if line.empty? ## skipping empty line in pixel art source
|
47
|
+
next if line.start_with?( '#' ) ## skipping comment line in pixel art source
|
48
|
+
|
49
|
+
## note: allow multiple spaces or tabs to separate pixel codes
|
50
|
+
data << line.split( /[ \t]+/)
|
51
|
+
end
|
52
|
+
## todo/check: change to use strings (instead of nummbers) in the future?
|
53
|
+
## why? why not? stay "compatible" to pixel art format/machinery?
|
54
|
+
data = data.map do |row|
|
55
|
+
row.map do |color|
|
56
|
+
color.to_i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
new( data )
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def initialize( data )
|
65
|
+
@data = data
|
66
|
+
end
|
67
|
+
|
68
|
+
def width
|
69
|
+
## todo/check: use/find max - why? why not? lets you you used "unbalanced" / shortcut lines too
|
70
|
+
@data[0].size
|
71
|
+
end
|
72
|
+
def height() @data.size; end
|
73
|
+
|
74
|
+
|
75
|
+
def each_with_index( &blk )
|
76
|
+
@data.each_with_index { |row, y| blk.call( row, y ) }
|
77
|
+
end
|
78
|
+
def each( &blk )
|
79
|
+
@data.each { |row| blk.call( row ) }
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
def to_txt
|
85
|
+
buf = String.new('')
|
86
|
+
|
87
|
+
@data.each do |row|
|
88
|
+
buf << row.join( ' ' )
|
89
|
+
buf << "\n"
|
90
|
+
end
|
91
|
+
buf
|
92
|
+
end
|
93
|
+
end # class Design
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
###############
|
98
|
+
## todo/check:
|
99
|
+
## find a better way to (auto?) include more designs?
|
100
|
+
class DesignSeries ## find a better name for class - why? why not?
|
101
|
+
def self.build( dir )
|
102
|
+
data = {}
|
103
|
+
paths = Dir.glob( "#{dir}/**.txt" )
|
104
|
+
paths.each do |path|
|
105
|
+
basename = File.basename( path, File.extname( path ) )
|
106
|
+
num = basename.to_i( 10 ) ## use base 10 (e.g. 001 => 1, 002 => 2, etc.)
|
107
|
+
text = File.open( path, 'r:utf-8' ) { |f| f.read }
|
108
|
+
## todo/check: auto-parse "ahead of time" here
|
109
|
+
## or keep "raw" text - why? why not?
|
110
|
+
data[ num ] = text
|
111
|
+
end
|
112
|
+
data
|
113
|
+
end
|
114
|
+
|
115
|
+
def initialize( dir )
|
116
|
+
@dir = dir # e.g. "#{Mooncats.root}/config/v2"
|
117
|
+
end
|
118
|
+
|
119
|
+
def data
|
120
|
+
## note: lazy load / build on first demand only
|
121
|
+
@data ||= self.class.build( @dir )
|
122
|
+
end
|
123
|
+
|
124
|
+
def [](index) data[ index ]; end
|
125
|
+
def size() data.size; end
|
126
|
+
end # class DesignSeries
|
127
|
+
end # module Mooncats
|
data/lib/mooncats/image.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
|
2
2
|
module Mooncats
|
3
|
-
|
3
|
+
|
4
|
+
|
5
|
+
class Image < Pixelart::Image
|
4
6
|
|
5
7
|
|
6
8
|
COLORS_GENESIS_WHITE = ['#555555', '#d3d3d3', '#ffffff', '#aaaaaa', '#ff9999']
|
7
9
|
COLORS_GENESIS_BLACK = ['#555555', '#222222', '#111111', '#bbbbbb', '#ff9999']
|
8
10
|
|
9
11
|
|
10
|
-
def self.generate( id
|
12
|
+
def self.generate( id )
|
11
13
|
meta = Metadata.new( id )
|
12
14
|
|
13
15
|
design = meta.design.to_i # note: meta.design is a struct/object - keep/use a local int !!!
|
@@ -20,14 +22,14 @@ def self.generate( id, zoom: 1 )
|
|
20
22
|
COLORS_GENESIS_BLACK
|
21
23
|
end
|
22
24
|
else
|
23
|
-
derive_palette( meta.r,
|
24
|
-
meta.g,
|
25
|
-
|
25
|
+
derive_palette( r: meta.r,
|
26
|
+
g: meta.g,
|
27
|
+
b: meta.b,
|
28
|
+
invert: meta.invert? )
|
26
29
|
end
|
27
30
|
|
28
31
|
new( design: design,
|
29
|
-
colors: colors
|
30
|
-
zoom: zoom )
|
32
|
+
colors: colors )
|
31
33
|
end
|
32
34
|
|
33
35
|
### add more (convenience) aliases
|
@@ -36,80 +38,92 @@ class << self
|
|
36
38
|
end
|
37
39
|
|
38
40
|
|
41
|
+
def self.read( path ) ## convenience helper
|
42
|
+
img = ChunkyPNG::Image.from_file( path )
|
43
|
+
new( img )
|
44
|
+
end
|
39
45
|
|
40
46
|
|
41
|
-
def initialize( design: 0,
|
42
|
-
colors: COLORS_GENESIS_WHITE,
|
43
|
-
zoom: 1 )
|
44
|
-
|
45
|
-
## puts "==> [Mooncats] Image.new zoom: #{zoom}"
|
46
|
-
|
47
|
-
design = if design.is_a?( String )
|
48
|
-
Design.parse( design )
|
49
|
-
elsif design.is_a?( Array )
|
50
|
-
Design.new( design )
|
51
|
-
elsif design.is_a?( Design )
|
52
|
-
design ## pass through as is 1:1
|
53
|
-
else ## assume integer nuber
|
54
|
-
design_num = design ## note: for convenience "porcelain" param is named design (NOT design_num)
|
55
|
-
Design.find( design_num )
|
56
|
-
end
|
57
|
-
|
58
|
-
## note: first color (index 0) is always nil (default/white or transparent)
|
59
|
-
colors = [ nil ] + parse_colors( colors )
|
60
|
-
|
61
|
-
## puts " colors:"
|
62
|
-
## pp colors
|
63
|
-
|
64
|
-
@cat = ChunkyPNG::Image.new( design.width*zoom,
|
65
|
-
design.height*zoom,
|
66
|
-
ChunkyPNG::Color::WHITE ) # why? why not?
|
67
|
-
|
68
|
-
design.each_with_index do |row, x|
|
69
|
-
row.each_with_index do |color, y|
|
70
|
-
if color > 0
|
71
|
-
pixel = colors[ color ]
|
72
|
-
zoom.times do |n|
|
73
|
-
zoom.times do |m|
|
74
|
-
@cat[n+zoom*x,m+zoom*y] = pixel
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end # has color?
|
78
|
-
end # each row
|
79
|
-
end # each data
|
80
|
-
end
|
81
47
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
48
|
+
def initialize( initial=nil, design: nil,
|
49
|
+
colors: nil )
|
50
|
+
if initial
|
51
|
+
## pass image through as-is
|
52
|
+
img = inital
|
53
|
+
else
|
54
|
+
design ||= 0
|
55
|
+
colors ||= COLORS_GENESIS_WHITE
|
56
|
+
|
57
|
+
design = if design.is_a?( String )
|
58
|
+
Design.parse( design )
|
59
|
+
elsif design.is_a?( Array )
|
60
|
+
Design.new( design )
|
61
|
+
elsif design.is_a?( Design )
|
62
|
+
design ## pass through as is 1:1
|
63
|
+
else ## assume integer nuber
|
64
|
+
design_num = design ## note: for convenience "porcelain" param is named design (NOT design_num)
|
65
|
+
Design.find( design_num )
|
66
|
+
end
|
67
|
+
|
68
|
+
## note: first color (index 0) is always nil (default/white or transparent)
|
69
|
+
colors = [ nil ] + colors.map { |color| Pixelart::Color.parse( color ) }
|
70
|
+
|
71
|
+
## puts " colors:"
|
72
|
+
## pp colors
|
73
|
+
|
74
|
+
img = ChunkyPNG::Image.new( design.width,
|
75
|
+
design.height,
|
76
|
+
ChunkyPNG::Color::TRANSPARENT ) # why? why not?
|
77
|
+
|
78
|
+
design.each_with_index do |row, y|
|
79
|
+
row.each_with_index do |color, x|
|
80
|
+
if color > 0
|
81
|
+
pixel = colors[ color ]
|
82
|
+
|
83
|
+
## note: special built-in color palette black & white "hack"
|
84
|
+
## only active if colors 6 & 7 NOT defined
|
85
|
+
## color 6 => black (000000 / ff) rgb / a(lpha)
|
86
|
+
## color 7 => white (ffffff / ff) rgb / a(lpha)
|
87
|
+
pixel = 0xff if pixel.nil? && color == 6
|
88
|
+
pixel = 0xffffffff if pixel.nil? && color == 7
|
89
|
+
|
90
|
+
img[x,y] = pixel
|
91
|
+
end # has color?
|
92
|
+
end # each row
|
93
|
+
end # each data
|
94
|
+
end
|
95
|
+
|
96
|
+
super( img.width, img.height, img )
|
87
97
|
end
|
88
98
|
|
89
|
-
def width() @cat.width; end
|
90
|
-
def height() @cat.height; end
|
91
|
-
|
92
|
-
## return image ref - use a different name - why? why not?
|
93
|
-
def image() @cat; end
|
94
99
|
|
95
100
|
|
96
101
|
##################
|
97
102
|
# (static) helpers
|
98
|
-
def self.derive_palette( r, g, b
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
103
|
+
def self.derive_palette( r: nil, g: nil, b: nil,
|
104
|
+
hue: nil,
|
105
|
+
invert: false )
|
106
|
+
|
107
|
+
if hue
|
108
|
+
## pass through as is 1:1
|
109
|
+
else ## assume r, g, b
|
110
|
+
## note: Color.rgb returns an Integer (e.g. 34113279 - true color or just hex rgba or?)
|
111
|
+
rgb = ChunkyPNG::Color.rgb( r, g, b )
|
112
|
+
|
113
|
+
# to_hsl(color, include_alpha = false) ⇒ Array<Fixnum>[0], ...
|
114
|
+
# Returns an array with the separate HSL components of a color.
|
115
|
+
hsl = ChunkyPNG::Color.to_hsl( rgb )
|
116
|
+
#=> [237, 0.9705882352941178, 0.26666666666666666]
|
117
|
+
|
118
|
+
# h = hsl[0]
|
119
|
+
# s = hsl[1]
|
120
|
+
# l = hsl[2]
|
121
|
+
|
122
|
+
hue = hsl[0]
|
123
|
+
end
|
124
|
+
|
125
|
+
hx = hue % 360 ## note: makes sure number is always POSITIVE (e.g. -13 % 360 => 347)
|
126
|
+
hy = (hue + 320) % 360
|
113
127
|
#=> e.g. hx: 237, hy: 197
|
114
128
|
|
115
129
|
c1 = ChunkyPNG::Color.from_hsl( hx, 1, 0.1 )
|
@@ -131,75 +145,6 @@ def self.derive_palette( r, g, b, invert: false )
|
|
131
145
|
end
|
132
146
|
|
133
147
|
|
134
|
-
######
|
135
|
-
# helpers
|
136
|
-
def parse_colors( colors )
|
137
|
-
## convert into ChunkyPNG::Color
|
138
|
-
colors.map { |color| parse_color( color ) }
|
139
|
-
end
|
140
|
-
|
141
|
-
def parse_color( color )
|
142
|
-
if color.is_a?( Integer ) ## e.g. Assumess ChunkyPNG::Color.rgb() or such
|
143
|
-
color ## pass through as is 1:1
|
144
|
-
elsif color.is_a?(String)
|
145
|
-
## note: return an Integer !!! (not a Color class or such!!! )
|
146
|
-
ChunkyPNG::Color.from_hex( color )
|
147
|
-
else
|
148
|
-
raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3"
|
149
|
-
end
|
150
|
-
end
|
151
148
|
end # class Image
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
class Design
|
156
|
-
def self.find( num ) ## pass in design index number (0 to 127)
|
157
|
-
## todo: add cache (memoize) - why? why not?
|
158
|
-
str = DESIGNS[ num ]
|
159
|
-
design = parse( str )
|
160
|
-
|
161
|
-
puts " design ##{num} (#{design.width}x#{design.height})"
|
162
|
-
## pp design.data
|
163
|
-
## puts
|
164
|
-
|
165
|
-
design
|
166
|
-
end
|
167
|
-
|
168
|
-
|
169
|
-
def self.parse( str )
|
170
|
-
data = str.split('.')
|
171
|
-
new( data )
|
172
|
-
end
|
173
|
-
|
174
|
-
def initialize( data )
|
175
|
-
## todo: add cache (memoize) - why? why not?
|
176
|
-
|
177
|
-
## note: map colors encoded as a string to an array of integers - why? why not?
|
178
|
-
## e.g. "00011111133344411"
|
179
|
-
## =>
|
180
|
-
## [0,0,0,1,1,1,1,1,1,3,3,3,4,4,4,1,1]
|
181
|
-
|
182
|
-
@data = data.map do |row|
|
183
|
-
row.chars.map do |color|
|
184
|
-
color.to_i
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
## note: design data stored mirrored (data.size is the width NOT height)
|
190
|
-
def width() @data.size; end
|
191
|
-
def height() @data[0].size; end
|
192
|
-
|
193
|
-
def data() @data; end
|
194
|
-
|
195
|
-
def each_with_index( &blk )
|
196
|
-
## note: y,x is reversed - keep for now
|
197
|
-
## (todo/fix later? and "pivot" raw data on init - why? why not?)
|
198
|
-
@data.each_with_index do |row, x|
|
199
|
-
blk.call( row, x )
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end # class Design
|
203
|
-
|
204
149
|
end # module Mooncats
|
205
150
|
|