zoom in on the object on load
This commit is contained in:
parent
2dc4256f0f
commit
eb48192542
@ -70,6 +70,7 @@ import {
|
||||
RepositionEvent,
|
||||
ToolEvent,
|
||||
Vec2,
|
||||
Vec2Box,
|
||||
} from '../../modules/house-planner/interfaces';
|
||||
import type { HousePlannerCanvasTools } from '../../modules/house-planner/tools';
|
||||
import deepUnref from '../../utils/deep-unref';
|
||||
@ -98,16 +99,23 @@ const props = withDefaults(
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update', document: FloorDocument, rooms: Line[]): void;
|
||||
(
|
||||
e: 'update',
|
||||
document: FloorDocument,
|
||||
rooms: Line[],
|
||||
boundingBox?: Vec2Box
|
||||
): void;
|
||||
(e: 'edited'): void;
|
||||
}>();
|
||||
|
||||
const localFloorDocument = ref(deepUnref(props.floorDocument));
|
||||
const emitUpdate = () =>
|
||||
module.value.manager &&
|
||||
emit(
|
||||
'update',
|
||||
localFloorDocument.value,
|
||||
(module.value.manager?.getAllObjectsOfType('room') || []) as Line[]
|
||||
module.value.manager.getAllObjectsOfType('room') as Line[],
|
||||
module.value.manager.getBoundingBox()
|
||||
);
|
||||
const debouncedUpdate = useDebounceFn(emitUpdate, 10000);
|
||||
|
||||
@ -241,14 +249,18 @@ defineExpose({
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
module.value.initialize(
|
||||
const [setZoom, setPos] = module.value.initialize(
|
||||
canvas.value,
|
||||
deepUnref(localFloorDocument.value.layers),
|
||||
[localFloorDocument.value.width, localFloorDocument.value.height],
|
||||
canvasPos.value,
|
||||
canvasZoom.value,
|
||||
props.editable
|
||||
props.editable,
|
||||
localFloorDocument.value.boundingBox
|
||||
);
|
||||
canvasPos.value = setPos;
|
||||
canvasZoom.value = setZoom;
|
||||
|
||||
Object.keys(events).forEach((event) =>
|
||||
canvas.value.addEventListener(event, events[event])
|
||||
);
|
||||
|
104
src/components/house-planner/PlannerBuildingSelect.vue
Normal file
104
src/components/house-planner/PlannerBuildingSelect.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="absolute top-0 left-0 z-10">
|
||||
<Transition
|
||||
enter-active-class="transition-max-height ease-out duration-200 overflow-hidden"
|
||||
enter-from-class="max-h-0"
|
||||
enter-to-class="max-h-14"
|
||||
leave-active-class="transition-max-height ease-in duration-150 overflow-hidden"
|
||||
leave-from-class="max-h-14"
|
||||
leave-to-class="max-h-0"
|
||||
>
|
||||
<div
|
||||
v-if="open"
|
||||
class="rounded-br-md border-2 border-t-0 border-gray-200 bg-white shadow-lg"
|
||||
>
|
||||
<div class="h-14 px-2 py-2.5">
|
||||
<div class="flex flex-row items-center space-x-4 px-4">
|
||||
<RouterLink to="/" title="Go back"
|
||||
><span class="sr-only">Go back</span
|
||||
><ChevronLeftIcon class="-ml-4 h-6 w-6"
|
||||
/></RouterLink>
|
||||
<div class="flex flex-row items-center space-x-4">
|
||||
<label for="building">Building:</label>
|
||||
<select
|
||||
id="building"
|
||||
class="rounded-sm border-gray-300 py-1 focus:ring-2 focus:ring-blue-200"
|
||||
:value="selectedBuildingId"
|
||||
@change="
|
||||
emit(
|
||||
'update:selectedBuildingId',
|
||||
Number(($event.target as HTMLSelectElement).value)
|
||||
)
|
||||
"
|
||||
>
|
||||
<option v-for="building of buildings" :value="building.id">
|
||||
{{ building.displayName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-row items-center space-x-4"
|
||||
v-if="selectedBuildingId"
|
||||
>
|
||||
<label for="floor">Floor:</label>
|
||||
<select
|
||||
id="floor"
|
||||
class="rounded-sm border-gray-300 py-1 focus:ring-2 focus:ring-blue-200"
|
||||
:value="selectedFloorId"
|
||||
@change="
|
||||
emit(
|
||||
'update:selectedFloorId',
|
||||
Number(($event.target as HTMLSelectElement).value)
|
||||
)
|
||||
"
|
||||
>
|
||||
<option v-for="floor of floors" :value="floor.id">
|
||||
{{ floor.displayName }} ({{ floor.number }})
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="selectedFloorId">{{ status }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<button
|
||||
:class="[
|
||||
'bg-white',
|
||||
'-mt-0.5 ml-2 h-8 rounded-br-md rounded-bl-md px-2 py-2 ring-1 ring-black ring-opacity-5',
|
||||
]"
|
||||
:title="`${open ? 'Hide' : 'Open'} panel`"
|
||||
:aria-expanded="open"
|
||||
@click="() => (open = !open)"
|
||||
>
|
||||
<span class="sr-only">{{ open ? 'Hide' : 'Open' }} panel</span>
|
||||
<ChevronDoubleUpIcon class="h-4 w-4" v-if="open" />
|
||||
<ChevronDoubleDownIcon class="h-4 w-4" v-else />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronDoubleDownIcon,
|
||||
ChevronDoubleUpIcon,
|
||||
} from '@heroicons/vue/24/outline';
|
||||
import { BuildingListItem } from '../../interfaces/building.interfaces';
|
||||
import { FloorListItem } from '../../interfaces/floor.interfaces';
|
||||
|
||||
const open = ref(true);
|
||||
|
||||
const props = defineProps<{
|
||||
floors: FloorListItem[];
|
||||
buildings: BuildingListItem[];
|
||||
selectedBuildingId?: number;
|
||||
selectedFloorId?: number;
|
||||
status: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:selectedBuildingId', buildingId: number): void;
|
||||
(e: 'update:selectedFloorId', buildingId: number): void;
|
||||
}>();
|
||||
</script>
|
@ -10,7 +10,7 @@
|
||||
]"
|
||||
>
|
||||
<Square3Stack3DIcon class="h-4 w-4" />
|
||||
<span>{{ layer.name }}</span>
|
||||
<span>{{ layer.name }} ({{ layer.contents.length }})</span>
|
||||
</button>
|
||||
<div class="flex flex-col bg-gray-50" v-if="layer.active">
|
||||
<div v-for="object of layer.contents">
|
||||
|
@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div class="relative flex flex-row px-2 pb-4 pr-0">
|
||||
<div class="pointer-events-auto relative flex flex-row px-2 pb-4 pr-0">
|
||||
<button
|
||||
:class="[
|
||||
open ? 'bg-gray-200' : 'bg-white',
|
||||
'h-8 rounded-tl-md rounded-bl-md px-2 py-2 ring-1 ring-black ring-opacity-5',
|
||||
]"
|
||||
:title="`${open ? 'Hide' : 'Open'} panel`"
|
||||
:aria-expanded="open"
|
||||
@click="() => (open = !open)"
|
||||
>
|
||||
<span class="sr-only">{{ open ? 'Hide' : 'Open' }} panel</span>
|
||||
<ChevronDoubleRightIcon class="h-4 w-4" v-if="open" />
|
||||
<ChevronDoubleLeftIcon class="h-4 w-4" v-else />
|
||||
</button>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="z-8 absolute right-0 top-0 bottom-0 my-4 overflow-hidden">
|
||||
<div
|
||||
class="z-8 pointer-events-none absolute right-0 top-0 bottom-0 my-4 overflow-hidden"
|
||||
>
|
||||
<div class="flex h-full flex-col items-end space-y-2">
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Layer } from '../../../modules/house-planner/interfaces';
|
||||
import { Layer, Vec2 } from '../../../modules/house-planner/interfaces';
|
||||
|
||||
export interface FloorDocument {
|
||||
id: number;
|
||||
@ -6,4 +6,8 @@ export interface FloorDocument {
|
||||
width: number;
|
||||
height: number;
|
||||
layers: Layer[];
|
||||
/**
|
||||
* Min, Max
|
||||
*/
|
||||
boundingBox?: [Vec2, Vec2];
|
||||
}
|
||||
|
@ -10,10 +10,13 @@ import {
|
||||
LineSegment,
|
||||
RepositionEvent,
|
||||
Vec2,
|
||||
Vec2Box,
|
||||
} from './interfaces';
|
||||
import { HousePlannerCanvasTools } from './tools';
|
||||
import { LayerObjectType } from './types';
|
||||
import {
|
||||
boundingBox,
|
||||
isValidVec2,
|
||||
vec2Add,
|
||||
vec2AngleFromOrigin,
|
||||
vec2Distance,
|
||||
@ -270,6 +273,42 @@ export class HousePlannerCanvas {
|
||||
return objects;
|
||||
}
|
||||
|
||||
getBoundingBox(): Vec2Box | undefined {
|
||||
let box: Vec2Box = [
|
||||
[Infinity, Infinity],
|
||||
[0, 0],
|
||||
];
|
||||
|
||||
this.layers.forEach((layer) => {
|
||||
let layerPoints = layer.contents
|
||||
.filter((object) => ['line', 'curve', 'room'].includes(object.type))
|
||||
.reduce<Vec2[]>(
|
||||
(list, object) => [...list, ...extractLinePoints(object as Line)],
|
||||
[]
|
||||
);
|
||||
if (!layerPoints.length) return;
|
||||
box = boundingBox(layerPoints, box);
|
||||
});
|
||||
|
||||
if (
|
||||
vec2Distance(box[0], box[1]) < 80 ||
|
||||
!isValidVec2(box[0]) ||
|
||||
!isValidVec2(box[1])
|
||||
)
|
||||
return;
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
private drawBoxBounds(box: Vec2Box) {
|
||||
for (const point of box) {
|
||||
const [x, y] = point;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
||||
this.ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
private keyDownEvent(e: KeyboardEvent) {
|
||||
if (e.target !== document.body && e.target != null) return;
|
||||
this.tools?.onKeyDown(e);
|
||||
@ -386,7 +425,9 @@ export class HousePlannerCanvas {
|
||||
onTouchStart(e: TouchEvent) {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0] || e.changedTouches[0];
|
||||
this.dragging = true;
|
||||
this.mouseClickPosition = [touch.clientX, touch.clientY];
|
||||
this.realMousePos(touch.clientX, touch.clientY);
|
||||
|
||||
this.moved = false;
|
||||
|
||||
if (e.touches.length === 2) {
|
||||
@ -410,6 +451,41 @@ export class HousePlannerCanvas {
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
initViewport(boundingBox: Vec2Box) {
|
||||
if (!isValidVec2(boundingBox[0]) || !isValidVec2(boundingBox[1])) return;
|
||||
const [zoom, pos] = this.calculateViewport(boundingBox);
|
||||
this.canvasZoom = zoom;
|
||||
this.canvasPos = vec2MultiplyScalar(pos, -1);
|
||||
this.canvas.dispatchEvent(
|
||||
new CustomEvent<RepositionEvent>('hpc:position', {
|
||||
detail: {
|
||||
position: this.canvasPos,
|
||||
zoom: this.canvasZoom,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private calculateViewport(box: Vec2Box): [number, Vec2] {
|
||||
let [min, max] = box;
|
||||
const gap = 80;
|
||||
min = vec2Sub(min, [gap, gap]);
|
||||
max = vec2Add(max, [gap, gap]);
|
||||
|
||||
const { width: windowWidth, height: windowHeight } =
|
||||
this.canvas.parentElement!.getBoundingClientRect();
|
||||
const diagonal = vec2Distance(min, max);
|
||||
const target = vec2Sub(max, min);
|
||||
const zoomScale = windowHeight / diagonal;
|
||||
|
||||
let scaledPos = vec2MultiplyScalar(min, zoomScale);
|
||||
const scaled = vec2MultiplyScalar(target, zoomScale);
|
||||
const overlap = vec2Sub([windowWidth, windowHeight], scaled);
|
||||
scaledPos = vec2Sub(scaledPos, vec2DivideScalar(overlap, 2));
|
||||
|
||||
return [zoomScale, scaledPos];
|
||||
}
|
||||
|
||||
private onMouseWheel(e: WheelEvent) {
|
||||
e.preventDefault();
|
||||
this.realMousePos(e.clientX, e.clientY);
|
||||
|
@ -3,8 +3,17 @@ import { LayerObject } from './interfaces';
|
||||
|
||||
export class HousePlannerCanvasClipboard {
|
||||
public storedObjects: LayerObject[] = [];
|
||||
public wasCutOperation = false;
|
||||
|
||||
storeToClipboard(items: LayerObject[], cut = false) {
|
||||
this.wasCutOperation = cut;
|
||||
|
||||
// If we are cutting, we store the object by reference and return it all in its original state
|
||||
if (cut) {
|
||||
this.storedObjects = [...items];
|
||||
return;
|
||||
}
|
||||
|
||||
storeToClipboard(items: LayerObject[]) {
|
||||
this.storedObjects = [
|
||||
...items.map((item) => {
|
||||
const itemCopy = {
|
||||
@ -19,6 +28,12 @@ export class HousePlannerCanvasClipboard {
|
||||
}
|
||||
|
||||
getFromClipboard(newId: number, selected = true): LayerObject[] {
|
||||
if (this.wasCutOperation) {
|
||||
const unalteredList = [...this.storedObjects];
|
||||
this.storeToClipboard(this.storedObjects, false);
|
||||
return unalteredList;
|
||||
}
|
||||
|
||||
const newObjects = deepUnref(this.storedObjects);
|
||||
return newObjects.map((item, index) => ({
|
||||
...item,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Ref } from 'vue';
|
||||
import { HousePlannerCanvas } from './canvas';
|
||||
import { Layer, Vec2 } from './interfaces';
|
||||
import { Layer, Vec2, Vec2Box } from './interfaces';
|
||||
import { HousePlannerCanvasTools } from './tools';
|
||||
|
||||
export class HousePlanner {
|
||||
@ -13,8 +12,9 @@ export class HousePlanner {
|
||||
canvasDim: Vec2,
|
||||
canvasPos: Vec2,
|
||||
canvasZoom = 1,
|
||||
editable = true
|
||||
) {
|
||||
editable = true,
|
||||
boundingBox?: Vec2Box
|
||||
): [number, Vec2] {
|
||||
this.canvas = canvas;
|
||||
this.manager = new HousePlannerCanvas(
|
||||
canvas,
|
||||
@ -35,6 +35,11 @@ export class HousePlanner {
|
||||
}
|
||||
|
||||
this.manager.draw();
|
||||
|
||||
if (boundingBox) {
|
||||
this.manager.initViewport(boundingBox);
|
||||
}
|
||||
return [this.manager.canvasZoom, this.manager.canvasPos];
|
||||
}
|
||||
|
||||
cleanUp() {
|
||||
|
@ -2,6 +2,7 @@ import { HousePlannerCanvasHistory } from './history';
|
||||
import { LayerObjectType } from './types';
|
||||
|
||||
export type Vec2 = [number, number];
|
||||
export type Vec2Box = [Vec2, Vec2];
|
||||
export interface LineSegment {
|
||||
start?: Vec2;
|
||||
end: Vec2;
|
||||
|
@ -297,8 +297,14 @@ export class HousePlannerCanvasTools implements ICanvasToolkit {
|
||||
if (e.key === 'x' && e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
if (this.selectedObjects.length && this.selectedLayer) {
|
||||
this.clipboard.storeToClipboard(this.selectedObjects);
|
||||
this.deleteSelection();
|
||||
this.clipboard.storeToClipboard(this.selectedObjects, true);
|
||||
this.deleteSelection([
|
||||
{
|
||||
object: this.clipboard,
|
||||
property: 'wasCutOperation',
|
||||
value: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,7 +367,7 @@ export class HousePlannerCanvasTools implements ICanvasToolkit {
|
||||
}
|
||||
}
|
||||
|
||||
deleteSelection() {
|
||||
deleteSelection(additionalHistory?: History<any>[]) {
|
||||
if (!this.selectedObjects.length || !this.selectedLayer) return;
|
||||
this.history.appendToHistory([
|
||||
{
|
||||
@ -369,6 +375,7 @@ export class HousePlannerCanvasTools implements ICanvasToolkit {
|
||||
property: 'contents',
|
||||
value: [...this.selectedLayer.contents],
|
||||
} as History<typeof this.selectedLayer>,
|
||||
...(additionalHistory || []),
|
||||
]);
|
||||
|
||||
this.selectedLayer.contents = this.selectedLayer.contents.filter(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { clamp } from '@vueuse/shared';
|
||||
import { Vec2 } from './interfaces';
|
||||
import { Vec2, Vec2Box } from './interfaces';
|
||||
|
||||
export const vec2Length = ([x, y]: Vec2) => Math.abs(Math.sqrt(x * x + y * y));
|
||||
|
||||
@ -69,3 +69,34 @@ export const rad2deg = (rad: number) => rad * (180 / Math.PI);
|
||||
|
||||
export const randomNumber = (min: number, max: number) =>
|
||||
Math.floor(Math.random() * (max - min + 1) + min);
|
||||
|
||||
export const boundingBox = (points: Vec2[], start: Vec2Box): Vec2Box => {
|
||||
let minX = start[0][0];
|
||||
let minY = start[0][1];
|
||||
let maxX = start[1][0];
|
||||
let maxY = start[1][1];
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (points[i][0] > maxX) {
|
||||
maxX = points[i][0];
|
||||
}
|
||||
if (points[i][0] < minX) {
|
||||
minX = points[i][0];
|
||||
}
|
||||
|
||||
if (points[i][1] > maxY) {
|
||||
maxY = points[i][1];
|
||||
}
|
||||
if (points[i][1] < minY) {
|
||||
minY = points[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
[minX, minY],
|
||||
[maxX, maxY],
|
||||
];
|
||||
};
|
||||
|
||||
export const isValidVec2 = ([x, y]: Vec2) =>
|
||||
x != null && y != null && !isNaN(x) && !isNaN(y);
|
||||
|
@ -20,9 +20,6 @@ const routes: RouteRecordRaw[] = [
|
||||
name: 'planner',
|
||||
path: '/planner',
|
||||
component: HousePlanner,
|
||||
meta: {
|
||||
authenticated: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -9,7 +9,7 @@ export const useUserStore = defineStore('user', {
|
||||
state: () => {
|
||||
return {
|
||||
currentUser: null as User | null,
|
||||
accessToken: useLocalStorage<string>('accessToken', null, {
|
||||
accessToken: useLocalStorage<string | null>('accessToken', null, {
|
||||
writeDefaults: false,
|
||||
}),
|
||||
};
|
||||
@ -21,11 +21,17 @@ export const useUserStore = defineStore('user', {
|
||||
actions: {
|
||||
loginFromToken() {
|
||||
const token = this.accessToken;
|
||||
if (!token) return;
|
||||
try {
|
||||
const decoded = jwtDecode(token) as Record<string, unknown>;
|
||||
if (!decoded.sub) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
|
||||
if ((decoded.exp as number) * 1000 < Date.now()) {
|
||||
throw new Error('Expired token');
|
||||
}
|
||||
|
||||
this.currentUser = {
|
||||
sub: decoded.sub as string,
|
||||
name: decoded.name as string,
|
||||
@ -33,7 +39,8 @@ export const useUserStore = defineStore('user', {
|
||||
picture: decoded.picture as string,
|
||||
};
|
||||
} catch {
|
||||
// TODO
|
||||
this.currentUser = null;
|
||||
this.accessToken = null;
|
||||
}
|
||||
},
|
||||
async login({ email, password }: { email: string; password: string }) {
|
||||
|
@ -1,47 +1,24 @@
|
||||
<template>
|
||||
<div class="relative h-full bg-gray-100">
|
||||
<div
|
||||
class="absolute top-0 left-0 z-10 rounded-br-md border-b-2 border-r-2 border-gray-200 bg-white px-2 py-2 shadow-lg"
|
||||
>
|
||||
<div class="flex flex-row items-center space-x-4 px-4">
|
||||
<div class="flex flex-row items-center space-x-4">
|
||||
<label for="building">Building:</label>
|
||||
<select
|
||||
id="building"
|
||||
class="rounded-sm border-gray-300 py-1 focus:ring-2 focus:ring-blue-200"
|
||||
v-model="selectedBuildingId"
|
||||
@change="buildingSelected()"
|
||||
>
|
||||
<option v-for="building of buildings" :value="building.id">
|
||||
{{ building.displayName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-row items-center space-x-4"
|
||||
v-if="selectedBuildingId"
|
||||
>
|
||||
<label for="floor">Floor:</label>
|
||||
<select
|
||||
id="floor"
|
||||
class="rounded-sm border-gray-300 py-1 focus:ring-2 focus:ring-blue-200"
|
||||
v-model="selectedFloorId"
|
||||
>
|
||||
<option v-for="floor of floors" :value="floor.id">
|
||||
{{ floor.displayName }} ({{ floor.number }})
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="selectedFloorId">{{ status }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<PlannerBuildingSelect
|
||||
:buildings="buildings"
|
||||
:floors="floors"
|
||||
:status="status"
|
||||
:selected-building-id="selectedBuildingId"
|
||||
:selected-floor-id="selectedFloorId"
|
||||
@update:selected-building-id="(newValue) => buildingSelected(newValue)"
|
||||
@update:selected-floor-id="(newValue) => (selectedFloorId = newValue)"
|
||||
/>
|
||||
<HousePlanner
|
||||
v-if="selectedFloorId"
|
||||
editable
|
||||
ref="plannerRef"
|
||||
:key="`planner-${selectedFloorId}`"
|
||||
:floor-document="floorPlan"
|
||||
@update="(layers, rooms) => updateDocument(layers, rooms)"
|
||||
@update="
|
||||
(layers, rooms, boundingBox) =>
|
||||
updateDocument(layers, rooms, boundingBox)
|
||||
"
|
||||
@edited="status = 'Modified'"
|
||||
/>
|
||||
<div
|
||||
@ -61,8 +38,9 @@ import { computed, onMounted, ref } from 'vue';
|
||||
import { defaultRoomData } from '../components/house-planner/helpers/default-room';
|
||||
import HousePlanner from '../components/house-planner/HousePlanner.vue';
|
||||
import { FloorDocument } from '../components/house-planner/interfaces/floor-document.interface';
|
||||
import PlannerBuildingSelect from '../components/house-planner/PlannerBuildingSelect.vue';
|
||||
import { UpsertRoomItem } from '../interfaces/room.interfaces';
|
||||
import { Line } from '../modules/house-planner/interfaces';
|
||||
import { Line, Vec2Box } from '../modules/house-planner/interfaces';
|
||||
import { useBuildingStore } from '../store/building.store';
|
||||
import extractLinePoints from '../utils/extract-line-points';
|
||||
const building = useBuildingStore();
|
||||
@ -88,10 +66,11 @@ const floorPlan = computed(
|
||||
})
|
||||
);
|
||||
|
||||
const buildingSelected = async () => {
|
||||
if (selectedBuildingId.value == null) return;
|
||||
const buildingSelected = async (id: number) => {
|
||||
if (id == null) return;
|
||||
selectedFloorId.value = undefined;
|
||||
await building.getFloors(selectedBuildingId.value);
|
||||
selectedBuildingId.value = id;
|
||||
await building.getFloors(id);
|
||||
};
|
||||
|
||||
const updateRooms = async (data: FloorDocument, rooms: Line[]) => {
|
||||
@ -136,10 +115,15 @@ const updateRooms = async (data: FloorDocument, rooms: Line[]) => {
|
||||
return data;
|
||||
};
|
||||
|
||||
const updateDocument = async (data: FloorDocument, rooms: Line[]) => {
|
||||
const updateDocument = async (
|
||||
data: FloorDocument,
|
||||
rooms: Line[],
|
||||
boundingBox?: Vec2Box
|
||||
) => {
|
||||
if (
|
||||
!selectedBuildingId.value ||
|
||||
!selectedFloorId.value ||
|
||||
selectedFloorId.value !== data.id ||
|
||||
!currentFloor.value ||
|
||||
status.value === 'Saving...'
|
||||
)
|
||||
@ -150,7 +134,7 @@ const updateDocument = async (data: FloorDocument, rooms: Line[]) => {
|
||||
data = await updateRooms(data, rooms);
|
||||
|
||||
// Prevent useless requests
|
||||
const floorPlan = JSON.stringify(data);
|
||||
const floorPlan = JSON.stringify({ ...data, boundingBox });
|
||||
if (currentFloor.value.plan === floorPlan) {
|
||||
status.value = 'Saved!';
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user