完成搜索
This commit is contained in:
parent
948370b644
commit
9f50214cbb
@ -6,7 +6,7 @@
|
||||
--main-light-color: var(--main-color);
|
||||
--sidebar-width: 330px;
|
||||
--right-nav-width: 60px;
|
||||
--time-scale-height: 50px;
|
||||
--time-scale-height: 30px;
|
||||
--sidebar-padding: 10px;
|
||||
--sidebar-item-margin: 5px;
|
||||
--toolbar-height: 50px;
|
||||
|
@ -71,7 +71,7 @@ onMounted(async () => {
|
||||
// document.body.style.setProperty('--el-color-white', 'var(--background)');
|
||||
|
||||
// 设置全局宏
|
||||
document.body.style.setProperty('--time-scale-height', '50px');
|
||||
document.body.style.setProperty('--time-scale-height', '30px');
|
||||
document.body.style.setProperty('--vcd-render-padding', '30px');
|
||||
document.body.style.setProperty('--sidebar-width', '330px');
|
||||
document.body.style.setProperty('--toolbar-height', '60px');
|
||||
|
47
src/components/render/cursor.js
Normal file
47
src/components/render/cursor.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { globalLookup } from '@/hook/global';
|
||||
import formatTime from '@/hook/wave-view/format-time';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
export const StaticCursor = reactive({
|
||||
label: '',
|
||||
show: false,
|
||||
currentTime: 0,
|
||||
left: 0,
|
||||
updateLabel(timescale) {
|
||||
this.label = formatTime(this.currentTime, timescale);
|
||||
},
|
||||
updateLeft() {
|
||||
this.left = calcCursorLeft(this.currentTime);
|
||||
}
|
||||
});
|
||||
|
||||
export function changeCursorLocation() {
|
||||
StaticCursor.show = true;
|
||||
const pstate = globalLookup.pstate;
|
||||
if (pstate) {
|
||||
const { xCursor, xOffset, xScale, tgcd, timescale } = pstate;
|
||||
const currentTime = Math.round((xCursor - xOffset) / xScale) * tgcd;
|
||||
StaticCursor.currentTime = currentTime;
|
||||
StaticCursor.updateLabel(timescale);
|
||||
StaticCursor.updateLeft();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 给出当前的时间,比如 23ns,计算当前这个点应该相对于左侧偏移多少
|
||||
* @param {number} currentTime
|
||||
* @returns {number}
|
||||
*/
|
||||
export function calcCursorLeft(currentTime) {
|
||||
const pstate = globalLookup.pstate;
|
||||
if (pstate) {
|
||||
const { xOffset, xScale, tgcd, timescale } = pstate;
|
||||
const xCursor = (currentTime / tgcd) * xScale + xOffset;
|
||||
return xCursor;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const MovingCursor = reactive({
|
||||
|
||||
});
|
@ -1,94 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-show="StaticCursor.show">
|
||||
<div class="vertical-cursor-wrapper"
|
||||
@click="makeCursor"
|
||||
:style="`left: ${currentX}px`">
|
||||
:style="cursorStyle"
|
||||
>
|
||||
<div class="current-display-cursor-up">
|
||||
<div class="current-time-value">{{ currentX }} px</div>
|
||||
<div class="cursor-down-arrow"></div>
|
||||
<div class="current-time-value" :style="colorStyle">{{ StaticCursor.label }}</div>
|
||||
<div class="cursor-down-arrow" :style="borderStyle"></div>
|
||||
</div>
|
||||
<div class="vertical-line"></div>
|
||||
<div class="vertical-line" :style="colorStyle"></div>
|
||||
<div class="current-display-cursor-down">
|
||||
<div class="current-time-value">{{ currentX }} px</div>
|
||||
<div class="cursor-up-arrow"></div>
|
||||
<div class="current-time-value" :style="colorStyle">{{ StaticCursor.label }}</div>
|
||||
<div class="cursor-up-arrow" :style="borderStyle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="showFixedOne"
|
||||
class="vertical-cursor-wrapper"
|
||||
@click="makeCursor"
|
||||
:style="`left: ${fixedLeft}px`">
|
||||
<div class="fix-current-display-cursor-up">
|
||||
{{ globalLookup.view.currentX }} px
|
||||
</div>
|
||||
<div class="fix-vertical-line"></div>
|
||||
<div class="fix-current-display-cursor-down">
|
||||
{{ globalLookup.view.currentX }} px
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { globalLookup } from '../../hook/global';
|
||||
<script setup>
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { StaticCursor } from './cursor';
|
||||
|
||||
export default {
|
||||
name: 'vertical-cursor',
|
||||
setup() {
|
||||
// see --sidebar-width
|
||||
const left = 280 + 20;
|
||||
defineComponent({ name: 'vertical-cursor' });
|
||||
|
||||
// see --right-nav-width
|
||||
const right = document.body.clientWidth - 60;
|
||||
|
||||
const showFixedOne = ref(false);
|
||||
const fixedLeft = ref(0);
|
||||
const currentX = ref(left);
|
||||
const boxShift = 25;
|
||||
|
||||
document.onmousemove = event => {
|
||||
const x = event.x;
|
||||
if (x < left || x > right) {
|
||||
return;
|
||||
}
|
||||
currentX.value = x - boxShift;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { PointerEvent } event
|
||||
*/
|
||||
function makeCursor(event) {
|
||||
// console.log(event.x);
|
||||
showFixedOne.value = true;
|
||||
fixedLeft.value = event.x - boxShift;
|
||||
globalLookup.view.currentX = event.x - boxShift;
|
||||
}
|
||||
|
||||
return {
|
||||
makeCursor,
|
||||
fixedLeft,
|
||||
showFixedOne,
|
||||
boxShift,
|
||||
globalLookup,
|
||||
currentX
|
||||
}
|
||||
const props = defineProps({
|
||||
color: {
|
||||
type: String,
|
||||
default: 'var(--main-color)'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const colorStyle = {
|
||||
backgroundColor: props.color
|
||||
};
|
||||
|
||||
const borderStyle = {
|
||||
borderLeft: '5px solid ' + props.color,
|
||||
borderTop: '5px solid ' + props.color
|
||||
};
|
||||
|
||||
const boxShift = 25;
|
||||
const cursorStyle = computed(() => ({
|
||||
left: StaticCursor.left - boxShift + 'px'
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
.vertical-cursor-wrapper {
|
||||
position: absolute;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
cursor: crosshair;
|
||||
z-index: 55;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; /* 沿着横轴(水平方向)对齐 */
|
||||
justify-content: center; /* 沿着纵轴(垂直方向)对齐 */
|
||||
transform: translateY(var(--toolbar-height));
|
||||
}
|
||||
|
||||
.current-display-cursor-up {
|
||||
@ -115,7 +83,6 @@ export default {
|
||||
|
||||
.current-time-value {
|
||||
border-radius: .4em;
|
||||
background-color: var(--color-deepPurple);
|
||||
color: var(--sidebar-item-text);
|
||||
padding: 5px;
|
||||
width: fit-content;
|
||||
@ -123,8 +90,6 @@ export default {
|
||||
}
|
||||
|
||||
.cursor-down-arrow {
|
||||
border-left: 5px solid var(--color-deepPurple);
|
||||
border-top: 5px solid var(--color-deepPurple);
|
||||
transform: rotate(225deg);
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
@ -134,9 +99,12 @@ export default {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.vertical-line {
|
||||
width: 2px;
|
||||
height: calc(100vh - 2 * var(--time-scale-height) - 60px);
|
||||
}
|
||||
|
||||
.cursor-up-arrow {
|
||||
border-left: 5px solid var(--color-deepPurple);
|
||||
border-top: 5px solid var(--color-deepPurple);
|
||||
transform: rotate(45deg);
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
@ -144,40 +112,4 @@ export default {
|
||||
left: 17.93px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.vertical-line {
|
||||
width: 1px;
|
||||
height: calc(100vh - 2 * var(--time-scale-height));
|
||||
}
|
||||
|
||||
.fix-vertical-line {
|
||||
width: 1px;
|
||||
height: calc(100vh - 2 * var(--time-scale-height));
|
||||
background-color: var(--color-deepPurple);
|
||||
}
|
||||
|
||||
.fix-current-display-cursor-up {
|
||||
height: var(--time-scale-height);
|
||||
width: 50px;
|
||||
background-color: var(--color-deepPurple);
|
||||
border-radius: .5em .5em .5em .5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
color: var(--sidebar-item-text);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.fix-current-display-cursor-down {
|
||||
height: var(--time-scale-height);
|
||||
width: 50px;
|
||||
background-color: var(--color-deepPurple);
|
||||
border-radius: .5em .5em .5em .5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
color: var(--sidebar-item-text);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
</style>
|
@ -1,35 +1,27 @@
|
||||
<template>
|
||||
<!-- <VerticalCursor></VerticalCursor> -->
|
||||
<!-- <TimeScale></TimeScale> -->
|
||||
<div class="vcd-render-wrapper" @click="updateWireCurrentValue()">
|
||||
|
||||
<div class="vcd-render-wrapper" @click="onRenderClick()">
|
||||
<VerticalCursor></VerticalCursor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { reactive, ref } from 'vue';
|
||||
<script setup>
|
||||
import { defineComponent, 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';
|
||||
import { changeCursorLocation } from './cursor';
|
||||
|
||||
export default {
|
||||
name: 'main-render',
|
||||
components: {
|
||||
// VerticalCursor,
|
||||
// TimeScale
|
||||
},
|
||||
setup() {
|
||||
defineComponent({ name: 'main-render' });
|
||||
|
||||
|
||||
|
||||
return {
|
||||
globalLookup,
|
||||
updateWireCurrentValue
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @description 渲染区域被鼠标点击时
|
||||
*/
|
||||
function onRenderClick() {
|
||||
updateWireCurrentValue();
|
||||
changeCursorLocation();
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -89,6 +81,7 @@ export default {
|
||||
.vcd-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.vcd-values text {
|
||||
|
@ -150,7 +150,7 @@ function handleSidebarGlobalClick() {
|
||||
border-radius: 0 .8em .8em 0;
|
||||
border: solid 1px var(--sidebar-border);
|
||||
width: var(--sidebar-width);
|
||||
height: calc(100vh - var(--toolbar-height));
|
||||
height: calc(100vh - var(--toolbar-height) - 50px);
|
||||
box-shadow: 0 0 15px 1px rgb(16, 16, 16);
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -165,6 +165,14 @@ function handleSidebarGlobalClick() {
|
||||
transition: var(--animation-5s);
|
||||
}
|
||||
|
||||
.group-children .display-signal-item {
|
||||
width: calc(200px - 8px - 10px) !important;
|
||||
}
|
||||
|
||||
.group-children .signal-color-vendor > span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.display-signal-item-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -17,11 +17,13 @@
|
||||
<span>{{ signalInfo.name }}</span>
|
||||
</div>
|
||||
<div class="signal-info-width" v-show="showWidth">
|
||||
<div :class="signalInfo.size > 1 ? 'signal-info-caption' : ''">
|
||||
<div
|
||||
:class="{ 'signal-info-caption': signalInfo.size > 1 }"
|
||||
>
|
||||
{{ makeSignalCaption(signalInfo) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signal-info-current-value-wrapper">
|
||||
@ -70,7 +72,7 @@ const signalInfo = computed(() => {
|
||||
/**
|
||||
* @param {WireItemBaseInfo} signal
|
||||
*/
|
||||
function makeSignalIconClass(signal) {
|
||||
function makeSignalIconClass(signal) {
|
||||
const realSignal = globalLookup.link2CurrentWires.get(signal.link);
|
||||
return 'iconfont ' + makeIconClass(realSignal);
|
||||
}
|
||||
|
160
src/components/toolbar/cursor-location.js
Normal file
160
src/components/toolbar/cursor-location.js
Normal file
@ -0,0 +1,160 @@
|
||||
import { globalLookup } from "@/hook/global";
|
||||
import { calcCursorLeft, StaticCursor } from "../render/cursor";
|
||||
import { sidebarSelectedWires } from "@/hook/sidebar-select-wire";
|
||||
|
||||
|
||||
/**
|
||||
* @description 将 某时间点 移动到指定位置
|
||||
* @param {number} time 需要移动到的时间点
|
||||
* @param {number} leftMargin 移动后的该时间点距离左侧多少px
|
||||
* @param {Pstate} pstate
|
||||
*/
|
||||
function moveto(time, leftMargin, pstate) {
|
||||
StaticCursor.show = true;
|
||||
StaticCursor.currentTime = time;
|
||||
pstate.oldXOffset = pstate.xOffset;
|
||||
const { width, xOffset, xScale, tgcd, timescale } = pstate;
|
||||
pstate.xOffset = leftMargin - (time / tgcd) * xScale;
|
||||
|
||||
StaticCursor.updateLeft();
|
||||
StaticCursor.updateLabel(pstate.timescale);
|
||||
globalLookup.render();
|
||||
}
|
||||
|
||||
export function toBegin() {
|
||||
const pstate = globalLookup.pstate;
|
||||
if (pstate) {
|
||||
moveto(0, pstate.sidebarWidth - 20, pstate);
|
||||
}
|
||||
}
|
||||
|
||||
export function toEnd() {
|
||||
const pstate = globalLookup.pstate;
|
||||
if (pstate) {
|
||||
moveto(globalLookup.time, pstate.width - pstate.sidebarWidth, pstate);
|
||||
}
|
||||
}
|
||||
|
||||
function clip(v, min, max) {
|
||||
if (v < min) {
|
||||
v = min;
|
||||
}
|
||||
if (v > max) {
|
||||
v = max;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
export function toPrevChange() {
|
||||
const link = sidebarSelectedWires.lastLink;
|
||||
const pstate = globalLookup.pstate;
|
||||
|
||||
if (link === undefined || pstate === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chango = globalLookup.chango[link];
|
||||
if (chango && chango.wave && chango.wave.length > 0) {
|
||||
const wave = chango.wave;
|
||||
|
||||
// 如果长度为 1,直接到前面或者后面即可
|
||||
if (wave.length === 1) {
|
||||
toBegin();
|
||||
} else {
|
||||
const currentT = StaticCursor.currentTime;
|
||||
let pivot = bisearch(wave, currentT);
|
||||
|
||||
const pwave = wave[pivot];
|
||||
if (pwave[0] === currentT) {
|
||||
pivot = clip(pivot - 1, 0, wave.length - 1);
|
||||
} else {
|
||||
pivot = clip(pivot, 0, wave.length - 1);
|
||||
}
|
||||
|
||||
const targetT = wave[pivot][0];
|
||||
moveto(targetT, (pstate.width + pstate.sidebarWidth) / 2, pstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function toNextChange() {
|
||||
const link = sidebarSelectedWires.lastLink;
|
||||
const pstate = globalLookup.pstate;
|
||||
|
||||
if (link === undefined || pstate === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chango = globalLookup.chango[link];
|
||||
if (chango && chango.wave && chango.wave.length > 0) {
|
||||
const wave = chango.wave;
|
||||
|
||||
// 如果长度为 1,直接到前面或者后面即可
|
||||
if (wave.length === 1) {
|
||||
toEnd();
|
||||
} else {
|
||||
const currentT = StaticCursor.currentTime;
|
||||
let pivot = bisearch(wave, currentT);
|
||||
let targetT;
|
||||
if (pivot === wave.length - 1) {
|
||||
pivot = clip(pivot + 1, 0, wave.length - 1);
|
||||
targetT = globalLookup.time;
|
||||
} else {
|
||||
pivot = clip(pivot + 1, 0, wave.length - 1);
|
||||
targetT = wave[pivot][0];
|
||||
}
|
||||
|
||||
moveto(targetT, (pstate.width + pstate.sidebarWidth) / 2, pstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const locationManager = {
|
||||
/**
|
||||
* @description 记录所有的信标
|
||||
* @type {Map<number, HTMLElement>} 时间点到元素的映射
|
||||
*/
|
||||
pivots: new Map(),
|
||||
|
||||
};
|
||||
|
||||
|
||||
export function makeLocation() {
|
||||
const currentT = StaticCursor.currentTime;
|
||||
// 在 currentT 创建新的信标
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 二分查找找到最近的索引下标
|
||||
* @param {number[][]} wave
|
||||
* @param {number} time
|
||||
* @return {number}
|
||||
*/
|
||||
function bisearch(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;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
@ -1,35 +1,86 @@
|
||||
<template>
|
||||
<div class="location">
|
||||
<div
|
||||
class="item iconfont icon-beginning"
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('toolbar.location.to-begin')"
|
||||
placement="bottom"
|
||||
raw-content
|
||||
>
|
||||
<div
|
||||
class="item iconfont icon-beginning"
|
||||
@click="toBegin()"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
/>
|
||||
<div
|
||||
class="item iconfont icon-ending"
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('toolbar.location.to-end')"
|
||||
placement="bottom"
|
||||
raw-content
|
||||
>
|
||||
<div
|
||||
class="item iconfont icon-ending"
|
||||
@click="toEnd()"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
/>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<div
|
||||
class="item iconfont icon-arrow-to-previous"
|
||||
|
||||
/>
|
||||
<div
|
||||
class="item iconfont icon-arrow-to-next"
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('toolbar.location.to-prev-change')"
|
||||
placement="bottom"
|
||||
raw-content
|
||||
>
|
||||
<div
|
||||
class="item iconfont icon-arrow-to-previous"
|
||||
:class="{ 'disable' : globalLookup.sidebarSelectedWireLinks.size === 0 }"
|
||||
@click="toPrevChange()"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
/>
|
||||
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('toolbar.location.to-next-change')"
|
||||
placement="bottom"
|
||||
raw-content
|
||||
>
|
||||
<div
|
||||
class="item iconfont icon-arrow-to-next"
|
||||
:class="{ 'disable' : globalLookup.sidebarSelectedWireLinks.size === 0 }"
|
||||
@click="toNextChange()"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<div
|
||||
class="item iconfont icon-add-line"
|
||||
|
||||
/>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('toolbar.location.make-location')"
|
||||
placement="bottom"
|
||||
raw-content
|
||||
>
|
||||
<div
|
||||
class="item iconfont icon-add-line"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineComponent } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toBegin, toEnd, toNextChange, toPrevChange } from './cursor-location';
|
||||
import { globalLookup } from '@/hook/global';
|
||||
|
||||
defineComponent({ name: 'cursor-location' });
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@ -62,4 +113,15 @@ defineComponent({ name: 'cursor-location' });
|
||||
color: var(--sidebar);
|
||||
transition: var(--animation-3s);
|
||||
}
|
||||
|
||||
.disable {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.location .item.disable:hover {
|
||||
background-color: unset !important;
|
||||
color: unset !important;
|
||||
}
|
||||
|
||||
</style>
|
@ -37,6 +37,7 @@ defineComponent({ name: 'toolbar' });
|
||||
width: 100%;
|
||||
height: var(--toolbar-height);
|
||||
background-color: var(--background);
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.toolbar-body {
|
||||
|
1
src/components/toolbar/pivot-view.js
Normal file
1
src/components/toolbar/pivot-view.js
Normal file
@ -0,0 +1 @@
|
||||
|
@ -54,14 +54,15 @@ const signalModal = ref(0);
|
||||
const disabled = ref(true);
|
||||
|
||||
function onSignalModalUpdate() {
|
||||
console.log('enter change');
|
||||
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);
|
||||
const realChange = globalLookup.setRenderOption(link, 'renderModal', modal);
|
||||
if (realChange) {
|
||||
globalLookup.waveRender.resetVertice(link);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新渲染
|
||||
|
@ -37,9 +37,6 @@ const { t } = useI18n();
|
||||
const currentOption = ref(2);
|
||||
const disabled = ref(true);
|
||||
|
||||
function onChange() {
|
||||
console.log(currentOption.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
formatOptions.push({
|
||||
@ -95,6 +92,23 @@ onMounted(() => {
|
||||
|
||||
const formatOptions = reactive([]);
|
||||
|
||||
function onChange() {
|
||||
const links = globalLookup.sidebarSelectedWireLinks;
|
||||
if (links.size > 0) {
|
||||
const valueFormat = currentOption.value;
|
||||
// 更新渲染选项
|
||||
for (const link of links) {
|
||||
const realChange = globalLookup.setRenderOption(link, 'valueFormat', valueFormat);
|
||||
if (realChange) {
|
||||
globalLookup.waveRender.resetVertice(link);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新渲染,不过只需要渲染数值即可
|
||||
globalLookup.render({ type: 'value' });
|
||||
}
|
||||
}
|
||||
|
||||
sidebarSelectedWires.addToPipe(lastLink => {
|
||||
if (lastLink) {
|
||||
const option = globalLookup.currentSignalRenderOptions.get(lastLink);
|
||||
|
@ -14,20 +14,132 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="value-input-wrapper">
|
||||
<el-input></el-input>
|
||||
<el-input
|
||||
v-model="searchManage.content"
|
||||
@input="safeSearch"
|
||||
@focus="searchManage.displayResult = true"
|
||||
@blur="searchManage.displayResult = false"
|
||||
></el-input>
|
||||
|
||||
<transition name="collapse-from-top">
|
||||
<div
|
||||
v-show="searchManage.displayResult || searchManage.mouseOnResult"
|
||||
class="toolbar-search-container"
|
||||
>
|
||||
<el-scrollbar
|
||||
height="300px"
|
||||
width="100%"
|
||||
@mouseenter="searchManage.mouseOnResult = true"
|
||||
@mouseleave="searchManage.mouseOnResult = false"
|
||||
class="search-result"
|
||||
>
|
||||
<div v-if="searchManage.searchResult.length > 0">
|
||||
<div v-for="(signal, index) in searchManage.searchResult"
|
||||
:key="index"
|
||||
@click="clickSearchItem(signal)"
|
||||
class="toolbar-search"
|
||||
>
|
||||
<span :class="makeSignalIconClass(signal)"></span>
|
||||
<span class="signal-name">{{ signal.name }}</span>
|
||||
<span
|
||||
class="signal-width"
|
||||
>
|
||||
<span :class="{ 'vec': signal.width > 1 }">{{ makeSignalCaption(signal) }}</span>
|
||||
</span>
|
||||
<span class="signal-value">{{ signal.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="toolbar-search-nothing">
|
||||
<span class="iconfont icon-empty"></span>
|
||||
<span>{{ t('search-nothing') }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { globalLookup } from '@/hook/global';
|
||||
import { debounceWrapper, makeIconClass } from '@/hook/utils';
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
defineComponent({ name: 'value-search' })
|
||||
|
||||
const { t } = useI18n();
|
||||
const searchMode = ref(1);
|
||||
|
||||
const searchMode = ref(0);
|
||||
const searchManage = reactive({
|
||||
content: '',
|
||||
displayResult: false,
|
||||
mouseOnResult: false,
|
||||
searchResult: [],
|
||||
});
|
||||
|
||||
function addWire(wireItem) {
|
||||
searchManage.searchResult.push({
|
||||
name: wireItem.name,
|
||||
width: wireItem.size,
|
||||
type: wireItem.type,
|
||||
value: globalLookup.currentSignalValues[wireItem.link]
|
||||
});
|
||||
}
|
||||
|
||||
function searchByValue() {
|
||||
const value = searchManage.content.trim();
|
||||
for (const wireItem of globalLookup.currentWires) {
|
||||
const currentVal = globalLookup.currentSignalValues[wireItem.link];
|
||||
if (currentVal.toString().includes(value)) {
|
||||
addWire(wireItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function searchByName() {
|
||||
const name = searchManage.content.trim();
|
||||
for (const wireItem of globalLookup.currentWires) {
|
||||
if (wireItem.name.includes(name)) {
|
||||
addWire(wireItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function search() {
|
||||
if (globalLookup.currentWires.size === 0 || searchManage.content.trim().length === 0) {
|
||||
searchManage.searchResult.length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (searchMode.value) {
|
||||
case 0:
|
||||
searchByValue();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
searchByName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const safeSearch = debounceWrapper(search, 200);
|
||||
|
||||
function clickSearchItem() {
|
||||
|
||||
}
|
||||
|
||||
function makeSignalCaption(signal) {
|
||||
if (signal === undefined) {
|
||||
return '';
|
||||
}
|
||||
return signal.width === 1 ? '' : `${signal.width - 1}:0`;
|
||||
}
|
||||
|
||||
function makeSignalIconClass(signal) {
|
||||
return 'iconfont ' + makeIconClass(signal);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@ -41,8 +153,106 @@ const searchMode = ref(0);
|
||||
}
|
||||
|
||||
.value-input-wrapper {
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.toolbar-search-container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: -10px;
|
||||
transition: var(--animation-3s);
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
background-color: var(--sidebar);
|
||||
border: 1.5px solid var(--main-color);
|
||||
color: var(--sidebar-item-text);
|
||||
border-radius: .5em;
|
||||
width: 420px;
|
||||
}
|
||||
|
||||
.toolbar-search-nothing {
|
||||
height: 260px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.toolbar-search-nothing .iconfont {
|
||||
font-size: 80px;
|
||||
}
|
||||
|
||||
.toolbar-search .signal-name {
|
||||
width: 100px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.toolbar-search .iconfont {
|
||||
font-size: 13px;
|
||||
color: var(--sidebar);
|
||||
background-color: #7ca532;
|
||||
border-radius: 99em;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.toolbar-search {
|
||||
display: flex;
|
||||
margin: 3px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: var(--animation-3s);
|
||||
border-radius: .5em;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.toolbar-search:hover {
|
||||
transition: var(--animation-3s);
|
||||
background-color: var(--sidebar-item-selected);
|
||||
}
|
||||
|
||||
.toolbar-search .signal-width {
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.toolbar-search .vec {
|
||||
color: var(--sidebar);
|
||||
background-color: #7ca532;
|
||||
padding: 2px 3px;
|
||||
border-radius: .5em;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.toolbar-search .signal-value {
|
||||
width: 150px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
</style>
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
<transition name="collapse-from-top">
|
||||
<div
|
||||
v-show="searchManage.displayResult | searchManage.mouseOnResult"
|
||||
v-show="searchManage.displayResult || searchManage.mouseOnResult"
|
||||
:style="searchResultWrapper"
|
||||
class="search-result-wrapper"
|
||||
id="search-result-wrapper"
|
||||
@ -110,7 +110,7 @@ emitter.on('signal-search', searchResultItem => {
|
||||
searchManage.searchResult.push(searchResultItem);
|
||||
});
|
||||
|
||||
const safeSearch = debounceWrapper(search, 500);
|
||||
const safeSearch = debounceWrapper(search, 200);
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -86,10 +86,11 @@ export const globalLookup = reactive({
|
||||
*/
|
||||
chango: {},
|
||||
|
||||
// 初始化时会被定义
|
||||
render: () => {},
|
||||
|
||||
|
||||
/**
|
||||
* @description 【核心】接入渲染管线,重新渲染,该函数必须在 domContainer 初始化完成后再去调用
|
||||
* @param {RenderConfig} renderConfig 渲染的类型,默认为重新渲染所有可视项
|
||||
*/
|
||||
render: (renderConfig) => {},
|
||||
|
||||
/**
|
||||
* @description 非常核心的用于操控 webgl 进行渲染和动画的核心类
|
||||
@ -98,6 +99,10 @@ export const globalLookup = reactive({
|
||||
waveRender: undefined,
|
||||
|
||||
|
||||
/**
|
||||
* @description 描述描述当前渲染区的一些基本几何状态
|
||||
* @type {Pstate | undefined}
|
||||
*/
|
||||
pstate: undefined,
|
||||
|
||||
xScale: 1,
|
||||
@ -126,13 +131,18 @@ export const globalLookup = reactive({
|
||||
* @param {string} link
|
||||
* @param {'height' | 'color' | 'valueFormat' | 'renderModal'} option
|
||||
* @param {any} value
|
||||
* @returns {boolean} true 代表改变了值,否则则没有,可以用来判断是否需要重复塑造 Vertices
|
||||
*/
|
||||
setRenderOption(link, option, value) {
|
||||
if (!this.currentSignalRenderOptions.has(link)) {
|
||||
this.currentSignalRenderOptions.set(link, {});
|
||||
}
|
||||
const renderOption = this.currentSignalRenderOptions.get(link);
|
||||
if (renderOption[option] === value) {
|
||||
return false;
|
||||
}
|
||||
renderOption[option] = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -65,8 +65,6 @@ function makeWaveView(parentElement) {
|
||||
console.log('updater');
|
||||
};
|
||||
|
||||
globalLookup
|
||||
|
||||
// 注册基本的响应事件
|
||||
container.elo.container.addEventListener('wheel',
|
||||
registerWheelEvent(parentElement, container.pstate, globalLookup, eventHandler)
|
||||
|
@ -21,14 +21,20 @@ const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
|
||||
// 这是上面展示当前 time 的圆角矩形内的字体大小
|
||||
const fontHeight = 20;
|
||||
const fontWidth = fontHeight / 2;
|
||||
const sidebarWidth = pstate.sidebarWidth;
|
||||
|
||||
const handler = event => {
|
||||
const x = pstate.xCursor = event.clientX;
|
||||
cursor.style.left = (x - xmargin) + 'px';
|
||||
cursor.innerHTML = renderCursor({ xmargin, fontWidth, fontHeight }, pstate);
|
||||
const left = x - xmargin;
|
||||
// 不能进入左侧,也不能进入右侧
|
||||
if (left > sidebarWidth - 195) {
|
||||
cursor.style.left = (x - xmargin) + 'px';
|
||||
cursor.innerHTML = renderCursor({ xmargin, fontWidth, fontHeight }, pstate);
|
||||
}
|
||||
};
|
||||
// 添加初始鼠标位置
|
||||
handler({ clientX: pstate.width / 2 });
|
||||
content.addEventListener('mousemove', handler);
|
||||
document.addEventListener('mousemove', handler);
|
||||
};
|
||||
|
||||
|
||||
@ -171,7 +177,6 @@ const domContainer = (obj) => {
|
||||
setTime(pstate, deso.timeOpt.value);
|
||||
}
|
||||
|
||||
|
||||
const waveRender = new WebGL2WaveRender(elo, deso, pstate, obj.renderPlugins);
|
||||
deso.render = waveRender.render.bind(waveRender);
|
||||
deso.waveRender = waveRender;
|
||||
|
@ -81,7 +81,7 @@ function addVecRenderItem(link) {
|
||||
/**
|
||||
*
|
||||
* @param {GlobalLookup} desc
|
||||
* @param {*} pstate
|
||||
* @param {Pstate} pstate
|
||||
* @returns
|
||||
*/
|
||||
function* renderValues(desc, pstate) {
|
||||
@ -89,9 +89,9 @@ function* renderValues(desc, pstate) {
|
||||
|
||||
// 根据 currentWiresRenderView 视图渲染
|
||||
// 此处应该和 render-wave 的相同注释的地方保持逻辑上的一致(render-wave.js 约 646 行)
|
||||
|
||||
const currentWires = desc.currentWires;
|
||||
const renderSignals = [];
|
||||
|
||||
for (const view of globalLookup.currentWiresRenderView) {
|
||||
if (view.renderType === 0) {
|
||||
const realSignal = addVecRenderItem(view.signalInfo.link);
|
||||
@ -115,48 +115,57 @@ function* renderValues(desc, pstate) {
|
||||
|
||||
|
||||
const lineHeightExtra = (globalSetting.displaySignalHeight - 30) / 2;
|
||||
const ilen = height / yStep;
|
||||
const iskip = (yOffset - lineHeightExtra) / yStep;
|
||||
const canvaseHeight = height - topBarHeight - botBarHeight;
|
||||
|
||||
const ml = genSVG(width, height - topBarHeight - botBarHeight);
|
||||
// 当前一页最多渲染这么多项目
|
||||
const validRenderNum = Math.ceil(canvaseHeight / yStep);
|
||||
|
||||
const ml = genSVG(width, canvaseHeight);
|
||||
|
||||
// 需要跳过前面几项
|
||||
const renderStartNo = (yOffset - lineHeightExtra) / yStep;
|
||||
let ifirst = 0;
|
||||
for (let i = 0; i < ilen; ++ i) {
|
||||
for (let i = 0; i < validRenderNum; ++ i) {
|
||||
const lane = renderSignals[i];
|
||||
if (lane && (lane.name || lane.kind)) {
|
||||
if (i > iskip) {
|
||||
if (i > renderStartNo) {
|
||||
break;
|
||||
}
|
||||
ifirst = i;
|
||||
}
|
||||
}
|
||||
|
||||
ml.push(defs);
|
||||
yield;
|
||||
|
||||
const markers = ['g'];
|
||||
ml.push(markers);
|
||||
|
||||
for (let i = 0; i < (iskip + ilen); i++) {
|
||||
for (let i = 0; i < (renderStartNo + validRenderNum); i++) {
|
||||
const lane = renderSignals[i + (ifirst | 0)];
|
||||
const link = (lane || {}).ref;
|
||||
|
||||
if (link === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lane && lane.kind === 'DIZ') {
|
||||
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(water(lane, desc, pstate)));
|
||||
markers.push(['g', tt(0, Math.round((i - (renderStartNo - ifirst) + 1.18) * yStep))].concat(water(lane, desc, pstate)));
|
||||
} else if (lane && lane.kind === 'brace') {
|
||||
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(bracer(lane, desc, pstate)));
|
||||
markers.push(['g', tt(0, Math.round((i - (renderStartNo - ifirst) + 1.18) * yStep))].concat(bracer(lane, desc, pstate)));
|
||||
} else if (lane && link) {
|
||||
const chango = desc.chango[link];
|
||||
if (chango && chango.kind === 'vec') {
|
||||
// tt: 计算出当前这一行的所有 值 svg 在 Y 方向的偏移
|
||||
const mLane = ['g', tt(0, Math.round((i - (iskip - ifirst) + 0.15) * yStep))];
|
||||
const mLane = ['g', tt(0, Math.round((i - (renderStartNo - ifirst) + 0.15) * yStep))];
|
||||
const { wave } = chango;
|
||||
const jlen = wave.length;
|
||||
|
||||
perLane: {
|
||||
let [tPre, vPre, mPre] = wave[0];
|
||||
|
||||
let xPre = getX(pstate, tPre);
|
||||
const labeler = getLabel(lane);
|
||||
for (let j = 1; j <= jlen; j++) {
|
||||
for (let j = 1; j <= wave.length; j++) {
|
||||
const mark = wave[j];
|
||||
|
||||
const [tCur, vCur, mCur] = (mark || [desc.time, 0, 0]);
|
||||
@ -175,7 +184,7 @@ function* renderValues(desc, pstate) {
|
||||
if (w > 8) {
|
||||
const x = Math.round((xPreNorm + xCurNorm) / 2);
|
||||
// 计算 当前这一个 值 在 X 方向上的偏移
|
||||
const renderLabel = labeler(vPre, mPre, x, w, link);
|
||||
const renderLabel = labeler(vPre, mPre, x, w, link);
|
||||
mLane.push(renderLabel);
|
||||
}
|
||||
}
|
||||
@ -191,7 +200,6 @@ function* renderValues(desc, pstate) {
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(view);
|
||||
for (let i = 0; i < renderSignals.length; i++) {
|
||||
const lane = renderSignals[i];
|
||||
if (lane && lane.vlines) {
|
||||
|
@ -3,6 +3,7 @@ import { globalSetting, globalStyle } from '../global';
|
||||
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_WidthShifts, barShift, getRatio, screenHeightPixel, maskColorIndexOffset, posYFactor, glslInputLength, prettyPrint, ladderAnalog_GL_WidthShifts, lineAnlog_GL_WidthShifts } from './render-utils.js';
|
||||
import { vertexShader, fragmentShader } from './render-shader.js';
|
||||
import { renderAsBit, renderAsCommonDigital, renderAsLadderAnalog, renderAsLineAnalog } from './toolbar/renderModal';
|
||||
import { StaticCursor } from '@/components/render/cursor';
|
||||
|
||||
/**
|
||||
* @description
|
||||
@ -392,6 +393,17 @@ class WebGL2WaveRender {
|
||||
|
||||
const _this = this;
|
||||
|
||||
if (renderConfig.type === 'value') {
|
||||
const renderValue = plugins.filter(plugin => plugin.name === 'pluginRenderValues')[0];
|
||||
if (renderValue) {
|
||||
renderValue(globalLookup, pstate, elements);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新静态 cursor
|
||||
StaticCursor.updateLeft();
|
||||
|
||||
function linearAnimation(delta, oldVal, newVal) {
|
||||
return (1 - delta) * oldVal + delta * newVal;
|
||||
}
|
||||
@ -537,10 +549,8 @@ class WebGL2WaveRender {
|
||||
pstate.oldYStep = pstate.yStep;
|
||||
pstate.oldYDuty = pstate.yDuty;
|
||||
|
||||
if (needHiddenAnimation) {
|
||||
_this.elements.grid.classList.remove('vcd-hidden');
|
||||
_this.elements.values.classList.remove('vcd-hidden');
|
||||
}
|
||||
_this.elements.grid.classList.remove('vcd-hidden');
|
||||
_this.elements.values.classList.remove('vcd-hidden');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ export class FormatValueRender {
|
||||
if (valueString.length <= pos) {
|
||||
return valueString;
|
||||
}
|
||||
const valSign = (value < 0) ? '-' : '+';
|
||||
const valSign = (value < 0) ? '-' : '';
|
||||
if (pos === 1) {
|
||||
return valSign;
|
||||
}
|
||||
@ -155,6 +155,8 @@ export class FormatValueRender {
|
||||
* @param {number} pos 位置
|
||||
*/
|
||||
floatTransform(fWidth, value, pos) {
|
||||
const replacer = this.replacer;
|
||||
|
||||
if (fWidth === 16) {
|
||||
value = this.calcIEEEFloat(value, 5, 10);
|
||||
} else if (fWidth === 32) {
|
||||
@ -167,7 +169,7 @@ export class FormatValueRender {
|
||||
if (valueString.length <= pos) {
|
||||
return valueString;
|
||||
}
|
||||
const valSign = (value < 0) ? '-' : '+';
|
||||
const valSign = (value < 0) ? '-' : '';
|
||||
if (pos === 1) {
|
||||
return valSign;
|
||||
}
|
||||
@ -177,6 +179,144 @@ export class FormatValueRender {
|
||||
return valSign + replacer + valueString.slice(2 - pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据 e 和 m 得到计算 IEEE 浮点数的函数,默认第一个位置代表符号
|
||||
* @param {number} value 值
|
||||
* @param {number} eWidth 代表指数部分的位数
|
||||
* @param {number} mWidth 代表有效尾数的位数
|
||||
* @returns {(value: number) => number}
|
||||
*/
|
||||
calcIEEEFloat(value, eWidth, mWidth) {
|
||||
const offset = (1 << (eWidth - 1)) - 1;
|
||||
const mFrac = 1 << mWidth;
|
||||
|
||||
// 计算参考博客:https://blog.csdn.net/leo0308/article/details/117398166
|
||||
let bits = value.toString(2);
|
||||
const width = 1 + eWidth + mWidth;
|
||||
// 应对 bits 长度和 预定长度不一致的情况
|
||||
// 不足的地方补上 0,否则进行截断
|
||||
if (bits.length < width) {
|
||||
bits = '0'.repeat(width - bits.length) + bits;
|
||||
}
|
||||
if (bits.length > width) {
|
||||
bits = bits.substring(bits.length - width);
|
||||
}
|
||||
|
||||
const s = parseInt(bits[0], 2);
|
||||
const e = parseInt(bits.substring(1, 1 + eWidth), 2);
|
||||
const m = parseInt(bits.substring(2 + eWidth), 2);
|
||||
|
||||
const sign = (s === 1) ? -1 : 1;
|
||||
// 全 0
|
||||
if (e === 0) {
|
||||
return sign * efficentPow2(-offset + 1 - mWidth) * m;
|
||||
}
|
||||
|
||||
// 全 1
|
||||
if (e === ((1 << eWidth) - 1)) {
|
||||
if (m === 0) {
|
||||
return sign * Infinity;
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
return sign * efficentPow2(e - offset) * (1 + m / mFrac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class JSValueRender {
|
||||
/**
|
||||
*
|
||||
* @param {string} link 信号的ID
|
||||
* @param {number} width 信号的位置宽度
|
||||
* @param {string} replacer 位置不足的地方会用 replacer 补足,比如 ...
|
||||
*/
|
||||
constructor(link, width) {
|
||||
this.formatCode = getValueFormatCode(link);
|
||||
this.width = BigInt(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据当前的设置进行数值的渲染
|
||||
* @param {number} value 波形当前的数值
|
||||
* @returns {string} 转换后需要渲染的字符串
|
||||
*/
|
||||
explainAsNumber(value) {
|
||||
const formatCode = this.formatCode;
|
||||
switch (formatCode) {
|
||||
// 二进制
|
||||
case 0: return this.radixTransform(2, value);
|
||||
// 八进制
|
||||
case 1: return this.radixTransform(8, value);
|
||||
// 十六进制
|
||||
case 2: return this.radixTransform(16, value);
|
||||
// 有符号整型
|
||||
case 3: return this.radixTransform(10, value, true);
|
||||
// 无符号整型
|
||||
case 4: return this.radixTransform(10, value);
|
||||
// 半精度浮点数
|
||||
case 5: return this.floatTransform(16, value);
|
||||
// 单精度浮点数
|
||||
case 6: return this.floatTransform(32, value);
|
||||
// 双精度浮点数
|
||||
case 7: return this.floatTransform(64, value);
|
||||
// 未知
|
||||
default: return '?';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 整数的进制转换
|
||||
* @param {number} radix 进制
|
||||
* @param {number} value 值
|
||||
* @param {number} pos 位置宽度
|
||||
* @param {boolean} sign 是否是有符号数,默认为 false
|
||||
*/
|
||||
radixTransform(radix, value, sign = false) {
|
||||
const width = this.width;
|
||||
const replacer = this.replacer;
|
||||
|
||||
if (value === 'x') {
|
||||
return -1;
|
||||
}
|
||||
value = BigInt(value);
|
||||
|
||||
// 如果是有符号数
|
||||
if (sign) {
|
||||
if ((value >> (width - 1n)) & 1n) {
|
||||
value = value - (2n ** width);
|
||||
}
|
||||
|
||||
return parseInt(value);
|
||||
}
|
||||
|
||||
// 无符号的各类进制数字
|
||||
return parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description IEEE 浮点数的转换
|
||||
* 为什么没有 sign 参数?因为 IEEE 定义下的 float 第一个位置就是符号
|
||||
* @param {number} fWidth 16, 32 或者 64
|
||||
* @param {number} value 值
|
||||
* @param {number} pos 位置
|
||||
*/
|
||||
floatTransform(fWidth, value) {
|
||||
const replacer = this.replacer;
|
||||
|
||||
if (fWidth === 16) {
|
||||
value = this.calcIEEEFloat(value, 5, 10);
|
||||
} else if (fWidth === 32) {
|
||||
value = this.calcIEEEFloat(value, 8, 23);
|
||||
} else if (fWidth === 64) {
|
||||
value = this.calcIEEEFloat(value, 11, 52);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据 e 和 m 得到计算 IEEE 浮点数的函数,默认第一个位置代表符号
|
||||
* @param {number} value 值
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
import { globalLookup } from "@/hook/global";
|
||||
import { lineAnalog_WidthShift, lineAnlog_GL_WidthShifts, maskColorIndexOffset, posYFactor, prettyPrint } from "../render-utils";
|
||||
import { hexToSignedInt } from "./renderFormat";
|
||||
import { FormatValueRender, hexToSignedInt, JSValueRender } from "./renderFormat";
|
||||
|
||||
function makeQuadVertices(p1, p2, p3, p4) {
|
||||
return [
|
||||
@ -413,11 +413,13 @@ function getMaxMinByFormat(link, wave, time, formatCode, width) {
|
||||
let minVal = undefined;
|
||||
const length = wave.length;
|
||||
|
||||
const valueRender = new JSValueRender(link, width);
|
||||
|
||||
// TODO : 优化这段代码
|
||||
for (let i = 0; i < length; ++ i) {
|
||||
const [t1, val, mask] = wave[i];
|
||||
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
|
||||
const numVal = explainAsJSNumber(formatCode, val, width);
|
||||
const numVal = valueRender.explainAsNumber(val);
|
||||
|
||||
if (maxVal === undefined) {
|
||||
maxVal = numVal;
|
||||
@ -479,6 +481,7 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
|
||||
|
||||
const { maxVal, minVal } = getMaxMinByFormat(link, wave, time, formatCode, width);
|
||||
const coordinateTransform = getMappingFunc(formatCode, maxVal, minVal);
|
||||
const valueRender = new JSValueRender(link, width);
|
||||
|
||||
function makeLadderAnalogRenderParam(link, wave, time) {
|
||||
const [t1, val, mask] = wave;
|
||||
@ -489,7 +492,7 @@ export function renderAsLadderAnalog(lookup, link, wave, time) {
|
||||
}
|
||||
|
||||
// 根据当前格式进行转换
|
||||
const numVal = explainAsJSNumber(formatCode, val, width);
|
||||
const numVal = valueRender.explainAsNumber(val);
|
||||
// 此时的 y 是一个 -1 到 1 的浮点数,因为 VAO 我设置了 Int
|
||||
const y = coordinateTransform(numVal);
|
||||
const colorParam = { y, color: 5 };
|
||||
@ -648,6 +651,7 @@ export function renderAsLineAnalog(lookup, link, wave, time) {
|
||||
const length = wave.length;
|
||||
const lineVertices = [];
|
||||
const maskVertices = [];
|
||||
const valueRender = new JSValueRender(link, width);
|
||||
|
||||
function makeLineAnalogRenderParam(link, wave, time) {
|
||||
const [t1, val, mask] = wave;
|
||||
@ -658,7 +662,7 @@ export function renderAsLineAnalog(lookup, link, wave, time) {
|
||||
}
|
||||
|
||||
// 根据当前格式进行转换
|
||||
const numVal = explainAsJSNumber(formatCode, val, width);
|
||||
const numVal = valueRender.explainAsNumber(val);
|
||||
// 此时的 y 是一个 -1 到 1 的浮点数,因为 VAO 我设置了 Int
|
||||
const y = coordinateTransform(numVal);
|
||||
const colorParam = { y, color: 5 };
|
||||
|
@ -21,6 +21,7 @@ const xOffsetUpdate = (pstate, nextOffsetXFn) => {
|
||||
|
||||
pstate.oldXOffset = pstate.xOffset;
|
||||
pstate.xOffset = nextOffsetX;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -6,19 +6,25 @@ const yOffsetUpdate = (pstate, nextOffsetYFn) => {
|
||||
|
||||
nextOffsetY = nextOffsetY;
|
||||
|
||||
// 当前所有信号的高度之和
|
||||
const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep;
|
||||
|
||||
// 当前有效的画布高度
|
||||
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
|
||||
// console.log(currentRenderHeight, canvasHeight);
|
||||
const maxOffsetY = Math.max(-20,
|
||||
currentRenderHeight - canvasHeight); // maximum offset
|
||||
|
||||
if (currentRenderHeight < canvasHeight) {
|
||||
// 如果当前的信号高度小于画布的高度,则取消本次渲染
|
||||
return false;
|
||||
}
|
||||
|
||||
const maxOffsetY = Math.max(-20, currentRenderHeight - canvasHeight);
|
||||
nextOffsetY = Math.min(nextOffsetY, maxOffsetY);
|
||||
|
||||
const minOffsetY = -20; // minimum offset
|
||||
const minOffsetY = -20;
|
||||
nextOffsetY = Math.max(nextOffsetY, minOffsetY);
|
||||
|
||||
// if (nextOffsetY === xOffset) {
|
||||
// return false; // exit without scroll
|
||||
// }
|
||||
console.log(nextOffsetY);
|
||||
|
||||
|
||||
pstate.oldYOffset = pstate.yOffset;
|
||||
pstate.yOffset = nextOffsetY;
|
||||
|
@ -87,6 +87,12 @@
|
||||
"toolbar.format.float": "Float (32bit)",
|
||||
"toolbar.format.double": "Double (64bit)",
|
||||
|
||||
"toolbar.location.to-begin": "Move to Beginning",
|
||||
"toolbar.location.to-end": "Move to End",
|
||||
"toolbar.location.to-next-change": "Go to Next Change Edge",
|
||||
"toolbar.location.to-prev-change": "Go to Previous Change Edge",
|
||||
"toolbar.location.make-location": "Create New Pivot",
|
||||
|
||||
"current-version": "current version",
|
||||
"copyright": "The copyright of this software belongs to <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> project team. Welcome to <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>."
|
||||
}
|
@ -85,7 +85,11 @@
|
||||
"toolbar.format.float": "单精度(32bit)",
|
||||
"toolbar.format.double": "双精度(64bit)",
|
||||
|
||||
|
||||
"toolbar.location.to-begin": "移动至开头",
|
||||
"toolbar.location.to-end": "移动至结尾",
|
||||
"toolbar.location.to-next-change": "前往下一个变化的边沿",
|
||||
"toolbar.location.to-prev-change": "前往上一个变化的边沿",
|
||||
"toolbar.location.make-location": "创建新的信标",
|
||||
|
||||
"current-version": "当前版本",
|
||||
"copyright": "本软件版权归 <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> 项目组所有,欢迎 <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>。"
|
||||
|
@ -96,13 +96,18 @@
|
||||
/**
|
||||
* @description
|
||||
* @typedef {Object} Pstate
|
||||
* @property {number} tgcd
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {number} xScale
|
||||
* @property {number} xCursor
|
||||
* @property {number} xOffset
|
||||
* @property {number} yOffset
|
||||
* @property {number} yStep
|
||||
* @property {number} yDuty
|
||||
* @property {number} sidebarWidth
|
||||
* @property {number} topBarHeight
|
||||
* @property {number} botBarHeight
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -124,7 +129,7 @@
|
||||
/**
|
||||
* @description
|
||||
* @typedef {Object} RenderConfig
|
||||
* @property {'common' | 'action' | undefined} type
|
||||
* @property {'common' | 'action' | 'value' | undefined} type
|
||||
*/
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user