packages/core/src/features/viz/lib/pie-slices.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import type { PieLayoutProps, PieLayoutSlice } from './pie-types.js';
2 import type { LineAccessor } from "./types.js";
3 import type { PlotKeyAccessor } from "./plot-types.js";
4 import { pointAccessor, pointData } from "./point-data.js";
5
6 /**
7 * Value and stable-key accessors used to derive pie-slice geometry.
8 *
9 * @see {@link LineAccessor}
10 * @see {@link PlotKeyAccessor}
11 * @see {@link pieSlices}
12 * @see {@link PieLayoutProps}
13 */
14 export interface PieOptions<D> {
15 /** Value associated with this sample, input, or result. See {@link LineAccessor}. */
16 readonly value: LineAccessor<D>;
17 /** Property or callback supplying stable identity for each datum. See {@link PlotKeyAccessor}. */
18 readonly keyBy?: PlotKeyAccessor<D>;
19 }
20
21 /**
22 * A datum's key, value, fraction, and angular interval in a pie layout.
23 *
24 * @see {@link pieSlices}
25 * @see {@link PieLayoutProps}
26 * @see {@link PieLayoutSlice}
27 */
28 export interface PieSliceGeometry<D> {
29 /** The original data item selected by this result. See {@link PieSliceGeometry}. */
30 readonly datum: D;
31 /** Index of the selected datum in its source data. See {@link PieSliceGeometry}. */
32 readonly index: number;
33 /** Stable identity used by the containing protocol. See {@link PieSliceGeometry}. */
34 readonly key: string;
35 /** Value associated with this sample, input, or result. See {@link PieSliceGeometry}. */
36 readonly value: number;
37 /** Normalized share or position represented by this value. See {@link PieSliceGeometry}. */
38 readonly fraction: number;
39 /** Angle at which the arc or slice begins. See {@link PieSliceGeometry}. */
40 readonly startAngle: number;
41 /** Angle at which the arc or slice ends. See {@link PieSliceGeometry}. */
42 readonly endAngle: number;
43 }
44
45 /**
46 * Pure source-ordered full-circle layout. Angles use Canvas radians, clockwise from the top.
47 *
48 * @param data - Records whose values determine slice proportions.
49 * @param options - Value accessor and optional stable key accessor. See {@link PieOptions}.
50 * @returns Ordered slice geometry associated with the input records. See {@link PieSliceGeometry}.
51 *
52 * @see {@link PieOptions}
53 * @see {@link PieSliceGeometry}
54 */
55 export function pieSlices<D>(
56 data: readonly D[],
57 { value: input, keyBy }: PieOptions<D>,
58 ): readonly PieSliceGeometry<D>[] {
59 pointData(data, "pieSlices");
60 const accessor = pointAccessor(input);
61 const keys = new Set<string>();
62 let total = 0;
63 const rows = data.map((datum, index) => {
64 const raw = accessor(datum, index, data);
65 const value = raw == null ? 0 : raw;
66 if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
67 throw new RangeError(
68 `pieSlices: value at index ${index} must be finite and nonnegative, null, or undefined.`,
69 );
70 total += value;
71 const rawKey =
72 keyBy === undefined
73 ? index
74 : typeof keyBy === "function"
75 ? keyBy(datum, index, data)
76 : datum[keyBy];
77 if (
78 typeof rawKey !== "string" &&
79 (typeof rawKey !== "number" || !Number.isFinite(rawKey))
80 )
81 throw new TypeError(
82 `pieSlices: keyBy at index ${index} must return a string or finite number.`,
83 );
84 const key = String(rawKey);
85 if (keys.has(key)) throw new Error(`pieSlices: duplicate key "${key}".`);
86 keys.add(key);
87 return { datum, index, key, value };
88 });
89 if (!Number.isFinite(total))
90 throw new RangeError(
91 "pieSlices: value total must be finite; rescale the values.",
92 );
93 const start = -Math.PI / 2;
94 let sum = 0;
95 let angle = start;
96 return Object.freeze(
97 rows.map((row) => {
98 const startAngle = angle;
99 sum += row.value;
100 if (total > 0 && row.value > 0)
101 angle = start + (sum / total) * Math.PI * 2;
102 return Object.freeze({
103 ...row,
104 fraction: total === 0 ? 0 : row.value / total,
105 startAngle,
106 endAngle: angle,
107 });
108 }),
109 );
110 }
111
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.