Skip to content

Commit

Permalink
Fix #430 : Unreadable breaks on axis
Browse files Browse the repository at this point in the history
  • Loading branch information
alshan committed Sep 24, 2021
1 parent f7c446f commit 1347b9a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 67 deletions.
3 changes: 2 additions & 1 deletion future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
- Tooltips for discrete variables: add the dependence of the tooltip on the number of factors.
The X-axis tooltip is always shown for discrete data.
- map_join doesn't work when join variables with same names without mappings [[#428](https://github.com/JetBrains/lets-plot/issues/428)]
- Zoom without clipping breaks tooltips [[#373](https://github.com/JetBrains/lets-plot/issues/373)].
- Zoom without clipping breaks tooltips [[#373](https://github.com/JetBrains/lets-plot/issues/373)].
- Unreadable breaks on axis [[#430](https://github.com/JetBrains/lets-plot/issues/430)].
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,13 @@

package jetbrains.datalore.plot

import jetbrains.datalore.plot.base.DataFrame
import jetbrains.datalore.plot.base.data.DataFrameUtil

object FeatureSwitch {

const val PLOT_DEBUG_DRAWING = false
const val LEGEND_DEBUG_DRAWING = false
private const val PRINT_DEBUG_LOGS = false

private const val PRINT_ENCODED_DATA_SUMMARY = false

fun printEncodedDataSummary(header: String, dataSpec: Map<String, Any>) {
@Suppress("ConstantConditionIf")
if (PRINT_ENCODED_DATA_SUMMARY) {
printEncodedDataSummary(
header,
DataFrameUtil.fromMap(dataSpec)
)
}
}

fun isDebugLogEnabled(): Boolean {
return PRINT_DEBUG_LOGS
}

private fun printEncodedDataSummary(header: String, df: DataFrame) {
@Suppress("ConstantConditionIf")
if (PRINT_ENCODED_DATA_SUMMARY) {
//ToDo:
//check(!GWT.isClient()) {"Not expected on client"}
val summary = DataFrameUtil.getSummaryText(df)
println(header)
println(summary)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package jetbrains.datalore.plot.builder.guide

import jetbrains.datalore.base.gcommon.collect.ClosedRange
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.base.observable.event.EventSource
import jetbrains.datalore.base.observable.event.EventSources
Expand All @@ -17,6 +18,7 @@ import jetbrains.datalore.plot.base.render.svg.SvgComponent
import jetbrains.datalore.plot.base.render.svg.TextLabel
import jetbrains.datalore.plot.base.render.svg.TextLabel.HorizontalAnchor.*
import jetbrains.datalore.plot.base.render.svg.TextLabel.VerticalAnchor.*
import jetbrains.datalore.plot.builder.presentation.PlotLabelSpec
import jetbrains.datalore.plot.builder.presentation.Style
import jetbrains.datalore.vis.svg.SvgGElement
import jetbrains.datalore.vis.svg.SvgLineElement
Expand All @@ -26,9 +28,11 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {

val breaks: Property<List<Double>?> = ValueProperty(null)
val labels: Property<List<String>?> = ValueProperty(null)

// layout
val tickLabelRotationDegree: Property<Double> = ValueProperty(0.0)
val tickLabelHorizontalAnchor: Property<TextLabel.HorizontalAnchor>

// todo: minorBreaks
val tickLabelVerticalAnchor: Property<TextLabel.VerticalAnchor>
val tickLabelSmallFont: Property<Boolean> = ValueProperty(false)
Expand All @@ -42,6 +46,7 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
val tickMarkPadding: Property<Double> = ValueProperty(3.0)
private val length = ValueProperty<Double?>(null)
private val orientation = ValueProperty<Orientation?>(null)

// theme
private val myTickMarksEnabled = ValueProperty(true)
private val myTickLabelsEnabled = ValueProperty(true)
Expand Down Expand Up @@ -74,7 +79,8 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
tickLabelVerticalAnchor = ValueProperty(defTickLabelVerticalAnchor(orientation))

@Suppress("UNCHECKED_CAST")
fun <T> EventSource<in PropertyChangeEvent<T>>.asPropertyChangedEventSource() = this as EventSource<PropertyChangeEvent<*>>
fun <T> EventSource<in PropertyChangeEvent<T>>.asPropertyChangedEventSource() =
this as EventSource<PropertyChangeEvent<*>>

EventSources.composite(
this.length.asPropertyChangedEventSource(),
Expand Down Expand Up @@ -152,16 +158,19 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
}
}

var i = 0
for (br in breaks) {

val labelsCleaner = TickLabelsCleaner(orientation.get()!!.isHorizontal)

for ((i, br) in breaks.withIndex()) {
val addGridLine = br >= gridLineMinPos && br <= gridLineMaxPos
val label = labels[i % labels.size]
val labelOffset = tickLabelOffset(i)
i++
val group = buildTick(
label,
labelOffset,
if (addGridLine) gridLineLength.get() else 0.0)
label,
labelOffset,
if (addGridLine) gridLineLength.get() else 0.0,
skipLabel = !labelsCleaner.beforeAddLabel(br, tickLabelRotationDegree.get())
)

when (orientation.get()) {
Orientation.LEFT, Orientation.RIGHT -> transformTranslate(group, 0.0, br)
Expand All @@ -180,7 +189,12 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
}
}

private fun buildTick(label: String, labelOffset: DoubleVector, gridLineLength: Double): SvgGElement {
private fun buildTick(
label: String,
labelOffset: DoubleVector,
gridLineLength: Double,
skipLabel: Boolean
): SvgGElement {

var tickMark: SvgLineElement? = null
if (tickMarksEnabled().get()) {
Expand All @@ -190,7 +204,7 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
}

var tickLabel: TextLabel? = null
if (tickLabelsEnabled().get()) {
if (tickLabelsEnabled().get() && !skipLabel) {
tickLabel = TextLabel(label)
reg(bindOneWay(tickColor, tickLabel.textColor()))
}
Expand Down Expand Up @@ -312,5 +326,40 @@ class AxisComponent(length: Double, orientation: Orientation) : SvgComponent() {
fun axisLineEnabled(): Property<Boolean> {
return myAxisLineEnabled
}


private class TickLabelsCleaner(val horizontalAxis: Boolean) {
private val filledRanges = ArrayList<ClosedRange<Double>>()

fun beforeAddLabel(loc: Double, rotationDegree: Double): Boolean {
if (!isRelevant(rotationDegree)) return true

val len = PlotLabelSpec.AXIS_TICK.height()

// find overlap
if (filledRanges.any { it.contains(loc) || it.contains(loc + len) }) {
// overlap - don't add this label
return false
}

filledRanges.add(ClosedRange(loc, loc + len))
return true
}

private fun isRelevant(rotationDegree: Double): Boolean {
return when {
horizontalAxis -> isVertical(rotationDegree)
else -> isHorizontal(rotationDegree)
}
}

private fun isHorizontal(rotationDegree: Double): Boolean {
return rotationDegree % 180 == 0.0
}

private fun isVertical(rotationDegree: Double): Boolean {
return (rotationDegree / 90) % 2 == 1.0
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,5 @@ internal class AdaptableAxisBreaksProvider(
@Suppress("UnnecessaryVariable")
val scaleBreaks = breaksGenerator.generateBreaks(domainAfterTransform, targetCount)
return scaleBreaks
// return ScaleBreaks(
// scaleBreaks.domainValues,
// scaleBreaks.transformedValues,
// scaleBreaks.labels
// )
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,10 @@ package jetbrains.datalore.plot.builder.layout.axis

import jetbrains.datalore.plot.base.scale.ScaleBreaks

//class FixedAxisBreaksProvider(
// domainBreaks: List<Any>,
// transformedBreaks: List<Double>,
// labels: List<String>
//) : AxisBreaksProvider {
class FixedAxisBreaksProvider(
override val fixedBreaks: ScaleBreaks
) : AxisBreaksProvider {

// override val fixedBreaks: ScaleBreaks = ScaleBreaks(
// domainBreaks,
// transformedBreaks,
// labels
// )

override val isFixedBreaks: Boolean
get() = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import jetbrains.datalore.base.gcommon.collect.ClosedRange
import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.plot.base.scale.ScaleBreaks
import jetbrains.datalore.plot.builder.guide.Orientation
import jetbrains.datalore.plot.builder.guide.Orientation.*
import jetbrains.datalore.plot.builder.layout.axis.AxisBreaksProvider
import jetbrains.datalore.plot.builder.theme.AxisTheme
Expand Down Expand Up @@ -51,7 +52,7 @@ internal object BreakLabelsLayoutUtil {
}

fun doLayoutVerticalAxisLabels(
orientation: jetbrains.datalore.plot.builder.guide.Orientation,
orientation: Orientation,
breaks: ScaleBreaks,
axisDomain: ClosedRange<Double>,
axisMapper: (Double?) -> Double?,
Expand All @@ -60,12 +61,11 @@ internal object BreakLabelsLayoutUtil {

val axisBounds = when {
theme.showTickLabels() -> {
val labelsBounds =
verticalAxisLabelsBounds(
breaks,
axisDomain,
axisMapper
)
val labelsBounds = verticalAxisLabelsBounds(
breaks,
axisDomain,
axisMapper
)
applyLabelsOffset(
labelsBounds,
theme.tickLabelDistance(),
Expand Down Expand Up @@ -132,18 +132,16 @@ internal object BreakLabelsLayoutUtil {
axisDomain: ClosedRange<Double>,
axisMapper: (Double?) -> Double?
): DoubleRectangle {
val maxLength =
maxLength(breaks.labels)
val maxLength = maxLength(breaks.labels)
val maxLabelWidth = AxisLabelsLayout.TICK_LABEL_SPEC.width(maxLength)
var y1 = 0.0
var y2 = 0.0
if (!breaks.isEmpty) {
val axisBreaks =
mapToAxis(
breaks.transformedValues,
axisDomain,
axisMapper
)
val axisBreaks = mapToAxis(
breaks.transformedValues,
axisDomain,
axisMapper
)

y1 = min(axisBreaks[0], axisBreaks.last())
y2 = max(axisBreaks[0], axisBreaks.last())
Expand Down

0 comments on commit 1347b9a

Please sign in to comment.