|
@@ -1,4 +1,6 @@
|
|
|
<script setup lang="ts">
|
|
|
+import axios from 'axios'
|
|
|
+import { v4 as uuidv4 } from 'uuid';
|
|
|
import Viewer from 'viewerjs'
|
|
|
import 'viewerjs/dist/viewer.css'
|
|
|
|
|
@@ -6,7 +8,6 @@ const positiveDirectionPOptions = ref([{ label: '+', value: '+' }, { label: '-',
|
|
|
const positiveDirectionTOptions = ref([{ label: '+', value: '+' }, { label: '-', value: '-' }])
|
|
|
|
|
|
const system = useSystemStore()
|
|
|
-
|
|
|
const currentBallCamera = ref('')
|
|
|
|
|
|
const addDialogV = ref(false)
|
|
@@ -52,18 +53,109 @@ function imgSrcLoad() {
|
|
|
imgSrcLoading.value = false
|
|
|
}
|
|
|
|
|
|
-interface MarkItemType { id: number, x: number, y: number, color: string }
|
|
|
+interface MarkItemType { id: string, p: number, t: number, x: number, y: number, color: string }
|
|
|
type MarkType = MarkItemType[]
|
|
|
const mark = ref<MarkType>([])
|
|
|
const markClone = ref<MarkType>([])
|
|
|
const currentMark = ref<MarkItemType>({
|
|
|
- id: 0,
|
|
|
+ id: '',
|
|
|
+ p: 0.0,
|
|
|
+ t: 0.0,
|
|
|
x: 0.0,
|
|
|
y: 0.0,
|
|
|
color: '#000',
|
|
|
})
|
|
|
|
|
|
+// opencv ==============start
|
|
|
+async function buildMatrix() {
|
|
|
+ let srcPoints: any = [];
|
|
|
+ let dstPoints: any = [];
|
|
|
+
|
|
|
+ // 遍历点集收集源点和目标点
|
|
|
+ // Object.values(_imgController.p).forEach(v => {
|
|
|
+ // if (v.p !== undefined && v.t !== undefined) {
|
|
|
+ // srcPoints.push([v.x, v.y]);
|
|
|
+ // dstPoints.push([v.p, v.t]);
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ PTXYMap.forEach(function (v: any, key) {
|
|
|
+ if (v.P !== undefined && v.T !== undefined) {
|
|
|
+ srcPoints.push([v.X, v.Y]);
|
|
|
+ dstPoints.push([v.P, v.T]);
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ let matrix = null;
|
|
|
+
|
|
|
+ if (srcPoints.length >= 4) {
|
|
|
+ // 将点集转换为OpenCV矩阵格式
|
|
|
+ const srcMat = pointsToMat(srcPoints);
|
|
|
+ const dstMat = pointsToMat(dstPoints);
|
|
|
+
|
|
|
+ // 计算单应性矩阵
|
|
|
+ matrix = findHomography(srcMat, dstMat);
|
|
|
+ console.log(matrix);
|
|
|
+
|
|
|
+ // 清理临时矩阵(OpenCV.js内存管理)
|
|
|
+ srcMat.delete();
|
|
|
+ dstMat.delete();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用矩阵到所有点
|
|
|
+ PTXYMap.forEach(function (v: any, key) {
|
|
|
+ if (v.P !== undefined && v.T !== undefined) {
|
|
|
+ matrix2Point(v, matrix);
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 将点数组转换为OpenCV矩阵(CV_64FC2类型)
|
|
|
+function pointsToMat(points: any) {
|
|
|
+ const data = new Float64Array(points.length * 2);
|
|
|
+ points.forEach(([x, y]:[any, any], i: number) => {
|
|
|
+ data[i * 2] = x;
|
|
|
+ data[i * 2 + 1] = y;
|
|
|
+ });
|
|
|
+ return window.cv.matFromArray(points.length, 1, window.cv.CV_64FC2, data);
|
|
|
+}
|
|
|
+
|
|
|
+// 计算单应性矩阵
|
|
|
+function findHomography(srcPoints:any, dstPoints:any) {
|
|
|
+ const homography = new window.cv.Mat();
|
|
|
+ // 使用基本参数版本:src, dst, method (0), ransacReprojThreshold (3)
|
|
|
+ window.cv.findHomography(srcPoints, dstPoints, homography, 0, 3);
|
|
|
+ return homography;
|
|
|
+}
|
|
|
+
|
|
|
+// 应用矩阵变换到单个点
|
|
|
+function matrix2Point(v:any, matrix:any) {
|
|
|
+ if (!matrix || matrix.empty()) return;
|
|
|
+
|
|
|
+ // 创建输入矩阵(齐次坐标)
|
|
|
+ const src = window.cv.matFromArray(3, 1, window.cv.CV_64FC1, [v.X, v.Y, 1]);
|
|
|
+ const dst = new window.cv.Mat();
|
|
|
+
|
|
|
+ // 执行矩阵变换
|
|
|
+ window.cv.gemm(matrix, src, 1, null, 0, dst);
|
|
|
+
|
|
|
+ // 转换为笛卡尔坐标
|
|
|
+ const w = dst.data64F[2];
|
|
|
+ if (Math.abs(w) > Number.EPSILON) {
|
|
|
+ v.p_transformed = dst.data64F[0] / w;
|
|
|
+ v.t_transformed = dst.data64F[1] / w;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理临时矩阵
|
|
|
+ src.delete();
|
|
|
+ dst.delete();
|
|
|
+}
|
|
|
+
|
|
|
+// opencv =============end
|
|
|
+
|
|
|
+
|
|
|
function markClick(item: MarkItemType) {
|
|
|
+ console.log(item);
|
|
|
+
|
|
|
currentMark.value = item
|
|
|
colorReset(item.id)
|
|
|
}
|
|
@@ -76,7 +168,7 @@ function calculateOriginalCoords(x: number, y: number, originalWidth: number, or
|
|
|
}
|
|
|
|
|
|
// 重置颜色
|
|
|
-function colorReset(id: number) {
|
|
|
+function colorReset(id: string) {
|
|
|
mark.value.forEach((v, i) => {
|
|
|
v.color = '#000'
|
|
|
markClone.value[i].color = '#000'
|
|
@@ -90,19 +182,22 @@ function colorReset(id: number) {
|
|
|
}
|
|
|
|
|
|
// 删除标记
|
|
|
-function del(id: number) {
|
|
|
+function del(id: string) {
|
|
|
if (!id)
|
|
|
return
|
|
|
mark.value = mark.value.filter(v => v.id !== id)
|
|
|
markClone.value = markClone.value.filter(v => v.id !== id)
|
|
|
- currentMark.value = mark.value[mark.value.length - 1]
|
|
|
+ mark.value.length && (currentMark.value = mark.value[mark.value.length - 1])
|
|
|
+ mark.value.length && colorReset(currentMark.value.id)
|
|
|
}
|
|
|
// 清空标记
|
|
|
function clear() {
|
|
|
mark.value = []
|
|
|
markClone.value = []
|
|
|
currentMark.value = {
|
|
|
- id: 0,
|
|
|
+ id: '',
|
|
|
+ p: 0.0,
|
|
|
+ t: 0.0,
|
|
|
x: 0.0,
|
|
|
y: 0.0,
|
|
|
color: '#000',
|
|
@@ -125,22 +220,25 @@ function markPointOnImageWithMargin(imgElement: HTMLImageElement) {
|
|
|
id: v.id,
|
|
|
x: v.x * (scaledWidth / originalWidth) + left - 12,
|
|
|
y: v.y * (scaledHeight / originalHeight) + top - 12,
|
|
|
+ p: v.p,
|
|
|
+ t: v.t,
|
|
|
color: v.color,
|
|
|
}
|
|
|
})
|
|
|
mark.value.length && colorReset(currentMark.value.id)
|
|
|
}
|
|
|
|
|
|
-let id = 0
|
|
|
function dbClick(e: MouseEvent) {
|
|
|
// 获取鼠标点击位置
|
|
|
const { offsetX, offsetY } = e
|
|
|
const { originalX, originalY } = calculateOriginalCoords(offsetX, offsetY, (imgElement as HTMLImageElement).naturalWidth, (imgElement as HTMLImageElement).naturalHeight, (imgElement as HTMLImageElement).offsetWidth, (imgElement as HTMLImageElement).offsetHeight)
|
|
|
|
|
|
const markV = {
|
|
|
- id: ++id,
|
|
|
+ id: uuidv4(),
|
|
|
x: originalX,
|
|
|
y: originalY,
|
|
|
+ p: 0.0,
|
|
|
+ t: 0.0,
|
|
|
color: '#000',
|
|
|
}
|
|
|
mark.value.push(markV)
|
|
@@ -149,9 +247,92 @@ function dbClick(e: MouseEvent) {
|
|
|
markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
}
|
|
|
|
|
|
-onMounted(() => {
|
|
|
+
|
|
|
+const PTXYMap = new Map<string, { P: number, T: number, X: number, Y: number }>()
|
|
|
+
|
|
|
+
|
|
|
+function getPTXY(id: string, x: number, y: number) {
|
|
|
+ if (!system.globalConfig.serverUrl) {
|
|
|
+ system.msg('请先设置服务器地址')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!currentMark.value.id) {
|
|
|
+ system.msg('请双击创建标记')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!id) {
|
|
|
+ system.msg('请选择球机')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ axios.get(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzGet?CameraId=${id}`).then(res => {
|
|
|
+ PTXYMap.set(currentMark.value.id, { P: res.data.data.P, T: res.data.data.T, X: x, Y: y })
|
|
|
+ mark.value.forEach((v, i) => {
|
|
|
+ if (v.id === currentMark.value.id) {
|
|
|
+ const p = system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, res.data.data.P, system.positiveConfig.P_Direction, "")
|
|
|
+ const t = system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, res.data.data.T, system.positiveConfig.T_Direction, "")
|
|
|
+ v.p = p
|
|
|
+ v.t = t
|
|
|
+ markClone.value[i].p = p
|
|
|
+ markClone.value[i].t = t
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+const loadOld = () => {
|
|
|
+ if (!system.ballCameraInfo) return
|
|
|
+ console.log(system.ballCameraInfo);
|
|
|
+ PTXYMap.clear()
|
|
|
+ const ps: any = system.ballCameraInfo.Matrix.PointSet
|
|
|
+ console.log(ps);
|
|
|
+
|
|
|
+ for (const key in ps) {
|
|
|
+ PTXYMap.set(key, {
|
|
|
+ P: ps[key].P,
|
|
|
+ T: ps[key].T,
|
|
|
+ X: ps[key].X,
|
|
|
+ Y: ps[key].Y,
|
|
|
+ })
|
|
|
+ mark.value.push({
|
|
|
+ id: key,
|
|
|
+ x: ps[key].X,
|
|
|
+ y: ps[key].Y,
|
|
|
+ p: ps[key].P,
|
|
|
+ t: ps[key].T,
|
|
|
+ color: '#000',
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ markClone.value = JSON.parse(JSON.stringify(mark.value))
|
|
|
+ colorReset(mark.value[mark.value.length - 1].id)
|
|
|
+ currentMark.value = mark.value[mark.value.length - 1]
|
|
|
+ markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
+}
|
|
|
+
|
|
|
+const jumpToPT = (p: number, t: number) => {
|
|
|
+ if (!system.globalConfig.serverUrl) {
|
|
|
+ system.msg('请先设置服务器地址')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const pV = system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, p, system.positiveConfig.P_Direction, "inv")
|
|
|
+ const tV = system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, t, system.positiveConfig.T_Direction, "inv")
|
|
|
+ axios.put(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzSet?CameraId=${currentBallCamera.value}&Action=1`, { P: pV, T: tV, Z: 0 }).then(res => {
|
|
|
+ console.log(res);
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+let viewer: Viewer
|
|
|
+
|
|
|
+watch(() => system.drawer, () => {
|
|
|
+ console.log(viewer)
|
|
|
+})
|
|
|
+
|
|
|
+function initViewer() {
|
|
|
const updloadImg = document.getElementById('uploadImage') as HTMLImageElement
|
|
|
- const _ = new Viewer(updloadImg, {
|
|
|
+ viewer = new Viewer(updloadImg, {
|
|
|
inline: true,
|
|
|
backdrop: true,
|
|
|
navbar: false,
|
|
@@ -184,6 +365,10 @@ onMounted(() => {
|
|
|
markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
},
|
|
|
})
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ initViewer()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
@@ -191,30 +376,21 @@ onMounted(() => {
|
|
|
<v-row>
|
|
|
<v-col class="relative" cols="12" md="6">
|
|
|
<div class="flex-1">
|
|
|
- <v-select
|
|
|
- v-if="system.type?.Baseline === 'BoGuan'"
|
|
|
- v-model="system.currentDevice" :items="system.deviceList" item-title="name" item-value="id"
|
|
|
- label="选择设备"
|
|
|
- @update:model-value="system.getBallCameraList"
|
|
|
- />
|
|
|
- <v-select
|
|
|
- v-model="currentBallCamera" :items="system.ballCameraList" label="球机列表" item-title="name" item-value="id" @update:model-value="system.getBallCameraInfo"
|
|
|
- >
|
|
|
+ <v-select v-if="system.type?.Baseline === 'BoGuan'" v-model="system.currentDevice" :items="system.deviceList"
|
|
|
+ item-title="name" item-value="id" label="选择设备" @update:model-value="system.getBallCameraList" />
|
|
|
+ <v-select v-model="currentBallCamera" :items="system.ballCameraList" label="球机列表" item-title="name"
|
|
|
+ item-value="id" @update:model-value="system.getBallCameraInfo">
|
|
|
<template #append>
|
|
|
<add-ball v-model:dialog="addDialogV">
|
|
|
<template #activator="{ props: activatorProps }">
|
|
|
- <v-btn
|
|
|
- v-tooltip:top="'添加球机'" size="large" rounded="0" variant="flat" icon="mdi-plus-thick"
|
|
|
- v-bind="activatorProps"
|
|
|
- />
|
|
|
+ <v-btn v-tooltip:top="'添加球机'" size="large" rounded="0" variant="flat" icon="mdi-plus-thick"
|
|
|
+ v-bind="activatorProps" />
|
|
|
</template>
|
|
|
</add-ball>
|
|
|
<v-dialog max-width="400">
|
|
|
<template #activator="{ props: activatorProps }">
|
|
|
- <v-btn
|
|
|
- v-tooltip:top="'删除球机'" size="large" rounded="0" variant="flat" icon="mdi-trash-can"
|
|
|
- v-bind="activatorProps"
|
|
|
- />
|
|
|
+ <v-btn v-tooltip:top="'删除球机'" size="large" rounded="0" variant="flat" icon="mdi-trash-can"
|
|
|
+ v-bind="activatorProps" />
|
|
|
</template>
|
|
|
|
|
|
<template #default="{ isActive }">
|
|
@@ -236,47 +412,62 @@ onMounted(() => {
|
|
|
<v-row>
|
|
|
<v-col>
|
|
|
<v-sheet>
|
|
|
- <v-number-input
|
|
|
- v-model="system.positiveConfig.P_Start" control-variant="stacked" :precision="1"
|
|
|
- hide-details="auto" label="P 起始值"
|
|
|
- />
|
|
|
+ <v-number-input v-model="system.positiveConfig.P_Start" control-variant="stacked" :precision="1"
|
|
|
+ hide-details="auto" label="P 起始值" />
|
|
|
+ </v-sheet>
|
|
|
+ </v-col>
|
|
|
+
|
|
|
+ <v-col>
|
|
|
+ <v-sheet>
|
|
|
+ <v-number-input v-model="system.positiveConfig.T_Start" control-variant="stacked" :precision="1"
|
|
|
+ hide-details="auto" label="T 起始值" />
|
|
|
+ </v-sheet>
|
|
|
+ </v-col>
|
|
|
+ </v-row>
|
|
|
+ <v-row>
|
|
|
+ <v-col>
|
|
|
+ <v-sheet>
|
|
|
+ <v-number-input v-model="system.positiveConfig.P_Max" control-variant="stacked" :precision="1"
|
|
|
+ hide-details="auto" label="P 最大值" />
|
|
|
</v-sheet>
|
|
|
</v-col>
|
|
|
|
|
|
<v-col>
|
|
|
<v-sheet>
|
|
|
- <v-number-input
|
|
|
- v-model="system.positiveConfig.T_Start" control-variant="stacked" :precision="1"
|
|
|
- hide-details="auto" label="T 起始值"
|
|
|
- />
|
|
|
+ <v-number-input v-model="system.positiveConfig.T_Max" control-variant="stacked" :precision="1"
|
|
|
+ hide-details="auto" label="T 最大值" />
|
|
|
</v-sheet>
|
|
|
</v-col>
|
|
|
</v-row>
|
|
|
<v-row>
|
|
|
<v-col>
|
|
|
<v-sheet>
|
|
|
- <v-select
|
|
|
- v-model="system.positiveConfig.p" :items="positiveDirectionPOptions" item-title="label"
|
|
|
- item-value="value" label="P 正方向"
|
|
|
- />
|
|
|
+ <v-select v-model="system.positiveConfig.P_Direction" :items="positiveDirectionPOptions"
|
|
|
+ item-title="label" item-value="value" label="P 正方向" />
|
|
|
</v-sheet>
|
|
|
</v-col>
|
|
|
<v-col>
|
|
|
<v-sheet>
|
|
|
- <v-select
|
|
|
- v-model="system.positiveConfig.t" :items="positiveDirectionTOptions" item-title="label"
|
|
|
- item-value="value" label="T 正方向"
|
|
|
- />
|
|
|
+ <v-select v-model="system.positiveConfig.T_Direction" :items="positiveDirectionTOptions"
|
|
|
+ item-title="label" item-value="value" label="T 正方向" />
|
|
|
</v-sheet>
|
|
|
</v-col>
|
|
|
</v-row>
|
|
|
|
|
|
<!-- 视频播放 -->
|
|
|
<div class="p-1">
|
|
|
- <v-card loading title="画面预览" subtitle="当前P0.0,当前T0.0,当前标定点位的个数0" text="">
|
|
|
- <div class="aspect-ratio-video b-1 b-#ccc b-solid bg-black" />
|
|
|
+ <v-card :loading="system.canvasVideoLoading" title="画面预览"
|
|
|
+ :subtitle="`当前P ${system.PT.p},当前T ${system.PT.t},当前标定点位的个数0`" text="">
|
|
|
+ <div class="relative aspect-ratio-video b-1 b-#ccc b-solid bg-black">
|
|
|
+ <v-icon icon="mdi-plus" color="#ef4444" class="absolute top-50% left-50% transform-translate--50%" :style="{
|
|
|
+ textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
|
|
|
+ -1px -1px #fff, 1px 1px #fff, 1px -1px #fff, -1px 1px #fff`,
|
|
|
+ }" />
|
|
|
+ <canvas v-if="system.resetDom" class="canvas-video h-full w-full" />
|
|
|
+ </div>
|
|
|
<v-card-actions>
|
|
|
- <v-btn block variant="tonal">
|
|
|
+ <v-btn :disabled="system.canvasVideoLoading" block variant="tonal"
|
|
|
+ @click="system.playBallCamera(currentBallCamera)">
|
|
|
播放
|
|
|
</v-btn>
|
|
|
</v-card-actions>
|
|
@@ -286,7 +477,9 @@ onMounted(() => {
|
|
|
<div class="grid grid-cols-3 grid-rows-3">
|
|
|
<div v-for="tool in tools" :key="tool.type">
|
|
|
<div v-if="!tool.type" class="invisible" />
|
|
|
- <v-btn v-else width="60" height="40" variant="tonal" color="blue-darken-2">
|
|
|
+ <v-btn v-else width="60" height="40" variant="tonal" color="blue-darken-2"
|
|
|
+ @mousedown="system.ballMove(currentBallCamera, { Speed: step, Direction: tool.value })"
|
|
|
+ @mouseup="system.ballStop(currentBallCamera, { Direction: tool.value })">
|
|
|
{{ tool.icon }}
|
|
|
</v-btn>
|
|
|
</div>
|
|
@@ -294,10 +487,14 @@ onMounted(() => {
|
|
|
<v-divider class="mx-2" vertical opacity="8" />
|
|
|
<div class="flex-1">
|
|
|
<div class="flex justify-around">
|
|
|
- <v-btn variant="tonal" color="blue-darken-2">
|
|
|
+ <v-btn variant="tonal" color="blue-darken-2"
|
|
|
+ @mousedown="system.ballMove(currentBallCamera, { Speed: step, Direction: 9 })"
|
|
|
+ @mouseup="system.ballStop(currentBallCamera, { Direction: 9 })">
|
|
|
🔍+
|
|
|
</v-btn>
|
|
|
- <v-btn variant="tonal" color="blue-darken-2">
|
|
|
+ <v-btn variant="tonal" color="blue-darken-2"
|
|
|
+ @mousedown="system.ballMove(currentBallCamera, { Speed: step, Direction: 10 })"
|
|
|
+ @mouseup="system.ballStop(currentBallCamera, { Direction: 10 })">
|
|
|
🔎-
|
|
|
</v-btn>
|
|
|
</div>
|
|
@@ -308,8 +505,8 @@ onMounted(() => {
|
|
|
云台转动速度:{{ step }}
|
|
|
</div>
|
|
|
<div class="mt-1 text-center">
|
|
|
- <v-btn color="blue">
|
|
|
- 使用旧节点
|
|
|
+ <v-btn color="blue" @click="loadOld">
|
|
|
+ 使用旧的点
|
|
|
</v-btn>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -323,20 +520,18 @@ onMounted(() => {
|
|
|
<v-skeleton-loader v-if="imgSrcLoading" height="100%" type="image" />
|
|
|
<img id="uploadImage" class="w-full" :src="imgSrc" alt="图片预览" @load="imgSrcLoad">
|
|
|
<v-empty-state v-if="!imgSrcLoading && !imgSrc" icon="mdi-image-broken-variant" title="暂无上传图片" />
|
|
|
- <v-icon
|
|
|
- v-for="(item, index) in mark" :key="index" :style="{
|
|
|
- top: `${item.y}px`,
|
|
|
- left: `${item.x}px`,
|
|
|
- textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
|
|
|
+ <v-icon v-for="(item, index) in mark" :key="index" :style="{
|
|
|
+ top: `${item.y}px`,
|
|
|
+ left: `${item.x}px`,
|
|
|
+ textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
|
|
|
-1px -1px #fff, 1px 1px #fff, 1px -1px #fff, -1px 1px #fff`,
|
|
|
- }" :color="item.color" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal"
|
|
|
- @click="markClick(item)"
|
|
|
- />
|
|
|
+ }" :color="item.color" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal"
|
|
|
+ @click="markClick(item)" />
|
|
|
</div>
|
|
|
<div class="flex flex-col items-center">
|
|
|
<v-chip class="ma-2" color="blue" label>
|
|
|
<v-icon icon="mdi-map-marker-radius" start />
|
|
|
- 选中的标记 P: {{ currentMark.x }}, T: {{ currentMark.y }}
|
|
|
+ 选中的标记 P: {{ currentMark.p }}, T: {{ currentMark.t }}
|
|
|
</v-chip>
|
|
|
<v-card min-width="90%">
|
|
|
<v-card-text>
|
|
@@ -348,23 +543,23 @@ onMounted(() => {
|
|
|
</div>
|
|
|
<v-row justify="center">
|
|
|
<v-col>
|
|
|
- <v-btn width="100%">
|
|
|
- 当前PT位置装在PT
|
|
|
+ <v-btn width="100%" @click="getPTXY(currentBallCamera, currentMark.x, currentMark.y)">
|
|
|
+ 当前PT位置装载PT
|
|
|
</v-btn>
|
|
|
</v-col>
|
|
|
|
|
|
<v-col>
|
|
|
<v-btn width="100%">
|
|
|
- 根据映射矩阵装在PT
|
|
|
+ 根据映射矩阵装载PT
|
|
|
</v-btn>
|
|
|
</v-col>
|
|
|
<v-col>
|
|
|
- <v-btn width="100%">
|
|
|
+ <v-btn width="100%" @click="buildMatrix">
|
|
|
生成映射矩阵
|
|
|
</v-btn>
|
|
|
</v-col>
|
|
|
<v-col>
|
|
|
- <v-btn width="100%">
|
|
|
+ <v-btn width="100%" @click="jumpToPT(currentMark.p, currentMark.t)">
|
|
|
跳转到PT
|
|
|
</v-btn>
|
|
|
</v-col>
|