完成 vec 模式下的 0 绘制
This commit is contained in:
parent
b2e4fb8a8a
commit
0a36a28a57
@ -56,6 +56,8 @@ export default {
|
||||
document.body.style.setProperty('--el-color-info', 'var(--foreground)');
|
||||
document.body.style.setProperty('--el-color-info-light-8', 'var(--vscode-focusBorder)');
|
||||
document.body.style.setProperty('--el-bg-color-overlay', 'transplant');
|
||||
// document.body.style.setProperty('--el-color-white', 'var(--background)');
|
||||
|
||||
|
||||
// signal height
|
||||
document.body.style.setProperty('--display-signal-info-height', globalSetting.displaySignalHeight + 'px');
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<!-- <VerticalCursor></VerticalCursor> -->
|
||||
<!-- <TimeScale></TimeScale> -->
|
||||
<div class="vcd-render-wrapper" >
|
||||
<div class="vcd-render-wrapper" @click="updateWireCurrentValue()">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@ -10,6 +10,7 @@
|
||||
<script>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { emitter, globalLookup, globalSetting } from '@/hook/global';
|
||||
import { updateWireCurrentValue } from '@/hook/utils';
|
||||
|
||||
import VerticalCursor from '@/components/render/cursor.vue';
|
||||
import TimeScale from '@/components/render/time-scale.vue';
|
||||
@ -22,8 +23,11 @@ export default {
|
||||
},
|
||||
setup() {
|
||||
|
||||
|
||||
|
||||
return {
|
||||
globalLookup
|
||||
globalLookup,
|
||||
updateWireCurrentValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,11 +131,15 @@ rect.wd-cursor-time {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
text.xred {
|
||||
text.common {
|
||||
fill: var(--sidebar-item-text);
|
||||
}
|
||||
|
||||
text.high-impedance {
|
||||
fill: hsl(0, 100%, 50%);
|
||||
}
|
||||
|
||||
text.zxviolet {
|
||||
text.unknown {
|
||||
fill: hsl(287, 100%, 67%);
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,11 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<br>
|
||||
<div class="setting-option" style="width: 380px;">
|
||||
<span class="option-title" style="width: 300px;">{{ t('wheel-zoom-ratio') }}</span>
|
||||
<el-slider v-model="languageSetting.wheelZoomRatio" show-stops :min="1" :max="5"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
@ -145,4 +150,8 @@ export default {
|
||||
min-width: 80px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.el-slider__button {
|
||||
background-color: var(--background) !important;
|
||||
}
|
||||
</style>
|
@ -8,9 +8,35 @@
|
||||
<span :class="makeSignalIconClass(signal)"></span>
|
||||
</div>
|
||||
<div class="signal-info">
|
||||
<div class="signal-info">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="makeFullSignalNameDeps(signal)"
|
||||
placement="top"
|
||||
raw-content
|
||||
>
|
||||
<div class="signal-info-name">
|
||||
{{ signal.name }}
|
||||
</div></el-tooltip>
|
||||
|
||||
<div class="signal-info-width">
|
||||
<div :class="signal.size > 1 ? 'signal-info-caption' : ''">
|
||||
{{ makeSignalCaption(signal) }}
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signal-info-current-value-wrapper">
|
||||
<span></span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="globalLookup.currentSignalValues[signal.link] + ''"
|
||||
placement="top"
|
||||
raw-content
|
||||
><div class="signal-info-current-value">
|
||||
{{ globalLookup.currentSignalValues[signal.link] }}
|
||||
</div></el-tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -41,10 +67,41 @@ export default {
|
||||
return 'iconfont ' + makeIconClass(signal);
|
||||
}
|
||||
|
||||
function makeSignalCaption(signal) {
|
||||
return signal.size === 1 ? '' : `${signal.size - 1}:0`;
|
||||
}
|
||||
|
||||
function makeFullSignalNameDeps(signal) {
|
||||
const deps = [];
|
||||
while (signal) {
|
||||
if (signal.name && signal.type) {
|
||||
deps.push(signal);
|
||||
}
|
||||
signal = signal.parent;
|
||||
}
|
||||
let htmlString = '';
|
||||
for (let i = deps.length - 1; i >= 0; -- i) {
|
||||
const mod = deps[i];
|
||||
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
|
||||
const iconClass = makeIconClass(mod);
|
||||
const iconText = `<span class="iconfont ${iconClass}"></span> ${mod.name}`;
|
||||
|
||||
htmlString += iconText;
|
||||
|
||||
if (i > 0) {
|
||||
htmlString += '<div class="dep-arrow"></div>';
|
||||
}
|
||||
}
|
||||
htmlString = '<div class="signal-info-tooltip-wrapper">' + htmlString + '</div>';
|
||||
return htmlString;
|
||||
}
|
||||
|
||||
return {
|
||||
t,
|
||||
globalLookup,
|
||||
makeSignalIconClass,
|
||||
makeSignalCaption,
|
||||
makeFullSignalNameDeps,
|
||||
addSignal
|
||||
}
|
||||
}
|
||||
@ -112,11 +169,54 @@ export default {
|
||||
}
|
||||
|
||||
.signal-info {
|
||||
width: 90%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signal-info-name {
|
||||
cursor: pointer;
|
||||
max-width: 100px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.signal-info-tooltip-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.signal-info-width {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.signal-info-caption {
|
||||
width: fit-content;
|
||||
color: var(--sidebar-item-text);
|
||||
border-radius: .5em;
|
||||
background-color: #7CA532;
|
||||
padding: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.signal-info-current-value-wrapper {
|
||||
margin-left: 10px;
|
||||
width: 60px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.signal-info-current-value {
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
.signal-info::selection {
|
||||
background: none;
|
||||
cursor: none;
|
||||
|
@ -1,48 +0,0 @@
|
||||
|
||||
class LineDrawer {
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
this.lastColor = null;
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
this.ctx.beginPath();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
*/
|
||||
moveTo(x, y) {
|
||||
this.ctx.moveTo(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {string} color
|
||||
*/
|
||||
lineTo(x, y, color) {
|
||||
const ctx = this.ctx;
|
||||
ctx.lineTo(x, y);
|
||||
|
||||
if (this.lastColor === null) {
|
||||
ctx.strokeStyle = color;
|
||||
this.lastColor = color;
|
||||
} else if (this.lastColor !== color) {
|
||||
ctx.stroke();
|
||||
ctx.strokeStyle = color;
|
||||
this.lastColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
LineDrawer
|
||||
}
|
@ -12,6 +12,11 @@ const globalLookup = reactive({
|
||||
// 当前选中的某个 信号 的 数据。可复选。
|
||||
currentWires: new Set(),
|
||||
|
||||
currentSignalValues: {},
|
||||
|
||||
// 当前 ns 数(或者 ps)
|
||||
currentTime: 0,
|
||||
|
||||
// 模拟器的版本,比如如果使用 iverilog 那么就是 Icarus Verilog
|
||||
version: '',
|
||||
// 创建时间
|
||||
@ -65,7 +70,7 @@ const globalSetting = reactive({
|
||||
searchMode: 'so', // so, mo, sm
|
||||
searchScope: ['wire', 'reg', 'integer'],
|
||||
displaySignalInfoScope: ['width', 'parent'],
|
||||
|
||||
wheelZoomRatio: 1,
|
||||
minGridWidth: 300
|
||||
});
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { globalLookup, globalSetting, signalValues } from "./global";
|
||||
import { LineDrawer } from './canvas';
|
||||
import { domContainer, pluginRenderTimeGrid, pluginRenderValues, mountTree,
|
||||
genOnWheel, genKeyHandler, keyBindo } from './wave-view';
|
||||
|
||||
import { createCodeMirrorState, mountCodeMirror6 } from 'waveql';
|
||||
import { findCurrentSignalValue } from './utils';
|
||||
|
||||
let mainRenderEl = null;
|
||||
const canvasMap = new Map();
|
||||
@ -22,227 +20,6 @@ function getMainRenderEl() {
|
||||
return mainRenderEl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{
|
||||
* time: BigInt,
|
||||
* cmd: number,
|
||||
* value?: BigInt,
|
||||
* mask?: BigInt
|
||||
* }} dataItem
|
||||
* @returns
|
||||
*/
|
||||
function isBlocked(dataItem) {
|
||||
const mask = dataItem.mask;
|
||||
if (mask && mask === 1n) {
|
||||
return true;
|
||||
}
|
||||
if (mask && mask === 0n) {
|
||||
return false;
|
||||
}
|
||||
if (mask === undefined && dataItem.cmd > 15) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* kind: 'var' | 'scope',
|
||||
* type: 'wire' | 'reg' | 'module',
|
||||
* name: string,
|
||||
* size: number,
|
||||
* link: string
|
||||
* }} signal
|
||||
*/
|
||||
function makeWaveCanvas(signal) {
|
||||
const mainRender = getMainRenderEl();
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.id = 'wave-' + signal.link;
|
||||
canvas.className = 'render-canvas';
|
||||
canvas.width = document.body.clientWidth;
|
||||
canvas.height = globalSetting.displaySignalHeight;
|
||||
|
||||
mainRender.appendChild(canvas);
|
||||
canvasMap.set(signal, canvas);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const datas = signalValues[signal.link];
|
||||
|
||||
if (signal.size === 1) {
|
||||
drawSimpleSignal(ctx, datas);
|
||||
} else {
|
||||
// adwa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* kind: 'var' | 'scope',
|
||||
* type: 'wire' | 'reg' | 'module',
|
||||
* name: string,
|
||||
* size: number,
|
||||
* link: string
|
||||
* }} signal
|
||||
*/
|
||||
function makeWaveSvg(signal) {
|
||||
const mainRender = getMainRenderEl();
|
||||
const height = globalSetting.displaySignalHeight;
|
||||
const width = document.body.clientWidth;
|
||||
const draw = SVG().addTo(mainRender).size(width, height);
|
||||
const datas = signalValues[signal.link];
|
||||
|
||||
if (signal.size === 1) {
|
||||
drawSimpleSignalSvg(draw, datas);
|
||||
} else {
|
||||
// adwa
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Svg} ctx
|
||||
* @param {Array<{
|
||||
* time: BigInt,
|
||||
* cmd: number,
|
||||
* value?: BigInt,
|
||||
* mask?: BigInt
|
||||
* }>} signals
|
||||
*/
|
||||
function drawSimpleSignalSvg(ctx, signals) {
|
||||
let lastData = null;
|
||||
let lastX = 300;
|
||||
let lastColor = '';
|
||||
|
||||
// ctx.lineWidth = 3;
|
||||
// ctx.lineJoin = 'round';
|
||||
|
||||
// const drawer = new LineDrawer(ctx);
|
||||
// drawer.beginPath();
|
||||
for (const data of signals) {
|
||||
if (lastData === null) {
|
||||
lastData = data;
|
||||
lastX = 300;
|
||||
lastColor = getDataRenderColor(data);
|
||||
let lastY = Math.max((15 - data.cmd) * 30, 0) + 10;
|
||||
// drawer.moveTo(last_x, last_y);
|
||||
ctx.move(lastX, lastY);
|
||||
|
||||
} else {
|
||||
// 高阻态
|
||||
const start = lastData.time;
|
||||
const end = data.time;
|
||||
console.log(lastData, isBlocked(lastData));
|
||||
let lastY = Math.max((15 - lastData.cmd) * 30, 0) + 10;
|
||||
|
||||
const duration = parseInt(end - start);
|
||||
const currentX = lastX + 100;
|
||||
const currentColor = getDataRenderColor(data);
|
||||
console.log(currentX, lastY, duration);
|
||||
|
||||
const line = ctx.line(lastX, lastY, currentX, lastY);
|
||||
|
||||
if (currentColor !== lastColor) {
|
||||
ctx.stroke({ color: lastColor, linecap: 'round', width: 3 });
|
||||
}
|
||||
// drawer.lineTo(current_x, last_y, color);
|
||||
|
||||
if (lastData.cmd !== data.cmd) {
|
||||
let currentY = Math.max((15 - data.cmd) * 30, 0) + 10;
|
||||
const color = getDataRenderColor(data);
|
||||
ctx.line(currentX, lastY, currentX, currentY);
|
||||
// drawer.lineTo(current_x, current_y, color);
|
||||
}
|
||||
|
||||
lastData = data;
|
||||
lastX = currentX;
|
||||
lastColor = currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 last_data 的时间不足最长时间,就用最后一个时刻的去补足
|
||||
ctx.stroke({ color: lastColor, linecap: 'round', width: 3 });
|
||||
}
|
||||
|
||||
|
||||
function getDataRenderColor(data) {
|
||||
return isBlocked(data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* kind: 'var' | 'scope',
|
||||
* type: 'wire' | 'reg' | 'module',
|
||||
* name: string,
|
||||
* size: number,
|
||||
* link: string
|
||||
* }} signal
|
||||
*/
|
||||
function removeWaveCanvas(signal) {
|
||||
const canvas = canvasMap.get(signal);
|
||||
if (canvas instanceof HTMLCanvasElement) {
|
||||
canvas.parentNode.removeChild(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {Array<{
|
||||
* time: BigInt,
|
||||
* cmd: number,
|
||||
* value?: BigInt,
|
||||
* mask?: BigInt
|
||||
* }>} signals
|
||||
*/
|
||||
function drawSimpleSignal(ctx, signals) {
|
||||
let last_data = null;
|
||||
let last_x = 300;
|
||||
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineJoin = 'round';
|
||||
const drawer = new LineDrawer(ctx);
|
||||
|
||||
drawer.beginPath();
|
||||
for (const data of signals) {
|
||||
if (last_data === null) {
|
||||
last_data = data;
|
||||
last_x = 300;
|
||||
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
|
||||
drawer.moveTo(last_x, last_y);
|
||||
} else {
|
||||
// 高阻态
|
||||
const start = last_data.time;
|
||||
const end = data.time;
|
||||
console.log(last_data, isBlocked(last_data));
|
||||
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
|
||||
|
||||
const duration = parseInt(end - start);
|
||||
const current_x = last_x + 100;
|
||||
|
||||
console.log(current_x, last_y, duration);
|
||||
|
||||
const color = getDataRenderColor(last_data);
|
||||
drawer.lineTo(current_x, last_y, color);
|
||||
|
||||
if (last_data.cmd !== data.cmd) {
|
||||
let current_y = Math.max((15 - data.cmd) * 30, 0) + 10;
|
||||
const color = getDataRenderColor(data);
|
||||
drawer.lineTo(current_x, current_y, color);
|
||||
}
|
||||
|
||||
last_data = data;
|
||||
last_x = current_x;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 last_data 的时间不足最长时间,就用最后一个时刻的去补足
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function pluginLocalStore(desc, pstate /* , els */) {
|
||||
const pstateJsonString = JSON.stringify({
|
||||
@ -278,22 +55,7 @@ function makeWaveView(parentElement) {
|
||||
console.log('updater');
|
||||
};
|
||||
|
||||
const cmState = createCodeMirrorState(globalLookup, container.pstate);
|
||||
|
||||
const cm = mountCodeMirror6(
|
||||
cmState,
|
||||
container.elo.waveqlPanel,
|
||||
globalLookup,
|
||||
container.pstate
|
||||
);
|
||||
|
||||
cm.view.dispatch({changes: {from: 0, insert: ' '}});
|
||||
cm.view.dispatch({changes: {from: 0, to: 1, insert: ''}});
|
||||
|
||||
container.elo.container.addEventListener('keydown', genKeyHandler.genKeyHandler(parentElement, container.pstate, globalLookup, cm, keyBindo));
|
||||
container.elo.container.addEventListener('wheel', genOnWheel(parentElement, container.pstate, globalLookup, cm, keyBindo));
|
||||
// console.log(cm);
|
||||
// cm.view.focus();
|
||||
container.elo.container.addEventListener('wheel', genOnWheel(parentElement, container.pstate, globalLookup, keyBindo));
|
||||
}
|
||||
|
||||
|
||||
@ -309,18 +71,20 @@ function makeWaveView(parentElement) {
|
||||
function toggleRender(signal) {
|
||||
if (globalLookup.currentWires.has(signal)) {
|
||||
globalLookup.currentWires.delete(signal);
|
||||
delete globalLookup.currentSignalValues[signal.link];
|
||||
globalLookup.render();
|
||||
} else {
|
||||
globalLookup.currentWires.add(signal);
|
||||
const signalItem = globalLookup.chango[signal.link];
|
||||
const { wave } = signalItem;
|
||||
const time = globalLookup.currentTime;
|
||||
globalLookup.currentSignalValues[signal.link] = findCurrentSignalValue(wave, time);
|
||||
globalLookup.render();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getMainRenderEl,
|
||||
makeWaveCanvas,
|
||||
makeWaveSvg,
|
||||
removeWaveCanvas,
|
||||
makeWaveView,
|
||||
toggleRender
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import { Stream } from "stream";
|
||||
import { globalLookup } from "./global";
|
||||
|
||||
/**
|
||||
*
|
||||
@ -110,34 +111,100 @@ function makeSearchResultItem(searchString, module, searchScope, caseSensitivity
|
||||
}
|
||||
|
||||
let htmlString = '';
|
||||
let depString = '';
|
||||
let currentCounts = 0;
|
||||
for (let i = deps.length - 1; i >= 0; -- i) {
|
||||
const mod = deps[i];
|
||||
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
|
||||
const iconClass = makeIconClass(mod);
|
||||
const iconText = `<span class="iconfont ${iconClass}"></span> ${mod.name}`;
|
||||
|
||||
depString += iconText;
|
||||
htmlString += iconText;
|
||||
|
||||
if (i > 0) {
|
||||
depString += '<br>';
|
||||
htmlString += '<div class="dep-arrow"></div>';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
htmlString,
|
||||
module,
|
||||
depString
|
||||
module
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BigInt} value
|
||||
* @param {BigInt} mask
|
||||
* @returns {number | string}
|
||||
*/
|
||||
function getWireValueCaption(value, mask) {
|
||||
if (!mask) {
|
||||
return value;
|
||||
}
|
||||
// mask 不为空,代表存在问题
|
||||
if (value) {
|
||||
return '?';
|
||||
} else {
|
||||
return 'x';
|
||||
}
|
||||
}
|
||||
|
||||
async function updateWireCurrentValue() {
|
||||
const chango = globalLookup.chango;
|
||||
const time = globalLookup.currentTime;
|
||||
const currentSignalValues = globalLookup.currentSignalValues;
|
||||
|
||||
for (const signal of globalLookup.currentWires) {
|
||||
const signalItem = chango[signal.link];
|
||||
const { wave } = signalItem;
|
||||
if (wave === undefined || wave.length === 0) {
|
||||
currentSignalValues[signal.link] = 'x';
|
||||
} else if (wave.length === 1) {
|
||||
currentSignalValues[signal.link] = getWireValueCaption(wave[0][1], wave[0][2]);
|
||||
} else {
|
||||
currentSignalValues[signal.link] = findCurrentSignalValue(wave, time);
|
||||
}
|
||||
|
||||
// console.log(signal.name, currentSignalValues[signal.link]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array<Array<string | number>>} wave
|
||||
* @param {number} time
|
||||
*/
|
||||
function findCurrentSignalValue(wave, time) {
|
||||
const times = wave.map(p => p[0]);
|
||||
|
||||
// 二分查找,并将结果存入 i
|
||||
let i = 0, j = wave.length - 1;
|
||||
while (i < j) {
|
||||
if (times[i] === time) {
|
||||
break;
|
||||
}
|
||||
if (times[j] === time) {
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
if (j - i === 1) {
|
||||
break;
|
||||
}
|
||||
const mid = (i + j) >> 1;
|
||||
if (times[mid] > time) {
|
||||
j = mid;
|
||||
} else {
|
||||
i = mid;
|
||||
}
|
||||
}
|
||||
|
||||
const value = getWireValueCaption(wave[i][1], wave[i][2]);
|
||||
console.log(wave[i][1], wave[i][2]);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} timescale
|
||||
@ -263,5 +330,7 @@ export {
|
||||
parseTimescale,
|
||||
numberOrString,
|
||||
gcd,
|
||||
normaliseUsingGCD
|
||||
findCurrentSignalValue,
|
||||
normaliseUsingGCD,
|
||||
updateWireCurrentValue
|
||||
};
|
@ -14,7 +14,9 @@ const setTime = (pstate, str) => {
|
||||
};
|
||||
|
||||
const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
|
||||
// 这是上面展示当前 time 的圆角矩形框框的宽度
|
||||
const xmargin = 160;
|
||||
// 这是上面展示当前 time 的圆角矩形内的字体大小
|
||||
const fontHeight = 20;
|
||||
const fontWidth = fontHeight / 2;
|
||||
const handler = event => {
|
||||
@ -22,6 +24,7 @@ const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
|
||||
cursor.style.left = (x - xmargin) + 'px';
|
||||
cursor.innerHTML = renderCursor({ xmargin, fontWidth, fontHeight }, pstate);
|
||||
};
|
||||
// 添加初始鼠标位置
|
||||
handler({ clientX: pstate.width / 2 });
|
||||
content.addEventListener('mousemove', handler);
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const genOnWheel = (element, pstate, deso, cm, keyBindo, plugins) =>
|
||||
const genOnWheel = (element, pstate, deso, keyBindo, plugins) =>
|
||||
event => {
|
||||
const {deltaY} = event;
|
||||
if (event.ctrlKey) {
|
||||
|
@ -6,7 +6,7 @@ const cColors = new Float32Array([
|
||||
|
||||
0.2, 0.847, 0.1, 1, // 2: strong 0
|
||||
0.2, 0.847, 0.1, 1, // 3: strong 1
|
||||
1, 0, 0, 1, // 4: (x X) strong unknown
|
||||
0.9, 0.2, 0.2, 1, // 4: (x X) strong unknown
|
||||
|
||||
.5, 1, 1, 1, // 5: vec
|
||||
1, 1, 0, 1, // 6: yellow
|
||||
@ -289,27 +289,25 @@ class WebGL2WaveRender {
|
||||
const vertices = [];
|
||||
const ilen = wave.length;
|
||||
for (let i = 0; i < ilen; i++) {
|
||||
const [tim, val, msk] = wave[i];
|
||||
const tt = (i === (ilen - 1)) ? time : wave[i + 1][0];
|
||||
const [t1, val, msk] = wave[i];
|
||||
const t2 = (i === (ilen - 1)) ? time : wave[i + 1][0];
|
||||
|
||||
if (val) {
|
||||
if (msk) {
|
||||
vertices.push(...brick[2](2, 2, tim, tt, 4, 4)); // x,z?
|
||||
vertices.push(...brick[2](2, 2, t1, t2, 4, 4)); // x,z?
|
||||
} else {
|
||||
vertices.push(
|
||||
tim, 0, 0,
|
||||
tt, 0, 0, tt, 0, 5, tt, 4, 5,
|
||||
tim, 6, 5, tim, 0, 5, tim, 3, 5,
|
||||
tt, 1, 5, tt, 0, 5
|
||||
t1, 0, 0,
|
||||
t2, 0, 0,
|
||||
t2, 0, 5,
|
||||
t2, 4, 5,
|
||||
t1, 6, 5,
|
||||
t1, 0, 5,
|
||||
t1, 3, 5,
|
||||
t2, 1, 5,
|
||||
t2, 0, 5
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (msk) {
|
||||
vertices.push(...brick[2](2, 2, tim, tt, 4, 4)); // x
|
||||
} else {
|
||||
vertices.push(tim, 6, 2, tt, 4, 2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Uint32Array(vertices);
|
||||
|
@ -8,6 +8,8 @@ const genResizeHandler = pstate =>
|
||||
pstate.width = width;
|
||||
pstate.height = height;
|
||||
|
||||
console.log('time', time);
|
||||
|
||||
// Y
|
||||
const yOffsetMax = (numLanes + 2) * 2 * yStep;
|
||||
if (yOffsetMax < 0) {
|
||||
@ -16,11 +18,13 @@ const genResizeHandler = pstate =>
|
||||
yOffset = yOffsetMax;
|
||||
}
|
||||
pstate.yOffset = yOffset;
|
||||
console.log('resize handler', pstate);
|
||||
// console.log('resize handler', pstate);
|
||||
// X
|
||||
const xScaleMin = pstate.xScaleMin = (width - sidebarWidth) / time;
|
||||
// const xScaleMin = pstate.xScaleMin = (width - sidebarWidth) / time;
|
||||
const xScaleMin = pstate.xScaleMin = 0.001;
|
||||
console.log(width, sidebarWidth);
|
||||
console.log('xScaleMin', xScaleMin);
|
||||
pstate.xScale = (xScale < xScaleMin) ? xScaleMin : xScale;
|
||||
pstate.xScaleMin = 0.001;
|
||||
xOffsetUpdate(pstate, xOffset);
|
||||
};
|
||||
|
||||
|
@ -10,23 +10,25 @@ const getLabel = (lane) => {
|
||||
const width = Number(lane.width || 1);
|
||||
const formatter = format(fmt, width);
|
||||
|
||||
return (vPre, mPre, x, w) => {
|
||||
return (value, mask, x, w) => {
|
||||
console.log(value, mask, x, w);
|
||||
|
||||
if (mPre) {
|
||||
if (vPre) {
|
||||
return ['text', {x, class: 'zxviolet'}, '?'];
|
||||
if (mask) {
|
||||
if (value) {
|
||||
return ['text', { x, class: 'unknown' }, '?'];
|
||||
} else {
|
||||
return ['text', {x, class: 'xred'}, 'x'];
|
||||
return ['text', { x, class: 'high-impedance' }, 'x'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const pos = (w / 8) | 0;
|
||||
|
||||
vPre = BigInt(vPre);
|
||||
value = BigInt(value);
|
||||
|
||||
const txtShort = formatter(vPre, pos, width);
|
||||
const txtShort = formatter(value, pos, width);
|
||||
|
||||
return ['text', {x}, txtShort];
|
||||
return ['text', { x, class: 'common' }, txtShort];
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -11,12 +11,12 @@ const yScroll = delta => (pstate, cm) => {
|
||||
|
||||
const pluso = {
|
||||
desc: 'Zoom in time',
|
||||
fn: pstate => xScaleUpdate(pstate, 3 / 2 * pstate.xScale)
|
||||
fn: pstate => xScaleUpdate(pstate, 10 / 9 * pstate.xScale)
|
||||
};
|
||||
|
||||
const minuso = {
|
||||
desc: 'Zoom out time',
|
||||
fn: pstate => xScaleUpdate(pstate, 2 / 3 * pstate.xScale)
|
||||
fn: pstate => xScaleUpdate(pstate, 9 / 10 * pstate.xScale)
|
||||
};
|
||||
|
||||
const fullo = {
|
||||
@ -27,11 +27,11 @@ const fullo = {
|
||||
const scroll = {
|
||||
left: {
|
||||
desc: 'Scroll into the past',
|
||||
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset + .2 * pstate.width)
|
||||
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset + pstate.time)
|
||||
},
|
||||
right: {
|
||||
desc: 'Scroll into the future',
|
||||
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset - .2 * pstate.width)
|
||||
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset - pstate.time)
|
||||
},
|
||||
up: {
|
||||
desc: 'scroll up',
|
||||
|
@ -4,11 +4,14 @@ const genSVG = require('onml/gen-svg.js');
|
||||
const stringify = require('onml/stringify.js');
|
||||
const formatTime = require('./format-time.js');
|
||||
|
||||
const { globalLookup } = require('../global');
|
||||
|
||||
const renderCursor = (cfg, pstate) => {
|
||||
const { xmargin, fontWidth, fontHeight } = cfg;
|
||||
const { height, xScale, xOffset, tgcd, timescale, xCursor } = pstate;
|
||||
|
||||
const xx = Math.round((xCursor - xOffset) / xScale) * tgcd;
|
||||
globalLookup.currentTime = xx;
|
||||
const label = formatTime(xx, timescale);
|
||||
const lWidth = (label.length + 1) * fontWidth;
|
||||
|
||||
|
@ -88,16 +88,21 @@ function* renderValues(desc, pstate) {
|
||||
const labeler = getLabel(lane);
|
||||
for (let j = 1; j <= jlen; j++) {
|
||||
const mark = wave[j];
|
||||
|
||||
const [tCur, vCur, mCur] = (mark || [desc.time, 0, 0]);
|
||||
const xCur = getX(pstate, tCur);
|
||||
if (vPre || mPre) {
|
||||
console.log(mark, vPre, mPre, vCur, mCur);
|
||||
|
||||
if (vPre !== undefined || mPre !== undefined) {
|
||||
if (xPre > width && xCur > width) { // both time stamps to the right
|
||||
break perLane;
|
||||
}
|
||||
|
||||
if (!((xPre < sidebarWidth) && (xCur < sidebarWidth))) { // both time stamps to the left
|
||||
const xPreNorm = ((xPre > sidebarWidth) ? xPre : sidebarWidth) | 0;
|
||||
const xCurNorm = ((xCur < width) ? xCur : width) | 0;
|
||||
const w = xCurNorm - xPreNorm;
|
||||
// 宽度太小不画了
|
||||
if (w > 8) {
|
||||
const x = Math.round((xPreNorm + xCurNorm) / 2);
|
||||
mLane.push(labeler(vPre, mPre, x, w));
|
||||
@ -106,7 +111,9 @@ function* renderValues(desc, pstate) {
|
||||
}
|
||||
xPre = xCur;
|
||||
vPre = vCur;
|
||||
console.log(mPre, mCur);
|
||||
mPre = mCur;
|
||||
console.log(mPre, mCur);
|
||||
}
|
||||
}
|
||||
ml.push(mLane);
|
||||
|
@ -1,202 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('lodash.get');
|
||||
|
||||
const diz = (cols, wires, path, rowIdx) => {
|
||||
if (cols[0] !== 'DIZ') {
|
||||
return;
|
||||
}
|
||||
|
||||
const levelo = get(wires, path, false);
|
||||
const arg1 = cols[1]; // pipeline stages
|
||||
|
||||
// find all potential candidates
|
||||
const othero0 = Object.keys(levelo).reduce((res, name) => {
|
||||
const m = name.match(arg1);
|
||||
if (m) {
|
||||
const {id, pc, go} = m.groups;
|
||||
const stage = res[id] = (res[id] || {});
|
||||
const desc = {name, ref: levelo[name]};
|
||||
if (pc) {
|
||||
stage[pc] = desc;
|
||||
} else
|
||||
if (go) {
|
||||
stage[go] = desc;
|
||||
} else {
|
||||
console.log(desc);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
|
||||
// filter out singles
|
||||
const othero = Object.keys(othero0).reduce((res, name) => {
|
||||
const val = othero0[name];
|
||||
if (val.pc && val.go) {
|
||||
res[name] = val;
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
|
||||
return {kind: 'DIZ', idx: rowIdx, othero, clock: {name: 'clock', ref: levelo.clock}};
|
||||
};
|
||||
|
||||
exports.parser = wires => str => {
|
||||
const arr = str.split('\n');
|
||||
const path = [];
|
||||
const labelo = {};
|
||||
let nRow;
|
||||
let mat;
|
||||
|
||||
const res = arr.map((row, rowIdx) => {
|
||||
row = row.trim();
|
||||
|
||||
// Section with Parentheses
|
||||
|
||||
if (row[0] === '(') {
|
||||
const cols = row.slice(1).split(/\s+/);
|
||||
const res = diz(cols, wires, path, rowIdx); // || other commands
|
||||
if (res) {
|
||||
nRow = res;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (row[0] === ')') {
|
||||
if (nRow !== undefined) {
|
||||
nRow.len = rowIdx - nRow.idx + 1;
|
||||
nRow = undefined;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const rowo = {};
|
||||
const cols = row.split(/\s+/);
|
||||
cols.map(col => {
|
||||
if (col === '...') { path.pop(); path.pop(); return; }
|
||||
if (col === '..') { path.pop(); return; }
|
||||
if (col === '.') { return; }
|
||||
if (col === '/') { path.length = 0; return; }
|
||||
|
||||
mat = col.match(/^:(\S+)$/); if (mat) {
|
||||
labelo[mat[1]] = rowo;
|
||||
return;
|
||||
}
|
||||
|
||||
mat = col.match(/^(\{)([^}]+)(\})$/); if (mat) {
|
||||
const a = mat[2];
|
||||
const b = a.split(',');
|
||||
rowo.kind = 'brace';
|
||||
rowo.body = b.reduce((res, e) => {
|
||||
const ee = e.split(':');
|
||||
if (ee.length === 2) {
|
||||
const key = ee[0];
|
||||
const val = labelo[ee[1]] || Number(ee[1]);
|
||||
res[key] = val;
|
||||
} else
|
||||
if (ee.length === 1) {
|
||||
res[ee[0]] = labelo[ee[0]] || {};
|
||||
} else {
|
||||
console.error(ee);
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
return;
|
||||
}
|
||||
|
||||
mat = col.match(/^%([<>^])?([+-])?([su])?([bodhHac])$/); if (mat) {
|
||||
rowo.format = {
|
||||
align: mat[1],
|
||||
plus: mat[2],
|
||||
sign: mat[3],
|
||||
radix: mat[4]
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const newPath = path.concat(col);
|
||||
const ref = get(wires, newPath, false);
|
||||
|
||||
if (typeof ref === 'string') {
|
||||
rowo.name = col;
|
||||
rowo.ref = ref;
|
||||
} else
|
||||
if (typeof ref === 'object') {
|
||||
path.push(col);
|
||||
}
|
||||
});
|
||||
return rowo;
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
exports.cmMode = function(CodeMirror, desc) {
|
||||
const { wires } = desc;
|
||||
CodeMirror.defineMode('waveql', function() {
|
||||
return {
|
||||
startState: function() {
|
||||
return {path: []};
|
||||
},
|
||||
token: function (stream, stt) {
|
||||
// const line = stream.lineOracle.line;
|
||||
let mat;
|
||||
|
||||
if (stream.eatSpace()) { return null; }
|
||||
|
||||
mat = stream.match(/^\.\.\.(\s+|$)/); if (mat) { stt.path.pop(); stt.path.pop(); return 'punct'; }
|
||||
mat = stream.match(/^\.\.(\s+|$)/); if (mat) { stt.path.pop(); return 'punct'; }
|
||||
mat = stream.match(/^\.(\s+|$)/); if (mat) { return 'punct'; }
|
||||
mat = stream.match(/^\/(\s+|$)/); if (mat) { stt.path.length = 0; return 'punct'; }
|
||||
|
||||
mat = stream.match(/^:(\S+)(\s+|$)/); if (mat) {
|
||||
return 'label';
|
||||
}
|
||||
|
||||
mat = stream.match(/^\{[^}]+\}(\s+|$)/); if (mat) {
|
||||
return 'mark';
|
||||
}
|
||||
|
||||
mat = stream.match(/^%([<>^])?([+-])?([su])?([bodhHac])(\s+|$)/); if (mat) {
|
||||
return 'format';
|
||||
}
|
||||
|
||||
mat = stream.match(/^(\S+)(\s+|$)/); if (mat) {
|
||||
const sigName = mat[1];
|
||||
const newPath = stt.path.concat(sigName);
|
||||
const ref = get(wires, newPath, false);
|
||||
if (typeof ref === 'string') {
|
||||
return 'signal';
|
||||
}
|
||||
if (typeof ref === 'object') {
|
||||
stt.path = newPath;
|
||||
return 'path';
|
||||
}
|
||||
return 'comment';
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
CodeMirror.defineMIME('text/x-waveql', 'waveql');
|
||||
};
|
||||
|
||||
exports.cmHint = (CodeMirror, desc) => {
|
||||
const { wires } = desc;
|
||||
return async (cm /*, options */) => {
|
||||
const cursor = cm.getCursor();
|
||||
const token = cm.getTokenAt(cursor);
|
||||
const line = cm.getLine(cursor.line);
|
||||
let start = cursor.ch;
|
||||
let end = cursor.ch;
|
||||
while (start && /\w/.test(line.charAt(start - 1))) --start;
|
||||
while (end < line.length && /\w/.test(line.charAt(end))) ++end;
|
||||
const cur = token.string.trim();
|
||||
const list = get(wires, token.state.path, {});
|
||||
const alls = ['/', '..'].concat(Object.keys(list));
|
||||
const hints = alls.filter(e => e.match(cur));
|
||||
return {
|
||||
list: hints,
|
||||
from: CodeMirror.Pos(cursor.line, start),
|
||||
to: CodeMirror.Pos(cursor.line, end)
|
||||
};
|
||||
};
|
||||
};
|
@ -1,19 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
const xOffsetUpdate = (pstate, xOffsetNext) => {
|
||||
let {width, xOffset, xScale, time, sidebarWidth} = pstate;
|
||||
const xOffsetUpdate = (pstate, nextOffsetX) => {
|
||||
const { width, xOffset, xScale, time, sidebarWidth } = pstate;
|
||||
|
||||
const xOffsetMax = sidebarWidth; // maximum offset
|
||||
xOffsetNext = (xOffsetNext > xOffsetMax) ? xOffsetMax : xOffsetNext;
|
||||
// console.log('input offset', nextOffsetX);
|
||||
// console.log('xScale', xScale);
|
||||
const maxOffsetX = width - xScale * time + 2000; // maximum offset
|
||||
nextOffsetX = Math.min(nextOffsetX, maxOffsetX);
|
||||
|
||||
const xOffsetMin = width - xScale * time; // minimum offset
|
||||
xOffsetNext = (xOffsetNext < xOffsetMin) ? xOffsetMin : xOffsetNext;
|
||||
const minOffsetX = 0; // minimum offset
|
||||
nextOffsetX = Math.max(nextOffsetX, minOffsetX);
|
||||
|
||||
if (xOffsetNext === xOffset) {
|
||||
// console.log('max offset', maxOffsetX);
|
||||
// console.log('min offset', minOffsetX);
|
||||
// console.log('next offset', nextOffsetX);
|
||||
|
||||
if (nextOffsetX === xOffset) {
|
||||
return false; // exit without scroll
|
||||
}
|
||||
|
||||
pstate.xOffset = xOffsetNext;
|
||||
pstate.xOffset = nextOffsetX;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -9,8 +9,7 @@ const xScaleUpdate = (pstate, xScaleNext) => {
|
||||
|
||||
xScaleNext = (xScaleNext < xScaleMin) ? xScaleMin : xScaleNext;
|
||||
|
||||
console.log('pstate', pstate);
|
||||
console.log('scale next', xScaleNext);
|
||||
// console.log('scale next', xScaleNext);
|
||||
|
||||
if (xScaleNext === xScale) {
|
||||
return false; // exit without scale change
|
||||
|
@ -3,6 +3,7 @@
|
||||
"signal": "Signals",
|
||||
"search-signal": "Search Signal",
|
||||
"language-setting": "Language",
|
||||
"wheel-zoom-ratio": "Wheel Zoom Level",
|
||||
|
||||
"search-setting": "Search",
|
||||
"search-case-sensitivity": "Case Sensitivity",
|
||||
@ -11,6 +12,7 @@
|
||||
"search-display-parent-only": "Display Parent Module Only",
|
||||
"search-nothing": "Find Nothing",
|
||||
|
||||
|
||||
"signal-only": "Signal Only",
|
||||
"module-only": "Module Only",
|
||||
"signal-module": "Signal + Module",
|
||||
|
@ -3,6 +3,7 @@
|
||||
"signal": "信号",
|
||||
"search-signal": "搜索信号",
|
||||
"language-setting": "语言",
|
||||
"wheel-zoom-ratio": "滚轮缩放倍率",
|
||||
|
||||
"search-setting": "搜索",
|
||||
"search-case-sensitivity": "区分大小写",
|
||||
|
Loading…
x
Reference in New Issue
Block a user