Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'linetype' for theme elements #1072

Merged
merged 14 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -86,6 +87,10 @@ class SvgCircleElement() : SvgGraphicsElement(), SvgTransformable,
return getAttribute(STROKE_WIDTH)
}

override fun strokeDashArray(): Property<String?> {
return getAttribute(STROKE_DASHARRAY)
}

override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector {
return container().getPeer()!!.invertTransform(this, point)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -92,6 +93,10 @@ class SvgEllipseElement() : SvgGraphicsElement(),
return getAttribute(STROKE_WIDTH)
}

override fun strokeDashArray(): Property<String?> {
return getAttribute(STROKE_DASHARRAY)
}

override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector {
return container().getPeer()!!.invertTransform(this, point)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -92,6 +93,10 @@ class SvgLineElement() : SvgGraphicsElement(), SvgTransformable,
return getAttribute(STROKE_WIDTH)
}

override fun strokeDashArray(): Property<String?> {
return getAttribute(STROKE_DASHARRAY)
}

override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector {
return container().getPeer()!!.invertTransform(this, point)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -75,6 +76,10 @@ class SvgPathElement() : SvgGraphicsElement(), SvgTransformable,
return getAttribute(STROKE_WIDTH)
}

override fun strokeDashArray(): Property<String?> {
return getAttribute(STROKE_DASHARRAY)
}

fun strokeMiterLimit(): Property<Double?> {
return getAttribute(STROKE_MITER_LIMIT)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -96,6 +97,10 @@ class SvgRectElement() : SvgGraphicsElement(), SvgTransformable,
return getAttribute(STROKE_WIDTH)
}

override fun strokeDashArray(): Property<String?> {
return getAttribute(STROKE_DASHARRAY)
}

override fun pointToTransformedCoordinates(point: DoubleVector): DoubleVector {
return container().getPeer()!!.invertTransform(this, point)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface SvgShape {

fun strokeWidth(): Property<Double?>

fun strokeDashArray(): Property<String?>

companion object {
val FILL: SvgAttributeSpec<SvgColor> =
SvgAttributeSpec.createSpec("fill")
Expand All @@ -36,5 +38,7 @@ interface SvgShape {
SvgAttributeSpec.createSpec("stroke-opacity")
val STROKE_WIDTH: SvgAttributeSpec<Double> =
SvgAttributeSpec.createSpec("stroke-width")
val STROKE_DASHARRAY: SvgAttributeSpec<String> =
SvgAttributeSpec.createSpec(SvgConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down Expand Up @@ -59,6 +61,7 @@ class TooltipBoxDemo : SimpleDemoBase(DEMO_BOX_SIZE) {
textColor,
borderColor,
strokeWidth,
lineType,
lines,
title,
textClassName,
Expand Down Expand Up @@ -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<TooltipSpec.Line>,
val title: String? = null,
val textClassName: String = TOOLTIP_TEXT_CLASS,
Expand Down
329 changes: 329 additions & 0 deletions docs/f-24b/theme_linetype.ipynb

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -317,38 +317,21 @@ 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) {
AestheticsUtil.updateFill(shape, p)
} 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,25 @@ enum class NamedLineType(val code: Int, private val myDashArray: List<Double>?)
}
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Double>? = null
private var myLineType: LineType? = null

init {
myPath = SvgPathElement(builder.build())
Expand Down Expand Up @@ -81,24 +82,19 @@ class LinePath(builder: SvgPathDataBuilder) : SvgComponent() {
}
}

fun dashArray(): WritableProperty<List<Double>> {
return object : WritableProperty<List<Double>> {
override fun set(value: List<Double>) {
myDashArray = ArrayList(value)
fun lineType(): WritableProperty<LineType> {
return object : WritableProperty<LineType> {
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!!)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,34 @@

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
* length of alternating dashes and gaps
* is defined as multiples of line width
*/
object StrokeDashArraySupport {
fun apply(element: SvgElement, strokeWidth: Double, dashArray: List<Double>) {
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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -36,6 +37,10 @@ interface AxisTheme {

fun tickMarkColor(): Color

fun lineType(): LineType

fun tickMarkLineType(): LineType

fun labelStyle(): ThemeTextStyle

fun rotateLabels(): Boolean
Expand All @@ -62,6 +67,7 @@ interface AxisTheme {
fun tooltipFill(): Color
fun tooltipColor(): Color
fun tooltipStrokeWidth(): Double
fun tooltipLineType(): LineType

fun tooltipTextStyle(): ThemeTextStyle
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
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.layout.TextJustification
import org.jetbrains.letsPlot.core.plot.base.render.linetype.LineType

interface LegendTheme {
fun keySize(): Double
Expand Down Expand Up @@ -40,4 +41,5 @@ interface LegendTheme {
fun backgroundColor(): Color
fun backgroundFill(): Color
fun backgroundStrokeWidth(): Double
fun backgroundLineType(): LineType
}
Loading