commonMain.jetbrains.datalore.plot.base.Aes.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
import jetbrains.datalore.base.typedKey.TypedKey
import jetbrains.datalore.base.values.Color
import jetbrains.datalore.plot.base.render.linetype.LineType
import jetbrains.datalore.plot.base.render.point.PointShape
class Aes private constructor(val name: String, val isNumeric: Boolean = true) : TypedKey {
val isColor: Boolean
get() = isColor(this)
init {
values.add(this)
}
override fun toString(): String {
return "aes '$name'"
}
companion object {
private val values = ArrayList>()
val X: Aes = Aes("x")
val Y: Aes = Aes("y")
val Z: Aes = Aes("z")
val COLOR: Aes = Aes("color", false)
val FILL: Aes = Aes("fill", false)
val ALPHA: Aes = Aes("alpha")
val SHAPE: Aes = Aes("shape", false)
val LINETYPE: Aes = Aes("linetype", false)
val SIZE: Aes = Aes("size")
val STACKSIZE: Aes = Aes("stacksize")
val WIDTH: Aes = Aes("width")
val HEIGHT: Aes = Aes("height")
val BINWIDTH: Aes = Aes("binwidth")
val VIOLINWIDTH: Aes = Aes("violinwidth")
val WEIGHT: Aes = Aes("weight")
val INTERCEPT: Aes = Aes("intercept")
val SLOPE: Aes = Aes("slope")
val XINTERCEPT: Aes = Aes("xintercept")
val YINTERCEPT: Aes = Aes("yintercept")
val LOWER: Aes = Aes("lower")
val MIDDLE: Aes = Aes("middle")
val UPPER: Aes = Aes("upper")
val SAMPLE: Aes = Aes("sample")
val QUANTILE: Aes = Aes("quantile")
val XMIN: Aes = Aes("xmin")
val XMAX: Aes = Aes("xmax")
val YMIN: Aes = Aes("ymin")
val YMAX: Aes = Aes("ymax")
val XEND: Aes = Aes("xend")
val YEND: Aes = Aes("yend")
val MAP_ID: Aes = Aes("map_id", false)
val FRAME: Aes = Aes("frame", false)
val SPEED: Aes = Aes("speed")
val FLOW: Aes = Aes("flow")
val LABEL: Aes = Aes("label", false)
val FAMILY: Aes = Aes("family", false)
val FONTFACE: Aes = Aes("fontface", false)
val LINEHEIGHT: Aes = Aes("lineheight")
// text horizontal justification (numbers [0..1] or predefined strings = new Aes<>(); not positional)
val HJUST = Aes("hjust", false)
// text vertical justification (numbers [0..1] or predefined strings, not positional)
val VJUST = Aes("vjust", false)
val ANGLE: Aes = Aes("angle")
// pie geom - defines size of sector
val SLICE: Aes = Aes("slice")
// pie geom - to explode sector from center point, detaching it from the main pie
val EXPLODE: Aes = Aes("explode")
fun numeric(unfiltered: Iterable>): List> {
// safe to cast all 'numeric' aesthetics are 'Double'
@Suppress("UNCHECKED_CAST")
return unfiltered.filter { aes -> aes.isNumeric } as List>
}
fun isPositional(aes: Aes<*>): Boolean {
return isPositionalXY(aes) ||
// SLOPE must be positional or
// `geom_abline(slope=number)` will not work.
// it should draw the same line as:
// `geom_abline(slope=number, intersept=0)`
// See: PlotUtil.createLayerAesthetics()
aes == SLOPE
}
fun isPositionalXY(aes: Aes<*>): Boolean {
return isPositionalX(aes) ||
isPositionalY(aes)
}
fun isPositionalX(aes: Aes<*>): Boolean {
return aes == X ||
aes == XINTERCEPT ||
aes == XMIN ||
aes == XMAX ||
aes == XEND
}
fun isPositionalY(aes: Aes<*>): Boolean {
return aes == Y ||
aes == YMIN ||
aes == YMAX ||
aes == INTERCEPT ||
aes == YINTERCEPT ||
aes == LOWER ||
aes == MIDDLE ||
aes == UPPER ||
aes == SAMPLE ||
aes == YEND
}
fun toAxisAes(aes: Aes<*>, isYOrientation: Boolean): Aes<*> {
// Aes like `LOWER` (boxplot) are mapped on either X or Y-axis depending on the geom orientation.
return when {
aes == X || aes == Y -> aes
isPositionalX(aes) -> if (isYOrientation) Y else X
isPositionalY(aes) -> if (isYOrientation) X else Y
else -> throw IllegalArgumentException("Expected a positional aes by was $aes")
}
}
fun isColor(aes: Aes<*>): Boolean {
return aes == COLOR || aes == FILL
}
fun affectingScaleX(aes: Aes<*>): Boolean {
return isPositionalX(aes)
}
fun affectingScaleY(aes: Aes<*>): Boolean {
return isPositionalY(aes) &&
// "INTERCEPT" is "positional Y" because it must use the same 'mapper' as other "positional Y"-s,
// but its range of values is not taken in account when computing the Y-mapper.
aes != INTERCEPT
}
fun affectingScaleX(unfiltered: Iterable>): List> {
val numeric = numeric(unfiltered)
return numeric.filter { affectingScaleX(it) }
}
fun affectingScaleY(unfiltered: Iterable>): List> {
val numeric = numeric(unfiltered)
return numeric.filter { affectingScaleY(it) }
}
fun noGuideNeeded(aes: Aes<*>): Boolean {
return aes == MAP_ID ||
aes == FRAME ||
aes == SPEED ||
aes == FLOW ||
aes == LABEL ||
aes == SLOPE ||
aes == STACKSIZE ||
aes == WIDTH ||
aes == HEIGHT ||
aes == BINWIDTH ||
aes == VIOLINWIDTH ||
aes == QUANTILE ||
aes == HJUST ||
aes == VJUST ||
aes == ANGLE ||
aes == FAMILY ||
aes == FONTFACE ||
aes == LINEHEIGHT ||
aes == SLICE ||
aes == EXPLODE ||
isPositional(aes)
}
fun values(): List> {
return values
}
fun allPositional(): List> {
@Suppress("UNCHECKED_CAST")
return values.filter { isPositional(it) } as List>
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy