From 32152c88a8cec6c72290ad992483dd6a32ef16d8 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sun, 12 Nov 2023 10:58:34 +0100 Subject: [PATCH] `ChartEntryModelProducer` and `ComposedChartEntryModelProducer`: Introduce extras Co-authored-by: Patryk Goworowski --- .../chart/entry/ChartEntryModelExtensions.kt | 6 +- .../patrykandpatrick/vico/core/chart/Chart.kt | 12 +- .../vico/core/chart/column/ColumnChart.kt | 20 ++-- .../vico/core/chart/composed/ComposedChart.kt | 14 +-- .../vico/core/chart/line/LineChart.kt | 20 ++-- .../vico/core/entry/ChartEntryModel.kt | 8 +- .../core/entry/ChartEntryModelProducer.kt | 56 +++++---- .../vico/core/entry/ChartModelProducer.kt | 4 +- .../ComposedChartEntryModelProducer.kt | 39 ++++--- .../vico/core/entry/diff/DrawingModelStore.kt | 87 -------------- .../vico/core/entry/diff/ExtraStore.kt | 108 ++++++++++++++++++ .../vico/views/chart/BaseChartView.kt | 6 +- 12 files changed, 212 insertions(+), 168 deletions(-) delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelStore.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/ExtraStore.kt diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt index 3facdec89..0c0b2f859 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt @@ -34,7 +34,7 @@ import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.chart.values.toChartValuesProvider import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -66,7 +66,7 @@ public fun ChartModelProducer.collectAsState( ): State> { val chartEntryModelWrapperState = remember(chart, producerKey) { ChartEntryModelWrapperState() } val modelTransformerProvider = remember(chart) { chart.modelTransformerProvider } - val drawingModelStore = remember(chart) { MutableDrawingModelStore() } + val extraStore = remember(chart) { MutableExtraStore() } val scope = rememberCoroutineScope() val isInPreview = LocalInspectionMode.current DisposableEffect(chart, producerKey, runInitialAnimation, isInPreview) { @@ -127,7 +127,7 @@ public fun ChartModelProducer.collectAsState( startAnimation = startAnimation, getOldModel = { chartEntryModelWrapperState.value.chartEntryModel }, modelTransformerProvider = modelTransformerProvider, - drawingModelStore = drawingModelStore, + extraStore = extraStore, updateChartValues = { model -> chartValuesManager.resetChartValues() if (model != null) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt index c06aa6f0f..c41514734 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt @@ -31,8 +31,8 @@ import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.dimensions.BoundsAware import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.diff.DrawingModel -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.marker.Marker internal const val AXIS_VALUES_DEPRECATION_MESSAGE: String = "Axis values should be overridden via " + @@ -219,9 +219,9 @@ public interface Chart : BoundsAware, ChartInsetter { */ public abstract class ModelTransformer { /** - * Used for writing to and reading from the host’s [DrawingModelStore]. + * Used for writing to and reading from the host’s [ExtraStore]. */ - protected abstract val key: DrawingModelStore.Key<*> + protected abstract val key: ExtraStore.Key<*> /** * Prepares the [Chart] for a difference animation. @@ -229,14 +229,14 @@ public interface Chart : BoundsAware, ChartInsetter { public abstract fun prepareForTransformation( oldModel: Model?, newModel: Model?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, chartValuesProvider: ChartValuesProvider, ) /** * Carries out the pending difference animation. [fraction] is the animation progress. */ - public abstract suspend fun transform(drawingModelStore: MutableDrawingModelStore, fraction: Float) + public abstract suspend fun transform(extraStore: MutableExtraStore, fraction: Float) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt index b67022df0..65ab4ba2c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt @@ -39,8 +39,8 @@ import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.diff.DefaultDrawingModelInterpolator import com.patrykandpatrick.vico.core.entry.diff.DrawingModelInterpolator -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.extension.doubled import com.patrykandpatrick.vico.core.extension.getRepeating import com.patrykandpatrick.vico.core.extension.getStart @@ -115,7 +115,7 @@ public open class ColumnChart( */ protected val horizontalDimensions: MutableHorizontalDimensions = MutableHorizontalDimensions() - protected val drawingModelKey: DrawingModelStore.Key = DrawingModelStore.Key() + protected val drawingModelKey: ExtraStore.Key = ExtraStore.Key() override val entryLocationMap: HashMap> = HashMap() @@ -127,7 +127,7 @@ public open class ColumnChart( drawChartInternal( chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), model = model, - drawingModel = model.drawingModelStore.getOrNull(drawingModelKey), + drawingModel = model.extraStore.getOrNull(drawingModelKey), ) heightMap.clear() } @@ -451,7 +451,7 @@ public open class ColumnChart( } protected class ColumnChartModelTransformer( - override val key: DrawingModelStore.Key, + override val key: ExtraStore.Key, private val getTargetVerticalAxisPosition: () -> AxisPosition.Vertical?, private val getDrawingModelInterpolator: () -> DrawingModelInterpolator< ColumnChartDrawingModel.ColumnInfo, @@ -462,20 +462,20 @@ public open class ColumnChart( override fun prepareForTransformation( oldModel: ChartEntryModel?, newModel: ChartEntryModel?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, chartValuesProvider: ChartValuesProvider, ) { getDrawingModelInterpolator().setModels( - drawingModelStore.getOrNull(key), + extraStore.getOrNull(key), newModel?.toDrawingModel(chartValuesProvider.getChartValues(getTargetVerticalAxisPosition())), ) } - override suspend fun transform(drawingModelStore: MutableDrawingModelStore, fraction: Float) { + override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { getDrawingModelInterpolator() .transform(fraction) - ?.let { drawingModelStore[key] = it } - ?: drawingModelStore.remove(key) + ?.let { extraStore[key] = it } + ?: extraStore.remove(key) } private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): ColumnChartDrawingModel = entries diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt index 842bdd7b8..b4cb155a8 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt @@ -30,8 +30,8 @@ import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.entry.ChartEntryModel -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.extension.set import com.patrykandpatrick.vico.core.extension.updateAll import com.patrykandpatrick.vico.core.marker.Marker @@ -160,12 +160,12 @@ public class ComposedChart( private val getModelTransformers: () -> List>, ) : Chart.ModelTransformer() { - override val key: DrawingModelStore.Key = DrawingModelStore.Key() + override val key: ExtraStore.Key = ExtraStore.Key() override fun prepareForTransformation( oldModel: T?, newModel: T?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, chartValuesProvider: ChartValuesProvider, ) { getModelTransformers().forEachIndexed { index, transformer -> @@ -173,15 +173,15 @@ public class ComposedChart( transformer.prepareForTransformation( (oldModel as ComposedChartEntryModel<*>?)?.composedEntryCollections?.getOrNull(index) as T?, (newModel as ComposedChartEntryModel<*>).composedEntryCollections[index] as T, - drawingModelStore, + extraStore, chartValuesProvider, ) } } - override suspend fun transform(drawingModelStore: MutableDrawingModelStore, fraction: Float) { + override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { getModelTransformers().forEach { transformer -> - transformer.transform(drawingModelStore, fraction) + transformer.transform(extraStore, fraction) } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt index 2f17b9fd1..9c873ba35 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt @@ -50,8 +50,8 @@ import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.diff.DefaultDrawingModelInterpolator import com.patrykandpatrick.vico.core.entry.diff.DrawingModelInterpolator -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.extension.doubled import com.patrykandpatrick.vico.core.extension.getRepeating import com.patrykandpatrick.vico.core.extension.getStart @@ -279,7 +279,7 @@ public open class LineChart( */ protected val lineBackgroundPath: Path = Path() - protected val drawingModelKey: DrawingModelStore.Key = DrawingModelStore.Key() + protected val drawingModelKey: ExtraStore.Key = ExtraStore.Key() override val entryLocationMap: HashMap> = HashMap() @@ -289,7 +289,7 @@ public open class LineChart( ): Unit = with(context) { resetTempData() - val drawingModel = model.drawingModelStore.getOrNull(drawingModelKey) + val drawingModel = model.extraStore.getOrNull(drawingModelKey) model.entries.forEachIndexed { entryListIndex, entries -> @@ -601,7 +601,7 @@ public open class LineChart( } protected class LineChartModelTransformer( - override val key: DrawingModelStore.Key, + override val key: ExtraStore.Key, private val getTargetVerticalAxisPosition: () -> AxisPosition.Vertical?, private val getDrawingModelInterpolator: () -> DrawingModelInterpolator< LineChartDrawingModel.PointInfo, @@ -612,20 +612,20 @@ public open class LineChart( override fun prepareForTransformation( oldModel: ChartEntryModel?, newModel: ChartEntryModel?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, chartValuesProvider: ChartValuesProvider, ) { getDrawingModelInterpolator().setModels( - drawingModelStore.getOrNull(key), + extraStore.getOrNull(key), newModel?.toDrawingModel(chartValuesProvider.getChartValues(getTargetVerticalAxisPosition())), ) } - override suspend fun transform(drawingModelStore: MutableDrawingModelStore, fraction: Float) { + override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { getDrawingModelInterpolator() .transform(fraction) - ?.let { drawingModelStore[key] = it } - ?: drawingModelStore.remove(key) + ?.let { extraStore[key] = it } + ?: extraStore.remove(key) } private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): LineChartDrawingModel = entries diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt index 07636c0ff..5ace93f79 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt @@ -23,7 +23,7 @@ import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer import com.patrykandpatrick.vico.core.entry.diff.DrawingModel -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore /** * Contains the data for a [Chart]. Pre-calculates values needed for the rendering of the [Chart]. @@ -92,10 +92,10 @@ public interface ChartEntryModel { public val xGcd: Float /** - * Houses [DrawingModel]s. + * Houses auxiliary data, including [DrawingModel]s. */ - public val drawingModelStore: DrawingModelStore - get() = DrawingModelStore.empty + public val extraStore: ExtraStore + get() = ExtraStore.empty /** * Returns an immutable copy of this [ChartEntryModel]. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt index c0abdce74..9e63c2679 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt @@ -19,8 +19,8 @@ package com.patrykandpatrick.vico.core.entry import androidx.annotation.WorkerThread import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.extension.copy import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher @@ -52,6 +52,7 @@ public class ChartEntryModelProducer( private val mutex = Mutex() private val coroutineScope = CoroutineScope(dispatcher) private val updateReceivers: HashMap = HashMap() + private val extraStore = MutableExtraStore() public constructor( vararg entryCollections: List, @@ -65,11 +66,13 @@ public class ChartEntryModelProducer( /** * Requests that the data set be updated to the provided one. If the update is accepted, `true` is returned. If the * update is rejected, which occurs when there’s already an update in progress, `false` is returned. For suspending - * behavior, use [setEntriesSuspending]. + * behavior, use [setEntriesSuspending]. [updateExtras] allows for adding auxiliary data, which can later be + * retrieved via [ChartEntryModel.extraStore]. */ - public fun setEntries(entries: List>): Boolean { + public fun setEntries(entries: List>, updateExtras: (MutableExtraStore) -> Unit = {}): Boolean { if (!mutex.tryLock()) return false series = entries.copy() + updateExtras(extraStore) cachedInternalModel = null val deferredUpdates = updateReceivers.values.map { updateReceiver -> coroutineScope.async { updateReceiver.handleUpdate() } @@ -84,11 +87,16 @@ public class ChartEntryModelProducer( /** * Updates the data set. Unlike [setEntries], this function suspends the current coroutine and waits until an update * can be run, meaning the update cannot be rejected. The returned [Deferred] implementation is marked as completed - * once the update has been processed. + * once the update has been processed. [updateExtras] allows for adding auxiliary data, which can later be retrieved + * via [ChartEntryModel.extraStore]. */ - public suspend fun setEntriesSuspending(entries: List>): Deferred { + public suspend fun setEntriesSuspending( + entries: List>, + updateExtras: (MutableExtraStore) -> Unit = {}, + ): Deferred { mutex.lock() series = entries.copy() + updateExtras(extraStore) cachedInternalModel = null val completableDeferred = CompletableDeferred() val deferredUpdates = updateReceivers.values.map { updateReceiver -> @@ -105,23 +113,29 @@ public class ChartEntryModelProducer( /** * Requests that the data set be updated to the provided one. If the update is accepted, `true` is returned. If the * update is rejected, which occurs when there’s already an update in progress, `false` is returned. For suspending - * behavior, use [setEntriesSuspending]. + * behavior, use [setEntriesSuspending]. [updateExtras] allows for adding auxiliary data, which can later be + * retrieved via [ChartEntryModel.extraStore]. */ - public fun setEntries(vararg entries: List): Boolean = setEntries(entries.toList()) + public fun setEntries(vararg entries: List, updateExtras: (MutableExtraStore) -> Unit = {}): Boolean = + setEntries(entries.toList(), updateExtras) /** * Updates the data set. Unlike [setEntries], this function suspends the current coroutine and waits until an update * can be run, meaning the update cannot be rejected. The returned [Deferred] implementation is marked as completed - * once the update has been processed. + * once the update has been processed. [updateExtras] allows for adding auxiliary data, which can later be retrieved + * via [ChartEntryModel.extraStore]. */ - public suspend fun setEntriesSuspending(vararg entries: List): Deferred = - setEntriesSuspending(entries.toList()) + public suspend fun setEntriesSuspending( + vararg entries: List, + updateExtras: (MutableExtraStore) -> Unit = {}, + ): Deferred = setEntriesSuspending(entries.toList(), updateExtras) - private fun getInternalModel(drawingModelStore: DrawingModelStore = DrawingModelStore.empty) = + private fun getInternalModel(extraStore: ExtraStore? = null) = if (series.isEmpty()) { null } else { - cachedInternalModel?.copy(drawingModelStore = drawingModelStore) + val mergedExtraStore = this.extraStore.let { if (extraStore != null) it + extraStore else it } + cachedInternalModel?.copy(extraStore = mergedExtraStore) ?: run { val xRange = series.xRange val yRange = series.yRange @@ -136,7 +150,7 @@ public class ChartEntryModelProducer( stackedNegativeY = aggregateYRange.start, xGcd = series.calculateXGcd(), id = series.hashCode(), - drawingModelStore = drawingModelStore, + extraStore = mergedExtraStore, ).also { cachedInternalModel = it } } } @@ -145,8 +159,8 @@ public class ChartEntryModelProducer( override suspend fun transformModel(key: Any, fraction: Float) { with(updateReceivers[key] ?: return) { - modelTransformer?.transform(drawingModelStore, fraction) - val internalModel = getInternalModel(drawingModelStore.copy()) + modelTransformer?.transform(extraStore, fraction) + val internalModel = getInternalModel(extraStore.copy()) currentCoroutineContext().ensureActive() onModelCreated(internalModel) } @@ -159,7 +173,7 @@ public class ChartEntryModelProducer( startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, getOldModel: () -> ChartEntryModel?, modelTransformerProvider: Chart.ModelTransformerProvider?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, updateChartValues: (ChartEntryModel?) -> ChartValuesProvider, onModelCreated: (ChartEntryModel?) -> Unit, ) { @@ -167,7 +181,7 @@ public class ChartEntryModelProducer( cancelAnimation, startAnimation, onModelCreated, - drawingModelStore, + extraStore, modelTransformerProvider?.getModelTransformer(), getOldModel, updateChartValues, @@ -187,7 +201,7 @@ public class ChartEntryModelProducer( val cancelAnimation: () -> Unit, val startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, val onModelCreated: (ChartEntryModel?) -> Unit, - val drawingModelStore: MutableDrawingModelStore, + val extraStore: MutableExtraStore, val modelTransformer: Chart.ModelTransformer?, val getOldModel: () -> ChartEntryModel?, val updateChartValues: (ChartEntryModel?) -> ChartValuesProvider, @@ -197,7 +211,7 @@ public class ChartEntryModelProducer( modelTransformer?.prepareForTransformation( oldModel = getOldModel(), newModel = getModel(), - drawingModelStore = drawingModelStore, + extraStore = extraStore, chartValuesProvider = updateChartValues(getModel()), ) startAnimation(::transformModel) @@ -214,6 +228,6 @@ public class ChartEntryModelProducer( override val stackedNegativeY: Float, override val xGcd: Float, override val id: Int, - override val drawingModelStore: DrawingModelStore, + override val extraStore: ExtraStore, ) : ChartEntryModel } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt index 42c1dfc65..8f35b4e5b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt @@ -20,7 +20,7 @@ import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore /** * Generates [ChartEntryModel]s and handles difference animations. @@ -60,7 +60,7 @@ public interface ChartModelProducer { startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, getOldModel: () -> Model?, modelTransformerProvider: Chart.ModelTransformerProvider?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, updateChartValues: (Model?) -> ChartValuesProvider, onModelCreated: (Model?) -> Unit, ) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt index 0a7a80bcf..885692e65 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt @@ -25,8 +25,8 @@ import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer import com.patrykandpatrick.vico.core.entry.calculateStackedYRange import com.patrykandpatrick.vico.core.entry.calculateXGcd -import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.ExtraStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.entry.xRange import com.patrykandpatrick.vico.core.entry.yRange import com.patrykandpatrick.vico.core.extension.copy @@ -58,6 +58,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor private val mutex = Mutex() private val coroutineScope = CoroutineScope(dispatcher) private val updateReceivers = mutableMapOf() + private val extraStore = MutableExtraStore() private fun setDataSets(dataSets: List>>): Boolean { if (!mutex.tryLock()) return false @@ -89,16 +90,17 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor return completableDeferred } - private fun getInternalModel(drawingModelStore: DrawingModelStore = DrawingModelStore.empty) = + private fun getInternalModel(extraStore: ExtraStore? = null) = if (dataSets.isEmpty()) { null } else { + val mergedExtraStore = this.extraStore.let { if (extraStore != null) it + extraStore else it } cachedInternalComposedModel ?.let { composedModel -> composedModel.copy( composedEntryCollections = composedModel.composedEntryCollections - .map { model -> model.copy(drawingModelStore = drawingModelStore) }, - drawingModelStore = drawingModelStore, + .map { model -> model.copy(extraStore = mergedExtraStore) }, + extraStore = mergedExtraStore, ) } ?: run { @@ -115,7 +117,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor stackedPositiveY = aggregateYRange.endInclusive, stackedNegativeY = aggregateYRange.start, xGcd = dataSet.calculateXGcd(), - drawingModelStore = drawingModelStore, + extraStore = mergedExtraStore, ) } InternalComposedModel( @@ -131,7 +133,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor gcd?.gcdWith(model.xGcd) ?: model.xGcd } ?: 1f, id = models.map { it.id }.hashCode(), - drawingModelStore = drawingModelStore, + extraStore = mergedExtraStore, ).also { cachedInternalComposedModel = it } } } @@ -140,8 +142,8 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor override suspend fun transformModel(key: Any, fraction: Float) { with(updateReceivers[key] ?: return) { - modelTransformer?.transform(drawingModelStore, fraction) - val internalModel = getInternalModel(drawingModelStore.copy()) + modelTransformer?.transform(extraStore, fraction) + val internalModel = getInternalModel(extraStore.copy()) currentCoroutineContext().ensureActive() onModelCreated(internalModel) } @@ -154,7 +156,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, getOldModel: () -> ComposedChartEntryModel?, modelTransformerProvider: Chart.ModelTransformerProvider?, - drawingModelStore: MutableDrawingModelStore, + extraStore: MutableExtraStore, updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, onModelCreated: (ComposedChartEntryModel?) -> Unit, ) { @@ -162,7 +164,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor cancelAnimation, startAnimation, onModelCreated, - drawingModelStore, + extraStore, modelTransformerProvider?.getModelTransformer(), getOldModel, updateChartValues, @@ -258,6 +260,13 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor newDataSets.clear() } + /** + * Allows for adding auxiliary values, which can later be retrieved via [ChartEntryModel.extraStore]. + */ + public fun updateExtras(block: (MutableExtraStore) -> Unit) { + block(extraStore) + } + /** * Requests a data update. If the update is accepted, `true` is returned. If the update is rejected, which * occurs when there’s already an update in progress, `false` is returned. For suspending behavior, use @@ -277,7 +286,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor val cancelAnimation: () -> Unit, val startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, val onModelCreated: (ComposedChartEntryModel?) -> Unit, - val drawingModelStore: MutableDrawingModelStore, + val extraStore: MutableExtraStore, val modelTransformer: Chart.ModelTransformer>?, val getOldModel: () -> ComposedChartEntryModel?, val updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, @@ -287,7 +296,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor modelTransformer?.prepareForTransformation( oldModel = getOldModel(), newModel = getModel(), - drawingModelStore = drawingModelStore, + extraStore = extraStore, chartValuesProvider = updateChartValues(getModel()), ) startAnimation(::transformModel) @@ -303,7 +312,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor override val stackedPositiveY: Float, override val stackedNegativeY: Float, override val xGcd: Float, - override val drawingModelStore: DrawingModelStore, + override val extraStore: ExtraStore, ) : ChartEntryModel private data class InternalComposedModel( @@ -317,7 +326,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor override val stackedNegativeY: Float, override val xGcd: Float, override val id: Int, - override val drawingModelStore: DrawingModelStore, + override val extraStore: ExtraStore, ) : ComposedChartEntryModel public companion object { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelStore.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelStore.kt deleted file mode 100644 index 31d866484..000000000 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelStore.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.patrykandpatrick.vico.core.entry.diff - -/** - * Houses [DrawingModel]s. - */ -@Suppress("UNCHECKED_CAST") -public abstract class DrawingModelStore internal constructor() { - /** - * The underlying [Map]. - */ - protected abstract val mapDelegate: Map, DrawingModel<*>> - - /** - * Used for writing to and reading from [DrawingModelStore]s. - */ - @Suppress("UNUSED") - public open class Key> - - /** - * Returns the value associated with the provided key. - */ - public open operator fun > get(key: Key): T = mapDelegate[key] as T - - /** - * Returns the value associated with the provided key, or `null` if there’s no such value. - */ - public fun > getOrNull(key: Key): T? = mapDelegate[key] as? T - - /** - * Creates a copy of this [DrawingModelStore]. - */ - public abstract fun copy(): DrawingModelStore - - public companion object { - - /** - * An empty [DrawingModelStore]. - */ - public val empty: DrawingModelStore = MutableDrawingModelStore() - } -} - -/** - * A [DrawingModelStore] subclass that allows for data updates. - */ -public class MutableDrawingModelStore internal constructor( - mapDelegate: Map, DrawingModel<*>>, -) : DrawingModelStore() { - override val mapDelegate: MutableMap, DrawingModel<*>> = HashMap(mapDelegate) - - /** - * Creates an empty [MutableDrawingModelStore]. - */ - public constructor() : this(emptyMap()) - - /** - * Saves the provided value to this [MutableDrawingModelStore], associating the value with the given key. - */ - public operator fun > set(key: Key, value: T) { - mapDelegate[key] = value - } - - /** - * Removes the value associated with the provided key. - */ - public fun > remove(key: Key) { - mapDelegate.remove(key) - } - - override fun copy(): DrawingModelStore = MutableDrawingModelStore(mapDelegate) -} diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/ExtraStore.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/ExtraStore.kt new file mode 100644 index 000000000..f977c318a --- /dev/null +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/ExtraStore.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.patrykandpatrick.vico.core.entry.diff + +/** + * Houses auxiliary data. + */ +@Suppress("UNCHECKED_CAST") +public abstract class ExtraStore internal constructor() { + /** + * The underlying [Map]. + */ + protected abstract val mapDelegate: Map, Any> + + /** + * Used for writing to and reading from [ExtraStore]s. + */ + @Suppress("UNUSED") + public open class Key + + /** + * Returns the value associated with the provided key. + */ + public open operator fun get(key: Key): T = mapDelegate[key] as T + + /** + * Returns the value associated with the provided key, or `null` if there’s no such value. + */ + public fun getOrNull(key: Key): T? = mapDelegate[key] as? T + + /** + * Creates a copy of this [ExtraStore]. + */ + public abstract fun copy(): ExtraStore + + /** + * Combines this [ExtraStore] and [other]. + */ + public abstract operator fun plus(other: ExtraStore): ExtraStore + + /** + * Copies this [ExtraStore]’s content to [destination]. + */ + public abstract fun copyContentTo(destination: MutableMap, Any>) + + public companion object { + + /** + * An empty [ExtraStore]. + */ + public val empty: ExtraStore = MutableExtraStore() + } +} + +/** + * A [ExtraStore] subclass that allows for data updates. + */ +public class MutableExtraStore internal constructor( + mapDelegate: Map, Any>, +) : ExtraStore() { + override val mapDelegate: MutableMap, Any> = HashMap(mapDelegate) + + /** + * Creates an empty [MutableExtraStore]. + */ + public constructor() : this(emptyMap()) + + /** + * Saves the provided value to this [MutableExtraStore], associating the value with the given key. + */ + public operator fun set(key: Key, value: T) { + mapDelegate[key] = value + } + + /** + * Removes the value associated with the provided key. + */ + public fun > remove(key: Key) { + mapDelegate.remove(key) + } + + override fun copy(): ExtraStore = MutableExtraStore(mapDelegate) + + override operator fun plus(other: ExtraStore): ExtraStore = MutableExtraStore( + buildMap { + putAll(mapDelegate) + other.copyContentTo(this) + }, + ) + + override fun copyContentTo(destination: MutableMap, Any>) { + destination.putAll(mapDelegate) + } +} diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt index 26650c9a0..1da1228dc 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt @@ -54,7 +54,7 @@ import com.patrykandpatrick.vico.core.component.shape.ShapeComponent import com.patrykandpatrick.vico.core.context.MutableMeasureContext import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer -import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore +import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore import com.patrykandpatrick.vico.core.extension.set import com.patrykandpatrick.vico.core.extension.spToPx import com.patrykandpatrick.vico.core.layout.VirtualLayout @@ -151,7 +151,7 @@ public abstract class BaseChartView internal constructo interpolator = FastOutSlowInInterpolator() } - private val drawingModelStore = MutableDrawingModelStore() + private val extraStore = MutableExtraStore() private var coroutineScope: CoroutineScope? = null @@ -304,7 +304,7 @@ public abstract class BaseChartView internal constructo }, getOldModel = { model }, modelTransformerProvider = chart?.modelTransformerProvider, - drawingModelStore = drawingModelStore, + extraStore = extraStore, updateChartValues = { model -> chartValuesManager.resetChartValues() if (model != null) {