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

commonMain.jetbrains.datalore.plot.base.aes.AestheticsBuilder.kt Maven / Gradle / Ivy

There is a newer version: 4.5.3-alpha1
Show newest version
/*
 * Copyright (c) 2020. 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.aes

import jetbrains.datalore.base.function.Function
import jetbrains.datalore.base.interval.DoubleSpan
import jetbrains.datalore.base.values.Color
import jetbrains.datalore.plot.base.Aes
import jetbrains.datalore.plot.base.Aes.Companion.ALPHA
import jetbrains.datalore.plot.base.Aes.Companion.ANGLE
import jetbrains.datalore.plot.base.Aes.Companion.COLOR
import jetbrains.datalore.plot.base.Aes.Companion.EXPLODE
import jetbrains.datalore.plot.base.Aes.Companion.FAMILY
import jetbrains.datalore.plot.base.Aes.Companion.FILL
import jetbrains.datalore.plot.base.Aes.Companion.FLOW
import jetbrains.datalore.plot.base.Aes.Companion.FONTFACE
import jetbrains.datalore.plot.base.Aes.Companion.FRAME
import jetbrains.datalore.plot.base.Aes.Companion.HJUST
import jetbrains.datalore.plot.base.Aes.Companion.LABEL
import jetbrains.datalore.plot.base.Aes.Companion.LINEHEIGHT
import jetbrains.datalore.plot.base.Aes.Companion.LINETYPE
import jetbrains.datalore.plot.base.Aes.Companion.MAP_ID
import jetbrains.datalore.plot.base.Aes.Companion.SHAPE
import jetbrains.datalore.plot.base.Aes.Companion.SIZE
import jetbrains.datalore.plot.base.Aes.Companion.SLICE
import jetbrains.datalore.plot.base.Aes.Companion.SPEED
import jetbrains.datalore.plot.base.Aes.Companion.VIOLINWIDTH
import jetbrains.datalore.plot.base.Aes.Companion.VJUST
import jetbrains.datalore.plot.base.Aes.Companion.WEIGHT
import jetbrains.datalore.plot.base.Aes.Companion.WIDTH
import jetbrains.datalore.plot.base.Aes.Companion.HEIGHT
import jetbrains.datalore.plot.base.Aes.Companion.X
import jetbrains.datalore.plot.base.Aes.Companion.XMAX
import jetbrains.datalore.plot.base.Aes.Companion.XMIN
import jetbrains.datalore.plot.base.Aes.Companion.Y
import jetbrains.datalore.plot.base.Aes.Companion.YMAX
import jetbrains.datalore.plot.base.Aes.Companion.YMIN
import jetbrains.datalore.plot.base.Aesthetics
import jetbrains.datalore.plot.base.DataPointAesthetics
import jetbrains.datalore.plot.base.ScaleMapper
import jetbrains.datalore.plot.base.render.linetype.LineType
import jetbrains.datalore.plot.base.render.point.PointShape
import jetbrains.datalore.plot.common.data.SeriesUtil
import kotlin.jvm.JvmOverloads

class AestheticsBuilder @JvmOverloads constructor(private var myDataPointCount: Int = 0) {

    private val myIndexFunctionMap: MutableMap, (Int) -> Any?>
    private var myGroup = constant(0)
    private val myConstantAes = HashSet(Aes.values())  // initially contains all Aes;

    init {
        myIndexFunctionMap = HashMap()
        for (aes in Aes.values()) {
            // Safe cast because AesInitValue.get(aes) is guaranteed to return correct type.
            myIndexFunctionMap[aes] =
                constant(
                    AesInitValue[aes]
                )
        }
    }

    fun dataPointCount(v: Int): AestheticsBuilder {
        myDataPointCount = v
        return this
    }

    fun x(v: (Int) -> Double?): AestheticsBuilder {
        return aes(X, v)
    }

    fun y(v: (Int) -> Double?): AestheticsBuilder {
        return aes(Y, v)
    }

    fun color(v: (Int) -> Color?): AestheticsBuilder {
        return aes(COLOR, v)
    }

    fun fill(v: (Int) -> Color?): AestheticsBuilder {
        return aes(FILL, v)
    }

    fun alpha(v: (Int) -> Double?): AestheticsBuilder {
        return aes(ALPHA, v)
    }

    fun shape(v: (Int) -> PointShape?): AestheticsBuilder {
        return aes(SHAPE, v)
    }

    fun lineType(v: (Int) -> LineType?): AestheticsBuilder {
        return aes(LINETYPE, v)
    }

    fun size(v: (Int) -> Double?): AestheticsBuilder {
        return aes(SIZE, v)
    }

    fun width(v: (Int) -> Double?): AestheticsBuilder {
        return aes(WIDTH, v)
    }

    fun height(v: (Int) -> Double?): AestheticsBuilder {
        return aes(HEIGHT, v)
    }

    fun violinwidth(v: (Int) -> Double?): AestheticsBuilder {
        return aes(VIOLINWIDTH, v)
    }

    fun weight(v: (Int) -> Double?): AestheticsBuilder {
        return aes(WEIGHT, v)
    }

    fun mapId(v: (Int) -> Any?): AestheticsBuilder {
        return aes(MAP_ID, v)
    }

    fun frame(v: (Int) -> String?): AestheticsBuilder {
        return aes(FRAME, v)
    }

    fun speed(v: (Int) -> Double?): AestheticsBuilder {
        return aes(SPEED, v)
    }

    fun flow(v: (Int) -> Double?): AestheticsBuilder {
        return aes(FLOW, v)
    }

    fun group(v: (Int) -> Int): AestheticsBuilder {
        myGroup = v
        return this
    }

    fun label(v: (Int) -> Any?): AestheticsBuilder {
        return aes(LABEL, v)
    }

    fun family(v: (Int) -> String?): AestheticsBuilder {
        return aes(FAMILY, v)
    }

    fun fontface(v: (Int) -> String?): AestheticsBuilder {
        return aes(FONTFACE, v)
    }

    fun lineheight(v: (Int) -> Double?): AestheticsBuilder {
        return aes(LINEHEIGHT, v)
    }

    fun hjust(v: (Int) -> Any?): AestheticsBuilder {
        return aes(HJUST, v)
    }

    fun vjust(v: (Int) -> Any?): AestheticsBuilder {
        return aes(VJUST, v)
    }

    fun angle(v: (Int) -> Double?): AestheticsBuilder {
        return aes(ANGLE, v)
    }

    fun xmin(v: (Int) -> Double?): AestheticsBuilder {
        return aes(XMIN, v)
    }

    fun xmax(v: (Int) -> Double?): AestheticsBuilder {
        return aes(XMAX, v)
    }

    fun ymin(v: (Int) -> Double?): AestheticsBuilder {
        return aes(YMIN, v)
    }

    fun ymax(v: (Int) -> Double?): AestheticsBuilder {
        return aes(YMAX, v)
    }

    fun slice(v: (Int) -> Double?): AestheticsBuilder {
        return aes(SLICE, v)
    }

    fun explode(v: (Int) -> Double?): AestheticsBuilder {
        return aes(EXPLODE, v)
    }

    fun  constantAes(aes: Aes, v: T?): AestheticsBuilder {
        myConstantAes.add(aes)
        myIndexFunctionMap[aes] = constant(v)
        return this
    }

    fun  aes(aes: Aes, v: (Int) -> T?): AestheticsBuilder {
        myConstantAes.remove(aes)
        myIndexFunctionMap[aes] = v
        return this
    }

    fun build(): Aesthetics {
        return MyAesthetics(this)
    }


    private class MyAesthetics internal constructor(b: AestheticsBuilder) : Aesthetics {
        private val myDataPointCount: Int = b.myDataPointCount
        private val myIndexFunctionMap = TypedIndexFunctionMap(b.myIndexFunctionMap)
        val group = b.myGroup
        private val myConstantAes: Set> = HashSet(b.myConstantAes)

        private val myResolutionByAes = HashMap, Double>()
        private val myRangeByNumericAes = HashMap, DoubleSpan?>()

        override val isEmpty: Boolean
            get() = myDataPointCount == 0

        fun  aes(aes: Aes): (Int) -> T {
            return myIndexFunctionMap[aes]
        }

        override fun dataPointAt(index: Int): DataPointAesthetics {
            return MyDataPointAesthetics(
                index,
                this
            )
        }

        override fun dataPointCount(): Int {
            return myDataPointCount
        }

        override fun dataPoints(): Iterable {
            val self = this
            return object : Iterable {
                override fun iterator(): Iterator =
                    MyDataPointsIterator(
                        myDataPointCount,
                        self
                    )
            }
        }

        override fun range(aes: Aes): DoubleSpan? {
            if (!myRangeByNumericAes.containsKey(aes)) {
                val r = when {
                    myDataPointCount <= 0 -> null
                    myConstantAes.contains(aes) -> {
                        val v = numericValues(aes).iterator().next()
                        if (v != null && v.isFinite()) {
                            DoubleSpan(v, v)
                        } else null
                    }

                    else -> {
                        val values = numericValues(aes)
                        SeriesUtil.range(values)
                    }
                }
                myRangeByNumericAes[aes] = r
            }

            return myRangeByNumericAes[aes]
        }

        override fun resolution(aes: Aes, naValue: Double): Double {
            if (!myResolutionByAes.containsKey(aes)) {
                val resolution: Double =
                    when {
                        myConstantAes.contains(aes) -> 0.0
                        else -> {
                            val values = numericValues(aes)
                            SeriesUtil.resolution(values, naValue)
                        }
                    }
                myResolutionByAes[aes] = resolution
            }

            return myResolutionByAes[aes]!!
        }

        override fun numericValues(aes: Aes): Iterable {
            require(aes.isNumeric) { "Numeric aes is expected: $aes" }
            return object : Iterable {
                override fun iterator(): Iterator {
                    return AesIterator(
                        myDataPointCount,
                        aes(aes)
                    )
                }
            }
        }

        override fun groups(): Iterable {
            return object : Iterable {
                override fun iterator(): Iterator {
                    return AesIterator(
                        myDataPointCount,
                        group
                    )
                }
            }
        }
    }

    private class MyDataPointsIterator internal constructor(
        private val myLength: Int,
        private val myAesthetics: MyAesthetics
    ) : Iterator {
        private var myIndex = 0

        override fun hasNext(): Boolean {
            return myIndex < myLength
        }

        override fun next(): DataPointAesthetics {
            if (hasNext()) {
                return myAesthetics.dataPointAt(myIndex++)
            }
            throw NoSuchElementException("index=$myIndex")
        }
    }

    private class AesIterator internal constructor(private val myLength: Int, private val myAes: (Int) -> T) :
        Iterator {
        private var myIndex = 0

        override fun hasNext(): Boolean {
            return myIndex < myLength
        }

        override fun next(): T {
            if (hasNext()) {
                return myAes(myIndex++)
            }
            throw NoSuchElementException("index=$myIndex")
        }
    }


    private class MyDataPointAesthetics(
        private val myIndex: Int,
        private val myAesthetics: MyAesthetics
    ) : DataPointAesthetics() {

        override fun index(): Int {
            return myIndex
        }

        override fun group(): Int {
            return myAesthetics.group(myIndex)
        }

        override fun  get(aes: Aes): T? {
            return myAesthetics.aes(aes)(myIndex)
        }
    }


    private class ArrayAes internal constructor(
        private val myVal: Array
    ) : Function {
        override fun apply(value: Int): ValueT {
            return myVal[value]
        }
    }

    private class MapperAes internal constructor(
        private val myL: List,
        private val myF: ((Double) -> ValueT)
    ) : Function {
        override fun apply(value: Int): ValueT {
            return myF(myL[value])
        }
    }

    companion object {
        fun  constant(v: T): (Int) -> T = { v }

        fun  array(v: Array): (Int) -> T {
            return { index -> v[index] }
        }

        fun  list(v: List): (Int) -> T {
            return { index -> v[index] }
        }

        fun  listMapper(v: List, f: ScaleMapper): (Int) -> T? {
            return { value -> f(v[value]) }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy