Skip to content

Commit

Permalink
plot_title_position,plot_caption_position in theme() (#1118)
Browse files Browse the repository at this point in the history
* `plot_title_position`,`plot_caption_position` in `theme()`.

* Rename variable for the area to align the title/caption to.
  • Loading branch information
OLarionova-HORIS committed Jun 19, 2024
1 parent 111f5b3 commit 36f33f3
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 6 deletions.
355 changes: 355 additions & 0 deletions docs/f-24e/theme_plot_title_position.ipynb

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

### Added
- Legend title in guide_legend() and guide_colorbar().

See [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-24e/legend_title.ipynb).

- `plot_title_position` and `plot_caption_position` parameters in `theme()`.

See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-24e/theme_plot_title_position.ipynb).



### Changed
- [**breaking change**] guide_legend()/guide_colorbar() require keyword arguments for 'nrow'/'barwidth' other parameters except 'title'.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ interface PlotTheme {
fun captionMargins(): Thickness
fun plotMargins(): Thickness
fun plotInset(): Thickness

fun titlePosition(): TitlePosition
fun captionPosition(): TitlePosition
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2024. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

package org.jetbrains.letsPlot.core.plot.base.theme

/**
* Alignment of the plot title/subtitle and caption:
* - "panel" - titles/caption are aligned to the plot panels
* - "plot" - titles/caption are aligned to the entire plot (excluding margins)
*/
enum class TitlePosition {
PANEL,
PLOT
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent
import org.jetbrains.letsPlot.core.plot.base.render.svg.Text.HorizontalAnchor
import org.jetbrains.letsPlot.core.plot.base.render.svg.Text.VerticalAnchor
import org.jetbrains.letsPlot.core.plot.base.render.svg.TextLabel
import org.jetbrains.letsPlot.core.plot.base.theme.TitlePosition
import org.jetbrains.letsPlot.core.plot.base.theme.Theme
import org.jetbrains.letsPlot.core.plot.builder.coord.CoordProvider
import org.jetbrains.letsPlot.core.plot.builder.guide.Orientation
Expand Down Expand Up @@ -275,11 +276,15 @@ class PlotSvgComponent constructor(
leftMargin = margins.left
)

val titleAlignmentArea = when (plotTheme.titlePosition()) {
TitlePosition.PANEL -> geomAreaBounds
TitlePosition.PLOT -> plotOuterBounds
}
val plotTitleElementRect = title?.let {
DoubleRectangle(
geomAreaBounds.left,
titleAlignmentArea.left,
plotOuterBounds.top,
geomAreaBounds.width,
titleAlignmentArea.width,
PlotLayoutUtil.titleThickness(
title,
PlotLabelSpecFactory.plotTitle(plotTheme),
Expand All @@ -306,9 +311,9 @@ class PlotSvgComponent constructor(

val subtitleElementRect = subtitle?.let {
DoubleRectangle(
geomAreaBounds.left,
titleAlignmentArea.left,
plotTitleElementRect?.bottom ?: plotOuterBounds.top,
geomAreaBounds.width,
titleAlignmentArea.width,
PlotLayoutUtil.titleThickness(
subtitle,
PlotLabelSpecFactory.plotSubtitle(plotTheme),
Expand All @@ -333,16 +338,20 @@ class PlotSvgComponent constructor(
}
}

val captionAlignmentArea = when (plotTheme.captionPosition()) {
TitlePosition.PANEL -> geomAreaBounds
TitlePosition.PLOT -> plotOuterBounds
}
val captionElementRect = caption?.let {
val captionRectHeight = PlotLayoutUtil.titleThickness(
caption,
PlotLabelSpecFactory.plotCaption(plotTheme),
plotTheme.captionMargins()
)
DoubleRectangle(
geomAreaBounds.left,
captionAlignmentArea.left,
plotOuterBounds.bottom - captionRectHeight,
geomAreaBounds.width,
captionAlignmentArea.width,
captionRectHeight
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.TEXT
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.TITLE
import org.jetbrains.letsPlot.core.plot.base.theme.FontFamilyRegistry
import org.jetbrains.letsPlot.core.plot.base.theme.TitlePosition
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_CAPTION_POSITION
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_INSET
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_TITLE_POSITION

internal class DefaultPlotTheme(
options: Map<String, Any>,
Expand Down Expand Up @@ -99,6 +102,14 @@ internal class DefaultPlotTheme(

override fun plotInset() = getPadding(getElemValue(insetKey))

override fun titlePosition(): TitlePosition {
return getValue(PLOT_TITLE_POSITION) as TitlePosition
}

override fun captionPosition(): TitlePosition {
return getValue(PLOT_CAPTION_POSITION) as TitlePosition
}

override fun showMessage(): Boolean {
return !isElemBlank(messagesKey)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ object ThemeOption {
const val PLOT_MARGIN = "plot_margin"
const val PLOT_INSET = "plot_inset"

const val PLOT_TITLE_POSITION = "plot_title_position"
const val PLOT_CAPTION_POSITION = "plot_caption_position"

// ToDo: "text_width_scale" is used Violin demo - update.
// const val TEXT_WIDTH_FACTOR = "text_width_scale"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.jetbrains.letsPlot.core.plot.base.guide.LegendDirection
import org.jetbrains.letsPlot.core.plot.base.guide.LegendJustification
import org.jetbrains.letsPlot.core.plot.base.guide.LegendPosition
import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType
import org.jetbrains.letsPlot.core.plot.base.theme.TitlePosition
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.ThemeFlavor.Companion.SymbolicColor
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.AXIS_ONTOP
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.AXIS_TEXT
Expand All @@ -31,10 +32,12 @@ import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PANEL_INSET
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_BKGR_RECT
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_CAPTION
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_CAPTION_POSITION
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_INSET
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_MARGIN
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_SUBTITLE
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_TITLE
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_TITLE_POSITION
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.RECT
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.TEXT
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.TITLE
Expand Down Expand Up @@ -174,6 +177,9 @@ internal open class ThemeValuesBase : ThemeValues(VALUES) {
Elem.Inset.BOTTOM to 6.5,
Elem.Inset.LEFT to 6.5
),

PLOT_TITLE_POSITION to TitlePosition.PANEL,
PLOT_CAPTION_POSITION to TitlePosition.PANEL,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ object Option {
const val PLOT_MESSAGE = ThemeOption.PLOT_MESSAGE
const val PLOT_MARGIN = ThemeOption.PLOT_MARGIN
const val PLOT_INSET = ThemeOption.PLOT_INSET
const val PLOT_TITLE_POSITION = ThemeOption.PLOT_TITLE_POSITION
const val PLOT_CAPTION_POSITION = ThemeOption.PLOT_CAPTION_POSITION

// Axis
const val AXIS = ThemeOption.AXIS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ package org.jetbrains.letsPlot.core.spec.config

import org.jetbrains.letsPlot.core.plot.base.theme.ExponentFormat
import org.jetbrains.letsPlot.core.plot.base.theme.FontFamilyRegistry
import org.jetbrains.letsPlot.core.plot.base.theme.TitlePosition
import org.jetbrains.letsPlot.core.plot.base.theme.Theme
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.ThemeUtil
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.ELEMENT_BLANK
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PANEL_INSET
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_CAPTION_POSITION
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_INSET
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_MARGIN
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.PLOT_TITLE_POSITION
import org.jetbrains.letsPlot.core.spec.Option

class ThemeConfig constructor(
Expand All @@ -34,6 +37,7 @@ class ThemeConfig constructor(
value = convertMargins(key, value)
value = convertInset(key, value)
value = convertExponentFormat(key, value)
value = convertTitlePosition(key, value)
LegendThemeConfig.convertValue(key, value)
}

Expand Down Expand Up @@ -170,5 +174,20 @@ class ThemeConfig constructor(
else -> value
}
}

private fun convertTitlePosition(key: String, value: Any): Any {
return when (key) {
PLOT_TITLE_POSITION, PLOT_CAPTION_POSITION -> {
when (value) {
"panel" -> TitlePosition.PANEL
"plot" -> TitlePosition.PLOT
else -> throw IllegalArgumentException(
"Illegal value: '$value', $key. Expected values are: 'panel' or 'plot'."
)
}
}
else -> value
}
}
}
}
11 changes: 11 additions & 0 deletions python-package/lets_plot/plot/theme_.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def theme(*,
plot_margin=None,
plot_inset=None,

plot_title_position=None,
plot_caption_position=None,

strip_background=None, # ToDo: x/y
strip_text=None, # ToDo: x/y
# ToDo: strip.placement
Expand Down Expand Up @@ -232,6 +235,14 @@ def theme(*,
- a list of four numbers - the insets are applied to the top, right, bottom and left in that order.
It is acceptable to use None for any side; in this case, the default value for the plot inset side will be used.
plot_title_position : {'panel', 'plot'}, default='panel'
Alignment of the plot title/subtitle.
A value of 'panel' means that title and subtitle are aligned to the plot panels.
A value of 'plot' means that title and subtitle are aligned to the entire plot (excluding margins).
plot_caption_position : {'panel', 'plot'}, default='panel'
Alignment of the plot caption.
A value of 'panel' means that title and subtitle are aligned to the plot panels.
A value of 'plot' means that title and subtitle are aligned to the entire plot (excluding margins).
strip_background : str or dict
Background of facet labels.
Set 'blank' or result of `element_blank()` to draw nothing.
Expand Down

0 comments on commit 36f33f3

Please sign in to comment.