Vico

2.2. CartesianChartModelProducer

2.2.1. Overview

A CartesianChart’s data is stored in its CartesianChartModel. Much like a CartesianChart is a collection of CartesianLayers, a CartesianChartModel is a collection of CartesianLayerModels. CartesianChartModels are created via the Transaction-based CartesianChartModelProducer.

2.2.2. CartesianChartModelProducer creation

Create a CartesianChartModelProducer via the constructor. A CartesianChart’s CartesianChartModelProducer mustn’t be replaced—data updates are performed via Transactions—so store the CartesianChartModelProducer in a place with sufficient persistence, such as a ViewModel.

2.2.3. Transaction

Transactions are run via runTransaction, which is a suspending function:

cartesianChartModelProducer.runTransaction { /* ... */ }

This function returns when the update is complete—that is, once a new CartesianChartModel has been generated, and the hosts have been notified. If there’s already an update in progress, the current coroutine is first suspended until the ongoing update’s completion.

How data is added to a Transaction depends on the CartesianLayers in use; see 2.4.2, 2.5.2, and 2.6.2.

2.2.4. Asynchrony

Transactions are handled off the main thread, meaning that CartesianChartModels are generated and processed asynchronously. Moreover, during an update, two CartesianChartModels may be being dealt with at once—one in the foreground and one in the background. Thus, dynamic setup tied to CartesianChartModel updates based on Vico-provided arguments of lambdas, interface functions, and the like. These functions may receive CartesianChartModels themselves or related data. For such setup, external mechanisms should be avoided:

These solutions don’t have the tight coupling with the Transaction mechanism that is required for synchronization and may thus produce improper, unpredictable results.

For setup derived from series data, the correct, argument-based approach is straightforward, with the data being readily available in CartesianChartModel and CartesianChartRanges. Functions may receive these directly, but they’re usually accessed via CartesianMeasuringContext and subtypes thereof; see CartesianMeasuringContext.model and CartesianMeasuringContext.ranges.

However, changes that aren’t directly derived from series data may also need to be aligned with CartesianChartModel updates. We thus need a means of sending additional information through the same channel that’s used for series data. This is where extras, described in 2.2.5, come in.

2.2.5. Extras

Extras are a means of adding auxiliary data to CartesianChartModels. They’re stored in CartesianChartModel.extraStore and have typed keys (ExtraStore.Key instances), enabling you to save any kind of data in a type-safe manner. To add extras, use Transaction.extras, as shown below. (This is, of course, a simplified example. Extras are used for values that change; static values don’t require synchronization.)

val UnitKey = ExtraStore.Key<String>()
cartesianChartModelProducer.runTransaction {
    extras { extraStore ->
        extraStore[UnitKey] = "Ω"
        // ...
    }
    // ...
}

Just like series data, extras can be accessed via function arguments. In ExtraStore-focused contexts, the ExtraStore is provided explicitly. Where this isn’t the case, use CartesianChartModel.extraStore, obtaining the CartesianChartModel as described in 2.2.4. (That’s context.model.extraStore in most cases.)

Extras are read like Map elements. Assume you have an ExtraStore reference called extraStore and an ExtraStore.Key called Key. If the extra is added on every Transaction, use the following:

extraStore[Key]

If the extra is added conditionally, meaning that it may not be present, use this:

extraStore.getOrNull(Key)

ExtraStore.Keys are compared by instance. Given the asynchronous context in which they’re used, it’s important not to recreate or swap them more often than appropriate. Ensure the following:

See the following sample charts for examples of extra usage:

2.2.6. Manual CartesianChartModel creation

CartesianChartModelProducer is recommended because it offers performance benefits and supports animations. However, you can create CartesianChartModels manually via the constructor, which takes a list of CartesianLayerModels. When a host receives a CartesianChartModel, it handles it synchronously. Any asynchronous processing is explicitly handled by the consumer. Thus, extras are unneeded.

2.2.7. Sample charts