chartai 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chart-library.js
CHANGED
|
@@ -14,7 +14,7 @@ var exports_worker_inline = {};
|
|
|
14
14
|
__export(exports_worker_inline, {
|
|
15
15
|
WORKER_CODE: () => WORKER_CODE
|
|
16
16
|
});
|
|
17
|
-
var WORKER_CODE = 'import"./chunk-cj3zanvs.js";var a=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,};",r="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 W="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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),g[`${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}}),_=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,E("line",C,y,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),E("box",D,H,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:_}]},primitive:{topology:"triangle-strip"}}),P=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:"error",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext("webgpu");if(!x){postMessage({type:"error",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:"chart-registered",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type==="line"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:"stats",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(w=n.isDark||!1,await J())h();break;case"theme":w=n.isDark,T();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;T();break}};\n';
|
|
17
|
+
var WORKER_CODE = 'var a=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,};",r="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 W="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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),g[`${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}}),_=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,E("line",C,y,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),E("box",D,H,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:_}]},primitive:{topology:"triangle-strip"}}),P=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:"error",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext("webgpu");if(!x){postMessage({type:"error",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:"chart-registered",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type==="line"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:"stats",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(w=n.isDark||!1,await J())h();break;case"theme":w=n.isDark,T();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;T();break}};\n';
|
|
18
18
|
|
|
19
19
|
// src/chart-library.ts
|
|
20
20
|
var globalPlugins = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var W=Object.defineProperty;var H=(t,e)=>{for(var i in e)W(t,i,{get:e[i],enumerable:!0,configurable:!0,set:(r)=>e[i]=()=>r})};var O={};H(O,{WORKER_CODE:()=>k});var k='import"./chunk-cj3zanvs.js";var a=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,};",r="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 W="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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),g[`${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}}),_=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,E("line",C,y,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),E("box",D,H,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:_}]},primitive:{topology:"triangle-strip"}}),P=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:"error",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext("webgpu");if(!x){postMessage({type:"error",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:"chart-registered",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type==="line"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:"stats",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(w=n.isDark||!1,await J())h();break;case"theme":w=n.isDark,T();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;T();break}};\n';var m=[];function Z(t){if(!m.some((e)=>e.name===t.name))m.push(t)}function ee(t){let e=m.findIndex((i)=>i.name===t);if(e>=0)m.splice(e,1)}function te(){return m}class x{static instance=null;static MARGIN={left:55,right:10,top:8,bottom:45};static HOVER_MAX_DISTANCE_PX=50;static MIN_ZOOM=0.1;static MAX_ZOOM=1e7;static DEFAULT_LABEL_SIZE=12;static DEFAULT_FONT='-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';static resizeCanvas(t,e,i){let r=devicePixelRatio||1;if(t.width=Math.round(e*r),t.height=Math.round(i*r),t instanceof HTMLCanvasElement)t.style.width=`${e}px`,t.style.height=`${i}px`}worker=null;charts=new Map;chartIdCounter=0;_syncViews=!1;_isDark=!1;statsCallbacks=[];currentStats={fps:0,renderMs:0,total:0,active:0};visibilityObserver;resizeObserver;constructor(){this._isDark=document.documentElement.classList.contains("dark"),this.visibilityObserver=new IntersectionObserver((t)=>{for(let e of t){let i=e.target.dataset.chartId;if(i){let r=this.charts.get(i);if(r)r.visible=e.isIntersecting,this.worker?.postMessage({type:"set-visibility",id:i,visible:e.isIntersecting})}}},{threshold:0.01}),this.resizeObserver=new ResizeObserver((t)=>{for(let e of t){let i=e.target.dataset.chartId;if(!i)continue;let r=this.charts.get(i);if(!r)continue;let{width:a,height:u}=e.contentRect;if(a<=0||u<=0)continue;r.width=a,r.height=u;let o=devicePixelRatio||1;this.worker?.postMessage({type:"resize",id:i,width:Math.round(a*o),height:Math.round(u*o)}),x.resizeCanvas(r.backCanvas,a,u),x.resizeCanvas(r.axisCanvas,a,u),this.drawChart(r)}})}static getInstance(){if(!x.instance)x.instance=new x;return x.instance}get isDark(){return this._isDark}get syncViews(){return this._syncViews}async init(){if(this.worker)return!0;return new Promise((t)=>{Promise.resolve().then(() => O).then(({WORKER_CODE:e})=>{let i=new Blob([e],{type:"application/javascript"}),r=URL.createObjectURL(i);this.worker=new Worker(r,{type:"module"}),this.setupWorkerHandlers(t)}).catch(()=>{let e=new URL("./gpu-worker.js",import.meta.url);this.worker=new Worker(e,{type:"module"}),this.setupWorkerHandlers(t)})})}setupWorkerHandlers(t){if(!this.worker)return;this.worker.onmessage=(e)=>{let{type:i,...r}=e.data;switch(i){case"gpu-ready":t(!0);break;case"error":console.error("ChartManager GPU Error:",r.message),t(!1);break;case"stats":this.currentStats={fps:r.fps,renderMs:r.renderMs,total:r.totalCharts,active:r.activeCharts};for(let a of this.statsCallbacks)a(this.currentStats);break;case"bounds-update":{let a=this.charts.get(r.id);if(a)a.bounds={minX:r.minX,maxX:r.maxX,minY:r.minY,maxY:r.maxY},this.drawChart(a);break}default:break}},this.worker.onerror=(e)=>{console.error("ChartManager Worker Error:",e),t(!1)},this.worker.postMessage({type:"init",isDark:this._isDark})}create(t){if(!this.worker)throw new Error("ChartManager not initialized. Call init() first.");let e=`chart-${++this.chartIdCounter}`,i=document.createElement("div");i.className="gpu-chart",i.dataset.chartId=e;let r=document.createElement("div");r.className="gpu-chart-canvas-wrap",r.dataset.chartId=e;let a=document.createElement("canvas");a.className="gpu-chart-back-canvas";let u=document.createElement("canvas");u.className="gpu-chart-canvas";let o=document.createElement("canvas");o.className="gpu-chart-axis-canvas",r.appendChild(a),r.appendChild(u),r.appendChild(o),i.appendChild(r),t.container.appendChild(i);let p;try{p=u.transferControlToOffscreen()}catch(d){throw console.error(`ChartManager: Failed to create offscreen canvas for ${e}:`,d),new Error(`Failed to create chart ${e}: ${d}`)}let v=r.getBoundingClientRect(),l=v.width||400,c=v.height||200;if(x.resizeCanvas(p,l,c),x.resizeCanvas(a,l,c),x.resizeCanvas(o,l,c),t.bgColor){let[d,s,g]=t.bgColor;r.style.background=`rgb(${Math.round(d*255)},${Math.round(s*255)},${Math.round(g*255)})`}let n={id:e,config:t,el:i,backCanvas:a,axisCanvas:o,width:l,height:c,series:[],bounds:{minX:0,maxX:1,minY:0,maxY:1},view:{panX:0,panY:0,zoomX:1,zoomY:1},zoomMode:t.zoomMode||"both",visible:!0,dragging:!1,bgColor:t.bgColor,textColor:t.textColor,gridColor:t.gridColor,fontFamily:t.fontFamily,momentum:null,findNearestPoint(d,s,g,_){if(this.series.length===0)return null;let z=d/g,L=s/_,G=this.bounds.maxX-this.bounds.minX,R=this.bounds.maxY-this.bounds.minY,T=G/this.view.zoomX,A=R/this.view.zoomY,C=this.bounds.minX+this.view.panX*G,V=this.bounds.minY+this.view.panY*R,M=C+z*T,$=V+(1-L)*A,w=-1,S=-1,I=1/0,E=1/0;for(let P=0;P<this.series.length;P++){let y=this.series[P],N=y.rawX.length;if(N===0)continue;let b=0,U=N-1;while(b<U){let Y=b+U>>1;if(y.rawX[Y]<M)b=Y+1;else U=Y}let B=b;if(b>0){let Y=Math.abs(y.rawX[b]-M);if(Math.abs(y.rawX[b-1]-M)<Y)B=b-1}let h=Math.abs(y.rawX[B]-M),D=Math.abs(y.rawY[B]-$);if(h<I||h===I&&D<E)I=h,E=D,w=P,S=B}if(w===-1)return null;let X=this.series[w],F=(X.rawX[S]-C)/T*g;if(Math.abs(F-d)>x.HOVER_MAX_DISTANCE_PX)return null;return{x:X.rawX[S],y:X.rawY[S],index:S,screenX:d,screenY:s,seriesIndex:w,seriesLabel:X.label}}};this.charts.set(e,n);let f=t.type==="bar"?"box":t.type;this.worker.postMessage({type:"register-chart",id:e,canvas:p,chartType:f,pointSize:t.pointSize??3,maxSamplesPerPixel:t.maxSamplesPerPixel??1e4,bgColor:t.bgColor??null},[p]),this.visibilityObserver.observe(i),this.resizeObserver.observe(r);for(let d of m)d.install?.(n,r);return this.updateSeries(e,t.series),e}destroy(t){let e=this.charts.get(t);if(!e)return;for(let r of m)r.uninstall?.(e);if(e.momentum)cancelAnimationFrame(e.momentum);this.visibilityObserver.unobserve(e.el);let i=e.el.querySelector(".gpu-chart-canvas-wrap");if(i)this.resizeObserver.unobserve(i);e.el.remove(),this.worker?.postMessage({type:"unregister-chart",id:t}),this.charts.delete(t)}updateSeries(t,e){let i=this.charts.get(t);if(!i||!this.worker)return;if(e.length===0)return;i.series=e.map((n)=>{let f=n.x.length;if(f===0)return{label:n.label,color:n.color,rawX:[],rawY:[]};let d=Array.from({length:f},(s,g)=>g);return d.sort((s,g)=>n.x[s]-n.x[g]),{label:n.label,color:n.color,rawX:d.map((s)=>n.x[s]),rawY:d.map((s)=>n.y[s])}});let r=1/0,a=-1/0,u=1/0,o=-1/0;for(let n of i.series)for(let f=0;f<n.rawX.length;f++){if(n.rawX[f]<r)r=n.rawX[f];if(n.rawX[f]>a)a=n.rawX[f];if(n.rawY[f]<u)u=n.rawY[f];if(n.rawY[f]>o)o=n.rawY[f]}let p=(a-r)*0.05||1,v=(o-u)*0.1||1;r-=p,a+=p,u-=v,o+=v;let l=i.config.defaultBounds;if(l){if(l.minX!==void 0)r=l.minX;if(l.maxX!==void 0)a=l.maxX;if(l.minY!==void 0)u=l.minY;if(l.maxY!==void 0)o=l.maxY}i.bounds={minX:r,maxX:a,minY:u,maxY:o};let c=i.series.map((n)=>({label:n.label,colorR:n.color.r,colorG:n.color.g,colorB:n.color.b,dataX:new Float32Array(n.rawX),dataY:new Float32Array(n.rawY)}));this.worker.postMessage({type:"update-series",id:t,series:c,bounds:i.bounds},c.flatMap((n)=>[n.dataX.buffer,n.dataY.buffer])),this.sendViewTransform(i),this.drawChart(i)}setPointSize(t,e){if(!this.charts.get(t))return;this.worker?.postMessage({type:"set-point-size",id:t,pointSize:Math.max(1,Math.min(8,Math.round(e)))})}setMaxSamplesPerPixel(t,e){if(!this.charts.get(t))return;this.worker?.postMessage({type:"set-max-samples",id:t,maxSamplesPerPixel:Math.max(0,e|0)})}setStyle(t,e){let i=this.charts.get(t);if(!i)return;if(e.bgColor!==void 0)i.bgColor=e.bgColor;if(e.textColor!==void 0)i.textColor=e.textColor;if(e.gridColor!==void 0)i.gridColor=e.gridColor;if(e.fontFamily!==void 0)i.fontFamily=e.fontFamily;if(e.bgColor!==void 0){let r=i.el.querySelector(".gpu-chart-canvas-wrap");if(r){let[a,u,o]=e.bgColor;r.style.background=`rgb(${Math.round(a*255)},${Math.round(u*255)},${Math.round(o*255)})`}this.worker?.postMessage({type:"set-style",id:t,bgColor:e.bgColor})}this.drawChart(i)}setZoomMode(t,e){let i=this.charts.get(t);if(i)i.zoomMode=e}getChartCount(){return this.charts.size}getZoomMode(t){return this.charts.get(t)?.zoomMode||"both"}setSyncViews(t){this._syncViews=t}setTheme(t){this._isDark=t,this.worker?.postMessage({type:"theme",isDark:t});for(let e of this.charts.values())this.drawChart(e)}onStats(t){return this.statsCallbacks.push(t),()=>{let e=this.statsCallbacks.indexOf(t);if(e>=0)this.statsCallbacks.splice(e,1)}}getStats(){return{...this.currentStats}}resetView(t){let e=this.charts.get(t);if(!e)return;if(e.momentum)cancelAnimationFrame(e.momentum),e.momentum=null;let i=e.view.panX,r=e.view.panY,a=e.view.zoomX,u=e.view.zoomY,o=performance.now(),p=()=>{let v=Math.min(1,(performance.now()-o)/300),l=1-Math.pow(1-v,3);if(e.view.panX=i*(1-l),e.view.panY=r*(1-l),e.view.zoomX=a+(1-a)*l,e.view.zoomY=u+(1-u)*l,this.sendViewTransform(e),this.drawChart(e),this._syncViews)this.syncAllViews(e);if(v<1)requestAnimationFrame(p)};requestAnimationFrame(p)}sendViewTransform(t){this.worker?.postMessage({type:"view-transform",id:t.id,panX:t.view.panX,panY:t.view.panY,zoomX:t.view.zoomX,zoomY:t.view.zoomY})}syncAllViews(t){let e=[];for(let i of this.charts.values())if(i.id!==t.id)i.view={...t.view},e.push({id:i.id}),this.drawChart(i);if(e.length>0)this.worker?.postMessage({type:"batch-view-transform",panX:t.view.panX,panY:t.view.panY,zoomX:t.view.zoomX,zoomY:t.view.zoomY,transforms:e})}drawChart(t){if(!t.visible)return;let e=devicePixelRatio||1,i=t.backCanvas.getContext("2d");if(i){i.clearRect(0,0,t.backCanvas.width,t.backCanvas.height),i.save(),i.scale(e,e);for(let a of m)a.beforeDraw?.(i,t);i.restore()}let r=t.axisCanvas.getContext("2d");if(r){r.clearRect(0,0,t.axisCanvas.width,t.axisCanvas.height),r.save(),r.scale(e,e);for(let a of m)a.afterDraw?.(r,t);r.restore()}}}export{ee as unregisterPlugin,Z as registerPlugin,te as getPlugins,x as ChartManager};
|
|
1
|
+
var W=Object.defineProperty;var H=(t,e)=>{for(var i in e)W(t,i,{get:e[i],enumerable:!0,configurable:!0,set:(r)=>e[i]=()=>r})};var O={};H(O,{WORKER_CODE:()=>k});var k='var a=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,};",r="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 W="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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),g[`${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}}),_=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,E("line",C,y,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),E("box",D,H,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:_}]},primitive:{topology:"triangle-strip"}}),P=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:"error",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext("webgpu");if(!x){postMessage({type:"error",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:"chart-registered",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type==="line"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:"stats",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(w=n.isDark||!1,await J())h();break;case"theme":w=n.isDark,T();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;T();break}};\n';var m=[];function Z(t){if(!m.some((e)=>e.name===t.name))m.push(t)}function ee(t){let e=m.findIndex((i)=>i.name===t);if(e>=0)m.splice(e,1)}function te(){return m}class x{static instance=null;static MARGIN={left:55,right:10,top:8,bottom:45};static HOVER_MAX_DISTANCE_PX=50;static MIN_ZOOM=0.1;static MAX_ZOOM=1e7;static DEFAULT_LABEL_SIZE=12;static DEFAULT_FONT='-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';static resizeCanvas(t,e,i){let r=devicePixelRatio||1;if(t.width=Math.round(e*r),t.height=Math.round(i*r),t instanceof HTMLCanvasElement)t.style.width=`${e}px`,t.style.height=`${i}px`}worker=null;charts=new Map;chartIdCounter=0;_syncViews=!1;_isDark=!1;statsCallbacks=[];currentStats={fps:0,renderMs:0,total:0,active:0};visibilityObserver;resizeObserver;constructor(){this._isDark=document.documentElement.classList.contains("dark"),this.visibilityObserver=new IntersectionObserver((t)=>{for(let e of t){let i=e.target.dataset.chartId;if(i){let r=this.charts.get(i);if(r)r.visible=e.isIntersecting,this.worker?.postMessage({type:"set-visibility",id:i,visible:e.isIntersecting})}}},{threshold:0.01}),this.resizeObserver=new ResizeObserver((t)=>{for(let e of t){let i=e.target.dataset.chartId;if(!i)continue;let r=this.charts.get(i);if(!r)continue;let{width:a,height:u}=e.contentRect;if(a<=0||u<=0)continue;r.width=a,r.height=u;let o=devicePixelRatio||1;this.worker?.postMessage({type:"resize",id:i,width:Math.round(a*o),height:Math.round(u*o)}),x.resizeCanvas(r.backCanvas,a,u),x.resizeCanvas(r.axisCanvas,a,u),this.drawChart(r)}})}static getInstance(){if(!x.instance)x.instance=new x;return x.instance}get isDark(){return this._isDark}get syncViews(){return this._syncViews}async init(){if(this.worker)return!0;return new Promise((t)=>{Promise.resolve().then(() => O).then(({WORKER_CODE:e})=>{let i=new Blob([e],{type:"application/javascript"}),r=URL.createObjectURL(i);this.worker=new Worker(r,{type:"module"}),this.setupWorkerHandlers(t)}).catch(()=>{let e=new URL("./gpu-worker.js",import.meta.url);this.worker=new Worker(e,{type:"module"}),this.setupWorkerHandlers(t)})})}setupWorkerHandlers(t){if(!this.worker)return;this.worker.onmessage=(e)=>{let{type:i,...r}=e.data;switch(i){case"gpu-ready":t(!0);break;case"error":console.error("ChartManager GPU Error:",r.message),t(!1);break;case"stats":this.currentStats={fps:r.fps,renderMs:r.renderMs,total:r.totalCharts,active:r.activeCharts};for(let a of this.statsCallbacks)a(this.currentStats);break;case"bounds-update":{let a=this.charts.get(r.id);if(a)a.bounds={minX:r.minX,maxX:r.maxX,minY:r.minY,maxY:r.maxY},this.drawChart(a);break}default:break}},this.worker.onerror=(e)=>{console.error("ChartManager Worker Error:",e),t(!1)},this.worker.postMessage({type:"init",isDark:this._isDark})}create(t){if(!this.worker)throw new Error("ChartManager not initialized. Call init() first.");let e=`chart-${++this.chartIdCounter}`,i=document.createElement("div");i.className="gpu-chart",i.dataset.chartId=e;let r=document.createElement("div");r.className="gpu-chart-canvas-wrap",r.dataset.chartId=e;let a=document.createElement("canvas");a.className="gpu-chart-back-canvas";let u=document.createElement("canvas");u.className="gpu-chart-canvas";let o=document.createElement("canvas");o.className="gpu-chart-axis-canvas",r.appendChild(a),r.appendChild(u),r.appendChild(o),i.appendChild(r),t.container.appendChild(i);let p;try{p=u.transferControlToOffscreen()}catch(d){throw console.error(`ChartManager: Failed to create offscreen canvas for ${e}:`,d),new Error(`Failed to create chart ${e}: ${d}`)}let g=r.getBoundingClientRect(),l=g.width||400,c=g.height||200;if(x.resizeCanvas(p,l,c),x.resizeCanvas(a,l,c),x.resizeCanvas(o,l,c),t.bgColor){let[d,s,v]=t.bgColor;r.style.background=`rgb(${Math.round(d*255)},${Math.round(s*255)},${Math.round(v*255)})`}let n={id:e,config:t,el:i,backCanvas:a,axisCanvas:o,width:l,height:c,series:[],bounds:{minX:0,maxX:1,minY:0,maxY:1},view:{panX:0,panY:0,zoomX:1,zoomY:1},zoomMode:t.zoomMode||"both",visible:!0,dragging:!1,bgColor:t.bgColor,textColor:t.textColor,gridColor:t.gridColor,fontFamily:t.fontFamily,momentum:null,findNearestPoint(d,s,v,_){if(this.series.length===0)return null;let z=d/v,L=s/_,h=this.bounds.maxX-this.bounds.minX,R=this.bounds.maxY-this.bounds.minY,T=h/this.view.zoomX,A=R/this.view.zoomY,C=this.bounds.minX+this.view.panX*h,V=this.bounds.minY+this.view.panY*R,M=C+z*T,$=V+(1-L)*A,w=-1,S=-1,I=1/0,E=1/0;for(let P=0;P<this.series.length;P++){let y=this.series[P],N=y.rawX.length;if(N===0)continue;let b=0,U=N-1;while(b<U){let Y=b+U>>1;if(y.rawX[Y]<M)b=Y+1;else U=Y}let B=b;if(b>0){let Y=Math.abs(y.rawX[b]-M);if(Math.abs(y.rawX[b-1]-M)<Y)B=b-1}let G=Math.abs(y.rawX[B]-M),D=Math.abs(y.rawY[B]-$);if(G<I||G===I&&D<E)I=G,E=D,w=P,S=B}if(w===-1)return null;let X=this.series[w],F=(X.rawX[S]-C)/T*v;if(Math.abs(F-d)>x.HOVER_MAX_DISTANCE_PX)return null;return{x:X.rawX[S],y:X.rawY[S],index:S,screenX:d,screenY:s,seriesIndex:w,seriesLabel:X.label}}};this.charts.set(e,n);let f=t.type==="bar"?"box":t.type;this.worker.postMessage({type:"register-chart",id:e,canvas:p,chartType:f,pointSize:t.pointSize??3,maxSamplesPerPixel:t.maxSamplesPerPixel??1e4,bgColor:t.bgColor??null},[p]),this.visibilityObserver.observe(i),this.resizeObserver.observe(r);for(let d of m)d.install?.(n,r);return this.updateSeries(e,t.series),e}destroy(t){let e=this.charts.get(t);if(!e)return;for(let r of m)r.uninstall?.(e);if(e.momentum)cancelAnimationFrame(e.momentum);this.visibilityObserver.unobserve(e.el);let i=e.el.querySelector(".gpu-chart-canvas-wrap");if(i)this.resizeObserver.unobserve(i);e.el.remove(),this.worker?.postMessage({type:"unregister-chart",id:t}),this.charts.delete(t)}updateSeries(t,e){let i=this.charts.get(t);if(!i||!this.worker)return;if(e.length===0)return;i.series=e.map((n)=>{let f=n.x.length;if(f===0)return{label:n.label,color:n.color,rawX:[],rawY:[]};let d=Array.from({length:f},(s,v)=>v);return d.sort((s,v)=>n.x[s]-n.x[v]),{label:n.label,color:n.color,rawX:d.map((s)=>n.x[s]),rawY:d.map((s)=>n.y[s])}});let r=1/0,a=-1/0,u=1/0,o=-1/0;for(let n of i.series)for(let f=0;f<n.rawX.length;f++){if(n.rawX[f]<r)r=n.rawX[f];if(n.rawX[f]>a)a=n.rawX[f];if(n.rawY[f]<u)u=n.rawY[f];if(n.rawY[f]>o)o=n.rawY[f]}let p=(a-r)*0.05||1,g=(o-u)*0.1||1;r-=p,a+=p,u-=g,o+=g;let l=i.config.defaultBounds;if(l){if(l.minX!==void 0)r=l.minX;if(l.maxX!==void 0)a=l.maxX;if(l.minY!==void 0)u=l.minY;if(l.maxY!==void 0)o=l.maxY}i.bounds={minX:r,maxX:a,minY:u,maxY:o};let c=i.series.map((n)=>({label:n.label,colorR:n.color.r,colorG:n.color.g,colorB:n.color.b,dataX:new Float32Array(n.rawX),dataY:new Float32Array(n.rawY)}));this.worker.postMessage({type:"update-series",id:t,series:c,bounds:i.bounds},c.flatMap((n)=>[n.dataX.buffer,n.dataY.buffer])),this.sendViewTransform(i),this.drawChart(i)}setPointSize(t,e){if(!this.charts.get(t))return;this.worker?.postMessage({type:"set-point-size",id:t,pointSize:Math.max(1,Math.min(8,Math.round(e)))})}setMaxSamplesPerPixel(t,e){if(!this.charts.get(t))return;this.worker?.postMessage({type:"set-max-samples",id:t,maxSamplesPerPixel:Math.max(0,e|0)})}setStyle(t,e){let i=this.charts.get(t);if(!i)return;if(e.bgColor!==void 0)i.bgColor=e.bgColor;if(e.textColor!==void 0)i.textColor=e.textColor;if(e.gridColor!==void 0)i.gridColor=e.gridColor;if(e.fontFamily!==void 0)i.fontFamily=e.fontFamily;if(e.bgColor!==void 0){let r=i.el.querySelector(".gpu-chart-canvas-wrap");if(r){let[a,u,o]=e.bgColor;r.style.background=`rgb(${Math.round(a*255)},${Math.round(u*255)},${Math.round(o*255)})`}this.worker?.postMessage({type:"set-style",id:t,bgColor:e.bgColor})}this.drawChart(i)}setZoomMode(t,e){let i=this.charts.get(t);if(i)i.zoomMode=e}getChartCount(){return this.charts.size}getZoomMode(t){return this.charts.get(t)?.zoomMode||"both"}setSyncViews(t){this._syncViews=t}setTheme(t){this._isDark=t,this.worker?.postMessage({type:"theme",isDark:t});for(let e of this.charts.values())this.drawChart(e)}onStats(t){return this.statsCallbacks.push(t),()=>{let e=this.statsCallbacks.indexOf(t);if(e>=0)this.statsCallbacks.splice(e,1)}}getStats(){return{...this.currentStats}}resetView(t){let e=this.charts.get(t);if(!e)return;if(e.momentum)cancelAnimationFrame(e.momentum),e.momentum=null;let i=e.view.panX,r=e.view.panY,a=e.view.zoomX,u=e.view.zoomY,o=performance.now(),p=()=>{let g=Math.min(1,(performance.now()-o)/300),l=1-Math.pow(1-g,3);if(e.view.panX=i*(1-l),e.view.panY=r*(1-l),e.view.zoomX=a+(1-a)*l,e.view.zoomY=u+(1-u)*l,this.sendViewTransform(e),this.drawChart(e),this._syncViews)this.syncAllViews(e);if(g<1)requestAnimationFrame(p)};requestAnimationFrame(p)}sendViewTransform(t){this.worker?.postMessage({type:"view-transform",id:t.id,panX:t.view.panX,panY:t.view.panY,zoomX:t.view.zoomX,zoomY:t.view.zoomY})}syncAllViews(t){let e=[];for(let i of this.charts.values())if(i.id!==t.id)i.view={...t.view},e.push({id:i.id}),this.drawChart(i);if(e.length>0)this.worker?.postMessage({type:"batch-view-transform",panX:t.view.panX,panY:t.view.panY,zoomX:t.view.zoomX,zoomY:t.view.zoomY,transforms:e})}drawChart(t){if(!t.visible)return;let e=devicePixelRatio||1,i=t.backCanvas.getContext("2d");if(i){i.clearRect(0,0,t.backCanvas.width,t.backCanvas.height),i.save(),i.scale(e,e);for(let a of m)a.beforeDraw?.(i,t);i.restore()}let r=t.axisCanvas.getContext("2d");if(r){r.clearRect(0,0,t.axisCanvas.width,t.axisCanvas.height),r.save(),r.scale(e,e);for(let a of m)a.afterDraw?.(r,t);r.restore()}}}export{ee as unregisterPlugin,Z as registerPlugin,te as getPlugins,x as ChartManager};
|
package/dist/worker-inline.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const WORKER_CODE = "import\"./chunk-cj3zanvs.js\";var a=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,};\",r=\"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 W=\"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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:\"main\"}}),g[`${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}}),_=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,E(\"line\",C,y,\"line-list\",{color:{srcFactor:\"src-alpha\",dstFactor:\"one-minus-src-alpha\"},alpha:{srcFactor:\"one\",dstFactor:\"one-minus-src-alpha\"}}),E(\"box\",D,H,\"triangle-list\",{color:{srcFactor:\"src-alpha\",dstFactor:\"one-minus-src-alpha\"},alpha:{srcFactor:\"one\",dstFactor:\"one-minus-src-alpha\"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:\"main\"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:\"vs\"},fragment:{module:i,entryPoint:\"fs\",targets:[{format:_}]},primitive:{topology:\"triangle-strip\"}}),P=t.createSampler({magFilter:\"linear\",minFilter:\"linear\"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:\"error\",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext(\"webgpu\");if(!x){postMessage({type:\"error\",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:\"premultiplied\"})}catch(M){postMessage({type:\"error\",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:\"error\",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:\"chart-registered\",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type===\"line\"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!==\"scatter\"){let x=e.type===\"line\"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:\"stats\",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case\"init\":if(w=n.isDark||!1,await J())h();break;case\"theme\":w=n.isDark,T();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;T();break}};\n";
|
|
1
|
+
export declare const WORKER_CODE = "var a=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,};\",r=\"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 W=\"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 C=`${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>;${r}@compute @workgroup_size(${a})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);}`,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> 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 L=`${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(${a})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 D=`${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;${r}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(${a})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);}`,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> 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,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,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)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[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([w?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,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:\"main\"}}),g[`${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}}),_=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,E(\"line\",C,y,\"line-list\",{color:{srcFactor:\"src-alpha\",dstFactor:\"one-minus-src-alpha\"},alpha:{srcFactor:\"one\",dstFactor:\"one-minus-src-alpha\"}}),E(\"box\",D,H,\"triangle-list\",{color:{srcFactor:\"src-alpha\",dstFactor:\"one-minus-src-alpha\"},alpha:{srcFactor:\"one\",dstFactor:\"one-minus-src-alpha\"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:\"main\"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:\"vs\"},fragment:{module:i,entryPoint:\"fs\",targets:[{format:_}]},primitive:{topology:\"triangle-strip\"}}),P=t.createSampler({magFilter:\"linear\",minFilter:\"linear\"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:\"error\",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext(\"webgpu\");if(!x){postMessage({type:\"error\",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:\"premultiplied\"})}catch(M){postMessage({type:\"error\",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:\"error\",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:\"chart-registered\",id:e})}function A(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:P}]})}function $(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{A(e);for(let l=0;l<e.series.length;l++)$(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++)$(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),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(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/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type===\"line\"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!==\"scatter\"){let x=e.type===\"line\"?g.lineRender:g.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 m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.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(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){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(U!==null)return;U=setInterval(()=>{postMessage({type:\"stats\",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case\"init\":if(w=n.isDark||!1,await J())h();break;case\"theme\":w=n.isDark,T();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;T();break}};\n";
|
|
2
2
|
//# sourceMappingURL=worker-inline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-inline.d.ts","sourceRoot":"","sources":["../src/worker-inline.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"worker-inline.d.ts","sourceRoot":"","sources":["../src/worker-inline.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,oo5BAAoo5B,CAAC"}
|