mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-29 02:21:41 +00:00
Rename to PhotonVision
This commit is contained in:
53
photon-client/src/components/common/cv-icon.vue
Normal file
53
photon-client/src/components/common/cv-icon.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-tooltip
|
||||
:right="right"
|
||||
:bottom="!right"
|
||||
nudge-right="10"
|
||||
>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
:class="hoverClass"
|
||||
:color="color"
|
||||
@click="handleClick"
|
||||
v-on="on"
|
||||
>
|
||||
{{ text }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>{{ tooltip }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Icon',
|
||||
props: ['color', 'tooltip', 'text', 'right', 'hover'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
hoverClass: {
|
||||
get() {
|
||||
if (this.hover !== undefined) {
|
||||
return "hover";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('click');
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hover:hover {
|
||||
color: white !important;
|
||||
}
|
||||
</style>
|
||||
33
photon-client/src/components/common/cv-image.vue
Normal file
33
photon-client/src/components/common/cv-image.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<img
|
||||
id="CameraStream"
|
||||
:style="styleObject"
|
||||
:src="address"
|
||||
crossorigin="Anonymous"
|
||||
alt=""
|
||||
@click="e => $emit('click', e)"
|
||||
>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CvImage",
|
||||
props: ['address', 'scale'],
|
||||
data: () => {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
styleObject: {
|
||||
get() {
|
||||
return {
|
||||
width: `${this.scale}%`,
|
||||
height: `${this.scale}%`,
|
||||
display: 'block',
|
||||
margin: 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
53
photon-client/src/components/common/cv-input.vue
Normal file
53
photon-client/src/components/common/cv-input.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="3">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col :cols="9">
|
||||
<v-text-field
|
||||
v-model="localValue"
|
||||
dark
|
||||
dense
|
||||
:disabled="disabled"
|
||||
:error-messages="errorMessage"
|
||||
@keydown="handleKeyboard"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
s
|
||||
<script>
|
||||
export default {
|
||||
name: 'Input',
|
||||
props: ['name', 'value', 'disabled', 'errorMessage'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleKeyboard(event) {
|
||||
if (event.key === "Enter") {
|
||||
this.$emit("Enter");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
48
photon-client/src/components/common/cv-number-input.vue
Normal file
48
photon-client/src/components/common/cv-number-input.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="2">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="localValue"
|
||||
dark
|
||||
class="mt-0 pt-0"
|
||||
hide-details
|
||||
single-line
|
||||
type="number"
|
||||
style="width: 70px"
|
||||
:step="step"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NumberInput',
|
||||
props: ['name', 'value', 'step'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', parseFloat(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
42
photon-client/src/components/common/cv-radio.vue
Normal file
42
photon-client/src/components/common/cv-radio.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-radio-group
|
||||
v-model="localValue"
|
||||
row
|
||||
dark
|
||||
:mandatory="true"
|
||||
>
|
||||
<v-radio
|
||||
v-for="(name,index) in list"
|
||||
:key="index"
|
||||
color="#4baf62"
|
||||
:label="name"
|
||||
:value="index"
|
||||
/>
|
||||
</v-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Radio',
|
||||
props: ['value', 'list'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
107
photon-client/src/components/common/cv-range-slider.vue
Normal file
107
photon-client/src/components/common/cv-range-slider.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="2">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col :cols="10">
|
||||
<v-range-slider
|
||||
:value="localValue"
|
||||
:max="max"
|
||||
:min="min"
|
||||
hide-details
|
||||
class="align-center"
|
||||
dark
|
||||
color="#4baf62"
|
||||
:step="step"
|
||||
@input="handleInput"
|
||||
@mousedown="$emit('rollback', localValue)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-text-field
|
||||
dark
|
||||
:value="localValue[0]"
|
||||
:max="max"
|
||||
:min="min"
|
||||
class="mt-0 pt-0"
|
||||
hide-details
|
||||
single-line
|
||||
type="number"
|
||||
style="width: 50px"
|
||||
:step="step"
|
||||
@input="handleChange"
|
||||
@focus="prependFocused = true"
|
||||
@blur="prependFocused = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:append>
|
||||
<v-text-field
|
||||
dark
|
||||
:value="localValue[1]"
|
||||
:max="max"
|
||||
:min="min"
|
||||
class="mt-0 pt-0"
|
||||
hide-details
|
||||
single-line
|
||||
type="number"
|
||||
style="width: 50px"
|
||||
:step="step"
|
||||
@input="handleChange"
|
||||
@focus="appendFocused = true"
|
||||
@blur="appendFocused = false"
|
||||
/>
|
||||
</template>
|
||||
</v-range-slider>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RangeSlider',
|
||||
props: ['name', 'min', 'max', 'value', 'step'],
|
||||
data() {
|
||||
return {
|
||||
prependFocused: false,
|
||||
appendFocused: false
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(val) {
|
||||
let i = 0;
|
||||
if (this.prependFocused === false && this.appendFocused === true) {
|
||||
i = 1;
|
||||
}
|
||||
if (this.prependFocused || this.appendFocused) {
|
||||
this.$set(this.localValue, i, val);
|
||||
this.$emit('rollback', this.localValue)
|
||||
}
|
||||
},
|
||||
handleInput(val) {
|
||||
if (!this.prependFocused || !this.appendFocused) {
|
||||
this.localValue = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
58
photon-client/src/components/common/cv-select.vue
Normal file
58
photon-client/src/components/common/cv-select.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="3">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col :cols="9">
|
||||
<v-select
|
||||
v-model="localValue"
|
||||
:items="indexList"
|
||||
item-text="name"
|
||||
item-value="index"
|
||||
dark
|
||||
color="#4baf62"
|
||||
item-color="green"
|
||||
:disabled="disabled"
|
||||
@change="$emit('rollback', localValue)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Select',
|
||||
props: ['list', 'name', 'value', 'disabled'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value)
|
||||
}
|
||||
},
|
||||
indexList() {
|
||||
let list = [];
|
||||
for (let i = 0; i < this.list.length; i++) {
|
||||
list.push({
|
||||
name: this.list[i],
|
||||
index: i
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
92
photon-client/src/components/common/cv-slider.vue
Normal file
92
photon-client/src/components/common/cv-slider.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="2">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col :cols="10">
|
||||
<v-slider
|
||||
:value="localValue"
|
||||
dark
|
||||
class="align-center"
|
||||
:max="max"
|
||||
:min="min"
|
||||
hide-details
|
||||
color="#4baf62"
|
||||
:step="step"
|
||||
@start="isClicked = true"
|
||||
@end="isClicked = false"
|
||||
@change="handleclick"
|
||||
@input="handleInput"
|
||||
@mousedown="$emit('rollback', localValue)"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<v-text-field
|
||||
dark
|
||||
:max="max"
|
||||
:min="min"
|
||||
:value="localValue"
|
||||
class="mt-0 pt-0"
|
||||
hide-details
|
||||
single-line
|
||||
type="number"
|
||||
style="width: 50px"
|
||||
:step="step"
|
||||
@input="handleChange"
|
||||
@focus="isFocused = true"
|
||||
@blur="isFocused = false"
|
||||
/>
|
||||
</template>
|
||||
</v-slider>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Slider',
|
||||
props: ['min', 'max', 'name', 'value', 'step'],
|
||||
data() {
|
||||
return {
|
||||
isFocused: false,
|
||||
isClicked: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(val) {
|
||||
if (this.isFocused) {
|
||||
this.localValue = parseFloat(val);
|
||||
this.$emit('rollback', this.localValue)
|
||||
}
|
||||
},
|
||||
handleInput(val) {
|
||||
if (!this.isFocused && this.isClicked) {
|
||||
this.localValue = val;
|
||||
}
|
||||
},
|
||||
handleclick(val) {
|
||||
if (!this.isFocused) {
|
||||
this.localValue = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
45
photon-client/src/components/common/cv-switch.vue
Normal file
45
photon-client/src/components/common/cv-switch.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="2">
|
||||
<span>{{ name }}</span>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-switch
|
||||
v-model="localValue"
|
||||
dark
|
||||
:disabled="disabled"
|
||||
color="#4baf62"
|
||||
@change="$emit('rollback', localValue)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CVSwitch',
|
||||
props: ['name', 'value', 'disabled'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
187
photon-client/src/components/pipeline/3D/MiniMap.vue
Normal file
187
photon-client/src/components/pipeline/3D/MiniMap.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
style="width: 400px;"
|
||||
align="center"
|
||||
>
|
||||
<canvas
|
||||
id="canvasId"
|
||||
width="800"
|
||||
height="800"
|
||||
/>
|
||||
</v-row>
|
||||
<v-row
|
||||
style="width: 400px;"
|
||||
align="center"
|
||||
>
|
||||
<v-simple-table
|
||||
style="text-align: center;background-color: transparent; display: block;margin: auto"
|
||||
dense
|
||||
dark
|
||||
>
|
||||
<template v-slot:default>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
Target
|
||||
</th>
|
||||
<th class="text-center">
|
||||
X
|
||||
</th>
|
||||
<th class="text-center">
|
||||
Y
|
||||
</th>
|
||||
<th class="text-center">
|
||||
Angle
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(target, index) in targets"
|
||||
:key="index"
|
||||
>
|
||||
<td>{{ index }}</td>
|
||||
<td>{{ target.pose.translation.x.toFixed(2) }}</td>
|
||||
<td>{{ target.pose.translation.y.toFixed(2) }}</td>
|
||||
<td>{{ target.pose.rotation.radians.toFixed(2) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MiniMap",
|
||||
props: {
|
||||
targets: Array,
|
||||
horizontalFOV: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ctx: undefined,
|
||||
canvas: undefined,
|
||||
x: 0,
|
||||
y: 0,
|
||||
targetWidth: 40,
|
||||
targetHeight: 6
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hLen: {
|
||||
get() {
|
||||
return Math.tan(this.horizontalFOV / 2 * Math.PI / 180) * 150;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
targets: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.draw();
|
||||
}
|
||||
},
|
||||
horizontalFOV() {
|
||||
this.draw();
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
const canvas = document.getElementById("canvasId"); // getting the canvas element
|
||||
const ctx = canvas.getContext("2d"); // getting the canvas context
|
||||
this.canvas = canvas; // setting the canvas as a vue variable
|
||||
this.ctx = ctx; // setting the canvas context as a vue variable
|
||||
this.grad = this.ctx.createLinearGradient(400, 800, 400, 600);
|
||||
this.grad.addColorStop(0, "rgb(119,119,119)");
|
||||
this.grad.addColorStop(0.05, "rgba(14,92,22,0.96)");
|
||||
this.grad.addColorStop(0.8, 'rgba(43,43,43,0.48)');
|
||||
|
||||
// setting canvas context values for drawing
|
||||
|
||||
|
||||
this.ctx.font = "26px Arial";
|
||||
this.ctx.strokeStyle = "whitesmoke";
|
||||
this.ctx.lineWidth = 2;
|
||||
|
||||
this.$nextTick(function () {
|
||||
this.drawPlayer();
|
||||
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
draw() {
|
||||
this.clearBoard();
|
||||
this.drawPlayer();
|
||||
for (let index in this.targets) {
|
||||
this.drawTarget(index, this.targets[index].pose);
|
||||
}
|
||||
},
|
||||
drawTarget(index, target) {
|
||||
// first save the untranslated/unrotated context
|
||||
let x = 800 - (160 * target.translation.x); // getting meters as pixels
|
||||
let y = 400 - (160 * target.translation.y);
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
// move the rotation point to the center of the rect
|
||||
this.ctx.translate(y + this.targetWidth / 2, x + this.targetHeight / 2); // wpi lib makes x forward and back and y left to right
|
||||
// rotate the rect
|
||||
this.ctx.rotate(target.rotation.radians * -1);
|
||||
|
||||
// draw the rect on the transformed context
|
||||
// Note: after transforming [0,0] is visually [x,y]
|
||||
// so the rect needs to be offset accordingly when drawn
|
||||
this.ctx.rect(-this.targetWidth / 2, -this.targetHeight / 2, this.targetWidth, this.targetHeight);
|
||||
|
||||
this.ctx.fillStyle = "#01a209";
|
||||
this.ctx.fill();
|
||||
|
||||
// restore the context to its untranslated/unrotated state
|
||||
this.ctx.restore();
|
||||
this.ctx.fillStyle = "whitesmoke";
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(y + this.targetWidth / 2, x + this.targetHeight / 2, 3, 0, 2 * Math.PI, true);
|
||||
this.ctx.fill();
|
||||
this.ctx.fillText(index, y - 30, x - 5);
|
||||
|
||||
},
|
||||
drawPlayer() {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(400, 820);
|
||||
this.ctx.lineTo(400 + this.hLen, 650);
|
||||
this.ctx.lineTo(400 - this.hLen, 650);
|
||||
this.ctx.closePath();
|
||||
this.ctx.fillStyle = this.grad;
|
||||
this.ctx.fill();
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(400, 820);
|
||||
this.ctx.lineTo(400 + this.hLen, 650);
|
||||
this.ctx.stroke();
|
||||
this.ctx.moveTo(400, 820);
|
||||
this.ctx.lineTo(400 - this.hLen, 650);
|
||||
this.ctx.stroke();
|
||||
|
||||
},
|
||||
clearBoard() {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // clearing the canvas
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#canvasId {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background-color: #2b2b2b;
|
||||
border-radius: 5px;
|
||||
border: 2px solid grey;
|
||||
box-shadow: 0 0 5px 1px;
|
||||
}
|
||||
|
||||
th {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row align="center">
|
||||
<v-col
|
||||
:cols="3"
|
||||
class=""
|
||||
>
|
||||
<div style="padding-left:30px">
|
||||
<CVselect
|
||||
v-if="isCameraNameEdit === false"
|
||||
v-model="currentCameraIndex"
|
||||
name="Camera"
|
||||
:list="$store.getters.cameraList"
|
||||
@input="handleInput('currentCamera',currentCameraIndex)"
|
||||
/>
|
||||
<CVinput
|
||||
v-else
|
||||
v-model="newCameraName"
|
||||
name="Camera"
|
||||
:error-message="checkCameraName"
|
||||
@Enter="saveCameraNameChange"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col :cols="1">
|
||||
<CVicon
|
||||
v-if="isCameraNameEdit === false"
|
||||
color="#c5c5c5"
|
||||
:hover="true"
|
||||
text="edit"
|
||||
tooltip="Edit camera name"
|
||||
@click="toCameraNameChange"
|
||||
/>
|
||||
<div v-else>
|
||||
<CVicon
|
||||
color="#c5c5c5"
|
||||
style="display: inline-block;"
|
||||
:hover="true"
|
||||
text="save"
|
||||
tooltip="Save Camera Name"
|
||||
@click="saveCameraNameChange"
|
||||
/>
|
||||
<CVicon
|
||||
color="error"
|
||||
style="display: inline-block;"
|
||||
:hover="true"
|
||||
text="close"
|
||||
tooltip="Discard Changes"
|
||||
@click="discardCameraNameChange"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col
|
||||
:cols="3"
|
||||
class=""
|
||||
>
|
||||
<CVselect
|
||||
v-model="currentPipelineIndex"
|
||||
name="Pipeline"
|
||||
:list="['Driver Mode'].concat($store.getters.pipelineList)"
|
||||
@input="handleInput('currentPipeline',currentPipelineIndex - 1)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
v-if="currentPipelineIndex !== 0"
|
||||
:cols="1"
|
||||
class=""
|
||||
md="3"
|
||||
>
|
||||
<v-menu
|
||||
offset-y
|
||||
dark
|
||||
auto
|
||||
>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
color="white"
|
||||
v-on="on"
|
||||
>
|
||||
menu
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item @click="toPipelineNameChange">
|
||||
<v-list-item-title>
|
||||
<CVicon
|
||||
color="#c5c5c5"
|
||||
:right="true"
|
||||
text="edit"
|
||||
tooltip="Edit pipeline name"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="toCreatePipeline">
|
||||
<v-list-item-title>
|
||||
<CVicon
|
||||
color="#c5c5c5"
|
||||
:right="true"
|
||||
text="add"
|
||||
tooltip="Add new pipeline"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="deleteCurrentPipeline">
|
||||
<v-list-item-title>
|
||||
<CVicon
|
||||
color="red darken-2"
|
||||
:right="true"
|
||||
text="delete"
|
||||
tooltip="Delete pipeline"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="openDuplicateDialog">
|
||||
<v-list-item-title>
|
||||
<CVicon
|
||||
color="#c5c5c5"
|
||||
:right="true"
|
||||
text="mdi-content-copy"
|
||||
tooltip="Duplicate pipeline"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
|
||||
<v-btn
|
||||
style="position: absolute; top:5px;right: 0;"
|
||||
tile
|
||||
color="#4baf62"
|
||||
@click="handleInput('command','save')"
|
||||
>
|
||||
<v-icon>save</v-icon>
|
||||
Save
|
||||
</v-btn>
|
||||
</v-row>
|
||||
<!--pipeline duplicate dialog-->
|
||||
<v-dialog
|
||||
v-model="duplicateDialog"
|
||||
dark
|
||||
width="500"
|
||||
height="357"
|
||||
>
|
||||
<v-card dark>
|
||||
<v-card-title
|
||||
class="headline"
|
||||
primary-title
|
||||
>
|
||||
Duplicate Pipeline
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<CVselect
|
||||
v-model="pipelineDuplicate.pipeline"
|
||||
name="Pipeline"
|
||||
:list="$store.getters.pipelineList"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-if="$store.getters.cameraList.length > 1"
|
||||
v-model="anotherCamera"
|
||||
dark
|
||||
:label="'To another camera'"
|
||||
/>
|
||||
<CVselect
|
||||
v-if="anotherCamera === true"
|
||||
v-model="pipelineDuplicate.camera"
|
||||
name="Camera"
|
||||
:list="$store.getters.cameraList"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="#4baf62"
|
||||
@click="duplicatePipeline"
|
||||
>
|
||||
Duplicate
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
@click="closeDuplicateDialog"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<!--pipeline naming dialog-->
|
||||
<v-dialog
|
||||
v-model="namingDialog"
|
||||
dark
|
||||
width="500"
|
||||
height="357"
|
||||
>
|
||||
<v-card dark>
|
||||
<v-card-title
|
||||
class="headline"
|
||||
primary-title
|
||||
>
|
||||
Pipeline Name
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<CVinput
|
||||
v-model="newPipelineName"
|
||||
name="Pipeline"
|
||||
:error-message="checkPipelineName"
|
||||
@Enter="savePipelineNameChange"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="#4baf62"
|
||||
:disabled="checkPipelineName !==''"
|
||||
@click="savePipelineNameChange"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
@click="discardPipelineNameChange"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVicon from '../common/cv-icon'
|
||||
import CVselect from '../common/cv-select'
|
||||
import CVinput from '../common/cv-input'
|
||||
|
||||
export default {
|
||||
name: "CameraAndPipelineSelect",
|
||||
components: {
|
||||
CVicon,
|
||||
CVselect,
|
||||
CVinput
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
re: RegExp("^[A-Za-z0-9 \\-)(]*[A-Za-z0-9][A-Za-z0-9 \\-)(.]*$"),
|
||||
isCameraNameEdit: false,
|
||||
newCameraName: "",
|
||||
cameraNameError: "",
|
||||
isPipelineNameEdit: false,
|
||||
namingDialog: false,
|
||||
newPipelineName: "",
|
||||
duplicateDialog: false,
|
||||
anotherCamera: false,
|
||||
pipelineDuplicate: {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checkCameraName() {
|
||||
if (this.newCameraName !== this.$store.getters.cameraList[this.currentCameraIndex]) {
|
||||
if (this.re.test(this.newCameraName)) {
|
||||
for (let cam in this.cameraList) {
|
||||
if (this.cameraList.hasOwnProperty(cam)) {
|
||||
if (this.newCameraName === this.cameraList[cam]) {
|
||||
return "Camera by that name already Exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "Camera name can only contain letters, numbers and spaces"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
checkPipelineName() {
|
||||
if (this.newPipelineName !== this.$store.getters.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
|
||||
if (this.re.test(this.newPipelineName)) {
|
||||
for (let pipe in this.$store.getters.pipelineList) {
|
||||
if (this.$store.getters.pipelineList.hasOwnProperty(pipe)) {
|
||||
if (this.newPipelineName === this.$store.getters.pipelineList[pipe]) {
|
||||
return "A pipeline with this name already exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "Pipeline name can only contain letters, numbers, and spaces"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
currentCameraIndex: {
|
||||
get() {
|
||||
return this.$store.getters.currentCameraIndex;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('currentPipelineIndex', value - 1);
|
||||
}
|
||||
},
|
||||
currentPipelineIndex: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineIndex + 1;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('currentPipelineIndex', value - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toCameraNameChange() {
|
||||
this.newCameraName = this.$store.getters.cameraList[this.currentCameraIndex];
|
||||
this.isCameraNameEdit = true;
|
||||
},
|
||||
saveCameraNameChange() {
|
||||
if (this.checkCameraName === "") {
|
||||
this.handleInput("changeCameraName", this.newCameraName);
|
||||
this.discardCameraNameChange();
|
||||
}
|
||||
},
|
||||
discardCameraNameChange() {
|
||||
this.isCameraNameEdit = false;
|
||||
this.newCameraName = "";
|
||||
},
|
||||
toPipelineNameChange() {
|
||||
this.newPipelineName = this.$store.getters.pipelineList[this.currentPipelineIndex - 1];
|
||||
this.isPipelineNameEdit = true;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
toCreatePipeline() {
|
||||
this.newPipelineName = "New Pipeline";
|
||||
this.isPipelineNameEdit = false;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
openDuplicateDialog() {
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: this.currentPipelineIndex - 1,
|
||||
camera: -1
|
||||
};
|
||||
this.duplicateDialog = true;
|
||||
},
|
||||
deleteCurrentPipeline() {
|
||||
if (this.$store.getters.pipelineList.length > 1) {
|
||||
this.handleInput('command', 'deleteCurrentPipeline');
|
||||
} else {
|
||||
this.snackbar = true;
|
||||
}
|
||||
},
|
||||
savePipelineNameChange() {
|
||||
if (this.checkPipelineName === "") {
|
||||
if (this.isPipelineNameEdit) {
|
||||
this.handleInput("changePipelineName", this.newPipelineName);
|
||||
} else {
|
||||
this.handleInput("addNewPipeline", this.newPipelineName);
|
||||
}
|
||||
this.discardPipelineNameChange();
|
||||
}
|
||||
},
|
||||
duplicatePipeline() {
|
||||
if (!this.anotherCamera) {
|
||||
this.pipelineDuplicate.camera = -1
|
||||
}
|
||||
// this.handleInput("duplicatePipeline", this.pipelineDuplicate);
|
||||
this.axios.post("http://" + this.$address + "/api/vision/duplicate", this.pipelineDuplicate);
|
||||
this.closeDuplicateDialog();
|
||||
},
|
||||
closeDuplicateDialog() {
|
||||
this.duplicateDialog = false;
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
}
|
||||
},
|
||||
discardPipelineNameChange() {
|
||||
this.namingDialog = false;
|
||||
this.isPipelineNameEdit = false;
|
||||
this.newPipelineName = "";
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
align="center"
|
||||
justify="start"
|
||||
>
|
||||
<v-col
|
||||
style="padding-right:0"
|
||||
:cols="3"
|
||||
>
|
||||
<v-btn
|
||||
small
|
||||
color="#4baf62"
|
||||
@click="takePointA"
|
||||
>
|
||||
Take Point A
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
style="margin-left:0"
|
||||
:cols="3"
|
||||
>
|
||||
<v-btn
|
||||
small
|
||||
color="#4baf62"
|
||||
@click="takePointB"
|
||||
>
|
||||
Take Point B
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
small
|
||||
color="yellow darken-3"
|
||||
@click="clearSlope"
|
||||
>
|
||||
Clear All Points
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DualCalibration",
|
||||
props: ['rawPoint'],
|
||||
data() {
|
||||
return {
|
||||
pointA: undefined,
|
||||
pointB: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
takePointA() {
|
||||
this.pointA = this.rawPoint;
|
||||
this.calcSlope();
|
||||
},
|
||||
takePointB() {
|
||||
this.pointB = this.rawPoint;
|
||||
this.calcSlope();
|
||||
},
|
||||
calcSlope() {
|
||||
if (this.pointA !== undefined && this.pointB !== undefined) {
|
||||
let m = (this.pointB[1] - this.pointA[1]) / (this.pointB[0] - this.pointA[0]);
|
||||
let b = this.pointA[1] - (m * this.pointA[0]);
|
||||
if (isNaN(m) === false && isNaN(b) === false) {
|
||||
this.sendSlope(m, b, true);
|
||||
} else {
|
||||
this.$emit('snackbar', "Points are too close");
|
||||
}
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
}
|
||||
},
|
||||
sendSlope(m, b) {
|
||||
this.handleInput('dualTargetCalibrationM', m);
|
||||
this.handleInput('dualTargetCalibrationB', b);
|
||||
this.$emit('update');
|
||||
},
|
||||
clearSlope() {
|
||||
this.sendSlope(1, 0, false);
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row
|
||||
align="center"
|
||||
justify="start"
|
||||
>
|
||||
<v-col
|
||||
style="padding-right:0"
|
||||
:cols="3"
|
||||
>
|
||||
<v-btn
|
||||
small
|
||||
color="#4baf62"
|
||||
@click="takePoint"
|
||||
>
|
||||
Take Point
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
small
|
||||
color="yellow darken-3"
|
||||
@click="clearPoint"
|
||||
>
|
||||
Clear Point
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SingleCalibration",
|
||||
props: ['rawPoint'],
|
||||
methods: {
|
||||
clearPoint() {
|
||||
this.handleInput('point', []);
|
||||
this.$emit('update');
|
||||
},
|
||||
takePoint() {
|
||||
if (this.rawPoint[0] && this.rawPoint[1]) {
|
||||
this.handleInput('point', this.rawPoint);
|
||||
this.$emit('update');
|
||||
} else {
|
||||
this.$emit('snackbar', "No target found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user