diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt index 68d90e0aa..47e32361c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt @@ -36,6 +36,7 @@ import com.patrykandpatrick.vico.core.common.data.MutableExtraStore import com.patrykandpatrick.vico.core.common.inClip import com.patrykandpatrick.vico.core.common.set import com.patrykandpatrick.vico.core.common.setAll +import java.util.SortedMap /** * A chart based on a Cartesian coordinate plane, composed of [CartesianLayer]s. @@ -53,7 +54,7 @@ public open class CartesianChart( private val tempInsets = Insets() private val axisManager = AxisManager() private val virtualLayout = VirtualLayout(axisManager) - private val _markerTargets = mutableMapOf>() + private val _markerTargets = sortedMapOf>() private val drawingModelAndLayerConsumer = object : ModelAndLayerConsumer { @@ -117,7 +118,9 @@ public open class CartesianChart( public val chartInsetters: Collection = persistentMarkers.values /** Links _x_ values to [CartesianMarker.Target]s. */ - public val markerTargets: Map> = _markerTargets + @Suppress("UNCHECKED_CAST") + public val markerTargets: SortedMap> = + _markerTargets as SortedMap> /** * The start axis. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt index a32d3effd..749e186e4 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt @@ -118,13 +118,13 @@ public fun CartesianDrawContext.drawMarker( if (previousX != null) visibilityListener?.onHidden(marker) return null } - var targets = chart.markerTargets.values.first() - var previousDistance = abs(markerTouchPoint.x - targets.first().canvasX) - for (i in 1..() + var previousDistance = Float.POSITIVE_INFINITY + for (xTargets in chart.markerTargets.values) { + val (distance, canvasXTargets) = + xTargets.groupBy { abs(markerTouchPoint.x - it.canvasX) }.toSortedMap().entries.first() if (distance > previousDistance) break - targets = potentialTargets + targets = canvasXTargets previousDistance = distance } marker.draw(this, targets) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt index 5ddf548a0..63896e6c2 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt @@ -89,7 +89,7 @@ public open class CandlestickCartesianLayer( public companion object } - private val _markerTargets = mutableMapOf() + private val _markerTargets = mutableMapOf>() /** * Holds information on the [CandlestickCartesianLayer]’s horizontal dimensions. @@ -98,7 +98,7 @@ public open class CandlestickCartesianLayer( protected val drawingModelKey: ExtraStore.Key = ExtraStore.Key() - override val markerTargets: Map = _markerTargets + override val markerTargets: Map> = _markerTargets override fun drawInternal( context: CartesianDrawContext, @@ -193,20 +193,22 @@ public open class CandlestickCartesianLayer( val limitedBodyBottomCanvasY = bodyBottomCanvasY.coerceIn(bounds.top, bounds.bottom) val limitedBodyTopCanvasY = bodyTopCanvasY.coerceIn(bounds.top, bounds.bottom) _markerTargets[entry.x] = - CandlestickCartesianLayerMarkerTarget( - x = entry.x, - canvasX = canvasX, - entry = entry, - openingCanvasY = - if (entry.absoluteChange == Change.Bullish) limitedBodyBottomCanvasY else limitedBodyTopCanvasY, - closingCanvasY = - if (entry.absoluteChange == Change.Bullish) limitedBodyTopCanvasY else limitedBodyBottomCanvasY, - lowCanvasY = lowCanvasY.coerceIn(bounds.top, bounds.bottom), - highCanvasY = highCanvasY.coerceIn(bounds.top, bounds.bottom), - openingColor = candle.body.solidOrStrokeColor, - closingColor = candle.body.solidOrStrokeColor, - lowColor = candle.bottomWick.solidOrStrokeColor, - highColor = candle.topWick.solidOrStrokeColor, + listOf( + CandlestickCartesianLayerMarkerTarget( + x = entry.x, + canvasX = canvasX, + entry = entry, + openingCanvasY = + if (entry.absoluteChange == Change.Bullish) limitedBodyBottomCanvasY else limitedBodyTopCanvasY, + closingCanvasY = + if (entry.absoluteChange == Change.Bullish) limitedBodyTopCanvasY else limitedBodyBottomCanvasY, + lowCanvasY = lowCanvasY.coerceIn(bounds.top, bounds.bottom), + highCanvasY = highCanvasY.coerceIn(bounds.top, bounds.bottom), + openingColor = candle.body.solidOrStrokeColor, + closingColor = candle.body.solidOrStrokeColor, + lowColor = candle.bottomWick.solidOrStrokeColor, + highColor = candle.topWick.solidOrStrokeColor, + ), ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt index 621b4ebcb..211938b07 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt @@ -33,7 +33,7 @@ import com.patrykandpatrick.vico.core.common.data.MutableExtraStore */ public interface CartesianLayer : BoundsAware, ChartInsetter { /** Links _x_ values to [CartesianMarker.Target]s. */ - public val markerTargets: Map + public val markerTargets: Map> /** * Draws the [CartesianLayer]. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt index 4e719b338..92a5f54a3 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt @@ -76,7 +76,7 @@ public open class ColumnCartesianLayer( DrawingModelInterpolator = DefaultDrawingModelInterpolator(), ) : BaseCartesianLayer() { - private val _markerTargets = mutableMapOf() + private val _markerTargets = mutableMapOf>() protected val stackInfo: MutableMap = mutableMapOf() @@ -87,7 +87,7 @@ public open class ColumnCartesianLayer( protected val drawingModelKey: ExtraStore.Key = ExtraStore.Key() - override val markerTargets: Map = _markerTargets + override val markerTargets: Map> = _markerTargets override fun drawInternal( context: CartesianDrawContext, @@ -165,7 +165,7 @@ public open class ColumnCartesianLayer( thicknessScale = zoom, ) ) { - updateMarkerTargets(entry, columnCenterX, columnSignificantY, column) + updateMarkerTargets(entry, columnCenterX, columnSignificantY, column, mergeMode) column.drawVertical(this, columnTop, columnBottom, columnCenterX, zoom, drawingModel?.opacity ?: 1f) } @@ -310,16 +310,30 @@ public open class ColumnCartesianLayer( canvasX: Float, canvasY: Float, column: LineComponent, + mergeMode: MergeMode, ) { if (canvasX <= bounds.left - 1 || canvasX >= bounds.right + 1) return - _markerTargets - .getOrPut(entry.x) { MutableColumnCartesianLayerMarkerTarget(entry.x, canvasX) } - .columns += + val targetColumn = ColumnCartesianLayerMarkerTarget.Column( entry, canvasY.coerceIn(bounds.top, bounds.bottom), column.solidOrStrokeColor, ) + when (mergeMode) { + MergeMode.Grouped -> + _markerTargets.getOrPut(entry.x) { mutableListOf() } += + MutableColumnCartesianLayerMarkerTarget(entry.x, canvasX, mutableListOf(targetColumn)) + MergeMode.Stacked -> + _markerTargets + .getOrPut(entry.x) { mutableListOf(MutableColumnCartesianLayerMarkerTarget(entry.x, canvasX)) } + .first() + .columns += + ColumnCartesianLayerMarkerTarget.Column( + entry, + canvasY.coerceIn(bounds.top, bounds.bottom), + column.solidOrStrokeColor, + ) + } } override fun updateChartValues( diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt index bb8cea71c..9732fd646 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt @@ -251,7 +251,7 @@ public open class LineCartesianLayer( } } - private val _markerTargets = mutableMapOf() + private val _markerTargets = mutableMapOf>() /** * The [Path] used to draw the lines, each of which corresponds to a [LineSpec]. @@ -265,7 +265,7 @@ public open class LineCartesianLayer( protected val drawingModelKey: ExtraStore.Key = ExtraStore.Key() - override val markerTargets: Map = _markerTargets + override val markerTargets: Map> = _markerTargets override fun drawInternal( context: CartesianDrawContext, @@ -349,7 +349,8 @@ public open class LineCartesianLayer( if (canvasX <= bounds.left - 1 || canvasX >= bounds.right + 1) return val limitedCanvasY = canvasY.coerceIn(bounds.top, bounds.bottom) _markerTargets - .getOrPut(entry.x) { MutableLineCartesianLayerMarkerTarget(entry.x, canvasX) } + .getOrPut(entry.x) { listOf(MutableLineCartesianLayerMarkerTarget(entry.x, canvasX)) } + .first() .points += LineCartesianLayerMarkerTarget.Point( entry,