Class Chart

java.lang.Object
limn.scene.Widget
limn.components.chart.Chart
Direct Known Subclasses:
CartesianChart, DonutChart

public abstract class Chart extends Widget
What every chart in this package has in common: the data (labels and series), the colors, the title, the legend, the hover tooltip, the click callback and the animation. BarChart, LineChart and DonutChart add the marks and the axes.

A chart is usable with nothing configured: colors come from the palette that matches the active Theme, the scale rounds itself, the legend appears when there is more than one thing to name, and values animate in. Everything past that is a setter.


 BarChart chart = new BarChart();
 chart.setLabels("Mon", "Tue", "Wed", "Thu", "Fri");
 chart.addSeries(ChartSeries.of("Signups", 12, 19, 3, 5, 22));
 chart.addSeries(ChartSeries.of("Churn",    2,  3, 1, 4,  2));
 chart.setStacked(true);
 chart.onPointClick(point -> open(point.label()));
 

Animation. Values interpolate, not pixels: on the first appearance a series grows out of the axis baseline, and afterwards it eases from the number on screen to the number it was just given, so a live chart re-pushed every second reads as one moving shape rather than as a slideshow. setAnimationDuration(double) with 0 turns it off, which is also what a chart with no scene does: a headless test or a screenshot sees final values, never an in-between frame.

Interaction. Hovering reports the datum under the pointer; the tooltip follows it and stays inside the chart. A chart with more than one series reports every series at the hovered category at once (Chart.TooltipMode.INDEX), which is what makes two lines comparable at a glance. Clicking calls onPointClick(Consumer) with the same datum. Clicking a legend entry hides and shows its series, animating the scale and any stack around it.

Charts are painted, not composed: a chart has no child widgets, with one exception: DonutChart.setCenter(Widget) puts a real widget in the hole, so the middle of a donut can hold a label, an icon or a button.

On chaining. The setters here return Chart, so a chain that starts on a subclass cannot continue into one of that subclass's own setters: new BarChart().setTitle("x").setStacked(true) does not compile. Configure a chart in statements (which is how the demo scenes read anyway) or start from BarChart.of(java.util.List<java.lang.String>, limn.components.chart.ChartSeries...), LineChart.of(java.util.List<java.lang.String>, limn.components.chart.ChartSeries...) and DonutChart.of(java.util.List<java.lang.String>, double...), which take the labels and the series together. The alternative, redeclaring every setter in all three subclasses purely to narrow its return type, buys one expression shape at the price of forty methods whose documentation could only repeat their signatures.

  • Field Details

    • UNSET

      public static final float UNSET
      Hands a dimension back to the default size, the ProgressBar.UNSET idiom.
      See Also:
  • Constructor Details

    • Chart

      public Chart()
  • Method Details

    • labels

      public List<String> labels()
      The category labels as they currently read, in order.
    • label

      public String label(int index)
      The label for index, or "" when there are fewer labels than values.
    • labelSource

      public I18nString labelSource(int index)
      The localizable value behind label(int), or I18nString.EMPTY past the end: what a language change re-resolves.
    • setLabels

      public Chart setLabels(String... values)
      Sets the category labels: the x axis of a bar or line chart, the slices of a donut.
    • setLabels

      public Chart setLabels(I18nString... values)
      Sets category labels that follow the UI language. There is no List form of it (List<String> and List<I18nString> erase to the same signature), so hand a list over as list.toArray(new I18nString[0]).
    • setLabels

      public Chart setLabels(List<String> values)
      setLabels(String...) from a list; the list is copied.
    • series

      public List<ChartSeries> series()
      The series, in the order they were added (paint and palette order).
    • addSeries

      public Chart addSeries(ChartSeries newSeries)
      Adds a series. It takes the next palette slot unless it names its own color.
      Throws:
      IllegalStateException - if the series already belongs to a chart
    • onSeriesAdded

      protected void onSeriesAdded(ChartSeries added)
      Called with a series that has just joined the chart, before anything is animated. Charts with per-series defaults of their own apply them here, so a series added after the default was set is styled like the ones that were already there.
    • setSeries

      public Chart setSeries(ChartSeries... newSeries)
      Replaces every series at once.
    • removeSeries

      public Chart removeSeries(ChartSeries victim)
      Removes a series; a no-op when it is not in this chart.
    • clearSeries

      public Chart clearSeries()
      Removes every series.
    • series

      protected final ChartSeries series(int index)
      The series at index, without copying the list, the accessor the painting and hit-testing paths use, since series() allocates and both run per frame.
    • seriesCount

      protected final int seriesCount()
      How many series the chart holds, visible or not.
    • dataGeneration

      protected final int dataGeneration()
      Bumped by every change to the data or to what is visible in it. Derived structures that are expensive to rebuild (a stack-key list walked once per bar) cache against it instead of rebuilding per frame.
    • contentLeft

      protected final float contentLeft()
      Left edge of the region left after the title and the legend.
    • contentTop

      protected final float contentTop()
      Top edge of the region left after the title and the legend.
    • contentBoxWidth

      protected final float contentBoxWidth()
      Width of the region left after the title and the legend.
    • contentBoxHeight

      protected final float contentBoxHeight()
      Height of the region left after the title and the legend.
    • categoryCount

      public final int categoryCount()
      How many categories the chart spans: the labels, or the longest series.
    • palette

      public ChartPalette palette()
      The palette in force: the one that was set, or the built-in one for this theme.
    • setPalette

      public Chart setPalette(ChartPalette value)
      Pins the palette; null hands it back to ChartPalette.defaultFor(limn.components.Theme), which follows the active theme's light/dark mode.
    • seriesColor

      public final Color seriesColor(int index)
      The color of series index: its own, or its palette slot.
    • title

      public String title()
      The chart title as it currently reads, or null when there is none.
    • titleSource

      public I18nString titleSource()
      The localizable value behind title(), or null.
    • setTitle

      public Chart setTitle(String value)
      Sets a title drawn above the plot (null for none).
    • setTitle

      public Chart setTitle(I18nString value)
      Sets a title that follows the UI language (null for none).
    • background

      public Color background()
      The panel color behind the chart, or null when it paints on what is below.
    • setBackground

      public Chart setBackground(Color color)
      Fills the chart's box with color before anything else, rounded like a card; null (the default) paints nothing, letting the chart sit on the surface it was placed on.
    • legendPosition

      public Chart.LegendPosition legendPosition()
      Where the legend sits.
    • setLegendPosition

      public Chart setLegendPosition(Chart.LegendPosition position)
      Moves the legend, or takes it away with Chart.LegendPosition.NONE.
    • isLegendInteractive

      public boolean isLegendInteractive()
      Whether clicking a legend entry hides and shows its data.
    • setLegendInteractive

      public Chart setLegendInteractive(boolean value)
      Enables or disables hide/show on legend clicks (on by default).
    • setPreferredSize

      public Chart setPreferredSize(float width, float height)
      Overrides the natural size (360.0f x 220.0f); UNSET on either axis restores it. A chart in an Expanded or a stretched Column takes the space it is given regardless; this is the fallback for an unconstrained parent.
    • valueFormat

      public DoubleFunction<String> valueFormat()
      How values are written in tooltips (and, unless overridden, on the value axis).
    • setValueFormat

      public Chart setValueFormat(DoubleFunction<String> format)
      Sets the tooltip value format; see ChartFormats for ready-made ones.
    • isTooltipEnabled

      public boolean isTooltipEnabled()
      Whether hovering shows a tooltip.
    • setTooltipEnabled

      public Chart setTooltipEnabled(boolean value)
      Turns the hover tooltip on or off (on by default).
    • tooltipMode

      public Chart.TooltipMode tooltipMode()
      How much of the data a hover reports.
    • setTooltipMode

      public Chart setTooltipMode(Chart.TooltipMode mode)
      Sets whether a hover reports the whole category or only the mark under the pointer.
    • setTooltipFormat

      public Chart setTooltipFormat(Function<ChartPoint,String> format)
      Replaces the text of a tooltip row. The default lays each row out in two columns (the series name where reading starts, the formatted value where it ends, so both columns swap in a chart that reads right to left), which is what makes a multi-series tooltip scannable; a formatter set here produces the whole row as one string instead. null restores the default.
    • animationDuration

      public double animationDuration()
      Length of the entry and value-change animation, in seconds.
    • setAnimationDuration

      public Chart setAnimationDuration(double seconds)
      Sets the animation length in seconds; 0 draws every change immediately.
    • setAnimationEasing

      public Chart setAnimationEasing(Easing easing)
      Sets the animation curve (default Easing.EASE_OUT).
    • replayAnimation

      public Chart replayAnimation()
      Replays the entry animation from the axis baseline.
    • onPointClick

      public Chart onPointClick(Consumer<ChartPoint> listener)
      Called with the datum under the pointer on a left click. In Chart.TooltipMode.INDEX a click anywhere in a category reports that category's nearest mark, so a thin line is as clickable as a fat bar.
    • onPointHover

      public Chart onPointHover(Consumer<ChartPoint> listener)
      Called whenever the hovered datum changes, with null when the pointer leaves the marks. Fires on changes only, not on every pointer move.
    • hoveredPoint

      public ChartPoint hoveredPoint()
      The datum under the pointer, or null.
    • withControlSize

      public Chart withControlSize(ControlSize size)
      Chaining form of Widget.setControlSize(limn.scene.ControlSize); setControlSize is void.
    • paintContent

      protected abstract void paintContent(Canvas canvas, float x, float y, float w, float h)
      Paints the marks inside the region left after the title and the legend, in the chart's own coordinates. Grid, axes and everything data-shaped belong here.
    • pickAt

      protected abstract ChartPoint pickAt(float localX, float localY)
      The datum under a point in the chart's local coordinates, or null when the pointer is not over the marks. Called for hover and for clicks, so it decides both.
    • tooltipRows

      protected List<ChartPoint> tooltipRows(ChartPoint picked)
      The rows a tooltip shows for picked. The default is Chart.TooltipMode.INDEX-aware: every visible series at the same index, or just the picked datum in Chart.TooltipMode.POINT.
    • tooltipTitle

      protected String tooltipTitle(ChartPoint picked)
      The heading over the tooltip rows; the category label by default.
    • tooltipRowName

      protected String tooltipRowName(ChartPoint row)
      The name a tooltip row carries where reading starts, the series name by default.
    • tooltipRowValue

      protected String tooltipRowValue(ChartPoint row)
      The value a tooltip row carries where reading ends.
    • tooltipRowColor

      protected Color tooltipRowColor(ChartPoint row)
      The swatch color for a tooltip row.
    • legendEntries

      protected List<Chart.LegendEntry> legendEntries()
      One entry per series, in palette order. Overridden by charts whose legend is not that.
    • toggleLegendEntry

      protected void toggleLegendEntry(int index)
      Hides or shows what legend entry index stands for.
    • legendChanged

      protected final void legendChanged()
      Drops the legend's cached entries, for a chart whose legend is not the series list.
    • onHoverChanged

      protected void onHoverChanged(ChartPoint picked)
      Called when the datum under the pointer changes, null when there is none: the hook for a chart that animates something on hover. The application-facing callback is onPointHover(Consumer); this is for subclasses.
    • updateRegions

      protected final void updateRegions()
      Recomputes the title/legend/plot split. For a subclass laying out in onLayout.
    • animationBaseline

      protected double animationBaseline()
      The value a series grows out of when it first appears, and collapses to when it is hidden: where the axis crosses the plot, which is zero on any scale that contains it and the nearer end otherwise.
    • staggerFraction

      protected float staggerFraction()
      How far apart consecutive elements start, as a fraction of the animation: a wipe rather than a single pop. 0 (the default) starts everything at once, which is what a line wants: staggering the points of a line animates a wave through it.
    • progress

      protected final float progress()
      Overall animation progress in [0,1]; 1 means the data as it stands.
    • elementProgress

      protected final float elementProgress(int index, int count)
      Animation progress for element index of count, with staggerFraction() applied. Elements past the first start later and finish later, and every one of them reaches 1 by the time progress() does.
    • seriesAlpha

      protected final float seriesAlpha(int index)
      The alpha a series is currently drawn at, folding in its hide/show fade.
    • drawnValue

      protected final double drawnValue(int seriesIndex, int index)
      The value series seriesIndex is drawn at right now, animation included.
    • pointFor

      protected final ChartPoint pointFor(int seriesIndex, int index, double share, float x, float y)
      Builds a ChartPoint for a datum, with the anchor the caller measured.
    • tokens

      protected final SizeTokens tokens()
      The tokens for the step resolved on this chart; resolve once per pass, never in a field.
    • measure

      protected final TextMetrics measure(String text, Font font)
      Measures a single line with the layout ruler (agrees with what drawText draws).
    • ellipsize

      protected final String ellipsize(String text, Font font, float maxWidth)
      text shortened with an ellipsis until it fits maxWidth, or "" when not even the ellipsis fits. Category labels are application data: they are as long as they are, and a chart that lets them collide is unreadable.

      Shaped once and cut where ShapedText.fitEnd(int, float) says, then re-shaped once or twice, the way Label does it. The obvious loop (drop a character, measure, repeat) made a label that overflows by forty characters cost forty shapings a paint, and a chart paints every frame of a value transition; worse, each candidate was a distinct string in the ruler's memo, so a few long labels evicted every other widget's text from it and everything on screen re-shaped each frame.

    • onAttached

      protected void onAttached()
      Description copied from class: Widget
      Called when this widget enters a scene (attached to the tree). Widget.scene() is the scene it just joined. Fires top-down, so a parent runs before its children. Default no-op.
      Overrides:
      onAttached in class Widget
    • onMeasure

      protected Size onMeasure(Constraints constraints)
      Description copied from class: Widget
      Reports the size this widget wants within constraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.

      Resolve the ControlSize and the LayoutDirection once each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, and I18n.locale() answers it wherever text is resolved.

      Specified by:
      onMeasure in class Widget
    • onPaint

      protected void onPaint(Canvas canvas)
      Description copied from class: Widget
      Widget's own background/content, in local coordinates.
      Overrides:
      onPaint in class Widget
    • onPaintOverlay

      protected void onPaintOverlay(Canvas canvas)
      Description copied from class: Widget
      Painted after children (scrollbars, focus rings…).
      Overrides:
      onPaintOverlay in class Widget
    • onMouseEvent

      protected void onMouseEvent(MouseEvent event)
      Description copied from class: Widget
      Mouse events (bubbling); call event.consume() when handled.
      Overrides:
      onMouseEvent in class Widget
    • stateFadeSeconds

      protected final double stateFadeSeconds()
      How long a state fade lasts (hiding a series, popping a hovered mark), in seconds. Short, because it is feedback rather than data movement, and 0 whenever setAnimationDuration(double) turned animation off, so "off" means all of it.