homemanager-fe/src/modules/house-planner/tools/line.ts

192 lines
5.0 KiB
TypeScript

import { History, LayerObject, Line, Vec2 } from '../interfaces';
import { LayerObjectType } from '../types';
import { vec2Equals } from '../utils';
import { CanvasToolBase } from './tool-base';
export type LineToolType = 'line' | 'curve' | 'room';
export class LineTool extends CanvasToolBase<LineToolType> {
public name = 'line';
public autoClose = false;
private drawingLine: Line | null = null;
public subTool: LineToolType = 'line';
drawControls(): void {
const [mx, my] = this.mousePosition;
this.ctx.fillStyle = '#00ddffaa';
this.ctx.beginPath();
this.ctx.arc(mx, my, this.manager.selectError / 2, 0, 2 * Math.PI);
this.ctx.fill();
}
mouseMoved(mouse: Vec2, offset: Vec2, mouseAbsolute: Vec2): void {
super.mouseMoved(mouse, offset, mouseAbsolute);
if (this.drawingLine) {
const lastSegment = this.drawingLine.segments.at(-1);
if (lastSegment) {
lastSegment.end = [...mouse];
}
}
this.renderer.draw();
}
mouseUp(moved?: boolean): void {
if (!moved) this.startLine();
}
enterPress(e: KeyboardEvent): void {
if (this.drawingLine && this.layer) {
if (this.subTool === 'room') {
this.drawingLine.closed = true;
}
this.drawingLine.segments.splice(this.drawingLine.segments.length - 1, 1);
this.history.appendToHistory([
{
object: this.layer,
property: 'contents',
value: [...this.layer.contents].filter(
(item) => item !== this.drawingLine
),
} as History<typeof this.layer>,
{
object: this,
property: 'selectedObjects',
value: [],
},
]);
this.emitEvent(
new CustomEvent('hpc:newobject', {
detail: this.drawingLine,
})
);
this.emitEvent(
new CustomEvent('hpc:update', {
detail: { event: 'newobject', object: this.drawingLine },
})
);
this.drawingLine = null;
this.renderer.draw();
}
}
escapePress(e: KeyboardEvent): void {
this.cancel();
}
selectionDeleted(): void {
this.drawingLine = null;
}
private cancel() {
if (!this.layer || !this.drawingLine) return;
const indexOf = this.layer.contents.indexOf(this.drawingLine);
if (indexOf > -1) {
this.layer.contents.splice(indexOf, 1);
}
this.drawingLine = null;
this.renderer.draw();
this.emitEvent(
new CustomEvent('hpc:update', {
detail: { event: 'draw-cancel' },
})
);
}
private getSequentialId() {
return (
this.renderer.layers.reduce(
(total, current) =>
current.contents.reduce(
(total2, current2) => current2.id + total2,
0
) + total,
0
) + 1
);
}
private startLine() {
if (!this.layer?.visible) return;
if (this.drawingLine) {
if (
vec2Equals(
this.mousePosition,
this.drawingLine.segments[0].start as Vec2
) ||
this.drawingLine.type === 'curve'
) {
if (this.drawingLine.type !== 'curve' && this.autoClose) {
this.drawingLine.segments.splice(
this.drawingLine.segments.length - 1,
1
);
}
this.history.appendToHistory([
{
object: this.layer,
property: 'contents',
value: [...this.layer.contents].filter(
(item) => item !== this.drawingLine
),
} as History<typeof this.layer>,
{
object: this,
property: 'selectedObjects',
value: [],
},
]);
this.drawingLine.closed = this.autoClose;
this.canvas.dispatchEvent(
new CustomEvent('hpc:newobject', {
detail: this.drawingLine,
})
);
this.canvas.dispatchEvent(
new CustomEvent('hpc:update', {
detail: { event: 'newobject', object: this.drawingLine },
})
);
this.drawingLine = null;
return;
}
this.drawingLine.segments.push({
end: [...this.mousePosition],
});
return;
}
const newLineObject: Line = {
id: this.getSequentialId(),
name: this.defaultName,
type: this.subTool! as LayerObjectType,
visible: true,
selected: false,
closed: false,
color: this.manager.lastColor,
width: this.subTool === 'curve' ? 2 : this.manager.lastStrokeWidth,
segments: [
{
start: [...this.mousePosition],
end: [...this.mousePosition],
},
],
};
this.drawingLine = newLineObject;
this.layer.contents.unshift(newLineObject);
this.manager.selectObject(this.drawingLine);
this.canvas.dispatchEvent(
new CustomEvent('hpc:startdrawing', {
detail: newLineObject,
})
);
}
private get defaultName() {
let name = 'New ';
if (this.subTool === 'curve') return name + 'Curve';
if (this.subTool === 'room') return name + 'Room';
return name + 'Line';
}
}