Exit Full View

Rapid Rag Doll / src / main / kotlin / plasma.kt

/*
Rapid Rag Doll
Copyright (C) 2017 Nick Robinson

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
import javafx.scene.canvas.Canvas
import javafx.scene.effect.BoxBlur
import javafx.scene.effect.Effect
import javafx.scene.paint.Color
import org.joml.Vector2d
import uk.co.nickthecoder.tickle.util.RandomFactory

class LineStyle(val width: Double, val effect: Effect?, val color: Color)

/**
 * Creates a plasma effect. I run this interactively using [uk.co.nickthecoder.tickle.editor.FXCoder].
 * Make sure to uncomment the final line!
 */
class PlasmaScript {

    val rand = RandomFactory(11L)

    /**
     * Size of a single image.
     */
    val w = 300.0
    val h = 120.0

    /**
     * The number of images generated (will be tiled vertically).
     */
    val frames = 4

    /**
     * Maximum random value added to the y values
     */
    var deltaY = 30.0

    /**
     * An exponential value determining the number of line segments. Adding one will double the number of line segments.
     */
    val iterations = 4

    /**
     * Each line is drawn multiple times, each with different line widths, blurs, and colors.
     * This is the styles of the front-most plasma, and is the thickest and most prominent
     */
    val majorLineStyles = listOf(
            LineStyle(32.0, BoxBlur(16.0, 16.0, 4), Color(0.1, 0.1, 1.0, 0.5)),
            LineStyle(16.0, BoxBlur(16.0, 16.0, 2), Color(0.1, 0.1, 1.0, 0.8)),
            LineStyle(10.0, BoxBlur(8.0, 8.0, 2), Color(0.3, 0.3, 1.0, 1.0)),
            LineStyle(3.0, null, Color.WHITE)
    )
    /**
     * A thinner and less prominent style than the mojorLineStyles.
     */
    val minorLineStyles1 = listOf<LineStyle>(
            LineStyle(5.0, BoxBlur(3.0, 3.0, 2), Color(0.3, 0.3, 1.0, 0.5)),
            LineStyle(1.0, null, Color(0.5, 0.5, 1.0, 1.0))
    )
    /**
     * The darkest and thin style
     */
    val minorLineStyles2 = listOf<LineStyle>(
            LineStyle(5.0, BoxBlur(3.0, 3.0, 2), Color(0.0, 0.0, 0.4, 0.5)),
            LineStyle(1.0, null, Color(0.2, 0.2, 0.6, 1.0))
    )

    val points = mutableListOf<Vector2d>()
    val canvas = Canvas(w, h * frames)

    /**
     * Binary chops from the two ends, adding a random factor to the x and y values.
     */
    fun createPoints() {
        points.clear()
        val start = Vector2d(0.0, h / 2)
        val end = Vector2d(w, h / 2)
        points.add(start)
        points.add(end)
        addPoints(start, end, deltaY, iterations)
        points.sortBy { it.x }
    }

    fun addPoints(start: Vector2d, end: Vector2d, dy: Double, i: Int) {
        val newPoint = Vector2d()
        start.add(end, newPoint).mul(0.5).add(0.0, rand.plusMinus(dy))
        points.add(newPoint)
        if (i > 0) {
            addPoints(start, newPoint, dy / 2, i - 1)
            addPoints(newPoint, end, dy / 2, i - 1)
        }
    }

    fun blackBackground() {
        with(canvas.graphicsContext2D) {
            fill = Color.BLACK
            fillRect(0.0, 0.0, w, h * frames)
        }
    }

    fun draw(): Canvas {

        //blackBackground()

        for (frame in 0..frames) {
            drawArc(minorLineStyles2)
            drawArc(minorLineStyles1)
            drawArc(majorLineStyles)
            // Move down, so that the next frame does not overlap this one.
            canvas.graphicsContext2D.translate(0.0, h)
        }

        return canvas
    }

    fun drawArc(lineStyles: List<LineStyle>) {

        createPoints()

        with(canvas.graphicsContext2D) {

            lineStyles.forEach { lineStyle ->
                save()
                beginPath()
                moveTo(points[0].x, points[0].y)
                lineWidth = lineStyle.width
                setEffect(lineStyle.effect)
                stroke = lineStyle.color

                points.forEach { point ->
                    lineTo(point.x, point.y)
                }
                stroke()
                restore()
            }
        }

    }
}

// PlasmaScript().draw()