@aeriz/chart 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ const TAU=Math.PI*2;const clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v;const lerp=(a,b,t)=>a+(b-a)*t;function extent(arr,fn=(x)=>x){let mn=Infinity,mx=-Infinity;for(let i=0;i<arr.length;i++){const v=fn(arr[i],i);if(v<mn)mn=v;if(v>mx)mx=v;}return[mn===Infinity?0:mn,mx===-Infinity?1:mx];}function niceTicks(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;const lo=Math.ceil(min/step)*step;const hi=Math.floor(max/step)*step;const ticks=[];for(let v=lo;v<=hi+step/2;v+=step){ticks.push(Math.round(v/step)*step);}return ticks;}function niceDomain(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;return[Math.floor(min/step)*step,Math.ceil(max/step)*step];}const T={sec:1e3,min:6e4,hour:36e5,day:864e5,week:6.048e8,month:2.628e9};function timeNiceTicks(minMs,maxMs,count=6){if(minMs===maxMs){minMs-=1000;maxMs+=1000;}const span=maxMs-minMs;const target=span/count;const units=[[T.sec,1],[T.sec,5],[T.sec,15],[T.sec,30],[T.min,1],[T.min,5],[T.min,15],[T.min,30],[T.hour,1],[T.hour,3],[T.hour,6],[T.hour,12],[T.day,1],[T.day,2],[T.week,1],[T.month,1],[T.month,3],[T.month,6],[T.month,12],];let step=T.sec;for(const[u,n]of units)if(u*n<=target*1.5)step=u*n;const lo=Math.ceil(minMs/step)*step;const ticks=[];for(let v=lo;v<=maxMs;v+=step)ticks.push(v);return ticks;}function timeFormat(step){if(step<T.min)return(t)=>new Date(t).toISOString().slice(11,19);if(step<T.hour)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.day)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.month)return(t)=>new Date(t).toISOString().slice(5,10);return(t)=>new Date(t).toISOString().slice(0,7);}function minMaxDecimate(data,targetCount,fn=(d)=>d.value){if(data.length<=targetCount)return data;const bucket=data.length/targetCount;const out=[];for(let i=0;i<targetCount;i++){const lo=Math.floor(i*bucket);const hi=Math.min(data.length,Math.floor((i+1)*bucket));let mnI=lo,mxI=lo;let mn=fn(data[lo],lo),mx=mn;for(let j=lo+1;j<hi;j++){const v=fn(data[j],j);if(v<mn){mn=v;mnI=j;}if(v>mx){mx=v;mxI=j;}}if(mnI<mxI){out.push(data[mnI]);out.push(data[mxI]);}else{out.push(data[mxI]);out.push(data[mnI]);}}return out;}function shouldDecimate(n,pxWidth){return n>pxWidth*2;}function bezierControlPoints(pts,tension=1/6){const n=pts.length;if(n<2)return[];const cps=[];for(let i=0;i<n-1;i++){const p0=pts[i-1]||pts[i];const p1=pts[i];const p2=pts[i+1];const p3=pts[i+2]||p2;cps.push({c1x:p1.x+(p2.x-p0.x)*tension,c1y:p1.y+(p2.y-p0.y)*tension,c2x:p2.x-(p3.x-p1.x)*tension,c2y:p2.y-(p3.y-p1.y)*tension,});}return cps;}function springStep(v,tgt,vel,cfg={}){const stiffness=cfg.stiffness||170;const damping=cfg.damping||26;const dt=1/60;const F=-stiffness*(v-tgt);const D=-damping*vel;const a=F+D;vel=vel+a*dt;v=v+vel*dt;const done=Math.abs(vel)<0.05&&Math.abs(v-tgt)<0.05;if(done){v=tgt;vel=0;}return{value:v,velocity:vel,done};}function colorParse(c){if(!c)return[0,0,0,1];if(c[0]==='#'){const h=c.slice(1);if(h.length===3)return[parseInt(h[0]+h[0],16),parseInt(h[1]+h[1],16),parseInt(h[2]+h[2],16),1];if(h.length===6)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),1];if(h.length===8)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),parseInt(h.slice(6,8),16)/255];}const m=c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:[,\s/]+([\d.]+))?\s*\)/);if(m)return[+m[1],+m[2],+m[3],m[4]!==undefined?+m[4]:1];return[0,0,0,1];}function colorAlpha(c,a){const[r,g,b]=colorParse(c);return`rgba(${r|0},${g|0},${b|0},${a})`;}function colorMix(a,b,t){const[r1,g1,b1,a1]=colorParse(a);const[r2,g2,b2,a2]=colorParse(b);return`rgba(${(r1+(r2-r1)*t)|0},${(g1+(g2-g1)*t)|0},${(b1+(b2-b1)*t)|0},${a1+(a2-a1)*t})`;}const PALETTE=['#64c8ff','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899','#84cc16','#6366f1',];const M={TAU,clamp,lerp,extent,niceTicks,niceDomain,timeNiceTicks,timeFormat,minMaxDecimate,shouldDecimate,bezierControlPoints,springStep,colorParse,colorAlpha,colorMix,PALETTE,};const math=M;class Engine{constructor(canvas){this.canvas=canvas;this.ctx=canvas.getContext('2d');this.w=0;this.h=0;this.dpr=1;this._gradCache=new Map();this._gradCacheMax=32;this._snap=true;}resize(w,h){const dpr=(typeof window!=='undefined'&&window.devicePixelRatio)||1;this.canvas.width=Math.round(w*dpr);this.canvas.height=Math.round(h*dpr);this.canvas.style.width=w+'px';this.canvas.style.height=h+'px';this.ctx.setTransform(dpr,0,0,dpr,0,0);this.w=w;this.h=h;this.dpr=dpr;this._gradCache.clear();}clear(color){if(color){this.ctx.fillStyle=color;this.ctx.fillRect(0,0,this.w,this.h);}else{this.ctx.clearRect(0,0,this.w,this.h);}}rect(x,y,w,h,opts={}){if(!(w>0)||!(h>0))return;const ctx=this.ctx;if(opts.radius&&opts.radius>0){const r=Math.max(0,Math.min(opts.radius,w/2,h/2));ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath();}else{ctx.beginPath();ctx.rect(x,y,w,h);}if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}rects(items,fill){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const r of items){if(!(r.w>0)||!(r.h>0))continue;if(r.radius&&r.radius>0){const rad=Math.max(0,Math.min(r.radius,r.w/2,r.h/2));ctx.moveTo(r.x+rad,r.y);ctx.arcTo(r.x+r.w,r.y,r.x+r.w,r.y+r.h,rad);ctx.arcTo(r.x+r.w,r.y+r.h,r.x,r.y+r.h,rad);ctx.arcTo(r.x,r.y+r.h,r.x,r.y,rad);ctx.arcTo(r.x,r.y,r.x+r.w,r.y,rad);ctx.closePath();}else{ctx.rect(r.x,r.y,r.w,r.h);}}ctx.fill();}circle(x,y,r,opts={}){if(!(r>0))return;const ctx=this.ctx;ctx.beginPath();ctx.arc(x,y,r,0,TAU);if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}circles(items,fill,fixedRadius){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const c of items){const r=fixedRadius!=null?fixedRadius:c.r;if(!(r>0))continue;ctx.moveTo(c.x+r,c.y);ctx.arc(c.x,c.y,r,0,TAU);}ctx.fill();}poly(points,opts={}){if(points.length<2)return;const ctx=this.ctx;const snap=this._snap&&opts.lineWidth===1;const adj=snap?0.5:0;ctx.beginPath();ctx.moveTo(points[0].x+adj,points[0].y+adj);for(let i=1;i<points.length;i++){ctx.lineTo(points[i].x+adj,points[i].y+adj);}if(opts.close)ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.lineCap=opts.cap||'butt';ctx.lineJoin=opts.join||'miter';if(opts.dash)ctx.setLineDash(opts.dash);ctx.stroke();if(opts.dash)ctx.setLineDash([]);}}smooth(points,cps,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||2;ctx.lineCap=opts.cap||'round';ctx.lineJoin=opts.join||'round';ctx.stroke();}}smoothArea(points,cps,y0,fill){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,y0);ctx.lineTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}ctx.lineTo(points[points.length-1].x,y0);ctx.closePath();ctx.fillStyle=fill;ctx.fill();}step(points,mode,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=1;i<points.length;i++){const a=points[i-1],b=points[i];if(mode==='after'){ctx.lineTo(b.x,a.y);ctx.lineTo(b.x,b.y);}else if(mode==='middle'){const mx=(a.x+b.x)/2;ctx.lineTo(mx,a.y);ctx.lineTo(mx,b.y);ctx.lineTo(b.x,b.y);}else{ctx.lineTo(a.x,b.y);ctx.lineTo(b.x,b.y);}}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.stroke();}}text(str,x,y,opts={}){const ctx=this.ctx;ctx.font=opts.font||'11px ui-monospace, monospace';ctx.fillStyle=opts.fill||'#000';ctx.textAlign=opts.align||'left';ctx.textBaseline=opts.baseline||'alphabetic';if(opts.rotate){ctx.save();ctx.translate(x,y);ctx.rotate(opts.rotate);ctx.fillText(str,0,0);ctx.restore();}else{ctx.fillText(str,x,y);}}measure(str,font){this.ctx.font=font||'11px ui-monospace, monospace';return this.ctx.measureText(str).width;}arc(cx,cy,rIn,rOut,a0,a1,opts={}){const ri=Math.max(0,rIn);const ro=Math.max(0,rOut);if(ro<=0)return;const ctx=this.ctx;ctx.beginPath();ctx.arc(cx,cy,ro,a0,a1);ctx.arc(cx,cy,ri,a1,a0,true);ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}vGradient(key,x0,y0,y1,stops){if(this._gradCache.has(key))return this._gradCache.get(key);const g=this.ctx.createLinearGradient(x0,y0,x0,y1);for(const[pos,color]of stops)g.addColorStop(pos,color);if(this._gradCache.size>=this._gradCacheMax){const first=this._gradCache.keys().next().value;this._gradCache.delete(first);}this._gradCache.set(key,g);return g;}clip(x,y,w,h){const ctx=this.ctx;ctx.save();ctx.beginPath();ctx.rect(x,y,w,h);ctx.clip();}unclip(){this.ctx.restore();}withAlpha(a,fn){const ctx=this.ctx;const prev=ctx.globalAlpha;ctx.globalAlpha=prev*a;fn();ctx.globalAlpha=prev;}}const charts=new Set();let rafId=null;let lastTime=0;let fps=60;let frames=0;let fpsT=0;function tick(now){if(now-fpsT>500){fps=Math.round((1000*frames)/(now-fpsT));frames=0;fpsT=now;}frames++;let active=false;for(const c of charts){try{const stillActive=c._tick(now);if(stillActive)active=true;}catch(e){console.error(e);}}rafId=(charts.size>0&&active)?requestAnimationFrame(tick):null;}function schedule(chart){charts.add(chart);if(rafId===null){fpsT=lastTime=performance.now();rafId=requestAnimationFrame(tick);}}const scheduler={schedule,fps:()=>fps,count:()=>charts.size};function getFPS(){return fps;}function getActiveCount(){return charts.size;}const TOKENS={bg:['--aeriz-bg','#ffffff'],surface:['--aeriz-surface','transparent'],text:['--aeriz-text','#222'],muted:['--aeriz-muted','#888'],grid:['--aeriz-grid','rgba(0,0,0,0.08)'],axis:['--aeriz-axis','rgba(0,0,0,0.3)'],accent:['--aeriz-accent','#64c8ff'],font:['--aeriz-font','ui-monospace, SFMono-Regular, Menlo, monospace'],fontSize:['--aeriz-font-size','11','number'],lineWidth:['--aeriz-line-width','2','number'],pointRadius:['--aeriz-point-radius','3','number'],barRadius:['--aeriz-bar-radius','3','number'],tooltipBg:['--aeriz-tooltip-bg','rgba(20,20,28,0.95)'],tooltipFg:['--aeriz-tooltip-fg','#fff'],};function resolveTheme(el){const cs=getComputedStyle(el);const out={};for(const[k,[varName,fallback,kind]]of Object.entries(TOKENS)){const raw=(cs.getPropertyValue(varName)||'').trim();const v=raw||fallback;out[k]=kind==='number'?parseFloat(v):v;}return out;}function readPalette(el){const cs=getComputedStyle(el);const out=[];for(let i=1;i<=12;i++){const v=(cs.getPropertyValue(`--aeriz-color-${i}`)||'').trim();if(v)out.push(v);}return out.length?out:M.PALETTE;}class Tooltip{constructor(parent){this.parent=parent;this.el=document.createElement('div');Object.assign(this.el.style,{position:'absolute',pointerEvents:'none',padding:'6px 10px',borderRadius:'4px',fontSize:'11px',fontFamily:'inherit',lineHeight:'1.5',whiteSpace:'nowrap',opacity:'0',transform:'translate(-50%, calc(-100% - 8px))',transition:'opacity 0.1s',zIndex:'20',boxShadow:'0 4px 16px rgba(0,0,0,0.3)',});parent.appendChild(this.el);}show(x,y,content,theme){this.el.style.background=theme.tooltipBg;this.el.style.color=theme.tooltipFg;if(typeof content==='string'){this.el.textContent=content;}else{this.el.innerHTML='';if(content.title){const t=document.createElement('div');t.style.cssText='opacity:0.7;font-size:10px;margin-bottom:4px';t.textContent=content.title;this.el.appendChild(t);}for(const r of content.rows){const row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:6px;line-height:1.4';if(r.color){const sw=document.createElement('span');sw.style.cssText=`width:8px;height:8px;border-radius:2px;background:${r.color};display:inline-block`;row.appendChild(sw);}const nm=document.createElement('span');nm.style.cssText='opacity:0.8;flex:1';nm.textContent=r.name||'';row.appendChild(nm);const val=document.createElement('span');val.style.cssText='font-weight:700;margin-left:12px';val.textContent=r.value==null?'—':String(r.value);row.appendChild(val);this.el.appendChild(row);}}this.el.style.left=x+'px';this.el.style.top=y+'px';this.el.style.opacity='1';}hide(){this.el.style.opacity='0';}destroy(){this.el.remove();}}class Overlay{constructor(parent){this.parent=parent;this.el=document.createElement('div');Object.assign(this.el.style,{position:'absolute',inset:'0',display:'none',alignItems:'center',justifyContent:'center',flexDirection:'column',gap:'8px',fontSize:'12px',fontFamily:'inherit',pointerEvents:'none',zIndex:'15',});parent.appendChild(this.el);}show(kind,msg,theme){const el=this.el;el.style.display='flex';el.style.background=M.colorAlpha(theme.bg,0.85);el.style.color=theme.text;el.innerHTML='';const icon=document.createElement('div');icon.style.fontSize='20px';icon.style.opacity='0.6';if(kind==='loading'){icon.innerHTML=`<svg width="22" height="22" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="${theme.accent}" stroke-width="2.5" stroke-linecap="round" stroke-dasharray="14 50"><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/></circle></svg>`;}else if(kind==='empty'){icon.textContent='◌';}else if(kind==='error'){icon.textContent='!';icon.style.color='#ef4444';}el.appendChild(icon);if(msg){const t=document.createElement('div');t.style.opacity='0.85';t.textContent=msg;el.appendChild(t);}}hide(){this.el.style.display='none';}destroy(){this.el.remove();}}class HitIndex{constructor(){this.regions=[];}clear(){this.regions.length=0;}add(r){this.regions.push(r);}find(x,y){for(let i=this.regions.length-1;i>=0;i--){const r=this.regions[i];if(x>=r.x&&x<=r.x+r.w&&y>=r.y&&y<=r.y+r.h)return r;}return null;}}class Chart{constructor(container,options={}){if(!container)throw new Error('Chart: container is required');this.container=typeof container==='string'?document.querySelector(container):container;this.options=options;this.container.classList.add('aeriz');if(getComputedStyle(this.container).position==='static'){this.container.style.position='relative';}if(!this.container.style.height&&this.container.offsetHeight===0){this.container.style.height='320px';}this.canvas=document.createElement('canvas');this.canvas.style.cssText='display:block;width:100%;height:100%;touch-action:none';this.container.appendChild(this.canvas);this.engine=new Engine(this.canvas);this.tooltip=new Tooltip(this.container);this.overlay=new Overlay(this.container);this.hits=new HitIndex();this.theme=resolveTheme(this.container);this.palette=options.palette||readPalette(this.container);this._cursor=null;this._hover=null;this._dirty=true;this._destroyed=false;this._mounted=false;this._anim={progress:0,start:0,duration:600};this._state=null;this._setupEvents();this._observeResize();this._resize();schedule(this);}invalidate(){this.theme=resolveTheme(this.container);this.palette=this.options.palette||readPalette(this.container);this.engine._gradCache.clear();this._dirty=true;schedule(this);}setData(data){this.options.data=data;if(this._state==='empty')this._state=null;this._anim.start=performance.now();this._anim.progress=0;this._dirty=true;schedule(this);return this;}setLoading(msg){this._state='loading';this._stateMsg=msg||'';this._dirty=true;schedule(this);return this;}setEmpty(msg){this._state='empty';this._stateMsg=msg||'No data';this._dirty=true;schedule(this);return this;}setError(msg){this._state='error';this._stateMsg=msg||'Error';this._dirty=true;schedule(this);return this;}clearState(){this._state=null;this._dirty=true;schedule(this);return this;}destroy(){this._destroyed=true;charts.delete(this);this._ro&&this._ro.disconnect();this._removeEvents();this.tooltip.destroy();this.overlay.destroy();this.canvas.remove();this.container.classList.remove('aeriz');}_resize(){const w=this.container.clientWidth;const h=this.container.clientHeight;if(w===this.engine.w&&h===this.engine.h)return;this.engine.resize(Math.max(50,w),Math.max(50,h));this.theme=resolveTheme(this.container);this._dirty=true;}_observeResize(){if(typeof ResizeObserver==='undefined')return;this._ro=new ResizeObserver(()=>{this._resize();schedule(this);});this._ro.observe(this.container);}_setupEvents(){const c=this.canvas;this._handlers={move:(e)=>this._onMove(e),leave:()=>this._onLeave(),click:(e)=>this._onClick(e),};c.addEventListener('mousemove',this._handlers.move);c.addEventListener('mouseleave',this._handlers.leave);c.addEventListener('click',this._handlers.click);}_removeEvents(){const c=this.canvas;c.removeEventListener('mousemove',this._handlers.move);c.removeEventListener('mouseleave',this._handlers.leave);c.removeEventListener('click',this._handlers.click);}_ptr(e){const r=this.canvas.getBoundingClientRect();return{x:e.clientX-r.left,y:e.clientY-r.top};}_onMove(e){const p=this._ptr(e);this._cursor=p;const hit=this.hits.find(p.x,p.y);const changed=(hit?._id??null)!==(this._hover?._id??null);this._hover=hit;if(changed){if(hit&&hit.tooltip){this.tooltip.show(hit.tipX!=null?hit.tipX:hit.x+hit.w/2,hit.tipY!=null?hit.tipY:hit.y,hit.tooltip,this.theme,);}else if(!hit&&!this._sharedTooltip){this.tooltip.hide();}this._dirty=true;schedule(this);}if(typeof this._onMoveX==='function')this._onMoveX(p);}_onLeave(){this._cursor=null;this._hover=null;this.tooltip.hide();this._dirty=true;schedule(this);}_onClick(e){const p=this._ptr(e);const hit=this.hits.find(p.x,p.y);if(this.options.onClick)this.options.onClick(hit,p,this);}_tick(now){if(this._destroyed)return false;let active=false;if(this._anim.progress<1){if(!this._anim.start)this._anim.start=now;this._anim.progress=Math.min(1,(now-this._anim.start)/this._anim.duration);this._dirty=true;if(this._anim.progress<1)active=true;}if(this._dirty){this._draw();this._dirty=false;}return active;}_draw(){const e=this.engine;e.clear();if(this.theme.surface&&this.theme.surface!=='transparent'){e.rect(0,0,e.w,e.h,{fill:this.theme.surface});}if(this._state){this.overlay.show(this._state,this._stateMsg,this.theme);this.hits.clear();return;}else{this.overlay.hide();}this.hits.clear();if(typeof this._render==='function')this._render();}_axisPad(opts={}){const t=this.theme;return{l:opts.l!=null?opts.l:50,r:opts.r!=null?opts.r:16,t:opts.t!=null?opts.t:16,b:opts.b!=null?opts.b:32,};}_drawCartesian(spec){const e=this.engine;const t=this.theme;const pad=this._axisPad(spec.pad);const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const yMin=spec.yMin!=null?spec.yMin:0;const yMax=spec.yMax;const yScale=spec.yScale||'linear';const font=`${t.fontSize}px ${t.font}`;let yTicks,yToPx;if(yScale==='log'){const lo=Math.max(1e-10,spec.yMin||1);const hi=yMax;const lLo=Math.log10(lo),lHi=Math.log10(hi);yToPx=v=>py+ph*(1-(Math.log10(Math.max(lo,v))-lLo)/(lHi-lLo));yTicks=[];for(let p=Math.floor(lLo);p<=Math.ceil(lHi);p++){const v=Math.pow(10,p);if(v>=lo&&v<=hi)yTicks.push(v);}}else{yTicks=M.niceTicks(yMin,yMax,5);yToPx=v=>py+ph*(1-(v-yMin)/(yMax-yMin));}e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of yTicks){const y=Math.round(yToPx(v))+0.5;e.ctx.moveTo(px,y);e.ctx.lineTo(px+pw,y);}e.ctx.stroke();const yFmt=spec.yTickFormat||(v=>yScale==='log'&&v>=1000?v.toExponential(0):String(Math.round(v*100)/100));for(const v of yTicks){e.text(yFmt(v),px-6,yToPx(v),{fill:t.muted,font,align:'right',baseline:'middle',});}let xToPx,xTicks,xFmt;if(spec.xTime){const{min,max}=spec.xTime;xToPx=v=>px+pw*(v-min)/(max-min||1);xTicks=M.timeNiceTicks(min,max,6);const step=xTicks.length>1?xTicks[1]-xTicks[0]:(max-min);xFmt=spec.xTickFormat||M.timeFormat(step);}else if(spec.xLinear){const{min,max}=spec.xLinear;xToPx=v=>px+pw*(v-min)/(max-min||1);xTicks=M.niceTicks(min,max,6);xFmt=spec.xTickFormat||(v=>String(v));}else if(spec.xLabels){const n=spec.xLabels.length;xToPx=i=>px+(i+0.5)*(pw/n);xTicks=spec.xLabels.map((_,i)=>i);xFmt=spec.xTickFormat||(i=>String(spec.xLabels[i]));}if(xToPx){let lastEnd=-Infinity;for(const v of xTicks){const x=xToPx(v);const txt=xFmt(v);const w=e.measure(txt,font);if(x-w/2<lastEnd+4)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1,});return{px,py,pw,ph,yToPx,xToPx,yTicks,xTicks,yMin,yMax};}_drawLegend(items,pw){const e=this.engine;const t=this.theme;const font=`${t.fontSize}px ${t.font}`;const swatch=10,gap=16,rowH=18;let x=0,y=0,maxW=pw;for(const it of items){const tw=e.measure(it.label,font);const w=swatch+6+tw;if(x+w>maxW&&x>0){x=0;y+=rowH;}e.rect(x,y+4,swatch,swatch,{fill:it.hidden?t.muted:it.color,radius:2});e.text(it.label,x+swatch+4,y+swatch/2+4,{fill:it.hidden?t.muted:t.text,font,align:'left',baseline:'middle',});this.hits.add({_id:'legend-'+it.idx,x,y,w:swatch+4+tw,h:rowH,legendIdx:it.idx,});x+=w+gap;}return y+rowH+6;}_colorOf(idx,item){if(item&&item.color)return item.color;return this.palette[idx%this.palette.length];}}class BarChart extends Chart{_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const yMin=Math.min(0,...values);const yMax=Math.max(...values);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(labelOf),yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,});const slot=ax.pw/data.length;const barW=Math.max(2,Math.min(60,slot-8));const prog=this._anim.progress;const radius=t.barRadius;const groups=new Map();const fmt=this.options.dataLabels;for(let i=0;i<data.length;i++){const v=values[i];const x=ax.px+(i+0.5)*slot-barW/2;const y0=ax.yToPx(0);const y1=ax.yToPx(v);const top=Math.min(y0,y1);const h=Math.abs(y1-y0)*prog;const animTop=v>=0?y0-h:y0;const color=this._colorOf(i,data[i]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y:animTop,w:barW,h,radius});this.hits.add({_id:i,x,y:Math.min(animTop,animTop+h),w:barW,h:Math.abs(h),tipX:x+barW/2,tipY:animTop,tooltip:`${labelOf(data[i], i)}: ${v}`,});if(fmt&&prog>0.95){const txt=typeof fmt==='function'?fmt(data[i],i):String(v);e.text(txt,x+barW/2,animTop-4,{fill:t.text,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'bottom',});}}for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}class HBarChart extends Chart{_axisPad(){return{l:80,r:16,t:16,b:32};}_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const xMin=Math.min(0,...values);const xMax=Math.max(...values);const[xLo,xHi]=M.niceDomain(xMin,xMax,5);const pad=this._axisPad();const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;const xTicks=M.niceTicks(xLo,xHi,5);const xToPx=v=>px+pw*(v-xLo)/(xHi-xLo);e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of xTicks){const x=Math.round(xToPx(v))+0.5;e.ctx.moveTo(x,py);e.ctx.lineTo(x,py+ph);}e.ctx.stroke();for(const v of xTicks){e.text(String(v),xToPx(v),py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});const slot=ph/data.length;const barH=Math.max(2,Math.min(40,slot-6));const prog=this._anim.progress;const groups=new Map();for(let i=0;i<data.length;i++){const v=values[i];const yMid=py+(i+0.5)*slot;const y=yMid-barH/2;const x0=xToPx(0);const x1=xToPx(v);const start=Math.min(x0,x1);const w=Math.abs(x1-x0)*prog;const animLeft=v>=0?x0:x0-w;const color=this._colorOf(i,data[i]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x:animLeft,y,w,h:barH,radius:t.barRadius});this.hits.add({_id:i,x:animLeft,y,w,h:barH,tipX:animLeft+w/2,tipY:y,tooltip:`${labelOf(data[i], i)}: ${v}`,});e.text(labelOf(data[i],i),px-6,yMid,{fill:t.muted,font,align:'right',baseline:'middle',});}for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}class LineChart extends Chart{_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const yMin=Math.min(0,...values);const yMax=Math.max(...values);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const useTime=this.options.xScale==='time';let spec={yMin:yLo,yMax:yHi,yScale:this.options.yScale,yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,};if(useTime){const xs=data.map((d,i)=>+labelOf(d,i));spec.xTime={min:xs[0],max:xs[xs.length-1]};}else{spec.xLabels=data.map(labelOf);}const ax=this._drawCartesian(spec);const color=this.options.color||this.palette[0];const interp=this.options.interpolation||'smooth';const lineW=t.lineWidth;const prog=this._anim.progress;let renderData=data;if(M.shouldDecimate(data.length,ax.pw)){renderData=M.minMaxDecimate(data,Math.floor(ax.pw),valueOf);}const points=renderData.map((d,i)=>{const xv=useTime?+labelOf(d,i):i;const idx=useTime?null:i;const x=useTime?ax.xToPx(xv):ax.px+((idx+0.5)/renderData.length)*ax.pw;const y=ax.yToPx(valueOf(d,i));return{x,y,d,i};});e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);if(interp==='smooth'||interp==='linear'){const cps=interp==='smooth'?M.bezierControlPoints(points):null;const grad=e.vGradient(`area-${color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(color,0.25)],[1,M.colorAlpha(color,0)],]);if(cps)e.smoothArea(points,cps,ax.py+ax.ph,grad);else{const areaPts=points.concat([{x:points[points.length-1].x,y:ax.py+ax.ph},{x:points[0].x,y:ax.py+ax.ph},]);e.poly(areaPts,{fill:grad});}if(cps)e.smooth(points,cps,{stroke:color,lineWidth:lineW});else e.poly(points,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else if(interp==='step'||interp.startsWith('step-')){const mode=interp==='step'?'before':interp.split('-')[1];e.step(points,mode,{stroke:color,lineWidth:lineW});}e.unclip();if(renderData===data&&data.length<=50){const pr=t.pointRadius;e.circles(points,color,pr);for(const p of points){this.hits.add({_id:p.i,x:p.x-8,y:p.y-8,w:16,h:16,tipX:p.x,tipY:p.y-pr,tooltip:`${labelOf(p.d, p.i)}: ${valueOf(p.d, p.i)}`,});}}else{const colW=ax.pw/renderData.length;for(let i=0;i<renderData.length;i++){const p=points[i];this.hits.add({_id:p.i,x:p.x-colW/2,y:ax.py,w:colW,h:ax.ph,tipX:p.x,tipY:p.y-8,tooltip:`${labelOf(p.d, p.i)}: ${valueOf(p.d, p.i)}`,});}}if(this._hover){const p=points.find(pp=>pp.i===this._hover._id);if(p)e.circle(p.x,p.y,t.pointRadius+3,{stroke:color,lineWidth:2,fill:t.bg,});}}}class AreaChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const stacked=this.options.stacked===true;let yMax;if(stacked){yMax=0;for(let i=0;i<cats.length;i++){let sum=0;for(const s of series)sum+=+s.values[i]||0;if(sum>yMax)yMax=sum;}}else{yMax=-Infinity;for(const s of series)for(const v of s.values)if(+v>yMax)yMax=+v;}const[yLo,yHi]=M.niceDomain(0,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const lineW=t.lineWidth;const prog=this._anim.progress;e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);if(stacked){const cum=new Array(cats.length).fill(0);const seriesPts=[];for(let s=0;s<series.length;s++){const ser=series[s];const ptsTop=[],ptsBot=[];for(let i=0;i<cats.length;i++){const v=+ser.values[i]||0;const x=ax.px+((i+0.5)/cats.length)*ax.pw;const yBot=ax.yToPx(cum[i]);const yTop=ax.yToPx(cum[i]+v);ptsTop.push({x,y:yTop});ptsBot.push({x,y:yBot});cum[i]+=v;}const color=this._colorOf(s,ser);const cps=M.bezierControlPoints(ptsTop);const ctx=e.ctx;ctx.beginPath();ctx.moveTo(ptsTop[0].x,ptsTop[0].y);for(let i=0;i<cps.length;i++){ctx.bezierCurveTo(cps[i].c1x,cps[i].c1y,cps[i].c2x,cps[i].c2y,ptsTop[i+1].x,ptsTop[i+1].y);}for(let i=ptsBot.length-1;i>=0;i--)ctx.lineTo(ptsBot[i].x,ptsBot[i].y);ctx.closePath();ctx.fillStyle=M.colorAlpha(color,0.7);ctx.fill();ctx.strokeStyle=color;ctx.lineWidth=lineW;ctx.stroke();seriesPts.push({ser,color,ptsTop});}}else{for(let s=0;s<series.length;s++){const ser=series[s];const pts=[];for(let i=0;i<cats.length;i++){pts.push({x:ax.px+((i+0.5)/cats.length)*ax.pw,y:ax.yToPx(+ser.values[i]||0),});}const color=this._colorOf(s,ser);const cps=M.bezierControlPoints(pts);const grad=e.vGradient(`area-${s}-${color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(color,0.4)],[1,M.colorAlpha(color,0)],]);e.smoothArea(pts,cps,ax.py+ax.ph,grad);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});}}e.unclip();this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class StackedBarChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const totals=cats.map((_,i)=>series.reduce((a,s)=>a+(+s.values[i]||0),0));const[yLo,yHi]=M.niceDomain(0,Math.max(...totals),5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const slot=ax.pw/cats.length;const barW=Math.max(2,Math.min(50,slot-10));const prog=this._anim.progress;const groups=new Map();for(let i=0;i<cats.length;i++){let cum=0;const x=ax.px+(i+0.5)*slot-barW/2;for(let s=0;s<series.length;s++){const v=+series[s].values[i]||0;if(v<=0)continue;const segH=((v/yHi)*ax.ph)*prog;const y=ax.yToPx(cum+v);cum+=v;const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);const isTop=s===series.length-1;groups.get(color).push({x,y,w:barW,h:segH,radius:isTop?t.barRadius:0,});this.hits.add({_id:`${i}-${s}`,x,y,w:barW,h:segH,tipX:x+barW/2,tipY:y,tooltip:{title:cats[i],rows:[{color,name:series[s].name||'Series '+(s+1),value:v,}],},});}}for(const[color,items]of groups)e.rects(items,color);this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class GroupedBarChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let yMax=-Infinity;for(const s of series)for(const v of s.values)if(+v>yMax)yMax=+v;const[yLo,yHi]=M.niceDomain(0,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const slot=ax.pw/cats.length;const inner=slot*0.8;const barW=Math.max(2,inner/series.length);const prog=this._anim.progress;const groups=new Map();for(let i=0;i<cats.length;i++){const xCenter=ax.px+(i+0.5)*slot;const left=xCenter-inner/2;for(let s=0;s<series.length;s++){const v=+series[s].values[i]||0;const x=left+s*barW;const h=((v/yHi)*ax.ph)*prog;const y=ax.yToPx(v);const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y,w:barW-1,h,radius:t.barRadius});this.hits.add({_id:`${i}-${s}`,x,y,w:barW-1,h,tipX:x+barW/2,tipY:y,tooltip:`${cats[i]} · ${series[s].name || s}: ${v}`,});}}for(const[color,items]of groups)e.rects(items,color);this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class MultiLineChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const interp=this.options.interpolation||'smooth';let yMax=-Infinity,yMin=Infinity;for(const s of series)for(const v of s.values){if(+v>yMax)yMax=+v;if(+v<yMin)yMin=+v;}const[yLo,yHi]=M.niceDomain(Math.min(0,yMin),yMax,5);const useTime=this.options.xScale==='time';let spec={yMin:yLo,yMax:yHi,yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,};if(useTime){spec.xTime={min:+cats[0],max:+cats[cats.length-1]};}else{spec.xLabels=cats;}const ax=this._drawCartesian(spec);const lineW=t.lineWidth;const prog=this._anim.progress;e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);const allPts=[];for(let s=0;s<series.length;s++){const ser=series[s];if(ser.hidden)continue;const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:useTime?ax.xToPx(+cats[i]):ax.px+((i+0.5)/cats.length)*ax.pw,y:ax.yToPx(+v),i,s,v,}));allPts.push({pts,color,s,ser});if(interp==='smooth'){const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});}else if(interp==='linear'){e.poly(pts,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else{const mode=interp==='step'?'before':interp.split('-')[1];e.step(pts,mode,{stroke:color,lineWidth:lineW});}if(ser.values.length<=50)e.circles(pts,color,t.pointRadius);}e.unclip();if(this._cursor&&allPts.length){const cx=this._cursor.x;if(cx>=ax.px&&cx<=ax.px+ax.pw){const i=useTime?Math.round(((cx-ax.px)/ax.pw)*(cats.length-1)):Math.floor((cx-ax.px)/(ax.pw/cats.length));const idx=Math.max(0,Math.min(cats.length-1,i));const xPx=useTime?ax.xToPx(+cats[idx]):ax.px+(idx+0.5)*(ax.pw/cats.length);e.poly([{x:xPx,y:ax.py},{x:xPx,y:ax.py+ax.ph}],{stroke:t.muted,lineWidth:1,dash:[3,3],});const rows=[];for(const sp of allPts){const p=sp.pts[idx];if(!p)continue;e.circle(p.x,p.y,t.pointRadius+2,{fill:t.bg,stroke:sp.color,lineWidth:2,});rows.push({color:sp.color,name:sp.ser.name||'Series '+(sp.s+1),value:sp.ser.values[idx],});}const title=useTime?new Date(+cats[idx]).toISOString().slice(0,19).replace('T',' '):String(cats[idx]);this.tooltip.show(xPx,ax.py+8,{title,rows},t);this._sharedTooltip=true;}}this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:!!s.hidden,})),ax.pw);}}class ScatterChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;for(const d of data){if(d.x<xMin)xMin=d.x;if(d.x>xMax)xMax=d.x;if(d.y<yMin)yMin=d.y;if(d.y>yMax)yMax=d.y;}const[xLo,xHi]=M.niceDomain(xMin,xMax,6);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});const prog=this._anim.progress;const groups=new Map();const defaultR=t.pointRadius+1;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(d.r||defaultR)*prog;const g=d.group!=null?d.group:0;const color=this._colorOf(g,{color:d.color});const key=color+':'+r.toFixed(1);if(!groups.has(key))groups.set(key,{color,r,items:[]});groups.get(key).items.push({x:cx,y:cy});this.hits.add({_id:i,x:cx-r-2,y:cy-r-2,w:r*2+4,h:r*2+4,tipX:cx,tipY:cy-r,tooltip:`(${d.x}, ${d.y})${d.label ? ` · ${d.label}` : ''}`,});}e.withAlpha(0.7,()=>{for(const{color,r,items}of groups.values())e.circles(items,color,r);});}}class BubbleChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity,zMax=0;for(const d of data){if(d.x<xMin)xMin=d.x;if(d.x>xMax)xMax=d.x;if(d.y<yMin)yMin=d.y;if(d.y>yMax)yMax=d.y;if((d.z||0)>zMax)zMax=d.z;}const[xLo,xHi]=M.niceDomain(xMin,xMax,6);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});const prog=this._anim.progress;const maxR=32,minR=4;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(minR+(Math.sqrt((d.z||0)/Math.max(1,zMax)))*(maxR-minR))*prog;const color=d.color||this._colorOf(d.group!=null?d.group:i);e.circle(cx,cy,r,{fill:M.colorAlpha(color,0.5),stroke:color,lineWidth:1.5,});this.hits.add({_id:i,x:cx-r,y:cy-r,w:r*2,h:r*2,tipX:cx,tipY:cy-r,tooltip:`${d.label || ''} (${d.x}, ${d.y}) · z=${d.z}`,});}}}class PieChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const total=data.reduce((a,d,i)=>a+valueOf(d,i),0);if(total<=0){this.setEmpty('No data');return;}const cx=e.w/2,cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-24;const innerR=this.options.innerRadius!=null?(typeof this.options.innerRadius==='number'?this.options.innerRadius:R*0.55):0;const prog=this._anim.progress;let a0=-Math.PI/2;for(let i=0;i<data.length;i++){const v=valueOf(data[i],i);const sweep=(v/total)*TAU*prog;const a1=a0+sweep;const color=this._colorOf(i,data[i]);const isHover=this._hover&&this._hover._id===i;const offset=isHover?6:0;const mid=(a0+a1)/2;const ox=Math.cos(mid)*offset,oy=Math.sin(mid)*offset;e.arc(cx+ox,cy+oy,innerR,R,a0,a1,{fill:color});const midR=(innerR+R)/2;const hx=cx+Math.cos(mid)*midR;const hy=cy+Math.sin(mid)*midR;this.hits.add({_id:i,x:hx-12,y:hy-12,w:24,h:24,tipX:hx,tipY:hy-8,tooltip:`${labelOf(data[i], i)}: ${v} (${(v / total * 100).toFixed(1)}%)`,});a0=a1;}if(innerR>0&&this.options.centerLabel!==false){const center=this._hover?`${(valueOf(data[this._hover._id], this._hover._id) / total * 100).toFixed(0)}%`:(this.options.totalLabel||total.toLocaleString());e.text(center,cx,cy,{fill:t.text,font:`600 ${t.fontSize * 1.6}px ${t.font}`,align:'center',baseline:'middle',});}}}class RadarChart extends Chart{_render(){const series=this.options.series||[];const axes=this.options.axes||[];if(!series.length||!axes.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const cx=e.w/2;const cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-32;const N=axes.length;const angle=i=>-Math.PI/2+(i/N)*TAU;let max=-Infinity;for(const s of series)for(const v of s.values)if(+v>max)max=+v;const yLo=0;const niceMax=M.niceDomain(0,max,5)[1];const rings=M.niceTicks(0,niceMax,4).filter(v=>v>0);e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;for(const v of rings){const r=(v/niceMax)*R;e.ctx.beginPath();for(let i=0;i<=N;i++){const a=angle(i%N);const x=cx+Math.cos(a)*r;const y=cy+Math.sin(a)*r;if(i===0)e.ctx.moveTo(x,y);else e.ctx.lineTo(x,y);}e.ctx.stroke();}e.ctx.strokeStyle=t.grid;for(let i=0;i<N;i++){const a=angle(i);e.ctx.beginPath();e.ctx.moveTo(cx,cy);e.ctx.lineTo(cx+Math.cos(a)*R,cy+Math.sin(a)*R);e.ctx.stroke();}const font=`${t.fontSize}px ${t.font}`;for(let i=0;i<N;i++){const a=angle(i);const lx=cx+Math.cos(a)*(R+14);const ly=cy+Math.sin(a)*(R+14);e.text(axes[i],lx,ly,{fill:t.muted,font,align:Math.cos(a)>0.3?'left':Math.cos(a)<-0.3?'right':'center',baseline:Math.sin(a)>0.3?'top':Math.sin(a)<-0.3?'bottom':'middle',});}const prog=this._anim.progress;for(let s=0;s<series.length;s++){const ser=series[s];const color=this._colorOf(s,ser);const pts=[];for(let i=0;i<N;i++){const v=(+ser.values[i]||0)*prog;const r=(v/niceMax)*R;const a=angle(i);pts.push({x:cx+Math.cos(a)*r,y:cy+Math.sin(a)*r});}e.poly(pts,{fill:M.colorAlpha(color,0.25),stroke:color,lineWidth:2,close:true,});e.circles(pts,color,t.pointRadius);}this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),e.w-16);}}class PolarChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const N=data.length;const cx=e.w/2,cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-24;const max=Math.max(...data.map((d,i)=>valueOf(d,i)));const prog=this._anim.progress;for(let i=0;i<N;i++){const v=valueOf(data[i],i);const a0=-Math.PI/2+(i/N)*TAU;const a1=a0+(TAU/N)*0.92;const r=(v/max)*R*prog;const color=this._colorOf(i,data[i]);e.arc(cx,cy,0,r,a0,a1,{fill:color});const mid=(a0+a1)/2;const lx=cx+Math.cos(mid)*(R+12);const ly=cy+Math.sin(mid)*(R+12);e.text(labelOf(data[i],i),lx,ly,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});}}}class GaugeChart extends Chart{setValue(v){this.options.value=v;this._anim.start=performance.now();this._anim.progress=0;this._dirty=true;scheduler.schedule(this);return this;}_render(){const e=this.engine;const t=this.theme;const val=this.options.value||0;const max=this.options.max||100;const cx=e.w/2;const cy=e.h*0.7;const R=Math.min(e.w/2,e.h*0.7)-12;const r=R-18;const a0=Math.PI*1.1;const a1=Math.PI*1.9;const span=a1-a0;const prog=this._anim.progress;const color=this.options.color||this.palette[0];e.arc(cx,cy,r,R,a0,a1,{fill:t.grid});const aEnd=a0+span*(val/max)*prog;if(aEnd>a0)e.arc(cx,cy,r,R,a0,aEnd,{fill:color});const num=Math.round(val);const unit=this.options.unit||'';e.text(`${num}${unit}`,cx,cy,{fill:t.text,font:`700 ${R / 2.5}px ${t.font}`,align:'center',baseline:'middle',});if(this.options.label){e.text(this.options.label,cx,cy+R/2.5,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'top',});}}}class HeatmapChart extends Chart{_axisPad(){return{l:60,r:16,t:16,b:40};}_render(){const data=this.options.data||[];const xLabels=this.options.xLabels||[];const yLabels=this.options.yLabels||[];if(!data.length||!xLabels.length||!yLabels.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const pad=this._axisPad();const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;let vMax=-Infinity,vMin=Infinity;for(const d of data){if(d.value>vMax)vMax=d.value;if(d.value<vMin)vMin=d.value;}const cellW=pw/xLabels.length;const cellH=ph/yLabels.length;const lo=this.options.colorLow||M.colorAlpha(this.palette[0],0.1);const hi=this.options.colorHigh||this.palette[0];const gap=1;const prog=this._anim.progress;for(const d of data){const xi=typeof d.x==='number'?d.x:xLabels.indexOf(d.x);const yi=typeof d.y==='number'?d.y:yLabels.indexOf(d.y);if(xi<0||yi<0)continue;const x=px+xi*cellW;const y=py+yi*cellH;const tNorm=vMax>vMin?(d.value-vMin)/(vMax-vMin):0.5;const fill=M.colorMix(lo,hi,tNorm*prog);e.rect(x+gap,y+gap,cellW-gap*2,cellH-gap*2,{fill,radius:2});this.hits.add({_id:`${xi}-${yi}`,x,y,w:cellW,h:cellH,tipX:x+cellW/2,tipY:y,tooltip:`${xLabels[xi]} · ${yLabels[yi]}: ${d.value}`,});}for(let i=0;i<xLabels.length;i++){e.text(xLabels[i],px+(i+0.5)*cellW,py+ph+4,{fill:t.muted,font,align:'center',baseline:'top',});}for(let i=0;i<yLabels.length;i++){e.text(yLabels[i],px-6,py+(i+0.5)*cellH,{fill:t.muted,font,align:'right',baseline:'middle',});}}}class BoxplotChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let yMax=-Infinity,yMin=Infinity;for(const d of data){if(d.max>yMax)yMax=d.max;if(d.min<yMin)yMin=d.min;if(d.outliers)for(const o of d.outliers){if(o>yMax)yMax=o;if(o<yMin)yMin=o;}}const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(d=>d.label),});const slot=ax.pw/data.length;const boxW=Math.min(40,slot*0.5);for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.px+(i+0.5)*slot;const yMin=ax.yToPx(d.min);const yMax=ax.yToPx(d.max);const yQ1=ax.yToPx(d.q1);const yQ3=ax.yToPx(d.q3);const yMed=ax.yToPx(d.median);const color=this._colorOf(i,d);e.poly([{x:cx,y:yMax},{x:cx,y:yMin}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:yMax},{x:cx+boxW/3,y:yMax}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:yMin},{x:cx+boxW/3,y:yMin}],{stroke:color,lineWidth:1});e.rect(cx-boxW/2,yQ3,boxW,yQ1-yQ3,{fill:M.colorAlpha(color,0.4),stroke:color,lineWidth:1.5,});e.poly([{x:cx-boxW/2,y:yMed},{x:cx+boxW/2,y:yMed}],{stroke:color,lineWidth:2,});if(d.outliers){e.circles(d.outliers.map(v=>({x:cx,y:ax.yToPx(v)})),color,2);}this.hits.add({_id:i,x:cx-boxW/2,y:yQ3,w:boxW,h:yQ1-yQ3,tipX:cx,tipY:yQ3,tooltip:{title:d.label,rows:[{name:'min',value:d.min},{name:'Q1',value:d.q1},{name:'med',value:d.median},{name:'Q3',value:d.q3},{name:'max',value:d.max},],},});}}}class FunnelChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const N=data.length;const max=Math.max(...data.map((d,i)=>valueOf(d,i)));const pad=24;const w=e.w-pad*2;const segH=(e.h-pad*2)/N;const cx=e.w/2;const prog=this._anim.progress;const font=`${t.fontSize}px ${t.font}`;for(let i=0;i<N;i++){const v=valueOf(data[i],i);const wTop=(v/max)*w*prog;const wBot=i+1<N?(valueOf(data[i+1],i+1)/max)*w*prog:wTop*0.85;const yTop=pad+i*segH;const yBot=yTop+segH-2;const color=this._colorOf(i,data[i]);const trap=[{x:cx-wTop/2,y:yTop},{x:cx+wTop/2,y:yTop},{x:cx+wBot/2,y:yBot},{x:cx-wBot/2,y:yBot},];e.poly(trap,{fill:color,close:true});e.text(`${labelOf(data[i], i)} ${v}`,cx,yTop+segH/2,{fill:'#fff',font:`600 ${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});this.hits.add({_id:i,x:cx-wTop/2,y:yTop,w:wTop,h:segH,tipX:cx,tipY:yTop,tooltip:`${labelOf(data[i], i)}: ${v} (${(v / max * 100).toFixed(1)}%)`,});}}}class TreemapChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const items=data.map((d,i)=>({label:labelOf(d,i),value:valueOf(d,i),color:this._colorOf(i,d),data:d,_id:i,})).sort((a,b)=>b.value-a.value);const total=items.reduce((a,x)=>a+x.value,0);const rect={x:4,y:4,w:e.w-8,h:e.h-8};const prog=this._anim.progress;squarify(items,total,rect,(it,r)=>{const padIn=2;const x=r.x+padIn,y=r.y+padIn;const w=(r.w-padIn*2)*prog;const h=(r.h-padIn*2)*prog;if(w<1||h<1)return;e.rect(x,y,w,h,{fill:it.color,radius:2});if(w>60&&h>30){e.text(it.label,x+6,y+6,{fill:'#fff',font:`600 ${t.fontSize}px ${t.font}`,align:'left',baseline:'top',});e.text(String(it.value),x+6,y+6+t.fontSize+2,{fill:'rgba(255,255,255,0.85)',font:`${t.fontSize}px ${t.font}`,align:'left',baseline:'top',});}this.hits.add({_id:it._id,x,y,w,h,tipX:x+w/2,tipY:y,tooltip:`${it.label}: ${it.value}`,});});}}function squarify(items,total,rect,draw){if(!items.length)return;const area=rect.w*rect.h;let i=0;while(i<items.length){const remaining=items.slice(i);const remTotal=remaining.reduce((a,x)=>a+x.value,0);if(remTotal<=0)break;const remArea=(remTotal/total)*area;const w=Math.min(rect.w,rect.h);let row=[],rowSum=0;let bestRatio=Infinity;for(let j=0;j<remaining.length;j++){const next=remaining[j];const trialSum=rowSum+next.value;const trialAreaPx=(trialSum/remTotal)*remArea;const trialW=trialAreaPx/w;let worst=0;for(const r of[...row,next]){const a=(r.value/trialSum)*trialAreaPx;const ratio=Math.max(trialW/(a/trialW),(a/trialW)/trialW);if(ratio>worst)worst=ratio;}if(worst>bestRatio)break;bestRatio=worst;row.push(next);rowSum=trialSum;}if(!row.length)row=[remaining[0]],rowSum=remaining[0].value;const isHoriz=rect.w>=rect.h;const rowAreaPx=(rowSum/remTotal)*remArea;const stripT=rowAreaPx/w;let cursor=isHoriz?rect.y:rect.x;for(const it of row){const itLen=((it.value/rowSum)*rowAreaPx)/stripT;const r=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};const r2=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};const placedRect=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};draw(it,placedRect);cursor+=itLen;}if(isHoriz){rect.x+=stripT;rect.w-=stripT;}else{rect.y+=stripT;rect.h-=stripT;}i+=row.length;if(rect.w<1||rect.h<1)break;}}class Sparkline extends Chart{_render(){const data=this.options.data||[];if(!data.length)return;const e=this.engine;const t=this.theme;const valueOf=this.options.value||(v=>+v);const values=data.map(valueOf);const yMin=Math.min(...values),yMax=Math.max(...values);const yRange=yMax-yMin||1;const pad=2;const w=e.w-pad*2;const h=e.h-pad*2;const points=values.map((v,i)=>({x:pad+(i/(values.length-1||1))*w,y:pad+h-((v-yMin)/yRange)*h,}));const color=this.options.color||this.palette[0];const cps=M.bezierControlPoints(points);if(this.options.fill!==false){const grad=e.vGradient(`spark-${color}`,0,pad,pad+h,[[0,M.colorAlpha(color,0.3)],[1,M.colorAlpha(color,0)],]);e.smoothArea(points,cps,pad+h,grad);}e.smooth(points,cps,{stroke:color,lineWidth:t.lineWidth});if(this.options.lastDot!==false){const last=points[points.length-1];e.circle(last.x,last.y,t.pointRadius+1,{fill:color});}}}class RealtimeChart extends Chart{constructor(container,options={}){super(container,options);this.options.windowSize=options.windowSize||200;this._series=(options.series||[]).map(s=>({name:s.name||'Series',color:s.color,values:[],hidden:false,}));this._streaming=true;}push(values){const arr=Array.isArray(values)?values:[values];for(let i=0;i<this._series.length;i++){const s=this._series[i];s.values.push(arr[i]!=null?arr[i]:0);if(s.values.length>this.options.windowSize)s.values.shift();}if(this._state==='empty')this._state=null;this._dirty=true;scheduler.schedule(this);}clear(){for(const s of this._series)s.values.length=0;this._dirty=true;scheduler.schedule(this);}_render(){const e=this.engine;const t=this.theme;const hasData=this._series.some(s=>s.values.length>0);if(!hasData){const ax=this._drawCartesian({yMin:0,yMax:100,yTickFormat:this.options.yTickFormat,});e.text('streaming…',ax.px+ax.pw/2,ax.py+ax.ph/2,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});return;}let yMin=Infinity,yMax=-Infinity;for(const s of this._series)for(const v of s.values){if(v<yMin)yMin=v;if(v>yMax)yMax=v;}const[yLo,yHi]=M.niceDomain(Math.min(0,yMin),yMax,5);const N=this.options.windowSize;const ax=this._drawCartesian({yMin:yLo,yMax:yHi,yTickFormat:this.options.yTickFormat,});for(let s=0;s<this._series.length;s++){const ser=this._series[s];if(ser.hidden)continue;const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:ax.px+(i/(N-1))*ax.pw,y:ax.yToPx(v),}));if(pts.length<2){if(pts.length===1)e.circle(pts[0].x,pts[0].y,t.pointRadius+2,{fill:color});continue;}const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:t.lineWidth});const last=pts[pts.length-1];e.circle(last.x,last.y,t.pointRadius+2,{fill:color});}this._drawLegend(this._series.map((s,i)=>({label:s.name,color:this._colorOf(i,s),idx:i,hidden:s.hidden,})),ax.pw);}}class WaterfallChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let cum=0;const segs=data.map(d=>{if(d.isTotal){const seg={from:0,to:d.value,value:d.value,isTotal:true,label:d.label};cum=d.value;return seg;}else{const seg={from:cum,to:cum+d.value,value:d.value,label:d.label};cum+=d.value;return seg;}});const allVals=segs.flatMap(s=>[s.from,s.to]);const[yLo,yHi]=M.niceDomain(Math.min(...allVals,0),Math.max(...allVals),5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(d=>d.label),});const slot=ax.pw/segs.length;const barW=Math.max(2,Math.min(60,slot-12));const prog=this._anim.progress;const upColor=this.options.colorUp||'#10b981';const dnColor=this.options.colorDown||'#ef4444';const totalColor=this.options.colorTotal||'#6366f1';for(let i=0;i<segs.length;i++){const s=segs[i];const x=ax.px+(i+0.5)*slot-barW/2;const y0=ax.yToPx(s.from);const y1=ax.yToPx(s.to);const top=Math.min(y0,y1);const fullH=Math.abs(y1-y0);const h=fullH*prog;const animTop=s.to>=s.from?y0-h:y0;const color=s.isTotal?totalColor:(s.value>=0?upColor:dnColor);e.rect(x,animTop,barW,h,{fill:color,radius:t.barRadius});if(i<segs.length-1&&!segs[i+1].isTotal){const nextX=ax.px+(i+1.5)*slot-barW/2;const yEnd=ax.yToPx(s.to);e.poly([{x:x+barW,y:yEnd},{x:nextX,y:yEnd}],{stroke:t.muted,lineWidth:1,dash:[3,3],});}this.hits.add({_id:i,x,y:animTop,w:barW,h,tipX:x+barW/2,tipY:animTop,tooltip:`${s.label}: ${s.value > 0 && !s.isTotal ? '+' : ''}${s.value}`,});}}}class CompositeChart extends Chart{_render(){const layers=this.options.layers||[];const cats=this.options.categories||[];if(!layers.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const leftLayers=layers.filter(l=>l.axis!=='right');const rightLayers=layers.filter(l=>l.axis==='right');const domain=(ll)=>{let mn=Infinity,mx=-Infinity;for(const l of ll){if(l.type==='scatter'){for(const p of l.values){if(p.y<mn)mn=p.y;if(p.y>mx)mx=p.y;}}else{for(const v of l.values){if(+v<mn)mn=+v;if(+v>mx)mx=+v;}}}if(mn===Infinity)return[0,1];return M.niceDomain(Math.min(0,mn),mx,5);};const[yLLo,yLHi]=domain(leftLayers);const[yRLo,yRHi]=rightLayers.length?domain(rightLayers):[0,1];const pad={l:50,r:rightLayers.length?50:16,t:30,b:32};const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;const yLPx=v=>py+ph*(1-(v-yLLo)/(yLHi-yLLo));const yRPx=v=>py+ph*(1-(v-yRLo)/(yRHi-yRLo));const yLTicks=M.niceTicks(yLLo,yLHi,5);const yRTicks=rightLayers.length?M.niceTicks(yRLo,yRHi,5):[];e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of yLTicks){const y=Math.round(yLPx(v))+0.5;e.ctx.moveTo(px,y);e.ctx.lineTo(px+pw,y);}e.ctx.stroke();const yLFmt=this.options.yLeftTickFormat||(v=>String(v));const yRFmt=this.options.yRightTickFormat||(v=>String(v));for(const v of yLTicks){e.text(yLFmt(v),px-6,yLPx(v),{fill:t.muted,font,align:'right',baseline:'middle',});}for(const v of yRTicks){e.text(yRFmt(v),px+pw+6,yRPx(v),{fill:t.muted,font,align:'left',baseline:'middle',});}const useTime=this.options.xScale==='time';const xToPx=useTime?(i)=>px+((+cats[i]-+cats[0])/(+cats[cats.length-1]-+cats[0]||1))*pw:(i)=>px+((i+0.5)/cats.length)*pw;let lastEnd=-Infinity;for(let i=0;i<cats.length;i++){const x=xToPx(i);const txt=useTime?new Date(+cats[i]).toISOString().slice(0,10):String(cats[i]);const w=e.measure(txt,font);if(x-w/2<lastEnd+4)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});const prog=this._anim.progress;const lineW=t.lineWidth;const barLayers=layers.filter(l=>l.type==='bar');const slot=pw/cats.length;const innerSlot=slot*0.7;const barW=barLayers.length?Math.max(2,innerSlot/barLayers.length):0;barLayers.forEach((layer,bi)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const yBase=yPx(0);const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const items=[];for(let i=0;i<cats.length;i++){const v=+layer.values[i]||0;const xC=xToPx(i);const x=xC-innerSlot/2+bi*barW;const y0=yBase;const y1=yPx(v);const top=Math.min(y0,y1);const h=Math.abs(y1-y0)*prog;const animTop=v>=0?y0-h:y0;items.push({x,y:animTop,w:barW-1,h,radius:t.barRadius});this.hits.add({_id:`${layer.name || bi}-${i}`,x,y:animTop,w:barW-1,h,tipX:x+barW/2,tipY:animTop,tooltip:`${layer.name || ''} · ${cats[i]}: ${v}`,});}e.rects(items,color);});e.clip(px,py,pw*prog,ph);layers.filter(l=>l.type==='area').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const pts=layer.values.map((v,i)=>({x:xToPx(i),y:yPx(+v)}));const cps=M.bezierControlPoints(pts);const grad=e.vGradient(`area-${layer.name}-${color}`,px,py,py+ph,[[0,M.colorAlpha(color,0.4)],[1,M.colorAlpha(color,0)],]);e.smoothArea(pts,cps,py+ph,grad);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});});layers.filter(l=>l.type==='line').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const pts=layer.values.map((v,i)=>({x:xToPx(i),y:yPx(+v),i,v}));const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});if(pts.length<=50)e.circles(pts,color,t.pointRadius);for(const p of pts){this.hits.add({_id:`${layer.name || 'line'}-${p.i}`,x:p.x-6,y:p.y-6,w:12,h:12,tipX:p.x,tipY:p.y-t.pointRadius,tooltip:`${layer.name || ''} · ${cats[p.i]}: ${p.v}`,});}});e.unclip();layers.filter(l=>l.type==='scatter').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const items=layer.values.map((p)=>({x:xToPx(typeof p.x==='number'?p.x:cats.indexOf(p.x)),y:yPx(p.y),}));e.withAlpha(0.7,()=>e.circles(items,color,layer.radius||t.pointRadius+1));});this._drawLegend(layers.map((l,i)=>({label:(l.name||l.type)+(l.axis==='right'?' (R)':''),color:l.color||this._colorOf(i,l),idx:i,hidden:false,})),pw);}}export{Chart,Engine,getFPS,getActiveCount,scheduler,math,PALETTE,BarChart,HBarChart,LineChart,AreaChart,StackedBarChart,GroupedBarChart,MultiLineChart,ScatterChart,BubbleChart,PieChart,RadarChart,PolarChart,GaugeChart,HeatmapChart,BoxplotChart,FunnelChart,TreemapChart,Sparkline,RealtimeChart,WaterfallChart,CompositeChart,};