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

commonMain.jetbrains.datalore.plot.builder.guide.LegendBox.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.base.values.Color
import jetbrains.datalore.plot.base.render.svg.MultilineLabel
import jetbrains.datalore.plot.base.render.svg.SvgComponent
import jetbrains.datalore.plot.builder.layout.PlotLabelSpecFactory
import jetbrains.datalore.plot.builder.layout.PlotLayoutUtil
import jetbrains.datalore.plot.builder.layout.TextJustification
import jetbrains.datalore.plot.builder.layout.TextJustification.Companion.applyJustification
import jetbrains.datalore.plot.builder.presentation.Style
import jetbrains.datalore.plot.builder.theme.LegendTheme
import jetbrains.datalore.vis.svg.SvgGElement
import jetbrains.datalore.vis.svg.SvgNode
import jetbrains.datalore.vis.svg.SvgRectElement

abstract class LegendBox : SvgComponent() {

    var debug: Boolean = false

    abstract val spec: LegendBoxSpec

    protected val theme: LegendTheme get() = spec.theme

    private val title: String
        get() = spec.title

    val size: DoubleVector
        get() = spec.size

    private fun hasTitle(): Boolean {
        return spec.hasTitle()
    }

    override fun buildComponent() {
        if (theme.showBackground()) {
            add(SvgRectElement(spec.innerBounds).apply {
                strokeColor().set(theme.backgroundColor())
                strokeWidth().set(theme.backgroundStrokeWidth())
                fillColor().set(theme.backgroundFill())
            })
        }

        val innerGroup = SvgGElement()
        innerGroup.transform().set(buildTransform(spec.contentOrigin, 0.0))

        val l = spec.layout

        val titleBoundingRect = let {
            if (!hasTitle()) return@let DoubleRectangle(DoubleVector.ZERO, DoubleVector.ZERO)

            val titleRectSize = when {
                l.isHorizontal -> {
                    spec.contentBounds.dimension.subtract(DoubleVector(l.graphSize.x, 0.0))
                }
                else -> {
                    DoubleVector(spec.contentBounds.width, l.titleSize.y)
                }
            }
            DoubleRectangle(DoubleVector.ZERO, titleRectSize)
        }

        if (hasTitle()) {
            val label = createTitleLabel(
                titleBoundingRect,
                l.titleSize,
                theme.titleJustification()
            )
            innerGroup.children().add(label.rootGroup)
        }

        val graphGroup = SvgGElement()
        graphGroup.transform().set(buildTransform(l.graphOrigin, 0.0))
        appendGuideContent(graphGroup)
        innerGroup.children().add(graphGroup)

        if (debug) {
            // outer bounds
            val outerBounds = DoubleRectangle(DoubleVector.ZERO, spec.size)
            add(createTransparentRect(outerBounds, Color.CYAN, 1.0))
            run {
                // inner bounds
                val rect = SvgRectElement(spec.innerBounds)
                rect.fillColor().set(Color.BLACK)
                rect.strokeWidth().set(0.0)
                rect.fillOpacity().set(0.1)
                add(rect)
            }
            // content bounds
            add(createTransparentRect(spec.contentBounds, Color.DARK_MAGENTA, 1.0))
            // title bounds
            val rect = titleBoundingRect.add(spec.contentOrigin)
            add(createTransparentRect(rect, Color.MAGENTA, 1.0))
            // title bounding box
            val textDimensions = PlotLayoutUtil.textDimensions(title, PlotLabelSpecFactory.legendTitle(theme))
            val titleBoundingBox = DoubleRectangle(spec.contentBounds.left, spec.contentBounds.top, textDimensions.x, textDimensions.y)
            add(createTransparentRect(titleBoundingBox, Color.DARK_GREEN, 1.0))
        }

        add(innerGroup)
    }

    protected abstract fun appendGuideContent(contentRoot: SvgNode): DoubleVector

    private fun createTitleLabel(
        boundRect: DoubleRectangle,
        titleSize: DoubleVector,
        justification: TextJustification
    ): MultilineLabel {
        val lineHeight = PlotLabelSpecFactory.legendTitle(theme).height()

        val label = MultilineLabel(title)
        val (pos, hAnchor) = applyJustification(
            boundRect,
            textSize = titleSize,
            lineHeight,
            justification
        )
        label.addClassName(Style.LEGEND_TITLE)
        label.setHorizontalAnchor(hAnchor)
        label.setLineHeight(lineHeight)
        label.moveTo(pos)
        return label
    }

    companion object {
        fun createTransparentRect(
            bounds: DoubleRectangle,
            strokeColor: Color,
            strokeWidth: Double
        ): SvgRectElement {
            val rect = SvgRectElement(bounds)
            rect.strokeColor().set(strokeColor)
            rect.strokeWidth().set(strokeWidth)
            rect.fillOpacity().set(0.0)
            return rect
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy