pixelart-characters 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/Manifest.txt +6 -0
- data/README.md +337 -0
- data/Rakefile +29 -0
- data/lib/pixelart/characters.rb +174 -0
- data/lib/pixelart/characters/version.rb +26 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 199f065720b94d8445a3138c4ed903b2576f05d42f1c0f48c30a4286d64c4945
|
4
|
+
data.tar.gz: 50c3e59a290b5dad5469ba1b5d3322e42d747a41f0e7d24687dd8a62b21cd590
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e3cc3fafb8fb08ac5a7ffacd012b4cbbcfd100ea1337efc93bfabae2be05d890da389247b4450319e9f37d9444245aa251edcedf3ca201bbaf0b577e5d286260
|
7
|
+
data.tar.gz: bbd2f9090c70a79d15a41ceb91274ec480f04798e87a0925e6ee23c52f27d82633d66374b8bc1f8472ae433d7fca8e2eb48cebd9fce7b8ab60cf7474eb4262bb
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,337 @@
|
|
1
|
+
# Pixel Art Characters
|
2
|
+
|
3
|
+
pixelart-characters - generate pixel arts characters, algorithmically - paint by numbers - what about a new punk avatar portrait series? - yes, you can!
|
4
|
+
|
5
|
+
|
6
|
+
* home :: [github.com/rubycoco/pixel](https://github.com/rubycoco/pixel)
|
7
|
+
* bugs :: [github.com/rubycoco/pixel/issues](https://github.com/rubycoco/pixel/issues)
|
8
|
+
* gem :: [rubygems.org/gems/pixelart-characters](https://rubygems.org/gems/pixelart-characters)
|
9
|
+
* rdoc :: [rubydoc.info/gems/pixelart-characters](http://rubydoc.info/gems/pixelart-characters)
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
### Example №1 - 8-Bit Icon Series (20x20)
|
17
|
+
|
18
|
+
Let's try the 8-bit icon series with a 20x20 pixel art image set,
|
19
|
+
see [/design.punks/8bit](https://github.com/cryptopunksnotdead/design.punks#8-bit-icon-series-20x20).
|
20
|
+
|
21
|
+
|
22
|
+
Step 1: Map the pixel art images
|
23
|
+
(or designs in ascii text)
|
24
|
+
to a characters generation definition.
|
25
|
+
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
require 'pixelart/characters'
|
29
|
+
|
30
|
+
######
|
31
|
+
# skin tone colors
|
32
|
+
HUMAN_LIGHTER_BASE1 = '0xead9d9' # rgb(234 217 217) - hsl( 0° 29% 88%)
|
33
|
+
HUMAN_LIGHT_BASE1 = '0xdbb180' # rgb(219 177 128) - hsl( 32° 56% 68%)
|
34
|
+
HUMAN_DARK_BASE1 = '0xae8b61' # rgb(174 139 97) - hsl( 33° 32% 53%)
|
35
|
+
HUMAN_DARKER_BASE1 = '0x713f1d' # rgb(113 63 29) - hsl( 24° 59% 28%)
|
36
|
+
|
37
|
+
|
38
|
+
###############
|
39
|
+
# characters generation definition
|
40
|
+
CHARACTERS = {
|
41
|
+
'male' => {
|
42
|
+
'face' => [{ design: 'male', colors: [HUMAN_LIGHTER_BASE1] },
|
43
|
+
{ design: 'male', colors: [HUMAN_LIGHT_BASE1] },
|
44
|
+
{ design: 'male', colors: [HUMAN_DARK_BASE1] },
|
45
|
+
{ design: 'male', colors: [HUMAN_DARKER_BASE1] }],
|
46
|
+
'clothes' => (1..65).to_a,
|
47
|
+
'eye' => (1..32).to_a,
|
48
|
+
'mouth' => (1..26).to_a,
|
49
|
+
'hair' => (1..36).to_a,
|
50
|
+
},
|
51
|
+
'female' => {
|
52
|
+
'face' => [{ design: 'female', colors: [HUMAN_LIGHTER_BASE1] },
|
53
|
+
{ design: 'female', colors: [HUMAN_LIGHT_BASE1] },
|
54
|
+
{ design: 'female', colors: [HUMAN_DARK_BASE1] },
|
55
|
+
{ design: 'female', colors: [HUMAN_DARKER_BASE1] }],
|
56
|
+
'clothes' => (1..59).to_a,
|
57
|
+
'eye' => (1..53).to_a,
|
58
|
+
'mouth' => (1..17).to_a,
|
59
|
+
'hair' => (1..33).to_a,
|
60
|
+
}
|
61
|
+
}
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
Note: List all parts (such as face, clothes, eyes, etc.) that make up
|
66
|
+
a character in the paste order, that is,
|
67
|
+
the algo will generate an empty pixel image / canvas and than adds - or is that composes or pastes - one part after the other on top all the way from face to hair.
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
Let's set up the character generator (or is that artist?) by
|
72
|
+
passing in the characters definition and
|
73
|
+
the root directory housing all the the pixel art:
|
74
|
+
|
75
|
+
``` ruby
|
76
|
+
artist = Character.new( CHARACTERS,
|
77
|
+
dir: "./8bit" )
|
78
|
+
```
|
79
|
+
|
80
|
+
|
81
|
+
Let's generate some characters.
|
82
|
+
Let's start with a male using the part selection (1,1,1,1,1) - that is,
|
83
|
+
face #1, clothes #1, eye #1, mouth #1, and hair #1 -
|
84
|
+
and so on:
|
85
|
+
|
86
|
+
|
87
|
+
``` ruby
|
88
|
+
img = artist.generate( 'male', 1, 1, 1, 1, 1 )
|
89
|
+
img.save( '8bit-male1.png')
|
90
|
+
img.zoom(4).save( '8bit-male1x4.png')
|
91
|
+
|
92
|
+
img = artist.generate( 'male', 2, 2, 2, 2, 2 )
|
93
|
+
img.save( '8bit-male2.png')
|
94
|
+
img.zoom(4).save( '8bit-male2x4.png')
|
95
|
+
|
96
|
+
img = artist.generate( 'male', 3, 3, 3, 3, 3 )
|
97
|
+
img.save( '8bit-male3.png')
|
98
|
+
img.zoom(4).save( '8bit-male3x4.png')
|
99
|
+
|
100
|
+
img = artist.generate( 'male', 4, 4, 4, 4, 4 )
|
101
|
+
img.save( '8bit-male4.png')
|
102
|
+
img.zoom(4).save( '8bit-male4x4.png')
|
103
|
+
|
104
|
+
|
105
|
+
img = artist.generate( 'female', 1, 1, 1, 1, 1 )
|
106
|
+
img.save( '8bit-female1.png')
|
107
|
+
img.zoom(4).save( '8bit-female1x4.png')
|
108
|
+
|
109
|
+
img = artist.generate( 'female', 2, 2, 2, 2, 2 )
|
110
|
+
img.save( '8bit-female2.png')
|
111
|
+
img.zoom(4).save( '8bit-female2x4.png')
|
112
|
+
|
113
|
+
img = artist.generate( 'female', 3, 3, 3, 3, 3 )
|
114
|
+
img.save( '8bit-female3.png')
|
115
|
+
img.zoom(4).save( '8bit-female3x4.png')
|
116
|
+
|
117
|
+
img = artist.generate( 'female', 4, 4, 4, 4, 4 )
|
118
|
+
img.save( '8bit-female4.png')
|
119
|
+
img.zoom(4).save( '8bit-female4x4.png')
|
120
|
+
```
|
121
|
+
|
122
|
+
Voila!
|
123
|
+
|
124
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male1.png)
|
125
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male2.png)
|
126
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male3.png)
|
127
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male4.png)
|
128
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female1.png)
|
129
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female2.png)
|
130
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female3.png)
|
131
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female4.png)
|
132
|
+
|
133
|
+
4x
|
134
|
+
|
135
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male1x4.png)
|
136
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male2x4.png)
|
137
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male3x4.png)
|
138
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male4x4.png)
|
139
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female1x4.png)
|
140
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female2x4.png)
|
141
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female3x4.png)
|
142
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female4x4.png)
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
Let the artist do the painting, that is, let the computer fill in the numbers
|
147
|
+
with a random lottery.
|
148
|
+
|
149
|
+
``` ruby
|
150
|
+
img = artist.random( 'male' )
|
151
|
+
img.save( '8bit-male-random1.png')
|
152
|
+
img.zoom(4).save( '8bit-male-random1x4.png')
|
153
|
+
|
154
|
+
img = artist.random( 'female' )
|
155
|
+
img.save( '8bit-female-random1.png')
|
156
|
+
img.zoom(4).save( '8bit-female-random1x4.png')
|
157
|
+
|
158
|
+
img = artist.random
|
159
|
+
img.save( '8bit-random1.png')
|
160
|
+
img.zoom(4).save( '8bit-random1x4.png')
|
161
|
+
|
162
|
+
img = artist.random
|
163
|
+
img.save( '8bit-random2.png')
|
164
|
+
img.zoom(4).save( '8bit-random2x4.png')
|
165
|
+
```
|
166
|
+
|
167
|
+
Voila!
|
168
|
+
|
169
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male-random1.png)
|
170
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female-random1.png)
|
171
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-random1.png)
|
172
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-random2.png)
|
173
|
+
|
174
|
+
4x
|
175
|
+
|
176
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-male-random1x4.png)
|
177
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-female-random1x4.png)
|
178
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-random1x4.png)
|
179
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/8bit-random2x4.png)
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
### Example №2 - "SubstraPunks" - CryptoPunks, Polkadot Edition (32x32)
|
185
|
+
|
186
|
+
|
187
|
+
Let's try the "SubstraPunks", that is, the CryptoPunks, Polkadot Edition series with a 32x32 pixel art image set,
|
188
|
+
see [Inside the Punk Art Machinery - How To Generate 10 000 Punks (and Punkettes), Algorithmically - Paint by Numbers](https://github.com/cryptopunksnotdead/programming-cryptopunks/blob/master/03_generate.md) in the Programming CryptoPunks & Copypastas book(let).
|
189
|
+
|
190
|
+
|
191
|
+
Step 1: Map the pixel art images
|
192
|
+
(or designs in ascii text)
|
193
|
+
to a characters generation definition.
|
194
|
+
|
195
|
+
|
196
|
+
``` ruby
|
197
|
+
PARTS = {
|
198
|
+
face: { required: true,
|
199
|
+
attributes: [['', 'u'],
|
200
|
+
['', 'u']] },
|
201
|
+
mouth: { required: true,
|
202
|
+
attributes: [['Black Lipstick', 'f'],
|
203
|
+
['Red Lipstick', 'f'],
|
204
|
+
['Smile', 'u'],
|
205
|
+
['', 'u'],
|
206
|
+
['Teeth Smile', 'm'],
|
207
|
+
['Purple Lipstick', 'f']] },
|
208
|
+
nose: { required: true,
|
209
|
+
attributes: [['', 'u'],
|
210
|
+
['Nose Ring', 'u']] },
|
211
|
+
eyes: { required: true,
|
212
|
+
attributes: [['', 'u'],
|
213
|
+
['Asian Eyes', 'u'],
|
214
|
+
['Sun Glasses', 'u'],
|
215
|
+
['Red Glasses', 'u'],
|
216
|
+
['Round Eyes', 'u']] },
|
217
|
+
ears: { required: true,
|
218
|
+
attributes: [['', 'u'],
|
219
|
+
['Left Earring', 'u'],
|
220
|
+
['Right Earring', 'u'],
|
221
|
+
['Two Earrings', 'u']] },
|
222
|
+
beard: { required: false,
|
223
|
+
attributes: [['Brown Beard', 'm'],
|
224
|
+
['Mustache-Beard', 'm'],
|
225
|
+
['Mustache', 'm'],
|
226
|
+
['Regular Beard', 'm']] },
|
227
|
+
hair: { required: false,
|
228
|
+
attributes: [['Up Hair', 'm'],
|
229
|
+
['Down Hair', 'u'],
|
230
|
+
['Mahawk', 'u'],
|
231
|
+
['Red Mahawk', 'u'],
|
232
|
+
['Orange Hair', 'u'],
|
233
|
+
['Bubble Hair', 'm'],
|
234
|
+
['Emo Hair', 'u'],
|
235
|
+
['Thin Hair', 'm'],
|
236
|
+
['Bald', 'm'],
|
237
|
+
['Blonde Hair', 'f']] }
|
238
|
+
}
|
239
|
+
```
|
240
|
+
|
241
|
+
|
242
|
+
Note: This characters generation definition uses the "compact" format
|
243
|
+
with "auto-implied" male and female types and
|
244
|
+
using the m/f/u type flags -
|
245
|
+
standing in for male, female and both (that is, unisex).
|
246
|
+
|
247
|
+
|
248
|
+
Let's set up the character generator (or is that artist?) by
|
249
|
+
passing in the characters definition and
|
250
|
+
the root directory housing all the the pixel art:
|
251
|
+
|
252
|
+
``` ruby
|
253
|
+
artist = Character.new( PARTS, dir: "./parts",
|
254
|
+
format: 'compact' )
|
255
|
+
```
|
256
|
+
|
257
|
+
|
258
|
+
Let's generate some characters.
|
259
|
+
|
260
|
+
``` ruby
|
261
|
+
img = artist.generate( 'female', 2, 2, 2, 3, 1, 6 )
|
262
|
+
img.save( 'polka-female.png')
|
263
|
+
img.zoom(3).save( 'polka-femalex3.png')
|
264
|
+
|
265
|
+
img = artist.generate( 'male', 1, 3, 2, 3, 1, 1, 5 )
|
266
|
+
img.save( 'polka-male.png')
|
267
|
+
img.zoom(3).save( 'polka-malex3.png')
|
268
|
+
```
|
269
|
+
|
270
|
+
Voila!
|
271
|
+
|
272
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-female.png)
|
273
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-male.png)
|
274
|
+
|
275
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-femalex3.png)
|
276
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-malex3.png)
|
277
|
+
|
278
|
+
|
279
|
+
And with a random lottery.
|
280
|
+
|
281
|
+
``` ruby
|
282
|
+
img = artist.random( 'female' )
|
283
|
+
img.save( 'polka-female-random.png')
|
284
|
+
img.zoom(3).save( 'polka-female-randomx3.png')
|
285
|
+
|
286
|
+
img = artist.random( 'male' )
|
287
|
+
img.save( 'polka-male-random.png')
|
288
|
+
img.zoom(3).save( 'polka-male-randomx3.png')
|
289
|
+
|
290
|
+
img = artist.random
|
291
|
+
img.save( 'polka-random1.png')
|
292
|
+
img.zoom(3).save( 'polka-random1x3.png')
|
293
|
+
|
294
|
+
img = artist.random
|
295
|
+
img.save( 'polka-random2.png')
|
296
|
+
img.zoom(3).save( 'polka-random2x3.png')
|
297
|
+
|
298
|
+
img = artist.random
|
299
|
+
img.save( 'polka-random3.png')
|
300
|
+
img.zoom(3).save( 'polka-random3x3.png')
|
301
|
+
```
|
302
|
+
|
303
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-female-random.png)
|
304
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-male-random.png)
|
305
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random1.png)
|
306
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random2.png)
|
307
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random3.png)
|
308
|
+
|
309
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-female-randomx3.png)
|
310
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-male-randomx3.png)
|
311
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random1x3.png)
|
312
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random2x3.png)
|
313
|
+
![](https://github.com/rubycoco/pixel/raw/master/pixelart-characters/i/polka-random3x3.png)
|
314
|
+
|
315
|
+
|
316
|
+
That's it.
|
317
|
+
|
318
|
+
|
319
|
+
|
320
|
+
## Install
|
321
|
+
|
322
|
+
Just install the gem:
|
323
|
+
|
324
|
+
$ gem install pixelart-characters
|
325
|
+
|
326
|
+
|
327
|
+
## License
|
328
|
+
|
329
|
+
The scripts are dedicated to the public domain.
|
330
|
+
Use it as you please with no restrictions whatsoever.
|
331
|
+
|
332
|
+
|
333
|
+
|
334
|
+
|
335
|
+
## Questions? Comments?
|
336
|
+
|
337
|
+
Post them on the [CryptoPunksDev reddit](https://old.reddit.com/r/CryptoPunksDev). Thanks.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/pixelart/characters/version.rb'
|
3
|
+
|
4
|
+
Hoe.spec 'pixelart-characters' do
|
5
|
+
|
6
|
+
self.version = Pixelart::Module::Characters::VERSION
|
7
|
+
|
8
|
+
self.summary = "pixelart-characters - generate pixel arts characters, algorithmically - paint by numbers - what about a new punk avatar portrait series? - yes, you can!"
|
9
|
+
self.description = summary
|
10
|
+
|
11
|
+
self.urls = { home: 'https://github.com/rubycoco/pixel' }
|
12
|
+
|
13
|
+
self.author = 'Gerald Bauer'
|
14
|
+
self.email = 'wwwmake@googlegroups.com'
|
15
|
+
|
16
|
+
# switch extension to .markdown for gihub formatting
|
17
|
+
self.readme_file = 'README.md'
|
18
|
+
self.history_file = 'CHANGELOG.md'
|
19
|
+
|
20
|
+
self.extra_deps = [
|
21
|
+
['pixelart'],
|
22
|
+
]
|
23
|
+
|
24
|
+
self.licenses = ['Public Domain']
|
25
|
+
|
26
|
+
self.spec_extras = {
|
27
|
+
required_ruby_version: '>= 2.3'
|
28
|
+
}
|
29
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'pixelart/base'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
## our own code
|
6
|
+
require 'pixelart/characters/version' # note: let version always go first
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
module Pixelart
|
11
|
+
class Character ## todo/check: rename to Generator or Artist or such? why? why not?
|
12
|
+
|
13
|
+
|
14
|
+
def self.build( chars, format=nil )
|
15
|
+
## try to unify characters format
|
16
|
+
## format 1 - "compact" style
|
17
|
+
## check for attributes with "type flags" e.g.
|
18
|
+
## face: { required: true,
|
19
|
+
## attributes: [['', 'u'],
|
20
|
+
## ['', 'u']] },
|
21
|
+
|
22
|
+
if format && [:compact].include?( format.downcase.to_sym )
|
23
|
+
## note: assumes male/female types
|
24
|
+
chars_by_type = { 'male' => {},
|
25
|
+
'female' => {},
|
26
|
+
}
|
27
|
+
m = chars_by_type['male'] ## access shortcuts
|
28
|
+
f = chars_by_type['female']
|
29
|
+
|
30
|
+
## mouth: { attributes: [['Black Lipstick', 'f'],
|
31
|
+
## ['Red Lipstick', 'f'],
|
32
|
+
## ['Smile', 'u'],
|
33
|
+
## ['', 'u'],
|
34
|
+
## ['Teeth Smile', 'm'],
|
35
|
+
## ['Purple Lipstick', 'f']]}
|
36
|
+
chars.each do |part_key, part|
|
37
|
+
## always convert keys to string for now - why? why not?
|
38
|
+
part_key = part_key.to_s
|
39
|
+
|
40
|
+
## todo/fix: add/pass along required: true/false too - skiped for now
|
41
|
+
## add at the end? if some attributes present? why? why not?
|
42
|
+
part[:attributes].each_with_index do |rec,i|
|
43
|
+
attr_name = rec[0]
|
44
|
+
type_flag = rec[1] ## char type flag e.g. m/f/u (male/female/unisex)
|
45
|
+
|
46
|
+
attribute = {
|
47
|
+
## todo/check: rename path to id - why? why not? (for now its basename w/o extension etc.)
|
48
|
+
path: "#{part_key}/#{part_key}#{i+1}" ## map to filename (note: w/o extension for now)
|
49
|
+
}
|
50
|
+
attribute[:name] = attr_name if attr_name && attr_name.size > 0
|
51
|
+
|
52
|
+
## note: only add part "on demand", that is, if no attributes for m/f than no part too
|
53
|
+
if type_flag == 'm' || type_flag == 'u'
|
54
|
+
m_attributes = ( m[part_key] ||= { attributes: [] } )[:attributes]
|
55
|
+
m_attributes << attribute
|
56
|
+
end
|
57
|
+
|
58
|
+
if type_flag == 'f' || type_flag == 'u'
|
59
|
+
f_attributes = ( f[part_key] ||= { attributes: [] } )[:attributes]
|
60
|
+
f_attributes << attribute
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "(normalized) characters spec:"
|
66
|
+
pp chars_by_type
|
67
|
+
|
68
|
+
chars_by_type
|
69
|
+
else ## pass-through for now
|
70
|
+
chars
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
def initialize( chars, dir: '.',
|
77
|
+
format: nil )
|
78
|
+
## todo/check: (re)name format param to style or ? - find a better name - why? why not?
|
79
|
+
|
80
|
+
@chars = self.class.build( chars, format ) ## build ("unify/normalize") characters spec
|
81
|
+
|
82
|
+
@rootdir = dir
|
83
|
+
end
|
84
|
+
|
85
|
+
def types() @chars.keys; end
|
86
|
+
|
87
|
+
|
88
|
+
def random( type=nil )
|
89
|
+
type = types[ rand( types.size ) ] if type.nil?
|
90
|
+
|
91
|
+
character = @chars[ type ]
|
92
|
+
|
93
|
+
nums = []
|
94
|
+
character.each do |_, part|
|
95
|
+
attributes_size = if part.is_a?( Hash )
|
96
|
+
part[:attributes].size
|
97
|
+
else ## assume array - "inline" compact attribute format
|
98
|
+
part.size
|
99
|
+
end
|
100
|
+
|
101
|
+
## todo/fix: check for part optional (true/false) ?
|
102
|
+
## if optional possible (start rand at 0 NOT 1) - why? why not?
|
103
|
+
nums << rand( 1..attributes_size )
|
104
|
+
end
|
105
|
+
generate( type, *nums )
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
## todo/fix: add alternate support for **kwargs
|
110
|
+
## instead of positional number list - why? why not?
|
111
|
+
def generate( type, *args )
|
112
|
+
puts "==> generate >#{type}< - with #{args.size} part(s) #{args.inspect}"
|
113
|
+
|
114
|
+
character = @chars[ type ]
|
115
|
+
|
116
|
+
img = nil ## (by default) auto-derive width x height dimension from first image
|
117
|
+
|
118
|
+
character.each_with_index do |(part_key, part),i|
|
119
|
+
num = args[i]
|
120
|
+
|
121
|
+
## todo/check - double check if part required set to false - why? why not?
|
122
|
+
next if num == 0 ## note: skip part if index is 0!!!
|
123
|
+
|
124
|
+
attributes = if part.is_a?( Hash )
|
125
|
+
part[:attributes]
|
126
|
+
else ## assume array - "inline" compact attribute format
|
127
|
+
part
|
128
|
+
end
|
129
|
+
|
130
|
+
attribute = attributes[ num-1 ] ## note: num starts counting at 1 (sub 1 for zero-based array index)
|
131
|
+
raise ArgumentError, "no >#{part_key}< part found for index #{num}; sorry" if attribute.nil?
|
132
|
+
|
133
|
+
print "#{i} #{part_key} => #{num}"
|
134
|
+
print " - #{attribute[:name]}" if attribute.is_a?( Hash ) && attribute.has_key?( :name )
|
135
|
+
print "\n"
|
136
|
+
|
137
|
+
|
138
|
+
img_part = if attribute.is_a?( Integer ) ## assume filename with num (as .png)
|
139
|
+
path = "#{@rootdir}/#{type}/#{part_key}/#{part_key}#{num}.png"
|
140
|
+
img_part = Image.read( path )
|
141
|
+
elsif attribute.is_a?( Hash )
|
142
|
+
if attribute.has_key?( :design )
|
143
|
+
path = "#{@rootdir}/#{attribute[:design]}.txt"
|
144
|
+
txt = File.open( path, 'r:utf-8') {|f| f.read }
|
145
|
+
img_part = Image.parse( txt, colors: attribute[:colors] )
|
146
|
+
else ## assume for now has key :path
|
147
|
+
path = "#{@rootdir}/#{attribute[:path]}.png"
|
148
|
+
img_part = Image.read( path )
|
149
|
+
end
|
150
|
+
else
|
151
|
+
puts "!! ERROR:"
|
152
|
+
pp part
|
153
|
+
raise ArgumentError, "unknown part data type; expected Integer|Hash"
|
154
|
+
end
|
155
|
+
## note: if (very) first part - auto-create empty image with size/dimensions from part
|
156
|
+
img = Image.new( img_part.width, img_part.height ) if i==0
|
157
|
+
img.compose!( img_part )
|
158
|
+
end
|
159
|
+
|
160
|
+
img
|
161
|
+
end
|
162
|
+
|
163
|
+
end # class Character
|
164
|
+
end # module Pixelart
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
###
|
169
|
+
# note: for convenience auto include Pixelart namespace!!! - why? why not?
|
170
|
+
include Pixelart
|
171
|
+
|
172
|
+
|
173
|
+
puts Pixelart::Module::Characters.banner # say hello
|
174
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module Pixelart
|
3
|
+
module Module
|
4
|
+
module Characters
|
5
|
+
|
6
|
+
MAJOR = 0
|
7
|
+
MINOR = 1
|
8
|
+
PATCH = 0
|
9
|
+
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
10
|
+
|
11
|
+
def self.version
|
12
|
+
VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.banner
|
16
|
+
"pixelart-characters/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.root
|
20
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) )
|
21
|
+
end
|
22
|
+
|
23
|
+
end # module Characters
|
24
|
+
end # module Module
|
25
|
+
end # module Pixelart
|
26
|
+
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pixelart-characters
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gerald Bauer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-05-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pixelart
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '7'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '4.0'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: hoe
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.22'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.22'
|
61
|
+
description: pixelart-characters - generate pixel arts characters, algorithmically
|
62
|
+
- paint by numbers - what about a new punk avatar portrait series? - yes, you can!
|
63
|
+
email: wwwmake@googlegroups.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files:
|
67
|
+
- CHANGELOG.md
|
68
|
+
- Manifest.txt
|
69
|
+
- README.md
|
70
|
+
files:
|
71
|
+
- CHANGELOG.md
|
72
|
+
- Manifest.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- lib/pixelart/characters.rb
|
76
|
+
- lib/pixelart/characters/version.rb
|
77
|
+
homepage: https://github.com/rubycoco/pixel
|
78
|
+
licenses:
|
79
|
+
- Public Domain
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options:
|
83
|
+
- "--main"
|
84
|
+
- README.md
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '2.3'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubygems_version: 3.1.4
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: pixelart-characters - generate pixel arts characters, algorithmically - paint
|
102
|
+
by numbers - what about a new punk avatar portrait series? - yes, you can!
|
103
|
+
test_files: []
|