Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lablim scale parameter. Disable default axis label truncation. #949

Merged
merged 3 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
321 changes: 321 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 labelLengthLimit(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 labelLengthLimit: 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
labelLengthLimit = 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
labelLengthLimit = b.myLabelLengthLimit
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 (labelLengthLimit > 0 && str.length > labelLengthLimit) {
str.take(labelLengthLimit) + "..."
} 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 myLabelLengthLimit: Int = scale.labelLengthLimit
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 labelLengthLimit(v: Int): Scale.Builder {
myLabelLengthLimit = 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 myLabelLengthLimit: 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 labelLengthLimit(v: Int): ScaleProviderBuilder<T> {
myLabelLengthLimit = 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 myLabelLengthLimit: Int? = b.myLabelLengthLimit
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 (myLabelLengthLimit != null) {
with.labelLengthLimit(myLabelLengthLimit)
}
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.labelLengthLimit(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