Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Webcam): add a optional overlay for IDEX calibration #2053

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/assets/styles/page.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ body {
height: 100% !important;
}

.p-abs {
.position-absolute {
position: absolute !important;
}

.p-rel {
.position-relative {
position: relative !important;
}

Expand Down
8 changes: 4 additions & 4 deletions src/components/panels/ToolheadControls/CrossControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand All @@ -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 ||
Expand Down
98 changes: 98 additions & 0 deletions src/components/settings/Webcams/WebcamForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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
}
Expand Down
94 changes: 94 additions & 0 deletions src/components/webcams/WebcamNozzleCrosshair.vue
Original file line number Diff line number Diff line change
@@ -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>
7 changes: 7 additions & 0 deletions src/components/webcams/streamers/Mjpegstreamer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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" />
Expand Down Expand Up @@ -99,6 +100,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 {
Expand Down
7 changes: 7 additions & 0 deletions src/components/webcams/streamers/MjpegstreamerAdaptive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,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" />
Expand Down Expand Up @@ -100,6 +101,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)
}
Expand Down
10 changes: 8 additions & 2 deletions src/components/webcams/streamers/WebrtcCameraStreamer.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div>
<div class="position-relative d-flex">
<video
v-show="status === 'connected'"
ref="stream"
Expand All @@ -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" />
Expand All @@ -23,14 +24,15 @@ 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
iceServers?: RTCIceServer[]
}

@Component({
methods: { capitalize },
components: { WebcamNozzleCrosshair },
})
export default class WebrtcCameraStreamer extends Mixins(BaseMixin, WebcamMixin) {
capitalize = capitalize
Expand Down Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1278,9 +1278,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",
Expand All @@ -1300,10 +1302,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",
Expand Down
3 changes: 3 additions & 0 deletions src/store/gui/webcams/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export interface GuiWebcamStateWebcam {
extra_data?: {
enableAudio?: boolean
hideFps?: boolean
nozzleCrosshair?: boolean
nozzleCrosshairColor?: string
nozzleCrosshairSize?: number
}
source?: 'config' | 'database'
}