punks 0.0.1 → 0.2.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.
Binary file
@@ -0,0 +1,49 @@
1
+ id, category, name, more_names
2
+ 0, , male1,
3
+ 1, , male2,
4
+ 2, , male3,
5
+ 3, , female1,
6
+ 4, , female2,
7
+ 5, , female3,
8
+ 6, , Zombie 1, Zombie
9
+ 7, , zombie2,
10
+ 8, , zombie3,
11
+ 9, , zombie4,
12
+ 10, , Ape 1, Ape
13
+ 11, , ape2,
14
+ 12, , ape3,
15
+ 13, , ape4,
16
+ 14, , ape5,
17
+ 15, , ape6,
18
+ 16, , ape7,
19
+ 17, , ape8,
20
+ 18, , Alien 1, Alien
21
+ 19, , alien2,
22
+ 20, , alien3,
23
+ 21, , blonde_bob,
24
+ 22, , crazy_hair,
25
+ 23, , half_shaved,
26
+ 24, , mohawk,
27
+ 25, , wild_hair,
28
+ 26, , bandana,
29
+ 27, , beanie,
30
+ 28, , cap_forward,
31
+ 29, , cowboy_hat,
32
+ 30, , headband,
33
+ 31, , knitted_cap,
34
+ 32, , top_hat,
35
+ 33, , 3d_glasses,
36
+ 34, , big_shades,
37
+ 35, , small_shades,
38
+ 36, , nerd_glasses,
39
+ 37, , eye_patch,
40
+ 38, , goat,
41
+ 39, , green_eye_shadow,
42
+ 40, , purple_eye_shadow,
43
+ 41, , hot_lipstick,
44
+ 42, , smile,
45
+ 43, , earring,
46
+ 44, , cigarette,
47
+ 45, , medical_mask,
48
+ 46, , pipe,
49
+ 47, , hoodie,
Binary file
@@ -0,0 +1,302 @@
1
+
2
+ module Punk
3
+
4
+ class Metadata
5
+ ### todo/fix:
6
+ ## move into Punks::Metadata or such
7
+ class Sprite
8
+ attr_reader :id, :name, :type, :gender, :size, :more_names
9
+
10
+
11
+ def initialize( id:,
12
+ name:,
13
+ type:,
14
+ gender:,
15
+ size:,
16
+ more_names: [] )
17
+ @id = id # zero-based index eg. 0,1,2,3, etc.
18
+ @name = name
19
+ @type = type
20
+ @gender = gender
21
+ @size = size
22
+ @more_names = more_names
23
+ end
24
+
25
+ ## todo/check - find better names for type attribute/archetypes?
26
+ ## use (alternate name/alias) base or face for archetypes? any others?
27
+ def attribute?() @type.downcase.start_with?( 'attribute' ); end
28
+ def archetype?() @type.downcase.start_with?( 'archetype' ); end
29
+
30
+ def small?() @size == 's'; end
31
+ def large?() @size == 'l'; end
32
+ def universal?() @size == 'u'; end
33
+ alias_method :unisize?, :universal? ## add unisize or allsizes or such - why? why not?
34
+
35
+ def male?() @gender == 'm'; end
36
+ def female?() @gender == 'f'; end
37
+ def unisex?() @gender == 'u'; end
38
+ end # class Metadata::Sprite
39
+ end # class Metadata
40
+
41
+
42
+
43
+
44
+ class Generator
45
+
46
+ ######
47
+ # static helpers - (turn into "true" static self.class methods - why? why not?)
48
+ #
49
+ def self.normalize_key( str )
50
+ ## add & e.g. B&W
51
+ str.downcase.gsub(/[ ()&°_-]/, '').strip
52
+ end
53
+
54
+ def self.normalize_gender( str )
55
+ ## e.g. Female => f
56
+ ## F => f
57
+ ## always return f/m
58
+ str.downcase[0]
59
+ end
60
+
61
+ def self.normalize_size( str )
62
+ ## e.g. U or Unisize or Univeral => u
63
+ ## S or Small => s
64
+ ## L or Large => l
65
+ ## always return u/l/s
66
+ str.downcase[0]
67
+ end
68
+
69
+ def self.normalize_name( str )
70
+ ## normalize spaces in more names
71
+ str.strip.gsub( /[ ]{2,}/, ' ' )
72
+ end
73
+
74
+ def normalize_key( str ) self.class.normalize_key( str ); end
75
+ def normalize_gender( str ) self.class.normalize_gender( str ); end
76
+ def normalize_size( str ) self.class.normalize_size( str ); end
77
+ def normalize_name( str ) self.class.normalize_name( str ); end
78
+
79
+
80
+
81
+
82
+ def build_attributes_by_name( recs )
83
+ h = {}
84
+ recs.each_with_index do |rec|
85
+ names = [rec.name] + rec.more_names
86
+
87
+ names.each do |name|
88
+ key = normalize_key( name )
89
+ key << "_(#{rec.gender}+#{rec.size})" if rec.attribute?
90
+
91
+ if h[ key ]
92
+ puts "!!! ERROR - attribute name is not unique:"
93
+ pp rec
94
+ puts "duplicate:"
95
+ pp h[key]
96
+ exit 1
97
+ end
98
+ h[ key ] = rec
99
+ end
100
+ end
101
+ ## pp h
102
+ h
103
+ end
104
+
105
+
106
+ def build_recs( recs ) ## build and normalize (meta data) records
107
+
108
+ ## sort by id
109
+ recs = recs.sort do |l,r|
110
+ l['id'].to_i( 10 ) <=> r['id'].to_i( 10 ) # use base10 (decimal)
111
+ end
112
+
113
+ ## assert all recs are in order by id (0 to size)
114
+ recs.each_with_index do |rec, exp_id|
115
+ id = rec['id'].to_i(10)
116
+ if id != exp_id
117
+ puts "!! ERROR - meta data record ids out-of-order - expected id #{exp_id}; got #{id}"
118
+ exit 1
119
+ end
120
+ end
121
+
122
+ ## convert to "wrapped / immutable" kind-of struct
123
+ recs = recs.map do |rec|
124
+ id = rec['id'].to_i( 10 )
125
+ name = normalize_name( rec['name'] )
126
+ gender = normalize_gender( rec['gender'] )
127
+ size = normalize_size( rec['size'] )
128
+ type = rec['type']
129
+
130
+ more_names = (rec['more_names'] || '').split( '|' )
131
+ more_names = more_names.map {|str| normalize_name( str ) }
132
+
133
+ Metadata::Sprite.new(
134
+ id: id,
135
+ name: name,
136
+ type: type,
137
+ gender: gender,
138
+ size: size,
139
+ more_names: more_names )
140
+ end
141
+ recs
142
+ end # method build_recs
143
+
144
+
145
+
146
+
147
+ def initialize( image_path="./spritesheet.png",
148
+ meta_path="./spritesheet.csv",
149
+ image_class: )
150
+ @image_class = image_class
151
+
152
+ @sheet = Pixelart::ImageComposite.read( image_path,
153
+ width: 24,
154
+ height: 24 )
155
+ recs = CsvHash.read( meta_path )
156
+
157
+ @recs = build_recs( recs )
158
+
159
+ ## lookup by "case/space-insensitive" name / key
160
+ @attributes_by_name = build_attributes_by_name( @recs )
161
+ end
162
+
163
+
164
+ def spritesheet() @sheet; end
165
+ alias_method :sheet, :spritesheet
166
+
167
+
168
+ def records() @recs; end
169
+ alias_method :meta, :records
170
+
171
+
172
+
173
+
174
+ def find_meta( q, gender: nil,
175
+ size: nil,
176
+ style: nil ) ## note: gender (m/f) required for attributes!!!
177
+
178
+ key = normalize_key( q ) ## normalize q(uery) string/symbol
179
+
180
+ keys = [] ## note allow lookup by more than one keys
181
+ ## e.g. if gender set try f/m first and than try unisex as fallback
182
+ if gender
183
+ gender = normalize_gender( gender )
184
+ ## auto-fill size if not passed in
185
+ ## for f(emale) => s(mall)
186
+ ## m(ale) => l(arge)
187
+ size = if size.nil?
188
+ gender == 'f' ? 's' : 'l'
189
+ else
190
+ normalize_size( size )
191
+ end
192
+
193
+ ###
194
+ # try (auto-add) style-specific version first (fallback to "regular" if not found)
195
+ if style
196
+ ## for now only support natural series
197
+ style_key = if style.downcase.start_with?( 'natural' )
198
+ 'natural'
199
+ else
200
+ puts "!! ERROR - unknown attribute style #{style}; sorry"
201
+ exit 1
202
+ end
203
+
204
+ keys << "#{key}#{style_key}_(#{gender}+#{size})"
205
+ ## auto-add (u)niversal size as fallback
206
+ keys << "#{key}#{style_key}_(#{gender}+u)" if size == 's' || size == 'l'
207
+ ## auto-add u(nisex) as fallback
208
+ keys << "#{key}#{style_key}_(u+#{size})" if gender == 'f' || gender == 'm'
209
+ end
210
+
211
+
212
+ keys << "#{key}_(#{gender}+#{size})"
213
+ ## auto-add (u)niversal size as fallback
214
+ keys << "#{key}_(#{gender}+u)" if size == 's' || size == 'l'
215
+ ## auto-add u(nisex) as fallback
216
+ keys << "#{key}_(u+#{size})" if gender == 'f' || gender == 'm'
217
+ else
218
+ keys << key
219
+ end
220
+
221
+
222
+ rec = nil
223
+ keys.each do |key|
224
+ rec = @attributes_by_name[ key ]
225
+ if rec
226
+ puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type} (#{rec.gender}+#{rec.size})"
227
+ # pp rec
228
+ break
229
+ end
230
+ end
231
+
232
+ if rec.nil?
233
+ puts "!! WARN - no lookup found for #{keys.size} key(s) >#{keys.inspect}<"
234
+ end
235
+
236
+ rec
237
+ end
238
+
239
+
240
+ def find( q, gender: nil, size: nil, style: nil ) ## gender (m/f) required for attributes!!!
241
+ rec = find_meta( q, gender: gender, size: size, style: style )
242
+
243
+ ## return image if record found
244
+ rec ? @sheet[ rec.id ] : nil
245
+ end
246
+
247
+
248
+
249
+
250
+ def to_recs( *values, style: nil )
251
+ archetype_name = values[0]
252
+
253
+ ### todo/fix: check for nil/not found!!!!
254
+ ## todo/check/fix: assert meta record returned is archetype NOT attribute!!!!
255
+ archetype = find_meta( archetype_name )
256
+ if archetype.nil?
257
+ puts "!! ERROR - archetype >#{archetype}< not found; sorry"
258
+ exit 1
259
+ end
260
+
261
+ recs = [archetype]
262
+
263
+ attribute_names = values[1..-1]
264
+ ## note: attribute lookup requires gender from archetype!!!!
265
+ attribute_gender = archetype.gender
266
+ attribute_size = archetype.size
267
+
268
+ attribute_names.each do |attribute_name|
269
+ attribute = find_meta( attribute_name,
270
+ gender: attribute_gender,
271
+ size: attribute_size,
272
+ style: style )
273
+ if attribute.nil?
274
+ puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}+#{attribute_size}) not found; sorry"
275
+ exit 1
276
+ end
277
+ recs << attribute
278
+ end
279
+
280
+ recs
281
+ end
282
+
283
+
284
+
285
+
286
+ def generate_image( *values, style: nil )
287
+
288
+ recs = to_recs( *values, style: style )
289
+
290
+ punk = @image_class.new( 24, 24 )
291
+
292
+ recs.each do |rec|
293
+ punk.compose!( @sheet[ rec.id ] )
294
+ end
295
+
296
+ punk
297
+ end
298
+ alias_method :generate, :generate_image
299
+
300
+ end # class Generator
301
+
302
+ end # module Punk
@@ -0,0 +1,20 @@
1
+
2
+
3
+ module Marilyn
4
+
5
+ class Image < Pixelart::Image
6
+
7
+ MARILYN_ATTRIBUTES = ['Female 3', 'Wild Blonde', 'Mole',
8
+ 'Blue Eye Shadow']
9
+
10
+ def self.generate( *values, style: nil )
11
+ punk = Punk::Image.generate( *MARILYN_ATTRIBUTES,
12
+ *values, style: style )
13
+ phunk = punk.mirror
14
+
15
+ ## wrap in Marilyn class (note: use/ requires inner image)
16
+ new( phunk.width, phunk.height, phunk.image )
17
+ end
18
+ end # class Image
19
+ end # module Marilyn
20
+
@@ -0,0 +1,24 @@
1
+
2
+
3
+ module Philip
4
+
5
+ class Image < Pixelart::Image
6
+
7
+ ## note: right-looking ("pre-phlipped") philip
8
+ PHILIP = Pixelart::Image.read( "#{Pixelart::Module::Punks.root}/config/philip-24x24.png" )
9
+
10
+ def self.generate( *values )
11
+ punk = Image.new( 24, 24 )
12
+ punk.compose!( PHILIP )
13
+
14
+ values.each do |name|
15
+ attribute = Punk::Sheet.find_by( name: name,
16
+ gender: 'm' )
17
+ punk.compose!( attribute )
18
+ end
19
+
20
+ phunk = punk.mirror
21
+ phunk
22
+ end
23
+ end # class Image
24
+ end # module Philip
@@ -0,0 +1,14 @@
1
+
2
+ module Phunk
3
+
4
+ class Image < Pixelart::Image
5
+
6
+ def self.generate( *values, style: nil )
7
+ punk = Punk::Image.generate( *values, style: style )
8
+ phunk = punk.mirror
9
+
10
+ ## wrap in Phunks class (note: use/ requires inner image)
11
+ new( phunk.width, phunk.height, phunk.image )
12
+ end
13
+ end # class Image
14
+ end # module Phunk
@@ -0,0 +1,116 @@
1
+
2
+
3
+ module Punk
4
+
5
+ class Image < Pixelart::Image
6
+ def self.generator
7
+ ### todo/fix: break out/ split off spritesheet from generator!!!!
8
+ @generator ||= Generator.new( "#{Pixelart::Module::Punks.root}/config/punks-24x24.png",
9
+ "#{Pixelart::Module::Punks.root}/config/punks-24x24.csv", image_class: Image )
10
+ end
11
+
12
+
13
+ def self.generate( *values, style: nil )
14
+
15
+ ##### add style option / hack - why? why not?
16
+ if style
17
+ values = if style.downcase.index( 'natural') && style.downcase.index( '2')
18
+ ["#{values[0]} (N2)"] + values[1..-1]
19
+ elsif style.downcase[0] == 'n' ## starting with n - always assume natural(s)
20
+ ## auto-add (N) for Natural to archetype
21
+ ["#{values[0]} (N)"] + values[1..-1]
22
+ else
23
+ puts "!! ERROR - unknown punk style #{style}; sorry"
24
+ exit 1
25
+ end
26
+ end
27
+
28
+
29
+ ###### hack for black&white
30
+ ## auto-add b&w (black&white) to all attribute names e.g.
31
+ ## Eyes => Eyes B&W
32
+ ## Smile => Smile B&W
33
+ ## ....
34
+
35
+ archetype_key = Generator.normalize_key( values[0] )
36
+ if archetype_key.end_with?( 'bw' ) || ## e.g. B&W
37
+ archetype_key.end_with?( '1bit') ## e.g. 1-Bit or 1Bit
38
+
39
+ values = [values[0]] + values[1..-1].map do |attribute|
40
+ attribute_key = Generator.normalize_key( attribute )
41
+ if ['wildhair'].include?( attribute_key ) ## pass through known b&w attributes by "default"
42
+ attribute
43
+ elsif attribute_key.index( "black" )
44
+ attribute ## pass through as-is
45
+ else
46
+ "#{attribute} B&W"
47
+ end
48
+ end
49
+
50
+ pp values
51
+ end
52
+
53
+
54
+ # note: female mouth by default has "custom" colors (not black)
55
+ # for every 1/2/3/4 (human) skin tone and for zombie
56
+ # auto-"magically" add mapping
57
+ #
58
+ # todo/check/fix - add more "contraints"
59
+ # for mapping to only kick-in for "basic" versions
60
+ # and not "colored" e.g. golden and such - why? why not?
61
+ #
62
+ # move this mapping here to "post-lookup" processing
63
+ # to get/incl. more "meta" attribute info - why? why not?
64
+ if archetype_key.index( 'female1' ) ||
65
+ archetype_key.index( 'female2' ) ||
66
+ archetype_key.index( 'female3' ) ||
67
+ archetype_key.index( 'female4' ) ||
68
+ archetype_key.index( 'zombiefemale' )
69
+
70
+ values = [values[0]] + values[1..-1].map do |attribute|
71
+ attribute_key = Generator.normalize_key( attribute )
72
+
73
+ if attribute_key == 'smile' || attribute_key == 'frown'
74
+ attribute += if archetype_key.index( 'zombiefemale' ) then ' Zombie'
75
+ elsif archetype_key.index( 'female1' ) then ' 1'
76
+ elsif archetype_key.index( 'female2' ) then ' 2'
77
+ elsif archetype_key.index( 'female3' ) then ' 3'
78
+ elsif archetype_key.index( 'female4' ) then ' 4'
79
+ else
80
+ puts "!! ERROR - smile or frown (mouth expression) not supported for archetype:"
81
+ pp values
82
+ exit 1
83
+ end
84
+ end
85
+ attribute
86
+ end
87
+ end
88
+
89
+ generator.generate( *values, style: style )
90
+ end # method Image.generate
91
+ end # class Image
92
+
93
+
94
+
95
+ class Spritesheet
96
+ def self.builtin
97
+ ## for now get spritesheet via generator
98
+ Image.generator
99
+ end
100
+ ## note: for now class used for "namespace" only
101
+ def self.find_by( name:, gender: nil, size: nil ) ## return archetype/attribute image by name
102
+ # note: pass along name as q (query string)
103
+ builtin.find( name,
104
+ gender: gender,
105
+ size: size )
106
+ end
107
+ end # class Spritesheet
108
+ ## add convenience (alternate spelling) alias - why? why not?
109
+ SpriteSheet = Spritesheet
110
+ Sheet = Spritesheet
111
+ Sprite = Spritesheet
112
+ end # module Punk
113
+
114
+
115
+
116
+
@@ -0,0 +1,36 @@
1
+
2
+
3
+ module Punkxl
4
+
5
+ class Spritesheet
6
+ def self.builtin
7
+ @builtin ||= Pixelart::Spritesheet.read( "#{Pixelart::Module::Punks.root}/config/punks_xl-32x32.png",
8
+ "#{Pixelart::Module::Punks.root}/config/punks_xl-32x32.csv",
9
+ width: 32,
10
+ height: 32 )
11
+ end
12
+
13
+ ## note: for now class used for "namespace" only
14
+ def self.find_by( name: ) ## return archetype/attribute image by name
15
+ builtin.find_by( name: name )
16
+ end
17
+ end # class Spritesheet
18
+ ## add convenience (alternate spelling) alias - why? why not?
19
+ SpriteSheet = Spritesheet
20
+ Sheet = Spritesheet
21
+ Sprite = Spritesheet
22
+
23
+
24
+ class Image < Pixelart::Image
25
+ def self.generator
26
+ @generator ||= Artfactory.use( Punkxl::Sheet.builtin,
27
+ image_class: Image )
28
+ end
29
+ def self.generate( *names )
30
+ generator.generate( *names )
31
+ end
32
+ end # class Image
33
+
34
+
35
+ end # module Punkxl
36
+
data/lib/punks/version.rb CHANGED
@@ -4,8 +4,8 @@ module Module
4
4
  module Punks
5
5
 
6
6
  MAJOR = 0
7
- MINOR = 0
8
- PATCH = 1
7
+ MINOR = 2
8
+ PATCH = 0
9
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
10
10
 
11
11
  def self.version
data/lib/punks.rb CHANGED
@@ -1,11 +1,54 @@
1
1
  ## 3rd party
2
2
  require 'pixelart/base'
3
-
3
+ require 'backgrounds/base'
4
+ require 'artfactory/base'
4
5
 
5
6
 
6
7
  ## our own code
7
8
  require 'punks/version' # note: let version always go first
8
9
 
10
+ require 'punks/generator'
11
+
12
+
13
+ ## --- 24x24 series
14
+ require 'punks/punks'
15
+ require 'punks/phunks'
16
+ require 'punks/marilyns'
17
+ require 'punks/philips'
18
+ ## --- 32x32 series
19
+ require 'punks/punks_xl'
20
+
21
+
22
+
23
+
24
+
25
+ ### add some convenience shortcuts
26
+ Cryptopunks = Punk
27
+ CryptoPunks = Punk
28
+ Punks = Punk
29
+ ## add singular too -why? why not?
30
+ Cryptopunk = Punk
31
+ CryptoPunk = Punk
32
+
33
+
34
+ ### add some convenience shortcuts / alternate spelling variants
35
+ PunkXL = Punkxl
36
+ PunkXl = Punkxl
37
+
38
+ ### add some convenience shortcuts / alternate spelling variants
39
+ Phunks = Phunk
40
+
41
+ ### add some convenience shortcuts / alternate spelling variants
42
+ Philips = Philip
43
+
44
+ ### add some convenience shortcuts / alternate spelling variants
45
+ Marilyns = Marilyn
46
+
47
+
48
+
49
+ ###
50
+ # note: for convenience auto include Pixelart namespace!!! - why? why not?
51
+ include Pixelart
9
52
 
10
53
 
11
54