Rename to PhotonVision

This commit is contained in:
Matt
2020-06-27 14:58:03 -07:00
parent b28d0e046e
commit bdbd6b9d18
394 changed files with 1656 additions and 979 deletions

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>