@rfkit/renderer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/color/ColorInterpolator.d.ts +41 -0
- package/color/ColorInterpolator.d.ts.map +1 -0
- package/color/ColorUtils.d.ts +27 -0
- package/color/ColorUtils.d.ts.map +1 -0
- package/color/index.d.ts +3 -0
- package/color/index.d.ts.map +1 -0
- package/core/Engine.d.ts +11 -0
- package/core/Engine.d.ts.map +1 -0
- package/core/WebGLEngine.d.ts +35 -0
- package/core/WebGLEngine.d.ts.map +1 -0
- package/index.d.ts +8 -0
- package/index.d.ts.map +1 -0
- package/index.js +106 -0
- package/package.json +21 -0
- package/renderers/cartesian/Fluorescence.d.ts +39 -0
- package/renderers/cartesian/Fluorescence.d.ts.map +1 -0
- package/renderers/cartesian/Gauge.d.ts +12 -0
- package/renderers/cartesian/Gauge.d.ts.map +1 -0
- package/renderers/cartesian/Heatmap.d.ts +14 -0
- package/renderers/cartesian/Heatmap.d.ts.map +1 -0
- package/renderers/cartesian/Series.d.ts +13 -0
- package/renderers/cartesian/Series.d.ts.map +1 -0
- package/renderers/cartesian/index.d.ts +5 -0
- package/renderers/cartesian/index.d.ts.map +1 -0
- package/renderers/circular/CircularBase.d.ts +27 -0
- package/renderers/circular/CircularBase.d.ts.map +1 -0
- package/renderers/circular/Dial.d.ts +15 -0
- package/renderers/circular/Dial.d.ts.map +1 -0
- package/renderers/circular/Radar.d.ts +15 -0
- package/renderers/circular/Radar.d.ts.map +1 -0
- package/renderers/circular/index.d.ts +4 -0
- package/renderers/circular/index.d.ts.map +1 -0
- package/renderers/index.d.ts +5 -0
- package/renderers/index.d.ts.map +1 -0
- package/renderers/scatter/IQ.d.ts +9 -0
- package/renderers/scatter/IQ.d.ts.map +1 -0
- package/renderers/scatter/IQEye.d.ts +13 -0
- package/renderers/scatter/IQEye.d.ts.map +1 -0
- package/renderers/scatter/index.d.ts +3 -0
- package/renderers/scatter/index.d.ts.map +1 -0
- package/renderers/webgl/HeatmapWebGL.d.ts +30 -0
- package/renderers/webgl/HeatmapWebGL.d.ts.map +1 -0
- package/renderers/webgl/SeriesWebGL.d.ts +56 -0
- package/renderers/webgl/SeriesWebGL.d.ts.map +1 -0
- package/renderers/webgl/index.d.ts +5 -0
- package/renderers/webgl/index.d.ts.map +1 -0
- package/types/common.d.ts +36 -0
- package/types/common.d.ts.map +1 -0
- package/types/data.d.ts +45 -0
- package/types/data.d.ts.map +1 -0
- package/types/enums.d.ts +15 -0
- package/types/enums.d.ts.map +1 -0
- package/types/index.d.ts +6 -0
- package/types/index.d.ts.map +1 -0
- package/types/state.d.ts +107 -0
- package/types/state.d.ts.map +1 -0
- package/utils/imageData.d.ts +13 -0
- package/utils/imageData.d.ts.map +1 -0
- package/utils/index.d.ts +3 -0
- package/utils/index.d.ts.map +1 -0
- package/utils/math.d.ts +13 -0
- package/utils/math.d.ts.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# rfkit-renderer
|
|
2
|
+
Canvas/WebGL 渲染器集合,适用于射频和通用数据可视化。内置线谱、热力/荧光图、IQ/眼图、仪表盘、环形/雷达等组件,并提供 WebGL 版热力渲染以处理大矩阵数据。
|
|
3
|
+
|
|
4
|
+
## 主要能力
|
|
5
|
+
- Canvas 2D:`Series`(Line/Bar/Area/Stepline)、`Heatmap`、`Fluorescence`、`Gauge`、`Dial`、`Radar`、`IQ`、`IQEye`
|
|
6
|
+
- WebGL:`HeatmapWebGL`(大规模强度矩阵,颜色查表渲染)
|
|
7
|
+
- 工具与类型:`ColorInterpolator`、`GraphicType`、`OrientationType`、`AxisYRange`、`SeriesConfig` 等
|
|
8
|
+
|
|
9
|
+
## 安装与构建
|
|
10
|
+
仓库开发:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm install
|
|
14
|
+
pnpm dev # 实时构建
|
|
15
|
+
pnpm build # 生成 dist
|
|
16
|
+
pnpm check # biome 格式与检查
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
作为依赖使用(发布包名为 `@rfkit/renderer`):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @rfkit/renderer
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 快速上手
|
|
26
|
+
HTML 容器:
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<div id="heatmap" style="width:640px;height:240px;"></div>
|
|
30
|
+
<div id="series" style="width:640px;height:200px;"></div>
|
|
31
|
+
<div id="heatmap-webgl" style="width:640px;height:240px;"></div>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
创建渲染器并推送数据:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import {
|
|
38
|
+
Heatmap,
|
|
39
|
+
Series,
|
|
40
|
+
HeatmapWebGL,
|
|
41
|
+
GraphicType
|
|
42
|
+
} from '@rfkit/renderer';
|
|
43
|
+
|
|
44
|
+
const heatmap = new Heatmap({
|
|
45
|
+
id: 'heatmap',
|
|
46
|
+
range: [-120, 0],
|
|
47
|
+
colors: ['#001B44', '#0041FF', '#00D1FF', '#00FFA3', '#FEE440', '#FF006E']
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
heatmap.render(
|
|
51
|
+
Array.from({ length: 120 }, () =>
|
|
52
|
+
Array.from({ length: 256 }, (_, i) => -110 + Math.sin(i / 16) * 30)
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const sweep = new Series({
|
|
57
|
+
id: 'series',
|
|
58
|
+
range: [-120, 10]
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
sweep.render({
|
|
62
|
+
name: 'spectrum',
|
|
63
|
+
type: GraphicType.Line,
|
|
64
|
+
color: '#00FFA3',
|
|
65
|
+
data: Float32Array.from({ length: 512 }, (_, i) => -90 + Math.cos(i / 18) * 15)
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
使用 WebGL 热力渲染(需要 WebGL2 支持):
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const heatmapGL = new HeatmapWebGL({
|
|
73
|
+
id: 'heatmap-webgl',
|
|
74
|
+
range: [-120, 0],
|
|
75
|
+
colors: ['#0B1021', '#3465A4', '#5BC0EB', '#9BF6FF', '#FDE74C', '#FF206E']
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
heatmapGL.render(largeMatrix); // largeMatrix: number[][]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 数据格式速览
|
|
82
|
+
- `Series.render` 接收 `SeriesConfig`(必填 `name`,`data` 为 `Float32Array`,`type` 由 `GraphicType` 指定)。
|
|
83
|
+
- `Heatmap` / `HeatmapWebGL` 接收 `number[][]`,内层数组表示一行数据。
|
|
84
|
+
- `Fluorescence` 接收 `{ fluorescenceData: Map<number, number>[], fluorescenceMaxCount: number }`。
|
|
85
|
+
- `IQ`/`IQEye` 接收 `{ IData: number[], QData: number[] }`。
|
|
86
|
+
- `Dial`/`Radar` 使用 `CircularData`;`Gauge` 使用 `{ value: number; limit?: number }`。
|
|
87
|
+
|
|
88
|
+
常用操作:`setRange(range)` 调整量程并重绘;`resize()` 在容器尺寸变化后调用;`clear()` 清空画布及内部数据。
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ColorValue, RGBA } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 颜色插值器类,用于在设定范围内插值颜色。
|
|
4
|
+
* 预计算颜色列表以实现 O(1) 查找性能。
|
|
5
|
+
*/
|
|
6
|
+
export default class ColorInterpolator {
|
|
7
|
+
private colorsPresetList;
|
|
8
|
+
private range;
|
|
9
|
+
private static readonly transparent;
|
|
10
|
+
/**
|
|
11
|
+
* 构造函数,接收颜色数组和范围信息,生成插值颜色列表。
|
|
12
|
+
* @param colors 颜色数组,用于插值
|
|
13
|
+
* @param rangeMin 插值范围的最小值
|
|
14
|
+
* @param rangeMax 插值范围的最大值
|
|
15
|
+
* @param stepSize 插值步长,默认为0.1
|
|
16
|
+
*/
|
|
17
|
+
constructor(colors: ColorValue[], rangeMin: number, rangeMax: number, stepSize?: number);
|
|
18
|
+
/**
|
|
19
|
+
* 设置颜色数组并生成插值颜色列表。
|
|
20
|
+
* @param colors 颜色数组,用于插值
|
|
21
|
+
* @param rangeMin 插值范围最小值
|
|
22
|
+
* @param rangeMax 插值范围最大值
|
|
23
|
+
* @param stepSize 插值步长
|
|
24
|
+
*/
|
|
25
|
+
setColors(colors: ColorValue[], rangeMin: number, rangeMax: number, stepSize: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* 根据位置值在颜色数组之间进行插值计算。
|
|
28
|
+
* @param colors 颜色数组,用于插值
|
|
29
|
+
* @param position 归一化的位置值 (0 - 1)
|
|
30
|
+
* @returns 插值后的颜色
|
|
31
|
+
*/
|
|
32
|
+
private interpolateColor;
|
|
33
|
+
/**
|
|
34
|
+
* 根据给定值获取插值后的颜色。
|
|
35
|
+
* O(1) 复杂度的查找。
|
|
36
|
+
* @param value 输入的数值,应该在设定的范围内
|
|
37
|
+
* @returns 插值后的颜色对象
|
|
38
|
+
*/
|
|
39
|
+
getColor(value: number): RGBA;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=ColorInterpolator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorInterpolator.d.ts","sourceRoot":"","sources":["../../src/color/ColorInterpolator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGjD;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAEvE;;;;;;OAMG;gBAED,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAM;IAKhB;;;;;;OAMG;IACI,SAAS,CACd,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI;IAiBP;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;OAKG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAkBrC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ColorValue, RGBA } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 将十六进制颜色转换为 RGBA
|
|
4
|
+
* @param hexColor 十六进制颜色字符串 (#RGB 或 #RRGGBB 或 #RRGGBBAA)
|
|
5
|
+
* @param alpha 可选的透明度 (0-1)
|
|
6
|
+
*/
|
|
7
|
+
export declare function hexToRGBA(hexColor: string, alpha?: number): RGBA;
|
|
8
|
+
/**
|
|
9
|
+
* 将 RGBA 颜色转换为十六进制表示 "#RRGGBB" 或 "#RRGGBBAA"
|
|
10
|
+
*/
|
|
11
|
+
export declare function rgbToHex(r: number, g: number, b: number, a?: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* 解析颜色值(带缓存)
|
|
14
|
+
* @param color 颜色值(字符串或 RGB 对象)
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseColor(color: ColorValue): RGBA;
|
|
17
|
+
/**
|
|
18
|
+
* 生成强度颜色数组(用于 Dial/Radar)
|
|
19
|
+
* @param color 基础颜色
|
|
20
|
+
* @param length 数组长度
|
|
21
|
+
*/
|
|
22
|
+
export declare function color2intensity(color: string, length?: number): string[];
|
|
23
|
+
/**
|
|
24
|
+
* 清除颜色缓存
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearColorCache(): void;
|
|
27
|
+
//# sourceMappingURL=ColorUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorUtils.d.ts","sourceRoot":"","sources":["../../src/color/ColorUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAKjD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAyChE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,SAAM,GAAG,MAAM,CAIzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAoBlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,EAAE,CAWrE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
package/color/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/color/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EACL,eAAe,EACf,eAAe,EACf,SAAS,EACT,UAAU,EACV,QAAQ,EACT,MAAM,cAAc,CAAC"}
|
package/core/Engine.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BaseState, InitProps } from '../types';
|
|
2
|
+
export default class Engine<TState extends BaseState = BaseState> {
|
|
3
|
+
state: TState;
|
|
4
|
+
constructor(props: InitProps);
|
|
5
|
+
updateProps(e: Partial<TState>): void;
|
|
6
|
+
init(_props: InitProps): void;
|
|
7
|
+
clearRect(): void;
|
|
8
|
+
resize(draw?: boolean): void;
|
|
9
|
+
draw(): void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=Engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErD,MAAM,CAAC,OAAO,OAAO,MAAM,CAAC,MAAM,SAAS,SAAS,GAAG,SAAS;IACvD,KAAK,EAAE,MAAM,CAAC;gBAET,KAAK,EAAE,SAAS;IAW5B,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;IAO9B,IAAI,CAAC,MAAM,EAAE,SAAS;IAiCtB,SAAS;IAKT,MAAM,CAAC,IAAI,UAAO;IAgBlB,IAAI;CACL"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InitProps } from '../types';
|
|
2
|
+
/** WebGL 基础状态 */
|
|
3
|
+
export interface WebGLBaseState {
|
|
4
|
+
id: string;
|
|
5
|
+
container?: HTMLElement;
|
|
6
|
+
canvas: HTMLCanvasElement;
|
|
7
|
+
gl: WebGL2RenderingContext;
|
|
8
|
+
range: [number, number] | [number, number, number];
|
|
9
|
+
}
|
|
10
|
+
/** Shader 源码 */
|
|
11
|
+
interface ShaderSource {
|
|
12
|
+
vertex: string;
|
|
13
|
+
fragment: string;
|
|
14
|
+
}
|
|
15
|
+
export default class WebGLEngine<TState extends WebGLBaseState = WebGLBaseState> {
|
|
16
|
+
state: TState;
|
|
17
|
+
constructor(props: InitProps);
|
|
18
|
+
updateProps(e: Partial<TState>): void;
|
|
19
|
+
init(_props: InitProps): void;
|
|
20
|
+
/** 编译 Shader */
|
|
21
|
+
protected compileShader(type: number, source: string): WebGLShader | null;
|
|
22
|
+
/** 创建 Shader 程序 */
|
|
23
|
+
protected createProgram(shaderSource: ShaderSource): WebGLProgram | null;
|
|
24
|
+
/** 创建纹理 */
|
|
25
|
+
protected createTexture(): WebGLTexture | null;
|
|
26
|
+
/** 创建全屏四边形顶点缓冲 */
|
|
27
|
+
protected createQuadBuffer(): WebGLBuffer | null;
|
|
28
|
+
clearRect(): void;
|
|
29
|
+
resize(draw?: boolean): void;
|
|
30
|
+
draw(): void;
|
|
31
|
+
/** 销毁资源 */
|
|
32
|
+
dispose(): void;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=WebGLEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebGLEngine.d.ts","sourceRoot":"","sources":["../../src/core/WebGLEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,iBAAiB;AACjB,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,EAAE,EAAE,sBAAsB,CAAC;IAC3B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD;AAED,gBAAgB;AAChB,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,CAAC,OAAO,OAAO,WAAW,CAC9B,MAAM,SAAS,cAAc,GAAG,cAAc;IAEvC,KAAK,EAAE,MAAM,CAAC;gBAET,KAAK,EAAE,SAAS;IAW5B,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;IAO9B,IAAI,CAAC,MAAM,EAAE,SAAS;IAgCtB,gBAAgB;IAChB,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAiBzE,mBAAmB;IACnB,SAAS,CAAC,aAAa,CAAC,YAAY,EAAE,YAAY,GAAG,YAAY,GAAG,IAAI;IAkCxE,WAAW;IACX,SAAS,CAAC,aAAa,IAAI,YAAY,GAAG,IAAI;IAgB9C,kBAAkB;IAClB,SAAS,CAAC,gBAAgB,IAAI,WAAW,GAAG,IAAI;IAehD,SAAS;IAMT,MAAM,CAAC,IAAI,UAAO;IAkBlB,IAAI;IAEJ,WAAW;IACX,OAAO;CAWR"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as ColorInterpolator } from './color/ColorInterpolator';
|
|
2
|
+
export { color2intensity, hexToRGBA, rgbToHex } from './color/ColorUtils';
|
|
3
|
+
export { Fluorescence, Gauge, Heatmap, Series } from './renderers/cartesian';
|
|
4
|
+
export { Dial, Radar } from './renderers/circular';
|
|
5
|
+
export { IQ, IQEye } from './renderers/scatter';
|
|
6
|
+
export { HeatmapWebGL, SeriesWebGL } from './renderers/webgl';
|
|
7
|
+
export { type AxisYRange, GraphicType, OrientationType, type SeriesConfig, type StateProps } from './types';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC"}
|
package/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const colorCache=new Map;function hexToRGBA(hexColor,alpha){const hexRegex=/^#([A-Fa-f0-9]{3}){1,2}([A-Fa-f0-9]{2})?$/;if(!hexRegex.test(hexColor))throw new Error("Invalid hex color format");const hex=hexColor.replace("#","");let r;let g;let b;let a;if(3===hex.length){r=Number.parseInt(hex[0]+hex[0],16);g=Number.parseInt(hex[1]+hex[1],16);b=Number.parseInt(hex[2]+hex[2],16);a=255}else if(6===hex.length){r=Number.parseInt(hex.substring(0,2),16);g=Number.parseInt(hex.substring(2,4),16);b=Number.parseInt(hex.substring(4,6),16);a=255}else if(8===hex.length){r=Number.parseInt(hex.substring(0,2),16);g=Number.parseInt(hex.substring(2,4),16);b=Number.parseInt(hex.substring(4,6),16);a=Number.parseInt(hex.substring(6,8),16)}else throw new Error("Invalid hex color length");if("number"==typeof alpha)a=Math.round(255*Math.max(0,Math.min(1,alpha)));return{r,g,b,a}}function rgbToHex(r,g,b,a=255){const toHex=channel=>channel.toString(16).padStart(2,"0");const hex=`#${toHex(r)}${toHex(g)}${toHex(b)}`;return a<255?`${hex}${toHex(a)}`:hex}function parseColor(color){if("string"==typeof color){const cached=colorCache.get(color);if(cached)return cached;const result=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(color);const parsed=result?{r:parseInt(result[1],16),g:parseInt(result[2],16),b:parseInt(result[3],16),a:result[4]?parseInt(result[4],16):255}:{r:0,g:0,b:0,a:255};colorCache.set(color,parsed);return parsed}return{r:color.r,g:color.g,b:color.b,a:color.a??255}}function color2intensity(color,length=100){const colorArray=[];for(let i=0;i<=length;i++){const alphaHex=Math.round(i/length*255);const alphaHexString=alphaHex.toString(16).padStart(2,"0").toUpperCase();const colorWithOpacity=`${color}${alphaHexString}`;colorArray.push(colorWithOpacity)}return colorArray}function clearColorCache(){colorCache.clear()}class ColorInterpolator{colorsPresetList=[];range=[0,0,0];static transparent={r:0,g:0,b:0,a:0};constructor(colors,rangeMin,rangeMax,stepSize=.1){this.setColors(colors,rangeMin,rangeMax,stepSize)}setColors(colors,rangeMin,rangeMax,stepSize){this.colorsPresetList=[];const steps=Math.max(1,Math.floor((rangeMax-rangeMin)/stepSize));const rangeDiff=rangeMax-rangeMin;this.range=[rangeMin,rangeMax,rangeDiff];const parsedColors=colors.map(parseColor);for(let i=0;i<=steps;i++){const value=rangeMin+i*stepSize;const position=(value-rangeMin)/rangeDiff;this.colorsPresetList.push(this.interpolateColor(parsedColors,position))}}interpolateColor(colors,position){const index=Math.floor(position*(colors.length-1));const ratio=position*(colors.length-1)-index;const startColor=colors[index];const endColor=colors[index+1]||colors[index];const rgba={r:Math.round(startColor.r+ratio*(endColor.r-startColor.r)),g:Math.round(startColor.g+ratio*(endColor.g-startColor.g)),b:Math.round(startColor.b+ratio*(endColor.b-startColor.b)),a:Math.round(startColor.a+ratio*(endColor.a-startColor.a))};return{...rgba,hax:rgbToHex(rgba.r,rgba.g,rgba.b,rgba.a)}}getColor(value){if(!Number.isFinite(value))return ColorInterpolator.transparent;const[rangeMin,,rangeDiff]=this.range;const position=(value-rangeMin)/rangeDiff;if(position<=0)return this.colorsPresetList[0];if(position>=1)return this.colorsPresetList[this.colorsPresetList.length-1];const index=Math.min(Math.floor(position*this.colorsPresetList.length),this.colorsPresetList.length-1);return this.colorsPresetList[index]}}class Engine{state;constructor(props){this.state={id:props.id,container:void 0,canvas:null,ctx:null,range:props.range??[-20,100]};this.init(props)}updateProps(e){this.state={...this.state,...e}}init(_props){const{id}=this.state;const container=document.getElementById(id);const canvas=document.createElement("canvas");const ctx=canvas.getContext?.("2d");canvas.style.transform="scaleY(-1)";if(ctx){ctx.imageSmoothingEnabled=false;ctx.lineJoin="miter";ctx.lineCap="butt";ctx.textBaseline="middle"}if(ctx)this.updateProps({container:container??void 0,canvas,ctx});container?.appendChild(canvas);this.resize(false)}clearRect(){const{ctx,canvas}=this.state;ctx.clearRect(0,0,canvas.width,canvas.height)}resize(draw=true){const{canvas,container}=this.state;const{clientWidth,clientHeight}=container;if(!clientWidth||!clientHeight)return;canvas.width=clientWidth;canvas.height=clientHeight;if(draw)setTimeout(()=>{this.draw()})}draw(){}}const BLOCK_RENDER_MODE=true;class Fluorescence extends Engine{init(props){super.init(props);const{colors,display}=props;this.updateProps({colors,data:[],display});this.resize()}updateProps(e){super.updateProps(e)}clearImageData(){const{ctx,canvas:{height,width}}=this.state;if(height&&width)this.updateProps({imageData:ctx.createImageData(width,height)})}clearRect(){super.clearRect();this.clearImageData()}clear(){this.clearRect();this.updateProps({data:[]})}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(){const{imageData,data,canvas,ctx,colors=["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],range=[-20,100],fluorescenceMaxCount=1}=this.state;if(!data||0===data.length||!imageData)return;const{width,height}=canvas;const[rangeMin,rangeMax]=range;const rangeSpan=rangeMax-rangeMin;if(rangeSpan<=0)return;const dataLength=data.length;const currentParams={dataLength,width,height,rangeMin,rangeMax};const paramsChanged=!this.lastBlockRenderParams||Object.keys(currentParams).some(key=>this.lastBlockRenderParams?.[key]!==currentParams[key]);if(paramsChanged||!this.blockPositionsCache||this.blockPositionsCache.length!==dataLength){this.blockPositionsCache=new Array(dataLength);const blockWidth=Math.max(1,Math.floor(width/dataLength));for(let i=0;i<dataLength;i++){const startX=Math.floor(i*width/dataLength);const endX=Math.min(width-1,startX+blockWidth-1);this.blockPositionsCache[i]={startX,endX,width:endX-startX+1}}}this.lastBlockRenderParams=currentParams;const pixelData=imageData.data;pixelData.fill(0);const invRangeSpan=1/rangeSpan;const heightMinus1=height-1;for(let dataIndex=0;dataIndex<dataLength;dataIndex++){const bin=data[dataIndex];if(!bin||0===bin.size)continue;const blockPos=this.blockPositionsCache[dataIndex];const{startX:blockStartX,endX:blockEndX}=blockPos;for(const[yValue,count]of bin.entries()){if(yValue<rangeMin||yValue>rangeMax||count<=0)continue;const normalizedY=(yValue-rangeMin)*invRangeSpan;const centerY=normalizedY*heightMinus1;const yValueCount=bin.size;const blockHeight=Math.max(1,Math.floor(height/Math.max(2*yValueCount,20)));const blockStartY=Math.max(0,Math.floor(centerY-blockHeight/2));const blockEndY=Math.min(heightMinus1,blockStartY+blockHeight-1);const intensity=Math.min(count/fluorescenceMaxCount,1);const alpha=Math.round(255*intensity);const blockColor=this.interpolateColor(colors,intensity);const{r:colorR,g:colorG,b:colorB}=blockColor;for(let py=blockStartY;py<=blockEndY;py++){const rowIndex=py*width;for(let px=blockStartX;px<=blockEndX;px++){const pixelIndex=(rowIndex+px)*4;pixelData[pixelIndex]=colorR;pixelData[pixelIndex+1]=colorG;pixelData[pixelIndex+2]=colorB;pixelData[pixelIndex+3]=alpha}}}}ctx.putImageData(imageData,0,0)}resize(){super.resize();this.clearImageData()}setRange(range){if(!range)return;this.updateProps({range});this.draw()}render(data){if(data?.fluorescenceData?.length>=0){this.state.data=data.fluorescenceData;this.state.fluorescenceMaxCount=data.fluorescenceMaxCount;this.draw()}}interpolateColor(colors,ratio){if(!colors||0===colors.length)return{r:255,g:255,b:255,a:255};ratio=Math.max(0,Math.min(1,ratio));const getColorRgb=color=>{if("string"==typeof color)return this.hexToRgb(color);return{r:color.r,g:color.g,b:color.b}};if(1===colors.length){const color=getColorRgb(colors[0]);return{...color,a:255}}if(0===ratio){const color=getColorRgb(colors[0]);return{...color,a:255}}if(1===ratio){const color=getColorRgb(colors[colors.length-1]);return{...color,a:255}}const scaledRatio=ratio*(colors.length-1);const index=Math.floor(scaledRatio);const localRatio=scaledRatio-index;const safeIndex=Math.min(index,colors.length-2);const color1=getColorRgb(colors[safeIndex]);const color2=getColorRgb(colors[safeIndex+1]);return{r:Math.round(color1.r+(color2.r-color1.r)*localRatio),g:Math.round(color1.g+(color2.g-color1.g)*localRatio),b:Math.round(color1.b+(color2.b-color1.b)*localRatio),a:255}}colorCache=new Map;hexToRgb(hex){const cached=this.colorCache.get(hex);if(cached)return cached;const parsed=parseColor(hex);const color={r:parsed.r,g:parsed.g,b:parsed.b};this.colorCache.set(hex,color);return color}lastRenderParams=null;intensityMatrixCache=null;gridCentersCache=null;weightLookupCache=null;colorLookupCache=null;blockPositionsCache=null;lastBlockRenderParams=null;draw(){const{imageData,data,canvas,ctx,colors=["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],range=[-20,100],fluorescenceMaxCount=1,display}=this.state;if(!display)return;if(!data||0===data.length||!imageData)return;if(BLOCK_RENDER_MODE){this.drawBlocks();return}const{width,height}=canvas;const[rangeMin,rangeMax]=range;const rangeSpan=rangeMax-rangeMin;if(rangeSpan<=0)return;const currentParams={dataLength:data.length,rangeMin,rangeMax,width,height,maxCount:fluorescenceMaxCount};const paramsChanged=!this.lastRenderParams||Object.keys(currentParams).some(key=>this.lastRenderParams?.[key]!==currentParams[key]);if(!paramsChanged)return;const needRecalcGridCenters=!this.lastRenderParams||this.lastRenderParams.dataLength!==currentParams.dataLength||this.lastRenderParams.width!==currentParams.width;const needRecalcWeightLookup=!this.lastRenderParams||this.lastRenderParams.height!==currentParams.height;const needRecalcColorLookup=!this.colorLookupCache;this.lastRenderParams=currentParams;const pixelData=imageData.data;pixelData.fill(0);const interpolationRadius=Math.max(2,Math.floor(height/50));const radiusSquared=interpolationRadius*interpolationRadius;const dataLength=data.length;const matrixSize=width*height;if(this.intensityMatrixCache&&this.intensityMatrixCache.length===matrixSize)this.intensityMatrixCache.fill(0);else this.intensityMatrixCache=new Float32Array(matrixSize);const intensityMatrix=this.intensityMatrixCache;if(needRecalcGridCenters||!this.gridCentersCache||this.gridCentersCache.length!==dataLength){this.gridCentersCache=new Float32Array(dataLength);const cellWidth=width/dataLength;for(let i=0;i<dataLength;i++){const startX=Math.floor(cellWidth*i);const endX=Math.ceil(cellWidth*(i+1));this.gridCentersCache[i]=(startX+endX)/2}}const gridCenters=this.gridCentersCache;const maxDistance=interpolationRadius;const lookupSize=Math.ceil(10*maxDistance)+1;if(needRecalcWeightLookup||!this.weightLookupCache||this.weightLookupCache.length!==lookupSize){this.weightLookupCache=new Float32Array(lookupSize);for(let i=0;i<=10*maxDistance;i++){const distance=i/10;this.weightLookupCache[i]=Math.exp(-(distance*distance)/(2*radiusSquared))}}const weightLookup=this.weightLookupCache;let maxIntensity=0;for(let dataIndex=0;dataIndex<dataLength;dataIndex++){const bin=data[dataIndex];if(!bin||0===bin.size)continue;const centerX=gridCenters[dataIndex];for(const[yValue,count]of bin.entries()){if(yValue<rangeMin||yValue>rangeMax||count<=0)continue;const normalizedY=(yValue-rangeMin)/rangeSpan;const centerY=normalizedY*(height-1);const intensity=Math.min(count/fluorescenceMaxCount,1);const expandStartX=Math.max(0,Math.floor(centerX-interpolationRadius));const expandEndX=Math.min(width-1,Math.ceil(centerX+interpolationRadius));const startY=Math.max(0,Math.floor(centerY-interpolationRadius));const endY=Math.min(height-1,Math.ceil(centerY+interpolationRadius));for(let py=startY;py<=endY;py++){const dy=py-centerY;const dySquared=dy*dy;const rowIndex=py*width;for(let px=expandStartX;px<=expandEndX;px++){const dx=px-centerX;const distanceSquared=dx*dx+dySquared;if(distanceSquared>radiusSquared)continue;const distance=Math.sqrt(distanceSquared);const lookupIndex=Math.floor(10*distance);const weight=lookupIndex<weightLookup.length?weightLookup[lookupIndex]:0;const matrixIndex=rowIndex+px;const newIntensity=intensityMatrix[matrixIndex]+intensity*weight;intensityMatrix[matrixIndex]=newIntensity;if(newIntensity>maxIntensity)maxIntensity=newIntensity}}}}const intensityThreshold=.001;const invMaxIntensity=maxIntensity>0?1/maxIntensity:0;const colorSteps=256;if(needRecalcColorLookup||!this.colorLookupCache||this.colorLookupCache.length!==colorSteps){this.colorLookupCache=new Array(colorSteps);for(let i=0;i<colorSteps;i++){const ratio=i/(colorSteps-1);this.colorLookupCache[i]=this.interpolateColor(colors,ratio)}}const colorLookup=this.colorLookupCache;for(let i=0;i<intensityMatrix.length;i++){const rawIntensity=intensityMatrix[i];if(rawIntensity<=intensityThreshold)continue;const intensity=rawIntensity*invMaxIntensity;const colorIndex=Math.floor(intensity*(colorSteps-1));const color=colorLookup[colorIndex];const pixelIndex=4*i;pixelData[pixelIndex]=color.r;pixelData[pixelIndex+1]=color.g;pixelData[pixelIndex+2]=color.b;pixelData[pixelIndex+3]=Math.round(255*intensity)}ctx.putImageData(imageData,0,0)}}const DEFAULTS={COLOR:"#000000",THICKNESS:1,FILL_STYLE:"#ffffffB0",FILL_STYLE_PRIMARY:"#1890ff",FILL_STYLE_TRANSPARENT_BASE:"#ffffff10",LINE_COLOR:"#00ff00",POINT_COLOR:"#ff0000"};var enums_OrientationType=/*#__PURE__*/function(OrientationType){OrientationType["Horizontal"]="horizontal";OrientationType["Vertical"]="vertical";return OrientationType}({});var enums_GraphicType=/*#__PURE__*/function(GraphicType){GraphicType["Circle"]="circle";GraphicType["Rect"]="rect";GraphicType["Line"]="line";GraphicType["Stepline"]="stepline";GraphicType["Bar"]="bar";GraphicType["Area"]="area";return GraphicType}({});function fillImageData(canvasWidth,canvasHeight,data,imageData,getColor){if(!data)return;const numRows=data.length;const cellHeight=canvasHeight/numRows;let cellWidth=0;for(let r=0;r<numRows;r++){const rowData=data[r];const numCols=rowData.length;if(0===numCols)continue;if(0===cellWidth)cellWidth=canvasWidth/numCols;const startRow=Math.floor(cellHeight*r);const endRow=Math.ceil(cellHeight*(r+1));for(let c=0;c<numCols;c++){const value=rowData[c];const startCol=Math.floor(cellWidth*c);const endCol=Math.ceil(cellWidth*(c+1));const color=getColor(value);for(let y=startRow;y<endRow;y++){const rowIndex=y*canvasWidth;for(let x=startCol;x<endCol;x++){const index=(rowIndex+x)*4;imageData[index]=color.r;imageData[index+1]=color.g;imageData[index+2]=color.b;imageData[index+3]=color.a}}}}}function getMinMax(numbers){if(!Array.isArray(numbers)||0===numbers.length)throw new Error("Input must be a non-empty array.");let min=numbers[0];let max=numbers[0];for(let i=1;i<numbers.length;i++)if(numbers[i]<min)min=numbers[i];else if(numbers[i]>max)max=numbers[i];return[min,max]}function isNumberAlias(n){return"number"==typeof n&&Number.isFinite(n)}function convertToF64(odata){const flattened=odata.flatMap(row=>{if(0===row.length)return Array(odata[0].length).fill(Number.NaN);return row});return new Float64Array(flattened)}class Gauge extends Engine{init(props){super.init(props);this.state.canvas.style.transform="scaleY(1)";const fillStyle=props.fillStyle??DEFAULTS.FILL_STYLE;const fillStylePrimary=props.fillStylePrimary??DEFAULTS.FILL_STYLE_PRIMARY;const fillStyleTransparentBase=props.fillStyleTransparentBase??DEFAULTS.FILL_STYLE_TRANSPARENT_BASE;const baseWidth=6;this.updateProps({fillStyle,fillStylePrimary,fillStyleTransparentBase,baseWidth,padding:props.padding??0,step:props.ticksStep??10,lineWidth:1,data:{value:0,limit:0}})}clear(){this.clearRect();const{ctx,BGCanvas}=this.state;if(BGCanvas)ctx.drawImage(BGCanvas,0,0)}resize(){super.resize();if(this.state.lineWidth)this.drawBackground()}render(data){this.state.data={...this.state.data,...data};this.draw()}draw(){const{data:{value},range:[min,max],canvas,ctx,baseWidth,padding,BGCanvas,fillStylePrimary}=this.state;this.clearRect();if(BGCanvas)ctx.drawImage(BGCanvas,0,0);const width=3*baseWidth;const height=canvas.height-2*padding;const x=(canvas.width-width)/2;const renderHeight=(value-min)/(max-min)*height;const y=height-renderHeight+padding;ctx.fillStyle=fillStylePrimary;ctx.fillRect(x,y,width,renderHeight);this.drawLimit()}drawLimit(){const{data:{limit},range:[min,max],canvas,ctx,baseWidth,padding,lineWidth,fillStylePrimary}=this.state;if(!isNumberAlias(limit))return;const width=3*baseWidth;const height=canvas.height-2*padding;const x=(canvas.width-width)/2;const y=height*(max-limit)/(max-min)+padding;const limitTagHeight=9;const triangleWidth=9;const rectangleWidth=30;ctx.beginPath();ctx.moveTo(x+width,y);ctx.lineTo(x+width+triangleWidth,y-limitTagHeight);ctx.lineTo(x+width+triangleWidth+rectangleWidth,y-limitTagHeight);ctx.lineTo(x+width+triangleWidth+rectangleWidth,y+limitTagHeight);ctx.lineTo(x+width+triangleWidth,y+limitTagHeight);ctx.lineTo(x+width,y);ctx.lineTo(x,y);ctx.lineWidth=lineWidth;ctx.strokeStyle=fillStylePrimary;ctx.stroke();ctx.fillStyle=fillStylePrimary;ctx.font="12px Arial";ctx.textAlign="center";ctx.textBaseline="middle";ctx.fillText(limit.toString(),x+width+triangleWidth+rectangleWidth/2,y+1)}drawBackground(){const{canvas,baseWidth,padding,lineWidth,step,range:[min,max],fillStyle,fillStyleTransparentBase}=this.state;const BGCanvas=document.createElement("canvas");BGCanvas.width=canvas.width;BGCanvas.height=canvas.height;const BGCtx=BGCanvas.getContext("2d");if(!BGCtx)return;const width=3*baseWidth;const height=canvas.height-2*padding;const x=(canvas.width-width)/2;const y=padding;const tagNun=11;const markedAngles=Array.from({length:tagNun},(_,i)=>min+Math.round(i*(max-min)/(tagNun-1)/step)*step);BGCtx.fillStyle=fillStyleTransparentBase;BGCtx.fillRect(x,y,width,height);for(let i=min;i<=max;i+=step){const isMarked=markedAngles.includes(i);const tagY=height*(max-i)/(max-min)+padding;BGCtx.beginPath();BGCtx.moveTo(x-(isMarked?baseWidth:baseWidth/2),tagY);BGCtx.lineTo(x,tagY);BGCtx.strokeStyle=fillStyle;BGCtx.lineWidth=isMarked?lineWidth:lineWidth/2;BGCtx.stroke();if(isMarked){BGCtx.fillStyle=fillStyle;BGCtx.font="12px Arial";BGCtx.textAlign="right";BGCtx.textBaseline="middle";BGCtx.fillText(i.toString(),x-1.2*baseWidth,tagY)}}this.state.BGCanvas=BGCanvas}}class Heatmap extends Engine{init(props){super.init(props);const{colors}=props;this.updateProps({colors,data:[]});this.resize()}updateProps(e){super.updateProps(e);const{range=this.state.range,colors=this.state.colors}=e;if(range&&colors){const[rangeMin,rangeMax]=range;if(isNumberAlias(rangeMin)&&isNumberAlias(rangeMax)&&rangeMin<rangeMax){if(this.state.CI)this.state.CI.setColors(colors,rangeMin,rangeMax,.1);else this.updateProps({CI:new ColorInterpolator(colors,rangeMin,rangeMax,.1)})}}}clearImageData(){const{ctx,canvas:{height,width}}=this.state;if(height&&width)this.updateProps({imageData:ctx.createImageData(width,height)})}clearRect(){super.clearRect();this.clearImageData()}clear(){this.clearRect();this.updateProps({data:[]})}resize(){super.resize();this.clearImageData()}setRange(range){if(!range)return;this.updateProps({range});this.draw()}render(data){if(data?.length>=0){this.state.data=data;this.draw()}}draw(){const{imageData,data,canvas,ctx,CI}=this.state;if(0===data.length)return;fillImageData(canvas.width,canvas.height,data,imageData.data,value=>CI.getColor(value));ctx.putImageData(imageData,0,0)}}const ENABLE_AREA_GRADIENT=false;class Series extends Engine{init(props){super.init(props);const{series,barValue2Color,disabledClearRect,colors}=props;this.updateProps({interval:0,series:{},barValue2Color,disabledClearRect,colors});this.setRange(this.state.range);if(Array.isArray(series))series.forEach(e=>{this.setSeries(e)})}updateProps(e){super.updateProps(e);const{colors=this.state.colors}=e;if(this.state.barValue2Color&&!this.state.CI&&colors)this.updateProps({CI:new ColorInterpolator(colors,0,100,.1)})}clear(){super.clearRect();const{series}=this.state;const seriesArray=Object.entries(series);seriesArray.forEach(([name,i])=>{this.setSeries({...i,name,data:void 0})})}setRange(range){this.updateProps({range:[range[0],range[1],range[1]-range[0]]});this.draw()}setIntervel(interval){if(!interval)return;this.updateProps({interval})}setSeries(e){if(e?.name){const{name}=e;const{series}=this.state;series[name]={thickness:1,display:true,color:"#000000",type:enums_GraphicType.Line,data:void 0,path:void 0,orientation:enums_OrientationType.Vertical,...series[name],...e};this.draw()}}render(e){e&&this.setSeries(e);this.draw()}draw(){const{series,ctx,disabledClearRect}=this.state;if(!disabledClearRect)this.clearRect();const seriesArray=Object.values(series);const{range,canvas}=this.state;const min=range[0];const max=range[1];const{width,height}=canvas;const rangeY=max-min;ctx.save();ctx.translate(.5,.5);for(let s=0;s<seriesArray.length;s+=1){const seriesItem=seriesArray[s];const{display,type,data,orientation}=seriesItem;const thickness=seriesItem.thickness??DEFAULTS.THICKNESS;const color=seriesItem.color??DEFAULTS.COLOR;if(!data||!display)continue;const len=data.length;const per=width/len;switch(type){case enums_GraphicType.Line:case enums_GraphicType.Stepline:ctx.lineWidth=thickness;ctx.strokeStyle=color;break;case enums_GraphicType.Circle:case enums_GraphicType.Rect:case enums_GraphicType.Bar:case enums_GraphicType.Area:ctx.fillStyle=color;break}if(type===enums_GraphicType.Line){let points=[];for(let i=0;i<len;i+=1){const value=data[i];if(void 0===value||Number.isNaN(value)){if(points.length>0){ctx.beginPath();ctx.moveTo(points[0][0],points[0][1]);for(let j=1;j<points.length;j++)ctx.lineTo(points[j][0],points[j][1]);ctx.lineWidth=thickness;ctx.strokeStyle=color;ctx.stroke();points=[]}}else{const x=Math.round((i+.5)*per);const y=Math.round((value-min)/rangeY*height);points.push([x,y])}}if(points.length>0){ctx.beginPath();ctx.moveTo(points[0][0],points[0][1]);for(let j=1;j<points.length;j++)ctx.lineTo(points[j][0],points[j][1]);ctx.lineWidth=thickness;ctx.strokeStyle=color;ctx.stroke()}}if(type===enums_GraphicType.Stepline){let points=[];for(let i=0;i<len;i+=1){const value=data[i];if(void 0===value||Number.isNaN(value)){if(points.length>0){ctx.beginPath();ctx.moveTo(points[0][0],points[0][1]);for(let j=1;j<points.length;j++){const[_,py]=points[j-1];const[cx,cy]=points[j];ctx.lineTo(cx,py);ctx.lineTo(cx,cy)}const[lx,ly]=points[points.length-1];const endX=Math.round(lx+per);ctx.lineTo(endX,ly);ctx.lineWidth=thickness;ctx.strokeStyle=color;ctx.stroke();points=[]}}else{const x=Math.round(i*per);const y=Math.round((value-min)/rangeY*height);points.push([x,y])}}if(points.length>0){ctx.beginPath();ctx.moveTo(points[0][0],points[0][1]);for(let j=1;j<points.length;j++){const[_,py]=points[j-1];const[cx,cy]=points[j];ctx.lineTo(cx,py);ctx.lineTo(cx,cy)}const[lx,ly]=points[points.length-1];const endX=Math.round(lx+per);ctx.lineTo(endX,ly);ctx.lineWidth=thickness;ctx.strokeStyle=color;ctx.stroke()}}if(type===enums_GraphicType.Circle){const radius=+thickness/2;const endAngle=2*Math.PI;for(let i=0;i<len;i+=1){const x=Math.round((i+.5)*per);const y=Math.round((data[i]-min)/rangeY*height);ctx.beginPath();ctx.arc(x,y,radius,0,endAngle);ctx.fillStyle=color;ctx.fill()}}if(type===enums_GraphicType.Rect){const side=+thickness;for(let i=0;i<len;i+=1){const x=Math.round((i+.5)*per);const y=Math.round((data[i]-min)/rangeY*height);ctx.beginPath();ctx.rect(x,y,side,side);ctx.fillStyle=color;ctx.fill()}}if(type===enums_GraphicType.Area){let points=[];const{r,g,b}=hexToRGBA(color??"#000000");const drawArea=pointsToDraw=>{if(0===pointsToDraw.length)return;const[firstX,firstY]=pointsToDraw[0];const[lastX,lastY]=pointsToDraw[pointsToDraw.length-1];const endX=Math.round(lastX+per);ctx.beginPath();ctx.moveTo(firstX,0);ctx.lineTo(firstX,firstY);for(let j=1;j<pointsToDraw.length;j++){const[_,prevY]=pointsToDraw[j-1];const[currX,currY]=pointsToDraw[j];ctx.lineTo(currX,prevY);ctx.lineTo(currX,currY)}ctx.lineTo(endX,lastY);ctx.lineTo(endX,0);ctx.closePath();if(ENABLE_AREA_GRADIENT){const gradient=ctx.createLinearGradient(0,0,0,height);gradient.addColorStop(0,`rgba(${r}, ${g}, ${b}, 0)`);gradient.addColorStop(1,`rgba(${r}, ${g}, ${b}, 0.5)`);ctx.fillStyle=gradient}else ctx.fillStyle=`rgba(${r}, ${g}, ${b}, 1)`;ctx.fill()};for(let i=0;i<len;i+=1){const value=data[i];if(void 0===value||Number.isNaN(value)){drawArea(points);points=[]}else points.push([Math.round(i*per),Math.round((value-min)/rangeY*height)])}drawArea(points)}if(type===enums_GraphicType.Bar){if(orientation===enums_OrientationType.Horizontal){const h=height/len;const hh=Math.round(h);for(let i=0;i<len;i+=1){const y=Math.round(height-(i+1)*h);const w=Math.round((data[i]-min)/rangeY*width);const x=width-w;ctx.beginPath();ctx.rect(x,y,w,hh);ctx.fillStyle=color;ctx.fill()}}else for(let i=0;i<len;i+=1){const startX=Math.floor(i*width/len);const endX=Math.floor((i+1)*width/len);const w=endX-startX;const h=Math.round((data[i]-min)/rangeY*height);ctx.beginPath();ctx.rect(startX,0,w,h);ctx.fillStyle=this.state.barValue2Color?this.state.CI?.getColor(data[i]).hax??color:color;ctx.fill()}}}ctx.restore()}}function getCoordinates(radius,padding,angle){const x=radius+(radius-padding)*Math.cos(angle);const y=radius+(radius-padding)*Math.sin(angle);return[x,y]}class CircularBase extends Engine{getSpecialTickConfig(_angle){}getAngleOffset(){return 0}init(props){super.init(props);this.state.canvas.style.transform="scaleY(1)";const fillStyle=props.fillStyle??DEFAULTS.FILL_STYLE;const fillStylePrimary=props.fillStylePrimary??DEFAULTS.FILL_STYLE_PRIMARY;const fillStyleTransparentBase=props.fillStyleTransparentBase??DEFAULTS.FILL_STYLE_TRANSPARENT_BASE;const markedNum=12;const baseWidth=6;this.updateProps({fillStyle,fillStylePrimary,fillStyleTransparentBase,baseWidth,padding:10*baseWidth,ticksStep:6,ticksRenderLength:5,markedAngles:Array.from({length:markedNum},(_,i)=>360/markedNum*i),color2intensity:color2intensity(fillStylePrimary),data:{series:[],range:[0,0],yawAngle:0,isNorthFacing:true}})}clear(){this.clearRect()}resize(){super.resize();if(this.state.markedAngles)this.drawBackground();if(this.state.data)this.drawTicks()}render(data){const isYawAngleChange=void 0!==data.yawAngle&&data.yawAngle!==this.state.data?.yawAngle;this.state.data={...this.state.data,...data};if(isYawAngleChange)this.drawTicks();this.draw()}drawTicks(){const{canvas,baseWidth,fillStyle,fillStylePrimary,ticksStep,ticksRenderLength,markedAngles,data}=this.state;const{yawAngle=0,isNorthFacing=true}=data;const ticksCanvas=document.createElement("canvas");ticksCanvas.width=canvas.width;ticksCanvas.height=canvas.height;const ticksCtx=ticksCanvas.getContext("2d");if(!ticksCtx)return;const radius=canvas.width/2;const northYawgle=isNorthFacing?0:-yawAngle;for(let i=0;i<360;i+=ticksStep){const angle=(i+northYawgle)*Math.PI/180;const isMarked=markedAngles.includes(i);const length=ticksRenderLength*(isMarked?1.2:1);const[x,y]=getCoordinates(radius,3.5*baseWidth+length+(isMarked?3:0),angle);const[xOuter,yOuter]=getCoordinates(radius,3.5*baseWidth,angle);ticksCtx.beginPath();ticksCtx.moveTo(x,y);ticksCtx.lineTo(xOuter,yOuter);ticksCtx.strokeStyle=fillStyle;ticksCtx.lineWidth=isMarked?2:.5;ticksCtx.stroke()}ticksCtx.font=`${2*baseWidth}px Arial`;ticksCtx.textAlign="center";ticksCtx.textBaseline="middle";markedAngles.forEach(angle=>{const radian=(angle+northYawgle-90)*Math.PI/180;const[x,y]=getCoordinates(radius,8*baseWidth,radian);const specialConfig=this.getSpecialTickConfig(angle);if(specialConfig){ticksCtx.fillStyle=specialConfig.color;ticksCtx.fillText(specialConfig.alias,x,y)}else{ticksCtx.fillStyle=fillStyle;ticksCtx.fillText(angle.toString(),x,y)}});const arrowWidth=canvas.width/18;ticksCtx.translate(canvas.width/2,canvas.height/2);ticksCtx.scale(1,-1);ticksCtx.rotate((isNorthFacing?-yawAngle:0)*Math.PI/180);ticksCtx.lineJoin="round";ticksCtx.lineCap="round";ticksCtx.strokeStyle=fillStylePrimary;ticksCtx.fillStyle=fillStylePrimary;ticksCtx.lineWidth=arrowWidth/6;ticksCtx.beginPath();ticksCtx.moveTo(0,arrowWidth/2);ticksCtx.lineTo(arrowWidth/2,-arrowWidth/2);ticksCtx.lineTo(0,arrowWidth/2-1/((1+Math.sqrt(5))/2)*arrowWidth);ticksCtx.lineTo(-arrowWidth/2,-arrowWidth/2);ticksCtx.lineTo(0,arrowWidth/2);ticksCtx.fill();ticksCtx.stroke();this.state.ticksCanvas=ticksCanvas}drawBackground(){const{canvas,baseWidth,padding,fillStyleTransparentBase}=this.state;const BGCanvas=document.createElement("canvas");BGCanvas.width=canvas.width;BGCanvas.height=canvas.height;const BGCtx=BGCanvas.getContext("2d");if(!BGCtx)return;const radius=canvas.width/2;BGCtx.beginPath();BGCtx.arc(radius,radius,radius-2*baseWidth,0,2*Math.PI);BGCtx.strokeStyle=fillStyleTransparentBase;BGCtx.lineWidth=baseWidth;BGCtx.stroke();new Array(5).fill(1).forEach((_,i)=>{BGCtx.beginPath();BGCtx.arc(radius,radius,(radius-padding)*(i+1)/5,0,2*Math.PI);BGCtx.strokeStyle=fillStyleTransparentBase;BGCtx.lineWidth=baseWidth/4;BGCtx.stroke()});this.state.BGCanvas=BGCanvas}draw(){const{data,canvas,ctx,baseWidth,padding,BGCanvas,ticksCanvas,color2intensity:c2i,fillStyleTransparentBase}=this.state;const{series,range,isNorthFacing=true,yawAngle=0}=data;if(!series)return;this.clearRect();const radius=canvas.width/2;const northYawgle=isNorthFacing?0:-yawAngle;const angleOffset=this.getAngleOffset();if(BGCanvas)ctx.drawImage(BGCanvas,0,0);if(range){const sliceAngle=360/range.length;range.forEach((value,index)=>{const startAngle=(index*sliceAngle+northYawgle+angleOffset)*Math.PI/180;const endAngle=((index+1)*sliceAngle+northYawgle+angleOffset)*Math.PI/180;ctx.beginPath();ctx.moveTo(radius,radius);ctx.arc(radius,radius,radius-padding,startAngle,endAngle);ctx.closePath();ctx.fillStyle=c2i[value];ctx.fill()})}series.forEach(({value,color,lineWidth=baseWidth,radio=1})=>{if(null==value)return;const pointerAngle=(value+northYawgle)*Math.PI/180-Math.PI/2;const[startX,startY]=getCoordinates(radius,radius,pointerAngle);const[endX,endY]=getCoordinates(radius,padding+(1-radio)*(radius-padding)+lineWidth/2,pointerAngle);if(1!==radio){ctx.beginPath();ctx.moveTo(...getCoordinates(radius,padding+lineWidth/2,pointerAngle));ctx.lineTo(endX,endY);ctx.strokeStyle=fillStyleTransparentBase;ctx.stroke()}ctx.beginPath();ctx.moveTo(endX,endY);ctx.lineTo(startX,startY);ctx.strokeStyle=color;ctx.lineWidth=lineWidth;ctx.lineJoin="round";ctx.lineCap="round";ctx.stroke()});if(ticksCanvas)ctx.drawImage(ticksCanvas,0,0)}}const SPECIAL_TICK_CONFIG={0:{color:"#ff4d4f",alias:"北"},180:{color:"#1890ff",alias:"南"}};class Dial extends CircularBase{getSpecialTickConfig(angle){return SPECIAL_TICK_CONFIG[angle]}getAngleOffset(){return -90}}class Radar extends CircularBase{getSpecialTickConfig(_angle){}getAngleOffset(){return 0}}class IQ extends Engine{init(props){super.init(props);this.updateProps({lineColor:props.lineColor??DEFAULTS.LINE_COLOR,pointColor:props.pointColor??DEFAULTS.POINT_COLOR})}clear(){this.clearRect();this.updateProps({data:{IData:[],QData:[]}})}render(data){if(data.IData&&data.QData){this.state.data=data;this.draw()}}draw(){const{data,canvas,pointColor,lineColor,ctx}=this.state;if(!data)return;const{IData,QData}=data;if(0===IData.length||0===QData.length)return;const{width,height}=ctx.canvas;ctx.clearRect(0,0,canvas.width,canvas.height);const[minX,maxX]=getMinMax(IData);const[minY,maxY]=getMinMax([minX,maxX,...QData]);const xScale=width/(maxX-minX);const yScale=height/(maxY-minY);const pointRadius=2;const points=IData.map((xVal,i)=>{const x=(xVal-minX)*xScale;const y=(QData[i]-minY)*yScale;return{x,y}});ctx.beginPath();points.forEach((point,i)=>{ctx[0===i?"moveTo":"lineTo"](point.x,point.y)});ctx.strokeStyle=lineColor;ctx.lineWidth=1;ctx.stroke();points.forEach(point=>{ctx.beginPath();ctx.arc(point.x,point.y,pointRadius,0,2*Math.PI);ctx.fillStyle=pointColor;ctx.fill()})}}const EYE_CONFIG={Y_RANGE:{MIN:-1,MAX:1},SEGMENT_SIZE:5};class IQEye extends Engine{init(props){super.init(props);this.updateProps({lineColor:props.lineColor??DEFAULTS.LINE_COLOR,pointColor:props.pointColor??DEFAULTS.POINT_COLOR})}clear(){this.clearRect();this.updateProps({data:{IData:[],QData:[]}})}render(data){if(data.IData&&data.QData){this.state.data=data;this.draw()}}draw(){if(!this.state.data)return;const{data:{IData,QData},ctx,canvas}=this.state;if(0===IData.length||0===QData.length)return;this.clearRect();const min=EYE_CONFIG.Y_RANGE.MIN;const rangeY=EYE_CONFIG.Y_RANGE.MAX-EYE_CONFIG.Y_RANGE.MIN;const{width,height}=canvas;const offscreenCanvas=document.createElement("canvas");offscreenCanvas.width=width;offscreenCanvas.height=height;const offCtx=offscreenCanvas.getContext("2d",{willReadFrequently:true});if(!offCtx)return;this.drawAllSegments(IData,QData,min,rangeY,width,height,offCtx);const imageData=offCtx.getImageData(0,0,width,height);const data=imageData.data;this.applyColorGradient(data);ctx.putImageData(imageData,0,0)}drawAllSegments(iData,qData,min,rangeY,width,height,ctx){ctx.clearRect(0,0,width,height);const accumulator=new Uint8Array(width*height);this.drawSegmentedData(iData,min,rangeY,width,height,accumulator);this.drawSegmentedData(qData,min,rangeY,width,height,accumulator);const imageData=ctx.getImageData(0,0,width,height);const data=imageData.data;for(let i=0;i<accumulator.length;i++)if(accumulator[i]>0){const index=4*i;data[index]=255;data[index+1]=255;data[index+2]=255;data[index+3]=Math.min(50*accumulator[i],255)}ctx.putImageData(imageData,0,0)}applyColorGradient(data){const{lineColor,pointColor}=this.state;const startColor=parseColor(lineColor);const endColor=parseColor(pointColor);for(let i=0;i<data.length;i+=4)if(data[i+3]>0){const intensity=data[i+3]/255;data[i]=Math.round(startColor.r+(endColor.r-startColor.r)*intensity);data[i+1]=Math.round(startColor.g+(endColor.g-startColor.g)*intensity);data[i+2]=Math.round(startColor.b+(endColor.b-startColor.b)*intensity)}}drawSegmentedData(data,min,rangeY,width,height,accumulator){const segmentSize=EYE_CONFIG.SEGMENT_SIZE;const segmentCount=Math.ceil(data.length/segmentSize);const pointSpacing=width/(segmentSize-1);for(let segment=0;segment<segmentCount;segment++){const points=[];for(let i=0;i<segmentSize;i++){const dataIndex=segment*segmentSize+i;if(dataIndex>=data.length)continue;const value=data[dataIndex];if(void 0!==value){const x=Math.round(i*pointSpacing);const y=Math.round((value-min)/rangeY*height);points.push({x,y})}}if(points.length>1)for(let i=0;i<points.length-1;i++)this.drawLine(points[i].x,points[i].y,points[i+1].x,points[i+1].y,width,height,accumulator)}}drawLine(x0,y0,x1,y1,width,height,accumulator){const dx=Math.abs(x1-x0);const dy=Math.abs(y1-y0);const sx=x0<x1?1:-1;const sy=y0<y1?1:-1;let x=x0;let y=y0;let err=dx-dy;while(true){if(x>=0&&x<width&&y>=0&&y<height){const index=y*width+x;if(accumulator[index]<255)accumulator[index]++}if(x===x1&&y===y1)break;const e2=2*err;if(e2>-dy){err-=dy;x+=sx}if(e2<dx){err+=dx;y+=sy}}}}class WebGLEngine{state;constructor(props){this.state={id:props.id,container:void 0,canvas:null,gl:null,range:props.range??[-20,100]};this.init(props)}updateProps(e){this.state={...this.state,...e}}init(_props){const{id}=this.state;const container=document.getElementById(id);const canvas=document.createElement("canvas");const gl=canvas.getContext("webgl2",{alpha:true,antialias:false,premultipliedAlpha:false,preserveDrawingBuffer:false});if(!gl){console.error("WebGL 2.0 not supported");return}canvas.style.transform="scaleY(-1)";this.updateProps({container:container??void 0,canvas,gl});container?.appendChild(canvas);this.resize(false)}compileShader(type,source){const{gl}=this.state;const shader=gl.createShader(type);if(!shader)return null;gl.shaderSource(shader,source);gl.compileShader(shader);if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){console.error("Shader compile error:",gl.getShaderInfoLog(shader));gl.deleteShader(shader);return null}return shader}createProgram(shaderSource){const{gl}=this.state;const vertexShader=this.compileShader(gl.VERTEX_SHADER,shaderSource.vertex);const fragmentShader=this.compileShader(gl.FRAGMENT_SHADER,shaderSource.fragment);if(!vertexShader||!fragmentShader)return null;const program=gl.createProgram();if(!program)return null;gl.attachShader(program,vertexShader);gl.attachShader(program,fragmentShader);gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS)){console.error("Program link error:",gl.getProgramInfoLog(program));gl.deleteProgram(program);return null}gl.deleteShader(vertexShader);gl.deleteShader(fragmentShader);return program}createTexture(){const{gl}=this.state;const texture=gl.createTexture();if(!texture)return null;gl.bindTexture(gl.TEXTURE_2D,texture);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);return texture}createQuadBuffer(){const{gl}=this.state;const buffer=gl.createBuffer();if(!buffer)return null;gl.bindBuffer(gl.ARRAY_BUFFER,buffer);const vertices=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);return buffer}clearRect(){const{gl}=this.state;gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT)}resize(draw=true){const{canvas,container,gl}=this.state;if(!container)return;const{clientWidth,clientHeight}=container;if(!clientWidth||!clientHeight)return;canvas.width=clientWidth;canvas.height=clientHeight;gl.viewport(0,0,clientWidth,clientHeight);if(draw)setTimeout(()=>{this.draw()})}draw(){}dispose(){const{gl,canvas}=this.state;if(gl){const loseContext=gl.getExtension("WEBGL_lose_context");if(loseContext)loseContext.loseContext()}canvas?.remove()}}const VERTEX_SHADER=`#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
|
+
// 将 [-1, 1] 映射到 [0, 1]
|
|
8
|
+
v_texCoord = a_position * 0.5 + 0.5;
|
|
9
|
+
}
|
|
10
|
+
`;const FRAGMENT_SHADER=`#version 300 es
|
|
11
|
+
precision highp float;
|
|
12
|
+
|
|
13
|
+
in vec2 v_texCoord;
|
|
14
|
+
out vec4 fragColor;
|
|
15
|
+
|
|
16
|
+
uniform sampler2D u_dataTexture;
|
|
17
|
+
uniform sampler2D u_colorTexture;
|
|
18
|
+
uniform float u_rangeMin;
|
|
19
|
+
uniform float u_rangeMax;
|
|
20
|
+
|
|
21
|
+
void main() {
|
|
22
|
+
// 采样数据纹理获取原始值
|
|
23
|
+
float value = texture(u_dataTexture, v_texCoord).r;
|
|
24
|
+
|
|
25
|
+
// 归一化到 [0, 1]
|
|
26
|
+
float normalized = (value - u_rangeMin) / (u_rangeMax - u_rangeMin);
|
|
27
|
+
normalized = clamp(normalized, 0.0, 1.0);
|
|
28
|
+
|
|
29
|
+
// 从颜色查找纹理获取颜色
|
|
30
|
+
fragColor = texture(u_colorTexture, vec2(normalized, 0.5));
|
|
31
|
+
}
|
|
32
|
+
`;class HeatmapWebGL extends WebGLEngine{init(props){super.init(props);const{gl}=this.state;if(!gl)return;const{colors=[],range}=props;const program=this.createProgram({vertex:VERTEX_SHADER,fragment:FRAGMENT_SHADER});const quadBuffer=this.createQuadBuffer();const dataTexture=this.createTexture();const colorTexture=this.createTexture();const[rangeMin,rangeMax]=range??[-20,100];const CI=colors.length>0?new ColorInterpolator(colors,rangeMin,rangeMax,.1):null;this.updateProps({data:[],colors,program,quadBuffer,dataTexture,colorTexture,dataWidth:0,dataHeight:0,CI});if(CI)this.updateColorTexture();this.resize()}updateColorTexture(){const{gl,colorTexture,CI,range}=this.state;if(!gl||!colorTexture||!CI)return;const[rangeMin,rangeMax]=range;const size=256;const colorData=new Uint8Array(4*size);for(let i=0;i<size;i++){const value=rangeMin+i/(size-1)*(rangeMax-rangeMin);const color=CI.getColor(value);colorData[4*i]=color.r;colorData[4*i+1]=color.g;colorData[4*i+2]=color.b;colorData[4*i+3]=color.a}gl.bindTexture(gl.TEXTURE_2D,colorTexture);gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,size,1,0,gl.RGBA,gl.UNSIGNED_BYTE,colorData)}updateDataTexture(){const{gl,dataTexture,data}=this.state;if(!gl||!dataTexture||0===data.length)return;const height=data.length;const width=data[0]?.length??0;if(0===width)return;const floatData=new Float32Array(width*height);for(let row=0;row<height;row++){const rowData=data[row];for(let col=0;col<width;col++)floatData[row*width+col]=rowData[col]??0}gl.bindTexture(gl.TEXTURE_2D,dataTexture);gl.texImage2D(gl.TEXTURE_2D,0,gl.R32F,width,height,0,gl.RED,gl.FLOAT,floatData);this.updateProps({dataWidth:width,dataHeight:height})}updateProps(e){super.updateProps(e);const{colors=this.state.colors,range=this.state.range}=e;if((e.colors||e.range)&&colors.length>0){const[rangeMin,rangeMax]=range;if(this.state.CI)this.state.CI.setColors(colors,rangeMin,rangeMax,.1);else this.state.CI=new ColorInterpolator(colors,rangeMin,rangeMax,.1);this.updateColorTexture()}}setRange(range){if(!range)return;this.updateProps({range});this.draw()}clear(){this.clearRect();this.updateProps({data:[]})}render(data){if(data?.length>=0){this.state.data=data;this.updateDataTexture();this.draw()}}draw(){const{gl,program,quadBuffer,dataTexture,colorTexture,data,range}=this.state;if(!gl||!program||!quadBuffer||!dataTexture||!colorTexture)return;if(0===data.length)return;const[rangeMin,rangeMax]=range;gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.useProgram(program);gl.bindBuffer(gl.ARRAY_BUFFER,quadBuffer);const positionLoc=gl.getAttribLocation(program,"a_position");gl.enableVertexAttribArray(positionLoc);gl.vertexAttribPointer(positionLoc,2,gl.FLOAT,false,0,0);gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,dataTexture);gl.uniform1i(gl.getUniformLocation(program,"u_dataTexture"),0);gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,colorTexture);gl.uniform1i(gl.getUniformLocation(program,"u_colorTexture"),1);gl.uniform1f(gl.getUniformLocation(program,"u_rangeMin"),rangeMin);gl.uniform1f(gl.getUniformLocation(program,"u_rangeMax"),rangeMax);gl.drawArrays(gl.TRIANGLES,0,6)}resize(){super.resize()}dispose(){const{gl,program,quadBuffer,dataTexture,colorTexture}=this.state;if(gl){if(program)gl.deleteProgram(program);if(quadBuffer)gl.deleteBuffer(quadBuffer);if(dataTexture)gl.deleteTexture(dataTexture);if(colorTexture)gl.deleteTexture(colorTexture)}super.dispose()}}const MAX_VERTICES=36e4;const SeriesWebGL_VERTEX_SHADER=`#version 300 es
|
|
33
|
+
in vec2 a_position;
|
|
34
|
+
uniform vec2 u_resolution;
|
|
35
|
+
uniform float u_rangeMin;
|
|
36
|
+
uniform float u_rangeMax;
|
|
37
|
+
|
|
38
|
+
void main() {
|
|
39
|
+
// x: 归一化到 [-1, 1]
|
|
40
|
+
float x = (a_position.x / u_resolution.x) * 2.0 - 1.0;
|
|
41
|
+
// y: 根据 range 归一化到 [-1, 1]
|
|
42
|
+
float y = ((a_position.y - u_rangeMin) / (u_rangeMax - u_rangeMin)) * 2.0 - 1.0;
|
|
43
|
+
gl_Position = vec4(x, y, 0.0, 1.0);
|
|
44
|
+
}
|
|
45
|
+
`;const SeriesWebGL_FRAGMENT_SHADER=`#version 300 es
|
|
46
|
+
precision highp float;
|
|
47
|
+
uniform vec4 u_color;
|
|
48
|
+
out vec4 fragColor;
|
|
49
|
+
|
|
50
|
+
void main() {
|
|
51
|
+
fragColor = u_color;
|
|
52
|
+
}
|
|
53
|
+
`;const VERTEX_SHADER_WITH_COLOR=`#version 300 es
|
|
54
|
+
in vec2 a_position;
|
|
55
|
+
in vec4 a_color;
|
|
56
|
+
out vec4 v_color;
|
|
57
|
+
uniform vec2 u_resolution;
|
|
58
|
+
uniform float u_rangeMin;
|
|
59
|
+
uniform float u_rangeMax;
|
|
60
|
+
|
|
61
|
+
void main() {
|
|
62
|
+
float x = (a_position.x / u_resolution.x) * 2.0 - 1.0;
|
|
63
|
+
float y = ((a_position.y - u_rangeMin) / (u_rangeMax - u_rangeMin)) * 2.0 - 1.0;
|
|
64
|
+
gl_Position = vec4(x, y, 0.0, 1.0);
|
|
65
|
+
v_color = a_color;
|
|
66
|
+
}
|
|
67
|
+
`;const FRAGMENT_SHADER_WITH_COLOR=`#version 300 es
|
|
68
|
+
precision highp float;
|
|
69
|
+
in vec4 v_color;
|
|
70
|
+
out vec4 fragColor;
|
|
71
|
+
|
|
72
|
+
void main() {
|
|
73
|
+
fragColor = v_color;
|
|
74
|
+
}
|
|
75
|
+
`;const POINT_VERTEX_SHADER=`#version 300 es
|
|
76
|
+
in vec2 a_position;
|
|
77
|
+
uniform vec2 u_resolution;
|
|
78
|
+
uniform float u_rangeMin;
|
|
79
|
+
uniform float u_rangeMax;
|
|
80
|
+
uniform float u_pointSize;
|
|
81
|
+
|
|
82
|
+
void main() {
|
|
83
|
+
float x = (a_position.x / u_resolution.x) * 2.0 - 1.0;
|
|
84
|
+
float y = ((a_position.y - u_rangeMin) / (u_rangeMax - u_rangeMin)) * 2.0 - 1.0;
|
|
85
|
+
gl_Position = vec4(x, y, 0.0, 1.0);
|
|
86
|
+
gl_PointSize = u_pointSize;
|
|
87
|
+
}
|
|
88
|
+
`;const POINT_CIRCLE_FRAGMENT_SHADER=`#version 300 es
|
|
89
|
+
precision highp float;
|
|
90
|
+
uniform vec4 u_color;
|
|
91
|
+
out vec4 fragColor;
|
|
92
|
+
|
|
93
|
+
void main() {
|
|
94
|
+
vec2 coord = gl_PointCoord - vec2(0.5);
|
|
95
|
+
if (length(coord) > 0.5) discard;
|
|
96
|
+
fragColor = u_color;
|
|
97
|
+
}
|
|
98
|
+
`;const POINT_RECT_FRAGMENT_SHADER=`#version 300 es
|
|
99
|
+
precision highp float;
|
|
100
|
+
uniform vec4 u_color;
|
|
101
|
+
out vec4 fragColor;
|
|
102
|
+
|
|
103
|
+
void main() {
|
|
104
|
+
fragColor = u_color;
|
|
105
|
+
}
|
|
106
|
+
`;class SeriesWebGL extends WebGLEngine{fillProgramWithColor=null;pointCircleProgram=null;pointRectProgram=null;lineUniforms=null;lineAttribs=null;fillUniforms=null;fillAttribs=null;fillColorUniforms=null;fillColorAttribs=null;pointCircleUniforms=null;pointCircleAttribs=null;pointRectUniforms=null;pointRectAttribs=null;vertexDataBuffer=new Float32Array(MAX_VERTICES);vertexColorBuffer=new Float32Array(MAX_VERTICES);init(props){super.init(props);const{gl}=this.state;if(!gl)return;const{series,barValue2Color,disabledClearRect,colors=[]}=props;const lineProgram=this.createProgram({vertex:SeriesWebGL_VERTEX_SHADER,fragment:SeriesWebGL_FRAGMENT_SHADER});const fillProgram=this.createProgram({vertex:SeriesWebGL_VERTEX_SHADER,fragment:SeriesWebGL_FRAGMENT_SHADER});this.fillProgramWithColor=this.createProgram({vertex:VERTEX_SHADER_WITH_COLOR,fragment:FRAGMENT_SHADER_WITH_COLOR});const pointProgram=this.createProgram({vertex:POINT_VERTEX_SHADER,fragment:POINT_CIRCLE_FRAGMENT_SHADER});this.pointCircleProgram=pointProgram;this.pointRectProgram=this.createProgram({vertex:POINT_VERTEX_SHADER,fragment:POINT_RECT_FRAGMENT_SHADER});this.cacheLocations(gl,lineProgram,fillProgram);const vertexBuffer=gl.createBuffer();const CI=barValue2Color&&colors.length>0?new ColorInterpolator(colors,0,100,.1):null;const colorTexture=this.createTexture();this.updateProps({series:{},colors,interval:props.interval??0,barValue2Color,disabledClearRect,lineProgram,fillProgram,pointProgram,vertexBuffer,colorTexture,CI});if(Array.isArray(series))series.forEach(e=>{this.setSeries(e)});gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA)}cacheLocations(gl,lineProgram,fillProgram){if(lineProgram){this.lineUniforms={resolution:gl.getUniformLocation(lineProgram,"u_resolution"),rangeMin:gl.getUniformLocation(lineProgram,"u_rangeMin"),rangeMax:gl.getUniformLocation(lineProgram,"u_rangeMax"),color:gl.getUniformLocation(lineProgram,"u_color")};this.lineAttribs={position:gl.getAttribLocation(lineProgram,"a_position")}}if(fillProgram){this.fillUniforms={resolution:gl.getUniformLocation(fillProgram,"u_resolution"),rangeMin:gl.getUniformLocation(fillProgram,"u_rangeMin"),rangeMax:gl.getUniformLocation(fillProgram,"u_rangeMax"),color:gl.getUniformLocation(fillProgram,"u_color")};this.fillAttribs={position:gl.getAttribLocation(fillProgram,"a_position")}}if(this.fillProgramWithColor){this.fillColorUniforms={resolution:gl.getUniformLocation(this.fillProgramWithColor,"u_resolution"),rangeMin:gl.getUniformLocation(this.fillProgramWithColor,"u_rangeMin"),rangeMax:gl.getUniformLocation(this.fillProgramWithColor,"u_rangeMax"),color:null};this.fillColorAttribs={position:gl.getAttribLocation(this.fillProgramWithColor,"a_position"),color:gl.getAttribLocation(this.fillProgramWithColor,"a_color")}}if(this.pointCircleProgram){this.pointCircleUniforms={resolution:gl.getUniformLocation(this.pointCircleProgram,"u_resolution"),rangeMin:gl.getUniformLocation(this.pointCircleProgram,"u_rangeMin"),rangeMax:gl.getUniformLocation(this.pointCircleProgram,"u_rangeMax"),color:gl.getUniformLocation(this.pointCircleProgram,"u_color"),pointSize:gl.getUniformLocation(this.pointCircleProgram,"u_pointSize")};this.pointCircleAttribs={position:gl.getAttribLocation(this.pointCircleProgram,"a_position")}}if(this.pointRectProgram){this.pointRectUniforms={resolution:gl.getUniformLocation(this.pointRectProgram,"u_resolution"),rangeMin:gl.getUniformLocation(this.pointRectProgram,"u_rangeMin"),rangeMax:gl.getUniformLocation(this.pointRectProgram,"u_rangeMax"),color:gl.getUniformLocation(this.pointRectProgram,"u_color"),pointSize:gl.getUniformLocation(this.pointRectProgram,"u_pointSize")};this.pointRectAttribs={position:gl.getAttribLocation(this.pointRectProgram,"a_position")}}}updateProps(e){super.updateProps(e);const{colors=this.state.colors}=e;if(this.state.barValue2Color&&!this.state.CI&&colors?.length>0)this.state.CI=new ColorInterpolator(colors,0,100,.1)}clear(){this.clearRect();const{series}=this.state;const seriesArray=Object.entries(series);seriesArray.forEach(([name,i])=>{this.setSeries({...i,name,data:void 0})})}setRange(range){this.updateProps({range:[range[0],range[1],range[1]-range[0]]});this.draw()}setIntervel(interval){if(!interval)return;this.updateProps({interval})}setSeries(e){if(e?.name){const{name}=e;const{series}=this.state;series[name]={thickness:1,display:true,color:"#000000",type:enums_GraphicType.Line,data:void 0,orientation:enums_OrientationType.Vertical,...series[name],...e};this.draw()}}render(e){e&&this.setSeries(e);this.draw()}draw(){const{gl,series,disabledClearRect,range,canvas}=this.state;if(!gl)return;if(!disabledClearRect)this.clearRect();const seriesArray=Object.values(series);const min=range[0];const max=range[1];const{width,height}=canvas;for(const seriesItem of seriesArray){const{display,type,data,orientation}=seriesItem;const thickness=seriesItem.thickness??DEFAULTS.THICKNESS;const color=seriesItem.color??DEFAULTS.COLOR;if(!!data&&!!display)switch(type){case enums_GraphicType.Line:this.drawLine(data,color,thickness,width,height,min,max);break;case enums_GraphicType.Stepline:this.drawStepline(data,color,thickness,width,height,min,max);break;case enums_GraphicType.Bar:this.drawBar(data,color,width,height,min,max,orientation??enums_OrientationType.Vertical);break;case enums_GraphicType.Area:this.drawArea(data,color,width,height,min,max);break;case enums_GraphicType.Circle:this.drawPoints(data,color,thickness,width,height,min,max,true);break;case enums_GraphicType.Rect:this.drawPoints(data,color,thickness,width,height,min,max,false);break}}}drawLine(data,color,thickness,width,height,min,max){const{gl,lineProgram,vertexBuffer}=this.state;if(!gl||!lineProgram||!vertexBuffer||!this.lineUniforms||!this.lineAttribs)return;const len=data.length;const per=width/len;gl.useProgram(lineProgram);this.setUniformsCached(gl,this.lineUniforms,color,width,height,min,max);gl.lineWidth(thickness);let segmentStart=0;let vertexCount=0;for(let i=0;i<len;i++){const value=data[i];if(void 0!==value&&!Number.isNaN(value)&&Number.isFinite(value)){const x=(i+.5)*per;this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=value}else if(vertexCount>segmentStart){this.uploadAndDrawLineStrip(gl,vertexBuffer,this.lineAttribs.position,segmentStart,vertexCount);segmentStart=vertexCount}}if(vertexCount>segmentStart)this.uploadAndDrawLineStrip(gl,vertexBuffer,this.lineAttribs.position,segmentStart,vertexCount)}uploadAndDrawLineStrip(gl,vertexBuffer,positionLoc,start,end){const count=(end-start)/2;if(count<2)return;gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertexDataBuffer.subarray(start,end),gl.DYNAMIC_DRAW);gl.enableVertexAttribArray(positionLoc);gl.vertexAttribPointer(positionLoc,2,gl.FLOAT,false,0,0);gl.drawArrays(gl.LINE_STRIP,0,count)}drawStepline(data,color,thickness,width,height,min,max){const{gl,lineProgram,vertexBuffer}=this.state;if(!gl||!lineProgram||!vertexBuffer||!this.lineUniforms||!this.lineAttribs)return;const len=data.length;const per=width/len;gl.useProgram(lineProgram);this.setUniformsCached(gl,this.lineUniforms,color,width,height,min,max);gl.lineWidth(thickness);let segmentStart=0;let vertexCount=0;let prevY=0;let hasData=false;for(let i=0;i<len;i++){const value=data[i];if(void 0!==value&&!Number.isNaN(value)&&Number.isFinite(value)){const x=i*per;if(hasData){this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=prevY}this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=value;prevY=value;hasData=true}else if(hasData){const lastX=this.vertexDataBuffer[vertexCount-2];this.vertexDataBuffer[vertexCount++]=lastX+per;this.vertexDataBuffer[vertexCount++]=prevY;this.uploadAndDrawLineStrip(gl,vertexBuffer,this.lineAttribs.position,segmentStart,vertexCount);segmentStart=vertexCount;hasData=false}}if(hasData&&vertexCount>segmentStart){const lastX=this.vertexDataBuffer[vertexCount-2];this.vertexDataBuffer[vertexCount++]=lastX+per;this.vertexDataBuffer[vertexCount++]=prevY;this.uploadAndDrawLineStrip(gl,vertexBuffer,this.lineAttribs.position,segmentStart,vertexCount)}}drawBar(data,color,width,height,min,max,orientation){const{gl,fillProgram,vertexBuffer,barValue2Color,CI}=this.state;if(!gl||!fillProgram||!vertexBuffer)return;const len=data.length;if(orientation===enums_OrientationType.Horizontal){const h=height/len;let vertexCount=0;for(let i=0;i<len;i++){const value=data[i];if(void 0===value||Number.isNaN(value))continue;const yPixel=i*height/len;const barWidth=(value-min)/(max-min)*width;this.vertexDataBuffer[vertexCount++]=width-barWidth;this.vertexDataBuffer[vertexCount++]=yPixel;this.vertexDataBuffer[vertexCount++]=width;this.vertexDataBuffer[vertexCount++]=yPixel;this.vertexDataBuffer[vertexCount++]=width-barWidth;this.vertexDataBuffer[vertexCount++]=yPixel+h;this.vertexDataBuffer[vertexCount++]=width-barWidth;this.vertexDataBuffer[vertexCount++]=yPixel+h;this.vertexDataBuffer[vertexCount++]=width;this.vertexDataBuffer[vertexCount++]=yPixel;this.vertexDataBuffer[vertexCount++]=width;this.vertexDataBuffer[vertexCount++]=yPixel+h}if(0===vertexCount)return;this.drawFilledTrianglesCached(vertexCount,color,width,height,0,height)}else if(barValue2Color&&CI&&this.fillProgramWithColor)this.drawBarWithColor(data,width,height,min,max,CI);else{let vertexCount=0;for(let i=0;i<len;i++){const value=data[i];if(void 0===value||Number.isNaN(value))continue;const startX=Math.floor(i*width/len);const endX=Math.floor((i+1)*width/len);const barW=endX-startX;this.vertexDataBuffer[vertexCount++]=startX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=startX+barW;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=startX;this.vertexDataBuffer[vertexCount++]=value;this.vertexDataBuffer[vertexCount++]=startX;this.vertexDataBuffer[vertexCount++]=value;this.vertexDataBuffer[vertexCount++]=startX+barW;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=startX+barW;this.vertexDataBuffer[vertexCount++]=value}if(0===vertexCount)return;this.drawFilledTrianglesCached(vertexCount,color,width,height,min,max)}}drawBarWithColor(data,width,height,min,max,CI){const{gl,vertexBuffer}=this.state;const program=this.fillProgramWithColor;if(!gl||!program||!vertexBuffer||!this.fillColorUniforms||!this.fillColorAttribs)return;const len=data.length;let vertexCount=0;for(let i=0;i<len;i++){const value=data[i];if(void 0===value||Number.isNaN(value))continue;const startX=Math.floor(i*width/len);const endX=Math.floor((i+1)*width/len);const barW=endX-startX;const rgba=CI.getColor(value);const r=rgba.r/255;const g=rgba.g/255;const b=rgba.b/255;const a=rgba.a/255;const vertices=[startX,min,r,g,b,a,startX+barW,min,r,g,b,a,startX,value,r,g,b,a,startX,value,r,g,b,a,startX+barW,min,r,g,b,a,startX+barW,value,r,g,b,a];for(const v of vertices)this.vertexColorBuffer[vertexCount++]=v}if(0===vertexCount)return;gl.useProgram(program);gl.uniform2f(this.fillColorUniforms.resolution,width,height);gl.uniform1f(this.fillColorUniforms.rangeMin,min);gl.uniform1f(this.fillColorUniforms.rangeMax,max);gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertexColorBuffer.subarray(0,vertexCount),gl.DYNAMIC_DRAW);const stride=24;gl.enableVertexAttribArray(this.fillColorAttribs.position);gl.vertexAttribPointer(this.fillColorAttribs.position,2,gl.FLOAT,false,stride,0);gl.enableVertexAttribArray(this.fillColorAttribs.color);gl.vertexAttribPointer(this.fillColorAttribs.color,4,gl.FLOAT,false,stride,8);gl.drawArrays(gl.TRIANGLES,0,vertexCount/6)}drawArea(data,color,width,height,min,max){const{gl,fillProgram,vertexBuffer}=this.state;if(!gl||!fillProgram||!vertexBuffer)return;const len=data.length;const per=width/len;let vertexCount=0;let prevX=0;let prevY=0;let hasData=false;for(let i=0;i<len;i++){const value=data[i];if(void 0!==value&&!Number.isNaN(value)&&Number.isFinite(value)){const x=i*per;if(hasData){this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=prevY}prevX=x;prevY=value;hasData=true}else if(hasData){const endX=prevX+per;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=prevY;hasData=false}}if(hasData){const endX=prevX+per;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=prevX;this.vertexDataBuffer[vertexCount++]=prevY;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=min;this.vertexDataBuffer[vertexCount++]=endX;this.vertexDataBuffer[vertexCount++]=prevY}if(0===vertexCount)return;this.drawFilledTrianglesCached(vertexCount,color,width,height,min,max)}drawPoints(data,color,thickness,width,height,min,max,isCircle){const{gl,vertexBuffer}=this.state;const program=isCircle?this.pointCircleProgram:this.pointRectProgram;const uniforms=isCircle?this.pointCircleUniforms:this.pointRectUniforms;const attribs=isCircle?this.pointCircleAttribs:this.pointRectAttribs;if(!gl||!program||!vertexBuffer||!uniforms||!attribs)return;const len=data.length;const per=width/len;let vertexCount=0;for(let i=0;i<len;i++){const value=data[i];if(void 0!==value&&!Number.isNaN(value)&&Number.isFinite(value)){const x=(i+.5)*per;this.vertexDataBuffer[vertexCount++]=x;this.vertexDataBuffer[vertexCount++]=value}}if(0===vertexCount)return;const{r,g,b,a}=hexToRGBA(color);gl.useProgram(program);gl.uniform2f(uniforms.resolution,width,height);gl.uniform1f(uniforms.rangeMin,min);gl.uniform1f(uniforms.rangeMax,max);gl.uniform4f(uniforms.color,r/255,g/255,b/255,a/255);gl.uniform1f(uniforms.pointSize,thickness);gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertexDataBuffer.subarray(0,vertexCount),gl.DYNAMIC_DRAW);gl.enableVertexAttribArray(attribs.position);gl.vertexAttribPointer(attribs.position,2,gl.FLOAT,false,0,0);gl.drawArrays(gl.POINTS,0,vertexCount/2)}drawFilledTrianglesCached(vertexCount,color,width,height,min,max){const{gl,fillProgram,vertexBuffer}=this.state;if(!gl||!fillProgram||!vertexBuffer||!this.fillUniforms||!this.fillAttribs||0===vertexCount)return;gl.useProgram(fillProgram);this.setUniformsCached(gl,this.fillUniforms,color,width,height,min,max);gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertexDataBuffer.subarray(0,vertexCount),gl.DYNAMIC_DRAW);gl.enableVertexAttribArray(this.fillAttribs.position);gl.vertexAttribPointer(this.fillAttribs.position,2,gl.FLOAT,false,0,0);gl.drawArrays(gl.TRIANGLES,0,vertexCount/2)}setUniformsCached(gl,uniforms,color,width,height,min,max){const{r,g,b,a}=hexToRGBA(color);gl.uniform2f(uniforms.resolution,width,height);gl.uniform1f(uniforms.rangeMin,min);gl.uniform1f(uniforms.rangeMax,max);gl.uniform4f(uniforms.color,r/255,g/255,b/255,a/255)}resize(){super.resize()}dispose(){const{gl,lineProgram,fillProgram,pointProgram,vertexBuffer,colorTexture}=this.state;if(gl){if(lineProgram)gl.deleteProgram(lineProgram);if(fillProgram)gl.deleteProgram(fillProgram);if(pointProgram)gl.deleteProgram(pointProgram);if(this.fillProgramWithColor)gl.deleteProgram(this.fillProgramWithColor);if(this.pointCircleProgram)gl.deleteProgram(this.pointCircleProgram);if(this.pointRectProgram)gl.deleteProgram(this.pointRectProgram);if(vertexBuffer)gl.deleteBuffer(vertexBuffer);if(colorTexture)gl.deleteTexture(colorTexture)}super.dispose()}}export{ColorInterpolator,Dial,Fluorescence,Gauge,enums_GraphicType as GraphicType,Heatmap,HeatmapWebGL,IQ,IQEye,enums_OrientationType as OrientationType,Radar,Series,SeriesWebGL,color2intensity,hexToRGBA,rgbToHex};
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rfkit/renderer",
|
|
3
|
+
"description": "A high-performance canvas rendering engine for charts and visualizations, supporting Canvas 2D and WebGL rendering",
|
|
4
|
+
"module": "index.js",
|
|
5
|
+
"types": "index.d.ts",
|
|
6
|
+
"author": "Hxgh",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"private": false,
|
|
10
|
+
"keywords": [
|
|
11
|
+
"canvas",
|
|
12
|
+
"webgl",
|
|
13
|
+
"renderer",
|
|
14
|
+
"chart",
|
|
15
|
+
"visualization",
|
|
16
|
+
"heatmap",
|
|
17
|
+
"fluorescence",
|
|
18
|
+
"spectrum",
|
|
19
|
+
"typescript"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { FluorescenceRenderData, FluorescenceState, InitProps, Range } from '../../types';
|
|
3
|
+
export default class Fluorescence extends Engine<FluorescenceState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
updateProps(e: Partial<FluorescenceState>): void;
|
|
6
|
+
clearImageData(): void;
|
|
7
|
+
clearRect(): void;
|
|
8
|
+
clear(): void;
|
|
9
|
+
dispose(): void;
|
|
10
|
+
private drawBlocks;
|
|
11
|
+
resize(): void;
|
|
12
|
+
setRange(range: Range): void;
|
|
13
|
+
render(data: FluorescenceRenderData): void;
|
|
14
|
+
interpolateColor(colors: Array<string | {
|
|
15
|
+
r: number;
|
|
16
|
+
g: number;
|
|
17
|
+
b: number;
|
|
18
|
+
}>, ratio: number): {
|
|
19
|
+
r: number;
|
|
20
|
+
g: number;
|
|
21
|
+
b: number;
|
|
22
|
+
a: number;
|
|
23
|
+
};
|
|
24
|
+
private colorCache;
|
|
25
|
+
hexToRgb(hex: string): {
|
|
26
|
+
r: number;
|
|
27
|
+
g: number;
|
|
28
|
+
b: number;
|
|
29
|
+
};
|
|
30
|
+
private lastRenderParams;
|
|
31
|
+
private intensityMatrixCache;
|
|
32
|
+
private gridCentersCache;
|
|
33
|
+
private weightLookupCache;
|
|
34
|
+
private colorLookupCache;
|
|
35
|
+
private blockPositionsCache;
|
|
36
|
+
private lastBlockRenderParams;
|
|
37
|
+
draw(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=Fluorescence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fluorescence.d.ts","sourceRoot":"","sources":["../../../src/renderers/cartesian/Fluorescence.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EACV,sBAAsB,EACtB,iBAAiB,EACjB,SAAS,EACT,KAAK,EACN,MAAM,aAAa,CAAC;AAKrB,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,MAAM,CAAC,iBAAiB,CAAC;IACjE,IAAI,CAAC,KAAK,EAAE,SAAS;IAYrB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAKzC,cAAc;IAcd,SAAS;IAMT,KAAK;IAQL,OAAO;IAYP,OAAO,CAAC,UAAU;IA0HlB,MAAM;IAKN,QAAQ,CAAC,KAAK,EAAE,KAAK;IAMrB,MAAM,CAAC,IAAI,EAAE,sBAAsB;IASnC,gBAAgB,CACd,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAC3D,KAAK,EAAE,MAAM,GACZ;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAgDjD,OAAO,CAAC,UAAU,CAA0D;IAG5E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAa1D,OAAO,CAAC,gBAAgB,CAOR;IAGhB,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,gBAAgB,CAKP;IAGjB,OAAO,CAAC,mBAAmB,CAIV;IACjB,OAAO,CAAC,qBAAqB,CAMb;IAEhB,IAAI;CA6NL"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { GaugeDataType, GaugeState, InitProps } from '../../types';
|
|
3
|
+
export default class Gauge extends Engine<GaugeState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
clear(): void;
|
|
6
|
+
resize(): void;
|
|
7
|
+
render(data: Partial<GaugeDataType>): void;
|
|
8
|
+
draw(): void;
|
|
9
|
+
drawLimit(): void;
|
|
10
|
+
drawBackground(): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=Gauge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Gauge.d.ts","sourceRoot":"","sources":["../../../src/renderers/cartesian/Gauge.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxE,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,MAAM,CAAC,UAAU,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,SAAS;IA2BrB,KAAK;IAOL,MAAM;IAKN,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;IASnC,IAAI;IA8BJ,SAAS;IA8CT,cAAc;CA4Df"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { HeatmapState, InitProps, Range } from '../../types';
|
|
3
|
+
export default class Heatmap extends Engine<HeatmapState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
updateProps(e: Partial<HeatmapState>): void;
|
|
6
|
+
clearImageData(): void;
|
|
7
|
+
clearRect(): void;
|
|
8
|
+
clear(): void;
|
|
9
|
+
resize(): void;
|
|
10
|
+
setRange(range: Range): void;
|
|
11
|
+
render(data: number[][]): void;
|
|
12
|
+
draw(): void;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=Heatmap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Heatmap.d.ts","sourceRoot":"","sources":["../../../src/renderers/cartesian/Heatmap.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,MAAM,CAAC,YAAY,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,SAAS;IAWrB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAwBpC,cAAc;IAcd,SAAS;IAMT,KAAK;IAOL,MAAM;IAKN,QAAQ,CAAC,KAAK,EAAE,KAAK;IAMrB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;IAOvB,IAAI;CAeL"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { InitProps, Range, SeriesConfig, SeriesState } from '../../types';
|
|
3
|
+
export default class Series extends Engine<SeriesState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
updateProps(e: Partial<SeriesState>): void;
|
|
6
|
+
clear(): void;
|
|
7
|
+
setRange(range: Range): void;
|
|
8
|
+
setIntervel(interval: number): void;
|
|
9
|
+
setSeries(e: SeriesConfig): void;
|
|
10
|
+
render(e: SeriesConfig): void;
|
|
11
|
+
draw(): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Series.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Series.d.ts","sourceRoot":"","sources":["../../../src/renderers/cartesian/Series.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/E,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,MAAM,CAAC,WAAW,CAAC;IACrD,IAAI,CAAC,KAAK,EAAE,SAAS;IAqBrB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;IAYnC,KAAK;IAcL,QAAQ,CAAC,KAAK,EAAE,KAAK;IAOrB,WAAW,CAAC,QAAQ,EAAE,MAAM;IAK5B,SAAS,CAAC,CAAC,EAAE,YAAY;IAoBzB,MAAM,CAAC,CAAC,EAAE,YAAY;IAKtB,IAAI;CAuQL"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/renderers/cartesian/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { CircularData, CircularState, InitProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* 根据半径、内边距和角度获取坐标
|
|
5
|
+
*/
|
|
6
|
+
export declare function getCoordinates(radius: number, padding: number, angle: number): [number, number];
|
|
7
|
+
/**
|
|
8
|
+
* 圆形图表基类(Dial/Radar 共用)
|
|
9
|
+
*/
|
|
10
|
+
export default abstract class CircularBase extends Engine {
|
|
11
|
+
state: CircularState;
|
|
12
|
+
/** 子类可覆盖:特殊刻度配置 */
|
|
13
|
+
protected getSpecialTickConfig(_angle: number): {
|
|
14
|
+
color: string;
|
|
15
|
+
alias: string;
|
|
16
|
+
} | undefined;
|
|
17
|
+
/** 子类可覆盖:起始角度偏移(Dial 减 90 度,Radar 不减) */
|
|
18
|
+
protected getAngleOffset(): number;
|
|
19
|
+
init(props: InitProps): void;
|
|
20
|
+
clear(): void;
|
|
21
|
+
resize(): void;
|
|
22
|
+
render(data: Partial<CircularData>): void;
|
|
23
|
+
drawTicks(): void;
|
|
24
|
+
drawBackground(): void;
|
|
25
|
+
draw(): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=CircularBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CircularBase.d.ts","sourceRoot":"","sources":["../../../src/renderers/circular/CircularBase.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EACV,YAAY,EAEZ,aAAa,EACb,SAAS,EACV,MAAM,aAAa,CAAC;AAGrB;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,YAAa,SAAQ,MAAM;IAC/C,KAAK,EAAE,aAAa,CAAC;IAE7B,mBAAmB;IACnB,SAAS,CAAC,oBAAoB,CAC5B,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAI/C,yCAAyC;IACzC,SAAS,CAAC,cAAc,IAAI,MAAM;IAIlC,IAAI,CAAC,KAAK,EAAE,SAAS;IAkCrB,KAAK;IAIL,MAAM;IAMN,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC;IAclC,SAAS;IAqFT,cAAc;IAkCd,IAAI;CAoFL"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import CircularBase from './CircularBase';
|
|
2
|
+
/**
|
|
3
|
+
* 仪表盘 - 圆形图表
|
|
4
|
+
* 特点:0度指向上方(北),有特殊刻度配置
|
|
5
|
+
*/
|
|
6
|
+
export default class Dial extends CircularBase {
|
|
7
|
+
/** 特殊刻度配置:北/南用不同颜色 */
|
|
8
|
+
protected getSpecialTickConfig(angle: number): {
|
|
9
|
+
color: string;
|
|
10
|
+
alias: string;
|
|
11
|
+
} | undefined;
|
|
12
|
+
/** 角度偏移:减 90 度使 0 度指向上方 */
|
|
13
|
+
protected getAngleOffset(): number;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=Dial.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dial.d.ts","sourceRoot":"","sources":["../../../src/renderers/circular/Dial.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAQ1C;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,sBAAsB;IACtB,SAAS,CAAC,oBAAoB,CAC5B,KAAK,EAAE,MAAM,GACZ;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAI/C,2BAA2B;IAC3B,SAAS,CAAC,cAAc,IAAI,MAAM;CAGnC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import CircularBase from './CircularBase';
|
|
2
|
+
/**
|
|
3
|
+
* 雷达图 - 圆形图表
|
|
4
|
+
* 特点:0度指向右方,无特殊刻度配置
|
|
5
|
+
*/
|
|
6
|
+
export default class Radar extends CircularBase {
|
|
7
|
+
/** 无特殊刻度配置 */
|
|
8
|
+
protected getSpecialTickConfig(_angle: number): {
|
|
9
|
+
color: string;
|
|
10
|
+
alias: string;
|
|
11
|
+
} | undefined;
|
|
12
|
+
/** 无角度偏移 */
|
|
13
|
+
protected getAngleOffset(): number;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=Radar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Radar.d.ts","sourceRoot":"","sources":["../../../src/renderers/circular/Radar.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAE1C;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,YAAY;IAC7C,cAAc;IACd,SAAS,CAAC,oBAAoB,CAC5B,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAI/C,YAAY;IACZ,SAAS,CAAC,cAAc,IAAI,MAAM;CAGnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/renderers/circular/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { InitProps, IQData, IQState } from '../../types';
|
|
3
|
+
export default class IQ extends Engine<IQState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
clear(): void;
|
|
6
|
+
render(data: IQData): void;
|
|
7
|
+
draw(): void;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=IQ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IQ.d.ts","sourceRoot":"","sources":["../../../src/renderers/scatter/IQ.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAS9D,MAAM,CAAC,OAAO,OAAO,EAAG,SAAQ,MAAM,CAAC,OAAO,CAAC;IAC7C,IAAI,CAAC,KAAK,EAAE,SAAS;IASrB,KAAK;IAUL,MAAM,CAAC,IAAI,EAAE,MAAM;IAOnB,IAAI;CA6CL"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Engine from '../../core/Engine';
|
|
2
|
+
import type { InitProps, IQData, IQEyeState } from '../../types';
|
|
3
|
+
export default class IQEye extends Engine<IQEyeState> {
|
|
4
|
+
init(props: InitProps): void;
|
|
5
|
+
clear(): void;
|
|
6
|
+
render(data: IQData): void;
|
|
7
|
+
draw(): void;
|
|
8
|
+
private drawAllSegments;
|
|
9
|
+
private applyColorGradient;
|
|
10
|
+
private drawSegmentedData;
|
|
11
|
+
private drawLine;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=IQEye.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IQEye.d.ts","sourceRoot":"","sources":["../../../src/renderers/scatter/IQEye.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASjE,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,MAAM,CAAC,UAAU,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,SAAS;IASrB,KAAK;IAUL,MAAM,CAAC,IAAI,EAAE,MAAM;IAOnB,IAAI;IA2CJ,OAAO,CAAC,eAAe;IA0CvB,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,iBAAiB;IAgDzB,OAAO,CAAC,QAAQ;CAuCjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/renderers/scatter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ColorInterpolator } from '../../color';
|
|
2
|
+
import WebGLEngine, { type WebGLBaseState } from '../../core/WebGLEngine';
|
|
3
|
+
import type { ColorValue, InitProps, Range } from '../../types';
|
|
4
|
+
/** HeatmapWebGL 状态 */
|
|
5
|
+
export interface HeatmapWebGLState extends WebGLBaseState {
|
|
6
|
+
data: number[][];
|
|
7
|
+
colors: ColorValue[];
|
|
8
|
+
program: WebGLProgram | null;
|
|
9
|
+
quadBuffer: WebGLBuffer | null;
|
|
10
|
+
dataTexture: WebGLTexture | null;
|
|
11
|
+
colorTexture: WebGLTexture | null;
|
|
12
|
+
dataWidth: number;
|
|
13
|
+
dataHeight: number;
|
|
14
|
+
CI: ColorInterpolator | null;
|
|
15
|
+
}
|
|
16
|
+
export default class HeatmapWebGL extends WebGLEngine<HeatmapWebGLState> {
|
|
17
|
+
init(props: InitProps): void;
|
|
18
|
+
/** 更新颜色查找纹理 */
|
|
19
|
+
private updateColorTexture;
|
|
20
|
+
/** 更新数据纹理 */
|
|
21
|
+
private updateDataTexture;
|
|
22
|
+
updateProps(e: Partial<HeatmapWebGLState>): void;
|
|
23
|
+
setRange(range: Range): void;
|
|
24
|
+
clear(): void;
|
|
25
|
+
render(data: number[][]): void;
|
|
26
|
+
draw(): void;
|
|
27
|
+
resize(): void;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=HeatmapWebGL.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeatmapWebGL.d.ts","sourceRoot":"","sources":["../../../src/renderers/webgl/HeatmapWebGL.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,WAAW,EAAE,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,sBAAsB;AACtB,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC9B;AAuCD,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,WAAW,CAAC,iBAAiB,CAAC;IACtE,IAAI,CAAC,KAAK,EAAE,SAAS;IAgDrB,eAAe;IACf,OAAO,CAAC,kBAAkB;IAiC1B,aAAa;IACb,OAAO,CAAC,iBAAiB;IAqCzB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAgBzC,QAAQ,CAAC,KAAK,EAAE,KAAK;IAMrB,KAAK;IAOL,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;IAQvB,IAAI;IAwCJ,MAAM;IAIN,OAAO;CAUR"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ColorInterpolator } from '../../color';
|
|
2
|
+
import WebGLEngine, { type WebGLBaseState } from '../../core/WebGLEngine';
|
|
3
|
+
import type { ColorValue, InitProps, Range, SeriesConfig } from '../../types';
|
|
4
|
+
/** SeriesWebGL 状态 */
|
|
5
|
+
export interface SeriesWebGLState extends WebGLBaseState {
|
|
6
|
+
series: Record<string, SeriesConfig>;
|
|
7
|
+
colors: ColorValue[];
|
|
8
|
+
interval: number;
|
|
9
|
+
barValue2Color?: boolean;
|
|
10
|
+
disabledClearRect?: boolean;
|
|
11
|
+
lineProgram: WebGLProgram | null;
|
|
12
|
+
fillProgram: WebGLProgram | null;
|
|
13
|
+
pointProgram: WebGLProgram | null;
|
|
14
|
+
vertexBuffer: WebGLBuffer | null;
|
|
15
|
+
colorTexture: WebGLTexture | null;
|
|
16
|
+
CI: ColorInterpolator | null;
|
|
17
|
+
}
|
|
18
|
+
export default class SeriesWebGL extends WebGLEngine<SeriesWebGLState> {
|
|
19
|
+
private fillProgramWithColor;
|
|
20
|
+
private pointCircleProgram;
|
|
21
|
+
private pointRectProgram;
|
|
22
|
+
private lineUniforms;
|
|
23
|
+
private lineAttribs;
|
|
24
|
+
private fillUniforms;
|
|
25
|
+
private fillAttribs;
|
|
26
|
+
private fillColorUniforms;
|
|
27
|
+
private fillColorAttribs;
|
|
28
|
+
private pointCircleUniforms;
|
|
29
|
+
private pointCircleAttribs;
|
|
30
|
+
private pointRectUniforms;
|
|
31
|
+
private pointRectAttribs;
|
|
32
|
+
private vertexDataBuffer;
|
|
33
|
+
private vertexColorBuffer;
|
|
34
|
+
init(props: InitProps): void;
|
|
35
|
+
/** 缓存所有 shader 的 Uniform/Attribute Locations */
|
|
36
|
+
private cacheLocations;
|
|
37
|
+
updateProps(e: Partial<SeriesWebGLState>): void;
|
|
38
|
+
clear(): void;
|
|
39
|
+
setRange(range: Range): void;
|
|
40
|
+
setIntervel(interval: number): void;
|
|
41
|
+
setSeries(e: SeriesConfig): void;
|
|
42
|
+
render(e: SeriesConfig): void;
|
|
43
|
+
draw(): void;
|
|
44
|
+
private drawLine;
|
|
45
|
+
private uploadAndDrawLineStrip;
|
|
46
|
+
private drawStepline;
|
|
47
|
+
private drawBar;
|
|
48
|
+
private drawBarWithColor;
|
|
49
|
+
private drawArea;
|
|
50
|
+
private drawPoints;
|
|
51
|
+
private drawFilledTrianglesCached;
|
|
52
|
+
private setUniformsCached;
|
|
53
|
+
resize(): void;
|
|
54
|
+
dispose(): void;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=SeriesWebGL.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeriesWebGL.d.ts","sourceRoot":"","sources":["../../../src/renderers/webgl/SeriesWebGL.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAa,MAAM,aAAa,CAAC;AAC3D,OAAO,WAAW,EAAE,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkB9E,qBAAqB;AACrB,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC9B;AAoGD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW,CAAC,gBAAgB,CAAC;IAEpE,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,gBAAgB,CAA6B;IAGrD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAmC;IAC3D,OAAO,CAAC,mBAAmB,CAAiC;IAC5D,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAmC;IAG3D,OAAO,CAAC,gBAAgB,CAAgD;IACxE,OAAO,CAAC,iBAAiB,CAAgD;IAEzE,IAAI,CAAC,KAAK,EAAE,SAAS;IA8ErB,gDAAgD;IAChD,OAAO,CAAC,cAAc;IAyFtB,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAUxC,KAAK;IAaL,QAAQ,CAAC,KAAK,EAAE,KAAK;IAWrB,WAAW,CAAC,QAAQ,EAAE,MAAM;IAK5B,SAAS,CAAC,CAAC,EAAE,YAAY;IAkBzB,MAAM,CAAC,CAAC,EAAE,YAAY;IAKtB,IAAI;IAmEJ,OAAO,CAAC,QAAQ;IA0EhB,OAAO,CAAC,sBAAsB;IAsB9B,OAAO,CAAC,YAAY;IA6FpB,OAAO,CAAC,OAAO;IAqGf,OAAO,CAAC,gBAAgB;IAuHxB,OAAO,CAAC,QAAQ;IA4FhB,OAAO,CAAC,UAAU;IA4DlB,OAAO,CAAC,yBAAyB;IA2CjC,OAAO,CAAC,iBAAiB;IAgBzB,MAAM;IAIN,OAAO;CAsBR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/renderers/webgl/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** RGBA 颜色类型 */
|
|
2
|
+
export interface RGBA {
|
|
3
|
+
r: number;
|
|
4
|
+
g: number;
|
|
5
|
+
b: number;
|
|
6
|
+
a: number;
|
|
7
|
+
/** 预计算的十六进制值 */
|
|
8
|
+
hax?: string;
|
|
9
|
+
}
|
|
10
|
+
/** 颜色值类型:支持十六进制字符串或 RGB 对象 */
|
|
11
|
+
export type ColorValue = string | {
|
|
12
|
+
r: number;
|
|
13
|
+
g: number;
|
|
14
|
+
b: number;
|
|
15
|
+
a?: number;
|
|
16
|
+
};
|
|
17
|
+
/** Y轴范围 [min, max] */
|
|
18
|
+
export type Range = [number, number];
|
|
19
|
+
/** 带跨度的范围 [min, max, span] */
|
|
20
|
+
export type RangeWithSpan = [number, number, number];
|
|
21
|
+
/** 点坐标 */
|
|
22
|
+
export interface Point {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
}
|
|
26
|
+
/** 默认值常量 */
|
|
27
|
+
export declare const DEFAULTS: {
|
|
28
|
+
readonly COLOR: "#000000";
|
|
29
|
+
readonly THICKNESS: 1;
|
|
30
|
+
readonly FILL_STYLE: "#ffffffB0";
|
|
31
|
+
readonly FILL_STYLE_PRIMARY: "#1890ff";
|
|
32
|
+
readonly FILL_STYLE_TRANSPARENT_BASE: "#ffffff10";
|
|
33
|
+
readonly LINE_COLOR: "#00ff00";
|
|
34
|
+
readonly POINT_COLOR: "#ff0000";
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,gBAAgB;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAClB,MAAM,GACN;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,sBAAsB;AACtB,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErC,8BAA8B;AAC9B,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAErD,UAAU;AACV,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,YAAY;AACZ,eAAO,MAAM,QAAQ;;;;;;;;CAaX,CAAC"}
|
package/types/data.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { GraphicType, OrientationType } from './enums';
|
|
2
|
+
/** Dial/Radar 系列数据 */
|
|
3
|
+
export interface CircularSeriesItem {
|
|
4
|
+
value: number;
|
|
5
|
+
color: string;
|
|
6
|
+
lineWidth?: number;
|
|
7
|
+
radio?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Dial/Radar 数据类型 */
|
|
10
|
+
export interface CircularData {
|
|
11
|
+
series?: CircularSeriesItem[];
|
|
12
|
+
range?: number[];
|
|
13
|
+
yawAngle?: number;
|
|
14
|
+
isNorthFacing?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** IQ 数据类型 */
|
|
17
|
+
export interface IQData {
|
|
18
|
+
IData: number[];
|
|
19
|
+
QData: number[];
|
|
20
|
+
}
|
|
21
|
+
/** Y轴范围 */
|
|
22
|
+
export type AxisYRange = [number, number];
|
|
23
|
+
/** Series 配置 */
|
|
24
|
+
export interface SeriesConfig {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
color?: string;
|
|
28
|
+
display?: boolean;
|
|
29
|
+
data?: Float32Array;
|
|
30
|
+
type?: GraphicType;
|
|
31
|
+
thickness?: number;
|
|
32
|
+
orientation?: OrientationType;
|
|
33
|
+
path?: Path2D;
|
|
34
|
+
}
|
|
35
|
+
/** Gauge 数据类型 */
|
|
36
|
+
export interface GaugeData {
|
|
37
|
+
value: number;
|
|
38
|
+
range?: [number, number];
|
|
39
|
+
}
|
|
40
|
+
/** Fluorescence 渲染数据 */
|
|
41
|
+
export interface FluorescenceRenderData {
|
|
42
|
+
fluorescenceData: Map<number, number>[];
|
|
43
|
+
fluorescenceMaxCount: number;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/types/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE5D,sBAAsB;AACtB,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,sBAAsB;AACtB,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,cAAc;AACd,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,WAAW;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE1C,gBAAgB;AAChB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iBAAiB;AACjB,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1B;AAED,wBAAwB;AACxB,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACxC,oBAAoB,EAAE,MAAM,CAAC;CAC9B"}
|
package/types/enums.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** 方向类型 */
|
|
2
|
+
export declare enum OrientationType {
|
|
3
|
+
Horizontal = "horizontal",
|
|
4
|
+
Vertical = "vertical"
|
|
5
|
+
}
|
|
6
|
+
/** 图形类型 */
|
|
7
|
+
export declare enum GraphicType {
|
|
8
|
+
Circle = "circle",
|
|
9
|
+
Rect = "rect",
|
|
10
|
+
Line = "line",
|
|
11
|
+
Stepline = "stepline",
|
|
12
|
+
Bar = "bar",
|
|
13
|
+
Area = "area"
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=enums.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,oBAAY,eAAe;IACzB,UAAU,eAAe;IACzB,QAAQ,aAAa;CACtB;AAED,WAAW;AACX,oBAAY,WAAW;IACrB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,GAAG,QAAQ;IACX,IAAI,SAAS;CACd"}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { ColorValue, Point, Range, RangeWithSpan, RGBA } from './common';
|
|
2
|
+
export { DEFAULTS } from './common';
|
|
3
|
+
export type { AxisYRange, CircularData, CircularSeriesItem, FluorescenceRenderData, GaugeData, IQData, SeriesConfig } from './data';
|
|
4
|
+
export { GraphicType, OrientationType } from './enums';
|
|
5
|
+
export type { BaseState, CircularState, FluorescenceState, GaugeDataType, GaugeState, HeatmapState, InitProps, IQEyeState, IQState, SeriesState, StateProps } from './state';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,YAAY,EACV,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,MAAM,EACN,YAAY,EACb,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGvD,YAAY,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAAU,EACV,OAAO,EACP,WAAW,EACX,UAAU,EACX,MAAM,SAAS,CAAC"}
|
package/types/state.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type ColorInterpolator from '../color/ColorInterpolator';
|
|
2
|
+
import type { ColorValue, Range } from './common';
|
|
3
|
+
import type { CircularData, IQData, SeriesConfig } from './data';
|
|
4
|
+
/** 基础状态属性接口 */
|
|
5
|
+
export interface BaseState {
|
|
6
|
+
id: string;
|
|
7
|
+
container?: HTMLElement;
|
|
8
|
+
canvas: HTMLCanvasElement;
|
|
9
|
+
ctx: CanvasRenderingContext2D;
|
|
10
|
+
range: Range | [number, number, number];
|
|
11
|
+
}
|
|
12
|
+
/** 初始化属性(构造函数参数) */
|
|
13
|
+
export interface InitProps {
|
|
14
|
+
id: string;
|
|
15
|
+
range?: Range | [number, number, number];
|
|
16
|
+
colors?: ColorValue[];
|
|
17
|
+
fillStyle?: string;
|
|
18
|
+
fillStylePrimary?: string;
|
|
19
|
+
fillStyleTransparentBase?: string;
|
|
20
|
+
lineColor?: string;
|
|
21
|
+
pointColor?: string;
|
|
22
|
+
series?: SeriesConfig[];
|
|
23
|
+
barValue2Color?: boolean;
|
|
24
|
+
disabledClearRect?: boolean;
|
|
25
|
+
display?: boolean;
|
|
26
|
+
ignoreValue?: number;
|
|
27
|
+
fluorescenceMaxCount?: number;
|
|
28
|
+
baseWidth?: number;
|
|
29
|
+
padding?: number;
|
|
30
|
+
ticksStep?: number;
|
|
31
|
+
ticksRenderLength?: number;
|
|
32
|
+
markedAngles?: number[];
|
|
33
|
+
interval?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Series 状态 */
|
|
36
|
+
export interface SeriesState extends BaseState {
|
|
37
|
+
series: Record<string, SeriesConfig>;
|
|
38
|
+
colors: ColorValue[];
|
|
39
|
+
CI?: ColorInterpolator;
|
|
40
|
+
interval: number;
|
|
41
|
+
barValue2Color?: boolean;
|
|
42
|
+
disabledClearRect?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Heatmap 状态 */
|
|
45
|
+
export interface HeatmapState extends BaseState {
|
|
46
|
+
data: number[][];
|
|
47
|
+
colors: ColorValue[];
|
|
48
|
+
imageData: ImageData;
|
|
49
|
+
CI: ColorInterpolator;
|
|
50
|
+
}
|
|
51
|
+
/** Fluorescence 状态 */
|
|
52
|
+
export interface FluorescenceState extends BaseState {
|
|
53
|
+
data: Map<number, number>[];
|
|
54
|
+
colors: ColorValue[];
|
|
55
|
+
imageData: ImageData;
|
|
56
|
+
fluorescenceMaxCount: number;
|
|
57
|
+
display?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Circular (Dial/Radar) 状态 */
|
|
60
|
+
export interface CircularState extends BaseState {
|
|
61
|
+
data: CircularData;
|
|
62
|
+
fillStyle: string;
|
|
63
|
+
fillStylePrimary: string;
|
|
64
|
+
fillStyleTransparentBase: string;
|
|
65
|
+
color2intensity: Record<number, string>;
|
|
66
|
+
BGCanvas?: HTMLCanvasElement;
|
|
67
|
+
ticksCanvas?: HTMLCanvasElement;
|
|
68
|
+
baseWidth: number;
|
|
69
|
+
padding: number;
|
|
70
|
+
ticksStep: number;
|
|
71
|
+
ticksRenderLength: number;
|
|
72
|
+
markedAngles: number[];
|
|
73
|
+
}
|
|
74
|
+
/** Gauge 数据类型 */
|
|
75
|
+
export interface GaugeDataType {
|
|
76
|
+
value: number;
|
|
77
|
+
limit?: number;
|
|
78
|
+
}
|
|
79
|
+
/** Gauge 状态 */
|
|
80
|
+
export interface GaugeState extends BaseState {
|
|
81
|
+
data: GaugeDataType;
|
|
82
|
+
fillStyle: string;
|
|
83
|
+
fillStylePrimary: string;
|
|
84
|
+
fillStyleTransparentBase: string;
|
|
85
|
+
BGCanvas?: HTMLCanvasElement;
|
|
86
|
+
padding: number;
|
|
87
|
+
step: number;
|
|
88
|
+
baseWidth: number;
|
|
89
|
+
lineWidth: number;
|
|
90
|
+
}
|
|
91
|
+
/** IQ 状态 */
|
|
92
|
+
export interface IQState extends BaseState {
|
|
93
|
+
data: IQData;
|
|
94
|
+
lineColor: string;
|
|
95
|
+
pointColor: string;
|
|
96
|
+
}
|
|
97
|
+
/** IQEye 状态 */
|
|
98
|
+
export interface IQEyeState extends BaseState {
|
|
99
|
+
data: IQData;
|
|
100
|
+
lineColor: string;
|
|
101
|
+
pointColor: string;
|
|
102
|
+
offscreenCanvas?: HTMLCanvasElement;
|
|
103
|
+
offscreenCtx?: CanvasRenderingContext2D;
|
|
104
|
+
}
|
|
105
|
+
/** 兼容旧版 StateProps - 用于渐进迁移 */
|
|
106
|
+
export type StateProps = InitProps & Partial<BaseState>;
|
|
107
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/types/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEjE,eAAe;AACf,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,wBAAwB,CAAC;IAC9B,KAAK,EAAE,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,oBAAoB;AACpB,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,gBAAgB;AAChB,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,iBAAiB;AACjB,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAED,sBAAsB;AACtB,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,+BAA+B;AAC/B,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB,EAAE,MAAM,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,iBAAiB;AACjB,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAe;AACf,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,YAAY;AACZ,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAe;AACf,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,iBAAiB,CAAC;IACpC,YAAY,CAAC,EAAE,wBAAwB,CAAC;CACzC;AAED,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RGBA } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 根据给定的数据数组填充图像数据
|
|
4
|
+
* 性能优化:cellWidth 只计算一次,直接操作像素数组
|
|
5
|
+
*
|
|
6
|
+
* @param canvasWidth 画布宽度
|
|
7
|
+
* @param canvasHeight 画布高度
|
|
8
|
+
* @param data 数据数组
|
|
9
|
+
* @param imageData 图像数据对象
|
|
10
|
+
* @param getColor 颜色获取函数
|
|
11
|
+
*/
|
|
12
|
+
export declare function fillImageData(canvasWidth: number, canvasHeight: number, data: number[][], imageData: Uint8ClampedArray, getColor: (value: number) => RGBA): void;
|
|
13
|
+
//# sourceMappingURL=imageData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageData.d.ts","sourceRoot":"","sources":["../../src/utils/imageData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EAAE,EAAE,EAChB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAChC,IAAI,CAkCN"}
|
package/utils/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC"}
|
package/utils/math.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取数组的最小值和最大值
|
|
3
|
+
*/
|
|
4
|
+
export declare function getMinMax(numbers: number[]): [number, number];
|
|
5
|
+
/**
|
|
6
|
+
* 判断是否为有效数字
|
|
7
|
+
*/
|
|
8
|
+
export declare function isNumberAlias(n: unknown): n is number;
|
|
9
|
+
/**
|
|
10
|
+
* 将二维数组转换为 Float64Array
|
|
11
|
+
*/
|
|
12
|
+
export declare function convertToF64(odata: number[][]): Float64Array;
|
|
13
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/utils/math.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAiB7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,YAAY,CAQ5D"}
|