mooncats 0.1.0 → 1.1.0

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 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/>