David Lambert/Art

From J Wiki
Jump to navigation Jump to search
Screen capture

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 _