Skip to content

Instantly share code, notes, and snippets.

@ylegall
Created October 24, 2020 17:51
Show Gist options
  • Save ylegall/39d239b1025dfd2771082df5ee6dcfdd to your computer and use it in GitHub Desktop.
Save ylegall/39d239b1025dfd2771082df5ee6dcfdd to your computer and use it in GitHub Desktop.
package org.ygl.openrndr.demos
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorBuffer
import org.openrndr.draw.loadImage
import org.openrndr.draw.renderTarget
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.post
import org.openrndr.extra.fx.blur.FrameBlur
import org.openrndr.extra.gui.GUI
import org.openrndr.extra.noise.poissonDiskSampling
import org.openrndr.extra.parameters.Description
import org.openrndr.extra.parameters.DoubleParameter
import org.openrndr.ffmpeg.VideoWriter
import org.openrndr.math.Vector2
import org.openrndr.math.mix
import org.openrndr.shape.Rectangle
import org.openrndr.shape.compound
import org.ygl.fastnoise.FastNoise
import org.ygl.openrndr.utils.TileGrid
import org.ygl.openrndr.utils.isolatedWithTarget
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
private const val WIDTH = 920
private const val HEIGHT = 920
private const val TOTAL_FRAMES = 360
private const val LOOPS = 2
private const val DELAY_FRAMES = 60
private const val RECORDING = true
fun main() = application {
configure {
width = WIDTH
height = HEIGHT
}
program {
var time = 0.0
var frame = 0
val noise = FastNoise()
var videoFrame: ColorBuffer? = null
val border = compound {
difference {
shape(drawer.bounds.shape)
shape(drawer.bounds.offsetEdges(-20.0).shape)
}
}
val initialPositions = poissonDiskSampling(width.toDouble() + 200.0, height.toDouble() + 200.0, 14.0)
.map { it - Vector2(100.0, 100.0) }
.toMutableList()
val positions = MutableList(initialPositions.size) { Vector2.ZERO }
val radii = MutableList(initialPositions.size) { 7.0 }
val params = @Description("params") object {
@DoubleParameter("scale", 0.0, 4.0, precision = 2)
var scale = 0.15
@DoubleParameter("radius", 0.0, 1000.0, precision = 1)
var radius = 30.0
@DoubleParameter("noise magnitude", 0.0, 400.0, precision = 1)
var mag = 40.0
}
fun List<Vector2>.minDistanceFrom(pos: Vector2): Double {
var minDist = 35.0
for (item in this) {
if (item !== pos) {
minDist = min(minDist, item.distanceTo(pos))
}
}
return minDist
}
fun loadVideoFrame() {
videoFrame?.destroy()
val frameNumber = "%03d".format(frame)
videoFrame = loadImage("video/video-frames/frame-$frameNumber.png")
}
fun update() {
time = frame / TOTAL_FRAMES.toDouble()
frame = (frame % TOTAL_FRAMES) + 1
loadVideoFrame()
val shadow = videoFrame!!.shadow
shadow.download()
val index = TileGrid<Vector2>(drawer.bounds, 10.0, 10.0)
val noiseAngle = 2 * PI * time
for (i in initialPositions.indices) {
val (x, y) = initialPositions[i]
noise.seed = 1
val dx = params.mag * noise.getSimplex(
params.scale * x + params.radius * cos(noiseAngle),
params.scale * y + params.radius * sin(noiseAngle),
)
noise.seed = 2
val dy = params.mag * noise.getSimplex(
params.scale * x + params.radius * cos(noiseAngle),
params.scale * y + params.radius * sin(noiseAngle),
)
positions[i] = Vector2(x + dx, y + dy)
index.add(positions[i], positions[i])
//val strength = abs(dx * dy) / (params.mag * params.mag)
//radii[i] = mix(6.0, 18.0, strength)
if (positions[i] in drawer.bounds) {
val strength = shadow[positions[i].x.toInt(), positions[i].y.toInt()].luminance
radii[i] = mix(1.0, 15.0, strength)
} else {
radii[i] = 3.0
}
}
for (i in initialPositions.indices) {
val currentPosition = positions[i]
val neighbors = index.queryRange(Rectangle.fromCenter(currentPosition, 60.0, 60.0))
val minDist = neighbors.minDistanceFrom(currentPosition) * 0.6
radii[i] = min(radii[i], minDist)
}
}
val composite = compose {
draw {
drawer.clear(ColorRGBa.BLACK)
drawer.fill = ColorRGBa.WHITE
drawer.stroke = null
drawer.circles(positions, radii)
// render border
drawer.fill = ColorRGBa.BLACK
drawer.stroke = ColorRGBa.BLACK
drawer.shapes(border)
}
if (RECORDING) {
post(FrameBlur())
}
}
val videoTarget = renderTarget(width, height) { colorBuffer() }
val videoWriter = VideoWriter.create()
.size(width, height)
.frameRate(30)
.output("video/stipple.mp4")
if (RECORDING) {
videoWriter.start()
} else {
extend(GUI()) {
add(params)
}
}
extend {
update()
if (RECORDING) {
drawer.isolatedWithTarget(videoTarget) {
composite.draw(this)
}
drawer.image(videoTarget.colorBuffer(0))
if (frameCount > DELAY_FRAMES) {
videoWriter.frame(videoTarget.colorBuffer(0))
}
if (frameCount >= TOTAL_FRAMES * LOOPS + DELAY_FRAMES) {
videoWriter.stop()
application.exit()
}
} else {
composite.draw(drawer)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment