cryptopunks 1.2.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,23 +5,36 @@ module Cryptopunks
5
5
  ### todo/fix:
6
6
  ## move into Punks::Metadata or such
7
7
  class Sprite
8
- attr_reader :id, :name, :type, :gender
8
+ attr_reader :id, :name, :type, :gender, :size, :more_names
9
9
 
10
10
 
11
11
  def initialize( id:,
12
12
  name:,
13
13
  type:,
14
- gender: )
15
- @id = id # zero-based index eg. 0,1,2,3, etc.
16
- @name = name
17
- @type = type
18
- @gender = gender
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
19
23
  end
20
24
 
21
25
  ## todo/check - find better names for type attribute/archetypes?
22
26
  ## use (alternate name/alias) base or face for archetypes? any others?
23
27
  def attribute?() @type.downcase.start_with?( 'attribute' ); end
24
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
25
38
  end # class Metadata::Sprite
26
39
  end # class Metadata
27
40
 
@@ -33,33 +46,60 @@ module Cryptopunks
33
46
  ######
34
47
  # static helpers - (turn into "true" static self.class methods - why? why not?)
35
48
  #
36
- def normalize_key( str )
37
- str.downcase.gsub(/[ ()°_-]/, '').strip
49
+ def self.normalize_key( str )
50
+ ## add & e.g. B&W
51
+ str.downcase.gsub(/[ ()&°_-]/, '').strip
38
52
  end
39
53
 
40
- def normalize_gender( str )
54
+ def self.normalize_gender( str )
41
55
  ## e.g. Female => f
42
56
  ## F => f
43
- ## always return f or m
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
44
66
  str.downcase[0]
45
67
  end
46
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
+
47
83
 
48
84
  def build_attributes_by_name( recs )
49
85
  h = {}
50
86
  recs.each_with_index do |rec|
51
- key = normalize_key( rec.name )
52
- key << "_(#{rec.gender})" if rec.attribute?
53
-
54
- if h[ key ]
55
- puts "!!! ERROR - attribute name is not unique:"
56
- pp rec
57
- puts "duplicate:"
58
- pp h[key]
59
- exit 1
87
+ names = [rec.name] + rec.more_names
88
+
89
+ names.each do |name|
90
+ key = normalize_key( name )
91
+ key << "_(#{rec.gender}+#{rec.size})" if rec.attribute?
92
+
93
+ if h[ key ]
94
+ puts "!!! ERROR - attribute name is not unique:"
95
+ pp rec
96
+ puts "duplicate:"
97
+ pp h[key]
98
+ exit 1
99
+ end
100
+ h[ key ] = rec
60
101
  end
61
- h[ key ] = rec
62
- end
102
+ end
63
103
  ## pp h
64
104
  h
65
105
  end
@@ -83,16 +123,22 @@ module Cryptopunks
83
123
 
84
124
  ## convert to "wrapped / immutable" kind-of struct
85
125
  recs = recs.map do |rec|
86
- id = rec['id'].to_i( 10 )
87
- name = rec['name']
88
- gender = normalize_gender( rec['gender'] )
89
- type = rec['type']
126
+ id = rec['id'].to_i( 10 )
127
+ name = normalize_name( rec['name'] )
128
+ gender = normalize_gender( rec['gender'] )
129
+ size = normalize_size( rec['size'] )
130
+ type = rec['type']
131
+
132
+ more_names = (rec['more_names'] || '').split( '|' )
133
+ more_names = more_names.map {|str| normalize_name( str ) }
90
134
 
91
135
  Metadata::Sprite.new(
92
- id: id,
93
- name: name,
94
- type: type,
95
- gender: gender)
136
+ id: id,
137
+ name: name,
138
+ type: type,
139
+ gender: gender,
140
+ size: size,
141
+ more_names: more_names )
96
142
  end
97
143
  recs
98
144
  end # method build_recs
@@ -116,27 +162,80 @@ module Cryptopunks
116
162
  alias_method :sheet, :spritesheet
117
163
 
118
164
 
165
+ def records() @recs; end
166
+ alias_method :meta, :records
119
167
 
120
168
 
121
169
 
122
- def find_meta( q, gender: nil ) ## gender (m/f) required for attributes!!!
170
+
171
+ def find_meta( q, gender: nil,
172
+ size: nil,
173
+ style: nil ) ## note: gender (m/f) required for attributes!!!
123
174
 
124
175
  key = normalize_key( q ) ## normalize q(uery) string/symbol
125
- key << "_(#{normalize_gender( gender )})" if gender
126
176
 
127
- rec = @attributes_by_name[ key ]
128
- if rec
129
- puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type} (#{rec.gender})"
130
- # pp rec
177
+ keys = [] ## note allow lookup by more than one keys
178
+ ## e.g. if gender set try f/m first and than try unisex as fallback
179
+ if gender
180
+ gender = normalize_gender( gender )
181
+ ## auto-fill size if not passed in
182
+ ## for f(emale) => s(mall)
183
+ ## m(ale) => l(arge)
184
+ size = if size.nil?
185
+ gender == 'f' ? 's' : 'l'
186
+ else
187
+ normalize_size( size )
188
+ end
189
+
190
+ ###
191
+ # try (auto-add) style-specific version first (fallback to "regular" if not found)
192
+ if style
193
+ ## for now only support natural series
194
+ style_key = if style.downcase.start_with?( 'natural' )
195
+ 'natural'
196
+ else
197
+ puts "!! ERROR - unknown attribute style #{style}; sorry"
198
+ exit 1
199
+ end
200
+
201
+ keys << "#{key}#{style_key}_(#{gender}+#{size})"
202
+ ## auto-add (u)niversal size as fallback
203
+ keys << "#{key}#{style_key}_(#{gender}+u)" if size == 's' || size == 'l'
204
+ ## auto-add u(nisex) as fallback
205
+ keys << "#{key}#{style_key}_(u+#{size})" if gender == 'f' || gender == 'm'
206
+ end
207
+
208
+
209
+ keys << "#{key}_(#{gender}+#{size})"
210
+ ## auto-add (u)niversal size as fallback
211
+ keys << "#{key}_(#{gender}+u)" if size == 's' || size == 'l'
212
+ ## auto-add u(nisex) as fallback
213
+ keys << "#{key}_(u+#{size})" if gender == 'f' || gender == 'm'
131
214
  else
132
- puts "!! WARN - no lookup found for key >#{key}<"
215
+ keys << key
133
216
  end
217
+
218
+
219
+ rec = nil
220
+ keys.each do |key|
221
+ rec = @attributes_by_name[ key ]
222
+ if rec
223
+ puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type} (#{rec.gender}+#{rec.size})"
224
+ # pp rec
225
+ break
226
+ end
227
+ end
228
+
229
+ if rec.nil?
230
+ puts "!! WARN - no lookup found for #{keys.size} key(s) >#{keys.inspect}<"
231
+ end
232
+
134
233
  rec
135
234
  end
136
235
 
137
236
 
138
- def find( q, gender: nil ) ## gender (m/f) required for attributes!!!
139
- rec = find_meta( q, gender: gender )
237
+ def find( q, gender: nil, size: nil, style: nil ) ## gender (m/f) required for attributes!!!
238
+ rec = find_meta( q, gender: gender, size: size, style: style )
140
239
 
141
240
  ## return image if record found
142
241
  rec ? @sheet[ rec.id ] : nil
@@ -145,10 +244,11 @@ module Cryptopunks
145
244
 
146
245
 
147
246
 
148
- def to_recs( *values )
247
+ def to_recs( *values, style: nil )
149
248
  archetype_name = values[0]
150
249
 
151
250
  ### todo/fix: check for nil/not found!!!!
251
+ ## todo/check/fix: assert meta record returned is archetype NOT attribute!!!!
152
252
  archetype = find_meta( archetype_name )
153
253
  if archetype.nil?
154
254
  puts "!! ERROR - archetype >#{archetype}< not found; sorry"
@@ -160,11 +260,15 @@ module Cryptopunks
160
260
  attribute_names = values[1..-1]
161
261
  ## note: attribute lookup requires gender from archetype!!!!
162
262
  attribute_gender = archetype.gender
263
+ attribute_size = archetype.size
163
264
 
164
265
  attribute_names.each do |attribute_name|
165
- attribute = find_meta( attribute_name, gender: attribute_gender )
266
+ attribute = find_meta( attribute_name,
267
+ gender: attribute_gender,
268
+ size: attribute_size,
269
+ style: style )
166
270
  if attribute.nil?
167
- puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}) not found; sorry"
271
+ puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}+#{attribute_size}) not found; sorry"
168
272
  exit 1
169
273
  end
170
274
  recs << attribute
@@ -176,12 +280,13 @@ module Cryptopunks
176
280
 
177
281
 
178
282
 
179
- def generate_image( *values, background: nil )
283
+ def generate_image( *values, style: nil,
284
+ background: nil )
180
285
 
181
286
  ids = if values[0].is_a?( Integer ) ## assume integer number (indexes)
182
287
  values
183
288
  else ## assume strings (names)
184
- to_recs( *values ).map { |rec| rec.id }
289
+ to_recs( *values, style: style ).map { |rec| rec.id }
185
290
  end
186
291
 
187
292
 
@@ -1,45 +1,6 @@
1
1
  module Cryptopunks
2
2
 
3
3
 
4
- class Design ## todo/fix - move to its own file!!!
5
-
6
- end # class Design
7
-
8
-
9
-
10
- ##############
11
- ## todo/check:
12
- ## find a better way to (auto?) include more designs?
13
- class DesignSeries ## find a better name for class (just use Series?) - why? why not?
14
- def self.build( dir )
15
- data = {}
16
- paths = Dir.glob( "#{dir}/**.txt" )
17
- paths.each do |path|
18
- basename = File.basename( path, File.extname( path ) )
19
- text = File.open( path, 'r:utf-8' ) { |f| f.read }
20
- ## todo/check: auto-parse "ahead of time" here
21
- ## or keep "raw" text - why? why not?
22
- data[ basename ] = text
23
- end
24
- data
25
- end
26
-
27
- def initialize( dir )
28
- @dir = dir # e.g. "#{Cryptopunks.root}/config/more"
29
- end
30
-
31
- def data
32
- ## note: lazy load / build on first demand only
33
- @data ||= self.class.build( @dir )
34
- end
35
-
36
- def [](key) data[ key ]; end
37
- def size() data.size; end
38
- def keys() data.keys; end
39
- def to_h() data; end ## todo/check: use to_hash() - why? why not?
40
- end # class DesignSeries
41
-
42
-
43
4
 
44
5
  class Image
45
6
 
@@ -49,65 +10,16 @@ def self.read( path ) ## convenience helper
49
10
  end
50
11
 
51
12
 
13
+
14
+ ### keep design & colors keyword args in c'tor here
15
+ ## or use parse() like in pixelart - why? why not?
16
+
52
17
  def initialize( initial=nil, design: nil,
53
18
  colors: nil )
54
19
  if initial
55
20
  ## pass image through as-is
56
21
  img = initial
57
22
  else
58
-
59
- ## todo/fix:
60
- ## move design code into design class!!!
61
- ## for now assume design is a string
62
- ## split into parts
63
- ## original/alien-male or original@alien-male
64
- ## more/alien-female or more@alien-female
65
- ## original/human-male+darker or original@human-male!darker ????
66
- ## human-male!darker ?????
67
- ## keep @ as separator too - why? why not?
68
- parts = design.split( %r{[@/]} )
69
- parts.unshift( '*' ) if parts.size == 1 ## assume "all-in-one" series (use * as name/id/placeholder)
70
-
71
- series_key = parts[0]
72
- design_composite = parts[1]
73
-
74
- ## todo/check - find a way for unambigious (color) variant key
75
- ## use unique char e.g. +*!# or such
76
- more_parts = design_composite.split( %r{[!+]} )
77
- design_key = more_parts[0]
78
- variant_key = more_parts[1] ## color variant for now (for humans) e.g. lighter/light/dark/darker
79
-
80
- series = if ['*','**','_','__'].include?( series_key )
81
- DESIGNS ## use all-series-in-one collection
82
- else
83
- case series_key
84
- when 'original' then DESIGNS_ORIGINAL
85
- when 'more' then DESIGNS_MORE
86
- else raise ArgumentError, "unknown design series >#{series_key}<; sorry"
87
- end
88
- end
89
-
90
- design = series[ design_key ]
91
- raise ArgumentError, "unknow design >#{design_key}< in series >#{series_key}<; sorry" if design.nil?
92
-
93
- if colors.nil? ## try to auto-fill in colors
94
- ## note: (auto-)remove _male,_female qualifier if exist
95
- colors_key = design_key.sub( '-male', '' ).sub( '-female', '' )
96
- colors = COLORS[ colors_key ]
97
-
98
- ## allow / support color scheme variants (e.g. lighter/light/dark/darker) etc.
99
- if colors.is_a?(Hash)
100
- if variant_key
101
- colors = colors[ variant_key ]
102
- raise ArgumentError, "no colors defined for variant >#{variant_key}< for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
103
- else ## note: use (fallback to) first color scheme if no variant key present
104
- colors = colors[ colors.keys[0] ]
105
- end
106
- end
107
-
108
- raise ArgumentError, "no (default) colors defined for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
109
- end
110
-
111
23
  ## note: unwrap inner image before passing on to super c'tor
112
24
  img = Pixelart::Image.parse( design, colors: colors ).image
113
25
  end