@rfkit/renderer 0.3.0 → 0.4.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/README.md +32 -6
- package/core/canvasCapture.d.ts +12 -0
- package/index.d.ts +4 -3
- package/index.js +265 -3
- package/package.json +1 -1
- package/renderers/cartesian/Fluorescence.d.ts +47 -25
- package/renderers/cartesian/FluorescenceCanvas.d.ts +40 -0
- package/renderers/cartesian/Heatmap.d.ts +2 -15
- package/renderers/cartesian/index.d.ts +2 -1
- package/renderers/fluorescence/shared.d.ts +18 -0
- package/renderers/webgl/FluorescenceWebGL.d.ts +78 -0
- package/renderers/webgl/HeatmapWebGL.d.ts +14 -0
- package/renderers/webgl/index.d.ts +2 -0
- package/types/index.d.ts +1 -1
- package/types/state.d.ts +2 -0
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# rfkit-renderer
|
|
2
|
-
Canvas/WebGL 渲染器集合,适用于射频和通用数据可视化。内置线谱、热力/荧光图、IQ/眼图、仪表盘、环形/雷达等组件,并提供 WebGL
|
|
2
|
+
Canvas/WebGL 渲染器集合,适用于射频和通用数据可视化。内置线谱、热力/荧光图、IQ/眼图、仪表盘、环形/雷达等组件,并提供 WebGL 热力渲染与 Canvas 自动降级。
|
|
3
3
|
|
|
4
4
|
## 主要能力
|
|
5
|
-
- Canvas 2D:`Series`(Line/Bar/Area/Stepline)、`
|
|
6
|
-
- WebGL:`HeatmapWebGL
|
|
5
|
+
- Canvas 2D:`Series`(Line/Bar/Area/Stepline)、`HeatmapCanvas`、`FluorescenceCanvas`、`Gauge`、`Dial`、`Radar`、`IQ`、`IQEye`
|
|
6
|
+
- WebGL:`HeatmapWebGL`、`FluorescenceWebGL`;统一入口 `Heatmap` / `Fluorescence` 在能力不足时自动降级到 Canvas
|
|
7
7
|
- 工具与类型:`ColorInterpolator`、`GraphicType`、`OrientationType`、`AxisYRange`、`SeriesConfig` 等
|
|
8
8
|
|
|
9
9
|
## 安装
|
|
@@ -18,6 +18,7 @@ HTML 容器:
|
|
|
18
18
|
```html
|
|
19
19
|
<div id="heatmap" style="width:640px;height:240px;"></div>
|
|
20
20
|
<div id="series" style="width:640px;height:200px;"></div>
|
|
21
|
+
<div id="fluorescence" style="width:640px;height:240px;"></div>
|
|
21
22
|
<div id="heatmap-webgl" style="width:640px;height:240px;"></div>
|
|
22
23
|
```
|
|
23
24
|
|
|
@@ -25,10 +26,13 @@ HTML 容器:
|
|
|
25
26
|
|
|
26
27
|
```ts
|
|
27
28
|
import {
|
|
29
|
+
Fluorescence,
|
|
30
|
+
FluorescenceRenderMode,
|
|
28
31
|
Heatmap,
|
|
29
|
-
Series,
|
|
30
32
|
HeatmapWebGL,
|
|
31
|
-
GraphicType
|
|
33
|
+
GraphicType,
|
|
34
|
+
RendererType,
|
|
35
|
+
Series
|
|
32
36
|
} from '@rfkit/renderer';
|
|
33
37
|
|
|
34
38
|
const heatmap = new Heatmap({
|
|
@@ -54,6 +58,20 @@ sweep.render({
|
|
|
54
58
|
color: '#00FFA3',
|
|
55
59
|
data: Float32Array.from({ length: 512 }, (_, i) => -90 + Math.cos(i / 18) * 15)
|
|
56
60
|
});
|
|
61
|
+
|
|
62
|
+
const fluorescence = new Fluorescence({
|
|
63
|
+
id: 'fluorescence',
|
|
64
|
+
range: [-20, 100],
|
|
65
|
+
display: true,
|
|
66
|
+
renderer: RendererType.WebGL,
|
|
67
|
+
fluorescenceRenderMode: FluorescenceRenderMode.Gaussian
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
fluorescence.render({
|
|
71
|
+
// 频率优先排列;每个频率点固定包含 -20..140 的 161 个电平计数。
|
|
72
|
+
fluorescenceData: new Uint32Array(frequencyPointCount * 161),
|
|
73
|
+
fluorescenceMaxCount: 100
|
|
74
|
+
});
|
|
57
75
|
```
|
|
58
76
|
|
|
59
77
|
使用 WebGL 热力渲染(需要 WebGL2 支持):
|
|
@@ -71,10 +89,18 @@ heatmapGL.render(largeMatrix); // largeMatrix: number[][]
|
|
|
71
89
|
## 数据格式速览
|
|
72
90
|
- `Series.render` 接收 `SeriesConfig`(必填 `name`,`data` 为 `Float32Array`,`type` 由 `GraphicType` 指定)。
|
|
73
91
|
- `Heatmap` / `HeatmapWebGL` 接收 `number[][]`,内层数组表示一行数据。
|
|
74
|
-
- `Fluorescence` 接收 `{ fluorescenceData:
|
|
92
|
+
- `Fluorescence` 接收 `{ fluorescenceData: Uint32Array, fluorescenceMaxCount: number }`;数组按频率点优先排列,每个频率点固定包含 `-20..140` 的 161 个电平计数。
|
|
75
93
|
- `IQ`/`IQEye` 接收 `{ IData: number[], QData: number[] }`。
|
|
76
94
|
`IQEye` 额外支持 `iqEyeMode`(`'classic' | 'legacy' | 'modern'`),默认 `classic`:每帧重算、绿红显影;传 `modern` 可启用跨帧 decay 模式。
|
|
77
95
|
- `Dial`/`Radar` 使用 `CircularData`;`Gauge` 使用 `{ value: number; limit?: number }`。
|
|
78
96
|
`CircularData` 支持可选 `polygon: CircularPolygonItem[]` 绘制雷达多边形:每个多边形提供 `values`(各方向归一化值 0-1,长度即方向数,按 0..360 度均匀分布)、可选 `color`(缺省走主题主色)与可选 `showDots`(是否绘制顶点小圆点,缺省 true);各方向值决定半径,连成闭合多边形,径向渐变填充(中心透明→边缘主题色)+ 细描边 + 顶点实心圆点。支持多网叠加,绘制时按每网最大半径降序(大网下层、小网上层)。
|
|
79
97
|
|
|
80
98
|
常用操作:`setRange(range)` 调整量程并重绘;`resize()` 在容器尺寸变化后调用;`clear()` 清空画布及内部数据。
|
|
99
|
+
|
|
100
|
+
WebGL 默认不保留绘图缓冲。DOM 截图工具需要同步取得当前帧时,可对目标 Canvas 调用 `captureCanvasFrame(canvas)`;受管 WebGL Canvas 返回方向已校正的 Data URL,普通 Canvas 返回 `null` 并继续使用截图工具的原生路径。
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { captureCanvasFrame } from '@rfkit/renderer';
|
|
104
|
+
|
|
105
|
+
const frame = captureCanvasFrame(document.querySelector('canvas')!);
|
|
106
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 可在绘图缓冲释放前同步导出当前帧的 Canvas 渲染器。 */
|
|
2
|
+
export interface CanvasCaptureSource {
|
|
3
|
+
toDataURL(type?: string, quality?: number): string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare const registerCanvasCapture: (canvas: HTMLCanvasElement, source: CanvasCaptureSource) => void;
|
|
6
|
+
export declare const unregisterCanvasCapture: (canvas: HTMLCanvasElement, source: CanvasCaptureSource) => void;
|
|
7
|
+
/**
|
|
8
|
+
* 导出受管 WebGL Canvas 的当前可见帧。
|
|
9
|
+
*
|
|
10
|
+
* 普通 Canvas 返回 null,交由截图工具按原有 DOM/CSS 路径处理。
|
|
11
|
+
*/
|
|
12
|
+
export declare const captureCanvasFrame: (canvas: HTMLCanvasElement, type?: string, quality?: number) => string | null;
|
package/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { default as ColorInterpolator } from './color/ColorInterpolator';
|
|
2
2
|
export { color2intensity, hexToRGBA, rgbToHex } from './color/ColorUtils';
|
|
3
|
-
export type
|
|
4
|
-
export {
|
|
3
|
+
export { type CanvasCaptureSource, captureCanvasFrame } from './core/canvasCapture';
|
|
4
|
+
export type { FluorescenceRendererState, IFluorescence, IHeatmap, ISeries } from './renderers/cartesian';
|
|
5
|
+
export { createFluorescence, createHeatmap, createSeries, Fluorescence, FluorescenceCanvas, Gauge, Heatmap, HeatmapCanvas, Series, SeriesCanvas } from './renderers/cartesian';
|
|
5
6
|
export { Dial, Radar } from './renderers/circular';
|
|
6
7
|
export { IQ, IQEye } from './renderers/scatter';
|
|
7
|
-
export { HeatmapWebGL } from './renderers/webgl';
|
|
8
|
+
export { FluorescenceWebGL, HeatmapWebGL } from './renderers/webgl';
|
|
8
9
|
export { type AxisYRange, type CircularData, type CircularPolygonItem, FluorescenceRenderMode, type GaugeData, GraphicType, type IQData, type IQEyeRenderMode, OrientationType, RendererType, type SeriesConfig, type StateProps } from './types';
|
package/index.js
CHANGED
|
@@ -1,4 +1,266 @@
|
|
|
1
|
-
let t=new Map;function e(t,e){let a,r,i,l;if(!/^#([A-Fa-f0-9]{3}){1,2}([A-Fa-f0-9]{2})?$/.test(t))throw Error("Invalid hex color format");let n=t.replace("#","");if(3===n.length)a=Number.parseInt(n[0]+n[0],16),r=Number.parseInt(n[1]+n[1],16),i=Number.parseInt(n[2]+n[2],16),l=255;else if(6===n.length)a=Number.parseInt(n.substring(0,2),16),r=Number.parseInt(n.substring(2,4),16),i=Number.parseInt(n.substring(4,6),16),l=255;else if(8===n.length)a=Number.parseInt(n.substring(0,2),16),r=Number.parseInt(n.substring(2,4),16),i=Number.parseInt(n.substring(4,6),16),l=Number.parseInt(n.substring(6,8),16);else throw Error("Invalid hex color length");return"number"==typeof e&&(l=Math.round(255*Math.max(0,Math.min(1,e)))),{r:a,g:r,b:i,a:l}}function a(t,e,r,i=255){let l=t=>t.toString(16).padStart(2,"0"),n=`#${l(t)}${l(e)}${l(r)}`;return i<255?`${n}${l(i)}`:n}function r(e){if("string"==typeof e){let a=t.get(e);if(a)return a;let r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(e),i=r?{r:parseInt(r[1],16),g:parseInt(r[2],16),b:parseInt(r[3],16),a:r[4]?parseInt(r[4],16):255}:{r:0,g:0,b:0,a:255};return t.set(e,i),i}return{r:e.r,g:e.g,b:e.b,a:e.a??255}}function i(t,e=100){let a=[];for(let r=0;r<=e;r++){let i=Math.round(r/e*255).toString(16).padStart(2,"0").toUpperCase(),l=`${t}${i}`;a.push(l)}return a}function l(){t.clear()}class n{colorsPresetList=[];range=[0,0,0];static transparent={r:0,g:0,b:0,a:0};constructor(t,e,a,r=.1){this.setColors(t,e,a,r)}setColors(t,e,a,i){this.colorsPresetList=[];let l=Math.max(1,Math.floor((a-e)/i)),n=a-e;this.range=[e,a,n];let s=t.map(r);for(let t=0;t<=l;t++){let a=(e+t*i-e)/n;this.colorsPresetList.push(this.interpolateColor(s,a))}}interpolateColor(t,e){let r=Math.floor(e*(t.length-1)),i=e*(t.length-1)-r,l=t[r],n=t[r+1]||t[r],s={r:Math.round(l.r+i*(n.r-l.r)),g:Math.round(l.g+i*(n.g-l.g)),b:Math.round(l.b+i*(n.b-l.b)),a:Math.round(l.a+i*(n.a-l.a))};return{...s,hax:a(s.r,s.g,s.b,s.a)}}getColor(t){if(!Number.isFinite(t))return n.transparent;let[e,,a]=this.range,r=(t-e)/a;if(r<=0)return this.colorsPresetList[0];if(r>=1)return this.colorsPresetList[this.colorsPresetList.length-1];let i=Math.min(Math.floor(r*this.colorsPresetList.length),this.colorsPresetList.length-1);return this.colorsPresetList[i]}}class s{state;constructor(t){this.state={id:t.id,container:void 0,canvas:null,ctx:null,range:t.range??[-20,100]},this.init(t)}updateProps(t){this.state={...this.state,...t}}init(t){let{id:e}=this.state,a=document.getElementById(e),r=document.createElement("canvas"),i=r.getContext?.("2d");r.style.transform="scaleY(-1)",i&&(i.imageSmoothingEnabled=!1,i.lineJoin="miter",i.lineCap="butt",i.textBaseline="middle"),i&&this.updateProps({container:a??void 0,canvas:r,ctx:i}),a?.appendChild(r),this.resize(!1)}clearRect(){let{ctx:t,canvas:e}=this.state;t.clearRect(0,0,e.width,e.height)}resize(t=!0){let{canvas:e,container:a}=this.state,{clientWidth:r,clientHeight:i}=a;r&&i&&(e.width=r,e.height=i,t&&setTimeout(()=>{this.draw()}))}draw(){}dispose(){let{canvas:t,container:e}=this.state;e?.removeChild(t)}}let o={COLOR:"#000000",THICKNESS:1,FILL_STYLE:"#ffffffB0",FILL_STYLE_PRIMARY:"#1890ff",FILL_STYLE_TRANSPARENT_BASE:"#ffffff10",LINE_COLOR:"#00ff00",POINT_COLOR:"#ff0000",DIAL_NORTH_COLOR:"#ff4d4f",DIAL_SOUTH_COLOR:"#1890ff",FLUORESCENCE_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],RANGE:[-20,100]},h={LEVEL_MIN:-20,LEVEL_MAX:140,LEVEL_RANGE:161,MAX_GAUSSIAN_RADIUS:30},u={Y_RANGE:[-1,1],SEGMENT_SIZE:5,MODE:"classic",DECAY_FACTOR:.98,MAX_ACCUMULATION:1e3,MODERN_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],CLASSIC_COLORS:["#00ff00","#ff0000"],CLASSIC_ALPHA_GAIN:50};var d=/*#__PURE__*/function(t){return t.Horizontal="horizontal",t.Vertical="vertical",t}({}),c=/*#__PURE__*/function(t){return t.Circle="circle",t.Rect="rect",t.Line="line",t.Stepline="stepline",t.Bar="bar",t.Area="area",t}({}),g=/*#__PURE__*/function(t){return t.Canvas="canvas",t.WebGL="webgl",t}({}),f=/*#__PURE__*/function(t){return t.Gaussian="gaussian",t.Grid="grid",t}({});class p extends s{init(t){super.init(t);let{colors:e,display:a,fluorescenceRenderMode:r}=t;this.updateProps({colors:e,data:new Uint32Array(0),display:a,renderMode:r??f.Gaussian}),this.resize()}updateProps(t){let e=t.range&&(t.range[0]!==this.state.range?.[0]||t.range[1]!==this.state.range?.[1]),a=void 0!==t.renderMode&&t.renderMode!==this.state.renderMode;super.updateProps(t),(e||a)&&this.state.data?.length>0&&this.draw()}setRenderMode(t){this.updateProps({renderMode:t})}clearImageData(){let{ctx:t,canvas:{height:e,width:a}}=this.state;e&&a&&this.updateProps({imageData:t.createImageData(a,e)})}clearRect(){super.clearRect(),this.clearImageData()}clear(){this.clearRect(),this.updateProps({data:new Uint32Array(0)})}dispose(){this.intensityMatrixCache=null,this.gridCentersCache=null,this.weightLookupCache=null,this.colorLookupCache=null,this.colorCache.clear(),this.lastRenderParams=null,this.blockPositionsCache=null,this.lastBlockRenderParams=null}drawBlocks(){let{imageData:t,data:e,canvas:a,ctx:r,colors:i=o.FLUORESCENCE_COLORS,range:l=o.RANGE}=this.state;if(!e||0===e.length||!t)return;let{width:n,height:s}=a,[u,d]=l,c=d-u;if(c<=0)return;let g=e.length/h.LEVEL_RANGE;if(!Number.isInteger(g)||g<=0)return;let f={dataLength:g,width:n,height:s,rangeMin:u,rangeMax:d};if(!this.lastBlockRenderParams||Object.keys(f).some(t=>this.lastBlockRenderParams?.[t]!==f[t])||!this.blockPositionsCache||this.blockPositionsCache.length!==g){this.blockPositionsCache=Array(g);let t=Math.max(1,Math.floor(n/g));for(let e=0;e<g;e++){let a=Math.floor(e*n/g),r=Math.min(n-1,a+t-1);this.blockPositionsCache[e]={startX:a,endX:r,width:r-a+1}}}this.lastBlockRenderParams=f;let p=t.data;p.fill(0);let m=1/c,T=s-1,C=Math.max(1,Math.ceil(s/c)),E=0;for(let t=0;t<g;t++){let a=t*h.LEVEL_RANGE;for(let t=0;t<h.LEVEL_RANGE;t++){let r=e[a+t];if(r<=0)continue;let i=t+h.LEVEL_MIN;i<u||i>d||!(r>E)||(E=r)}}let M=Math.log(Math.max(E,1)+1);for(let t=0;t<g;t++){let{startX:a,endX:r}=this.blockPositionsCache[t],l=t*h.LEVEL_RANGE;for(let t=0;t<h.LEVEL_RANGE;t++){let s=e[l+t];if(s<=0)continue;let o=t+h.LEVEL_MIN;if(o<u||o>d)continue;let c=Math.max(0,Math.floor((o-u)*m*T-C/2)),g=Math.min(T,c+C-1),f=Math.log(s+1)/M,E=Math.round(255*f),{r:R,g:L,b:_}=this.interpolateColor(i,f);for(let t=c;t<=g;t++){let e=t*n;for(let t=a;t<=r;t++){let a=(e+t)*4;p[a]=R,p[a+1]=L,p[a+2]=_,p[a+3]=E}}}}r.putImageData(t,0,0)}resize(){super.resize(),this.clearImageData()}setRange(t){t&&(this.updateProps({range:t}),this.draw())}render(t){t?.fluorescenceData?.length>=0&&((this.state.data?.length??0)!==t.fluorescenceData.length&&(this.clearImageData(),this.intensityMatrixCache=null,this.gridCentersCache=null,this.blockPositionsCache=null,this.lastRenderParams=null,this.lastBlockRenderParams=null),this.state.data=t.fluorescenceData,this.state.fluorescenceMaxCount=t.fluorescenceMaxCount,this.draw())}interpolateColor(t,e){if(!t||0===t.length)return{r:255,g:255,b:255,a:255};e=Math.max(0,Math.min(1,e));let a=t=>"string"==typeof t?this.hexToRgb(t):{r:t.r,g:t.g,b:t.b};if(1===t.length||0===e)return{...a(t[0]),a:255};if(1===e)return{...a(t[t.length-1]),a:255};let r=e*(t.length-1),i=Math.floor(r),l=r-i,n=Math.min(i,t.length-2),s=a(t[n]),o=a(t[n+1]);return{r:Math.round(s.r+(o.r-s.r)*l),g:Math.round(s.g+(o.g-s.g)*l),b:Math.round(s.b+(o.b-s.b)*l),a:255}}colorCache=new Map;hexToRgb(t){let e=this.colorCache.get(t);if(e)return e;let a=r(t),i={r:a.r,g:a.g,b:a.b};return this.colorCache.set(t,i),i}lastRenderParams=null;intensityMatrixCache=null;gridCentersCache=null;weightLookupCache=null;colorLookupCache=null;blockPositionsCache=null;lastBlockRenderParams=null;draw(){let{imageData:t,data:e,canvas:a,ctx:r,colors:i=o.FLUORESCENCE_COLORS,range:l=o.RANGE,fluorescenceMaxCount:n=1,display:s,renderMode:u=f.Gaussian}=this.state;if(!s||!e||0===e.length||!t)return;if(u===f.Grid){this.drawBlocks();return}let{width:d,height:c}=a,[g,p]=l,m=p-g;if(m<=0)return;let T=e.length/h.LEVEL_RANGE;if(!Number.isInteger(T)||T<=0)return;let C={dataLength:T,rangeMin:g,rangeMax:p,width:d,height:c,maxCount:n},E=!this.lastRenderParams||this.lastRenderParams.dataLength!==C.dataLength||this.lastRenderParams.width!==C.width,M=!this.lastRenderParams||this.lastRenderParams.height!==C.height,R=!this.colorLookupCache;this.lastRenderParams=C;let L=t.data;L.fill(0);let _=Math.min(Math.max(2,Math.ceil(c/m),Math.floor(c/50)),h.MAX_GAUSSIAN_RADIUS),A=_*_,S=d*c,x=Math.min(Math.max(_,Math.ceil(d/T)),h.MAX_GAUSSIAN_RADIUS);this.intensityMatrixCache&&this.intensityMatrixCache.length===S?this.intensityMatrixCache.fill(0):this.intensityMatrixCache=new Float32Array(S);let w=this.intensityMatrixCache;if(E||!this.gridCentersCache||this.gridCentersCache.length!==T){this.gridCentersCache=new Float32Array(T);let t=d/T;for(let e=0;e<T;e++){let a=Math.floor(t*e),r=Math.ceil(t*(e+1));this.gridCentersCache[e]=(a+r)/2}}let I=this.gridCentersCache,P=Math.ceil(10*_)+1;if(M||!this.weightLookupCache||this.weightLookupCache.length!==P){this.weightLookupCache=new Float32Array(P);for(let t=0;t<=10*_;t++){let e=t/10;this.weightLookupCache[t]=Math.exp(-(e*e)/(2*A))}}let b=this.weightLookupCache,y=0;for(let t=0;t<T;t++){let a=I[t],r=t*h.LEVEL_RANGE;for(let t=0;t<h.LEVEL_RANGE;t++){let i=e[r+t];if(i<=0)continue;let l=t+h.LEVEL_MIN;if(l<g||l>p)continue;let s=(l-g)/m*(c-1),o=Math.log(i+1)/Math.log(Math.max(n,1)+1),u=Math.max(0,Math.floor(a-x)),f=Math.min(d-1,Math.ceil(a+x)),T=Math.max(0,Math.floor(s-_)),C=Math.min(c-1,Math.ceil(s+_));for(let t=T;t<=C;t++){let e=t-s,r=e*e,i=t*d;for(let t=u;t<=f;t++){let e=(t-a)*_/x,l=e*e+r;if(l>A)continue;let n=Math.floor(10*Math.sqrt(l)),s=n<b.length?b[n]:0,h=i+t,u=w[h]+o*s;w[h]=u,u>y&&(y=u)}}}}let v=y>0?1/y:0;if(R||!this.colorLookupCache||256!==this.colorLookupCache.length){this.colorLookupCache=Array(256);for(let t=0;t<256;t++){let e=t/255;this.colorLookupCache[t]=this.interpolateColor(i,e)}}let N=this.colorLookupCache;for(let t=0;t<w.length;t++){let e=w[t];if(e<=.001)continue;let a=Math.min(e*v,1),r=N[Math.min(Math.floor(255*a),255)],i=4*t;L[i]=r.r,L[i+1]=r.g,L[i+2]=r.b,L[i+3]=Math.round(255*a)}r.putImageData(t,0,0)}}function m(t,e,a,r,i){if(r.fill(0),!a)return;let l=a.length;if(0===l)return;let n=e/l,s=0;for(let e=0;e<l;e++){let l=a[e],o=l.length;if(0===o)continue;0===s&&(s=t/o);let h=Math.floor(n*e),u=Math.ceil(n*(e+1));for(let e=0;e<o;e++){let a=l[e],n=Math.floor(s*e),o=Math.ceil(s*(e+1)),d=i(a);for(let e=h;e<u;e++){let a=e*t;for(let t=n;t<o;t++){let e=(a+t)*4;r[e]=d.r,r[e+1]=d.g,r[e+2]=d.b,r[e+3]=d.a}}}}}function T(t){if(!Array.isArray(t)||0===t.length)throw Error("Input must be a non-empty array.");let e=t[0],a=t[0];for(let r=1;r<t.length;r++)t[r]<e?e=t[r]:t[r]>a&&(a=t[r]);return[e,a]}function C(t){return"number"==typeof t&&Number.isFinite(t)}function E(t){return new Float64Array(t.flatMap(e=>0===e.length?Array(t[0].length).fill(Number.NaN):e))}let M=null;function R(){if(null!==M)return M;try{let t=document.createElement("canvas").getContext("webgl2");if(M=!!t,t){let e=t.getExtension("WEBGL_lose_context");e?.loseContext()}}catch{M=!1}return M}class L extends s{init(t){super.init(t),this.state.canvas.style.transform="scaleY(1)";let e=t.fillStyle??o.FILL_STYLE,a=t.fillStylePrimary??o.FILL_STYLE_PRIMARY,r=t.fillStyleTransparentBase??o.FILL_STYLE_TRANSPARENT_BASE;this.updateProps({fillStyle:e,fillStylePrimary:a,fillStyleTransparentBase:r,baseWidth:6,padding:t.padding??0,step:t.ticksStep??10,lineWidth:1,data:{value:0,limit:0}})}clear(){this.clearRect();let{ctx:t,BGCanvas:e}=this.state;e&&t.drawImage(e,0,0)}resize(){super.resize(),this.state.lineWidth&&this.drawBackground()}render(t){this.state.data={...this.state.data,...t},this.draw()}draw(){let{data:{value:t},range:[e,a],canvas:r,ctx:i,baseWidth:l,padding:n,BGCanvas:s,fillStylePrimary:o}=this.state;this.clearRect(),s&&i.drawImage(s,0,0);let h=3*l,u=r.height-2*n,d=(r.width-h)/2,c=(t-e)/(a-e)*u;i.fillStyle=o,i.fillRect(d,u-c+n,h,c),this.drawLimit()}drawLimit(){let{data:{limit:t},range:[e,a],canvas:r,ctx:i,baseWidth:l,padding:n,lineWidth:s,fillStylePrimary:o}=this.state;if(!C(t))return;let h=3*l,u=r.height-2*n,d=(r.width-h)/2,c=u*(a-t)/(a-e)+n;i.beginPath(),i.moveTo(d+h,c),i.lineTo(d+h+9,c-9),i.lineTo(d+h+9+30,c-9),i.lineTo(d+h+9+30,c+9),i.lineTo(d+h+9,c+9),i.lineTo(d+h,c),i.lineTo(d,c),i.lineWidth=s,i.strokeStyle=o,i.stroke(),i.fillStyle=o,i.font="12px Arial",i.textAlign="center",i.textBaseline="middle",i.fillText(t.toString(),d+h+9+15,c+1)}drawBackground(){let{canvas:t,baseWidth:e,padding:a,lineWidth:r,step:i,range:[l,n],fillStyle:s,fillStyleTransparentBase:o}=this.state,h=document.createElement("canvas");h.width=t.width,h.height=t.height;let u=h.getContext("2d");if(!u)return;let d=3*e,c=t.height-2*a,g=(t.width-d)/2,f=Array.from({length:11},(t,e)=>l+Math.round(e*(n-l)/10/i)*i);u.fillStyle=o,u.fillRect(g,a,d,c);for(let t=l;t<=n;t+=i){let i=f.includes(t),o=c*(n-t)/(n-l)+a;u.beginPath(),u.moveTo(g-(i?e:e/2),o),u.lineTo(g,o),u.strokeStyle=s,u.lineWidth=i?r:r/2,u.stroke(),i&&(u.fillStyle=s,u.font="12px Arial",u.textAlign="right",u.textBaseline="middle",u.fillText(t.toString(),g-1.2*e,o))}this.state.BGCanvas=h}}class _{state;constructor(t){this.state={id:t.id,container:void 0,canvas:null,gl:null,range:t.range??[-20,100]},this.init(t)}updateProps(t){this.state={...this.state,...t}}init(t){let{id:e}=this.state,a=document.getElementById(e),r=document.createElement("canvas"),i=r.getContext("webgl2",{alpha:!0,antialias:!1,premultipliedAlpha:!0,preserveDrawingBuffer:!1});if(!i){console.error("WebGL 2.0 not supported");return}r.style.transform="scaleY(-1)",this.updateProps({container:a??void 0,canvas:r,gl:i}),a?.appendChild(r),this.resize(!1)}compileShader(t,e){let{gl:a}=this.state,r=a.createShader(t);return r?(a.shaderSource(r,e),a.compileShader(r),a.getShaderParameter(r,a.COMPILE_STATUS))?r:(console.error("Shader compile error:",a.getShaderInfoLog(r)),a.deleteShader(r),null):null}createProgram(t){let{gl:e}=this.state,a=this.compileShader(e.VERTEX_SHADER,t.vertex),r=this.compileShader(e.FRAGMENT_SHADER,t.fragment);if(!a||!r)return null;let i=e.createProgram();return i?(e.attachShader(i,a),e.attachShader(i,r),e.linkProgram(i),e.getProgramParameter(i,e.LINK_STATUS))?(e.deleteShader(a),e.deleteShader(r),i):(console.error("Program link error:",e.getProgramInfoLog(i)),e.deleteProgram(i),null):null}createTexture(){let{gl:t}=this.state,e=t.createTexture();return e?(t.bindTexture(t.TEXTURE_2D,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),e):null}createQuadBuffer(){let{gl:t}=this.state,e=t.createBuffer();if(!e)return null;t.bindBuffer(t.ARRAY_BUFFER,e);let a=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);return t.bufferData(t.ARRAY_BUFFER,a,t.STATIC_DRAW),e}clearRect(){let{gl:t}=this.state;t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT)}resize(t=!0){let{canvas:e,container:a,gl:r}=this.state;if(!a)return;let{clientWidth:i,clientHeight:l}=a;i&&l&&(e.width=i,e.height=l,r.viewport(0,0,i,l),t&&setTimeout(()=>{this.draw()}))}draw(){}dispose(){let{gl:t,canvas:e}=this.state;if(t){let e=t.getExtension("WEBGL_lose_context");e&&e.loseContext()}e?.remove()}}let A=`#version 300 es
|
|
1
|
+
let e=new Map;function t(e,t){let a,r,i,n;if(!/^#([A-Fa-f0-9]{3}){1,2}([A-Fa-f0-9]{2})?$/.test(e))throw Error("Invalid hex color format");let l=e.replace("#","");if(3===l.length)a=Number.parseInt(l[0]+l[0],16),r=Number.parseInt(l[1]+l[1],16),i=Number.parseInt(l[2]+l[2],16),n=255;else if(6===l.length)a=Number.parseInt(l.substring(0,2),16),r=Number.parseInt(l.substring(2,4),16),i=Number.parseInt(l.substring(4,6),16),n=255;else if(8===l.length)a=Number.parseInt(l.substring(0,2),16),r=Number.parseInt(l.substring(2,4),16),i=Number.parseInt(l.substring(4,6),16),n=Number.parseInt(l.substring(6,8),16);else throw Error("Invalid hex color length");return"number"==typeof t&&(n=Math.round(255*Math.max(0,Math.min(1,t)))),{r:a,g:r,b:i,a:n}}function a(e,t,r,i=255){let n=e=>e.toString(16).padStart(2,"0"),l=`#${n(e)}${n(t)}${n(r)}`;return i<255?`${l}${n(i)}`:l}function r(t){if("string"==typeof t){let a=e.get(t);if(a)return a;let r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(t),i=r?{r:parseInt(r[1],16),g:parseInt(r[2],16),b:parseInt(r[3],16),a:r[4]?parseInt(r[4],16):255}:{r:0,g:0,b:0,a:255};return e.set(t,i),i}return{r:t.r,g:t.g,b:t.b,a:t.a??255}}function i(e,t=100){let a=[];for(let r=0;r<=t;r++){let i=Math.round(r/t*255).toString(16).padStart(2,"0").toUpperCase(),n=`${e}${i}`;a.push(n)}return a}function n(){e.clear()}class l{colorsPresetList=[];range=[0,0,0];static transparent={r:0,g:0,b:0,a:0};constructor(e,t,a,r=.1){this.setColors(e,t,a,r)}setColors(e,t,a,i){this.colorsPresetList=[];let n=Math.max(1,Math.floor((a-t)/i)),l=a-t;this.range=[t,a,l];let o=e.map(r);for(let e=0;e<=n;e++){let a=(t+e*i-t)/l;this.colorsPresetList.push(this.interpolateColor(o,a))}}interpolateColor(e,t){let r=Math.floor(t*(e.length-1)),i=t*(e.length-1)-r,n=e[r],l=e[r+1]||e[r],o={r:Math.round(n.r+i*(l.r-n.r)),g:Math.round(n.g+i*(l.g-n.g)),b:Math.round(n.b+i*(l.b-n.b)),a:Math.round(n.a+i*(l.a-n.a))};return{...o,hax:a(o.r,o.g,o.b,o.a)}}getColor(e){if(!Number.isFinite(e))return l.transparent;let[t,,a]=this.range,r=(e-t)/a;if(r<=0)return this.colorsPresetList[0];if(r>=1)return this.colorsPresetList[this.colorsPresetList.length-1];let i=Math.min(Math.floor(r*this.colorsPresetList.length),this.colorsPresetList.length-1);return this.colorsPresetList[i]}}let o=(e,t)=>{e.__rfkitCanvasCapture=t},s=(e,t)=>{e.__rfkitCanvasCapture===t&&delete e.__rfkitCanvasCapture},h=(e,t="image/png",a)=>{let r=e.__rfkitCanvasCapture??e.__rfkitHeatmap;return r?.toDataURL(t,a)??null},u={COLOR:"#000000",THICKNESS:1,FILL_STYLE:"#ffffffB0",FILL_STYLE_PRIMARY:"#1890ff",FILL_STYLE_TRANSPARENT_BASE:"#ffffff10",LINE_COLOR:"#00ff00",POINT_COLOR:"#ff0000",DIAL_NORTH_COLOR:"#ff4d4f",DIAL_SOUTH_COLOR:"#1890ff",FLUORESCENCE_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],RANGE:[-20,100]},d={LEVEL_MIN:-20,LEVEL_MAX:140,LEVEL_RANGE:161,MAX_GAUSSIAN_RADIUS:30},c={Y_RANGE:[-1,1],SEGMENT_SIZE:5,MODE:"classic",DECAY_FACTOR:.98,MAX_ACCUMULATION:1e3,MODERN_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],CLASSIC_COLORS:["#00ff00","#ff0000"],CLASSIC_ALPHA_GAIN:50};var f=/*#__PURE__*/function(e){return e.Horizontal="horizontal",e.Vertical="vertical",e}({}),g=/*#__PURE__*/function(e){return e.Circle="circle",e.Rect="rect",e.Line="line",e.Stepline="stepline",e.Bar="bar",e.Area="area",e}({}),p=/*#__PURE__*/function(e){return e.Canvas="canvas",e.WebGL="webgl",e}({}),m=/*#__PURE__*/function(e){return e.Gaussian="gaussian",e.Grid="grid",e}({});function E(e,t,a,r,i){if(r.fill(0),!a)return;let n=a.length;if(0===n)return;let l=t/n,o=0;for(let t=0;t<n;t++){let n=a[t],s=n.length;if(0===s)continue;0===o&&(o=e/s);let h=Math.floor(l*t),u=Math.ceil(l*(t+1));for(let t=0;t<s;t++){let a=n[t],l=Math.floor(o*t),s=Math.ceil(o*(t+1)),d=i(a);for(let t=h;t<u;t++){let a=t*e;for(let e=l;e<s;e++){let t=(a+e)*4;r[t]=d.r,r[t+1]=d.g,r[t+2]=d.b,r[t+3]=d.a}}}}}function T(e){if(!Array.isArray(e)||0===e.length)throw Error("Input must be a non-empty array.");let t=e[0],a=e[0];for(let r=1;r<e.length;r++)e[r]<t?t=e[r]:e[r]>a&&(a=e[r]);return[t,a]}function _(e){return"number"==typeof e&&Number.isFinite(e)}function x(e){return new Float64Array(e.flatMap(t=>0===t.length?Array(e[0].length).fill(Number.NaN):t))}let M=null;function C(){if(null!==M)return M;try{let e=document.createElement("canvas").getContext("webgl2");if(M=!!e,e){let t=e.getExtension("WEBGL_lose_context");t?.loseContext()}}catch{M=!1}return M}let R=256,b=4.5,L=Math.exp(-4.5),v=1.3,A=1/(1-L),y=(e,t,a,r,i)=>{let n=i-r,l=a/n,o=Math.min(Math.max(2,Math.ceil(l),Math.floor(a/50)),d.MAX_GAUSSIAN_RADIUS),s=t/e,h=Math.min(Math.max(o,Math.ceil(s)),d.MAX_GAUSSIAN_RADIUS);return{cellWidth:s,radiusX:h,radiusY:o,rangeSpan:n,xSampleCount:Math.ceil((2*h+2)/s)+2,ySampleCount:Math.ceil((2*o+2)/l)+2}},w=(e,t)=>{if(0===e.length)return{r:255,g:255,b:255,a:255};let a=Math.max(0,Math.min(1,t))*(e.length-1),i=Math.min(Math.floor(a),e.length-1),n=Math.min(i+1,e.length-1),l=a-i,o=r(e[i]),s=r(e[n]);return{r:Math.round(o.r+(s.r-o.r)*l),g:Math.round(o.g+(s.g-o.g)*l),b:Math.round(o.b+(s.b-o.b)*l),a:Math.round(o.a+(s.a-o.a)*l)}},F=e=>{let t=new Uint8Array(1024);for(let a=0;a<256;a++){let r=w(e,a/255),i=4*a;t[i]=r.r,t[i+1]=r.g,t[i+2]=r.b,t[i+3]=255}return t},S=(e,t,a,r)=>{let i=Math.max(0,Math.ceil(a-d.LEVEL_MIN)),n=Math.min(d.LEVEL_RANGE-1,Math.floor(r-d.LEVEL_MIN)),l=0;for(let a=0;a<t;a++){let t=a*d.LEVEL_RANGE;for(let a=i;a<=n;a++)l=Math.max(l,e[t+a])}return Math.max(l,1)};class I{state;constructor(e){this.state={id:e.id,container:void 0,canvas:null,gl:null,range:e.range??[-20,100]},this.init(e)}updateProps(e){this.state={...this.state,...e}}init(e){let{id:t}=this.state,a=document.getElementById(t),r=document.createElement("canvas"),i=r.getContext("webgl2",{alpha:!0,antialias:!1,premultipliedAlpha:!0,preserveDrawingBuffer:!1});if(!i)throw Error("WebGL 2.0 is not available");r.style.transform="scaleY(-1)",this.updateProps({container:a??void 0,canvas:r,gl:i}),a?.appendChild(r),this.resize(!1)}compileShader(e,t){let{gl:a}=this.state,r=a.createShader(e);return r?(a.shaderSource(r,t),a.compileShader(r),a.getShaderParameter(r,a.COMPILE_STATUS))?r:(console.warn("Shader compile error:",a.getShaderInfoLog(r)),a.deleteShader(r),null):null}createProgram(e){let{gl:t}=this.state,a=this.compileShader(t.VERTEX_SHADER,e.vertex),r=this.compileShader(t.FRAGMENT_SHADER,e.fragment);if(!a||!r)return a&&t.deleteShader(a),r&&t.deleteShader(r),null;let i=t.createProgram();return i?(t.attachShader(i,a),t.attachShader(i,r),t.linkProgram(i),t.getProgramParameter(i,t.LINK_STATUS))?(t.deleteShader(a),t.deleteShader(r),i):(console.warn("Program link error:",t.getProgramInfoLog(i)),t.deleteShader(a),t.deleteShader(r),t.deleteProgram(i),null):(t.deleteShader(a),t.deleteShader(r),null)}createTexture(){let{gl:e}=this.state,t=e.createTexture();return t?(e.bindTexture(e.TEXTURE_2D,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),t):null}createQuadBuffer(){let{gl:e}=this.state,t=e.createBuffer();if(!t)return null;e.bindBuffer(e.ARRAY_BUFFER,t);let a=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);return e.bufferData(e.ARRAY_BUFFER,a,e.STATIC_DRAW),t}clearRect(){let{gl:e}=this.state;e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)}resize(e=!0){let{canvas:t,container:a,gl:r}=this.state;if(!a)return;let{clientWidth:i,clientHeight:n}=a;i&&n&&(t.width=i,t.height=n,r.viewport(0,0,i,n),e&&setTimeout(()=>{this.draw()}))}draw(){}dispose(){let{gl:e,canvas:t}=this.state;if(e){let t=e.getExtension("WEBGL_lose_context");t&&t.loseContext()}t?.remove()}}let k=256,D=`#version 300 es
|
|
2
|
+
in vec2 a_position;
|
|
3
|
+
out vec2 v_texCoord;
|
|
4
|
+
|
|
5
|
+
void main() {
|
|
6
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
7
|
+
v_texCoord = a_position * 0.5 + 0.5;
|
|
8
|
+
}
|
|
9
|
+
`,P=`
|
|
10
|
+
const float GAUSSIAN_EDGE_EXPONENT = 4.5;
|
|
11
|
+
const float GAUSSIAN_EDGE_WEIGHT = 0.0111089965;
|
|
12
|
+
|
|
13
|
+
float gaussianWeight(float normalizedDistanceSquared) {
|
|
14
|
+
if (normalizedDistanceSquared >= 1.0) return 0.0;
|
|
15
|
+
float quantized = floor(normalizedDistanceSquared * 255.0) / 255.0;
|
|
16
|
+
return max(
|
|
17
|
+
0.0,
|
|
18
|
+
(exp(-GAUSSIAN_EDGE_EXPONENT * quantized) - GAUSSIAN_EDGE_WEIGHT) /
|
|
19
|
+
(1.0 - GAUSSIAN_EDGE_WEIGHT)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
`,N=`#version 300 es
|
|
23
|
+
precision highp float;
|
|
24
|
+
precision highp int;
|
|
25
|
+
precision highp usampler2D;
|
|
26
|
+
|
|
27
|
+
layout(location = 0) out vec2 horizontalValue;
|
|
28
|
+
|
|
29
|
+
uniform usampler2D u_countTexture;
|
|
30
|
+
uniform int u_dataWidth;
|
|
31
|
+
uniform float u_cellWidth;
|
|
32
|
+
uniform float u_radiusX;
|
|
33
|
+
uniform float u_invLogMaxCount;
|
|
34
|
+
|
|
35
|
+
${P}
|
|
36
|
+
|
|
37
|
+
void main() {
|
|
38
|
+
float pixelX = floor(gl_FragCoord.x);
|
|
39
|
+
int level = int(floor(gl_FragCoord.y));
|
|
40
|
+
int startIndex = max(
|
|
41
|
+
0,
|
|
42
|
+
int(floor((pixelX - u_radiusX) / u_cellWidth)) - 2
|
|
43
|
+
);
|
|
44
|
+
float accumulated = 0.0;
|
|
45
|
+
float coverage = 0.0;
|
|
46
|
+
|
|
47
|
+
for (int offset = 0; offset < 256; offset++) {
|
|
48
|
+
int dataIndex = startIndex + offset;
|
|
49
|
+
if (dataIndex >= u_dataWidth) break;
|
|
50
|
+
|
|
51
|
+
float startCellX = floor(u_cellWidth * float(dataIndex));
|
|
52
|
+
float endCellX = ceil(u_cellWidth * float(dataIndex + 1));
|
|
53
|
+
float centerX = (startCellX + endCellX) * 0.5;
|
|
54
|
+
if (centerX > pixelX + u_radiusX + 1.0) break;
|
|
55
|
+
|
|
56
|
+
float normalizedDistance = (pixelX - centerX) / u_radiusX;
|
|
57
|
+
float weight = gaussianWeight(
|
|
58
|
+
normalizedDistance * normalizedDistance
|
|
59
|
+
);
|
|
60
|
+
if (weight <= 0.0) continue;
|
|
61
|
+
|
|
62
|
+
coverage += weight;
|
|
63
|
+
uint count = texelFetch(
|
|
64
|
+
u_countTexture,
|
|
65
|
+
ivec2(level, dataIndex),
|
|
66
|
+
0
|
|
67
|
+
).r;
|
|
68
|
+
if (count > 0u) {
|
|
69
|
+
float intensity = min(log(float(count) + 1.0) * u_invLogMaxCount, 1.0);
|
|
70
|
+
accumulated += intensity * weight;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
horizontalValue = vec2(accumulated, coverage);
|
|
75
|
+
}
|
|
76
|
+
`,U=`#version 300 es
|
|
77
|
+
precision highp float;
|
|
78
|
+
precision highp int;
|
|
79
|
+
|
|
80
|
+
in vec2 v_texCoord;
|
|
81
|
+
out vec4 fragColor;
|
|
82
|
+
|
|
83
|
+
uniform sampler2D u_horizontalTexture;
|
|
84
|
+
uniform sampler2D u_colorTexture;
|
|
85
|
+
uniform int u_canvasWidth;
|
|
86
|
+
uniform int u_canvasHeight;
|
|
87
|
+
uniform float u_rangeMin;
|
|
88
|
+
uniform float u_rangeMax;
|
|
89
|
+
uniform float u_radiusY;
|
|
90
|
+
uniform float u_intensityGain;
|
|
91
|
+
|
|
92
|
+
${P}
|
|
93
|
+
|
|
94
|
+
void main() {
|
|
95
|
+
int pixelX = clamp(
|
|
96
|
+
int(floor(gl_FragCoord.x)),
|
|
97
|
+
0,
|
|
98
|
+
u_canvasWidth - 1
|
|
99
|
+
);
|
|
100
|
+
int pixelY = clamp(
|
|
101
|
+
int(floor((1.0 - v_texCoord.y) * float(u_canvasHeight))),
|
|
102
|
+
0,
|
|
103
|
+
u_canvasHeight - 1
|
|
104
|
+
);
|
|
105
|
+
float rangeSpan = u_rangeMax - u_rangeMin;
|
|
106
|
+
float heightMinusOne = max(float(u_canvasHeight - 1), 1.0);
|
|
107
|
+
float sourceLevel =
|
|
108
|
+
u_rangeMin + (float(pixelY) / heightMinusOne) * rangeSpan - ${d.LEVEL_MIN}.0;
|
|
109
|
+
float levelRadius = u_radiusY * rangeSpan / heightMinusOne;
|
|
110
|
+
int startLevel = max(0, int(floor(sourceLevel - levelRadius)) - 2);
|
|
111
|
+
float accumulated = 0.0;
|
|
112
|
+
float yCoverage = 0.0;
|
|
113
|
+
|
|
114
|
+
for (int offset = 0; offset < ${d.LEVEL_RANGE}; offset++) {
|
|
115
|
+
int level = startLevel + offset;
|
|
116
|
+
if (level >= ${d.LEVEL_RANGE}) break;
|
|
117
|
+
|
|
118
|
+
float yValue = float(level + ${d.LEVEL_MIN});
|
|
119
|
+
if (yValue < u_rangeMin || yValue > u_rangeMax) continue;
|
|
120
|
+
float centerY =
|
|
121
|
+
((yValue - u_rangeMin) / rangeSpan) * heightMinusOne;
|
|
122
|
+
if (centerY > float(pixelY) + u_radiusY + 1.0) break;
|
|
123
|
+
|
|
124
|
+
float normalizedDistance = (float(pixelY) - centerY) / u_radiusY;
|
|
125
|
+
float weight = gaussianWeight(
|
|
126
|
+
normalizedDistance * normalizedDistance
|
|
127
|
+
);
|
|
128
|
+
if (weight <= 0.0) continue;
|
|
129
|
+
|
|
130
|
+
vec2 horizontal = texelFetch(
|
|
131
|
+
u_horizontalTexture,
|
|
132
|
+
ivec2(pixelX, level),
|
|
133
|
+
0
|
|
134
|
+
).rg;
|
|
135
|
+
accumulated += horizontal.r * weight;
|
|
136
|
+
yCoverage += weight;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
float xCoverage = texelFetch(
|
|
140
|
+
u_horizontalTexture,
|
|
141
|
+
ivec2(pixelX, 0),
|
|
142
|
+
0
|
|
143
|
+
).g;
|
|
144
|
+
float denominator = xCoverage * yCoverage;
|
|
145
|
+
if (denominator <= 0.0) {
|
|
146
|
+
fragColor = vec4(0.0);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Match Canvas by normalizing against potential sample coverage.
|
|
151
|
+
float intensity = min(
|
|
152
|
+
(accumulated / denominator) * u_intensityGain,
|
|
153
|
+
1.0
|
|
154
|
+
);
|
|
155
|
+
if (intensity <= 0.001) {
|
|
156
|
+
fragColor = vec4(0.0);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
int colorIndex = min(
|
|
161
|
+
int(floor(intensity * float(255))),
|
|
162
|
+
255
|
|
163
|
+
);
|
|
164
|
+
vec3 color = texelFetch(u_colorTexture, ivec2(colorIndex, 0), 0).rgb;
|
|
165
|
+
fragColor = vec4(color * intensity, intensity);
|
|
166
|
+
}
|
|
167
|
+
`,G=`#version 300 es
|
|
168
|
+
precision highp float;
|
|
169
|
+
precision highp int;
|
|
170
|
+
precision highp usampler2D;
|
|
171
|
+
|
|
172
|
+
in vec2 v_texCoord;
|
|
173
|
+
out vec4 fragColor;
|
|
174
|
+
|
|
175
|
+
uniform usampler2D u_countTexture;
|
|
176
|
+
uniform sampler2D u_colorTexture;
|
|
177
|
+
uniform int u_dataWidth;
|
|
178
|
+
uniform int u_canvasWidth;
|
|
179
|
+
uniform int u_canvasHeight;
|
|
180
|
+
uniform float u_rangeMin;
|
|
181
|
+
uniform float u_rangeMax;
|
|
182
|
+
uniform float u_invLogMaxCount;
|
|
183
|
+
|
|
184
|
+
void main() {
|
|
185
|
+
int pixelX = clamp(
|
|
186
|
+
int(floor(gl_FragCoord.x)),
|
|
187
|
+
0,
|
|
188
|
+
u_canvasWidth - 1
|
|
189
|
+
);
|
|
190
|
+
int pixelY = clamp(
|
|
191
|
+
int(floor((1.0 - v_texCoord.y) * float(u_canvasHeight))),
|
|
192
|
+
0,
|
|
193
|
+
u_canvasHeight - 1
|
|
194
|
+
);
|
|
195
|
+
float canvasWidth = float(u_canvasWidth);
|
|
196
|
+
float canvasHeight = float(u_canvasHeight);
|
|
197
|
+
float dataWidth = float(u_dataWidth);
|
|
198
|
+
int blockWidth = max(1, int(floor(canvasWidth / dataWidth)));
|
|
199
|
+
int baseDataIndex = int(floor(float(pixelX) * dataWidth / canvasWidth));
|
|
200
|
+
int dataIndex = -1;
|
|
201
|
+
|
|
202
|
+
for (int candidateOffset = -2; candidateOffset <= 2; candidateOffset++) {
|
|
203
|
+
int candidate = baseDataIndex + candidateOffset;
|
|
204
|
+
if (candidate < 0 || candidate >= u_dataWidth) continue;
|
|
205
|
+
int startX = int(floor(float(candidate) * canvasWidth / dataWidth));
|
|
206
|
+
int endX = min(u_canvasWidth - 1, startX + blockWidth - 1);
|
|
207
|
+
if (pixelX >= startX && pixelX <= endX) {
|
|
208
|
+
dataIndex = candidate;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (dataIndex < 0) {
|
|
214
|
+
fragColor = vec4(0.0);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
float rangeSpan = u_rangeMax - u_rangeMin;
|
|
219
|
+
float heightMinusOne = max(canvasHeight - 1.0, 1.0);
|
|
220
|
+
float yAtPixel =
|
|
221
|
+
u_rangeMin + (float(pixelY) / heightMinusOne) * rangeSpan;
|
|
222
|
+
int baseLevel = int(floor(yAtPixel - ${d.LEVEL_MIN}.0));
|
|
223
|
+
int blockHeight = max(1, int(ceil(canvasHeight / rangeSpan)));
|
|
224
|
+
int level = -1;
|
|
225
|
+
|
|
226
|
+
for (int candidateOffset = -3; candidateOffset <= 3; candidateOffset++) {
|
|
227
|
+
int candidate = baseLevel + candidateOffset;
|
|
228
|
+
if (candidate < 0 || candidate >= ${d.LEVEL_RANGE}) continue;
|
|
229
|
+
float yValue = float(candidate + ${d.LEVEL_MIN});
|
|
230
|
+
if (yValue < u_rangeMin || yValue > u_rangeMax) continue;
|
|
231
|
+
float centerY = ((yValue - u_rangeMin) / rangeSpan) * heightMinusOne;
|
|
232
|
+
int startY = max(0, int(floor(centerY - float(blockHeight) * 0.5)));
|
|
233
|
+
int endY = min(u_canvasHeight - 1, startY + blockHeight - 1);
|
|
234
|
+
if (pixelY >= startY && pixelY <= endY) {
|
|
235
|
+
level = candidate;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (level < 0) {
|
|
241
|
+
fragColor = vec4(0.0);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
uint count = texelFetch(
|
|
246
|
+
u_countTexture,
|
|
247
|
+
ivec2(level, dataIndex),
|
|
248
|
+
0
|
|
249
|
+
).r;
|
|
250
|
+
if (count == 0u) {
|
|
251
|
+
fragColor = vec4(0.0);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
float intensity = min(log(float(count) + 1.0) * u_invLogMaxCount, 1.0);
|
|
256
|
+
int colorIndex = min(
|
|
257
|
+
int(floor(intensity * float(255))),
|
|
258
|
+
255
|
|
259
|
+
);
|
|
260
|
+
vec3 color = texelFetch(u_colorTexture, ivec2(colorIndex, 0), 0).rgb;
|
|
261
|
+
fragColor = vec4(color * intensity, intensity);
|
|
262
|
+
}
|
|
263
|
+
`,O=(e,t,a)=>{let r=e.getUniformLocation(t,a);if(!r)throw Error(`Missing WebGL uniform: ${a}`);return r},X=(e,t)=>{let a=e.getAttribLocation(t,"a_position");if(a<0)throw Error("Missing WebGL attribute: a_position");return a};class W extends I{fallbackHandler;disposed=!1;init(e){super.init(e);let{canvas:t,gl:a}=this.state;if(!a)throw Error("WebGL 2.0 is not available");let r=null,i=null,n=null,l=null,s=null,h=null,d=null,c=null;try{if(!a.getExtension("EXT_color_buffer_float"))throw Error("EXT_color_buffer_float is not available");r=this.createProgram({vertex:D,fragment:N}),i=this.createProgram({vertex:D,fragment:U}),n=this.createProgram({vertex:D,fragment:G}),l=this.createQuadBuffer(),s=this.createTexture(),h=this.createTexture(),d=this.createTexture(),c=a.createFramebuffer();let f=a.getParameter(a.MAX_TEXTURE_SIZE);if(!r||!i||!n||!l||!s||!h||!d||!c||!Number.isFinite(f)||f<=0)throw Error("Failed to allocate fluorescence WebGL resources");let g={position:X(a,r),countTexture:O(a,r,"u_countTexture"),dataWidth:O(a,r,"u_dataWidth"),cellWidth:O(a,r,"u_cellWidth"),radiusX:O(a,r,"u_radiusX"),invLogMaxCount:O(a,r,"u_invLogMaxCount")},p={position:X(a,i),horizontalTexture:O(a,i,"u_horizontalTexture"),colorTexture:O(a,i,"u_colorTexture"),canvasWidth:O(a,i,"u_canvasWidth"),canvasHeight:O(a,i,"u_canvasHeight"),rangeMin:O(a,i,"u_rangeMin"),rangeMax:O(a,i,"u_rangeMax"),radiusY:O(a,i,"u_radiusY"),intensityGain:O(a,i,"u_intensityGain")},E={position:X(a,n),countTexture:O(a,n,"u_countTexture"),colorTexture:O(a,n,"u_colorTexture"),dataWidth:O(a,n,"u_dataWidth"),canvasWidth:O(a,n,"u_canvasWidth"),canvasHeight:O(a,n,"u_canvasHeight"),rangeMin:O(a,n,"u_rangeMin"),rangeMax:O(a,n,"u_rangeMax"),invLogMaxCount:O(a,n,"u_invLogMaxCount")};super.updateProps({colors:e.colors&&e.colors.length>0?e.colors:u.FLUORESCENCE_COLORS,data:new Uint32Array(0),display:e.display,fluorescenceMaxCount:e.fluorescenceMaxCount??1,renderMode:e.fluorescenceRenderMode??m.Gaussian,maxTextureSize:f,quadBuffer:l,countTexture:s,colorTexture:h,horizontalTexture:d,horizontalFramebuffer:c,horizontalProgram:r,gaussianProgram:i,gridProgram:n,horizontalLocations:g,gaussianLocations:p,gridLocations:E}),this.updateColorTexture(),this.resize(!1),o(t,this)}catch(e){throw c&&a.deleteFramebuffer(c),d&&a.deleteTexture(d),h&&a.deleteTexture(h),s&&a.deleteTexture(s),l&&a.deleteBuffer(l),n&&a.deleteProgram(n),i&&a.deleteProgram(i),r&&a.deleteProgram(r),t.remove(),e}}setFallbackHandler(e){this.state.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.fallbackHandler=e,this.state.canvas.addEventListener("webglcontextlost",this.handleContextLost)}handleContextLost=e=>{e.preventDefault(),this.disposed||this.requestFallback(Error("Fluorescence WebGL context lost"))};requestFallback(e){if(this.fallbackHandler){this.fallbackHandler(e);return}throw e instanceof Error?e:Error(String(e))}updateColorTexture(){let{colorTexture:e,colors:t,gl:a}=this.state,r=F(t);a.bindTexture(a.TEXTURE_2D,e),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.NEAREST),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.NEAREST),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,256,1,0,a.RGBA,a.UNSIGNED_BYTE,r)}rebuildHorizontalTarget(){let{canvas:e,gl:t,horizontalFramebuffer:a,horizontalTexture:r,maxTextureSize:i}=this.state,{width:n}=e;if(n<=0)return;if(n>i||d.LEVEL_RANGE>i)throw Error("Fluorescence WebGL render target exceeds texture limits");t.bindTexture(t.TEXTURE_2D,r),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),t.texImage2D(t.TEXTURE_2D,0,t.RG32F,n,d.LEVEL_RANGE,0,t.RG,t.FLOAT,null),t.bindFramebuffer(t.FRAMEBUFFER,a),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,r,0);let l=t.checkFramebufferStatus(t.FRAMEBUFFER);if(t.bindFramebuffer(t.FRAMEBUFFER,null),l!==t.FRAMEBUFFER_COMPLETE)throw Error(`Fluorescence WebGL framebuffer incomplete: ${l}`)}uploadCounts(e,t){let{countTexture:a,gl:r,maxTextureSize:i}=this.state;if(t>i)throw Error("Fluorescence data exceeds WebGL texture limits");r.bindTexture(r.TEXTURE_2D,a),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.NEAREST),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.NEAREST),r.texImage2D(r.TEXTURE_2D,0,r.R32UI,d.LEVEL_RANGE,t,0,r.RED_INTEGER,r.UNSIGNED_INT,e)}bindQuad(e,t){let{gl:a,quadBuffer:r}=this.state;a.useProgram(e),a.bindBuffer(a.ARRAY_BUFFER,r),a.enableVertexAttribArray(t),a.vertexAttribPointer(t,2,a.FLOAT,!1,0,0)}drawGaussian(e,t,a){let{canvas:r,colorTexture:i,countTexture:n,fluorescenceMaxCount:l,gaussianLocations:o,gaussianProgram:s,gl:h,horizontalFramebuffer:u,horizontalLocations:c,horizontalProgram:f,horizontalTexture:g}=this.state,{width:p,height:m}=r,E=y(e,p,m,t,a);if(E.xSampleCount>256)throw Error("Fluorescence Gaussian kernel exceeds shader limits");let T=1/Math.log1p(Math.max(l,1));h.bindFramebuffer(h.FRAMEBUFFER,u),h.viewport(0,0,p,d.LEVEL_RANGE),this.bindQuad(f,c.position),h.activeTexture(h.TEXTURE0),h.bindTexture(h.TEXTURE_2D,n),h.uniform1i(c.countTexture,0),h.uniform1i(c.dataWidth,e),h.uniform1f(c.cellWidth,E.cellWidth),h.uniform1f(c.radiusX,E.radiusX),h.uniform1f(c.invLogMaxCount,T),h.drawArrays(h.TRIANGLES,0,6),h.bindFramebuffer(h.FRAMEBUFFER,null),h.viewport(0,0,p,m),this.bindQuad(s,o.position),h.activeTexture(h.TEXTURE0),h.bindTexture(h.TEXTURE_2D,g),h.uniform1i(o.horizontalTexture,0),h.activeTexture(h.TEXTURE1),h.bindTexture(h.TEXTURE_2D,i),h.uniform1i(o.colorTexture,1),h.uniform1i(o.canvasWidth,p),h.uniform1i(o.canvasHeight,m),h.uniform1f(o.rangeMin,t),h.uniform1f(o.rangeMax,a),h.uniform1f(o.radiusY,E.radiusY),h.uniform1f(o.intensityGain,1.3),h.drawArrays(h.TRIANGLES,0,6)}drawGrid(e,t,a){let{canvas:r,colorTexture:i,countTexture:n,data:l,gl:o,gridLocations:s,gridProgram:h}=this.state,u=1/Math.log1p(S(l,e,t,a));o.bindFramebuffer(o.FRAMEBUFFER,null),o.viewport(0,0,r.width,r.height),this.bindQuad(h,s.position),o.activeTexture(o.TEXTURE0),o.bindTexture(o.TEXTURE_2D,n),o.uniform1i(s.countTexture,0),o.activeTexture(o.TEXTURE1),o.bindTexture(o.TEXTURE_2D,i),o.uniform1i(s.colorTexture,1),o.uniform1i(s.dataWidth,e),o.uniform1i(s.canvasWidth,r.width),o.uniform1i(s.canvasHeight,r.height),o.uniform1f(s.rangeMin,t),o.uniform1f(s.rangeMax,a),o.uniform1f(s.invLogMaxCount,u),o.drawArrays(o.TRIANGLES,0,6)}updateProps(e){let t=void 0!==e.colors,a=e.data,r=a?a.length/d.LEVEL_RANGE:0,i=void 0===a||0===a.length||Number.isInteger(r)&&r>0,n=void 0!==e.range||void 0!==e.renderMode||void 0!==e.display||t,{data:l,...o}=e;super.updateProps({...o,...i&&void 0!==a?{data:a}:{}});try{a&&i&&this.uploadCounts(a,r),t&&this.updateColorTexture(),n&&this.state.data.length>0&&this.draw()}catch(e){this.requestFallback(e)}}setRange(e){e&&this.updateProps({range:e})}setRenderMode(e){this.updateProps({renderMode:e})}clear(){let{canvas:e,gl:t}=this.state;this.state.data=new Uint32Array(0),t.bindFramebuffer(t.FRAMEBUFFER,null),t.viewport(0,0,e.width,e.height),this.clearRect()}render(e){let t=e?.fluorescenceData;if(!t||0===t.length){this.clear();return}let a=t.length/d.LEVEL_RANGE;if(Number.isInteger(a)&&!(a<=0))try{this.state.data=t,this.state.fluorescenceMaxCount=e.fluorescenceMaxCount,this.uploadCounts(t,a),this.draw()}catch(e){this.requestFallback(e)}}draw(){let{canvas:e,data:t,display:a,gl:r,range:i=u.RANGE,renderMode:n}=this.state;if(!a||0===t.length||0===e.width||0===e.height)return;let l=t.length/d.LEVEL_RANGE;if(!Number.isInteger(l)||l<=0)return;let[o,s]=i;if(!(s<=o))try{r.bindFramebuffer(r.FRAMEBUFFER,null),r.viewport(0,0,e.width,e.height),r.clearColor(0,0,0,0),r.clear(r.COLOR_BUFFER_BIT),n===m.Grid?this.drawGrid(l,o,s):this.drawGaussian(l,o,s)}catch(e){this.requestFallback(e)}}resize(e=!0){try{if(super.resize(!1),!this.state.horizontalTexture)return;this.rebuildHorizontalTarget(),e&&this.draw()}catch(e){this.requestFallback(e)}}toDataURL(e="image/png",t){this.draw();let{canvas:a}=this.state,r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i?(i.translate(0,a.height),i.scale(1,-1),i.drawImage(a,0,0),r.toDataURL(e,t)):null}dispose(){if(this.disposed)return;this.disposed=!0;let{canvas:e,colorTexture:t,countTexture:a,gaussianProgram:r,gl:i,gridProgram:n,horizontalFramebuffer:l,horizontalProgram:o,horizontalTexture:h,quadBuffer:u}=this.state;s(e,this),e?.removeEventListener("webglcontextlost",this.handleContextLost),i&&(i.deleteProgram(o),i.deleteProgram(r),i.deleteProgram(n),i.deleteBuffer(u),i.deleteTexture(a),i.deleteTexture(t),i.deleteTexture(h),i.deleteFramebuffer(l)),this.fallbackHandler=void 0,super.dispose()}}class H{state;constructor(e){this.state={id:e.id,container:void 0,canvas:null,ctx:null,range:e.range??[-20,100]},this.init(e)}updateProps(e){this.state={...this.state,...e}}init(e){let{id:t}=this.state,a=document.getElementById(t),r=document.createElement("canvas"),i=r.getContext?.("2d");r.style.transform="scaleY(-1)",i&&(i.imageSmoothingEnabled=!1,i.lineJoin="miter",i.lineCap="butt",i.textBaseline="middle"),i&&this.updateProps({container:a??void 0,canvas:r,ctx:i}),a?.appendChild(r),this.resize(!1)}clearRect(){let{ctx:e,canvas:t}=this.state;e.clearRect(0,0,t.width,t.height)}resize(e=!0){let{canvas:t,container:a}=this.state,{clientWidth:r,clientHeight:i}=a;r&&i&&(t.width=r,t.height=i,e&&setTimeout(()=>{this.draw()}))}draw(){}dispose(){let{canvas:e,container:t}=this.state;t?.removeChild(e)}}let B=256,z=new Float32Array(256);for(let e=0;e<256;e++){let t=e/255;z[e]=Math.max(0,(Math.exp(-4.5*t)-L)*A)}class V extends H{init(e){super.init(e);let{colors:t,display:a,fluorescenceRenderMode:r}=e;this.updateProps({colors:t,data:new Uint32Array(0),display:a,renderMode:r??m.Gaussian}),this.resize()}updateProps(e){void 0!==e.colors&&(this.colorLookupCache=null);let t=e.range&&(e.range[0]!==this.state.range?.[0]||e.range[1]!==this.state.range?.[1]),a=void 0!==e.renderMode&&e.renderMode!==this.state.renderMode;super.updateProps(e),(t||a)&&this.state.data?.length>0&&this.draw()}setRenderMode(e){this.updateProps({renderMode:e})}clearImageData(){let{ctx:e,canvas:{height:t,width:a}}=this.state;t&&a&&this.updateProps({imageData:e.createImageData(a,t)})}clearRect(){super.clearRect(),this.clearImageData()}clear(){this.clearRect(),this.updateProps({data:new Uint32Array(0)})}dispose(){this.intensityMatrixCache=null,this.gaussianGeometryCache=null,this.colorLookupCache=null,this.colorCache.clear(),this.blockPositionsCache=null,this.lastBlockRenderParams=null,super.dispose()}drawBlocks(){let{imageData:e,data:t,canvas:a,ctx:r,colors:i=u.FLUORESCENCE_COLORS,range:n=u.RANGE}=this.state;if(!t||0===t.length||!e)return;let{width:l,height:o}=a,[s,h]=n,c=h-s;if(c<=0)return;let f=t.length/d.LEVEL_RANGE;if(!Number.isInteger(f)||f<=0)return;let g={dataLength:f,width:l,height:o,rangeMin:s,rangeMax:h};if(!this.lastBlockRenderParams||Object.keys(g).some(e=>this.lastBlockRenderParams?.[e]!==g[e])||!this.blockPositionsCache||this.blockPositionsCache.length!==f){this.blockPositionsCache=Array(f);let e=Math.max(1,Math.floor(l/f));for(let t=0;t<f;t++){let a=Math.floor(t*l/f),r=Math.min(l-1,a+e-1);this.blockPositionsCache[t]={startX:a,endX:r,width:r-a+1}}}this.lastBlockRenderParams=g;let p=e.data;p.fill(0);let m=1/c,E=o-1,T=Math.max(1,Math.ceil(o/c)),_=0;for(let e=0;e<f;e++){let a=e*d.LEVEL_RANGE;for(let e=0;e<d.LEVEL_RANGE;e++){let r=t[a+e];if(r<=0)continue;let i=e+d.LEVEL_MIN;i<s||i>h||!(r>_)||(_=r)}}let x=Math.log(Math.max(_,1)+1);for(let e=0;e<f;e++){let{startX:a,endX:r}=this.blockPositionsCache[e],n=e*d.LEVEL_RANGE;for(let e=0;e<d.LEVEL_RANGE;e++){let o=t[n+e];if(o<=0)continue;let u=e+d.LEVEL_MIN;if(u<s||u>h)continue;let c=Math.max(0,Math.floor((u-s)*m*E-T/2)),f=Math.min(E,c+T-1),g=Math.log(o+1)/x,_=Math.round(255*g),{r:M,g:C,b:R}=this.interpolateColor(i,g);for(let e=c;e<=f;e++){let t=e*l;for(let e=a;e<=r;e++){let a=(t+e)*4;p[a]=M,p[a+1]=C,p[a+2]=R,p[a+3]=_}}}}r.putImageData(e,0,0)}resize(){super.resize(),this.clearImageData()}setRange(e){e&&this.updateProps({range:e})}render(e){if(e?.fluorescenceData?.length>=0){if(0===e.fluorescenceData.length){this.state.fluorescenceMaxCount=e.fluorescenceMaxCount,this.clear();return}(this.state.data?.length??0)!==e.fluorescenceData.length&&(this.clearImageData(),this.intensityMatrixCache=null,this.gaussianGeometryCache=null,this.blockPositionsCache=null,this.lastBlockRenderParams=null),this.state.data=e.fluorescenceData,this.state.fluorescenceMaxCount=e.fluorescenceMaxCount,this.draw()}}interpolateColor(e,t){if(!e||0===e.length)return{r:255,g:255,b:255,a:255};t=Math.max(0,Math.min(1,t));let a=e=>"string"==typeof e?this.hexToRgb(e):{r:e.r,g:e.g,b:e.b};if(1===e.length||0===t)return{...a(e[0]),a:255};if(1===t)return{...a(e[e.length-1]),a:255};let r=t*(e.length-1),i=Math.floor(r),n=r-i,l=Math.min(i,e.length-2),o=a(e[l]),s=a(e[l+1]);return{r:Math.round(o.r+(s.r-o.r)*n),g:Math.round(o.g+(s.g-o.g)*n),b:Math.round(o.b+(s.b-o.b)*n),a:255}}colorCache=new Map;hexToRgb(e){let t=this.colorCache.get(e);if(t)return t;let a=r(e),i={r:a.r,g:a.g,b:a.b};return this.colorCache.set(e,i),i}intensityMatrixCache=null;gaussianGeometryCache=null;colorLookupCache=null;blockPositionsCache=null;lastBlockRenderParams=null;prepareGaussianGeometry(e,t,a,r,i){let n=this.gaussianGeometryCache;if(n&&n.dataLength===e&&n.width===t&&n.height===a&&n.rangeMin===r&&n.rangeMax===i)return n;let{cellWidth:l,radiusX:o,radiusY:s,rangeSpan:h}=y(e,t,a,r,i),u=2*o+2,c=2*s+2,f=new Int32Array(e),g=new Uint8Array(e),p=new Float32Array(e*u),m=new Float32Array(t),E=new Int32Array(d.LEVEL_RANGE),T=new Uint8Array(d.LEVEL_RANGE),_=new Float32Array(d.LEVEL_RANGE*c),x=new Float32Array(a);for(let a=0;a<e;a++){let e=(Math.floor(l*a)+Math.ceil(l*(a+1)))/2,r=Math.max(0,Math.floor(e-o)),i=Math.min(t-1,Math.ceil(e+o))-r+1,n=a*u;f[a]=r,g[a]=i;for(let t=0;t<i;t++){let a=r+t,i=(a-e)/o,l=i*i;if(l>=1)continue;let s=z[Math.floor(255*l)];p[n+t]=s,m[a]+=s}}for(let e=0;e<d.LEVEL_RANGE;e++){let t=e+d.LEVEL_MIN;if(t<r||t>i)continue;let n=(t-r)/h*(a-1),l=Math.max(0,Math.floor(n-s)),o=Math.min(a-1,Math.ceil(n+s))-l+1,u=e*c;E[e]=l,T[e]=o;for(let e=0;e<o;e++){let t=l+e,a=(t-n)/s,r=a*a;if(r>=1)continue;let i=z[Math.floor(255*r)];_[u+e]=i,x[t]+=i}}let M={dataLength:e,height:a,rangeMax:i,rangeMin:r,width:t,xCoverage:m,xLengths:g,xStarts:f,xStride:u,xWeights:p,yCoverage:x,yLengths:T,yStarts:E,yStride:c,yWeights:_};return this.gaussianGeometryCache=M,M}draw(){let{imageData:e,data:t,canvas:a,ctx:r,colors:i=u.FLUORESCENCE_COLORS,range:n=u.RANGE,fluorescenceMaxCount:l=1,display:o,renderMode:s=m.Gaussian}=this.state;if(!o||!t||0===t.length||!e)return;if(s===m.Grid){this.drawBlocks();return}let{width:h,height:c}=a,[f,g]=n;if(g-f<=0)return;let p=t.length/d.LEVEL_RANGE;if(!Number.isInteger(p)||p<=0)return;let E=!this.colorLookupCache,T=e.data;T.fill(0);let _=1/Math.log1p(Math.max(l,1)),x=h*c;this.intensityMatrixCache&&this.intensityMatrixCache.length===x?this.intensityMatrixCache.fill(0):this.intensityMatrixCache=new Float32Array(x);let M=this.intensityMatrixCache,{xCoverage:C,xLengths:R,xStarts:b,xStride:L,xWeights:v,yCoverage:A,yLengths:y,yStarts:w,yStride:F,yWeights:S}=this.prepareGaussianGeometry(p,h,c,f,g);for(let e=0;e<p;e++){let a=e*d.LEVEL_RANGE,r=b[e],i=R[e],n=e*L;for(let e=0;e<d.LEVEL_RANGE;e++){let l=t[a+e];if(l<=0)continue;let o=y[e];if(0===o)continue;let s=Math.min(Math.log1p(l)*_,1),u=w[e],d=e*F;for(let e=0;e<o;e++){let t=S[d+e];if(0===t)continue;let a=(u+e)*h,l=s*t;for(let e=0;e<i;e++){let t=v[n+e];if(0===t)continue;let i=a+r+e;M[i]+=l*t}}}}if(E||!this.colorLookupCache||256!==this.colorLookupCache.length){this.colorLookupCache=Array(256);for(let e=0;e<256;e++){let t=e/255;this.colorLookupCache[e]=this.interpolateColor(i,t)}}let I=this.colorLookupCache;for(let e=0;e<c;e++){let t=A[e];if(t<=0)continue;let a=e*h;for(let e=0;e<h;e++){let r=a+e,i=M[r];if(i<=0)continue;let n=C[e]*t;if(n<=0)continue;let l=Math.min(i/n*1.3,1);if(l<=.001)continue;let o=I[Math.min(Math.floor(255*l),255)],s=4*r;T[s]=o.r,T[s+1]=o.g,T[s+2]=o.b,T[s+3]=Math.round(255*l)}}r.putImageData(e,0,0)}}class Y{backend;latestData=null;props;fallingBack=!1;disposed=!1;constructor(e){this.init(e)}get state(){return this.backend.state}init(e){this.backend?.dispose(),this.props={...e},this.latestData=null,this.fallingBack=!1,this.disposed=!1,this.backend=this.createBackend(e)}createBackend(e){if((e.renderer??p.WebGL)===p.WebGL){if(C())try{let t=new W(e);return t.setFallbackHandler(e=>this.fallbackToCanvas(e)),t}catch(e){console.warn("[Fluorescence] WebGL initialization failed, falling back to Canvas",e)}else console.warn("[Fluorescence] WebGL 2.0 not supported, falling back to Canvas")}return new V(e)}fallbackToCanvas(e){if(!this.disposed&&!this.fallingBack&&!(this.backend instanceof V)){this.fallingBack=!0;try{let{colors:t,display:a,range:r,renderMode:i}=this.backend.state;console.warn("[Fluorescence] WebGL rendering failed, falling back to Canvas",e),this.backend.dispose(),this.props={...this.props,colors:t,display:a,fluorescenceRenderMode:i,range:r,renderer:p.Canvas},this.backend=new V(this.props),this.latestData&&this.backend.render(this.latestData)}finally{this.fallingBack=!1}}}updateProps(e){void 0!==e.colors&&(this.props.colors=e.colors),void 0!==e.display&&(this.props.display=e.display),void 0!==e.range&&(this.props.range=e.range),void 0!==e.renderMode&&(this.props.fluorescenceRenderMode=e.renderMode);let t=e.data??this.backend.state.data;(void 0!==e.data||void 0!==e.fluorescenceMaxCount)&&(this.latestData=0===t.length?null:{fluorescenceData:t,fluorescenceMaxCount:e.fluorescenceMaxCount??this.backend.state.fluorescenceMaxCount}),this.backend.updateProps(e)}render(e){this.latestData=e,this.backend.render(e)}setRange(e){this.props.range=e,this.backend.setRange(e)}setRenderMode(e){this.props.fluorescenceRenderMode=e,this.backend.setRenderMode(e)}draw(){this.backend.draw()}clearImageData(){this.backend instanceof V&&this.backend.clearImageData()}clearRect(){this.backend.clearRect()}clear(){this.latestData=null,this.backend.clear()}resize(){this.backend.resize()}interpolateColor(e,t){return{...w(e,t),a:255}}hexToRgb(e){let{r:t,g:a,b:i}=r(e);return{r:t,g:a,b:i}}dispose(){this.disposed||(this.disposed=!0,this.latestData=null,this.backend.dispose())}}let $=e=>new Y(e);class q extends H{init(e){super.init(e),this.state.canvas.style.transform="scaleY(1)";let t=e.fillStyle??u.FILL_STYLE,a=e.fillStylePrimary??u.FILL_STYLE_PRIMARY,r=e.fillStyleTransparentBase??u.FILL_STYLE_TRANSPARENT_BASE;this.updateProps({fillStyle:t,fillStylePrimary:a,fillStyleTransparentBase:r,baseWidth:6,padding:e.padding??0,step:e.ticksStep??10,lineWidth:1,data:{value:0,limit:0}})}clear(){this.clearRect();let{ctx:e,BGCanvas:t}=this.state;t&&e.drawImage(t,0,0)}resize(){super.resize(),this.state.lineWidth&&this.drawBackground()}render(e){this.state.data={...this.state.data,...e},this.draw()}draw(){let{data:{value:e},range:[t,a],canvas:r,ctx:i,baseWidth:n,padding:l,BGCanvas:o,fillStylePrimary:s}=this.state;this.clearRect(),o&&i.drawImage(o,0,0);let h=3*n,u=r.height-2*l,d=(r.width-h)/2,c=(e-t)/(a-t)*u;i.fillStyle=s,i.fillRect(d,u-c+l,h,c),this.drawLimit()}drawLimit(){let{data:{limit:e},range:[t,a],canvas:r,ctx:i,baseWidth:n,padding:l,lineWidth:o,fillStylePrimary:s}=this.state;if(!_(e))return;let h=3*n,u=r.height-2*l,d=(r.width-h)/2,c=u*(a-e)/(a-t)+l;i.beginPath(),i.moveTo(d+h,c),i.lineTo(d+h+9,c-9),i.lineTo(d+h+9+30,c-9),i.lineTo(d+h+9+30,c+9),i.lineTo(d+h+9,c+9),i.lineTo(d+h,c),i.lineTo(d,c),i.lineWidth=o,i.strokeStyle=s,i.stroke(),i.fillStyle=s,i.font="12px Arial",i.textAlign="center",i.textBaseline="middle",i.fillText(e.toString(),d+h+9+15,c+1)}drawBackground(){let{canvas:e,baseWidth:t,padding:a,lineWidth:r,step:i,range:[n,l],fillStyle:o,fillStyleTransparentBase:s}=this.state,h=document.createElement("canvas");h.width=e.width,h.height=e.height;let u=h.getContext("2d");if(!u)return;let d=3*t,c=e.height-2*a,f=(e.width-d)/2,g=Array.from({length:11},(e,t)=>n+Math.round(t*(l-n)/10/i)*i);u.fillStyle=s,u.fillRect(f,a,d,c);for(let e=n;e<=l;e+=i){let i=g.includes(e),s=c*(l-e)/(l-n)+a;u.beginPath(),u.moveTo(f-(i?t:t/2),s),u.lineTo(f,s),u.strokeStyle=o,u.lineWidth=i?r:r/2,u.stroke(),i&&(u.fillStyle=o,u.font="12px Arial",u.textAlign="right",u.textBaseline="middle",u.fillText(e.toString(),f-1.2*t,s))}this.state.BGCanvas=h}}let Q=`#version 300 es
|
|
2
264
|
in vec2 a_position;
|
|
3
265
|
out vec2 v_texCoord;
|
|
4
266
|
|
|
@@ -7,7 +269,7 @@ void main() {
|
|
|
7
269
|
// 将 [-1, 1] 映射到 [0, 1]
|
|
8
270
|
v_texCoord = a_position * 0.5 + 0.5;
|
|
9
271
|
}
|
|
10
|
-
`,
|
|
272
|
+
`,Z=Number.NEGATIVE_INFINITY,J=`#version 300 es
|
|
11
273
|
precision highp float;
|
|
12
274
|
|
|
13
275
|
in vec2 v_texCoord;
|
|
@@ -59,4 +321,4 @@ void main() {
|
|
|
59
321
|
normalized = clamp(normalized, 0.0, 1.0);
|
|
60
322
|
fragColor = texture(u_colorTexture, vec2(normalized, 0.5));
|
|
61
323
|
}
|
|
62
|
-
`;class w extends _{ringStaging=new Float32Array(0);programLocations=null;resolveProgramLocations(t,e){let a=t.getAttribLocation(e,"a_position"),r=t.getUniformLocation(e,"u_dataTexture"),i=t.getUniformLocation(e,"u_colorTexture"),l=t.getUniformLocation(e,"u_rangeMin"),n=t.getUniformLocation(e,"u_rangeMax"),s=t.getUniformLocation(e,"u_ringHead"),o=t.getUniformLocation(e,"u_ringValidCount"),h=t.getUniformLocation(e,"u_ringCapacity"),u=t.getUniformLocation(e,"u_dataHeight"),d=t.getUniformLocation(e,"u_tileWidth"),c=t.getUniformLocation(e,"u_ringMode");return!(a<0)&&r&&i&&l&&n&&s&&o&&h&&u&&d&&c?{position:a,dataTexture:r,colorTexture:i,rangeMin:l,rangeMax:n,ringHead:s,ringValidCount:o,ringCapacity:h,dataHeight:u,tileWidth:d,ringMode:c}:null}init(t){super.init(t);let{gl:e}=this.state;if(!e)return;this.state.canvas.__rfkitHeatmap=this;let a=t.colors,r=a&&a.length>0?a:o.FLUORESCENCE_COLORS,i=this.createProgram({vertex:A,fragment:x}),l=this.createQuadBuffer(),s=this.createTexture(),h=this.createTexture(),u=e.getParameter(e.MAX_TEXTURE_SIZE);if(s){e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,s);let t=new Float32Array([S]);e.texImage2D(e.TEXTURE_2D,0,e.R32F,1,1,0,e.RED,e.FLOAT,t)}let[d,c]=t.range??o.RANGE,g=new n(r,d,c,.1);this.updateProps({data:[],colors:r,program:i,quadBuffer:l,dataTexture:s,dataTextures:s?[s]:[],dataTiles:[],colorTexture:h,dataWidth:0,dataHeight:0,CI:g,maxTextureSize:u,ringMode:!1,ringCapacity:0,ringHead:0,ringCount:0}),e.enable(e.BLEND),e.blendFunc(e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),this.updateColorTexture(),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),this.resize()}updateColorTexture(){let{gl:t,colorTexture:e,CI:a,range:r}=this.state;if(!t||!e||!a)return;let[i,l]=r,n=new Uint8Array(1024);for(let t=0;t<256;t++){let e=i+t/255*(l-i),r=a.getColor(e);n[4*t]=r.r,n[4*t+1]=r.g,n[4*t+2]=r.b,n[4*t+3]=r.a}t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,e),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,256,1,0,t.RGBA,t.UNSIGNED_BYTE,n)}updateDataTexture(){let{gl:t,data:e}=this.state;if(!t||0===e.length)return;let a=e.length,r=0;for(let t=0;t<a;t++){let a=e[t]?.length??0;a>r&&(r=a)}if(0===r)return;let i=this.state.maxTextureSize||t.getParameter(t.MAX_TEXTURE_SIZE);if(!i||i<=0)return;let l=Math.ceil(r/i),n=Math.ceil(a/i),s=l*n,o=this.state.dataTextures??[];if(o.length<s){let t=s-o.length;for(let e=0;e<t;e++){let t=this.createTexture();t&&o.push(t)}}else if(o.length>s){for(let e=s;e<o.length;e++)t.deleteTexture(o[e]);o=o.slice(0,s)}let h=[];t.activeTexture(t.TEXTURE0);for(let s=0;s<n;s++){let n=s*i,u=Math.min(i,a-n);for(let a=0;a<l;a++){let d=a*i,c=Math.min(i,r-d),g=s*l+a,f=o[g];if(!f)continue;let p=new Float32Array(c*u);for(let t=0;t<u;t++){let a=e[n+t],r=a?.length??0,i=t*c;for(let t=0;t<c;t++){let e=d+t;if(0===r||e>=r){p[i+t]=S;continue}let l=a[e];void 0===l||Number.isNaN(l)?p[i+t]=S:p[i+t]=l}}t.bindTexture(t.TEXTURE_2D,f),t.texImage2D(t.TEXTURE_2D,0,t.R32F,c,u,0,t.RED,t.FLOAT,p),h[g]={startCol:d,startRow:n,width:c,height:u}}}this.updateProps({dataTexture:o[0]??null,dataTextures:o,dataTiles:h,dataWidth:r,dataHeight:a})}ensureRingLayout(t,e){let{gl:a,maxTextureSize:r}=this.state;if(!a||t<=0||e<=0||e>r)return!1;let i=Math.ceil(t/r);if(this.state.ringMode&&this.state.ringCapacity===e&&this.state.dataWidth===t&&this.state.dataTextures.length===i)return!0;let l=[],n=[];a.activeTexture(a.TEXTURE0);for(let s=0;s<i;s++){let i=s*r,o=Math.min(r,t-i),h=this.createTexture();if(!h){for(let t of l)a.deleteTexture(t);return!1}let u=new Float32Array(o*e);u.fill(S),a.bindTexture(a.TEXTURE_2D,h),a.texImage2D(a.TEXTURE_2D,0,a.R32F,o,e,0,a.RED,a.FLOAT,u),l.push(h),n.push({startCol:i,startRow:0,width:o,height:e})}for(let t of this.state.dataTextures)a.deleteTexture(t);return this.updateProps({data:[],dataTexture:l[0]??null,dataTextures:l,dataTiles:n,dataWidth:t,dataHeight:e,ringMode:!0,ringCapacity:e,ringHead:0,ringCount:0}),!0}uploadRingRow(t,e){let{gl:a,dataTextures:r,dataTiles:i,dataWidth:l}=this.state;if(a&&!(l<=0)){a.activeTexture(a.TEXTURE0);for(let n=0;n<i.length;n++){let s;let o=i[n],h=r[n];if(o&&h){if(t instanceof Float32Array&&t.length>=l)s=t.subarray(o.startCol,o.startCol+o.width);else{this.ringStaging.length<o.width&&(this.ringStaging=new Float32Array(o.width)),s=this.ringStaging;for(let e=0;e<o.width;e++){let a=t[o.startCol+e];s[e]=void 0===a||Number.isNaN(a)?S:a}}a.bindTexture(a.TEXTURE_2D,h),a.texSubImage2D(a.TEXTURE_2D,0,0,e,o.width,1,a.RED,a.FLOAT,s)}}}}uploadRingReplace(t,e){let{gl:a,dataTextures:r,dataTiles:i}=this.state;if(!a)return 0;let l=0;for(;l<t.length&&0===t[l].length;)l+=1;let n=Math.min(e,t.length-l);a.activeTexture(a.TEXTURE0);for(let s=0;s<i.length;s++){let o=i[s],h=r[s];if(!o||!h)continue;let u=new Float32Array(o.width*e);u.fill(S);for(let e=0;e<n;e++){let a=t[l+e],r=e*o.width;if(a instanceof Float32Array){let t=Math.min(a.length,o.startCol+o.width);t>o.startCol&&u.set(a.subarray(o.startCol,t),r)}else for(let t=0;t<o.width;t++){let e=a[o.startCol+t];u[r+t]=void 0===e||Number.isNaN(e)?S:e}}a.bindTexture(a.TEXTURE_2D,h),a.texSubImage2D(a.TEXTURE_2D,0,0,0,o.width,e,a.RED,a.FLOAT,u)}return n}replace(t,e=t.length,a=t.reduce((t,e)=>Math.max(t,e.length),0)){if(e<=0||a<=0){this.clear();return}if(!this.ensureRingLayout(a,e)){this.render(t.map(t=>Array.from(t)));return}let r=this.uploadRingReplace(t,e);this.state.data=[],this.state.ringMode=!0,this.state.ringHead=0,this.state.ringCount=r,this.draw()}append(t,e=this.state.ringCapacity||1){let a=t.length;if(a<=0||e<=0)return;if(!this.ensureRingLayout(a,e)){let a=this.state.data.slice(-Math.max(0,e-1));a.push(Array.from(t)),this.render(a);return}let{ringCapacity:r,ringCount:i,ringHead:l}=this.state;this.uploadRingRow(t,i<r?(l+i)%r:l),i<r?this.state.ringCount=i+1:this.state.ringHead=(l+1)%r,this.state.data=[],this.state.ringMode=!0,this.draw()}updateProps(t){super.updateProps(t);let e=t.colors??this.state.colors,a=t.range??this.state.range;if((t.colors||t.range)&&e.length>0){let[t,r]=a;this.state.CI?this.state.CI.setColors(e,t,r,.1):this.state.CI=new n(e,t,r,.1),this.updateColorTexture()}}setRange(t){t&&(this.updateProps({range:t}),this.draw())}clear(){this.clearRect(),this.updateProps({data:[],ringHead:0,ringCount:0})}render(t){t?.length>0&&(this.state.data=t,this.state.ringMode=!1,this.state.ringHead=0,this.state.ringCount=0,this.updateDataTexture(),this.draw())}draw(){let{gl:t,program:e,quadBuffer:a,dataTextures:r,dataTiles:i,colorTexture:l,data:n,range:s,dataWidth:o,dataHeight:h,canvas:u,ringMode:d,ringHead:c,ringCount:g,ringCapacity:f}=this.state;if(!t||!e||!a||!l)return;let p=this.programLocations??this.resolveProgramLocations(t,e);if(!p||(this.programLocations=p,t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT),!d&&0===n.length||d&&0===g||0===o||0===h||0===i.length||0===r.length))return;let[m,T]=s;t.useProgram(e),t.bindBuffer(t.ARRAY_BUFFER,a),t.enableVertexAttribArray(p.position),t.vertexAttribPointer(p.position,2,t.FLOAT,!1,0,0),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,l),t.uniform1i(p.colorTexture,1),t.uniform1f(p.rangeMin,m),t.uniform1f(p.rangeMax,T),t.uniform1i(p.ringHead,c),t.uniform1i(p.ringValidCount,g),t.uniform1i(p.ringCapacity,f),t.uniform1i(p.dataHeight,h),t.uniform1i(p.ringMode,+!!d);let C=u.width,E=u.height,M=t=>Math.round(t/o*C),R=t=>Math.round(t/h*E);for(let e=0;e<i.length;e++){let a=i[e],l=r[e];if(!a||!l)continue;let n=M(a.startCol),s=M(a.startCol+a.width),o=R(a.startRow),h=E-R(a.startRow+a.height),u=s-n,c=E-o-h;u<=0||c<=0||(d?t.viewport(n,0,u,E):t.viewport(n,h,u,c),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,l),t.uniform1i(p.dataTexture,0),t.uniform1i(p.tileWidth,a.width),t.drawArrays(t.TRIANGLES,0,6))}t.viewport(0,0,C,E)}resize(){super.resize()}toDataURL(t="image/png",e){this.draw();let{canvas:a}=this.state,r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i?(i.translate(0,a.height),i.scale(1,-1),i.drawImage(a,0,0),r.toDataURL(t,e)):null}dispose(){let{gl:t,program:e,quadBuffer:a,dataTextures:r,colorTexture:i}=this.state;if(t){for(let i of(e&&t.deleteProgram(e),a&&t.deleteBuffer(a),r))t.deleteTexture(i);i&&t.deleteTexture(i)}this.programLocations=null,super.dispose()}}class I extends s{init(t){super.init(t);let{colors:e}=t;this.updateProps({colors:e,data:[]}),this.resize()}updateProps(t){super.updateProps(t);let{range:e=this.state.range,colors:a=this.state.colors}=t;if(e&&a){let[t,r]=e;C(t)&&C(r)&&t<r&&(this.state.CI?this.state.CI.setColors(a,t,r,.1):this.updateProps({CI:new n(a,t,r,.1)}))}}clearImageData(){let{ctx:t,canvas:{height:e,width:a}}=this.state;e&&a&&this.updateProps({imageData:t.createImageData(a,e)})}clearRect(){super.clearRect(),this.clearImageData()}clear(){this.clearRect(),this.updateProps({data:[]})}resize(){super.resize(),this.clearImageData()}setRange(t){t&&(this.updateProps({range:t}),this.draw())}render(t){t?.length>=0&&(this.state.data=t,this.draw())}draw(){let{imageData:t,data:e,canvas:a,ctx:r,CI:i}=this.state;if(!t||0===e.length){r.clearRect(0,0,a.width,a.height);return}m(a.width,a.height,e,t.data,t=>i.getColor(t)),r.putImageData(t,0,0)}}function P(t){let{renderer:e=g.WebGL}=t;if(e===g.WebGL){if(R())return new w(t);console.warn("[Heatmap] WebGL 2.0 not supported, falling back to Canvas")}return new I(t)}let b=function(t){return P(t)},y=b,v=!1;class N extends s{init(t){super.init(t);let{series:e,barValue2Color:a,disabledClearRect:r,colors:i}=t;this.updateProps({interval:0,series:{},barValue2Color:a,disabledClearRect:r,colors:i}),this.setRange(this.state.range),Array.isArray(e)&&e.forEach(t=>{this.setSeries(t)})}updateProps(t){super.updateProps(t);let{colors:e=this.state.colors}=t;this.state.barValue2Color&&!this.state.CI&&e&&this.updateProps({CI:new n(e,0,100,.1)})}clear(){super.clearRect();let{series:t}=this.state;Object.entries(t).forEach(([t,e])=>{this.setSeries({...e,name:t,data:void 0})})}setRange(t){this.updateProps({range:[t[0],t[1],t[1]-t[0]]}),this.draw()}setIntervel(t){t&&this.updateProps({interval:t})}setSeries(t){if(t?.name){let{name:e}=t,{series:a}=this.state;a[e]={thickness:o.THICKNESS,display:!0,color:o.COLOR,type:c.Line,data:void 0,path:void 0,orientation:d.Vertical,...a[e],...t},this.draw()}}render(t){t&&this.setSeries(t),this.draw()}draw(){let{series:t,ctx:a,disabledClearRect:r}=this.state;r||this.clearRect();let i=Object.values(t),{range:l,canvas:n}=this.state,s=l[0],h=l[1],{width:u,height:g}=n,f=h-s;a.save(),a.translate(.5,.5);for(let t=0;t<i.length;t+=1){let r=i[t],{display:l,type:n,data:h,orientation:p}=r,m=r.thickness??o.THICKNESS,T=r.color??o.COLOR;if(!h||!l)continue;let C=h.length,E=u/C;switch(n){case c.Line:case c.Stepline:a.lineWidth=m,a.strokeStyle=T;break;case c.Circle:case c.Rect:case c.Bar:case c.Area:a.fillStyle=T}if(n===c.Line){let t=[];for(let e=0;e<C;e+=1){let r=h[e];if(void 0===r||Number.isNaN(r)){if(t.length>0){a.beginPath(),a.moveTo(t[0][0],t[0][1]);for(let e=1;e<t.length;e++)a.lineTo(t[e][0],t[e][1]);a.lineWidth=m,a.strokeStyle=T,a.stroke(),t=[]}}else{let a=Math.round((e+.5)*E),i=Math.round((r-s)/f*g);t.push([a,i])}}if(t.length>0){a.beginPath(),a.moveTo(t[0][0],t[0][1]);for(let e=1;e<t.length;e++)a.lineTo(t[e][0],t[e][1]);a.lineWidth=m,a.strokeStyle=T,a.stroke()}}if(n===c.Stepline){let t=[];for(let e=0;e<C;e+=1){let r=h[e];if(void 0===r||Number.isNaN(r)){if(t.length>0){a.beginPath(),a.moveTo(t[0][0],t[0][1]);for(let e=1;e<t.length;e++){let[r,i]=t[e-1],[l,n]=t[e];a.lineTo(l,i),a.lineTo(l,n)}let[e,r]=t[t.length-1],i=Math.round(e+E);a.lineTo(i,r),a.lineWidth=m,a.strokeStyle=T,a.stroke(),t=[]}}else{let a=Math.round(e*E),i=Math.round((r-s)/f*g);t.push([a,i])}}if(t.length>0){a.beginPath(),a.moveTo(t[0][0],t[0][1]);for(let e=1;e<t.length;e++){let[r,i]=t[e-1],[l,n]=t[e];a.lineTo(l,i),a.lineTo(l,n)}let[e,r]=t[t.length-1],i=Math.round(e+E);a.lineTo(i,r),a.lineWidth=m,a.strokeStyle=T,a.stroke()}}if(n===c.Circle){let t=+m/2,e=2*Math.PI;for(let r=0;r<C;r+=1){let i=Math.round((r+.5)*E),l=Math.round((h[r]-s)/f*g);a.beginPath(),a.arc(i,l,t,0,e),a.fillStyle=T,a.fill()}}if(n===c.Rect){let t=+m;for(let e=0;e<C;e+=1){let r=Math.round((e+.5)*E),i=Math.round((h[e]-s)/f*g);a.beginPath(),a.rect(r,i,t,t),a.fillStyle=T,a.fill()}}if(n===c.Area){let t=[],{r,g:i,b:l}=e(T??o.COLOR),n=t=>{if(0===t.length)return;let[e,n]=t[0],[s,o]=t[t.length-1],h=Math.round(s+E);a.beginPath(),a.moveTo(e,0),a.lineTo(e,n);for(let e=1;e<t.length;e++){let[r,i]=t[e-1],[l,n]=t[e];a.lineTo(l,i),a.lineTo(l,n)}a.lineTo(h,o),a.lineTo(h,0),a.closePath(),a.fillStyle=`rgba(${r}, ${i}, ${l}, 1)`,a.fill()};for(let e=0;e<C;e+=1){let a=h[e];void 0===a||Number.isNaN(a)?(n(t),t=[]):t.push([Math.round(e*E),Math.round((a-s)/f*g)])}n(t)}if(n===c.Bar){if(p===d.Horizontal){let t=g/C,e=Math.round(t);for(let r=0;r<C;r+=1){let i=Math.round(g-(r+1)*t),l=Math.round((h[r]-s)/f*u),n=u-l;a.beginPath(),a.rect(n,i,l,e),a.fillStyle=T,a.fill()}}else for(let t=0;t<C;t+=1){let e=Math.floor(t*u/C),r=Math.floor((t+1)*u/C)-e,i=Math.round((h[t]-s)/f*g);a.beginPath(),a.rect(e,0,r,i),a.fillStyle=this.state.barValue2Color?this.state.CI?.getColor(h[t]).hax??T:T,a.fill()}}}a.restore()}}function D(t){return new N(t)}let F=function(t){return D(t)},k=F;function O(t,e,a){return[t+(t-e)*Math.cos(a),t+(t-e)*Math.sin(a)]}function U(t){let e=0;for(let a of t.values)a>e&&(e=a);return e}class B extends s{getSpecialTickConfig(t){}getAngleOffset(){return 0}init(t){super.init(t),this.state.canvas.style.transform="scaleY(1)";let e=t.fillStyle??o.FILL_STYLE,a=t.fillStylePrimary??o.FILL_STYLE_PRIMARY,r=t.fillStyleTransparentBase??o.FILL_STYLE_TRANSPARENT_BASE;this.updateProps({fillStyle:e,fillStylePrimary:a,fillStyleTransparentBase:r,baseWidth:6,padding:60,ticksStep:6,ticksRenderLength:5,markedAngles:Array.from({length:12},(t,e)=>30*e),color2intensity:i(a),data:{series:[],range:[0,0],polygon:[],yawAngle:0,isNorthFacing:!0}})}clear(){this.clearRect()}resize(){super.resize(),this.state.markedAngles&&this.drawBackground(),this.state.data&&this.drawTicks()}render(t){let e=this.state.data,a={...e,...t},r=void 0!==t.yawAngle&&t.yawAngle!==e?.yawAngle||void 0!==t.isNorthFacing&&t.isNorthFacing!==e?.isNorthFacing;this.state.data=a,r&&this.drawTicks(),this.draw()}drawTicks(){let{canvas:t,baseWidth:e,fillStyle:a,fillStylePrimary:r,ticksStep:i,ticksRenderLength:l,markedAngles:n,data:s}=this.state,{yawAngle:o=0,isNorthFacing:h=!0}=s,u=document.createElement("canvas");u.width=t.width,u.height=t.height;let d=u.getContext("2d");if(!d)return;let c=t.width/2,g=h?0:-o;for(let t=0;t<360;t+=i){let r=(t+g)*Math.PI/180,i=n.includes(t),[s,o]=O(c,3.5*e+l*(i?1.2:1)+3*!!i,r),[h,u]=O(c,3.5*e,r);d.beginPath(),d.moveTo(s,o),d.lineTo(h,u),d.strokeStyle=a,d.lineWidth=i?2:.5,d.stroke()}d.font=`${2*e}px Arial`,d.textAlign="center",d.textBaseline="middle",n.forEach(t=>{let[r,i]=O(c,8*e,(t+g-90)*Math.PI/180),l=this.getSpecialTickConfig(t);l?(d.fillStyle=l.color,d.fillText(l.alias,r,i)):(d.fillStyle=a,d.fillText(t.toString(),r,i))});let f=t.width/18;d.translate(t.width/2,t.height/2),d.scale(1,-1),d.rotate((h?-o:0)*Math.PI/180),d.lineJoin="round",d.lineCap="round",d.strokeStyle=r,d.fillStyle=r,d.lineWidth=f/6,d.beginPath(),d.moveTo(0,f/2),d.lineTo(f/2,-f/2),d.lineTo(0,f/2-1/((1+Math.sqrt(5))/2)*f),d.lineTo(-f/2,-f/2),d.lineTo(0,f/2),d.fill(),d.stroke(),this.state.ticksCanvas=u}drawBackground(){let{canvas:t,baseWidth:e,padding:a,fillStyleTransparentBase:r}=this.state,i=document.createElement("canvas");i.width=t.width,i.height=t.height;let l=i.getContext("2d");if(!l)return;let n=t.width/2;l.beginPath(),l.arc(n,n,n-2*e,0,2*Math.PI),l.strokeStyle=r,l.lineWidth=e,l.stroke(),[,,,,,].fill(1).forEach((t,i)=>{l.beginPath(),l.arc(n,n,(n-a)*(i+1)/5,0,2*Math.PI),l.strokeStyle=r,l.lineWidth=e/4,l.stroke()}),this.state.BGCanvas=i}draw(){let{data:t,canvas:e,ctx:a,baseWidth:r,padding:i,BGCanvas:l,ticksCanvas:n,color2intensity:s,fillStylePrimary:o,fillStyleTransparentBase:h}=this.state,{series:u,range:d,polygon:c,isNorthFacing:g=!0,yawAngle:f=0}=t;if(!u&&!c)return;this.clearRect();let p=e.width/2,m=g?0:-f,T=this.getAngleOffset();if(l&&a.drawImage(l,0,0),d){let t=360/d.length;d.forEach((e,r)=>{let l=(r*t+m+T)*Math.PI/180,n=((r+1)*t+m+T)*Math.PI/180;a.beginPath(),a.moveTo(p,p),a.arc(p,p,p-i,l,n),a.closePath(),a.fillStyle=s[e],a.fill()})}if(c?.length)for(let t of[...c].sort((t,e)=>U(e)-U(t))){let{values:e,color:l,showDots:n}=t;if(!e||e.length<3)continue;let s=360/e.length,h=l??o;a.beginPath();let u=[];e.forEach((t,e)=>{let r=Math.max(0,Math.min(1,t))*(p-i),[l,n]=O(p,p-r,(e*s+m+T)*Math.PI/180);u.push([l,n]),0===e?a.moveTo(l,n):a.lineTo(l,n)}),a.closePath(),a.save();let d=a.createRadialGradient(p,p,0,p,p,p-i);if(d.addColorStop(0,"transparent"),d.addColorStop(1,h),a.fillStyle=d,a.fill(),a.strokeStyle=h,a.lineWidth=1,a.lineJoin="round",a.lineCap="round",a.stroke(),!1!==n){a.fillStyle=h;let t=r/2;for(let[e,r]of u)a.beginPath(),a.arc(e,r,t,0,2*Math.PI),a.fill()}a.restore()}u?.forEach(({value:t,color:e,lineWidth:l=r,radio:n=1})=>{if(null==t)return;let s=(t+m)*Math.PI/180-Math.PI/2,[o,u]=O(p,p,s),[d,c]=O(p,i+(1-n)*(p-i)+l/2,s);1!==n&&(a.beginPath(),a.moveTo(...O(p,i+l/2,s)),a.lineTo(d,c),a.strokeStyle=h,a.stroke()),a.beginPath(),a.moveTo(d,c),a.lineTo(o,u),a.strokeStyle=e,a.lineWidth=l,a.lineJoin="round",a.lineCap="round",a.stroke()}),n&&a.drawImage(n,0,0)}}let G={0:{color:o.DIAL_NORTH_COLOR,alias:"北"},180:{color:o.DIAL_SOUTH_COLOR,alias:"南"}};class X extends B{getSpecialTickConfig(t){return G[t]}getAngleOffset(){return -90}}class H extends B{getSpecialTickConfig(t){}getAngleOffset(){return 0}}class W extends s{init(t){super.init(t),this.updateProps({lineColor:t.lineColor??o.LINE_COLOR,pointColor:t.pointColor??o.POINT_COLOR})}clear(){this.clearRect(),this.updateProps({data:{IData:[],QData:[]}})}render(t){t.IData&&t.QData&&(this.state.data=t,this.draw())}draw(){let{data:t,canvas:e,pointColor:a,lineColor:r,ctx:i}=this.state;if(!t)return;let{IData:l,QData:n}=t;if(0===l.length||0===n.length)return;let{width:s,height:o}=i.canvas;i.clearRect(0,0,e.width,e.height);let[h,u]=T(l),[d,c]=T([h,u,...n]),g=s/(u-h),f=o/(c-d),p=l.map((t,e)=>({x:(t-h)*g,y:(n[e]-d)*f}));i.beginPath(),p.forEach((t,e)=>{i[0===e?"moveTo":"lineTo"](t.x,t.y)}),i.strokeStyle=r,i.lineWidth=1,i.stroke(),p.forEach(t=>{i.beginPath(),i.arc(t.x,t.y,2,0,2*Math.PI),i.fillStyle=a,i.fill()})}}let V=4,z=256,Y=t=>{let e=[];for(let a=0;a<256;a++){let i=a/255,l=t.length-1,n=Math.min(Math.floor(i*l),l-1),s=i*l-n,o=r(t[n]),h=r(t[n+1]);e.push({r:Math.round(o.r+(h.r-o.r)*s),g:Math.round(o.g+(h.g-o.g)*s),b:Math.round(o.b+(h.b-o.b)*s)})}return e},$=()=>{let t=Y(u.CLASSIC_COLORS),e=new Uint8ClampedArray((u.MAX_ACCUMULATION+1)*4),a=t.length-1;for(let r=1;r<=u.MAX_ACCUMULATION;r++){let i=t[Math.min(Math.floor(Math.min(r*u.CLASSIC_ALPHA_GAIN/255,1)*a),a)],l=4*r;e[l]=i.r,e[l+1]=i.g,e[l+2]=i.b,e[l+3]=Math.min(r*u.CLASSIC_ALPHA_GAIN,255)}return e},Q=$();class q extends s{init(t){super.init(t),this.state.iqEyeMode=this.resolveMode(t.iqEyeMode),this.initColorLookup()}resize(){super.resize();let{width:t,height:e}=this.state.canvas;this.state.accumulator=new Uint16Array(t*e),this.state.imageData=this.state.ctx.createImageData(t,e)}clear(){this.clearRect(),this.state.accumulator?.fill(0),this.updateProps({data:{IData:[],QData:[]}})}render(t){t.IData?.length&&t.QData?.length&&(this.state.data=t,this.state.accumulator&&("classic"===this.state.iqEyeMode?this.state.accumulator.fill(0):this.applyDecay(),this.accumulateData(t),this.draw()))}draw(){if("classic"===this.state.iqEyeMode){this.drawClassic();return}this.drawModern()}drawClassic(){let{accumulator:t,imageData:e,ctx:a}=this.state;if(!t||!e)return;let r=e.data;for(let e=0;e<t.length;e++){let a=t[e],i=4*e;if(a>0){let t=4*a;r[i]=Q[t],r[i+1]=Q[t+1],r[i+2]=Q[t+2],r[i+3]=Q[t+3]}else r[i+3]=0}a.putImageData(e,0,0)}drawModern(){let{accumulator:t,imageData:e,ctx:a,colorLookup:r}=this.state;if(!t||!e||!r)return;let i=e.data,l=u.MAX_ACCUMULATION;for(let e=0;e<t.length;e++){let a=t[e],n=4*e;if(a>0){let t=Math.min(a/l,1),e=Math.min(Math.floor(t*(r.length-1)),r.length-1),s=r[e];i[n]=s.r,i[n+1]=s.g,i[n+2]=s.b,i[n+3]=Math.round(255*Math.min(2*t,1))}else i[n+3]=0}a.putImageData(e,0,0)}initColorLookup(){let t="classic"===this.state.iqEyeMode?u.CLASSIC_COLORS:u.MODERN_COLORS;this.state.colorLookup=Y(t)}resolveMode(t){return"modern"===t?"modern":u.MODE}applyDecay(){let{accumulator:t}=this.state;if(!t)return;let e=u.DECAY_FACTOR;for(let a=0;a<t.length;a++)t[a]=Math.floor(t[a]*e)}accumulateData(t){let{accumulator:e,canvas:a}=this.state;if(!e)return;let{width:r,height:i}=a,[l,n]=u.Y_RANGE,s=n-l,o=u.SEGMENT_SIZE,h=r/(o-1);this.accumulateSegments(t.IData,l,s,r,i,o,h,e),this.accumulateSegments(t.QData,l,s,r,i,o,h,e)}accumulateSegments(t,e,a,r,i,l,n,s){let o=Math.ceil(t.length/l);for(let h=0;h<o;h++){let o=-1,u=-1;for(let d=0;d<l;d++){let c=h*l+d;if(c>=t.length)break;let g=t[c];if(void 0===g)continue;let f=Math.round(d*n),p=Math.round((g-e)/a*(i-1));o>=0&&this.drawLine(o,u,f,p,r,i,s),o=f,u=p}}}drawLine(t,e,a,r,i,l,n){let s=Math.abs(a-t),o=Math.abs(r-e),h=t<a?1:-1,d=e<r?1:-1,c=t,g=e,f=s-o,p=u.MAX_ACCUMULATION;for(;;){if(c>=0&&c<i&&g>=0&&g<l){let t=g*i+c;n[t]<p&&n[t]++}if(c===a&&g===r)break;let t=2*f;t>-o&&(f-=o,c+=h),t<s&&(f+=s,g+=d)}}}export{n as ColorInterpolator,X as Dial,p as Fluorescence,f as FluorescenceRenderMode,L as Gauge,c as GraphicType,y as Heatmap,I as HeatmapCanvas,w as HeatmapWebGL,W as IQ,q as IQEye,d as OrientationType,H as Radar,g as RendererType,k as Series,N as SeriesCanvas,i as color2intensity,P as createHeatmap,D as createSeries,e as hexToRGBA,a as rgbToHex};
|
|
324
|
+
`;class K extends I{disposed=!1;fallbackHandler;ringStaging=new Float32Array(0);programLocations=null;drawFrame=null;scheduleDraw(){if(null===this.drawFrame){if("function"!=typeof requestAnimationFrame){this.drawSafely();return}this.drawFrame=requestAnimationFrame(()=>{this.drawFrame=null,this.drawSafely()})}}drawSafely(){try{this.draw()}catch(e){this.requestFallback(e)}}cancelScheduledDraw(){null!==this.drawFrame&&("function"==typeof cancelAnimationFrame&&cancelAnimationFrame(this.drawFrame),this.drawFrame=null)}resolveProgramLocations(e,t){let a=e.getAttribLocation(t,"a_position"),r=e.getUniformLocation(t,"u_dataTexture"),i=e.getUniformLocation(t,"u_colorTexture"),n=e.getUniformLocation(t,"u_rangeMin"),l=e.getUniformLocation(t,"u_rangeMax"),o=e.getUniformLocation(t,"u_ringHead"),s=e.getUniformLocation(t,"u_ringValidCount"),h=e.getUniformLocation(t,"u_ringCapacity"),u=e.getUniformLocation(t,"u_dataHeight"),d=e.getUniformLocation(t,"u_tileWidth"),c=e.getUniformLocation(t,"u_ringMode");return!(a<0)&&r&&i&&n&&l&&o&&s&&h&&u&&d&&c?{position:a,dataTexture:r,colorTexture:i,rangeMin:n,rangeMax:l,ringHead:o,ringValidCount:s,ringCapacity:h,dataHeight:u,tileWidth:d,ringMode:c}:null}init(e){super.init(e);let{canvas:t,gl:a}=this.state;if(!a)throw Error("WebGL 2.0 is not available");let r=e.colors,i=r&&r.length>0?r:u.FLUORESCENCE_COLORS,n=this.createProgram({vertex:Q,fragment:J}),s=this.createQuadBuffer(),h=this.createTexture(),d=this.createTexture(),c=a.getParameter(a.MAX_TEXTURE_SIZE);if(!n||!s||!h||!d||!Number.isFinite(c)||c<=0)throw n&&a.deleteProgram(n),s&&a.deleteBuffer(s),h&&a.deleteTexture(h),d&&a.deleteTexture(d),t.remove(),Error("Failed to allocate heatmap WebGL resources");let f=this.resolveProgramLocations(a,n);if(!f)throw a.deleteProgram(n),a.deleteBuffer(s),a.deleteTexture(h),a.deleteTexture(d),t.remove(),Error("Failed to resolve heatmap WebGL program locations");if(this.programLocations=f,h){a.activeTexture(a.TEXTURE0),a.bindTexture(a.TEXTURE_2D,h);let e=new Float32Array([Z]);a.texImage2D(a.TEXTURE_2D,0,a.R32F,1,1,0,a.RED,a.FLOAT,e)}let[g,p]=e.range??u.RANGE,m=new l(i,g,p,.1);this.updateProps({data:[],colors:i,program:n,quadBuffer:s,dataTexture:h,dataTextures:h?[h]:[],dataTiles:[],colorTexture:d,dataWidth:0,dataHeight:0,CI:m,maxTextureSize:c,ringMode:!1,ringCapacity:0,ringHead:0,ringCount:0}),a.enable(a.BLEND),a.blendFunc(a.SRC_ALPHA,a.ONE_MINUS_SRC_ALPHA),this.updateColorTexture(),a.clearColor(0,0,0,0),a.clear(a.COLOR_BUFFER_BIT),this.resize(),t.__rfkitHeatmap=this,o(t,this)}setFallbackHandler(e){this.state.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.fallbackHandler=e,this.state.canvas.addEventListener("webglcontextlost",this.handleContextLost)}handleContextLost=e=>{e.preventDefault(),this.disposed||this.requestFallback(Error("Heatmap WebGL context lost"))};requestFallback(e){if(this.fallbackHandler){this.fallbackHandler(e);return}throw e instanceof Error?e:Error(String(e))}updateColorTexture(){let{gl:e,colorTexture:t,CI:a,range:r}=this.state;if(!e||!t||!a)return;let[i,n]=r,l=new Uint8Array(1024);for(let e=0;e<256;e++){let t=i+e/255*(n-i),r=a.getColor(t);l[4*e]=r.r,l[4*e+1]=r.g,l[4*e+2]=r.b,l[4*e+3]=r.a}e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,t),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,256,1,0,e.RGBA,e.UNSIGNED_BYTE,l)}updateDataTexture(){let{gl:e,data:t}=this.state;if(!e||0===t.length)return;let a=t.length,r=0;for(let e=0;e<a;e++){let a=t[e]?.length??0;a>r&&(r=a)}if(0===r)return;let i=this.state.maxTextureSize||e.getParameter(e.MAX_TEXTURE_SIZE);if(!i||i<=0)return;let n=Math.ceil(r/i),l=Math.ceil(a/i),o=n*l,s=this.state.dataTextures??[];if(s.length<o){let e=o-s.length;for(let t=0;t<e;t++){let e=this.createTexture();e&&s.push(e)}}else if(s.length>o){for(let t=o;t<s.length;t++)e.deleteTexture(s[t]);s=s.slice(0,o)}let h=[];e.activeTexture(e.TEXTURE0);for(let o=0;o<l;o++){let l=o*i,u=Math.min(i,a-l);for(let a=0;a<n;a++){let d=a*i,c=Math.min(i,r-d),f=o*n+a,g=s[f];if(!g)continue;let p=new Float32Array(c*u);for(let e=0;e<u;e++){let a=t[l+e],r=a?.length??0,i=e*c;for(let e=0;e<c;e++){let t=d+e;if(0===r||t>=r){p[i+e]=Z;continue}let n=a[t];void 0===n||Number.isNaN(n)?p[i+e]=Z:p[i+e]=n}}e.bindTexture(e.TEXTURE_2D,g),e.texImage2D(e.TEXTURE_2D,0,e.R32F,c,u,0,e.RED,e.FLOAT,p),h[f]={startCol:d,startRow:l,width:c,height:u}}}this.updateProps({dataTexture:s[0]??null,dataTextures:s,dataTiles:h,dataWidth:r,dataHeight:a})}ensureRingLayout(e,t){let{gl:a,maxTextureSize:r}=this.state;if(!a||e<=0||t<=0||t>r)return!1;let i=Math.ceil(e/r);if(this.state.ringMode&&this.state.ringCapacity===t&&this.state.dataWidth===e&&this.state.dataTextures.length===i)return!0;let n=[],l=[];a.activeTexture(a.TEXTURE0);for(let o=0;o<i;o++){let i=o*r,s=Math.min(r,e-i),h=this.createTexture();if(!h){for(let e of n)a.deleteTexture(e);return!1}let u=new Float32Array(s*t);u.fill(Z),a.bindTexture(a.TEXTURE_2D,h),a.texImage2D(a.TEXTURE_2D,0,a.R32F,s,t,0,a.RED,a.FLOAT,u),n.push(h),l.push({startCol:i,startRow:0,width:s,height:t})}for(let e of this.state.dataTextures)a.deleteTexture(e);return this.updateProps({data:[],dataTexture:n[0]??null,dataTextures:n,dataTiles:l,dataWidth:e,dataHeight:t,ringMode:!0,ringCapacity:t,ringHead:0,ringCount:0}),!0}uploadRingRow(e,t){let{gl:a,dataTextures:r,dataTiles:i,dataWidth:n}=this.state;if(a&&!(n<=0)){a.activeTexture(a.TEXTURE0);for(let l=0;l<i.length;l++){let o;let s=i[l],h=r[l];if(s&&h){if(e instanceof Float32Array&&e.length>=n)o=e.subarray(s.startCol,s.startCol+s.width);else{this.ringStaging.length<s.width&&(this.ringStaging=new Float32Array(s.width)),o=this.ringStaging;for(let t=0;t<s.width;t++){let a=e[s.startCol+t];o[t]=void 0===a||Number.isNaN(a)?Z:a}}a.bindTexture(a.TEXTURE_2D,h),a.texSubImage2D(a.TEXTURE_2D,0,0,t,s.width,1,a.RED,a.FLOAT,o)}}}}uploadRingReplace(e,t){let{gl:a,dataTextures:r,dataTiles:i}=this.state;if(!a)return 0;let n=0;for(;n<e.length&&0===e[n].length;)n+=1;let l=Math.min(t,e.length-n);a.activeTexture(a.TEXTURE0);for(let o=0;o<i.length;o++){let s=i[o],h=r[o];if(!s||!h)continue;let u=new Float32Array(s.width*t);u.fill(Z);for(let t=0;t<l;t++){let a=e[n+t],r=t*s.width;if(a instanceof Float32Array){let e=Math.min(a.length,s.startCol+s.width);e>s.startCol&&u.set(a.subarray(s.startCol,e),r)}else for(let e=0;e<s.width;e++){let t=a[s.startCol+e];u[r+e]=void 0===t||Number.isNaN(t)?Z:t}}a.bindTexture(a.TEXTURE_2D,h),a.texSubImage2D(a.TEXTURE_2D,0,0,0,s.width,t,a.RED,a.FLOAT,u)}return l}replace(e,t=e.length,a=e.reduce((e,t)=>Math.max(e,t.length),0)){if(t<=0||a<=0){this.clear();return}if(!this.ensureRingLayout(a,t)){this.render(e.map(e=>Array.from(e)));return}let r=this.uploadRingReplace(e,t);this.state.data=[],this.state.ringMode=!0,this.state.ringHead=0,this.state.ringCount=r,this.draw()}append(e,t=this.state.ringCapacity||1){let a=e.length;if(a<=0||t<=0)return;if(!this.ensureRingLayout(a,t)){let a=this.state.data.slice(-Math.max(0,t-1));a.push(Array.from(e)),this.render(a);return}let{ringCapacity:r,ringCount:i,ringHead:n}=this.state;this.uploadRingRow(e,i<r?(n+i)%r:n),i<r?this.state.ringCount=i+1:this.state.ringHead=(n+1)%r,this.state.data=[],this.state.ringMode=!0,this.scheduleDraw()}updateProps(e){super.updateProps(e);let t=e.colors??this.state.colors,a=e.range??this.state.range;if((e.colors||e.range)&&t.length>0){let[e,r]=a;this.state.CI?this.state.CI.setColors(t,e,r,.1):this.state.CI=new l(t,e,r,.1),this.updateColorTexture()}}setRange(e){e&&(this.updateProps({range:e}),this.draw())}clear(){this.cancelScheduledDraw(),this.clearRect(),this.updateProps({data:[],ringHead:0,ringCount:0})}render(e){e?.length>0&&(this.state.data=e,this.state.ringMode=!1,this.state.ringHead=0,this.state.ringCount=0,this.updateDataTexture(),this.draw())}draw(){this.cancelScheduledDraw();let{gl:e,program:t,quadBuffer:a,dataTextures:r,dataTiles:i,colorTexture:n,data:l,range:o,dataWidth:s,dataHeight:h,canvas:u,ringMode:d,ringHead:c,ringCount:f,ringCapacity:g}=this.state;if(!e||!t||!a||!n)return;let p=this.programLocations??this.resolveProgramLocations(e,t);if(!p||(this.programLocations=p,e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),!d&&0===l.length||d&&0===f||0===s||0===h||0===i.length||0===r.length))return;let[m,E]=o;e.useProgram(t),e.bindBuffer(e.ARRAY_BUFFER,a),e.enableVertexAttribArray(p.position),e.vertexAttribPointer(p.position,2,e.FLOAT,!1,0,0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,n),e.uniform1i(p.colorTexture,1),e.uniform1f(p.rangeMin,m),e.uniform1f(p.rangeMax,E),e.uniform1i(p.ringHead,c),e.uniform1i(p.ringValidCount,f),e.uniform1i(p.ringCapacity,g),e.uniform1i(p.dataHeight,h),e.uniform1i(p.ringMode,+!!d);let T=u.width,_=u.height,x=e=>Math.round(e/s*T),M=e=>Math.round(e/h*_);for(let t=0;t<i.length;t++){let a=i[t],n=r[t];if(!a||!n)continue;let l=x(a.startCol),o=x(a.startCol+a.width),s=M(a.startRow),h=_-M(a.startRow+a.height),u=o-l,c=_-s-h;u<=0||c<=0||(d?e.viewport(l,0,u,_):e.viewport(l,h,u,c),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,n),e.uniform1i(p.dataTexture,0),e.uniform1i(p.tileWidth,a.width),e.drawArrays(e.TRIANGLES,0,6))}e.viewport(0,0,T,_)}resize(){super.resize(!1),this.drawSafely()}toDataURL(e="image/png",t){try{this.draw();let{canvas:a}=this.state,r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");if(!i)return null;return i.translate(0,a.height),i.scale(1,-1),i.drawImage(a,0,0),r.toDataURL(e,t)}catch(e){return this.requestFallback(e),null}}dispose(){if(this.disposed)return;this.disposed=!0,this.cancelScheduledDraw();let{canvas:e,gl:t,program:a,quadBuffer:r,dataTextures:i,colorTexture:n}=this.state;if(s(e,this),e?.removeEventListener("webglcontextlost",this.handleContextLost),e.__rfkitHeatmap===this&&delete e.__rfkitHeatmap,t){for(let e of(a&&t.deleteProgram(a),r&&t.deleteBuffer(r),i))t.deleteTexture(e);n&&t.deleteTexture(n)}this.programLocations=null,this.fallbackHandler=void 0,super.dispose()}}class j extends H{init(e){super.init(e);let{colors:t}=e;this.updateProps({colors:t,data:[]}),this.resize()}updateProps(e){super.updateProps(e);let{range:t=this.state.range,colors:a=this.state.colors}=e;if(t&&a){let[e,r]=t;_(e)&&_(r)&&e<r&&(this.state.CI?this.state.CI.setColors(a,e,r,.1):this.updateProps({CI:new l(a,e,r,.1)}))}}clearImageData(){let{ctx:e,canvas:{height:t,width:a}}=this.state;t&&a&&this.updateProps({imageData:e.createImageData(a,t)})}clearRect(){super.clearRect(),this.clearImageData()}clear(){this.clearRect(),this.updateProps({data:[]})}resize(){super.resize(),this.clearImageData()}setRange(e){e&&(this.updateProps({range:e}),this.draw())}render(e){e?.length>=0&&(this.state.data=e,this.draw())}draw(){let{imageData:e,data:t,canvas:a,ctx:r,CI:i}=this.state;if(!e||0===t.length){r.clearRect(0,0,a.width,a.height);return}E(a.width,a.height,t,e.data,e=>i.getColor(e)),r.putImageData(e,0,0)}}class ee{append;replace;backend;disposed=!1;fallingBack=!1;latestData=null;props;ringCapacity=0;ringCount=0;ringFrames=[];ringHead=0;ringWidth=0;constructor(e){this.props={...e},this.backend=this.createBackend(e),this.backend instanceof K&&(this.append=(e,t)=>this.appendFrame(e,t),this.replace=(e,t)=>this.replaceFrames(e,t))}get state(){return this.backend.state}createBackend(e){if((e.renderer??p.WebGL)===p.WebGL){if(C())try{let t=new K(e);return t.setFallbackHandler(e=>this.fallbackToCanvas(e)),t}catch(e){console.warn("[Heatmap] WebGL initialization failed, falling back to Canvas",e)}else console.warn("[Heatmap] WebGL 2.0 not supported, falling back to Canvas")}return new j(e)}fallbackToCanvas(e){if(!this.disposed&&!this.fallingBack&&!(this.backend instanceof j)){this.fallingBack=!0;try{let{colors:t,range:a}=this.backend.state;console.warn("[Heatmap] WebGL rendering failed, falling back to Canvas",e),this.backend.dispose(),this.props={...this.props,colors:t,range:a,renderer:p.Canvas},this.backend=new j(this.props);let r=this.ringCapacity>0?this.materializeRingFrames():this.latestData;r&&this.backend.render(r)}finally{this.fallingBack=!1}}}resetRingFrames(){this.ringCapacity=0,this.ringCount=0,this.ringFrames=[],this.ringHead=0,this.ringWidth=0}initializeRingFrames(e,t){this.ringCapacity=e,this.ringCount=0,this.ringFrames=Array(e),this.ringHead=0,this.ringWidth=t}trackReplacement(e,t){let a=0;for(;a<e.length&&0===e[a].length;)a+=1;let r=Math.min(t,e.length-a),i=0;for(let t=0;t<r;t+=1)i=Math.max(i,e[a+t].length);if(t<=0||i<=0){this.resetRingFrames();return}this.initializeRingFrames(t,i);for(let t=0;t<r;t+=1)this.ringFrames[t]=e[a+t];this.ringCount=r}trackAppend(e,t){let a=e.length;if(t<=0||a<=0)return;(this.ringCapacity!==t||this.ringWidth!==a)&&this.initializeRingFrames(t,a);let r=this.ringCount<this.ringCapacity?(this.ringHead+this.ringCount)%this.ringCapacity:this.ringHead;this.ringFrames[r]=e,this.ringCount<this.ringCapacity?this.ringCount+=1:this.ringHead=(this.ringHead+1)%this.ringCapacity}materializeRingFrames(){let e=Array.from({length:this.ringCapacity},()=>[]),t=this.ringCapacity-this.ringCount;for(let a=0;a<this.ringCount;a+=1){let r=(this.ringHead+a)%this.ringCapacity,i=this.ringFrames[r];i&&(e[t+a]=Array.from(i))}return e}handleBackendError(e){if(this.backend instanceof K){this.fallbackToCanvas(e);return}throw e}replaceFrames(e,t=e.length){this.latestData=null,this.trackReplacement(e,t);try{if(this.backend instanceof K){this.backend.replace(e,t);return}this.backend.render(this.materializeRingFrames())}catch(e){this.handleBackendError(e)}}appendFrame(e,t=this.ringCapacity||1){this.latestData=null,this.trackAppend(e,t);try{if(this.backend instanceof K){this.backend.append(e,t);return}this.backend.render(this.materializeRingFrames())}catch(e){this.handleBackendError(e)}}updateProps(e){void 0!==e.colors&&(this.props.colors=e.colors),void 0!==e.range&&(this.props.range=e.range);try{this.backend.updateProps(e)}catch(e){this.handleBackendError(e)}}render(e){this.latestData=e,this.resetRingFrames();try{this.backend.render(e)}catch(e){this.handleBackendError(e)}}setRange(e){this.props.range=e;try{this.backend.setRange(e)}catch(e){this.handleBackendError(e)}}clear(){this.latestData=null,this.resetRingFrames();try{this.backend.clear()}catch(e){this.handleBackendError(e)}}resize(){try{this.backend.resize()}catch(e){this.handleBackendError(e)}}dispose(){this.disposed||(this.disposed=!0,this.latestData=null,this.resetRingFrames(),this.backend.dispose())}}function et(e){return new ee(e)}let ea=function(e){return et(e)},er=ea,ei=!1;class en extends H{init(e){super.init(e);let{series:t,barValue2Color:a,disabledClearRect:r,colors:i}=e;this.updateProps({interval:0,series:{},barValue2Color:a,disabledClearRect:r,colors:i}),this.setRange(this.state.range),Array.isArray(t)&&t.forEach(e=>{this.setSeries(e)})}updateProps(e){super.updateProps(e);let{colors:t=this.state.colors}=e;this.state.barValue2Color&&!this.state.CI&&t&&this.updateProps({CI:new l(t,0,100,.1)})}clear(){super.clearRect();let{series:e}=this.state;Object.entries(e).forEach(([e,t])=>{this.setSeries({...t,name:e,data:void 0})})}setRange(e){this.updateProps({range:[e[0],e[1],e[1]-e[0]]}),this.draw()}setIntervel(e){e&&this.updateProps({interval:e})}setSeries(e){if(e?.name){let{name:t}=e,{series:a}=this.state;a[t]={thickness:u.THICKNESS,display:!0,color:u.COLOR,type:g.Line,data:void 0,path:void 0,orientation:f.Vertical,...a[t],...e},this.draw()}}render(e){e&&this.setSeries(e),this.draw()}draw(){let{series:e,ctx:a,disabledClearRect:r}=this.state;r||this.clearRect();let i=Object.values(e),{range:n,canvas:l}=this.state,o=n[0],s=n[1],{width:h,height:d}=l,c=s-o;a.save(),a.translate(.5,.5);for(let e=0;e<i.length;e+=1){let r=i[e],{display:n,type:l,data:s,orientation:p}=r,m=r.thickness??u.THICKNESS,E=r.color??u.COLOR;if(!s||!n)continue;let T=s.length,_=h/T;switch(l){case g.Line:case g.Stepline:a.lineWidth=m,a.strokeStyle=E;break;case g.Circle:case g.Rect:case g.Bar:case g.Area:a.fillStyle=E}if(l===g.Line){let e=[];for(let t=0;t<T;t+=1){let r=s[t];if(void 0===r||Number.isNaN(r)){if(e.length>0){a.beginPath(),a.moveTo(e[0][0],e[0][1]);for(let t=1;t<e.length;t++)a.lineTo(e[t][0],e[t][1]);a.lineWidth=m,a.strokeStyle=E,a.stroke(),e=[]}}else{let a=Math.round((t+.5)*_),i=Math.round((r-o)/c*d);e.push([a,i])}}if(e.length>0){a.beginPath(),a.moveTo(e[0][0],e[0][1]);for(let t=1;t<e.length;t++)a.lineTo(e[t][0],e[t][1]);a.lineWidth=m,a.strokeStyle=E,a.stroke()}}if(l===g.Stepline){let e=[];for(let t=0;t<T;t+=1){let r=s[t];if(void 0===r||Number.isNaN(r)){if(e.length>0){a.beginPath(),a.moveTo(e[0][0],e[0][1]);for(let t=1;t<e.length;t++){let[r,i]=e[t-1],[n,l]=e[t];a.lineTo(n,i),a.lineTo(n,l)}let[t,r]=e[e.length-1],i=Math.round(t+_);a.lineTo(i,r),a.lineWidth=m,a.strokeStyle=E,a.stroke(),e=[]}}else{let a=Math.round(t*_),i=Math.round((r-o)/c*d);e.push([a,i])}}if(e.length>0){a.beginPath(),a.moveTo(e[0][0],e[0][1]);for(let t=1;t<e.length;t++){let[r,i]=e[t-1],[n,l]=e[t];a.lineTo(n,i),a.lineTo(n,l)}let[t,r]=e[e.length-1],i=Math.round(t+_);a.lineTo(i,r),a.lineWidth=m,a.strokeStyle=E,a.stroke()}}if(l===g.Circle){let e=+m/2,t=2*Math.PI;for(let r=0;r<T;r+=1){let i=Math.round((r+.5)*_),n=Math.round((s[r]-o)/c*d);a.beginPath(),a.arc(i,n,e,0,t),a.fillStyle=E,a.fill()}}if(l===g.Rect){let e=+m;for(let t=0;t<T;t+=1){let r=Math.round((t+.5)*_),i=Math.round((s[t]-o)/c*d);a.beginPath(),a.rect(r,i,e,e),a.fillStyle=E,a.fill()}}if(l===g.Area){let e=[],{r,g:i,b:n}=t(E??u.COLOR),l=e=>{if(0===e.length)return;let[t,l]=e[0],[o,s]=e[e.length-1],h=Math.round(o+_);a.beginPath(),a.moveTo(t,0),a.lineTo(t,l);for(let t=1;t<e.length;t++){let[r,i]=e[t-1],[n,l]=e[t];a.lineTo(n,i),a.lineTo(n,l)}a.lineTo(h,s),a.lineTo(h,0),a.closePath(),a.fillStyle=`rgba(${r}, ${i}, ${n}, 1)`,a.fill()};for(let t=0;t<T;t+=1){let a=s[t];void 0===a||Number.isNaN(a)?(l(e),e=[]):e.push([Math.round(t*_),Math.round((a-o)/c*d)])}l(e)}if(l===g.Bar){if(p===f.Horizontal){let e=d/T,t=Math.round(e);for(let r=0;r<T;r+=1){let i=Math.round(d-(r+1)*e),n=Math.round((s[r]-o)/c*h),l=h-n;a.beginPath(),a.rect(l,i,n,t),a.fillStyle=E,a.fill()}}else for(let e=0;e<T;e+=1){let t=Math.floor(e*h/T),r=Math.floor((e+1)*h/T)-t,i=Math.round((s[e]-o)/c*d);a.beginPath(),a.rect(t,0,r,i),a.fillStyle=this.state.barValue2Color?this.state.CI?.getColor(s[e]).hax??E:E,a.fill()}}}a.restore()}}function el(e){return new en(e)}let eo=function(e){return el(e)},es=eo;function eh(e,t,a){return[e+(e-t)*Math.cos(a),e+(e-t)*Math.sin(a)]}function eu(e){let t=0;for(let a of e.values)a>t&&(t=a);return t}class ed extends H{getSpecialTickConfig(e){}getAngleOffset(){return 0}init(e){super.init(e),this.state.canvas.style.transform="scaleY(1)";let t=e.fillStyle??u.FILL_STYLE,a=e.fillStylePrimary??u.FILL_STYLE_PRIMARY,r=e.fillStyleTransparentBase??u.FILL_STYLE_TRANSPARENT_BASE;this.updateProps({fillStyle:t,fillStylePrimary:a,fillStyleTransparentBase:r,baseWidth:6,padding:60,ticksStep:6,ticksRenderLength:5,markedAngles:Array.from({length:12},(e,t)=>30*t),color2intensity:i(a),data:{series:[],range:[0,0],polygon:[],yawAngle:0,isNorthFacing:!0}})}clear(){this.clearRect()}resize(){super.resize(),this.state.markedAngles&&this.drawBackground(),this.state.data&&this.drawTicks()}render(e){let t=this.state.data,a={...t,...e},r=void 0!==e.yawAngle&&e.yawAngle!==t?.yawAngle||void 0!==e.isNorthFacing&&e.isNorthFacing!==t?.isNorthFacing;this.state.data=a,r&&this.drawTicks(),this.draw()}drawTicks(){let{canvas:e,baseWidth:t,fillStyle:a,fillStylePrimary:r,ticksStep:i,ticksRenderLength:n,markedAngles:l,data:o}=this.state,{yawAngle:s=0,isNorthFacing:h=!0}=o,u=document.createElement("canvas");u.width=e.width,u.height=e.height;let d=u.getContext("2d");if(!d)return;let c=e.width/2,f=h?0:-s;for(let e=0;e<360;e+=i){let r=(e+f)*Math.PI/180,i=l.includes(e),[o,s]=eh(c,3.5*t+n*(i?1.2:1)+3*!!i,r),[h,u]=eh(c,3.5*t,r);d.beginPath(),d.moveTo(o,s),d.lineTo(h,u),d.strokeStyle=a,d.lineWidth=i?2:.5,d.stroke()}d.font=`${2*t}px Arial`,d.textAlign="center",d.textBaseline="middle",l.forEach(e=>{let[r,i]=eh(c,8*t,(e+f-90)*Math.PI/180),n=this.getSpecialTickConfig(e);n?(d.fillStyle=n.color,d.fillText(n.alias,r,i)):(d.fillStyle=a,d.fillText(e.toString(),r,i))});let g=e.width/18;d.translate(e.width/2,e.height/2),d.scale(1,-1),d.rotate((h?-s:0)*Math.PI/180),d.lineJoin="round",d.lineCap="round",d.strokeStyle=r,d.fillStyle=r,d.lineWidth=g/6,d.beginPath(),d.moveTo(0,g/2),d.lineTo(g/2,-g/2),d.lineTo(0,g/2-1/((1+Math.sqrt(5))/2)*g),d.lineTo(-g/2,-g/2),d.lineTo(0,g/2),d.fill(),d.stroke(),this.state.ticksCanvas=u}drawBackground(){let{canvas:e,baseWidth:t,padding:a,fillStyleTransparentBase:r}=this.state,i=document.createElement("canvas");i.width=e.width,i.height=e.height;let n=i.getContext("2d");if(!n)return;let l=e.width/2;n.beginPath(),n.arc(l,l,l-2*t,0,2*Math.PI),n.strokeStyle=r,n.lineWidth=t,n.stroke(),[,,,,,].fill(1).forEach((e,i)=>{n.beginPath(),n.arc(l,l,(l-a)*(i+1)/5,0,2*Math.PI),n.strokeStyle=r,n.lineWidth=t/4,n.stroke()}),this.state.BGCanvas=i}draw(){let{data:e,canvas:t,ctx:a,baseWidth:r,padding:i,BGCanvas:n,ticksCanvas:l,color2intensity:o,fillStylePrimary:s,fillStyleTransparentBase:h}=this.state,{series:u,range:d,polygon:c,isNorthFacing:f=!0,yawAngle:g=0}=e;if(!u&&!c)return;this.clearRect();let p=t.width/2,m=f?0:-g,E=this.getAngleOffset();if(n&&a.drawImage(n,0,0),d){let e=360/d.length;d.forEach((t,r)=>{let n=(r*e+m+E)*Math.PI/180,l=((r+1)*e+m+E)*Math.PI/180;a.beginPath(),a.moveTo(p,p),a.arc(p,p,p-i,n,l),a.closePath(),a.fillStyle=o[t],a.fill()})}if(c?.length)for(let e of[...c].sort((e,t)=>eu(t)-eu(e))){let{values:t,color:n,showDots:l}=e;if(!t||t.length<3)continue;let o=360/t.length,h=n??s;a.beginPath();let u=[];t.forEach((e,t)=>{let r=Math.max(0,Math.min(1,e))*(p-i),[n,l]=eh(p,p-r,(t*o+m+E)*Math.PI/180);u.push([n,l]),0===t?a.moveTo(n,l):a.lineTo(n,l)}),a.closePath(),a.save();let d=a.createRadialGradient(p,p,0,p,p,p-i);if(d.addColorStop(0,"transparent"),d.addColorStop(1,h),a.fillStyle=d,a.fill(),a.strokeStyle=h,a.lineWidth=1,a.lineJoin="round",a.lineCap="round",a.stroke(),!1!==l){a.fillStyle=h;let e=r/2;for(let[t,r]of u)a.beginPath(),a.arc(t,r,e,0,2*Math.PI),a.fill()}a.restore()}u?.forEach(({value:e,color:t,lineWidth:n=r,radio:l=1})=>{if(null==e)return;let o=(e+m)*Math.PI/180-Math.PI/2,[s,u]=eh(p,p,o),[d,c]=eh(p,i+(1-l)*(p-i)+n/2,o);1!==l&&(a.beginPath(),a.moveTo(...eh(p,i+n/2,o)),a.lineTo(d,c),a.strokeStyle=h,a.stroke()),a.beginPath(),a.moveTo(d,c),a.lineTo(s,u),a.strokeStyle=t,a.lineWidth=n,a.lineJoin="round",a.lineCap="round",a.stroke()}),l&&a.drawImage(l,0,0)}}let ec={0:{color:u.DIAL_NORTH_COLOR,alias:"北"},180:{color:u.DIAL_SOUTH_COLOR,alias:"南"}};class ef extends ed{getSpecialTickConfig(e){return ec[e]}getAngleOffset(){return -90}}class eg extends ed{getSpecialTickConfig(e){}getAngleOffset(){return 0}}class ep extends H{init(e){super.init(e),this.updateProps({lineColor:e.lineColor??u.LINE_COLOR,pointColor:e.pointColor??u.POINT_COLOR})}clear(){this.clearRect(),this.updateProps({data:{IData:[],QData:[]}})}render(e){e.IData&&e.QData&&(this.state.data=e,this.draw())}draw(){let{data:e,canvas:t,pointColor:a,lineColor:r,ctx:i}=this.state;if(!e)return;let{IData:n,QData:l}=e;if(0===n.length||0===l.length)return;let{width:o,height:s}=i.canvas;i.clearRect(0,0,t.width,t.height);let[h,u]=T(n),[d,c]=T([h,u,...l]),f=o/(u-h),g=s/(c-d),p=n.map((e,t)=>({x:(e-h)*f,y:(l[t]-d)*g}));i.beginPath(),p.forEach((e,t)=>{i[0===t?"moveTo":"lineTo"](e.x,e.y)}),i.strokeStyle=r,i.lineWidth=1,i.stroke(),p.forEach(e=>{i.beginPath(),i.arc(e.x,e.y,2,0,2*Math.PI),i.fillStyle=a,i.fill()})}}let em=4,eE=256,eT=e=>{let t=[];for(let a=0;a<256;a++){let i=a/255,n=e.length-1,l=Math.min(Math.floor(i*n),n-1),o=i*n-l,s=r(e[l]),h=r(e[l+1]);t.push({r:Math.round(s.r+(h.r-s.r)*o),g:Math.round(s.g+(h.g-s.g)*o),b:Math.round(s.b+(h.b-s.b)*o)})}return t},e_=()=>{let e=eT(c.CLASSIC_COLORS),t=new Uint8ClampedArray((c.MAX_ACCUMULATION+1)*4),a=e.length-1;for(let r=1;r<=c.MAX_ACCUMULATION;r++){let i=e[Math.min(Math.floor(Math.min(r*c.CLASSIC_ALPHA_GAIN/255,1)*a),a)],n=4*r;t[n]=i.r,t[n+1]=i.g,t[n+2]=i.b,t[n+3]=Math.min(r*c.CLASSIC_ALPHA_GAIN,255)}return t},ex=e_();class eM extends H{init(e){super.init(e),this.state.iqEyeMode=this.resolveMode(e.iqEyeMode),this.initColorLookup()}resize(){super.resize();let{width:e,height:t}=this.state.canvas;this.state.accumulator=new Uint16Array(e*t),this.state.imageData=this.state.ctx.createImageData(e,t)}clear(){this.clearRect(),this.state.accumulator?.fill(0),this.updateProps({data:{IData:[],QData:[]}})}render(e){e.IData?.length&&e.QData?.length&&(this.state.data=e,this.state.accumulator&&("classic"===this.state.iqEyeMode?this.state.accumulator.fill(0):this.applyDecay(),this.accumulateData(e),this.draw()))}draw(){if("classic"===this.state.iqEyeMode){this.drawClassic();return}this.drawModern()}drawClassic(){let{accumulator:e,imageData:t,ctx:a}=this.state;if(!e||!t)return;let r=t.data;for(let t=0;t<e.length;t++){let a=e[t],i=4*t;if(a>0){let e=4*a;r[i]=ex[e],r[i+1]=ex[e+1],r[i+2]=ex[e+2],r[i+3]=ex[e+3]}else r[i+3]=0}a.putImageData(t,0,0)}drawModern(){let{accumulator:e,imageData:t,ctx:a,colorLookup:r}=this.state;if(!e||!t||!r)return;let i=t.data,n=c.MAX_ACCUMULATION;for(let t=0;t<e.length;t++){let a=e[t],l=4*t;if(a>0){let e=Math.min(a/n,1),t=Math.min(Math.floor(e*(r.length-1)),r.length-1),o=r[t];i[l]=o.r,i[l+1]=o.g,i[l+2]=o.b,i[l+3]=Math.round(255*Math.min(2*e,1))}else i[l+3]=0}a.putImageData(t,0,0)}initColorLookup(){let e="classic"===this.state.iqEyeMode?c.CLASSIC_COLORS:c.MODERN_COLORS;this.state.colorLookup=eT(e)}resolveMode(e){return"modern"===e?"modern":c.MODE}applyDecay(){let{accumulator:e}=this.state;if(!e)return;let t=c.DECAY_FACTOR;for(let a=0;a<e.length;a++)e[a]=Math.floor(e[a]*t)}accumulateData(e){let{accumulator:t,canvas:a}=this.state;if(!t)return;let{width:r,height:i}=a,[n,l]=c.Y_RANGE,o=l-n,s=c.SEGMENT_SIZE,h=r/(s-1);this.accumulateSegments(e.IData,n,o,r,i,s,h,t),this.accumulateSegments(e.QData,n,o,r,i,s,h,t)}accumulateSegments(e,t,a,r,i,n,l,o){let s=Math.ceil(e.length/n);for(let h=0;h<s;h++){let s=-1,u=-1;for(let d=0;d<n;d++){let c=h*n+d;if(c>=e.length)break;let f=e[c];if(void 0===f)continue;let g=Math.round(d*l),p=Math.round((f-t)/a*(i-1));s>=0&&this.drawLine(s,u,g,p,r,i,o),s=g,u=p}}}drawLine(e,t,a,r,i,n,l){let o=Math.abs(a-e),s=Math.abs(r-t),h=e<a?1:-1,u=t<r?1:-1,d=e,f=t,g=o-s,p=c.MAX_ACCUMULATION;for(;;){if(d>=0&&d<i&&f>=0&&f<n){let e=f*i+d;l[e]<p&&l[e]++}if(d===a&&f===r)break;let e=2*g;e>-s&&(g-=s,d+=h),e<o&&(g+=o,f+=u)}}}export{l as ColorInterpolator,ef as Dial,Y as Fluorescence,V as FluorescenceCanvas,m as FluorescenceRenderMode,W as FluorescenceWebGL,q as Gauge,g as GraphicType,er as Heatmap,j as HeatmapCanvas,K as HeatmapWebGL,ep as IQ,eM as IQEye,f as OrientationType,eg as Radar,p as RendererType,es as Series,en as SeriesCanvas,h as captureCanvasFrame,i as color2intensity,$ as createFluorescence,et as createHeatmap,el as createSeries,t as hexToRGBA,a as rgbToHex};
|
package/package.json
CHANGED
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
export
|
|
1
|
+
import type { ColorValue, FluorescenceRenderData, FluorescenceRendererState, InitProps, Range, RGBA } from '../../types';
|
|
2
|
+
import { type FluorescenceRenderMode } from '../../types';
|
|
3
|
+
import FluorescenceCanvas from './FluorescenceCanvas';
|
|
4
|
+
export type { FluorescenceRendererState } from '../../types';
|
|
5
|
+
/** 荧光谱 Canvas/WebGL 后端共享的公开接口。 */
|
|
6
|
+
export interface IFluorescence {
|
|
7
|
+
readonly state: FluorescenceRendererState;
|
|
5
8
|
init(props: InitProps): void;
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
render(data: FluorescenceRenderData): void;
|
|
10
|
+
setRange(range: Range): void;
|
|
8
11
|
setRenderMode(mode: FluorescenceRenderMode): void;
|
|
12
|
+
updateProps(props: Partial<FluorescenceRendererState>): void;
|
|
13
|
+
draw(): void;
|
|
9
14
|
clearImageData(): void;
|
|
10
15
|
clearRect(): void;
|
|
11
16
|
clear(): void;
|
|
12
|
-
dispose(): void;
|
|
13
|
-
private drawBlocks;
|
|
14
17
|
resize(): void;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
r: number;
|
|
19
|
-
g: number;
|
|
20
|
-
b: number;
|
|
21
|
-
}>, ratio: number): {
|
|
18
|
+
dispose(): void;
|
|
19
|
+
interpolateColor(colors: ColorValue[], ratio: number): RGBA;
|
|
20
|
+
hexToRgb(hex: string): {
|
|
22
21
|
r: number;
|
|
23
22
|
g: number;
|
|
24
23
|
b: number;
|
|
25
|
-
a: number;
|
|
26
24
|
};
|
|
27
|
-
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 荧光谱统一入口。
|
|
28
|
+
*
|
|
29
|
+
* 默认优先使用 WebGL;能力不足、初始化失败或运行中上下文丢失时,
|
|
30
|
+
* 使用最后的数据和显示状态自动切换到二维画布。
|
|
31
|
+
*/
|
|
32
|
+
export default class Fluorescence implements IFluorescence {
|
|
33
|
+
private backend;
|
|
34
|
+
private latestData;
|
|
35
|
+
private props;
|
|
36
|
+
private fallingBack;
|
|
37
|
+
private disposed;
|
|
38
|
+
constructor(props: InitProps);
|
|
39
|
+
get state(): FluorescenceRendererState;
|
|
40
|
+
/** 兼容旧类的显式初始化入口。 */
|
|
41
|
+
init(props: InitProps): void;
|
|
42
|
+
private createBackend;
|
|
43
|
+
private fallbackToCanvas;
|
|
44
|
+
updateProps(props: Partial<FluorescenceRendererState>): void;
|
|
45
|
+
render(data: FluorescenceRenderData): void;
|
|
46
|
+
setRange(range: Range): void;
|
|
47
|
+
setRenderMode(mode: FluorescenceRenderMode): void;
|
|
48
|
+
draw(): void;
|
|
49
|
+
/** Canvas 后端重建像素缓存;WebGL 后端无需对应缓存。 */
|
|
50
|
+
clearImageData(): void;
|
|
51
|
+
clearRect(): void;
|
|
52
|
+
clear(): void;
|
|
53
|
+
resize(): void;
|
|
54
|
+
interpolateColor(colors: ColorValue[], ratio: number): RGBA;
|
|
28
55
|
hexToRgb(hex: string): {
|
|
29
56
|
r: number;
|
|
30
57
|
g: number;
|
|
31
58
|
b: number;
|
|
32
59
|
};
|
|
33
|
-
|
|
34
|
-
private intensityMatrixCache;
|
|
35
|
-
private gridCentersCache;
|
|
36
|
-
private weightLookupCache;
|
|
37
|
-
private colorLookupCache;
|
|
38
|
-
private blockPositionsCache;
|
|
39
|
-
private lastBlockRenderParams;
|
|
40
|
-
draw(): void;
|
|
60
|
+
dispose(): void;
|
|
41
61
|
}
|
|
62
|
+
export declare const createFluorescence: (props: InitProps) => IFluorescence;
|
|
63
|
+
export { FluorescenceCanvas };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { FluorescenceRenderData, FluorescenceState, InitProps, Range } from '../../types';
|
|
3
|
+
import { FluorescenceRenderMode } from '../../types';
|
|
4
|
+
export default class FluorescenceCanvas extends Engine<FluorescenceState> {
|
|
5
|
+
init(props: InitProps): void;
|
|
6
|
+
updateProps(e: Partial<FluorescenceState>): void;
|
|
7
|
+
/** 设置渲染模式 */
|
|
8
|
+
setRenderMode(mode: FluorescenceRenderMode): void;
|
|
9
|
+
clearImageData(): void;
|
|
10
|
+
clearRect(): void;
|
|
11
|
+
clear(): void;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
private drawBlocks;
|
|
14
|
+
resize(): void;
|
|
15
|
+
setRange(range: Range): void;
|
|
16
|
+
render(data: FluorescenceRenderData): void;
|
|
17
|
+
interpolateColor(colors: Array<string | {
|
|
18
|
+
r: number;
|
|
19
|
+
g: number;
|
|
20
|
+
b: number;
|
|
21
|
+
}>, ratio: number): {
|
|
22
|
+
r: number;
|
|
23
|
+
g: number;
|
|
24
|
+
b: number;
|
|
25
|
+
a: number;
|
|
26
|
+
};
|
|
27
|
+
private colorCache;
|
|
28
|
+
hexToRgb(hex: string): {
|
|
29
|
+
r: number;
|
|
30
|
+
g: number;
|
|
31
|
+
b: number;
|
|
32
|
+
};
|
|
33
|
+
private intensityMatrixCache;
|
|
34
|
+
private gaussianGeometryCache;
|
|
35
|
+
private colorLookupCache;
|
|
36
|
+
private blockPositionsCache;
|
|
37
|
+
private lastBlockRenderParams;
|
|
38
|
+
private prepareGaussianGeometry;
|
|
39
|
+
draw(): void;
|
|
40
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InitProps, Range } from '../../types';
|
|
2
2
|
import HeatmapCanvas from './HeatmapCanvas';
|
|
3
|
-
/**
|
|
3
|
+
/** 热力图 Canvas/WebGL 后端共享的公开接口。 */
|
|
4
4
|
export interface IHeatmap {
|
|
5
5
|
render(data: number[][]): void;
|
|
6
6
|
/** 可选能力:用完整帧集合替换增量纹理。 */
|
|
@@ -17,21 +17,8 @@ interface HeatmapConstructor {
|
|
|
17
17
|
new (props: InitProps): IHeatmap;
|
|
18
18
|
(props: InitProps): IHeatmap;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* 按渲染器配置创建热力图实例。
|
|
22
|
-
* 请求 WebGL 但运行环境不支持时自动降级到二维画布。
|
|
23
|
-
*/
|
|
24
20
|
export declare function createHeatmap(props: InitProps): IHeatmap;
|
|
25
|
-
/**
|
|
26
|
-
* 热力图统一入口,支持函数调用和构造调用。
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* // 默认使用 WebGL 渲染器
|
|
30
|
-
* const heatmap = new Heatmap({ id: 'canvas', colors, range });
|
|
31
|
-
*
|
|
32
|
-
* // 显式使用二维画布渲染器
|
|
33
|
-
* const heatmap = new Heatmap({ id: 'canvas', colors, range, renderer: RendererType.Canvas });
|
|
34
|
-
*/
|
|
21
|
+
/** 热力图统一入口,支持函数调用和构造调用。 */
|
|
35
22
|
declare const Heatmap: HeatmapConstructor;
|
|
36
23
|
export default Heatmap;
|
|
37
24
|
export { HeatmapCanvas };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { FluorescenceRendererState, IFluorescence } from './Fluorescence';
|
|
2
|
+
export { createFluorescence, default as Fluorescence, FluorescenceCanvas } from './Fluorescence';
|
|
2
3
|
export { default as Gauge } from './Gauge';
|
|
3
4
|
export type { IHeatmap } from './Heatmap';
|
|
4
5
|
export { createHeatmap, default as Heatmap, HeatmapCanvas } from './Heatmap';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ColorValue, RGBA } from '../../types';
|
|
2
|
+
export declare const FLUORESCENCE_COLOR_STEPS = 256;
|
|
3
|
+
export declare const GAUSSIAN_EDGE_EXPONENT = 4.5;
|
|
4
|
+
export declare const GAUSSIAN_EDGE_WEIGHT: number;
|
|
5
|
+
export declare const GAUSSIAN_INTENSITY_GAIN = 1.3;
|
|
6
|
+
export declare const GAUSSIAN_WEIGHT_SCALE: number;
|
|
7
|
+
export interface FluorescenceGaussianMetrics {
|
|
8
|
+
cellWidth: number;
|
|
9
|
+
radiusX: number;
|
|
10
|
+
radiusY: number;
|
|
11
|
+
rangeSpan: number;
|
|
12
|
+
xSampleCount: number;
|
|
13
|
+
ySampleCount: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const getFluorescenceGaussianMetrics: (dataLength: number, width: number, height: number, rangeMin: number, rangeMax: number) => FluorescenceGaussianMetrics;
|
|
16
|
+
export declare const interpolateFluorescenceColor: (colors: readonly ColorValue[], ratio: number) => RGBA;
|
|
17
|
+
export declare const createFluorescenceColorLookup: (colors: readonly ColorValue[]) => Uint8Array;
|
|
18
|
+
export declare const getVisibleFluorescenceMaxCount: (data: Uint32Array, dataLength: number, rangeMin: number, rangeMax: number) => number;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import WebGLEngine, { type WebGLBaseState } from '../../core/WebGLEngine';
|
|
2
|
+
import type { ColorValue, FluorescenceRenderData, FluorescenceRendererState, InitProps, Range } from '../../types';
|
|
3
|
+
import { FluorescenceRenderMode } from '../../types';
|
|
4
|
+
interface HorizontalLocations {
|
|
5
|
+
position: number;
|
|
6
|
+
countTexture: WebGLUniformLocation;
|
|
7
|
+
dataWidth: WebGLUniformLocation;
|
|
8
|
+
cellWidth: WebGLUniformLocation;
|
|
9
|
+
radiusX: WebGLUniformLocation;
|
|
10
|
+
invLogMaxCount: WebGLUniformLocation;
|
|
11
|
+
}
|
|
12
|
+
interface GaussianLocations {
|
|
13
|
+
position: number;
|
|
14
|
+
horizontalTexture: WebGLUniformLocation;
|
|
15
|
+
colorTexture: WebGLUniformLocation;
|
|
16
|
+
canvasWidth: WebGLUniformLocation;
|
|
17
|
+
canvasHeight: WebGLUniformLocation;
|
|
18
|
+
rangeMin: WebGLUniformLocation;
|
|
19
|
+
rangeMax: WebGLUniformLocation;
|
|
20
|
+
radiusY: WebGLUniformLocation;
|
|
21
|
+
intensityGain: WebGLUniformLocation;
|
|
22
|
+
}
|
|
23
|
+
interface GridLocations {
|
|
24
|
+
position: number;
|
|
25
|
+
countTexture: WebGLUniformLocation;
|
|
26
|
+
colorTexture: WebGLUniformLocation;
|
|
27
|
+
dataWidth: WebGLUniformLocation;
|
|
28
|
+
canvasWidth: WebGLUniformLocation;
|
|
29
|
+
canvasHeight: WebGLUniformLocation;
|
|
30
|
+
rangeMin: WebGLUniformLocation;
|
|
31
|
+
rangeMax: WebGLUniformLocation;
|
|
32
|
+
invLogMaxCount: WebGLUniformLocation;
|
|
33
|
+
}
|
|
34
|
+
export interface FluorescenceWebGLState extends WebGLBaseState {
|
|
35
|
+
colors: ColorValue[];
|
|
36
|
+
data: Uint32Array;
|
|
37
|
+
fluorescenceMaxCount: number;
|
|
38
|
+
display?: boolean;
|
|
39
|
+
renderMode: FluorescenceRenderMode;
|
|
40
|
+
maxTextureSize: number;
|
|
41
|
+
quadBuffer: WebGLBuffer;
|
|
42
|
+
countTexture: WebGLTexture;
|
|
43
|
+
colorTexture: WebGLTexture;
|
|
44
|
+
horizontalTexture: WebGLTexture;
|
|
45
|
+
horizontalFramebuffer: WebGLFramebuffer;
|
|
46
|
+
horizontalProgram: WebGLProgram;
|
|
47
|
+
gaussianProgram: WebGLProgram;
|
|
48
|
+
gridProgram: WebGLProgram;
|
|
49
|
+
horizontalLocations: HorizontalLocations;
|
|
50
|
+
gaussianLocations: GaussianLocations;
|
|
51
|
+
gridLocations: GridLocations;
|
|
52
|
+
}
|
|
53
|
+
type FallbackHandler = (error: unknown) => void;
|
|
54
|
+
export default class FluorescenceWebGL extends WebGLEngine<FluorescenceWebGLState> {
|
|
55
|
+
private fallbackHandler?;
|
|
56
|
+
private disposed;
|
|
57
|
+
init(props: InitProps): void;
|
|
58
|
+
setFallbackHandler(handler: FallbackHandler): void;
|
|
59
|
+
private handleContextLost;
|
|
60
|
+
private requestFallback;
|
|
61
|
+
private updateColorTexture;
|
|
62
|
+
private rebuildHorizontalTarget;
|
|
63
|
+
private uploadCounts;
|
|
64
|
+
private bindQuad;
|
|
65
|
+
private drawGaussian;
|
|
66
|
+
private drawGrid;
|
|
67
|
+
updateProps(e: Partial<FluorescenceRendererState>): void;
|
|
68
|
+
setRange(range: Range): void;
|
|
69
|
+
setRenderMode(mode: FluorescenceRenderMode): void;
|
|
70
|
+
clear(): void;
|
|
71
|
+
render(data: FluorescenceRenderData): void;
|
|
72
|
+
draw(): void;
|
|
73
|
+
resize(draw?: boolean): void;
|
|
74
|
+
/** 同步重绘并导出与页面显示方向一致的当前帧。 */
|
|
75
|
+
toDataURL(type?: string, quality?: number): string | null;
|
|
76
|
+
dispose(): void;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ColorInterpolator } from '../../color';
|
|
2
2
|
import WebGLEngine, { type WebGLBaseState } from '../../core/WebGLEngine';
|
|
3
3
|
import type { ColorValue, InitProps, Range } from '../../types';
|
|
4
|
+
type FallbackHandler = (error: unknown) => void;
|
|
4
5
|
/** WebGL 热力图运行状态。 */
|
|
5
6
|
export interface HeatmapWebGLState extends WebGLBaseState {
|
|
6
7
|
data: number[][];
|
|
@@ -33,13 +34,25 @@ export interface HeatmapWebGLState extends WebGLBaseState {
|
|
|
33
34
|
ringCount: number;
|
|
34
35
|
}
|
|
35
36
|
export default class HeatmapWebGL extends WebGLEngine<HeatmapWebGLState> {
|
|
37
|
+
private disposed;
|
|
38
|
+
private fallbackHandler?;
|
|
36
39
|
/** 非类型化输入行上传时复用,避免按分片重复分配。 */
|
|
37
40
|
private ringStaging;
|
|
38
41
|
/** 首次绘制时建立,避免每帧重复查询着色程序位置。 */
|
|
39
42
|
private programLocations;
|
|
43
|
+
/** 已安排但尚未执行的画面刷新。 */
|
|
44
|
+
private drawFrame;
|
|
45
|
+
/** 合并同一显示帧内的多次增量上传。 */
|
|
46
|
+
private scheduleDraw;
|
|
47
|
+
private drawSafely;
|
|
48
|
+
/** 取消尚未执行的合并绘制。 */
|
|
49
|
+
private cancelScheduledDraw;
|
|
40
50
|
/** 解析当前着色程序使用的属性和统一变量位置。 */
|
|
41
51
|
private resolveProgramLocations;
|
|
42
52
|
init(props: InitProps): void;
|
|
53
|
+
setFallbackHandler(handler: FallbackHandler): void;
|
|
54
|
+
private handleContextLost;
|
|
55
|
+
private requestFallback;
|
|
43
56
|
/** 生成并上传固定大小的颜色查找纹理。 */
|
|
44
57
|
private updateColorTexture;
|
|
45
58
|
/** 将完整矩阵转换为浮点纹理,并按设备上限进行横纵分片。 */
|
|
@@ -71,3 +84,4 @@ export default class HeatmapWebGL extends WebGLEngine<HeatmapWebGLState> {
|
|
|
71
84
|
toDataURL(type?: string, quality?: number): string | null;
|
|
72
85
|
dispose(): void;
|
|
73
86
|
}
|
|
87
|
+
export {};
|
package/types/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export type { ColorValue, Point, Range, RangeWithSpan, RGBA } from './common';
|
|
|
2
2
|
export { DEFAULTS, EYE_DEFAULTS, FLUORESCENCE } from './common';
|
|
3
3
|
export type { AxisYRange, CircularData, CircularPolygonItem, CircularSeriesItem, FluorescenceRenderData, GaugeData, IQData, IQEyeRenderMode, SeriesConfig } from './data';
|
|
4
4
|
export { FluorescenceRenderMode, GraphicType, OrientationType, RendererType } from './enums';
|
|
5
|
-
export type { BaseState, CircularState, FluorescenceState, GaugeDataType, GaugeState, HeatmapState, InitProps, IQEyeState, IQState, SeriesState, StateProps } from './state';
|
|
5
|
+
export type { BaseState, CircularState, FluorescenceRendererState, FluorescenceState, GaugeDataType, GaugeState, HeatmapState, InitProps, IQEyeState, IQState, SeriesState, StateProps } from './state';
|
package/types/state.d.ts
CHANGED
|
@@ -61,6 +61,8 @@ export interface FluorescenceState extends BaseState {
|
|
|
61
61
|
display?: boolean;
|
|
62
62
|
renderMode?: FluorescenceRenderMode;
|
|
63
63
|
}
|
|
64
|
+
/** Canvas 与 WebGL 荧光谱统一入口都能真实提供的运行状态。 */
|
|
65
|
+
export type FluorescenceRendererState = Omit<FluorescenceState, 'ctx' | 'imageData'>;
|
|
64
66
|
/** Circular (Dial/Radar) 状态 */
|
|
65
67
|
export interface CircularState extends BaseState {
|
|
66
68
|
data: CircularData;
|