Browse Source

Initial commit

Johann Schmitz 7 months ago
parent
commit
c223002836
Signed by: Johann Schmitz <johann@j-schmitz.net> GPG Key ID: A084064277C501ED
3 changed files with 295 additions and 0 deletions
  1. 227
    0
      pydenticon5/__init__.py
  2. 1
    0
      requirements.txt
  3. 67
    0
      test.py

+ 227
- 0
pydenticon5/__init__.py View File

@@ -0,0 +1,227 @@
1
+# -*- coding: utf-8 -*-
2
+import math
3
+
4
+from PIL import Image, ImageDraw
5
+
6
+
7
+class Pydenticon5(object):
8
+
9
+    def _get_sprite(self, shape, size):
10
+
11
+        if shape == 0:  # triangle
12
+            coords = [ (0.5, 1), (1, 0), (1, 1) ]
13
+        elif shape == 1:  # parallelogram
14
+            coords = [ (0.5, 0), (1, 0), (0.5, 1), (0, 1) ]
15
+        elif shape == 2:  # mouse ears
16
+            coords = [ (0.5, 0), (1, 0), (1, 1), (0.5, 1), (1, 0.5) ]
17
+        elif shape == 3: # ribbon
18
+            coords = [ (0, 0.5), (0.5, 0), (1, 0.5), (0.5, 1), (0.5, 0.5)]
19
+        elif shape == 4: # sails
20
+            coords = [ (0, 0.5), (1, 0), (1, 1), (0, 1), (1, 0.5)]
21
+        elif shape == 5: # fins
22
+            coords = [ (1, 0), (1, 1), (0.5, 1), (1, 0.5), (0.5, 0.5)]
23
+        elif shape == 6: # beak
24
+            coords = [ (0, 0), (1, 0), (1, 0.5), (0, 0), (0.5, 1), (0, 1)]
25
+        elif shape == 7: # chevron
26
+            coords = [ (0, 0), (0.5, 0), (1, 0.5), (0.5, 1), (0, 1), (0.5, 0.5)]
27
+        elif shape == 8: # fish
28
+            coords = [ (0.5, 0), (0.5, 0.5), (1, 0.5), (1, 1), (0.5, 1), (0.5, 0.5), (0, 0.5)]
29
+        elif shape == 9: # kite
30
+            coords = [ (0, 0), (1, 0), (0.5, 0.5), (1, 0.5), (0.5, 1), (0.5, 0.5), (0, 1)]
31
+        elif shape == 10: # trough
32
+            coords = [ (0, 0.5), (0.5, 1), (1, 0.5), (0.5, 0), (1, 0), (1, 1), (0, 1)]
33
+        elif shape == 11: # rays
34
+            coords = [ (0.5, 0), (1, 0), (1, 1), (0.5, 1), (1, 0.75), (0.5, 0.5), (1, 0.25)]
35
+        elif shape == 12: # double rhombus
36
+            coords = [ (0, 0.5), (0.5, 0), (0.5, 0.5), (1, 0), (1, 0.5), (0.5, 1), (0.5, 0.5), (0, 1)]
37
+        elif shape == 13: # crown
38
+            coords = [ (0, 0), (1, 0), (1, 1), (0, 1), (1, 0.5), (0.5, 0.25), (0.5, 0.75), (0, 0.59), (0.5, 0.25)]
39
+        elif shape == 14: # radioactive
40
+            coords = [ (0, 0.5), (0.5, 0.5), (0.5, 0), (1, 0), (0.5, 0.5), (1, 0.5), (0.5, 1), (0.5, 0.5), (0, 1)]
41
+        else: # tiles
42
+            coords = [ (0, 0), (1, 0), (0.5, 0.5), (0.5, 0), (0, 0.5), (1, 0.5), (0.5, 1), (0.5, 0.5), (0, 1)]
43
+
44
+        # scale up
45
+
46
+        for i in range(len(coords)):
47
+            coords[i] = (coords[i][0] * size, coords[i][1] * size)
48
+
49
+        return coords
50
+
51
+    def draw_rotated_polygon(self, orig, box_size, sprite, x, y, shapeangle, angle, size, fillstyle):
52
+
53
+        print("Draw rotated polygon: sprite=%s, x=%s, y=%s, shapeangle=%s, angle=%s, size=%s, fillstyle=%s" % (sprite, x, y, shapeangle, angle, size, fillstyle))
54
+
55
+        poly_img = Image.new('RGBA', (box_size, box_size))
56
+
57
+        # ImageDraw.Draw(poly_img).polygon([
58
+        #     (0, size),
59
+        #     (size/2, 0),
60
+        #     (size, size)
61
+        # ], fill=(255,255,255))
62
+        
63
+        ImageDraw.Draw(poly_img).polygon(sprite, fill=fillstyle)
64
+        
65
+        pillow_angle = (angle - 180) * -1
66
+        poly_img = poly_img.rotate(pillow_angle)
67
+        orig.paste(poly_img, (x, y))
68
+        
69
+        #
70
+        # #img.rotate(angle * math.pi / 180, center=(x, y))
71
+        # if angle == 90:
72
+        #     img = orig.rotate(angle * -1, center=(x, y)) # Pillow rotate is counter clockwise!
73
+        #     img.show()
74
+        # else:
75
+        #     img = orig
76
+        #
77
+        # draw = ImageDraw.Draw(img)
78
+        # draw.rectangle(
79
+        #     [x, y, x+size, y+size], outline=(0,0,0)
80
+        # )
81
+        #
82
+        # #center = (x, y)
83
+        # #img.rotate(angle * math.pi / 180, center=center)
84
+        # #img.rotate(shapeangle, center=(size/2, size/2))
85
+        #
86
+        # # move sprite to the correct box
87
+        # translated_sprite = [
88
+        #     (s[0] + x, s[1] + y) for s in sprite
89
+        # ]
90
+        # draw.polygon(translated_sprite, fill=fillstyle)
91
+        #
92
+        #
93
+        #
94
+        #
95
+        #
96
+        # # # var halfSize = size / 2;
97
+        # # half_size = size / 2
98
+        # #
99
+        # # # # ctx.translate(x, y);
100
+        # # # # ctx.rotate(angle * Math.PI / 180);
101
+        # # center = (x, y)
102
+        # # img.rotate(angle * math.pi / 180, center=center)
103
+        # # #
104
+        # # # # ctx.translate(halfSize, halfSize);
105
+        # # # # ctx.rotate(shapeangle);
106
+        # # center = (half_size, half_size)
107
+        # # img.rotate(shapeangle, center=center)
108
+        # # #
109
+        # # # # var tmpSprite = [];
110
+        # # # # for (var p = 0; p < sprite.length; p++) {
111
+        # # # #     tmpSprite[p] = sprite[p] - halfSize;
112
+        # # # # }
113
+        # # # # fillPoly(ctx, tmpSprite);
114
+        # # tmpsprite = [(coords[0] + center[0] - half_size, coords[1] + center[1] - half_size) for coords in sprite]
115
+        # # draw.polygon(tmpsprite, fillstyle)
116
+        #
117
+        # # debug
118
+        #
119
+        # # img.rotate(shapeangle * -1, center=center)
120
+        # # img.rotate((angle * math.pi / 180) * -1, center=(x, y))
121
+        return orig
122
+
123
+
124
+    def get_center(self, shape, size):
125
+
126
+        if shape == 0:  # empty
127
+            center = []
128
+        elif shape == 1:  # fill
129
+            center = [0, 0, 1, 0, 1, 1, 0, 1]
130
+        elif shape == 2:  # diamond
131
+            center = [ 0.5, 0, 1, 0.5, 0.5, 1, 0, 0.5]
132
+
133
+        elif shape == 3:  # reverse diamond
134
+            center = [0, 0, 1, 0, 1, 1, 0, 1, 0, 0.5, 0.5, 1, 1, 0.5, 0.5, 0, 0, 0.5]
135
+
136
+        elif shape == 4:  # cross
137
+            center = [ 0.25, 0, 0.75, 0, 0.5, 0.5, 1, 0.25, 1, 0.75, 0.5, 0.5, 0.75, 1, 0.25, 1, 0.5, 0.5, 0, 0.75, 0, 0.25, 0.5, 0.5]
138
+
139
+        elif shape == 5:  # morning star
140
+            center = [ 0, 0, 0.5, 0.25, 1, 0, 0.75, 0.5, 1, 1, 0.5, 0.75, 0, 1, 0.25, 0.5]
141
+
142
+        elif shape == 6:  # small square
143
+            center = [ 0.33, 0.33, 0.67, 0.33, 0.67, 0.67, 0.33, 0.67]
144
+
145
+        elif shape == 7:  # checkerboard
146
+            center = [ 0, 0, 0.33, 0, 0.33, 0.33, 0.66, 0.33, 0.67, 0, 1, 0, 1, 0.33, 0.67, 0.33, 0.67, 0.67,
147
+                    1, 0.67, 1, 1, 0.67, 1, 0.67, 0.67, 0.33, 0.67, 0.33, 1, 0, 1, 0, 0.67, 0.33, 0.67,
148
+                    0.33, 0.33, 0, 0.33]
149
+        else: # tiles
150
+            center = [ 0, 0, 1, 0, 0.5, 0.5, 0.5, 0, 0, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1]
151
+
152
+        # apply ratios
153
+        for i in range(len(center)):
154
+            center[i] = center[i] * size
155
+
156
+        return center
157
+
158
+    def draw(self, hash, size, rotate):
159
+
160
+        img = Image.new('RGBA', (size, size))
161
+        #img = ImageDraw.Draw(i)
162
+
163
+        corner_sprite_shape = int(hash[0], 16)
164
+        side_sprite_shape = int(hash[1], 16)
165
+        center_sprite_shape = int(hash[2], 16) & 7
166
+
167
+        half_pi = math.pi / 2
168
+
169
+        corner_sprite_rotation = half_pi * (int(hash[3], 16) & 3)
170
+        side_sprite_rotation = half_pi * (int(hash[4], 16) & 3)
171
+        center_sprite_background = int(hash[5], 16) % 2
172
+
173
+        # corner sprite foreground color
174
+        cfr = int(hash[6:8], 16)
175
+        cfg = int(hash[8:10], 16)
176
+        cfb = int(hash[10:12], 16)
177
+
178
+        # side sprite foreground color
179
+        sfr = int(hash[12:14], 16)
180
+        sfg = int(hash[14:16], 16)
181
+        sfb = int(hash[16:18], 16)
182
+
183
+        # size of each sprite
184
+        block_size = int(size / 3)
185
+        # totalsize = size
186
+
187
+        # start with blank 3x3 identicon
188
+
189
+        # generate corner sprites
190
+        corner = self._get_sprite(corner_sprite_shape, block_size)
191
+        ### ctx.fillStyle = "rgb(" + cfr + "," + cfg + "," + cfb + ")";
192
+
193
+        if not rotate:
194
+            corner_sprite_rotation = 0
195
+
196
+        fillstyle = (cfr, cfg, cfb)
197
+
198
+        img = self.draw_rotated_polygon(img, block_size, corner, 0, 0, corner_sprite_rotation, 0, block_size, fillstyle)
199
+        img = self.draw_rotated_polygon(img, block_size, corner, size - block_size, 0, corner_sprite_rotation, 90, block_size, fillstyle)
200
+        img = self.draw_rotated_polygon(img, block_size, corner, size - block_size, size - block_size, corner_sprite_rotation, 180, block_size, fillstyle)
201
+        img = self.draw_rotated_polygon(img, block_size, corner, 0, size - block_size, corner_sprite_rotation, 270, block_size, fillstyle)
202
+
203
+        # draw sides
204
+        if not rotate:
205
+            side_sprite_rotation = 0
206
+
207
+        side = self._get_sprite(side_sprite_shape, size)
208
+
209
+        fillstyle = (sfr, sfg, sfb)
210
+        img = self.draw_rotated_polygon(img, block_size, side, 0, block_size, side_sprite_rotation, 0, block_size, fillstyle)
211
+        img = self.draw_rotated_polygon(img, block_size, side, 2 * block_size, 0, side_sprite_rotation, 90, block_size, fillstyle)
212
+        img = self.draw_rotated_polygon(img, block_size, side, 3 * block_size, 2 * block_size, side_sprite_rotation, 180, block_size, fillstyle)
213
+        img = self.draw_rotated_polygon(img, block_size, side, block_size, 3 * block_size, side_sprite_rotation, 270, block_size, fillstyle)
214
+        #
215
+        # center = self.get_center(center_sprite_shape, size)
216
+        #
217
+        # # make sure there's enough contrast before we use background color of side sprite
218
+        #
219
+        # if center_sprite_background > 0 and (abs(cfr - sfr) > 127 or abs(cfg - sfg) > 127 or abs(cfb - sfb) > 127):
220
+        #     fillstyle = (sfr, sfg, sfb)
221
+        # else:
222
+        #     fillstyle = (cfr, cfg, cfb)
223
+        #
224
+        # if center:
225
+        #     self.draw_rotated_polygon(img, center, size, size, 0, 0, size, fillstyle)
226
+        
227
+        return img

+ 1
- 0
requirements.txt View File

@@ -0,0 +1 @@
1
+Pillow

+ 67
- 0
test.py View File

@@ -0,0 +1,67 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from pydenticon5 import Pydenticon5
4
+
5
+p = Pydenticon5()
6
+p.draw('071e3f61671e790fc492b583a01ae22b', 180, 0).show()
7
+# 071e3f61671e790fc492b583a01ae22b
8
+# abcde9c290d0fb1ca068ffaddf22cbd0
9
+# 94d093eda664addd6e450d7e9881bcad
10
+# d6a3104f846a8165d50b5b14af7f0313
11
+# 211cd88046364c21c9b619d5e119eba9
12
+# c45635f6c3cf0ae2cbade753980667ad
13
+# e4d909c290d0fb1ca068ffaddf22cbd0
14
+# fb469d7ef430b0baf0cab6c436e70375
15
+# 098f6bcd4621d373cade4e832627b4f6
16
+# 81d5381bc1bd1f795970a22b6f19b0c8
17
+# a46ae8d094a190cf890a3d54df54b59c
18
+# 589293d9e6e3ed1bc91fa2cc828f91d9
19
+# 2048561fdd17e80531ba4e29f543ecfc
20
+# f1902ee7999262719edf2b39a4747f85
21
+# a5414c9d27243e857d54b1e2309bf58e
22
+# 5a6309327e044f0ff51388ac879869db
23
+# e45aaef667ac6ae08cf88785c4e901c0
24
+# 25ab3b38f7afc116f18fa9821e44d561
25
+# 2f6c2404198add983753e94fc24e752f
26
+# dc57b55036e648390508e2f3277b9ab5
27
+# 739c5b1cd5681e668f689aa66bcc254c
28
+# 16c75dbf8dc6239f9d74b96299404446
29
+# d90fe1178a183ec92bdb7a2ab2df06f0
30
+# 24a1a3bc832644156b357026cbca787e
31
+# d4528bfc85cbf1377e1c84c18a7c730e
32
+# 9edf71b9ce29b59c2db12810f40a2870
33
+# 9f6b918a4da1dd5a1ef888607aac440d
34
+# 64c5857d6a56157c426074f3b6437917
35
+# c9cb450e5ccb84806134dbf44e3d3366
36
+# 9fc4f98751e9f7ad7114b46e6babd3d1
37
+# f456eb93f6f55ab83146cfb6ce7acc9c
38
+# 8b40a072809a51b5fbd0f47e95267a08
39
+# 4c1d5466e3e0dca6adcf8d3379bd5c93
40
+# 24f80fd406fa88765c631dc78a582230
41
+# cca4a1658b11d3bbd905e6f6c4b100b3
42
+# 808271918ac8d973447dbebe60cc6f77
43
+# d49921a2878af306054e20e0c90dc010
44
+# 9c35a101f93d2999970c06c00639471c
45
+# b17f49b533397d68d33732e424d9a5e3
46
+# 5145e1299684b7f092be73c0795a1509
47
+# 378d397007693fc72ee20dcab7d04594
48
+# 31020cc740e8d620b0178171fb175fe0
49
+# 5f8e518fd288cf9e8492ed4ae9b0382c
50
+# 9948abfedd979c0407ebae27db4b9e3d
51
+# c63501f4aced22d23647445256a5b045
52
+# b438911b33e9378fb71391a37f8a1d3c
53
+# a7e528b4a79d120bfdd02ab80d2e638f
54
+# 5e57d93e095d874ffaaeb6a80504fd0e
55
+# af12d03e5b7c2e25ee23f823cd8322ca
56
+# be0dc8187703a612fff7926aa58ab054
57
+# 2f895d91dbd09991f669a9cb58615825
58
+# bb08f8f7f2910029fe74c879873cfa87
59
+# 57fe4602e139db1f53a53c64a780bb0d
60
+# a18f3a67c46a52875b26c390b181ee66
61
+# 2cfcfd96203ff2f17c4a7f4e69398e3d
62
+# ef7bf4cbfb9104faad17cd9a91bc1ac8
63
+# e1761b8fb966cb3480f6d1d851855a95
64
+# d22e81efa901eea60d9e6fc884711360
65
+# 4d0ee028589c4f6f7b52fde872fb6b2d
66
+# dc469d10b57502a73cbc2efa53989feb
67
+# fe533d35b1ef243c16d189548780869b