Skip to content

Commit

Permalink
Add lablim scale parameter. Disable default axis label truncation.
Browse files Browse the repository at this point in the history
  • Loading branch information
IKupriyanov-HORIS committed Dec 4, 2023
1 parent de139f5 commit 93d2222
Show file tree
Hide file tree
Showing 14 changed files with 662 additions and 119 deletions.
314 changes: 314 additions & 0 deletions docs/f-23f/scale_lablim.ipynb

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
See: [example notebook](https://nbviewer.jupyter.org/github/JetBrains/lets-plot/blob/master/docs/f-23f/scale_params_with_dict.ipynb).


- The `lablim` parameter for `scale_xxx()` functions [[#939](https://github.com/JetBrains/lets-plot/issues/939), [#946](https://github.com/JetBrains/lets-plot/issues/946)].

See: [example notebook](https://nbviewer.jupyter.org/github/JetBrains/lets-plot/blob/master/docs/f-23f/scale_lablim.ipynb).


### Changed

- The `plot_margin` parameter in `theme()` and the `margin` parameter in `element_text()` accept a number or a list of numbers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ interface Scale {

fun getScaleBreaks(): ScaleBreaks

// For axis and legend (truncated labels). For tooltips the getScaleBreaks functions should be used (full labels).
fun getShortenedScaleBreaks(): ScaleBreaks

fun with(): Builder

interface Builder {
Expand All @@ -41,6 +44,8 @@ interface Scale {

fun labels(l: List<String>): Builder

fun labelWidthLimit(v: Int): Builder

fun labelFormatter(v: (Any) -> String): Builder

fun multiplicativeExpand(v: Double): Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal abstract class AbstractScale<DomainT> : Scale {

private val definedBreaks: List<DomainT>?
private val definedLabels: List<String>?
private val labelWidthLimit: Int

final override val name: String

Expand All @@ -23,6 +24,7 @@ internal abstract class AbstractScale<DomainT> : Scale {
protected constructor(name: String, breaks: List<DomainT>? = null) {
this.name = name
this.definedBreaks = breaks
labelWidthLimit = 0
definedLabels = null
labelFormatter = null
}
Expand All @@ -31,6 +33,7 @@ internal abstract class AbstractScale<DomainT> : Scale {
name = b.myName
definedBreaks = b.myBreaks
definedLabels = b.myLabels
labelWidthLimit = b.myLabelWidthLimit
labelFormatter = b.myLabelFormatter

multiplicativeExpand = b.myMultiplicativeExpand
Expand All @@ -54,12 +57,20 @@ internal abstract class AbstractScale<DomainT> : Scale {
}

override fun getScaleBreaks(): ScaleBreaks {
return createScaleBreaks(shortenLabels = false)
}

override fun getShortenedScaleBreaks(): ScaleBreaks {
return createScaleBreaks(shortenLabels = true)
}

private fun createScaleBreaks(shortenLabels: Boolean): ScaleBreaks {
if (!hasBreaks()) {
return ScaleBreaks.EMPTY
}

val breakValuesIntern = getBreaksIntern()
val labels = getLabels(breakValuesIntern)
val labels = getLabels(breakValuesIntern).map { if (shortenLabels) shorten(it) else it }
val transformCore = transform.unwrap() // make sure 'original' transform is used.
val transformed = ScaleUtil.applyTransform(breakValuesIntern, transformCore)

Expand All @@ -77,6 +88,14 @@ internal abstract class AbstractScale<DomainT> : Scale {
)
}

private fun shorten(str: String): String {
return if (labelWidthLimit > 0 && str.length > labelWidthLimit) {
str.substring(0, labelWidthLimit) + "..."
} else {
str
}
}

private fun getLabels(breaks: List<DomainT>): List<String> {
if (definedLabels != null) {
val labels = getLabelsIntern()
Expand All @@ -97,6 +116,7 @@ internal abstract class AbstractScale<DomainT> : Scale {

internal var myBreaks: List<DomainT>? = scale.definedBreaks
internal var myLabels: List<String>? = scale.definedLabels
internal var myLabelWidthLimit: Int = scale.labelWidthLimit
internal var myLabelFormatter: ((Any) -> String)? = scale.labelFormatter

internal var myMultiplicativeExpand: Double = scale.multiplicativeExpand
Expand All @@ -120,6 +140,11 @@ internal abstract class AbstractScale<DomainT> : Scale {
return this
}

override fun labelWidthLimit(v: Int): Scale.Builder {
myLabelWidthLimit = v
return this
}

override fun labelFormatter(v: (Any) -> String): Scale.Builder {
myLabelFormatter = v
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ internal class BogusScale : Scale {
throw IllegalStateException("Bogus scale is not supposed to be used.")
}

override fun getShortenedScaleBreaks(): ScaleBreaks {
throw IllegalStateException("Bogus scale is not supposed to be used.")
}

override fun getBreaksGenerator(): BreaksGenerator {
throw IllegalStateException("Bogus scale is not supposed to be used.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class LegendAssembler(
}
check(scale.hasBreaks()) { "No breaks were defined for scale $aes" }

val scaleBreaks = scale.getScaleBreaks()
val scaleBreaks = scale.getShortenedScaleBreaks()
val aesValues = scaleBreaks.transformedValues.map {
scaleMappers.getValue(aes)(it) as Any // Don't expect nulls.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract class AxisBreaksProviderFactory {
companion object {
fun forScale(scale: Scale): AxisBreaksProviderFactory {
return if (scale.hasBreaks()) {
FixedBreaksProviderFactory(FixedAxisBreaksProvider(scale.getScaleBreaks()))
FixedBreaksProviderFactory(FixedAxisBreaksProvider(scale.getShortenedScaleBreaks()))
} else {
AdaptableBreaksProviderFactory(scale.getBreaksGenerator())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ package org.jetbrains.letsPlot.core.plot.builder.layout.axis
import org.jetbrains.letsPlot.commons.interval.DoubleSpan
import org.jetbrains.letsPlot.core.plot.base.ScaleMapper
import org.jetbrains.letsPlot.core.plot.base.scale.Mappers
import org.jetbrains.letsPlot.core.plot.base.scale.ScaleBreaks
import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme
import org.jetbrains.letsPlot.core.plot.builder.guide.Orientation
import org.jetbrains.letsPlot.core.plot.builder.layout.AxisLayoutInfo
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.AxisLabelsLayout
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.AxisLabelsLayout.Companion.horizontalFixedBreaks
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.AxisLabelsLayout.Companion.horizontalFlexBreaks
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.AxisLabelsLayout.Companion.verticalFixedBreaks
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.AxisLabelsLayout.Companion.verticalFlexBreaks
import org.jetbrains.letsPlot.core.plot.builder.layout.axis.label.BreakLabelsLayoutUtil
import org.jetbrains.letsPlot.core.plot.builder.layout.util.Insets
import org.jetbrains.letsPlot.core.plot.builder.presentation.Defaults.Common.Axis

internal abstract class AxisLayouter(
val orientation: Orientation,
Expand Down Expand Up @@ -62,51 +64,22 @@ internal abstract class AxisLayouter(
geomAreaInsets: Insets,
theme: AxisTheme
): AxisLayouter {

if (orientation.isHorizontal) {
val labelsLayout: AxisLabelsLayout = if (breaksProvider.isFixedBreaks) {
val trimmedScaleBreaks = with(breaksProvider.fixedBreaks) {
ScaleBreaks(domainValues, transformedValues, labels.map(::trimLongValues))
val labelsLayout =
if (breaksProvider.isFixedBreaks) {
if (orientation.isHorizontal) {
horizontalFixedBreaks(orientation, axisDomain, breaksProvider.fixedBreaks, geomAreaInsets, theme)
} else {
verticalFixedBreaks(orientation, axisDomain, breaksProvider.fixedBreaks, theme)
}
AxisLabelsLayout.horizontalFixedBreaks(
orientation,
axisDomain,
trimmedScaleBreaks,
geomAreaInsets,
theme
)
} else {
AxisLabelsLayout.horizontalFlexBreaks(orientation, axisDomain, breaksProvider, theme)
}
return HorizontalAxisLayouter(
orientation,
axisDomain,
labelsLayout
)
}

// vertical
val labelsLayout: AxisLabelsLayout = if (breaksProvider.isFixedBreaks) {
val trimmedScaleBreaks = with(breaksProvider.fixedBreaks) {
ScaleBreaks(domainValues, transformedValues, labels.map(::trimLongValues))
if (orientation.isHorizontal) {
horizontalFlexBreaks(orientation, axisDomain, breaksProvider, theme)
} else {
verticalFlexBreaks(orientation, axisDomain, breaksProvider, theme)
}
}
AxisLabelsLayout.verticalFixedBreaks(orientation, axisDomain, trimmedScaleBreaks, theme)
} else {
AxisLabelsLayout.verticalFlexBreaks(orientation, axisDomain, breaksProvider, theme)
}
return VerticalAxisLayouter(
orientation,
axisDomain,
labelsLayout
)
}

private fun trimLongValues(text: String): String {
return if (text.length <= Axis.LABEL_MAX_LENGTH) {
text
} else {
text.take(Axis.LABEL_MAX_LENGTH) + ".."
}
return VerticalAxisLayouter(orientation, axisDomain, labelsLayout)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ScaleProviderBuilder<T> constructor(private val aes: Aes<T>) {
private var myName: String? = null
private var myBreaks: List<Any>? = null
private var myLabels: List<String>? = null
private var myLabelWidthLimit: Int? = null
private var myLabelFormat: String? = null
private var myMultiplicativeExpand: Double? = null
private var myAdditiveExpand: Double? = null
Expand Down Expand Up @@ -56,6 +57,11 @@ class ScaleProviderBuilder<T> constructor(private val aes: Aes<T>) {
return this
}

fun labelWidthLimit(v: Int): ScaleProviderBuilder<T> {
myLabelWidthLimit = v
return this
}

fun labelFormat(format: String?): ScaleProviderBuilder<T> {
myLabelFormat = format
return this
Expand Down Expand Up @@ -129,6 +135,7 @@ class ScaleProviderBuilder<T> constructor(private val aes: Aes<T>) {
private val myName: String? = b.myName

private val myLabels: List<String>? = b.myLabels?.let { ArrayList(it) }
private val myLabelWidthLimit: Int? = b.myLabelWidthLimit
private val myLabelFormat: String? = b.myLabelFormat
private val myMultiplicativeExpand: Double? = b.myMultiplicativeExpand
private val myAdditiveExpand: Double? = b.myAdditiveExpand
Expand Down Expand Up @@ -216,6 +223,9 @@ class ScaleProviderBuilder<T> constructor(private val aes: Aes<T>) {
if (myLabels != null) {
with.labels(myLabels)
}
if (myLabelWidthLimit != null) {
with.labelWidthLimit(myLabelWidthLimit)
}
if (myLabelFormat != null) {
with.labelFormatter(StringFormat.forOneArg(myLabelFormat)::format)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ object Option {
const val AES = "aesthetic"
const val BREAKS = "breaks"
const val LABELS = "labels"
const val LABLIM = "lablim"
const val EXPAND = "expand"
const val LIMITS = "limits"
const val DISCRETE_DOMAIN = "discrete"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import org.jetbrains.letsPlot.core.spec.Option.Scale.GUIDE
import org.jetbrains.letsPlot.core.spec.Option.Scale.HIGH
import org.jetbrains.letsPlot.core.spec.Option.Scale.HUE_RANGE
import org.jetbrains.letsPlot.core.spec.Option.Scale.LABELS
import org.jetbrains.letsPlot.core.spec.Option.Scale.LABLIM
import org.jetbrains.letsPlot.core.spec.Option.Scale.LIMITS
import org.jetbrains.letsPlot.core.spec.Option.Scale.LOW
import org.jetbrains.letsPlot.core.spec.Option.Scale.LUMINANCE
Expand Down Expand Up @@ -251,24 +252,31 @@ class ScaleConfig<T> constructor(
if (has(NAME)) {
b.name(getString(NAME)!!)
}

if (has(BREAKS)) {
b.breaks(getList(BREAKS).mapNotNull { it })
}

if (has(LABELS)) {
b.labels(getStringList(LABELS))
} else {
// Skip format is labels are defined
b.labelFormat(getString(FORMAT))
}

if (has(LABLIM)) {
b.labelWidthLimit(getInteger(LABLIM)!!)
}

if (has(EXPAND)) {
val list = getList(EXPAND)
if (list.isNotEmpty()) {
val multiplicativeExpand = list[0] as Number
b.multiplicativeExpand(multiplicativeExpand.toDouble())
if (list.size > 1) {
val additiveExpand = list[1] as Number
b.additiveExpand(additiveExpand.toDouble())
}
val expandList = getDoubleList(EXPAND)

expandList.getOrNull(0)?.let {
b.multiplicativeExpand(it)
}

expandList.getOrNull(1)?.let {
b.additiveExpand(it)
}
}
if (has(LIMITS)) {
Expand Down

0 comments on commit 93d2222

Please sign in to comment.