This commit is contained in:
锦恢 2024-08-23 22:57:33 +08:00
parent a5a8735cac
commit 9a1642633f
6 changed files with 410 additions and 303 deletions

View File

@ -1,3 +0,0 @@
import { reactive } from "vue";
export const signalModal = reactive();

View File

@ -1,6 +1,8 @@
<template> <template>
<div class=""> <div class="">
<el-radio-group v-model="signalModal"> <el-radio-group v-model="signalModal"
@click="onRadioClick"
>
<!-- 数字模式 --> <!-- 数字模式 -->
<el-radio-button :label="0"> <el-radio-button :label="0">
<el-tooltip <el-tooltip
@ -37,16 +39,41 @@
<script setup> <script setup>
import { defineComponent, ref } from 'vue'; import { globalLookup } from '@/hook/global';
import { defineComponent, nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
// 线 // 线
defineComponent({ name: 'signal-modal' }); defineComponent({ name: 'signal-modal' });
const { t } = useI18n(); const { t } = useI18n();
const signalModal = ref(0); const signalModal = ref(0);
function getOneSelectLink() {
let selectedLink = undefined;
for (const link of globalLookup.sidebarSelectedWireLinks) {
selectedLink = link;
break;
}
return selectedLink;
}
watch(globalLookup.sidebarSelectedWireLinks, () => {
const link = getOneSelectLink();
if (link) {
const option = globalLookup.currentSignalRenderOptions.get(link);
if (option && typeof option.renderModal === 'number') {
signalModal.value = option.renderModal;
} else {
signalModal.value = 0;
}
}
});
</script> </script>
<style scoped> <style scoped>

View File

@ -21,7 +21,7 @@ class ShaderMaker {
gl.compileShader(shader); gl.compileShader(shader);
const ok = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const ok = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!ok) { if (!ok) {
console.log('创建类型为 ' + type + ' 的着色器失败!'); console.log('创建类型为 ' + this.type + ' 的着色器失败!');
} }
return shader; return shader;
} }
@ -30,8 +30,10 @@ class ShaderMaker {
const colorsLength = gl_Colors_template.length << 1; const colorsLength = gl_Colors_template.length << 1;
const vertexShader = new ShaderMaker('VERTEX_SHADER', `#version 300 es const vertexShader = new ShaderMaker('VERTEX_SHADER', `#version 300 es
in uvec4 pos; in ivec2 pos;
in ivec3 control;
out vec4 v_color; out vec4 v_color;
uniform float posYFactor;
uniform vec2 scale; uniform vec2 scale;
uniform vec2 offset; uniform vec2 offset;
uniform vec4 colors[${colorsLength}]; uniform vec4 colors[${colorsLength}];
@ -39,14 +41,24 @@ uniform vec2 shifts[7]; // 基础八位图偏移量为了性能po
uniform vec2 widthShifts[8]; // 用于构造线宽的偏移 uniform vec2 widthShifts[8]; // 用于构造线宽的偏移
void main() { void main() {
v_color = colors[pos.z]; vec2 shift = shifts[control.x];
vec2 shift = shifts[pos.y]; vec2 ws = widthShifts[control.y];
vec2 widthShift = widthShifts[pos.w]; v_color = colors[control.z];
gl_Position = vec4(
float(pos.x) * scale.x + offset.x + float(widthShift.x) + shift.y, // 为了性能传递进来进来的都是整数pos.y 需要除以 posYFactor
float(shift.x) * scale.y + offset.y + float(widthShift.y), float posX = float(pos.x);
1, 1 float posY = float(pos.y) / posYFactor;
);
// 偏移包括三部分:用户滚动时发生的全局位移、为了设置线宽发生的位移、与 scale 无关的额外位移
float offsetX = offset.x + float(ws.x) + float(shift.x);
float offsetY = offset.y + float(ws.y) + float(shift.y);
gl_Position = vec4(
posX * scale.x + offsetX,
posY * scale.y + offsetY,
1,
1
);
}`); }`);
const fragmentShader = new ShaderMaker('FRAGMENT_SHADER', `#version 300 es const fragmentShader = new ShaderMaker('FRAGMENT_SHADER', `#version 300 es

View File

@ -88,24 +88,24 @@ const gl_Shifts = new Float32Array([ // 14
-1, 0 // 6 -1, 0 // 6
]); ]);
// 视屏幕本身而定
const posYFactor = 10000;
const glslInputLength = 5;
// 为了满足 vec 类型的波形的开头和结尾的那个小小的内嵌的、不受scale影响的值 // 为了满足 vec 类型的波形的开头和结尾的那个小小的内嵌的、不受scale影响的值
// 第一列 Y 第二列 X 在所有计算完成后的额外偏移量 // 第一列 Y 第二列 X 在所有计算完成后的额外偏移量
const barShift = 0.004; const barShift = 0.004;
const gl_Shifts_for_bar = new Float32Array([ const gl_Shifts_for_bar = new Float32Array([
0, 0, // 0 0, 0, // 0
1, barShift, // 1 barShift, 0, // 1
1, -barShift, // 2 -barShift, 0, // 2
1, 0, // 3 0, 0, // 3
-1, barShift, // 4 barShift, 0, // 4
-1, -barShift, // 5 -barShift, 0, // 5
-1, 0 // 6 0, 0, // 6
]); ]);
const gl_Shifts_map = new Map();
gl_Shifts_map.set(-1, 4);
gl_Shifts_map.set(0, 0);
gl_Shifts_map.set(1, 1);
const lineWidth = 0.004 * 3800 / screenWidthPixel; // 不能写为 0.0045 这样会因为插值造成不同线段的宽度不一致的问题 const lineWidth = 0.004 * 3800 / screenWidthPixel; // 不能写为 0.0045 这样会因为插值造成不同线段的宽度不一致的问题
const widthShift = 0.002; const widthShift = 0.002;
@ -120,16 +120,33 @@ const gl_WidthShifts = new Float32Array([
widthShift, widthShift // 7 widthShift, widthShift // 7
]); ]);
function prettyPrint(array) {
const stack = [];
for (const num of array) {
if (stack.length === 5) {
console.log(stack);
stack.length = 0;
}
stack.push(num);
}
if (stack.length > 0) {
console.log(stack);
}
}
export { export {
getRatio, getRatio,
gl_Colors, gl_Colors,
gl_Shifts, gl_Shifts,
posYFactor,
lineWidth, lineWidth,
widthShift, widthShift,
gl_WidthShifts, gl_WidthShifts,
gl_Shifts_map,
screenWidthPixel, screenWidthPixel,
screenHeightPixel, screenHeightPixel,
gl_Shifts_for_bar, gl_Shifts_for_bar,
barShift barShift,
glslInputLength,
prettyPrint
}; };

View File

@ -1,8 +1,8 @@
import { globalSetting, globalStyle } from '../global'; import { globalSetting, globalStyle } from '../global';
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_Shifts_map, gl_WidthShifts, barShift, getRatio, screenHeightPixel, maskColorIndexOffset } from './render-utils.js'; import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_WidthShifts, barShift, getRatio, screenHeightPixel, maskColorIndexOffset, posYFactor, glslInputLength, prettyPrint } from './render-utils.js';
import { vertexShader, fragmentShader } from './render-shader.js'; import { vertexShader, fragmentShader } from './render-shader.js';
import { renderAsCommonDigital } from './toolbar/renderModal'; import { renderAsBit, renderAsCommonDigital, renderAsLadderAnalog } from './toolbar/renderModal';
// const { ChangoItem } = require('./types.d.ts'); // const { ChangoItem } = require('./types.d.ts');
@ -47,6 +47,7 @@ class WebGL2WaveRender {
* scale: WebGLUniformLocation, * scale: WebGLUniformLocation,
* offset: WebGLUniformLocation, * offset: WebGLUniformLocation,
* pos: number, * pos: number,
* control: number,
* widthShifts: WebGLUniformLocation, * widthShifts: WebGLUniformLocation,
* gl: WebGL2RenderingContext * gl: WebGL2RenderingContext
* }} * }}
@ -61,9 +62,11 @@ class WebGL2WaveRender {
const webglLocation = { const webglLocation = {
colors: gl.getUniformLocation(program, 'colors'), colors: gl.getUniformLocation(program, 'colors'),
shifts: gl.getUniformLocation(program, 'shifts'), shifts: gl.getUniformLocation(program, 'shifts'),
posYFactor: gl.getUniformLocation(program, 'posYFactor'),
scale: gl.getUniformLocation(program, 'scale'), scale: gl.getUniformLocation(program, 'scale'),
offset: gl.getUniformLocation(program, 'offset'), offset: gl.getUniformLocation(program, 'offset'),
pos: gl.getAttribLocation(program, 'pos'), pos: gl.getAttribLocation(program, 'pos'),
control: gl.getAttribLocation(program, 'control'),
widthShifts: gl.getUniformLocation(program, 'widthShifts'), widthShifts: gl.getUniformLocation(program, 'widthShifts'),
gl gl
}; };
@ -74,8 +77,8 @@ class WebGL2WaveRender {
/** /**
* *
* @returns {{ * @returns {{
* lineVerticesMap: Map<string, Uint32Array>, * lineVerticesMap: Map<string, Int32Array>,
* maskVerticesMap: Map<string, Uint32Array> * maskVerticesMap: Map<string, Int32Array>
* }} * }}
*/ */
makeVertex() { makeVertex() {
@ -98,8 +101,8 @@ class WebGL2WaveRender {
* *
* @param {string} id 波形的 link * @param {string} id 波形的 link
* @returns {{ * @returns {{
* lineVertices: Uint32Array * lineVertices: Int32Array
* maskVertices: Uint32Array * maskVertices: Int32Array
* }} * }}
*/ */
makeVertexByID(id) { makeVertexByID(id) {
@ -122,239 +125,27 @@ class WebGL2WaveRender {
} }
/**
* @description bit 类型的 wave 的值转化为渲染需要用到的值
* @param {string} link
* @param {number} value
* @return {{
* y: number // 裁剪空间的纵坐标
* color: number // 颜色的索引 颜色rgb = gl_Colors[color]
* }}
*/
translateValue2RenderParameter(link, value) {
let colorParam;
switch (value) {
case 0: colorParam = { y: -1, color: 2 }; break; // 0 value
case 1: colorParam = { y: 1, color: 3 }; break; // 1 value
case 2: case 3: colorParam = { y: -1, color: 4 }; break; // 不定态 x
case 4: case 5: colorParam = { y: 0, color: 2 }; break; // 高阻态 z
default: colorParam = { y: -1, color: 7 }; break; // 其他,我也不知道还有啥
}
if (value === 0 || value === 1) {
const renderOptions = this.globalLookup.currentSignalRenderOptions;
if (renderOptions.has(link)) {
const option = renderOptions.get(link);
if (typeof option.color === 'number') {
colorParam.color = option.color;
}
}
}
return colorParam;
}
/**
*
* @param {{x: number, y: number, color: number} | undefined} p0 前一个点
* @param {{x: number, y: number, color: number} | undefined} p1 当前的点
* @param {{x: number, y: number, color: number} | undefined} p2 后一个点
* @returns {number} 这是 widthshift 的索引只需要 + 4 % 8 就能得到另一个
*/
makeWidthShiftIndexByPoints(p0, p1, p2) {
if (p0 === undefined) {
if (p1.y === p2.y) {
return 0;
} else if (p1.x === p2.x) {
return 6;
}
} else if (p2 === undefined) {
if (p1.y === p0.y) {
return 0;
} else if (p1.x === p0.x) {
return 6;
}
} else {
if (p0.x !== p1.x && p0.y === p1.y && p1.x === p2.x && p1.y !== p2.y) {
if (p2.y > p1.y) {
return 1;
} else {
return 7;
}
} else if (p0.x === p1.x && p0.y !== p1.y && p1.x !== p2.x && p1.y === p2.y) {
if (p1.y > p0.y) {
return 1;
} else {
return 7;
}
}
}
return 0;
}
/**
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} color
* @param {number} wsIndex
* @returns {number[]}
*/
makeRectangleVertices(x1, y1, x2, y2, color, wsIndex = 0) {
const r1 = [x1, gl_Shifts_map.get(y2), color, wsIndex];
const r2 = [x1, gl_Shifts_map.get(y1), color, wsIndex];
const r3 = [x2, gl_Shifts_map.get(y1), color, wsIndex];
const r4 = [x2, gl_Shifts_map.get(y2), color, wsIndex];
return this.makeQuadVertices(r1, r2, r3, r4);
}
/**
*
* @param {number[]} p1
* @param {number[]} p2
* @param {number[]} p3
* @param {number[]} p4
* @returns {number[]}
*/
makeQuadVertices(p1, p2, p3, p4) {
return [
...p1, ...p2, ...p3,
...p1, ...p3, ...p4
]
}
/** /**
* *
* @param {string} link * @param {string} link
* @param {Array<string | number>} wave * @param {Array<string | number>} wave
* @param { number } time * @param { number } time
* @returns {{ * @returns {{
* lineVertices: Uint32Array * lineVertices: Int32Array
* maskVertices: Uint32Array * maskVertices: Int32Array
* }} * }}
*/ */
makeBitVertex(link, wave, time, debug = false) { makeBitVertex(link, wave, time, debug = false) {
const length = wave.length;
// 先将节点数据转化为裁剪空间的坐标
const perspectivePoints = [];
for (let i = 0; i < length; ++ i) { const { lineVertices, maskVertices } = renderAsBit(link, wave, time);
// const currentWave = wave[(i === 0) ? 0 : (i - 1)];
const currentWave = wave[i];
const nextWave = (i === (length - 1)) ? wave[i] : wave[i + 1];
const t1 = currentWave[0];
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
const value1 = currentWave[1];
const value2 = nextWave[1];
const renderParam1 = this.translateValue2RenderParameter(link, value1);
const renderParam2 = this.translateValue2RenderParameter(link, value2);
if (i === 0) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
} else {
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if ((lastPoint.y !== renderParam1.y) || (lastPoint.color !== renderParam1.color)) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
}
if ((renderParam1.y !== renderParam2.y) || (renderParam1.color !== renderParam2.color)) {
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
}
}
}
// 确保最后一个点延申到了 time
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if (lastPoint.x < time) {
perspectivePoints.push({ x: time, y: lastPoint.y, color: lastPoint.color });
}
// 计算出传入 shader 四元组数组
// 四元组: (x, yshift_index, color_index, width_shift_index)
const pointNum = perspectivePoints.length;
const lineVertices = [];
const maskVertices = [];
// 制作 lineVertices
for (let i = 0; i < pointNum; ++ i) {
// p0: 上一个点
// p1: 当前的点
// p2: 下一个点
const p0 = perspectivePoints[i - 1];
const p1 = perspectivePoints[i];
const p2 = perspectivePoints[i + 1];
// p1.x, y1Index, p1.color, 0,
const y1Index = gl_Shifts_map.get(p1.y);
const wsIndex = this.makeWidthShiftIndexByPoints(p0, p1, p2);
if (wsIndex === undefined) {
console.log(p0, p1, p2);
}
lineVertices.push(
p1.x, y1Index, p1.color, wsIndex,
p1.x, y1Index, p1.color, (wsIndex + 4) % 8
);
// 防止颜色不同导致单个图元内出现两个颜色这会引发shader的渐变
if (p2 !== undefined && p1.color !== p2.color) {
lineVertices.push(
p1.x, y1Index, p2.color, wsIndex,
p1.x, y1Index, p2.color, (wsIndex + 4) % 8
);
}
}
// 制作 maskVertices
for (let i = 0; i < pointNum; ++ i) {
const p1 = perspectivePoints[i];
// 开头就有一条线
if (i === 0 && p1.y === 1) {
while (perspectivePoints[++ i] && perspectivePoints[i].y === 1);
// 回退
if (-- i > 0) {
// 四元组: (x, yshift_index, color_index, width_shift_index)
const rectangleVertices = this.makeRectangleVertices(p1.x, 1, perspectivePoints[i].x, -1, p1.color + maskColorIndexOffset, 4);
// 三角图元画矩形
maskVertices.push(...rectangleVertices);
continue;
}
}
// 上升沿才使用
const p2 = perspectivePoints[i + 1];
const p3 = perspectivePoints[i + 2];
if (p1 === undefined || p2 === undefined || p3 === undefined) {
continue;
}
if (p2.y > p1.y) {
// 矩形的四个点
// 四元组: (x, yshift_index, color_index, width_shift_index)
const rectangleVertices = this.makeRectangleVertices(p2.x, p2.y, p3.x, p1.y, p2.color + maskColorIndexOffset, 4);
// 三角图元画矩形
maskVertices.push(...rectangleVertices);
}
}
if (debug) { if (debug) {
console.log(perspectivePoints);
console.log(pointNum);
console.log(lineVertices); console.log(lineVertices);
} }
return { return {
lineVertices : new Uint32Array(lineVertices), lineVertices : new Int32Array(lineVertices),
maskVertices : new Uint32Array(maskVertices) maskVertices : new Int32Array(maskVertices)
}; };
} }
@ -364,12 +155,13 @@ class WebGL2WaveRender {
* @param {Array<string | number>} wave * @param {Array<string | number>} wave
* @param { number } time * @param { number } time
* @returns {{ * @returns {{
* lineVertices: Uint32Array * lineVertices: Int32Array
* maskVertices: Uint32Array * maskVertices: Int32Array
* }} * }}
*/ */
makeVecVertex(link, wave, time, debug = false) { makeVecVertex(link, wave, time, debug = false) {
const lookup = this.globalLookup; const lookup = this.globalLookup;
const { lineVertices, maskVertices } = renderAsCommonDigital(lookup, link, wave, time); const { lineVertices, maskVertices } = renderAsCommonDigital(lookup, link, wave, time);
if (debug) { if (debug) {
@ -377,13 +169,24 @@ class WebGL2WaveRender {
} }
return { return {
lineVertices: new Uint32Array(lineVertices), lineVertices: new Int32Array(lineVertices),
maskVertices: new Uint32Array(maskVertices) maskVertices: new Int32Array(maskVertices)
}; };
} }
setShaderInput() {
const UIntSize = Int32Array.BYTES_PER_ELEMENT; // 4
const webglLocation = this.webglLocation;
const gl = webglLocation.gl;
gl.vertexAttribIPointer(webglLocation.pos, 2, gl.INT, 5 * UIntSize, 0);
gl.vertexAttribIPointer(webglLocation.control, 3, gl.INT, 5 * UIntSize, 2 * UIntSize);
gl.enableVertexAttribArray(webglLocation.pos);
gl.enableVertexAttribArray(webglLocation.control);
}
/** /**
* * @description 根据 id link 将对应信号的 value 数据转换成对应视图的 VAO 以供 webgl 渲染
* @param {string} id * @param {string} id
* @returns * @returns
*/ */
@ -407,8 +210,7 @@ class WebGL2WaveRender {
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW);
signalItem.lineVao = gl.createVertexArray(); signalItem.lineVao = gl.createVertexArray();
gl.bindVertexArray(signalItem.lineVao); gl.bindVertexArray(signalItem.lineVao);
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0); this.setShaderInput();
gl.enableVertexAttribArray(webglLocation.pos);
// 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置 // 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置
const maskVertexBuffer = gl.createBuffer(); const maskVertexBuffer = gl.createBuffer();
@ -416,8 +218,7 @@ class WebGL2WaveRender {
gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW);
signalItem.maskVao = gl.createVertexArray(); signalItem.maskVao = gl.createVertexArray();
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.maskVao);
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0); this.setShaderInput();
gl.enableVertexAttribArray(webglLocation.pos);
} }
/** /**
@ -439,8 +240,7 @@ class WebGL2WaveRender {
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW);
signalItem.lineVao = gl.createVertexArray(); signalItem.lineVao = gl.createVertexArray();
gl.bindVertexArray(signalItem.lineVao); gl.bindVertexArray(signalItem.lineVao);
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0); this.setShaderInput();
gl.enableVertexAttribArray(webglLocation.pos);
// 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置 // 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置
const maskVertexBuffer = gl.createBuffer(); const maskVertexBuffer = gl.createBuffer();
@ -448,8 +248,7 @@ class WebGL2WaveRender {
gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW);
signalItem.maskVao = gl.createVertexArray(); signalItem.maskVao = gl.createVertexArray();
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.maskVao);
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0); this.setShaderInput();
gl.enableVertexAttribArray(webglLocation.pos);
} }
initData() { initData() {
@ -464,6 +263,7 @@ class WebGL2WaveRender {
gl.uniform4fv(webglLocation.colors, gl_Colors); gl.uniform4fv(webglLocation.colors, gl_Colors);
gl.uniform2fv(webglLocation.widthShifts, gl_WidthShifts); gl.uniform2fv(webglLocation.widthShifts, gl_WidthShifts);
gl.uniform2fv(webglLocation.shifts, gl_Shifts); gl.uniform2fv(webglLocation.shifts, gl_Shifts);
gl.uniform1f(webglLocation.posYFactor, posYFactor);
} }
/** /**
@ -574,7 +374,7 @@ class WebGL2WaveRender {
// 设置 webgl 和 canvas 大小位置一致 // 设置 webgl 和 canvas 大小位置一致
gl.viewport(0, 0, canvasWidth, canvasHeight); gl.viewport(0, 0, canvasWidth, canvasHeight);
// 清颜色缓冲区,也就是删除上一次的渲染结果 // 清颜色缓冲区,也就是删除上一次的渲染结果
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
// 根据 currentWiresRenderView 视图渲染 // 根据 currentWiresRenderView 视图渲染
@ -626,18 +426,18 @@ class WebGL2WaveRender {
// 如果是 width 为 1 的 // 如果是 width 为 1 的
gl.uniform2fv(webglLocation.shifts, gl_Shifts); gl.uniform2fv(webglLocation.shifts, gl_Shifts);
gl.bindVertexArray(signalItem.lineVao); gl.bindVertexArray(signalItem.lineVao);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, lineVertices.length / 4); gl.drawArrays(gl.TRIANGLE_STRIP, 0, lineVertices.length / glslInputLength);
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / 4); gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
} else { } else {
// 如果是 width 大于 1 的 // 如果是 width 大于 1 的
gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar); gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar);
gl.bindVertexArray(signalItem.lineVao); gl.bindVertexArray(signalItem.lineVao);
gl.drawArrays(gl.TRIANGLES, 0, lineVertices.length / 4); gl.drawArrays(gl.TRIANGLES, 0, lineVertices.length / glslInputLength);
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / 4); gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
} }
} }

View File

@ -13,7 +13,23 @@
* @property {number} minVal * @property {number} minVal
*/ */
import { maskColorIndexOffset } from "../render-utils"; /**
* @description
* @typedef {Object} BitRenderTempStruct
* @property {number} color 颜色的索引 颜色rgb = gl_Colors[color]
* @property {number} y 裁剪空间的纵坐标
*/
/**
* @description
* @typedef {Object} BitRenderHandlePoint
* @property {number} x
* @property {number} y
* @property {number} color
*/
import { globalLookup } from "@/hook/global";
import { maskColorIndexOffset, posYFactor } from "../render-utils";
import { hexToSignedInt } from "./renderFormat"; import { hexToSignedInt } from "./renderFormat";
@ -24,6 +40,227 @@ function makeQuadVertices(p1, p2, p3, p4) {
] ]
} }
/**
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} color
* @param {number} wsIndex
* @returns {number[]}
*/
function makeRectangleVertices(x1, y1, x2, y2, color, wsIndex = 0) {
// uvec2 pos; uvec3 control;
const r1 = [x1, y2 * posYFactor, 0, wsIndex, color];
const r2 = [x1, y1 * posYFactor, 0, wsIndex, color];
const r3 = [x2, y1 * posYFactor, 0, wsIndex, color];
const r4 = [x2, y2 * posYFactor, 0, wsIndex, color];
return makeQuadVertices(r1, r2, r3, r4);
}
/**
* @description bit 类型的 wave 的值转化为渲染需要用到的值
* @param {string} link
* @param {number[]} wave
* @param {number} time
* @return {BitRenderTempStruct}
*/
function makeBitRenderParam(link, wave, time) {
const value = wave[1];
let colorParam;
switch (value) {
case 0: colorParam = { y: -1, color: 2 }; break; // 0 value
case 1: colorParam = { y: 1, color: 3 }; break; // 1 value
case 2: case 3: colorParam = { y: -1, color: 4 }; break; // 不定态 x
case 4: case 5: colorParam = { y: 0, color: 2 }; break; // 高阻态 z
default: colorParam = { y: -1, color: 7 }; break; // 其他,我也不知道还有啥
}
if (value === 0 || value === 1) {
const renderOptions = globalLookup.currentSignalRenderOptions;
if (renderOptions.has(link)) {
const option = renderOptions.get(link);
if (typeof option.color === 'number') {
colorParam.color = option.color;
}
}
}
return colorParam;
}
/**
*
* @param {BitRenderHandlePoint | undefined} p0 前一个点
* @param {BitRenderHandlePoint | undefined} p1 当前的点
* @param {BitRenderHandlePoint | undefined} p2 后一个点
* @returns {number} 这是 widthshift 的索引只需要 + 4 % 8 就能得到另一个
*/
function makeWidthShiftIndexByPoints(p0, p1, p2) {
if (p0 === undefined) {
if (p1.y === p2.y) {
return 0;
} else if (p1.x === p2.x) {
return 6;
}
} else if (p2 === undefined) {
if (p1.y === p0.y) {
return 0;
} else if (p1.x === p0.x) {
return 6;
}
} else {
if (p0.x !== p1.x && p0.y === p1.y && p1.x === p2.x && p1.y !== p2.y) {
if (p2.y > p1.y) {
return 1;
} else {
return 7;
}
} else if (p0.x === p1.x && p0.y !== p1.y && p1.x !== p2.x && p1.y === p2.y) {
if (p1.y > p0.y) {
return 1;
} else {
return 7;
}
}
}
return 0;
}
/**
*
* @param {string} link
* @param {Array<string | number>} wave
* @param { number } time
* @param {(link: string, wave: number[], time: number) => BitRenderTempStruct} renderParamMaker
* @returns {VecRenderNumberVertices}
*/
function renderBlockWave(link, wave, time, renderParamMaker) {
const length = wave.length;
// 先将节点数据转化为裁剪空间的坐标
const perspectivePoints = [];
for (let i = 0; i < length; ++ i) {
// const currentWave = wave[(i === 0) ? 0 : (i - 1)];
const currentWave = wave[i];
const nextWave = (i === (length - 1)) ? wave[i] : wave[i + 1];
const t1 = currentWave[0];
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
const value1 = currentWave[1];
const value2 = nextWave[1];
const renderParam1 = renderParamMaker(link, currentWave, time);
const renderParam2 = renderParamMaker(link, nextWave, time);
if (i === 0) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
} else {
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if ((lastPoint.y !== renderParam1.y) || (lastPoint.color !== renderParam1.color)) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
}
if ((renderParam1.y !== renderParam2.y) || (renderParam1.color !== renderParam2.color)) {
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
}
}
}
// 确保最后一个点延申到了 time
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if (lastPoint.x < time) {
perspectivePoints.push({ x: time, y: lastPoint.y, color: lastPoint.color });
}
// 计算出传入 shader 四元组数组
// 四元组: (x, yshift_index, color_index, width_shift_index)
const pointNum = perspectivePoints.length;
const lineVertices = [];
const maskVertices = [];
// 制作 lineVertices
for (let i = 0; i < pointNum; ++ i) {
// p0: 上一个点
// p1: 当前的点
// p2: 下一个点
const p0 = perspectivePoints[i - 1];
const p1 = perspectivePoints[i];
const p2 = perspectivePoints[i + 1];
const wsIndex = makeWidthShiftIndexByPoints(p0, p1, p2);
if (wsIndex === undefined) {
console.log(p0, p1, p2);
}
lineVertices.push(
// uvec2 pos; uvec3 control;
p1.x, p1.y * posYFactor, 0, wsIndex, p1.color,
p1.x, p1.y * posYFactor, 0, (wsIndex + 4) % 8, p1.color
);
// 防止颜色不同导致单个图元内出现两个颜色这会引发shader的渐变
if (p2 !== undefined && p1.color !== p2.color) {
lineVertices.push(
// uvec2 pos; uvec3 control;
p1.x, p1.y * posYFactor, 0, wsIndex, p2.color,
p1.x, p1.y * posYFactor, 0, (wsIndex + 4) % 8, p2.color,
);
}
}
// 制作 maskVertices
for (let i = 0; i < pointNum; ++ i) {
const p1 = perspectivePoints[i];
// 开头就有一条线
if (i === 0 && p1.y === 1) {
while (perspectivePoints[++ i] && perspectivePoints[i].y === 1);
// 回退
if (-- i > 0) {
// 四元组: (x, yshift_index, color_index, width_shift_index)
const rectangleVertices = makeRectangleVertices(p1.x, 1, perspectivePoints[i].x, -1, p1.color + maskColorIndexOffset, 4);
// 三角图元画矩形
maskVertices.push(...rectangleVertices);
continue;
}
}
// 上升沿才使用
const p2 = perspectivePoints[i + 1];
const p3 = perspectivePoints[i + 2];
if (p1 === undefined || p2 === undefined || p3 === undefined) {
continue;
}
if (p2.y > p1.y) {
// 矩形的四个点
// 四元组: (x, yshift_index, color_index, width_shift_index)
const rectangleVertices = makeRectangleVertices(p2.x, p2.y, p3.x, p1.y, p2.color + maskColorIndexOffset, 4);
// 三角图元画矩形
maskVertices.push(...rectangleVertices);
}
}
return { lineVertices, maskVertices };
}
/**
*
* @param {string} link
* @param {Array<string | number>} wave
* @param { number } time
* @returns {VecRenderNumberVertices}
*/
export function renderAsBit(link, wave, time) {
return renderBlockWave(link, wave, time, makeBitRenderParam);
}
/** /**
* @description Digital * @description Digital
* @param {GlobalLookup} lookup * @param {GlobalLookup} lookup
@ -43,13 +280,13 @@ export function renderAsCommonDigital(lookup, link, wave, time) {
// t1(val) --- t2(val) // t1(val) --- t2(val)
// 详见 设计图 ./design/webgl.drawio makeVecVertex原理 sheet // 详见 设计图 ./design/webgl.drawio makeVecVertex原理 sheet
// 此处的 y 不是 y 的索引,详见 gl_Shifts_for_bar 的设计 // 详见 gl_Shifts_for_bar 的设计
const p0 = {x: t1, y: 0}; const p0 = {x: t1, y: 0, shift: 0};
const p1 = {x: t2, y: 0}; const p1 = {x: t2, y: 0, shift: 0};
const a0 = {x: t1, y: 4}; const a0 = {x: t1, y: -1, shift: 4};
const a1 = {x: t2, y: 5}; const a1 = {x: t2, y: -1, shift: 5};
const a2 = {x: t1, y: 1}; const a2 = {x: t1, y: 1, shift: 1};
const a3 = {x: t2, y: 2}; const a3 = {x: t2, y: 1, shift: 2};
let color = 5; let color = 5;
const renderOptions = lookup.currentSignalRenderOptions; const renderOptions = lookup.currentSignalRenderOptions;
@ -70,10 +307,10 @@ export function renderAsCommonDigital(lookup, link, wave, time) {
const preWsIndex = wsIndice[j]; const preWsIndex = wsIndice[j];
const nextWsIndex = wsIndice[(j + 1) % 6]; const nextWsIndex = wsIndice[(j + 1) % 6];
const quadVertices = makeQuadVertices( const quadVertices = makeQuadVertices(
[prePoint.x, prePoint.y, color, preWsIndex], [prePoint.x, prePoint.y * posYFactor, prePoint.shift, preWsIndex, color],
[prePoint.x, prePoint.y, color, (preWsIndex + 4) % 8], [prePoint.x, prePoint.y * posYFactor, prePoint.shift, (preWsIndex + 4) % 8, color],
[nextPoint.x, nextPoint.y, color, (nextWsIndex + 4) % 8], [nextPoint.x, nextPoint.y * posYFactor, nextPoint.shift, (nextWsIndex + 4) % 8, color],
[nextPoint.x, nextPoint.y, color, nextWsIndex], [nextPoint.x, nextPoint.y * posYFactor, nextPoint.shift, nextWsIndex, color],
); );
lineVertices.push(...quadVertices); lineVertices.push(...quadVertices);
} }
@ -91,9 +328,9 @@ export function renderAsCommonDigital(lookup, link, wave, time) {
const point2 = points[i2]; const point2 = points[i2];
const point3 = points[i3]; const point3 = points[i3];
maskVertices.push( maskVertices.push(
point1.x, point1.y, color + maskColorIndexOffset, wsIndice[i1], point1.x, point1.y * posYFactor, point1.shift, wsIndice[i1], color + maskColorIndexOffset,
point2.x, point2.y, color + maskColorIndexOffset, wsIndice[i2], point2.x, point2.y * posYFactor, point2.shift, wsIndice[i2], color + maskColorIndexOffset,
point3.x, point3.y, color + maskColorIndexOffset, wsIndice[i3] point3.x, point3.y * posYFactor, point3.shift, wsIndice[i3], color + maskColorIndexOffset,
); );
} }
} }
@ -156,9 +393,10 @@ function explainAsJSNumber(formatCode, val, width) {
function getMaxMinByFormat(link, wave, time, formatCode, width) { function getMaxMinByFormat(link, wave, time, formatCode, width) {
let maxVal = undefined; let maxVal = undefined;
let minVal = undefined; let minVal = undefined;
const length = wave.length;
// TODO : 优化这段代码 // TODO : 优化这段代码
for (let i = 0; i < wave.length; ++ i) { for (let i = 0; i < length; ++ i) {
const [t1, val, mask] = wave[i]; const [t1, val, mask] = wave[i];
const t2 = (i === (length - 1)) ? time : wave[i + 1][0]; const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
const numVal = explainAsJSNumber(formatCode, val, width); const numVal = explainAsJSNumber(formatCode, val, width);
@ -189,11 +427,17 @@ function getMaxMinByFormat(link, wave, time, formatCode, width) {
function getMappingFunc(formatCode, maxVal, minVal) { function getMappingFunc(formatCode, maxVal, minVal) {
if (formatCode === 3) { if (formatCode === 3) {
const maxAbs = Math.max(Math.abs(maxVal), Math.abs(minVal)); const maxAbs = Math.max(Math.abs(maxVal), Math.abs(minVal));
if (maxAbs === 0) {
return val => 0;
}
const k = 1 / maxAbs; const k = 1 / maxAbs;
const b = -1; const b = -1;
// 把 [-maxAbs, +maxAbs] 映射到 [-1, 1] // 把 [-maxAbs, +maxAbs] 映射到 [-1, 1]
return val => k * (val + maxAbs) + b; return val => k * (val + maxAbs) + b;
} else { } else {
if (maxVal === 0) {
return val => -1;
}
const k = 2 / maxVal; const k = 2 / maxVal;
const b = -1; const b = -1;
// 把 [0, max] 映射到 [-1, 1] // 把 [0, max] 映射到 [-1, 1]
@ -215,33 +459,39 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
const signal = lookup.link2CurrentWires.get(link); const signal = lookup.link2CurrentWires.get(link);
const width = signal.size; const width = signal.size;
const { maxVal, minVal } = getMaxMinByFormat(lookup, link, wave, time, formatCode, width); const { maxVal, minVal } = getMaxMinByFormat(link, wave, time, formatCode, width);
const coordinateTransform = getMappingFunc(formatCode, maxVal, minVal); const coordinateTransform = getMappingFunc(formatCode, maxVal, minVal);
const length = wave.length; const length = wave.length;
const lineVertices = []; const lineVertices = [];
const maskVertices = []; const maskVertices = [];
for (let i = 0; i < length; ++ i) { function makeLadderAnalogRenderParam(link, wave, time) {
const [t1, val, mask] = wave[i]; const [t1, val, mask] = wave;
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
let color = 5;
if (mask) { if (mask) {
color = 4; // 不定态
} else if (renderOptions.has(link)) { return { y: -1, color: 4 };
}
// 根据当前格式进行转换
const numVal = explainAsJSNumber(formatCode, val, width);
// 此时的 y 是一个 -1 到 1 的浮点数,因为 VAO 我设置了 Int
const y = coordinateTransform(numVal);
console.log(t1, y);
const colorParam = { y, color: 5 };
if (renderOptions.has(link)) {
const option = renderOptions.get(link); const option = renderOptions.get(link);
if (typeof option.color === 'number') { if (typeof option.color === 'number') {
color = option.color; colorParam.color = option.color;
} }
} }
return colorParam;
const numVal = explainAsJSNumber(formatCode, val, width);
const y = coordinateTransform(numVal);
} }
return renderBlockWave(link, wave, time, makeLadderAnalogRenderParam);
} }
/** /**
@ -255,7 +505,11 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
export function renderAsLineAnalog(lookup, link, wave, time) { export function renderAsLineAnalog(lookup, link, wave, time) {
const renderOptions = lookup.currentSignalRenderOptions; const renderOptions = lookup.currentSignalRenderOptions;
const formatCode = getValFormatCode(lookup, link); const formatCode = getValFormatCode(lookup, link);
const signal = lookup.link2CurrentWires.get(link);
const width = signal.size;
const { maxVal, minVal } = getMaxMinByFormat(link, wave, time, formatCode, width);
const { maxVal, minVal } = getMaxMinByFormat(lookup, link, wave, time, formatCode);
} }