Created
September 18, 2020 16:56
-
-
Save ylegall/a481a9abdcdba5bc80da346eb5863dd0 to your computer and use it in GitHub Desktop.
TangledCube: code for https://www.instagram.com/p/CFQozvynDZK/?utm_source=ig_web_copy_link
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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