aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/blood/BloodProgram.kt
blob: ed702130fb34bd53a63e6624fe4d7992d4031335 (plain) (blame)
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)
        }
      }
    }
  }
}