Skip to content

Commit

Permalink
Add element_geom(pen, brush, paper) (#836)
Browse files Browse the repository at this point in the history
* `theme_none()`: use grey colors for elements.

* Add theme keys for 'pen', 'brush', 'paper'. Add ColorTheme to access these values.

* Use `ColorTheme` colors as default in NamedSystemColors.

* Update demo.

* Access to colors via `ColorTheme` (also use for color defaults of geometries).

* Use `color.paper()` in `GeomContext`.

* Check ordering in theme setting to choose which colors to use.

* Update demo.

* Fix key order in plot spec.

* Minor.

* Add `element_geom()` to python.
Update notebook.

* Remove extra import.

* Add ThemeConfigTest.

* Update future_changes.md.

* Reformat code.
  • Loading branch information
OLarionova-HORIS committed Aug 4, 2023
1 parent c4a69cb commit 161af49
Show file tree
Hide file tree
Showing 27 changed files with 732 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class EmptyGeomContext : GeomContext {
override val flipped: Boolean = false
override val targetCollector: GeomTargetCollector = NullGeomTargetCollector()
override val annotations: Annotations? = null
override val plotBackground: Color = Color.WHITE
override val backgroundColor: Color = Color.WHITE

override fun getResolution(aes: org.jetbrains.letsPlot.core.plot.base.Aes<Double>): Double {
throw IllegalStateException("Not available in an empty geom context")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,90 @@ import demoAndTestShared.parsePlotSpec
class NamedSystemColors {
fun plotSpecList(): List<MutableMap<String, Any>> {
return listOf(
withFlavor(null),
withFlavor("solarized_light"),
withFlavor("solarized_dark"),
withFlavor("solarized_dark", background = "dark_blue"),
pieChart(),
pieChart(flavor = "darcula"),
pieChart(flavor = "solarized_light"),
pieChart(flavor = "solarized_dark"),
pieChart(flavor = "high_contrast_light"),
pieChart(flavor = "high_contrast_dark"),
pieChart(flavor = "darcula", withCustomColors = true, flavorOverCustomColors = true),
pieChart(flavor = "darcula", withCustomColors = true, flavorOverCustomColors = false)
)
}

private fun withFlavor(flavor: String?, background: String? = null): MutableMap<String, Any> {
private fun pieChart(
theme: String = "grey",
flavor: String? = null,
withCustomColors: Boolean = false,
flavorOverCustomColors: Boolean = true
): MutableMap<String, Any> {
val flavorOpts = flavor?.let { "'flavor': '$flavor'" } ?: ""
val customColors = if (withCustomColors) {
"'geom': { 'pen': 'red', 'paper': 'green', 'brush': 'blue' }"
} else {
""
}

val themeSettings = (
listOf(
"'name': '$theme'"
) + if (flavorOverCustomColors) {
listOf(customColors, flavorOpts)
} else {
listOf(flavorOpts, customColors)
})
.filter(String::isNotEmpty)
.joinToString()

val spec = """
{
'data': {
'name': ['pen', 'brush', 'paper']
},
'theme': { $themeSettings },
'ggtitle': { 'text': 'theme=$theme, flavor=$flavor, custom colors=$withCustomColors,
${if (flavorOverCustomColors) "theme() + flavor()" else "flavor() + theme()"}' },
'kind': 'plot',
'scales': [
{
'aesthetic': 'fill',
'values': ['pen', 'brush', 'paper']
}
],
'layers': [
{
'geom': 'pie',
'stat': 'identity',
'mapping': {
'fill': 'name'
},
'tooltips': 'none',
'labels': {
'lines': ['@name']
},
'color': 'pen'
}
]
}""".trimIndent()
return parsePlotSpec(spec)
}

private fun example(
theme: String = "light",
flavor: String? = null,
background: String? = null
): MutableMap<String, Any> {
val themeSettings = listOf(
"'name': '$theme'",
background?.let { "'plot_background': {'fill': '$background', 'blank': false}" } ?: "",
flavor?.let { "'flavor': '$flavor'" } ?: ""
).filter(String::isNotEmpty).joinToString()

val spec = """{
"ggsize": { "width": 400, "height": 200 },
"theme": {
${background?.let { "'plot_background': {'fill': '$background', 'blank': false}" } ?: "" }
${if (background != null && flavor != null) "," else ""}
${flavor?.let { "'flavor': '$flavor'" } ?: ""}
},
"ggtitle": { "text": "theme=$theme, flavor=$flavor
point: \'brush\'+\'paper\'; line: \'pen\'" },
"theme": { $themeSettings },
"kind": "plot",
"layers": [
{
Expand Down
374 changes: 357 additions & 17 deletions docs/f-23c/named_system_colors.ipynb

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/mas
See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-23c/geom_pie_stroke_and_spacers.ipynb).


- New named system colors: "pen", "paper", "brush".
- New named system colors: "pen", "paper", "brush";

`geom` parameter in `theme()` to specify new values for these colors via `element_geom()` function.

See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-23c/named_system_colors.ipynb).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fun dynamicObjectToMap(o: dynamic): MutableMap<String, Any> {
var handleAnyNullable: (o: dynamic) -> Any? = {}

val handleObject: (o: dynamic) -> MutableMap<String, Any> = { o: dynamic ->
val map = HashMap<String, Any>()
val map = LinkedHashMap<String, Any>()
val entries = js("Object.entries(o)")
for (entry in entries) {
val key = entry[0] as String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface GeomContext {
val flipped: Boolean
val targetCollector: GeomTargetCollector
val annotations: Annotations?
val plotBackground: Color
val backgroundColor: Color

// ToDo: Just positional resolution along x or y-axis. Also, its now equal to "data resolution". No need to compute it in 'Aesthetics'.
fun getResolution(aes: Aes<Double>): Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class PieGeom : GeomBase(), WithWidth, WithHeight {
root.appendNodes(pieSectors.map(::buildSvgArcs))
if (spacerWidth > 0) {
root.appendNodes(
buildSvgSpacerLines(pieSectors, width = spacerWidth, color = spacerColor ?: ctx.plotBackground)
buildSvgSpacerLines(pieSectors, width = spacerWidth, color = spacerColor ?: ctx.backgroundColor)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2023. 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

import org.jetbrains.letsPlot.commons.values.Color

interface ColorTheme {
fun pen(): Color
fun brush(): Color
fun paper(): Color
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ interface Theme {
fun tooltips(): TooltipsTheme

fun geometries(geomKind: GeomKind): GeomTheme

fun colors(): ColorTheme
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object BogusContext : GeomContext {
get() = error("Not available in a bogus geom context")
override val annotations: Annotations
get() = error("Not available in a bogus geom context")
override val plotBackground: Color
override val backgroundColor: Color
get() = error("Not available in a bogus geom context")

override fun getResolution(aes: Aes<Double>): Double {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object DemoAndTest {
coord = coord,
flippedAxis = flippedAxis,
targetCollector = targetCollector,
plotBackground = plotBackground
backgroundColor = plotBackground
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class GeomContextBuilder : ImmutableGeomContext.Builder {
private var geomTargetCollector: GeomTargetCollector = NullGeomTargetCollector()
private var fontFamilyRegistry: FontFamilyRegistry? = null
private var annotations: Annotations? = null
private var plotBackground: Color = Color.WHITE
private var backgroundColor: Color = Color.WHITE

constructor()

Expand All @@ -39,7 +39,7 @@ class GeomContextBuilder : ImmutableGeomContext.Builder {
aesBounds = ctx._aesBounds
geomTargetCollector = ctx.targetCollector
annotations = ctx.annotations
plotBackground = ctx.plotBackground
backgroundColor = ctx.backgroundColor
}

override fun flipped(flipped: Boolean): ImmutableGeomContext.Builder {
Expand Down Expand Up @@ -77,8 +77,8 @@ class GeomContextBuilder : ImmutableGeomContext.Builder {
return this
}

override fun plotBackground(color: Color): ImmutableGeomContext.Builder {
this.plotBackground = color
override fun backgroundColor(color: Color): ImmutableGeomContext.Builder {
this.backgroundColor = color
return this
}

Expand All @@ -95,7 +95,7 @@ class GeomContextBuilder : ImmutableGeomContext.Builder {
override val flipped: Boolean = b.flipped
override val targetCollector = b.geomTargetCollector
override val annotations = b.annotations
override val plotBackground = b.plotBackground
override val backgroundColor = b.backgroundColor

private val fontFamilyRegistry: FontFamilyRegistry? = b.fontFamilyRegistry

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface ImmutableGeomContext : GeomContext {

fun annotations(annotations: Annotations?): Builder

fun plotBackground(color: Color): Builder
fun backgroundColor(color: Color): Builder

fun build(): ImmutableGeomContext
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023. 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.builder.defaultTheme

import org.jetbrains.letsPlot.core.plot.base.theme.ColorTheme
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.GEOM
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Geom.BRUSH
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Geom.PAPER
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Geom.PEN
import org.jetbrains.letsPlot.core.plot.builder.presentation.FontFamilyRegistry

internal class DefaultColorTheme(
options: Map<String, Any>,
fontFamilyRegistry: FontFamilyRegistry
) : ThemeValuesAccess(options, fontFamilyRegistry), ColorTheme {

private val geomElem = getElemValue(listOf(GEOM))

override fun pen() = getColor(geomElem, PEN)

override fun brush() = getColor(geomElem, BRUSH)

override fun paper() = getColor(geomElem, PAPER)
}
Loading

0 comments on commit 161af49

Please sign in to comment.