All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.Box.kt Maven / Gradle / Ivy

package org.openrndr.kartifex


import org.openrndr.kartifex.utils.Scalars
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt


/**
 * @author ztellman
 */
abstract class Box, U : Box> {
    abstract fun lower(): T
    abstract fun upper(): T
    abstract val isEmpty: Boolean

    protected abstract fun construct(a: T, b: T): U
    protected abstract fun empty(): U
    fun distanceSquared(point: T): Double {
        val l: T = lower().sub(point)
        val u: T = point.sub(upper())
        return u.zip(
            l
        ) { a: Double, b: Double ->
            Scalars.max(
                0.0,
                a,
                b
            )
        }.lengthSquared()
    }

    fun distance(point: T): Double {
        return sqrt(distanceSquared(point))
    }

    fun union(b: U): U {
        if (isEmpty) {
            return b
        } else if (b.isEmpty) {
            @Suppress("UNCHECKED_CAST")
            return this as U
        }
        return construct(
            lower().zip(
                b.lower()
            ) { x: Double, y: Double -> min(x, y) }, upper().zip(
                b.upper()
            ) { x: Double, y: Double -> max(x, y) }
        )
    }

    fun union(v: T): U {
        return if (isEmpty) {
            construct(v, v)
        } else construct(
            lower().zip(v
            ) { a, b -> min(a, b) }, upper().zip(v
            ) { a, b -> max(a, b) }
        )
    }

    fun intersection(b: U): U {
        return if (isEmpty || b.isEmpty || !intersects(b)) {
            empty()
        } else construct(
            lower().zip(b.lower()) { u: Double, v: Double -> max(u, v) },
            upper().zip(b.upper()) { u: Double, v: Double -> min(u, v) }
        )
    }

    open fun intersects(b: U): Boolean {
        return if (isEmpty || b.isEmpty) {
            false
        } else b.upper().sub(lower()).every(NOT_NEGATIVE)
                && upper().sub(b.lower()).every(NOT_NEGATIVE)
    }

    operator fun contains(v: T): Boolean {
        return (v.sub(lower()).every(NOT_NEGATIVE)
                && upper().sub(v).every(NOT_NEGATIVE))
    }

    fun nth(idx: Int): Interval {
        return Interval.interval(lower().nth(idx), upper().nth(idx))
    }

    fun clamp(v: T): T {
        return v.zip(lower(), { a: Double, b: Double -> max(a, b) }).zip(upper(),
            { a: Double, b: Double -> min(a, b) })
    }

    fun size(): T {
        return upper().sub(lower())
    }

    fun normalize(v: T): T {
        return v.sub(lower()).div(size())
    }

    fun lerp(t: Double): T {
        return lower().add(size().mul(t))
    }

    fun lerp(v: T): T {
        return lower().add(size().mul(v))
    }

    fun translate(v: T): U {
        return construct(lower().add(v), upper().add(v))
    }

    fun scale(v: T): U {
        return construct(lower().mul(v), upper().mul(v))
    }

    fun expand(t: Double): U {
        if (isEmpty) {
            @Suppress("UNCHECKED_CAST")
            return this as U
        }
        val nLower: T = lower().map { n -> n - t }
        val nUpper: T = upper().map { n -> n + t }
        return if (nUpper.sub(nLower).every(NOT_NEGATIVE)) construct(nLower, nUpper) else empty()
    }

    fun expand(v: T): U {
        if (isEmpty) {
            @Suppress("UNCHECKED_CAST")
            return this as U
        }
        val nLower: T = lower().sub(v)
        val nUpper: T = upper().add(v)
        return if (nUpper.sub(nLower).every(NOT_NEGATIVE)) construct(nLower, nUpper) else empty()
    }

    override fun hashCode(): Int {
        return if (isEmpty) {
            0
        } else 31 * lower().hashCode() xor upper().hashCode()
    }

    override fun equals(other: Any?): Boolean {
        if (other is Box<*, *>) {
            val b = other
            return if (isEmpty) {
                b.isEmpty
            } else lower() == b.lower() && upper() == b.upper()
        }
        return false
    }

    override fun toString(): String {
        return "[" + lower() + ", " + upper() + "]"
    }

    companion object {
        fun box(a: Vec2, b: Vec2): Box2 {
            return Box2(a, b)
        }

        fun box(a: Interval, b: Interval): Box2 {
            return Box2(Vec2(a.lo, b.lo), Vec2(a.hi, b.hi))
        }

        fun box(a: Vec3, b: Vec3): Box3 {
            return Box3(a, b)
        }

        fun box(a: Vec4, b: Vec4): Box4 {
            return Box4(a, b)
        }

        private val POSITIVE: DoublePredicate = { d: Double -> d > 0 }
        private val NOT_NEGATIVE: DoublePredicate = { d: Double -> d >= 0 }

        fun , U : Box> equals(
            a: Box,
            b: Box,
            epsilon: Double
        ): Boolean {
            return Vec.equals(
                a.lower(),
                b.lower(),
                epsilon
            ) && Vec.equals(a.upper(), b.upper(), epsilon)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy