@uoa-css-lab/duckscatter 1.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.
Files changed (83) hide show
  1. package/.github/dependabot.yml +42 -0
  2. package/.github/workflows/ci.yaml +111 -0
  3. package/.github/workflows/release.yml +55 -0
  4. package/.prettierrc +11 -0
  5. package/LICENSE +22 -0
  6. package/README.md +250 -0
  7. package/dist/data/data-layer.d.ts +169 -0
  8. package/dist/data/data-layer.js +402 -0
  9. package/dist/data/index.d.ts +2 -0
  10. package/dist/data/index.js +2 -0
  11. package/dist/data/repository.d.ts +48 -0
  12. package/dist/data/repository.js +109 -0
  13. package/dist/diagnostics.d.ts +27 -0
  14. package/dist/diagnostics.js +71 -0
  15. package/dist/errors.d.ts +22 -0
  16. package/dist/errors.js +58 -0
  17. package/dist/event-emitter.d.ts +62 -0
  18. package/dist/event-emitter.js +82 -0
  19. package/dist/index.d.ts +12 -0
  20. package/dist/index.js +13 -0
  21. package/dist/renderer/gpu-layer.d.ts +204 -0
  22. package/dist/renderer/gpu-layer.js +611 -0
  23. package/dist/renderer/index.d.ts +3 -0
  24. package/dist/renderer/index.js +3 -0
  25. package/dist/renderer/shaders.d.ts +13 -0
  26. package/dist/renderer/shaders.js +216 -0
  27. package/dist/renderer/webgpu-context.d.ts +20 -0
  28. package/dist/renderer/webgpu-context.js +88 -0
  29. package/dist/scatter-plot.d.ts +210 -0
  30. package/dist/scatter-plot.js +450 -0
  31. package/dist/types.d.ts +171 -0
  32. package/dist/types.js +1 -0
  33. package/dist/ui/index.d.ts +1 -0
  34. package/dist/ui/index.js +1 -0
  35. package/dist/ui/label-layer.d.ts +176 -0
  36. package/dist/ui/label-layer.js +488 -0
  37. package/docs/image.png +0 -0
  38. package/eslint.config.js +72 -0
  39. package/examples/next/README.md +36 -0
  40. package/examples/next/app/components/ColorExpressionInput.tsx +41 -0
  41. package/examples/next/app/components/ControlPanel.tsx +30 -0
  42. package/examples/next/app/components/HoverControlPanel.tsx +69 -0
  43. package/examples/next/app/components/HoverInfoDisplay.tsx +40 -0
  44. package/examples/next/app/components/LabelFilterInput.tsx +46 -0
  45. package/examples/next/app/components/LabelList.tsx +106 -0
  46. package/examples/next/app/components/PointAlphaSlider.tsx +21 -0
  47. package/examples/next/app/components/PointLimitSlider.tsx +23 -0
  48. package/examples/next/app/components/PointList.tsx +105 -0
  49. package/examples/next/app/components/PointSizeScaleSlider.tsx +22 -0
  50. package/examples/next/app/components/ScatterPlotCanvas.tsx +150 -0
  51. package/examples/next/app/components/SearchBox.tsx +46 -0
  52. package/examples/next/app/components/Slider.tsx +76 -0
  53. package/examples/next/app/components/StatsDisplay.tsx +15 -0
  54. package/examples/next/app/components/TimeFilterSlider.tsx +169 -0
  55. package/examples/next/app/context/ScatterPlotContext.tsx +402 -0
  56. package/examples/next/app/favicon.ico +0 -0
  57. package/examples/next/app/globals.css +23 -0
  58. package/examples/next/app/layout.tsx +35 -0
  59. package/examples/next/app/page.tsx +15 -0
  60. package/examples/next/eslint.config.mjs +18 -0
  61. package/examples/next/next.config.ts +7 -0
  62. package/examples/next/package-lock.json +6572 -0
  63. package/examples/next/package.json +27 -0
  64. package/examples/next/postcss.config.mjs +7 -0
  65. package/examples/next/scripts/generate_labels.py +167 -0
  66. package/examples/next/tsconfig.json +34 -0
  67. package/package.json +43 -0
  68. package/src/data/data-layer.ts +515 -0
  69. package/src/data/index.ts +2 -0
  70. package/src/data/repository.ts +146 -0
  71. package/src/diagnostics.ts +108 -0
  72. package/src/errors.ts +69 -0
  73. package/src/event-emitter.ts +88 -0
  74. package/src/index.ts +40 -0
  75. package/src/renderer/gpu-layer.ts +757 -0
  76. package/src/renderer/index.ts +3 -0
  77. package/src/renderer/shaders.ts +219 -0
  78. package/src/renderer/webgpu-context.ts +98 -0
  79. package/src/scatter-plot.ts +533 -0
  80. package/src/types.ts +218 -0
  81. package/src/ui/index.ts +1 -0
  82. package/src/ui/label-layer.ts +648 -0
  83. package/tsconfig.json +19 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * WebGPU診断結果のインターフェース
3
+ */
4
+ export interface WebGPUDiagnostics {
5
+ /** WebGPU APIがサポートされているか */
6
+ supported: boolean;
7
+ /** WebGPUが利用可能か */
8
+ available: boolean;
9
+ /** GPUアダプター情報 */
10
+ adapter: {
11
+ /** アダプターが利用可能か */
12
+ available: boolean;
13
+ /** サポートされている機能の配列 */
14
+ features?: string[];
15
+ /** GPUの制限値 */
16
+ limits?: Record<string, number>;
17
+ };
18
+ /** ブラウザ情報 */
19
+ browser: string;
20
+ /** エラーメッセージ(ある場合) */
21
+ error?: string;
22
+ }
23
+
24
+ /**
25
+ * WebGPUの利用可能性を診断する
26
+ * @returns 診断結果のWebGPUDiagnosticsオブジェクト
27
+ */
28
+ export async function diagnoseWebGPU(): Promise<WebGPUDiagnostics> {
29
+ const result: WebGPUDiagnostics = {
30
+ supported: false,
31
+ available: false,
32
+ adapter: {
33
+ available: false,
34
+ },
35
+ browser: getBrowserInfo(),
36
+ };
37
+
38
+ if (!('gpu' in navigator)) {
39
+ result.error = 'WebGPU API not found in navigator';
40
+ return result;
41
+ }
42
+
43
+ result.supported = true;
44
+
45
+ try {
46
+ const adapter = await navigator.gpu.requestAdapter();
47
+
48
+ if (!adapter) {
49
+ result.error = 'GPU adapter is null - GPU may be blocklisted or WebGPU is disabled';
50
+ return result;
51
+ }
52
+
53
+ result.adapter.available = true;
54
+ result.adapter.features = Array.from(adapter.features);
55
+ result.adapter.limits = {};
56
+
57
+ const limitsToCheck = [
58
+ 'maxTextureDimension1D',
59
+ 'maxTextureDimension2D',
60
+ 'maxBufferSize',
61
+ 'maxVertexBuffers',
62
+ 'maxVertexAttributes',
63
+ ];
64
+
65
+ for (const limit of limitsToCheck) {
66
+ if (limit in adapter.limits) {
67
+ result.adapter.limits[limit] = (adapter.limits as any)[limit];
68
+ }
69
+ }
70
+
71
+ result.available = true;
72
+ } catch (e) {
73
+ result.error = `Error requesting adapter: ${e}`;
74
+ return result;
75
+ }
76
+
77
+ return result;
78
+ }
79
+
80
+ /**
81
+ * ブラウザ情報を取得する
82
+ * @returns ブラウザ名とバージョンの文字列
83
+ */
84
+ function getBrowserInfo(): string {
85
+ const ua = navigator.userAgent;
86
+
87
+ if (ua.includes('Chrome') && !ua.includes('Edg')) {
88
+ const match = ua.match(/Chrome\/(\d+)/);
89
+ return match ? `Chrome ${match[1]}` : 'Chrome (unknown version)';
90
+ }
91
+
92
+ if (ua.includes('Edg')) {
93
+ const match = ua.match(/Edg\/(\d+)/);
94
+ return match ? `Edge ${match[1]}` : 'Edge (unknown version)';
95
+ }
96
+
97
+ if (ua.includes('Safari') && !ua.includes('Chrome')) {
98
+ const match = ua.match(/Version\/(\d+)/);
99
+ return match ? `Safari ${match[1]}` : 'Safari (unknown version)';
100
+ }
101
+
102
+ if (ua.includes('Firefox')) {
103
+ const match = ua.match(/Firefox\/(\d+)/);
104
+ return match ? `Firefox ${match[1]}` : 'Firefox (unknown version)';
105
+ }
106
+
107
+ return 'Unknown browser';
108
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,69 @@
1
+ import type { ScatterPlotError, ErrorCode, ErrorCategory, ErrorSeverity } from './types.js';
2
+
3
+ /**
4
+ * エラーコードからカテゴリへのマッピング
5
+ */
6
+ const categoryMap: Record<ErrorCode, ErrorCategory> = {
7
+ WEBGPU_NOT_SUPPORTED: 'webgpu',
8
+ GPU_ADAPTER_NOT_AVAILABLE: 'webgpu',
9
+ GPU_DEVICE_FAILED: 'webgpu',
10
+ WEBGPU_CONTEXT_FAILED: 'webgpu',
11
+ DATA_LAYER_NOT_INITIALIZED: 'data',
12
+ PARQUET_LOAD_FAILED: 'data',
13
+ QUERY_FAILED: 'query',
14
+ LABEL_FETCH_FAILED: 'label',
15
+ LABEL_PARSE_FAILED: 'label',
16
+ NETWORK_ERROR: 'network',
17
+ };
18
+
19
+ /**
20
+ * エラーコードから重大度レベルへのマッピング
21
+ */
22
+ const severityMap: Record<ErrorCode, ErrorSeverity> = {
23
+ WEBGPU_NOT_SUPPORTED: 'fatal',
24
+ GPU_ADAPTER_NOT_AVAILABLE: 'fatal',
25
+ GPU_DEVICE_FAILED: 'fatal',
26
+ WEBGPU_CONTEXT_FAILED: 'fatal',
27
+ DATA_LAYER_NOT_INITIALIZED: 'fatal',
28
+ PARQUET_LOAD_FAILED: 'fatal',
29
+ QUERY_FAILED: 'error',
30
+ NETWORK_ERROR: 'error',
31
+ LABEL_FETCH_FAILED: 'warning',
32
+ LABEL_PARSE_FAILED: 'warning',
33
+ };
34
+
35
+ /**
36
+ * 指定されたコードとメッセージでScatterPlotErrorオブジェクトを作成する
37
+ *
38
+ * @param code エラーコード
39
+ * @param message 人間が読めるエラーメッセージ
40
+ * @param options 追加オプション
41
+ * @returns ScatterPlotErrorオブジェクト
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const error = createError(
46
+ * 'WEBGPU_NOT_SUPPORTED',
47
+ * 'WebGPU is not supported in this browser',
48
+ * { context: { userAgent: navigator.userAgent } }
49
+ * );
50
+ * ```
51
+ */
52
+ export function createError(
53
+ code: ErrorCode,
54
+ message: string,
55
+ options?: {
56
+ cause?: Error;
57
+ context?: Record<string, unknown>;
58
+ }
59
+ ): ScatterPlotError {
60
+ return {
61
+ code,
62
+ category: categoryMap[code],
63
+ severity: severityMap[code],
64
+ message,
65
+ cause: options?.cause,
66
+ context: options?.context,
67
+ timestamp: Date.now(),
68
+ };
69
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * イベントハンドラー関数の型
3
+ */
4
+ export type EventHandler<T = unknown> = (event: T) => void;
5
+
6
+ /**
7
+ * ブラウザ環境向けの軽量なEventEmitter実装
8
+ * 外部依存なしで型安全なイベントハンドリングを提供する
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * interface MyEvents {
13
+ * data: { value: number };
14
+ * error: Error;
15
+ * }
16
+ *
17
+ * class MyClass extends EventEmitter<MyEvents> {
18
+ * doSomething() {
19
+ * this.emit('data', { value: 42 });
20
+ * }
21
+ * }
22
+ *
23
+ * const instance = new MyClass();
24
+ * instance.on('data', (event) => console.log(event.value));
25
+ * ```
26
+ */
27
+ export class EventEmitter<T extends { [K in keyof T]: unknown }> {
28
+ /** イベント名からハンドラーのセットへのマップ */
29
+ private listeners = new Map<keyof T, Set<EventHandler<any>>>();
30
+
31
+ /**
32
+ * 指定されたイベントに対してハンドラーを登録する
33
+ *
34
+ * @param event リッスンするイベント名
35
+ * @param handler イベント発生時に呼び出されるハンドラー関数
36
+ * @returns メソッドチェーン用のthisインスタンス
37
+ */
38
+ on<K extends keyof T>(event: K, handler: EventHandler<T[K]>): this {
39
+ if (!this.listeners.has(event)) {
40
+ this.listeners.set(event, new Set());
41
+ }
42
+ this.listeners.get(event)!.add(handler);
43
+ return this;
44
+ }
45
+
46
+ /**
47
+ * 指定されたイベントからハンドラーを削除する
48
+ *
49
+ * @param event イベント名
50
+ * @param handler 削除するハンドラー関数
51
+ * @returns メソッドチェーン用のthisインスタンス
52
+ */
53
+ off<K extends keyof T>(event: K, handler: EventHandler<T[K]>): this {
54
+ this.listeners.get(event)?.delete(handler);
55
+ return this;
56
+ }
57
+
58
+ /**
59
+ * 登録されたすべてのハンドラーにイベントを発行する
60
+ *
61
+ * @param event 発行するイベント名
62
+ * @param data ハンドラーに渡すイベントデータ
63
+ * @returns リスナーがあればtrue、なければfalse
64
+ */
65
+ protected emit<K extends keyof T>(event: K, data: T[K]): boolean {
66
+ const handlers = this.listeners.get(event);
67
+ if (!handlers || handlers.size === 0) {
68
+ return false;
69
+ }
70
+ handlers.forEach((handler) => handler(data));
71
+ return true;
72
+ }
73
+
74
+ /**
75
+ * 指定されたイベント、またはすべてのイベントのリスナーを削除する
76
+ *
77
+ * @param event イベント名(省略時はすべてのリスナーを削除)
78
+ * @returns メソッドチェーン用のthisインスタンス
79
+ */
80
+ removeAllListeners(event?: keyof T): this {
81
+ if (event) {
82
+ this.listeners.delete(event);
83
+ } else {
84
+ this.listeners.clear();
85
+ }
86
+ return this;
87
+ }
88
+ }
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * duckscatter - WebGPUを使用して散布図を描画するTypeScriptライブラリ
3
+ */
4
+
5
+ export { ScatterPlot } from './scatter-plot.js';
6
+ export type {
7
+ ColorRGBA,
8
+ ScatterPlotOptions,
9
+ Label,
10
+ WhereCondition,
11
+ NumericFilter,
12
+ StringFilter,
13
+ RawSqlFilter,
14
+ NumericOperator,
15
+ StringOperator,
16
+ // GPU filter types
17
+ GpuWhereCondition,
18
+ // Error handling types
19
+ ErrorSeverity,
20
+ ErrorCategory,
21
+ ErrorCode,
22
+ ScatterPlotError,
23
+ ScatterPlotEventMap,
24
+ // Hover control types
25
+ PointId,
26
+ LabelIdentifier,
27
+ LabelHoverCallback,
28
+ } from './types.js';
29
+
30
+ export { diagnoseWebGPU } from './diagnostics.js';
31
+ export type { WebGPUDiagnostics } from './diagnostics.js';
32
+
33
+ /**
34
+ * 現在の環境でWebGPUがサポートされているかを確認する
35
+ * @returns WebGPUがサポートされている場合はtrue、そうでない場合はfalse
36
+ */
37
+ export function isWebGPUSupported(): boolean {
38
+ // navigatorオブジェクトにgpuプロパティが存在するかをチェック
39
+ return 'gpu' in navigator;
40
+ }