diff --git a/future_changes.md b/future_changes.md index b9e2f94829c..4552c356d99 100644 --- a/future_changes.md +++ b/future_changes.md @@ -10,4 +10,5 @@ - Undesired vertical scroller when displaying `gggrid` in Jupyter notebook. - GeoJson structure breaks if the ring start label occurs several times [[#1086](https://github.com/JetBrains/lets-plot/issues/1086)]. - `theme`: left margin doesn't work for the `plot_title` parameter [[#1101](https://github.com/JetBrains/lets-plot/issues/1101)]. -- Support for marker rotation [[#736](https://github.com/JetBrains/lets-plot/issues/736)]. \ No newline at end of file +- Support for marker rotation [[#736](https://github.com/JetBrains/lets-plot/issues/736)]. +- Improve border line type experience [[LPK-220](https://github.com/JetBrains/lets-plot-kotlin/issues/220)]. \ No newline at end of file diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/BoxplotGeom.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/BoxplotGeom.kt index 4c33b35460a..616733b684a 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/BoxplotGeom.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/BoxplotGeom.kt @@ -53,7 +53,17 @@ class BoxplotGeom : GeomBase() { ctx: GeomContext, geomHelper: GeomHelper ) { - BoxHelper.buildMidlines(root, aesthetics, middleAesthetic = Aes.MIDDLE, ctx, geomHelper, fatten = fattenMidline) + BoxHelper.buildMidlines( + root, + aesthetics, + xAes = Aes.X, + middleAes = Aes.MIDDLE, + sizeAes = Aes.WIDTH, + ctx, + geomHelper, + fatten = fattenMidline, + flip = false + ) val elementHelper = geomHelper.createSvgElementHelper() for (p in aesthetics.dataPoints()) { diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/CrossBarGeom.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/CrossBarGeom.kt index d80d2ffbf0f..e99a1818e3e 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/CrossBarGeom.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/CrossBarGeom.kt @@ -15,7 +15,6 @@ import org.jetbrains.letsPlot.core.plot.base.geom.util.GeomHelper import org.jetbrains.letsPlot.core.plot.base.geom.util.HintColorUtil import org.jetbrains.letsPlot.core.plot.base.render.LegendKeyElementFactory import org.jetbrains.letsPlot.core.plot.base.render.SvgRoot -import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape class CrossBarGeom( private val isVertical: Boolean @@ -48,10 +47,6 @@ class CrossBarGeom( return flipHelper.flip(rectangle) } - private fun afterRotation(vector: DoubleVector): DoubleVector { - return flipHelper.flip(vector) - } - override fun buildIntern( root: SvgRoot, aesthetics: Aesthetics, @@ -64,7 +59,17 @@ class CrossBarGeom( root, aesthetics, pos, coord, ctx, rectFactory = clientRectByDataPoint(ctx, geomHelper, isHintRect = false) ) - buildMidlines(root, aesthetics, ctx, geomHelper, fatten = fattenMidline) + BoxHelper.buildMidlines( + root, + aesthetics, + xAes = afterRotation(Aes.X), + middleAes = afterRotation(Aes.Y), + sizeAes = Aes.WIDTH, // do not flip as height is not defined for CrossBarGeom + ctx, + geomHelper, + fatten = fattenMidline, + flip = !isVertical + ) // tooltip flipHelper.buildHints( listOf(Aes.YMIN, Aes.YMAX).map(::afterRotation), @@ -114,39 +119,6 @@ class CrossBarGeom( } } - private fun buildMidlines( - root: SvgRoot, - aesthetics: Aesthetics, - ctx: GeomContext, - geomHelper: GeomHelper, - fatten: Double - ) { - val elementHelper = geomHelper.createSvgElementHelper() - val xAes = afterRotation(Aes.X) - val yAes = afterRotation(Aes.Y) - val sizeAes = Aes.WIDTH // do not flip as height is not defined for CrossBarGeom - for (p in aesthetics.dataPoints()) { - val x = p.finiteOrNull(xAes) ?: continue - val middle = p.finiteOrNull(yAes) ?: continue - val w = p.finiteOrNull(sizeAes) ?: continue - - val width = w * ctx.getResolution(xAes) - val (line) = elementHelper.createLine( - afterRotation(DoubleVector(x - width / 2, middle)), - afterRotation(DoubleVector(x + width / 2, middle)), - p - ) ?: continue - - // TODO: use strokeScale in createLine() function - // adjust thickness - require(line is SvgShape) - val thickness = line.strokeWidth().get()!! - line.strokeWidth().set(thickness * fatten) - - root.add(line) - } - } - companion object { const val HANDLES_GROUPS = false private val LEGEND_FACTORY = BoxHelper.legendFactory(false) diff --git a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/BoxHelper.kt b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/BoxHelper.kt index c86cbea2f60..678aa229e6d 100644 --- a/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/BoxHelper.kt +++ b/plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/geom/util/BoxHelper.kt @@ -34,30 +34,30 @@ object BoxHelper { fun buildMidlines( root: SvgRoot, aesthetics: Aesthetics, - middleAesthetic: Aes, + xAes: Aes, + middleAes: Aes, + sizeAes: Aes, ctx: GeomContext, geomHelper: GeomHelper, - fatten: Double + fatten: Double, + flip: Boolean ) { val elementHelper = geomHelper.createSvgElementHelper() for (p in aesthetics.dataPoints()) { - val x = p.finiteOrNull(Aes.X) ?: continue - val middle = p.finiteOrNull(middleAesthetic) ?: continue - val w = p.finiteOrNull(Aes.WIDTH) ?: continue + val x = p.finiteOrNull(xAes) ?: continue + val middle = p.finiteOrNull(middleAes) ?: continue + val w = p.finiteOrNull(sizeAes) ?: continue - val width = w * ctx.getResolution(Aes.X) + val width = w * ctx.getResolution(xAes) val (line) = elementHelper.createLine( - DoubleVector(x - width / 2, middle), - DoubleVector(x + width / 2, middle), + DoubleVector(x - width / 2, middle).flipIf(flip), + DoubleVector(x + width / 2, middle).flipIf(flip), p - ) ?: continue + ) { AesScaling.strokeWidth(it) * fatten } ?: continue // TODO: use strokeScale in createLine() function - // adjust thickness require(line is SvgShape) - val thickness = line.strokeWidth().get()!! - line.strokeWidth().set(thickness * fatten) root.add(line) } 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 322d3cbf6f2..d2e842da827 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 @@ -16,14 +16,14 @@ enum class NamedLineType(val code: Int, private val myDashArray: List?) override val isSolid: Boolean get() = true }, - DASHED(2, listOf(4.3, 4.3)), - DOTTED(3, listOf(1.0, 3.2)), + DASHED(2, listOf(4.0, 4.0)), + DOTTED(3, listOf(1.0, 3.0)), @Suppress("SpellCheckingInspection") - DOTDASH(4, listOf(1.0, 3.2, 4.3, 3.2)), + DOTDASH(4, listOf(1.0, 3.0, 4.0, 3.0)), @Suppress("SpellCheckingInspection") - LONGDASH(5, listOf(7.4, 3.2)), + LONGDASH(5, listOf(7.0, 3.0)), @Suppress("SpellCheckingInspection") - TWODASH(6, listOf(2.4, 2.4, 6.4, 2.4)); + TWODASH(6, listOf(2.0, 2.0, 6.0, 2.0)); override val isSolid: Boolean get() = false diff --git a/plot-livemap/src/commonTest/kotlin/org/jetbrains/letsPlot/core/plot/livemap/PathConverterTest.kt b/plot-livemap/src/commonTest/kotlin/org/jetbrains/letsPlot/core/plot/livemap/PathConverterTest.kt index c70249f32dd..7b238ee3b82 100644 --- a/plot-livemap/src/commonTest/kotlin/org/jetbrains/letsPlot/core/plot/livemap/PathConverterTest.kt +++ b/plot-livemap/src/commonTest/kotlin/org/jetbrains/letsPlot/core/plot/livemap/PathConverterTest.kt @@ -48,7 +48,7 @@ class PathConverterTest { matcher!! .strokeWidth(eq(1.1)) - .lineDash(vectorEq(listOf(4.73, 4.73))) + .lineDash(vectorEq(listOf(4.4, 4.4))) assertMapObject() } @@ -61,7 +61,7 @@ class PathConverterTest { matcher!! .strokeWidth(eq(4.4)) - .lineDash(vectorEq(listOf(18.92, 18.92))) + .lineDash(vectorEq(listOf(17.6, 17.6))) assertMapObject() }