mooncats 0.1.0 → 0.1.2

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: a440b059c12270381d0f9b58b277a3f06b7e44d1cc1cdc503965ac2f888a2cb9
4
+ data.tar.gz: e153163695f9e23a07d105f9eccba2d7c666bed7b234f18dead8ca0c1a659ae7
5
5
  SHA512:
6
- metadata.gz: 34f9b2e6117b7a8fe4134dc20cbdb9541cbb97500e278853d76defeca1d48b6fa32d312de84ec557701ccf5835108e4bffbfc1b12bb27f71f031fb6aa6899c13
7
- data.tar.gz: aef96b644a304e287ee44b7539ea2705b7c98c2d9a40cdb4c3ef76ff1b59b96ec380ee3b5ec22d0acf70ed760748fefdfcd554e3eb30ad1cdab79b66362322ec
6
+ metadata.gz: 936a405f9b9abf5cb4118be4c6a3b0d9f49925099a20e56667925b2275c2bcb6a5498055e3c3be2e0c3781297eacb8b616da681a856c061fd553ec533b82344f
7
+ data.tar.gz: 602fe2482ca77d1004368a809080239b66a01456f529d4b4aa30138eed2ca2f852dd2ab57dc38acaf6c3595f493b05156ffaf4b51b29b1821ad5232ae9f291cf
data/Manifest.txt CHANGED
@@ -5,6 +5,9 @@ README.md
5
5
  Rakefile
6
6
  bin/mooncat
7
7
  lib/mooncats.rb
8
+ lib/mooncats/composite.rb
9
+ lib/mooncats/dataset.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/lib/mooncats.rb CHANGED
@@ -12,8 +12,10 @@ require 'optparse'
12
12
  ## our own code
13
13
  require 'mooncats/version' # note: let version always go first
14
14
  require 'mooncats/designs'
15
+ require 'mooncats/structs'
15
16
  require 'mooncats/image'
16
-
17
+ require 'mooncats/composite'
18
+ require 'mooncats/dataset'
17
19
 
18
20
 
19
21
 
@@ -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::WHITE ) # 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,45 @@
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
+ mint = row['mint'].to_i
35
+
36
+ mooncats << Metadata.new( id )
37
+ end
38
+ ## print "\n" ## add progress
39
+
40
+ mooncats
41
+ end
42
+ end # module Dataset
43
+ end # module Mooncat
44
+
45
+
@@ -7,27 +7,22 @@ COLORS_GENESIS_WHITE = ['#555555', '#d3d3d3', '#ffffff', '#aaaaaa', '#ff9999']
7
7
  COLORS_GENESIS_BLACK = ['#555555', '#222222', '#111111', '#bbbbbb', '#ff9999']
8
8
 
9
9
 
10
- def self.generate( cat_id, zoom: 1 )
11
- bytes = hex_to_bytes( cat_id )
10
+ def self.generate( id, zoom: 1 )
11
+ meta = Metadata.new( id )
12
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]
13
+ design = meta.design.to_i # note: meta.design is a struct/object - keep/use a local int !!!
18
14
 
19
- invert = k >= 128
20
- design = k % 128
21
-
22
- colors = if genesis
23
- if design % 2 === 0 && invert ||
24
- design % 2 === 1 && !invert
15
+ colors = if meta.genesis?
16
+ if design % 2 == 0 && meta.invert? ||
17
+ design % 2 == 1 && !meta.invert?
25
18
  COLORS_GENESIS_WHITE
26
19
  else
27
20
  COLORS_GENESIS_BLACK
28
21
  end
29
22
  else
30
- derive_palette( r, g, b, invert: invert )
23
+ derive_palette( meta.r,
24
+ meta.g,
25
+ meta.b, invert: meta.invert? )
31
26
  end
32
27
 
33
28
  new( design: design,
@@ -35,6 +30,13 @@ def self.generate( cat_id, zoom: 1 )
35
30
  zoom: zoom )
36
31
  end
37
32
 
33
+ ### add more (convenience) aliases
34
+ class << self
35
+ alias_method :mint, :generate
36
+ end
37
+
38
+
39
+
38
40
 
39
41
  def initialize( design: 0,
40
42
  colors: COLORS_GENESIS_WHITE,
@@ -80,31 +82,19 @@ end
80
82
  #####
81
83
  # (image) delegates
82
84
  ## todo/check: add some more??
83
- def save( path ) @cat.save( path ); end
85
+ def save( path, constraints = {} )
86
+ @cat.save( path, constraints )
87
+ end
88
+
84
89
  def width() @cat.width; end
85
90
  def height() @cat.height; end
86
91
 
92
+ ## return image ref - use a different name - why? why not?
93
+ def image() @cat; end
87
94
 
88
95
 
89
96
  ##################
90
97
  # (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
98
  def self.derive_palette( r, g, b, invert: false )
109
99
  ## note: Color.rgb returns an Integer (e.g. 34113279 - true color or just hex rgba or?)
110
100
  rgb = ChunkyPNG::Color.rgb( r, g, b )
@@ -0,0 +1,151 @@
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
+ FACES = [
15
+ 'Smile', ## 00
16
+ 'Frown (Look Down)', ## 01
17
+ 'Frown (Look Up)', ## 10
18
+ 'Flat Whiskers', ## 11
19
+ ]
20
+
21
+ FURS = [
22
+ 'Solid', ## 00
23
+ 'Striped', ## 01
24
+ 'Eyepatch', ## 10
25
+ 'Half/Half', ## 11
26
+ ]
27
+
28
+ FACINGS = [
29
+ 'Left', # 0
30
+ 'Right', # 1
31
+ ]
32
+
33
+
34
+
35
+ class Metadata
36
+
37
+ class Design ## nested classed - why? lets you use Metadata::Design "standalone", that is, without 5-byte id
38
+ def initialize( num ) # 0-127 design num(ber)
39
+ @num = num
40
+ end
41
+
42
+ def to_i() @num; end
43
+
44
+ def bits ## keep private / internal - why? why not?
45
+ ## keep 128 possible designs 0 to 127
46
+ ## as 7 bit string e.g. 01010111 for now - why? why not?
47
+ @bits ||= '%08b' % @num
48
+ end
49
+
50
+ def facing
51
+ @facing ||= FACINGS[ bits[1,1].to_i(2) ] ## use desgin > 63 instead - why? why not?
52
+ end
53
+ def face
54
+ @face ||= FACES[ bits[2,2].to_i(2) ]
55
+ end
56
+ def fur
57
+ @fur ||= FURS[ bits[4,2].to_i(2) ]
58
+ end
59
+ def pose
60
+ @poses ||= POSES[ bits[6,2].to_i(2) ] ## use design % 4 instead - why? why not?
61
+ end
62
+ end ## (nested) class Metadata::Design
63
+
64
+
65
+
66
+
67
+ def initialize( id, **more )
68
+ @bytes = self.class.hex_to_bytes( id )
69
+
70
+ ## add support for more "external" meta data
71
+ ## ## e.g. mint, mint_block, etc.
72
+ @more = more
73
+ end
74
+
75
+ def id
76
+ @id ||= @bytes.map { |byte| '%02x' % byte }.join
77
+ end
78
+
79
+ def genesis?
80
+ @bytes[0] != 0 ## note: convert to bool (if zero assume NOT genesis)
81
+ end
82
+ def k() @bytes[1]; end
83
+ def r() @bytes[2]; end
84
+ def g() @bytes[3]; end
85
+ def b() @bytes[4]; end
86
+
87
+ def rgb() [r,g,b]; end ## add rgb shortcut helper - why? why not?
88
+
89
+ def invert?() k >= 128; end
90
+ def pattern() k % 64; end ## treat facing left|right as the same
91
+
92
+
93
+ def design
94
+ @design ||= Design.new( k % 128 )
95
+ end
96
+
97
+ def facing() design.facing; end
98
+ def face() design.face; end
99
+ def fur() design.fur; end
100
+ def pose() design.pose; end
101
+
102
+
103
+ ####
104
+ # more "external" attributes
105
+ def mint() @more[:mint]; end
106
+
107
+ #####
108
+ # enable array-like access to - why? why not?
109
+ def []( key )
110
+ case key.to_sym
111
+ when :id then id
112
+ when :genesis then genesis?
113
+ when :k then k
114
+ when :r then r
115
+ when :g then g
116
+ when :b then b
117
+ when :rgb then rgb
118
+ when :invert then invert?
119
+ when :design then design.to_i
120
+ when :pattern then pattern
121
+ when :facing then facing
122
+ when :face then face
123
+ when :fur then fur
124
+ when :pose then pose
125
+ else
126
+ @more[ key ]
127
+ end
128
+ end
129
+
130
+
131
+ ##################
132
+ # (static) helpers
133
+ def self.hex_to_bytes( str_or_num )
134
+ if str_or_num.is_a?( Integer ) ## allow passing in of integer to e.g. 0x... etc.
135
+ num = str_or_num
136
+ str = '%010x' % num # 5 bytes (10 hex digits/chars)
137
+ else ## assume string
138
+ ## cut-off optionial 0x
139
+ str = str_or_num
140
+ str = str.downcase
141
+ str = str[2..-1] if str.start_with?( '0x')
142
+ end
143
+
144
+ raise ArgumentError, "expected 5 byte hex string (10 digits/chars); got #{str_or_num}" if str.size != 10
145
+
146
+ bytes = [str].pack('H*').bytes
147
+ bytes
148
+ end
149
+ end # class Metadata
150
+
151
+ end # module Mooncats
@@ -3,7 +3,7 @@ module Mooncats
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- PATCH = 0
6
+ PATCH = 2
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mooncats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
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-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png
@@ -92,8 +92,11 @@ files:
92
92
  - Rakefile
93
93
  - bin/mooncat
94
94
  - lib/mooncats.rb
95
+ - lib/mooncats/composite.rb
96
+ - lib/mooncats/dataset.rb
95
97
  - lib/mooncats/designs.rb
96
98
  - lib/mooncats/image.rb
99
+ - lib/mooncats/structs.rb
97
100
  - lib/mooncats/version.rb
98
101
  homepage: https://github.com/cryptocopycats/mooncats
99
102
  licenses: