Skip to content

Commit

Permalink
Marker rotation (#1096)
Browse files Browse the repository at this point in the history
* Support for marker rotation

* Circles must not rotate in target SVG

* Add RotateSpec for parameters count minimization

* Add shape rotation for points on livemap.

* Use SvgTransform.

* Revert "Add shape rotation for points on livemap."

This reverts commit 2281fca

* Undo marker rotation for TinyDotShape
  • Loading branch information
RYangazov committed May 23, 2024
1 parent 569e87f commit 3125625
Show file tree
Hide file tree
Showing 13 changed files with 680 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.jetbrains.letsPlot.datamodel.svg.dom.slim

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform

internal abstract class SlimBase protected constructor(val elementName: String) :
SvgSlimShape {
Expand Down Expand Up @@ -61,6 +62,10 @@ internal abstract class SlimBase protected constructor(val elementName: String)
setAttribute(strokeDashArray, v)
}

override fun setTransform(t: SvgTransform) {
setAttribute(transform, t.toString())
}

internal fun setAttribute(index: Int, v: Double) {
setAttribute(index, v.toString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
package org.jetbrains.letsPlot.datamodel.svg.dom.slim

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform

interface SvgSlimShape : SvgSlimObject {
fun setFill(c: Color, alpha: Double)
fun setStroke(c: Color, alpha: Double)
fun setStrokeWidth(v: Double)
fun setStrokeDashArray(v: String)
fun setTransform(t: SvgTransform)
}
597 changes: 597 additions & 0 deletions docs/dev/notebooks/rotate_points.ipynb

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
- Livemap: improve "tiles" documentation [[#1093](https://github.com/JetBrains/lets-plot/issues/1093)].
- 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)].
- `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)].
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ object GeomMeta {
Aes.FILL,
Aes.ALPHA,
Aes.SHAPE,
Aes.ANGLE,
Aes.MAP_ID
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.core.plot.base.DataPointAesthetics
import org.jetbrains.letsPlot.core.plot.base.render.point.UpdatableShape
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgShape
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgUtils

object AestheticsUtil {
Expand All @@ -24,7 +25,14 @@ object AestheticsUtil {
return Color.TRANSPARENT
}

fun decorate(shape: UpdatableShape, filled: Boolean, solid: Boolean, p: DataPointAesthetics, strokeWidth: Double) {
fun decorate(
shape: UpdatableShape,
filled: Boolean,
solid: Boolean,
p: DataPointAesthetics,
strokeWidth: Double,
transform: SvgTransform?
) {
val fill = fill(filled, solid, p)
val stroke = p.color()!!

Expand All @@ -38,7 +46,7 @@ object AestheticsUtil {
strokeAlpha = alpha(stroke, p)
}

shape.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth)
shape.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth, transform)
}

fun alpha(color: Color, p: DataPointAesthetics): Double {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
package org.jetbrains.letsPlot.core.plot.base.render.point

import org.jetbrains.letsPlot.commons.geometry.DoubleVector
import org.jetbrains.letsPlot.core.plot.base.Aes
import org.jetbrains.letsPlot.core.plot.base.DataPointAesthetics
import org.jetbrains.letsPlot.core.plot.base.aes.AestheticsUtil
import org.jetbrains.letsPlot.core.plot.base.render.point.NamedShape.*
import org.jetbrains.letsPlot.core.plot.base.render.point.symbol.CircleGlyph
import org.jetbrains.letsPlot.core.plot.base.render.point.symbol.Glyph
import org.jetbrains.letsPlot.core.plot.base.render.point.symbol.Glyphs
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransformBuilder
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimElements
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimObject

Expand Down Expand Up @@ -52,7 +55,13 @@ object PointShapeSvg {
): SvgSlimObject {
val stroke = shape.strokeWidth(p)
val glyph = createSlimGlyph(shape, location, size, stroke)
AestheticsUtil.decorate(glyph, shape.isFilled, shape.isSolid, p, stroke)
val angle = p.finiteOrNull(Aes.ANGLE)
val transform = if (glyph is CircleGlyph) null
else angle?.let {
SvgTransformBuilder().rotate(it, location).build()
}

AestheticsUtil.decorate(glyph, shape.isFilled, shape.isSolid, p, stroke, transform)
return glyph
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
package org.jetbrains.letsPlot.core.plot.base.render.point

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform

interface UpdatableShape {
fun update(fill: Color, fillAlpha: Double, stroke: Color, strokeAlpha: Double, strokeWidth: Double)
fun update(
fill: Color,
fillAlpha: Double,
stroke: Color,
strokeAlpha: Double,
strokeWidth: Double,
transform: SvgTransform?
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@
package org.jetbrains.letsPlot.core.plot.base.render.point.symbol

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimGroup

internal class GlyphPair(private val myG1: Glyph, private val myG2: Glyph) :
Glyph {

override fun update(fill: Color, fillAlpha: Double, stroke: Color, strokeAlpha: Double, strokeWidth: Double) {
myG1.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth)
myG2.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth)
override fun update(
fill: Color,
fillAlpha: Double,
stroke: Color,
strokeAlpha: Double,
strokeWidth: Double,
transform: SvgTransform?
) {
myG1.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth, transform)
myG2.update(fill, fillAlpha, stroke, strokeAlpha, strokeWidth, transform)
}

override fun appendTo(g: SvgSlimGroup) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
package org.jetbrains.letsPlot.core.plot.base.render.point.symbol

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimShape

internal abstract class MultiShapeGlyph : Glyph {
protected fun update(shape: SvgSlimShape?, fill: Color, fillAlpha: Double, stroke: Color, strokeAlpha: Double, strokeWidth: Double) {
protected fun update(
shape: SvgSlimShape?,
fill: Color,
fillAlpha: Double,
stroke: Color,
strokeAlpha: Double,
strokeWidth: Double,
transform: SvgTransform?
) {
shape?.setFill(fill, fillAlpha)
shape?.setStroke(stroke, strokeAlpha)
shape?.setStrokeWidth(strokeWidth)
transform?.let { shape?.setTransform(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package org.jetbrains.letsPlot.core.plot.base.render.point.symbol

import org.jetbrains.letsPlot.commons.geometry.DoubleVector
import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimGroup
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimShape

Expand All @@ -23,10 +24,18 @@ abstract class SingletonGlyph : Glyph {

protected abstract fun createShape(location: DoubleVector, width: Double): SvgSlimShape

override fun update(fill: Color, fillAlpha: Double, stroke: Color, strokeAlpha: Double, strokeWidth: Double) {
override fun update(
fill: Color,
fillAlpha: Double,
stroke: Color,
strokeAlpha: Double,
strokeWidth: Double,
transform: SvgTransform?
) {
myShape.setFill(fill, fillAlpha)
myShape.setStroke(stroke, strokeAlpha)
myShape.setStrokeWidth(strokeWidth)
transform?.let { myShape.setTransform(it) }
}

override fun appendTo(g: SvgSlimGroup) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.jetbrains.letsPlot.core.plot.base.render.point.symbol

import org.jetbrains.letsPlot.commons.values.Color
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgTransform
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimGroup
import org.jetbrains.letsPlot.datamodel.svg.dom.slim.SvgSlimShape

Expand All @@ -18,9 +19,16 @@ internal abstract class TwoShapeGlyph : MultiShapeGlyph() {
myS2 = s2
}

override fun update(fill: Color, fillAlpha: Double, stroke: Color, strokeAlpha: Double, strokeWidth: Double) {
update(myS1, fill, fillAlpha, stroke, strokeAlpha, strokeWidth)
update(myS2, fill, fillAlpha, stroke, strokeAlpha, strokeWidth)
override fun update(
fill: Color,
fillAlpha: Double,
stroke: Color,
strokeAlpha: Double,
strokeWidth: Double,
transform: SvgTransform?
) {
update(myS1, fill, fillAlpha, stroke, strokeAlpha, strokeWidth, transform)
update(myS2, fill, fillAlpha, stroke, strokeAlpha, strokeWidth, transform)
}

override fun appendTo(g: SvgSlimGroup) {
Expand Down
1 change: 1 addition & 0 deletions python-package/lets_plot/plot/geom.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def geom_point(mapping=None, *, data=None, stat=None, position=None, show_legend
- color (colour) : color of the geometry. String in the following formats: RGB/RGBA (e.g. "rgb(0, 0, 255)"); HEX (e.g. "#0000FF"); color name (e.g. "red"); role name ("pen", "paper" or "brush").
- fill : fill color. Is applied only to the points of shapes having inner area. String in the following formats: RGB/RGBA (e.g. "rgb(0, 0, 255)"); HEX (e.g. "#0000FF"); color name (e.g. "red"); role name ("pen", "paper" or "brush").
- shape : shape of the point, an integer from 0 to 25.
- angle : rotation angle of the point shape, in degrees.
- size : size of the point.
- stroke : width of the shape border. Applied only to the shapes having border.
Expand Down

0 comments on commit 3125625

Please sign in to comment.