|
189 | 189 |
|
190 | 190 | ;; ### Getting indices of Neighbours |
191 | 191 | ;; |
192 | | -;; The following function determines the neighbouring indices of a cell.recursing over each dimension. |
| 192 | +;; The following function determines the neighbouring indices of a cell recursing over each dimension. |
193 | 193 | (defn neighbours |
194 | 194 | [& args] |
195 | 195 | (if (seq args) |
196 | 196 | (mapcat (fn [v] (map (fn [delta] (into [(+ (first args) delta)] v)) [-1 0 1])) |
197 | 197 | (apply neighbours (rest args)) ) |
198 | 198 | [[]])) |
199 | 199 |
|
200 | | - |
201 | 200 | (facts "Get neighbouring indices" |
202 | 201 | (neighbours) => [[]] |
203 | 202 | (neighbours 0) => [[-1] [0] [1]] |
204 | 203 | (neighbours 3) => [[2] [3] [4]] |
205 | 204 | (neighbours 1 10) => [[0 9] [1 9] [2 9] [0 10] [1 10] [2 10] [0 11] [1 11] [2 11]]) |
206 | 205 |
|
207 | 206 |
|
| 207 | +;; ### Sampling Worley noise |
| 208 | +;; Using above functions one can now implement Worley noise. |
| 209 | +;; For each pixel the distance to the closest seed point is calculated. |
| 210 | +;; The distance to each random point in all neighbouring cells is calculated and the minimum is taken. |
208 | 211 | (defn worley-noise |
209 | 212 | [{:keys [size dimensions] :as params}] |
210 | 213 | (let [random-points (random-points params)] |
|
220 | 223 | (apply wrap-get random-points neighbour)))))) |
221 | 224 | :double)))) |
222 | 225 |
|
223 | | - |
| 226 | +;; Here a 256x256 Worley noise tensor is created. |
224 | 227 | (def worley (worley-noise (make-noise-params 256 8 2))) |
225 | 228 |
|
| 229 | +;; The values are normalised to be between 0 and 255. |
226 | 230 | (def worley-norm |
227 | 231 | (dfn/* (/ 255 (- (dfn/reduce-max worley) (dfn/reduce-min worley))) |
228 | 232 | (dfn/- (dfn/reduce-max worley) worley))) |
229 | 233 |
|
| 234 | +;; Finally one can display the noise. |
230 | 235 | (bufimg/tensor->image worley-norm) |
231 | 236 |
|
232 | 237 | ;; ## Perlin noise |
233 | | -;; https://adrianb.io/2014/08/09/perlinnoise.html |
| 238 | +;; |
| 239 | +;; [Perlin noise](https://adrianb.io/2014/08/09/perlinnoise.html) is generated by choosing a random gradient vector at each cell corner. |
| 240 | +;; The noise tensor's intermediate values are interpolated with a continuous function, utilizing the gradient at the corner points. |
234 | 241 |
|
| 242 | +;; ### Random gradients |
| 243 | +;; |
| 244 | +;; The 2D or 3D gradients are generated by creating a vector where each component is set to a random number between -1 and 1. |
| 245 | +;; Random vectors are generated until the vector length is greater 0 and lower or equal to 1. |
| 246 | +;; The vector then is normalized and returned. |
| 247 | +;; Random vectors outside the unit circle or sphere are discarded in order to achieve a uniform distribution on the surface of the unit circle or sphere. |
235 | 248 | (defn random-gradient |
236 | 249 | [& args] |
237 | 250 | (loop [args args] |
|
241 | 254 | (div random-vector vector-length) |
242 | 255 | (recur args))))) |
243 | 256 |
|
244 | | - |
| 257 | +;; The function below serves as a Midje checker for a vector with an approximate expected value. |
245 | 258 | (defn roughly-vec |
246 | 259 | [expected error] |
247 | 260 | (fn [actual] |
248 | 261 | (<= (mag (sub actual expected)) error))) |
249 | 262 |
|
250 | | - |
| 263 | +;; In the following tests, the random function is again replaced with a deterministic function. |
251 | 264 | (facts "Create unit vector with random direction" |
252 | 265 | (with-redefs [rand (constantly 0.5)] |
253 | 266 | (random-gradient 0 0) |
|
256 | 269 | (random-gradient 0 0) |
257 | 270 | => (roughly-vec (vec2 (sqrt 0.5) (sqrt 0.5)) 1e-6))) |
258 | 271 |
|
259 | | - |
| 272 | +;; The random gradient function is then used to generate a field of random gradients. |
260 | 273 | (defn random-gradients |
261 | 274 | [{:keys [divisions dimensions]}] |
262 | 275 | (tensor/clone (tensor/compute-tensor (repeat dimensions divisions) random-gradient))) |
263 | 276 |
|
264 | | - |
| 277 | +;; The function is tested that it generates 2D and 3D random gradient fields correctly. |
265 | 278 | (facts "Random gradients" |
266 | 279 | (with-redefs [rand (constantly 1.5)] |
267 | 280 | (dtype/shape (random-gradients {:divisions 8 :dimensions 2})) |
|
272 | 285 | ((random-gradients {:divisions 8 :dimensions 3}) 0 0 0) |
273 | 286 | => (vec3 (/ 1 (sqrt 3)) (/ 1 (sqrt 3)) (/ 1 (sqrt 3))))) |
274 | 287 |
|
275 | | - |
| 288 | +;; The gradient field can be plotted with Plotly as a scatter plot of disconnected lines/ |
276 | 289 | (let [gradients (tensor/reshape (random-gradients (make-noise-params 256 8 2)) |
277 | 290 | [(* 8 8)]) |
278 | 291 | points (tensor/reshape (tensor/compute-tensor [8 8] (fn [y x] (vec2 x y))) |
|
0 commit comments