Skip to content

Instantly share code, notes, and snippets.

@ylegall
Created September 18, 2020 16:56
Show Gist options
  • Save ylegall/a481a9abdcdba5bc80da346eb5863dd0 to your computer and use it in GitHub Desktop.
Save ylegall/a481a9abdcdba5bc80da346eb5863dd0 to your computer and use it in GitHub Desktop.
package org.ygl.openrndr.demos
import org.openrndr.application
import org.openrndr.color.rgb
import org.openrndr.draw.LineCap
import org.openrndr.draw.renderTarget
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.parameters.Description
import org.openrndr.extra.parameters.DoubleParameter
import org.openrndr.ffmpeg.VideoWriter
import org.openrndr.math.Matrix44
import org.openrndr.math.Polar
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.transform
import org.openrndr.shape.Segment
import org.openrndr.shape.Segment3D
import org.openrndr.shape.ShapeContour
import org.ygl.fastnoise.FastNoise
import org.ygl.openrndr.utils.isolatedWithTarget
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
private const val WIDTH = 920
private const val HEIGHT = 920
private const val TOTAL_FRAMES = 360 * 6
private const val DELAY_FRAMES = 60
private const val LOOPS = 1
private const val RECORDING = false
fun main() = application {
configure {
width = WIDTH
height = HEIGHT
}
program {
var time = 0.0
val noise = FastNoise()
val numPaths = 500
val pointsPerPath = 1000
val bounds = drawer.bounds.moved(-drawer.bounds.center).offsetEdges(-40.0)
val bgColor = rgb("e6e8e6")
val lineColor = rgb("e75a7c")
val lineColor2 = rgb("6461a0")
val params = @Description("params") object {
@DoubleParameter("noise scale", 0.0, 5.0, precision = 2)
var noiseScale = 0.15
@DoubleParameter("noise mag", 0.0, 100.0)
var noiseMag = 1.0
@DoubleParameter("noise radius", 0.0, 100.0)
var noiseRadius = 50.0
}
fun boxSegments(size: Double): List<Segment3D> {
val leftTopFront = Vector3(-size, -size, -size)
val rightTopFront = Vector3(size, -size, -size)
val leftBottomFront = Vector3(-size, size, -size)
val rightBottomFront = Vector3(size, size, -size)
val leftTopBack = Vector3(-size, -size, size)
val rightTopBack = Vector3(size, -size, size)
val leftBottomBack = Vector3(-size, size, size)
val rightBottomBack = Vector3(size, size, size)
return listOf(
Segment3D(leftTopFront, rightTopFront),
Segment3D(rightTopFront, rightBottomFront),
Segment3D(rightBottomFront, leftBottomFront),
Segment3D(leftBottomFront, leftTopFront),
Segment3D(leftTopBack, rightTopBack),
Segment3D(rightTopBack, rightBottomBack),
Segment3D(rightBottomBack, leftBottomBack),
Segment3D(leftBottomBack, leftTopBack),
Segment3D(leftTopFront, leftTopBack),
Segment3D(rightTopFront, rightTopBack),
Segment3D(rightBottomFront, rightBottomBack),
Segment3D(leftBottomFront, leftBottomBack),
)
}
val boxSegments3D = boxSegments(200.0)
var segments: List<Segment>
var paths = emptyList<ShapeContour>()
fun transformPoint(point: Vector3, tx: Matrix44): Vector2 {
val p2 = (tx * point.xyz1)
return Vector2(p2.x, p2.y)
}
fun orthoProjectSegments(): List<Segment> {
val angle = 2 * PI * time
val tx = transform {
rotate(Vector3.UNIT_Y, 120 * sin(angle))
rotate(Vector3.UNIT_X, 60 * sin(angle))
rotate(Vector3.UNIT_Z, 45 * sin(angle))
}
return boxSegments3D.map {
Segment(transformPoint(it.start, tx), transformPoint(it.end, tx))
}
}
fun computePath(index: Int, segments: List<Segment>): ShapeContour {
val pct = index / (numPaths - 0.0)
//val timeOffset = (pct + time) % 1.0
val timeOffset = pct
val segmentProgress = (segments.size * timeOffset)
val segment = segments[segmentProgress.toInt()]
var pos = segment.position(segmentProgress % 1.0)
val points = mutableListOf(pos)
val noiseAngle = 2 * PI * time
//val noiseScale = params.noiseScale + 0.1 * sin(noiseAngle)
val noiseScale = params.noiseScale + params.noiseScale * sin(2 * noiseAngle)
val rotationFactor = 3 + sin(noiseAngle)
while (pos in bounds && points.size < pointsPerPath) {
noise.seed = 1
val dTheta = params.noiseMag + noise.getSimplex(
noiseScale * pos.x,
noiseScale * pos.y,
params.noiseRadius * cos(noiseAngle),
params.noiseRadius * sin(noiseAngle)
)
val dRadius = 1.0
val dir = Vector2.fromPolar(Polar(rotationFactor * 360 * dTheta, dRadius))
pos += dir
points.add(pos)
}
return ShapeContour.fromPoints(points, false)
}
fun update() {
segments = orthoProjectSegments()
paths = List(numPaths) { idx -> computePath(idx, segments) }
}
val composite = compose {
draw {
drawer.translate(drawer.bounds.center)
drawer.clear(bgColor)
drawer.lineCap = LineCap.ROUND
drawer.stroke = lineColor.opacify(0.8)
drawer.strokeWeight = 2.0
drawer.contours(paths.filterIndexed { index, shapeContour -> index % 2 == 0 })
drawer.stroke = lineColor2.opacify(0.8)
drawer.strokeWeight = 2.0
drawer.contours(paths.filterIndexed { index, shapeContour -> index % 2 == 1 })
drawer.stroke = lineColor2
drawer.strokeWeight = 3.0
drawer.fill = null
drawer.contour(bounds.contour)
}
post(FrameBlur()) {
blend = 0.5
}
}
val videoTarget = renderTarget(width, height) { colorBuffer() }
val videoWriter = VideoWriter.create()
.size(width, height)
.output("video/tangled-cube.mp4")
.frameRate(60)
if (RECORDING) videoWriter.start()
extend {
time = ((frameCount - 1) % TOTAL_FRAMES) / TOTAL_FRAMES.toDouble()
update()
if (RECORDING) {
drawer.isolatedWithTarget(videoTarget) {
composite.draw(drawer)
}
drawer.image(videoTarget.colorBuffer(0))
if (frameCount > DELAY_FRAMES) {
videoWriter.frame(videoTarget.colorBuffer(0))
}
if (frameCount >= DELAY_FRAMES + (TOTAL_FRAMES * LOOPS)) {
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