ChartGeometry.h header

#include <ew/app/ChartGeometry.h>

Namespace ew::app

AxisScale struct

struct ew::app::AxisScale

Where a chart's value axis starts, ends, and places its gridlines.

Deliberately a plain result rather than an axis object: it is computed once per paint, read by the gridline loop, the bar geometry and the label formatter, and never mutated.

Members

double ew::app::chart::AxisScale::minimum = 0.0

The value at the bottom of the plot.

double ew::app::chart::AxisScale::maximum = 1.0

The value at the top of the plot.

double ew::app::chart::AxisScale::step = 1.0

The distance between neighbouring gridlines, always > 0.

bool ew::app::chart::AxisScale::operator==(const AxisScale &other) const =default

Defaulted equality comparison.

PieSlice struct

struct ew::app::PieSlice

One pie slice: which category it is, and the wedge it occupies.

Angles are measured clockwise from twelve o'clock, which is how a reader describes a pie and how the tests read. Qt's own convention (counter-clockwise from three o'clock, in sixteenths of a degree) is applied by the painter at the one place it draws, so it does not leak into the maths.

Members

std::size_t ew::app::chart::PieSlice::index = 0

The category index this slice shows.

double ew::app::chart::PieSlice::startDegrees = 0.0

Where the slice begins, in degrees clockwise from twelve o'clock.

double ew::app::chart::PieSlice::spanDegrees = 0.0

How far the slice sweeps, in degrees; always > 0.

bool ew::app::chart::PieSlice::operator==(const PieSlice &other) const =default

Defaulted equality comparison.

ValueRange struct

struct ew::app::ValueRange

The lowest and highest values a chart plots.

Members

double ew::app::chart::ValueRange::lowest = 0.0

The smallest plotted value.

double ew::app::chart::ValueRange::highest = 0.0

The largest plotted value.

bool ew::app::chart::ValueRange::operator==(const ValueRange &other) const =default

Defaulted equality comparison.

Functions

int ew::app::chart::axisLabelDecimals(double step)

How many decimal places an axis label needs to distinguish neighbouring ticks of step.

Formatting itself is the painter's job, because it is locale-dependent and this is not; what is shared – and what would otherwise be guessed differently in each place – is that a step of 2000 needs none and a step of 0.25 needs two.

std::vector< double > ew::app::chart::axisTicks(AxisScale scale)

The values scale places a gridline and a label at, from minimum to maximum inclusive.

QRectF ew::app::chart::barRect(const QRectF &plot, AxisScale scale, std::size_t categoryIndex, std::size_t categoryCount, std::size_t seriesIndex, std::size_t seriesCount, double value)

The rectangle a bar covers: category categoryIndex of categoryCount, series seriesIndex of seriesCount, holding value under scale within plot.

The bar runs between the value and the baseline, so a negative value produces a rectangle hanging below zero rather than an inverted one, and a value of exactly the baseline produces an empty rectangle rather than a stripe of colour meaning nothing. Bars fill most of their category band and share it between series, leaving a gap that is what makes a grouped bar chart readable as groups.

double ew::app::chart::categoryCentreX(const QRectF &plot, std::size_t index, std::size_t count)

The centre x of category index's band – where its line point, tick and label align.

std::size_t ew::app::chart::categoryLabelStride(double slotWidth, double minimumLabelWidth)

Draw every n th category label, where n is 1 when they all fit.

Eliding alone is not enough: past roughly thirty categories every label elides to an ellipsis and the axis says nothing at all. Below the width at which a label can carry a character or two, the answer is to show fewer labels rather than narrower ones.

QRectF ew::app::chart::categorySlot(const QRectF &plot, std::size_t index, std::size_t count)

The horizontal band category index occupies within plot, of count equal bands.

ValueRange ew::app::chart::chartValueRange(const ew::core::chart::Chart &chart)

The lowest and highest values chart plots, including the zeros a short series implies.

Zero is forced into the range for bar and area charts because both are drawn from a baseline: a bar chart of values 90 to 100 scaled 90-100 shows four bars of wildly different heights that are all within 10% of each other, which is a misleading picture rather than a tight one. A line chart is not anchored that way, so it keeps the range its data actually occupies.

AxisScale ew::app::chart::niceAxisScale(ValueRange range, int targetTicks=5)

Axis bounds and a gridline step that land on round numbers covering range.

A range of 0 to 7300 must tick at 0, 2000, 4000, 6000, 8000 – not at 0 and 7300. The step is snapped to 1, 2, 2.5, 5 or 10 times a power of ten, which is what makes every gridline a number a reader can hold in their head, and targetTicks is a target rather than a promise because rounding outwards to a whole step can add one.

Degenerate input is handled here rather than at each call site: a range of zero at zero (an empty chart, or a chart of all zeros) becomes 0 to 1, any other flat range is opened out around its value so a flat line is still drawn inside the plot, and a non-finite bound – which a project file can carry – falls back to 0 to 1 instead of propagating into every later division.

std::vector< PieSlice > ew::app::chart::pieSlices(const std::vector< double > &values)

The wedges a pie of values is divided into, in category order.

A pie can only show a share of a whole, so values that are not part of one are dropped rather than drawn wrongly: a negative value has no share, and a set that totals zero or less has no whole to divide, which returns no slices at all and leaves the painter to say so. This is the case that silently divided by zero in every chart library that has ever had the bug.

double ew::app::chart::valueAt(const ew::core::chart::ChartSeries &series, std::size_t index)

The value in series for category index, or 0 when the series has no value there.

The category list is authoritative for a series' length (see ew::core::chart::ChartSeries), so a series shorter than it reads as 0 for the missing tail. Every surface that plots a chart asks this one function rather than indexing the vector itself, because a hand-edited project file or an MCP client can produce a ragged series and a direct index would read out of bounds.

double ew::app::chart::valueToY(AxisScale scale, double value, double plotTop, double plotHeight)

The y coordinate value sits at within a plot of plotTop and plotHeight under scale.

Larger values are nearer the top, which is the one place the screen's inverted y axis is dealt with.