All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.jetbrains.datalore.plot.builder.guide.LegendComponentLayout.kt Maven / Gradle / Ivy

There is a newer version: 4.5.3-alpha1
Show newest version
/*
 * Copyright (c) 2020. 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.builder.guide

import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.plot.builder.layout.GeometryUtil
import jetbrains.datalore.plot.builder.layout.PlotLabelSpecFactory
import jetbrains.datalore.plot.builder.theme.LegendTheme
import kotlin.math.max

abstract class LegendComponentLayout(
    title: String,
    protected val breaks: List,
    val keySizes: List,
    legendDirection: LegendDirection,
    theme: LegendTheme
) : LegendBoxLayout(title, legendDirection, theme) {

    private var myContentSize: DoubleVector? = null
    private val myKeyLabelBoxes = ArrayList()
    private val myLabelBoxes = ArrayList()

    var isFillByRow = false
    var rowCount = 0
        set(rowCount) {
            check(rowCount > 0) { "Row count must be greater than 0, was $rowCount" }
            field = rowCount
        }
    var colCount = 0
        set(colCount) {
            check(colCount > 0) { "Col count must be greater than 0, was $colCount" }
            field = colCount
        }

    override val graphSize: DoubleVector
        get() {
            ensureInited()
            return myContentSize!!
        }

    val keyLabelBoxes: List
        get() {
            ensureInited()
            return myKeyLabelBoxes
        }

    val labelBoxes: List
        get() {
            ensureInited()
            return myLabelBoxes
        }

    private fun ensureInited() {
        if (myContentSize == null) {
            doLayout()
        }
    }

    private fun doLayout() {
        val labelHeight = PlotLabelSpecFactory.legendItem(theme).height()
        val labelLeftMargin = PlotLabelSpecFactory.legendItem(theme).width(PlotLabelSpecFactory.DISTANCE_TO_LABEL_IN_CHARS) / 2

        val contentOrigin = DoubleVector.ZERO
        var breakBoxBounds: DoubleRectangle? = null
        for (i in breaks.indices) {
            val labelSize = labelSize(i)
            val keySize = keySizes[i]
            val labelVOffset = (keySize.y - labelHeight) / 2
            val labelHOffset = keySize.x + labelLeftMargin
            val breakBoxSize = DoubleVector(labelHOffset + labelSize.x, keySize.y)
            breakBoxBounds = DoubleRectangle(
                breakBoxBounds?.let { breakBoxOrigin(i, it) } ?: contentOrigin,
                breakBoxSize
            )

            myKeyLabelBoxes.add(breakBoxBounds)
            myLabelBoxes.add(
                DoubleRectangle(
                    labelHOffset, labelVOffset,
                    labelSize.x, labelSize.y
                )
            )
        }

        myContentSize = GeometryUtil.union(DoubleRectangle(contentOrigin, DoubleVector.ZERO), myKeyLabelBoxes).dimension
    }

    protected abstract fun breakBoxOrigin(index: Int, prevBreakBoxBounds: DoubleRectangle): DoubleVector

    protected abstract fun labelSize(index: Int): DoubleVector

    private class MyHorizontal internal constructor(
        title: String,
        breaks: List,
        keySizes: List,
        theme: LegendTheme
    ) : LegendComponentLayout(
        title, breaks, keySizes,
        LegendDirection.HORIZONTAL,
        theme
    ) {
        init {
            colCount = breaks.size
            rowCount = 1
        }

        override fun breakBoxOrigin(index: Int, prevBreakBoxBounds: DoubleRectangle): DoubleVector {
            return DoubleVector(prevBreakBoxBounds.right, 0.0)
        }

        override fun labelSize(index: Int): DoubleVector {
            val label = breaks[index].label
            return DoubleVector(PlotLabelSpecFactory.legendItem(theme).width(label), PlotLabelSpecFactory.legendItem(theme).height())
        }
    }

    private class MyHorizontalMultiRow internal constructor(
        title: String,
        breaks: List,
        keySizes: List,
        theme: LegendTheme
    ) : MyMultiRow(
        title, breaks, keySizes,
        LegendDirection.HORIZONTAL,
        theme
    ) {
        init {
            colCount = breaks.size
            rowCount = 1
        }
    }

    private class MyVertical internal constructor(
        title: String,
        breaks: List,
        keySizes: List,
        theme: LegendTheme
    ) : MyMultiRow(
        title, breaks, keySizes,
        LegendDirection.VERTICAL,
        theme
    ) {
        init {
            colCount = 1
            rowCount = breaks.size
        }
    }

    private abstract class MyMultiRow internal constructor(
        title: String,
        breaks: List,
        keySizes: List,
        legendDirection: LegendDirection,
        theme: LegendTheme
    ) : LegendComponentLayout(title, breaks, keySizes, legendDirection, theme) {
        private var myMaxLabelWidth = 0.0

        init {
            for (br in breaks) {
                myMaxLabelWidth = max(myMaxLabelWidth, PlotLabelSpecFactory.legendItem(theme).width(br.label))
            }
        }

        override fun breakBoxOrigin(index: Int, prevBreakBoxBounds: DoubleRectangle): DoubleVector {
            if (isFillByRow) {
                return if (index % colCount == 0) {
                    DoubleVector(0.0, prevBreakBoxBounds.bottom)
                } else DoubleVector(prevBreakBoxBounds.right, prevBreakBoxBounds.top)
            }

            // fill by column
            return if (index % rowCount == 0) {
                DoubleVector(prevBreakBoxBounds.right, 0.0)
            } else DoubleVector(prevBreakBoxBounds.left, prevBreakBoxBounds.bottom)

        }

        override fun labelSize(index: Int): DoubleVector {
            return DoubleVector(myMaxLabelWidth, PlotLabelSpecFactory.legendItem(theme).height())
        }
    }

    companion object {
        fun horizontal(title: String, breaks: List, keySizes: List, theme: LegendTheme): LegendComponentLayout {
            return MyHorizontal(
                title,
                breaks,
                keySizes,
                theme
            )
        }

        fun horizontalMultiRow(title: String, breaks: List, keySizes: List, theme: LegendTheme): LegendComponentLayout {
            return MyHorizontalMultiRow(
                title,
                breaks,
                keySizes,
                theme
            )
        }

        fun vertical(title: String, breaks: List, keySizes: List, theme: LegendTheme): LegendComponentLayout {
            return MyVertical(
                title,
                breaks,
                keySizes,
                theme
            )
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy