mooncats 0.1.2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|