David Lambert/Art
Jump to navigation
Jump to search
The defaults draw 1024 by 1024 pixel grid.
User Notes
In the current draft, the canvas to be painted is created (but left blank) when this script is loaded.
If you are working on a small screen, you can cut the size of that canvas in half in each direction by changing the line assigning DEFAULTS_z_ to:
DEFAULTS_z_=: 8 6
To paint the canvas, you would run diffuse with an iteration count. An iteration count with a power of 2 argument may be nice for initial experimentation:
diffuse 2^0
diffuse 2^1
diffuse 2^2
diffuse 2^3
...
1NB. Engine: j9.4.1
2
3TITLE=: 'Diffusion with sources.'
4
5Note'description'
6
7 1st, this my 1st use of forms, and 1st threaded application.
8
9 Goal: display the beauty of diffusion.
10
11 Method: solve the transient 2d diffusion equation with moving sources
12 and sinks independently for R, G, and B. Periodically combine these
13 for display.
14
15 Use: modify to your delight,
16 diffuse ITERATIONS
17
18
19 Using 3x3 weighted averaging kernel on equi-size rectilinear grids,
20 the RG and B colors represent three species. These have independent
21 sources and sinks. They can move to keep the display interesting
22 over time. The solution runs some number of iterations between
23 rendering. (Not in this version: The per-specie iteration count
24 controls the diffusion rate.) Multigrid method accelerates the
25 computation. The colors diffuse in separate threads. With the hope
26 that they need to copy less information, they are independent
27 objects.
28
29 Control variables (UPPERCASE):
30
31 INITIAL_SIZE is the edge size of the smallest grid.
32 DOUBLINGS specifies the number of times that small grid doubles in
33 size
34
35 The display viewport in pixels is INITIAL_SIZE * 2 ^ DOUBLINGS
36
37 Verbs:
38 diffuse runs the model
39
40 Performance:
41 Thinkpad W540 3 threads 1.5 seconds / iteration
42 Thinkpad single thread 3 seconds / iteration
43 RPi3 9 seconds / iteration
44
45 Multigrid, which gets to the point much faster.
46 W540 with color objects 1.3 seconds / iteration
47)
48
49coclass'base'
50
51GLOBALS_z_ =: 'INITIAL_SIZE DOUBLINGS'
52DEFAULTS_z_=: 16 6
53
54(;: GLOBALS)=: DEFAULTS
55
56DISPLAY=: INITIAL_SIZE * 2 ^ DOUBLINGS
57
58coclass'diffuse' NB. computational section
59
60create=: {{
61 g=. ;: GLOBALS
62 (g)=: DEFAULTS
63 a=. y <.&:# g
64 (a {. g)=: a {. y
65 reset''
66 bcs 2
67 EMPTY
68}}
69
70reset_intended=: {{
71 GRID=: 0 ?@:$~ ,~ INITIAL_SIZE NB. values on [0,1)
72}}
73pretty_reset_blunder=: {{ NB. dropped the 0 somewhere.
74 GRID=: ?@:$~ ,~ INITIAL_SIZE NB. more fun.
75}}
76reset=: pretty_reset_blunder
77
78mp=: +/ .*
79
80'o s c'=: 0 , ^ - %: 1 2 NB. weights for center, side, corner
81
82KERNEL=: (% +/)c,s,c,s,o,s,c,s,c
83KTOP=: (% +/)s,o,s,+:c,s,c
84KBOT=: (% +/)(+:c,s,c),s,o,s
85KLFT=: (% +/)(s,c,o,s,s,c)*6$1 2
86KRHT=: (% +/)(c,s,s,o,c,s)*6$2 1
87
88center=: (1,:3 3) KERNEL&mp@:,;._3 ]
89top_edge=: [: , (0 1,:2 3) KTOP&mp@:,;._3 ]
90bot_edge=: [: , (0 1,:2 3) KBOT&mp@:,;._3 (_2&{.)
91lft_edge=: [: , (1 0,:3 2) KLFT&mp@:,;._3 ]
92rht_edge=: [: , (1 0,:3 2) KRHT&mp@:,;._3 (_ _2&{.)
93tl=: (KERNEL mp ([: , (,~ {:)@:|:@:(,~ {:)))@:(2 2&{.)
94tr=: (KERNEL mp ([: , (, {.)@:|:@:(,~ {:)))@:(2 _2&{.)
95bl=: (KERNEL mp ([: , (,~ {:)@:|:@:(, {.)))@:(_2 2&{.)
96br=: (KERNEL mp ([: , (, {.)@:|:@:(, {.)))@:(_2 _2&{.)
97
98relax=: (tl , top_edge , tr) , (lft_edge ,. center ,. rht_edge) , (bl , bot_edge , br)
99
100bcs=: {{ NB. y is the number of sources
101 I=: INITIAL_SIZE ?@:$~ y , 2 NB. location
102 NB. wild effects if T exceeds 1.
103 NB. 0 is a sink
104 T=: 2 ?@:$~ y NB. temperature.
105 EMPTY
106}}
107
108solve=: {{
109 GRID=: relax T I} GRID NB. apply boundary condition
110 relax@:(2 # 2 #"1 ])^:DOUBLINGS GRID
111}}
112
113
114
115coclass'base' NB. display section.
116
117ForeignTime=: 6!:
118ymdhms=: 0 ForeignTime
119sessionTime=: 1 ForeignTime
120
121require'stats'
122require'gl2'
123coinsert'jgl2'
124
125ymdhms=: 6!:0
126
127
128NB. left top corner offsets
129NB. (rightward,downward) paintPixelMatrix (ARGB matrix)
130NB. glpaintx pushes the data to the display.
131paintPixelMatrixx=: 1 1&$: :(glpaintx@:glpixels@:(, (|.@:$ , ,)))
132
133wd rplc&('DIMS';":2#DISPLAY) (0 :0)
134 pc "Diffusion" closeok closebutton;
135 minwh DIMS;
136 cc canvas isidraw;
137 pshow;
138)
139
140THREAD_POOL=: -. IFRASPI
141{{ 0 T. THREAD_POOL }}^:] 3 * -. IFRASPI NB. thread for each color
142
143diffuse=: {{ NB. diffuse ITERATIONS
144 R=: ''conew'diffuse'
145 G=: ''conew'diffuse'
146 B=: ''conew'diffuse'
147 START=: sessionTime''
148 i=. 0
149 whilst. i < y do.
150 rgb=. 1 | > (solve__R t. THREAD_POOL'') , (solve__B t. THREAD_POOL'') , (solve__G t. THREAD_POOL'')
151
152 NB. could relax each color individually after lowering resolution.
153 NB. relaxing the single matrix messes with the colors.
154 NB. saturation with high bit set is negative on Raspberry Pi, hence 127.
155 NB. The effect is strong.
156
157 argb=: 256 #. (255 <. 127) ,"1 <. 255 * 1 2 0 |: rgb
158
159 paintPixelMatrixx (relax^:0) argb
160
161 if. 0 = 10 | i do. NB. when gray, reset
162 s=. stddev , rgb
163 NB. echo i, s
164 if. 0.17 > s do. NB. threshhold tied to pretty_reset_blunder
165 reset__R reset__G reset__B ''
166 end.
167 end.
168
169 bcs__R@:4:^:(0.02&>) ? 0 NB. was 0.016&>
170 bcs__G@:4:^:(0.02&>) ? 0
171 bcs__B@:4:^:(0.02&>) ? 0
172
173 i=. >: i
174
175 end.
176 (START -~ sessionTime)''
177}}
178
179NB. diffuse _