mooncats 0.1.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73a562781defbd28b7cd681aae41379de40fbf3704a5a97834ce530d71dda15f
4
- data.tar.gz: 8abb18f6713ab38a9e68a84acff16ca3f3e71fb57edc93cb15f204b612c88804
3
+ metadata.gz: bf22f607c257a0cef8877f44594782f1c6f704a9c580c01af96a39d49f985209
4
+ data.tar.gz: f6b10d2506c5b3dc05477ad4d8ba18c24eada0382cbcc29fdbfa0c572a04c892
5
5
  SHA512:
6
- metadata.gz: 34f9b2e6117b7a8fe4134dc20cbdb9541cbb97500e278853d76defeca1d48b6fa32d312de84ec557701ccf5835108e4bffbfc1b12bb27f71f031fb6aa6899c13
7
- data.tar.gz: aef96b644a304e287ee44b7539ea2705b7c98c2d9a40cdb4c3ef76ff1b59b96ec380ee3b5ec22d0acf70ed760748fefdfcd554e3eb30ad1cdab79b66362322ec
6
+ metadata.gz: 4e20d5394cd29624a64d0b7a830fbf7bc0b0890c021a25c077a7c51771600b9d7187220abd2e76c5a94b2f1a50d66fae040727950fca4be859db8a821791082a
7
+ data.tar.gz: 6028eadc62a9861e3e9c5be6215ec0bb142701cffd15e88d681ce4c30cfc137df4ee102f9b09de0c41f9d1a4715771d631d488a1b49326a8fba96bf211615e08
data/Manifest.txt CHANGED
@@ -1,10 +1,13 @@
1
1
  CHANGELOG.md
2
- LICENSE.md
3
2
  Manifest.txt
4
3
  README.md
5
4
  Rakefile
6
5
  bin/mooncat
7
6
  lib/mooncats.rb
7
+ lib/mooncats/composite.rb
8
+ lib/mooncats/dataset.rb
9
+ lib/mooncats/design.rb
8
10
  lib/mooncats/designs.rb
9
11
  lib/mooncats/image.rb
12
+ lib/mooncats/structs.rb
10
13
  lib/mooncats/version.rb
data/README.md CHANGED
@@ -25,12 +25,12 @@ resulting in:
25
25
 
26
26
  ```
27
27
  Usage: mooncat [options] IDs
28
- Mint mooncats from original designs - for IDs use 5 byte hexstrings (e.g 0x004fc21270)
28
+ Mint mooncats from original designs - for IDs use 5 byte hexstrings (e.g 0x004fc21270)
29
29
 
30
- Options:
31
- -z, --zoom=ZOOM Zoom factor x2, x4, x8, etc. (default: 1)
32
- -d, --dir=DIR Output directory (default: .)
33
- -h, --help Prints this help
30
+ Options:
31
+ -z, --zoom=ZOOM Zoom factor x2, x4, x8, etc. (default: 1)
32
+ -d, --dir=DIR Output directory (default: .)
33
+ -h, --help Prints this help
34
34
  ```
35
35
 
36
36
 
@@ -55,9 +55,9 @@ printing:
55
55
 
56
56
  And voila!
57
57
 
58
- ![](i/mooncat-00000800fa.png)
59
- ![](i/mooncat-0077c8278d.png)
60
- ![](i/mooncat-ff5f000ca7.png)
58
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-00000800fa.png)
59
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-0077c8278d.png)
60
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-ff5f000ca7.png)
61
61
 
62
62
 
63
63
 
@@ -86,23 +86,24 @@ printing:
86
86
 
87
87
  And voila!
88
88
 
89
- ![](i/mooncat-00000800fa_x2.png)
90
- ![](i/mooncat-0077c8278d_x2.png)
91
- ![](i/mooncat-ff5f000ca7_x2.png)
89
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-00000800fa_x2.png)
90
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-0077c8278d_x2.png)
91
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-ff5f000ca7_x2.png)
92
92
 
93
93
 
94
94
  And x4:
95
95
 
96
- ![](i/mooncat-00000800fa_x4.png)
97
- ![](i/mooncat-0077c8278d_x4.png)
98
- ![](i/mooncat-ff5f000ca7_x4.png)
96
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-00000800fa_x4.png)
97
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-0077c8278d_x4.png)
98
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-ff5f000ca7_x4.png)
99
99
 
100
100
 
101
101
  And x8:
102
102
 
103
- ![](i/mooncat-00000800fa_x8.png)
104
- ![](i/mooncat-0077c8278d_x8.png)
105
- ![](i/mooncat-ff5f000ca7_x8.png)
103
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-00000800fa_x8.png)
104
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-0077c8278d_x8.png)
105
+ ![](https://github.com/cryptocopycats/mooncats/raw/master/mooncats/i/mooncat-ff5f000ca7_x8.png)
106
+
106
107
 
107
108
 
108
109
 
@@ -115,7 +116,7 @@ And so on.
115
116
  Yes, you can mint mooncats in your own scripts
116
117
  and much more.
117
118
  See the
118
- [**Programming Mooncats Step-by-Step Booklet / Guide »**](https://github.com/cryptopunksnotdead/programming-mooncats) - Upcoming
119
+ [**Programming MoonCats & MarsCats Step-by-Step Booklet / Guide »**](https://github.com/cryptocopycats/programming-mooncats)
119
120
 
120
121
 
121
122
 
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ Hoe.spec 'mooncats' do
18
18
  self.history_file = 'CHANGELOG.md'
19
19
 
20
20
  self.extra_deps = [
21
- ['chunky_png'],
21
+ ['pixelart'],
22
22
  ['csvreader'],
23
23
  ]
24
24
 
data/lib/mooncats.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  ## 3rd party
2
- require 'chunky_png'
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
 
@@ -12,8 +11,11 @@ require 'optparse'
12
11
  ## our own code
13
12
  require 'mooncats/version' # note: let version always go first
14
13
  require 'mooncats/designs'
14
+ require 'mooncats/structs'
15
+ require 'mooncats/design'
15
16
  require 'mooncats/image'
16
-
17
+ require 'mooncats/composite'
18
+ require 'mooncats/dataset'
17
19
 
18
20
 
19
21
 
@@ -61,15 +63,20 @@ module Mooncats
61
63
  ## note: cut-off optionial 0x
62
64
  cat_id = cat_id[2..-1] if cat_id.start_with?( '0x')
63
65
 
66
+ cat = Image.generate( cat_id )
67
+
64
68
  cat_name = "mooncat-#{cat_id}"
65
69
 
66
70
  ## if zoom - add x2,x4 or such
67
- cat_name << "_x#{opts[:zoom]}" if opts[:zoom] != 1
71
+ if opts[:zoom] != 1
72
+ cat = cat.zoom( opts[:zoom] )
73
+ cat_name << "_x#{opts[:zoom]}"
74
+ end
68
75
 
69
76
  path = "#{opts[:outdir]}/#{cat_name}.png"
70
77
  puts "==> (#{index+1}/#{args.size}) minting mooncat 0x#{cat_id}; writing to >#{path}<..."
71
78
 
72
- Image.generate( cat_id, zoom: opts[:zoom] ).save( path )
79
+ cat.save( path )
73
80
  end
74
81
 
75
82
  puts "done"
@@ -87,4 +94,11 @@ module Mooncats
87
94
  MoonCats = Mooncats
88
95
 
89
96
 
97
+
98
+
99
+ ###
100
+ # note: for convenience auto include Pixelart namespace!!! - why? why not?
101
+ include Pixelart
102
+
103
+
90
104
  puts Mooncats.banner # say hello
@@ -0,0 +1,69 @@
1
+
2
+ module Mooncats
3
+ class Image
4
+ class Composite ## nest Composite inside Image - why? why not?
5
+
6
+
7
+ CANVAS_WIDTH = 24 ## sorry - for now "hard-coded" / fixed - 24x24
8
+ CANVAS_HEIGHT = 24
9
+
10
+ def initialize( cols=100, rows=255 )
11
+ @composite = ChunkyPNG::Image.new( cols*CANVAS_WIDTH,
12
+ rows*CANVAS_HEIGHT,
13
+ ChunkyPNG::Color::TRANSPARENT ) # why? why not? - use TRANSPARENT (is default?)
14
+
15
+ ## todo/check - find a better name for cols/rows - why? why not?
16
+ @cols = cols
17
+ @rows = rows
18
+
19
+ @i = 0 # (track) current index (of added images)
20
+ end
21
+
22
+
23
+ def add( image )
24
+ y, x = @i.divmod( @cols )
25
+
26
+ puts " width: #{image.width}, height: #{image.height}"
27
+
28
+ ## try to center image (identicon) in 24x24 canvas
29
+ ## the 4 formats are
30
+ ## - 21×17 - Standing
31
+ ## - 20×14 - Sleeping
32
+ ## - 17×22 - Pouncing
33
+ ## - 20×21 - Stalking
34
+ ## e.g. add left padding (x_center) and
35
+ ## top padding (y_center)
36
+ x_center, y_center = case [image.width, image.height]
37
+ when [21,17] then [1,3]
38
+ when [20,14] then [2,5]
39
+ when [17,22] then [3,1]
40
+ when [20,21] then [2,1]
41
+ ## todo/fix: report unknown image format/size!!!!
42
+ end
43
+
44
+ ## note: image.image - "unwrap" the "raw" ChunkyPNG::Image
45
+ @composite.compose!( image.image, x*CANVAS_WIDTH+x_center, y*CANVAS_HEIGHT+y_center )
46
+ @i += 1
47
+ end
48
+
49
+ alias_method :<<, :add
50
+
51
+
52
+
53
+ #####
54
+ # (image) delegates
55
+ ## todo/check: add some more??
56
+ def save( path, constraints = {} )
57
+ @composite.save( path, constraints )
58
+ end
59
+
60
+ def width() @composite.width; end
61
+ def height() @composite.height; end
62
+
63
+ ## return image ref - use a different name - why? why not?
64
+ def image() @composite; end
65
+
66
+
67
+ end # class Composite
68
+ end # class Image
69
+ end # module Mooncats
@@ -0,0 +1,53 @@
1
+
2
+
3
+ module Mooncats
4
+ module Dataset
5
+
6
+ def self.read( path='./datasets/mooncats/*.csv' )
7
+
8
+ datasets = Dir.glob( path )
9
+ ## pp datasets
10
+ #=> ["./datasets/mooncats/00.csv",
11
+ # "./datasets/mooncats/01.csv",
12
+ # "./datasets/mooncats/02.csv",
13
+ # "./datasets/mooncats/03.csv",
14
+ # "./datasets/mooncats/04.csv",
15
+ # ...]
16
+
17
+ rows = []
18
+ datasets.each do |dataset|
19
+ rows += CsvHash.read( dataset )
20
+ end
21
+
22
+ ## puts " #{rows.size} rows(s)"
23
+ #=> 25440 rows(s)
24
+
25
+
26
+ ### wrap in mooncat struct for easier access
27
+ mooncats = []
28
+ rows.each_with_index do |row,i|
29
+ ## print "." if i % 100
30
+ id = row['id']
31
+ ## note: skip all derived column from id e.g.
32
+ ## - r,g,b, etc.
33
+
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 )
45
+ end
46
+ ## print "\n" ## add progress
47
+
48
+ mooncats
49
+ end
50
+ end # module Dataset
51
+ end # module Mooncat
52
+
53
+
@@ -0,0 +1,92 @@
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
+
19
+ def self.parse( str )
20
+ ## support original "classic" compact single-line format
21
+ ## e.g. 00011111100000000.01113333310000000.13533333331110000....
22
+ ## note: this format needs to get rotated by 90 degree!!!
23
+ if str.start_with?( /[0-5]+\.[0-5]+\.[0-5]+/ ) ## quick (and dirty) heuristic check
24
+ data = str.split('.')
25
+
26
+ ## note: map colors encoded as a string to an array of integers - why? why not?
27
+ ## e.g. "00011111133344411"
28
+ ## =>
29
+ ## [0,0,0,1,1,1,1,1,1,3,3,3,4,4,4,1,1]
30
+ data = data.map do |row|
31
+ row.chars.map do |color|
32
+ color.to_i
33
+ end
34
+ end
35
+ data = data.transpose ## note: rotate by 90 degree!!!!!
36
+ else ## assume "modern" pixelart format
37
+ ## todo/check: delegate to pixelart parse or such - why? why not?
38
+
39
+ data = []
40
+ str.each_line do |line|
41
+ line = line.strip
42
+ next if line.empty? ## skipping empty line in pixel art source
43
+ next if line.start_with?( '#' ) ## skipping comment line in pixel art source
44
+
45
+ ## note: allow multiple spaces or tabs to separate pixel codes
46
+ data << line.split( /[ \t]+/)
47
+ end
48
+ ## todo/check: change to use strings (instead of nummbers) in the future?
49
+ ## why? why not? stay "compatible" to pixel art format/machinery?
50
+ data = data.map do |row|
51
+ row.map do |color|
52
+ color.to_i
53
+ end
54
+ end
55
+ end
56
+ new( data )
57
+ end
58
+
59
+
60
+ def initialize( data )
61
+ @data = data
62
+ end
63
+
64
+ def width
65
+ ## todo/check: use/find max - why? why not? lets you you used "unbalanced" / shortcut lines too
66
+ @data[0].size
67
+ end
68
+ def height() @data.size; end
69
+
70
+
71
+ def each_with_index( &blk )
72
+ @data.each_with_index { |row, y| blk.call( row, y ) }
73
+ end
74
+ def each( &blk )
75
+ @data.each { |row| blk.call( row ) }
76
+ end
77
+
78
+
79
+
80
+ def to_txt
81
+ buf = String.new('')
82
+
83
+ @data.each do |row|
84
+ buf << row.join( ' ' )
85
+ buf << "\n"
86
+ end
87
+ buf
88
+ end
89
+
90
+
91
+ end # class Design
92
+ end # module Mooncats
@@ -1,46 +1,46 @@
1
1
 
2
2
  module Mooncats
3
- class Image
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( cat_id, zoom: 1 )
11
- bytes = hex_to_bytes( cat_id )
12
-
13
- genesis = bytes[0] != 0 ## note: convert to bool (if zero assume NOT genesis)
14
- k = bytes[1]
15
- r = bytes[2]
16
- g = bytes[3]
17
- b = bytes[4]
12
+ def self.generate( id )
13
+ meta = Metadata.new( id )
18
14
 
19
- invert = k >= 128
20
- design = k % 128
15
+ design = meta.design.to_i # note: meta.design is a struct/object - keep/use a local int !!!
21
16
 
22
- colors = if genesis
23
- if design % 2 === 0 && invert ||
24
- design % 2 === 1 && !invert
17
+ colors = if meta.genesis?
18
+ if design % 2 == 0 && meta.invert? ||
19
+ design % 2 == 1 && !meta.invert?
25
20
  COLORS_GENESIS_WHITE
26
21
  else
27
22
  COLORS_GENESIS_BLACK
28
23
  end
29
24
  else
30
- derive_palette( r, g, b, invert: invert )
25
+ derive_palette( r: meta.r,
26
+ g: meta.g,
27
+ b: meta.b,
28
+ invert: meta.invert? )
31
29
  end
32
30
 
33
31
  new( design: design,
34
- colors: colors,
35
- zoom: zoom )
32
+ colors: colors )
36
33
  end
37
34
 
35
+ ### add more (convenience) aliases
36
+ class << self
37
+ alias_method :mint, :generate
38
+ end
38
39
 
39
- def initialize( design: 0,
40
- colors: COLORS_GENESIS_WHITE,
41
- zoom: 1 )
42
40
 
43
- ## puts "==> [Mooncats] Image.new zoom: #{zoom}"
41
+
42
+ def initialize( design: 0,
43
+ colors: COLORS_GENESIS_WHITE )
44
44
 
45
45
  design = if design.is_a?( String )
46
46
  Design.parse( design )
@@ -54,72 +54,55 @@ def initialize( design: 0,
54
54
  end
55
55
 
56
56
  ## note: first color (index 0) is always nil (default/white or transparent)
57
- colors = [ nil ] + parse_colors( colors )
57
+ colors = [ nil ] + colors.map { |color| Pixelart::Color.parse( color ) }
58
58
 
59
59
  ## puts " colors:"
60
60
  ## pp colors
61
61
 
62
- @cat = ChunkyPNG::Image.new( design.width*zoom,
63
- design.height*zoom,
64
- ChunkyPNG::Color::WHITE ) # why? why not?
62
+ img = ChunkyPNG::Image.new( design.width,
63
+ design.height,
64
+ ChunkyPNG::Color::TRANSPARENT ) # why? why not?
65
65
 
66
- design.each_with_index do |row, x|
67
- row.each_with_index do |color, y|
66
+ design.each_with_index do |row, y|
67
+ row.each_with_index do |color, x|
68
68
  if color > 0
69
69
  pixel = colors[ color ]
70
- zoom.times do |n|
71
- zoom.times do |m|
72
- @cat[n+zoom*x,m+zoom*y] = pixel
73
- end
74
- end
70
+ img[x,y] = pixel
75
71
  end # has color?
76
72
  end # each row
77
73
  end # each data
78
- end
79
74
 
80
- #####
81
- # (image) delegates
82
- ## todo/check: add some more??
83
- def save( path ) @cat.save( path ); end
84
- def width() @cat.width; end
85
- def height() @cat.height; end
75
+ super( img.width, img.height, img )
76
+ end
86
77
 
87
78
 
88
79
 
89
80
  ##################
90
81
  # (static) helpers
91
- def self.hex_to_bytes( str_or_num )
92
- if str_or_num.is_a?( Integer ) ## allow passing in of integer to e.g. 0x... etc.
93
- num = str_or_num
94
- str = '%010x' % num # 5 bytes (10 hex digits/chars)
95
- else ## assume string
96
- ## cut-off optionial 0x
97
- str = str_or_num
98
- str = str.downcase
99
- str = str[2..-1] if str.start_with?( '0x')
100
- end
101
-
102
- raise ArgumentError, "expected 5 byte hex string (10 digits/chars); got #{str_or_num}" if str.size != 10
103
-
104
- bytes = [str].pack('H*').bytes
105
- bytes
106
- end
107
-
108
- def self.derive_palette( r, g, b, invert: false )
109
- ## note: Color.rgb returns an Integer (e.g. 34113279 - true color or just hex rgba or?)
110
- rgb = ChunkyPNG::Color.rgb( r, g, b )
111
-
112
- # to_hsl(color, include_alpha = false) ⇒ Array<Fixnum>[0], ...
113
- # Returns an array with the separate HSL components of a color.
114
- hsl = ChunkyPNG::Color.to_hsl( rgb )
115
- #=> [237, 0.9705882352941178, 0.26666666666666666]
116
-
117
- h = hsl[0]
118
- s = hsl[1]
119
- l = hsl[2]
120
-
121
- hx = h % 360
122
- hy = (h + 320) % 360
82
+ def self.derive_palette( r: nil, g: nil, b: nil,
83
+ hue: nil,
84
+ invert: false )
85
+
86
+ if hue
87
+ ## pass through as is 1:1
88
+ else ## assume r, g, b
89
+ ## note: Color.rgb returns an Integer (e.g. 34113279 - true color or just hex rgba or?)
90
+ rgb = ChunkyPNG::Color.rgb( r, g, b )
91
+
92
+ # to_hsl(color, include_alpha = false) ⇒ Array<Fixnum>[0], ...
93
+ # Returns an array with the separate HSL components of a color.
94
+ hsl = ChunkyPNG::Color.to_hsl( rgb )
95
+ #=> [237, 0.9705882352941178, 0.26666666666666666]
96
+
97
+ # h = hsl[0]
98
+ # s = hsl[1]
99
+ # l = hsl[2]
100
+
101
+ hue = hsl[0]
102
+ end
103
+
104
+ hx = hue % 360 ## note: makes sure number is always POSITIVE (e.g. -13 % 360 => 347)
105
+ hy = (hue + 320) % 360
123
106
  #=> e.g. hx: 237, hy: 197
124
107
 
125
108
  c1 = ChunkyPNG::Color.from_hsl( hx, 1, 0.1 )
@@ -141,75 +124,6 @@ def self.derive_palette( r, g, b, invert: false )
141
124
  end
142
125
 
143
126
 
144
- ######
145
- # helpers
146
- def parse_colors( colors )
147
- ## convert into ChunkyPNG::Color
148
- colors.map { |color| parse_color( color ) }
149
- end
150
-
151
- def parse_color( color )
152
- if color.is_a?( Integer ) ## e.g. Assumess ChunkyPNG::Color.rgb() or such
153
- color ## pass through as is 1:1
154
- elsif color.is_a?(String)
155
- ## note: return an Integer !!! (not a Color class or such!!! )
156
- ChunkyPNG::Color.from_hex( color )
157
- else
158
- raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3"
159
- end
160
- end
161
127
  end # class Image
162
-
163
-
164
-
165
- class Design
166
- def self.find( num ) ## pass in design index number (0 to 127)
167
- ## todo: add cache (memoize) - why? why not?
168
- str = DESIGNS[ num ]
169
- design = parse( str )
170
-
171
- puts " design ##{num} (#{design.width}x#{design.height})"
172
- ## pp design.data
173
- ## puts
174
-
175
- design
176
- end
177
-
178
-
179
- def self.parse( str )
180
- data = str.split('.')
181
- new( data )
182
- end
183
-
184
- def initialize( data )
185
- ## todo: add cache (memoize) - why? why not?
186
-
187
- ## note: map colors encoded as a string to an array of integers - why? why not?
188
- ## e.g. "00011111133344411"
189
- ## =>
190
- ## [0,0,0,1,1,1,1,1,1,3,3,3,4,4,4,1,1]
191
-
192
- @data = data.map do |row|
193
- row.chars.map do |color|
194
- color.to_i
195
- end
196
- end
197
- end
198
-
199
- ## note: design data stored mirrored (data.size is the width NOT height)
200
- def width() @data.size; end
201
- def height() @data[0].size; end
202
-
203
- def data() @data; end
204
-
205
- def each_with_index( &blk )
206
- ## note: y,x is reversed - keep for now
207
- ## (todo/fix later? and "pivot" raw data on init - why? why not?)
208
- @data.each_with_index do |row, x|
209
- blk.call( row, x )
210
- end
211
- end
212
- end # class Design
213
-
214
128
  end # module Mooncats
215
129
 
@@ -0,0 +1,242 @@
1
+
2
+ module Mooncats
3
+
4
+ ### wrap metadata (e.g. pose, fur, facing, color, etc.)
5
+ ## in structs for easy/easier access)
6
+
7
+ POSES = [
8
+ 'Standing', ## 00
9
+ 'Sleeping', ## 01
10
+ 'Pouncing', ## 10
11
+ 'Stalking', ## 11
12
+ ]
13
+
14
+
15
+ OLD_FACES = [ ## old names for face (expressions)
16
+ 'Smile', ## 00
17
+ 'Frown (Look Down)', ## 01
18
+ 'Frown (Look Up)', ## 10
19
+ 'Flat Whiskers', ## 11
20
+ ]
21
+
22
+ FACES = [ ## face expressions
23
+ 'Smiling', ## 00
24
+ 'Grumpy', ## 01
25
+ 'Pouting', ## 10
26
+ 'Shy', ## 11
27
+ ]
28
+
29
+
30
+ OLD_FURS = [ ## old names for fur (patterns)
31
+ 'Solid', ## 00
32
+ 'Striped', ## 01
33
+ 'Eyepatch', ## 10
34
+ 'Half/Half', ## 11
35
+ ]
36
+
37
+ FURS = [ ## fur (patterns)
38
+ 'Pure', ## 00
39
+ 'Tabby', ## 01
40
+ 'Spotted', ## 10
41
+ 'Tortie', ## 11
42
+ ]
43
+
44
+
45
+ FACINGS = [
46
+ 'Left', # 0
47
+ 'Right', # 1
48
+ ]
49
+
50
+
51
+
52
+ class Metadata
53
+
54
+ class Design ## nested classed - why? lets you use Metadata::Design "standalone", that is, without 5-byte id
55
+ def initialize( num ) # 0-127 design num(ber)
56
+ @num = num
57
+ end
58
+
59
+ def to_i() @num; end
60
+
61
+ def bits ## keep private / internal - why? why not?
62
+ ## keep 128 possible designs 0 to 127
63
+ ## as 7 bit string e.g. 01010111 for now - why? why not?
64
+ @bits ||= '%08b' % @num
65
+ end
66
+
67
+ def facing
68
+ @facing ||= FACINGS[ bits[1,1].to_i(2) ] ## use desgin > 63 instead - why? why not?
69
+ end
70
+ def face ## face (expression)
71
+ @face ||= FACES[ bits[2,2].to_i(2) ]
72
+ end
73
+ alias_method :expression, :face
74
+
75
+ def fur ## fur (pattern) - add pattern alias - why? why not?
76
+ @fur ||= FURS[ bits[4,2].to_i(2) ]
77
+ end
78
+
79
+ def pose
80
+ @poses ||= POSES[ bits[6,2].to_i(2) ] ## use design % 4 instead - why? why not?
81
+ end
82
+ end ## (nested) class Metadata::Design
83
+
84
+
85
+
86
+
87
+ def initialize( id, **more )
88
+ @bytes = self.class.hex_to_bytes( id )
89
+
90
+ ## add support for more "external" meta data
91
+ ## ## e.g. mint, mint_block, etc.
92
+ @more = more
93
+ end
94
+
95
+ def id
96
+ @id ||= @bytes.map { |byte| '%02x' % byte }.join
97
+ end
98
+
99
+ def genesis?
100
+ @bytes[0] != 0 ## note: convert to bool (if zero assume NOT genesis)
101
+ end
102
+ def k() @bytes[1]; end
103
+ def r() @bytes[2]; end
104
+ def g() @bytes[3]; end
105
+ def b() @bytes[4]; end
106
+
107
+ def rgb() [r,g,b]; end ## add rgb shortcut helper - why? why not?
108
+
109
+ def invert?() k >= 128; end
110
+ alias_method :pale?, :invert?
111
+
112
+
113
+ def pattern() k % 64; end ## treat facing left|right as the same - note: conflicts with fur (pattern) - find a better/different name?
114
+
115
+
116
+ def hue
117
+ @hue ||= begin
118
+ ## note: hsl[0], that is, hue MIGHT BE NEGATIVE!
119
+ ## e.g. try rbg( 91, 27, 41 )
120
+ ## resulting in
121
+ ## hsl( -13, 0.5423728813559322, 0.23137254901960785 )
122
+ ## remember: always use % 360 to make positive!!!
123
+ ## e.g. -13 % 360 => 347
124
+ ## -25 % 360 => 335
125
+ rgb = ChunkyPNG::Color.rgb( r, g, b )
126
+ hsl = ChunkyPNG::Color.to_hsl( rgb )
127
+ hsl[0] % 360 ## make sure number is always POSITIVE!!!
128
+ end
129
+ end
130
+
131
+
132
+ def color
133
+ case hue
134
+ when 345..359,
135
+ 0..14 then 'Red'
136
+ when 15..44 then 'Orange'
137
+ when 45..74 then 'Yellow'
138
+ when 75..104 then 'Chartreuse'
139
+ when 105..134 then 'Green'
140
+ when 135..164 then 'Teal'
141
+ when 165..194 then 'Cyan'
142
+ when 195..224 then 'Sky Blue'
143
+ when 225..254 then 'Blue'
144
+ when 255..284 then 'Purple'
145
+ when 285..314 then 'Magenta'
146
+ when 315..344 then 'Fuchsia'
147
+ else
148
+ puts "!! ERROR - unexpected hue (in degress); got #{hue} - expected 0 to 359"
149
+ exit 1
150
+ end
151
+ end
152
+
153
+
154
+ def xxx_color_old_formula ## remove - move to attic??
155
+ case hue
156
+ when 0..29 then 'Red'
157
+ when 30..59 then 'Orange'
158
+ when 60..89 then 'Yellow'
159
+ when 90..119 then 'Chartreuse'
160
+ when 120..149 then 'Green'
161
+ when 150..179 then 'Lime Green' ## now renamed to Teal
162
+ when 180..209 then 'Cyan'
163
+ when 210..239 then 'Sky Blue'
164
+ when 240..269 then 'Blue'
165
+ when 270..299 then 'Purple'
166
+ when 300..329 then 'Magenta'
167
+ when 330..359 then 'Fuchsia'
168
+ else
169
+ puts "!! ERROR - unexpected hue (in degress); got #{hue} - expected 0 to 359"
170
+ exit 1
171
+ end
172
+ end
173
+
174
+
175
+ def design
176
+ @design ||= Design.new( k % 128 )
177
+ end
178
+
179
+ def facing() design.facing; end
180
+ def face() design.face; end
181
+ alias_method :expression, :face
182
+ def fur() design.fur; end
183
+ def pose() design.pose; end
184
+
185
+
186
+ ####
187
+ # more "external" attributes
188
+ def mint() @more[:mint]; end
189
+ def block() @more[:block]; end
190
+ def timestamp() @more[:timestamp]; end
191
+ def year() timestamp ? timestamp.year : nil; end
192
+
193
+ #####
194
+ # enable array-like access to - why? why not?
195
+ def []( key )
196
+ case key.to_sym
197
+ when :id then id
198
+ when :genesis then genesis?
199
+ when :k then k
200
+ when :r then r
201
+ when :g then g
202
+ when :b then b
203
+ when :rgb then rgb
204
+ when :invert,
205
+ :pale then invert?
206
+ when :hue then hue
207
+ when :color then color
208
+ when :design then design.to_i
209
+ when :pattern then pattern
210
+ when :facing then facing
211
+ when :face,
212
+ :expression then face
213
+ when :fur then fur
214
+ when :pose then pose
215
+ when :year then year # note: from more via timestamp
216
+ else
217
+ @more[ key ]
218
+ end
219
+ end
220
+
221
+
222
+ ##################
223
+ # (static) helpers
224
+ def self.hex_to_bytes( str_or_num )
225
+ if str_or_num.is_a?( Integer ) ## allow passing in of integer to e.g. 0x... etc.
226
+ num = str_or_num
227
+ str = '%010x' % num # 5 bytes (10 hex digits/chars)
228
+ else ## assume string
229
+ ## cut-off optionial 0x
230
+ str = str_or_num
231
+ str = str.downcase
232
+ str = str[2..-1] if str.start_with?( '0x')
233
+ end
234
+
235
+ raise ArgumentError, "expected 5 byte hex string (10 digits/chars); got #{str_or_num}" if str.size != 10
236
+
237
+ bytes = [str].pack('H*').bytes
238
+ bytes
239
+ end
240
+ end # class Metadata
241
+
242
+ end # module Mooncats
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Mooncats
3
3
 
4
- MAJOR = 0
4
+ MAJOR = 1
5
5
  MINOR = 1
6
6
  PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mooncats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-16 00:00:00.000000000 Z
11
+ date: 2021-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: chunky_png
14
+ name: pixelart
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -81,19 +81,21 @@ executables:
81
81
  extensions: []
82
82
  extra_rdoc_files:
83
83
  - CHANGELOG.md
84
- - LICENSE.md
85
84
  - Manifest.txt
86
85
  - README.md
87
86
  files:
88
87
  - CHANGELOG.md
89
- - LICENSE.md
90
88
  - Manifest.txt
91
89
  - README.md
92
90
  - Rakefile
93
91
  - bin/mooncat
94
92
  - lib/mooncats.rb
93
+ - lib/mooncats/composite.rb
94
+ - lib/mooncats/dataset.rb
95
+ - lib/mooncats/design.rb
95
96
  - lib/mooncats/designs.rb
96
97
  - lib/mooncats/image.rb
98
+ - lib/mooncats/structs.rb
97
99
  - lib/mooncats/version.rb
98
100
  homepage: https://github.com/cryptocopycats/mooncats
99
101
  licenses:
data/LICENSE.md DELETED
@@ -1,116 +0,0 @@
1
- CC0 1.0 Universal
2
-
3
- Statement of Purpose
4
-
5
- The laws of most jurisdictions throughout the world automatically confer
6
- exclusive Copyright and Related Rights (defined below) upon the creator and
7
- subsequent owner(s) (each and all, an "owner") of an original work of
8
- authorship and/or a database (each, a "Work").
9
-
10
- Certain owners wish to permanently relinquish those rights to a Work for the
11
- purpose of contributing to a commons of creative, cultural and scientific
12
- works ("Commons") that the public can reliably and without fear of later
13
- claims of infringement build upon, modify, incorporate in other works, reuse
14
- and redistribute as freely as possible in any form whatsoever and for any
15
- purposes, including without limitation commercial purposes. These owners may
16
- contribute to the Commons to promote the ideal of a free culture and the
17
- further production of creative, cultural and scientific works, or to gain
18
- reputation or greater distribution for their Work in part through the use and
19
- efforts of others.
20
-
21
- For these and/or other purposes and motivations, and without any expectation
22
- of additional consideration or compensation, the person associating CC0 with a
23
- Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24
- and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25
- and publicly distribute the Work under its terms, with knowledge of his or her
26
- Copyright and Related Rights in the Work and the meaning and intended legal
27
- effect of CC0 on those rights.
28
-
29
- 1. Copyright and Related Rights. A Work made available under CC0 may be
30
- protected by copyright and related or neighboring rights ("Copyright and
31
- Related Rights"). Copyright and Related Rights include, but are not limited
32
- to, the following:
33
-
34
- i. the right to reproduce, adapt, distribute, perform, display, communicate,
35
- and translate a Work;
36
-
37
- ii. moral rights retained by the original author(s) and/or performer(s);
38
-
39
- iii. publicity and privacy rights pertaining to a person's image or likeness
40
- depicted in a Work;
41
-
42
- iv. rights protecting against unfair competition in regards to a Work,
43
- subject to the limitations in paragraph 4(a), below;
44
-
45
- v. rights protecting the extraction, dissemination, use and reuse of data in
46
- a Work;
47
-
48
- vi. database rights (such as those arising under Directive 96/9/EC of the
49
- European Parliament and of the Council of 11 March 1996 on the legal
50
- protection of databases, and under any national implementation thereof,
51
- including any amended or successor version of such directive); and
52
-
53
- vii. other similar, equivalent or corresponding rights throughout the world
54
- based on applicable law or treaty, and any national implementations thereof.
55
-
56
- 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57
- applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58
- unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59
- and Related Rights and associated claims and causes of action, whether now
60
- known or unknown (including existing as well as future claims and causes of
61
- action), in the Work (i) in all territories worldwide, (ii) for the maximum
62
- duration provided by applicable law or treaty (including future time
63
- extensions), (iii) in any current or future medium and for any number of
64
- copies, and (iv) for any purpose whatsoever, including without limitation
65
- commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66
- the Waiver for the benefit of each member of the public at large and to the
67
- detriment of Affirmer's heirs and successors, fully intending that such Waiver
68
- shall not be subject to revocation, rescission, cancellation, termination, or
69
- any other legal or equitable action to disrupt the quiet enjoyment of the Work
70
- by the public as contemplated by Affirmer's express Statement of Purpose.
71
-
72
- 3. Public License Fallback. Should any part of the Waiver for any reason be
73
- judged legally invalid or ineffective under applicable law, then the Waiver
74
- shall be preserved to the maximum extent permitted taking into account
75
- Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76
- is so judged Affirmer hereby grants to each affected person a royalty-free,
77
- non transferable, non sublicensable, non exclusive, irrevocable and
78
- unconditional license to exercise Affirmer's Copyright and Related Rights in
79
- the Work (i) in all territories worldwide, (ii) for the maximum duration
80
- provided by applicable law or treaty (including future time extensions), (iii)
81
- in any current or future medium and for any number of copies, and (iv) for any
82
- purpose whatsoever, including without limitation commercial, advertising or
83
- promotional purposes (the "License"). The License shall be deemed effective as
84
- of the date CC0 was applied by Affirmer to the Work. Should any part of the
85
- License for any reason be judged legally invalid or ineffective under
86
- applicable law, such partial invalidity or ineffectiveness shall not
87
- invalidate the remainder of the License, and in such case Affirmer hereby
88
- affirms that he or she will not (i) exercise any of his or her remaining
89
- Copyright and Related Rights in the Work or (ii) assert any associated claims
90
- and causes of action with respect to the Work, in either case contrary to
91
- Affirmer's express Statement of Purpose.
92
-
93
- 4. Limitations and Disclaimers.
94
-
95
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
96
- surrendered, licensed or otherwise affected by this document.
97
-
98
- b. Affirmer offers the Work as-is and makes no representations or warranties
99
- of any kind concerning the Work, express, implied, statutory or otherwise,
100
- including without limitation warranties of title, merchantability, fitness
101
- for a particular purpose, non infringement, or the absence of latent or
102
- other defects, accuracy, or the present or absence of errors, whether or not
103
- discoverable, all to the greatest extent permissible under applicable law.
104
-
105
- c. Affirmer disclaims responsibility for clearing rights of other persons
106
- that may apply to the Work or any use thereof, including without limitation
107
- any person's Copyright and Related Rights in the Work. Further, Affirmer
108
- disclaims responsibility for obtaining any necessary consents, permissions
109
- or other rights required for any use of the Work.
110
-
111
- d. Affirmer understands and acknowledges that Creative Commons is not a
112
- party to this document and has no duty or obligation with respect to this
113
- CC0 or use of the Work.
114
-
115
- For more information, please see
116
- <http://creativecommons.org/publicdomain/zero/1.0/>