Skip to content

Commit 8c0cf70

Browse files
committed
voronoi cell coloring mode
1 parent f0fb959 commit 8c0cf70

File tree

6 files changed

+109
-21
lines changed

6 files changed

+109
-21
lines changed

cells.png

31.3 KB
Loading

cells3D.png

33.2 KB
Loading

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
![](super_simplex3D.png)
3737
![](voronoi3D.png)
38+
![](cells3D.png)
3839
![](poisson.png)
3940

4041
### A note on building

sources/noise/cell.swift

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct CellNoise2D:Noise
3434
}
3535

3636
public
37-
func evaluate(_ x:Double, _ y:Double) -> Double
37+
func closest_point(_ x:Double, _ y:Double) -> (point:(Int, Int), r2:Double)
3838
{
3939
let sample:Math.DoubleV2 = (x * self.frequency, y * self.frequency)
4040

@@ -80,14 +80,20 @@ struct CellNoise2D:Noise
8080
let nearpoint_disp:Math.DoubleV2 = (abs(sample_rel.x - Double((quadrant.a + 1) >> 1)),
8181
abs(sample_rel.y - Double((quadrant.b + 1) >> 1)))
8282

83-
var r2:Double = self.distance2(from: sample, generating_point: near)
83+
var r2:Double = self.distance2(from: sample, generating_point: near),
84+
closest_point:Math.IntV2 = near
8485

8586
@inline(__always)
8687
func _inspect(generating_point:Math.IntV2, dx:Double = 0, dy:Double = 0)
8788
{
8889
if dx*dx + dy*dy < r2
8990
{
90-
r2 = min(r2, self.distance2(from: sample, generating_point: generating_point))
91+
let dr2:Double = self.distance2(from: sample, generating_point: generating_point)
92+
if dr2 < r2
93+
{
94+
r2 = dr2
95+
closest_point = generating_point
96+
}
9197
}
9298
}
9399

@@ -105,7 +111,7 @@ struct CellNoise2D:Noise
105111
guard r2 > 0.25
106112
else
107113
{
108-
return self.amplitude * r2
114+
return (closest_point, r2)
109115
}
110116

111117
// This is the part where shit hits the fan. (`inner` and `outer` are never
@@ -143,7 +149,7 @@ struct CellNoise2D:Noise
143149
guard r2 > 1.0
144150
else
145151
{
146-
return self.amplitude * r2
152+
return (closest_point, r2)
147153
}
148154

149155
// Cell group III:
@@ -159,6 +165,13 @@ struct CellNoise2D:Noise
159165
_inspect(generating_point: (far.a, outer.b), dx: nearpoint_disp.x - 0.5, dy: nearpoint_disp.y - 1.5)
160166
_inspect(generating_point: (outer.a, far.b), dx: nearpoint_disp.x - 1.5, dy: nearpoint_disp.y - 0.5)
161167

168+
return (closest_point, r2)
169+
}
170+
171+
public
172+
func evaluate(_ x:Double, _ y:Double) -> Double
173+
{
174+
let (_, r2):((Int, Int), Double) = self.closest_point(x, y)
162175
return self.amplitude * r2
163176
}
164177

@@ -214,13 +227,7 @@ struct CellNoise3D:Noise
214227
}
215228

216229
public
217-
func evaluate(_ x:Double, _ y:Double) -> Double
218-
{
219-
return self.evaluate(x, y, 0)
220-
}
221-
222-
public
223-
func evaluate(_ x:Double, _ y:Double, _ z:Double) -> Double
230+
func closest_point(_ x:Double, _ y:Double, _ z:Double) -> (point:(Int, Int, Int), r2:Double)
224231
{
225232
let sample:Math.DoubleV3 = (x * self.frequency, y * self.frequency, z * self.frequency)
226233

@@ -248,7 +255,8 @@ struct CellNoise3D:Noise
248255
abs(sample_rel.y - Double((quadrant.b + 1) >> 1)),
249256
abs(sample_rel.z - Double((quadrant.c + 1) >> 1)))
250257

251-
var r2:Double = self.distance2(from: sample, generating_point: near)
258+
var r2:Double = self.distance2(from: sample, generating_point: near),
259+
closest_point:Math.IntV3 = near
252260

253261
@inline(__always)
254262
func _inspect_cell(offset:Math.IntV3)
@@ -286,7 +294,12 @@ struct CellNoise3D:Noise
286294
let generating_point:Math.IntV3 = (near.a + quadrant.a*offset.a,
287295
near.b + quadrant.b*offset.b,
288296
near.c + quadrant.c*offset.c)
289-
r2 = min(r2, self.distance2(from: sample, generating_point: generating_point))
297+
let dr2:Double = self.distance2(from: sample, generating_point: generating_point)
298+
if dr2 < r2
299+
{
300+
r2 = dr2
301+
closest_point = generating_point
302+
}
290303
}
291304

292305
// check each cell group, exiting early if we are guaranteed to have found
@@ -311,7 +324,7 @@ struct CellNoise3D:Noise
311324
guard r2 > 0.25
312325
else
313326
{
314-
return self.amplitude * r2
327+
return (closest_point, r2)
315328
}
316329
for offset in [(1, 0, 0), ( 0, 1, 0), ( 0, 0, 1),
317330
(0, -1, 1), ( 0, 1, -1), ( 1, 0, -1), (-1, 0, 1), (-1, 1, 0), (1, -1, 0),
@@ -326,7 +339,7 @@ struct CellNoise3D:Noise
326339
guard r2 > 0.5
327340
else
328341
{
329-
return self.amplitude * r2
342+
return (closest_point, r2)
330343
}
331344
for offset in [(0, 1, 1), (1, 0, 1), (1, 1, 0), (-1, 1, 1), (1, -1, 1), (1, 1, -1)]
332345
{
@@ -343,7 +356,7 @@ struct CellNoise3D:Noise
343356
guard r2 > 1.0
344357
else
345358
{
346-
return self.amplitude * r2
359+
return (closest_point, r2)
347360
}
348361
for offset in [(-2, 0, 0), ( 0, -2, 0), ( 0, 0, -2),
349362
( 0, -2, -1), ( 0, -1, -2), (-2, 0, -1), (-1, 0, -2), (-2, -1, 0), (-1, -2, 0),
@@ -358,7 +371,7 @@ struct CellNoise3D:Noise
358371
guard r2 > 1.25
359372
else
360373
{
361-
return self.amplitude * r2
374+
return (closest_point, r2)
362375
}
363376
for offset in [( 0, 1, -2), ( 0, -2, 1), (1, 0, -2), (-2, 0, 1), (1, -2, 0), (-2, 1, 0),
364377
(-2, 1, -1), (-2, -1, 1), (1, -2, -1), (-1, -2, 1), (1, -1, -2), (-1, 1, -2)]
@@ -376,7 +389,7 @@ struct CellNoise3D:Noise
376389
guard r2 > 2.0
377390
else
378391
{
379-
return self.amplitude * r2
392+
return (closest_point, r2)
380393
}
381394
for offset in [(0, -2, -2), (-2, 0, -2), (-2, -2, 0), (-1, -2, -2), (-2, -1, -2), (-2, -2, -1)]
382395
{
@@ -389,7 +402,7 @@ struct CellNoise3D:Noise
389402
guard r2 > 2.25
390403
else
391404
{
392-
return self.amplitude * r2
405+
return (closest_point, r2)
393406
}
394407
for offset in [(2, 0, 0), (0, 2, 0), ( 0, 0, 2),
395408
(0, -1, 2), (0, 2, -1), (-1, 0, 2), ( 2, 0, -1), (-1, 2, 0), ( 2, -1, 0),
@@ -404,7 +417,7 @@ struct CellNoise3D:Noise
404417
guard r2 > 2.5
405418
else
406419
{
407-
return self.amplitude * r2
420+
return (closest_point, r2)
408421
}
409422
for offset in [(0, 1, 2), (0, 2, 1), (1, 0, 2), ( 2, 0, 1), (1, 2, 0), ( 2, 1, 0),
410423
(2, 1, -1), (2, -1, 1), (1, 2, -1), (-1, 2, 1), (1, -1, 2), (-1, 1, 2)]
@@ -417,6 +430,19 @@ struct CellNoise3D:Noise
417430
// cumulative sample coverage = 100%
418431

419432
// stop outside r^2 = 3.0
433+
return (closest_point, r2)
434+
}
435+
436+
public
437+
func evaluate(_ x:Double, _ y:Double) -> Double
438+
{
439+
return self.evaluate(x, y, 0)
440+
}
441+
442+
public
443+
func evaluate(_ x:Double, _ y:Double, _ z:Double) -> Double
444+
{
445+
let (_, r2):((Int, Int, Int), Double) = self.closest_point(x, y, z)
420446
return self.amplitude * r2
421447
}
422448

sources/noise/noise.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,13 @@ struct RandomXORShift
256256
}
257257
}
258258

259+
public
259260
struct PermutationTable
260261
{
261262
private
262263
let permut:[UInt8] // keep these small to minimize cache misses
263264

265+
public
264266
init(seed:Int)
265267
{
266268
var permutations:[UInt8] = [UInt8](0 ... 255),
@@ -282,4 +284,8 @@ struct PermutationTable
282284
{
283285
return Int(self.permut[self.hash((n3.a, n3.b)) ^ (n3.c & 255)])
284286
}
287+
288+
public func hash(_ h1:Int) -> UInt8 { return self.permut[h1 & 255] }
289+
public func hash(_ h1:Int, _ h2:Int) -> UInt8 { return self.permut[Int(self.hash(h1 )) ^ (h2 & 255)] }
290+
public func hash(_ h1:Int, _ h2:Int, _ h3:Int) -> UInt8 { return self.permut[Int(self.hash(h1, h2)) ^ (h3 & 255)] }
285291
}

tests/LinuxMain.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import func Glibc.clock
2424
var pixbuf:[UInt8]
2525
let png_properties:PNGProperties = PNGProperties(width: viewer_size, height: viewer_size, bit_depth: 8, color: .grayscale, interlaced: false)!
2626

27+
2728
var t0:Int
2829

2930
var poisson = PoissonSampler(seed: 0)
@@ -43,12 +44,66 @@ pixbuf = V.sample_area_saturated_to_u8(width: viewer_size, height: viewer_size,
4344
print(clock() - t0)
4445
try png_encode(path: "voronoi.png", raw_data: pixbuf, properties: png_properties)
4546

47+
t0 = clock()
48+
try {
49+
let png_color:PNGProperties = PNGProperties(width: viewer_size, height: viewer_size, bit_depth: 8, color: .rgb, interlaced: false)!
50+
var pixbuf_color = [UInt8](repeating: 0, count: viewer_size * viewer_size * 3)
51+
let red:PermutationTable = PermutationTable(seed: 0),
52+
green:PermutationTable = PermutationTable(seed: 1),
53+
blue:PermutationTable = PermutationTable(seed: 2)
54+
55+
var base_addr:Int = 0
56+
for y in 0 ..< viewer_size
57+
{
58+
for x in 0 ..< viewer_size
59+
{
60+
let (point, _):((Int, Int), Double) = V.closest_point(Double(x), Double(y))
61+
pixbuf_color[base_addr ] = red.hash (point.0, point.1)
62+
pixbuf_color[base_addr + 1] = green.hash(point.0, point.1)
63+
pixbuf_color[base_addr + 2] = blue.hash (point.0, point.1)
64+
65+
base_addr += 3
66+
}
67+
}
68+
69+
print(clock() - t0)
70+
71+
try png_encode(path: "cells.png", raw_data: pixbuf_color, properties: png_color)
72+
}()
73+
4674
let V3:CellNoise3D = CellNoise3D(amplitude: 255, frequency: 0.01)
4775
t0 = clock()
4876
pixbuf = V3.sample_area_saturated_to_u8(width: viewer_size, height: viewer_size, offset: 0)
4977
print(clock() - t0)
5078
try png_encode(path: "voronoi3D.png", raw_data: pixbuf, properties: png_properties)
5179

80+
t0 = clock()
81+
try {
82+
let png_color:PNGProperties = PNGProperties(width: viewer_size, height: viewer_size, bit_depth: 8, color: .rgb, interlaced: false)!
83+
var pixbuf_color = [UInt8](repeating: 0, count: viewer_size * viewer_size * 3)
84+
let red:PermutationTable = PermutationTable(seed: 0),
85+
green:PermutationTable = PermutationTable(seed: 1),
86+
blue:PermutationTable = PermutationTable(seed: 2)
87+
88+
var base_addr:Int = 0
89+
for y in 0 ..< viewer_size
90+
{
91+
for x in 0 ..< viewer_size
92+
{
93+
let (point, _):((Int, Int, Int), Double) = V3.closest_point(Double(x), Double(y), 0)
94+
pixbuf_color[base_addr ] = red.hash (point.0, point.1, point.2)
95+
pixbuf_color[base_addr + 1] = green.hash(point.0, point.1, point.2)
96+
pixbuf_color[base_addr + 2] = blue.hash (point.0, point.1, point.2)
97+
98+
base_addr += 3
99+
}
100+
}
101+
102+
print(clock() - t0)
103+
104+
try png_encode(path: "cells3D.png", raw_data: pixbuf_color, properties: png_color)
105+
}()
106+
52107
let S:fBm<SimplexNoise2D> = fBm<SimplexNoise2D>(amplitude: 0.5*127.5, frequency: 0.001, octaves: 10)
53108
t0 = clock()
54109
pixbuf = S.sample_area_saturated_to_u8(width: viewer_size, height: viewer_size, offset: 127.5)

0 commit comments

Comments
 (0)