mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-23 01:21:40 +00:00
removed old ui src and moved new one to its location and builded ui for distribution
This commit is contained in:
@@ -1,181 +1,95 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div class="layout">
|
||||
<Layout :style="{minHeight: '100vh'}">
|
||||
<Layout>
|
||||
<Sider id="main-nav" @on-collapse="onCollapse" collapsible :collapsed-width="78" v-model="isCollapsed">
|
||||
<img src="./assets/logo.png" style="width:60px;height:auto;transition: .2s ease;" :class="menuIconClass">
|
||||
<Menu ref="menu" @on-open-change="onOpenChange" :active-name="activeName" :open-names="openedNames" theme="dark" width="auto" :class="menuitemClasses">
|
||||
<Submenu name="/vision">
|
||||
<template slot="title">
|
||||
<Icon type="ios-videocam"/>
|
||||
<span v-if="!isCollapsed">Vision</span>
|
||||
</template>
|
||||
<MenuItem name="/vision/input" to="/vision/input">Input</MenuItem>
|
||||
<MenuItem name="/vision/threshold" to="/vision/threshold">Threshold</MenuItem>
|
||||
<MenuItem name="/vision/contours" to="/vision/contours">Contours</MenuItem>
|
||||
<MenuItem name="/vision/output" to="/vision/output">Output</MenuItem>
|
||||
</Submenu>
|
||||
<Submenu name="/settings">
|
||||
<template slot="title">
|
||||
<Icon type="ios-settings"/>
|
||||
<span v-if="!isCollapsed">Settings</span>
|
||||
</template>
|
||||
<MenuItem name="/settings/system" to="/settings/system">System</MenuItem>
|
||||
<MenuItem name="/settings/camera" to="/settings/camera">Cameras</MenuItem>
|
||||
</Submenu>
|
||||
</Menu>
|
||||
</Sider>
|
||||
<router-view></router-view>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</div>
|
||||
</div>
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar app dense clipped-left dark>
|
||||
<img class="imgClass" src="./assets/logo.png">
|
||||
<v-toolbar-title id="title">Chameleon Vision</v-toolbar-title>
|
||||
<div class="flex-grow-1"></div>
|
||||
<v-toolbar-items>
|
||||
<v-tabs dark height="48" slider-color="#4baf62">
|
||||
<v-tab to="Vision">Vision</v-tab>
|
||||
<v-tab to="Settings">Settings</v-tab>
|
||||
</v-tabs>
|
||||
</v-toolbar-items>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-container fluid fill-height>
|
||||
<v-layout>
|
||||
<v-flex>
|
||||
<router-view></router-view>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
import chselect from './components/ch-select.vue'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components:{
|
||||
chselect,
|
||||
components: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isCollapsed: false,
|
||||
openedNames: ["/" + this.$route.path.split("/")[1]],
|
||||
activeName: this.$route.path
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onOpenChange(data) {
|
||||
this.isCollapsed = false;
|
||||
console.info('App currentRoute:', this.$router.currentRoute);
|
||||
},
|
||||
onCollapse() {
|
||||
if (this.isCollapsed) {
|
||||
this.openedNames = [''];
|
||||
} else {
|
||||
this.activeName = this.$refs.menu.currentActiveName;
|
||||
this.openedNames = ["/" + this.activeName.split("/")[1]];
|
||||
}
|
||||
this.$nextTick(function() {
|
||||
this.$refs.menu.updateOpened();
|
||||
this.$refs.menu.updateActiveName();
|
||||
})
|
||||
},
|
||||
isEquale(message,prop){
|
||||
if(typeof (this.$store.state[prop]) == "object"){
|
||||
for(var i = this.$store.state[prop].length; i--;) {
|
||||
if(this.$store.state[prop][i] !== message[prop][i]){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else{
|
||||
if(this.$store.state[prop] != message[prop]){
|
||||
return false
|
||||
},
|
||||
methods:{
|
||||
handleMessage(key,value){
|
||||
if(this.$store.state.hasOwnProperty(key)){
|
||||
this.$store.commit(key,value);
|
||||
} else if(this.$store.state.pipeline.hasOwnProperty(key)){
|
||||
this.$store.commit('setPipeValues',{[key]:value});
|
||||
}
|
||||
else{
|
||||
switch(key){
|
||||
|
||||
default:{
|
||||
console.log(key + " : " + value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
menuitemClasses: function () {
|
||||
return [
|
||||
'menu-item',
|
||||
this.isCollapsed ? 'collapsed-menu' : ''
|
||||
]
|
||||
},
|
||||
menuIconClass(){
|
||||
return this.isCollapsed ? '' :'icon'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$options.sockets.onmessage = (data) => {
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
|
||||
}),
|
||||
created(){
|
||||
this.$options.sockets.onmessage = async (data) =>{
|
||||
try{
|
||||
let message = JSON.parse(data.data);
|
||||
for (var prop in message){
|
||||
if(message.hasOwnProperty(prop)){
|
||||
this.$store.state[prop] = message[prop];
|
||||
// console.log(message);
|
||||
|
||||
var buffer = await data.data.arrayBuffer();
|
||||
let message = this.$msgPack.decode(buffer);
|
||||
for(let prop in message){
|
||||
if(message.hasOwnProperty(prop)){
|
||||
this.handleMessage(prop, message[prop]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch{
|
||||
console.log("error" + data.data)
|
||||
}
|
||||
} // console writes recived data
|
||||
}
|
||||
catch(error){
|
||||
console.error('error: ' + data.data+ " , "+ error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
/* color: #2c3e50; */
|
||||
html{
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
|
||||
#camera, #main-layout {
|
||||
background-color: #272e35;
|
||||
.imgClass{
|
||||
width: auto;
|
||||
height: 45px;
|
||||
vertical-align: middle;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#main-nav {
|
||||
box-shadow: 2px 0px 10px black;
|
||||
.tabClass{
|
||||
color: #4baf62;
|
||||
}
|
||||
|
||||
#main-header {
|
||||
box-shadow: 0px 2px 10px black;
|
||||
text-align: left
|
||||
.container{
|
||||
background-color: #212121;
|
||||
padding: 0!important;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
padding: 30px
|
||||
#title{
|
||||
color:#4baf62;
|
||||
}
|
||||
|
||||
.layout{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
span{
|
||||
color: white;
|
||||
}
|
||||
.menu-item span{
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 69px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: bottom;
|
||||
transition: width .2s ease .2s;
|
||||
}
|
||||
.menu-item i{
|
||||
transform: translateX(0px);
|
||||
transition: font-size .2s ease, transform .2s ease;
|
||||
vertical-align: middle;
|
||||
font-size: 16px;
|
||||
}
|
||||
.collapsed-menu span{
|
||||
width: 0px;
|
||||
transition: width .2s ease;
|
||||
}
|
||||
.collapsed-menu .ivu-icon-ios-arrow-down{
|
||||
display: none;
|
||||
}
|
||||
.collapsed-menu i{
|
||||
transform: translateX(5px);
|
||||
transition: font-size .2s ease .2s, transform .2s ease .2s;
|
||||
vertical-align: middle;
|
||||
font-size: 22px;
|
||||
}
|
||||
.icon{
|
||||
width: 100px !important;
|
||||
transition: .2s ease;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<div id="cameraTab" class="spacing">
|
||||
<chselect title="select camera" :list="cameraList" Xkey="curr_camera"></chselect>
|
||||
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Resolution:</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<i-select v-model="resolution" size="small" >
|
||||
<i-option v-for="(item,index) in resolutionList" :value="index" :key="index">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Diagonal FOV:</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<InputNumber :min="0" v-model="FOV" size="small"></InputNumber>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Button type="primary" size="small" class="buttonClass spacing" v-on:click="socketSendAll">Save settings to current camera</Button>
|
||||
<h4 class="spacing">Please Restart the computer Manually after saving all cameras</h4>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chselect from './ch-select.vue'
|
||||
import chIndexSelect from './ch-IndexSelect.vue'
|
||||
|
||||
export default {
|
||||
name: 'cameraTab',
|
||||
components: {
|
||||
chselect,
|
||||
chIndexSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
socketSendAll: function(){
|
||||
this.$socket.sendObj({'resolution':this.resolution});
|
||||
this.$socket.sendObj({'FOV':this.FOV});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cameraList:{
|
||||
get:function(){
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
resolutionList:{
|
||||
get:function(){
|
||||
return this.$store.state.resolutionList;
|
||||
}
|
||||
},
|
||||
resolution:{
|
||||
get: function(){
|
||||
return this.$store.state.resolution;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('resolution',value);
|
||||
}
|
||||
},
|
||||
FOV:{
|
||||
get: function(){
|
||||
return this.$store.state.FOV;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('FOV',value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title{
|
||||
text-align:left;
|
||||
color: aliceblue
|
||||
}
|
||||
.spacing{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.buttonClass{
|
||||
display: flex;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<template>
|
||||
<div id="InputTab">
|
||||
<chslider class="spacing" title="exposure" Xkey="exposure"></chslider>
|
||||
<chslider class="spacing" title="Brightness" Xkey="brightness"></chslider>
|
||||
<chselect class="spacing" title="Orientation" Xkey="orientation" :list="['Normal','Inverted']"></chselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chslider from './ch-slider.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chIndexSelect from './ch-IndexSelect.vue'
|
||||
export default {
|
||||
name: 'InputTab',
|
||||
data () {
|
||||
return{
|
||||
}
|
||||
},
|
||||
components: {
|
||||
chslider,
|
||||
chselect,
|
||||
chIndexSelect
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
computed:{
|
||||
resolutionList:{
|
||||
get:function(){
|
||||
return this.$store.state.resolutionList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +0,0 @@
|
||||
<template>
|
||||
<Layout id="main-layout">
|
||||
<Content id="main-content">
|
||||
<router-view></router-view>
|
||||
</Content>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Settings',
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,163 +0,0 @@
|
||||
<template>
|
||||
<div id="SystemTab">
|
||||
<div>
|
||||
<row type="flex" justify="start" align="middle" :gutter="10" >
|
||||
<Col span="6">
|
||||
<h4>Team Number:</h4>
|
||||
</Col>
|
||||
<col span="4">
|
||||
<InputNumber :min="0" v-model="team_number" size="small"></InputNumber>
|
||||
</col>
|
||||
</row>
|
||||
</div>
|
||||
<Divider class="divdiv" orientation="left">Networking</Divider>
|
||||
<div>
|
||||
<RadioGroup v-model="connection_type" style="display: flex;">
|
||||
<Radio label="DHCP"></Radio>
|
||||
<Radio label="Static"></Radio>
|
||||
</RadioGroup>
|
||||
<div class="ipSettings">
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>IP:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="ip" size="small" :disabled="isConnection"></Input>
|
||||
</Col>
|
||||
</row>
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Netmask:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="netmask" size="small" :disabled="isConnection"></Input>
|
||||
</Col>
|
||||
</row>
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Gateway:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="gateway" size="small" :disabled="isConnection"></Input>
|
||||
</Col>
|
||||
</row>
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Hostname:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="hostname" size="small">
|
||||
<span slot="append">.local</span>
|
||||
</Input>
|
||||
</Col>
|
||||
</row>
|
||||
</div>
|
||||
<Divider class="divdiv" orientation="left"></Divider>
|
||||
<row type="flex" justify="start" align="middle" style="margin-top:20px">
|
||||
<Button type="primary" size="small" v-on:click="socketSendAll">Save Changes and Restart</Button>
|
||||
</row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chInputNumber from './ch-inputNumber.vue'
|
||||
import chSelect from './ch-select.vue'
|
||||
export default {
|
||||
name: 'SystemTab',
|
||||
data() {
|
||||
return {
|
||||
lan:0
|
||||
}
|
||||
},
|
||||
components:{
|
||||
chInputNumber,
|
||||
chSelect
|
||||
},
|
||||
methods: {
|
||||
socketSendAll: function(){
|
||||
this.$socket.sendObj(
|
||||
{'change_general_settings_values':{
|
||||
'team_number':this.team_number,
|
||||
'connection_type':this.connection_type,
|
||||
'ip':this.ip,
|
||||
'netmask':this.netmask,
|
||||
'gateway':this.gateway,
|
||||
'hostname':this.hostname}});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
team_number:{
|
||||
get: function(){
|
||||
return this.$store.state.team_number;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('team_number',value);
|
||||
}
|
||||
},
|
||||
connection_type:{
|
||||
get: function(){
|
||||
return this.$store.state.connection_type;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('connection_type',value);
|
||||
}
|
||||
},
|
||||
ip:{
|
||||
get: function(){
|
||||
return this.$store.state.ip;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('ip',value);
|
||||
}
|
||||
},
|
||||
netmask:{
|
||||
get: function(){
|
||||
return this.$store.state.netmask;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('netmask',value);
|
||||
}
|
||||
},
|
||||
gateway:{
|
||||
get: function(){
|
||||
return this.$store.state.gateway;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('gateway',value);
|
||||
}
|
||||
},
|
||||
hostname:{
|
||||
get: function(){
|
||||
return this.$store.state.hostname;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('hostname',value);
|
||||
}
|
||||
},
|
||||
isConnection: function(){
|
||||
if(this.connection_type == "DHCP"){
|
||||
return true
|
||||
} else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.ivu-divider-inner-text{
|
||||
color: aliceblue
|
||||
}
|
||||
.ivu-radio-group {
|
||||
display: flex;
|
||||
text-align: left;
|
||||
color: aliceblue;
|
||||
}
|
||||
.ipSettings{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.spacing{
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,36 +0,0 @@
|
||||
<template>
|
||||
<div id="Threshold">
|
||||
<chrange class="spacing" title="Hue" Xkey="hue" :maximum="180"></chrange>
|
||||
<chrange class="spacing" title="Saturation" Xkey="saturation" :maximum="255"></chrange>
|
||||
<chrange class="spacing" title="Value" Xkey="value" :maximum="255"></chrange>
|
||||
<chswitch class="spacing" title="Erode" Xkey="erode"></chswitch>
|
||||
<chswitch class="spacing" title="Dilate" Xkey="dilate"></chswitch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chrange from './ch-range.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chswitch from './ch-switch.vue'
|
||||
export default {
|
||||
name: 'Threshold',
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components:{
|
||||
chrange,
|
||||
chselect,
|
||||
chswitch
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<Layout id="main-layout">
|
||||
<Header id="main-header">
|
||||
<Row type="flex" justify="start" align="middle" :gutter="10">
|
||||
<Col span="12">
|
||||
<chselect title="camera" :list="cameraList" Xkey="curr_camera"></chselect>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<chselect title="pipeline" :list="pipelineList" Xkey="curr_pipeline"></chselect>
|
||||
</Col>
|
||||
</Row>
|
||||
</Header>
|
||||
<Content id="main-content">
|
||||
<row type="flex" justify="start" align="top" :gutter="5" >
|
||||
<Col span="12">
|
||||
<router-view></router-view>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<Tabs :animated="false" v-model="isBinary" @on-click="handleImage">
|
||||
<TabPane label="Normal"></TabPane>
|
||||
<TabPane label="Threshold"></TabPane>
|
||||
</Tabs>
|
||||
<img class="imageSize" :src="steamAdress" style="">
|
||||
<h4 class="pointText">{{point}}</h4>
|
||||
</Col>
|
||||
</Col>
|
||||
</row>
|
||||
</Content>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue"
|
||||
import chselect from './ch-select.vue'
|
||||
|
||||
export default {
|
||||
name: 'Vision',
|
||||
components: {
|
||||
chselect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleImage() {
|
||||
this.$socket.sendObj({"is_binary":this.isBinary});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cameraList:{
|
||||
get:function(){
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
pipelineList:{
|
||||
get: function(){
|
||||
return this.$store.state.pipelineList;
|
||||
}
|
||||
},
|
||||
steamAdress: {
|
||||
get: function(){
|
||||
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
|
||||
}
|
||||
},
|
||||
isBinary: {
|
||||
get: function(){
|
||||
return this.$store.state.is_binary;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('is_binary',value)
|
||||
}
|
||||
},
|
||||
point:{
|
||||
get:function(){
|
||||
let p = this.$store.state.point;
|
||||
return ("Pitch: " + parseFloat(p['pitch']).toFixed(2) + " Yaw: " + parseFloat(p['yaw']).toFixed(2) + " FPS: " + parseFloat(p['fps']).toFixed(2))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.ivu-tabs-nav .ivu-tabs-tab:hover{
|
||||
color: #0cdfc3 !important;
|
||||
}
|
||||
.imageSize{
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
.pointText{
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" >
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<i-select v-model="value" size="small" @on-change="handleInput">
|
||||
<i-option v-for="(item,index) in list" :value="index" :key="index">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-select',
|
||||
props:{
|
||||
title: String,
|
||||
list: Array,
|
||||
Xkey: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
color: #e6ebf1;
|
||||
text-align: left;
|
||||
}
|
||||
/* .ivu-select-selection{
|
||||
background-color: #2c3e50 !important;
|
||||
} */
|
||||
/* .ivu-select-selected-value{
|
||||
color: #fff !important;
|
||||
} */
|
||||
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<div id="InputNumber">
|
||||
<row type="flex" justify="start" align="middle" :gutter="10" >
|
||||
<Col span="6">
|
||||
<h4>{{title}}</h4>
|
||||
</Col>
|
||||
<col span="4">
|
||||
<InputNumber :min="0" v-model="value" size="small" @on-change="handleInput"></InputNumber>
|
||||
</col>
|
||||
</row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-InputNumber',
|
||||
props:{
|
||||
title:String,
|
||||
Xkey:String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
<template>
|
||||
<row type="flex" justify="start" align="middle" :gutter="1">
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: left">
|
||||
<InputNumber style="align-self: flex-start;" v-model="value[0]" size="small" :step="steps" ></InputNumber>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Slider range v-model="value" @on-input="handleInput" :step="steps" :max="maximum"></Slider>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: right">
|
||||
<InputNumber style="align-self: flex-end;" v-model="value[1]" size="small" :step="steps" :max="maximum"></InputNumber>
|
||||
</Col>
|
||||
</row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-range',
|
||||
props:{
|
||||
title:String,
|
||||
Xkey:String,
|
||||
steps:Number,
|
||||
maximum:Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get:function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set:function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
color: #e6ebf1;
|
||||
}
|
||||
/* .ivu-input-number-input{
|
||||
background-color: #2c3e50 !important;
|
||||
color: #fff !important;
|
||||
} */
|
||||
</style>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" >
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<i-select v-model="value" size="small" @on-change="handleInput" :disabled="isDisabled" @on-query-change="$emit('change')">
|
||||
<i-option v-for="item in list" :value="item" :key="item">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-select',
|
||||
props:{
|
||||
title: String,
|
||||
list: Array,
|
||||
Xkey: String,
|
||||
isDisabled:Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
color: #e6ebf1;
|
||||
text-align: left;
|
||||
}
|
||||
/* .ivu-select-selection{
|
||||
background-color: #2c3e50 !important;
|
||||
} */
|
||||
/* .ivu-select-selected-value{
|
||||
color: #fff !important;
|
||||
} */
|
||||
|
||||
</style>
|
||||
@@ -1,54 +0,0 @@
|
||||
|
||||
<template>
|
||||
<row type="flex" justify="start" align="middle" :gutter="1">
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: left">
|
||||
<InputNumber style="align-self: flex-start;" v-model="value" size="small"></InputNumber>
|
||||
</Col>
|
||||
<Col span="14">
|
||||
<Slider v-model="value" @on-input="handleInput"></Slider>
|
||||
</Col>
|
||||
</row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-slider',
|
||||
props:{
|
||||
title:String,
|
||||
Xkey:String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
color: #e6ebf1;
|
||||
}
|
||||
/* .ivu-input-number-input{
|
||||
background-color: #2c3e50 !important;
|
||||
color: #fff !important;
|
||||
} */
|
||||
</style>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<template>
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" >
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: left">
|
||||
<i-switch v-model="value" @on-change="handleInput" style="align-self: flex-start"></i-switch>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-switch',
|
||||
props:{
|
||||
title: String,
|
||||
Xkey: String
|
||||
},
|
||||
methods:{
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<div id="ContourTab">
|
||||
<chrange class="spacing" title="Area" Xkey="area"></chrange>
|
||||
<chrange class="spacing" title="Ratio (W/H)" Xkey="ratio" :steps="0.1"></chrange>
|
||||
<chrange class="spacing" title="Extent" Xkey="extent"></chrange>
|
||||
<chselect class="spacing" title="Target Group" Xkey="target_group"
|
||||
:list="['Single','Dual','Triple','Quadruple','Quintuple']"></chselect>
|
||||
<chselect class="spacing" title="Target Intersection" Xkey="target_intersection"
|
||||
:list="['Up','Down','Left','Right','Parallel']" :isDisabled="isSingle"></chselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chslider from './ch-slider.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chrange from './ch-range.vue'
|
||||
|
||||
export default {
|
||||
name: 'ContourTab',
|
||||
components:{
|
||||
chslider,
|
||||
chselect,
|
||||
chrange
|
||||
},
|
||||
methods:{
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSingle:function(){
|
||||
if (this.$store.state.target_group == 'Single'){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
41
chameleon-client/src/components/cv-icon.vue
Normal file
41
chameleon-client/src/components/cv-icon.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-tooltip :right="right" :bottom="!right" nudge-right="10">
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon :class="hoverClass" @click="handleClick" v-on="on" :color="color">{{text}}</v-icon>
|
||||
</template>
|
||||
<span>{{tooltip}}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Icon',
|
||||
props:['color','tooltip','text','right','hover'],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handleClick(){
|
||||
this.$emit('click');
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
hoverClass(){
|
||||
if(this.hover !== undefined){
|
||||
return "hover";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hover:hover{
|
||||
color: white !important;
|
||||
}
|
||||
</style>
|
||||
45
chameleon-client/src/components/cv-input.vue
Normal file
45
chameleon-client/src/components/cv-input.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row dense align="center">
|
||||
<v-col :cols="3">
|
||||
<span>{{name}}</span>
|
||||
</v-col>
|
||||
<v-col :cols="9">
|
||||
<v-text-field @keydown="handleKeyboard" dark v-model="localValue" dense :disabled="disabled" :error-messages="errorMessage"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Input',
|
||||
props:['name','value','disabled','errorMessage'],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handleKeyboard(event){
|
||||
if(event.key == "Enter"){
|
||||
this.$emit("Enter");
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
localValue:{
|
||||
get(){
|
||||
return this.value;
|
||||
},
|
||||
set(value){
|
||||
this.$emit('input',value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
38
chameleon-client/src/components/cv-number-input.vue
Normal file
38
chameleon-client/src/components/cv-number-input.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row dense align="center">
|
||||
<v-col :cols="2">
|
||||
<span>{{name}}</span>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field dark v-model="localValue" class="mt-0 pt-0" hide-details single-line type="number" style="width: 70px"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NumberInput',
|
||||
props:['name','value'],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
localValue:{
|
||||
get(){
|
||||
return this.value;
|
||||
},
|
||||
set(value){
|
||||
this.$emit('input', parseInt(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
33
chameleon-client/src/components/cv-radio.vue
Normal file
33
chameleon-client/src/components/cv-radio.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-radio-group row v-model="localValue" dark :mandatory="true">
|
||||
<v-radio color="#4baf62" v-for="(name,index) in list" :label="name" v-bind:key="index" :value="index"></v-radio>
|
||||
</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>
|
||||
64
chameleon-client/src/components/cv-range-slider.vue
Normal file
64
chameleon-client/src/components/cv-range-slider.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<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" @input="handleInput" :max="max" :min="min" hide-details class="align-center" dark color="#4baf62" :step="step">
|
||||
<template v-slot:prepend>
|
||||
<v-text-field :value="localValue[0]" :max="max" :min="min" @input="handleChange" @focus="prependFocused = true" @blur="prependFocused = false" class="mt-0 pt-0" hide-details single-line type="number" style="width: 50px" :step="step"></v-text-field>
|
||||
</template>
|
||||
|
||||
<template v-slot:append>
|
||||
<v-text-field :value="localValue[1]" :max="max" :min="min" @input="handleChange" @focus="appendFocused = true" @blur="appendFocused = false" class="mt-0 pt-0" hide-details single-line type="number" style="width: 50px" :step="step"></v-text-field>
|
||||
</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
|
||||
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
},
|
||||
handleInput(val){
|
||||
if(!this.prependFocused || !this.appendFocused){
|
||||
this.localValue = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
localValue:{
|
||||
get(){
|
||||
return this.value;
|
||||
},
|
||||
set(value){
|
||||
this.$emit('input',value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
45
chameleon-client/src/components/cv-select.vue
Normal file
45
chameleon-client/src/components/cv-select.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<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"></v-select>
|
||||
</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>
|
||||
54
chameleon-client/src/components/cv-slider.vue
Normal file
54
chameleon-client/src/components/cv-slider.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row dense align="center">
|
||||
<v-col :cols="2">
|
||||
<span>{{name}}</span>
|
||||
</v-col>
|
||||
<v-col :cols="10">
|
||||
<v-slider :value="localValue" @input="handleInput" dark class="align-center" :max="max" :min="min" hide-details color="#4baf62" :step="step">
|
||||
<template v-slot:append>
|
||||
<v-text-field dark :max="max" :min="min" :value="localValue" @input="handleChange" @focus="isFocused = true" @blur="isFocused = false" class="mt-0 pt-0" hide-details single-line type="number" style="width: 50px" :step="step"></v-text-field>
|
||||
</template>
|
||||
</v-slider>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Slider',
|
||||
props:['min','max','name','value','step'],
|
||||
data() {
|
||||
return {
|
||||
isFocused:false
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handleChange(val){
|
||||
if(this.isFocused){
|
||||
this.localValue = parseFloat(val);
|
||||
}
|
||||
},
|
||||
handleInput(val){
|
||||
if(!this.isFocused){
|
||||
this.localValue = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
localValue:{
|
||||
get(){
|
||||
return this.value;
|
||||
},
|
||||
set(value){
|
||||
this.$emit('input',value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
38
chameleon-client/src/components/cv-switch.vue
Normal file
38
chameleon-client/src/components/cv-switch.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row dense align="center">
|
||||
<v-col :cols="2">
|
||||
<span>{{name}}</span>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-switch v-model="localValue" color="#4baf62"></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CVSwitch',
|
||||
props:['name','value'],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
localValue:{
|
||||
get(){
|
||||
return this.value;
|
||||
},
|
||||
set(value){
|
||||
this.$emit('input',value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,88 +0,0 @@
|
||||
<template>
|
||||
<div id="OutputTab">
|
||||
<chselect class="spacing" title="Sort Mode" Xkey="sort_mode"
|
||||
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']"></chselect>
|
||||
<Row type="flex" justify="start" align="middle" class="spacing" :gutter="10">
|
||||
<col>
|
||||
<Button type="primary" size="small" v-on:click="takePointA">Take Point A</Button>
|
||||
</col>
|
||||
<col style="margin-left:10px">
|
||||
<Button type="primary" size="small" v-on:click="takePointB">Take Point B</Button>
|
||||
</col>
|
||||
</Row>
|
||||
<Row type="flex" align="middle" class="spacing" :gutter="10">
|
||||
<col>
|
||||
<Button type="warning" size="small" v-on:click="clearPoints">Clear All Points</Button>
|
||||
</col>
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chslider from './ch-slider.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chrange from './ch-range.vue'
|
||||
|
||||
export default {
|
||||
name: 'OutputTab',
|
||||
components:{
|
||||
chslider,
|
||||
chselect,
|
||||
chrange
|
||||
},
|
||||
methods:{
|
||||
takePointA:function(){
|
||||
this.pointA = this.raw_point;
|
||||
this.calcSlope();
|
||||
},
|
||||
takePointB:function(){
|
||||
this.pointB = this.raw_point;
|
||||
this.calcSlope();
|
||||
},
|
||||
calcSlope:function(){
|
||||
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.$Message.error("Point A and B are to close apart");
|
||||
}
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
}
|
||||
},
|
||||
clearPoints:function(){
|
||||
this.sendSlope(1,0,false);
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
},
|
||||
sendSlope(m,b,valid){
|
||||
this.$socket.sendObj({'M':m});
|
||||
this.$socket.sendObj({'B':b});
|
||||
this.$socket.sendObj({'is_calibrated':valid});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
raw_point:{
|
||||
get:function(){
|
||||
return this.$store.state.raw_point;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pointA:undefined,
|
||||
pointB:undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,20 +1,26 @@
|
||||
import Vue from 'vue';
|
||||
import App from './App.vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import iView from 'iview';
|
||||
import router from "./routes";
|
||||
import '../theme/index.less';
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import vuetify from './plugins/vuetify';
|
||||
import VueNativeSock from 'vue-native-websocket';
|
||||
import locale from 'iview/dist/locale/en-US';
|
||||
import {store} from './store';
|
||||
import msgPack from 'msgpack5';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(iView , { locale });
|
||||
Vue.use(VueNativeSock,'ws://'+location.hostname+':8888/websocket',{format:'JSON'});
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
// Vue.use(VueNativeSock,'ws://' + location.host + '/websocket',{format: 'json'});
|
||||
Vue.use(VueNativeSock,'ws://'+location.hostname+':8888/websocket');
|
||||
Vue.prototype.$msgPack = msgPack(true)
|
||||
Vue.mixin({
|
||||
methods:{
|
||||
handleInput(key,value){
|
||||
let msg = this.$msgPack.encode({[key]:value})
|
||||
this.$socket.send(msg);
|
||||
}
|
||||
}
|
||||
})
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
vuetify,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
13
chameleon-client/src/plugins/vuetify.js
Normal file
13
chameleon-client/src/plugins/vuetify.js
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
import '@mdi/font/css/materialdesignicons.css';
|
||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
||||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify/lib';
|
||||
Vue.use(Vuetify);
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
27
chameleon-client/src/router.js
Normal file
27
chameleon-client/src/router.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
Vue.use(Router)
|
||||
|
||||
function lazyLoad(view){
|
||||
return() => import(`@/views/${view}.vue`)
|
||||
}
|
||||
export default new Router({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect:'/Vision'
|
||||
},
|
||||
{
|
||||
path: '/Vision',
|
||||
name: 'Vision',
|
||||
component: lazyLoad('Camera')
|
||||
},
|
||||
{
|
||||
path: '/Settings',
|
||||
name: 'Settings',
|
||||
component: lazyLoad('Settings')
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
import VueRouter from "vue-router";
|
||||
import Vision from "./components/Vision.vue"
|
||||
import Setting from "./components/Settings.vue"
|
||||
import Input from "./components/InputTab.vue";
|
||||
import Threshold from "./components/ThresholdTab.vue";
|
||||
import System from "./components/SystemTab.vue";
|
||||
import Camera from "./components/CameraTab.vue";
|
||||
import Contours from "./components/contourTab.vue";
|
||||
import Output from './components/outputTab.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', redirect: '/vision/input'},
|
||||
{ path: '/vision', component: Vision, children: [
|
||||
{ path: 'input', component: Input },
|
||||
{ path: 'threshold', component: Threshold },
|
||||
{ path: 'contours', component: Contours },
|
||||
{ path: 'output', component: Output },
|
||||
]},
|
||||
{ path: '/settings', component: Setting, children: [
|
||||
{ path: 'system', component: System },
|
||||
{ path: 'camera', component: Camera }
|
||||
]}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
routes
|
||||
})
|
||||
|
||||
export default router;
|
||||
@@ -1,119 +1,75 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(Vuex)
|
||||
|
||||
const set = key => (state,val) =>{
|
||||
state[key] = val
|
||||
Vue.set(state,key,val);
|
||||
};
|
||||
export const store = new Vuex.Store({
|
||||
|
||||
state:{
|
||||
//header
|
||||
curr_camera:"",
|
||||
curr_pipeline:"",
|
||||
cameraList:[],
|
||||
pipelineList:[],
|
||||
//input
|
||||
exposure:54,
|
||||
brightness:0,
|
||||
orientation:0,
|
||||
resolution:0,
|
||||
resolutionList:[],
|
||||
FOV:0,
|
||||
//threshold
|
||||
hue:[0,10],
|
||||
saturation:[0,10],
|
||||
value:[0,10],
|
||||
erode: false,
|
||||
dilate: false,
|
||||
//contours
|
||||
area:[0,100],
|
||||
ratio:[0,20],
|
||||
extent:[0,100],
|
||||
sort_mode:'Largest',
|
||||
target_group:'Single',
|
||||
target_intersection:'Up',
|
||||
//Settings
|
||||
team_number:0,
|
||||
connection_type:"DHCP",
|
||||
ip:"",
|
||||
gateway:"",
|
||||
netmask:"",
|
||||
hostname:"",
|
||||
//live info
|
||||
port:1181,
|
||||
is_binary:0,
|
||||
//points
|
||||
raw_point:[],
|
||||
point:{}
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
settings:{
|
||||
teamNumber:1577,
|
||||
connectionType:0,
|
||||
ip:"",
|
||||
gateway:"",
|
||||
netmask:"",
|
||||
hostname: "Chameleon-vision"
|
||||
},
|
||||
mutations:{
|
||||
curr_camera (state,value){
|
||||
state['curr_camera'] = value;
|
||||
state['pipeline'] = "0";
|
||||
},
|
||||
curr_pipeline: set('curr_pipeline'),
|
||||
brightness: set('brightness'),
|
||||
exposure: set('exposure'),
|
||||
orientation:set('orientation'),
|
||||
resolution: set('resolution'),
|
||||
hue: set('hue'),
|
||||
saturation: set('saturation'),
|
||||
value: set('value'),
|
||||
erode: set('erode'),
|
||||
dilate: set('dilate'),
|
||||
area: set('area'),
|
||||
ratio: set('ratio'),
|
||||
extent: set('extent'),
|
||||
team_number: set('team_number'),
|
||||
connection_type: set('connection_type'),
|
||||
ip: set('ip'),
|
||||
netmask: set('netmask'),
|
||||
gateway : set('gateway'),
|
||||
hostname : set('hostname'),
|
||||
is_binary: set('is_binary'),
|
||||
cameraList : set('cameraList'),
|
||||
pipelineList: set('piplineList'),
|
||||
sort_mode: set('sort_mode'),
|
||||
target_group:set('target_group'),
|
||||
target_intersection:set('target_intersection'),
|
||||
FOV:set('FOV'),
|
||||
port:set('port'),
|
||||
raw_point:set('raw_point'),
|
||||
point:set('point')
|
||||
pipeline:{
|
||||
exposure:0,
|
||||
brightness:0,
|
||||
orientation:0,
|
||||
hue:[0,15],
|
||||
saturation:[0,15],
|
||||
value:[0,25],
|
||||
erode:false,
|
||||
dilate:false,
|
||||
area:[0,12],
|
||||
ratio:[0,12],
|
||||
extent:[0,12],
|
||||
targetGrouping:0,
|
||||
targetIntersection:0,
|
||||
sortMode:0,
|
||||
isBinary:0
|
||||
},
|
||||
getters:{
|
||||
curr_camera: state => state.curr_camera,
|
||||
curr_pipeline: state => state.curr_pipeline,
|
||||
brightness: state => state.brightness,
|
||||
exposure: state => state.exposure,
|
||||
orientation: state => state.orientation,
|
||||
resolution: state => state.resolution,
|
||||
hue: state => state.hue,
|
||||
saturation: state => state.saturation,
|
||||
value: state => state.value,
|
||||
erode: state => state.dilate,
|
||||
dilate: state => state.dilate,
|
||||
area: state =>state.area,
|
||||
ratio: state =>state.ratio,
|
||||
extent: state =>state.extent,
|
||||
team_number: state => state.teamValue,
|
||||
connection_type: state => state.connectionType,
|
||||
ip: state => state.ip,
|
||||
netmask: state => state.netmask,
|
||||
gateway: state => state.gateway,
|
||||
hostname: state => state.hostName,
|
||||
is_binary: state => state.is_binary,
|
||||
cameraList: state => state.cameraList,
|
||||
pipelineList: state => state.pipelineList,
|
||||
sort_mode: state => state.sort_mode,
|
||||
target_group: state => state.target_group,
|
||||
target_intersection: state => state.target_intersection,
|
||||
FOV: state => state.FOV,
|
||||
port: state => state.port,
|
||||
raw_point:state => state.raw_point,
|
||||
point: state => state.point
|
||||
|
||||
},
|
||||
});
|
||||
cameraSettings:{},
|
||||
resolutionList:[],
|
||||
port:1181,
|
||||
currentCameraIndex:0,
|
||||
currentPipelineIndex:0,
|
||||
cameraList:[],
|
||||
pipelineList:[],
|
||||
point:{}
|
||||
},
|
||||
mutations: {
|
||||
settings: set('settings'),
|
||||
pipeline: set('pipeline'),
|
||||
cameraSettings: set('cameraSettings'),
|
||||
resolutionList: set('resolutionList'),
|
||||
port: set('port'),
|
||||
currentCameraIndex: set('currentCameraIndex'),
|
||||
currentPipelineIndex: set('currentPipelineIndex'),
|
||||
cameraList: set('cameraList'),
|
||||
pipelineList: set('pipelineList'),
|
||||
point:set('point')
|
||||
},
|
||||
actions: {
|
||||
settings: state => state.settings,
|
||||
pipeline: state => state.pipeline,
|
||||
cameraSettings: state =>state.cameraSettings,
|
||||
resolutionList: state =>state.resolutionList,
|
||||
port: state =>state.port,
|
||||
currentCameraIndex: state =>state.currentCameraIndex,
|
||||
currentPipelineIndex: state =>state.currentPipelineIndex,
|
||||
cameraList: state =>state.cameraList,
|
||||
pipelineList: state =>state.pipelineList,
|
||||
point: state =>state.point,
|
||||
setPipeValues(state,obj){
|
||||
for(let i in obj){
|
||||
Vue.set(state.pipeline,i,obj[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
281
chameleon-client/src/views/Camera.vue
Normal file
281
chameleon-client/src/views/Camera.vue
Normal file
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<v-row align="center">
|
||||
<v-col :cols="3" class="colsClass">
|
||||
<div style="padding-left:30px">
|
||||
<CVselect v-if="isCameraNameEdit == false" name="Camera" v-model="currentCameraIndex" :list="cameraList" @input="handleInput('currentCamera',currentCameraIndex)"></CVselect>
|
||||
<CVinput v-else name="Camera" v-model="newCameraName" @Enter="saveCameraNameChange" :errorMessage="checkCameraName"></CVinput>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col :cols="1">
|
||||
<CVicon color="#c5c5c5" v-if="isCameraNameEdit == false" hover text="edit" @click="toCameraNameChange" tooltip="Edit camera name"></CVicon>
|
||||
<div v-else>
|
||||
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save" @click="saveCameraNameChange" tooltip="Save Camera Name"></CVicon>
|
||||
<CVicon color="error" style="display: inline-block;" hover text="close" @click="discardCameraNameChange" tooltip="Discard Changes"></CVicon>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col :cols="3" class="colsClass">
|
||||
<CVselect v-if="isPipelineEdit == false" name="Pipeline" :list="pipelineList" v-model="currentPipelineIndex" @input="handleInput('currentPipeline',currentPipelineIndex)"></CVselect>
|
||||
<CVinput v-else name="Pipeline" v-model="newPipelineName" @Enter="savePipelineNameChange"></CVinput>
|
||||
</v-col>
|
||||
<v-col :cols="1" class="colsClass">
|
||||
<v-menu v-if="isPipelineEdit == false" 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"></CVicon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="handleInput('command','addNewPipeline')">
|
||||
<v-list-item-title>
|
||||
<CVicon color="#c5c5c5" :right="true" text="add" tooltip="Add new pipeline"></CVicon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="handleInput('command','deleteCurrentPipeline')">
|
||||
<v-list-item-title>
|
||||
<CVicon color="red darken-2" :right="true" text="delete" tooltip="Delete pipeline"></CVicon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="duplicateDialog = true">
|
||||
<v-list-item-title>
|
||||
<CVicon color="#c5c5c5" :right="true" text="mdi-content-copy" tooltip="Duplicate pipeline"></CVicon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<div v-else>
|
||||
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save" @click="savePipelineNameChange" tooltip="Save Pipeline Name"></CVicon>
|
||||
<CVicon color="error" style="display: inline-block;" hover text="close" @click="discardPipelineNameChange" tooltip="Discard Changes"></CVicon>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</div>
|
||||
<v-row>
|
||||
<v-col cols="6" class="colsClass">
|
||||
<v-tabs fixed-tabs background-color="#212121" dark height="48" slider-color="#4baf62" v-model="selectedTab">
|
||||
<v-tab>Input</v-tab>
|
||||
<v-tab>Threshold</v-tab>
|
||||
<v-tab>Contours</v-tab>
|
||||
<v-tab>Output</v-tab>
|
||||
</v-tabs>
|
||||
<div style="padding-left:30px">
|
||||
<component v-model="pipeline" :is="selectedComponent"></component>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" class="colsClass">
|
||||
<div>
|
||||
<v-tabs background-color="#212121" dark height="48" slider-color="#4baf62" centered style="padding-bottom:10px" v-model="isBinaryNumber" @change="handleInput('isBinary',pipeline.isBinary)">
|
||||
<v-tab>Normal</v-tab>
|
||||
<v-tab>Threshold</v-tab>
|
||||
</v-tabs>
|
||||
<div class="videoClass">
|
||||
<img v-if="cameraList.length > 0" :src="steamAdress">
|
||||
<span v-else>No Cameras Are connected</span>
|
||||
<h5 id="Point">{{point}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- pipeline duplicate dialog -->
|
||||
<v-dialog dark v-model="duplicateDialog" width="500" height="357" >
|
||||
<v-card dark>
|
||||
<v-card-title class="headline" primary-title>Duplicate Pipeline</v-card-title>
|
||||
<v-card-text>
|
||||
<CVselect name="Pipeline" :list="pipelineList" v-model="pipelineDuplicate.pipeline" @input="handleInput('currentPipeline',currentPipelineIndex)"></CVselect>
|
||||
<v-checkbox dark :label="'To another camera'" v-model="pipelineDuplicate.anotherCamera"></v-checkbox>
|
||||
<CVselect v-if="pipelineDuplicate.anotherCamera === true" name="Camera" v-model="pipelineDuplicate.camera" :list="cameraList"></CVselect>
|
||||
</v-card-text>
|
||||
<v-divider>
|
||||
</v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="#4baf62" text @click="duplicatePipeline">Duplicate</v-btn>
|
||||
<v-btn color="error" text @click="closeDuplicateDialog">Discard</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InputTab from './CameraViewes/InputTab'
|
||||
import ThresholdTab from './CameraViewes/ThresholdTab'
|
||||
import ContoursTab from './CameraViewes/ContoursTab'
|
||||
import OutputTab from './CameraViewes/OutputTab'
|
||||
import CVselect from '../components/cv-select'
|
||||
import CVicon from '../components/cv-icon'
|
||||
import CVinput from '../components/cv-input'
|
||||
export default {
|
||||
name: 'CameraTab',
|
||||
components:{
|
||||
InputTab,
|
||||
ThresholdTab,
|
||||
ContoursTab,
|
||||
OutputTab,
|
||||
CVselect,
|
||||
CVicon,
|
||||
CVinput
|
||||
},
|
||||
methods:{
|
||||
test(value){
|
||||
console.log(value)
|
||||
},
|
||||
toCameraNameChange(){
|
||||
this.newCameraName = this.cameraList[this.currentCameraIndex];
|
||||
this.isCameraNameEdit = true;
|
||||
},
|
||||
saveCameraNameChange(){
|
||||
if(this.cameraNameError === ""){
|
||||
this.handleInput("changeCameraName",this.newCameraName);
|
||||
this.discardCameraNameChange();
|
||||
}
|
||||
},
|
||||
discardCameraNameChange(){
|
||||
this.isCameraNameEdit = false;
|
||||
this.newCameraName = "";
|
||||
},
|
||||
toPipelineNameChange(){
|
||||
this.newPipelineName = this.pipelineList[this.currentPipelineIndex];
|
||||
this.isPipelineEdit = true;
|
||||
},
|
||||
savePipelineNameChange(){
|
||||
this.handleInput("changePipelineName",this.newPipelineName);
|
||||
this.discardPipelineNameChange();
|
||||
},
|
||||
discardPipelineNameChange(){
|
||||
this.isPipelineEdit = false;
|
||||
this.newPipelineName = "";
|
||||
},
|
||||
duplicatePipeline(){
|
||||
this.handleInput("dupicatePipeline",this.pipelineDuplicate);
|
||||
this.closeDuplicateDialog();
|
||||
},
|
||||
closeDuplicateDialog(){
|
||||
this.duplicateDialog = false;
|
||||
this.pipelineDuplicate = {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTab:0,
|
||||
// camera edit variables
|
||||
isCameraNameEdit:false,
|
||||
newCameraName:"",
|
||||
cameraNameError:"",
|
||||
// pipeline edit variables
|
||||
isPipelineEdit:false,
|
||||
newPipelineName:"",
|
||||
duplicateDialog:false,
|
||||
pipelineDuplicate:{},
|
||||
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
checkCameraName(){
|
||||
if(this.newCameraName !== this.cameraList[this.currentCameraIndex]){
|
||||
for(let cam in this.cameraList){
|
||||
if(this.newCameraName == this.cameraList[cam]){
|
||||
return "Camera by that name already Exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
isBinaryNumber:{
|
||||
get(){
|
||||
return this.pipeline.isBinary ? 1:0
|
||||
},
|
||||
set(value){
|
||||
this.pipeline.isBinary = !!value;
|
||||
}
|
||||
},
|
||||
selectedComponent:{
|
||||
get(){
|
||||
switch(this.selectedTab){
|
||||
case 0:
|
||||
return "InputTab";
|
||||
case 1:
|
||||
return "ThresholdTab";
|
||||
case 2:
|
||||
return "ContoursTab";
|
||||
case 3:
|
||||
return "OutputTab";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
point:{
|
||||
get:function(){
|
||||
let p = this.$store.state.point.calculated;
|
||||
let fps = this.$store.state.point.fps;
|
||||
if(p !== undefined){
|
||||
return ("Pitch: " + parseFloat(p['pitch']).toFixed(2) + " Yaw: " + parseFloat(p['yaw']).toFixed(2) + " FPS: " + fps.toFixed(2))
|
||||
} else{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
currentCameraIndex:{
|
||||
get(){
|
||||
return this.$store.state.currentCameraIndex;
|
||||
},
|
||||
set(value){
|
||||
this.$store.commit('currentCameraIndex',value);
|
||||
}
|
||||
},
|
||||
currentPipelineIndex:{
|
||||
get(){
|
||||
return this.$store.state.currentPipelineIndex;
|
||||
},
|
||||
set(value){
|
||||
this.$store.commit('currentPipelineIndex',value);
|
||||
}
|
||||
},
|
||||
cameraList:{
|
||||
get(){
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
pipelineList:{
|
||||
get(){
|
||||
return this.$store.state.pipelineList;
|
||||
}
|
||||
},
|
||||
pipeline:{
|
||||
get(){
|
||||
return this.$store.state.pipeline;
|
||||
}
|
||||
},
|
||||
steamAdress: {
|
||||
get: function(){
|
||||
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.colsClass{
|
||||
padding: 0 !important;
|
||||
|
||||
}
|
||||
.videoClass{
|
||||
text-align: center;
|
||||
}
|
||||
.videoClass img{
|
||||
height: auto !important;
|
||||
width: 70%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#Point{
|
||||
padding-top: 5px;
|
||||
text-align: center;
|
||||
color: #f4f4f4;
|
||||
}
|
||||
</style>
|
||||
40
chameleon-client/src/views/CameraViewes/ContoursTab.vue
Normal file
40
chameleon-client/src/views/CameraViewes/ContoursTab.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVrangeSlider v-model="value.area" name="Area" :min="0" :max="100" :step="0.1" @input="handleInput('area',value.area)"></CVrangeSlider>
|
||||
<CVrangeSlider v-model="value.ratio" name="Ratio (W/H)" :min="0" :max="100" :step="0.1" @input="handleInput('ratio',value.ratio)"></CVrangeSlider>
|
||||
<CVrangeSlider v-model="value.extent" name="Extent" :min="0" :max="100" @input="handleInput('extent',value.extent)"></CVrangeSlider>
|
||||
<CVselect name="Target Group" :list="['Single','Dual']" v-model="value.targetGroup" @input="handleInput('targetGroup',value.targetGroup)"></CVselect>
|
||||
<CVselect name="Target Intersection" :list="['None','Up','Down','Left','Right']" :disabled="isDisabled" v-model="value.targetIntersection" @input="handleInput('targetIntersection',value.targetIntersection)"></CVselect>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVrangeSlider from '../../components/cv-range-slider'
|
||||
import CVselect from '../../components/cv-select'
|
||||
export default {
|
||||
name: 'Contours',
|
||||
props:['value'],
|
||||
components:{
|
||||
CVrangeSlider,
|
||||
CVselect
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
isDisabled(){
|
||||
if(this.value.targetGroup === 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
34
chameleon-client/src/views/CameraViewes/InputTab.vue
Normal file
34
chameleon-client/src/views/CameraViewes/InputTab.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVslider name="Exposure" v-model="value.exposure" :min="0" :max="100" @input="handleInput('exposure',value.exposure)"></CVslider>
|
||||
<CVslider name="Brightness" v-model="value.brightness" :min="0" :max="100" @input="handleInput('brightness',value.brightness)"></CVslider>
|
||||
<CVselect name="Orientation" v-model="value.orientation" :list="['Normal','Inverted']" @input="handleInput('orientation',value.orientation)"></CVselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVslider from '../../components/cv-slider'
|
||||
import CVselect from '../../components/cv-select'
|
||||
export default {
|
||||
name: 'Input',
|
||||
props:['value'],
|
||||
components:{
|
||||
CVslider,
|
||||
CVselect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
t:0,
|
||||
a:1
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
|
||||
},
|
||||
computed:{}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
84
chameleon-client/src/views/CameraViewes/OutputTab.vue
Normal file
84
chameleon-client/src/views/CameraViewes/OutputTab.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVselect name="SortMode" v-model="value.sortMode" :list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']" @input="handleInput('sortMode',value.sortMode)"></CVselect>
|
||||
<span>Calibrate:</span><v-divider dark color="white"></v-divider>
|
||||
<v-row align="center" justify="start">
|
||||
<v-col style="padding-right:0px" :cols="3">
|
||||
<v-btn small color="#4baf62" @click="takePointA">Take Point A</v-btn>
|
||||
</v-col>
|
||||
<v-col style="margin-left:0px" :cols="3">
|
||||
<v-btn small color="#4baf62" @click="takePointB">Take Point B</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn small @click="clearSlope" color="yellow darken-3">Clear All Points</v-btn>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
<v-snackbar :timeout="3000" v-model="snackbar" top color="error">
|
||||
<span style="color:#000">Points are too close</span>
|
||||
<v-btn color="black" text @click="snackbar = false">Close</v-btn>
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVselect from '../../components/cv-select'
|
||||
export default {
|
||||
name: 'Output',
|
||||
props:['value'],
|
||||
components:{
|
||||
CVselect
|
||||
},
|
||||
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.snackbar = true;
|
||||
}
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
}
|
||||
},
|
||||
sendSlope(m,b,valid){
|
||||
this.handleInput('m',m);
|
||||
this.handleInput('b',b);
|
||||
this.handleInput('isCalibrated',valid);
|
||||
},
|
||||
clearSlope(){
|
||||
this.sendSlope(1,0,false);
|
||||
this.pointA = undefined;
|
||||
this.pointB = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
snackbar: false,
|
||||
pointA: undefined,
|
||||
pointB: undefined
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
rawPoint:{
|
||||
get(){
|
||||
return this.$store.state.point.rawPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
34
chameleon-client/src/views/CameraViewes/ThresholdTab.vue
Normal file
34
chameleon-client/src/views/CameraViewes/ThresholdTab.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVrangeSlider v-model="value.hue" name="Hue" :min="0" :max="180" @input="handleInput('hue',value.hue)"></CVrangeSlider>
|
||||
<CVrangeSlider v-model="value.saturation" name="Saturation" :min="0" :max="255" @input="handleInput('saturation',value.saturation)"></CVrangeSlider>
|
||||
<CVrangeSlider v-model="value.value" name="Value" :min="0" :max="255" @input="handleInput('value',value.value)"></CVrangeSlider>
|
||||
<CVswitch v-model="value.erode" name="Erode" @input="handleInput('erode',value.erode)"></CVswitch>
|
||||
<CVswitch v-model="value.dilate" name="Dilate" @input="handleInput('dilate',value.dilate)"></CVswitch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVrangeSlider from '../../components/cv-range-slider'
|
||||
import CVswitch from '../../components/cv-switch'
|
||||
export default {
|
||||
name: 'Threshold',
|
||||
props:['value'],
|
||||
components:{
|
||||
CVrangeSlider,
|
||||
CVswitch
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
67
chameleon-client/src/views/Settings.vue
Normal file
67
chameleon-client/src/views/Settings.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row align="center">
|
||||
<v-col cols="6" class="colsClass">
|
||||
<v-tabs fixed-tabs background-color="#212121" dark height="50" slider-color="#4baf62" v-model="selectedTab">
|
||||
<v-tab to="">General</v-tab>
|
||||
<v-tab to="">Cameras</v-tab>
|
||||
</v-tabs>
|
||||
<div style="padding-left:30px">
|
||||
<component :is="selectedComponent"></component>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col v-show="selectedTab === 1" class="colsClass">
|
||||
<div class="videoClass">
|
||||
<img :src="steamAdress">
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import General from './SettingsViewes/General'
|
||||
import Cameras from './SettingsViewes/Cameras'
|
||||
|
||||
export default {
|
||||
name: 'SettingsTab',
|
||||
components:{
|
||||
General,
|
||||
Cameras,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTab:0,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
selectedComponent(){
|
||||
switch(this.selectedTab){
|
||||
case 0:
|
||||
return "General";
|
||||
case 1:
|
||||
return "Cameras";
|
||||
}
|
||||
},
|
||||
steamAdress: {
|
||||
get: function(){
|
||||
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.videoClass{
|
||||
text-align: center;
|
||||
}
|
||||
.videoClass img{
|
||||
height: auto !important;
|
||||
width: 75%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.colsClass{
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
69
chameleon-client/src/views/SettingsViewes/Cameras.vue
Normal file
69
chameleon-client/src/views/SettingsViewes/Cameras.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVselect name="Camera" :list="cameraList" v-model="currentCameraIndex"></CVselect>
|
||||
<CVselect name="Resulotion" v-model="cameraSettings.resolution" :list="resolutionList"></CVselect>
|
||||
<CVselect name="Stream Resulotion" v-model="cameraSettings.streamDivisor" :list="['1:1','1:2','1:4','1:6']"></CVselect>
|
||||
<CVnumberinput name="Diagonal FOV" v-model="cameraSettings.fov" ></CVnumberinput>
|
||||
<v-btn style="margin-top:10px" small color="#4baf62" @click="sendCameraSettings">Save Camera Settings</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVselect from '../../components/cv-select'
|
||||
import CVnumberinput from '../../components/cv-number-input'
|
||||
export default {
|
||||
name: 'CameraSettings',
|
||||
components:{
|
||||
CVselect,
|
||||
CVnumberinput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
sendCameraSettings(){
|
||||
this.handleInput('cameraSettings',this.cameraSettings);
|
||||
},
|
||||
|
||||
},
|
||||
computed:{
|
||||
|
||||
currentCameraIndex:{
|
||||
get(){
|
||||
return this.$store.state.currentCameraIndex;
|
||||
},
|
||||
set(value){
|
||||
this.$store.commit('currentCameraIndex',value);
|
||||
}
|
||||
},
|
||||
cameraList:{
|
||||
get(){
|
||||
return this.$store.state.cameraList;
|
||||
},
|
||||
set(value){
|
||||
this.$store.commit('cameraList',value);
|
||||
}
|
||||
},
|
||||
resolutionList:{
|
||||
get(){
|
||||
return this.$store.state.resolutionList;
|
||||
}
|
||||
},
|
||||
cameraSettings:{
|
||||
get(){
|
||||
return this.$store.state.cameraSettings;
|
||||
},
|
||||
set(value){
|
||||
this.$store.commit('cameraSettings',value);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
53
chameleon-client/src/views/SettingsViewes/General.vue
Normal file
53
chameleon-client/src/views/SettingsViewes/General.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVnumberinput v-model="settings.teamNumber" name="Team Number"></CVnumberinput>
|
||||
<CVradio v-model="settings.connectionType" :list="['DHCP','Static']"></CVradio>
|
||||
<v-divider color="white"></v-divider>
|
||||
<CVinput name="IP" v-model="settings.ip" :disabled="isDisabled"></CVinput>
|
||||
<CVinput name="NetMask" v-model="settings.netmask" :disabled="isDisabled"></CVinput>
|
||||
<CVinput name="Gateway" v-model="settings.gateway" :disabled="isDisabled"></CVinput>
|
||||
<v-divider color="white"></v-divider>
|
||||
<CVinput name="Hostname" v-model="settings.hostname"></CVinput>
|
||||
<v-btn style="margin-top:10px" small color="#4baf62" @click="sendGeneralSettings">Save General Settings</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVnumberinput from '../../components/cv-number-input'
|
||||
import CVradio from '../../components/cv-radio'
|
||||
import CVinput from '../../components/cv-input'
|
||||
export default {
|
||||
name: 'General',
|
||||
components:{
|
||||
CVnumberinput,
|
||||
CVradio,
|
||||
CVinput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
sendGeneralSettings(){
|
||||
this.handleInput('generalSettings',this.settings);
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
isDisabled(){
|
||||
if(this.settings.connectionType === 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
settings:{
|
||||
get(){
|
||||
return this.$store.state.settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user