Skip to content

Commit

Permalink
Rework core APIs
Browse files Browse the repository at this point in the history
Co-authored-by: Patryk Goworowski <[email protected]>
  • Loading branch information
patrickmichalik and Gowsky committed Nov 26, 2023
1 parent c3ee8aa commit 4e74816
Show file tree
Hide file tree
Showing 78 changed files with 1,671 additions and 2,598 deletions.
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ dependencyResolutionManagement {

rootProject.name = "Vico"

include("sample", "vico", "vico:compose", "vico:compose-m2", "vico:compose-m3", "vico:core", "vico:views")
include("vico", "vico:compose", "vico:compose-m2", "vico:compose-m3", "vico:core")
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@ import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.patrykandpatrick.vico.compose.chart.column.columnChart
import com.patrykandpatrick.vico.compose.chart.entry.collectAsState
import com.patrykandpatrick.vico.compose.chart.entry.defaultDiffAnimationSpec
import com.patrykandpatrick.vico.compose.chart.layout.segmented
import com.patrykandpatrick.vico.compose.chart.line.lineChart
import com.patrykandpatrick.vico.compose.chart.scroll.ChartScrollSpec
import com.patrykandpatrick.vico.compose.chart.scroll.ChartScrollState
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec
Expand All @@ -61,7 +59,8 @@ import com.patrykandpatrick.vico.core.DefaultDimens
import com.patrykandpatrick.vico.core.axis.AxisManager
import com.patrykandpatrick.vico.core.axis.AxisPosition
import com.patrykandpatrick.vico.core.axis.AxisRenderer
import com.patrykandpatrick.vico.core.chart.Chart
import com.patrykandpatrick.vico.core.chart.composed.CartesianChart
import com.patrykandpatrick.vico.core.chart.composed.CartesianChartModel
import com.patrykandpatrick.vico.core.chart.dimensions.MutableHorizontalDimensions
import com.patrykandpatrick.vico.core.chart.draw.chartDrawContext
import com.patrykandpatrick.vico.core.chart.draw.drawMarker
Expand All @@ -70,11 +69,10 @@ import com.patrykandpatrick.vico.core.chart.draw.getMaxScrollDistance
import com.patrykandpatrick.vico.core.chart.edges.FadingEdges
import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout
import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp
import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager
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.chart.values.ChartValues
import com.patrykandpatrick.vico.core.chart.values.MutableChartValues
import com.patrykandpatrick.vico.core.chart.values.toImmutable
import com.patrykandpatrick.vico.core.entry.composed.CartesianChartModelProducer
import com.patrykandpatrick.vico.core.extension.set
import com.patrykandpatrick.vico.core.extension.spToPx
import com.patrykandpatrick.vico.core.layout.VirtualLayout
Expand All @@ -89,11 +87,10 @@ import com.patrykandpatrick.vico.core.util.setValue
import kotlinx.coroutines.launch

/**
* Displays a chart.
* Displays a [CartesianChart].
*
* @param chart the chart itself (excluding axes, markers, etc.). You can use [lineChart] or [columnChart], or provide a
* custom [Chart] implementation.
* @param chartModelProducer creates and updates the [ChartEntryModel] for the chart.
* @param chart the [CartesianChart].
* @param chartModelProducer creates and updates the [CartesianChartModel].
* @param modifier the modifier to be applied to the chart.
* @param startAxis the axis displayed at the start of the chart.
* @param topAxis the axis displayed at the top of the chart.
Expand All @@ -113,13 +110,13 @@ import kotlinx.coroutines.launch
* @param chartScrollState houses information on the chart’s scroll state. Allows for programmatic scrolling.
* @param horizontalLayout defines how the chart’s content is positioned horizontally.
* @param getXStep overrides the _x_ step (the difference between the _x_ values of neighboring major entries). If this
* is null, the default _x_ step ([ChartEntryModel.xGcd]) is used.
* @param placeholder shown when no [ChartEntryModel] is available.
* is null, the default _x_ step ([CartesianChartModel.xDeltaGcd]) is used.
* @param placeholder shown when no [CartesianChartModel] is available.
*/
@Composable
public fun <Model : ChartEntryModel> Chart(
chart: Chart<Model>,
chartModelProducer: ChartModelProducer<Model>,
public fun CartesianChartHost(
chart: CartesianChart,
chartModelProducer: CartesianChartModelProducer,
modifier: Modifier = Modifier,
startAxis: AxisRenderer<AxisPosition.Vertical.Start>? = null,
topAxis: AxisRenderer<AxisPosition.Horizontal.Top>? = null,
Expand All @@ -128,28 +125,28 @@ public fun <Model : ChartEntryModel> Chart(
marker: Marker? = null,
markerVisibilityChangeListener: MarkerVisibilityChangeListener? = null,
legend: Legend? = null,
chartScrollSpec: ChartScrollSpec<Model> = rememberChartScrollSpec(),
chartScrollSpec: ChartScrollSpec = rememberChartScrollSpec(),
isZoomEnabled: Boolean = true,
diffAnimationSpec: AnimationSpec<Float>? = defaultDiffAnimationSpec,
runInitialAnimation: Boolean = true,
fadingEdges: FadingEdges? = null,
autoScaleUp: AutoScaleUp = AutoScaleUp.Full,
chartScrollState: ChartScrollState = rememberChartScrollState(),
horizontalLayout: HorizontalLayout = HorizontalLayout.segmented(),
getXStep: ((Model) -> Float)? = null,
getXStep: ((CartesianChartModel) -> Float)? = null,
placeholder: @Composable BoxScope.() -> Unit = {},
) {
val chartValuesManager = remember(chart) { ChartValuesManager() }
val mutableChartValues = remember(chart) { MutableChartValues() }
val chartEntryModelWrapper by chartModelProducer
.collectAsState(chart, chartModelProducer, diffAnimationSpec, runInitialAnimation, chartValuesManager, getXStep)
val (chartEntryModel, previousChartEntryModel, chartValuesProvider) = chartEntryModelWrapper
.collectAsState(chart, chartModelProducer, diffAnimationSpec, runInitialAnimation, mutableChartValues, getXStep)
val (model, previousModel, chartValues) = chartEntryModelWrapper

ChartBox(modifier = modifier) {
if (chartEntryModel != null) {
ChartImpl(
CartesianChartHostBox(modifier = modifier) {
if (model != null) {
CartesianChartHostImpl(
chart = chart,
model = chartEntryModel,
oldModel = previousChartEntryModel,
model = model,
oldModel = previousModel,
startAxis = startAxis,
topAxis = topAxis,
endAxis = endAxis,
Expand All @@ -163,7 +160,7 @@ public fun <Model : ChartEntryModel> Chart(
autoScaleUp = autoScaleUp,
chartScrollState = chartScrollState,
horizontalLayout = horizontalLayout,
chartValuesProvider = chartValuesProvider,
chartValues = chartValues,
)
} else {
placeholder()
Expand All @@ -172,14 +169,11 @@ public fun <Model : ChartEntryModel> Chart(
}

/**
* Displays a chart.
* Displays a [CartesianChart]. This function accepts a [CartesianChartModel]. For dynamic data, use the function
* overload that accepts a [CartesianChartModelProducer] instance.
*
* This function accepts a [ChartEntryModel]. For dynamic data, use the function overload that accepts a
* [ChartModelProducer] instance.
*
* @param chart the chart itself (excluding axes, markers, etc.). You can use [lineChart] or [columnChart], or provide a
* custom [Chart] implementation.
* @param model the [ChartEntryModel] for the chart.
* @param chart the [CartesianChart].
* @param model the [CartesianChartModel].
* @param modifier the modifier to be applied to the chart.
* @param startAxis the axis displayed at the start of the chart.
* @param topAxis the axis displayed at the top of the chart.
Expand All @@ -190,21 +184,21 @@ public fun <Model : ChartEntryModel> Chart(
* @param legend an optional legend for the chart.
* @param chartScrollSpec houses scrolling-related settings.
* @param isZoomEnabled whether zooming in and out is enabled.
* @param oldModel the chart’s previous [ChartEntryModel]. This is used to determine whether to perform an automatic
* @param oldModel the chart’s previous [CartesianChartModel]. This is used to determine whether to perform an automatic
* scroll.
* @param fadingEdges applies a horizontal fade to the edges of the chart area for scrollable charts.
* @param autoScaleUp defines whether the content of the chart should be scaled up when the dimensions are such that, at
* a scale factor of 1, an empty space would be visible near the end edge of the chart.
* @param chartScrollState houses information on the chart’s scroll state. Allows for programmatic scrolling.
* @param horizontalLayout defines how the chart’s content is positioned horizontally.
* @param getXStep overrides the _x_ step (the difference between the _x_ values of neighboring major entries). If this
* is null, the default _x_ step ([ChartEntryModel.xGcd]) is used.
* is null, the default _x_ step ([CartesianChartModel.xDeltaGcd]) is used.
*/
@Composable
@SuppressLint("RememberReturnType")
public fun <Model : ChartEntryModel> Chart(
chart: Chart<Model>,
model: Model,
public fun CartesianChartHost(
chart: CartesianChart,
model: CartesianChartModel,
modifier: Modifier = Modifier,
startAxis: AxisRenderer<AxisPosition.Vertical.Start>? = null,
topAxis: AxisRenderer<AxisPosition.Horizontal.Top>? = null,
Expand All @@ -213,22 +207,22 @@ public fun <Model : ChartEntryModel> Chart(
marker: Marker? = null,
markerVisibilityChangeListener: MarkerVisibilityChangeListener? = null,
legend: Legend? = null,
chartScrollSpec: ChartScrollSpec<Model> = rememberChartScrollSpec(),
chartScrollSpec: ChartScrollSpec = rememberChartScrollSpec(),
isZoomEnabled: Boolean = true,
oldModel: Model? = null,
oldModel: CartesianChartModel? = null,
fadingEdges: FadingEdges? = null,
autoScaleUp: AutoScaleUp = AutoScaleUp.Full,
chartScrollState: ChartScrollState = rememberChartScrollState(),
horizontalLayout: HorizontalLayout = HorizontalLayout.segmented(),
getXStep: ((Model) -> Float)? = null,
getXStep: ((CartesianChartModel) -> Float)? = null,
) {
val chartValuesManager = remember(chart) { ChartValuesManager() }
remember(chartValuesManager, model, getXStep) {
chartValuesManager.resetChartValues()
chart.updateChartValues(chartValuesManager, model, getXStep?.invoke(model))
val chartValues = remember(chart) { MutableChartValues() }
remember(chartValues, model, getXStep) {
chartValues.reset()
chart.updateChartValues(chartValues, model, getXStep?.invoke(model))
}
ChartBox(modifier = modifier) {
ChartImpl(
CartesianChartHostBox(modifier = modifier) {
CartesianChartHostImpl(
chart = chart,
model = model,
startAxis = startAxis,
Expand All @@ -245,31 +239,31 @@ public fun <Model : ChartEntryModel> Chart(
autoScaleUp = autoScaleUp,
chartScrollState = chartScrollState,
horizontalLayout = horizontalLayout,
chartValuesProvider = chartValuesManager.toChartValuesProvider(),
chartValues = chartValues.toImmutable(),
)
}
}

@Suppress("LongMethod")
@Composable
internal fun <Model : ChartEntryModel> ChartImpl(
chart: Chart<Model>,
model: Model,
internal fun CartesianChartHostImpl(
chart: CartesianChart,
model: CartesianChartModel,
startAxis: AxisRenderer<AxisPosition.Vertical.Start>?,
topAxis: AxisRenderer<AxisPosition.Horizontal.Top>?,
endAxis: AxisRenderer<AxisPosition.Vertical.End>?,
bottomAxis: AxisRenderer<AxisPosition.Horizontal.Bottom>?,
marker: Marker?,
markerVisibilityChangeListener: MarkerVisibilityChangeListener?,
legend: Legend?,
chartScrollSpec: ChartScrollSpec<Model>,
chartScrollSpec: ChartScrollSpec,
isZoomEnabled: Boolean,
oldModel: Model? = null,
oldModel: CartesianChartModel? = null,
fadingEdges: FadingEdges?,
autoScaleUp: AutoScaleUp,
chartScrollState: ChartScrollState = rememberChartScrollState(),
horizontalLayout: HorizontalLayout,
chartValuesProvider: ChartValuesProvider,
chartValues: ChartValues,
) {
val axisManager = remember { AxisManager() }
val bounds = remember { RectF() }
Expand All @@ -281,7 +275,7 @@ internal fun <Model : ChartEntryModel> ChartImpl(
bounds,
horizontalLayout,
with(LocalContext.current) { ::spToPx },
chartValuesProvider,
chartValues,
)
val scrollListener = rememberScrollListener(markerTouchPoint)
val lastMarkerEntryModels = remember { mutableStateOf(emptyList<Marker.EntryModel>()) }
Expand Down Expand Up @@ -377,15 +371,14 @@ internal fun <Model : ChartEntryModel> ChartImpl(
val count = if (fadingEdges != null) chartDrawContext.saveLayer() else -1

axisManager.drawBehindChart(chartDrawContext)
chart.drawScrollableContent(chartDrawContext, model)
chart.draw(chartDrawContext, model)

fadingEdges?.apply {
applyFadingEdges(chartDrawContext, chart.bounds)
chartDrawContext.restoreCanvasToCount(count)
}

axisManager.drawAboveChart(chartDrawContext)
chart.drawNonScrollableContent(chartDrawContext, model)
legend?.draw(chartDrawContext)

if (marker != null) {
Expand All @@ -406,7 +399,7 @@ internal fun <Model : ChartEntryModel> ChartImpl(
}

@Composable
internal fun ChartBox(
internal fun CartesianChartHostBox(
modifier: Modifier,
content: @Composable BoxScope.() -> Unit,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 by Patryk Goworowski and Patrick Michalik.
* 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.
Expand All @@ -14,18 +14,14 @@
* limitations under the License.
*/

package com.patrykandpatrick.vico.core.entry
package com.patrykandpatrick.vico.compose.chart.cartesian

import androidx.compose.runtime.Composable
import com.patrykandpatrick.vico.core.chart.CartesianLayer
import com.patrykandpatrick.vico.core.chart.composed.CartesianChart

/**
* The default implementation of [ChartEntry].
* Creates and remembers a [CartesianChart].
*/
public data class FloatEntry(
override val x: Float,
override val y: Float,
) : ChartEntry {

override fun withY(y: Float): ChartEntry = FloatEntry(
x = x,
y = y,
)
}
@Composable
public fun rememberCartesianChart(vararg layers: CartesianLayer<*>): CartesianChart = CartesianChart(*layers)
Original file line number Diff line number Diff line change
Expand Up @@ -19,66 +19,57 @@ package com.patrykandpatrick.vico.compose.chart.column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.Dp
import com.patrykandpatrick.vico.compose.chart.Chart
import com.patrykandpatrick.vico.compose.style.currentChartStyle
import com.patrykandpatrick.vico.core.axis.AxisPosition
import com.patrykandpatrick.vico.core.axis.AxisRenderer
import com.patrykandpatrick.vico.core.chart.column.ColumnChart
import com.patrykandpatrick.vico.core.chart.column.ColumnChart.MergeMode
import com.patrykandpatrick.vico.core.chart.column.ColumnChartDrawingModel
import com.patrykandpatrick.vico.core.chart.composed.ComposedChart
import com.patrykandpatrick.vico.core.chart.decoration.Decoration
import com.patrykandpatrick.vico.core.chart.column.ColumnCartesianLayer
import com.patrykandpatrick.vico.core.chart.column.ColumnCartesianLayer.MergeMode
import com.patrykandpatrick.vico.core.chart.column.ColumnCartesianLayerDrawingModel
import com.patrykandpatrick.vico.core.chart.column.ColumnCartesianLayerModel
import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider
import com.patrykandpatrick.vico.core.chart.values.ChartValues
import com.patrykandpatrick.vico.core.component.shape.LineComponent
import com.patrykandpatrick.vico.core.component.text.TextComponent
import com.patrykandpatrick.vico.core.component.text.VerticalPosition
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.formatter.ValueFormatter
import com.patrykandpatrick.vico.core.marker.Marker

/**
* Creates a [ColumnChart].
* Creates a [ColumnCartesianLayer].
*
* @param columns the [LineComponent] instances to use for columns. This list is iterated through as many times
* as necessary for each column collection. If the list contains a single element, all columns have the same appearance.
* @param spacing the distance between neighboring column collections.
* @param innerSpacing the distance between neighboring grouped columns.
* @param mergeMode defines how columns should be drawn in column collections.
* @param decorations the list of [Decoration]s that will be added to the [ColumnChart].
* @param persistentMarkers maps x-axis values to persistent [Marker]s.
* @param dataLabel an optional [TextComponent] to use for data labels.
* @param dataLabelVerticalPosition the vertical position of data labels relative to the top of their
* respective columns.
* @param dataLabelValueFormatter the [ValueFormatter] to use for data labels.
* @param dataLabelRotationDegrees the rotation of data labels (in degrees).
* @param axisValuesOverrider overrides the minimum and maximum x-axis and y-axis values.
* @param targetVerticalAxisPosition if this is set, any [AxisRenderer] with an [AxisPosition] equal to the provided
* value will use the [ChartValues] provided by this chart. This is meant to be used with [ComposedChart].
* @param drawingModelInterpolator interpolates the [ColumnChart]’s [ColumnChartDrawingModel]s.
*
* @see Chart
* @see ColumnChart
* value will use the [ChartValues] provided by this chart.
* @param drawingModelInterpolator interpolates the [ColumnCartesianLayer]’s [ColumnCartesianLayerDrawingModel]s.
*/
@Composable
public fun columnChart(
public fun rememberColumnCartesianLayer(
columns: List<LineComponent> = currentChartStyle.columnChart.columns,
spacing: Dp = currentChartStyle.columnChart.outsideSpacing,
innerSpacing: Dp = currentChartStyle.columnChart.innerSpacing,
mergeMode: MergeMode = currentChartStyle.columnChart.mergeMode,
decorations: List<Decoration>? = null,
persistentMarkers: Map<Float, Marker>? = null,
targetVerticalAxisPosition: AxisPosition.Vertical? = null,
dataLabel: TextComponent? = currentChartStyle.columnChart.dataLabel,
dataLabelVerticalPosition: VerticalPosition = currentChartStyle.columnChart.dataLabelVerticalPosition,
dataLabelValueFormatter: ValueFormatter = currentChartStyle.columnChart.dataLabelValueFormatter,
dataLabelRotationDegrees: Float = currentChartStyle.columnChart.dataLabelRotationDegrees,
axisValuesOverrider: AxisValuesOverrider<ChartEntryModel>? = null,
drawingModelInterpolator: DrawingModelInterpolator<ColumnChartDrawingModel.ColumnInfo, ColumnChartDrawingModel> =
remember { DefaultDrawingModelInterpolator() },
): ColumnChart = remember { ColumnChart() }.apply {
axisValuesOverrider: AxisValuesOverrider<ColumnCartesianLayerModel>? = null,
drawingModelInterpolator: DrawingModelInterpolator<
ColumnCartesianLayerDrawingModel.ColumnInfo,
ColumnCartesianLayerDrawingModel,
> = remember { DefaultDrawingModelInterpolator() },
): ColumnCartesianLayer = remember { ColumnCartesianLayer() }.apply {
this.columns = columns
this.spacingDp = spacing.value
this.innerSpacingDp = innerSpacing.value
Expand All @@ -90,6 +81,4 @@ public fun columnChart(
this.axisValuesOverrider = axisValuesOverrider
this.targetVerticalAxisPosition = targetVerticalAxisPosition
this.drawingModelInterpolator = drawingModelInterpolator
decorations?.also(::setDecorations)
persistentMarkers?.also(::setPersistentMarkers)
}
Loading

0 comments on commit 4e74816

Please sign in to comment.