zooming and panning
This commit is contained in:
parent
668b2c5001
commit
b82f1b03f2
@ -1,6 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative h-full w-full bg-white">
|
<div class="relative h-full w-full bg-white">
|
||||||
<canvas ref="canvas" class="border-none" />
|
<div class="relative h-full w-full overflow-hidden bg-gray-100">
|
||||||
|
<canvas
|
||||||
|
ref="canvas"
|
||||||
|
class="border-none bg-white"
|
||||||
|
:style="{
|
||||||
|
width: `${canvasDim[0] * canvasZoom}px`,
|
||||||
|
height: `${canvasDim[1] * canvasZoom}px`,
|
||||||
|
transformOrigin: 'top left',
|
||||||
|
transform: `translate(${canvasPos[0]}px, ${canvasPos[1]}px)`,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<PlannerToolbar>
|
<PlannerToolbar>
|
||||||
<PlannerTool
|
<PlannerTool
|
||||||
v-for="toolItem of toolbar"
|
v-for="toolItem of toolbar"
|
||||||
@ -54,7 +65,12 @@ import { useSessionStorage } from '@vueuse/core';
|
|||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { onMounted, ref, shallowRef } from 'vue';
|
import { onMounted, ref, shallowRef } from 'vue';
|
||||||
import { HousePlanner } from '../../modules/house-planner';
|
import { HousePlanner } from '../../modules/house-planner';
|
||||||
import { Layer, ToolEvent } from '../../modules/house-planner/interfaces';
|
import {
|
||||||
|
Layer,
|
||||||
|
RepositionEvent,
|
||||||
|
ToolEvent,
|
||||||
|
Vec2,
|
||||||
|
} from '../../modules/house-planner/interfaces';
|
||||||
import { LayerObjectType } from '../../modules/house-planner/types';
|
import { LayerObjectType } from '../../modules/house-planner/types';
|
||||||
import deepUnref from '../../utils/deep-unref';
|
import deepUnref from '../../utils/deep-unref';
|
||||||
import { ToolbarTool } from './interfaces/toolbar.interfaces';
|
import { ToolbarTool } from './interfaces/toolbar.interfaces';
|
||||||
@ -69,6 +85,9 @@ const canvas = ref();
|
|||||||
const module = shallowRef(new HousePlanner());
|
const module = shallowRef(new HousePlanner());
|
||||||
const tool = ref<string | undefined>('move');
|
const tool = ref<string | undefined>('move');
|
||||||
const subTool = ref<string | undefined>(undefined);
|
const subTool = ref<string | undefined>(undefined);
|
||||||
|
const canvasDim = ref<Vec2>([1920, 1080]);
|
||||||
|
const canvasPos = ref<Vec2>([0, 0]);
|
||||||
|
const canvasZoom = ref<number>(1);
|
||||||
const serializedLayers = useSessionStorage<Layer[]>(
|
const serializedLayers = useSessionStorage<Layer[]>(
|
||||||
'roomData',
|
'roomData',
|
||||||
[
|
[
|
||||||
@ -167,7 +186,10 @@ const updateObjectProperty = (
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const cleanUp = module.value.initialize(
|
const cleanUp = module.value.initialize(
|
||||||
canvas.value,
|
canvas.value,
|
||||||
deepUnref(serializedLayers.value)
|
deepUnref(serializedLayers.value),
|
||||||
|
[1920, 1080],
|
||||||
|
canvasPos,
|
||||||
|
canvasZoom.value
|
||||||
);
|
);
|
||||||
|
|
||||||
const events: Record<string, (e: CustomEvent) => void> = {
|
const events: Record<string, (e: CustomEvent) => void> = {
|
||||||
@ -181,6 +203,10 @@ onMounted(() => {
|
|||||||
tool.value = e.detail.primary;
|
tool.value = e.detail.primary;
|
||||||
subTool.value = e.detail.secondary as string;
|
subTool.value = e.detail.secondary as string;
|
||||||
},
|
},
|
||||||
|
'hpc:position': (e: CustomEvent<RepositionEvent>) => {
|
||||||
|
//canvasPos.value = e.detail.position;
|
||||||
|
canvasZoom.value = e.detail.zoom;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(events).forEach((event) =>
|
Object.keys(events).forEach((event) =>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { Ref } from 'vue';
|
||||||
import { HousePlannerCanvasGrid } from './grid';
|
import { HousePlannerCanvasGrid } from './grid';
|
||||||
import {
|
import {
|
||||||
BezierSegment,
|
BezierSegment,
|
||||||
@ -5,6 +6,7 @@ import {
|
|||||||
LayerObject,
|
LayerObject,
|
||||||
Line,
|
Line,
|
||||||
LineSegment,
|
LineSegment,
|
||||||
|
RepositionEvent,
|
||||||
Vec2,
|
Vec2,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { HousePlannerCanvasTools } from './tools';
|
import { HousePlannerCanvasTools } from './tools';
|
||||||
@ -12,9 +14,12 @@ import {
|
|||||||
rad2deg,
|
rad2deg,
|
||||||
vec2Add,
|
vec2Add,
|
||||||
vec2AngleFromOrigin,
|
vec2AngleFromOrigin,
|
||||||
|
vec2Clamp,
|
||||||
vec2Distance,
|
vec2Distance,
|
||||||
vec2DivideScalar,
|
vec2DivideScalar,
|
||||||
|
vec2MultiplyScalar,
|
||||||
vec2PointFromAngle,
|
vec2PointFromAngle,
|
||||||
|
vec2Sub,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
export class HousePlannerCanvas {
|
export class HousePlannerCanvas {
|
||||||
@ -23,9 +28,15 @@ export class HousePlannerCanvas {
|
|||||||
public tools = new HousePlannerCanvasTools(this);
|
public tools = new HousePlannerCanvasTools(this);
|
||||||
public grid = new HousePlannerCanvasGrid(this, 8);
|
public grid = new HousePlannerCanvasGrid(this, 8);
|
||||||
|
|
||||||
constructor(public canvas: HTMLCanvasElement) {
|
constructor(
|
||||||
|
public canvas: HTMLCanvasElement,
|
||||||
|
public canvasDim: Vec2,
|
||||||
|
public canvasPos: Ref<Vec2>,
|
||||||
|
public canvasZoom = 1
|
||||||
|
) {
|
||||||
this.ctx = this.canvas.getContext('2d')!;
|
this.ctx = this.canvas.getContext('2d')!;
|
||||||
this.setupEvents();
|
this.setupEvents();
|
||||||
|
this.resizeCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
@ -41,6 +52,13 @@ export class HousePlannerCanvas {
|
|||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resizeCanvas() {
|
||||||
|
const [w, h] = this.canvasDim;
|
||||||
|
this.canvas.width = w;
|
||||||
|
this.canvas.height = h;
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
|
||||||
cleanUp() {
|
cleanUp() {
|
||||||
this.tools.cleanUp();
|
this.tools.cleanUp();
|
||||||
window.removeEventListener('keyup', this.boundKeyUpEvent);
|
window.removeEventListener('keyup', this.boundKeyUpEvent);
|
||||||
@ -206,6 +224,17 @@ export class HousePlannerCanvas {
|
|||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translateCanvas(offset: Vec2) {
|
||||||
|
const realSize = vec2MultiplyScalar(this.canvasDim, this.canvasZoom);
|
||||||
|
this.canvasPos.value = vec2Sub(this.canvasPos.value, offset);
|
||||||
|
this.repositionEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomCanvas(diff: number) {
|
||||||
|
this.canvasZoom -= diff;
|
||||||
|
this.repositionEvent();
|
||||||
|
}
|
||||||
|
|
||||||
private keyDownEvent(e: KeyboardEvent) {
|
private keyDownEvent(e: KeyboardEvent) {
|
||||||
if (e.target !== document.body && e.target != null) return;
|
if (e.target !== document.body && e.target != null) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -287,4 +316,15 @@ export class HousePlannerCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private repositionEvent() {
|
||||||
|
this.canvas.dispatchEvent(
|
||||||
|
new CustomEvent<RepositionEvent>('hpc:position', {
|
||||||
|
detail: {
|
||||||
|
position: this.canvasPos.value,
|
||||||
|
zoom: this.canvasZoom,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
|
import { Ref } from 'vue';
|
||||||
import { HousePlannerCanvas } from './canvas';
|
import { HousePlannerCanvas } from './canvas';
|
||||||
import { Layer } from './interfaces';
|
import { Layer, Vec2 } from './interfaces';
|
||||||
|
|
||||||
export class HousePlanner {
|
export class HousePlanner {
|
||||||
public canvas!: HTMLCanvasElement;
|
public canvas!: HTMLCanvasElement;
|
||||||
public manager?: HousePlannerCanvas;
|
public manager?: HousePlannerCanvas;
|
||||||
|
|
||||||
initialize(canvas: HTMLCanvasElement, initialData: Layer[]) {
|
initialize(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
initialData: Layer[],
|
||||||
|
canvasDim: Vec2,
|
||||||
|
canvasPos: Ref<Vec2>,
|
||||||
|
canvasZoom = 1,
|
||||||
|
editable = true
|
||||||
|
) {
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.resizeCanvas();
|
this.manager = new HousePlannerCanvas(
|
||||||
this.addResizeEvents();
|
canvas,
|
||||||
this.manager = new HousePlannerCanvas(canvas);
|
canvasDim,
|
||||||
|
canvasPos,
|
||||||
|
canvasZoom
|
||||||
|
);
|
||||||
this.manager.layers = initialData;
|
this.manager.layers = initialData;
|
||||||
this.manager.tools.selectLayer(
|
this.manager.tools.selectLayer(
|
||||||
initialData[initialData.findIndex((layer) => layer.active)]
|
initialData[initialData.findIndex((layer) => layer.active)]
|
||||||
@ -21,19 +32,6 @@ export class HousePlanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanUp() {
|
cleanUp() {
|
||||||
window.removeEventListener('resize', this.boundResizeEvent);
|
|
||||||
this.manager?.cleanUp();
|
this.manager?.cleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
private addResizeEvents() {
|
|
||||||
window.addEventListener('resize', this.boundResizeEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeCanvas() {
|
|
||||||
this.canvas.width = this.canvas.parentElement!.clientWidth;
|
|
||||||
this.canvas.height = this.canvas.parentElement!.clientHeight;
|
|
||||||
this.manager?.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boundResizeEvent = this.resizeCanvas.bind(this);
|
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,11 @@ export interface ToolEvent {
|
|||||||
secondary?: unknown;
|
secondary?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepositionEvent {
|
||||||
|
position: Vec2;
|
||||||
|
zoom: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ICanvasToolBase<U = undefined> {
|
export interface ICanvasToolBase<U = undefined> {
|
||||||
name: string;
|
name: string;
|
||||||
subTool: U | undefined;
|
subTool: U | undefined;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { clamp } from '@vueuse/core';
|
||||||
import type { HousePlannerCanvas } from './canvas';
|
import type { HousePlannerCanvas } from './canvas';
|
||||||
import { HousePlannerCanvasHistory } from './history';
|
import { HousePlannerCanvasHistory } from './history';
|
||||||
import {
|
import {
|
||||||
@ -6,12 +7,20 @@ import {
|
|||||||
Layer,
|
Layer,
|
||||||
LayerObject,
|
LayerObject,
|
||||||
Line,
|
Line,
|
||||||
|
RepositionEvent,
|
||||||
ToolEvent,
|
ToolEvent,
|
||||||
Vec2,
|
Vec2,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { LineTool } from './tools/line';
|
import { LineTool } from './tools/line';
|
||||||
import { MoveTool } from './tools/move';
|
import { MoveTool } from './tools/move';
|
||||||
import { vec2Distance, vec2Snap, vec2Sub } from './utils';
|
import {
|
||||||
|
vec2Add,
|
||||||
|
vec2Distance,
|
||||||
|
vec2DivideScalar,
|
||||||
|
vec2MultiplyScalar,
|
||||||
|
vec2Snap,
|
||||||
|
vec2Sub,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
export class HousePlannerCanvasTools {
|
export class HousePlannerCanvasTools {
|
||||||
public selectedLayer?: Layer;
|
public selectedLayer?: Layer;
|
||||||
@ -237,12 +246,7 @@ export class HousePlannerCanvasTools {
|
|||||||
this.dragEvent(e.clientX, e.clientY);
|
this.dragEvent(e.clientX, e.clientY);
|
||||||
}
|
}
|
||||||
onMouseDown(e: MouseEvent) {
|
onMouseDown(e: MouseEvent) {
|
||||||
this.mousePosition = [e.clientX, e.clientY];
|
this.mouseClickPosition = [e.clientX, e.clientY];
|
||||||
this.mouseClickPosition = [...this.mousePosition];
|
|
||||||
this.mousePositionSnapped = this.gridSnap
|
|
||||||
? vec2Snap(this.mousePosition, this.gridSnapScale)
|
|
||||||
: this.mousePosition;
|
|
||||||
this.dragging = true;
|
|
||||||
this.moved = false;
|
this.moved = false;
|
||||||
|
|
||||||
this.pointerDown();
|
this.pointerDown();
|
||||||
@ -282,11 +286,6 @@ export class HousePlannerCanvasTools {
|
|||||||
onTouchStart(e: TouchEvent) {
|
onTouchStart(e: TouchEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const touch = e.touches[0] || e.changedTouches[0];
|
const touch = e.touches[0] || e.changedTouches[0];
|
||||||
this.mousePosition = [touch.pageX, touch.pageY];
|
|
||||||
this.mouseClickPosition = [...this.mousePosition];
|
|
||||||
this.mousePositionSnapped = this.gridSnap
|
|
||||||
? vec2Snap(this.mousePosition, this.gridSnapScale)
|
|
||||||
: this.mousePosition;
|
|
||||||
this.dragging = true;
|
this.dragging = true;
|
||||||
this.moved = false;
|
this.moved = false;
|
||||||
|
|
||||||
@ -309,18 +308,43 @@ export class HousePlannerCanvasTools {
|
|||||||
|
|
||||||
onMouseWheel(e: WheelEvent) {
|
onMouseWheel(e: WheelEvent) {
|
||||||
// TODO: zoom
|
// TODO: zoom
|
||||||
// ev.preventDefault();
|
e.preventDefault();
|
||||||
// this._mousex = ev.clientX;
|
this.realMousePos(e.clientX, e.clientY);
|
||||||
// this._mousey = ev.clientY;
|
|
||||||
// const scaleX = (ev.clientX - this._posx) / this._zoom;
|
const scaleX =
|
||||||
// const scaleY = (ev.clientY - this._posy) / this._zoom;
|
(e.clientX - this.manager.canvasPos.value[0]) / this.manager.canvasZoom;
|
||||||
// ev.deltaY < 0 ? (this._zoom *= 1.2) : (this._zoom /= 1.2);
|
const scaleY =
|
||||||
// this._zoom = clamp(this._zoom, this._minZoom, this._maxZoom);
|
(e.clientY - this.manager.canvasPos.value[1]) / this.manager.canvasZoom;
|
||||||
// this._posx = ev.clientX - scaleX * this._zoom;
|
e.deltaY < 0
|
||||||
// this._posy = ev.clientY - scaleY * this._zoom;
|
? (this.manager.canvasZoom *= 1.2)
|
||||||
// const realSize = this._zoom * this._size;
|
: (this.manager.canvasZoom /= 1.2);
|
||||||
// this._posx = clamp(this._posx, this._cursorx - realSize, this._cursorx);
|
this.manager.canvasZoom = clamp(this.manager.canvasZoom, 0.1, 10);
|
||||||
// this._posy = clamp(this._posy, this._cursory - realSize, this._cursory);
|
this.manager.canvasPos.value = [
|
||||||
|
e.clientX - scaleX * this.manager.canvasZoom,
|
||||||
|
e.clientY - scaleY * this.manager.canvasZoom,
|
||||||
|
];
|
||||||
|
|
||||||
|
this.manager.canvasPos.value = [
|
||||||
|
clamp(
|
||||||
|
this.manager.canvasPos.value[0],
|
||||||
|
this.mousePosition[0] - this.manager.canvasDim[0],
|
||||||
|
this.mousePosition[0]
|
||||||
|
),
|
||||||
|
clamp(
|
||||||
|
this.manager.canvasPos.value[1],
|
||||||
|
this.mousePosition[1] - this.manager.canvasDim[1],
|
||||||
|
this.mousePosition[1]
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
this.canvas.dispatchEvent(
|
||||||
|
new CustomEvent<RepositionEvent>('hpc:position', {
|
||||||
|
detail: {
|
||||||
|
position: this.manager.canvasPos.value,
|
||||||
|
zoom: this.manager.canvasZoom,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
onPointerLeave() {
|
onPointerLeave() {
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
@ -419,6 +443,9 @@ export class HousePlannerCanvasTools {
|
|||||||
private pointerDown() {
|
private pointerDown() {
|
||||||
this.clickedOn = this.getMousedObject();
|
this.clickedOn = this.getMousedObject();
|
||||||
this.tool?.mouseDown(this.clickedOn || undefined);
|
this.tool?.mouseDown(this.clickedOn || undefined);
|
||||||
|
if (!this.clickedOn) {
|
||||||
|
this.dragging = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private pointerUp() {
|
private pointerUp() {
|
||||||
@ -432,29 +459,38 @@ export class HousePlannerCanvasTools {
|
|||||||
|
|
||||||
this.tool?.mouseUp(this.moved);
|
this.tool?.mouseUp(this.moved);
|
||||||
|
|
||||||
|
this.dragging = false;
|
||||||
this.clickedOn = null;
|
this.clickedOn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private dragEvent(x: number, y: number) {
|
private dragEvent(x: number, y: number) {
|
||||||
this.moved = true;
|
this.moved = true;
|
||||||
|
|
||||||
const currentPos = this.gridSnap
|
const currentPosAbsolute: Vec2 = [...this.mousePosition];
|
||||||
? vec2Snap(this.mousePosition, this.gridSnapScale)
|
const currentPosSnapped: Vec2 = [
|
||||||
: this.mousePosition;
|
...(this.gridSnap ? this.mousePositionSnapped : this.mousePosition),
|
||||||
|
];
|
||||||
|
|
||||||
const rect = this.manager.canvas.getBoundingClientRect();
|
this.realMousePos(x, y);
|
||||||
this.mousePosition = [x - rect.left, y - rect.top];
|
let offset = vec2Sub(currentPosSnapped, this.mousePositionSnapped);
|
||||||
this.mousePositionSnapped = this.gridSnap
|
let offsetAbsolute = vec2Sub(currentPosAbsolute, this.mousePosition);
|
||||||
? vec2Snap(this.mousePosition, this.gridSnapScale)
|
|
||||||
: this.mousePosition;
|
|
||||||
|
|
||||||
let offset = vec2Sub(currentPos, this.mousePositionSnapped);
|
|
||||||
|
|
||||||
this.tool?.mouseMoved(
|
this.tool?.mouseMoved(
|
||||||
this.mousePositionSnapped,
|
this.mousePositionSnapped,
|
||||||
offset,
|
offset,
|
||||||
this.mousePosition
|
this.mousePosition
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.dragging) {
|
||||||
|
const divided = vec2MultiplyScalar(
|
||||||
|
offsetAbsolute,
|
||||||
|
this.manager.canvasZoom
|
||||||
|
);
|
||||||
|
this.manager.translateCanvas(divided);
|
||||||
|
// Bias the offset
|
||||||
|
const [nx, ny] = vec2Add([x, y], divided);
|
||||||
|
this.realMousePos(nx, ny);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private selectedEvent() {
|
private selectedEvent() {
|
||||||
@ -464,4 +500,14 @@ export class HousePlannerCanvasTools {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private realMousePos(x: number, y: number) {
|
||||||
|
const rect = this.manager.canvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.canvas.width / rect.width;
|
||||||
|
const scaleY = this.canvas.height / rect.height;
|
||||||
|
this.mousePosition = [(x - rect.left) * scaleX, (y - rect.top) * scaleY];
|
||||||
|
this.mousePositionSnapped = this.gridSnap
|
||||||
|
? vec2Snap(this.mousePosition, this.gridSnapScale)
|
||||||
|
: this.mousePosition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { clamp } from '@vueuse/shared';
|
||||||
import { Vec2 } from './interfaces';
|
import { Vec2 } from './interfaces';
|
||||||
|
|
||||||
export const vec2Length = ([x, y]: Vec2) => Math.abs(Math.sqrt(x * x + y * y));
|
export const vec2Length = ([x, y]: Vec2) => Math.abs(Math.sqrt(x * x + y * y));
|
||||||
@ -7,11 +8,22 @@ export const vec2Add = ([x1, y1]: Vec2, [x2, y2]: Vec2): Vec2 => [
|
|||||||
y1 + y2,
|
y1 + y2,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const vec2Multiply = ([x1, y1]: Vec2, [x2, y2]: Vec2): Vec2 => [
|
||||||
|
x1 * x2,
|
||||||
|
y1 * y2,
|
||||||
|
];
|
||||||
|
|
||||||
export const vec2Sub = ([x1, y1]: Vec2, [x2, y2]: Vec2): Vec2 => [
|
export const vec2Sub = ([x1, y1]: Vec2, [x2, y2]: Vec2): Vec2 => [
|
||||||
x1 - x2,
|
x1 - x2,
|
||||||
y1 - y2,
|
y1 - y2,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const vec2Clamp = (
|
||||||
|
[x1, y1]: Vec2,
|
||||||
|
[x2, y2]: Vec2,
|
||||||
|
[x3, y3]: Vec2
|
||||||
|
): Vec2 => [clamp(x1, x2, x3), clamp(y1, y2, y3)];
|
||||||
|
|
||||||
export const vec2MultiplyScalar = ([x, y]: Vec2, scalar: number): Vec2 => [
|
export const vec2MultiplyScalar = ([x, y]: Vec2, scalar: number): Vec2 => [
|
||||||
x * scalar,
|
x * scalar,
|
||||||
y * scalar,
|
y * scalar,
|
||||||
|
Loading…
Reference in New Issue
Block a user