Pantrix UI

Polar Chart

A polar chart: bars set out round a middle, each as long from the middle out as its value: a rose. It suits what comes round again, the hours of a day, the months of a year, the points of a compass, and what is to be compared at a glance by how far each reaches.

import com.pantrixui.components.chart.polar.PantrixPolarChart

Examples

The hours of a day

A shop's visitors by the hour of the day, in parts of two hours. There is a hole in the middle (hole): the short bars of the night hours show too.

Move the pointer over a sector or tap it: the sector is shaded and the tooltip writes the visitors of those hours.

PantrixPolarChart(
    series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday })),
    hole = 0.25f,
    description = "Visits by the hour",
    tagKey = "plain"
)

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

Two series side by side

Weekdays and the weekend, side by side in each sector: the difference between the two at the same hours is read.

val hours = shopHours()
PantrixPolarChart(
    series = listOf(
        PolarSeries("weekday", "Weekdays", hours, x = { it.hours }, y = { it.weekday }),
        PolarSeries("weekend", "Weekend", hours, x = { it.hours }, y = { it.weekend })
    ),
    hole = 0.25f,
    description = "Visits by the hour, on weekdays and at the weekend",
    tagKey = "grouped"
)

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

Stacked

Two series that name the same stack share one bar in each sector: the whole bar shows the total, and its parts who it came from.

val hours = shopHours()
PantrixPolarChart(
    series = listOf(
        PolarSeries("store", "Shop", hours, x = { it.hours }, y = { it.weekday }, stack = "all"),
        PolarSeries("online", "Online", hours, x = { it.hours }, y = { it.online }, stack = "all")
    ),
    description = "Visits by the hour, in the shop and online",
    tagKey = "stacked"
)

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

Straight rings

Straight rings (RadarGrid.Polygon): each ring is straight from one side of a sector to the other, and the bars' ends are straight too and sit on their ring's line. The bars are filled with sixty per cent of their colour (fillAlpha): the rings behind them show.

PantrixPolarChart(
    series = listOf(
        PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday }, fillAlpha = 0.6f)
    ),
    hole = 0.25f,
    grid = RadarGrid.Polygon,
    description = "Visits by the hour",
    tagKey = "polygon"
)

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

A look of its own

A chart's own look: more room between the bars (sliceGap), no lines between the sectors (the line of xAxis), and an axis that marks four values (the ticks of yAxis). A field that is not given stays the theme's.

PantrixPolarChart(
    series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekend })),
    xAxis = ChartAxis(line = false),
    yAxis = ChartAxis(ticks = 4),
    description = "Visits by the hour at the weekend",
    style = ChartStyle(sliceGap = 6.dp),
    tagKey = "own_look"
)

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

Export

export = ChartExport(...) puts an export button over the chart: press it and choose the kind (PNG, SVG or PDF). The chart makes the file and hands it to onExport; below, its name and size show, and a PNG's picture, and "Save the file" hands it over in the platform's way.

The file has the rings, the hours and the slices.

var file by remember { mutableStateOf<ChartExportFile?>(null) }
PantrixPolarChart(
    series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday })),
    hole = 0.25f,
    description = "Visits by the hour",
    tagKey = "exported",
    export = ChartExport(fileName = "hours") { file = it }
)
ExportedFile(file, tagKey = "exported")

internal fun shopHours(): List<ShopHours> = listOf(
    ShopHours("00", weekday = 4, weekend = 9, online = 12),
    ShopHours("02", weekday = 2, weekend = 5, online = 7),
    ShopHours("04", weekday = 2, weekend = 3, online = 4),
    ShopHours("06", weekday = 8, weekend = 4, online = 6),
    ShopHours("08", weekday = 26, weekend = 12, online = 14),
    ShopHours("10", weekday = 41, weekend = 30, online = 22),
    ShopHours("12", weekday = 58, weekend = 47, online = 27),
    ShopHours("14", weekday = 46, weekend = 62, online = 25),
    ShopHours("16", weekday = 52, weekend = 70, online = 29),
    ShopHours("18", weekday = 63, weekend = 55, online = 38),
    ShopHours("20", weekday = 35, weekend = 41, online = 44),
    ShopHours("22", weekday = 14, weekend = 24, online = 30)
)

internal class ShopHours(
    val hours: String,
    val weekday: Int,
    val weekend: Int,
    val online: Int
)

API

series

A polar chart: bars set out round a middle, each as long from the middle out as its value: a rose. It suits what comes round again, the hours of a day, the months of a year, the points of a compass, and what is to be compared at a glance by how far each reaches.

ParameterTypeDefaultWhat it is
seriesList<PolarSeries<*>>the series, in the order they are drawn and listed. Their keys tell them apart, and two with one key are refused.
modifierModifierModifierthe chart's modifier. A height here is the chart's, the legend's with it.
holeFloat0fhow much of the chart's radius is cut out of its middle: nought for bars that start at the middle. No more than nine tenths.
xAxisChartAxis?ChartAxis()the sectors: whether their names are written about the chart (labels), how a name is written (label), and whether a line is drawn between one sector and the next (line). null draws nothing of them.
yAxisChartAxis?ChartAxis()the axis of the values, out from the chart's middle: its ends, about how many values it marks, how a value is written, whether the marked values are written (labels) and whether a ring is drawn at each (grid). It starts at nought unless startFromZero or min says otherwise. null draws nothing of it.
legendChartLegend?ChartLegend()where the names of the series stand. null shows none.
tooltipChartTooltip?ChartTooltip()what is shown for the sector that is pointed at. null shows none.
empty(@Composable () -> Unit)?nullwhat the chart shows while it has no value to draw, in the place of the theme's words for it ("No data"), which a screen reader is then not told either. null writes the words.
stateChartStaterememberChartState()what the reader has hidden and what they point at: the chart's own where none is given.
descriptionString?nullwhat the chart shows, in a few words, for a screen reader: the first thing it says of the chart. Left out, the chart is announced by the names of its series.
onSelect((ChartPoint) -> Unit)?nullcalled with the value that is pressed: the one pointed at, or of the series whose bar is nearest the press where the sector's values are pointed at together.
styleChartStyle?nullthis chart's own geometry: the fields set here, with the theme's for the rest.
colorsChartColors?nullthis chart's own colours, where they are not the theme's.
tagKeyString?nullthe chart's automation key: the chart is PolarChart_<tagKey>, its plot PolarChart_<tagKey>.Plot, a sector of it PolarChart_<tagKey>.Point_<number> counted from one, the legend's entry of a series PolarChart_<tagKey>.Legend_<series' key> and the tooltip PolarChart_<tagKey>.Tooltip. A press on a value is reported as a PolarChart select event with the series' key, and a press on a legend's entry as a toggle event with the key and whether the series is shown. Nothing of the values is reported.
gridRadarGridRadarGrid.Circlehow the rings are drawn: round, or straight from one side of a sector to the next, a web, whose bars end straight on its lines and are read against them. A chart of fewer than three sectors has round rings either way.
exportChartExport?nullwhat lets the reader take the chart out of the app as a PNG, an SVG or a PDF: a button over the chart makes the file of it as it is and hands it over (see ChartExport). null, the chart has no such button.

ChartStyle

A chart's geometry: how tall it is where nothing says, how thick its lines and how large its marks are, and the room between its parts; and whether its fills have textures besides their colours.

ParameterTypeDefaultWhat it is
heightDpDp.UnspecifiedHow tall a chart's plot is, with its axes, where the chart's modifier gives it no height. A legend above or under the plot adds its own.
lineWidthDpDp.UnspecifiedThe width of a series' line.
markerSizeDpDp.UnspecifiedThe width and height of the mark on a point.
selectedMarkerSizeDpDp.UnspecifiedThe same of the mark on the point that is pointed at.
gridWidthDpDp.UnspecifiedThe width of the grid's lines, of the lines along the axes and of the line that marks the place pointed at. Zero draws none.
labelGapDpDp.UnspecifiedBetween the plot and what is written beside it and under it, and between that and an axis' title.
tickSpacingDpDp.UnspecifiedThe least room between two values marked along the axis of values: what decides how many are marked where the axis does not say.
legendGapDpDp.UnspecifiedBetween the legend and the chart it stands beside.
legendItemGapDpDp.UnspecifiedBetween one entry of the legend and the next.
swatchSizeDpDp.UnspecifiedHow wide the sample of a series is in the legend and in the tooltip: a short run of its line, or a patch of its fill.
swatchGapDpDp.UnspecifiedBetween that sample and the name after it.
tooltipShapeShapeSpecShapeSpec.UnspecifiedThe tooltip's corners: a step of the brand's shape scale, or a fixed shape.
tooltipHorizontalPaddingDpDp.UnspecifiedBetween the tooltip's sides and what it holds.
tooltipVerticalPaddingDpDp.UnspecifiedBetween its top and bottom and what it holds, and between its heading and rows.
tooltipGapDpDp.UnspecifiedBetween the place pointed at and the tooltip beside it.
tooltipElevationDpDp.UnspecifiedThe tooltip's shadow.
barGapDpDp.UnspecifiedBetween two bars that stand side by side at a place. Bars too thin for it stand closer.
barMaxWidthDpDp.UnspecifiedThe widest a bar is, however much room its place has: a chart of three places is not three slabs.
barRadiusDpDp.UnspecifiedHow round the corners of a bar's end are, the end away from nought.
sliceGapDpDp.UnspecifiedBetween two slices of a pie that lie side by side, the same from the pie's middle to its rim.
selectedSliceOffsetDpDp.UnspecifiedHow far the slice of a pie that is pointed at stands out past the pie's rim. The pie keeps this much room about itself, so that the slice has where to stand.
gaugeWidthDpDp.UnspecifiedHow thick the arc of a gauge is. An arc is no thicker than half its radius.
meterHeightDpDp.UnspecifiedHow tall the bar of a meter group is.
trackerHeightDpDp.UnspecifiedHow tall the blocks of a status tracker are.
bubbleMinSizeDpDp.UnspecifiedHow far across the smallest bubble of a scatter chart is: a point whose size is nothing, or too little to be seen as an area, is still a point.
bubbleMaxSizeDpDp.UnspecifiedHow far across its largest bubble is: the one whose size is the most of the chart's.
navigatorHeightDpDp.UnspecifiedHow tall the strip under a chart's plot is, on which the part of the chart that is in view is marked and moved, in a chart whose zoom has one: how wide, beside a plot whose places go down its side.
barBandShareFloatFloat.NaNHow much of a place's band its bars take, more than nought and one at the most: the rest is the room between one place's bars and the next's. Bars are no wider than barMaxWidth all the same.
markLineWidthDpDp.UnspecifiedThe width of a mark's line: a target, a limit, a day. Zero draws none, and the mark's words are written all the same.
markBandAlphaFloatFloat.NaNHow much of its colour a mark's band is drawn in, from nought to one: a band lies behind the series, and is a tint.
patternsBoolean?nullWhether the fills of a chart's series, slices and tiles are drawn with textures besides their colours, and its lines broken in turn (ChartPattern): what tells them apart for a reader who cannot tell the colours apart. Left out, as the system is set: on where it is set to tell things apart without colour, iOS's "Differentiate Without Color" and Android's colour correction, and off otherwise.
nodeWidthDpDp.UnspecifiedHow wide a node of a sankey is.
nodeGapDpDp.UnspecifiedBetween two nodes of a column of a sankey.

ChartColors

What a chart is painted with, for a chart that does not take the theme's colours: a colour that is set is used, and one that is left out stays the theme's.

ParameterTypeDefaultWhat it is
seriesList<Color>emptyList()The colours the series are drawn in, in the order they are handed out, for the series that name none of their own. Empty, they are the theme's chart colours.
gridColorColor.UnspecifiedThe lines drawn across the chart at the marks of an axis. The theme's divider.
axisColorColor.UnspecifiedThe line along an axis. The theme's border.
labelColorColor.UnspecifiedWhat is written beside the axes: the values, the places and the axes' titles, and the names in the legend. The theme's textSecondary.
crosshairColorColor.UnspecifiedThe line that marks the place pointed at. The theme's borderHover: stronger than the grid it crosses, and quieter than the series it stands behind.
tooltipContainerColorColor.UnspecifiedThe tooltip's fill. The theme's surface.
tooltipBorderColorColor.UnspecifiedThe tooltip's edge. The theme's border.
tooltipTextColorColor.UnspecifiedThe tooltip's heading and its values. The theme's textPrimary.
tooltipLabelColorColor.UnspecifiedThe names of the series in the tooltip. The theme's textSecondary.
risingColorColor.UnspecifiedWhat a value that goes up is drawn in: a step of a waterfall that adds. The theme's success.
fallingColorColor.UnspecifiedWhat a value that goes down is drawn in: a step of a waterfall that takes away. The theme's danger.

Accessibility