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

coord_polar: axis, ticks, labels, xlim/ylim #979

Merged
merged 18 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c574810
Make expand additive
IKupriyanov-HORIS Dec 20, 2023
2467a22
coord_polar: reduce number of ticks for v axis
IKupriyanov-HORIS Dec 20, 2023
4fc32be
coord_polar: replace absolute expand with relative
IKupriyanov-HORIS Dec 20, 2023
4be95a0
coord_polar: v axis ticks alignment
IKupriyanov-HORIS Dec 20, 2023
e3d7535
coord_polar: hardcoded anchor for ticks
IKupriyanov-HORIS Dec 20, 2023
a9705f1
coord_polar: more ticks for circle axis
IKupriyanov-HORIS Dec 21, 2023
52151a7
coord_polar: fix horizontal axis ticks order, fix axis circle positio…
IKupriyanov-HORIS Dec 21, 2023
7af0732
coord_polar: fix horizontal axis first and last ticks overlapping
IKupriyanov-HORIS Dec 21, 2023
f6fd891
coord_polar: update notebook
IKupriyanov-HORIS Dec 22, 2023
0900649
coord_polar: replace thetaFromX with flipped
IKupriyanov-HORIS Dec 25, 2023
f54d197
coord_polar: temp fix for axis, need proper fix for the domain flip
IKupriyanov-HORIS Dec 26, 2023
62f8d02
coord_polar: properly handle theta=y in PolarCoordinateSystem
IKupriyanov-HORIS Dec 27, 2023
dc71ebc
coord_polar: handle theta=y mostly in PolarCoordinateSystem. Investig…
IKupriyanov-HORIS Dec 27, 2023
9c94803
coord_polar: fixed breaks and ticks. The only not working case is a Y…
IKupriyanov-HORIS Dec 28, 2023
a550e39
coord_polar: fixed breaks and ticks with start parameter
IKupriyanov-HORIS Dec 28, 2023
b99f9ea
coord_polar: minor code cleanup
IKupriyanov-HORIS Dec 29, 2023
09921c8
coord_polar: fix domain for discrete angle scale
IKupriyanov-HORIS Jan 1, 2024
0e4782f
coord_polar: add xlim/ylim for radar plot / lollipop
IKupriyanov-HORIS Jan 5, 2024
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
Prev Previous commit
Next Next commit
coord_polar: fix horizontal axis ticks order, fix axis circle positio…
…ned above ticks
  • Loading branch information
IKupriyanov-HORIS committed Jan 5, 2024
commit 52151a7b155506867408688efbf61b79e12f1050
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package org.jetbrains.letsPlot.commons.geometry
import kotlin.math.*

class DoubleVector(val x: Double, val y: Double) {
constructor(x: Number, y: Number) : this(x.toDouble(), y.toDouble())

operator fun component1(): Double = x
operator fun component2(): Double = y

Expand Down
578 changes: 340 additions & 238 deletions docs/dev/notebooks/coord_polar.ipynb

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.jetbrains.letsPlot.core.plot.builder.GeomLayer
import org.jetbrains.letsPlot.core.plot.builder.MarginalLayerUtil
import org.jetbrains.letsPlot.core.plot.builder.PlotSvgComponent
import org.jetbrains.letsPlot.core.plot.builder.coord.CoordProvider
import org.jetbrains.letsPlot.core.plot.builder.coord.PolarCoordProvider
import org.jetbrains.letsPlot.core.plot.builder.frame.BogusFrameOfReferenceProvider
import org.jetbrains.letsPlot.core.plot.builder.frame.SquareFrameOfReferenceProvider
import org.jetbrains.letsPlot.core.plot.builder.layout.GeomMarginsLayout
Expand Down Expand Up @@ -97,9 +98,13 @@ class PlotAssembler constructor(

val flipAxis = coordProvider.flipped

val (hAxisPosition, vAxisPosition) = when (flipAxis) {
true -> yAxisPosition.flip() to xAxisPosition.flip()
else -> xAxisPosition to yAxisPosition
val (hAxisPosition, vAxisPosition) = if (coordProvider is PolarCoordProvider) {
AxisPosition.TOP to AxisPosition.LEFT
} else {
when (flipAxis) {
true -> yAxisPosition.flip() to xAxisPosition.flip()
else -> xAxisPosition to yAxisPosition
}
}

frameProviderByTile = frameProviderByTile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,20 @@ class PolarAxisComponent(

private fun buildAxis() {
val rootElement = rootGroup
val start = 0.0
val end: Double = length

// Axis line
if (!hideAxisBreaks && axisTheme.showLine()) {
if (orientation.isHorizontal) {
val cx = length / 2
val cy = length / 2
val circle = SvgCircleElement(cx, cy, length / 2).apply {
strokeWidth().set(gridTheme.majorLineWidth())
strokeColor().set(gridTheme.majorLineColor())
fillColor().set(Color.TRANSPARENT)
}
rootElement.children().add(circle)
}
}

// Axis
if (!hideAxis) {
Expand All @@ -57,29 +69,14 @@ class PolarAxisComponent(

when (orientation.isHorizontal) {
false -> SvgUtils.transformTranslate(group, 0.0, br.y)
true -> SvgUtils.transformTranslate(group, br.x, -br.y)
true -> SvgUtils.transformTranslate(group, br.x, br.y)
}

rootElement.children().add(group)
//}
}
}

// Axis line
if (!hideAxisBreaks && axisTheme.showLine()) {
if (orientation.isHorizontal) {
val cx = length / 2
val cy = -length / 2
val circle = SvgCircleElement(cx, cy, length / 2).apply {
strokeWidth().set(gridTheme.majorLineWidth())
strokeColor().set(gridTheme.majorLineColor())
fillColor().set(Color.TRANSPARENT)
}
rootElement.children().add(circle)
}
}
}

}

private fun buildTick(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

package org.jetbrains.letsPlot.core.plot.builder

import org.jetbrains.letsPlot.commons.geometry.DoubleRectangle
import org.jetbrains.letsPlot.commons.geometry.DoubleVector
import org.jetbrains.letsPlot.core.plot.base.scale.ScaleBreaks
import org.jetbrains.letsPlot.core.plot.builder.AxisUtil.breaksData
import org.jetbrains.letsPlot.core.plot.builder.coord.PolarCoordProvider
import org.jetbrains.letsPlot.core.plot.builder.defaultTheme.DefaultTheme
import org.jetbrains.letsPlot.core.plot.builder.guide.AxisComponent
import org.jetbrains.letsPlot.core.plot.builder.guide.Orientation
import org.junit.Test
import kotlin.test.assertEquals

class BreaksDataTest {
@Test
fun simple() {
val polarCoordProvider = PolarCoordProvider(thetaFromX = true, start = 0.0, clockwise = true)
val adjustedDomain = DoubleRectangle.XYWH(-5.0, 10.0, 5.0, 8.625)
val clientSize = DoubleVector(504.0, 504.0)
val coordinateSystem = polarCoordProvider.createCoordinateSystem(adjustedDomain, clientSize)
val breaksData = breaksData(
scaleBreaks = ScaleBreaks(
domainValues = listOf(-5.0, -4.0, -3.0, -2.0, -1.0, 0.0),
transformedValues = listOf(-5.0, -4.0, -3.0, -2.0, -1.0, 0.0),
labels = listOf("-5", "-4", "-3", "-2", "-1", "0"),
),
coord = coordinateSystem,
domain = adjustedDomain,
flipAxis = false,
orientation = Orientation.BOTTOM,
axisTheme = DefaultTheme.minimal2().horizontalAxis(flipAxis = false),
labelAdjustments = AxisComponent.TickLabelAdjustments(Orientation.BOTTOM)
)

// Breaks start at top center
val topCenter = DoubleVector(clientSize.x / 2, 0.0)

assertDoubleVectorEquals(topCenter, breaksData.majorBreaks[0])
assertDoubleVectorEquals(DoubleVector(491, 174), breaksData.majorBreaks[1])
assertDoubleVectorEquals(DoubleVector(400, 455), breaksData.majorBreaks[2])
assertDoubleVectorEquals(DoubleVector(103, 455), breaksData.majorBreaks[3])
assertDoubleVectorEquals(DoubleVector(12, 174), breaksData.majorBreaks[4])
assertDoubleVectorEquals(topCenter, breaksData.majorBreaks[5])
}

private fun assertDoubleVectorEquals(expected: DoubleVector, actual: DoubleVector, tolerance: Double = 1.0) {
assertEquals(expected.x, actual.x, tolerance)
assertEquals(expected.y, actual.y, tolerance)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import kotlin.test.Test
import kotlin.test.assertContentEquals

class PolarCoordTest {
private val x = listOf(0, 90, 180, 270, 360)
private val y = listOf(0, 1, 2, 3, 4)
private val adjustedDomain = DoubleRectangle.LTRB(0, 0, 360, 4)
private val clientSize = DoubleVector(200.0, 200.0)

private fun applyPolarTransform(start: Double, clockwise: Boolean): List<Vector> {
val polarCoordProvider = PolarCoordProvider(thetaFromX = true, start, clockwise)
val x = listOf(0, 90, 180, 270, 360)
val y = listOf(0, 1, 2, 3, 4)
val adjustedDomain = DoubleRectangle.LTRB(0, 0, 360, 4)
val clientSize = DoubleVector(200.0, 200.0)

val polarMapper = polarCoordProvider.createCoordinateMapper(
adjustedDomain = adjustedDomain,
Expand All @@ -32,6 +32,15 @@ class PolarCoordTest {
.map { (x, y) -> Vector(x.roundToInt(), y.roundToInt()) }
}

private fun applyPolarScreenTransform(start: Double, clockwise: Boolean): List<Vector> {
val polarCoordProvider = PolarCoordProvider(thetaFromX = true, start, clockwise)
val coordinateSystem = polarCoordProvider.createCoordinateSystem(adjustedDomain, clientSize)

return x.zip(y)
.mapNotNull { (x, y) -> coordinateSystem.toClient(DoubleVector(x.toDouble(), y.toDouble())) }
.map { (x, y) -> Vector(x.roundToInt(), y.roundToInt()) }
}

@Test
fun default() {
assertContentEquals(
Expand All @@ -44,6 +53,17 @@ class PolarCoordTest {
),
actual = applyPolarTransform(start = 0.0, clockwise = true)
)

assertContentEquals(
expected = listOf(
Vector(100, 100),
Vector(125, 100),
Vector(100, 150),
Vector(25, 100),
Vector(100, 0),
),
actual = applyPolarScreenTransform(start = 0.0, clockwise = true)
)
}

@Test
Expand All @@ -58,6 +78,16 @@ class PolarCoordTest {
),
actual = applyPolarTransform(start = 0.0, clockwise = false)
)
assertContentEquals(
expected = listOf(
Vector(100, 100),
Vector(75, 100),
Vector(100, 150),
Vector(175, 100),
Vector(100, 0),
),
actual = applyPolarScreenTransform(start = 0.0, clockwise = false)
)
}

@Test
Expand All @@ -72,6 +102,17 @@ class PolarCoordTest {
),
actual = applyPolarTransform(start = PI / 2, clockwise = true)
)

assertContentEquals(
expected = listOf(
Vector(100, 100),
Vector(100, 125),
Vector(50, 100),
Vector(100, 25),
Vector(200, 100),
),
actual = applyPolarScreenTransform(start = PI / 2, clockwise = true)
)
}

@Test
Expand All @@ -86,6 +127,17 @@ class PolarCoordTest {
),
actual = applyPolarTransform(start = -PI / 2, clockwise = true)
)

assertContentEquals(
expected = listOf(
Vector(100, 100),
Vector(100, 75),
Vector(150, 100),
Vector(100, 175),
Vector(0, 100),
),
actual = applyPolarScreenTransform(start = -PI / 2, clockwise = true)
)
}


Expand All @@ -101,5 +153,16 @@ class PolarCoordTest {
),
actual = applyPolarTransform(start = PI / 2, clockwise = false)
)

assertContentEquals(
expected = listOf(
Vector(100, 100),
Vector(100, 125),
Vector(150, 100),
Vector(100, 25),
Vector(0, 100),
),
actual = applyPolarScreenTransform(start = PI / 2, clockwise = false)
)
}
}