profilepic 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,499 +1,286 @@
1
-
2
1
  ###
3
- # helpers
4
- # to generate
5
-
6
- def radio_options_w_sprites( options,
7
- name:,
8
- legend: )
2
+ # serve-up built-in spritesheet(s) and metadata
3
+ FILES = {
4
+ # doge
5
+ 'doge-24x24.png' => "#{Shibainus.root}/config/spritesheet.png",
6
+ 'doge-24x24.csv' => "#{Shibainus.root}/config/spritesheet.csv",
7
+ # marcs
8
+ 'marcs-24x24.png' => "#{Pixelart::Module::Punks.root}/config/marcs-24x24.png",
9
+ 'marcs-24x24.csv' => "#{Pixelart::Module::Punks.root}/config/marcs-24x24.csv",
10
+ # saudis
11
+ 'saudis-24x24.png' => "#{Pixelart::Module::Punks.root}/config/saudis-24x24.png",
12
+ 'saudis-24x24.csv' => "#{Pixelart::Module::Punks.root}/config/saudis-24x24.csv",
13
+ # yeoldepunks
14
+ 'yeoldepunks-24x24.png' => "#{Pixelart::Module::Punks.root}/config/yeoldepunks-24x24.png",
15
+ 'yeoldepunks-24x24.csv' => "#{Pixelart::Module::Punks.root}/config/yeoldepunks-24x24.csv",
16
+ }
17
+
18
+ SPRITESHEETS = {
19
+ doge: ['doge-24x24.png', 'doge-24x24.csv'],
20
+ marcs: ['marcs-24x24.png', 'marcs-24x24.csv'],
21
+ saudis: ['saudis-24x24.png', 'saudis-24x24.csv'],
22
+ yeoldepunks: ['yeoldepunks-24x24.png', 'yeoldepunks-24x24.csv'],
23
+ }
24
+
25
+
26
+
27
+ def render_spritesheet( type, image_path, meta_path, zoom: 2 )
28
+ buf = String.new('')
29
+ buf += <<TXT
9
30
 
10
- buf = <<TXT
11
- <fieldset>
12
- <legend>#{legend}:</legend>
31
+ <h1> #{type} Spritesheet </h1>
13
32
  TXT
14
33
 
15
- options.each_with_index do |option,i|
34
+ ## note: map "virtual" to real path
35
+ path = FILES[ meta_path ]
36
+ recs = read_csv( path )
16
37
 
17
- value = option.downcase
38
+ recs.each do |rec|
39
+ id = rec['id']
40
+ name = rec['name']
41
+ more_names = (rec['more_names'] || '').split( '|' )
18
42
 
19
- ## auto-extract qualifiers e.g. (m)ale/(f)emale
20
- qualifiers = if value.index( '(m)' ) then ['m']
21
- elsif value.index( '(f)' ) then ['f']
22
- elsif value.index( '(m/f)' ) then ['m', 'f']
23
- else []
24
- end
43
+ names = [name]
44
+ names += more_names
25
45
 
26
- value = value.sub( '(m)', '' ).sub( '(f)', '' ).sub( '(m/f)', '' ).sub( '¹', '' )
27
- value = value.strip
46
+ key = name
47
+ if rec.has_key?( 'gender' )
48
+ archetype = (rec['type'] || rec['category'] || '').downcase.strip == 'archetype'
49
+ if archetype
50
+ ## do nothing for base archetype (e.g. male 1, alien, etc.)
51
+ else
52
+ ## auto-add qualifer e.g. (m) or (f) via gender
53
+ gender = (rec['gender'] || 'm').downcase.strip
54
+ key = "#{name} (#{gender})"
55
+ names = names.map { |name| "#{name} (#{gender})"}
56
+ end
57
+ end
28
58
 
29
59
 
60
+ buf << "#{id} "
61
+ buf << "<span class='sprite' data-name='#{key}'></span>"
62
+ buf << " "
63
+ buf << names.join( ' | ' )
64
+ buf << "<br>\n"
65
+ end
30
66
 
31
- label = option
32
67
 
33
68
  buf += <<TXT
34
- <div>
35
- <input type="radio" id="#{name}#{i}" name="#{name}" value="#{value}"
36
- #{ i==0 ? 'checked' : '' }>
69
+ <script>
70
+ var sheet = Spritesheet.read( '#{image_path}',
71
+ `#{spritesheet( meta_path )}` );
72
+ sheet.draw();
73
+ </script>
37
74
  TXT
38
75
 
39
- if ['none'].include?( value )
40
- ## do nothing - no sprite(s) - for none & friends
41
- elsif qualifiers.empty?
42
- buf += %Q[ <span class="sprite" data-name="#{value}"></span>\n]
43
- else
44
- qualifiers.each do |qualifier|
45
- buf += %Q[ <span class="sprite" data-name="#{value} (#{qualifier})"></span>\n]
46
- end
47
- end
76
+ buf
48
77
 
49
- buf += <<TXT
50
- <label for="#{name}#{i}">#{label}</label>
51
- </div>
52
- TXT
53
78
  end
54
79
 
55
80
 
56
- buf += "</fieldset>\n"
57
- buf
58
81
 
59
- end
82
+ class ProfilepicService < Sinatra::Base
60
83
 
61
84
 
62
- def radio_options( options,
63
- name:,
64
- legend: )
85
+ get '/' do
86
+ erb :index
87
+ end
65
88
 
66
- buf = <<TXT
67
- <fieldset>
68
- <legend>#{legend}:</legend>
69
- TXT
89
+ get '/marcs' do
90
+ erb :marcs
91
+ end
70
92
 
71
- options.each_with_index do |option,i|
93
+ get '/saudis' do
94
+ erb :saudis
95
+ end
72
96
 
73
- value = option.downcase
74
- value = value.sub( '(m)', '' ).sub( '(f)', '' ).sub( '(m/f)', '' ).sub( '¹', '' )
75
- value = value.strip
97
+ get '/doge' do
98
+ erb :doge
99
+ end
76
100
 
77
- label = option
101
+ get '/yeoldepunks' do
102
+ erb :yeoldepunks
103
+ end
78
104
 
79
- buf += <<TXT
80
- <div>
81
- <input type="radio" id="#{name}#{i}" name="#{name}" value="#{value}"
82
- #{ i==0 ? 'checked' : '' }>
83
- <label for="#{name}#{i}">#{label}</label>
84
- </div>
85
- TXT
86
- end
87
105
 
88
- buf += "</fieldset>\n"
89
- buf
90
- end
91
106
 
107
+ get '/spritesheets' do
92
108
 
93
- YEOLDEPUNK_ARCHETYPE = [
94
- 'Male 1',
95
- 'Male 2',
96
- 'Male 3',
97
- 'Male 4',
98
- 'Female 1',
99
- 'Female 2',
100
- 'Female 3',
101
- 'Female 4',
102
- 'Zombie',
103
- 'Ape',
104
- 'Alien',
105
- ]
106
-
107
-
108
- YEOLDEPUNK_HAIR = [
109
- 'Shaved Head (m)',
110
- 'Peak Spike (m)',
111
- 'Vampire Hair (m)',
112
- 'Purple Hair (m)',
113
-
114
- 'Mohawk (m/f)',
115
- 'Mohawk Dark (m/f)',
116
- 'Mohawk Thin (m/f)',
117
- 'Wild Hair (m/f)',
118
- 'Crazy Hair (m/f)',
119
- 'Messy Hair (m/f)',
120
- 'Frumpy Hair (m/f)',
121
- 'Stringy Hair (m/f)',
122
- 'Clown Hair Green (m/f)',
123
-
124
- 'Straight Hair (f)',
125
- 'Straight Hair Dark (f)',
126
- 'Straight Hair Blonde (f)',
127
- 'Blonde Short (f)',
128
- 'Blonde Bob (f)',
129
- 'Wild Blonde (f)',
130
- 'Wild White Hair (f)',
131
- 'Orange Side (f)',
132
- 'Dark Hair (f)',
133
- 'Pigtails (f)',
134
- 'Pink With Hat¹ (f)',
135
- 'Half Shaved (f)',
136
- 'Red Mohawk (f)',
137
- ]
138
-
139
- YEOLDEPUNK_BEARD = [
140
- 'Shadow Beard (m)',
141
- 'Normal Beard (m)',
142
- 'Normal Beard Black (m)',
143
- 'Big Beard (m)',
144
- 'Luxurious Beard (m)',
145
- 'Mustache (m)',
146
- 'Goat (m)',
147
- 'Handlebars (m)',
148
- 'Front Beard (m)',
149
- 'Front Beard Dark (m)',
150
- 'Chinstrap (m)',
151
- 'Muttonchops (m)',
152
- ]
153
-
154
- YEOLDEPUNK_EYEWEAR = [
155
- 'Small Shades (m)',
156
- 'Regular Shades (m/f)',
157
- 'Classic Shades (m/f)',
158
- 'Big Shades (m/f)',
159
- 'Nerd Glasses (m/f)',
160
- 'Horned Rim Glasses (m/f)',
161
- '3D Glasses (m/f)',
162
- 'VR (m/f)',
163
- 'Eye Mask (m/f)',
164
- 'Eye Patch (m/f)',
165
- 'Welding Goggles (f)',
166
- ]
167
-
168
- YEOLDEPUNK_EARRING = [
169
- 'Earring (m/f)'
170
- ]
171
-
172
-
173
- YEOLDEPUNK_HEADWEAR = [
174
- 'Cowboy Hat (m)',
175
- 'Fedora (m)',
176
- 'Hoodie (m)',
177
- 'Beanie (m)',
178
- 'Top Hat (m)',
179
- 'Do-rag (m)',
180
- 'Police Cap (m)',
181
- 'Cap Forward (m)',
182
- 'Cap (m/f)',
183
- 'Knitted Cap (m/f)',
184
- 'Bandana (m/f)',
185
- 'Headband¹ (m/f)',
186
- 'Pilot Helmet (f)',
187
- 'Tassle Hat (f)',
188
- 'Tiara (f)',
189
- ]
190
-
191
- YEOLDEPUNK_EYES = [
192
- 'Clown Eyes Green (m/f)',
193
- 'Clown Eyes Blue (m/f)',
194
- 'Green Eye Shadow (f)',
195
- 'Blue Eye Shadow (f)',
196
- 'Purple Eye Shadow (f)',
197
- ]
198
-
199
- YEOLDEPUNK_MOUTH = [
200
- 'Smile (m)',
201
- 'Frown (m)',
202
- 'Buck Teeth (m)',
203
- 'Hot Lipstick (f)',
204
- 'Black Lipstick (f)',
205
- 'Purple Lipstick (f)',
206
- ]
207
-
208
-
209
- YEOLDEPUNK_MOUTH_PROP = [
210
- 'Cigarette (m/f)',
211
- 'Vape (m/f)',
212
- 'Pipe (m/f)',
213
- 'Medical Mask (m/f)',
214
- ]
215
-
216
- YEOLDEPUNK_NECK = [
217
- 'Silver Chain (m/f)',
218
- 'Gold Chain (m/f)',
219
- 'Choker (f)',
220
- ]
221
-
222
- YEOLDEPUNK_BLEMISH = [
223
- 'Mole (m/f)',
224
- 'Spots (m/f)',
225
- 'Rosy Cheeks (m/f)',
226
- ]
227
-
228
- YEOLDEPUNK_NOSE = [
229
- 'Clown Nose (m/f)',
230
- ]
231
-
232
-
233
-
234
-
235
- DOGE_ARCHETYPE = [
236
- 'Classic',
237
- 'Dark',
238
- 'Zombie',
239
- 'Alien',
240
- ]
241
-
242
- DOGE_HAIR = [
243
- 'Crazy Hair',
244
- ]
245
-
246
- DOGE_HEADWEAR = [
247
- 'Beanie',
248
- 'Cap',
249
- 'Cap Forward',
250
- 'Cowboy Hat',
251
- 'Fedora',
252
- 'Knitted Cap',
253
- 'Top Hat',
254
- 'Bandana',
255
- 'Headband',
256
- 'Tiara',
257
- ]
258
-
259
- DOGE_EYEWEAR = [
260
- '3D Glasses',
261
- 'Big Shades',
262
- 'Classic Shades',
263
- 'Regular Shades',
264
- 'Small Shades',
265
- 'Nerd Glasses',
266
- 'Eye Patch',
267
- ]
268
-
269
-
270
-
271
- MARC_ARCHETYPE = [
272
- 'Marc',
273
- 'Marc Mid',
274
- 'Marc Dark',
275
- 'Marc Albino',
276
- 'Marc Golden',
277
- 'Mad Lad',
278
- 'Zombie',
279
- 'Ape',
280
- 'Ape Golden',
281
- 'Ape Pink',
282
- 'Alien',
283
- 'Alien Green',
284
- 'Devil',
285
- 'Orc',
286
- 'Skeleton',
287
- 'Bot']
288
-
289
-
290
- MARC_EYES = [
291
- 'Blue Eyes',
292
- 'Green Eyes',
293
- ]
294
-
295
- MARC_FACE = [
296
- 'Blue Eye Shadow',
297
- 'Green Eye Shadow',
298
- 'Purple Eye Shadow',
299
- 'Clown Eyes Blue',
300
- 'Clown Eyes Green',
301
- 'Bagner',
302
- 'Marc Tyson',
303
- 'Tears',
304
- ]
305
-
306
- MARC_BEARD = [
307
- 'Big Beard White',
308
- 'Big Beard',
309
- 'Chinstrap',
310
- 'Front Beard',
311
- 'Front Beard Dark',
312
- 'Full Mustache',
313
- 'Full Mustache Dark',
314
- 'Goat',
315
- 'Goat Dark',
316
- 'Handlebar',
317
- 'Luxurious Beard',
318
- 'Mustache',
319
- 'Mutton Chop',
320
- 'Normal Beard',
321
- 'Normal Beard Black',
322
- 'Shadow Beard',
323
- 'Soul Patch',
324
- ]
325
-
326
- MARC_HAIR = [
327
- 'Blonde Bob',
328
- 'Chad',
329
- 'Clown Hair',
330
- 'Crazy White Hair',
331
- 'Crazy Hair',
332
- 'Frumpy Hair',
333
- 'Marc Three',
334
- 'Purple Hair',
335
- 'Stringy Hair',
336
- 'Vampire Hair',
337
- 'Wild Blonde Hair',
338
- 'Wild Hair',
339
- ]
340
-
341
- MARC_HEADWEAR = [
342
- 'Bandana',
343
- 'Beanie',
344
- 'Bunny Ears',
345
- 'Cap',
346
- 'Skull Cap',
347
- 'Cap Forward',
348
- 'Police Cap',
349
- 'Cowboy Hat',
350
- 'Do Rag',
351
- 'Fast Food',
352
- 'Marcdonalds',
353
- 'Fedora',
354
- 'Headband',
355
- 'Roaring Headband',
356
- 'Hoodie',
357
- 'Purple Hoodie',
358
- 'Knitted Cap',
359
- 'Laurels',
360
- 'Shemagh',
361
- 'Tassle Hat',
362
- 'Tiarra',
363
- 'Top Hat',
364
- 'Uncle Sam',
365
- 'Viking',
366
- 'Welding Goggles',
367
- ]
368
-
369
-
370
- MARC_EYEWEAR = [
371
- '3D Glasses',
372
- 'Aviators',
373
- 'Big Shades',
374
- 'Classic Shades',
375
- 'Deal With It',
376
- 'Glasses',
377
- 'Gold Glasses',
378
- 'Horned-Rim Glasses',
379
- 'Monocle',
380
- 'Nerd Glasses',
381
- 'Pink Shades',
382
- 'Polarized',
383
- 'Polarized White',
384
- 'Regular Shades',
385
- 'Small Shades',
386
- 'VR Headset',
387
- 'Eye Mask',
388
- 'Eye Patch',
389
- 'Lasers']
390
-
391
- MARC_MOUTH_PROP = [
392
- 'Cigar',
393
- 'Cigarette',
394
- 'Hookah',
395
- 'Pipe',
396
- 'Vape',
397
- 'Medical Mask',
398
- 'Bubble Gum' ]
109
+ erb <<TXT
399
110
 
111
+ <p style="font-size: 80%;">
112
+ <a href="/">« Profile Pic(ture) As A Service</a>
113
+ </p>
400
114
 
115
+ <h1>Spritesheets</h1>
401
116
 
402
117
 
403
- class ProfilepicService < Sinatra::Base
118
+ <% SPRITESHEETS.each do |type, files| %>
119
+ <p>
120
+ <b><a href="/<%= type %>_spritesheet"> <%= type %> Attribute Quick Reference</b></a>
404
121
 
405
- get '/cache' do
406
122
 
407
- html =<<HTML
408
- <pre>
409
- #{IMAGES.size} image(s) in cache:
123
+ <span style="font-size: 80%;">
124
+ (Source:
125
+ <% files.each_with_index do |name, i| %>
126
+ <%= i > 0 ? ' · ' : '' %>
127
+ <a href="/<%= name %>"><%= name %></a>
128
+ <% end %>
129
+ )
130
+ </span>
131
+ </p>
132
+ <% end %>
133
+ TXT
134
+ end
410
135
 
411
136
 
412
- </pre>
413
- HTML
414
- html
415
- end
416
137
 
138
+ FILES.each do |name, real_path|
417
139
 
140
+ basename = File.basename( name )
141
+ extname = File.extname( name )
142
+ pp [basename, extname]
418
143
 
419
- get '/' do
420
- erb :index
421
- end
144
+ puts "adding get route >/#{name}< serving >#{real_path}<"
422
145
 
423
- get '/marcs' do
424
- erb :marcs
146
+ get "/#{name}" do
147
+ puts " serving >#{name}<..."
148
+ if extname == '.csv'
149
+ ## headers( 'Content-Type' => 'text/plain; charset=utf-8' )
150
+ headers( 'Content-Type' => 'text/plain; charset=utf-8' )
151
+ headers( 'Content-Disposition' => 'inline' )
152
+ ## note - do NOT use content type for now
153
+ ## chrome will DOWNLOAD the file and not display inline
154
+ read_text( real_path )
155
+ else ## assume png - image/png
156
+ headers( 'Content-Type' => 'image/png' )
157
+ ## todo/fix. change to read_blob/bin/binary( real_path )
158
+ Pixelart::Image.read( real_path ).to_blob
159
+ end
425
160
  end
161
+ end
426
162
 
427
- get '/doge' do
428
- erb :doge
429
- end
430
163
 
431
- get '/yeoldepunks' do
432
- erb :yeoldepunks
164
+ SPRITESHEETS.each do |type, files|
165
+ get "/#{type}_spritesheet" do
166
+ erb render_spritesheet( type, files[0], files[1], zoom: 2 )
433
167
  end
168
+ end
434
169
 
435
170
 
436
171
 
437
-
172
+ ##
173
+ # generators
438
174
  get '/generate_marcs' do
439
-
440
175
  r = ImageReq.build_marc( params )
441
176
 
442
- img = r.image
177
+ IMAGES[ r.image_key ] = r.image_blob
178
+ redirect "/more?key=#{r.image_key}"
179
+ end
180
+
181
+ get '/generate_saudis' do
182
+ r = ImageReq.build_saudi( params )
443
183
 
444
- blob = img.image.to_blob
445
- IMAGES[ r.image_key ] = blob
446
- redirect "/#{r.image_key}.png"
447
- end
184
+ IMAGES[ r.image_key ] = r.image_blob
185
+ redirect "/more?key=#{r.image_key}"
186
+ end
448
187
 
449
188
  get '/generate_yeoldepunks' do
189
+ r = ImageReq.build_yeoldepunk( params )
450
190
 
451
- r = ImageReq.build_yeoldepunk( params )
191
+ IMAGES[ r.image_key ] = r.image_blob
192
+ redirect "/more?key=#{r.image_key}"
193
+ end
452
194
 
453
- img = r.image
195
+ get '/generate_doge' do
196
+ r = ImageReq.build_doge( params )
454
197
 
455
- blob = img.image.to_blob
456
- IMAGES[ r.image_key ] = blob
457
- redirect "/#{r.image_key}.png"
458
- end
198
+ IMAGES[ r.image_key ] = r.image_blob
199
+ redirect "/more?key=#{r.image_key}"
200
+ end
459
201
 
460
202
 
461
- get '/generate_doge' do
203
+ get '/generate' do
204
+ r = ImageReq.build( params )
205
+
206
+ IMAGES[ r.image_key ] = r.image_blob
207
+ # redirect "/#{r.image_key}.png"
208
+ redirect "/more?key=#{r.image_key}"
209
+ end
462
210
 
463
- r = ImageReq.build_doge( params )
464
211
 
465
- img = r.image
466
212
 
467
- blob = img.image.to_blob
468
- IMAGES[ r.image_key ] = blob
469
- redirect "/#{r.image_key}.png"
470
- end
471
213
 
214
+ get '/more' do
472
215
 
473
- get '/generate' do
216
+ zoom = (params[:z] || '1').strip.to_i( 10 )
474
217
 
475
- r = ImageReq.build( params )
218
+ key = params[:key]
219
+ ## split key in name and id e.g.
220
+ ## moonbird1660753325 => moonbird / 1660753325
221
+ name, id = if m = key.match( /^(?<name>[a-z_-]+)
222
+ (?<id>[0-9]+)$
223
+ /ix )
224
+ [m[:name],m[:id]]
225
+ else
226
+ ['?','?']
227
+ end
228
+
229
+ ## change/rename pic to src (or image_src or such - why? why not?)
230
+ pic = "#{key}"
231
+ pic += "@#{zoom}x" if [2,3,4,5,6,7,8,9,10,20].include?( zoom )
232
+ pic += ".png"
233
+
234
+ bg = (params[:bg] || 'none').strip.downcase
476
235
 
477
- img = r.image
236
+ pic += "?bg=#{bg}" if bg != 'none'
478
237
 
479
- blob = img.image.to_blob
480
- IMAGES[ r.image_key ] = blob
481
- redirect "/#{r.image_key}.png"
238
+
239
+ erb :more, locals: { pic: pic,
240
+ zoom: zoom,
241
+ bg: bg,
242
+ key: key,
243
+ name: name,
244
+ id: id }
482
245
  end
483
246
 
484
247
 
485
- get '/:key.png' do
248
+ get %r{/(?<key>[a-z0-9]+)
249
+ (@(?<zoom>[0-9]+)x)?
250
+ \.png}xi do
251
+
252
+ # get '/:key(@:zoom)?.png' do
486
253
  puts " .png image request for key: >#{params[:key]}<"
254
+ puts " with zoom: >#{params[:zoom]}< : #{params[:zoom].class.name}"
487
255
 
488
256
  blob = IMAGES[ params[:key] ]
489
257
 
258
+
490
259
  if blob
260
+ img = Pixelart::Image.blob( blob )
261
+
262
+ bg = (params[:bg] || 'none').strip.downcase
263
+ img = img.background( bg ) if bg != 'none'
264
+
265
+ zoom = (params[:zoom] || '1').strip.to_i( 10 )
266
+ img = img.zoom( zoom ) if [2,3,4,5,6,7,8,9,10,20].include?( zoom )
267
+
491
268
  headers( 'Content-Type' => "image/png" )
492
- blob
269
+ img.to_blob
493
270
  else
494
271
  "404 not found; sorry no generated .png image found for key >#{params[:key]}<"
495
272
  end
496
273
  end
497
- end
498
274
 
275
+ get '/cache' do
276
+
277
+ html =<<HTML
278
+ <pre>
279
+ #{IMAGES.size} image(s) in cache:
499
280
 
281
+
282
+ </pre>
283
+ HTML
284
+ html
285
+ end
286
+ end # class ProfilepicService
@@ -3,7 +3,7 @@
3
3
  module Profilepic
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- PATCH = 4
6
+ PATCH = 5
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version