advent22/ui/src/components/rects/RectPad.vue

169 lines
No EOL
3.8 KiB
Vue

<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
preserveAspectRatio="none"
@mousedown.left="draw_start"
@mouseup.left="draw_finish"
@contextmenu.prevent
@mousedown.right="drag_start"
@mouseup.right="drag_finish"
@mousemove="on_mousemove"
@click.middle="remove_rect"
@dblclick.left="remove_rect"
>
<Rect
v-for="(rect, index) in _rectangles"
:key="'rect' + index"
:rectangle="rect"
/>
<Rect
v-if="preview_visible"
:focused="true"
:rectangle="preview_rectangle"
/>
</svg>
</template>
<script lang="ts">
import { Vue, Options } from "vue-class-component";
import { Vector2D, Rectangle } from "./rectangles";
import Rect from "./Rect.vue";
function get_event_thous(event: MouseEvent): Vector2D {
if (event.currentTarget === null) {
return new Vector2D();
}
const target = event.currentTarget as Element;
return new Vector2D(
Math.round((event.offsetX / target.clientWidth) * 1000),
Math.round((event.offsetY / target.clientHeight) * 1000)
);
}
@Options({
components: {
Rect,
},
})
export default class RectPad extends Vue {
private readonly min_rect_area = 300;
private drawing = false;
private preview_corner1 = new Vector2D();
private preview_corner2 = new Vector2D();
private dragging = false;
private drag_rect?: Rectangle;
private drag_origin = new Vector2D();
private _rectangles: Rectangle[] = [];
private get preview_visible(): boolean {
return this.drawing || this.dragging;
}
private get preview_rectangle(): Rectangle {
return new Rectangle(
this.preview_corner1,
this.preview_corner2
).normalize();
}
public get rectangles(): Rectangle[] {
return this._rectangles; //.map((rect) => rect.normalize());
}
public set rectangles(rects: Rectangle[]) {
this._rectangles = rects; //.map((rect) => rect.normalize());
}
private pop_rectangle(point: Vector2D): Rectangle | undefined {
const idx = this._rectangles.findIndex((rect) => rect.contains(point));
if (idx === -1) {
return;
}
return this._rectangles.splice(idx, 1)[0];
}
private draw_start(event: MouseEvent) {
if (this.preview_visible) {
return;
}
this.drawing = true;
this.preview_corner1 = get_event_thous(event);
this.preview_corner2 = get_event_thous(event);
}
private draw_finish() {
if (!this.drawing) {
return;
}
this.drawing = false;
if (this.preview_rectangle.area < this.min_rect_area) {
return;
}
this._rectangles.push(this.preview_rectangle);
}
private drag_start(event: MouseEvent) {
if (this.preview_visible) {
return;
}
const point = get_event_thous(event);
this.drag_rect = this.pop_rectangle(point);
if (this.drag_rect === undefined) {
return;
}
this.dragging = true;
this.drag_origin = point;
this.preview_corner1 = this.drag_rect.origin;
this.preview_corner2 = this.drag_rect.corner;
}
private drag_finish() {
if (!this.dragging) {
return;
}
this.dragging = false;
this._rectangles.push(this.preview_rectangle);
}
private on_mousemove(event: MouseEvent) {
if (this.drawing) {
this.preview_corner2 = get_event_thous(event);
} else if (this.dragging && this.drag_rect) {
const movement = get_event_thous(event).minus(this.drag_origin);
this.preview_corner1 = this.drag_rect.origin.plus(movement);
this.preview_corner2 = this.drag_rect.corner.plus(movement);
}
}
private remove_rect(event: MouseEvent) {
if (this.preview_visible) {
return;
}
this.pop_rectangle(get_event_thous(event));
}
}
</script>
<style scoped>
svg {
cursor: crosshair;
height: 100%;
width: 100%;
}
</style>