Browse code

pixelisation v2

rep authored on 10/12/2015 00:46:48
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,1112 @@
0
+#!/usr/bin/env python
1
+
2
+#    Copyright (C) 2004 Paul Harrison
3
+#    This program is free software; you can redistribute it and/or modify
4
+#    it under the terms of the GNU General Public License as published by
5
+#    the Free Software Foundation; either version 2 of the License, or
6
+#    (at your option) any later version.
7
+#
8
+#    This program is distributed in the hope that it will be useful,
9
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+#    GNU General Public License for more details.
12
+#
13
+#    You should have received a copy of the GNU General Public License
14
+#    along with this program; if not, write to the Free Software
15
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
+
17
+"""  Ghost Diagrams
18
+     
19
+     This program takes sets of tiles that connect together in certain
20
+     ways, and looks for the patterns these tiles imply. The patterns
21
+     are often surprising. 
22
+     
23
+
24
+     This software is currently somewhat alpha.
25
+     
26
+  Tile set specification:
27
+  
28
+     A tile set specification is a list of 6- or 4-character strings,
29
+     eg 'B Aa  ', 'b  Aa '
30
+     
31
+     Each character represents a tile edge. Letters (abcd, ABCD)
32
+     match with their opposite case. Numbers match with themselves.
33
+     
34
+     A number of extra paramters can also be supplied:
35
+     
36
+         border : True/False : draw tile borders or not
37
+         thickness : the thickness of the border
38
+         width : minimum width of diagram
39
+         height : minimum height of diagram
40
+         colors : a list of colors 
41
+            [ background color, edge color, tile 1 color, tile 2 color... ]
42
+         grid : True/False : draw a grid
43
+         labels : True/False : draw labels for each tile under diagram
44
+            
45
+     eg 'B Aa  ', 'b  Aa ', width=1000, height=1000, thickness=0.5, colors=['#000','#000','#fff','#f00']
46
+     
47
+  Change log:
48
+     
49
+     0.1 -- initial release
50
+     0.2 -- don't segfault on empty tiles
51
+     0.3 -- random keeps trying till it finds something that will grow
52
+            optimization (options_cache)
53
+     0.4 -- assembly algorithm tweaks
54
+            random tile set tweaks
55
+     0.5 -- Patch by Jeff Epler
56
+             - allow window resizing
57
+             - new connection types (33,44,cC,dD)
58
+             - DNA tile set 
59
+            widget to set size of tiles
60
+            no repeated tiles in random
61
+            improvements to assembler
62
+     0.6 -- Use Bezier curves
63
+            Parameters to set width, height, thickness, color
64
+            Save images
65
+     0.7 -- Allow square tiles
66
+            Smarter assembler
67
+            Animate assembly
68
+     0.8 -- Knotwork
69
+            Don't fill all of memory
70
+            Use psyco if available
71
+
72
+
73
+  TODO: don't backtrack areas outside current locus
74
+        (difficulty: accidentally creating disconnected islands)
75
+        
76
+  TODO: (blue sky) 3D, third dimension == time
77
+  TODO: allowances: 3 of this, 2 of that, etc.
78
+"""
79
+
80
+try:
81
+    import psyco
82
+    psyco.profile()
83
+except:
84
+    pass
85
+
86
+__version__ = '0.8'
87
+
88
+import sys, os, random, gtk, pango, gobject, string, math, sets
89
+
90
+class Point:
91
+    def __init__(self, x,y):
92
+        self.x = x
93
+        self.y = y
94
+    
95
+    def __add__(self, other): return Point(self.x+other.x,self.y+other.y)
96
+    def __sub__(self, other): return Point(self.x-other.x,self.y-other.y)
97
+    def __mul__(self, factor): return Point(self.x*factor, self.y*factor)
98
+    def length(self): return (self.y*self.y + self.x*self.x) ** 0.5
99
+    def int_xy(self): return int(self.x+0.5), int(self.y+0.5)
100
+    def left90(self): return Point(-self.y, self.x)
101
+
102
+
103
+# ========================================================================
104
+# Constants
105
+
106
+# Hexagonal connection pattern:
107
+#
108
+#     o o
109
+#   o * o
110
+#   o o
111
+#
112
+# (all points remain on a square grid, but a regular hexagon pattern 
113
+#  can be formed by a simple linear transformation)
114
+
115
+connections_6 = [ (-1, 0, 3), (-1, 1, 4), (0, 1, 5), (1, 0, 0), (1, -1, 1), (0, -1, 2) ]
116
+# [ (y, x, index of reverse connection) ]
117
+x_mapper_6 = Point(1.0, 0.0)
118
+y_mapper_6 = Point(0.5, 0.75**0.5)
119
+
120
+connections_4 = [ (-1,0,2), (0,1,3), (1,0,0), (0,-1,1) ]
121
+x_mapper_4 = Point(1.0, 0.0)
122
+y_mapper_4 = Point(0.0, 1.0)
123
+
124
+
125
+
126
+# What edge type connects with what?
127
+# (a tile is represented as a string of 6 characters representing the 6 edges)
128
+compatabilities = { 
129
+    ' ':' ', 
130
+    'A':'a', 'a':'A', 'B':'b', 'b':'B', 'c':'C', 'C':'c', 'd':'D', 'D':'d',
131
+    '1':'1', '2':'2', '3':'3', '4':'4',  '-':'-'
132
+}
133
+
134
+
135
+# Amount of oversampling in drawing
136
+factor = 3
137
+
138
+
139
+# Some cool tile sets people have found
140
+catalogue = [
141
+    "'  33Aa', ' 33 Aa'",
142
+    "'  33Aa/#000', ' 33 Aa/#000', colors=['#fff','#fff'], border=0, grid=0",
143
+    "'ab A  ', 'B  C  ', 'B  c  ', 'B  D  ', 'B  d  '",
144
+    "'d D 4 ', 'd  D  ', '44    '",
145
+    "'AaAa  '",
146
+    "'aA    ', 'AaAa  '",
147
+    "'   bB ', 'bAaB  ', 'aAaA  '",    
148
+    "'B Aa  ', 'b  Aa '",
149
+    "'44    ', '11  4 '",
150
+    "'3 3 3 ', '33    '",
151
+    "'1 1 1 ', '2  12 '",
152
+    "' a    ', 'a AA  '",
153
+    "' AAaa ', 'a  A  '",
154
+    "' a A  ', 'Aaa  A'",
155
+    "'a  a  ', ' aAA A'",
156
+    "' a A a', '    A '",
157
+    "'  AA A', 'a a a '",
158
+    "'a  aa ', '    AA'",
159
+    "'A A a ', 'a a   '",
160
+    "'A A a ', 'a  a  '",
161
+    "' a   4', 'a4 44A', '4A    '",
162
+    "'a2  A ', ' a2 A2'",
163
+    "'a 2a2 ', ' A   A', '     2'",
164
+    "'141   ', '4  4  ', '1 1   '",
165
+    "' 22 22', '22    '",
166
+    "' Aaa  ', 'A1A   ', 'a 1AAa'",
167
+    "'aA a2 ', '2A    ', '  2  A'",
168
+    "'  bB1 ', ' b  B '",
169
+    "'BbB 1 ', '     b'",
170
+    "'b  b b', '  BbB '",
171
+    "'aA1   ', '   AA ', 'a  2  '",
172
+    "' a  4 ', ' 4 4  ', '  A441'",
173
+    "'212111', ' 1 2  '",
174
+    "'22222a', '22 A22'",
175
+    "'2 222 ', '2   B2', '  b  2'",
176
+    "' 21221', '   221', '   2 2'",
177
+    "' a a a', '   A A'",
178
+    "' Dd cA', '   d D', '   a C'",
179
+    "'  CCCc', ' 3Ca A', '  3  c', '     c'",
180
+    "' C dDc', '  CC C', '   ccC'",
181
+    "' Aa Cc', '     c', '     C'",
182
+    "' CcDdC', '  cC c', '     C'",
183
+    "'  CcCc', ' CcC c', '   c C'",
184
+    "'A 1 1 ','a1   B','b  1  '",
185
+    "'aa aa /#fff', 'AA    /#fff', 'A  A  /#fff', grid=0, border=0, thickness=0.3",
186
+    #"'bb bb ', 'BB    ', 'B  B  '",
187
+    "' 44B4D', ' dbB4b', ' 44D d', '    44'",
188
+    "'  d3 3', '   D D'",
189
+    "'  cc c', ' C C c'",
190
+    "'AaAaaa', '  1 Aa', '     A'",
191
+    "'d D 3 ', 'dD    ', '3     '",
192
+    "'a 1 A ', 'a  A  '",
193
+    "'cCCcCC', 'cccC  ', 'c C  C'",
194
+    "'A44444', 'a4   4', '4 4   '",
195
+    "'acaACA', 'acbBCB', 'bcaBCB', 'bcbACA'",
196
+    "'A  ab ', 'B ab  ', 'A  a  ', 'B  b  ', 'ABd  D'", #Tree
197
+    "'d AD  ', ' a  A ', 'a   A ', 'aa A  '", #Counter (?)
198
+    "'bBbBBB', 'bb    ', 'b   B '",
199
+    "'a AA A', 'a a   '",
200
+    "'cC a A', 'a A   '",
201
+    "'bbB  B', 'b BBB ', 'bb    '",
202
+    "'cCc C ', 'cC c C'",
203
+    "'d4 Dd ', 'd D   ', 'DD    '",
204
+    "' 111'",
205
+    "'abA ', 'B C ', 'B c ', 'B D ', 'B d '",
206
+    "'4A4a', '  a4', ' A B', '  Ab'",
207
+    "'acAC', 'adBD', 'bcBD', 'bdAC'",
208
+    "'1111', '   1'",
209
+    "' bbb', '  BB'",
210
+    "'1B1B', 'a A ', ' bA ', 'ab B'",
211
+]
212
+
213
+
214
+default_colors=['#fff','#000', '#8ff', '#f44', '#aaf','#449', '#ff0088', '#ff4088', '#ff4040', '#ff00ff', '#40c0ff']
215
+
216
+default_thickness = 3.0/16
217
+
218
+
219
+# ========================================================================
220
+# Utility functions
221
+
222
+def bezier(a,b,c,d):
223
+    result = [ ]
224
+    n = 12
225
+    for i in xrange(1,n):
226
+        u = float(i) / n
227
+        result.append(
228
+            a * ((1-u)*(1-u)*(1-u)) +
229
+            b * (3*u*(1-u)*(1-u)) +
230
+            c * (3*u*u*(1-u)) +
231
+            d * (u*u*u)
232
+        )
233
+    return result
234
+
235
+
236
+def normalize(form):
237
+    best = form
238
+    for i in xrange(len(form)-1):
239
+        form = form[1:] + form[0]
240
+        if form > best: best = form
241
+    return best
242
+
243
+
244
+# =========================================================================
245
+
246
+class Config:
247
+    def __init__(self, *forms, **kwargs):                                
248
+        self.colors = kwargs.get("colors", [ ])
249
+        self.colors += default_colors[len(self.colors):]
250
+    
251
+        self.border = kwargs.get("border", True)
252
+        self.thickness = kwargs.get("thickness", default_thickness)
253
+        self.width = kwargs.get("width", -1)
254
+        self.height = kwargs.get("height", -1)
255
+        
256
+        self.grid = kwargs.get("grid", True)
257
+        self.labels = kwargs.get("labels", False)
258
+    
259
+        forms = list(forms)
260
+    
261
+        if len(forms) < 1: raise "error"
262
+        
263
+        for item in forms:
264
+            if type(item) != type(""):
265
+                raise "error"
266
+                
267
+        for i in xrange(len(forms)):
268
+            if "/" in forms[i]:
269
+                forms[i], self.colors[i%(len(self.colors)-2)+2] = forms[i].split("/",1)
270
+                
271
+        if len(forms[0]) == 4:
272
+            self.connections = connections_4
273
+            self.x_mapper = x_mapper_4
274
+            self.y_mapper = y_mapper_4
275
+        else:
276
+            self.connections = connections_6
277
+            self.x_mapper = x_mapper_6
278
+            self.y_mapper = y_mapper_6
279
+            
280
+        for item in forms:
281
+            if len(item) != len(self.connections):
282
+                raise "error"
283
+            for edge in item:
284
+                if edge not in compatabilities:
285
+                    raise "error"
286
+        
287
+        self.forms = forms
288
+
289
+# ========================================================================
290
+
291
+
292
+class Assembler:
293
+    def __init__(self, connections, compatabilities, forms, point_set):
294
+        self.connections = connections    # [(y,x,index of reverse connection)]
295
+        self.compatabilities = compatabilities    # { edge-char -> edge-char }
296
+        self.point_set = point_set   # (y,x) -> True
297
+
298
+        self.basic_forms = forms   # ['edge types']
299
+        self.forms = [ ]   # ['edge types']
300
+        self.form_id = [ ]   # [original form number]
301
+        self.rotation = [ ]  # [rotation from original]
302
+        
303
+        for id, form in enumerate(forms):
304
+            current = form
305
+            for i in xrange(len(self.connections)):
306
+                if current not in self.forms:
307
+                    self.forms.append(current)
308
+                    self.form_id.append(id)
309
+                    self.rotation.append(i)
310
+                current = current[1:] + current[0]
311
+
312
+        self.tiles = { }   # (y,x) -> form number
313
+        
314
+        self.dirty = { }   # (y,x) -> True   -- Possible sites for adding tiles
315
+        
316
+        self.options_cache = { }   # pattern -> [form_ids]
317
+        
318
+        self.dead_loci = sets.Set([ ]) # [ {(y,x)->form number} ]
319
+        
320
+        self.history = [ ]
321
+                
322
+        self.total_y = 0
323
+        self.total_x = 0
324
+        
325
+        self.changes = { }
326
+
327
+    def put(self, y,x, value):
328
+        if (y,x) in self.changes:
329
+            if value == self.changes[(y,x)]:
330
+                del self.changes[(y,x)]
331
+        else:
332
+            self.changes[(y,x)] = self.tiles.get((y,x),None)
333
+        
334
+            
335
+        if (y,x) in self.tiles:
336
+            self.total_y -= y
337
+            self.total_x -= x
338
+            
339
+        if value == None:
340
+            if (y,x) not in self.tiles: return
341
+            del self.tiles[(y,x)]
342
+            self.dirty[(y,x)] = True
343
+        else:
344
+            self.tiles[(y,x)] = value
345
+            self.total_y += y
346
+            self.total_x += x
347
+        
348
+        for oy, ox, opposite in self.connections:
349
+            y1 = y + oy
350
+            x1 = x + ox
351
+            if (y1,x1) not in self.tiles and (y1, x1) in self.point_set:
352
+                self.dirty[(y1,x1)] = True
353
+                    
354
+    def get_pattern(self, y,x):
355
+        result = ''
356
+        for oy, ox, opposite in self.connections:
357
+            y1 = y + oy
358
+            x1 = x + ox
359
+            if self.tiles.has_key((y1,x1)):
360
+                result += self.compatabilities[self.forms[self.tiles[(y1,x1)]][opposite]]
361
+            #elif (y1,x1) not in self.point_set:
362
+            #    result += ' '
363
+            else:
364
+                result += '.'
365
+        
366
+        return result
367
+
368
+    def fit_ok(self, pattern,form_number):
369
+        form = self.forms[form_number]
370
+        for i in xrange(len(self.connections)):
371
+            if pattern[i] != '.' and pattern[i] != form[i]:
372
+                return False
373
+               
374
+        return True
375
+    
376
+    def options(self, y,x):
377
+        pattern = self.get_pattern(y,x)
378
+        if pattern in self.options_cache:
379
+            result = self.options_cache[pattern]
380
+        
381
+        result = [ ]
382
+        for i in xrange(len(self.forms)):
383
+            if self.fit_ok(pattern,i):
384
+                result.append(i)
385
+        result = tuple(result)
386
+        
387
+        self.options_cache[pattern] = result
388
+        
389
+        return result
390
+            
391
+    def locus(self, y,x, rotation=0):
392
+        visited = { }
393
+        neighbours = { }
394
+        todo = [ ((y,x), (0,0)) ]
395
+        result = [ ]
396
+        
397
+        min_y = 1<<30
398
+        min_x = 1<<30
399
+        
400
+        while todo:
401
+            current, offset = todo.pop(0)
402
+            if current in visited: continue
403
+            visited[current] = True
404
+
405
+            any = False
406
+            new_todo = [ ]
407
+            for i, (oy, ox, opposite) in enumerate(self.connections):
408
+                neighbour = (current[0]+oy, current[1]+ox)
409
+                if neighbour in self.point_set:                
410
+                    if neighbour in self.tiles:
411
+                        any = True
412
+                        neighbours[neighbour] = True
413
+                        min_y = min(min_y, offset[0])
414
+                        min_x = min(min_x, offset[1])
415
+                        result.append( (offset, opposite, 
416
+                                        self.forms[self.tiles[neighbour]][opposite]) )
417
+                    else:
418
+                        temp = self.connections[(i+rotation) % len(self.connections)]
419
+                        new_offset = (offset[0]+temp[0], offset[1]+temp[1])                
420
+                        new_todo.append((neighbour, new_offset))
421
+                        
422
+            if not any and len(self.connections) == 4:
423
+                for oy, ox in ((-1,-1), (-1,1), (1,-1), (1,1)):
424
+                    neighbour = (current[0]+oy, current[1]+ox)
425
+                    if neighbour in self.tiles:
426
+                        any = True
427
+                        break
428
+        
429
+            if any:
430
+                todo.extend(new_todo)
431
+                
432
+        result = [ (yy-min_y,xx-min_x,a,b) for ((yy,xx),a,b) in result ]
433
+
434
+        return sets.ImmutableSet(result), visited, neighbours
435
+
436
+        
437
+    def filter_options(self, y,x,options):
438
+        result = [ ]
439
+        for i in options:
440
+            self.tiles[(y,x)] = i
441
+            visiteds = [ ]
442
+            
443
+            for oy, ox, oppoiste in self.connections:
444
+                y1 = y+oy
445
+                x1 = x+ox
446
+                
447
+                ok = True
448
+                if (y1,x1) not in self.tiles and (y1,x1) in self.point_set:
449
+                    for visited in visiteds:
450
+                        if (y1,x1) in visited: 
451
+                            ok = False
452
+                            break
453
+                    if ok:
454
+                        locus, visited, _ = self.locus(y1,x1)
455
+                        visiteds.append(visited)
456
+                        if locus is not None and locus in self.dead_loci: 
457
+                            break
458
+            else:
459
+                result.append(i)
460
+            
461
+            del self.tiles[(y,x)]
462
+
463
+        return result
464
+                            
465
+    def any_links_to(self, y,x):
466
+        for oy, ox, opposite in self.connections:
467
+            y1 = y + oy
468
+            x1 = x + ox
469
+            if (y1, x1) in self.tiles:
470
+                if self.forms[self.tiles[(y1,x1)]][opposite] != ' ':
471
+                    return True
472
+        return False
473
+
474
+    def prune_dead_loci(self):
475
+        for item in list(self.dead_loci):
476
+            if random.randrange(2):
477
+                self.dead_loci.remove(item)
478
+
479
+    def iterate(self):
480
+        if not self.tiles:
481
+            self.put(0,0,0)
482
+            self.history.append((0,0))
483
+            return True
484
+            
485
+        mid_y = 0.0
486
+        mid_x = 0.0
487
+        for y, x in self.dirty.keys():
488
+            if (y,x) in self.tiles or not self.any_links_to(y,x): 
489
+                del self.dirty[(y,x)]
490
+                continue
491
+            mid_y += y
492
+            mid_x += x
493
+        
494
+        if not self.dirty:
495
+            return False
496
+        
497
+        mid_y /= len(self.dirty)
498
+        mid_x /= len(self.dirty)
499
+                        
500
+        point_list = [ ]
501
+        for y, x in self.dirty.keys():
502
+            yy = y - mid_y
503
+            xx = x - mid_x
504
+            sorter = ((yy*2)**2+(xx*2+yy)**2)
505
+            point_list.append( (sorter,y,x) )
506
+            
507
+        point_list.sort()
508
+        
509
+        best = None        
510
+
511
+        for sorter, y, x in point_list:
512
+            options = self.options(y,x)
513
+            
514
+            if len(options) < 2:
515
+                score = 0
516
+            else:
517
+                score = 1
518
+                                                
519
+            if best == None or score < best_score:
520
+                best = options
521
+                best_score = score
522
+                best_x = x
523
+                best_y = y
524
+                if score == 0: break
525
+
526
+        if best == None: return False
527
+        
528
+        best = self.filter_options(best_y,best_x,best)
529
+                
530
+        if len(best) > 0:
531
+            self.put(best_y,best_x,random.choice(best))
532
+            self.history.append((best_y,best_x))
533
+            return True
534
+            
535
+        #otherwise, backtrack:
536
+            
537
+        for i in xrange(len(self.connections)):
538
+            locus, _, relevant = self.locus(best_y,best_x,i)
539
+            if locus is None: break
540
+            self.dead_loci.add(locus)
541
+            if len(locus) > 8: break
542
+
543
+        if len(self.dead_loci) > 10000:
544
+            self.prune_dead_loci()                
545
+            
546
+        # Shape of distribution
547
+        autism = 1.0 # 1.0 == normal, >1.0 == autistic (just a theory :-) )
548
+            
549
+        # Overall level
550
+        adhd = 2.0   # Lower == more adhd
551
+            
552
+        n = 1
553
+        while n < len(self.tiles)-1 and random.random() < ( n/(n+autism) )**adhd:
554
+            n += 1
555
+            
556
+        while self.history and (n > 0 or
557
+                                self.locus(best_y,best_x)[0] in self.dead_loci):
558
+            item = self.history.pop(-1)
559
+            self.put(item[0],item[1],None)
560
+            n -= 1
561
+                
562
+        if not self.tiles:
563
+            return False
564
+
565
+        return True
566
+
567
+
568
+
569
+
570
+# ========================================================================
571
+
572
+
573
+class Interface:
574
+    def __init__(self):
575
+        self.iteration = 0
576
+        self.idle_enabled = False
577
+
578
+        self.drawing = gtk.DrawingArea()
579
+        self.drawing.unset_flags(gtk.DOUBLE_BUFFERED)
580
+        #self.drawing.add_events(gtk.gdk.BUTTON_PRESS_MASK)
581
+        self.drawing.connect('expose-event', self.on_expose)
582
+        self.drawing.connect('size-allocate', self.on_size)
583
+        #self.drawing.connect('button-press-event', self.on_click)
584
+        
585
+        scroller = gtk.ScrolledWindow()
586
+        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
587
+        scroller.add_with_viewport(self.drawing)
588
+    
589
+        self.combo = gtk.Combo()
590
+        for item in catalogue:
591
+            item = gtk.ListItem(item)
592
+            item.get_children()[0].modify_font(pango.FontDescription("mono"))
593
+            item.show()
594
+            self.combo.list.append_items([item])
595
+        self.combo.list.connect('select-child', lambda widget, child: self.reset())
596
+        self.combo.entry.modify_font(pango.FontDescription("mono"))
597
+        
598
+        knot_box = gtk.CheckButton("knotwork")
599
+        knot_box.set_active(False)
600
+        self.knot = False
601
+        knot_box.connect("toggled", self.on_knot_changed)
602
+        
603
+        scale_label = gtk.Label(' Size: ')
604
+        scale = gtk.SpinButton()
605
+        scale.set_digits(1)
606
+        scale.set_increments(1.0,1.0)
607
+        scale.set_range(3.0, 50.0)
608
+        scale.set_value(10.0)
609
+        scale.connect('value-changed', self.on_set_scale)
610
+
611
+        random_button = gtk.Button(' Random ')
612
+        random_button.connect('clicked', lambda widget: self.random())
613
+        
614
+        reset = gtk.Button(' Restart ')
615
+        reset.connect('clicked', lambda widget: self.reset())
616
+
617
+        save = gtk.Button(' Save image... ')
618
+        save.connect('clicked', self.on_save)
619
+
620
+        hbox = gtk.HBox(False,5)
621
+        hbox.set_border_width(3)
622
+        hbox.pack_start(knot_box, False, False, 0)
623
+        hbox.pack_end(save, False,False,0)
624
+        hbox.pack_end(reset, False,False,0)
625
+        hbox.pack_end(random_button, False,False,0)
626
+        hbox.pack_end(scale, False,False,0)
627
+        hbox.pack_end(scale_label, False,False,0)
628
+
629
+        vbox = gtk.VBox(False,5)
630
+        vbox.pack_start(self.combo, False,False,0)
631
+        vbox.pack_start(hbox, False,False,0)
632
+        vbox.pack_start(scroller, True,True,0)
633
+
634
+        self.window = gtk.Window()
635
+        self.window.set_default_size(600,650)
636
+        #self.window.set_default_size(200,200)
637
+        self.window.set_title('Ghost Diagrams')
638
+        self.window.add(vbox)
639
+        self.window.connect('destroy', lambda widget: gtk.main_quit())
640
+
641
+        self.drawing.realize()
642
+        
643
+        self.gc = gtk.gdk.GC(self.drawing.window)
644
+
645
+        self.pixbuf_ready = False
646
+        self.render_iterator = None
647
+        
648
+        self.randomizing = False
649
+        
650
+        self.set_scale(scale.get_value())
651
+    
652
+    def on_size(self, widget, event):
653
+        self.pixbuf_ready = False
654
+        
655
+        self.width = event.width
656
+        self.height = event.height
657
+        self.pixmap = gtk.gdk.Pixmap(self.drawing.window, self.width*factor,self.height*factor)
658
+
659
+        self.pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
660
+                                     self.width*factor,self.height*factor)
661
+        self.scaled_pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 
662
+                                            self.width,self.height)
663
+        self.reset()
664
+
665
+    def on_set_scale(self, widget):
666
+        self.set_scale(widget.get_value())
667
+        self.reset()
668
+
669
+    def set_scale(self, value):
670
+        self.scale = value
671
+        
672
+    def on_knot_changed(self, widget):
673
+        self.knot = widget.get_active()
674
+        self.reset()
675
+        
676
+    def reset(self):
677
+        try:
678
+            self.config = eval('Config('+self.combo.entry.get_text()+')')
679
+        except:
680
+            import traceback
681
+            traceback.print_exc()
682
+            self.config = Config("    ", grid=False, colors=["#f66"])
683
+
684
+        colormap = self.drawing.get_colormap()
685
+        self.colors = [ colormap.alloc_color(item) for item in self.config.colors ]
686
+        
687
+        point_set = { }
688
+        yr = int( self.height/self.scale/4 )
689
+        xr = int( self.width/self.scale/4 )        
690
+        if self.config.labels:
691
+            bound = self.scale * 3
692
+            for y in xrange(-yr,yr):
693
+                for x in xrange(-yr,xr):
694
+                    point = self.pos(x*2,y*2)
695
+                    if point.x > bound and point.x < self.width-bound and \
696
+                       point.y > bound and point.y < self.height-bound-90:
697
+                        point_set[(y,x)] = True
698
+        else:
699
+            bound = self.scale * 3
700
+            for y in xrange(-yr,yr):
701
+                for x in xrange(-yr,xr):
702
+                    point = self.pos(x*2,y*2)
703
+                    if point.x > -bound and point.x < self.width+bound and \
704
+                       point.y > -bound and point.y < self.height+bound:
705
+                        point_set[(y,x)] = True
706
+                    
707
+
708
+        self.pixbuf_ready = False
709
+        self.render_iterator = None
710
+        self.randomizing = False
711
+        self.iteration = 0
712
+        self.drawing.queue_draw()
713
+        self.shapes = { }
714
+        self.polys = { }
715
+        self.assembler = Assembler(self.config.connections, compatabilities, 
716
+                                   self.config.forms, point_set)
717
+        if not self.idle_enabled:
718
+            gobject.idle_add(self.idle)
719
+            self.idle_enabled = True
720
+            
721
+        self.drawing.set_size_request(self.config.width, self.config.height)
722
+        self.drawing.queue_draw()
723
+
724
+                
725
+    def run(self):
726
+        self.window.show_all()
727
+        gtk.main()
728
+
729
+
730
+    def idle(self):
731
+        if self.render_iterator:
732
+            try:
733
+                self.render_iterator.next()
734
+            except StopIteration:
735
+                self.render_iterator = None
736
+            return True
737
+        
738
+        if not self.idle_enabled: 
739
+            return False
740
+        
741
+        self.idle_enabled = self.assembler.iterate()
742
+
743
+        self.iteration += 1
744
+        
745
+        if self.randomizing and \
746
+           (self.iteration == 100 or not self.idle_enabled):
747
+            self.randomizing = False
748
+            
749
+            forms_present = { }
750
+            for item in self.assembler.tiles.values():
751
+                forms_present[self.assembler.form_id[item]] = 1
752
+            
753
+            if len(self.assembler.tiles) < 10 \
754
+               or len(forms_present) < len(self.assembler.basic_forms):
755
+                self.idle_enabled = False
756
+                self.random(True)
757
+                return False
758
+
759
+        if not self.idle_enabled or len(self.assembler.changes) >= 8:
760
+            changes = self.assembler.changes
761
+            self.assembler.changes = { }
762
+            for y,x in changes:
763
+                old = changes[(y,x)]
764
+                if old is not None:
765
+                    self.draw_poly(y,x,old,1, self.drawing.window, True)
766
+            for y,x in changes:
767
+                new = self.assembler.tiles.get((y,x),None)
768
+                if new is not None:
769
+                    self.draw_poly(y,x,new,1, self.drawing.window, False)
770
+
771
+        if not self.idle_enabled and not self.assembler.dirty:
772
+            self.render_iterator = self.make_render_iter()
773
+
774
+        return True
775
+
776
+
777
+    def pos(self, x,y, center=True):
778
+        result = (self.config.x_mapper*x + self.config.y_mapper*y) * (self.scale*2)
779
+        if center: 
780
+            return result + Point(self.width/2.0,self.height/2.0)
781
+        else:
782
+            return result
783
+        
784
+    def make_shape(self, form_number):
785
+        if form_number in self.shapes:
786
+            return self.shapes[form_number]
787
+            
788
+        result = [ ]
789
+        connections = { }
790
+        
791
+        for i in xrange(len(self.assembler.connections)):
792
+            yy, xx = self.assembler.connections[i][:2]
793
+            symbol = self.assembler.forms[form_number][i]
794
+            if symbol in ' -': continue
795
+            
796
+            edge = self.pos(xx,yy,0)
797
+            out = edge
798
+            left = out.left90()
799
+        
800
+            if symbol in 'aA1':
801
+                r = 0.4
802
+                #r = 0.6
803
+            elif symbol in 'bB2': 
804
+                r = 0.3
805
+            elif symbol in 'cC3':
806
+                r = 0.225
807
+            else:
808
+                r = 0.15
809
+                        
810
+            if symbol in 'ABCD': 
811
+                poke = 0.3 #r
812
+            elif symbol in 'abcd': 
813
+                poke = -0.3 #-r
814
+            else:
815
+                poke = 0.0
816
+
817
+            points = [
818
+                edge + left*-r,
819
+                edge + out*poke,
820
+                edge + left*r,
821
+            ]                        
822
+            
823
+            result.append( (out * (1.0/out.length()), points, 0.5)) #0.625))
824
+            connections[i] = points
825
+            # Note: set constant to ~0.35 for old-style circular look
826
+
827
+        if len(result) == 1:
828
+            point = result[0][0]*(self.scale*-0.7)
829
+            result.append( (result[0][0].left90()*-1.0, [point], 0.8) )
830
+            result.append( (result[0][0].left90(), [point], 0.8) )
831
+
832
+        poly = [ ]
833
+        for i in xrange(len(result)):
834
+            a = result[i-1][1][-1]
835
+            d = result[i][1][0]
836
+            length = (d-a).length() * ((result[i][2]+result[i-1][2])*0.5)
837
+            b = a - result[i-1][0]*length
838
+            c = d - result[i][0]*length
839
+            poly.extend(bezier(a,b,c,d))                 
840
+            poly.extend(result[i][1])
841
+            
842
+        links = [ ]    
843
+        if self.knot:
844
+            form = self.assembler.forms[form_number]
845
+            items = connections.keys()
846
+            cords = [ ]
847
+            
848
+            if len(items)%2 != 0:
849
+                for item in items[:]:
850
+                    if (item+len(form)/2)   % len(form) not in items and \
851
+                       (item+len(form)/2+1) % len(form) not in items and \
852
+                       (item+len(form)/2-1) % len(form) not in items:
853
+                        items.remove(item)
854
+                        
855
+            if len(items)%2 != 0:
856
+                for i in xrange(len(form)):
857
+                    if form[i] not in ' -' and \
858
+                       form.count(form[i]) == 1 and \
859
+                       (compatabilities[form[i]] == form[i] or \
860
+                        form.count(compatabilities[form[i]])%2 == 0):
861
+                        items.remove(i)
862
+                        
863
+            if len(items)%2 != 0:
864
+                for item in items[:]:
865
+                    if (item+len(form)/2) % len(form) not in items:
866
+                        items.remove(item)
867
+                        
868
+            if len(items)%2 == 0:
869
+                rot = self.assembler.rotation[form_number]
870
+                mod = len(self.assembler.connections)
871
+                items.sort(lambda a,b: cmp((a+rot)%mod,(b+rot)%mod))
872
+                step = len(items)/2
873
+              
874
+                for ii in xrange(len(items)/2):
875
+                    i = items[ii]
876
+                    j = items[ii-step]
877
+                    cords.append((i,j))
878
+                    
879
+            for i,j in cords:
880
+                a = connections[i]
881
+                b = connections[j]
882
+                a_in = (a[-1]-a[0]).left90()
883
+                a_in = a_in*(self.scale*1.25/a_in.length())
884
+                b_in = (b[-1]-b[0]).left90()
885
+                b_in = b_in*(self.scale*1.25/b_in.length())
886
+                a = [(a[0]+a[1])*0.5,a[1],(a[-2]+a[-1])*0.5]
887
+                b = [(b[0]+b[1])*0.5,b[1],(b[-2]+b[-1])*0.5]
888
+                bez1 = bezier(a[-1],a[-1]+a_in,b[0]+b_in,b[0])
889
+                bez2 = bezier(b[-1],b[-1]+b_in,a[0]+a_in,a[0])
890
+                linker = a + bez1 + b + bez2
891
+                links.append((linker,a[-1:]+bez1+b[:1],b[-1:]+bez2+a[:1]))
892
+                
893
+        self.shapes[form_number] = poly, links
894
+        return poly, links
895
+        
896
+    def draw_poly(self, y,x,form_number,factor, drawable, erase=False):
897
+        id = (y,x,form_number,factor)
898
+            
899
+        if id not in self.polys:
900
+            def intify(points): return [ ((middle+point)*factor).int_xy() for point in points ]
901
+        
902
+            middle = self.pos(x*2,y*2)
903
+            
904
+            poly, links = self.make_shape(form_number)        
905
+            poly = intify(poly)
906
+            links = [ (intify(link), intify(line1), intify(line2)) for link,line1,line2 in links ]
907
+            
908
+            self.polys[id] = poly, links
909
+        else:
910
+            poly, links = self.polys[id]
911
+
912
+        if len(poly) > 0:
913
+            if erase:
914
+                color = 0
915
+            else:
916
+                color = self.assembler.form_id[form_number] % (len(self.colors)-2) + 2  
917
+                
918
+            self.gc.set_rgb_fg_color(self.colors[color])
919
+            drawable.draw_polygon(self.gc, True, poly)
920
+            
921
+            if self.knot:
922
+                self.gc.set_line_attributes(max(factor,int(self.scale*factor * self.config.thickness)),
923
+                     gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND)
924
+                for link, line1, line2 in links:
925
+                    if not erase: self.gc.set_rgb_fg_color(self.colors[1])
926
+                    drawable.draw_polygon(self.gc, True, link)
927
+                    if not erase: self.gc.set_rgb_fg_color(self.colors[color])
928
+                    #drawable.draw_polygon(self.gc, False, link)
929
+                    drawable.draw_lines(self.gc, line1)
930
+                    drawable.draw_lines(self.gc, line2)
931
+                            #drawable.draw_line(self.gc, *(connections[i][-1]+connections[j][0]))
932
+                            #drawable.draw_line(self.gc, *(connections[j][-1]+connections[i][0]))
933
+
934
+            if self.config.border:
935
+                self.gc.set_line_attributes(max(factor,int(self.scale*factor * self.config.thickness)),
936
+                     gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND)
937
+                if not erase: self.gc.set_rgb_fg_color(self.colors[1])
938
+                self.pixmap.draw_polygon(self.gc, False, poly)
939
+                drawable.draw_polygon(self.gc, False, poly)
940
+        
941
+    def make_render_iter(self):
942
+        yield None
943
+        
944
+        if not self.assembler.tiles:
945
+            self.pixbuf_ready = False
946
+            return
947
+        
948
+        self.gc.set_rgb_fg_color(self.colors[0])
949
+        self.pixmap.draw_rectangle(self.gc, True, 0,0,self.width*factor,self.height*factor)
950
+        
951
+        if self.config.labels and False:
952
+            font = pango.FontDescription("mono bold 36")
953
+            self.gc.set_rgb_fg_color(self.colors[1])
954
+        
955
+            for i, form in enumerate(self.assembler.basic_forms):
956
+                layout = self.drawing.create_pango_layout(" "+form.replace(" ","-")+" ")
957
+                layout.set_font_description(font)
958
+                x = (i+1)*(len(form)+3)*30
959
+                y = (self.height-70) *factor
960
+                width, height = layout.get_pixel_size()
961
+                self.pixmap.draw_rectangle(self.gc, True, x-6,y-6, width+12,height+12)
962
+                self.pixmap.draw_layout(self.gc, x,y, layout, self.colors[1], self.colors[i+2])            
963
+
964
+        if self.config.grid:
965
+            colormap = self.drawing.get_colormap()
966
+            self.gc.set_rgb_fg_color(colormap.alloc_color("#eee"))
967
+            self.gc.set_line_attributes(factor,
968
+                     gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND)
969
+            f = 4.0 / len(self.config.connections)
970
+            for (y,x) in self.assembler.point_set.keys():
971
+                poly = [ ]
972
+                for i in xrange(len(self.config.connections)):
973
+                    a = self.config.connections[i-1]
974
+                    b = self.config.connections[i]
975
+                    poly.append((self.pos(x*2+(a[0]+b[0])*f,y*2+(a[1]+b[1])*f)*factor).int_xy())
976
+            
977
+                self.pixmap.draw_polygon(self.gc, False, poly)
978
+                yield None
979
+
980
+        for (y,x), form_number in self.assembler.tiles.items():
981
+            self.draw_poly(y,x,form_number,factor, self.pixmap)
982
+            yield None
983
+            
984
+        self.pixbuf.get_from_drawable(self.pixmap, self.pixmap.get_colormap(), 
985
+                                      0,0,0,0, self.width*factor,self.height*factor)
986
+                                      
987
+        yield None
988
+            
989
+        self.pixbuf.scale(self.scaled_pixbuf, 0,0,self.width,self.height,
990
+              0,0,1.0/factor,1.0/factor,gtk.gdk.INTERP_BILINEAR)
991
+        
992
+        self.pixbuf_ready = True
993
+        self.drawing.queue_draw()
994
+    
995
+    def on_expose(self, widget, event):
996
+        self.assembler.changes = dict([ (item, None) for item in self.assembler.tiles ])
997
+
998
+        if self.pixbuf_ready:
999
+            self.drawing.window.draw_pixbuf(self.gc, self.scaled_pixbuf, 0,0,0,0,-1,-1)
1000
+        else:
1001
+            self.gc.set_rgb_fg_color(self.colors[0])
1002
+            self.drawing.window.draw_rectangle(self.gc, True, 0,0,self.width,self.height)
1003
+
1004
+    def random(self, same_form=False):
1005
+        if same_form:
1006
+            n = len(self.assembler.basic_forms)
1007
+            sides = len(self.assembler.basic_forms[0])
1008
+        else:
1009
+            n = random.choice([1,1,2,2,2,3,3,3,4])
1010
+            if self.knot:
1011
+                sides = 6
1012
+            else:
1013
+                sides = random.choice([4,6])
1014
+        
1015
+                    
1016
+        while True:
1017
+            if self.knot:
1018
+                edge_counts = [ random.choice(range(2,sides+1,2)) 
1019
+                                for i in xrange(n) ]
1020
+            else:
1021
+                edge_counts = [ random.choice(range(1,sides+1)) 
1022
+                                for i in xrange(n) ]
1023
+                                
1024
+            edge_counts.sort()
1025
+            edge_counts.reverse()
1026
+            if edge_counts[0] != 1: break
1027
+        
1028
+        while True:
1029
+            try:
1030
+                result = [ ]
1031
+                previous = '1234' + 'aAbBcCdD' #* 3
1032
+                for edge_count in edge_counts:
1033
+                    item = [' ']*(sides-edge_count)
1034
+                    for j in xrange(edge_count): 
1035
+                        selection = random.choice(previous)
1036
+                        previous += compatabilities[selection]*6 #12
1037
+                        item.append(selection)
1038
+                
1039
+                    random.shuffle(item)
1040
+                    item = normalize(string.join(item,''))
1041
+                    if item in result: raise "repeat"
1042
+                    result.append(item)
1043
+            
1044
+                all = string.join(result,'')
1045
+                for a, b in compatabilities.items():
1046
+                    if a in all and b not in all: raise "repeat"
1047
+        
1048
+                break
1049
+            except "repeat":
1050
+                pass
1051
+        
1052
+        self.combo.entry.set_text(repr(result)[1:-1])
1053
+        self.reset()
1054
+        self.randomizing = True
1055
+
1056
+    def on_save(self, widget):
1057
+        selecter = gtk.FileSelection('Save image')
1058
+        selecter.set_filename('diagram.png')
1059
+        
1060
+        def on_ok(widget):
1061
+            filename = selecter.get_filename()
1062
+            selecter.destroy()
1063
+            
1064
+            if self.pixbuf_ready:
1065
+                self.scaled_pixbuf.save(filename, 'png')
1066
+            else:
1067
+                pass
1068
+                # TODO: show error
1069
+            
1070
+        def on_cancel(widget):
1071
+            selecter.destroy()
1072
+            
1073
+        selecter.ok_button.connect('clicked', on_ok)
1074
+        selecter.cancel_button.connect('clicked', on_ok)
1075
+        
1076
+        selecter.show()
1077
+
1078
+
1079
+
1080
+if __name__ == '__main__':
1081
+    Interface().run()
1082
+    sys.exit(0)
1083
+
1084
+    # Just some phd stuff...
1085
+    interface = Interface()
1086
+    interface.window.show_all()
1087
+    
1088
+    base = 2
1089
+    chars = " 1"
1090
+    n = len(connections)
1091
+    done = { }
1092
+    
1093
+    for i in xrange(1, base ** n):
1094
+        result = ""
1095
+        for j in xrange(n):
1096
+            result += chars[(i / (base ** j)) % base]
1097
+        if normalize(result) in done or normalize(result.swapcase()) in done: continue
1098
+        print result
1099
+        done[normalize(result)] = True
1100
+        
1101
+        interface.combo.entry.set_text("'"+result+"', width=350, height=400")
1102
+        interface.reset()
1103
+    
1104
+        while gtk.events_pending():
1105
+            gtk.main_iteration()
1106
+            
1107
+        if interface.assembler.dirty:
1108
+            print "--- failed"
1109
+            continue
1110
+        
1111
+        interface.scaled_pixbuf.save("/tmp/T" + result.replace(" ","-") + ".png", "png")
0 1112
new file mode 100644
... ...
@@ -0,0 +1,268 @@
0
+#!/usr/bin/python
1
+# coding: utf-8
2
+
3
+# haacheuur 0.24
4
+# port industriel de port la nouvelle - couleur - 60cm*30cm
5
+# image source : pln.jpg
6
+# image obtenue : pln..20150910-11h59m53s.jpg
7
+
8
+# pln.png 3936x1024 pix
9
+
10
+'''
11
+image input puredata : 16384 width x 768 height
12
+donc par exemple, pour 3 images :
13
+3 x 16384 = 49152 pixels en tout
14
+'''
15
+
16
+import sys
17
+import Image
18
+import random
19
+import os
20
+import ImageDraw
21
+import ImageFont
22
+import ImageFilter
23
+from time import gmtime, strftime
24
+import time
25
+
26
+if not len(sys.argv) > 1:
27
+    raise SystemExit("Usage: %s image_source" % sys.argv[0])
28
+
29
+# modifs du 30/10/2013
30
+import ImageEnhance
31
+
32
+
33
+#rapport d'allongement de la nouvelle image par rapport à la largeur de l'image originale
34
+allongement = 1
35
+
36
+
37
+#ouverture de l'image source et conversion en mode couleur 1bit
38
+#1 bit (sys.argv[1])).convert('1') et niveaux de gris : (sys.argv[1])).convert('L')
39
+im1 = Image.open(str(sys.argv[1]))
40
+im2 = im1.copy()
41
+im3 = Image.new("RGBA",(im1.size[0], im1.size[1]))
42
+im5 = Image.new("RGBA",(im1.size[0], im1.size[1]))
43
+im4 = Image.new("RGBA",(im1.size[0]*allongement, im1.size[1]))
44
+#im4 = Image.new("RGBA",(49152, im1.size[1]))
45
+
46
+Larg = im1.size[0]
47
+Haut = im1.size[1]
48
+import pickle
49
+
50
+loadfile = False
51
+
52
+class Sequence:
53
+	def __init__(s):
54
+		randomCoupeHauteur = []
55
+		s.randomCopyPosi =[]
56
+		s.proportions=[]
57
+		s.choix=[]
58
+		s.sizeOutput=None
59
+		s.sizeInput=None
60
+	
61
+"""
62
+seq = dict()
63
+seq["randomCoupeHauteur"] = []
64
+seq["randomCopyPosi"] = []
65
+seq["proportions"] = []
66
+seq["choix"] = []
67
+seq["sizeOutput"]= im4.size
68
+seq["sizeInput"] = im1.size
69
+"""
70
+
71
+if loadfile:
72
+	seq=pickle.load(open("test.pickle"))
73
+
74
+else :
75
+	seq=Sequence()
76
+	
77
+for i in range(1):
78
+	# constitution de la liste des tranches horizontales
79
+	# genre comme si qu'on avait un 16 pistes :)
80
+	# nombre aleatoire compris dans les limites de l'image
81
+	def randHaut():
82
+		return random.randint(0, im1.size[1]/8)*8
83
+
84
+	if loadfile:
85
+		randomCoupeHauteur = seq.randomCoupeHauteur
86
+
87
+	else:
88
+		randomCoupeHauteur = [0, \
89
+		randHaut(),randHaut(),randHaut(),randHaut(), \
90
+		randHaut(),randHaut(),randHaut(),randHaut(), \
91
+		randHaut(),randHaut(),randHaut(),randHaut(), \
92
+		randHaut(),randHaut(),randHaut(),randHaut(), \
93
+		randHaut(),randHaut(),randHaut(),randHaut(), \
94
+		randHaut(),randHaut(),randHaut(),randHaut(), \
95
+		randHaut(),randHaut(),randHaut(),randHaut(), \
96
+		randHaut(),randHaut(),randHaut(),randHaut(), \
97
+		im1.size[1]]
98
+		# rangement des valeurs des plus petites au plus grandes
99
+		randomCoupeHauteur.sort()
100
+		seq.randomCoupeHauteur = randomCoupeHauteur
101
+
102
+	# les hachures
103
+	def Hacheur(haut, bas) :
104
+
105
+		n=0
106
+		i=0
107
+		#!!!!!!!!!!
108
+		while n<im4.size[0] :
109
+			if loadfile:
110
+				proportions = seq.proportions
111
+				choix_rnd = seq.choix[i]
112
+				randomCopyPosi = seq.randomCopyPosi[i]
113
+			else:
114
+
115
+				'''
116
+				8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128 136 144 152 160 168
117
+				'''
118
+
119
+				#proportions = [(160,6),(120,4),(120,8),(80,16),(60,16),(20,32),(8,24),(8,16),(5,64),(3,24),(2,24),(1,256),(1,128),(1,64),(1,32),(1,16),(1,8)]
120
+				#proportions = [(160,6),(120,4),(120,8),(80,16),(60,16),(20,32),(8,24),(8,16),(5,64),(3,24),(2,24),(1,256),(1,128),(1,64),(1,32),(1,16),(1,8)]
121
+				#proportions = [(1280,1),(800,1),(800,2),(640,1),(640,2),(640,4),(320,4),(320,2),(320,1),(160,1),(120,2),(120,3),(80,6),(64,12),(64,6),(32,4),(32,8),(32,16),(32,32),(32,64)]
122
+
123
+				proportions = [(16,2),(8,2),(4,2),(2,2),(1,2)]
124
+
125
+				#proportion pln pour output final avec puredata/gem (decalage phase 32pix)
126
+				
127
+				'''
128
+				proportions = [	(768,2),\
129
+								(512,4),\
130
+								(256,8),\
131
+								(128,4),(128,16),\
132
+								(64,4),(64,8),(64,16),\
133
+								(32,8),(32,16),(32,32),(32,32),(32,64),(32,128),\
134
+								(16,1),(16,4),(16,16),(16,64)]
135
+				'''
136
+				#proportions = [(512,1),(256,2),(128,4),(64,8),(64,4),(32,16),(32,32),(32,8),(16,8),(16,32),(8,64),(8,32),(8,16)]
137
+				#proportions = [(32,1),(32,2),(32,3),(32,4),(32,5),(32,6),(32,8),(32,12),(32,16),(32,24),(32,32),(32,64)]
138
+				
139
+				seq.proportions = proportions
140
+				#proportions = seq.proportions[]
141
+				choix_rnd = random.randint(0, len(proportions)-1)
142
+				#choix_rnd = random.randint(0, len(proportions)-1)
143
+				seq.choix.append(choix_rnd)
144
+				
145
+				largeur = proportions[choix_rnd][0]
146
+				
147
+				# positionnement de la copie, aleatoirement, entre 0 et la largeur totale de l'image 
148
+				randomCopyPosi = random.randint(0, (im1.size[0]-largeur))
149
+				#randomCopyPosi = seq.randomCopyPosi
150
+				seq.randomCopyPosi.append(randomCopyPosi)
151
+
152
+			i+=1
153
+			
154
+			# tirage au sort
155
+			#seq["choix"].append(choix_rnd)
156
+
157
+			# assignation des valeurs (paires) finales choisies
158
+			largeur = proportions[choix_rnd][0]			
159
+			repeat = proportions[choix_rnd][1]
160
+
161
+			#seq["randomCopyPosi"].append(randomCopyPosi)
162
+
163
+			# pixellisation : choix de la taille du pixel = pixelSize
164
+			#pixelSizeList = [1,1,1,1,1,1,1,1,4,8]
165
+			pixelSizeList = [1,2,4,8]
166
+			pixelSizeIndex = random.randint(0,len(pixelSizeList)-1)
167
+			pixelSize = pixelSizeList[pixelSizeIndex]
168
+			
169
+			cropfinal = [largeur,bas-haut]
170
+			hauteur = bas-haut
171
+			'''
172
+			haut gauche(0,0).
173
+			haut droit (largeur-1 , 0).
174
+			bas gauche (0 , hauteur-1).
175
+			bas droit (largeur-1, hauteur-1)
176
+			'''
177
+			
178
+			if largeur % pixelSize != 0:
179
+				croop = largeur / pixelSize
180
+				largeur = (int(croop) + 1 ) * pixelSize
181
+				
182
+			if  hauteur % pixelSize != 0:
183
+				croop2 = hauteur / pixelSize
184
+				hauteur = (int(croop2) + 1 ) * pixelSize
185
+				
186
+			# decoupage du sample
187
+			im5 = im2.crop((randomCopyPosi,haut,randomCopyPosi+largeur,haut+hauteur))
188
+			
189
+			'''COLLAGE DU SAMPLE '''
190
+			loop = 0
191
+			while loop<repeat:	
192
+				px1 = n
193
+				px2 = n + largeur
194
+#				draw = ImageDraw.Draw(im3)
195
+				
196
+				''' PIXELISATION'''
197
+				im5 = im5.resize((im3.size[0]/pixelSize, im3.size[1]/pixelSize), Image.NEAREST)
198
+				im5 = im5.resize((im3.size[0]*pixelSize, im3.size[1]*pixelSize), Image.NEAREST)
199
+				im3 = im5.crop(0,0,cropfinal[0]-1,cropfinal[1]-1)
200
+				
201
+				#lignes colorées de 1 pix
202
+				draw.line((im3.size[0]-1, 0, im3.size[0]-1, im3.size[1]-1), fill="rgb(255,255,255)")
203
+				im4.paste(im3, (px1, haut, px2, bas))
204
+				
205
+				n = n + largeur
206
+				loop = loop + 1
207
+	
208
+	''' OPTIONS'''	
209
+	def TrancheHorizontale() :
210
+		# tirage au hasard de la bande copiee
211
+		pos = random.randint(0, im1.size[1]-im1.size[1]/20)
212
+		# copiage
213
+		im5 = im2.crop((0,pos,im1.size[0],pos+im1.size[1]/20))
214
+		
215
+		# le soulignage en blanc
216
+		draw = ImageDraw.Draw(im5)
217
+		draw.line((0, im5.size[1]-1, im5.size[0], im5.size[1]-1), fill="black")
218
+		draw.line((0, 1, im5.size[0], 1), fill="black")
219
+		
220
+		# collage	
221
+		im4.paste(im5, (0,pos,im1.size[0],pos+im1.size[1]/20))
222
+
223
+	# HAACHEUUR
224
+	for j in range(len(randomCoupeHauteur)-1):
225
+		Hacheur(randomCoupeHauteur[j], randomCoupeHauteur[j+1])
226
+	
227
+	''' SAUVEGARDE	
228
+	'''
229
+	
230
+	# CTRL + S
231
+	#chemin du script	: scriptpy = sys.argv[0]
232
+	#chemin de l'image	: str(sys.argv[1])
233
+	scriptpy = str(sys.argv[1])
234
+	script = scriptpy[:-3]
235
+	n = "%05d" % i
236
+	im4.save(script+"."+n+"_"+strftime("%Y%m%d-%Hh%Mm%Ss", gmtime())+".png",'PNG', quality=100)
237
+	
238
+	#pickle.dump(seq, open("test.pickle","w"))
239
+	pickle.dump(seq, open(script+"."+n+"_"+strftime("%Y%m%d-%Hh%Mm%Ss", gmtime())+".pickle","w"))
240
+	#print script+"."+str(i)+"_"+strftime("%Y%m%d-%Hh%Mm%Ss", gmtime())+".jpg"
241
+	#millis = int(round(time.time() * 1000))
242
+	#print "millis-secondes : "+str(millis)
243
+
244
+
245
+
246
+#obtenir les valeurs RGB des pixels de l'image
247
+'''
248
+from PIL import Image
249
+
250
+im = Image.open('test.png')
251
+pixels = list(im.getdata())
252
+width, height = im.size
253
+
254
+pixels = [pixels[i * width:(i + 1) * width] for i in xrange(height)]
255
+
256
+for r in range(0, height-⁠1) :
257
+        print "pixels = "+str(pixels[r])
258
+
259
+print " "
260
+print "len(pixels) = "+str(len(pixels))
261
+print "width = "+str(width)
262
+print "height = "+str(height)
263
+print " "
264
+
265
+#tintin = im.tostring( "raw", "RGBA", 0, -⁠1)
266
+#print "tostring = "+tintin
267
+'''
... ...
@@ -34,22 +34,12 @@ allongement = 2
34 34
 #im1 = Image.open(str(sys.argv[1])).convert('L')
35 35
 im1 = Image.open(str(sys.argv[1]))
36 36
 im2 = im1.copy()
37
-im3 = Image.new("RGBA",(im1.size[0], im1.size[1]))
37
+
38 38
 im4 = Image.new("RGBA",(im1.size[0]*allongement, im1.size[1]))
39
+im5 = Image.new("RGBA",(im1.size[0]*allongement, im1.size[1]))
39 40
 
40 41
 boutImage = Image.new("RGBA",(im1.size[0], im1.size[1]))
41 42
 
42
-pix2 	=	 im3.resize((im3.size[0]/2, 	im3.size[1]/2), 	Image.NEAREST)
43
-pix4 	=	 im3.resize((im3.size[0]/4, 	im3.size[1]/4), 	Image.NEAREST)
44
-pix8 	=	 im3.resize((im3.size[0]/8, 	im3.size[1]/8), 	Image.NEAREST)
45
-pix16 	=	 im3.resize((im3.size[0]/16, 	im3.size[1]/16),	Image.NEAREST)
46