@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/cli-plot.js CHANGED
@@ -1806,6 +1806,120 @@ function renderGeomViolin(data, geom, _aes, scales, canvas) {
1806
1806
  }
1807
1807
  }
1808
1808
  }
1809
+ function renderGeomRidgeline(data, geom, aes, scales, canvas) {
1810
+ const scaleFactor = geom.params.scale ?? 0.9;
1811
+ const alpha = geom.params.alpha ?? 0.8;
1812
+ const showOutline = geom.params.outline ?? true;
1813
+ const fixedFill = geom.params.fill;
1814
+ const fixedColor = geom.params.color;
1815
+ const plotLeft = Math.round(scales.x.range[0]);
1816
+ const plotRight = Math.round(scales.x.range[1]);
1817
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
1818
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
1819
+ const groups = new Map;
1820
+ const groupKeys = [];
1821
+ for (const row of data) {
1822
+ const key = String(row.y ?? "default");
1823
+ if (!groups.has(key)) {
1824
+ groups.set(key, { data: [], index: Number(row.yIndex) ?? groupKeys.length });
1825
+ groupKeys.push(key);
1826
+ }
1827
+ groups.get(key).data.push(row);
1828
+ }
1829
+ const numGroups = groupKeys.length;
1830
+ if (numGroups === 0)
1831
+ return;
1832
+ const plotHeight = Math.abs(plotBottom - plotTop);
1833
+ const ridgeBaseHeight = plotHeight / numGroups;
1834
+ const defaultColors = [
1835
+ { r: 79, g: 169, b: 238, a: 1 },
1836
+ { r: 238, g: 136, b: 102, a: 1 },
1837
+ { r: 102, g: 204, b: 153, a: 1 },
1838
+ { r: 204, g: 102, b: 204, a: 1 },
1839
+ { r: 255, g: 200, b: 87, a: 1 },
1840
+ { r: 138, g: 201, b: 222, a: 1 },
1841
+ { r: 255, g: 153, b: 153, a: 1 },
1842
+ { r: 170, g: 170, b: 170, a: 1 }
1843
+ ];
1844
+ let parsedFillColor = null;
1845
+ if (fixedFill) {
1846
+ parsedFillColor = parseColorToRgba(fixedFill);
1847
+ }
1848
+ const sortedKeys = [...groupKeys].sort((a, b) => {
1849
+ const idxA = groups.get(a)?.index ?? 0;
1850
+ const idxB = groups.get(b)?.index ?? 0;
1851
+ return idxB - idxA;
1852
+ });
1853
+ for (const groupKey of sortedKeys) {
1854
+ const group = groups.get(groupKey);
1855
+ if (!group)
1856
+ continue;
1857
+ const groupIndex = group.index;
1858
+ const groupData = group.data;
1859
+ const sorted = [...groupData].sort((a, b) => {
1860
+ const ax = Number(a.x) || 0;
1861
+ const bx = Number(b.x) || 0;
1862
+ return ax - bx;
1863
+ });
1864
+ if (sorted.length < 2)
1865
+ continue;
1866
+ const baseline = plotTop + (groupIndex + 0.5) * ridgeBaseHeight;
1867
+ let fillColor;
1868
+ if (parsedFillColor) {
1869
+ fillColor = parsedFillColor;
1870
+ } else if ((aes.fill || aes.color) && scales.color) {
1871
+ const mappedColor = scales.color.map(groupKey);
1872
+ if (typeof mappedColor === "object" && "r" in mappedColor) {
1873
+ fillColor = mappedColor;
1874
+ } else {
1875
+ fillColor = defaultColors[groupIndex % defaultColors.length];
1876
+ }
1877
+ } else {
1878
+ fillColor = defaultColors[groupIndex % defaultColors.length];
1879
+ }
1880
+ const alphaFillColor = {
1881
+ r: Math.round(fillColor.r * alpha + 255 * (1 - alpha) * 0.1),
1882
+ g: Math.round(fillColor.g * alpha + 255 * (1 - alpha) * 0.1),
1883
+ b: Math.round(fillColor.b * alpha + 255 * (1 - alpha) * 0.1),
1884
+ a: 1
1885
+ };
1886
+ const maxRidgeHeight = ridgeBaseHeight * scaleFactor * 1.5;
1887
+ const outlinePoints = [];
1888
+ for (const row of sorted) {
1889
+ const xVal = Number(row.x);
1890
+ const scaled = Number(row.scaled) || 0;
1891
+ const px = Math.round(scales.x.map(xVal));
1892
+ const ridgeHeight = scaled * maxRidgeHeight;
1893
+ const py = Math.round(baseline - ridgeHeight);
1894
+ if (px < plotLeft || px > plotRight)
1895
+ continue;
1896
+ const fillTop = Math.max(plotTop, py);
1897
+ const fillBottom = Math.min(plotBottom, Math.round(baseline));
1898
+ for (let y = fillTop;y <= fillBottom; y++) {
1899
+ canvas.drawChar(px, y, "█", alphaFillColor);
1900
+ }
1901
+ if (showOutline) {
1902
+ outlinePoints.push({ x: px, y: Math.max(plotTop, py) });
1903
+ }
1904
+ }
1905
+ if (showOutline && outlinePoints.length > 1) {
1906
+ 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 };
1907
+ for (let i = 0;i < outlinePoints.length - 1; i++) {
1908
+ const p1 = outlinePoints[i];
1909
+ const p2 = outlinePoints[i + 1];
1910
+ if (Math.abs(p2.x - p1.x) <= 1) {
1911
+ if (p1.y >= plotTop && p1.y <= plotBottom) {
1912
+ canvas.drawChar(p1.x, p1.y, "▄", outlineColor);
1913
+ }
1914
+ }
1915
+ }
1916
+ const last = outlinePoints[outlinePoints.length - 1];
1917
+ if (last.y >= plotTop && last.y <= plotBottom) {
1918
+ canvas.drawChar(last.x, last.y, "▄", outlineColor);
1919
+ }
1920
+ }
1921
+ }
1922
+ }
1809
1923
  function renderGeomTile(data, geom, aes, scales, canvas) {
1810
1924
  const alpha = geom.params.alpha ?? 1;
1811
1925
  const plotLeft = Math.round(scales.x.range[0]);
@@ -2195,6 +2309,58 @@ function parseColor(color) {
2195
2309
  }
2196
2310
  return { r: 128, g: 128, b: 128, a: 1 };
2197
2311
  }
2312
+ function renderGeomBeeswarm(data, geom, aes, scales, canvas) {
2313
+ const alpha = geom.params.alpha ?? 1;
2314
+ const fixedColor = geom.params.color;
2315
+ const shape = getPointShape(geom.params.shape);
2316
+ const plotLeft = Math.round(scales.x.range[0]);
2317
+ const plotRight = Math.round(scales.x.range[1]);
2318
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2319
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2320
+ const defaultColors = [
2321
+ { r: 79, g: 169, b: 238, a: 1 },
2322
+ { r: 238, g: 136, b: 102, a: 1 },
2323
+ { r: 102, g: 204, b: 153, a: 1 },
2324
+ { r: 204, g: 102, b: 204, a: 1 },
2325
+ { r: 255, g: 200, b: 87, a: 1 },
2326
+ { r: 138, g: 201, b: 222, a: 1 },
2327
+ { r: 255, g: 153, b: 153, a: 1 },
2328
+ { r: 170, g: 170, b: 170, a: 1 }
2329
+ ];
2330
+ const categories = new Set;
2331
+ for (const row of data) {
2332
+ categories.add(String(row.xOriginal ?? row[aes.x] ?? "default"));
2333
+ }
2334
+ const categoryList = [...categories];
2335
+ for (const row of data) {
2336
+ const xVal = row.x;
2337
+ const yVal = row.y;
2338
+ if (xVal === null || xVal === undefined || yVal === null || yVal === undefined) {
2339
+ continue;
2340
+ }
2341
+ const numGroups = categoryList.length;
2342
+ const xRange = plotRight - plotLeft;
2343
+ const xNormalized = (Number(xVal) + 0.5) / numGroups;
2344
+ const cx = Math.round(plotLeft + xNormalized * xRange);
2345
+ const cy = Math.round(scales.y.map(yVal));
2346
+ let color;
2347
+ if (fixedColor) {
2348
+ color = parseColorToRgba(fixedColor);
2349
+ } else if (scales.color && aes.color) {
2350
+ color = getPointColor(row, aes, scales.color);
2351
+ } else {
2352
+ const category = String(row.xOriginal ?? row[aes.x] ?? "default");
2353
+ const categoryIdx = categoryList.indexOf(category);
2354
+ color = defaultColors[categoryIdx % defaultColors.length];
2355
+ }
2356
+ if (alpha < 1) {
2357
+ color = { ...color, a: alpha };
2358
+ }
2359
+ if (cx >= plotLeft && cx <= plotRight && cy >= plotTop && cy <= plotBottom) {
2360
+ canvas.drawChar(cx, cy, shape, color);
2361
+ }
2362
+ }
2363
+ }
2198
2364
  function renderGeom(data, geom, aes, scales, canvas, coordType) {
2199
2365
  switch (geom.type) {
2200
2366
  case "point":
@@ -2244,6 +2410,10 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
2244
2410
  case "violin":
2245
2411
  renderGeomViolin(data, geom, aes, scales, canvas);
2246
2412
  break;
2413
+ case "ridgeline":
2414
+ case "joy":
2415
+ renderGeomRidgeline(data, geom, aes, scales, canvas);
2416
+ break;
2247
2417
  case "tile":
2248
2418
  case "raster":
2249
2419
  case "bin2d":
@@ -2272,6 +2442,10 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
2272
2442
  case "smooth":
2273
2443
  renderGeomSmooth(data, geom, aes, scales, canvas);
2274
2444
  break;
2445
+ case "beeswarm":
2446
+ case "quasirandom":
2447
+ renderGeomBeeswarm(data, geom, aes, scales, canvas);
2448
+ break;
2275
2449
  default:
2276
2450
  break;
2277
2451
  }
@@ -3352,6 +3526,270 @@ function stat_ydensity(params = {}) {
3352
3526
  }
3353
3527
  };
3354
3528
  }
3529
+ function stat_xdensity(params = {}) {
3530
+ return {
3531
+ type: "xdensity",
3532
+ compute(data, aes) {
3533
+ const groups = new Map;
3534
+ const groupOrder = [];
3535
+ for (const row of data) {
3536
+ const groupKey = String(row[aes.y] ?? "default");
3537
+ const xVal = row[aes.x];
3538
+ if (xVal === null || xVal === undefined)
3539
+ continue;
3540
+ const numX = Number(xVal);
3541
+ if (isNaN(numX))
3542
+ continue;
3543
+ if (!groups.has(groupKey)) {
3544
+ groups.set(groupKey, []);
3545
+ groupOrder.push(groupKey);
3546
+ }
3547
+ groups.get(groupKey).push(numX);
3548
+ }
3549
+ const result = [];
3550
+ let groupIndex = 0;
3551
+ for (const groupKey of groupOrder) {
3552
+ const xValues = groups.get(groupKey);
3553
+ if (xValues.length < 2) {
3554
+ groupIndex++;
3555
+ continue;
3556
+ }
3557
+ const tempData = xValues.map((v) => ({ x: v }));
3558
+ const densityResult = computeDensity(tempData, "x", params);
3559
+ for (const d of densityResult) {
3560
+ result.push({
3561
+ x: d.x,
3562
+ y: groupKey,
3563
+ yIndex: groupIndex,
3564
+ density: d.density,
3565
+ scaled: d.scaled,
3566
+ height: d.scaled
3567
+ });
3568
+ }
3569
+ groupIndex++;
3570
+ }
3571
+ return result;
3572
+ }
3573
+ };
3574
+ }
3575
+
3576
+ // src/stats/beeswarm.ts
3577
+ function swarmArrange(yValues, params) {
3578
+ const n = yValues.length;
3579
+ if (n === 0)
3580
+ return { offsets: [], indices: [] };
3581
+ const cex = params.cex ?? 1;
3582
+ const spacing = params.spacing ?? 1;
3583
+ const side = params.side ?? 0;
3584
+ const yRange = Math.max(...yValues) - Math.min(...yValues);
3585
+ const pointSize = yRange / Math.max(n, 10) * cex * spacing;
3586
+ let indices = yValues.map((_, i) => i);
3587
+ const priority = params.priority ?? "ascending";
3588
+ switch (priority) {
3589
+ case "ascending":
3590
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3591
+ break;
3592
+ case "descending":
3593
+ indices.sort((a, b) => yValues[b] - yValues[a]);
3594
+ break;
3595
+ case "random":
3596
+ for (let i = indices.length - 1;i > 0; i--) {
3597
+ const j = Math.floor(Math.random() * (i + 1));
3598
+ [indices[i], indices[j]] = [indices[j], indices[i]];
3599
+ }
3600
+ break;
3601
+ case "density":
3602
+ const median = yValues.slice().sort((a, b) => a - b)[Math.floor(n / 2)];
3603
+ indices.sort((a, b) => Math.abs(yValues[a] - median) - Math.abs(yValues[b] - median));
3604
+ break;
3605
+ }
3606
+ const placed = [];
3607
+ const offsets = new Array(n).fill(0);
3608
+ for (const idx of indices) {
3609
+ const y = yValues[idx];
3610
+ let bestOffset = 0;
3611
+ if (placed.length > 0) {
3612
+ const nearby = placed.filter((p) => Math.abs(p.y - y) < pointSize * 2);
3613
+ if (nearby.length > 0) {
3614
+ const maxOffset = nearby.length * pointSize;
3615
+ let foundSpot = false;
3616
+ for (let tryOffset = 0;tryOffset <= maxOffset && !foundSpot; tryOffset += pointSize * 0.5) {
3617
+ if (side >= 0) {
3618
+ let collision = false;
3619
+ for (const p of nearby) {
3620
+ const dx = tryOffset - p.x;
3621
+ const dy = y - p.y;
3622
+ const dist = Math.sqrt(dx * dx + dy * dy);
3623
+ if (dist < pointSize) {
3624
+ collision = true;
3625
+ break;
3626
+ }
3627
+ }
3628
+ if (!collision) {
3629
+ bestOffset = tryOffset;
3630
+ foundSpot = true;
3631
+ break;
3632
+ }
3633
+ }
3634
+ if (side <= 0 && tryOffset > 0 && !foundSpot) {
3635
+ let collision = false;
3636
+ for (const p of nearby) {
3637
+ const dx = -tryOffset - p.x;
3638
+ const dy = y - p.y;
3639
+ const dist = Math.sqrt(dx * dx + dy * dy);
3640
+ if (dist < pointSize) {
3641
+ collision = true;
3642
+ break;
3643
+ }
3644
+ }
3645
+ if (!collision) {
3646
+ bestOffset = -tryOffset;
3647
+ foundSpot = true;
3648
+ break;
3649
+ }
3650
+ }
3651
+ }
3652
+ }
3653
+ }
3654
+ offsets[idx] = bestOffset;
3655
+ placed.push({ y, x: bestOffset });
3656
+ }
3657
+ const maxAbsOffset = Math.max(...offsets.map(Math.abs), 0.001);
3658
+ const dodge = params.dodge ?? 0.8;
3659
+ const scale = dodge * 0.5 / maxAbsOffset;
3660
+ for (let i = 0;i < offsets.length; i++) {
3661
+ offsets[i] *= scale;
3662
+ }
3663
+ return { offsets, indices };
3664
+ }
3665
+ function centerArrange(yValues, params) {
3666
+ const n = yValues.length;
3667
+ if (n === 0)
3668
+ return { offsets: [], indices: [] };
3669
+ const dodge = params.dodge ?? 0.8;
3670
+ const side = params.side ?? 0;
3671
+ const indices = yValues.map((_, i) => i);
3672
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3673
+ const offsets = new Array(n).fill(0);
3674
+ const maxOffset = dodge * 0.4;
3675
+ for (let i = 0;i < indices.length; i++) {
3676
+ const idx = indices[i];
3677
+ const layer = Math.floor(i / 2) + 1;
3678
+ const offset = layer / Math.ceil(n / 2) * maxOffset;
3679
+ if (side === 0) {
3680
+ offsets[idx] = i % 2 === 0 ? offset : -offset;
3681
+ } else {
3682
+ offsets[idx] = side * offset;
3683
+ }
3684
+ }
3685
+ return { offsets, indices };
3686
+ }
3687
+ function squareArrange(yValues, params) {
3688
+ const n = yValues.length;
3689
+ if (n === 0)
3690
+ return { offsets: [], indices: [] };
3691
+ const dodge = params.dodge ?? 0.8;
3692
+ const side = params.side ?? 0;
3693
+ const indices = yValues.map((_, i) => i);
3694
+ indices.sort((a, b) => yValues[a] - yValues[b]);
3695
+ const offsets = new Array(n).fill(0);
3696
+ const yRange = Math.max(...yValues) - Math.min(...yValues);
3697
+ const binSize = yRange / Math.max(Math.sqrt(n), 3);
3698
+ const bins = new Map;
3699
+ for (let i = 0;i < indices.length; i++) {
3700
+ const idx = indices[i];
3701
+ const y = yValues[idx];
3702
+ const binKey = Math.floor(y / binSize);
3703
+ if (!bins.has(binKey)) {
3704
+ bins.set(binKey, []);
3705
+ }
3706
+ bins.get(binKey).push(idx);
3707
+ }
3708
+ for (const binIndices of bins.values()) {
3709
+ const binN = binIndices.length;
3710
+ const maxOffset = dodge * 0.4;
3711
+ for (let i = 0;i < binN; i++) {
3712
+ const idx = binIndices[i];
3713
+ const offset = (i - (binN - 1) / 2) * (maxOffset * 2 / Math.max(binN - 1, 1));
3714
+ if (side === 0) {
3715
+ offsets[idx] = offset;
3716
+ } else if (side > 0) {
3717
+ offsets[idx] = Math.abs(offset);
3718
+ } else {
3719
+ offsets[idx] = -Math.abs(offset);
3720
+ }
3721
+ }
3722
+ }
3723
+ return { offsets, indices };
3724
+ }
3725
+ function computeBeeswarm(data, _xField, yField, groupKey, groupIndex, params = {}) {
3726
+ const yValues = [];
3727
+ const originalRows = [];
3728
+ for (const row of data) {
3729
+ const yVal = row[yField];
3730
+ if (yVal === null || yVal === undefined)
3731
+ continue;
3732
+ const numY = Number(yVal);
3733
+ if (isNaN(numY))
3734
+ continue;
3735
+ yValues.push(numY);
3736
+ originalRows.push(row);
3737
+ }
3738
+ if (yValues.length === 0)
3739
+ return [];
3740
+ const method = params.method ?? "swarm";
3741
+ let result;
3742
+ switch (method) {
3743
+ case "center":
3744
+ result = centerArrange(yValues, params);
3745
+ break;
3746
+ case "square":
3747
+ result = squareArrange(yValues, params);
3748
+ break;
3749
+ case "swarm":
3750
+ default:
3751
+ result = swarmArrange(yValues, params);
3752
+ }
3753
+ const output = [];
3754
+ for (let i = 0;i < yValues.length; i++) {
3755
+ const originalRow = originalRows[i];
3756
+ output.push({
3757
+ x: groupIndex + result.offsets[i],
3758
+ y: yValues[i],
3759
+ xOriginal: groupKey,
3760
+ yOriginal: yValues[i],
3761
+ xOffset: result.offsets[i],
3762
+ ...originalRow
3763
+ });
3764
+ }
3765
+ return output;
3766
+ }
3767
+ function stat_beeswarm(params = {}) {
3768
+ return {
3769
+ type: "beeswarm",
3770
+ compute(data, aes) {
3771
+ const groups = new Map;
3772
+ const groupOrder = [];
3773
+ for (const row of data) {
3774
+ const groupKey = String(row[aes.x] ?? "default");
3775
+ if (!groups.has(groupKey)) {
3776
+ groups.set(groupKey, []);
3777
+ groupOrder.push(groupKey);
3778
+ }
3779
+ groups.get(groupKey).push(row);
3780
+ }
3781
+ const result = [];
3782
+ let groupIndex = 0;
3783
+ for (const groupKey of groupOrder) {
3784
+ const groupData = groups.get(groupKey);
3785
+ const swarmResult = computeBeeswarm(groupData, aes.x, aes.y, groupKey, groupIndex, params);
3786
+ result.push(...swarmResult);
3787
+ groupIndex++;
3788
+ }
3789
+ return result;
3790
+ }
3791
+ };
3792
+ }
3355
3793
 
3356
3794
  // src/stats/smooth.ts
3357
3795
  function linearRegression(xs, ys) {
@@ -4224,6 +4662,23 @@ function applyStatTransform(data, geom, aes) {
4224
4662
  adjust: geom.params.adjust
4225
4663
  });
4226
4664
  return ydensityStat.compute(data, aes);
4665
+ } else if (geom.stat === "xdensity") {
4666
+ const xdensityStat = stat_xdensity({
4667
+ bw: geom.params.bw,
4668
+ kernel: geom.params.kernel,
4669
+ n: geom.params.n,
4670
+ adjust: geom.params.adjust
4671
+ });
4672
+ return xdensityStat.compute(data, aes);
4673
+ } else if (geom.stat === "beeswarm") {
4674
+ const beeswarmStat = stat_beeswarm({
4675
+ method: geom.params.method,
4676
+ cex: geom.params.cex,
4677
+ side: geom.params.side,
4678
+ priority: geom.params.priority,
4679
+ dodge: geom.params.dodge
4680
+ });
4681
+ return beeswarmStat.compute(data, aes);
4227
4682
  } else if (geom.stat === "smooth") {
4228
4683
  const smoothStat = stat_smooth({
4229
4684
  method: geom.params.method,
@@ -4346,6 +4801,14 @@ function renderToCanvas(spec, options) {
4346
4801
  scaleData = applyStatTransform(spec.data, geom, spec.aes);
4347
4802
  scaleAes = { ...spec.aes, x: "x", y: "y" };
4348
4803
  break;
4804
+ } else if (geom.stat === "xdensity") {
4805
+ scaleData = applyStatTransform(spec.data, geom, spec.aes);
4806
+ scaleAes = { ...spec.aes, x: "x", y: "y" };
4807
+ break;
4808
+ } else if (geom.stat === "beeswarm") {
4809
+ scaleData = spec.data;
4810
+ scaleAes = spec.aes;
4811
+ break;
4349
4812
  }
4350
4813
  }
4351
4814
  scaleData = applyCoordTransform(scaleData, scaleAes, spec.coord);
@@ -4384,6 +4847,10 @@ function renderToCanvas(spec, options) {
4384
4847
  geomAes = { ...spec.aes, x: "x", y: "y", fill: "fill" };
4385
4848
  } else if (geom.stat === "density_2d") {
4386
4849
  geomAes = { ...spec.aes, x: "x", y: "y" };
4850
+ } else if (geom.stat === "xdensity") {
4851
+ geomAes = { ...spec.aes, x: "x", y: "y" };
4852
+ } else if (geom.stat === "beeswarm") {
4853
+ geomAes = { ...spec.aes, x: "x", y: "y" };
4387
4854
  }
4388
4855
  geomData = applyCoordTransform(geomData, geomAes, spec.coord);
4389
4856
  }
@@ -5306,8 +5773,55 @@ function geom_qq_line(options = {}) {
5306
5773
  };
5307
5774
  }
5308
5775
 
5776
+ // src/geoms/ridgeline.ts
5777
+ function geom_ridgeline(options = {}) {
5778
+ return {
5779
+ type: "ridgeline",
5780
+ stat: "xdensity",
5781
+ position: "identity",
5782
+ params: {
5783
+ scale: options.scale ?? 0.9,
5784
+ alpha: options.alpha ?? 0.8,
5785
+ fill: options.fill,
5786
+ color: options.color,
5787
+ adjust: options.adjust ?? 1,
5788
+ n: options.n ?? 128,
5789
+ outline: options.outline ?? true
5790
+ }
5791
+ };
5792
+ }
5793
+ var geom_joy;
5794
+ var init_ridgeline = __esm(() => {
5795
+ geom_joy = geom_ridgeline;
5796
+ });
5797
+
5798
+ // src/geoms/beeswarm.ts
5799
+ function geom_beeswarm(options = {}) {
5800
+ return {
5801
+ type: "beeswarm",
5802
+ stat: "beeswarm",
5803
+ position: "identity",
5804
+ params: {
5805
+ method: options.method ?? "swarm",
5806
+ size: options.size ?? 1,
5807
+ cex: options.cex ?? 1,
5808
+ alpha: options.alpha ?? 1,
5809
+ color: options.color,
5810
+ shape: options.shape ?? "circle",
5811
+ side: options.side ?? 0,
5812
+ priority: options.priority ?? "ascending",
5813
+ dodge: options.dodge ?? 0.8
5814
+ }
5815
+ };
5816
+ }
5817
+ function geom_quasirandom(options = {}) {
5818
+ return geom_beeswarm({ ...options, method: "center" });
5819
+ }
5820
+
5309
5821
  // src/geoms/index.ts
5310
- var init_geoms = () => {};
5822
+ var init_geoms = __esm(() => {
5823
+ init_ridgeline();
5824
+ });
5311
5825
 
5312
5826
  // src/stats/index.ts
5313
5827
  var init_stats = __esm(() => {
@@ -9712,6 +10226,7 @@ __export(exports_src, {
9712
10226
  stat_boxplot: () => stat_boxplot,
9713
10227
  stat_bin2d: () => stat_bin2d,
9714
10228
  stat_bin: () => stat_bin,
10229
+ stat_beeswarm: () => stat_beeswarm,
9715
10230
  startREPL: () => startREPL,
9716
10231
  selectRenderer: () => selectRenderer,
9717
10232
  selectColorMode: () => selectColorMode,
@@ -9816,9 +10331,11 @@ __export(exports_src, {
9816
10331
  geom_smooth: () => geom_smooth,
9817
10332
  geom_segment: () => geom_segment,
9818
10333
  geom_rug: () => geom_rug,
10334
+ geom_ridgeline: () => geom_ridgeline,
9819
10335
  geom_ribbon: () => geom_ribbon,
9820
10336
  geom_rect: () => geom_rect,
9821
10337
  geom_raster: () => geom_raster,
10338
+ geom_quasirandom: () => geom_quasirandom,
9822
10339
  geom_qq_line: () => geom_qq_line,
9823
10340
  geom_qq: () => geom_qq,
9824
10341
  geom_pointrange: () => geom_pointrange,
@@ -9827,6 +10344,7 @@ __export(exports_src, {
9827
10344
  geom_linerange: () => geom_linerange,
9828
10345
  geom_line: () => geom_line,
9829
10346
  geom_label: () => geom_label,
10347
+ geom_joy: () => geom_joy,
9830
10348
  geom_hline: () => geom_hline,
9831
10349
  geom_histogram: () => geom_histogram,
9832
10350
  geom_freqpoly: () => geom_freqpoly,
@@ -9840,6 +10358,7 @@ __export(exports_src, {
9840
10358
  geom_col: () => geom_col,
9841
10359
  geom_boxplot: () => geom_boxplot,
9842
10360
  geom_bin2d: () => geom_bin2d,
10361
+ geom_beeswarm: () => geom_beeswarm,
9843
10362
  geom_bar: () => geom_bar,
9844
10363
  geom_area: () => geom_area,
9845
10364
  geom_abline: () => geom_abline,
@@ -9886,6 +10405,7 @@ __export(exports_src, {
9886
10405
  computeBoxplotStats: () => computeBoxplotStats,
9887
10406
  computeBins2d: () => computeBins2d,
9888
10407
  computeBins: () => computeBins,
10408
+ computeBeeswarm: () => computeBeeswarm,
9889
10409
  colorDistance: () => colorDistance,
9890
10410
  clearCapabilityCache: () => clearCapabilityCache,
9891
10411
  calculatePanelLayouts: () => calculatePanelLayouts,
@@ -10735,6 +11255,10 @@ var GEOM_TYPES = [
10735
11255
  "freqpoly",
10736
11256
  "boxplot",
10737
11257
  "violin",
11258
+ "ridgeline",
11259
+ "joy",
11260
+ "beeswarm",
11261
+ "quasirandom",
10738
11262
  "area",
10739
11263
  "ribbon",
10740
11264
  "rug",
@@ -11129,7 +11653,7 @@ Error: Unknown geometry type "${geomType}"`);
11129
11653
  Available geom types:`);
11130
11654
  console.error(` Points/Lines: point, line, path, step, smooth, segment`);
11131
11655
  console.error(` Bars/Areas: bar, col, histogram, freqpoly, area, ribbon`);
11132
- console.error(` Distributions: boxplot, violin, qq, density_2d`);
11656
+ console.error(` Distributions: boxplot, violin, ridgeline, joy, beeswarm, quasirandom, qq, density_2d`);
11133
11657
  console.error(` Uncertainty: errorbar, errorbarh, crossbar, linerange, pointrange`);
11134
11658
  console.error(` 2D: tile, rect, raster, bin2d, contour, contour_filled`);
11135
11659
  console.error(` Text: text, label`);
@@ -11274,6 +11798,14 @@ If you want a univariate plot, try: histogram, bar, qq, or freqpoly`);
11274
11798
  case "violin":
11275
11799
  plot = plot.geom(geom_violin());
11276
11800
  break;
11801
+ case "ridgeline":
11802
+ case "joy":
11803
+ plot = plot.geom(geom_ridgeline());
11804
+ break;
11805
+ case "beeswarm":
11806
+ case "quasirandom":
11807
+ plot = plot.geom(geom_beeswarm());
11808
+ break;
11277
11809
  case "bar":
11278
11810
  plot = plot.geom(geom_bar());
11279
11811
  break;