UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

567 lines (566 loc) • 15.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Box_exports = {}; __export(Box_exports, { Box: () => Box, ROTATE_CORNER_TO_SELECTION_CORNER: () => ROTATE_CORNER_TO_SELECTION_CORNER, flipSelectionHandleX: () => flipSelectionHandleX, flipSelectionHandleY: () => flipSelectionHandleY, isSelectionCorner: () => isSelectionCorner, rotateSelectionHandle: () => rotateSelectionHandle }); module.exports = __toCommonJS(Box_exports); var import_Vec = require("./Vec"); var import_utils = require("./utils"); class Box { constructor(x = 0, y = 0, w = 0, h = 0) { this.x = x; this.y = y; this.w = w; this.h = h; } x = 0; y = 0; w = 0; h = 0; // eslint-disable-next-line no-restricted-syntax get point() { return new import_Vec.Vec(this.x, this.y); } // eslint-disable-next-line no-restricted-syntax set point(val) { this.x = val.x; this.y = val.y; } // eslint-disable-next-line no-restricted-syntax get minX() { return this.x; } // eslint-disable-next-line no-restricted-syntax set minX(n) { this.x = n; } // eslint-disable-next-line no-restricted-syntax get left() { return this.x; } // eslint-disable-next-line no-restricted-syntax get midX() { return this.x + this.w / 2; } // eslint-disable-next-line no-restricted-syntax get maxX() { return this.x + this.w; } // eslint-disable-next-line no-restricted-syntax get right() { return this.x + this.w; } // eslint-disable-next-line no-restricted-syntax get minY() { return this.y; } // eslint-disable-next-line no-restricted-syntax set minY(n) { this.y = n; } // eslint-disable-next-line no-restricted-syntax get top() { return this.y; } // eslint-disable-next-line no-restricted-syntax get midY() { return this.y + this.h / 2; } // eslint-disable-next-line no-restricted-syntax get maxY() { return this.y + this.h; } // eslint-disable-next-line no-restricted-syntax get bottom() { return this.y + this.h; } // eslint-disable-next-line no-restricted-syntax get width() { return this.w; } // eslint-disable-next-line no-restricted-syntax set width(n) { this.w = n; } // eslint-disable-next-line no-restricted-syntax get height() { return this.h; } // eslint-disable-next-line no-restricted-syntax set height(n) { this.h = n; } // eslint-disable-next-line no-restricted-syntax get aspectRatio() { return this.width / this.height; } // eslint-disable-next-line no-restricted-syntax get center() { return new import_Vec.Vec(this.x + this.w / 2, this.y + this.h / 2); } // eslint-disable-next-line no-restricted-syntax set center(v) { this.x = v.x - this.w / 2; this.y = v.y - this.h / 2; } // eslint-disable-next-line no-restricted-syntax get corners() { return [ new import_Vec.Vec(this.x, this.y), new import_Vec.Vec(this.x + this.w, this.y), new import_Vec.Vec(this.x + this.w, this.y + this.h), new import_Vec.Vec(this.x, this.y + this.h) ]; } // eslint-disable-next-line no-restricted-syntax get cornersAndCenter() { return [ new import_Vec.Vec(this.x, this.y), new import_Vec.Vec(this.x + this.w, this.y), new import_Vec.Vec(this.x + this.w, this.y + this.h), new import_Vec.Vec(this.x, this.y + this.h), new import_Vec.Vec(this.x + this.w / 2, this.y + this.h / 2) ]; } // eslint-disable-next-line no-restricted-syntax get sides() { const { corners } = this; return [ [corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]] ]; } // eslint-disable-next-line no-restricted-syntax get size() { return new import_Vec.Vec(this.w, this.h); } toFixed() { this.x = (0, import_utils.toPrecision)(this.x); this.y = (0, import_utils.toPrecision)(this.y); this.w = (0, import_utils.toPrecision)(this.w); this.h = (0, import_utils.toPrecision)(this.h); return this; } setTo(B) { this.x = B.x; this.y = B.y; this.w = B.w; this.h = B.h; return this; } set(x = 0, y = 0, w = 0, h = 0) { this.x = x; this.y = y; this.w = w; this.h = h; return this; } expand(A) { const minX = Math.min(this.x, A.x); const minY = Math.min(this.y, A.y); const maxX = Math.max(this.x + this.w, A.x + A.w); const maxY = Math.max(this.y + this.h, A.y + A.h); this.x = minX; this.y = minY; this.w = maxX - minX; this.h = maxY - minY; return this; } expandBy(n) { this.x -= n; this.y -= n; this.w += n * 2; this.h += n * 2; return this; } scale(n) { this.x /= n; this.y /= n; this.w /= n; this.h /= n; return this; } clone() { const { x, y, w, h } = this; return new Box(x, y, w, h); } translate(delta) { this.x += delta.x; this.y += delta.y; return this; } snapToGrid(size) { const minX = Math.round(this.x / size) * size; const minY = Math.round(this.y / size) * size; const maxX = Math.round((this.x + this.w) / size) * size; const maxY = Math.round((this.y + this.h) / size) * size; this.minX = minX; this.minY = minY; this.width = Math.max(1, maxX - minX); this.height = Math.max(1, maxY - minY); } collides(B) { return Box.Collides(this, B); } contains(B) { return Box.Contains(this, B); } includes(B) { return Box.Includes(this, B); } containsPoint(V, margin = 0) { return Box.ContainsPoint(this, V, margin); } getHandlePoint(handle) { switch (handle) { case "top_left": return new import_Vec.Vec(this.x, this.y); case "top_right": return new import_Vec.Vec(this.x + this.w, this.y); case "bottom_left": return new import_Vec.Vec(this.x, this.y + this.h); case "bottom_right": return new import_Vec.Vec(this.x + this.w, this.y + this.h); case "top": return new import_Vec.Vec(this.x + this.w / 2, this.y); case "right": return new import_Vec.Vec(this.x + this.w, this.y + this.h / 2); case "bottom": return new import_Vec.Vec(this.x + this.w / 2, this.y + this.h); case "left": return new import_Vec.Vec(this.x, this.y + this.h / 2); } } toJson() { return { x: this.x, y: this.y, w: this.w, h: this.h }; } resize(handle, dx, dy) { const { minX: a0x, minY: a0y, maxX: a1x, maxY: a1y } = this; let { minX: b0x, minY: b0y, maxX: b1x, maxY: b1y } = this; switch (handle) { case "left": case "top_left": case "bottom_left": { b0x += dx; break; } case "right": case "top_right": case "bottom_right": { b1x += dx; break; } } switch (handle) { case "top": case "top_left": case "top_right": { b0y += dy; break; } case "bottom": case "bottom_left": case "bottom_right": { b1y += dy; break; } } const scaleX = (b1x - b0x) / (a1x - a0x); const scaleY = (b1y - b0y) / (a1y - a0y); const flipX = scaleX < 0; const flipY = scaleY < 0; if (flipX) { const t = b1x; b1x = b0x; b0x = t; } if (flipY) { const t = b1y; b1y = b0y; b0y = t; } this.minX = b0x; this.minY = b0y; this.width = Math.abs(b1x - b0x); this.height = Math.abs(b1y - b0y); } union(box) { const minX = Math.min(this.x, box.x); const minY = Math.min(this.y, box.y); const maxX = Math.max(this.x + this.w, box.x + box.w); const maxY = Math.max(this.y + this.h, box.y + box.h); this.x = minX; this.y = minY; this.width = maxX - minX; this.height = maxY - minY; return this; } static From(box) { return new Box(box.x, box.y, box.w, box.h); } static FromCenter(center, size) { return new Box(center.x - size.x / 2, center.y - size.y / 2, size.x, size.y); } static FromPoints(points) { if (points.length === 0) return new Box(); let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; let point; for (let i = 0, n = points.length; i < n; i++) { point = points[i]; minX = Math.min(point.x, minX); minY = Math.min(point.y, minY); maxX = Math.max(point.x, maxX); maxY = Math.max(point.y, maxY); } return new Box(minX, minY, maxX - minX, maxY - minY); } static Expand(A, B) { const minX = Math.min(B.minX, A.minX); const minY = Math.min(B.minY, A.minY); const maxX = Math.max(B.maxX, A.maxX); const maxY = Math.max(B.maxY, A.maxY); return new Box(minX, minY, maxX - minX, maxY - minY); } static ExpandBy(A, n) { return new Box(A.minX - n, A.minY - n, A.width + n * 2, A.height + n * 2); } static Collides(A, B) { return !(A.maxX < B.minX || A.minX > B.maxX || A.maxY < B.minY || A.minY > B.maxY); } static Contains(A, B) { return A.minX < B.minX && A.minY < B.minY && A.maxY > B.maxY && A.maxX > B.maxX; } static Includes(A, B) { return Box.Collides(A, B) || Box.Contains(A, B); } static ContainsPoint(A, B, margin = 0) { return !(B.x < A.minX - margin || B.y < A.minY - margin || B.x > A.maxX + margin || B.y > A.maxY + margin); } static Common(boxes) { let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; for (let i = 0; i < boxes.length; i++) { const B = boxes[i]; minX = Math.min(minX, B.minX); minY = Math.min(minY, B.minY); maxX = Math.max(maxX, B.maxX); maxY = Math.max(maxY, B.maxY); } return new Box(minX, minY, maxX - minX, maxY - minY); } static Sides(A, inset = 0) { const { corners } = A; if (inset) { } return [ [corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]] ]; } static Resize(box, handle, dx, dy, isAspectRatioLocked = false) { const { minX: a0x, minY: a0y, maxX: a1x, maxY: a1y } = box; let { minX: b0x, minY: b0y, maxX: b1x, maxY: b1y } = box; switch (handle) { case "left": case "top_left": case "bottom_left": { b0x += dx; break; } case "right": case "top_right": case "bottom_right": { b1x += dx; break; } } switch (handle) { case "top": case "top_left": case "top_right": { b0y += dy; break; } case "bottom": case "bottom_left": case "bottom_right": { b1y += dy; break; } } const scaleX = (b1x - b0x) / (a1x - a0x); const scaleY = (b1y - b0y) / (a1y - a0y); const flipX = scaleX < 0; const flipY = scaleY < 0; if (isAspectRatioLocked) { const aspectRatio = (a1x - a0x) / (a1y - a0y); const bw = Math.abs(b1x - b0x); const bh = Math.abs(b1y - b0y); const tw = bw * (scaleY < 0 ? 1 : -1) * (1 / aspectRatio); const th = bh * (scaleX < 0 ? 1 : -1) * aspectRatio; const isTall = aspectRatio < bw / bh; switch (handle) { case "top_left": { if (isTall) b0y = b1y + tw; else b0x = b1x + th; break; } case "top_right": { if (isTall) b0y = b1y + tw; else b1x = b0x - th; break; } case "bottom_right": { if (isTall) b1y = b0y - tw; else b1x = b0x - th; break; } case "bottom_left": { if (isTall) b1y = b0y - tw; else b0x = b1x + th; break; } case "bottom": case "top": { const m = (b0x + b1x) / 2; const w = bh * aspectRatio; b0x = m - w / 2; b1x = m + w / 2; break; } case "left": case "right": { const m = (b0y + b1y) / 2; const h = bw / aspectRatio; b0y = m - h / 2; b1y = m + h / 2; break; } } } if (flipX) { const t = b1x; b1x = b0x; b0x = t; } if (flipY) { const t = b1y; b1y = b0y; b0y = t; } const final = new Box(b0x, b0y, Math.abs(b1x - b0x), Math.abs(b1y - b0y)); return { box: final, scaleX: +(final.width / box.width * (scaleX > 0 ? 1 : -1)).toFixed(5), scaleY: +(final.height / box.height * (scaleY > 0 ? 1 : -1)).toFixed(5) }; } equals(other) { return Box.Equals(this, other); } static Equals(a, b) { return b.x === a.x && b.y === a.y && b.w === a.w && b.h === a.h; } zeroFix() { this.w = Math.max(1, this.w); this.h = Math.max(1, this.h); return this; } static ZeroFix(other) { return new Box(other.x, other.y, Math.max(1, other.w), Math.max(1, other.h)); } } function flipSelectionHandleY(handle) { switch (handle) { case "top": return "bottom"; case "bottom": return "top"; case "top_left": return "bottom_left"; case "top_right": return "bottom_right"; case "bottom_left": return "top_left"; case "bottom_right": return "top_right"; default: return handle; } } function flipSelectionHandleX(handle) { switch (handle) { case "left": return "right"; case "right": return "left"; case "top_left": return "top_right"; case "top_right": return "top_left"; case "bottom_left": return "bottom_right"; case "bottom_right": return "bottom_left"; default: return handle; } } const ORDERED_SELECTION_HANDLES = [ "top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left" ]; function rotateSelectionHandle(handle, rotation) { rotation = rotation % import_utils.PI2; const numSteps = Math.round(rotation / (import_utils.PI / 4)); const currentIndex = ORDERED_SELECTION_HANDLES.indexOf(handle); return ORDERED_SELECTION_HANDLES[(currentIndex + numSteps) % ORDERED_SELECTION_HANDLES.length]; } function isSelectionCorner(selection) { return selection === "top_left" || selection === "top_right" || selection === "bottom_right" || selection === "bottom_left"; } const ROTATE_CORNER_TO_SELECTION_CORNER = { top_left_rotate: "top_left", top_right_rotate: "top_right", bottom_right_rotate: "bottom_right", bottom_left_rotate: "bottom_left", mobile_rotate: "top_left" }; //# sourceMappingURL=Box.js.map