commonMain.jetbrains.datalore.plot.builder.layout.FacetedPlotLayout.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lets-plot-common Show documentation
Show all versions of lets-plot-common Show documentation
Lets-Plot JVM package without rendering part
/*
* Copyright (c) 2022. 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.layout
import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.plot.builder.assemble.PlotFacets
import jetbrains.datalore.plot.builder.coord.CoordProvider
import jetbrains.datalore.plot.builder.layout.FacetedPlotLayoutUtil.geomOffsetsByCol
import jetbrains.datalore.plot.builder.layout.FacetedPlotLayoutUtil.geomOffsetsByRow
import jetbrains.datalore.plot.builder.layout.facet.FixedScalesTilesLayouter
import jetbrains.datalore.plot.builder.layout.facet.FreeScalesTilesLayouter
import jetbrains.datalore.plot.builder.theme.AxisTheme
internal class FacetedPlotLayout constructor(
private val facets: PlotFacets,
private val layoutProviderByTile: List,
private val showFacetStrip: Boolean,
private val hAxisTheme: AxisTheme,
private val vAxisTheme: AxisTheme,
) : PlotLayoutBase() {
private val totalAddedHSize: Double = PANEL_PADDING * (facets.colCount - 1)
private val totalAddedVSize: Double = PANEL_PADDING * (facets.rowCount - 1)
init {
val padding = 10.0
val leftPadding = if (!vAxisTheme.showTitle() && !vAxisTheme.showLabels()) padding else 0.0
val bottomPadding = if (!hAxisTheme.showTitle() && !hAxisTheme.showLabels()) padding else 0.0
setPadding(top = padding, right = padding, bottomPadding, leftPadding)
require(facets.isDefined) { "Undefined facets." }
}
override fun doLayout(preferredSize: DoubleVector, coordProvider: CoordProvider): PlotLayoutInfo {
var tilesAreaSize = DoubleVector(
preferredSize.x - (paddingLeft + paddingRight),
preferredSize.y - (paddingTop + paddingBottom)
)
val facetTiles = facets.tileInfos()
if (showFacetStrip) {
val tileColLabCounts = facetTiles.firstOrNull { it.colLabs.isNotEmpty() }?.colLabs?.size ?: 0
val tileWithColLabInRowCount = facetTiles
.filter { it.colLabs.isNotEmpty() }
.distinctBy { it.row }.count()
val totalAddedHeight = facetColHeadHeight(tileColLabCounts) * tileWithColLabInRowCount
val labsInRow = if (facetTiles.any { it.rowLab != null }) 1 else 0
val labsTotalDim = DoubleVector(labsInRow * FACET_TAB_HEIGHT, totalAddedHeight)
tilesAreaSize = tilesAreaSize.subtract(labsTotalDim)
}
val layoutInfos: List = if (facets.freeHScale || facets.freeVScale) {
FreeScalesTilesLayouter.createTileLayoutInfos(
tilesAreaSize,
facets,
layoutProviderByTile,
totalAddedHSize,
totalAddedVSize,
coordProvider,
hAxisTheme = hAxisTheme,
vAxisTheme = vAxisTheme,
)
} else {
FixedScalesTilesLayouter.createTileLayoutInfos(
tilesAreaSize,
facets,
layoutProviderByTile,
totalAddedHSize,
totalAddedVSize,
coordProvider,
hAxisTheme = hAxisTheme,
vAxisTheme = vAxisTheme,
)
}
// Create final plot tiles layout infos.
// Align geom areas of tiles.
// absolute offsets of tile gom areas.
val geomOffsetByCol = geomOffsetsByCol(layoutInfos, facetTiles, PANEL_PADDING, facets.colCount)
val geomOffsetByRow = geomOffsetsByRow(
layoutInfos, facetTiles, showFacetStrip,
PANEL_PADDING, facets.rowCount
)
val tileBoundsList = ArrayList()
val geomOuterBoundsList = ArrayList()
for ((index, facetTile) in facetTiles.withIndex()) {
val layoutInfo = layoutInfos[index]
val col = facetTile.col
val row = facetTile.row
val geomX = geomOffsetByCol[col]
val geomY = geomOffsetByRow[row]
val outerGeomSize = layoutInfo.geomOuterBounds.dimension
// Tile width
val tileLabelWidth = if (facetTile.rowLab != null && showFacetStrip) {
FACET_TAB_HEIGHT // one label on the left side.
} else {
0.0
}
val axisWidth = if (facetTile.hasVAxis) {
layoutInfo.axisThicknessY()
} else {
0.0
}
val tileX = geomX - axisWidth
val tileWidth = outerGeomSize.x + axisWidth + tileLabelWidth
// Tile height
val tileLabelHeight = if (showFacetStrip) {
facetColHeadHeight(facetTile.colLabs.size)
} else {
0.0
}
val axisHeight = if (facetTile.hasHAxis) {
layoutInfo.axisThicknessX()
} else {
0.0
}
val tileY = geomY - tileLabelHeight
val tileHeight = outerGeomSize.y + tileLabelHeight + axisHeight
// if (col == 0) {
// println("[$row][$tileY] $tileHeight = ${geomSize.y} + $tileLabelHeight + $axisHeight")
// }
// Absolute bounds...
val tileBounds = DoubleRectangle(
DoubleVector(tileX, tileY),
DoubleVector(tileWidth, tileHeight)
)
val geomOuterBounds = DoubleRectangle(
DoubleVector(geomX, geomY),
outerGeomSize
)
tileBoundsList.add(tileBounds)
geomOuterBoundsList.add(geomOuterBounds)
}
val tilesAreaOrigin = tileBoundsList
.reduce { b0, b1 -> b0.union(b1) }
.origin
// Normalize origin of tilesAreaBounds.
val originDelta = tilesAreaOrigin.negate()
val tilesPaddingLeftTop = DoubleVector(paddingLeft, paddingTop)
val finalLayoutInfos = ArrayList()
for ((index, facetTile) in facetTiles.withIndex()) {
val layoutInfo = layoutInfos[index]
val geomInnerBoundsOffset = layoutInfo.geomInnerBounds.origin
.subtract(layoutInfo.geomOuterBounds.origin)
val tileBounds = tileBoundsList[index]
val geomOuterBounds = geomOuterBoundsList[index]
val geomInnerBounds = DoubleRectangle(
geomOuterBounds.origin.add(geomInnerBoundsOffset),
layoutInfo.geomInnerBounds.dimension
)
val newLayoutInfo = TileLayoutInfo(
tilesPaddingLeftTop,
bounds = tileBounds.add(originDelta),
geomOuterBounds = geomOuterBounds.add(originDelta),
geomInnerBounds = geomInnerBounds.add(originDelta),
layoutInfo.hAxisInfo,
layoutInfo.vAxisInfo,
hAxisShown = facetTile.hasHAxis,
vAxisShown = facetTile.hasVAxis,
trueIndex = facetTile.trueIndex
)
finalLayoutInfos.add(
if (showFacetStrip) {
newLayoutInfo.withFacetLabels(facetTile.colLabs, facetTile.rowLab)
} else {
newLayoutInfo
}
)
}
val combinedTilesSize = finalLayoutInfos.map { it.bounds }.reduce { b0, b1 -> b0.union(b1) }.dimension
val plotSize = combinedTilesSize
.add(tilesPaddingLeftTop)
.add(DoubleVector(paddingRight, paddingBottom))
return PlotLayoutInfo(finalLayoutInfos, plotSize)
}
companion object {
const val FACET_TAB_HEIGHT = 30.0
const val FACET_H_PADDING = 0
const val FACET_V_PADDING = 6 //5
private const val PANEL_PADDING = 10.0
fun facetColLabelSize(colWidth: Double): DoubleVector {
return DoubleVector(colWidth - FACET_H_PADDING * 2, FACET_TAB_HEIGHT - FACET_V_PADDING * 2.0)
}
fun facetColHeadHeight(labCount: Int): Double {
return if (labCount > 0) {
facetColLabelSize(0.0).y * labCount + FACET_V_PADDING * 2
} else {
0.0
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy