((list, segment) => {
+ if (segment.start) return [...list, segment.start, segment.end];
+ return [...list, segment.end];
+ }, [])
+ .filter(
+ (vec, index, arry) =>
+ arry.findIndex((point) => vec2Equals(point, vec)) === index
+ );
const centerPoint = vec2DivideScalar(
- line.segments.reduce
((prev, curr) => {
- if (!prev) {
- if (curr.start) {
- return vec2Add(curr.start, curr.end);
- }
- return curr.end;
- }
-
- let preadd = vec2Add(prev, curr.end);
- if (curr.start) {
- preadd = vec2Add(curr.start, preadd);
- }
- return preadd;
- }, null) as Vec2,
- line.segments.length + 1
+ points.reduce(
+ (prev, current) => (prev ? vec2Add(prev, current) : current),
+ null
+ ) as Vec2,
+ points.length
);
+
this.ctx.font = '16px Arial';
this.ctx.fillStyle = line.color;
const { width } = this.ctx.measureText(line.name);
diff --git a/src/modules/house-planner/index.ts b/src/modules/house-planner/index.ts
index cbac031..1f75a60 100644
--- a/src/modules/house-planner/index.ts
+++ b/src/modules/house-planner/index.ts
@@ -1,6 +1,7 @@
import { Ref } from 'vue';
import { HousePlannerCanvas } from './canvas';
import { Layer, Vec2 } from './interfaces';
+import { HousePlannerCanvasTools } from './tools';
export class HousePlanner {
public canvas!: HTMLCanvasElement;
@@ -25,15 +26,15 @@ export class HousePlanner {
this.manager.layers = initialData;
if (editable && this.manager.tools) {
- this.manager.tools.selectLayer(
+ const stdToolkit = this.manager.tools as HousePlannerCanvasTools;
+ stdToolkit.selectLayer(
initialData[initialData.findIndex((layer) => layer.active)]
);
- this.manager.tools.setInitialSelection();
- this.manager.tools.setTool('move');
+ stdToolkit.setInitialSelection();
+ stdToolkit.setTool('move');
}
this.manager.draw();
- return () => this.cleanUp();
}
cleanUp() {
diff --git a/src/modules/house-planner/interfaces.ts b/src/modules/house-planner/interfaces.ts
index defbc62..38718a5 100644
--- a/src/modules/house-planner/interfaces.ts
+++ b/src/modules/house-planner/interfaces.ts
@@ -1,3 +1,4 @@
+import { HousePlannerCanvasHistory } from './history';
import { LayerObjectType } from './types';
export type Vec2 = [number, number];
@@ -13,6 +14,7 @@ export interface BezierSegment extends LineSegment {
export interface LayerObject {
id: number;
+ databaseId?: number;
name: string;
visible: boolean;
selected: boolean;
@@ -66,6 +68,16 @@ export interface ICanvasToolMouseEvents {
mouseUp(moved: boolean): void;
}
+export interface ICanvasToolkit extends ICanvasToolMouseEvents {
+ history: HousePlannerCanvasHistory;
+ drawHighlights(): void;
+ drawControls(): void;
+ cleanUp(): void;
+ onKeyDown(e: KeyboardEvent): void;
+ onKeyUp(e: KeyboardEvent): void;
+ getMousedObject(): LayerObject | null;
+}
+
export interface ICanvasToolBase extends ICanvasToolMouseEvents {
name: string;
subTool: U | undefined;
diff --git a/src/modules/house-planner/tools.ts b/src/modules/house-planner/tools.ts
index cece037..18d1dee 100644
--- a/src/modules/house-planner/tools.ts
+++ b/src/modules/house-planner/tools.ts
@@ -3,7 +3,7 @@ import { HousePlannerCanvasHistory } from './history';
import {
History,
ICanvasToolBase,
- ICanvasToolMouseEvents,
+ ICanvasToolkit,
Layer,
LayerObject,
Line,
@@ -14,11 +14,12 @@ import { CutTool } from './tools/cut';
import { LineTool } from './tools/line';
import { MoveTool } from './tools/move';
-export class HousePlannerCanvasTools implements ICanvasToolMouseEvents {
+export class HousePlannerCanvasTools implements ICanvasToolkit {
public selectedLayer?: Layer;
public selectedObjects: LayerObject[] = [];
public gridSnap = true;
public gridSnapScale = 8;
+ public autoClose = false;
public tool?: ICanvasToolBase;
public tools: Record> = {
['move']: new MoveTool(this),
diff --git a/src/modules/house-planner/tools/cut.ts b/src/modules/house-planner/tools/cut.ts
index 14a36a6..6550673 100644
--- a/src/modules/house-planner/tools/cut.ts
+++ b/src/modules/house-planner/tools/cut.ts
@@ -1,10 +1,4 @@
-import {
- BezierSegment,
- LayerObject,
- Line,
- LineSegment,
- Vec2,
-} from '../interfaces';
+import { LayerObject, Line, LineSegment, Vec2 } from '../interfaces';
import { CanvasToolBase } from './tool-base';
export type CutToolType = 'cut' | 'remove-segment';
@@ -46,34 +40,7 @@ export class CutTool extends CanvasToolBase {
this.renderer.draw();
}
- drawBezierControls(bezier: BezierSegment, previousEnd: Vec2) {
- const [cp1x, cp1y] = bezier.startControl;
- const [cp2x, cp2y] = bezier.endControl;
- const [endx, endy] = bezier.end;
- const [prevx, prevy] = previousEnd;
- this.ctx.fillStyle = '#00ddffaa';
- this.ctx.strokeStyle = '#00ddffaa';
- this.ctx.lineWidth = 2;
- this.ctx.beginPath();
- this.ctx.arc(cp1x, cp1y, this.manager.selectError / 2, 0, 2 * Math.PI);
- this.ctx.fill();
-
- this.ctx.beginPath();
- this.ctx.arc(cp2x, cp2y, this.manager.selectError / 2, 0, 2 * Math.PI);
- this.ctx.fill();
-
- this.ctx.beginPath();
- this.ctx.moveTo(cp1x, cp1y);
- this.ctx.lineTo(prevx, prevy);
- this.ctx.stroke();
-
- this.ctx.beginPath();
- this.ctx.moveTo(cp2x, cp2y);
- this.ctx.lineTo(endx, endy);
- this.ctx.stroke();
- }
-
- drawLineControls(line: LineSegment, previousEnd: Vec2) {
+ drawLineControls(line: LineSegment) {
const [endx, endy] = line.end;
this.ctx.fillStyle = '#00ddffaa';
@@ -99,15 +66,8 @@ export class CutTool extends CanvasToolBase {
for (const object of this.manager.selectedObjects) {
const line = object as Line;
if (line.segments && line.render) {
- let lastSegment = null;
for (const segment of line.segments) {
- const bezier = segment as BezierSegment;
- const previousPoint = lastSegment ? lastSegment.end : segment.start!;
- if (bezier.startControl && bezier.endControl) {
- this.drawBezierControls(bezier, previousPoint);
- }
- this.drawLineControls(segment, previousPoint);
- lastSegment = segment;
+ this.drawLineControls(segment);
}
}
}
diff --git a/src/modules/house-planner/tools/line.ts b/src/modules/house-planner/tools/line.ts
index e0cae2f..04f2091 100644
--- a/src/modules/house-planner/tools/line.ts
+++ b/src/modules/house-planner/tools/line.ts
@@ -6,7 +6,6 @@ import { CanvasToolBase } from './tool-base';
export type LineToolType = 'line' | 'curve' | 'room';
export class LineTool extends CanvasToolBase {
public name = 'line';
- public autoClose = false;
private drawingLine: Line | null = null;
public subTool: LineToolType = 'line';
@@ -115,7 +114,7 @@ export class LineTool extends CanvasToolBase {
) ||
this.drawingLine.type === 'curve'
) {
- if (this.drawingLine.type !== 'curve' && this.autoClose) {
+ if (this.drawingLine.type !== 'curve' && this.manager.autoClose) {
this.drawingLine.segments.splice(
this.drawingLine.segments.length - 1,
1
@@ -135,7 +134,10 @@ export class LineTool extends CanvasToolBase {
value: [],
},
]);
- this.drawingLine.closed = this.autoClose;
+ this.drawingLine.closed = this.manager.autoClose;
+ if (!this.manager.autoClose) {
+ this.drawingLine.lineCap = 'square';
+ }
this.canvas.dispatchEvent(
new CustomEvent('hpc:newobject', {
detail: this.drawingLine,
diff --git a/src/router/index.ts b/src/router/index.ts
index ea74b57..2c152a6 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -3,7 +3,7 @@ import Dashboard from '../views/Dashboard.vue';
import Login from '../views/Login.vue';
import { createRouter, createWebHashHistory } from 'vue-router';
import { useUserStore } from '../store/user.store';
-import HousePlanner from '../components/house-planner/HousePlanner.vue';
+import HousePlanner from '../views/HousePlanner.vue';
const routes: RouteRecordRaw[] = [
{
diff --git a/src/store/building.store.ts b/src/store/building.store.ts
index 4bcc404..8688029 100644
--- a/src/store/building.store.ts
+++ b/src/store/building.store.ts
@@ -2,6 +2,8 @@ import { defineStore } from 'pinia';
import { useAccessToken } from '../composables/useAccessToken';
import { BACKEND_URL } from '../constants';
import { BuildingListItem } from '../interfaces/building.interfaces';
+import { FloorListItem } from '../interfaces/floor.interfaces';
+import { RoomListItem } from '../interfaces/room.interfaces';
import jfetch from '../utils/jfetch';
const { authHeader } = useAccessToken();
@@ -9,6 +11,7 @@ export const useBuildingStore = defineStore('building', {
state: () => {
return {
buildings: [] as BuildingListItem[],
+ floors: [] as FloorListItem[],
};
},
actions: {
@@ -18,5 +21,34 @@ export const useBuildingStore = defineStore('building', {
});
this.buildings = buildings;
},
+ async getFloors(building: number) {
+ const { data: floors } = await jfetch(
+ `${BACKEND_URL}/buildings/${building}/floors`,
+ {
+ headers: authHeader.value,
+ }
+ );
+ this.floors = floors;
+ },
+ async saveFloor(
+ building: number,
+ number: number,
+ floor: Partial
+ ) {
+ await jfetch(`${BACKEND_URL}/buildings/${building}/floors/${number}`, {
+ method: 'PATCH',
+ headers: {
+ ...authHeader.value,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(floor),
+ });
+ },
+ async upsertFloorRooms(
+ building: number,
+ floorNo: number,
+ rooms: RoomListItem[],
+ removedRooms: number[]
+ ) {},
},
});
diff --git a/src/views/HousePlanner.vue b/src/views/HousePlanner.vue
new file mode 100644
index 0000000..a015d0d
--- /dev/null
+++ b/src/views/HousePlanner.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ status }}
+
+
+
updateDocument($newValue)"
+ @edited="status = 'Modified'"
+ />
+
+
+
+