commonMain.jetbrains.datalore.plot.base.scale.Mappers.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lets-plot-common Show documentation
Show all versions of lets-plot-common Show documentation
Lets-Plot JVM package without rendering part
/*
* Copyright (c) 2019. 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.plot.base.scale
import jetbrains.datalore.base.function.Function
import jetbrains.datalore.base.gcommon.collect.ClosedRange
import jetbrains.datalore.plot.base.scale.breaks.QuantizeScale
import jetbrains.datalore.plot.common.data.SeriesUtil
import kotlin.math.round
object Mappers {
val IDENTITY = { v: Double? -> v }
fun undefined(): (Double?) -> T = { throw IllegalStateException("Undefined mapper") }
fun nullable(f: (Double?) -> T, ifNull: T): (Double?) -> T {
return { n ->
if (n == null) {
ifNull
} else {
f(n)
}
}
}
fun constant(v: Double): (Double?) -> Double = { v }
fun mul(domain: ClosedRange, rangeSpan: Double): (Double?) -> Double? {
val factor = rangeSpan / (domain.upperEnd - domain.lowerEnd)
check(!(factor.isInfinite() || factor.isNaN())) { "Can't create mapper with ratio: $factor" }
return mul(factor)
}
fun mul(factor: Double): (Double?) -> Double? {
return { v ->
if (v != null) {
factor * v
} else null
}
}
fun linear(domain: ClosedRange, range: ClosedRange, reverse: Boolean = false): (Double?) -> Double {
return linear(
domain,
rangeLow = if (reverse) range.upperEnd else range.lowerEnd,
rangeHigh = if (reverse) range.lowerEnd else range.upperEnd,
Double.NaN
)
}
fun linear(domain: ClosedRange, range: ClosedRange, defaultValue: Double): (Double?) -> Double {
return linear(
domain,
range.lowerEnd,
range.upperEnd,
defaultValue
)
}
fun linear(
domain: ClosedRange,
rangeLow: Double,
rangeHigh: Double,
defaultValue: Double
): (Double?) -> Double {
val slop = (rangeHigh - rangeLow) / (domain.upperEnd - domain.lowerEnd)
if (!SeriesUtil.isFinite(slop)) {
// no slop
val v = (rangeHigh - rangeLow) / 2 + rangeLow
return constant(v)
}
val intersect = rangeLow - domain.lowerEnd * slop
return { input ->
if (SeriesUtil.isFinite(input))
input!! * slop + intersect
else
defaultValue
}
}
fun discreteToContinuous(
domainValues: Collection<*>,
outputRange: ClosedRange,
naValue: Double
): (Double?) -> Double? {
val numberByDomainValue =
MapperUtil.mapDiscreteDomainValuesToNumbers(domainValues)
val dataRange = SeriesUtil.range(numberByDomainValue.values) ?: return IDENTITY
return linear(dataRange, outputRange, naValue)
}
fun discrete(outputValues: List, defaultOutputValue: T): (Double?) -> T? {
val f = DiscreteFun(outputValues, defaultOutputValue)
return { f.apply(it) }
}
fun quantized(
domain: ClosedRange?,
outputValues: Collection,
defaultOutputValue: T
): (Double?) -> T {
if (domain == null) {
return { defaultOutputValue }
}
// todo: extract quantizer
val quantizer = QuantizeScale()
quantizer.domain(domain.lowerEnd, domain.upperEnd)
quantizer.range(outputValues)
val f = QuantizedFun(quantizer, defaultOutputValue)
return { f.apply(it) }
}
private class DiscreteFun(
private val myOutputValues: List,
private val myDefaultOutputValue: T
) : Function {
override fun apply(value: Double?): T? {
if (!SeriesUtil.isFinite(value)) {
return myDefaultOutputValue
}
// ToDo: index-based discrete fun won't work for discrete numeric onput (see: MapperUtil#mapDiscreteDomainValuesToNumbers())
var index = round(value!!).toInt()
index %= myOutputValues.size
if (index < 0) {
index += myOutputValues.size
}
return myOutputValues[index]
}
}
private class QuantizedFun internal constructor(
private val myQuantizer: QuantizeScale,
private val myDefaultOutputValue: T
) : Function {
override fun apply(value: Double?): T {
return if (!SeriesUtil.isFinite(value)) myDefaultOutputValue else myQuantizer.quantize(value!!)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy