feat(Webcam): add a optional overlay for IDEX calibration (#2053)
This commit is contained in:
parent
34f3f08bd6
commit
04433fc397
@ -48,11 +48,11 @@ body {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.p-abs {
|
||||
.position-absolute {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.p-rel {
|
||||
.position-relative {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,9 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="3" class="p-rel">
|
||||
<v-col cols="3" class="position-relative">
|
||||
<v-btn
|
||||
class="btnMinWidthAuto fill-width p-abs"
|
||||
class="btnMinWidthAuto fill-width position-absolute"
|
||||
style="top: -50%; width: calc(100% - 8px)"
|
||||
:disabled="
|
||||
!xAxisHomed ||
|
||||
@ -72,9 +72,9 @@
|
||||
<v-icon>{{ mdiChevronDown }}</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="3" class="p-rel">
|
||||
<v-col cols="3" class="position-relative">
|
||||
<v-btn
|
||||
class="btnMinWidthAuto fill-width p-abs"
|
||||
class="btnMinWidthAuto fill-width position-absolute"
|
||||
style="top: -50%; width: calc(100% - 8px)"
|
||||
:disabled="
|
||||
!xAxisHomed ||
|
||||
|
@ -143,6 +143,58 @@
|
||||
:label="$t('Settings.WebcamsTab.Vertically')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<template v-if="nozzleCrosshairAvialable">
|
||||
<v-row>
|
||||
<v-col class="pt-3 pb-3">
|
||||
<div class="v-label v-label--active theme--dark text-subtitle-1">
|
||||
{{ $t('Settings.WebcamsTab.NozzleCrosshair') }}:
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class="mt-0">
|
||||
<v-col class="py-0">
|
||||
<v-checkbox
|
||||
v-model="nozzleCrosshair"
|
||||
class="mt-1"
|
||||
hide-details
|
||||
:label="$t('Settings.WebcamsTab.Enable')" />
|
||||
</v-col>
|
||||
<v-col v-if="nozzleCrosshair" class="py-0">
|
||||
<v-menu bottom left offset-y :close-on-content-click="false">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn
|
||||
v-bind="attrs"
|
||||
:color="nozzleCrosshairColor"
|
||||
class="minwidth-0 px-5"
|
||||
small
|
||||
v-on="on" />
|
||||
</template>
|
||||
<v-color-picker
|
||||
:value="nozzleCrosshairColor"
|
||||
hide-mode-switch
|
||||
mode="rgba"
|
||||
@update:color="updateLogoColor" />
|
||||
</v-menu>
|
||||
<div
|
||||
class="v-label v-label--active theme--dark text-subtitle-1 d-inline-block ml-2 mt-2">
|
||||
{{ $t('Settings.WebcamsTab.Color') }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="nozzleCrosshair">
|
||||
<v-col>
|
||||
<v-slider
|
||||
v-model="nozzleCrosshairSize"
|
||||
:max="1"
|
||||
:min="0.01"
|
||||
:step="0.01"
|
||||
thumb-label
|
||||
thumb-size="24"
|
||||
hide-details
|
||||
:label="$t('Settings.WebcamsTab.Size')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-col>
|
||||
<v-col class="col-12 col-sm-6 text-center" align-self="center">
|
||||
<webcam-wrapper :webcam="webcam" page="settings" />
|
||||
@ -311,6 +363,52 @@ export default class WebcamForm extends Mixins(BaseMixin, WebcamMixin) {
|
||||
this.webcam.extra_data.enableAudio = newVal
|
||||
}
|
||||
|
||||
get nozzleCrosshairAvialable() {
|
||||
return ['mjpegstreamer', 'mjpegstreamer-adaptive', 'webrtc-camerastreamer'].includes(this.webcam.service)
|
||||
}
|
||||
|
||||
get nozzleCrosshair() {
|
||||
return this.webcam.extra_data?.nozzleCrosshair ?? false
|
||||
}
|
||||
|
||||
set nozzleCrosshair(newVal) {
|
||||
const extraData = { ...(this.webcam.extra_data ?? {}) }
|
||||
extraData.nozzleCrosshair = newVal
|
||||
|
||||
this.webcam.extra_data = extraData
|
||||
}
|
||||
|
||||
get nozzleCrosshairColor() {
|
||||
return this.webcam.extra_data?.nozzleCrosshairColor ?? '#ff0000'
|
||||
}
|
||||
|
||||
set nozzleCrosshairColor(newVal: string) {
|
||||
const extraData = { ...(this.webcam.extra_data ?? {}) }
|
||||
extraData.nozzleCrosshairColor = newVal
|
||||
|
||||
this.webcam.extra_data = extraData
|
||||
}
|
||||
|
||||
updateLogoColor(color: string | { hex: string }) {
|
||||
if (typeof color === 'object') {
|
||||
this.nozzleCrosshairColor = color.hex
|
||||
return
|
||||
}
|
||||
|
||||
this.nozzleCrosshairColor = color
|
||||
}
|
||||
|
||||
get nozzleCrosshairSize() {
|
||||
return this.webcam.extra_data?.nozzleCrosshairSize ?? 0.1
|
||||
}
|
||||
|
||||
set nozzleCrosshairSize(newVal: number) {
|
||||
const extraData = { ...(this.webcam.extra_data ?? {}) }
|
||||
extraData.nozzleCrosshairSize = newVal
|
||||
|
||||
this.webcam.extra_data = extraData
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.oldWebcamName = this.webcam.name
|
||||
}
|
||||
|
94
src/components/webcams/WebcamNozzleCrosshair.vue
Normal file
94
src/components/webcams/WebcamNozzleCrosshair.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div ref="container" class="crosshair-container">
|
||||
<div class="line horizontal" :style="styleLines" />
|
||||
<div class="line vertical" :style="styleLines" />
|
||||
<div class="circle" :style="styleCircle" />
|
||||
<resize-observer @notify="handleResize" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Component from 'vue-class-component'
|
||||
import { Mixins, Prop, Ref } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
|
||||
|
||||
@Component
|
||||
export default class WebcamWrapper extends Mixins(BaseMixin) {
|
||||
@Prop({ type: Object, required: true }) webcam!: GuiWebcamStateWebcam
|
||||
@Ref() container!: HTMLDivElement
|
||||
|
||||
clientHeight = 0
|
||||
|
||||
get color() {
|
||||
return this.webcam.extra_data?.nozzleCrosshairColor ?? '#ff0000'
|
||||
}
|
||||
|
||||
get styleLines() {
|
||||
return {
|
||||
backgroundColor: this.color,
|
||||
}
|
||||
}
|
||||
|
||||
get styleCircle() {
|
||||
const nozzleCrosshairSize = this.webcam.extra_data?.nozzleCrosshairSize ?? 0.1
|
||||
const size = this.clientHeight * nozzleCrosshairSize
|
||||
|
||||
return {
|
||||
borderColor: this.color,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
marginLeft: `-${size / 2}px`,
|
||||
marginTop: `-${size / 2}px`,
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.handleResize()
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
this.$nextTick(() => {
|
||||
this.clientHeight = this.container.clientHeight
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.crosshair-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
height: 1px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
left: 50%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.circle {
|
||||
position: absolute;
|
||||
border: 1px solid #ff0000;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
</style>
|
@ -12,6 +12,7 @@
|
||||
<span v-if="showFpsCounter && status === 'connected'" class="webcamFpsOutput">
|
||||
{{ $t('Panels.WebcamPanel.FPS') }}: {{ fpsOutput }}
|
||||
</span>
|
||||
<webcam-nozzle-crosshair v-if="showNozzleCrosshair" :webcam="camSettings" />
|
||||
<v-row v-if="status !== 'connected'">
|
||||
<v-col class="_webcam_mjpegstreamer_output text-center d-flex flex-column justify-center align-center">
|
||||
<v-progress-circular v-if="status === 'connecting'" indeterminate color="primary" class="mb-3" />
|
||||
@ -100,6 +101,12 @@ export default class Mjpegstreamer extends Mixins(BaseMixin, WebcamMixin) {
|
||||
return this.$store.getters['gui/getPanelExpand']('webcam-panel', this.viewport) ?? false
|
||||
}
|
||||
|
||||
get showNozzleCrosshair() {
|
||||
const nozzleCrosshair = this.camSettings.extra_data?.nozzleCrosshair ?? false
|
||||
|
||||
return nozzleCrosshair && this.status === 'connected'
|
||||
}
|
||||
|
||||
// start or stop the video when the expanded state changes
|
||||
@Watch('expanded', { immediate: true })
|
||||
expandChanged(newExpanded: boolean): void {
|
||||
|
@ -13,6 +13,7 @@
|
||||
<span v-if="status === 'connected' && showFpsCounter" class="webcamFpsOutput">
|
||||
{{ $t('Panels.WebcamPanel.FPS') }}: {{ fpsOutput }}
|
||||
</span>
|
||||
<webcam-nozzle-crosshair v-if="showNozzleCrosshair" :webcam="camSettings" />
|
||||
<v-row v-if="status !== 'connected'">
|
||||
<v-col class="_webcam_mjpegstreamer_output text-center d-flex flex-column justify-center align-center">
|
||||
<v-progress-circular v-if="status === 'connecting'" indeterminate color="primary" class="mb-3" />
|
||||
@ -101,6 +102,12 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin, WebcamMixin
|
||||
return this.isVisibleDocument && this.isVisibleViewport
|
||||
}
|
||||
|
||||
get showNozzleCrosshair() {
|
||||
const nozzleCrosshair = this.camSettings.extra_data?.nozzleCrosshair ?? false
|
||||
|
||||
return nozzleCrosshair && this.status === 'connected'
|
||||
}
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="position-relative d-flex">
|
||||
<video
|
||||
v-show="status === 'connected'"
|
||||
ref="stream"
|
||||
@ -8,6 +8,7 @@
|
||||
autoplay
|
||||
muted
|
||||
playsinline />
|
||||
<webcam-nozzle-crosshair v-if="nozzleCrosshair" :webcam="camSettings" />
|
||||
<v-row v-if="status !== 'connected'">
|
||||
<v-col class="_webcam_webrtc_output text-center d-flex flex-column justify-center align-center">
|
||||
<v-progress-circular v-if="status === 'connecting'" indeterminate color="primary" class="mb-3" />
|
||||
@ -23,6 +24,7 @@ import BaseMixin from '@/components/mixins/base'
|
||||
import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
|
||||
import WebcamMixin from '@/components/mixins/webcam'
|
||||
import { capitalize } from '@/plugins/helpers'
|
||||
import WebcamNozzleCrosshair from '@/components/webcams/WebcamNozzleCrosshair.vue'
|
||||
|
||||
interface CameraStreamerResponse extends RTCSessionDescriptionInit {
|
||||
id: string
|
||||
@ -30,7 +32,7 @@ interface CameraStreamerResponse extends RTCSessionDescriptionInit {
|
||||
}
|
||||
|
||||
@Component({
|
||||
methods: { capitalize },
|
||||
components: { WebcamNozzleCrosshair },
|
||||
})
|
||||
export default class WebrtcCameraStreamer extends Mixins(BaseMixin, WebcamMixin) {
|
||||
capitalize = capitalize
|
||||
@ -65,6 +67,10 @@ export default class WebrtcCameraStreamer extends Mixins(BaseMixin, WebcamMixin)
|
||||
return output
|
||||
}
|
||||
|
||||
get nozzleCrosshair() {
|
||||
return this.camSettings.extra_data?.nozzleCrosshair ?? false
|
||||
}
|
||||
|
||||
get expanded(): boolean {
|
||||
if (this.page !== 'dashboard') return true
|
||||
|
||||
|
@ -1292,9 +1292,11 @@
|
||||
"Update": "update",
|
||||
"WebcamsTab": {
|
||||
"AddWebcam": "add webcam",
|
||||
"Color": "Color",
|
||||
"CreateWebcam": "Create Webcam",
|
||||
"EditCrowsnestConf": "Edit crowsnest.conf",
|
||||
"EditWebcam": "Edit Webcam",
|
||||
"Enable": "Enable",
|
||||
"EnableAudio": "Enable audio",
|
||||
"FlipWebcam": "Flip webcam image:",
|
||||
"HideFps": "Hide FPS counter",
|
||||
@ -1314,10 +1316,12 @@
|
||||
"MjpegstreamerAdaptive": "Adaptive MJPEG-Streamer (experimental)",
|
||||
"Name": "Name",
|
||||
"NameAlreadyExists": "Name already exists",
|
||||
"NozzleCrosshair": "Nozzle Crosshair",
|
||||
"Required": "required",
|
||||
"Rotate": "Rotate",
|
||||
"SaveWebcam": "Save Webcam",
|
||||
"Service": "Service",
|
||||
"Size": "Size",
|
||||
"TargetFPS": "Target FPS",
|
||||
"UpdateWebcam": "Update Webcam",
|
||||
"UrlSnapshot": "URL Snapshot",
|
||||
|
@ -28,6 +28,9 @@ export interface GuiWebcamStateWebcam {
|
||||
extra_data?: {
|
||||
enableAudio?: boolean
|
||||
hideFps?: boolean
|
||||
nozzleCrosshair?: boolean
|
||||
nozzleCrosshairColor?: string
|
||||
nozzleCrosshairSize?: number
|
||||
}
|
||||
source?: 'config' | 'database'
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user