chartai 0.1.0 → 1.0.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 (78) hide show
  1. package/dist/chart-library.d.ts +33 -146
  2. package/dist/chart-library.d.ts.map +1 -1
  3. package/dist/chart-library.js +378 -321
  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 +132 -0
  18. package/dist/charts/candlestick.min.js +32 -0
  19. package/dist/charts/line.d.ts +12 -0
  20. package/dist/charts/line.d.ts.map +1 -0
  21. package/dist/charts/line.js +62 -0
  22. package/dist/charts/line.min.js +1 -0
  23. package/dist/charts/scatter.d.ts +11 -0
  24. package/dist/charts/scatter.d.ts.map +1 -0
  25. package/dist/charts/scatter.js +46 -0
  26. package/dist/charts/scatter.min.js +1 -0
  27. package/dist/chunk-0jepamv9.js +7 -0
  28. package/dist/chunk-1p45ex5n.min.js +2 -0
  29. package/dist/chunk-831dem4f.js +4 -0
  30. package/dist/chunk-93yrr7er.js +35 -0
  31. package/dist/chunk-94kc81rr.min.js +2 -0
  32. package/dist/chunk-a27be8p9.js +105 -0
  33. package/dist/chunk-bfyv7z27.min.js +2 -0
  34. package/dist/chunk-dmaxrg6s.min.js +2 -0
  35. package/dist/chunk-e7d3zgw5.min.js +2 -0
  36. package/dist/chunk-g6m56ptf.js +609 -0
  37. package/dist/chunk-m17t3vjq.js +9 -0
  38. package/dist/chunk-me3qaz3m.min.js +2 -0
  39. package/dist/chunk-qr6mweck.min.js +2 -0
  40. package/dist/chunk-yabjrff2.js +11 -0
  41. package/dist/gpu-worker.js +625 -686
  42. package/dist/gpu-worker.min.js +1 -1
  43. package/dist/msg.d.ts +33 -0
  44. package/dist/msg.d.ts.map +1 -0
  45. package/dist/plugins/coords.d.ts +18 -0
  46. package/dist/plugins/coords.d.ts.map +1 -0
  47. package/dist/plugins/hover.d.ts +15 -2
  48. package/dist/plugins/hover.d.ts.map +1 -1
  49. package/dist/plugins/hover.js +92 -13
  50. package/dist/plugins/hover.min.js +1 -1
  51. package/dist/plugins/labels-panel.d.ts +4 -0
  52. package/dist/plugins/labels-panel.d.ts.map +1 -0
  53. package/dist/plugins/labels-panel.js +122 -0
  54. package/dist/plugins/labels-panel.min.js +1 -0
  55. package/dist/plugins/labels.d.ts +17 -2
  56. package/dist/plugins/labels.d.ts.map +1 -1
  57. package/dist/plugins/labels.js +11 -99
  58. package/dist/plugins/labels.min.js +1 -1
  59. package/dist/plugins/legend.d.ts +16 -0
  60. package/dist/plugins/legend.d.ts.map +1 -0
  61. package/dist/plugins/legend.js +282 -0
  62. package/dist/plugins/legend.min.js +21 -0
  63. package/dist/plugins/shared.d.ts +7 -0
  64. package/dist/plugins/shared.d.ts.map +1 -0
  65. package/dist/plugins/zoom.d.ts +10 -2
  66. package/dist/plugins/zoom.d.ts.map +1 -1
  67. package/dist/plugins/zoom.js +63 -62
  68. package/dist/plugins/zoom.min.js +1 -1
  69. package/dist/types.d.ts +179 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +0 -0
  72. package/dist/types.min.js +0 -0
  73. package/dist/worker-inline.d.ts +1 -1
  74. package/dist/worker-inline.d.ts.map +1 -1
  75. package/package.json +11 -11
  76. package/readme.md +51 -42
  77. package/dist/chunk-bgfkgcmg.js +0 -25
  78. package/dist/chunk-cj3zanvs.min.js +0 -2
@@ -14,46 +14,139 @@ var exports_worker_inline = {};
14
14
  __export(exports_worker_inline, {
15
15
  WORKER_CODE: () => WORKER_CODE
16
16
  });
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';
17
+ 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)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)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,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;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';
18
+
19
+ // src/msg.ts
20
+ var M = {
21
+ INIT: 0,
22
+ THEME: 1,
23
+ REGISTER_RENDERER: 2,
24
+ REGISTER_CHART: 3,
25
+ UNREGISTER_CHART: 4,
26
+ UPDATE_SERIES: 5,
27
+ RESIZE: 6,
28
+ VIEW_TRANSFORM: 7,
29
+ BATCH_VIEW_TRANSFORM: 8,
30
+ SET_VISIBILITY: 9,
31
+ SET_STYLE: 10,
32
+ SET_UNIFORMS: 11,
33
+ GPU_READY: 12,
34
+ ERROR: 13,
35
+ STATS: 14
36
+ };
18
37
 
19
38
  // src/chart-library.ts
20
- var globalPlugins = [];
21
- function registerPlugin(plugin) {
22
- if (!globalPlugins.some((p) => p.name === plugin.name)) {
23
- globalPlugins.push(plugin);
39
+ class Chart {
40
+ id;
41
+ _mgr;
42
+ constructor(id, mgr) {
43
+ this.id = id;
44
+ this._mgr = mgr;
45
+ }
46
+ get _c() {
47
+ return this._mgr["charts"].get(this.id);
48
+ }
49
+ setData(series) {
50
+ this._mgr.updateSeries(this.id, series);
51
+ }
52
+ configure(patch) {
53
+ const c = this._c;
54
+ if (!c)
55
+ return;
56
+ Object.assign(c.config, patch);
57
+ const uniformNames = new Set((c.renderer.uniforms ?? []).map((u) => u.name));
58
+ const workerValues = {};
59
+ for (const key of Object.keys(patch)) {
60
+ const val = patch[key];
61
+ if (typeof val === "number" && uniformNames.has(key)) {
62
+ workerValues[key] = val;
63
+ }
64
+ }
65
+ if (Object.keys(workerValues).length > 0) {
66
+ Object.assign(c.customUniforms, workerValues);
67
+ this._mgr["worker"]?.postMessage({
68
+ type: M.SET_UNIFORMS,
69
+ id: this.id,
70
+ values: workerValues
71
+ });
72
+ }
73
+ if ("bgColor" in patch && patch.bgColor !== undefined) {
74
+ const [r, g, b] = patch.bgColor;
75
+ const wrap = c.el.querySelector("div");
76
+ if (wrap)
77
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
78
+ this._mgr["worker"]?.postMessage({
79
+ type: M.SET_STYLE,
80
+ id: this.id,
81
+ bgColor: patch.bgColor
82
+ });
83
+ }
84
+ this._mgr.requestRender(this.id);
85
+ this._mgr.drawChart(c);
86
+ }
87
+ addPlugin(plugin) {
88
+ const c = this._c;
89
+ if (!c || c.plugins.some((p) => p.name === plugin.name))
90
+ return;
91
+ const wrap = c.el.querySelector("div");
92
+ plugin.install?.(c, wrap);
93
+ c.plugins.push(plugin);
94
+ this._mgr.drawChart(c);
95
+ }
96
+ removePlugin(name) {
97
+ const c = this._c;
98
+ if (!c)
99
+ return;
100
+ const idx = c.plugins.findIndex((p) => p.name === name);
101
+ if (idx >= 0) {
102
+ c.plugins[idx].uninstall?.(c);
103
+ c.plugins.splice(idx, 1);
104
+ this._mgr.drawChart(c);
105
+ }
106
+ }
107
+ hasPlugin(name) {
108
+ return this._c?.plugins.some((p) => p.name === name) ?? false;
109
+ }
110
+ resetView() {
111
+ this._mgr.resetView(this.id);
112
+ }
113
+ destroy() {
114
+ this._mgr.destroy(this.id);
24
115
  }
25
116
  }
26
- function unregisterPlugin(name) {
27
- const idx = globalPlugins.findIndex((p) => p.name === name);
28
- if (idx >= 0)
29
- globalPlugins.splice(idx, 1);
117
+ var _colorEl = null;
118
+ function parseColor(c) {
119
+ if (typeof c !== "string")
120
+ return c;
121
+ if (!_colorEl) {
122
+ _colorEl = document.createElement("i");
123
+ _colorEl.style.cssText = "display:none";
124
+ document.body.appendChild(_colorEl);
125
+ }
126
+ _colorEl.style.color = c;
127
+ const m = getComputedStyle(_colorEl).color.match(/\d+/g);
128
+ return { r: +m[0] / 255, g: +m[1] / 255, b: +m[2] / 255 };
30
129
  }
31
- function getPlugins() {
32
- return globalPlugins;
130
+ function resizeCanvas(canvas, cssW, cssH) {
131
+ const dpr = devicePixelRatio || 1;
132
+ canvas.width = Math.round(cssW * dpr);
133
+ canvas.height = Math.round(cssH * dpr);
134
+ if (canvas instanceof HTMLCanvasElement) {
135
+ canvas.style.width = `${cssW}px`;
136
+ canvas.style.height = `${cssH}px`;
137
+ }
33
138
  }
34
139
 
35
- class ChartManager {
140
+ class _ChartManager {
36
141
  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
142
  worker = null;
53
143
  charts = new Map;
144
+ renderers = new Map;
145
+ uiPlugins = [];
146
+ pendingRenderers = [];
54
147
  chartIdCounter = 0;
55
- _syncViews = false;
56
148
  _isDark = false;
149
+ _syncViews = false;
57
150
  statsCallbacks = [];
58
151
  currentStats = {
59
152
  fps: 0,
@@ -66,55 +159,56 @@ class ChartManager {
66
159
  constructor() {
67
160
  this._isDark = document.documentElement.classList.contains("dark");
68
161
  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
- }
162
+ for (const e of entries) {
163
+ const id = e.target.dataset.chartId;
164
+ if (!id)
165
+ continue;
166
+ const chart = this.charts.get(id);
167
+ if (!chart)
168
+ continue;
169
+ chart.visible = e.isIntersecting;
170
+ this.worker?.postMessage({
171
+ type: M.SET_VISIBILITY,
172
+ id,
173
+ visible: e.isIntersecting
174
+ });
175
+ if (e.isIntersecting)
176
+ this.drawChart(chart);
85
177
  }
86
178
  }, { threshold: 0.01 });
87
179
  this.resizeObserver = new ResizeObserver((entries) => {
88
- for (const entry of entries) {
89
- const id = entry.target.dataset.chartId;
180
+ for (const e of entries) {
181
+ const id = e.target.dataset.chartId;
90
182
  if (!id)
91
183
  continue;
92
184
  const chart = this.charts.get(id);
93
185
  if (!chart)
94
186
  continue;
95
- const { width, height } = entry.contentRect;
187
+ const { width, height } = e.contentRect;
96
188
  if (width <= 0 || height <= 0)
97
189
  continue;
98
190
  chart.width = width;
99
191
  chart.height = height;
100
192
  const dpr = devicePixelRatio || 1;
193
+ resizeCanvas(chart.backCanvas, width, height);
194
+ resizeCanvas(chart.frontCanvas, width, height);
195
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
101
196
  this.worker?.postMessage({
102
- type: "resize",
197
+ type: M.RESIZE,
103
198
  id,
104
199
  width: Math.round(width * dpr),
105
- height: Math.round(height * dpr)
200
+ height: Math.round(height * dpr),
201
+ bufferSizes,
202
+ perSeriesPassMeta
106
203
  });
107
- ChartManager.resizeCanvas(chart.backCanvas, width, height);
108
- ChartManager.resizeCanvas(chart.axisCanvas, width, height);
109
204
  this.drawChart(chart);
110
205
  }
111
206
  });
112
207
  }
113
208
  static getInstance() {
114
- if (!ChartManager.instance) {
115
- ChartManager.instance = new ChartManager;
116
- }
117
- return ChartManager.instance;
209
+ if (!_ChartManager.instance)
210
+ _ChartManager.instance = new _ChartManager;
211
+ return _ChartManager.instance;
118
212
  }
119
213
  get isDark() {
120
214
  return this._isDark;
@@ -122,6 +216,20 @@ class ChartManager {
122
216
  get syncViews() {
123
217
  return this._syncViews;
124
218
  }
219
+ use(plugin) {
220
+ if ("passes" in plugin) {
221
+ const r = plugin;
222
+ this.renderers.set(r.name, r);
223
+ if (this.worker)
224
+ this.sendRendererRegistration(r);
225
+ else
226
+ this.pendingRenderers.push(r);
227
+ } else {
228
+ const p = plugin;
229
+ if (!this.uiPlugins.some((x) => x.name === p.name))
230
+ this.uiPlugins.push(p);
231
+ }
232
+ }
125
233
  async init() {
126
234
  if (this.worker)
127
235
  return true;
@@ -130,12 +238,12 @@ class ChartManager {
130
238
  const blob = new Blob([WORKER_CODE2], {
131
239
  type: "application/javascript"
132
240
  });
133
- const workerUrl = URL.createObjectURL(blob);
134
- this.worker = new Worker(workerUrl, { type: "module" });
241
+ this.worker = new Worker(URL.createObjectURL(blob), {
242
+ type: "module"
243
+ });
135
244
  this.setupWorkerHandlers(resolve);
136
245
  }).catch(() => {
137
- const workerUrl = new URL("./gpu-worker.js", import.meta.url);
138
- this.worker = new Worker(workerUrl, { type: "module" });
246
+ this.worker = new Worker(new URL("./gpu-worker.js", import.meta.url), { type: "module" });
139
247
  this.setupWorkerHandlers(resolve);
140
248
  });
141
249
  });
@@ -146,14 +254,17 @@ class ChartManager {
146
254
  this.worker.onmessage = (e) => {
147
255
  const { type, ...data } = e.data;
148
256
  switch (type) {
149
- case "gpu-ready":
257
+ case M.GPU_READY:
258
+ for (const r of this.pendingRenderers)
259
+ this.sendRendererRegistration(r);
260
+ this.pendingRenderers = [];
150
261
  resolve(true);
151
262
  break;
152
- case "error":
153
- console.error("ChartManager GPU Error:", data.message);
263
+ case M.ERROR:
264
+ console.error("chartai:", data.code);
154
265
  resolve(false);
155
266
  break;
156
- case "stats":
267
+ case M.STATS:
157
268
  this.currentStats = {
158
269
  fps: data.fps,
159
270
  renderMs: data.renderMs,
@@ -163,227 +274,220 @@ class ChartManager {
163
274
  for (const cb of this.statsCallbacks)
164
275
  cb(this.currentStats);
165
276
  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
277
  }
182
278
  };
183
279
  this.worker.onerror = (e) => {
184
- console.error("ChartManager Worker Error:", e);
280
+ console.error("chartai:", e);
185
281
  resolve(false);
186
282
  };
187
- this.worker.postMessage({ type: "init", isDark: this._isDark });
283
+ this.worker.postMessage({ type: M.INIT, isDark: this._isDark });
188
284
  }
189
- create(config) {
190
- if (!this.worker) {
191
- throw new Error("ChartManager not initialized. Call init() first.");
285
+ sendRendererRegistration(renderer) {
286
+ const bufferDefs = (renderer.buffers ?? []).map((buf) => ({
287
+ name: buf.name,
288
+ usages: buf.usages,
289
+ perSeries: renderer.passes.some((p) => p.perSeries !== false && p.bindings.some((b) => b.source === buf.name))
290
+ }));
291
+ this.worker?.postMessage({
292
+ type: M.REGISTER_RENDERER,
293
+ name: renderer.name,
294
+ shaders: renderer.shaders,
295
+ passes: renderer.passes.map((p) => ({
296
+ type: p.type,
297
+ shader: p.shader,
298
+ bindings: p.bindings,
299
+ perSeries: p.perSeries !== false,
300
+ topology: p.topology,
301
+ loadOp: p.loadOp,
302
+ blend: p.blend
303
+ })),
304
+ bufferDefs,
305
+ uniformDefs: renderer.uniforms ?? []
306
+ });
307
+ }
308
+ computeRendererMeta(renderer, chart) {
309
+ const bufferSizes = {};
310
+ const perSeriesPassMeta = [];
311
+ const series = chart.series.length > 0 ? chart.series : [
312
+ {
313
+ rawX: [],
314
+ rawY: [],
315
+ extra: {},
316
+ label: "",
317
+ color: { r: 0, g: 0, b: 0 }
318
+ }
319
+ ];
320
+ const dpr = devicePixelRatio || 1;
321
+ const physW = Math.round(chart.width * dpr);
322
+ const physH = Math.round(chart.height * dpr);
323
+ for (const s of series) {
324
+ const ctx = {
325
+ width: physW,
326
+ height: physH,
327
+ samples: s.rawX.length,
328
+ seriesCount: series.length,
329
+ bounds: chart.bounds,
330
+ view: chart.view
331
+ };
332
+ for (const buf of renderer.buffers ?? []) {
333
+ const size = buf.bytes(ctx);
334
+ bufferSizes[buf.name] = Math.max(bufferSizes[buf.name] ?? 0, size);
335
+ }
336
+ perSeriesPassMeta.push(renderer.passes.map((p) => ({
337
+ dispatch: p.dispatch?.(ctx),
338
+ draw: p.draw?.(ctx)
339
+ })));
192
340
  }
341
+ return { bufferSizes, perSeriesPassMeta };
342
+ }
343
+ create(config) {
344
+ if (!this.worker)
345
+ throw new Error("Call init() before create().");
346
+ const renderer = this.renderers.get(config.type);
347
+ if (!renderer)
348
+ throw new Error(`No renderer "${config.type}". Call manager.use() with a matching RendererPlugin first.`);
193
349
  const id = `chart-${++this.chartIdCounter}`;
194
350
  const el = document.createElement("div");
195
351
  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);
352
+ el.style.cssText = "width:100%;height:100%;position:relative;";
353
+ const wrap = document.createElement("div");
354
+ wrap.dataset.chartId = id;
355
+ wrap.style.cssText = "width:100%;height:100%;position:relative;";
356
+ const mkCanvas = (z, events) => {
357
+ const c = document.createElement("canvas");
358
+ c.style.cssText = `position:absolute;inset:0;width:100%;height:100%;pointer-events:${events};z-index:${z};`;
359
+ return c;
360
+ };
361
+ const backCanvas = mkCanvas(0, "none");
362
+ const gpuCanvas = mkCanvas(1, "auto");
363
+ const frontCanvas = mkCanvas(2, "none");
364
+ wrap.append(backCanvas, gpuCanvas, frontCanvas);
365
+ el.appendChild(wrap);
210
366
  config.container.appendChild(el);
211
367
  let offscreen;
212
368
  try {
213
369
  offscreen = gpuCanvas.transferControlToOffscreen();
214
370
  } catch (e) {
215
- console.error(`ChartManager: Failed to create offscreen canvas for ${id}:`, e);
216
- throw new Error(`Failed to create chart ${id}: ${e}`);
371
+ throw new Error(`Failed to acquire OffscreenCanvas: ${e}`);
217
372
  }
218
- const rect = canvasWrap.getBoundingClientRect();
373
+ const rect = wrap.getBoundingClientRect();
219
374
  const cssW = rect.width || 400;
220
375
  const cssH = rect.height || 200;
221
- ChartManager.resizeCanvas(offscreen, cssW, cssH);
222
- ChartManager.resizeCanvas(backCanvas, cssW, cssH);
223
- ChartManager.resizeCanvas(axisCanvas, cssW, cssH);
376
+ resizeCanvas(offscreen, cssW, cssH);
377
+ resizeCanvas(backCanvas, cssW, cssH);
378
+ resizeCanvas(frontCanvas, cssW, cssH);
224
379
  if (config.bgColor) {
225
380
  const [r, g, b] = config.bgColor;
226
- canvasWrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
381
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
382
+ }
383
+ const customUniforms = {};
384
+ for (const u of renderer.uniforms ?? []) {
385
+ const v = config[u.name];
386
+ customUniforms[u.name] = typeof v === "number" ? v : u.default;
227
387
  }
228
388
  const chart = {
229
389
  id,
230
390
  config,
231
391
  el,
232
392
  backCanvas,
233
- axisCanvas,
393
+ frontCanvas,
234
394
  width: cssW,
235
395
  height: cssH,
236
396
  series: [],
237
397
  bounds: { minX: 0, maxX: 1, minY: 0, maxY: 1 },
238
398
  view: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
239
- zoomMode: config.zoomMode || "both",
240
399
  visible: true,
241
400
  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
- }
401
+ plugins: [...this.uiPlugins],
402
+ renderer,
403
+ customUniforms
311
404
  };
312
405
  this.charts.set(id, chart);
313
- const workerType = config.type === "bar" ? "box" : config.type;
406
+ const dpr = devicePixelRatio || 1;
407
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(renderer, chart);
314
408
  this.worker.postMessage({
315
- type: "register-chart",
409
+ type: M.REGISTER_CHART,
316
410
  id,
317
411
  canvas: offscreen,
318
- chartType: workerType,
319
- pointSize: config.pointSize ?? 3,
320
- maxSamplesPerPixel: config.maxSamplesPerPixel ?? 1e4,
321
- bgColor: config.bgColor ?? null
412
+ rendererName: config.type,
413
+ bgColor: config.bgColor ?? null,
414
+ bufferSizes,
415
+ perSeriesPassMeta,
416
+ customUniformValues: customUniforms,
417
+ width: Math.round(cssW * dpr),
418
+ height: Math.round(cssH * dpr)
322
419
  }, [offscreen]);
323
420
  this.visibilityObserver.observe(el);
324
- this.resizeObserver.observe(canvasWrap);
325
- for (const plugin of globalPlugins) {
326
- plugin.install?.(chart, canvasWrap);
327
- }
421
+ this.resizeObserver.observe(wrap);
422
+ for (const plugin of chart.plugins)
423
+ plugin.install?.(chart, wrap);
424
+ renderer.install?.(chart, wrap);
328
425
  this.updateSeries(id, config.series);
329
- return id;
426
+ return new Chart(id, this);
330
427
  }
331
428
  destroy(id) {
332
429
  const chart = this.charts.get(id);
333
430
  if (!chart)
334
431
  return;
335
- for (const plugin of globalPlugins) {
336
- plugin.uninstall?.(chart);
337
- }
338
- if (chart.momentum)
339
- cancelAnimationFrame(chart.momentum);
432
+ chart.renderer.uninstall?.(chart);
433
+ for (const p of chart.plugins)
434
+ p.uninstall?.(chart);
340
435
  this.visibilityObserver.unobserve(chart.el);
341
- const wrap = chart.el.querySelector(".gpu-chart-canvas-wrap");
436
+ const wrap = chart.el.querySelector("div");
342
437
  if (wrap)
343
438
  this.resizeObserver.unobserve(wrap);
344
439
  chart.el.remove();
345
- this.worker?.postMessage({ type: "unregister-chart", id });
440
+ this.worker?.postMessage({ type: M.UNREGISTER_CHART, id });
346
441
  this.charts.delete(id);
347
442
  }
348
443
  updateSeries(id, series) {
349
444
  const chart = this.charts.get(id);
350
- if (!chart || !this.worker)
351
- return;
352
- if (series.length === 0)
445
+ if (!chart || !this.worker || series.length === 0)
353
446
  return;
354
447
  chart.series = series.map((s) => {
355
448
  const n = s.x.length;
449
+ const color = parseColor(s.color);
356
450
  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]);
451
+ return { label: s.label, color, rawX: [], rawY: [], extra: {} };
452
+ const idx = Array.from({ length: n }, (_, i) => i).sort((a, b) => s.x[a] - s.x[b]);
453
+ const extra = {};
454
+ for (const key in s) {
455
+ if (key !== "label" && key !== "color" && key !== "x" && key !== "y" && Array.isArray(s[key])) {
456
+ extra[key] = idx.map((i) => s[key][i]);
457
+ }
458
+ }
360
459
  return {
361
460
  label: s.label,
362
- color: s.color,
363
- rawX: indices.map((i) => s.x[i]),
364
- rawY: indices.map((i) => s.y[i])
461
+ color,
462
+ rawX: idx.map((i) => s.x[i]),
463
+ rawY: idx.map((i) => s.y[i]),
464
+ extra
365
465
  };
366
466
  });
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];
467
+ const customBounds = chart.renderer.computeBounds?.(chart.series);
468
+ let { minX, maxX, minY, maxY } = customBounds ?? (() => {
469
+ let minX2 = Infinity, maxX2 = -Infinity, minY2 = Infinity, maxY2 = -Infinity;
470
+ for (const s of chart.series) {
471
+ for (let i = 0;i < s.rawX.length; i++) {
472
+ if (s.rawX[i] < minX2)
473
+ minX2 = s.rawX[i];
474
+ if (s.rawX[i] > maxX2)
475
+ maxX2 = s.rawX[i];
476
+ if (s.rawY[i] < minY2)
477
+ minY2 = s.rawY[i];
478
+ if (s.rawY[i] > maxY2)
479
+ maxY2 = s.rawY[i];
480
+ }
379
481
  }
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;
482
+ const px = (maxX2 - minX2) * 0.05 || 1;
483
+ const py = (maxY2 - minY2) * 0.1 || 1;
484
+ return {
485
+ minX: minX2 - px,
486
+ maxX: maxX2 + px,
487
+ minY: minY2 - py,
488
+ maxY: maxY2 + py
489
+ };
490
+ })();
387
491
  const db = chart.config.defaultBounds;
388
492
  if (db) {
389
493
  if (db.minX !== undefined)
@@ -396,89 +500,45 @@ class ChartManager {
396
500
  maxY = db.maxY;
397
501
  }
398
502
  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
- }));
503
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
504
+ const seriesData = chart.series.map((s) => {
505
+ const extra = {};
506
+ for (const key in s.extra)
507
+ extra[key] = new Float32Array(s.extra[key]);
508
+ return {
509
+ label: s.label,
510
+ colorR: s.color.r,
511
+ colorG: s.color.g,
512
+ colorB: s.color.b,
513
+ dataX: new Float32Array(s.rawX),
514
+ dataY: new Float32Array(s.rawY),
515
+ extra
516
+ };
517
+ });
518
+ const transferables = seriesData.flatMap((s) => [
519
+ s.dataX.buffer,
520
+ s.dataY.buffer,
521
+ ...Object.values(s.extra).map((a) => a.buffer)
522
+ ]);
407
523
  this.worker.postMessage({
408
- type: "update-series",
524
+ type: M.UPDATE_SERIES,
409
525
  id,
410
526
  series: seriesData,
411
- bounds: chart.bounds
412
- }, seriesData.flatMap((s) => [s.dataX.buffer, s.dataY.buffer]));
527
+ bounds: chart.bounds,
528
+ bufferSizes,
529
+ perSeriesPassMeta
530
+ }, transferables);
413
531
  this.sendViewTransform(chart);
414
532
  this.drawChart(chart);
415
533
  }
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
534
  setSyncViews(sync) {
474
535
  this._syncViews = sync;
475
536
  }
476
537
  setTheme(dark) {
477
538
  this._isDark = dark;
478
- this.worker?.postMessage({ type: "theme", isDark: dark });
479
- for (const chart of this.charts.values()) {
539
+ this.worker?.postMessage({ type: M.THEME, isDark: dark });
540
+ for (const chart of this.charts.values())
480
541
  this.drawChart(chart);
481
- }
482
542
  }
483
543
  onStats(callback) {
484
544
  this.statsCallbacks.push(callback);
@@ -495,22 +555,17 @@ class ChartManager {
495
555
  const chart = this.charts.get(id);
496
556
  if (!chart)
497
557
  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();
558
+ for (const p of chart.plugins)
559
+ p.resetView?.(chart);
560
+ const { panX: spx, panY: spy, zoomX: szx, zoomY: szy } = chart.view;
561
+ const t0 = performance.now();
507
562
  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;
563
+ const t = Math.min(1, (performance.now() - t0) / 300);
564
+ const e = 1 - Math.pow(1 - t, 3);
565
+ chart.view.panX = spx * (1 - e);
566
+ chart.view.panY = spy * (1 - e);
567
+ chart.view.zoomX = szx + (1 - szx) * e;
568
+ chart.view.zoomY = szy + (1 - szy) * e;
514
569
  this.sendViewTransform(chart);
515
570
  this.drawChart(chart);
516
571
  if (this._syncViews)
@@ -520,9 +575,14 @@ class ChartManager {
520
575
  };
521
576
  requestAnimationFrame(animate);
522
577
  }
578
+ requestRender(id) {
579
+ const chart = this.charts.get(id);
580
+ if (chart)
581
+ this.sendViewTransform(chart);
582
+ }
523
583
  sendViewTransform(chart) {
524
584
  this.worker?.postMessage({
525
- type: "view-transform",
585
+ type: M.VIEW_TRANSFORM,
526
586
  id: chart.id,
527
587
  panX: chart.view.panX,
528
588
  panY: chart.view.panY,
@@ -541,7 +601,7 @@ class ChartManager {
541
601
  }
542
602
  if (transforms.length > 0) {
543
603
  this.worker?.postMessage({
544
- type: "batch-view-transform",
604
+ type: M.BATCH_VIEW_TRANSFORM,
545
605
  panX: source.view.panX,
546
606
  panY: source.view.panY,
547
607
  zoomX: source.view.zoomX,
@@ -559,26 +619,23 @@ class ChartManager {
559
619
  backCtx.clearRect(0, 0, chart.backCanvas.width, chart.backCanvas.height);
560
620
  backCtx.save();
561
621
  backCtx.scale(dpr, dpr);
562
- for (const plugin of globalPlugins) {
563
- plugin.beforeDraw?.(backCtx, chart);
564
- }
622
+ for (const p of chart.plugins)
623
+ p.beforeDraw?.(backCtx, chart);
565
624
  backCtx.restore();
566
625
  }
567
- const ctx = chart.axisCanvas.getContext("2d");
626
+ const ctx = chart.frontCanvas.getContext("2d");
568
627
  if (ctx) {
569
- ctx.clearRect(0, 0, chart.axisCanvas.width, chart.axisCanvas.height);
628
+ ctx.clearRect(0, 0, chart.frontCanvas.width, chart.frontCanvas.height);
570
629
  ctx.save();
571
630
  ctx.scale(dpr, dpr);
572
- for (const plugin of globalPlugins) {
573
- plugin.afterDraw?.(ctx, chart);
574
- }
631
+ for (const p of chart.plugins)
632
+ p.afterDraw?.(ctx, chart);
575
633
  ctx.restore();
576
634
  }
577
635
  }
578
636
  }
637
+ var ChartManager = _ChartManager.getInstance();
579
638
  export {
580
- unregisterPlugin,
581
- registerPlugin,
582
- getPlugins,
583
- ChartManager
639
+ ChartManager,
640
+ Chart
584
641
  };