1if not modules then modules = { } end modules ['mlib-ran'] = {
2 version = 1.001,
3 comment = "companion to mlib-ctx.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files",
7}
8
9local next = next
10local ceil, floor, random, sqrt, cos, sin, pi, max, min = math.ceil, math.floor, math.random, math.sqrt, math.cos, math.sin, math.pi, math.min, math.max
11local remove = table.remove
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34local function poisson(width, height, mindist, newpointscount, initialx, initialy)
35 local starttime = os.clock()
36 local cellsize = mindist / sqrt(2)
37 local nofwidth = ceil(width // cellsize)
38 local nofheight = ceil(height // cellsize)
39 local grid = lua.newtable(nofwidth,0)
40 local firstx = initialx or random() * width
41 local firsty = initialy or random() * height
42 local firstpoint = { firstx, firsty, 1 }
43
44 local processlist = { firstpoint }
45 local nofprocesslist = 1
46 local nofsamplepoints = 1
47 local twopi = 2 * pi
48
49 for i=1,nofwidth do
50 local g = lua.newindex(nofheight,false)
51 grid[i] = g
52 end
53
54 local x = floor(firstx // cellsize) + 1
55 local y = floor(firsty // cellsize) + 1
56
57 x = max(1, min(x, width - 1))
58 y = max(1, min(y, height - 1))
59
60 grid[x][y] = firstpoint
61
62
63
64
65
66
67
68
69 while nofprocesslist > 0 do
70 local point = remove(processlist,random(1,nofprocesslist))
71 nofprocesslist = nofprocesslist - 1
72 for i=1,newpointscount do
73 local radius = mindist * (random() + 1)
74 local angle = twopi * random()
75 local nx = point[1] + radius * cos(angle)
76 local ny = point[2] + radius * sin(angle)
77 if nx > 1 and ny > 1 and nx <= width and ny <= height then
78 local gx = floor(nx // cellsize)
79 local gy = floor(ny // cellsize)
80
81 for i=-2,2 do
82 for j=-2,2 do
83 local cell = grid[i + gx]
84 if cell then
85 cell = cell[j + gy]
86 if cell and sqrt((cell[1] - nx)^2 + (cell[2] - ny)^2) < mindist then
87 goto next
88 end
89 end
90 end
91 end
92
93 nofprocesslist = nofprocesslist + 1
94 nofsamplepoints = nofsamplepoints + 1
95 local newpoint = { nx, ny, nofsamplepoints }
96 processlist [nofprocesslist] = newpoint
97
98 grid[gx][gy] = newpoint
99 end
100 ::next::
101 end
102 end
103
104 return {
105 count = nofsamplepoints,
106
107 grid = grid,
108 time = os.clock() - starttime,
109 }
110end
111
112
113
114local randomizers = utilities.randomizers or { }
115utilities.randomizers = randomizers
116randomizers.poisson = poisson
117
118
119
120local formatters = string.formatters
121local concat = table.concat
122
123local f_macro = formatters["%s(%N,%N);"]
124
125local f_macros = {
126 [2] = formatters["%s(%N,%N);"],
127 [3] = formatters["%s(%N,%N,%i);"],
128 [4] = formatters["%s(%N,%N,%i,%i);"],
129}
130
131local function grid_to_mp(t,f,n)
132 local grid = t.grid
133 local count = t.count
134 local result = { }
135 local r = 0
136 local macro = f or "draw"
137 local runner = f_macros[n or 2] or f_macros[2]
138 for i=1,#grid do
139 local g = grid[i]
140 if g then
141 for j=1,#g do
142 local v = g[j]
143 if v then
144 r = r + 1
145 result[r] = runner(macro,v[1],v[2],v[3],count)
146 end
147 end
148 end
149 end
150 return concat(result, " ")
151end
152
153local getparameter = metapost.getparameter
154
155local function lmt_poisson()
156 local initialx = getparameter { "initialx" }
157 local initialy = getparameter { "initialy" }
158 local width = getparameter { "width" }
159 local height = getparameter { "height" }
160 local distance = getparameter { "distance" }
161 local count = getparameter { "count" }
162
163 local result = poisson (
164 width, height, distance, count,
165 initialx > 0 and initialx or false,
166 initialy > 0 and initialy or false
167 )
168
169 if result then
170 logs.report("poisson","w=%N, h=%N, d=%N, c=%N, n=%i, runtime %.3f",
171 width, height, distance, count, result.count, result.time
172 )
173 end
174
175 return result
176end
177
178function mp.lmt_poisson_generate()
179 local result = lmt_poisson()
180 if result then
181 return grid_to_mp (
182 result,
183 getparameter { "macro" },
184 getparameter { "arguments" }
185 )
186 end
187end
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238 |