@ggterm/core 0.2.11 → 0.2.12

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.js CHANGED
@@ -2361,6 +2361,469 @@ function renderGeomBeeswarm(data, geom, aes, scales, canvas) {
2361
2361
  }
2362
2362
  }
2363
2363
  }
2364
+ function renderGeomDumbbell(data, geom, aes, scales, canvas) {
2365
+ const lineColor = parseColorToRgba(geom.params.lineColor ?? "#666666");
2366
+ const alpha = geom.params.alpha ?? 1;
2367
+ const shape = getPointShape(geom.params.shape);
2368
+ const plotLeft = Math.round(scales.x.range[0]);
2369
+ const plotRight = Math.round(scales.x.range[1]);
2370
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2371
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2372
+ const defaultColors = [
2373
+ { r: 79, g: 169, b: 238, a: 1 },
2374
+ { r: 238, g: 136, b: 102, a: 1 },
2375
+ { r: 102, g: 204, b: 153, a: 1 },
2376
+ { r: 204, g: 102, b: 204, a: 1 }
2377
+ ];
2378
+ for (let i = 0;i < data.length; i++) {
2379
+ const row = data[i];
2380
+ const xVal = row[aes.x];
2381
+ const xendVal = row["xend"] ?? row[aes.x];
2382
+ const yVal = row[aes.y];
2383
+ if (xVal === null || xVal === undefined || yVal === null || yVal === undefined) {
2384
+ continue;
2385
+ }
2386
+ const x1 = Math.round(scales.x.map(xVal));
2387
+ const x2 = Math.round(scales.x.map(xendVal));
2388
+ const cy = Math.round(scales.y.map(yVal));
2389
+ let startColor;
2390
+ let endColor;
2391
+ if (geom.params.color) {
2392
+ startColor = parseColorToRgba(geom.params.color);
2393
+ } else if (scales.color && aes.color) {
2394
+ startColor = getPointColor(row, aes, scales.color);
2395
+ } else {
2396
+ startColor = defaultColors[0];
2397
+ }
2398
+ if (geom.params.colorEnd) {
2399
+ endColor = parseColorToRgba(geom.params.colorEnd);
2400
+ } else {
2401
+ endColor = geom.params.color ? startColor : defaultColors[1];
2402
+ }
2403
+ if (alpha < 1) {
2404
+ startColor = { ...startColor, a: alpha };
2405
+ endColor = { ...endColor, a: alpha };
2406
+ }
2407
+ if (cy >= plotTop && cy <= plotBottom) {
2408
+ const left = Math.max(plotLeft, Math.min(x1, x2));
2409
+ const right = Math.min(plotRight, Math.max(x1, x2));
2410
+ for (let x = left;x <= right; x++) {
2411
+ canvas.drawChar(x, cy, "─", lineColor);
2412
+ }
2413
+ }
2414
+ if (x1 >= plotLeft && x1 <= plotRight && cy >= plotTop && cy <= plotBottom) {
2415
+ canvas.drawChar(x1, cy, shape, startColor);
2416
+ }
2417
+ if (x2 >= plotLeft && x2 <= plotRight && cy >= plotTop && cy <= plotBottom) {
2418
+ canvas.drawChar(x2, cy, shape, endColor);
2419
+ }
2420
+ }
2421
+ }
2422
+ function renderGeomLollipop(data, geom, aes, scales, canvas) {
2423
+ const alpha = geom.params.alpha ?? 1;
2424
+ const baseline = geom.params.baseline ?? 0;
2425
+ const direction = geom.params.direction ?? "vertical";
2426
+ const shape = getPointShape(geom.params.shape);
2427
+ const plotLeft = Math.round(scales.x.range[0]);
2428
+ const plotRight = Math.round(scales.x.range[1]);
2429
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2430
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2431
+ const defaultColors = [
2432
+ { r: 79, g: 169, b: 238, a: 1 },
2433
+ { r: 238, g: 136, b: 102, a: 1 },
2434
+ { r: 102, g: 204, b: 153, a: 1 },
2435
+ { r: 204, g: 102, b: 204, a: 1 },
2436
+ { r: 255, g: 200, b: 87, a: 1 },
2437
+ { r: 138, g: 201, b: 222, a: 1 }
2438
+ ];
2439
+ const xValues = [...new Set(data.map((row) => row[aes.x]))];
2440
+ for (let i = 0;i < data.length; i++) {
2441
+ const row = data[i];
2442
+ const xVal = row[aes.x];
2443
+ const yVal = row[aes.y];
2444
+ if (xVal === null || xVal === undefined || yVal === null || yVal === undefined) {
2445
+ continue;
2446
+ }
2447
+ const cx = Math.round(scales.x.map(xVal));
2448
+ const cy = Math.round(scales.y.map(yVal));
2449
+ let color;
2450
+ if (geom.params.color) {
2451
+ color = parseColorToRgba(geom.params.color);
2452
+ } else if (scales.color && aes.color) {
2453
+ color = getPointColor(row, aes, scales.color);
2454
+ } else {
2455
+ const categoryIdx = xValues.indexOf(xVal);
2456
+ color = defaultColors[categoryIdx % defaultColors.length];
2457
+ }
2458
+ let lineColor;
2459
+ if (geom.params.lineColor) {
2460
+ lineColor = parseColorToRgba(geom.params.lineColor);
2461
+ } else {
2462
+ lineColor = {
2463
+ r: Math.round(color.r * 0.7),
2464
+ g: Math.round(color.g * 0.7),
2465
+ b: Math.round(color.b * 0.7),
2466
+ a: color.a
2467
+ };
2468
+ }
2469
+ if (alpha < 1) {
2470
+ color = { ...color, a: alpha };
2471
+ lineColor = { ...lineColor, a: alpha };
2472
+ }
2473
+ if (direction === "vertical") {
2474
+ let baselineY = Math.round(scales.y.map(baseline));
2475
+ baselineY = Math.max(plotTop, Math.min(plotBottom, baselineY));
2476
+ if (cx >= plotLeft && cx <= plotRight) {
2477
+ const top = Math.min(cy, baselineY);
2478
+ const bottom = Math.max(cy, baselineY);
2479
+ for (let y = top;y <= bottom; y++) {
2480
+ if (y >= plotTop && y <= plotBottom) {
2481
+ canvas.drawChar(cx, y, "│", lineColor);
2482
+ }
2483
+ }
2484
+ }
2485
+ if (cx >= plotLeft && cx <= plotRight && cy >= plotTop && cy <= plotBottom) {
2486
+ canvas.drawChar(cx, cy, shape, color);
2487
+ }
2488
+ } else {
2489
+ let baselineX = Math.round(scales.x.map(baseline));
2490
+ baselineX = Math.max(plotLeft, Math.min(plotRight, baselineX));
2491
+ if (cy >= plotTop && cy <= plotBottom) {
2492
+ const left = Math.min(cx, baselineX);
2493
+ const right = Math.max(cx, baselineX);
2494
+ for (let x = left;x <= right; x++) {
2495
+ if (x >= plotLeft && x <= plotRight) {
2496
+ canvas.drawChar(x, cy, "─", lineColor);
2497
+ }
2498
+ }
2499
+ }
2500
+ if (cx >= plotLeft && cx <= plotRight && cy >= plotTop && cy <= plotBottom) {
2501
+ canvas.drawChar(cx, cy, shape, color);
2502
+ }
2503
+ }
2504
+ }
2505
+ }
2506
+ function renderGeomWaffle(data, geom, aes, scales, canvas) {
2507
+ const rows = geom.params.rows ?? 10;
2508
+ const cols = geom.params.cols ?? 10;
2509
+ const fillChar = geom.params.fill_char ?? "█";
2510
+ const emptyChar = geom.params.empty_char ?? "░";
2511
+ const showLegend = geom.params.show_legend ?? true;
2512
+ const flip = geom.params.flip ?? false;
2513
+ const gap = geom.params.gap ?? 0;
2514
+ const plotLeft = Math.round(scales.x.range[0]);
2515
+ const plotRight = Math.round(scales.x.range[1]);
2516
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2517
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2518
+ const defaultColors = [
2519
+ { r: 79, g: 169, b: 238, a: 1 },
2520
+ { r: 238, g: 136, b: 102, a: 1 },
2521
+ { r: 102, g: 204, b: 153, a: 1 },
2522
+ { r: 204, g: 102, b: 204, a: 1 },
2523
+ { r: 255, g: 200, b: 87, a: 1 },
2524
+ { r: 138, g: 201, b: 222, a: 1 },
2525
+ { r: 255, g: 153, b: 153, a: 1 },
2526
+ { r: 170, g: 170, b: 170, a: 1 }
2527
+ ];
2528
+ const fillField = aes.fill || aes.color || "category";
2529
+ const valueField = aes.y || "value";
2530
+ const categories = new Map;
2531
+ let totalValue = 0;
2532
+ for (const row of data) {
2533
+ const cat = String(row[fillField] ?? "default");
2534
+ const val = Number(row[valueField]) || 1;
2535
+ categories.set(cat, (categories.get(cat) ?? 0) + val);
2536
+ totalValue += val;
2537
+ }
2538
+ const cellsPerCategory = [];
2539
+ const categoryList = [...categories.keys()];
2540
+ let cellsAssigned = 0;
2541
+ for (let i = 0;i < categoryList.length; i++) {
2542
+ const cat = categoryList[i];
2543
+ const val = categories.get(cat);
2544
+ const proportion = val / totalValue;
2545
+ const cells = Math.round(proportion * rows * cols);
2546
+ const color = scales.color?.map(cat) ?? defaultColors[i % defaultColors.length];
2547
+ cellsPerCategory.push({ category: cat, cells, color });
2548
+ cellsAssigned += cells;
2549
+ }
2550
+ if (cellsAssigned < rows * cols && cellsPerCategory.length > 0) {
2551
+ cellsPerCategory[0].cells += rows * cols - cellsAssigned;
2552
+ }
2553
+ const grid = [];
2554
+ for (const { cells, color } of cellsPerCategory) {
2555
+ for (let i = 0;i < cells; i++) {
2556
+ grid.push({ char: fillChar, color });
2557
+ }
2558
+ }
2559
+ const emptyColor = { r: 80, g: 80, b: 80, a: 0.3 };
2560
+ while (grid.length < rows * cols) {
2561
+ grid.push({ char: emptyChar, color: emptyColor });
2562
+ }
2563
+ const availableWidth = plotRight - plotLeft - (showLegend ? 15 : 0);
2564
+ const availableHeight = plotBottom - plotTop;
2565
+ const cellWidth = Math.max(1, Math.floor(availableWidth / cols)) + gap;
2566
+ const cellHeight = Math.max(1, Math.floor(availableHeight / rows)) + gap;
2567
+ for (let row = 0;row < rows; row++) {
2568
+ for (let col = 0;col < cols; col++) {
2569
+ let idx;
2570
+ if (flip) {
2571
+ idx = row * cols + col;
2572
+ } else {
2573
+ idx = col * rows + (rows - 1 - row);
2574
+ }
2575
+ if (idx >= grid.length)
2576
+ continue;
2577
+ const cell = grid[idx];
2578
+ const x = plotLeft + col * cellWidth;
2579
+ const y = plotTop + row * cellHeight;
2580
+ if (x >= plotLeft && x < plotRight - (showLegend ? 15 : 0) && y >= plotTop && y <= plotBottom) {
2581
+ canvas.drawChar(x, y, cell.char, cell.color);
2582
+ }
2583
+ }
2584
+ }
2585
+ if (showLegend) {
2586
+ const legendX = plotRight - 12;
2587
+ let legendY = plotTop;
2588
+ for (let i = 0;i < cellsPerCategory.length && legendY < plotBottom; i++) {
2589
+ const { category, cells, color } = cellsPerCategory[i];
2590
+ const pct = Math.round(cells / (rows * cols) * 100);
2591
+ const label = `${category.slice(0, 6)} ${pct}%`;
2592
+ canvas.drawChar(legendX, legendY, "█", color);
2593
+ canvas.drawString(legendX + 2, legendY, label, { r: 180, g: 180, b: 180, a: 1 });
2594
+ legendY += 2;
2595
+ }
2596
+ }
2597
+ }
2598
+ function renderGeomSparkline(data, geom, aes, scales, canvas) {
2599
+ const sparkType = geom.params.sparkType ?? "bar";
2600
+ const width = geom.params.width ?? 20;
2601
+ const showMinmax = geom.params.show_minmax ?? false;
2602
+ const normalize = geom.params.normalize ?? true;
2603
+ const minColor = parseColorToRgba(geom.params.min_color ?? "#e74c3c");
2604
+ const maxColor = parseColorToRgba(geom.params.max_color ?? "#2ecc71");
2605
+ const plotLeft = Math.round(scales.x.range[0]);
2606
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2607
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2608
+ const SPARK_CHARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
2609
+ const defaultColor = { r: 79, g: 169, b: 238, a: 1 };
2610
+ const groupField = aes.group || aes.color;
2611
+ const groups = new Map;
2612
+ if (groupField) {
2613
+ for (const row of data) {
2614
+ const key = String(row[groupField] ?? "default");
2615
+ if (!groups.has(key))
2616
+ groups.set(key, []);
2617
+ groups.get(key).push(row);
2618
+ }
2619
+ } else {
2620
+ groups.set("default", [...data]);
2621
+ }
2622
+ let currentY = plotTop;
2623
+ for (const [groupKey, groupData] of groups) {
2624
+ const sorted = aes.x ? [...groupData].sort((a, b) => Number(a[aes.x]) - Number(b[aes.x])) : groupData;
2625
+ const values = sorted.map((row) => Number(row[aes.y]) || 0);
2626
+ if (values.length === 0)
2627
+ continue;
2628
+ const minVal = Math.min(...values);
2629
+ const maxVal = Math.max(...values);
2630
+ const minIdx = values.indexOf(minVal);
2631
+ const maxIdx = values.indexOf(maxVal);
2632
+ const range = maxVal - minVal || 1;
2633
+ const color = scales.color?.map(groupKey) ?? defaultColor;
2634
+ const sparkValues = [];
2635
+ if (values.length <= width) {
2636
+ sparkValues.push(...values);
2637
+ } else {
2638
+ for (let i = 0;i < width; i++) {
2639
+ const idx = Math.floor(i * values.length / width);
2640
+ sparkValues.push(values[idx]);
2641
+ }
2642
+ }
2643
+ for (let i = 0;i < sparkValues.length; i++) {
2644
+ const val = sparkValues[i];
2645
+ const normalized = normalize ? (val - minVal) / range : val / (maxVal || 1);
2646
+ const charIdx = Math.min(7, Math.max(0, Math.floor(normalized * 8)));
2647
+ const char = sparkType === "dot" ? "•" : SPARK_CHARS[charIdx];
2648
+ const x = plotLeft + i;
2649
+ const y = currentY;
2650
+ let pointColor = color;
2651
+ if (showMinmax) {
2652
+ const origIdx = Math.floor(i * values.length / sparkValues.length);
2653
+ if (origIdx === minIdx)
2654
+ pointColor = minColor;
2655
+ else if (origIdx === maxIdx)
2656
+ pointColor = maxColor;
2657
+ }
2658
+ if (x < plotLeft + width && y >= plotTop && y <= plotBottom) {
2659
+ canvas.drawChar(x, y, char, pointColor);
2660
+ }
2661
+ }
2662
+ if (groupField && groups.size > 1) {
2663
+ const labelX = plotLeft + width + 1;
2664
+ canvas.drawString(labelX, currentY, groupKey.slice(0, 8), { r: 180, g: 180, b: 180, a: 1 });
2665
+ }
2666
+ currentY += 2;
2667
+ }
2668
+ }
2669
+ function renderGeomBullet(data, geom, aes, scales, canvas) {
2670
+ const width = geom.params.width ?? 40;
2671
+ const targetChar = geom.params.target_char ?? "│";
2672
+ const barChar = geom.params.bar_char ?? "█";
2673
+ const rangeChars = geom.params.range_chars ?? ["░", "▒", "▓"];
2674
+ const showValues = geom.params.show_values ?? true;
2675
+ const targetColor = parseColorToRgba(geom.params.target_color ?? "#e74c3c");
2676
+ const plotLeft = Math.round(scales.x.range[0]);
2677
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2678
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2679
+ const defaultColors = [
2680
+ { r: 79, g: 169, b: 238, a: 1 },
2681
+ { r: 238, g: 136, b: 102, a: 1 },
2682
+ { r: 102, g: 204, b: 153, a: 1 }
2683
+ ];
2684
+ const rangeColors = [
2685
+ { r: 60, g: 60, b: 60, a: 1 },
2686
+ { r: 90, g: 90, b: 90, a: 1 },
2687
+ { r: 120, g: 120, b: 120, a: 1 }
2688
+ ];
2689
+ let currentY = plotTop;
2690
+ for (let i = 0;i < data.length; i++) {
2691
+ const row = data[i];
2692
+ const label = aes.x ? String(row[aes.x]).slice(0, 10) : `Item ${i + 1}`;
2693
+ const value = Number(row[aes.y]) || 0;
2694
+ const target = Number(row["target"]) || null;
2695
+ const maxValue = Number(row["max"]) || Math.max(value, target || 0) * 1.2;
2696
+ const ranges = row["ranges"] ?? [maxValue * 0.6, maxValue * 0.8, maxValue];
2697
+ const color = scales.color?.map(label) ?? defaultColors[i % defaultColors.length];
2698
+ const labelWidth = 12;
2699
+ canvas.drawString(plotLeft, currentY, label.padEnd(labelWidth), { r: 180, g: 180, b: 180, a: 1 });
2700
+ const barStart = plotLeft + labelWidth;
2701
+ const barWidth = Math.min(width, scales.x.range[1] - barStart - (showValues ? 8 : 0));
2702
+ for (let r = ranges.length - 1;r >= 0; r--) {
2703
+ const rangeWidth = Math.round(ranges[r] / maxValue * barWidth);
2704
+ for (let x = 0;x < rangeWidth; x++) {
2705
+ if (barStart + x <= scales.x.range[1]) {
2706
+ canvas.drawChar(barStart + x, currentY, rangeChars[r], rangeColors[r]);
2707
+ }
2708
+ }
2709
+ }
2710
+ const valueWidth = Math.round(value / maxValue * barWidth);
2711
+ for (let x = 0;x < valueWidth; x++) {
2712
+ if (barStart + x <= scales.x.range[1]) {
2713
+ canvas.drawChar(barStart + x, currentY, barChar, color);
2714
+ }
2715
+ }
2716
+ if (target !== null) {
2717
+ const targetX = barStart + Math.round(target / maxValue * barWidth);
2718
+ if (targetX >= barStart && targetX <= barStart + barWidth) {
2719
+ canvas.drawChar(targetX, currentY, targetChar, targetColor);
2720
+ }
2721
+ }
2722
+ if (showValues) {
2723
+ const valueStr = value.toFixed(0);
2724
+ canvas.drawString(barStart + barWidth + 2, currentY, valueStr, { r: 180, g: 180, b: 180, a: 1 });
2725
+ }
2726
+ currentY += 2;
2727
+ if (currentY > plotBottom)
2728
+ break;
2729
+ }
2730
+ }
2731
+ function renderGeomBraille(data, geom, aes, scales, canvas) {
2732
+ const brailleType = geom.params.brailleType ?? "point";
2733
+ const fill = geom.params.fill ?? false;
2734
+ const alpha = geom.params.alpha ?? 1;
2735
+ const BRAILLE_BASE = 10240;
2736
+ const DOTS = [
2737
+ [1, 2, 4, 64],
2738
+ [8, 16, 32, 128]
2739
+ ];
2740
+ const plotLeft = Math.round(scales.x.range[0]);
2741
+ const plotRight = Math.round(scales.x.range[1]);
2742
+ const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
2743
+ const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
2744
+ const plotWidth = plotRight - plotLeft;
2745
+ const plotHeight = plotBottom - plotTop;
2746
+ const brailleWidth = plotWidth;
2747
+ const brailleHeight = plotHeight;
2748
+ const buffer = [];
2749
+ for (let y = 0;y < brailleHeight; y++) {
2750
+ buffer[y] = new Array(brailleWidth).fill(0);
2751
+ }
2752
+ const defaultColor = { r: 79, g: 169, b: 238, a: 1 };
2753
+ const color = geom.params.color ? parseColorToRgba(geom.params.color) : defaultColor;
2754
+ const finalColor = alpha < 1 ? { ...color, a: alpha } : color;
2755
+ const sorted = aes.x ? [...data].sort((a, b) => Number(a[aes.x]) - Number(b[aes.x])) : data;
2756
+ const setDot = (canvasX, canvasY) => {
2757
+ const subX = (canvasX - plotLeft) * 2;
2758
+ const subY = (canvasY - plotTop) * 4;
2759
+ const cellX = Math.floor(subX / 2);
2760
+ const cellY = Math.floor(subY / 4);
2761
+ const dotCol = subX % 2;
2762
+ const dotRow = subY % 4;
2763
+ if (cellX >= 0 && cellX < brailleWidth && cellY >= 0 && cellY < brailleHeight) {
2764
+ if (dotCol >= 0 && dotCol < 2 && dotRow >= 0 && dotRow < 4) {
2765
+ buffer[cellY][cellX] |= DOTS[dotCol][dotRow];
2766
+ }
2767
+ }
2768
+ };
2769
+ let prevCx = null;
2770
+ let prevCy = null;
2771
+ for (const row of sorted) {
2772
+ const xVal = row[aes.x];
2773
+ const yVal = row[aes.y];
2774
+ if (xVal === null || xVal === undefined || yVal === null || yVal === undefined) {
2775
+ prevCx = null;
2776
+ prevCy = null;
2777
+ continue;
2778
+ }
2779
+ const cx = Math.round(scales.x.map(xVal));
2780
+ const cy = Math.round(scales.y.map(yVal));
2781
+ if (brailleType === "line" && prevCx !== null && prevCy !== null) {
2782
+ const dx = Math.abs(cx - prevCx);
2783
+ const dy = Math.abs(cy - prevCy);
2784
+ const sx = prevCx < cx ? 1 : -1;
2785
+ const sy = prevCy < cy ? 1 : -1;
2786
+ let err = dx - dy;
2787
+ let x = prevCx;
2788
+ let y = prevCy;
2789
+ while (true) {
2790
+ if (x >= plotLeft && x < plotRight && y >= plotTop && y < plotBottom) {
2791
+ setDot(x, y);
2792
+ if (fill) {
2793
+ for (let fy = y;fy < plotBottom; fy++) {
2794
+ setDot(x, fy);
2795
+ }
2796
+ }
2797
+ }
2798
+ if (x === cx && y === cy)
2799
+ break;
2800
+ const e2 = 2 * err;
2801
+ if (e2 > -dy) {
2802
+ err -= dy;
2803
+ x += sx;
2804
+ }
2805
+ if (e2 < dx) {
2806
+ err += dx;
2807
+ y += sy;
2808
+ }
2809
+ }
2810
+ } else {
2811
+ if (cx >= plotLeft && cx < plotRight && cy >= plotTop && cy < plotBottom) {
2812
+ setDot(cx, cy);
2813
+ }
2814
+ }
2815
+ prevCx = cx;
2816
+ prevCy = cy;
2817
+ }
2818
+ for (let y = 0;y < brailleHeight; y++) {
2819
+ for (let x = 0;x < brailleWidth; x++) {
2820
+ if (buffer[y][x] > 0) {
2821
+ const char = String.fromCharCode(BRAILLE_BASE + buffer[y][x]);
2822
+ canvas.drawChar(plotLeft + x, plotTop + y, char, finalColor);
2823
+ }
2824
+ }
2825
+ }
2826
+ }
2364
2827
  function renderGeom(data, geom, aes, scales, canvas, coordType) {
2365
2828
  switch (geom.type) {
2366
2829
  case "point":
@@ -2446,6 +2909,24 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
2446
2909
  case "quasirandom":
2447
2910
  renderGeomBeeswarm(data, geom, aes, scales, canvas);
2448
2911
  break;
2912
+ case "dumbbell":
2913
+ renderGeomDumbbell(data, geom, aes, scales, canvas);
2914
+ break;
2915
+ case "lollipop":
2916
+ renderGeomLollipop(data, geom, aes, scales, canvas);
2917
+ break;
2918
+ case "waffle":
2919
+ renderGeomWaffle(data, geom, aes, scales, canvas);
2920
+ break;
2921
+ case "sparkline":
2922
+ renderGeomSparkline(data, geom, aes, scales, canvas);
2923
+ break;
2924
+ case "bullet":
2925
+ renderGeomBullet(data, geom, aes, scales, canvas);
2926
+ break;
2927
+ case "braille":
2928
+ renderGeomBraille(data, geom, aes, scales, canvas);
2929
+ break;
2449
2930
  default:
2450
2931
  break;
2451
2932
  }
@@ -5813,9 +6294,136 @@ function geom_quasirandom(options = {}) {
5813
6294
  return geom_beeswarm({ ...options, method: "center" });
5814
6295
  }
5815
6296
 
6297
+ // src/geoms/dumbbell.ts
6298
+ function geom_dumbbell(options = {}) {
6299
+ return {
6300
+ type: "dumbbell",
6301
+ stat: "identity",
6302
+ position: "identity",
6303
+ params: {
6304
+ size: options.size ?? 2,
6305
+ sizeEnd: options.sizeEnd ?? options.size ?? 2,
6306
+ color: options.color,
6307
+ colorEnd: options.colorEnd ?? options.color,
6308
+ lineColor: options.lineColor ?? "#666666",
6309
+ lineWidth: options.lineWidth ?? 1,
6310
+ alpha: options.alpha ?? 1,
6311
+ shape: options.shape ?? "circle"
6312
+ }
6313
+ };
6314
+ }
6315
+
6316
+ // src/geoms/lollipop.ts
6317
+ function geom_lollipop(options = {}) {
6318
+ return {
6319
+ type: "lollipop",
6320
+ stat: "identity",
6321
+ position: "identity",
6322
+ params: {
6323
+ size: options.size ?? 2,
6324
+ color: options.color,
6325
+ lineColor: options.lineColor,
6326
+ lineWidth: options.lineWidth ?? 1,
6327
+ alpha: options.alpha ?? 1,
6328
+ shape: options.shape ?? "circle",
6329
+ direction: options.direction ?? "vertical",
6330
+ baseline: options.baseline ?? 0
6331
+ }
6332
+ };
6333
+ }
6334
+
6335
+ // src/geoms/waffle.ts
6336
+ function geom_waffle(options = {}) {
6337
+ return {
6338
+ type: "waffle",
6339
+ stat: "identity",
6340
+ position: "identity",
6341
+ params: {
6342
+ rows: options.rows ?? 10,
6343
+ cols: options.cols ?? 10,
6344
+ n_total: options.n_total ?? 100,
6345
+ fill_char: options.fill_char ?? "█",
6346
+ empty_char: options.empty_char ?? "░",
6347
+ alpha: options.alpha ?? 1,
6348
+ show_legend: options.show_legend ?? true,
6349
+ flip: options.flip ?? false,
6350
+ gap: options.gap ?? 0
6351
+ }
6352
+ };
6353
+ }
6354
+
6355
+ // src/geoms/sparkline.ts
6356
+ function geom_sparkline(options = {}) {
6357
+ return {
6358
+ type: "sparkline",
6359
+ stat: "identity",
6360
+ position: "identity",
6361
+ params: {
6362
+ sparkType: options.type ?? "bar",
6363
+ width: options.width ?? 20,
6364
+ height: options.height ?? 1,
6365
+ show_minmax: options.show_minmax ?? false,
6366
+ color: options.color,
6367
+ min_color: options.min_color ?? "#e74c3c",
6368
+ max_color: options.max_color ?? "#2ecc71",
6369
+ normalize: options.normalize ?? true
6370
+ }
6371
+ };
6372
+ }
6373
+ var SPARK_BARS, SPARK_DOTS;
6374
+ var init_sparkline = __esm(() => {
6375
+ SPARK_BARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
6376
+ SPARK_DOTS = ["⠀", "⢀", "⢠", "⢰", "⢸", "⣸", "⣾", "⣿"];
6377
+ });
6378
+
6379
+ // src/geoms/bullet.ts
6380
+ function geom_bullet(options = {}) {
6381
+ return {
6382
+ type: "bullet",
6383
+ stat: "identity",
6384
+ position: "identity",
6385
+ params: {
6386
+ width: options.width ?? 40,
6387
+ height: options.height ?? 1,
6388
+ target_char: options.target_char ?? "│",
6389
+ bar_char: options.bar_char ?? "█",
6390
+ range_chars: options.range_chars ?? ["░", "▒", "▓"],
6391
+ show_values: options.show_values ?? true,
6392
+ color: options.color,
6393
+ target_color: options.target_color ?? "#e74c3c",
6394
+ orientation: options.orientation ?? "horizontal"
6395
+ }
6396
+ };
6397
+ }
6398
+
6399
+ // src/geoms/braille.ts
6400
+ function geom_braille(options = {}) {
6401
+ return {
6402
+ type: "braille",
6403
+ stat: "identity",
6404
+ position: "identity",
6405
+ params: {
6406
+ brailleType: options.type ?? "point",
6407
+ color: options.color,
6408
+ fill: options.fill ?? false,
6409
+ alpha: options.alpha ?? 1,
6410
+ dot_size: options.dot_size ?? 1
6411
+ }
6412
+ };
6413
+ }
6414
+ var BRAILLE_BASE = 10240, BRAILLE_DOTS;
6415
+ var init_braille = __esm(() => {
6416
+ BRAILLE_DOTS = [
6417
+ [1, 2, 4, 64],
6418
+ [8, 16, 32, 128]
6419
+ ];
6420
+ });
6421
+
5816
6422
  // src/geoms/index.ts
5817
6423
  var init_geoms = __esm(() => {
5818
6424
  init_ridgeline();
6425
+ init_sparkline();
6426
+ init_braille();
5819
6427
  });
5820
6428
 
5821
6429
  // src/scales/continuous.ts
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Braille Geom
3
+ *
4
+ * Creates high-resolution plots using Unicode Braille patterns.
5
+ * Each character cell contains a 2x4 grid of dots, allowing
6
+ * 8x the resolution of regular character plots.
7
+ *
8
+ * Braille patterns: U+2800 to U+28FF (256 patterns)
9
+ * Each pattern is a 2-column x 4-row grid:
10
+ * ⠁⠂ (1,4)
11
+ * ⠄⠈ (2,5)
12
+ * ⠐⠠ (3,6)
13
+ * ⡀⢀ (7,8)
14
+ *
15
+ * Required aesthetics:
16
+ * - x: x-axis variable
17
+ * - y: y-axis variable
18
+ *
19
+ * Optional aesthetics:
20
+ * - color: point color
21
+ * - group: for multiple series
22
+ */
23
+ import type { Geom } from '../types.js';
24
+ export interface BrailleOptions {
25
+ /** Type of braille plot: 'point' (default), 'line' */
26
+ type?: 'point' | 'line';
27
+ /** Color for the dots/lines */
28
+ color?: string;
29
+ /** Fill the area under the line (for line type) */
30
+ fill?: boolean;
31
+ /** Opacity (0-1) */
32
+ alpha?: number;
33
+ /** Dot size - how many braille dots per data point (1-4) */
34
+ dot_size?: number;
35
+ }
36
+ export declare const BRAILLE_BASE = 10240;
37
+ export declare const BRAILLE_DOTS: number[][];
38
+ export declare function geom_braille(options?: BrailleOptions): Geom;
39
+ //# sourceMappingURL=braille.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"braille.d.ts","sourceRoot":"","sources":["../../src/geoms/braille.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAOD,eAAO,MAAM,YAAY,QAAS,CAAA;AAGlC,eAAO,MAAM,YAAY,EAAE,MAAM,EAAE,EAGlC,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,IAAI,CAa/D"}