1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
package blood
// import org.openrndr.extra.timer.repeat
import blood.*
import org.openrndr.WindowMultisample
import org.openrndr.application
import org.openrndr.color.*
import org.openrndr.draw.*
import org.openrndr.extra.noise.perlinLinear
import org.openrndr.extra.shadestyles.fills.gradients.gradient
import org.openrndr.math.IntVector2
import org.openrndr.math.Vector2
import org.openrndr.shape.Rectangle
import phenax.utils.*
fun main() = application {
recordDuration(15.seconds())
if (captureEnabled) {
configure {
width = 1920
height = 1080
windowResizable = false
multisample = WindowMultisample.SampleCount(8)
}
} else {
configure {
width = 640
height = 360
windowResizable = false
}
}
oliveProgram {
val cell_size = 3.0
val cell_count = IntVector2(width, height) / cell_size.toInt()
val config = getConfig()
var cells: Cells = Array(cell_count.y) { Array(cell_count.x) { Vector2(1.0, 0.0) } }
config.initializeState(cells)
val feedRate = config.feedRate
val killRate = config.killRate
val diffusion = config.diffusion
fun laplaceThyNeighbour(row: Int, col: Int): Vector2 {
var d = Vector2.ZERO
for (i in -1 until 2) {
for (j in -1 until 2) {
val r = row + i
val c = col + j
val weight = if (i == 0 && j == 0) -1.0 else if (i == 0 || j == 0) 0.2 else 0.05
if (r >= 0 && r < cell_count.y && c >= 0 && c < cell_count.x) d += cells[r][c] * weight
}
}
return d
}
fun coerceIn(v: Vector2, min: Double, max: Double): Vector2 {
return Vector2(v.x.coerceIn(min, max), v.y.coerceIn(min, max))
}
fun nextState(dt: Double): Cells {
return Array(cell_count.y) { row ->
Array(cell_count.x) { col ->
val d = cells[row][col]
val lapl = laplaceThyNeighbour(row, col)
val u = -d.x * d.y * d.y + feedRate * (1 - d.x)
val v = d.x * d.y * d.y - (feedRate + killRate) * d.y
var change = lapl * diffusion + Vector2(u, v)
coerceIn(d + change * dt, 0.0, 1.0)
}
}
}
val col_flesh = ColorRGBa(0.53, 0.20, 0.20)
val col_blood = ColorRGBa(0.34, 0.00, 0.00)
fun drawCell(pos: Vector2, amounts: Vector2) {
var color = col_flesh.opacify(0.0)
run {
val noiseScale = 0.99
val noise = perlinLinear(100, pos.x * noiseScale, pos.y * noiseScale)
color = mix(color, col_flesh.shade(1.1).opacify(0.14), noise)
}
color = mix(color, col_blood, (amounts.y * 3).coerceIn(0.0, 1.0))
if (amounts.y > 0.38) color = mix(color, ColorRGBa.BLACK, amounts.y * 0.08)
if (amounts.y > 0.40) color = mix(color, ColorRGBa.WHITE, amounts.y * 0.28)
drawer.fill = color
drawer.stroke = drawer.fill
drawer.rectangle(pos.x * cell_size, pos.y * cell_size, cell_size, cell_size)
}
extend { repeat(config.iterationsPerFrame) { cells = nextState(0.8) } }
extend {
drawer.clear(col_flesh)
drawer.shadeStyle =
gradient<ColorRGBa> {
stops[0.4] = col_flesh
stops[0.9] = col_flesh.shade(0.65)
radial {}
}
drawer.rectangle(
Rectangle.fromCenter(drawer.bounds.center, width.toDouble(), height.toDouble())
)
drawer.shadeStyle = null
drawer.strokeWeight = 0.0
for ((row, rowdiff) in cells.withIndex()) {
for ((col, diff) in rowdiff.withIndex()) {
drawCell(Vector2(col.toDouble(), row.toDouble()), diff)
}
}
}
}
}
|