refactor: refactor heightmap page (#1759)
* refactor: refactor heightmap page Signed-off-by: Stefan Dej <meteyou@gmail.com> * refactor: remove unused import in printer/getters Signed-off-by: Stefan Dej <meteyou@gmail.com> --------- Signed-off-by: Stefan Dej <meteyou@gmail.com>
This commit is contained in:
parent
405bcac390
commit
f9c59e1f26
411
src/components/charts/HeightmapChart.vue
Normal file
411
src/components/charts/HeightmapChart.vue
Normal file
@ -0,0 +1,411 @@
|
||||
<template>
|
||||
<e-chart
|
||||
ref="heightmap"
|
||||
:option="chartOptions"
|
||||
:init-options="{ renderer: 'canvas' }"
|
||||
style="height: 600px; width: 100%; overflow: hidden" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import BedmeshMixin from '@/components/mixins/bedmesh'
|
||||
|
||||
import { use } from 'echarts/core'
|
||||
|
||||
// import ECharts modules manually to reduce bundle size
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { VisualMapComponent } from 'echarts/components'
|
||||
|
||||
// @ts-ignore
|
||||
import { Grid3DComponent } from 'echarts-gl/components'
|
||||
//type definitions for echarts-gl do not exist
|
||||
// @ts-ignore
|
||||
import { SurfaceChart } from 'echarts-gl/charts'
|
||||
import type { ECharts } from 'echarts'
|
||||
import ThemeMixin from '@/components/mixins/theme'
|
||||
|
||||
use([CanvasRenderer, VisualMapComponent, Grid3DComponent, SurfaceChart])
|
||||
|
||||
interface HeightmapSerie {
|
||||
type: string
|
||||
name: string
|
||||
data: number[][]
|
||||
dataShape?: number[]
|
||||
itemStyle?: {
|
||||
opacity?: number
|
||||
color?: number[]
|
||||
}
|
||||
wireframe: {
|
||||
show: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
export default class HeightmapChart extends Mixins(BaseMixin, BedmeshMixin, ThemeMixin) {
|
||||
declare $refs: {
|
||||
// eslint-disable-next-line
|
||||
heightmap: any
|
||||
}
|
||||
|
||||
@Prop({ type: Boolean, default: false }) showProbed!: boolean
|
||||
@Prop({ type: Boolean, default: false }) showMesh!: boolean
|
||||
@Prop({ type: Boolean, default: false }) showFlat!: boolean
|
||||
@Prop({ type: Boolean, default: false }) wireframe!: boolean
|
||||
@Prop({ type: Boolean, default: false }) scaleGradient!: boolean
|
||||
@Prop({ type: Number, default: 1 }) scaleZMax!: number
|
||||
|
||||
get chart(): ECharts | null {
|
||||
return this.$refs.heightmap?.chart ?? null
|
||||
}
|
||||
|
||||
get chartOptions() {
|
||||
return {
|
||||
tooltip: {
|
||||
backgroundColor: this.bgColor(0.9),
|
||||
borderWidth: 0,
|
||||
textStyle: {
|
||||
color: this.fgColor(1),
|
||||
fontSize: '14px',
|
||||
},
|
||||
padding: 15,
|
||||
formatter: this.tooltipFormatter,
|
||||
},
|
||||
darkMode: this.$vuetify.theme.dark,
|
||||
animation: false,
|
||||
legend: {
|
||||
show: false,
|
||||
selected: this.selected,
|
||||
},
|
||||
visualMap: {
|
||||
show: true,
|
||||
min: this.visualMapRange[0],
|
||||
max: this.visualMapRange[1],
|
||||
calculable: true,
|
||||
dimension: 2,
|
||||
inRange: {
|
||||
color: this.colorMap,
|
||||
},
|
||||
seriesIndex: this.visualMapSeriesIndex,
|
||||
left: this.isMobile ? 10 : 30,
|
||||
top: 20,
|
||||
bottom: 0,
|
||||
itemWidth: this.isMobile ? 10 : 30,
|
||||
itemHeight: 550,
|
||||
precision: 3,
|
||||
textStyle: {
|
||||
color: this.fgColorHi,
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
xAxis3D: {
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: this.fgColorMid,
|
||||
},
|
||||
min: this.rangeX[0],
|
||||
max: this.rangeX[1],
|
||||
minInterval: 1,
|
||||
},
|
||||
yAxis3D: {
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: this.fgColorMid,
|
||||
},
|
||||
min: this.rangeY[0],
|
||||
max: this.rangeY[1],
|
||||
},
|
||||
zAxis3D: {
|
||||
type: 'value',
|
||||
min: this.scaleZMax * -1,
|
||||
max: this.scaleZMax,
|
||||
nameTextStyle: {
|
||||
color: this.fgColorMid,
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
formatter: function (value: any) {
|
||||
value = parseFloat(value)
|
||||
return value.toFixed(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
grid3D: {
|
||||
axisLabel: {
|
||||
textStyle: { color: this.fgColorMid },
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: { color: this.fgColorLow },
|
||||
},
|
||||
axisTick: {
|
||||
lineStyle: { color: this.fgColorLow },
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: { color: this.fgColorLow },
|
||||
},
|
||||
axisPointer: {
|
||||
lineStyle: { color: this.fgColorHi },
|
||||
label: {
|
||||
textStyle: { color: this.fgColorHi },
|
||||
},
|
||||
},
|
||||
boxWidth: 100 * this.scaleX,
|
||||
boxDepth: 100 * this.scaleY,
|
||||
viewControl: { distance: 150 },
|
||||
},
|
||||
series: this.series,
|
||||
}
|
||||
}
|
||||
|
||||
get selected(): { [key: string]: boolean } {
|
||||
return {
|
||||
probed: this.showProbed,
|
||||
mesh: this.showMesh,
|
||||
flat: this.showFlat,
|
||||
}
|
||||
}
|
||||
|
||||
get series(): HeightmapSerie[] {
|
||||
const series = []
|
||||
|
||||
if (this.bed_mesh) {
|
||||
series.push(this.seriesProbed)
|
||||
series.push(this.seriesMesh)
|
||||
series.push(this.seriesFlat)
|
||||
}
|
||||
|
||||
return series
|
||||
}
|
||||
|
||||
get seriesProbed(): HeightmapSerie {
|
||||
const serie: HeightmapSerie = {
|
||||
type: 'surface',
|
||||
name: 'probed',
|
||||
data: [],
|
||||
itemStyle: { opacity: 1 },
|
||||
wireframe: { show: this.wireframe },
|
||||
}
|
||||
|
||||
if (this.bed_mesh) {
|
||||
const xCount = this.bed_mesh.probed_matrix[0].length
|
||||
const yCount = this.bed_mesh.probed_matrix.length
|
||||
const xMin = this.bed_mesh.mesh_min[0]
|
||||
const xMax = this.bed_mesh.mesh_max[0]
|
||||
const yMin = this.bed_mesh.mesh_min[1]
|
||||
const yMax = this.bed_mesh.mesh_max[1]
|
||||
const xStep = (xMax - xMin) / (xCount - 1)
|
||||
const yStep = (yMax - yMin) / (yCount - 1)
|
||||
|
||||
const data: any[] = []
|
||||
|
||||
let yPoint = 0
|
||||
this.bed_mesh.probed_matrix.forEach((meshRow: number[]) => {
|
||||
let xPoint = 0
|
||||
meshRow.forEach((value: number) => {
|
||||
data.push([xMin + xStep * xPoint, yMin + yStep * yPoint, value])
|
||||
xPoint++
|
||||
})
|
||||
yPoint++
|
||||
})
|
||||
|
||||
serie.data = data
|
||||
serie.dataShape = [yCount, xCount]
|
||||
}
|
||||
|
||||
return serie
|
||||
}
|
||||
|
||||
get seriesMesh(): HeightmapSerie {
|
||||
const serie: HeightmapSerie = {
|
||||
type: 'surface',
|
||||
name: 'mesh',
|
||||
data: [],
|
||||
itemStyle: { opacity: 1 },
|
||||
wireframe: { show: this.wireframe },
|
||||
}
|
||||
|
||||
if (this.bed_mesh) {
|
||||
const xCount = this.bed_mesh.mesh_matrix[0].length
|
||||
const yCount = this.bed_mesh.mesh_matrix.length
|
||||
const xMin = this.bed_mesh.mesh_min[0]
|
||||
const xMax = this.bed_mesh.mesh_max[0]
|
||||
const yMin = this.bed_mesh.mesh_min[1]
|
||||
const yMax = this.bed_mesh.mesh_max[1]
|
||||
const xStep = (xMax - xMin) / (xCount - 1)
|
||||
const yStep = (yMax - yMin) / (yCount - 1)
|
||||
|
||||
const data: any[] = []
|
||||
|
||||
let yPoint = 0
|
||||
this.bed_mesh.mesh_matrix.forEach((meshRow: number[]) => {
|
||||
let xPoint = 0
|
||||
meshRow.forEach((value: number) => {
|
||||
data.push([xMin + xStep * xPoint, yMin + yStep * yPoint, value])
|
||||
xPoint++
|
||||
})
|
||||
yPoint++
|
||||
})
|
||||
|
||||
serie.data = data
|
||||
serie.dataShape = [yCount, xCount]
|
||||
}
|
||||
|
||||
return serie
|
||||
}
|
||||
|
||||
get seriesFlat(): HeightmapSerie {
|
||||
const serie: HeightmapSerie = {
|
||||
type: 'surface',
|
||||
name: 'flat',
|
||||
data: [],
|
||||
itemStyle: {
|
||||
color: [1, 1, 1, 1],
|
||||
opacity: 0.5,
|
||||
},
|
||||
wireframe: { show: this.wireframe },
|
||||
}
|
||||
|
||||
const config = this.$store.state.printer.configfile?.settings?.bed_mesh
|
||||
if (config) {
|
||||
let probe_count = [1, 1]
|
||||
if (config.probe_count && typeof config.probe_count === 'string') {
|
||||
probe_count = config.probe_count.split(',')
|
||||
} else if (config.probe_count) {
|
||||
probe_count =
|
||||
config.probe_count.length < 2 ? [config.probe_count, config.probe_count] : config.probe_count
|
||||
} else if (config.round_probe_count) {
|
||||
probe_count = [config.round_probe_count, config.round_probe_count]
|
||||
}
|
||||
|
||||
let mesh_min = config.mesh_min ?? [0, 0]
|
||||
let mesh_max = config.mesh_max ?? [200, 200]
|
||||
|
||||
if ('mesh_radius' in config) {
|
||||
// delta min/max
|
||||
mesh_min = [config.mesh_radius * -1, config.mesh_radius * -1]
|
||||
|
||||
mesh_max = [config.mesh_radius, config.mesh_radius]
|
||||
}
|
||||
|
||||
const xCount = probe_count[0]
|
||||
const yCount = probe_count[1]
|
||||
const xMin = parseFloat(mesh_min[0])
|
||||
const xMax = parseFloat(mesh_max[0])
|
||||
const yMin = parseFloat(mesh_min[1])
|
||||
const yMax = parseFloat(mesh_max[1])
|
||||
const xStep = (xMax - xMin) / (xCount - 1)
|
||||
const yStep = (yMax - yMin) / (yCount - 1)
|
||||
|
||||
const data: number[][] = []
|
||||
|
||||
for (let y = 0; y < yCount; y++) {
|
||||
for (let x = 0; x < xCount; x++) {
|
||||
data.push([xMin + xStep * x, yMin + yStep * y, 0])
|
||||
}
|
||||
}
|
||||
|
||||
serie.data = data
|
||||
serie.dataShape = [yCount, xCount]
|
||||
}
|
||||
|
||||
return serie
|
||||
}
|
||||
|
||||
get colorMap(): string[] {
|
||||
return this.$store.getters['gui/heightmap/getActiveColorSchemeList']
|
||||
}
|
||||
|
||||
get visualMapSeriesIndex(): number[] {
|
||||
const output = []
|
||||
|
||||
if (this.showProbed) output.push(0)
|
||||
else if (this.showMesh) output.push(1)
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
get visualMapRange(): number[] {
|
||||
if (!this.scaleGradient) return [-0.1, 0.1]
|
||||
|
||||
return this.heightmapLimit
|
||||
}
|
||||
|
||||
get heightmapLimit(): number[] {
|
||||
let min = 0
|
||||
let max = 0
|
||||
|
||||
if (this.bed_mesh) {
|
||||
const points = []
|
||||
if (this.showProbed) {
|
||||
for (const row of this.bed_mesh.probed_matrix) for (const col of row) points.push(col)
|
||||
}
|
||||
if (this.showMesh) {
|
||||
for (const row of this.bed_mesh.mesh_matrix) for (const col of row) points.push(col)
|
||||
}
|
||||
|
||||
min = Math.min(min, ...points)
|
||||
max = Math.max(max, ...points)
|
||||
}
|
||||
|
||||
return [min, max]
|
||||
}
|
||||
|
||||
get rangeX(): number[] {
|
||||
const axis_minimum = this.$store.state.printer.toolhead?.axis_minimum
|
||||
const axis_maximum = this.$store.state.printer.toolhead?.axis_maximum
|
||||
|
||||
return [axis_minimum[0] ?? 0, axis_maximum[0] ?? 0]
|
||||
}
|
||||
|
||||
get rangeY(): number[] {
|
||||
const axis_minimum = this.$store.state.printer.toolhead?.axis_minimum ?? [0, 0]
|
||||
const axis_maximum = this.$store.state.printer.toolhead?.axis_maximum ?? [0, 0]
|
||||
|
||||
return [axis_minimum[1] ?? 0, axis_maximum[1] ?? 0]
|
||||
}
|
||||
|
||||
get absRangeX(): number {
|
||||
return this.rangeX[1] - this.rangeX[0]
|
||||
}
|
||||
|
||||
get absRangeY(): number {
|
||||
return this.rangeY[1] - this.rangeY[0]
|
||||
}
|
||||
|
||||
get minRangeXY(): number {
|
||||
return Math.min(this.absRangeX, this.absRangeY)
|
||||
}
|
||||
|
||||
get scaleX(): number {
|
||||
if (this.minRangeXY === 0) return 1
|
||||
|
||||
return this.absRangeX / this.minRangeXY
|
||||
}
|
||||
|
||||
get scaleY(): number {
|
||||
if (this.minRangeXY === 0) return 1
|
||||
|
||||
return this.absRangeY / this.minRangeXY
|
||||
}
|
||||
|
||||
tooltipFormatter(data: any): string {
|
||||
const outputArray: string[] = []
|
||||
outputArray.push(`<b>${data.seriesName}</b>`)
|
||||
|
||||
Object.keys(data.encode)
|
||||
.sort()
|
||||
.forEach((axisName: string) => {
|
||||
const value = data.data[data.encode[axisName][0]].toFixed(axisName === 'z' ? 3 : 1)
|
||||
|
||||
outputArray.push(`<b>${axisName.toUpperCase()}</b>: ${value} mm`)
|
||||
})
|
||||
|
||||
return outputArray.join('<br />')
|
||||
}
|
||||
|
||||
beforeDestroy(): void {
|
||||
if (typeof window === 'undefined') return
|
||||
if (this.chart) this.chart.dispose()
|
||||
}
|
||||
}
|
||||
</script>
|
88
src/components/dialogs/HeightmapCalibrateMeshDialog.vue
Normal file
88
src/components/dialogs/HeightmapCalibrateMeshDialog.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<v-dialog :value="show" persistent :max-width="400" @keydown.esc="closeDialog">
|
||||
<panel
|
||||
:title="$t('Heightmap.BedMeshCalibrate')"
|
||||
:icon="mdiGrid"
|
||||
card-class="heightmap-calibrate-dialog"
|
||||
:margin-bottom="false">
|
||||
<template #buttons>
|
||||
<v-btn icon tile @click="closeDialog">
|
||||
<v-icon>{{ mdiCloseThick }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
ref="input"
|
||||
v-model="name"
|
||||
:label="$t('Heightmap.Name')"
|
||||
required
|
||||
:rules="rules"
|
||||
@update:error="
|
||||
(newVal) => {
|
||||
isInvalidName = newVal
|
||||
}
|
||||
"
|
||||
@keyup.enter="calibrateMesh" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">{{ $t('Heightmap.Abort') }}</v-btn>
|
||||
<v-btn :disabled="isInvalidName" color="primary" text @click="calibrateMesh">
|
||||
{{ $t('Heightmap.Calibrate') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</panel>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop, Watch } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiCloseThick, mdiGrid } from '@mdi/js'
|
||||
|
||||
@Component
|
||||
export default class HeightmapRenameProfileDialog extends Mixins(BaseMixin) {
|
||||
mdiCloseThick = mdiCloseThick
|
||||
mdiGrid = mdiGrid
|
||||
|
||||
@Prop({ type: Boolean, required: true }) show!: boolean
|
||||
|
||||
$refs!: {
|
||||
input: HTMLInputElement
|
||||
}
|
||||
|
||||
isInvalidName = false
|
||||
name = ''
|
||||
|
||||
rules = [
|
||||
(value: string) => !!value || this.$t('Heightmap.InvalidNameEmpty'),
|
||||
// eslint-disable-next-line no-control-regex
|
||||
(value: string) => value === value.replace(/[^\x00-\x7F]/g, '') || this.$t('Heightmap.InvalidNameAscii'),
|
||||
]
|
||||
|
||||
calibrateMesh(): void {
|
||||
const gcode = `BED_MESH_CALIBRATE PROFILE="${this.name}"`
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: 'bedMeshCalibrate' })
|
||||
|
||||
this.closeDialog()
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.$emit('close')
|
||||
}
|
||||
|
||||
@Watch('show')
|
||||
showChanged() {
|
||||
if (this.show) {
|
||||
this.name = 'default'
|
||||
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs.input?.focus()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
50
src/components/dialogs/HeightmapRemoveProfileDialog.vue
Normal file
50
src/components/dialogs/HeightmapRemoveProfileDialog.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<v-dialog :value="show" persistent :max-width="400" @keydown.esc="closeDialog">
|
||||
<panel
|
||||
:title="$t('Heightmap.BedMeshRemove')"
|
||||
:icon="mdiGrid"
|
||||
card-class="heightmap-remove-dialog"
|
||||
:margin-bottom="false">
|
||||
<template #buttons>
|
||||
<v-btn icon tile @click="closeDialog">
|
||||
<v-icon>{{ mdiCloseThick }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card-text>
|
||||
<p class="mb-0">{{ $t('Heightmap.DoYouReallyWantToDelete', { name }) }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">{{ $t('Heightmap.Abort') }}</v-btn>
|
||||
<v-btn color="error" text @click="removeProfile">{{ $t('Heightmap.Remove') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</panel>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiCloseThick, mdiGrid } from '@mdi/js'
|
||||
|
||||
@Component
|
||||
export default class HeightmapRemoveProfileDialog extends Mixins(BaseMixin) {
|
||||
mdiCloseThick = mdiCloseThick
|
||||
mdiGrid = mdiGrid
|
||||
|
||||
@Prop({ type: Boolean, required: true }) show!: boolean
|
||||
@Prop({ type: String, required: true }) name!: string
|
||||
|
||||
removeProfile() {
|
||||
const gcode = `BED_MESH_PROFILE REMOVE="${this.name}"`
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: 'bedMeshRemove' })
|
||||
|
||||
this.closeDialog()
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
</script>
|
95
src/components/dialogs/HeightmapRenameProfileDialog.vue
Normal file
95
src/components/dialogs/HeightmapRenameProfileDialog.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<v-dialog :value="show" persistent :max-width="400" @keydown.esc="closeDialog">
|
||||
<panel
|
||||
:title="$t('Heightmap.RenameBedMeshProfile')"
|
||||
:icon="mdiGrid"
|
||||
card-class="heightmap-rename-dialog"
|
||||
:margin-bottom="false">
|
||||
<template #buttons>
|
||||
<v-btn icon tile @click="closeDialog">
|
||||
<v-icon>{{ mdiCloseThick }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
ref="input"
|
||||
v-model="newName"
|
||||
:label="$t('Heightmap.Name')"
|
||||
required
|
||||
:rules="rules"
|
||||
@update:error="
|
||||
(newVal) => {
|
||||
isInvalidName = newVal
|
||||
}
|
||||
"
|
||||
@keyup.enter="renameProfile" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">{{ $t('Heightmap.Abort') }}</v-btn>
|
||||
<v-btn :disabled="isInvalidName" color="primary" text @click="renameProfile">
|
||||
{{ $t('Heightmap.Rename') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</panel>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop, Watch } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiCloseThick, mdiGrid } from '@mdi/js'
|
||||
|
||||
@Component
|
||||
export default class HeightmapRenameProfileDialog extends Mixins(BaseMixin) {
|
||||
mdiCloseThick = mdiCloseThick
|
||||
mdiGrid = mdiGrid
|
||||
|
||||
@Prop({ type: Boolean, required: true }) show!: boolean
|
||||
@Prop({ type: String, required: true }) name!: string
|
||||
|
||||
$refs!: {
|
||||
input: HTMLInputElement
|
||||
}
|
||||
|
||||
isInvalidName = false
|
||||
newName = ''
|
||||
|
||||
rules = [
|
||||
(value: string) => !!value || this.$t('Heightmap.InvalidNameEmpty'),
|
||||
(value: string) => value !== 'default' || this.$t('Heightmap.InvalidNameReserved'),
|
||||
(value: string) => !this.profileNames.includes(value) || this.$t('Heightmap.InvalidNameAlreadyExists'),
|
||||
// eslint-disable-next-line no-control-regex
|
||||
(value: string) => value === value.replace(/[^\x00-\x7F]/g, '') || this.$t('Heightmap.InvalidNameAscii'),
|
||||
]
|
||||
|
||||
get profileNames() {
|
||||
return Object.keys(this.$store.state.printer.bed_mesh?.profiles ?? {})
|
||||
}
|
||||
|
||||
renameProfile() {
|
||||
const gcode = `BED_MESH_PROFILE SAVE="${this.newName}"\nBED_MESH_PROFILE REMOVE="${this.name}"`
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: 'bedMeshRename' })
|
||||
|
||||
this.closeDialog()
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.$emit('close')
|
||||
}
|
||||
|
||||
@Watch('show')
|
||||
showChanged() {
|
||||
if (this.show) {
|
||||
this.newName = this.name
|
||||
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs.input?.focus()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
62
src/components/mixins/bedmesh.ts
Normal file
62
src/components/mixins/bedmesh.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import Vue from 'vue'
|
||||
import Component from 'vue-class-component'
|
||||
|
||||
@Component
|
||||
export default class BedmeshMixin extends Vue {
|
||||
get bed_mesh() {
|
||||
return this.$store.state.printer.bed_mesh ?? {}
|
||||
}
|
||||
|
||||
get profiles() {
|
||||
return this.bed_mesh.profiles ?? {}
|
||||
}
|
||||
|
||||
get mesh_min() {
|
||||
return this.bed_mesh.mesh_min ?? [0, 0]
|
||||
}
|
||||
|
||||
get mesh_max() {
|
||||
return this.bed_mesh.mesh_max ?? [0, 0]
|
||||
}
|
||||
|
||||
get min() {
|
||||
return Math.min(...this.points)
|
||||
}
|
||||
|
||||
get max() {
|
||||
return Math.max(...this.points)
|
||||
}
|
||||
|
||||
get variance() {
|
||||
return Math.abs(this.min - this.max).toFixed(3)
|
||||
}
|
||||
|
||||
get is_active() {
|
||||
// if the current profile_mane is not empty, return true
|
||||
if (this.bed_mesh.profile_name !== '') return true
|
||||
|
||||
return this.mesh_min[0] !== 0 || this.mesh_min[1] !== 0 || this.mesh_max[0] !== 0 || this.mesh_max[1] !== 0
|
||||
}
|
||||
|
||||
get name() {
|
||||
if (this.bed_mesh.profile_name !== '') return this.bed_mesh.profile_name
|
||||
|
||||
return 'Unknown'
|
||||
}
|
||||
|
||||
get probed_matrix() {
|
||||
return this.bed_mesh.probed_matrix ?? []
|
||||
}
|
||||
|
||||
get points() {
|
||||
const points: number[] = []
|
||||
|
||||
for (let i = 0; i < this.probed_matrix.length; i++) {
|
||||
for (let j = 0; j < this.probed_matrix[i].length; j++) {
|
||||
points.push(this.probed_matrix[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
}
|
220
src/components/panels/Heightmap/HeightmapChartPanel.vue
Normal file
220
src/components/panels/Heightmap/HeightmapChartPanel.vue
Normal file
@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<panel card-class="heightmap-map-panel" :title="$t('Heightmap.Heightmap')" :icon="mdiGrid">
|
||||
<template #buttons>
|
||||
<v-btn
|
||||
icon
|
||||
tile
|
||||
class="d-none d-sm-flex"
|
||||
:disabled="printerIsPrinting"
|
||||
:color="homedAxes.includes('xyz') ? 'primary' : 'warning'"
|
||||
:loading="loadings.includes('homeAll')"
|
||||
:title="$t('Heightmap.TitleHomeAll')"
|
||||
:ripple="true"
|
||||
@click="homePrinter">
|
||||
<v-icon>{{ mdiHome }}</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="is_active"
|
||||
text
|
||||
tile
|
||||
class="d-none d-sm-flex"
|
||||
:loading="loadings.includes('bedMeshClear')"
|
||||
:title="$t('Heightmap.TitleClear')"
|
||||
@click="clearBedMesh">
|
||||
{{ $t('Heightmap.Clear') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
tile
|
||||
class="d-none d-sm-flex"
|
||||
:loading="loadings.includes('bedMeshCalibrate')"
|
||||
:disabled="printerIsPrinting"
|
||||
:title="$t('Heightmap.TitleCalibrate')"
|
||||
@click="calibrateDialog = true">
|
||||
{{ $t('Heightmap.Calibrate') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card-text class="d-sm-none text-center pb-0">
|
||||
<v-item-group tile class="v-btn-toggle" name="controllers">
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
class="px-2 minwidth-0"
|
||||
:disabled="printerIsPrinting"
|
||||
:color="homedAxes.includes('xyz') ? 'primary' : 'warning'"
|
||||
:loading="loadings.includes('homeAll')"
|
||||
:title="$t('Heightmap.TitleHomeAll')"
|
||||
@click="homePrinter">
|
||||
<v-icon :color="homedAxes.includes('xyz') ? 'primary' : 'warning'" small>
|
||||
{{ mdiHome }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="bed_mesh"
|
||||
text
|
||||
small
|
||||
class="px-2 minwidth-0"
|
||||
color="primary"
|
||||
:loading="loadings.includes('bedMeshClear')"
|
||||
:title="$t('Heightmap.TitleClear')"
|
||||
@click="clearBedMesh">
|
||||
{{ $t('Heightmap.Clear') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
class="px-2 minwidth-0"
|
||||
color="primary"
|
||||
:loading="loadings.includes('bedMeshCalibrate')"
|
||||
:disabled="printerIsPrinting"
|
||||
:title="$t('Heightmap.TitleCalibrate')"
|
||||
@click="calibrateDialog = true">
|
||||
{{ $t('Heightmap.Calibrate') }}
|
||||
</v-btn>
|
||||
</v-item-group>
|
||||
</v-card-text>
|
||||
<template v-if="!is_active">
|
||||
<v-card-text class="text-center py-3 font-italic">
|
||||
{{ $t('Heightmap.NoBedMeshHasBeenLoadedYet') }}
|
||||
</v-card-text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-card-text class="py-0 px-0">
|
||||
<v-row>
|
||||
<v-col class="">
|
||||
<heightmap-chart
|
||||
:show-probed="showProbed"
|
||||
:show-mesh="showMesh"
|
||||
:show-flat="showFlat"
|
||||
:wireframe="wireframe"
|
||||
:scale-gradient="scaleGradient"
|
||||
:scale-z-max="scaleZMax" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col class="col-12 col-sm-auto pt-0 pb-0 pl-lg-6 d-flex justify-center justify-sm-start">
|
||||
<v-switch v-model="scaleGradient" :label="$t('Heightmap.ScaleGradient')" class="mt-0 ml-5" />
|
||||
</v-col>
|
||||
<v-col class="d-flex justify-center pt-0 pb-6 pb-lg-3">
|
||||
<v-checkbox
|
||||
v-model="showProbed"
|
||||
:label="$t('Heightmap.Probed')"
|
||||
hide-details
|
||||
class="mx-3 mt-0" />
|
||||
<v-checkbox v-model="showMesh" :label="$t('Heightmap.Mesh')" hide-details class="mx-3 mt-0" />
|
||||
<v-checkbox v-model="showFlat" :label="$t('Heightmap.Flat')" hide-details class="mx-3 mt-0" />
|
||||
<v-checkbox
|
||||
v-model="wireframe"
|
||||
:label="$t('Heightmap.Wireframe')"
|
||||
hide-details
|
||||
class="mx-3 mt-0" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-text class="pt-0 pb-3">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-slider
|
||||
v-model="scaleZMax"
|
||||
:label="$t('Heightmap.ScaleZMax')"
|
||||
:min="heightmapRangeLimit[0]"
|
||||
:max="heightmapRangeLimit[1]"
|
||||
:step="0.1"
|
||||
ticks="always"
|
||||
class="mt-4"
|
||||
hide-details />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</template>
|
||||
<heightmap-calibrate-mesh-dialog :show="calibrateDialog" @close="calibrateDialog = false" />
|
||||
</panel>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiGrid, mdiHome } from '@mdi/js'
|
||||
import ControlMixin from '@/components/mixins/control'
|
||||
import BedmeshMixin from '@/components/mixins/bedmesh'
|
||||
import HeightmapCalibrateMeshDialog from '@/components/dialogs/HeightmapCalibrateMeshDialog.vue'
|
||||
|
||||
@Component({
|
||||
components: { HeightmapCalibrateMeshDialog },
|
||||
})
|
||||
export default class HeightmapChartPanel extends Mixins(BaseMixin, ControlMixin, BedmeshMixin) {
|
||||
mdiGrid = mdiGrid
|
||||
mdiHome = mdiHome
|
||||
|
||||
calibrateDialog = false
|
||||
|
||||
get showProbed(): boolean {
|
||||
return this.$store.state.gui.view.heightmap.probed ?? true
|
||||
}
|
||||
|
||||
set showProbed(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.probed', value: newVal })
|
||||
}
|
||||
|
||||
get showMesh(): boolean {
|
||||
return this.$store.state.gui.view.heightmap.mesh ?? true
|
||||
}
|
||||
|
||||
set showMesh(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.mesh', value: newVal })
|
||||
}
|
||||
|
||||
get showFlat(): boolean {
|
||||
return this.$store.state.gui.view.heightmap.flat ?? true
|
||||
}
|
||||
|
||||
set showFlat(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.flat', value: newVal })
|
||||
}
|
||||
|
||||
get wireframe(): boolean {
|
||||
return this.$store.state.gui.view.heightmap.wireframe ?? true
|
||||
}
|
||||
|
||||
set wireframe(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.wireframe', value: newVal })
|
||||
}
|
||||
|
||||
get scaleGradient(): boolean {
|
||||
return this.$store.state.gui.view.heightmap.scaleGradient ?? false
|
||||
}
|
||||
|
||||
set scaleGradient(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.scaleGradient', value: newVal })
|
||||
}
|
||||
|
||||
get scaleZMax(): number {
|
||||
return this.$store.state.gui.view.heightmap.scaleZMax ?? 0.5
|
||||
}
|
||||
|
||||
set scaleZMax(newVal) {
|
||||
this.$store.dispatch('gui/saveSetting', { name: 'view.heightmap.scaleZMax', value: newVal })
|
||||
}
|
||||
|
||||
get heightmapRangeLimit(): number[] {
|
||||
const minRange = Math.round(Math.max(Math.abs(this.min), Math.abs(this.max)) * 10) / 10
|
||||
const maxRange = Math.max(minRange, 1)
|
||||
|
||||
return [minRange, maxRange]
|
||||
}
|
||||
|
||||
homePrinter(): void {
|
||||
const gcode = 'G28'
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: 'homeAll' })
|
||||
}
|
||||
|
||||
clearBedMesh(): void {
|
||||
const gcode = 'BED_MESH_CLEAR'
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: 'bedMeshClear' })
|
||||
}
|
||||
}
|
||||
</script>
|
142
src/components/panels/Heightmap/HeightmapCurrentProfilePanel.vue
Normal file
142
src/components/panels/Heightmap/HeightmapCurrentProfilePanel.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<panel
|
||||
v-if="is_active"
|
||||
:title="$t('Heightmap.CurrentMesh.Headline')"
|
||||
card-class="heightmap-current-mesh-panel"
|
||||
:icon="mdiInformation"
|
||||
:collapsible="true"
|
||||
class="mt-0">
|
||||
<v-card-text class="py-3 px-0">
|
||||
<v-row class="px-3">
|
||||
<v-col>{{ $t('Heightmap.CurrentMesh.Name') }}</v-col>
|
||||
<v-col class="text-right">
|
||||
<span
|
||||
v-if="!name.startsWith('adaptive-')"
|
||||
class="currentMeshName cursor-pointer font-weight-bold"
|
||||
@click="showRename = true">
|
||||
<v-icon left small color="primary">{{ mdiPencil }}</v-icon>
|
||||
{{ name }}
|
||||
</span>
|
||||
<span v-else class="font-weight-bold">{{ name }}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-divider class="my-3" />
|
||||
<v-row class="px-3">
|
||||
<v-col>{{ $t('Heightmap.CurrentMesh.Size') }}</v-col>
|
||||
<v-col class="text-right">{{ x_count }}x{{ y_count }}</v-col>
|
||||
</v-row>
|
||||
<v-divider class="my-3" />
|
||||
<v-row v-if="index_max > -1" class="px-3">
|
||||
<v-col>
|
||||
{{ $t('Heightmap.CurrentMesh.Max') }} [{{ position_max_x.toFixed(1) }},
|
||||
{{ position_max_y.toFixed(1) }}]
|
||||
</v-col>
|
||||
<v-col class="text-right">{{ max.toFixed(3) }} mm</v-col>
|
||||
</v-row>
|
||||
<v-divider class="my-3" />
|
||||
<v-row class="px-3">
|
||||
<v-col>
|
||||
{{ $t('Heightmap.CurrentMesh.Min') }} [{{ position_min_x.toFixed(1) }},
|
||||
{{ position_min_y.toFixed(1) }}]
|
||||
</v-col>
|
||||
<v-col class="text-right">{{ min.toFixed(3) }} mm</v-col>
|
||||
</v-row>
|
||||
<v-divider class="my-3" />
|
||||
<v-row class="px-3">
|
||||
<v-col>{{ $t('Heightmap.CurrentMesh.Range') }}</v-col>
|
||||
<v-col class="text-right">{{ variance }} mm</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<heightmap-rename-profile-dialog :show="showRename" :name="name" @close="showRename = false" />
|
||||
</panel>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiInformation, mdiPencil } from '@mdi/js'
|
||||
import BedmeshMixin from '@/components/mixins/bedmesh'
|
||||
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
export default class HeightmapCurrentProfilePanel extends Mixins(BaseMixin, BedmeshMixin) {
|
||||
mdiInformation = mdiInformation
|
||||
mdiPencil = mdiPencil
|
||||
|
||||
showRename = false
|
||||
|
||||
get x_count() {
|
||||
return this.bed_mesh.probed_matrix[0]?.length ?? 0
|
||||
}
|
||||
|
||||
get y_count() {
|
||||
return this.bed_mesh.probed_matrix?.length ?? 0
|
||||
}
|
||||
|
||||
get x_step_size() {
|
||||
if (this.x_count < 1) return 0
|
||||
|
||||
return (this.mesh_max[0] - this.mesh_min[0]) / (this.x_count - 1)
|
||||
}
|
||||
|
||||
get y_step_size() {
|
||||
if (this.y_count < 1) return 0
|
||||
|
||||
return (this.mesh_max[1] - this.mesh_min[1]) / (this.y_count - 1)
|
||||
}
|
||||
|
||||
get index_max() {
|
||||
return this.points.indexOf(this.max)
|
||||
}
|
||||
|
||||
get index_max_y() {
|
||||
return Math.trunc(this.index_max / this.x_count)
|
||||
}
|
||||
|
||||
get index_max_x() {
|
||||
return this.index_max % this.y_count
|
||||
}
|
||||
|
||||
get position_max_x() {
|
||||
return this.mesh_min[0] + this.index_max_x * this.x_step_size
|
||||
}
|
||||
|
||||
get position_max_y() {
|
||||
return this.mesh_min[1] + this.index_max_y * this.y_step_size
|
||||
}
|
||||
|
||||
get index_min() {
|
||||
return this.points.indexOf(this.min)
|
||||
}
|
||||
|
||||
get index_min_y() {
|
||||
return Math.trunc(this.index_min / this.x_count)
|
||||
}
|
||||
|
||||
get index_min_x() {
|
||||
return this.index_min % this.y_count
|
||||
}
|
||||
|
||||
get position_min_x() {
|
||||
return this.mesh_min[0] + this.index_min_x * this.x_step_size
|
||||
}
|
||||
|
||||
get position_min_y() {
|
||||
return this.mesh_min[1] + this.index_min_y * this.y_step_size
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.currentMeshName {
|
||||
color: var(--v-primary-base);
|
||||
|
||||
.v-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .v-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
35
src/components/panels/Heightmap/HeightmapProfilesPanel.vue
Normal file
35
src/components/panels/Heightmap/HeightmapProfilesPanel.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<panel
|
||||
:title="$t('Heightmap.Profiles')"
|
||||
card-class="heightmap-profiles-panel"
|
||||
:icon="mdiStackOverflow"
|
||||
:collapsible="true"
|
||||
class="mt-6 mt-md-0">
|
||||
<v-card-text v-if="Object.keys(profiles).length" class="px-0 py-3">
|
||||
<template v-for="(profile, name, index) in profiles">
|
||||
<v-divider v-if="index" :key="`deliver_${name}`" class="my-3" />
|
||||
<heightmap-profiles-panel-row :key="`profile_${name}`" :profile="profile" :name="name" />
|
||||
</template>
|
||||
</v-card-text>
|
||||
<v-card-text v-else>
|
||||
<p class="mb-0">{{ $t('Heightmap.NoProfile') }}</p>
|
||||
</v-card-text>
|
||||
</panel>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiStackOverflow } from '@mdi/js'
|
||||
import HeightmapProfilesPanelRow from '@/components/panels/Heightmap/HeightmapProfilesPanelRow.vue'
|
||||
|
||||
@Component({
|
||||
components: { HeightmapProfilesPanelRow },
|
||||
})
|
||||
export default class HeightmapProfilesPanel extends Mixins(BaseMixin) {
|
||||
mdiStackOverflow = mdiStackOverflow
|
||||
|
||||
get profiles() {
|
||||
return this.$store.state.printer.bed_mesh?.profiles ?? {}
|
||||
}
|
||||
}
|
||||
</script>
|
148
src/components/panels/Heightmap/HeightmapProfilesPanelRow.vue
Normal file
148
src/components/panels/Heightmap/HeightmapProfilesPanelRow.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<v-row class="rowProfile">
|
||||
<v-col class="pl-6">
|
||||
<span
|
||||
:class="{ 'font-weight-bold': is_active, currentMeshName: is_active, 'cursor-pointer': true }"
|
||||
@click="clickOnName">
|
||||
{{ name }}
|
||||
</span>
|
||||
</v-col>
|
||||
<v-col class="col-auto text-center d-flex align-center justify-center pr-6">
|
||||
<v-tooltip top color="rgba(0,0,0,0.8)">
|
||||
<template #activator="{ on, attrs }">
|
||||
<small v-bind="attrs" v-on="on">{{ variance }}</small>
|
||||
</template>
|
||||
<span>
|
||||
max: {{ max }}
|
||||
<br />
|
||||
min: {{ min }}
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col class="col-auto py-0 d-flex flex-row align-center justify-end">
|
||||
<v-btn
|
||||
v-if="!is_active"
|
||||
text
|
||||
tile
|
||||
class="px-2 minwidth-0"
|
||||
:loading="isLoadingLoad"
|
||||
style="height: 48px; width: 48px"
|
||||
@click="loadProfile">
|
||||
<v-icon>{{ mdiProgressUpload }}</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-else
|
||||
text
|
||||
tile
|
||||
class="px-2 minwidth-0"
|
||||
:loading="isLoadingLoad"
|
||||
style="height: 48px; width: 48px"
|
||||
@click="showRename = true">
|
||||
<v-icon>{{ mdiPencil }}</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
tile
|
||||
class="px-2 minwidth-0"
|
||||
style="height: 48px; width: 48px"
|
||||
:loading="isLoadingRemove"
|
||||
:title="$t('Heightmap.DeleteBedMeshProfile')"
|
||||
@click="showRemove = true">
|
||||
<v-icon>{{ mdiDelete }}</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<heightmap-remove-profile-dialog :show="showRemove" :name="name" @close="showRemove = false" />
|
||||
<heightmap-rename-profile-dialog :show="showRename" :name="name" @close="showRename = false" />
|
||||
</v-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import { mdiDelete, mdiPencil, mdiProgressUpload } from '@mdi/js'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { PrinterStateBedMeshProfile } from '@/store/printer/types'
|
||||
import HeightmapRenameProfileDialog from '@/components/dialogs/HeightmapRenameProfileDialog.vue'
|
||||
import HeightmapRemoveProfileDialog from '@/components/dialogs/HeightmapRemoveProfileDialog.vue'
|
||||
|
||||
@Component({
|
||||
components: { HeightmapRenameProfileDialog, HeightmapRemoveProfileDialog },
|
||||
})
|
||||
export default class HeightmapProfilesPanelRow extends Mixins(BaseMixin) {
|
||||
mdiDelete = mdiDelete
|
||||
mdiPencil = mdiPencil
|
||||
mdiProgressUpload = mdiProgressUpload
|
||||
|
||||
@Prop({ type: String, required: true }) name!: string
|
||||
@Prop({ type: Object, required: true }) profile!: PrinterStateBedMeshProfile
|
||||
|
||||
showRemove = false
|
||||
showRename = false
|
||||
|
||||
get points() {
|
||||
const points: number[] = []
|
||||
|
||||
for (let i = 0; i < this.profile.points.length; i++) {
|
||||
for (let j = 0; j < this.profile.points[i].length; j++) {
|
||||
points.push(this.profile.points[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
get min() {
|
||||
return Math.round(Math.min(...this.points) * 1000) / 1000
|
||||
}
|
||||
|
||||
get max() {
|
||||
return Math.round(Math.max(...this.points) * 1000) / 1000
|
||||
}
|
||||
|
||||
get variance() {
|
||||
return Math.abs(this.min - this.max).toFixed(3)
|
||||
}
|
||||
|
||||
get is_active() {
|
||||
const currentProfile = this.$store.state.printer.bed_mesh?.profile_name ?? ''
|
||||
|
||||
return currentProfile === this.name
|
||||
}
|
||||
|
||||
get loadingNameLoad() {
|
||||
return `bedMeshLoad_${this.name}`
|
||||
}
|
||||
|
||||
get loadingNameRemove() {
|
||||
return `bedMeshRemove_${this.name}`
|
||||
}
|
||||
|
||||
get isLoadingLoad() {
|
||||
return this.loadings.includes(this.loadingNameLoad)
|
||||
}
|
||||
|
||||
get isLoadingRemove() {
|
||||
return this.loadings.includes(this.loadingNameRemove)
|
||||
}
|
||||
|
||||
clickOnName() {
|
||||
if (this.is_active) {
|
||||
this.showRename = true
|
||||
return
|
||||
}
|
||||
|
||||
this.loadProfile()
|
||||
}
|
||||
|
||||
loadProfile(): void {
|
||||
const gcode = `BED_MESH_PROFILE LOAD="${this.name}"`
|
||||
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode }, { loading: this.loadingNameLoad })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.currentMeshName {
|
||||
color: var(--v-primary-base);
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@ import { checkKlipperConfigModules } from '@/store/variables'
|
||||
import { GetterTree } from 'vuex'
|
||||
import {
|
||||
PrinterState,
|
||||
PrinterStateBedMesh,
|
||||
PrinterStateExtruder,
|
||||
PrinterStateExtruderStepper,
|
||||
PrinterStateFan,
|
||||
@ -526,38 +525,6 @@ export const getters: GetterTree<PrinterState, RootState> = {
|
||||
return output
|
||||
},
|
||||
|
||||
getBedMeshProfiles: (state) => {
|
||||
const profiles: PrinterStateBedMesh[] = []
|
||||
let currentProfile = ''
|
||||
if (state.bed_mesh) currentProfile = state.bed_mesh.profile_name
|
||||
|
||||
if (state.bed_mesh && 'profiles' in state.bed_mesh) {
|
||||
Object.keys(state.bed_mesh?.profiles).forEach((key) => {
|
||||
const value: any = state.bed_mesh.profiles[key]
|
||||
|
||||
let points: number[] = []
|
||||
value.points.forEach((row: number[]) => {
|
||||
points = points.concat(row)
|
||||
})
|
||||
|
||||
const min = Math.min(...points)
|
||||
const max = Math.max(...points)
|
||||
|
||||
profiles.push({
|
||||
name: key,
|
||||
data: { ...value.mesh_params, points: value.points },
|
||||
points: points,
|
||||
min: min,
|
||||
max: max,
|
||||
variance: Math.abs(min - max),
|
||||
is_active: currentProfile === key,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return caseInsensitiveSort(profiles, 'name')
|
||||
},
|
||||
|
||||
getExtruders: (state) => {
|
||||
const extruders: PrinterStateExtruder[] = []
|
||||
if (state.configfile?.settings) {
|
||||
|
@ -157,26 +157,30 @@ export interface PrinterStateFilamentSensors {
|
||||
}
|
||||
|
||||
export interface PrinterStateBedMesh {
|
||||
name: string
|
||||
data: {
|
||||
algo: string
|
||||
max_x: number
|
||||
max_y: number
|
||||
mesh_x_pps: number
|
||||
mesh_y_pps: number
|
||||
profile_name: string
|
||||
mesh_min: [number, number]
|
||||
mesh_max: [number, number]
|
||||
probed_matrix: number[][]
|
||||
mesh_matrix: number[][]
|
||||
profiles: {
|
||||
[key: string]: PrinterStateBedMeshProfile
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrinterStateBedMeshProfile {
|
||||
points: number[][]
|
||||
mesh_params: {
|
||||
min_x: number
|
||||
max_x: number
|
||||
min_y: number
|
||||
points: { [key: number]: number[] }
|
||||
tension: number
|
||||
version: number
|
||||
max_y: number
|
||||
x_count: number
|
||||
y_count: number
|
||||
mesh_x_pps: number
|
||||
mesh_y_pps: number
|
||||
algo: 'bicubic' | 'lagrange'
|
||||
tension: number
|
||||
}
|
||||
points: number[]
|
||||
min: number
|
||||
max: number
|
||||
variance: number
|
||||
is_active: boolean
|
||||
}
|
||||
|
||||
export interface PrinterStateMacroParam {
|
||||
|
Loading…
x
Reference in New Issue
Block a user