244 lines
6.9 KiB
Vue
244 lines
6.9 KiB
Vue
<template>
|
|
<Dropzone
|
|
v-slot="{isDragOver}"
|
|
class="map-control"
|
|
:class="{
|
|
'map-control--over': isDragOver
|
|
}"
|
|
@dropFile="onDrop"
|
|
@mousewheel="onScroll"
|
|
@mousedown="onMouseDown"
|
|
>
|
|
<button @click="reset()">reset</button>
|
|
<select v-model="mode">
|
|
<option value="drag">drag</option>
|
|
<option value="draw">draw</option>
|
|
<option value="measure">measure</option>
|
|
</select>
|
|
<select
|
|
v-if="canvasDisplay?.mapLayer"
|
|
v-model="canvasDisplay.mapLayer.grid"
|
|
>
|
|
<option value="none">no grid</option>
|
|
<option value="light">light grid</option>
|
|
<option value="dark">dark grid</option>
|
|
</select>
|
|
<select
|
|
v-if="canvasDisplay?.mapLayer"
|
|
v-model="canvasDisplay.mapLayer.rotation"
|
|
>
|
|
<option :value="0">0°</option>
|
|
<option :value="1">90°</option>
|
|
<option :value="2">180°</option>
|
|
<option :value="3">270°</option>
|
|
</select>
|
|
<input
|
|
v-if="canvasDisplay?.mapLayer"
|
|
type="number"
|
|
v-model="canvasDisplay.mapLayer.mapWidthInInches"
|
|
/>
|
|
<div ref="rendererWrapper" v-show="canvasDisplay">
|
|
<canvas :width="selectedScreen?.imageSink?.resolution.width" :height="selectedScreen?.imageSink?.resolution.height" ref="canvas" style="max-width: 100%;" />
|
|
</div>
|
|
</Dropzone>
|
|
|
|
</template>
|
|
<script setup lang="ts">
|
|
import {computed, inject, onBeforeUnmount, onMounted, reactive, ref} from 'vue';
|
|
import Dropzone from '../Dropzone/Dropzone.vue';
|
|
import MapRenderer from './MapRenderer.vue';
|
|
import {MountedFsSymbol, RemoteDeviceManagerSymbol} from '../../model/injectionSymbols';
|
|
import {closerToZero, MapDrawable, MapImage} from './helpers';
|
|
import {VirtualFile} from '../../model/FileSystem/types';
|
|
import {CanvasDisplay, MapLayerDrawable} from './object';
|
|
import {Devices} from '../../model/Remote/RemoteDeviceManager';
|
|
|
|
const fs = inject(MountedFsSymbol)!;
|
|
const remoteManager = inject(RemoteDeviceManagerSymbol)!;
|
|
|
|
const screens = computed(() => {
|
|
return remoteManager.devices
|
|
.filter(device => device.hasImageSink)
|
|
})
|
|
|
|
const canvas = ref<HTMLCanvasElement>();
|
|
const selectedScreen = ref<Devices>();
|
|
const pointerVisible = ref(false);
|
|
let canvasDisplay: undefined | CanvasDisplay;
|
|
|
|
function selectScreen (screen: any) {
|
|
if (screen.imageSink?.display) {
|
|
selectedScreen.value = screen;
|
|
console.log('selectScreen!!!', screen.imageSink?.display);
|
|
canvasDisplay?.disconnectTarget(canvas.value!);
|
|
canvasDisplay = screen.imageSink?.display;
|
|
canvasDisplay?.connectTarget(canvas.value!);
|
|
}
|
|
}
|
|
|
|
async function onDrop(file: VirtualFile) {
|
|
console.log('onDrop!!', file);
|
|
onLoad(file);
|
|
}
|
|
|
|
const mode = ref<'drag' | 'draw' | 'measure'>('drag');
|
|
const dragging = ref(false);
|
|
|
|
|
|
|
|
function onScroll(event: WheelEvent) {
|
|
event.preventDefault();
|
|
// console.log('scroll', event.deltaY, event.offsetX, event.offsetY);
|
|
const delta = -1 * event.deltaY / 2000;
|
|
if (!canvasDisplay) {
|
|
return;
|
|
}
|
|
canvasDisplay.scale = Math.min(Math.max(0.1, canvasDisplay.scale + delta), 10);
|
|
|
|
if (canvas.value) {
|
|
if (delta > 0) {
|
|
const displayMousePosition = {
|
|
x: ((event.offsetX / canvas.value!.width) - 0.5) * 2 * canvasDisplay.width,
|
|
y: ((event.offsetY / canvas.value!.height) - 0.5) * 2 * canvasDisplay.height,
|
|
};
|
|
canvasDisplay.offsetX += displayMousePosition.x * delta;
|
|
canvasDisplay.offsetY += displayMousePosition.y * delta;
|
|
} else {
|
|
canvasDisplay.offsetX -= closerToZero((canvasDisplay.width * Math.abs(delta)) * Math.sign(canvasDisplay.offsetX), canvasDisplay.offsetX);
|
|
canvasDisplay.offsetY -= closerToZero((canvasDisplay.height * Math.abs(delta)) * Math.sign(canvasDisplay.offsetY), canvasDisplay.offsetY);
|
|
}
|
|
}
|
|
}
|
|
|
|
function showPointer(show: boolean) {
|
|
pointerVisible.value = show;
|
|
if (canvasDisplay && !show) {
|
|
canvasDisplay.distanceMeasurement.pointer = undefined;
|
|
}
|
|
}
|
|
|
|
function onMouseDown(event: MouseEvent) {
|
|
window.getSelection()?.removeAllRanges();
|
|
if (mode.value === 'drag') {
|
|
dragging.value = true;
|
|
}
|
|
if (mode.value === 'measure') {
|
|
if (canvasDisplay) {
|
|
const mouseScale = canvas.value!.clientWidth / canvasDisplay.width;
|
|
canvasDisplay.distanceMeasurement.start = {x: event.offsetX / mouseScale, y: event.offsetY / mouseScale}
|
|
}
|
|
}
|
|
window.addEventListener('mouseup', onMouseUp);
|
|
}
|
|
|
|
function onMouseUp(event: MouseEvent) {
|
|
dragging.value = false;
|
|
|
|
window.removeEventListener('mouseup', onMouseUp);
|
|
if (mode.value === 'measure' && canvasDisplay) {
|
|
canvasDisplay.distanceMeasurement.start = undefined;
|
|
}
|
|
}
|
|
function onMouseMove(event: MouseEvent) {
|
|
if (pointerVisible.value && canvasDisplay) {
|
|
const mouseScale = canvas.value!.clientWidth / canvasDisplay.width;
|
|
if (canvasDisplay) {
|
|
canvasDisplay.distanceMeasurement.pointer = {x: event.offsetX / mouseScale, y: event.offsetY / mouseScale};
|
|
}
|
|
}
|
|
|
|
if (event.buttons === 0) {
|
|
onMouseUp(event);
|
|
return;
|
|
}
|
|
|
|
if (!canvasDisplay) {
|
|
return;
|
|
}
|
|
|
|
const mouseScale = canvas.value!.clientWidth / canvasDisplay.width;
|
|
|
|
if (dragging.value) {
|
|
canvasDisplay.offsetX -= event.movementX / mouseScale;
|
|
canvasDisplay.offsetY -= event.movementY / mouseScale;
|
|
}
|
|
if (mode.value === 'measure') {
|
|
if (canvasDisplay) {
|
|
canvasDisplay.distanceMeasurement.end = {x: event.offsetX / mouseScale, y: event.offsetY / mouseScale};
|
|
}
|
|
}
|
|
}
|
|
|
|
function reset() {
|
|
if (!canvasDisplay) {
|
|
return;
|
|
}
|
|
canvasDisplay.offsetX = 0;
|
|
canvasDisplay.offsetY = 0;
|
|
canvasDisplay.scale = 1;
|
|
}
|
|
|
|
function onKeydown(event: KeyboardEvent) {
|
|
if (event.key === 'Control') {
|
|
showPointer(true);
|
|
}
|
|
}
|
|
|
|
function onKeyup(event: KeyboardEvent) {
|
|
if (event.key === 'Control') {
|
|
showPointer(false);
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
onLoad();
|
|
canvasDisplay?.connectTarget(canvas.value!);
|
|
window.addEventListener('reveal-screen', (event: CustomEvent) => {
|
|
selectScreen(event.detail);
|
|
});
|
|
|
|
window.addEventListener('keydown', onKeydown);
|
|
window.addEventListener('keyup', onKeyup);
|
|
window.addEventListener('mousemove', onMouseMove);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
canvasDisplay?.disconnectTarget(canvas.value!);
|
|
window.removeEventListener('keydown', onKeydown);
|
|
window.removeEventListener('keyup', onKeyup);
|
|
window.removeEventListener('mousemove', onMouseMove);
|
|
});
|
|
|
|
async function onLoad(file?: VirtualFile | File) {
|
|
const meta = file;
|
|
if (!canvasDisplay) {
|
|
return;
|
|
}
|
|
if (!(file instanceof File) && file?.type === 'file') {
|
|
file = await fs.download(file);
|
|
} else {
|
|
return;
|
|
}
|
|
console.log('onLoad', file, meta?.map?.width);
|
|
// @ts-ignore
|
|
const layer = new MapLayerDrawable(file, meta?.map?.width);
|
|
await layer.preloadFile();
|
|
canvasDisplay.mapLayer = layer;
|
|
};
|
|
|
|
</script>
|
|
<style lang="css">
|
|
.map-control {
|
|
display: inline-block;
|
|
border: 1px solid white;
|
|
background: white;
|
|
position: relative;
|
|
}
|
|
.map-control--over:before {
|
|
content: 'Drop here';
|
|
position: absolute;
|
|
inset: 10px;
|
|
border: 3px solid black;
|
|
}
|
|
|
|
</style> |