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

commonMain.jetbrains.datalore.plot.builder.assemble.PlotAssembler.kt Maven / Gradle / Ivy

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

import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.plot.base.Aes
import jetbrains.datalore.plot.base.PlotContext
import jetbrains.datalore.plot.base.Scale
import jetbrains.datalore.plot.base.ScaleMapper
import jetbrains.datalore.plot.builder.FrameOfReferenceProvider
import jetbrains.datalore.plot.builder.GeomLayer
import jetbrains.datalore.plot.builder.MarginalLayerUtil
import jetbrains.datalore.plot.builder.PlotSvgComponent
import jetbrains.datalore.plot.builder.coord.CoordProvider
import jetbrains.datalore.plot.builder.frame.BogusFrameOfReferenceProvider
import jetbrains.datalore.plot.builder.frame.SquareFrameOfReferenceProvider
import jetbrains.datalore.plot.builder.guide.Orientation
import jetbrains.datalore.plot.builder.layout.GeomMarginsLayout
import jetbrains.datalore.plot.builder.layout.LegendBoxInfo
import jetbrains.datalore.plot.builder.layout.PlotLayout
import jetbrains.datalore.plot.builder.layout.TileLayoutProvider
import jetbrains.datalore.plot.builder.layout.tile.LiveMapAxisTheme
import jetbrains.datalore.plot.builder.layout.tile.LiveMapTileLayoutProvider
import jetbrains.datalore.plot.builder.presentation.Style
import jetbrains.datalore.plot.builder.theme.Theme
import jetbrains.datalore.vis.StyleSheet

class PlotAssembler private constructor(
    private val layersByTile: List>,
    private val scaleMap: TypedScaleMap,
    private val scaleMappersNP: Map, ScaleMapper<*>>,
    private val coordProvider: CoordProvider,
    private val xAxisOrientation: Orientation,
    private val yAxisOrientation: Orientation,
    private val theme: Theme
) {

    private val scaleXProto: Scale = scaleMap.get(Aes.X)
    private val scaleYProto: Scale = scaleMap.get(Aes.Y)

    val coreLayersByTile: List> = layersByTile.map { layers ->
        layers.filterNot { it.isMarginal }
    }
    private val marginalLayersByTile: List> = layersByTile.map { layers ->
        layers.filter { it.isMarginal }.filterNot { it.isLiveMap }
    }

    val containsLiveMap: Boolean = coreLayersByTile.flatten().any(GeomLayer::isLiveMap)

    var facets: PlotFacets = PlotFacets.undefined()
    var title: String? = null
    var subtitle: String? = null
    var caption: String? = null
    var guideOptionsMap: Map, GuideOptions> = HashMap()

    private var legendsEnabled = true
    private var interactionsEnabled = true


    private fun hasLayers(): Boolean {
        return coreLayersByTile.any { it.isNotEmpty() }
    }

    fun createPlot(): PlotSvgComponent {
        require(hasLayers()) { "No layers in plot" }

        val styleSheet: StyleSheet = Style.fromTheme(theme, coordProvider.flipped)

        // ToDo: transformed ranges by aes
        val plotContext: PlotContext = PlotAssemblerPlotContext(layersByTile, scaleMap)

        val legendsBoxInfos = when {
            legendsEnabled -> PlotAssemblerUtil.createLegends(
                plotContext,
                scaleMappersNP,
                guideOptionsMap,
                theme.legend()
            )

            else -> emptyList()
        }

        return if (containsLiveMap) {
            // build 'live map' plot:
            //  - skip X/Y scale training
            //  - ignore coord provider
            //  - plot layout without axes
            val layoutProviderByTile = coreLayersByTile.map {
                LiveMapTileLayoutProvider()
            }
            val plotLayout = PlotAssemblerUtil.createPlotLayout(
                layoutProviderByTile,
                facets,
                theme.facets(),
                hAxisTheme = LiveMapAxisTheme(),
                vAxisTheme = LiveMapAxisTheme(),
            )
            val frameProviderByTile = coreLayersByTile.map {
                BogusFrameOfReferenceProvider()
            }
            createPlot(frameProviderByTile, plotLayout, legendsBoxInfos, styleSheet, plotContext)
        } else {
            val flipAxis = coordProvider.flipped
            val domainsXYByTile = PositionalScalesUtil.computePlotXYTransformedDomains(
                coreLayersByTile,
                scaleXProto,
                scaleYProto,
                facets
            )
            val (hScaleProto, vScaleProto) = when (flipAxis) {
                true -> scaleYProto to scaleXProto
                else -> scaleXProto to scaleYProto
            }

            val (hAxisOrientation, vAxisOrientation) = when (flipAxis) {
                true -> yAxisOrientation.flip() to xAxisOrientation.flip()
                else -> xAxisOrientation to yAxisOrientation
            }

            // Marginal layers.
            // Marginal layers share "marginal domain" and layout across all tiles.
            val marginalLayers = marginalLayersByTile.flatten()
            val domainByMargin = MarginalLayerUtil.marginalDomainByMargin(marginalLayers, scaleXProto, scaleYProto)
            val marginsLayout: GeomMarginsLayout = GeomMarginsLayout.create(marginalLayers)

            // Create frame of reference provider for each tile.
            val frameProviderByTile: List =
                domainsXYByTile.map { (xDomain, yDomain) ->
                    val adjustedDomain = coordProvider.adjustDomain(DoubleRectangle(xDomain, yDomain))
                    SquareFrameOfReferenceProvider(
                        hScaleProto, vScaleProto,
                        adjustedDomain,
                        flipAxis,
                        hAxisOrientation, vAxisOrientation,
                        theme,
                        marginsLayout,
                        domainByMargin
                    )
                }

            val layoutProviderByTile: List = frameProviderByTile.map {
                it.createTileLayoutProvider()
            }
            val plotLayout = PlotAssemblerUtil.createPlotLayout(
                layoutProviderByTile,
                facets,
                theme.facets(),
                hAxisTheme = theme.horizontalAxis(flipAxis),
                vAxisTheme = theme.verticalAxis(flipAxis),
            )

            createPlot(frameProviderByTile, plotLayout, legendsBoxInfos, styleSheet, plotContext)
        }
    }

    private fun createPlot(
        frameProviderByTile: List,
        plotLayout: PlotLayout,
        legendBoxInfos: List,
        styleSheet: StyleSheet,
        plotContext: PlotContext
    ): PlotSvgComponent {

        return PlotSvgComponent(
            title = title,
            subtitle = subtitle,
            caption = caption,
            coreLayersByTile = coreLayersByTile,
            marginalLayersByTile = marginalLayersByTile,
            plotLayout = plotLayout,
            frameProviderByTile = frameProviderByTile,
            coordProvider = coordProvider,
            legendBoxInfos = legendBoxInfos,
            interactionsEnabled = interactionsEnabled,
            theme = theme,
            styleSheet = styleSheet,
            plotContext = plotContext
        )
    }

    fun disableLegends() {
        legendsEnabled = false
    }

    fun disableInteractions() {
        interactionsEnabled = false
    }

    companion object {
        fun demoAndTest(
            plotLayers: List,
            scaleMap: TypedScaleMap,
            scaleMappersNP: Map, ScaleMapper<*>>,
            coordProvider: CoordProvider,
            theme: Theme,
            xAxisOrientation: Orientation = Orientation.BOTTOM,
            yAxisOrientation: Orientation = Orientation.LEFT,
        ): PlotAssembler {
            val layersByTile = ArrayList>()
            layersByTile.add(plotLayers)
            return multiTile(
                layersByTile,
                scaleMap,
                scaleMappersNP,
                coordProvider,
                xAxisOrientation,
                yAxisOrientation,
                theme
            )
        }

        fun multiTile(
            layersByTile: List>,
            scaleMap: TypedScaleMap,
            scaleMappersNP: Map, ScaleMapper<*>>,
            coordProvider: CoordProvider,
            xAxisOrientation: Orientation,
            yAxisOrientation: Orientation,
            theme: Theme
        ): PlotAssembler {
            return PlotAssembler(
                layersByTile,
                scaleMap,
                scaleMappersNP,
                coordProvider,
                xAxisOrientation,
                yAxisOrientation,
                theme
            )
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy