commonMain.jetbrains.datalore.plot.builder.scale.ScaleProviderBuilder.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.builder.scale
import jetbrains.datalore.base.gcommon.collect.ClosedRange
import jetbrains.datalore.base.stringFormat.StringFormat
import jetbrains.datalore.plot.base.Aes
import jetbrains.datalore.plot.base.DataFrame
import jetbrains.datalore.plot.base.Scale
import jetbrains.datalore.plot.base.Transform
import jetbrains.datalore.plot.base.scale.Scales
import jetbrains.datalore.plot.common.data.SeriesUtil.ensureApplicableRange
class ScaleProviderBuilder(private val aes: Aes) {
private var _mapperProvider: MapperProvider? = null
private var myName: String? = null
private var myBreaks: List? = null
private var myLabels: List? = null
private var myLabelFormat: String? = null
private var myMultiplicativeExpand: Double? = null
private var myAdditiveExpand: Double? = null
private var myLimits: List<*>? = null
private var myTransform: Transform? = null
private var myDiscreteDomain = false
private var myDiscreteDomainReverse = false
var mapperProvider: MapperProvider
get() {
if (_mapperProvider == null) {
_mapperProvider = DefaultMapperProvider[aes]
}
return _mapperProvider ?: throw AssertionError("Set to null by another thread")
}
set(p: MapperProvider) {
_mapperProvider = p
}
fun mapperProvider(mapperProvider: MapperProvider): ScaleProviderBuilder {
this.mapperProvider = mapperProvider
return this
}
fun name(name: String): ScaleProviderBuilder {
myName = name
return this
}
fun breaks(breaks: List): ScaleProviderBuilder {
myBreaks = breaks
return this
}
@Suppress("FunctionName")
fun minorBreaks_NI(
@Suppress("UNUSED_PARAMETER") minorBreaks: List
): ScaleProviderBuilder {
// continuous scale
throw IllegalStateException("Not implemented")
}
fun labels(labels: List): ScaleProviderBuilder {
myLabels = ArrayList(labels)
return this
}
fun labelFormat(format: String?): ScaleProviderBuilder {
myLabelFormat = format
return this
}
fun multiplicativeExpand(v: Double): ScaleProviderBuilder {
myMultiplicativeExpand = v
return this
}
fun additiveExpand(v: Double): ScaleProviderBuilder {
myAdditiveExpand = v
return this
}
fun limits(v: List<*>): ScaleProviderBuilder {
// Limits for continuous scale : list(min, max)
// Limits for discrete scale : list ("a", "b", "c")
myLimits = v
return this
}
@Suppress("FunctionName")
fun rescaler_NI(
@Suppress("UNUSED_PARAMETER") v: Any
): ScaleProviderBuilder {
throw IllegalStateException("Not implemented")
}
@Suppress("FunctionName")
fun oob_NI(
@Suppress("UNUSED_PARAMETER") v: Any
): ScaleProviderBuilder {
throw IllegalStateException("Not implemented")
}
fun transform(v: Transform): ScaleProviderBuilder {
myTransform = v
return this
}
@Suppress("FunctionName")
fun guide_NI(
@Suppress("UNUSED_PARAMETER") v: Any
): ScaleProviderBuilder {
// Name of guide object, or object itself.
throw IllegalStateException("Not implemented")
}
fun discreteDomain(b: Boolean): ScaleProviderBuilder {
myDiscreteDomain = b
return this
}
fun discreteDomainReverse(b: Boolean): ScaleProviderBuilder {
myDiscreteDomainReverse = b
return this
}
fun build(): ScaleProvider {
return MyScaleProvider(this)
}
private class MyScaleProvider(b: ScaleProviderBuilder) :
ScaleProvider {
private val myName: String? = b.myName
private val myBreaks: List? = b.myBreaks?.let { ArrayList(b.myBreaks!!) }
private val myLabels: List? = b.myLabels?.let { ArrayList(b.myLabels!!) }
private val myLabelFormat: String? = b.myLabelFormat
private val myMultiplicativeExpand: Double? = b.myMultiplicativeExpand
private val myAdditiveExpand: Double? = b.myAdditiveExpand
private val myLimits: List<*>? = b.myLimits?.let { ArrayList(b.myLimits!!) }
private val discreteDomainReverse: Boolean = b.myDiscreteDomainReverse
private val myContinuousTransform: Transform? = b.myTransform
private val myAes: Aes = b.aes
private val mapperProvider: MapperProvider = b.mapperProvider
override val discreteDomain: Boolean = b.myDiscreteDomain
private fun scaleName(variable: DataFrame.Variable): String {
return myName ?: variable.label
}
override fun createScale(defaultName: String, discreteDomain: Collection<*>): Scale {
val name = myName ?: defaultName
var scale: Scale
// discrete domain
var domainValues = discreteDomain.filterNotNull()
val mapper = if (discreteDomain.isEmpty()) {
absentMapper(defaultName)
} else {
mapperProvider.createDiscreteMapper(domainValues)::apply
}
if (discreteDomainReverse) {
domainValues = domainValues.reversed()
}
scale = Scales.discreteDomain(
name,
domainValues,
mapper
)
if (myLimits != null) {
val limits = myLimits.filterNotNull().let { limits ->
if (discreteDomainReverse) {
limits.reversed()
} else {
limits
}
}
scale = scale.with()
.limits(limits)
.build()
}
return completeScale(scale)
}
override fun createScale(defaultName: String, continuousDomain: ClosedRange): Scale {
val name = myName ?: defaultName
var scale: Scale
// continuous (numeric) domain
val dataRange = ensureApplicableRange(continuousDomain)
var lowerLimit: Double? = null
var upperLimit: Double? = null
if (myLimits != null) {
var lower = true
for (limit in myLimits) {
if (limit is Number) {
val v = limit.toDouble()
if (v.isFinite()) {
if (lower) {
lowerLimit = v
} else {
upperLimit = v
}
}
}
lower = false
}
}
val mapper = mapperProvider.createContinuousMapper(
dataRange,
lowerLimit,
upperLimit,
myContinuousTransform
)
val continuousRange = mapper.isContinuous || myAes.isNumeric
scale = Scales.continuousDomain(name, { v -> mapper.apply(v) }, continuousRange)
if (mapper is WithGuideBreaks<*>) {
@Suppress("UNCHECKED_CAST")
mapper as WithGuideBreaks
scale = scale.with()
.breaks(mapper.breaks)
.labelFormatter(mapper.formatter)
.build()
}
if (myContinuousTransform != null) {
scale = scale.with()
.continuousTransform(myContinuousTransform)
.build()
}
if (myLimits != null) {
val with = scale.with()
if (lowerLimit != null) {
with.lowerLimit(lowerLimit)
}
if (upperLimit != null) {
with.upperLimit(upperLimit)
}
scale = with.build()
}
return completeScale(scale)
}
private fun completeScale(scale: Scale): Scale {
val with = scale.with()
if (myBreaks != null) {
with.breaks(myBreaks)
}
if (myLabels != null) {
with.labels(myLabels)
}
if (myLabelFormat != null) {
with.labelFormatter(StringFormat.create(myLabelFormat)::format)
}
if (myMultiplicativeExpand != null) {
with.multiplicativeExpand(myMultiplicativeExpand)
}
if (myAdditiveExpand != null) {
with.additiveExpand(myAdditiveExpand)
}
return with.build()
}
private fun absentMapper(`var`: DataFrame.Variable): (Double?) -> T {
// mapper for empty data is a special case - should never be used
return { v -> throw IllegalStateException("Mapper for empty data series '" + `var`.name + "' was invoked with arg " + v) }
}
private fun absentMapper(label: String): (Double?) -> T {
// mapper for empty data is a special case - should never be used
return { v -> throw IllegalStateException("Mapper for empty data series '$label' was invoked with arg " + v) }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy