cryptopunks 1.2.2 → 2.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.
@@ -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