diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgCircleElement.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgCircleElement.kt index dc061cecf89..e4bef2aacca 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgCircleElement.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgCircleElement.kt @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_DASHARRAY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_WIDTH import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformable.Companion.TRANSFORM @@ -86,6 +87,10 @@ class SvgCircleElement() : SvgGraphicsElement(), SvgTransformable, return getAttribute(STROKE_WIDTH) } + override fun strokeDashArray(): Property { + return getAttribute(STROKE_DASHARRAY) + } + override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector { return container().getPeer()!!.invertTransform(this, point) } diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgEllipseElement.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgEllipseElement.kt index 17e260e5e53..1b2afe91663 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgEllipseElement.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgEllipseElement.kt @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_DASHARRAY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_WIDTH import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformable.Companion.TRANSFORM @@ -92,6 +93,10 @@ class SvgEllipseElement() : SvgGraphicsElement(), return getAttribute(STROKE_WIDTH) } + override fun strokeDashArray(): Property { + return getAttribute(STROKE_DASHARRAY) + } + override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector { return container().getPeer()!!.invertTransform(this, point) } diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgLineElement.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgLineElement.kt index 53bb605bbbf..3ccf4af0f94 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgLineElement.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgLineElement.kt @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_DASHARRAY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_WIDTH import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformable.Companion.TRANSFORM @@ -92,6 +93,10 @@ class SvgLineElement() : SvgGraphicsElement(), SvgTransformable, return getAttribute(STROKE_WIDTH) } + override fun strokeDashArray(): Property { + return getAttribute(STROKE_DASHARRAY) + } + override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector { return container().getPeer()!!.invertTransform(this, point) } diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgPathElement.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgPathElement.kt index 851bd410d4f..60b00e7058e 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgPathElement.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgPathElement.kt @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_DASHARRAY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_WIDTH import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformable.Companion.TRANSFORM @@ -75,6 +76,10 @@ class SvgPathElement() : SvgGraphicsElement(), SvgTransformable, return getAttribute(STROKE_WIDTH) } + override fun strokeDashArray(): Property { + return getAttribute(STROKE_DASHARRAY) + } + fun strokeMiterLimit(): Property { return getAttribute(STROKE_MITER_LIMIT) } diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgRectElement.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgRectElement.kt index 133127b069c..1c44818ab1d 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgRectElement.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgRectElement.kt @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.FILL_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_DASHARRAY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_OPACITY import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape.Companion.STROKE_WIDTH import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformable.Companion.TRANSFORM @@ -96,6 +97,10 @@ class SvgRectElement() : SvgGraphicsElement(), SvgTransformable, return getAttribute(STROKE_WIDTH) } + override fun strokeDashArray(): Property { + return getAttribute(STROKE_DASHARRAY) + } + override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector { return container().getPeer()!!.invertTransform(this, point) } diff --git a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgShape.kt b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgShape.kt index fde05e3b92a..6ca7ac7834d 100644 --- a/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgShape.kt +++ b/datamodel/src/commonMain/kotlin/org/jetbrains/letsPlot/datamodel/svg/dom/SvgShape.kt @@ -25,6 +25,8 @@ interface SvgShape { fun strokeWidth(): Property + fun strokeDashArray(): Property + companion object { val FILL: SvgAttributeSpec = SvgAttributeSpec.createSpec("fill") @@ -36,5 +38,7 @@ interface SvgShape { SvgAttributeSpec.createSpec("stroke-opacity") val STROKE_WIDTH: SvgAttributeSpec = SvgAttributeSpec.createSpec("stroke-width") + val STROKE_DASHARRAY: SvgAttributeSpec = + SvgAttributeSpec.createSpec(SvgConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE) } } \ No newline at end of file diff --git a/demo/plot/src/commonMain/kotlin/demo/plot/shared/model/component/TooltipBoxDemo.kt b/demo/plot/src/commonMain/kotlin/demo/plot/shared/model/component/TooltipBoxDemo.kt index 8f53863454d..b4de333affa 100644 --- a/demo/plot/src/commonMain/kotlin/demo/plot/shared/model/component/TooltipBoxDemo.kt +++ b/demo/plot/src/commonMain/kotlin/demo/plot/shared/model/component/TooltipBoxDemo.kt @@ -14,6 +14,8 @@ import org.jetbrains.letsPlot.core.plot.builder.presentation.Style.TOOLTIP_LABEL import org.jetbrains.letsPlot.core.plot.builder.presentation.Style.TOOLTIP_TITLE import org.jetbrains.letsPlot.core.plot.builder.tooltip.component.TooltipBox import demo.plot.common.model.SimpleDemoBase +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType class TooltipBoxDemo : SimpleDemoBase(DEMO_BOX_SIZE) { @@ -59,6 +61,7 @@ class TooltipBoxDemo : SimpleDemoBase(DEMO_BOX_SIZE) { textColor, borderColor, strokeWidth, + lineType, lines, title, textClassName, @@ -90,6 +93,7 @@ class TooltipBoxDemo : SimpleDemoBase(DEMO_BOX_SIZE) { val textColor: Color? = Color.BLACK, val borderColor: Color = Color.BLACK, val strokeWidth: Double = 2.0, + val lineType: LineType = NamedLineType.SOLID, val lines: List, val title: String? = null, val textClassName: String = TOOLTIP_TEXT_CLASS, diff --git a/docs/f-24b/theme_linetype.ipynb b/docs/f-24b/theme_linetype.ipynb new file mode 100644 index 00000000000..f7e41ec3720 --- /dev/null +++ b/docs/f-24b/theme_linetype.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "16b70cf7", + "metadata": {}, + "source": [ + "# Line Type for Plot Elements\n", + "\n", + "\n", + "The `linetype` parameter in `element_line()` and `element_rect()` specifies the line type for the element using either text (\"blank\", \"solid\", \"dashed\", \"dotted\", \"dotdash\", \"longdash\", \"twodash\") or number (0, 1, 2, 3, 4, 5, 6).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "49ca5237", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "from lets_plot import *" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cbf716cd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "LetsPlot.setup_html()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "3b3c06c0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Unnamed: 0manufacturermodeldisplyearcyltransdrvctyhwyflclass
01audia41.819994auto(l5)f1829pcompact
12audia41.819994manual(m5)f2129pcompact
23audia42.020084manual(m6)f2031pcompact
\n", + "
" + ], + "text/plain": [ + " Unnamed: 0 manufacturer model displ year cyl trans drv cty hwy \\\n", + "0 1 audi a4 1.8 1999 4 auto(l5) f 18 29 \n", + "1 2 audi a4 1.8 1999 4 manual(m5) f 21 29 \n", + "2 3 audi a4 2.0 2008 4 manual(m6) f 20 31 \n", + "\n", + " fl class \n", + "0 p compact \n", + "1 p compact \n", + "2 p compact " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mpg_df = pd.read_csv (\"https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/mpg.csv\")\n", + "mpg_df.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9b433408", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p = ggplot(mpg_df) + geom_point(aes('cty', 'hwy', color='drv'))\n", + "p" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2da1a757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p + theme(\n", + " panel_grid=element_line(linetype='dashed', color='grey'),\n", + " panel_grid_minor_y=element_line(linetype='dotted', size=0.75, color='grey'),\n", + " legend_background=element_rect(linetype=1, size=0.75, color='grey')\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/future_changes.md b/future_changes.md index a3583fc05cb..78edbea6a69 100644 --- a/future_changes.md +++ b/future_changes.md @@ -6,6 +6,11 @@ See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-24b/facet_multiline_titles.ipynb). +- Parameter `linetype` in `element_text()` and `element_rect()` in `theme()` [[LPK-235](https://github.com/JetBrains/lets-plot-kotlin/issues/235)]. + + See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot/blob/master/docs/f-24b/theme_linetype.ipynb). + + ### Changed @@ -27,6 +32,7 @@ - Missing outer bar annotations when specifying `scale_x_reverse()/scale_y_reverse()` [[#1058](https://github.com/JetBrains/lets-plot/issues/1058)]. - `geom_density2d`: the doc missing some 'computed' variables [[#1062](https://github.com/JetBrains/lets-plot/issues/1062)]. - Weird and problematic behavior : lets-plot does not respect x and y. Sizing problem ?[[#1068](https://github.com/JetBrains/lets-plot/issues/1068)]. +- Add `linetype` parameter in `elementLine()` and `elementRect()` [[LPK-235](https://github.com/JetBrains/lets-plot-kotlin/issues/235)]. - Any way to line-wrap facet labels? [[LPK-237](https://github.com/JetBrains/lets-plot-kotlin/issues/237)]. - Missing marginal gridlines. \ No newline at end of file diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/GeomHelper.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/GeomHelper.kt index 72413ba020b..5d26795024d 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/GeomHelper.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/GeomHelper.kt @@ -317,30 +317,11 @@ open class GeomHelper( companion object { fun decorate( - node: SvgNode, + shape: SvgShape, p: DataPointAesthetics, applyAlphaToAll: Boolean = ALPHA_CONTROLS_BOTH, strokeScaler: (DataPointAesthetics) -> Double = AesScaling::strokeWidth, filled: Boolean = true - ) { - if (node is SvgShape) { - decorateShape(node as SvgShape, p, applyAlphaToAll, strokeScaler, filled) - } - - if (node is SvgElement) { - val lineType = p.lineType() - if (!(lineType.isBlank || lineType.isSolid)) { - StrokeDashArraySupport.apply(node, strokeScaler(p), lineType.dashArray) - } - } - } - - private fun decorateShape( - shape: SvgShape, - p: DataPointAesthetics, - applyAlphaToAll: Boolean, - strokeScaler: (DataPointAesthetics) -> Double, - filled: Boolean ) { AestheticsUtil.updateStroke(shape, p, applyAlphaToAll) if (filled) { @@ -348,7 +329,9 @@ open class GeomHelper( } else { shape.fill().set(SvgColors.NONE) } - shape.strokeWidth().set(strokeScaler(p)) + val strokeWidth = strokeScaler(p) + shape.strokeWidth().set(strokeWidth) + StrokeDashArraySupport.apply(shape, strokeWidth, p.lineType()) } internal fun decorateSlimShape( diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/LinesHelper.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/LinesHelper.kt index 1fc5960d0f1..58b0d469d1d 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/LinesHelper.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/LinesHelper.kt @@ -237,9 +237,7 @@ open class LinesHelper( path.width().set(size) val lineType = p.lineType() - if (!(lineType.isBlank || lineType.isSolid)) { - path.dashArray().set(lineType.dashArray) - } + path.lineType().set(lineType) } private fun decorateFillingPart(path: LinePath, p: DataPointAesthetics) { diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/linetype/NamedLineType.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/linetype/NamedLineType.kt index 98b3fe8ac07..322d3cbf6f2 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/linetype/NamedLineType.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/linetype/NamedLineType.kt @@ -38,4 +38,25 @@ enum class NamedLineType(val code: Int, private val myDashArray: List?) } throw IllegalStateException("No dash array in " + name.lowercase() + " linetype") } + +} + +private val LINE_TYPE_BY_CODE = NamedLineType.values().associateBy { it.code } +private val LINE_TYPE_BY_NAME = NamedLineType.values().associateBy { it.name.lowercase() } + +fun parse(value: Any?): LineType { + /* + * The line type is specified by either an integer (code 0..6) or a name. + * Codes and names: + * 0 = blank, 1 = solid, 2 = dashed, 3 = dotted, 4 = dotdash, 5 = longdash, 6 = twodash + * + * ToDo: could be string of hexadecimal digits (not implemented) + */ + return when { + value == null -> NamedLineType.SOLID + value is LineType -> value + value is String && LINE_TYPE_BY_NAME.containsKey(value) -> LINE_TYPE_BY_NAME[value]!! + value is Number && LINE_TYPE_BY_CODE.containsKey(value.toInt()) -> LINE_TYPE_BY_CODE[value.toInt()]!! + else -> NamedLineType.SOLID + } } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/LinePath.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/LinePath.kt index b86decd8d82..008ff1c19f1 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/LinePath.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/LinePath.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.base.render.svg import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.intern.observable.property.WritableProperty import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType import org.jetbrains.letsPlot.datamodel.svg.dom.SvgColors import org.jetbrains.letsPlot.datamodel.svg.dom.SvgPathDataBuilder import org.jetbrains.letsPlot.datamodel.svg.dom.SvgPathElement @@ -18,7 +19,7 @@ import org.jetbrains.letsPlot.datamodel.svg.dom.SvgPathElement class LinePath(builder: SvgPathDataBuilder) : SvgComponent() { private val myPath: SvgPathElement - private var myDashArray: List? = null + private var myLineType: LineType? = null init { myPath = SvgPathElement(builder.build()) @@ -81,24 +82,19 @@ class LinePath(builder: SvgPathDataBuilder) : SvgComponent() { } } - fun dashArray(): WritableProperty> { - return object : WritableProperty> { - override fun set(value: List) { - myDashArray = ArrayList(value) + fun lineType(): WritableProperty { + return object : WritableProperty { + override fun set(value: LineType) { + myLineType = value updatePathDashArray() } } } private fun updatePathDashArray() { - if (!(myDashArray == null || myDashArray!!.isEmpty())) { - val w = myPath.strokeWidth().get() - val width = w ?: 1.0 - StrokeDashArraySupport.apply( - myPath, - width, - myDashArray!! - ) + if (myLineType != null) { + val width = myPath.strokeWidth().get() ?: 1.0 + StrokeDashArraySupport.apply(myPath, width, myLineType!!) } } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/StrokeDashArraySupport.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/StrokeDashArraySupport.kt index ac9df191d8c..c114f0b4348 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/StrokeDashArraySupport.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/render/svg/StrokeDashArraySupport.kt @@ -5,8 +5,8 @@ package org.jetbrains.letsPlot.core.plot.base.render.svg -import org.jetbrains.letsPlot.datamodel.svg.dom.SvgConstants -import org.jetbrains.letsPlot.datamodel.svg.dom.SvgElement +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType +import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape /** * The counterpart of SVG 'stroke-dasharray' attribute but @@ -14,15 +14,25 @@ import org.jetbrains.letsPlot.datamodel.svg.dom.SvgElement * is defined as multiples of line width */ object StrokeDashArraySupport { - fun apply(element: SvgElement, strokeWidth: Double, dashArray: List) { + fun apply(element: SvgShape, strokeWidth: Double, lineType: LineType) { + val dashArray = toStrokeDashArray(strokeWidth, lineType) + if (dashArray != null) { + element.strokeDashArray().set(dashArray) + } + } + + private fun toStrokeDashArray(strokeWidth: Double, lineType: LineType): String? { + if (lineType.isBlank || lineType.isSolid) { + return null + } val sb = StringBuilder() - for (relativeLength in dashArray) { + lineType.dashArray.forEach { relativeLength -> val length = relativeLength * strokeWidth - if (sb.length > 0) { + if (sb.isNotEmpty()) { sb.append(',') } sb.append(length.toString()) } - element.getAttribute(SvgConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE).set(sb.toString()) + return sb.toString() } } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/AxisTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/AxisTheme.kt index 2b88d6767b0..ce5f305d6a6 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/AxisTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/AxisTheme.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface AxisTheme { val axis: String @@ -36,6 +37,10 @@ interface AxisTheme { fun tickMarkColor(): Color + fun lineType(): LineType + + fun tickMarkLineType(): LineType + fun labelStyle(): ThemeTextStyle fun rotateLabels(): Boolean @@ -62,6 +67,7 @@ interface AxisTheme { fun tooltipFill(): Color fun tooltipColor(): Color fun tooltipStrokeWidth(): Double + fun tooltipLineType(): LineType fun tooltipTextStyle(): ThemeTextStyle } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/FacetsTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/FacetsTheme.kt index d561d5d9be5..6487838b9be 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/FacetsTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/FacetsTheme.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface FacetsTheme { fun showStrip(): Boolean @@ -16,6 +17,7 @@ interface FacetsTheme { fun stripFill(): Color fun stripColor(): Color fun stripStrokeWidth(): Double + fun stripLineType(): LineType fun stripTextStyle(): ThemeTextStyle fun stripMargins(): Thickness fun stripTextJustification(): TextJustification diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/LegendTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/LegendTheme.kt index adfe1d00e87..50c58320771 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/LegendTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/LegendTheme.kt @@ -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.layout.TextJustification +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface LegendTheme { fun keySize(): Double @@ -40,4 +41,5 @@ interface LegendTheme { fun backgroundColor(): Color fun backgroundFill(): Color fun backgroundStrokeWidth(): Double + fun backgroundLineType(): LineType } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelGridTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelGridTheme.kt index 766f298faee..04536cf8bb0 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelGridTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelGridTheme.kt @@ -6,6 +6,7 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface PanelGridTheme { fun isOntop(): Boolean @@ -21,4 +22,8 @@ interface PanelGridTheme { fun majorLineColor(): Color fun minorLineColor(): Color + + fun majorLineType(): LineType + + fun minorLineType(): LineType } diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelTheme.kt index 64648f1de53..2d2f9661c12 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PanelTheme.kt @@ -7,6 +7,7 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType /** * Plotting area, drawn underneath plot. @@ -16,11 +17,13 @@ interface PanelTheme { fun rectColor(): Color fun rectFill(): Color fun rectStrokeWidth(): Double + fun rectLineType(): LineType fun showBorder(): Boolean fun borderColor(): Color fun borderWidth(): Double fun borderIsOntop(): Boolean + fun borderLineType(): LineType fun gridX(flipAxis: Boolean = false): PanelGridTheme fun gridY(flipAxis: Boolean = false): PanelGridTheme diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PlotTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PlotTheme.kt index 89f28adc18a..64e05eecec5 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PlotTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/PlotTheme.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface PlotTheme { fun showBackground(): Boolean @@ -15,6 +16,7 @@ interface PlotTheme { fun backgroundColor(): Color fun backgroundFill(): Color fun backgroundStrokeWidth(): Double + fun backgroundLineType(): LineType fun titleStyle(): ThemeTextStyle fun subtitleStyle(): ThemeTextStyle fun captionStyle(): ThemeTextStyle diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/TooltipsTheme.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/TooltipsTheme.kt index 1b703c20eaf..2271123f359 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/TooltipsTheme.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/theme/TooltipsTheme.kt @@ -6,11 +6,13 @@ package org.jetbrains.letsPlot.core.plot.base.theme import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType interface TooltipsTheme { fun tooltipColor(): Color fun tooltipFill(): Color fun tooltipStrokeWidth(): Double + fun tooltipLineType(): LineType fun textStyle(): ThemeTextStyle fun titleStyle(): ThemeTextStyle diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotSvgComponent.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotSvgComponent.kt index b66c5afff0c..7b841694e66 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotSvgComponent.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotSvgComponent.kt @@ -21,6 +21,7 @@ import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification.Companion. import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification.Companion.applyJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness import org.jetbrains.letsPlot.core.plot.base.render.svg.MultilineLabel +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport 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 @@ -252,6 +253,7 @@ class PlotSvgComponent constructor( fillColor().set(Color.TRANSPARENT) strokeColor().set(plotTheme.backgroundColor()) strokeWidth().set(plotTheme.backgroundStrokeWidth()) + StrokeDashArraySupport.apply(this, plotTheme.backgroundStrokeWidth(), plotTheme.backgroundLineType()) d().set(SvgPathDataBuilder().rect(backgroundRect).build()) } } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotTile.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotTile.kt index 1332cf63197..3b329f75d53 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotTile.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/PlotTile.kt @@ -15,6 +15,7 @@ import org.jetbrains.letsPlot.core.plot.base.geom.LiveMapProvider import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification.Companion.TextRotation import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification.Companion.applyJustification import org.jetbrains.letsPlot.core.plot.base.render.svg.MultilineLabel +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.theme.FacetsTheme import org.jetbrains.letsPlot.core.plot.base.theme.Theme @@ -159,6 +160,7 @@ internal class PlotTile( strokeWidth().set(facetTheme.stripStrokeWidth()) fillColor().set(facetTheme.stripFill()) strokeColor().set(facetTheme.stripColor()) + StrokeDashArraySupport.apply(this, facetTheme.stripStrokeWidth(), facetTheme.stripLineType()) } add(rect) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultAxisTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultAxisTheme.kt index dc107efaf65..cc71e9132a9 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultAxisTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultAxisTheme.kt @@ -88,6 +88,8 @@ internal class DefaultAxisTheme( return getColor(getElemValue(lineKey), Elem.COLOR) } + override fun lineType() = getLineType(getElemValue(lineKey)) + override fun tickMarkWidth(): Double { return getNumber(getElemValue(tickKey), Elem.SIZE) } @@ -100,6 +102,8 @@ internal class DefaultAxisTheme( return getColor(getElemValue(tickKey), Elem.COLOR) } + override fun tickMarkLineType() = getLineType(getElemValue(tickKey)) + override fun tickLabelMargins() = getMargins(getElemValue(textKey)) override fun labelStyle(): ThemeTextStyle { @@ -124,6 +128,8 @@ internal class DefaultAxisTheme( return getNumber(getElemValue(tooltipKey), Elem.SIZE) } + override fun tooltipLineType() = getLineType(getElemValue(tooltipKey)) + override fun tooltipTextStyle(): ThemeTextStyle { val tooltipTextColor = getColor(getElemValue(tooltipTextColorKey), Elem.COLOR) val textStyle = getTextStyle(getElemValue(tooltipTextKey)) diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultFacetsTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultFacetsTheme.kt index 4155e868911..21fe17cb3a9 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultFacetsTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultFacetsTheme.kt @@ -43,6 +43,8 @@ internal class DefaultFacetsTheme( return getNumber(getElemValue(rectKey), Elem.SIZE) } + override fun stripLineType() = getLineType(getElemValue(rectKey)) + override fun stripTextStyle(): ThemeTextStyle { return getTextStyle(getElemValue(textKey)) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultLegendTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultLegendTheme.kt index 4f7de3d10c3..ff8b4717137 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultLegendTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultLegendTheme.kt @@ -83,4 +83,6 @@ internal class DefaultLegendTheme( override fun backgroundStrokeWidth(): Double { return getNumber(getElemValue(backgroundKey), ThemeOption.Elem.SIZE) } + + override fun backgroundLineType() = getLineType(getElemValue(backgroundKey)) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelGridTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelGridTheme.kt index e4eca9ee464..07e3cd9ae03 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelGridTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelGridTheme.kt @@ -53,4 +53,8 @@ internal class DefaultPanelGridTheme( override fun minorLineColor(): Color { return getColor(getElemValue(minorLineKey), Elem.COLOR) } + + override fun majorLineType() = getLineType(getElemValue(majorLineKey)) + + override fun minorLineType() = getLineType(getElemValue(minorLineKey)) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelTheme.kt index 852bf8f0145..3a7643fd7aa 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPanelTheme.kt @@ -45,6 +45,8 @@ internal class DefaultPanelTheme( return getNumber(getElemValue(rectKey), Elem.SIZE) } + override fun rectLineType() = getLineType(getElemValue(rectKey)) + override fun showBorder() = !isElemBlank(borderKey) override fun borderColor() = getColor(getElemValue(borderKey), Elem.COLOR) @@ -53,6 +55,8 @@ internal class DefaultPanelTheme( override fun borderIsOntop(): Boolean = getBoolean(borderOntopKey) + override fun borderLineType() = getLineType(getElemValue(borderKey)) + override fun gridX(flipAxis: Boolean): PanelGridTheme = if (flipAxis) gridY else gridX override fun gridY(flipAxis: Boolean): PanelGridTheme = if (flipAxis) gridX else gridY diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPlotTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPlotTheme.kt index d1b96e4f7dc..a0ce0179537 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPlotTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultPlotTheme.kt @@ -7,6 +7,7 @@ package org.jetbrains.letsPlot.core.plot.builder.defaultTheme import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType import org.jetbrains.letsPlot.core.plot.base.theme.PlotTheme import org.jetbrains.letsPlot.core.plot.base.theme.ThemeTextStyle import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem @@ -49,6 +50,8 @@ internal class DefaultPlotTheme( return getNumber(getElemValue(backgroundKey), Elem.SIZE) } + override fun backgroundLineType() = getLineType(getElemValue(backgroundKey)) + override fun titleStyle(): ThemeTextStyle { return getTextStyle(getElemValue(titleKey)) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTheme.kt index 647b9936715..208e370bf07 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTheme.kt @@ -12,6 +12,7 @@ import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.BLANK import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.COLOR import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.FILL +import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.LINETYPE import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.Margin import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem.SIZE import org.jetbrains.letsPlot.core.plot.builder.presentation.DefaultFontFamilyRegistry @@ -67,6 +68,7 @@ class DefaultTheme internal constructor( FILL to containerTheme.plot.backgroundFill(), COLOR to this.plot.backgroundColor(), SIZE to this.plot.backgroundStrokeWidth(), + LINETYPE to this.plot.backgroundLineType() ) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTooltipsTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTooltipsTheme.kt index 6e98a43f0bb..2da5482f683 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTooltipsTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/DefaultTooltipsTheme.kt @@ -32,6 +32,8 @@ internal class DefaultTooltipsTheme( override fun tooltipStrokeWidth() = getNumber(getElemValue(tooltipKey), Elem.SIZE) + override fun tooltipLineType() = getLineType(getElemValue(tooltipKey)) + override fun textStyle(): ThemeTextStyle = getTextStyle(getElemValue(textKey)) override fun titleStyle(): ThemeTextStyle { diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/ThemeValuesAccess.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/ThemeValuesAccess.kt index b590e46b24f..bcf66aa3e38 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/ThemeValuesAccess.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/ThemeValuesAccess.kt @@ -11,6 +11,8 @@ import org.jetbrains.letsPlot.commons.values.FontFace import org.jetbrains.letsPlot.commons.values.FontFamily import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType +import org.jetbrains.letsPlot.core.plot.base.render.linetype.parse import org.jetbrains.letsPlot.core.plot.base.theme.FontFamilyRegistry import org.jetbrains.letsPlot.core.plot.base.theme.ThemeTextStyle import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.values.ThemeOption.Elem @@ -136,4 +138,8 @@ internal open class ThemeValuesAccess( left = getNumber(elem, Elem.Inset.LEFT), ) } + + protected fun getLineType(elem: Map): LineType { + return parse(elem.getValue(Elem.LINETYPE)) + } } \ No newline at end of file diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeOption.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeOption.kt index da7e93b81a0..b86ee9c4de3 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeOption.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeOption.kt @@ -109,7 +109,7 @@ object ThemeOption { const val FILL = "fill" const val COLOR = "color" const val SIZE = "size" - const val LINETYPE = "linetype" // ToDo + const val LINETYPE = "linetype" const val ARROW = "arrow" // ToDo // text diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeValuesBase.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeValuesBase.kt index e623624dad0..4d66d0f3664 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeValuesBase.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/defaultTheme/values/ThemeValuesBase.kt @@ -9,6 +9,7 @@ import org.jetbrains.letsPlot.commons.values.FontFace 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.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 @@ -48,11 +49,13 @@ internal open class ThemeValuesBase : ThemeValues(VALUES) { LINE to mapOf( Elem.SIZE to 1.0, Elem.COLOR to SymbolicColor.BLACK, + Elem.LINETYPE to NamedLineType.SOLID ), RECT to mapOf( Elem.SIZE to 1.0, Elem.COLOR to SymbolicColor.BLACK, - Elem.FILL to SymbolicColor.WHITE + Elem.FILL to SymbolicColor.WHITE, + Elem.LINETYPE to NamedLineType.SOLID ), TEXT to mapOf( Elem.SIZE to Defaults.FONT_SMALL, diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/PolarFrameOfReference.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/PolarFrameOfReference.kt index ca69b78e572..112d3e11fbe 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/PolarFrameOfReference.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/PolarFrameOfReference.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.builder.frame import org.jetbrains.letsPlot.commons.geometry.DoubleRectangle import org.jetbrains.letsPlot.core.plot.base.CoordinateSystem import org.jetbrains.letsPlot.core.plot.base.PlotContext +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.scale.ScaleBreaks import org.jetbrains.letsPlot.core.plot.base.theme.PanelGridTheme @@ -134,6 +135,7 @@ internal class PolarFrameOfReference( val strokeBkgr = createPanelElement() { it.strokeColor().set(theme.panel().rectColor()) it.strokeWidth().set(theme.panel().rectStrokeWidth()) + StrokeDashArraySupport.apply(it, theme.panel().rectStrokeWidth(), theme.panel().rectLineType()) it.fillOpacity().set(0.0) } @@ -144,6 +146,7 @@ internal class PolarFrameOfReference( val border = createPanelElement() { it.strokeColor().set(theme.panel().borderColor()) it.strokeWidth().set(theme.panel().borderWidth()) + StrokeDashArraySupport.apply(it, theme.panel().borderWidth(), theme.panel().borderLineType()) it.fillOpacity().set(0.0) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/SquareFrameOfReference.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/SquareFrameOfReference.kt index aa85f8f90d8..d51771cfd09 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/SquareFrameOfReference.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/frame/SquareFrameOfReference.kt @@ -10,6 +10,7 @@ import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.CoordinateSystem import org.jetbrains.letsPlot.core.plot.base.PlotContext +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.scale.ScaleBreaks import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme @@ -108,6 +109,7 @@ internal open class SquareFrameOfReference( val panelBorder = SvgRectElement(layoutInfo.geomContentBounds).apply { strokeColor().set(theme.panel().borderColor()) strokeWidth().set(theme.panel().borderWidth()) + StrokeDashArraySupport.apply(this, theme.panel().borderWidth(), theme.panel().borderLineType()) fillOpacity().set(0.0) } parent.add(panelBorder) @@ -196,6 +198,7 @@ internal open class SquareFrameOfReference( val panelRectStroke = SvgRectElement(layoutInfo.geomContentBounds).apply { strokeColor().set(theme.panel().rectColor()) strokeWidth().set(theme.panel().rectStrokeWidth()) + StrokeDashArraySupport.apply(this, theme.panel().rectStrokeWidth(), theme.panel().rectLineType()) fillOpacity().set(0.0) } parent.add(panelRectStroke) diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/AxisComponent.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/AxisComponent.kt index 3df78f8b2e6..e0aacab84b1 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/AxisComponent.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/AxisComponent.kt @@ -6,6 +6,7 @@ package org.jetbrains.letsPlot.core.plot.builder.guide import org.jetbrains.letsPlot.commons.geometry.DoubleVector +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.render.svg.Text import org.jetbrains.letsPlot.core.plot.base.render.svg.Text.HorizontalAnchor.* @@ -70,6 +71,7 @@ class AxisComponent( val axisLine = SvgLineElement(x1, y1, x2, y2).apply { strokeWidth().set(axisTheme.lineWidth()) strokeColor().set(axisTheme.lineColor()) + StrokeDashArraySupport.apply(this, axisTheme.lineWidth(), axisTheme.lineType()) } rootElement.children().add(axisLine) } @@ -87,6 +89,7 @@ class AxisComponent( tickMark = SvgLineElement() tickMark.strokeWidth().set(axisTheme.tickMarkWidth()) tickMark.strokeColor().set(axisTheme.tickMarkColor()) + StrokeDashArraySupport.apply(tickMark, axisTheme.tickMarkWidth(), axisTheme.tickMarkLineType()) } var tickLabel: TextLabel? = null diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/GridComponent.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/GridComponent.kt index 633d75b31fe..ad17e32e09a 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/GridComponent.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/GridComponent.kt @@ -7,6 +7,8 @@ package org.jetbrains.letsPlot.core.plot.builder.guide import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.render.svg.lineString import org.jetbrains.letsPlot.core.plot.base.theme.PanelGridTheme @@ -21,7 +23,12 @@ class GridComponent( if (gridTheme.showMinor()) { for (lineString in minorGrid) { - val elem = buildGridLine(lineString, gridTheme.minorLineWidth(), gridTheme.minorLineColor()) + val elem = buildGridLine( + lineString, + gridTheme.minorLineWidth(), + gridTheme.minorLineColor(), + gridTheme.minorLineType() + ) rootGroup.children().add(elem) } } @@ -29,13 +36,18 @@ class GridComponent( // Major grid. if (gridTheme.showMajor()) { for (lineString in majorGrid) { - val elem = buildGridLine(lineString, gridTheme.majorLineWidth(), gridTheme.majorLineColor()) + val elem = buildGridLine( + lineString, + gridTheme.majorLineWidth(), + gridTheme.majorLineColor(), + gridTheme.majorLineType() + ) rootGroup.children().add(elem) } } } - private fun buildGridLine(lineString: List, width: Double, color: Color): SvgNode { + private fun buildGridLine(lineString: List, width: Double, color: Color, lineType: LineType): SvgNode { val shapeElem: SvgShape = when { lineString.size == 2 -> SvgLineElement(lineString[0].x, lineString[0].y, lineString[1].x, lineString[1].y ) lineString.size < 2 -> SvgPathElement() @@ -44,6 +56,7 @@ class GridComponent( shapeElem.strokeColor().set(color) shapeElem.strokeWidth().set(width) + StrokeDashArraySupport.apply(shapeElem, width, lineType) shapeElem.fill().set(SvgColors.NONE) return shapeElem as SvgNode } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/LegendBox.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/LegendBox.kt index 89b8ae9dd63..5c4976d80bd 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/LegendBox.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/LegendBox.kt @@ -11,6 +11,7 @@ import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification.Companion.applyJustification import org.jetbrains.letsPlot.core.plot.base.render.svg.MultilineLabel +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.theme.LegendTheme import org.jetbrains.letsPlot.core.plot.builder.layout.PlotLabelSpecFactory @@ -43,6 +44,7 @@ abstract class LegendBox : SvgComponent() { add(SvgRectElement(spec.innerBounds).apply { strokeColor().set(theme.backgroundColor()) strokeWidth().set(theme.backgroundStrokeWidth()) + StrokeDashArraySupport.apply(this, theme.backgroundStrokeWidth(), theme.backgroundLineType()) fillColor().set(theme.backgroundFill()) }) } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/PolarAxisComponent.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/PolarAxisComponent.kt index e9f5fdddf4a..e15fd8cb284 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/PolarAxisComponent.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/guide/PolarAxisComponent.kt @@ -8,6 +8,7 @@ package org.jetbrains.letsPlot.core.plot.builder.guide import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.intern.math.toDegrees import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport 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 @@ -65,6 +66,7 @@ class PolarAxisComponent( ) strokeWidth().set(axisTheme.lineWidth()) strokeColor().set(axisTheme.lineColor()) + StrokeDashArraySupport.apply(this, axisTheme.lineWidth(), axisTheme.lineType()) fillColor().set(Color.TRANSPARENT) } rootElement.children().add(axisLine) @@ -74,6 +76,7 @@ class PolarAxisComponent( y2().set(breaksData.center.y - length / 2.0) strokeWidth().set(axisTheme.lineWidth()) strokeColor().set(axisTheme.lineColor()) + StrokeDashArraySupport.apply(this, axisTheme.lineWidth(), axisTheme.lineType()) } rootElement.children().add(axisLine) } @@ -93,6 +96,7 @@ class PolarAxisComponent( val tickMark = SvgLineElement() tickMark.strokeWidth().set(axisTheme.tickMarkWidth()) tickMark.strokeColor().set(axisTheme.tickMarkColor()) + StrokeDashArraySupport.apply(tickMark, axisTheme.tickMarkWidth(), axisTheme.tickMarkLineType()) val markLength = axisTheme.tickMarkLength() when (orientation) { diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/layout/tile/LiveMapAxisTheme.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/layout/tile/LiveMapAxisTheme.kt index 47f3ed1dd37..9b3a04e3fe1 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/layout/tile/LiveMapAxisTheme.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/layout/tile/LiveMapAxisTheme.kt @@ -10,6 +10,7 @@ import org.jetbrains.letsPlot.commons.values.FontFace import org.jetbrains.letsPlot.commons.values.FontFamily import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme import org.jetbrains.letsPlot.core.plot.base.theme.ThemeTextStyle import org.jetbrains.letsPlot.core.plot.builder.presentation.Defaults @@ -45,6 +46,10 @@ internal class LiveMapAxisTheme : AxisTheme { override fun tickMarkColor() = Defaults.Plot.Axis.LINE_COLOR + override fun lineType() = NamedLineType.SOLID + + override fun tickMarkLineType() = NamedLineType.SOLID + override fun labelStyle(): ThemeTextStyle = ThemeTextStyle( family = FontFamily.SERIF, face = FontFace.NORMAL, @@ -68,6 +73,8 @@ internal class LiveMapAxisTheme : AxisTheme { override fun tooltipStrokeWidth() = 1.0 + override fun tooltipLineType() = NamedLineType.SOLID + override fun tooltipTextStyle(): ThemeTextStyle = ThemeTextStyle( family = FontFamily.SERIF, face = FontFace.NORMAL, diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/subPlots/CompositeFigureSvgComponent.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/subPlots/CompositeFigureSvgComponent.kt index f6791864f20..102bbdf1fe7 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/subPlots/CompositeFigureSvgComponent.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/subPlots/CompositeFigureSvgComponent.kt @@ -7,6 +7,7 @@ package org.jetbrains.letsPlot.core.plot.builder.subPlots import org.jetbrains.letsPlot.commons.geometry.DoubleRectangle import org.jetbrains.letsPlot.commons.geometry.DoubleVector +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.theme.Theme import org.jetbrains.letsPlot.core.plot.builder.FigureSvgRoot @@ -28,6 +29,7 @@ class CompositeFigureSvgComponent constructor( fillColor().set(plotTheme.backgroundFill()) strokeColor().set(plotTheme.backgroundColor()) strokeWidth().set(plotTheme.backgroundStrokeWidth()) + StrokeDashArraySupport.apply(this, plotTheme.backgroundStrokeWidth(), plotTheme.backgroundLineType()) }) } } diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipRenderer.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipRenderer.kt index c2af8bd1d5e..1823293f8ab 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipRenderer.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipRenderer.kt @@ -17,6 +17,7 @@ import org.jetbrains.letsPlot.commons.values.Color.Companion.WHITE import org.jetbrains.letsPlot.commons.values.Colors import org.jetbrains.letsPlot.commons.values.Colors.mimicTransparency import org.jetbrains.letsPlot.core.plot.base.PlotContext +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme import org.jetbrains.letsPlot.core.plot.base.theme.TooltipsTheme import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator @@ -128,6 +129,13 @@ internal class TooltipRenderer constructor( else -> tooltipsTheme.tooltipStrokeWidth() } + val lineType = when { + spec.layoutHint.kind == X_AXIS_TOOLTIP -> xAxisTheme.tooltipLineType() + spec.layoutHint.kind == Y_AXIS_TOOLTIP -> yAxisTheme.tooltipLineType() + spec.isSide -> NamedLineType.SOLID + else -> tooltipsTheme.tooltipLineType() + } + val borderRadius = when (spec.layoutHint.kind) { X_AXIS_TOOLTIP, Y_AXIS_TOOLTIP -> 0.0 else -> BORDER_RADIUS @@ -142,6 +150,7 @@ internal class TooltipRenderer constructor( textColor = textColor, borderColor = borderColor, strokeWidth = strokeWidth, + lineType = lineType, lines = spec.lines, title = spec.title, textClassName = spec.style, diff --git a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/component/TooltipBox.kt b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/component/TooltipBox.kt index f4bbefc6774..6a1415e6833 100644 --- a/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/component/TooltipBox.kt +++ b/plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/component/TooltipBox.kt @@ -9,7 +9,9 @@ import org.jetbrains.letsPlot.commons.geometry.DoubleRectangle import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.intern.math.toRadians import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType import org.jetbrains.letsPlot.core.plot.base.render.svg.MultilineLabel +import org.jetbrains.letsPlot.core.plot.base.render.svg.StrokeDashArraySupport import org.jetbrains.letsPlot.core.plot.base.render.svg.SvgComponent import org.jetbrains.letsPlot.core.plot.base.render.svg.Text import org.jetbrains.letsPlot.core.plot.builder.presentation.Defaults.Common.Tooltip.COLOR_BAR_STROKE_WIDTH @@ -78,6 +80,7 @@ class TooltipBox : SvgComponent() { textColor: Color?, borderColor: Color, strokeWidth: Double, + lineType: LineType, lines: List, title: String?, textClassName: String, @@ -101,7 +104,7 @@ class TooltipBox : SvgComponent() { markerColors, textClassName ) - myPointerBox.updateStyle(fillColor, borderColor, strokeWidth, borderRadius, pointMarkerStrokeColor) + myPointerBox.updateStyle(fillColor, borderColor, strokeWidth, lineType, borderRadius, pointMarkerStrokeColor) } fun setPosition( @@ -141,6 +144,7 @@ class TooltipBox : SvgComponent() { fillColor: Color, borderColor: Color, strokeWidth: Double, + lineType: LineType, borderRadius: Double, pointMarkerStrokeColor: Color ) { @@ -150,6 +154,7 @@ class TooltipBox : SvgComponent() { strokeColor().set(borderColor) strokeWidth().set(strokeWidth) fillColor().set(fillColor) + StrokeDashArraySupport.apply(this, strokeWidth, lineType) } myHighlightPoint.apply { diff --git a/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TestUtil.kt b/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TestUtil.kt index 430262f0cde..0813bcd86af 100644 --- a/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TestUtil.kt +++ b/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TestUtil.kt @@ -15,6 +15,7 @@ import org.jetbrains.letsPlot.core.plot.base.Aes import org.jetbrains.letsPlot.core.plot.base.GeomKind import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme import org.jetbrains.letsPlot.core.plot.base.theme.ThemeTextStyle import org.jetbrains.letsPlot.core.plot.base.tooltip.* @@ -48,6 +49,8 @@ object TestUtil { override fun lineWidth() = TODO("Not yet implemented") override fun lineColor() = TODO("Not yet implemented") override fun tickMarkColor() = TODO("Not yet implemented") + override fun lineType() = NamedLineType.SOLID + override fun tickMarkLineType() = NamedLineType.SOLID override fun labelStyle(): ThemeTextStyle = TODO("Not yet implemented") override fun rotateLabels() = TODO("Not yet implemented") override fun labelAngle(): Double = TODO("Not yet implemented") @@ -57,6 +60,7 @@ object TestUtil { override fun tooltipFill() = AXIS_TOOLTIP_COLOR override fun tooltipColor() = AXIS_TOOLTIP_COLOR override fun tooltipStrokeWidth() = 1.0 + override fun tooltipLineType() = NamedLineType.SOLID override fun tooltipTextStyle(): ThemeTextStyle = ThemeTextStyle( // Defaults.FONT_FAMILY_NORMAL, FontFamily.SERIF, diff --git a/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipBoxTest.kt b/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipBoxTest.kt index a58cb794abd..a6bd7fc343e 100644 --- a/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipBoxTest.kt +++ b/plot-builder/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/TooltipBoxTest.kt @@ -10,6 +10,7 @@ import org.jetbrains.letsPlot.commons.geometry.DoubleVector import org.jetbrains.letsPlot.commons.geometry.DoubleVector.Companion.ZERO import org.jetbrains.letsPlot.commons.unsupported.UNSUPPORTED import org.jetbrains.letsPlot.commons.values.Color +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType import org.jetbrains.letsPlot.core.plot.builder.tooltip.component.TooltipBox import org.jetbrains.letsPlot.core.plot.builder.tooltip.component.TooltipBox.Orientation.HORIZONTAL import org.jetbrains.letsPlot.core.plot.builder.tooltip.component.TooltipBox.Orientation.VERTICAL @@ -45,6 +46,7 @@ class TooltipBoxTest { textColor = Color.WHITE, borderColor = Color.BLACK, strokeWidth = 1.0, + lineType = NamedLineType.SOLID, lines = listOf(TooltipSpec.Line.withValue(wordText)), title = null, textClassName = "anyStyle", diff --git a/plot-stem/src/commonMain/kotlin/org/jetbrains/letsPlot/core/spec/conversion/LineTypeOptionConverter.kt b/plot-stem/src/commonMain/kotlin/org/jetbrains/letsPlot/core/spec/conversion/LineTypeOptionConverter.kt index 5ef547de566..1332c0bba7e 100644 --- a/plot-stem/src/commonMain/kotlin/org/jetbrains/letsPlot/core/spec/conversion/LineTypeOptionConverter.kt +++ b/plot-stem/src/commonMain/kotlin/org/jetbrains/letsPlot/core/spec/conversion/LineTypeOptionConverter.kt @@ -7,45 +7,8 @@ package org.jetbrains.letsPlot.core.spec.conversion import org.jetbrains.letsPlot.commons.intern.function.Function import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType -import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType +import org.jetbrains.letsPlot.core.plot.base.render.linetype.parse internal class LineTypeOptionConverter : Function { - - override fun apply(value: Any?): LineType { - /* - * Line type is specified with either an integer (code 0..6), a name, or with a string of - * an even number (up to eight) of hexadecimal digits which give the lengths in - * consecutive positions in the string. - *

- * Codes and names: - * 0 = blank, 1 = solid, 2 = dashed, 3 = dotted, 4 = dotdash, 5 = longdash, 6 = twodash - */ - - if (value == null) { - return NamedLineType.SOLID - } - if (value is LineType) { - return value - } - if (value is String && LINE_TYPE_BY_NAME.containsKey(value)) { - return LINE_TYPE_BY_NAME[value]!! - } - return if (value is Number && LINE_TYPE_BY_CODE.containsKey(value.toInt())) { - LINE_TYPE_BY_CODE[value.toInt()]!! - } else NamedLineType.SOLID - - // todo: could be string of hexadecimal digits (not implemented) - } - - companion object { - private val LINE_TYPE_BY_CODE = HashMap() - private val LINE_TYPE_BY_NAME = HashMap() - - init { - for (lineType in NamedLineType.values()) { - LINE_TYPE_BY_CODE[lineType.code] = lineType - LINE_TYPE_BY_NAME[lineType.name.lowercase()] = lineType - } - } - } + override fun apply(value: Any?) = parse(value) } diff --git a/plot-stem/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/spec/config/TooltipTestUtil.kt b/plot-stem/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/spec/config/TooltipTestUtil.kt index 4c20f49ba90..d1586e045fb 100644 --- a/plot-stem/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/spec/config/TooltipTestUtil.kt +++ b/plot-stem/src/jvmTest/kotlin/org/jetbrains/letsPlot/core/spec/config/TooltipTestUtil.kt @@ -12,6 +12,7 @@ import org.jetbrains.letsPlot.commons.values.FontFamily import org.jetbrains.letsPlot.core.plot.base.PlotContext import org.jetbrains.letsPlot.core.plot.base.layout.TextJustification import org.jetbrains.letsPlot.core.plot.base.layout.Thickness +import org.jetbrains.letsPlot.core.plot.base.render.linetype.NamedLineType import org.jetbrains.letsPlot.core.plot.base.theme.AxisTheme import org.jetbrains.letsPlot.core.plot.base.theme.ThemeTextStyle import org.jetbrains.letsPlot.core.plot.base.tooltip.ContextualMapping @@ -41,6 +42,8 @@ object TooltipTestUtil { override fun lineWidth() = TODO("Not yet implemented") override fun lineColor() = TODO("Not yet implemented") override fun tickMarkColor() = TODO("Not yet implemented") + override fun lineType() = NamedLineType.SOLID + override fun tickMarkLineType() = NamedLineType.SOLID override fun labelStyle(): ThemeTextStyle = TODO("Not yet implemented") override fun rotateLabels() = TODO("Not yet implemented") override fun labelAngle() = TODO("Not yet implemented") @@ -50,6 +53,7 @@ object TooltipTestUtil { override fun tooltipFill() = Color.WHITE override fun tooltipColor() = Color.BLACK override fun tooltipStrokeWidth() = 1.0 + override fun tooltipLineType() = NamedLineType.SOLID override fun tooltipTextStyle(): ThemeTextStyle = ThemeTextStyle( // Defaults.FONT_FAMILY_NORMAL, diff --git a/python-package/lets_plot/plot/theme_.py b/python-package/lets_plot/plot/theme_.py index 3202bf9191f..43043dce3be 100644 --- a/python-package/lets_plot/plot/theme_.py +++ b/python-package/lets_plot/plot/theme_.py @@ -358,7 +358,7 @@ def element_rect( fill=None, color=None, size=None, - # ToDo: linetype + linetype=None, blank=False, ) -> dict: """ @@ -373,6 +373,9 @@ def element_rect( Border color. size : int Border size. + linetype : int or str + Type of the line. + Codes and names: 0 = 'blank', 1 = 'solid', 2 = 'dashed', 3 = 'dotted', 4 = 'dotdash', 5 = 'longdash', 6 = 'twodash'. blank : bool, default=False If True - draws nothing, and assigns no space. @@ -402,7 +405,8 @@ def element_rect( def element_line( color=None, size=None, - # ToDo: linetype, lineend, arrow + linetype=None, + # ToDo: lineend, arrow blank=False, ) -> dict: """ @@ -415,6 +419,9 @@ def element_line( Line color. size : int Line size. + linetype : int or str + Type of the line. + Codes and names: 0 = 'blank', 1 = 'solid', 2 = 'dashed', 3 = 'dotted', 4 = 'dotdash', 5 = 'longdash', 6 = 'twodash'. blank : bool, default=False If True - draws nothing, and assigns no space.