Skip to content

Pie and donut ​

D3PPieRenderer draws a series of [name, size] pairs as slices: no axes, one renderer. It draws in a polar coordinate system and takes its center, radius, start angle, direction and hole from it, so the settings of the system shape both pies and polar charts. A donut is a pie in a system whose innerRadius is above 0.

Hover a slice to move it and see its share in the tooltip; click an entry of the legend to hide a slice, and the others fill the circle.

ts
import {
  d3pAutoInteractionArea, D3PChart, d3pDomLegend, d3pDomTooltip, D3PLegendBundle, D3PPieRenderer,
  D3PPolarSystem, D3PSeries, D3PTooltip, getGlobalBus,
} from 'd3pict'

const bus = getGlobalBus()

const container = document.getElementById('app')! // any element with a size: the chart fills it

const share = new D3PSeries({
  bus,
  name: 'browsers',
  data: [['Chrome', 62], ['Safari', 20], ['Edge', 5.5], ['Firefox', 3.2], ['Samsung', 2.6], ['Opera', 2.1], ['Other', 4.6]],
})
const system = new D3PPolarSystem({ bus, props: { padding: 20 } })
const pie = new D3PPieRenderer({ bus, series: share, props: { padAngle: 1, cornerRadius: 3, sort: 'descending' } })
const tooltip = new D3PTooltip({ bus })
const legend = new D3PLegendBundle({ bus })
// the system comes first: the pie draws in it
const objects = [system, pie, tooltip, legend]

const chart = new D3PChart({ container, bus })
const stopArea = d3pAutoInteractionArea(chart) // before the objects: the tools, the cursor and the tooltip ask for an interaction area
objects.forEach((o) => o.addToChart(chart))
const stopTooltip = d3pDomTooltip(tooltip)
const stopLegend = d3pDomLegend(legend)

// a pie is a donut with no hole
const shape = Object.assign(document.createElement('div'), { className: 'toolbar' })
for (const [name, innerRadius] of [['pie', 0], ['donut', 0.55]] as const) {
  const button = Object.assign(document.createElement('button'), { textContent: name, onclick: () => system.props({ innerRadius }) })
  shape.append(button)
}
container.before(shape)

What it does ​

  • Slices. One per point with a positive size, in data order (or sorted with sort: 'ascending' | 'descending'). Values that are not positive numbers have no slice. padAngle (degrees) and cornerRadius (pixels) shape them; style.colors replaces the default palette.
  • Legend. Its legend has one entry per slice, not one for the renderer: every renderer lists its own entries (legendEntries()), and an entry's toggle() shows or hides what it stands for. Custom legends use entry.label, entry.color, entry.visible and entry.toggle().
  • Tooltip. Without axes, there are no values to read under the pointer: the renderer answers by testing the pointer against its slices (its probeHit). The line shows the name, the size and the share (label, percent), and the box follows the pointer. Renderers of your own can do the same.
  • Hover. The slice under the pointer moves outwards by explode pixels. It is the default marker of the pie (marker: false removes it, hover: false turns the effect off; the pie then does not ask for an interaction area).
  • Labels. The share of each slice, above labels.minPercent; labels.format replaces the text.
  • Nested pies. radius: [from, to] (fractions of the ring of the system) gives each pie its own band, so two pies on one chart form concentric rings.
  • Next to polar axes. A pie can share the system, and the chart, of polar series: the renderer ignores the axes.

A pie needs a polar system: given a cartesian one, it throws an explicit error when attached.