UNPKG

modern-canvas

Version:

A JavaScript WebGL rendering engine.

1,826 lines (1,796 loc) 416 kB
'use strict'; const modernIdoc = require('modern-idoc'); const colord = require('colord'); const namesPlugin = require('colord/plugins/names'); const modernPath2d = require('modern-path2d'); const modernText = require('modern-text'); const load = require('yoga-layout/load'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const namesPlugin__default = /*#__PURE__*/_interopDefaultCompat(namesPlugin); const customNodes = /* @__PURE__ */ new Map(); function customNode(name, defaultProperties) { return function(constructor) { Object.defineProperty(constructor.prototype, "is", { value: name, enumerable: true, configurable: true }); if (defaultProperties) { Object.keys(defaultProperties).forEach((key) => { modernIdoc.defineProperty(constructor, key, { fallback: defaultProperties[key] }); }); } customNodes.set(name, constructor); }; } function createNode(tag = "node", options = {}) { const Klass = customNodes.get(tag); if (!Klass) { throw new Error(`Failed to createNode, tag: ${tag}`); } return new Klass().setProperties(options); } class Ticker { static _queue = []; static _currentTime = 0; static _elapsed = 0; static _requestId; static get currentTime() { return this._currentTime; } static get elapsed() { return this._elapsed; } static on(cb, options = {}) { const { sort = 0, once = false } = options; if (!this._queue[sort]) this._queue[sort] = []; this._queue[sort].push({ cb, once }); } static off(cb, options = {}) { const { sort = 0 } = options; const items = this._queue[sort]; if (!items) return; const newItems = []; for (let len = items.length, i = 0; i < len; i++) { const item = items[i]; if (item.cb !== cb) { newItems.push(item); } } if (newItems.length) { this._queue[sort] = newItems; } else { this._queue.splice(sort, 1); } } static start() { if ("requestAnimationFrame" in globalThis) { this._currentTime = performance.now(); this._requestId = requestAnimationFrame((time) => this._update(time)); } } static stop() { if (this._requestId) { cancelAnimationFrame(this._requestId); this._requestId = void 0; } } static _update(time) { this._elapsed = time - this._currentTime; this._currentTime = time; this._requestId = requestAnimationFrame((time2) => this._update(time2)); this._performUpdate(); } static _performUpdate() { const queue = this._queue; const newQueue = []; for (let len = queue.length, i = len; i >= 0; i--) { const items = queue[i]; if (!items) continue; const newItems = []; for (let len2 = items.length, i2 = 0; i2 < len2; i2++) { const item = items[i2]; try { item.cb(); } catch (err) { console.warn(err); } if (!item.once) { newItems.push(item); } } if (newItems.length) { newQueue[i] = newItems; } } this._queue = newQueue; } } Ticker.start(); async function nextTick(cb) { return new Promise((resolve) => { Ticker.on( () => { cb?.(); resolve(); }, { sort: 1, once: true } ); }); } const PI = Math.PI; const PI_2 = PI * 2; let UID = 0; function uid(object) { return object?.__SPECTOR_Object_TAG?.id ?? ++UID; } function isPow2(v) { return !(v & v - 1) && !!v; } const FUNCTIONS_RE = /([\w-]+)\((.+?)\)/g; const ARGS_RE = /[^,]+/g; const ARG_RE = /([-e.\d]+)(.*)/; function getDefaultCssPropertyValue(value) { if (Array.isArray(value)) { return value.map((func) => { return { name: func.name, args: func.args.map((arg) => { return { ...arg, normalizedIntValue: arg.normalizedDefaultIntValue }; }) }; }); } else { return { ...value, normalizedIntValue: value.normalizedDefaultIntValue }; } } function parseCssProperty(name, propertyValue, context = {}) { const functions = parseCssFunctions(propertyValue, context); return functions.length ? functions : parseArgument(name, propertyValue, context); } function parseCssFunctions(propertyValue, context = {}) { const functions = []; let match; while ((match = FUNCTIONS_RE.exec(propertyValue)) !== null) { const [, name, value] = match; if (name) { functions.push({ name, args: parseArguments(name, value, context) }); } } return functions; } function parseArguments(name, value, context = {}) { const values = []; let match; let i = 0; while ((match = ARGS_RE.exec(value)) !== null) { values.push( parseArgument(name, match[0], { ...context, index: i++ }) ); } return values; } function parseArgument(name, value, context = {}) { const { width = 1, height = 1, index = 0 } = context; const matched = value.match(ARG_RE); const result = { unit: matched?.[2] ?? null, value, intValue: Number(matched?.[1]), normalizedIntValue: 0, normalizedDefaultIntValue: 0 }; switch (name) { case "scale": case "scaleX": case "scaleY": case "scale3d": result.normalizedDefaultIntValue = 1; break; } switch (result.unit) { case "%": result.normalizedIntValue = result.intValue / 100; break; case "rad": result.normalizedIntValue = result.intValue / PI_2; break; case "deg": result.normalizedIntValue = result.intValue / 360; break; case "px": switch (index) { case 0: result.normalizedIntValue = result.intValue / width; break; case 1: result.normalizedIntValue = result.intValue / height; break; } break; case "turn": case "em": // div fontSize case "rem": // div fontSize default: result.normalizedIntValue = result.intValue; break; } return result; } const SUPPORTS_WEBGL2 = "WebGL2RenderingContext" in globalThis; const SUPPORTS_IMAGE_BITMAP = "ImageBitmap" in globalThis; const SUPPORTS_RESIZE_OBSERVER = "ResizeObserver" in globalThis; const SUPPORTS_POINTER_EVENTS = "PointerEvent" in globalThis; const SUPPORTS_WHEEL_EVENTS = "WheelEvent" in globalThis; const SUPPORTS_MOUSE_EVENTS = "MouseEvent" in globalThis; const SUPPORTS_TOUCH_EVENTS = "ontouchstart" in globalThis; const SUPPORTS_CLICK_EVENTS = "onclick" in globalThis; const SUPPORTS_CREATE_IMAGE_BITMAP = "createImageBitmap" in globalThis; const SUPPORTS_AUDIO_CONTEXT = "AudioContext" in globalThis; const SUPPORTS_WEBKIT_AUDIO_CONTEXT = "webkitAudioContext" in globalThis; const SUPPORTS_OFFLINE_AUDIO_CONTEXT = "OfflineAudioContext" in globalThis; const SUPPORTS_WEBKIT_OFFLINE_AUDIO_CONTEXT = "webkitOfflineAudioContext" in globalThis; const SUPPORTS_WEB_AUDIO = SUPPORTS_AUDIO_CONTEXT || SUPPORTS_WEBKIT_AUDIO_CONTEXT; const IN_BROWSER = typeof window !== "undefined"; const DEVICE_PIXEL_RATIO = globalThis.devicePixelRatio || 1; const isElementNode = (node) => node !== null && typeof node === "object" && node.nodeType === 1; const isVideoElement = (node) => isElementNode(node) && node.tagName === "VIDEO"; const isImageElement = (node) => isElementNode(node) && node.tagName === "IMG"; function isCanvasElement(node) { return typeof node === "object" && node !== null && node.nodeType === 1 && node.tagName === "CANVAS"; } function isWebgl2(gl) { return SUPPORTS_WEBGL2 && gl instanceof globalThis.WebGL2RenderingContext; } function createHTMLCanvas() { if (IN_BROWSER) { return globalThis.document.createElement("canvas"); } return void 0; } function determineCrossOrigin(url, loc = globalThis.location) { if (url.startsWith("data:")) { return ""; } loc = loc || globalThis.location; const parsedUrl = new URL(url, document.baseURI); if (parsedUrl.hostname !== loc.hostname || parsedUrl.port !== loc.port || parsedUrl.protocol !== loc.protocol) { return "anonymous"; } return ""; } function crossOrigin(element, url, crossorigin) { if (crossorigin === null && !url.startsWith("data:")) { element.crossOrigin = determineCrossOrigin(url); } else if (crossorigin !== false) { element.crossOrigin = typeof crossorigin === "string" ? crossorigin : "anonymous"; } } class RawWeakMap { _map = /* @__PURE__ */ new WeakMap(); // fix: vue reactive object _toRaw(value) { if (value && typeof value === "object") { const raw = value.__v_raw; if (raw) { value = this._toRaw(raw); } } return value; } /** * Removes the specified element from the WeakMap. * @returns true if the element was successfully removed, or false if it was not present. */ delete(key) { return this._map.delete(this._toRaw(key)); } /** * @returns a specified element. */ get(key) { return this._map.get(this._toRaw(key)); } /** * @returns a boolean indicating whether an element with the specified key exists or not. */ has(key) { return this._map.has(this._toRaw(key)); } /** * Adds a new element with a specified key and value. * @param key Must be an object or symbol. */ set(key, value) { this._map.set(this._toRaw(key), this._toRaw(value)); return this; } } class InputEvent { cursor; // Event bubbles = false; cancelBubble = false; cancelable = false; composed = false; defaultPrevented = false; eventPhase = InputEvent.prototype.NONE; isTrusted = false; timeStamp = 0; type = ""; composedPath() { return this.path; } initEvent(..._args) { throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API."); } preventDefault() { if ("preventDefault" in this.nativeEvent && this.nativeEvent.cancelable) { this.nativeEvent.preventDefault(); } this.defaultPrevented = true; } propagationImmediatelyStopped = false; stopImmediatePropagation() { this.propagationImmediatelyStopped = true; } propagationStopped = false; stopPropagation() { if ("stopPropagation" in this.nativeEvent) { this.nativeEvent.stopPropagation(); } this.propagationStopped = true; } NONE = 0; CAPTURING_PHASE = 1; AT_TARGET = 2; BUBBLING_PHASE = 3; initUIEvent(..._args) { throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API."); } } class KeyboardInputEvent extends InputEvent { getModifierState(..._args) { throw new Error("getModifierState() is a legacy DOM API. It is not implemented in the Federated Events API."); } initKeyboardEvent(..._args) { throw new Error("initKeyboardEvent() is a legacy DOM API. It is not implemented in the Federated Events API."); } } class MouseInputEvent extends InputEvent { client = { x: 0, y: 0 }; get clientX() { return this.client.x; } get clientY() { return this.client.y; } layer = { x: 0, y: 0 }; get layerX() { return this.layer.x; } get layerY() { return this.layer.y; } movement = { x: 0, y: 0 }; get movementX() { return this.movement.x; } get movementY() { return this.movement.y; } offset = { x: 0, y: 0 }; get offsetX() { return this.offset.x; } get offsetY() { return this.offset.y; } page = { x: 0, y: 0 }; get pageX() { return this.page.x; } get pageY() { return this.page.y; } screen = { x: 0, y: 0 }; get screenX() { return this.screen.x; } get screenY() { return this.screen.y; } get x() { return this.clientX; } get y() { return this.clientY; } getModifierState(key) { return "getModifierState" in this.nativeEvent && this.nativeEvent.getModifierState(key); } initMouseEvent(..._args) { throw new Error("Method not implemented."); } global = { x: 0, y: 0 }; get globalX() { return this.global.x; } get globalY() { return this.global.y; } } class PointerInputEvent extends MouseInputEvent { width = 0; height = 0; isPrimary = false; getCoalescedEvents() { if (this.type === "pointermove" || this.type === "mousemove" || this.type === "touchmove") { return [this]; } return []; } getPredictedEvents() { throw new Error("getPredictedEvents is not supported!"); } } class WheelInputEvent extends MouseInputEvent { static DOM_DELTA_PIXEL = 0; DOM_DELTA_PIXEL = 0; static DOM_DELTA_LINE = 1; DOM_DELTA_LINE = 1; static DOM_DELTA_PAGE = 2; DOM_DELTA_PAGE = 2; } const TOUCH_TO_POINTER = { touchstart: "pointerdown", touchend: "pointerup", touchendoutside: "pointerupoutside", touchmove: "pointermove", touchcancel: "pointercancel" }; class Input extends modernIdoc.EventEmitter { /** * Current event */ event; target; cursor = "default"; cursorStyles = { default: "inherit", pointer: "pointer" }; setuped = false; enableMoveEvent = true; enableWheelEvent = true; enableClickEvent = true; setTarget(target) { this.removeEventListeners(); this.target = target; this.addEventListeners(); } removeEventListeners() { if (!this.setuped || !this.target) { return; } const style = this.target.style; if (globalThis.navigator.msPointerEnabled) { style.msContentZooming = ""; style.msTouchAction = ""; } else if (SUPPORTS_POINTER_EVENTS) { style.touchAction = ""; } if (SUPPORTS_POINTER_EVENTS) { this.target.removeEventListener("pointerdown", this._onPointerDown); this.target.removeEventListener("pointerleave", this._onPointerOver); this.target.removeEventListener("pointerover", this._onPointerOver); this.target.removeEventListener("pointermove", this._onPointerMove); this.target.removeEventListener("pointerup", this._onPointerUp); } else { this.target.removeEventListener("mousedown", this._onPointerDown); this.target.removeEventListener("mouseout", this._onPointerOver); this.target.removeEventListener("mouseover", this._onPointerOver); this.target.removeEventListener("mousemove", this._onPointerMove); this.target.removeEventListener("mouseup", this._onPointerUp); } if (SUPPORTS_TOUCH_EVENTS) { this.target.removeEventListener("touchstart", this._onPointerDown); this.target.removeEventListener("touchmove", this._onPointerMove); this.target.removeEventListener("touchend", this._onPointerUp); } this.target.removeEventListener("wheel", this._onWheel); document.removeEventListener("keydown", this._onKeyDown); document.removeEventListener("keypress", this._onKeyPress); document.removeEventListener("keyup", this._onKeyUp); this.target = void 0; this.setuped = false; } addEventListeners() { if (this.setuped || !this.target) { return; } const style = this.target.style; if (style) { if (globalThis.navigator.msPointerEnabled) { style.msContentZooming = "none"; style.msTouchAction = "none"; } else if (SUPPORTS_POINTER_EVENTS) { style.touchAction = "none"; } } if (SUPPORTS_POINTER_EVENTS) { this.target.addEventListener("pointerdown", this._onPointerDown); this.target.addEventListener("pointerleave", this._onPointerOver); this.target.addEventListener("pointerover", this._onPointerOver); this.target.addEventListener("pointermove", this._onPointerMove); this.target.addEventListener("pointerup", this._onPointerUp); } else { this.target.addEventListener("mousedown", this._onPointerDown); this.target.addEventListener("mouseout", this._onPointerOver); this.target.addEventListener("mouseover", this._onPointerOver); this.target.addEventListener("mousemove", this._onPointerMove); this.target.addEventListener("mouseup", this._onPointerUp); } if (SUPPORTS_TOUCH_EVENTS) { this.target.addEventListener("touchstart", this._onPointerDown); this.target.addEventListener("touchmove", this._onPointerMove); this.target.addEventListener("touchend", this._onPointerUp); } this.target.addEventListener("wheel", this._onWheel); document.addEventListener("keydown", this._onKeyDown); document.addEventListener("keypress", this._onKeyPress); document.addEventListener("keyup", this._onKeyUp); this.setuped = true; } normalize(event) { const events = []; if (SUPPORTS_TOUCH_EVENTS && event instanceof globalThis.TouchEvent) { for (let i = 0, li = event.changedTouches.length; i < li; i++) { const touch = event.changedTouches[i]; if (typeof touch.button === "undefined") touch.button = 0; if (typeof touch.buttons === "undefined") touch.buttons = 1; if (typeof touch.isPrimary === "undefined") { touch.isPrimary = event.touches.length === 1 && event.type === "touchstart"; } if (typeof touch.width === "undefined") touch.width = touch.radiusX || 1; if (typeof touch.height === "undefined") touch.height = touch.radiusY || 1; if (typeof touch.tiltX === "undefined") touch.tiltX = 0; if (typeof touch.tiltY === "undefined") touch.tiltY = 0; if (typeof touch.pointerType === "undefined") touch.pointerType = "touch"; if (typeof touch.pointerId === "undefined") touch.pointerId = touch.identifier || 0; if (typeof touch.pressure === "undefined") touch.pressure = touch.force || 0.5; if (typeof touch.twist === "undefined") touch.twist = 0; if (typeof touch.tangentialPressure === "undefined") touch.tangentialPressure = 0; if (typeof touch.layerX === "undefined") touch.layerX = touch.offsetX = touch.clientX; if (typeof touch.layerY === "undefined") touch.layerY = touch.offsetY = touch.clientY; touch.type = event.type; events.push(touch); } } else if (SUPPORTS_WHEEL_EVENTS && event instanceof globalThis.WheelEvent) { events.push(event); } else if (SUPPORTS_POINTER_EVENTS && event instanceof globalThis.PointerEvent) { events.push(event); } else { const mouse = event; if (typeof mouse.isPrimary === "undefined") mouse.isPrimary = true; if (typeof mouse.width === "undefined") mouse.width = 1; if (typeof mouse.height === "undefined") mouse.height = 1; if (typeof mouse.tiltX === "undefined") mouse.tiltX = 0; if (typeof mouse.tiltY === "undefined") mouse.tiltY = 0; if (typeof mouse.pointerType === "undefined") mouse.pointerType = "mouse"; if (typeof mouse.pointerId === "undefined") mouse.pointerId = 1; if (typeof mouse.pressure === "undefined") mouse.pressure = 0.5; if (typeof mouse.twist === "undefined") mouse.twist = 0; if (typeof mouse.tangentialPressure === "undefined") mouse.tangentialPressure = 0; events.push(mouse); } return events; } _clonePointerEvent(nativeEvent) { const event = new PointerInputEvent(); event.nativeEvent = nativeEvent; event.pointerId = nativeEvent.pointerId; event.width = nativeEvent.width; event.height = nativeEvent.height; event.isPrimary = nativeEvent.isPrimary; event.pointerType = nativeEvent.pointerType; event.pressure = nativeEvent.pressure; event.tangentialPressure = nativeEvent.tangentialPressure; event.tiltX = nativeEvent.tiltX; event.tiltY = nativeEvent.tiltY; event.twist = nativeEvent.twist; event.isTrusted = nativeEvent.isTrusted; this._copyMouseEvent(event, nativeEvent); this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY); event.global.x = event.screen.x; event.global.y = event.screen.y; event.offset.x = event.screen.x; event.offset.y = event.screen.y; if (event.type === "pointerleave") { event.type = "pointerout"; } else if (event.type.startsWith("mouse")) { event.type = event.type.replace("mouse", "pointer"); } else if (event.type.startsWith("touch")) { event.type = TOUCH_TO_POINTER[event.type] || event.type; } return event; } _copyInputEvent(event, nativeEvent) { event.nativeEvent = nativeEvent; event.bubbles = nativeEvent.bubbles; event.cancelBubble = nativeEvent.cancelBubble; event.cancelable = nativeEvent.cancelable; event.composed = nativeEvent.composed; event.currentTarget = nativeEvent.currentTarget; event.defaultPrevented = nativeEvent.defaultPrevented; event.eventPhase = nativeEvent.eventPhase; event.isTrusted = nativeEvent.isTrusted; event.returnValue = nativeEvent.returnValue; event.srcElement = nativeEvent.srcElement; event.timeStamp = nativeEvent.timeStamp; event.type = nativeEvent.type; } _copyMouseEvent(event, nativeEvent) { this._copyInputEvent(event, nativeEvent); event.altKey = nativeEvent.altKey; event.button = nativeEvent.button; event.buttons = nativeEvent.buttons; event.client.x = nativeEvent.clientX; event.client.y = nativeEvent.clientY; event.ctrlKey = nativeEvent.ctrlKey; event.metaKey = nativeEvent.metaKey; event.movement.x = nativeEvent.movementX; event.movement.y = nativeEvent.movementY; event.page.x = nativeEvent.pageX; event.page.y = nativeEvent.pageY; event.relatedTarget = null; event.shiftKey = nativeEvent.shiftKey; } _cloneWheelEvent(nativeEvent) { const event = new WheelInputEvent(); this._copyMouseEvent(event, nativeEvent); event.wheelDeltaY = nativeEvent.wheelDeltaY; event.deltaX = nativeEvent.deltaX; event.deltaY = nativeEvent.deltaY; event.deltaZ = nativeEvent.deltaZ; event.deltaMode = nativeEvent.deltaMode; this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY); event.global.x = event.screen.x; event.global.y = event.screen.y; event.offset.x = event.screen.x; event.offset.y = event.screen.y; return event; } _cloneKeyboardEvent(nativeEvent) { const event = new KeyboardInputEvent(); this._copyInputEvent(event, nativeEvent); event.altKey = nativeEvent.altKey; event.charCode = nativeEvent.charCode; event.code = nativeEvent.code; event.ctrlKey = nativeEvent.ctrlKey; event.isComposing = nativeEvent.isComposing; event.key = nativeEvent.key; event.keyCode = nativeEvent.keyCode; event.location = nativeEvent.location; event.metaKey = nativeEvent.metaKey; event.repeat = nativeEvent.repeat; event.shiftKey = nativeEvent.shiftKey; return event; } setCursor(mode = "default") { if (!this.target) return; if (this.cursor === mode) { return; } this.cursor = mode; const applyStyles = !(globalThis.OffscreenCanvas && this.target instanceof OffscreenCanvas); const style = this.cursorStyles[mode]; if (style) { switch (typeof style) { case "string": if (applyStyles) { this.target.style.cursor = style; } break; case "function": style(mode); break; case "object": if (applyStyles) { Object.assign(this.target.style, style); } break; } } else if (applyStyles && typeof mode === "string" && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode)) { this.target.style.cursor = mode; } } mapPositionToPoint(point, x, y) { if (!this.target) return; const clientRect = this.target.isConnected ? this.target.getBoundingClientRect() : void 0; const pixelRatio = Number(this.target.getAttribute("data-pixel-ratio")) || 1; const width = Number(this.target.getAttribute("width")) || (clientRect ? clientRect.width * pixelRatio : 0); const height = Number(this.target.getAttribute("height")) || (clientRect ? clientRect.height * pixelRatio : 0); const rect = clientRect ?? { width, height, left: 0, top: 0 }; const multiplier = 1 / pixelRatio; point.x = (x - rect.left) * (width / rect.width) * multiplier; point.y = (y - rect.top) * (height / rect.height) * multiplier; } _onKeyDown = (nativeEvent) => { const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("keydown", this.event = this._cloneKeyboardEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onKeyPress = (nativeEvent) => { const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("keypress", this.event = this._cloneKeyboardEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onKeyUp = (nativeEvent) => { const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("keyup", this.event = this._cloneKeyboardEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onPointerDown = (nativeEvent) => { if (SUPPORTS_TOUCH_EVENTS && nativeEvent.pointerType === "touch") return; const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("pointerdown", this.event = this._clonePointerEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onPointerOver = (nativeEvent) => { if (!this.enableClickEvent) return; if (SUPPORTS_TOUCH_EVENTS && nativeEvent.pointerType === "touch") return; const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("pointerover", this.event = this._clonePointerEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onPointerMove = (nativeEvent) => { if (!this.enableMoveEvent) return; if (SUPPORTS_TOUCH_EVENTS && nativeEvent.pointerType === "touch") return; const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("pointermove", this.event = this._clonePointerEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onPointerUp = (nativeEvent) => { if (!this.enableClickEvent) return; if (SUPPORTS_TOUCH_EVENTS && nativeEvent.pointerType === "touch") return; let target = nativeEvent.target; if (nativeEvent.composedPath && nativeEvent.composedPath().length > 0) { target = nativeEvent.composedPath()[0]; } const outside = target !== this.target ? "outside" : ""; const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { const event = this._clonePointerEvent(events[i]); event.type += outside; this.emit("pointerup", this.event = event); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; _onWheel = (nativeEvent) => { if (!this.enableWheelEvent) return; const events = this.normalize(nativeEvent); for (let i = 0, len = events.length; i < len; i++) { this.emit("wheel", this.event = this._cloneWheelEvent(events[i])); } if (this.event?.cursor) { this.setCursor(this.event.cursor); } }; } let IID = 0; class CoreObject extends modernIdoc.EventEmitter { instanceId = ++IID; _customPropertyAccessor; _properties = /* @__PURE__ */ new Map(); _updatedProperties = /* @__PURE__ */ new Map(); _changedProperties = /* @__PURE__ */ new Set(); _updatingPromise = Promise.resolve(); _updating = false; useCustomPropertyAccessor(accessor) { this._customPropertyAccessor = accessor; this.getPropertyDeclarations().forEach((declaration, key) => { const newValue = accessor.get(key, () => void 0); const oldValue = this._properties.get(key); if (newValue === void 0) { if (oldValue !== void 0) { accessor.set(key, oldValue); } } else if (newValue !== oldValue) { if (declaration.alias && declaration.alias !== key) { this.setProperty(key, newValue); } else { this._properties.set(key, newValue); } this._updateProperty(key, newValue, oldValue, declaration); this.emit("updateProperty", key, newValue, oldValue, declaration); } }); return this; } getter(key, context) { if (context?.declaration.protected) { return this[context.internalKey]; } else { return this._customPropertyAccessor ? this._customPropertyAccessor.get(key, () => this._properties.get(key)) : this._properties.get(key); } } setter(key, value, context) { if (context?.declaration.protected) { this[context.internalKey] = value; } else { if (this._customPropertyAccessor) { this._customPropertyAccessor.set(key, value); } this._properties.set(key, value); } } equal(target) { return Boolean(target && this.instanceId === target.instanceId); } async _enqueueUpdate() { this._updating = true; try { await this._updatingPromise; } catch (e) { Promise.reject(e); } await nextTick(); if (!this._updating) return; this.update(); this._updating = false; } update() { this._update(this._updatedProperties); this._updatedProperties = /* @__PURE__ */ new Map(); } // eslint-disable-next-line unused-imports/no-unused-vars _update(changed) { } // eslint-disable-next-line unused-imports/no-unused-vars _updateProperty(key, value, oldValue, declaration) { } isDirty(key) { return this._updatedProperties.has(key); } getPropertyDeclarations() { return modernIdoc.getDeclarations(this.constructor); } getPropertyDeclaration(key) { return this.getPropertyDeclarations().get(key); } getProperty(key) { return this[key]; } setProperty(key, value) { this[key] = value; return this; } getProperties(keys) { const properties = {}; for (const [name, property] of this.getPropertyDeclarations()) { if (!property.protected && !property.alias && (!keys || keys.includes(name))) { properties[name] = this.getProperty(name); } } return properties; } setProperties(properties) { if (properties && typeof properties === "object") { for (const [name] of this.getPropertyDeclarations()) { if (name in properties) { this.setProperty(name, properties[name]); } } } return this; } resetProperties() { for (const [name, property] of this.getPropertyDeclarations()) { this.setProperty( name, typeof property.fallback === "function" ? property.fallback() : property.fallback ); } return this; } onUpdateProperty(key, newValue, oldValue, declaration) { this.requestUpdate(key, newValue, oldValue, declaration); } requestUpdate(key, newValue, oldValue, declaration) { if (key !== void 0) { if (!Object.is(newValue, oldValue)) { this._updatedProperties.set(key, oldValue); this._changedProperties.add(key); declaration ??= this.getPropertyDeclaration(key); this._updateProperty(key, newValue, oldValue, declaration); this.emit("updateProperty", key, newValue, oldValue, declaration); } else { return; } } if (!this._updating) { this._updatingPromise = this._enqueueUpdate(); } } toJSON() { const json = {}; this._properties.forEach((value, key) => { if (value === void 0) { return; } if (value && typeof value === "object") { if ("toJSON" in value && typeof value.toJSON === "function") { json[key] = value.toJSON(); } else if (Array.isArray(value)) { json[key] = [...value]; } else { json[key] = { ...value }; } } else { json[key] = value; } }); return json; } clone() { return new this.constructor(this.toJSON()); } free() { this.removeAllListeners(); } } class RefCounted extends CoreObject { // } class Resource extends RefCounted { // } colord.extend([namesPlugin__default]); class Color { get value() { return this._value; } set value(value) { this._colord = modernIdoc.parseColor(value ?? "none"); } get r8() { return this._colord.rgba.r; } get g8() { return this._colord.rgba.g; } get b8() { return this._colord.rgba.b; } get a8() { return this._colord.rgba.a * 255 & 255; } get r() { return this.r8 / 255; } get g() { return this.g8 / 255; } get b() { return this.b8 / 255; } get a() { return this._colord.rgba.a; } get rgb() { return (this.r8 << 16) + (this.g8 << 8) + this.b8; } get bgr() { return (this.b8 << 16) + (this.g8 << 8) + this.r8; } get abgr() { return (this.a8 << 24) + this.bgr; } constructor(value = 0) { this.value = value; } toHex() { return this._colord.toHex(); } toRgb() { return this._colord.toRgb(); } toRgbString() { return this._colord.toRgbString(); } toHsl() { return this._colord.toHsl(); } toHslString() { return this._colord.toHslString(); } toHsv() { return this._colord.toHsv(); } toArgb(alpha = this.a, applyToRGB = true) { if (alpha === 1) { return (255 << 24) + this.rgb; } if (alpha === 0) { return applyToRGB ? 0 : this.rgb; } let r = this.r8; let g = this.g8; let b = this.b8; if (applyToRGB) { r = r * alpha + 0.5 | 0; g = g * alpha + 0.5 | 0; b = b * alpha + 0.5 | 0; } return (alpha * 255 << 24) + (r << 16) + (g << 8) + b; } toArray() { return [this.r, this.g, this.b, this.a]; } } class Vector extends modernIdoc.EventEmitter { constructor(dim) { super(); this.dim = dim; } _array = []; get length() { return this.dim; } operate(operator, target, output) { const { dim: length, _array: array } = this; let targetArray; if (typeof target === "number") { targetArray = Array.from({ length }, () => target); } else if (target instanceof Matrix || target instanceof Vector) { targetArray = target.toArray(); } else { targetArray = target; } let outputObject; let outputArray = []; if (!output) { outputObject = this; } else if (output instanceof Vector) { outputObject = output; } else { outputArray = output; } if (target instanceof Matrix) { const { cols } = target; switch (operator) { case "*": for (let x = 0; x < length; x++) { let val = 0; for (let y = 0; y < length; y++) { val += array[x] * targetArray[y * cols + x]; } outputArray[x] = val; } break; default: throw new Error(`Not support operator in '${this.toName()} ${operator} ${target.toName()}'`); } } else { switch (operator) { case "+": for (let i = 0; i < length; i++) { outputArray[i] = array[i] + targetArray[i]; } break; case "-": for (let i = 0; i < length; i++) { outputArray[i] = array[i] - targetArray[i]; } break; case "*": for (let i = 0; i < length; i++) { outputArray[i] = array[i] * targetArray[i]; } break; case "/": for (let i = 0; i < length; i++) { outputArray[i] = array[i] / targetArray[i]; } break; case "rot": { const c = Math.cos(targetArray[0]); const s = Math.sin(targetArray[0]); outputArray[0] = array[0] * c - array[1] * s; outputArray[1] = array[1] * c + array[0] * s; break; } case "==": { let flag = true; for (let i = 0; i < length; i++) { flag = flag && array[i] === targetArray[i]; } return flag; } case "=": for (let i = 0; i < length; i++) { const val = targetArray[i]; if (val !== void 0) { array[i] = val; } } this._onUpdate(array); this.emit("update", array); return this; default: throw new Error(`Not support operator in '${this.toName()} ${operator} Vector'`); } } return outputObject?.set(outputArray) ?? outputArray; } add(value, ...args) { if (args.length && typeof value === "number") { value = [value, ...args]; } return this.operate("+", value); } sub(value, ...args) { if (args.length && typeof value === "number") { value = [value, ...args]; } return this.operate("-", value); } multiply(value, ...args) { if (args.length && typeof value === "number") { value = [value, ...args]; } return this.operate("*", value); } divide(value, ...args) { if (args.length && typeof value === "number") { value = [value, ...args]; } return this.operate("/", value); } rotate(angle) { return this.operate("rot", angle); } set(value, ...args) { if (args.length && typeof value === "number") { value = [value, ...args]; } return this.operate("=", value); } equals(value) { return this.operate("==", value); } copy(value) { return this.set(value); } clone() { const cloned = new this.constructor(); cloned.set(this.toArray()); return cloned; } _onUpdate(_array) { } toName() { return `Vector${this.dim}`; } toArray() { return this._array.slice(); } toJSON() { return this.toArray(); } } class Matrix extends modernIdoc.EventEmitter { constructor(rows, cols, array) { super(); this.rows = rows; this.cols = cols; if (array) { this.set(array); } else { this.identity(); } } _array = []; dirtyId = 0; get length() { return this.cols * this.rows; } operate(operator, target, output) { const { cols, rows, length, _array: array } = this; let targetArray; if (typeof target === "number") { targetArray = Array.from({ length }, () => target); } else if (target instanceof Vector || target instanceof Matrix) { targetArray = target.toArray(); } else { targetArray = target; } let outputObject; let outputArray = []; if (!output) { if (target instanceof Vector) { outputObject = new target.constructor(); } else { outputObject = this; } } else if (output instanceof Vector) { outputObject = output; } else if (output instanceof Matrix) { outputObject = output; } else { outputArray = output; } if (target instanceof Vector) { const { dim } = target; switch (operator) { case "*": for (let y = 0; y < dim; y++) { let sum = 0; for (let i = 0; i < cols; i++) { if (i < dim) { sum += array[y * cols + i] * (targetArray[i] ?? 0); } } outputArray[y] = sum; } break; default: throw new Error(`Not support operator in '${this.toName()} ${operator} ${target.toName()}'`); } } else { switch (operator) { case "*": for (let x = 0; x < cols; x++) { for (let y = 0; y < rows; y++) { const iy = y * cols; let sum = 0; for (let i = 0; i < rows; i++) { const a = iy + i; const b = i * cols + x; sum += array[a] * (targetArray[b] ?? 0); } outputArray[iy + x] = sum; } } break; case "=": for (let i = 0; i < length; i++) { const val = targetArray[i]; if (val !== void 0) { array[i] = val; } } this._onUpdate(array); this.emit("update", array); return this; default: throw new Error(`Not support operator in '${this.toName()} ${operator} Matrix2'`); } } return outputObject?.set(outputArray) ?? outputArray; } identity() { const { cols, rows } = this; const array = []; for (let x = 0; x < cols; x++) { for (let y = 0; y < rows; y++) { const iy = y * cols; const i = x + iy; array[i] = y + iy === i ? 1 : 0; } } return this.set(array); } set(value) { return this.operate("=", value); } copy(value) { return this.set(value); } clone() { const cloned = new this.constructor(); cloned.set(this.toArray()); return cloned; } multiply(value, output) { return this.operate("*", value, output); } _onUpdate(_array) { this.dirtyId++; } toArray(transpose = false) { const { cols, rows, _array: array } = this; if (transpose) { const newArray = []; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { newArray[y + x * cols] = array[x + y * cols]; } } return newArray; } return array.slice(); } toName() { return `Matrix${this.rows}(${this.rows}x${this.cols})`; } toJSON() { return this._array.slice(); } } class Matrix4 extends Matrix { constructor(array) { super(4, 4, array); } } const DEG_TO_RAD = PI / 180; const RAD_TO_DEG = 180 / PI; function clamp(val, min, max) { return Math.max(min, Math.min(val, max)); } function lerp(a, b, weight) { return (1 - weight) * a + weight * b; } const curves = { adaptive: true, maxLength: 10, minSegments: 8, maxSegments: 2048, epsilon: 1e-4, _segmentsCount(length, defaultSegments = 20) { if (!this.adaptive || !length || Number.isNaN(length)) { return defaultSegments; } let result = Math.ceil(length / this.maxLength); if (result < this.minSegments) { result = this.minSegments; } else if (result > this.maxSegments) { result = this.maxSegments; } return result; } }; class Vector4 extends Vector { constructor(x = 0, y = 0, z = 0, m = 0) { super(4); this.set(x, y, z, m); } } class ColorMatrix extends Matrix { constructor(array) { super(4, 5, array); } hueRotate(angle = 0) { const sin = Math.sin(angle); const cos = Math.cos(angle); const lumR = 0.213; const lumG = 0.715; const lumB = 0.072; return this.multiply([ lumR + cos * (1 - lumR) + sin * -lumR, lumG + cos * -lumG + sin * -lumG, lumB + cos * -lumB + sin * (1 - lumB), 0, 0, lumR + cos * -lumR + sin * 0.143, lumG + cos * (1 - lumG) + sin * 0.14, lumB + cos * -lumB + sin * -0.283, 0, 0, lumR + cos * -lumR + sin * -0.787, lumG + cos * -lumG + sin * lumG, lumB + cos * (1 - lumB) + sin * lumB, 0, 0, 0, 0, 0, 1, 0 ]); } saturate(amount = 1) { const x = (amount - 1) * 2 / 3 + 1; const y = (x - 1) * -0.5; return this.multiply([ x, y, y, 0, 0, y, x, y, 0, 0, y, y, x, 0, 0, 0, 0, 0, 1, 0 ]); } brightness(amount = 1) { const v = amount; return this.multiply([ v, 0, 0, 0, 0, 0, v, 0, 0, 0, 0, 0, v, 0, 0, 0, 0, 0, 1, 0 ]); } contrast(amount = 1) { const v = amount; const o = -128 * (v - 1); return this.multiply([ v, 0, 0, 0, o, 0, v, 0, 0, o, 0, 0, v, 0, o, 0, 0, 0, 1, 0 ]); } invert(amount = 1) { const v = lerp(1, -1, amount); const o = lerp(0, 255, amount); return this.multiply([ v, 0, 0, 0, o, 0, v, 0, 0, o, 0, 0, v, 0, o, 0, 0, 0, 1, 0 ]); } sepia(amount = 1) { const v = clamp(amount, 0, 1); return this.multiply([ lerp(1, 0.393, v), lerp(0, 0.7689999, v), lerp(0, 0.18899999, v), 0, 0, lerp(0, 0.349, v), lerp(1, 0.6859999, v), lerp(0, 0.16799999, v), 0, 0, lerp(0, 0.272, v), lerp(0, 0.5339999, v), lerp(1, 0.13099999, v), 0, 0, 0, 0, 0, 1, 0 ]); } opacity(amount = 1) { return this.multiply([ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, amount, 0 ]); } grayscale(amount = 1) { const v = clamp(amount, 0, 1); const r = lerp(1, 0.3, v); const rr = lerp(0, 0.3, v); const g = lerp(1, 0.59, v); const gg = lerp(0, 0.59, v); const b = lerp(1, 0.11, v); const bb = lerp(0, 0.11, v); return this.multiply([ r, gg, bb, 0, 0, rr, g, bb, 0, 0, rr, gg, b, 0, 0, 0, 0, 0, 1, 0 ]); } multiply(target) { const b = target; const a = this._array; return this.set([ // Red Channel a[0] * b[0] + a[1] * b[5] + a[2] * b[10] + a[3] * b[15], a[0] * b[1] + a[1] * b[6] + a[2] * b[11] + a[3] * b[16], a[0] * b[2] + a[1] * b[7] + a[2] * b[12] + a[3] * b[17], a[0] * b[3] + a[1] * b[8] + a[2] * b[13] + a[3] * b[18], a[0] * b[4] + a[1] * b[9] + a[2] * b[14] + a[3] * b[19] + a[4], // Green Channel a[5] * b[0] + a[6] * b[5] + a[7] * b[10] + a[8] * b[15], a[5] * b[1] + a[6] * b[6] + a[7] * b[11] + a[8] * b[16], a[5] * b[2] + a[6] * b[7] + a[7] * b[12] + a[8] * b[17], a[5] * b[3] + a[6] * b[8] + a[7] * b[13] + a[8] * b[18], a[5] * b[4] + a[6] * b[9] + a[7] * b[14] + a[8] * b[19] + a[9], // Blue Channel a[10] * b[0] + a[11] * b[5] + a[12] * b[10] + a[13] * b[15], a[10] * b[1] + a[11] * b[6] + a[12] * b[11] + a[13] * b[16], a[10] * b[2] + a[11] * b[7] + a[12] * b[12] + a[13] * b[17], a[10] * b[3] + a[11] * b[8] + a[12] * b[13] + a[13] * b[18], a[10] * b[4] + a[11] * b[9] + a[12] * b[14] + a[13] * b[19] + a[14], // Alpha Channel a[15] * b[0] + a[16] * b[5] + a[17] * b[10] + a[18] * b[15], a[15] * b[1] + a[16] * b[6] + a[17] * b[11] + a[18] * b[16], a[15] * b[2] + a[16] * b[7] + a[17] * b[12] + a[18] * b[17], a[15] * b[3] + a[16] * b[8] + a[17] * b[13] + a[18] * b[18], a[15] * b[4] + a[16] * b[9] + a[17] * b[14] + a[18] * b[19] + a[19] ]); } toMatrix4() { const array = this._array; return new Matrix4([ array[0], array[1], array[2], array[3], array[5], array[6], array[7], array[8], array[10], array[11], array[12], array[13], array[15], array[16], array[17], array[18] ]); } toVector4() { const array = this._array; return new Vector4(array[4] / 255, array[9] / 255, array[14] / 255, array[19] / 255); } } class Matrix2 extends Matrix { constructor(array) { super(2, 2, array); } } class Matrix3 extends Matrix { constructor(array) { super(3, 3, array); } invert() { const [ n11, n21, n31, n12, n22, n32, n13, n23, n33 ] = this._array; const t11 = n33 * n22 - n32 * n23; const t12 = n32 * n13 - n33 * n12; const t13 = n23 * n12 - n22 * n13; const det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) { return this.set([0, 0, 0, 0, 0, 0, 0, 0, 0]); } const detInv = 1 / det; return this.set([ t11 * detInv, (n31 * n23 - n33 * n21) * detInv, (n32 * n21 - n31 * n22) * detInv, t12 * detInv, (n33 * n11 - n31 * n13) * detInv, (n31 * n12 - n32 * n11) * detInv, t13 * detInv, (n21 * n13 - n23 * n11) * detInv, (n22 * n11 - n21 * n12) * detInv ]); } } class Projection2D extends Matrix3 { constructor(_x = 0, _y = 0, _width = 0, _height = 0, _flipY = false) { super(); this._x = _x; this._y = _y; this._width = _width; this._height = _height; this._flipY = _flipY; this._performUpdateArray(); } flipY(flipY) { if (this._flipY !== flipY) { this._flipY = flipY; this._performUpdateArray(); } return this; } translate(x, y) { if (this._x !== x || this._y !== y) { this._x = x; this._y = y; this._performUpdateArray(); } return this; } resize(width, height) { if (this._width !== width || this._height !== height) { this._width = width; this._height = height; this._performUpdateArray(); } return this; } _performUpdateArray() { const width = this._width; const height = this._height; if (!width || !height) { return; } const x = this._x; const y = this._y; const sign = !this._flipY ? 1 : -1; const a = 1 / width * 2; const d