Skip to content

Commit 7c9c731

Browse files
Improve tooltip-triggering logic for point targets — non-point geoms that use the POINT target were unintentionally assigned FAKE_DISTANCE, causing nearer objects to receive a lower weight
1 parent 9df15e2 commit 7c9c731

File tree

10 files changed

+47
-16
lines changed

10 files changed

+47
-16
lines changed

livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/livemap/chart/Locator.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ import org.jetbrains.letsPlot.livemap.Client
1111
import org.jetbrains.letsPlot.livemap.core.ecs.EcsEntity
1212
import org.jetbrains.letsPlot.livemap.mapengine.RenderHelper
1313

14+
enum class HoverObjectKind {
15+
POINT,
16+
PATH,
17+
POLYGON,
18+
}
19+
1420
data class HoverObject(
21+
val kind: HoverObjectKind,
1522
val layerIndex: Int,
1623
val index: Int,
1724
val distance: Double,

livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/livemap/chart/donut/Locator.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55

66
package org.jetbrains.letsPlot.livemap.chart.donut
77

8-
import org.jetbrains.letsPlot.commons.intern.typedGeometry.*
8+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Scalar
9+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Vec
10+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.length
11+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.minus
912
import org.jetbrains.letsPlot.livemap.Client
1013
import org.jetbrains.letsPlot.livemap.chart.*
1114
import org.jetbrains.letsPlot.livemap.chart.Locator
1215
import org.jetbrains.letsPlot.livemap.core.ecs.EcsEntity
1316
import org.jetbrains.letsPlot.livemap.mapengine.RenderHelper
1417
import org.jetbrains.letsPlot.livemap.mapengine.placement.WorldOriginComponent
15-
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Scalar
16-
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Vec
17-
import org.jetbrains.letsPlot.commons.intern.typedGeometry.length
1818
import kotlin.math.PI
1919
import kotlin.math.abs
2020
import kotlin.math.atan2
@@ -40,6 +40,7 @@ object Locator : Locator {
4040
)
4141
) {
4242
return HoverObject(
43+
kind = HoverObjectKind.POINT,
4344
layerIndex = target.get<IndexComponent>().layerIndex,
4445
index = sector.index,
4546
distance = 0.0,

livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/livemap/chart/path/PathLocator.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ package org.jetbrains.letsPlot.livemap.chart.path
77

88
import org.jetbrains.letsPlot.commons.intern.math.isOnSegment
99
import org.jetbrains.letsPlot.commons.intern.math.projection
10-
import org.jetbrains.letsPlot.commons.intern.typedGeometry.*
10+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.MultiLineString
11+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Vec
12+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.toDoubleVector
13+
import org.jetbrains.letsPlot.commons.intern.typedGeometry.toVec
1114
import org.jetbrains.letsPlot.commons.intern.util.ClosestPointChecker
1215
import org.jetbrains.letsPlot.livemap.Client
1316
import org.jetbrains.letsPlot.livemap.World
1417
import org.jetbrains.letsPlot.livemap.chart.HoverObject
18+
import org.jetbrains.letsPlot.livemap.chart.HoverObjectKind
1519
import org.jetbrains.letsPlot.livemap.chart.IndexComponent
1620
import org.jetbrains.letsPlot.livemap.chart.Locator
1721
import org.jetbrains.letsPlot.livemap.core.ecs.EcsEntity
@@ -32,6 +36,7 @@ object PathLocator : Locator {
3236
)
3337
if (candidate != null) {
3438
return HoverObject(
39+
kind = HoverObjectKind.PATH,
3540
layerIndex = target.get<IndexComponent>().layerIndex,
3641
index = target.get<IndexComponent>().index,
3742
distance = renderHelper.dimToClient(pointChecker.distance).value,

livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/livemap/chart/point/PointLocator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ object PointLocator : Locator {
2828
val distance = (renderHelper.posToWorld(coord) - origin).length
2929
if (distance <= radius + renderHelper.dimToWorld(EXTRA_RADIUS)) {
3030
return HoverObject(
31+
kind = HoverObjectKind.POINT,
3132
layerIndex = target.get<IndexComponent>().layerIndex,
3233
index = target.get<IndexComponent>().index,
3334
distance = 0.0,//renderHelper.dimToClient(distance).value,

livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/livemap/chart/polygon/PolygonLocator.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.jetbrains.letsPlot.commons.intern.typedGeometry.contains
1111
import org.jetbrains.letsPlot.livemap.Client
1212
import org.jetbrains.letsPlot.livemap.World
1313
import org.jetbrains.letsPlot.livemap.chart.HoverObject
14+
import org.jetbrains.letsPlot.livemap.chart.HoverObjectKind
1415
import org.jetbrains.letsPlot.livemap.chart.IndexComponent
1516
import org.jetbrains.letsPlot.livemap.chart.Locator
1617
import org.jetbrains.letsPlot.livemap.chart.fragment.RegionFragmentsComponent
@@ -25,6 +26,7 @@ object PolygonLocator : Locator {
2526
target.get<RegionFragmentsComponent>().fragments.forEach { fragment ->
2627
if (isCoordinateOnEntity(coord, fragment, renderHelper)) {
2728
return HoverObject(
29+
kind = HoverObjectKind.POLYGON,
2830
layerIndex = target.get<IndexComponent>().layerIndex,
2931
index = target.get<IndexComponent>().index,
3032
distance = 0.0,
@@ -37,6 +39,7 @@ object PolygonLocator : Locator {
3739
} else {
3840
return when (isCoordinateOnEntity(coord, target, renderHelper)) {
3941
true -> HoverObject(
42+
kind = HoverObjectKind.POLYGON,
4043
layerIndex = target.get<IndexComponent>().layerIndex,
4144
index = target.get<IndexComponent>().index,
4245
distance = 0.0,

plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/tooltip/GeomTargetLocator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface GeomTargetLocator {
3737
open val distance: Double,
3838
open val geomKind: GeomKind,
3939
open val contextualMapping: ContextualMapping,
40-
val isCrosshairEnabled: Boolean
40+
val isCrosshairEnabled: Boolean,
41+
val hitShapeKind: HitShape.Kind
4142
)
4243
}

plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/loc/LayerTargetLocator.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator.LookupSpa
1616
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator.LookupSpace.Y
1717
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator.LookupStrategy
1818
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator.LookupStrategy.HOVER
19+
import org.jetbrains.letsPlot.core.plot.base.tooltip.HitShape
1920
import org.jetbrains.letsPlot.core.plot.base.tooltip.HitShape.Kind.*
2021
import org.jetbrains.letsPlot.core.plot.base.tooltip.TipLayoutHint.Kind.CURSOR_TOOLTIP
2122
import org.jetbrains.letsPlot.core.plot.builder.tooltip.loc.LayerTargetLocator.Collector.CollectingStrategy
@@ -93,7 +94,8 @@ internal class LayerTargetLocator(
9394

9495
private fun addLookupResults(
9596
collector: Collector<GeomTarget>,
96-
targets: MutableList<GeomTargetLocator.LookupResult>
97+
targets: MutableList<GeomTargetLocator.LookupResult>,
98+
hitShapeKind: HitShape.Kind
9799
) {
98100
if (collector.size() == 0) {
99101
return
@@ -107,7 +109,8 @@ internal class LayerTargetLocator(
107109
max(0.0, collector.closestPointChecker.distance),
108110
geomKind,
109111
contextualMapping,
110-
contextualMapping.isCrosshairEnabled
112+
contextualMapping.isCrosshairEnabled,
113+
hitShapeKind
111114
)
112115
)
113116
}
@@ -134,10 +137,10 @@ internal class LayerTargetLocator(
134137

135138
val lookupResults = ArrayList<GeomTargetLocator.LookupResult>()
136139

137-
addLookupResults(pathCollector, lookupResults)
138-
addLookupResults(rectCollector, lookupResults)
139-
addLookupResults(pointCollector, lookupResults)
140-
addLookupResults(polygonCollector, lookupResults)
140+
addLookupResults(pathCollector, lookupResults, HitShape.Kind.PATH)
141+
addLookupResults(rectCollector, lookupResults, HitShape.Kind.RECT)
142+
addLookupResults(pointCollector, lookupResults, HitShape.Kind.POINT)
143+
addLookupResults(polygonCollector, lookupResults, HitShape.Kind.POLYGON)
141144

142145
return getClosestTarget(lookupResults)
143146
}

plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/loc/LocatedTargetsPicker.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.jetbrains.letsPlot.commons.intern.math.distance
1111
import org.jetbrains.letsPlot.core.plot.base.GeomKind.*
1212
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTarget
1313
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator.LookupResult
14+
import org.jetbrains.letsPlot.core.plot.base.tooltip.HitShape
1415
import kotlin.math.abs
1516

1617
class LocatedTargetsPicker(
@@ -130,14 +131,14 @@ class LocatedTargetsPicker(
130131
// between cursor is zero). Fake the distance to give a chance for tooltips from other layers.
131132
return if (lookupResult.distance == 0.0) {
132133
when {
133-
lookupResult.geomKind == POINT -> 0.0 // Points are small; on hovering over them, we don't want to give priority to other tooltips by faking distance.
134134
lookupResult.isCrosshairEnabled -> {
135135
// use XY distance for tooltips with crosshair to avoid giving them priority
136136
lookupResult.targets
137137
.filter { it.tipLayoutHint.coord != null }
138138
.minOfOrNull { target -> distance(coord, target.tipLayoutHint.coord!!) }
139139
?: FAKE_DISTANCE
140140
}
141+
lookupResult.hitShapeKind == HitShape.Kind.POINT -> 0.0 // Points are small; on hovering over them, we don't want to give priority to other tooltips by faking distance.
141142
else -> FAKE_DISTANCE // fake distance to give a chance for tooltips from other layers
142143
}
143144
} else {
@@ -154,7 +155,8 @@ class LocatedTargetsPicker(
154155
distance = distance,
155156
geomKind = geomKind,
156157
contextualMapping = contextualMapping,
157-
isCrosshairEnabled = isCrosshairEnabled
158+
isCrosshairEnabled = isCrosshairEnabled,
159+
hitShapeKind = hitShapeKind
158160
)
159161

160162
private fun filterResults(

plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/tooltip/loc/TransformedTargetLocator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ abstract class TransformedTargetLocator(private val targetLocator: GeomTargetLoc
2626
convertToPlotDistance(lookupResult.distance),
2727
lookupResult.geomKind,
2828
lookupResult.contextualMapping,
29-
lookupResult.contextualMapping.isCrosshairEnabled
29+
lookupResult.contextualMapping.isCrosshairEnabled,
30+
lookupResult.hitShapeKind
3031
)
3132
}
3233

plot-livemap/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/livemap/LiveMapProviderUtil.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.jetbrains.letsPlot.core.plot.base.geom.util.HintColorUtil
1717
import org.jetbrains.letsPlot.core.plot.base.livemap.LivemapConstants.Projection.*
1818
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTarget
1919
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator
20+
import org.jetbrains.letsPlot.core.plot.base.tooltip.HitShape
2021
import org.jetbrains.letsPlot.core.plot.base.tooltip.TipLayoutHint
2122
import org.jetbrains.letsPlot.core.plot.builder.GeomLayer
2223
import org.jetbrains.letsPlot.core.plot.builder.LayerRendererUtil.LayerRendererData
@@ -40,6 +41,7 @@ import org.jetbrains.letsPlot.livemap.api.Services
4041
import org.jetbrains.letsPlot.livemap.api.liveMapGeocoding
4142
import org.jetbrains.letsPlot.livemap.api.liveMapVectorTiles
4243
import org.jetbrains.letsPlot.livemap.chart.HoverObject
44+
import org.jetbrains.letsPlot.livemap.chart.HoverObjectKind
4345
import org.jetbrains.letsPlot.livemap.config.DevParams
4446
import org.jetbrains.letsPlot.livemap.config.LiveMapCanvasFigure
4547
import org.jetbrains.letsPlot.livemap.core.Clipboard
@@ -246,7 +248,12 @@ object LiveMapProviderUtil {
246248
distance = hoverObjects.maxOf { it.distance },
247249
geomKind = layer.geomKind,
248250
contextualMapping = layer.contextualMapping,
249-
isCrosshairEnabled = false // no crosshair on livemap
251+
isCrosshairEnabled = false, // no crosshair on livemap,
252+
hitShapeKind = when (hoverObjects.first().kind) {
253+
HoverObjectKind.POINT -> HitShape.Kind.POINT
254+
HoverObjectKind.PATH -> HitShape.Kind.PATH
255+
HoverObjectKind.POLYGON -> HitShape.Kind.POLYGON
256+
}
250257
)
251258
}
252259
}

0 commit comments

Comments
 (0)