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

io.data2viz.scale.Threshold.kt Maven / Gradle / Ivy

There is a newer version: 0.8.0-RC5
Show newest version
package io.data2viz.scale

/**
 * Threshold scales are similar to quantize scales, except they allow you to map arbitrary subsets of the
 * domain to discrete values in the range.
 * The input domain is still continuous, and divided into slices based on a set of threshold values.
 */
class ThresholdScale internal constructor() : Scale, DiscreteRange, DiscreteDomain {


    var _domain: List = listOf(.5)
    var _range: List = listOf()

    /**
     * Sets the scale’s range to the specified array of values.
     * If the number of values in the scale’s domain is N, the number of values in the scale’s range must be N+1.
     */
    override var range: List
        get() = _range.toList()
        set(value) {
            _range = value.toList()
        }

    /**
     * Sets the scale’s domain to the specified array of values.
     * The values must be in sorted ascending order, or the behavior of the scale is undefined.
     * The values are typically numbers, but any naturally ordered values (such as strings) will work; a threshold
     * scale can be used to encode any type that is ordered.
     * If the number of values in the scale’s range is N+1, the number of values in the scale’s domain must be N.
     */
    override var domain: List
        get() = _domain.toList()
        set(value) {
            require(value.sorted() == value, { "The domain must be sorted in ascending order." })
            _domain = value.toList()
        }

    operator fun invoke(domainValue: Int): R {
        return this(domainValue.toDouble())
    }

    /**
     * Given a value in the input domain, returns the corresponding value in the output range.
     */
    override fun invoke(domainValue: Double): R {
        check(_range.size == _domain.size + 1,
            { "The range size (actual: ${_range.size}) must be 1 more than the domain size (actual: ${_domain.size})." })
        return _range[bisectRight(_domain, domainValue, naturalOrder(), 0, _domain.size)]
    }

    /**
     * Returns the extent of values in the domain [x0, x1] for the corresponding value in the range,
     * representing the inverse mapping from range to domain.
     * This method is useful for interaction, say to determine the value in the domain that corresponds to the
     * pixel location under the mouse.
     */
    fun invertExtent(rangeValue: R): List {
        check(_range.size == _domain.size + 1,
            { "The range size (actual: ${_range.size}) must be 1 more than the domain size (actual: ${_domain.size})." })
        val i = _range.indexOf(rangeValue)
        val size = _range.size - 1
        return when {
            i < 0 || i > size -> listOf(Double.NaN, Double.NaN)
            i == 0 -> listOf(Double.NaN, _domain.first())
            i == size -> listOf(domain.last(), Double.NaN)
            else -> listOf(_domain[i - 1], _domain[i])
        }
    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy