完成信标功能

This commit is contained in:
锦恢 2024-08-30 23:56:49 +08:00
parent f74fc31aaf
commit 47e9e57c80
26 changed files with 974 additions and 296 deletions

View File

@ -1,6 +1,6 @@
@font-face {
font-family: "iconfont"; /* Project id 4440655 */
src: url('iconfont.woff2?t=1723382481292') format('woff2');
src: url('iconfont.woff2?t=1725033317914') format('woff2');
}
.iconfont {
@ -11,6 +11,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-axis:before {
content: "\ed1f";
}
.icon-add-line:before {
content: "\e7b0";
}

Binary file not shown.

View File

@ -2,8 +2,8 @@
<!-- 上方的 toolbar -->
<ToolBar></ToolBar>
<!-- 主渲染区 -->
<MainRender></MainRender>
<!-- 信标区域 -->
<Pivot></Pivot>
<!-- 左上角的 sidebar用于显示 -->
<Sidebar></Sidebar>
@ -27,7 +27,7 @@ import { getCrossOriginWorkerURL } from '@/hook/network';
import ToolBar from '@/components/toolbar';
import Sidebar from '@/components/sidebar';
import RightNav from '@/components/right-nav.vue';
import MainRender from '@/components/render';
import Pivot from '@/components/pivot';
const { t } = useI18n();

View File

@ -2,30 +2,53 @@ import { globalLookup } from '@/hook/global';
import formatTime from '@/hook/wave-view/format-time';
import { reactive } from 'vue';
export const StaticCursor = reactive({
/**
* @typedef {(event: MouseEvent) => void} MousemoveFn
*/
export const MovingPivot = reactive({
color: '#CB81DA',
label: '',
left: 0,
currentTime: 0,
show: true,
/**
* @type {import('./pivot-view').UserPivot | undefined}
*/
currentTakenPivot: undefined,
dragEnable: false
});
export const SystemPivot = reactive({
label: '',
show: false,
currentTime: 0,
left: 0,
color: '#CB81DA',
updateLabel(timescale) {
this.label = formatTime(this.currentTime, timescale);
this.show = true;
},
updateLeft() {
this.left = calcCursorLeft(this.currentTime);
this.show = true;
}
});
export function changeCursorLocation() {
StaticCursor.show = true;
/**
* @description 左击改变系统信标的位置
*/
export function changeCursorLocation() {
if (MovingPivot.dragEnable) {
return;
}
SystemPivot.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();
SystemPivot.currentTime = currentTime;
SystemPivot.updateLabel(timescale);
SystemPivot.updateLeft();
}
}
@ -44,6 +67,7 @@ export function calcCursorLeft(currentTime) {
return 0;
}
export const MovingCursor = reactive({
});
/**
* @type {Map<number, MousemoveFn>}
*/
export const mousemoveEventPipes = new Map();

View File

@ -1,18 +1,47 @@
<template>
<!-- <VerticalCursor></VerticalCursor> -->
<!-- <TimeScale></TimeScale> -->
<div class="vcd-render-wrapper" @click="onRenderClick()">
<VerticalCursor></VerticalCursor>
<div
class="vcd-render-wrapper"
@mousedown="onRenderClick()"
@mouseenter="onEnter()"
:ref="el => pivotWrapper = el"
>
<Pivot
v-show="SystemPivot.show"
:color="SystemPivot.color"
:label="SystemPivot.label"
:x="SystemPivot.left"
/>
<PivotView />
<transition name="main-fade">
<MovingPivotVue
v-show="MovingPivot.show"
/>
</transition>
<transition name="collapse-from-top">
<UserContextMenu
v-show="pivotcontextmenu.show"
/>
</transition>
</div>
</template>
<script setup>
import { defineComponent, reactive, ref } from 'vue';
import { emitter, globalLookup, globalSetting } from '@/hook/global';
import { defineComponent, onMounted, ref } from 'vue';
import { updateWireCurrentValue } from '@/hook/utils';
import VerticalCursor from '@/components/render/cursor.vue';
import { changeCursorLocation } from './cursor';
import { changeCursorLocation, SystemPivot, MovingPivot, mousemoveEventPipes } from './cursor';
import { eventHandler, registerWheelEvent } from '@/hook/wave-view';
import PivotView from './pivot-view.vue';
import Pivot from '@/components/pivot/system-pivot.vue';
import { globalLookup } from '@/hook/global';
import MovingPivotVue from './moving-pivot.vue';
import { pivotcontextmenu } from './user-context-menu';
import UserContextMenu from './user-context-menu.vue';
defineComponent({ name: 'main-render' });
@ -24,10 +53,34 @@ function onRenderClick() {
changeCursorLocation();
}
function onEnter() {
MovingPivot.show = true;
MovingPivot.currentTakenPivot = undefined;
}
const pivotWrapper = ref(null);
onMounted(() => {
const pivotContainer = pivotWrapper.value;
if (pivotContainer instanceof HTMLElement) {
pivotContainer.addEventListener('wheel',
registerWheelEvent(null, globalLookup.pstate, globalLookup, eventHandler)
);
pivotContainer.addEventListener('mousemove', event => {
for (const pipeId of mousemoveEventPipes.keys()) {
const fn = mousemoveEventPipes.get(pipeId);
fn(event);
}
});
}
});
</script>
<style>
.vcd-render-wrapper {
user-select: none;
height: 98vh;
width: 98vw;
cursor: crosshair;
@ -81,7 +134,7 @@ function onRenderClick() {
.vcd-cursor {
position: absolute;
pointer-events: none;
z-index: 60;
z-index: 80;
}
.vcd-values text {

View File

@ -0,0 +1,165 @@
<template>
<div :ref="el => element = el">
<div
class="vertical-cursor-wrapper"
:style="cursorStyle"
>
<div class="current-display-cursor-up">
<div class="current-time-value" :style="colorStyle">{{ MovingPivot.label }}</div>
<div class="cursor-down-arrow" :style="borderStyle"></div>
</div>
<div class="vertical-line" :style="verticalLineStyle"></div>
<div class="current-display-cursor-down">
<div class="current-time-value" :style="colorStyle">{{ MovingPivot.label }}</div>
<div class="cursor-up-arrow" :style="borderStyle"></div>
</div>
</div>
</div>
</template>
<script setup>
import { emitter, globalLookup } from '@/hook/global';
import { eventHandler, registerWheelEvent } from '@/hook/wave-view';
import { computed, defineComponent, ref, onMounted } from 'vue';
import { calcCursorLeft, MovingPivot } from './cursor';
import formatTime from '@/hook/wave-view/format-time';
import { getNearestUserPivot } from './pivot-view';
defineComponent({ name: 'user-pivot' });
const element = ref(null);
const colorStyle = computed(() => ({
backgroundColor: MovingPivot.color
}));
const verticalLineStyle = computed(() => ({
border: '2px dashed ' + MovingPivot.color
}));
const borderStyle = computed(() => ({
borderLeft: '5px solid ' + MovingPivot.color,
borderTop: '5px solid ' + MovingPivot.color
}));
const boxShift = 30;
const cursorStyle = computed(() => ({
left: MovingPivot.left - boxShift + 'px'
}));
/**
* @param {MouseEvent} event
*/
function onMousemove(event) {
const pstate = globalLookup.pstate;
const x = event.clientX || event.x;
pstate.xCursor = x;
const { xScale, xOffset, tgcd, timescale, xCursor } = pstate;
const currentT = Math.round((xCursor - xOffset) / xScale) * tgcd;
globalLookup.currentTime = currentT;
MovingPivot.currentTime = currentT;
MovingPivot.label = formatTime(currentT, timescale);
MovingPivot.left = calcCursorLeft(currentT);
if (MovingPivot.currentTakenPivot !== undefined) {
return;
}
//
const userPivot = getNearestUserPivot(currentT);
if (userPivot) {
const marginX = Math.abs(x - userPivot.x);
if (marginX < 25) {
//
MovingPivot.show = false;
MovingPivot.currentTakenPivot = userPivot;
}
}
}
emitter.on('meta-ready', () => {
const pivot = element.value;
if (pivot instanceof HTMLElement) {
document.addEventListener('mousemove', onMousemove);
//
const validWidth = globalLookup.pstate.width || window.innerWidth;
onMousemove({ clientX: validWidth / 2 })
}
});
</script>
<style scoped>
.vertical-cursor-wrapper {
position: absolute;
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 {
height: var(--time-scale-height);
width: 60px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-display-cursor-down {
height: var(--time-scale-height);
width: 60px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-time-value {
border-radius: .4em;
color: var(--sidebar);
padding: 5px;
width: fit-content;
font-size: 1.05rem;
font-family: var(--vcd-value-font-family);
white-space: nowrap;
z-index: 60;
}
.cursor-down-arrow {
transform: rotate(225deg);
position: absolute;
width: 15px;
height: 15px;
/* 30px - 10 * 2 / \sqrt{2} */
left: 19.93px;
bottom: -3px;
z-index: 50;
}
.vertical-line {
height: calc(100vh - 2 * var(--time-scale-height) - 65px);
}
.cursor-up-arrow {
transform: rotate(45deg);
position: absolute;
width: 15px;
height: 15px;
left: 19.93px;
top: -3px;
z-index: 50;
}
</style>

View File

@ -0,0 +1,138 @@
import { reactive, ref } from "vue";
import { calcCursorLeft, SystemPivot } from "./cursor";
import { globalLookup } from "@/hook/global";
import formatTime from "@/hook/wave-view/format-time";
/**
* @description 每一个动态信标
* @typedef {Object} UserPivot
* @property {number} id
* @property {string} label
* @property {number} x
* @property {number} time
* @property {boolean} show
*/
/**
* @description 该数据结构描述了所有通过 makePivot 创建的 用户信标
* @type {Map<number, UserPivot>}
*/
export const UserPivots = reactive(new Map());
export const orderedTimes = [];
export const UserPivotColor = ref('#1e90ff');
/**
* @description 创建一个信标
* @param {number} time 需要创建的轴的时间点
*/
export function createPivot(time) {
if (UserPivots.has(time)) {
return;
}
const id = Date.now();
const x = calcCursorLeft(time);
const timescale = globalLookup.timescale;
const label = formatTime(time, timescale);
const show = true;
UserPivots.set(time, { time, x, id, label, show });
orderedTimes.push(time);
orderedTimes.sort((a, b) => a < b);
}
export function searchOrderedTimeIndex(time) {
for (let i = 0; i < orderedTimes.length; ++ i) {
if (orderedTimes[i] === time) {
return i;
}
}
return undefined;
}
export function deletePivot(time) {
if (!UserPivots.has(time)) {
return;
}
UserPivots.delete(time);
const i = searchOrderedTimeIndex(time);
if (i !== undefined) {
orderedTimes.splice(i, 1);
}
}
export function updateAllPivots() {
// 更新系统信标
SystemPivot.updateLeft();
// 更新所有用户信标
for (const time of UserPivots.keys()) {
const pivot = UserPivots.get(time);
pivot.x = calcCursorLeft(time);
}
}
/**
*
* @param {number[]} times
* @param {number} target 目标时间
* @returns
*/
function bisearch(times, target) {
let i = 0, j = times.length - 1;
while (i < j) {
if (times[i] === target) {
break;
}
if (times[j] <= target) {
i = j;
break;
}
if (j - i === 1) {
break;
}
const mid = (i + j) >> 1;
if (times[mid] > target) {
j = mid;
} else {
i = mid;
}
}
return i;
}
/**
* @description 高效获取距离 time 最近的时间点 t时间复杂度 O(logn)
* @param {number} time
* @returns {UserPivot | undefined}
*/
export function getNearestUserPivot(time) {
const pivotNum = UserPivots.size;
if (pivotNum === 0) {
return undefined;
}
if (pivotNum === 1) {
for (const [time, pivot] of UserPivots) {
return pivot;
}
}
const i = bisearch(orderedTimes, time);
// console.log(i, orderedTimes, time);
if (i === pivotNum - 1) {
return UserPivots.get(orderedTimes[i]);
}
const prevT = orderedTimes[i];
const nextT = orderedTimes[i + 1];
if (nextT - time > time - prevT) {
return UserPivots.get(prevT);
}
return UserPivots.get(nextT);
}

View File

@ -0,0 +1,27 @@
<template>
<div
v-for="(pivot, index) in UserPivots"
:key="pivot[1].id"
>
<UserPivot
v-show="pivot[1].show"
:color="UserPivotColor"
:label="pivot[1].label"
:x="pivot[1].x"
:time="pivot[1].time"
/>
</div>
</template>
<script setup>
/**
* @description 该模块为创建新的信标采用的视图可以看 makePivot 这个函数
*/
import { defineComponent } from 'vue';
import { UserPivotColor, UserPivots } from './pivot-view';
import UserPivot from './user-pivot.vue';
defineComponent({ name: 'pivot-view' });
</script>

View File

@ -1,48 +1,65 @@
<template>
<div v-show="StaticCursor.show">
<div class="vertical-cursor-wrapper"
<div v-if="renderPivot">
<div
class="vertical-cursor-wrapper"
:style="cursorStyle"
>
<div class="current-display-cursor-up">
<div class="current-time-value" :style="colorStyle">{{ StaticCursor.label }}</div>
<div class="current-time-value" :style="colorStyle">{{ props.label }}</div>
<div class="cursor-down-arrow" :style="borderStyle"></div>
</div>
<div class="vertical-line" :style="colorStyle"></div>
<div class="current-display-cursor-down">
<div class="current-time-value" :style="colorStyle">{{ StaticCursor.label }}</div>
<div class="current-time-value" :style="colorStyle">{{ props.label }}</div>
<div class="cursor-up-arrow" :style="borderStyle"></div>
</div>
</div>
</div>
</template>
<script setup>
import { globalLookup } from '@/hook/global';
import { computed, defineComponent } from 'vue';
import { StaticCursor } from './cursor';
defineComponent({ name: 'vertical-cursor' });
defineComponent({ name: 'system-pivot' });
const props = defineProps({
color: {
type: String,
default: 'var(--main-color)'
},
label: {
type: String
},
x: {
type: Number
}
});
const colorStyle = {
const colorStyle = computed(() => ({
backgroundColor: props.color
};
}));
const borderStyle = {
const borderStyle = computed(() => ({
borderLeft: '5px solid ' + props.color,
borderTop: '5px solid ' + props.color
};
}));
const boxShift = 25;
const cursorStyle = computed(() => ({
left: StaticCursor.left - boxShift + 'px'
left: props.x - boxShift + 'px'
}));
const renderPivot = computed(() => {
const x = props.x - boxShift;
if (x > 0 && x < (globalLookup.pstate.width || window.innerWidth)) {
return true;
}
return false;
})
</script>
<style scoped>
@ -50,12 +67,11 @@ const cursorStyle = computed(() => ({
position: absolute;
top: 0;
padding: 0;
cursor: crosshair;
z-index: 55;
display: flex;
flex-direction: column;
align-items: center; /* 沿着横轴(水平方向)对齐 */
justify-content: center; /* 沿着纵轴(垂直方向)对齐 */
align-items: center;
justify-content: center;
transform: translateY(var(--toolbar-height));
}
@ -86,6 +102,7 @@ const cursorStyle = computed(() => ({
color: var(--sidebar-item-text);
padding: 5px;
width: fit-content;
font-family: var(--vcd-value-font-family);
white-space: nowrap;
}

View File

@ -0,0 +1,17 @@
import { reactive } from "vue";
import { getNearestUserPivot } from "./pivot-view";
import { globalLookup } from "@/hook/global";
export const pivotcontextmenu = reactive({
show: false,
x: 0,
y: 0
});
export function handleUserContextMenu(event) {
const x = event.x;
pivotcontextmenu.x = event.x;
pivotcontextmenu.y = event.y;
pivotcontextmenu.show = true;
}

View File

@ -0,0 +1,76 @@
<template>
<div
class="user-context-menu"
:style="contextStyle"
>
<div class="menu-container">
<div
class="menu-item-container"
@click="deleteUserPivot()"
>
<span class="menu-item-icon iconfont icon-delete"></span>
<span class="menu-item-name">{{ t('pivot.context.delete') }}</span>
</div>
<div
class="menu-item-container"
@click="displayRelativeAxis()"
>
<span class="menu-item-icon iconfont icon-axis"></span>
<span class="menu-item-name">{{ t('pivot.context.display-axis') }}</span>
</div>
</div>
</div>
</template>
<script setup>
import { computed, defineComponent } from 'vue';
import { MovingPivot } from './cursor';
import { useI18n } from 'vue-i18n';
import { pivotcontextmenu } from './user-context-menu';
import { globalLookup } from '@/hook/global';
import { getNearestUserPivot, orderedTimes, searchOrderedTimeIndex, UserPivots } from './pivot-view';
defineComponent({ name: 'user-context-menu' });
const { t } = useI18n();
const contextStyle = computed(() => ({
top: pivotcontextmenu.y + ' px',
left: pivotcontextmenu.x + 20 + 'px'
}));
function deleteUserPivot() {
const x = pivotcontextmenu.x;
const pstate = globalLookup.pstate;
if (pstate) {
const { xOffset, xScale, tgcd } = pstate;
const currentTime = Math.round((x - xOffset) / xScale) * tgcd;
const pivot = getNearestUserPivot(currentTime);
if (pivot) {
const i = searchOrderedTimeIndex(pivot.time);
orderedTimes.splice(i, 1);
UserPivots.delete(pivot.time);
pivotcontextmenu.show = false;
}
}
}
function displayRelativeAxis() {
}
</script>
<style scoped>
.user-context-menu {
z-index: 200;
border-radius: .5em;
padding: 10px;
position: fixed;
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
background-color: var(--sidebar);
border: solid 1px var(--sidebar-border);
}
</style>

View File

@ -0,0 +1,232 @@
<template>
<div
v-if="renderPivot"
@mouseenter="onEnter()"
@mouseleave="onLeave()"
@contextmenu.prevent="handleUserContextMenu($event)"
class="user-pivot"
>
<div
class="vertical-cursor-wrapper"
:style="cursorStyle"
>
<div class="current-display-cursor-up">
<div class="current-time-value" :style="colorStyle">{{ props.label }}</div>
<div class="cursor-down-arrow" :style="borderStyle"></div>
</div>
<div
class="vertical-line"
:style="colorStyle"
:ref="el => vline.element = el"
@mousedown.stop="onVLineMousedown()"
@mouseup.stop="onVLineMouseup()"
></div>
<div class="current-display-cursor-down">
<div class="current-time-value" :style="colorStyle">{{ props.label }}</div>
<div class="cursor-up-arrow" :style="borderStyle"></div>
</div>
</div>
</div>
</template>
<script setup>
import { globalLookup } from '@/hook/global';
import { eventHandler, registerWheelEvent } from '@/hook/wave-view';
import { computed, defineComponent, ref, onMounted, reactive, nextTick } from 'vue';
import { calcCursorLeft, mousemoveEventPipes, MovingPivot } from './cursor';
import { orderedTimes, searchOrderedTimeIndex, UserPivots } from './pivot-view';
import formatTime from '@/hook/wave-view/format-time';
import { handleUserContextMenu } from './user-context-menu';
defineComponent({ name: 'user-pivot' });
const props = defineProps({
color: {
type: String,
default: 'var(--main-color)'
},
label: {
type: String
},
x: {
type: Number
},
time: {
type: Number
}
});
const colorStyle = computed(() => ({
backgroundColor: props.color
}));
const borderStyle = computed(() => ({
borderLeft: '5px solid ' + props.color,
borderTop: '5px solid ' + props.color
}));
const boxShift = 25;
const cursorStyle = computed(() => ({
left: props.x - boxShift + 'px'
}));
const renderPivot = computed(() => {
const x = props.x - boxShift;
if (x > 0 && x < (globalLookup.pstate.width || window.innerWidth)) {
return true;
}
return false;
})
function onEnter() {
}
function onLeave() {
MovingPivot.show = true;
MovingPivot.currentTakenPivot = undefined;
}
const vline = reactive({
dragEnable: false,
element: undefined,
originTime: 0
});
function onVLineMousedown() {
const div = vline.element;
if (!(div instanceof HTMLElement)) {
return;
}
vline.dragEnable = true;
MovingPivot.dragEnable = true;
vline.originTime = props.time;
const pivot = UserPivots.get(vline.originTime);
if (pivot === undefined) {
return;
}
// orderTimes
const i = searchOrderedTimeIndex(vline.originTime);
if (i !== undefined) {
orderedTimes.splice(i, 1);
}
//
console.log('begin to drag');
mousemoveEventPipes.set(pivot.id, event => {
const x = event.clientX || event.x;
pivot.x = x;
const pstate = globalLookup.pstate;
if (pstate) {
const { xOffset, xScale, tgcd, timescale } = pstate;
const t = Math.round((x - xOffset) / xScale) * tgcd;
pivot.time = t;
pivot.label = formatTime(t, timescale);
}
});
}
function onVLineMouseup() {
const div = vline.element;
if (!(div instanceof HTMLElement)) {
return;
}
const pivot = UserPivots.get(vline.originTime);
if (pivot) {
mousemoveEventPipes.delete(pivot.id);
UserPivots.delete(vline.originTime);
UserPivots.set(pivot.time, pivot);
orderedTimes.push(pivot.time);
orderedTimes.sort((a, b) => a < b);
}
vline.originTime = 0;
vline.dragEnable = false;
MovingPivot.currentTakenPivot = pivot;
MovingPivot.show = false;
MovingPivot.dragEnable = false;
}
</script>
<style scoped>
.user-pivot {
cursor: pointer;
}
.vertical-cursor-wrapper {
position: absolute;
top: 0;
padding: 0;
z-index: 55;
display: flex;
flex-direction: column;
align-items: center; /* 沿着横轴(水平方向)对齐 */
justify-content: center; /* 沿着纵轴(垂直方向)对齐 */
transform: translateY(var(--toolbar-height));
}
.current-display-cursor-up {
height: var(--time-scale-height);
width: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-display-cursor-down {
height: var(--time-scale-height);
width: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-time-value {
border-radius: .4em;
color: var(--sidebar-item-text);
padding: 5px;
width: fit-content;
font-family: var(--vcd-value-font-family);
white-space: nowrap;
}
.cursor-down-arrow {
transform: rotate(225deg);
position: absolute;
width: 10px;
height: 10px;
/* 25px - 10 * 2 / \sqrt{2} */
left: 17.93px;
bottom: 0;
}
.vertical-line {
cursor: ew-resize;
width: 2px;
height: calc(100vh - 2 * var(--time-scale-height) - 60px);
}
.cursor-up-arrow {
transform: rotate(45deg);
position: absolute;
width: 10px;
height: 10px;
left: 17.93px;
top: 0;
}
</style>

View File

@ -1,135 +0,0 @@
<template>
<div class="time-scale-up-wrapper">
</div>
<div class="time-scale-wrapper" :style="`left: ${currentX}px; width: ${300}px;`">
<div class="current-display-cursor-up">
<div class="time-scale-value">{{ currentX }} {{ timeScaleManage.unit }}</div>
<div class="time-scale-down"></div>
</div>
<div class="time-scale-line"></div>
<div class="current-display-cursor-down">
<div class="time-scale-value">{{ currentX }} {{ timeScaleManage.unit }}</div>
<div class="time-scale-up"></div>
</div>
</div>
<div class="time-scale-down-wrapper">
</div>
</template>
<script>
import { onMounted, reactive } from 'vue';
import { globalLookup, emitter } from '@/hook/global';
import { wheelScale } from '@/hook/wheel';
export default {
name: 'time-scale',
setup() {
// see --sidebar-width
const left = 280 + 20;
// see --right-nav-width
const right = document.body.clientWidth - 60;
const timeScaleManage = reactive({
scale: 1,
unit: 'ns'
});
//
emitter.on('meta-ready', () => {
});
// document.addEventListener('wheel', event => {
// if ((event.wheelDelta && event.ctrlKey) || event.detail) {
// event.preventDefault();
// }
// if (event.ctrlKey) {
// // console.log(event.wheelDelta);
// wheelScale(event);
// } else if (event.shiftKey) {
// // console.log(event.wheelDelta);
// console.log('deltaX', event.deltaX);
// console.log('deltaY', event.deltaY);
// }
// }, { capture: false, passive: false });
const currentX = 300;
return {
currentX,
timeScaleManage
}
}
}
</script>
<style>
.time-scale-up-wrapper {
position: absolute;
top: 0;
left: calc(var(--sidebar-width) + 20px);
width: calc(100vw - var(--sidebar-width) - var(--right-nav-width) - 20px);
height: var(--time-scale-height);
background-color: var(--sidebar);
z-index: 50;
cursor: crosshair;
}
.time-scale-down-wrapper {
position: absolute;
bottom: 0;
left: 0;
width: calc(100vw - var(--right-nav-width));
height: var(--time-scale-height);
background-color: var(--sidebar);
z-index: 50;
cursor: crosshair;
}
.time-scale-down {
position: absolute;
bottom: 0;
width: 1px;
height: 10px;
border: 1px solid var(--scrollbar-hover);
background-color: var(--scrollbar-hover);
}
.time-scale-up {
position: absolute;
top: 0;
width: 1px;
height: 10px;
border: 1px solid var(--scrollbar-hover);
background-color: var(--scrollbar-hover);
}
.time-scale-wrapper {
position: absolute;
height: 100vh;
padding: 0;
top: 0;
cursor: crosshair;
z-index: 55;
display: flex;
flex-direction: column;
align-items: center;
/* 沿着横轴(水平方向)对齐 */
justify-content: center;
/* 沿着纵轴(垂直方向)对齐 */
}
.time-scale-line {
width: 1px;
background-color: var(--scrollbar-hover);
height: calc(100vh - 2 * var(--time-scale-height));
}
</style>

View File

@ -10,6 +10,7 @@
name="language-setting"
class="language-setting"
v-model="locale"
@change="onlanguagechange"
>
<el-option
v-for="option in languageSetting.options"
@ -19,6 +20,22 @@
</el-option>
</el-select>
</div>
<el-dialog
v-model="languageDialogShow"
class="language-dialog"
:title="t('tips')"
width="500"
>
<span>{{ t('setting.language.change-dialog') }}</span>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="confirmLanguageDialog()">
{{ t('confirm') }}
</el-button>
</div>
</template>
</el-dialog>
</div>
<br>
<!-- <div class="setting-option">
@ -98,14 +115,14 @@
<span class="option-title" style="width: 100px;">{{ t('wavecolor') }}</span>
<div style="width: 120px">
<el-select
v-model="wavecolor.currentOptionIndex"
collapse-tags
collapse-tags-tooltip
placeholder="Select"
>
<el-option v-for="option in wavecolor.options" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
v-model="wavecolor.currentOptionIndex"
collapse-tags
collapse-tags-tooltip
placeholder="Select"
>
<el-option v-for="option in wavecolor.options" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</div>
<div style="height: 20px; width: 20px;"></div>
<el-color-picker
@ -115,6 +132,30 @@
size="large"
/>
</div>
<br>
<div class="setting-option">
<span class="option-title" style="width: 100px;">{{ t('setting.appearance.pivot-color') }}</span>
<div style="width: 120px">
<el-select
v-model="pivotColor.currentOptionIndex"
collapse-tags
collapse-tags-tooltip
placeholder="Select"
>
<el-option v-for="option in pivotColor.options" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</div>
<div style="height: 20px; width: 20px;"></div>
<el-color-picker
v-model="pivotColor.colors[pivotColor.currentOptionIndex]"
show-alpha
:predefine="predefinedColors"
size="large"
/>
</div>
</div>
<hr>
@ -159,13 +200,14 @@
</template>
<script setup>
import { reactive, defineComponent, watch } from 'vue';
import { reactive, defineComponent, watch, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { globalSetting, globalLookup } from '@/hook/global';
import { debounceWrapper, scopes, variables, predefinedColors, webglColor2rgba, rgba2WebglColor } from '@/hook/utils';
import { gl_Colors } from '@/hook/wave-view/render-utils';
import HelpIcon from '../help-icon.vue';
import { UserPivotColor } from '../pivot/pivot-view';
import { MovingPivot, SystemPivot } from '../pivot/cursor';
defineComponent({ name: "dide-setting" });
@ -180,6 +222,16 @@ watch(
}
);
const languageDialogShow = ref(false);
function confirmLanguageDialog() {
languageDialogShow.value = false;
}
function onlanguagechange() {
languageDialogShow.value = true;
}
const wavecolor = reactive({
options: [
{
@ -210,10 +262,31 @@ const wavecolor = reactive({
reset() {}
});
watch(() => wavecolor.currentOptionIndex, () => {
console.log(wavecolor.currentOptionIndex);
const pivotColor = reactive({
options: [
{
value: 0,
label: t('setting.appearance.moving-pivot')
},
{
value: 1,
label: t('setting.appearance.user-pivot')
},
{
value: 2,
label: t('setting.appearance.system-pivot')
}
],
currentOptionIndex: 0,
colors: [
MovingPivot.color,
UserPivotColor.value,
SystemPivot.color
]
});
//
watch(() => wavecolor.colors, () => {
const colorString = wavecolor.colors[wavecolor.currentOptionIndex];
@ -231,6 +304,23 @@ watch(() => wavecolor.colors, () => {
}
}, { deep: true });
//
watch(() => pivotColor.colors, () => {
const colorString = pivotColor.colors[pivotColor.currentOptionIndex];
switch (pivotColor.currentOptionIndex) {
case 0:
MovingPivot.color = colorString;
break;
case 1:
UserPivotColor.value = colorString;
break;
case 2:
SystemPivot.color = colorString;
break;
default:
break;
}
}, { deep: true });
const languageSetting = reactive({
options: [
@ -252,6 +342,9 @@ function modifySignalTrackHeight() {
}
const safeModifySignalTrackHeight = debounceWrapper(modifySignalTrackHeight, 200);
</script>
<style>
@ -313,4 +406,14 @@ const safeModifySignalTrackHeight = debounceWrapper(modifySignalTrackHeight, 200
transition: var(--animation-3s);
-webkit-transition: var(--animation-3s);
}
.language-dialog {
color: var(--sidebar-item-text);
background-color: var(--sidebar) !important;
}
.el-dialog__title {
color: var(--sidebar-item-text) !important;
}
</style>

View File

@ -220,8 +220,4 @@ function makeSignalIconClass(signal) {
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
}
.el-color-picker__panel button {
display: none;
}
</style>

View File

@ -1,7 +1,8 @@
import { globalLookup } from "@/hook/global";
import { calcCursorLeft, StaticCursor } from "../render/cursor";
import { calcCursorLeft, SystemPivot } from "../pivot/cursor";
import { sidebarSelectedWires } from "@/hook/sidebar-select-wire";
import { updateWireCurrentValue } from "@/hook/utils";
import { createPivot } from "../pivot/pivot-view";
/**
@ -11,8 +12,8 @@ import { updateWireCurrentValue } from "@/hook/utils";
* @param {Pstate} pstate
*/
export function moveto(time, leftMargin, pstate) {
StaticCursor.show = true;
StaticCursor.currentTime = time;
SystemPivot.show = true;
SystemPivot.currentTime = time;
const { width, xOffset, xScale, tgcd, timescale } = pstate;
let nextOffsetX = leftMargin - (time / tgcd) * xScale;
@ -26,8 +27,8 @@ export function moveto(time, leftMargin, pstate) {
pstate.xOffset = nextOffsetX;
// 改变静态信标
StaticCursor.updateLeft();
StaticCursor.updateLabel(pstate.timescale);
SystemPivot.updateLeft();
SystemPivot.updateLabel(pstate.timescale);
// 更新所有轨道的数字
updateWireCurrentValue(time);
@ -76,7 +77,7 @@ export function toPrevChange() {
if (wave.length === 1) {
toBegin();
} else {
const currentT = StaticCursor.currentTime;
const currentT = SystemPivot.currentTime;
let pivot = bisearch(wave, currentT);
const pwave = wave[pivot];
@ -109,7 +110,7 @@ export function toNextChange() {
if (wave.length === 1) {
toEnd();
} else {
const currentT = StaticCursor.currentTime;
const currentT = SystemPivot.currentTime;
let pivot = bisearch(wave, currentT);
let targetT;
if (pivot === wave.length - 1) {
@ -135,10 +136,10 @@ const locationManager = {
};
export function makeLocation() {
const currentT = StaticCursor.currentTime;
export function makePivot() {
const currentT = SystemPivot.currentTime;
// 在 currentT 创建新的信标
createPivot(currentT);
}
/**

View File

@ -64,6 +64,8 @@
>
<div
class="item iconfont icon-add-line"
:class="{ 'disable' : !SystemPivot.show }"
@click="makePivot()"
/>
</el-tooltip>
@ -73,8 +75,9 @@
<script setup>
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { toBegin, toEnd, toNextChange, toPrevChange } from './cursor-location';
import { makePivot, toBegin, toEnd, toNextChange, toPrevChange } from './cursor-location';
import { globalLookup } from '@/hook/global';
import { SystemPivot } from '../pivot/cursor';
defineComponent({ name: 'cursor-location' });

View File

@ -1 +0,0 @@

View File

@ -13,7 +13,7 @@ import { globalLookup } from "@/hook/global";
import { sidebarSelectedWires } from "@/hook/sidebar-select-wire";
import { reactive, ref } from "vue";
import { bisearch, moveto } from "./cursor-location";
import { StaticCursor } from "../render/cursor";
import { SystemPivot } from "../pivot/cursor";
import { FormatValueRender, JSValueRender } from "@/hook/wave-view/toolbar/renderFormat";
import { ElMessage } from "element-plus";
import i18n from '@/i18n/index';
@ -49,9 +49,9 @@ function toPrevValue() {
// TODO: 空间换时间,提升性能
const prevTimes = [];
const currentT = StaticCursor.currentTime;
// StaticCursor.updateLabel(currentT);
// StaticCursor.updateLeft();
const currentT = SystemPivot.currentTime;
// SystemPivot.updateLabel(currentT);
// SystemPivot.updateLeft();
const searchContent = toolbarValueSearch.content.trim();
@ -93,9 +93,9 @@ function toPrevValue() {
function toNextValue() {
const prevTimes = [];
const currentT = StaticCursor.currentTime;
// StaticCursor.updateLabel(currentT);
// StaticCursor.updateLeft();
const currentT = SystemPivot.currentTime;
// SystemPivot.updateLabel(currentT);
// SystemPivot.updateLeft();
const searchContent = toolbarValueSearch.content.trim();

View File

@ -47,6 +47,14 @@ export const sidebarSelectedWires = {
clear() {
this.lastLink = undefined;
for (const link of globalLookup.sidebarSelectedWireLinks) {
globalLookup.setRenderOption(link, 'highlight', 0);
}
if (globalLookup.sidebarSelectedWireLinks.size > 0) {
globalLookup.render({ type: 'wave-only' });
}
globalLookup.sidebarSelectedWireLinks.clear();
this.togglePipe();
}

View File

@ -1,7 +1,6 @@
import { globalLookup } from '../global';
import WebGL2WaveRender from './render-wave';
import renderCursor from './render-cursor';
import genResizeHandler from './gen-resize-handler';
import mTree from './mount-tree';
@ -15,29 +14,6 @@ const setTime = (pstate, str) => {
}
};
const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
// 这是上面展示当前 time 的圆角矩形框框的宽度
const xmargin = 160;
// 这是上面展示当前 time 的圆角矩形内的字体大小
const fontHeight = 20;
const fontWidth = fontHeight / 2;
const sidebarWidth = pstate.sidebarWidth;
const handler = event => {
const x = pstate.xCursor = event.clientX;
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 });
document.addEventListener('mousemove', handler);
};
const getFullView = desc => {
// console.log(desc);
if (desc.waveql) {
@ -100,7 +76,6 @@ class DomContainer {
const elementInfos = this.elementInfos;
for (const elName of Reflect.ownKeys(elementInfos)) {
const elementInfo = elementInfos[elName];
}
}
}
@ -201,7 +176,6 @@ const domContainer = (obj) => {
resizeObserver.observe(document.body);
resizeHandler(elo.container.clientWidth, elo.container.clientHeight);
mouseMoveHandler(elo.cursor, elo.container, pstate, deso.render);
deso.render();
}
};

View File

@ -1,59 +0,0 @@
import genSVG from 'onml/gen-svg.js';
import stringify from 'onml/stringify.js';
import formatTime from './format-time';
import { globalLookup } from '../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;
const body = [
// vertical line
['line', {
class: 'wd-cursor-line',
x1: xmargin + 0.5,
x2: xmargin + 0.5,
y1: 0,
y2: height
}],
// top time label
['rect', {
class: 'wd-cursor-time',
x: xmargin - lWidth / 2,
y: 0,
rx: 9,
ry: 9,
width: lWidth,
height: fontHeight * 1.25
}],
['text', {
class: 'wd-cursor-time',
x: xmargin,
y: fontHeight
}, label],
// bottom time label
['rect', {
class: 'wd-cursor-time',
x: xmargin - lWidth / 2,
y: height - fontHeight * 1.25,
rx: 9,
ry: 9,
width: lWidth,
height: fontHeight * 1.25
}],
['text', {
class: 'wd-cursor-time',
x: xmargin,
y: height - fontHeight * .25
}, label]
];
return stringify(genSVG(2 * xmargin, height).concat(body));
};
export default renderCursor;

View File

@ -3,7 +3,7 @@ import { globalLookup, 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';
import { updateAllPivots } from '@/components/pivot/pivot-view';
/**
* @description
@ -404,7 +404,7 @@ class WebGL2WaveRender {
}
// 更新静态 cursor
StaticCursor.updateLeft();
updateAllPivots();
function linearAnimation(delta, oldVal, newVal) {
return (1 - delta) * oldVal + delta * newVal;

View File

@ -9,12 +9,15 @@
* @param {WheelEvent} event
*/
function windowsWheel(element, pstate, deso, eventHandler, event) {
if (pstate === undefined) {
pstate = deso.pstate;
}
const { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
? 'scaleUp'
: ((deltaY > 0) ? 'scaleDown' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
@ -50,6 +53,9 @@ function windowsWheel(element, pstate, deso, eventHandler, event) {
* @param {WheelEvent} event
*/
function macOsWheel(element, pstate, deso, eventHandler, event) {
if (pstate === undefined) {
pstate = deso.pstate;
}
let { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
@ -90,6 +96,9 @@ function macOsWheel(element, pstate, deso, eventHandler, event) {
* @param {WheelEvent} event
*/
function linuxWheel(element, pstate, deso, eventHandler, event) {
if (pstate === undefined) {
pstate = deso.pstate;
}
const { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)

View File

@ -100,6 +100,19 @@
"toolbar.search.value.already-to-tail": "already to tail",
"toolbar.search.value.searching": "searching",
"pivot.context.delete": "delete pivot",
"pivot.context.display-axis": "create relative axis",
"setting.appearance.pivot-color": "pivot color",
"setting.appearance.moving-pivot": "moving pivot",
"setting.appearance.user-pivot": "user pivot",
"setting.appearance.system-pivot": "system pivot",
"setting.language.change-dialog": "You've switched language to English, we recommend you reload Wave Viewer",
"confirm": "confirm",
"cancel": "cancel",
"tips": "Tips",
"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>."
}

View File

@ -98,6 +98,19 @@
"toolbar.search.value.already-to-tail": "已经到结尾了",
"toolbar.search.value.searching": "搜索中",
"pivot.context.delete": "删除信标",
"pivot.context.display-axis": "创建相对坐标轴",
"setting.appearance.pivot-color": "信标颜色",
"setting.appearance.moving-pivot": "移动信标",
"setting.appearance.user-pivot": "用户信标",
"setting.appearance.system-pivot": "系统信标",
"setting.language.change-dialog": "您已经更换语言为简体中文,我们建议您重启 Wave Viewer",
"confirm": "确定",
"cancel": "取消",
"tips": "提示",
"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>。"
}