package org.openrndr.extra.noise

import org.openrndr.math.Vector2

fun cubic(seed: Int, x: Double, y: Double) = cubic(seed, x, y, ::linear)
fun cubicLinear(seed: Int, x: Double, y: Double) = cubic(seed, x, y, ::linear)
fun cubicQuintic(seed: Int, x: Double, y: Double) = cubic(seed, x, y, ::quintic)
fun cubicHermite(seed: Int, x: Double, y: Double) = cubic(seed, x, y, ::hermite)

fun cubic(seed: Int, position: Vector2) = cubic(seed, position.x, position.y, ::linear)
fun cubicLinear(seed: Int, position: Vector2) = cubic(seed, position.x, position.y, ::linear)
fun cubicQuintic(seed: Int, position: Vector2) = cubic(seed, position.x, position.y, ::quintic)
fun cubicHermite(seed: Int, position: Vector2) = cubic(seed, position.x, position.y, ::hermite)

private const val CUBIC_2D_BOUNDING = 1 / (1.5 * 1.5).toFloat()

fun cubic(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double = ::linear): Double {
    val x1 = x.fastFloor()
    val y1 = y.fastFloor()

    val x0 = x1 - 1
    val y0 = y1 - 1
    val x2 = x1 + 1
    val y2 = y1 + 1
    val x3 = x1 + 2
    val y3 = y1 + 2

    val xs = interpolator(x - x1.toDouble())
    val ys = interpolator(y - y1.toDouble())

    return cubic(
            cubic(valCoord2D(seed, x0, y0), valCoord2D(seed, x1, y0), valCoord2D(seed, x2, y0), valCoord2D(seed, x3, y0),
                    xs),
            cubic(valCoord2D(seed, x0, y1), valCoord2D(seed, x1, y1), valCoord2D(seed, x2, y1), valCoord2D(seed, x3, y1),
                    xs),
            cubic(valCoord2D(seed, x0, y2), valCoord2D(seed, x1, y2), valCoord2D(seed, x2, y2), valCoord2D(seed, x3, y2),
                    xs),
            cubic(valCoord2D(seed, x0, y3), valCoord2D(seed, x1, y3), valCoord2D(seed, x2, y3), valCoord2D(seed, x3, y3),
                    xs),
            ys) * CUBIC_2D_BOUNDING
}

val cubicLinear2D: (Int, Double, Double) -> Double = ::cubicLinear
val cubicQuintic2D: (Int, Double, Double) -> Double = ::cubicQuintic
val cubicHermite2D: (Int, Double, Double) -> Double = ::cubicHermite