chartai 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chart-library.d.ts +1 -0
- package/dist/chart-library.d.ts.map +1 -1
- package/dist/chart-library.js +43 -11
- package/dist/chart-library.min.js +1 -1
- package/dist/charts/area.js +1 -1
- package/dist/charts/area.min.js +1 -1
- package/dist/charts/bar.js +1 -1
- package/dist/charts/bar.min.js +1 -1
- package/dist/charts/boids.js +9 -9
- package/dist/charts/boids.min.js +1 -1
- package/dist/charts/candlestick.js +5 -127
- package/dist/charts/candlestick.min.js +1 -32
- package/dist/charts/experimental/baseline-area.js +70 -0
- package/dist/charts/experimental/baseline-area.min.js +1 -0
- package/dist/charts/experimental/bubble.js +48 -0
- package/dist/charts/experimental/bubble.min.js +1 -0
- package/dist/charts/experimental/error-band.js +111 -0
- package/dist/charts/experimental/error-band.min.js +1 -0
- package/dist/charts/experimental/heatmap.js +69 -0
- package/dist/charts/experimental/heatmap.min.js +1 -0
- package/dist/charts/experimental/histogram.js +139 -0
- package/dist/charts/experimental/histogram.min.js +7 -0
- package/dist/charts/experimental/ohlc.js +132 -0
- package/dist/charts/experimental/ohlc.min.js +32 -0
- package/dist/charts/experimental/step.js +67 -0
- package/dist/charts/experimental/step.min.js +1 -0
- package/dist/charts/experimental/waterfall.js +121 -0
- package/dist/charts/experimental/waterfall.min.js +7 -0
- package/dist/charts/line.js +1 -1
- package/dist/charts/line.min.js +1 -1
- package/dist/charts/scatter.js +1 -1
- package/dist/charts/scatter.min.js +1 -1
- package/dist/{chunk-e7d3zgw5.min.js → chunk-0eh4rzy9.min.js} +1 -1
- package/dist/{chunk-a27be8p9.js → chunk-1ngxm8t2.js} +25 -1
- package/dist/chunk-50bcv2hw.min.js +2 -0
- package/dist/{chunk-dmaxrg6s.min.js → chunk-64q9a7nw.min.js} +1 -1
- package/dist/{chunk-me3qaz3m.min.js → chunk-bbyt23tw.min.js} +1 -1
- package/dist/chunk-cbydth3q.min.js +2 -0
- package/dist/chunk-cvtt04m6.min.js +2 -0
- package/dist/chunk-g2qmt43n.min.js +33 -0
- package/dist/{chunk-1p45ex5n.min.js → chunk-gm0d4cgx.min.js} +2 -2
- package/dist/chunk-mmsy3yqt.js +27 -0
- package/dist/{chunk-g6m56ptf.js → chunk-n8ew0z0e.js} +38 -10
- package/dist/chunk-t0kdz02m.js +129 -0
- package/dist/{chunk-bfyv7z27.min.js → chunk-wdfq2fpx.min.js} +1 -1
- package/dist/gpu-worker.js +8 -3
- package/dist/gpu-worker.min.js +1 -1
- package/dist/plugins/experimental/annotations.js +164 -0
- package/dist/plugins/experimental/annotations.min.js +1 -0
- package/dist/plugins/experimental/crosshair.js +82 -0
- package/dist/plugins/experimental/crosshair.min.js +1 -0
- package/dist/plugins/experimental/minimap.js +190 -0
- package/dist/plugins/experimental/minimap.min.js +1 -0
- package/dist/plugins/experimental/range-selector.js +220 -0
- package/dist/plugins/experimental/range-selector.min.js +1 -0
- package/dist/plugins/experimental/ruler.js +434 -0
- package/dist/plugins/experimental/ruler.min.js +59 -0
- package/dist/plugins/experimental/stats.js +229 -0
- package/dist/plugins/experimental/stats.min.js +8 -0
- package/dist/plugins/experimental/threshold.js +96 -0
- package/dist/plugins/experimental/threshold.min.js +1 -0
- package/dist/plugins/experimental/tooltip-pin.js +177 -0
- package/dist/plugins/experimental/tooltip-pin.min.js +1 -0
- package/dist/plugins/experimental/watermark.js +76 -0
- package/dist/plugins/experimental/watermark.min.js +1 -0
- package/dist/plugins/hover.d.ts.map +1 -1
- package/dist/plugins/hover.js +12 -30
- package/dist/plugins/hover.min.js +1 -1
- package/dist/plugins/labels-panel.js +3 -3
- package/dist/plugins/labels-panel.min.js +1 -1
- package/dist/plugins/labels.d.ts.map +1 -1
- package/dist/plugins/labels.js +3 -3
- package/dist/plugins/labels.min.js +1 -1
- package/dist/plugins/legend.d.ts.map +1 -1
- package/dist/plugins/legend.js +87 -16
- package/dist/plugins/legend.min.js +23 -7
- package/dist/plugins/zoom.js +2 -2
- package/dist/plugins/zoom.min.js +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker-inline.d.ts +1 -1
- package/dist/worker-inline.d.ts.map +1 -1
- package/package.json +1 -1
- package/readme.md +6 -3
- package/dist/chunk-94kc81rr.min.js +0 -2
- package/dist/chunk-qr6mweck.min.js +0 -2
- /package/dist/{chunk-m17t3vjq.js → chunk-5gtx3pza.js} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as j}from"./chunk-wdfq2fpx.js";import{t as _}from"./chunk-bbyt23tw.js";class Y{id;_mgr;constructor(q,A){this.id=q,this._mgr=A}get _c(){return this._mgr.charts.get(this.id)}setData(q){this._mgr.updateSeries(this.id,q)}configure(q){let A=this._c;if(!A)return;Object.assign(A.config,q);let F=new Set((A.renderer.uniforms??[]).map((J)=>J.name)),G={};for(let J of Object.keys(q)){let T=q[J];if(typeof T==="number"&&F.has(J))G[J]=T}if(Object.keys(G).length>0)Object.assign(A.customUniforms,G),this._mgr.worker?.postMessage({type:j.SET_UNIFORMS,id:this.id,values:G});if("hiddenSeries"in q)this._mgr.worker?.postMessage({type:j.SET_STYLE,id:this.id,hiddenSeries:q.hiddenSeries??new Set});if("bgColor"in q&&q.bgColor!==void 0){let[J,T,N]=q.bgColor,K=A.el.querySelector("div");if(K)K.style.background=`rgb(${Math.round(J*255)},${Math.round(T*255)},${Math.round(N*255)})`;this._mgr.worker?.postMessage({type:j.SET_STYLE,id:this.id,bgColor:q.bgColor})}this._mgr.requestRender(this.id),this._mgr.drawChart(A)}addPlugin(q){let A=this._c;if(!A||A.plugins.some((G)=>G.name===q.name))return;let F=A.el.querySelector("div");q.install?.(A,F),A.plugins.push(q),this._mgr.drawChart(A)}removePlugin(q){let A=this._c;if(!A)return;let F=A.plugins.findIndex((G)=>G.name===q);if(F>=0)A.plugins[F].uninstall?.(A),A.plugins.splice(F,1),this._mgr.drawChart(A)}hasPlugin(q){return this._c?.plugins.some((A)=>A.name===q)??!1}resetView(){this._mgr.resetView(this.id)}destroy(){this._mgr.destroy(this.id)}}var S=null;function z(q){if(typeof q!=="string")return q;if(!S)S=document.createElement("i"),S.style.cssText="display:none",document.body.appendChild(S);S.style.color=q;let A=getComputedStyle(S).color.match(/\d+/g);return{r:+A[0]/255,g:+A[1]/255,b:+A[2]/255}}function X(q,A,F){let G=devicePixelRatio||1;if(q.width=Math.round(A*G),q.height=Math.round(F*G),q instanceof HTMLCanvasElement)q.style.width=`${A}px`,q.style.height=`${F}px`}class W{static instance=null;worker=null;charts=new Map;renderers=new Map;uiPlugins=[];pendingRenderers=[];chartIdCounter=0;_isDark=!1;_syncViews=!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((q)=>{for(let A of q){let F=A.target.dataset.chartId;if(!F)continue;let G=this.charts.get(F);if(!G)continue;if(G.visible=A.isIntersecting,this.worker?.postMessage({type:j.SET_VISIBILITY,id:F,visible:A.isIntersecting}),A.isIntersecting)this.drawChart(G)}},{threshold:0.01}),this.resizeObserver=new ResizeObserver((q)=>{for(let A of q){let F=A.target.dataset.chartId;if(!F)continue;let G=this.charts.get(F);if(!G)continue;let{width:J,height:T}=A.contentRect;if(J<=0||T<=0)continue;G.width=J,G.height=T;let N=devicePixelRatio||1;X(G.backCanvas,J,T),X(G.frontCanvas,J,T);let{bufferSizes:K,perSeriesPassMeta:$}=this.computeRendererMeta(G.renderer,G);this.worker?.postMessage({type:j.RESIZE,id:F,width:Math.round(J*N),height:Math.round(T*N),bufferSizes:K,perSeriesPassMeta:$}),this.drawChart(G)}})}static getInstance(){if(!W.instance)W.instance=new W;return W.instance}get isDark(){return this._isDark}get syncViews(){return this._syncViews}use(q){if("passes"in q){let A=q;if(this.renderers.set(A.name,A),this.worker)this.sendRendererRegistration(A);else this.pendingRenderers.push(A)}else{let A=q;if(!this.uiPlugins.some((F)=>F.name===A.name))this.uiPlugins.push(A)}}async init(){if(this.worker)return!0;return new Promise((q)=>{import("./worker-inline.js").then(({WORKER_CODE:A})=>{let F=new Blob([A],{type:"application/javascript"});this.worker=new Worker(URL.createObjectURL(F),{type:"module"}),this.setupWorkerHandlers(q)}).catch(()=>{this.worker=new Worker(new URL("./gpu-worker.js",import.meta.url),{type:"module"}),this.setupWorkerHandlers(q)})})}setupWorkerHandlers(q){if(!this.worker)return;this.worker.onmessage=(A)=>{let{type:F,...G}=A.data;switch(F){case j.GPU_READY:for(let J of this.pendingRenderers)this.sendRendererRegistration(J);this.pendingRenderers=[],q(!0);break;case j.ERROR:console.error("chartai:",G.code),q(!1);break;case j.STATS:this.currentStats={fps:G.fps,renderMs:G.renderMs,total:G.totalCharts,active:G.activeCharts};for(let J of this.statsCallbacks)J(this.currentStats);break}},this.worker.onerror=(A)=>{console.error("chartai:",A),q(!1)},this.worker.postMessage({type:j.INIT,isDark:this._isDark})}sendRendererRegistration(q){let A=(q.buffers??[]).map((F)=>({name:F.name,usages:F.usages,perSeries:q.passes.some((G)=>G.perSeries!==!1&&G.bindings.some((J)=>J.source===F.name))}));this.worker?.postMessage({type:j.REGISTER_RENDERER,name:q.name,shaders:q.shaders,passes:q.passes.map((F)=>({type:F.type,shader:F.shader,bindings:F.bindings,perSeries:F.perSeries!==!1,topology:F.topology,loadOp:F.loadOp,blend:F.blend})),bufferDefs:A,uniformDefs:q.uniforms??[]})}computeRendererMeta(q,A){let F={},G=[],J=A.series.length>0?A.series:[{rawX:[],rawY:[],extra:{},label:"",color:{r:0,g:0,b:0}}],T=devicePixelRatio||1,N=Math.round(A.width*T),K=Math.round(A.height*T);for(let $ of J){let O={width:N,height:K,samples:$.rawX.length,seriesCount:J.length,bounds:A.bounds,view:A.view};for(let R of q.buffers??[]){let P=R.bytes(O);F[R.name]=Math.max(F[R.name]??0,P)}G.push(q.passes.map((R)=>({dispatch:R.dispatch?.(O),draw:R.draw?.(O)})))}return{bufferSizes:F,perSeriesPassMeta:G}}create(q){if(!this.worker)throw Error("No worker. Call init().");let A=this.renderers.get(q.type);if(!A)throw Error(`No renderer "${q.type}". Call manager.use() first.`);let F=`chart-${++this.chartIdCounter}`,G=document.createElement("div");G.dataset.chartId=F,G.style.cssText="width:100%;height:100%;position:relative;";let J=document.createElement("div");J.dataset.chartId=F,J.style.cssText="width:100%;height:100%;position:relative;";let T=(B,Q)=>{let Z=document.createElement("canvas");return Z.style.cssText=`position:absolute;inset:0;width:100%;height:100%;pointer-events:${Q};z-index:${B};`,Z},N=T(0,"none"),K=T(1,"auto"),$=T(2,"none");J.append(N,K,$),G.appendChild(J),q.container.appendChild(G);let O;try{O=K.transferControlToOffscreen()}catch(B){throw Error(`Failed OffscreenCanvas: ${B}`)}let R=J.getBoundingClientRect(),P=R.width||400,V=R.height||200;if(X(O,P,V),X(N,P,V),X($,P,V),q.bgColor){let[B,Q,Z]=q.bgColor;J.style.background=`rgb(${Math.round(B*255)},${Math.round(Q*255)},${Math.round(Z*255)})`}let E={};for(let B of A.uniforms??[]){let Q=q[B.name];E[B.name]=typeof Q==="number"?Q:B.default}let L={id:F,config:q,el:G,backCanvas:N,frontCanvas:$,width:P,height:V,series:[],bounds:{minX:0,maxX:1,minY:0,maxY:1},view:{panX:0,panY:0,zoomX:1,zoomY:1},homeView:{panX:0,panY:0,zoomX:1,zoomY:1},visible:!0,dragging:!1,plugins:[...this.uiPlugins],renderer:A,customUniforms:E};this.charts.set(F,L);let I=devicePixelRatio||1,{bufferSizes:U,perSeriesPassMeta:D}=this.computeRendererMeta(A,L);this.worker.postMessage({type:j.REGISTER_CHART,id:F,canvas:O,rendererName:q.type,bgColor:q.bgColor??null,bufferSizes:U,perSeriesPassMeta:D,customUniformValues:E,width:Math.round(P*I),height:Math.round(V*I)},[O]),this.visibilityObserver.observe(G),this.resizeObserver.observe(J);for(let B of L.plugins)B.install?.(L,J);return A.install?.(L,J),this.updateSeries(F,q.series),new Y(F,this)}destroy(q){let A=this.charts.get(q);if(!A)return;A.renderer.uninstall?.(A);for(let G of A.plugins)G.uninstall?.(A);this.visibilityObserver.unobserve(A.el);let F=A.el.querySelector("div");if(F)this.resizeObserver.unobserve(F);A.el.remove(),this.worker?.postMessage({type:j.UNREGISTER_CHART,id:q}),this.charts.delete(q)}updateSeries(q,A){let F=this.charts.get(q);if(!F||!this.worker||A.length===0)return;F.config.hiddenSeries=A.reduce((L,I,U)=>{if(I.hidden)L.add(U);return L},new Set),F.series=A.map((L)=>{let I=L.x.length,U=z(L.color);if(I===0)return{label:L.label,color:U,rawX:[],rawY:[],extra:{}};let D=Array.from({length:I},(Q,Z)=>Z).sort((Q,Z)=>L.x[Q]-L.x[Z]),B={};for(let Q in L)if(Q!=="label"&&Q!=="color"&&Q!=="x"&&Q!=="y"&&Array.isArray(L[Q]))B[Q]=D.map((Z)=>L[Q][Z]);return{label:L.label,color:U,rawX:D.map((Q)=>L.x[Q]),rawY:D.map((Q)=>L.y[Q]),extra:B}});let G=F.renderer.computeBounds?.(F.series),{minX:J,maxX:T,minY:N,maxY:K}=G??(()=>{let L=1/0,I=-1/0,U=1/0,D=-1/0;for(let Z of F.series)for(let H=0;H<Z.rawX.length;H++){if(Z.rawX[H]<L)L=Z.rawX[H];if(Z.rawX[H]>I)I=Z.rawX[H];if(Z.rawY[H]<U)U=Z.rawY[H];if(Z.rawY[H]>D)D=Z.rawY[H]}let B=(I-L)*0.05||1,Q=(D-U)*0.1||1;return{minX:L-B,maxX:I+B,minY:U-Q,maxY:D+Q}})(),$=F.config.defaultBounds;if($){if($.minX!==void 0)J=$.minX;if($.maxX!==void 0)T=$.maxX;if($.minY!==void 0)N=$.minY;if($.maxY!==void 0)K=$.maxY}F.bounds={minX:J,maxX:T,minY:N,maxY:K};let{bufferSizes:O,perSeriesPassMeta:R}=this.computeRendererMeta(F.renderer,F),P=F.config.hiddenSeries??new Set,V=F.series.map((L,I)=>{let U={};for(let D in L.extra)U[D]=new Float32Array(L.extra[D]);return{label:L.label,colorR:L.color.r,colorG:L.color.g,colorB:L.color.b,dataX:new Float32Array(L.rawX),dataY:new Float32Array(L.rawY),extra:U,hidden:P.has(I)}}),E=V.flatMap((L)=>[L.dataX.buffer,L.dataY.buffer,...Object.values(L.extra).map((I)=>I.buffer)]);this.worker.postMessage({type:j.UPDATE_SERIES,id:q,series:V,bounds:F.bounds,bufferSizes:O,perSeriesPassMeta:R},E),this.sendViewTransform(F),this.drawChart(F)}setSyncViews(q){this._syncViews=q}setTheme(q){this._isDark=q,this.worker?.postMessage({type:j.THEME,isDark:q});for(let A of this.charts.values())this.drawChart(A)}onStats(q){return this.statsCallbacks.push(q),()=>{let A=this.statsCallbacks.indexOf(q);if(A>=0)this.statsCallbacks.splice(A,1)}}getStats(){return{...this.currentStats}}resetView(q){let A=this.charts.get(q);if(!A)return;for(let V of A.plugins)V.resetView?.(A);let{panX:F,panY:G,zoomX:J,zoomY:T}=A.view,{panX:N,panY:K,zoomX:$,zoomY:O}=A.homeView,R=performance.now(),P=()=>{let V=Math.min(1,(performance.now()-R)/300),E=1-Math.pow(1-V,3);if(A.view.panX=F+(N-F)*E,A.view.panY=G+(K-G)*E,A.view.zoomX=J+($-J)*E,A.view.zoomY=T+(O-T)*E,this.sendViewTransform(A),this.drawChart(A),this._syncViews)this.syncAllViews(A);if(V<1)requestAnimationFrame(P)};requestAnimationFrame(P)}setHiddenSeries(q,A){let F=this.charts.get(q);if(!F)return;F.config.hiddenSeries=new Set(A),this.worker?.postMessage({type:j.SET_STYLE,id:q,hiddenSeries:F.config.hiddenSeries}),this.drawChart(F)}requestRender(q){let A=this.charts.get(q);if(A)this.sendViewTransform(A)}sendViewTransform(q){this.worker?.postMessage({type:j.VIEW_TRANSFORM,id:q.id,panX:q.view.panX,panY:q.view.panY,zoomX:q.view.zoomX,zoomY:q.view.zoomY})}syncAllViews(q){let A=[];for(let F of this.charts.values())if(F.id!==q.id)F.view={...q.view},A.push({id:F.id}),this.drawChart(F);if(A.length>0)this.worker?.postMessage({type:j.BATCH_VIEW_TRANSFORM,panX:q.view.panX,panY:q.view.panY,zoomX:q.view.zoomX,zoomY:q.view.zoomY,transforms:A})}drawChart(q){if(!q.visible)return;let A=devicePixelRatio||1,F=q.backCanvas.getContext("2d");if(F){F.clearRect(0,0,q.backCanvas.width,q.backCanvas.height),F.save(),F.scale(A,A);for(let J of q.plugins)J.beforeDraw?.(F,q);F.restore()}let G=q.frontCanvas.getContext("2d");if(G){G.clearRect(0,0,q.frontCanvas.width,q.frontCanvas.height),G.save(),G.scale(A,A);for(let J of q.plugins)J.afterDraw?.(G,q);G.restore()}}}var w=W.getInstance();
|
|
2
|
+
export{Y as p,w as q};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var e=256,t="struct Uniforms{width: f32,height: f32,viewMinX: f32,viewMaxX: f32,viewMinY: f32,viewMaxY: f32,pointCount: u32,seriesCount: u32,isDark: u32,bgR: f32,bgG: f32,bgB: f32,dataMinX: f32,dataMaxX: f32,dataMinY: f32,dataMaxY: f32,};struct SeriesInfo{color: vec4f,visibleRange: vec2u,_pad0: f32,_pad1: f32,};struct SeriesIndex{index: u32,_pad0: u32,_pad1: u32,_pad2: u32,};",a="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;}",l="fn luma(c:vec4f)->f32{return dot(c.rgb,vec3f(.299,.587,.114));}fn laaa(uv:vec2f,t:texture_2d<f32>,s:sampler)->vec4f{let r=1./vec2f(textureDimensions(t));let m=textureSample(t,s,uv);let n=textureSample(t,s,uv+vec2f(0.,-r.y));let e=textureSample(t,s,uv+vec2f(r.x,0.));let w=textureSample(t,s,uv+vec2f(-r.x,0.));let sv=textureSample(t,s,uv+vec2f(0.,r.y));let lm=luma(m);let ln=luma(n);let le=luma(e);let lw=luma(w);let ls=luma(sv);let lo=min(lm,min(min(ln,ls),min(le,lw)));let hi=max(lm,max(max(ln,ls),max(le,lw)));let rng=hi-lo;if(rng<max(.0833,hi*.166)){return m;}return mix(m,(m+n+e+w+sv)*.2,min(rng*3.,1.));}struct BV{@builtin(position)p:vec4f,@location(0)uv:vec2f}@group(0)@binding(0)var inputTex:texture_2d<f32>;@group(0)@binding(1)var samp:sampler;@vertex fn vs(@builtin(vertex_index)i:u32)->BV{var p=array<vec2f,4>(vec2f(-1,-1),vec2f(1,-1),vec2f(-1,1),vec2f(1,1));var u=array<vec2f,4>(vec2f(0,1),vec2f(1,1),vec2f(0,0),vec2f(1,0));return BV(vec4f(p[i],0,1),u[i]);}@fragment fn fs(v:BV)->@location(0)vec4f{return laaa(v.uv,inputTex,samp);}";
|
|
2
|
-
export{e as
|
|
2
|
+
export{e as f,t as g,a as h,l as i};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var d=((a)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});
|
|
2
|
-
export{d as
|
|
2
|
+
export{d as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function I(y,z,j,A,B){let k=j.bounds.maxX-j.bounds.minX,q=j.bounds.maxY-j.bounds.minY,D=k/j.view.zoomX,E=q/j.view.zoomY,F=j.bounds.minX+j.view.panX*k,G=j.bounds.minY+j.view.panY*q;return{x:(y-F)/D*A,y:B*(1-(z-G)/E)}}function J(y,z,j,A,B){let k=j.bounds.maxX-j.bounds.minX,q=j.bounds.maxY-j.bounds.minY,D=k/j.view.zoomX,E=q/j.view.zoomY,F=j.bounds.minX+j.view.panX*k,G=j.bounds.minY+j.view.panY*q;return{x:F+y/A*D,y:G+(1-z/B)*E}}
|
|
2
|
+
export{I as j,J as k};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{o as z}from"./chunk-0eh4rzy9.js";import{q as d}from"./chunk-50bcv2hw.js";var k='-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',D=12;function S(e,t){let n=e-32-8,r=t-8-48;return{panX:n>0?-32/n:0,panY:r>0?-48/r:0,zoomX:n>0?n/e:1,zoomY:r>0?r/t:1}}var C=(e,t,l)=>{let i=t-e;if(i<=0)return[e];let o=i/l,s=10**Math.floor(Math.log10(o)),n=o/s,r=s*(n<=1.5?1:n<=3?2:n<=7?5:10),a=[];for(let m=Math.ceil(e/r)*r;m<=t;m+=r)a.push(m);return a},v=(e)=>{let{width:t,height:l}=e,i=z,{bounds:o,view:s}=e,n=o.maxX-o.minX,r=o.maxY-o.minY,a=n/s.zoomX,m=r/s.zoomY,p=o.minX+s.panX*n,h=o.minY+s.panY*r,b=e.config.bgColor??(d.isDark?[0.11,0.11,0.12]:[0.98,0.98,0.98]);return{w:t,h:l,m:i,rx:a,ry:m,mx:p,my:h,bg:`${Math.round(b[0]*255)},${Math.round(b[1]*255)},${Math.round(b[2]*255)}`,font:e.config.fontFamily??k,text:e.config.textColor??(d.isDark?"#c0c0c0":"#333333"),grid:e.config.gridColor??(d.isDark?"rgba(255,255,255,0.06)":"rgba(0,0,0,0.06)")}},A={name:"labels",install(e){let t=S(e.width,e.height);e.homeView=t,e.view={...t},d.requestRender(e.id)},beforeDraw(e,t){let l=S(t.width,t.height),i=t.homeView;if(t.homeView=l,l.zoomX!==i.zoomX||l.zoomY!==i.zoomY||l.panX!==i.panX||l.panY!==i.panY)t.view={...l},d.requestRender(t.id);let{w:o,h:s,m:n,rx:r,ry:a,mx:m,my:p,grid:h}=v(t);e.strokeStyle=h,e.lineWidth=1,e.beginPath(),C(p,p+a,7).forEach((b)=>{let f=s*(1-(b-p)/a);if(f>5&&f<s-n.bottom-5)e.moveTo(n.left,f),e.lineTo(o,f)}),C(m,m+r,8).forEach((b)=>{let f=o*((b-m)/r);if(f>n.left&&f<o)e.moveTo(f,0),e.lineTo(f,s-n.bottom)}),e.stroke()},afterDraw(e,t){let{w:l,h:i,m:o,rx:s,ry:n,mx:r,my:a,bg:m,font:p,text:h}=v(t),{formatX:b=String,formatY:f=String,labelSize:L=D}=t.config,y=(u,g,c,w,X)=>{let Y=u==="left"?e.createLinearGradient(g,0,g+w,0):e.createLinearGradient(0,c,0,c+X),M=u==="left"?[1,0.7,0.2,0.05,0]:[0,0.05,0.2,0.7,1];[0,0.35,0.55,0.7,1].forEach((T,E)=>Y.addColorStop(T,`rgba(${m},${M[E]})`)),e.fillStyle=Y,e.fillRect(g,c,w,X)};y("left",0,0,o.left+20,i),y("bottom",0,i-o.bottom-20,l,o.bottom+20),e.font=`${L}px ${p}`,e.fillStyle=h,e.textAlign="right",e.textBaseline="middle",C(a,a+n,7).forEach((u)=>{let g=i*(1-(u-a)/n);if(g>5&&g<i-o.bottom-5)e.fillText(f(u),o.left-5,g)}),e.textAlign="right",e.textBaseline="top",C(r,r+s,8).forEach((u)=>{let g=l*((u-r)/s);if(g<o.left-10||g>l+30)return;e.save(),e.translate(g,i-o.bottom+5),e.rotate(-Math.PI/14),e.fillText(b(u),0,0),e.restore()})}};
|
|
2
|
+
export{k as l,D as m,A as n};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import{f as v,g as K,h as H}from"./chunk-64q9a7nw.js";var b=`
|
|
2
|
+
struct CandleUniforms {
|
|
3
|
+
maxSamples: f32,
|
|
4
|
+
upColor: u32,
|
|
5
|
+
downColor: u32,
|
|
6
|
+
binSize: u32,
|
|
7
|
+
interval: f32,
|
|
8
|
+
_p0: u32, _p1: u32, _p2: u32,
|
|
9
|
+
};
|
|
10
|
+
struct CandleData {
|
|
11
|
+
screenX: f32,
|
|
12
|
+
barWidth: f32,
|
|
13
|
+
low: f32,
|
|
14
|
+
bodyBottom: f32,
|
|
15
|
+
bodyTop: f32,
|
|
16
|
+
high: f32,
|
|
17
|
+
isUp: f32,
|
|
18
|
+
};`,B=`
|
|
19
|
+
fn effectiveInterval() -> f32 {
|
|
20
|
+
if (cu.interval > 0.0) { return cu.interval; }
|
|
21
|
+
let raw = (u.viewMaxX - u.viewMinX) / u.width * f32(cu.binSize);
|
|
22
|
+
let steps = array<f32, 20>(
|
|
23
|
+
1.0, 2.0, 5.0, 10.0, 15.0, 30.0,
|
|
24
|
+
60.0, 120.0, 300.0, 600.0, 900.0, 1800.0,
|
|
25
|
+
3600.0, 7200.0, 14400.0, 43200.0,
|
|
26
|
+
86400.0, 259200.0, 604800.0, 2592000.0
|
|
27
|
+
);
|
|
28
|
+
for (var i = 0u; i < 20u; i++) {
|
|
29
|
+
if (steps[i] >= raw) { return steps[i]; }
|
|
30
|
+
}
|
|
31
|
+
return raw;
|
|
32
|
+
}`,G=`${K}${b}@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> dataClose: array<f32>;@group(0)@binding(3)var<storage,read_write> candleData: array<CandleData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;@group(0)@binding(6)var<uniform> cu: CandleUniforms;@group(0)@binding(7)var<storage,read> dataOpen: array<f32>;@group(0)@binding(8)var<storage,read> dataHigh: array<f32>;@group(0)@binding(9)var<storage,read> dataLow: array<f32>;${H}${B}@compute @workgroup_size(${v})fn main(@builtin(global_invocation_id)id: vec3u){let binIdx = id.x;let totalPixels = u32(u.width);let count = u.pointCount;if(count == 0u){if(binIdx < totalPixels){candleData[binIdx] = CandleData(0.0,0.0,0.0,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){if(binIdx < totalPixels){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);}return;}let interval = effectiveInterval();let alignedStart = floor(u.viewMinX / interval)* interval;let numBins = min(u32(ceil(viewRangeX / interval))+ 2u,totalPixels);if(binIdx >= numBins){return;}let binMinX = alignedStart + f32(binIdx)* interval;let binMaxX = binMinX + interval;if(binMinX >= u.viewMaxX){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);return;}let binMidX = binMinX + interval * 0.5;let screenX =(binMidX - u.viewMinX)/ viewRangeX;let barWidth = interval / viewRangeX;let onePixel = 1.0 / u.width;let bw = max(barWidth * 0.95,onePixel);let startIdx = lowerBound(binMinX,count);var endIdx = lowerBound(binMaxX,count);endIdx = min(endIdx,count);if(startIdx >= endIdx){var bestIdx: u32 = 0u;var bestDist: f32 = 1e10;var hit = false;if(startIdx < count){let bx = dataX[startIdx];let hw = interval * 0.5;if(binMinX < bx + hw && binMaxX > bx - hw){bestIdx = startIdx;bestDist = abs(bx - binMidX);hit = true;}}if(startIdx > 0u){let prev = startIdx - 1u;let bx = dataX[prev];let hw = interval * 0.5;if(binMinX < bx + hw && binMaxX > bx - hw){let d = abs(bx - binMidX);if(!hit || d < bestDist){bestIdx = prev;}hit = true;}}if(!hit){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);return;}let o = dataOpen[bestIdx];let h = dataHigh[bestIdx];let l = dataLow[bestIdx];let c = dataClose[bestIdx];candleData[binIdx] = CandleData(screenX,bw,l,min(o,c),max(o,c),h,select(0.0,1.0,c>=o));return;}let o = dataOpen[startIdx];var h = dataHigh[startIdx];var l = dataLow[startIdx];let c = dataClose[endIdx - 1u];let rangeCount = endIdx - startIdx;let maxSamples = u32(cu.maxSamples);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){h = max(h,dataHigh[idx]);l = min(l,dataLow[idx]);}}h = max(h,dataHigh[endIdx - 1u]);l = min(l,dataLow[endIdx - 1u]);}else{for(var i = startIdx;i < endIdx;i++){h = max(h,dataHigh[i]);l = min(l,dataLow[i]);}}candleData[binIdx] = CandleData(screenX,bw,l,min(o,c),max(o,c),h,select(0.0,1.0,c>=o));}`,V=`${K}${b}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> candleData: array<CandleData>;@group(0)@binding(2)var<uniform> cu: CandleUniforms;${B}struct VertexOutput{@builtin(position)pos: vec4f,@location(0)@interpolate(flat)isUp: f32,@location(1)@interpolate(flat)isWick: f32,};@vertex fn vs(@builtin(vertex_index)vi: u32)-> VertexOutput{var out: VertexOutput;let viewRangeX = u.viewMaxX - u.viewMinX;let interval = effectiveInterval();let numBins = min(u32(ceil(viewRangeX / interval))+ 2u,u32(u.width));let colIdx = vi / 30u;let localVi = vi % 30u;let section = localVi / 6u;let vertexType = localVi % 6u;if(colIdx >= numBins){out.pos = vec4f(0.0,0.0,0.0,0.0);out.isUp = 0.0;out.isWick = 0.0;return out;}let cd = candleData[colIdx];if(cd.barWidth <= 0.0){out.pos = vec4f(0.0,0.0,0.0,0.0);out.isUp = 0.0;out.isWick = 0.0;return out;}out.isUp = cd.isUp;out.isWick = select(0.0,1.0,section > 0u);let viewRangeY = u.viewMaxY - u.viewMinY;let safeRangeY = select(viewRangeY,1.0,viewRangeY < 0.0001);let onePixelX = 1.0 / u.width;let onePixelY = 1.0 / u.height;var sLeft: f32;var sRight: f32;var sTop: f32;var sBottom: f32;if(section == 0u){let nb =(cd.bodyBottom - u.viewMinY)/ safeRangeY;let nt =(cd.bodyTop - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = cd.barWidth * 0.5;sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 1u){let nb =(cd.bodyTop - u.viewMinY)/ safeRangeY;let nt =(cd.high - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = max(onePixelX,cd.barWidth * 0.08);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 2u){let nb =(cd.low - u.viewMinY)/ safeRangeY;let nt =(cd.bodyBottom - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = max(onePixelX,cd.barWidth * 0.08);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 3u){let sy = 1.0 -(cd.high - u.viewMinY)/ safeRangeY;let wickHW = max(onePixelX,cd.barWidth * 0.08);let capHH = wickHW * u.width / u.height;sTop = sy - capHH;sBottom = sy + capHH;let hw = max(onePixelX * 2.0,cd.barWidth * 0.28);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else{let sy = 1.0 -(cd.low - u.viewMinY)/ safeRangeY;let wickHW = max(onePixelX,cd.barWidth * 0.08);let capHH = wickHW * u.width / u.height;sTop = sy - capHH;sBottom = sy + capHH;let hw = max(onePixelX * 2.0,cd.barWidth * 0.28);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}var positions = array<vec2f,6>(vec2f(sLeft,sBottom),vec2f(sRight,sBottom),vec2f(sLeft,sTop),vec2f(sLeft,sTop),vec2f(sRight,sBottom),vec2f(sRight,sTop));let sp = positions[vertexType];out.pos = vec4f(sp.x * 2.0 - 1.0,1.0 - sp.y * 2.0,0.0,1.0);return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{let upRgb = unpack4x8unorm(cu.upColor).rgb;let downRgb = unpack4x8unorm(cu.downColor).rgb;let base = select(downRgb,upRgb,in.isUp > 0.5);let color = select(base,base * 0.65,in.isWick > 0.5);return vec4f(color,0.92);}`;var M=(q,J,Q)=>(Math.round(q*255)&255|(Math.round(J*255)&255)<<8|(Math.round(Q*255)&255)<<16|-16777216)>>>0,O=28,y={name:"candlestick",shaders:{compute:G,render:V},uniforms:[{name:"maxSamples",type:"f32",default:1e4},{name:"upColor",type:"u32",default:M(0.2,0.7,0.3)},{name:"downColor",type:"u32",default:M(0.9,0.3,0.3)},{name:"binSize",type:"u32",default:8},{name:"interval",type:"f32",default:0}],buffers:[{name:"candleBuffer",bytes:({width:q})=>Math.max(16,q*O),usages:["STORAGE"]}],passes:[{type:"compute",shader:"compute",perSeries:!0,dispatch:({width:q})=>({x:Math.ceil(Math.max(1,q)/v)}),bindings:[{binding:0,source:"uniforms"},{binding:1,source:"x-data"},{binding:2,source:"y-data"},{binding:3,source:"candleBuffer",write:!0},{binding:4,source:"series-info"},{binding:5,source:"series-index"},{binding:6,source:"custom-uniforms"},{binding:7,source:"open-data"},{binding:8,source:"high-data"},{binding:9,source:"low-data"}]},{type:"render",shader:"render",topology:"triangle-list",loadOp:"load",blend:{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}},draw:({width:q})=>q*30,bindings:[{binding:0,source:"uniforms"},{binding:1,source:"candleBuffer"},{binding:2,source:"custom-uniforms"}]}],computeBounds(q){let J=1/0,Q=-1/0,Z=1/0,$=-1/0;for(let z of q){for(let j of z.rawX){if(j<J)J=j;if(j>Q)Q=j}for(let j of z.extra.high??[])if(j>$)$=j;for(let j of z.extra.low??[])if(j<Z)Z=j}if(!isFinite(J))return{minX:0,maxX:1,minY:0,maxY:1};let W=(Q-J)*0.05||1,k=($-Z)*0.1||1;return{minX:J-W,maxX:Q+W,minY:Z-k,maxY:$+k}}};
|
|
33
|
+
export{G as a,M as b,y as c};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
export{n as
|
|
1
|
+
import{f as a,g as e,h as t}from"./chunk-64q9a7nw.js";var n=`${e}struct LineUniforms{maxSamplesPerPixel: u32,_p1: u32,_p2: u32,_p3: u32};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>;@group(0)@binding(5)var<uniform> lu: LineUniforms;${t}@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;if(pixelMaxX < u.dataMinX || pixelMinX > u.dataMaxX){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}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 = lu.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);}`,r=`${e}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);}`;
|
|
2
|
+
export{n as d,r as e};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/plugins/coords.ts
|
|
2
|
+
function dataToScreen(dataX, dataY, chart, width, height) {
|
|
3
|
+
const rX = chart.bounds.maxX - chart.bounds.minX;
|
|
4
|
+
const rY = chart.bounds.maxY - chart.bounds.minY;
|
|
5
|
+
const vW = rX / chart.view.zoomX;
|
|
6
|
+
const vH = rY / chart.view.zoomY;
|
|
7
|
+
const vMinX = chart.bounds.minX + chart.view.panX * rX;
|
|
8
|
+
const vMinY = chart.bounds.minY + chart.view.panY * rY;
|
|
9
|
+
return {
|
|
10
|
+
x: (dataX - vMinX) / vW * width,
|
|
11
|
+
y: height * (1 - (dataY - vMinY) / vH)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function screenToData(screenX, screenY, chart, width, height) {
|
|
15
|
+
const rX = chart.bounds.maxX - chart.bounds.minX;
|
|
16
|
+
const rY = chart.bounds.maxY - chart.bounds.minY;
|
|
17
|
+
const vW = rX / chart.view.zoomX;
|
|
18
|
+
const vH = rY / chart.view.zoomY;
|
|
19
|
+
const vMinX = chart.bounds.minX + chart.view.panX * rX;
|
|
20
|
+
const vMinY = chart.bounds.minY + chart.view.panY * rY;
|
|
21
|
+
return {
|
|
22
|
+
x: vMinX + screenX / width * vW,
|
|
23
|
+
y: vMinY + (1 - screenY / height) * vH
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { dataToScreen, screenToData };
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-93yrr7er.js";
|
|
4
4
|
import {
|
|
5
5
|
__require
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5gtx3pza.js";
|
|
7
7
|
|
|
8
8
|
// src/chart-library.ts
|
|
9
9
|
class Chart {
|
|
@@ -40,6 +40,13 @@ class Chart {
|
|
|
40
40
|
values: workerValues
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
+
if ("hiddenSeries" in patch) {
|
|
44
|
+
this._mgr["worker"]?.postMessage({
|
|
45
|
+
type: M.SET_STYLE,
|
|
46
|
+
id: this.id,
|
|
47
|
+
hiddenSeries: patch.hiddenSeries ?? new Set
|
|
48
|
+
});
|
|
49
|
+
}
|
|
43
50
|
if ("bgColor" in patch && patch.bgColor !== undefined) {
|
|
44
51
|
const [r, g, b] = patch.bgColor;
|
|
45
52
|
const wrap = c.el.querySelector("div");
|
|
@@ -312,10 +319,10 @@ class _ChartManager {
|
|
|
312
319
|
}
|
|
313
320
|
create(config) {
|
|
314
321
|
if (!this.worker)
|
|
315
|
-
throw new Error("Call init()
|
|
322
|
+
throw new Error("No worker. Call init().");
|
|
316
323
|
const renderer = this.renderers.get(config.type);
|
|
317
324
|
if (!renderer)
|
|
318
|
-
throw new Error(`No renderer "${config.type}". Call manager.use()
|
|
325
|
+
throw new Error(`No renderer "${config.type}". Call manager.use() first.`);
|
|
319
326
|
const id = `chart-${++this.chartIdCounter}`;
|
|
320
327
|
const el = document.createElement("div");
|
|
321
328
|
el.dataset.chartId = id;
|
|
@@ -338,7 +345,7 @@ class _ChartManager {
|
|
|
338
345
|
try {
|
|
339
346
|
offscreen = gpuCanvas.transferControlToOffscreen();
|
|
340
347
|
} catch (e) {
|
|
341
|
-
throw new Error(`Failed
|
|
348
|
+
throw new Error(`Failed OffscreenCanvas: ${e}`);
|
|
342
349
|
}
|
|
343
350
|
const rect = wrap.getBoundingClientRect();
|
|
344
351
|
const cssW = rect.width || 400;
|
|
@@ -366,6 +373,7 @@ class _ChartManager {
|
|
|
366
373
|
series: [],
|
|
367
374
|
bounds: { minX: 0, maxX: 1, minY: 0, maxY: 1 },
|
|
368
375
|
view: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
|
|
376
|
+
homeView: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
|
|
369
377
|
visible: true,
|
|
370
378
|
dragging: false,
|
|
371
379
|
plugins: [...this.uiPlugins],
|
|
@@ -414,6 +422,11 @@ class _ChartManager {
|
|
|
414
422
|
const chart = this.charts.get(id);
|
|
415
423
|
if (!chart || !this.worker || series.length === 0)
|
|
416
424
|
return;
|
|
425
|
+
chart.config.hiddenSeries = series.reduce((acc, s, i) => {
|
|
426
|
+
if (s.hidden)
|
|
427
|
+
acc.add(i);
|
|
428
|
+
return acc;
|
|
429
|
+
}, new Set);
|
|
417
430
|
chart.series = series.map((s) => {
|
|
418
431
|
const n = s.x.length;
|
|
419
432
|
const color = parseColor(s.color);
|
|
@@ -471,7 +484,8 @@ class _ChartManager {
|
|
|
471
484
|
}
|
|
472
485
|
chart.bounds = { minX, maxX, minY, maxY };
|
|
473
486
|
const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
|
|
474
|
-
const
|
|
487
|
+
const hidden = chart.config.hiddenSeries ?? new Set;
|
|
488
|
+
const seriesData = chart.series.map((s, i) => {
|
|
475
489
|
const extra = {};
|
|
476
490
|
for (const key in s.extra)
|
|
477
491
|
extra[key] = new Float32Array(s.extra[key]);
|
|
@@ -482,7 +496,8 @@ class _ChartManager {
|
|
|
482
496
|
colorB: s.color.b,
|
|
483
497
|
dataX: new Float32Array(s.rawX),
|
|
484
498
|
dataY: new Float32Array(s.rawY),
|
|
485
|
-
extra
|
|
499
|
+
extra,
|
|
500
|
+
hidden: hidden.has(i)
|
|
486
501
|
};
|
|
487
502
|
});
|
|
488
503
|
const transferables = seriesData.flatMap((s) => [
|
|
@@ -528,14 +543,15 @@ class _ChartManager {
|
|
|
528
543
|
for (const p of chart.plugins)
|
|
529
544
|
p.resetView?.(chart);
|
|
530
545
|
const { panX: spx, panY: spy, zoomX: szx, zoomY: szy } = chart.view;
|
|
546
|
+
const { panX: tpx, panY: tpy, zoomX: tzx, zoomY: tzy } = chart.homeView;
|
|
531
547
|
const t0 = performance.now();
|
|
532
548
|
const animate = () => {
|
|
533
549
|
const t = Math.min(1, (performance.now() - t0) / 300);
|
|
534
550
|
const e = 1 - Math.pow(1 - t, 3);
|
|
535
|
-
chart.view.panX = spx
|
|
536
|
-
chart.view.panY = spy
|
|
537
|
-
chart.view.zoomX = szx + (
|
|
538
|
-
chart.view.zoomY = szy + (
|
|
551
|
+
chart.view.panX = spx + (tpx - spx) * e;
|
|
552
|
+
chart.view.panY = spy + (tpy - spy) * e;
|
|
553
|
+
chart.view.zoomX = szx + (tzx - szx) * e;
|
|
554
|
+
chart.view.zoomY = szy + (tzy - szy) * e;
|
|
539
555
|
this.sendViewTransform(chart);
|
|
540
556
|
this.drawChart(chart);
|
|
541
557
|
if (this._syncViews)
|
|
@@ -545,6 +561,18 @@ class _ChartManager {
|
|
|
545
561
|
};
|
|
546
562
|
requestAnimationFrame(animate);
|
|
547
563
|
}
|
|
564
|
+
setHiddenSeries(id, hidden) {
|
|
565
|
+
const chart = this.charts.get(id);
|
|
566
|
+
if (!chart)
|
|
567
|
+
return;
|
|
568
|
+
chart.config.hiddenSeries = new Set(hidden);
|
|
569
|
+
this.worker?.postMessage({
|
|
570
|
+
type: M.SET_STYLE,
|
|
571
|
+
id,
|
|
572
|
+
hiddenSeries: chart.config.hiddenSeries
|
|
573
|
+
});
|
|
574
|
+
this.drawChart(chart);
|
|
575
|
+
}
|
|
548
576
|
requestRender(id) {
|
|
549
577
|
const chart = this.charts.get(id);
|
|
550
578
|
if (chart)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BINARY_SEARCH,
|
|
3
|
+
COMPUTE_WG,
|
|
4
|
+
UNIFORM_STRUCT
|
|
5
|
+
} from "./chunk-0jepamv9.js";
|
|
6
|
+
|
|
7
|
+
// src/shaders/candlestick.ts
|
|
8
|
+
var CANDLE_TYPES = `
|
|
9
|
+
struct CandleUniforms {
|
|
10
|
+
maxSamples: f32,
|
|
11
|
+
upColor: u32,
|
|
12
|
+
downColor: u32,
|
|
13
|
+
binSize: u32,
|
|
14
|
+
interval: f32,
|
|
15
|
+
_p0: u32, _p1: u32, _p2: u32,
|
|
16
|
+
};
|
|
17
|
+
struct CandleData {
|
|
18
|
+
screenX: f32,
|
|
19
|
+
barWidth: f32,
|
|
20
|
+
low: f32,
|
|
21
|
+
bodyBottom: f32,
|
|
22
|
+
bodyTop: f32,
|
|
23
|
+
high: f32,
|
|
24
|
+
isUp: f32,
|
|
25
|
+
};`;
|
|
26
|
+
var EFFECTIVE_INTERVAL = `
|
|
27
|
+
fn effectiveInterval() -> f32 {
|
|
28
|
+
if (cu.interval > 0.0) { return cu.interval; }
|
|
29
|
+
let raw = (u.viewMaxX - u.viewMinX) / u.width * f32(cu.binSize);
|
|
30
|
+
let steps = array<f32, 20>(
|
|
31
|
+
1.0, 2.0, 5.0, 10.0, 15.0, 30.0,
|
|
32
|
+
60.0, 120.0, 300.0, 600.0, 900.0, 1800.0,
|
|
33
|
+
3600.0, 7200.0, 14400.0, 43200.0,
|
|
34
|
+
86400.0, 259200.0, 604800.0, 2592000.0
|
|
35
|
+
);
|
|
36
|
+
for (var i = 0u; i < 20u; i++) {
|
|
37
|
+
if (steps[i] >= raw) { return steps[i]; }
|
|
38
|
+
}
|
|
39
|
+
return raw;
|
|
40
|
+
}`;
|
|
41
|
+
var CANDLESTICK_COMPUTE_SHADER = `${UNIFORM_STRUCT}${CANDLE_TYPES}@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> dataClose: array<f32>;@group(0)@binding(3)var<storage,read_write> candleData: array<CandleData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;@group(0)@binding(6)var<uniform> cu: CandleUniforms;@group(0)@binding(7)var<storage,read> dataOpen: array<f32>;@group(0)@binding(8)var<storage,read> dataHigh: array<f32>;@group(0)@binding(9)var<storage,read> dataLow: array<f32>;${BINARY_SEARCH}${EFFECTIVE_INTERVAL}@compute @workgroup_size(${COMPUTE_WG})fn main(@builtin(global_invocation_id)id: vec3u){let binIdx = id.x;let totalPixels = u32(u.width);let count = u.pointCount;if(count == 0u){if(binIdx < totalPixels){candleData[binIdx] = CandleData(0.0,0.0,0.0,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){if(binIdx < totalPixels){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);}return;}let interval = effectiveInterval();let alignedStart = floor(u.viewMinX / interval)* interval;let numBins = min(u32(ceil(viewRangeX / interval))+ 2u,totalPixels);if(binIdx >= numBins){return;}let binMinX = alignedStart + f32(binIdx)* interval;let binMaxX = binMinX + interval;if(binMinX >= u.viewMaxX){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);return;}let binMidX = binMinX + interval * 0.5;let screenX =(binMidX - u.viewMinX)/ viewRangeX;let barWidth = interval / viewRangeX;let onePixel = 1.0 / u.width;let bw = max(barWidth * 0.95,onePixel);let startIdx = lowerBound(binMinX,count);var endIdx = lowerBound(binMaxX,count);endIdx = min(endIdx,count);if(startIdx >= endIdx){var bestIdx: u32 = 0u;var bestDist: f32 = 1e10;var hit = false;if(startIdx < count){let bx = dataX[startIdx];let hw = interval * 0.5;if(binMinX < bx + hw && binMaxX > bx - hw){bestIdx = startIdx;bestDist = abs(bx - binMidX);hit = true;}}if(startIdx > 0u){let prev = startIdx - 1u;let bx = dataX[prev];let hw = interval * 0.5;if(binMinX < bx + hw && binMaxX > bx - hw){let d = abs(bx - binMidX);if(!hit || d < bestDist){bestIdx = prev;}hit = true;}}if(!hit){candleData[binIdx] = CandleData(0.0,0.0,0.0,0.0,0.0,0.0,0.0);return;}let o = dataOpen[bestIdx];let h = dataHigh[bestIdx];let l = dataLow[bestIdx];let c = dataClose[bestIdx];candleData[binIdx] = CandleData(screenX,bw,l,min(o,c),max(o,c),h,select(0.0,1.0,c>=o));return;}let o = dataOpen[startIdx];var h = dataHigh[startIdx];var l = dataLow[startIdx];let c = dataClose[endIdx - 1u];let rangeCount = endIdx - startIdx;let maxSamples = u32(cu.maxSamples);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){h = max(h,dataHigh[idx]);l = min(l,dataLow[idx]);}}h = max(h,dataHigh[endIdx - 1u]);l = min(l,dataLow[endIdx - 1u]);}else{for(var i = startIdx;i < endIdx;i++){h = max(h,dataHigh[i]);l = min(l,dataLow[i]);}}candleData[binIdx] = CandleData(screenX,bw,l,min(o,c),max(o,c),h,select(0.0,1.0,c>=o));}`;
|
|
42
|
+
var CANDLESTICK_RENDER_SHADER = `${UNIFORM_STRUCT}${CANDLE_TYPES}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> candleData: array<CandleData>;@group(0)@binding(2)var<uniform> cu: CandleUniforms;${EFFECTIVE_INTERVAL}struct VertexOutput{@builtin(position)pos: vec4f,@location(0)@interpolate(flat)isUp: f32,@location(1)@interpolate(flat)isWick: f32,};@vertex fn vs(@builtin(vertex_index)vi: u32)-> VertexOutput{var out: VertexOutput;let viewRangeX = u.viewMaxX - u.viewMinX;let interval = effectiveInterval();let numBins = min(u32(ceil(viewRangeX / interval))+ 2u,u32(u.width));let colIdx = vi / 30u;let localVi = vi % 30u;let section = localVi / 6u;let vertexType = localVi % 6u;if(colIdx >= numBins){out.pos = vec4f(0.0,0.0,0.0,0.0);out.isUp = 0.0;out.isWick = 0.0;return out;}let cd = candleData[colIdx];if(cd.barWidth <= 0.0){out.pos = vec4f(0.0,0.0,0.0,0.0);out.isUp = 0.0;out.isWick = 0.0;return out;}out.isUp = cd.isUp;out.isWick = select(0.0,1.0,section > 0u);let viewRangeY = u.viewMaxY - u.viewMinY;let safeRangeY = select(viewRangeY,1.0,viewRangeY < 0.0001);let onePixelX = 1.0 / u.width;let onePixelY = 1.0 / u.height;var sLeft: f32;var sRight: f32;var sTop: f32;var sBottom: f32;if(section == 0u){let nb =(cd.bodyBottom - u.viewMinY)/ safeRangeY;let nt =(cd.bodyTop - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = cd.barWidth * 0.5;sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 1u){let nb =(cd.bodyTop - u.viewMinY)/ safeRangeY;let nt =(cd.high - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = max(onePixelX,cd.barWidth * 0.08);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 2u){let nb =(cd.low - u.viewMinY)/ safeRangeY;let nt =(cd.bodyBottom - u.viewMinY)/ safeRangeY;sBottom = 1.0 - nb;sTop = 1.0 - nt;let hw = max(onePixelX,cd.barWidth * 0.08);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else if(section == 3u){let sy = 1.0 -(cd.high - u.viewMinY)/ safeRangeY;let wickHW = max(onePixelX,cd.barWidth * 0.08);let capHH = wickHW * u.width / u.height;sTop = sy - capHH;sBottom = sy + capHH;let hw = max(onePixelX * 2.0,cd.barWidth * 0.28);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}else{let sy = 1.0 -(cd.low - u.viewMinY)/ safeRangeY;let wickHW = max(onePixelX,cd.barWidth * 0.08);let capHH = wickHW * u.width / u.height;sTop = sy - capHH;sBottom = sy + capHH;let hw = max(onePixelX * 2.0,cd.barWidth * 0.28);sLeft = cd.screenX - hw;sRight = cd.screenX + hw;}var positions = array<vec2f,6>(vec2f(sLeft,sBottom),vec2f(sRight,sBottom),vec2f(sLeft,sTop),vec2f(sLeft,sTop),vec2f(sRight,sBottom),vec2f(sRight,sTop));let sp = positions[vertexType];out.pos = vec4f(sp.x * 2.0 - 1.0,1.0 - sp.y * 2.0,0.0,1.0);return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{let upRgb = unpack4x8unorm(cu.upColor).rgb;let downRgb = unpack4x8unorm(cu.downColor).rgb;let base = select(downRgb,upRgb,in.isUp > 0.5);let color = select(base,base * 0.65,in.isWick > 0.5);return vec4f(color,0.92);}`;
|
|
43
|
+
|
|
44
|
+
// src/charts/candlestick.ts
|
|
45
|
+
var packRGB = (r, g, b) => (Math.round(r * 255) & 255 | (Math.round(g * 255) & 255) << 8 | (Math.round(b * 255) & 255) << 16 | 255 << 24) >>> 0;
|
|
46
|
+
var BYTES_PER_CANDLE = 7 * 4;
|
|
47
|
+
var CandlestickChart = {
|
|
48
|
+
name: "candlestick",
|
|
49
|
+
shaders: {
|
|
50
|
+
compute: CANDLESTICK_COMPUTE_SHADER,
|
|
51
|
+
render: CANDLESTICK_RENDER_SHADER
|
|
52
|
+
},
|
|
53
|
+
uniforms: [
|
|
54
|
+
{ name: "maxSamples", type: "f32", default: 1e4 },
|
|
55
|
+
{ name: "upColor", type: "u32", default: packRGB(0.2, 0.7, 0.3) },
|
|
56
|
+
{ name: "downColor", type: "u32", default: packRGB(0.9, 0.3, 0.3) },
|
|
57
|
+
{ name: "binSize", type: "u32", default: 8 },
|
|
58
|
+
{ name: "interval", type: "f32", default: 0 }
|
|
59
|
+
],
|
|
60
|
+
buffers: [
|
|
61
|
+
{
|
|
62
|
+
name: "candleBuffer",
|
|
63
|
+
bytes: ({ width }) => Math.max(16, width * BYTES_PER_CANDLE),
|
|
64
|
+
usages: ["STORAGE"]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
passes: [
|
|
68
|
+
{
|
|
69
|
+
type: "compute",
|
|
70
|
+
shader: "compute",
|
|
71
|
+
perSeries: true,
|
|
72
|
+
dispatch: ({ width }) => ({ x: Math.ceil(Math.max(1, width) / COMPUTE_WG) }),
|
|
73
|
+
bindings: [
|
|
74
|
+
{ binding: 0, source: "uniforms" },
|
|
75
|
+
{ binding: 1, source: "x-data" },
|
|
76
|
+
{ binding: 2, source: "y-data" },
|
|
77
|
+
{ binding: 3, source: "candleBuffer", write: true },
|
|
78
|
+
{ binding: 4, source: "series-info" },
|
|
79
|
+
{ binding: 5, source: "series-index" },
|
|
80
|
+
{ binding: 6, source: "custom-uniforms" },
|
|
81
|
+
{ binding: 7, source: "open-data" },
|
|
82
|
+
{ binding: 8, source: "high-data" },
|
|
83
|
+
{ binding: 9, source: "low-data" }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "render",
|
|
88
|
+
shader: "render",
|
|
89
|
+
topology: "triangle-list",
|
|
90
|
+
loadOp: "load",
|
|
91
|
+
blend: {
|
|
92
|
+
color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha" },
|
|
93
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha" }
|
|
94
|
+
},
|
|
95
|
+
draw: ({ width }) => width * 30,
|
|
96
|
+
bindings: [
|
|
97
|
+
{ binding: 0, source: "uniforms" },
|
|
98
|
+
{ binding: 1, source: "candleBuffer" },
|
|
99
|
+
{ binding: 2, source: "custom-uniforms" }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
computeBounds(series) {
|
|
104
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
105
|
+
for (const s of series) {
|
|
106
|
+
for (const x of s.rawX) {
|
|
107
|
+
if (x < minX)
|
|
108
|
+
minX = x;
|
|
109
|
+
if (x > maxX)
|
|
110
|
+
maxX = x;
|
|
111
|
+
}
|
|
112
|
+
for (const y of s.extra.high ?? []) {
|
|
113
|
+
if (y > maxY)
|
|
114
|
+
maxY = y;
|
|
115
|
+
}
|
|
116
|
+
for (const y of s.extra.low ?? []) {
|
|
117
|
+
if (y < minY)
|
|
118
|
+
minY = y;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!isFinite(minX))
|
|
122
|
+
return { minX: 0, maxX: 1, minY: 0, maxY: 1 };
|
|
123
|
+
const px = (maxX - minX) * 0.05 || 1;
|
|
124
|
+
const py = (maxY - minY) * 0.1 || 1;
|
|
125
|
+
return { minX: minX - px, maxX: maxX + px, minY: minY - py, maxY: maxY + py };
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export { CANDLESTICK_COMPUTE_SHADER, packRGB, CandlestickChart };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var a={INIT:0,THEME:1,REGISTER_RENDERER:2,REGISTER_CHART:3,UNREGISTER_CHART:4,UPDATE_SERIES:5,RESIZE:6,VIEW_TRANSFORM:7,BATCH_VIEW_TRANSFORM:8,SET_VISIBILITY:9,SET_STYLE:10,SET_UNIFORMS:11,GPU_READY:12,ERROR:13,STATS:14},g={NO_GPU:"e1:no-gpu",NO_ADAPTER:"e2:no-adapter",DEVICE_LOST:"e3:device-lost",NOT_READY:"e4:not-ready",COMPILE:"e5:compile",CTX_GET:"e6:ctx-get",CTX_CFG:"e7:ctx-cfg",TEX:"e8:tex",BIND_S:"e9:bind-s",BIND_C:"e10:bind-c",UPDATE:"e11:update",NO_RENDERER:"e12:no-renderer",RESIZE:"e13:resize"};
|
|
2
|
-
export{a as
|
|
2
|
+
export{a as r,g as s};
|
package/dist/gpu-worker.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
E,
|
|
6
6
|
M
|
|
7
7
|
} from "./chunk-93yrr7er.js";
|
|
8
|
-
import"./chunk-
|
|
8
|
+
import"./chunk-5gtx3pza.js";
|
|
9
9
|
|
|
10
10
|
// src/gpu-worker.ts
|
|
11
11
|
var device;
|
|
@@ -337,7 +337,7 @@ function renderChart(chart) {
|
|
|
337
337
|
if (pass.perSeries) {
|
|
338
338
|
for (let si = 0;si < chart.series.length; si++) {
|
|
339
339
|
const series = chart.series[si];
|
|
340
|
-
if (series.pointCount === 0)
|
|
340
|
+
if (series.pointCount === 0 || series.hidden)
|
|
341
341
|
continue;
|
|
342
342
|
writeUniforms(chart, series);
|
|
343
343
|
const meta = chart.perSeriesPassMeta[si]?.[passIdx];
|
|
@@ -377,7 +377,7 @@ function renderChart(chart) {
|
|
|
377
377
|
if (pass.perSeries) {
|
|
378
378
|
for (let si = 0;si < chart.series.length; si++) {
|
|
379
379
|
const series = chart.series[si];
|
|
380
|
-
if (series.pointCount === 0)
|
|
380
|
+
if (series.pointCount === 0 || series.hidden)
|
|
381
381
|
continue;
|
|
382
382
|
const bg = series.passBindGroups[passIdx];
|
|
383
383
|
if (!bg)
|
|
@@ -570,6 +570,7 @@ function processUpdateSeries(id, seriesData, bounds, bufferSizes, perSeriesPassM
|
|
|
570
570
|
pointCount: sd.dataX.length,
|
|
571
571
|
visibleStart: 0,
|
|
572
572
|
visibleCount: sd.dataX.length,
|
|
573
|
+
hidden: sd.hidden ?? false,
|
|
573
574
|
passBindGroups: []
|
|
574
575
|
};
|
|
575
576
|
chart.series.push(series);
|
|
@@ -770,6 +771,10 @@ self.onmessage = async (e) => {
|
|
|
770
771
|
if (chart) {
|
|
771
772
|
if (data.bgColor !== undefined)
|
|
772
773
|
chart.bgColor = data.bgColor;
|
|
774
|
+
if (data.hiddenSeries !== undefined) {
|
|
775
|
+
for (let i = 0;i < chart.series.length; i++)
|
|
776
|
+
chart.series[i].hidden = data.hiddenSeries.has(i);
|
|
777
|
+
}
|
|
773
778
|
markDirty(chart);
|
|
774
779
|
}
|
|
775
780
|
break;
|
package/dist/gpu-worker.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{f as m}from"./chunk-dmaxrg6s.js";import{m as H,n as V}from"./chunk-bfyv7z27.js";import"./chunk-me3qaz3m.js";var W,k,X=new Map,P=new Map,R=!1,y,z,M,C=0,x=0,B=!1,u=null,E=new ArrayBuffer(64),f=new Float32Array(E),n=new Uint32Array(E);function v(j){let K=GPUBufferUsage.COPY_DST;for(let J of j)switch(J.toUpperCase()){case"STORAGE":K|=GPUBufferUsage.STORAGE;break;case"VERTEX":K|=GPUBufferUsage.VERTEX;break;case"UNIFORM":K|=GPUBufferUsage.UNIFORM;break;case"COPY_SRC":K|=GPUBufferUsage.COPY_SRC;break;case"COPY_DST":K|=GPUBufferUsage.COPY_DST;break;case"INDEX":K|=GPUBufferUsage.INDEX;break;case"INDIRECT":K|=GPUBufferUsage.INDIRECT;break}return K}function d(j,K,J){let q=J==="compute"?GPUShaderStage.COMPUTE:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT;if(j==="uniforms"||j==="custom-uniforms"||j==="series-index")return{visibility:q,buffer:{type:"uniform"}};if(j==="render-target"){if(K)return{visibility:GPUShaderStage.COMPUTE,storageTexture:{access:"write-only",format:"rgba8unorm"}};return{visibility:q,texture:{sampleType:"float"}}}if(K)return{visibility:q,buffer:{type:"storage"}};return{visibility:q,buffer:{type:"read-only-storage"}}}function S(j,K,J,q,N){switch(j){case"uniforms":return{buffer:J.uniformBuffer};case"custom-uniforms":return{buffer:J.customUniformBuffer};case"series-info":return{buffer:J.seriesStorageBuffer};case"render-target":return J.outputTextureView;case"x-data":return{buffer:q.dataX};case"y-data":return{buffer:q.dataY};case"series-index":return{buffer:q.seriesIndexBuffer}}if(j.endsWith("-data")){let Q=j.slice(0,-5);return{buffer:q.extraBuffers.get(Q)}}if(N.config.bufferDefs.find((Q)=>Q.name===j)?.perSeries)return{buffer:q.seriesBuffers.get(j)};return{buffer:J.chartBuffers.get(j)}}function i(j){let K=new Map,J=new Map;for(let q=0;q<j.passes.length;q++){let N=j.passes[q],O=N.bindings.map((G)=>({binding:G.binding,...d(G.source,G.write,N.type)})),Q=W.createBindGroupLayout({entries:O});J.set(`pass-${q}`,Q);let $=W.createPipelineLayout({bindGroupLayouts:[Q]}),Z=W.createShaderModule({code:j.shaders[N.shader]});if(N.type==="compute")K.set(`pass-${q}`,W.createComputePipeline({layout:$,compute:{module:Z,entryPoint:"main"}}));else K.set(`pass-${q}`,W.createRenderPipeline({layout:$,vertex:{module:Z,entryPoint:"vs"},fragment:{module:Z,entryPoint:"fs",targets:[{format:"rgba8unorm",blend:N.blend}]},primitive:{topology:N.topology??"triangle-list"}}))}return{config:j,pipelines:K,passLayouts:J}}function h(j){if(!j.seriesStorageBuffer||j.series.length===0)return;let K=new Float32Array(j.series.length*8),J=new Uint32Array(K.buffer);for(let q=0;q<j.series.length;q++){let N=j.series[q],O=q*8;K[O+0]=N.colorR,K[O+1]=N.colorG,K[O+2]=N.colorB,K[O+3]=1,J[O+4]=N.visibleStart,J[O+5]=N.visibleCount}W.queue.writeBuffer(j.seriesStorageBuffer,0,K)}function I(j,K){let J=f,q=n,N=j.maxX-j.minX,O=j.maxY-j.minY,Q=j.bgColor??(R?[0.11,0.11,0.12]:[0.98,0.98,0.98]);J[0]=j.width,J[1]=j.height,J[2]=j.minX+j.panX*N,J[3]=j.minX+j.panX*N+N/j.zoomX,J[4]=j.minY+j.panY*O,J[5]=j.minY+j.panY*O+O/j.zoomY,q[6]=K.pointCount,q[7]=j.series.length,q[8]=R?1:0,J[9]=Q[0],J[10]=Q[1],J[11]=Q[2],J[12]=j.minX,J[13]=j.maxX,J[14]=j.minY,J[15]=j.maxY,W.queue.writeBuffer(j.uniformBuffer,0,E)}function g(j,K){if(!j.customUniformBuffer||K.uniformDefs.length===0)return;let J=K.uniformDefs.length,q=Math.ceil(J*4/16)*16,N=new ArrayBuffer(q),O=new Float32Array(N),Q=new Uint32Array(N);for(let $=0;$<J;$++){let Z=K.uniformDefs[$],G=j.customUniformValues[Z.name]??Z.default;if(Z.type==="u32")Q[$]=G>>>0;else O[$]=G}W.queue.writeBuffer(j.customUniformBuffer,0,N)}function b(j,K,J){for(let[,q]of j.chartBuffers)q.destroy();j.chartBuffers.clear();for(let q of K.config.bufferDefs)if(!q.perSeries){let N=Math.max(16,J[q.name]??16);j.chartBuffers.set(q.name,W.createBuffer({size:N,usage:v(q.usages)}))}if(K.config.uniformDefs.length>0){if(j.customUniformBuffer)j.customUniformBuffer.destroy();let q=Math.max(16,Math.ceil(K.config.uniformDefs.length*4/16)*16);j.customUniformBuffer=W.createBuffer({size:q,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),g(j,K.config)}}function l(j,K,J,q){for(let[,N]of j.seriesBuffers)N.destroy();j.seriesBuffers.clear();for(let N of J.config.bufferDefs)if(N.perSeries){let O=Math.max(16,q[N.name]??16);j.seriesBuffers.set(N.name,W.createBuffer({size:O,usage:v(N.usages)}))}if(j.seriesIndexBuffer)j.seriesIndexBuffer.destroy();j.seriesIndexBuffer=W.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),W.queue.writeBuffer(j.seriesIndexBuffer,0,new Uint32Array([K,0,0,0]))}function p(j,K){if(!j.seriesStorageBuffer)return;for(let J=0;J<j.series.length;J++){let q=j.series[J];q.passBindGroups=[];for(let N=0;N<K.config.passes.length;N++){let O=K.config.passes[N];if(!O.perSeries){q.passBindGroups.push(null);continue}let Q=K.passLayouts.get(`pass-${N}`);try{let $=O.bindings.map((Z)=>({binding:Z.binding,resource:S(Z.source,Z.write,j,q,K)}));q.passBindGroups.push(W.createBindGroup({layout:Q,entries:$}))}catch($){postMessage({type:H.ERROR,code:V.BIND_S}),q.passBindGroups.push(null)}}}j.chartPassBindGroups=[];for(let J=0;J<K.config.passes.length;J++){let q=K.config.passes[J];if(q.perSeries){j.chartPassBindGroups.push(null);continue}let N=K.passLayouts.get(`pass-${J}`);try{let O=q.bindings.map((Q)=>({binding:Q.binding,resource:S(Q.source,Q.write,j,null,K)}));j.chartPassBindGroups.push(W.createBindGroup({layout:N,entries:O}))}catch(O){postMessage({type:H.ERROR,code:V.BIND_C}),j.chartPassBindGroups.push(null)}}}function D(j){if(j.outputTexture)j.outputTexture.destroy();let K=Math.max(1,j.width),J=Math.max(1,j.height),q=P.get(j.rendererName),N=GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT;if(q){for(let O of q.config.passes)if(O.type==="compute"){for(let Q of O.bindings)if(Q.source==="render-target"&&Q.write){N|=GPUTextureUsage.STORAGE_BINDING;break}}}j.outputTexture=W.createTexture({size:[K,J],format:"rgba8unorm",usage:N}),j.outputTextureView=j.outputTexture.createView(),j.blitBindGroup=W.createBindGroup({layout:z,entries:[{binding:0,resource:j.outputTextureView},{binding:1,resource:M}]})}function c(j){let K=P.get(j.rendererName);if(!K)return;if(!j.ctx||j.width===0||j.height===0||j.series.length===0)return;let J;try{J=j.ctx.getCurrentTexture().createView()}catch{return}let q=W.createCommandEncoder();if(h(j),j.series.length>0)I(j,j.series[0]);q.beginRenderPass({colorAttachments:[{view:j.outputTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]}).end();for(let Q=0;Q<K.config.passes.length;Q++){let $=K.config.passes[Q],Z=K.pipelines.get(`pass-${Q}`);if(!Z)continue;if($.type==="compute")if($.perSeries)for(let G=0;G<j.series.length;G++){let T=j.series[G];if(T.pointCount===0)continue;I(j,T);let _=j.perSeriesPassMeta[G]?.[Q]?.dispatch??{x:1},A=T.passBindGroups[Q];if(!A)continue;let F=q.beginComputePass();F.setPipeline(Z),F.setBindGroup(0,A),F.dispatchWorkgroups(_.x,_.y??1,_.z??1),F.end()}else{let G=j.chartPassBindGroups[Q];if(!G)continue;let Y=j.perSeriesPassMeta[0]?.[Q]?.dispatch??{x:1},_=q.beginComputePass();_.setPipeline(Z),_.setBindGroup(0,G),_.dispatchWorkgroups(Y.x,Y.y??1,Y.z??1),_.end()}else if($.type==="render"){let T=j.perSeriesPassMeta[0]?.[Q]?.draw??0,Y=q.beginRenderPass({colorAttachments:[{view:j.outputTextureView,loadOp:$.loadOp??"load",storeOp:"store"}]});if(Y.setPipeline(Z),$.perSeries)for(let _=0;_<j.series.length;_++){let A=j.series[_];if(A.pointCount===0)continue;let F=A.passBindGroups[Q];if(!F)continue;Y.setBindGroup(0,F),Y.draw(T,1,0,_)}else{let _=j.chartPassBindGroups[Q];if(_)Y.setBindGroup(0,_),Y.draw(T,1,0,0)}Y.end()}}let O=q.beginRenderPass({colorAttachments:[{view:J,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]});if(O.setPipeline(y),j.blitBindGroup)O.setBindGroup(0,j.blitBindGroup);O.draw(4),O.end(),W.queue.submit([q.finish()])}function U(){if(!B)B=!0,requestAnimationFrame(t)}function L(j){if(j.dirty=!0,j.visible)U()}function s(){let j=!1;for(let K of X.values())if(K.dirty=!0,K.visible)j=!0;if(j)U()}function t(){B=!1;let j=performance.now();for(let K of X.values())if(K.visible&&K.dirty&&K.width>0)c(K),K.dirty=!1;x=performance.now()-j,C++}function a(){let j=0;for(let K of X.values())if(K.visible&&K.width>0)j++;return j}async function e(){if(W)return!0;if(!navigator.gpu)return postMessage({type:H.ERROR,code:V.NO_GPU}),!1;let j=await navigator.gpu.requestAdapter();if(!j)return postMessage({type:H.ERROR,code:V.NO_ADAPTER}),!1;W=await j.requestDevice({requiredLimits:{maxBufferSize:j.limits.maxBufferSize,maxStorageBufferBindingSize:j.limits.maxStorageBufferBindingSize}}),k=navigator.gpu.getPreferredCanvasFormat(),W.lost.then((J)=>{postMessage({type:H.ERROR,code:V.DEVICE_LOST})}),z=W.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{}}]});let K=W.createShaderModule({code:m});return y=W.createRenderPipeline({layout:W.createPipelineLayout({bindGroupLayouts:[z]}),vertex:{module:K,entryPoint:"vs"},fragment:{module:K,entryPoint:"fs",targets:[{format:k}]},primitive:{topology:"triangle-strip"}}),M=W.createSampler({magFilter:"linear",minFilter:"linear"}),u=setInterval(()=>{postMessage({type:H.STATS,fps:C,renderMs:x,totalCharts:X.size,activeCharts:a()}),C=0},1000),postMessage({type:H.GPU_READY}),!0}function o(j){j.dataX.destroy(),j.dataY.destroy();for(let[,K]of j.extraBuffers)K.destroy();for(let[,K]of j.seriesBuffers)K.destroy();if(j.seriesIndexBuffer)j.seriesIndexBuffer.destroy()}function r(j,K,J,q,N){let O=X.get(j);if(!O||!W)return;let Q=P.get(O.rendererName);if(!Q){postMessage({type:H.ERROR,code:V.NO_RENDERER});return}try{O.minX=J.minX,O.maxX=J.maxX,O.minY=J.minY,O.maxY=J.maxY,O.perSeriesPassMeta=N;for(let $ of O.series)o($);if(O.series=[],O.seriesStorageBuffer)O.seriesStorageBuffer.destroy();if(K.length>0)O.seriesStorageBuffer=W.createBuffer({size:Math.max(32,K.length*32),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});b(O,Q,q);for(let $=0;$<K.length;$++){let Z=K[$],G=W.createBuffer({size:Math.max(16,Z.dataX.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),T=W.createBuffer({size:Math.max(16,Z.dataY.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});W.queue.writeBuffer(G,0,Z.dataX),W.queue.writeBuffer(T,0,Z.dataY);let Y=new Map;for(let[A,F]of Object.entries(Z.extra??{})){let w=W.createBuffer({size:Math.max(16,F.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});W.queue.writeBuffer(w,0,F),Y.set(A,w)}let _={label:Z.label,colorR:Z.colorR,colorG:Z.colorG,colorB:Z.colorB,dataX:G,dataY:T,extraBuffers:Y,seriesBuffers:new Map,seriesIndexBuffer:null,pointCount:Z.dataX.length,visibleStart:0,visibleCount:Z.dataX.length,passBindGroups:[]};O.series.push(_),l(_,$,Q,q)}p(O,Q)}catch($){postMessage({type:H.ERROR,code:V.UPDATE})}}self.onmessage=async(j)=>{let{type:K,...J}=j.data;switch(K){case H.INIT:R=J.isDark||!1,await e();break;case H.THEME:R=J.isDark,s();break;case H.REGISTER_RENDERER:{if(!W){postMessage({type:H.ERROR,code:V.NOT_READY});break}let q={name:J.name,shaders:J.shaders,passes:J.passes,bufferDefs:J.bufferDefs??[],uniformDefs:J.uniformDefs??[]};try{P.set(J.name,i(q))}catch(N){postMessage({type:H.ERROR,code:V.COMPILE})}break}case H.REGISTER_CHART:{if(!W)break;let q=J.canvas.getContext("webgpu");if(!q){postMessage({type:H.ERROR,code:V.CTX_GET});break}try{q.configure({device:W,format:k,alphaMode:"premultiplied"})}catch(Z){postMessage({type:H.ERROR,code:V.CTX_CFG});break}let N=W.limits.maxTextureDimension2D,O=Math.min(Math.max(1,Math.floor(Number(J.canvas.width)||800)),N),Q=Math.min(Math.max(1,Math.floor(Number(J.canvas.height)||400)),N),$={id:J.id,canvas:J.canvas,ctx:q,rendererName:J.rendererName,visible:!0,series:[],uniformBuffer:W.createBuffer({size:64,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,blitBindGroup:null,chartBuffers:new Map,customUniformBuffer:null,customUniformValues:J.customUniformValues??{},chartPassBindGroups:[],perSeriesPassMeta:J.perSeriesPassMeta??[],width:O,height:Q,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,maxY:1,minY:0,bgColor:J.bgColor??null,dirty:!0};try{D($)}catch(Z){postMessage({type:H.ERROR,code:V.TEX});break}X.set(J.id,$);break}case H.UNREGISTER_CHART:{let q=X.get(J.id);if(q){try{q.ctx.unconfigure()}catch{}if(q.uniformBuffer.destroy(),q.seriesStorageBuffer)q.seriesStorageBuffer.destroy();if(q.outputTexture)q.outputTexture.destroy();if(q.customUniformBuffer)q.customUniformBuffer.destroy();for(let[,N]of q.chartBuffers)N.destroy();for(let N of q.series)o(N);X.delete(J.id)}break}case H.UPDATE_SERIES:{r(J.id,J.series,J.bounds,J.bufferSizes??{},J.perSeriesPassMeta??[]);let q=X.get(J.id);if(q)L(q);break}case H.RESIZE:{let q=X.get(J.id);if(!q||J.width<=0||J.height<=0)break;let N=W.limits.maxTextureDimension2D,O=Math.min(J.width,N),Q=Math.min(J.height,N);if(O===q.width&&Q===q.height)break;if(q.width=O,q.height=Q,q.canvas.width=O,q.canvas.height=Q,J.perSeriesPassMeta?.length>0)q.perSeriesPassMeta=J.perSeriesPassMeta;let $=P.get(q.rendererName);try{if(D(q),$&&J.bufferSizes){b(q,$,J.bufferSizes);for(let Z=0;Z<q.series.length;Z++)l(q.series[Z],Z,$,J.bufferSizes);p(q,$)}}catch(Z){postMessage({type:H.ERROR,code:V.RESIZE})}L(q);break}case H.VIEW_TRANSFORM:{let q=X.get(J.id);if(q)q.panX=J.panX,q.panY=J.panY,q.zoomX=Math.max(0.1,Math.min(1e6,J.zoomX)),q.zoomY=Math.max(0.1,Math.min(1e6,J.zoomY)),L(q);break}case H.BATCH_VIEW_TRANSFORM:{let q=Math.max(0.1,Math.min(1e6,J.zoomX)),N=Math.max(0.1,Math.min(1e6,J.zoomY));for(let O of J.transforms){let Q=X.get(O.id);if(Q)Q.panX=J.panX,Q.panY=J.panY,Q.zoomX=q,Q.zoomY=N,Q.dirty=!0}U();break}case H.SET_VISIBILITY:{let q=X.get(J.id);if(q){if(q.visible=J.visible,J.visible&&q.dirty)U()}break}case H.SET_STYLE:{let q=X.get(J.id);if(q){if(J.bgColor!==void 0)q.bgColor=J.bgColor;L(q)}break}case H.SET_UNIFORMS:{let q=X.get(J.id);if(!q)break;Object.assign(q.customUniformValues,J.values);let N=P.get(q.rendererName);if(N)g(q,N.config);L(q);break}}};
|
|
1
|
+
import{i as m}from"./chunk-64q9a7nw.js";import{r as H,s as V}from"./chunk-wdfq2fpx.js";import"./chunk-bbyt23tw.js";var W,k,X=new Map,P=new Map,R=!1,y,z,M,C=0,x=0,B=!1,u=null,E=new ArrayBuffer(64),f=new Float32Array(E),n=new Uint32Array(E);function v(j){let K=GPUBufferUsage.COPY_DST;for(let J of j)switch(J.toUpperCase()){case"STORAGE":K|=GPUBufferUsage.STORAGE;break;case"VERTEX":K|=GPUBufferUsage.VERTEX;break;case"UNIFORM":K|=GPUBufferUsage.UNIFORM;break;case"COPY_SRC":K|=GPUBufferUsage.COPY_SRC;break;case"COPY_DST":K|=GPUBufferUsage.COPY_DST;break;case"INDEX":K|=GPUBufferUsage.INDEX;break;case"INDIRECT":K|=GPUBufferUsage.INDIRECT;break}return K}function d(j,K,J){let q=J==="compute"?GPUShaderStage.COMPUTE:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT;if(j==="uniforms"||j==="custom-uniforms"||j==="series-index")return{visibility:q,buffer:{type:"uniform"}};if(j==="render-target"){if(K)return{visibility:GPUShaderStage.COMPUTE,storageTexture:{access:"write-only",format:"rgba8unorm"}};return{visibility:q,texture:{sampleType:"float"}}}if(K)return{visibility:q,buffer:{type:"storage"}};return{visibility:q,buffer:{type:"read-only-storage"}}}function S(j,K,J,q,N){switch(j){case"uniforms":return{buffer:J.uniformBuffer};case"custom-uniforms":return{buffer:J.customUniformBuffer};case"series-info":return{buffer:J.seriesStorageBuffer};case"render-target":return J.outputTextureView;case"x-data":return{buffer:q.dataX};case"y-data":return{buffer:q.dataY};case"series-index":return{buffer:q.seriesIndexBuffer}}if(j.endsWith("-data")){let Q=j.slice(0,-5);return{buffer:q.extraBuffers.get(Q)}}if(N.config.bufferDefs.find((Q)=>Q.name===j)?.perSeries)return{buffer:q.seriesBuffers.get(j)};return{buffer:J.chartBuffers.get(j)}}function i(j){let K=new Map,J=new Map;for(let q=0;q<j.passes.length;q++){let N=j.passes[q],O=N.bindings.map((G)=>({binding:G.binding,...d(G.source,G.write,N.type)})),Q=W.createBindGroupLayout({entries:O});J.set(`pass-${q}`,Q);let $=W.createPipelineLayout({bindGroupLayouts:[Q]}),Z=W.createShaderModule({code:j.shaders[N.shader]});if(N.type==="compute")K.set(`pass-${q}`,W.createComputePipeline({layout:$,compute:{module:Z,entryPoint:"main"}}));else K.set(`pass-${q}`,W.createRenderPipeline({layout:$,vertex:{module:Z,entryPoint:"vs"},fragment:{module:Z,entryPoint:"fs",targets:[{format:"rgba8unorm",blend:N.blend}]},primitive:{topology:N.topology??"triangle-list"}}))}return{config:j,pipelines:K,passLayouts:J}}function h(j){if(!j.seriesStorageBuffer||j.series.length===0)return;let K=new Float32Array(j.series.length*8),J=new Uint32Array(K.buffer);for(let q=0;q<j.series.length;q++){let N=j.series[q],O=q*8;K[O+0]=N.colorR,K[O+1]=N.colorG,K[O+2]=N.colorB,K[O+3]=1,J[O+4]=N.visibleStart,J[O+5]=N.visibleCount}W.queue.writeBuffer(j.seriesStorageBuffer,0,K)}function I(j,K){let J=f,q=n,N=j.maxX-j.minX,O=j.maxY-j.minY,Q=j.bgColor??(R?[0.11,0.11,0.12]:[0.98,0.98,0.98]);J[0]=j.width,J[1]=j.height,J[2]=j.minX+j.panX*N,J[3]=j.minX+j.panX*N+N/j.zoomX,J[4]=j.minY+j.panY*O,J[5]=j.minY+j.panY*O+O/j.zoomY,q[6]=K.pointCount,q[7]=j.series.length,q[8]=R?1:0,J[9]=Q[0],J[10]=Q[1],J[11]=Q[2],J[12]=j.minX,J[13]=j.maxX,J[14]=j.minY,J[15]=j.maxY,W.queue.writeBuffer(j.uniformBuffer,0,E)}function g(j,K){if(!j.customUniformBuffer||K.uniformDefs.length===0)return;let J=K.uniformDefs.length,q=Math.ceil(J*4/16)*16,N=new ArrayBuffer(q),O=new Float32Array(N),Q=new Uint32Array(N);for(let $=0;$<J;$++){let Z=K.uniformDefs[$],G=j.customUniformValues[Z.name]??Z.default;if(Z.type==="u32")Q[$]=G>>>0;else O[$]=G}W.queue.writeBuffer(j.customUniformBuffer,0,N)}function b(j,K,J){for(let[,q]of j.chartBuffers)q.destroy();j.chartBuffers.clear();for(let q of K.config.bufferDefs)if(!q.perSeries){let N=Math.max(16,J[q.name]??16);j.chartBuffers.set(q.name,W.createBuffer({size:N,usage:v(q.usages)}))}if(K.config.uniformDefs.length>0){if(j.customUniformBuffer)j.customUniformBuffer.destroy();let q=Math.max(16,Math.ceil(K.config.uniformDefs.length*4/16)*16);j.customUniformBuffer=W.createBuffer({size:q,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),g(j,K.config)}}function l(j,K,J,q){for(let[,N]of j.seriesBuffers)N.destroy();j.seriesBuffers.clear();for(let N of J.config.bufferDefs)if(N.perSeries){let O=Math.max(16,q[N.name]??16);j.seriesBuffers.set(N.name,W.createBuffer({size:O,usage:v(N.usages)}))}if(j.seriesIndexBuffer)j.seriesIndexBuffer.destroy();j.seriesIndexBuffer=W.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),W.queue.writeBuffer(j.seriesIndexBuffer,0,new Uint32Array([K,0,0,0]))}function p(j,K){if(!j.seriesStorageBuffer)return;for(let J=0;J<j.series.length;J++){let q=j.series[J];q.passBindGroups=[];for(let N=0;N<K.config.passes.length;N++){let O=K.config.passes[N];if(!O.perSeries){q.passBindGroups.push(null);continue}let Q=K.passLayouts.get(`pass-${N}`);try{let $=O.bindings.map((Z)=>({binding:Z.binding,resource:S(Z.source,Z.write,j,q,K)}));q.passBindGroups.push(W.createBindGroup({layout:Q,entries:$}))}catch($){postMessage({type:H.ERROR,code:V.BIND_S}),q.passBindGroups.push(null)}}}j.chartPassBindGroups=[];for(let J=0;J<K.config.passes.length;J++){let q=K.config.passes[J];if(q.perSeries){j.chartPassBindGroups.push(null);continue}let N=K.passLayouts.get(`pass-${J}`);try{let O=q.bindings.map((Q)=>({binding:Q.binding,resource:S(Q.source,Q.write,j,null,K)}));j.chartPassBindGroups.push(W.createBindGroup({layout:N,entries:O}))}catch(O){postMessage({type:H.ERROR,code:V.BIND_C}),j.chartPassBindGroups.push(null)}}}function D(j){if(j.outputTexture)j.outputTexture.destroy();let K=Math.max(1,j.width),J=Math.max(1,j.height),q=P.get(j.rendererName),N=GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT;if(q){for(let O of q.config.passes)if(O.type==="compute"){for(let Q of O.bindings)if(Q.source==="render-target"&&Q.write){N|=GPUTextureUsage.STORAGE_BINDING;break}}}j.outputTexture=W.createTexture({size:[K,J],format:"rgba8unorm",usage:N}),j.outputTextureView=j.outputTexture.createView(),j.blitBindGroup=W.createBindGroup({layout:z,entries:[{binding:0,resource:j.outputTextureView},{binding:1,resource:M}]})}function c(j){let K=P.get(j.rendererName);if(!K)return;if(!j.ctx||j.width===0||j.height===0||j.series.length===0)return;let J;try{J=j.ctx.getCurrentTexture().createView()}catch{return}let q=W.createCommandEncoder();if(h(j),j.series.length>0)I(j,j.series[0]);q.beginRenderPass({colorAttachments:[{view:j.outputTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]}).end();for(let Q=0;Q<K.config.passes.length;Q++){let $=K.config.passes[Q],Z=K.pipelines.get(`pass-${Q}`);if(!Z)continue;if($.type==="compute")if($.perSeries)for(let G=0;G<j.series.length;G++){let T=j.series[G];if(T.pointCount===0||T.hidden)continue;I(j,T);let _=j.perSeriesPassMeta[G]?.[Q]?.dispatch??{x:1},A=T.passBindGroups[Q];if(!A)continue;let F=q.beginComputePass();F.setPipeline(Z),F.setBindGroup(0,A),F.dispatchWorkgroups(_.x,_.y??1,_.z??1),F.end()}else{let G=j.chartPassBindGroups[Q];if(!G)continue;let Y=j.perSeriesPassMeta[0]?.[Q]?.dispatch??{x:1},_=q.beginComputePass();_.setPipeline(Z),_.setBindGroup(0,G),_.dispatchWorkgroups(Y.x,Y.y??1,Y.z??1),_.end()}else if($.type==="render"){let T=j.perSeriesPassMeta[0]?.[Q]?.draw??0,Y=q.beginRenderPass({colorAttachments:[{view:j.outputTextureView,loadOp:$.loadOp??"load",storeOp:"store"}]});if(Y.setPipeline(Z),$.perSeries)for(let _=0;_<j.series.length;_++){let A=j.series[_];if(A.pointCount===0||A.hidden)continue;let F=A.passBindGroups[Q];if(!F)continue;Y.setBindGroup(0,F),Y.draw(T,1,0,_)}else{let _=j.chartPassBindGroups[Q];if(_)Y.setBindGroup(0,_),Y.draw(T,1,0,0)}Y.end()}}let O=q.beginRenderPass({colorAttachments:[{view:J,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]});if(O.setPipeline(y),j.blitBindGroup)O.setBindGroup(0,j.blitBindGroup);O.draw(4),O.end(),W.queue.submit([q.finish()])}function U(){if(!B)B=!0,requestAnimationFrame(t)}function L(j){if(j.dirty=!0,j.visible)U()}function s(){let j=!1;for(let K of X.values())if(K.dirty=!0,K.visible)j=!0;if(j)U()}function t(){B=!1;let j=performance.now();for(let K of X.values())if(K.visible&&K.dirty&&K.width>0)c(K),K.dirty=!1;x=performance.now()-j,C++}function a(){let j=0;for(let K of X.values())if(K.visible&&K.width>0)j++;return j}async function e(){if(W)return!0;if(!navigator.gpu)return postMessage({type:H.ERROR,code:V.NO_GPU}),!1;let j=await navigator.gpu.requestAdapter();if(!j)return postMessage({type:H.ERROR,code:V.NO_ADAPTER}),!1;W=await j.requestDevice({requiredLimits:{maxBufferSize:j.limits.maxBufferSize,maxStorageBufferBindingSize:j.limits.maxStorageBufferBindingSize}}),k=navigator.gpu.getPreferredCanvasFormat(),W.lost.then((J)=>{postMessage({type:H.ERROR,code:V.DEVICE_LOST})}),z=W.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{}}]});let K=W.createShaderModule({code:m});return y=W.createRenderPipeline({layout:W.createPipelineLayout({bindGroupLayouts:[z]}),vertex:{module:K,entryPoint:"vs"},fragment:{module:K,entryPoint:"fs",targets:[{format:k}]},primitive:{topology:"triangle-strip"}}),M=W.createSampler({magFilter:"linear",minFilter:"linear"}),u=setInterval(()=>{postMessage({type:H.STATS,fps:C,renderMs:x,totalCharts:X.size,activeCharts:a()}),C=0},1000),postMessage({type:H.GPU_READY}),!0}function o(j){j.dataX.destroy(),j.dataY.destroy();for(let[,K]of j.extraBuffers)K.destroy();for(let[,K]of j.seriesBuffers)K.destroy();if(j.seriesIndexBuffer)j.seriesIndexBuffer.destroy()}function r(j,K,J,q,N){let O=X.get(j);if(!O||!W)return;let Q=P.get(O.rendererName);if(!Q){postMessage({type:H.ERROR,code:V.NO_RENDERER});return}try{O.minX=J.minX,O.maxX=J.maxX,O.minY=J.minY,O.maxY=J.maxY,O.perSeriesPassMeta=N;for(let $ of O.series)o($);if(O.series=[],O.seriesStorageBuffer)O.seriesStorageBuffer.destroy();if(K.length>0)O.seriesStorageBuffer=W.createBuffer({size:Math.max(32,K.length*32),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});b(O,Q,q);for(let $=0;$<K.length;$++){let Z=K[$],G=W.createBuffer({size:Math.max(16,Z.dataX.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),T=W.createBuffer({size:Math.max(16,Z.dataY.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});W.queue.writeBuffer(G,0,Z.dataX),W.queue.writeBuffer(T,0,Z.dataY);let Y=new Map;for(let[A,F]of Object.entries(Z.extra??{})){let w=W.createBuffer({size:Math.max(16,F.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});W.queue.writeBuffer(w,0,F),Y.set(A,w)}let _={label:Z.label,colorR:Z.colorR,colorG:Z.colorG,colorB:Z.colorB,dataX:G,dataY:T,extraBuffers:Y,seriesBuffers:new Map,seriesIndexBuffer:null,pointCount:Z.dataX.length,visibleStart:0,visibleCount:Z.dataX.length,hidden:Z.hidden??!1,passBindGroups:[]};O.series.push(_),l(_,$,Q,q)}p(O,Q)}catch($){postMessage({type:H.ERROR,code:V.UPDATE})}}self.onmessage=async(j)=>{let{type:K,...J}=j.data;switch(K){case H.INIT:R=J.isDark||!1,await e();break;case H.THEME:R=J.isDark,s();break;case H.REGISTER_RENDERER:{if(!W){postMessage({type:H.ERROR,code:V.NOT_READY});break}let q={name:J.name,shaders:J.shaders,passes:J.passes,bufferDefs:J.bufferDefs??[],uniformDefs:J.uniformDefs??[]};try{P.set(J.name,i(q))}catch(N){postMessage({type:H.ERROR,code:V.COMPILE})}break}case H.REGISTER_CHART:{if(!W)break;let q=J.canvas.getContext("webgpu");if(!q){postMessage({type:H.ERROR,code:V.CTX_GET});break}try{q.configure({device:W,format:k,alphaMode:"premultiplied"})}catch(Z){postMessage({type:H.ERROR,code:V.CTX_CFG});break}let N=W.limits.maxTextureDimension2D,O=Math.min(Math.max(1,Math.floor(Number(J.canvas.width)||800)),N),Q=Math.min(Math.max(1,Math.floor(Number(J.canvas.height)||400)),N),$={id:J.id,canvas:J.canvas,ctx:q,rendererName:J.rendererName,visible:!0,series:[],uniformBuffer:W.createBuffer({size:64,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,blitBindGroup:null,chartBuffers:new Map,customUniformBuffer:null,customUniformValues:J.customUniformValues??{},chartPassBindGroups:[],perSeriesPassMeta:J.perSeriesPassMeta??[],width:O,height:Q,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,maxY:1,minY:0,bgColor:J.bgColor??null,dirty:!0};try{D($)}catch(Z){postMessage({type:H.ERROR,code:V.TEX});break}X.set(J.id,$);break}case H.UNREGISTER_CHART:{let q=X.get(J.id);if(q){try{q.ctx.unconfigure()}catch{}if(q.uniformBuffer.destroy(),q.seriesStorageBuffer)q.seriesStorageBuffer.destroy();if(q.outputTexture)q.outputTexture.destroy();if(q.customUniformBuffer)q.customUniformBuffer.destroy();for(let[,N]of q.chartBuffers)N.destroy();for(let N of q.series)o(N);X.delete(J.id)}break}case H.UPDATE_SERIES:{r(J.id,J.series,J.bounds,J.bufferSizes??{},J.perSeriesPassMeta??[]);let q=X.get(J.id);if(q)L(q);break}case H.RESIZE:{let q=X.get(J.id);if(!q||J.width<=0||J.height<=0)break;let N=W.limits.maxTextureDimension2D,O=Math.min(J.width,N),Q=Math.min(J.height,N);if(O===q.width&&Q===q.height)break;if(q.width=O,q.height=Q,q.canvas.width=O,q.canvas.height=Q,J.perSeriesPassMeta?.length>0)q.perSeriesPassMeta=J.perSeriesPassMeta;let $=P.get(q.rendererName);try{if(D(q),$&&J.bufferSizes){b(q,$,J.bufferSizes);for(let Z=0;Z<q.series.length;Z++)l(q.series[Z],Z,$,J.bufferSizes);p(q,$)}}catch(Z){postMessage({type:H.ERROR,code:V.RESIZE})}L(q);break}case H.VIEW_TRANSFORM:{let q=X.get(J.id);if(q)q.panX=J.panX,q.panY=J.panY,q.zoomX=Math.max(0.1,Math.min(1e6,J.zoomX)),q.zoomY=Math.max(0.1,Math.min(1e6,J.zoomY)),L(q);break}case H.BATCH_VIEW_TRANSFORM:{let q=Math.max(0.1,Math.min(1e6,J.zoomX)),N=Math.max(0.1,Math.min(1e6,J.zoomY));for(let O of J.transforms){let Q=X.get(O.id);if(Q)Q.panX=J.panX,Q.panY=J.panY,Q.zoomX=q,Q.zoomY=N,Q.dirty=!0}U();break}case H.SET_VISIBILITY:{let q=X.get(J.id);if(q){if(q.visible=J.visible,J.visible&&q.dirty)U()}break}case H.SET_STYLE:{let q=X.get(J.id);if(q){if(J.bgColor!==void 0)q.bgColor=J.bgColor;if(J.hiddenSeries!==void 0)for(let N=0;N<q.series.length;N++)q.series[N].hidden=J.hiddenSeries.has(N);L(q)}break}case H.SET_UNIFORMS:{let q=X.get(J.id);if(!q)break;Object.assign(q.customUniformValues,J.values);let N=P.get(q.rendererName);if(N)g(q,N.config);L(q);break}}};
|