修复修改高度导致渲染器不对齐 | 完成阶梯状的渲染

This commit is contained in:
锦恢 2024-08-24 22:50:11 +08:00
parent 9a1642633f
commit 7ff8576bc9
6 changed files with 278 additions and 68 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<div class=""> <div class="">
<el-radio-group v-model="signalModal" <el-radio-group v-model="signalModal"
@click="onRadioClick" @change="onSignalModalUpdate"
> >
<!-- 数字模式 --> <!-- 数字模式 -->
<el-radio-button :label="0"> <el-radio-button :label="0">
@ -14,7 +14,7 @@
<span class="iconfont icon-common-digital"></span> <span class="iconfont icon-common-digital"></span>
</el-tooltip> </el-tooltip>
</el-radio-button> </el-radio-button>
<!-- 折现模拟 --> <!-- 阶梯模拟 -->
<el-radio-button :label="1"> <el-radio-button :label="1">
<el-tooltip <el-tooltip
effect="dark" effect="dark"
@ -23,7 +23,7 @@
raw-content raw-content
><span class="iconfont icon-ladder-analog"></span></el-tooltip> ><span class="iconfont icon-ladder-analog"></span></el-tooltip>
</el-radio-button> </el-radio-button>
<!-- 阶梯模拟 --> <!-- 折线模拟 -->
<el-radio-button :label="2"> <el-radio-button :label="2">
<el-tooltip <el-tooltip
effect="dark" effect="dark"
@ -49,6 +49,20 @@ const { t } = useI18n();
const signalModal = ref(0); const signalModal = ref(0);
function onSignalModalUpdate() {
const links = globalLookup.sidebarSelectedWireLinks;
if (links.size > 0) {
const modal = signalModal.value;
//
for (const link of links) {
globalLookup.setRenderOption(link, 'renderModal', modal);
globalLookup.waveRender.resetVertice(link);
}
//
globalLookup.render();
}
}
function getOneSelectLink() { function getOneSelectLink() {
let selectedLink = undefined; let selectedLink = undefined;
@ -60,7 +74,6 @@ function getOneSelectLink() {
} }
watch(globalLookup.sidebarSelectedWireLinks, () => { watch(globalLookup.sidebarSelectedWireLinks, () => {
const link = getOneSelectLink(); const link = getOneSelectLink();
if (link) { if (link) {

View File

@ -52,7 +52,7 @@ void main() {
// 偏移包括三部分:用户滚动时发生的全局位移、为了设置线宽发生的位移、与 scale 无关的额外位移 // 偏移包括三部分:用户滚动时发生的全局位移、为了设置线宽发生的位移、与 scale 无关的额外位移
float offsetX = offset.x + float(ws.x) + float(shift.x); float offsetX = offset.x + float(ws.x) + float(shift.x);
float offsetY = offset.y + float(ws.y) + float(shift.y); float offsetY = offset.y + float(ws.y) + float(shift.y);
gl_Position = vec4( gl_Position = vec4(
posX * scale.x + offsetX, posX * scale.x + offsetX,
posY * scale.y + offsetY, posY * scale.y + offsetY,

View File

@ -8,7 +8,7 @@ import vline from './vline';
import getX from './get-x'; import getX from './get-x';
import vlineStylo from './vline-stylo'; import vlineStylo from './vline-stylo';
import getLabel from './get-label'; import getLabel from './get-label';
import { globalLookup } from '../global.js'; import { globalLookup, globalSetting } from '../global.js';
const defs = ['defs', const defs = ['defs',
['linearGradient', { id: 'valid' }, ['linearGradient', { id: 'valid' },
@ -33,7 +33,49 @@ const defs = ['defs',
}) })
]; ];
/**
* @param {string} link
* @returns {number}
*/
function getVecRenderModal(link) {
const renderOptions = globalLookup.currentSignalRenderOptions;
if (renderOptions.has(link)) {
const option = renderOptions.get(link);
if (typeof option.renderModal === 'number') {
return option.renderModal;
}
}
return 0;
}
/**
* @description 根据 link 得到对应的 value 渲染模式
* @param {*} link
*/
function addVecRenderItem(link) {
const link2CurrentWires = globalLookup.link2CurrentWires;
const modal = getVecRenderModal(link);
if (modal === 0) {
const signal = link2CurrentWires.get(link);
return {
name: signal.name,
kind: signal.kind,
ref: signal.link
}
} else if (modal === 1) {
return {
name: '',
kind: '',
ref: ''
}
} else {
return {
name: '',
kind: '',
ref: ''
}
}
}
/** /**
@ -52,15 +94,10 @@ function* renderValues(desc, pstate) {
const currentWires = desc.currentWires; const currentWires = desc.currentWires;
const renderSignals = []; const renderSignals = [];
const link2CurrentWires = globalLookup.link2CurrentWires;
for (const view of globalLookup.currentWiresRenderView) { for (const view of globalLookup.currentWiresRenderView) {
if (view.renderType === 0) { if (view.renderType === 0) {
const realSignal = link2CurrentWires.get(view.signalInfo.link); const realSignal = addVecRenderItem(view.signalInfo.link);
renderSignals.push({ renderSignals.push(realSignal);
name: realSignal.name,
kind: realSignal.kind,
ref: realSignal.link
});
} else { } else {
// 如果是组,需要渲染空白的一行 // 如果是组,需要渲染空白的一行
renderSignals.push({ renderSignals.push({
@ -71,21 +108,17 @@ function* renderValues(desc, pstate) {
// 如果没有关闭,把所有子节点加入其中 // 如果没有关闭,把所有子节点加入其中
if (view.groupInfo.collapse === false) { if (view.groupInfo.collapse === false) {
for (const child of view.children) { for (const child of view.children) {
const realSignal = link2CurrentWires.get(child.signalInfo.link); const realSignal = addVecRenderItem(view.signalInfo.link);
renderSignals.push({ renderSignals.push(realSignal);
name: realSignal.name,
kind: realSignal.kind,
ref: realSignal.link
});
} }
} }
} }
} }
const lineHeightExtra = (globalSetting.displaySignalHeight - 30) / 2;
const ilen = height / yStep; const ilen = height / yStep;
const iskip = yOffset / yStep; const iskip = (yOffset - lineHeightExtra) / yStep;
const ml = genSVG(width, height - topBarHeight - botBarHeight); const ml = genSVG(width, height - topBarHeight - botBarHeight);

View File

@ -2,7 +2,14 @@ import { globalSetting, globalStyle } from '../global';
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_WidthShifts, barShift, getRatio, screenHeightPixel, maskColorIndexOffset, posYFactor, glslInputLength, prettyPrint } 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 { renderAsBit, renderAsCommonDigital, renderAsLadderAnalog } from './toolbar/renderModal'; import { renderAsBit, renderAsCommonDigital, renderAsLadderAnalog, renderAsLineAnalog } from './toolbar/renderModal';
/**
* @description
* @typedef {Object} VecRenderNumberVertices
* @property {number[]} lineVertices
* @property {number[]} maskVertices
*/
// const { ChangoItem } = require('./types.d.ts'); // const { ChangoItem } = require('./types.d.ts');
@ -149,6 +156,42 @@ class WebGL2WaveRender {
}; };
} }
/**
* @param {GlobalLookup} lookup
* @param {string} link
* @returns {number}
*/
getVecRenderModal(lookup, link) {
const renderOptions = lookup.currentSignalRenderOptions;
if (renderOptions.has(link)) {
const option = renderOptions.get(link);
if (typeof option.renderModal === 'number') {
return option.renderModal;
}
}
return 0;
}
/**
* @param {GlobalLookup} lookup
* @param {string} link
* @param {Array<string | number>} wave
* @param { number } time
* @returns {(lookup: GlobalLookup, link: string, wave: number[], time: number) => VecRenderNumberVertices}
*/
selectVecRenderFn(lookup, link, wave, time) {
const modal = this.getVecRenderModal(lookup, link);
switch (modal) {
case 0:
return renderAsCommonDigital;
case 1:
return renderAsLadderAnalog;
case 2:
return renderAsLineAnalog;
default:
return renderAsCommonDigital;
}
}
/** /**
* @param {string} link * @param {string} link
@ -161,8 +204,8 @@ class WebGL2WaveRender {
*/ */
makeVecVertex(link, wave, time, debug = false) { makeVecVertex(link, wave, time, debug = false) {
const lookup = this.globalLookup; const lookup = this.globalLookup;
const vecRenderFn = this.selectVecRenderFn(lookup, link, wave, time);
const { lineVertices, maskVertices } = renderAsCommonDigital(lookup, link, wave, time); const { lineVertices, maskVertices } = vecRenderFn(lookup, link, wave, time);
if (debug) { if (debug) {
console.log(lineVertices); console.log(lineVertices);
@ -326,7 +369,7 @@ class WebGL2WaveRender {
const plugins = this.plugins; const plugins = this.plugins;
pstate.yStep = globalSetting.displaySignalHeight + globalStyle.sideBarItemMargin; pstate.yStep = globalSetting.displaySignalHeight + globalStyle.sideBarItemMargin;
pstate.yDuty = (1 - 2 * globalStyle.sideBarItemMargin / this.pstate.yStep) * 0.95; pstate.yDuty = (1 - 2 * globalStyle.sideBarItemMargin / pstate.yStep) * 0.95;
document.body.style.setProperty('--vcd-render-padding', pstate.topBarHeight + 'px'); document.body.style.setProperty('--vcd-render-padding', pstate.topBarHeight + 'px');
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight; const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
@ -411,8 +454,12 @@ class WebGL2WaveRender {
return; return;
} }
const offsetX = 2 * xOffset / canvasWidth - 1; // 区间映射公式,将 [a, b] 内的数字映射到 [c, d] 中
const offsetY = 2 * (yOffset - yStep * index) / canvasHeight + 1; // $f(x) = \frac{d - c}{b - a} (x - a) + c$
const offsetX = 2 / canvasWidth * xOffset - 1;
const lineHeightExtra = (globalSetting.displaySignalHeight - 30) / 2;
const offsetY = 2 / canvasHeight * (yOffset - yStep * index - lineHeightExtra) + 1;
gl.uniform2f(webglLocation.offset, offsetX, offsetY); gl.uniform2f(webglLocation.offset, offsetX, offsetY);
// 根据 lineVao 进行绘制 // 根据 lineVao 进行绘制
@ -423,7 +470,7 @@ class WebGL2WaveRender {
const maskVertices = maskVerticesMap.get(id); const maskVertices = maskVerticesMap.get(id);
if (signal.size === 1) { if (signal.size === 1) {
// 如果是 width 为 1 的 // 如果是 bit
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 / glslInputLength); gl.drawArrays(gl.TRIANGLE_STRIP, 0, lineVertices.length / glslInputLength);
@ -431,15 +478,34 @@ class WebGL2WaveRender {
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength); gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
} else { } else {
// 如果是 width 大于 1 的 // 如果是 vec根据设定的渲染模式和进行设置
gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar); const vecRenderModal = _this.getVecRenderModal(globalLookup, signal.link);
gl.bindVertexArray(signalItem.lineVao); if (vecRenderModal === 0) {
gl.drawArrays(gl.TRIANGLES, 0, lineVertices.length / glslInputLength); // 普通数字渲染模式
gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar);
gl.bindVertexArray(signalItem.maskVao); gl.bindVertexArray(signalItem.lineVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength); gl.drawArrays(gl.TRIANGLES, 0, lineVertices.length / glslInputLength);
gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
} else if (vecRenderModal === 1) {
// 梯形渲染模式
gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar);
gl.bindVertexArray(signalItem.lineVao);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, lineVertices.length / glslInputLength);
gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
} else {
// 折线渲染模式
gl.uniform2fv(webglLocation.shifts, gl_Shifts_for_bar);
gl.bindVertexArray(signalItem.lineVao);
gl.drawArrays(gl.TRIANGLES, 0, lineVertices.length / glslInputLength);
gl.bindVertexArray(signalItem.maskVao);
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / glslInputLength);
}
} }
} }
plugins.map(fn => fn(globalLookup, pstate, elements)); plugins.map(fn => fn(globalLookup, pstate, elements));

View File

@ -29,7 +29,7 @@
*/ */
import { globalLookup } from "@/hook/global"; import { globalLookup } from "@/hook/global";
import { maskColorIndexOffset, posYFactor } from "../render-utils"; import { maskColorIndexOffset, posYFactor, prettyPrint } from "../render-utils";
import { hexToSignedInt } from "./renderFormat"; import { hexToSignedInt } from "./renderFormat";
@ -130,15 +130,15 @@ function makeWidthShiftIndexByPoints(p0, p1, p2) {
return 0; return 0;
} }
/** /**
* *
* @param {string} link * @param {string} link
* @param {Array<string | number>} wave * @param {Array<string | number>} wave
* @param { number } time * @param { number } time
* @param {(link: string, wave: number[], time: number) => BitRenderTempStruct} renderParamMaker
* @returns {VecRenderNumberVertices} * @returns {VecRenderNumberVertices}
*/ */
function renderBlockWave(link, wave, time, renderParamMaker) { export function renderAsBit(link, wave, time) {
const length = wave.length; const length = wave.length;
// 先将节点数据转化为裁剪空间的坐标 // 先将节点数据转化为裁剪空间的坐标
const perspectivePoints = []; const perspectivePoints = [];
@ -153,8 +153,8 @@ function renderBlockWave(link, wave, time, renderParamMaker) {
const value1 = currentWave[1]; const value1 = currentWave[1];
const value2 = nextWave[1]; const value2 = nextWave[1];
const renderParam1 = renderParamMaker(link, currentWave, time); const renderParam1 = makeBitRenderParam(link, currentWave, time);
const renderParam2 = renderParamMaker(link, nextWave, time); const renderParam2 = makeBitRenderParam(link, nextWave, time);
if (i === 0) { if (i === 0) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color }); perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
@ -250,17 +250,6 @@ function renderBlockWave(link, wave, time, renderParamMaker) {
return { lineVertices, maskVertices }; 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
@ -462,10 +451,6 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
const { maxVal, minVal } = getMaxMinByFormat(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 lineVertices = [];
const maskVertices = [];
function makeLadderAnalogRenderParam(link, wave, time) { function makeLadderAnalogRenderParam(link, wave, time) {
const [t1, val, mask] = wave; const [t1, val, mask] = wave;
@ -478,7 +463,6 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
const numVal = explainAsJSNumber(formatCode, val, width); const numVal = explainAsJSNumber(formatCode, val, width);
// 此时的 y 是一个 -1 到 1 的浮点数,因为 VAO 我设置了 Int // 此时的 y 是一个 -1 到 1 的浮点数,因为 VAO 我设置了 Int
const y = coordinateTransform(numVal); const y = coordinateTransform(numVal);
console.log(t1, y);
const colorParam = { y, color: 5 }; const colorParam = { y, color: 5 };
if (renderOptions.has(link)) { if (renderOptions.has(link)) {
@ -490,8 +474,115 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
return colorParam; return colorParam;
} }
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 = makeLadderAnalogRenderParam(link, currentWave, time);
const renderParam2 = makeLadderAnalogRenderParam(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 = [];
return renderBlockWave(link, wave, time, makeLadderAnalogRenderParam); // 制作 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 > -1) {
// 矩形的四个点
const rectangleVertices = makeRectangleVertices(p2.x, p2.y, p3.x, -1, p2.color + maskColorIndexOffset, 4);
// 三角图元画矩形
maskVertices.push(...rectangleVertices);
}
}
return { lineVertices, maskVertices };
} }
/** /**
@ -507,9 +598,13 @@ export function renderAsLineAnalog(lookup, link, wave, time) {
const formatCode = getValFormatCode(lookup, link); const formatCode = getValFormatCode(lookup, link);
const signal = lookup.link2CurrentWires.get(link); const signal = lookup.link2CurrentWires.get(link);
const width = signal.size; const width = signal.size;
const { maxVal, minVal } = getMaxMinByFormat(link, wave, time, formatCode, width); const { maxVal, minVal } = getMaxMinByFormat(link, wave, time, formatCode, width);
const coordinateTransform = getMappingFunc(formatCode, maxVal, minVal);
const length = wave.length;
const lineVertices = [];
const maskVertices = [];
} }

View File

@ -1,21 +1,24 @@
import { globalStyle, globalLookup } from '../global'; import { globalStyle, globalLookup, globalSetting } from '../global';
const yOffsetUpdate = (pstate, nextOffsetYFn) => { const yOffsetUpdate = (pstate, nextOffsetYFn) => {
let nextOffsetY = nextOffsetYFn(); let nextOffsetY = nextOffsetYFn();
const { width, xOffset, xScale, time, sidebarWidth } = pstate; const { width, xOffset, xScale, time, sidebarWidth } = pstate;
nextOffsetY = nextOffsetY;
const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep; const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep;
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight; const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
// console.log(currentRenderHeight, canvasHeight); // console.log(currentRenderHeight, canvasHeight);
const maxOffsetX = Math.max(-20, currentRenderHeight - canvasHeight); // maximum offset const maxOffsetY = Math.max(-20,
nextOffsetY = Math.min(nextOffsetY, maxOffsetX); currentRenderHeight - canvasHeight); // maximum offset
nextOffsetY = Math.min(nextOffsetY, maxOffsetY);
const minOffsetX = -20; // minimum offset const minOffsetY = -20; // minimum offset
nextOffsetY = Math.max(nextOffsetY, minOffsetX); nextOffsetY = Math.max(nextOffsetY, minOffsetY);
if (nextOffsetY === xOffset) { // if (nextOffsetY === xOffset) {
return false; // exit without scroll // return false; // exit without scroll
} // }
pstate.oldYOffset = pstate.yOffset; pstate.oldYOffset = pstate.yOffset;
pstate.yOffset = nextOffsetY; pstate.yOffset = nextOffsetY;