Feature: exclude objects (#362)
* feature: add exclude objects on the dashboard * chore: fix ts issues from status panel * chore: update gcodeviewer to v2.1.12 * feature: add object selection to gcode viewer Signed-off-by: Stefan Dej <meteyou@gmail.com>
This commit is contained in:
parent
dc9d3361a0
commit
eadc796e01
14
package-lock.json
generated
14
package-lock.json
generated
@ -16,7 +16,7 @@
|
||||
"@codemirror/state": "^0.19.1",
|
||||
"@codemirror/stream-parser": "^0.19.1",
|
||||
"@codemirror/view": "^0.19.1",
|
||||
"@sindarius/gcodeviewer": "^2.1.11",
|
||||
"@sindarius/gcodeviewer": "^2.1.12",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.16.0",
|
||||
"echarts": "^5.1.2",
|
||||
@ -1980,9 +1980,9 @@
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"node_modules/@sindarius/gcodeviewer": {
|
||||
"version": "2.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-2.1.11.tgz",
|
||||
"integrity": "sha512-B2qTxf+Tiv6RTIe9HULdqAJfrPIIPmvRtcChZDsjwTnNzmDuP7/l1K0VANgp9+kFvsGSzdFuM72r+auUMZyGsw==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-2.1.12.tgz",
|
||||
"integrity": "sha512-gquHWaNMvInCuV0Wrzy8nM84TD5fysg80kfImL+DqJdw8O7jrtYS3Rd11oVeL9mOMtcmd+HQ99VWI0Bz5xvCZQ==",
|
||||
"dependencies": {
|
||||
"@babylonjs/core": "^4.2.0",
|
||||
"@babylonjs/inspector": "^4.2.0",
|
||||
@ -21797,9 +21797,9 @@
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"@sindarius/gcodeviewer": {
|
||||
"version": "2.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-2.1.11.tgz",
|
||||
"integrity": "sha512-B2qTxf+Tiv6RTIe9HULdqAJfrPIIPmvRtcChZDsjwTnNzmDuP7/l1K0VANgp9+kFvsGSzdFuM72r+auUMZyGsw==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-2.1.12.tgz",
|
||||
"integrity": "sha512-gquHWaNMvInCuV0Wrzy8nM84TD5fysg80kfImL+DqJdw8O7jrtYS3Rd11oVeL9mOMtcmd+HQ99VWI0Bz5xvCZQ==",
|
||||
"requires": {
|
||||
"@babylonjs/core": "^4.2.0",
|
||||
"@babylonjs/inspector": "^4.2.0",
|
||||
|
@ -22,7 +22,7 @@
|
||||
"@codemirror/state": "^0.19.1",
|
||||
"@codemirror/stream-parser": "^0.19.1",
|
||||
"@codemirror/view": "^0.19.1",
|
||||
"@sindarius/gcodeviewer": "^2.1.11",
|
||||
"@sindarius/gcodeviewer": "^2.1.12",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.16.0",
|
||||
"echarts": "^5.1.2",
|
||||
|
@ -96,6 +96,9 @@
|
||||
<v-list-item class="minHeight36">
|
||||
<v-checkbox class="mt-0" hide-details v-model="showTravelMoves" :label="$t('GCodeViewer.ShowTravelMoves')"></v-checkbox>
|
||||
</v-list-item>
|
||||
<v-list-item class="minHeight36" v-if="loadedFile === sdCardFilePath && printing_objects.length > 1">
|
||||
<v-checkbox class="mt-0" hide-details v-model="showObjectSelection" :label="$t('GCodeViewer.ShowObjectSelection')"></v-checkbox>
|
||||
</v-list-item>
|
||||
<v-divider></v-divider>
|
||||
<v-list-item class="minHeight36">
|
||||
<v-checkbox class="mt-0" hide-details v-model="hdRendering" :label="$t('GCodeViewer.HDRendering')"></v-checkbox>
|
||||
@ -260,6 +263,14 @@ export default class Viewer extends Mixins(BaseMixin) {
|
||||
return this.printerIsPrinting && this.sdCardFilePath === this.loadedFile
|
||||
}
|
||||
|
||||
get printing_objects() {
|
||||
return this.$store.state.printer.exclude_object?.objects ?? []
|
||||
}
|
||||
|
||||
get excluded_objects() {
|
||||
return this.$store.state.printer.exclude_object?.excluded_objects ?? []
|
||||
}
|
||||
|
||||
async init() {
|
||||
let canvasElement = this.$store.state.gcodeviewer?.canvasBackup ?? null
|
||||
|
||||
@ -356,6 +367,26 @@ export default class Viewer extends Mixins(BaseMixin) {
|
||||
this.zSlider = this.maxZSlider
|
||||
this.loading = false
|
||||
viewer.setCursorVisiblity(this.showCursor)
|
||||
viewer.gcodeProcessor.updateFilePosition(viewer.fileSize)
|
||||
|
||||
if (this.loadedFile === this.sdCardFilePath && this.printing_objects.length) {
|
||||
let objects: any = []
|
||||
|
||||
this.printing_objects.forEach((object: any) => {
|
||||
const xValues = object.polygon.map((point: number[]) => point[0])
|
||||
const yValues = object.polygon.map((point: number[]) => point[1])
|
||||
|
||||
objects.push({
|
||||
cancelled: this.excluded_objects.includes(object.name),
|
||||
name: object.name,
|
||||
x: [Math.min(...xValues), Math.max(...xValues)],
|
||||
y: [Math.min(...yValues), Math.max(...yValues)],
|
||||
})
|
||||
})
|
||||
|
||||
viewer.buildObjects.loadObjectBoundaries(objects)
|
||||
viewer.buildObjects.showObjectSelection(this.showObjectSelection)
|
||||
}
|
||||
}
|
||||
|
||||
async fileSelected(e: any) {
|
||||
@ -423,8 +454,8 @@ export default class Viewer extends Mixins(BaseMixin) {
|
||||
await new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
loadCurrentFile() {
|
||||
this.loadFile('gcodes/' + this.sdCardFilePath)
|
||||
async loadCurrentFile() {
|
||||
await this.loadFile('gcodes/' + this.sdCardFilePath)
|
||||
this.loadedFile = this.sdCardFilePath
|
||||
}
|
||||
|
||||
@ -532,6 +563,19 @@ export default class Viewer extends Mixins(BaseMixin) {
|
||||
viewer?.toggleTravels(newVal)
|
||||
}
|
||||
|
||||
get showObjectSelection(): boolean {
|
||||
return this.$store.state.gui.gcodeViewer.showObjectSelection ?? false
|
||||
}
|
||||
|
||||
set showObjectSelection(newVal: boolean) {
|
||||
this.$store.dispatch('gui/saveSetting', {name: 'gcodeViewer.showObjectSelection', value: newVal})
|
||||
}
|
||||
|
||||
@Watch('showObjectSelection')
|
||||
showObjectSelectionChanged(newVal: boolean) {
|
||||
viewer?.buildObjects.showObjectSelection(newVal)
|
||||
}
|
||||
|
||||
get hdRendering() {
|
||||
return this.$store.state.gui.gcodeViewer.hdRendering
|
||||
}
|
||||
|
@ -73,6 +73,43 @@
|
||||
</v-card-title>
|
||||
</v-img>
|
||||
</template>
|
||||
<template v-if="['printing', 'paused'].includes(printer_state) && printing_objects.length">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col class="py-2">
|
||||
<span class="subtitle-2 d-block px-0 text--disabled"><v-icon class="mr-2" small>mdi-printer-3d-nozzle</v-icon>{{ current_object !== null ? current_object : '--' }}</span>
|
||||
</v-col>
|
||||
<v-col class="col-auto py-2">
|
||||
<v-icon class="text--disabled cursor-pointer" @click="openCancelObjectDialog(current_object)" small v-if="current_object !== null">mdi-close-circle</v-icon>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-divider class="mt-0 mb-0" ></v-divider>
|
||||
<template v-if="boolShowObjects">
|
||||
<div v-for="object in printing_objects" v-bind:key="object.name">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col class="py-2">
|
||||
<span class="subtitle-2 d-block px-0 text--disabled">{{ object.name }}</span>
|
||||
</v-col>
|
||||
<v-col class="col-auto py-2">
|
||||
<v-chip pill small class="text--disabled" v-if="excluded_objects.includes(object.name)">{{ $t('Panels.StatusPanel.Canceled') }}</v-chip>
|
||||
<v-icon class="text--disabled cursor-pointer" @click="openCancelObjectDialog(object.name)" small v-else>mdi-close-circle</v-icon>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-divider class="mt-0 mb-0" ></v-divider>
|
||||
</div>
|
||||
</template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col class="py-1 font-italic text-center">
|
||||
<span @click="boolShowObjects = !boolShowObjects" class="text--disabled cursor-pointer"><v-icon class="mr-3 text--disabled" small>mdi-chevron-{{ boolShowObjects ? 'up' : 'down' }}</v-icon><small>{{ $t('Panels.StatusPanel.CountObjects', {count: printing_objects.length}) }}</small><v-icon class="ml-3 text--disabled" small>mdi-chevron-{{ boolShowObjects ? 'up' : 'down' }}</v-icon></span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-divider class="mt-0 mb-0" ></v-divider>
|
||||
</template>
|
||||
<template v-if="display_message || print_stats_message">
|
||||
<v-container>
|
||||
<v-row>
|
||||
@ -267,28 +304,47 @@
|
||||
</template>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-dialog v-model="cancelObjectDialog.bool" max-width="290">
|
||||
<v-card>
|
||||
<v-toolbar flat dense >
|
||||
<v-toolbar-title>
|
||||
<span class="subheading"><v-icon class="mdi mdi-information" left></v-icon>{{ $t("Panels.StatusPanel.ExcludeObjectHeadline") }}</span>
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text class="mt-3">{{ $t("Panels.StatusPanel.ExcludeObjectText", {name: cancelObjectDialog.objectName}) }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="cancelObjectDialog.bool = false">{{ $t("Panels.StatusPanel.Cancel") }}</v-btn>
|
||||
<v-btn color="primary" text @click="cancelObject">{{ $t("Panels.StatusPanel.ExcludeObject") }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import Component from 'vue-class-component'
|
||||
import { Mixins, Watch } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import VueLoadImage from 'vue-load-image'
|
||||
import MinSettingsPanel from '@/components/panels/MinSettingsPanel'
|
||||
import MoonrakerStatePanel from '@/components/panels/MoonrakerStatePanel'
|
||||
import KlippyStatePanel from '@/components/panels/KlippyStatePanel'
|
||||
import KlipperWarningsPanel from '@/components/panels/KlipperWarningsPanel'
|
||||
import MinSettingsPanel from '@/components/panels/MinSettingsPanel.vue'
|
||||
import MoonrakerStatePanel from '@/components/panels/MoonrakerStatePanel.vue'
|
||||
import KlippyStatePanel from '@/components/panels/KlippyStatePanel.vue'
|
||||
import KlipperWarningsPanel from '@/components/panels/KlipperWarningsPanel.vue'
|
||||
|
||||
@Component({
|
||||
components: {KlipperWarningsPanel, KlippyStatePanel, MoonrakerStatePanel, MinSettingsPanel},
|
||||
VueLoadImage
|
||||
components: {KlipperWarningsPanel, KlippyStatePanel, MoonrakerStatePanel, MinSettingsPanel}
|
||||
})
|
||||
export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
maxFlow = 0
|
||||
boolShowObjects = false
|
||||
|
||||
refs = {
|
||||
bigThumbnail: HTMLDivElement
|
||||
cancelObjectDialog = {
|
||||
bool: false,
|
||||
objectName: ''
|
||||
}
|
||||
|
||||
$refs!: {
|
||||
bigThumbnail: any
|
||||
}
|
||||
|
||||
get current_filename() {
|
||||
@ -485,7 +541,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
'thumbnails' in this.current_file &&
|
||||
this.current_file.thumbnails.length
|
||||
) {
|
||||
const thumbnail = this.current_file.thumbnails.find(thumb =>
|
||||
const thumbnail = this.current_file.thumbnails.find((thumb: any) =>
|
||||
thumb.width >= 32 && thumb.width <= 64 &&
|
||||
thumb.height >= 32 && thumb.height <= 64
|
||||
)
|
||||
@ -509,7 +565,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
'thumbnails' in this.current_file &&
|
||||
this.current_file.thumbnails.length
|
||||
) {
|
||||
const thumbnail = this.current_file.thumbnails.find(thumb => thumb.width >= 300 && thumb.width <= 400)
|
||||
const thumbnail = this.current_file.thumbnails.find((thumb: any) => thumb.width >= 300 && thumb.width <= 400)
|
||||
|
||||
if (thumbnail && 'relative_path' in thumbnail) {
|
||||
let relative_url = ''
|
||||
@ -530,7 +586,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
'thumbnails' in this.current_file &&
|
||||
this.current_file.thumbnails.length
|
||||
) {
|
||||
const thumbnail = this.current_file.thumbnails.find(thumb => thumb.width >= 300 && thumb.width <= 400)
|
||||
const thumbnail = this.current_file.thumbnails.find((thumb: any) => thumb.width >= 300 && thumb.width <= 400)
|
||||
|
||||
if (thumbnail && 'height' in thumbnail) {
|
||||
return thumbnail.height
|
||||
@ -545,7 +601,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
'thumbnails' in this.current_file &&
|
||||
this.current_file.thumbnails.length
|
||||
) {
|
||||
const thumbnail = this.current_file.thumbnails.find(thumb => thumb.width >= 300 && thumb.width <= 400)
|
||||
const thumbnail = this.current_file.thumbnails.find((thumb: any) => thumb.width >= 300 && thumb.width <= 400)
|
||||
|
||||
if (thumbnail && 'width' in thumbnail) {
|
||||
return thumbnail.width
|
||||
@ -561,6 +617,18 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
return this.current_filename && setting && this.thumbnailBig
|
||||
}
|
||||
|
||||
get printing_objects() {
|
||||
return this.$store.state.printer.exclude_object?.objects ?? []
|
||||
}
|
||||
|
||||
get current_object() {
|
||||
return this.$store.state.printer.exclude_object?.current_object ?? null
|
||||
}
|
||||
|
||||
get excluded_objects() {
|
||||
return this.$store.state.printer.exclude_object?.excluded_objects ?? []
|
||||
}
|
||||
|
||||
btnPauseJob() {
|
||||
this.$socket.emit('printer.print.pause', { }, { loading: 'statusPrintPause' })
|
||||
}
|
||||
@ -585,7 +653,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
this.$socket.emit('printer.gcode.script', {script: 'M117'})
|
||||
}
|
||||
|
||||
formatTime(seconds) {
|
||||
formatTime(seconds: number) {
|
||||
let h = Math.floor(seconds / 3600)
|
||||
seconds %= 3600
|
||||
let m = ('0' + Math.floor(seconds / 60)).slice(-2)
|
||||
@ -594,19 +662,17 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
return h+':'+m+':'+s
|
||||
}
|
||||
|
||||
formatDateTime(msec) {
|
||||
formatDateTime(msec: number) {
|
||||
const date = new Date(msec)
|
||||
const h = date.getHours() >= 10 ? date.getHours() : '0'+date.getHours()
|
||||
const m = date.getMinutes() >= 10 ? date.getMinutes() : '0'+date.getMinutes()
|
||||
|
||||
const diff = msec - new Date().getTime()
|
||||
return h+':'+m+((diff > 60*60*24*1000) ? '+'+parseInt(diff / (60*60*24*1000)) : '')
|
||||
return h+':'+m+((diff > 60*60*24*1000) ? '+'+Math.round(diff / (60*60*24*1000)) : '')
|
||||
}
|
||||
|
||||
@Watch('live_flow')
|
||||
live_flowChanged(newVal) {
|
||||
newVal = parseFloat(newVal)
|
||||
|
||||
live_flowChanged(newVal: number) {
|
||||
if (newVal && this.maxFlow < newVal) this.maxFlow = newVal
|
||||
}
|
||||
|
||||
@ -626,6 +692,16 @@ export default class StatusPanel extends Mixins(BaseMixin) {
|
||||
}
|
||||
}
|
||||
|
||||
openCancelObjectDialog(objectName: string) {
|
||||
this.cancelObjectDialog.objectName = objectName
|
||||
this.cancelObjectDialog.bool = true
|
||||
}
|
||||
|
||||
cancelObject() {
|
||||
this.$socket.emit('printer.gcode.script', {script: 'EXCLUDE_OBJECT NAME='+this.cancelObjectDialog.objectName})
|
||||
this.cancelObjectDialog.bool = false
|
||||
}
|
||||
|
||||
onResize() {
|
||||
const isFocused = (document.activeElement === this.$refs.bigThumbnail?.$el)
|
||||
if (isFocused) this.focusBigThumbnail()
|
||||
|
@ -336,7 +336,13 @@
|
||||
"Total": "Total",
|
||||
"Slicer": "Slicer",
|
||||
"ETA": "ETA",
|
||||
"Unknown": "Unknown"
|
||||
"Unknown": "Unknown",
|
||||
"ExcludeObjectHeadline": "Exclude Object",
|
||||
"ExcludeObjectText": "Do you really want to exclude \"{name}\"?",
|
||||
"Cancel": "cancel",
|
||||
"ExcludeObject": "Exclude object",
|
||||
"CountObjects": "{count} Objects",
|
||||
"Canceled": "Canceled"
|
||||
},
|
||||
"ToolsPanel": {
|
||||
"Headline": "Temperatures",
|
||||
@ -613,7 +619,8 @@
|
||||
"Ultra" : "Ultra",
|
||||
"Max" : "Max",
|
||||
"ShowToolhead":"Show Toolhead",
|
||||
"ShowTravelMoves":"ShowTravelMoves",
|
||||
"ShowTravelMoves":"Show Travel Moves",
|
||||
"ShowObjectSelection":"Show Object Selection",
|
||||
"HDRendering":"HD Rendering (Round Extrusions)",
|
||||
"ForceLineRendering":"Force Line Rendering",
|
||||
"Transparency":"Transparency",
|
||||
|
@ -178,6 +178,7 @@ export const getDefaultState = (): GuiState => {
|
||||
progressColor : '#FFFFFF',
|
||||
showCursor: true,
|
||||
showTravelMoves: false,
|
||||
showObjectSelection: false,
|
||||
hdRendering: false,
|
||||
forceLineRendering: false,
|
||||
transparency: false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user