@rfkit/renderer 0.1.5 → 0.1.7
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/index.js +2 -2
- package/package.json +1 -1
- package/renderers/cartesian/Fluorescence.d.ts.map +1 -1
- package/renderers/scatter/IQEye.d.ts +5 -3
- package/renderers/scatter/IQEye.d.ts.map +1 -1
- package/types/common.d.ts +14 -0
- package/types/common.d.ts.map +1 -1
- package/types/data.d.ts +1 -1
- package/types/data.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/state.d.ts +8 -3
- package/types/state.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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(){}dispose(){const{canvas,container}=this.state;container?.removeChild(canvas)}}const DEFAULTS={COLOR:"#000000",THICKNESS:1,FILL_STYLE:"#ffffffB0",FILL_STYLE_PRIMARY:"#1890ff",FILL_STYLE_TRANSPARENT_BASE:"#ffffff10",LINE_COLOR:"#00ff00",POINT_COLOR:"#ff0000",DIAL_NORTH_COLOR:"#ff4d4f",DIAL_SOUTH_COLOR:"#1890ff",FLUORESCENCE_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],RANGE:[-20,100]};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}({});var enums_RendererType=/*#__PURE__*/function(RendererType){RendererType["Canvas"]="canvas";RendererType["WebGL"]="webgl";return RendererType}({});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=DEFAULTS.FLUORESCENCE_COLORS,range=DEFAULTS.RANGE,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=DEFAULTS.FLUORESCENCE_COLORS,range=DEFAULTS.RANGE,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)}}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)}let webgl2Supported=null;function isWebGL2Supported(){if(null!==webgl2Supported)return webgl2Supported;try{const canvas=document.createElement("canvas");const gl=canvas.getContext("webgl2");webgl2Supported=!!gl;if(gl){const ext=gl.getExtension("WEBGL_lose_context");ext?.loseContext()}}catch{webgl2Supported=false}return webgl2Supported}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 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
|
|
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(){}dispose(){const{canvas,container}=this.state;container?.removeChild(canvas)}}const DEFAULTS={COLOR:"#000000",THICKNESS:1,FILL_STYLE:"#ffffffB0",FILL_STYLE_PRIMARY:"#1890ff",FILL_STYLE_TRANSPARENT_BASE:"#ffffff10",LINE_COLOR:"#00ff00",POINT_COLOR:"#ff0000",DIAL_NORTH_COLOR:"#ff4d4f",DIAL_SOUTH_COLOR:"#1890ff",FLUORESCENCE_COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#FF0000"],RANGE:[-20,100]};const FLUORESCENCE={LEVEL_MIN:-20,LEVEL_MAX:140,LEVEL_RANGE:161};const EYE_DEFAULTS={Y_RANGE:[-1,1],SEGMENT_SIZE:5,DECAY_FACTOR:.98,MAX_ACCUMULATION:1e3,COLORS:["#000080","#0000FF","#00FFFF","#00FF00","#FFFF00","#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}({});var enums_RendererType=/*#__PURE__*/function(RendererType){RendererType["Canvas"]="canvas";RendererType["WebGL"]="webgl";return RendererType}({});const BLOCK_RENDER_MODE=false;class Fluorescence extends Engine{init(props){super.init(props);const{colors,display}=props;this.updateProps({colors,data:new Uint32Array(0),display});this.resize()}updateProps(e){const rangeChanged=e.range&&(e.range[0]!==this.state.range?.[0]||e.range[1]!==this.state.range?.[1]);super.updateProps(e);if(rangeChanged&&this.state.data?.length>0)this.draw()}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:new Uint32Array(0)})}dispose(){this.intensityMatrixCache=null;this.gridCentersCache=null;this.weightLookupCache=null;this.colorLookupCache=null;this.colorCache.clear();this.lastRenderParams=null;this.blockPositionsCache=null;this.lastBlockRenderParams=null}drawBlocks(){const{imageData,data,canvas,ctx,colors=DEFAULTS.FLUORESCENCE_COLORS,range=DEFAULTS.RANGE,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/FLUORESCENCE.LEVEL_RANGE;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;const pixelsPerDb=height/rangeSpan;const blockHeight=Math.max(1,Math.ceil(pixelsPerDb));for(let dataIndex=0;dataIndex<dataLength;dataIndex++){const blockPos=this.blockPositionsCache[dataIndex];const{startX:blockStartX,endX:blockEndX}=blockPos;const baseIndex=dataIndex*FLUORESCENCE.LEVEL_RANGE;for(let level=0;level<FLUORESCENCE.LEVEL_RANGE;level++){const count=data[baseIndex+level];if(count<=0)continue;const yValue=level+FLUORESCENCE.LEVEL_MIN;if(yValue<rangeMin||yValue>rangeMax)continue;const normalizedY=(yValue-rangeMin)*invRangeSpan;const centerY=normalizedY*heightMinus1;const blockStartY=Math.max(0,Math.floor(centerY-blockHeight/2));const blockEndY=Math.min(heightMinus1,blockStartY+blockHeight-1);const intensity=Math.log(count+1)/Math.log(fluorescenceMaxCount+1);const alpha=Math.round(255*intensity);const{r:colorR,g:colorG,b:colorB}=this.interpolateColor(colors,intensity);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){const oldLength=this.state.data?.length??0;const newLength=data.fluorescenceData.length;if(oldLength!==newLength){this.clearImageData();this.intensityMatrixCache=null;this.gridCentersCache=null;this.blockPositionsCache=null;this.lastRenderParams=null;this.lastBlockRenderParams=null}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=DEFAULTS.FLUORESCENCE_COLORS,range=DEFAULTS.RANGE,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 dataLength=data.length/FLUORESCENCE.LEVEL_RANGE;const currentParams={dataLength,rangeMin,rangeMax,width,height,maxCount:fluorescenceMaxCount};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 pixelsPerLevel=height/rangeSpan;const interpolationRadius=Math.max(2,Math.ceil(pixelsPerLevel),Math.floor(height/50));const radiusSquared=interpolationRadius*interpolationRadius;const matrixSize=width*height;const cellWidth=width/dataLength;const radiusX=Math.max(interpolationRadius,Math.ceil(cellWidth));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 centerX=gridCenters[dataIndex];const baseIndex=dataIndex*FLUORESCENCE.LEVEL_RANGE;for(let level=0;level<FLUORESCENCE.LEVEL_RANGE;level++){const count=data[baseIndex+level];if(count<=0)continue;const yValue=level+FLUORESCENCE.LEVEL_MIN;if(yValue<rangeMin||yValue>rangeMax)continue;const normalizedY=(yValue-rangeMin)/rangeSpan;const centerY=normalizedY*(height-1);const intensity=Math.log(count+1)/Math.log(fluorescenceMaxCount+1);const expandStartX=Math.max(0,Math.floor(centerX-radiusX));const expandEndX=Math.min(width-1,Math.ceil(centerX+radiusX));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 normalizedDx=dx*interpolationRadius/radiusX;const distanceSquared=normalizedDx*normalizedDx+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=Math.min(rawIntensity*invMaxIntensity,1);const colorIndex=Math.min(Math.floor(intensity*(colorSteps-1)),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)}}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)}let webgl2Supported=null;function isWebGL2Supported(){if(null!==webgl2Supported)return webgl2Supported;try{const canvas=document.createElement("canvas");const gl=canvas.getContext("webgl2");webgl2Supported=!!gl;if(gl){const ext=gl.getExtension("WEBGL_lose_context");ext?.loseContext()}}catch{webgl2Supported=false}return webgl2Supported}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 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
2
|
in vec2 a_position;
|
|
3
3
|
out vec2 v_texCoord;
|
|
4
4
|
|
|
@@ -43,4 +43,4 @@ void main() {
|
|
|
43
43
|
// 从颜色查找纹理获取颜色
|
|
44
44
|
fragColor = texture(u_colorTexture, vec2(normalized, 0.5));
|
|
45
45
|
}
|
|
46
|
-
`;class HeatmapWebGL extends WebGLEngine{init(props){super.init(props);const{gl}=this.state;if(!gl)return;const colorsInput=props.colors;const colors=colorsInput&&colorsInput.length>0?colorsInput:DEFAULTS.FLUORESCENCE_COLORS;const program=this.createProgram({vertex:VERTEX_SHADER,fragment:FRAGMENT_SHADER});const quadBuffer=this.createQuadBuffer();const dataTexture=this.createTexture();const colorTexture=this.createTexture();const maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE);if(dataTexture){gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,dataTexture);const emptyData=new Float32Array([INVALID_VALUE]);gl.texImage2D(gl.TEXTURE_2D,0,gl.R32F,1,1,0,gl.RED,gl.FLOAT,emptyData)}const range=props.range??DEFAULTS.RANGE;const[rangeMin,rangeMax]=range;const CI=new ColorInterpolator(colors,rangeMin,rangeMax,.1);this.updateProps({data:[],colors,program,quadBuffer,dataTexture,dataTextures:dataTexture?[dataTexture]:[],dataTiles:[],colorTexture,dataWidth:0,dataHeight:0,CI,maxTextureSize});gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);this.updateColorTexture();gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);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.activeTexture(gl.TEXTURE1);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,data}=this.state;if(!gl||0===data.length)return;const height=data.length;let width=0;for(let row=0;row<height;row++){const rowLen=data[row]?.length??0;if(rowLen>width)width=rowLen}if(0===width)return;const maxSize=this.state.maxTextureSize||gl.getParameter(gl.MAX_TEXTURE_SIZE);if(!maxSize||maxSize<=0)return;const tilesX=Math.ceil(width/maxSize);const tilesY=Math.ceil(height/maxSize);const nextTileCount=tilesX*tilesY;let dataTextures=this.state.dataTextures??[];if(dataTextures.length<nextTileCount){const missing=nextTileCount-dataTextures.length;for(let i=0;i<missing;i++){const tex=this.createTexture();if(tex)dataTextures.push(tex)}}else if(dataTextures.length>nextTileCount){for(let i=nextTileCount;i<dataTextures.length;i++)gl.deleteTexture(dataTextures[i]);dataTextures=dataTextures.slice(0,nextTileCount)}const dataTiles=[];gl.activeTexture(gl.TEXTURE0);for(let ty=0;ty<tilesY;ty++){const startRow=ty*maxSize;const tileH=Math.min(maxSize,height-startRow);for(let tx=0;tx<tilesX;tx++){const startCol=tx*maxSize;const tileW=Math.min(maxSize,width-startCol);const tileIndex=ty*tilesX+tx;const texture=dataTextures[tileIndex];if(!texture)continue;const floatData=new Float32Array(tileW*tileH);for(let localRow=0;localRow<tileH;localRow++){const globalRow=startRow+localRow;const rowData=data[globalRow];const rowLen=rowData?.length??0;const rowOffset=localRow*tileW;for(let localCol=0;localCol<tileW;localCol++){const globalCol=startCol+localCol;if(0===rowLen||globalCol>=rowLen){floatData[rowOffset+localCol]=INVALID_VALUE;continue}const value=rowData[globalCol];if(void 0===value||Number.isNaN(value))floatData[rowOffset+localCol]=INVALID_VALUE;else floatData[rowOffset+localCol]=value}}gl.bindTexture(gl.TEXTURE_2D,texture);gl.texImage2D(gl.TEXTURE_2D,0,gl.R32F,tileW,tileH,0,gl.RED,gl.FLOAT,floatData);dataTiles[tileIndex]={startCol,startRow,width:tileW,height:tileH}}}this.updateProps({dataTexture:dataTextures[0]??null,dataTextures,dataTiles,dataWidth:width,dataHeight:height})}updateProps(e){super.updateProps(e);const colors=e.colors??this.state.colors;const range=e.range??this.state.range;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,dataTextures,dataTiles,colorTexture,data,range,dataWidth,dataHeight,canvas}=this.state;if(!gl||!program||!quadBuffer||!colorTexture)return;gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);if(0===data.length||0===dataWidth||0===dataHeight||0===dataTiles.length||0===dataTextures.length)return;const[rangeMin,rangeMax]=range;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);const dataTextureLoc=gl.getUniformLocation(program,"u_dataTexture");const colorTextureLoc=gl.getUniformLocation(program,"u_colorTexture");const rangeMinLoc=gl.getUniformLocation(program,"u_rangeMin");const rangeMaxLoc=gl.getUniformLocation(program,"u_rangeMax");if(!dataTextureLoc||!colorTextureLoc||!rangeMinLoc||!rangeMaxLoc)return;gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,colorTexture);gl.uniform1i(colorTextureLoc,1);gl.uniform1f(rangeMinLoc,rangeMin);gl.uniform1f(rangeMaxLoc,rangeMax);const canvasWidth=canvas.width;const canvasHeight=canvas.height;const toPixX=col=>Math.round(col/dataWidth*canvasWidth);const toPixYFromTop=row=>Math.round(row/dataHeight*canvasHeight);for(let i=0;i<dataTiles.length;i++){const tile=dataTiles[i];const texture=dataTextures[i];if(!tile||!texture)continue;const x0=toPixX(tile.startCol);const x1=toPixX(tile.startCol+tile.width);const yTop0=toPixYFromTop(tile.startRow);const yTop1=toPixYFromTop(tile.startRow+tile.height);const y0=canvasHeight-yTop1;const y1=canvasHeight-yTop0;const w=x1-x0;const h=y1-y0;if(!(w<=0)&&!(h<=0)){gl.viewport(x0,y0,w,h);gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,texture);gl.uniform1i(dataTextureLoc,0);gl.drawArrays(gl.TRIANGLES,0,6)}}gl.viewport(0,0,canvasWidth,canvasHeight)}resize(){super.resize()}dispose(){const{gl,program,quadBuffer,dataTextures,colorTexture}=this.state;if(gl){if(program)gl.deleteProgram(program);if(quadBuffer)gl.deleteBuffer(quadBuffer);for(const tex of dataTextures)gl.deleteTexture(tex);if(colorTexture)gl.deleteTexture(colorTexture)}super.dispose()}}class HeatmapCanvas 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)}}function createHeatmap(props){const{renderer=enums_RendererType.Canvas}=props;if(renderer===enums_RendererType.WebGL){if(isWebGL2Supported())return new HeatmapWebGL(props);console.warn("[Heatmap] WebGL 2.0 not supported, falling back to Canvas")}return new HeatmapCanvas(props)}const Heatmap_Heatmap=function(props){return createHeatmap(props)};const cartesian_Heatmap=Heatmap_Heatmap;const ENABLE_AREA_GRADIENT=false;class SeriesCanvas 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:DEFAULTS.THICKNESS,display:true,color:DEFAULTS.COLOR,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??DEFAULTS.COLOR);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 createSeries(props){return new SeriesCanvas(props)}const Series_Series=function(props){return createSeries(props)};const cartesian_Series=Series_Series;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 prevData=this.state.data;const nextData={...prevData,...data};const shouldRedrawTicks=void 0!==data.yawAngle&&data.yawAngle!==prevData?.yawAngle||void 0!==data.isNorthFacing&&data.isNorthFacing!==prevData?.isNorthFacing;this.state.data=nextData;if(shouldRedrawTicks)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:DEFAULTS.DIAL_NORTH_COLOR,alias:"北"},180:{color:DEFAULTS.DIAL_SOUTH_COLOR,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}}}}export{ColorInterpolator,Dial,Fluorescence,Gauge,enums_GraphicType as GraphicType,cartesian_Heatmap as Heatmap,HeatmapCanvas,HeatmapWebGL,IQ,IQEye,enums_OrientationType as OrientationType,Radar,enums_RendererType as RendererType,cartesian_Series as Series,SeriesCanvas,color2intensity,createHeatmap,createSeries,hexToRGBA,rgbToHex};
|
|
46
|
+
`;class HeatmapWebGL extends WebGLEngine{init(props){super.init(props);const{gl}=this.state;if(!gl)return;const colorsInput=props.colors;const colors=colorsInput&&colorsInput.length>0?colorsInput:DEFAULTS.FLUORESCENCE_COLORS;const program=this.createProgram({vertex:VERTEX_SHADER,fragment:FRAGMENT_SHADER});const quadBuffer=this.createQuadBuffer();const dataTexture=this.createTexture();const colorTexture=this.createTexture();const maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE);if(dataTexture){gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,dataTexture);const emptyData=new Float32Array([INVALID_VALUE]);gl.texImage2D(gl.TEXTURE_2D,0,gl.R32F,1,1,0,gl.RED,gl.FLOAT,emptyData)}const range=props.range??DEFAULTS.RANGE;const[rangeMin,rangeMax]=range;const CI=new ColorInterpolator(colors,rangeMin,rangeMax,.1);this.updateProps({data:[],colors,program,quadBuffer,dataTexture,dataTextures:dataTexture?[dataTexture]:[],dataTiles:[],colorTexture,dataWidth:0,dataHeight:0,CI,maxTextureSize});gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);this.updateColorTexture();gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);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.activeTexture(gl.TEXTURE1);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,data}=this.state;if(!gl||0===data.length)return;const height=data.length;let width=0;for(let row=0;row<height;row++){const rowLen=data[row]?.length??0;if(rowLen>width)width=rowLen}if(0===width)return;const maxSize=this.state.maxTextureSize||gl.getParameter(gl.MAX_TEXTURE_SIZE);if(!maxSize||maxSize<=0)return;const tilesX=Math.ceil(width/maxSize);const tilesY=Math.ceil(height/maxSize);const nextTileCount=tilesX*tilesY;let dataTextures=this.state.dataTextures??[];if(dataTextures.length<nextTileCount){const missing=nextTileCount-dataTextures.length;for(let i=0;i<missing;i++){const tex=this.createTexture();if(tex)dataTextures.push(tex)}}else if(dataTextures.length>nextTileCount){for(let i=nextTileCount;i<dataTextures.length;i++)gl.deleteTexture(dataTextures[i]);dataTextures=dataTextures.slice(0,nextTileCount)}const dataTiles=[];gl.activeTexture(gl.TEXTURE0);for(let ty=0;ty<tilesY;ty++){const startRow=ty*maxSize;const tileH=Math.min(maxSize,height-startRow);for(let tx=0;tx<tilesX;tx++){const startCol=tx*maxSize;const tileW=Math.min(maxSize,width-startCol);const tileIndex=ty*tilesX+tx;const texture=dataTextures[tileIndex];if(!texture)continue;const floatData=new Float32Array(tileW*tileH);for(let localRow=0;localRow<tileH;localRow++){const globalRow=startRow+localRow;const rowData=data[globalRow];const rowLen=rowData?.length??0;const rowOffset=localRow*tileW;for(let localCol=0;localCol<tileW;localCol++){const globalCol=startCol+localCol;if(0===rowLen||globalCol>=rowLen){floatData[rowOffset+localCol]=INVALID_VALUE;continue}const value=rowData[globalCol];if(void 0===value||Number.isNaN(value))floatData[rowOffset+localCol]=INVALID_VALUE;else floatData[rowOffset+localCol]=value}}gl.bindTexture(gl.TEXTURE_2D,texture);gl.texImage2D(gl.TEXTURE_2D,0,gl.R32F,tileW,tileH,0,gl.RED,gl.FLOAT,floatData);dataTiles[tileIndex]={startCol,startRow,width:tileW,height:tileH}}}this.updateProps({dataTexture:dataTextures[0]??null,dataTextures,dataTiles,dataWidth:width,dataHeight:height})}updateProps(e){super.updateProps(e);const colors=e.colors??this.state.colors;const range=e.range??this.state.range;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,dataTextures,dataTiles,colorTexture,data,range,dataWidth,dataHeight,canvas}=this.state;if(!gl||!program||!quadBuffer||!colorTexture)return;gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);if(0===data.length||0===dataWidth||0===dataHeight||0===dataTiles.length||0===dataTextures.length)return;const[rangeMin,rangeMax]=range;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);const dataTextureLoc=gl.getUniformLocation(program,"u_dataTexture");const colorTextureLoc=gl.getUniformLocation(program,"u_colorTexture");const rangeMinLoc=gl.getUniformLocation(program,"u_rangeMin");const rangeMaxLoc=gl.getUniformLocation(program,"u_rangeMax");if(!dataTextureLoc||!colorTextureLoc||!rangeMinLoc||!rangeMaxLoc)return;gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,colorTexture);gl.uniform1i(colorTextureLoc,1);gl.uniform1f(rangeMinLoc,rangeMin);gl.uniform1f(rangeMaxLoc,rangeMax);const canvasWidth=canvas.width;const canvasHeight=canvas.height;const toPixX=col=>Math.round(col/dataWidth*canvasWidth);const toPixYFromTop=row=>Math.round(row/dataHeight*canvasHeight);for(let i=0;i<dataTiles.length;i++){const tile=dataTiles[i];const texture=dataTextures[i];if(!tile||!texture)continue;const x0=toPixX(tile.startCol);const x1=toPixX(tile.startCol+tile.width);const yTop0=toPixYFromTop(tile.startRow);const yTop1=toPixYFromTop(tile.startRow+tile.height);const y0=canvasHeight-yTop1;const y1=canvasHeight-yTop0;const w=x1-x0;const h=y1-y0;if(!(w<=0)&&!(h<=0)){gl.viewport(x0,y0,w,h);gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,texture);gl.uniform1i(dataTextureLoc,0);gl.drawArrays(gl.TRIANGLES,0,6)}}gl.viewport(0,0,canvasWidth,canvasHeight)}resize(){super.resize()}dispose(){const{gl,program,quadBuffer,dataTextures,colorTexture}=this.state;if(gl){if(program)gl.deleteProgram(program);if(quadBuffer)gl.deleteBuffer(quadBuffer);for(const tex of dataTextures)gl.deleteTexture(tex);if(colorTexture)gl.deleteTexture(colorTexture)}super.dispose()}}class HeatmapCanvas 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)}}function createHeatmap(props){const{renderer=enums_RendererType.Canvas}=props;if(renderer===enums_RendererType.WebGL){if(isWebGL2Supported())return new HeatmapWebGL(props);console.warn("[Heatmap] WebGL 2.0 not supported, falling back to Canvas")}return new HeatmapCanvas(props)}const Heatmap_Heatmap=function(props){return createHeatmap(props)};const cartesian_Heatmap=Heatmap_Heatmap;const ENABLE_AREA_GRADIENT=false;class SeriesCanvas 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:DEFAULTS.THICKNESS,display:true,color:DEFAULTS.COLOR,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??DEFAULTS.COLOR);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 createSeries(props){return new SeriesCanvas(props)}const Series_Series=function(props){return createSeries(props)};const cartesian_Series=Series_Series;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 prevData=this.state.data;const nextData={...prevData,...data};const shouldRedrawTicks=void 0!==data.yawAngle&&data.yawAngle!==prevData?.yawAngle||void 0!==data.isNorthFacing&&data.isNorthFacing!==prevData?.isNorthFacing;this.state.data=nextData;if(shouldRedrawTicks)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:DEFAULTS.DIAL_NORTH_COLOR,alias:"北"},180:{color:DEFAULTS.DIAL_SOUTH_COLOR,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()})}}class IQEye extends Engine{init(props){super.init(props);this.initColorLookup()}resize(){super.resize();const{width,height}=this.state.canvas;this.state.accumulator=new Uint16Array(width*height);this.state.imageData=this.state.ctx.createImageData(width,height)}clear(){this.clearRect();this.state.accumulator?.fill(0);this.updateProps({data:{IData:[],QData:[]}})}render(data){if(!data.IData?.length||!data.QData?.length)return;this.state.data=data;this.applyDecay();this.accumulateData(data);this.draw()}draw(){const{accumulator,imageData,ctx,colorLookup}=this.state;if(!accumulator||!imageData||!colorLookup)return;const pixelData=imageData.data;const maxAcc=EYE_DEFAULTS.MAX_ACCUMULATION;for(let i=0;i<accumulator.length;i++){const acc=accumulator[i];const idx=4*i;if(acc>0){const intensity=Math.min(acc/maxAcc,1);const colorIdx=Math.min(Math.floor(intensity*(colorLookup.length-1)),colorLookup.length-1);const color=colorLookup[colorIdx];pixelData[idx]=color.r;pixelData[idx+1]=color.g;pixelData[idx+2]=color.b;pixelData[idx+3]=Math.round(255*Math.min(2*intensity,1))}else pixelData[idx+3]=0}ctx.putImageData(imageData,0,0)}initColorLookup(){const colors=EYE_DEFAULTS.COLORS;const steps=256;const lookup=[];for(let i=0;i<steps;i++){const t=i/(steps-1);const segmentCount=colors.length-1;const segment=Math.min(Math.floor(t*segmentCount),segmentCount-1);const localT=t*segmentCount-segment;const c1=parseColor(colors[segment]);const c2=parseColor(colors[segment+1]);lookup.push({r:Math.round(c1.r+(c2.r-c1.r)*localT),g:Math.round(c1.g+(c2.g-c1.g)*localT),b:Math.round(c1.b+(c2.b-c1.b)*localT)})}this.state.colorLookup=lookup}applyDecay(){const{accumulator}=this.state;if(!accumulator)return;const decay=EYE_DEFAULTS.DECAY_FACTOR;for(let i=0;i<accumulator.length;i++)accumulator[i]=Math.floor(accumulator[i]*decay)}accumulateData(data){const{accumulator,canvas}=this.state;if(!accumulator)return;const{width,height}=canvas;const[yMin,yMax]=EYE_DEFAULTS.Y_RANGE;const rangeY=yMax-yMin;const segmentSize=EYE_DEFAULTS.SEGMENT_SIZE;const pointSpacing=width/(segmentSize-1);this.accumulateSegments(data.IData,yMin,rangeY,width,height,segmentSize,pointSpacing,accumulator);this.accumulateSegments(data.QData,yMin,rangeY,width,height,segmentSize,pointSpacing,accumulator)}accumulateSegments(data,yMin,rangeY,width,height,segmentSize,pointSpacing,accumulator){const segmentCount=Math.ceil(data.length/segmentSize);for(let seg=0;seg<segmentCount;seg++){let prevX=-1,prevY=-1;for(let i=0;i<segmentSize;i++){const idx=seg*segmentSize+i;if(idx>=data.length)break;const value=data[idx];if(void 0===value)continue;const x=Math.round(i*pointSpacing);const y=Math.round((value-yMin)/rangeY*(height-1));if(prevX>=0)this.drawLine(prevX,prevY,x,y,width,height,accumulator);prevX=x;prevY=y}}}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,y=y0,err=dx-dy;const maxAcc=EYE_DEFAULTS.MAX_ACCUMULATION;while(true){if(x>=0&&x<width&&y>=0&&y<height){const idx=y*width+x;if(accumulator[idx]<maxAcc)accumulator[idx]++}if(x===x1&&y===y1)break;const e2=2*err;if(e2>-dy){err-=dy;x+=sx}if(e2<dx){err+=dx;y+=sy}}}}export{ColorInterpolator,Dial,Fluorescence,Gauge,enums_GraphicType as GraphicType,cartesian_Heatmap as Heatmap,HeatmapCanvas,HeatmapWebGL,IQ,IQEye,enums_OrientationType as OrientationType,Radar,enums_RendererType as RendererType,cartesian_Series as Series,SeriesCanvas,color2intensity,createHeatmap,createSeries,hexToRGBA,rgbToHex};
|
package/package.json
CHANGED
|
@@ -1 +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;AAMrB,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;
|
|
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;AAMrB,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;IAYzC,cAAc;IAcd,SAAS;IAMT,KAAK;IAQL,OAAO;IAYP,OAAO,CAAC,UAAU;IA4FlB,MAAM;IAKN,QAAQ,CAAC,KAAK,EAAE,KAAK;IAMrB,MAAM,CAAC,IAAI,EAAE,sBAAsB;IAqBnC,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;CA2ML"}
|
|
@@ -2,12 +2,14 @@ import Engine from '../../core/Engine';
|
|
|
2
2
|
import type { InitProps, IQData, IQEyeState } from '../../types';
|
|
3
3
|
export default class IQEye extends Engine<IQEyeState> {
|
|
4
4
|
init(props: InitProps): void;
|
|
5
|
+
resize(): void;
|
|
5
6
|
clear(): void;
|
|
6
7
|
render(data: IQData): void;
|
|
7
8
|
draw(): void;
|
|
8
|
-
private
|
|
9
|
-
private
|
|
10
|
-
private
|
|
9
|
+
private initColorLookup;
|
|
10
|
+
private applyDecay;
|
|
11
|
+
private accumulateData;
|
|
12
|
+
private accumulateSegments;
|
|
11
13
|
private drawLine;
|
|
12
14
|
}
|
|
13
15
|
//# sourceMappingURL=IQEye.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAGjE,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,MAAM,CAAC,UAAU,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,SAAS;IAKrB,MAAM;IAQN,KAAK;IAML,MAAM,CAAC,IAAI,EAAE,MAAM;IAQnB,IAAI;IA0BJ,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,QAAQ;CA0BjB"}
|
package/types/common.d.ts
CHANGED
|
@@ -37,4 +37,18 @@ export declare const DEFAULTS: {
|
|
|
37
37
|
FLUORESCENCE_COLORS: string[];
|
|
38
38
|
RANGE: [number, number];
|
|
39
39
|
};
|
|
40
|
+
/** 荧光谱常量(与 spectrum-analyzer 保持一致) */
|
|
41
|
+
export declare const FLUORESCENCE: {
|
|
42
|
+
readonly LEVEL_MIN: -20;
|
|
43
|
+
readonly LEVEL_MAX: 140;
|
|
44
|
+
readonly LEVEL_RANGE: 161;
|
|
45
|
+
};
|
|
46
|
+
/** 眼图常量 */
|
|
47
|
+
export declare const EYE_DEFAULTS: {
|
|
48
|
+
readonly Y_RANGE: [number, number];
|
|
49
|
+
readonly SEGMENT_SIZE: 5;
|
|
50
|
+
readonly DECAY_FACTOR: 0.98;
|
|
51
|
+
readonly MAX_ACCUMULATION: 1000;
|
|
52
|
+
readonly COLORS: readonly ["#000080", "#0000FF", "#00FFFF", "#00FF00", "#FFFF00", "#FF0000"];
|
|
53
|
+
};
|
|
40
54
|
//# sourceMappingURL=common.d.ts.map
|
package/types/common.d.ts.map
CHANGED
|
@@ -1 +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;;;;;;;;;;yBA0Bd,MAAM,EAAE;WAGQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACtC,CAAC"}
|
|
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;;;;;;;;;;yBA0Bd,MAAM,EAAE;WAGQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACtC,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAEX,WAAW;AACX,eAAO,MAAM,YAAY;sBACH,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;CAK5B,CAAC"}
|
package/types/data.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface GaugeData {
|
|
|
39
39
|
}
|
|
40
40
|
/** Fluorescence 渲染数据 */
|
|
41
41
|
export interface FluorescenceRenderData {
|
|
42
|
-
fluorescenceData:
|
|
42
|
+
fluorescenceData: Uint32Array;
|
|
43
43
|
fluorescenceMaxCount: number;
|
|
44
44
|
}
|
|
45
45
|
//# sourceMappingURL=data.d.ts.map
|
package/types/data.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,WAAW,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;CAC9B"}
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ColorValue, Point, Range, RangeWithSpan, RGBA } from './common';
|
|
2
|
-
export { DEFAULTS } from './common';
|
|
2
|
+
export { DEFAULTS, EYE_DEFAULTS, FLUORESCENCE } from './common';
|
|
3
3
|
export type { AxisYRange, CircularData, CircularSeriesItem, FluorescenceRenderData, GaugeData, IQData, SeriesConfig } from './data';
|
|
4
4
|
export { GraphicType, OrientationType, RendererType } from './enums';
|
|
5
5
|
export type { BaseState, CircularState, FluorescenceState, GaugeDataType, GaugeState, HeatmapState, InitProps, IQEyeState, IQState, SeriesState, StateProps } from './state';
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhE,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,YAAY,EAAE,MAAM,SAAS,CAAC;AAGrE,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
CHANGED
|
@@ -52,7 +52,7 @@ export interface HeatmapState extends BaseState {
|
|
|
52
52
|
}
|
|
53
53
|
/** Fluorescence 状态 */
|
|
54
54
|
export interface FluorescenceState extends BaseState {
|
|
55
|
-
data:
|
|
55
|
+
data: Uint32Array;
|
|
56
56
|
colors: ColorValue[];
|
|
57
57
|
imageData: ImageData;
|
|
58
58
|
fluorescenceMaxCount: number;
|
|
@@ -101,8 +101,13 @@ export interface IQEyeState extends BaseState {
|
|
|
101
101
|
data: IQData;
|
|
102
102
|
lineColor: string;
|
|
103
103
|
pointColor: string;
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
accumulator?: Uint16Array;
|
|
105
|
+
imageData?: ImageData;
|
|
106
|
+
colorLookup?: {
|
|
107
|
+
r: number;
|
|
108
|
+
g: number;
|
|
109
|
+
b: number;
|
|
110
|
+
}[];
|
|
106
111
|
}
|
|
107
112
|
/** 兼容旧版 StateProps - 用于渐进迁移 */
|
|
108
113
|
export type StateProps = InitProps & Partial<BaseState>;
|
package/types/state.d.ts.map
CHANGED
|
@@ -1 +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;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,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,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,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,
|
|
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;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,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,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,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,WAAW,CAAC;IAClB,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,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD;AAED,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC"}
|