@ggterm/core 0.2.9 → 0.2.11

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.js CHANGED
@@ -1805,6 +1805,120 @@ function renderGeomViolin(data, geom, _aes, scales, canvas) {
1805
1805
  }
1806
1806
  }
1807
1807
  }
1808
+ function renderGeomRidgeline(data, geom, aes, scales, canvas) {
1809
+ const scaleFactor = geom.params.scale ?? 0.9;
1810
+ const alpha = geom.params.alpha ?? 0.8;
1811
+ const showOutline = geom.params.outline ?? true;
1812
+ const fixedFill = geom.params.fill;
1813
+ const fixedColor = geom.params.color;
1814
+ const plotLeft = Math.round(scales.x.range[0]);
1815
+ const plotRight = Math.round(scales.x.range[1]);
1816
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
1817
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
1818
+ const groups = new Map;
1819
+ const groupKeys = [];
1820
+ for (const row of data) {
1821
+ const key = String(row.y ?? "default");
1822
+ if (!groups.has(key)) {
1823
+ groups.set(key, { data: [], index: Number(row.yIndex) ?? groupKeys.length });
1824
+ groupKeys.push(key);
1825
+ }
1826
+ groups.get(key).data.push(row);
1827
+ }
1828
+ const numGroups = groupKeys.length;
1829
+ if (numGroups === 0)
1830
+ return;
1831
+ const plotHeight = Math.abs(plotBottom - plotTop);
1832
+ const ridgeBaseHeight = plotHeight / numGroups;
1833
+ const defaultColors = [
1834
+ { r: 79, g: 169, b: 238, a: 1 },
1835
+ { r: 238, g: 136, b: 102, a: 1 },
1836
+ { r: 102, g: 204, b: 153, a: 1 },
1837
+ { r: 204, g: 102, b: 204, a: 1 },
1838
+ { r: 255, g: 200, b: 87, a: 1 },
1839
+ { r: 138, g: 201, b: 222, a: 1 },
1840
+ { r: 255, g: 153, b: 153, a: 1 },
1841
+ { r: 170, g: 170, b: 170, a: 1 }
1842
+ ];
1843
+ let parsedFillColor = null;
1844
+ if (fixedFill) {
1845
+ parsedFillColor = parseColorToRgba(fixedFill);
1846
+ }
1847
+ const sortedKeys = [...groupKeys].sort((a, b) => {
1848
+ const idxA = groups.get(a)?.index ?? 0;
1849
+ const idxB = groups.get(b)?.index ?? 0;
1850
+ return idxB - idxA;
1851
+ });
1852
+ for (const groupKey of sortedKeys) {
1853
+ const group = groups.get(groupKey);
1854
+ if (!group)
1855
+ continue;
1856
+ const groupIndex = group.index;
1857
+ const groupData = group.data;
1858
+ const sorted = [...groupData].sort((a, b) => {
1859
+ const ax = Number(a.x) || 0;
1860
+ const bx = Number(b.x) || 0;
1861
+ return ax - bx;
1862
+ });
1863
+ if (sorted.length < 2)
1864
+ continue;
1865
+ const baseline = plotTop + (groupIndex + 0.5) * ridgeBaseHeight;
1866
+ let fillColor;
1867
+ if (parsedFillColor) {
1868
+ fillColor = parsedFillColor;
1869
+ } else if ((aes.fill || aes.color) && scales.color) {
1870
+ const mappedColor = scales.color.map(groupKey);
1871
+ if (typeof mappedColor === "object" && "r" in mappedColor) {
1872
+ fillColor = mappedColor;
1873
+ } else {
1874
+ fillColor = defaultColors[groupIndex % defaultColors.length];
1875
+ }
1876
+ } else {
1877
+ fillColor = defaultColors[groupIndex % defaultColors.length];
1878
+ }
1879
+ const alphaFillColor = {
1880
+ r: Math.round(fillColor.r * alpha + 255 * (1 - alpha) * 0.1),
1881
+ g: Math.round(fillColor.g * alpha + 255 * (1 - alpha) * 0.1),
1882
+ b: Math.round(fillColor.b * alpha + 255 * (1 - alpha) * 0.1),
1883
+ a: 1
1884
+ };
1885
+ const maxRidgeHeight = ridgeBaseHeight * scaleFactor * 1.5;
1886
+ const outlinePoints = [];
1887
+ for (const row of sorted) {
1888
+ const xVal = Number(row.x);
1889
+ const scaled = Number(row.scaled) || 0;
1890
+ const px = Math.round(scales.x.map(xVal));
1891
+ const ridgeHeight = scaled * maxRidgeHeight;
1892
+ const py = Math.round(baseline - ridgeHeight);
1893
+ if (px < plotLeft || px > plotRight)
1894
+ continue;
1895
+ const fillTop = Math.max(plotTop, py);
1896
+ const fillBottom = Math.min(plotBottom, Math.round(baseline));
1897
+ for (let y = fillTop;y <= fillBottom; y++) {
1898
+ canvas.drawChar(px, y, "█", alphaFillColor);
1899
+ }
1900
+ if (showOutline) {
1901
+ outlinePoints.push({ x: px, y: Math.max(plotTop, py) });
1902
+ }
1903
+ }
1904
+ if (showOutline && outlinePoints.length > 1) {
1905
+ const outlineColor = fixedColor ? parseColorToRgba(fixedColor) : { r: Math.min(255, fillColor.r + 40), g: Math.min(255, fillColor.g + 40), b: Math.min(255, fillColor.b + 40), a: 1 };
1906
+ for (let i = 0;i < outlinePoints.length - 1; i++) {
1907
+ const p1 = outlinePoints[i];
1908
+ const p2 = outlinePoints[i + 1];
1909
+ if (Math.abs(p2.x - p1.x) <= 1) {
1910
+ if (p1.y >= plotTop && p1.y <= plotBottom) {
1911
+ canvas.drawChar(p1.x, p1.y, "▄", outlineColor);
1912
+ }
1913
+ }
1914
+ }
1915
+ const last = outlinePoints[outlinePoints.length - 1];
1916
+ if (last.y >= plotTop && last.y <= plotBottom) {
1917
+ canvas.drawChar(last.x, last.y, "▄", outlineColor);
1918
+ }
1919
+ }
1920
+ }
1921
+ }
1808
1922
  function renderGeomTile(data, geom, aes, scales, canvas) {
1809
1923
  const alpha = geom.params.alpha ?? 1;
1810
1924
  const plotLeft = Math.round(scales.x.range[0]);
@@ -2194,6 +2308,58 @@ function parseColor(color) {
2194
2308
  }
2195
2309
  return { r: 128, g: 128, b: 128, a: 1 };
2196
2310
  }
2311
+ function renderGeomBeeswarm(data, geom, aes, scales, canvas) {
2312
+ const alpha = geom.params.alpha ?? 1;
2313
+ const fixedColor = geom.params.color;
2314
+ const shape = getPointShape(geom.params.shape);
2315
+ const plotLeft = Math.round(scales.x.range[0]);
2316
+ const plotRight = Math.round(scales.x.range[1]);
2317
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2318
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2319
+ const defaultColors = [
2320
+ { r: 79, g: 169, b: 238, a: 1 },
2321
+ { r: 238, g: 136, b: 102, a: 1 },
2322
+ { r: 102, g: 204, b: 153, a: 1 },
2323
+ { r: 204, g: 102, b: 204, a: 1 },
2324
+ { r: 255, g: 200, b: 87, a: 1 },
2325
+ { r: 138, g: 201, b: 222, a: 1 },
2326
+ { r: 255, g: 153, b: 153, a: 1 },
2327
+ { r: 170, g: 170, b: 170, a: 1 }
2328
+ ];
2329
+ const categories = new Set;
2330
+ for (const row of data) {
2331
+ categories.add(String(row.xOriginal ?? row[aes.x] ?? "default"));
2332
+ }
2333
+ const categoryList = [...categories];
2334
+ for (const row of data) {
2335
+ const xVal = row.x;
2336
+ const yVal = row.y;
2337
+ if (xVal === null || xVal === undefined || yVal === null || yVal === undefined) {
2338
+ continue;
2339
+ }
2340
+ const numGroups = categoryList.length;
2341
+ const xRange = plotRight - plotLeft;
2342
+ const xNormalized = (Number(xVal) + 0.5) / numGroups;
2343
+ const cx = Math.round(plotLeft + xNormalized * xRange);
2344
+ const cy = Math.round(scales.y.map(yVal));
2345
+ let color;
2346
+ if (fixedColor) {
2347
+ color = parseColorToRgba(fixedColor);
2348
+ } else if (scales.color && aes.color) {
2349
+ color = getPointColor(row, aes, scales.color);
2350
+ } else {
2351
+ const category = String(row.xOriginal ?? row[aes.x] ?? "default");
2352
+ const categoryIdx = categoryList.indexOf(category);
2353
+ color = defaultColors[categoryIdx % defaultColors.length];
2354
+ }
2355
+ if (alpha < 1) {
2356
+ color = { ...color, a: alpha };
2357
+ }
2358
+ if (cx >= plotLeft && cx <= plotRight && cy >= plotTop && cy <= plotBottom) {
2359
+ canvas.drawChar(cx, cy, shape, color);
2360
+ }
2361
+ }
2362
+ }
2197
2363
  function renderGeom(data, geom, aes, scales, canvas, coordType) {
2198
2364
  switch (geom.type) {
2199
2365
  case "point":
@@ -2243,6 +2409,10 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
2243
2409
  case "violin":
2244
2410
  renderGeomViolin(data, geom, aes, scales, canvas);
2245
2411
  break;
2412
+ case "ridgeline":
2413
+ case "joy":
2414
+ renderGeomRidgeline(data, geom, aes, scales, canvas);
2415
+ break;
2246
2416
  case "tile":
2247
2417
  case "raster":
2248
2418
  case "bin2d":
@@ -2271,6 +2441,10 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
2271
2441
  case "smooth":
2272
2442
  renderGeomSmooth(data, geom, aes, scales, canvas);
2273
2443
  break;
2444
+ case "beeswarm":
2445
+ case "quasirandom":
2446
+ renderGeomBeeswarm(data, geom, aes, scales, canvas);
2447
+ break;
2274
2448
  default:
2275
2449
  break;
2276
2450
  }
@@ -3351,6 +3525,270 @@ function stat_ydensity(params = {}) {
3351
3525
  }
3352
3526
  };
3353
3527
  }
3528
+ function stat_xdensity(params = {}) {
3529
+ return {
3530
+ type: "xdensity",
3531
+ compute(data, aes) {
3532
+ const groups = new Map;
3533
+ const groupOrder = [];
3534
+ for (const row of data) {
3535
+ const groupKey = String(row[aes.y] ?? "default");
3536
+ const xVal = row[aes.x];
3537
+ if (xVal === null || xVal === undefined)
3538
+ continue;
3539
+ const numX = Number(xVal);
3540
+ if (isNaN(numX))
3541
+ continue;
3542
+ if (!groups.has(groupKey)) {
3543
+ groups.set(groupKey, []);
3544
+ groupOrder.push(groupKey);
3545
+ }
3546
+ groups.get(groupKey).push(numX);
3547
+ }
3548
+ const result = [];
3549
+ let groupIndex = 0;
3550
+ for (const groupKey of groupOrder) {
3551
+ const xValues = groups.get(groupKey);
3552
+ if (xValues.length < 2) {
3553
+ groupIndex++;
3554
+ continue;
3555
+ }
3556
+ const tempData = xValues.map((v) => ({ x: v }));
3557
+ const densityResult = computeDensity(tempData, "x", params);
3558
+ for (const d of densityResult) {
3559
+ result.push({
3560
+ x: d.x,
3561
+ y: groupKey,
3562
+ yIndex: groupIndex,
3563
+ density: d.density,
3564
+ scaled: d.scaled,
3565
+ height: d.scaled
3566
+ });
3567
+ }
3568
+ groupIndex++;
3569
+ }
3570
+ return result;
3571
+ }
3572
+ };
3573
+ }
3574
+
3575
+ // src/stats/beeswarm.ts
3576
+ function swarmArrange(yValues, params) {
3577
+ const n = yValues.length;
3578
+ if (n === 0)
3579
+ return { offsets: [], indices: [] };
3580
+ const cex = params.cex ?? 1;
3581
+ const spacing = params.spacing ?? 1;
3582
+ const side = params.side ?? 0;
3583
+ const yRange = Math.max(...yValues) - Math.min(...yValues);
3584
+ const pointSize = yRange / Math.max(n, 10) * cex * spacing;
3585
+ let indices = yValues.map((_, i) => i);
3586
+ const priority = params.priority ?? "ascending";
3587
+ switch (priority) {
3588
+ case "ascending":
3589
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3590
+ break;
3591
+ case "descending":
3592
+ indices.sort((a, b) => yValues[b] - yValues[a]);
3593
+ break;
3594
+ case "random":
3595
+ for (let i = indices.length - 1;i > 0; i--) {
3596
+ const j = Math.floor(Math.random() * (i + 1));
3597
+ [indices[i], indices[j]] = [indices[j], indices[i]];
3598
+ }
3599
+ break;
3600
+ case "density":
3601
+ const median = yValues.slice().sort((a, b) => a - b)[Math.floor(n / 2)];
3602
+ indices.sort((a, b) => Math.abs(yValues[a] - median) - Math.abs(yValues[b] - median));
3603
+ break;
3604
+ }
3605
+ const placed = [];
3606
+ const offsets = new Array(n).fill(0);
3607
+ for (const idx of indices) {
3608
+ const y = yValues[idx];
3609
+ let bestOffset = 0;
3610
+ if (placed.length > 0) {
3611
+ const nearby = placed.filter((p) => Math.abs(p.y - y) < pointSize * 2);
3612
+ if (nearby.length > 0) {
3613
+ const maxOffset = nearby.length * pointSize;
3614
+ let foundSpot = false;
3615
+ for (let tryOffset = 0;tryOffset <= maxOffset && !foundSpot; tryOffset += pointSize * 0.5) {
3616
+ if (side >= 0) {
3617
+ let collision = false;
3618
+ for (const p of nearby) {
3619
+ const dx = tryOffset - p.x;
3620
+ const dy = y - p.y;
3621
+ const dist = Math.sqrt(dx * dx + dy * dy);
3622
+ if (dist < pointSize) {
3623
+ collision = true;
3624
+ break;
3625
+ }
3626
+ }
3627
+ if (!collision) {
3628
+ bestOffset = tryOffset;
3629
+ foundSpot = true;
3630
+ break;
3631
+ }
3632
+ }
3633
+ if (side <= 0 && tryOffset > 0 && !foundSpot) {
3634
+ let collision = false;
3635
+ for (const p of nearby) {
3636
+ const dx = -tryOffset - p.x;
3637
+ const dy = y - p.y;
3638
+ const dist = Math.sqrt(dx * dx + dy * dy);
3639
+ if (dist < pointSize) {
3640
+ collision = true;
3641
+ break;
3642
+ }
3643
+ }
3644
+ if (!collision) {
3645
+ bestOffset = -tryOffset;
3646
+ foundSpot = true;
3647
+ break;
3648
+ }
3649
+ }
3650
+ }
3651
+ }
3652
+ }
3653
+ offsets[idx] = bestOffset;
3654
+ placed.push({ y, x: bestOffset });
3655
+ }
3656
+ const maxAbsOffset = Math.max(...offsets.map(Math.abs), 0.001);
3657
+ const dodge = params.dodge ?? 0.8;
3658
+ const scale = dodge * 0.5 / maxAbsOffset;
3659
+ for (let i = 0;i < offsets.length; i++) {
3660
+ offsets[i] *= scale;
3661
+ }
3662
+ return { offsets, indices };
3663
+ }
3664
+ function centerArrange(yValues, params) {
3665
+ const n = yValues.length;
3666
+ if (n === 0)
3667
+ return { offsets: [], indices: [] };
3668
+ const dodge = params.dodge ?? 0.8;
3669
+ const side = params.side ?? 0;
3670
+ const indices = yValues.map((_, i) => i);
3671
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3672
+ const offsets = new Array(n).fill(0);
3673
+ const maxOffset = dodge * 0.4;
3674
+ for (let i = 0;i < indices.length; i++) {
3675
+ const idx = indices[i];
3676
+ const layer = Math.floor(i / 2) + 1;
3677
+ const offset = layer / Math.ceil(n / 2) * maxOffset;
3678
+ if (side === 0) {
3679
+ offsets[idx] = i % 2 === 0 ? offset : -offset;
3680
+ } else {
3681
+ offsets[idx] = side * offset;
3682
+ }
3683
+ }
3684
+ return { offsets, indices };
3685
+ }
3686
+ function squareArrange(yValues, params) {
3687
+ const n = yValues.length;
3688
+ if (n === 0)
3689
+ return { offsets: [], indices: [] };
3690
+ const dodge = params.dodge ?? 0.8;
3691
+ const side = params.side ?? 0;
3692
+ const indices = yValues.map((_, i) => i);
3693
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3694
+ const offsets = new Array(n).fill(0);
3695
+ const yRange = Math.max(...yValues) - Math.min(...yValues);
3696
+ const binSize = yRange / Math.max(Math.sqrt(n), 3);
3697
+ const bins = new Map;
3698
+ for (let i = 0;i < indices.length; i++) {
3699
+ const idx = indices[i];
3700
+ const y = yValues[idx];
3701
+ const binKey = Math.floor(y / binSize);
3702
+ if (!bins.has(binKey)) {
3703
+ bins.set(binKey, []);
3704
+ }
3705
+ bins.get(binKey).push(idx);
3706
+ }
3707
+ for (const binIndices of bins.values()) {
3708
+ const binN = binIndices.length;
3709
+ const maxOffset = dodge * 0.4;
3710
+ for (let i = 0;i < binN; i++) {
3711
+ const idx = binIndices[i];
3712
+ const offset = (i - (binN - 1) / 2) * (maxOffset * 2 / Math.max(binN - 1, 1));
3713
+ if (side === 0) {
3714
+ offsets[idx] = offset;
3715
+ } else if (side > 0) {
3716
+ offsets[idx] = Math.abs(offset);
3717
+ } else {
3718
+ offsets[idx] = -Math.abs(offset);
3719
+ }
3720
+ }
3721
+ }
3722
+ return { offsets, indices };
3723
+ }
3724
+ function computeBeeswarm(data, _xField, yField, groupKey, groupIndex, params = {}) {
3725
+ const yValues = [];
3726
+ const originalRows = [];
3727
+ for (const row of data) {
3728
+ const yVal = row[yField];
3729
+ if (yVal === null || yVal === undefined)
3730
+ continue;
3731
+ const numY = Number(yVal);
3732
+ if (isNaN(numY))
3733
+ continue;
3734
+ yValues.push(numY);
3735
+ originalRows.push(row);
3736
+ }
3737
+ if (yValues.length === 0)
3738
+ return [];
3739
+ const method = params.method ?? "swarm";
3740
+ let result;
3741
+ switch (method) {
3742
+ case "center":
3743
+ result = centerArrange(yValues, params);
3744
+ break;
3745
+ case "square":
3746
+ result = squareArrange(yValues, params);
3747
+ break;
3748
+ case "swarm":
3749
+ default:
3750
+ result = swarmArrange(yValues, params);
3751
+ }
3752
+ const output = [];
3753
+ for (let i = 0;i < yValues.length; i++) {
3754
+ const originalRow = originalRows[i];
3755
+ output.push({
3756
+ x: groupIndex + result.offsets[i],
3757
+ y: yValues[i],
3758
+ xOriginal: groupKey,
3759
+ yOriginal: yValues[i],
3760
+ xOffset: result.offsets[i],
3761
+ ...originalRow
3762
+ });
3763
+ }
3764
+ return output;
3765
+ }
3766
+ function stat_beeswarm(params = {}) {
3767
+ return {
3768
+ type: "beeswarm",
3769
+ compute(data, aes) {
3770
+ const groups = new Map;
3771
+ const groupOrder = [];
3772
+ for (const row of data) {
3773
+ const groupKey = String(row[aes.x] ?? "default");
3774
+ if (!groups.has(groupKey)) {
3775
+ groups.set(groupKey, []);
3776
+ groupOrder.push(groupKey);
3777
+ }
3778
+ groups.get(groupKey).push(row);
3779
+ }
3780
+ const result = [];
3781
+ let groupIndex = 0;
3782
+ for (const groupKey of groupOrder) {
3783
+ const groupData = groups.get(groupKey);
3784
+ const swarmResult = computeBeeswarm(groupData, aes.x, aes.y, groupKey, groupIndex, params);
3785
+ result.push(...swarmResult);
3786
+ groupIndex++;
3787
+ }
3788
+ return result;
3789
+ }
3790
+ };
3791
+ }
3354
3792
 
3355
3793
  // src/stats/smooth.ts
3356
3794
  function linearRegression(xs, ys) {
@@ -4223,6 +4661,23 @@ function applyStatTransform(data, geom, aes) {
4223
4661
  adjust: geom.params.adjust
4224
4662
  });
4225
4663
  return ydensityStat.compute(data, aes);
4664
+ } else if (geom.stat === "xdensity") {
4665
+ const xdensityStat = stat_xdensity({
4666
+ bw: geom.params.bw,
4667
+ kernel: geom.params.kernel,
4668
+ n: geom.params.n,
4669
+ adjust: geom.params.adjust
4670
+ });
4671
+ return xdensityStat.compute(data, aes);
4672
+ } else if (geom.stat === "beeswarm") {
4673
+ const beeswarmStat = stat_beeswarm({
4674
+ method: geom.params.method,
4675
+ cex: geom.params.cex,
4676
+ side: geom.params.side,
4677
+ priority: geom.params.priority,
4678
+ dodge: geom.params.dodge
4679
+ });
4680
+ return beeswarmStat.compute(data, aes);
4226
4681
  } else if (geom.stat === "smooth") {
4227
4682
  const smoothStat = stat_smooth({
4228
4683
  method: geom.params.method,
@@ -4345,6 +4800,14 @@ function renderToCanvas(spec, options) {
4345
4800
  scaleData = applyStatTransform(spec.data, geom, spec.aes);
4346
4801
  scaleAes = { ...spec.aes, x: "x", y: "y" };
4347
4802
  break;
4803
+ } else if (geom.stat === "xdensity") {
4804
+ scaleData = applyStatTransform(spec.data, geom, spec.aes);
4805
+ scaleAes = { ...spec.aes, x: "x", y: "y" };
4806
+ break;
4807
+ } else if (geom.stat === "beeswarm") {
4808
+ scaleData = spec.data;
4809
+ scaleAes = spec.aes;
4810
+ break;
4348
4811
  }
4349
4812
  }
4350
4813
  scaleData = applyCoordTransform(scaleData, scaleAes, spec.coord);
@@ -4383,6 +4846,10 @@ function renderToCanvas(spec, options) {
4383
4846
  geomAes = { ...spec.aes, x: "x", y: "y", fill: "fill" };
4384
4847
  } else if (geom.stat === "density_2d") {
4385
4848
  geomAes = { ...spec.aes, x: "x", y: "y" };
4849
+ } else if (geom.stat === "xdensity") {
4850
+ geomAes = { ...spec.aes, x: "x", y: "y" };
4851
+ } else if (geom.stat === "beeswarm") {
4852
+ geomAes = { ...spec.aes, x: "x", y: "y" };
4386
4853
  }
4387
4854
  geomData = applyCoordTransform(geomData, geomAes, spec.coord);
4388
4855
  }
@@ -5305,8 +5772,55 @@ function geom_qq_line(options = {}) {
5305
5772
  };
5306
5773
  }
5307
5774
 
5775
+ // src/geoms/ridgeline.ts
5776
+ function geom_ridgeline(options = {}) {
5777
+ return {
5778
+ type: "ridgeline",
5779
+ stat: "xdensity",
5780
+ position: "identity",
5781
+ params: {
5782
+ scale: options.scale ?? 0.9,
5783
+ alpha: options.alpha ?? 0.8,
5784
+ fill: options.fill,
5785
+ color: options.color,
5786
+ adjust: options.adjust ?? 1,
5787
+ n: options.n ?? 128,
5788
+ outline: options.outline ?? true
5789
+ }
5790
+ };
5791
+ }
5792
+ var geom_joy;
5793
+ var init_ridgeline = __esm(() => {
5794
+ geom_joy = geom_ridgeline;
5795
+ });
5796
+
5797
+ // src/geoms/beeswarm.ts
5798
+ function geom_beeswarm(options = {}) {
5799
+ return {
5800
+ type: "beeswarm",
5801
+ stat: "beeswarm",
5802
+ position: "identity",
5803
+ params: {
5804
+ method: options.method ?? "swarm",
5805
+ size: options.size ?? 1,
5806
+ cex: options.cex ?? 1,
5807
+ alpha: options.alpha ?? 1,
5808
+ color: options.color,
5809
+ shape: options.shape ?? "circle",
5810
+ side: options.side ?? 0,
5811
+ priority: options.priority ?? "ascending",
5812
+ dodge: options.dodge ?? 0.8
5813
+ }
5814
+ };
5815
+ }
5816
+ function geom_quasirandom(options = {}) {
5817
+ return geom_beeswarm({ ...options, method: "center" });
5818
+ }
5819
+
5308
5820
  // src/geoms/index.ts
5309
- var init_geoms = () => {};
5821
+ var init_geoms = __esm(() => {
5822
+ init_ridgeline();
5823
+ });
5310
5824
 
5311
5825
  // src/stats/index.ts
5312
5826
  var init_stats = __esm(() => {
@@ -9711,6 +10225,7 @@ __export(exports_src, {
9711
10225
  stat_boxplot: () => stat_boxplot,
9712
10226
  stat_bin2d: () => stat_bin2d,
9713
10227
  stat_bin: () => stat_bin,
10228
+ stat_beeswarm: () => stat_beeswarm,
9714
10229
  startREPL: () => startREPL,
9715
10230
  selectRenderer: () => selectRenderer,
9716
10231
  selectColorMode: () => selectColorMode,
@@ -9815,9 +10330,11 @@ __export(exports_src, {
9815
10330
  geom_smooth: () => geom_smooth,
9816
10331
  geom_segment: () => geom_segment,
9817
10332
  geom_rug: () => geom_rug,
10333
+ geom_ridgeline: () => geom_ridgeline,
9818
10334
  geom_ribbon: () => geom_ribbon,
9819
10335
  geom_rect: () => geom_rect,
9820
10336
  geom_raster: () => geom_raster,
10337
+ geom_quasirandom: () => geom_quasirandom,
9821
10338
  geom_qq_line: () => geom_qq_line,
9822
10339
  geom_qq: () => geom_qq,
9823
10340
  geom_pointrange: () => geom_pointrange,
@@ -9826,6 +10343,7 @@ __export(exports_src, {
9826
10343
  geom_linerange: () => geom_linerange,
9827
10344
  geom_line: () => geom_line,
9828
10345
  geom_label: () => geom_label,
10346
+ geom_joy: () => geom_joy,
9829
10347
  geom_hline: () => geom_hline,
9830
10348
  geom_histogram: () => geom_histogram,
9831
10349
  geom_freqpoly: () => geom_freqpoly,
@@ -9839,6 +10357,7 @@ __export(exports_src, {
9839
10357
  geom_col: () => geom_col,
9840
10358
  geom_boxplot: () => geom_boxplot,
9841
10359
  geom_bin2d: () => geom_bin2d,
10360
+ geom_beeswarm: () => geom_beeswarm,
9842
10361
  geom_bar: () => geom_bar,
9843
10362
  geom_area: () => geom_area,
9844
10363
  geom_abline: () => geom_abline,
@@ -9885,6 +10404,7 @@ __export(exports_src, {
9885
10404
  computeBoxplotStats: () => computeBoxplotStats,
9886
10405
  computeBins2d: () => computeBins2d,
9887
10406
  computeBins: () => computeBins,
10407
+ computeBeeswarm: () => computeBeeswarm,
9888
10408
  colorDistance: () => colorDistance,
9889
10409
  clearCapabilityCache: () => clearCapabilityCache,
9890
10410
  calculatePanelLayouts: () => calculatePanelLayouts,
@@ -9957,6 +10477,7 @@ export {
9957
10477
  stat_boxplot,
9958
10478
  stat_bin2d,
9959
10479
  stat_bin,
10480
+ stat_beeswarm,
9960
10481
  startREPL,
9961
10482
  selectRenderer,
9962
10483
  selectColorMode,
@@ -10061,9 +10582,11 @@ export {
10061
10582
  geom_smooth,
10062
10583
  geom_segment,
10063
10584
  geom_rug,
10585
+ geom_ridgeline,
10064
10586
  geom_ribbon,
10065
10587
  geom_rect,
10066
10588
  geom_raster,
10589
+ geom_quasirandom,
10067
10590
  geom_qq_line,
10068
10591
  geom_qq,
10069
10592
  geom_pointrange,
@@ -10072,6 +10595,7 @@ export {
10072
10595
  geom_linerange,
10073
10596
  geom_line,
10074
10597
  geom_label,
10598
+ geom_joy,
10075
10599
  geom_hline,
10076
10600
  geom_histogram,
10077
10601
  geom_freqpoly,
@@ -10085,6 +10609,7 @@ export {
10085
10609
  geom_col,
10086
10610
  geom_boxplot,
10087
10611
  geom_bin2d,
10612
+ geom_beeswarm,
10088
10613
  geom_bar,
10089
10614
  geom_area,
10090
10615
  geom_abline,
@@ -10131,6 +10656,7 @@ export {
10131
10656
  computeBoxplotStats,
10132
10657
  computeBins2d,
10133
10658
  computeBins,
10659
+ computeBeeswarm,
10134
10660
  colorDistance,
10135
10661
  clearCapabilityCache,
10136
10662
  calculatePanelLayouts,
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAA6C,QAAQ,EAAE,aAAa,EAAQ,MAAM,UAAU,CAAA;AACxG,OAAO,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAA;AAkB/D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,QAAQ,EAAE;QACR,CAAC,EAAE,MAAM,CAAA;QACT,CAAC,EAAE,MAAM,CAAA;QACT,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,UAAU,CAAC,EAAE;QACX,CAAC,EAAE,MAAM,CAAA;QACT,CAAC,EAAE,MAAM,CAAA;QACT,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,UAAU,CAgEZ;AAqID;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,cAAc,CA8LhB;AAwcD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,MAAM,CASR"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAA6C,QAAQ,EAAE,aAAa,EAAQ,MAAM,UAAU,CAAA;AACxG,OAAO,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAA;AAmB/D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,QAAQ,EAAE;QACR,CAAC,EAAE,MAAM,CAAA;QACT,CAAC,EAAE,MAAM,CAAA;QACT,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,UAAU,CAAC,EAAE;QACX,CAAC,EAAE,MAAM,CAAA;QACT,CAAC,EAAE,MAAM,CAAA;QACT,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,UAAU,CAgEZ;AAwJD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,cAAc,CA+MhB;AAwcD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,GACrB,MAAM,CASR"}
@@ -81,6 +81,12 @@ export declare function renderGeomSegment(data: DataSource, geom: Geom, aes: Aes
81
81
  * Data should be density curves grouped by x
82
82
  */
83
83
  export declare function renderGeomViolin(data: DataSource, geom: Geom, _aes: AestheticMapping, scales: ScaleContext, canvas: TerminalCanvas): void;
84
+ /**
85
+ * Render geom_ridgeline (joy plot)
86
+ * Creates stacked density plots for comparing distributions across groups.
87
+ * Data should be density curves grouped by y (categorical)
88
+ */
89
+ export declare function renderGeomRidgeline(data: DataSource, geom: Geom, aes: AestheticMapping, scales: ScaleContext, canvas: TerminalCanvas): void;
84
90
  /**
85
91
  * Render geom_tile (heatmap)
86
92
  */
@@ -114,6 +120,11 @@ export declare function renderGeomPointrange(data: DataSource, geom: Geom, aes:
114
120
  * Render geom_smooth (smoothed line with optional confidence band)
115
121
  */
116
122
  export declare function renderGeomSmooth(data: DataSource, geom: Geom, _aes: AestheticMapping, scales: ScaleContext, canvas: TerminalCanvas): void;
123
+ /**
124
+ * Render geom_beeswarm (beeswarm/quasirandom points)
125
+ * Data should be pre-transformed by stat_beeswarm with x containing offset positions
126
+ */
127
+ export declare function renderGeomBeeswarm(data: DataSource, geom: Geom, aes: AestheticMapping, scales: ScaleContext, canvas: TerminalCanvas): void;
117
128
  /**
118
129
  * Geometry renderer dispatch
119
130
  */