@funnest/charts-viz 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +132 -1
- package/dist/shapes.d.ts +45 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,3 +9,4 @@ export { niceStep, niceDomain, ticks, logTicks, sampleIndices } from './ticks';
|
|
|
9
9
|
export { linePath, areaPath, barPath, segments, type XY, type Curve } from './path';
|
|
10
10
|
export { extent, maxLength, stack, stackedExtent, percentages, nearestIndex, seriesChange, type SeriesData, type StackedSeries, type PercentageSegment, } from './data';
|
|
11
11
|
export { DEFAULT_PALETTE, seriesColor } from './palette';
|
|
12
|
+
export { arcs, arcPath, arcCentroid, treemap, type ArcSegment, type TreemapRect } from './shapes';
|
package/dist/index.js
CHANGED
|
@@ -276,8 +276,138 @@ function seriesChange(data, end = data.length - 1) {
|
|
|
276
276
|
// src/palette.ts
|
|
277
277
|
var DEFAULT_PALETTE = ["#6C4DFF", "#F59E0B", "#14B8A6", "#EC4899", "#0EA5E9", "#84CC16", "#A16207", "#64748B"];
|
|
278
278
|
var seriesColor = (index, palette = DEFAULT_PALETTE) => palette[(index % palette.length + palette.length) % palette.length];
|
|
279
|
+
|
|
280
|
+
// src/shapes.ts
|
|
281
|
+
var TAU = Math.PI * 2;
|
|
282
|
+
var f2 = (n) => Math.round(n * 100) / 100;
|
|
283
|
+
function arcs(items, padAngle = 0) {
|
|
284
|
+
const total = items.reduce((t, i) => t + Math.max(0, i.value), 0);
|
|
285
|
+
const visible = items.filter((i) => i.value > 0).length;
|
|
286
|
+
const pad = visible > 1 ? padAngle : 0;
|
|
287
|
+
let a = 0;
|
|
288
|
+
return items.map((i) => {
|
|
289
|
+
const fraction = total > 0 ? Math.max(0, i.value) / total : 0;
|
|
290
|
+
const span = fraction * TAU;
|
|
291
|
+
const p = Math.min(pad, span * 0.8);
|
|
292
|
+
const seg = { id: i.id, value: i.value, fraction, startAngle: a + p / 2, endAngle: a + span - p / 2 };
|
|
293
|
+
a += span;
|
|
294
|
+
return seg;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
var pt = (cx, cy, r, a) => [cx + r * Math.sin(a), cy - r * Math.cos(a)];
|
|
298
|
+
function arcPath(cx, cy, innerRadius, outerRadius, startAngle, endAngle, cornerRadius = 0) {
|
|
299
|
+
const span = endAngle - startAngle;
|
|
300
|
+
if (span <= 0 || outerRadius <= 0) return "";
|
|
301
|
+
if (span >= TAU - 1e-6) {
|
|
302
|
+
const [x0, y0] = pt(cx, cy, outerRadius, 0);
|
|
303
|
+
const [x1, y1] = pt(cx, cy, outerRadius, Math.PI);
|
|
304
|
+
let d2 = `M${f2(x0)},${f2(y0)}A${f2(outerRadius)},${f2(outerRadius)} 0 1 1 ${f2(x1)},${f2(y1)}A${f2(outerRadius)},${f2(outerRadius)} 0 1 1 ${f2(x0)},${f2(y0)}Z`;
|
|
305
|
+
if (innerRadius > 0) {
|
|
306
|
+
const [i0x, i0y] = pt(cx, cy, innerRadius, 0);
|
|
307
|
+
const [i1x, i1y] = pt(cx, cy, innerRadius, Math.PI);
|
|
308
|
+
d2 += `M${f2(i0x)},${f2(i0y)}A${f2(innerRadius)},${f2(innerRadius)} 0 1 0 ${f2(i1x)},${f2(i1y)}A${f2(innerRadius)},${f2(innerRadius)} 0 1 0 ${f2(i0x)},${f2(i0y)}Z`;
|
|
309
|
+
}
|
|
310
|
+
return d2;
|
|
311
|
+
}
|
|
312
|
+
const large = span > Math.PI ? 1 : 0;
|
|
313
|
+
const thick = outerRadius - innerRadius;
|
|
314
|
+
const cr = Math.max(0, Math.min(cornerRadius, thick / 2, (innerRadius || outerRadius) * span / 2 - 0.01));
|
|
315
|
+
if (cr <= 0.01) {
|
|
316
|
+
const [ox0, oy0] = pt(cx, cy, outerRadius, startAngle);
|
|
317
|
+
const [ox1, oy1] = pt(cx, cy, outerRadius, endAngle);
|
|
318
|
+
let d2 = `M${f2(ox0)},${f2(oy0)}A${f2(outerRadius)},${f2(outerRadius)} 0 ${large} 1 ${f2(ox1)},${f2(oy1)}`;
|
|
319
|
+
if (innerRadius > 0) {
|
|
320
|
+
const [ix1, iy1] = pt(cx, cy, innerRadius, endAngle);
|
|
321
|
+
const [ix0, iy0] = pt(cx, cy, innerRadius, startAngle);
|
|
322
|
+
d2 += `L${f2(ix1)},${f2(iy1)}A${f2(innerRadius)},${f2(innerRadius)} 0 ${large} 0 ${f2(ix0)},${f2(iy0)}Z`;
|
|
323
|
+
} else d2 += `L${f2(cx)},${f2(cy)}Z`;
|
|
324
|
+
return d2;
|
|
325
|
+
}
|
|
326
|
+
const ao = cr / (outerRadius - cr);
|
|
327
|
+
const ai = innerRadius > 0 ? cr / (innerRadius + cr) : 0;
|
|
328
|
+
const [a1x, a1y] = pt(cx, cy, outerRadius - cr, startAngle);
|
|
329
|
+
const [b1x, b1y] = pt(cx, cy, outerRadius, startAngle + ao);
|
|
330
|
+
const [b2x, b2y] = pt(cx, cy, outerRadius, endAngle - ao);
|
|
331
|
+
const [a2x, a2y] = pt(cx, cy, outerRadius - cr, endAngle);
|
|
332
|
+
let d = `M${f2(a1x)},${f2(a1y)}A${f2(cr)},${f2(cr)} 0 0 1 ${f2(b1x)},${f2(b1y)}`;
|
|
333
|
+
d += `A${f2(outerRadius)},${f2(outerRadius)} 0 ${span - 2 * ao > Math.PI ? 1 : 0} 1 ${f2(b2x)},${f2(b2y)}`;
|
|
334
|
+
d += `A${f2(cr)},${f2(cr)} 0 0 1 ${f2(a2x)},${f2(a2y)}`;
|
|
335
|
+
if (innerRadius > 0) {
|
|
336
|
+
const [c2x, c2y] = pt(cx, cy, innerRadius + cr, endAngle);
|
|
337
|
+
const [d2x, d2y] = pt(cx, cy, innerRadius, endAngle - ai);
|
|
338
|
+
const [d1x, d1y] = pt(cx, cy, innerRadius, startAngle + ai);
|
|
339
|
+
const [c1x, c1y] = pt(cx, cy, innerRadius + cr, startAngle);
|
|
340
|
+
d += `L${f2(c2x)},${f2(c2y)}A${f2(cr)},${f2(cr)} 0 0 1 ${f2(d2x)},${f2(d2y)}`;
|
|
341
|
+
d += `A${f2(innerRadius)},${f2(innerRadius)} 0 ${span - 2 * ai > Math.PI ? 1 : 0} 0 ${f2(d1x)},${f2(d1y)}`;
|
|
342
|
+
d += `A${f2(cr)},${f2(cr)} 0 0 1 ${f2(c1x)},${f2(c1y)}Z`;
|
|
343
|
+
} else d += `L${f2(cx)},${f2(cy)}Z`;
|
|
344
|
+
return d;
|
|
345
|
+
}
|
|
346
|
+
function arcCentroid(cx, cy, r, startAngle, endAngle) {
|
|
347
|
+
const [x, y] = pt(cx, cy, r, (startAngle + endAngle) / 2);
|
|
348
|
+
return { x, y };
|
|
349
|
+
}
|
|
350
|
+
function treemap(items, width, height, gap = 0) {
|
|
351
|
+
const list = items.filter((i2) => i2.value > 0).sort((a, b) => b.value - a.value);
|
|
352
|
+
const total = list.reduce((t, i2) => t + i2.value, 0);
|
|
353
|
+
if (!total || width <= 0 || height <= 0) return [];
|
|
354
|
+
const scale = width * height / total;
|
|
355
|
+
const out = [];
|
|
356
|
+
let x = 0;
|
|
357
|
+
let y = 0;
|
|
358
|
+
let w = width;
|
|
359
|
+
let h = height;
|
|
360
|
+
let i = 0;
|
|
361
|
+
const worst = (row, side) => {
|
|
362
|
+
const s = row.reduce((t, v) => t + v, 0);
|
|
363
|
+
const max = Math.max(...row);
|
|
364
|
+
const min = Math.min(...row);
|
|
365
|
+
return Math.max(side * side * max / (s * s), s * s / (side * side * min));
|
|
366
|
+
};
|
|
367
|
+
while (i < list.length) {
|
|
368
|
+
const side = Math.min(w, h);
|
|
369
|
+
const row = [list[i].value * scale];
|
|
370
|
+
let j = i + 1;
|
|
371
|
+
while (j < list.length) {
|
|
372
|
+
const next = [...row, list[j].value * scale];
|
|
373
|
+
if (worst(next, side) > worst(row, side)) break;
|
|
374
|
+
row.push(list[j].value * scale);
|
|
375
|
+
j++;
|
|
376
|
+
}
|
|
377
|
+
const sum = row.reduce((t, v) => t + v, 0);
|
|
378
|
+
const thick = sum / side;
|
|
379
|
+
let off = 0;
|
|
380
|
+
row.forEach((area, k) => {
|
|
381
|
+
const len = area / thick;
|
|
382
|
+
const item = list[i + k];
|
|
383
|
+
const r = w >= h ? { x, y: y + off, width: thick, height: len } : { x: x + off, y, width: len, height: thick };
|
|
384
|
+
out.push({ id: item.id, value: item.value, ...r });
|
|
385
|
+
off += len;
|
|
386
|
+
});
|
|
387
|
+
if (w >= h) {
|
|
388
|
+
x += thick;
|
|
389
|
+
w -= thick;
|
|
390
|
+
} else {
|
|
391
|
+
y += thick;
|
|
392
|
+
h -= thick;
|
|
393
|
+
}
|
|
394
|
+
i = j;
|
|
395
|
+
}
|
|
396
|
+
if (gap <= 0) return out;
|
|
397
|
+
const g = gap / 2;
|
|
398
|
+
return out.map((r) => ({
|
|
399
|
+
...r,
|
|
400
|
+
x: r.x + (r.x > 0.5 ? g : 0),
|
|
401
|
+
y: r.y + (r.y > 0.5 ? g : 0),
|
|
402
|
+
width: Math.max(0, r.width - (r.x > 0.5 ? g : 0) - (r.x + r.width < width - 0.5 ? g : 0)),
|
|
403
|
+
height: Math.max(0, r.height - (r.y > 0.5 ? g : 0) - (r.y + r.height < height - 0.5 ? g : 0))
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
279
406
|
export {
|
|
280
407
|
DEFAULT_PALETTE,
|
|
408
|
+
arcCentroid,
|
|
409
|
+
arcPath,
|
|
410
|
+
arcs,
|
|
281
411
|
areaPath,
|
|
282
412
|
bandScale,
|
|
283
413
|
barPath,
|
|
@@ -298,5 +428,6 @@ export {
|
|
|
298
428
|
seriesColor,
|
|
299
429
|
stack,
|
|
300
430
|
stackedExtent,
|
|
301
|
-
ticks
|
|
431
|
+
ticks,
|
|
432
|
+
treemap
|
|
302
433
|
};
|
package/dist/shapes.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Pie/donut and treemap geometry. Pure math, no DOM. */
|
|
2
|
+
export interface ArcSegment {
|
|
3
|
+
id: string;
|
|
4
|
+
value: number;
|
|
5
|
+
/** Share of the total, 0–1. */
|
|
6
|
+
fraction: number;
|
|
7
|
+
/** Radians, clockwise from 12 o'clock. */
|
|
8
|
+
startAngle: number;
|
|
9
|
+
endAngle: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Split a full circle into segments by value (negatives count as 0).
|
|
13
|
+
* `padAngle` (radians) is the gap between neighbours; it is taken out of each segment.
|
|
14
|
+
*/
|
|
15
|
+
export declare function arcs(items: readonly {
|
|
16
|
+
id: string;
|
|
17
|
+
value: number;
|
|
18
|
+
}[], padAngle?: number): ArcSegment[];
|
|
19
|
+
/**
|
|
20
|
+
* SVG path for a ring segment (a pie slice when `innerRadius` is 0).
|
|
21
|
+
* `cornerRadius` softens the four corners.
|
|
22
|
+
*/
|
|
23
|
+
export declare function arcPath(cx: number, cy: number, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, cornerRadius?: number): string;
|
|
24
|
+
/** Point at the middle of a segment, `r` from the center (labels, hover offsets). */
|
|
25
|
+
export declare function arcCentroid(cx: number, cy: number, r: number, startAngle: number, endAngle: number): {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
};
|
|
29
|
+
export interface TreemapRect {
|
|
30
|
+
id: string;
|
|
31
|
+
value: number;
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Squarified treemap (Bruls, Huizing, van Wijk): rectangles with areas proportional to
|
|
39
|
+
* value and aspect ratios close to 1. Items are laid out largest first; non-positive
|
|
40
|
+
* values are dropped.
|
|
41
|
+
*/
|
|
42
|
+
export declare function treemap(items: readonly {
|
|
43
|
+
id: string;
|
|
44
|
+
value: number;
|
|
45
|
+
}[], width: number, height: number, gap?: number): TreemapRect[];
|
package/package.json
CHANGED