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

commonMain.jetbrains.datalore.base.algorithms.AdaptiveResampler.kt Maven / Gradle / Ivy

There is a newer version: 4.5.3-alpha1
Show newest version
/*
 * Copyright (c) 2021. JetBrains s.r.o.
 * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
 */

package jetbrains.datalore.base.algorithms

import jetbrains.datalore.base.geometry.DoubleVector


class AdaptiveResampler private constructor(
    private val transform: (T) -> T?,
    precision: Double,
    private val dataAdapter: DataAdapter
) {
    companion object {
        private val DOUBLE_VECTOR_ADAPTER = object : DataAdapter {
            override fun x(p: DoubleVector) = p.x
            override fun y(p: DoubleVector) = p.y
            override fun create(x: Double, y: Double) = DoubleVector(x, y)
        }

        fun forDoubleVector(transform: (DoubleVector) -> DoubleVector?, precision: Double): AdaptiveResampler {
            return AdaptiveResampler(transform, precision, DOUBLE_VECTOR_ADAPTER)
        }

        fun  generic(transform: (T) -> T?, precision: Double, adapter: DataAdapter): AdaptiveResampler {
            return AdaptiveResampler(transform, precision, adapter)
        }
    }
    private val precisionSqr: Double = precision * precision

    fun resample(points: List): List {
        val result = ArrayList(points.size)

        for (i in 1.until(points.size)) {
            result.removeLastOrNull()

            resample(points[i - 1], points[i])
                .forEach(result::add)
        }

        return result
    }

    fun resample(p1: T, p2: T): List {
        val result = ArrayList()
        val temp = ArrayList()
        result.add(p1)
        temp.add(p2)

        while (temp.isNotEmpty()) {
            when (val missingPoint = computeMissingPoint(result.last(), temp.last())) {
                null -> result.add(temp.removeLast())
                else -> temp.add(missingPoint)
            }
        }
        return result.mapNotNull { transform(it) }
    }

    private fun computeMissingPoint(p1: T, p2: T): T? {
        val pc = (p1 + p2) / 2.0
        val q1 = transform(p1) ?: return null
        val q2 = transform(p2) ?: return null
        val qc = transform(pc) ?: return null

        val distance = if (q1 == q2) {
            length(q1, qc)
        } else {
            distance(qc, q1, q2)
        }

        return if (distance < precisionSqr) null else pc
    }


    private fun length(p1: T, p2: T): Double {
        val x = p2.x - p1.x
        val y = p2.y - p1.y
        return x * x + y * y
    }

    private fun distance(p: T, l1: T, l2: T): Double {
        val ortX = l2.x - l1.x
        val ortY = -(l2.y - l1.y)

        val dot = (p.x - l1.x) * ortY + (p.y - l1.y) * ortX
        val len = ortY * ortY + ortX * ortX

        return dot * dot / len
    }

    val T.x get() = dataAdapter.x(this)
    val T.y get() = dataAdapter.y(this)
    private operator fun T.plus(other: T): T = dataAdapter.create(x + other.x, y + other.y)
    private operator fun T.div(other: T): T = dataAdapter.create(x / other.x, y / other.y)
    private operator fun T.div(v: Double): T = dataAdapter.create(x / v, y / v)

    interface DataAdapter {
        fun x(p: T): Double
        fun y(p: T): Double
        fun create(x: Double, y: Double): T

        val T.x get() = x(this)
        val T.y get() = y(this)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy