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 a5acd6f
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 119 deletions.
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 a5acd6f

Please sign in to comment.