Naive Generator

A generator that implemented based on scipy.ndimage
import numpy as np
import scipy.ndimage
import matplotlib.pyplot as plt
import time

Generating a latent space representation

To make it more “understandable” where material is placed we generate a slowly varying landscape by interpolation of a low resolution map.

features = 8
zoom = 16
heatmap = np.random.random((features,features))-0.5
large_heatmap = scipy.ndimage.zoom(heatmap, zoom)
plt.imshow(large_heatmap)
plt.xlabel("x")
plt.ylabel("y")
plt.grid()

Generating the brush

kernel_size=9
brush = np.ones((kernel_size, kernel_size))
brush[0,0] = 0
brush[0,-1] = 0
brush[-1,0] = 0
brush[-1,-1] = 0

nx, ny = brush.shape

plt.imshow(brush)
ax = plt.gca()
ax.set_yticks(np.arange(nx)+0.5)
ax.set_yticklabels(["" for i in range(nx)])
ax.set_xticks(np.arange(ny)+0.5)
ax.set_xticklabels(["" for i in range(ny)])
ax.set_yticks(np.arange(nx), minor=True)
ax.set_yticklabels([f"{i}" for i in range(nx)], minor=True)
ax.set_xticks(np.arange(ny), minor=True)
ax.set_xticklabels([f"{i}" for i in range(ny)], minor=True)
plt.grid()

Running the Generator

def dilate(img, brush):
    global time_dilate
    time_dilate -= time.process_time() 
    dil = scipy.ndimage.morphology.binary_dilation(img, brush)
    time_dilate += time.process_time() 
    return dil

def existing_pixels(touches, brush):
    return dilate(touches, brush)

def impossible_touches(existing_other, brush):
    return dilate(existing_other, brush)

def valid_touches(impossible, touches):
    return np.logical_and(np.logical_not(impossible), np.logical_not(touches))

def possible_pixels(valid, touches, brush):
    possible_touches = np.logical_or(touches, valid)
    return dilate(possible_touches, brush)

def required_pixels(existing, possible_other):
    return np.logical_and(np.logical_not(existing), np.logical_not(possible_other))

def resolving_touches(required, valid, brush):
    return np.logical_and(dilate(required, brush), valid)

def free_touches(possible_other, existing_other, valid, brush):
    dilated = dilate(np.logical_or(possible_other, existing_other), brush)
    return np.logical_and(np.logical_not(dilated), valid)

def select_single(s_valid, v_valid, s_suggest, v_suggest, brush, t_s, t_v):
    s_weights = scipy.ndimage.convolve(s_suggest, brush)
    v_weights = -scipy.ndimage.convolve(v_suggest, brush)

    s_weights[np.logical_not(s_valid)] = np.nan
    v_weights[np.logical_not(v_valid)] = np.nan

    max_s = max_v = -np.inf
    if s_valid.any():
        max_pos_s = np.nanargmax(s_weights)
        max_s = s_weights.flat[max_pos_s]
    
    if v_valid.any():
        max_pos_v = np.nanargmax(v_weights)
        max_v = v_weights.flat[max_pos_v]

    if  max_s > max_v:
        t_s.flat[max_pos_s] = True
    else:
        t_v.flat[max_pos_v] = True

    return t_s, t_v

s_suggest = large_heatmap.copy()
v_suggest = large_heatmap.copy()
t_s = np.zeros_like(s_suggest).astype(bool)
t_v = t_s.copy()

time_update = 0
time_select = 0 
time_dilate = 0
time_existing = 0
time_impossible = 0
time_valid = 0
time_possible = 0
time_required = 0
time_resolving = 0
time_free = 0

debug = True
def log(*args):
  if debug:
    print(*args)

for i in range(10000):
    time_update -= time.process_time() 

    time_existing -= time.process_time() 
    p_s_existing = existing_pixels(t_s, brush)
    p_v_existing = existing_pixels(t_v, brush)
    time_existing += time.process_time() 

    s_suggest[p_s_existing] = 0
    v_suggest[p_v_existing] = 0

    time_impossible -= time.process_time() 
    t_s_impossible = impossible_touches(p_v_existing, brush)
    t_v_impossible = impossible_touches(p_s_existing, brush)
    time_impossible += time.process_time() 

    time_valid -= time.process_time() 
    t_s_valid = valid_touches(t_s_impossible, t_s)
    t_v_valid = valid_touches(t_v_impossible, t_v)
    time_valid += time.process_time() 

    time_possible -= time.process_time() 
    p_s_possible = possible_pixels(t_s_valid,t_s,brush)
    p_v_possible = possible_pixels(t_v_valid,t_v,brush)
    time_possible += time.process_time() 

    time_required -= time.process_time() 
    p_s_required = required_pixels(p_s_existing, p_v_possible)
    p_v_required = required_pixels(p_v_existing, p_s_possible)
    time_required += time.process_time() 

    time_resolving -= time.process_time() 
    t_s_resolving = resolving_touches(p_s_required, t_s_valid, brush)
    t_v_resolving = resolving_touches(p_v_required, t_v_valid, brush)
    time_resolving += time.process_time() 

    time_free -= time.process_time() 
    t_s_free = free_touches(p_v_possible, p_v_existing, t_s_valid, brush)
    t_v_free = free_touches(p_s_possible, p_s_existing, t_v_valid, brush)
    time_free += time.process_time() 

    time_update += time.process_time() 
    time_select -= time.process_time() 

    if t_s_free.any() or t_v_free.any():
        log(f"{i}: free")
        t_s = np.logical_or(t_s, t_s_free)
        t_v = np.logical_or(t_v, t_v_free)
    elif t_s_resolving.any() or t_v_resolving.any():
        log(f"{i}: resolving")
        t_s, t_v = select_single(t_s_resolving, t_v_resolving, s_suggest, v_suggest, brush, t_s, t_v)
    elif t_s_valid.any() or t_v_valid.any():
        log(f"{i}: valid")
        t_s, t_v = select_single(t_s_valid, t_v_valid, s_suggest, v_suggest, brush, t_s, t_v)
    else:
        print("finished")
        time_select += time.process_time() 
        break

    time_select += time.process_time() 


plt.figure(figsize = (6,9))
plt.subplot(321)
plt.title("Solid")
plt.imshow(p_s_existing)
plt.subplot(322)
plt.title("Void")
plt.imshow(p_v_existing)
plt.subplot(323)
plt.title("Solid Touches")
plt.imshow(t_s)
plt.subplot(324)
plt.title("Void Touches")
plt.imshow(t_v)
plt.subplot(325)
plt.imshow(large_heatmap)
/tmp/ipykernel_3808/3885273807.py:4: DeprecationWarning: Please use `binary_dilation` from the `scipy.ndimage` namespace, the `scipy.ndimage.morphology` namespace is deprecated.
  dil = scipy.ndimage.morphology.binary_dilation(img, brush)
0: valid
1: valid
2: valid
3: free
4: valid
5: valid
6: resolving
7: free
8: valid
9: valid
10: resolving
11: free
12: valid
13: resolving
14: free
15: valid
16: valid
17: free
18: valid
19: resolving
20: free
21: valid
22: resolving
23: free
24: valid
25: free
26: valid
27: valid
28: resolving
29: free
30: valid
31: resolving
32: free
33: valid
34: resolving
35: free
36: valid
37: valid
38: resolving
39: free
40: valid
41: free
42: resolving
43: free
44: valid
45: resolving
46: free
47: valid
48: resolving
49: free
50: valid
51: free
52: valid
53: resolving
54: free
55: valid
56: valid
57: free
58: resolving
59: free
60: resolving
61: free
62: resolving
63: free
64: resolving
65: free
66: valid
67: free
68: valid
69: valid
70: free
71: valid
72: free
73: valid
74: valid
75: free
76: valid
77: free
78: valid
79: free
80: valid
81: free
82: valid
83: free
84: resolving
85: free
86: valid
87: free
88: resolving
89: free
90: valid
91: valid
92: resolving
93: free
94: valid
95: valid
96: resolving
97: free
98: valid
99: valid
100: valid
101: free
102: valid
103: valid
104: resolving
105: free
106: valid
107: resolving
108: free
109: resolving
110: free
111: valid
112: free
113: valid
114: free
115: resolving
116: free
117: valid
118: free
119: valid
120: free
121: valid
122: free
123: valid
124: valid
125: resolving
126: free
127: valid
128: free
129: valid
130: valid
131: free
132: valid
133: free
134: resolving
135: valid
136: free
137: valid
138: valid
139: free
140: resolving
141: free
142: valid
143: free
144: resolving
145: free
146: valid
147: resolving
148: free
149: resolving
150: free
151: valid
152: resolving
153: free
154: resolving
155: free
156: resolving
157: free
158: valid
159: free
160: resolving
161: free
162: resolving
163: free
164: valid
165: free
166: resolving
167: free
168: valid
169: valid
170: resolving
171: free
172: valid
173: free
174: valid
175: free
176: valid
177: valid
178: free
179: valid
180: valid
181: free
182: valid
183: valid
184: free
185: valid
186: resolving
187: free
188: valid
189: free
190: valid
191: free
192: resolving
193: free
194: resolving
195: free
196: valid
197: free
198: valid
199: free
200: valid
201: free
202: valid
203: free
204: resolving
205: free
206: resolving
207: free
208: resolving
209: free
210: valid
211: resolving
212: free
213: resolving
214: free
215: valid
216: free
217: valid
218: free
219: valid
220: free
221: resolving
222: resolving
223: free
224: valid
225: valid
226: free
227: valid
228: free
229: valid
230: free
231: valid
232: free
233: valid
234: free
235: resolving
236: free
237: valid
238: free
239: valid
240: free
241: valid
242: free
243: valid
244: free
245: valid
246: free
247: valid
248: free
249: valid
250: free
251: resolving
252: free
253: resolving
254: free
255: resolving
256: free
257: resolving
258: free
259: valid
260: free
261: valid
262: free
263: valid
264: free
265: valid
266: free
267: valid
268: free
269: valid
270: resolving
271: free
272: resolving
273: free
274: resolving
275: resolving
276: free
277: resolving
278: free
279: valid
280: free
281: valid
282: free
283: valid
284: free
285: valid
286: free
287: valid
288: free
289: valid
290: free
291: valid
292: free
293: valid
294: free
295: resolving
296: free
297: resolving
298: free
299: valid
300: free
301: valid
302: free
303: valid
304: free
305: valid
306: free
307: valid
308: free
309: resolving
310: free
311: resolving
312: free
313: valid
314: free
315: valid
316: free
317: valid
318: free
319: valid
320: free
321: valid
322: free
323: valid
324: free
325: valid
326: free
327: valid
328: free
329: valid
330: free
331: valid
332: free
333: valid
334: free
335: valid
336: free
337: valid
338: free
339: valid
340: free
341: valid
342: free
343: valid
344: free
345: valid
346: free
347: valid
348: free
349: valid
350: free
351: valid
352: free
353: valid
354: free
355: valid
356: free
357: valid
358: free
359: valid
360: free
361: valid
362: free
363: valid
364: free
365: valid
366: free
367: valid
368: free
369: valid
370: free
371: valid
372: free
373: valid
374: free
375: valid
376: free
377: valid
378: free
379: valid
380: free
381: valid
382: free
383: valid
384: free
385: valid
386: free
387: valid
388: free
389: valid
390: free
391: valid
392: free
393: valid
394: free
395: valid
396: free
397: valid
398: free
399: valid
400: free
401: valid
402: free
403: valid
404: free
405: valid
406: free
407: valid
408: free
409: valid
410: free
411: valid
412: free
413: valid
414: free
415: valid
416: free
417: valid
418: free
419: valid
420: free
421: valid
422: free
423: valid
424: free
425: valid
426: free
427: valid
428: free
429: valid
430: free
431: valid
432: free
433: valid
434: free
435: valid
436: free
437: valid
438: free
439: valid
440: free
441: valid
442: free
443: valid
444: free
445: valid
446: free
447: valid
448: free
449: valid
450: free
451: valid
452: free
453: valid
454: free
455: valid
456: free
457: valid
458: free
459: valid
460: free
461: valid
462: free
463: valid
464: free
465: valid
466: valid
467: free
468: valid
469: free
470: valid
471: free
472: valid
473: free
474: valid
475: free
476: valid
477: free
478: valid
479: free
480: valid
481: valid
482: free
483: valid
484: free
485: valid
486: valid
487: free
488: valid
489: free
490: valid
491: free
492: valid
493: free
494: valid
495: free
496: valid
497: free
498: valid
499: free
500: valid
501: free
502: valid
503: free
504: valid
505: free
506: valid
507: free
508: valid
509: free
510: valid
511: valid
512: free
513: valid
514: free
515: valid
516: free
517: valid
518: free
519: valid
520: free
521: valid
522: free
523: valid
524: free
525: valid
526: free
527: valid
528: free
529: valid
530: free
531: valid
532: free
533: valid
534: valid
535: free
536: valid
537: free
538: valid
539: free
540: valid
541: valid
542: free
543: valid
544: free
545: valid
546: free
547: valid
548: free
549: valid
550: free
551: valid
552: free
553: valid
554: free
555: valid
556: free
557: valid
558: free
559: valid
560: free
561: valid
562: free
563: valid
564: free
565: valid
566: free
567: valid
568: free
569: valid
570: free
571: valid
572: free
573: valid
574: free
575: valid
576: free
577: valid
578: free
579: valid
580: free
581: valid
582: free
583: valid
584: valid
585: free
586: valid
587: valid
588: free
589: valid
590: valid
591: free
592: valid
593: free
594: valid
595: valid
596: free
597: valid
598: valid
599: valid
600: free
601: valid
602: valid
603: free
604: valid
605: free
606: valid
607: free
608: valid
609: free
610: valid
611: valid
612: valid
613: free
614: valid
615: valid
616: valid
617: valid
618: valid
finished
<matplotlib.image.AxesImage at 0x7f7f283cd100>

print(f"""
{time_update = }
{time_select = }
{time_dilate = }
{time_existing = }
{time_impossible = }
{time_valid = }
{time_possible = }
{time_required = }
{time_resolving = }
{time_free = }""")

time_update = 9.158790900000017
time_select = 1.343904300000009
time_dilate = 9.031094400000002
time_existing = 2.179708000000021
time_impossible = 1.7292600000000053
time_valid = 0.016886099999984694
time_possible = 1.3348817000000164
time_required = 0.014584900000011558
time_resolving = 2.914022299999994
time_free = 0.9344202000000212