chartai 0.1.0 → 1.1.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 (116) hide show
  1. package/dist/chart-library.d.ts +34 -146
  2. package/dist/chart-library.d.ts.map +1 -1
  3. package/dist/chart-library.js +411 -322
  4. package/dist/chart-library.min.js +1 -1
  5. package/dist/charts/area.d.ts +6 -0
  6. package/dist/charts/area.d.ts.map +1 -0
  7. package/dist/charts/area.js +65 -0
  8. package/dist/charts/area.min.js +1 -0
  9. package/dist/charts/bar.d.ts +11 -0
  10. package/dist/charts/bar.d.ts.map +1 -0
  11. package/dist/charts/bar.js +65 -0
  12. package/dist/charts/bar.min.js +1 -0
  13. package/dist/charts/boids.js +167 -0
  14. package/dist/charts/boids.min.js +18 -0
  15. package/dist/charts/candlestick.d.ts +21 -0
  16. package/dist/charts/candlestick.d.ts.map +1 -0
  17. package/dist/charts/candlestick.js +10 -0
  18. package/dist/charts/candlestick.min.js +1 -0
  19. package/dist/charts/experimental/baseline-area.js +70 -0
  20. package/dist/charts/experimental/baseline-area.min.js +1 -0
  21. package/dist/charts/experimental/bubble.js +48 -0
  22. package/dist/charts/experimental/bubble.min.js +1 -0
  23. package/dist/charts/experimental/error-band.js +111 -0
  24. package/dist/charts/experimental/error-band.min.js +1 -0
  25. package/dist/charts/experimental/heatmap.js +69 -0
  26. package/dist/charts/experimental/heatmap.min.js +1 -0
  27. package/dist/charts/experimental/histogram.js +139 -0
  28. package/dist/charts/experimental/histogram.min.js +7 -0
  29. package/dist/charts/experimental/ohlc.js +132 -0
  30. package/dist/charts/experimental/ohlc.min.js +32 -0
  31. package/dist/charts/experimental/step.js +67 -0
  32. package/dist/charts/experimental/step.min.js +1 -0
  33. package/dist/charts/experimental/waterfall.js +121 -0
  34. package/dist/charts/experimental/waterfall.min.js +7 -0
  35. package/dist/charts/line.d.ts +12 -0
  36. package/dist/charts/line.d.ts.map +1 -0
  37. package/dist/charts/line.js +62 -0
  38. package/dist/charts/line.min.js +1 -0
  39. package/dist/charts/scatter.d.ts +11 -0
  40. package/dist/charts/scatter.d.ts.map +1 -0
  41. package/dist/charts/scatter.js +46 -0
  42. package/dist/charts/scatter.min.js +1 -0
  43. package/dist/chunk-0eh4rzy9.min.js +2 -0
  44. package/dist/chunk-0jepamv9.js +7 -0
  45. package/dist/chunk-1ngxm8t2.js +129 -0
  46. package/dist/chunk-50bcv2hw.min.js +2 -0
  47. package/dist/chunk-5gtx3pza.js +9 -0
  48. package/dist/chunk-64q9a7nw.min.js +2 -0
  49. package/dist/chunk-831dem4f.js +4 -0
  50. package/dist/chunk-93yrr7er.js +35 -0
  51. package/dist/chunk-bbyt23tw.min.js +2 -0
  52. package/dist/chunk-cbydth3q.min.js +2 -0
  53. package/dist/chunk-cvtt04m6.min.js +2 -0
  54. package/dist/chunk-g2qmt43n.min.js +33 -0
  55. package/dist/chunk-gm0d4cgx.min.js +2 -0
  56. package/dist/chunk-mmsy3yqt.js +27 -0
  57. package/dist/chunk-n8ew0z0e.js +637 -0
  58. package/dist/chunk-t0kdz02m.js +129 -0
  59. package/dist/chunk-wdfq2fpx.min.js +2 -0
  60. package/dist/chunk-yabjrff2.js +11 -0
  61. package/dist/gpu-worker.js +630 -686
  62. package/dist/gpu-worker.min.js +1 -1
  63. package/dist/msg.d.ts +33 -0
  64. package/dist/msg.d.ts.map +1 -0
  65. package/dist/plugins/coords.d.ts +18 -0
  66. package/dist/plugins/coords.d.ts.map +1 -0
  67. package/dist/plugins/experimental/annotations.js +164 -0
  68. package/dist/plugins/experimental/annotations.min.js +1 -0
  69. package/dist/plugins/experimental/crosshair.js +82 -0
  70. package/dist/plugins/experimental/crosshair.min.js +1 -0
  71. package/dist/plugins/experimental/minimap.js +190 -0
  72. package/dist/plugins/experimental/minimap.min.js +1 -0
  73. package/dist/plugins/experimental/range-selector.js +220 -0
  74. package/dist/plugins/experimental/range-selector.min.js +1 -0
  75. package/dist/plugins/experimental/ruler.js +434 -0
  76. package/dist/plugins/experimental/ruler.min.js +59 -0
  77. package/dist/plugins/experimental/stats.js +229 -0
  78. package/dist/plugins/experimental/stats.min.js +8 -0
  79. package/dist/plugins/experimental/threshold.js +96 -0
  80. package/dist/plugins/experimental/threshold.min.js +1 -0
  81. package/dist/plugins/experimental/tooltip-pin.js +177 -0
  82. package/dist/plugins/experimental/tooltip-pin.min.js +1 -0
  83. package/dist/plugins/experimental/watermark.js +76 -0
  84. package/dist/plugins/experimental/watermark.min.js +1 -0
  85. package/dist/plugins/hover.d.ts +15 -2
  86. package/dist/plugins/hover.d.ts.map +1 -1
  87. package/dist/plugins/hover.js +75 -14
  88. package/dist/plugins/hover.min.js +1 -1
  89. package/dist/plugins/labels-panel.d.ts +4 -0
  90. package/dist/plugins/labels-panel.d.ts.map +1 -0
  91. package/dist/plugins/labels-panel.js +122 -0
  92. package/dist/plugins/labels-panel.min.js +1 -0
  93. package/dist/plugins/labels.d.ts +17 -2
  94. package/dist/plugins/labels.d.ts.map +1 -1
  95. package/dist/plugins/labels.js +11 -99
  96. package/dist/plugins/labels.min.js +1 -1
  97. package/dist/plugins/legend.d.ts +16 -0
  98. package/dist/plugins/legend.d.ts.map +1 -0
  99. package/dist/plugins/legend.js +353 -0
  100. package/dist/plugins/legend.min.js +37 -0
  101. package/dist/plugins/shared.d.ts +7 -0
  102. package/dist/plugins/shared.d.ts.map +1 -0
  103. package/dist/plugins/zoom.d.ts +10 -2
  104. package/dist/plugins/zoom.d.ts.map +1 -1
  105. package/dist/plugins/zoom.js +63 -62
  106. package/dist/plugins/zoom.min.js +1 -1
  107. package/dist/types.d.ts +187 -0
  108. package/dist/types.d.ts.map +1 -0
  109. package/dist/types.js +0 -0
  110. package/dist/types.min.js +0 -0
  111. package/dist/worker-inline.d.ts +1 -1
  112. package/dist/worker-inline.d.ts.map +1 -1
  113. package/package.json +11 -11
  114. package/readme.md +54 -42
  115. package/dist/chunk-bgfkgcmg.js +0 -25
  116. package/dist/chunk-cj3zanvs.min.js +0 -2
@@ -1,11 +1,15 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __returnValue = (v) => v;
3
+ function __exportSetter(name, newValue) {
4
+ this[name] = __returnValue.bind(null, newValue);
5
+ }
2
6
  var __export = (target, all) => {
3
7
  for (var name in all)
4
8
  __defProp(target, name, {
5
9
  get: all[name],
6
10
  enumerable: true,
7
11
  configurable: true,
8
- set: (newValue) => all[name] = () => newValue
12
+ set: __exportSetter.bind(all, name)
9
13
  });
10
14
  };
11
15
 
@@ -14,46 +18,146 @@ var exports_worker_inline = {};
14
18
  __export(exports_worker_inline, {
15
19
  WORKER_CODE: () => WORKER_CODE
16
20
  });
17
- var WORKER_CODE = 'var m=256,X="struct Uniforms{width: f32,height: f32,viewMinX: f32,viewMaxX: f32,viewMinY: f32,viewMaxY: f32,pointCount: u32,isDark: f32,bgR: f32,bgG: f32,bgB: f32,pointRadius: f32,dataMinY: f32,dataMaxY: f32,dataMinX: f32,dataMaxX: f32,visibleStart: u32,visibleCount: u32,dispatchXCount: u32,maxSamplesPerPixel: u32,seriesCount: u32,_pad2: u32,_pad3: u32,_pad4: u32,};struct SeriesInfo{color: vec4f,visibleRange: vec2u,pointSize: f32,_pad: f32,};struct SeriesIndex{index: u32,_pad0: u32,_pad1: u32,_pad2: u32,};",w="fn lowerBound(val: f32,count: u32)-> u32{var lo = 0u;var hi = count;while(lo < hi){let mid =(lo + hi)/ 2u;if(dataX[mid] < val){lo = mid + 1u;}else{hi = mid;}}return lo;}";var C="struct Uniforms{width: f32,height: f32,viewMinX: f32,viewMaxX: f32,viewMinY: f32,viewMaxY: f32,pointCount: u32,isDark: f32,bgR: f32,bgG: f32,bgB: f32,pointRadius: f32,dataMinY: f32,dataMaxY: f32,dataMinX: f32,dataMaxX: f32,visibleStart: u32,visibleCount: u32,dispatchXCount: u32,maxSamplesPerPixel: u32,seriesCount: u32,_pad2: u32,_pad3: u32,_pad4: u32,};struct SeriesInfo{color: vec4f,visibleRange: vec2u,pointSize: f32,_pad: f32,};struct SeriesIndex{index: u32,_pad0: u32,_pad1: u32,_pad2: u32,};fn l(c:vec4f)->f32{return dot(c.rgb,vec3f(.299,.587,.114))+c.a*.25;}fn fxaa(u:vec2f,t:texture_2d<f32>,s:sampler)->vec4f{let r=1./vec2f(textureDimensions(t));let rM=textureSampleLevel(t,s,u,0.);let rN=textureSampleLevel(t,s,u+vec2f(0.,-r.y),0.);let rS=textureSampleLevel(t,s,u+vec2f(0.,r.y),0.);let rE=textureSampleLevel(t,s,u+vec2f(r.x,0.),0.);let rW=textureSampleLevel(t,s,u+vec2f(-r.x,0.),0.);let lM=l(rM);let lN=l(rN);let lS=l(rS);let lE=l(rE);let lW=l(rW);let mi=min(lM,min(min(lN,lS),min(lE,lW)));let ma=max(lM,max(max(lN,lS),max(lE,lW)));let ra=ma-mi;if(ra<max(.0833,ma*.166)){return rM;}let lNW=l(textureSampleLevel(t,s,u+vec2f(-r.x,-r.y),0.));let lNE=l(textureSampleLevel(t,s,u+vec2f(r.x,-r.y),0.));let lSW=l(textureSampleLevel(t,s,u+vec2f(-r.x,r.y),0.));let lSE=l(textureSampleLevel(t,s,u+vec2f(r.x,r.y),0.));let sB=min(.35,max(0.,abs((lN+lS+lE+lW)*.25-lM)/ra-.25)*1.33);let iH=abs(lNW+lNE-2.*lN)+abs(lW+lE-2.*lM)*2.+abs(lSW+lSE-2.*lS)>=abs(lNW+lSW-2.*lW)+abs(lN+lS-2.*lM)*2.+abs(lNE+lSE-2.*lE);let l1=select(lW,lN,iH);let l2=select(lE,lS,iH);let pD=abs(l2-lM)>abs(l1-lM);let sL=select(r.x,r.y,iH);let lA=.5*(select(l1,l2,pD)+lM);let gS=max(abs(l1-lM),abs(l2-lM))*.25;var eU=u;if(iH){eU.y+=select(-.5,.5,pD)*sL;}else{eU.x+=select(-.5,.5,pD)*sL;}let eS=select(vec2f(r.x,0.),vec2f(0.,r.y),iH);var uN=eU-eS;var uP=eU+eS;var eN=l(textureSampleLevel(t,s,uN,0.))-lA;var eP=l(textureSampleLevel(t,s,uP,0.))-lA;var dN=abs(eN)>=gS;var dP=abs(eP)>=gS;for(var i=1;i<8;i++){if(!dN){uN-=eS*1.5;eN=l(textureSampleLevel(t,s,uN,0.))-lA;dN=abs(eN)>=gS;}if(!dP){uP+=eS*1.5;eP=l(textureSampleLevel(t,s,uP,0.))-lA;dP=abs(eP)>=gS;}if(dN&&dP){break;}}let dtN=select(u.x-uN.x,u.y-uN.y,iH);let dtP=select(uP.x-u.x,uP.y-u.y,iH);let eB=select(0.,.5-min(dtN,dtP)/(dtN+dtP),(lM-lA<0.)!=(select(eP<0.,eN<0.,dtN<dtP)));var fU=u;let fL=max(eB,sB);if(iH){fU.y+=select(-1.,1.,pD)*fL*sL;}else{fU.x+=select(-1.,1.,pD)*fL*sL;}return textureSampleLevel(t,s,fU,0.);}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var inputTex: texture_2d<f32>;@group(0)@binding(2)var samp: sampler;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)uv: vec2f,};@vertex fn vs(@builtin(vertex_index)vi: u32)-> VertexOutput{var positions = array<vec2f,4>(vec2f(-1.0,-1.0),vec2f(1.0,-1.0),vec2f(-1.0,1.0),vec2f(1.0,1.0));var uvs = array<vec2f,4>(vec2f(0.0,1.0),vec2f(1.0,1.0),vec2f(0.0,0.0),vec2f(1.0,0.0));var out: VertexOutput;out.pos = vec4f(positions[vi],0.0,1.0);out.uv = uvs[vi];return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{return fxaa(in.uv,inputTex,samp);}";var y=`${X}struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var<storage,read_write> lineData: array<LineData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;${w}@compute @workgroup_size(${m})fn main(@builtin(global_invocation_id)id: vec3u){let outputIdx = id.x;let maxCols = u32(u.width);let count = u.pointCount;if(outputIdx >= maxCols || count == 0u){if(outputIdx < maxCols){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);}return;}let viewRangeX = u.viewMaxX - u.viewMinX;let viewRangeY = u.viewMaxY - u.viewMinY;if(viewRangeX < 0.0001 || viewRangeY < 0.0001){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let relPx = f32(outputIdx);let pixelMinX = u.viewMinX +(relPx / u.width)* viewRangeX;let pixelMaxX = u.viewMinX +((relPx + 1.0)/ u.width)* viewRangeX;let startIdx = lowerBound(pixelMinX,count);var endIdx = lowerBound(pixelMaxX,count);endIdx = min(endIdx,count);let centerX =(pixelMinX + pixelMaxX)* 0.5;if(startIdx >= endIdx){var bestIdx = startIdx;if(startIdx > 0u && startIdx < count){let distPrev = abs(dataX[startIdx - 1u] - centerX);let distCurr = abs(dataX[startIdx] - centerX);if(distPrev < distCurr){bestIdx = startIdx - 1u;}}else if(startIdx >= count && count > 0u){bestIdx = count - 1u;}if(bestIdx >= count){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let y = dataY[bestIdx];let normY =(y - u.viewMinY)/ viewRangeY;let screenY = 1.0 - normY;let normX =(dataX[bestIdx] - u.viewMinX)/ viewRangeX;let screenX = normX;lineData[outputIdx] = LineData(screenX,screenY,screenY,1.0);return;}var dataMinY = dataY[startIdx];var dataMaxY = dataY[startIdx];let rangeCount = endIdx - startIdx;let maxSamples = u.maxSamplesPerPixel;if(maxSamples > 1u && rangeCount > maxSamples){let stride = f32(rangeCount - 1u)/ f32(maxSamples - 1u);for(var s = 0u;s < maxSamples;s++){let idx = startIdx + u32(f32(s)* stride);if(idx < endIdx){let y = dataY[idx];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let lastY = dataY[endIdx - 1u];dataMinY = min(dataMinY,lastY);dataMaxY = max(dataMaxY,lastY);}else{for(var i = startIdx + 1u;i < endIdx;i++){let y = dataY[i];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let normX =(centerX - u.viewMinX)/ viewRangeX;let screenX = normX;let normMaxY =(dataMaxY - u.viewMinY)/ viewRangeY;let normMinY =(dataMinY - u.viewMinY)/ viewRangeY;let minScreenY = 1.0 - normMaxY;let maxScreenY = 1.0 - normMinY;lineData[outputIdx] = LineData(screenX,minScreenY,maxScreenY,1.0);}`,L=`${X}struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> lineData: array<LineData>;@group(0)@binding(2)var<storage,read> allSeries: array<SeriesInfo>;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)alpha: f32,@location(1)@interpolate(flat)seriesIdx: u32,};@vertex fn vs(@builtin(vertex_index)vi: u32,@builtin(instance_index)series_idx: u32)-> VertexOutput{var out: VertexOutput;out.seriesIdx = series_idx;let maxCols = u32(u.width);let segIdx = vi / 2u;let endpoint = vi % 2u;if(segIdx < maxCols){let d = lineData[segIdx];let y = select(d.maxScreenY,d.minScreenY,endpoint == 0u);out.pos = vec4f(d.screenX * 2.0 - 1.0,1.0 - y * 2.0,0.0,d.valid);out.alpha = d.valid;}else{let connIdx = segIdx - maxCols;if(connIdx + 1u >= maxCols){out.pos = vec4f(0.0,0.0,0.0,0.0);out.alpha = 0.0;return out;}let d0 = lineData[connIdx];let d1 = lineData[connIdx + 1u];let segValid = min(d0.valid,d1.valid);if(endpoint == 0u){let midY =(d0.minScreenY + d0.maxScreenY)* 0.5;out.pos = vec4f(d0.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}else{let midY =(d1.minScreenY + d1.maxScreenY)* 0.5;out.pos = vec4f(d1.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}out.alpha = segValid;}return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{if(in.alpha < 0.1){discard;}let series = allSeries[in.seriesIdx];return vec4f(series.color.rgb,1.0);}`;var D=`${X}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var outputTex: texture_storage_2d<rgba8unorm,write>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;@compute @workgroup_size(${m})fn main(@builtin(global_invocation_id)id: vec3u){let series = allSeries[seriesIdx.index];let visStart = series.visibleRange.x;let visCount = series.visibleRange.y;let localIdx = id.y * u.dispatchXCount + id.x;if(localIdx >= visCount){return;}let idx = visStart + localIdx;let count = u.pointCount;if(idx >= count){return;}let x = dataX[idx];let y = dataY[idx];if(y < u.viewMinY || y > u.viewMaxY){return;}let width = u32(u.width);let height = u32(u.height);let rangeX = u.viewMaxX - u.viewMinX;let rangeY = u.viewMaxY - u.viewMinY;if(rangeX < 0.0001 || rangeY < 0.0001){return;}let normX =(x - u.viewMinX)/ rangeX;let normY =(y - u.viewMinY)/ rangeY;let screenX = normX;let screenY = 1.0 - normY;let pixelX = i32(screenX * f32(width));let pixelY = i32(screenY * f32(height));if(idx > visStart){let prevX = dataX[idx - 1u];let prevY = dataY[idx - 1u];let prevNormX =(prevX - u.viewMinX)/ rangeX;let prevNormY =(prevY - u.viewMinY)/ rangeY;let prevPx = i32(prevNormX * f32(width));let prevPy = i32((1.0 - prevNormY)* f32(height));if(pixelX == prevPx && pixelY == prevPy){return;}}let iWidth = i32(width);let iHeight = i32(height);if(pixelX < 0 || pixelX >= iWidth){return;}if(pixelY < 0 || pixelY >= iHeight){return;}let color = series.color;let radius = i32(series.pointSize);for(var dy = -radius;dy <= radius;dy++){for(var dx = -radius;dx <= radius;dx++){if(dx * dx + dy * dy > radius * radius){continue;}let px = pixelX + dx;let py = pixelY + dy;if(px >= 0 && px < iWidth && py >= 0 && py < iHeight){textureStore(outputTex,vec2i(px,py),color);}}}}`;var H=`${X}struct BarData{screenX: f32,minY: f32,maxY: f32,barWidth: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var<storage,read_write> barData: array<BarData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;${w}fn barHalfWidth(idx: u32,count: u32)-> f32{if(count <= 1u){return(u.viewMaxX - u.viewMinX)* 0.4;}var spacing: f32;if(idx == 0u){spacing = dataX[1u] - dataX[0u];}else if(idx >= count - 1u){spacing = dataX[count - 1u] - dataX[count - 2u];}else{spacing = min(dataX[idx + 1u] - dataX[idx],dataX[idx] - dataX[idx - 1u]);}let seriesCount = max(1u,u.seriesCount);return(spacing * 0.4)/ f32(seriesCount);}@compute @workgroup_size(${m})fn main(@builtin(global_invocation_id)id: vec3u){let outputIdx = id.x;let maxCols = u32(u.width);let count = u.pointCount;if(outputIdx >= maxCols || count == 0u){if(outputIdx < maxCols){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);}return;}let viewRangeX = u.viewMaxX - u.viewMinX;let viewRangeY = u.viewMaxY - u.viewMinY;if(viewRangeX < 0.0001 || viewRangeY < 0.0001){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);return;}let relPx = f32(outputIdx);let pixelMinX = u.viewMinX +(relPx / u.width)* viewRangeX;let pixelMaxX = u.viewMinX +((relPx + 1.0)/ u.width)* viewRangeX;let startIdx = lowerBound(pixelMinX,count);var endIdx = lowerBound(pixelMaxX,count);endIdx = min(endIdx,count);let centerX =(pixelMinX + pixelMaxX)* 0.5;let onePixel = 1.0 / u.width;if(startIdx >= endIdx){var hit = false;var bestX: f32 = 0.0;var bestY: f32 = 0.0;var bestHW: f32 = 0.0;var bestDist: f32 = 1e10;if(startIdx < count){let bx = dataX[startIdx];let hw = barHalfWidth(startIdx,count);if(pixelMinX < bx + hw && pixelMaxX > bx - hw){let d = abs(bx - centerX);bestX = bx;bestY = dataY[startIdx];bestHW = hw;bestDist = d;hit = true;}}if(startIdx > 0u){let prev = startIdx - 1u;let bx = dataX[prev];let hw = barHalfWidth(prev,count);if(pixelMinX < bx + hw && pixelMaxX > bx - hw){let d = abs(bx - centerX);if(d < bestDist){bestX = bx;bestY = dataY[prev];bestHW = hw;bestDist = d;}hit = true;}}if(!hit){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);return;}let seriesCount = max(1u,u.seriesCount);let barOffset =(f32(seriesIdx.index)- f32(seriesCount - 1u)* 0.5)*(bestHW * 2.0);let offsetX = bestX + barOffset;let normX =(offsetX - u.viewMinX)/ viewRangeX;let fullWidth = bestHW * 2.0 / viewRangeX;let gapSize = max(onePixel,fullWidth * 0.05);let bw = max(fullWidth - gapSize,onePixel);barData[outputIdx] = BarData(normX,bestY,bestY,bw);return;}var dataMinY = dataY[startIdx];var dataMaxY = dataY[startIdx];let rangeCount = endIdx - startIdx;let maxSamples = u.maxSamplesPerPixel;if(maxSamples > 0u && rangeCount > maxSamples){let stride = f32(rangeCount - 1u)/ f32(maxSamples - 1u);for(var s = 0u;s < maxSamples;s++){let idx = startIdx + u32(f32(s)* stride);if(idx < endIdx){let y = dataY[idx];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let lastY = dataY[endIdx - 1u];dataMinY = min(dataMinY,lastY);dataMaxY = max(dataMaxY,lastY);}else{for(var i = startIdx + 1u;i < endIdx;i++){let y = dataY[i];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let hw = barHalfWidth(startIdx,count);let fullWidth = hw * 2.0 / viewRangeX;let gapSize = max(onePixel,fullWidth * 0.05);let bw = max(fullWidth - gapSize,onePixel);let seriesCount = max(1u,u.seriesCount);let barOffset =(f32(seriesIdx.index)- f32(seriesCount - 1u)* 0.5)*(hw * 2.0);let dataX_centered = dataX[startIdx] + barOffset;let normX =(dataX_centered - u.viewMinX)/ viewRangeX;barData[outputIdx] = BarData(normX,dataMinY,dataMaxY,bw);}`,U=`${X}struct BarData{screenX: f32,minY: f32,maxY: f32,barWidth: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> barData: array<BarData>;@group(0)@binding(2)var<storage,read> allSeries: array<SeriesInfo>;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)normY: f32,@location(1)@interpolate(flat)seriesIdx: u32,};@vertex fn vs(@builtin(vertex_index)vi: u32,@builtin(instance_index)series_idx: u32)-> VertexOutput{var out: VertexOutput;out.seriesIdx = series_idx;let maxCols = u32(u.width);let colIdx = vi / 6u;let vertexType = vi % 6u;if(colIdx >= maxCols){out.pos = vec4f(0.0,0.0,0.0,0.0);out.normY = 0.0;return out;}let bd = barData[colIdx];if(bd.barWidth <= 0.0){out.pos = vec4f(0.0,0.0,0.0,0.0);out.normY = 0.0;return out;}let viewRangeY = u.viewMaxY - u.viewMinY;let safeRangeY = select(viewRangeY,1.0,viewRangeY < 0.0001);let normMinY =(min(bd.minY,0.0)- u.viewMinY)/ safeRangeY;let normMaxY =(max(bd.maxY,0.0)- u.viewMinY)/ safeRangeY;let top = 1.0 - normMaxY;let bottom = 1.0 - normMinY;let halfW = bd.barWidth * 0.5;let left = bd.screenX - halfW;let right = bd.screenX + halfW;var positions = array<vec2f,6>(vec2f(left,bottom),vec2f(right,bottom),vec2f(left,top),vec2f(left,top),vec2f(right,bottom),vec2f(right,top));let screenPos = positions[vertexType];let clipX = screenPos.x * 2.0 - 1.0;let clipY = 1.0 - screenPos.y * 2.0;out.pos = vec4f(clipX,clipY,0.0,1.0);out.normY = normMaxY;return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{let series = allSeries[in.seriesIdx];return vec4f(series.color.rgb,0.85);}`;var t,R,v=new Map,S=!1,p={},a={},N=0,P=0,_=!1,O=null,W=new ArrayBuffer(112),j=new Float32Array(W),F=new Uint32Array(W),A,q=(e,i)=>{if(!e.seriesStorageBuffer||e.series.length===0)return;let n=new Float32Array(e.series.length*8),l=new Uint32Array(n.buffer);for(let u=0;u<e.series.length;u++){let o=e.series[u],x=u*8;n[x+0]=o.colorR,n[x+1]=o.colorG,n[x+2]=o.colorB,n[x+3]=1,l[x+4]=o.visibleStart,l[x+5]=o.visibleCount,n[x+6]=i?.[u]??e.pointSize,n[x+7]=0}t.queue.writeBuffer(e.seriesStorageBuffer,0,n)},E=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(S?[0.11,0.11,0.12]:[0.98,0.98,0.98]);u.set([e.width,e.height,e.minX+e.panX*x,e.minX+e.panX*x+x/e.zoomX,e.minY+e.panY*f,e.minY+e.panY*f+f/e.zoomY],0),o[6]=i.pointCount,u.set([S?1:0,...Y,l??e.pointSize,e.minY,e.maxY,e.minX,e.maxX],7),o[16]=i.visibleStart,o[17]=i.visibleCount,o[18]=0,o[19]=e.maxSamplesPerPixel,o[20]=e.series.length,t.queue.writeBuffer(e.uniformBuffer,0,W)},T=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});a[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),a[`${e}Render`]=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Render`]]}),vertex:{module:x,entryPoint:"vs"},fragment:{module:x,entryPoint:"fs",targets:[{format:"rgba8unorm",blend:u}]},primitive:{topology:l}})};async function J(){if(t)return!0;if(!navigator.gpu)return postMessage({type:"error",message:"WebGPU not supported"}),!1;let e=await navigator.gpu.requestAdapter();if(!e)return postMessage({type:"error",message:"No GPU adapter found"}),!1;return t=await e.requestDevice({requiredLimits:{maxBufferSize:e.limits.maxBufferSize,maxStorageBufferBindingSize:e.limits.maxStorageBufferBindingSize}}),R=navigator.gpu.getPreferredCanvasFormat(),t.lost.then((i)=>{postMessage({type:"error",message:`GPU device lost: ${i.reason} - ${i.message}`})}),K(),postMessage({type:"gpu-ready"}),!0}function K(){p.lineCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}}]}),p.lineRender=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]}),p.scatterCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,storageTexture:{access:"write-only",format:"rgba8unorm"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:5,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}}]}),p.fxaaRender=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,sampler:{}}]}),p.boxCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:5,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}}]}),p.boxRender=p.lineRender,T("line",y,L,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),T("box",H,U,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:D});a.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:C});a.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:R}]},primitive:{topology:"triangle-strip"}}),A=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){let x=t?i.getContext("webgpu"):null;if(!x){postMessage({type:"error",message:`Failed to initialize WebGPU context: ${e}`});return}try{x.configure({device:t,format:R,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Failed to configure WebGPU context: ${e}`,err:M.toString()});return}let f=t.limits.maxTextureDimension2D,Y=Math.min(Math.floor(Number(i.width)||800),f),d=Math.min(Math.floor(Number(i.height)||400),f),g=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),I={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:g,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:Y,height:d,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{$(I)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,I),postMessage({type:"chart-registered",id:e})}function $(e){if(e.outputTexture)e.outputTexture.destroy();let i=Math.max(1,e.width),n=Math.max(1,e.height),l=e.type==="scatter"?GPUTextureUsage.STORAGE_BINDING|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT;e.outputTexture=t.createTexture({size:[i,n],format:"rgba8unorm",usage:l}),e.outputTextureView=e.outputTexture.createView(),e.fxaaBindGroup=t.createBindGroup({layout:p.fxaaRender,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:e.outputTextureView},{binding:2,resource:A}]})}function V(e,i,n){if(!e.seriesStorageBuffer)return;if(e.type==="scatter"){if(i.seriesIndexBuffer)i.seriesIndexBuffer.destroy();i.seriesIndexBuffer=t.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let l=new Uint32Array([n,0,0,0]);t.queue.writeBuffer(i.seriesIndexBuffer,0,l),i.computeBindGroup=t.createBindGroup({layout:p.scatterCompute,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.dataX}},{binding:2,resource:{buffer:i.dataY}},{binding:3,resource:e.outputTextureView},{binding:4,resource:{buffer:e.seriesStorageBuffer}},{binding:5,resource:{buffer:i.seriesIndexBuffer}}]})}else{let l=e.type==="line"?p.lineCompute:p.boxCompute,u=e.type==="line"?p.lineRender:p.boxRender;if(i.lineBuffer)i.lineBuffer.destroy();if(i.lineBuffer=t.createBuffer({size:e.width*16,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.VERTEX}),e.type==="box"){if(i.seriesIndexBuffer)i.seriesIndexBuffer.destroy();i.seriesIndexBuffer=t.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let x=new Uint32Array([n,0,0,0]);t.queue.writeBuffer(i.seriesIndexBuffer,0,x)}let o=[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.dataX}},{binding:2,resource:{buffer:i.dataY}},{binding:3,resource:{buffer:i.lineBuffer}},{binding:4,resource:{buffer:e.seriesStorageBuffer}}];if(e.type==="box"&&i.seriesIndexBuffer)o.push({binding:5,resource:{buffer:i.seriesIndexBuffer}});i.computeBindGroup=t.createBindGroup({layout:l,entries:o}),i.renderBindGroup=t.createBindGroup({layout:u,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.lineBuffer}},{binding:2,resource:{buffer:e.seriesStorageBuffer}}]})}}function Z(e,i,n){if(i===e.width&&n===e.height)return;if(i<=0||n<=0)return;e.width=i,e.height=n,e.canvas.width=i,e.canvas.height=n;try{$(e);for(let l=0;l<e.series.length;l++)V(e,e.series[l],l)}catch(l){postMessage({type:"error",message:`resize failed for chart ${e.id}: ${l}`})}}function G(e,i,n){let l=v.get(e);if(!l||!t){if(!l)postMessage({type:"error",message:`update-series failed: chart ${e} not found`});return}try{l.minX=n.minX,l.maxX=n.maxX,l.minY=n.minY,l.maxY=n.maxY;for(let u of l.series){if(u.dataX.destroy(),u.dataY.destroy(),u.lineBuffer)u.lineBuffer.destroy();if(u.seriesIndexBuffer)u.seriesIndexBuffer.destroy()}if(l.series=[],l.seriesStorageBuffer)l.seriesStorageBuffer.destroy();if(i.length>0)l.seriesStorageBuffer=t.createBuffer({size:Math.max(32,i.length*32),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});for(let u of i){let o=t.createBuffer({size:Math.max(16,u.dataX.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),x=t.createBuffer({size:Math.max(16,u.dataY.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});t.queue.writeBuffer(o,0,u.dataX),t.queue.writeBuffer(x,0,u.dataY);let f={label:u.label,colorR:u.colorR,colorG:u.colorG,colorB:u.colorB,dataX:o,dataY:x,lineBuffer:null,seriesIndexBuffer:null,pointCount:u.dataX.length,visibleStart:0,visibleCount:u.dataX.length,computeBindGroup:null,renderBindGroup:null};l.series.push(f)}for(let u=0;u<l.series.length;u++)V(l,l.series[u],u);postMessage({type:"bounds-update",id:e,...n})}catch(u){postMessage({type:"error",message:`update-series failed for chart ${e}: ${u}`})}}function z(e){if(!e.ctx)return;if(e.width===0||e.height===0)return;if(e.series.length===0)return;let i;try{i=e.ctx.getCurrentTexture().createView()}catch{return}k(e,i)}function k(e,i){let n=t.createCommandEncoder(),l;if(e.type==="scatter"){let f=e.width*e.height*4;l=e.series.map((Y)=>{let d=Math.max(1,Y.visibleCount),g=Math.PI*e.pointSize*e.pointSize;if(d*g>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)E(e,e.series[0],0);n.beginRenderPass({colorAttachments:[{view:e.outputTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]}).end();for(let x=0;x<e.series.length;x++){let f=e.series[x];if(f.pointCount===0)continue;if(e.type==="scatter"){let d=Math.ceil(f.visibleCount/m),g=Math.min(d,65535),I=Math.ceil(d/65535),M=new Uint32Array([g*m]);t.queue.writeBuffer(e.uniformBuffer,72,M);let r=n.beginComputePass();r.setPipeline(a.scatterCompute),r.setBindGroup(0,f.computeBindGroup),r.dispatchWorkgroups(g,I),r.end()}else{E(e,f,x);let Y=e.type==="line"?a.lineCompute:a.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/m)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?a.lineRender:a.boxRender,f=e.type==="line"?Math.max(0,e.width*4-2):e.width*6,Y=n.beginRenderPass({colorAttachments:[{view:e.outputTextureView,loadOp:"load",storeOp:"store"}]});Y.setPipeline(x);for(let d=0;d<e.series.length;d++){let g=e.series[d];if(g.pointCount===0)continue;Y.setBindGroup(0,g.renderBindGroup),Y.draw(f,1,0,d)}Y.end()}let o=n.beginRenderPass({colorAttachments:[{view:i,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]});if(o.setPipeline(a.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function s(){if(!_)_=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)s()}function B(){let e=!1;for(let i of v.values())if(i.dirty=!0,i.visible)e=!0;if(e)s()}function c(){let e=0;for(let i of v.values())if(i.visible&&i.width>0)e++;return e}function h(){if(O!==null)return;O=setInterval(()=>{postMessage({type:"stats",fps:N,renderMs:P,totalCharts:v.size,activeCharts:c()}),N=0},1000)}function ee(){_=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;P=performance.now()-e,N++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(S=n.isDark||!1,await J())h();break;case"theme":S=n.isDark,B();break;case"register-chart":Q(n.id,n.canvas,n.chartType||"scatter",n.pointSize??3,n.maxSamplesPerPixel??100,n.bgColor??null);{let l=v.get(n.id);if(l)b(l)}break;case"unregister-chart":{let l=v.get(n.id);if(l){try{l.ctx.unconfigure()}catch{}if(l.uniformBuffer.destroy(),l.seriesStorageBuffer)l.seriesStorageBuffer.destroy();if(l.outputTexture)l.outputTexture.destroy();for(let u of l.series){if(u.dataX.destroy(),u.dataY.destroy(),u.lineBuffer)u.lineBuffer.destroy();if(u.seriesIndexBuffer)u.seriesIndexBuffer.destroy()}v.delete(n.id)}postMessage({type:"chart-unregistered",id:n.id});break}case"set-point-size":{let l=v.get(n.id);if(l)l.pointSize=Math.max(1,Math.min(8,n.pointSize)),b(l);break}case"set-max-samples":{let l=v.get(n.id);if(l)l.maxSamplesPerPixel=Math.max(0,n.maxSamplesPerPixel|0),b(l);break}case"set-style":{let l=v.get(n.id);if(l){if(n.bgColor!==void 0)l.bgColor=n.bgColor;b(l)}break}case"update-series":{G(n.id,n.series,n.bounds);let l=v.get(n.id);if(l)b(l);break}case"set-visibility":{let l=v.get(n.id);if(l){if(l.visible=n.visible,n.visible&&l.dirty)s()}break}case"view-transform":{let l=v.get(n.id);if(l)l.panX=n.panX,l.panY=n.panY,l.zoomX=Math.max(0.1,Math.min(1e6,n.zoomX)),l.zoomY=Math.max(0.1,Math.min(1e6,n.zoomY)),b(l);break}case"resize":{let l=v.get(n.id);if(l&&n.width>0&&n.height>0)Z(l,n.width,n.height),b(l);break}case"batch-view-transform":{let l=Math.max(0.1,Math.min(1e6,n.zoomX)),u=Math.max(0.1,Math.min(1e6,n.zoomY));for(let o of n.transforms){let x=v.get(o.id);if(x)x.panX=n.panX,x.panY=n.panY,x.zoomX=l,x.zoomY=u,x.dirty=!0}s();break}case"sync-view":for(let l of v.values())l.panX=n.panX,l.panY=n.panY,l.zoomX=n.zoomX,l.zoomY=n.zoomY;B();break}};\n';
21
+ var WORKER_CODE = 'var k="fn luma(c:vec4f)->f32{return dot(c.rgb,vec3f(.299,.587,.114));}fn laaa(uv:vec2f,t:texture_2d<f32>,s:sampler)->vec4f{let r=1./vec2f(textureDimensions(t));let m=textureSample(t,s,uv);let n=textureSample(t,s,uv+vec2f(0.,-r.y));let e=textureSample(t,s,uv+vec2f(r.x,0.));let w=textureSample(t,s,uv+vec2f(-r.x,0.));let sv=textureSample(t,s,uv+vec2f(0.,r.y));let lm=luma(m);let ln=luma(n);let le=luma(e);let lw=luma(w);let ls=luma(sv);let lo=min(lm,min(min(ln,ls),min(le,lw)));let hi=max(lm,max(max(ln,ls),max(le,lw)));let rng=hi-lo;if(rng<max(.0833,hi*.166)){return m;}return mix(m,(m+n+e+w+sv)*.2,min(rng*3.,1.));}struct BV{@builtin(position)p:vec4f,@location(0)uv:vec2f}@group(0)@binding(0)var inputTex:texture_2d<f32>;@group(0)@binding(1)var samp:sampler;@vertex fn vs(@builtin(vertex_index)i:u32)->BV{var p=array<vec2f,4>(vec2f(-1,-1),vec2f(1,-1),vec2f(-1,1),vec2f(1,1));var u=array<vec2f,4>(vec2f(0,1),vec2f(1,1),vec2f(0,0),vec2f(1,0));return BV(vec4f(p[i],0,1),u[i]);}@fragment fn fs(v:BV)->@location(0)vec4f{return laaa(v.uv,inputTex,samp);}";var T={INIT:0,THEME:1,REGISTER_RENDERER:2,REGISTER_CHART:3,UNREGISTER_CHART:4,UPDATE_SERIES:5,RESIZE:6,VIEW_TRANSFORM:7,BATCH_VIEW_TRANSFORM:8,SET_VISIBILITY:9,SET_STYLE:10,SET_UNIFORMS:11,GPU_READY:12,ERROR:13,STATS:14},Q={NO_GPU:"e1:no-gpu",NO_ADAPTER:"e2:no-adapter",DEVICE_LOST:"e3:device-lost",NOT_READY:"e4:not-ready",COMPILE:"e5:compile",CTX_GET:"e6:ctx-get",CTX_CFG:"e7:ctx-cfg",TEX:"e8:tex",BIND_S:"e9:bind-s",BIND_C:"e10:bind-c",UPDATE:"e11:update",NO_RENDERER:"e12:no-renderer",RESIZE:"e13:resize"};var j,U,J=new Map,R=new Map,w=!1,I,B,l,P=0,M=0,C=!1,p=null,L=new ArrayBuffer(64),b=new Float32Array(L),n=new Uint32Array(L);function D(_){let O=GPUBufferUsage.COPY_DST;for(let N of _)switch(N.toUpperCase()){case"STORAGE":O|=GPUBufferUsage.STORAGE;break;case"VERTEX":O|=GPUBufferUsage.VERTEX;break;case"UNIFORM":O|=GPUBufferUsage.UNIFORM;break;case"COPY_SRC":O|=GPUBufferUsage.COPY_SRC;break;case"COPY_DST":O|=GPUBufferUsage.COPY_DST;break;case"INDEX":O|=GPUBufferUsage.INDEX;break;case"INDIRECT":O|=GPUBufferUsage.INDIRECT;break}return O}function i(_,O,N){let m=N==="compute"?GPUShaderStage.COMPUTE:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT;if(_==="uniforms"||_==="custom-uniforms"||_==="series-index")return{visibility:m,buffer:{type:"uniform"}};if(_==="render-target"){if(O)return{visibility:GPUShaderStage.COMPUTE,storageTexture:{access:"write-only",format:"rgba8unorm"}};return{visibility:m,texture:{sampleType:"float"}}}if(O)return{visibility:m,buffer:{type:"storage"}};return{visibility:m,buffer:{type:"read-only-storage"}}}function z(_,O,N,m,W){switch(_){case"uniforms":return{buffer:N.uniformBuffer};case"custom-uniforms":return{buffer:N.customUniformBuffer};case"series-info":return{buffer:N.seriesStorageBuffer};case"render-target":return N.outputTextureView;case"x-data":return{buffer:m.dataX};case"y-data":return{buffer:m.dataY};case"series-index":return{buffer:m.seriesIndexBuffer}}if(_.endsWith("-data")){let Y=_.slice(0,-5);return{buffer:m.extraBuffers.get(Y)}}if(W.config.bufferDefs.find((Y)=>Y.name===_)?.perSeries)return{buffer:m.seriesBuffers.get(_)};return{buffer:N.chartBuffers.get(_)}}function d(_){let O=new Map,N=new Map;for(let m=0;m<_.passes.length;m++){let W=_.passes[m],X=W.bindings.map((v)=>({binding:v.binding,...i(v.source,v.write,W.type)})),Y=j.createBindGroupLayout({entries:X});N.set(`pass-${m}`,Y);let H=j.createPipelineLayout({bindGroupLayouts:[Y]}),G=j.createShaderModule({code:_.shaders[W.shader]});if(W.type==="compute")O.set(`pass-${m}`,j.createComputePipeline({layout:H,compute:{module:G,entryPoint:"main"}}));else O.set(`pass-${m}`,j.createRenderPipeline({layout:H,vertex:{module:G,entryPoint:"vs"},fragment:{module:G,entryPoint:"fs",targets:[{format:"rgba8unorm",blend:W.blend}]},primitive:{topology:W.topology??"triangle-list"}}))}return{config:_,pipelines:O,passLayouts:N}}function h(_){if(!_.seriesStorageBuffer||_.series.length===0)return;let O=new Float32Array(_.series.length*8),N=new Uint32Array(O.buffer);for(let m=0;m<_.series.length;m++){let W=_.series[m],X=m*8;O[X+0]=W.colorR,O[X+1]=W.colorG,O[X+2]=W.colorB,O[X+3]=1,N[X+4]=W.visibleStart,N[X+5]=W.visibleCount}j.queue.writeBuffer(_.seriesStorageBuffer,0,O)}function E(_,O){let N=b,m=n,W=_.maxX-_.minX,X=_.maxY-_.minY,Y=_.bgColor??(w?[0.11,0.11,0.12]:[0.98,0.98,0.98]);N[0]=_.width,N[1]=_.height,N[2]=_.minX+_.panX*W,N[3]=_.minX+_.panX*W+W/_.zoomX,N[4]=_.minY+_.panY*X,N[5]=_.minY+_.panY*X+X/_.zoomY,m[6]=O.pointCount,m[7]=_.series.length,m[8]=w?1:0,N[9]=Y[0],N[10]=Y[1],N[11]=Y[2],N[12]=_.minX,N[13]=_.maxX,N[14]=_.minY,N[15]=_.maxY,j.queue.writeBuffer(_.uniformBuffer,0,L)}function y(_,O){if(!_.customUniformBuffer||O.uniformDefs.length===0)return;let N=O.uniformDefs.length,m=Math.ceil(N*4/16)*16,W=new ArrayBuffer(m),X=new Float32Array(W),Y=new Uint32Array(W);for(let H=0;H<N;H++){let G=O.uniformDefs[H],v=_.customUniformValues[G.name]??G.default;if(G.type==="u32")Y[H]=v>>>0;else X[H]=v}j.queue.writeBuffer(_.customUniformBuffer,0,W)}function g(_,O,N){for(let[,m]of _.chartBuffers)m.destroy();_.chartBuffers.clear();for(let m of O.config.bufferDefs)if(!m.perSeries){let W=Math.max(16,N[m.name]??16);_.chartBuffers.set(m.name,j.createBuffer({size:W,usage:D(m.usages)}))}if(O.config.uniformDefs.length>0){if(_.customUniformBuffer)_.customUniformBuffer.destroy();let m=Math.max(16,Math.ceil(O.config.uniformDefs.length*4/16)*16);_.customUniformBuffer=j.createBuffer({size:m,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),y(_,O.config)}}function u(_,O,N,m){for(let[,W]of _.seriesBuffers)W.destroy();_.seriesBuffers.clear();for(let W of N.config.bufferDefs)if(W.perSeries){let X=Math.max(16,m[W.name]??16);_.seriesBuffers.set(W.name,j.createBuffer({size:X,usage:D(W.usages)}))}if(_.seriesIndexBuffer)_.seriesIndexBuffer.destroy();_.seriesIndexBuffer=j.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),j.queue.writeBuffer(_.seriesIndexBuffer,0,new Uint32Array([O,0,0,0]))}function f(_,O){if(!_.seriesStorageBuffer)return;for(let N=0;N<_.series.length;N++){let m=_.series[N];m.passBindGroups=[];for(let W=0;W<O.config.passes.length;W++){let X=O.config.passes[W];if(!X.perSeries){m.passBindGroups.push(null);continue}let Y=O.passLayouts.get(`pass-${W}`);try{let H=X.bindings.map((G)=>({binding:G.binding,resource:z(G.source,G.write,_,m,O)}));m.passBindGroups.push(j.createBindGroup({layout:Y,entries:H}))}catch(H){postMessage({type:T.ERROR,code:Q.BIND_S}),m.passBindGroups.push(null)}}}_.chartPassBindGroups=[];for(let N=0;N<O.config.passes.length;N++){let m=O.config.passes[N];if(m.perSeries){_.chartPassBindGroups.push(null);continue}let W=O.passLayouts.get(`pass-${N}`);try{let X=m.bindings.map((Y)=>({binding:Y.binding,resource:z(Y.source,Y.write,_,null,O)}));_.chartPassBindGroups.push(j.createBindGroup({layout:W,entries:X}))}catch(X){postMessage({type:T.ERROR,code:Q.BIND_C}),_.chartPassBindGroups.push(null)}}}function S(_){if(_.outputTexture)_.outputTexture.destroy();let O=Math.max(1,_.width),N=Math.max(1,_.height),m=R.get(_.rendererName),W=GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT;if(m){for(let X of m.config.passes)if(X.type==="compute"){for(let Y of X.bindings)if(Y.source==="render-target"&&Y.write){W|=GPUTextureUsage.STORAGE_BINDING;break}}}_.outputTexture=j.createTexture({size:[O,N],format:"rgba8unorm",usage:W}),_.outputTextureView=_.outputTexture.createView(),_.blitBindGroup=j.createBindGroup({layout:B,entries:[{binding:0,resource:_.outputTextureView},{binding:1,resource:l}]})}function c(_){let O=R.get(_.rendererName);if(!O)return;if(!_.ctx||_.width===0||_.height===0||_.series.length===0)return;let N;try{N=_.ctx.getCurrentTexture().createView()}catch{return}let m=j.createCommandEncoder();if(h(_),_.series.length>0)E(_,_.series[0]);m.beginRenderPass({colorAttachments:[{view:_.outputTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]}).end();for(let Y=0;Y<O.config.passes.length;Y++){let H=O.config.passes[Y],G=O.pipelines.get(`pass-${Y}`);if(!G)continue;if(H.type==="compute")if(H.perSeries)for(let v=0;v<_.series.length;v++){let V=_.series[v];if(V.pointCount===0||V.hidden)continue;E(_,V);let q=_.perSeriesPassMeta[v]?.[Y]?.dispatch??{x:1},$=V.passBindGroups[Y];if(!$)continue;let Z=m.beginComputePass();Z.setPipeline(G),Z.setBindGroup(0,$),Z.dispatchWorkgroups(q.x,q.y??1,q.z??1),Z.end()}else{let v=_.chartPassBindGroups[Y];if(!v)continue;let K=_.perSeriesPassMeta[0]?.[Y]?.dispatch??{x:1},q=m.beginComputePass();q.setPipeline(G),q.setBindGroup(0,v),q.dispatchWorkgroups(K.x,K.y??1,K.z??1),q.end()}else if(H.type==="render"){let V=_.perSeriesPassMeta[0]?.[Y]?.draw??0,K=m.beginRenderPass({colorAttachments:[{view:_.outputTextureView,loadOp:H.loadOp??"load",storeOp:"store"}]});if(K.setPipeline(G),H.perSeries)for(let q=0;q<_.series.length;q++){let $=_.series[q];if($.pointCount===0||$.hidden)continue;let Z=$.passBindGroups[Y];if(!Z)continue;K.setBindGroup(0,Z),K.draw(V,1,0,q)}else{let q=_.chartPassBindGroups[Y];if(q)K.setBindGroup(0,q),K.draw(V,1,0,0)}K.end()}}let X=m.beginRenderPass({colorAttachments:[{view:N,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]});if(X.setPipeline(I),_.blitBindGroup)X.setBindGroup(0,_.blitBindGroup);X.draw(4),X.end(),j.queue.submit([m.finish()])}function F(){if(!C)C=!0,requestAnimationFrame(t)}function A(_){if(_.dirty=!0,_.visible)F()}function s(){let _=!1;for(let O of J.values())if(O.dirty=!0,O.visible)_=!0;if(_)F()}function t(){C=!1;let _=performance.now();for(let O of J.values())if(O.visible&&O.dirty&&O.width>0)c(O),O.dirty=!1;M=performance.now()-_,P++}function e(){let _=0;for(let O of J.values())if(O.visible&&O.width>0)_++;return _}async function a(){if(j)return!0;if(!navigator.gpu)return postMessage({type:T.ERROR,code:Q.NO_GPU}),!1;let _=await navigator.gpu.requestAdapter();if(!_)return postMessage({type:T.ERROR,code:Q.NO_ADAPTER}),!1;j=await _.requestDevice({requiredLimits:{maxBufferSize:_.limits.maxBufferSize,maxStorageBufferBindingSize:_.limits.maxStorageBufferBindingSize}}),U=navigator.gpu.getPreferredCanvasFormat(),j.lost.then((N)=>{postMessage({type:T.ERROR,code:Q.DEVICE_LOST})}),B=j.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{}}]});let O=j.createShaderModule({code:k});return I=j.createRenderPipeline({layout:j.createPipelineLayout({bindGroupLayouts:[B]}),vertex:{module:O,entryPoint:"vs"},fragment:{module:O,entryPoint:"fs",targets:[{format:U}]},primitive:{topology:"triangle-strip"}}),l=j.createSampler({magFilter:"linear",minFilter:"linear"}),p=setInterval(()=>{postMessage({type:T.STATS,fps:P,renderMs:M,totalCharts:J.size,activeCharts:e()}),P=0},1000),postMessage({type:T.GPU_READY}),!0}function o(_){_.dataX.destroy(),_.dataY.destroy();for(let[,O]of _.extraBuffers)O.destroy();for(let[,O]of _.seriesBuffers)O.destroy();if(_.seriesIndexBuffer)_.seriesIndexBuffer.destroy()}function r(_,O,N,m,W){let X=J.get(_);if(!X||!j)return;let Y=R.get(X.rendererName);if(!Y){postMessage({type:T.ERROR,code:Q.NO_RENDERER});return}try{X.minX=N.minX,X.maxX=N.maxX,X.minY=N.minY,X.maxY=N.maxY,X.perSeriesPassMeta=W;for(let H of X.series)o(H);if(X.series=[],X.seriesStorageBuffer)X.seriesStorageBuffer.destroy();if(O.length>0)X.seriesStorageBuffer=j.createBuffer({size:Math.max(32,O.length*32),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});g(X,Y,m);for(let H=0;H<O.length;H++){let G=O[H],v=j.createBuffer({size:Math.max(16,G.dataX.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),V=j.createBuffer({size:Math.max(16,G.dataY.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});j.queue.writeBuffer(v,0,G.dataX),j.queue.writeBuffer(V,0,G.dataY);let K=new Map;for(let[$,Z]of Object.entries(G.extra??{})){let x=j.createBuffer({size:Math.max(16,Z.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});j.queue.writeBuffer(x,0,Z),K.set($,x)}let q={label:G.label,colorR:G.colorR,colorG:G.colorG,colorB:G.colorB,dataX:v,dataY:V,extraBuffers:K,seriesBuffers:new Map,seriesIndexBuffer:null,pointCount:G.dataX.length,visibleStart:0,visibleCount:G.dataX.length,hidden:G.hidden??!1,passBindGroups:[]};X.series.push(q),u(q,H,Y,m)}f(X,Y)}catch(H){postMessage({type:T.ERROR,code:Q.UPDATE})}}self.onmessage=async(_)=>{let{type:O,...N}=_.data;switch(O){case T.INIT:w=N.isDark||!1,await a();break;case T.THEME:w=N.isDark,s();break;case T.REGISTER_RENDERER:{if(!j){postMessage({type:T.ERROR,code:Q.NOT_READY});break}let m={name:N.name,shaders:N.shaders,passes:N.passes,bufferDefs:N.bufferDefs??[],uniformDefs:N.uniformDefs??[]};try{R.set(N.name,d(m))}catch(W){postMessage({type:T.ERROR,code:Q.COMPILE})}break}case T.REGISTER_CHART:{if(!j)break;let m=N.canvas.getContext("webgpu");if(!m){postMessage({type:T.ERROR,code:Q.CTX_GET});break}try{m.configure({device:j,format:U,alphaMode:"premultiplied"})}catch(G){postMessage({type:T.ERROR,code:Q.CTX_CFG});break}let W=j.limits.maxTextureDimension2D,X=Math.min(Math.max(1,Math.floor(Number(N.canvas.width)||800)),W),Y=Math.min(Math.max(1,Math.floor(Number(N.canvas.height)||400)),W),H={id:N.id,canvas:N.canvas,ctx:m,rendererName:N.rendererName,visible:!0,series:[],uniformBuffer:j.createBuffer({size:64,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,blitBindGroup:null,chartBuffers:new Map,customUniformBuffer:null,customUniformValues:N.customUniformValues??{},chartPassBindGroups:[],perSeriesPassMeta:N.perSeriesPassMeta??[],width:X,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,maxY:1,minY:0,bgColor:N.bgColor??null,dirty:!0};try{S(H)}catch(G){postMessage({type:T.ERROR,code:Q.TEX});break}J.set(N.id,H);break}case T.UNREGISTER_CHART:{let m=J.get(N.id);if(m){try{m.ctx.unconfigure()}catch{}if(m.uniformBuffer.destroy(),m.seriesStorageBuffer)m.seriesStorageBuffer.destroy();if(m.outputTexture)m.outputTexture.destroy();if(m.customUniformBuffer)m.customUniformBuffer.destroy();for(let[,W]of m.chartBuffers)W.destroy();for(let W of m.series)o(W);J.delete(N.id)}break}case T.UPDATE_SERIES:{r(N.id,N.series,N.bounds,N.bufferSizes??{},N.perSeriesPassMeta??[]);let m=J.get(N.id);if(m)A(m);break}case T.RESIZE:{let m=J.get(N.id);if(!m||N.width<=0||N.height<=0)break;let W=j.limits.maxTextureDimension2D,X=Math.min(N.width,W),Y=Math.min(N.height,W);if(X===m.width&&Y===m.height)break;if(m.width=X,m.height=Y,m.canvas.width=X,m.canvas.height=Y,N.perSeriesPassMeta?.length>0)m.perSeriesPassMeta=N.perSeriesPassMeta;let H=R.get(m.rendererName);try{if(S(m),H&&N.bufferSizes){g(m,H,N.bufferSizes);for(let G=0;G<m.series.length;G++)u(m.series[G],G,H,N.bufferSizes);f(m,H)}}catch(G){postMessage({type:T.ERROR,code:Q.RESIZE})}A(m);break}case T.VIEW_TRANSFORM:{let m=J.get(N.id);if(m)m.panX=N.panX,m.panY=N.panY,m.zoomX=Math.max(0.1,Math.min(1e6,N.zoomX)),m.zoomY=Math.max(0.1,Math.min(1e6,N.zoomY)),A(m);break}case T.BATCH_VIEW_TRANSFORM:{let m=Math.max(0.1,Math.min(1e6,N.zoomX)),W=Math.max(0.1,Math.min(1e6,N.zoomY));for(let X of N.transforms){let Y=J.get(X.id);if(Y)Y.panX=N.panX,Y.panY=N.panY,Y.zoomX=m,Y.zoomY=W,Y.dirty=!0}F();break}case T.SET_VISIBILITY:{let m=J.get(N.id);if(m){if(m.visible=N.visible,N.visible&&m.dirty)F()}break}case T.SET_STYLE:{let m=J.get(N.id);if(m){if(N.bgColor!==void 0)m.bgColor=N.bgColor;if(N.hiddenSeries!==void 0)for(let W=0;W<m.series.length;W++)m.series[W].hidden=N.hiddenSeries.has(W);A(m)}break}case T.SET_UNIFORMS:{let m=J.get(N.id);if(!m)break;Object.assign(m.customUniformValues,N.values);let W=R.get(m.rendererName);if(W)y(m,W.config);A(m);break}}};\n';
22
+
23
+ // src/msg.ts
24
+ var M = {
25
+ INIT: 0,
26
+ THEME: 1,
27
+ REGISTER_RENDERER: 2,
28
+ REGISTER_CHART: 3,
29
+ UNREGISTER_CHART: 4,
30
+ UPDATE_SERIES: 5,
31
+ RESIZE: 6,
32
+ VIEW_TRANSFORM: 7,
33
+ BATCH_VIEW_TRANSFORM: 8,
34
+ SET_VISIBILITY: 9,
35
+ SET_STYLE: 10,
36
+ SET_UNIFORMS: 11,
37
+ GPU_READY: 12,
38
+ ERROR: 13,
39
+ STATS: 14
40
+ };
18
41
 
19
42
  // src/chart-library.ts
20
- var globalPlugins = [];
21
- function registerPlugin(plugin) {
22
- if (!globalPlugins.some((p) => p.name === plugin.name)) {
23
- globalPlugins.push(plugin);
43
+ class Chart {
44
+ id;
45
+ _mgr;
46
+ constructor(id, mgr) {
47
+ this.id = id;
48
+ this._mgr = mgr;
49
+ }
50
+ get _c() {
51
+ return this._mgr["charts"].get(this.id);
52
+ }
53
+ setData(series) {
54
+ this._mgr.updateSeries(this.id, series);
55
+ }
56
+ configure(patch) {
57
+ const c = this._c;
58
+ if (!c)
59
+ return;
60
+ Object.assign(c.config, patch);
61
+ const uniformNames = new Set((c.renderer.uniforms ?? []).map((u) => u.name));
62
+ const workerValues = {};
63
+ for (const key of Object.keys(patch)) {
64
+ const val = patch[key];
65
+ if (typeof val === "number" && uniformNames.has(key)) {
66
+ workerValues[key] = val;
67
+ }
68
+ }
69
+ if (Object.keys(workerValues).length > 0) {
70
+ Object.assign(c.customUniforms, workerValues);
71
+ this._mgr["worker"]?.postMessage({
72
+ type: M.SET_UNIFORMS,
73
+ id: this.id,
74
+ values: workerValues
75
+ });
76
+ }
77
+ if ("hiddenSeries" in patch) {
78
+ this._mgr["worker"]?.postMessage({
79
+ type: M.SET_STYLE,
80
+ id: this.id,
81
+ hiddenSeries: patch.hiddenSeries ?? new Set
82
+ });
83
+ }
84
+ if ("bgColor" in patch && patch.bgColor !== undefined) {
85
+ const [r, g, b] = patch.bgColor;
86
+ const wrap = c.el.querySelector("div");
87
+ if (wrap)
88
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
89
+ this._mgr["worker"]?.postMessage({
90
+ type: M.SET_STYLE,
91
+ id: this.id,
92
+ bgColor: patch.bgColor
93
+ });
94
+ }
95
+ this._mgr.requestRender(this.id);
96
+ this._mgr.drawChart(c);
97
+ }
98
+ addPlugin(plugin) {
99
+ const c = this._c;
100
+ if (!c || c.plugins.some((p) => p.name === plugin.name))
101
+ return;
102
+ const wrap = c.el.querySelector("div");
103
+ plugin.install?.(c, wrap);
104
+ c.plugins.push(plugin);
105
+ this._mgr.drawChart(c);
106
+ }
107
+ removePlugin(name) {
108
+ const c = this._c;
109
+ if (!c)
110
+ return;
111
+ const idx = c.plugins.findIndex((p) => p.name === name);
112
+ if (idx >= 0) {
113
+ c.plugins[idx].uninstall?.(c);
114
+ c.plugins.splice(idx, 1);
115
+ this._mgr.drawChart(c);
116
+ }
117
+ }
118
+ hasPlugin(name) {
119
+ return this._c?.plugins.some((p) => p.name === name) ?? false;
120
+ }
121
+ resetView() {
122
+ this._mgr.resetView(this.id);
123
+ }
124
+ destroy() {
125
+ this._mgr.destroy(this.id);
24
126
  }
25
127
  }
26
- function unregisterPlugin(name) {
27
- const idx = globalPlugins.findIndex((p) => p.name === name);
28
- if (idx >= 0)
29
- globalPlugins.splice(idx, 1);
128
+ var _colorEl = null;
129
+ function parseColor(c) {
130
+ if (typeof c !== "string")
131
+ return c;
132
+ if (!_colorEl) {
133
+ _colorEl = document.createElement("i");
134
+ _colorEl.style.cssText = "display:none";
135
+ document.body.appendChild(_colorEl);
136
+ }
137
+ _colorEl.style.color = c;
138
+ const m = getComputedStyle(_colorEl).color.match(/\d+/g);
139
+ return { r: +m[0] / 255, g: +m[1] / 255, b: +m[2] / 255 };
30
140
  }
31
- function getPlugins() {
32
- return globalPlugins;
141
+ function resizeCanvas(canvas, cssW, cssH) {
142
+ const dpr = devicePixelRatio || 1;
143
+ canvas.width = Math.round(cssW * dpr);
144
+ canvas.height = Math.round(cssH * dpr);
145
+ if (canvas instanceof HTMLCanvasElement) {
146
+ canvas.style.width = `${cssW}px`;
147
+ canvas.style.height = `${cssH}px`;
148
+ }
33
149
  }
34
150
 
35
- class ChartManager {
151
+ class _ChartManager {
36
152
  static instance = null;
37
- static MARGIN = { left: 55, right: 10, top: 8, bottom: 45 };
38
- static HOVER_MAX_DISTANCE_PX = 50;
39
- static MIN_ZOOM = 0.1;
40
- static MAX_ZOOM = 1e7;
41
- static DEFAULT_LABEL_SIZE = 12;
42
- static DEFAULT_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';
43
- static resizeCanvas(canvas, cssWidth, cssHeight) {
44
- const dpr = devicePixelRatio || 1;
45
- canvas.width = Math.round(cssWidth * dpr);
46
- canvas.height = Math.round(cssHeight * dpr);
47
- if (canvas instanceof HTMLCanvasElement) {
48
- canvas.style.width = `${cssWidth}px`;
49
- canvas.style.height = `${cssHeight}px`;
50
- }
51
- }
52
153
  worker = null;
53
154
  charts = new Map;
155
+ renderers = new Map;
156
+ uiPlugins = [];
157
+ pendingRenderers = [];
54
158
  chartIdCounter = 0;
55
- _syncViews = false;
56
159
  _isDark = false;
160
+ _syncViews = false;
57
161
  statsCallbacks = [];
58
162
  currentStats = {
59
163
  fps: 0,
@@ -66,55 +170,56 @@ class ChartManager {
66
170
  constructor() {
67
171
  this._isDark = document.documentElement.classList.contains("dark");
68
172
  this.visibilityObserver = new IntersectionObserver((entries) => {
69
- for (const entry of entries) {
70
- const id = entry.target.dataset.chartId;
71
- if (id) {
72
- const chart = this.charts.get(id);
73
- if (chart) {
74
- chart.visible = entry.isIntersecting;
75
- this.worker?.postMessage({
76
- type: "set-visibility",
77
- id,
78
- visible: entry.isIntersecting
79
- });
80
- if (entry.isIntersecting) {
81
- this.drawChart(chart);
82
- }
83
- }
84
- }
173
+ for (const e of entries) {
174
+ const id = e.target.dataset.chartId;
175
+ if (!id)
176
+ continue;
177
+ const chart = this.charts.get(id);
178
+ if (!chart)
179
+ continue;
180
+ chart.visible = e.isIntersecting;
181
+ this.worker?.postMessage({
182
+ type: M.SET_VISIBILITY,
183
+ id,
184
+ visible: e.isIntersecting
185
+ });
186
+ if (e.isIntersecting)
187
+ this.drawChart(chart);
85
188
  }
86
189
  }, { threshold: 0.01 });
87
190
  this.resizeObserver = new ResizeObserver((entries) => {
88
- for (const entry of entries) {
89
- const id = entry.target.dataset.chartId;
191
+ for (const e of entries) {
192
+ const id = e.target.dataset.chartId;
90
193
  if (!id)
91
194
  continue;
92
195
  const chart = this.charts.get(id);
93
196
  if (!chart)
94
197
  continue;
95
- const { width, height } = entry.contentRect;
198
+ const { width, height } = e.contentRect;
96
199
  if (width <= 0 || height <= 0)
97
200
  continue;
98
201
  chart.width = width;
99
202
  chart.height = height;
100
203
  const dpr = devicePixelRatio || 1;
204
+ resizeCanvas(chart.backCanvas, width, height);
205
+ resizeCanvas(chart.frontCanvas, width, height);
206
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
101
207
  this.worker?.postMessage({
102
- type: "resize",
208
+ type: M.RESIZE,
103
209
  id,
104
210
  width: Math.round(width * dpr),
105
- height: Math.round(height * dpr)
211
+ height: Math.round(height * dpr),
212
+ bufferSizes,
213
+ perSeriesPassMeta
106
214
  });
107
- ChartManager.resizeCanvas(chart.backCanvas, width, height);
108
- ChartManager.resizeCanvas(chart.axisCanvas, width, height);
109
215
  this.drawChart(chart);
110
216
  }
111
217
  });
112
218
  }
113
219
  static getInstance() {
114
- if (!ChartManager.instance) {
115
- ChartManager.instance = new ChartManager;
116
- }
117
- return ChartManager.instance;
220
+ if (!_ChartManager.instance)
221
+ _ChartManager.instance = new _ChartManager;
222
+ return _ChartManager.instance;
118
223
  }
119
224
  get isDark() {
120
225
  return this._isDark;
@@ -122,6 +227,20 @@ class ChartManager {
122
227
  get syncViews() {
123
228
  return this._syncViews;
124
229
  }
230
+ use(plugin) {
231
+ if ("passes" in plugin) {
232
+ const r = plugin;
233
+ this.renderers.set(r.name, r);
234
+ if (this.worker)
235
+ this.sendRendererRegistration(r);
236
+ else
237
+ this.pendingRenderers.push(r);
238
+ } else {
239
+ const p = plugin;
240
+ if (!this.uiPlugins.some((x) => x.name === p.name))
241
+ this.uiPlugins.push(p);
242
+ }
243
+ }
125
244
  async init() {
126
245
  if (this.worker)
127
246
  return true;
@@ -130,12 +249,12 @@ class ChartManager {
130
249
  const blob = new Blob([WORKER_CODE2], {
131
250
  type: "application/javascript"
132
251
  });
133
- const workerUrl = URL.createObjectURL(blob);
134
- this.worker = new Worker(workerUrl, { type: "module" });
252
+ this.worker = new Worker(URL.createObjectURL(blob), {
253
+ type: "module"
254
+ });
135
255
  this.setupWorkerHandlers(resolve);
136
256
  }).catch(() => {
137
- const workerUrl = new URL("./gpu-worker.js", import.meta.url);
138
- this.worker = new Worker(workerUrl, { type: "module" });
257
+ this.worker = new Worker(new URL("./gpu-worker.js", import.meta.url), { type: "module" });
139
258
  this.setupWorkerHandlers(resolve);
140
259
  });
141
260
  });
@@ -146,14 +265,17 @@ class ChartManager {
146
265
  this.worker.onmessage = (e) => {
147
266
  const { type, ...data } = e.data;
148
267
  switch (type) {
149
- case "gpu-ready":
268
+ case M.GPU_READY:
269
+ for (const r of this.pendingRenderers)
270
+ this.sendRendererRegistration(r);
271
+ this.pendingRenderers = [];
150
272
  resolve(true);
151
273
  break;
152
- case "error":
153
- console.error("ChartManager GPU Error:", data.message);
274
+ case M.ERROR:
275
+ console.error("chartai:", data.code);
154
276
  resolve(false);
155
277
  break;
156
- case "stats":
278
+ case M.STATS:
157
279
  this.currentStats = {
158
280
  fps: data.fps,
159
281
  renderMs: data.renderMs,
@@ -163,227 +285,226 @@ class ChartManager {
163
285
  for (const cb of this.statsCallbacks)
164
286
  cb(this.currentStats);
165
287
  break;
166
- case "bounds-update": {
167
- const chart = this.charts.get(data.id);
168
- if (chart) {
169
- chart.bounds = {
170
- minX: data.minX,
171
- maxX: data.maxX,
172
- minY: data.minY,
173
- maxY: data.maxY
174
- };
175
- this.drawChart(chart);
176
- }
177
- break;
178
- }
179
- default:
180
- break;
181
288
  }
182
289
  };
183
290
  this.worker.onerror = (e) => {
184
- console.error("ChartManager Worker Error:", e);
291
+ console.error("chartai:", e);
185
292
  resolve(false);
186
293
  };
187
- this.worker.postMessage({ type: "init", isDark: this._isDark });
294
+ this.worker.postMessage({ type: M.INIT, isDark: this._isDark });
188
295
  }
189
- create(config) {
190
- if (!this.worker) {
191
- throw new Error("ChartManager not initialized. Call init() first.");
296
+ sendRendererRegistration(renderer) {
297
+ const bufferDefs = (renderer.buffers ?? []).map((buf) => ({
298
+ name: buf.name,
299
+ usages: buf.usages,
300
+ perSeries: renderer.passes.some((p) => p.perSeries !== false && p.bindings.some((b) => b.source === buf.name))
301
+ }));
302
+ this.worker?.postMessage({
303
+ type: M.REGISTER_RENDERER,
304
+ name: renderer.name,
305
+ shaders: renderer.shaders,
306
+ passes: renderer.passes.map((p) => ({
307
+ type: p.type,
308
+ shader: p.shader,
309
+ bindings: p.bindings,
310
+ perSeries: p.perSeries !== false,
311
+ topology: p.topology,
312
+ loadOp: p.loadOp,
313
+ blend: p.blend
314
+ })),
315
+ bufferDefs,
316
+ uniformDefs: renderer.uniforms ?? []
317
+ });
318
+ }
319
+ computeRendererMeta(renderer, chart) {
320
+ const bufferSizes = {};
321
+ const perSeriesPassMeta = [];
322
+ const series = chart.series.length > 0 ? chart.series : [
323
+ {
324
+ rawX: [],
325
+ rawY: [],
326
+ extra: {},
327
+ label: "",
328
+ color: { r: 0, g: 0, b: 0 }
329
+ }
330
+ ];
331
+ const dpr = devicePixelRatio || 1;
332
+ const physW = Math.round(chart.width * dpr);
333
+ const physH = Math.round(chart.height * dpr);
334
+ for (const s of series) {
335
+ const ctx = {
336
+ width: physW,
337
+ height: physH,
338
+ samples: s.rawX.length,
339
+ seriesCount: series.length,
340
+ bounds: chart.bounds,
341
+ view: chart.view
342
+ };
343
+ for (const buf of renderer.buffers ?? []) {
344
+ const size = buf.bytes(ctx);
345
+ bufferSizes[buf.name] = Math.max(bufferSizes[buf.name] ?? 0, size);
346
+ }
347
+ perSeriesPassMeta.push(renderer.passes.map((p) => ({
348
+ dispatch: p.dispatch?.(ctx),
349
+ draw: p.draw?.(ctx)
350
+ })));
192
351
  }
352
+ return { bufferSizes, perSeriesPassMeta };
353
+ }
354
+ create(config) {
355
+ if (!this.worker)
356
+ throw new Error("No worker. Call init().");
357
+ const renderer = this.renderers.get(config.type);
358
+ if (!renderer)
359
+ throw new Error(`No renderer "${config.type}". Call manager.use() first.`);
193
360
  const id = `chart-${++this.chartIdCounter}`;
194
361
  const el = document.createElement("div");
195
362
  el.dataset.chartId = id;
196
- el.style.cssText = "width: 100%; height: 100%; position: relative;";
197
- const canvasWrap = document.createElement("div");
198
- canvasWrap.dataset.chartId = id;
199
- canvasWrap.style.cssText = "width: 100%; height: 100%; position: relative;";
200
- const backCanvas = document.createElement("canvas");
201
- backCanvas.style.cssText = "position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;";
202
- const gpuCanvas = document.createElement("canvas");
203
- gpuCanvas.style.cssText = "position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: auto; z-index: 1;";
204
- const axisCanvas = document.createElement("canvas");
205
- axisCanvas.style.cssText = "position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2;";
206
- canvasWrap.appendChild(backCanvas);
207
- canvasWrap.appendChild(gpuCanvas);
208
- canvasWrap.appendChild(axisCanvas);
209
- el.appendChild(canvasWrap);
363
+ el.style.cssText = "width:100%;height:100%;position:relative;";
364
+ const wrap = document.createElement("div");
365
+ wrap.dataset.chartId = id;
366
+ wrap.style.cssText = "width:100%;height:100%;position:relative;";
367
+ const mkCanvas = (z, events) => {
368
+ const c = document.createElement("canvas");
369
+ c.style.cssText = `position:absolute;inset:0;width:100%;height:100%;pointer-events:${events};z-index:${z};`;
370
+ return c;
371
+ };
372
+ const backCanvas = mkCanvas(0, "none");
373
+ const gpuCanvas = mkCanvas(1, "auto");
374
+ const frontCanvas = mkCanvas(2, "none");
375
+ wrap.append(backCanvas, gpuCanvas, frontCanvas);
376
+ el.appendChild(wrap);
210
377
  config.container.appendChild(el);
211
378
  let offscreen;
212
379
  try {
213
380
  offscreen = gpuCanvas.transferControlToOffscreen();
214
381
  } catch (e) {
215
- console.error(`ChartManager: Failed to create offscreen canvas for ${id}:`, e);
216
- throw new Error(`Failed to create chart ${id}: ${e}`);
382
+ throw new Error(`Failed OffscreenCanvas: ${e}`);
217
383
  }
218
- const rect = canvasWrap.getBoundingClientRect();
384
+ const rect = wrap.getBoundingClientRect();
219
385
  const cssW = rect.width || 400;
220
386
  const cssH = rect.height || 200;
221
- ChartManager.resizeCanvas(offscreen, cssW, cssH);
222
- ChartManager.resizeCanvas(backCanvas, cssW, cssH);
223
- ChartManager.resizeCanvas(axisCanvas, cssW, cssH);
387
+ resizeCanvas(offscreen, cssW, cssH);
388
+ resizeCanvas(backCanvas, cssW, cssH);
389
+ resizeCanvas(frontCanvas, cssW, cssH);
224
390
  if (config.bgColor) {
225
391
  const [r, g, b] = config.bgColor;
226
- canvasWrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
392
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
393
+ }
394
+ const customUniforms = {};
395
+ for (const u of renderer.uniforms ?? []) {
396
+ const v = config[u.name];
397
+ customUniforms[u.name] = typeof v === "number" ? v : u.default;
227
398
  }
228
399
  const chart = {
229
400
  id,
230
401
  config,
231
402
  el,
232
403
  backCanvas,
233
- axisCanvas,
404
+ frontCanvas,
234
405
  width: cssW,
235
406
  height: cssH,
236
407
  series: [],
237
408
  bounds: { minX: 0, maxX: 1, minY: 0, maxY: 1 },
238
409
  view: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
239
- zoomMode: config.zoomMode || "both",
410
+ homeView: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
240
411
  visible: true,
241
412
  dragging: false,
242
- bgColor: config.bgColor,
243
- textColor: config.textColor,
244
- gridColor: config.gridColor,
245
- fontFamily: config.fontFamily,
246
- momentum: null,
247
- findNearestPoint(screenX, screenY, width, height) {
248
- if (this.series.length === 0)
249
- return null;
250
- const normX = screenX / width;
251
- const normY = screenY / height;
252
- const rangeX = this.bounds.maxX - this.bounds.minX;
253
- const rangeY = this.bounds.maxY - this.bounds.minY;
254
- const viewWidth = rangeX / this.view.zoomX;
255
- const viewHeight = rangeY / this.view.zoomY;
256
- const viewMinX = this.bounds.minX + this.view.panX * rangeX;
257
- const viewMinY = this.bounds.minY + this.view.panY * rangeY;
258
- const dataX = viewMinX + normX * viewWidth;
259
- const dataY = viewMinY + (1 - normY) * viewHeight;
260
- let bestSeriesIdx = -1;
261
- let bestIdx = -1;
262
- let bestDistX = Infinity;
263
- let bestDistY = Infinity;
264
- for (let s = 0;s < this.series.length; s++) {
265
- const series2 = this.series[s];
266
- const n = series2.rawX.length;
267
- if (n === 0)
268
- continue;
269
- let lo = 0, hi = n - 1;
270
- while (lo < hi) {
271
- const mid = lo + hi >> 1;
272
- if (series2.rawX[mid] < dataX)
273
- lo = mid + 1;
274
- else
275
- hi = mid;
276
- }
277
- let idx = lo;
278
- if (lo > 0) {
279
- const distLo = Math.abs(series2.rawX[lo] - dataX);
280
- const distPrev = Math.abs(series2.rawX[lo - 1] - dataX);
281
- if (distPrev < distLo)
282
- idx = lo - 1;
283
- }
284
- const distX = Math.abs(series2.rawX[idx] - dataX);
285
- const distY = Math.abs(series2.rawY[idx] - dataY);
286
- if (distX < bestDistX || distX === bestDistX && distY < bestDistY) {
287
- bestDistX = distX;
288
- bestDistY = distY;
289
- bestSeriesIdx = s;
290
- bestIdx = idx;
291
- }
292
- }
293
- if (bestSeriesIdx === -1)
294
- return null;
295
- const series = this.series[bestSeriesIdx];
296
- const pointNormX = (series.rawX[bestIdx] - viewMinX) / viewWidth;
297
- const pointScreenX = pointNormX * width;
298
- const pixelDistX = Math.abs(pointScreenX - screenX);
299
- if (pixelDistX > ChartManager.HOVER_MAX_DISTANCE_PX)
300
- return null;
301
- return {
302
- x: series.rawX[bestIdx],
303
- y: series.rawY[bestIdx],
304
- index: bestIdx,
305
- screenX,
306
- screenY,
307
- seriesIndex: bestSeriesIdx,
308
- seriesLabel: series.label
309
- };
310
- }
413
+ plugins: [...this.uiPlugins],
414
+ renderer,
415
+ customUniforms
311
416
  };
312
417
  this.charts.set(id, chart);
313
- const workerType = config.type === "bar" ? "box" : config.type;
418
+ const dpr = devicePixelRatio || 1;
419
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(renderer, chart);
314
420
  this.worker.postMessage({
315
- type: "register-chart",
421
+ type: M.REGISTER_CHART,
316
422
  id,
317
423
  canvas: offscreen,
318
- chartType: workerType,
319
- pointSize: config.pointSize ?? 3,
320
- maxSamplesPerPixel: config.maxSamplesPerPixel ?? 1e4,
321
- bgColor: config.bgColor ?? null
424
+ rendererName: config.type,
425
+ bgColor: config.bgColor ?? null,
426
+ bufferSizes,
427
+ perSeriesPassMeta,
428
+ customUniformValues: customUniforms,
429
+ width: Math.round(cssW * dpr),
430
+ height: Math.round(cssH * dpr)
322
431
  }, [offscreen]);
323
432
  this.visibilityObserver.observe(el);
324
- this.resizeObserver.observe(canvasWrap);
325
- for (const plugin of globalPlugins) {
326
- plugin.install?.(chart, canvasWrap);
327
- }
433
+ this.resizeObserver.observe(wrap);
434
+ for (const plugin of chart.plugins)
435
+ plugin.install?.(chart, wrap);
436
+ renderer.install?.(chart, wrap);
328
437
  this.updateSeries(id, config.series);
329
- return id;
438
+ return new Chart(id, this);
330
439
  }
331
440
  destroy(id) {
332
441
  const chart = this.charts.get(id);
333
442
  if (!chart)
334
443
  return;
335
- for (const plugin of globalPlugins) {
336
- plugin.uninstall?.(chart);
337
- }
338
- if (chart.momentum)
339
- cancelAnimationFrame(chart.momentum);
444
+ chart.renderer.uninstall?.(chart);
445
+ for (const p of chart.plugins)
446
+ p.uninstall?.(chart);
340
447
  this.visibilityObserver.unobserve(chart.el);
341
- const wrap = chart.el.querySelector(".gpu-chart-canvas-wrap");
448
+ const wrap = chart.el.querySelector("div");
342
449
  if (wrap)
343
450
  this.resizeObserver.unobserve(wrap);
344
451
  chart.el.remove();
345
- this.worker?.postMessage({ type: "unregister-chart", id });
452
+ this.worker?.postMessage({ type: M.UNREGISTER_CHART, id });
346
453
  this.charts.delete(id);
347
454
  }
348
455
  updateSeries(id, series) {
349
456
  const chart = this.charts.get(id);
350
- if (!chart || !this.worker)
351
- return;
352
- if (series.length === 0)
457
+ if (!chart || !this.worker || series.length === 0)
353
458
  return;
459
+ chart.config.hiddenSeries = series.reduce((acc, s, i) => {
460
+ if (s.hidden)
461
+ acc.add(i);
462
+ return acc;
463
+ }, new Set);
354
464
  chart.series = series.map((s) => {
355
465
  const n = s.x.length;
466
+ const color = parseColor(s.color);
356
467
  if (n === 0)
357
- return { label: s.label, color: s.color, rawX: [], rawY: [] };
358
- const indices = Array.from({ length: n }, (_, i) => i);
359
- indices.sort((a, b) => s.x[a] - s.x[b]);
468
+ return { label: s.label, color, rawX: [], rawY: [], extra: {} };
469
+ const idx = Array.from({ length: n }, (_, i) => i).sort((a, b) => s.x[a] - s.x[b]);
470
+ const extra = {};
471
+ for (const key in s) {
472
+ if (key !== "label" && key !== "color" && key !== "x" && key !== "y" && Array.isArray(s[key])) {
473
+ extra[key] = idx.map((i) => s[key][i]);
474
+ }
475
+ }
360
476
  return {
361
477
  label: s.label,
362
- color: s.color,
363
- rawX: indices.map((i) => s.x[i]),
364
- rawY: indices.map((i) => s.y[i])
478
+ color,
479
+ rawX: idx.map((i) => s.x[i]),
480
+ rawY: idx.map((i) => s.y[i]),
481
+ extra
365
482
  };
366
483
  });
367
- let minX = Infinity, maxX = -Infinity;
368
- let minY = Infinity, maxY = -Infinity;
369
- for (const s of chart.series) {
370
- for (let i = 0;i < s.rawX.length; i++) {
371
- if (s.rawX[i] < minX)
372
- minX = s.rawX[i];
373
- if (s.rawX[i] > maxX)
374
- maxX = s.rawX[i];
375
- if (s.rawY[i] < minY)
376
- minY = s.rawY[i];
377
- if (s.rawY[i] > maxY)
378
- maxY = s.rawY[i];
484
+ const customBounds = chart.renderer.computeBounds?.(chart.series);
485
+ let { minX, maxX, minY, maxY } = customBounds ?? (() => {
486
+ let minX2 = Infinity, maxX2 = -Infinity, minY2 = Infinity, maxY2 = -Infinity;
487
+ for (const s of chart.series) {
488
+ for (let i = 0;i < s.rawX.length; i++) {
489
+ if (s.rawX[i] < minX2)
490
+ minX2 = s.rawX[i];
491
+ if (s.rawX[i] > maxX2)
492
+ maxX2 = s.rawX[i];
493
+ if (s.rawY[i] < minY2)
494
+ minY2 = s.rawY[i];
495
+ if (s.rawY[i] > maxY2)
496
+ maxY2 = s.rawY[i];
497
+ }
379
498
  }
380
- }
381
- const px = (maxX - minX) * 0.05 || 1;
382
- const py = (maxY - minY) * 0.1 || 1;
383
- minX -= px;
384
- maxX += px;
385
- minY -= py;
386
- maxY += py;
499
+ const px = (maxX2 - minX2) * 0.05 || 1;
500
+ const py = (maxY2 - minY2) * 0.1 || 1;
501
+ return {
502
+ minX: minX2 - px,
503
+ maxX: maxX2 + px,
504
+ minY: minY2 - py,
505
+ maxY: maxY2 + py
506
+ };
507
+ })();
387
508
  const db = chart.config.defaultBounds;
388
509
  if (db) {
389
510
  if (db.minX !== undefined)
@@ -396,89 +517,47 @@ class ChartManager {
396
517
  maxY = db.maxY;
397
518
  }
398
519
  chart.bounds = { minX, maxX, minY, maxY };
399
- const seriesData = chart.series.map((s) => ({
400
- label: s.label,
401
- colorR: s.color.r,
402
- colorG: s.color.g,
403
- colorB: s.color.b,
404
- dataX: new Float32Array(s.rawX),
405
- dataY: new Float32Array(s.rawY)
406
- }));
520
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
521
+ const hidden = chart.config.hiddenSeries ?? new Set;
522
+ const seriesData = chart.series.map((s, i) => {
523
+ const extra = {};
524
+ for (const key in s.extra)
525
+ extra[key] = new Float32Array(s.extra[key]);
526
+ return {
527
+ label: s.label,
528
+ colorR: s.color.r,
529
+ colorG: s.color.g,
530
+ colorB: s.color.b,
531
+ dataX: new Float32Array(s.rawX),
532
+ dataY: new Float32Array(s.rawY),
533
+ extra,
534
+ hidden: hidden.has(i)
535
+ };
536
+ });
537
+ const transferables = seriesData.flatMap((s) => [
538
+ s.dataX.buffer,
539
+ s.dataY.buffer,
540
+ ...Object.values(s.extra).map((a) => a.buffer)
541
+ ]);
407
542
  this.worker.postMessage({
408
- type: "update-series",
543
+ type: M.UPDATE_SERIES,
409
544
  id,
410
545
  series: seriesData,
411
- bounds: chart.bounds
412
- }, seriesData.flatMap((s) => [s.dataX.buffer, s.dataY.buffer]));
546
+ bounds: chart.bounds,
547
+ bufferSizes,
548
+ perSeriesPassMeta
549
+ }, transferables);
413
550
  this.sendViewTransform(chart);
414
551
  this.drawChart(chart);
415
552
  }
416
- setPointSize(id, size) {
417
- const chart = this.charts.get(id);
418
- if (!chart)
419
- return;
420
- this.worker?.postMessage({
421
- type: "set-point-size",
422
- id,
423
- pointSize: Math.max(1, Math.min(8, Math.round(size)))
424
- });
425
- }
426
- setMaxSamplesPerPixel(id, maxSamples) {
427
- const chart = this.charts.get(id);
428
- if (!chart)
429
- return;
430
- this.worker?.postMessage({
431
- type: "set-max-samples",
432
- id,
433
- maxSamplesPerPixel: Math.max(0, maxSamples | 0)
434
- });
435
- }
436
- setStyle(id, style) {
437
- const chart = this.charts.get(id);
438
- if (!chart)
439
- return;
440
- if (style.bgColor !== undefined)
441
- chart.bgColor = style.bgColor;
442
- if (style.textColor !== undefined)
443
- chart.textColor = style.textColor;
444
- if (style.gridColor !== undefined)
445
- chart.gridColor = style.gridColor;
446
- if (style.fontFamily !== undefined)
447
- chart.fontFamily = style.fontFamily;
448
- if (style.bgColor !== undefined) {
449
- const wrap = chart.el.querySelector(".gpu-chart-canvas-wrap");
450
- if (wrap) {
451
- const [r, g, b] = style.bgColor;
452
- wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
453
- }
454
- this.worker?.postMessage({
455
- type: "set-style",
456
- id,
457
- bgColor: style.bgColor
458
- });
459
- }
460
- this.drawChart(chart);
461
- }
462
- setZoomMode(id, mode) {
463
- const chart = this.charts.get(id);
464
- if (chart)
465
- chart.zoomMode = mode;
466
- }
467
- getChartCount() {
468
- return this.charts.size;
469
- }
470
- getZoomMode(id) {
471
- return this.charts.get(id)?.zoomMode || "both";
472
- }
473
553
  setSyncViews(sync) {
474
554
  this._syncViews = sync;
475
555
  }
476
556
  setTheme(dark) {
477
557
  this._isDark = dark;
478
- this.worker?.postMessage({ type: "theme", isDark: dark });
479
- for (const chart of this.charts.values()) {
558
+ this.worker?.postMessage({ type: M.THEME, isDark: dark });
559
+ for (const chart of this.charts.values())
480
560
  this.drawChart(chart);
481
- }
482
561
  }
483
562
  onStats(callback) {
484
563
  this.statsCallbacks.push(callback);
@@ -495,22 +574,18 @@ class ChartManager {
495
574
  const chart = this.charts.get(id);
496
575
  if (!chart)
497
576
  return;
498
- if (chart.momentum) {
499
- cancelAnimationFrame(chart.momentum);
500
- chart.momentum = null;
501
- }
502
- const startPanX = chart.view.panX;
503
- const startPanY = chart.view.panY;
504
- const startZoomX = chart.view.zoomX;
505
- const startZoomY = chart.view.zoomY;
506
- const startTime = performance.now();
577
+ for (const p of chart.plugins)
578
+ p.resetView?.(chart);
579
+ const { panX: spx, panY: spy, zoomX: szx, zoomY: szy } = chart.view;
580
+ const { panX: tpx, panY: tpy, zoomX: tzx, zoomY: tzy } = chart.homeView;
581
+ const t0 = performance.now();
507
582
  const animate = () => {
508
- const t = Math.min(1, (performance.now() - startTime) / 300);
509
- const ease = 1 - Math.pow(1 - t, 3);
510
- chart.view.panX = startPanX * (1 - ease);
511
- chart.view.panY = startPanY * (1 - ease);
512
- chart.view.zoomX = startZoomX + (1 - startZoomX) * ease;
513
- chart.view.zoomY = startZoomY + (1 - startZoomY) * ease;
583
+ const t = Math.min(1, (performance.now() - t0) / 300);
584
+ const e = 1 - Math.pow(1 - t, 3);
585
+ chart.view.panX = spx + (tpx - spx) * e;
586
+ chart.view.panY = spy + (tpy - spy) * e;
587
+ chart.view.zoomX = szx + (tzx - szx) * e;
588
+ chart.view.zoomY = szy + (tzy - szy) * e;
514
589
  this.sendViewTransform(chart);
515
590
  this.drawChart(chart);
516
591
  if (this._syncViews)
@@ -520,9 +595,26 @@ class ChartManager {
520
595
  };
521
596
  requestAnimationFrame(animate);
522
597
  }
598
+ setHiddenSeries(id, hidden) {
599
+ const chart = this.charts.get(id);
600
+ if (!chart)
601
+ return;
602
+ chart.config.hiddenSeries = new Set(hidden);
603
+ this.worker?.postMessage({
604
+ type: M.SET_STYLE,
605
+ id,
606
+ hiddenSeries: chart.config.hiddenSeries
607
+ });
608
+ this.drawChart(chart);
609
+ }
610
+ requestRender(id) {
611
+ const chart = this.charts.get(id);
612
+ if (chart)
613
+ this.sendViewTransform(chart);
614
+ }
523
615
  sendViewTransform(chart) {
524
616
  this.worker?.postMessage({
525
- type: "view-transform",
617
+ type: M.VIEW_TRANSFORM,
526
618
  id: chart.id,
527
619
  panX: chart.view.panX,
528
620
  panY: chart.view.panY,
@@ -541,7 +633,7 @@ class ChartManager {
541
633
  }
542
634
  if (transforms.length > 0) {
543
635
  this.worker?.postMessage({
544
- type: "batch-view-transform",
636
+ type: M.BATCH_VIEW_TRANSFORM,
545
637
  panX: source.view.panX,
546
638
  panY: source.view.panY,
547
639
  zoomX: source.view.zoomX,
@@ -559,26 +651,23 @@ class ChartManager {
559
651
  backCtx.clearRect(0, 0, chart.backCanvas.width, chart.backCanvas.height);
560
652
  backCtx.save();
561
653
  backCtx.scale(dpr, dpr);
562
- for (const plugin of globalPlugins) {
563
- plugin.beforeDraw?.(backCtx, chart);
564
- }
654
+ for (const p of chart.plugins)
655
+ p.beforeDraw?.(backCtx, chart);
565
656
  backCtx.restore();
566
657
  }
567
- const ctx = chart.axisCanvas.getContext("2d");
658
+ const ctx = chart.frontCanvas.getContext("2d");
568
659
  if (ctx) {
569
- ctx.clearRect(0, 0, chart.axisCanvas.width, chart.axisCanvas.height);
660
+ ctx.clearRect(0, 0, chart.frontCanvas.width, chart.frontCanvas.height);
570
661
  ctx.save();
571
662
  ctx.scale(dpr, dpr);
572
- for (const plugin of globalPlugins) {
573
- plugin.afterDraw?.(ctx, chart);
574
- }
663
+ for (const p of chart.plugins)
664
+ p.afterDraw?.(ctx, chart);
575
665
  ctx.restore();
576
666
  }
577
667
  }
578
668
  }
669
+ var ChartManager = _ChartManager.getInstance();
579
670
  export {
580
- unregisterPlugin,
581
- registerPlugin,
582
- getPlugins,
583
- ChartManager
671
+ ChartManager,
672
+ Chart
584
673
  };