PageSourceSearch

https://parimalanath-bean-lab-qa.hf.space/assets/Index-DC3EL6G-.js

js parimalanath-bean-lab-qa.hf.space collected 2026-10-03 17:14:12 UTC 1,199,536 bytes, 38,194 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./browserAll-DiLbNwOz.js","./webworkerAll-acwYx1_h.js","./colorToUniform-BD1KgYAg.js","./i18n-dpAHICcw.js","./index-CDZuCcOm.js","./index-DYz7DaJH.css","./i18n-UT_CQGRO.css","./misc-C2MjMwBX.js","./utils.svelte-CyWLYi-B.js","./clone-dZfS06Ds.js","./ImagePreview-BoKQyUgY.js","./ScrollFade.svelte_svelte_type_style_lang-DUvCSfRk.js","./snippet-DVkMfmSq.js","./ScrollFade-DXC81vHi.css","./MarkdownCode.svelte_svelte_type_style_lang-GeNUGzep.js","./prism-python-C_fanlsZ.js","./MarkdownCode-Dptk5bYK.css","./BlockLabel-D4yjUUAn.js","./DownloadLink-CmpyjDGR.js","./Empty-617iGDfy.js","./ShareButton-c5E0ChlI.js","./Download-bn1Yc3QR.js","./Image-CdSr17SW.js","./IconButtonWrapper-KjCt2Pl8.js","./FullscreenButton-wNz2x9hr.js","./Maximize-CNFXHhlb.js","./Image-CJziNDBt.js","./Image-DQfoGqtb.css","./ImagePreview-CAbFrrhc.css","./Block-DntE23uJ.js","./index-DyDpuTN9.js","./StreamingBar.svelte_svelte_type_style_lang-BxBb9ZZb.js","./html-h_YSgefI.js","./StreamingBar-M0OJiclA.css","./Clear-tvJMRS4J.js","./Upload-BlCAnBIo.js","./actions-BTh6ZJJ8.js","./Upload-B9AbOy1B.css","./props-BwDqDG8n.js","./Webcam-D1D2o0z2.js","./DropdownArrow-BRSpwupS.js","./Square-Bg2evxzG.js","./Spinner-CNWYfN22.js","./StreamingBar-BnG6Vbta.js","./ImageUploader-79Z_jqCu.css","./window-DwfrWsjF.js","./tinycolor-DVeHTKjv.js","./Plus-CQgnmcGN.js","./input-UUW65DyE.js","./Check-4kogBHUX.js","./event-modifiers-DanhKw3_.js","./ArrowUp-BZnStkIc.js","./Trash-DD3UIc2i.js","./Undo-BTdg4xEQ.js","./Example-BDoLBXEz.css","./WebGPURenderer-C0Fiuo6s.js","./SharedSystems-CHymZ9tt.js","./WebGLRenderer-x_6nJWeO.js"])))=>i.map(i=>d[i]);
2import { w as writable, p as prop, e as init$1, i as if_block, a as set_attribute, b as set_class, f as set_style, G as getDefaultExportFromCjs, d as bind_this, k as each, u as index, t as remove_input_defaults, F as set_value, j as get$1, m as setup_stores, q as store_get, s as slot, r as rest_props, g as spread_props } from './i18n-dpAHICcw.js';
3import { a as append, f as from_svg, ak as delegate, R as push, a1 as legacy_pre_effect, x as set, I as deep_read_state, a2 as legacy_pre_effect_reset, V as child, X as sibling, Y as reset, t as template_effect, w as get, T as pop, a3 as mutable_source, U as flushSync, a0 as set_text, W as from_html, ag as __vitePreload, ab as onMount, Z as event, ax as $window, S as first_child, z as untrack, N as tick, a4 as createEventDispatcher, ae as derived_safe_equal, a5 as user_derived, au as to_array, a6 as comment, av as prepare_files, u as state, aw as FileData } from './index-CDZuCcOm.js';
4import { b as bubble_event } from './misc-C2MjMwBX.js';
5import { G as Gradio } from './utils.svelte-CyWLYi-B.js';
6import { I as ImagePreview } from './ImagePreview-BoKQyUgY.js';
7import { B as Block } from './Block-DntE23uJ.js';
8import './MarkdownCode.svelte_svelte_type_style_lang-GeNUGzep.js';
9import { U as Upload, a as ImagePaste, W as Webcam, I as IconButton$1 } from './ScrollFade.svelte_svelte_type_style_lang-DUvCSfRk.js';
10import { s as spring, S as Static } from './index-DyDpuTN9.js';
11import './StreamingBar.svelte_svelte_type_style_lang-BxBb9ZZb.js';
12/* empty css                                                     */
13import { c as create_drag } from './Upload-BlCAnBIo.js';
14/* empty css                                             */
15/* empty css                                               */
16import { b as bind_prop } from './props-BwDqDG8n.js';
17import { a as action } from './actions-BTh6ZJJ8.js';
18import { C as Circle$1, W as Webcam$1 } from './Webcam-D1D2o0z2.js';
19import { I as Image$1 } from './Image-CdSr17SW.js';
20import { b as bind_window_size } from './window-DwfrWsjF.js';
21import { t as tinycolor, E as Eyedropper } from './tinycolor-DVeHTKjv.js';
22import { P as Plus } from './Plus-CQgnmcGN.js';
23import { b as bind_value } from './input-UUW65DyE.js';
24import { C as Check } from './Check-4kogBHUX.js';
25/* empty css                                                    */
26import { s as stopPropagation } from './event-modifiers-DanhKw3_.js';
27import { A as ArrowUp } from './ArrowUp-BZnStkIc.js';
28import { C as Clear } from './Clear-tvJMRS4J.js';
29import { D as Download } from './Download-bn1Yc3QR.js';
30import { T as Trash }
30 from './Trash-DD3UIc2i.js';
31import { U as Undo } from './Undo-BTdg4xEQ.js';
32import { I as IconButtonWrapper } from './IconButtonWrapper-KjCt2Pl8.js';
33import { B as BlockLabel } from './BlockLabel-D4yjUUAn.js';
34
35var root$l = from_svg(`<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><polyline points="19 12 12 19 5 12"></polyline></svg>`);
36
37function ArrowDown($$anchor) {
38	var svg = root$l();
39
40	append($$anchor, svg);
41}
42
43var root$k = from_svg(`<svg width="100%" height="100%" viewBox="0 0 32 32"><path d="M28.828 3.172a4.094 4.094 0 0 0-5.656 0L4.05 22.292A6.954 6.954 0 0 0 2 27.242V30h2.756a6.952 6.952 0 0 0 4.95-2.05L28.828 8.829a3.999 3.999 0 0 0 0-5.657zM10.91 18.26l2.829 2.829l-2.122 2.121l-2.828-2.828zm-2.619 8.276A4.966 4.966 0 0 1 4.756 28H4v-.759a4.967 4.967 0 0 1 1.464-3.535l1.91-1.91l2.829 2.828zM27.415 7.414l-12.261 12.26l-2.829-2.828l12.262-12.26a2.047 2.047 0 0 1 2.828 0a2 2 0 0 1 0 2.828z" fill="currentColor"></path><path d="M6.5 15a3.5 3.5 0 0 1-2.475-5.974l3.5-3.5a1.502 1.502 0 0 0 0-2.121a1.537 1.537 0 0 0-2.121 0L3.415 5.394L2 3.98l1.99-1.988a3.585 3.585 0 0 1 4.95 0a3.504 3.504 0 0 1 0 4.949L5.439 10.44a1.502 1.502 0 0 0 0 2.121a1.537 1.537 0 0 0 2.122 0l4.024-4.024L13 9.95l-4.025 4.024A3.475 3.475 0 0 1 6.5 15z" fill="currentColor"></path></svg>`);
44
45function Brush($$anchor) {
46	var svg = root$k();
47
48	append($$anchor, svg);
49}
50
51var root$j = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M2.753 2.933a.75.75 0 0 1 .814-.68l3.043.272c2.157.205 4.224.452 5.922.732c1.66.273 3.073.594 3.844.983c.197.1.412.233.578.415c.176.192.352.506.28.9c-.067.356-.304.59-.487.729a3.001 3.001 0 0 1-.695.369c-1.02.404-2.952.79-5.984 1.169c-1.442.18-2.489.357-3.214.522c.205.045.43.089.674.132c.992.174 2.241.323 3.568.437a31.21 31.21 0 0 1 3.016.398c.46.087.893.186 1.261.296c.352.105.707.236.971.412c.13.086.304.225.42.437a.988.988 0 0 1 .063.141A1.75 1.75 0 0 0 14.5 12.25v.158c-.758.154-1.743.302-2.986.444c-2.124.243-3.409.55-4.117.859c-.296.128-.442.236-.508.3c.026.037.073.094.156.17c.15.138.369.29.65.45c.56.316 1.282.61 1.979.838l2.637.814a.75.75 0 1 1-.443 1.433l-2.655-.819c-.754-.247-1.58-.578-2.257-.96a5.082 5.082 0 0 1-.924-.65c-.255-.233-.513-.544-.62-.935c-.12-.441-.016-.88.274-1.244c.261-.328.656-.574 1.113-.773c.92-.4 2.387-.727 4.545-.974c1.366-.156 2.354-.313 3.041-.462a16.007 16.007 0 0 0-.552-.114a29.716 29.716 0 0 0-2.865-.378c-1.352-.116-2.649-.27-3.7-.454c-.524-.092-1-.194-1.395-.307c-.376-.106-.75-.241-1.021-.426a1.186 1.186 0 0 1-.43-.49a.934.934 0 0 1 .059-.873c.13-.213.32-.352.472-.442a3.23 3.23 0 0 1 .559-.251c.807-.287 2.222-.562 4.37-.83c2.695-.338 4.377-.666 5.295-.962c-.638-.21-1.623-.427-2.89-.635c-1.65-.273-3.679-.515-5.816-.718l-3.038-.272a.75.75 0 0 1-.68-.814M17 12.25a.75.75 0 0 0-1.5 0v4.19l-.72-.72a.75.75 0 1 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l2-2a.75.75 0 1 0-1.06-1.06l-.72.72z"></path></svg>`);
52
53function BrushSize($$anchor) {
54	var svg = root$j();
55
56	append($$anchor, svg);
57}
58
59var root$i = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 22q-2.05 0-3.875-.788t-3.187-2.15t-2.15-3.187T2 12q0-2.075.813-3.9t2.2-3.175T8.25 2.788T12.2 2q2 0 3.775.688t3.113 1.9t2.125 2.875T22 11.05q0 2.875-1.75 4.413T16 17h-1.85q-.225 0-.312.125t-.088.275q0 .3.375.863t.375 1.287q0 1.25-.687 1.85T12 22m-5.5-9q.65 0 1.075-.425T8 11.5t-.425-1.075T6.5 10t-1.075.425T5 11.5t.425 1.075T6.5 13m3-4q.65 0 1.075-.425T11 7.5t-.425-1.075T9.5 6t-1.075.425T8 7.5t.425 1.075T9.5 9m5 0q.65 0 1.075-.425T16 7.5t-.425-1.075T14.5 6t-1.075.425T13 7.5t.425 1.075T14.5 9m3 4q.65 0 1.075-.425T19 11.5t-.425-1.075T17.5 10t-1.075.425T16 11.5t.425 1.075T17.5 13"></path></svg>`);
60
61function ColorPickerSolid($$anchor) {
62	var svg = root$i();
63
64	append($$anchor, svg);
65}
66
67var root$h = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewB
67ox="0 0 256 256"><path fill="currentColor" d="M240 192a8 8 0 0 1-8 8h-32v32a8 8 0 0 1-16 0v-32H64a8 8 0 0 1-8-8V72H24a8 8 0 0 1 0-16h32V24a8 8 0 0 1 16 0v160h160a8 8 0 0 1 8 8M96 72h88v88a8 8 0 0 0 16 0V64a8 8 0 0 0-8-8H96a8 8 0 0 0 0 16"></path></svg>`);
68
69function Crop($$anchor) {
70	var svg = root$h();
71
72	append($$anchor, svg);
73}
74
75var root$g = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><g fill="none"><path fill="currentColor" d="m5.505 11.41l.53.53l-.53-.53ZM3 14.952h-.75H3ZM9.048 21v.75V21ZM11.41 5.505l-.53-.53l.53.53Zm1.831 12.34a.75.75 0 0 0 1.06-1.061l-1.06 1.06ZM7.216 9.697a.75.75 0 1 0-1.06 1.061l1.06-1.06Zm10.749 2.362l-5.905 5.905l1.06 1.06l5.905-5.904l-1.06-1.06Zm-11.93-.12l5.905-5.905l-1.06-1.06l-5.905 5.904l1.06 1.06Zm0 6.025c-.85-.85-1.433-1.436-1.812-1.933c-.367-.481-.473-.79-.473-1.08h-1.5c0 .749.312 1.375.78 1.99c.455.596 1.125 1.263 1.945 2.083l1.06-1.06Zm-1.06-7.086c-.82.82-1.49 1.488-1.945 2.084c-.468.614-.78 1.24-.78 1.99h1.5c0-.29.106-.6.473-1.08c.38-.498.962-1.083 1.812-1.933l-1.06-1.06Zm7.085 7.086c-.85.85-1.435 1.433-1.933 1.813c-.48.366-.79.472-1.08.472v1.5c.75 0 1.376-.312 1.99-.78c.596-.455 1.264-1.125 2.084-1.945l-1.06-1.06Zm-7.085 1.06c.82.82 1.487 1.49 2.084 1.945c.614.468 1.24.78 1.989.78v-1.5c-.29 0-.599-.106-1.08-.473c-.497-.38-1.083-.962-1.933-1.812l-1.06 1.06Zm12.99-12.99c.85.85 1.433 1.436 1.813 1.933c.366.481.472.79.472 1.08h1.5c0-.749-.312-1.375-.78-1.99c-.455-.596-1.125-1.263-1.945-2.083l-1.06 1.06Zm1.06 7.086c.82-.82 1.49-1.488 1.945-2.084c.468-.614.78-1.24.78-1.99h-1.5c0 .29-.106.6-.473 1.08c-.38.498-.962 1.083-1.812 1.933l1.06 1.06Zm0-8.146c-.82-.82-1.487-1.49-2.084-1.945c-.614-.468-1.24-.78-1.989-.78v1.5c.29 0 .599.106 1.08.473c.497.38 1.083.962 1.933 1.812l1.06-1.06Zm-7.085 1.06c.85-.85 1.435-1.433 1.933-1.812c.48-.367.79-.473 1.08-.473v-1.5c-.75 0-1.376.312-1.99.78c-.596.455-1.264 1.125-2.084 1.945l1.06 1.06Zm2.362 10.749L7.216 9.698l-1.06 1.061l7.085 7.085l1.06-1.06Z"></path><path stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="M9 21h12"></path></g></svg>`);
76
77function Erase($$anchor) {
78	var svg = root$g();
79
80	append($$anchor, svg);
81}
82
83var root$f = from_svg(`<svg width="100%" height="100%" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.35327 10.9495L6.77663 15.158C7.12221 15.4229 7.50051 15.5553 7.91154 15.5553C8.32258 15.5553 8.70126 15.4229 9.0476 15.158L14.471 10.9495" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path><path d="M7.23461 11.4324C7.23406 11.432 7.2335 11.4316 7.23295 11.4312L1.81496 7.2268C1.81471 7.22661 1.81446 7.22641 1.8142 7.22621C1.52269 6.99826 1.39429 6.73321 1.39429 6.37014C1.39429 6.00782 1.52236 5.74301 1.81325 5.51507C1.8136 5.5148 1.81394 5.51453 1.81428 5.51426L7.2331 1.30812C7.45645 1.13785 7.67632 1.06653 7.91159 1.06653C8.14692 1.06653 8.36622 1.13787 8.58861 1.30787C8.58915 1.30828 8.58969 1.30869 8.59023 1.30911L14.0082 5.51462C14.0085 5.51485 14.0088 5.51507 14.0091 5.51529C14.3008 5.74345 14.4289 6.00823 14.4289 6.37014C14.4289 6.73356 14.3006 6.99862 14.01 7.22634C14.0096 7.22662 14.0093 7.22689 14.0089 7.22717L8.59007 11.4322C8.36672 11.6024 8.14686 11.6738 7.91159 11.6738C7.67628 11.6738 7.45699 11.6024 7.23461 11.4324Z" stroke="currentColor" stroke-width="1.5"></path></svg>`);
84
85function Layers($$anchor) {
86	var svg = root$f();
87
88	append($$anchor, svg);
89}
90
91var root$e = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M10.05 23q-.75 0-1.4-.337T7.575 21.7l-5.9-8.65q-.2-.3-.175-.65t.3-.6q.475-.475 1.125-.55t1.175.3L7 13.575V4q0-.425.288-.712T8 3t.713.288T9 4v11.5q0 .6-.537.888t-1.038-.063l-2.125-1.5l3.925 5.725q.125.2.35.325t.475.125H17q.825 0 1.413-.587T19 19V5q0-.425.288-.712T20 4t.713.288T21 5v14q0 1.65-1.175 2.825T17 23zM12 1q.425 0 .713.288T13 2v9q0 .425-.288.713T12 12t-.712-.288T11 11V2q0-.425.288-.712T12 1m4 1q.425 0 .713.288T17 3v8q0 .425-.288.713T16 12t-.712-.288T15 11V3q0-.425.288-.712T16 2m-3.85 14.5"></path></svg>`);
92
93function Pan($$anchor) {
94	var svg = root$e();
95
96	append($$anchor, svg);
97}
98
99var root$d = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewB
99ox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-rotate-ccw" style="transform: rotateY(180deg);"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`);
100
101function Redo($$anchor) {
102	var svg = root$d();
103
104	append($$anchor, svg);
105}
106
107var root$c = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 16q1.875 0 3.188-1.312T16.5 11.5t-1.312-3.187T12 7T8.813 8.313T7.5 11.5t1.313 3.188T12 16m0-1.8q-1.125 0-1.912-.788T9.3 11.5t.788-1.912T12 8.8t1.913.788t.787 1.912t-.787 1.913T12 14.2m0 4.8q-3.65 0-6.65-2.037T1 11.5q1.35-3.425 4.35-5.462T12 4t6.65 2.038T23 11.5q-1.35 3.425-4.35 5.463T12 19"></path></svg>`);
108
109function Visibility($$anchor) {
110	var svg = root$c();
111
112	append($$anchor, svg);
113}
114
115var root$b = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="currentColor" d="m19.8 22.6l-4.2-4.15q-.875.275-1.762.413T12 19q-3.775 0-6.725-2.087T1 11.5q.525-1.325 1.325-2.463T4.15 7L1.4 4.2l1.4-1.4l18.4 18.4zM12 16q.275 0 .513-.025t.512-.1l-5.4-5.4q-.075.275-.1.513T7.5 11.5q0 1.875 1.313 3.188T12 16m7.3.45l-3.175-3.15q.175-.425.275-.862t.1-.938q0-1.875-1.312-3.187T12 7q-.5 0-.937.1t-.863.3L7.65 4.85q1.025-.425 2.1-.637T12 4q3.775 0 6.725 2.088T23 11.5q-.575 1.475-1.513 2.738T19.3 16.45m-4.625-4.6l-3-3q.7-.125 1.288.113t1.012.687t.613 1.038t.087 1.162"></path></svg>`);
116
117function VisibilityOff($$anchor) {
118	var svg = root$b();
119
120	append($$anchor, svg);
121}
122
123var root$a = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256"><path fill="currentColor" d="M144 120v88a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8v-88a8 8 0 0 1 8-8h88a8 8 0 0 1 8 8m64 56a8 8 0 0 0-8 8v16h-24a8 8 0 0 0 0 16h24a16 16 0 0 0 16-16v-16a8 8 0 0 0-8-8m0-72a8 8 0 0 0-8 8v32a8 8 0 0 0 16 0v-32a8 8 0 0 0-8-8m-8-64h-16a8 8 0 0 0 0 16h16v16a8 8 0 0 0 16 0V56a16 16 0 0 0-16-16m-56 0h-32a8 8 0 0 0 0 16h32a8 8 0 0 0 0-16M48 88a8 8 0 0 0 8-8V56h16a8 8 0 0 0 0-16H56a16 16 0 0 0-16 16v24a8 8 0 0 0 8 8"></path></svg>`);
124
125function Resize($$anchor) {
126	var svg = root$a();
127
128	append($$anchor, svg);
129}
130
131var root$9 = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21l-4.343-4.343m0 0A8 8 0 1 0 5.343 5.343a8 8 0 0 0 11.314 11.314M11 8v6m-3-3h6"></path></svg>`);
132
133function ZoomIn($$anchor) {
134	var svg = root$9();
135
136	append($$anchor, svg);
137}
138
139var root$8 = from_svg(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21l-4.343-4.343m0 0A8 8 0 1 0 5.343 5.343a8 8 0 0 0 11.314 11.314M8 11h6"></path></svg>`);
140
141function ZoomOut($$anchor) {
142	var svg = root$8();
143
144	append($$anchor, svg);
145}
146
147class CommandNode {
148  command;
149  next;
150  previous;
151  constructor(command) {
152    this.command = command || null;
153    this.next = null;
154    this.previous = null;
155  }
156  push(command) {
157    this.next = null;
158    const node = new CommandNode(command);
159    node.previous = this;
160    this.next = node;
161  }
162}
163class CommandManager {
164  history = new CommandNode();
165  current_history = writable(this.history);
166  undo() {
167    if (this.history.previous) {
168      this.history.command?.undo();
169      this.history = this.history.previous;
170      this.current_history.update(() => this.history);
171    }
172  }
173  redo(context) {
174    if (this.history.next) {
175      this.history = this.history.next;
176      this.history.command?.execute(context);
177      this.current_history.update(() => this.history);
178    }
179  }
180  async execute(command, context) {
181    await command.execute(context);
182    this.history.push(command);
183    this.history = this.history.next;
184    this.current_history.update(() => this.history);
185  }
186  async wait_for_next_frame() {
187    return new Promise((resolve) => {
188      requestAnimationFrame(() => {
189        resolve();
190      });
191    });
192  }
193  async replay(full_history, context) {
194    while (full_history.previous) {
195      full_history = full_history.previous;
196    }
197    while (full_history.next) {
198      await full_history.next.command.execute(context);
199      full_history = full_history.next;
200    }
201    this.history = full_history;
202    this.current_history.update(() => this.history);
203  }
204  contains(command_name) {
205    let current = this.history;
206    while (current) {
207      if (current.command?.name === command_name) return true;
208      current = current.next;
209    }
210    return false;
211  }
212  reset() {
213    this.history = new CommandNode();
214    this.current_history.update(() => this.history);
215  }
216}
217
218var root_1$5 = from_html(`<span style="margin-left: 4px;" class="svelte-1lrnmgy"> </span>`);
219var root_2$6 = from_html(`<span style="margin-right: 4px;" class="svelte-1lrnmgy"> </span>`);
220var root$7 = from_html(`<button><!> <div><!></div> <!></button>`);
221
222function IconButton($$anchor, $$props) {
223	push($$props, false);
224
225	const _color = mutable_source();
226	let Icon = prop($$props, 'Icon', 12);
227	let label = prop($$props, 'label', 12, "");
228	let show_label = prop($$props, 'show_label', 12, false);
229	let pending = prop($$props, 'pending', 12, false);
230	let size = prop($$props, 'size', 12, "small");
231	let padded = prop($$props, 'padded', 12, true);
232	let highlight = prop($$props, 'highlight', 12, false);
233	let disabled = prop($$props, 'disabled', 12, false);
234	let hasPopup = prop($$props, 'hasPopup', 12, false);
235	let color = prop($$props, 'color', 12, "var(--block-label-text-color)");
236	let transparent = prop($$props, 'transparent', 12, false);
237	let background = prop($$props, 'background', 12, "var(--background-fill-primary)");
238	let offset = prop($$props, 'offset', 12, 0);
239	let label_position = prop($$props, 'label_position', 12, "left");
240	let roundedness = prop($$props, 'roundedness', 12, "quite");
241	let onclick = prop($$props, 'onclick', 12, undefined);
242
243	legacy_pre_effect(() => (deep_read_state(highlight()), deep_read_state(color())), () => {
244		set(_color, highlight() ? "var(--color-accent)" : color().toString());
245	});
246
247	legacy_pre_effect_reset();
248
249	var $$exports = {
250		get Icon() {
251			return Icon();
252		},
253
254		set Icon($$value) {
255			Icon($$value);
256			flushSync();
257		},
258
259		get label() {
260			return label();
261		},
262
263		set label($$value) {
264			label($$value);
265			flushSync();
266		},
267
268		get show_label() {
269			return show_label();
270		},
271
272		set show_label($$value) {
273			show_label($$value);
274			flushSync();
275		},
276
277		get pending() {
278			return pending();
279		},
280
281		set pending($$value) {
282			pending($$value);
283			flushSync();
284		},
285
286		get size() {
287			return size();
288		},
289
290		set size($$value) {
291			size($$value);
292			flushSync();
293		},
294
295		get padded() {
296			return padded();
297		},
298
299		set padded($$value) {
300			padded($$value);
301			flushSync();
302		},
303
304		get highlight() {
305			return highlight();
306		},
307
308		set highlight($$value) {
309			highlight($$value);
310			flushSync();
311		},
312
313		get disabled() {
314			return disabled();
315		},
316
317		set disabled($$value) {
318			disabled($$value);
319			flushSync();
320		},
321
322		get hasPopup() {
323			return hasPopup();
324		},
325
326		set hasPopup($$value) {
327			hasPopup($$value);
328			flushSync();
329		},
330
331		get color() {
332			return color();
333		},
334
335		set color($$value) {
336			color($$value);
337			flushSync();
338		},
339
340		get transparent() {
341			return transparent();
342		},
343
344		set transparent($$value) {
345			transparent($$value);
346			flushSync();
347		},
348
349		get background() {
350			return background();
351		},
352
353		set background($$value) {
354			background($$value);
355			flushSync();
356		},
357
358		get offset() {
359			return offset();
360		},
361
362		set offset($$value) {
363			offset($$value);
364			flushSync();
365		},
366
367		get label_position() {
368			return label_position();
369		},
370
371		set label_position($$value) {
372			label_position($$value);
373			flushSync();
374		},
375
376		get roundedness() {
377			return roundedness();
378		},
379
380		set roundedness($$value) {
381			roundedness($$value);
382			flushSync();
383		},
384
385		get onclick() {
386			return onclick();
387		},
388
389		set onclick($$value) {
390			onclick($$value);
391			flushSync();
392		}
393	};
394
395	init$1();
396
397	var button = root$7();
398
399	button.__click = function (...$$args) {
400		onclick()?.apply(this, $$args);
401	};
402
403	let classes;
404	let styles;
405	var node = child(button);
406
407	{
408		var consequent = ($$anchor) => {
409			var span = root_1$5();
410			var text = child(span, true);
411
412			reset(span);
413			template_effect(() => set_text(text, label()));
414			append($$anchor, span);
415		};
416
417		if_block(node, ($$render) => {
418			if (show_label() && label_position() === "left") $$render(consequent);
419		});
420	}
421
422	var div = sibling(node, 2);
423	let classes_1;
424	var node_1 = child(div);
425
426	Icon()(node_1, {});
427	reset(div);
428
429	var node_2 = sibling(div, 2);
430
431	{
432		var consequent_1 = ($$anchor) => {
433			var span_1 = root_2$6();
434			var text_1 = child(span_1, true);
435
436			reset(span_1);
437			template_effect(() => set_text(text_1, label()));
438			append($$anchor, span_1);
439		};
440
441		if_block(node_2, ($$render) => {
442			if (show_label() && label_position() === "right") $$render(consequent_1);
443		});
444	}
445
446	reset(button);
447
448	template_effect(() => {
449		button.disabled = disabled();
450		set_attribute(button, 'aria-label', label());
451		set_attribute(button, 'aria-haspopup', hasPopup());
452		set_attribute(button, 'title', label());
453
454		classes = set_class(button, 1, `${roundedness() ?? ''}-round`, 'svelte-1lrnmgy', classes, {
455			pending: pending(),
456			padded: padded(),
457			highlight: highlight(),
458			transparent: transparent()
459		});
460
461		styles = set_style(button, '', styles, {
462			color: !disabled() && get(_color) ? get(_color) : "var(--block-label-text-color)",
463			'--bg-color': !disabled() ? background() : "auto",
464			'margin-left': offset() + "px"
465		});
466
467		classes_1 = set_class(div, 1, 'svelte-1lrnmgy', null, classes_1, {
468			small: size() === "small",
469			large: size() === "large",
470			medium: size() === "medium"
471		});
472	});
473
474	append($$anchor, button);
475
476	return pop($$exports);
477}
478
479delegate(['click']);
480
481function click_outside(node, callback) {
482  const handle_click = (event) => {
483    if (node && !node.contains(event.target) && !event.defaultPrevented) {
484      callback(event);
485    }
486  };
487  document.addEventListener("mousedown", handle_click, true);
488  return {
489    destroy() {
490      document.removeEventListener("mousedown", handle_click, true);
491    }
492  };
493}
494
495var ExtensionType = /* @__PURE__ */ ((ExtensionType2) => {
496  ExtensionType2["Application"] = "application";
497  ExtensionType2["WebGLPipes"] = "webgl-pipes";
498  ExtensionType2["WebGLPipesAdaptor"] = "webgl-pipes-adaptor";
499  ExtensionType2["WebGLSystem"] = "webgl-system";
500  ExtensionType2["WebGPUPipes"] = "webgpu-pipes";
501  ExtensionType2["WebGPUPipesAdaptor"] = "webgpu-pipes-adaptor";
502  ExtensionType2["WebGPUSystem"] = "webgpu-system";
503  ExtensionType2["CanvasSystem"] = "canvas-system";
504  ExtensionType2["CanvasPipesAdaptor"] = "canvas-pipes-adaptor";
505  ExtensionType2["CanvasPipes"] = "canvas-pipes";
506  ExtensionType2["Asset"] = "asset";
507  ExtensionType2["LoadParser"] = "load-parser";
508  ExtensionType2["ResolveParser"] = "resolve-parser";
509  ExtensionType2["CacheParser"] = "cache-parser";
510  ExtensionType2["DetectionParser"] = "detection-parser";
511  ExtensionType2["MaskEffect"] = "mask-effect";
512  ExtensionType2["BlendMode"] = "blend-mode";
513  ExtensionType2["TextureSource"] = "texture-source";
514  ExtensionType2["Environment"] = "environment";
515  ExtensionType2["ShapeBuilder"] = "shape-builder";
516  ExtensionType2["Batcher"] = "batcher";
517  return ExtensionType2;
518})(ExtensionType || {});
519const normalizeExtension = (ext) => {
520  if (typeof ext === "function" || typeof ext === "object" && ext.extension) {
521    if (!ext.extension) {
522      throw new Error("Extension class must have an extension object");
523    }
524    const metadata = typeof ext.extension !== "object" ? { type: ext.extension } : ext.extension;
525    ext = { ...metadata, ref: ext };
526  }
527  if (typeof ext === "object") {
528    ext = { ...ext };
529  } else {
530    throw new Error("Invalid extension type");
531  }
532  if (typeof ext.type === "string") {
533    ext.type = [ext.type];
534  }
535  return ext;
536};
537const normalizeExtensionPriority = (ext, defaultPriority) => normalizeExtension(ext).priority ?? defaultPriority;
538const extensions = {
539  /** @ignore */
540  _addHandlers: {},
541  /** @ignore */
542  _removeHandlers: {},
543  /** @ignore */
544  _queue: {},
545  /**
546   * Remove extensions from PixiJS.
547   * @param extensions - Extensions to be removed. Can be:
548   * - Extension class with static `extension` property
549   * - Extension format object with `type` and `ref`
550   * - Multiple extensions as separate arguments
551   * @returns {extensions} this for chaining
552   * @example
553   * ```ts
554   * // Remove a single extension
555   * extensions.remove(MyRendererPlugin);
556   *
557   * // Remove multiple extensions
558   * extensions.remove(
559   *     MyRendererPlugin,
560   *     MySystemPlugin
561   * );
562   * ```
563   * @see {@link ExtensionType} For available extension types
564   * @see {@link ExtensionFormat} For extension format details
565   */
566  remove(...extensions2) {
567    extensions2.map(normalizeExtension).forEach((ext) => {
568      ext.type.forEach((type) => this._removeHandlers[type]?.(ext));
569    });
570    return this;
571  },
572  /**
573   * Register new extensions with PixiJS. Extensions can be registered in multiple formats:
574   * - As a class with a static `extension` property
575   * - As an extension format object
576   * - As multiple extensions passed as separate arguments
577   * @param extensions - Extensions to add to PixiJS. Each can be:
578   * - A class with static `extension` property
579   * - An extension format object with `type` and `ref`
580   * - Multiple extensions as separate arguments
581   * @returns This extensions instance for chaining
582   * @example
583   * ```ts
584   * // Register a simple extension
585   * extensions.add(MyRendererPlugin);
586   *
587   * // Register multiple extensions
588   * extensions.add(
589   *     MyRendererPlugin,
590   *     MySystemPlugin,
591   * });
592   * ```
593   * @see {@link ExtensionType} For available extension types
594   * @see {@link ExtensionFormat} For extension format details
595   * @see {@link extensions.remove} For removing registered extensions
596   */
597  add(...extensions2) {
598    extensions2.map(normalizeExtension).forEach((ext) => {
599      ext.type.forEach((type) => {
600        const handlers = this._addHandlers;
601        const queue = this._queue;
602        if (!handlers[type]) {
603          queue[type] = queue[type] || [];
604          queue[type]?.push(ext);
605        } else {
606          handlers[type]?.(ext);
607        }
608      });
609    });
610    return this;
611  },
612  /**
613   * Internal method to handle extensions by name.
614   * @param type - The extension type.
615   * @param onAdd  - Function handler when extensions are added/registered {@link StrictExtensionFormat}.
616   * @param onRemove  - Function handler when extensions are removed/unregistered {@link StrictExtensionFormat}.
617   * @returns this for chaining.
618   * @internal
619   * @ignore
620   */
621  handle(type, onAdd, onRemove) {
622    const addHandlers = this._addHandlers;
623    const removeHandlers = this._removeHandlers;
624    if (addHandlers[type] || removeHandlers[type]) {
625      throw new Error(`Extension type ${type} already has a handler`);
626    }
627    addHandlers[type] = onAdd;
628    removeHandlers[type] = onRemove;
629    const queue = this._queue;
630    if (queue[type]) {
631      queue[type]?.forEach((ext) => onAdd(ext));
632      delete queue[type];
633    }
634    return this;
635  },
636  /**
637   * Handle a type, but using a map by `name` property.
638   * @param type - Type of extension to handle.
639   * @param map - The object map of named extensions.
640   * @returns this for chaining.
641   * @ignore
642   */
643  handleByMap(type, map) {
644    return this.handle(
645      type,
646      (extension) => {
647        if (extension.name) {
648          map[extension.name] = extension.ref;
649        }
650      },
651      (extension) => {
652        if (extension.name) {
653          delete map[extension.name];
654        }
655      }
656    );
657  },
658  /**
659   * Handle a type, but using a list of extensions with a `name` property.
660   * @param type - Type of extension to handle.
661   * @param map - The array of named extensions.
662   * @param defaultPriority - Fallback priority if none is defined.
663   * @returns this for chaining.
664   * @ignore
665   */
666  handleByNamedList(type, map, defaultPriority = -1) {
667    return this.handle(
668      type,
669      (extension) => {
670        const index = map.findIndex((item) => item.name === extension.name);
671        if (index >= 0)
672          return;
673        map.push({ name: extension.name, value: extension.ref });
674        map.sort((a, b) => normalizeExtensionPriority(b.value, defaultPriority) - normalizeExtensionPriority(a.value, defaultPriority));
675      },
676      (extension) => {
677        const index = map.findIndex((item) => item.name === extension.name);
678        if (index !== -1) {
679          map.splice(index, 1);
680        }
681      }
682    );
683  },
684  /**
685   * Handle a type, but using a list of extensions.
686   * @param type - Type of extension to handle.
687   * @param list - The list of extensions.
688   * @param defaultPriority - The default priority to use if none is specified.
689   * @returns this for chaining.
690   * @ignore
691   */
692  handleByList(type, list, defaultPriority = -1) {
693    return this.handle(
694      type,
695      (extension) => {
696        if (list.includes(extension.ref)) {
697          return;
698        }
699        list.push(extension.ref);
700        list.sort((a, b) => normalizeExtensionPriority(b, defaultPriority) - normalizeExtensionPriority(a, defaultPriority));
701      },
702      (extension) => {
703        const index = list.indexOf(extension.ref);
704        if (index !== -1) {
705          list.splice(index, 1);
706        }
707      }
708    );
709  },
710  /**
711   * Mixin the source object(s) properties into the target class's prototype.
712   * Copies all property descriptors from source objects to the target's prototype.
713   * @param Target - The target class to mix properties into
714   * @param sources - One or more source objects containing properties to mix in
715   * @example
716   * ```ts
717   * // Create a mixin with shared properties
718   * const moveable = {
719   *     x: 0,
720   *     y: 0,
721   *     move(x: number, y: number) {
722   *         this.x += x;
723   *         this.y += y;
724   *     }
725   * };
726   *
727   * // Create a mixin with computed properties
728   * const scalable = {
729   *     scale: 1,
730   *     get scaled() {
731   *         return this.scale > 1;
732   *     }
733   * };
734   *
735   * // Apply mixins to a class
736   * extensions.mixin(Sprite, moveable, scalable);
737   *
738   * // Use mixed-in properties
739   * const sprite = new Sprite();
740   * sprite.move(10, 20);
741   * console.log(sprite.x, sprite.y); // 10, 20
742   * ```
743   * @remarks
744   * - Copies all properties including getters/setters
745   * - Does not modify source objects
746   * - Preserves property descriptors
747   * @see {@link Object.defineProperties} For details on property descriptors
748   * @see {@link Object.getOwnPropertyDescriptors} For details on property copying
749   */
750  mixin(Target, ...sources) {
751    for (const source of sources) {
752      Object.defineProperties(Target.prototype, Object.getOwnPropertyDescriptors(source));
753    }
754  }
755};
756
757const browserExt = {
758  extension: {
759    type: ExtensionType.Environment,
760    name: "browser",
761    priority: -1
762  },
763  test: () => true,
764  load: async () => {
765    await __vitePreload(() => import('./browserAll-DiLbNwOz.js'),true              ?__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54]):void 0,import.meta.url);
766  }
767};
768
769const webworkerExt = {
770  extension: {
771    type: ExtensionType.Environment,
772    name: "webworker",
773    priority: 0
774  },
775  test: () => typeof self !== "undefined" && self.WorkerGlobalScope !== void 0,
776  load: async () => {
777    await __vitePreload(() => import('./webworkerAll-acwYx1_h.js'),true              ?__vite__mapDeps([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54]):void 0,import.meta.url);
778  }
779};
780
781class ObservablePoint {
782  /**
783   * Creates a new `ObservablePoint`
784   * @param observer - Observer to pass to listen for change events.
785   * @param {number} [x=0] - position of the point on the x axis
786   * @param {number} [y=0] - position of the point on the y axis
787   */
788  constructor(observer, x, y) {
789    this._x = x || 0;
790    this._y = y || 0;
791    this._observer = observer;
792  }
793  /**
794   * Creates a clone of this point.
795   * @example
796   * ```ts
797   * // Basic cloning
798   * const point = new ObservablePoint(observer, 100, 200);
799   * const copy = point.clone();
800   *
801   * // Clone with new observer
802   * const newObserver = {
803   *     _onUpdate: (p) => console.log(`Clone updated: (${p.x}, ${p.y})`)
804   * };
805   * const watched = point.clone(newObserver);
806   *
807   * // Verify independence
808   * watched.set(300, 400); // Only triggers new observer
809   * ```
810   * @param observer - Optional observer to pass to the new observable point
811   * @returns A copy of this observable point
812   * @see {@link ObservablePoint.copyFrom} For copying into existing point
813   * @see {@link Observer} For observer interface details
814   */
815  clone(observer) {
816    return new ObservablePoint(observer ?? this._observer, this._x, this._y);
817  }
818  /**
819   * Sets the point to a new x and y position.
820   *
821   * If y is omitted, both x and y will be set to x.
822   * @example
823   * ```ts
824   * // Basic position setting
825   * const point = new ObservablePoint(observer);
826   * point.set(100, 200);
827   *
828   * // Set both x and y to same value
829   * point.set(50); // x=50, y=50
830   * ```
831   * @param x - Position on the x axis
832   * @param y - Position on the y axis, defaults to x
833   * @returns The point instance itself
834   * @see {@link ObservablePoint.copyFrom} For copying from another point
835   * @see {@link ObservablePoint.equals} For comparing positions
836   */
837  set(x = 0, y = x) {
838    if (this._x !== x || this._y !== y) {
839      this._x = x;
840      this._y = y;
841      this._observer._onUpdate(this);
842    }
843    return this;
844  }
845  /**
846   * Copies x and y from the given point into this point.
847   * @example
848   * ```ts
849   * // Basic copying
850   * const source = new ObservablePoint(observer, 100, 200);
851   * const target = new ObservablePoint();
852   * target.copyFrom(source);
853   *
854   * // Copy and chain operations
855   * const point = new ObservablePoint()
856   *     .copyFrom(source)
857   *     .set(x + 50, y + 50);
858   *
859   * // Copy from any PointData
860   * const data = { x: 10, y: 20 };
861   * point.copyFrom(data);
862   * ```
863   * @param p - The point to copy from
864   * @returns The point instance itself
865   * @see {@link ObservablePoint.copyTo} For copying to another point
866   * @see {@link ObservablePoint.clone} For creating new point copy
867   */
868  copyFrom(p) {
869    if (this._x !== p.x || this._y !== p.y) {
870      this._x = p.x;
871      this._y = p.y;
872      this._observer._onUpdate(this);
873    }
874    return this;
875  }
876  /**
877   * Copies this point's x and y into the given point.
878   * @example
879   * ```ts
880   * // Basic copying
881   * const source = new ObservablePoint(100, 200);
882   * const target = new ObservablePoint();
883   * source.copyTo(target);
884   * ```
885   * @param p - The point to copy to. Can be any type that is or extends `PointLike`
886   * @returns The point (`p`) with values updated
887   * @see {@link ObservablePoint.copyFrom} For copying from another point
888   * @see {@link ObservablePoint.clone} For creating new point copy
889   */
890  copyTo(p) {
891    p.set(this._x, this._y);
892    return p;
893  }
894  /**
895   * Checks if another point is equal to this point.
896   *
897   * Compares x and y values using strict equality.
898   * @example
899   * ```ts
900   * // Basic equality check
901   * const p1 = new ObservablePoint(100, 200);
902   * const p2 = new ObservablePoint(100, 200);
903   * console.log(p1.equals(p2)); // true
904   *
905   * // Compare with PointData
906   * const data = { x: 100, y: 200 };
907   * console.log(p1.equals(data)); // true
908   *
909   * // Check different points
910   * const p3 = new ObservablePoint(200, 300);
911   * console.log(p1.equals(p3)); // false
912   * ```
913   * @param p - The point to check
914   * @returns `true` if both `x` and `y` are equal
915   * @see {@link ObservablePoint.copyFrom} For making points equal
916   * @see {@link PointData} For point data interface
917   */
918  equals(p) {
919    return p.x === this._x && p.y === this._y;
920  }
921  toString() {
922    return `[pixi.js/math:ObservablePoint x=${this._x} y=${this._y} scope=${this._observer}]`;
923  }
924  /**
925   * Position of the observable point on the x axis.
926   * Triggers observer callback when value changes.
927   * @example
928   * ```ts
929   * // Basic x position
930   * const point = new ObservablePoint(observer);
931   * point.x = 100; // Triggers observer
932   *
933   * // Use in calculations
934   * const width = rightPoint.x - leftPoint.x;
935   * ```
936   * @default 0
937   */
938  get x() {
939    return this._x;
940  }
941  set x(value) {
942    if (this._x !== value) {
943      this._x = value;
944      this._observer._onUpdate(this);
945    }
946  }
947  /**
948   * Position of the observable point on the y axis.
949   * Triggers observer callback when value changes.
950   * @example
951   * ```ts
952   * // Basic y position
953   * const point = new ObservablePoint(observer);
954   * point.y = 200; // Triggers observer
955   *
956   * // Use in calculations
957   * const height = bottomPoint.y - topPoint.y;
958   * ```
959   * @default 0
960   */
961  get y() {
962    return this._y;
963  }
964  set y(value) {
965    if (this._y !== value) {
966      this._y = value;
967      this._observer._onUpdate(this);
968    }
vendor: 9,997 bytes, lines 968-1319
968
969  }
970}
971
972var eventemitter3 = {exports: {}};
973
974var hasRequiredEventemitter3;
975
976function requireEventemitter3 () {
977	if (hasRequiredEventemitter3) return eventemitter3.exports;
978	hasRequiredEventemitter3 = 1;
979	(function (module) {
980
981		var has = Object.prototype.hasOwnProperty
982		  , prefix = '~';
983
984		/**
985		 * Constructor to create a storage for our `EE` objects.
986		 * An `Events` instance is a plain object whose properties are event names.
987		 *
988		 * @constructor
989		 * @private
990		 */
991		function Events() {}
992
993		//
994		// We try to not inherit from `Object.prototype`. In some engines creating an
995		// instance in this way is faster than calling `Object.create(null)` directly.
996		// If `Object.create(null)` is not supported we prefix the event names with a
997		// character to make sure that the built-in object properties are not
998		// overridden or used as an attack vector.
999		//
1000		if (Object.create) {
1001		  Events.prototype = Object.create(null);
1002
1003		  //
1004		  // This hack is needed because the `__proto__` property is still inherited in
1005		  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
1006		  //
1007		  if (!new Events().__proto__) prefix = false;
1008		}
1009
1010		/**
1011		 * Representation of a single event listener.
1012		 *
1013		 * @param {Function} fn The listener function.
1014		 * @param {*} context The context to invoke the listener with.
1015		 * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
1016		 * @constructor
1017		 * @private
1018		 */
1019		function EE(fn, context, once) {
1020		  this.fn = fn;
1021		  this.context = context;
1022		  this.once = once || false;
1023		}
1024
1025		/**
1026		 * Add a listener for a given event.
1027		 *
1028		 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
1029		 * @param {(String|Symbol)} event The event name.
1030		 * @param {Function} fn The listener function.
1031		 * @param {*} context The context to invoke the listener with.
1032		 * @param {Boolean} once Specify if the listener is a one-time listener.
1033		 * @returns {EventEmitter}
1034		 * @private
1035		 */
1036		function addListener(emitter, event, fn, context, once) {
1037		  if (typeof fn !== 'function') {
1038		    throw new TypeError('The listener must be a function');
1039		  }
1040
1041		  var listener = new EE(fn, context || emitter, once)
1042		    , evt = prefix ? prefix + event : event;
1043
1044		  if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
1045		  else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
1046		  else emitter._events[evt] = [emitter._events[evt], listener];
1047
1048		  return emitter;
1049		}
1050
1051		/**
1052		 * Clear event by name.
1053		 *
1054		 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
1055		 * @param {(String|Symbol)} evt The Event name.
1056		 * @private
1057		 */
1058		function clearEvent(emitter, evt) {
1059		  if (--emitter._eventsCount === 0) emitter._events = new Events();
1060		  else delete emitter._events[evt];
1061		}
1062
1063		/**
1064		 * Minimal `EventEmitter` interface that is molded against the Node.js
1065		 * `EventEmitter` interface.
1066		 *
1067		 * @constructor
1068		 * @public
1069		 */
1070		function EventEmitter() {
1071		  this._events = new Events();
1072		  this._eventsCount = 0;
1073		}
1074
1075		/**
1076		 * Return an array listing the events for which the emitter has registered
1077		 * listeners.
1078		 *
1079		 * @returns {Array}
1080		 * @public
1081		 */
1082		EventEmitter.prototype.eventNames = function eventNames() {
1083		  var names = []
1084		    , events
1085		    , name;
1086
1087		  if (this._eventsCount === 0) return names;
1088
1089		  for (name in (events = this._events)) {
1090		    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
1091		  }
1092
1093		  if (Object.getOwnPropertySymbols) {
1094		    return names.concat(Object.getOwnPropertySymbols(events));
1095		  }
1096
1097		  return names;
1098		};
1099
1100		/**
1101		 * Return the listeners registered for a given event.
1102		 *
1103		 * @param {(String|Symbol)} event The event name.
1104		 * @returns {Array} The registered listeners.
1105		 * @public
1106		 */
1107		EventEmitter.prototype.listeners = function listeners(event) {
1108		  var evt = prefix ? prefix + event : event
1109		    , handlers = this._events[evt];
1110
1111		  if (!handlers) return [];
1112		  if (handlers.fn) return [handlers.fn];
1113
1114		  for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
1115		    ee[i] = handlers[i].fn;
1116		  }
1117
1118		  return ee;
1119		};
1120
1121		/**
1122		 * Return the number of listeners listening to a given event.
1123		 *
1124		 * @param {(String|Symbol)} event The event name.
1125		 * @returns {Number} The number of listeners.
1126		 * @public
1127		 */
1128		EventEmitter.prototype.listenerCount = function listenerCount(event) {
1129		  var evt = prefix ? prefix + event : event
1130		    , listeners = this._events[evt];
1131
1132		  if (!listeners) return 0;
1133		  if (listeners.fn) return 1;
1134		  return listeners.length;
1135		};
1136
1137		/**
1138		 * Calls each of the listeners registered for a given event.
1139		 *
1140		 * @param {(String|Symbol)} event The event name.
1141		 * @returns {Boolean} `true` if the event had listeners, else `false`.
1142		 * @public
1143		 */
1144		EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
1145		  var evt = prefix ? prefix + event : event;
1146
1147		  if (!this._events[evt]) return false;
1148
1149		  var listeners = this._events[evt]
1150		    , len = arguments.length
1151		    , args
1152		    , i;
1153
1154		  if (listeners.fn) {
1155		    if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
1156
1157		    switch (len) {
1158		      case 1: return listeners.fn.call(listeners.context), true;
1159		      case 2: return listeners.fn.call(listeners.context, a1), true;
1160		      case 3: return listeners.fn.call(listeners.context, a1, a2), true;
1161		      case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
1162		      case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
1163		      case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
1164		    }
1165
1166		    for (i = 1, args = new Array(len -1); i < len; i++) {
1167		      args[i - 1] = arguments[i];
1168		    }
1169
1170		    listeners.fn.apply(listeners.context, args);
1171		  } else {
1172		    var length = listeners.length
1173		      , j;
1174
1175		    for (i = 0; i < length; i++) {
1176		      if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
1177
1178		      switch (len) {
1179		        case 1: listeners[i].fn.call(listeners[i].context); break;
1180		        case 2: listeners[i].fn.call(listeners[i].context, a1); break;
1181		        case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
1182		        case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
1183		        default:
1184		          if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
1185		            args[j - 1] = arguments[j];
1186		          }
1187
1188		          listeners[i].fn.apply(listeners[i].context, args);
1189		      }
1190		    }
1191		  }
1192
1193		  return true;
1194		};
1195
1196		/**
1197		 * Add a listener for a given event.
1198		 *
1199		 * @param {(String|Symbol)} event The event name.
1200		 * @param {Function} fn The listener function.
1201		 * @param {*} [context=this] The context to invoke the listener with.
1202		 * @returns {EventEmitter} `this`.
1203		 * @public
1204		 */
1205		EventEmitter.prototype.on = function on(event, fn, context) {
1206		  return addListener(this, event, fn, context, false);
1207		};
1208
1209		/**
1210		 * Add a one-time listener for a given event.
1211		 *
1212		 * @param {(String|Symbol)} event The event name.
1213		 * @param {Function} fn The listener function.
1214		 * @param {*} [context=this] The context to invoke the listener with.
1215		 * @returns {EventEmitter} `this`.
1216		 * @public
1217		 */
1218		EventEmitter.prototype.once = function once(event, fn, context) {
1219		  return addListener(this, event, fn, context, true);
1220		};
1221
1222		/**
1223		 * Remove the listeners of a given event.
1224		 *
1225		 * @param {(String|Symbol)} event The event name.
1226		 * @param {Function} fn Only remove the listeners that match this function.
1227		 * @param {*} context Only remove the listeners that have this context.
1228		 * @param {Boolean} once Only remove one-time listeners.
1229		 * @returns {EventEmitter} `this`.
1230		 * @public
1231		 */
1232		EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
1233		  var evt = prefix ? prefix + event : event;
1234
1235		  if (!this._events[evt]) return this;
1236		  if (!fn) {
1237		    clearEvent(this, evt);
1238		    return this;
1239		  }
1240
1241		  var listeners = this._events[evt];
1242
1243		  if (listeners.fn) {
1244		    if (
1245		      listeners.fn === fn &&
1246		      (!once || listeners.once) &&
1247		      (!context || listeners.context === context)
1248		    ) {
1249		      clearEvent(this, evt);
1250		    }
1251		  } else {
1252		    for (var i = 0, events = [], length = listeners.length; i < length; i++) {
1253		      if (
1254		        listeners[i].fn !== fn ||
1255		        (once && !listeners[i].once) ||
1256		        (context && listeners[i].context !== context)
1257		      ) {
1258		        events.push(listeners[i]);
1259		      }
1260		    }
1261
1262		    //
1263		    // Reset the array, or remove it completely if we have no more listeners.
1264		    //
1265		    if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
1266		    else clearEvent(this, evt);
1267		  }
1268
1269		  return this;
1270		};
1271
1272		/**
1273		 * Remove all listeners, or those of the specified event.
1274		 *
1275		 * @param {(String|Symbol)} [event] The event name.
1276		 * @returns {EventEmitter} `this`.
1277		 * @public
1278		 */
1279		EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
1280		  var evt;
1281
1282		  if (event) {
1283		    evt = prefix ? prefix + event : event;
1284		    if (this._events[evt]) clearEvent(this, evt);
1285		  } else {
1286		    this._events = new Events();
1287		    this._eventsCount = 0;
1288		  }
1289
1290		  return this;
1291		};
1292
1293		//
1294		// Alias methods names because people roll like that.
1295		//
1296		EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
1297		EventEmitter.prototype.addListener = EventEmitter.prototype.on;
1298
1299		//
1300		// Expose the prefix.
1301		//
1302		EventEmitter.prefixed = prefix;
1303
1304		//
1305		// Allow `EventEmitter` to be imported as module namespace.
1306		//
1307		EventEmitter.EventEmitter = EventEmitter;
1308
1309		//
1310		// Expose the module.
1311		//
1312		{
1313		  module.exports = EventEmitter;
1314		} 
1315	} (eventemitter3));
1316	return eventemitter3.exports;
1317}
1318
1319var eventemitter3Exports = requireEventemitter3();
1320const EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
1321
1322const PI_2 = Math.PI * 2;
1323const RAD_TO_DEG = 180 / Math.PI;
1324const DEG_TO_RAD = Math.PI / 180;
1325
1326class Point {
1327  /**
1328   * Creates a new `Point`
1329   * @param {number} [x=0] - position of the point on the x axis
1330   * @param {number} [y=0] - position of the point on the y axis
1331   */
1332  constructor(x = 0, y = 0) {
1333    /**
1334     * Position of the point on the x axis
1335     * @example
1336     * ```ts
1337     * // Set x position
1338     * const point = new Point();
1339     * point.x = 100;
1340     *
1341     * // Use in calculations
1342     * const width = rightPoint.x - leftPoint.x;
1343     * ```
1344     */
1345    this.x = 0;
1346    /**
1347     * Position of the point on the y axis
1348     * @example
1349     * ```ts
1350     * // Set y position
1351     * const point = new Point();
1352     * point.y = 200;
1353     *
1354     * // Use in calculations
1355     * const height = bottomPoint.y - topPoint.y;
1356     * ```
1357     */
1358    this.y = 0;
1359    this.x = x;
1360    this.y = y;
1361  }
1362  /**
1363   * Creates a clone of this point, which is a new instance with the same `x` and `y` values.
1364   * @example
1365   * ```ts
1366   * // Basic point cloning
1367   * const original = new Point(100, 200);
1368   * const copy = original.clone();
1369   *
1370   * // Clone and modify
1371   * const modified = original.clone();
1372   * modified.set(300, 400);
1373   *
1374   * // Verify independence
1375   * console.log(original); // Point(100, 200)
1376   * console.log(modified); // Point(300, 400)
1377   * ```
1378   * @remarks
1379   * - Creates new Point instance
1380   * - Deep copies x and y values
1381   * - Independent from original
1382   * - Useful for preserving values
1383   * @returns A clone of this point
1384   * @see {@link Point.copyFrom} For copying into existing point
1385   * @see {@link Point.copyTo} For copying to existing point
1386   */
1387  clone() {
1388    return new Point(this.x, this.y);
1389  }
1390  /**
1391   * Copies x and y from the given point into this point.
1392   * @example
1393   * ```ts
1394   * // Basic copying
1395   * const source = new Point(100, 200);
1396   * const target = new Point();
1397   * target.copyFrom(source);
1398   *
1399   * // Copy and chain operations
1400   * const point = new Point()
1401   *     .copyFrom(source)
1402   *     .set(x + 50, y + 50);
1403   *
1404   * // Copy from any PointData
1405   * const data = { x: 10, y: 20 };
1406   * point.copyFrom(data);
1407   * ```
1408   * @param p - The point to copy from
1409   * @returns The point instance itself
1410   * @see {@link Point.copyTo} For copying to another point
1411   * @see {@link Point.clone} For creating new point copy
1412   */
1413  copyFrom(p) {
1414    this.set(p.x, p.y);
1415    return this;
1416  }
1417  /**
1418   * Copies this point's x and y into the given point.
1419   * @example
1420   * ```ts
1421   * // Basic copying
1422   * const source = new Point(100, 200);
1423   * const target = new Point();
1424   * source.copyTo(target);
1425   * ```
1426   * @param p - The point to copy to. Can be any type that is or extends `PointLike`
1427   * @returns The point (`p`) with values updated
1428   * @see {@link Point.copyFrom} For copying from another point
1429   * @see {@link Point.clone} For creating new point copy
1430   */
1431  copyTo(p) {
1432    p.set(this.x, this.y);
1433    return p;
1434  }
1435  /**
1436   * Checks if another point is equal to this point.
1437   *
1438   * Compares x and y values using strict equality.
1439   * @example
1440   * ```ts
1441   * // Basic equality check
1442   * const p1 = new Point(100, 200);
1443   * const p2 = new Point(100, 200);
1444   * console.log(p1.equals(p2)); // true
1445   *
1446   * // Compare with PointData
1447   * const data = { x: 100, y: 200 };
1448   * console.log(p1.equals(data)); // true
1449   *
1450   * // Check different points
1451   * const p3 = new Point(200, 300);
1452   * console.log(p1.equals(p3)); // false
1453   * ```
1454   * @param p - The point to check
1455   * @returns `true` if both `x` and `y` are equal
1456   * @see {@link Point.copyFrom} For making points equal
1457   * @see {@link PointData} For point data interface
1458   */
1459  equals(p) {
1460    return p.x === this.x && p.y === this.y;
1461  }
1462  /**
1463   * Sets the point to a new x and y position.
1464   *
1465   * If y is omitted, both x and y will be set to x.
1466   * @example
1467   * ```ts
1468   * // Basic position setting
1469   * const point = new Point();
1470   * point.set(100, 200);
1471   *
1472   * // Set both x and y to same value
1473   * point.set(50); // x=50, y=50
1474   *
1475   * // Chain with other operations
1476   * point
1477   *     .set(10, 20)
1478   *     .copyTo(otherPoint);
1479   * ```
1480   * @param x - Position on the x axis
1481   * @param y - Position on the y axis, defaults to x
1482   * @returns The point instance itself
1483   * @see {@link Point.copyFrom} For copying from another point
1484   * @see {@link Point.equals} For comparing positions
1485   */
1486  set(x = 0, y = x) {
1487    this.x = x;
1488    this.y = y;
1489    return this;
1490  }
1491  toString() {
1492    return `[pixi.js/math:Point x=${this.x} y=${this.y}]`;
1493  }
1494  /**
1495   * A static Point object with `x` and `y` values of `0`.
1496   *
1497   * This shared instance is reset to zero values when accessed.
1498   *
1499   * > [!IMPORTANT] This point is shared and temporary. Do not store references to it.
1500   * @example
1501   * ```ts
1502   * // Use for temporary calculations
1503   * const tempPoint = Point.shared;
1504   * tempPoint.set(100, 200);
1505   * matrix.apply(tempPoint);
1506   *
1507   * // Will be reset to (0,0) on next access
1508   * const fresh = Point.shared; // x=0, y=0
1509   * ```
1510   * @readonly
1511   * @returns A fresh zeroed point for temporary use
1512   * @see {@link Point.constructor} For creating new points
1513   * @see {@link PointData} For basic point interface
1514   */
1515  static get shared() {
1516    tempPoint.x = 0;
1517    tempPoint.y = 0;
1518    return tempPoint;
1519  }
1520}
1521const tempPoint = new Point();
vendor: 16,584 bytes, lines 1522-2045
1522
1523class Matrix {
1524  /**
1525   * @param a - x scale
1526   * @param b - y skew
1527   * @param c - x skew
1528   * @param d - y scale
1529   * @param tx - x translation
1530   * @param ty - y translation
1531   */
1532  constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {
1533    /**
1534     * Array representation of the matrix.
1535     * Only populated when `toArray()` is called.
1536     * @default null
1537     * @see {@link Matrix.toArray} For filling this array
1538     */
1539    this.array = null;
1540    this.a = a;
1541    this.b = b;
1542    this.c = c;
1543    this.d = d;
1544    this.tx = tx;
1545    this.ty = ty;
1546  }
1547  /**
1548   * Creates a Matrix object based on the given array.
1549   * Populates matrix components from a flat array in column-major order.
1550   *
1551   * > [!NOTE] Array mapping order:
1552   * > ```
1553   * > array[0] = a  (x scale)
1554   * > array[1] = b  (y skew)
1555   * > array[2] = tx (x translation)
1556   * > array[3] = c  (x skew)
1557   * > array[4] = d  (y scale)
1558   * > array[5] = ty (y translation)
1559   * > ```
1560   * @example
1561   * ```ts
1562   * // Create matrix from array
1563   * const matrix = new Matrix();
1564   * matrix.fromArray([
1565   *     2, 0,  100,  // a, b, tx
1566   *     0, 2,  100   // c, d, ty
1567   * ]);
1568   *
1569   * // Create matrix from typed array
1570   * const float32Array = new Float32Array([
1571   *     1, 0, 0,     // Scale x1, no skew
1572   *     0, 1, 0      // No skew, scale x1
1573   * ]);
1574   * matrix.fromArray(float32Array);
1575   * ```
1576   * @param array - The array to populate the matrix from
1577   * @see {@link Matrix.toArray} For converting matrix to array
1578   * @see {@link Matrix.set} For setting values directly
1579   */
1580  fromArray(array) {
1581    this.a = array[0];
1582    this.b = array[1];
1583    this.c = array[3];
1584    this.d = array[4];
1585    this.tx = array[2];
1586    this.ty = array[5];
1587  }
1588  /**
1589   * Sets the matrix properties directly.
1590   * All matrix components can be set in one call.
1591   * @example
1592   * ```ts
1593   * // Set to identity matrix
1594   * matrix.set(1, 0, 0, 1, 0, 0);
1595   *
1596   * // Set to scale matrix
1597   * matrix.set(2, 0, 0, 2, 0, 0); // Scale 2x
1598   *
1599   * // Set to translation matrix
1600   * matrix.set(1, 0, 0, 1, 100, 50); // Move 100,50
1601   * ```
1602   * @param a - Scale on x axis
1603   * @param b - Shear on y axis
1604   * @param c - Shear on x axis
1605   * @param d - Scale on y axis
1606   * @param tx - Translation on x axis
1607   * @param ty - Translation on y axis
1608   * @returns This matrix. Good for chaining method calls.
1609   * @see {@link Matrix.identity} For resetting to identity
1610   * @see {@link Matrix.fromArray} For setting from array
1611   */
1612  set(a, b, c, d, tx, ty) {
1613    this.a = a;
1614    this.b = b;
1615    this.c = c;
1616    this.d = d;
1617    this.tx = tx;
1618    this.ty = ty;
1619    return this;
1620  }
1621  /**
1622   * Creates an array from the current Matrix object.
1623   *
1624   * > [!NOTE] The array format is:
1625   * > ```
1626   * > Non-transposed:
1627   * > [a, c, tx,
1628   * > b, d, ty,
1629   * > 0, 0, 1]
1630   * >
1631   * > Transposed:
1632   * > [a, b, 0,
1633   * > c, d, 0,
1634   * > tx,ty,1]
1635   * > ```
1636   * @example
1637   * ```ts
1638   * // Basic array conversion
1639   * const matrix = new Matrix(2, 0, 0, 2, 100, 100);
1640   * const array = matrix.toArray();
1641   *
1642   * // Using existing array
1643   * const float32Array = new Float32Array(9);
1644   * matrix.toArray(false, float32Array);
1645   *
1646   * // Get transposed array
1647   * const transposed = matrix.toArray(true);
1648   * ```
1649   * @param transpose - Whether to transpose the matrix
1650   * @param out - Optional Float32Array to store the result
1651   * @returns The array containing the matrix values
1652   * @see {@link Matrix.fromArray} For creating matrix from array
1653   * @see {@link Matrix.array} For cached array storage
1654   */
1655  toArray(transpose, out) {
1656    if (!this.array) {
1657      this.array = new Float32Array(9);
1658    }
1659    const array = out || this.array;
1660    if (transpose) {
1661      array[0] = this.a;
1662      array[1] = this.b;
1663      array[2] = 0;
1664      array[3] = this.c;
1665      array[4] = this.d;
1666      array[5] = 0;
1667      array[6] = this.tx;
1668      array[7] = this.ty;
1669      array[8] = 1;
1670    } else {
1671      array[0] = this.a;
1672      array[1] = this.c;
1673      array[2] = this.tx;
1674      array[3] = this.b;
1675      array[4] = this.d;
1676      array[5] = this.ty;
1677      array[6] = 0;
1678      array[7] = 0;
1679      array[8] = 1;
1680    }
1681    return array;
1682  }
1683  /**
1684   * Get a new position with the current transformation applied.
1685   *
1686   * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering)
1687   * @example
1688   * ```ts
1689   * // Basic point transformation
1690   * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4);
1691   * const point = new Point(10, 20);
1692   * const transformed = matrix.apply(point);
1693   *
1694   * // Reuse existing point
1695   * const output = new Point();
1696   * matrix.apply(point, output);
1697   * ```
1698   * @param pos - The origin point to transform
1699   * @param newPos - Optional point to store the result
1700   * @returns The transformed point
1701   * @see {@link Matrix.applyInverse} For inverse transformation
1702   * @see {@link Point} For point operations
1703   */
1704  apply(pos, newPos) {
1705    newPos = newPos || new Point();
1706    const x = pos.x;
1707    const y = pos.y;
1708    newPos.x = this.a * x + this.c * y + this.tx;
1709    newPos.y = this.b * x + this.d * y + this.ty;
1710    return newPos;
1711  }
1712  /**
1713   * Get a new position with the inverse of the current transformation applied.
1714   *
1715   * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input)
1716   * @example
1717   * ```ts
1718   * // Basic inverse transformation
1719   * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4);
1720   * const worldPoint = new Point(150, 100);
1721   * const localPoint = matrix.applyInverse(worldPoint);
1722   *
1723   * // Reuse existing point
1724   * const output = new Point();
1725   * matrix.applyInverse(worldPoint, output);
1726   *
1727   * // Convert mouse position to local space
1728   * const mousePoint = new Point(mouseX, mouseY);
1729   * const localMouse = matrix.applyInverse(mousePoint);
1730   * ```
1731   * @param pos - The origin point to inverse-transform
1732   * @param newPos - Optional point to store the result
1733   * @returns The inverse-transformed point
1734   * @see {@link Matrix.apply} For forward transformation
1735   * @see {@link Matrix.invert} For getting inverse matrix
1736   */
1737  applyInverse(pos, newPos) {
1738    newPos = newPos || new Point();
1739    const a = this.a;
1740    const b = this.b;
1741    const c = this.c;
1742    const d = this.d;
1743    const tx = this.tx;
1744    const ty = this.ty;
1745    const id = 1 / (a * d + c * -b);
1746    const x = pos.x;
1747    const y = pos.y;
1748    newPos.x = d * id * x + -c * id * y + (ty * c - tx * d) * id;
1749    newPos.y = a * id * y + -b * id * x + (-ty * a + tx * b) * id;
1750    return newPos;
1751  }
1752  /**
1753   * Translates the matrix on the x and y axes.
1754   * Adds to the position values while preserving scale, rotation and skew.
1755   * @example
1756   * ```ts
1757   * // Basic translation
1758   * const matrix = new Matrix();
1759   * matrix.translate(100, 50); // Move right 100, down 50
1760   *
1761   * // Chain with other transformations
1762   * matrix
1763   *     .scale(2, 2)
1764   *     .translate(100, 0)
1765   *     .rotate(Math.PI / 4);
1766   * ```
1767   * @param x - How much to translate on the x axis
1768   * @param y - How much to translate on the y axis
1769   * @returns This matrix. Good for chaining method calls.
1770   * @see {@link Matrix.set} For setting position directly
1771   * @see {@link Matrix.setTransform} For complete transform setup
1772   */
1773  translate(x, y) {
1774    this.tx += x;
1775    this.ty += y;
1776    return this;
1777  }
1778  /**
1779   * Applies a scale transformation to the matrix.
1780   * Multiplies the scale values with existing matrix components.
1781   * @example
1782   * ```ts
1783   * // Basic scaling
1784   * const matrix = new Matrix();
1785   * matrix.scale(2, 3); // Scale 2x horizontally, 3x vertically
1786   *
1787   * // Chain with other transformations
1788   * matrix
1789   *     .translate(100, 100)
1790   *     .scale(2, 2)     // Scales after translation
1791   *     .rotate(Math.PI / 4);
1792   * ```
1793   * @param x - The amount to scale horizontally
1794   * @param y - The amount to scale vertically
1795   * @returns This matrix. Good for chaining method calls.
1796   * @see {@link Matrix.setTransform} For setting scale directly
1797   * @see {@link Matrix.append} For combining transformations
1798   */
1799  scale(x, y) {
1800    this.a *= x;
1801    this.d *= y;
1802    this.c *= x;
1803    this.b *= y;
1804    this.tx *= x;
1805    this.ty *= y;
1806    return this;
1807  }
1808  /**
1809   * Applies a rotation transformation to the matrix.
1810   *
1811   * Rotates around the origin (0,0) by the given angle in radians.
1812   * @example
1813   * ```ts
1814   * // Basic rotation
1815   * const matrix = new Matrix();
1816   * matrix.rotate(Math.PI / 4); // Rotate 45 degrees
1817   *
1818   * // Chain with other transformations
1819   * matrix
1820   *     .translate(100, 100) // Move to rotation center
1821   *     .rotate(Math.PI)     // Rotate 180 degrees
1822   *     .scale(2, 2);        // Scale after rotation
1823   *
1824   * // Common angles
1825   * matrix.rotate(Math.PI / 2);  // 90 degrees
1826   * matrix.rotate(Math.PI);      // 180 degrees
1827   * matrix.rotate(Math.PI * 2);  // 360 degrees
1828   * ```
1829   * @remarks
1830   * - Rotates around origin point (0,0)
1831   * - Affects position if translation was set
1832   * - Uses counter-clockwise rotation
1833   * - Order of operations matters when chaining
1834   * @param angle - The angle in radians
1835   * @returns This matrix. Good for chaining method calls.
1836   * @see {@link Matrix.setTransform} For setting rotation directly
1837   * @see {@link Matrix.append} For combining transformations
1838   */
1839  rotate(angle) {
1840    const cos = Math.cos(angle);
1841    const sin = Math.sin(angle);
1842    const a1 = this.a;
1843    const c1 = this.c;
1844    const tx1 = this.tx;
1845    this.a = a1 * cos - this.b * sin;
1846    this.b = a1 * sin + this.b * cos;
1847    this.c = c1 * cos - this.d * sin;
1848    this.d = c1 * sin + this.d * cos;
1849    this.tx = tx1 * cos - this.ty * sin;
1850    this.ty = tx1 * sin + this.ty * cos;
1851    return this;
1852  }
1853  /**
1854   * Appends the given Matrix to this Matrix.
1855   * Combines two matrices by multiplying them together: this = this * matrix
1856   * @example
1857   * ```ts
1858   * // Basic matrix combination
1859   * const matrix = new Matrix();
1860   * const other = new Matrix().translate(100, 0).rotate(Math.PI / 4);
1861   * matrix.append(other);
1862   * ```
1863   * @remarks
1864   * - Order matters: A.append(B) !== B.append(A)
1865   * - Modifies current matrix
1866   * - Preserves transformation order
1867   * - Commonly used for combining transforms
1868   * @param matrix - The matrix to append
1869   * @returns This matrix. Good for chaining method calls.
1870   * @see {@link Matrix.prepend} For prepending transformations
1871   * @see {@link Matrix.appendFrom} For appending two external matrices
1872   */
1873  append(matrix) {
1874    const a1 = this.a;
1875    const b1 = this.b;
1876    const c1 = this.c;
1877    const d1 = this.d;
1878    this.a = matrix.a * a1 + matrix.b * c1;
1879    this.b = matrix.a * b1 + matrix.b * d1;
1880    this.c = matrix.c * a1 + matrix.d * c1;
1881    this.d = matrix.c * b1 + matrix.d * d1;
1882    this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx;
1883    this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty;
1884    return this;
1885  }
1886  /**
1887   * Appends two matrices and sets the result to this matrix.
1888   * Performs matrix multiplication: this = A * B
1889   * @example
1890   * ```ts
1891   * // Basic matrix multiplication
1892   * const result = new Matrix();
1893   * const matrixA = new Matrix().scale(2, 2);
1894   * const matrixB = new Matrix().rotate(Math.PI / 4);
1895   * result.appendFrom(matrixA, matrixB);
1896   * ```
1897   * @remarks
1898   * - Order matters: A * B !== B * A
1899   * - Creates a new transformation from two others
1900   * - More efficient than append() for multiple operations
1901   * - Does not modify input matrices
1902   * @param a - The first matrix to multiply
1903   * @param b - The second matrix to multiply
1904   * @returns This matrix. Good for chaining method calls.
1905   * @see {@link Matrix.append} For single matrix combination
1906   * @see {@link Matrix.prepend} For reverse order multiplication
1907   */
1908  appendFrom(a, b) {
1909    const a1 = a.a;
1910    const b1 = a.b;
1911    const c1 = a.c;
1912    const d1 = a.d;
1913    const tx = a.tx;
1914    const ty = a.ty;
1915    const a2 = b.a;
1916    const b2 = b.b;
1917    const c2 = b.c;
1918    const d2 = b.d;
1919    this.a = a1 * a2 + b1 * c2;
1920    this.b = a1 * b2 + b1 * d2;
1921    this.c = c1 * a2 + d1 * c2;
1922    this.d = c1 * b2 + d1 * d2;
1923    this.tx = tx * a2 + ty * c2 + b.tx;
1924    this.ty = tx * b2 + ty * d2 + b.ty;
1925    return this;
1926  }
1927  /**
1928   * Sets the matrix based on all the available properties.
1929   * Combines position, scale, rotation, skew and pivot in a single operation.
1930   * @example
1931   * ```ts
1932   * // Basic transform setup
1933   * const matrix = new Matrix();
1934   * matrix.setTransform(
1935   *     100, 100,    // position
1936   *     0, 0,        // pivot
1937   *     2, 2,        // scale
1938   *     Math.PI / 4, // rotation (45 degrees)
1939   *     0, 0         // skew
1940   * );
1941   * ```
1942   * @remarks
1943   * - Updates all matrix components at once
1944   * - More efficient than separate transform calls
1945   * - Uses radians for rotation and skew
1946   * - Pivot affects rotation center
1947   * @param x - Position on the x axis
1948   * @param y - Position on the y axis
1949   * @param pivotX - Pivot on the x axis
1950   * @param pivotY - Pivot on the y axis
1951   * @param scaleX - Scale on the x axis
1952   * @param scaleY - Scale on the y axis
1953   * @param rotation - Rotation in radians
1954   * @param skewX - Skew on the x axis
1955   * @param skewY - Skew on the y axis
1956   * @returns This matrix. Good for chaining method calls.
1957   * @see {@link Matrix.decompose} For extracting transform properties
1958   * @see {@link TransformableObject} For transform data structure
1959   */
1960  setTransform(x, y, pivotX, pivotY, scaleX, scaleY, rotation, skewX, skewY) {
1961    this.a = Math.cos(rotation + skewY) * scaleX;
1962    this.b = Math.sin(rotation + skewY) * scaleX;
1963    this.c = -Math.sin(rotation - skewX) * scaleY;
1964    this.d = Math.cos(rotation - skewX) * scaleY;
1965    this.tx = x - (pivotX * this.a + pivotY * this.c);
1966    this.ty = y - (pivotX * this.b + pivotY * this.d);
1967    return this;
1968  }
1969  /**
1970   * Prepends the given Matrix to this Matrix.
1971   * Combines two matrices by multiplying them together: this = matrix * this
1972   * @example
1973   * ```ts
1974   * // Basic matrix prepend
1975   * const matrix = new Matrix().scale(2, 2);
1976   * const other = new Matrix().translate(100, 0);
1977   * matrix.prepend(other); // Translation happens before scaling
1978   * ```
1979   * @remarks
1980   * - Order matters: A.prepend(B) !== B.prepend(A)
1981   * - Modifies current matrix
1982   * - Reverses transformation order compared to append()
1983   * @param matrix - The matrix to prepend
1984   * @returns This matrix. Good for chaining method calls.
1985   * @see {@link Matrix.append} For appending transformations
1986   * @see {@link Matrix.appendFrom} For combining external matrices
1987   */
1988  prepend(matrix) {
1989    const tx1 = this.tx;
1990    if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) {
1991      const a1 = this.a;
1992      const c1 = this.c;
1993      this.a = a1 * matrix.a + this.b * matrix.c;
1994      this.b = a1 * matrix.b + this.b * matrix.d;
1995      this.c = c1 * matrix.a + this.d * matrix.c;
1996      this.d = c1 * matrix.b + this.d * matrix.d;
1997    }
1998    this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx;
1999    this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty;
2000    return this;
2001  }
2002  /**
2003   * Decomposes the matrix into its individual transform components.
2004   * Extracts position, scale, rotation and skew values from the matrix.
2005   * @example
2006   * ```ts
2007   * // Basic decomposition
2008   * const matrix = new Matrix()
2009   *     .translate(100, 100)
2010   *     .rotate(Math.PI / 4)
2011   *     .scale(2, 2);
2012   *
2013   * const transform = {
2014   *     position: new Point(),
2015   *     scale: new Point(),
2016   *     pivot: new Point(),
2017   *     skew: new Point(),
2018   *     rotation: 0
2019   * };
2020   *
2021   * matrix.decompose(transform);
2022   * console.log(transform.position); // Point(100, 100)
2023   * console.log(transform.rotation); // ~0.785 (PI/4)
2024   * console.log(transform.scale); // Point(2, 2)
2025   * ```
2026   * @remarks
2027   * - Handles combined transformations
2028   * - Accounts for pivot points
2029   * - Chooses between rotation/skew based on transform type
2030   * - Uses radians for rotation and skew
2031   * @param transform - The transform object to store the decomposed values
2032   * @returns The transform with the newly applied properties
2033   * @see {@link Matrix.setTransform} For composing from components
2034   * @see {@link TransformableObject} For transform structure
2035   */
2036  decompose(transform) {
2037    const a = this.a;
2038    const b = this.b;
2039    const c = this.c;
2040    const d = this.d;
2041    const pivot = transform.pivot;
2042    const skewX = -Math.atan2(-c, d);
2043    const skewY = Math.atan2(b, a);
2044    const delta = Math.abs(skewX + skewY);
2045    
vendor: 7,573 bytes, lines 2045-2292
2045if (delta < 1e-5 || Math.abs(PI_2 - delta) < 1e-5) {
2046      transform.rotation = skewY;
2047      transform.skew.x = transform.skew.y = 0;
2048    } else {
2049      transform.rotation = 0;
2050      transform.skew.x = skewX;
2051      transform.skew.y = skewY;
2052    }
2053    transform.scale.x = Math.sqrt(a * a + b * b);
2054    transform.scale.y = Math.sqrt(c * c + d * d);
2055    transform.position.x = this.tx + (pivot.x * a + pivot.y * c);
2056    transform.position.y = this.ty + (pivot.x * b + pivot.y * d);
2057    return transform;
2058  }
2059  /**
2060   * Inverts this matrix.
2061   * Creates the matrix that when multiplied with this matrix results in an identity matrix.
2062   * @example
2063   * ```ts
2064   * // Basic matrix inversion
2065   * const matrix = new Matrix()
2066   *     .translate(100, 50)
2067   *     .scale(2, 2);
2068   *
2069   * matrix.invert(); // Now transforms in opposite direction
2070   *
2071   * // Verify inversion
2072   * const point = new Point(50, 50);
2073   * const transformed = matrix.apply(point);
2074   * const original = matrix.invert().apply(transformed);
2075   * // original ≈ point
2076   * ```
2077   * @remarks
2078   * - Modifies the current matrix
2079   * - Useful for reversing transformations
2080   * - Cannot invert matrices with zero determinant
2081   * @returns This matrix. Good for chaining method calls.
2082   * @see {@link Matrix.identity} For resetting to identity
2083   * @see {@link Matrix.applyInverse} For inverse transformations
2084   */
2085  invert() {
2086    const a1 = this.a;
2087    const b1 = this.b;
2088    const c1 = this.c;
2089    const d1 = this.d;
2090    const tx1 = this.tx;
2091    const n = a1 * d1 - b1 * c1;
2092    this.a = d1 / n;
2093    this.b = -b1 / n;
2094    this.c = -c1 / n;
2095    this.d = a1 / n;
2096    this.tx = (c1 * this.ty - d1 * tx1) / n;
2097    this.ty = -(a1 * this.ty - b1 * tx1) / n;
2098    return this;
2099  }
2100  /**
2101   * Checks if this matrix is an identity matrix.
2102   *
2103   * An identity matrix has no transformations applied (default state).
2104   * @example
2105   * ```ts
2106   * // Check if matrix is identity
2107   * const matrix = new Matrix();
2108   * console.log(matrix.isIdentity()); // true
2109   *
2110   * // Check after transformations
2111   * matrix.translate(100, 0);
2112   * console.log(matrix.isIdentity()); // false
2113   *
2114   * // Reset and verify
2115   * matrix.identity();
2116   * console.log(matrix.isIdentity()); // true
2117   * ```
2118   * @remarks
2119   * - Verifies a = 1, d = 1 (no scale)
2120   * - Verifies b = 0, c = 0 (no skew)
2121   * - Verifies tx = 0, ty = 0 (no translation)
2122   * @returns True if matrix has no transformations
2123   * @see {@link Matrix.identity} For resetting to identity
2124   * @see {@link Matrix.IDENTITY} For constant identity matrix
2125   */
2126  isIdentity() {
2127    return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.tx === 0 && this.ty === 0;
2128  }
2129  /**
2130   * Resets this Matrix to an identity (default) matrix.
2131   * Sets all components to their default values: scale=1, no skew, no translation.
2132   * @example
2133   * ```ts
2134   * // Reset transformed matrix
2135   * const matrix = new Matrix()
2136   *     .scale(2, 2)
2137   *     .rotate(Math.PI / 4);
2138   * matrix.identity(); // Back to default state
2139   *
2140   * // Chain after reset
2141   * matrix
2142   *     .identity()
2143   *     .translate(100, 100)
2144   *     .scale(2, 2);
2145   *
2146   * // Compare with identity constant
2147   * const isDefault = matrix.equals(Matrix.IDENTITY);
2148   * ```
2149   * @remarks
2150   * - Sets a=1, d=1 (default scale)
2151   * - Sets b=0, c=0 (no skew)
2152   * - Sets tx=0, ty=0 (no translation)
2153   * @returns This matrix. Good for chaining method calls.
2154   * @see {@link Matrix.IDENTITY} For constant identity matrix
2155   * @see {@link Matrix.isIdentity} For checking identity state
2156   */
2157  identity() {
2158    this.a = 1;
2159    this.b = 0;
2160    this.c = 0;
2161    this.d = 1;
2162    this.tx = 0;
2163    this.ty = 0;
2164    return this;
2165  }
2166  /**
2167   * Creates a new Matrix object with the same values as this one.
2168   * @returns A copy of this matrix. Good for chaining method calls.
2169   */
2170  clone() {
2171    const matrix = new Matrix();
2172    matrix.a = this.a;
2173    matrix.b = this.b;
2174    matrix.c = this.c;
2175    matrix.d = this.d;
2176    matrix.tx = this.tx;
2177    matrix.ty = this.ty;
2178    return matrix;
2179  }
2180  /**
2181   * Creates a new Matrix object with the same values as this one.
2182   * @param matrix
2183   * @example
2184   * ```ts
2185   * // Basic matrix cloning
2186   * const matrix = new Matrix()
2187   *     .translate(100, 100)
2188   *     .rotate(Math.PI / 4);
2189   * const copy = matrix.clone();
2190   *
2191   * // Clone and modify
2192   * const modified = matrix.clone()
2193   *     .scale(2, 2);
2194   *
2195   * // Compare matrices
2196   * console.log(matrix.equals(copy));     // true
2197   * console.log(matrix.equals(modified)); // false
2198   * ```
2199   * @returns A copy of this matrix. Good for chaining method calls.
2200   * @see {@link Matrix.copyTo} For copying to existing matrix
2201   * @see {@link Matrix.copyFrom} For copying from another matrix
2202   */
2203  copyTo(matrix) {
2204    matrix.a = this.a;
2205    matrix.b = this.b;
2206    matrix.c = this.c;
2207    matrix.d = this.d;
2208    matrix.tx = this.tx;
2209    matrix.ty = this.ty;
2210    return matrix;
2211  }
2212  /**
2213   * Changes the values of the matrix to be the same as the ones in given matrix.
2214   * @example
2215   * ```ts
2216   * // Basic matrix copying
2217   * const source = new Matrix()
2218   *     .translate(100, 100)
2219   *     .rotate(Math.PI / 4);
2220   * const target = new Matrix();
2221   * target.copyFrom(source);
2222   * ```
2223   * @param matrix - The matrix to copy from
2224   * @returns This matrix. Good for chaining method calls.
2225   * @see {@link Matrix.clone} For creating new matrix copy
2226   * @see {@link Matrix.copyTo} For copying to another matrix
2227   */
2228  copyFrom(matrix) {
2229    this.a = matrix.a;
2230    this.b = matrix.b;
2231    this.c = matrix.c;
2232    this.d = matrix.d;
2233    this.tx = matrix.tx;
2234    this.ty = matrix.ty;
2235    return this;
2236  }
2237  /**
2238   * Checks if this matrix equals another matrix.
2239   * Compares all components for exact equality.
2240   * @example
2241   * ```ts
2242   * // Basic equality check
2243   * const m1 = new Matrix();
2244   * const m2 = new Matrix();
2245   * console.log(m1.equals(m2)); // true
2246   *
2247   * // Compare transformed matrices
2248   * const transform = new Matrix()
2249   *     .translate(100, 100)
2250   * const clone = new Matrix()
2251   *     .scale(2, 2);
2252   * console.log(transform.equals(clone)); // false
2253   * ```
2254   * @param matrix - The matrix to compare to
2255   * @returns True if matrices are identical
2256   * @see {@link Matrix.copyFrom} For copying matrix values
2257   * @see {@link Matrix.isIdentity} For identity comparison
2258   */
2259  equals(matrix) {
2260    return matrix.a === this.a && matrix.b === this.b && matrix.c === this.c && matrix.d === this.d && matrix.tx === this.tx && matrix.ty === this.ty;
2261  }
2262  toString() {
2263    return `[pixi.js:Matrix a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`;
2264  }
2265  /**
2266   * A default (identity) matrix with no transformations applied.
2267   *
2268   * > [!IMPORTANT] This is a shared read-only object. Create a new Matrix if you need to modify it.
2269   * @example
2270   * ```ts
2271   * // Get identity matrix reference
2272   * const identity = Matrix.IDENTITY;
2273   * console.log(identity.isIdentity()); // true
2274   *
2275   * // Compare with identity
2276   * const matrix = new Matrix();
2277   * console.log(matrix.equals(Matrix.IDENTITY)); // true
2278   *
2279   * // Create new matrix instead of modifying IDENTITY
2280   * const transform = new Matrix()
2281   *     .copyFrom(Matrix.IDENTITY)
2282   *     .translate(100, 100);
2283   * ```
2284   * @readonly
2285   * @returns A read-only identity matrix
2286   * @see {@link Matrix.shared} For temporary calculations
2287   * @see {@link Matrix.identity} For resetting matrices
2288   */
2289  static get IDENTITY() {
2290    return identityMatrix$1.identity();
2291  }
2292  /**
2293   * A static Matrix that can be used to avoid creating new objects.
2294   * Will always ensure the matrix is reset to identity when requested.
2295   *
2296   * > [!IMPORTANT] This matrix is shared and temporary. Do not store references to it.
2297   * @example
2298   * ```ts
2299   * // Use for temporary calculations
2300   * const tempMatrix = Matrix.shared;
2301   * tempMatrix.translate(100, 100).rotate(Math.PI / 4);
2302   * const point = tempMatrix.apply({ x: 10, y: 20 });
2303   *
2304   * // Will be reset to identity on next access
2305   * const fresh = Matrix.shared; // Back to identity
2306   * ```
2307   * @remarks
2308   * - Always returns identity matrix
2309   * - Safe to modify temporarily
2310   * - Not safe to store references
2311   * - Useful for one-off calculations
2312   * @readonly
2313   * @returns A fresh identity matrix for temporary use
2314   * @see {@link Matrix.IDENTITY} For immutable identity matrix
2315   * @see {@link Matrix.identity} For resetting matrices
2316   */
2317  static get shared() {
2318    return tempMatrix$3.identity();
2319  }
2320}
2321const tempMatrix$3 = new Matrix();
2322const identityMatrix$1 = new Matrix();
2323
2324const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1];
2325const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1];
2326const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1];
2327const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1];
2328const rotationCayley = [];
2329const rotationMatrices = [];
2330const signum = Math.sign;
2331function init() {
2332  for (let i = 0; i < 16; i++) {
2333    const row = [];
2334    rotationCayley.push(row);
2335    for (let j = 0; j < 16; j++) {
2336      const _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]);
2337      const _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]);
2338      const _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]);
2339      const _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]);
2340      for (let k = 0; k < 16; k++) {
2341        if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) {
2342          row.push(k);
2343          break;
2344        }
2345      }
2346    }
2347  }
2348  for (let i = 0; i < 16; i++) {
2349    const mat = new Matrix();
2350    mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0);
2351    rotationMatrices.push(mat);
2352  }
2353}
2354init();
2355const groupD8 = {
2356  /**
2357   * | Rotation | Direction |
2358   * |----------|-----------|
2359   * | 0°       | East      |
2360   * @group groupD8
2361   * @type {GD8Symmetry}
2362   */
2363  E: 0,
2364  /**
2365   * | Rotation | Direction |
2366   * |----------|-----------|
2367   * | 45°↻     | Southeast |
2368   * @group groupD8
2369   * @type {GD8Symmetry}
2370   */
2371  SE: 1,
2372  /**
2373   * | Rotation | Direction |
2374   * |----------|-----------|
2375   * | 90°↻     | South     |
2376   * @group groupD8
2377   * @type {GD8Symmetry}
2378   */
2379  S: 2,
2380  /**
2381   * | Rotation | Direction |
2382   * |----------|-----------|
2383   * | 135°↻    | Southwest |
2384   * @group groupD8
2385   * @type {GD8Symmetry}
2386   */
2387  SW: 3,
2388  /**
2389   * | Rotation | Direction |
2390   * |----------|-----------|
2391   * | 180°     | West      |
2392   * @group groupD8
2393   * @type {GD8Symmetry}
2394   */
2395  W: 4,
2396  /**
2397   * | Rotation    | Direction    |
2398   * |-------------|--------------|
2399   * | -135°/225°↻ | Northwest    |
2400   * @group groupD8
2401   * @type {GD8Symmetry}
2402   */
2403  NW: 5,
2404  /**
2405   * | Rotation    | Direction    |
2406   * |-------------|--------------|
2407   * | -90°/270°↻  | North        |
2408   * @group groupD8
2409   * @type {GD8Symmetry}
2410   */
2411  N: 6,
2412  /**
2413   * | Rotation    | Direction    |
2414   * |-------------|--------------|
2415   * | -45°/315°↻  | Northeast    |
2416   * @group groupD8
2417   * @type {GD8Symmetry}
2418   */
2419  NE: 7,
2420  /**
2421   * Reflection about Y-axis.
2422   * @group groupD8
2423   * @type {GD8Symmetry}
2424   */
2425  MIRROR_VERTICAL: 8,
2426  /**
2427   * Reflection about the main diagonal.
2428   * @group groupD8
2429   * @type {GD8Symmetry}
2430   */
2431  MAIN_DIAGONAL: 10,
2432  /**
2433   * Reflection about X-axis.
2434   * @group groupD8
2435   * @type {GD8Symmetry}
2436   */
2437  MIRROR_HORIZONTAL: 12,
2438  /**
2439   * Reflection about reverse diagonal.
2440   * @group groupD8
2441   * @type {GD8Symmetry}
2442   */
2443  REVERSE_DIAGONAL: 14,
2444  /**
2445   * @group groupD8
2446   * @param {GD8Symmetry} ind - sprite rotation angle.
2447   * @returns {GD8Symmetry} The X-component of the U-axis
2448   *    after rotating the axes.
2449   */
2450  uX: (ind) => ux[ind],
2451  /**
2452   * @group groupD8
2453   * @param {GD8Symmetry} ind - sprite rotation angle.
2454   * @returns {GD8Symmetry} The Y-component of the U-axis
2455   *    after rotating the axes.
2456   */
2457  uY: (ind) => uy[ind],
2458  /**
2459   * @group groupD8
2460   * @param {GD8Symmetry} ind - sprite rotation angle.
2461   * @returns {GD8Symmetry} The X-component of the V-axis
2462   *    after rotating the axes.
2463   */
2464  vX: (ind) => vx[ind],
2465  /**
2466   * @group groupD8
2467   * @param {GD8Symmetry} ind - sprite rotation angle.
2468   * @returns {GD8Symmetry} The Y-component of the V-axis
2469   *    after rotating the axes.
2470   */
2471  vY: (ind) => vy[ind],
2472  /**
2473   * @group groupD8
2474   * @param {GD8Symmetry} rotation - symmetry whose opposite
2475   *   is needed. Only rotations have opposite symmetries while
2476   *   reflections don't.
2477   * @returns {GD8Symmetry} The opposite symmetry of `rotation`
2478   */
2479  inv: (rotation) => {
2480    if (rotation & 8) {
2481      return rotation & 15;
2482    }
2483    return -rotation & 7;
2484  },
2485  /**
2486   * Composes the two D8 operations.
2487   *
2488   * Taking `^` as reflection:
2489   *
2490   * |       | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 |
2491   * |-------|-----|-----|-----|-----|------|-------|-------|-------|
2492   * | E=0   | E   | S   | W   | N   | E^   | S^    | W^    | N^    |
2493   * | S=2   | S   | W   | N   | E   | S^   | W^    | N^    | E^    |
2494   * | W=4   | W   | N   | E   | S   | W^   | N^    | E^    | S^    |
2495   * | N=6   | N   | E   | S   | W   | N^   | E^    | S^    | W^    |
2496   * | E^=8  | E^  | N^  | W^  | S^  | E    | N     | W     | S     |
2497   * | S^=10 | S^  | E^  | N^  | W^  | S    | E     | N     | W     |
2498   * | W^=12 | W^  | S^  | E^  | N^  | W    | S     | E     | N     |
2499   * | N^=14 | N^  | W^  | S^  | E^  | N    | W     | S     | E     |
2500   *
2501   * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table}
2502   * @group groupD8
2503   * @param {GD8Symmetry} rotationSecond - Second operation, which
2504   *   is the row in the above cayley table.
2505   * @param {GD8Symmetry} rotationFirst - First operation, which
2506   *   is the column in the above cayley table.
2507   * @returns {GD8Symmetry} Composed operation
2508   */
2509  add: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][rotationFirst],
2510  /**
2511   * Reverse of `add`.
2512   * @group groupD8
2513   * @param {GD8Symmetry} rotationSecond - Second operation
2514   * @param {GD8Symmetry} rotationFirst - First operation
2515   * @returns {GD8Symmetry} Result
2516   */
2517  sub: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][groupD8.inv(rotationFirst)],
2518  /**
2519   * Adds 180 degrees to rotation, which is a commutative
2520   * operation.
2521   * @group groupD8
2522   * @param {number} rotation - The number to rotate.
2523   * @returns {number} Rotated number
2524   */
2525  rotate180: (rotation) => rotation ^ 4,
2526  /**
2527   * Checks if the rotation angle is vertical, i.e. south
2528   * or north. It doesn't work for reflections.
2529   * @group groupD8
2530   * @param {GD8Symmetry} rotation - The number to check.
2531   * @returns {boolean} Whether or not the direction is vertical
2532   */
2533  isVertical: (rotation) => (rotation & 3) === 2,
2534  // rotation % 4 === 2
2535  /**
2536   * Approximates the vector `V(dx,dy)` into one of the
2537   * eight directions provided by `groupD8`.
2538   * @group groupD8
2539   * @param {number} dx - X-component of the vector
2540   * @param {number} dy - Y-component of the vector
2541   * @returns {GD8Symmetry} Approximation of the vector into
2542   *  one of the eight symmetries.
2543   */
2544  byDirection: (dx, dy) => {
2545    if (Math.abs(dx) * 2 <= Math.abs(dy)) {
2546      if (dy >= 0) {
2547        return groupD8.S;
2548      }
2549      return groupD8.N;
2550    } else if (Math.abs(dy) * 2 <= Math.abs(dx)) {
2551      if (dx > 0) {
2552        return groupD8.E;
2553      }
2554      return groupD8.W;
2555    } else if (dy > 0) {
2556      if (dx > 0) {
2557        return groupD8.SE;
2558      }
2559      return groupD8.SW;
2560    } else if (dx > 0) {
2561      return groupD8.NE;
2562    }
2563    return groupD8.NW;
2564  },
2565  /**
2566   * Helps sprite to compensate texture packer rotation.
2567   * @group groupD8
2568   * @param {Matrix} matrix - sprite world matrix
2569   * @param {GD8Symmetry} rotation - The rotation factor to use.
2570   * @param {number} tx - sprite anchoring
2571   * @param {number} ty - sprite anchoring
2572   */
2573  matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => {
2574    const mat = rotationMatrices[groupD8.inv(rotation)];
2575    mat.tx = tx;
2576    mat.ty = ty;
2577    matrix.append(mat);
2578  },
2579  /**
2580   * Transforms rectangle coordinates based on texture packer rotation.
2581   * Used when texture atlas pages are rotated and coordinates need to be adju
2581sted.
2582   * @group groupD8
2583   * @param {RectangleLike} rect - Rectangle with original coordinates to transform
2584   * @param {RectangleLike} sourceFrame - Source texture frame (includes offset and dimensions)
2585   * @param {GD8Symmetry} rotation - The groupD8 rotation value
2586   * @param {Rectangle} out - Rectangle to store the result
2587   * @returns {Rectangle} Transformed coordinates (includes source frame offset)
2588   */
2589  transformRectCoords: (rect, sourceFrame, rotation, out) => {
2590    const { x, y, width, height } = rect;
2591    const { x: frameX, y: frameY, width: frameWidth, height: frameHeight } = sourceFrame;
2592    if (rotation === groupD8.E) {
2593      out.set(x + frameX, y + frameY, width, height);
2594      return out;
2595    } else if (rotation === groupD8.S) {
2596      return out.set(
2597        frameWidth - y - height + frameX,
2598        x + frameY,
2599        height,
2600        width
2601      );
2602    } else if (rotation === groupD8.W) {
2603      return out.set(
2604        frameWidth - x - width + frameX,
2605        frameHeight - y - height + frameY,
2606        width,
2607        height
2608      );
2609    } else if (rotation === groupD8.N) {
2610      return out.set(
2611        y + frameX,
2612        frameHeight - x - width + frameY,
2613        height,
2614        width
2615      );
2616    }
2617    return out.set(x + frameX, y + frameY, width, height);
2618  }
2619};
vendor: 9,439 bytes, lines 2620-2922
2620
2621const tempPoints = [new Point(), new Point(), new Point(), new Point()];
2622class Rectangle {
2623  /**
2624   * @param x - The X coordinate of the upper-left corner of the rectangle
2625   * @param y - The Y coordinate of the upper-left corner of the rectangle
2626   * @param width - The overall width of the rectangle
2627   * @param height - The overall height of the rectangle
2628   */
2629  constructor(x = 0, y = 0, width = 0, height = 0) {
2630    /**
2631     * The type of the object, mainly used to avoid `instanceof` checks
2632     * @example
2633     * ```ts
2634     * // Check shape type
2635     * const shape = new Rectangle(0, 0, 100, 100);
2636     * console.log(shape.type); // 'rectangle'
2637     *
2638     * // Use in type guards
2639     * if (shape.type === 'rectangle') {
2640     *     console.log(shape.width, shape.height);
2641     * }
2642     * ```
2643     * @readonly
2644     * @default 'rectangle'
2645     * @see {@link SHAPE_PRIMITIVE} For all shape types
2646     */
2647    this.type = "rectangle";
2648    this.x = Number(x);
2649    this.y = Number(y);
2650    this.width = Number(width);
2651    this.height = Number(height);
2652  }
2653  /**
2654   * Returns the left edge (x-coordinate) of the rectangle.
2655   * @example
2656   * ```ts
2657   * // Get left edge position
2658   * const rect = new Rectangle(100, 100, 200, 150);
2659   * console.log(rect.left); // 100
2660   *
2661   * // Use in alignment calculations
2662   * sprite.x = rect.left + padding;
2663   *
2664   * // Compare positions
2665   * if (point.x > rect.left) {
2666   *     console.log('Point is right of rectangle');
2667   * }
2668   * ```
2669   * @readonly
2670   * @returns The x-coordinate of the left edge
2671   * @see {@link Rectangle.right} For right edge position
2672   * @see {@link Rectangle.x} For direct x-coordinate access
2673   */
2674  get left() {
2675    return this.x;
2676  }
2677  /**
2678   * Returns the right edge (x + width) of the rectangle.
2679   * @example
2680   * ```ts
2681   * // Get right edge position
2682   * const rect = new Rectangle(100, 100, 200, 150);
2683   * console.log(rect.right); // 300
2684   *
2685   * // Align to right edge
2686   * sprite.x = rect.right - sprite.width;
2687   *
2688   * // Check boundaries
2689   * if (point.x < rect.right) {
2690   *     console.log('Point is inside right bound');
2691   * }
2692   * ```
2693   * @readonly
2694   * @returns The x-coordinate of the right edge
2695   * @see {@link Rectangle.left} For left edge position
2696   * @see {@link Rectangle.width} For width value
2697   */
2698  get right() {
2699    return this.x + this.width;
2700  }
2701  /**
2702   * Returns the top edge (y-coordinate) of the rectangle.
2703   * @example
2704   * ```ts
2705   * // Get top edge position
2706   * const rect = new Rectangle(100, 100, 200, 150);
2707   * console.log(rect.top); // 100
2708   *
2709   * // Position above rectangle
2710   * sprite.y = rect.top - sprite.height;
2711   *
2712   * // Check vertical position
2713   * if (point.y > rect.top) {
2714   *     console.log('Point is below top edge');
2715   * }
2716   * ```
2717   * @readonly
2718   * @returns The y-coordinate of the top edge
2719   * @see {@link Rectangle.bottom} For bottom edge position
2720   * @see {@link Rectangle.y} For direct y-coordinate access
2721   */
2722  get top() {
2723    return this.y;
2724  }
2725  /**
2726   * Returns the bottom edge (y + height) of the rectangle.
2727   * @example
2728   * ```ts
2729   * // Get bottom edge position
2730   * const rect = new Rectangle(100, 100, 200, 150);
2731   * console.log(rect.bottom); // 250
2732   *
2733   * // Stack below rectangle
2734   * sprite.y = rect.bottom + margin;
2735   *
2736   * // Check vertical bounds
2737   * if (point.y < rect.bottom) {
2738   *     console.log('Point is above bottom edge');
2739   * }
2740   * ```
2741   * @readonly
2742   * @returns The y-coordinate of the bottom edge
2743   * @see {@link Rectangle.top} For top edge position
2744   * @see {@link Rectangle.height} For height value
2745   */
2746  get bottom() {
2747    return this.y + this.height;
2748  }
2749  /**
2750   * Determines whether the Rectangle is empty (has no area).
2751   * @example
2752   * ```ts
2753   * // Check zero dimensions
2754   * const rect = new Rectangle(100, 100, 0, 50);
2755   * console.log(rect.isEmpty()); // true
2756   * ```
2757   * @returns True if the rectangle has no area
2758   * @see {@link Rectangle.width} For width value
2759   * @see {@link Rectangle.height} For height value
2760   */
2761  isEmpty() {
2762    return this.left === this.right || this.top === this.bottom;
2763  }
2764  /**
2765   * A constant empty rectangle. This is a new object every time the property is accessed.
2766   * @example
2767   * ```ts
2768   * // Get fresh empty rectangle
2769   * const empty = Rectangle.EMPTY;
2770   * console.log(empty.isEmpty()); // true
2771   * ```
2772   * @returns A new empty rectangle instance
2773   * @see {@link Rectangle.isEmpty} For empty state testing
2774   */
2775  static get EMPTY() {
2776    return new Rectangle(0, 0, 0, 0);
2777  }
2778  /**
2779   * Creates a clone of this Rectangle
2780   * @example
2781   * ```ts
2782   * // Basic cloning
2783   * const original = new Rectangle(100, 100, 200, 150);
2784   * const copy = original.clone();
2785   *
2786   * // Clone and modify
2787   * const modified = original.clone();
2788   * modified.width *= 2;
2789   * modified.height += 50;
2790   *
2791   * // Verify independence
2792   * console.log(original.width);  // 200
2793   * console.log(modified.width);  // 400
2794   * ```
2795   * @returns A copy of the rectangle
2796   * @see {@link Rectangle.copyFrom} For copying into existing rectangle
2797   * @see {@link Rectangle.copyTo} For copying to another rectangle
2798   */
2799  clone() {
2800    return new Rectangle(this.x, this.y, this.width, this.height);
2801  }
2802  /**
2803   * Converts a Bounds object to a Rectangle object.
2804   * @example
2805   * ```ts
2806   * // Convert bounds to rectangle
2807   * const bounds = container.getBounds();
2808   * const rect = new Rectangle().copyFromBounds(bounds);
2809   * ```
2810   * @param bounds - The bounds to copy and convert to a rectangle
2811   * @returns Returns itself
2812   * @see {@link Bounds} For bounds object structure
2813   * @see {@link Rectangle.getBounds} For getting rectangle bounds
2814   */
2815  copyFromBounds(bounds) {
2816    this.x = bounds.minX;
2817    this.y = bounds.minY;
2818    this.width = bounds.maxX - bounds.minX;
2819    this.height = bounds.maxY - bounds.minY;
2820    return this;
2821  }
2822  /**
2823   * Copies another rectangle to this one.
2824   * @example
2825   * ```ts
2826   * // Basic copying
2827   * const source = new Rectangle(100, 100, 200, 150);
2828   * const target = new Rectangle();
2829   * target.copyFrom(source);
2830   *
2831   * // Chain with other operations
2832   * const rect = new Rectangle()
2833   *     .copyFrom(source)
2834   *     .pad(10);
2835   * ```
2836   * @param rectangle - The rectangle to copy from
2837   * @returns Returns itself
2838   * @see {@link Rectangle.copyTo} For copying to another rectangle
2839   * @see {@link Rectangle.clone} For creating new rectangle copy
2840   */
2841  copyFrom(rectangle) {
2842    this.x = rectangle.x;
2843    this.y = rectangle.y;
2844    this.width = rectangle.width;
2845    this.height = rectangle.height;
2846    return this;
2847  }
2848  /**
2849   * Copies this rectangle to another one.
2850   * @example
2851   * ```ts
2852   * // Basic copying
2853   * const source = new Rectangle(100, 100, 200, 150);
2854   * const target = new Rectangle();
2855   * source.copyTo(target);
2856   *
2857   * // Chain with other operations
2858   * const result = source
2859   *     .copyTo(new Rectangle())
2860   *     .getBounds();
2861   * ```
2862   * @param rectangle - The rectangle to copy to
2863   * @returns Returns given parameter
2864   * @see {@link Rectangle.copyFrom} For copying from another rectangle
2865   * @see {@link Rectangle.clone} For creating new rectangle copy
2866   */
2867  copyTo(rectangle) {
2868    rectangle.copyFrom(this);
2869    return rectangle;
2870  }
2871  /**
2872   * Checks whether the x and y coordinates given are contained within this Rectangle
2873   * @example
2874   * ```ts
2875   * // Basic containment check
2876   * const rect = new Rectangle(100, 100, 200, 150);
2877   * const isInside = rect.contains(150, 125); // true
2878   * // Check edge cases
2879   * console.log(rect.contains(100, 100)); // true (on edge)
2880   * console.log(rect.contains(300, 250)); // false (outside)
2881   * ```
2882   * @param x - The X coordinate of the point to test
2883   * @param y - The Y coordinate of the point to test
2884   * @returns Whether the x/y coordinates are within this Rectangle
2885   * @see {@link Rectangle.containsRect} For rectangle containment
2886   * @see {@link Rectangle.strokeContains} For checking stroke intersection
2887   */
2888  contains(x, y) {
2889    if (this.width <= 0 || this.height <= 0) {
2890      return false;
2891    }
2892    if (x >= this.x && x < this.x + this.width) {
2893      if (y >= this.y && y < this.y + this.height) {
2894        return true;
2895      }
2896    }
2897    return false;
2898  }
2899  /**
2900   * Checks whether the x and y coordinates given are contained within this rectangle including the stroke.
2901   * @example
2902   * ```ts
2903   * // Basic stroke check
2904   * const rect = new Rectangle(100, 100, 200, 150);
2905   * const isOnStroke = rect.strokeContains(150, 100, 4); // 4px line width
2906   *
2907   * // Check with different alignments
2908   * const innerStroke = rect.strokeContains(150, 100, 4, 1);   // Inside
2909   * const centerStroke = rect.strokeContains(150, 100, 4, 0.5); // Centered
2910   * const outerStroke = rect.strokeContains(150, 100, 4, 0);   // Outside
2911   * ```
2912   * @param x - The X coordinate of the point to test
2913   * @param y - The Y coordinate of the point to test
2914   * @param strokeWidth - The width of the line to check
2915   * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer)
2916   * @returns Whether the x/y coordinates are within this rectangle's stroke
2917   * @see {@link Rectangle.contains} For checking fill containment
2918   * @see {@link Rectangle.getBounds} For getting stroke bounds
2919   */
2920  strokeContains(x, y, strokeWidth, alignment = 0.5) {
2921    const { width, height } = this;
2922    if (width <= 0 || height <= 0)
2922
2923     
vendor: 11,306 bytes, lines 2923-3221
2923 return false;
2924    const _x = this.x;
2925    const _y = this.y;
2926    const strokeWidthOuter = strokeWidth * (1 - alignment);
2927    const strokeWidthInner = strokeWidth - strokeWidthOuter;
2928    const outerLeft = _x - strokeWidthOuter;
2929    const outerRight = _x + width + strokeWidthOuter;
2930    const outerTop = _y - strokeWidthOuter;
2931    const outerBottom = _y + height + strokeWidthOuter;
2932    const innerLeft = _x + strokeWidthInner;
2933    const innerRight = _x + width - strokeWidthInner;
2934    const innerTop = _y + strokeWidthInner;
2935    const innerBottom = _y + height - strokeWidthInner;
2936    return x >= outerLeft && x <= outerRight && y >= outerTop && y <= outerBottom && !(x > innerLeft && x < innerRight && y > innerTop && y < innerBottom);
2937  }
2938  /**
2939   * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object.
2940   * Returns true only if the area of the intersection is >0, this means that Rectangles
2941   * sharing a side are not overlapping. Another side effect is that an arealess rectangle
2942   * (width or height equal to zero) can't intersect any other rectangle.
2943   * @param {Rectangle} other - The Rectangle to intersect with `this`.
2944   * @param {Matrix} transform - The transformation matrix of `other`.
2945   * @returns {boolean} A value of `true` if the transformed `other` Rectangle intersects with `this`; otherwise `false`.
2946   */
2947  /**
2948   * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object.
2949   *
2950   * Returns true only if the area of the intersection is greater than 0.
2951   * This means that rectangles sharing only a side are not considered intersecting.
2952   * @example
2953   * ```ts
2954   * // Basic intersection check
2955   * const rect1 = new Rectangle(0, 0, 100, 100);
2956   * const rect2 = new Rectangle(50, 50, 100, 100);
2957   * console.log(rect1.intersects(rect2)); // true
2958   *
2959   * // With transformation matrix
2960   * const matrix = new Matrix();
2961   * matrix.rotate(Math.PI / 4); // 45 degrees
2962   * console.log(rect1.intersects(rect2, matrix)); // Checks with rotation
2963   *
2964   * // Edge cases
2965   * const zeroWidth = new Rectangle(0, 0, 0, 100);
2966   * console.log(rect1.intersects(zeroWidth)); // false (no area)
2967   * ```
2968   * @remarks
2969   * - Returns true only if intersection area is > 0
2970   * - Rectangles sharing only a side are not intersecting
2971   * - Zero-area rectangles cannot intersect anything
2972   * - Supports optional transformation matrix
2973   * @param other - The Rectangle to intersect with `this`
2974   * @param transform - Optional transformation matrix of `other`
2975   * @returns True if the transformed `other` Rectangle intersects with `this`
2976   * @see {@link Rectangle.containsRect} For containment testing
2977   * @see {@link Rectangle.contains} For point testing
2978   */
2979  intersects(other, transform) {
2980    if (!transform) {
2981      const x02 = this.x < other.x ? other.x : this.x;
2982      const x12 = this.right > other.right ? other.right : this.right;
2983      if (x12 <= x02) {
2984        return false;
2985      }
2986      const y02 = this.y < other.y ? other.y : this.y;
2987      const y12 = this.bottom > other.bottom ? other.bottom : this.bottom;
2988      return y12 > y02;
2989    }
2990    const x0 = this.left;
2991    const x1 = this.right;
2992    const y0 = this.top;
2993    const y1 = this.bottom;
2994    if (x1 <= x0 || y1 <= y0) {
2995      return false;
2996    }
2997    const lt = tempPoints[0].set(other.left, other.top);
2998    const lb = tempPoints[1].set(other.left, other.bottom);
2999    const rt = tempPoints[2].set(other.right, other.top);
3000    const rb = tempPoints[3].set(other.right, other.bottom);
3001    if (rt.x <= lt.x || lb.y <= lt.y) {
3002      return false;
3003    }
3004    const s = Math.sign(transform.a * transform.d - transform.b * transform.c);
3005    if (s === 0) {
3006      return false;
3007    }
3008    transform.apply(lt, lt);
3009    transform.apply(lb, lb);
3010    transform.apply(rt, rt);
3011    transform.apply(rb, rb);
3012    if (Math.max(lt.x, lb.x, rt.x, rb.x) <= x0 || Math.min(lt.x, lb.x, rt.x, rb.x) >= x1 || Math.max(lt.y, lb.y, rt.y, rb.y) <= y0 || Math.min(lt.y, lb.y, rt.y, rb.y) >= y1) {
3013      return false;
3014    }
3015    const nx = s * (lb.y - lt.y);
3016    const ny = s * (lt.x - lb.x);
3017    const n00 = nx * x0 + ny * y0;
3018    const n10 = nx * x1 + ny * y0;
3019    const n01 = nx * x0 + ny * y1;
3020    const n11 = nx * x1 + ny * y1;
3021    if (Math.max(n00, n10, n01, n11) <= nx * lt.x + ny * lt.y || Math.min(n00, n10, n01, n11) >= nx * rb.x + ny * rb.y) {
3022      return false;
3023    }
3024    const mx = s * (lt.y - rt.y);
3025    const my = s * (rt.x - lt.x);
3026    const m00 = mx * x0 + my * y0;
3027    const m10 = mx * x1 + my * y0;
3028    const m01 = mx * x0 + my * y1;
3029    const m11 = mx * x1 + my * y1;
3030    if (Math.max(m00, m10, m01, m11) <= mx * lt.x + my * lt.y || Math.min(m00, m10, m01, m11) >= mx * rb.x + my * rb.y) {
3031      return false;
3032    }
3033    return true;
3034  }
3035  /**
3036   * Pads the rectangle making it grow in all directions.
3037   *
3038   * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.
3039   * @example
3040   * ```ts
3041   * // Basic padding
3042   * const rect = new Rectangle(100, 100, 200, 150);
3043   * rect.pad(10); // Adds 10px padding on all sides
3044   *
3045   * // Different horizontal and vertical padding
3046   * const uiRect = new Rectangle(0, 0, 100, 50);
3047   * uiRect.pad(20, 10); // 20px horizontal, 10px vertical
3048   * ```
3049   * @remarks
3050   * - Adjusts x/y by subtracting padding
3051   * - Increases width/height by padding * 2
3052   * - Common in UI layout calculations
3053   * - Chainable with other methods
3054   * @param paddingX - The horizontal padding amount
3055   * @param paddingY - The vertical padding amount
3056   * @returns Returns itself
3057   * @see {@link Rectangle.enlarge} For growing to include another rectangle
3058   * @see {@link Rectangle.fit} For shrinking to fit within another rectangle
3059   */
3060  pad(paddingX = 0, paddingY = paddingX) {
3061    this.x -= paddingX;
3062    this.y -= paddingY;
3063    this.width += paddingX * 2;
3064    this.height += paddingY * 2;
3065    return this;
3066  }
3067  /**
3068   * Fits this rectangle around the passed one.
3069   * @example
3070   * ```ts
3071   * // Basic fitting
3072   * const container = new Rectangle(0, 0, 100, 100);
3073   * const content = new Rectangle(25, 25, 200, 200);
3074   * content.fit(container); // Clips to container bounds
3075   * ```
3076   * @param rectangle - The rectangle to fit around
3077   * @returns Returns itself
3078   * @see {@link Rectangle.enlarge} For growing to include another rectangle
3079   * @see {@link Rectangle.pad} For adding padding around the rectangle
3080   */
3081  fit(rectangle) {
3082    const x1 = Math.max(this.x, rectangle.x);
3083    const x2 = Math.min(this.x + this.width, rectangle.x + rectangle.width);
3084    const y1 = Math.max(this.y, rectangle.y);
3085    const y2 = Math.min(this.y + this.height, rectangle.y + rectangle.height);
3086    this.x = x1;
3087    this.width = Math.max(x2 - x1, 0);
3088    this.y = y1;
3089    this.height = Math.max(y2 - y1, 0);
3090    return this;
3091  }
3092  /**
3093   * Enlarges rectangle so that its corners lie on a grid defined by resolution.
3094   * @example
3095   * ```ts
3096   * // Basic grid alignment
3097   * const rect = new Rectangle(10.2, 10.6, 100.8, 100.4);
3098   * rect.ceil(); // Aligns to whole pixels
3099   *
3100   * // Custom resolution grid
3101   * const uiRect = new Rectangle(5.3, 5.7, 50.2, 50.8);
3102   * uiRect.ceil(0.5); // Aligns to half pixels
3103   *
3104   * // Use with precision value
3105   * const preciseRect = new Rectangle(20.001, 20.999, 100.001, 100.999);
3106   * preciseRect.ceil(1, 0.01); // Handles small decimal variations
3107   * ```
3108   * @param resolution - The grid size to align to (1 = whole pixels)
3109   * @param eps - Small number to prevent floating point errors
3110   * @returns Returns itself
3111   * @see {@link Rectangle.fit} For constraining to bounds
3112   * @see {@link Rectangle.enlarge} For growing dimensions
3113   */
3114  ceil(resolution = 1, eps = 1e-3) {
3115    const x2 = Math.ceil((this.x + this.width - eps) * resolution) / resolution;
3116    const y2 = Math.ceil((this.y + this.height - eps) * resolution) / resolution;
3117    this.x = Math.floor((this.x + eps) * resolution) / resolution;
3118    this.y = Math.floor((this.y + eps) * resolution) / resolution;
3119    this.width = x2 - this.x;
3120    this.height = y2 - this.y;
3121    return this;
3122  }
3123  /**
3124   * Scales the rectangle's dimensions and position by the specified factors.
3125   * @example
3126   * ```ts
3127   * const rect = new Rectangle(50, 50, 100, 100);
3128   *
3129   * // Scale uniformly
3130   * rect.scale(0.5, 0.5);
3131   * // rect is now: x=25, y=25, width=50, height=50
3132   *
3133   * // non-uniformly
3134   * rect.scale(0.5, 1);
3135   * // rect is now: x=25, y=50, width=50, height=100
3136   * ```
3137   * @param x - The factor by which to scale the horizontal properties (x, width).
3138   * @param y - The factor by which to scale the vertical properties (y, height).
3139   * @returns Returns itself
3140   */
3141  scale(x, y = x) {
3142    this.x *= x;
3143    this.y *= y;
3144    this.width *= x;
3145    this.height *= y;
3146    return this;
3147  }
3148  /**
3149   * Enlarges this rectangle to include the passed rectangle.
3150   * @example
3151   * ```ts
3152   * // Basic enlargement
3153   * const rect = new Rectangle(50, 50, 100, 100);
3154   * const other = new Rectangle(0, 0, 200, 75);
3155   * rect.enlarge(other);
3156   * // rect is now: x=0, y=0, width=200, height=150
3157   *
3158   * // Use for bounding box calculation
3159   * const bounds = new Rectangle();
3160   * objects.forEach((obj) => {
3161   *     bounds.enlarge(obj.getBounds());
3162   * });
3163   * ```
3164   * @param rectangle - The rectangle to include
3165   * @returns Returns itself
3166   * @see {@link Rectangle.fit} For shrinking to fit within another rectangle
3167   * @see {@link Rectangle.pad} For adding padding around the rectangle
3168   */
3169  enlarge(rectangle) {
3170    const x1 = Math.min(this.x, rectangle.x);
3171    const x2 = Math.max(this.x + this.width, rectangle.x + rectangle.width);
3172    const y1 = Math.min(this.y, rectangle.y);
3173    const y2 = Math.max(this.y + this.height, rectangle.y + rectangle.height);
3174    this.x = x1;
3175    this.width = x2 - x1;
3176    this.y = y1;
3177    this.height = y2 - y1;
3178    return this;
3179  }
3180  /**
3181   * Returns the framing rectangle of the rectangle as a Rectangle object
3182   * @example
3183   * ```ts
3184   * // Basic bounds retrieval
3185   * const rect = new Rectangle(100, 100, 200, 150);
3186   * const bounds = rect.getBounds();
3187   *
3188   * // Reuse existing rectangle
3189   * const out = new Rectangle();
3190   * rect.getBounds(out);
3191   * ```
3192   * @param out - Optional rectangle to store the result
3193   * @returns The framing rectangle
3194   * @see {@link Rectangle.copyFrom} For direct copying
3195   * @see {@link Rectangle.clone} For creating new copy
3196   */
3197  getBounds(out) {
3198    out || (out = new Rectangle());
3199    out.copyFrom(this);
3200    return out;
3201  }
3202  /**
3203   * Determines whether another Rectangle is fully contained within this Rectangle.
3204   *
3205   * Rectangles that occupy the same space are considered to be containing each other.
3206   *
3207   * Rectangles without area (width or height equal to zero) can't contain anything,
3208   * not even other arealess rectangles.
3209   * @example
3210   * ```ts
3211   * // Check if one rectangle contains another
3212   * const container = new Rectangle(0, 0, 100, 100);
3213   * const inner = new Rectangle(25, 25, 50, 50);
3214   *
3215   * console.log(container.containsRect(inner)); // true
3216   *
3217   * // Check overlapping rectangles
3218   * const partial = new Rectangle(75, 75, 50, 50);
3219   * console.log(container.containsRect(partial)); // false
3220   *
3221   * // Zero-area rectangles
3221
3222   * const empty = new Rectangle(0, 0, 0, 100);
3223   * console.log(container.containsRect(empty)); // false
3224   * ```
3225   * @param other - The Rectangle to check for containment
3226   * @returns True if other is fully contained within this Rectangle
3227   * @see {@link Rectangle.contains} For point containment
3228   * @see {@link Rectangle.intersects} For overlap testing
3229   */
3230  containsRect(other) {
3231    if (this.width <= 0 || this.height <= 0)
3232      return false;
3233    const x1 = other.x;
3234    const y1 = other.y;
3235    const x2 = other.x + other.width;
3236    const y2 = other.y + other.height;
3237    return x1 >= this.x && x1 < this.x + this.width && y1 >= this.y && y1 < this.y + this.height && x2 >= this.x && x2 < this.x + this.width && y2 >= this.y && y2 < this.y + this.height;
3238  }
3239  /**
3240   * Sets the position and dimensions of the rectangle.
3241   * @example
3242   * ```ts
3243   * // Basic usage
3244   * const rect = new Rectangle();
3245   * rect.set(100, 100, 200, 150);
3246   *
3247   * // Chain with other operations
3248   * const bounds = new Rectangle()
3249   *     .set(0, 0, 100, 100)
3250   *     .pad(10);
3251   * ```
3252   * @param x - The X coordinate of the upper-left corner of the rectangle
3253   * @param y - The Y coordinate of the upper-left corner of the rectangle
3254   * @param width - The overall width of the rectangle
3255   * @param height - The overall height of the rectangle
3256   * @returns Returns itself for method chaining
3257   * @see {@link Rectangle.copyFrom} For copying from another rectangle
3258   * @see {@link Rectangle.clone} For creating a new copy
3259   */
3260  set(x, y, width, height) {
3261    this.x = x;
3262    this.y = y;
3263    this.width = width;
3264    this.height = height;
3265    return this;
3266  }
3267  toString() {
3268    return `[pixi.js/math:Rectangle x=${this.x} y=${this.y} width=${this.width} height=${this.height}]`;
3269  }
3270}
3271
3272const uidCache = {
3273  default: -1
3274};
3275function uid(name = "default") {
3276  if (uidCache[name] === void 0) {
3277    uidCache[name] = -1;
3278  }
3279  return ++uidCache[name];
3280}
3281
3282const warnings = /* @__PURE__ */ new Set();
3283const v8_0_0 = "8.0.0";
3284const v8_3_4 = "8.3.4";
3285const deprecationState = {
3286  quiet: false,
3287  noColor: false
3288};
3289const deprecation = (version, message, ignoreDepth = 3) => {
3290  if (deprecationState.quiet || warnings.has(message))
3291    return;
3292  let stack = new Error().stack;
3293  const deprecationMessage = `${message}
3294Deprecated since v${version}`;
3295  const useGroup = typeof console.groupCollapsed === "function" && !deprecationState.noColor;
3296  if (typeof stack === "undefined") {
3297    console.warn("PixiJS Deprecation Warning: ", deprecationMessage);
3298  } else {
3299    stack = stack.split("\n").splice(ignoreDepth).join("\n");
3300    if (useGroup) {
3301      console.groupCollapsed(
3302        "%cPixiJS Deprecation Warning: %c%s",
3303        "color:#614108;background:#fffbe6",
3304        "font-weight:normal;color:#614108;background:#fffbe6",
3305        deprecationMessage
3306      );
3307      console.warn(stack);
3308      console.groupEnd();
3309    } else {
3310      console.warn("PixiJS Deprecation Warning: ", deprecationMessage);
3311      console.warn(stack);
3312    }
3313  }
3314  warnings.add(message);
3315};
3316Object.defineProperties(deprecation, {
3317  quiet: {
3318    get: () => deprecationState.quiet,
3319    set: (value) => {
3320      deprecationState.quiet = value;
3321    },
3322    enumerable: true,
3323    configurable: false
3324  },
3325  noColor: {
3326    get: () => deprecationState.noColor,
3327    set: (value) => {
3328      deprecationState.noColor = value;
3329    },
3330    enumerable: true,
3331    configurable: false
3332  }
3333});
3334
3335const NOOP = () => {
3336};
3337
3338function nextPow2(v) {
3339  v += v === 0 ? 1 : 0;
3340  --v;
3341  v |= v >>> 1;
3342  v |= v >>> 2;
3343  v |= v >>> 4;
3344  v |= v >>> 8;
3345  v |= v >>> 16;
3346  return v + 1;
3347}
3348function isPow2(v) {
3349  return !(v & v - 1) && !!v;
3350}
3351
3352function definedProps(obj) {
3353  const result = {};
3354  for (const key in obj) {
3355    if (obj[key] !== void 0) {
3356      result[key] = obj[key];
3357    }
3358  }
3359  return result;
3360}
3361
3362const idHash$1 = /* @__PURE__ */ Object.create(null);
3363function createResourceIdFromString(value) {
3364  const id = idHash$1[value];
3365  if (id === void 0) {
3366    idHash$1[value] = uid("resource");
3367  }
3368  return id;
3369}
3370const _TextureStyle = class _TextureStyle extends EventEmitter {
3371  /**
3372   * @param options - options for the style
3373   */
3374  constructor(options = {}) {
3375    super();
3376    /** @internal */
3377    this._resourceType = "textureSampler";
3378    /** @internal */
3379    this._touched = 0;
3380    /**
3381     * Specifies the maximum anisotropy value clamp used by the sampler.
3382     * Note: Most implementations support {@link TextureStyle#maxAnisotropy} values in range
3383     * between 1 and 16, inclusive. The used value of {@link TextureStyle#maxAnisotropy} will
3384     * be clamped to the maximum value that the platform supports.
3385     * @internal
3386     */
3387    this._maxAnisotropy = 1;
3388    /**
3389     * Has the style been destroyed?
3390     * @readonly
3391     */
3392    this.destroyed = false;
3393    options = { ..._TextureStyle.defaultOptions, ...options };
3394    this.addressMode = options.addressMode;
3395    this.addressModeU = options.addressModeU ?? this.addressModeU;
3396    this.addressModeV = options.addressModeV ?? this.addressModeV;
3397    this.addressModeW = options.addressModeW ?? this.addressModeW;
3398    this.scaleMode = options.scaleMode;
3399    this.magFilter = options.magFilter ?? this.magFilter;
3400    this.minFilter = options.minFilter ?? this.minFilter;
3401    this.mipmapFilter = options.mipmapFilter ?? this.mipmapFilter;
3402    this.lodMinClamp = options.lodMinClamp;
3403    this.lodMaxClamp = options.lodMaxClamp;
3404    this.compare = options.compare;
3405    this.maxAnisotropy = options.maxAnisotropy ?? 1;
3406  }
3407  set addressMode(value) {
3408    this.addressModeU = value;
3409    this.addressModeV = value;
3410    this.addressModeW = value;
3411  }
3412  /** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
3413  get addressMode() {
3414    return this.addressModeU;
3415  }
3416  set wrapMode(value) {
3417    deprecation(v8_0_0, "TextureStyle.wrapMode is now TextureStyle.addressMode");
3418    this.addressMode = value;
3419  }
3420  get wrapMode() {
3421    return this.addressMode;
3422  }
3423  set scaleMode(value) {
3424    this.magFilter = value;
3425    this.minFilter = value;
3426    this.mipmapFilter = value;
3427  }
3428  /** setting this will set magFilter,minFilter and mipmapFilter all at once!  */
3429  get scaleMode() {
3430    return this.magFilter;
3431  }
3432  /** Specifies the maximum anisotropy value clamp used by the sampler. */
3433  set maxAnisotropy(value) {
3434    this._maxAnisotropy = Math.min(value, 16);
3435    if (this._maxAnisotropy > 1) {
3436      this.scaleMode = "linear";
3437    }
3438  }
3439  get maxAnisotropy() {
3440    return this._maxAnisotropy;
3441  }
3442  // TODO - move this to WebGL?
3443  get _resourceId() {
3444    return this._sharedResourceId || this._generateResourceId();
3445  }
3446  update() {
3447    this._sharedResourceId = null;
3448    this.emit("change", this);
3449  }
3450  _generateResourceId() {
3451    const bigKey = `${this.addressModeU}-${this.addressModeV}-${this.addressModeW}-${this.magFilter}-${this.minFilter}-${this.mipmapFilter}-${this.lodMinClamp}-${this.lodMaxClamp}-${this.compare}-${this._maxAnisotropy}`;
3452    this._sharedResourceId = createResourceIdFromString(bigKey);
3453    return this._resourceId;
3454  }
3455  /** Destroys the style */
3456  destroy() {
3457    this.destroyed = true;
3458    this.emit("destroy", this);
3459    this.emit("change", this);
3460    this.removeAllListeners();
3461  }
3462};
3463/** default options for the style */
3464_TextureStyle.defaultOptions = {
3465  addressMode: "clamp-to-edge",
3466  scaleMode: "linear"
3467};
3468let TextureStyle = _TextureStyle;
3469
3470const _TextureSource = class _TextureSource extends EventEmitter {
3471  /**
3472   * @param options - options for creating a new TextureSource
3473   */
3474  constructor(options = {}) {
3475    super();
3476    this.options = options;
3477    /** @internal */
3478    this._gpuData = /* @__PURE__ */ Object.create(null);
3479    /** @internal */
3480    this._gcLastUsed = -1;
3481    /** unique id for this Texture source */
3482    this.uid = uid("textureSource");
3483    /**
3484     * The resource type used by this TextureSource. This is used by the bind groups to determine
3485     * how to handle this resource.
3486     * @internal
3487     */
3488    this._resourceType = "textureSource";
3489    /**
3490     * i unique resource id, used by the bind group systems.
3491     * This can change if the texture is resized or its resource changes
3492     * @internal
3493     */
3494    this._resourceId = uid("resource");
3495    /**
3496     * this is how the backends know how to upload this texture to the GPU
3497     * It changes depending on the resource type. Classes that extend TextureSource
3498     * should override this property.
3499     * @internal
3500     */
3501    this.uploadMethodId = "unknown";
3502    /** @internal */
3503    this._resolution = 1;
3504    /** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */
3505    this.pixelWidth = 1;
3506    /** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */
3507    this.pixelHeight = 1;
3508    /**
3509     * the width of this texture source, accounting for resolution
3510     * eg pixelWidth 200, resolution 2, then width will be 100
3511     */
3512    this.width = 1;
3513    /**
3514     * the height of this texture source, accounting for resolution
3515     * eg pixelHeight 200, resolution 2, then height will be 100
3516     */
3517    this.height = 1;
3518    /**
3519     * The number of samples of a multisample texture. This is always 1 for non-multisample textures.
3520     * To enable multisample for a texture, set antialias to true
3521     * @internal
3522     */
3523    this.sampleCount = 1;
3524    /** The number of mip levels to generate for this texture. this is  overridden if autoGenerateMipmaps is true */
3525    this.mipLevelCount = 1;
3526    /**
3527     * Should we auto generate mipmaps for this texture? This will automatically generate mipmaps
3528     * for this texture when uploading to the GPU. Mipmapped textures take up more memory, but
3529     * can look better when scaled down.
3530     *
3531     * For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.
3532     * If you do, make sure to call `updateMipmaps` after you update the texture.
3533     */
3534    this.autoGenerateMipmaps = false;
3535    /** the format that the texture data has */
3536    this.format = "rgba8unorm";
3537    /** how many dimensions does this texture have? currently v8 only supports 2d */
3538    this.dimension = "2d";
3539    /**
3540     * Only really affects RenderTextures.
3541     * Should we use antialiasing for this texture. It will look better, but may impact performance as a
3542     * Blit operation will be required to resolve the texture.
3543     */
3544    this.antialias = false;
3545    /**
3546     * Used by automatic texture Garbage Collection, stores last GC tick when it was bound
3547     * @protected
3548     */
3549    this._touched = 0;
3550    /**
3551     * Used by the batcher to build texture batches. faster to have the variable here!
3552     * @protected
3553     */
3554    this._batchTick = -1;
3555    /**
3556     * A temporary batch location for the texture batching. Here for performance reasons only!
3557     * @protected
3558     */
3559    this._textureBindLocation = -1;
3560    options = { ..._TextureSource.defaultOptions, ...options };
3561    this.label = options.label ?? "";
3562    this.resource = options.resource;
3563    this.autoGarbageCollect = options.autoGarbageCollect;
3564    this._resolution = options.resolution;
3565    if (options.width) {
3566      this.pixelWidth = options.width * this._resolution;
3567    } else {
3568      this.pixelWidth = this.resource ? this.resourceWidth ?? 1 : 1;
3569    }
3570    if (options.height) {
3571      this.pixelHeight = options.height * this._resolution;
3572    } else {
3573      this.pixelHeight = this.resource ? this.resourceHeight ?? 1 : 1;
3574    }
3575    this.width = this.pixelWidth / this._resolution;
3576    this.height = this.pixelHeight / this._resolution;
3577    this.format = options.format;
3578    this.dimension = options.dimensions;
3579    this.mipLevelCount = options.mipLevelCount;
3580    this.autoGenerateMipmaps = options.autoGenerateMipmaps;
3581    this.sampleCount = options.sampleCount;
3582    this.antialias = options.antialias;
3583    this.alphaMode = options.alphaMode;
3584    this.style = new TextureStyle(definedProps(options));
3585    this.destroyed = false;
3586    this._refreshPOT();
3587  }
3588  /** returns itself */
3589  get source() {
3590    return this;
3591  }
3592  /** the style of the texture */
3593  get style() {
3594    return this._style;
3595  }
3596  set style(value) {
3597    if (this.style === value)
3598      return;
3599    this._style?.off("change", this._onStyleChange, this);
3600    this._style = value;
3601    this._style?.on("change", this._onStyleChange, this);
3602    this._onStyleChange();
3603  }
3604  /** Specifies the maximum anisotropy value clamp used by the sampler. */
3605  set maxAnisotropy(value) {
3606    this._style.maxAnisotropy = value;
3607  }
3608  get maxAnisotropy() {
3609    return this._style.maxAnisotropy;
3610  }
3611  /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */
3612  get addressMode() {
3613    return this._style.addressMode;
3614  }
3615  set addressMode(value) {
3616    this._style.addressMode = value;
3617  }
3618  /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */
3619  get repeatMode() {
3620    return this._style.addressMode;
3621  }
3622  set repeatMode(value) {
3623    this._style.addressMode = value;
3624  }
3625  /** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */
3626  get magFilter() {
3627    return this._style.magFilter;
3628  }
3629  set magFilter(value) {
3630    this._style.magFilter = value;
3631  }
3632  /** Specifies the sampling behavior when the sample footprint is larger than one texel. */
3633  get minFilter() {
3634    return this._style.minFilter;
3635  }
3636  set minFilter(value) {
3637    this._style.minFilter = value;
3638  }
3639  /** Specifies behavior for sampling between mipmap levels. */
3640  get mipmapFilter() {
3641    return this._style.mipmapFilter;
3642  }
3643  set mipmapFilter(value) {
3644    this._style.mipmapFilter = value;
3645  }
3646  /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
3647  get lodMinClamp() {
3648    return this._style.lodMinClamp;
3649  }
3650  set lodMinClamp(value) {
3651    this._style.lodMinClamp = value;
3652  }
3653  /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
3654  get lodMaxClamp() {
3655    return this._style.lodMaxClamp;
3656  }
3657  set lodMaxClamp(value) {
3658    this._style.lodMaxClamp = value;
3659  }
3660  _onStyleChange() {
vendor: 6,885 bytes, lines 3661-3858
3661    this.emit("styleChange", this);
3662  }
3663  /** call this if you have modified the texture outside of the constructor */
3664  update() {
3665    if (this.resource) {
3666      const resolution = this._resolution;
3667      const didResize = this.resize(this.resourceWidth / resolution, this.resourceHeight / resolution);
3668      if (didResize)
3669        return;
3670    }
3671    this.emit("update", this);
3672  }
3673  /** Destroys this texture source */
3674  destroy() {
3675    this.destroyed = true;
3676    this.unload();
3677    this.emit("destroy", this);
3678    if (this._style) {
3679      this._style.destroy();
3680      this._style = null;
3681    }
3682    this.uploadMethodId = null;
3683    this.resource = null;
3684    this.removeAllListeners();
3685  }
3686  /**
3687   * This will unload the Texture source from the GPU. This will free up the GPU memory
3688   * As soon as it is required fore rendering, it will be re-uploaded.
3689   */
3690  unload() {
3691    this._resourceId = uid("resource");
3692    this.emit("change", this);
3693    this.emit("unload", this);
3694    for (const key in this._gpuData) {
3695      this._gpuData[key]?.destroy?.();
3696    }
3697    this._gpuData = /* @__PURE__ */ Object.create(null);
3698  }
3699  /** the width of the resource. This is the REAL pure number, not accounting resolution   */
3700  get resourceWidth() {
3701    const { resource } = this;
3702    return resource.naturalWidth || resource.videoWidth || resource.displayWidth || resource.width;
3703  }
3704  /** the height of the resource. This is the REAL pure number, not accounting resolution */
3705  get resourceHeight() {
3706    const { resource } = this;
3707    return resource.naturalHeight || resource.videoHeight || resource.displayHeight || resource.height;
3708  }
3709  /**
3710   * the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture
3711   * but will the size of the texture when rendered.
3712   *
3713   * changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel
3714   * density will have increased)
3715   */
3716  get resolution() {
3717    return this._resolution;
3718  }
3719  set resolution(resolution) {
3720    if (this._resolution === resolution)
3721      return;
3722    this._resolution = resolution;
3723    this.width = this.pixelWidth / resolution;
3724    this.height = this.pixelHeight / resolution;
3725  }
3726  /**
3727   * Resize the texture, this is handy if you want to use the texture as a render texture
3728   * @param width - the new width of the texture
3729   * @param height - the new height of the texture
3730   * @param resolution - the new resolution of the texture
3731   * @returns - if the texture was resized
3732   */
3733  resize(width, height, resolution) {
3734    resolution || (resolution = this._resolution);
3735    width || (width = this.width);
3736    height || (height = this.height);
3737    const newPixelWidth = Math.round(width * resolution);
3738    const newPixelHeight = Math.round(height * resolution);
3739    this.width = newPixelWidth / resolution;
3740    this.height = newPixelHeight / resolution;
3741    this._resolution = resolution;
3742    if (this.pixelWidth === newPixelWidth && this.pixelHeight === newPixelHeight) {
3743      return false;
3744    }
3745    this._refreshPOT();
3746    this.pixelWidth = newPixelWidth;
3747    this.pixelHeight = newPixelHeight;
3748    this.emit("resize", this);
3749    this._resourceId = uid("resource");
3750    this.emit("change", this);
3751    return true;
3752  }
3753  /**
3754   * Lets the renderer know that this texture has been updated and its mipmaps should be re-generated.
3755   * This is only important for RenderTexture instances, as standard Texture instances will have their
3756   * mipmaps generated on upload. You should call this method after you make any change to the texture
3757   *
3758   * The reason for this is is can be quite expensive to update mipmaps for a texture. So by default,
3759   * We want you, the developer to specify when this action should happen.
3760   *
3761   * Generally you don't want to have mipmaps generated on Render targets that are changed every frame,
3762   */
3763  updateMipmaps() {
3764    if (this.autoGenerateMipmaps && this.mipLevelCount > 1) {
3765      this.emit("updateMipmaps", this);
3766    }
3767  }
3768  set wrapMode(value) {
3769    this._style.wrapMode = value;
3770  }
3771  get wrapMode() {
3772    return this._style.wrapMode;
3773  }
3774  set scaleMode(value) {
3775    this._style.scaleMode = value;
3776  }
3777  /** setting this will set magFilter,minFilter and mipmapFilter all at once!  */
3778  get scaleMode() {
3779    return this._style.scaleMode;
3780  }
3781  /**
3782   * Refresh check for isPowerOfTwo texture based on size
3783   * @private
3784   */
3785  _refreshPOT() {
3786    this.isPowerOfTwo = isPow2(this.pixelWidth) && isPow2(this.pixelHeight);
3787  }
3788  static test(_resource) {
3789    throw new Error("Unimplemented");
3790  }
3791};
3792/** The default options used when creating a new TextureSource. override these to add your own defaults */
3793_TextureSource.defaultOptions = {
3794  resolution: 1,
3795  format: "bgra8unorm",
3796  alphaMode: "premultiply-alpha-on-upload",
3797  dimensions: "2d",
3798  mipLevelCount: 1,
3799  autoGenerateMipmaps: false,
3800  sampleCount: 1,
3801  antialias: false,
3802  autoGarbageCollect: false
3803};
3804let TextureSource = _TextureSource;
3805
3806class BufferImageSource extends TextureSource {
3807  constructor(options) {
3808    const buffer = options.resource || new Float32Array(options.width * options.height * 4);
3809    let format = options.format;
3810    if (!format) {
3811      if (buffer instanceof Float32Array) {
3812        format = "rgba32float";
3813      } else if (buffer instanceof Int32Array) {
3814        format = "rgba32uint";
3815      } else if (buffer instanceof Uint32Array) {
3816        format = "rgba32uint";
3817      } else if (buffer instanceof Int16Array) {
3818        format = "rgba16uint";
3819      } else if (buffer instanceof Uint16Array) {
3820        format = "rgba16uint";
3821      } else if (buffer instanceof Int8Array) {
3822        format = "bgra8unorm";
3823      } else {
3824        format = "bgra8unorm";
3825      }
3826    }
3827    super({
3828      ...options,
3829      resource: buffer,
3830      format
3831    });
3832    this.uploadMethodId = "buffer";
3833  }
3834  static test(resource) {
3835    return resource instanceof Int8Array || resource instanceof Uint8Array || resource instanceof Uint8ClampedArray || resource instanceof Int16Array || resource instanceof Uint16Array || resource instanceof Int32Array || resource instanceof Uint32Array || resource instanceof Float32Array;
3836  }
3837}
3838BufferImageSource.extension = ExtensionType.TextureSource;
3839
3840const tempMat = new Matrix();
3841class TextureMatrix {
3842  /**
3843   * @param texture - observed texture
3844   * @param clampMargin - Changes frame clamping, 0.5 by default. Use -0.5 for extra border.
3845   */
3846  constructor(texture, clampMargin) {
3847    this.mapCoord = new Matrix();
3848    this.uClampFrame = new Float32Array(4);
3849    this.uClampOffset = new Float32Array(2);
3850    this._textureID = -1;
3851    this._updateID = 0;
3852    this.clampOffset = 0;
3853    if (typeof clampMargin === "undefined") {
3854      this.clampMargin = texture.width < 10 ? 0 : 0.5;
3855    } else {
3856      this.clampMargin = clampMargin;
3857    }
3858    this.isSimple = false;
3859    this.texture = texture;
3860  }
3861  /** Texture property. */
3862  get texture() {
3863    return this._texture;
3864  }
3865  set texture(value) {
3866    if (this.texture === value)
3867      return;
3868    this._texture?.removeListener("update", this.update, this);
3869    this._texture = value;
3870    this._texture.addListener("update", this.update, this);
3871    this.update();
3872  }
3873  /**
3874   * Multiplies uvs array to transform
3875   * @param uvs - mesh uvs
3876   * @param [out=uvs] - output
3877   * @returns - output
3878   */
3879  multiplyUvs(uvs, out) {
3880    if (out === void 0) {
3881      out = uvs;
3882    }
3883    const mat = this.mapCoord;
3884    for (let i = 0; i < uvs.length; i += 2) {
3885      const x = uvs[i];
3886      const y = uvs[i + 1];
3887      out[i] = x * mat.a + y * mat.c + mat.tx;
3888      out[i + 1] = x * mat.b + y * mat.d + mat.ty;
3889    }
3890    return out;
3891  }
3892  /**
3893   * Updates matrices if texture was changed
3894   * @returns - whether or not it was updated
3895   */
3896  update() {
3897    const tex = this._texture;
3898    this._updateID++;
3899    const uvs = tex.uvs;
3900    this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0);
3901    const orig = tex.orig;
3902    const trim = tex.trim;
3903    if (trim) {
3904      tempMat.set(
3905        orig.width / trim.width,
3906        0,
3907        0,
3908        orig.height / trim.height,
3909        -trim.x / trim.width,
3910        -trim.y / trim.height
3911      );
3912      this.mapCoord.append(tempMat);
3913    }
3914    const texBase = tex.source;
3915    const frame = this.uClampFrame;
3916    const margin = this.clampMargin / texBase._resolution;
3917    const offset = this.clampOffset / texBase._resolution;
3918    frame[0] = (tex.frame.x + margin + offset) / texBase.width;
3919    frame[1] = (tex.frame.y + margin + offset) / texBase.height;
3920    frame[2] = (tex.frame.x + tex.frame.width - margin + offset) / texBase.width;
3921    frame[3] = (tex.frame.y + tex.frame.height - margin + offset) / texBase.height;
3922    this.uClampOffset[0] = this.clampOffset / texBase.pixelWidth;
3923    this.uClampOffset[1] = this.clampOffset / texBase.pixelHeight;
3924    this.isSimple = tex.frame.width === texBase.width && tex.frame.height === texBase.height && tex.rotate === 0;
3925    return true;
3926  }
3927}
3928
3929class Texture extends EventEmitter {
3930  /**
3931   * @param {TextureOptions} options - Options for the texture
3932   */
3933  constructor({
3934    source,
3935    label,
3936    frame,
3937    orig,
3938    trim,
3939    defaultAnchor,
3940    defaultBorders,
3941    rotate,
3942    dynamic
3943  } = {}) {
3944    super();
3945    /** unique id for this texture */
3946    this.uid = uid("texture");
3947    /** A uvs object based on the given frame and the texture source */
3948    this.uvs = { x0: 0, y0: 0, x1: 0, y1: 0, x2: 0, y2: 0, x3: 0, y3: 0 };
3949    /**
3950     * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,
3951     * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)
3952     */
3953    this.frame = new Rectangle();
3954    /**
3955     * Does this Texture have any frame data assigned to it?
3956     *
3957     * This mode is enabled automatically if no frame was passed inside constructor.
3958     *
3959     * In this mode texture is subscribed to baseTexture events, and fires `update` on any change.
3960     *
3961     * Beware, after loading or resize of baseTexture event can fired two times!
3962     * If you want more control, subscribe on baseTexture itself.
3963     * @example
3964     * texture.on('update', () => {});
3965     */
3966    this.noFrame = false;
3967    /**
3968     * Set to true if you plan on modifying the uvs of this texture.
3969     * When this is the case, sprites and other objects using the texture will
3970     * make sure to listen for changes to the uvs and update their vertices accordingly.
3971     */
3972    this.dynamic = false;
3973    /** is it a texture? yes! used for type checking */
3974    this.isTexture = true;
3975    this.label = label;
3976    this.source = source?.source ?? new TextureSource();
3977    this.noFrame = !frame;
3978    if (frame) {
3979      this.frame.copyFrom(frame);
3980    } else {
3981      const { width, height } = this._source;
3982      this.frame.width = width;
3983      this.frame.height = height;
3984    }
3985    this.orig = orig || this.frame;
3986    this.trim = trim;
3987    this.rotate = rotate ?? 0;
3988    this.defaultAnchor = defaultAnchor;
3989    this.defaultBorders = defaultBorders;
3990    this.destroyed = false;
vendor: 17,032 bytes, lines 3991-4555
3991    this.dynamic = dynamic || false;
3992    this.updateUvs();
3993  }
3994  set source(value) {
3995    if (this._source) {
3996      this._source.off("resize", this.update, this);
3997    }
3998    this._source = value;
3999    value.on("resize", this.update, this);
4000    this.emit("update", this);
4001  }
4002  /** the underlying source of the texture (equivalent of baseTexture in v7) */
4003  get source() {
4004    return this._source;
4005  }
4006  /** returns a TextureMatrix instance for this texture. By default, that object is not created because its heavy. */
4007  get textureMatrix() {
4008    if (!this._textureMatrix) {
4009      this._textureMatrix = new TextureMatrix(this);
4010    }
4011    return this._textureMatrix;
4012  }
4013  /** The width of the Texture in pixels. */
4014  get width() {
4015    return this.orig.width;
4016  }
4017  /** The height of the Texture in pixels. */
4018  get height() {
4019    return this.orig.height;
4020  }
4021  /** Call this function when you have modified the frame of this texture. */
4022  updateUvs() {
4023    const { uvs, frame } = this;
4024    const { width, height } = this._source;
4025    const nX = frame.x / width;
4026    const nY = frame.y / height;
4027    const nW = frame.width / width;
4028    const nH = frame.height / height;
4029    let rotate = this.rotate;
4030    if (rotate) {
4031      const w2 = nW / 2;
4032      const h2 = nH / 2;
4033      const cX = nX + w2;
4034      const cY = nY + h2;
4035      rotate = groupD8.add(rotate, groupD8.NW);
4036      uvs.x0 = cX + w2 * groupD8.uX(rotate);
4037      uvs.y0 = cY + h2 * groupD8.uY(rotate);
4038      rotate = groupD8.add(rotate, 2);
4039      uvs.x1 = cX + w2 * groupD8.uX(rotate);
4040      uvs.y1 = cY + h2 * groupD8.uY(rotate);
4041      rotate = groupD8.add(rotate, 2);
4042      uvs.x2 = cX + w2 * groupD8.uX(rotate);
4043      uvs.y2 = cY + h2 * groupD8.uY(rotate);
4044      rotate = groupD8.add(rotate, 2);
4045      uvs.x3 = cX + w2 * groupD8.uX(rotate);
4046      uvs.y3 = cY + h2 * groupD8.uY(rotate);
4047    } else {
4048      uvs.x0 = nX;
4049      uvs.y0 = nY;
4050      uvs.x1 = nX + nW;
4051      uvs.y1 = nY;
4052      uvs.x2 = nX + nW;
4053      uvs.y2 = nY + nH;
4054      uvs.x3 = nX;
4055      uvs.y3 = nY + nH;
4056    }
4057  }
4058  /**
4059   * Destroys this texture
4060   * @param destroySource - Destroy the source when the texture is destroyed.
4061   */
4062  destroy(destroySource = false) {
4063    if (this._source) {
4064      this._source.off("resize", this.update, this);
4065      if (destroySource) {
4066        this._source.destroy();
4067        this._source = null;
4068      }
4069    }
4070    this._textureMatrix = null;
4071    this.destroyed = true;
4072    this.emit("destroy", this);
4073    this.removeAllListeners();
4074  }
4075  /**
4076   * Call this if you have modified the `texture outside` of the constructor.
4077   *
4078   * If you have modified this texture's source, you must separately call `texture.source.update()` to see those changes.
4079   */
4080  update() {
4081    if (this.noFrame) {
4082      this.frame.width = this._source.width;
4083      this.frame.height = this._source.height;
4084    }
4085    this.updateUvs();
4086    this.emit("update", this);
4087  }
4088  /** @deprecated since 8.0.0 */
4089  get baseTexture() {
4090    deprecation(v8_0_0, "Texture.baseTexture is now Texture.source");
4091    return this._source;
4092  }
4093}
4094Texture.EMPTY = new Texture({
4095  label: "EMPTY",
4096  source: new TextureSource({
4097    label: "EMPTY"
4098  })
4099});
4100Texture.EMPTY.destroy = NOOP;
4101Texture.WHITE = new Texture({
4102  source: new BufferImageSource({
4103    resource: new Uint8Array([255, 255, 255, 255]),
4104    width: 1,
4105    height: 1,
4106    alphaMode: "premultiply-alpha-on-upload",
4107    label: "WHITE"
4108  }),
4109  label: "WHITE"
4110});
4111Texture.WHITE.destroy = NOOP;
4112
4113function updateQuadBounds(bounds, anchor, texture) {
4114  const { width, height } = texture.orig;
4115  const trim = texture.trim;
4116  if (trim) {
4117    const sourceWidth = trim.width;
4118    const sourceHeight = trim.height;
4119    bounds.minX = trim.x - anchor._x * width;
4120    bounds.maxX = bounds.minX + sourceWidth;
4121    bounds.minY = trim.y - anchor._y * height;
4122    bounds.maxY = bounds.minY + sourceHeight;
4123  } else {
4124    bounds.minX = -anchor._x * width;
4125    bounds.maxX = bounds.minX + width;
4126    bounds.minY = -anchor._y * height;
4127    bounds.maxY = bounds.minY + height;
4128  }
4129}
4130
4131const defaultMatrix = new Matrix();
4132class Bounds {
4133  /**
4134   * Creates a new Bounds object.
4135   * @param minX - The minimum X coordinate of the bounds.
4136   * @param minY - The minimum Y coordinate of the bounds.
4137   * @param maxX - The maximum X coordinate of the bounds.
4138   * @param maxY - The maximum Y coordinate of the bounds.
4139   */
4140  constructor(minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity) {
4141    /**
4142     * The minimum X coordinate of the bounds.
4143     * Represents the leftmost edge of the bounding box.
4144     * @example
4145     * ```ts
4146     * const bounds = new Bounds();
4147     * // Set left edge
4148     * bounds.minX = 100;
4149     * ```
4150     * @default Infinity
4151     */
4152    this.minX = Infinity;
4153    /**
4154     * The minimum Y coordinate of the bounds.
4155     * Represents the topmost edge of the bounding box.
4156     * @example
4157     * ```ts
4158     * const bounds = new Bounds();
4159     * // Set top edge
4160     * bounds.minY = 100;
4161     * ```
4162     * @default Infinity
4163     */
4164    this.minY = Infinity;
4165    /**
4166     * The maximum X coordinate of the bounds.
4167     * Represents the rightmost edge of the bounding box.
4168     * @example
4169     * ```ts
4170     * const bounds = new Bounds();
4171     * // Set right edge
4172     * bounds.maxX = 200;
4173     * // Get width
4174     * const width = bounds.maxX - bounds.minX;
4175     * ```
4176     * @default -Infinity
4177     */
4178    this.maxX = -Infinity;
4179    /**
4180     * The maximum Y coordinate of the bounds.
4181     * Represents the bottommost edge of the bounding box.
4182     * @example
4183     * ```ts
4184     * const bounds = new Bounds();
4185     * // Set bottom edge
4186     * bounds.maxY = 200;
4187     * // Get height
4188     * const height = bounds.maxY - bounds.minY;
4189     * ```
4190     * @default -Infinity
4191     */
4192    this.maxY = -Infinity;
4193    /**
4194     * The transformation matrix applied to this bounds object.
4195     * Used when calculating bounds with transforms.
4196     * @example
4197     * ```ts
4198     * const bounds = new Bounds();
4199     *
4200     * // Apply translation matrix
4201     * bounds.matrix = new Matrix()
4202     *     .translate(100, 100);
4203     *
4204     * // Combine transformations
4205     * bounds.matrix = new Matrix()
4206     *     .translate(50, 50)
4207     *     .rotate(Math.PI / 4)
4208     *     .scale(2, 2);
4209     *
4210     * // Use in bounds calculations
4211     * bounds.addFrame(0, 0, 100, 100); // Uses current matrix
4212     * bounds.addFrame(0, 0, 100, 100, customMatrix); // Override matrix
4213     * ```
4214     * @advanced
4215     */
4216    this.matrix = defaultMatrix;
4217    this.minX = minX;
4218    this.minY = minY;
4219    this.maxX = maxX;
4220    this.maxY = maxY;
4221  }
4222  /**
4223   * Checks if bounds are empty, meaning either width or height is zero or negative.
4224   * Empty bounds occur when min values exceed max values on either axis.
4225   * @example
4226   * ```ts
4227   * const bounds = new Bounds();
4228   *
4229   * // Check if newly created bounds are empty
4230   * console.log(bounds.isEmpty()); // true, default bounds are empty
4231   *
4232   * // Add frame and check again
4233   * bounds.addFrame(0, 0, 100, 100);
4234   * console.log(bounds.isEmpty()); // false, bounds now have area
4235   *
4236   * // Clear bounds
4237   * bounds.clear();
4238   * console.log(bounds.isEmpty()); // true, bounds are empty again
4239   * ```
4240   * @returns True if bounds are empty (have no area)
4241   * @see {@link Bounds#clear} For resetting bounds
4242   * @see {@link Bounds#isValid} For checking validity
4243   */
4244  isEmpty() {
4245    return this.minX > this.maxX || this.minY > this.maxY;
4246  }
4247  /**
4248   * The bounding rectangle representation of these bounds.
4249   * Lazily creates and updates a Rectangle instance based on the current bounds.
4250   * @example
4251   * ```ts
4252   * const bounds = new Bounds(0, 0, 100, 100);
4253   *
4254   * // Get rectangle representation
4255   * const rect = bounds.rectangle;
4256   * console.log(rect.x, rect.y, rect.width, rect.height);
4257   *
4258   * // Use for hit testing
4259   * if (bounds.rectangle.contains(mouseX, mouseY)) {
4260   *     console.log('Mouse is inside bounds!');
4261   * }
4262   * ```
4263   * @see {@link Rectangle} For rectangle methods
4264   * @see {@link Bounds.isEmpty} For bounds validation
4265   */
4266  get rectangle() {
4267    if (!this._rectangle) {
4268      this._rectangle = new Rectangle();
4269    }
4270    const rectangle = this._rectangle;
4271    if (this.minX > this.maxX || this.minY > this.maxY) {
4272      rectangle.x = 0;
4273      rectangle.y = 0;
4274      rectangle.width = 0;
4275      rectangle.height = 0;
4276    } else {
4277      rectangle.copyFromBounds(this);
4278    }
4279    return rectangle;
4280  }
4281  /**
4282   * Clears the bounds and resets all coordinates to their default values.
4283   * Resets the transformation matrix back to identity.
4284   * @example
4285   * ```ts
4286   * const bounds = new Bounds(0, 0, 100, 100);
4287   * console.log(bounds.isEmpty()); // false
4288   * // Clear the bounds
4289   * bounds.clear();
4290   * console.log(bounds.isEmpty()); // true
4291   * ```
4292   * @returns This bounds object for chaining
4293   */
4294  clear() {
4295    this.minX = Infinity;
4296    this.minY = Infinity;
4297    this.maxX = -Infinity;
4298    this.maxY = -Infinity;
4299    this.matrix = defaultMatrix;
4300    return this;
4301  }
4302  /**
4303   * Sets the bounds directly using coordinate values.
4304   * Provides a way to set all bounds values at once.
4305   * @example
4306   * ```ts
4307   * const bounds = new Bounds();
4308   * bounds.set(0, 0, 100, 100);
4309   * ```
4310   * @param x0 - Left X coordinate of frame
4311   * @param y0 - Top Y coordinate of frame
4312   * @param x1 - Right X coordinate of frame
4313   * @param y1 - Bottom Y coordinate of frame
4314   * @see {@link Bounds#addFrame} For matrix-aware bounds setting
4315   * @see {@link Bounds#clear} For resetting bounds
4316   */
4317  set(x0, y0, x1, y1) {
4318    this.minX = x0;
4319    this.minY = y0;
4320    this.maxX = x1;
4321    this.maxY = y1;
4322  }
4323  /**
4324   * Adds a rectangular frame to the bounds, optionally transformed by a matrix.
4325   * Updates the bounds to encompass the new frame coordinates.
4326   * @example
4327   * ```ts
4328   * const bounds = new Bounds();
4329   * bounds.addFrame(0, 0, 100, 100);
4330   *
4331   * // Add transformed frame
4332   * const matrix = new Matrix()
4333   *     .translate(50, 50)
4334   *     .rotate(Math.PI / 4);
4335   * bounds.addFrame(0, 0, 100, 100, matrix);
4336   * ```
4337   * @param x0 - Left X coordinate of frame
4338   * @param y0 - Top Y coordinate of frame
4339   * @param x1 - Right X coordinate of frame
4340   * @param y1 - Bottom Y coordinate of frame
4341   * @param matrix - Optional transformation matrix
4342   * @see {@link Bounds#addRect} For adding Rectangle objects
4343   * @see {@link Bounds#addBounds} For adding other Bounds
4344   */
4345  addFrame(x0, y0, x1, y1, matrix) {
4346    matrix || (matrix = this.matrix);
4347    const a = matrix.a;
4348    const b = matrix.b;
4349    const c = matrix.c;
4350    const d = matrix.d;
4351    const tx = matrix.tx;
4352    const ty = matrix.ty;
4353    let minX = this.minX;
4354    let minY = this.minY;
4355    let maxX = this.maxX;
4356    let maxY = this.maxY;
4357    let x = a * x0 + c * y0 + tx;
4358    let y = b * x0 + d * y0 + ty;
4359    if (x < minX)
4360      minX = x;
4361    if (y < minY)
4362      minY = y;
4363    if (x > maxX)
4364      maxX = x;
4365    if (y > maxY)
4366      maxY = y;
4367    x = a * x1 + c * y0 + tx;
4368    y = b * x1 + d * y0 + ty;
4369    if (x < minX)
4370      minX = x;
4371    if (y < minY)
4372      minY = y;
4373    if (x > maxX)
4374      maxX = x;
4375    if (y > maxY)
4376      maxY = y;
4377    x = a * x0 + c * y1 + tx;
4378    y = b * x0 + d * y1 + ty;
4379    if (x < minX)
4380      minX = x;
4381    if (y < minY)
4382      minY = y;
4383    if (x > maxX)
4384      maxX = x;
4385    if (y > maxY)
4386      maxY = y;
4387    x = a * x1 + c * y1 + tx;
4388    y = b * x1 + d * y1 + ty;
4389    if (x < minX)
4390      minX = x;
4391    if (y < minY)
4392      minY = y;
4393    if (x > maxX)
4394      maxX = x;
4395    if (y > maxY)
4396      maxY = y;
4397    this.minX = minX;
4398    this.minY = minY;
4399    this.maxX = maxX;
4400    this.maxY = maxY;
4401  }
4402  /**
4403   * Adds a rectangle to the bounds, optionally transformed by a matrix.
4404   * Updates the bounds to encompass the given rectangle.
4405   * @example
4406   * ```ts
4407   * const bounds = new Bounds();
4408   * // Add simple rectangle
4409   * const rect = new Rectangle(0, 0, 100, 100);
4410   * bounds.addRect(rect);
4411   *
4412   * // Add transformed rectangle
4413   * const matrix = new Matrix()
4414   *     .translate(50, 50)
4415   *     .rotate(Math.PI / 4);
4416   * bounds.addRect(rect, matrix);
4417   * ```
4418   * @param rect - The rectangle to be added
4419   * @param matrix - Optional transformation matrix
4420   * @see {@link Bounds#addFrame} For adding raw coordinates
4421   * @see {@link Bounds#addBounds} For adding other bounds
4422   */
4423  addRect(rect, matrix) {
4424    this.addFrame(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, matrix);
4425  }
4426  /**
4427   * Adds another bounds object to this one, optionally transformed by a matrix.
4428   * Expands the bounds to include the given bounds' area.
4429   * @example
4430   * ```ts
4431   * const bounds = new Bounds();
4432   *
4433   * // Add child bounds
4434   * const childBounds = sprite.getBounds();
4435   * bounds.addBounds(childBounds);
4436   *
4437   * // Add transformed bounds
4438   * const matrix = new Matrix()
4439   *     .scale(2, 2);
4440   * bounds.addBounds(childBounds, matrix);
4441   * ```
4442   * @param bounds - The bounds to be added
4443   * @param matrix - Optional transformation matrix
4444   * @see {@link Bounds#addFrame} For adding raw coordinates
4445   * @see {@link Bounds#addRect} For adding rectangles
4446   */
4447  addBounds(bounds, matrix) {
4448    this.addFrame(bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, matrix);
4449  }
4450  /**
4451   * Adds other Bounds as a mask, creating an intersection of the two bounds.
4452   * Only keeps the overlapping region between current bounds and mask bounds.
4453   * @example
4454   * ```ts
4455   * const bounds = new Bounds(0, 0, 100, 100);
4456   * // Create mask bounds
4457   * const mask = new Bounds();
4458   * mask.addFrame(50, 50, 150, 150);
4459   * // Apply mask - results in bounds of (50,50,100,100)
4460   * bounds.addBoundsMask(mask);
4461   * ```
4462   * @param mask - The Bounds to use as a mask
4463   * @see {@link Bounds#addBounds} For union operation
4464   * @see {@link Bounds#fit} For fitting to rectangle
4465   */
4466  addBoundsMask(mask) {
4467    this.minX = this.minX > mask.minX ? this.minX : mask.minX;
4468    this.minY = this.minY > mask.minY ? this.minY : mask.minY;
4469    this.maxX = this.maxX < mask.maxX ? this.maxX : mask.maxX;
4470    this.maxY = this.maxY < mask.maxY ? this.maxY : mask.maxY;
4471  }
4472  /**
4473   * Applies a transformation matrix to the bounds, updating its coordinates.
4474   * Transforms all corners of the bounds using the given matrix.
4475   * @example
4476   * ```ts
4477   * const bounds = new Bounds(0, 0, 100, 100);
4478   * // Apply translation
4479   * const translateMatrix = new Matrix()
4480   *     .translate(50, 50);
4481   * bounds.applyMatrix(translateMatrix);
4482   * ```
4483   * @param matrix - The matrix to apply to the bounds
4484   * @see {@link Matrix} For matrix operations
4485   * @see {@link Bounds#addFrame} For adding transformed frames
4486   */
4487  applyMatrix(matrix) {
4488    const minX = this.minX;
4489    const minY = this.minY;
4490    const maxX = this.maxX;
4491    const maxY = this.maxY;
4492    const { a, b, c, d, tx, ty } = matrix;
4493    let x = a * minX + c * minY + tx;
4494    let y = b * minX + d * minY + ty;
4495    this.minX = x;
4496    this.minY = y;
4497    this.maxX = x;
4498    this.maxY = y;
4499    x = a * maxX + c * minY + tx;
4500    y = b * maxX + d * minY + ty;
4501    this.minX = x < this.minX ? x : this.minX;
4502    this.minY = y < this.minY ? y : this.minY;
4503    this.maxX = x > this.maxX ? x : this.maxX;
4504    this.maxY = y > this.maxY ? y : this.maxY;
4505    x = a * minX + c * maxY + tx;
4506    y = b * minX + d * maxY + ty;
4507    this.minX = x < this.minX ? x : this.minX;
4508    this.minY = y < this.minY ? y : this.minY;
4509    this.maxX = x > this.maxX ? x : this.maxX;
4510    this.maxY = y > this.maxY ? y : this.maxY;
4511    x = a * maxX + c * maxY + tx;
4512    y = b * maxX + d * maxY + ty;
4513    this.minX = x < this.minX ? x : this.minX;
4514    this.minY = y < this.minY ? y : this.minY;
4515    this.maxX = x > this.maxX ? x : this.maxX;
4516    this.maxY = y > this.maxY ? y : this.maxY;
4517  }
4518  /**
4519   * Resizes the bounds object to fit within the given rectangle.
4520   * Clips the bounds if they extend beyond the rectangle's edges.
4521   * @example
4522   * ```ts
4523   * const bounds = new Bounds(0, 0, 200, 200);
4524   * // Fit within viewport
4525   * const viewport = new Rectangle(50, 50, 100, 100);
4526   * bounds.fit(viewport);
4527   * // bounds are now (50, 50, 150, 150)
4528   * ```
4529   * @param rect - The rectangle to fit within
4530   * @returns This bounds object for chaining
4531   * @see {@link Bounds#addBoundsMask} For intersection
4532   * @see {@link Bounds#pad} For expanding bounds
4533   */
4534  fit(rect) {
4535    if (this.minX < rect.left)
4536      this.minX = rect.left;
4537    if (this.maxX > rect.right)
4538      this.maxX = rect.right;
4539    if (this.minY < rect.top)
4540      this.minY = rect.top;
4541    if (this.maxY > rect.bottom)
4542      this.maxY = rect.bottom;
4543    return this;
4544  }
4545  /**
4546   * Resizes the bounds object to include the given bounds.
4547   * Similar to fit() but works with raw coordinate values instead of a Rectangle.
4548   * @example
4549   * ```ts
4550   * const bounds = new Bounds(0, 0, 200, 200);
4551   * // Fit to specific coordinates
4552   * bounds.fitBounds(50, 150, 50, 150);
4553   * // bounds are now (50, 50, 150, 150)
4554   * ```
4555   * @param left - The left value of the bounds
4556   * @param right - The right value of the bounds
4557   * @param top - The top value of the bounds
4558   * @param bottom - The bottom value of the bounds
4559   * @returns This bounds object for chaining
4560   * @see {@link Bounds#fit} For fitting to Rectangle
4561   * @see {@link Bounds#addBoundsMask} For intersection
4562   */
4563  fitBounds(left, right, top, bottom) {
4564    if (this.minX < left)
4565      this.minX = left;
4566    if (this.maxX > right)
4567      this.maxX = right;
4568    if (this.minY < top)
4569      this.minY = top;
4570    if (this.maxY > bottom)
4571     
vendor: 11,590 bytes, lines 4571-4975
4571 this.maxY = bottom;
4572    return this;
4573  }
4574  /**
4575   * Pads bounds object, making it grow in all directions.
4576   * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.
4577   * @example
4578   * ```ts
4579   * const bounds = new Bounds(0, 0, 100, 100);
4580   *
4581   * // Add equal padding
4582   * bounds.pad(10);
4583   * // bounds are now (-10, -10, 110, 110)
4584   *
4585   * // Add different padding for x and y
4586   * bounds.pad(20, 10);
4587   * // bounds are now (-30, -20, 130, 120)
4588   * ```
4589   * @param paddingX - The horizontal padding amount
4590   * @param paddingY - The vertical padding amount
4591   * @returns This bounds object for chaining
4592   * @see {@link Bounds#fit} For constraining bounds
4593   * @see {@link Bounds#scale} For uniform scaling
4594   */
4595  pad(paddingX, paddingY = paddingX) {
4596    this.minX -= paddingX;
4597    this.maxX += paddingX;
4598    this.minY -= paddingY;
4599    this.maxY += paddingY;
4600    return this;
4601  }
4602  /**
4603   * Ceils the bounds by rounding up max values and rounding down min values.
4604   * Useful for pixel-perfect calculations and avoiding fractional pixels.
4605   * @example
4606   * ```ts
4607   * const bounds = new Bounds();
4608   * bounds.set(10.2, 10.9, 50.1, 50.8);
4609   *
4610   * // Round to whole pixels
4611   * bounds.ceil();
4612   * // bounds are now (10, 10, 51, 51)
4613   * ```
4614   * @returns This bounds object for chaining
4615   * @see {@link Bounds#scale} For size adjustments
4616   * @see {@link Bounds#fit} For constraining bounds
4617   */
4618  ceil() {
4619    this.minX = Math.floor(this.minX);
4620    this.minY = Math.floor(this.minY);
4621    this.maxX = Math.ceil(this.maxX);
4622    this.maxY = Math.ceil(this.maxY);
4623    return this;
4624  }
4625  /**
4626   * Creates a new Bounds instance with the same values.
4627   * @example
4628   * ```ts
4629   * const bounds = new Bounds(0, 0, 100, 100);
4630   *
4631   * // Create a copy
4632   * const copy = bounds.clone();
4633   *
4634   * // Original and copy are independent
4635   * bounds.pad(10);
4636   * console.log(copy.width === bounds.width); // false
4637   * ```
4638   * @returns A new Bounds instance with the same values
4639   * @see {@link Bounds#copyFrom} For reusing existing bounds
4640   */
4641  clone() {
4642    return new Bounds(this.minX, this.minY, this.maxX, this.maxY);
4643  }
4644  /**
4645   * Scales the bounds by the given values, adjusting all edges proportionally.
4646   * @example
4647   * ```ts
4648   * const bounds = new Bounds(0, 0, 100, 100);
4649   *
4650   * // Scale uniformly
4651   * bounds.scale(2);
4652   * // bounds are now (0, 0, 200, 200)
4653   *
4654   * // Scale non-uniformly
4655   * bounds.scale(0.5, 2);
4656   * // bounds are now (0, 0, 100, 400)
4657   * ```
4658   * @param x - The X value to scale by
4659   * @param y - The Y value to scale by (defaults to x)
4660   * @returns This bounds object for chaining
4661   * @see {@link Bounds#pad} For adding padding
4662   * @see {@link Bounds#fit} For constraining size
4663   */
4664  scale(x, y = x) {
4665    this.minX *= x;
4666    this.minY *= y;
4667    this.maxX *= x;
4668    this.maxY *= y;
4669    return this;
4670  }
4671  /**
4672   * The x position of the bounds in local space.
4673   * Setting this value will move the bounds while maintaining its width.
4674   * @example
4675   * ```ts
4676   * const bounds = new Bounds(0, 0, 100, 100);
4677   * // Get x position
4678   * console.log(bounds.x); // 0
4679   *
4680   * // Move bounds horizontally
4681   * bounds.x = 50;
4682   * console.log(bounds.minX, bounds.maxX); // 50, 150
4683   *
4684   * // Width stays the same
4685   * console.log(bounds.width); // Still 100
4686   * ```
4687   */
4688  get x() {
4689    return this.minX;
4690  }
4691  set x(value) {
4692    const width = this.maxX - this.minX;
4693    this.minX = value;
4694    this.maxX = value + width;
4695  }
4696  /**
4697   * The y position of the bounds in local space.
4698   * Setting this value will move the bounds while maintaining its height.
4699   * @example
4700   * ```ts
4701   * const bounds = new Bounds(0, 0, 100, 100);
4702   * // Get y position
4703   * console.log(bounds.y); // 0
4704   *
4705   * // Move bounds vertically
4706   * bounds.y = 50;
4707   * console.log(bounds.minY, bounds.maxY); // 50, 150
4708   *
4709   * // Height stays the same
4710   * console.log(bounds.height); // Still 100
4711   * ```
4712   */
4713  get y() {
4714    return this.minY;
4715  }
4716  set y(value) {
4717    const height = this.maxY - this.minY;
4718    this.minY = value;
4719    this.maxY = value + height;
4720  }
4721  /**
4722   * The width value of the bounds.
4723   * Represents the distance between minX and maxX coordinates.
4724   * @example
4725   * ```ts
4726   * const bounds = new Bounds(0, 0, 100, 100);
4727   * // Get width
4728   * console.log(bounds.width); // 100
4729   * // Resize width
4730   * bounds.width = 200;
4731   * console.log(bounds.maxX - bounds.minX); // 200
4732   * ```
4733   */
4734  get width() {
4735    return this.maxX - this.minX;
4736  }
4737  set width(value) {
4738    this.maxX = this.minX + value;
4739  }
4740  /**
4741   * The height value of the bounds.
4742   * Represents the distance between minY and maxY coordinates.
4743   * @example
4744   * ```ts
4745   * const bounds = new Bounds(0, 0, 100, 100);
4746   * // Get height
4747   * console.log(bounds.height); // 100
4748   * // Resize height
4749   * bounds.height = 150;
4750   * console.log(bounds.maxY - bounds.minY); // 150
4751   * ```
4752   */
4753  get height() {
4754    return this.maxY - this.minY;
4755  }
4756  set height(value) {
4757    this.maxY = this.minY + value;
4758  }
4759  /**
4760   * The left edge coordinate of the bounds.
4761   * Alias for minX.
4762   * @example
4763   * ```ts
4764   * const bounds = new Bounds(50, 0, 150, 100);
4765   * console.log(bounds.left); // 50
4766   * console.log(bounds.left === bounds.minX); // true
4767   * ```
4768   * @readonly
4769   */
4770  get left() {
4771    return this.minX;
4772  }
4773  /**
4774   * The right edge coordinate of the bounds.
4775   * Alias for maxX.
4776   * @example
4777   * ```ts
4778   * const bounds = new Bounds(0, 0, 100, 100);
4779   * console.log(bounds.right); // 100
4780   * console.log(bounds.right === bounds.maxX); // true
4781   * ```
4782   * @readonly
4783   */
4784  get right() {
4785    return this.maxX;
4786  }
4787  /**
4788   * The top edge coordinate of the bounds.
4789   * Alias for minY.
4790   * @example
4791   * ```ts
4792   * const bounds = new Bounds(0, 25, 100, 125);
4793   * console.log(bounds.top); // 25
4794   * console.log(bounds.top === bounds.minY); // true
4795   * ```
4796   * @readonly
4797   */
4798  get top() {
4799    return this.minY;
4800  }
4801  /**
4802   * The bottom edge coordinate of the bounds.
4803   * Alias for maxY.
4804   * @example
4805   * ```ts
4806   * const bounds = new Bounds(0, 0, 100, 200);
4807   * console.log(bounds.bottom); // 200
4808   * console.log(bounds.bottom === bounds.maxY); // true
4809   * ```
4810   * @readonly
4811   */
4812  get bottom() {
4813    return this.maxY;
4814  }
4815  /**
4816   * Whether the bounds has positive width and height.
4817   * Checks if both dimensions are greater than zero.
4818   * @example
4819   * ```ts
4820   * const bounds = new Bounds(0, 0, 100, 100);
4821   * // Check if bounds are positive
4822   * console.log(bounds.isPositive); // true
4823   *
4824   * // Negative bounds
4825   * bounds.maxX = bounds.minX;
4826   * console.log(bounds.isPositive); // false, width is 0
4827   * ```
4828   * @readonly
4829   * @see {@link Bounds#isEmpty} For checking empty state
4830   * @see {@link Bounds#isValid} For checking validity
4831   */
4832  get isPositive() {
4833    return this.maxX - this.minX > 0 && this.maxY - this.minY > 0;
4834  }
4835  /**
4836   * Whether the bounds has valid coordinates.
4837   * Checks if the bounds has been initialized with real values.
4838   * @example
4839   * ```ts
4840   * const bounds = new Bounds();
4841   * console.log(bounds.isValid); // false, default state
4842   *
4843   * // Set valid bounds
4844   * bounds.addFrame(0, 0, 100, 100);
4845   * console.log(bounds.isValid); // true
4846   * ```
4847   * @readonly
4848   * @see {@link Bounds#isEmpty} For checking empty state
4849   * @see {@link Bounds#isPositive} For checking dimensions
4850   */
4851  get isValid() {
4852    return this.minX + this.minY !== Infinity;
4853  }
4854  /**
4855   * Adds vertices from a Float32Array to the bounds, optionally transformed by a matrix.
4856   * Used for efficiently updating bounds from raw vertex data.
4857   * @example
4858   * ```ts
4859   * const bounds = new Bounds();
4860   *
4861   * // Add vertices from geometry
4862   * const vertices = new Float32Array([
4863   *     0, 0,    // Vertex 1
4864   *     100, 0,  // Vertex 2
4865   *     100, 100 // Vertex 3
4866   * ]);
4867   * bounds.addVertexData(vertices, 0, 6);
4868   *
4869   * // Add transformed vertices
4870   * const matrix = new Matrix()
4871   *     .translate(50, 50)
4872   *     .rotate(Math.PI / 4);
4873   * bounds.addVertexData(vertices, 0, 6, matrix);
4874   *
4875   * // Add subset of vertices
4876   * bounds.addVertexData(vertices, 2, 4); // Only second vertex
4877   * ```
4878   * @param vertexData - The array of vertices to add
4879   * @param beginOffset - Starting index in the vertex array
4880   * @param endOffset - Ending index in the vertex array (excluded)
4881   * @param matrix - Optional transformation matrix
4882   * @see {@link Bounds#addFrame} For adding rectangular frames
4883   * @see {@link Matrix} For transformation details
4884   */
4885  addVertexData(vertexData, beginOffset, endOffset, matrix) {
4886    let minX = this.minX;
4887    let minY = this.minY;
4888    let maxX = this.maxX;
4889    let maxY = this.maxY;
4890    matrix || (matrix = this.matrix);
4891    const a = matrix.a;
4892    const b = matrix.b;
4893    const c = matrix.c;
4894    const d = matrix.d;
4895    const tx = matrix.tx;
4896    const ty = matrix.ty;
4897    for (let i = beginOffset; i < endOffset; i += 2) {
4898      const localX = vertexData[i];
4899      const localY = vertexData[i + 1];
4900      const x = a * localX + c * localY + tx;
4901      const y = b * localX + d * localY + ty;
4902      minX = x < minX ? x : minX;
4903      minY = y < minY ? y : minY;
4904      maxX = x > maxX ? x : maxX;
4905      maxY = y > maxY ? y : maxY;
4906    }
4907    this.minX = minX;
4908    this.minY = minY;
4909    this.maxX = maxX;
4910    this.maxY = maxY;
4911  }
4912  /**
4913   * Checks if a point is contained within the bounds.
4914   * Returns true if the point's coordinates fall within the bounds' area.
4915   * @example
4916   * ```ts
4917   * const bounds = new Bounds(0, 0, 100, 100);
4918   * // Basic point check
4919   * console.log(bounds.containsPoint(50, 50)); // true
4920   * console.log(bounds.containsPoint(150, 150)); // false
4921   *
4922   * // Check edges
4923   * console.log(bounds.containsPoint(0, 0));   // true, includes edges
4924   * console.log(bounds.containsPoint(100, 100)); // true, includes edges
4925   * ```
4926   * @param x - x coordinate to check
4927   * @param y - y coordinate to check
4928   * @returns True if the point is inside the bounds
4929   * @see {@link Bounds#isPositive} For valid bounds check
4930   * @see {@link Bounds#rectangle} For Rectangle representation
4931   */
4932  containsPoint(x, y) {
4933    if (this.minX <= x && this.minY <= y && this.maxX >= x && this.maxY >= y) {
4934      return true;
4935    }
4936    return false;
4937  }
4938  /**
4939   * Returns a string representation of the bounds.
4940   * Useful for debugging and logging bounds information.
4941   * @example
4942   * ```ts
4943   * const bounds = new Bounds(0, 0, 100, 100);
4944   * console.log(bounds.toString()); // "[pixi.js:Bounds minX=0 minY=0 maxX=100 maxY=100 width=100 height=100]"
4945   * ```
4946   * @returns A string describing the bounds
4947   * @see {@link Bounds#copyFrom} For copying bounds
4948   * @see {@link Bounds#clone} For creating a new instance
4949   */
4950  toString() {
4951    return `[pixi.js:Bounds minX=${this.minX} minY=${this.minY} maxX=${this.maxX} maxY=${this.maxY} width=${this.width} height=${this.height}]`;
4952  }
4953  /**
4954   * Copies the bounds from another bounds object.
4955   * Useful for reusing bounds objects and avoiding allocations.
4956   * @example
4957   * ```ts
4958   * const sourceBounds = new Bounds(0, 0, 100, 100);
4959   * // Copy bounds
4960   * const targetBounds = new Bounds();
4961   * targetBounds.copyFrom(sourceBounds);
4962   * ```
4963   * @param bounds - The bounds to copy from
4964   * @returns This bounds object for chaining
4965   * @see {@link Bounds#clone} For creating new instances
4966   */
4967  copyFrom(bounds) {
4968    this.minX = bounds.minX;
4969    this.minY = bounds.minY;
4970    this.maxX = bounds.maxX;
4971    this.maxY = bounds.maxY;
4972    return this;
4973  }
4974}
4975
vendor: 12,727 bytes, lines 4976-5099
4976var r={grad:.9,turn:360,rad:360/(2*Math.PI)},t=function(r){return "string"==typeof r?r.length>0:"number"==typeof r},n=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=Math.pow(10,t)),Math.round(n*r)/n+0},e=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=1),r>n?n:r>t?r:t},u=function(r){return (r=isFinite(r)?r%360:0)>0?r:r+360},a=function(r){return {r:e(r.r,0,255),g:e(r.g,0,255),b:e(r.b,0,255),a:e(r.a)}},o=function(r){return {r:n(r.r),g:n(r.g),b:n(r.b),a:n(r.a,3)}},i=/^#([0-9a-f]{3,8})$/i,s=function(r){var t=r.toString(16);return t.length<2?"0"+t:t},h=function(r){var t=r.r,n=r.g,e=r.b,u=r.a,a=Math.max(t,n,e),o=a-Math.min(t,n,e),i=o?a===t?(n-e)/o:a===n?2+(e-t)/o:4+(t-n)/o:0;return {h:60*(i<0?i+6:i),s:a?o/a*100:0,v:a/255*100,a:u}},b=function(r){var t=r.h,n=r.s,e=r.v,u=r.a;t=t/360*6,n/=100,e/=100;var a=Math.floor(t),o=e*(1-n),i=e*(1-(t-a)*n),s=e*(1-(1-t+a)*n),h=a%6;return {r:255*[e,i,o,o,s,e][h],g:255*[s,e,e,i,o,o][h],b:255*[o,o,s,e,e,i][h],a:u}},g=function(r){return {h:u(r.h),s:e(r.s,0,100),l:e(r.l,0,100),a:e(r.a)}},d=function(r){return {h:n(r.h),s:n(r.s),l:n(r.l),a:n(r.a,3)}},f=function(r){return b((n=(t=r).s,{h:t.h,s:(n*=((e=t.l)<50?e:100-e)/100)>0?2*n/(e+n)*100:0,v:e+n,a:t.a}));var t,n,e;},c=function(r){return {h:(t=h(r)).h,s:(u=(200-(n=t.s))*(e=t.v)/100)>0&&u<200?n*e/100/(u<=100?u:200-u)*100:0,l:u/2,a:t.a};var t,n,e,u;},l=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,p=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,v=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,m=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,y={string:[[function(r){var t=i.exec(r);return t?(r=t[1]).length<=4?{r:parseInt(r[0]+r[0],16),g:parseInt(r[1]+r[1],16),b:parseInt(r[2]+r[2],16),a:4===r.length?n(parseInt(r[3]+r[3],16)/255,2):1}:6===r.length||8===r.length?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:8===r.length?n(parseInt(r.substr(6,2),16)/255,2):1}:null:null},"hex"],[function(r){var t=v.exec(r)||m.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:a({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:void 0===t[7]?1:Number(t[7])/(t[8]?100:1)}):null},"rgb"],[function(t){var n=l.exec(t)||p.exec(t);if(!n)return null;var e,u,a=g({h:(e=n[1],u=n[2],void 0===u&&(u="deg"),Number(e)*(r[u]||1)),s:Number(n[3]),l:Number(n[4]),a:void 0===n[5]?1:Number(n[5])/(n[6]?100:1)});return f(a)},"hsl"]],object:[[function(r){var n=r.r,e=r.g,u=r.b,o=r.a,i=void 0===o?1:o;return t(n)&&t(e)&&t(u)?a({r:Number(n),g:Number(e),b:Number(u),a:Number(i)}):null},"rgb"],[function(r){var n=r.h,e=r.s,u=r.l,a=r.a,o=void 0===a?1:a;if(!t(n)||!t(e)||!t(u))return null;var i=g({h:Number(n),s:Number(e),l:Number(u),a:Number(o)});return f(i)},"hsl"],[function(r){var n=r.h,a=r.s,o=r.v,i=r.a,s=void 0===i?1:i;if(!t(n)||!t(a)||!t(o))return null;var h=function(r){return {h:u(r.h),s:e(r.s,0,100),v:e(r.v,0,100),a:e(r.a)}}({h:Number(n),s:Number(a),v:Number(o),a:Number(s)});return b(h)},"hsv"]]},N=function(r,t){for(var n=0;n<t.length;n++){var e=t[n][0](r);if(e)return [e,t[n][1]]}return [null,void 0]},x=function(r){return "string"==typeof r?N(r.trim(),y.string):"object"==typeof r&&null!==r?N(r,y.object):[null,void 0]},M=function(r,t){var n=c(r);return {h:n.h,s:e(n.s+100*t,0,100),l:n.l,a:n.a}},H=function(r){return (299*r.r+587*r.g+114*r.b)/1e3/255},$=function(r,t){var n=c(r);return {h:n.h,s:n.s,l:e(n.l+100*t,0,100),a:n.a}},j=function(){function r(r){this.parsed=x(r)[0],this.rgba=this.parsed||{r:0,g:0,b:0,a:1};}return r.prototype.isValid=function(){return null!==this.parsed},r.prototype.brightness=function(){return n(H(this.rgba),2)},r.prototype.isDark=function(){return H(this.rgba)<.5},r.prototype.isLight=function(){return H(this.rgba)>=.5},r.prototype.toHex=function(){return r=o(this.rgba),t=r.r,e=r.g,u=r.b,i=(a=r.a)<1?s(n(255*a)):"","#"+s(t)+s(e)+s(u)+i;var r,t,e,u,a,i;},r.prototype.toRgb=function(){return o(this.rgba)},r.prototype.toRgbString=function(){return r=o(this.rgba),t=r.r,n=r.g,e=r.b,(u=r.a)<1?"rgba("+t+", "+n+", "+e+", "+u+")":"rgb("+t+", "+n+", "+e+")";var r,t,n,e,u;},r.prototype.toHsl=function(){return d(c(this.rgba))},r.prototype.toHslString=function(){return r=d(c(this.rgba)),t=r.h,n=r.s,e=r.l,(u=r.a)<1?"hsla("+t+", "+n+"%, "+e+"%, "+u+")":"hsl("+t+", "+n+"%, "+e+"%)";var r,t,n,e,u;},r.prototype.toHsv=function(){return r=h(this.rgba),{h:n(r.h),s:n(r.s),v:n(r.v),a:n(r.a,3)};var r;},r.prototype.invert=function(){return w({r:255-(r=this.rgba).r,g:255-r.g,b:255-r.b,a:r.a});var r;},r.prototype.saturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,r))},r.prototype.desaturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,-r))},r.prototype.grayscale=function(){return w(M(this.rgba,-1))},r.prototype.lighten=function(r){return void 0===r&&(r=.1),w($(this.rgba,r))},r.prototype.darken=function(r){return void 0===r&&(r=.1),w($(this.rgba,-r))},r.prototype.rotate=function(r){return void 0===r&&(r=15),this.hue(this.hue()+r)},r.prototype.alpha=function(r){return "number"==typeof r?w({r:(t=this.rgba).r,g:t.g,b:t.b,a:r}):n(this.rgba.a,3);var t;},r.prototype.hue=function(r){var t=c(this.rgba);return "number"==typeof r?w({h:r,s:t.s,l:t.l,a:t.a}):n(t.h)},r.prototype.isEqual=function(r){return this.toHex()===w(r).toHex()},r}(),w=function(r){return r instanceof j?r:new j(r)},S=[],k=function(r){r.forEach(function(r){S.indexOf(r)<0&&(r(j,y),S.push(r));});};
4977
4978function namesPlugin(e,f){var a={white:"#ffffff",bisque:"#ffe4c4",blue:"#0000ff",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",antiquewhite:"#faebd7",aqua:"#00ffff",azure:"#f0ffff",whitesmoke:"#f5f5f5",papayawhip:"#ffefd5",plum:"#dda0dd",blanchedalmond:"#ffebcd",black:"#000000",gold:"#ffd700",goldenrod:"#daa520",gainsboro:"#dcdcdc",cornsilk:"#fff8dc",cornflowerblue:"#6495ed",burlywood:"#deb887",aquamarine:"#7fffd4",beige:"#f5f5dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkkhaki:"#bdb76b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",peachpuff:"#ffdab9",darkmagenta:"#8b008b",darkred:"#8b0000",darkorchid:"#9932cc",darkorange:"#ff8c00",darkslateblue:"#483d8b",gray:"#808080",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",deeppink:"#ff1493",deepskyblue:"#00bfff",wheat:"#f5deb3",firebrick:"#b22222",floralwhite:"#fffaf0",ghostwhite:"#f8f8ff",darkviolet:"#9400d3",magenta:"#ff00ff",green:"#008000",dodgerblue:"#1e90ff",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",blueviolet:"#8a2be2",forestgreen:"#228b22",lawngreen:"#7cfc00",indianred:"#cd5c5c",indigo:"#4b0082",fuchsia:"#ff00ff",brown:"#a52a2a",maroon:"#800000",mediumblue:"#0000cd",lightcoral:"#f08080",darkturquoise:"#00ced1",lightcyan:"#e0ffff",ivory:"#fffff0",lightyellow:"#ffffe0",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",linen:"#faf0e6",mediumaquamarine:"#66cdaa",lemonchiffon:"#fffacd",lime:"#00ff00",khaki:"#f0e68c",mediumseagreen:"#3cb371",limegreen:"#32cd32",mediumspringgreen:"#00fa9a",lightskyblue:"#87cefa",lightblue:"#add8e6",midnightblue:"#191970",lightpink:"#ffb6c1",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",mintcream:"#f5fffa",lightslategray:"#778899",lightslategrey:"#778899",navajowhite:"#ffdead",navy:"#000080",mediumvioletred:"#c71585",powderblue:"#b0e0e6",palegoldenrod:"#eee8aa",oldlace:"#fdf5e6",paleturquoise:"#afeeee",mediumturquoise:"#48d1cc",mediumorchid:"#ba55d3",rebeccapurple:"#663399",lightsteelblue:"#b0c4de",mediumslateblue:"#7b68ee",thistle:"#d8bfd8",tan:"#d2b48c",orchid:"#da70d6",mediumpurple:"#9370db",purple:"#800080",pink:"#ffc0cb",skyblue:"#87ceeb",springgreen:"#00ff7f",palegreen:"#98fb98",red:"#ff0000",yellow:"#ffff00",slateblue:"#6a5acd",lavenderblush:"#fff0f5",peru:"#cd853f",palevioletred:"#db7093",violet:"#ee82ee",teal:"#008080",slategray:"#708090",slategrey:"#708090",aliceblue:"#f0f8ff",darkseagreen:"#8fbc8f",darkolivegreen:"#556b2f",greenyellow:"#adff2f",seagreen:"#2e8b57",seashell:"#fff5ee",tomato:"#ff6347",silver:"#c0c0c0",sienna:"#a0522d",lavender:"#e6e6fa",lightgreen:"#90ee90",orange:"#ffa500",orangered:"#ff4500",steelblue:"#4682b4",royalblue:"#4169e1",turquoise:"#40e0d0",yellowgreen:"#9acd32",salmon:"#fa8072",saddlebrown:"#8b4513",sandybrown:"#f4a460",rosybrown:"#bc8f8f",darksalmon:"#e9967a",lightgoldenrodyellow:"#fafad2",snow:"#fffafa",lightgrey:"#d3d3d3",lightgray:"#d3d3d3",dimgray:"#696969",dimgrey:"#696969",olivedrab:"#6b8e23",olive:"#808000"},r={};for(var d in a)r[a[d]]=d;var l={};e.prototype.toName=function(f){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return "transparent";var d,i,n=r[this.toHex()];if(n)return n;if(null==f?void 0:f.closest){var o=this.toRgb(),t=1/0,b="black";if(!l.length)for(var c in a)l[c]=new e(a[c]).toRgb();for(var g in a){var u=(d=o,i=l[g],Math.pow(d.r-i.r,2)+Math.pow(d.g-i.g,2)+Math.pow(d.b-i.b,2));u<t&&(t=u,b=g);}return b}};f.string.push([function(f){var r=f.toLowerCase(),d="transparent"===r?"#0000":a[r];return d?new e(d).toRgb():null},"name"]);}
4979
4980k([namesPlugin]);
4981const _Color = class _Color {
4982  /**
4983   * @param {ColorSource} value - Optional value to use, if not provided, white is used.
4984   */
4985  constructor(value = 16777215) {
4986    this._value = null;
4987    this._components = new Float32Array(4);
4988    this._components.fill(1);
4989    this._int = 16777215;
4990    this.value = value;
4991  }
4992  /**
4993   * Get the red component of the color, normalized between 0 and 1.
4994   * @example
4995   * ```ts
4996   * const color = new Color('red');
4997   * console.log(color.red); // 1
4998   *
4999   * const green = new Color('#00ff00');
5000   * console.log(green.red); // 0
5001   * ```
5002   */
5003  get red() {
5004    return this._components[0];
5005  }
5006  /**
5007   * Get the green component of the color, normalized between 0 and 1.
5008   * @example
5009   * ```ts
5010   * const color = new Color('lime');
5011   * console.log(color.green); // 1
5012   *
5013   * const red = new Color('#ff0000');
5014   * console.log(red.green); // 0
5015   * ```
5016   */
5017  get green() {
5018    return this._components[1];
5019  }
5020  /**
5021   * Get the blue component of the color, normalized between 0 and 1.
5022   * @example
5023   * ```ts
5024   * const color = new Color('blue');
5025   * console.log(color.blue); // 1
5026   *
5027   * const yellow = new Color('#ffff00');
5028   * console.log(yellow.blue); // 0
5029   * ```
5030   */
5031  get blue() {
5032    return this._components[2];
5033  }
5034  /**
5035   * Get the alpha component of the color, normalized between 0 and 1.
5036   * @example
5037   * ```ts
5038   * const color = new Color('red');
5039   * console.log(color.alpha); // 1 (fully opaque)
5040   *
5041   * const transparent = new Color('rgba(255, 0, 0, 0.5)');
5042   * console.log(transparent.alpha); // 0.5 (semi-transparent)
5043   * ```
5044   */
5045  get alpha() {
5046    return this._components[3];
5047  }
5048  /**
5049   * Sets the color value and returns the instance for chaining.
5050   *
5051   * This is a chainable version of setting the `value` property.
5052   * @param value - The color to set. Accepts various formats:
5053   * - Hex strings/numbers (e.g., '#ff0000', 0xff0000)
5054   * - RGB/RGBA values (arrays, objects)
5055   * - CSS color names
5056   * - HSL/HSLA values
5057   * - HSV/HSVA values
5058   * @returns The Color instance for chaining
5059   * @example
5060   * ```ts
5061   * // Basic usage
5062   * const color = new Color();
5063   * color.setValue('#ff0000')
5064   *     .setAlpha(0.5)
5065   *     .premultiply(0.8);
5066   *
5067   * // Different formats
5068   * color.setValue(0xff0000);          // Hex number
5069   * color.setValue('#ff0000');         // Hex string
5070   * color.setValue([1, 0, 0]);         // RGB array
5071   * color.setValue([1, 0, 0, 0.5]);    // RGBA array
5072   * color.setValue({ r: 1, g: 0, b: 0 }); // RGB object
5073   *
5074   * // Copy from another color
5075   * const red = new Color('red');
5076   * color.setValue(red);
5077   * ```
5078   * @throws {Error} If the color value is invalid or null
5079   * @see {@link Color.value} For the underlying value property
5080   */
5081  setValue(value) {
5082    this.value = value;
5083    return this;
5084  }
5085  /**
5086   * The current color source. This property allows getting and setting the color value
5087   * while preserving the original format where possible.
5088   * @remarks
5089   * When setting:
5090   * - Setting to a `Color` instance copies its source and components
5091   * - Setting to other valid sources normalizes and stores the value
5092   * - Setting to `null` throws an Error
5093   * - The color remains unchanged if normalization fails
5094   *
5095   * When getting:
5096   * - Returns `null` if color was modified by {@link Color.multiply} or {@link Color.premultiply}
5097   * - Otherwise returns the original color source
5098   * @example
5099   * ```ts
5100   * // Setting different color formats
5101   * const color = new Color();
5102   *
5103   * color.value = 0xff0000;         // Hex number
5104   * color.value = '#ff0000';        // Hex string
5105   * color.value = [1, 0, 0];        // RGB array
5106   * color.value = [1, 0, 0, 0.5];   // RGBA array
5107   * color.value = { r: 1, g: 0, b: 0 }; // RGB object
5108   *
5109   * // Copying from another color
5110   * const red = new Color('red');
5111   * color.value = red;  // Copies red's components
5112   *
5113   * // Getting the value
5114   * console.log(color.value);  // Returns original format
5115   *
5116   * // After modifications
5117   * color.multiply([0.5, 0.5, 0.5]);
5118   * console.log(color.value);  // Returns null
5119   * ```
5120   * @throws {Error} When attempting to set `null`
5121   */
5122  set value(value) {
5123    if (value instanceof _Color) {
5124      this._value = this._cloneSource(value._value);
5125      this._int = value._int;
5126      this._components.set(value._components);
5127    } else if (value === null) {
5128      throw new Error("Cannot set Color#value to null");
5129    } else if (this._value === null || !this._isSourceEqual(this._value, value)) {
5130      this._value = this._cloneSource(value);
5131      this._normalize(this._value);
5132    }
5133  }
5134  get value() {
5135    return this._value;
5136  }
5137  /**
5138   * Copy a color source internally.
5139   * @param value - Color source
5140   */
5141  _cloneSource(value) {
5142    if (typeof value === "string" || typeof value === "number" || value instanceof Number || value === null) {
5143      return value;
5144    } else if (Array.isArray(value) || ArrayBuffer.isView(value)) {
5145      return value.slice(0);
5146    } else if (typeof value === "object" && value !== null) {
5147      return { ...value }
vendor: 15,400 bytes, lines 5147-5617
5147;
5148    }
5149    return value;
5150  }
5151  /**
5152   * Equality check for color sources.
5153   * @param value1 - First color source
5154   * @param value2 - Second color source
5155   * @returns `true` if the color sources are equal, `false` otherwise.
5156   */
5157  _isSourceEqual(value1, value2) {
5158    const type1 = typeof value1;
5159    const type2 = typeof value2;
5160    if (type1 !== type2) {
5161      return false;
5162    } else if (type1 === "number" || type1 === "string" || value1 instanceof Number) {
5163      return value1 === value2;
5164    } else if (Array.isArray(value1) && Array.isArray(value2) || ArrayBuffer.isView(value1) && ArrayBuffer.isView(value2)) {
5165      if (value1.length !== value2.length) {
5166        return false;
5167      }
5168      return value1.every((v, i) => v === value2[i]);
5169    } else if (value1 !== null && value2 !== null) {
5170      const keys1 = Object.keys(value1);
5171      const keys2 = Object.keys(value2);
5172      if (keys1.length !== keys2.length) {
5173        return false;
5174      }
5175      return keys1.every((key) => value1[key] === value2[key]);
5176    }
5177    return value1 === value2;
5178  }
5179  /**
5180   * Convert to a RGBA color object with normalized components (0-1).
5181   * @example
5182   * ```ts
5183   * import { Color } from 'pixi.js';
5184   *
5185   * // Convert colors to RGBA objects
5186   * new Color('white').toRgba();     // returns { r: 1, g: 1, b: 1, a: 1 }
5187   * new Color('#ff0000').toRgba();   // returns { r: 1, g: 0, b: 0, a: 1 }
5188   *
5189   * // With transparency
5190   * new Color('rgba(255,0,0,0.5)').toRgba(); // returns { r: 1, g: 0, b: 0, a: 0.5 }
5191   * ```
5192   * @returns An RGBA object with normalized components
5193   */
5194  toRgba() {
5195    const [r, g, b, a] = this._components;
5196    return { r, g, b, a };
5197  }
5198  /**
5199   * Convert to a RGB color object with normalized components (0-1).
5200   *
5201   * Alpha component is omitted in the output.
5202   * @example
5203   * ```ts
5204   * import { Color } from 'pixi.js';
5205   *
5206   * // Convert colors to RGB objects
5207   * new Color('white').toRgb();     // returns { r: 1, g: 1, b: 1 }
5208   * new Color('#ff0000').toRgb();   // returns { r: 1, g: 0, b: 0 }
5209   *
5210   * // Alpha is ignored
5211   * new Color('rgba(255,0,0,0.5)').toRgb(); // returns { r: 1, g: 0, b: 0 }
5212   * ```
5213   * @returns An RGB object with normalized components
5214   */
5215  toRgb() {
5216    const [r, g, b] = this._components;
5217    return { r, g, b };
5218  }
5219  /**
5220   * Convert to a CSS-style rgba string representation.
5221   *
5222   * RGB components are scaled to 0-255 range, alpha remains 0-1.
5223   * @example
5224   * ```ts
5225   * import { Color } from 'pixi.js';
5226   *
5227   * // Convert colors to RGBA strings
5228   * new Color('white').toRgbaString();     // returns "rgba(255,255,255,1)"
5229   * new Color('#ff0000').toRgbaString();   // returns "rgba(255,0,0,1)"
5230   *
5231   * // With transparency
5232   * new Color([1, 0, 0, 0.5]).toRgbaString(); // returns "rgba(255,0,0,0.5)"
5233   * ```
5234   * @returns A CSS-compatible rgba string
5235   */
5236  toRgbaString() {
5237    const [r, g, b] = this.toUint8RgbArray();
5238    return `rgba(${r},${g},${b},${this.alpha})`;
5239  }
5240  /**
5241   * Convert to an [R, G, B] array of clamped uint8 values (0 to 255).
5242   * @param {number[]|Uint8Array|Uint8ClampedArray} [out] - Optional output array. If not provided,
5243   * a cached array will be used and returned.
5244   * @returns Array containing RGB components as integers between 0-255
5245   * @example
5246   * ```ts
5247   * // Basic usage
5248   * new Color('white').toUint8RgbArray(); // returns [255, 255, 255]
5249   * new Color('#ff0000').toUint8RgbArray(); // returns [255, 0, 0]
5250   *
5251   * // Using custom output array
5252   * const rgb = new Uint8Array(3);
5253   * new Color('blue').toUint8RgbArray(rgb); // rgb is now [0, 0, 255]
5254   *
5255   * // Using different array types
5256   * new Color('red').toUint8RgbArray(new Uint8ClampedArray(3)); // [255, 0, 0]
5257   * new Color('red').toUint8RgbArray([]); // [255, 0, 0]
5258   * ```
5259   * @remarks
5260   * - Output values are always clamped between 0-255
5261   * - Alpha component is not included in output
5262   * - Reuses internal cache array if no output array provided
5263   */
5264  toUint8RgbArray(out) {
5265    const [r, g, b] = this._components;
5266    if (!this._arrayRgb) {
5267      this._arrayRgb = [];
5268    }
5269    out || (out = this._arrayRgb);
5270    out[0] = Math.round(r * 255);
5271    out[1] = Math.round(g * 255);
5272    out[2] = Math.round(b * 255);
5273    return out;
5274  }
5275  /**
5276   * Convert to an [R, G, B, A] array of normalized floats (numbers from 0.0 to 1.0).
5277   * @param {number[]|Float32Array} [out] - Optional output array. If not provided,
5278   * a cached array will be used and returned.
5279   * @returns Array containing RGBA components as floats between 0-1
5280   * @example
5281   * ```ts
5282   * // Basic usage
5283   * new Color('white').toArray();  // returns [1, 1, 1, 1]
5284   * new Color('red').toArray();    // returns [1, 0, 0, 1]
5285   *
5286   * // With alpha
5287   * new Color('rgba(255,0,0,0.5)').toArray(); // returns [1, 0, 0, 0.5]
5288   *
5289   * // Using custom output array
5290   * const rgba = new Float32Array(4);
5291   * new Color('blue').toArray(rgba); // rgba is now [0, 0, 1, 1]
5292   * ```
5293   * @remarks
5294   * - Output values are normalized between 0-1
5295   * - Includes alpha component as the fourth value
5296   * - Reuses internal cache array if no output array provided
5297   */
5298  toArray(out) {
5299    if (!this._arrayRgba) {
5300      this._arrayRgba = [];
5301    }
5302    out || (out = this._arrayRgba);
5303    const [r, g, b, a] = this._components;
5304    out[0] = r;
5305    out[1] = g;
5306    out[2] = b;
5307    out[3] = a;
5308    return out;
5309  }
5310  /**
5311   * Convert to an [R, G, B] array of normalized floats (numbers from 0.0 to 1.0).
5312   * @param {number[]|Float32Array} [out] - Optional output array. If not provided,
5313   * a cached array will be used and returned.
5314   * @returns Array containing RGB components as floats between 0-1
5315   * @example
5316   * ```ts
5317   * // Basic usage
5318   * new Color('white').toRgbArray(); // returns [1, 1, 1]
5319   * new Color('red').toRgbArray();   // returns [1, 0, 0]
5320   *
5321   * // Using custom output array
5322   * const rgb = new Float32Array(3);
5323   * new Color('blue').toRgbArray(rgb); // rgb is now [0, 0, 1]
5324   * ```
5325   * @remarks
5326   * - Output values are normalized between 0-1
5327   * - Alpha component is omitted from output
5328   * - Reuses internal cache array if no output array provided
5329   */
5330  toRgbArray(out) {
5331    if (!this._arrayRgb) {
5332      this._arrayRgb = [];
5333    }
5334    out || (out = this._arrayRgb);
5335    const [r, g, b] = this._components;
5336    out[0] = r;
5337    out[1] = g;
5338    out[2] = b;
5339    return out;
5340  }
5341  /**
5342   * Convert to a hexadecimal number.
5343   * @returns The color as a 24-bit RGB integer
5344   * @example
5345   * ```ts
5346   * // Basic usage
5347   * new Color('white').toNumber(); // returns 0xffffff
5348   * new Color('red').toNumber();   // returns 0xff0000
5349   *
5350   * // Store as hex
5351   * const color = new Color('blue');
5352   * const hex = color.toNumber(); // 0x0000ff
5353   * ```
5354   */
5355  toNumber() {
5356    return this._int;
5357  }
5358  /**
5359   * Convert to a BGR number.
5360   *
5361   * Useful for platforms that expect colors in BGR format.
5362   * @returns The color as a 24-bit BGR integer
5363   * @example
5364   * ```ts
5365   * // Convert RGB to BGR
5366   * new Color(0xffcc99).toBgrNumber(); // returns 0x99ccff
5367   *
5368   * // Common use case: platform-specific color format
5369   * const color = new Color('orange');
5370   * const bgrColor = color.toBgrNumber(); // Color with swapped R/B channels
5371   * ```
5372   * @remarks
5373   * This swaps the red and blue channels compared to the normal RGB format:
5374   * - RGB 0xRRGGBB becomes BGR 0xBBGGRR
5375   */
5376  toBgrNumber() {
5377    const [r, g, b] = this.toUint8RgbArray();
5378    return (b << 16) + (g << 8) + r;
5379  }
5380  /**
5381   * Convert to a hexadecimal number in little endian format (e.g., BBGGRR).
5382   *
5383   * Useful for platforms that expect colors in little endian byte order.
5384   * @example
5385   * ```ts
5386   * import { Color } from 'pixi.js';
5387   *
5388   * // Convert RGB color to little endian format
5389   * new Color(0xffcc99).toLittleEndianNumber(); // returns 0x99ccff
5390   *
5391   * // Common use cases:
5392   * const color = new Color('orange');
5393   * const leColor = color.toLittleEndianNumber(); // Swaps byte order for LE systems
5394   *
5395   * // Multiple conversions
5396   * const colors = {
5397   *     normal: 0xffcc99,
5398   *     littleEndian: new Color(0xffcc99).toLittleEndianNumber(), // 0x99ccff
5399   *     backToNormal: new Color(0x99ccff).toLittleEndianNumber()  // 0xffcc99
5400   * };
5401   * ```
5402   * @remarks
5403   * - Swaps R and B channels in the color value
5404   * - RGB 0xRRGGBB becomes 0xBBGGRR
5405   * - Useful for systems that use little endian byte order
5406   * - Can be used to convert back and forth between formats
5407   * @returns The color as a number in little endian format (BBGGRR)
5408   * @see {@link Color.toBgrNumber} For BGR format without byte swapping
5409   */
5410  toLittleEndianNumber() {
5411    const value = this._int;
5412    return (value >> 16) + (value & 65280) + ((value & 255) << 16);
5413  }
5414  /**
5415   * Multiply with another color.
5416   *
5417   * This action is destructive and modifies the original color.
5418   * @param {ColorSource} value - The color to multiply by. Accepts any valid color format:
5419   * - Hex strings/numbers (e.g., '#ff0000', 0xff0000)
5420   * - RGB/RGBA arrays ([1, 0, 0], [1, 0, 0, 1])
5421   * - Color objects ({ r: 1, g: 0, b: 0 })
5422   * - CSS color names ('red', 'blue')
5423   * @returns this - The Color instance for chaining
5424   * @example
5425   * ```ts
5426   * // Basic multiplication
5427   * const color = new Color('#ff0000');
5428   * color.multiply(0x808080); // 50% darker red
5429   *
5430   * // With transparency
5431   * color.multiply([1, 1, 1, 0.5]); // 50% transparent
5432   *
5433   * // Chain operations
5434   * color
5435   *     .multiply('#808080')
5436   *     .multiply({ r: 1, g: 1, b: 1, a: 0.5 });
5437   * ```
5438   * @remarks
5439   * - Multiplies each RGB component and alpha separately
5440   * - Values are clamped between 0-1
5441   * - Original color format is lost (value becomes null)
5442   * - Operation cannot be undone
5443   */
5444  multiply(value) {
5445    const [r, g, b, a] = _Color._temp.setValue(value)._components;
5446    this._components[0] *= r;
5447    this._components[1] *= g;
5448    this._components[2] *= b;
5449    this._components[3] *= a;
5450    this._refreshInt();
5451    this._value = null;
5452    return this;
5453  }
5454  /**
5455   * Converts color to a premultiplied alpha format.
5456   *
5457   * This action is destructive and modifies the original color.
5458   * @param alpha - The alpha value to multiply by (0-1)
5459   * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels
5460   * @returns {Color} The Color instance for chaining
5461   * @example
5462   * ```ts
5463   * // Basic premultiplication
5464   * const color = new Color('red');
5465   * color.premultiply(0.5); // 50% transparent red with premultiplied RGB
5466   *
5467   * // Alpha only (RGB unchanged)
5468   * color.premultiply(0.5, false); // 50% transparent, original RGB
5469   *
5470   * // Chain with other operations
5471   * color
5472   *     .multiply(0x808080)
5473   *     .premultiply(0.5)
5474   *     .toNumber();
5475   * ```
5476   * @remarks
5477   * - RGB channels are multiplied by alpha when applyToRGB is true
5478   * - Alpha is always set to the provided value
5479   * - Values are clamped between 0-1
5480   * - Original color format is lost (value becomes null)
5481   * - Operation cannot be undone
5482   */
5483  premultiply(alpha, applyToRGB = true) {
5484    if (applyToRGB) {
5485      this._components[0] *= alpha;
5486      this._components[1] *= alpha;
5487      this._components[2] *= alpha;
5488    }
5489    this._components[3] = alpha;
5490    this._refreshInt();
5491    this._value = null;
5492    return this;
5493  }
5494  /**
5495   * Returns the color as a 32-bit premultiplied alpha integer.
5496   *
5497   * Format: 0xAARRGGBB
5498   * @param {number} alpha - The alpha value to multiply by (0-1)
5499   * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels
5500   * @returns {number} The premultiplied color as a 32-bit integer
5501   * @example
5502   * ```ts
5503   * // Convert to premultiplied format
5504   * const color = new Color('red');
5505   *
5506   * // Full opacity (0xFFRRGGBB)
5507   * color.toPremultiplied(1.0); // 0xFFFF0000
5508   *
5509   * // 50% transparency with premultiplied RGB
5510   * color.toPremultiplied(0.5); // 0x7F7F0000
5511   *
5512   * // 50% transparency without RGB premultiplication
5513   * color.toPremultiplied(0.5, false); // 0x7FFF0000
5514   * ```
5515   * @remarks
5516   * - Returns full opacity (0xFF000000) when alpha is 1.0
5517   * - Returns 0 when alpha is 0.0 and applyToRGB is true
5518   * - RGB values are rounded during premultiplication
5519   */
5520  toPremultiplied(alpha, applyToRGB = true) {
5521    if (alpha === 1) {
5522      return (255 << 24) + this._int;
5523    }
5524    if (alpha === 0) {
5525      return applyToRGB ? 0 : this._int;
5526    }
5527    let r = this._int >> 16 & 255;
5528    let g = this._int >> 8 & 255;
5529    let b = this._int & 255;
5530    if (applyToRGB) {
5531      r = r * alpha + 0.5 | 0;
5532      g = g * alpha + 0.5 | 0;
5533      b = b * alpha + 0.5 | 0;
5534    }
5535    return (alpha * 255 << 24) + (r << 16) + (g << 8) + b;
5536  }
5537  /**
5538   * Convert to a hexadecimal string (6 characters).
5539   * @returns A CSS-compatible hex color string (e.g., "#ff0000")
5540   * @example
5541   * ```ts
5542   * import { Color } from 'pixi.js';
5543   *
5544   * // Basic colors
5545   * new Color('red').toHex();    // returns "#ff0000"
5546   * new Color('white').toHex();  // returns "#ffffff"
5547   * new Color('black').toHex();  // returns "#000000"
5548   *
5549   * // From different formats
5550   * new Color(0xff0000).toHex(); // returns "#ff0000"
5551   * new Color([1, 0, 0]).toHex(); // returns "#ff0000"
5552   * new Color({ r: 1, g: 0, b: 0 }).toHex(); // returns "#ff0000"
5553   * ```
5554   * @remarks
5555   * - Always returns a 6-character hex string
5556   * - Includes leading "#" character
5557   * - Alpha channel is ignored
5558   * - Values are rounded to nearest hex value
5559   */
5560  toHex() {
5561    const hexString = this._int.toString(16);
5562    return `#${"000000".substring(0, 6 - hexString.length) + hexString}`;
5563  }
5564  /**
5565   * Convert to a hexadecimal string with alpha (8 characters).
5566   * @returns A CSS-compatible hex color string with alpha (e.g., "#ff0000ff")
5567   * @example
5568   * ```ts
5569   * import { Color } from 'pixi.js';
5570   *
5571   * // Fully opaque colors
5572   * new Color('red').toHexa();   // returns "#ff0000ff"
5573   * new Color('white').toHexa(); // returns "#ffffffff"
5574   *
5575   * // With transparency
5576   * new Color('rgba(255, 0, 0, 0.5)').toHexa(); // returns "#ff00007f"
5577   * new Color([1, 0, 0, 0]).toHexa(); // returns "#ff000000"
5578   * ```
5579   * @remarks
5580   * - Returns an 8-character hex string
5581   * - Includes leading "#" character
5582   * - Alpha is encoded in last two characters
5583   * - Values are rounded to nearest hex value
5584   */
5585  toHexa() {
5586    const alphaValue = Math.round(this._components[3] * 255);
5587    const alphaString = alphaValue.toString(16);
5588    return this.toHex() + "00".substring(0, 2 - alphaString.length) + alphaString;
5589  }
5590  /**
5591   * Set alpha (transparency) value while preserving color components.
5592   *
5593   * Provides a chainable interface for setting alpha.
5594   * @param alpha - Alpha value between 0 (fully transparent) and 1 (fully opaque)
5595   * @returns The Color instance for chaining
5596   * @example
5597   * ```ts
5598   * // Basic alpha setting
5599   * const color = new Color('red');
5600   * color.setAlpha(0.5);  // 50% transparent red
5601   *
5602   * // Chain with other operations
5603   * color
5604   *     .setValue('#ff0000')
5605   *     .setAlpha(0.8)    // 80% opaque
5606   *     .premultiply(0.5); // Further modify alpha
5607   *
5608   * // Reset to fully opaque
5609   * color.setAlpha(1);
5610   * ```
5611   * @remarks
5612   * - Alpha value is clamped between 0-1
5613   * - Can be chained with other color operations
5614   */
5615  setAlpha(alpha) {
5616    this._components[3] = this._clamp(alpha);
5617    
5617return this;
5618  }
5619  /**
5620   * Normalize the input value into rgba
vendor: 7,895 bytes, lines 5621-5836
5621   * @param value - Input value
5622   */
5623  _normalize(value) {
5624    let r;
5625    let g;
5626    let b;
5627    let a;
5628    if ((typeof value === "number" || value instanceof Number) && value >= 0 && value <= 16777215) {
5629      const int = value;
5630      r = (int >> 16 & 255) / 255;
5631      g = (int >> 8 & 255) / 255;
5632      b = (int & 255) / 255;
5633      a = 1;
5634    } else if ((Array.isArray(value) || value instanceof Float32Array) && value.length >= 3 && value.length <= 4) {
5635      value = this._clamp(value);
5636      [r, g, b, a = 1] = value;
5637    } else if ((value instanceof Uint8Array || value instanceof Uint8ClampedArray) && value.length >= 3 && value.length <= 4) {
5638      value = this._clamp(value, 0, 255);
5639      [r, g, b, a = 255] = value;
5640      r /= 255;
5641      g /= 255;
5642      b /= 255;
5643      a /= 255;
5644    } else if (typeof value === "string" || typeof value === "object") {
5645      if (typeof value === "string") {
5646        const match = _Color.HEX_PATTERN.exec(value);
5647        if (match) {
5648          value = `#${match[2]}`;
5649        }
5650      }
5651      const color = w(value);
5652      if (color.isValid()) {
5653        ({ r, g, b, a } = color.rgba);
5654        r /= 255;
5655        g /= 255;
5656        b /= 255;
5657      }
5658    }
5659    if (r !== void 0) {
5660      this._components[0] = r;
5661      this._components[1] = g;
5662      this._components[2] = b;
5663      this._components[3] = a;
5664      this._refreshInt();
5665    } else {
5666      throw new Error(`Unable to convert color ${value}`);
5667    }
5668  }
5669  /** Refresh the internal color rgb number */
5670  _refreshInt() {
5671    this._clamp(this._components);
5672    const [r, g, b] = this._components;
5673    this._int = (r * 255 << 16) + (g * 255 << 8) + (b * 255 | 0);
5674  }
5675  /**
5676   * Clamps values to a range. Will override original values
5677   * @param value - Value(s) to clamp
5678   * @param min - Minimum value
5679   * @param max - Maximum value
5680   */
5681  _clamp(value, min = 0, max = 1) {
5682    if (typeof value === "number") {
5683      return Math.min(Math.max(value, min), max);
5684    }
5685    value.forEach((v, i) => {
5686      value[i] = Math.min(Math.max(v, min), max);
5687    });
5688    return value;
5689  }
5690  /**
5691   * Check if a value can be interpreted as a valid color format.
5692   * Supports all color formats that can be used with the Color class.
5693   * @param value - Value to check
5694   * @returns True if the value can be used as a color
5695   * @example
5696   * ```ts
5697   * import { Color } from 'pixi.js';
5698   *
5699   * // CSS colors and hex values
5700   * Color.isColorLike('red');          // true
5701   * Color.isColorLike('#ff0000');      // true
5702   * Color.isColorLike(0xff0000);       // true
5703   *
5704   * // Arrays (RGB/RGBA)
5705   * Color.isColorLike([1, 0, 0]);      // true
5706   * Color.isColorLike([1, 0, 0, 0.5]); // true
5707   *
5708   * // TypedArrays
5709   * Color.isColorLike(new Float32Array([1, 0, 0]));          // true
5710   * Color.isColorLike(new Uint8Array([255, 0, 0]));          // true
5711   * Color.isColorLike(new Uint8ClampedArray([255, 0, 0]));   // true
5712   *
5713   * // Object formats
5714   * Color.isColorLike({ r: 1, g: 0, b: 0 });            // true (RGB)
5715   * Color.isColorLike({ r: 1, g: 0, b: 0, a: 0.5 });    // true (RGBA)
5716   * Color.isColorLike({ h: 0, s: 100, l: 50 });         // true (HSL)
5717   * Color.isColorLike({ h: 0, s: 100, l: 50, a: 0.5 }); // true (HSLA)
5718   * Color.isColorLike({ h: 0, s: 100, v: 100 });        // true (HSV)
5719   * Color.isColorLike({ h: 0, s: 100, v: 100, a: 0.5 });// true (HSVA)
5720   *
5721   * // Color instances
5722   * Color.isColorLike(new Color('red')); // true
5723   *
5724   * // Invalid values
5725   * Color.isColorLike(null);           // false
5726   * Color.isColorLike(undefined);      // false
5727   * Color.isColorLike({});             // false
5728   * Color.isColorLike([]);             // false
5729   * Color.isColorLike('not-a-color');  // false
5730   * ```
5731   * @remarks
5732   * Checks for the following formats:
5733   * - Numbers (0x000000 to 0xffffff)
5734   * - CSS color strings
5735   * - RGB/RGBA arrays and objects
5736   * - HSL/HSLA objects
5737   * - HSV/HSVA objects
5738   * - TypedArrays (Float32Array, Uint8Array, Uint8ClampedArray)
5739   * - Color instances
5740   * @see {@link ColorSource} For supported color format types
5741   * @see {@link Color.setValue} For setting color values
5742   * @category utility
5743   */
5744  static isColorLike(value) {
5745    return typeof value === "number" || typeof value === "string" || value instanceof Number || value instanceof _Color || Array.isArray(value) || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Float32Array || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 && value.a !== void 0;
5746  }
5747};
5748/**
5749 * Static shared Color instance used for utility operations. This is a singleton color object
5750 * that can be reused to avoid creating unnecessary Color instances.
5751 * > [!IMPORTANT] You should be careful when using this shared instance, as it is mutable and can be
5752 * > changed by any code that uses it.
5753 * >
5754 * > It is best used for one-off color operations or temporary transformations.
5755 * > For persistent colors, create your own Color instance instead.
5756 * @example
5757 * ```ts
5758 * import { Color } from 'pixi.js';
5759 *
5760 * // Use shared instance for one-off color operations
5761 * Color.shared.setValue(0xff0000);
5762 * const redHex = Color.shared.toHex();     // "#ff0000"
5763 * const redRgb = Color.shared.toRgbArray(); // [1, 0, 0]
5764 *
5765 * // Temporary color transformations
5766 * const colorNumber = Color.shared
5767 *     .setValue('#ff0000')     // Set to red
5768 *     .setAlpha(0.5)          // Make semi-transparent
5769 *     .premultiply(0.8)       // Apply premultiplication
5770 *     .toNumber();            // Convert to number
5771 *
5772 * // Chain multiple operations
5773 * const result = Color.shared
5774 *     .setValue(someColor)
5775 *     .multiply(tintColor)
5776 *     .toPremultiplied(alpha);
5777 * ```
5778 * @remarks
5779 * - This is a shared instance - be careful about multiple code paths using it simultaneously
5780 * - Use for temporary color operations to avoid allocating new Color instances
5781 * - The value is preserved between operations, so reset if needed
5782 * - For persistent colors, create your own Color instance instead
5783 */
5784_Color.shared = new _Color();
5785/**
5786 * Temporary Color object for static uses internally.
5787 * As to not conflict with Color.shared.
5788 * @ignore
5789 */
5790_Color._temp = new _Color();
5791/** Pattern for hex strings */
5792// eslint-disable-next-line @typescript-eslint/naming-convention
5793_Color.HEX_PATTERN = /^(#|0x)?(([a-f0-9]{3}){1,2}([a-f0-9]{2})?)$/i;
5794let Color = _Color;
5795
5796const cullingMixin = {
5797  cullArea: null,
5798  cullable: false,
5799  cullableChildren: true
5800};
5801
5802let warnCount = 0;
5803const maxWarnings = 500;
5804function warn(...args) {
5805  if (warnCount === maxWarnings)
5806    return;
5807  warnCount++;
5808  if (warnCount === maxWarnings) {
5809    console.warn("PixiJS Warning: too many warnings, no more warnings will be reported to the console by PixiJS.");
5810  } else {
5811    console.warn("PixiJS Warning: ", ...args);
5812  }
5813}
5814
5815const GlobalResourceRegistry = {
5816  /**
5817   * Set of registered pools and cleanable objects.
5818   * @private
5819   */
5820  _registeredResources: /* @__PURE__ */ new Set(),
5821  /**
5822   * Registers a pool or cleanable object for cleanup.
5823   * @param {Cleanable} pool - The pool or object to register.
5824   */
5825  register(pool) {
5826    this._registeredResources.add(pool);
5827  },
5828  /**
5829   * Unregisters a pool or cleanable object from cleanup.
5830   * @param {Cleanable} pool - The pool or object to unregister.
5831   */
5832  unregister(pool) {
5833    this._registeredResources.delete(pool);
5834  },
5835  /** Clears all registered pools and cleanable objects. This will call clear() on each registered item. */
5836  release() {
5837    this._registeredResources.forEach((pool) => pool.clear());
5838  },
5839  /**
5840   * Gets the number of registered pools and cleanable objects.
5841   * @returns {number} The count of registered items.
5842   */
5843  get registeredCount() {
5844    return this._registeredResources.size;
5845  },
5846  /**
5847   * Checks if a specific pool or cleanable object is registered.
5848   * @param {Cleanable} pool - The pool or object to check.
5849   * @returns {boolean} True if the item is registered, false otherwise.
5850   */
5851  isRegistered(pool) {
5852    return this._registeredResources.has(pool);
5853  },
5854  /**
5855   * Removes all registrations without clearing the pools.
5856   * Useful if you want to reset the collector without affecting the pools.
5857   */
5858  reset() {
5859    this._registeredResources.clear();
5860  }
5861};
5862
5863class Pool {
5864  /**
5865   * Constructs a new Pool.
5866   * @param ClassType - The constructor of the items in the pool.
5867   * @param {number} [initialSize] - The initial size of the pool.
5868   */
5869  constructor(ClassType, initialSize) {
5870    this._pool = [];
5871    this._count = 0;
5872    this._index = 0;
5873    this._classType = ClassType;
5874    if (initialSize) {
5875      this.prepopulate(initialSize);
5876    }
5877  }
5878  /**
5879   * Prepopulates the pool with a given number of items.
5880   * @param total - The number of items to add to the pool.
5881   */
5882  prepopulate(total) {
5883    for (let i = 0; i < total; i++) {
5884      this._pool[this._index++] = new this._classType();
5885    }
5886    this._count += total;
5887  }
5888  /**
5889   * Gets an item from the pool. Calls the item's `init` method if it exists.
5890   * If there are no items left in the pool, a new one will be created.
5891   * @param {unknown} [data] - Optional data to pass to the item's constructor.
5892   * @returns {T} The item from the pool.
5893   */
5894  get(data) {
5895    let item;
5896    if (this._index > 0) {
5897      item = this._pool[--this._index];
5898    } else {
5899      item = new this._classType();
5900      this._count++;
5901    }
5902    item.init?.(data);
5903    return item;
5904  }
5905  /**
5906   * Returns an item to the pool. Calls the item's `reset` method if it exists.
5907   * @param {T} item - The item to return to the pool.
5908   */
5909  return(item) {
5910    item.reset?.();
5911    this._pool[this._index++] = item;
5912  }
5913  /**
5914   * Gets the number of items in the pool.
5915   * @readonly
5916   */
5917  get totalSize() {
5918    return this._count;
5919  }
5920  /**
5921   * Gets the number of items in the pool that are free to use without needing to create more.
5922   * @readonly
5923   */
5924  get totalFree() {
5925    return this._index;
5926  }
5927  /**
5928   * Gets the number of items in the pool that are currently in use.
5929   * @readonly
5930   */
5931  get totalUsed() {
5932    return this._count - this._index;
5933  }
5934  /** clears the pool */
5935  clear() {
5936    if (this._pool.length > 0 && this._pool[0].destroy) {
5937      for (let i = 0; i < this._index; i++) {
5938        this._pool[i].destroy();
5939      }
5940    }
5941    this._pool.length = 0;
5942    this._count = 0;
5943    this._index = 0;
5944  }
5945}
5946
5947class PoolGroupClass {
5948  constructor() {
5949    /**
5950     * A map to store the pools by their class type.
5951     * @private
5952     */
5953    this._poolsByClass = /* @__PURE__ */ new Map();
5954  }
5955  /**
5956   * Prepopulates a specific pool with a given number of items.
5957   * @template T The type of items in the pool. Must extend PoolItem.
5958   * @param {PoolItemConstructor<T>} Class - The constructor of the items in the pool.
5959   * @param {number} total - The number of items to add to the pool.
5960   */
5961  prepopulate(Class, total) {
5962    const classPool = this.getPool(Class);
5963    classPool.prepopulate(total);
5964  }
5965  /**
5966   * Gets an item from a specific pool.
5967   * @template T The type of items in the pool. Must extend PoolItem.
5968   * @param {PoolItemConstructor<T>} Class - The constructor of the items in the pool.
5969   * @param {unknown} [data] - Optional data to pass to the item's constructor.
5970   * @returns {T} The item from the pool.
5971   */
5972  get(Class, data) {
5973    const pool = this.getPool(Class);
5974    return pool.get(data);
5975  }
5976  /**
5977   * Returns an item to its respective pool.
5978   * @param {PoolItem} item - The item to return to the pool.
5979   */
5980  return(item) {
5981    const pool = this.getPool(item.constructor);
5982    pool.return(item);
5983  }
5984  /**
5985   * Gets a specific pool based on the class type.
5986   * @template T The type of items in the pool. Must extend PoolItem.
5987   * @param {PoolItemConstructor<T>} ClassType - The constructor of the items in the pool.
5988   * @returns {Pool<T>} The pool of the given class type.
5989   */
5990  getPool(ClassType) {
5991    if (!this._poolsByClass.has(ClassType)) {
5992      this._poolsByClass.set(ClassType, new Pool(ClassType));
5993    }
5994    return this._poolsByClass.get(ClassType);
5995  }
5996  /** gets the usage stats of each pool in the system */
5997  stats() {
5998    const stats = {};
5999    this._poolsByClass.forEach((pool) => {
6000      const name = stats[pool._classType.name] ? pool._classType.name + pool._classType.ID : pool._classType.name;
6001      stats[name] = {
6002        free: pool.totalFree,
6003        used: pool.totalUsed,
6004        size: pool.totalSize
6005      };
6006    });
6007    return stats;
6008  }
6009  /** Clears all pools in the group. This will reset all pools and free their resources. */
6010  clear() {
6011    this._poolsByClass.forEach((pool) => pool.clear());
6012    this._poolsByClass.clear();
6013  }
6014}
6015const BigPool = new PoolGroupClass();
6016GlobalResourceRegistry.register(BigPool);
6017
6018const cacheAsTextureMixin = {
6019  get isCachedAsTexture() {
6020    return !!this.renderGroup?.isCachedAsTexture;
6021  },
6022  cacheAsTexture(val) {
6023    if (typeof val === "boolean" && val === false) {
6024      this.disableRenderGroup();
6025    } else {
6026      this.enableRenderGroup();
6027      this.renderGroup.enableCacheAsTexture(val === true ? {} : val);
6028    }
6029  },
6030  updateCacheTexture() {
6031    this.renderGroup?.updateCacheTexture();
6032  },
6033  get cacheAsBitmap() {
6034    return this.isCachedAsTexture;
6035  },
6036  set cacheAsBitmap(val) {
6037    deprecation("v8.6.0", "cacheAsBitmap is deprecated, use cacheAsTexture instead.");
6038    this.cacheAsTexture(val);
6039  }
6040};
6041
6042function removeItems(arr, startIdx, removeCount) {
6043  const length = arr.length;
6044  let i;
6045  if (startIdx >= length || removeCount === 0) {
6046    return;
6047  }
6048  removeCount = startIdx + removeCount > length ? length - startIdx : removeCount;
6049  const len = length - removeCount;
6050  for (i = startIdx; i < len; ++i) {
6051    arr[i] = arr[i + removeCount];
6052  }
6053  arr.length = len;
6054}
6055
6056const childrenHelperMixin = {
6057  allowChildren: true,
6058  removeChildren(beginIndex = 0, endIndex) {
6059    const end = endIndex ?? this.children.length;
6060    const range = end - beginIndex;
6061    const removed = [];
6062    if (range > 0 && range <= end) {
6063      for (let i = end - 1; i >= beginIndex; i--) {
6064        const child = this.children[i];
6065        if (!child)
6066          continue;
6067        removed.push(child);
6068        child.parent = null;
6069      }
6070      removeItems(this.children, beginIndex, end);
6071      const renderGroup = this.renderGroup || this.parentRenderGroup;
6072      if (renderGroup) {
6073        renderGroup.removeChildren(removed);
6074      }
6075      for (let i = 0; i < removed.length; ++i) {
6076        const child = removed[i];
6077        child.parentRenderLayer?.detach(child);
6078        this.emit("childRemoved", child, this, i);
6079        removed[i].emit("removed", this);
6080      }
6081      if (removed.length > 0) {
6082        this._didViewChangeTick++;
6083      }
6084      return removed;
6085    } else if (range === 0 && this.children.length === 0) {
6086      return removed;
6087    }
6088    throw new RangeError("removeChildren: numeric values are outside the acceptable range.");
6089  },
6090  removeChildAt(index) {
6091    const child = this.getChildAt(index);
6092    return this.removeChild(child);
6093  },
6094  getChildAt(index) {
6095    if (index < 0 || index >= this.children.length) {
6096      throw new Error(`getChildAt: Index (${index}) does not exist.`);
6097    }
6098    return this.children[index];
6099  },
6100  setChildIndex(child, index) {
6101    if (index < 0 || index >= this.children.length) {
6102      throw new Error(`The index ${index} supplied is out of bounds ${this.children.length}`);
6103    }
6104    this.getChildIndex(child);
6105    this.addChildAt(child, index);
6106  },
6107  getChildIndex(child) {
6108    const index = this.children.indexOf(child);
6109    if (index === -1) {
6110      throw new Error("The supplied Container must be a child of the caller");
6111    }
6112    return index;
6113  },
6114  addChildAt(child, index) {
6115    if (!this.allowChildren) {
6116      deprecation(v8_0_0, "addChildAt: Only Containers will be allowed to add children in v8.0.0");
6117    }
6118    const { children } = this;
6119    if (index < 0 || index > children.length) {
6120      throw new Error(`${child}addChildAt: The index ${index} supplied is out of bounds ${children.length}`);
6121    }
6122    if (child.parent) {
6123      const currentIndex = child.parent.children.indexOf(child);
6124      if (child.parent === this && currentIndex === index) {
6125        return child;
6126      }
6127      if (currentIndex !== -1) {
6128        child.parent.children.splice(currentIndex, 1);
6129      }
6130    }
6131    if (index === children.length) {
6132      children.push(child);
6133    } else {
6134      children.splice(index, 0, child);
6135    }
6136    child.parent = this;
6137    child.didChange = true;
6138    child._updateFlags = 15;
6139    const renderGroup = this.renderGroup || this.parentRenderGroup;
6140    if (renderGroup) {
6141      renderGroup.addChild(child);
6142    }
6143    if (this.sortableChildren)
6144      this.sortDirty = true;
6145    this.emit("childAdded", child, this, index);
6146    child.emit("added", this);
6147    return child;
6148  },
6149  swapChildren(child, child2) {
6150    if (child === child2) {
6151      return;
6152    }
6153    const index1 = this.getChildIndex(child);
6154    const index2 = this.getChildIndex(child2);
6155    this.children[index1] = child2;
6156    this.children[index2] = child;
6157    const renderGroup = this.renderGroup || this.parentRenderGroup;
6158    if (renderGroup) {
6159      renderGroup.structureDidChange = true;
6160    }
6161    this._didContainerChangeTick++;
6162  },
6163  removeFromParent() {
6164    this.parent?.removeChild(this);
6165  },
6166  reparentChild(...child) {
6167    if (child.length === 1) {
6168      return this.reparentChildAt(child[0], this.children.length);
6169    }
6170    child.forEach((c) => this.reparentChildAt(c, this.children.length));
6171    return child[0];
6172  },
6173  reparentChildAt(child, index) {
6174    if (child.parent === this) {
6175      this.setChildIndex(child, index);
6176      return child;
6177    }
6178    const childMat = child.worldTransform.clone();
6179    child.removeFromParent();
6180    this.addChildAt(child, index);
6181    const newMatrix = this.worldTransform.clone();
6182    newMatrix.invert();
6183    childMat.prepend(newMatrix);
6184    child.setFromMatrix(childMat);
6185    return child;
6186  },
6187  replaceChild(oldChild, newChild) {
6188    oldChild.updateLocalTransform();
6189    this.addChildAt(newChild, this.getChildIndex(oldChild));
6190    newChild.setFromMatrix(oldChild.localTransform);
6191    newChild.updateLocalTransform();
6192    this.removeChild(oldChild);
6193  }
6194};
6195
6196const collectRenderablesMixin = {
6197  collectRenderables(instructionSet, renderer, currentLayer) {
6198    if (this.parentRenderLayer && this.parentRenderLayer !== currentLayer || this.globalDisplayStatus < 7 || !this.includeInBuild)
6199      return;
6200    if (this.sortableChildren) {
6201      this.sortChildren();
6202    }
6203    if (this.isSimple) {
6204      this.collectRenderablesSimple(instructionSet, renderer, currentLayer);
6205    } else if (this.renderGroup) {
6206      renderer.renderPipes.renderGroup.addRenderGroup(this.renderGroup, instructionSet);
6207    } else {
6208      this.collectRenderablesWithEffects(instructionSet, renderer, currentLayer);
6209    }
6210  },
6211  collectRenderablesSimple(instructionSet, renderer, currentLayer) {
6212    const children = this.children;
6213    const length = children.length;
6214    for (let i = 0; i < length; i++) {
6215      children[i].collectRenderables(instructionSet, renderer, currentLayer);
6216    }
6217  },
6218  collectRenderablesWithEffects(instructionSet, renderer, currentLayer) {
6219    const { renderPipes } = renderer;
6220    for (let i = 0; i < this.effects.length; i++) {
6221      const effect = this.effects[i];
6222      const pipe = renderPipes[effect.pipe];
6223      pipe.push(effect, this, instructionSet);
6224    }
6225    this.collectRenderablesSimple(instructionSet, renderer, currentLayer);
6226    for (let i = this.effects.length - 1; i >= 0; i--) {
6227      const effect = this.effects[i];
6228      const pipe = renderPipes[effect.pipe];
6229      pipe.pop(effect, this, instructionSet);
6230    }
6231  }
6232};
6233
6234class FilterEffect {
6235  constructor() {
6236    /** the pipe that knows how to handle this effect */
6237    this.pipe = "filter";
6238    /** the priority of this effect */
6239    this.priority = 1;
6240  }
6241  destroy() {
6242    for (let i = 0; i < this.filters.length; i++) {
6243      this.filters[i].destroy();
6244    }
6245    this.filters = null;
6246    this.filterArea = null;
6247  }
6248}
6249
6250class MaskEffectManagerClass {
6251  constructor() {
6252    /** @private */
6253    this._effectClasses = [];
6254    this._tests = [];
6255    this._initialized = false;
6256  }
6257  init() {
6258    if (this._initialized)
6259      return;
6260    this._initialized = true;
6261    this._effectClasses.forEach((test) => {
6262      this.add({
6263        test: test.test,
6264        maskClass: test
6265      });
6266    });
6267  }
6268  add(test) {
6269    this._tests.push(test);
6270  }
6271  getMaskEffect(item) {
6272    if (!this._initialized)
6273      this.init();
6274    for (let i = 0; i < this._tests.length; i++) {
6275      const test = this._tests[i];
6276      if (test.test(item)) {
6277        return BigPool.get(test.maskClass, item);
6278      }
6279    }
6280    return item;
6281  }
6282  returnMaskEffect(effect) {
6283    BigPool.return(effect);
6284  }
6285}
6286const MaskEffectManager = new MaskEffectManagerClass();
6287extensions.handleByList(ExtensionType.MaskEffect, MaskEffectManager._effectClasses);
6288
6289const effectsMixin = {
6290  _maskEffect: null,
6291  _maskOptions: {
6292    inverse: false
6293  },
6294  _filterEffect: null,
6295  effects: [],
6296  _markStructureAsChanged() {
6297    const renderGroup = this.renderGroup || this.parentRenderGroup;
6298    if (renderGroup) {
6299      renderGroup.structureDidChange = true;
6300    }
6301  },
6302  addEffect(effect) {
6303    const index = this.effects.indexOf(effect);
6304    if (index !== -1)
6305      return;
6306    this.effects.push(effect);
6307    this.effects.sort((a, b) => a.priority - b.priority);
6308    this._markStructureAsChanged();
6309    this._updateIsSimple();
6310  },
6311  removeEffect(effect) {
6312    const index = this.effects.indexOf(effect);
6313    if (index === -1)
6314      return;
6315    this.effects.splice(index, 1);
6316    this._markStructureAsChanged();
6317    this._updateIsSimple();
6318  },
6319  set mask(value) {
6320    const effect = this._maskEffect;
6321    if (effect?.mask === value)
6322      return;
6323    if (effect) {
6324      this.removeEffect(effect);
6325      MaskEffectManager.returnMaskEffect(effect);
6326      this._maskEffect = null;
6327    }
6328    if (value === null || value === void 0)
6329      return;
6330    this._maskEffect = MaskEffectManager.getMaskEffect(value);
6331    this.addEffect(this._maskEffect);
6332  },
6333  get mask() {
6334    return this._maskEffect?.mask;
6335  },
6336  setMask(options) {
6337    this._maskOptions = {
6338      ...this._maskOptions,
6339      ...options
6340    };
6341    if (options.mask) {
6342      this.mask = options.mask;
6343    }
6344    this._markStructureAsChanged();
6345  },
6346  set filters(value) {
6347    if (!Array.isArray(value) && value)
6348      value = [value];
6349    const effect = this._filterEffect || (this._filterEffect = new FilterEffect());
6350    value = value;
6351    const hasFilters = value?.length > 0;
6352    const hadFilters = effect.filters?.length > 0;
6353    const didChange = hasFilters !== hadFilters;
6354    value = Array.isArray(value) ? value.slice(0) : value;
6355    effect.filters = Object.freeze(value);
6356    if (didChange) {
6357      if (hasFilters) {
6358        this.addEffect(effect);
6359      } else {
6360        this.removeEffect(effect);
6361        effect.filters = value ?? null;
6362      }
6363    }
6364  },
6365  get filters() {
6366    return this._filterEffect?.filters;
6367  },
6368  set filterArea(value) {
6369    this._filterEffect || (this._filterEffect = new FilterEffect());
6370    this._filterEffect.filterArea = value;
6371  },
6372  get filterArea() {
6373    return this._filterEffect?.filterArea;
6374  }
6375};
6376
6377const findMixin = {
6378  label: null,
6379  get name() {
6380    deprecation(v8_0_0, "Container.name property has been removed, use Container.label instead");
6381    return this.label;
6382  },
6383  set name(value) {
6384    deprecation(v8_0_0, "Container.name property has been removed, use Container.label instead");
6385    this.label = value;
6386  },
6387  getChildByName(name, deep = false) {
6388    return this.getChildByLabel(name, deep);
6389  },
6390  getChildByLabel(label, deep = false) {
6391    const children = this.children;
6392    for (let i = 0; i < children.length; i++) {
6393      const child = children[i];
6394      if (child.label === label || label instanceof RegExp && label.test(child.label))
6395        return child;
6396    }
6397    if (deep) {
6398      for (let i = 0; i < children.length; i++) {
6399        const child = children[i];
6400        const found = child.getChildByLabel(label, true);
6401        if (found) {
6402          return found;
6403        }
6404      }
6405    }
6406    return null;
6407  },
6408  getChildrenByLabel(label, deep = false, out = []) {
6409    const children = this.children;
6410    for (let i = 0; i < children.length; i++) {
6411      const child = children[i];
6412      if (child.label === label || label instanceof RegExp && label.test(child.label)) {
6413        out.push(child);
6414      }
6415    }
6416    if (deep) {
6417      for (let i = 0; i < children.length; i++) {
6418        children[i].getChildrenByLabel(label, true, out);
6419      }
6420    }
6421    return out;
6422  }
6423};
6424
6425const matrixPool = BigPool.getPool(Matrix);
6426const boundsPool = BigPool.getPool(Bounds);
6427
6428const tempMatrix$2 = new Matrix();
6429const getFastGlobalBoundsMixin = {
6430  getFastGlobalBounds(factorRenderLayers, bounds) {
6431    bounds || (bounds = new Bounds());
6432    bounds.clear();
6433    this._getGlobalBoundsRecursive(!!factorRenderLayers, bounds, this.parentRenderLayer);
6434    if (!bounds.isValid) {
6435      bounds.set(0, 0, 0, 0);
6436    }
6437    const renderGroup = this.renderGroup || this.parentRenderGroup;
6438    bounds.applyMatrix(renderGroup.worldTransform);
6439    return bounds;
6440  },
6441  _getGlobalBoundsRecursive(factorRenderLayers, bounds, currentLayer) {
6442    let localBounds = bounds;
6443    if (factorRenderLayers && this.parentRenderLayer && this.parentRenderLayer !== currentLayer)
6444      return;
6445    if (this.localDisplayStatus !== 7 || !this.measurable) {
6446      return;
6447    }
6448    const manageEffects = !!this.effects.length;
6449    if (this.renderGroup || manageEffects) {
6450      localBounds = boundsPool.get().clear();
6451    }
6452    if (this.boundsArea) {
6453      bounds.addRect(this.boundsArea, this.worldTransform);
6454    } else {
6455      if (this.renderPipeId) {
vendor: 8,881 bytes, lines 6456-6748
6456        const viewBounds = this.bounds;
6457        localBounds.addFrame(
6458          viewBounds.minX,
6459          viewBounds.minY,
6460          viewBounds.maxX,
6461          viewBounds.maxY,
6462          this.groupTransform
6463        );
6464      }
6465      const children = this.children;
6466      for (let i = 0; i < children.length; i++) {
6467        children[i]._getGlobalBoundsRecursive(factorRenderLayers, localBounds, currentLayer);
6468      }
6469    }
6470    if (manageEffects) {
6471      let advanced = false;
6472      const renderGroup = this.renderGroup || this.parentRenderGroup;
6473      for (let i = 0; i < this.effects.length; i++) {
6474        if (this.effects[i].addBounds) {
6475          if (!advanced) {
6476            advanced = true;
6477            localBounds.applyMatrix(renderGroup.worldTransform);
6478          }
6479          this.effects[i].addBounds(localBounds, true);
6480        }
6481      }
6482      if (advanced) {
6483        localBounds.applyMatrix(renderGroup.worldTransform.copyTo(tempMatrix$2).invert());
6484      }
6485      bounds.addBounds(localBounds);
6486      boundsPool.return(localBounds);
6487    } else if (this.renderGroup) {
6488      bounds.addBounds(localBounds, this.relativeGroupTransform);
6489      boundsPool.return(localBounds);
6490    }
6491  }
6492};
6493
6494function getGlobalBounds(target, skipUpdateTransform, bounds) {
6495  bounds.clear();
6496  let parentTransform;
6497  let pooledMatrix;
6498  if (target.parent) {
6499    if (!skipUpdateTransform) {
6500      pooledMatrix = matrixPool.get().identity();
6501      parentTransform = updateTransformBackwards(target, pooledMatrix);
6502    } else {
6503      parentTransform = target.parent.worldTransform;
6504    }
6505  } else {
6506    parentTransform = Matrix.IDENTITY;
6507  }
6508  _getGlobalBounds(target, bounds, parentTransform, skipUpdateTransform);
6509  if (pooledMatrix) {
6510    matrixPool.return(pooledMatrix);
6511  }
6512  if (!bounds.isValid) {
6513    bounds.set(0, 0, 0, 0);
6514  }
6515  return bounds;
6516}
6517function _getGlobalBounds(target, bounds, parentTransform, skipUpdateTransform) {
6518  if (!target.visible || !target.measurable)
6519    return;
6520  let worldTransform;
6521  if (!skipUpdateTransform) {
6522    target.updateLocalTransform();
6523    worldTransform = matrixPool.get();
6524    worldTransform.appendFrom(target.localTransform, parentTransform);
6525  } else {
6526    worldTransform = target.worldTransform;
6527  }
6528  const parentBounds = bounds;
6529  const preserveBounds = !!target.effects.length;
6530  if (preserveBounds) {
6531    bounds = boundsPool.get().clear();
6532  }
6533  if (target.boundsArea) {
6534    bounds.addRect(target.boundsArea, worldTransform);
6535  } else {
6536    const renderableBounds = target.bounds;
6537    if (renderableBounds && !renderableBounds.isEmpty()) {
6538      bounds.matrix = worldTransform;
6539      bounds.addBounds(renderableBounds);
6540    }
6541    for (let i = 0; i < target.children.length; i++) {
6542      _getGlobalBounds(target.children[i], bounds, worldTransform, skipUpdateTransform);
6543    }
6544  }
6545  if (preserveBounds) {
6546    for (let i = 0; i < target.effects.length; i++) {
6547      target.effects[i].addBounds?.(bounds);
6548    }
6549    parentBounds.addBounds(bounds, Matrix.IDENTITY);
6550    boundsPool.return(bounds);
6551  }
6552  if (!skipUpdateTransform) {
6553    matrixPool.return(worldTransform);
6554  }
6555}
6556function updateTransformBackwards(target, parentTransform) {
6557  const parent = target.parent;
6558  if (parent) {
6559    updateTransformBackwards(parent, parentTransform);
6560    parent.updateLocalTransform();
6561    parentTransform.append(parent.localTransform);
6562  }
6563  return parentTransform;
6564}
6565
6566function multiplyHexColors(color1, color2) {
6567  if (color1 === 16777215 || !color2)
6568    return color2;
6569  if (color2 === 16777215 || !color1)
6570    return color1;
6571  const r1 = color1 >> 16 & 255;
6572  const g1 = color1 >> 8 & 255;
6573  const b1 = color1 & 255;
6574  const r2 = color2 >> 16 & 255;
6575  const g2 = color2 >> 8 & 255;
6576  const b2 = color2 & 255;
6577  const r = r1 * r2 / 255 | 0;
6578  const g = g1 * g2 / 255 | 0;
6579  const b = b1 * b2 / 255 | 0;
6580  return (r << 16) + (g << 8) + b;
6581}
6582
6583const WHITE_BGR = 16777215;
6584function multiplyColors(localBGRColor, parentBGRColor) {
6585  if (localBGRColor === WHITE_BGR) {
6586    return parentBGRColor;
6587  }
6588  if (parentBGRColor === WHITE_BGR) {
6589    return localBGRColor;
6590  }
6591  return multiplyHexColors(localBGRColor, parentBGRColor);
6592}
6593
6594function bgr2rgb(color) {
6595  return ((color & 255) << 16) + (color & 65280) + (color >> 16 & 255);
6596}
6597const getGlobalMixin = {
6598  getGlobalAlpha(skipUpdate) {
6599    if (skipUpdate) {
6600      if (this.renderGroup) {
6601        return this.renderGroup.worldAlpha;
6602      }
6603      if (this.parentRenderGroup) {
6604        return this.parentRenderGroup.worldAlpha * this.alpha;
6605      }
6606      return this.alpha;
6607    }
6608    let alpha = this.alpha;
6609    let current = this.parent;
6610    while (current) {
6611      alpha *= current.alpha;
6612      current = current.parent;
6613    }
6614    return alpha;
6615  },
6616  getGlobalTransform(matrix = new Matrix(), skipUpdate) {
6617    if (skipUpdate) {
6618      return matrix.copyFrom(this.worldTransform);
6619    }
6620    this.updateLocalTransform();
6621    const parentTransform = updateTransformBackwards(this, matrixPool.get().identity());
6622    matrix.appendFrom(this.localTransform, parentTransform);
6623    matrixPool.return(parentTransform);
6624    return matrix;
6625  },
6626  getGlobalTint(skipUpdate) {
6627    if (skipUpdate) {
6628      if (this.renderGroup) {
6629        return bgr2rgb(this.renderGroup.worldColor);
6630      }
6631      if (this.parentRenderGroup) {
6632        return bgr2rgb(
6633          multiplyColors(this.localColor, this.parentRenderGroup.worldColor)
6634        );
6635      }
6636      return this.tint;
6637    }
6638    let color = this.localColor;
6639    let parent = this.parent;
6640    while (parent) {
6641      color = multiplyColors(color, parent.localColor);
6642      parent = parent.parent;
6643    }
6644    return bgr2rgb(color);
6645  }
6646};
6647
6648function getLocalBounds(target, bounds, relativeMatrix) {
6649  bounds.clear();
6650  relativeMatrix || (relativeMatrix = Matrix.IDENTITY);
6651  _getLocalBounds(target, bounds, relativeMatrix, target, true);
6652  if (!bounds.isValid) {
6653    bounds.set(0, 0, 0, 0);
6654  }
6655  return bounds;
6656}
6657function _getLocalBounds(target, bounds, parentTransform, rootContainer, isRoot) {
6658  let relativeTransform;
6659  if (!isRoot) {
6660    if (!target.visible || !target.measurable)
6661      return;
6662    target.updateLocalTransform();
6663    const localTransform = target.localTransform;
6664    relativeTransform = matrixPool.get();
6665    relativeTransform.appendFrom(localTransform, parentTransform);
6666  } else {
6667    relativeTransform = matrixPool.get();
6668    relativeTransform = parentTransform.copyTo(relativeTransform);
6669  }
6670  const parentBounds = bounds;
6671  const preserveBounds = !!target.effects.length;
6672  if (preserveBounds) {
6673    bounds = boundsPool.get().clear();
6674  }
6675  if (target.boundsArea) {
6676    bounds.addRect(target.boundsArea, relativeTransform);
6677  } else {
6678    if (target.renderPipeId) {
6679      bounds.matrix = relativeTransform;
6680      bounds.addBounds(target.bounds);
6681    }
6682    const children = target.children;
6683    for (let i = 0; i < children.length; i++) {
6684      _getLocalBounds(children[i], bounds, relativeTransform, rootContainer, false);
6685    }
6686  }
6687  if (preserveBounds) {
6688    for (let i = 0; i < target.effects.length; i++) {
6689      target.effects[i].addLocalBounds?.(bounds, rootContainer);
6690    }
6691    parentBounds.addBounds(bounds, Matrix.IDENTITY);
6692    boundsPool.return(bounds);
6693  }
6694  matrixPool.return(relativeTransform);
6695}
6696
6697function checkChildrenDidChange(container, previousData) {
6698  const children = container.children;
6699  for (let i = 0; i < children.length; i++) {
6700    const child = children[i];
6701    const uid = child.uid;
6702    const didChange = (child._didViewChangeTick & 65535) << 16 | child._didContainerChangeTick & 65535;
6703    const index = previousData.index;
6704    if (previousData.data[index] !== uid || previousData.data[index + 1] !== didChange) {
6705      previousData.data[previousData.index] = uid;
6706      previousData.data[previousData.index + 1] = didChange;
6707      previousData.didChange = true;
6708    }
6709    previousData.index = index + 2;
6710    if (child.children.length) {
6711      checkChildrenDidChange(child, previousData);
6712    }
6713  }
6714  return previousData.didChange;
6715}
6716
6717const tempMatrix$1 = new Matrix();
6718const measureMixin = {
6719  _localBoundsCacheId: -1,
6720  _localBoundsCacheData: null,
6721  _setWidth(value, localWidth) {
6722    const sign = Math.sign(this.scale.x) || 1;
6723    if (localWidth !== 0) {
6724      this.scale.x = value / localWidth * sign;
6725    } else {
6726      this.scale.x = sign;
6727    }
6728  },
6729  _setHeight(value, localHeight) {
6730    const sign = Math.sign(this.scale.y) || 1;
6731    if (localHeight !== 0) {
6732      this.scale.y = value / localHeight * sign;
6733    } else {
6734      this.scale.y = sign;
6735    }
6736  },
6737  getLocalBounds() {
6738    if (!this._localBoundsCacheData) {
6739      this._localBoundsCacheData = {
6740        data: [],
6741        index: 1,
6742        didChange: false,
6743        localBounds: new Bounds()
6744      };
6745    }
6746    const localBoundsCacheData = this._localBoundsCacheData;
6747    localBoundsCacheData.index = 1;
6748    localBoundsCacheData.didChange = false;
6749    if (localBoundsCacheData.data[0] !== this._didViewChangeTick) {
6750      localBoundsCacheData.didChange = true;
6751      localBoundsCacheData.data[0] = this._didViewChangeTick;
6752    }
6753    checkChildrenDidChange(this, localBoundsCacheData);
6754    if (localBoundsCacheData.didChange) {
6755      getLocalBounds(this, localBoundsCacheData.localBounds, tempMatrix$1);
6756    }
6757    return localBoundsCacheData.localBounds;
6758  },
6759  getBounds(skipUpdate, bounds) {
6760    return getGlobalBounds(this, skipUpdate, bounds || new Bounds());
6761  }
6762};
6763
6764const onRenderMixin = {
6765  _onRender: null,
6766  set onRender(func) {
6767    const renderGroup = this.renderGroup || this.parentRenderGroup;
6768    if (!func) {
6769      if (this._onRender) {
6770        renderGroup?.removeOnRender(this);
6771      }
6772      this._onRender = null;
6773      return;
6774    }
6775    if (!this._onRender) {
6776      renderGroup?.addOnRender(this);
6777    }
6778    this._onRender = func;
6779  },
6780  get onRender() {
6781    return this._onRender;
6782  }
6783};
6784
6785const sortMixin = {
6786  _zIndex: 0,
6787  sortDirty: false,
6788  sortableChildren: false,
6789  get zIndex() {
6790    return this._zIndex;
6791  },
6792  set zIndex(value) {
6793    if (this._zIndex === value)
6794      return;
6795    this._zIndex = value;
6796    this.depthOfChildModified();
6797  },
6798  depthOfChildModified() {
6799    if (this.parent) {
6800      this.parent.sortableChildren = true;
6801      this.parent.sortDirty = true;
6802    }
6803    if (this.parentRenderGroup) {
6804      this.parentRenderGroup.structureDidChange = true;
6805    }
6806  },
6807  sortChildren() {
6808    if (!this.sortDirty)
6809      return;
6810    this.sortDirty = false;
6811    this.children.sort(sortChildren);
6812  }
6813};
6814function sortChildren(a, b) {
6815  return a._zIndex - b._zIndex;
6816}
6817
6818const toLocalGlobalMixin = {
6819  getGlobalPosition(point = new Point(), skipUpdate = false) {
6820    if (this.parent) {
6821      this.parent.toGlobal(this._position, point, skipUpdate);
6822    } else {
6823      point.x = this._position.x;
6824      point.y = this._position.y;
6825    }
6826    return point;
6827  },
6828  toGlobal(position, point, skipUpdate = false) {
6829    const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);
6830    point = globalMatrix.apply(position, point);
6831    matrixPool.return(globalMatrix);
6832    return point;
6833  },
6834  toLocal(position, from, point, skipUpdate) {
6835    if (from) {
6836      position = from.toGlobal(position, point, skipUpdate);
6837    }
6838    const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);
6839    point = globalMatrix.applyInverse(position, point);
6840    matrixPool.return(globalMatrix);
6841    return point;
6842  }
6843};
6844
6845class InstructionSet {
6846  constructor() {
6847    /** a unique id for this instruction set used through the renderer */
6848    this.uid = uid("instructionSet");
6849    /** the array of instructions */
6850    this.instructions = [];
6851    /** the actual size of the array (any instructions passed this should be ignored) */
6852    this.instructionSize = 0;
6853    this.renderables = [];
6854    /** used by the garbage collector to track when the instruction set was last used */
6855    this.gcTick = 0;
6856  }
6857  /** reset the instruction set so it can be reused set size back to 0 */
6858  reset() {
6859    this.instructionSize = 0;
6860  }
6861  /**
6862   * Destroy the instruction set, clearing the instructions and renderables.
6863   * @internal
6864   */
6865  destroy() {
6866    this.instructions.length = 0;
6867    this.renderables.length = 0;
6868    this.renderPipes = null;
6869    this.gcTick = 0;
6870  }
6871  /**
6872   * Add an instruction to the set
6873   * @param instruction - add an instruction to the set
6874   */
6875  add(instruction) {
6876    this.instructions[this.instructionSize++] = instruction;
6877  }
6878  /**
6879   * Log the instructions to the console (for debugging)
6880   * @internal
6881   */
6882  log() {
6883    this.instructions.length = this.instructionSize;
6884    console.table(this.instructions, ["type", "action"]);
6885  }
6886}
6887
6888let count = 0;
6889class TexturePoolClass {
6890  /**
6891   * @param textureOptions - options that will be passed to BaseRenderTexture constructor
6892   * @param {SCALE_MODE} [textureOptions.scaleMode] - See {@link SCALE_MODE} for possible values.
6893   */
6894  constructor(textureOptions) {
6895    this._poolKeyHash = /* @__PURE__ */ Object.create(null);
6896    this._texturePool = {};
6897    this.textureOptions = textureOptions || {};
6898    this.enableFullScreen = false;
6899    this.textureStyle = new TextureStyle(this.textureOptions);
6900  }
6901  /**
6902   * Creates texture with params that were specified in pool constructor.
6903   * @param pixelWidth - Width of texture in pixels.
6904   * @param pixelHeight - Height of texture in pixels.
6905   * @param antialias
6906   */
6907  createTexture(pixelWidth, pixelHeight, antialias) {
6908    const textureSource = new TextureSource({
6909      ...this.textureOptions,
6910      width: pixelWidth,
6911      height: pixelHeight,
6912      resolution: 1,
6913      antialias,
6914      autoGarbageCollect: false
6915    });
6916    return new Texture({
6917      source: textureSource,
6918      label: `texturePool_${count++}`
6919    });
6920  }
6921  /**
6922   * Gets a Power-of-Two render texture or fullScreen texture
6923   * @param frameWidth - The minimum width of the render texture.
6924   * @param frameHeight - The minimum height of the render texture.
6925   * @param resolution - The resolution of the render texture.
6926   * @param antialias
6927   * @returns The new render texture.
6928   */
6929  getOptimalTexture(frameWidth, frameHeight, resolution = 1, antialias) {
6930    let po2Width = Math.ceil(frameWidth * resolution - 1e-6);
6931    let po2Height = Math.ceil(frameHeight * resolution - 1e-6);
6932    po2Width = nextPow2(po2Width);
6933    po2Height = nextPow2(po2Height);
6934    const key = (po2Width << 17) + (po2Height << 1) + (antialias ? 1 : 0);
6935    if (!this._texturePool[key]) {
6936      this._texturePool[key] = [];
6937    }
6938    let texture = this._texturePool[key].pop();
6939    if (!texture) {
6940      texture = this.createTexture(po2Width, po2Height, antialias);
6941    }
6942    texture.source._resolution = resolution;
6943    texture.source.width = po2Width / resolution;
6944    texture.source.height = po2Height / resolution;
6945    texture.source.pixelWidth = po2Width;
6946    texture.source.pixelHeight = po2Height;
6947    texture.frame.x = 0;
6948    texture.frame.y = 0;
6949    texture.frame.width = frameWidth;
6950    texture.frame.height = frameHeight;
6951    texture.updateUvs();
6952    this._poolKeyHash[texture.uid] = key;
6953    return texture;
6954  }
6955  /**
6956   * Gets extra texture of the same size as input renderTexture
6957   * @param texture - The texture to check what size it is.
6958   * @param antialias - Whether to use antialias.
6959   * @returns A texture that is a power of two
6960   */
6961  getSameSizeTexture(texture, antialias = false) {
6962    const source = texture.source;
6963    return this.getOptimalTexture(texture.width, texture.height, source._resolution, antialias);
6964  }
6965  /**
6966   * Place a render texture back into the pool. Optionally reset the style of the texture to the default texture style.
6967   * useful if you modified the style of the texture after getting it from the pool.
6968   * @param renderTexture - The renderTexture to free
6969   * @param resetStyle - Whether to reset the style of the texture to the default texture style
6970   */
6971  returnTexture(renderTexture, resetStyle = false) {
6972    const key = this._poolKeyHash[renderTexture.uid];
6973    if (resetStyle) {
6974      renderTexture.source.style = this.textureStyle;
6975    }
6976    this._texturePool[key].push(renderTexture);
6977  }
6978  /**
6979   * Clears the pool.
6980   * @param destroyTextures - Destroy all stored textures.
6981   */
6982  clear(destroyTextures) {
6983    destroyTextures = destroyTextures !== false;
6984    if (destroyTextures) {
6985      for (const i in this._texturePool) {
6986        const textures = this._texturePool[i];
6987        if (textures) {
6988          for (let j = 0; j < textures.length; j++) {
6989            textures[j].destroy(true);
6990          }
6991        }
6992      }
6993    }
6994    this._texturePool = {};
6995  }
6996}
6997const TexturePool = new TexturePoolClass();
6998GlobalResourceRegistry.register(TexturePool);
6999
7000class RenderGroup {
7001  constructor() {
7002    this.renderPipeId = "renderGroup";
7003    this.root = null;
7004    this.canBundle = false;
7005    this.renderGroupParent = null;
7006    this.renderGroupChildren = [];
7007    this.worldTransform = new Matrix();
7008    this.worldColorAlpha = 4294967295;
7009    this.worldColor = 16777215;
7010    this.worldAlpha = 1;
7011    // these updates are transform changes..
7012    this.childrenToUpdate = /* @__PURE__ */ Object.create(null);
7013    this.updateTick = 0;
7014    this.gcTick = 0;
7015    // these update are renderable changes..
7016    this.childrenRenderablesToUpdate = { list: [], index: 0 };
7017    // other
7018    this.structureDidChange = true;
7019    this.instructionSet = new InstructionSet();
7020    this._onRenderContainers = [];
7021    /**
7022     * Indicates if the cached texture needs to be updated.
7023     * @default true
7024     */
7025    this.textureNeedsUpdate = true;
7026    /**
7027     * Indicates if the container should be cached as a texture.
7028     * @default false
7029     */
7030    this.isCachedAsTexture = false;
vendor: 8,393 bytes, lines 7031-7283
7031    this._matrixDirty = 7;
7032  }
7033  init(root) {
7034    this.root = root;
7035    if (root._onRender)
7036      this.addOnRender(root);
7037    root.didChange = true;
7038    const children = root.children;
7039    for (let i = 0; i < children.length; i++) {
7040      const child = children[i];
7041      child._updateFlags = 15;
7042      this.addChild(child);
7043    }
7044  }
7045  enableCacheAsTexture(options = {}) {
7046    this.textureOptions = options;
7047    this.isCachedAsTexture = true;
7048    this.textureNeedsUpdate = true;
7049  }
7050  disableCacheAsTexture() {
7051    this.isCachedAsTexture = false;
7052    if (this.texture) {
7053      TexturePool.returnTexture(this.texture, true);
7054      this.texture = null;
7055    }
7056  }
7057  updateCacheTexture() {
7058    this.textureNeedsUpdate = true;
7059    const cachedParent = this._parentCacheAsTextureRenderGroup;
7060    if (cachedParent && !cachedParent.textureNeedsUpdate) {
7061      cachedParent.updateCacheTexture();
7062    }
7063  }
7064  reset() {
7065    this.renderGroupChildren.length = 0;
7066    for (const i in this.childrenToUpdate) {
7067      const childrenAtDepth = this.childrenToUpdate[i];
7068      childrenAtDepth.list.fill(null);
7069      childrenAtDepth.index = 0;
7070    }
7071    this.childrenRenderablesToUpdate.index = 0;
7072    this.childrenRenderablesToUpdate.list.fill(null);
7073    this.root = null;
7074    this.updateTick = 0;
7075    this.structureDidChange = true;
7076    this._onRenderContainers.length = 0;
7077    this.renderGroupParent = null;
7078    this.disableCacheAsTexture();
7079  }
7080  get localTransform() {
7081    return this.root.localTransform;
7082  }
7083  addRenderGroupChild(renderGroupChild) {
7084    if (renderGroupChild.renderGroupParent) {
7085      renderGroupChild.renderGroupParent._removeRenderGroupChild(renderGroupChild);
7086    }
7087    renderGroupChild.renderGroupParent = this;
7088    this.renderGroupChildren.push(renderGroupChild);
7089  }
7090  _removeRenderGroupChild(renderGroupChild) {
7091    const index = this.renderGroupChildren.indexOf(renderGroupChild);
7092    if (index > -1) {
7093      this.renderGroupChildren.splice(index, 1);
7094    }
7095    renderGroupChild.renderGroupParent = null;
7096  }
7097  addChild(child) {
7098    this.structureDidChange = true;
7099    child.parentRenderGroup = this;
7100    child.updateTick = -1;
7101    if (child.parent === this.root) {
7102      child.relativeRenderGroupDepth = 1;
7103    } else {
7104      child.relativeRenderGroupDepth = child.parent.relativeRenderGroupDepth + 1;
7105    }
7106    child.didChange = true;
7107    this.onChildUpdate(child);
7108    if (child.renderGroup) {
7109      this.addRenderGroupChild(child.renderGroup);
7110      return;
7111    }
7112    if (child._onRender)
7113      this.addOnRender(child);
7114    const children = child.children;
7115    for (let i = 0; i < children.length; i++) {
7116      this.addChild(children[i]);
7117    }
7118  }
7119  removeChild(child) {
7120    this.structureDidChange = true;
7121    if (child._onRender) {
7122      if (!child.renderGroup) {
7123        this.removeOnRender(child);
7124      }
7125    }
7126    child.parentRenderGroup = null;
7127    if (child.renderGroup) {
7128      this._removeRenderGroupChild(child.renderGroup);
7129      return;
7130    }
7131    const children = child.children;
7132    for (let i = 0; i < children.length; i++) {
7133      this.removeChild(children[i]);
7134    }
7135  }
7136  removeChildren(children) {
7137    for (let i = 0; i < children.length; i++) {
7138      this.removeChild(children[i]);
7139    }
7140  }
7141  onChildUpdate(child) {
7142    let childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth];
7143    if (!childrenToUpdate) {
7144      childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth] = {
7145        index: 0,
7146        list: []
7147      };
7148    }
7149    childrenToUpdate.list[childrenToUpdate.index++] = child;
7150  }
7151  updateRenderable(renderable) {
7152    if (renderable.globalDisplayStatus < 7)
7153      return;
7154    this.instructionSet.renderPipes[renderable.renderPipeId].updateRenderable(renderable);
7155    renderable.didViewUpdate = false;
7156  }
7157  onChildViewUpdate(child) {
7158    this.childrenRenderablesToUpdate.list[this.childrenRenderablesToUpdate.index++] = child;
7159  }
7160  get isRenderable() {
7161    return this.root.localDisplayStatus === 7 && this.worldAlpha > 0;
7162  }
7163  /**
7164   * adding a container to the onRender list will make sure the user function
7165   * passed in to the user defined 'onRender` callBack
7166   * @param container - the container to add to the onRender list
7167   */
7168  addOnRender(container) {
7169    this._onRenderContainers.push(container);
7170  }
7171  removeOnRender(container) {
7172    this._onRenderContainers.splice(this._onRenderContainers.indexOf(container), 1);
7173  }
7174  runOnRender(renderer) {
7175    for (let i = 0; i < this._onRenderContainers.length; i++) {
7176      this._onRenderContainers[i]._onRender(renderer);
7177    }
7178  }
7179  destroy() {
7180    this.disableCacheAsTexture();
7181    this.renderGroupParent = null;
7182    this.root = null;
7183    this.childrenRenderablesToUpdate = null;
7184    this.childrenToUpdate = null;
7185    this.renderGroupChildren = null;
7186    this._onRenderContainers = null;
7187    this.instructionSet = null;
7188  }
7189  getChildren(out = []) {
7190    const children = this.root.children;
7191    for (let i = 0; i < children.length; i++) {
7192      this._getChildren(children[i], out);
7193    }
7194    return out;
7195  }
7196  _getChildren(container, out = []) {
7197    out.push(container);
7198    if (container.renderGroup)
7199      return out;
7200    const children = container.children;
7201    for (let i = 0; i < children.length; i++) {
7202      this._getChildren(children[i], out);
7203    }
7204    return out;
7205  }
7206  invalidateMatrices() {
7207    this._matrixDirty = 7;
7208  }
7209  /**
7210   * Returns the inverse of the world transform matrix.
7211   * @returns {Matrix} The inverse of the world transform matrix.
7212   */
7213  get inverseWorldTransform() {
7214    if ((this._matrixDirty & 1) === 0)
7215      return this._inverseWorldTransform;
7216    this._matrixDirty &= -2;
7217    this._inverseWorldTransform || (this._inverseWorldTransform = new Matrix());
7218    return this._inverseWorldTransform.copyFrom(this.worldTransform).invert();
7219  }
7220  /**
7221   * Returns the inverse of the texture offset transform matrix.
7222   * @returns {Matrix} The inverse of the texture offset transform matrix.
7223   */
7224  get textureOffsetInverseTransform() {
7225    if ((this._matrixDirty & 2) === 0)
7226      return this._textureOffsetInverseTransform;
7227    this._matrixDirty &= -3;
7228    this._textureOffsetInverseTransform || (this._textureOffsetInverseTransform = new Matrix());
7229    return this._textureOffsetInverseTransform.copyFrom(this.inverseWorldTransform).translate(
7230      -this._textureBounds.x,
7231      -this._textureBounds.y
7232    );
7233  }
7234  /**
7235   * Returns the inverse of the parent texture transform matrix.
7236   * This is used to properly transform coordinates when rendering into cached textures.
7237   * @returns {Matrix} The inverse of the parent texture transform matrix.
7238   */
7239  get inverseParentTextureTransform() {
7240    if ((this._matrixDirty & 4) === 0)
7241      return this._inverseParentTextureTransform;
7242    this._matrixDirty &= -5;
7243    const parentCacheAsTexture = this._parentCacheAsTextureRenderGroup;
7244    if (parentCacheAsTexture) {
7245      this._inverseParentTextureTransform || (this._inverseParentTextureTransform = new Matrix());
7246      return this._inverseParentTextureTransform.copyFrom(this.worldTransform).prepend(parentCacheAsTexture.inverseWorldTransform).translate(
7247        -parentCacheAsTexture._textureBounds.x,
7248        -parentCacheAsTexture._textureBounds.y
7249      );
7250    }
7251    return this.worldTransform;
7252  }
7253  /**
7254   * Returns a matrix that transforms coordinates to the correct coordinate space of the texture being rendered to.
7255   * This is the texture offset inverse transform of the closest parent RenderGroup that is cached as a texture.
7256   * @returns {Matrix | null} The transform matrix for the cached texture coordinate space,
7257   * or null if no parent is cached as texture.
7258   */
7259  get cacheToLocalTransform() {
7260    if (this.isCachedAsTexture) {
7261      return this.textureOffsetInverseTransform;
7262    }
7263    if (!this._parentCacheAsTextureRenderGroup)
7264      return null;
7265    return this._parentCacheAsTextureRenderGroup.textureOffsetInverseTransform;
7266  }
7267}
7268
7269function assignWithIgnore(target, options, ignore = {}) {
7270  for (const key in options) {
7271    if (!ignore[key] && options[key] !== void 0) {
7272      target[key] = options[key];
7273    }
7274  }
7275}
7276
7277const defaultSkew = new ObservablePoint(null);
7278const defaultPivot = new ObservablePoint(null);
7279const defaultScale = new ObservablePoint(null, 1, 1);
7280const defaultOrigin = new ObservablePoint(null);
7281const UPDATE_COLOR = 1;
7282const UPDATE_BLEND = 2;
7283const UPDATE_VISIBLE = 4;
7284class Container extends EventEmitter {
7285  constructor(options = {}) {
7286    super();
7287    /**
7288     * unique id for this container
7289     * @internal
7290     */
7291    this.uid = uid("renderable");
7292    /** @private */
7293    this._updateFlags = 15;
7294    // the render group this container owns
7295    /** @private */
7296    this.renderGroup = null;
7297    // the render group this container belongs to
7298    /** @private */
7299    this.parentRenderGroup = null;
7300    // the index of the container in the render group
7301    /** @private */
7302    this.parentRenderGroupIndex = 0;
7303    // set to true if the container has changed. It is reset once the changes have been applied
7304    // by the transform system
7305    // its here to stop ensure that when things change, only one update gets registers with the transform system
7306    /** @private */
7307    this.didChange = false;
7308    // same as above, but for the renderable
7309    /** @private */
7310    this.didViewUpdate = false;
7311    // how deep is the container relative to its render group..
7312    // unless the element is the root render group - it will be relative to its parent
7313    /** @private */
7314    this.relativeRenderGroupDepth = 0;
7315    /**
7316     * The array of children of this container. Each child must be a Container or extend from it.
7317     *
7318     * The array is read-only, but its contents can be modified using Container methods.
7319     * @example
7320     * ```ts
7321     * // Access children
7322     * const firstChild = container.children[0];
7323     * const lastChild = container.children[container.children.length - 1];
7324     * ```
7325     * @readonly
7326     * @see {@link Container#addChild} For adding children
7327     * @see {@link Container#removeChild} For removing children
7328     */
7329    this.children = [];
7330    /**
7331     * The display object container that contains this display object.
7332     * This represents the parent-child relationship in the display tree.
7333     * @example
7334     * ```ts
7335     * // Basic parent access
7336     * const parent = sprite.parent;
7337     *
7338     * // Walk up the tree
7339     * let current = sprite;
7340     * while (current.parent) {
7341     *     console.log('Level up:', current.parent.constructor.name);
7342     *     current = current.parent;
7343     * }
7344     * ```
7345     * @readonly
7346     * @see {@link Container#addChild} For adding to a parent
7347     * @see {@link Container#removeChild} For removing from parent
7348     */
7349    this.parent = null;
7350    // used internally for changing up the render order.. mainly for masks and filters
7351    // TODO setting this should cause a rebuild??
7352    /** @private */
7353    this.includeInBuild = true;
7354    /** @private */
7355    this.measurable = true;
7356    /** @private */
7357    this.isSimple = true;
7358    /**
7359     * The RenderLayer this container belongs to, if any.
7360     * If it belongs to a RenderLayer, it will be rendered from the RenderLayer's position in the scene.
7361     * @readonly
7362     * @advanced
7363     */
7364    this.parentRenderLayer = null;
7365    // / /////////////Transform related props//////////////
7366    // used by the transform system to check if a container needs to be updated that frame
7367    // if the tick matches the current transform system tick, it is not updated again
7368    /** @internal */
7369    this.updateTick = -1;
7370    /**
7371     * Current transform of the object based on local factors: position, scale, other stuff.
7372     * This matrix represents the local transformation without any parent influence.
7373     * @example
7374     * ```ts
7375     * // Basic transform access
7376     * const localMatrix = sprite.localTransform;
7377     * console.log(localMatrix.toString());
7378     * ```
7379     * @readonly
7380     * @see {@link Container#worldTransform} For global transform
7381     * @see {@link Container#groupTransform} For render group transform
7382     */
7383    this.localTransform = new Matrix();
7384    /**
7385     * The relative group transform is a transform relative to the render group it belongs too. It will include all parent
7386     * transforms and up to the render group (think of it as kind of like a stage - but the stage can be nested).
7387     * If this container is is self a render group matrix will be relative to its parent render group
7388     * @readonly
7389     * @advanced
7390     */
7391    this.relativeGroupTransform = new Matrix();
7392    /**
7393     * The group transform is a transform relative to the render group it belongs too.
7394     * If this container is render group then this will be an identity matrix. other wise it
7395     * will be the same as the relativeGroupTransform.
7396     * Use this value when actually rendering things to the screen
7397     * @readonly
7398     * @advanced
7399     */
7400    this.groupTransform = this.relativeGroupTransform;
7401    /**
7402     * Whether this object has been destroyed. If true, the object should no longer be used.
7403     * After an object is destroyed, all of its functionality is disabled and references are removed.
7404     * @example
7405     * ```ts
7406     * // Cleanup with destroy
7407     * sprite.destroy();
7408     * console.log(sprite.destroyed); // true
7409     * ```
7410     * @default false
7411     * @see {@link Container#destroy} For destroying objects
7412     */
7413    this.destroyed = false;
vendor: 5,870 bytes, lines 7414-7605
7414    // transform data..
7415    /**
7416     * The coordinate of the object relative to the local coordinates of the parent.
7417     * @internal
7418     */
7419    this._position = new ObservablePoint(this, 0, 0);
7420    /**
7421     * The scale factor of the object.
7422     * @internal
7423     */
7424    this._scale = defaultScale;
7425    /**
7426     * The pivot point of the container that it rotates around.
7427     * @internal
7428     */
7429    this._pivot = defaultPivot;
7430    /**
7431     * The origin point around which the container rotates and scales.
7432     * Unlike pivot, changing origin will not move the container's position.
7433     * @private
7434     */
7435    this._origin = defaultOrigin;
7436    /**
7437     * The skew amount, on the x and y axis.
7438     * @internal
7439     */
7440    this._skew = defaultSkew;
7441    /**
7442     * The X-coordinate value of the normalized local X axis,
7443     * the first column of the local transformation matrix without a scale.
7444     * @internal
7445     */
7446    this._cx = 1;
7447    /**
7448     * The Y-coordinate value of the normalized local X axis,
7449     * the first column of the local transformation matrix without a scale.
7450     * @internal
7451     */
7452    this._sx = 0;
7453    /**
7454     * The X-coordinate value of the normalized local Y axis,
7455     * the second column of the local transformation matrix without a scale.
7456     * @internal
7457     */
7458    this._cy = 0;
7459    /**
7460     * The Y-coordinate value of the normalized local Y axis,
7461     * the second column of the local transformation matrix without a scale.
7462     * @internal
7463     */
7464    this._sy = 1;
7465    /**
7466     * The rotation amount.
7467     * @internal
7468     */
7469    this._rotation = 0;
7470    // / COLOR related props //////////////
7471    // color stored as ABGR
7472    /** @internal */
7473    this.localColor = 16777215;
7474    /** @internal */
7475    this.localAlpha = 1;
7476    /** @internal */
7477    this.groupAlpha = 1;
7478    // A
7479    /** @internal */
7480    this.groupColor = 16777215;
7481    // BGR
7482    /** @internal */
7483    this.groupColorAlpha = 4294967295;
7484    // ABGR
7485    // / BLEND related props //////////////
7486    /** @internal */
7487    this.localBlendMode = "inherit";
7488    /** @internal */
7489    this.groupBlendMode = "normal";
7490    // / VISIBILITY related props //////////////
7491    // visibility
7492    // 0b11
7493    // first bit is visible, second bit is renderable
7494    /**
7495     * This property holds three bits: culled, visible, renderable
7496     * the third bit represents culling (0 = culled, 1 = not culled) 0b100
7497     * the second bit represents visibility (0 = not visible, 1 = visible) 0b010
7498     * the first bit represents renderable (0 = not renderable, 1 = renderable) 0b001
7499     * @internal
7500     */
7501    this.localDisplayStatus = 7;
7502    // 0b11 | 0b10 | 0b01 | 0b00
7503    /** @internal */
7504    this.globalDisplayStatus = 7;
7505    /**
7506     * A value that increments each time the containe is modified
7507     * eg children added, removed etc
7508     * @ignore
7509     */
7510    this._didContainerChangeTick = 0;
7511    /**
7512     * A value that increments each time the container view is modified
7513     * eg texture swap, geometry change etc
7514     * @ignore
7515     */
7516    this._didViewChangeTick = 0;
7517    /**
7518     * property that tracks if the container transform has changed
7519     * @ignore
7520     */
7521    this._didLocalTransformChangeId = -1;
7522    this.effects = [];
7523    assignWithIgnore(this, options, {
7524      children: true,
7525      parent: true,
7526      effects: true
7527    });
7528    options.children?.forEach((child) => this.addChild(child));
7529    options.parent?.addChild(this);
7530  }
7531  /**
7532   * Mixes all enumerable properties and methods from a source object to Container.
7533   * @param source - The source of properties and methods to mix in.
7534   * @deprecated since 8.8.0
7535   */
7536  static mixin(source) {
7537    deprecation("8.8.0", "Container.mixin is deprecated, please use extensions.mixin instead.");
7538    extensions.mixin(Container, source);
7539  }
7540  // = 'default';
7541  /**
7542   * We now use the _didContainerChangeTick and _didViewChangeTick to track changes
7543   * @deprecated since 8.2.6
7544   * @ignore
7545   */
7546  set _didChangeId(value) {
7547    this._didViewChangeTick = value >> 12 & 4095;
7548    this._didContainerChangeTick = value & 4095;
7549  }
7550  /** @ignore */
7551  get _didChangeId() {
7552    return this._didContainerChangeTick & 4095 | (this._didViewChangeTick & 4095) << 12;
7553  }
7554  /**
7555   * Adds one or more children to the container.
7556   * The children will be rendered as part of this container's display list.
7557   * @example
7558   * ```ts
7559   * // Add a single child
7560   * container.addChild(sprite);
7561   *
7562   * // Add multiple children
7563   * container.addChild(background, player, foreground);
7564   *
7565   * // Add with type checking
7566   * const sprite = container.addChild<Sprite>(new Sprite(texture));
7567   * sprite.tint = 'red';
7568   * ```
7569   * @param children - The Container(s) to add to the container
7570   * @returns The first child that was added
7571   * @see {@link Container#removeChild} For removing children
7572   * @see {@link Container#addChildAt} For adding at specific index
7573   */
7574  addChild(...children) {
7575    if (!this.allowChildren) {
7576      deprecation(v8_0_0, "addChild: Only Containers will be allowed to add children in v8.0.0");
7577    }
7578    if (children.length > 1) {
7579      for (let i = 0; i < children.length; i++) {
7580        this.addChild(children[i]);
7581      }
7582      return children[0];
7583    }
7584    const child = children[0];
7585    const renderGroup = this.renderGroup || this.parentRenderGroup;
7586    if (child.parent === this) {
7587      this.children.splice(this.children.indexOf(child), 1);
7588      this.children.push(child);
7589      if (renderGroup) {
7590        renderGroup.structureDidChange = true;
7591      }
7592      return child;
7593    }
7594    if (child.parent) {
7595      child.parent.removeChild(child);
7596    }
7597    this.children.push(child);
7598    if (this.sortableChildren)
7599      this.sortDirty = true;
7600    child.parent = this;
7601    child.didChange = true;
7602    child._updateFlags = 15;
7603    if (renderGroup) {
7604      renderGroup.addChild(child);
7605    }
7606    this.emit("childAdded", child, this, this.children.length - 1);
7607    child.emit("added", this);
7608    this._didViewChangeTick++;
7609    if (child._zIndex !== 0) {
7610      child.depthOfChildModified();
7611    }
7612    return child;
7613  }
7614  /**
7615   * Removes one or more children from the container.
7616   * When removing multiple children, events will be triggered for each child in sequence.
7617   * @example
7618   * ```ts
7619   * // Remove a single child
7620   * const removed = container.removeChild(sprite);
7621   *
7622   * // Remove multiple children
7623   * const bg = container.removeChild(background, player, userInterface);
7624   *
7625   * // Remove with type checking
7626   * const sprite = container.removeChild<Sprite>(childSprite);
7627   * sprite.texture = newTexture;
7628   * ```
7629   * @param children - The Container(s) to remove
7630   * @returns The first child that was removed
7631   * @see {@link Container#addChild} For adding children
7632   * @see {@link Container#removeChildren} For removing multiple children
7633   */
7634  removeChild(...children) {
7635    if (children.length > 1) {
7636      for (let i = 0; i < children.length; i++) {
7637        this.removeChild(children[i]);
7638      }
7639      return children[0];
7640    }
7641    const child = children[0];
7642    const index = this.children.indexOf(child);
7643    if (index > -1) {
7644      this._didViewChangeTick++;
7645      this.children.splice(index, 1);
7646      if (this.renderGroup) {
7647        this.renderGroup.removeChild(child);
7648      } else if (this.parentRenderGroup) {
7649        this.parentRenderGroup.removeChild(child);
7650      }
7651      if (child.parentRenderLayer) {
7652        child.parentRenderLayer.detach(child);
7653      }
7654      child.parent = null;
7655      this.emit("childRemoved", child, this, index);
7656      child.emit("removed", this);
7657    }
7658    return child;
7659  }
7660  /** @ignore */
7661  _onUpdate(point) {
7662    if (point) {
7663      if (point === this._skew) {
7664        this._updateSkew();
7665      }
7666    }
7667    this._didContainerChangeTick++;
7668    if (this.didChange)
7669      return;
7670    this.didChange = true;
7671    if (this.parentRenderGroup) {
7672      this.parentRenderGroup.onChildUpdate(this);
7673    }
7674  }
7675  set isRenderGroup(value) {
7676    if (!!this.renderGroup === value)
7677      return;
7678    if (value) {
7679      this.enableRenderGroup();
7680    } else {
7681      this.disableRenderGroup();
7682    }
7683  }
7684  /**
7685   * Returns true if this container is a render group.
7686   * This means that it will be rendered as a separate pass, with its own set of instructions
7687   * @advanced
7688   */
7689  get isRenderGroup() {
7690    return !!this.renderGroup;
7691  }
7692  /**
7693   * Calling this enables a render group for this container.
7694   * This means it will be rendered as a separate set of instructions.
7695   * The transform of the container will also be handled on the GPU rather than the CPU.
7696   * @advanced
7697   */
7698  enableRenderGroup() {
7699    if (this.renderGroup)
7700      return;
7701    const parentRenderGroup = this.parentRenderGroup;
7702    parentRenderGroup?.removeChild(this);
7703    this.renderGroup = BigPool.get(RenderGroup, this);
7704    this.groupTransform = Matrix.IDENTITY;
7705    parentRenderGroup?.addChild(this);
7706    this._updateIsSimple();
7707  }
7708  /**
7709   * This will disable the render group for this container.
7710   * @advanced
7711   */
7712  disableRenderGroup() {
7713    if (!this.renderGroup)
7714      return;
7715    const parentRenderGroup = this.parentRenderGroup;
7716    parentRenderGroup?.removeChild(this);
7717    BigPool.return(this.renderGroup);
7718    this.renderGroup = null;
7719    this.groupTransform = this.relativeGroupTransform;
7720    parentRenderGroup?
vendor: 10,732 bytes, lines 7720-8068
7720.addChild(this);
7721    this._updateIsSimple();
7722  }
7723  /** @ignore */
7724  _updateIsSimple() {
7725    this.isSimple = !this.renderGroup && this.effects.length === 0;
7726  }
7727  /**
7728   * Current transform of the object based on world (parent) factors.
7729   *
7730   * This matrix represents the absolute transformation in the scene graph.
7731   * @example
7732   * ```ts
7733   * // Get world position
7734   * const worldPos = container.worldTransform;
7735   * console.log(`World position: (${worldPos.tx}, ${worldPos.ty})`);
7736   * ```
7737   * @readonly
7738   * @see {@link Container#localTransform} For local space transform
7739   */
7740  get worldTransform() {
7741    this._worldTransform || (this._worldTransform = new Matrix());
7742    if (this.renderGroup) {
7743      this._worldTransform.copyFrom(this.renderGroup.worldTransform);
7744    } else if (this.parentRenderGroup) {
7745      this._worldTransform.appendFrom(this.relativeGroupTransform, this.parentRenderGroup.worldTransform);
7746    }
7747    return this._worldTransform;
7748  }
7749  /**
7750   * The position of the container on the x axis relative to the local coordinates of the parent.
7751   *
7752   * An alias to position.x
7753   * @example
7754   * ```ts
7755   * // Basic position
7756   * container.x = 100;
7757   * ```
7758   */
7759  get x() {
7760    return this._position.x;
7761  }
7762  set x(value) {
7763    this._position.x = value;
7764  }
7765  /**
7766   * The position of the container on the y axis relative to the local coordinates of the parent.
7767   *
7768   * An alias to position.y
7769   * @example
7770   * ```ts
7771   * // Basic position
7772   * container.y = 200;
7773   * ```
7774   */
7775  get y() {
7776    return this._position.y;
7777  }
7778  set y(value) {
7779    this._position.y = value;
7780  }
7781  /**
7782   * The coordinate of the object relative to the local coordinates of the parent.
7783   * @example
7784   * ```ts
7785   * // Basic position setting
7786   * container.position.set(100, 200);
7787   * container.position.set(100); // Sets both x and y to 100
7788   * // Using point data
7789   * container.position = { x: 50, y: 75 };
7790   * ```
7791   * @since 4.0.0
7792   */
7793  get position() {
7794    return this._position;
7795  }
7796  set position(value) {
7797    this._position.copyFrom(value);
7798  }
7799  /**
7800   * The rotation of the object in radians.
7801   *
7802   * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;
7803   * > rotation is in radians, angle is in degrees.
7804   * @example
7805   * ```ts
7806   * // Basic rotation
7807   * container.rotation = Math.PI / 4; // 45 degrees
7808   *
7809   * // Convert from degrees
7810   * const degrees = 45;
7811   * container.rotation = degrees * Math.PI / 180;
7812   *
7813   * // Rotate around center
7814   * container.pivot.set(container.width / 2, container.height / 2);
7815   * container.rotation = Math.PI; // 180 degrees
7816   *
7817   * // Rotate around center with origin
7818   * container.origin.set(container.width / 2, container.height / 2);
7819   * container.rotation = Math.PI; // 180 degrees
7820   * ```
7821   */
7822  get rotation() {
7823    return this._rotation;
7824  }
7825  set rotation(value) {
7826    if (this._rotation !== value) {
7827      this._rotation = value;
7828      this._onUpdate(this._skew);
7829    }
7830  }
7831  /**
7832   * The angle of the object in degrees.
7833   *
7834   * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;
7835   * > rotation is in radians, angle is in degrees.
7836   * @example
7837   * ```ts
7838   * // Basic angle rotation
7839   * sprite.angle = 45; // 45 degrees
7840   *
7841   * // Rotate around center
7842   * sprite.pivot.set(sprite.width / 2, sprite.height / 2);
7843   * sprite.angle = 180; // Half rotation
7844   *
7845   * // Rotate around center with origin
7846   * sprite.origin.set(sprite.width / 2, sprite.height / 2);
7847   * sprite.angle = 180; // Half rotation
7848   *
7849   * // Reset rotation
7850   * sprite.angle = 0;
7851   * ```
7852   */
7853  get angle() {
7854    return this.rotation * RAD_TO_DEG;
7855  }
7856  set angle(value) {
7857    this.rotation = value * DEG_TO_RAD;
7858  }
7859  /**
7860   * The center of rotation, scaling, and skewing for this display object in its local space.
7861   * The `position` is the projection of `pivot` in the parent's local space.
7862   *
7863   * By default, the pivot is the origin (0, 0).
7864   * @example
7865   * ```ts
7866   * // Rotate around center
7867   * container.pivot.set(container.width / 2, container.height / 2);
7868   * container.rotation = Math.PI; // Rotates around center
7869   * ```
7870   * @since 4.0.0
7871   */
7872  get pivot() {
7873    if (this._pivot === defaultPivot) {
7874      this._pivot = new ObservablePoint(this, 0, 0);
7875    }
7876    return this._pivot;
7877  }
7878  set pivot(value) {
7879    if (this._pivot === defaultPivot) {
7880      this._pivot = new ObservablePoint(this, 0, 0);
7881      if (this._origin !== defaultOrigin) {
7882        warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`);
7883      }
7884    }
7885    typeof value === "number" ? this._pivot.set(value) : this._pivot.copyFrom(value);
7886  }
7887  /**
7888   * The skew factor for the object in radians. Skewing is a transformation that distorts
7889   * the object by rotating it differently at each point, creating a non-uniform shape.
7890   * @example
7891   * ```ts
7892   * // Basic skewing
7893   * container.skew.set(0.5, 0); // Skew horizontally
7894   * container.skew.set(0, 0.5); // Skew vertically
7895   *
7896   * // Skew with point data
7897   * container.skew = { x: 0.3, y: 0.3 }; // Diagonal skew
7898   *
7899   * // Reset skew
7900   * container.skew.set(0, 0);
7901   *
7902   * // Animate skew
7903   * app.ticker.add(() => {
7904   *     // Create wave effect
7905   *     container.skew.x = Math.sin(Date.now() / 1000) * 0.3;
7906   * });
7907   *
7908   * // Combine with rotation
7909   * container.rotation = Math.PI / 4; // 45 degrees
7910   * container.skew.set(0.2, 0.2); // Skew the rotated object
7911   * ```
7912   * @since 4.0.0
7913   * @type {ObservablePoint} Point-like object with x/y properties in radians
7914   * @default {x: 0, y: 0}
7915   */
7916  get skew() {
7917    if (this._skew === defaultSkew) {
7918      this._skew = new ObservablePoint(this, 0, 0);
7919    }
7920    return this._skew;
7921  }
7922  set skew(value) {
7923    if (this._skew === defaultSkew) {
7924      this._skew = new ObservablePoint(this, 0, 0);
7925    }
7926    this._skew.copyFrom(value);
7927  }
7928  /**
7929   * The scale factors of this object along the local coordinate axes.
7930   *
7931   * The default scale is (1, 1).
7932   * @example
7933   * ```ts
7934   * // Basic scaling
7935   * container.scale.set(2, 2); // Scales to double size
7936   * container.scale.set(2); // Scales uniformly to double size
7937   * container.scale = 2; // Scales uniformly to double size
7938   * // Scale to a specific width and height
7939   * container.setSize(200, 100); // Sets width to 200 and height to 100
7940   * ```
7941   * @since 4.0.0
7942   */
7943  get scale() {
7944    if (this._scale === defaultScale) {
7945      this._scale = new ObservablePoint(this, 1, 1);
7946    }
7947    return this._scale;
7948  }
7949  set scale(value) {
7950    if (this._scale === defaultScale) {
7951      this._scale = new ObservablePoint(this, 0, 0);
7952    }
7953    if (typeof value === "string") {
7954      value = parseFloat(value);
7955    }
7956    typeof value === "number" ? this._scale.set(value) : this._scale.copyFrom(value);
7957  }
7958  /**
7959   * @experimental
7960   * The origin point around which the container rotates and scales without affecting its position.
7961   * Unlike pivot, changing the origin will not move the container's position.
7962   * @example
7963   * ```ts
7964   * // Rotate around center point
7965   * container.origin.set(container.width / 2, container.height / 2);
7966   * container.rotation = Math.PI; // Rotates around center
7967   *
7968   * // Reset origin
7969   * container.origin.set(0, 0);
7970   * ```
7971   */
7972  get origin() {
7973    if (this._origin === defaultOrigin) {
7974      this._origin = new ObservablePoint(this, 0, 0);
7975    }
7976    return this._origin;
7977  }
7978  set origin(value) {
7979    if (this._origin === defaultOrigin) {
7980      this._origin = new ObservablePoint(this, 0, 0);
7981      if (this._pivot !== defaultPivot) {
7982        warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`);
7983      }
7984    }
7985    typeof value === "number" ? this._origin.set(value) : this._origin.copyFrom(value);
7986  }
7987  /**
7988   * The width of the Container, setting this will actually modify the scale to achieve the value set.
7989   * > [!NOTE] Changing the width will adjust the scale.x property of the container while maintaining its aspect ratio.
7990   * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}
7991   * as it is more optimized by not recalculating the local bounds twice.
7992   * @example
7993   * ```ts
7994   * // Basic width setting
7995   * container.width = 100;
7996   * // Optimized width setting
7997   * container.setSize(100, 100);
7998   * ```
7999   */
8000  get width() {
8001    return Math.abs(this.scale.x * this.getLocalBounds().width);
8002  }
8003  set width(value) {
8004    const localWidth = this.getLocalBounds().width;
8005    this._setWidth(value, localWidth);
8006  }
8007  /**
8008   * The height of the Container,
8009   * > [!NOTE] Changing the height will adjust the scale.y property of the container while maintaining its aspect ratio.
8010   * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}
8011   * as it is more optimized by not recalculating the local bounds twice.
8012   * @example
8013   * ```ts
8014   * // Basic height setting
8015   * container.height = 200;
8016   * // Optimized height setting
8017   * container.setSize(100, 200);
8018   * ```
8019   */
8020  get height() {
8021    return Math.abs(this.scale.y * this.getLocalBounds().height);
8022  }
8023  set height(value) {
8024    const localHeight = this.getLocalBounds().height;
8025    this._setHeight(value, localHeight);
8026  }
8027  /**
8028   * Retrieves the size of the container as a [Size]{@link Size} object.
8029   *
8030   * This is faster than get the width and height separately.
8031   * @example
8032   * ```ts
8033   * // Basic size retrieval
8034   * const size = container.getSize();
8035   * console.log(`Size: ${size.width}x${size.height}`);
8036   *
8037   * // Reuse existing size object
8038   * const reuseSize = { width: 0, height: 0 };
8039   * container.getSize(reuseSize);
8040   * ```
8041   * @param out - Optional object to store the size in.
8042   * @returns The size of the container.
8043   */
8044  getSize(out) {
8045    if (!out) {
8046      out = {};
8047    }
8048    const bounds = this.getLocalBounds();
8049    out.width = Math.abs(this.scale.x * bounds.width);
8050    out.height = Math.abs(this.scale.y * bounds.height);
8051    return out;
8052  }
8053  /**
8054   * Sets the size of the container to the specified width and height.
8055   * This is more efficient than setting width and height separately as it only recalculates bounds once.
8056   * @example
8057   * ```ts
8058   * // Basic size setting
8059   * container.setSize(100, 200);
8060   *
8061   * // Set uniform size
8062   * container.setSize(100); // Sets both width and height to 100
8063   * ```
8064   * @param value - This can be either a number or a [Size]{@link Size} object.
8065   * @param height - The height to set. Defaults to the value of `width` if not provided.
8066   */
8067  setSize(value, height) {
8068    
8068const size = this.getLocalBounds();
8069    if (typeof value === "object") {
8070      height = value.height ?? value.width;
8071      value = value.width;
8072    } else {
8073      height ?? (height = value);
8074    }
8075    value !== void 0 && this._setWidth(value, size.width);
8076    height !== void 0 && this._setHeight(height, size.height);
8077  }
8078  /** Called when the skew or the rotation changes. */
8079  _updateSkew() {
8080    const rotation = this._rotation;
8081    const skew = this._skew;
8082    this._cx = Math.cos(rotation + skew._y);
8083    this._sx = Math.sin(rotation + skew._y);
8084    this._cy = -Math.sin(rotation - skew._x);
8085    this._sy = Math.cos(rotation - skew._x);
8086  }
8087  /**
8088   * Updates the transform properties of the container.
8089   * Allows partial updates of transform properties for optimized manipulation.
8090   * @example
8091   * ```ts
8092   * // Basic transform update
8093   * container.updateTransform({
8094   *     x: 100,
8095   *     y: 200,
8096   *     rotation: Math.PI / 4
8097   * });
8098   *
8099   * // Scale and rotate around center
8100   * sprite.updateTransform({
8101   *     pivotX: sprite.width / 2,
8102   *     pivotY: sprite.height / 2,
8103   *     scaleX: 2,
8104   *     scaleY: 2,
8105   *     rotation: Math.PI
8106   * });
8107   *
8108   * // Update position only
8109   * button.updateTransform({
8110   *     x: button.x + 10, // Move right
8111   *     y: button.y      // Keep same y
8112   * });
8113   * ```
8114   * @param opts - Transform options to update
8115   * @param opts.x - The x position
8116   * @param opts.y - The y position
8117   * @param opts.scaleX - The x-axis scale factor
8118   * @param opts.scaleY - The y-axis scale factor
8119   * @param opts.rotation - The rotation in radians
8120   * @param opts.skewX - The x-axis skew factor
8121   * @param opts.skewY - The y-axis skew factor
8122   * @param opts.pivotX - The x-axis pivot point
8123   * @param opts.pivotY - The y-axis pivot point
8124   * @returns This container, for chaining
8125   * @see {@link Container#setFromMatrix} For matrix-based transforms
8126   * @see {@link Container#position} For direct position access
8127   */
8128  updateTransform(opts) {
8129    this.position.set(
8130      typeof opts.x === "number" ? opts.x : this.position.x,
8131      typeof opts.y === "number" ? opts.y : this.position.y
8132    );
8133    this.scale.set(
8134      typeof opts.scaleX === "number" ? opts.scaleX || 1 : this.scale.x,
8135      typeof opts.scaleY === "number" ? opts.scaleY || 1 : this.scale.y
8136    );
8137    this.rotation = typeof opts.rotation === "number" ? opts.rotation : this.rotation;
8138    this.skew.set(
8139      typeof opts.skewX === "number" ? opts.skewX : this.skew.x,
8140      typeof opts.skewY === "number" ? opts.skewY : this.skew.y
8141    );
8142    this.pivot.set(
8143      typeof opts.pivotX === "number" ? opts.pivotX : this.pivot.x,
8144      typeof opts.pivotY === "number" ? opts.pivotY : this.pivot.y
8145    );
8146    this.origin.set(
8147      typeof opts.originX === "number" ? opts.originX : this.origin.x,
8148      typeof opts.originY === "number" ? opts.originY : this.origin.y
8149    );
8150    return this;
8151  }
8152  /**
8153   * Updates the local transform properties by decomposing the given matrix.
8154   * Extracts position, scale, rotation, and skew from a transformation matrix.
8155   * @example
8156   * ```ts
8157   * // Basic matrix transform
8158   * const matrix = new Matrix()
8159   *     .translate(100, 100)
8160   *     .rotate(Math.PI / 4)
8161   *     .scale(2, 2);
8162   *
8163   * container.setFromMatrix(matrix);
8164   *
8165   * // Copy transform from another container
8166   * const source = new Container();
8167   * source.position.set(100, 100);
8168   * source.rotation = Math.PI / 2;
8169   *
8170   * target.setFromMatrix(source.localTransform);
8171   *
8172   * // Reset transform
8173   * container.setFromMatrix(Matrix.IDENTITY);
8174   * ```
8175   * @param matrix - The matrix to use for updating the transform
8176   * @see {@link Container#updateTransform} For property-based updates
8177   * @see {@link Matrix#decompose} For matrix decomposition details
8178   */
8179  setFromMatrix(matrix) {
8180    matrix.decompose(this);
8181  }
8182  /** Updates the local transform. */
8183  updateLocalTransform() {
8184    const localTransformChangeId = this._didContainerChangeTick;
8185    if (this._didLocalTransformChangeId === localTransformChangeId)
8186      return;
8187    this._didLocalTransformChangeId = localTransformChangeId;
8188    const lt = this.localTransform;
8189    const scale = this._scale;
8190    const pivot = this._pivot;
8191    const origin = this._origin;
8192    const position = this._position;
8193    const sx = scale._x;
8194    const sy = scale._y;
8195    const px = pivot._x;
8196    const py = pivot._y;
8197    const ox = -origin._x;
8198    const oy = -origin._y;
8199    lt.a = this._cx * sx;
8200    lt.b = this._sx * sx;
8201    lt.c = this._cy * sy;
8202    lt.d = this._sy * sy;
8203    lt.tx = position._x - (px * lt.a + py * lt.c) + (ox * lt.a + oy * lt.c) - ox;
8204    lt.ty = position._y - (px * lt.b + py * lt.d) + (ox * lt.b + oy * lt.d) - oy;
8205  }
8206  // / ///// color related stuff
8207  set alpha(value) {
8208    if (value === this.localAlpha)
8209      return;
8210    this.localAlpha = value;
8211    this._updateFlags |= UPDATE_COLOR;
8212    this._onUpdate();
8213  }
8214  /**
8215   * The opacity of the object relative to its parent's opacity.
8216   * Value ranges from 0 (fully transparent) to 1 (fully opaque).
8217   * @example
8218   * ```ts
8219   * // Basic transparency
8220   * sprite.alpha = 0.5; // 50% opacity
8221   *
8222   * // Inherited opacity
8223   * container.alpha = 0.5;
8224   * const child = new Sprite(texture);
8225   * child.alpha = 0.5;
8226   * container.addChild(child);
8227   * // child's effective opacity is 0.25 (0.5 * 0.5)
8228   * ```
8229   * @default 1
8230   * @see {@link Container#visible} For toggling visibility
8231   * @see {@link Container#renderable} For render control
8232   */
8233  get alpha() {
8234    return this.localAlpha;
8235  }
8236  set tint(value) {
8237    const tempColor = Color.shared.setValue(value ?? 16777215);
8238    const bgr = tempColor.toBgrNumber();
8239    if (bgr === this.localColor)
8240      return;
8241    this.localColor = bgr;
8242    this._updateFlags |= UPDATE_COLOR;
8243    this._onUpdate();
8244  }
8245  /**
8246   * The tint applied to the sprite.
8247   *
8248   * This can be any valid {@link ColorSource}.
8249   * @example
8250   * ```ts
8251   * // Basic color tinting
8252   * container.tint = 0xff0000; // Red tint
8253   * container.tint = 'red';    // Same as above
8254   * container.tint = '#00ff00'; // Green
8255   * container.tint = 'rgb(0,0,255)'; // Blue
8256   *
8257   * // Remove tint
8258   * container.tint = 0xffffff; // White = no tint
8259   * container.tint = null;     // Also removes tint
8260   * ```
8261   * @default 0xFFFFFF
8262   * @see {@link Container#alpha} For transparency
8263   * @see {@link Container#visible} For visibility control
8264   */
8265  get tint() {
8266    return bgr2rgb(this.localColor);
8267  }
8268  // / //////////////// blend related stuff
8269  set blendMode(value) {
vendor: 4,385 bytes, lines 8270-8410
8270    if (this.localBlendMode === value)
8271      return;
8272    if (this.parentRenderGroup) {
8273      this.parentRenderGroup.structureDidChange = true;
8274    }
8275    this._updateFlags |= UPDATE_BLEND;
8276    this.localBlendMode = value;
8277    this._onUpdate();
8278  }
8279  /**
8280   * The blend mode to be applied to the sprite. Controls how pixels are blended when rendering.
8281   *
8282   * Setting to 'normal' will reset to default blending.
8283   * > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export.
8284   * @example
8285   * ```ts
8286   * // Basic blend modes
8287   * sprite.blendMode = 'add';        // Additive blending
8288   * sprite.blendMode = 'multiply';   // Multiply colors
8289   * sprite.blendMode = 'screen';     // Screen blend
8290   *
8291   * // Reset blend mode
8292   * sprite.blendMode = 'normal';     // Normal blending
8293   * ```
8294   * @default 'normal'
8295   * @see {@link Container#alpha} For transparency
8296   * @see {@link Container#tint} For color adjustments
8297   */
8298  get blendMode() {
8299    return this.localBlendMode;
8300  }
8301  // / ///////// VISIBILITY / RENDERABLE /////////////////
8302  /**
8303   * The visibility of the object. If false the object will not be drawn,
8304   * and the transform will not be updated.
8305   * @example
8306   * ```ts
8307   * // Basic visibility toggle
8308   * sprite.visible = false; // Hide sprite
8309   * sprite.visible = true;  // Show sprite
8310   * ```
8311   * @default true
8312   * @see {@link Container#renderable} For render-only control
8313   * @see {@link Container#alpha} For transparency
8314   */
8315  get visible() {
8316    return !!(this.localDisplayStatus & 2);
8317  }
8318  set visible(value) {
8319    const valueNumber = value ? 2 : 0;
8320    if ((this.localDisplayStatus & 2) === valueNumber)
8321      return;
8322    if (this.parentRenderGroup) {
8323      this.parentRenderGroup.structureDidChange = true;
8324    }
8325    this._updateFlags |= UPDATE_VISIBLE;
8326    this.localDisplayStatus ^= 2;
8327    this._onUpdate();
8328  }
8329  /** @ignore */
8330  get culled() {
8331    return !(this.localDisplayStatus & 4);
8332  }
8333  /** @ignore */
8334  set culled(value) {
8335    const valueNumber = value ? 0 : 4;
8336    if ((this.localDisplayStatus & 4) === valueNumber)
8337      return;
8338    if (this.parentRenderGroup) {
8339      this.parentRenderGroup.structureDidChange = true;
8340    }
8341    this._updateFlags |= UPDATE_VISIBLE;
8342    this.localDisplayStatus ^= 4;
8343    this._onUpdate();
8344  }
8345  /**
8346   * Controls whether this object can be rendered. If false the object will not be drawn,
8347   * but the transform will still be updated. This is different from visible, which skips
8348   * transform updates.
8349   * @example
8350   * ```ts
8351   * // Basic render control
8352   * sprite.renderable = false; // Skip rendering
8353   * sprite.renderable = true;  // Enable rendering
8354   * ```
8355   * @default true
8356   * @see {@link Container#visible} For skipping transform updates
8357   * @see {@link Container#alpha} For transparency
8358   */
8359  get renderable() {
8360    return !!(this.localDisplayStatus & 1);
8361  }
8362  set renderable(value) {
8363    const valueNumber = value ? 1 : 0;
8364    if ((this.localDisplayStatus & 1) === valueNumber)
8365      return;
8366    this._updateFlags |= UPDATE_VISIBLE;
8367    this.localDisplayStatus ^= 1;
8368    if (this.parentRenderGroup) {
8369      this.parentRenderGroup.structureDidChange = true;
8370    }
8371    this._onUpdate();
8372  }
8373  /**
8374   * Whether or not the object should be rendered.
8375   * @advanced
8376   */
8377  get isRenderable() {
8378    return this.localDisplayStatus === 7 && this.groupAlpha > 0;
8379  }
8380  /**
8381   * Removes all internal references and listeners as well as removes children from the display list.
8382   * Do not use a Container after calling `destroy`.
8383   * @param options - Options parameter. A boolean will act as if all options
8384   *  have been set to that value
8385   * @example
8386   * ```ts
8387   * container.destroy();
8388   * container.destroy(true);
8389   * container.destroy({ children: true });
8390   * container.destroy({ children: true, texture: true, textureSource: true });
8391   * ```
8392   */
8393  destroy(options = false) {
8394    if (this.destroyed)
8395      return;
8396    this.destroyed = true;
8397    let oldChildren;
8398    if (this.children.length) {
8399      oldChildren = this.removeChildren(0, this.children.length);
8400    }
8401    this.removeFromParent();
8402    this.parent = null;
8403    this._maskEffect = null;
8404    this._filterEffect = null;
8405    this.effects = null;
8406    this._position = null;
8407    this._scale = null;
8408    this._pivot = null;
8409    this._origin = null;
8410    this._skew = null;
8411    this.emit("destroyed", this);
8412    this.removeAllListeners();
8413    const destroyChildren = typeof options === "boolean" ? options : options?.children;
8414    if (destroyChildren && oldChildren) {
8415      for (let i = 0; i < oldChildren.length; ++i) {
8416        oldChildren[i].destroy(options);
8417      }
8418    }
8419    this.renderGroup?.destroy();
8420    this.renderGroup = null;
8421  }
8422}
8423extensions.mixin(
8424  Container,
8425  childrenHelperMixin,
8426  getFastGlobalBoundsMixin,
8427  toLocalGlobalMixin,
8428  onRenderMixin,
8429  measureMixin,
8430  effectsMixin,
8431  findMixin,
8432  sortMixin,
8433  cullingMixin,
8434  cacheAsTextureMixin,
8435  getGlobalMixin,
8436  collectRenderablesMixin
8437);
8438
8439class ViewContainer extends Container {
8440  constructor(options) {
8441    super(options);
8442    /** @internal */
8443    this.canBundle = true;
8444    /** @internal */
8445    this.allowChildren = false;
8446    /** @internal */
8447    this._roundPixels = 0;
8448    /** @internal */
8449    this._lastUsed = -1;
8450    /** @internal */
8451    this._gpuData = /* @__PURE__ */ Object.create(null);
8452    /** If set to true, the resource will be garbage collected automatically when it is not used. */
8453    this.autoGarbageCollect = true;
8454    /** @internal */
8455    this._gcLastUsed = -1;
8456    this._bounds = new Bounds(0, 1, 0, 0);
8457    this._boundsDirty = true;
8458    this.autoGarbageCollect = options.autoGarbageCollect ?? true;
8459  }
8460  /**
8461   * The local bounds of the view in its own coordinate space.
8462   * Bounds are automatically updated when the view's content changes.
8463   * @example
8464   * ```ts
8465   * // Get bounds dimensions
8466   * const bounds = view.bounds;
8467   * console.log(`Width: ${bounds.maxX - bounds.minX}`);
8468   * console.log(`Height: ${bounds.maxY - bounds.minY}`);
8469   * ```
8470   * @returns The rectangular bounds of the view
8471   * @see {@link Bounds} For bounds operations
8472   */
8473  get bounds() {
8474    if (!this._boundsDirty)
8475      return this._bounds;
8476    this.updateBounds();
8477    this._boundsDirty = false;
8478    return this._bounds;
8479  }
8480  /**
8481   * Whether or not to round the x/y position of the sprite.
8482   * @example
8483   * ```ts
8484   * // Enable pixel rounding for crisp rendering
8485   * view.roundPixels = true;
8486   * ```
8487   * @default false
8488   */
8489  get roundPixels() {
8490    return !!this._roundPixels;
8491  }
8492  set roundPixels(value) {
8493    this._roundPixels = value ? 1 : 0;
8494  }
8495  /**
8496   * Checks if the object contains the given point in local coordinates.
8497   * Uses the view's bounds for hit testing.
8498   * @example
8499   * ```ts
8500   * // Basic point check
8501   * const localPoint = { x: 50, y: 25 };
8502   * const contains = view.containsPoint(localPoint);
8503   * console.log('Point is inside:', contains);
8504   * ```
8505   * @param point - The point to check in local coordinates
8506   * @returns True if the point is within the view's bounds
8507   * @see {@link ViewContainer#bounds} For the bounds used in hit testing
8508   * @see {@link Container#toLocal} For converting global coordinates to local
8509   */
8510  containsPoint(point) {
8511    const bounds = this.bounds;
8512    const { x, y } = point;
8513    return x >= bounds.minX && x <= bounds.maxX && y >= bounds.minY && y <= bounds.maxY;
8514  }
8515  /** @private */
8516  onViewUpdate() {
8517    this._didViewChangeTick++;
8518    this._boundsDirty = true;
8519    if (this.didViewUpdate)
8520      return;
8521    this.didViewUpdate = true;
8522    const renderGroup = this.renderGroup || this.parentRenderGroup;
8523    if (renderGroup) {
8524      renderGroup.onChildViewUpdate(this);
8525    }
8526  }
8527  /** Unloads the GPU data from the view. */
8528  unload() {
8529    this.emit("unload", this);
8530    for (const key in this._gpuData) {
8531      this._gpuData[key]?.destroy();
8532    }
8533    this._gpuData = /* @__PURE__ */ Object.create(null);
8534    this.onViewUpdate();
8535  }
8536  destroy(options) {
8537    this.unload();
8538    super.destroy(options);
8539    this._bounds = null;
8540  }
8541  /**
8542   * Collects renderables for the view container.
8543   * @param instructionSet - The instruction set to collect renderables for.
8544   * @param renderer - The renderer to collect renderables for.
8545   * @param currentLayer - The current render layer.
8546   * @internal
8547   */
8548  collectRenderablesSimple(instructionSet, renderer, currentLayer) {
8549    const { renderPipes } = renderer;
8550    renderPipes.blendMode.pushBlendMode(this, this.groupBlendMode, instructionSet);
8551    const rp = renderPipes;
8552    rp[this.renderPipeId].addRenderable(this, instructionSet);
8553    this.didViewUpdate = false;
8554    const children = this.children;
8555    const length = children.length;
8556    for (let i = 0; i < length; i++) {
8557      children[i].collectRenderables(instructionSet, renderer, currentLayer);
8558    }
8559    renderPipes.blendMode.popBlendMode(instructionSet);
8560  }
8561}
8562
8563class Sprite extends ViewContainer {
8564  /**
8565   * @param options - The options for creating the sprite.
8566   */
8567  constructor(options = Texture.EMPTY) {
8568    if (options instanceof Texture) {
8569      options = { texture: options };
8570    }
8571    const { texture = Texture.EMPTY, anchor, roundPixels, width, height, ...rest } = options;
8572    super({
8573      label: "Sprite",
8574      ...rest
8575    });
8576    /** @internal */
8577    this.renderPipeId = "sprite";
8578    /** @internal */
8579    this.batched = true;
8580    this._visualBounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
8581    this._anchor = new ObservablePoint(
8582      {
8583        _onUpdate: () => {
8584          this.onViewUpdate();
8585        }
8586      }
8587    );
8588    if (anchor) {
8589      this.anchor = anchor;
8590    } else if (texture.defaultAnchor) {
8591      this.anchor = texture.defaultAnchor;
8592    }
8593    this.texture = texture;
8594    this.allowChildren = false;
8595    this.roundPixels = roundPixels ?? false;
8596    if (width !== void 0)
8597      this.width = width;
8598    if (height !== void 0)
8599      this.height = height;
8600  }
8601  /**
8602   * Creates a new sprite based on a source texture, image, video, or canvas element.
8603   * This is a convenience method that automatically creates and manages textures.
8604   * @example
8605   * ```ts
8606   * // Create from path or URL
8607   * const sprite = Sprite.from('assets/image.png');
8608   *
8609   * // Create from existing texture
8610   * const sprite = Sprite.from(texture);
8611   *
8612   * // Create from canvas
8613   * const canvas = document.createElement('canvas');
8614   * const sprite = Sprite.from(canvas, true); // Skip caching new texture
8615   * ```
8616   * @param source - The source to create the sprite from. Can be a path to an image, a texture,
8617   * or any valid texture source (canvas, video, etc.)
8618   * @param skipCache - Whether to skip adding to the texture cache when creating a new texture
8619   * @returns A new sprite based on the source
8620   * @see {@link Texture.from} For texture creation details
8621   * @see {@link Assets} For asset loading and management
8622   */
8623  static from(source, skipCache = false) {
8624    if (source instanceof Texture) {
8625      return new Sprite(source);
8626    }
8627    return new Sprite(Texture.from(source, skipCache));
8628  }
8629  set texture(value) {
8630    value || (value = Texture.EMPTY);
8631    const currentTexture = this._texture;
8632    if (currentTexture === value)
8633      return;
8634    if (currentTexture && currentTexture.dynamic)
8635      currentTexture.off("update", this.onViewUpdate, this);
8636    if (value.dynamic)
8637      value.on("update", this.onViewUpdate, this);
8638    this._texture = value;
8639    if (this._width) {
8640      this._setWidth(this._width, this._texture.orig.width);
8641    }
8642    if (this._height) {
8643      this._setHeight(this._height, this._texture.orig.height);
8644    }
8645    this.onViewUpdate();
8646  }
8647  /**
8648   * The texture that is displayed by the sprite. When changed, automatically updates
8649   * the sprite dimensions and manages texture event listeners.
8650   * @example
8651   * ```ts
8652   * // Create sprite with texture
8653   * const sprite = new Sprite({
8654   *     texture: Texture.from('sprite.png')
8655   * });
8656   *
8657   * // Update texture
8658   * sprite.texture = Texture.from('newSprite.png');
8659   *
8660   * // Use texture from spritesheet
8661   * const sheet = await Assets.load('spritesheet.json');
8662   * sprite.texture = sheet.textures['frame1.png'];
8663   *
8664   * // Reset to empty texture
8665   * sprite.texture = Texture.EMPTY;
8666   * ```
8667   * @see {@link Texture} For texture creation and management
8668   * @see {@link Assets} For asset loading
8669   */
8670  get texture() {
8671    return this._texture;
8672  }
8673  /**
8674   * The bounds of the sprite, taking into account the texture's trim area.
8675   * @example
8676   * ```ts
8677   * const texture = new Texture({
8678   *     source: new TextureSource({ width: 300, height: 300 }),
8679   *     frame: new Rectangle(196, 66, 58, 56),
8680   *     trim: new Rectangle(4, 4, 58, 56),
8681   *     orig: new Rectangle(0, 0, 64, 64),
8682   *     rotate: 2,
8683   * });
8684   * const sprite = new Sprite(texture);
8685   * const visualBounds = sprite.visualBounds;
8686   * // console.log(visualBounds); // { minX: -4, maxX: 62, minY: -4, maxY: 60 }
8687   */
8688  get visualBounds() {
8689    updateQuadBounds(this._visualBounds, this._anchor, this._texture);
8690    return this._visualBounds;
8691  }
8692  /**
8693   * @deprecated
8694   * @ignore
8695   */
8696  get sourceBounds() {
8697    deprecation("8.6.1", "Sprite.sourceBounds is deprecated, use visualBounds instead.");
8698    return this.visualBounds;
8699  }
8700  /** @private */
8701  updateBounds() {
8702    const anchor = this._anchor;
8703    const texture = this._texture;
8704    const bounds = this._bounds;
8705    const { width, height } = texture.orig;
8706    bounds.minX = -anchor._x * width;
8707    bounds.maxX = bounds.minX + width;
8708    bounds.minY = -anchor._y * height;
8709    bounds.maxY = bounds.minY + height;
8710  }
8711  /**
8712   * Destroys this sprite renderable and optionally its texture.
8713   * @param options - Options parameter. A boolean will act as if all options
8714   *  have been set to that value
8715   * @example
8716   * sprite.destroy();
8717   * sprite.destroy(true);
8718   * sprite.destroy({ texture: true, textureSource: true });
8719   */
8720  destroy(options = false) {
8721    super.destroy(options);
8722    const destroyTexture = typeof options === "boolean" ? options : options?.texture;
8723    if (destroyTexture) {
8724      const destroyTextureSource = typeof options === "boolean" ? options : options?
vendor: 5,378 bytes, lines 8724-8882
8724.textureSource;
8725      this._texture.destroy(destroyTextureSource);
8726    }
8727    this._texture = null;
8728    this._visualBounds = null;
8729    this._bounds = null;
8730    this._anchor = null;
8731  }
8732  /**
8733   * The anchor sets the origin point of the sprite. The default value is taken from the {@link Texture}
8734   * and passed to the constructor.
8735   *
8736   * - The default is `(0,0)`, this means the sprite's origin is the top left.
8737   * - Setting the anchor to `(0.5,0.5)` means the sprite's origin is centered.
8738   * - Setting the anchor to `(1,1)` would mean the sprite's origin point will be the bottom right corner.
8739   *
8740   * If you pass only single parameter, it will set both x and y to the same value as shown in the example below.
8741   * @example
8742   * ```ts
8743   * // Center the anchor point
8744   * sprite.anchor = 0.5; // Sets both x and y to 0.5
8745   * sprite.position.set(400, 300); // Sprite will be centered at this position
8746   *
8747   * // Set specific x/y anchor points
8748   * sprite.anchor = {
8749   *     x: 1, // Right edge
8750   *     y: 0  // Top edge
8751   * };
8752   *
8753   * // Using individual coordinates
8754   * sprite.anchor.set(0.5, 1); // Center-bottom
8755   *
8756   * // For rotation around center
8757   * sprite.anchor.set(0.5);
8758   * sprite.rotation = Math.PI / 4; // 45 degrees around center
8759   *
8760   * // For scaling from center
8761   * sprite.anchor.set(0.5);
8762   * sprite.scale.set(2); // Scales from center point
8763   * ```
8764   */
8765  get anchor() {
8766    return this._anchor;
8767  }
8768  set anchor(value) {
8769    typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value);
8770  }
8771  /**
8772   * The width of the sprite, setting this will actually modify the scale to achieve the value set.
8773   * @example
8774   * ```ts
8775   * // Set width directly
8776   * sprite.width = 200;
8777   * console.log(sprite.scale.x); // Scale adjusted to match width
8778   *
8779   * // Set width while preserving aspect ratio
8780   * const ratio = sprite.height / sprite.width;
8781   * sprite.width = 300;
8782   * sprite.height = 300 * ratio;
8783   *
8784   * // For better performance when setting both width and height
8785   * sprite.setSize(300, 400); // Avoids recalculating bounds twice
8786   *
8787   * // Reset to original texture size
8788   * sprite.width = sprite.texture.orig.width;
8789   * ```
8790   */
8791  get width() {
8792    return Math.abs(this.scale.x) * this._texture.orig.width;
8793  }
8794  set width(value) {
8795    this._setWidth(value, this._texture.orig.width);
8796    this._width = value;
8797  }
8798  /**
8799   * The height of the sprite, setting this will actually modify the scale to achieve the value set.
8800   * @example
8801   * ```ts
8802   * // Set height directly
8803   * sprite.height = 150;
8804   * console.log(sprite.scale.y); // Scale adjusted to match height
8805   *
8806   * // Set height while preserving aspect ratio
8807   * const ratio = sprite.width / sprite.height;
8808   * sprite.height = 200;
8809   * sprite.width = 200 * ratio;
8810   *
8811   * // For better performance when setting both width and height
8812   * sprite.setSize(300, 400); // Avoids recalculating bounds twice
8813   *
8814   * // Reset to original texture size
8815   * sprite.height = sprite.texture.orig.height;
8816   * ```
8817   */
8818  get height() {
8819    return Math.abs(this.scale.y) * this._texture.orig.height;
8820  }
8821  set height(value) {
8822    this._setHeight(value, this._texture.orig.height);
8823    this._height = value;
8824  }
8825  /**
8826   * Retrieves the size of the Sprite as a [Size]{@link Size} object based on the texture dimensions and scale.
8827   * This is faster than getting width and height separately as it only calculates the bounds once.
8828   * @example
8829   * ```ts
8830   * // Basic size retrieval
8831   * const sprite = new Sprite(Texture.from('sprite.png'));
8832   * const size = sprite.getSize();
8833   * console.log(`Size: ${size.width}x${size.height}`);
8834   *
8835   * // Reuse existing size object
8836   * const reuseSize = { width: 0, height: 0 };
8837   * sprite.getSize(reuseSize);
8838   * ```
8839   * @param out - Optional object to store the size in, to avoid allocating a new object
8840   * @returns The size of the Sprite
8841   * @see {@link Sprite#width} For getting just the width
8842   * @see {@link Sprite#height} For getting just the height
8843   * @see {@link Sprite#setSize} For setting both width and height
8844   */
8845  getSize(out) {
8846    out || (out = {});
8847    out.width = Math.abs(this.scale.x) * this._texture.orig.width;
8848    out.height = Math.abs(this.scale.y) * this._texture.orig.height;
8849    return out;
8850  }
8851  /**
8852   * Sets the size of the Sprite to the specified width and height.
8853   * This is faster than setting width and height separately as it only recalculates bounds once.
8854   * @example
8855   * ```ts
8856   * // Basic size setting
8857   * const sprite = new Sprite(Texture.from('sprite.png'));
8858   * sprite.setSize(100, 200); // Width: 100, Height: 200
8859   *
8860   * // Set uniform size
8861   * sprite.setSize(100); // Sets both width and height to 100
8862   *
8863   * // Set size with object
8864   * sprite.setSize({
8865   *     width: 200,
8866   *     height: 300
8867   * });
8868   *
8869   * // Reset to texture size
8870   * sprite.setSize(
8871   *     sprite.texture.orig.width,
8872   *     sprite.texture.orig.height
8873   * );
8874   * ```
8875   * @param value - This can be either a number or a {@link Size} object
8876   * @param height - The height to set. Defaults to the value of `width` if not provided
8877   * @see {@link Sprite#width} For setting width only
8878   * @see {@link Sprite#height} For setting height only
8879   * @see {@link Sprite#texture} For the source dimensions
8880   */
8881  setSize(value, height) {
8882    
8882if (typeof value === "object") {
8883      height = value.height ?? value.width;
8884      value = value.width;
8885    } else {
8886      height ?? (height = value);
8887    }
8888    value !== void 0 && this._setWidth(value, this._texture.orig.width);
8889    height !== void 0 && this._setHeight(height, this._texture.orig.height);
8890  }
8891}
8892
8893const tempBounds = new Bounds();
8894function addMaskBounds(mask, bounds, skipUpdateTransform) {
8895  const boundsToMask = tempBounds;
8896  mask.measurable = true;
8897  getGlobalBounds(mask, skipUpdateTransform, boundsToMask);
8898  bounds.addBoundsMask(boundsToMask);
8899  mask.measurable = false;
8900}
8901
8902function addMaskLocalBounds(mask, bounds, localRoot) {
8903  const boundsToMask = boundsPool.get();
8904  mask.measurable = true;
8905  const tempMatrix = matrixPool.get().identity();
8906  const relativeMask = getMatrixRelativeToParent(mask, localRoot, tempMatrix);
8907  getLocalBounds(mask, boundsToMask, relativeMask);
8908  mask.measurable = false;
8909  bounds.addBoundsMask(boundsToMask);
8910  matrixPool.return(tempMatrix);
8911  boundsPool.return(boundsToMask);
8912}
8913function getMatrixRelativeToParent(target, root, matrix) {
8914  if (!target) {
8915    warn("Mask bounds, renderable is not inside the root container");
8916    return matrix;
8917  }
8918  if (target !== root) {
8919    getMatrixRelativeToParent(target.parent, root, matrix);
8920    target.updateLocalTransform();
8921    matrix.append(target.localTransform);
8922  }
8923  return matrix;
8924}
8925
8926class AlphaMask {
8927  constructor(options) {
8928    this.priority = 0;
8929    this.inverse = false;
8930    this.pipe = "alphaMask";
8931    if (options?.mask) {
8932      this.init(options.mask);
8933    }
8934  }
8935  init(mask) {
8936    this.mask = mask;
8937    this.renderMaskToTexture = !(mask instanceof Sprite);
8938    this.mask.renderable = this.renderMaskToTexture;
8939    this.mask.includeInBuild = !this.renderMaskToTexture;
8940    this.mask.measurable = false;
8941  }
8942  reset() {
8943    if (this.mask === null)
8944      return;
8945    this.mask.measurable = true;
8946    this.mask = null;
8947  }
8948  addBounds(bounds, skipUpdateTransform) {
8949    if (!this.inverse) {
8950      addMaskBounds(this.mask, bounds, skipUpdateTransform);
8951    }
8952  }
8953  addLocalBounds(bounds, localRoot) {
8954    addMaskLocalBounds(this.mask, bounds, localRoot);
8955  }
8956  containsPoint(point, hitTestFn) {
8957    const mask = this.mask;
8958    return hitTestFn(mask, point);
8959  }
8960  destroy() {
8961    this.reset();
8962  }
8963  static test(mask) {
8964    return mask instanceof Sprite;
8965  }
8966}
8967AlphaMask.extension = ExtensionType.MaskEffect;
8968
8969class ColorMask {
8970  constructor(options) {
8971    this.priority = 0;
8972    this.pipe = "colorMask";
8973    if (options?.mask) {
8974      this.init(options.mask);
8975    }
8976  }
8977  init(mask) {
8978    this.mask = mask;
8979  }
8980  destroy() {
8981  }
8982  static test(mask) {
8983    return typeof mask === "number";
8984  }
8985}
8986ColorMask.extension = ExtensionType.MaskEffect;
8987
8988class StencilMask {
8989  constructor(options) {
8990    this.priority = 0;
8991    this.pipe = "stencilMask";
8992    if (options?.mask) {
8993      this.init(options.mask);
8994    }
8995  }
8996  init(mask) {
8997    this.mask = mask;
8998    this.mask.includeInBuild = false;
8999    this.mask.measurable = false;
9000  }
9001  reset() {
9002    if (this.mask === null)
9003      return;
9004    this.mask.measurable = true;
9005    this.mask.includeInBuild = true;
9006    this.mask = null;
9007  }
9008  addBounds(bounds, skipUpdateTransform) {
9009    addMaskBounds(this.mask, bounds, skipUpdateTransform);
9010  }
9011  addLocalBounds(bounds, localRoot) {
9012    addMaskLocalBounds(this.mask, bounds, localRoot);
9013  }
9014  containsPoint(point, hitTestFn) {
9015    const mask = this.mask;
9016    return hitTestFn(mask, point);
9017  }
9018  destroy() {
9019    this.reset();
9020  }
9021  static test(mask) {
9022    return mask instanceof Container;
9023  }
9024}
9025StencilMask.extension = ExtensionType.MaskEffect;
9026
9027const BrowserAdapter = {
9028  createCanvas: (width, height) => {
9029    const canvas = document.createElement("canvas");
9030    canvas.width = width;
9031    canvas.height = height;
9032    return canvas;
9033  },
9034  createImage: () => new Image(),
9035  getCanvasRenderingContext2D: () => CanvasRenderingContext2D,
9036  getWebGLRenderingContext: () => WebGLRenderingContext,
9037  getNavigator: () => navigator,
9038  getBaseUrl: () => document.baseURI ?? window.location.href,
9039  getFontFaceSet: () => document.fonts,
9040  fetch: (url, options) => fetch(url, options),
9041  parseXML: (xml) => {
9042    const parser = new DOMParser();
9043    return parser.parseFromString(xml, "text/xml");
9044  }
9045};
9046
9047let currentAdapter = BrowserAdapter;
9048const DOMAdapter = {
9049  /**
9050   * Returns the current adapter.
9051   * @returns {environment.Adapter} The current adapter.
9052   */
9053  get() {
9054    return currentAdapter;
9055  },
9056  /**
9057   * Sets the current adapter.
9058   * @param adapter - The new adapter.
9059   */
9060  set(adapter) {
9061    currentAdapter = adapter;
9062  }
9063};
9064
9065class CanvasSource extends TextureSource {
9066  constructor(options) {
9067    if (!options.resource) {
9068      options.resource = DOMAdapter.get().createCanvas();
9069    }
9070    if (!options.width) {
9071      options.width = options.resource.width;
9072      if (!options.autoDensity) {
9073        options.width /= options.resolution;
9074      }
9075    }
9076    if (!options.height) {
9077      options.height = options.resource.height;
9078      if (!options.autoDensity) {
9079        options.height /= options.resolution;
9080      }
9081    }
9082    super(options);
9083    this.uploadMethodId = "image";
9084    this.autoDensity = options.autoDensity;
9085    this.resizeCanvas();
9086    this.transparent = !!options.transparent;
9087  }
9088  resizeCanvas() {
9089    if (this.autoDensity && "style" in this.resource) {
9090      this.resource.style.width = `${this.width}px`;
9091      this.resource.style.height = `${this.height}px`;
9092    }
9093    if (this.resource.width !== this.pixelWidth || this.resource.height !== this.pixelHeight) {
9094      this.resource.width = this.pixelWidth;
9095      this.resource.height = this.pixelHeight;
9096    }
9097  }
9098  resize(width = this.width, height = this.height, resolution = this._resolution) {
9099    const didResize = super.resize(width, height, resolution);
9100    if (didResize) {
9101      this.resizeCanvas();
9102    }
9103    return didResize;
9104  }
9105  static test(resource) {
9106    return globalThis.HTMLCanvasElement && resource instanceof HTMLCanvasElement || globalThis.OffscreenCanvas && resource instanceof OffscreenCanvas;
9107  }
9108  /**
9109   * Returns the 2D rendering context for the canvas.
9110   * Caches the context after creating it.
9111   * @returns The 2D rendering context of the canvas.
9112   */
9113  get context2D() {
9114    return this._context2D || (this._context2D = this.resource.getContext("2d"));
9115  }
9116}
9117CanvasSource.extension = ExtensionType.TextureSource;
9118
9119class ImageSource extends TextureSource {
9120  constructor(options) {
9121    super(options);
9122    this.uploadMethodId = "image";
9123    this.autoGarbageCollect = true;
9124  }
9125  static test(resource) {
9126    return globalThis.HTMLImageElement && resource instanceof HTMLImageElement || typeof ImageBitmap !== "undefined" && resource instanceof ImageBitmap || globalThis.VideoFrame && resource instanceof VideoFrame;
9127  }
9128}
9129ImageSource.extension = ExtensionType.TextureSource;
9130
9131var UPDATE_PRIORITY = /* @__PURE__ */ ((UPDATE_PRIORITY2) => {
9132  UPDATE_PRIORITY2[UPDATE_PRIORITY2["INTERACTION"] = 50] = "INTERACTION";
9133  UPDATE_PRIORITY2[UPDATE_PRIORITY2["HIGH"] = 25] = "HIGH";
9134  UPDATE_PRIORITY2[UPDATE_PRIORITY2["NORMAL"] = 0] = "NORMAL";
9135  UPDATE_PRIORITY2[UPDATE_PRIORITY2["LOW"] = -25] = "LOW";
9136  UPDATE_PRIORITY2[UPDATE_PRIORITY2["UTILITY"] = -50] = "UTILITY";
9137  return UPDATE_PRIORITY2;
9138})(UPDATE_PRIORITY || {});
9139
9140class TickerListener {
9141  /**
9142   * Constructor
9143   * @private
9144   * @param fn - The listener function to be added for one update
9145   * @param context - The listener context
9146   * @param priority - The priority for emitting
9147   * @param once - If the handler should fire once
9148   */
9149  constructor(fn, context = null, priority = 0, once = false) {
9150    /** The next item in chain. */
9151    this.next = null;
9152    /** The previous item in chain. */
9153    this.previous = null;
9154    /** `true` if this listener has been destroyed already. */
9155    this._destroyed = false;
vendor: 2,140 bytes, lines 9156-9236
9156    this._fn = fn;
9157    this._context = context;
9158    this.priority = priority;
9159    this._once = once;
9160  }
9161  /**
9162   * Simple compare function to figure out if a function and context match.
9163   * @param fn - The listener function to be added for one update
9164   * @param context - The listener context
9165   * @returns `true` if the listener match the arguments
9166   */
9167  match(fn, context = null) {
9168    return this._fn === fn && this._context === context;
9169  }
9170  /**
9171   * Emit by calling the current function.
9172   * @param ticker - The ticker emitting.
9173   * @returns Next ticker
9174   */
9175  emit(ticker) {
9176    if (this._fn) {
9177      if (this._context) {
9178        this._fn.call(this._context, ticker);
9179      } else {
9180        this._fn(ticker);
9181      }
9182    }
9183    const redirect = this.next;
9184    if (this._once) {
9185      this.destroy(true);
9186    }
9187    if (this._destroyed) {
9188      this.next = null;
9189    }
9190    return redirect;
9191  }
9192  /**
9193   * Connect to the list.
9194   * @param previous - Input node, previous listener
9195   */
9196  connect(previous) {
9197    this.previous = previous;
9198    if (previous.next) {
9199      previous.next.previous = this;
9200    }
9201    this.next = previous.next;
9202    previous.next = this;
9203  }
9204  /**
9205   * Destroy and don't use after this.
9206   * @param hard - `true` to remove the `next` reference, this
9207   *        is considered a hard destroy. Soft destroy maintains the next reference.
9208   * @returns The listener to redirect while emitting or removing.
9209   */
9210  destroy(hard = false) {
9211    this._destroyed = true;
9212    this._fn = null;
9213    this._context = null;
9214    if (this.previous) {
9215      this.previous.next = this.next;
9216    }
9217    if (this.next) {
9218      this.next.previous = this.previous;
9219    }
9220    const redirect = this.next;
9221    this.next = hard ? null : redirect;
9222    this.previous = null;
9223    return redirect;
9224  }
9225}
9226
9227const _Ticker = class _Ticker {
9228  constructor() {
9229    /**
9230     * Whether or not this ticker should invoke the method {@link Ticker#start|start}
9231     * automatically when a listener is added.
9232     * @example
9233     * ```ts
9234     * // Default behavior (manual start)
9235     * const ticker = new Ticker();
9236     * ticker.autoStart = false;
9237     * ticker.add(() => {
9238     *     // Won't run until ticker.start() is called
9239     * });
9240     *
9241     * // Auto-start behavior
9242     * const autoTicker = new Ticker();
9243     * autoTicker.autoStart = true;
9244     * autoTicker.add(() => {
9245     *     // Runs immediately when added
9246     * });
9247     * ```
9248     * @default false
9249     * @see {@link Ticker#start} For manually starting the ticker
9250     * @see {@link Ticker#stop} For manually stopping the ticker
9251     */
9252    this.autoStart = false;
9253    /**
9254     * Scalar representing the delta time factor.
9255     * This is a dimensionless value representing the fraction of a frame at the target framerate.
9256     * At 60 FPS, this value is typically around 1.0.
9257     *
9258     * This is NOT in milliseconds - it's a scalar multiplier for frame-independent animations.
9259     * For actual milliseconds, use {@link Ticker#deltaMS}.
9260     * @example
9261     * ```ts
9262     * // Frame-independent animation using deltaTime scalar
9263     * ticker.add((ticker) => {
9264     *     // Rotate sprite by 0.1 radians per frame, scaled by deltaTime
9265     *     sprite.rotation += 0.1 * ticker.deltaTime;
9266     * });
9267     * ```
9268     */
9269    this.deltaTime = 1;
9270    /**
9271     * The last time update was invoked, in milliseconds since epoch.
9272     * Similar to performance.now() timestamp format.
9273     *
9274     * Used internally for calculating time deltas between frames.
9275     * @example
9276     * ```ts
9277     * ticker.add((ticker) => {
9278     *     const currentTime = performance.now();
9279     *     const timeSinceLastFrame = currentTime - ticker.lastTime;
9280     *     console.log(`Time since last frame: ${timeSinceLastFrame}ms`);
9281     * });
9282     * ```
9283     */
9284    this.lastTime = -1;
9285    /**
9286     * Factor of current {@link Ticker#deltaTime|deltaTime}.
9287     * Used to scale time for slow motion or fast-forward effects.
9288     * @example
9289     * ```ts
9290     * // Basic speed adjustment
9291     * ticker.speed = 0.5; // Half speed (slow motion)
9292     * ticker.speed = 2.0; // Double speed (fast forward)
9293     *
9294     * // Temporary speed changes
9295     * function slowMotion() {
9296     *     const normalSpeed = ticker.speed;
9297     *     ticker.speed = 0.2;
9298     *     setTimeout(() => {
9299     *         ticker.speed = normalSpeed;
9300     *     }, 1000);
9301     * }
9302     * ```
9303     */
9304    this.speed = 1;
9305    /**
9306     * Whether or not this ticker has been started.
9307     *
9308     * `true` if {@link Ticker#start|start} has been called.
9309     * `false` if {@link Ticker#stop|Stop} has been called.
9310     *
9311     * While `false`, this value may change to `true` in the
9312     * event of {@link Ticker#autoStart|autoStart} being `true`
9313     * and a listener is added.
9314     * @example
9315     * ```ts
9316     * // Check ticker state
9317     * const ticker = new Ticker();
9318     * console.log(ticker.started); // false
9319     *
9320     * // Start and verify
9321     * ticker.start();
9322     * console.log(ticker.started); // true
9323     * ```
9324     */
9325    this.started = false;
9326    /** Internal current frame request ID */
9327    this._requestId = null;
9328    /**
9329     * Internal value managed by minFPS property setter and getter.
9330     * This is the maximum allowed milliseconds between updates.
9331     */
9332    this._maxElapsedMS = 100;
9333    /**
9334     * Internal value managed by min
vendor: 11,311 bytes, lines 9334-9699
9334FPS property setter and getter.
9335     * This is the minimum allowed milliseconds between updates.
9336     */
9337    this._minElapsedMS = 0;
9338    /** If enabled, deleting is disabled.*/
9339    this._protected = false;
9340    /** The last time keyframe was executed. Maintains a relatively fixed interval with the previous value. */
9341    this._lastFrame = -1;
9342    this._head = new TickerListener(null, null, Infinity);
9343    this.deltaMS = 1 / _Ticker.targetFPMS;
9344    this.elapsedMS = 1 / _Ticker.targetFPMS;
9345    this._tick = (time) => {
9346      this._requestId = null;
9347      if (this.started) {
9348        this.update(time);
9349        if (this.started && this._requestId === null && this._head.next) {
9350          this._requestId = requestAnimationFrame(this._tick);
9351        }
9352      }
9353    };
9354  }
9355  /**
9356   * Conditionally requests a new animation frame.
9357   * If a frame has not already been requested, and if the internal
9358   * emitter has listeners, a new frame is requested.
9359   */
9360  _requestIfNeeded() {
9361    if (this._requestId === null && this._head.next) {
9362      this.lastTime = performance.now();
9363      this._lastFrame = this.lastTime;
9364      this._requestId = requestAnimationFrame(this._tick);
9365    }
9366  }
9367  /** Conditionally cancels a pending animation frame. */
9368  _cancelIfNeeded() {
9369    if (this._requestId !== null) {
9370      cancelAnimationFrame(this._requestId);
9371      this._requestId = null;
9372    }
9373  }
9374  /**
9375   * Conditionally requests a new animation frame.
9376   * If the ticker has been started it checks if a frame has not already
9377   * been requested, and if the internal emitter has listeners. If these
9378   * conditions are met, a new frame is requested. If the ticker has not
9379   * been started, but autoStart is `true`, then the ticker starts now,
9380   * and continues with the previous conditions to request a new frame.
9381   */
9382  _startIfPossible() {
9383    if (this.started) {
9384      this._requestIfNeeded();
9385    } else if (this.autoStart) {
9386      this.start();
9387    }
9388  }
9389  /**
9390   * Register a handler for tick events.
9391   * @param fn - The listener function to add. Receives the Ticker instance as parameter
9392   * @param context - The context for the listener
9393   * @param priority - The priority of the listener
9394   * @example
9395   * ```ts
9396   * // Access time properties through the ticker parameter
9397   * ticker.add((ticker) => {
9398   *     // Use deltaTime (dimensionless scalar) for frame-independent animations
9399   *     sprite.rotation += 0.1 * ticker.deltaTime;
9400   *
9401   *     // Use deltaMS (milliseconds) for time-based calculations
9402   *     const progress = ticker.deltaMS / animationDuration;
9403   *
9404   *     // Use elapsedMS for raw timing measurements
9405   *     console.log(`Raw frame time: ${ticker.elapsedMS}ms`);
9406   * });
9407   * ```
9408   */
9409  add(fn, context, priority = UPDATE_PRIORITY.NORMAL) {
9410    return this._addListener(new TickerListener(fn, context, priority));
9411  }
9412  /**
9413   * Add a handler for the tick event which is only executed once on the next frame.
9414   * @example
9415   * ```ts
9416   * // Basic one-time update
9417   * ticker.addOnce(() => {
9418   *     console.log('Runs next frame only');
9419   * });
9420   *
9421   * // With specific context
9422   * const game = {
9423   *     init(ticker) {
9424   *         this.loadResources();
9425   *         console.log('Game initialized');
9426   *     }
9427   * };
9428   * ticker.addOnce(game.init, game);
9429   *
9430   * // With priority
9431   * ticker.addOnce(
9432   *     () => {
9433   *         // High priority one-time setup
9434   *         physics.init();
9435   *     },
9436   *     undefined,
9437   *     UPDATE_PRIORITY.HIGH
9438   * );
9439   * ```
9440   * @param fn - The listener function to be added for one update
9441   * @param context - The listener context
9442   * @param priority - The priority for emitting (default: UPDATE_PRIORITY.NORMAL)
9443   * @returns This instance of a ticker
9444   * @see {@link Ticker#add} For continuous updates
9445   * @see {@link Ticker#remove} For removing handlers
9446   */
9447  addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL) {
9448    return this._addListener(new TickerListener(fn, context, priority, true));
9449  }
9450  /**
9451   * Internally adds the event handler so that it can be sorted by priority.
9452   * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
9453   * before the rendering.
9454   * @private
9455   * @param listener - Current listener being added.
9456   * @returns This instance of a ticker
9457   */
9458  _addListener(listener) {
9459    let current = this._head.next;
9460    let previous = this._head;
9461    if (!current) {
9462      listener.connect(previous);
9463    } else {
9464      while (current) {
9465        if (listener.priority > current.priority) {
9466          listener.connect(previous);
9467          break;
9468        }
9469        previous = current;
9470        current = current.next;
9471      }
9472      if (!listener.previous) {
9473        listener.connect(previous);
9474      }
9475    }
9476    this._startIfPossible();
9477    return this;
9478  }
9479  /**
9480   * Removes any handlers matching the function and context parameters.
9481   * If no handlers are left after removing, then it cancels the animation frame.
9482   * @example
9483   * ```ts
9484   * // Basic removal
9485   * const onTick = () => {
9486   *     sprite.rotation += 0.1;
9487   * };
9488   * ticker.add(onTick);
9489   * ticker.remove(onTick);
9490   *
9491   * // Remove with context
9492   * const game = {
9493   *     update(ticker) {
9494   *         this.physics.update(ticker.deltaTime);
9495   *     }
9496   * };
9497   * ticker.add(game.update, game);
9498   * ticker.remove(game.update, game);
9499   *
9500   * // Remove all matching handlers
9501   * // (if same function was added multiple times)
9502   * ticker.add(onTick);
9503   * ticker.add(onTick);
9504   * ticker.remove(onTick); // Removes all instances
9505   * ```
9506   * @param fn - The listener function to be removed
9507   * @param context - The listener context to be removed
9508   * @returns This instance of a ticker
9509   * @see {@link Ticker#add} For adding handlers
9510   * @see {@link Ticker#addOnce} For one-time handlers
9511   */
9512  remove(fn, context) {
9513    let listener = this._head.next;
9514    while (listener) {
9515      if (listener.match(fn, context)) {
9516        listener = listener.destroy();
9517      } else {
9518        listener = listener.next;
9519      }
9520    }
9521    if (!this._head.next) {
9522      this._cancelIfNeeded();
9523    }
9524    return this;
9525  }
9526  /**
9527   * The number of listeners on this ticker, calculated by walking through linked list.
9528   * @example
9529   * ```ts
9530   * // Check number of active listeners
9531   * const ticker = new Ticker();
9532   * console.log(ticker.count); // 0
9533   *
9534   * // Add some listeners
9535   * ticker.add(() => {});
9536   * ticker.add(() => {});
9537   * console.log(ticker.count); // 2
9538   *
9539   * // Check after cleanup
9540   * ticker.destroy();
9541   * console.log(ticker.count); // 0
9542   * ```
9543   * @readonly
9544   * @see {@link Ticker#add} For adding listeners
9545   * @see {@link Ticker#remove} For removing listeners
9546   */
9547  get count() {
9548    if (!this._head) {
9549      return 0;
9550    }
9551    let count = 0;
9552    let current = this._head;
9553    while (current = current.next) {
9554      count++;
9555    }
9556    return count;
9557  }
9558  /**
9559   * Starts the ticker. If the ticker has listeners a new animation frame is requested at this point.
9560   * @example
9561   * ```ts
9562   * // Basic manual start
9563   * const ticker = new Ticker();
9564   * ticker.add(() => {
9565   *     // Animation code here
9566   * });
9567   * ticker.start();
9568   * ```
9569   * @see {@link Ticker#stop} For stopping the ticker
9570   * @see {@link Ticker#autoStart} For automatic starting
9571   * @see {@link Ticker#started} For checking ticker state
9572   */
9573  start() {
9574    if (!this.started) {
9575      this.started = true;
9576      this._requestIfNeeded();
9577    }
9578  }
9579  /**
9580   * Stops the ticker. If the ticker has requested an animation frame it is canceled at this point.
9581   * @example
9582   * ```ts
9583   * // Basic stop
9584   * const ticker = new Ticker();
9585   * ticker.stop();
9586   * ```
9587   * @see {@link Ticker#start} For starting the ticker
9588   * @see {@link Ticker#started} For checking ticker state
9589   * @see {@link Ticker#destroy} For cleaning up the ticker
9590   */
9591  stop() {
9592    if (this.started) {
9593      this.started = false;
9594      this._cancelIfNeeded();
9595    }
9596  }
9597  /**
9598   * Destroy the ticker and don't use after this. Calling this method removes all references to internal events.
9599   * @example
9600   * ```ts
9601   * // Clean up with active listeners
9602   * const ticker = new Ticker();
9603   * ticker.add(() => {});
9604   * ticker.destroy(); // Removes all listeners
9605   * ```
9606   * @see {@link Ticker#stop} For stopping without destroying
9607   * @see {@link Ticker#remove} For removing specific listeners
9608   */
9609  destroy() {
9610    if (!this._protected) {
9611      this.stop();
9612      let listener = this._head.next;
9613      while (listener) {
9614        listener = listener.destroy(true);
9615      }
9616      this._head.destroy();
9617      this._head = null;
9618    }
9619  }
9620  /**
9621   * Triggers an update.
9622   *
9623   * An update entails setting the
9624   * current {@link Ticker#elapsedMS|elapsedMS},
9625   * the current {@link Ticker#deltaTime|deltaTime},
9626   * invoking all listeners with current deltaTime,
9627   * and then finally setting {@link Ticker#lastTime|lastTime}
9628   * with the value of currentTime that was provided.
9629   *
9630   * This method will be called automatically by animation
9631   * frame callbacks if the ticker instance has been started
9632   * and listeners are added.
9633   * @example
9634   * ```ts
9635   * // Basic manual update
9636   * const ticker = new Ticker();
9637   * ticker.update(performance.now());
9638   * ```
9639   * @param currentTime - The current time of execution (defaults to performance.now())
9640   * @see {@link Ticker#deltaTime} For frame delta value
9641   * @see {@link Ticker#elapsedMS} For raw elapsed time
9642   */
9643  update(currentTime = performance.now()) {
9644    let elapsedMS;
9645    if (currentTime > this.lastTime) {
9646      elapsedMS = this.elapsedMS = currentTime - this.lastTime;
9647      if (elapsedMS > this._maxElapsedMS) {
9648        elapsedMS = this._maxElapsedMS;
9649      }
9650      elapsedMS *= this.speed;
9651      if (this._minElapsedMS) {
9652        const delta = currentTime - this._lastFrame | 0;
9653        if (delta < this._minElapsedMS) {
9654          return;
9655        }
9656        this._lastFrame = currentTime - delta % this._minElapsedMS;
9657      }
9658      this.deltaMS = elapsedMS;
9659      this.deltaTime = this.deltaMS * _Ticker.targetFPMS;
9660      const head = this._head;
9661      let listener = head.next;
9662      while (listener) {
9663        listener = listener.emit(this);
9664      }
9665      if (!head.next) {
9666        this._cancelIfNeeded();
9667      }
9668    } else {
9669      this.deltaTime = this.deltaMS = this.elapsedMS = 0;
9670    }
9671    this.lastTime = currentTime;
9672  }
9673  /**
9674   * The frames per second at which this ticker is running.
9675   * The default is approximately 60 in most modern browsers.
9676   * > [!NOTE] This does not factor in the value of
9677   * > {@link Ticker#speed|speed}, which is specific
9678   * > to scaling {@link Ticker#deltaTime|deltaTime}.
9679   * @example
9680   * ```ts
9681   * // Basic FPS monitoring
9682   * ticker.add(() => {
9683   *     console.log(`Current FPS: ${Math.round(ticker.FPS)}`);
9684   * });
9685   * ```
9686   * @readonly
9687   */
9688  get FPS() {
9689    return 1e3 / this.elapsedMS;
9690  }
9691  /**
9692   * Manages the maximum amount of milliseconds allowed to
9693   * elapse between invoking {@link Ticker#update|update}.
9694   *
9695   * This value is used to cap {@link Ticker#deltaTime|deltaTime},
9696   * but does not effect the measured value of {@link Ticker#FPS|FPS}.
9697   *
9698   * When setting this property it is clamped to a value between
9699   * `0` and `Ticker.targetFPMS * 1000`
9699.
9700   * @example
9701   * ```ts
9702   * // Set minimum acceptable frame rate
9703   * const ticker = new Ticker();
9704   * ticker.minFPS = 30; // Never go below 30 FPS
9705   *
9706   * // Use with maxFPS for frame rate clamping
9707   * ticker.minFPS = 30;
9708   * ticker.maxFPS = 60;
9709   *
9710   * // Monitor delta capping
9711   * ticker.add(() => {
9712   *     // Delta time will be capped based on minFPS
9713   *     console.log(`Delta time: ${ticker.deltaTime}`);
9714   * });
9715   * ```
9716   * @default 10
9717   */
9718  get minFPS() {
9719    return 1e3 / this._maxElapsedMS;
9720  }
9721  set minFPS(fps) {
9722    const minFPS = Math.min(this.maxFPS, fps);
9723    const minFPMS = Math.min(Math.max(0, minFPS) / 1e3, _Ticker.targetFPMS);
9724    this._maxElapsedMS = 1 / minFPMS;
9725  }
9726  /**
9727   * Manages the minimum amount of milliseconds required to
9728   * elapse between invoking {@link Ticker#update|update}.
9729   *
9730   * This will effect the measured value of {@link Ticker#FPS|FPS}.
9731   *
9732   * If it is set to `0`, then there is no limit; PixiJS will render as many frames as it can.
9733   * Otherwise it will be at least `minFPS`
9734   * @example
9735   * ```ts
9736   * // Set minimum acceptable frame rate
9737   * const ticker = new Ticker();
9738   * ticker.maxFPS = 60; // Never go above 60 FPS
9739   *
9740   * // Use with maxFPS for frame rate clamping
9741   * ticker.minFPS = 30;
9742   * ticker.maxFPS = 60;
9743   *
9744   * // Monitor delta capping
9745   * ticker.add(() => {
9746   *     // Delta time will be capped based on maxFPS
9747   *     console.log(`Delta time: ${ticker.deltaTime}`);
9748   * });
9749   * ```
9750   * @default 0
9751   */
9752  get maxFPS() {
9753    if (this._minElapsedMS) {
9754      return Math.round(1e3 / this._minElapsedMS);
9755    }
9756    return 0;
9757  }
9758  set maxFPS(fps) {
9759    if (fps === 0) {
9760      this._minElapsedMS = 0;
9761    } else {
9762      const maxFPS = Math.max(this.minFPS, fps);
9763      this._minElapsedMS = 1 / (maxFPS
vendor: 3,151 bytes, lines 9763-9860
9763 / 1e3);
9764    }
9765  }
9766  /**
9767   * The shared ticker instance used by {@link AnimatedSprite} and by
9768   * {@link VideoSource} to update animation frames / video textures.
9769   *
9770   * It may also be used by {@link Application} if created with the `sharedTicker` option property set to true.
9771   *
9772   * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance.
9773   * Please follow the examples for usage, including how to opt-out of auto-starting the shared ticker.
9774   * @example
9775   * import { Ticker } from 'pixi.js';
9776   *
9777   * const ticker = Ticker.shared;
9778   * // Set this to prevent starting this ticker when listeners are added.
9779   * // By default this is true only for the Ticker.shared instance.
9780   * ticker.autoStart = false;
9781   *
9782   * // FYI, call this to ensure the ticker is stopped. It should be stopped
9783   * // if you have not attempted to render anything yet.
9784   * ticker.stop();
9785   *
9786   * // Call this when you are ready for a running shared ticker.
9787   * ticker.start();
9788   * @example
9789   * import { autoDetectRenderer, Container } from 'pixi.js';
9790   *
9791   * // You may use the shared ticker to render...
9792   * const renderer = autoDetectRenderer();
9793   * const stage = new Container();
9794   * document.body.appendChild(renderer.view);
9795   * ticker.add((time) => renderer.render(stage));
9796   *
9797   * // Or you can just update it manually.
9798   * ticker.autoStart = false;
9799   * ticker.stop();
9800   * const animate = (time) => {
9801   *     ticker.update(time);
9802   *     renderer.render(stage);
9803   *     requestAnimationFrame(animate);
9804   * };
9805   * animate(performance.now());
9806   * @type {Ticker}
9807   * @readonly
9808   */
9809  static get shared() {
9810    if (!_Ticker._shared) {
9811      const shared = _Ticker._shared = new _Ticker();
9812      shared.autoStart = true;
9813      shared._protected = true;
9814    }
9815    return _Ticker._shared;
9816  }
9817  /**
9818   * The system ticker instance used by {@link PrepareBase} for core timing
9819   * functionality that shouldn't usually need to be paused, unlike the `shared`
9820   * ticker which drives visual animations and rendering which may want to be paused.
9821   *
9822   * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance.
9823   * @type {Ticker}
9824   * @readonly
9825   * @advanced
9826   */
9827  static get system() {
9828    if (!_Ticker._system) {
9829      const system = _Ticker._system = new _Ticker();
9830      system.autoStart = true;
9831      system._protected = true;
9832    }
9833    return _Ticker._system;
9834  }
9835};
9836/**
9837 * Target frame rate in frames per millisecond.
9838 * Used for converting deltaTime to a scalar time delta.
9839 * @example
9840 * ```ts
9841 * // Default is 0.06 (60 FPS)
9842 * console.log(Ticker.targetFPMS); // 0.06
9843 *
9844 * // Calculate target frame duration
9845 * const frameDuration = 1 / Ticker.targetFPMS; // ≈ 16.67ms
9846 *
9847 * // Use in custom timing calculations
9848 * const deltaTime = elapsedMS * Ticker.targetFPMS;
9849 * ```
9850 * @remarks
9851 * - Default is 0.06 (equivalent to 60 FPS)
9852 * - Used in deltaTime calculations
9853 * - Affects all ticker instances
9854 * @default 0.06
9855 * @see {@link Ticker#deltaTime} For time scaling
9856 * @see {@link Ticker#FPS} For actual frame rate
9857 */
9858_Ticker.targetFPMS = 0.06;
9859let Ticker = _Ticker;
9860
9861let promise;
9862async function detectVideoAlphaMode() {
9863  promise ?? (promise = (async () => {
9864    const canvas = DOMAdapter.get().createCanvas(1, 1);
9865    const gl = canvas.getContext("webgl");
9866    if (!gl) {
9867      return "premultiply-alpha-on-upload";
9868    }
9869    const video = await new Promise((resolve) => {
9870      const video2 = document.createElement("video");
9871      video2.onloadeddata = () => resolve(video2);
9872      video2.onerror = () => resolve(null);
9873      video2.autoplay = false;
9874      video2.crossOrigin = "anonymous";
9875      video2.preload = "auto";
9876      video2.src = "data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEAAAAAAAHTEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggEXTbuMU6uEHFO7a1OsggG97AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmoCrXsYMPQkBNgIRMYXZmV0GETGF2ZkSJiEBEAAAAAAAAFlSua8yuAQAAAAAAAEPXgQFzxYgAAAAAAAAAAZyBACK1nIN1bmSIgQCGhVZfVlA5g4EBI+ODhAJiWgDglLCBArqBApqBAlPAgQFVsIRVuYEBElTDZ9Vzc9JjwItjxYgAAAAAAAAAAWfInEWjh0VOQ09ERVJEh49MYXZjIGxpYnZweC12cDlnyKJFo4hEVVJBVElPTkSHlDAwOjAwOjAwLjA0MDAwMDAwMAAAH0O2dcfngQCgwqGggQAAAIJJg0IAABAAFgA4JBwYSgAAICAAEb///4r+AAB1oZ2mm+6BAaWWgkmDQgAAEAAWADgkHBhKAAAgIABIQBxTu2uRu4+zgQC3iveBAfGCAXHwgQM=";
9877      video2.load();
9878    });
9879    if (!video) {
9880      return "premultiply-alpha-on-upload";
9881    }
9882    const texture = gl.createTexture();
9883    gl.bindTexture(gl.TEXTURE_2D, texture);
9884    const framebuffer = gl.createFramebuffer();
9885    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
9886    gl.framebufferTexture2D(
9887      gl.FRAMEBUFFER,
9888      gl.COLOR_ATTACHMENT0,
9889      gl.TEXTURE_2D,
9890      texture,
9891      0
9892    );
9893    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
9894    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
9895    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
9896    const pixel = new Uint8Array(4);
9897    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
9898    gl.deleteFramebuffer(framebuffer);
9899    gl.deleteTexture(texture);
9900    gl.getExtension("WEBGL_lose_context")?.loseContext();
9901    return pixel[0] <= pixel[3] ? "premultiplied-alpha" : "premultiply-alpha-on-upload";
9902  })());
9903  return promise;
9904}
9905
9906const _VideoSource = class _VideoSource extends TextureSource {
9907  constructor(options) {
9908    super(options);
9909    // Public
9910    /** Whether or not the video is ready to play. */
9911    this.isReady = false;
9912    /** The upload method for this texture. */
9913    this.uploadMethodId = "video";
9914    options = {
9915      ..._VideoSource.defaultOptions,
9916      ...options
9917    };
9918    this._autoUpdate = true;
9919    this._isConnectedToTicker = false;
9920    this._updateFPS = options.updateFPS || 0;
9921    this._msToNextUpdate = 0;
9922    this.autoPlay = options.autoPlay !== false;
9923    this.alphaMode = options.alphaMode ?? "premultiply-alpha-on-upload";
9924    this._videoFrameRequestCallback = this._videoFrameRequestCallback.bind(this);
9925    this._videoFrameRequestCallbackHandle = null;
9926    this._load = null;
9927    this._resolve = null;
9928    this._reject = null;
9929    this._onCanPlay = this._onCanPlay.bind(this);
9930    this._onCanPlayThrough = this._onCanPlayThrough.bind(this);
9931    this._onError = this._onError.bind(this);
9932    this._onPlayStart = this._onPlayStart.bind(this);
9933    this._onPlayStop = this._onPlayStop.bind(this);
9934    this._onSeeked = this._onSeeked.bind(this);
9935    if (options.autoLoad !== false) {
9936      void this.load();
9937    }
9938  }
9939  /** Update the video frame if the source is not destroyed and meets certain conditions. */
9940  updateFrame() {
9941    if (this.destroyed) {
9942      return;
9943    }
9944    if (this._updateFPS) {
9945      const elapsedMS = Ticker.shared.elapsedMS * this.resource.playbackRate;
9946      this._msToNextUpdate = Math.floor(this._msToNextUpdate - elapsedMS);
9947    }
9948    if (!this._updateFPS || this._msToNextUpdate <= 0) {
9949      this._msToNextUpdate = this._updateFPS ? Math.floor(1e3 / this._updateFPS) : 0;
9950    }
9951    if (this.isValid) {
9952      this.update();
9953    }
9954  }
9955  /** Callback to update the video frame and potentially request the next frame update. */
9956  _videoFrameRequestCallback() {
9957    this.updateFrame();
9958    if (this.destroyed) {
9959      this._videoFrameRequestCallbackHandle = null;
9960    } else {
9961      this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
9962        this._videoFrameRequestCallback
9963      );
9964    }
9965  }
9966  /**
9967   * Checks if the resource has valid dimensions.
9968   * @returns {boolean} True if width and height are set, otherwise false.
9969   */
9970  get isValid() {
9971    return !!this.resource.videoWidth && !!this.resource.videoHeight;
9972  }
9973  /**
9974   * Start preloading the video resource.
9975   * @returns {Promise<this>} Handle the validate event
9976   */
9977  async load() {
9978    if (this._load) {
9979      return this._load;
9980    }
9981    const source = this.resource;
9982    const options = this.options;
9983    if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) {
9984      source.complete = true;
9985    }
9986    source.addEventListener("play", this._onPlayStart);
9987    source.addEventListener("pause", this._onPlayStop);
9988    source.addEventListener("seeked", this._onSeeked);
9989    if (!this._isSourceReady()) {
9990      if (!options.preload) {
9991        source.addEventListener("canplay", this._onCanPlay);
9992      }
9993      source.addEventListener("canplaythrough", this._onCanPlayThrough);
9994      source.addEventListener("error", this._onError, true);
9995    } else {
9996      this._mediaReady();
9997    }
9998    this.alphaMode = await detectVideoAlphaMode();
9999    this._load = new Promise((resolve, reject) => {
10000      if (this.isValid) {
10001        resolve(this);
10002      } else {
10003        this._resolve = resolve;
10004        this._reject = reject;
10005        if (options.preloadTimeoutMs !== void 0) {
10006          this._preloadTimeout = setTimeout(() => {
10007            this._onError(new ErrorEvent(`Preload exceeded timeout of ${options.preloadTimeoutMs}ms`));
10008          });
10009        }
10010        source.load();
10011      }
10012    });
10013    return this._load;
10014  }
10015  /**
10016   * Handle video error events.
10017   * @param event - The error event
10018   */
10019  _onError(event) {
10020    this.resource.removeEventListener("error", this._onError, true);
vendor: 22,330 bytes, lines 10021-10757
10021    this.emit("error", event);
10022    if (this._reject) {
10023      this._reject(event);
10024      this._reject = null;
10025      this._resolve = null;
10026    }
10027  }
10028  /**
10029   * Checks if the underlying source is playing.
10030   * @returns True if playing.
10031   */
10032  _isSourcePlaying() {
10033    const source = this.resource;
10034    return !source.paused && !source.ended;
10035  }
10036  /**
10037   * Checks if the underlying source is ready for playing.
10038   * @returns True if ready.
10039   */
10040  _isSourceReady() {
10041    const source = this.resource;
10042    return source.readyState > 2;
10043  }
10044  /** Runs the update loop when the video is ready to play. */
10045  _onPlayStart() {
10046    if (!this.isValid) {
10047      this._mediaReady();
10048    }
10049    this._configureAutoUpdate();
10050  }
10051  /** Stops the update loop when a pause event is triggered. */
10052  _onPlayStop() {
10053    this._configureAutoUpdate();
10054  }
10055  /** Handles behavior when the video completes seeking to the current playback position. */
10056  _onSeeked() {
10057    if (this._autoUpdate && !this._isSourcePlaying()) {
10058      this._msToNextUpdate = 0;
10059      this.updateFrame();
10060      this._msToNextUpdate = 0;
10061    }
10062  }
10063  _onCanPlay() {
10064    const source = this.resource;
10065    source.removeEventListener("canplay", this._onCanPlay);
10066    this._mediaReady();
10067  }
10068  _onCanPlayThrough() {
10069    const source = this.resource;
10070    source.removeEventListener("canplaythrough", this._onCanPlay);
10071    if (this._preloadTimeout) {
10072      clearTimeout(this._preloadTimeout);
10073      this._preloadTimeout = void 0;
10074    }
10075    this._mediaReady();
10076  }
10077  /** Fired when the video is loaded and ready to play. */
10078  _mediaReady() {
10079    const source = this.resource;
10080    if (this.isValid) {
10081      this.isReady = true;
10082      this.resize(source.videoWidth, source.videoHeight);
10083    }
10084    this._msToNextUpdate = 0;
10085    this.updateFrame();
10086    this._msToNextUpdate = 0;
10087    if (this._resolve) {
10088      this._resolve(this);
10089      this._resolve = null;
10090      this._reject = null;
10091    }
10092    if (this._isSourcePlaying()) {
10093      this._onPlayStart();
10094    } else if (this.autoPlay) {
10095      void this.resource.play();
10096    }
10097  }
10098  /** Cleans up resources and event listeners associated with this texture. */
10099  destroy() {
10100    this._configureAutoUpdate();
10101    const source = this.resource;
10102    if (source) {
10103      source.removeEventListener("play", this._onPlayStart);
10104      source.removeEventListener("pause", this._onPlayStop);
10105      source.removeEventListener("seeked", this._onSeeked);
10106      source.removeEventListener("canplay", this._onCanPlay);
10107      source.removeEventListener("canplaythrough", this._onCanPlayThrough);
10108      source.removeEventListener("error", this._onError, true);
10109      source.pause();
10110      source.src = "";
10111      source.load();
10112    }
10113    super.destroy();
10114  }
10115  /** Should the base texture automatically update itself, set to true by default. */
10116  get autoUpdate() {
10117    return this._autoUpdate;
10118  }
10119  set autoUpdate(value) {
10120    if (value !== this._autoUpdate) {
10121      this._autoUpdate = value;
10122      this._configureAutoUpdate();
10123    }
10124  }
10125  /**
10126   * How many times a second to update the texture from the video.
10127   * Leave at 0 to update at every render.
10128   * A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient.
10129   */
10130  get updateFPS() {
10131    return this._updateFPS;
10132  }
10133  set updateFPS(value) {
10134    if (value !== this._updateFPS) {
10135      this._updateFPS = value;
10136      this._configureAutoUpdate();
10137    }
10138  }
10139  /**
10140   * Configures the updating mechanism based on the current state and settings.
10141   *
10142   * This method decides between using the browser's native video frame callback or a custom ticker
10143   * for updating the video frame. It ensures optimal performance and responsiveness
10144   * based on the video's state, playback status, and the desired frames-per-second setting.
10145   *
10146   * - If `_autoUpdate` is enabled and the video source is playing:
10147   *   - It will prefer the native video frame callback if available and no specific FPS is set.
10148   *   - Otherwise, it will use a custom ticker for manual updates.
10149   * - If `_autoUpdate` is disabled or the video isn't playing, any active update mechanisms are halted.
10150   */
10151  _configureAutoUpdate() {
10152    if (this._autoUpdate && this._isSourcePlaying()) {
10153      if (!this._updateFPS && this.resource.requestVideoFrameCallback) {
10154        if (this._isConnectedToTicker) {
10155          Ticker.shared.remove(this.updateFrame, this);
10156          this._isConnectedToTicker = false;
10157          this._msToNextUpdate = 0;
10158        }
10159        if (this._videoFrameRequestCallbackHandle === null) {
10160          this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
10161            this._videoFrameRequestCallback
10162          );
10163        }
10164      } else {
10165        if (this._videoFrameRequestCallbackHandle !== null) {
10166          this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
10167          this._videoFrameRequestCallbackHandle = null;
10168        }
10169        if (!this._isConnectedToTicker) {
10170          Ticker.shared.add(this.updateFrame, this);
10171          this._isConnectedToTicker = true;
10172          this._msToNextUpdate = 0;
10173        }
10174      }
10175    } else {
10176      if (this._videoFrameRequestCallbackHandle !== null) {
10177        this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
10178        this._videoFrameRequestCallbackHandle = null;
10179      }
10180      if (this._isConnectedToTicker) {
10181        Ticker.shared.remove(this.updateFrame, this);
10182        this._isConnectedToTicker = false;
10183        this._msToNextUpdate = 0;
10184      }
10185    }
10186  }
10187  static test(resource) {
10188    return globalThis.HTMLVideoElement && resource instanceof HTMLVideoElement;
10189  }
10190};
10191_VideoSource.extension = ExtensionType.TextureSource;
10192/** The default options for video sources. */
10193_VideoSource.defaultOptions = {
10194  ...TextureSource.defaultOptions,
10195  /** If true, the video will start loading immediately. */
10196  autoLoad: true,
10197  /** If true, the video will start playing as soon as it is loaded. */
10198  autoPlay: true,
10199  /** The number of times a second to update the texture from the video. Leave at 0 to update at every render. */
10200  updateFPS: 0,
10201  /** If true, the video will be loaded with the `crossorigin` attribute. */
10202  crossorigin: true,
10203  /** If true, the video will loop when it ends. */
10204  loop: false,
10205  /** If true, the video will be muted. */
10206  muted: true,
10207  /** If true, the video will play inline. */
10208  playsinline: true,
10209  /** If true, the video will be preloaded. */
10210  preload: false
10211};
10212/**
10213 * Map of video MIME types that can't be directly derived from file extensions.
10214 * @readonly
10215 */
10216_VideoSource.MIME_TYPES = {
10217  ogv: "video/ogg",
10218  mov: "video/quicktime",
10219  m4v: "video/mp4"
10220};
10221let VideoSource = _VideoSource;
10222
10223const convertToList = (input, transform, forceTransform = false) => {
10224  if (!Array.isArray(input)) {
10225    input = [input];
10226  }
10227  if (!transform) {
10228    return input;
10229  }
10230  return input.map((item) => {
10231    if (typeof item === "string" || forceTransform) {
10232      return transform(item);
10233    }
10234    return item;
10235  });
10236};
10237
10238class CacheClass {
10239  constructor() {
10240    this._parsers = [];
10241    this._cache = /* @__PURE__ */ new Map();
10242    this._cacheMap = /* @__PURE__ */ new Map();
10243  }
10244  /** Clear all entries. */
10245  reset() {
10246    this._cacheMap.clear();
10247    this._cache.clear();
10248  }
10249  /**
10250   * Check if the key exists
10251   * @param key - The key to check
10252   */
10253  has(key) {
10254    return this._cache.has(key);
10255  }
10256  /**
10257   * Fetch entry by key
10258   * @param key - The key of the entry to get
10259   */
10260  get(key) {
10261    const result = this._cache.get(key);
10262    if (!result) {
10263      warn(`[Assets] Asset id ${key} was not found in the Cache`);
10264    }
10265    return result;
10266  }
10267  /**
10268   * Set a value by key or keys name
10269   * @param key - The key or keys to set
10270   * @param value - The value to store in the cache or from which cacheable assets will be derived.
10271   */
10272  set(key, value) {
10273    const keys = convertToList(key);
10274    let cacheableAssets;
10275    for (let i = 0; i < this.parsers.length; i++) {
10276      const parser = this.parsers[i];
10277      if (parser.test(value)) {
10278        cacheableAssets = parser.getCacheableAssets(keys, value);
10279        break;
10280      }
10281    }
10282    const cacheableMap = new Map(Object.entries(cacheableAssets || {}));
10283    if (!cacheableAssets) {
10284      keys.forEach((key2) => {
10285        cacheableMap.set(key2, value);
10286      });
10287    }
10288    const cacheKeys = [...cacheableMap.keys()];
10289    const cachedAssets = {
10290      cacheKeys,
10291      keys
10292    };
10293    keys.forEach((key2) => {
10294      this._cacheMap.set(key2, cachedAssets);
10295    });
10296    cacheKeys.forEach((key2) => {
10297      const val = cacheableAssets ? cacheableAssets[key2] : value;
10298      if (this._cache.has(key2) && this._cache.get(key2) !== val) {
10299        warn("[Cache] already has key:", key2);
10300      }
10301      this._cache.set(key2, cacheableMap.get(key2));
10302    });
10303  }
10304  /**
10305   * Remove entry by key
10306   *
10307   * This function will also remove any associated alias from the cache also.
10308   * @param key - The key of the entry to remove
10309   */
10310  remove(key) {
10311    if (!this._cacheMap.has(key)) {
10312      warn(`[Assets] Asset id ${key} was not found in the Cache`);
10313      return;
10314    }
10315    const cacheMap = this._cacheMap.get(key);
10316    const cacheKeys = cacheMap.cacheKeys;
10317    cacheKeys.forEach((key2) => {
10318      this._cache.delete(key2);
10319    });
10320    cacheMap.keys.forEach((key2) => {
10321      this._cacheMap.delete(key2);
10322    });
10323  }
10324  /**
10325   * All loader parsers registered
10326   * @advanced
10327   */
10328  get parsers() {
10329    return this._parsers;
10330  }
10331}
10332const Cache = new CacheClass();
10333
10334const sources = [];
10335extensions.handleByList(ExtensionType.TextureSource, sources);
10336function textureSourceFrom(options = {}) {
10337  const hasResource = options && options.resource;
10338  const res = hasResource ? options.resource : options;
10339  const opts = hasResource ? options : { resource: options };
10340  for (let i = 0; i < sources.length; i++) {
10341    const Source = sources[i];
10342    if (Source.test(res)) {
10343      return new Source(opts);
10344    }
10345  }
10346  throw new Error(`Could not find a source type for resource: ${opts.resource}`);
10347}
10348function resourceToTexture(options = {}, skipCache = false) {
10349  const hasResource = options && options.resource;
10350  const resource = hasResource ? options.resource : options;
10351  const opts = hasResource ? options : { resource: options };
10352  if (!skipCache && Cache.has(resource)) {
10353    return Cache.get(resource);
10354  }
10355  const texture = new Texture({ source: textureSourceFrom(opts) });
10356  texture.on("destroy", () => {
10357    if (Cache.has(resource)) {
10358      Cache.remove(resource);
10359    }
10360  });
10361  if (!skipCache) {
10362    Cache.set(resource, texture);
10363  }
10364  return texture;
10365}
10366function textureFrom(id, skipCache = false) {
10367  if (typeof id === "string") {
10368    return Cache.get(id);
10369  } else if (id instanceof TextureSource) {
10370    return new Texture({ source: id });
10371  }
10372  return resourceToTexture(id, skipCache);
10373}
10374Texture.from = textureFrom;
10375TextureSource.from = textureSourceFrom;
10376
10377extensions.add(AlphaMask, ColorMask, StencilMask, VideoSource, ImageSource, CanvasSource, BufferImageSource);
10378
10379var LoaderParserPriority = /* @__PURE__ */ ((LoaderParserPriority2) => {
10380  LoaderParserPriority2[LoaderParserPriority2["Low"] = 0] = "Low";
10381  LoaderParserPriority2[LoaderParserPriority2["Normal"] = 1] = "Normal";
10382  LoaderParserPriority2[LoaderParserPriority2["High"] = 2] = "High";
10383  return LoaderParserPriority2;
10384})(LoaderParserPriority || {});
10385
10386function assertPath(path2) {
10387  if (typeof path2 !== "string") {
10388    throw new TypeError(`Path must be a string. Received ${JSON.stringify(path2)}`);
10389  }
10390}
10391function removeUrlParams(url) {
10392  const re = url.split("?")[0];
10393  return re.split("#")[0];
10394}
10395function escapeRegExp(string) {
10396  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10397}
10398function replaceAll(str, find, replace) {
10399  return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
10400}
10401function normalizeStringPosix(path2, allowAboveRoot) {
10402  let res = "";
10403  let lastSegmentLength = 0;
10404  let lastSlash = -1;
10405  let dots = 0;
10406  let code = -1;
10407  for (let i = 0; i <= path2.length; ++i) {
10408    if (i < path2.length) {
10409      code = path2.charCodeAt(i);
10410    } else if (code === 47) {
10411      break;
10412    } else {
10413      code = 47;
10414    }
10415    if (code === 47) {
10416      if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) {
10417        if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
10418          if (res.length > 2) {
10419            const lastSlashIndex = res.lastIndexOf("/");
10420            if (lastSlashIndex !== res.length - 1) {
10421              if (lastSlashIndex === -1) {
10422                res = "";
10423                lastSegmentLength = 0;
10424              } else {
10425                res = res.slice(0, lastSlashIndex);
10426                lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
10427              }
10428              lastSlash = i;
10429              dots = 0;
10430              continue;
10431            }
10432          } else if (res.length === 2 || res.length === 1) {
10433            res = "";
10434            lastSegmentLength = 0;
10435            lastSlash = i;
10436            dots = 0;
10437            continue;
10438          }
10439        }
10440      } else {
10441        if (res.length > 0) {
10442          res += `/${path2.slice(lastSlash + 1, i)}`;
10443        } else {
10444          res = path2.slice(lastSlash + 1, i);
10445        }
10446        lastSegmentLength = i - lastSlash - 1;
10447      }
10448      lastSlash = i;
10449      dots = 0;
10450    } else if (code === 46 && dots !== -1) {
10451      ++dots;
10452    } else {
10453      dots = -1;
10454    }
10455  }
10456  return res;
10457}
10458const path = {
10459  /**
10460   * Converts a path to posix format.
10461   * @param path - The path to convert to posix
10462   * @example
10463   * ```ts
10464   * // Convert a Windows path to POSIX format
10465   * path.toPosix('C:\\Users\\User\\Documents\\file.txt');
10466   * // -> 'C:/Users/User/Documents/file.txt'
10467   * ```
10468   */
10469  toPosix(path2) {
10470    return replaceAll(path2, "\\", "/");
10471  },
10472  /**
10473   * Checks if the path is a URL e.g. http://, https://
10474   * @param path - The path to check
10475   * @example
10476   * ```ts
10477   * // Check if a path is a URL
10478   * path.isUrl('http://www.example.com');
10479   * // -> true
10480   * path.isUrl('C:/Users/User/Documents/file.txt');
10481   * // -> false
10482   * ```
10483   */
10484  isUrl(path2) {
10485    return /^https?:/.test(this.toPosix(path2));
10486  },
10487  /**
10488   * Checks if the path is a data URL
10489   * @param path - The path to check
10490   * @example
10491   * ```ts
10492   * // Check if a path is a data URL
10493   * path.isDataUrl('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...');
10494   * // -> true
10495   * ```
10496   */
10497  isDataUrl(path2) {
10498    return /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i.test(path2);
10499  },
10500  /**
10501   * Checks if the path is a blob URL
10502   * @param path - The path to check
10503   * @example
10504   * ```ts
10505   * // Check if a path is a blob URL
10506   * path.isBlobUrl('blob:http://www.example.com/12345678-1234-1234-1234-123456789012');
10507   * // -> true
10508   * ```
10509   */
10510  isBlobUrl(path2) {
10511    return path2.startsWith("blob:");
10512  },
10513  /**
10514   * Checks if the path has a protocol e.g. http://, https://, file:///, data:, blob:, C:/
10515   * This will return true for windows file paths
10516   * @param path - The path to check
10517   * @example
10518   * ```ts
10519   * // Check if a path has a protocol
10520   * path.hasProtocol('http://www.example.com');
10521   * // -> true
10522   * path.hasProtocol('C:/Users/User/Documents/file.txt');
10523   * // -> true
10524   * ```
10525   */
10526  hasProtocol(path2) {
10527    return /^[^/:]+:/.test(this.toPosix(path2));
10528  },
10529  /**
10530   * Returns the protocol of the path e.g. http://, https://, file:///, data:, blob:, C:/
10531   * @param path - The path to get the protocol from
10532   * @example
10533   * ```ts
10534   * // Get the protocol from a URL
10535   * path.getProtocol('http://www.example.com/path/to/resource');
10536   * // -> 'http://'
10537   * // Get the protocol from a file path
10538   * path.getProtocol('C:/Users/User/Documents/file.txt');
10539   * // -> 'C:/'
10540   * ```
10541   */
10542  getProtocol(path2) {
10543    assertPath(path2);
10544    path2 = this.toPosix(path2);
10545    const matchFile = /^file:\/\/\//.exec(path2);
10546    if (matchFile) {
10547      return matchFile[0];
10548    }
10549    const matchProtocol = /^[^/:]+:\/{0,2}/.exec(path2);
10550    if (matchProtocol) {
10551      return matchProtocol[0];
10552    }
10553    return "";
10554  },
10555  /**
10556   * Converts URL to an absolute path.
10557   * When loading from a Web Worker, we must use absolute paths.
10558   * If the URL is already absolute we return it as is
10559   * If it's not, we convert it
10560   * @param url - The URL to test
10561   * @param customBaseUrl - The base URL to use
10562   * @param customRootUrl - The root URL to use
10563   * @example
10564   * ```ts
10565   * // Convert a relative URL to an absolute path
10566   * path.toAbsolute('images/texture.png', 'http://example.com/assets/');
10567   * // -> 'http://example.com/assets/images/texture.png'
10568   * ```
10569   */
10570  toAbsolute(url, customBaseUrl, customRootUrl) {
10571    assertPath(url);
10572    if (this.isDataUrl(url) || this.isBlobUrl(url))
10573      return url;
10574    const baseUrl = removeUrlParams(this.toPosix(customBaseUrl ?? DOMAdapter.get().getBaseUrl()));
10575    const rootUrl = removeUrlParams(this.toPosix(customRootUrl ?? this.rootname(baseUrl)));
10576    url = this.toPosix(url);
10577    if (url.startsWith("/")) {
10578      return path.join(rootUrl, url.slice(1));
10579    }
10580    const absolutePath = this.isAbsolute(url) ? url : this.join(baseUrl, url);
10581    return absolutePath;
10582  },
10583  /**
10584   * Normalizes the given path, resolving '..' and '.' segments
10585   * @param path - The path to normalize
10586   * @example
10587   * ```ts
10588   * // Normalize a path with relative segments
10589   * path.normalize('http://www.example.com/foo/bar/../baz');
10590   * // -> 'http://www.example.com/foo/baz'
10591   * // Normalize a file path with relative segments
10592   * path.normalize('C:\\Users\\User\\Documents\\..\\file.txt');
10593   * // -> 'C:/Users/User/file.txt'
10594   * ```
10595   */
10596  normalize(path2) {
10597    assertPath(path2);
10598    if (path2.length === 0)
10599      return ".";
10600    if (this.isDataUrl(path2) || this.isBlobUrl(path2))
10601      return path2;
10602    path2 = this.toPosix(path2);
10603    let protocol = "";
10604    const isAbsolute = path2.startsWith("/");
10605    if (this.hasProtocol(path2)) {
10606      protocol = this.rootname(path2);
10607      path2 = path2.slice(protocol.length);
10608    }
10609    const trailingSeparator = path2.endsWith("/");
10610    path2 = normalizeStringPosix(path2);
10611    if (path2.length > 0 && trailingSeparator)
10612      path2 += "/";
10613    if (isAbsolute)
10614      return `/${path2}`;
10615    return protocol + path2;
10616  },
10617  /**
10618   * Determines if path is an absolute path.
10619   * Absolute paths can be urls, data urls, or paths on disk
10620   * @param path - The path to test
10621   * @example
10622   * ```ts
10623   * // Check if a path is absolute
10624   * path.isAbsolute('http://www.example.com/foo/bar');
10625   * // -> true
10626   * path.isAbsolute('C:/Users/User/Documents/file.txt');
10627   * // -> true
10628   * ```
10629   */
10630  isAbsolute(path2) {
10631    assertPath(path2);
10632    path2 = this.toPosix(path2);
10633    if (this.hasProtocol(path2))
10634      return true;
10635    return path2.startsWith("/");
10636  },
10637  /**
10638   * Joins all given path segments together using the platform-specific separator as a delimiter,
10639   * then normalizes the resulting path
10640   * @param segments - The segments of the path to join
10641   * @example
10642   * ```ts
10643   * // Join multiple path segments
10644   * path.join('assets', 'images', 'sprite.png');
10645   * // -> 'assets/images/sprite.png'
10646   * // Join with relative segments
10647   * path.join('assets', 'images', '../textures', 'sprite.png');
10648   * // -> 'assets/textures/sprite.png'
10649   * ```
10650   */
10651  join(...segments) {
10652    if (segments.length === 0) {
10653      return ".";
10654    }
10655    let joined;
10656    for (let i = 0; i < segments.length; ++i) {
10657      const arg = segments[i];
10658      assertPath(arg);
10659      if (arg.length > 0) {
10660        if (joined === void 0)
10661          joined = arg;
10662        else {
10663          const prevArg = segments[i - 1] ?? "";
10664          if (this.joinExtensions.includes(this.extname(prevArg).toLowerCase())) {
10665            joined += `/../${arg}`;
10666          } else {
10667            joined += `/${arg}`;
10668          }
10669        }
10670      }
10671    }
10672    if (joined === void 0) {
10673      return ".";
10674    }
10675    return this.normalize(joined);
10676  },
10677  /**
10678   * Returns the directory name of a path
10679   * @param path - The path to parse
10680   * @example
10681   * ```ts
10682   * // Get the directory name of a path
10683   * path.dirname('http://www.example.com/foo/bar/baz.png');
10684   * // -> 'http://www.example.com/foo/bar'
10685   * // Get the directory name of a file path
10686   * path.dirname('C:/Users/User/Documents/file.txt');
10687   * // -> 'C:/Users/User/Documents'
10688   * ```
10689   */
10690  dirname(path2) {
10691    assertPath(path2);
10692    if (path2.length === 0)
10693      return ".";
10694    path2 = this.toPosix(path2);
10695    let code = path2.charCodeAt(0);
10696    const hasRoot = code === 47;
10697    let end = -1;
10698    let matchedSlash = true;
10699    const proto = this.getProtocol(path2);
10700    const origpath = path2;
10701    path2 = path2.slice(proto.length);
10702    for (let i = path2.length - 1; i >= 1; --i) {
10703      code = path2.charCodeAt(i);
10704      if (code === 47) {
10705        if (!matchedSlash) {
10706          end = i;
10707          break;
10708        }
10709      } else {
10710        matchedSlash = false;
10711      }
10712    }
10713    if (end === -1)
10714      return hasRoot ? "/" : this.isUrl(origpath) ? proto + path2 : proto;
10715    if (hasRoot && end === 1)
10716      return "//";
10717    return proto + path2.slice(0, end);
10718  },
10719  /**
10720   * Returns the root of the path e.g. /, C:/, file:///, http://domain.com/
10721   * @param path - The path to parse
10722   * @example
10723   * ```ts
10724   * // Get the root of a URL
10725   * path.rootname('http://www.example.com/foo/bar/baz.png');
10726   * // -> 'http://www.example.com/'
10727   * // Get the root of a file path
10728   * path.rootname('C:/Users/User/Documents/file.txt');
10729   * // -> 'C:/'
10730   * ```
10731   */
10732  rootname(path2) {
10733    assertPath(path2);
10734    path2 = this.toPosix(path2);
10735    let root = "";
10736    if (path2.startsWith("/"))
10737      root = "/";
10738    else {
10739      root = this.getProtocol(path2);
10740    }
10741    if (this.isUrl(path2)) {
10742      const index = path2.indexOf("/", root.length);
10743      if (index !== -1) {
10744        root = path2.slice(0, index);
10745      } else
10746        root = path2;
10747      if (!root.endsWith("/"))
10748        root += "/";
10749    }
10750    return root;
10751  },
10752  /**
10753   * Returns the last portion of a path
10754   * @param path - The path to test
10755   * @param ext - Optional extension to remove
10756   * @example
10757   * ```ts
10758   * // Get the basename of a URL
10759   * path.basename('http://www.example.com/foo/bar/baz.png');
10760   * // -> 'baz.png'
10761   * // Get the basename of a file path
10762   * path.basename('C:/Users/User/Documents/file.txt');
10763   * // -> 'file.txt'
10764   * ```
10765   */
10766  basename(path2, ext) {
10767    assertPath(path2);
10768    if (ext)
10769      assertPath(ext);
10770    path2 = removeUrlParams(this.toPosix(path2));
10771    let start = 0;
10772    let end = -1;
10773    let matchedSlash = true;
10774    let i;
10775    if (ext !== void 0 && ext.length > 0 && ext.length <= path2.length) {
10776      if (ext.length === path2.length && ext === path2)
10777        return "";
10778      let extIdx = ext.length - 1;
10779      let firstNonSlashEnd = -1;
10780      for (i = path2.length - 1; i >= 0; --i) {
10781        const code = path2.charCodeAt(i);
10782        if (code === 47) {
10783          if (!matchedSlash) {
10784            start = i + 1;
10785            break;
10786          }
10787        } else {
10788          if (firstNonSlashEnd === -1) {
10789            matchedSlash = false;
10790            firstNonSlashEnd = i + 1;
10791          }
10792          if (extIdx >= 0) {
10793            if (code === ext.charCodeAt(extIdx)) {
10794              if (--extIdx === -1) {
10795                end = i;
10796              }
10797            } else {
10798              extIdx = -1;
10799              end = firstNonSlashEnd;
10800            }
10801          }
10802        }
10803      }
10804      if (start === end)
10805        end = firstNonSlashEnd;
10806      else if (end === -1)
10807        end = path2.length;
10808      return path2.slice(start, end);
10809    }
10810    for (i = path2.length - 1; i >= 0; --i) {
10811      if (path2.charCodeAt(i) === 47) {
10812        if (!matchedSlash) {
10813          start = i + 1;
10814          break;
10815        }
10816      } else if (end === -1) {
10817        matchedSlash = false;
10818        end = i + 1;
10819      }
10820    }
10821    if (end === -1)
10822      return "";
10823    return path2.slice(start, end);
10824  },
10825  /**
10826   * Returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last
10827   * portion of the path. If there is no . in the last portion of the path, or if there are no . characters other than
10828   * the first character of the basename of path, an empty string is returned.
10829   * @param path - The path to parse
10830   * @example
10831   * ```ts
10832   * // Get the extension of a URL
10833   * path.extname('http://www.example.com/foo/bar/baz.png');
10834   * // -> '.png'
10835   * // Get the extension of a file path
10836   * path.extname('C:/Users/User/Documents/file.txt');
10837   * // -> '.txt'
10838   * ```
10839   */
10840  extname(path2) {
10841    assertPath(path2);
10842    path2 = removeUrlParams(this.toPosix(path2));
10843    let startDot = -1;
10844    let startPart = 0;
10845    let end = -1;
10846    let matchedSlash = true;
10847    let preDotState = 0;
10848    for (let i = path2.length - 1; i >= 0; --i) {
10849      const code = path2.charCodeAt(i);
10850      if (code === 47) {
10851        if (!matchedSlash) {
10852          startPart = i + 1;
10853          break;
10854        }
10855        continue;
10856      }
10857      if (end === -1) {
10858        matchedSlash = false;
10859        end = i + 1;
10860      }
10861      if (code === 46) {
10862        if (startDot === -1)
10863          startDot = i;
10864        else if (preDotState !== 1)
10865          preDotState = 1;
10866      } else if (startDot !== -1) {
10867        preDotState = -1;
10868      }
10869    }
10870    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
10871      return "";
10872    }
10873    return path2.slice(startDot, end);
10874  },
10875  /**
10876   * Parses a path into an object containing the 'root', `dir`, `base`, `ext`, and `name` properties.
10877   * @param path - The path to parse
10878   * @example
10879   * ```ts
10880   * // Parse a URL
10881   * const parsed = path.parse('http://www.example.com/foo/bar/baz.png');
10882   * // -> {
10883   * //   root: 'http://www.example.com/',
10884   * //   dir: 'http://www.example.com/foo/bar',
10885   * //   base: 'baz.png',
10886   * //   ext: '.png',
10887   * //   name: 'baz'
10888   * // }
10889   * // Parse a file path
10890   * const parsedFile = path.parse('C:/Users/User/Documents/file.txt');
10891   * // -> {
10892   * //   root: 'C:/',
10893   * //   dir: 'C:/Users/User/Documents',
10894   * //   base: 'file.txt',
10895   * //   ext: '.txt',
10896   * //   name: 'file'
10897   * // }
10898   * ```
10899   */
10900  parse(path2) {
10901    assertPath(path2);
10902    const ret = { root: "", dir: "", base: "", ext: "", name: "" };
10903    if (path2.length === 0)
10904      return ret;
10905    path2 = removeUrlParams(this.toPosix(path2));
10906    let code = path2.charCodeAt(0);
10907    const isAbsolute = this.isAbsolute(path2);
10908    let start;
10909    ret.root = this.rootname(path2);
10910    if (isAbsolute || this.hasProtocol(path2)) {
10911      start = 1;
10912    } else {
10913      start = 0;
10914    }
10915    let startDot = -1;
10916    let startPart = 0;
10917    let end = -1;
10918    let matchedSlash = true;
10919    let i = path2.length - 1;
10920    let preDotState = 0;
10921    for (; i >= start; --i) {
10922      code = path2.charCodeAt(i);
10923      if (code === 47) {
10924        if (!matchedSlash) {
10925          startPart = i + 1;
10926          break;
10927        }
10928        continue;
10929      }
10930      if (end === -1) {
10931        matchedSlash = false;
10932        end = i + 1;
10933      }
10934      if (code === 46) {
10935        if (startDot === -1)
10936          startDot = i;
10937        else if (preDotState !== 1)
10938          preDotState = 1;
10939      } else if (startDot !== -1) {
10940        preDotState = -1;
10941      }
10942    }
10943    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
10944      if (end !== -1) {
10945        if (startPart === 0 && isAbsolute)
10946          ret.base = ret.name = path2.slice(1, end);
10947        else
10948          ret.base = ret.name = path2.slice(startPart, end);
10949      }
10950    } else {
10951      if (startPart === 0 && isAbsolute) {
10952        ret.name = path2.slice(1, startDot);
10953        ret.base = path2.slice(1, end);
10954      } else {
10955        ret.name = path2.slice(startPart, startDot);
10956        ret.base = path2.slice(startPart, end);
10957      }
10958      ret.ext = path2.slice(startDot, end);
10959    }
10960    ret.dir = this.dirname(path2);
10961    return ret;
10962  },
10963  sep: "/",
10964  delimiter: ":",
10965  joinExtensions: [".html"]
10966};
10967
10968function processX(base, ids, depth, result, tags) {
10969  const id = ids[depth];
10970  for (let i = 0; i < id.length; i++) {
10971    const value = id[i];
10972    if (depth < ids.length - 1) {
10973      processX(base.replace(result[depth], value), ids, depth + 1, result, tags);
10974    } else {
10975      tags.push(base.replace(result[depth], value));
10976    }
10977  }
10978}
10979function createStringVariations(string) {
10980  const regex = /\{(.*?)\}/g;
10981  const result = string.match(regex);
10982  const tags = [];
10983  if (result) {
10984    const ids = [];
10985    result.forEach((vars) => {
10986      const split = vars.substring(1, vars.length - 1).split(",");
10987      ids.push(split);
10988    });
10989    processX(string, ids, 0, result, tags);
10990  } else {
10991    tags.push(string);
10992  }
10993  return tags;
10994}
10995
10996const isSingleItem = (item) => !Array.isArray(item);
10997
10998class Resolver {
10999  constructor() {
11000    this._defaultBundleIdentifierOptions = {
11001      connector: "-",
11002      createBundleAssetId: (bundleId, assetId) => `${bundleId}${this._bundleIdConnector}${assetId}`,
11003      extractAssetIdFromBundle: (bundleId, assetBundleId) => assetBundleId.replace(`${bundleId}${this._bundleIdConnector}`, "")
11004    };
11005    /** The character that is used to connect the bundleId and the assetId when generating a bundle asset id key */
11006    this._bundleIdConnector = this._defaultBundleIdentifierOptions.connector;
11007    /**
11008     * A function that generates a bundle asset id key from a bundleId and an assetId
11009     * @param bundleId - the bundleId
11010     * @param assetId  - the assetId
11011     * @returns the bundle asset id key
11012     */
11013    this._createBundleAssetId = this._defaultBundleIdentifierOptions.createBundleAssetId;
11014    /**
11015     * A function that generates an assetId from a bundle asset id key. This is the reverse of generateBundleAssetId
11016     * @param bundleId - the bundleId
11017     * @param assetBundleId - the bundle asset id key
11018     * @returns the assetId
11019     */
11020    this._extractAssetIdFromBundle = this._defaultBundleIdentifierOptions.extractAssetIdFromBundle;
11021    this._assetMap = {};
11022    this._preferredOrder = [];
11023    this._parsers = [];
11024    this._resolverHash = {};
11025    this._bundles = {};
11026  }
11027  /**
11028   * Override how the resolver deals with generating bundle ids.
11029   * must be called before any bundles are added
11030   * @param bundleIdentifier - the bundle identifier options
11031   */
11032  setBundleIdentifier(bundleIdentifier) {
11033    this._bundleIdConnector = bundleIdentifier.connector ?? this._bundleIdConnector;
11034    this._createBundleAssetId = bundleIdentifier.createBundleAssetId ?? this._createBundleAssetId;
11035    this._extractAssetIdFromBundle = bundleIdentifier.extractAssetIdFromBundle ?? this._extractAssetIdFromBundle;
11036    if (this._extractAssetIdFromBundle("foo", this._createBundleAssetId("foo", "bar")) !== "bar") {
11037      throw new Error("[Resolver] GenerateBundleAssetId are not working correctly");
11038    }
11039  }
11040  /**
11041   * Let the resolver know which assets you prefer to use when resolving assets.
11042   * Multiple prefer user defined rules can be added.
11043   * @example
11044   * resolver.prefer({
11045   *     // first look for something with the correct format, and then then correct resolution
11046   *     priority: ['format', 'resolution'],
11047   *     params:{
11048   *         format:'webp', // prefer webp images
11049   *         resolution: 2, // prefer a resolution of 2
11050   *     }
11051   * })
11052   * resolver.add('foo', ['[email protected]', '[email protected]', 'bar.webp', 'bar.png']);
11053   * resolver.resolveUrl('foo') // => '[email protected]'
11054   * @param preferOrders - the prefer options
11055   */
11056  prefer(...preferOrders) {
11057    preferOrders.forEach((prefer) => {
11058      this._preferredOrder.push(prefer);
11059      if (!prefer.priority) {
11060        prefer.priority = Object.keys(prefer.params);
11061      }
11062    });
11063    this._resolverHash = {};
11064  }
11065  /**
11066   * Set the base path to prepend to all urls when resolving
11067   * @example
11068   * resolver.basePath = 'https://home.com/';
11069   * resolver.add('foo', 'bar.ong');
11070   * resolver.resolveUrl('foo', 'bar.png'); // => 'https://home.com/bar.png'
11071   * @param basePath - the base path to use
11072   */
11073  set basePath(basePath) {
11074    this._basePath = basePath;
11075  }
11076  get basePath() {
11077    return this._basePath;
11078  }
11079  /**
11080   * Set the root path for root-relative URLs. By default the `basePath`'s root is used. If no `basePath` is set, then the
11081   * default value for browsers is `window.location.origin`
11082   * @example
11083   * // Application hosted on https://home.com/some-path/index.html
11084   * resolver.basePath = 'https://home.com/some-path/';
11085   * resolver.rootPath = 'https://home.com/';
11086   * resolver.add('foo', '/bar.png');
11087   * resolver.resolveUrl('foo', '/bar.png'); // => 'https://home.com/bar.png'
11088   * @param rootPath - the root path to use
11089   */
11090  set rootPath(rootPath) {
11091    this._rootPath = rootPath;
11092  }
11093  get rootPath() {
11094    return this._rootPath;
11095  }
11096  /**
11097   * All the active URL parsers that help the parser to extract information and create
11098   * an asset object-based on parsing the URL itself.
11099   *
11100   * Can be added using the extensions API
11101   * @example
11102   * resolver.add('foo', [
11103   *     {
11104   *         resolution: 2,
11105   *         format: 'png',
11106   *         src: '[email protected]',
11107   *     },
11108   *     {
11109   *         resolution:1,
11110   *         format:'png',
11111   *         src: 'image.png',
11112   *     },
11113   * ]);
11114   *
11115   * // With a url parser the information such as resolution and file format could extracted from the url itself:
11116   * extensions.add({
11117   *     extension: ExtensionType.ResolveParser,
11118   *     test: loadTextures.test, // test if url ends in an image
11119   *     parse: (value: string) =>
11120   *     ({
11121   *         resolution: parseFloat(Resolver.RETINA_PREFIX.exec(value)?.[1] ?? '1'),
11122   *         format: value.split('.').pop(),
11123   *         src: value,
11124   *     }),
11125   * });
11126   *
11127   * // Now resolution and format can be extracted from the url
11128   * resolver.add('foo', [
11129   *     '[email protected]',
11130   *     'image.png',
11131   * ]);
11132   */
11133  get parsers() {
11134    return this._parsers;
11135  }
11136  /** Used for testing, this resets the resolver to its initial state */
11137  reset() {
11138    this.setBundleIdentifier(this._defaultBundleIdentifierOptions);
11139    this._assetMap = {};
11140    this._preferredOrder = [];
11141    this._resolverHash = {};
11142    this._rootPath = null;
11143    this._basePath = null;
11144    this._manifest = null;
11145    this._bundles = {};
11146    this._defaultSearchParams = null;
11147  }
11148  /**
11149   * Sets the default URL search parameters for the URL resolver. The urls can be specified as a string or an object.
11150   * @param searchParams - the default url parameters to append when resolving urls
11151   */
11152  setDefaultSearchParams(searchParams) {
11153    if (typeof searchParams === "string") {
11154      this._defaultSearchParams = searchParams;
11155    } else {
11156      const queryValues = searchParams;
11157      this._defaultSearchParams = Object.keys(queryValues).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryValues[key])}`).join("&");
11158    }
11159  }
11160  /**
11161   * Returns the aliases for a given asset
11162   * @param asset - the asset to get the aliases for
11163   */
11164  getAlias(asset) {
11165    const { alias, src } = asset;
11166    const aliasesToUse = convertToList(
11167      alias || src,
11168      (value) => {
11169        if (typeof value === "string")
11170          return value;
11171        if (Array.isArray(value))
11172          return value.map((v) => v?.src ?? v);
11173        if (value?.src)
11174          return value.src;
11175        return value;
11176      },
11177      true
11178    );
11179    return aliasesToUse;
11180  }
11181  /**
11182   * Add a manifest to the asset resolver. This is a nice way to add all the asset information in one go.
11183   * generally a manifest would be built using a tool.
11184   * @param manifest - the manifest to add to the resolver
11185   */
11186  addManifest(manifest) {
11187    if (this._manifest) {
11188      warn("[Resolver] Manifest already exists, this will be overwritten");
11189    }
11190    this._manifest = manifest;
11191    manifest.bundles.forEach((bundle) => {
11192      this.addBundle(bundle.name, bundle.assets);
11193    });
11194  }
11195  /**
11196   * This adds a bundle of assets in one go so that you can resolve them as a group.
11197   * For example you could add a bundle for each screen in you pixi app
11198   * @example
11199   * resolver.addBundle('animals', [
11200   *  { alias: 'bunny', src: 'bunny.png' },
11201   *  { alias: 'chicken', src: 'chicken.png' },
11202   *  { alias: 'thumper', src: 'thumper.png' },
11203   * ]);
11204   * // or
11205   * resolver.addBundle('animals', {
11206   *     bunny: 'bunny.png',
11207   *     chicken: 'chicken.png',
11208   *     thumper: 'thumper.png',
11209   * });
11210   *
11211   * const resolvedAssets = await resolver.resolveBundle('animals');
11212   * @param bundleId - The id of the bundle to add
11213   * @param assets - A record of the asset or assets that will be chosen from when loading via the specified key
11214   */
11215  addBundle(bundleId, assets) {
11216    const assetNames = [];
11217    let convertedAssets = assets;
11218    if (!Array.isArray(assets)) {
11219      convertedAssets = Object.entries(assets).map(([alias, src]) => {
11220        if (typeof src === "string" || Array.isArray(src)) {
11221          return { alias, src };
11222        }
11223        return { alias, ...src };
11224      });
11225    }
11226    convertedAssets.forEach((asset) => {
11227      const srcs = asset.src;
11228      const aliases = asset.alias;
11229      let ids;
11230      if (typeof aliases === "string") {
11231        const bundleAssetId = this._createBundleAssetId(bundleId, aliases);
11232        assetNames.push(bundleAssetId);
11233        ids = [aliases, bundleAssetId];
11234      } else {
11235        const bundleIds = aliases.map((name) => this._createBundleAssetId(bundleId, name));
11236        assetNames.push(...bundleIds);
11237        ids = [...aliases, ...bundleIds];
11238      }
11239      this.add({
11240        ...asset,
11241        ...{
11242          alias: ids,
11243          src: srcs
11244        }
11245      });
11246    });
11247    this._bundles[bundleId] = assetNames;
11248  }
11249  /**
11250   * Tells the resolver what keys are associated with witch asset.
11251   * The most important thing the resolver does
11252   * @example
11253   * // Single key, single asset:
11254   * resolver.add({alias: 'foo', src: 'bar.png');
11255   * resolver.resolveUrl('foo') // => 'bar.png'
11256   *
11257   * // Multiple keys, single asset:
11258   * resolver.add({alias: ['foo', 'boo'], src: 'bar.png'});
11259   * resolver.resolveUrl('foo') // => 'bar.png'
11260   * resolver.resolveUrl('boo') // => 'bar.png'
11261   *
11262   * // Multiple keys, multiple assets:
11263   * resolver.add({alias: ['foo', 'boo'], src: ['bar.png', 'bar.webp']});
11264   * resolver.resolveUrl('foo') // => 'bar.png'
11265   *
11266   * // Add custom data attached to the resolver
11267   * Resolver.add({
11268   *     alias: 'bunnyBooBooSmooth',
11269   *     src: 'bunny{png,webp}',
11270   *     data: { scaleMode:SCALE_MODES.NEAREST }, // Base texture options
11271   * });
11272   *
11273   * resolver.resolve('bunnyBooBooSmooth') // => { src: 'bunny.png', data: { scaleMode: SCALE_MODES.NEAREST } }
11274   * @param aliases - the UnresolvedAsset or array of UnresolvedAssets to add to the resolver
11275   */
11276  add(aliases) {
11277    const assets = [];
11278    if (Array.isArray(aliases)) {
11279      assets.push(...aliases);
11280    } else {
11281      assets.push(aliases);
11282    }
11283    let keyCheck;
11284    keyCheck = (key) => {
11285      if (this.hasKey(key)) {
11286        warn(`[Resolver] already has key: ${key} overwriting`);
11287      }
11288    };
11289    const assetArray = convertToList(assets);
11290    assetArray.forEach((asset) => {
11291      const { src } = asset;
11292      let {
11293        data,
11294        format,
11295        loadParser: userDefinedLoadParser,
11296        parser: userDefinedParser
11297      } = asset;
11298      const srcsToUse = convertToList(src).map((src2) => {
11299        if (typeof src2 === "string") {
11300          return createStringVariations(src2);
11301        }
11302        return Array.isArray(src2) ? src2 : [src2];
11303      });
11304      const aliasesToUse = this.getAlias(asset);
11305      Array.isArray(aliasesToUse) ? aliasesToUse.forEach(keyCheck) : keyCheck(aliasesToUse);
11306      const resolvedAssets = [];
11307      const parseUrl = (url) => {
11308        const parser = this._parsers.find((p) => p.test(url));
11309        return {
11310          ...parser?.parse(url),
11311          src: url
11312        };
11313      };
11314      srcsToUse.forEach((srcs) => {
11315        srcs.forEach((src2) => {
11316          let formattedAsset = {};
11317          if (typeof src2 !== "object") {
11318            formattedAsset = parseUrl(src2);
11319          } else {
11320            data = src2.data ?? data;
11321            format = src2.format ?? format;
11322            if (src2.loadParser || src2.parser) {
11323              userDefinedLoadParser = src2.loadParser ?? userDefinedLoadParser;
11324              userDefinedParser = src2.parser ?? userDefinedParser;
11325            }
11326            formattedAsset = {
11327              ...parseUrl(src2.src),
11328              ...src2
11329            };
11330          }
11331          if (!aliasesToUse) {
11332            throw new Error(`[Resolver] alias is undefined for this asset: ${formattedAsset.src}`);
11333          }
vendor: 5,135 bytes, lines 11334-11498
11334          formattedAsset = this._buildResolvedAsset(formattedAsset, {
11335            aliases: aliasesToUse,
11336            data,
11337            format,
11338            loadParser: userDefinedLoadParser,
11339            parser: userDefinedParser,
11340            progressSize: asset.progressSize
11341          });
11342          resolvedAssets.push(formattedAsset);
11343        });
11344      });
11345      aliasesToUse.forEach((alias) => {
11346        this._assetMap[alias] = resolvedAssets;
11347      });
11348    });
11349  }
11350  // TODO: this needs an overload like load did in Assets
11351  /**
11352   * If the resolver has had a manifest set via setManifest, this will return the assets urls for
11353   * a given bundleId or bundleIds.
11354   * @example
11355   * // Manifest Example
11356   * const manifest = {
11357   *     bundles: [
11358   *         {
11359   *             name: 'load-screen',
11360   *             assets: [
11361   *                 {
11362   *                     alias: 'background',
11363   *                     src: 'sunset.png',
11364   *                 },
11365   *                 {
11366   *                     alias: 'bar',
11367   *                     src: 'load-bar.{png,webp}',
11368   *                 },
11369   *             ],
11370   *         },
11371   *         {
11372   *             name: 'game-screen',
11373   *             assets: [
11374   *                 {
11375   *                     alias: 'character',
11376   *                     src: 'robot.png',
11377   *                 },
11378   *                 {
11379   *                     alias: 'enemy',
11380   *                     src: 'bad-guy.png',
11381   *                 },
11382   *             ],
11383   *         },
11384   *     ]
11385   * };
11386   *
11387   * resolver.setManifest(manifest);
11388   * const resolved = resolver.resolveBundle('load-screen');
11389   * @param bundleIds - The bundle ids to resolve
11390   * @returns All the bundles assets or a hash of assets for each bundle specified
11391   */
11392  resolveBundle(bundleIds) {
11393    const singleAsset = isSingleItem(bundleIds);
11394    bundleIds = convertToList(bundleIds);
11395    const out = {};
11396    bundleIds.forEach((bundleId) => {
11397      const assetNames = this._bundles[bundleId];
11398      if (assetNames) {
11399        const results = this.resolve(assetNames);
11400        const assets = {};
11401        for (const key in results) {
11402          const asset = results[key];
11403          assets[this._extractAssetIdFromBundle(bundleId, key)] = asset;
11404        }
11405        out[bundleId] = assets;
11406      }
11407    });
11408    return singleAsset ? out[bundleIds[0]] : out;
11409  }
11410  /**
11411   * Does exactly what resolve does, but returns just the URL rather than the whole asset object
11412   * @param key - The key or keys to resolve
11413   * @returns - The URLs associated with the key(s)
11414   */
11415  resolveUrl(key) {
11416    const result = this.resolve(key);
11417    if (typeof key !== "string") {
11418      const out = {};
11419      for (const i in result) {
11420        out[i] = result[i].src;
11421      }
11422      return out;
11423    }
11424    return result.src;
11425  }
11426  resolve(keys) {
11427    const singleAsset = isSingleItem(keys);
11428    keys = convertToList(keys);
11429    const result = {};
11430    keys.forEach((key) => {
11431      if (!this._resolverHash[key]) {
11432        if (this._assetMap[key]) {
11433          let assets = this._assetMap[key];
11434          const preferredOrder = this._getPreferredOrder(assets);
11435          preferredOrder?.priority.forEach((priorityKey) => {
11436            preferredOrder.params[priorityKey].forEach((value) => {
11437              const filteredAssets = assets.filter((asset) => {
11438                if (asset[priorityKey]) {
11439                  return asset[priorityKey] === value;
11440                }
11441                return false;
11442              });
11443              if (filteredAssets.length) {
11444                assets = filteredAssets;
11445              }
11446            });
11447          });
11448          this._resolverHash[key] = assets[0];
11449        } else {
11450          this._resolverHash[key] = this._buildResolvedAsset({
11451            alias: [key],
11452            src: key
11453          }, {});
11454        }
11455      }
11456      result[key] = this._resolverHash[key];
11457    });
11458    return singleAsset ? result[keys[0]] : result;
11459  }
11460  /**
11461   * Checks if an asset with a given key exists in the resolver
11462   * @param key - The key of the asset
11463   */
11464  hasKey(key) {
11465    return !!this._assetMap[key];
11466  }
11467  /**
11468   * Checks if a bundle with the given key exists in the resolver
11469   * @param key - The key of the bundle
11470   */
11471  hasBundle(key) {
11472    return !!this._bundles[key];
11473  }
11474  /**
11475   * Internal function for figuring out what prefer criteria an asset should use.
11476   * @param assets
11477   */
11478  _getPreferredOrder(assets) {
11479    for (let i = 0; i < assets.length; i++) {
11480      const asset = assets[i];
11481      const preferred = this._preferredOrder.find((preference) => preference.params.format.includes(asset.format));
11482      if (preferred) {
11483        return preferred;
11484      }
11485    }
11486    return this._preferredOrder[0];
11487  }
11488  /**
11489   * Appends the default url parameters to the url
11490   * @param url - The url to append the default parameters to
11491   * @returns - The url with the default parameters appended
11492   */
11493  _appendDefaultSearchParams(url) {
11494    if (!this._defaultSearchParams)
11495      return url;
11496    const paramConnector = /\?/.test(url) ? "&" : "?";
11497    return `${url}${paramConnector}${this._defaultSearchParams}`;
11498  }
11499  _buildResolvedAsset(formattedAsset, data) {
11500    const { aliases, data: assetData, loadParser, parser, format, progressSize } = data;
11501    if (this._basePath || this._rootPath) {
11502      formattedAsset.src = path.toAbsolute(formattedAsset.src, this._basePath, this._rootPath);
11503    }
11504    formattedAsset.alias = aliases ?? formattedAsset.alias ?? [formattedAsset.src];
11505    formattedAsset.src = this._appendDefaultSearchParams(formattedAsset.src);
11506    formattedAsset.data = { ...assetData || {}, ...formattedAsset.data };
11507    formattedAsset.loadParser = loadParser ?? formattedAsset.loadParser;
11508    formattedAsset.parser = parser ?? formattedAsset.parser;
11509    formattedAsset.format = format ?? formattedAsset.format ?? getUrlExtension(formattedAsset.src);
11510    if (progressSize !== void 0) {
11511      formattedAsset.progressSize = progressSize;
11512    }
11513    return formattedAsset;
11514  }
11515}
11516/**
11517 * The prefix that denotes a URL is for a retina asset.
11518 * @default /@([0-9\.]+)x/
11519 * @example `@2x`
11520 */
11521Resolver.RETINA_PREFIX = /@([0-9\.]+)x/;
11522function getUrlExtension(url) {
11523  return url.split(".").pop().split("?").shift().split("#").shift();
11524}
11525
11526const copySearchParams = (targetUrl, sourceUrl) => {
11527  const searchParams = sourceUrl.split("?")[1];
11528  if (searchParams) {
11529    targetUrl += `?${searchParams}`;
11530  }
11531  return targetUrl;
11532};
11533
11534const _Spritesheet = class _Spritesheet {
11535  constructor(optionsOrTexture, arg1) {
11536    /** For multi-packed spritesheets, this contains a reference to all the other spritesheets it depends on. */
11537    this.linkedSheets = [];
11538    let options = optionsOrTexture;
11539    if (optionsOrTexture?.source instanceof TextureSource) {
11540      options = {
11541        texture: optionsOrTexture,
11542        data: arg1
11543      };
11544    }
11545    const { texture, data, cachePrefix = "" } = options;
11546    this.cachePrefix = cachePrefix;
11547    this._texture = texture instanceof Texture ? texture : null;
11548    this.textureSource = texture.source;
11549    this.textures = {};
11550    this.animations = {};
11551    this.data = data;
11552    const metaResolution = parseFloat(data.meta.scale);
11553    if (metaResolution) {
11554      this.resolution = metaResolution;
11555      texture.source.resolution = this.resolution;
11556    } else {
11557      this.resolution = texture.source._resolution;
11558    }
11559    this._frames = this.data.frames;
11560    this._frameKeys = Object.keys(this._frames);
11561    this._batchIndex = 0;
11562    this._callback = null;
11563  }
11564  /**
11565   * Parser spritesheet from loaded data. This is done asynchronously
11566   * to prevent creating too many Texture within a single process.
11567   */
11568  parse() {
11569    return new Promise((resolve) => {
11570      this._callback = resolve;
11571      this._batchIndex = 0;
11572      if (this._frameKeys.length <= _Spritesheet.BATCH_SIZE) {
11573        this._processFrames(0);
11574        this._processAnimations();
11575        this._parseComplete();
11576      } else {
11577        this._nextBatch();
11578      }
11579    });
11580  }
11581  /**
11582   * Process a batch of frames
11583   * @param initialFrameIndex - The index of frame to start.
11584   */
11585  _processFrames(initialFrameIndex) {
11586    let frameIndex = initialFrameIndex;
11587    const maxFrames = _Spritesheet.BATCH_SIZE;
11588    while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) {
11589      const i = this._frameKeys[frameIndex];
11590      const data = this._frames[i];
11591      const rect = data.frame;
11592      if (rect) {
11593        let frame = null;
11594        let trim = null;
11595        const sourceSize = data.trimmed !== false && data.sourceSize ? data.sourceSize : data.frame;
11596        const orig = new Rectangle(
11597          0,
11598          0,
11599          Math.floor(sourceSize.w) / this.resolution,
11600          Math.floor(sourceSize.h) / this.resolution
11601        );
11602        if (data.rotated) {
11603          frame = new Rectangle(
11604            Math.floor(rect.x) / this.resolution,
11605            Math.floor(rect.y) / this.resolution,
11606            Math.floor(rect.h) / this.resolution,
11607            Math.floor(rect.w) / this.resolution
11608          );
11609        } else {
11610          frame = new Rectangle(
11611            Math.floor(rect.x) / this.resolution,
11612            Math.floor(rect.y) / this.resolution,
11613            Math.floor(rect.w) / this.resolution,
11614            Math.floor(rect.h) / this.resolution
11615          );
11616        }
11617        if (data.trimmed !== false && data.spriteSourceSize) {
11618          trim = new Rectangle(
11619            Math.floor(data.spriteSourceSize.x) / this.resolution,
11620            Math.floor(data.spriteSourceSize.y) / this.resolution,
11621            Math.floor(rect.w) / this.resolution,
11622            Math.floor(rect.h) / this.resolution
11623          );
11624        }
11625        this.textures[i] = new Texture({
11626          source: this.textureSource,
11627          frame,
11628          orig,
11629          trim,
11630          rotate: data.rotated ? 2 : 0,
11631          defaultAnchor: data.anchor,
11632          defaultBorders: data.borders,
11633          label: i.toString()
11634        });
11635      }
11636      frameIndex++;
11637    }
11638  }
11639  /** Parse animations config. */
11640  _processAnimations() {
11641    const animations = this.data.animations || {};
11642    for (const animName in animations) {
11643      this.animations[animName] = [];
11644      for (let i = 0; i < animations[animName].length; i++) {
11645        const frameName = animations[animName][i];
11646        this.animations[animName].push(this.textures[frameName]);
11647      }
11648    }
11649  }
11650  /** The parse has completed. */
11651  _parseComplete() {
11652    const callback = this._callback;
11653    this._callback = null;
11654    this._batchIndex = 0;
11655    callback.call(this, this.textures);
11656  }
11657  /** Begin the next batch of textures. */
11658  _nextBatch() {
11659    this._processFrames(this._batchIndex * _Spritesheet.BATCH_SIZE);
11660    this._batchIndex++;
11661    setTimeout(() => {
11662      if (this._batchIndex * _Spritesheet.BATCH_SIZE < this._frameKeys.length) {
11663        this._nextBatch();
11664      } else {
11665        this._processAnimations();
11666        this._parseComplete();
11667      }
11668    }, 0);
11669  }
11670  /**
11671   * Destroy Spritesheet and don't use after this.
11672   * @param {boolean} [destroyBase=false] - Whether to destroy the base texture as well
11673   */
11674  destroy(destroyBase = false) {
11675    for (const i in this.textures) {
11676      this.textures[i].destroy();
11677    }
11678    this._frames = null;
11679    this._frameKeys = null;
11680    this.data = null;
11681    this.textures = null;
11682    if (destroyBase) {
11683      this._texture?.destroy();
11684      this.textureSource.destroy();
11685    }
11686    this._texture = null;
11687    this.textureSource = null;
11688    this.linkedSheets = [];
11689  }
11690};
11691/**
11692 * The maximum number of Textures to build per process.
11693 * @advanced
11694 */
11695_Spritesheet.BATCH_SIZE = 1e3;
11696let Spritesheet = _Spritesheet;
11697
11698const validImages = [
11699  "jpg",
11700  "png",
11701  "jpeg",
11702  "avif",
11703  "webp",
11704  "basis",
11705  "etc2",
11706  "bc7",
11707  "bc6h",
11708  "bc5",
11709  "bc4",
11710  "bc3",
11711  "bc2",
11712  "bc1",
11713  "eac",
11714  "astc"
11715];
11716function getCacheableAssets(keys, asset, ignoreMultiPack) {
11717  const out = {};
11718  keys.forEach((key) => {
11719    out[key] = asset;
11720  });
11721  Object.keys(asset.textures).forEach((key) => {
11722    out[`${asset.cachePrefix}${key}`] = asset.textures[key];
11723  });
11724  if (!ignoreMultiPack) {
11725    const basePath = path.dirname(keys[0]);
11726    asset.linkedSheets.forEach((item, i) => {
11727      const out2 = getCacheableAssets([`${basePath}/${asset.data.meta.related_multi_packs[i]}`], item, true);
11728      Object.assign(out, out2);
11729    });
11730  }
11731  return out;
11732}
11733const spritesheetAsset = {
11734  extension: ExtensionType.Asset,
11735  /** Handle the caching of the related Spritesheet Textures */
11736  cache: {
11737    test: (asset) => asset instanceof Spritesheet,
11738    getCacheableAssets: (keys, asset) => getCacheableAssets(keys, asset, false)
11739  },
11740  /** Resolve the resolution of the asset. */
11741  resolver: {
11742    extension: {
11743      type: ExtensionType.ResolveParser,
11744      name: "resolveSpritesheet"
11745    },
11746    test: (value) => {
11747      const tempURL = value.split("?")[0];
11748      const split = tempURL.split(".");
11749      const extension = split.pop();
11750      const format = split.pop();
11751      return extension === "json" && validImages.includes(format);
11752    },
11753    parse: (value) => {
11754      const split = value.split(".");
11755      return {
11756        resolution: parseFloat(Resolver.RETINA_PREFIX.exec(value)?.[1] ?? "1"),
11757        format: split[split.length - 2],
11758        src: value
11759      };
11760    }
11761  },
11762  /**
11763   * Loader plugin that parses sprite sheets!
11764   * once the JSON has been loaded this checks to see if the JSON is spritesheet data.
11765   * If it is, we load the spritesheets image and parse the data into Spritesheet
11766   * All textures in the sprite sheet are then added to the cache
11767   */
11768  loader: {
11769    /** used for deprecation purposes */
11770    name: "spritesheetLoader",
11771    id: "spritesheet",
11772    extension: {
11773      type: ExtensionType.LoadParser,
11774      priority: LoaderParserPriority.Normal,
11775      name: "spritesheetLoader"
11776    },
11777    async testParse(asset, options) {
11778      return path.extname(options.src).toLowerCase() === ".json" && !!asset.frames;
11779    },
11780    async parse(asset, options, loader) {
11781      const {
11782        texture: imageTexture,
11783        // if user need to use preloaded texture
11784        imageFilename,
11785        // if user need to use custom filename (not from jsonFile.meta.image)
11786        textureOptions,
11787        // if user need to set texture options on texture
11788        cachePrefix
11789        // if user need to use custom cache prefix
11790      } = options?.data ?? {};
11791      let basePath = path.dirname(options.src);
11792      if (basePath && basePath.lastIndexOf("/") !== basePath.length - 1) {
11793        basePath += "/";
11794      }
11795      let texture;
11796      if (imageTexture instanceof Texture) {
11797        texture = imageTexture;
11798      } else {
11799        const imagePath = copySearchParams(basePath + (imageFilename ?? asset.meta.image), options.src);
11800        const assets = await loader.load([{ src: imagePath, data: textureOptions }]);
11801        texture = assets[imagePath];
11802      }
11803      const spritesheet = new Spritesheet({
11804        texture: texture.source,
11805        data: asset,
11806        cachePrefix
11807      });
11808      await spritesheet.parse();
11809      const multiPacks = asset?.meta?.related_multi_packs;
11810      if (Array.isArray(multiPacks)) {
11811        const promises = [];
11812        for (const item of multiPacks) {
11813          if (typeof item !== "string") {
11814            continue;
11815          }
11816          let itemUrl = basePath + item;
11817          if (options.data?.ignoreMultiPack) {
11818            continue;
11819          }
11820          itemUrl = copySearchParams(itemUrl, options.src);
11821          promises.push(loader.load({
11822            src: itemUrl,
11823            data: {
11824              textureOptions,
11825              ignoreMultiPack: true
11826            }
11827          }));
11828        }
11829        const res = await Promise.all(promises);
11830        spritesheet.linkedSheets = res;
11831        res.forEach((item) => {
11832          item.linkedSheets = [spritesheet].concat(spritesheet.linkedSheets.filter((sp) => sp !== item));
11833        });
11834      }
11835      return spritesheet;
11836    },
11837    async unload(spritesheet, _resolvedAsset, loader) {
11838      await loader.unload(spritesheet.textureSource._sourceOrigin);
11839      spritesheet.destroy(false);
11840    }
11841  }
11842};
11843
11844extensions.add(spritesheetAsset);
11845
11846const idCounts = /* @__PURE__ */ Object.create(null);
11847const idHash = /* @__PURE__ */ Object.create(null);
11848function createIdFromString(value, groupId) {
11849  let id = idHash[value];
11850  if (id === void 0) {
11851    if (idCounts[groupId] === void 0) {
11852      idCounts[groupId] = 1;
11853    }
11854    idHash[value] = id = idCounts[groupId]++;
11855  }
11856  return id;
11857}
11858
11859let context;
11860function getTestContext() {
11861  if (!context || context?.isContextLost()) {
11862    const canvas = DOMAdapter.get().createCanvas();
11863    context = canvas.getContext("webgl", {});
11864  }
11865  return context;
11866}
11867
11868let maxFragmentPrecision;
11869function getMaxFragmentPrecision() {
11870  if (!maxFragmentPrecision) {
11871    maxFragmentPrecision = "mediump";
11872    const gl = getTestContext();
11873    if (gl) {
11874      if (gl.getShaderPrecisionFormat) {
11875        const shaderFragment = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
11876        maxFragmentPrecision = shaderFragment.precision ? "highp" : "mediump";
11877      }
11878    }
11879  }
11880  return maxFragmentPrecision;
11881}
11882
11883function addProgramDefines(src, isES300, isFragment) {
11884  if (isES300)
11885    return src;
11886  if (isFragment) {
11887    src = src.replace("out vec4 finalColor;", "");
11888    return `
11889
11890        #ifdef GL_ES // This checks if it is WebGL1
11891        #define in varying
11892        #define finalColor gl_FragColor
11893        #define texture texture2D
11894        #endif
11895        ${src}
11896        `;
11897  }
11898  return `
11899
11900        #ifdef GL_ES // This checks if it is WebGL1
11901        #define in attribute
11902        #define out varying
11903        #endif
11904        ${src}
11905        `;
11906}
11907
11908function ensurePrecision(src, options, isFragment) {
11909  const maxSupportedPrecision = isFragment ? options.maxSupportedFragmentPrecision : options.maxSupportedVertexPrecision;
11910  if (src.substring(0, 9) !== "precision") {
11911    let precision = isFragment ? options.requestedFragmentPrecision : options.requestedVertexPrecision;
11912    if (precision === "highp" && maxSupportedPrecision !== "highp") {
11913      precision = "mediump";
11914    }
11915    return `precision ${precision} float;
11916${src}`;
11917  } else if (maxSupportedPrecision !== "highp" && src.substring(0, 15) === "precision highp") {
11918    return src.replace("precision highp", "precision mediump");
11919  }
11920  return src;
11921}
11922
11923function insertVersion(src, isES300) {
11924  if (!isES300)
11925    return src;
11926  return `#version 300 es
11927${src}`;
11928}
11929
11930const fragmentNameCache = {};
11931const VertexNameCache = {};
11932function setProgramName(src, { name = `pixi-program` }, isFragment = true) {
11933  name = name.replace(/\s+/g, "-");
11934  name += isFragment ? "-fragment" : "-vertex";
11935  const nameCache = isFragment ? fragmentNameCache : VertexNameCache;
11936  if (nameCache[name]) {
11937    nameCache[name]++;
11938    name += `-${nameCache[name]}`;
11939  } else {
11940    nameCache[name] = 1;
11941  }
11942  if (src.indexOf("#define SHADER_NAME") !== -1)
11943    return src;
11944  const shaderName = `#define SHADER_NAME ${name}`;
11945  return `${shaderName}
11946${src}`;
11947}
11948
11949function stripVersion(src, isES300) {
11950  if (!isES300)
11951    return src;
11952  return src.replace("#version 300 es", "");
11953}
11954
11955const processes = {
11956  // strips any version headers..
11957  stripVersion,
11958  // adds precision string if not already present
11959  ensurePrecision,
11960  // add some defines if WebGL1 to make it more compatible with WebGL2 shaders
11961  addProgramDefines,
11962  // add the program name to the shader
11963  setProgramName,
11964  // add the version string to the shader header
11965  insertVersion
11966};
11967const programCache$1 = /* @__PURE__ */ Object.create(null);
11968const _GlProgram = class _GlProgram {
11969  /**
11970   * Creates a shiny new GlProgram. Used by WebGL renderer.
11971   * @param options - The options for the program.
11972   */
11973  constructor(options) {
11974    options = { ..._GlProgram.defaultOptions, ...options };
11975    const isES300 = options.fragment.indexOf("#version 300 es") !== -1;
11976    const preprocessorOptions = {
11977      stripVersion: isES300,
11978      ensurePrecision: {
11979        requestedFragmentPrecision: options.preferredFragmentPrecision,
11980        requestedVertexPrecision: options.preferredVertexPrecision,
11981        maxSupportedVertexPrecision: "highp",
11982        maxSupportedFragmentPrecision: getMaxFragmentPrecision()
11983      },
11984      setProgramName: {
11985        name: options.name
11986      },
11987      addProgramDefines: isES300,
11988      insertVersion: isES300
11989    };
11990    let fragment = options.fragment;
11991    let vertex = options.vertex;
11992    Object.keys(processes).forEach((processKey) => {
11993      const processOptions = preprocessorOptions[processKey];
11994      fragment = processes[processKey](fragment, processOptions, true);
11995      vertex = processes[processKey](vertex, processOptions, false);
11996    });
11997    this.fragment = fragment;
11998    this.vertex = vertex;
11999    this.transformFeedbackVaryings = options.transformFeedbackVaryings;
12000    this._key = createIdFromString(`${this.vertex}:${this.fragment}`, "gl-program");
12001  }
12002  /** destroys the program */
12003  destroy() {
12004    this.fragment = null;
12005    this.vertex = null;
12006    this._attributeData = null;
12007    this._uniformData = null;
12008    this._uniformBlockData = null;
12009    this.transformFeedbackVaryings = null;
12010    programCache$1[this._cacheKey] = null;
12011  }
12012  /**
12013   * Helper function that creates a program for a given source.
12014   * It will check the program cache if the program has already been created.
12015   * If it has that one will be returned, if not a new one will be created and cached.
12016   * @param options - The options for the program.
12017   * @returns A program using the same source
12018   */
12019  static from(options) {
12020    const key = `${options.vertex}:${options.fragment}`;
12021    if (!programCache$1[key]) {
12022      programCache$1[key] = new _GlProgram(options);
12023      programCache$1[key]._cacheKey = key;
12024    }
12025    return programCache$1[key];
12026  }
12027};
12028/** The default options used by the program. */
12029_GlProgram.defaultOptions = {
12030  preferredVertexPrecision: "highp",
12031  preferredFragmentPrecision: "mediump"
12032};
12033let GlProgram = _GlProgram;
12034
12035const attributeFormatData = {
12036  uint8x2: { size: 2, stride: 2, normalised: false },
12037  uint8x4: { size: 4, stride: 4, normalised: false },
12038  sint8x2: { size: 2, stride: 2, normalised: false },
12039  sint8x4: { size: 4, stride: 4, normalised: false },
12040  unorm8x2: { size: 2, stride: 2, normalised: true },
12041  unorm8x4: { size: 4, stride: 4, normalised: true },
12042  snorm8x2: { size: 2, stride: 2, normalised: true },
12043  snorm8x4: { size: 4, stride: 4, normalised: true },
12044  uint16x2: { size: 2, stride: 4, normalised: false },
12045  uint16x4: { size: 4, stride: 8, normalised: false },
12046  sint16x2: { size: 2, stride: 4, normalised: false },
12047  sint16x4: { size: 4, stride: 8, normalised: false },
12048  unorm16x2: { size: 2, stride: 4, normalised: true },
12049  unorm16x4: { size: 4, stride: 8, normalised: true },
12050  snorm16x2: { size: 2, stride: 4, normalised: true },
12051  snorm16x4: { size: 4, stride: 8, normalised: true },
12052  float16x2: { size: 2, stride: 4, normalised: false },
12053  float16x4: { size: 4, stride: 8, normalised: false },
12054  float32: { size: 1, stride: 4, normalised: false },
12055  float32x2: { size: 2, stride: 8, normalised: false },
12056  float32x3: { size: 3, stride: 12, normalised: false },
12057  float32x4: { size: 4, stride: 16, normalised: false },
12058  uint32: { size: 1, stride: 4, normalised: false },
12059  uint32x2: { size: 2, stride: 8, normalised: false },
12060  uint32x3: { size: 3, stride: 12, normalised: false },
12061  uint32x4: { size: 4, stride: 16, normalised: false },
12062  sint32: { size: 1, stride: 4, normalised: false },
12063  sint32x2: { size: 2, stride: 8, normalised: false },
12064  sint32x3: { size: 3, stride: 12, normalised: false },
12065  sint32x4: { size: 4, stride: 16, normalised: false }
12066};
12067function getAttributeInfoFromFormat(format) {
12068  return attributeFormatData[format] ?? attributeFormatData.float32;
12069}
12070
12071const WGSL_TO_VERTEX_TYPES = {
12072  f32: "float32",
12073  "vec2<f32>": "float32x2",
12074  "vec3<f32>": "float32x3",
12075  "vec4<f32>": "float32x4",
12076  vec2f: "float32x2",
12077  vec3f: "float32x3",
12078  vec4f: "float32x4",
12079  i32: "sint32",
12080  "vec2<i32>": "sint32x2",
12081  "vec3<i32>": "sint32x3",
12082  "vec4<i32>": "sint32x4",
12083  u32: "uint32",
12084  "vec2<u32>": "uint32x2",
12085  "vec3<u32>": "uint32x3",
12086  "vec4<u32>": "uint32x4",
12087  bool: "uint32",
12088  "vec2<bool>": "uint32x2",
12089  "vec3<bool>": "uint32x3",
12090  "vec4<bool>": "uint32x4"
12091};
12092function extractAttributesFromGpuProgram({ source, entryPoint }) {
12093  const results = {};
12094  const mainVertStart = source.indexOf(`fn ${entryPoint}`);
12095  if (mainVertStart !== -1) {
12096    const arrowFunctionStart = source.indexOf("->", mainVertStart);
12097    if (arrowFunctionStart !== -1) {
12098      const functionArgsSubstring = source.substring(mainVertStart, arrowFunctionStart);
12099      const inputsRegex = /@location\((\d+)\)\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)(?:,|\s|$)/g;
12100      let match;
12101      while ((match = inputsRegex.exec(functionArgsSubstring)) !== null) {
12102        const format = WGSL_TO_VERTEX_TYPES[match[3]] ?? "float32";
12103        results[match[2]] = {
12104          location: parseInt(match[1], 10),
12105          format,
12106          stride: getAttributeInfoFromFormat(format).stride,
12107          offset: 0,
12108          instance: false,
12109          start: 0
12110        };
12111      }
12112    }
12113  }
12114  return results;
12115}
12116
12117function extractStructAndGroups(wgsl) {
12118  const linePattern = /(^|[^/])@(group|binding)\(\d+\)[^;]+;/g;
12119  const groupPattern = /@group\((\d+)\)/;
12120  const bindingPattern = /@binding\((\d+)\)/;
12121  const namePattern = /var(<[^>]+>)? (\w+)/;
12122  const typePattern = /:\s*(\w+)/;
12123  const structPattern = /struct\s+(\w+)\s*{([^}]+)}/g;
12124  const structMemberPattern = /(\w+)\s*:\s*([\w\<\>]+)/g;
12125  const structName = /struct\s+(\w+)/;
12126  const groups = wgsl.match(linePattern)?.map((item) => ({
12127    group: parseInt(item.match(groupPattern)[1], 10),
12128    binding: parseInt(item.match(bindingPattern)[1], 10),
12129    name: item.match(namePattern)[2],
12130    isUniform: item.match(namePattern)[1] === "<uniform>",
12131    type: item.match(typePattern)[1]
12132  }));
12133  if (!groups) {
12134    return {
12135      groups: [],
12136      structs: []
12137    };
12138  }
12139  const structs = wgsl.match(structPattern)?.map((struct) => {
12140    const name = struct.match(structName)[1];
12141    const members = struct.match(structMemberPattern).reduce((acc, member) => {
12142      const [name2, type] = member.split(":");
12143      acc[name2.trim()] = type.trim();
12144      return acc;
12145    }, {});
12146    if (!members) {
12147      return null;
12148    }
12149    return { name, members };
12150  }).filter(({ name }) => groups.some((group) => group.type === name)) ?? [];
12151  return {
12152    groups,
12153    structs
12154  };
12155}
12156
12157var ShaderStage = /* @__PURE__ */ ((ShaderStage2) => {
12158  ShaderStage2[ShaderStage2["VERTEX"] = 1] = "VERTEX";
12159  ShaderStage2[ShaderStage2["FRAGMENT"] = 2] = "FRAGMENT";
12160  ShaderStage2[ShaderStage2["COMPUTE"] = 4] = "COMPUTE";
12161  return ShaderStage2;
12162})(ShaderStage || {});
12163
12164function generateGpuLayoutGroups({ groups }) {
12165  const layout = [];
12166  for (let i = 0; i < groups.length; i++) {
12167    const group = groups[i];
12168    if (!layout[group.group]) {
12169      layout[group.group] = [];
12170    }
12171    if (group.isUniform) {
12172      layout[group.group].push({
12173        binding: group.binding,
12174        visibility: ShaderStage.VERTEX | ShaderStage.FRAGMENT,
12175        buffer: {
12176          type: "uniform"
12177        }
12178      });
12179    } else if (group.type === "sampler") {
12180      layout[group.group].push({
12181        binding: group.binding,
12182        visibility: ShaderStage.FRAGMENT,
12183        sampler: {
12184          type: "filtering"
12185        }
12186      });
12187    } else if (group.type === "texture_2d") {
12188      layout[group.group].push({
12189        binding: group.binding,
12190        visibility: ShaderStage.FRAGMENT,
12191        texture: {
12192          sampleType: "float",
12193          viewDimension: "2d",
12194          multisampled: false
12195        }
12196      });
12197    }
12198  }
12199  return layout;
12200}
12201
12202function generateLayoutHash({ groups }) {
12203  const layout = [];
12204  for (let i = 0; i < groups.length; i++) {
12205    const group = groups[i];
12206    if (!layout[group.group]) {
12207      layout[group.group] = {};
12208    }
12209    layout[group.group][group.name] = group.binding;
12210  }
12211  return layout;
12212}
12213
12214function removeStructAndGroupDuplicates(vertexStructsAndGroups, fragmentStructsAndGroups) {
12215  const structNameSet = /* @__PURE__ */ new Set();
12216  const dupeGroupKeySet = /* @__PURE__ */ new Set();
12217  const structs = [...vertexStructsAndGroups.structs, ...fragmentStructsAndGroups.structs].filter((struct) => {
12218    if (structNameSet.has(struct.name)) {
12219      return false;
12220    }
12221    structNameSet.add(struct.name);
12222    return true;
12223  });
12224  const groups = [...vertexStructsAndGroups.groups, ...fragmentStructsAndGroups.groups].filter((group) => {
12225    const key = `${group.name}-${group.binding}`;
12226    if (dupeGroupKeySet.has(key)) {
12227      return false;
12228    }
12229    dupeGroupKeySet.add(key);
12230    return true;
12231  });
12232  return { structs, groups };
12233}
12234
12235const programCache = /* @__PURE__ */ Object.create(null);
12236class GpuProgram {
12237  /**
12238   * Create a new GpuProgram
12239   * @param options - The options for the gpu program
12240   */
12241  constructor(options) {
12242    /** @internal */
12243    this._layoutKey = 0;
12244    /** @internal */
12245    this._attributeLocationsKey = 0;
12246    const { fragment, vertex, layout, gpuLayout, name } = options;
12247    this.name = name;
12248    this.fragment = fragment;
12249    this.vertex = vertex;
12250    if (fragment.source === vertex.source) {
12251      const structsAndGroups = extractStructAndGroups(fragment.source);
12252      this.structsAndGroups = structsAndGroups;
12253    } else {
12254      const vertexStructsAndGroups = extractStructAndGroups(vertex.source);
12255      const fragmentStructsAndGroups = extractStructAndGroups(fragment.source);
12256      this.structsAndGroups = removeStructAndGroupDuplicates(vertexStructsAndGroups, fragmentStructsAndGroups);
12257    }
12258    this.layout = layout ?? generateLayoutHash(this.structsAndGroups);
12259    this.gpuLayout = gpuLayout ?? generateGpuLayoutGroups(this.structsAndGroups);
12260    this.autoAssignGlobalUniforms = !!(this.layout[0]?.globalUniforms !== void 0);
12261    this.autoAssignLocalUniforms = !!(this.layout[1]?.localUniforms !== void 0);
12262    this._generateProgramKey();
12263  }
12264  // TODO maker this pure
12265  _generateProgramKey() {
12266    const { vertex, fragment } = this;
12267    const bigKey = vertex.source + fragment.source + vertex.entryPoint + fragment.entryPoint;
12268    this._layoutKey = createIdFromString(bigKey, "program");
12269  }
12270  get attributeData() {
12271    this._attributeData ?? (this._attributeData = extractAttributesFromGpuProgram(this.vertex));
12272    return this._attributeData;
12273  }
12274  /** destroys the program */
12275  destroy() {
12276    this.gpuLayout = null;
12277    this.layout = null;
12278    this.structsAndGroups = null;
12279    this.fragment = null;
12280    this.vertex = null;
12281    programCache[this._cacheKey] = null;
12282  }
12283  /**
12284   * Helper function that creates a program for a given source.
12285   * It will check the program cache if the program has already been created.
12286   * If it has that one will be returned, if not a new one will be created and cached.
12287   * @param options - The options for the program.
12288   * @returns A program using the same source
12289   */
12290  static from(options) {
12291    const key = `${options.vertex.source}:${options.fragment.source}:${options.fragment.entryPoint}:${options.vertex.entryPoint}`;
12292    if (!programCache[key]) {
12293      programCache[key] = new GpuProgram(options);
12294      programCache[key]._cacheKey = key;
12295    }
12296    return programCache[key];
12297  }
12298}
12299
12300const UNIFORM_TYPES_VALUES = [
12301  "f32",
12302  "i32",
12303  "vec2<f32>",
12304  "vec3<f32>",
12305  "vec4<f32>",
12306  "mat2x2<f32>",
12307  "mat3x3<f32>",
12308  "mat4x4<f32>",
12309  "mat3x2<f32>",
12310  "mat4x2<f32>",
12311  "mat2x3<f32>",
12312  "mat4x3<f32>",
12313  "mat2x4<f32>",
12314  "mat3x4<f32>",
12315  "vec2<i32>",
12316  "vec3<i32>",
12317  "vec4<i32>"
12318];
12319const UNIFORM_TYPES_MAP = UNIFORM_TYPES_VALUES.reduce((acc, type) => {
12320  acc[type] = true;
12321  return acc;
12322}, {});
12323
12324function getDefaultUniformValue(type, size) {
12325  switch (type) {
12326    case "f32":
12327      return 0;
12328    case "vec2<f32>":
12329      return new Float32Array(2 * size);
12330    case "vec3<f32>":
12331      return new Float32Array(3 * size);
12332    case "vec4<f32>":
12333      return new Float32Array(4 * size);
12334    case "mat2x2<f32>":
12335      return new Float32Array([
12336        1,
12337        0,
12338        0,
12339        1
12340      ]);
12341    case "mat3x3<f32>":
12342      return new Float32Array([
12343        1,
12344        0,
12345        0,
12346        0,
12347        1,
12348        0,
12349        0,
12350        0,
12351        1
12352      ]);
12353    case "mat4x4<f32>":
12354      return new Float32Array([
12355        1,
12356        0,
12357        0,
12358        0,
12359        0,
12360        1,
12361        0,
12362        0,
12363        0,
12364        0,
12365        1,
12366        0,
12367        0,
12368        0,
12369        0,
12370        1
12371      ]);
12372  }
12373  return null;
12374}
12375
12376const _UniformGroup = class _UniformGroup {
12377  /**
12378   * Create a new Uniform group
12379   * @param uniformStructures - The structures of the uniform group
12380   * @param options - The optional parameters of this uniform group
12381   */
12382  constructor(uniformStructures, options) {
12383    /**
12384     * used internally to know if a uniform group was used in the last render pass
12385     * @internal
12386     */
12387    this._touched = 0;
12388    /** a unique id for this uniform group used through the renderer */
12389    this.uid = uid("uniform");
12390    /**
12391     * a resource type, used to identify how to handle it when its in a bind group / shader resource
12392     * @internal
12393     */
12394    this._resourceType = "uniformGroup";
12395    /**
12396     * the resource id used internally by the renderer to build bind group keys
12397     * @internal
12398     */
12399    this._resourceId = uid("resource");
12400    /** used ito identify if this is a uniform group */
12401    this.isUniformGroup = true;
12402    /**
12403     * used to flag if this Uniform groups data is different from what it has stored in its buffer / on the GPU
12404     * @internal
12405     */
12406    this._dirtyId = 0;
12407    // implementing the interface - UniformGroup are not destroyed
12408    this.destroyed = false;
12409    options = { ..._UniformGroup.defaultOptions, ...options };
12410    this.uniformStructures = uniformStructures;
12411    const uniforms = {};
12412    for (const i in uniformStructures) {
12413      const uniformData = uniformStructures[i];
12414      uniformData.name = i;
12415      uniformData.size = uniformData.size ?? 1;
12416      if (!UNIFORM_TYPES_MAP[uniformData.type]) {
12417        const arrayMatch = uniformData.type.match(/^array<(\w+(?:<\w+>)?),\s*(\d+)>$/);
12418        if (arrayMatch) {
12419          const [, innerType, size] = arrayMatch;
12420          throw new Error(
12421            `Uniform type ${uniformData.type} is not supported. Use type: '${innerType}', size: ${size} instead.`
12422          );
12423        }
12424        throw new Error(`Uniform type ${uniformData.type} is not supported. Supported uniform types are: ${UNIFORM_TYPES_VALUES.join(", ")}`);
12425      }
12426      uniformData.value ?? (uniformData.value = getDefaultUniformValue(uniformData.type, uniformData.size));
12427      uniforms[i] = uniformData.value;
12428    }
12429    this.uniforms = uniforms;
12430    this._dirtyId = 1;
12431    this.ubo = options.ubo;
12432    this.isStatic = options.isStatic;
12433    this._signature = createIdFromString(Object.keys(uniforms).map(
12434      (i) => `${i}-${uniformStructures[i].type}`
12435    ).join("-"), "uniform-group");
12436  }
12437  /** Call this if you want the uniform groups data to be uploaded to the GPU only useful if `isStatic` is true. */
12438  update() {
12439    this._dirtyId++;
12440  }
12441};
12442/** The default options used by the uniform group. */
12443_UniformGroup.defaultOptions = {
12444  /** if true the UniformGroup is handled as an Uniform buffer object. */
12445  ubo: false,
12446  /** if true, then you are responsible for when the data is uploaded to the GPU by calling `update()` */
12447  isStatic: false
12448};
12449let UniformGroup = _UniformGroup;
12450
12451class BindGroup {
12452  /**
12453   * Create a new instance eof the Bind Group.
12454   * @param resources - The resources that are bound together for use by a shader.
12455   */
12456  constructor(resources) {
12457    /** The resources that are bound together for use by a shader. */
12458    this.resources = /* @__PURE__ */ Object.create(null);
12459    this._dirty = true;
12460    let index = 0;
12461    for (const i in resources) {
12462      const resource = resources[i];
12463      this.setResource(resource, index++);
12464    }
12465    this._updateKey();
12466  }
12467  /**
12468   * Updates the key if its flagged as dirty. This is used internally to
12469   * match this bind group to a WebGPU BindGroup.
12470   * @internal
12471   */
12472  _updateKey() {
12473    if (!this._dirty)
12474      return;
12475    this._dirty = false;
12476    const keyParts = [];
12477    let index = 0;
12478    for (const i in this.resources) {
12479      keyParts[index++] = this.resources[i]._resourceId;
12480    }
12481    this._key = keyParts.join("|");
12482  }
12483  /**
12484   * Set a resource at a given index. this function will
12485   * ensure that listeners will be removed from the current resource
12486   * and added to the new resource.
12487   * @param resource - The resource to set.
12488   * @param index - The index to set the resource at.
12489   */
12490  setResource(resource, index) {
12491    const currentResource = this.resources[index];
12492    if (resource === currentResource)
12493      return;
12494    if (currentResource) {
12495      resource.off?.("change", this.onResourceChange, this);
12496    }
12497    resource.on?.("change", this.onResourceChange, this);
12498    this.resources[index] = resource;
12499    this._dirty = true;
12500  }
12501  /**
12502   * Returns the resource at the current specified index.
12503   * @param index - The index of the resource to get.
12504   * @returns - The resource at the specified index.
12505   */
12506  getResource(index) {
12507    return this.resources[index];
12508  }
12509  /**
12510   * Used internally to 'touch' each resource, to ensure that the GC
12511   * knows that all resources in this bind group are still being used.
12512   * @param now - The current time in milliseconds.
12513   * @param tick - The current tick.
12514   * @internal
12515   */
12516  _touch(now, tick) {
12517    const resources = this.resources;
12518    for (const i in resources) {
12519      resources[i]._gcLastUsed = now;
12520      resources[i]._touched = tick;
12521    }
12522  }
12523  /** Destroys this bind group and removes all listeners. */
12524  destroy() {
12525    const resources = this.resources;
12526    for (const i in resources) {
12527      const resource = resources[i];
12528      resource?.off?.("change", this.onResourceChange, this);
12529    }
12530    this.resources = null;
12531  }
12532  onResourceChange(resource) {
12533    this._dirty = true;
12534    if (resource.destroyed) {
12535      const resources = this.resources;
12536      for (const i in resources) {
12537        if (resources[i] === resource) {
12538          resources[i] = null;
12539        }
12540      }
12541    } else {
12542      this._updateKey();
12543    }
12544  }
12545}
12546
12547var RendererType = /* @__PURE__ */ ((RendererType2) => {
12548  RendererType2[RendererType2["WEBGL"] = 1] = "WEBGL";
12549  RendererType2[RendererType2["WEBGPU"] = 2] = "WEBGPU";
12550  RendererType2[RendererType2["BOTH"] = 3] = "BOTH";
12551  return RendererType2;
12552})(RendererType || {});
12553
12554class Shader extends EventEmitter {
12555  constructor(options) {
12556    super();
12557    /** A unique identifier for the shader */
12558    this.uid = uid("shader");
12559    /**
12560     * A record of the uniform groups and resources used by the shader.
12561     * This is used by WebGL renderer to sync uniform data.
12562     * @internal
12563     */
12564    this._uniformBindMap = /* @__PURE__ */ Object.create(null);
12565    this._ownedBindGroups = [];
12566    /** @internal */
12567    this._destroyed = false;
12568    let {
12569      gpuProgram,
12570      glProgram,
12571      groups,
12572      resources,
12573      compatibleRenderers,
12574      groupMap
12575    } = options;
12576    this.gpuProgram = gpuProgram;
12577    this.glProgram = glProgram;
12578    if (compatibleRenderers === void 0) {
12579      compatibleRenderers = 0;
12580      if (gpuProgram)
12581        compatibleRenderers |= RendererType.WEBGPU;
12582      if (glProgram)
12583        compatibleRenderers |= RendererType.WEBGL;
12584    }
12585    this.compatibleRenderers = compatibleRenderers;
12586    const nameHash = {};
12587    if (!resources && !groups) {
12588      resources = {};
12589    }
12590    if (resources && groups) {
12591      throw new Error("[Shader] Cannot have both resources and groups");
12592    } else if (!gpuProgram && groups && !groupMap) {
12593      throw new Error("[Shader] No group map or WebGPU shader provided - consider using resources instead.");
12594    } else if (!gpuProgram && groups && groupMap) {
12595      for (const i in groupMap) {
12596        for (const j in groupMap[i]) {
12597          const uniformName = groupMap[i][j];
12598          nameHash[uniformName] = {
12599            group: i,
12600            binding: j,
12601            name: uniformName
12602          };
12603        }
12604      }
12605    } else if (gpuProgram && groups && !groupMap) {
12606      const groupData = gpuProgram.structsAndGroups.groups;
12607      groupMap = {};
12608      groupData.forEach((data) => {
12609        groupMap[data.group] = groupMap[data.group] || {};
12610        groupMap[data.group][data.binding] = data.name;
12611        nameHash[data.name] = data;
12612      });
12613    } else if (resources) {
12614      groups = {};
12615      groupMap = {};
12616      if (gpuProgram) {
12617        const groupData = gpuProgram.structsAndGroups.groups;
12618        groupData.forEach((data) => {
12619          groupMap[data.group] = groupMap[data.group] || {};
12620          groupMap[data.group][data.binding] = data.name;
12621          nameHash[data.name] = data;
12622        });
12623      }
12624      let bindTick = 0;
12625      for (const i in resources) {
12626        if (nameHash[i])
12627          continue;
12628        if (!groups[99]) {
12629          groups[99] = new BindGroup();
12630          this._ownedBindGroups.push(groups[99]);
12631        }
12632        nameHash[i] = { group: 99, binding: bindTick, name: i };
12633        groupMap[99] = groupMap[99] || {};
12634        groupMap[99][bindTick] = i;
12635        bindTick++;
12636      }
12637      for (const i in resources) {
12638        const name = i;
12639        let value = resources[i];
12640        if (!value.source && !value._resourceType) {
12641          value = new UniformGroup(value);
12642        }
12643        const data = nameHash[name];
12644        if (data) {
12645          if (!groups[data.group]) {
12646            groups[data.group] = new BindGroup();
12647            this._ownedBindGroups.push(groups[data.group]);
12648          }
12649          groups[data.group].setResource(value, data.binding);
12650        }
12651      }
12652    }
12653    this.groups = groups;
12654    this._uniformBindMap = groupMap;
12655    this.resources = this._buildResourceAccessor(groups, nameHash);
12656  }
12657  /**
12658   * Sometimes a resource group will be provided later (for example global uniforms)
12659   * In such cases, this method can be used to let the shader know about the group.
12660   * @param name - the name of the resource group
12661   * @param groupIndex - the index of the group (should match the webGPU shader group location)
12662   * @param bindIndex - the index of the bind point (should match the webGPU shader bind point)
12663   */
12664  addResource(name, groupIndex, bindIndex) {
12665    var _a, _b;
12666    (_a = this._uniformBindMap)[groupIndex] || (_a[groupIndex] = {});
12667    (_b = this._uniformBindMap[groupIndex])[bindIndex] || (_b[bindIndex] = name);
12668    if (!this.groups[groupIndex]) {
12669      this.groups[groupIndex] = new BindGroup();
12670      this._ownedBindGroups.push(this.groups[groupIndex]);
12671    }
12672  }
12673  _buildResourceAccessor(groups, nameHash) {
12674    const uniformsOut = {};
12675    for (const i in nameHash) {
12676      const data = nameHash[i];
12677      Object.defineProperty(uniformsOut, data.name, {
12678        get() {
12679          return groups[data.group].getResource(data.binding);
12680        },
12681        set(value) {
12682          groups[data.group].setResource(value, data.binding);
12683        }
12684      });
12685    }
12686    return uniformsOut;
12687  }
12688  /**
12689   * Use to destroy the shader when its not longer needed.
12690   * It will destroy the resources and remove listeners.
12691   * @param destroyPrograms - if the programs should be destroyed as well.
12692   * Make sure its not being used by other shaders!
12693   */
12694  destroy(destroyPrograms = false) {
12695    if (this._destroyed)
12696      return;
12697    this._destroyed = true;
12698    this.emit("destroy", this);
12699    if (destroyPrograms) {
12700      this.gpuProgram?.destroy();
12701      this.glProgram?.destroy();
12702    }
12703    this.gpuProgram = null;
12704    this.glProgram = null;
12705    this.removeAllListeners();
12706    this._uniformBindMap = null;
12707    this._ownedBindGroups.forEach((bindGroup) => {
12708      bindGroup.destroy();
12709    });
12710    this._ownedBindGroups = null;
12711    this.resources = null;
12712    this.groups = null;
12713  }
12714  static from(options) {
12715    const { gpu, gl, ...rest } = options;
12716    let gpuProgram;
12717    let glProgram;
12718    if (gpu) {
12719      gpuProgram = GpuProgram.from(gpu);
12720    }
12721    if (gl) {
12722      glProgram = GlProgram.from(gl);
12723    }
12724    return new Shader({
12725      gpuProgram,
12726      glProgram,
12727      ...rest
12728    });
12729  }
12730}
12731
12732const blendModeIds = {
12733  normal: 0,
12734  add: 1,
12735  multiply: 2,
12736  screen: 3,
12737  overlay: 4,
12738  erase: 5,
12739  "normal-npm": 6,
12740  "add-npm": 7,
12741  "screen-npm": 8,
12742  min: 9,
12743  max: 10
12744};
12745const BLEND = 0;
12746const OFFSET = 1;
12747const CULLING = 2;
12748const DEPTH_TEST = 3;
12749const WINDING = 4;
12750const DEPTH_MASK = 5;
12751const _State = class _State {
12752  constructor() {
12753    this.data = 0;
12754    this.blendMode = "normal";
12755    this.polygonOffset = 0;
12756    this.blend = true;
12757    this.depthMask = true;
12758  }
12759  /**
12760   * Activates blending of the computed fragment color values.
12761   * @default true
12762   */
12763  get blend() {
12764    return !!(this.data & 1 << BLEND);
12765  }
12766  set blend(value) {
12767    if (!!(this.data & 1 << BLEND) !== value) {
12768      this.data ^= 1 << BLEND;
12769    }
12770  }
12771  /**
12772   * Activates adding an offset to depth values of polygon's fragments
12773   * @default false
12774   */
12775  get offsets() {
12776    return !!(this.data & 1 << OFFSET);
12777  }
12778  set offsets(value) {
12779    if (!!(this.data & 1 << OFFSET) !== value) {
12780      this.data ^= 1 << OFFSET;
12781    }
12782  }
12783  /** The culling settings for this state none - No culling back - Back face culling front - Front face culling */
12784  set cullMode(value) {
12785    if (value === "none") {
12786      this.culling = false;
12787      return;
12788    }
12789    this.culling = true;
12790    this.clockwiseFrontFace = value === "front";
12791  }
12792  get cullMode() {
12793    if (!this.culling) {
12794      return "none";
12795    }
12796    return this.clockwiseFrontFace ? "front" : "back";
12797  }
12798  /**
12799   * Activates culling of polygons.
12800   * @default false
12801   */
12802  get culling() {
12803    return !!(this.data & 1 << CULLING);
12804  }
12805  set culling(value) {
12806    if (!!(this.data & 1 << CULLING) !== value) {
12807      this.data ^= 1 << CULLING;
12808    }
12809  }
12810  /**
12811   * Activates depth comparisons and updates to the depth buffer.
12812   * @default false
12813   */
12814  get depthTest() {
12815    return !!(this.data & 1 << DEPTH_TEST);
12816  }
12817  set depthTest(value) {
12818    if (!!(this.data & 1 << DEPTH_TEST) !== value) {
12819      this.data ^= 1 << DEPTH_TEST;
12820    }
12821  }
12822  /**
12823   * Enables or disables writing to the depth buffer.
12824   * @default true
12825   */
12826  get depthMask() {
12827    return !!(this.data & 1 << DEPTH_MASK);
12828  }
12829  set depthMask(value) {
12830    if (!!(this.data & 1 << DEPTH_MASK) !== value) {
12831      this.data ^= 1 << DEPTH_MASK;
12832    }
12833  }
12834  /**
12835   * Specifies whether or not front or back-facing polygons can be culled.
12836   * @default false
12837   */
12838  get clockwiseFrontFace() {
12839    return !!(this.data & 1 << WINDING);
12840  }
12841  set clockwiseFrontFace(value) {
12842    if (!!(this.data & 1 << WINDING) !== value) {
12843      this.data ^= 1 << WINDING;
12844    }
12845  }
12846  /**
12847   * The blend mode to be applied when this state is set. Apply a value of `normal` to reset the blend mode.
12848   * Setting this mode to anything other than NO_BLEND will automatically switch blending on.
12849   * @default 'normal'
12850   */
12851  get blendMode() {
12852    return this._blendMode;
12853  }
12854  set blendMode(value) {
12855    this.blend = value !== "none";
12856    this._blendMode = value;
12857    this._blendModeId = blendModeIds[value] || 0;
12858  }
12859  /**
12860   * The polygon offset. Setting this property to anything other than 0 will automatically enable polygon offset fill.
12861   * @default 0
12862   */
12863  get polygonOffset() {
12864    return this._polygonOffset;
12865  }
12866  set polygonOffset(value) {
12867    this.offsets = !!value;
12868    this._polygonOffset = value;
12869  }
12870  toString() {
12871    return `[pixi.js/core:State blendMode=${this.blendMode} clockwiseFrontFace=${this.clockwiseFrontFace} culling=${this.culling} depthMask=${this.depthMask} polygonOffset=${this.polygonOffset}]`;
12872  }
12873  /**
12874   * A quickly getting an instance of a State that is configured for 2d rendering.
12875   * @returns a new State with values set for 2d rendering
12876   */
12877  static for2d() {
12878    const state = new _State();
12879    state.depthTest = false;
12880    state.blend = true;
12881    return state;
12882  }
12883};
12884_State.default2d = _State.for2d();
12885let State = _State;
12886
12887const _Filter = class _Filter extends Shader {
12888  /**
12889   * @param options - The optional parameters of this filter.
12890   */
12891  constructor(options) {
12892    options = { ..._Filter.defaultOptions, ...options };
12893    super(options);
12894    /** If enabled is true the filter is applied, if false it will not. */
12895    this.enabled = true;
12896    /**
12897     * The gpu state the filter requires to render.
12898     * @internal
12899     */
12900    this._state = State.for2d();
12901    this.blendMode = options.blendMode;
12902    this.padding = options.padding;
12903    if (typeof options.antialias === "boolean") {
12904      this.antialias = options.antialias ? "on" : "off";
12905    } else {
12906      this.antialias = options.antialias;
12907    }
12908    this.resolution = options.resolution;
12909    this.blendRequired = options.blendRequired;
12910    this.clipToViewport = options.clipToViewport;
12911    this.addResource("uTexture", 0, 1);
12912    if (options.blendRequired) {
12913      this.addResource("uBackTexture", 0, 3);
12914    }
12915  }
12916  /**
12917   * Applies the filter
12918   * @param filterManager - The renderer to retrieve the filter from
12919   * @param input - The input render target.
12920   * @param output - The target to output to.
12921   * @param clearMode - Should the output be cleared before rendering to it
12922   */
12923  apply(filterManager, input, output, clearMode) {
12924    filterManager.applyFilter(this, input, output, clearMode);
12925  }
12926  /**
12927   * Get the blend mode of the filter.
12928   * @default "normal"
12929   */
12930  get blendMode() {
12931    return this._state.blendMode;
12932  }
12933  /** Sets the blend mode of the filter. */
12934  set blendMode(value) {
12935    this._state.blendMode = value;
12936  }
12937  /**
12938   * A short hand function to create a filter based of a vertex and fragment shader src.
12939   * @param options
12940   * @returns A shiny new PixiJS filter!
12941   */
12942  static from(options) {
12943    const { gpu, gl, ...rest } = options;
12944    let gpuProgram;
12945    let glProgram;
12946    if (gpu) {
12947      gpuProgram = GpuProgram.from(gpu);
12948    }
12949    if (gl) {
12950      glProgram = GlProgram.from(gl);
12951    }
12952    return new _Filter({
12953      gpuProgram,
12954      glProgram,
12955      ...rest
12956    });
12957  }
12958};
12959/** The default filter settings */
12960_Filter.defaultOptions = {
12961  blendMode: "normal",
12962  resolution: 1,
12963  padding: 0,
12964  antialias: "off",
12965  blendRequired: false,
12966  clipToViewport: true
12967};
12968let Filter = _Filter;
12969
12970const environments = [];
12971extensions.handleByNamedList(ExtensionType.Environment, environments);
12972async function loadEnvironmentExtensions(skip) {
12973  if (skip)
12974    return;
12975  for (let i = 0; i < environments.length; i++) {
12976    const env = environments[i];
12977    if (env.value.test()) {
12978      await env.value.load();
12979      return;
12980    }
12981  }
12982}
vendor: 5,111 bytes, lines 12983-13149
12983
12984let unsafeEval;
12985function unsafeEvalSupported() {
12986  if (typeof unsafeEval === "boolean") {
12987    return unsafeEval;
12988  }
12989  try {
12990    const func = new Function("param1", "param2", "param3", "return param1[param2] === param3;");
12991    unsafeEval = func({ a: "b" }, "a", "b") === true;
12992  } catch (_e) {
12993    unsafeEval = false;
12994  }
12995  return unsafeEval;
12996}
12997
12998function earcut$1(data, holeIndices, dim = 2) {
12999
13000    const hasHoles = holeIndices && holeIndices.length;
13001    const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
13002    let outerNode = linkedList(data, 0, outerLen, dim, true);
13003    const triangles = [];
13004
13005    if (!outerNode || outerNode.next === outerNode.prev) return triangles;
13006
13007    let minX, minY, invSize;
13008
13009    if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
13010
13011    // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
13012    if (data.length > 80 * dim) {
13013        minX = data[0];
13014        minY = data[1];
13015        let maxX = minX;
13016        let maxY = minY;
13017
13018        for (let i = dim; i < outerLen; i += dim) {
13019            const x = data[i];
13020            const y = data[i + 1];
13021            if (x < minX) minX = x;
13022            if (y < minY) minY = y;
13023            if (x > maxX) maxX = x;
13024            if (y > maxY) maxY = y;
13025        }
13026
13027        // minX, minY and invSize are later used to transform coords into integers for z-order calculation
13028        invSize = Math.max(maxX - minX, maxY - minY);
13029        invSize = invSize !== 0 ? 32767 / invSize : 0;
13030    }
13031
13032    earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
13033
13034    return triangles;
13035}
13036
13037// create a circular doubly linked list from polygon points in the specified winding order
13038function linkedList(data, start, end, dim, clockwise) {
13039    let last;
13040
13041    if (clockwise === (signedArea(data, start, end, dim) > 0)) {
13042        for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
13043    } else {
13044        for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
13045    }
13046
13047    if (last && equals(last, last.next)) {
13048        removeNode(last);
13049        last = last.next;
13050    }
13051
13052    return last;
13053}
13054
13055// eliminate colinear or duplicate points
13056function filterPoints(start, end) {
13057    if (!start) return start;
13058    if (!end) end = start;
13059
13060    let p = start,
13061        again;
13062    do {
13063        again = false;
13064
13065        if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
13066            removeNode(p);
13067            p = end = p.prev;
13068            if (p === p.next) break;
13069            again = true;
13070
13071        } else {
13072            p = p.next;
13073        }
13074    } while (again || p !== end);
13075
13076    return end;
13077}
13078
13079// main ear slicing loop which triangulates a polygon (given as a linked list)
13080function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
13081    if (!ear) return;
13082
13083    // interlink polygon nodes in z-order
13084    if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
13085
13086    let stop = ear;
13087
13088    // iterate through ears, slicing them one by one
13089    while (ear.prev !== ear.next) {
13090        const prev = ear.prev;
13091        const next = ear.next;
13092
13093        if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
13094            triangles.push(prev.i, ear.i, next.i); // cut off the triangle
13095
13096            removeNode(ear);
13097
13098            // skipping the next vertex leads to less sliver triangles
13099            ear = next.next;
13100            stop = next.next;
13101
13102            continue;
13103        }
13104
13105        ear = next;
13106
13107        // if we looped through the whole remaining polygon and can't find any more ears
13108        if (ear === stop) {
13109            // try filtering points and slicing again
13110            if (!pass) {
13111                earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
13112
13113            // if this didn't work, try curing all small self-intersections locally
13114            } else if (pass === 1) {
13115                ear = cureLocalIntersections(filterPoints(ear), triangles);
13116                earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
13117
13118            // as a last resort, try splitting the remaining polygon into two
13119            } else if (pass === 2) {
13120                splitEarcut(ear, triangles, dim, minX, minY, invSize);
13121            }
13122
13123            break;
13124        }
13125    }
13126}
13127
13128// check whether a polygon node forms a valid ear with adjacent nodes
13129function isEar(ear) {
13130    const a = ear.prev,
13131        b = ear,
13132        c = ear.next;
13133
13134    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
13135
13136    // now make sure we don't have other points inside the potential ear
13137    const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
13138
13139    // triangle bbox
13140    const x0 = Math.min(ax, bx, cx),
13141        y0 = Math.min(ay, by, cy),
13142        x1 = Math.max(ax, bx, cx),
13143        y1 = Math.max(ay, by, cy);
13144
13145    let p = c.next;
13146    while (p !== a) {
13147        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
13148            pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
13149            area(p.prev, p, p.next) >= 0) return false;
vendor: 14,700 bytes, lines 13150-13629
13150        p = p.next;
13151    }
13152
13153    return true;
13154}
13155
13156function isEarHashed(ear, minX, minY, invSize) {
13157    const a = ear.prev,
13158        b = ear,
13159        c = ear.next;
13160
13161    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
13162
13163    const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
13164
13165    // triangle bbox
13166    const x0 = Math.min(ax, bx, cx),
13167        y0 = Math.min(ay, by, cy),
13168        x1 = Math.max(ax, bx, cx),
13169        y1 = Math.max(ay, by, cy);
13170
13171    // z-order range for the current triangle bbox;
13172    const minZ = zOrder(x0, y0, minX, minY, invSize),
13173        maxZ = zOrder(x1, y1, minX, minY, invSize);
13174
13175    let p = ear.prevZ,
13176        n = ear.nextZ;
13177
13178    // look for points inside the triangle in both directions
13179    while (p && p.z >= minZ && n && n.z <= maxZ) {
13180        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
13181            pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
13182        p = p.prevZ;
13183
13184        if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
13185            pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
13186        n = n.nextZ;
13187    }
13188
13189    // look for remaining points in decreasing z-order
13190    while (p && p.z >= minZ) {
13191        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
13192            pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
13193        p = p.prevZ;
13194    }
13195
13196    // look for remaining points in increasing z-order
13197    while (n && n.z <= maxZ) {
13198        if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
13199            pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
13200        n = n.nextZ;
13201    }
13202
13203    return true;
13204}
13205
13206// go through all polygon nodes and cure small local self-intersections
13207function cureLocalIntersections(start, triangles) {
13208    let p = start;
13209    do {
13210        const a = p.prev,
13211            b = p.next.next;
13212
13213        if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
13214
13215            triangles.push(a.i, p.i, b.i);
13216
13217            // remove two nodes involved
13218            removeNode(p);
13219            removeNode(p.next);
13220
13221            p = start = b;
13222        }
13223        p = p.next;
13224    } while (p !== start);
13225
13226    return filterPoints(p);
13227}
13228
13229// try splitting polygon into two and triangulate them independently
13230function splitEarcut(start, triangles, dim, minX, minY, invSize) {
13231    // look for a valid diagonal that divides the polygon into two
13232    let a = start;
13233    do {
13234        let b = a.next.next;
13235        while (b !== a.prev) {
13236            if (a.i !== b.i && isValidDiagonal(a, b)) {
13237                // split the polygon in two by the diagonal
13238                let c = splitPolygon(a, b);
13239
13240                // filter colinear points around the cuts
13241                a = filterPoints(a, a.next);
13242                c = filterPoints(c, c.next);
13243
13244                // run earcut on each half
13245                earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
13246                earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
13247                return;
13248            }
13249            b = b.next;
13250        }
13251        a = a.next;
13252    } while (a !== start);
13253}
13254
13255// link every hole into the outer loop, producing a single-ring polygon without holes
13256function eliminateHoles(data, holeIndices, outerNode, dim) {
13257    const queue = [];
13258
13259    for (let i = 0, len = holeIndices.length; i < len; i++) {
13260        const start = holeIndices[i] * dim;
13261        const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
13262        const list = linkedList(data, start, end, dim, false);
13263        if (list === list.next) list.steiner = true;
13264        queue.push(getLeftmost(list));
13265    }
13266
13267    queue.sort(compareXYSlope);
13268
13269    // process holes from left to right
13270    for (let i = 0; i < queue.length; i++) {
13271        outerNode = eliminateHole(queue[i], outerNode);
13272    }
13273
13274    return outerNode;
13275}
13276
13277function compareXYSlope(a, b) {
13278    let result = a.x - b.x;
13279    // when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find
13280    // the bridge to the outer shell is always the point that they meet at.
13281    if (result === 0) {
13282        result = a.y - b.y;
13283        if (result === 0) {
13284            const aSlope = (a.next.y - a.y) / (a.next.x - a.x);
13285            const bSlope = (b.next.y - b.y) / (b.next.x - b.x);
13286            result = aSlope - bSlope;
13287        }
13288    }
13289    return result;
13290}
13291
13292// find a bridge between vertices that connects hole with an outer ring and link it
13293function eliminateHole(hole, outerNode) {
13294    const bridge = findHoleBridge(hole, outerNode);
13295    if (!bridge) {
13296        return outerNode;
13297    }
13298
13299    const bridgeReverse = splitPolygon(bridge, hole);
13300
13301    // filter collinear points around the cuts
13302    filterPoints(bridgeReverse, bridgeReverse.next);
13303    return filterPoints(bridge, bridge.next);
13304}
13305
13306// David Eberly's algorithm for finding a bridge between hole and outer polygon
13307function findHoleBridge(hole, outerNode) {
13308    let p = outerNode;
13309    const hx = hole.x;
13310    const hy = hole.y;
13311    let qx = -Infinity;
13312    let m;
13313
13314    // find a segment intersected by a ray from the hole's leftmost point to the left;
13315    // segment's endpoint with lesser x will be potential connection point
13316    // unless they intersect at a vertex, then choose the vertex
13317    if (equals(hole, p)) return p;
13318    do {
13319        if (equals(hole, p.next)) return p.next;
13320        else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
13321            const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
13322            if (x <= hx && x > qx) {
13323                qx = x;
13324                m = p.x < p.next.x ? p : p.next;
13325                if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint
13326            }
13327        }
13328        p = p.next;
13329    } while (p !== outerNode);
13330
13331    if (!m) return null;
13332
13333    // look for points inside the triangle of hole point, segment intersection and endpoint;
13334    // if there are no points found, we have a valid connection;
13335    // otherwise choose the point of the minimum angle with the ray as connection point
13336
13337    const stop = m;
13338    const mx = m.x;
13339    const my = m.y;
13340    let tanMin = Infinity;
13341
13342    p = m;
13343
13344    do {
13345        if (hx >= p.x && p.x >= mx && hx !== p.x &&
13346                pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
13347
13348            const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
13349
13350            if (locallyInside(p, hole) &&
13351                (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
13352                m = p;
13353                tanMin = tan;
13354            }
13355        }
13356
13357        p = p.next;
13358    } while (p !== stop);
13359
13360    return m;
13361}
13362
13363// whether sector in vertex m contains sector in vertex p in the same coordinates
13364function sectorContainsSector(m, p) {
13365    return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
13366}
13367
13368// interlink polygon nodes in z-order
13369function indexCurve(start, minX, minY, invSize) {
13370    let p = start;
13371    do {
13372        if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
13373        p.prevZ = p.prev;
13374        p.nextZ = p.next;
13375        p = p.next;
13376    } while (p !== start);
13377
13378    p.prevZ.nextZ = null;
13379    p.prevZ = null;
13380
13381    sortLinked(p);
13382}
13383
13384// Simon Tatham's linked list merge sort algorithm
13385// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
13386function sortLinked(list) {
13387    let numMerges;
13388    let inSize = 1;
13389
13390    do {
13391        let p = list;
13392        let e;
13393        list = null;
13394        let tail = null;
13395        numMerges = 0;
13396
13397        while (p) {
13398            numMerges++;
13399            let q = p;
13400            let pSize = 0;
13401            for (let i = 0; i < inSize; i++) {
13402                pSize++;
13403                q = q.nextZ;
13404                if (!q) break;
13405            }
13406            let qSize = inSize;
13407
13408            while (pSize > 0 || (qSize > 0 && q)) {
13409
13410                if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
13411                    e = p;
13412                    p = p.nextZ;
13413                    pSize--;
13414                } else {
13415                    e = q;
13416                    q = q.nextZ;
13417                    qSize--;
13418                }
13419
13420                if (tail) tail.nextZ = e;
13421                else list = e;
13422
13423                e.prevZ = tail;
13424                tail = e;
13425            }
13426
13427            p = q;
13428        }
13429
13430        tail.nextZ = null;
13431        inSize *= 2;
13432
13433    } while (numMerges > 1);
13434
13435    return list;
13436}
13437
13438// z-order of a point given coords and inverse of the longer side of data bbox
13439function zOrder(x, y, minX, minY, invSize) {
13440    // coords are transformed into non-negative 15-bit integer range
13441    x = (x - minX) * invSize | 0;
13442    y = (y - minY) * invSize | 0;
13443
13444    x = (x | (x << 8)) & 0x00FF00FF;
13445    x = (x | (x << 4)) & 0x0F0F0F0F;
13446    x = (x | (x << 2)) & 0x33333333;
13447    x = (x | (x << 1)) & 0x55555555;
13448
13449    y = (y | (y << 8)) & 0x00FF00FF;
13450    y = (y | (y << 4)) & 0x0F0F0F0F;
13451    y = (y | (y << 2)) & 0x33333333;
13452    y = (y | (y << 1)) & 0x55555555;
13453
13454    return x | (y << 1);
13455}
13456
13457// find the leftmost node of a polygon ring
13458function getLeftmost(start) {
13459    let p = start,
13460        leftmost = start;
13461    do {
13462        if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;
13463        p = p.next;
13464    } while (p !== start);
13465
13466    return leftmost;
13467}
13468
13469// check if a point lies within a convex triangle
13470function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
13471    return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
13472           (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
13473           (bx - px) * (cy - py) >= (cx - px) * (by - py);
13474}
13475
13476// check if a point lies within a convex triangle but false if its equal to the first point of the triangle
13477function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) {
13478    return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py);
13479}
13480
13481// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
13482function isValidDiagonal(a, b) {
13483    return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges
13484           (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
13485            (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
13486            equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
13487}
13488
13489// signed area of a triangle
13490function area(p, q, r) {
13491    return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
13492}
13493
13494// check if two points are equal
13495function equals(p1, p2) {
13496    return p1.x === p2.x && p1.y === p2.y;
13497}
13498
13499// check if two segments intersect
13500function intersects(p1, q1, p2, q2) {
13501    const o1 = sign(area(p1, q1, p2));
13502    const o2 = sign(area(p1, q1, q2));
13503    const o3 = sign(area(p2, q2, p1));
13504    const o4 = sign(area(p2, q2, q1));
13505
13506    if (o1 !== o2 && o3 !== o4) return true; // general case
13507
13508    if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
13509    if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
13510    if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
13511    if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
13512
13513    return false;
13514}
13515
13516// for collinear points p, q, r, check if point q lies on segment pr
13517function onSegment(p, q, r) {
13518    return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
13519}
13520
13521function sign(num) {
13522    return num > 0 ? 1 : num < 0 ? -1 : 0;
13523}
13524
13525// check if a polygon diagonal intersects any polygon segments
13526function intersectsPolygon(a, b) {
13527    let p = a;
13528    do {
13529        if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
13530                intersects(p, p.next, a, b)) return true;
13531        p = p.next;
13532    } while (p !== a);
13533
13534    return false;
13535}
13536
13537// check if a polygon diagonal is locally inside the polygon
13538function locallyInside(a, b) {
13539    return area(a.prev, a, a.next) < 0 ?
13540        area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
13541        area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
13542}
13543
13544// check if the middle point of a polygon diagonal is inside the polygon
13545function middleInside(a, b) {
13546    let p = a;
13547    let inside = false;
13548    const px = (a.x + b.x) / 2;
13549    const py = (a.y + b.y) / 2;
13550    do {
13551        if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
13552                (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
13553            inside = !inside;
13554        p = p.next;
13555    } while (p !== a);
13556
13557    return inside;
13558}
13559
13560// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
13561// if one belongs to the outer ring and another to a hole, it merges it into a single ring
13562function splitPolygon(a, b) {
13563    const a2 = createNode(a.i, a.x, a.y),
13564        b2 = createNode(b.i, b.x, b.y),
13565        an = a.next,
13566        bp = b.prev;
13567
13568    a.next = b;
13569    b.prev = a;
13570
13571    a2.next = an;
13572    an.prev = a2;
13573
13574    b2.next = a2;
13575    a2.prev = b2;
13576
13577    bp.next = b2;
13578    b2.prev = bp;
13579
13580    return b2;
13581}
13582
13583// create a node and optionally link it with previous one (in a circular doubly linked list)
13584function insertNode(i, x, y, last) {
13585    const p = createNode(i, x, y);
13586
13587    if (!last) {
13588        p.prev = p;
13589        p.next = p;
13590
13591    } else {
13592        p.next = last.next;
13593        p.prev = last;
13594        last.next.prev = p;
13595        last.next = p;
13596    }
13597    return p;
13598}
13599
13600function removeNode(p) {
13601    p.next.prev = p.prev;
13602    p.prev.next = p.next;
13603
13604    if (p.prevZ) p.prevZ.nextZ = p.nextZ;
13605    if (p.nextZ) p.nextZ.prevZ = p.prevZ;
13606}
13607
13608function createNode(i, x, y) {
13609    return {
13610        i, // vertex index in coordinates array
13611        x, y, // vertex coordinates
13612        prev: null, // previous and next vertex nodes in a polygon ring
13613        next: null,
13614        z: 0, // z-order curve value
13615        prevZ: null, // previous and next nodes in z-order
13616        nextZ: null,
13617        steiner: false // indicates whether this is a steiner point
13618    };
13619}
13620
13621function signedArea(data, start, end, dim) {
13622    let sum = 0;
13623    for (let i = start, j = end - dim; i < end; i += dim) {
13624        sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
13625        j = i;
13626    }
13627    return sum;
13628}
13629
13630const earcut = earcut$1.default || earcut$1;
13631
13632var CLEAR = /* @__PURE__ */ ((CLEAR2) => {
13633  CLEAR2[CLEAR2["NONE"] = 0] = "NONE";
13634  CLEAR2[CLEAR2["COLOR"] = 16384] = "COLOR";
13635  CLEAR2[CLEAR2["STENCIL"] = 1024] = "STENCIL";
13636  CLEAR2[CLEAR2["DEPTH"] = 256] = "DEPTH";
13637  CLEAR2[CLEAR2["COLOR_DEPTH"] = 16640] = "COLOR_DEPTH";
13638  CLEAR2[CLEAR2["COLOR_STENCIL"] = 17408] = "COLOR_STENCIL";
13639  CLEAR2[CLEAR2["DEPTH_STENCIL"] = 1280] = "DEPTH_STENCIL";
13640  CLEAR2[CLEAR2["ALL"] = 17664] = "ALL";
13641  return CLEAR2;
13642})(CLEAR || {});
13643
13644class SystemRunner {
13645  /**
13646   * @param name - The function name that will be executed on the listeners added to this Runner.
13647   */
13648  constructor(name) {
13649    this.items = [];
13650    this._name = name;
13651  }
13652  /* jsdoc/check-param-names */
13653  /**
13654   * Dispatch/Broadcast Runner to all listeners added to the queue.
13655   * @param {...any} params - (optional) parameters to pass to each listener
13656   */
13657  /* jsdoc/check-param-names */
13658  emit(a0, a1, a2, a3, a4, a5, a6, a7) {
13659    const { name, items } = this;
13660    for (let i = 0, len = items.length; i < len; i++) {
13661      items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
13662    }
13663    return this;
13664  }
13665  /**
13666   * Add a listener to the Runner
13667   *
13668   * Runners do not need to have scope or functions passed to them.
13669   * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
13670   * as the name provided to the Runner when it was created.
13671   *
13672   * Eg A listener passed to this Runner will require a 'complete' function.
13673   *
13674   * ```ts
13675   * import { Runner } from 'pixi.js';
13676   *
13677   * const complete = new Runner('complete');
13678   * ```
13679   *
13680   * The scope used will be the object itself.
13681   * @param {any} item - The object that will be listening.
13682   */
13683  add(item) {
13684    if (item[this._name]) {
13685      this.remove(item);
13686      this.items.push(item);
13687    }
13688    return this;
13689  }
13690  /**
13691   * Remove a single listener from the dispatch queue.
13692   * @param {any} item - The listener that you would like to remove.
13693   */
13694  remove(item) {
13695    const index = this.items.indexOf(item);
13696    if (index !== -1) {
13697      this.items.splice(index, 1);
13698    }
13699    return this;
13700  }
13701  /**
13702   * Check to see if the listener is already in the Runner
13703   * @param {any} item - The listener that you would like to check.
13704   */
13705  contains(item) {
13706    return this.items.indexOf(item) !== -1;
13707  }
13708  /** Remove all listeners from the Runner */
13709  removeAll() {
13710    this.items.length = 0;
13711    return this;
13712  }
13713  /** Remove all references, don't use after this. */
13714  destroy() {
13715    this.removeAll();
13716    this.items = null;
13717    this._name = null;
13718  }
13719  /**
13720   * `true` if there are no this Runner contains no listeners
13721   * @readonly
13722   */
13723  get empty() {
13724    return this.items.length === 0;
13725  }
13726  /**
13727   * The name of the runner.
13728   * @readonly
13729   */
13730  get name() {
13731    return this._name;
13732  }
13733}
13734
13735const defaultRunners = [
13736  "init",
13737  "destroy",
13738  "contextChange",
13739  "resolutionChange",
13740  "resetState",
13741  "renderEnd",
13742  "renderStart",
13743  "render",
13744  "update",
13745  "postrender",
13746  "prerender"
13747];
13748const _AbstractRenderer = class _AbstractRenderer extends EventEmitter {
13749  /**
13750   * Set up a system with a collection of SystemClasses and runners.
13751   * Systems are attached dynamically to this class when added.
13752   * @param config - the config for the system manager
13753   */
13754  constructor(config) {
13755    super();
13756    /** The current tick of the renderer. */
13757    this.tick = 0;
13758    /** @internal */
13759    this.uid = uid("renderer");
13760    /** @internal */
13761    this.runners = /* @__PURE__ */ Object.create(null);
13762    /** @internal */
13763    this.renderPipes = /* @__PURE__ */ Object.create(null);
13764    this._initOptions = {};
13765    this._systemsHash = /* @__PURE__ */ Object.create(null);
13766    this.type = config.type;
13767    this.name = config.name;
13768    this.config = config;
13769    const combinedRunners = [...defaultRunners, ...this.config.runners ?? []];
13770    this._addRunners(...combinedRunners);
13771    this._unsafeEvalCheck();
13772  }
13773  /**
13774   * Initialize the renderer.
13775   * @param options - The options to use to create the renderer.
13776   */
13777  async init(options = {}) {
13778    const skip = options.skipExtensionImports === true ? true : options.manageImports === false;
13779    await loadEnvironmentExtensions(skip);
13780    this._addSystems(this.config.systems);
13781    this._addPipes(this.config.renderPipes, this.config.renderPipeAdaptors);
13782    for (const systemName in this._systemsHash) {
13783      const system = this._systemsHash[systemName];
13784      const defaultSystemOptions = system.constructor.defaultOptions;
13785      options = { ...defaultSystemOptions, ...options };
13786    }
13787    options = { ..._AbstractRenderer.defaultOptions, ...options };
13788    this._roundPixels = options.roundPixels ? 1 : 0;
13789    for (let i = 0; i < this.runners.init.items.length; i++) {
13790      await this.runners.init.items[i].init(options);
13791    }
13792    this._initOptions = options;
13793  }
13794  render(args, deprecated) {
13795    this.tick++;
13796    let options = args;
13797    if (options instanceof Container) {
13798      options = { container: options };
13799      if (deprecated) {
13800        deprecation(v8_0_0, "passing a second argument is deprecated, please use render options instead");
13801        options.target = deprecated.renderTexture;
13802      }
13803    }
13804    options.target || (options.target = this.view.renderTarget);
13805    if (options.target === this.view.renderTarget) {
13806      this._lastObjectRendered = options.container;
13807      options.clearColor ?? (options.clearColor = this.background.colorRgba);
13808      options.clear ?? (options.clear = this.background.clearBeforeRender);
13809    }
13810    if (options.clearColor) {
13811      const isRGBAArray = Array.isArray(options.clearColor) && options.clearColor.length === 4;
13812      options.clearColor = isRGBAArray ? options.clearColor : Color.shared.setValue(options.clearColor).toArray();
13813    }
13814    if (!options.transform) {
13815      options.container.updateLocalTransform();
13816      options.transform = options.container.localTransform;
13817    }
13818    if (!options.container.visible) {
13819      return;
13820    }
13821    options.container.enableRenderGroup();
13822    this.runners.prerender.emit(options);
13823    this.runners.renderStart.emit(options);
13824    this.runners.render.emit(options);
13825    this.runners.renderEnd.emit(options);
13826    this.runners.postrender.emit(options);
13827  }
13828  /**
13829   * Resizes the WebGL view to the specified width and height.
13830   * @param desiredScreenWidth - The desired width of the screen.
13831   * @param desiredScreenHeight - The desired height of the screen.
13832   * @param resolution - The resolution / device pixel ratio of the renderer.
13833   */
13834  resize(desiredScreenWidth, desiredScreenHeight, resolution) {
13835    const previousResolution = this.view.resolution;
13836    this.view.resize(desiredScreenWidth, desiredScreenHeight, resolution);
13837    this.emit("resize", this.view.screen.width, this.view.screen.height, this.view.resolution);
13838    if (resolution !== void 0 && resolution !== previousResolution) {
13839      this.runners.resolutionChange.emit(resolution);
13840    }
13841  }
13842  /**
13843   * Clears the render target.
13844   * @param options - The options to use when clearing the render target.
13845   * @param options.target - The render target to clear.
13846   * @param options.clearColor - The color to clear with.
13847   * @param options.clear - The clear mode to use.
13848   * @advanced
13849   */
13850  clear(options = {}) {
13851    const renderer = this;
13852    options.target || (options.target = renderer.renderTarget.renderTarget);
13853    options.clearColor || (options.clearColor = this.background.colorRgba);
13854    options.clear ?? (options.clear = CLEAR.ALL);
13855    const { clear, clearColor, target } = options;
13856    Color.shared.setValue(clearColor ?? this.background.colorRgba);
13857    renderer.renderTarget.clear(target, clear, Color.shared.toArray());
13858  }
13859  /** The resolution / device pixel ratio of the renderer. */
13860  get resolution() {
13861    return this.view.resolution;
13862  }
13863  set resolution(value) {
13864    this.view.resolution = value;
13865    this.runners.resolutionChange.emit(value);
13866  }
13867  /**
13868   * Same as view.width, actual number of pixels in the canvas by horizontal.
13869   * @type {number}
13870   * @readonly
13871   * @default 800
13872   */
13873  get width() {
13874    return this.view.texture.frame.width;
13875  }
13876  /**
13877   * Same as view.height, actual number of pixels in the canvas by vertical.
13878   * @default 600
13879   */
13880  get height() {
13881    return this.view.texture.frame.height;
13882  }
13883  // NOTE: this was `view` in v7
13884  /**
13885   * The canvas element that everything is drawn to.
13886   * @type {environment.ICanvas}
13887   */
13888  get canvas() {
13889    return this.view.canvas;
13890  }
13891  /**
13892   * the last object rendered by the renderer. Useful for other plugins like interaction managers
13893   * @readonly
13894   */
13895  get lastObjectRendered() {
13896    return this._lastObjectRendered;
13897  }
13898  /**
13899   * Flag if we are rendering to the screen vs renderTexture
13900   * @readonly
13901   * @default true
13902   */
13903  get renderingToScreen() {
13904    const renderer = this;
13905    return renderer.renderTarget.renderingToScreen;
13906  }
13907  /**
13908   * Measurements of the screen. (0, 0, screenWidth, screenHeight).
13909   *
13910   * Its safe to use as filterArea or hitArea for the whole stage.
13911   */
13912  get screen() {
13913    return this.view.screen;
13914  }
13915  /**
13916   * Create a bunch of runners based of a collection of ids
13917   * @param runnerIds - the runner ids to add
13918   */
13919  _addRunners(...runnerIds) {
13920    runnerIds.forEach((runnerId) => {
13921      this.runners[runnerId] = new SystemRunner(runnerId);
13922    });
13923  }
13924  _addSystems(systems) {
13925    let i;
13926    for (i in systems) {
13927      const val = systems[i];
13928      this._addSystem(val.value, val.name);
13929    }
13930  }
13931  /**
13932   * Add a new system to the renderer.
13933   * @param ClassRef - Class reference
13934   * @param name - Property name for system, if not specified
13935   *        will use a static `name` property on the class itself. This
13936   *        name will be assigned as s property on the Renderer so make
13937   *        sure it doesn't collide with properties on Renderer.
13938   * @returns Return instance of renderer
13939   */
13940  _addSystem(ClassRef, name) {
13941    const system = new ClassRef(this);
13942    if (this[name]) {
13943      throw new Error(`Whoops! The name "${name}" is already in use`);
13944    }
13945    this[name] = system;
13946    this._systemsHash[name] = system;
13947    for (const i in this.runners) {
13948      this.runners[i].add(system);
13949    }
13950    return this;
13951  }
13952  _addPipes(pipes, pipeAdaptors) {
13953    const adaptors = pipeAdaptors.reduce((acc, adaptor) => {
13954      acc[adaptor.name] = adaptor.value;
13955      return acc;
13956    }, {});
13957    pipes.forEach((pipe) => {
13958      const PipeClass = pipe.value;
13959      const name = pipe.name;
13960      const Adaptor = adaptors[name];
13961      this.renderPipes[name] = new PipeClass(
13962        this,
13963        Adaptor ? new Adaptor() : null
13964      );
13965      this.runners.destroy.add(this.renderPipes[name]);
13966    });
13967  }
13968  destroy(options = false) {
13969    this.runners.destroy.items.reverse();
13970    this.runners.destroy.emit(options);
13971    if (options === true || typeof options === "object" && options.releaseGlobalResources) {
13972      GlobalResourceRegistry.release();
13973    }
13974    Object.values(this.runners).forEach((runner) => {
13975      runner.destroy();
13976    });
13977    this._systemsHash = null;
13978    this.renderPipes = null;
13979  }
13980  /**
13981   * Generate a texture from a container.
13982   * @param options - options or container target to use when generating the texture
13983   * @returns a texture
13984   */
13985  generateTexture(options) {
13986    return this.textureGenerator.generateTexture(options);
13987  }
13988  /**
13989   * Whether the renderer will round coordinates to whole pixels when rendering.
13990   * Can be overridden on a per scene item basis.
13991   */
13992  get roundPixels() {
13993    return !!this._roundPixels;
13994  }
13995  /**
13996   * Overridable function by `pixi.js/unsafe-eval` to silence
13997   * throwing an error if platform doesn't support unsafe-evals.
13998   * @private
13999   * @ignore
14000   */
14001  _unsafeEvalCheck() {
14002    if (!unsafeEvalSupported()) {
14003      throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support.");
14004    }
14005  }
14006  /**
14007   * Resets the rendering state of the renderer.
14008   * This is useful when you want to use the WebGL context directly and need to ensure PixiJS's internal state
14009   * stays synchronized. When modifying the WebGL context state externally, calling this method before the next Pixi
14010   * render will reset all internal caches and ensure it executes correctly.
14011   *
14012   * This is particularly useful when combining PixiJS with other rendering engines like Three.js:
14013   * ```js
14014   * // Reset Three.js state
14015   * threeRenderer.resetState();
14016   *
14017   * // Render a Three.js scene
14018   * threeRenderer.render(threeScene, threeCamera);
14019   *
14020   * // Reset PixiJS state since Three.js modified the WebGL context
14021   * pixiRenderer.resetState();
14022   *
14023   * // Now render Pixi content
14024   * pixiRenderer.render(pixiScene);
14025   * ```
14026   * @advanced
14027   */
14028  resetState() {
14029    this.runners.resetState.emit();
14030  }
14031};
14032/** The default options for the renderer. */
14033_AbstractRenderer.defaultOptions = {
14034  /**
14035   * Default resolution / device pixel ratio of the renderer.
14036   * @default 1
14037   */
14038  resolution: 1,
14039  /**
14040   * Should the `failIfMajorPerformanceCaveat` flag be enabled as a context option used in the `isWebGLSupported`
14041   * function. If set to true, a WebGL renderer can fail to be created if the browser thinks there could be
14042   * performance issues when using WebGL.
14043   *
14044   * In PixiJS v6 this has changed from true to false by default, to allow WebGL to work in as many
14045   * scenarios as possible. However, some users may have a poor experience, for example, if a user has a gpu or
14046   * driver version blacklisted by the
14047   * browser.
14048   *
14049   * If your application requires high performance rendering, you may wish to set this to false.
14050   * We recommend one of two options if you decide to set this flag to false:
14051   *
14052   * 1: Use the Canvas renderer as a fallback in case high performance WebGL is
14053   *    not supported.
14054   *
14055   * 2: Call `isWebGLSupported` (which if found in the utils package) in your code before attempting to create a
14056   *    PixiJS renderer, and show an error message to the user if the function returns false, explaining that their
14057   *    device & browser combination does not support high performance WebGL.
14058   *    This is a much better strategy than trying to create a PixiJS renderer and finding it then fails.
14059   * @default false
14060   */
14061  failIfMajorPerformanceCaveat: false,
14062  /**
14063   * Should round pixels be forced when rendering?
14064   * @default false
14065   */
14066  roundPixels: false
14067};
14068let AbstractRenderer = _AbstractRenderer;
14069
14070let _isWebGLSupported;
14071function isWebGLSupported(failIfMajorPerformanceCaveat) {
14072  if (_isWebGLSupported !== void 0)
14073    return _isWebGLSupported;
14074  _isWebGLSupported = (() => {
14075    const contextOptions = {
14076      stencil: true,
14077      failIfMajorPerformanceCaveat: failIfMajorPerformanceCaveat ?? AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat
14078    };
14079    try {
14080      if (!DOMAdapter.get().getWebGLRenderingContext()) {
14081        return false;
14082      }
14083      const canvas = DOMAdapter.get().createCanvas();
14084      let gl = canvas.getContext("webgl", contextOptions);
14085      const success = !!gl?.getContextAttributes()?.stencil;
14086      if (gl) {
14087        const loseContext = gl.getExtension("WEBGL_lose_context");
14088        if (loseContext) {
14089          loseContext.loseContext();
14090        }
14091      }
14092      gl = null;
14093      return success;
14094    } catch (_e) {
14095      return false;
14096    }
14097  })();
14098  return _isWebGLSupported;
14099}
14100
14101let _isWebGPUSupported;
14102async function isWebGPUSupported(options = {}) {
14103  if (_isWebGPUSupported !== void 0)
14104    return _isWebGPUSupported;
14105  _isWebGPUSupported = await (async () => {
14106    const gpu = DOMAdapter.get().getNavigator().gpu;
14107    if (!gpu) {
14108      return false;
14109    }
14110    try {
14111      const adapter = await gpu.requestAdapter(options);
14112      await adapter.requestDevice();
14113      return true;
14114    } catch (_e) {
14115      return false;
14116    }
14117  })();
14118  return _isWebGPUSupported;
14119}
14120
14121const renderPriority = ["webgl", "webgpu", "canvas"];
14122async function autoDetectRenderer(options) {
14123  let preferredOrder = [];
14124  if (options.preference) {
14125    preferredOrder.push(options.preference);
14126    renderPriority.forEach((item) => {
14127      if (item !== options.preference) {
14128        preferredOrder.push(item);
14129      }
14130    });
14131  } else {
14132    preferredOrder = renderPriority.slice();
14133  }
14134  let RendererClass;
14135  let finalOptions = {};
14136  for (let i = 0; i < preferredOrder.length; i++) {
14137    const rendererType = preferredOrder[i];
14138    if (rendererType === "webgpu" && await isWebGPUSupported()) {
14139      const { WebGPURenderer } = await __vitePreload(async () => { const { WebGPURenderer } = await import('./WebGPURenderer-C0Fiuo6s.js');return { WebGPURenderer }},true              ?__vite__mapDeps([55,2,56,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54]):void 0,import.meta.url);
14140      RendererClass = WebGPURenderer;
14141      finalOptions = { ...options, ...options.webgpu };
14142      break;
14143    } else if (rendererType === "webgl" && isWebGLSupported(
14144      options.failIfMajorPerformanceCaveat ?? AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat
14145    )) {
14146      const { WebGLRenderer } = await __vitePreload(async () => { const { WebGLRenderer } = await import('./WebGLRenderer-x_6nJWeO.js');return { WebGLRenderer }},true              ?__vite__mapDeps([57,2,56,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54]):void 0,import.meta.url);
14147      RendererClass = WebGLRenderer;
14148      finalOptions = { ...options, ...options.webgl };
14149      break;
14150    } else if (rendererType === "canvas") {
14151      finalOptions = { ...options };
14152      throw new Error("CanvasRenderer is not yet implemented");
14153    }
14154  }
14155  delete finalOptions.webgpu;
14156  delete finalOptions.webgl;
14157  if (!RendererClass) {
14158    throw new Error("No available renderer for the current environment");
14159  }
14160  const renderer = new RendererClass();
14161  await renderer.init(finalOptions);
14162  return renderer;
14163}
14164
14165const VERSION = "8.15.0";
14166
14167class ApplicationInitHook {
14168  static init() {
14169    globalThis.__PIXI_APP_INIT__?.(this, VERSION);
14170  }
14171  static destroy() {
14172  }
14173}
14174/** @ignore */
14175ApplicationInitHook.extension = ExtensionType.Application;
14176class RendererInitHook {
14177  constructor(renderer) {
14178    this._renderer = renderer;
14179  }
14180  init() {
14181    globalThis.__PIXI_RENDERER_INIT__?.(this._renderer, VERSION);
14182  }
14183  destroy() {
14184    this._renderer = null;
14185  }
14186}
14187/** @ignore */
14188RendererInitHook.extension = {
14189  type: [
14190    ExtensionType.WebGLSystem,
14191    ExtensionType.WebGPUSystem
14192  ],
14193  name: "initHook",
14194  priority: -10
14195};
14196
14197const _Application = class _Application {
14198  constructor(...args) {
14199    /**
14200     * The root display container for your application.
14201     * All visual elements should be added to this container or its children.
14202     * @example
14203     * ```js
14204     * // Create a sprite and add it to the stage
14205     * const sprite = Sprite.from('image.png');
14206     * app.stage.addChild(sprite);
14207     *
14208     * // Create a container for grouping objects
14209     * const container = new Container();
14210     * app.stage.addChild(container);
14211     * ```
14212     */
14213    this.stage = new Container();
14214    if (args[0] !== void 0) {
14215      deprecation(v8_0_0, "Application constructor options are deprecated, please use Application.init() instead.");
14216    }
14217  }
14218  /**
14219   * Initializes the PixiJS application with the specified options.
14220   *
14221   * This method must be called after creating a new Application instance.
14222   * @param options - Configuration options for the application and renderer
14223   * @returns A promise that resolves when initialization is complete
14224   * @example
14225   * ```js
14226   * const app = new Application();
14227   *
14228   * // Initialize with custom options
14229   * await app.init({
14230   *     width: 800,
14231   *     height: 600,
14232   *     backgroundColor: 0x1099bb,
14233   *     preference: 'webgl', // or 'webgpu'
14234   * });
14235   * ```
14236   */
14237  async init(options) {
14238    options = { ...options };
14239    this.stage || (this.stage = new Container());
14240    this.renderer = await autoDetectRenderer(options);
14241    _Application._plugins.forEach((plugin) => {
14242      plugin.init.call(this, options);
14243    });
14244  }
14245  /**
14246   * Renders the current stage to the screen.
14247   *
14248   * When using the default setup with {@link TickerPlugin} (enabled by default), you typically don't need to call
14249   * this method directly as rendering is handled automatically.
14250   *
14251   * Only use this method if you've disabled the {@link TickerPlugin} or need custom
14252   * render timing control.
14253   * @example
14254   * ```js
14255   * // Example 1: Default setup (TickerPlugin handles rendering)
14256   * const app = new Application();
14257   * await app.init();
14258   * // No need to call render() - TickerPlugin handles it
14259   *
14260   * // Example 2: Custom rendering loop (if TickerPlugin is disabled)
14261   * const app = new Application();
14262   * await app.init({ autoStart: false }); // Disable automatic rendering
14263   *
14264   * function animate() {
14265   *     app.render();
14266   *     requestAnimationFrame(animate);
14267   * }
14268   * animate();
14269   * ```
14270   */
14271  render() {
14272    this.renderer.render({ container: this.stage });
14273  }
14274  /**
14275   * Reference to the renderer's canvas element. This is the HTML element
14276   * that displays your application's graphics.
14277   * @readonly
14278   * @type {HTMLCanvasElement}
14279   * @example
14280   * ```js
14281   * // Create a new application
14282   * const app = new Application();
14283   * // Initialize the application
14284   * await app.init({...});
14285   * // Add canvas to the page
14286   * document.body.appendChild(app.canvas);
14287   *
14288   * // Access the canvas directly
14289   * console.log(app.canvas); // HTMLCanvasElement
14290   * ```
14291   */
14292  get canvas() {
14293    return this.renderer.canvas;
14294  }
14295  /**
14296   * Reference to the renderer's canvas element.
14297   * @type {HTMLCanvasElement}
14298   * @deprecated since 8.0.0
14299   * @see {@link Application#canvas}
14300   */
14301  get view() {
14302    deprecation(v8_0_0, "Application.view is deprecated, please use Application.canvas instead.");
14303    return this.renderer.canvas;
14304  }
14305  /**
14306   * Reference to the renderer's screen rectangle. This represents the visible area of your application.
14307   *
14308   * It's commonly used for:
14309   * - Setting filter areas for full-screen effects
14310   * - Defining hit areas for screen-wide interaction
14311   * - Determining the visible bounds of your application
14312   * @readonly
14313   * @example
14314   * ```js
14315   * // Use as filter area for a full-screen effect
14316   * const blurFilter = new BlurFilter();
14317   * sprite.filterArea = app.screen;
14318   *
14319   * // Use as hit area for screen-wide interaction
14320   * const screenSprite = new Sprite();
14321   * screenSprite.hitArea = app.screen;
14322   *
14323   * // Get screen dimensions
14324   * console.log(app.screen.width, app.screen.height);
14325   * ```
14326   * @see {@link Rectangle} For all available properties and methods
14327   */
14328  get screen() {
14329    return this.renderer.screen;
14330  }
14331  /**
14332   * Destroys the application and all of its resources.
14333   *
14334   * This method should be called when you want to completely
14335   * clean up the application and free all associated memory.
14336   * @param rendererDestroyOptions - Options for destroying the renderer:
14337   *  - `false` or `undefined`: Preserves the canvas element (default)
14338   *  - `true`: Removes the canvas element
14339   *  - `{ removeView: boolean }`: Object with removeView property to control canvas removal
14340   * @param options - Options for destroying the application:
14341   *  - `false` or `undefined`: Basic cleanup (default)
14342   *  - `true`: Complete cleanup including children
14343   *  - Detailed options object:
14344   *    - `children`: Remove children
14345   *    - `texture`: Destroy textures
14346   *    - `textureSource`: Destroy texture sources
14347   *    - `context`: Destroy WebGL context
14348   * @example
14349   * ```js
14350   * // Basic cleanup
14351   * app.destroy();
14352   *
14353   * // Remove canvas and do complete cleanup
14354   * app.destroy(true, true);
14355   *
14356   * // Remove canvas with explicit options
14357   * app.destroy({ removeView: true }, true);
14358   *
14359   * // Detailed cleanup with specific options
14360   * app.destroy(
14361   *     { removeView: true },
14362   *     {
14363   *         children: true,
14364   *         texture: true,
14365   *         textureSource: true,
14366   *         context: true
14367   *     }
14368   * );
14369   * ```
14370   * > [!WARNING] After calling destroy, the application instance should no longer be used.
14371   * > All properties will be null and further operations will throw errors.
14372   */
14373  destroy(rendererDestroyOptions = false, options = false) {
14374    const plugins = _Application._plugins.slice(0);
14375    plugins.reverse();
14376    plugins.forEach((plugin) => {
14377      plugin.destroy.call(this);
14378    });
14379    this.stage.destroy(options);
14380    this.stage = null;
14381    this.renderer.destroy(rendererDestroyOptions);
14382    this.renderer = null;
14383  }
14384};
14385/**
14386 * Collection of installed plugins.
14387 * @internal
14388 */
14389_Application._plugins = [];
14390let Application = _Application;
14391extensions.handleByList(ExtensionType.Application, Application._plugins);
14392extensions.add(ApplicationInitHook);
14393
14394class AbstractBitmapFont extends EventEmitter {
14395  constructor() {
14396    super(...arguments);
14397    /** The map of characters by character code. */
14398    this.chars = /* @__PURE__ */ Object.create(null);
14399    /**
14400     * The line-height of the font face in pixels.
14401     * @type {number}
14402     */
14403    this.lineHeight = 0;
14404    /**
14405     * The name of the font face
14406     * @type {string}
14407     */
14408    this.fontFamily = "";
14409    /** The metrics of the font face. */
14410    this.fontMetrics = { fontSize: 0, ascent: 0, descent: 0 };
14411    /**
14412     * The offset of the font face from the baseline.
14413     * @type {number}
14414     */
14415    this.baseLineOffset = 0;
14416    /** The range and type of the distance field for this font. */
14417    this.distanceField = { type: "none", range: 0 };
14418    /** The map of base page textures (i.e., sheets of glyphs). */
14419    this.pages = [];
14420    /** should the fill for this font be applied as a tint to the text. */
14421    this.applyFillAsTint = true;
14422    /** The size of the font face in pixels. */
14423    this.baseMeasurementFontSize = 100;
14424    this.baseRenderedFontSize = 100;
14425  }
14426  /**
14427   * The name of the font face.
14428   * @deprecated since 8.0.0 Use `fontFamily` instead.
14429   */
14430  get font() {
14431    deprecation(v8_0_0, "BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead.");
14432    return this.fontFamily;
14433  }
14434  /**
14435   * The map of base page textures (i.e., sheets of glyphs).
14436   * @deprecated since 8.0.0 Use `pages` instead.
14437   */
14438  get pageTextures() {
14439    deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
14440    return this.pages;
14441  }
14442  /**
14443   * The size of the font face in pixels.
14444   * @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead.
14445   */
14446  get size() {
14447    deprecation(v8_0_0, "BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead.");
14448    return this.fontMetrics.fontSize;
14449  }
14450  /**
14451   * The kind of distance field for this font or "none".
14452   * @deprecated since 8.0.0 Use `distanceField.type` instead.
14453   */
14454  get distanceFieldRange() {
14455    deprecation(v8_0_0, "BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead.");
14456    return this.distanceField.range;
14457  }
14458  /**
14459   * The range of the distance field in pixels.
14460   * @deprecated since 8.0.0 Use `distanceField.range` instead.
14461   */
14462  get distanceFieldType() {
14463    deprecation(v8_0_0, "BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead.");
14464    return this.distanceField.type;
14465  }
14466  destroy(destroyTextures = false) {
14467    this.emit("destroy", this);
14468    this.removeAllListeners();
14469    for (const i in this.chars) {
14470      this.chars[i].texture?.destroy();
14471    }
14472    this.chars = null;
14473    if (destroyTextures) {
14474      this.pages.forEach((page) => page.texture.destroy(true));
14475      this.pages = null;
14476    }
14477  }
14478}
14479
14480/**
14481 * tiny-lru
14482 *
14483 * @copyright 2026 Jason Mulligan <[email protected]>
14484 * @license BSD-3-Clause
14485 * @version 11.4.7
14486 */
14487/**
14488 * A high-performance Least Recently Used (LRU) cache implementation with optional TTL support.
14489 * Items are automatically evicted when the cache reaches its maximum size,
14490 * removing the least recently used items first. All core operations (get, set, delete) are O(1).
14491 *
14492 * @class LRU
14493 * @example
14494 * // Create a cache with max 100 items
14495 * const cache = new LRU(100);
14496 * cache.set('key1', 'value1');
14497 * console.log(cache.get('key1')); // 'value1'
14498 *
14499 * @example
14500 * // Create a cache with TTL
14501 * const cache = new LRU(100, 5000); // 5 second TTL
14502 * cache.set('key1', 'value1');
14503 * // After 5 seconds, key1 will be expired
14504 */
14505class LRU {
14506	/**
14507	 * Creates a new LRU cache instance.
14508	 * Note: Constructor does not validate parameters. Use lru() factory function for parameter validation.
14509	 *
14510	 * @constructor
14511	 * @param {number} [max=0] - Maximum number of items to store. 0 means unlimited.
14512	 * @param {number} [ttl=0] - Time to live in milliseconds. 0 means no expiration.
14513	 * @param {boolean} [resetTtl=false] - Whether to reset TTL when accessing existing items via get().
14514	 * @example
14515	 * const cache = new LRU(1000, 60000, true); // 1000 items, 1 minute TTL, reset on access
14516	 * @see {@link lru} For parameter validation
14517	 * @since 1.0.0
14518	 */
14519	constructor (max = 0, ttl = 0, resetTtl = false) {
14520		this.first = null;
14521		this.items = Object.create(null);
14522		this.last = null;
14523		this.max = max;
14524		this.resetTtl = resetTtl;
14525		this.size = 0;
14526		this.ttl = ttl;
14527	}
14528
14529	/**
14530	 * Removes all items from the cache.
14531	 *
14532	 * @method clear
14533	 * @memberof LRU
14534	 * @returns {LRU} The LRU instance for method chaining.
14535	 * @example
14536	 * cache.clear();
14537	 * console.log(cache.size); // 0
14538	 * @since 1.0.0
14539	 */
14540	clear () {
14541		this.first = null;
14542		this.items = Object.create(null);
14543		this.last = null;
14544		this.size = 0;
14545
14546		return this;
14547	}
14548
14549	/**
14550	 * Removes an item from the cache by key.
14551	 *
14552	 * @method delete
14553	 * @memberof LRU
14554	 * @param {string} key - The key of the item to delete.
14555	 * @returns {LRU} The LRU instance for method chaining.
14556	 * @example
14557	 * cache.set('key1', 'value1');
14558	 * cache.delete('key1');
14559	 * console.log(cache.has('key1')); // false
14560	 * @see {@link LRU#has}
14561	 * @see {@link LRU#clear}
14562	 * @since 1.0.0
14563	 */
14564	delete (key) {
14565		if (this.has(key)) {
14566			const item = this.items[key];
14567
14568			delete this.items[key];
14569			this.size--;
14570
14571			if (item.prev !== null) {
14572				item.prev.next = item.next;
14573			}
14574
14575			if (item.next !== null) {
14576				item.next.prev = item.prev;
14577			}
14578
14579			if (this.first === item) {
14580				this.first = item.next;
14581			}
14582
14583			if (this.last === item) {
14584				this.last = item.prev;
14585			}
14586		}
14587
14588		return this;
14589	}
14590
14591	/**
14592	 * Returns an array of [key, value] pairs for the specified keys.
14593	 * Order follows LRU order (least to most recently used).
14594	 *
14595	 * @method entries
14596	 * @memberof LRU
14597	 * @param {string[]} [keys=this.keys()] - Array of keys to get entries for. Defaults to all keys.
14598	 * @returns {Array<Array<*>>} Array of [key, value] pairs in LRU order.
14599	 * @example
14600	 * cache.set('a', 1).set('b', 2);
14601	 * console.log(cache.entries()); // [['a', 1], ['b', 2]]
14602	 * console.log(cache.entries(['a'])); // [['a', 1]]
14603	 * @see {@link LRU#keys}
14604	 * @see {@link LRU#values}
14605	 * @since 11.1.0
14606	 */
14607	entries (keys = this.keys()) {
14608		const result = new Array(keys.length);
14609		for (let i = 0; i < keys.length; i++) {
14610			const key = keys[i];
14611			result[i] = [key, this.get(key)];
14612		}
14613
14614		return result;
14615	}
14616
14617	/**
14618	 * Removes the least recently used item from the cache.
14619	 *
14620	 * @method evict
14621	 * @memberof LRU
14622	 * @param {boolean} [bypass=false] - Whether to force eviction even when cache is empty.
14623	 * @returns {LRU} The LRU instance for method chaining.
14624	 * @example
14625	 * cache.set('old', 'value').set('new', 'value');
14626	 * cache.evict(); // Removes 'old' item
14627	 * @see {@link LRU#setWithEvicted}
14628	 * @since 1.0.0
14629	 */
14630	evict (bypass = false) {
14631		if (bypass || this.size > 0) {
14632			const item = this.first;
14633
14634			delete this.items[item.key];
14635
14636			if (--this.size === 0) {
14637				this.first = null;
14638				this.last = null;
14639			} else {
14640				this.first = item.next;
14641				this.first.prev = null;
14642			}
14643		}
14644
14645		return this;
14646	}
14647
14648	/**
14649	 * Returns the expiration timestamp for a given key.
14650	 *
14651	 * @method expiresAt
14652	 * @memberof LRU
14653	 * @param {string} key - The key to check expiration for.
14654	 * @returns {number|undefined} The expiration timestamp in milliseconds, or undefined if key doesn't exist.
14655	 * @example
14656	 * const cache = new LRU(100, 5000); // 5 second TTL
14657	 * cache.set('key1', 'value1');
14658	 * console.log(cache.expiresAt('key1')); // timestamp 5 seconds from now
14659	 * @see {@link LRU#get}
14660	 * @see {@link LRU#has}
14661	 * @since 1.0.0
14662	 */
14663	expiresAt (key) {
14664		let result;
14665
14666		if (this.has(key)) {
14667			result = this.items[key].expiry;
14668		}
14669
14670		return result;
14671	}
14672
14673	/**
14674	 * Retrieves a value from the cache by key. Updates the item's position to most recently used.
14675	 *
14676	 * @method get
14677	 * @memberof LRU
14678	 * @param {string} key - The key to retrieve.
14679	 * @returns {*} The value associated with the key, or undefined if not found or expired.
14680	 * @example
14681	 * cache.set('key1', 'value1');
14682	 * console.log(cache.get('key1')); // 'value1'
14683	 * console.log(cache.get('nonexistent')); // undefined
14684	 * @see {@link LRU#set}
14685	 * @see {@link LRU#has}
14686	 * @since 1.0.0
14687	 */
14688	get (key) {
14689		const item = this.items[key];
14690
14691		if (item !== undefined) {
14692			// Check TTL only if enabled to avoid unnecessary Date.now() calls
14693			if (this.ttl > 0) {
14694				if (item.expiry <= Date.now()) {
14695					this.delete(key);
14696
14697					return undefined;
14698				}
14699			}
14700
14701			// Fast LRU update without full set() overhead
14702			this.moveToEnd(item);
14703
14704			return item.value;
14705		}
14706
14707		return undefined;
14708	}
14709
14710	/**
14711	 * Checks if a key exists in the cache.
14712	 *
14713	 * @method has
14714	 * @memberof LRU
14715	 * @param {string} key - The key to check for.
14716	 * @returns {boolean} True if the key exists, false otherwise.
14717	 * @example
14718	 * cache.set('key1', 'value1');
14719	 * console.log(cache.has('key1')); // true
14720	 * console.log(cache.has('nonexistent')); // false
14721	 * @see {@link LRU#get}
14722	 * @see {@link LRU#delete}
14723	 * @since 9.0.0
14724	 */
14725	has (key) {
14726		return key in this.items;
14727	}
14728
14729	/**
14730	 * Efficiently moves an item to the end of the LRU list (most recently used position).
14731	 * This is an internal optimization method that avoids the overhead of the full set() operation
14732	 * when only LRU position needs to be updated.
14733	 *
14734	 * @method moveToEnd
14735	 * @memberof LRU
14736	 * @param {Object} item - The cache item with prev/next pointers to reposition.
14737	 * @private
14738	 * @since 11.3.5
14739	 */
14740	moveToEnd (item) {
14741		// If already at the end, nothing to do
14742		if (this.last === item) {
14743			return;
14744		}
14745
14746		// Remove item from current position in the list
14747		if (item.prev !== null) {
14748			item.prev.next = item.next;
14749		}
14750
14751		if (item.next !== null) {
14752			item.next.prev = item.prev;
14753		}
14754
14755		// Update first pointer if this was the first item
14756		if (this.first === item) {
14757			this.first = item.next;
14758		}
14759
14760		// Add item to the end
14761		item.prev = this.last;
14762		item.next = null;
14763
14764		if (this.last !== null) {
14765			this.last.next = item;
14766		}
14767
14768		this.last = item;
14769
14770		// Handle edge case: if this was the only item, it's also first
14771		if (this.first === null) {
14772			this.first = item;
14773		}
14774	}
14775
vendor: 8,707 bytes, lines 14776-15054
14776	/**
14777	 * Returns an array of all keys in the cache, ordered from least to most recently used.
14778	 *
14779	 * @method keys
14780	 * @memberof LRU
14781	 * @returns {string[]} Array of keys in LRU order.
14782	 * @example
14783	 * cache.set('a', 1).set('b', 2);
14784	 * cache.get('a'); // Move 'a' to most recent
14785	 * console.log(cache.keys()); // ['b', 'a']
14786	 * @see {@link LRU#values}
14787	 * @see {@link LRU#entries}
14788	 * @since 9.0.0
14789	 */
14790	keys () {
14791		const result = new Array(this.size);
14792		let x = this.first;
14793		let i = 0;
14794
14795		while (x !== null) {
14796			result[i++] = x.key;
14797			x = x.next;
14798		}
14799
14800		return result;
14801	}
14802
14803	/**
14804	 * Sets a value in the cache and returns any evicted item.
14805	 *
14806	 * @method setWithEvicted
14807	 * @memberof LRU
14808	 * @param {string} key - The key to set.
14809	 * @param {*} value - The value to store.
14810	 * @param {boolean} [resetTtl=this.resetTtl] - Whether to reset the TTL for this operation.
14811	 * @returns {Object|null} The evicted item (if any) with shape {key, value, expiry, prev, next}, or null.
14812	 * @example
14813	 * const cache = new LRU(2);
14814	 * cache.set('a', 1).set('b', 2);
14815	 * const evicted = cache.setWithEvicted('c', 3); // evicted = {key: 'a', value: 1, ...}
14816	 * @see {@link LRU#set}
14817	 * @see {@link LRU#evict}
14818	 * @since 11.3.0
14819	 */
14820	setWithEvicted (key, value, resetTtl = this.resetTtl) {
14821		let evicted = null;
14822
14823		if (this.has(key)) {
14824			this.set(key, value, true, resetTtl);
14825		} else {
14826			if (this.max > 0 && this.size === this.max) {
14827				evicted = {...this.first};
14828				this.evict(true);
14829			}
14830
14831			let item = this.items[key] = {
14832				expiry: this.ttl > 0 ? Date.now() + this.ttl : this.ttl,
14833				key: key,
14834				prev: this.last,
14835				next: null,
14836				value
14837			};
14838
14839			if (++this.size === 1) {
14840				this.first = item;
14841			} else {
14842				this.last.next = item;
14843			}
14844
14845			this.last = item;
14846		}
14847
14848		return evicted;
14849	}
14850
14851	/**
14852	 * Sets a value in the cache. Updates the item's position to most recently used.
14853	 *
14854	 * @method set
14855	 * @memberof LRU
14856	 * @param {string} key - The key to set.
14857	 * @param {*} value - The value to store.
14858	 * @param {boolean} [bypass=false] - Internal parameter for setWithEvicted method.
14859	 * @param {boolean} [resetTtl=this.resetTtl] - Whether to reset the TTL for this operation.
14860	 * @returns {LRU} The LRU instance for method chaining.
14861	 * @example
14862	 * cache.set('key1', 'value1')
14863	 *      .set('key2', 'value2')
14864	 *      .set('key3', 'value3');
14865	 * @see {@link LRU#get}
14866	 * @see {@link LRU#setWithEvicted}
14867	 * @since 1.0.0
14868	 */
14869	set (key, value, bypass = false, resetTtl = this.resetTtl) {
14870		let item = this.items[key];
14871
14872		if (bypass || item !== undefined) {
14873			// Existing item: update value and position
14874			item.value = value;
14875
14876			if (bypass === false && resetTtl) {
14877				item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
14878			}
14879
14880			// Always move to end, but the bypass parameter affects TTL reset behavior
14881			this.moveToEnd(item);
14882		} else {
14883			// New item: check for eviction and create
14884			if (this.max > 0 && this.size === this.max) {
14885				this.evict(true);
14886			}
14887
14888			item = this.items[key] = {
14889				expiry: this.ttl > 0 ? Date.now() + this.ttl : this.ttl,
14890				key: key,
14891				prev: this.last,
14892				next: null,
14893				value
14894			};
14895
14896			if (++this.size === 1) {
14897				this.first = item;
14898			} else {
14899				this.last.next = item;
14900			}
14901
14902			this.last = item;
14903		}
14904
14905		return this;
14906	}
14907
14908	/**
14909	 * Returns an array of all values in the cache for the specified keys.
14910	 * Order follows LRU order (least to most recently used).
14911	 *
14912	 * @method values
14913	 * @memberof LRU
14914	 * @param {string[]} [keys=this.keys()] - Array of keys to get values for. Defaults to all keys.
14915	 * @returns {Array<*>} Array of values corresponding to the keys in LRU order.
14916	 * @example
14917	 * cache.set('a', 1).set('b', 2);
14918	 * console.log(cache.values()); // [1, 2]
14919	 * console.log(cache.values(['a'])); // [1]
14920	 * @see {@link LRU#keys}
14921	 * @see {@link LRU#entries}
14922	 * @since 11.1.0
14923	 */
14924	values (keys = this.keys()) {
14925		const result = new Array(keys.length);
14926		for (let i = 0; i < keys.length; i++) {
14927			result[i] = this.get(keys[i]);
14928		}
14929
14930		return result;
14931	}
14932}
14933
14934/**
14935 * Factory function to create a new LRU cache instance with parameter validation.
14936 *
14937 * @function lru
14938 * @param {number} [max=1000] - Maximum number of items to store. Must be >= 0. Use 0 for unlimited size.
14939 * @param {number} [ttl=0] - Time to live in milliseconds. Must be >= 0. Use 0 for no expiration.
14940 * @param {boolean} [resetTtl=false] - Whether to reset TTL when accessing existing items via get().
14941 * @returns {LRU} A new LRU cache instance.
14942 * @throws {TypeError} When parameters are invalid (negative numbers or wrong types).
14943 * @example
14944 * // Create cache with factory function
14945 * const cache = lru(100, 5000, true);
14946 * cache.set('key', 'value');
14947 *
14948 * @example
14949 * // Error handling
14950 * try {
14951 *   const cache = lru(-1); // Invalid max
14952 * } catch (error) {
14953 *   console.error(error.message); // "Invalid max value"
14954 * }
14955 * @see {@link LRU}
14956 * @since 1.0.0
14957 */
14958function lru (max = 1000, ttl = 0, resetTtl = false) {
14959	if (isNaN(max) || max < 0) {
14960		throw new TypeError("Invalid max value");
14961	}
14962
14963	if (isNaN(ttl) || ttl < 0) {
14964		throw new TypeError("Invalid ttl value");
14965	}
14966
14967	if (typeof resetTtl !== "boolean") {
14968		throw new TypeError("Invalid resetTtl value");
14969	}
14970
14971	return new LRU(max, ttl, resetTtl);
14972}
14973
14974const genericFontFamilies = [
14975  "serif",
14976  "sans-serif",
14977  "monospace",
14978  "cursive",
14979  "fantasy",
14980  "system-ui"
14981];
14982function fontStringFromTextStyle(style) {
14983  const fontSizeString = typeof style.fontSize === "number" ? `${style.fontSize}px` : style.fontSize;
14984  let fontFamilies = style.fontFamily;
14985  if (!Array.isArray(style.fontFamily)) {
14986    fontFamilies = style.fontFamily.split(",");
14987  }
14988  for (let i = fontFamilies.length - 1; i >= 0; i--) {
14989    let fontFamily = fontFamilies[i].trim();
14990    if (!/([\"\'])[^\'\"]+\1/.test(fontFamily) && !genericFontFamilies.includes(fontFamily)) {
14991      fontFamily = `"${fontFamily}"`;
14992    }
14993    fontFamilies[i] = fontFamily;
14994  }
14995  return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(",")}`;
14996}
14997
14998const contextSettings = {
14999  // TextMetrics requires getImageData readback for measuring fonts.
15000  willReadFrequently: true
15001};
15002const _CanvasTextMetrics = class _CanvasTextMetrics {
15003  /**
15004   * Checking that we can use modern canvas 2D API.
15005   *
15006   * Note: This is an unstable API, Chrome < 94 use `textLetterSpacing`, later versions use `letterSpacing`.
15007   * @see TextMetrics.experimentalLetterSpacing
15008   * @see https://developer.mozilla.org/en-US/docs/Web/API/ICanvasRenderingContext2D/letterSpacing
15009   * @see https://developer.chrome.com/origintrials/#/view_trial/3585991203293757441
15010   */
15011  static get experimentalLetterSpacingSupported() {
15012    let result = _CanvasTextMetrics._experimentalLetterSpacingSupported;
15013    if (result === void 0) {
15014      const proto = DOMAdapter.get().getCanvasRenderingContext2D().prototype;
15015      result = _CanvasTextMetrics._experimentalLetterSpacingSupported = "letterSpacing" in proto || "textLetterSpacing" in proto;
15016    }
15017    return result;
15018  }
15019  /**
15020   * @param text - the text that was measured
15021   * @param style - the style that was measured
15022   * @param width - the measured width of the text
15023   * @param height - the measured height of the text
15024   * @param lines - an array of the lines of text broken by new lines and wrapping if specified in style
15025   * @param lineWidths - an array of the line widths for each line matched to `lines`
15026   * @param lineHeight - the measured line height for this style
15027   * @param maxLineWidth - the maximum line width for all measured lines
15028   * @param {FontMetrics} fontProperties - the font properties object from TextMetrics.measureFont
15029   */
15030  constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties) {
15031    this.text = text;
15032    this.style = style;
15033    this.width = width;
15034    this.height = height;
15035    this.lines = lines;
15036    this.lineWidths = lineWidths;
15037    this.lineHeight = lineHeight;
15038    this.maxLineWidth = maxLineWidth;
15039    this.fontProperties = fontProperties;
15040  }
15041  /**
15042   * Measures the supplied string of text and returns a Rectangle.
15043   * @param text - The text to measure.
15044   * @param style - The text style to use for measuring
15045   * @param canvas - optional specification of the canvas to use for measuring.
15046   * @param wordWrap
15047   * @returns Measured width and height of the text.
15048   */
15049  static measureText(text = " ", style, canvas = _CanvasTextMetrics._canvas, wordWrap = style.wordWrap) {
15050    const textKey = `${text}-${style.styleKey}-wordWrap-${wordWrap}`;
15051    if (_CanvasTextMetrics._measurementCache.has(textKey)) {
15052      return _CanvasTextMetrics._measurementCache.get(textKey);
15053    }
15054    const font = fontStringFromTextStyle(style);
15055    const fontProperties = _CanvasTextMetrics.measureFont(font);
15056    if (fontProperties.fontSize === 0) {
15057      fontProperties.fontSize = style.fontSize;
15058      fontProperties.ascent = style.fontSize;
15059    }
15060    const context = _CanvasTextMetrics.__context;
15061    context.font = font;
15062    const outputText = wordWrap ? _CanvasTextMetrics._wordWrap(text, style, canvas) : text;
15063    const lines = outputText.split(/(?:\r\n|\r|\n)/);
15064    const lineWidths = new Array(lines.length);
15065    let maxLineWidth = 0;
15066    for (let i = 0; i < lines.length; i++) {
15067      const lineWidth = _CanvasTextMetrics._measureText(lines[i], style.letterSpacing, context);
15068      lineWidths[i] = lineWidth;
15069      maxLineWidth = Math.max(maxLineWidth, lineWidth);
15070    }
15071    const strokeWidth = style._stroke?.width || 0;
15072    let width = maxLineWidth + strokeWidth;
15073    if (style.dropShadow) {
15074      width += style.dropShadow.distance;
15075    }
15076    const lineHeight = style.lineHeight || fontProperties.fontSize;
15077    let height = Math.max(lineHeight, fontProperties.fontSize + strokeWidth) + (lines.length - 1) * (lineHeight + style.leading);
15078    if (style.dropShadow) {
15079      height += style.dropShadow.distance;
15080    }
15081    const measurements = new _CanvasTextMetrics(
15082      text,
15083      style,
15084      width,
15085      height,
15086      lines,
15087      lineWidths,
15088      lineHeight + style.leading,
15089      maxLineWidth,
15090      fontProperties
15091    );
15092    _CanvasTextMetrics._measurementCache.set(textKey, measurements);
15093    return measurements;
15094  }
15095  static _measureText(text, letterSpacing, context) {
15096    let useExperimentalLetterSpacing = false;
15097    if (_CanvasTextMetrics.experimentalLetterSpacingSupported) {
15098      if (_CanvasTextMetrics.experimentalLetterSpacing) {
15099        context.letterSpacing = `${letterSpacing}px`;
15100        context.textLetterSpacing = `${letterSpacing}px`;
15101        useExperimentalLetterSpacing = true;
15102      } else {
15103        context.letterSpacing = "0px";
15104        context.textLetterSpacing = "0px";
15105      }
15106    }
15107    const metrics = context.measureText(text);
15108    let metricWidth = metrics.width;
15109    const actualBoundingBoxLeft = -metrics.actualBoundingBoxLeft;
15110    const actualBoundingBoxRight = metrics.actualBoundingBoxRight;
15111    let boundsWidth = actualBoundingBoxRight - actualBoundingBoxLeft;
15112    if (metricWidth > 0) {
15113      if (useExperimentalLetterSpacing) {
15114        metricWidth -= letterSpacing;
15115        boundsWidth -= letterSpacing;
15116      } else {
15117        const val = (_CanvasTextMetrics.graphemeSegmenter(text).length - 1) * letterSpacing;
15118        metricWidth += val;
15119        boundsWidth += val;
15120      }
15121    }
15122    return Math.max(metricWidth, boundsWidth);
15123  }
15124  /**
15125   * Applies newlines to a string to have it optimally fit into the horizontal
15126   * bounds set by the Text object's wordWrapWidth property.
15127   * @param text - String to apply word wrapping to
15128   * @param style - the style to use when wrapping
15129   * @param canvas - optional specification of the canvas to use for measuring.
15130   * @returns New string with new lines applied where required
15131   */
15132  static _wordWrap(text, style, canvas = _CanvasTextMetrics._canvas) {
15133    const context = canvas.getContext("2d", contextSettings);
15134    let width = 0;
15135    let line = "";
15136    let lines = "";
15137    const cache = /* @__PURE__ */ Object.create(null);
15138    const { letterSpacing, whiteSpace } = style;
15139    const collapseSpaces = _CanvasTextMetrics._collapseSpaces(whiteSpace);
15140    const collapseNewlines = _CanvasTextMetrics._collapseNewlines(whiteSpace);
15141    let canPrependSpaces = !collapseSpaces;
15142    const wordWrapWidth = style.wordWrapWidth + letterSpacing;
15143    const tokens = _CanvasTextMetrics._tokenize(text);
15144    for (let i = 0; i < tokens.length; i++) {
15145      let token = tokens[i];
15146      if (_CanvasTextMetrics._isNewline(token)) {
15147        if (!collapseNewlines) {
15148          lines += _CanvasTextMetrics._addLine(line);
15149          canPrependSpaces = !collapseSpaces;
15150          line = "";
15151          width = 0;
15152          continue;
15153        }
15154        token = " ";
15155      }
15156      if (collapseSpaces) {
15157        const currIsBreakingSpace = _CanvasTextMetrics.isBreakingSpace(token);
15158        const lastIsBreakingSpace = _CanvasTextMetrics.isBreakingSpace(line[line.length - 1]);
15159        if (currIsBreakingSpace && lastIsBreakingSpace) {
15160          continue;
15161        }
15162      }
15163      const tokenWidth = _CanvasTextMetrics._getFromCache(token, letterSpacing, cache, context);
15164      if (tokenWidth > wordWrapWidth) {
15165        if (line !== "") {
15166          lines += _CanvasTextMetrics._addLine(line);
15167          line = "";
15168          width = 0;
15169        }
15170        if (_CanvasTextMetrics.canBreakWords(token, style.breakWords)) {
15171          const characters = _CanvasTextMetrics.wordWrapSplit(token);
15172          for (let j = 0; j < characters.length; j++) {
15173            let char = characters[j];
15174            let lastChar = char;
15175            let k = 1;
15176            while (characters[j + k]) {
15177              const nextChar = characters[j + k];
15178              if (!_CanvasTextMetrics.canBreakChars(lastChar, nextChar, token, j, style.breakWords)) {
15179                char += nextChar;
15180              } else {
15181                break;
15182              }
15183              lastChar = nextChar;
15184              k++;
15185            }
15186            j += k - 1;
15187            const characterWidth = _CanvasTextMetrics._getFromCache(char, letterSpacing, cache, context);
15188            if (characterWidth + width > wordWrapWidth) {
15189              lines += _CanvasTextMetrics._addLine(line);
15190              canPrependSpaces = false;
15191              line = "";
15192              width = 0;
15193            }
15194            line += char;
15195            width += characterWidth;
15196          }
15197        } else {
15198          if (line.length > 0) {
15199            lines += _CanvasTextMetrics._addLine(line);
15200            line = "";
15201            width = 0;
15202          }
15203          const isLastToken = i === tokens.length - 1;
15204          lines += _CanvasTextMetrics._addLine(token, !isLastToken);
15205          canPrependSpaces = false;
15206          line = "";
15207          width = 0;
15208        }
15209      } else {
15210        if (tokenWidth + width > wordWrapWidth) {
15211          canPrependSpaces = false;
15212          lines += _CanvasTextMetrics._addLine(line);
15213          line = "";
15214          width = 0;
15215        }
15216        if (line.length > 0 || !_CanvasTextMetrics.isBreakingSpace(token) || canPrependSpaces) {
15217          line += token;
15218          width += tokenWidth;
15219        }
15220      }
15221    }
15222    lines += _CanvasTextMetrics._addLine(line, false);
15223    return lines;
15224  }
15225  /**
15226   * Convenience function for logging each line added during the wordWrap method.
15227   * @param line    - The line of text to add
15228   * @param newLine - Add new line character to end
15229   * @returns A formatted line
15230   */
15231  static _addLine(line, newLine = true) {
15232    line = _CanvasTextMetrics._trimRight(line);
15233    line = newLine ? `${line}
15234` : line;
15235    return line;
15236  }
15237  /**
15238   * Gets & sets the widths of calculated characters in a cache object
15239   * @param key            - The key
15240   * @param letterSpacing  - The letter spacing
15241   * @param cache          - The cache
15242   * @param context        - The canvas context
15243   * @returns The from cache.
15244   */
15245  static _getFromCache(key, letterSpacing, cache, context) {
15246    let width = cache[key];
15247    if (typeof width !== "number") {
15248      width = _CanvasTextMetrics._measureText(key, letterSpacing, context) + letterSpacing;
15249      cache[key] = width;
15250    }
15251    return width;
15252  }
15253  /**
15254   * Determines whether we should collapse breaking spaces.
15255   * @param whiteSpace - The TextStyle property whiteSpace
15256   * @returns Should collapse
15257   */
15258  static _collapseSpaces(whiteSpace) {
15259    return whiteSpace === "normal" || whiteSpace === "pre-line";
15260  }
15261  /**
15262   * Determines whether we should collapse newLine chars.
15263   * @param whiteSpace - The white space
15264   * @returns should collapse
15265   */
15266  static _collapseNewlines(whiteSpace) {
15267    return whiteSpace === "normal";
15268  }
15269  /**
15270   * Trims breaking whitespaces from string.
15271   * @param text - The text
15272   * @returns Trimmed string
15273   */
15274  static _trimRight(text) {
15275    if (typeof text !== "string") {
15276      return "";
15277    }
15278    for (let i = text.length - 1; i >= 0; i--) {
15279      const char = text[i];
15280      if (!_CanvasTextMetrics.isBreakingSpace(char)) {
15281        break;
15282      }
15283      text = text.slice(0, -1);
15284    }
15285    return text;
15286  }
15287  /**
15288   * Determines if char is a newline.
15289   * @param char - The character
15290   * @returns True if newline, False otherwise.
15291   */
15292  static _isNewline(char) {
15293    if (typeof char !== "string") {
15294      return false;
15295    }
15296    return _CanvasTextMetrics._newlines.includes(char.charCodeAt(0));
15297  }
15298  /**
15299   * Determines if char is a breaking whitespace.
15300   *
15301   * It allows one to determine whether char should be a breaking whitespace
15302   * For example certain characters in CJK langs or numbers.
15303   * It must return a boolean.
15304   * @param char - The character
15305   * @param [_nextChar] - The next character
15306   * @returns True if whitespace, False otherwise.
15307   */
15308  static isBreakingSpace(char, _nextChar) {
15309    if (typeof char !== "string") {
15310      return false;
15311    }
15312    return _CanvasTextMetrics._breakingSpaces.includes(char.charCodeAt(0));
15313  }
15314  /**
15315   * Splits a string into words, breaking-spaces and newLine characters
15316   * @param text - The text
15317   * @returns A tokenized array
15318   */
15319  static _tokenize(text) {
15320    const tokens = [];
15321    let token = "";
15322    if (typeof text !== "string") {
15323      return tokens;
15324    }
15325    for (let i = 0; i < text.length; i++) {
15326      const char = text[i];
15327      const nextChar = text[i + 1];
15328      if (_CanvasTextMetrics.isBreakingSpace(char, nextChar) || _CanvasTextMetrics._isNewline(char)) {
15329        if (token !== "") {
15330          tokens.push(token);
15331          token = "";
15332        }
15333        if (char === "\r" && nextChar === "\n") {
15334          tokens.push("\r\n");
15335          i++;
15336        } else {
15337          tokens.push(char);
15338        }
15339        continue;
15340      }
15341      token += char;
15342    }
15343    if (token !== "") {
15344      tokens.push(token);
15345    }
15346    return tokens;
15347  }
15348  /**
15349   * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
15350   *
15351   * It allows one to customise which words should break
15352   * Examples are if the token is CJK or numbers.
15353   * It must return a boolean.
15354   * @param _token - The token
15355   * @param breakWords - The style attr break words
15356   * @returns Whether to break word or not
15357   */
15358  static canBreakWords(_token, breakWords) {
15359    return breakWords;
15360  }
15361  /**
15362   * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
15363   *
15364   * It allows one to determine whether a pair of characters
15365   * should be broken by newlines
15366   * For example certain characters in CJK langs or numbers.
15367   * It must return a boolean.
15368   * @param _char - The character
15369   * @param _nextChar - The next character
15370   * @param _token - The token/word the characters are from
15371   * @param _index - The index in the token of the char
15372   * @param _breakWords - The style attr break words
15373   * @returns whether to break word or not
15374   */
15375  static canBreakChars(_char, _nextChar, _token, _index, _breakWords) {
15376    return true;
15377  }
15378  /**
15379   * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
15380   *
15381   * It is called when a token (usually a word) has to be split into separate pieces
15382   * in order to determine the point to break a word.
15383   * It must return an array of characters.
15384   * @param token - The token to split
15385   * @returns The characters of the token
15386   * @see CanvasTextMetrics.graphemeSegmenter
15387   */
15388  static wordWrapSplit(token) {
15389    return _CanvasTextMetrics.graphemeSegmenter(token);
15390  }
15391  /**
15392   * Calculates the ascent, descent and fontSize of a given font-style
15393   * @param font - String representing the style of the font
15394   * @returns Font properties object
15395   */
15396  static measureFont(font) {
15397    if (_CanvasTextMetrics._fonts[font]) {
15398      return _CanvasTextMetrics._fonts[font];
15399    }
15400    const context = _CanvasTextMetrics._context;
15401    context.font = font;
15402    const metrics = context.measureText(_CanvasTextMetrics.METRICS_STRING + _CanvasTextMetrics.BASELINE_SYMBOL);
15403    const properties = {
15404      ascent: metrics.actualBoundingBoxAscent,
15405      descent: metrics.actualBoundingBoxDescent,
15406      fontSize: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
15407    };
15408    _CanvasTextMetrics._fonts[font] = properties;
15409    return properties;
15410  }
15411  /**
15412   * Clear font metrics in metrics cache.
15413   * @param {string} [font] - font name. If font name not set then clear cache for all fonts.
15414   */
15415  static clearMetrics(font = "") {
15416    if (font) {
15417      delete _CanvasTextMetrics._fonts[font];
15418    } else {
15419      _CanvasTextMetrics._fonts = {};
15420    }
15421  }
15422  /**
15423   * Cached canvas element for measuring text
15424   * TODO: this should be private, but isn't because of backward compat, will fix later.
15425   * @ignore
15426   */
15427  static get _canvas() {
15428    if (!_CanvasTextMetrics.__canvas) {
15429      let canvas;
15430      try {
15431        const c = new OffscreenCanvas(0, 0);
15432        const context = c.getContext("2d", contextSettings);
15433        if (context?.measureText) {
15434          _CanvasTextMetrics.__canvas = c;
15435          return c;
15436        }
15437        canvas = DOMAdapter.get().createCanvas();
15438      } catch (_cx) {
15439        canvas = DOMAdapter.get().createCanvas();
15440      }
15441      canvas.width = canvas.height = 10;
15442      _CanvasTextMetrics.__canvas = canvas;
15443    }
15444    return _CanvasTextMetrics.__canvas;
15445  }
15446  /**
15447   * TODO: this should be private, but isn't because of backward compat, will fix later.
15448   * @ignore
15449   */
15450  static get _context() {
15451    if (!_CanvasTextMetrics.__context) {
15452      _CanvasTextMetrics.__context = _CanvasTextMetrics._canvas.getContext("2d", contextSettings);
15453    }
15454    return _CanvasTextMetrics.__context;
15455  }
15456};
15457/**
15458 * String used for calculate font metrics.
15459 * These characters are all tall to help calculate the height required for text.
15460 */
15461_CanvasTextMetrics.METRICS_STRING = "|\xC9q\xC5";
15462/** Baseline symbol for calculate font metrics. */
15463_CanvasTextMetrics.BASELINE_SYMBOL = "M";
15464/** Baseline multiplier for calculate font metrics. */
15465_CanvasTextMetrics.BASELINE_MULTIPLIER = 1.4;
15466/** Height multiplier for setting height of canvas to calculate font metrics. */
15467_CanvasTextMetrics.HEIGHT_MULTIPLIER = 2;
15468/**
15469 * A Unicode "character", or "grapheme cluster", can be composed of multiple Unicode code points,
15470 * such as letters with diacritical marks (e.g. `'\u0065\u0301'`, letter e with acute)
15471 * or emojis with modifiers (e.g. `'\uD83E\uDDD1\u200D\uD83D\uDCBB'`, technologist).
15472 * The new `Intl.Segmenter` API in ES2022 can split the string into grapheme clusters correctly. If it is not available,
15473 * PixiJS will fallback to use the iterator of String, which can only spilt the string into code points.
15474 * If you want to get full functionality in environments that don't support `Intl.Segmenter` (such as Firefox),
15475 * you can use other libraries such as [grapheme-splitter]{@link https://www.npmjs.com/package/grapheme-splitter}
15476 * or [graphemer]{@link https://www.npmjs.com/package/graphemer} to create a polyfill. Since these libraries can be
15477 * relatively large in size to handle various Unicode grapheme clusters properly, PixiJS won't use them directly.
15478 */
15479_CanvasTextMetrics.graphemeSegmenter = (() => {
15480  if (typeof Intl?.Segmenter === "function") {
15481    const segmenter = new Intl.Segmenter();
15482    return (s) => {
15483      const segments = segmenter.segment(s);
15484      const result = [];
15485      let i = 0;
15486      for (const segment of segments) {
15487        result[i++] = segment.segment;
15488      }
15489      return result;
15490    };
15491  }
15492  return (s) => [...s];
15493})();
15494/**
15495 * New rendering behavior for letter-spacing which uses Chrome's new native API. This will
15496 * lead to more accurate letter-spacing results because it does not try to manually draw
15497 * each character. However, this Chrome API is experimental and may not serve all cases yet.
15498 * @see TextMetrics.experimentalLetterSpacingSupported
15499 */
15500_CanvasTextMetrics.experimentalLetterSpacing = false;
15501/** Cache of {@link TextMetrics.FontMetrics} objects. */
15502_CanvasTextMetrics._fonts = {};
15503/** Cache of new line chars. */
15504_CanvasTextMetrics._newlines = [
15505  10,
15506  // line feed
15507  13
15508  // carriage return
15509];
15510/** Cache of breaking spaces. */
15511_CanvasTextMetrics._breakingSpaces = [
15512  9,
15513  // character tabulation
15514  32,
15515  // space
15516  8192,
15517  // en quad
15518  8193,
15519  // em quad
15520  8194,
15521  // en space
15522  8195,
15523  // em space
15524  8196,
15525  // three-per-em space
15526  8197,
15527  // four-per-em space
15528  8198,
15529  // six-per-em space
15530  8200,
15531  // punctuation space
15532  8201,
15533  // thin space
15534  8202,
15535  // hair space
15536  8287,
15537  // medium mathematical space
15538  12288
15539  // ideographic space
15540];
15541/** Cache for measured text metrics */
15542_CanvasTextMetrics._measurementCache = lru(1e3);
15543let CanvasTextMetrics = _CanvasTextMetrics;
15544
15545const emptyColorStops = [{ offset: 0, color: "white" }, { offset: 1, color: "black" }];
15546const _FillGradient = class _FillGradient {
15547  constructor(...args) {
15548    /**
15549     * Unique identifier for this gradient instance
15550     * @internal
15551     */
15552    this.uid = uid("fillGradient");
15553    /**
15554     * Internal tick counter to track changes in the gradient.
15555     * This is used to invalidate the gradient when the texture changes.
15556     * @internal
15557     */
15558    this._tick = 0;
15559    /** Type of gradient - currently only supports 'linear' */
15560    this.type = "linear";
15561    /** Array of color stops defining the gradient */
15562    this.colorStops = [];
15563    let options = ensureGradientOptions(args);
15564    const defaults = options.type === "radial" ? _FillGradient.defaultRadialOptions : _FillGradient.defaultLinearOptions;
15565    options = { ...defaults, ...definedProps(options) };
15566    this._textureSize = options.textureSize;
15567    this._wrapMode = options.wrapMode;
15568    if (options.type === "radial") {
15569      this.center = options.center;
15570      this.outerCenter = options.outerCenter ?? this.center;
15571      this.innerRadius = options.innerRadius;
15572      this.outerRadius = options.outerRadius;
15573      this.scale = options.scale;
15574      this.rotation = options.rotation;
15575    } else {
15576      this.start = options.start;
15577      this.end = options.end;
15578    }
15579    this.textureSpace = options.textureSpace;
15580    this.type = options.type;
15581    options.colorStops.forEach((stop) => {
15582      this.addColorStop(stop.offset, stop.color);
15583    });
15584  }
15585  /**
15586   * Adds a color stop to the gradient
15587   * @param offset - Position of the stop (0-1)
15588   * @param color - Color of the stop
15589   * @returns This gradient instance for chaining
15590   */
15591  addColorStop(offset, color) {
15592    this.colorStops.push({ offset, color: Color.shared.setValue(color).toHexa() });
15593    return this;
15594  }
15595  /**
15596   * Builds the internal texture and transform for the gradient.
15597   * Called automatically when the gradient is first used.
15598   * @internal
15599   */
15600  buildLinearGradient() {
15601    if (this.texture)
15602      return;
15603    let { x: x0, y: y0 } = this.start;
15604    let { x: x1, y: y1 } = this.end;
15605    let dx = x1 - x0;
15606    let dy = y1 - y0;
15607    const flip = dx < 0 || dy < 0;
15608    if (this._wrapMode === "clamp-to-edge") {
15609      if (dx < 0) {
15610        const temp = x0;
15611        x0 = x1;
15612        x1 = temp;
15613        dx *= -1;
15614      }
15615      if (dy < 0) {
15616        const temp = y0;
15617        y0 = y1;
15618        y1 = temp;
15619        dy *= -1;
15620      }
15621    }
15622    const colorStops = this.colorStops.length ? this.colorStops : emptyColorStops;
15623    const defaultSize = this._textureSize;
15624    const { canvas, context } = getCanvas(defaultSize, 1);
15625    const gradient = !flip ? context.createLinearGradient(0, 0, this._textureSize, 0) : context.createLinearGradient(this._textureSize, 0, 0, 0);
15626    addColorStops(gradient, colorStops);
15627    context.fillStyle = gradient;
15628    context.fillRect(0, 0, defaultSize, 1);
15629    this.texture = new Texture({
15630      source: new ImageSource({
15631        resource: canvas,
15632        addressMode: this._wrapMode
15633      })
15634    });
15635    const dist = Math.sqrt(dx * dx + dy * dy);
15636    const angle = Math.atan2(dy, dx);
15637    const m = new Matrix();
15638    m.scale(dist / defaultSize, 1);
15639    m.rotate(angle);
15640    m.translate(x0, y0);
15641    if (this.textureSpace === "local") {
15642      m.scale(defaultSize, defaultSize);
15643    }
15644    this.transform = m;
15645  }
15646  /**
15647   * Builds the internal texture and transform for the gradient.
15648   * Called automatically when the gradient is first used.
15649   * @internal
15650   */
15651  buildGradient() {
15652    if (!this.texture)
15653      this._tick++;
15654    if (this.type === "linear") {
15655      this.buildLinearGradient();
15656    } else {
15657      this.buildRadialGradient();
15658    }
15659  }
15660  /**
15661   * Builds the internal texture and transform for the radial gradient.
15662   * Called automatically when the gradient is first used.
15663   * @internal
15664   */
15665  buildRadialGradient() {
15666    if (this.texture)
15667      return;
15668    const colorStops = this.colorStops.length ? this.colorStops : emptyColorStops;
15669    const defaultSize = this._textureSize;
15670    const { canvas, context } = getCanvas(defaultSize, defaultSize);
15671    const { x: x0, y: y0 } = this.center;
15672    const { x: x1, y: y1 } = this.outerCenter;
15673    const r0 = this.innerRadius;
15674    const r1 = this.outerRadius;
15675    const ox = x1 - r1;
15676    const oy = y1 - r1;
15677    const scale = defaultSize / (r1 * 2);
15678    const cx = (x0 - ox) * scale;
15679    const cy = (y0 - oy) * scale;
15680    const gradient = context.createRadialGradient(
15681      cx,
15682      cy,
15683      r0 * scale,
15684      (x1 - ox) * scale,
15685      (y1 - oy) * scale,
15686      r1 * scale
15687    );
15688    addColorStops(gradient, colorStops);
15689    context.fillStyle = colorStops[colorStops.length - 1].color;
15690    context.fillRect(0, 0, defaultSize, defaultSize);
15691    context.fillStyle = gradient;
15692    context.translate(cx, cy);
15693    context.rotate(this.rotation);
15694    context.scale(1, this.scale);
15695    context.translate(-cx, -cy);
15696    context.fillRect(0, 0, defaultSize, defaultSize);
15697    this.texture = new Texture({
15698      source: new ImageSource({
15699        resource: canvas,
15700        addressMode: this._wrapMode
15701      })
15702    });
15703    const m = new Matrix();
15704    m.scale(1 / scale, 1 / scale);
15705    m.translate(ox, oy);
15706    if (this.textureSpace === "local") {
15707      m.scale(defaultSize, defaultSize);
15708    }
15709    this.transform = m;
15710  }
15711  /** Destroys the gradient, releasing resources. This will also destroy the internal texture. */
15712  destroy() {
15713    this.texture?.destroy(true);
15714    this.texture = null;
15715    this.transform = null;
15716    this.colorStops = [];
15717    this.start = null;
15718    this.end = null;
15719    this.center = null;
15720    this.outerCenter = null;
15721  }
15722  /**
15723   * Returns a unique key for this gradient instance.
15724   * This key is used for caching and texture management.
15725   * @returns {string} Unique key for the gradient
15726   */
15727  get styleKey() {
15728    return `fill-gradient-${this.uid}-${this._tick}`;
15729  }
15730};
15731/** Default options for creating a gradient fill */
15732_FillGradient.defaultLinearOptions = {
15733  start: { x: 0, y: 0 },
15734  end: { x: 0, y: 1 },
15735  colorStops: [],
15736  textureSpace: "local",
15737  type: "linear",
15738  textureSize: 256,
15739  wrapMode: "clamp-to-edge"
15740};
15741/** Default options for creating a radial gradient fill */
15742_FillGradient.defaultRadialOptions = {
15743  center: { x: 0.5, y: 0.5 },
15744  innerRadius: 0,
15745  outerRadius: 0.5,
15746  colorStops: [],
15747  scale: 1,
15748  textureSpace: "local",
15749  type: "radial",
15750  textureSize: 256,
15751  wrapMode: "clamp-to-edge"
15752};
15753let FillGradient = _FillGradient;
15754function addColorStops(gradient, colorStops) {
15755  for (let i = 0; i < colorStops.length; i++) {
15756    const stop = colorStops[i];
15757    gradient.addColorStop(stop.offset, stop.color);
15758  }
15759}
15760function getCanvas(width, height) {
15761  const canvas = DOMAdapter.get().createCanvas(width, height);
15762  const context = canvas.getContext("2d");
15763  return { canvas, context };
15764}
15765function ensureGradientOptions(args) {
15766  let options = args[0] ?? {};
15767  if (typeof options === "number" || args[1]) {
15768    deprecation("8.5.2", `use options object instead`);
15769    options = {
15770      type: "linear",
15771      start: { x: args[0], y: args[1] },
15772      end: { x: args[2], y: args[3] },
15773      textureSpace: args[4],
15774      textureSize: args[5] ?? FillGradient.defaultLinearOptions.textureSize
15775    };
15776  }
15777  return options;
15778}
15779
15780const repetitionMap = {
15781  repeat: {
15782    addressModeU: "repeat",
15783    addressModeV: "repeat"
15784  },
15785  "repeat-x": {
15786    addressModeU: "repeat",
15787    addressModeV: "clamp-to-edge"
15788  },
15789  "repeat-y": {
15790    addressModeU: "clamp-to-edge",
15791    addressModeV: "repeat"
15792  },
15793  "no-repeat": {
15794    addressModeU: "clamp-to-edge",
15795    addressModeV: "clamp-to-edge"
15796  }
15797};
15798class FillPattern {
15799  constructor(texture, repetition) {
15800    /**
15801     * unique id for this fill pattern
15802     * @internal
15803     */
15804    this.uid = uid("fillPattern");
15805    /**
15806     * Internal tick counter to track changes in the pattern.
15807     * This is used to invalidate the pattern when the texture or transform changes.
15808     * @internal
15809     */
15810    this._tick = 0;
15811    /** The transform matrix applied to the pattern */
15812    this.transform = new Matrix();
15813    this.texture = texture;
15814    this.transform.scale(
15815      1 / texture.frame.width,
15816      1 / texture.frame.height
15817    );
15818    if (repetition) {
15819      texture.source.style.addressModeU = repetitionMap[repetition].addressModeU;
15820      texture.source.style.addressModeV = repetitionMap[repetition].addressModeV;
15821    }
15822  }
15823  /**
15824   * Sets the transform for the pattern
15825   * @param transform - The transform matrix to apply to the pattern.
15826   * If not provided, the pattern will use the default transform.
15827   */
15828  setTransform(transform) {
15829    const texture = this.texture;
15830    this.transform.copyFrom(transform);
15831    this.transform.invert();
15832    this.transform.scale(
15833      1 / texture.frame.width,
15834      1 / texture.frame.height
15835    );
15836    this._tick++;
15837  }
15838  /** Internal texture used to render the gradient */
15839  get texture() {
15840    return this._texture;
15841  }
15842  set texture(value) {
15843    if (this._texture === value)
15844      return;
15845    this._texture = value;
15846    this._tick++;
15847  }
15848  /**
15849   * Returns a unique key for this instance.
15850   * This key is used for caching.
15851   * @returns {string} Unique key for the instance
15852   */
15853  get styleKey() {
15854    return `fill-pattern-${this.uid}-${this._tick}`;
15855  }
15856  /** Destroys the fill pattern, releasing resources. This will also destroy the internal texture. */
15857  destroy() {
15858    this.texture.destroy(true);
15859    this.texture = null;
15860  }
15861}
15862
15863var parseSvgPath;
15864var hasRequiredParseSvgPath;
15865
15866function requireParseSvgPath () {
15867	if (hasRequiredParseSvgPath) return parseSvgPath;
15868	hasRequiredParseSvgPath = 1;
15869	parseSvgPath = parse;
15870
15871	/**
15872	 * expected argument lengths
15873	 * @type {Object}
15874	 */
15875
15876	var length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0};
15877
15878	/**
15879	 * segment pattern
15880	 * @type {RegExp}
15881	 */
15882
15883	var segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig;
15884
15885	/**
15886	 * parse an svg path data string. Generates an Array
15887	 * of commands where each command is an Array of the
15888	 * form `[command, arg1, arg2, ...]`
15889	 *
15890	 * @param {String} path
15891	 * @return {Array}
15892	 */
15893
15894	function parse(path) {
15895		var data = [];
15896		path.replace(segment, function(_, command, args){
15897			var type = command.toLowerCase();
15898			args = parseValues(args);
15899
15900			// overloaded moveTo
15901			if (type == 'm' && args.length > 2) {
15902				data.push([command].concat(args.splice(0, 2)));
15903				type = 'l';
15904				command = command == 'm' ? 'l' : 'L';
15905			}
15906
15907			while (true) {
15908				if (args.length == length[type]) {
15909					args.unshift(command);
15910					return data.push(args)
15911				}
15912				if (args.length < length[type]) throw new Error('malformed path data')
15913				data.push([command].concat(args.splice(0, length[type])));
15914			}
15915		});
15916		return data
15917	}
15918
15919	var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig;
15920
15921	function parseValues(args) {
15922		var numbers = args.match(number);
15923		return numbers ? numbers.map(Number) : []
15924	}
15925	return parseSvgPath;
15926}
15927
15928var parseSvgPathExports = requireParseSvgPath();
15929const parse = /*@__PURE__*/getDefaultExportFromCjs(parseSvgPathExports);
15930
15931function parseSVGPath(svgPath, path) {
15932  const commands = parse(svgPath);
15933  const subpaths = [];
15934  let currentSubPath = null;
15935  let lastX = 0;
15936  let lastY = 0;
15937  for (let i = 0; i < commands.length; i++) {
15938    const command = commands[i];
15939    const type = command[0];
15940    const data = command;
15941    switch (type) {
15942      case "M":
15943        lastX = data[1];
15944        lastY = data[2];
15945        path.moveTo(lastX, lastY);
15946        break;
15947      case "m":
15948        lastX += data[1];
15949        lastY += data[2];
15950        path.moveTo(lastX, lastY);
15951        break;
15952      case "H":
15953        lastX = data[1];
15954        path.lineTo(lastX, lastY);
15955        break;
15956      case "h":
15957        lastX += data[1];
15958        path.lineTo(lastX, lastY);
15959        break;
15960      case "V":
15961        lastY = data[1];
15962        path.lineTo(lastX, lastY);
15963        break;
15964      case "v":
15965        lastY += data[1];
15966        path.lineTo(lastX, lastY);
15967        break;
15968      case "L":
15969        lastX = data[1];
15970        lastY = data[2];
15971        path.lineTo(lastX, lastY);
15972        break;
15973      case "l":
15974        lastX += data[1];
15975        lastY += data[2];
15976        path.lineTo(lastX, lastY);
15977        break;
15978      case "C":
15979        lastX = data[5];
15980        lastY = data[6];
15981        path.bezierCurveTo(
15982          data[1],
15983          data[2],
15984          // First control point
15985          data[3],
15986          data[4],
15987          // Second control point
15988          lastX,
15989          lastY
15990          // End point
15991        );
15992        break;
15993      case "c":
15994        path.bezierCurveTo(
15995          lastX + data[1],
15996          lastY + data[2],
15997          // First control point
15998          lastX + data[3],
15999          lastY + data[4],
16000          // Second control point
16001          lastX + data[5],
16002          lastY + data[6]
16003          // End point
16004        );
16005        lastX += data[5];
16006        lastY += data[6];
16007        break;
16008      case "S":
16009        lastX = data[3];
16010        lastY = data[4];
16011        path.bezierCurveToShort(
16012          data[1],
16013          data[2],
16014          // Control point
16015          lastX,
16016          lastY
16017          // End point
16018        );
16019        break;
16020      case "s":
16021        path.bezierCurveToShort(
16022          lastX + data[1],
16023          lastY + data[2],
16024          // Control point
16025          lastX + data[3],
16026          lastY + data[4]
16027          // End point
16028        );
16029        lastX += data[3];
16030        lastY += data[4];
16031        break;
16032      case "Q":
16033        lastX = data[3];
16034        lastY = data[4];
16035        path.quadraticCurveTo(
16036          data[1],
16037          data[2],
16038          // Control point
16039          lastX,
16040          lastY
16041          // End point
16042        );
16043        break;
16044      case "q":
16045        path.quadraticCurveTo(
16046          lastX + data[1],
16047          lastY + data[2],
16048          // Control point
16049          lastX + data[3],
16050          lastY + data[4]
16051          // End point
16052        );
16053        lastX += data[3];
16054        lastY += data[4];
16055        break;
16056      case "T":
16057        lastX = data[1];
16058        lastY = data[2];
16059        path.quadraticCurveToShort(
16060          lastX,
16061          lastY
16062          // End point
16063        );
16064        break;
16065      case "t":
16066        lastX += data[1];
16067        lastY += data[2];
16068        path.quadraticCurveToShort(
16069          lastX,
16070          lastY
16071          // End point
16072        );
16073        break;
16074      case "A":
16075        lastX = data[6];
16076        lastY = data[7];
16077        path.arcToSvg(
16078          data[1],
16079          // rx
16080          data[2],
16081          // ry
16082          data[3],
16083          // x-axis-rotation
16084          data[4],
16085          // large-arc-flag
16086          data[5],
16087          // sweep-flag
16088          lastX,
16089          lastY
16090          // End point
16091        );
16092        break;
16093      case "a":
16094        lastX += data[6];
16095        lastY += data[7];
16096        path.arcToSvg(
16097          data[1],
16098          // rx
16099          data[2],
16100          // ry
16101          data[3],
16102          // x-axis-rotation
16103          data[4],
16104          // large-arc-flag
16105          data[5],
16106          // sweep-flag
16107          lastX,
16108          lastY
16109          // End point
16110        );
16111        break;
16112      case "Z":
16113      case "z":
16114        path.closePath();
16115        if (subpaths.length > 0) {
16116          currentSubPath = subpaths.pop();
16117          if (currentSubPath) {
16118            lastX = currentSubPath.startX;
16119            lastY = currentSubPath.startY;
16120          } else {
16121            lastX = 0;
16122            lastY = 0;
16123          }
16124        }
16125        currentSubPath = null;
16126        break;
16127      default:
16128        warn(`Unknown SVG path command: ${type}`);
16129    }
16130    if (type !== "Z" && type !== "z") {
16131      if (currentSubPath === null) {
16132        currentSubPath = { startX: lastX, startY: lastY };
16133        subpaths.push(currentSubPath);
16134      }
16135    }
16136  }
16137  return path;
16138}
16139
16140class Circle {
16141  /**
16142   * @param x - The X coordinate of the center of this circle
16143   * @param y - The Y coordinate of the center of this circle
16144   * @param radius - The radius of the circle
16145   */
16146  constructor(x = 0, y = 0, radius = 0) {
16147    /**
16148     * The type of the object, mainly used to avoid `instanceof` checks.
16149     * @example
16150     * ```ts
16151     * // Check shape type
16152     * const shape = new Circle(0, 0, 50);
16153     * console.log(shape.type); // 'circle'
16154     *
16155     * // Use in type guards
16156     * if (shape.type === 'circle') {
16157     *     console.log(shape.radius);
16158     * }
16159     * ```
16160     * @remarks
16161     * - Used for shape type checking
16162     * - More efficient than instanceof
16163     * - Read-only property
16164     * @readonly
16165     * @default 'circle'
16166     * @see {@link SHAPE_PRIMITIVE} For all shape types
16167     * @see {@link ShapePrimitive} For shape interface
16168     */
16169    this.type = "circle";
16170    this.x = x;
16171    this.y = y;
16172    this.radius = radius;
16173  }
16174  /**
16175   * Creates a clone of this Circle instance.
16176   * @example
16177   * ```ts
16178   * // Basic circle cloning
16179   * const original = new Circle(100, 100, 50);
16180   * const copy = original.clone();
16181   *
16182   * // Clone and modify
16183   * const modified = original.clone();
16184   * modified.radius = 75;
16185   *
16186   * // Verify independence
16187   * console.log(original.radius); // 50
16188   * console.log(modified.radius); // 75
16189   * ```
16190   * @returns A copy of the Circle
16191   * @see {@link Circle.copyFrom} For copying into existing circle
16192   * @see {@link Circle.copyTo} For copying to another circle
16193   */
16194  clone() {
16195    return new Circle(this.x, this.y, this.radius);
16196  }
16197  /**
16198   * Checks whether the x and y coordinates given are contained within this circle.
16199   *
16200   * Uses the distance formula to determine if a point is inside the circle's radius.
16201   *
16202   * Commonly used for hit testing in PixiJS events and graphics.
16203   * @example
16204   * ```ts
16205   * // Basic containment check
16206   * const circle = new Circle(100, 100, 50);
16207   * const isInside = circle.contains(120, 120);
16208   *
16209   * // Check mouse position
16210   * const circle = new Circle(0, 0, 100);
16211   * container.hitArea = circle;
16212   * container.on('pointermove', (e) => {
16213   *     // only called if pointer is within circle
16214   * });
16215   * ```
16216   * @param x - The X coordinate of the point to test
16217   * @param y - The Y coordinate of the point to test
16218   * @returns Whether the x/y coordinates are within this Circle
16219   * @see {@link Circle.strokeContains} For checking stroke intersection
16220   * @see {@link Circle.getBounds} For getting bounding box
16221   */
16222  contains(x, y) {
16223    if (this.radius <= 0)
16224      return false;
16225    const r2 = this.radius * this.radius;
16226    let dx = this.x - x;
16227    let dy = this.y - y;
16228    dx *= dx;
16229    dy *= dy;
16230    return dx + dy <= r2;
16231  }
16232  /**
16233   * Checks whether the x and y coordinates given are contained within this circle including the stroke.
16234   * @example
16235   * ```ts
16236   * // Basic stroke check
16237   * const circle = new Circle(100, 100, 50);
16238   * const isOnStroke = circle.strokeContains(150, 100, 4); // 4px line width
16239   *
16240   * // Check with different alignments
16241   * const innerStroke = circle.strokeContains(150, 100, 4, 1);   // Inside
16242   * const centerStroke = circle.strokeContains(150, 100, 4, 0.5); // Centered
16243   * const outerStroke = circle.strokeContains(150, 100, 4, 0);   // Outside
16244   * ```
16245   * @param x - The X coordinate of the point to test
16246   * @param y - The Y coordinate of the point to test
16247   * @param width - The width of the line to check
16248   * @param alignment - The alignment of the stroke, 0.5 by default
16249   * @returns Whether the x/y coordinates are within this Circle's stroke
16250   * @see {@link Circle.contains} For checking fill containment
16251   * @see {@link Circle.getBounds} For getting stroke bounds
16252   */
16253  strokeContains(x, y, width, alignment = 0.5) {
16254    if (this.radius === 0)
16255      return false;
16256    const dx = this.x - x;
16257    const dy = this.y - y;
16258    const radius = this.radius;
16259    const outerWidth = (1 - alignment) * width;
16260    const distance = Math.sqrt(dx * dx + dy * dy);
16261    return distance <= radius + outerWidth && distance > radius - (width - outerWidth);
16262  }
16263  /**
16264   * Returns the framing rectangle of the circle as a Rectangle object.
16265   * @example
16266   * ```ts
16267   * // Basic bounds calculation
16268   * const circle = new Circle(100, 100, 50);
16269   * const bounds = circle.getBounds();
16270   * // bounds: x=50, y=50, width=100, height=100
16271   *
16272   * // Reuse existing rectangle
16273   * const rect = new Rectangle();
16274   * circle.getBounds(rect);
16275   * ```
16276   * @param out - Optional Rectangle object to store the result
16277   * @returns The framing rectangle
16278   * @see {@link Rectangle} For rectangle properties
16279   * @see {@link Circle.contains} For point containment
16280   */
16281  getBounds(out) {
16282    out || (out = new Rectangle());
16283    out.x = this.x - this.radius;
16284    out.y = this.y - this.radius;
16285    out.width = this.radius * 2;
16286    out.height = this.radius * 2;
16287    return out;
16288  }
16289  /**
16290   * Copies another circle to this one.
16291   * @example
16292   * ```ts
16293   * // Basic copying
16294   * const source = new Circle(100, 100, 50);
16295   * const target = new Circle();
16296   * target.copyFrom(source);
16297   * ```
16298   * @param circle - The circle to copy from
16299   * @returns Returns itself
16300   * @see {@link Circle.copyTo} For copying to another circle
16301   * @see {@link Circle.clone} For creating new circle copy
16302   */
16303  copyFrom(circle) {
vendor: 4,401 bytes, lines 16304-16436
16304    this.x = circle.x;
16305    this.y = circle.y;
16306    this.radius = circle.radius;
16307    return this;
16308  }
16309  /**
16310   * Copies this circle to another one.
16311   * @example
16312   * ```ts
16313   * // Basic copying
16314   * const source = new Circle(100, 100, 50);
16315   * const target = new Circle();
16316   * source.copyTo(target);
16317   * ```
16318   * @param circle - The circle to copy to
16319   * @returns Returns given parameter
16320   * @see {@link Circle.copyFrom} For copying from another circle
16321   * @see {@link Circle.clone} For creating new circle copy
16322   */
16323  copyTo(circle) {
16324    circle.copyFrom(this);
16325    return circle;
16326  }
16327  toString() {
16328    return `[pixi.js/math:Circle x=${this.x} y=${this.y} radius=${this.radius}]`;
16329  }
16330}
16331
16332class Ellipse {
16333  /**
16334   * @param x - The X coordinate of the center of this ellipse
16335   * @param y - The Y coordinate of the center of this ellipse
16336   * @param halfWidth - The half width of this ellipse
16337   * @param halfHeight - The half height of this ellipse
16338   */
16339  constructor(x = 0, y = 0, halfWidth = 0, halfHeight = 0) {
16340    /**
16341     * The type of the object, mainly used to avoid `instanceof` checks
16342     * @example
16343     * ```ts
16344     * // Check shape type
16345     * const shape = new Ellipse(0, 0, 50, 25);
16346     * console.log(shape.type); // 'ellipse'
16347     *
16348     * // Use in type guards
16349     * if (shape.type === 'ellipse') {
16350     *     console.log(shape.halfWidth, shape.halfHeight);
16351     * }
16352     * ```
16353     * @readonly
16354     * @default 'ellipse'
16355     * @see {@link SHAPE_PRIMITIVE} For all shape types
16356     */
16357    this.type = "ellipse";
16358    this.x = x;
16359    this.y = y;
16360    this.halfWidth = halfWidth;
16361    this.halfHeight = halfHeight;
16362  }
16363  /**
16364   * Creates a clone of this Ellipse instance.
16365   * @example
16366   * ```ts
16367   * // Basic cloning
16368   * const original = new Ellipse(100, 100, 50, 25);
16369   * const copy = original.clone();
16370   *
16371   * // Clone and modify
16372   * const modified = original.clone();
16373   * modified.halfWidth *= 2;
16374   * modified.halfHeight *= 2;
16375   *
16376   * // Verify independence
16377   * console.log(original.halfWidth);  // 50
16378   * console.log(modified.halfWidth);  // 100
16379   * ```
16380   * @returns A copy of the ellipse
16381   * @see {@link Ellipse.copyFrom} For copying into existing ellipse
16382   * @see {@link Ellipse.copyTo} For copying to another ellipse
16383   */
16384  clone() {
16385    return new Ellipse(this.x, this.y, this.halfWidth, this.halfHeight);
16386  }
16387  /**
16388   * Checks whether the x and y coordinates given are contained within this ellipse.
16389   * Uses normalized coordinates and the ellipse equation to determine containment.
16390   * @example
16391   * ```ts
16392   * // Basic containment check
16393   * const ellipse = new Ellipse(100, 100, 50, 25);
16394   * const isInside = ellipse.contains(120, 110);
16395   * ```
16396   * @remarks
16397   * - Uses ellipse equation (x²/a² + y²/b² ≤ 1)
16398   * - Returns false if dimensions are 0 or negative
16399   * - Normalized to center (0,0) for calculation
16400   * @param x - The X coordinate of the point to test
16401   * @param y - The Y coordinate of the point to test
16402   * @returns Whether the x/y coords are within this ellipse
16403   * @see {@link Ellipse.strokeContains} For checking stroke intersection
16404   * @see {@link Ellipse.getBounds} For getting containing rectangle
16405   */
16406  contains(x, y) {
16407    if (this.halfWidth <= 0 || this.halfHeight <= 0) {
16408      return false;
16409    }
16410    let normx = (x - this.x) / this.halfWidth;
16411    let normy = (y - this.y) / this.halfHeight;
16412    normx *= normx;
16413    normy *= normy;
16414    return normx + normy <= 1;
16415  }
16416  /**
16417   * Checks whether the x and y coordinates given are contained within this ellipse including stroke.
16418   * @example
16419   * ```ts
16420   * // Basic stroke check
16421   * const ellipse = new Ellipse(100, 100, 50, 25);
16422   * const isOnStroke = ellipse.strokeContains(150, 100, 4); // 4px line width
16423   *
16424   * // Check with different alignments
16425   * const innerStroke = ellipse.strokeContains(150, 100, 4, 1);   // Inside
16426   * const centerStroke = ellipse.strokeContains(150, 100, 4, 0.5); // Centered
16427   * const outerStroke = ellipse.strokeContains(150, 100, 4, 0);   // Outside
16428   * ```
16429   * @remarks
16430   * - Uses normalized ellipse equations
16431   * - Considers stroke alignment
16432   * - Returns false if dimensions are 0
16433   * @param x - The X coordinate of the point to test
16434   * @param y - The Y coordinate of the point to test
16435   * @param strokeWidth - The width of the line to check
16436   * @param alignment - The alignment of the stroke (1 = inner, 
164360.5 = centered, 0 = outer)
16437   * @returns Whether the x/y coords are within this ellipse's stroke
16438   * @see {@link Ellipse.contains} For checking fill containment
16439   * @see {@link Ellipse.getBounds} For getting stroke bounds
16440   */
16441  strokeContains(x, y, strokeWidth, alignment = 0.5) {
16442    const { halfWidth, halfHeight } = this;
16443    if (halfWidth <= 0 || halfHeight <= 0) {
16444      return false;
16445    }
16446    const strokeOuterWidth = strokeWidth * (1 - alignment);
16447    const strokeInnerWidth = strokeWidth - strokeOuterWidth;
16448    const innerHorizontal = halfWidth - strokeInnerWidth;
16449    const innerVertical = halfHeight - strokeInnerWidth;
16450    const outerHorizontal = halfWidth + strokeOuterWidth;
16451    const outerVertical = halfHeight + strokeOuterWidth;
16452    const normalizedX = x - this.x;
16453    const normalizedY = y - this.y;
16454    const innerEllipse = normalizedX * normalizedX / (innerHorizontal * innerHorizontal) + normalizedY * normalizedY / (innerVertical * innerVertical);
16455    const outerEllipse = normalizedX * normalizedX / (outerHorizontal * outerHorizontal) + normalizedY * normalizedY / (outerVertical * outerVertical);
16456    return innerEllipse > 1 && outerEllipse <= 1;
16457  }
16458  /**
16459   * Returns the framing rectangle of the ellipse as a Rectangle object.
16460   * @example
16461   * ```ts
16462   * // Basic bounds calculation
16463   * const ellipse = new Ellipse(100, 100, 50, 25);
16464   * const bounds = ellipse.getBounds();
16465   * // bounds: x=50, y=75, width=100, height=50
16466   *
16467   * // Reuse existing rectangle
16468   * const rect = new Rectangle();
16469   * ellipse.getBounds(rect);
16470   * ```
16471   * @remarks
16472   * - Creates Rectangle if none provided
16473   * - Top-left is (x-halfWidth, y-halfHeight)
16474   * - Width is halfWidth * 2
16475   * - Height is halfHeight * 2
16476   * @param out - Optional Rectangle object to store the result
16477   * @returns The framing rectangle
16478   * @see {@link Rectangle} For rectangle properties
16479   * @see {@link Ellipse.contains} For checking if a point is inside
16480   */
16481  getBounds(out) {
16482    out || (out = new Rectangle());
16483    out.x = this.x - this.halfWidth;
16484    out.y = this.y - this.halfHeight;
16485    out.width = this.halfWidth * 2;
16486    out.height = this.halfHeight * 2;
16487    return out;
16488  }
16489  /**
16490   * Copies another ellipse to this one.
16491   * @example
16492   * ```ts
16493   * // Basic copying
16494   * const source = new Ellipse(100, 100, 50, 25);
16495   * const target = new Ellipse();
16496   * target.copyFrom(source);
16497   * ```
16498   * @param ellipse - The ellipse to copy from
16499   * @returns Returns itself
16500   * @see {@link Ellipse.copyTo} For copying to another ellipse
16501   * @see {@link Ellipse.clone} For creating new ellipse copy
16502   */
16503  copyFrom(ellipse) {
16504    this.x = ellipse.x;
16505    this.y = ellipse.y;
16506    this.halfWidth = ellipse.halfWidth;
16507    this.halfHeight = ellipse.halfHeight;
16508    return this;
16509  }
16510  /**
16511   * Copies this ellipse to another one.
16512   * @example
16513   * ```ts
16514   * // Basic copying
16515   * const source = new Ellipse(100, 100, 50, 25);
16516   * const target = new Ellipse();
16517   * source.copyTo(target);
16518   * ```
16519   * @param ellipse - The ellipse to copy to
16520   * @returns Returns given parameter
16521   * @see {@link Ellipse.copyFrom} For copying from another ellipse
16522   * @see {@link Ellipse.clone} For creating new ellipse copy
16523   */
16524  copyTo(ellipse) {
16525    ellipse.copyFrom(this);
16526    return ellipse;
16527  }
16528  toString() {
16529    return `[pixi.js/math:Ellipse x=${this.x} y=${this.y} halfWidth=${this.halfWidth} halfHeight=${this.halfHeight}]`;
16530  }
16531}
16532
16533function squaredDistanceToLineSegment(x, y, x1, y1, x2, y2) {
16534  const a = x - x1;
16535  const b = y - y1;
16536  const c = x2 - x1;
16537  const d = y2 - y1;
16538  const dot = a * c + b * d;
16539  const lenSq = c * c + d * d;
16540  let param = -1;
16541  if (lenSq !== 0) {
16542    param = dot / lenSq;
16543  }
16544  let xx;
16545  let yy;
16546  if (param < 0) {
16547    xx = x1;
16548    yy = y1;
16549  } else if (param > 1) {
16550    xx = x2;
16551    yy = y2;
16552  } else {
16553    xx = x1 + param * c;
16554    yy = y1 + param * d;
16555  }
16556  const dx = x - xx;
16557  const dy = y - yy;
16558  return dx * dx + dy * dy;
16559}
16560
16561let tempRect$2;
16562let tempRect2;
16563class Polygon {
16564  /**
16565   * @param points - This can be an array of Points
16566   *  that form the polygon, a flat array of numbers that will be interpreted as [x,y, x,y, ...], or
16567   *  the arguments passed can be all the points of the polygon e.g.
16568   *  `new Polygon(new Point(), new Point(), ...)`, or the arguments passed can be flat
16569   *  x,y values e.g. `new Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers.
16570   */
16571  constructor(...points) {
16572    /**
16573     * The type of the object, mainly used to avoid `instanceof` checks
16574     * @example
16575     * ```ts
16576     * // Check shape type
16577     * const shape = new Polygon([0, 0, 100, 0, 50, 100]);
16578     * console.log(shape.type); // 'polygon'
16579     *
16580     * // Use in type guards
16581     * if (shape.type === 'polygon') {
16582     *     // TypeScript knows this is a Polygon
16583     *     console.log(shape.points.length);
16584     * }
16585     * ```
16586     * @readonly
16587     * @default 'polygon'
16588     * @see {@link SHAPE_PRIMITIVE} For all shape types
16589     */
16590    this.type = "polygon";
16591    let flat = Array.isArray(points[0]) ? points[0] : points;
16592    if (typeof flat[0] !== "number") {
16593      const p = [];
16594      for (let i = 0, il = flat.length; i < il; i++) {
16595        p.push(flat[i].x, flat[i].y);
16596      }
16597      flat = p;
16598    }
16599    this.points = flat;
16600    this.closePath = true;
16601  }
16602  /**
16603   * Determines whether the polygon's points are arranged in a clockwise direction.
16604   * Uses the shoelace formula (surveyor's formula) to calculate the signed area.
16605   *
16606   * A positive area indicates clockwise winding, while negative indicates counter-clockwise.
16607   *
16608   * The formula sums up the cross products of adjacent vertices:
16609   * For each pair of adjacent points (x1,y1) and (x2,y2), we calculate (x1*y2 - x2*y1)
16610   * The final sum divided by 2 gives the signed area - positive for clockwise.
16611   * @example
16612   * ```ts
16613   * // Check polygon winding
16614   * const polygon = new Polygon([0, 0, 100, 0, 50, 100]);
16615   * console.log(polygon.isClockwise()); // Check direction
16616   *
16617   * // Use in path construction
16618   * const hole = new Polygon([25, 25, 75, 25, 75, 75, 25, 75]);
16619   * if (hole.isClockwise() === shape.isClockwise()) {
16620   *     hole.points.reverse(); // Reverse for proper hole winding
16621   * }
16622   * ```
16623   * @returns `true` if the polygon's points are arranged clockwise, `false` if counter-clockwise
16624   */
16625  isClockwise() {
16626    let area = 0;
16627    const points = this.points;
16628    const length = points.length;
16629    for (let i = 0; i < length; i += 2) {
16630      const x1 = points[i];
16631      const y1 = points[i + 1];
16632      const x2 = points[(i + 2) % length];
16633      const y2 = points[(i + 3) % length];
16634      area += (x2 - x1) * (y2 + y1);
16635    }
16636    return area < 0;
16637  }
16638  /**
16639   * Checks if this polygon completely contains another polygon.
16640   * Used for detecting holes in shapes, like when parsing SVG paths.
16641   * @example
16642   * ```ts
16643   * // Basic containment check
16644   * const outerSquare = new Polygon([0,0, 100,0, 100,100, 0,100]); // A square
16645   * const innerSquare = new Polygon([25,25, 75,25, 75,75, 25,75]); // A smaller square inside
16646   *
16647   * outerSquare.containsPolygon(innerSquare); // Returns true
16648   * innerSquare.containsPolygon(outerSquare); // Returns false
16649   * ```
16650   * @remarks
16651   * - Uses bounds check for quick rejection
16652   * - Tests all points for containment
16653   * @param polygon - The polygon to test for containment
16654   * @returns True if this polygon completely contains the other polygon
16655   * @see {@link Polygon.contains} For single point testing
16656   * @see {@link Polygon.getBounds} For bounds calculation
16657   */
16658  containsPolygon(polygon) {
16659    const thisBounds = this.getBounds(tempRect$2);
16660    const otherBounds = polygon.getBounds(tempRect2);
16661    if (!thisBounds.containsRect(otherBounds)) {
16662      return false;
16663    }
16664    const points = polygon.points;
16665    for (let i = 0; i < points.length; i += 2) {
16666      const x = points[i];
16667      const y = points[i + 1];
16668      if (!this.contains(x, y)) {
16669        return false;
16670      }
16671    }
16672    return true;
16673  }
16674  /**
16675   * Creates a clone of this polygon.
16676   * @example
16677   * ```ts
16678   * // Basic cloning
16679   * const original = new Polygon([0, 0, 100, 0, 50, 100]);
16680   * const copy = original.clone();
16681   *
16682   * // Clone and modify
16683   * const modified = original.clone();
16684   * modified.points[0] = 10; // Modify first x coordinate
16685   * ```
16686   * @returns A copy of the polygon
16687   * @see {@link Polygon.copyFrom} For copying into existing polygon
16688   * @see {@link Polygon.copyTo} For copying to another polygon
16689   */
16690  clone() {
16691    const points = this.points.slice();
16692    const polygon = new Polygon(points);
16693    polygon.closePath = this.closePath;
16694    return polygon;
16695  }
16696  /**
16697   * Checks whether the x and y coordinates passed to this function are contained within this polygon.
16698   * Uses raycasting algorithm for point-in-polygon testing.
16699   * @example
16700   * ```ts
16701   * // Basic containment check
16702   * const polygon = new Polygon([0, 0, 100, 0, 50, 100]);
16703   * const isInside = polygon.contains(25, 25); // true
16704   * ```
16705   * @param x - The X coordinate of the point to test
16706   * @param y - The Y coordinate of the point to test
16707   * @returns Whether the x/y coordinates are within this polygon
16708   * @see {@link Polygon.strokeContains} For checking stroke intersection
16709   * @see {@link Polygon.containsPolygon} For polygon-in-polygon testing
16710   */
16711  contains(x, y) {
16712    let inside = false;
vendor: 5,087 bytes, lines 16713-16855
16713    const length = this.points.length / 2;
16714    for (let i = 0, j = length - 1; i < length; j = i++) {
16715      const xi = this.points[i * 2];
16716      const yi = this.points[i * 2 + 1];
16717      const xj = this.points[j * 2];
16718      const yj = this.points[j * 2 + 1];
16719      const intersect = yi > y !== yj > y && x < (xj - xi) * ((y - yi) / (yj - yi)) + xi;
16720      if (intersect) {
16721        inside = !inside;
16722      }
16723    }
16724    return inside;
16725  }
16726  /**
16727   * Checks whether the x and y coordinates given are contained within this polygon including the stroke.
16728   * @example
16729   * ```ts
16730   * // Basic stroke check
16731   * const polygon = new Polygon([0, 0, 100, 0, 50, 100]);
16732   * const isOnStroke = polygon.strokeContains(25, 25, 4); // 4px line width
16733   *
16734   * // Check with different alignments
16735   * const innerStroke = polygon.strokeContains(25, 25, 4, 1);   // Inside
16736   * const centerStroke = polygon.strokeContains(25, 25, 4, 0.5); // Centered
16737   * const outerStroke = polygon.strokeContains(25, 25, 4, 0);   // Outside
16738   * ```
16739   * @param x - The X coordinate of the point to test
16740   * @param y - The Y coordinate of the point to test
16741   * @param strokeWidth - The width of the line to check
16742   * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer)
16743   * @returns Whether the x/y coordinates are within this polygon's stroke
16744   * @see {@link Polygon.contains} For checking fill containment
16745   * @see {@link Polygon.getBounds} For getting stroke bounds
16746   */
16747  strokeContains(x, y, strokeWidth, alignment = 0.5) {
16748    const strokeWidthSquared = strokeWidth * strokeWidth;
16749    const rightWidthSquared = strokeWidthSquared * (1 - alignment);
16750    const leftWidthSquared = strokeWidthSquared - rightWidthSquared;
16751    const { points } = this;
16752    const iterationLength = points.length - (this.closePath ? 0 : 2);
16753    for (let i = 0; i < iterationLength; i += 2) {
16754      const x1 = points[i];
16755      const y1 = points[i + 1];
16756      const x2 = points[(i + 2) % points.length];
16757      const y2 = points[(i + 3) % points.length];
16758      const distanceSquared = squaredDistanceToLineSegment(x, y, x1, y1, x2, y2);
16759      const sign = Math.sign((x2 - x1) * (y - y1) - (y2 - y1) * (x - x1));
16760      if (distanceSquared <= (sign < 0 ? leftWidthSquared : rightWidthSquared)) {
16761        return true;
16762      }
16763    }
16764    return false;
16765  }
16766  /**
16767   * Returns the framing rectangle of the polygon as a Rectangle object.
16768   * @example
16769   * ```ts
16770   * // Basic bounds calculation
16771   * const polygon = new Polygon([0, 0, 100, 0, 50, 100]);
16772   * const bounds = polygon.getBounds();
16773   * // bounds: x=0, y=0, width=100, height=100
16774   *
16775   * // Reuse existing rectangle
16776   * const rect = new Rectangle();
16777   * polygon.getBounds(rect);
16778   * ```
16779   * @param out - Optional rectangle to store the result
16780   * @returns The framing rectangle
16781   * @see {@link Rectangle} For rectangle properties
16782   * @see {@link Polygon.contains} For checking if a point is inside
16783   */
16784  getBounds(out) {
16785    out || (out = new Rectangle());
16786    const points = this.points;
16787    let minX = Infinity;
16788    let maxX = -Infinity;
16789    let minY = Infinity;
16790    let maxY = -Infinity;
16791    for (let i = 0, n = points.length; i < n; i += 2) {
16792      const x = points[i];
16793      const y = points[i + 1];
16794      minX = x < minX ? x : minX;
16795      maxX = x > maxX ? x : maxX;
16796      minY = y < minY ? y : minY;
16797      maxY = y > maxY ? y : maxY;
16798    }
16799    out.x = minX;
16800    out.width = maxX - minX;
16801    out.y = minY;
16802    out.height = maxY - minY;
16803    return out;
16804  }
16805  /**
16806   * Copies another polygon to this one.
16807   * @example
16808   * ```ts
16809   * // Basic copying
16810   * const source = new Polygon([0, 0, 100, 0, 50, 100]);
16811   * const target = new Polygon();
16812   * target.copyFrom(source);
16813   * ```
16814   * @param polygon - The polygon to copy from
16815   * @returns Returns itself
16816   * @see {@link Polygon.copyTo} For copying to another polygon
16817   * @see {@link Polygon.clone} For creating new polygon copy
16818   */
16819  copyFrom(polygon) {
16820    this.points = polygon.points.slice();
16821    this.closePath = polygon.closePath;
16822    return this;
16823  }
16824  /**
16825   * Copies this polygon to another one.
16826   * @example
16827   * ```ts
16828   * // Basic copying
16829   * const source = new Polygon([0, 0, 100, 0, 50, 100]);
16830   * const target = new Polygon();
16831   * source.copyTo(target);
16832   * ```
16833   * @param polygon - The polygon to copy to
16834   * @returns Returns given parameter
16835   * @see {@link Polygon.copyFrom} For copying from another polygon
16836   * @see {@link Polygon.clone} For creating new polygon copy
16837   */
16838  copyTo(polygon) {
16839    polygon.copyFrom(this);
16840    return polygon;
16841  }
16842  toString() {
16843    return `[pixi.js/math:PolygoncloseStroke=${this.closePath}points=${this.points.reduce((pointsDesc, currentPoint) => `${pointsDesc}, ${currentPoint}`, "")}]`;
16844  }
16845  /**
16846   * Get the last X coordinate of the polygon.
16847   * @example
16848   * ```ts
16849   * // Basic coordinate access
16850   * const polygon = new Polygon([0, 0, 100, 200, 300, 400]);
16851   * console.log(polygon.lastX); // 300
16852   * ```
16853   * @readonly
16854   * @returns The x-coordinate of the last vertex
16855   * @see {@link Polygon.lastY}
16855 For last Y coordinate
16856   * @see {@link Polygon.points} For raw points array
16857   */
16858  get lastX() {
16859    return this.points[this.points.length - 2];
16860  }
16861  /**
16862   * Get the last Y coordinate of the polygon.
16863   * @example
16864   * ```ts
16865   * // Basic coordinate access
16866   * const polygon = new Polygon([0, 0, 100, 200, 300, 400]);
16867   * console.log(polygon.lastY); // 400
16868   * ```
16869   * @readonly
16870   * @returns The y-coordinate of the last vertex
16871   * @see {@link Polygon.lastX} For last X coordinate
16872   * @see {@link Polygon.points} For raw points array
16873   */
16874  get lastY() {
16875    return this.points[this.points.length - 1];
16876  }
16877  /**
16878   * Get the last X coordinate of the polygon.
16879   * @readonly
16880   * @deprecated since 8.11.0, use {@link Polygon.lastX} instead.
16881   */
16882  get x() {
16883    deprecation("8.11.0", "Polygon.lastX is deprecated, please use Polygon.lastX instead.");
16884    return this.points[this.points.length - 2];
16885  }
16886  /**
16887   * Get the last Y coordinate of the polygon.
16888   * @readonly
16889   * @deprecated since 8.11.0, use {@link Polygon.lastY} instead.
16890   */
16891  get y() {
16892    deprecation("8.11.0", "Polygon.y is deprecated, please use Polygon.lastY instead.");
16893    return this.points[this.points.length - 1];
16894  }
16895  /**
16896   * Get the first X coordinate of the polygon.
16897   * @example
16898   * ```ts
16899   * // Basic coordinate access
16900   * const polygon = new Polygon([0, 0, 100, 200, 300, 400]);
16901   * console.log(polygon.x); // 0
16902   * ```
16903   * @readonly
16904   * @returns The x-coordinate of the first vertex
16905   * @see {@link Polygon.startY} For first Y coordinate
16906   * @see {@link Polygon.points} For raw points array
16907   */
16908  get startX() {
16909    return this.points[0];
16910  }
16911  /**
16912   * Get the first Y coordinate of the polygon.
16913   * @example
16914   * ```ts
16915   * // Basic coordinate access
16916   * const polygon = new Polygon([0, 0, 100, 200, 300, 400]);
16917   * console.log(polygon.y); // 0
16918   * ```
16919   * @readonly
16920   * @returns The y-coordinate of the first vertex
16921   * @see {@link Polygon.startX} For first X coordinate
16922   * @see {@link Polygon.points} For raw points array
16923   */
16924  get startY() {
16925    return this.points[1];
16926  }
16927}
16928
16929const isCornerWithinStroke = (pX, pY, cornerX, cornerY, radius, strokeWidthInner, strokeWidthOuter) => {
16930  const dx = pX - cornerX;
16931  const dy = pY - cornerY;
16932  const distance = Math.sqrt(dx * dx + dy * dy);
16933  return distance >= radius - strokeWidthInner && distance <= radius + strokeWidthOuter;
16934};
16935class RoundedRectangle {
16936  /**
16937   * @param x - The X coordinate of the upper-left corner of the rounded rectangle
16938   * @param y - The Y coordinate of the upper-left corner of the rounded rectangle
16939   * @param width - The overall width of this rounded rectangle
16940   * @param height - The overall height of this rounded rectangle
16941   * @param radius - Controls the radius of the rounded corners
16942   */
16943  constructor(x = 0, y = 0, width = 0, height = 0, radius = 20) {
16944    /**
16945     * The type of the object, mainly used to avoid `instanceof` checks
16946     * @example
16947     * ```ts
16948     * // Check shape type
16949     * const shape = new RoundedRectangle(0, 0, 100, 100, 20);
16950     * console.log(shape.type); // 'roundedRectangle'
16951     *
16952     * // Use in type guards
16953     * if (shape.type === 'roundedRectangle') {
16954     *     console.log(shape.radius);
16955     * }
16956     * ```
16957     * @readonly
16958     * @default 'roundedRectangle'
16959     * @see {@link SHAPE_PRIMITIVE} For all shape types
16960     */
16961    this.type = "roundedRectangle";
16962    this.x = x;
16963    this.y = y;
16964    this.width = width;
16965    this.height = height;
16966    this.radius = radius;
16967  }
16968  /**
16969   * Returns the framing rectangle of the rounded rectangle as a Rectangle object
16970   * @example
16971   * ```ts
16972   * // Basic bounds calculation
16973   * const rect = new RoundedRectangle(100, 100, 200, 150, 20);
16974   * const bounds = rect.getBounds();
16975   * // bounds: x=100, y=100, width=200, height=150
16976   *
16977   * // Reuse existing rectangle
16978   * const out = new Rectangle();
16979   * rect.getBounds(out);
16980   * ```
16981   * @remarks
16982   * - Rectangle matches outer dimensions
16983   * - Ignores corner radius
16984   * @param out - Optional rectangle to store the result
16985   * @returns The framing rectangle
16986   * @see {@link Rectangle} For rectangle properties
16987   * @see {@link RoundedRectangle.contains} For checking if a point is inside
16988   */
16989  getBounds(out) {
16990    out || (out = new Rectangle());
16991    out.x = this.x;
16992    out.y = this.y;
16993    out.width = this.width;
16994    out.height = this.height;
16995    return out;
16996  }
16997  /**
16998   * Creates a clone of this Rounded Rectangle.
16999   * @example
17000   * ```ts
17001   * // Basic cloning
17002   * const original = new RoundedRectangle(100, 100, 200, 150, 20);
17003   * const copy = original.clone();
17004   *
17005   * // Clone and modify
17006   * const modified = original.clone();
17007   * modified.radius = 30;
17008   * modified.width *= 2;
17009   *
17010   * // Verify independence
17011   * console.log(original.radius);  // 20
17012   * console.log(modified.radius);  // 30
17013   * ```
17014   * @returns A copy of the rounded rectangle
17015   * @see {@link RoundedRectangle.copyFrom} For copying into existing rectangle
17016   * @see {@link RoundedRectangle.copyTo} For copying to another rectangle
17017   */
17018  clone() {
17019    return new RoundedRectangle(this.x, this.y, this.width, this.height, this.radius);
17020  }
17021  /**
17022   * Copies another rectangle to this one.
17023   * @example
17024   * ```ts
17025   * // Basic copying
17026   * const source = new RoundedRectangle(100, 100, 200, 150, 20);
17027   * const target = new RoundedRectangle();
17028   * target.copyFrom(source);
17029   *
17030   * // Chain with other operations
17031   * const rect = new RoundedRectangle()
17032   *     .copyFrom(source)
17033   *     .getBounds(rect);
17034   * ```
17035   * @param rectangle - The rectangle to copy from
17036   * @returns Returns itself
17037   * @see {@link RoundedRectangle.copyTo} For copying to another rectangle
17038   * @see {@link RoundedRectangle.clone} For creating new rectangle copy
17039   */
17040  copyFrom(rectangle) {
17041    this.x = rectangle.x;
17042    this.y = rectangle.y;
17043    this.width = rectangle.width;
17044    this.height = rectangle.height;
17045    return this;
17046  }
17047  /**
17048   * Copies this rectangle to another one.
17049   * @example
17050   * ```ts
17051   * // Basic copying
17052   * const source = new RoundedRectangle(100, 100, 200, 150, 20);
17053   * const target = new RoundedRectangle();
17054   * source.copyTo(target);
17055   *
17056   * // Chain with other operations
17057   * const result = source
17058   *     .copyTo(new RoundedRectangle())
17059   *     .getBounds();
17060   * ```
17061   * @param rectangle - The rectangle to copy to
17062   * @returns Returns given parameter
17063   * @see {@link RoundedRectangle.copyFrom} For copying from another rectangle
17064   * @see {@link RoundedRectangle.clone} For creating new rectangle copy
17065   */
17066  copyTo(rectangle) {
17067    rectangle.copyFrom(this);
17068    return rectangle;
17069  }
17070  /**
17071   * Checks whether the x and y coordinates given are contained within this Rounded Rectangle
17072   * @example
17073   * ```ts
17074   * // Basic containment check
17075   * const rect = new RoundedRectangle(100, 100, 200, 150, 20);
17076   * const isInside = rect.contains(150, 125); // true
17077   * // Check corner radius
17078   * const corner = rect.contains(100, 100); // false if within corner curve
17079   * ```
17080   * @remarks
17081   * - Returns false if width/height is 0 or negative
17082   * - Handles rounded corners with radius check
17083   * @param x - The X coordinate of the point to test
17084   * @param y - The Y coordinate of the point to test
17085   * @returns Whether the x/y coordinates are within this Rounded Rectangle
17086   * @see {@link RoundedRectangle.strokeContains} For checking stroke intersection
17087   * @see {@link RoundedRectangle.getBounds} For getting containing rectangle
17088   */
17089  contains(x, y) {
17090    if (this.width <= 0 || this.height <= 0) {
17091      return false;
17092    }
17093    if (x >= this.x && x <= this.x + this.width) {
17094      if (y >= this.y && y <= this.y + this.height) {
17095        const radius = Math.max(0, Math.min(this.radius, Math.min(this.width, this.height) / 2));
17096        if (y >= this.y + radius && y <= this.y + this.height - radius || x >= this.x + radius && x <= this.x + this.width - radius) {
17097          return true;
17098        }
17099        let dx = x - (this.x + radius);
17100        let dy = y - (this.y + radius);
17101        const radius2 = radius * radius;
17102        if (dx * dx + dy * dy <= radius2) {
17103          return true;
17104        }
17105        dx = x - (this.x + this.width - radius);
17106        if (dx * dx + dy * dy <= radius2) {
17107          return true;
17108        }
17109        dy = y - (this.y + this.height - radius);
17110        if (dx * dx + dy * dy <= radius2) {
17111          return true;
17112        }
17113        dx = x - (this.x + radius);
17114        if (dx * dx + dy * dy <= radius2) {
17115          return true;
17116        }
17117      }
17118    }
17119    return false;
17120  }
17121  /**
17122   * Checks whether the x and y coordinates given are contained within this rectangle including the stroke.
17123   * @example
17124   * ```ts
17125   * // Basic stroke check
17126   * const rect = new RoundedRectangle(100, 100, 200, 150, 20);
17127   * const isOnStroke = rect.strokeContains(150, 100, 4); // 4px line width
17128   *
17129   * // Check with different alignments
17130   * const innerStroke = rect.strokeContains(150, 100, 4, 1);   // Inside
17131   * const centerStroke = rect.strokeContains(150, 100, 4, 0.5); // Centered
17132   * const outerStroke = rect.strokeContains(150, 100, 4, 0);   // Outside
17133   * ```
17134   * @param pX - The X coordinate of the point to test
17135   * @param pY - The Y coordinate of the point to test
17136   * @param strokeWidth - The width of the line to check
17137   * @param alignment - The alignment of the stroke (1 = inner, 
171370.5 = centered, 0 = outer)
17138   * @returns Whether the x/y coordinates are within this rectangle's stroke
17139   * @see {@link RoundedRectangle.contains} For checking fill containment
17140   * @see {@link RoundedRectangle.getBounds} For getting stroke bounds
17141   */
17142  strokeContains(pX, pY, strokeWidth, alignment = 0.5) {
17143    const { x, y, width, height, radius } = this;
17144    const strokeWidthOuter = strokeWidth * (1 - alignment);
17145    const strokeWidthInner = strokeWidth - strokeWidthOuter;
17146    const innerX = x + radius;
17147    const innerY = y + radius;
17148    const innerWidth = width - radius * 2;
17149    const innerHeight = height - radius * 2;
17150    const rightBound = x + width;
17151    const bottomBound = y + height;
17152    if ((pX >= x - strokeWidthOuter && pX <= x + strokeWidthInner || pX >= rightBound - strokeWidthInner && pX <= rightBound + strokeWidthOuter) && pY >= innerY && pY <= innerY + innerHeight) {
17153      return true;
17154    }
17155    if ((pY >= y - strokeWidthOuter && pY <= y + strokeWidthInner || pY >= bottomBound - strokeWidthInner && pY <= bottomBound + strokeWidthOuter) && pX >= innerX && pX <= innerX + innerWidth) {
17156      return true;
17157    }
17158    return (
17159      // Top-left
17160      pX < innerX && pY < innerY && isCornerWithinStroke(
17161        pX,
17162        pY,
17163        innerX,
17164        innerY,
17165        radius,
17166        strokeWidthInner,
17167        strokeWidthOuter
17168      ) || pX > rightBound - radius && pY < innerY && isCornerWithinStroke(
17169        pX,
17170        pY,
17171        rightBound - radius,
17172        innerY,
17173        radius,
17174        strokeWidthInner,
17175        strokeWidthOuter
17176      ) || pX > rightBound - radius && pY > bottomBound - radius && isCornerWithinStroke(
17177        pX,
17178        pY,
17179        rightBound - radius,
17180        bottomBound - radius,
17181        radius,
17182        strokeWidthInner,
17183        strokeWidthOuter
17184      ) || pX < innerX && pY > bottomBound - radius && isCornerWithinStroke(
17185        pX,
17186        pY,
17187        innerX,
17188        bottomBound - radius,
17189        radius,
17190        strokeWidthInner,
17191        strokeWidthOuter
17192      )
17193    );
17194  }
17195  toString() {
17196    return `[pixi.js/math:RoundedRectangle x=${this.x} y=${this.y}width=${this.width} height=${this.height} radius=${this.radius}]`;
17197  }
17198}
17199
17200const cachedGroups = {};
17201function getTextureBatchBindGroup(textures, size, maxTextures) {
17202  let uid = 2166136261;
17203  for (let i = 0; i < size; i++) {
17204    uid ^= textures[i].uid;
17205    uid = Math.imul(uid, 16777619);
17206    uid >>>= 0;
17207  }
17208  return cachedGroups[uid] || generateTextureBatchBindGroup(textures, size, uid, maxTextures);
17209}
17210function generateTextureBatchBindGroup(textures, size, key, maxTextures) {
17211  const bindGroupResources = {};
17212  let bindIndex = 0;
17213  for (let i = 0; i < maxTextures; i++) {
17214    const texture = i < size ? textures[i] : Texture.EMPTY.source;
17215    bindGroupResources[bindIndex++] = texture.source;
17216    bindGroupResources[bindIndex++] = texture.style;
17217  }
17218  const bindGroup = new BindGroup(bindGroupResources);
17219  cachedGroups[key] = bindGroup;
17220  return bindGroup;
17221}
17222
17223class ViewableBuffer {
17224  constructor(sizeOrBuffer) {
17225    if (typeof sizeOrBuffer === "number") {
17226      this.rawBinaryData = new ArrayBuffer(sizeOrBuffer);
17227    } else if (sizeOrBuffer instanceof Uint8Array) {
17228      this.rawBinaryData = sizeOrBuffer.buffer;
17229    } else {
17230      this.rawBinaryData = sizeOrBuffer;
17231    }
17232    this.uint32View = new Uint32Array(this.rawBinaryData);
17233    this.float32View = new Float32Array(this.rawBinaryData);
17234    this.size = this.rawBinaryData.byteLength;
17235  }
17236  /** View on the raw binary data as a `Int8Array`. */
17237  get int8View() {
17238    if (!this._int8View) {
17239      this._int8View = new Int8Array(this.rawBinaryData);
17240    }
17241    return this._int8View;
17242  }
17243  /** View on the raw binary data as a `Uint8Array`. */
17244  get uint8View() {
17245    if (!this._uint8View) {
17246      this._uint8View = new Uint8Array(this.rawBinaryData);
17247    }
17248    return this._uint8View;
17249  }
17250  /**  View on the raw binary data as a `Int16Array`. */
17251  get int16View() {
17252    if (!this._int16View) {
17253      this._int16View = new Int16Array(this.rawBinaryData);
17254    }
17255    return this._int16View;
17256  }
17257  /** View on the raw binary data as a `Int32Array`. */
17258  get int32View() {
17259    if (!this._int32View) {
17260      this._int32View = new Int32Array(this.rawBinaryData);
17261    }
17262    return this._int32View;
17263  }
17264  /** View on the raw binary data as a `Float64Array`. */
17265  get float64View() {
17266    if (!this._float64Array) {
17267      this._float64Array = new Float64Array(this.rawBinaryData);
17268    }
17269    return this._float64Array;
17270  }
17271  /** View on the raw binary data as a `BigUint64Array`. */
17272  get bigUint64View() {
17273    if (!this._bigUint64Array) {
17274      this._bigUint64Array = new BigUint64Array(this.rawBinaryData);
17275    }
17276    return this._bigUint64Array;
17277  }
17278  /**
17279   * Returns the view of the given type.
17280   * @param type - One of `int8`, `uint8`, `int16`,
17281   *    `uint16`, `int32`, `uint32`, and `float32`.
17282   * @returns - typed array of given type
17283   */
17284  view(type) {
17285    return this[`${type}View`];
17286  }
17287  /** Destroys all buffer references. Do not use after calling this. */
17288  destroy() {
17289    this.rawBinaryData = null;
17290    this.uint32View = null;
17291    this.float32View = null;
17292    this.uint16View = null;
17293    this._int8View = null;
17294    this._uint8View = null;
17295    this._int16View = null;
17296    this._int32View = null;
17297    this._float64Array = null;
17298    this._bigUint64Array = null;
17299  }
17300  /**
17301   * Returns the size of the given type in bytes.
17302   * @param type - One of `int8`, `uint8`, `int16`,
17303   *   `uint16`, `int32`, `uint32`, and `float32`.
17304   * @returns - size of the type in bytes
17305   */
17306  static sizeOf(type) {
17307    switch (type) {
17308      case "int8":
17309      case "uint8":
17310        return 1;
17311      case "int16":
17312      case "uint16":
17313        return 2;
17314      case "int32":
17315      case "uint32":
17316      case "float32":
17317        return 4;
17318      default:
17319        throw new Error(`${type} isn't a valid view type`);
17320    }
17321  }
17322}
17323
17324function fastCopy(sourceBuffer, destinationBuffer) {
17325  const lengthDouble = sourceBuffer.byteLength / 8 | 0;
17326  const sourceFloat64View = new Float64Array(sourceBuffer, 0, lengthDouble);
17327  const destinationFloat64View = new Float64Array(destinationBuffer, 0, lengthDouble);
17328  destinationFloat64View.set(sourceFloat64View);
17329  const remainingBytes = sourceBuffer.byteLength - lengthDouble * 8;
17330  if (remainingBytes > 0) {
17331    const sourceUint8View = new Uint8Array(sourceBuffer, lengthDouble * 8, remainingBytes);
17332    const destinationUint8View = new Uint8Array(destinationBuffer, lengthDouble * 8, remainingBytes);
17333    destinationUint8View.set(sourceUint8View);
17334  }
17335}
17336
17337const BLEND_TO_NPM = {
17338  normal: "normal-npm",
17339  add: "add-npm",
17340  screen: "screen-npm"
17341};
17342var STENCIL_MODES = /* @__PURE__ */ ((STENCIL_MODES2) => {
17343  STENCIL_MODES2[STENCIL_MODES2["DISABLED"] = 0] = "DISABLED";
17344  STENCIL_MODES2[STENCIL_MODES2["RENDERING_MASK_ADD"] = 1] = "RENDERING_MASK_ADD";
17345  STENCIL_MODES2[STENCIL_MODES2["MASK_ACTIVE"] = 2] = "MASK_ACTIVE";
17346  STENCIL_MODES2[STENCIL_MODES2["INVERSE_MASK_ACTIVE"] = 3] = "INVERSE_MASK_ACTIVE";
17347  STENCIL_MODES2[STENCIL_MODES2["RENDERING_MASK_REMOVE"] = 4] = "RENDERING_MASK_REMOVE";
17348  STENCIL_MODES2[STENCIL_MODES2["NONE"] = 5] = "NONE";
17349  return STENCIL_MODES2;
17350})(STENCIL_MODES || {});
17351
17352function getAdjustedBlendModeBlend(blendMode, textureSource) {
17353  if (textureSource.alphaMode === "no-premultiply-alpha") {
17354    return BLEND_TO_NPM[blendMode] || blendMode;
17355  }
17356  return blendMode;
17357}
17358
17359const fragTemplate = [
17360  "precision mediump float;",
17361  "void main(void){",
17362  "float test = 0.1;",
17363  "%forloop%",
17364  "gl_FragColor = vec4(0.0);",
17365  "}"
17366].join("\n");
17367function generateIfTestSrc(maxIfs) {
17368  let src = "";
17369  for (let i = 0; i < maxIfs; ++i) {
17370    if (i > 0) {
17371      src += "\nelse ";
17372    }
17373    if (i < maxIfs - 1) {
17374      src += `if(test == ${i}.0){}`;
17375    }
17376  }
17377  return src;
17378}
17379function checkMaxIfStatementsInShader(maxIfs, gl) {
17380  if (maxIfs === 0) {
17381    throw new Error("Invalid value of `0` passed to `checkMaxIfStatementsInShader`");
17382  }
17383  const shader = gl.createShader(gl.FRAGMENT_SHADER);
17384  try {
17385    while (true) {
17386      const fragmentSrc = fragTemplate.replace(/%forloop%/gi, generateIfTestSrc(maxIfs));
17387      gl.shaderSource(shader, fragmentSrc);
17388      gl.compileShader(shader);
17389      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
17390        maxIfs = maxIfs / 2 | 0;
17391      } else {
17392        break;
17393      }
17394    }
17395  } finally {
17396    gl.deleteShader(shader);
17397  }
17398  return maxIfs;
17399}
17400
17401let maxTexturesPerBatchCache = null;
17402function getMaxTexturesPerBatch() {
17403  if (maxTexturesPerBatchCache)
17404    return maxTexturesPerBatchCache;
17405  const gl = getTestContext();
17406  maxTexturesPerBatchCache = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
17407  maxTexturesPerBatchCache = checkMaxIfStatementsInShader(
17408    maxTexturesPerBatchCache,
17409    gl
17410  );
17411  gl.getExtension("WEBGL_lose_context")?.loseContext();
17412  return maxTexturesPerBatchCache;
17413}
17414
17415class BatchTextureArray {
17416  constructor() {
17417    /** Respective locations for textures. */
17418    this.ids = /* @__PURE__ */ Object.create(null);
17419    this.textures = [];
17420    this.count = 0;
17421  }
17422  /** Clear the textures and their locations. */
17423  clear() {
17424    for (let i = 0; i < this.count; i++) {
17425      const t = this.textures[i];
17426      this.textures[i] = null;
17427      this.ids[t.uid] = null;
17428    }
17429    this.count = 0;
17430  }
17431}
17432
17433class Batch {
17434  constructor() {
17435    this.renderPipeId = "batch";
17436    this.action = "startBatch";
17437    // TODO - eventually this could be useful for flagging batches as dirty and then only rebuilding those ones
17438    // public elementStart = 0;
17439    // public elementSize = 0;
17440    // for drawing..
17441    this.start = 0;
17442    this.size = 0;
17443    this.textures = new BatchTextureArray();
17444    this.blendMode = "normal";
17445    this.topology = "triangle-strip";
17446    this.canBundle = true;
17447  }
17448  destroy() {
17449    this.textures = null;
17450    this.gpuBindGroup = null;
17451    this.bindGroup = null;
17452    this.batcher = null;
17453  }
17454}
17455const batchPool = [];
17456let batchPoolIndex = 0;
17457GlobalResourceRegistry.register({
17458  clear: () => {
17459    if (batchPool.length > 0) {
17460      for (const item of batchPool) {
17461        if (item)
17462          item.destroy();
17463      }
17464    }
17465    batchPool.length = 0;
17466    batchPoolIndex = 0;
17467  }
17468});
17469function getBatchFromPool() {
17470  return batchPoolIndex > 0 ? batchPool[--batchPoolIndex] : new Batch();
17471}
17472function returnBatchToPool(batch) {
17473  batchPool[batchPoolIndex++] = batch;
17474}
17475let BATCH_TICK = 0;
17476const _Batcher = class _Batcher {
17477  constructor(options) {
17478    /** unique id for this batcher */
17479    this.uid = uid("batcher");
17480    /** Indicates whether the batch data has been modified and needs updating. */
17481    this.dirty = true;
17482    /** The current index of the batch being processed. */
17483    this.batchIndex = 0;
17484    /** An array of all batches created during the current rendering process. */
17485    this.batches = [];
17486    this._elements = [];
17487    options = { ..._Batcher.defaultOptions, ...options };
17488    if (!options.maxTextures) {
17489      deprecation("v8.8.0", "maxTextures is a required option for Batcher now, please pass it in the options");
17490      options.maxTextures = getMaxTexturesPerBatch();
17491    }
17492    const { maxTextures, attributesInitialSize, indicesInitialSize } = options;
17493    this.attributeBuffer = new ViewableBuffer(attributesInitialSize * 4);
17494    this.indexBuffer = new Uint16Array(indicesInitialSize);
17495    this.maxTextures = maxTextures;
17496  }
17497  begin() {
17498    this.elementSize = 0;
17499    this.elementStart = 0;
17500    this.indexSize = 0;
17501    this.attributeSize = 0;
17502    for (let i = 0; i < this.batchIndex; i++) {
17503      returnBatchToPool(this.batches[i]);
17504    }
17505    this.batchIndex = 0;
17506    this._batchIndexStart = 0;
17507    this._batchIndexSize = 0;
17508    this.dirty = true;
17509  }
17510  add(batchableObject) {
17511    this._elements[this.elementSize++] = batchableObject;
17512    batchableObject._indexStart = this.indexSize;
17513    batchableObject._attributeStart = this.attributeSize;
17514    batchableObject._batcher = this;
17515    this.indexSize += batchableObject.indexSize;
17516    this.attributeSize += batchableObject.attributeSize * this.vertexSize;
17517  }
17518  checkAndUpdateTexture(batchableObject, texture) {
17519    const textureId = batchableObject._batch.textures.ids[texture._source.uid];
17520    if (!textureId && textureId !== 0)
17521      return false;
17522    batchableObject._textureId = textureId;
17523    batchableObject.texture = texture;
17524    return true;
17525  }
17526  updateElement(batchableObject) {
17527    this.dirty = true;
17528    const attributeBuffer = this.attributeBuffer;
17529    if (batchableObject.packAsQuad) {
17530      this.packQuadAttributes(
17531        batchableObject,
17532        attributeBuffer.float32View,
17533        attributeBuffer.uint32View,
17534        batchableObject._attributeStart,
17535        batchableObject._textureId
17536      );
17537    } else {
17538      this.packAttributes(
17539        batchableObject,
17540        attributeBuffer.float32View,
17541        attributeBuffer.uint32View,
17542        batchableObject._attributeStart,
17543        batchableObject._textureId
17544      );
17545    }
17546  }
17547  /**
17548   * breaks the batcher. This happens when a batch gets too big,
17549   * or we need to switch to a different type of rendering (a filter for example)
17550   * @param instructionSet
17551   */
17552  break(instructionSet) {
17553    const elements = this._elements;
17554    if (!elements[this.elementStart])
17555      return;
17556    let batch = getBatchFromPool();
17557    let textureBatch = batch.textures;
17558    textureBatch.clear();
17559    const firstElement = elements[this.elementStart];
17560    let blendMode = getAdjustedBlendModeBlend(firstElement.blendMode, firstElement.texture._source);
17561    let topology = firstElement.topology;
17562    if (this.attributeSize * 4 > this.attributeBuffer.size) {
17563      this._resizeAttributeBuffer(this.attributeSize * 4);
17564    }
17565    if (this.indexSize > this.indexBuffer.length) {
17566      this._resizeIndexBuffer(this.indexSize);
17567    }
17568    const f32 = this.attributeBuffer.float32View;
17569    const u32 = this.attributeBuffer.uint32View;
17570    const indexBuffer = this.indexBuffer;
17571    let size = this._batchIndexSize;
17572    let start = this._batchIndexStart;
17573    let action = "startBatch";
17574    const maxTextures = this.maxTextures;
17575    for (let i = this.elementStart; i < this.elementSize; ++i) {
17576      const element = elements[i];
17577      elements[i] = null;
17578      const texture = element.texture;
17579      const source = texture._source;
17580      const adjustedBlendMode = getAdjustedBlendModeBlend(element.blendMode, source);
17581      const breakRequired = blendMode !== adjustedBlendMode || topology !== element.topology;
17582      if (source._batchTick === BATCH_TICK && !breakRequired) {
17583        element._textureId = source._textureBindLocation;
17584        size += element.indexSize;
17585        if (element.packAsQuad) {
17586          this.packQuadAttributes(
17587            element,
17588            f32,
17589            u32,
17590            element._attributeStart,
17591            element._textureId
17592          );
17593          this.packQuadIndex(
17594            indexBuffer,
17595            element._indexStart,
17596            element._attributeStart / this.vertexSize
17597          );
17598        } else {
17599          this.packAttributes(
17600            element,
17601            f32,
17602            u32,
17603            element._attributeStart,
17604            element._textureId
17605          );
17606          this.packIndex(
17607            element,
17608            indexBuffer,
17609            element._indexStart,
17610            element._attributeStart / this.vertexSize
17611          );
17612        }
17613        element._batch = batch;
17614        continue;
17615      }
17616      source._batchTick = BATCH_TICK;
17617      if (textureBatch.count >= maxTextures || breakRequired) {
17618        this._finishBatch(
17619          batch,
17620          start,
17621          size - start,
17622          textureBatch,
17623          blendMode,
17624          topology,
17625          instructionSet,
17626          action
17627        );
17628        action = "renderBatch";
17629        start = size;
17630        blendMode = adjustedBlendMode;
17631        topology = element.topology;
17632        batch = getBatchFromPool();
17633        textureBatch = batch.textures;
17634        textureBatch.clear();
17635        ++BATCH_TICK;
17636      }
17637      element._textureId = source._textureBindLocation = textureBatch.count;
17638      textureBatch.ids[source.uid] = textureBatch.count;
17639      textureBatch.textures[textureBatch.count++] = source;
17640      element._batch = batch;
17641      size += element.indexSize;
17642      if (element.packAsQuad) {
17643        this.packQuadAttributes(
17644          element,
17645          f32,
17646          u32,
17647          element._attributeStart,
17648          element._textureId
17649        );
17650        this.packQuadIndex(
17651          indexBuffer,
17652          element._indexStart,
17653          element._attributeStart / this.vertexSize
17654        );
17655      } else {
17656        this.packAttributes(
17657          element,
17658          f32,
17659          u32,
17660          element._attributeStart,
17661          element._textureId
17662        );
17663        this.packIndex(
17664          element,
17665          indexBuffer,
17666          element._indexStart,
17667          element._attributeStart / this.vertexSize
17668        );
17669      }
17670    }
17671    if (textureBatch.count > 0) {
17672      this._finishBatch(
17673        batch,
17674        start,
17675        size - start,
17676        textureBatch,
17677        blendMode,
17678        topology,
17679        instructionSet,
17680        action
17681      );
17682      start = size;
17683      ++BATCH_TICK;
17684    }
17685    this.elementStart = this.elementSize;
17686    this._batchIndexStart = start;
17687    this._batchIndexSize = size;
17688  }
17689  _finishBatch(batch, indexStart, indexSize, textureBatch, blendMode, topology, instructionSet, action) {
17690    batch.gpuBindGroup = null;
17691    batch.bindGroup = null;
17692    batch.action = action;
17693    batch.batcher = this;
17694    batch.textures = textureBatch;
17695    batch.blendMode = blendMode;
17696    batch.topology = topology;
17697    batch.start = indexStart;
17698    batch.size = indexSize;
17699    ++BATCH_TICK;
17700    this.batches[this.batchIndex++] = batch;
17701    instructionSet.add(batch);
17702  }
17703  finish(instructionSet) {
17704    this.break(instructionSet);
17705  }
17706  /**
17707   * Resizes the attribute buffer to the given size (1 = 1 float32)
17708   * @param size - the size in vertices to ensure (not bytes!)
17709   */
17710  ensureAttributeBuffer(size) {
17711    if (size * 4 <= this.attributeBuffer.size)
17712      return;
17713    this._resizeAttributeBuffer(size * 4);
17714  }
17715  /**
17716   * Resizes the index buffer to the given size (1 = 1 float32)
17717   * @param size - the size in vertices to ensure (not bytes!)
17718   */
17719  ensureIndexBuffer(size) {
17720    if (size <= this.indexBuffer.length)
17721      return;
17722    this._resizeIndexBuffer(size);
17723  }
17724  _resizeAttributeBuffer(size) {
17725    const newSize = Math.max(size, this.attributeBuffer.size * 2);
17726    const newArrayBuffer = new ViewableBuffer(newSize);
17727    fastCopy(this.attributeBuffer.rawBinaryData, newArrayBuffer.rawBinaryData);
17728    this.attributeBuffer = newArrayBuffer;
17729  }
17730  _resizeIndexBuffer(size) {
17731    const indexBuffer = this.indexBuffer;
17732    let newSize = Math.max(size, indexBuffer.length * 1.5);
17733    newSize += newSize % 2;
17734    const newIndexBuffer = newSize > 65535 ? new Uint32Array(newSize) : new Uint16Array(newSize);
17735    if (newIndexBuffer.BYTES_PER_ELEMENT !== indexBuffer.BYTES_PER_ELEMENT) {
17736      for (let i = 0; i < indexBuffer.length; i++) {
17737        newIndexBuffer[i] = indexBuffer[i];
17738      }
17739    } else {
17740      fastCopy(indexBuffer.buffer, newIndexBuffer.buffer);
17741    }
17742    this.indexBuffer = newIndexBuffer;
17743  }
17744  packQuadIndex(indexBuffer, index, indicesOffset) {
17745    indexBuffer[index] = indicesOffset + 0;
17746    indexBuffer[index + 1] = indicesOffset + 1;
17747    indexBuffer[index + 2] = indicesOffset + 2;
17748    indexBuffer[index + 3] = indicesOffset + 0;
17749    indexBuffer[index + 4] = indicesOffset + 2;
17750    indexBuffer[index + 5] = indicesOffset + 3;
17751  }
17752  packIndex(element, indexBuffer, index, indicesOffset) {
17753    const indices = element.indices;
17754    const size = element.indexSize;
17755    const indexOffset = element.indexOffset;
17756    const attributeOffset = element.attributeOffset;
17757    for (let i = 0; i < size; i++) {
17758      indexBuffer[index++] = indicesOffset + indices[i + indexOffset] - attributeOffset;
17759    }
17760  }
17761  /**
17762   * Destroys the batch and its resources.
17763   * @param options - destruction options
17764   * @param options.shader - whether to destroy the associated shader
17765   */
17766  destroy(options = {}) {
17767    if (this.batches === null)
17768      return;
17769    for (let i = 0; i < this.batchIndex; i++) {
17770      returnBatchToPool(this.batches[i]);
17771    }
17772    this.batches = null;
17773    this.geometry.destroy(true);
17774    this.geometry = null;
17775    if (options.shader) {
17776      this.shader?.destroy();
17777      this.shader = null;
17778    }
17779    for (let i = 0; i < this._elements.length; i++) {
17780      if (this._elements[i])
17781        this._elements[i]._batch = null;
17782    }
17783    this._elements = null;
17784    this.indexBuffer = null;
17785    this.attributeBuffer.destroy();
17786    this.attributeBuffer = null;
17787  }
17788};
17789_Batcher.defaultOptions = {
17790  maxTextures: null,
17791  attributesInitialSize: 4,
17792  indicesInitialSize: 6
17793};
17794let Batcher = _Batcher;
17795
17796var BufferUsage = /* @__PURE__ */ ((BufferUsage2) => {
17797  BufferUsage2[BufferUsage2["MAP_READ"] = 1] = "MAP_READ";
17798  BufferUsage2[BufferUsage2["MAP_WRITE"] = 2] = "MAP_WRITE";
17799  BufferUsage2[BufferUsage2["COPY_SRC"] = 4] = "COPY_SRC";
17800  BufferUsage2[BufferUsage2["COPY_DST"] = 8] = "COPY_DST";
17801  BufferUsage2[BufferUsage2["INDEX"] = 16] = "INDEX";
17802  BufferUsage2[BufferUsage2["VERTEX"] = 32] = "VERTEX";
17803  BufferUsage2[BufferUsage2["UNIFORM"] = 64] = "UNIFORM";
17804  BufferUsage2[BufferUsage2["STORAGE"] = 128] = "STORAGE";
17805  BufferUsage2[BufferUsage2["INDIRECT"] = 256] = "INDIRECT";
17806  BufferUsage2[BufferUsage2["QUERY_RESOLVE"] = 512] = "QUERY_RESOLVE";
17807  BufferUsage2[BufferUsage2["STATIC"] = 1024] = "STATIC";
17808  return BufferUsage2;
17809})(BufferUsage || {});
17810
17811class Buffer extends EventEmitter {
17812  /**
17813   * Creates a new Buffer with the given options
17814   * @param options - the options for the buffer
17815   */
17816  constructor(options) {
17817    let { data, size } = options;
17818    const { usage, label, shrinkToFit } = options;
17819    super();
17820    /**
17821     * emits when the underlying buffer has changed shape (i.e. resized)
17822     * letting the renderer know that it needs to discard the old buffer on the GPU and create a new one
17823     * @event change
17824     */
17825    /**
17826     * emits when the underlying buffer data has been updated. letting the renderer know
17827     * that it needs to update the buffer on the GPU
17828     * @event update
17829     */
17830    /**
17831     * emits when the buffer is destroyed. letting the renderer know that it needs to destroy the buffer on the GPU
17832     * @event destroy
17833     */
17834    /** @internal */
17835    this._gpuData = /* @__PURE__ */ Object.create(null);
17836    /** @internal */
17837    this._gcLastUsed = -1;
17838    /** If set to true, the buffer will be garbage collected automatically when it is not used. */
17839    this.autoGarbageCollect = true;
17840    /** a unique id for this uniform group used through the renderer */
17841    this.uid = uid("buffer");
17842    /**
17843     * a resource type, used to identify how to handle it when its in a bind group / shader resource
17844     * @internal
17845     */
17846    this._resourceType = "buffer";
17847    /**
17848     * the resource id used internally by the renderer to build bind group keys
17849     * @internal
17850     */
17851    this._resourceId = uid("resource");
17852    /**
17853     * used internally to know if a uniform group was used in the last render pass
17854     * @internal
17855     */
17856    this._touched = 0;
17857    /** @internal */
17858    this._updateID = 1;
17859    this._dataInt32 = null;
17860    /**
17861     * should the GPU buffer be shrunk when the data becomes smaller?
17862     * changing this will cause the buffer to be destroyed and a new one created on the GPU
17863     * this can be expensive, especially if the buffer is already big enough!
17864     * setting this to false will prevent the buffer from being shrunk. This will yield better performance
17865     * if you are constantly setting data that is changing size often.
17866     * @default true
17867     */
17868    this.shrinkToFit = true;
17869    /**
17870     * Has the buffer been destroyed?
17871     * @readonly
17872     */
17873    this.destroyed = false;
17874    if (data instanceof Array) {
17875      data = new Float32Array(data);
17876    }
17877    this._data = data;
17878    size ?? (size = data?.byteLength);
17879    const mappedAtCreation = !!data;
17880    this.descriptor = {
17881      size,
17882      usage,
17883      mappedAtCreation,
17884      label
17885    };
17886    this.shrinkToFit = shrinkToFit ?? true;
17887  }
17888  /** the data in the buffer */
17889  get data() {
17890    return this._data;
17891  }
17892  set data(value) {
17893    this.setDataWithSize(value, value.length, true);
17894  }
17895  get dataInt32() {
17896    if (!this._dataInt32) {
17897      this._dataInt32 = new Int32Array(this.data.buffer);
17898    }
17899    return this._dataInt32;
17900  }
17901  /** whether the buffer is static or not */
17902  get static() {
17903    return !!(this.descriptor.usage & BufferUsage.STATIC);
17904  }
17905  set static(value) {
17906    if (value) {
17907      this.descriptor.usage |= BufferUsage.STATIC;
17908    } else {
17909      this.descriptor.usage &= ~BufferUsage.STATIC;
17910    }
17911  }
17912  /**
17913   * Sets the data in the buffer to the given value. This will immediately update the buffer on the GPU.
17914   * If you only want to update a subset of the buffer, you can pass in the size of the data.
17915   * @param value - the data to set
17916   * @param size - the size of the data in bytes
17917   * @param syncGPU - should the buffer be updated on the GPU immediately?
17918   */
17919  setDataWithSize(value, size, syncGPU) {
17920    this._updateID++;
17921    this._updateSize = size * value.BYTES_PER_ELEMENT;
17922    if (this._data === value) {
17923      if (syncGPU)
17924        this.emit("update", this);
17925      return;
17926    }
17927    const oldData = this._data;
17928    this._data = value;
17929    this._dataInt32 = null;
17930    if (!oldData || oldData.length !== value.length) {
17931      if (!this.shrinkToFit && oldData && value.byteLength < oldData.byteLength) {
17932        if (syncGPU)
17933          this.emit("update", this);
17934      } else {
17935        this.descriptor.size = value.byteLength;
17936        this._resourceId = uid("resource");
17937        this.emit("change", this);
17938      }
17939      return;
17940    }
17941    if (syncGPU)
17942      this.emit("update", this);
17943  }
17944  /**
17945   * updates the buffer on the GPU to reflect the data in the buffer.
17946   * By default it will update the entire buffer. If you only want to update a subset of the buffer,
17947   * you can pass in the size of the buffer to update.
17948   * @param sizeInBytes - the new size of the buffer in bytes
17949   */
17950  update(sizeInBytes) {
17951    this._updateSize = sizeInBytes ?? this._updateSize;
17952    this._updateID++;
17953    this.emit("update", this);
17954  }
17955  /** Unloads the buffer from the GPU */
17956  unload() {
17957    this.emit("unload", this);
17958    for (const key in this._gpuData) {
17959      this._gpuData[key]?.destroy();
17960    }
17961    this._gpuData = /* @__PURE__ */ Object.create(null);
17962  }
17963  /** Destroys the buffer */
17964  destroy() {
17965    this.destroyed = true;
17966    this.unload();
17967    this.emit("destroy", this);
17968    this.emit("change", this);
17969    this._data = null;
17970    this.descriptor = null;
17971    this.removeAllListeners();
17972  }
17973}
17974
17975function ensureIsBuffer(buffer, index) {
17976  if (!(buffer instanceof Buffer)) {
17977    let usage = index ? BufferUsage.INDEX : BufferUsage.VERTEX;
17978    if (buffer instanceof Array) {
17979      if (index) {
17980        buffer = new Uint32Array(buffer);
17981        usage = BufferUsage.INDEX | BufferUsage.COPY_DST;
17982      } else {
17983        buffer = new Float32Array(buffer);
17984        usage = BufferUsage.VERTEX | BufferUsage.COPY_DST;
17985      }
17986    }
17987    buffer = new Buffer({
17988      data: buffer,
17989      label: index ? "index-mesh-buffer" : "vertex-mesh-buffer",
17990      usage
17991    });
17992  }
17993  return buffer;
17994}
17995
17996function getGeometryBounds(geometry, attributeId, bounds) {
17997  const attribute = geometry.getAttribute(attributeId);
17998  if (!attribute) {
17999    bounds.minX = 0;
18000    bounds.minY = 0;
18001    bounds.maxX = 0;
18002    bounds.maxY = 0;
18003    return bounds;
18004  }
18005  const data = attribute.buffer.data;
18006  let minX = Infinity;
18007  let minY = Infinity;
18008  let maxX = -Infinity;
18009  let maxY = -Infinity;
18010  const byteSize = data.BYTES_PER_ELEMENT;
18011  const offset = (attribute.offset || 0) / byteSize;
18012  const stride = (attribute.stride || 2 * 4) / byteSize;
18013  for (let i = offset; i < data.length; i += stride) {
18014    const x = data[i];
18015    const y = data[i + 1];
18016    if (x > maxX)
18017      maxX = x;
18018    if (y > maxY)
18019      maxY = y;
18020    if (x < minX)
18021      minX = x;
18022    if (y < minY)
18023      minY = y;
18024  }
18025  bounds.minX = minX;
18026  bounds.minY = minY;
18027  bounds.maxX = maxX;
18028  bounds.maxY = maxY;
18029  return bounds;
18030}
18031
18032function ensureIsAttribute(attribute) {
18033  if (attribute instanceof Buffer || Array.isArray(attribute) || attribute.BYTES_PER_ELEMENT) {
18034    attribute = {
18035      buffer: attribute
18036    };
18037  }
18038  attribute.buffer = ensureIsBuffer(attribute.buffer, false);
18039  return attribute;
18040}
18041class Geometry extends EventEmitter {
18042  /**
18043   * Create a new instance of a geometry
18044   * @param options - The options for the geometry.
18045   */
18046  constructor(options = {}) {
18047    super();
18048    /** @internal */
18049    this._gpuData = /* @__PURE__ */ Object.create(null);
18050    /** If set to true, the resource will be garbage collected automatically when it is not used. */
18051    this.autoGarbageCollect = true;
18052    /** @internal */
18053    this._gcLastUsed = -1;
18054    /** The unique id of the geometry. */
18055    this.uid = uid("geometry");
18056    /**
18057     * the layout key will be generated by WebGPU all geometries that have the same structure
18058     * will have the same layout key. This is used to cache the pipeline layout
18059     * @internal
18060     */
18061    this._layoutKey = 0;
18062    /** the instance count of the geometry to draw */
18063    this.instanceCount = 1;
18064    this._bounds = new Bounds();
18065    this._boundsDirty = true;
18066    const { attributes, indexBuffer, topology } = options;
18067    this.buffers = [];
18068    this.attributes = {};
18069    if (attributes) {
18070      for (const i in attributes) {
18071        this.addAttribute(i, attributes[i]);
18072      }
18073    }
18074    this.instanceCount = options.instanceCount ?? 1;
18075    if (indexBuffer) {
18076      this.addIndex(indexBuffer);
18077    }
18078    this.topology = topology || "triangle-list";
18079  }
18080  onBufferUpdate() {
18081    this._boundsDirty = true;
18082    this.emit("update", this);
18083  }
18084  /**
18085   * Returns the requested attribute.
18086   * @param id - The name of the attribute required
18087   * @returns - The attribute requested.
18088   */
18089  getAttribute(id) {
18090    return this.attributes[id];
18091  }
18092  /**
18093   * Returns the index buffer
18094   * @returns - The index buffer.
18095   */
18096  getIndex() {
18097    return this.indexBuffer;
18098  }
18099  /**
18100   * Returns the requested buffer.
18101   * @param id - The name of the buffer required.
18102   * @returns - The buffer requested.
18103   */
18104  getBuffer(id) {
18105    return this.getAttribute(id).buffer;
18106  }
18107  /**
18108   * Used to figure out how many vertices there are in this geometry
18109   * @returns the number of vertices in the geometry
18110   */
18111  getSize() {
18112    for (const i in this.attributes) {
18113      const attribute = this.attributes[i];
18114      const buffer = attribute.buffer;
18115      return buffer.data.length / (attribute.stride / 4 || attribute.size);
18116    }
18117    return 0;
18118  }
18119  /**
18120   * Adds an attribute to the geometry.
18121   * @param name - The name of the attribute to add.
18122   * @param attributeOption - The attribute option to add.
18123   */
18124  addAttribute(name, attributeOption) {
18125    const attribute = ensureIsAttribute(attributeOption);
18126    const bufferIndex = this.buffers.indexOf(attribute.buffer);
18127    if (bufferIndex === -1) {
18128      this.buffers.push(attribute.buffer);
18129      attribute.buffer.on("update", this.onBufferUpdate, this);
18130      attribute.buffer.on("change", this.onBufferUpdate, this);
18131    }
18132    this.attributes[name] = attribute;
18133  }
18134  /**
18135   * Adds an index buffer to the geometry.
18136   * @param indexBuffer - The index buffer to add. Can be a Buffer, TypedArray, or an array of numbers.
18137   */
18138  addIndex(indexBuffer) {
18139    this.indexBuffer = ensureIsBuffer(indexBuffer, true);
18140    this.buffers.push(this.indexBuffer);
18141  }
18142  /** Returns the bounds of the geometry. */
18143  get bounds() {
18144    if (!this._boundsDirty)
18145      return this._bounds;
18146    this._boundsDirty = false;
18147    return getGeometryBounds(this, "aPosition", this._bounds);
18148  }
18149  /** Unloads the geometry from the GPU. */
18150  unload() {
18151    this.emit("unload", this);
18152    for (const key in this._gpuData) {
18153      this._gpuData[key]?.destroy();
18154    }
18155    this._gpuData = /* @__PURE__ */ Object.create(null);
18156  }
18157  /**
18158   * destroys the geometry.
18159   * @param destroyBuffers - destroy the buffers associated with this geometry
18160   */
18161  destroy(destroyBuffers = false) {
18162    this.emit("destroy", this);
18163    this.removeAllListeners();
18164    if (destroyBuffers) {
18165      this.buffers.forEach((buffer) => buffer.destroy());
18166    }
18167    this.unload();
18168    this.indexBuffer?.destroy();
18169    this.attributes = null;
18170    this.buffers = null;
18171    this.indexBuffer = null;
18172    this._bounds = null;
18173  }
18174}
18175
18176const placeHolderBufferData = new Float32Array(1);
18177const placeHolderIndexData = new Uint32Array(1);
18178class BatchGeometry extends Geometry {
18179  constructor() {
18180    const vertexSize = 6;
18181    const attributeBuffer = new Buffer({
18182      data: placeHolderBufferData,
18183      label: "attribute-batch-buffer",
18184      usage: BufferUsage.VERTEX | BufferUsage.COPY_DST,
18185      shrinkToFit: false
18186    });
18187    const indexBuffer = new Buffer({
18188      data: placeHolderIndexData,
18189      label: "index-batch-buffer",
18190      usage: BufferUsage.INDEX | BufferUsage.COPY_DST,
18191      // | BufferUsage.STATIC,
18192      shrinkToFit: false
18193    });
18194    const stride = vertexSize * 4;
18195    super({
18196      attributes: {
18197        aPosition: {
18198          buffer: attributeBuffer,
18199          format: "float32x2",
18200          stride,
18201          offset: 0
18202        },
18203        aUV: {
18204          buffer: attributeBuffer,
18205          format: "float32x2",
18206          stride,
18207          offset: 2 * 4
18208        },
18209        aColor: {
18210          buffer: attributeBuffer,
18211          format: "unorm8x4",
18212          stride,
18213          offset: 4 * 4
18214        },
18215        aTextureIdAndRound: {
18216          buffer: attributeBuffer,
18217          format: "uint16x2",
18218          stride,
18219          offset: 5 * 4
18220        }
18221      },
18222      indexBuffer
18223    });
18224  }
18225}
18226
18227function addBits(srcParts, parts, name) {
18228  if (srcParts) {
18229    for (const i in srcParts) {
18230      const id = i.toLocaleLowerCase();
18231      const part = parts[id];
18232      if (part) {
18233        let sanitisedPart = srcParts[i];
18234        if (i === "header") {
18235          sanitisedPart = sanitisedPart.replace(/@in\s+[^;]+;\s*/g, "").replace(/@out\s+[^;]+;\s*/g, "");
18236        }
18237        if (name) {
18238          part.push(`//----${name}----//`);
18239        }
18240        part.push(sanitisedPart);
18241      } else {
18242        warn(`${i} placement hook does not exist in shader`);
18243      }
18244    }
18245  }
18246}
18247
18248const findHooksRx = /\{\{(.*?)\}\}/g;
18249function compileHooks(programSrc) {
18250  const parts = {};
18251  const partMatches = programSrc.match(findHooksRx)?.map((hook) => hook.replace(/[{()}]/g, "")) ?? [];
18252  partMatches.forEach((hook) => {
18253    parts[hook] = [];
18254  });
18255  return parts;
18256}
18257
18258function extractInputs(fragmentSource, out) {
18259  let match;
18260  const regex = /@in\s+([^;]+);/g;
18261  while ((match = regex.exec(fragmentSource)) !== null) {
18262    out.push(match[1]);
18263  }
18264}
18265function compileInputs(fragments, template, sort = false) {
18266  const results = [];
18267  extractInputs(template, results);
18268  fragments.forEach((fragment) => {
18269    if (fragment.header) {
18270      extractInputs(fragment.header, results);
18271    }
18272  });
18273  const mainInput = results;
18274  if (sort) {
18275    mainInput.sort();
18276  }
18277  const finalString = mainInput.map((inValue, i) => `       @location(${i}) ${inValue},`).join("\n");
18278  let cleanedString = template.replace(/@in\s+[^;]+;\s*/g, "");
18279  cleanedString = cleanedString.replace("{{in}}", `
18280${finalString}
18281`);
18282  return cleanedString;
18283}
18284
18285function extractOutputs(fragmentSource, out) {
18286  let match;
18287  const regex = /@out\s+([^;]+);/g;
18288  while ((match = regex.exec(fragmentSource)) !== null) {
18289    out.push(match[1]);
18290  }
18291}
18292function extractVariableName(value) {
18293  const regex = /\b(\w+)\s*:/g;
18294  const match = regex.exec(value);
18295  return match ? match[1] : "";
18296}
18297function stripVariable(value) {
18298  const regex = /@.*?\s+/g;
18299  return value.replace(regex, "");
18300}
18301function compileOutputs(fragments, template) {
18302  const results = [];
18303  extractOutputs(template, results);
18304  fragments.forEach((fragment) => {
18305    if (fragment.header) {
18306      extractOutputs(fragment.header, results);
18307    }
18308  });
18309  let index = 0;
18310  const mainStruct = results.sort().map((inValue) => {
18311    if (inValue.indexOf("builtin") > -1) {
18312      return inValue;
18313    }
18314    return `@location(${index++}) ${inValue}`;
18315  }).join(",\n");
18316  const mainStart = results.sort().map((inValue) => `       var ${stripVariable(inValue)};`).join("\n");
18317  const mainEnd = `return VSOutput(
18318            ${results.sort().map((inValue) => ` ${extractVariableName(inValue)}`).join(",\n")});`;
18319  let compiledCode = template.replace(/@out\s+[^;]+;\s*/g, "");
18320  compiledCode = compiledCode.replace("{{struct}}", `
18321${mainStruct}
18322`);
18323  compiledCode = compiledCode.replace("{{start}}", `
18324${mainStart}
18325`);
18326  compiledCode = compiledCode.replace("{{return}}", `
18327${mainEnd}
18328`);
18329  return compiledCode;
18330}
18331
18332function injectBits(templateSrc, fragmentParts) {
18333  let out = templateSrc;
18334  for (const i in fragmentParts) {
18335    const parts = fragmentParts[i];
18336    const toInject = parts.join("\n");
18337    if (toInject.length) {
18338      out = out.replace(`{{${i}}}`, `//-----${i} START-----//
18339${parts.join("\n")}
18340//----${i} FINISH----//`);
18341    } else {
18342      out = out.replace(`{{${i}}}`, "");
18343    }
18344  }
18345  return out;
18346}
18347
18348const cacheMap = /* @__PURE__ */ Object.create(null);
18349const bitCacheMap = /* @__PURE__ */ new Map();
18350let CACHE_UID = 0;
18351function compileHighShader({
18352  template,
18353  bits
18354}
vendor: 9,896 bytes, lines 18354-18782
18354) {
18355  const cacheId = generateCacheId(template, bits);
18356  if (cacheMap[cacheId])
18357    return cacheMap[cacheId];
18358  const { vertex, fragment } = compileInputsAndOutputs(template, bits);
18359  cacheMap[cacheId] = compileBits(vertex, fragment, bits);
18360  return cacheMap[cacheId];
18361}
18362function compileHighShaderGl({
18363  template,
18364  bits
18365}) {
18366  const cacheId = generateCacheId(template, bits);
18367  if (cacheMap[cacheId])
18368    return cacheMap[cacheId];
18369  cacheMap[cacheId] = compileBits(template.vertex, template.fragment, bits);
18370  return cacheMap[cacheId];
18371}
18372function compileInputsAndOutputs(template, bits) {
18373  const vertexFragments = bits.map((shaderBit) => shaderBit.vertex).filter((v) => !!v);
18374  const fragmentFragments = bits.map((shaderBit) => shaderBit.fragment).filter((v) => !!v);
18375  let compiledVertex = compileInputs(vertexFragments, template.vertex, true);
18376  compiledVertex = compileOutputs(vertexFragments, compiledVertex);
18377  const compiledFragment = compileInputs(fragmentFragments, template.fragment, true);
18378  return {
18379    vertex: compiledVertex,
18380    fragment: compiledFragment
18381  };
18382}
18383function generateCacheId(template, bits) {
18384  return bits.map((highFragment) => {
18385    if (!bitCacheMap.has(highFragment)) {
18386      bitCacheMap.set(highFragment, CACHE_UID++);
18387    }
18388    return bitCacheMap.get(highFragment);
18389  }).sort((a, b) => a - b).join("-") + template.vertex + template.fragment;
18390}
18391function compileBits(vertex, fragment, bits) {
18392  const vertexParts = compileHooks(vertex);
18393  const fragmentParts = compileHooks(fragment);
18394  bits.forEach((shaderBit) => {
18395    addBits(shaderBit.vertex, vertexParts, shaderBit.name);
18396    addBits(shaderBit.fragment, fragmentParts, shaderBit.name);
18397  });
18398  return {
18399    vertex: injectBits(vertex, vertexParts),
18400    fragment: injectBits(fragment, fragmentParts)
18401  };
18402}
18403
18404const vertexGPUTemplate = (
18405  /* wgsl */
18406  `
18407    @in aPosition: vec2<f32>;
18408    @in aUV: vec2<f32>;
18409
18410    @out @builtin(position) vPosition: vec4<f32>;
18411    @out vUV : vec2<f32>;
18412    @out vColor : vec4<f32>;
18413
18414    {{header}}
18415
18416    struct VSOutput {
18417        {{struct}}
18418    };
18419
18420    @vertex
18421    fn main( {{in}} ) -> VSOutput {
18422
18423        var worldTransformMatrix = globalUniforms.uWorldTransformMatrix;
18424        var modelMatrix = mat3x3<f32>(
18425            1.0, 0.0, 0.0,
18426            0.0, 1.0, 0.0,
18427            0.0, 0.0, 1.0
18428          );
18429        var position = aPosition;
18430        var uv = aUV;
18431
18432        {{start}}
18433
18434        vColor = vec4<f32>(1., 1., 1., 1.);
18435
18436        {{main}}
18437
18438        vUV = uv;
18439
18440        var modelViewProjectionMatrix = globalUniforms.uProjectionMatrix * worldTransformMatrix * modelMatrix;
18441
18442        vPosition =  vec4<f32>((modelViewProjectionMatrix *  vec3<f32>(position, 1.0)).xy, 0.0, 1.0);
18443
18444        vColor *= globalUniforms.uWorldColorAlpha;
18445
18446        {{end}}
18447
18448        {{return}}
18449    };
18450`
18451);
18452const fragmentGPUTemplate = (
18453  /* wgsl */
18454  `
18455    @in vUV : vec2<f32>;
18456    @in vColor : vec4<f32>;
18457
18458    {{header}}
18459
18460    @fragment
18461    fn main(
18462        {{in}}
18463      ) -> @location(0) vec4<f32> {
18464
18465        {{start}}
18466
18467        var outColor:vec4<f32>;
18468
18469        {{main}}
18470
18471        var finalColor:vec4<f32> = outColor * vColor;
18472
18473        {{end}}
18474
18475        return finalColor;
18476      };
18477`
18478);
18479const vertexGlTemplate = (
18480  /* glsl */
18481  `
18482    in vec2 aPosition;
18483    in vec2 aUV;
18484
18485    out vec4 vColor;
18486    out vec2 vUV;
18487
18488    {{header}}
18489
18490    void main(void){
18491
18492        mat3 worldTransformMatrix = uWorldTransformMatrix;
18493        mat3 modelMatrix = mat3(
18494            1.0, 0.0, 0.0,
18495            0.0, 1.0, 0.0,
18496            0.0, 0.0, 1.0
18497          );
18498        vec2 position = aPosition;
18499        vec2 uv = aUV;
18500
18501        {{start}}
18502
18503        vColor = vec4(1.);
18504
18505        {{main}}
18506
18507        vUV = uv;
18508
18509        mat3 modelViewProjectionMatrix = uProjectionMatrix * worldTransformMatrix * modelMatrix;
18510
18511        gl_Position = vec4((modelViewProjectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);
18512
18513        vColor *= uWorldColorAlpha;
18514
18515        {{end}}
18516    }
18517`
18518);
18519const fragmentGlTemplate = (
18520  /* glsl */
18521  `
18522
18523    in vec4 vColor;
18524    in vec2 vUV;
18525
18526    out vec4 finalColor;
18527
18528    {{header}}
18529
18530    void main(void) {
18531
18532        {{start}}
18533
18534        vec4 outColor;
18535
18536        {{main}}
18537
18538        finalColor = outColor * vColor;
18539
18540        {{end}}
18541    }
18542`
18543);
18544
18545const globalUniformsBit = {
18546  name: "global-uniforms-bit",
18547  vertex: {
18548    header: (
18549      /* wgsl */
18550      `
18551        struct GlobalUniforms {
18552            uProjectionMatrix:mat3x3<f32>,
18553            uWorldTransformMatrix:mat3x3<f32>,
18554            uWorldColorAlpha: vec4<f32>,
18555            uResolution: vec2<f32>,
18556        }
18557
18558        @group(0) @binding(0) var<uniform> globalUniforms : GlobalUniforms;
18559        `
18560    )
18561  }
18562};
18563const globalUniformsBitGl = {
18564  name: "global-uniforms-bit",
18565  vertex: {
18566    header: (
18567      /* glsl */
18568      `
18569          uniform mat3 uProjectionMatrix;
18570          uniform mat3 uWorldTransformMatrix;
18571          uniform vec4 uWorldColorAlpha;
18572          uniform vec2 uResolution;
18573        `
18574    )
18575  }
18576};
18577
18578function compileHighShaderGpuProgram({ bits, name }) {
18579  const source = compileHighShader({
18580    template: {
18581      fragment: fragmentGPUTemplate,
18582      vertex: vertexGPUTemplate
18583    },
18584    bits: [
18585      globalUniformsBit,
18586      ...bits
18587    ]
18588  });
18589  return GpuProgram.from({
18590    name,
18591    vertex: {
18592      source: source.vertex,
18593      entryPoint: "main"
18594    },
18595    fragment: {
18596      source: source.fragment,
18597      entryPoint: "main"
18598    }
18599  });
18600}
18601function compileHighShaderGlProgram({ bits, name }) {
18602  return new GlProgram({
18603    name,
18604    ...compileHighShaderGl({
18605      template: {
18606        vertex: vertexGlTemplate,
18607        fragment: fragmentGlTemplate
18608      },
18609      bits: [
18610        globalUniformsBitGl,
18611        ...bits
18612      ]
18613    })
18614  });
18615}
18616
18617const colorBit = {
18618  name: "color-bit",
18619  vertex: {
18620    header: (
18621      /* wgsl */
18622      `
18623            @in aColor: vec4<f32>;
18624        `
18625    ),
18626    main: (
18627      /* wgsl */
18628      `
18629            vColor *= vec4<f32>(aColor.rgb * aColor.a, aColor.a);
18630        `
18631    )
18632  }
18633};
18634const colorBitGl = {
18635  name: "color-bit",
18636  vertex: {
18637    header: (
18638      /* glsl */
18639      `
18640            in vec4 aColor;
18641        `
18642    ),
18643    main: (
18644      /* glsl */
18645      `
18646            vColor *= vec4(aColor.rgb * aColor.a, aColor.a);
18647        `
18648    )
18649  }
18650};
18651
18652const textureBatchBitGpuCache = {};
18653function generateBindingSrc(maxTextures) {
18654  const src = [];
18655  if (maxTextures === 1) {
18656    src.push("@group(1) @binding(0) var textureSource1: texture_2d<f32>;");
18657    src.push("@group(1) @binding(1) var textureSampler1: sampler;");
18658  } else {
18659    let bindingIndex = 0;
18660    for (let i = 0; i < maxTextures; i++) {
18661      src.push(`@group(1) @binding(${bindingIndex++}) var textureSource${i + 1}: texture_2d<f32>;`);
18662      src.push(`@group(1) @binding(${bindingIndex++}) var textureSampler${i + 1}: sampler;`);
18663    }
18664  }
18665  return src.join("\n");
18666}
18667function generateSampleSrc(maxTextures) {
18668  const src = [];
18669  if (maxTextures === 1) {
18670    src.push("outColor = textureSampleGrad(textureSource1, textureSampler1, vUV, uvDx, uvDy);");
18671  } else {
18672    src.push("switch vTextureId {");
18673    for (let i = 0; i < maxTextures; i++) {
18674      if (i === maxTextures - 1) {
18675        src.push(`  default:{`);
18676      } else {
18677        src.push(`  case ${i}:{`);
18678      }
18679      src.push(`      outColor = textureSampleGrad(textureSource${i + 1}, textureSampler${i + 1}, vUV, uvDx, uvDy);`);
18680      src.push(`      break;}`);
18681    }
18682    src.push(`}`);
18683  }
18684  return src.join("\n");
18685}
18686function generateTextureBatchBit(maxTextures) {
18687  if (!textureBatchBitGpuCache[maxTextures]) {
18688    textureBatchBitGpuCache[maxTextures] = {
18689      name: "texture-batch-bit",
18690      vertex: {
18691        header: `
18692                @in aTextureIdAndRound: vec2<u32>;
18693                @out @interpolate(flat) vTextureId : u32;
18694            `,
18695        main: `
18696                vTextureId = aTextureIdAndRound.y;
18697            `,
18698        end: `
18699                if(aTextureIdAndRound.x == 1)
18700                {
18701                    vPosition = vec4<f32>(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw);
18702                }
18703            `
18704      },
18705      fragment: {
18706        header: `
18707                @in @interpolate(flat) vTextureId: u32;
18708
18709                ${generateBindingSrc(maxTextures)}
18710            `,
18711        main: `
18712                var uvDx = dpdx(vUV);
18713                var uvDy = dpdy(vUV);
18714
18715                ${generateSampleSrc(maxTextures)}
18716            `
18717      }
18718    };
18719  }
18720  return textureBatchBitGpuCache[maxTextures];
18721}
18722const textureBatchBitGlCache = {};
18723function generateSampleGlSrc(maxTextures) {
18724  const src = [];
18725  for (let i = 0; i < maxTextures; i++) {
18726    if (i > 0) {
18727      src.push("else");
18728    }
18729    if (i < maxTextures - 1) {
18730      src.push(`if(vTextureId < ${i}.5)`);
18731    }
18732    src.push("{");
18733    src.push(`	outColor = texture(uTextures[${i}], vUV);`);
18734    src.push("}");
18735  }
18736  return src.join("\n");
18737}
18738function generateTextureBatchBitGl(maxTextures) {
18739  if (!textureBatchBitGlCache[maxTextures]) {
18740    textureBatchBitGlCache[maxTextures] = {
18741      name: "texture-batch-bit",
18742      vertex: {
18743        header: `
18744                in vec2 aTextureIdAndRound;
18745                out float vTextureId;
18746
18747            `,
18748        main: `
18749                vTextureId = aTextureIdAndRound.y;
18750            `,
18751        end: `
18752                if(aTextureIdAndRound.x == 1.)
18753                {
18754                    gl_Position.xy = roundPixels(gl_Position.xy, uResolution);
18755                }
18756            `
18757      },
18758      fragment: {
18759        header: `
18760                in float vTextureId;
18761
18762                uniform sampler2D uTextures[${maxTextures}];
18763
18764            `,
18765        main: `
18766
18767                ${generateSampleGlSrc(maxTextures)}
18768            `
18769      }
18770    };
18771  }
18772  return textureBatchBitGlCache[maxTextures];
18773}
18774
18775const roundPixelsBit = {
18776  name: "round-pixels-bit",
18777  vertex: {
18778    header: (
18779      /* wgsl */
18780      `
18781            fn roundPixels(position: vec2<f32>, targetSize: vec2<f32>) -> vec2<f32>
18782            {
18783                return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0;
18784            }
18785        `
18786    )
18787  }
18788};
18789const roundPixelsBitGl = {
18790  name: "round-pixels-bit",
18791  vertex: {
18792    header: (
18793      /* glsl */
18794      `
18795            vec2 roundPixels(vec2 position, vec2 targetSize)
18796            {
18797                return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0;
18798            }
18799        `
18800    )
18801  }
18802};
18803
18804const batchSamplersUniformGroupHash = {};
18805function getBatchSamplersUniformGroup(maxTextures) {
18806  let batchSamplersUniformGroup = batchSamplersUniformGroupHash[maxTextures];
18807  if (batchSamplersUniformGroup)
18808    return batchSamplersUniformGroup;
18809  const sampleValues = new Int32Array(maxTextures);
18810  for (let i = 0; i < maxTextures; i++) {
18811    sampleValues[i] = i;
18812  }
18813  batchSamplersUniformGroup = batchSamplersUniformGroupHash[maxTextures] = new UniformGroup({
18814    uTextures: { value: sampleValues, type: `i32`, size: maxTextures }
18815  }, { isStatic: true });
18816  return batchSamplersUniformGroup;
18817}
18818
18819class DefaultShader extends Shader {
18820  constructor(maxTextures) {
18821    const glProgram = compileHighShaderGlProgram({
18822      name: "batch",
18823      bits: [
18824        colorBitGl,
18825        generateTextureBatchBitGl(maxTextures),
18826        roundPixelsBitGl
18827      ]
18828    });
18829    const gpuProgram = compileHighShaderGpuProgram({
18830      name: "batch",
18831      bits: [
18832        colorBit,
18833        generateTextureBatchBit(maxTextures),
18834        roundPixelsBit
18835      ]
18836    });
18837    super({
18838      glProgram,
18839      gpuProgram,
18840      resources: {
18841        batchSamplers: getBatchSamplersUniformGroup(maxTextures)
18842      }
18843    });
18844    this.maxTextures = maxTextures;
18845  }
18846}
18847
18848let defaultShader = null;
18849const _DefaultBatcher = class _DefaultBatcher extends Batcher {
18850  constructor(options) {
18851    super(options);
18852    this.geometry = new BatchGeometry();
18853    this.name = _DefaultBatcher.extension.name;
18854    /** The size of one attribute. 1 = 32 bit. x, y, u, v, color, textureIdAndRound -> total = 6 */
18855    this.vertexSize = 6;
18856    defaultShader ?? (defaultShader = new DefaultShader(options.maxTextures));
18857    this.shader = defaultShader;
18858  }
18859  /**
18860   * Packs the attributes of a DefaultBatchableMeshElement into the provided views.
18861   * @param element - The DefaultBatchableMeshElement to pack.
18862   * @param float32View - The Float32Array view to pack into.
18863   * @param uint32View - The Uint32Array view to pack into.
18864   * @param index - The starting index in the views.
18865   * @param textureId - The texture ID to use.
18866   */
18867  packAttributes(element, float32View, uint32View, index, textureId) {
18868    const textureIdAndRound = textureId << 16 | element.roundPixels & 65535;
18869    const wt = element.transform;
18870    const a = wt.a;
18871    const b = wt.b;
18872    const c = wt.c;
18873    const d = wt.d;
18874    const tx = wt.tx;
18875    const ty = wt.ty;
18876    const { positions, uvs } = element;
18877    const argb = element.color;
18878    const offset = element.attributeOffset;
18879    const end = offset + element.attributeSize;
18880    for (let i = offset; i < end; i++) {
18881      const i2 = i * 2;
18882      const x = positions[i2];
18883      const y = positions[i2 + 1];
18884      float32View[index++] = a * x + c * y + tx;
18885      float32View[index++] = d * y + b * x + ty;
18886      float32View[index++] = uvs[i2];
18887      float32View[index++] = uvs[i2 + 1];
18888      uint32View[index++] = argb;
18889      uint32View[index++] = textureIdAndRound;
18890    }
18891  }
18892  /**
18893   * Packs the attributes of a DefaultBatchableQuadElement into the provided views.
18894   * @param element - The DefaultBatchableQuadElement to pack.
18895   * @param float32View - The Float32Array view to pack into.
18896   * @param uint32View - The Uint32Array view to pack into.
18897   * @param index - The starting index in the views.
18898   * @param textureId - The texture ID to use.
18899   */
18900  packQuadAttributes(element, float32View, uint32View, index, textureId) {
18901    const texture = element.texture;
18902    const wt = element.transform;
18903    const a = wt.a;
18904    const b = wt.b;
18905    const c = wt.c;
18906    const d = wt.d;
18907    const tx = wt.tx;
18908    const ty = wt.ty;
18909    const bounds = element.bounds;
18910    const w0 = bounds.maxX;
18911    const w1 = bounds.minX;
18912    const h0 = bounds.maxY;
18913    const h1 = bounds.minY;
18914    const uvs = texture.uvs;
18915    const argb = element.color;
18916    const textureIdAndRound = textureId << 16 | element.roundPixels & 65535;
18917    float32View[index + 0] = a * w1 + c * h1 + tx;
18918    float32View[index + 1] = d * h1 + b * w1 + ty;
18919    float32View[index + 2] = uvs.x0;
18920    float32View[index + 3] = uvs.y0;
18921    uint32View[index + 4] = argb;
18922    uint32View[index + 5] = textureIdAndRound;
18923    float32View[index + 6] = a * w0 + c * h1 + tx;
18924    float32View[index + 7] = d * h1 + b * w0 + ty;
18925    float32View[index + 8] = uvs.x1;
18926    float32View[index + 9] = uvs.y1;
18927    uint32View[index + 10] = argb;
18928    uint32View[index + 11] = textureIdAndRound;
18929    float32View[index + 12] = a * w0 + c * h0 + tx;
18930    float32View[index + 13] = d * h0 + b * w0 + ty;
18931    float32View[index + 14] = uvs.x2;
18932    float32View[index + 15] = uvs.y2;
18933    uint32View[index + 16] = argb;
18934    uint32View[index + 17] = textureIdAndRound;
18935    float32View[index + 18] = a * w1 + c * h0 + tx;
18936    float32View[index + 19] = d * h0 + b * w1 + ty;
18937    float32View[index + 20] = uvs.x3;
18938    float32View[index + 21] = uvs.y3;
18939    uint32View[index + 22] = argb;
18940    uint32View[index + 23] = textureIdAndRound;
18941  }
18942  /**
18943   * Updates the maximum number of textures that can be used in the shader.
18944   * @param maxTextures - The maximum number of textures that can be used in the shader.
18945   * @internal
18946   */
18947  _updateMaxTextures(maxTextures) {
18948    if (this.shader.maxTextures === maxTextures)
18949      return;
18950    defaultShader = new DefaultShader(maxTextures);
18951    this.shader = defaultShader;
18952  }
18953  destroy() {
18954    this.shader = null;
18955    super.destroy();
18956  }
18957};
18958/** @ignore */
18959_DefaultBatcher.extension = {
18960  type: [
18961    ExtensionType.Batcher
18962  ],
18963  name: "default"
18964};
18965let DefaultBatcher = _DefaultBatcher;
18966
18967class GCManagedHash {
18968  constructor(options) {
18969    // Exposed directly for GC system access
18970    this.items = /* @__PURE__ */ Object.create(null);
18971    const { renderer, type, onUnload, priority, name } = options;
18972    this._renderer = renderer;
18973    renderer.gc.addResourceHash(this, "items", type, priority ?? 0);
18974    this._onUnload = onUnload;
18975    this.name = name;
18976  }
18977  /**
18978   * Add an item to the hash. No-op if already added.
18979   * @param item
18980   * @returns true if the item was added, false if it was already in the hash
18981   */
18982  add(item) {
18983    if (this.items[item.uid])
18984      return false;
vendor: 51,499 bytes, lines 18985-20653
18985    this.items[item.uid] = item;
18986    item.once("unload", this.remove, this);
18987    item._gcLastUsed = this._renderer.gc.now;
18988    return true;
18989  }
18990  remove(item, ...args) {
18991    if (!this.items[item.uid])
18992      return;
18993    const gpuData = item._gpuData[this._renderer.uid];
18994    if (!gpuData)
18995      return;
18996    this._onUnload?.(item, ...args);
18997    gpuData.destroy();
18998    item._gpuData[this._renderer.uid] = null;
18999    this.items[item.uid] = null;
19000  }
19001  removeAll(...args) {
19002    Object.values(this.items).forEach((item) => item && this.remove(item, ...args));
19003  }
19004  destroy(...args) {
19005    this.removeAll(...args);
19006    this.items = /* @__PURE__ */ Object.create(null);
19007    this._renderer = null;
19008    this._onUnload = null;
19009  }
19010}
19011
19012function buildUvs(vertices, verticesStride, verticesOffset, uvs, uvsOffset, uvsStride, size, matrix = null) {
19013  let index = 0;
19014  verticesOffset *= verticesStride;
19015  uvsOffset *= uvsStride;
19016  const a = matrix.a;
19017  const b = matrix.b;
19018  const c = matrix.c;
19019  const d = matrix.d;
19020  const tx = matrix.tx;
19021  const ty = matrix.ty;
19022  while (index < size) {
19023    const x = vertices[verticesOffset];
19024    const y = vertices[verticesOffset + 1];
19025    uvs[uvsOffset] = a * x + c * y + tx;
19026    uvs[uvsOffset + 1] = b * x + d * y + ty;
19027    uvsOffset += uvsStride;
19028    verticesOffset += verticesStride;
19029    index++;
19030  }
19031}
19032function buildSimpleUvs(uvs, uvsOffset, uvsStride, size) {
19033  let index = 0;
19034  uvsOffset *= uvsStride;
19035  while (index < size) {
19036    uvs[uvsOffset] = 0;
19037    uvs[uvsOffset + 1] = 0;
19038    uvsOffset += uvsStride;
19039    index++;
19040  }
19041}
19042
19043function transformVertices(vertices, m, offset, stride, size) {
19044  const a = m.a;
19045  const b = m.b;
19046  const c = m.c;
19047  const d = m.d;
19048  const tx = m.tx;
19049  const ty = m.ty;
19050  offset || (offset = 0);
19051  stride || (stride = 2);
19052  size || (size = vertices.length / stride - offset);
19053  let index = offset * stride;
19054  for (let i = 0; i < size; i++) {
19055    const x = vertices[index];
19056    const y = vertices[index + 1];
19057    vertices[index] = a * x + c * y + tx;
19058    vertices[index + 1] = b * x + d * y + ty;
19059    index += stride;
19060  }
19061}
19062
19063const identityMatrix = new Matrix();
19064class BatchableGraphics {
19065  constructor() {
19066    this.packAsQuad = false;
19067    this.batcherName = "default";
19068    this.topology = "triangle-list";
19069    this.applyTransform = true;
19070    this.roundPixels = 0;
19071    this._batcher = null;
19072    this._batch = null;
19073  }
19074  get uvs() {
19075    return this.geometryData.uvs;
19076  }
19077  get positions() {
19078    return this.geometryData.vertices;
19079  }
19080  get indices() {
19081    return this.geometryData.indices;
19082  }
19083  get blendMode() {
19084    if (this.renderable && this.applyTransform) {
19085      return this.renderable.groupBlendMode;
19086    }
19087    return "normal";
19088  }
19089  get color() {
19090    const rgb = this.baseColor;
19091    const bgr = rgb >> 16 | rgb & 65280 | (rgb & 255) << 16;
19092    const renderable = this.renderable;
19093    if (renderable) {
19094      return multiplyHexColors(bgr, renderable.groupColor) + (this.alpha * renderable.groupAlpha * 255 << 24);
19095    }
19096    return bgr + (this.alpha * 255 << 24);
19097  }
19098  get transform() {
19099    return this.renderable?.groupTransform || identityMatrix;
19100  }
19101  copyTo(gpuBuffer) {
19102    gpuBuffer.indexOffset = this.indexOffset;
19103    gpuBuffer.indexSize = this.indexSize;
19104    gpuBuffer.attributeOffset = this.attributeOffset;
19105    gpuBuffer.attributeSize = this.attributeSize;
19106    gpuBuffer.baseColor = this.baseColor;
19107    gpuBuffer.alpha = this.alpha;
19108    gpuBuffer.texture = this.texture;
19109    gpuBuffer.geometryData = this.geometryData;
19110    gpuBuffer.topology = this.topology;
19111  }
19112  reset() {
19113    this.applyTransform = true;
19114    this.renderable = null;
19115    this.topology = "triangle-list";
19116  }
19117  destroy() {
19118    this.renderable = null;
19119    this.texture = null;
19120    this.geometryData = null;
19121    this._batcher = null;
19122    this._batch = null;
19123  }
19124}
19125
19126const buildCircle = {
19127  extension: {
19128    type: ExtensionType.ShapeBuilder,
19129    name: "circle"
19130  },
19131  build(shape, points) {
19132    let x;
19133    let y;
19134    let dx;
19135    let dy;
19136    let rx;
19137    let ry;
19138    if (shape.type === "circle") {
19139      const circle = shape;
19140      rx = ry = circle.radius;
19141      if (rx <= 0) {
19142        return false;
19143      }
19144      x = circle.x;
19145      y = circle.y;
19146      dx = dy = 0;
19147    } else if (shape.type === "ellipse") {
19148      const ellipse = shape;
19149      rx = ellipse.halfWidth;
19150      ry = ellipse.halfHeight;
19151      if (rx <= 0 || ry <= 0) {
19152        return false;
19153      }
19154      x = ellipse.x;
19155      y = ellipse.y;
19156      dx = dy = 0;
19157    } else {
19158      const roundedRect = shape;
19159      const halfWidth = roundedRect.width / 2;
19160      const halfHeight = roundedRect.height / 2;
19161      x = roundedRect.x + halfWidth;
19162      y = roundedRect.y + halfHeight;
19163      rx = ry = Math.max(0, Math.min(roundedRect.radius, Math.min(halfWidth, halfHeight)));
19164      dx = halfWidth - rx;
19165      dy = halfHeight - ry;
19166    }
19167    if (dx < 0 || dy < 0) {
19168      return false;
19169    }
19170    const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
19171    const m = n * 8 + (dx ? 4 : 0) + (dy ? 4 : 0);
19172    if (m === 0) {
19173      return false;
19174    }
19175    if (n === 0) {
19176      points[0] = points[6] = x + dx;
19177      points[1] = points[3] = y + dy;
19178      points[2] = points[4] = x - dx;
19179      points[5] = points[7] = y - dy;
19180      return true;
19181    }
19182    let j1 = 0;
19183    let j2 = n * 4 + (dx ? 2 : 0) + 2;
19184    let j3 = j2;
19185    let j4 = m;
19186    let x0 = dx + rx;
19187    let y0 = dy;
19188    let x1 = x + x0;
19189    let x2 = x - x0;
19190    let y1 = y + y0;
19191    points[j1++] = x1;
19192    points[j1++] = y1;
19193    points[--j2] = y1;
19194    points[--j2] = x2;
19195    if (dy) {
19196      const y22 = y - y0;
19197      points[j3++] = x2;
19198      points[j3++] = y22;
19199      points[--j4] = y22;
19200      points[--j4] = x1;
19201    }
19202    for (let i = 1; i < n; i++) {
19203      const a = Math.PI / 2 * (i / n);
19204      const x02 = dx + Math.cos(a) * rx;
19205      const y02 = dy + Math.sin(a) * ry;
19206      const x12 = x + x02;
19207      const x22 = x - x02;
19208      const y12 = y + y02;
19209      const y22 = y - y02;
19210      points[j1++] = x12;
19211      points[j1++] = y12;
19212      points[--j2] = y12;
19213      points[--j2] = x22;
19214      points[j3++] = x22;
19215      points[j3++] = y22;
19216      points[--j4] = y22;
19217      points[--j4] = x12;
19218    }
19219    x0 = dx;
19220    y0 = dy + ry;
19221    x1 = x + x0;
19222    x2 = x - x0;
19223    y1 = y + y0;
19224    const y2 = y - y0;
19225    points[j1++] = x1;
19226    points[j1++] = y1;
19227    points[--j4] = y2;
19228    points[--j4] = x1;
19229    if (dx) {
19230      points[j1++] = x2;
19231      points[j1++] = y1;
19232      points[--j4] = y2;
19233      points[--j4] = x2;
19234    }
19235    return true;
19236  },
19237  triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) {
19238    if (points.length === 0) {
19239      return;
19240    }
19241    let centerX = 0;
19242    let centerY = 0;
19243    for (let i = 0; i < points.length; i += 2) {
19244      centerX += points[i];
19245      centerY += points[i + 1];
19246    }
19247    centerX /= points.length / 2;
19248    centerY /= points.length / 2;
19249    let count = verticesOffset;
19250    vertices[count * verticesStride] = centerX;
19251    vertices[count * verticesStride + 1] = centerY;
19252    const centerIndex = count++;
19253    for (let i = 0; i < points.length; i += 2) {
19254      vertices[count * verticesStride] = points[i];
19255      vertices[count * verticesStride + 1] = points[i + 1];
19256      if (i > 0) {
19257        indices[indicesOffset++] = count;
19258        indices[indicesOffset++] = centerIndex;
19259        indices[indicesOffset++] = count - 1;
19260      }
19261      count++;
19262    }
19263    indices[indicesOffset++] = centerIndex + 1;
19264    indices[indicesOffset++] = centerIndex;
19265    indices[indicesOffset++] = count - 1;
19266  }
19267};
19268const buildEllipse = { ...buildCircle, extension: { ...buildCircle.extension, name: "ellipse" } };
19269const buildRoundedRectangle = { ...buildCircle, extension: { ...buildCircle.extension, name: "roundedRectangle" } };
19270
19271const closePointEps = 1e-4;
19272const curveEps = 1e-4;
19273
19274function getOrientationOfPoints(points) {
19275  const m = points.length;
19276  if (m < 6) {
19277    return 1;
19278  }
19279  let area = 0;
19280  for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
19281    const x2 = points[i];
19282    const y2 = points[i + 1];
19283    area += (x2 - x1) * (y2 + y1);
19284    x1 = x2;
19285    y1 = y2;
19286  }
19287  if (area < 0) {
19288    return -1;
19289  }
19290  return 1;
19291}
19292
19293function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
19294  const ix = x - nx * innerWeight;
19295  const iy = y - ny * innerWeight;
19296  const ox = x + nx * outerWeight;
19297  const oy = y + ny * outerWeight;
19298  let exx;
19299  let eyy;
19300  if (clockwise) {
19301    exx = ny;
19302    eyy = -nx;
19303  } else {
19304    exx = -ny;
19305    eyy = nx;
19306  }
19307  const eix = ix + exx;
19308  const eiy = iy + eyy;
19309  const eox = ox + exx;
19310  const eoy = oy + eyy;
19311  verts.push(eix, eiy);
19312  verts.push(eox, eoy);
19313  return 2;
19314}
19315function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
19316  const cx2p0x = sx - cx;
19317  const cy2p0y = sy - cy;
19318  let angle0 = Math.atan2(cx2p0x, cy2p0y);
19319  let angle1 = Math.atan2(ex - cx, ey - cy);
19320  if (clockwise && angle0 < angle1) {
19321    angle0 += Math.PI * 2;
19322  } else if (!clockwise && angle0 > angle1) {
19323    angle1 += Math.PI * 2;
19324  }
19325  let startAngle = angle0;
19326  const angleDiff = angle1 - angle0;
19327  const absAngleDiff = Math.abs(angleDiff);
19328  const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
19329  const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
19330  const angleInc = angleDiff / segCount;
19331  startAngle += angleInc;
19332  if (clockwise) {
19333    verts.push(cx, cy);
19334    verts.push(sx, sy);
19335    for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
19336      verts.push(cx, cy);
19337      verts.push(
19338        cx + Math.sin(angle) * radius,
19339        cy + Math.cos(angle) * radius
19340      );
19341    }
19342    verts.push(cx, cy);
19343    verts.push(ex, ey);
19344  } else {
19345    verts.push(sx, sy);
19346    verts.push(cx, cy);
19347    for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
19348      verts.push(
19349        cx + Math.sin(angle) * radius,
19350        cy + Math.cos(angle) * radius
19351      );
19352      verts.push(cx, cy);
19353    }
19354    verts.push(ex, ey);
19355    verts.push(cx, cy);
19356  }
19357  return segCount * 2;
19358}
19359function buildLine(points, lineStyle, flipAlignment, closed, vertices, indices) {
19360  const eps = closePointEps;
19361  if (points.length === 0) {
19362    return;
19363  }
19364  const style = lineStyle;
19365  let alignment = style.alignment;
19366  if (lineStyle.alignment !== 0.5) {
19367    let orientation = getOrientationOfPoints(points);
19368    alignment = (alignment - 0.5) * orientation + 0.5;
19369  }
19370  const firstPoint = new Point(points[0], points[1]);
19371  const lastPoint = new Point(points[points.length - 2], points[points.length - 1]);
19372  const closedShape = closed;
19373  const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
19374  if (closedShape) {
19375    points = points.slice();
19376    if (closedPath) {
19377      points.pop();
19378      points.pop();
19379      lastPoint.set(points[points.length - 2], points[points.length - 1]);
19380    }
19381    const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
19382    const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
19383    points.unshift(midPointX, midPointY);
19384    points.push(midPointX, midPointY);
19385  }
19386  const verts = vertices;
19387  const length = points.length / 2;
19388  let indexCount = points.length;
19389  const indexStart = verts.length / 2;
19390  const width = style.width / 2;
19391  const widthSquared = width * width;
19392  const miterLimitSquared = style.miterLimit * style.miterLimit;
19393  let x0 = points[0];
19394  let y0 = points[1];
19395  let x1 = points[2];
19396  let y1 = points[3];
19397  let x2 = 0;
19398  let y2 = 0;
19399  let perpX = -(y0 - y1);
19400  let perpY = x0 - x1;
19401  let perp1x = 0;
19402  let perp1y = 0;
19403  let dist = Math.sqrt(perpX * perpX + perpY * perpY);
19404  perpX /= dist;
19405  perpY /= dist;
19406  perpX *= width;
19407  perpY *= width;
19408  const ratio = alignment;
19409  const innerWeight = (1 - ratio) * 2;
19410  const outerWeight = ratio * 2;
19411  if (!closedShape) {
19412    if (style.cap === "round") {
19413      indexCount += round(
19414        x0 - perpX * (innerWeight - outerWeight) * 0.5,
19415        y0 - perpY * (innerWeight - outerWeight) * 0.5,
19416        x0 - perpX * innerWeight,
19417        y0 - perpY * innerWeight,
19418        x0 + perpX * outerWeight,
19419        y0 + perpY * outerWeight,
19420        verts,
19421        true
19422      ) + 2;
19423    } else if (style.cap === "square") {
19424      indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
19425    }
19426  }
19427  verts.push(
19428    x0 - perpX * innerWeight,
19429    y0 - perpY * innerWeight
19430  );
19431  verts.push(
19432    x0 + perpX * outerWeight,
19433    y0 + perpY * outerWeight
19434  );
19435  for (let i = 1; i < length - 1; ++i) {
19436    x0 = points[(i - 1) * 2];
19437    y0 = points[(i - 1) * 2 + 1];
19438    x1 = points[i * 2];
19439    y1 = points[i * 2 + 1];
19440    x2 = points[(i + 1) * 2];
19441    y2 = points[(i + 1) * 2 + 1];
19442    perpX = -(y0 - y1);
19443    perpY = x0 - x1;
19444    dist = Math.sqrt(perpX * perpX + perpY * perpY);
19445    perpX /= dist;
19446    perpY /= dist;
19447    perpX *= width;
19448    perpY *= width;
19449    perp1x = -(y1 - y2);
19450    perp1y = x1 - x2;
19451    dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
19452    perp1x /= dist;
19453    perp1y /= dist;
19454    perp1x *= width;
19455    perp1y *= width;
19456    const dx0 = x1 - x0;
19457    const dy0 = y0 - y1;
19458    const dx1 = x1 - x2;
19459    const dy1 = y2 - y1;
19460    const dot = dx0 * dx1 + dy0 * dy1;
19461    const cross = dy0 * dx1 - dy1 * dx0;
19462    const clockwise = cross < 0;
19463    if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
19464      verts.push(
19465        x1 - perpX * innerWeight,
19466        y1 - perpY * innerWeight
19467      );
19468      verts.push(
19469        x1 + perpX * outerWeight,
19470        y1 + perpY * outerWeight
19471      );
19472      if (dot >= 0) {
19473        if (style.join === "round") {
19474          indexCount += round(
19475            x1,
19476            y1,
19477            x1 - perpX * innerWeight,
19478            y1 - perpY * innerWeight,
19479            x1 - perp1x * innerWeight,
19480            y1 - perp1y * innerWeight,
19481            verts,
19482            false
19483          ) + 4;
19484        } else {
19485          indexCount += 2;
19486        }
19487        verts.push(
19488          x1 - perp1x * outerWeight,
19489          y1 - perp1y * outerWeight
19490        );
19491        verts.push(
19492          x1 + perp1x * innerWeight,
19493          y1 + perp1y * innerWeight
19494        );
19495      }
19496      continue;
19497    }
19498    const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
19499    const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
19500    const px = (dx0 * c2 - dx1 * c1) / cross;
19501    const py = (dy1 * c1 - dy0 * c2) / cross;
19502    const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
19503    const imx = x1 + (px - x1) * innerWeight;
19504    const imy = y1 + (py - y1) * innerWeight;
19505    const omx = x1 - (px - x1) * outerWeight;
19506    const omy = y1 - (py - y1) * outerWeight;
19507    const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
19508    const insideWeight = clockwise ? innerWeight : outerWeight;
19509    const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
19510    const insideMiterOk = pDist <= smallerInsideDiagonalSq;
19511    if (insideMiterOk) {
19512      if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
19513        if (clockwise) {
19514          verts.push(imx, imy);
19515          verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
19516          verts.push(imx, imy);
19517          verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
19518        } else {
19519          verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
19520          verts.push(omx, omy);
19521          verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
19522          verts.push(omx, omy);
19523        }
19524        indexCount += 2;
19525      } else if (style.join === "round") {
19526        if (clockwise) {
19527          verts.push(imx, imy);
19528          verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
19529          indexCount += round(
19530            x1,
19531            y1,
19532            x1 + perpX * outerWeight,
19533            y1 + perpY * outerWeight,
19534            x1 + perp1x * outerWeight,
19535            y1 + perp1y * outerWeight,
19536            verts,
19537            true
19538          ) + 4;
19539          verts.push(imx, imy);
19540          verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
19541        } else {
19542          verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
19543          verts.push(omx, omy);
19544          indexCount += round(
19545            x1,
19546            y1,
19547            x1 - perpX * innerWeight,
19548            y1 - perpY * innerWeight,
19549            x1 - perp1x * innerWeight,
19550            y1 - perp1y * innerWeight,
19551            verts,
19552            false
19553          ) + 4;
19554          verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
19555          verts.push(omx, omy);
19556        }
19557      } else {
19558        verts.push(imx, imy);
19559        verts.push(omx, omy);
19560      }
19561    } else {
19562      verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
19563      verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
19564      if (style.join === "round") {
19565        if (clockwise) {
19566          indexCount += round(
19567            x1,
19568            y1,
19569            x1 + perpX * outerWeight,
19570            y1 + perpY * outerWeight,
19571            x1 + perp1x * outerWeight,
19572            y1 + perp1y * outerWeight,
19573            verts,
19574            true
19575          ) + 2;
19576        } else {
19577          indexCount += round(
19578            x1,
19579            y1,
19580            x1 - perpX * innerWeight,
19581            y1 - perpY * innerWeight,
19582            x1 - perp1x * innerWeight,
19583            y1 - perp1y * innerWeight,
19584            verts,
19585            false
19586          ) + 2;
19587        }
19588      } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
19589        if (clockwise) {
19590          verts.push(omx, omy);
19591          verts.push(omx, omy);
19592        } else {
19593          verts.push(imx, imy);
19594          verts.push(imx, imy);
19595        }
19596        indexCount += 2;
19597      }
19598      verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
19599      verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
19600      indexCount += 2;
19601    }
19602  }
19603  x0 = points[(length - 2) * 2];
19604  y0 = points[(length - 2) * 2 + 1];
19605  x1 = points[(length - 1) * 2];
19606  y1 = points[(length - 1) * 2 + 1];
19607  perpX = -(y0 - y1);
19608  perpY = x0 - x1;
19609  dist = Math.sqrt(perpX * perpX + perpY * perpY);
19610  perpX /= dist;
19611  perpY /= dist;
19612  perpX *= width;
19613  perpY *= width;
19614  verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
19615  verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
19616  if (!closedShape) {
19617    if (style.cap === "round") {
19618      indexCount += round(
19619        x1 - perpX * (innerWeight - outerWeight) * 0.5,
19620        y1 - perpY * (innerWeight - outerWeight) * 0.5,
19621        x1 - perpX * innerWeight,
19622        y1 - perpY * innerWeight,
19623        x1 + perpX * outerWeight,
19624        y1 + perpY * outerWeight,
19625        verts,
19626        false
19627      ) + 2;
19628    } else if (style.cap === "square") {
19629      indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
19630    }
19631  }
19632  const eps2 = curveEps * curveEps;
19633  for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
19634    x0 = verts[i * 2];
19635    y0 = verts[i * 2 + 1];
19636    x1 = verts[(i + 1) * 2];
19637    y1 = verts[(i + 1) * 2 + 1];
19638    x2 = verts[(i + 2) * 2];
19639    y2 = verts[(i + 2) * 2 + 1];
19640    if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
19641      continue;
19642    }
19643    indices.push(i, i + 1, i + 2);
19644  }
19645}
19646
19647function buildPixelLine(points, closed, vertices, indices) {
19648  const eps = closePointEps;
19649  if (points.length === 0) {
19650    return;
19651  }
19652  const fx = points[0];
19653  const fy = points[1];
19654  const lx = points[points.length - 2];
19655  const ly = points[points.length - 1];
19656  const closePath = closed || Math.abs(fx - lx) < eps && Math.abs(fy - ly) < eps;
19657  const verts = vertices;
19658  const length = points.length / 2;
19659  const indexStart = verts.length / 2;
19660  for (let i = 0; i < length; i++) {
19661    verts.push(points[i * 2]);
19662    verts.push(points[i * 2 + 1]);
19663  }
19664  for (let i = 0; i < length - 1; i++) {
19665    indices.push(indexStart + i, indexStart + i + 1);
19666  }
19667  if (closePath) {
19668    indices.push(indexStart + length - 1, indexStart);
19669  }
19670}
19671
19672function triangulateWithHoles(points, holes, vertices, verticesStride, verticesOffset, indices, indicesOffset) {
19673  const triangles = earcut(points, holes, 2);
19674  if (!triangles) {
19675    return;
19676  }
19677  for (let i = 0; i < triangles.length; i += 3) {
19678    indices[indicesOffset++] = triangles[i] + verticesOffset;
19679    indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
19680    indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
19681  }
19682  let index = verticesOffset * verticesStride;
19683  for (let i = 0; i < points.length; i += 2) {
19684    vertices[index] = points[i];
19685    vertices[index + 1] = points[i + 1];
19686    index += verticesStride;
19687  }
19688}
19689
19690const emptyArray = [];
19691const buildPolygon = {
19692  extension: {
19693    type: ExtensionType.ShapeBuilder,
19694    name: "polygon"
19695  },
19696  build(shape, points) {
19697    for (let i = 0; i < shape.points.length; i++) {
19698      points[i] = shape.points[i];
19699    }
19700    return true;
19701  },
19702  triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) {
19703    triangulateWithHoles(points, emptyArray, vertices, verticesStride, verticesOffset, indices, indicesOffset);
19704  }
19705};
19706
19707const buildRectangle = {
19708  extension: {
19709    type: ExtensionType.ShapeBuilder,
19710    name: "rectangle"
19711  },
19712  build(shape, points) {
19713    const rectData = shape;
19714    const x = rectData.x;
19715    const y = rectData.y;
19716    const width = rectData.width;
19717    const height = rectData.height;
19718    if (!(width > 0 && height > 0)) {
19719      return false;
19720    }
19721    points[0] = x;
19722    points[1] = y;
19723    points[2] = x + width;
19724    points[3] = y;
19725    points[4] = x + width;
19726    points[5] = y + height;
19727    points[6] = x;
19728    points[7] = y + height;
19729    return true;
19730  },
19731  triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) {
19732    let count = 0;
19733    verticesOffset *= verticesStride;
19734    vertices[verticesOffset + count] = points[0];
19735    vertices[verticesOffset + count + 1] = points[1];
19736    count += verticesStride;
19737    vertices[verticesOffset + count] = points[2];
19738    vertices[verticesOffset + count + 1] = points[3];
19739    count += verticesStride;
19740    vertices[verticesOffset + count] = points[6];
19741    vertices[verticesOffset + count + 1] = points[7];
19742    count += verticesStride;
19743    vertices[verticesOffset + count] = points[4];
19744    vertices[verticesOffset + count + 1] = points[5];
19745    count += verticesStride;
19746    const verticesIndex = verticesOffset / verticesStride;
19747    indices[indicesOffset++] = verticesIndex;
19748    indices[indicesOffset++] = verticesIndex + 1;
19749    indices[indicesOffset++] = verticesIndex + 2;
19750    indices[indicesOffset++] = verticesIndex + 1;
19751    indices[indicesOffset++] = verticesIndex + 3;
19752    indices[indicesOffset++] = verticesIndex + 2;
19753  }
19754};
19755
19756const buildTriangle = {
19757  extension: {
19758    type: ExtensionType.ShapeBuilder,
19759    name: "triangle"
19760  },
19761  build(shape, points) {
19762    points[0] = shape.x;
19763    points[1] = shape.y;
19764    points[2] = shape.x2;
19765    points[3] = shape.y2;
19766    points[4] = shape.x3;
19767    points[5] = shape.y3;
19768    return true;
19769  },
19770  triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) {
19771    let count = 0;
19772    verticesOffset *= verticesStride;
19773    vertices[verticesOffset + count] = points[0];
19774    vertices[verticesOffset + count + 1] = points[1];
19775    count += verticesStride;
19776    vertices[verticesOffset + count] = points[2];
19777    vertices[verticesOffset + count + 1] = points[3];
19778    count += verticesStride;
19779    vertices[verticesOffset + count] = points[4];
19780    vertices[verticesOffset + count + 1] = points[5];
19781    const verticesIndex = verticesOffset / verticesStride;
19782    indices[indicesOffset++] = verticesIndex;
19783    indices[indicesOffset++] = verticesIndex + 1;
19784    indices[indicesOffset++] = verticesIndex + 2;
19785  }
19786};
19787
19788const tempTextureMatrix$1 = new Matrix();
19789const tempRect$1 = new Rectangle();
19790function generateTextureMatrix(out, style, shape, matrix) {
19791  const textureMatrix = style.matrix ? out.copyFrom(style.matrix).invert() : out.identity();
19792  if (style.textureSpace === "local") {
19793    const bounds = shape.getBounds(tempRect$1);
19794    if (style.width) {
19795      bounds.pad(style.width);
19796    }
19797    const { x: tx, y: ty } = bounds;
19798    const sx = 1 / bounds.width;
19799    const sy = 1 / bounds.height;
19800    const mTx = -tx * sx;
19801    const mTy = -ty * sy;
19802    const a1 = textureMatrix.a;
19803    const b1 = textureMatrix.b;
19804    const c1 = textureMatrix.c;
19805    const d1 = textureMatrix.d;
19806    textureMatrix.a *= sx;
19807    textureMatrix.b *= sx;
19808    textureMatrix.c *= sy;
19809    textureMatrix.d *= sy;
19810    textureMatrix.tx = mTx * a1 + mTy * c1 + textureMatrix.tx;
19811    textureMatrix.ty = mTx * b1 + mTy * d1 + textureMatrix.ty;
19812  } else {
19813    textureMatrix.translate(style.texture.frame.x, style.texture.frame.y);
19814    textureMatrix.scale(1 / style.texture.source.width, 1 / style.texture.source.height);
19815  }
19816  const sourceStyle = style.texture.source.style;
19817  if (!(style.fill instanceof FillGradient) && sourceStyle.addressMode === "clamp-to-edge") {
19818    sourceStyle.addressMode = "repeat";
19819    sourceStyle.update();
19820  }
19821  if (matrix) {
19822    textureMatrix.append(tempTextureMatrix$1.copyFrom(matrix).invert());
19823  }
19824  return textureMatrix;
19825}
19826
19827const shapeBuilders = {};
19828extensions.handleByMap(ExtensionType.ShapeBuilder, shapeBuilders);
19829extensions.add(buildRectangle, buildPolygon, buildTriangle, buildCircle, buildEllipse, buildRoundedRectangle);
19830const tempRect = new Rectangle();
19831const tempTextureMatrix = new Matrix();
19832function buildContextBatches(context, gpuContext) {
19833  const { geometryData, batches } = gpuContext;
19834  batches.length = 0;
19835  geometryData.indices.length = 0;
19836  geometryData.vertices.length = 0;
19837  geometryData.uvs.length = 0;
19838  for (let i = 0; i < context.instructions.length; i++) {
19839    const instruction = context.instructions[i];
19840    if (instruction.action === "texture") {
19841      addTextureToGeometryData(instruction.data, batches, geometryData);
19842    } else if (instruction.action === "fill" || instruction.action === "stroke") {
19843      const isStroke = instruction.action === "stroke";
19844      const shapePath = instruction.data.path.shapePath;
19845      const style = instruction.data.style;
19846      const hole = instruction.data.hole;
19847      if (isStroke && hole) {
19848        addShapePathToGeometryData(hole.shapePath, style, true, batches, geometryData);
19849      }
19850      if (hole) {
19851        shapePath.shapePrimitives[shapePath.shapePrimitives.length - 1].holes = hole.shapePath.shapePrimitives;
19852      }
19853      addShapePathToGeometryData(shapePath, style, isStroke, batches, geometryData);
19854    }
19855  }
19856}
19857function addTextureToGeometryData(data, batches, geometryData) {
19858  const points = [];
19859  const build = shapeBuilders.rectangle;
19860  const rect = tempRect;
19861  rect.x = data.dx;
19862  rect.y = data.dy;
19863  rect.width = data.dw;
19864  rect.height = data.dh;
19865  const matrix = data.transform;
19866  if (!build.build(rect, points)) {
19867    return;
19868  }
19869  const { vertices, uvs, indices } = geometryData;
19870  const indexOffset = indices.length;
19871  const vertOffset = vertices.length / 2;
19872  if (matrix) {
19873    transformVertices(points, matrix);
19874  }
19875  build.triangulate(points, vertices, 2, vertOffset, indices, indexOffset);
19876  const texture = data.image;
19877  const textureUvs = texture.uvs;
19878  uvs.push(
19879    textureUvs.x0,
19880    textureUvs.y0,
19881    textureUvs.x1,
19882    textureUvs.y1,
19883    textureUvs.x3,
19884    textureUvs.y3,
19885    textureUvs.x2,
19886    textureUvs.y2
19887  );
19888  const graphicsBatch = BigPool.get(BatchableGraphics);
19889  graphicsBatch.indexOffset = indexOffset;
19890  graphicsBatch.indexSize = indices.length - indexOffset;
19891  graphicsBatch.attributeOffset = vertOffset;
19892  graphicsBatch.attributeSize = vertices.length / 2 - vertOffset;
19893  graphicsBatch.baseColor = data.style;
19894  graphicsBatch.alpha = data.alpha;
19895  graphicsBatch.texture = texture;
19896  graphicsBatch.geometryData = geometryData;
19897  batches.push(graphicsBatch);
19898}
19899function addShapePathToGeometryData(shapePath, style, isStroke, batches, geometryData) {
19900  const { vertices, uvs, indices } = geometryData;
19901  shapePath.shapePrimitives.forEach(({ shape, transform: matrix, holes }) => {
19902    const points = [];
19903    const build = shapeBuilders[shape.type];
19904    if (!build.build(shape, points)) {
19905      return;
19906    }
19907    const indexOffset = indices.length;
19908    const vertOffset = vertices.length / 2;
19909    let topology = "triangle-list";
19910    if (matrix) {
19911      transformVertices(points, matrix);
19912    }
19913    if (!isStroke) {
19914      if (holes) {
19915        const holeIndices = [];
19916        const otherPoints = points.slice();
19917        const holeArrays = getHoleArrays(holes);
19918        holeArrays.forEach((holePoints) => {
19919          holeIndices.push(otherPoints.length / 2);
19920          otherPoints.push(...holePoints);
19921        });
19922        triangulateWithHoles(otherPoints, holeIndices, vertices, 2, vertOffset, indices, indexOffset);
19923      } else {
19924        build.triangulate(points, vertices, 2, vertOffset, indices, indexOffset);
19925      }
19926    } else {
19927      const close = shape.closePath ?? true;
19928      const lineStyle = style;
19929      if (!lineStyle.pixelLine) {
19930        buildLine(points, lineStyle, false, close, vertices, indices);
19931      } else {
19932        buildPixelLine(points, close, vertices, indices);
19933        topology = "line-list";
19934      }
19935    }
19936    const uvsOffset = uvs.length / 2;
19937    const texture = style.texture;
19938    if (texture !== Texture.WHITE) {
19939      const textureMatrix = generateTextureMatrix(tempTextureMatrix, style, shape, matrix);
19940      buildUvs(vertices, 2, vertOffset, uvs, uvsOffset, 2, vertices.length / 2 - vertOffset, textureMatrix);
19941    } else {
19942      buildSimpleUvs(uvs, uvsOffset, 2, vertices.length / 2 - vertOffset);
19943    }
19944    const graphicsBatch = BigPool.get(BatchableGraphics);
19945    graphicsBatch.indexOffset = indexOffset;
19946    graphicsBatch.indexSize = indices.length - indexOffset;
19947    graphicsBatch.attributeOffset = vertOffset;
19948    graphicsBatch.attributeSize = vertices.length / 2 - vertOffset;
19949    graphicsBatch.baseColor = style.color;
19950    graphicsBatch.alpha = style.alpha;
19951    graphicsBatch.texture = texture;
19952    graphicsBatch.geometryData = geometryData;
19953    graphicsBatch.topology = topology;
19954    batches.push(graphicsBatch);
19955  });
19956}
19957function getHoleArrays(holePrimitives) {
19958  const holeArrays = [];
19959  for (let k = 0; k < holePrimitives.length; k++) {
19960    const holePrimitive = holePrimitives[k].shape;
19961    const holePoints = [];
19962    const holeBuilder = shapeBuilders[holePrimitive.type];
19963    if (holeBuilder.build(holePrimitive, holePoints)) {
19964      holeArrays.push(holePoints);
19965    }
19966  }
19967  return holeArrays;
19968}
19969
19970class GpuGraphicsContext {
19971  constructor() {
19972    this.batches = [];
19973    this.geometryData = {
19974      vertices: [],
19975      uvs: [],
19976      indices: []
19977    };
19978  }
19979  reset() {
19980    if (this.batches) {
19981      this.batches.forEach((batch) => {
19982        BigPool.return(batch);
19983      });
19984    }
19985    if (this.graphicsData) {
19986      BigPool.return(this.graphicsData);
19987    }
19988    this.isBatchable = false;
19989    this.context = null;
19990    this.batches.length = 0;
19991    this.geometryData.indices.length = 0;
19992    this.geometryData.vertices.length = 0;
19993    this.geometryData.uvs.length = 0;
19994    this.graphicsData = null;
19995  }
19996  destroy() {
19997    this.reset();
19998    this.batches = null;
19999    this.geometryData = null;
20000  }
20001}
20002class GraphicsContextRenderData {
20003  constructor() {
20004    this.instructions = new InstructionSet();
20005  }
20006  init(options) {
20007    const maxTextures = options.maxTextures;
20008    this.batcher ? this.batcher._updateMaxTextures(maxTextures) : this.batcher = new DefaultBatcher({ maxTextures });
20009    this.instructions.reset();
20010  }
20011  /**
20012   * @deprecated since version 8.0.0
20013   * Use `batcher.geometry` instead.
20014   * @see {Batcher#geometry}
20015   */
20016  get geometry() {
20017    deprecation(v8_3_4, "GraphicsContextRenderData#geometry is deprecated, please use batcher.geometry instead.");
20018    return this.batcher.geometry;
20019  }
20020  destroy() {
20021    this.batcher.destroy();
20022    this.instructions.destroy();
20023    this.batcher = null;
20024    this.instructions = null;
20025  }
20026}
20027const _GraphicsContextSystem = class _GraphicsContextSystem {
20028  constructor(renderer) {
20029    this._renderer = renderer;
20030    this._managedContexts = new GCManagedHash({ renderer, type: "resource", name: "graphicsContext" });
20031  }
20032  /**
20033   * Runner init called, update the default options
20034   * @ignore
20035   */
20036  init(options) {
20037    _GraphicsContextSystem.defaultOptions.bezierSmoothness = options?.bezierSmoothness ?? _GraphicsContextSystem.defaultOptions.bezierSmoothness;
20038  }
20039  /**
20040   * Returns the render data for a given GraphicsContext.
20041   * @param context - The GraphicsContext to get the render data for.
20042   * @internal
20043   */
20044  getContextRenderData(context) {
20045    return context._gpuData[this._renderer.uid].graphicsData || this._initContextRenderData(context);
20046  }
20047  /**
20048   * Updates the GPU context for a given GraphicsContext.
20049   * If the context is dirty, it will rebuild the batches and geometry data.
20050   * @param context - The GraphicsContext to update.
20051   * @returns The updated GpuGraphicsContext.
20052   * @internal
20053   */
20054  updateGpuContext(context) {
20055    const hasContext = !!context._gpuData[this._renderer.uid];
20056    const gpuContext = context._gpuData[this._renderer.uid] || this._initContext(context);
20057    if (context.dirty || !hasContext) {
20058      if (hasContext) {
20059        gpuContext.reset();
20060      }
20061      buildContextBatches(context, gpuContext);
20062      const batchMode = context.batchMode;
20063      if (context.customShader || batchMode === "no-batch") {
20064        gpuContext.isBatchable = false;
20065      } else if (batchMode === "auto") {
20066        gpuContext.isBatchable = gpuContext.geometryData.vertices.length < 400;
20067      } else {
20068        gpuContext.isBatchable = true;
20069      }
20070      context.dirty = false;
20071    }
20072    return gpuContext;
20073  }
20074  /**
20075   * Returns the GpuGraphicsContext for a given GraphicsContext.
20076   * If it does not exist, it will initialize a new one.
20077   * @param context - The GraphicsContext to get the GpuGraphicsContext for.
20078   * @returns The GpuGraphicsContext for the given GraphicsContext.
20079   * @internal
20080   */
20081  getGpuContext(context) {
20082    return context._gpuData[this._renderer.uid] || this._initContext(context);
20083  }
20084  _initContextRenderData(context) {
20085    const graphicsData = BigPool.get(GraphicsContextRenderData, {
20086      maxTextures: this._renderer.limits.maxBatchableTextures
20087    });
20088    const gpuContext = context._gpuData[this._renderer.uid];
20089    const { batches, geometryData } = gpuContext;
20090    gpuContext.graphicsData = graphicsData;
20091    const vertexSize = geometryData.vertices.length;
20092    const indexSize = geometryData.indices.length;
20093    for (let i = 0; i < batches.length; i++) {
20094      batches[i].applyTransform = false;
20095    }
20096    const batcher = graphicsData.batcher;
20097    batcher.ensureAttributeBuffer(vertexSize);
20098    batcher.ensureIndexBuffer(indexSize);
20099    batcher.begin();
20100    for (let i = 0; i < batches.length; i++) {
20101      const batch = batches[i];
20102      batcher.add(batch);
20103    }
20104    batcher.finish(graphicsData.instructions);
20105    const geometry = batcher.geometry;
20106    geometry.indexBuffer.setDataWithSize(batcher.indexBuffer, batcher.indexSize, true);
20107    geometry.buffers[0].setDataWithSize(batcher.attributeBuffer.float32View, batcher.attributeSize, true);
20108    const drawBatches = batcher.batches;
20109    for (let i = 0; i < drawBatches.length; i++) {
20110      const batch = drawBatches[i];
20111      batch.bindGroup = getTextureBatchBindGroup(
20112        batch.textures.textures,
20113        batch.textures.count,
20114        this._renderer.limits.maxBatchableTextures
20115      );
20116    }
20117    return graphicsData;
20118  }
20119  _initContext(context) {
20120    const gpuContext = new GpuGraphicsContext();
20121    gpuContext.context = context;
20122    context._gpuData[this._renderer.uid] = gpuContext;
20123    this._managedContexts.add(context);
20124    return gpuContext;
20125  }
20126  destroy() {
20127    this._managedContexts.destroy();
20128    this._renderer = null;
20129  }
20130};
20131/** @ignore */
20132_GraphicsContextSystem.extension = {
20133  type: [
20134    ExtensionType.WebGLSystem,
20135    ExtensionType.WebGPUSystem,
20136    ExtensionType.CanvasSystem
20137  ],
20138  name: "graphicsContext"
20139};
20140/** The default options for the GraphicsContextSystem. */
20141_GraphicsContextSystem.defaultOptions = {
20142  /**
20143   * A value from 0 to 1 that controls the smoothness of bezier curves (the higher the smoother)
20144   * @default 0.5
20145   */
20146  bezierSmoothness: 0.5
20147};
20148let GraphicsContextSystem = _GraphicsContextSystem;
20149
20150const RECURSION_LIMIT$1 = 8;
20151const FLT_EPSILON$1 = 11920929e-14;
20152const PATH_DISTANCE_EPSILON$1 = 1;
20153function buildAdaptiveBezier(points, sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, smoothness) {
20154  const scale = 1;
20155  const smoothing = Math.min(
20156    0.99,
20157    // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
20158    Math.max(0, smoothness ?? GraphicsContextSystem.defaultOptions.bezierSmoothness)
20159  );
20160  let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
20161  distanceTolerance *= distanceTolerance;
20162  begin$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance);
20163  return points;
20164}
20165function begin$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance) {
20166  recursive$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance, 0);
20167  points.push(eX, eY);
20168}
20169function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
20170  if (level > RECURSION_LIMIT$1) {
20171    return;
20172  }
20173  const x12 = (x1 + x2) / 2;
20174  const y12 = (y1 + y2) / 2;
20175  const x23 = (x2 + x3) / 2;
20176  const y23 = (y2 + y3) / 2;
20177  const x34 = (x3 + x4) / 2;
20178  const y34 = (y3 + y4) / 2;
20179  const x123 = (x12 + x23) / 2;
20180  const y123 = (y12 + y23) / 2;
20181  const x234 = (x23 + x34) / 2;
20182  const y234 = (y23 + y34) / 2;
20183  const x1234 = (x123 + x234) / 2;
20184  const y1234 = (y123 + y234) / 2;
20185  if (level > 0) {
20186    let dx = x4 - x1;
20187    let dy = y4 - y1;
20188    const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
20189    const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
20190    if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
20191      if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
20192        {
20193          points.push(x1234, y1234);
20194          return;
20195        }
20196      }
20197    } else if (d2 > FLT_EPSILON$1) {
20198      if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
20199        {
20200          points.push(x1234, y1234);
20201          return;
20202        }
20203      }
20204    } else if (d3 > FLT_EPSILON$1) {
20205      if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
20206        {
20207          points.push(x1234, y1234);
20208          return;
20209        }
20210      }
20211    } else {
20212      dx = x1234 - (x1 + x4) / 2;
20213      dy = y1234 - (y1 + y4) / 2;
20214      if (dx * dx + dy * dy <= distanceTolerance) {
20215        points.push(x1234, y1234);
20216        return;
20217      }
20218    }
20219  }
20220  recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
20221  recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
20222}
20223
20224const RECURSION_LIMIT = 8;
20225const FLT_EPSILON = 11920929e-14;
20226const PATH_DISTANCE_EPSILON = 1;
20227function buildAdaptiveQuadratic(points, sX, sY, cp1x, cp1y, eX, eY, smoothness) {
20228  const scale = 1;
20229  const smoothing = Math.min(
20230    0.99,
20231    // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
20232    Math.max(0, smoothness ?? GraphicsContextSystem.defaultOptions.bezierSmoothness)
20233  );
20234  let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
20235  distanceTolerance *= distanceTolerance;
20236  begin(sX, sY, cp1x, cp1y, eX, eY, points, distanceTolerance);
20237  return points;
20238}
20239function begin(sX, sY, cp1x, cp1y, eX, eY, points, distanceTolerance) {
20240  recursive(points, sX, sY, cp1x, cp1y, eX, eY, distanceTolerance, 0);
20241  points.push(eX, eY);
20242}
20243function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
20244  if (level > RECURSION_LIMIT) {
20245    return;
20246  }
20247  const x12 = (x1 + x2) / 2;
20248  const y12 = (y1 + y2) / 2;
20249  const x23 = (x2 + x3) / 2;
20250  const y23 = (y2 + y3) / 2;
20251  const x123 = (x12 + x23) / 2;
20252  const y123 = (y12 + y23) / 2;
20253  let dx = x3 - x1;
20254  let dy = y3 - y1;
20255  const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
20256  if (d > FLT_EPSILON) {
20257    if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
20258      {
20259        points.push(x123, y123);
20260        return;
20261      }
20262    }
20263  } else {
20264    dx = x123 - (x1 + x3) / 2;
20265    dy = y123 - (y1 + y3) / 2;
20266    if (dx * dx + dy * dy <= distanceTolerance) {
20267      points.push(x123, y123);
20268      return;
20269    }
20270  }
20271  recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
20272  recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
20273}
20274
20275function buildArc(points, x, y, radius, start, end, clockwise, steps) {
20276  let dist = Math.abs(start - end);
20277  if (!clockwise && start > end) {
20278    dist = 2 * Math.PI - dist;
20279  } else if (clockwise && end > start) {
20280    dist = 2 * Math.PI - dist;
20281  }
20282  steps || (steps = Math.max(6, Math.floor(6 * Math.pow(radius, 1 / 3) * (dist / Math.PI))));
20283  steps = Math.max(steps, 3);
20284  let f = dist / steps;
20285  let t = start;
20286  f *= clockwise ? -1 : 1;
20287  for (let i = 0; i < steps + 1; i++) {
20288    const cs = Math.cos(t);
20289    const sn = Math.sin(t);
20290    const nx = x + cs * radius;
20291    const ny = y + sn * radius;
20292    points.push(nx, ny);
20293    t += f;
20294  }
20295}
20296
20297function buildArcTo(points, x1, y1, x2, y2, radius) {
20298  const fromX = points[points.length - 2];
20299  const fromY = points[points.length - 1];
20300  const a1 = fromY - y1;
20301  const b1 = fromX - x1;
20302  const a2 = y2 - y1;
20303  const b2 = x2 - x1;
20304  const mm = Math.abs(a1 * b2 - b1 * a2);
20305  if (mm < 1e-8 || radius === 0) {
20306    if (points[points.length - 2] !== x1 || points[points.length - 1] !== y1) {
20307      points.push(x1, y1);
20308    }
20309    return;
20310  }
20311  const dd = a1 * a1 + b1 * b1;
20312  const cc = a2 * a2 + b2 * b2;
20313  const tt = a1 * a2 + b1 * b2;
20314  const k1 = radius * Math.sqrt(dd) / mm;
20315  const k2 = radius * Math.sqrt(cc) / mm;
20316  const j1 = k1 * tt / dd;
20317  const j2 = k2 * tt / cc;
20318  const cx = k1 * b2 + k2 * b1;
20319  const cy = k1 * a2 + k2 * a1;
20320  const px = b1 * (k2 + j1);
20321  const py = a1 * (k2 + j1);
20322  const qx = b2 * (k1 + j2);
20323  const qy = a2 * (k1 + j2);
20324  const startAngle = Math.atan2(py - cy, px - cx);
20325  const endAngle = Math.atan2(qy - cy, qx - cx);
20326  buildArc(
20327    points,
20328    cx + x1,
20329    cy + y1,
20330    radius,
20331    startAngle,
20332    endAngle,
20333    b1 * a2 > b2 * a1
20334  );
20335}
20336
20337const TAU = Math.PI * 2;
20338const out = {
20339  centerX: 0,
20340  centerY: 0,
20341  ang1: 0,
20342  ang2: 0
20343};
20344const mapToEllipse = ({ x, y }, rx, ry, cosPhi, sinPhi, centerX, centerY, out2) => {
20345  x *= rx;
20346  y *= ry;
20347  const xp = cosPhi * x - sinPhi * y;
20348  const yp = sinPhi * x + cosPhi * y;
20349  out2.x = xp + centerX;
20350  out2.y = yp + centerY;
20351  return out2;
20352};
20353function approxUnitArc(ang1, ang2) {
20354  const a1 = ang2 === -1.5707963267948966 ? -0.551915024494 : 4 / 3 * Math.tan(ang2 / 4);
20355  const a = ang2 === 1.5707963267948966 ? 0.551915024494 : a1;
20356  const x1 = Math.cos(ang1);
20357  const y1 = Math.sin(ang1);
20358  const x2 = Math.cos(ang1 + ang2);
20359  const y2 = Math.sin(ang1 + ang2);
20360  return [
20361    {
20362      x: x1 - y1 * a,
20363      y: y1 + x1 * a
20364    },
20365    {
20366      x: x2 + y2 * a,
20367      y: y2 - x2 * a
20368    },
20369    {
20370      x: x2,
20371      y: y2
20372    }
20373  ];
20374}
20375const vectorAngle = (ux, uy, vx, vy) => {
20376  const sign = ux * vy - uy * vx < 0 ? -1 : 1;
20377  let dot = ux * vx + uy * vy;
20378  if (dot > 1) {
20379    dot = 1;
20380  }
20381  if (dot < -1) {
20382    dot = -1;
20383  }
20384  return sign * Math.acos(dot);
20385};
20386const getArcCenter = (px, py, cx, cy, rx, ry, largeArcFlag, sweepFlag, sinPhi, cosPhi, pxp, pyp, out2) => {
20387  const rxSq = Math.pow(rx, 2);
20388  const rySq = Math.pow(ry, 2);
20389  const pxpSq = Math.pow(pxp, 2);
20390  const pypSq = Math.pow(pyp, 2);
20391  let radicant = rxSq * rySq - rxSq * pypSq - rySq * pxpSq;
20392  if (radicant < 0) {
20393    radicant = 0;
20394  }
20395  radicant /= rxSq * pypSq + rySq * pxpSq;
20396  radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1);
20397  const centerXp = radicant * rx / ry * pyp;
20398  const centerYp = radicant * -ry / rx * pxp;
20399  const centerX = cosPhi * centerXp - sinPhi * centerYp + (px + cx) / 2;
20400  const centerY = sinPhi * centerXp + cosPhi * centerYp + (py + cy) / 2;
20401  const vx1 = (pxp - centerXp) / rx;
20402  const vy1 = (pyp - centerYp) / ry;
20403  const vx2 = (-pxp - centerXp) / rx;
20404  const vy2 = (-pyp - centerYp) / ry;
20405  const ang1 = vectorAngle(1, 0, vx1, vy1);
20406  let ang2 = vectorAngle(vx1, vy1, vx2, vy2);
20407  if (sweepFlag === 0 && ang2 > 0) {
20408    ang2 -= TAU;
20409  }
20410  if (sweepFlag === 1 && ang2 < 0) {
20411    ang2 += TAU;
20412  }
20413  out2.centerX = centerX;
20414  out2.centerY = centerY;
20415  out2.ang1 = ang1;
20416  out2.ang2 = ang2;
20417};
20418function buildArcToSvg(points, px, py, cx, cy, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0) {
20419  if (rx === 0 || ry === 0) {
20420    return;
20421  }
20422  const sinPhi = Math.sin(xAxisRotation * TAU / 360);
20423  const cosPhi = Math.cos(xAxisRotation * TAU / 360);
20424  const pxp = cosPhi * (px - cx) / 2 + sinPhi * (py - cy) / 2;
20425  const pyp = -sinPhi * (px - cx) / 2 + cosPhi * (py - cy) / 2;
20426  if (pxp === 0 && pyp === 0) {
20427    return;
20428  }
20429  rx = Math.abs(rx);
20430  ry = Math.abs(ry);
20431  const lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2);
20432  if (lambda > 1) {
20433    rx *= Math.sqrt(lambda);
20434    ry *= Math.sqrt(lambda);
20435  }
20436  getArcCenter(
20437    px,
20438    py,
20439    cx,
20440    cy,
20441    rx,
20442    ry,
20443    largeArcFlag,
20444    sweepFlag,
20445    sinPhi,
20446    cosPhi,
20447    pxp,
20448    pyp,
20449    out
20450  );
20451  let { ang1, ang2 } = out;
20452  const { centerX, centerY } = out;
20453  let ratio = Math.abs(ang2) / (TAU / 4);
20454  if (Math.abs(1 - ratio) < 1e-7) {
20455    ratio = 1;
20456  }
20457  const segments = Math.max(Math.ceil(ratio), 1);
20458  ang2 /= segments;
20459  let lastX = points[points.length - 2];
20460  let lastY = points[points.length - 1];
20461  const outCurvePoint = { x: 0, y: 0 };
20462  for (let i = 0; i < segments; i++) {
20463    const curve = approxUnitArc(ang1, ang2);
20464    const { x: x1, y: y1 } = mapToEllipse(curve[0], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint);
20465    const { x: x2, y: y2 } = mapToEllipse(curve[1], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint);
20466    const { x, y } = mapToEllipse(curve[2], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint);
20467    buildAdaptiveBezier(
20468      points,
20469      lastX,
20470      lastY,
20471      x1,
20472      y1,
20473      x2,
20474      y2,
20475      x,
20476      y
20477    );
20478    lastX = x;
20479    lastY = y;
20480    ang1 += ang2;
20481  }
20482}
20483
20484function roundedShapeArc(g, points, radius) {
20485  const vecFrom = (p, pp) => {
20486    const x = pp.x - p.x;
20487    const y = pp.y - p.y;
20488    const len = Math.sqrt(x * x + y * y);
20489    const nx = x / len;
20490    const ny = y / len;
20491    return { len, nx, ny };
20492  };
20493  const sharpCorner = (i, p) => {
20494    if (i === 0) {
20495      g.moveTo(p.x, p.y);
20496    } else {
20497      g.lineTo(p.x, p.y);
20498    }
20499  };
20500  let p1 = points[points.length - 1];
20501  for (let i = 0; i < points.length; i++) {
20502    const p2 = points[i % points.length];
20503    const pRadius = p2.radius ?? radius;
20504    if (pRadius <= 0) {
20505      sharpCorner(i, p2);
20506      p1 = p2;
20507      continue;
20508    }
20509    const p3 = points[(i + 1) % points.length];
20510    const v1 = vecFrom(p2, p1);
20511    const v2 = vecFrom(p2, p3);
20512    if (v1.len < 1e-4 || v2.len < 1e-4) {
20513      sharpCorner(i, p2);
20514      p1 = p2;
20515      continue;
20516    }
20517    let angle = Math.asin(v1.nx * v2.ny - v1.ny * v2.nx);
20518    let radDirection = 1;
20519    let drawDirection = false;
20520    if (v1.nx * v2.nx - v1.ny * -v2.ny < 0) {
20521      if (angle < 0) {
20522        angle = Math.PI + angle;
20523      } else {
20524        angle = Math.PI - angle;
20525        radDirection = -1;
20526        drawDirection = true;
20527      }
20528    } else if (angle > 0) {
20529      radDirection = -1;
20530      drawDirection = true;
20531    }
20532    const halfAngle = angle / 2;
20533    let cRadius;
20534    let lenOut = Math.abs(
20535      Math.cos(halfAngle) * pRadius / Math.sin(halfAngle)
20536    );
20537    if (lenOut > Math.min(v1.len / 2, v2.len / 2)) {
20538      lenOut = Math.min(v1.len / 2, v2.len / 2);
20539      cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
20540    } else {
20541      cRadius = pRadius;
20542    }
20543    const cX = p2.x + v2.nx * lenOut + -v2.ny * cRadius * radDirection;
20544    const cY = p2.y + v2.ny * lenOut + v2.nx * cRadius * radDirection;
20545    const startAngle = Math.atan2(v1.ny, v1.nx) + Math.PI / 2 * radDirection;
20546    const endAngle = Math.atan2(v2.ny, v2.nx) - Math.PI / 2 * radDirection;
20547    if (i === 0) {
20548      g.moveTo(
20549        cX + Math.cos(startAngle) * cRadius,
20550        cY + Math.sin(startAngle) * cRadius
20551      );
20552    }
20553    g.arc(cX, cY, cRadius, startAngle, endAngle, drawDirection);
20554    p1 = p2;
20555  }
20556}
20557function roundedShapeQuadraticCurve(g, points, radius, smoothness) {
20558  const distance = (p1, p2) => Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
20559  const pointLerp = (p1, p2, t) => ({
20560    x: p1.x + (p2.x - p1.x) * t,
20561    y: p1.y + (p2.y - p1.y) * t
20562  });
20563  const numPoints = points.length;
20564  for (let i = 0; i < numPoints; i++) {
20565    const thisPoint = points[(i + 1) % numPoints];
20566    const pRadius = thisPoint.radius ?? radius;
20567    if (pRadius <= 0) {
20568      if (i === 0) {
20569        g.moveTo(thisPoint.x, thisPoint.y);
20570      } else {
20571        g.lineTo(thisPoint.x, thisPoint.y);
20572      }
20573      continue;
20574    }
20575    const lastPoint = points[i];
20576    const nextPoint = points[(i + 2) % numPoints];
20577    const lastEdgeLength = distance(lastPoint, thisPoint);
20578    let start;
20579    if (lastEdgeLength < 1e-4) {
20580      start = thisPoint;
20581    } else {
20582      const lastOffsetDistance = Math.min(lastEdgeLength / 2, pRadius);
20583      start = pointLerp(
20584        thisPoint,
20585        lastPoint,
20586        lastOffsetDistance / lastEdgeLength
20587      );
20588    }
20589    const nextEdgeLength = distance(nextPoint, thisPoint);
20590    let end;
20591    if (nextEdgeLength < 1e-4) {
20592      end = thisPoint;
20593    } else {
20594      const nextOffsetDistance = Math.min(nextEdgeLength / 2, pRadius);
20595      end = pointLerp(
20596        thisPoint,
20597        nextPoint,
20598        nextOffsetDistance / nextEdgeLength
20599      );
20600    }
20601    if (i === 0) {
20602      g.moveTo(start.x, start.y);
20603    } else {
20604      g.lineTo(start.x, start.y);
20605    }
20606    g.quadraticCurveTo(thisPoint.x, thisPoint.y, end.x, end.y, smoothness);
20607  }
20608}
20609
20610const tempRectangle = new Rectangle();
20611class ShapePath {
20612  constructor(graphicsPath2D) {
20613    /** The list of shape primitives that make up the path. */
20614    this.shapePrimitives = [];
20615    this._currentPoly = null;
20616    this._bounds = new Bounds();
20617    this._graphicsPath2D = graphicsPath2D;
20618    this.signed = graphicsPath2D.checkForHoles;
20619  }
20620  /**
20621   * Sets the starting point for a new sub-path. Any subsequent drawing commands are considered part of this path.
20622   * @param x - The x-coordinate for the starting point.
20623   * @param y - The y-coordinate for the starting point.
20624   * @returns The instance of the current object for chaining.
20625   */
20626  moveTo(x, y) {
20627    this.startPoly(x, y);
20628    return this;
20629  }
20630  /**
20631   * Connects the current point to a new point with a straight line. This method updates the current path.
20632   * @param x - The x-coordinate of the new point to connect to.
20633   * @param y - The y-coordinate of the new point to connect to.
20634   * @returns The instance of the current object for chaining.
20635   */
20636  lineTo(x, y) {
20637    this._ensurePoly();
20638    const points = this._currentPoly.points;
20639    const fromX = points[points.length - 2];
20640    const fromY = points[points.length - 1];
20641    if (fromX !== x || fromY !== y) {
20642      points.push(x, y);
20643    }
20644    return this;
20645  }
20646  /**
20647   * Adds an arc to the path. The arc is centered at (x, y)
20648   *  position with radius `radius` starting at `startAngle` and ending at `endAngle`.
20649   * @param x - The x-coordinate of the arc's center.
20650   * @param y - The y-coordinate of the arc's center.
20651   * @param radius - The radius of the arc.
20652   * @param startAngle - The starting angle of the arc, in radians.
20653   * @param endAngle - The ending angle of the arc, in radians.
20654   * @param counterclockwise - Specifies whether the arc should be drawn in the anticlockwise direction. False by default.
20655   * @returns The instance of the current object for chaining.
20656   */
20657  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
20658    this._ensurePoly(false);
20659    const points = this._currentPoly.points;
20660    buildArc(points, x, y, radius, startAngle, endAngle, counterclockwise);
20661    return this;
20662  }
20663  /**
20664   * Adds an arc to the path with the arc tangent to the line joining two specified points.
20665   * The arc radius is specified by `radius`.
20666   * @param x1 - The x-coordinate of the first point.
20667   * @param y1 - The y-coordinate of the first point.
20668   * @param x2 - The x-coordinate of the second point.
20669   * @param y2 - The y-coordinate of the second point.
20670   * @param radius - The radius of the arc.
20671   * @returns The instance of the current object for chaining.
20672   */
20673  arcTo(x1, y1, x2, y2, radius) {
20674    this._ensurePoly();
20675    const points = this._currentPoly.points;
20676    buildArcTo(points, x1, y1, x2, y2, radius);
20677    return this;
20678  }
20679  /**
20680   * Adds an SVG-style arc to the path, allowing for elliptical arcs based on the SVG spec.
20681   * @param rx - The x-radius of the ellipse.
20682   * @param ry - The y-radius of the ellipse.
20683   * @param xAxisRotation - The rotation of the ellipse's x-axis relative
20684   * to the x-axis of the coordinate system, in degrees.
20685   * @param largeArcFlag - Determines if the arc should be greater than or less than 180 degrees.
20686   * @param sweepFlag - Determines if the arc should be swept in a positive angle direction.
20687   * @param x - The x-coordinate of the arc's end point.
20688   * @param y - The y-coordinate of the arc's end point.
20689   * @returns The instance of the current object for chaining.
20690   */
20691  arcToSvg(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
20692    const points = this._currentPoly.points;
20693    buildArcToSvg(
20694      points,
20695      this._currentPoly.lastX,
20696      this._currentPoly.lastY,
20697      x,
20698      y,
20699      rx,
20700      ry,
20701      xAxisRotation,
20702      largeArcFlag,
20703      sweepFlag
20704    );
20705    return this;
20706  }
20707  /**
20708   * Adds a cubic Bezier curve to the path.
20709   * It requires three points: the first two are control points and the third one is the end point.
20710   * The starting point is the last point in the current path.
20711   * @param cp1x - The x-coordinate of the first control point.
20712   * @param cp1y - The y-coordinate of the first control point.
20713   * @param cp2x - The x-coordinate of the second control point.
20714   * @param cp2y - The y-coordinate of the second control point.
20715   * @param x - The x-coordinate of the end point.
20716   * @param y - The y-coordinate of the end point.
20717   * @param smoothness - Optional parameter to adjust the smoothness of the curve.
20718   * @returns The instance of the current object for chaining.
20719   */
20720  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, smoothness) {
20721    this._ensurePoly();
20722    const currentPoly = this._currentPoly;
20723    buildAdaptiveBezier(
20724      this._currentPoly.points,
20725      currentPoly.lastX,
20726      currentPoly.lastY,
20727      cp1x,
20728      cp1y,
20729      cp2x,
20730      cp2y,
20731      x,
20732      y,
20733      smoothness
20734    );
20735    return this;
20736  }
20737  /**
20738   * Adds a quadratic curve to the path. It requires two points: the control point and the end point.
20739   * The starting point is the last point in the current path.
20740   * @param cp1x - The x-coordinate of the control point.
20741   * @param cp1y - The y-coordinate of the control point.
20742   * @param x - The x-coordinate of the end point.
20743   * @param y - The y-coordinate of the end point.
20744   * @param smoothing - Optional parameter to adjust the smoothness of the curve.
20745   * @returns The instance of the current object for chaining.
20746   */
20747  quadraticCurveTo(cp1x, cp1y, x, y, smoothing) {
20748    this._ensurePoly();
20749    const currentPoly = this._currentPoly;
20750    buildAdaptiveQuadratic(
20751      this._currentPoly.points,
20752      currentPoly.lastX,
20753      currentPoly.lastY,
20754      cp1x,
20755      cp1y,
20756      x,
20757      y,
20758      smoothing
20759    );
20760    return this;
20761  }
20762  /**
20763   * Closes the current path by drawing a straight line back to the start.
20764   * If the shape is already closed or there are no points in the path, this method does nothing.
20765   * @returns The instance of the current object for chaining.
20766   */
20767  closePath() {
20768    this.endPoly(true);
20769    return this;
20770  }
20771  /**
20772   * Adds another path to the current path. This method allows for the combination of multiple paths into one.
20773   * @param path - The `GraphicsPath` object representing the path to add.
20774   * @param transform - An optional `Matrix` object to apply a transformation to the path before adding it.
20775   * @returns The instance of the current object for chaining.
20776   */
20777  addPath(path, transform) {
20778    this.endPoly();
20779    if (transform && !transform.isIdentity()) {
20780      path = path.clone(true);
20781      path.transform(transform);
20782    }
20783    const shapePrimitives = this.shapePrimitives;
20784    const start = shapePrimitives.length;
20785    for (let i = 0; i < path.instructions.length; i++) {
20786      const instruction = path.instructions[i];
20787      this[instruction.action](...instruction.data);
20788    }
20789    if (path.checkForHoles && shapePrimitives.length - start > 1) {
20790      let mainShape = null;
20791      for (let i = start; i < shapePrimitives.length; i++) {
20792        const shapePrimitive = shapePrimitives[i];
20793        if (shapePrimitive.shape.type === "polygon") {
20794          const polygon = shapePrimitive.shape;
20795          const mainPolygon = mainShape?
vendor: 12,518 bytes, lines 20795-21087
20795.shape;
20796          if (mainPolygon && mainPolygon.containsPolygon(polygon)) {
20797            mainShape.holes || (mainShape.holes = []);
20798            mainShape.holes.push(shapePrimitive);
20799            shapePrimitives.copyWithin(i, i + 1);
20800            shapePrimitives.length--;
20801            i--;
20802          } else {
20803            mainShape = shapePrimitive;
20804          }
20805        }
20806      }
20807    }
20808    return this;
20809  }
20810  /**
20811   * Finalizes the drawing of the current path. Optionally, it can close the path.
20812   * @param closePath - A boolean indicating whether to close the path after finishing. False by default.
20813   */
20814  finish(closePath = false) {
20815    this.endPoly(closePath);
20816  }
20817  /**
20818   * Draws a rectangle shape. This method adds a new rectangle path to the current drawing.
20819   * @param x - The x-coordinate of the top-left corner of the rectangle.
20820   * @param y - The y-coordinate of the top-left corner of the rectangle.
20821   * @param w - The width of the rectangle.
20822   * @param h - The height of the rectangle.
20823   * @param transform - An optional `Matrix` object to apply a transformation to the rectangle.
20824   * @returns The instance of the current object for chaining.
20825   */
20826  rect(x, y, w, h, transform) {
20827    this.drawShape(new Rectangle(x, y, w, h), transform);
20828    return this;
20829  }
20830  /**
20831   * Draws a circle shape. This method adds a new circle path to the current drawing.
20832   * @param x - The x-coordinate of the center of the circle.
20833   * @param y - The y-coordinate of the center of the circle.
20834   * @param radius - The radius of the circle.
20835   * @param transform - An optional `Matrix` object to apply a transformation to the circle.
20836   * @returns The instance of the current object for chaining.
20837   */
20838  circle(x, y, radius, transform) {
20839    this.drawShape(new Circle(x, y, radius), transform);
20840    return this;
20841  }
20842  /**
20843   * Draws a polygon shape. This method allows for the creation of complex polygons by specifying a sequence of points.
20844   * @param points - An array of numbers, or or an array of PointData objects eg [{x,y}, {x,y}, {x,y}]
20845   * representing the x and y coordinates of the polygon's vertices, in sequence.
20846   * @param close - A boolean indicating whether to close the polygon path. True by default.
20847   * @param transform - An optional `Matrix` object to apply a transformation to the polygon.
20848   * @returns The instance of the current object for chaining.
20849   */
20850  poly(points, close, transform) {
20851    const polygon = new Polygon(points);
20852    polygon.closePath = close;
20853    this.drawShape(polygon, transform);
20854    return this;
20855  }
20856  /**
20857   * Draws a regular polygon with a specified number of sides. All sides and angles are equal.
20858   * @param x - The x-coordinate of the center of the polygon.
20859   * @param y - The y-coordinate of the center of the polygon.
20860   * @param radius - The radius of the circumscribed circle of the polygon.
20861   * @param sides - The number of sides of the polygon. Must be 3 or more.
20862   * @param rotation - The rotation angle of the polygon, in radians. Zero by default.
20863   * @param transform - An optional `Matrix` object to apply a transformation to the polygon.
20864   * @returns The instance of the current object for chaining.
20865   */
20866  regularPoly(x, y, radius, sides, rotation = 0, transform) {
20867    sides = Math.max(sides | 0, 3);
20868    const startAngle = -1 * Math.PI / 2 + rotation;
20869    const delta = Math.PI * 2 / sides;
20870    const polygon = [];
20871    for (let i = 0; i < sides; i++) {
20872      const angle = startAngle - i * delta;
20873      polygon.push(
20874        x + radius * Math.cos(angle),
20875        y + radius * Math.sin(angle)
20876      );
20877    }
20878    this.poly(polygon, true, transform);
20879    return this;
20880  }
20881  /**
20882   * Draws a polygon with rounded corners.
20883   * Similar to `regularPoly` but with the ability to round the corners of the polygon.
20884   * @param x - The x-coordinate of the center of the polygon.
20885   * @param y - The y-coordinate of the center of the polygon.
20886   * @param radius - The radius of the circumscribed circle of the polygon.
20887   * @param sides - The number of sides of the polygon. Must be 3 or more.
20888   * @param corner - The radius of the rounding of the corners.
20889   * @param rotation - The rotation angle of the polygon, in radians. Zero by default.
20890   * @param smoothness - Optional parameter to adjust the smoothness of the rounding.
20891   * @returns The instance of the current object for chaining.
20892   */
20893  roundPoly(x, y, radius, sides, corner, rotation = 0, smoothness) {
20894    sides = Math.max(sides | 0, 3);
20895    if (corner <= 0) {
20896      return this.regularPoly(x, y, radius, sides, rotation);
20897    }
20898    const sideLength = radius * Math.sin(Math.PI / sides) - 1e-3;
20899    corner = Math.min(corner, sideLength);
20900    const startAngle = -1 * Math.PI / 2 + rotation;
20901    const delta = Math.PI * 2 / sides;
20902    const internalAngle = (sides - 2) * Math.PI / sides / 2;
20903    for (let i = 0; i < sides; i++) {
20904      const angle = i * delta + startAngle;
20905      const x0 = x + radius * Math.cos(angle);
20906      const y0 = y + radius * Math.sin(angle);
20907      const a1 = angle + Math.PI + internalAngle;
20908      const a2 = angle - Math.PI - internalAngle;
20909      const x1 = x0 + corner * Math.cos(a1);
20910      const y1 = y0 + corner * Math.sin(a1);
20911      const x3 = x0 + corner * Math.cos(a2);
20912      const y3 = y0 + corner * Math.sin(a2);
20913      if (i === 0) {
20914        this.moveTo(x1, y1);
20915      } else {
20916        this.lineTo(x1, y1);
20917      }
20918      this.quadraticCurveTo(x0, y0, x3, y3, smoothness);
20919    }
20920    return this.closePath();
20921  }
20922  /**
20923   * Draws a shape with rounded corners. This function supports custom radius for each corner of the shape.
20924   * Optionally, corners can be rounded using a quadratic curve instead of an arc, providing a different aesthetic.
20925   * @param points - An array of `RoundedPoint` representing the corners of the shape to draw.
20926   * A minimum of 3 points is required.
20927   * @param radius - The default radius for the corners.
20928   * This radius is applied to all corners unless overridden in `points`.
20929   * @param useQuadratic - If set to true, rounded corners are drawn using a quadraticCurve
20930   *  method instead of an arc method. Defaults to false.
20931   * @param smoothness - Specifies the smoothness of the curve when `useQuadratic` is true.
20932   * Higher values make the curve smoother.
20933   * @returns The instance of the current object for chaining.
20934   */
20935  roundShape(points, radius, useQuadratic = false, smoothness) {
20936    if (points.length < 3) {
20937      return this;
20938    }
20939    if (useQuadratic) {
20940      roundedShapeQuadraticCurve(this, points, radius, smoothness);
20941    } else {
20942      roundedShapeArc(this, points, radius);
20943    }
20944    return this.closePath();
20945  }
20946  /**
20947   * Draw Rectangle with fillet corners. This is much like rounded rectangle
20948   * however it support negative numbers as well for the corner radius.
20949   * @param x - Upper left corner of rect
20950   * @param y - Upper right corner of rect
20951   * @param width - Width of rect
20952   * @param height - Height of rect
20953   * @param fillet - accept negative or positive values
20954   */
20955  filletRect(x, y, width, height, fillet) {
20956    if (fillet === 0) {
20957      return this.rect(x, y, width, height);
20958    }
20959    const maxFillet = Math.min(width, height) / 2;
20960    const inset = Math.min(maxFillet, Math.max(-maxFillet, fillet));
20961    const right = x + width;
20962    const bottom = y + height;
20963    const dir = inset < 0 ? -inset : 0;
20964    const size = Math.abs(inset);
20965    return this.moveTo(x, y + size).arcTo(x + dir, y + dir, x + size, y, size).lineTo(right - size, y).arcTo(right - dir, y + dir, right, y + size, size).lineTo(right, bottom - size).arcTo(right - dir, bottom - dir, x + width - size, bottom, size).lineTo(x + size, bottom).arcTo(x + dir, bottom - dir, x, bottom - size, size).closePath();
20966  }
20967  /**
20968   * Draw Rectangle with chamfer corners. These are angled corners.
20969   * @param x - Upper left corner of rect
20970   * @param y - Upper right corner of rect
20971   * @param width - Width of rect
20972   * @param height - Height of rect
20973   * @param chamfer - non-zero real number, size of corner cutout
20974   * @param transform
20975   */
20976  chamferRect(x, y, width, height, chamfer, transform) {
20977    if (chamfer <= 0) {
20978      return this.rect(x, y, width, height);
20979    }
20980    const inset = Math.min(chamfer, Math.min(width, height) / 2);
20981    const right = x + width;
20982    const bottom = y + height;
20983    const points = [
20984      x + inset,
20985      y,
20986      right - inset,
20987      y,
20988      right,
20989      y + inset,
20990      right,
20991      bottom - inset,
20992      right - inset,
20993      bottom,
20994      x + inset,
20995      bottom,
20996      x,
20997      bottom - inset,
20998      x,
20999      y + inset
21000    ];
21001    for (let i = points.length - 1; i >= 2; i -= 2) {
21002      if (points[i] === points[i - 2] && points[i - 1] === points[i - 3]) {
21003        points.splice(i - 1, 2);
21004      }
21005    }
21006    return this.poly(points, true, transform);
21007  }
21008  /**
21009   * Draws an ellipse at the specified location and with the given x and y radii.
21010   * An optional transformation can be applied, allowing for rotation, scaling, and translation.
21011   * @param x - The x-coordinate of the center of the ellipse.
21012   * @param y - The y-coordinate of the center of the ellipse.
21013   * @param radiusX - The horizontal radius of the ellipse.
21014   * @param radiusY - The vertical radius of the ellipse.
21015   * @param transform - An optional `Matrix` object to apply a transformation to the ellipse. This can include rotations.
21016   * @returns The instance of the current object for chaining.
21017   */
21018  ellipse(x, y, radiusX, radiusY, transform) {
21019    this.drawShape(new Ellipse(x, y, radiusX, radiusY), transform);
21020    return this;
21021  }
21022  /**
21023   * Draws a rectangle with rounded corners.
21024   * The corner radius can be specified to determine how rounded the corners should be.
21025   * An optional transformation can be applied, which allows for rotation, scaling, and translation of the rectangle.
21026   * @param x - The x-coordinate of the top-left corner of the rectangle.
21027   * @param y - The y-coordinate of the top-left corner of the rectangle.
21028   * @param w - The width of the rectangle.
21029   * @param h - The height of the rectangle.
21030   * @param radius - The radius of the rectangle's corners. If not specified, corners will be sharp.
21031   * @param transform - An optional `Matrix` object to apply a transformation to the rectangle.
21032   * @returns The instance of the current object for chaining.
21033   */
21034  roundRect(x, y, w, h, radius, transform) {
21035    this.drawShape(new RoundedRectangle(x, y, w, h, radius), transform);
21036    return this;
21037  }
21038  /**
21039   * Draws a given shape on the canvas.
21040   * This is a generic method that can draw any type of shape specified by the `ShapePrimitive` parameter.
21041   * An optional transformation matrix can be applied to the shape, allowing for complex transformations.
21042   * @param shape - The shape to draw, defined as a `ShapePrimitive` object.
21043   * @param matrix - An optional `Matrix` for transforming the shape. This can include rotations,
21044   * scaling, and translations.
21045   * @returns The instance of the current object for chaining.
21046   */
21047  drawShape(shape, matrix) {
21048    this.endPoly();
21049    this.shapePrimitives.push({ shape, transform: matrix });
21050    return this;
21051  }
21052  /**
21053   * Starts a new polygon path from the specified starting point.
21054   * This method initializes a new polygon or ends the current one if it exists.
21055   * @param x - The x-coordinate of the starting point of the new polygon.
21056   * @param y - The y-coordinate of the starting point of the new polygon.
21057   * @returns The instance of the current object for chaining.
21058   */
21059  startPoly(x, y) {
21060    let currentPoly = this._currentPoly;
21061    if (currentPoly) {
21062      this.endPoly();
21063    }
21064    currentPoly = new Polygon();
21065    currentPoly.points.push(x, y);
21066    this._currentPoly = currentPoly;
21067    return this;
21068  }
21069  /**
21070   * Ends the current polygon path. If `closePath` is set to true,
21071   * the path is closed by connecting the last point to the first one.
21072   * This method finalizes the current polygon and prepares it for drawing or adding to the shape primitives.
21073   * @param closePath - A boolean indicating whether to close the polygon by connecting the last point
21074   *  back to the starting point. False by default.
21075   * @returns The instance of the current object for chaining.
21076   */
21077  endPoly(closePath = false) {
21078    const shape = this._currentPoly;
21079    if (shape && shape.points.length > 2) {
21080      shape.closePath = closePath;
21081      this.shapePrimitives.push({ shape });
21082    }
21083    this._currentPoly = null;
21084    return this;
21085  }
21086  _ensurePoly(start = true) {
21087    if (this._currentPoly)
21087
21088      return;
21089    this._currentPoly = new Polygon();
21090    if (start) {
21091      const lastShape = this.shapePrimitives[this.shapePrimitives.length - 1];
21092      if (lastShape) {
21093        let lx = lastShape.shape.x;
21094        let ly = lastShape.shape.y;
21095        if (lastShape.transform && !lastShape.transform.isIdentity()) {
21096          const t = lastShape.transform;
21097          const tempX = lx;
21098          lx = t.a * lx + t.c * ly + t.tx;
21099          ly = t.b * tempX + t.d * ly + t.ty;
21100        }
21101        this._currentPoly.points.push(lx, ly);
21102      } else {
21103        this._currentPoly.points.push(0, 0);
21104      }
21105    }
21106  }
21107  /** Builds the path. */
21108  buildPath() {
21109    const path = this._graphicsPath2D;
21110    this.shapePrimitives.length = 0;
21111    this._currentPoly = null;
21112    for (let i = 0; i < path.instructions.length; i++) {
21113      const instruction = path.instructions[i];
21114      this[instruction.action](...instruction.data);
21115    }
21116    this.finish();
21117  }
21118  /** Gets the bounds of the path. */
21119  get bounds() {
21120    const bounds = this._bounds;
21121    bounds.clear();
21122    const shapePrimitives = this.shapePrimitives;
21123    for (let i = 0; i < shapePrimitives.length; i++) {
21124      const shapePrimitive = shapePrimitives[i];
21125      const boundsRect = shapePrimitive.shape.getBounds(tempRectangle);
21126      if (shapePrimitive.transform) {
21127        bounds.addRect(boundsRect, shapePrimitive.transform);
21128      } else {
21129        bounds.addRect(boundsRect);
21130      }
21131    }
21132    return bounds;
21133  }
21134}
21135
21136class GraphicsPath {
21137  /**
21138   * Creates a `GraphicsPath` instance optionally from an SVG path string or an array of `PathInstruction`.
21139   * @param instructions - An SVG path string or an array of `PathInstruction` objects.
21140   * @param signed
21141   */
21142  constructor(instructions, signed = false) {
21143    this.instructions = [];
21144    /** unique id for this graphics path */
21145    this.uid = uid("graphicsPath");
21146    this._dirty = true;
21147    this.checkForHoles = signed;
21148    if (typeof instructions === "string") {
21149      parseSVGPath(instructions, this);
21150    } else {
21151      this.instructions = instructions?.slice() ??
vendor: 10,832 bytes, lines 21151-21426
21151 [];
21152    }
21153  }
21154  /**
21155   * Provides access to the internal shape path, ensuring it is up-to-date with the current instructions.
21156   * @returns The `ShapePath` instance associated with this `GraphicsPath`.
21157   */
21158  get shapePath() {
21159    if (!this._shapePath) {
21160      this._shapePath = new ShapePath(this);
21161    }
21162    if (this._dirty) {
21163      this._dirty = false;
21164      this._shapePath.buildPath();
21165    }
21166    return this._shapePath;
21167  }
21168  /**
21169   * Adds another `GraphicsPath` to this path, optionally applying a transformation.
21170   * @param path - The `GraphicsPath` to add.
21171   * @param transform - An optional transformation to apply to the added path.
21172   * @returns The instance of the current object for chaining.
21173   */
21174  addPath(path, transform) {
21175    path = path.clone();
21176    this.instructions.push({ action: "addPath", data: [path, transform] });
21177    this._dirty = true;
21178    return this;
21179  }
21180  arc(...args) {
21181    this.instructions.push({ action: "arc", data: args });
21182    this._dirty = true;
21183    return this;
21184  }
21185  arcTo(...args) {
21186    this.instructions.push({ action: "arcTo", data: args });
21187    this._dirty = true;
21188    return this;
21189  }
21190  arcToSvg(...args) {
21191    this.instructions.push({ action: "arcToSvg", data: args });
21192    this._dirty = true;
21193    return this;
21194  }
21195  bezierCurveTo(...args) {
21196    this.instructions.push({ action: "bezierCurveTo", data: args });
21197    this._dirty = true;
21198    return this;
21199  }
21200  /**
21201   * Adds a cubic Bezier curve to the path.
21202   * It requires two points: the second control point and the end point. The first control point is assumed to be
21203   * The starting point is the last point in the current path.
21204   * @param cp2x - The x-coordinate of the second control point.
21205   * @param cp2y - The y-coordinate of the second control point.
21206   * @param x - The x-coordinate of the end point.
21207   * @param y - The y-coordinate of the end point.
21208   * @param smoothness - Optional parameter to adjust the smoothness of the curve.
21209   * @returns The instance of the current object for chaining.
21210   */
21211  bezierCurveToShort(cp2x, cp2y, x, y, smoothness) {
21212    const last = this.instructions[this.instructions.length - 1];
21213    const lastPoint = this.getLastPoint(Point.shared);
21214    let cp1x = 0;
21215    let cp1y = 0;
21216    if (!last || last.action !== "bezierCurveTo") {
21217      cp1x = lastPoint.x;
21218      cp1y = lastPoint.y;
21219    } else {
21220      cp1x = last.data[2];
21221      cp1y = last.data[3];
21222      const currentX = lastPoint.x;
21223      const currentY = lastPoint.y;
21224      cp1x = currentX + (currentX - cp1x);
21225      cp1y = currentY + (currentY - cp1y);
21226    }
21227    this.instructions.push({ action: "bezierCurveTo", data: [cp1x, cp1y, cp2x, cp2y, x, y, smoothness] });
21228    this._dirty = true;
21229    return this;
21230  }
21231  /**
21232   * Closes the current path by drawing a straight line back to the start.
21233   * If the shape is already closed or there are no points in the path, this method does nothing.
21234   * @returns The instance of the current object for chaining.
21235   */
21236  closePath() {
21237    this.instructions.push({ action: "closePath", data: [] });
21238    this._dirty = true;
21239    return this;
21240  }
21241  ellipse(...args) {
21242    this.instructions.push({ action: "ellipse", data: args });
21243    this._dirty = true;
21244    return this;
21245  }
21246  lineTo(...args) {
21247    this.instructions.push({ action: "lineTo", data: args });
21248    this._dirty = true;
21249    return this;
21250  }
21251  moveTo(...args) {
21252    this.instructions.push({ action: "moveTo", data: args });
21253    return this;
21254  }
21255  quadraticCurveTo(...args) {
21256    this.instructions.push({ action: "quadraticCurveTo", data: args });
21257    this._dirty = true;
21258    return this;
21259  }
21260  /**
21261   * Adds a quadratic curve to the path. It uses the previous point as the control point.
21262   * @param x - The x-coordinate of the end point.
21263   * @param y - The y-coordinate of the end point.
21264   * @param smoothness - Optional parameter to adjust the smoothness of the curve.
21265   * @returns The instance of the current object for chaining.
21266   */
21267  quadraticCurveToShort(x, y, smoothness) {
21268    const last = this.instructions[this.instructions.length - 1];
21269    const lastPoint = this.getLastPoint(Point.shared);
21270    let cpx1 = 0;
21271    let cpy1 = 0;
21272    if (!last || last.action !== "quadraticCurveTo") {
21273      cpx1 = lastPoint.x;
21274      cpy1 = lastPoint.y;
21275    } else {
21276      cpx1 = last.data[0];
21277      cpy1 = last.data[1];
21278      const currentX = lastPoint.x;
21279      const currentY = lastPoint.y;
21280      cpx1 = currentX + (currentX - cpx1);
21281      cpy1 = currentY + (currentY - cpy1);
21282    }
21283    this.instructions.push({ action: "quadraticCurveTo", data: [cpx1, cpy1, x, y, smoothness] });
21284    this._dirty = true;
21285    return this;
21286  }
21287  /**
21288   * Draws a rectangle shape. This method adds a new rectangle path to the current drawing.
21289   * @param x - The x-coordinate of the top-left corner of the rectangle.
21290   * @param y - The y-coordinate of the top-left corner of the rectangle.
21291   * @param w - The width of the rectangle.
21292   * @param h - The height of the rectangle.
21293   * @param transform - An optional `Matrix` object to apply a transformation to the rectangle.
21294   * @returns The instance of the current object for chaining.
21295   */
21296  rect(x, y, w, h, transform) {
21297    this.instructions.push({ action: "rect", data: [x, y, w, h, transform] });
21298    this._dirty = true;
21299    return this;
21300  }
21301  /**
21302   * Draws a circle shape. This method adds a new circle path to the current drawing.
21303   * @param x - The x-coordinate of the center of the circle.
21304   * @param y - The y-coordinate of the center of the circle.
21305   * @param radius - The radius of the circle.
21306   * @param transform - An optional `Matrix` object to apply a transformation to the circle.
21307   * @returns The instance of the current object for chaining.
21308   */
21309  circle(x, y, radius, transform) {
21310    this.instructions.push({ action: "circle", data: [x, y, radius, transform] });
21311    this._dirty = true;
21312    return this;
21313  }
21314  roundRect(...args) {
21315    this.instructions.push({ action: "roundRect", data: args });
21316    this._dirty = true;
21317    return this;
21318  }
21319  poly(...args) {
21320    this.instructions.push({ action: "poly", data: args });
21321    this._dirty = true;
21322    return this;
21323  }
21324  regularPoly(...args) {
21325    this.instructions.push({ action: "regularPoly", data: args });
21326    this._dirty = true;
21327    return this;
21328  }
21329  roundPoly(...args) {
21330    this.instructions.push({ action: "roundPoly", data: args });
21331    this._dirty = true;
21332    return this;
21333  }
21334  roundShape(...args) {
21335    this.instructions.push({ action: "roundShape", data: args });
21336    this._dirty = true;
21337    return this;
21338  }
21339  filletRect(...args) {
21340    this.instructions.push({ action: "filletRect", data: args });
21341    this._dirty = true;
21342    return this;
21343  }
21344  chamferRect(...args) {
21345    this.instructions.push({ action: "chamferRect", data: args });
21346    this._dirty = true;
21347    return this;
21348  }
21349  /**
21350   * Draws a star shape centered at a specified location. This method allows for the creation
21351   *  of stars with a variable number of points, outer radius, optional inner radius, and rotation.
21352   * The star is drawn as a closed polygon with alternating outer and inner vertices to create the star's points.
21353   * An optional transformation can be applied to scale, rotate, or translate the star as needed.
21354   * @param x - The x-coordinate of the center of the star.
21355   * @param y - The y-coordinate of the center of the star.
21356   * @param points - The number of points of the star.
21357   * @param radius - The outer radius of the star (distance from the center to the outer points).
21358   * @param innerRadius - Optional. The inner radius of the star
21359   * (distance from the center to the inner points between the outer points).
21360   * If not provided, defaults to half of the `radius`.
21361   * @param rotation - Optional. The rotation of the star in radians, where 0 is aligned with the y-axis.
21362   * Defaults to 0, meaning one point is directly upward.
21363   * @param transform - An optional `Matrix` object to apply a transformation to the star.
21364   * This can include rotations, scaling, and translations.
21365   * @returns The instance of the current object for chaining further drawing commands.
21366   */
21367  // eslint-disable-next-line max-len
21368  star(x, y, points, radius, innerRadius, rotation, transform) {
21369    innerRadius || (innerRadius = radius / 2);
21370    const startAngle = -1 * Math.PI / 2 + rotation;
21371    const len = points * 2;
21372    const delta = Math.PI * 2 / len;
21373    const polygon = [];
21374    for (let i = 0; i < len; i++) {
21375      const r = i % 2 ? innerRadius : radius;
21376      const angle = i * delta + startAngle;
21377      polygon.push(
21378        x + r * Math.cos(angle),
21379        y + r * Math.sin(angle)
21380      );
21381    }
21382    this.poly(polygon, true, transform);
21383    return this;
21384  }
21385  /**
21386   * Creates a copy of the current `GraphicsPath` instance. This method supports both shallow and deep cloning.
21387   * A shallow clone copies the reference of the instructions array, while a deep clone creates a new array and
21388   * copies each instruction individually, ensuring that modifications to the instructions of the cloned `GraphicsPath`
21389   * do not affect the original `GraphicsPath` and vice versa.
21390   * @param deep - A boolean flag indicating whether the clone should be deep.
21391   * @returns A new `GraphicsPath` instance that is a clone of the current instance.
21392   */
21393  clone(deep = false) {
21394    const newGraphicsPath2D = new GraphicsPath();
21395    newGraphicsPath2D.checkForHoles = this.checkForHoles;
21396    if (!deep) {
21397      newGraphicsPath2D.instructions = this.instructions.slice();
21398    } else {
21399      for (let i = 0; i < this.instructions.length; i++) {
21400        const instruction = this.instructions[i];
21401        newGraphicsPath2D.instructions.push({ action: instruction.action, data: instruction.data.slice() });
21402      }
21403    }
21404    return newGraphicsPath2D;
21405  }
21406  clear() {
21407    this.instructions.length = 0;
21408    this._dirty = true;
21409    return this;
21410  }
21411  /**
21412   * Applies a transformation matrix to all drawing instructions within the `GraphicsPath`.
21413   * This method enables the modification of the path's geometry according to the provided
21414   * transformation matrix, which can include translations, rotations, scaling, and skewing.
21415   *
21416   * Each drawing instruction in the path is updated to reflect the transformation,
21417   * ensuring the visual representation of the path is consistent with the applied matrix.
21418   *
21419   * Note: The transformation is applied directly to the coordinates and control points of the drawing instructions,
21420   * not to the path as a whole. This means the transformation's effects are baked into the individual instructions,
21421   * allowing for fine-grained control over the path's appearance.
21422   * @param matrix - A `Matrix` object representing the transformation to apply.
21423   * @returns The instance of the current object for chaining further operations.
21424   */
21425  transform(matrix) {
21426    if (matrix.isIdentity())
21426
21427      return this;
21428    const a = matrix.a;
21429    const b = matrix.b;
21430    const c = matrix.c;
21431    const d = matrix.d;
21432    const tx = matrix.tx;
21433    const ty = matrix.ty;
21434    let x = 0;
21435    let y = 0;
21436    let cpx1 = 0;
21437    let cpy1 = 0;
21438    let cpx2 = 0;
21439    let cpy2 = 0;
21440    let rx = 0;
21441    let ry = 0;
21442    for (let i = 0; i < this.instructions.length; i++) {
21443      const instruction = this.instructions[i];
21444      const data = instruction.data;
21445      switch (instruction.action) {
21446        case "moveTo":
21447        case "lineTo":
21448          x = data[0];
21449          y = data[1];
21450          data[0] = a * x + c * y + tx;
21451          data[1] = b * x + d * y + ty;
21452          break;
21453        case "bezierCurveTo":
21454          cpx1 = data[0];
21455          cpy1 = data[1];
21456          cpx2 = data[2];
21457          cpy2 = data[3];
21458          x = data[4];
21459          y = data[5];
21460          data[0] = a * cpx1 + c * cpy1 + tx;
21461          data[1] = b * cpx1 + d * cpy1 + ty;
21462          data[2] = a * cpx2 + c * cpy2 + tx;
21463          data[3] = b * cpx2 + d * cpy2 + ty;
21464          data[4] = a * x + c * y + tx;
21465          data[5] = b * x + d * y + ty;
21466          break;
21467        case "quadraticCurveTo":
21468          cpx1 = data[0];
21469          cpy1 = data[1];
21470          x = data[2];
21471          y = data[3];
21472          data[0] = a * cpx1 + c * cpy1 + tx;
21473          data[1] = b * cpx1 + d * cpy1 + ty;
21474          data[2] = a * x + c * y + tx;
21475          data[3] = b * x + d * y + ty;
21476          break;
21477        case "arcToSvg":
21478          x = data[5];
21479          y = data[6];
21480          rx = data[0];
21481          ry = data[1];
21482          data[0] = a * rx + c * ry;
21483          data[1] = b * rx + d * ry;
21484          data[5] = a * x + c * y + tx;
21485          data[6] = b * x + d * y + ty;
21486          break;
21487        case "circle":
21488          data[4] = adjustTransform(data[3], matrix);
21489          break;
21490        case "rect":
21491          data[4] = adjustTransform(data[4], matrix);
21492          break;
21493        case "ellipse":
21494          data[8] = adjustTransform(data[8], matrix);
21495          break;
21496        case "roundRect":
21497          data[5] = adjustTransform(data[5], matrix);
21498          break;
21499        case "addPath":
21500          data[0].transform(matrix);
21501          break;
21502        case "poly":
21503          data[2] = adjustTransform(data[2], matrix);
21504          break;
21505        default:
21506          warn("unknown transform action", instruction.action);
21507          break;
21508      }
21509    }
21510    this._dirty = true;
21511    return this;
21512  }
21513  get bounds() {
21514    return this.shapePath.bounds;
21515  }
21516  /**
21517   * Retrieves the last point from the current drawing instructions in the `GraphicsPath`.
21518   * This method is useful for operations that depend on the path's current endpoint,
21519   * such as connecting subsequent shapes or paths. It supports various drawing instructions,
21520   * ensuring the last point's position is accurately determined regardless of the path's complexity.
21521   *
21522   * If the last instruction is a `closePath`, the method iterates backward through the instructions
21523   *  until it finds an actionable instruction that defines a point (e.g., `moveTo`, `lineTo`,
21524   * `quadraticCurveTo`, etc.). For compound paths added via `addPath`, it recursively retrieves
21525   * the last point from the nested path.
21526   * @param out - A `Point` object where the last point's coordinates will be stored.
21527   * This object is modified directly to contain the result.
21528   * @returns The `Point` object containing the last point's coordinates.
21529   */
21530  getLastPoint(out) {
21531    let index = this.instructions.length - 1;
21532    let lastInstruction = this.instructions[index];
21533    if (!lastInstruction) {
21534      out.x = 0;
21535      out.y = 0;
21536      return out;
21537    }
21538    while (lastInstruction.action === "closePath") {
21539      index--;
21540      if (index < 0) {
21541        out.x = 0;
21542        out.y = 0;
21543        return out;
21544      }
21545      lastInstruction = this.instructions[index];
21546    }
21547    switch (lastInstruction.action) {
21548      case "moveTo":
21549      case "lineTo":
21550        out.x = lastInstruction.data[0];
21551        out.y = lastInstruction.data[1];
21552        break;
21553      case "quadraticCurveTo":
21554        out.x = lastInstruction.data[2];
21555        out.y = lastInstruction.data[3];
21556        break;
21557      case "bezierCurveTo":
21558        out.x = lastInstruction.data[4];
21559        out.y = lastInstruction.data[5];
21560        break;
21561      case "arc":
21562      case "arcToSvg":
21563        out.x = lastInstruction.data[5];
21564        out.y = lastInstruction.data[6];
21565        break;
21566      case "addPath":
21567        lastInstruction.data[0].getLastPoint(out);
21568        break;
21569    }
21570    return out;
21571  }
21572}
21573function adjustTransform(currentMatrix, transform) {
21574  if (currentMatrix) {
21575    return currentMatrix.prepend(transform);
21576  }
21577  return transform.clone();
21578}
21579
21580function parseSVGFloatAttribute(svg, id, defaultValue) {
21581  const value = svg.getAttribute(id);
21582  return value ? Number(value) : defaultValue;
21583}
21584
21585function parseSVGDefinitions(svg, session) {
21586  const definitions = svg.querySelectorAll("defs");
21587  for (let i = 0; i < definitions.length; i++) {
21588    const definition = definitions[i];
21589    for (let j = 0; j < definition.children.length; j++) {
21590      const child = definition.children[j];
21591      switch (child.nodeName.toLowerCase()) {
21592        case "lineargradient":
21593          session.defs[child.id] = parseLinearGradient(child);
21594          break;
21595        case "radialgradient":
21596          session.defs[child.id] = parseRadialGradient();
21597          break;
21598      }
21599    }
21600  }
21601}
21602function parseLinearGradient(child) {
21603  const x0 = parseSVGFloatAttribute(child, "x1", 0);
21604  const y0 = parseSVGFloatAttribute(child, "y1", 0);
21605  const x1 = parseSVGFloatAttribute(child, "x2", 1);
21606  const y1 = parseSVGFloatAttribute(child, "y2", 0);
21607  const gradientUnit = child.getAttribute("gradientUnits") || "objectBoundingBox";
21608  const gradient = new FillGradient(
21609    x0,
21610    y0,
21611    x1,
21612    y1,
21613    gradientUnit === "objectBoundingBox" ? "local" : "global"
21614  );
21615  for (let k = 0; k < child.children.length; k++) {
21616    const stop = child.children[k];
21617    const offset = parseSVGFloatAttribute(stop, "offset", 0);
21618    const color = Color.shared.setValue(stop.getAttribute("stop-color")).toNumber();
21619    gradient.addColorStop(offset, color);
21620  }
21621  return gradient;
21622}
21623function parseRadialGradient(_child) {
21624  warn("[SVG Parser] Radial gradients are not yet supported");
21625  return new FillGradient(0, 0, 1, 0);
21626}
21627
21628function extractSvgUrlId(url) {
21629  const match = url.match(/url\s*\(\s*['"]?\s*#([^'"\s)]+)\s*['"]?\s*\)/i);
21630  return match ? match[1] : "";
21631}
21632
21633const styleAttributes = {
21634  // Fill properties
21635  fill: { type: "paint", default: 0 },
21636  // Fill color/gradient
21637  "fill-opacity": { type: "number", default: 1 },
21638  // Fill transparency
21639  // Stroke properties
21640  stroke: { type: "paint", default: 0 },
21641  // Stroke color/gradient
21642  "stroke-width": { type: "number", default: 1 },
21643  // Width of stroke
21644  "stroke-opacity": { type: "number", default: 1 },
21645  // Stroke transparency
21646  "stroke-linecap": { type: "string", default: "butt" },
21647  // End cap style: butt, round, square
21648  "stroke-linejoin": { type: "string", default: "miter" },
21649  // Join style: miter, round, bevel
21650  "stroke-miterlimit": { type: "number", default: 10 },
vendor: 9,555 bytes, lines 21651-21951
21651  // Limit on miter join sharpness
21652  "stroke-dasharray": { type: "string", default: "none" },
21653  // Dash pattern
21654  "stroke-dashoffset": { type: "number", default: 0 },
21655  // Offset for dash pattern
21656  // Global properties
21657  opacity: { type: "number", default: 1 }
21658  // Overall opacity
21659};
21660function parseSVGStyle(svg, session) {
21661  const style = svg.getAttribute("style");
21662  const strokeStyle = {};
21663  const fillStyle = {};
21664  const result = {
21665    strokeStyle,
21666    fillStyle,
21667    useFill: false,
21668    useStroke: false
21669  };
21670  for (const key in styleAttributes) {
21671    const attribute = svg.getAttribute(key);
21672    if (attribute) {
21673      parseAttribute(session, result, key, attribute.trim());
21674    }
21675  }
21676  if (style) {
21677    const styleParts = style.split(";");
21678    for (let i = 0; i < styleParts.length; i++) {
21679      const stylePart = styleParts[i].trim();
21680      const [key, value] = stylePart.split(":");
21681      if (styleAttributes[key]) {
21682        parseAttribute(session, result, key, value.trim());
21683      }
21684    }
21685  }
21686  return {
21687    strokeStyle: result.useStroke ? strokeStyle : null,
21688    fillStyle: result.useFill ? fillStyle : null,
21689    useFill: result.useFill,
21690    useStroke: result.useStroke
21691  };
21692}
21693function parseAttribute(session, result, id, value) {
21694  switch (id) {
21695    case "stroke":
21696      if (value !== "none") {
21697        if (value.startsWith("url(")) {
21698          const id2 = extractSvgUrlId(value);
21699          result.strokeStyle.fill = session.defs[id2];
21700        } else {
21701          result.strokeStyle.color = Color.shared.setValue(value).toNumber();
21702        }
21703        result.useStroke = true;
21704      }
21705      break;
21706    case "stroke-width":
21707      result.strokeStyle.width = Number(value);
21708      break;
21709    case "fill":
21710      if (value !== "none") {
21711        if (value.startsWith("url(")) {
21712          const id2 = extractSvgUrlId(value);
21713          result.fillStyle.fill = session.defs[id2];
21714        } else {
21715          result.fillStyle.color = Color.shared.setValue(value).toNumber();
21716        }
21717        result.useFill = true;
21718      }
21719      break;
21720    case "fill-opacity":
21721      result.fillStyle.alpha = Number(value);
21722      break;
21723    case "stroke-opacity":
21724      result.strokeStyle.alpha = Number(value);
21725      break;
21726    case "opacity":
21727      result.fillStyle.alpha = Number(value);
21728      result.strokeStyle.alpha = Number(value);
21729      break;
21730  }
21731}
21732
21733function checkForNestedPattern(subpathsWithArea) {
21734  if (subpathsWithArea.length <= 2) {
21735    return true;
21736  }
21737  const areas = subpathsWithArea.map((s) => s.area).sort((a, b) => b - a);
21738  const [largestArea, secondArea] = areas;
21739  const smallestArea = areas[areas.length - 1];
21740  const largestToSecondRatio = largestArea / secondArea;
21741  const secondToSmallestRatio = secondArea / smallestArea;
21742  if (largestToSecondRatio > 3 && secondToSmallestRatio < 2) {
21743    return false;
21744  }
21745  return true;
21746}
21747
21748function extractSubpaths(pathData) {
21749  const parts = pathData.split(/(?=[Mm])/);
21750  const subpaths = parts.filter((part) => part.trim().length > 0);
21751  return subpaths;
21752}
21753function calculatePathArea(pathData) {
21754  const coords = pathData.match(/[-+]?[0-9]*\.?[0-9]+/g);
21755  if (!coords || coords.length < 4)
21756    return 0;
21757  const numbers = coords.map(Number);
21758  const xs = [];
21759  const ys = [];
21760  for (let i = 0; i < numbers.length; i += 2) {
21761    if (i + 1 < numbers.length) {
21762      xs.push(numbers[i]);
21763      ys.push(numbers[i + 1]);
21764    }
21765  }
21766  if (xs.length === 0 || ys.length === 0)
21767    return 0;
21768  const minX = Math.min(...xs);
21769  const maxX = Math.max(...xs);
21770  const minY = Math.min(...ys);
21771  const maxY = Math.max(...ys);
21772  const area = (maxX - minX) * (maxY - minY);
21773  return area;
21774}
21775function appendSVGPath(pathData, graphicsPath) {
21776  const tempPath = new GraphicsPath(pathData, false);
21777  for (const instruction of tempPath.instructions) {
21778    graphicsPath.instructions.push(instruction);
21779  }
21780}
21781
21782function SVGParser(svg, graphicsContext) {
21783  if (typeof svg === "string") {
21784    const div = document.createElement("div");
21785    div.innerHTML = svg.trim();
21786    svg = div.querySelector("svg");
21787  }
21788  const session = {
21789    context: graphicsContext,
21790    defs: {},
21791    path: new GraphicsPath()
21792  };
21793  parseSVGDefinitions(svg, session);
21794  const children = svg.children;
21795  const { fillStyle, strokeStyle } = parseSVGStyle(svg, session);
21796  for (let i = 0; i < children.length; i++) {
21797    const child = children[i];
21798    if (child.nodeName.toLowerCase() === "defs")
21799      continue;
21800    renderChildren(child, session, fillStyle, strokeStyle);
21801  }
21802  return graphicsContext;
21803}
21804function renderChildren(svg, session, fillStyle, strokeStyle) {
21805  const children = svg.children;
21806  const { fillStyle: f1, strokeStyle: s1 } = parseSVGStyle(svg, session);
21807  if (f1 && fillStyle) {
21808    fillStyle = { ...fillStyle, ...f1 };
21809  } else if (f1) {
21810    fillStyle = f1;
21811  }
21812  if (s1 && strokeStyle) {
21813    strokeStyle = { ...strokeStyle, ...s1 };
21814  } else if (s1) {
21815    strokeStyle = s1;
21816  }
21817  const noStyle = !fillStyle && !strokeStyle;
21818  if (noStyle) {
21819    fillStyle = { color: 0 };
21820  }
21821  let x;
21822  let y;
21823  let x1;
21824  let y1;
21825  let x2;
21826  let y2;
21827  let cx;
21828  let cy;
21829  let r;
21830  let rx;
21831  let ry;
21832  let points;
21833  let pointsString;
21834  let d;
21835  let graphicsPath;
21836  let width;
21837  let height;
21838  switch (svg.nodeName.toLowerCase()) {
21839    case "path": {
21840      d = svg.getAttribute("d");
21841      const fillRule = svg.getAttribute("fill-rule");
21842      const subpaths = extractSubpaths(d);
21843      const hasExplicitEvenodd = fillRule === "evenodd";
21844      const hasMultipleSubpaths = subpaths.length > 1;
21845      const shouldProcessHoles = hasExplicitEvenodd && hasMultipleSubpaths;
21846      if (shouldProcessHoles) {
21847        const subpathsWithArea = subpaths.map((subpath) => ({
21848          path: subpath,
21849          area: calculatePathArea(subpath)
21850        }));
21851        subpathsWithArea.sort((a, b) => b.area - a.area);
21852        const useMultipleHolesApproach = subpaths.length > 3 || !checkForNestedPattern(subpathsWithArea);
21853        if (useMultipleHolesApproach) {
21854          for (let i = 0; i < subpathsWithArea.length; i++) {
21855            const subpath = subpathsWithArea[i];
21856            const isMainShape = i === 0;
21857            session.context.beginPath();
21858            const newPath = new GraphicsPath(void 0, true);
21859            appendSVGPath(subpath.path, newPath);
21860            session.context.path(newPath);
21861            if (isMainShape) {
21862              if (fillStyle)
21863                session.context.fill(fillStyle);
21864              if (strokeStyle)
21865                session.context.stroke(strokeStyle);
21866            } else {
21867              session.context.cut();
21868            }
21869          }
21870        } else {
21871          for (let i = 0; i < subpathsWithArea.length; i++) {
21872            const subpath = subpathsWithArea[i];
21873            const isHole = i % 2 === 1;
21874            session.context.beginPath();
21875            const newPath = new GraphicsPath(void 0, true);
21876            appendSVGPath(subpath.path, newPath);
21877            session.context.path(newPath);
21878            if (isHole) {
21879              session.context.cut();
21880            } else {
21881              if (fillStyle)
21882                session.context.fill(fillStyle);
21883              if (strokeStyle)
21884                session.context.stroke(strokeStyle);
21885            }
21886          }
21887        }
21888      } else {
21889        const useEvenoddForGraphicsPath = fillRule ? fillRule === "evenodd" : true;
21890        graphicsPath = new GraphicsPath(d, useEvenoddForGraphicsPath);
21891        session.context.path(graphicsPath);
21892        if (fillStyle)
21893          session.context.fill(fillStyle);
21894        if (strokeStyle)
21895          session.context.stroke(strokeStyle);
21896      }
21897      break;
21898    }
21899    case "circle":
21900      cx = parseSVGFloatAttribute(svg, "cx", 0);
21901      cy = parseSVGFloatAttribute(svg, "cy", 0);
21902      r = parseSVGFloatAttribute(svg, "r", 0);
21903      session.context.ellipse(cx, cy, r, r);
21904      if (fillStyle)
21905        session.context.fill(fillStyle);
21906      if (strokeStyle)
21907        session.context.stroke(strokeStyle);
21908      break;
21909    case "rect":
21910      x = parseSVGFloatAttribute(svg, "x", 0);
21911      y = parseSVGFloatAttribute(svg, "y", 0);
21912      width = parseSVGFloatAttribute(svg, "width", 0);
21913      height = parseSVGFloatAttribute(svg, "height", 0);
21914      rx = parseSVGFloatAttribute(svg, "rx", 0);
21915      ry = parseSVGFloatAttribute(svg, "ry", 0);
21916      if (rx || ry) {
21917        session.context.roundRect(x, y, width, height, rx || ry);
21918      } else {
21919        session.context.rect(x, y, width, height);
21920      }
21921      if (fillStyle)
21922        session.context.fill(fillStyle);
21923      if (strokeStyle)
21924        session.context.stroke(strokeStyle);
21925      break;
21926    case "ellipse":
21927      cx = parseSVGFloatAttribute(svg, "cx", 0);
21928      cy = parseSVGFloatAttribute(svg, "cy", 0);
21929      rx = parseSVGFloatAttribute(svg, "rx", 0);
21930      ry = parseSVGFloatAttribute(svg, "ry", 0);
21931      session.context.beginPath();
21932      session.context.ellipse(cx, cy, rx, ry);
21933      if (fillStyle)
21934        session.context.fill(fillStyle);
21935      if (strokeStyle)
21936        session.context.stroke(strokeStyle);
21937      break;
21938    case "line":
21939      x1 = parseSVGFloatAttribute(svg, "x1", 0);
21940      y1 = parseSVGFloatAttribute(svg, "y1", 0);
21941      x2 = parseSVGFloatAttribute(svg, "x2", 0);
21942      y2 = parseSVGFloatAttribute(svg, "y2", 0);
21943      session.context.beginPath();
21944      session.context.moveTo(x1, y1);
21945      session.context.lineTo(x2, y2);
21946      if (strokeStyle)
21947        session.context.stroke(strokeStyle);
21948      break;
21949    case "polygon":
21950      pointsString = svg.getAttribute("points");
21951      points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10));
21952      session.context.poly(points, true);
21953      if (fillStyle)
21954        session.context.fill(fillStyle);
21955      if (strokeStyle)
21956        session.context.stroke(strokeStyle);
21957      break;
21958    case "polyline":
21959      pointsString = svg.getAttribute("points");
21960      points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10));
21961      session.context.poly(points, false);
21962      if (strokeStyle)
21963        session.context.stroke(strokeStyle);
21964      break;
21965    case "g":
21966    case "svg":
21967      break;
21968    default: {
21969      warn(`[SVG parser] <${svg.nodeName}> elements unsupported`);
21970      break;
21971    }
21972  }
21973  if (noStyle) {
21974    fillStyle = null;
21975  }
21976  for (let i = 0; i < children.length; i++) {
21977    renderChildren(children[i], session, fillStyle, strokeStyle);
21978  }
21979}
21980
21981function isColorLike(value) {
21982  return Color.isColorLike(value);
21983}
21984function isFillPattern(value) {
21985  return value instanceof FillPattern;
21986}
21987function isFillGradient(value) {
21988  return value instanceof FillGradient;
21989}
21990function isTexture(value) {
21991  return value instanceof Texture;
21992}
21993function handleColorLike(fill, value, defaultStyle) {
21994  const temp = Color.shared.setValue(value ?? 0);
21995  fill.color = temp.toNumber();
21996  fill.alpha = temp.alpha === 1 ? defaultStyle.alpha : temp.alpha;
21997  fill.texture = Texture.WHITE;
21998  return { ...defaultStyle, ...fill };
21999}
22000function handleTexture(fill, value, defaultStyle) {
22001  fill.texture = value;
22002  return { ...defaultStyle, ...fill };
22003}
22004function handleFillPattern(fill, value, defaultStyle) {
22005  fill.fill = value;
22006  fill.color = 16777215;
22007  fill.texture = value.texture;
22008  fill.matrix = value.transform;
22009  return { ...defaultStyle, ...fill };
22010}
22011function handleFillGradient(fill, value, defaultStyle) {
22012  value.buildGradient();
22013  fill.fill = value;
22014  fill.color = 16777215;
22015  fill.texture = value.texture;
22016  fill.matrix = value.transform;
22017  fill.textureSpace = value.textureSpace;
22018  return { ...defaultStyle, ...fill };
22019}
22020function handleFillObject(value, defaultStyle) {
22021  const style = { ...defaultStyle, ...value };
22022  const color = Color.shared.setValue(style.color);
22023  style.alpha *= color.alpha;
22024  style.color = color.toNumber();
22025  return style;
22026}
22027function toFillStyle(value, defaultStyle) {
22028  if (value === void 0 || value === null) {
22029    return null;
22030  }
22031  const fill = {};
22032  const objectStyle = value;
22033  if (isColorLike(value)) {
22034    return handleColorLike(fill, value, defaultStyle);
22035  } else if (isTexture(value)) {
22036    return handleTexture(fill, value, defaultStyle);
22037  } else if (isFillPattern(value)) {
22038    return handleFillPattern(fill, value, defaultStyle);
22039  } else if (isFillGradient(value)) {
22040    return handleFillGradient(fill, value, defaultStyle);
22041  } else if (objectStyle.fill && isFillPattern(objectStyle.fill)) {
22042    return handleFillPattern(objectStyle, objectStyle.fill, defaultStyle);
22043  } else if (objectStyle.fill && isFillGradient(objectStyle.fill)) {
22044    return handleFillGradient(objectStyle, objectStyle.fill, defaultStyle);
22045  }
22046  return handleFillObject(objectStyle, defaultStyle);
22047}
22048function toStrokeStyle(value, defaultStyle) {
22049  const { width, alignment, miterLimit, cap, join, pixelLine, ...rest } = defaultStyle;
22050  const fill = toFillStyle(value, rest);
22051  if (!fill) {
22052    return null;
22053  }
22054  return {
22055    width,
22056    alignment,
22057    miterLimit,
22058    cap,
22059    join,
22060    pixelLine,
22061    ...fill
22062  };
22063}
22064
22065const tmpPoint = new Point();
22066const tempMatrix = new Matrix();
22067const _GraphicsContext = class _GraphicsContext extends EventEmitter {
22068  constructor() {
22069    super(...arguments);
22070    /** @internal */
22071    this._gpuData = /* @__PURE__ */ Object.create(null);
22072    /** If set to true, the resource will be garbage collected automatically when it is not used. */
22073    this.autoGarbageCollect = true;
22074    /** @internal */
22075    this._gcLastUsed = -1;
22076    /**
22077     * unique id for this graphics context
22078     * @internal
22079     */
22080    this.uid = uid("graphicsContext");
22081    /**
22082     * Indicates whether content is updated and have to be re-rendered.
22083     * @internal
22084     */
22085    this.dirty = true;
22086    /** The batch mode for this graphics context. It can be 'auto', 'batch', or 'no-batch'. */
22087    this.batchMode = "auto";
22088    /** @internal */
22089    this.instructions = [];
22090    /** Whether the graphics context has been destroyed. */
22091    this.destroyed = false;
22092    this._activePath = new GraphicsPath();
22093    this._transform = new Matrix();
22094    this._fillStyle = { ..._GraphicsContext.defaultFillStyle };
22095    this._strokeStyle = { ..._GraphicsContext.defaultStrokeStyle };
22096    this._stateStack = [];
22097    this._tick = 0;
22098    this._bounds = new Bounds();
22099    this._boundsDirty = true;
22100  }
22101  /**
22102   * Creates a new GraphicsContext object that is a clone of this instance, copying all properties,
22103   * including the current drawing state, transformations, styles, and instructions.
22104   * @returns A new GraphicsContext instance with the same properties and state as this one.
22105   */
22106  clone() {
22107    const clone = new _GraphicsContext();
22108    clone.batchMode = this.batchMode;
22109    clone.instructions = this.instructions.slice();
22110    clone._activePath = this._activePath.clone();
22111    clone._transform = this._transform.clone();
22112    clone._fillStyle = { ...this._fillStyle };
22113    clone._strokeStyle = { ...this._strokeStyle };
22114    clone._stateStack = this._stateStack.slice();
22115    clone._bounds = this._bounds.clone();
22116    clone._boundsDirty = true;
22117    return clone;
22118  }
22119  /**
22120   * The current fill style of the graphics context. This can be a color, gradient, pattern, or a more complex style defined by a FillStyle object.
22121   */
22122  get fillStyle() {
22123    return this._fillStyle;
22124  }
22125  set fillStyle(value) {
22126    this._fillStyle = toFillStyle(value, _GraphicsContext.defaultFillStyle);
22127  }
22128  /**
22129   * The current stroke style of the graphics context. Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object.
22130   */
22131  get strokeStyle() {
22132    return this._strokeStyle;
22133  }
22134  set strokeStyle(value) {
22135    this._strokeStyle = toStrokeStyle(value, _GraphicsContext.defaultStrokeStyle);
22136  }
22137  /**
22138   * Sets the current fill style of the graphics context. The fill style can be a color, gradient,
22139   * pattern, or a more complex style defined by a FillStyle object.
22140   * @param style - The fill style to apply. This can be a simple color, a gradient or pattern object,
22141   *                or a FillStyle or ConvertedFillStyle object.
22142   * @returns The instance of the current GraphicsContext for method chaining.
22143   */
22144  setFillStyle(style) {
22145    this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle);
22146    return this;
22147  }
22148  /**
22149   * Sets the current stroke style of the graphics context. Similar to fill styles, stroke styles can
22150   * encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object.
22151   * @param style - The stroke style to apply. Can be defined as a color, a gradient or pattern,
22152   *                or a StrokeStyle or ConvertedStrokeStyle object.
22153   * @returns The instance of the current GraphicsContext for method chaining.
22154   */
22155  setStrokeStyle(style) {
22156    this._strokeStyle = toFillStyle(style, _GraphicsContext.defaultStrokeStyle);
22157    return this;
22158  }
22159  texture(texture, tint, dx, dy, dw, dh) {
22160    this.instructions.push({
22161      action: "texture",
22162      data: {
22163        image: texture,
22164        dx: dx || 0,
22165        dy: dy || 0,
22166        dw: dw || texture.frame.width,
22167        dh: dh || texture.frame.height,
22168        transform: this._transform.clone(),
22169        alpha: this._fillStyle.alpha,
22170        style: tint ? Color.shared.setValue(tint).toNumber() : 16777215
22171      }
22172    });
22173    this.onUpdate();
22174    return this;
22175  }
22176  /**
22177   * Resets the current path. Any previous path and its commands are discarded and a new path is
22178   * started. This is typically called before beginning a new shape or series of drawing commands.
22179   * @returns The instance of the current GraphicsContext for method chaining.
22180   */
22181  beginPath() {
22182    this._activePath = new GraphicsPath();
22183    return this;
22184  }
22185  fill(style, alpha) {
22186    let path;
22187    const lastInstruction = this.instructions[this.instructions.length - 1];
22188    if (this._tick === 0 && lastInstruction?.action === "stroke") {
22189      path = lastInstruction.data.path;
22190    } else {
22191      path = this._activePath.clone();
22192    }
22193    if (!path)
22194      return this;
22195    if (style != null) {
22196      if (alpha !== void 0 && typeof style === "number") {
22197        deprecation(v8_0_0, "GraphicsContext.fill(color, alpha) is deprecated, use GraphicsContext.fill({ color, alpha }) instead");
22198        style = { color: style, alpha };
22199      }
22200      this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle);
22201    }
22202    this.instructions.push({
22203      action: "fill",
22204      // TODO copy fill style!
22205      data: { style: this.fillStyle, path }
22206    });
22207    this.onUpdate();
22208    this._initNextPathLocation();
22209    this._tick = 0;
22210    return this;
22211  }
22212  _initNextPathLocation() {
22213    const { x, y } = this._activePath.getLastPoint(Point.shared);
22214    this._activePath.clear();
22215    this._activePath.moveTo(x, y);
22216  }
22217  /**
22218   * Strokes the current path with the current stroke style. This method can take an optional
22219   * FillInput parameter to define the stroke's appearance, including its color, width, and other properties.
22220   * @param style - (Optional) The stroke style to apply. Can be defined as a simple c
22220olor or a more complex style object. If omitted, uses the current stroke style.
22221   * @returns The instance of the current GraphicsContext for method chaining.
22222   */
22223  stroke(style) {
22224    let path;
22225    const lastInstruction = this.instructions[this.instructions.length - 1];
22226    if (this._tick === 0 && lastInstruction?.action === "fill") {
22227      path = lastInstruction.data.path;
22228    } else {
22229      path = this._activePath.clone();
22230    }
22231    if (!path)
22232      return this;
22233    if (style != null) {
22234      this._strokeStyle = toStrokeStyle(style, _GraphicsContext.defaultStrokeStyle);
22235    }
22236    this.instructions.push({
22237      action: "stroke",
22238      // TODO copy fill style!
22239      data: { style: this.strokeStyle, path }
22240    });
22241    this.onUpdate();
22242    this._initNextPathLocation();
22243    this._tick = 0;
22244    return this;
22245  }
22246  /**
22247   * Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by
22248   * subtracting a path from the previously drawn path. If a hole is not completely in a shape, it will
22249   * fail to cut correctly!
22250   * @returns The instance of the current GraphicsContext for method chaining.
22251   */
22252  cut() {
22253    for (let i = 0; i < 2; i++) {
22254      const lastInstruction = this.instructions[this.instructions.length - 1 - i];
22255      const holePath = this._activePath.clone();
22256      if (lastInstruction) {
22257        if (lastInstruction.action === "stroke" || lastInstruction.action === "fill") {
22258          if (lastInstruction.data.hole) {
22259            lastInstruction.data.hole.addPath(holePath);
22260          } else {
22261            lastInstruction.data.hole = holePath;
22262            break;
22263          }
22264        }
22265      }
22266    }
22267    this._initNextPathLocation();
22268    return this;
22269  }
22270  /**
22271   * Adds an arc to the current path, which is centered at (x, y) with the specified radius,
22272   * starting and ending angles, and direction.
22273   * @param x - The x-coordinate of the arc's center.
22274   * @param y - The y-coordinate of the arc's center.
22275   * @param radius - The arc's radius.
22276   * @param startAngle - The starting angle, in radians.
22277   * @param endAngle - The ending angle, in radians.
22278   * @param counterclockwise - (Optional) Specifies whether the arc is drawn counterclockwise (true) or clockwise (false). Defaults to false.
22279   * @returns The instance of the current GraphicsContext for method chaining.
22280   */
22281  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
22282    this._tick++;
22283    const t = this._transform;
22284    this._activePath.arc(
22285      t.a * x + t.c * y + t.tx,
22286      t.b * x + t.d * y + t.ty,
22287      radius,
22288      startAngle,
22289      endAngle,
22290      counterclockwise
22291    );
22292    return this;
22293  }
22294  /**
22295   * Adds an arc to the current path with the given control points and radius, connected to the previous point
22296   * by a straight line if necessary.
22297   * @param x1 - The x-coordinate of the first control point.
22298   * @param y1 - The y-coordinate of the first control point.
22299   * @param x2 - The x-coordinate of the second control point.
22300   * @param y2 - The y-coordinate of the second control point.
22301   * @param radius - The arc's radius.
22302   * @returns The instance of the current GraphicsContext for method chaining.
22303   */
22304  arcTo(x1, y1, x2, y2, radius) {
22305    this._tick++;
22306    const t = this._transform;
22307    this._activePath.arcTo(
22308      t.a * x1 + t.c * y1 + t.tx,
22309      t.b * x1 + t.d * y1 + t.ty,
22310      t.a * x2 + t.c * y2 + t.tx,
22311      t.b * x2 + t.d * y2 + t.ty,
22312      radius
22313    );
22314    return this;
22315  }
22316  /**
22317   * Adds an SVG-style arc to the path, allowing for elliptical arcs based on the SVG spec.
22318   * @param rx - The x-radius of the ellipse.
22319   * @param ry - The y-radius of the ellipse.
22320   * @param xAxisRotation - The rotation of the ellipse's x-axis relative
22321   * to the x-axis of the coordinate system, in degrees.
22322   * @param largeArcFlag - Determines if the arc should be greater than or less than 180 degrees.
22323   * @param sweepFlag - Determines if the arc should be swept in a positive angle direction.
22324   * @param x - The x-coordinate of the arc's end point.
22325   * @param y - The y-coordinate of the arc's end point.
22326   * @returns The instance of the current object for chaining.
22327   */
22328  arcToSvg(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
22329    this._tick++;
22330    const t = this._transform;
22331    this._activePath.arcToSvg(
22332      rx,
22333      ry,
22334      xAxisRotation,
22335      // should we rotate this with transform??
22336      largeArcFlag,
22337      sweepFlag,
22338      t.a * x + t.c * y + t.tx,
22339      t.b * x + t.d * y + t.ty
22340    );
22341    return this;
22342  }
22343  /**
22344   * Adds a cubic Bezier curve to the path.
22345   * It requires three points: the first two are control points and the third one is the end point.
22346   * The starting point is the last point in the current path.
22347   * @param cp1x - The x-coordinate of the first control point.
22348   * @param cp1y - The y-coordinate of the first control point.
22349   * @param cp2x - The x-coordinate of the second control point.
22350   * @param cp2y - The y-coordinate of the second control point.
22351   * @param x - The x-coordinate of the end point.
22352   * @param y - The y-coordinate of the end point.
22353   * @param smoothness - Optional parameter to adjust the smoothness of the curve.
22354   * @returns The instance of the current object for chaining.
22355   */
22356  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, smoothness) {
22357    this._tick++;
22358    const t = this._transform;
22359    this._activePath.bezierCurveTo(
22360      t.a * cp1x + t.c * cp1y + t.tx,
22361      t.b * cp1x + t.d * cp1y + t.ty,
22362      t.a * cp2x + t.c * cp2y + t.tx,
22363      t.b * cp2x + t.d * cp2y + t.ty,
22364      t.a * x + t.c * y + t.tx,
22365      t.b * x + t.d * y + t.ty,
22366      smoothness
22367    );
22368    return this;
22369  }
22370  /**
22371   * Closes the current path by drawing a straight line back to the start.
22372   * If the shape is already closed or there are no points in the path, this method does nothing.
22373   * @returns The instance of the current object for chaining.
22374   */
22375  closePath() {
22376    this._tick++;
22377    this._activePath?
vendor: 11,602 bytes, lines 22377-22643
22377.closePath();
22378    return this;
22379  }
22380  /**
22381   * Draws an ellipse at the specified location and with the given x and y radii.
22382   * An optional transformation can be applied, allowing for rotation, scaling, and translation.
22383   * @param x - The x-coordinate of the center of the ellipse.
22384   * @param y - The y-coordinate of the center of the ellipse.
22385   * @param radiusX - The horizontal radius of the ellipse.
22386   * @param radiusY - The vertical radius of the ellipse.
22387   * @returns The instance of the current object for chaining.
22388   */
22389  ellipse(x, y, radiusX, radiusY) {
22390    this._tick++;
22391    this._activePath.ellipse(x, y, radiusX, radiusY, this._transform.clone());
22392    return this;
22393  }
22394  /**
22395   * Draws a circle shape. This method adds a new circle path to the current drawing.
22396   * @param x - The x-coordinate of the center of the circle.
22397   * @param y - The y-coordinate of the center of the circle.
22398   * @param radius - The radius of the circle.
22399   * @returns The instance of the current object for chaining.
22400   */
22401  circle(x, y, radius) {
22402    this._tick++;
22403    this._activePath.circle(x, y, radius, this._transform.clone());
22404    return this;
22405  }
22406  /**
22407   * Adds another `GraphicsPath` to this path, optionally applying a transformation.
22408   * @param path - The `GraphicsPath` to add.
22409   * @returns The instance of the current object for chaining.
22410   */
22411  path(path) {
22412    this._tick++;
22413    this._activePath.addPath(path, this._transform.clone());
22414    return this;
22415  }
22416  /**
22417   * Connects the current point to a new point with a straight line. This method updates the current path.
22418   * @param x - The x-coordinate of the new point to connect to.
22419   * @param y - The y-coordinate of the new point to connect to.
22420   * @returns The instance of the current object for chaining.
22421   */
22422  lineTo(x, y) {
22423    this._tick++;
22424    const t = this._transform;
22425    this._activePath.lineTo(
22426      t.a * x + t.c * y + t.tx,
22427      t.b * x + t.d * y + t.ty
22428    );
22429    return this;
22430  }
22431  /**
22432   * Sets the starting point for a new sub-path. Any subsequent drawing commands are considered part of this path.
22433   * @param x - The x-coordinate for the starting point.
22434   * @param y - The y-coordinate for the starting point.
22435   * @returns The instance of the current object for chaining.
22436   */
22437  moveTo(x, y) {
22438    this._tick++;
22439    const t = this._transform;
22440    const instructions = this._activePath.instructions;
22441    const transformedX = t.a * x + t.c * y + t.tx;
22442    const transformedY = t.b * x + t.d * y + t.ty;
22443    if (instructions.length === 1 && instructions[0].action === "moveTo") {
22444      instructions[0].data[0] = transformedX;
22445      instructions[0].data[1] = transformedY;
22446      return this;
22447    }
22448    this._activePath.moveTo(
22449      transformedX,
22450      transformedY
22451    );
22452    return this;
22453  }
22454  /**
22455   * Adds a quadratic curve to the path. It requires two points: the control point and the end point.
22456   * The starting point is the last point in the current path.
22457   * @param cpx - The x-coordinate of the control point.
22458   * @param cpy - The y-coordinate of the control point.
22459   * @param x - The x-coordinate of the end point.
22460   * @param y - The y-coordinate of the end point.
22461   * @param smoothness - Optional parameter to adjust the smoothness of the curve.
22462   * @returns The instance of the current object for chaining.
22463   */
22464  quadraticCurveTo(cpx, cpy, x, y, smoothness) {
22465    this._tick++;
22466    const t = this._transform;
22467    this._activePath.quadraticCurveTo(
22468      t.a * cpx + t.c * cpy + t.tx,
22469      t.b * cpx + t.d * cpy + t.ty,
22470      t.a * x + t.c * y + t.tx,
22471      t.b * x + t.d * y + t.ty,
22472      smoothness
22473    );
22474    return this;
22475  }
22476  /**
22477   * Draws a rectangle shape. This method adds a new rectangle path to the current drawing.
22478   * @param x - The x-coordinate of the top-left corner of the rectangle.
22479   * @param y - The y-coordinate of the top-left corner of the rectangle.
22480   * @param w - The width of the rectangle.
22481   * @param h - The height of the rectangle.
22482   * @returns The instance of the current object for chaining.
22483   */
22484  rect(x, y, w, h) {
22485    this._tick++;
22486    this._activePath.rect(x, y, w, h, this._transform.clone());
22487    return this;
22488  }
22489  /**
22490   * Draws a rectangle with rounded corners.
22491   * The corner radius can be specified to determine how rounded the corners should be.
22492   * An optional transformation can be applied, which allows for rotation, scaling, and translation of the rectangle.
22493   * @param x - The x-coordinate of the top-left corner of the rectangle.
22494   * @param y - The y-coordinate of the top-left corner of the rectangle.
22495   * @param w - The width of the rectangle.
22496   * @param h - The height of the rectangle.
22497   * @param radius - The radius of the rectangle's corners. If not specified, corners will be sharp.
22498   * @returns The instance of the current object for chaining.
22499   */
22500  roundRect(x, y, w, h, radius) {
22501    this._tick++;
22502    this._activePath.roundRect(x, y, w, h, radius, this._transform.clone());
22503    return this;
22504  }
22505  /**
22506   * Draws a polygon shape by specifying a sequence of points. This method allows for the creation of complex polygons,
22507   * which can be both open and closed. An optional transformation can be applied, enabling the polygon to be scaled,
22508   * rotated, or translated as needed.
22509   * @param points - An array of numbers, or an array of PointData objects eg [{x,y}, {x,y}, {x,y}]
22510   * representing the x and y coordinates, of the polygon's vertices, in sequence.
22511   * @param close - A boolean indicating whether to close the polygon path. True by default.
22512   */
22513  poly(points, close) {
22514    this._tick++;
22515    this._activePath.poly(points, close, this._transform.clone());
22516    return this;
22517  }
22518  /**
22519   * Draws a regular polygon with a specified number of sides. All sides and angles are equal.
22520   * @param x - The x-coordinate of the center of the polygon.
22521   * @param y - The y-coordinate of the center of the polygon.
22522   * @param radius - The radius of the circumscribed circle of the polygon.
22523   * @param sides - The number of sides of the polygon. Must be 3 or more.
22524   * @param rotation - The rotation angle of the polygon, in radians. Zero by default.
22525   * @param transform - An optional `Matrix` object to apply a transformation to the polygon.
22526   * @returns The instance of the current object for chaining.
22527   */
22528  regularPoly(x, y, radius, sides, rotation = 0, transform) {
22529    this._tick++;
22530    this._activePath.regularPoly(x, y, radius, sides, rotation, transform);
22531    return this;
22532  }
22533  /**
22534   * Draws a polygon with rounded corners.
22535   * Similar to `regularPoly` but with the ability to round the corners of the polygon.
22536   * @param x - The x-coordinate of the center of the polygon.
22537   * @param y - The y-coordinate of the center of the polygon.
22538   * @param radius - The radius of the circumscribed circle of the polygon.
22539   * @param sides - The number of sides of the polygon. Must be 3 or more.
22540   * @param corner - The radius of the rounding of the corners.
22541   * @param rotation - The rotation angle of the polygon, in radians. Zero by default.
22542   * @returns The instance of the current object for chaining.
22543   */
22544  roundPoly(x, y, radius, sides, corner, rotation) {
22545    this._tick++;
22546    this._activePath.roundPoly(x, y, radius, sides, corner, rotation);
22547    return this;
22548  }
22549  /**
22550   * Draws a shape with rounded corners. This function supports custom radius for each corner of the shape.
22551   * Optionally, corners can be rounded using a quadratic curve instead of an arc, providing a different aesthetic.
22552   * @param points - An array of `RoundedPoint` representing the corners of the shape to draw.
22553   * A minimum of 3 points is required.
22554   * @param radius - The default radius for the corners.
22555   * This radius is applied to all corners unless overridden in `points`.
22556   * @param useQuadratic - If set to true, rounded corners are drawn using a quadraticCurve
22557   *  method instead of an arc method. Defaults to false.
22558   * @param smoothness - Specifies the smoothness of the curve when `useQuadratic` is true.
22559   * Higher values make the curve smoother.
22560   * @returns The instance of the current object for chaining.
22561   */
22562  roundShape(points, radius, useQuadratic, smoothness) {
22563    this._tick++;
22564    this._activePath.roundShape(points, radius, useQuadratic, smoothness);
22565    return this;
22566  }
22567  /**
22568   * Draw Rectangle with fillet corners. This is much like rounded rectangle
22569   * however it support negative numbers as well for the corner radius.
22570   * @param x - Upper left corner of rect
22571   * @param y - Upper right corner of rect
22572   * @param width - Width of rect
22573   * @param height - Height of rect
22574   * @param fillet - accept negative or positive values
22575   */
22576  filletRect(x, y, width, height, fillet) {
22577    this._tick++;
22578    this._activePath.filletRect(x, y, width, height, fillet);
22579    return this;
22580  }
22581  /**
22582   * Draw Rectangle with chamfer corners. These are angled corners.
22583   * @param x - Upper left corner of rect
22584   * @param y - Upper right corner of rect
22585   * @param width - Width of rect
22586   * @param height - Height of rect
22587   * @param chamfer - non-zero real number, size of corner cutout
22588   * @param transform
22589   */
22590  chamferRect(x, y, width, height, chamfer, transform) {
22591    this._tick++;
22592    this._activePath.chamferRect(x, y, width, height, chamfer, transform);
22593    return this;
22594  }
22595  /**
22596   * Draws a star shape centered at a specified location. This method allows for the creation
22597   *  of stars with a variable number of points, outer radius, optional inner radius, and rotation.
22598   * The star is drawn as a closed polygon with alternating outer and inner vertices to create the star's points.
22599   * An optional transformation can be applied to scale, rotate, or translate the star as needed.
22600   * @param x - The x-coordinate of the center of the star.
22601   * @param y - The y-coordinate of the center of the star.
22602   * @param points - The number of points of the star.
22603   * @param radius - The outer radius of the star (distance from the center to the outer points).
22604   * @param innerRadius - Optional. The inner radius of the star
22605   * (distance from the center to the inner points between the outer points).
22606   * If not provided, defaults to half of the `radius`.
22607   * @param rotation - Optional. The rotation of the star in radians, where 0 is aligned with the y-axis.
22608   * Defaults to 0, meaning one point is directly upward.
22609   * @returns The instance of the current object for chaining further drawing commands.
22610   */
22611  star(x, y, points, radius, innerRadius = 0, rotation = 0) {
22612    this._tick++;
22613    this._activePath.star(x, y, points, radius, innerRadius, rotation, this._transform.clone());
22614    return this;
22615  }
22616  /**
22617   * Parses and renders an SVG string into the graphics context. This allows for complex shapes and paths
22618   * defined in SVG format to be drawn within the graphics context.
22619   * @param svg - The SVG string to be parsed and rendered.
22620   */
22621  svg(svg) {
22622    this._tick++;
22623    SVGParser(svg, this);
22624    return this;
22625  }
22626  /**
22627   * Restores the most recently saved graphics state by popping the top of the graphics state stack.
22628   * This includes transformations, fill styles, and stroke styles.
22629   */
22630  restore() {
22631    const state = this._stateStack.pop();
22632    if (state) {
22633      this._transform = state.transform;
22634      this._fillStyle = state.fillStyle;
22635      this._strokeStyle = state.strokeStyle;
22636    }
22637    return this;
22638  }
22639  /** Saves the current graphics state, including transformations, fill styles, and stroke styles, onto a stack. */
22640  save() {
22641    this._stateStack.push({
22642      transform: this._transform.clone(),
22643      fillStyle: 
22643{ ...this._fillStyle },
22644      strokeStyle: { ...this._strokeStyle }
22645    });
22646    return this;
22647  }
22648  /**
22649   * Returns the current transformation matrix of the graphics context.
22650   * @returns The current transformation matrix.
22651   */
22652  getTransform() {
22653    return this._transform;
22654  }
22655  /**
22656   * Resets the current transformation matrix to the identity matrix, effectively removing any transformations (rotation, scaling, translation) previously applied.
22657   * @returns The instance of the current GraphicsContext for method chaining.
22658   */
22659  resetTransform() {
22660    this._transform.identity();
22661    return this;
22662  }
22663  /**
22664   * Applies a rotation transformation to the graphics context around the current origin.
22665   * @param angle - The angle of rotation in radians.
22666   * @returns The instance of the current GraphicsContext for method chaining.
22667   */
22668  rotate(angle) {
22669    this._transform.rotate(angle);
22670    return this;
22671  }
22672  /**
22673   * Applies a scaling transformation to the graphics context, scaling drawings by x horizontally and by y vertically.
22674   * @param x - The scale factor in the horizontal direction.
22675   * @param y - (Optional) The scale factor in the vertical direction. If not specified, the x value is used for both directions.
22676   * @returns The instance of the current GraphicsContext for method chaining.
22677   */
22678  scale(x, y = x) {
22679    this._transform.scale(x, y);
22680    return this;
22681  }
22682  setTransform(a, b, c, d, dx, dy) {
22683    if (a instanceof Matrix) {
22684      this._transform.set(a.a, a.b, a.c, a.d, a.tx, a.ty);
22685      return this;
22686    }
22687    this._transform.set(a, b, c, d, dx, dy);
22688    return this;
22689  }
22690  transform(a, b, c, d, dx, dy) {
22691    if (a instanceof Matrix) {
22692      this._transform.append(a);
22693      return this;
22694    }
22695    tempMatrix.set(a, b, c, d, dx, dy);
22696    this._transform.append(tempMatrix);
22697    return this;
22698  }
22699  /**
22700   * Applies a translation transformation to the graphics context, moving the origin by the specified amounts.
22701   * @param x - The amount to translate in the horizontal direction.
22702   * @param y - (Optional) The amount to translate in the vertical direction. If not specified, the x value is used for both directions.
22703   * @returns The instance of the current GraphicsContext for method chaining.
22704   */
22705  translate(x, y = x) {
22706    this._transform.translate(x, y);
22707    return this;
22708  }
22709  /**
22710   * Clears all drawing commands from the graphics context, effectively resetting it. This includes clearing the path,
22711   * and optionally resetting transformations to the identity matrix.
22712   * @returns The instance of the current GraphicsContext for method chaining.
22713   */
22714  clear() {
22715    this._activePath.clear();
22716    this.instructions.length = 0;
22717    this.resetTransform();
22718    this.onUpdate();
22719    return this;
22720  }
22721  onUpdate() {
22722    this._boundsDirty = true;
22723    if (this.dirty)
22724      return;
22725    this.emit("update", this, 16);
22726    this.dirty = true;
22727  }
22728  /** The bounds of the graphic shape. */
22729  get bounds() {
22730    if (!this._boundsDirty)
22731      return this._bounds;
22732    this._boundsDirty = false;
22733    const bounds = this._bounds;
22734    bounds.clear();
22735    for (let i = 0; i < this.instructions.length; i++) {
22736      const instruction = this.instructions[i];
22737      const action = instruction.action;
22738      if (action === "fill") {
22739        const data = instruction.data;
22740        bounds.addBounds(data.path.bounds);
22741      } else if (action === "texture") {
22742        const data = instruction.data;
22743        bounds.addFrame(data.dx, data.dy, data.dx + data.dw, data.dy + data.dh, data.transform);
22744      }
22745      if (action === "stroke") {
22746        const data = instruction.data;
22747        const alignment = data.style.alignment;
22748        const outerPadding = data.style.width * (1 - alignment);
22749        const _bounds = data.path.bounds;
22750        bounds.addFrame(
22751          _bounds.minX - outerPadding,
22752          _bounds.minY - outerPadding,
22753          _bounds.maxX + outerPadding,
22754          _bounds.maxY + outerPadding
22755        );
22756      }
22757    }
22758    return bounds;
22759  }
22760  /**
22761   * Check to see if a point is contained within this geometry.
22762   * @param point - Point to check if it's contained.
22763   * @returns {boolean} `true` if the point is contained within geometry.
22764   */
22765  containsPoint(point) {
22766    if (!this.bounds.containsPoint(point.x, point.y))
22767      return false;
22768    const instructions = this.instructions;
22769    let hasHit = false;
22770    for (let k = 0; k < instructions.length; k++) {
22771      const instruction = instructions[k];
22772      const data = instruction.data;
22773      const path = data.path;
22774      if (!instruction.action || !path)
22775        continue;
22776      const style = data.style;
22777      const shapes = path.shapePath.shapePrimitives;
22778      for (let i = 0; i < shapes.length; i++) {
22779        const shape = shapes[i].shape;
22780        if (!style || !shape)
22781          continue;
22782        const transform = shapes[i].transform;
22783        const transformedPoint = transform ? transform.applyInverse(point, tmpPoint) : point;
22784        if (instruction.action === "fill") {
22785          hasHit = shape.contains(transformedPoint.x, transformedPoint.y);
22786        } else {
22787          const strokeStyle = style;
22788          hasHit = shape.strokeContains(transformedPoint.x, transformedPoint.y, strokeStyle.width, strokeStyle.alignment);
22789        }
22790        const holes = data.hole;
22791        if (holes) {
22792          const holeShapes = holes.shapePath?.shapePrimitives;
22793          if (holeShapes) {
22794            for (let j = 0; j < holeShapes.length; j++) {
22795              if (holeShapes[j].shape.contains(transformedPoint.x, transformedPoint.y)) {
22796                hasHit = false;
22797              }
22798            }
22799          }
22800        }
22801        if (hasHit) {
22802          return true;
22803        }
22804      }
22805    }
22806    return hasHit;
22807  }
22808  /** Unloads the GPU data from the graphics context. */
22809  unload() {
22810    this.emit("unload", this);
22811    for (const key in this._gpuData) {
22812      this._gpuData[key]?.destroy();
22813    }
22814    this._gpuData = /* @__PURE__ */ Object.create(null);
22815  }
22816  /**
22817   * Destroys the GraphicsData object.
22818   * @param options - Options parameter. A boolean will act as if all options
22819   *  have been set to that value
22820   * @example
22821   * context.destroy();
22822   * context.destroy(true);
22823   * context.destroy({ texture: true, textureSource: true });
22824   */
22825  destroy(options = false) {
22826    if (this.destroyed)
22827      return;
22828    this.destroyed = true;
22829    this._stateStack.length = 0;
22830    this._transform = null;
22831    this.unload();
22832    this.emit("destroy", this);
22833    this.removeAllListeners();
22834    const destroyTexture = typeof options === "boolean" ? options : options?.texture;
22835    if (destroyTexture) {
22836      const destroyTextureSource = typeof options === "boolean" ? options : options?.textureSource;
22837      if (this._fillStyle.texture) {
22838        this._fillStyle.fill && "uid" in this._fillStyle.fill ? this._fillStyle.fill.destroy() : this._fillStyle.texture.destroy(destroyTextureSource);
22839      }
22840      if (this._strokeStyle.texture) {
22841        this._strokeStyle.fill && "uid" in this._strokeStyle.fill ? this._strokeStyle.fill.destroy() : this._strokeStyle.texture.destroy(destroyTextureSource);
22842      }
22843    }
22844    this._fillStyle = null;
22845    this._strokeStyle = null;
22846    this.instructions = null;
22847    this._activePath = null;
22848    this._bounds = null;
22849    this._stateStack = null;
22850    this.customShader = null;
22851    this._transform = null;
22852  }
22853};
22854/** The default fill style to use when none is provided. */
22855_GraphicsContext.defaultFillStyle = {
22856  /** The color to use for the fill. */
22857  color: 16777215,
22858  /** The alpha value to use for the fill. */
22859  alpha: 1,
22860  /** The texture to use for the fill. */
22861  texture: Texture.WHITE,
22862  /** The matrix to apply. */
22863  matrix: null,
22864  /** The fill pattern to use. */
22865  fill: null,
22866  /** Whether coordinates are 'global' or 'local' */
22867  textureSpace: "local"
22868};
22869/** The default stroke style to use when none is provided. */
22870_GraphicsContext.defaultStrokeStyle = {
22871  /** The width of the stroke. */
22872  width: 1,
22873  /** The color to use for the stroke. */
22874  color: 16777215,
22875  /** The alpha value to use for the stroke. */
22876  alpha: 1,
22877  /** The alignment of the stroke. */
22878  alignment: 0.5,
22879  /** The miter limit to use. */
22880  miterLimit: 10,
22881  /** The line cap style to use. */
22882  cap: "butt",
22883  /** The line join style to use. */
22884  join: "miter",
22885  /** The texture to use for the fill. */
22886  texture: Texture.WHITE,
22887  /** The matrix to apply. */
22888  matrix: null,
22889  /** The fill pattern to use. */
22890  fill: null,
22891  /** Whether coordinates are 'global' or 'local' */
22892  textureSpace: "local",
22893  /** If the stroke is a pixel line. */
22894  pixelLine: false
22895};
22896let GraphicsContext = _GraphicsContext;
22897
22898const _TextStyle = class _TextStyle extends EventEmitter {
22899  constructor(style = {}) {
22900    super();
22901    /**
22902     * Unique identifier for the TextStyle class.
22903     * This is used to track instances and ensure uniqueness.
22904     * @internal
22905     */
22906    this.uid = uid("textStyle");
22907    /**
22908     * Internal tick counter used to track updates and changes.
22909     * This is incremented whenever the style is modified, allowing for efficient change detection.
22910     * @internal
22911     */
22912    this._tick = 0;
22913    convertV7Tov8Style(style);
22914    const fullStyle = { ..._TextStyle.defaultTextStyle, ...style };
22915    for (const key in fullStyle) {
22916      const thisKey = key;
22917      this[thisKey] = fullStyle[key];
22918    }
22919    this.update();
22920    this._tick = 0;
22921  }
22922  /**
22923   * Alignment for multiline text, does not affect single line text.
22924   * @type {'left'|'center'|'right'|'justify'}
22925   */
22926  get align() {
22927    return this._align;
22928  }
22929  set align(value) {
22930    if (this._align === value)
22931      return;
22932    this._align = value;
22933    this.update();
22934  }
22935  /** Indicates if lines can be wrapped within words, it needs wordWrap to be set to true. */
22936  get breakWords() {
22937    return this._breakWords;
22938  }
22939  set breakWords(value) {
22940    if (this._breakWords === value)
22941      return;
22942    this._breakWords = value;
22943    this.update();
22944  }
22945  /** Set a drop shadow for the text. */
22946  get dropShadow() {
22947    return this._dropShadow;
22948  }
22949  set dropShadow(value) {
22950    if (this._dropShadow === value)
22951      return;
22952    if (value !== null && typeof value === "object") {
22953      this._dropShadow = this._createProxy({ ..._TextStyle.defaultDropShadow, ...value });
22954    } else {
22955      this._dropShadow = value ? this._createProxy({ ..._TextStyle.defaultDropShadow }) : null;
22956    }
22957    this.update();
22958  }
22959  /** The font family, can be a single font name, or a list of names where the first is the preferred font. */
22960  get fontFamily() {
22961    return this._fontFamily;
22962  }
22963  set fontFamily(value) {
22964    if (this._fontFamily === value)
22965      return;
22966    this._fontFamily = value;
22967    this.update();
22968  }
22969  /** The font size (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em') */
22970  get fontSize() {
22971    return this._fontSize;
22972  }
22973  set fontSize(value) {
22974    if (this._fontSize === value)
22975      return;
22976    if (typeof value === "string") {
22977      this._fontSize = parseInt(value, 10);
22978    } else {
22979      this._fontSize = value;
22980    }
22981    this.update();
22982  }
22983  /**
22984   * The font style.
22985   * @type {'normal'|'italic'|'oblique'}
22986   */
22987  get fontStyle() {
22988    return this._fontStyle;
22989  }
22990  set fontStyle(value) {
22991    if (this._fontStyle === value)
22992      return;
22993    this._fontStyle = value.toLowerCase();
22994    this.update();
22995  }
22996  /**
22997   * The font variant.
22998   * @type {'normal'|'small-caps'}
22999   */
23000  get fontVariant() {
23001    return this._fontVariant;
23002  }
23003  set fontVariant(value) {
23004    if (this._fontVariant === value)
23005      return;
23006    this._fontVariant = value;
23007    this.update();
23008  }
23009  /**
23010   * The font weight.
23011   * @type {'normal'|'bold'|'bolder'|'lighter'|'100'|'200'|'300'|'400'|'500'|'600'|'700'|'800'|'900'}
23012   */
23013  get fontWeight() {
23014    return this._fontWeight;
23015  }
23016  set fontWeight(value) {
23017    if (this._fontWeight === value)
23018      return;
23019    this._fontWeight = value;
23020    this.update();
23021  }
23022  /** The space between lines. */
23023  get leading() {
23024    return this._leading;
23025  }
23026  set leading(value) {
23027    if (this._leading === value)
23028      return;
23029    this._leading = value;
23030    this.update();
23031  }
23032  /** The amount of spacing between letters, default is 0. */
23033  get letterSpacing() {
23034    return this._letterSpacing;
23035  }
23036  set letterSpacing(value) {
23037    if (this._letterSpacing === value)
23038      return;
23039    this._letterSpacing = value;
23040    this.update();
23041  }
23042  /** The line height, a number that represents the vertical space that a letter uses. */
23043  get lineHeight() {
23044    return this._lineHeight;
23045  }
23046  set lineHeight(value) {
23047    if (this._lineHeight === value)
23048      return;
23049    this._lineHeight = value;
23050    this.update();
23051  }
23052  /**
23053   * Occasionally some fonts are cropped. Adding some padding will prevent this from happening
23054   * by adding padding to all sides of the text.
23055   * > [!NOTE] This will NOT affect the positioning or bounds of the text.
23056   */
23057  get padding() {
23058    return this._padding;
23059  }
23060  set padding(value) {
23061    if (this._padding === value)
23062      return;
23063    this._padding = value;
23064    this.update();
23065  }
23066  /**
23067   * An optional filter or array of filters to apply to the text, allowing for advanced visual effects.
23068   * These filters will be applied to the text as it is created, resulting in faster rendering for static text
23069   * compared to applying the filter directly to the text object (which would be applied at run time).
23070   * @default null
23071   */
23072  get filters() {
23073    return this._filters;
23074  }
23075  set filters(value) {
23076    if (this._filters === value)
23077      return;
23078    this._filters = Object.freeze(value);
23079    this.update();
23080  }
23081  /**
23082   * Trim transparent borders from the text texture.
23083   * > [!IMPORTANT] PERFORMANCE WARNING:
23084   * > This is a costly operation as it requires scanning pixel alpha values.
23085   * > Avoid using `trim: true` for dynamic text, as it could significantly impact performance.
23086   */
23087  get trim() {
23088    return this._trim;
23089  }
23090  set trim(value) {
23091    if (this._trim === value)
23092      return;
23093    this._trim = value;
23094    this.update();
23095  }
23096  /**
23097   * The baseline of the text that is rendered.
23098   * @type {'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom'}
23099   */
23100  get textBaseline() {
23101    return this._textBaseline;
23102  }
23103  set textBaseline(value) {
23104    if (this._textBaseline === value)
23105      return;
23106    this._textBaseline = value;
23107    this.update();
23108  }
23109  /**
23110   * How newlines and spaces should be handled.
23111   * Default is 'pre' (preserve, preserve).
23112   *
23113   *  value       | New lines     |   Spaces
23114   *  ---         | ---           |   ---
23115   * 'normal'     | Collapse      |   Collapse
23116   * 'pre'        | Preserve      |   Preserve
23117   * 'pre-line'   | Preserve      |   Collapse
23118   * @type {'normal'|'pre'|'pre-line'}
23119   */
23120  get whiteSpace() {
23121    return this._whiteSpace;
23122  }
23123  set whiteSpace(value) {
23124    if (this._whiteSpace === value)
23125      return;
23126    this._whiteSpace = value;
23127    this.update();
23128  }
23129  /** Indicates if word wrap should be used. */
23130  get wordWrap() {
23131    return this._wordWrap;
23132  }
23133  set wordWrap(value) {
23134    if (this._wordWrap === value)
23135      return;
23136    this._wordWrap = value;
23137    this.update();
23138  }
23139  /** The width at which text will wrap, it needs wordWrap to be set to true. */
23140  get wordWrapWidth() {
23141    return this._wordWrapWidth;
23142  }
23143  set wordWrapWidth(value) {
23144    if (this._wordWrapWidth === value)
23145      return;
23146    this._wordWrapWidth = value;
23147    this.update();
23148  }
23149  /**
23150   * The fill style that will be used to color the text.
23151   * This can be:
23152   * - A color string like 'red', '#00FF00', or 'rgba(255,0,0,0.5)'
23153   * - A hex number like 0xff0000 for red
23154   * - A FillStyle object with properties like { color: 0xff0000, alpha: 0.5 }
23155   * - A FillGradient for gradient fills
23156   * - A FillPattern for pattern/texture fills
23157   *
23158   * When using a FillGradient, vertical gradients (angle of 90 degrees) are applied per line of text,
23159   * while gradients at any other angle are spread across the entire text body as a whole.
23160   * @example
23161   * // Vertical gradient applied per line
23162   * const verticalGradient = new FillGradient(0, 0, 0, 1)
23163   *     .addColorStop(0, 0xff0000)
23164   *     .addColorStop(1, 0x0000ff);
23165   *
23166   * const text = new Text({
23167   *     text: 'Line 1\nLine 2',
23168   *     style: { fill: verticalGradient }
23169   * });
23170   *
23171   * To manage the gradient in a global scope, set the textureSpace property of the FillGradient to 'global'.
23172   * @type {string|number|FillStyle|FillGradient|FillPattern}
23173   */
23174  get fill() {
23175    return this._originalFill;
23176  }
23177  set fill(value) {
23178    if (value === this._originalFill)
23179      return;
23180    this._originalFill = value;
23181    if (this._isFillStyle(value)) {
23182      this._originalFill = this._createProxy({ ...GraphicsContext.defaultFillStyle, ...value }, () => {
23183        this._fill = toFillStyle(
23184          { ...this._originalFill },
23185          GraphicsContext.defaultFillStyle
23186        );
23187      });
23188    }
23189    this._fill = toFillStyle(
23190      value === 0 ? "black" : value,
23191      GraphicsContext.defaultFillStyle
23192    );
23193    this.update();
23194  }
23195  /** A fillstyle that will be used on the text stroke, e.g., 'blue', '#FCFF00'. */
23196  get stroke() {
23197    return this._originalStroke;
23198  }
23199  set stroke(value) {
23200    if (value === this._originalStroke)
23201      return;
23202    this._originalStroke = value;
23203    if (this._isFillStyle(value)) {
23204      this._originalStroke = this._createProxy({ ...GraphicsContext.defaultStrokeStyle, ...value }, () => {
23205        this._stroke = toStrokeStyle(
23206          { ...this._originalStroke },
23207          GraphicsContext.defaultStrokeStyle
23208        );
23209      });
23210    }
23211    this._stroke = toStrokeStyle(value, GraphicsContext.defaultStrokeStyle);
23212    this.update();
23213  }
23214  update() {
23215    this._tick++;
23216    this.emit("update", this);
23217  }
23218  /** Resets all properties to the default values */
23219  reset() {
23220    const defaultStyle = _TextStyle.defaultTextStyle;
23221    for (const key in defaultStyle) {
23222      this[key] = defaultStyle[key];
23223    }
23224  }
23225  /**
23226   * Returns a unique key for this instance.
23227   * This key is used for caching.
23228   * @returns {string} Unique key for the instance
23229   */
23230  get styleKey() {
23231    return `${this.uid}-${this._tick}`;
23232  }
23233  /**
23234   * Creates a new TextStyle object with the same values as this one.
23235   * @returns New cloned TextStyle object
23236   */
23237  clone() {
23238    return new _TextStyle({
23239      align: this.align,
23240      breakWords: this.breakWords,
23241      dropShadow: this._dropShadow ? { ...this._dropShadow } : null,
23242      fill: this._fill,
23243      fontFamily: this.fontFamily,
23244      fontSize: this.fontSize,
23245      fontStyle: this.fontStyle,
23246      fontVariant: this.fontVariant,
23247      fontWeight: this.fontWeight,
23248      leading: this.leading,
23249      letterSpacing: this.letterSpacing,
23250      lineHeight: this.lineHeight,
23251      padding: this.padding,
23252      stroke: this._stroke,
23253      textBaseline: this.textBaseline,
23254      whiteSpace: this.whiteSpace,
23255      wordWrap: this.wordWrap,
23256      wordWrapWidth: this.wordWrapWidth,
23257      filters: this._filters ? [...this._filters] : void 0
23258    });
23259  }
23260  /**
23261   * Returns the final padding for the text style, taking into account any filters applied.
23262   * Used internally for correct measurements
23263   * @internal
23264   * @returns {number} The final padding for the text style.
23265   */
23266  _getFinalPadding() {
23267    let filterPadding = 0;
23268    if (this._filters) {
23269      for (let i = 0; i < this._filters.length; i++) {
23270        filterPadding += this._filters[i].padding;
23271      }
23272    }
23273    return Math.max(this._padding, filterPadding);
23274  }
23275  /**
23276   * Destroys this text style.
23277   * @param options - Options parameter. A boolean will act as if all options
23278   *  have been set to that value
23279   * @example
23280   * // Destroy the text style and its textures
23281   * textStyle.destroy({ texture: true, textureSource: true });
23282   * textStyle.destroy(true);
23283   */
23284  destroy(options = false) {
23285    this.removeAllListeners();
23286    const destroyTexture = typeof options === "boolean" ? options : options?.texture;
23287    if (destroyTexture) {
23288      const destroyTextureSource = typeof options === "boolean" ? options : options?.textureSource;
23289      if (this._fill?.texture) {
23290        this._fill.texture.destroy(destroyTextureSource);
23291      }
23292      if (this._originalFill?.texture) {
23293        this._originalFill.texture.destroy(destroyTextureSource);
23294      }
23295      if (this._stroke?.texture) {
23296        this._stroke.texture.destroy(destroyTextureSource);
23297      }
23298      if (this._originalStroke?.texture) {
23299        this._originalStroke.texture.destroy(destroyTextureSource);
23300      }
23301    }
23302    this._fill = null;
23303    this._stroke = null;
23304    this.dropShadow = null;
23305    this._originalStroke = null;
23306    this._originalFill = null;
23307  }
23308  _createProxy(value, cb) {
23309    return new Proxy(value, {
23310      set: (target, property, newValue) => {
23311        if (target[property] === newValue)
23312          return true;
23313        target[property] = newValue;
23314        cb?.(property, newValue);
23315        this.update();
23316        return true;
23317      }
23318    });
23319  }
23320  _isFillStyle(value) {
23321    return (value ?? null) !== null && !(Color.isColorLike(value) || value instanceof FillGradient || value instanceof FillPattern);
23322  }
23323};
23324/**
23325 * Default drop shadow settings used when enabling drop shadows on text.
23326 * These values are used as the base configuration when drop shadows are enabled without specific settings.
23327 * @example
23328 * ```ts
23329 * // Customize default settings globally
23330 * TextStyle.defaultDropShadow.alpha = 0.5;    // 50% opacity for all shadows
23331 * TextStyle.defaultDropShadow.blur = 2;       // 2px blur for all shadows
23332 * TextStyle.defaultDropShadow.color = 'blue'; // Blue shadows by default
23333 * ```
23334 */
23335_TextStyle.defaultDropShadow = {
23336  alpha: 1,
23337  angle: Math.PI / 6,
23338  blur: 0,
23339  color: "black",
23340  distance: 5
23341};
23342/**
23343 * Default text style settings used when creating new text objects.
23344 * These values serve as the base configuration and can be customized globally.
23345 * @example
23346 * ```ts
23347 * // Customize default text style globally
23348 * TextStyle.defaultTextStyle.fontSize = 16;
23349 * TextStyle.defaultTextStyle.fill = 0x333333;
23350 * TextStyle.defaultTextStyle.fontFamily = ['Arial', 'Helvetica', 'sans-serif'];
23351 * ```
23352 */
23353_TextStyle.defaultTextStyle = {
23354  align: "left",
23355  breakWords: false,
23356  dropShadow: null,
23357  fill: "black",
23358  fontFamily: "Arial",
23359  fontSize: 26,
23360  fontStyle: "normal",
23361  fontVariant: "normal",
23362  fontWeight: "normal",
23363  leading: 0,
23364  letterSpacing: 0,
23365  lineHeight: 0,
23366  padding: 0,
23367  stroke: null,
23368  textBaseline: "alphabetic",
23369  trim: false,
23370  whiteSpace: "pre",
23371  wordWrap: false,
23372  wordWrapWidth: 100
23373};
23374let TextStyle = _TextStyle;
23375function convertV7Tov8Style(style) {
23376  const oldStyle = style;
23377  if (typeof oldStyle.dropShadow === "boolean" && oldStyle.dropShadow) {
23378    const defaults = TextStyle.defaultDropShadow;
23379    style.dropShadow = {
23380      alpha: oldStyle.dropShadowAlpha ?? defaults.alpha,
23381      angle: oldStyle.dropShadowAngle ?? defaults.angle,
23382      blur: oldStyle.dropShadowBlur ?? defaults.blur,
23383      color: oldStyle.dropShadowColor ?? defaults.color,
23384      distance: oldStyle.dropShadowDistance ?? defaults.distance
23385    };
23386  }
23387  if (oldStyle.strokeThickness !== void 0) {
23388    deprecation(v8_0_0, "strokeThickness is now a part of stroke");
23389    const color = oldStyle.stroke;
23390    let obj = {};
23391    if (Color.isColorLike(color)) {
23392      obj.color = color;
23393    } else if (color instanceof FillGradient || color instanceof FillPattern) {
23394      obj.fill = color;
23395    } else if (Object.hasOwnProperty.call(color, "color") || Object.hasOwnProperty.call(color, "fill")) {
23396      obj = color;
23397    } else {
23398      throw new Error("Invalid stroke value.");
23399    }
23400    style.stroke = {
23401      ...obj,
23402      width: oldStyle.strokeThickness
23403    };
23404  }
23405  if (Array.isArray(oldStyle.fillGradientStops)) {
23406    deprecation(v8_0_0, "gradient fill is now a fill pattern: `new FillGradient(...)`");
23407    if (!Array.isArray(oldStyle.fill) || oldStyle.fill.length === 0) {
23408      throw new Error("Invalid fill value. Expected an array of colors for gradient fill.");
23409    }
23410    if (oldStyle.fill.length !== oldStyle.fillGradientStops.length) {
23411      warn("The number of fill colors must match the number of fill gradient stops.");
23412    }
23413    const gradientFill = new FillGradient({
23414      start: { x: 0, y: 0 },
23415      end: { x: 0, y: 1 },
23416      textureSpace: "local"
23417    });
23418    const fillGradientStops = oldStyle.fillGradientStops.slice();
23419    const fills = oldStyle.fill.map((color) => Color.shared.setValue(color).toNumber());
23420    fillGradientStops.forEach((stop, index) => {
23421      gradientFill.addColorStop(stop, fills[index]);
23422    });
23423    style.fill = {
23424      fill: gradientFill
23425    };
23426  }
23427}
23428
23429class CanvasPoolClass {
23430  constructor(canvasOptions) {
23431    this._canvasPool = /* @__PURE__ */ Object.create(null);
23432    this.canvasOptions = canvasOptions || {};
23433    this.enableFullScreen = false;
23434  }
23435  /**
23436   * Creates texture with params that were specified in pool constructor.
23437   * @param pixelWidth - Width of texture in pixels.
23438   * @param pixelHeight - Height of texture in pixels.
23439   */
23440  _createCanvasAndContext(pixelWidth, pixelHeight) {
23441    const canvas = DOMAdapter.get().createCanvas();
23442    canvas.width = pixelWidth;
23443    canvas.height = pixelHeight;
23444    const context = canvas.getContext("2d");
23445    return { canvas, context };
23446  }
23447  /**
23448   * Gets a Power-of-Two render texture or fullScreen texture
23449   * @param minWidth - The minimum width of the render texture.
23450   * @param minHeight - The minimum height of the render texture.
23451   * @param resolution - The resolution of the render texture.
23452   * @returns The new render texture.
23453   */
23454  getOptimalCanvasAndContext(minWidth, minHeight, resolution = 1) {
23455    minWidth = Math.ceil(minWidth * resolution - 1e-6);
23456    minHeight = Math.ceil(minHeight * resolution - 1e-6);
23457    minWidth = nextPow2(minWidth);
23458    minHeight = nextPow2(minHeight);
23459    const key = (minWidth << 17) + (minHeight << 1);
23460    if (!this._canvasPool[key]) {
23461      this._canvasPool[key] = [];
23462    }
23463    let canvasAndContext = this._canvasPool[key].pop();
23464    if (!canvasAndContext) {
23465      canvasAndContext = this._createCanvasAndContext(minWidth, minHeight);
23466    }
23467    return canvasAndContext;
23468  }
23469  /**
23470   * Place a render texture back into the pool.
23471   * @param canvasAndContext
23472   */
23473  returnCanvasAndContext(canvasAndContext) {
23474    const canvas = canvasAndContext.canvas;
23475    const { width, height } = canvas;
23476    const key = (width << 17) + (height << 1);
23477    canvasAndContext.context.resetTransform();
23478    canvasAndContext.context.clearRect(0, 0, width, height);
23479    this._canvasPool[key].push(canvasAndContext);
23480  }
23481  clear() {
23482    this._canvasPool = {};
23483  }
23484}
23485const CanvasPool = new CanvasPoolClass();
23486GlobalResourceRegistry.register(CanvasPool);
23487
23488const PRECISION = 1e5;
23489function getCanvasFillStyle(fillStyle, context, textMetrics, padding = 0) {
23490  if (fillStyle.texture === Texture.WHITE && !fillStyle.fill) {
23491    return Color.shared.setValue(fillStyle.color).setAlpha(fillStyle.alpha ?? 1).toHexa();
23492  } else if (!fillStyle.fill) {
23493    const pattern = context.createPattern(fillStyle.texture.source.resource, "repeat");
23494    const tempMatrix = fillStyle.matrix.copyTo(Matrix.shared);
23495    tempMatrix.scale(fillStyle.texture.frame.width, fillStyle.texture.frame.height);
23496    pattern.setTransform(tempMatrix);
23497    return pattern;
23498  } else if (fillStyle.fill instanceof FillPattern) {
23499    const fillPattern = fillStyle.fill;
23500    const pattern = context.createPattern(fillPattern.texture.source.resource, "repeat");
23501    const tempMatrix = fillPattern.transform.copyTo(Matrix.shared);
23502    tempMatrix.scale(
23503      fillPattern.texture.frame.width,
23504      fillPattern.texture.frame.height
23505    );
23506    pattern.setTransform(tempMatrix);
23507    return pattern;
23508  } else if (fillStyle.fill instanceof FillGradient) {
23509    const fillGradient = fillStyle.fill;
23510    const isLinear = fillGradient.type === "linear";
23511    const isLocal = fillGradient.textureSpace === "local";
23512    let width = 1;
23513    let height = 1;
23514    if (isLocal && textMetrics) {
23515      width = textMetrics.width + padding;
23516      height = textMetrics.height + padding;
23517    }
23518    let gradient;
23519    let isNearlyVertical = false;
23520    if (isLinear) {
23521      const { start, end } = fillGradient;
23522      gradient = context.createLinearGradient(
23523        start.x * width,
23524        start.y * height,
23525        end.x * width,
23526        end.y * height
23527      );
23528      isNearlyVertical = Math.abs(end.x - start.x) < Math.abs((end.y - start.y) * 0.1);
23529    } else {
23530      const { center, innerRadius, outerCenter, outerRadius } = fillGradient;
23531      gradient = context.createRadialGradient(
23532        center.x * width,
23533        center.y * height,
23534        innerRadius * width,
23535        outerCenter.x * width,
23536        outerCenter.y * height,
23537        outerRadius * width
23538      );
23539    }
23540    if (isNearlyVertical && isLocal && textMetrics) {
23541      const ratio = textMetrics.lineHeight / height;
23542      for (let i = 0; i < textMetrics.lines.length; i++) {
23543        const start = (i * textMetrics.lineHeight + padding / 2) / height;
23544        fillGradient.colorStops.forEach((stop) => {
23545          const globalStop = start + stop.offset * ratio;
23546          gradient.addColorStop(
23547            // fix to 5 decimal places to avoid floating point precision issues
23548            Math.floor(globalStop * PRECISION) / PRECISION,
23549            Color.shared.setValue(stop.color).toHex()
23550          );
23551        });
23552      }
23553    } else {
23554      fillGradient.colorStops.forEach((stop) => {
23555        gradient.addColorStop(stop.offset, Color.shared.setValue(stop.color).toHex());
23556      });
23557    }
23558    return gradient;
23559  }
23560  warn("FillStyle not recognised", fillStyle);
23561  return "red";
23562}
23563
23564const _DynamicBitmapFont = class _DynamicBitmapFont extends AbstractBitmapFont {
23565  /**
23566   * @param options - The options for the dynamic bitmap font.
23567   */
23568  constructor(options) {
23569    super();
23570    /**
23571     * this is a resolution modifier for the font size..
23572     * texture resolution will also be used to scale texture according to its font size also
23573     */
23574    this.resolution = 1;
23575    /** The pages of the font. */
23576    this.pages = [];
23577    this._padding = 0;
23578    this._measureCache = /* @__PURE__ */ Object.create(null);
23579    this._currentChars = [];
23580    this._currentX = 0;
23581    this._currentY = 0;
23582    this._currentMaxCharHeight = 0;
23583    this._currentPageIndex = -1;
23584    this._skipKerning = false;
23585    const dynamicOptions = { ..._DynamicBitmapFont.defaultOptions, ...options };
23586    this._textureSize = dynamicOptions.textureSize;
23587    this._mipmap = dynamicOptions.mipmap;
23588    const style = dynamicOptions.style.clone();
23589    if (dynamicOptions.overrideFill) {
23590      style._fill.color = 16777215;
23591      style._fill.alpha = 1;
23592      style._fill.texture = Texture.WHITE;
23593      style._fill.fill = null;
23594    }
23595    this.applyFillAsTint = dynamicOptions.overrideFill;
23596    const requestedFontSize = style.fontSize;
23597    style.fontSize = this.baseMeasurementFontSize;
23598    const font = fontStringFromTextStyle(style);
23599    if (dynamicOptions.overrideSize) {
23600      if (style._stroke) {
23601        style._stroke.width *= this.baseRenderedFontSize / requestedFontSize;
23602      }
23603    } else {
23604      style.fontSize = this.baseRenderedFontSize = requestedFontSize;
23605    }
23606    this._style = style;
23607    this._skipKerning = dynamicOptions.skipKerning ?? false;
23608    this.resolution = dynamicOptions.resolution ?? 1;
23609    this._padding = dynamicOptions.padding ?? 4;
23610    if (dynamicOptions.textureStyle) {
23611      this._textureStyle = dynamicOptions.textureStyle instanceof TextureStyle ? dynamicOptions.textureStyle : new TextureStyle(dynamicOptions.textureStyle);
23612    }
23613    this.fontMetrics = CanvasTextMetrics.measureFont(font);
23614    this.lineHeight = style.lineHeight || this.fontMetrics.fontSize || style.fontSize;
23615  }
23616  ensureCharacters(chars) {
23617    const charList = CanvasTextMetrics.graphemeSegmenter(chars).filter((char) => !this._currentChars.includes(char)).filter((char, index, self) => self.indexOf(char) === index);
23618    if (!charList.length)
23619      return;
23620    this._currentChars = [...this._currentChars, ...charList];
23621    let pageData;
23622    if (this._currentPageIndex === -1) {
23623      pageData = this._nextPage();
23624    } else {
23625      pageData = this.pages[this._currentPageIndex];
23626    }
23627    let { canvas, context } = pageData.canvasAndContext;
23628    let textureSource = pageData.texture.source;
23629    const style = this._style;
23630    let currentX = this._currentX;
23631    let currentY = this._currentY;
23632    let currentMaxCharHeight = this._currentMaxCharHeight;
23633    const fontScale = this.baseRenderedFontSize / this.baseMeasurementFontSize;
23634    const padding = this._padding * fontScale;
23635    let skipTexture = false;
23636    const maxTextureWidth = canvas.width / this.resolution;
23637    const maxTextureHeight = canvas.height / this.resolution;
23638    for (let i = 0; i < charList.length; i++) {
23639      const char = charList[i];
23640      const metrics = CanvasTextMetrics.measureText(char, style, canvas, false);
23641      metrics.lineHeight = metrics.height;
23642      const width = metrics.width * fontScale;
23643      const textureGlyphWidth = Math.ceil((style.fontStyle === "italic" ? 2 : 1) * width);
23644      const height = metrics.height * fontScale;
23645      const paddedWidth = textureGlyphWidth + padding * 2;
23646      const paddedHeight = height + padding * 2;
23647      skipTexture = false;
23648      if (char !== "\n" && char !== "\r" && char !== "	" && char !== " ") {
23649        skipTexture = true;
23650        currentMaxCharHeight = Math.ceil(Math.max(paddedHeight, currentMaxCharHeight));
23651      }
23652      if (currentX + paddedWidth > maxTextureWidth) {
23653        currentY += currentMaxCharHeight;
23654        currentMaxCharHeight = paddedHeight;
23655        currentX = 0;
23656        if (currentY + currentMaxCharHeight > maxTextureHeight) {
23657          textureSource.update();
23658          const pageData2 = this._nextPage();
23659          canvas = pageData2.canvasAndContext.canvas;
23660          context = pageData2.canvasAndContext.context;
23661          textureSource = pageData2.texture.source;
23662          currentX = 0;
23663          currentY = 0;
23664          currentMaxCharHeight = 0;
23665        }
23666      }
23667      const xAdvance = width / fontScale - (style.dropShadow?.distance ?? 0) - (style._stroke?.width ?? 0);
23668      this.chars[char] = {
23669        id: char.codePointAt(0),
23670        xOffset: -this._padding,
23671        yOffset: -this._padding,
23672        xAdvance,
23673        kerning: {}
23674      };
23675      if (skipTexture) {
23676        this._drawGlyph(
23677          context,
23678          metrics,
23679          currentX + padding,
23680          currentY + padding,
23681          fontScale,
23682          style
23683        );
23684        const px = textureSource.width * fontScale;
23685        const py = textureSource.height * fontScale;
23686        const frame = new Rectangle(
23687          currentX / px * textureSource.width,
23688          currentY / py * textureSource.height,
23689          paddedWidth / px * textureSource.width,
23690          paddedHeight / py * textureSource.height
23691        );
23692        this.chars[char].texture = new Texture({
23693          source: textureSource,
23694          frame
23695        });
23696        currentX += Math.ceil(paddedWidth);
23697      }
23698    }
23699    textureSource.update();
23700    this._currentX = currentX;
23701    this._currentY = currentY;
23702    this._currentMaxCharHeight = currentMaxCharHeight;
23703    this._skipKerning && this._applyKerning(charList, context);
23704  }
23705  /**
23706   * @deprecated since 8.0.0
23707   * The map of base page textures (i.e., sheets of glyphs).
23708   */
23709  get pageTextures() {
23710    deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
23711    return this.pages;
23712  }
23713  _applyKerning(newChars, context) {
23714    const measureCache = this._measureCache;
23715    for (let i = 0; i < newChars.length; i++) {
23716      const first = newChars[i];
23717      for (let j = 0; j < this._currentChars.length; j++) {
23718        const second = this._currentChars[j];
23719        let c1 = measureCache[first];
23720        if (!c1)
23721          c1 = measureCache[first] = context.measureText(first).width;
23722        let c2 = measureCache[second];
23723        if (!c2)
23724          c2 = measureCache[second] = context.measureText(second).width;
23725        let total = context.measureText(first + second).width;
23726        let amount = total - (c1 + c2);
23727        if (amount) {
23728          this.chars[first].kerning[second] = amount;
23729        }
23730        total = context.measureText(first + second).width;
23731        amount = total - (c1 + c2);
23732        if (amount) {
23733          this.chars[second].kerning[first] = amount;
23734        }
23735      }
23736    }
23737  }
23738  _nextPage() {
23739    this._currentPageIndex++;
23740    const textureResolution = this.resolution;
23741    const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(
23742      this._textureSize,
23743      this._textureSize,
23744      textureResolution
23745    );
23746    this._setupContext(canvasAndContext.context, this._style, textureResolution);
23747    const resolution = textureResolution * (this.baseRenderedFontSize / this.baseMeasurementFontSize);
23748    const texture = new Texture({
23749      source: new ImageSource({
23750        resource: canvasAndContext.canvas,
23751        resolution,
23752        alphaMode: "premultiply-alpha-on-upload",
23753        autoGenerateMipmaps: this._mipmap
23754      })
23755    });
23756    if (this._textureStyle) {
23757      texture.source.style = this._textureStyle;
23758    }
23759    const pageData = {
23760      canvasAndContext,
23761      texture
23762    };
23763    this.pages[this._currentPageIndex] = pageData;
23764    return pageData;
23765  }
23766  // canvas style!
23767  _setupContext(context, style, resolution) {
23768    style.fontSize = this.baseRenderedFontSize;
23769    context.scale(resolution, resolution);
23770    context.font = fontStringFromTextStyle(style);
23771    style.fontSize = this.baseMeasurementFontSize;
23772    context.textBaseline = style.textBaseline;
23773    const stroke = style._stroke;
23774    const strokeThickness = stroke?.width ?? 0;
23775    if (stroke) {
23776      context.lineWidth = strokeThickness;
23777      context.lineJoin = stroke.join;
23778      context.miterLimit = stroke.miterLimit;
23779      context.strokeStyle = getCanvasFillStyle(stroke, context);
23780    }
23781    if (style._fill) {
23782      context.fillStyle = getCanvasFillStyle(style._fill, context);
23783    }
23784    if (style.dropShadow) {
23785      const shadowOptions = style.dropShadow;
23786      const rgb = Color.shared.setValue(shadowOptions.color).toArray();
23787      const dropShadowBlur = shadowOptions.blur * resolution;
23788      const dropShadowDistance = shadowOptions.distance * resolution;
23789      context.shadowColor = `rgba(${rgb[0] * 255},${rgb[1] * 255},${rgb[2] * 255},${shadowOptions.alpha})`;
23790      context.shadowBlur = dropShadowBlur;
23791      context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance;
23792      context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance;
23793    } else {
23794      context.shadowColor = "black";
23795      context.shadowBlur = 0;
23796      context.shadowOffsetX = 0;
23797      context.shadowOffsetY = 0;
23798    }
23799  }
23800  _drawGlyph(context, metrics, x, y, fontScale, style) {
23801    const char = metrics.text;
23802    const fontProperties = metrics.fontProperties;
23803    const stroke = style._stroke;
23804    const strokeThickness = (stroke?.width ?? 0) * fontScale;
23805    const tx = x + strokeThickness / 2;
23806    const ty = y - strokeThickness / 2;
23807    const descent = fontProperties.descent * fontScale;
23808    const lineHeight = metrics.lineHeight * fontScale;
23809    let removeShadow = false;
23810    if (style.stroke && strokeThickness) {
23811      removeShadow = true;
23812      context.strokeText(char, tx, ty + lineHeight - descent);
23813    }
23814    const { shadowBlur, shadowOffsetX, shadowOffsetY } = context;
23815    if (style._fill) {
23816      if (removeShadow) {
23817        context.shadowBlur = 0;
23818        context.shadowOffsetX = 0;
23819        context.shadowOffsetY = 0;
23820      }
23821      context.fillText(char, tx, ty + lineHeight - descent);
23822    }
23823    if (removeShadow) {
23824      context.shadowBlur = shadowBlur;
23825      context.shadowOffsetX = shadowOffsetX;
23826      context.shadowOffsetY = shadowOffsetY;
23827    }
23828  }
23829  destroy() {
23830    super.destroy();
23831    for (let i = 0; i < this.pages.length; i++) {
23832      const { canvasAndContext, texture } = this.pages[i];
23833      CanvasPool.returnCanvasAndContext(canvasAndContext);
23834      texture.destroy(true);
23835    }
23836    this.pages = null;
23837  }
23838};
23839_DynamicBitmapFont.defaultOptions = {
23840  textureSize: 512,
23841  style: new TextStyle(),
23842  mipmap: true
23843};
23844let DynamicBitmapFont = _DynamicBitmapFont;
23845
23846function getBitmapTextLayout(chars, style, font, trimEnd) {
23847  const layoutData = {
23848    width: 0,
23849    height: 0,
23850    offsetY: 0,
23851    scale: style.fontSize / font.baseMeasurementFontSize,
23852    lines: [{
23853      width: 0,
23854      charPositions: [],
23855      spaceWidth: 0,
23856      spacesIndex: [],
23857      chars: []
23858    }]
23859  };
23860  layoutData.offsetY = font.baseLineOffset;
23861  let currentLine = layoutData.lines[0];
23862  let previousChar = null;
23863  let firstWord = true;
23864  const currentWord = {
23865    width: 0,
23866    start: 0,
23867    index: 0,
23868    // use index to not modify the array as we use it a lot!
23869    positions: [],
23870    chars: []
23871  };
23872  const scale = font.baseMeasurementFontSize / style.fontSize;
23873  const adjustedLetterSpacing = style.letterSpacing * scale;
23874  const adjustedWordWrapWidth = style.wordWrapWidth * scale;
23875  const adjustedLineHeight = style.lineHeight ? style.lineHeight * scale : font.lineHeight;
23876  const breakWords = style.wordWrap && style.breakWords;
23877  const nextWord = (word) => {
23878    const start = currentLine.width;
23879    for (let j = 0; j < currentWord.index; j++) {
23880      const position = word.positions[j];
23881      currentLine.chars.push(word.chars[j]);
23882      currentLine.charPositions.push(position + start);
23883    }
23884    currentLine.width += word.width;
23885    firstWord = false;
23886    currentWord.width = 0;
23887    currentWord.index = 0;
23888    currentWord.chars.length = 0;
23889  };
23890  const nextLine = () => {
23891    let index = currentLine.chars.length - 1;
23892    if (trimEnd) {
23893      let lastChar = currentLine.chars[index];
23894      while (lastChar === " ") {
23895        currentLine.width -= font.chars[lastChar].xAdvance;
23896        lastChar = currentLine.chars[--index];
23897      }
23898    }
23899    layoutData.width = Math.max(layoutData.width, currentLine.width);
23900    currentLine = {
23901      width: 0,
23902      charPositions: [],
23903      chars: [],
23904      spaceWidth: 0,
23905      spacesIndex: []
23906    };
23907    firstWord = true;
23908    layoutData.lines.push(currentLine);
23909    layoutData.height += adjustedLineHeight;
23910  };
23911  const checkIsOverflow = (lineWidth) => lineWidth - adjustedLetterSpacing > adjustedWordWrapWidth;
23912  for (let i = 0; i < chars.length + 1; i++) {
23913    let char;
23914    const isEnd = i === chars.length;
23915    if (!isEnd) {
23916      char = chars[i];
23917    }
23918    const charData = font.chars[char] || font.chars[" "];
23919    const isSpace = /(?:\s)/.test(char);
23920    const isWordBreak = isSpace || char === "\r" || char === "\n" || isEnd;
23921    if (isWordBreak) {
23922      const addWordToNextLine = !firstWord && style.wordWrap && checkIsOverflow(currentLine.width + currentWord.width);
23923      if (addWordToNextLine) {
23924        nextLine();
23925        nextWord(currentWord);
23926        if (!isEnd) {
23927          currentLine.charPositions.push(0);
23928        }
23929      } else {
23930        currentWord.start = currentLine.width;
23931        nextWord(currentWord);
23932        if (!isEnd) {
23933          currentLine.charPositions.push(0);
23934        }
23935      }
23936      if (char === "\r" || char === "\n") {
23937        nextLine();
23938      } else if (!isEnd) {
23939        const spaceWidth = charData.xAdvance + (charData.kerning[previousChar] || 0) + adjustedLetterSpacing;
23940        currentLine.width += spaceWidth;
23941        currentLine.spaceWidth = spaceWidth;
23942        currentLine.spacesIndex.push(currentLine.charPositions.length);
23943        currentLine.chars.push(char);
23944      }
23945    } else {
23946      const kerning = charData.kerning[previousChar] || 0;
23947      const nextCharWidth = charData.xAdvance + kerning + adjustedLetterSpacing;
23948      const addWordToNextLine = breakWords && checkIsOverflow(currentLine.width + currentWord.width + nextCharWidth);
23949      if (addWordToNextLine) {
23950        nextWord(currentWord);
23951        nextLine();
23952      }
23953      currentWord.positions[currentWord.index++] = currentWord.width + kerning;
23954      currentWord.chars.push(char);
23955      currentWord.width += nextCharWidth;
23956    }
23957    previousChar = char;
23958  }
23959  nextLine();
23960  if (style.align === "center") {
23961    alignCenter(layoutData);
23962  } else if (style.align === "right") {
23963    alignRight(layoutData);
23964  } else if (style.align === "justify") {
23965    alignJustify(layoutData);
23966  }
23967  return layoutData;
23968}
23969function alignCenter(measurementData) {
23970  for (let i = 0; i < measurementData.lines.length; i++) {
23971    const line = measurementData.lines[i];
23972    const offset = measurementData.width / 2 - line.width / 2;
23973    for (let j = 0; j < line.charPositions.length; j++) {
23974      line.charPositions[j] += offset;
23975    }
23976  }
23977}
23978function alignRight(measurementData) {
23979  for (let i = 0; i < measurementData.lines.length; i++) {
23980    const line = measurementData.lines[i];
23981    const offset = measurementData.width - line.width;
23982    for (let j = 0; j < line.charPositions.length; j++) {
23983      line.charPositions[j] += offset;
23984    }
23985  }
23986}
23987function alignJustify(measurementData) {
23988  const width = measurementData.width;
23989  for (let i = 0; i < measurementData.lines.length; i++) {
23990    const line = measurementData.lines[i];
23991    let indy = 0;
23992    let spaceIndex = line.spacesIndex[indy++];
23993    let offset = 0;
23994    const totalSpaces = line.spacesIndex.length;
23995    const newSpaceWidth = (width - line.width) / totalSpaces;
23996    const spaceWidth = newSpaceWidth;
23997    for (let j = 0; j < line.charPositions.length; j++) {
23998      if (j === spaceIndex) {
23999        spaceIndex = line.spacesIndex[indy++];
24000        offset += spaceWidth;
24001      }
24002      line.charPositions[j] += offset;
24003    }
24004  }
24005}
24006
24007function resolveCharacters(chars) {
24008  if (chars === "") {
24009    return [];
24010  }
24011  if (typeof chars === "string") {
24012    chars = [chars];
24013  }
24014  const result = [];
24015  for (let i = 0, j = chars.length; i < j; i++) {
24016    const item = chars[i];
24017    if (Array.isArray(item)) {
24018      if (item.length !== 2) {
24019        throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`);
24020      }
24021      if (item[0].length === 0 || item[1].length === 0) {
24022        throw new Error("[BitmapFont]: Invalid character delimiter.");
24023      }
24024      const startCode = item[0].charCodeAt(0);
24025      const endCode = item[1].charCodeAt(0);
24026      if (endCode < startCode) {
24027        throw new Error("[BitmapFont]: Invalid character range.");
24028      }
24029      for (let i2 = startCode, j2 = endCode; i2 <= j2; i2++) {
24030        result.push(String.fromCharCode(i2));
24031      }
24032    } else {
24033      result.push(...Array.from(item));
24034    }
24035  }
24036  if (result.length === 0) {
24037    throw new Error("[BitmapFont]: Empty set when resolving characters.");
24038  }
24039  return result;
24040}
24041
24042let fontCount = 0;
24043class BitmapFontManagerClass {
24044  constructor() {
24045    /**
24046     * This character set includes all the letters in the alphabet (both lower- and upper- case).
24047     * @type {string[][]}
24048     * @example
24049     * BitmapFont.from('ExampleFont', style, { chars: BitmapFont.ALPHA })
24050     */
24051    this.ALPHA = [["a", "z"], ["A", "Z"], " "];
24052    /**
24053     * This character set includes all decimal digits (from 0 to 9).
24054     * @type {string[][]}
24055     * @example
24056     * BitmapFont.from('ExampleFont', style, { chars: BitmapFont.NUMERIC })
24057     */
24058    this.NUMERIC = [["0", "9"]];
24059    /**
24060     * This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`.
24061     * @type {string[][]}
24062     */
24063    this.ALPHANUMERIC = [["a", "z"], ["A", "Z"], ["0", "9"], " "];
24064    /**
24065     * This character set consists of all the ASCII table.
24066     * @type {string[][]}
24067     * @see http://www.asciitable.com/
24068     */
24069    this.ASCII = [[" ", "~"]];
24070    /** Default options for installing a new BitmapFont. */
24071    this.defaultOptions = {
24072      chars: this.ALPHANUMERIC,
24073      resolution: 1,
24074      padding: 4,
24075      skipKerning: false,
24076      textureStyle: null
24077    };
24078    /** Cache for measured text layouts to avoid recalculating them multiple times. */
24079    this.measureCache = lru(1e3);
24080  }
24081  /**
24082   * Get a font for the specified text and style.
24083   * @param text - The text to get the font for
24084   * @param style - The style to use
24085   */
24086  getFont(text, style) {
24087    let fontFamilyKey = `${style.fontFamily}-bitmap`;
24088    let overrideFill = true;
24089    if (style._fill.fill && !style._stroke) {
24090      fontFamilyKey += style._fill.fill.styleKey;
24091      overrideFill = false;
24092    } else if (style._stroke || style.dropShadow) {
24093      fontFamilyKey = `${style.styleKey}-bitmap`;
24094      overrideFill = false;
24095    }
24096    if (!Cache.has(fontFamilyKey)) {
24097      const styleCopy = Object.create(style);
24098      styleCopy.lineHeight = 0;
24099      const fnt = new DynamicBitmapFont({
24100        style: styleCopy,
24101        overrideFill,
24102        overrideSize: true,
24103        ...this.defaultOptions
24104      });
24105      fontCount++;
24106      if (fontCount > 50) {
24107        warn("BitmapText", `You have dynamically created ${fontCount} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``);
24108      }
24109      fnt.once("destroy", () => {
24110        fontCount--;
24111        Cache.remove(fontFamilyKey);
24112      });
24113      Cache.set(
24114        fontFamilyKey,
24115        fnt
24116      );
24117    }
24118    const dynamicFont = Cache.get(fontFamilyKey);
24119    dynamicFont.ensureCharacters?.(text);
24120    return dynamicFont;
24121  }
24122  /**
24123   * Get the layout of a text for the specified style.
24124   * @param text - The text to get the layout for
24125   * @param style - The style to use
24126   * @param trimEnd - Whether to ignore whitespaces at the end of each line
24127   */
24128  getLayout(text, style, trimEnd = true) {
24129    const bitmapFont = this.getFont(text, style);
24130    const id = `${text}-${style.styleKey}-${trimEnd}`;
24131    if (this.measureCache.has(id)) {
24132      return this.measureCache.get(id);
24133    }
24134    const segments = CanvasTextMetrics.graphemeSegmenter(text);
24135    const layoutData = getBitmapTextLayout(segments, style, bitmapFont, trimEnd);
24136    this.measureCache.set(id, layoutData);
24137    return layoutData;
24138  }
24139  /**
24140   * Measure the text using the specified style.
24141   * @param text - The text to measure
24142   * @param style - The style to use
24143   * @param trimEnd - Whether to ignore whitespaces at the end of each line
24144   */
24145  measureText(text, style, trimEnd = true) {
24146    return this.getLayout(text, style, trimEnd);
24147  }
24148  // eslint-disable-next-line max-len
24149  install(...args) {
24150    let options = args[0];
24151    if (typeof options === "string") {
24152      options = {
24153        name: options,
24154        style: args[1],
24155        chars: args[2]?.chars,
24156        resolution: args[2]?.resolution,
24157        padding: args[2]?.padding,
24158        skipKerning: args[2]?.skipKerning
24159      };
24160      deprecation(v8_0_0, "BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})");
24161    }
24162    const name = options?.name;
24163    if (!name) {
24164      throw new Error("[BitmapFontManager] Property `name` is required.");
24165    }
24166    options = { ...this.defaultOptions, ...options };
24167    const textStyle = options.style;
24168    const style = textStyle instanceof TextStyle ? textStyle : new TextStyle(textStyle);
24169    const overrideFill = options.dynamicFill ?? this._canUseTintForStyle(style);
24170    const font = new DynamicBitmapFont({
24171      style,
24172      overrideFill,
24173      skipKerning: options.skipKerning,
24174      padding: options.padding,
24175      resolution: options.resolution,
24176      overrideSize: false,
24177      textureStyle: options.textureStyle
24178    });
24179    const flatChars = resolveCharacters(options.chars);
24180    font.ensureCharacters(flatChars.join(""));
24181    Cache.set(`${name}-bitmap`, font);
24182    font.once("destroy", () => Cache.remove(`${name}-bitmap`));
24183    return font;
24184  }
24185  /**
24186   * Uninstalls a bitmap font from the cache.
24187   * @param {string} name - The name of the bitmap font to uninstall.
24188   */
24189  uninstall(name) {
24190    const cacheKey = `${name}-bitmap`;
24191    const font = Cache.get(cacheKey);
24192    if (font) {
24193      font.destroy();
24194    }
24195  }
24196  /**
24197   * Determines if a style can use tinting instead of baking colors into the bitmap.
24198   * Tinting is more efficient as it allows reusing the same bitmap with different colors.
24199   * @param style - The text style to evaluate
24200   * @returns true if the style can use tinting, false if colors must be baked in
24201   * @private
24202   */
24203  _canUseTintForStyle(style) {
24204    return !style._stroke && (!style.dropShadow || style.dropShadow.color === 0) && !style._fill.fill && style._fill.color === 16777215;
24205  }
24206}
24207const BitmapFontManager = new BitmapFontManagerClass();
24208
24209class BitmapFont extends AbstractBitmapFont {
24210  constructor(options, url) {
24211    super();
24212    const { textures, data } = options;
24213    Object.keys(data.pages).forEach((key) => {
24214      const pageData = data.pages[parseInt(key, 10)];
24215      const texture = textures[pageData.id];
24216      this.pages.push({ texture });
24217    });
24218    Object.keys(data.chars).forEach((key) => {
24219      const charData = data.chars[key];
24220      const {
24221        frame: textureFrame,
24222        source: textureSource,
24223        rotate: textureRotate
24224      } = textures[charData.page];
24225      const frame = groupD8.transformRectCoords(
24226        charData,
24227        textureFrame,
24228        textureRotate,
24229        new Rectangle()
24230      );
24231      const texture = new Texture({
24232        frame,
24233        orig: new Rectangle(0, 0, charData.width, charData.height),
24234        source: textureSource,
24235        rotate: textureRotate
24236      });
24237      this.chars[key] = {
24238        id: key.codePointAt(0),
24239        xOffset: charData.xOffset,
24240        yOffset: charData.yOffset,
24241        xAdvance: charData.xAdvance,
24242        kerning: charData.kerning ?? {},
24243        texture
24244      };
24245    });
24246    this.baseRenderedFontSize = data.fontSize;
24247    this.baseMeasurementFontSize = data.fontSize;
24248    this.fontMetrics = {
24249      ascent: 0,
24250      descent: 0,
24251      fontSize: data.fontSize
24252    };
24253    this.baseLineOffset = data.baseLineOffset;
24254    this.lineHeight = data.lineHeight;
24255    this.fontFamily = data.fontFamily;
24256    this.distanceField = data.distanceField ?? {
24257      type: "none",
24258      range: 0
24259    };
24260    this.url = url;
24261  }
24262  /** Destroys the BitmapFont object. */
24263  destroy() {
24264    super.destroy();
24265    for (let i = 0; i < this.pages.length; i++) {
24266      const { texture } = this.pages[i];
24267      texture.destroy(true);
24268    }
24269    this.pages = null;
24270  }
24271  /**
24272   * Generates and installs a bitmap font with the specified options.
24273   * The font will be cached and available for use in BitmapText objects.
24274   * @param options - Setup options for font generation
24275   * @returns Installed font instance
24276   * @example
24277   * ```ts
24278   * // Install a basic font
24279   * BitmapFont.install({
24280   *     name: 'Title',
24281   *     style: {
24282   *         fontFamily: 'Arial',
24283   *         fontSize: 32,
24284   *         fill: '#ffffff'
24285   *     }
24286   * });
24287   *
24288   * // Install with advanced options
24289   * BitmapFont.install({
24290   *     name: 'Custom',
24291   *     style: {
24292   *         fontFamily: 'Arial',
24293   *         fontSize: 24,
24294   *         fill: '#00ff00',
24295   *         stroke: { color: '#000000', width: 2 }
24296   *     },
24297   *     chars: [['a', 'z'], ['A', 'Z'], ['0', '9']],
24298   *     resolution: 2,
24299   *     padding: 4,
24300   *     textureStyle: {
24301   *         scaleMode: 'nearest'
24302   *     }
24303   * });
24304   * ```
24305   */
24306  static install(options) {
24307    BitmapFontManager.install(options);
24308  }
24309  /**
24310   * Uninstalls a bitmap font from the cache.
24311   * This frees up memory and resources associated with the font.
24312   * @param name - The name of the bitmap font to uninstall
24313   * @example
24314   * ```ts
24315   * // Remove a font when it's no longer needed
24316   * BitmapFont.uninstall('MyCustomFont');
24317   *
24318   * // Clear multiple fonts
24319   * ['Title', 'Heading', 'Body'].forEach(BitmapFont.uninstall);
24320   * ```
24321   */
24322  static uninstall(name) {
24323    BitmapFontManager.uninstall(name);
24324  }
24325}
24326
24327const bitmapFontTextParser = {
24328  test(data) {
24329    return typeof data === "string" && data.startsWith("info face=");
24330  },
24331  parse(txt) {
24332    const items = txt.match(/^[a-z]+\s+.+$/gm);
24333    const rawData = {
24334      info: [],
24335      common: [],
24336      page: [],
24337      char: [],
24338      chars: [],
24339      kerning: [],
24340      kernings: [],
24341      distanceField: []
24342    };
24343    for (const i in items) {
24344      const name = items[i].match(/^[a-z]+/gm)[0];
24345      const attributeList = items[i].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm);
24346      const itemData = {};
24347      for (const i2 in attributeList) {
24348        const split = attributeList[i2].split("=");
24349        const key = split[0];
24350        const strValue = split[1].replace(/"/gm, "");
24351        const floatValue = parseFloat(strValue);
24352        const value = isNaN(floatValue) ? strValue : floatValue;
24353        itemData[key] = value;
24354      }
24355      rawData[name].push(itemData);
24356    }
24357    const font = {
24358      chars: {},
24359      pages: [],
24360      lineHeight: 0,
24361      fontSize: 0,
24362      fontFamily: "",
24363      distanceField: null,
24364      baseLineOffset: 0
24365    };
24366    const [info] = rawData.info;
24367    const [common] = rawData.common;
24368    const [distanceField] = rawData.distanceField ?? [];
24369    if (distanceField) {
24370      font.distanceField = {
24371        range: parseInt(distanceField.distanceRange, 10),
24372        type: distanceField.fieldType
24373      };
24374    }
24375    font.fontSize = parseInt(info.size, 10);
24376    font.fontFamily = info.face;
24377    font.lineHeight = parseInt(common.lineHeight, 10);
24378    const page = rawData.page;
24379    for (let i = 0; i < page.length; i++) {
24380      font.pages.push({
24381        id: parseInt(page[i].id, 10) || 0,
24382        file: page[i].file
24383      });
24384    }
24385    const map = {};
24386    font.baseLineOffset = font.lineHeight - parseInt(common.base, 10);
24387    const char = rawData.char;
24388    for (let i = 0; i < char.length; i++) {
24389      const charNode = char[i];
24390      const id = parseInt(charNode.id, 10);
24391      let letter = charNode.letter ?? charNode.char ?? String.fromCharCode(id);
24392      if (letter === "space")
24393        letter = " ";
24394      map[id] = letter;
24395      font.chars[letter] = {
24396        id,
24397        // texture deets..
24398        page: parseInt(charNode.page, 10) || 0,
24399        x: parseInt(charNode.x, 10),
24400        y: parseInt(charNode.y, 10),
24401        width: parseInt(charNode.width, 10),
24402        height: parseInt(charNode.height, 10),
24403        xOffset: parseInt(charNode.xoffset, 10),
24404        yOffset: parseInt(charNode.yoffset, 10),
24405        xAdvance: parseInt(charNode.xadvance, 10),
24406        kerning: {}
24407      };
24408    }
24409    const kerning = rawData.kerning || [];
24410    for (let i = 0; i < kerning.length; i++) {
24411      const first = parseInt(kerning[i].first, 10);
24412      const second = parseInt(kerning[i].second, 10);
24413      const amount = parseInt(kerning[i].amount, 10);
24414      font.chars[map[second]].kerning[map[first]] = amount;
24415    }
24416    return font;
24417  }
24418};
24419
24420const bitmapFontXMLParser = {
24421  test(data) {
24422    const xml = data;
24423    return typeof xml !== "string" && "getElementsByTagName" in xml && xml.getElementsByTagName("page").length && xml.getElementsByTagName("info")[0].getAttribute("face") !== null;
24424  },
24425  parse(xml) {
24426    const data = {
24427      chars: {},
24428      pages: [],
24429      lineHeight: 0,
24430      fontSize: 0,
24431      fontFamily: "",
24432      distanceField: null,
24433      baseLineOffset: 0
24434    };
24435    const info = xml.getElementsByTagName("info")[0];
24436    const common = xml.getElementsByTagName("common")[0];
24437    const distanceField = xml.getElementsByTagName("distanceField")[0];
24438    if (distanceField) {
24439      data.distanceField = {
24440        type: distanceField.getAttribute("fieldType"),
24441        range: parseInt(distanceField.getAttribute("distanceRange"), 10)
24442      };
24443    }
24444    const page = xml.getElementsByTagName("page");
24445    const char = xml.getElementsByTagName("char");
24446    const kerning = xml.getElementsByTagName("kerning");
24447    data.fontSize = parseInt(info.getAttribute("size"), 10);
24448    data.fontFamily = info.getAttribute("face");
24449    data.lineHeight = parseInt(common.getAttribute("lineHeight"), 10);
24450    for (let i = 0; i < page.length; i++) {
24451      data.pages.push({
24452        id: parseInt(page[i].getAttribute("id"), 10) || 0,
24453        file: page[i].getAttribute("file")
24454      });
24455    }
24456    const map = {};
24457    data.baseLineOffset = data.lineHeight - parseInt(common.getAttribute("base"), 10);
24458    for (let i = 0; i < char.length; i++) {
24459      const charNode = char[i];
24460      const id = parseInt(charNode.getAttribute("id"), 10);
24461      let letter = charNode.getAttribute("letter") ?? charNode.getAttribute("char") ?? String.fromCharCode(id);
24462      if (letter === "space")
24463        letter = " ";
24464      map[id] = letter;
24465      data.chars[letter] = {
24466        id,
24467        // texture deets..
24468        page: parseInt(charNode.getAttribute("page"), 10) || 0,
24469        x: parseInt(charNode.getAttribute("x"), 10),
24470        y: parseInt(charNode.getAttribute("y"), 10),
24471        width: parseInt(charNode.getAttribute("width"), 10),
24472        height: parseInt(charNode.getAttribute("height"), 10),
24473        // render deets..
24474        xOffset: parseInt(charNode.getAttribute("xoffset"), 10),
24475        yOffset: parseInt(charNode.getAttribute("yoffset"), 10),
24476        // + baseLineOffset,
24477        xAdvance: parseInt(charNode.getAttribute("xadvance"), 10),
24478        kerning: {}
24479      };
24480    }
24481    for (let i = 0; i < kerning.length; i++) {
24482      const first = parseInt(kerning[i].getAttribute("first"), 10);
24483      const second = parseInt(kerning[i].getAttribute("second"), 10);
24484      const amount = parseInt(kerning[i].getAttribute("amount"), 10);
24485      data.chars[map[second]].kerning[map[first]] = amount;
24486    }
24487    return data;
24488  }
24489};
24490
24491const bitmapFontXMLStringParser = {
24492  test(data) {
24493    if (typeof data === "string" && data.match(/<font(\s|>)/)) {
24494      return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data));
24495    }
24496    return false;
24497  },
24498  parse(data) {
24499    return bitmapFontXMLParser.parse(DOMAdapter.get().parseXML(data));
24500  }
24501};
24502
24503const validExtensions = [".xml", ".fnt"];
24504const bitmapFontCachePlugin = {
24505  extension: {
24506    type: ExtensionType.CacheParser,
24507    name: "cacheBitmapFont"
24508  },
24509  test: (asset) => asset instanceof BitmapFont,
24510  getCacheableAssets(keys, asset) {
24511    const out = {};
24512    keys.forEach((key) => {
24513      out[key] = asset;
24514      out[`${key}-bitmap`] = asset;
24515    });
24516    out[`${asset.fontFamily}-bitmap`] = asset;
24517    return out;
24518  }
24519};
24520const loadBitmapFont = {
24521  extension: {
24522    type: ExtensionType.LoadParser,
24523    priority: LoaderParserPriority.Normal
24524  },
24525  /** used for deprecation purposes */
24526  name: "loadBitmapFont",
24527  id: "bitmap-font",
24528  test(url) {
24529    return validExtensions.includes(path.extname(url).toLowerCase());
24530  },
24531  async testParse(data) {
24532    return bitmapFontTextParser.test(data) || bitmapFontXMLStringParser.test(data);
24533  },
24534  async parse(asset, data, loader) {
24535    const bitmapFontData = bitmapFontTextParser.test(asset) ? bitmapFontTextParser.parse(asset) : bitmapFontXMLStringParser.parse(asset);
24536    const { src } = data;
24537    const { pages } = bitmapFontData;
24538    const textureUrls = [];
24539    const textureOptions = bitmapFontData.distanceField ? {
24540      scaleMode: "linear",
24541      alphaMode: "premultiply-alpha-on-upload",
24542      autoGenerateMipmaps: false,
24543      resolution: 1
24544    } : {};
24545    for (let i = 0; i < pages.length; ++i) {
24546      const pageFile = pages[i].file;
24547      let imagePath = path.join(path.dirname(src), pageFile);
24548      imagePath = copySearchParams(imagePath, src);
24549      textureUrls.push({
24550        src: imagePath,
24551        data: textureOptions
24552      });
24553    }
24554    const loadedTextures = await loader.load(textureUrls);
24555    const textures = textureUrls.map((url) => loadedTextures[url.src]);
24556    const bitmapFont = new BitmapFont({
24557      data: bitmapFontData,
24558      textures
24559    }, src);
24560    return bitmapFont;
24561  },
24562  async load(url, _options) {
24563    const response = await DOMAdapter.get().fetch(url);
24564    return await response.text();
24565  },
24566  async unload(bitmapFont, _resolvedAsset, loader) {
24567    await Promise.all(bitmapFont.pages.map((page) => loader.unload(page.texture.source._sourceOrigin)));
24568    bitmapFont.destroy();
24569  }
24570};
24571
24572class BackgroundLoader {
24573  /**
24574   * @param loader
24575   * @param verbose - should the loader log to the console
24576   */
24577  constructor(loader, verbose = false) {
24578    this._loader = loader;
24579    this._assetList = [];
24580    this._isLoading = false;
24581    this._maxConcurrent = 1;
24582    this.verbose = verbose;
24583  }
24584  /**
24585   * Adds assets to the background loading queue. Assets are loaded one at a time to minimize
24586   * performance impact.
24587   * @param assetUrls - Array of resolved assets to load in the background
24588   * @example
24589   * ```ts
24590   * // Add assets to background load queue
24591   * backgroundLoader.add([
24592   *     { src: 'images/level1/bg.png' },
24593   *     { src: 'images/level1/characters.json' }
24594   * ]);
24595   *
24596   * // Assets will load sequentially in the background
24597   * // The loader automatically pauses when high-priority loads occur
24598   * // e.g. Assets.load() is called
24599   * ```
24600   * @remarks
24601   * - Assets are loaded one at a time to minimize performance impact
24602   * - Loading automatically pauses when Assets.load() is called
24603   * - No progress tracking is available for background loading
24604   * - Assets are cached as they complete loading
24605   * @internal
24606   */
24607  add(assetUrls) {
24608    assetUrls.forEach((a) => {
24609      this._assetList.push(a);
24610    });
24611    if (this.verbose) {
24612      console.log("[BackgroundLoader] assets: ", this._assetList);
24613    }
24614    if (this._isActive && !this._isLoading) {
24615      void this._next();
24616    }
24617  }
24618  /**
24619   * Loads the next set of assets. Will try to load as many assets as it can at the same time.
24620   *
24621   * The max assets it will try to load at one time will be 4.
24622   */
24623  async _next() {
24624    if (this._assetList.length && this._isActive) {
24625      this._isLoading = true;
24626      const toLoad = [];
24627      const toLoadAmount = Math.min(this._assetList.length, this._maxConcurrent);
24628      for (let i = 0; i < toLoadAmount; i++) {
24629        toLoad.push(this._assetList.pop());
24630      }
24631      await this._loader.load(toLoad);
24632      this._isLoading = false;
24633      void this._next();
24634    }
24635  }
24636  /**
24637   * Controls the active state of the background loader. When active, the loader will
24638   * continue processing its queue. When inactive, loading is paused.
24639   * @returns Whether the background loader is currently active
24640   * @example
24641   * ```ts
24642   * // Pause background loading
24643   * backgroundLoader.active = false;
24644   *
24645   * // Resume background loading
24646   * backgroundLoader.active = true;
24647   *
24648   * // Check current state
24649   * console.log(backgroundLoader.active); // true/false
24650   *
24651   * // Common use case: Pause during intensive operations
24652   * backgroundLoader.active = false;  // Pause background loading
24653   * ... // Perform high-priority tasks
24654   * backgroundLoader.active = true;   // Resume background loading
24655   * ```
24656   * @remarks
24657   * - Setting to true resumes loading immediately
24658   * - Setting to false pauses after current asset completes
24659   * - Background loading is automatically paused during `Assets.load()`
24660   * - Assets already being loaded will complete even when set to false
24661   */
24662  get active() {
24663    return this._isActive;
24664  }
24665  set active(value) {
24666    if (this._isActive === value)
24667      return;
24668    this._isActive = value;
24669    if (value && !this._isLoading) {
24670      void this._next();
24671    }
24672  }
24673}
24674
24675const cacheTextureArray = {
24676  extension: {
24677    type: ExtensionType.CacheParser,
24678    name: "cacheTextureArray"
24679  },
24680  test: (asset) => Array.isArray(asset) && asset.every((t) => t instanceof Texture),
24681  getCacheableAssets: (keys, asset) => {
24682    const out = {};
24683    keys.forEach((key) => {
24684      asset.forEach((item, i) => {
24685        out[key + (i === 0 ? "" : i + 1)] = item;
24686      });
24687    });
24688    return out;
24689  }
24690};
24691
24692async function testImageFormat(imageData) {
24693  if ("Image" in globalThis) {
24694    return new Promise((resolve) => {
24695      const image = new Image();
24696      image.onload = () => {
24697        resolve(true);
24698      };
24699      image.onerror = () => {
24700        resolve(false);
24701      };
24702      image.src = imageData;
24703    });
24704  }
24705  if ("createImageBitmap" in globalThis && "fetch" in globalThis) {
24706    try {
24707      const blob = await (await fetch(imageData)).blob();
24708      await createImageBitmap(blob);
24709    } catch (_e) {
24710      return false;
24711    }
24712    return true;
24713  }
24714  return false;
24715}
24716
24717const detectAvif = {
24718  extension: {
24719    type: ExtensionType.DetectionParser,
24720    priority: 1
24721  },
24722  test: async () => testImageFormat(
24723    // eslint-disable-next-line max-len
24724    "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="
24725  ),
24726  add: async (formats) => [...formats, "avif"],
24727  remove: async (formats) => formats.filter((f) => f !== "avif")
24728};
24729
24730const imageFormats = ["png", "jpg", "jpeg"];
24731const detectDefaults = {
24732  extension: {
24733    type: ExtensionType.DetectionParser,
24734    priority: -1
24735  },
24736  test: () => Promise.resolve(true),
24737  add: async (formats) => [...formats, ...imageFormats],
24738  remove: async (formats) => formats.filter((f) => !imageFormats.includes(f))
24739};
24740
24741const inWorker = "WorkerGlobalScope" in globalThis && globalThis instanceof globalThis.WorkerGlobalScope;
24742function testVideoFormat(mimeType) {
24743  if (inWorker) {
24744    return false;
24745  }
24746  const video = document.createElement("video");
24747  return video.canPlayType(mimeType) !== "";
24748}
24749
24750const detectMp4 = {
24751  extension: {
24752    type: ExtensionType.DetectionParser,
24753    priority: 0
24754  },
24755  test: async () => testVideoFormat("video/mp4"),
24756  add: async (formats) => [...formats, "mp4", "m4v"],
24757  remove: async (formats) => formats.filter((f) => f !== "mp4" && f !== "m4v")
24758};
24759
24760const detectOgv = {
24761  extension: {
24762    type: ExtensionType.DetectionParser,
24763    priority: 0
24764  },
24765  test: async () => testVideoFormat("video/ogg"),
24766  add: async (formats) => [...formats, "ogv"],
24767  remove: async (formats) => formats.filter((f) => f !== "ogv")
24768};
24769
24770const detectWebm = {
24771  extension: {
24772    type: ExtensionType.DetectionParser,
24773    priority: 0
24774  },
24775  test: async () => testVideoFormat("video/webm"),
24776  add: async (formats) => [...formats, "webm"],
24777  remove: async (formats) => formats.filter((f) => f !== "webm")
24778};
24779
24780const detectWebp = {
24781  extension: {
24782    type: ExtensionType.DetectionParser,
24783    priority: 0
24784  },
24785  test: async () => testImageFormat(
24786    "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA="
24787  ),
24788  add: async (formats) => [...formats, "webp"],
24789  remove: async (formats) => formats.filter((f) => f !== "webp")
24790};
24791
24792const _Loader = class _Loader {
24793  constructor() {
24794    /**
24795     * Options for loading assets with the loader.
24796     * These options will be used as defaults for all load calls made with this loader instance.
24797     * They can be overridden by passing options directly to the load method.
24798     * @example
24799     * ```ts
24800     * // Create a loader with custom default options
24801     * const loader = new Loader();
24802     * loader.loadOptions = {
24803     *   strategy: 'skip', // Default strategy to 'skip'
24804     *   retryCount: 5,   // Default retry count to 5
24805     *   retryDelay: 500, // Default retry delay to 500ms
24806     * };
24807     *
24808     * // This load call will use the loader's default options
24809     * await loader.load('image1.png');
24810     */
24811    this.loadOptions = { ..._Loader.defaultOptions };
24812    this._parsers = [];
24813    this._parsersValidated = false;
24814    /**
24815     * All loader parsers registered
24816     * @type {assets.LoaderParser[]}
24817     */
24818    this.parsers = new Proxy(this._parsers, {
24819      set: (target, key, value) => {
24820        this._parsersValidated = false;
24821        target[key] = value;
24822        return true;
24823      }
24824    });
24825    /** Cache loading promises that ae currently active */
24826    this.promiseCache = {};
24827  }
24828  /** function used for testing */
24829  reset() {
24830    this._parsersValidated = false;
24831    this.promiseCache = {};
24832  }
24833  /**
24834   * Used internally to generate a promise for the asset to be loaded.
24835   * @param url - The URL to be loaded
24836   * @param data - any custom additional information relevant to the asset being loaded
24837   * @returns - a promise that will resolve to an Asset for example a Texture of a JSON object
24838   */
24839  _getLoadPromiseAndParser(url, data) {
24840    const result = {
24841      promise: null,
24842      parser: null
24843    };
24844    result.promise = (async () => {
24845      let asset = null;
24846      let parser = null;
24847      if (data.parser || data.loadParser) {
24848        parser = this._parserHash[data.parser || data.loadParser];
24849        if (data.loadParser) {
24850          warn(
24851            `[Assets] "loadParser" is deprecated, use "parser" instead for ${url}`
24852          );
24853        }
24854        if (!parser) {
24855          warn(
24856            `[Assets] specified load parser "${data.parser || data.loadParser}" not found while loading ${url}`
24857          );
24858        }
24859      }
24860      if (!parser) {
24861        for (let i = 0; i < this.parsers.length; i++) {
24862          const parserX = this.parsers[i];
24863          if (parserX.load && parserX.test?.(url, data, this)) {
24864            parser = parserX;
24865            break;
24866          }
24867        }
24868        if (!parser) {
24869          warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has been added`);
24870          return null;
24871        }
24872      }
24873      asset = await parser.load(url, data, this);
24874      result.parser = parser;
24875      for (let i = 0; i < this.parsers.length; i++) {
24876        const parser2 = this.parsers[i];
24877        if (parser2.parse) {
24878          if (parser2.parse && await parser2.testParse?.(asset, data, this)) {
24879            asset = await parser2.parse(asset, data, this) || asset;
24880            result.parser = parser2;
24881          }
24882        }
24883      }
24884      return asset;
24885    })();
24886    return result;
24887  }
24888  async load(assetsToLoadIn, onProgressOrOptions) {
24889    if (!this._parsersValidated) {
24890      this._validateParsers();
24891    }
24892    const options = typeof onProgressOrOptions === "function" ? { ..._Loader.defaultOptions, ...this.loadOptions, onProgress: onProgressOrOptions } : { ..._Loader.defaultOptions, ...this.loadOptions, ...onProgressOrOptions || {} };
24893    const { onProgress, onError, strategy, retryCount, retryDelay } = options;
24894    let count = 0;
24895    const assets = {};
24896    const singleAsset = isSingleItem(assetsToLoadIn);
24897    const assetsToLoad = convertToList(assetsToLoadIn, (item) => ({
24898      alias: [item],
24899      src: item,
24900      data: {}
24901    }));
24902    const total = assetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0);
24903    const promises = assetsToLoad.map(async (asset) => {
24904      const url = path.toAbsolute(asset.src);
24905      if (assets[asset.src])
24906        return;
24907      await this._loadAssetWithRetry(url, asset, { onProgress, onError, strategy, retryCount, retryDelay }, assets);
24908      count += asset.progressSize || 1;
24909      if (onProgress)
24910        onProgress(count / total);
24911    });
24912    await Promise.all(promises);
24913    return singleAsset ? assets[assetsToLoad[0].src] : assets;
24914  }
24915  /**
24916   * Unloads one or more assets. Any unloaded assets will be destroyed, freeing up memory for your app.
24917   * The parser that created the asset, will be the one that unloads it.
24918   * @example
24919   * // Single asset:
24920   * const asset = await Loader.load('cool.png');
24921   *
24922   * await Loader.unload('cool.png');
24923   *
24924   * console.log(asset.destroyed); // true
24925   * @param assetsToUnloadIn - urls that you want to unload, or a single one!
24926   */
24927  async unload(assetsToUnloadIn) {
24928    const assetsToUnload = convertToList(assetsToUnloadIn, (item) => ({
24929      alias: [item],
24930      src: item
24931    }));
24932    const promises = assetsToUnload.map(async (asset) => {
24933      const url = path.toAbsolute(asset.src);
24934      const loadPromise = this.promiseCache[url];
24935      if (loadPromise) {
24936        const loadedAsset = await loadPromise.promise;
24937        delete this.promiseCache[url];
24938        await loadPromise.parser?.unload?.(loadedAsset, asset, this);
24939      }
24940    });
24941    await Promise.all(promises);
24942  }
24943  /** validates our parsers, right now it only checks for name conflicts but we can add more here as required! */
24944  _validateParsers() {
24945    this._parsersValidated = true;
24946    this._parserHash = this._parsers.filter((parser) => parser.name || parser.id).reduce((hash, parser) => {
24947      if (!parser.name && !parser.id) {
24948        warn(`[Assets] parser should have an id`);
24949      } else if (hash[parser.name] || hash[parser.id]) {
24950        warn(`[Assets] parser id conflict "${parser.id}"`);
24951      }
24952      hash[parser.name] = parser;
24953      if (parser.id)
24954        hash[parser.id] = parser;
24955      return hash;
24956    }, {});
24957  }
24958  async _loadAssetWithRetry(url, asset, options, assets) {
24959    let attempt = 0;
24960    const { onError, strategy, retryCount, retryDelay } = options;
24961    const wait = (ms) => new Promise((r) => setTimeout(r, ms));
24962    while (true) {
24963      try {
24964        if (!this.promiseCache[url]) {
24965          this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset);
24966        }
24967        assets[asset.src] = await this.promiseCache[url].promise;
24968        return;
24969      } catch (e) {
24970        delete this.promiseCache[url];
24971        delete assets[asset.src];
24972        attempt++;
24973        const isLast = strategy !== "retry" || attempt > retryCount;
24974        if (strategy === "retry" && !isLast) {
24975          if (onError)
24976            onError(e, asset);
24977          await wait(retryDelay);
24978          continue;
24979        }
24980        if (strategy === "skip") {
24981          if (onError)
24982            onError(e, asset);
24983          return;
24984        }
24985        if (onError)
24986          onError(e, asset);
24987        const error = new Error(`[Loader.load] Failed to load ${url}.
24988${e}`);
24989        if (e instanceof Error && e.stack) {
24990          error.stack = e.stack;
24991        }
24992        throw error;
24993      }
24994    }
24995  }
24996};
24997/**
24998 * Default options for loading assets
24999 * @example
25000 * ```ts
25001 * // Change default load options globally
25002 * Loader.defaultOptions = {
25003 *   strategy: 'skip', // Change default strategy to 'skip'
25004 *   retryCount: 5,   // Change default retry count to 5
25005 *   retryDelay: 500, // Change default retry delay to 500ms
25006 * };
25007 * ```
25008 */
25009_Loader.defaultOptions = {
25010  onProgress: void 0,
25011  onError: void 0,
25012  strategy: "throw",
25013  retryCount: 3,
25014  retryDelay: 250
25015};
25016let Loader = _Loader;
25017
25018function checkDataUrl(url, mimes) {
25019  if (Array.isArray(mimes)) {
25020    for (const mime of mimes) {
25021      if (url.startsWith(`data:${mime}`))
25022        return true;
25023    }
25024    return false;
25025  }
25026  return url.startsWith(`data:${mimes}`);
25027}
25028
25029function checkExtension(url, extension) {
25030  const tempURL = url.split("?")[0];
25031  const ext = path.extname(tempURL).toLowerCase();
25032  if (Array.isArray(extension)) {
25033    return extension.includes(ext);
25034  }
25035  return ext === extension;
25036}
25037
25038const validJSONExtension = ".json";
25039const validJSONMIME = "application/json";
25040const loadJson = {
25041  extension: {
25042    type: ExtensionType.LoadParser,
25043    priority: LoaderParserPriority.Low
25044  },
25045  /** used for deprecation purposes */
25046  name: "loadJson",
25047  id: "json",
25048  test(url) {
25049    return checkDataUrl(url, validJSONMIME) || checkExtension(url, validJSONExtension);
25050  },
25051  async load(url) {
25052    const response = await DOMAdapter.get().fetch(url);
25053    const json = await response.json();
25054    return json;
25055  }
25056};
25057
25058const validTXTExtension = ".txt";
25059const validTXTMIME = "text/plain";
25060const loadTxt = {
25061  /** used for deprecation purposes */
25062  name: "loadTxt",
25063  id: "text",
25064  extension: {
25065    type: ExtensionType.LoadParser,
25066    priority: LoaderParserPriority.Low,
25067    name: "loadTxt"
25068  },
25069  test(url) {
25070    return checkDataUrl(url, validTXTMIME) || checkExtension(url, validTXTExtension);
25071  },
25072  async load(url) {
25073    const response = await DOMAdapter.get().fetch(url);
25074    const txt = await response.text();
25075    return txt;
25076  }
25077};
25078
25079const validWeights = [
25080  "normal",
25081  "bold",
25082  "100",
25083  "200",
25084  "300",
25085  "400",
25086  "500",
25087  "600",
25088  "700",
25089  "800",
25090  "900"
25091];
25092const validFontExtensions = [".ttf", ".otf", ".woff", ".woff2"];
25093const validFontMIMEs = [
25094  "font/ttf",
25095  "font/otf",
25096  "font/woff",
25097  "font/woff2"
25098];
25099const CSS_IDENT_TOKEN_REGEX = /^(--|-?[A-Z_])[0-9A-Z_-]*$/i;
25100function getFontFamilyName(url) {
25101  const ext = path.extname(url);
25102  const name = path.basename(url, ext);
25103  const nameWithSpaces = name.replace(/(-|_)/g, " ");
25104  const nameTokens = nameWithSpaces.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1));
25105  let valid = nameTokens.length > 0;
25106  for (const token of nameTokens) {
25107    if (!token.match(CSS_IDENT_TOKEN_REGEX)) {
25108      valid = false;
25109      break;
25110    }
25111  }
25112  let fontFamilyName = nameTokens.join(" ");
25113  if (!valid) {
25114    fontFamilyName = `"${fontFamilyName.replace(/[\\"]/g, "\\$&")}"`;
25115  }
25116  return fontFamilyName;
25117}
25118const validURICharactersRegex = /^[0-9A-Za-z%:/?#\[\]@!\$&'()\*\+,;=\-._~]*$/;
25119function encodeURIWhenNeeded(uri) {
25120  if (validURICharactersRegex.test(uri)) {
25121    return uri;
25122  }
25123  return encodeURI(uri);
25124}
25125const loadWebFont = {
25126  extension: {
25127    type: ExtensionType.LoadParser,
25128    priority: LoaderParserPriority.Low
25129  },
25130  /** used for deprecation purposes */
25131  name: "loadWebFont",
25132  id: "web-font",
25133  test(url) {
25134    return checkDataUrl(url, validFontMIMEs) || checkExtension(url, validFontExtensions);
25135  },
25136  async load(url, options) {
25137    const fonts = DOMAdapter.get().getFontFaceSet();
25138    if (fonts) {
25139      const fontFaces = [];
25140      const name = options.data?.family ?? getFontFamilyName(url);
25141      const weights = options.data?.weights?.filter((weight) => validWeights.includes(weight)) ?? ["normal"];
25142      const data = options.data ?? {};
25143      for (let i = 0; i < weights.length; i++) {
25144        const weight = weights[i];
25145        const font = new FontFace(name, `url(${encodeURIWhenNeeded(url)})`, {
25146          ...data,
25147          weight
25148        });
25149        await font.load();
25150        fonts.add(font);
25151        fontFaces.push(font);
25152      }
25153      if (Cache.has(`${name}-and-url`)) {
25154        const cached = Cache.get(`${name}-and-url`);
25155        cached.entries.push({ url, faces: fontFaces });
25156      } else {
25157        Cache.set(`${name}-and-url`, {
25158          entries: [{ url, faces: fontFaces }]
25159        });
25160      }
25161      return fontFaces.length === 1 ? fontFaces[0] : fontFaces;
25162    }
25163    warn("[loadWebFont] FontFace API is not supported. Skipping loading font");
25164    return null;
25165  },
25166  unload(font) {
25167    const fonts = Array.isArray(font) ? font : [font];
25168    const fontFamily = fonts[0].family;
25169    const cached = Cache.get(`${fontFamily}-and-url`);
25170    const entry = cached.entries.find((f) => f.faces.some((t) => fonts.indexOf(t) !== -1));
25171    entry.faces = entry.faces.filter((f) => fonts.indexOf(f) === -1);
25172    if (entry.faces.length === 0) {
25173      cached.entries = cached.entries.filter((f) => f !== entry);
25174    }
25175    fonts.forEach((t) => {
25176      DOMAdapter.get().getFontFaceSet().delete(t);
25177    });
25178    if (cached.entries.length === 0) {
25179      Cache.remove(`${fontFamily}-and-url`);
25180    }
25181  }
25182};
25183
25184function getResolutionOfUrl(url, defaultValue = 1) {
25185  const resolution = Resolver.RETINA_PREFIX?.exec(url);
25186  if (resolution) {
25187    return parseFloat(resolution[1]);
25188  }
25189  return defaultValue;
25190}
25191
25192function createTexture(source, loader, url) {
25193  source.label = url;
25194  source._sourceOrigin = url;
25195  const texture = new Texture({
25196    source,
25197    label: url
25198  });
25199  const unload = () => {
25200    delete loader.promiseCache[url];
25201    if (Cache.has(url)) {
25202      Cache.remove(url);
25203    }
25204  };
25205  texture.source.once("destroy", () => {
25206    if (loader.promiseCache[url]) {
25207      warn("[Assets] A TextureSource managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the TextureSource.");
25208      unload();
25209    }
25210  });
25211  texture.once("destroy", () => {
25212    if (!source.destroyed) {
25213      warn("[Assets] A Texture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the Texture.");
25214      unload();
25215    }
25216  });
25217  return texture;
25218}
25219
25220const validSVGExtension = ".svg";
25221const validSVGMIME = "image/svg+xml";
25222const loadSvg = {
25223  extension: {
25224    type: ExtensionType.LoadParser,
25225    priority: LoaderParserPriority.Low,
25226    name: "loadSVG"
25227  },
25228  /** used for deprecation purposes */
25229  name: "loadSVG",
25230  id: "svg",
25231  config: {
25232    crossOrigin: "anonymous",
25233    parseAsGraphicsContext: false
25234  },
25235  test(url) {
25236    return checkDataUrl(url, validSVGMIME) || checkExtension(url, validSVGExtension);
25237  },
25238  async load(url, asset, loader) {
25239    if (asset.data?.parseAsGraphicsContext ?? this.config.parseAsGraphicsContext) {
25240      return loadAsGraphics(url);
25241    }
25242    return loadAsTexture(url, asset, loader, this.config.crossOrigin);
25243  },
25244  unload(asset) {
25245    asset.destroy(true);
25246  }
25247};
25248async function loadAsTexture(url, asset, loader, crossOrigin) {
25249  const response = await DOMAdapter.get().fetch(url);
25250  const image = DOMAdapter.get().createImage();
25251  image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(await response.text())}`;
25252  image.crossOrigin = crossOrigin;
25253  await image.decode();
25254  const width = asset.data?.width ?? image.width;
25255  const height = asset.data?.height ?? image.height;
25256  const resolution = asset.data?.resolution || getResolutionOfUrl(url);
25257  const canvasWidth = Math.ceil(width * resolution);
25258  const canvasHeight = Math.ceil(height * resolution);
25259  const canvas = DOMAdapter.get().createCanvas(canvasWidth, canvasHeight);
25260  const context = canvas.getContext("2d");
25261  context.imageSmoothingEnabled = true;
25262  context.imageSmoothingQuality = "high";
25263  context.drawImage(image, 0, 0, width * resolution, height * resolution);
25264  const { parseAsGraphicsContext: _p, ...rest } = asset.data ?? {};
25265  const base = new ImageSource({
25266    resource: canvas,
25267    alphaMode: "premultiply-alpha-on-upload",
25268    resolution,
25269    ...rest
25270  });
25271  return createTexture(base, loader, url);
25272}
25273async function loadAsGraphics(url) {
25274  const response = await DOMAdapter.get().fetch(url);
25275  const svgSource = await response.text();
25276  const context = new GraphicsContext();
25277  context.svg(svgSource);
25278  return context;
25279}
25280
25281const WORKER_CODE$1 = "(function () {\n    'use strict';\n\n    const WHITE_PNG = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=\";\n    async function checkImageBitmap() {\n      try {\n        if (typeof createImageBitmap !== \"function\")\n          return false;\n        const response = await fetch(WHITE_PNG);\n        const imageBlob = await response.blob();\n        const imageBitmap = await createImageBitmap(imageBlob);\n        return imageBitmap.width === 1 && imageBitmap.height === 1;\n      } catch (_e) {\n        return false;\n      }\n    }\n    void checkImageBitmap().then((result) => {\n      self.postMessage(result);\n    });\n\n})();\n";
25282let WORKER_URL$1 = null;
25283let WorkerInstance$1 = class WorkerInstance
25284{
25285    constructor()
25286    {
25287        if (!WORKER_URL$1)
25288        {
25289            WORKER_URL$1 = URL.createObjectURL(new Blob([WORKER_CODE$1], { type: 'application/javascript' }));
25290        }
25291        this.worker = new Worker(WORKER_URL$1);
25292    }
25293};
25294WorkerInstance$1.revokeObjectURL = function revokeObjectURL()
25295{
25296    if (WORKER_URL$1)
25297    {
25298        URL.revokeObjectURL(WORKER_URL$1);
25299        WORKER_URL$1 = null;
25300    }
25301};
25302
25303const WORKER_CODE = "(function () {\n    'use strict';\n\n    async function loadImageBitmap(url, alphaMode) {\n      const response = await fetch(url);\n      if (!response.ok) {\n        throw new Error(`[WorkerManager.loadImageBitmap] Failed to fetch ${url}: ${response.status} ${response.statusText}`);\n      }\n      const imageBlob = await response.blob();\n      return alphaMode === \"premultiplied-alpha\" ? createImageBitmap(imageBlob, { premultiplyAlpha: \"none\" }) : createImageBitmap(imageBlob);\n    }\n    self.onmessage = async (event) =>
25303 {\n      try {\n        const imageBitmap = await loadImageBitmap(event.data.data[0], event.data.data[1]);\n        self.postMessage({\n          data: imageBitmap,\n          uuid: event.data.uuid,\n          id: event.data.id\n        }, [imageBitmap]);\n      } catch (e) {\n        self.postMessage({\n          error: e,\n          uuid: event.data.uuid,\n          id: event.data.id\n        });\n      }\n    };\n\n})();\n";
25304let WORKER_URL = null;
25305class WorkerInstance
25306{
25307    constructor()
25308    {
25309        if (!WORKER_URL)
25310        {
25311            WORKER_URL = URL.createObjectURL(new Blob([WORKER_CODE], { type: 'application/javascript' }));
25312        }
25313        this.worker = new Worker(WORKER_URL);
25314    }
25315}
25316WorkerInstance.revokeObjectURL = function revokeObjectURL()
25317{
25318    if (WORKER_URL)
25319    {
25320        URL.revokeObjectURL(WORKER_URL);
25321        WORKER_URL = null;
25322    }
25323};
25324
25325let UUID = 0;
25326let MAX_WORKERS;
25327class WorkerManagerClass {
25328  constructor() {
25329    /** Whether the worker manager has been initialized */
25330    this._initialized = false;
25331    /** Current number of created workers (used to enforce MAX_WORKERS limit) */
25332    this._createdWorkers = 0;
25333    this._workerPool = [];
25334    this._queue = [];
25335    this._resolveHash = {};
25336  }
25337  /**
25338   * Checks if ImageBitmap is supported in the current environment.
25339   *
25340   * This method uses a dedicated worker to test ImageBitmap support
25341   * and caches the result for subsequent calls.
25342   * @returns Promise that resolves to true if ImageBitmap is supported, false otherwise
25343   */
25344  isImageBitmapSupported() {
25345    if (this._isImageBitmapSupported !== void 0)
25346      return this._isImageBitmapSupported;
25347    this._isImageBitmapSupported = new Promise((resolve) => {
25348      const { worker } = new WorkerInstance$1();
25349      worker.addEventListener("message", (event) => {
25350        worker.terminate();
25351        WorkerInstance$1.revokeObjectURL();
25352        resolve(event.data);
25353      });
25354    });
25355    return this._isImageBitmapSupported;
25356  }
25357  /**
25358   * Loads an image as an ImageBitmap using a web worker.
25359   * @param src - The source URL or path of the image to load
25360   * @param asset - Optional resolved asset containing additional texture source options
25361   * @returns Promise that resolves to the loaded ImageBitmap
25362   * @example
25363   * ```typescript
25364   * const bitmap = await WorkerManager.loadImageBitmap('image.png');
25365   * const bitmapWithOptions = await WorkerManager.loadImageBitmap('image.png', asset);
25366   * ```
25367   */
25368  loadImageBitmap(src, asset) {
25369    return this._run("loadImageBitmap", [src, asset?.data?.alphaMode]);
25370  }
25371  /**
25372   * Initializes the worker pool if not already initialized.
25373   * Currently a no-op but reserved for future initialization logic.
25374   */
25375  async _initWorkers() {
25376    if (this._initialized)
25377      return;
25378    this._initialized = true;
25379  }
25380  /**
25381   * Gets an available worker from the pool or creates a new one if needed.
25382   *
25383   * Workers are created up to the MAX_WORKERS limit (based on navigator.hardwareConcurrency).
25384   * Each worker is configured with a message handler for processing results.
25385   * @returns Available worker or undefined if pool is at capacity and no workers are free
25386   */
25387  _getWorker() {
25388    if (MAX_WORKERS === void 0) {
25389      MAX_WORKERS = navigator.hardwareConcurrency || 4;
25390    }
25391    let worker = this._workerPool.pop();
25392    if (!worker && this._createdWorkers < MAX_WORKERS) {
25393      this._createdWorkers++;
25394      worker = new WorkerInstance().worker;
25395      worker.addEventListener("message", (event) => {
25396        this._complete(event.data);
25397        this._returnWorker(event.target);
25398        this._next();
25399      });
25400    }
25401    return worker;
25402  }
25403  /**
25404   * Returns a worker to the pool after completing a task.
25405   * @param worker - The worker to return to the pool
25406   */
25407  _returnWorker(worker) {
25408    this._workerPool.push(worker);
25409  }
25410  /**
25411   * Handles completion of a worker task by resolving or rejecting the corresponding promise.
25412   * @param data - Result data from the worker containing uuid, data, and optional error
25413   */
25414  _complete(data) {
25415    if (!this._resolveHash[data.uuid]) {
25416      return;
25417    }
25418    if (data.error !== void 0) {
25419      this._resolveHash[data.uuid].reject(data.error);
25420    } else {
25421      this._resolveHash[data.uuid].resolve(data.data);
25422    }
25423    delete this._resolveHash[data.uuid];
25424  }
25425  /**
25426   * Executes a task using the worker pool system.
25427   *
25428   * Queues the task and processes it when a worker becomes available.
25429   * @param id - Identifier for the type of task to run
25430   * @param args - Arguments to pass to the worker
25431   * @returns Promise that resolves with the worker's result
25432   */
25433  async _run(id, args) {
25434    await this._initWorkers();
25435    const promise = new Promise((resolve, reject) => {
25436      this._queue.push({ id, arguments: args, resolve, reject });
25437    });
25438    this._next();
25439    return promise;
25440  }
25441  /**
25442   * Processes the next item in the queue if workers are available.
25443   *
25444   * This method is called after worker initialization and when workers
25445   * complete tasks to continue processing the queue.
25446   */
25447  _next() {
25448    if (!this._queue.length)
25449      return;
25450    const worker = this._getWorker();
25451    if (!worker) {
25452      return;
25453    }
25454    const toDo = this._queue.pop();
25455    const id = toDo.id;
25456    this._resolveHash[UUID] = { resolve: toDo.resolve, reject: toDo.reject };
25457    worker.postMessage({
25458      data: toDo.arguments,
25459      uuid: UUID++,
25460      id
25461    });
25462  }
25463  /**
25464   * Resets the worker manager, terminating all workers and clearing the queue.
25465   *
25466   * This method:
25467   * - Terminates all active workers
25468   * - Rejects all pending promises with an error
25469   * - Clears all internal state
25470   * - Resets initialization flags
25471   *
25472   * This should be called when the worker manager is no longer needed
25473   * to prevent memory leaks and ensure proper cleanup.
25474   * @example
25475   * ```typescript
25476   * // Clean up when shutting down
25477   * WorkerManager.reset();
25478   * ```
25479   */
25480  reset() {
25481    this._workerPool.forEach((worker) => worker.terminate());
25482    this._workerPool.length = 0;
25483    Object.values(this._resolveHash).forEach(({ reject }) => {
25484      reject?.(new Error("WorkerManager has been reset before completion"));
25485    });
25486    this._resolveHash = {};
25487    this._queue.length = 0;
25488    this._initialized = false;
25489    this._createdWorkers = 0;
25490  }
25491}
25492const WorkerManager = new WorkerManagerClass();
25493
25494const validImageExtensions = [".jpeg", ".jpg", ".png", ".webp", ".avif"];
25495const validImageMIMEs = [
25496  "image/jpeg",
25497  "image/png",
25498  "image/webp",
25499  "image/avif"
25500];
25501async function loadImageBitmap(url, asset) {
25502  const response = await DOMAdapter.get().fetch(url);
25503  if (!response.ok) {
25504    throw new Error(`[loadImageBitmap] Failed to fetch ${url}: ${response.status} ${response.statusText}`);
25505  }
25506  const imageBlob = await response.blob();
25507  return asset?.data?.alphaMode === "premultiplied-alpha" ? createImageBitmap(imageBlob, { premultiplyAlpha: "none" }) : createImageBitmap(imageBlob);
25508}
25509const loadTextures = {
25510  /** used for deprecation purposes */
25511  name: "loadTextures",
25512  id: "texture",
25513  extension: {
25514    type: ExtensionType.LoadParser,
25515    priority: LoaderParserPriority.High,
25516    name: "loadTextures"
25517  },
25518  config: {
25519    preferWorkers: true,
25520    preferCreateImageBitmap: true,
25521    crossOrigin: "anonymous"
25522  },
25523  test(url) {
25524    return checkDataUrl(url, validImageMIMEs) || checkExtension(url, validImageExtensions);
25525  },
25526  async load(url, asset, loader) {
25527    let src = null;
25528    if (globalThis.createImageBitmap && this.config.preferCreateImageBitmap) {
25529      if (this.config.preferWorkers && await WorkerManager.isImageBitmapSupported()) {
25530        src = await WorkerManager.loadImageBitmap(url, asset);
25531      } else {
25532        src = await loadImageBitmap(url, asset);
25533      }
25534    } else {
25535      src = await new Promise((resolve, reject) => {
25536        src = DOMAdapter.get().createImage();
25537        src.crossOrigin = this.config.crossOrigin;
25538        src.src = url;
25539        if (src.complete) {
25540          resolve(src);
25541        } else {
25542          src.onload = () => {
25543            resolve(src);
25544          };
25545          src.onerror = reject;
25546        }
25547      });
25548    }
25549    const base = new ImageSource({
25550      resource: src,
25551      alphaMode: "premultiply-alpha-on-upload",
25552      resolution: asset.data?.resolution || getResolutionOfUrl(url),
25553      ...asset.data
25554    });
25555    return createTexture(base, loader, url);
25556  },
25557  unload(texture) {
25558    texture.destroy(true);
25559  }
25560};
25561
25562const potentialVideoExtensions = [".mp4", ".m4v", ".webm", ".ogg", ".ogv", ".h264", ".avi", ".mov"];
25563let validVideoExtensions;
25564let validVideoMIMEs;
25565function crossOrigin(element, url, crossorigin) {
25566  if (crossorigin === void 0 && !url.startsWith("data:")) {
25567    element.crossOrigin = determineCrossOrigin(url);
25568  } else if (crossorigin !== false) {
25569    element.crossOrigin = typeof crossorigin === "string" ? crossorigin : "anonymous";
25570  }
25571}
25572function preloadVideo(element) {
25573  return new Promise((resolve, reject) => {
25574    element.addEventListener("canplaythrough", loaded);
25575    element.addEventListener("error", error);
25576    element.load();
25577    function loaded() {
25578      cleanup();
25579      resolve();
25580    }
25581    function error(err) {
25582      cleanup();
25583      reject(err);
25584    }
25585    function cleanup() {
25586      element.removeEventListener("canplaythrough", loaded);
25587      element.removeEventListener("error", error);
25588    }
25589  });
25590}
25591function determineCrossOrigin(url, loc = globalThis.location) {
25592  if (url.startsWith("data:")) {
25593    return "";
25594  }
25595  loc || (loc = globalThis.location);
25596  const parsedUrl = new URL(url, document.baseURI);
25597  if (parsedUrl.hostname !== loc.hostname || parsedUrl.port !== loc.port || parsedUrl.protocol !== loc.protocol) {
25598    return "anonymous";
25599  }
25600  return "";
25601}
25602function getBrowserSupportedVideoExtensions() {
25603  const supportedExtensions = [];
25604  const supportedMimes = [];
25605  for (const ext of potentialVideoExtensions) {
25606    const mimeType = VideoSource.MIME_TYPES[ext.substring(1)] || `video/${ext.substring(1)}`;
25607    if (testVideoFormat(mimeType)) {
25608      supportedExtensions.push(ext);
25609      if (!supportedMimes.includes(mimeType)) {
25610        supportedMimes.push(mimeType);
25611      }
25612    }
25613  }
25614  return {
25615    validVideoExtensions: supportedExtensions,
25616    validVideoMime: supportedMimes
25617  };
25618}
25619const loadVideoTextures = {
25620  /** used for deprecation purposes */
25621  name: "loadVideo",
25622  id: "video",
25623  extension: {
25624    type: ExtensionType.LoadParser,
25625    name: "loadVideo"
25626  },
25627  test(url) {
25628    if (!validVideoExtensions || !validVideoMIMEs) {
25629      const { validVideoExtensions: ve, validVideoMime: vm } = getBrowserSupportedVideoExtensions();
25630      validVideoExtensions = ve;
25631      validVideoMIMEs = vm;
25632    }
25633    const isValidDataUrl = checkDataUrl(url, validVideoMIMEs);
25634    const isValidExtension = checkExtension(url, validVideoExtensions);
25635    return isValidDataUrl || isValidExtension;
25636  },
25637  async load(url, asset, loader) {
25638    const options = {
25639      ...VideoSource.defaultOptions,
25640      resolution: asset.data?.resolution || getResolutionOfUrl(url),
25641      alphaMode: asset.data?.alphaMode || await detectVideoAlphaMode(),
25642      ...asset.data
25643    };
25644    const videoElement = document.createElement("video");
25645    const attributeMap = {
25646      preload: options.autoLoad !== false ? "auto" : void 0,
25647      "webkit-playsinline": options.playsinline !== false ? "" : void 0,
25648      playsinline: options.playsinline !== false ? "" : void 0,
25649      muted: options.muted === true ? "" : void 0,
25650      loop: options.loop === true ? "" : void 0,
25651      autoplay: options.autoPlay !== false ? "" : void 0
25652    };
25653    Object.keys(attributeMap).forEach((key) => {
25654      const value = attributeMap[key];
25655      if (value !== void 0)
25656        videoElement.setAttribute(key, value);
25657    });
25658    if (options.muted === true) {
25659      videoElement.muted = true;
25660    }
25661    crossOrigin(videoElement, url, options.crossorigin);
25662    const sourceElement = document.createElement("source");
25663    let mime;
25664    if (options.mime) {
25665      mime = options.mime;
25666    } else if (url.startsWith("data:")) {
25667      mime = url.slice(5, url.indexOf(";"));
25668    } else if (!url.startsWith("blob:")) {
25669      const ext = url.split("?")[0].slice(url.lastIndexOf(".") + 1).toLowerCase();
25670      mime = VideoSource.MIME_TYPES[ext] || `video/${ext}`;
25671    }
25672    sourceElement.src = url;
25673    if (mime) {
25674      sourceElement.type = mime;
25675    }
25676    return new Promise((resolve) => {
25677      const onCanPlay = async () => {
25678        const base = new VideoSource({ ...options, resource: videoElement });
25679        videoElement.removeEventListener("canplay", onCanPlay);
25680        if (asset.data.preload) {
25681          await preloadVideo(videoElement);
25682        }
25683        resolve(createTexture(base, loader, url));
25684      };
25685      if (options.preload && !options.autoPlay) {
25686        videoElement.load();
25687      }
25688      videoElement.addEventListener("canplay", onCanPlay);
25689      videoElement.appendChild(sourceElement);
25690    });
25691  },
25692  unload(texture) {
25693    texture.destroy(true);
25694  }
25695};
25696
25697const resolveTextureUrl = {
25698  extension: {
25699    type: ExtensionType.ResolveParser,
25700    name: "resolveTexture"
25701  },
25702  test: loadTextures.test,
25703  parse: (value) => ({
25704    resolution: parseFloat(Resolver.RETINA_PREFIX.exec(value)?.[1] ?? "1"),
25705    format: value.split(".").pop(),
25706    src: value
25707  })
25708};
25709
25710const resolveJsonUrl = {
25711  extension: {
25712    type: ExtensionType.ResolveParser,
25713    priority: -2,
25714    name: "resolveJson"
25715  },
25716  test: (value) => Resolver.RETINA_PREFIX.test(value) && value.endsWith(".json"),
25717  parse: resolveTextureUrl.parse
25718};
25719
25720class AssetsClass {
25721  constructor() {
25722    this._detections = [];
25723    this._initialized = false;
25724    this.resolver = new Resolver();
25725    this.loader = new Loader();
25726    this.cache = Cache;
25727    this._backgroundLoader = new BackgroundLoader(this.loader);
25728    this._backgroundLoader.active = true;
25729    this.reset();
25730  }
25731  /**
25732   * Initializes the Assets class with configuration options. While not required,
25733   * calling this before loading assets is recommended to set up default behaviors.
25734   * @param options - Configuration options for the Assets system
25735   * @example
25736   * ```ts
25737   * // Basic initialization (optional as Assets.load will call this automatically)
25738   * await Assets.init();
25739   *
25740   * // With CDN configuration
25741   * await Assets.init({
25742   *     basePath: 'https://my-cdn.com/assets/',
25743   *     defaultSearchParams: { version: '1.0.0' }
25744   * });
25745   *
25746   * // With manifest and preferences
25747   * await Assets.init({
25748   *     manifest: {
25749   *         bundles: [{
25750   *             name: 'game-screen',
25751   *             assets: [
25752   *                 {
25753   *                     alias: 'hero',
25754   *                     src: 'hero.{png,webp}',
25755   *                     data: { scaleMode: SCALE_MODES.NEAREST }
25756   *                 },
25757   *                 {
25758   *                     alias: 'map',
25759   *                     src: 'map.json'
25760   *                 }
25761   *             ]
25762   *         }]
25763   *     },
25764   *     // Optimize for device capabilities
25765   *     texturePreference: {
25766   *         resolution: window.devicePixelRatio,
25767   *         format: ['webp', 'png']
25768   *     },
25769   *     // Set global preferences
25770   *     preferences: {
25771   *         crossOrigin: 'anonymous',
25772   *     }
25773   * });
25774   *
25775   * // Load assets after initialization
25776   * const heroTexture = await Assets.load('hero');
25777   * ```
25778   * @remarks
25779   * - Can be called only once; subsequent calls will be ignored with a warning
25780   * - Format detection runs automatically unless `skipDetections` is true
25781   * - The manifest can be a URL to a JSON file or an inline object
25782   * @see {@link AssetInitOptions} For all available initialization options
25783   * @see {@link AssetsManifest} For manifest format details
25784   */
25785  async init(options = {}) {
25786    if (this._initialized) {
25787      warn("[Assets]AssetManager already initialized, did you load before calling this Assets.init()?");
25788      return;
25789    }
25790    this._initialized = true;
25791    if (options.defaultSearchParams) {
25792      this.resolver.setDefaultSearchParams(options.defaultSearchParams);
25793    }
25794    if (options.basePath) {
25795      this.resolver.basePath = options.basePath;
25796    }
25797    if (options.bundleIdentifier) {
25798      this.resolver.setBundleIdentifier(options.bundleIdentifier);
25799    }
25800    if (options.manifest) {
25801      let manifest = options.manifest;
25802      if (typeof manifest === "string") {
25803        manifest = await this.load(manifest);
25804      }
25805      this.resolver.addManifest(manifest);
25806    }
25807    const resolutionPref = options.texturePreference?.resolution ?? 1;
25808    const resolution = typeof resolutionPref === "number" ? [resolutionPref] : resolutionPref;
25809    const formats = await this._detectFormats({
25810      preferredFormats: options.texturePreference?.format,
25811      skipDetections: options.skipDetections,
25812      detections: this._detections
25813    });
25814    this.resolver.prefer({
25815      params: {
25816        format: formats,
25817        resolution
25818      }
25819    });
25820    if (options.preferences) {
25821      this.setPreferences(options.preferences);
25822    }
25823    if (options.loadOptions) {
25824      this.loader.loadOptions = {
25825        ...this.loader.loadOptions,
25826        ...options.loadOptions
25827      };
25828    }
25829  }
25830  /**
25831   * Registers assets with the Assets resolver. This method maps keys (aliases) to asset sources,
25832   * allowing you to load assets using friendly names instead of direct URLs.
25833   * @param assets - The unresolved assets to add to the resolver
25834   * @example
25835   * ```ts
25836   * // Basic usage - single asset
25837   * Assets.add({
25838   *     alias: 'myTexture',
25839   *     src: 'assets/texture.png'
25840   * });
25841   * const texture = await Assets.load('myTexture');
25842   *
25843   * // Multiple aliases for the same asset
25844   * Assets.add({
25845   *     alias: ['hero', 'player'],
25846   *     src: 'hero.png'
25847   * });
25848   * const hero1 = await Assets.load('hero');
25849   * const hero2 = await Assets.load('player'); // Same texture
25850   *
25851   * // Multiple format support
25852   * Assets.add({
25853   *     alias: 'character',
25854   *     src: 'character.{webp,png}' // Will choose best format
25855   * });
25856   * Assets.add({
25857   *     alias: 'character',
25858   *     src: ['character.webp', 'character.png'], // Explicitly specify formats
25859   * });
25860   *
25861   * // With texture options
25862   * Assets.add({
25863   *     alias: 'sprite',
25864   *     src: 'sprite.png',
25865   *     data: { scaleMode: 'nearest' }
25866   * });
25867   *
25868   * // Multiple assets at once
25869   * Assets.add([
25870   *     { alias: 'bg', src: 'background.png' },
25871   *     { alias: 'music', src: 'music.mp3' },
25872   *     { alias: 'spritesheet', src: 'sheet.json', data: { ignoreMultiPack: false } }
25873   * ]);
25874   * ```
25875   * @remarks
25876   * - Assets are resolved when loaded, not when added
25877   * - Multiple formats use the best available format for the browser
25878   * - Adding with same alias overwrites previous definition
25879   * - The `data` property is passed to the asset loader
25880   * @see {@link Resolver} For details on asset resolution
25881   * @see {@link LoaderParser} For asset-specific data options
25882   * @advanced
25883   */
25884  add(assets) {
25885    this.resolver.add(assets);
25886  }
25887  async load(urls, onProgress) {
25888    if (!this._initialized) {
25889      await this.init();
25890    }
25891    const singleAsset = isSingleItem(urls);
25892    const urlArray = convertToList(urls).map((url) => {
25893      if (typeof url !== "string") {
25894        const aliases = this.resolver.getAlias(url);
25895        if (aliases.some((alias) => !this.resolver.hasKey(alias))) {
25896          this.add(url);
25897        }
25898        return Array.isArray(aliases) ? aliases[0] : aliases;
25899      }
25900      if (!this.resolver.hasKey(url))
25901        this.add({ alias: url, src: url });
25902      return url;
25903    });
25904    const resolveResults = this.resolver.resolve(urlArray);
25905    const out = await this._mapLoadToResolve(resolveResults, onProgress);
25906    return singleAsset ? out[urlArray[0]] : out;
25907  }
25908  /**
25909   * Registers a bundle of assets that can be loaded as a group. Bundles are useful for organizing
25910   * assets into logical groups, such as game levels or UI screens.
25911   * @param bundleId - Unique identifier for the bundle
25912   * @param assets - Assets to include in the bundle
25913   * @example
25914   * ```ts
25915   * // Add a bundle using array format
25916   * Assets.addBundle('animals', [
25917   *     { alias: 'bunny', src: 'bunny.png' },
25918   *     { alias: 'chicken', src: 'chicken.png' },
25919   *     { alias: 'thumper', src: 'thumper.png' },
25920   * ]);
25921   *
25922   * // Add a bundle using object format
25923   * Assets.addBundle('animals', {
25924   *     bunny: 'bunny.png',
25925   *     chicken: 'chicken.png',
25926   *     thumper: 'thumper.png',
25927   * });
25928   *
25929   * // Add a bundle with advanced options
25930   * Assets.addBundle('ui', [
25931   *     {
25932   *         alias: 'button',
25933   *         src: 'button.{webp,png}',
25934   *         data: { scaleMode: 'nearest' }
25935   *     },
25936   *     {
25937   *         alias: ['logo', 'brand'],  // Multiple aliases
25938   *         src: 'logo.svg',
25939   *         data: { resolution: 2 }
25940   *     }
25941   * ]);
25942   *
25943   * // Load the bundle
25944   * await Assets.loadBundle('animals');
25945   *
25946   * // Use the loaded assets
25947   * const bunny = Sprite.from('bunny');
25948   * const chicken = Sprite.from('chicken');
25949   * ```
25950   * @remarks
25951   * - Bundle IDs must be unique
25952   * - Assets in bundles are not loaded until `loadBundle` is called
25953   * - Bundles can be background loaded using `backgroundLoadBundle`
25954   * - Assets in bundles can be loaded individually using their aliases
25955   * @see {@link Assets.loadBundle} For loading bundles
25956   * @see {@link Assets.backgroundLoadBundle} For background loading bundles
25957   * @see {@link Assets.unloadBundle} For unloading bundles
25958   * @see {@link AssetsManifest} For manifest format details
25959   */
25960  addBundle(bundleId, assets) {
25961    this.resolver.addBundle(bundleId, assets);
25962  }
25963  /**
25964   * Loads a bundle or multiple bundles of assets. Bundles are collections of related assets
25965   * that can be loaded together.
25966   * @param bundleIds - Single bundle ID or array of bundle IDs to load
25967   * @param onProgress - Optional callback for load progress (0.0 to 1.0)
25968   * @returns Promise that resolves with the loaded bundle assets
25969   * @example
25970   * ```ts
25971   * // Define bundles in your manifest
25972   * const manifest = {
25973   *     bundles: [
25974   *         {
25975   *             name: 'load-screen',
25976   *             assets: [
25977   *                 {
25978   *                     alias: 'background',
25979   *                     src: 'sunset.png',
25980   *                 },
25981   *                 {
25982   *                     alias: 'bar',
25983   *                     src: 'load-bar.{png,webp}', // use an array of individual assets
25984   *                 },
25985   *             ],
25986   *         },
25987   *         {
25988   *             name: 'game-screen',
25989   *             assets: [
25990   *                 {
25991   *                     alias: 'character',
25992   *                     src: 'robot.png',
25993   *                 },
25994   *                 {
25995   *                     alias: 'enemy',
25996   *                     src: 'bad-guy.png',
25997   *                 },
25998   *             ],
25999   *         },
26000   *     ]
26001   * };
26002   *
26003   * // Initialize with manifest
26004   * await Assets.init({ manifest });
26005   *
26006   * // Or add bundles programmatically
26007   * Assets.addBundle('load-screen', [...]);
26008   * Assets.loadBundle('load-screen');
26009   *
26010   * // Load a single bundle
26011   * await Assets.loadBundle('load-screen');
26012   * const bg = Sprite.from('background'); // Uses alias from bundle
26013   *
26014   * // Load multiple bundles
26015   * await Assets.loadBundle([
26016   *     'load-screen',
26017   *     'game-screen'
26018   * ]);
26019   *
26020   * // Load with progress tracking
26021   * await Assets.loadBundle('game-screen', (progress) => {
26022   *     console.log(`Loading: ${Math.round(progress * 100)}%`);
26023   * });
26024   * ```
26025   * @remarks
26026   * - Bundle assets are cached automatically
26027   * - Bundles can be pre-loaded using `backgroundLoadBundle`
26028   * - Assets in bundles can be accessed by their aliases
26029   * - Progress callback receives values from 0.0 to 1.0
26030   * @throws {Error} If the bundle ID doesn't exist in the manifest
26031   * @see {@link Assets.addBundle} For adding bundles programmatically
26032   * @see {@link Assets.backgroundLoadBundle} For background loading bundles
26033   * @see {@link Assets.unloadBundle} For unloading bundles
26034   * @see {@link AssetsManifest} For manifest format details
26035   */
26036  async loadBundle(bundleIds, onProgress) {
26037    if (!this._initialized) {
26038      await this.init();
26039    }
26040    let singleAsset = false;
26041    if (typeof bundleIds === "string") {
26042      singleAsset = true;
26043      bundleIds = [bundleIds];
26044    }
26045    const resolveResults = this.resolver.resolveBundle(bundleIds);
26046    const out = {};
26047    const keys = Object.keys(resolveResults);
26048    let total = 0;
26049    const counts = [];
26050    const _onProgress = () => {
26051      onProgress?.
vendor: 11,632 bytes, lines 26051-26383
26051(counts.reduce((a, b) => a + b, 0) / total);
26052    };
26053    const promises = keys.map((bundleId, i) => {
26054      const resolveResult = resolveResults[bundleId];
26055      const values = Object.values(resolveResult);
26056      const totalAssetsToLoad = [...new Set(values.flat())];
26057      const progressSize = totalAssetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0);
26058      counts.push(0);
26059      total += progressSize;
26060      return this._mapLoadToResolve(resolveResult, (e) => {
26061        counts[i] = e * progressSize;
26062        _onProgress();
26063      }).then((resolveResult2) => {
26064        out[bundleId] = resolveResult2;
26065      });
26066    });
26067    await Promise.all(promises);
26068    return singleAsset ? out[bundleIds[0]] : out;
26069  }
26070  /**
26071   * Initiates background loading of assets. This allows assets to be loaded passively while other operations
26072   * continue, making them instantly available when needed later.
26073   *
26074   * Background loading is useful for:
26075   * - Preloading game levels while in a menu
26076   * - Loading non-critical assets during gameplay
26077   * - Reducing visible loading screens
26078   * @param urls - Single URL/alias or array of URLs/aliases to load in the background
26079   * @example
26080   * ```ts
26081   * // Basic background loading
26082   * Assets.backgroundLoad('images/level2-assets.png');
26083   *
26084   * // Background load multiple assets
26085   * Assets.backgroundLoad([
26086   *     'images/sprite1.png',
26087   *     'images/sprite2.png',
26088   *     'images/background.png'
26089   * ]);
26090   *
26091   * // Later, when you need the assets
26092   * const textures = await Assets.load([
26093   *     'images/sprite1.png',
26094   *     'images/sprite2.png'
26095   * ]); // Resolves immediately if background loading completed
26096   * ```
26097   * @remarks
26098   * - Background loading happens one asset at a time to avoid blocking the main thread
26099   * - Loading can be interrupted safely by calling `Assets.load()`
26100   * - Assets are cached as they complete loading
26101   * - No progress tracking is available for background loading
26102   */
26103  async backgroundLoad(urls) {
26104    if (!this._initialized) {
26105      await this.init();
26106    }
26107    if (typeof urls === "string") {
26108      urls = [urls];
26109    }
26110    const resolveResults = this.resolver.resolve(urls);
26111    this._backgroundLoader.add(Object.values(resolveResults));
26112  }
26113  /**
26114   * Initiates background loading of asset bundles. Similar to backgroundLoad but works with
26115   * predefined bundles of assets.
26116   *
26117   * Perfect for:
26118   * - Preloading level bundles during gameplay
26119   * - Loading UI assets during splash screens
26120   * - Preparing assets for upcoming game states
26121   * @param bundleIds - Single bundle ID or array of bundle IDs to load in the background
26122   * @example
26123   * ```ts
26124   * // Define bundles in your manifest
26125   * await Assets.init({
26126   *     manifest: {
26127   *         bundles: [
26128   *             {
26129   *               name: 'home',
26130   *               assets: [
26131   *                 {
26132   *                     alias: 'background',
26133   *                     src: 'images/home-bg.png',
26134   *                 },
26135   *                 {
26136   *                     alias: 'logo',
26137   *                     src: 'images/logo.png',
26138   *                 }
26139   *              ]
26140   *            },
26141   *            {
26142   *             name: 'level-1',
26143   *             assets: [
26144   *                 {
26145   *                     alias: 'background',
26146   *                     src: 'images/level1/bg.png',
26147   *                 },
26148   *                 {
26149   *                     alias: 'sprites',
26150   *                     src: 'images/level1/sprites.json'
26151   *                 }
26152   *             ]
26153   *         }]
26154   *     }
26155   * });
26156   *
26157   * // Load the home screen assets right away
26158   * await Assets.loadBundle('home');
26159   * showHomeScreen();
26160   *
26161   * // Start background loading while showing home screen
26162   * Assets.backgroundLoadBundle('level-1');
26163   *
26164   * // When player starts level, load completes faster
26165   * await Assets.loadBundle('level-1');
26166   * hideHomeScreen();
26167   * startLevel();
26168   * ```
26169   * @remarks
26170   * - Bundle assets are loaded one at a time
26171   * - Loading can be interrupted safely by calling `Assets.loadBundle()`
26172   * - Assets are cached as they complete loading
26173   * - Requires bundles to be registered via manifest or `addBundle`
26174   * @see {@link Assets.addBundle} For adding bundles programmatically
26175   * @see {@link Assets.loadBundle} For immediate bundle loading
26176   * @see {@link AssetsManifest} For manifest format details
26177   */
26178  async backgroundLoadBundle(bundleIds) {
26179    if (!this._initialized) {
26180      await this.init();
26181    }
26182    if (typeof bundleIds === "string") {
26183      bundleIds = [bundleIds];
26184    }
26185    const resolveResults = this.resolver.resolveBundle(bundleIds);
26186    Object.values(resolveResults).forEach((resolveResult) => {
26187      this._backgroundLoader.add(Object.values(resolveResult));
26188    });
26189  }
26190  /**
26191   * Only intended for development purposes.
26192   * This will wipe the resolver and caches.
26193   * You will need to reinitialize the Asset
26194   * @internal
26195   */
26196  reset() {
26197    this.resolver.reset();
26198    this.loader.reset();
26199    this.cache.reset();
26200    this._initialized = false;
26201  }
26202  get(keys) {
26203    if (typeof keys === "string") {
26204      return Cache.get(keys);
26205    }
26206    const assets = {};
26207    for (let i = 0; i < keys.length; i++) {
26208      assets[i] = Cache.get(keys[i]);
26209    }
26210    return assets;
26211  }
26212  /**
26213   * helper function to map resolved assets back to loaded assets
26214   * @param resolveResults - the resolve results from the resolver
26215   * @param progressOrLoadOptions - the progress callback or load options
26216   */
26217  async _mapLoadToResolve(resolveResults, progressOrLoadOptions) {
26218    const resolveArray = [...new Set(Object.values(resolveResults))];
26219    this._backgroundLoader.active = false;
26220    const loadedAssets = await this.loader.load(resolveArray, progressOrLoadOptions);
26221    this._backgroundLoader.active = true;
26222    const out = {};
26223    resolveArray.forEach((resolveResult) => {
26224      const asset = loadedAssets[resolveResult.src];
26225      const keys = [resolveResult.src];
26226      if (resolveResult.alias) {
26227        keys.push(...resolveResult.alias);
26228      }
26229      keys.forEach((key) => {
26230        out[key] = asset;
26231      });
26232      Cache.set(keys, asset);
26233    });
26234    return out;
26235  }
26236  /**
26237   * Unloads assets and releases them from memory. This method ensures proper cleanup of
26238   * loaded assets when they're no longer needed.
26239   * @param urls - Single URL/alias or array of URLs/aliases to unload
26240   * @example
26241   * ```ts
26242   * // Unload a single asset
26243   * await Assets.unload('images/sprite.png');
26244   *
26245   * // Unload using an alias
26246   * await Assets.unload('hero'); // Unloads the asset registered with 'hero' alias
26247   *
26248   * // Unload multiple assets
26249   * await Assets.unload([
26250   *     'images/background.png',
26251   *     'images/character.png',
26252   *     'hero'
26253   * ]);
26254   *
26255   * // Unload and handle creation of new instances
26256   * await Assets.unload('hero');
26257   * const newHero = await Assets.load('hero'); // Will load fresh from source
26258   * ```
26259   * @remarks
26260   * > [!WARNING]
26261   * > Make sure assets aren't being used before unloading:
26262   * > - Remove sprites using the texture
26263   * > - Clear any references to the asset
26264   * > - Textures will be destroyed and can't be used after unloading
26265   * @throws {Error} If the asset is not found in cache
26266   */
26267  async unload(urls) {
26268    if (!this._initialized) {
26269      await this.init();
26270    }
26271    const urlArray = convertToList(urls).map((url) => typeof url !== "string" ? url.src : url);
26272    const resolveResults = this.resolver.resolve(urlArray);
26273    await this._unloadFromResolved(resolveResults);
26274  }
26275  /**
26276   * Unloads all assets in a bundle. Use this to free memory when a bundle's assets
26277   * are no longer needed, such as when switching game levels.
26278   * @param bundleIds - Single bundle ID or array of bundle IDs to unload
26279   * @example
26280   * ```ts
26281   * // Define and load a bundle
26282   * Assets.addBundle('level-1', {
26283   *     background: 'level1/bg.png',
26284   *     sprites: 'level1/sprites.json',
26285   *     music: 'level1/music.mp3'
26286   * });
26287   *
26288   * // Load the bundle
26289   * const level1 = await Assets.loadBundle('level-1');
26290   *
26291   * // Use the assets
26292   * const background = Sprite.from(level1.background);
26293   *
26294   * // When done with the level, unload everything
26295   * await Assets.unloadBundle('level-1');
26296   * // background sprite is now invalid!
26297   *
26298   * // Unload multiple bundles
26299   * await Assets.unloadBundle([
26300   *     'level-1',
26301   *     'level-2',
26302   *     'ui-elements'
26303   * ]);
26304   * ```
26305   * @remarks
26306   * > [!WARNING]
26307   * > - All assets in the bundle will be destroyed
26308   * > - Bundle needs to be reloaded to use assets again
26309   * > - Make sure no sprites or other objects are using the assets
26310   * @throws {Error} If the bundle is not found
26311   * @see {@link Assets.addBundle} For adding bundles
26312   * @see {@link Assets.loadBundle} For loading bundles
26313   */
26314  async unloadBundle(bundleIds) {
26315    if (!this._initialized) {
26316      await this.init();
26317    }
26318    bundleIds = convertToList(bundleIds);
26319    const resolveResults = this.resolver.resolveBundle(bundleIds);
26320    const promises = Object.keys(resolveResults).map((bundleId) => this._unloadFromResolved(resolveResults[bundleId]));
26321    await Promise.all(promises);
26322  }
26323  async _unloadFromResolved(resolveResult) {
26324    const resolveArray = Object.values(resolveResult);
26325    resolveArray.forEach((resolveResult2) => {
26326      Cache.remove(resolveResult2.src);
26327    });
26328    await this.loader.unload(resolveArray);
26329  }
26330  /**
26331   * Detects the supported formats for the browser, and returns an array of supported formats, respecting
26332   * the users preferred formats order.
26333   * @param options - the options to use when detecting formats
26334   * @param options.preferredFormats - the preferred formats to use
26335   * @param options.skipDetections - if we should skip the detections altogether
26336   * @param options.detections - the detections to use
26337   * @returns - the detected formats
26338   */
26339  async _detectFormats(options) {
26340    let formats = [];
26341    if (options.preferredFormats) {
26342      formats = Array.isArray(options.preferredFormats) ? options.preferredFormats : [options.preferredFormats];
26343    }
26344    for (const detection of options.detections) {
26345      if (options.skipDetections || await detection.test()) {
26346        formats = await detection.add(formats);
26347      } else if (!options.skipDetections) {
26348        formats = await detection.remove(formats);
26349      }
26350    }
26351    formats = formats.filter((format, index) => formats.indexOf(format) === index);
26352    return formats;
26353  }
26354  /**
26355   * All the detection parsers currently added to the Assets class.
26356   * @advanced
26357   */
26358  get detections() {
26359    return this._detections;
26360  }
26361  /**
26362   * Sets global preferences for asset loading behavior. This method configures how assets
26363   * are loaded and processed across all parsers.
26364   * @param preferences - Asset loading preferences
26365   * @example
26366   * ```ts
26367   * // Basic preferences
26368   * Assets.setPreferences({
26369   *     crossOrigin: 'anonymous',
26370   *     parseAsGraphicsContext: false
26371   * });
26372   * ```
26373   * @remarks
26374   * Preferences are applied to all compatible parsers and affect future asset loading.
26375   * Common preferences include:
26376   * - `crossOrigin`: CORS setting for loaded assets
26377   * - `preferWorkers`: Whether to use web workers for loading textures
26378   * - `preferCreateImageBitmap`: Use `createImageBitmap` for texture creation. Turning this off will use the `Image` constructor instead.
26379   * @see {@link AssetsPreferences} For all available preferences
26380   */
26381  setPreferences(preferences) {
26382    this.loader.parsers.forEach((parser) => {
26383      if (!parser.config)
26383
26384        return;
26385      Object.keys(parser.config).filter((key) => key in preferences).forEach((key) => {
26386        parser.config[key] = preferences[key];
26387      });
26388    });
26389  }
26390}
26391const Assets = new AssetsClass();
26392extensions.handleByList(ExtensionType.LoadParser, Assets.loader.parsers).handleByList(ExtensionType.ResolveParser, Assets.resolver.parsers).handleByList(ExtensionType.CacheParser, Assets.cache.parsers).handleByList(ExtensionType.DetectionParser, Assets.detections);
26393extensions.add(
26394  cacheTextureArray,
26395  detectDefaults,
26396  detectAvif,
26397  detectWebp,
26398  detectMp4,
26399  detectOgv,
26400  detectWebm,
26401  loadJson,
26402  loadTxt,
26403  loadWebFont,
26404  loadSvg,
26405  loadTextures,
26406  loadVideoTextures,
26407  loadBitmapFont,
26408  bitmapFontCachePlugin,
26409  resolveTextureUrl,
26410  resolveJsonUrl
26411);
26412const assetKeyMap = {
26413  loader: ExtensionType.LoadParser,
26414  resolver: ExtensionType.ResolveParser,
26415  cache: ExtensionType.CacheParser,
26416  detection: ExtensionType.DetectionParser
26417};
26418extensions.handle(ExtensionType.Asset, (extension) => {
26419  const ref = extension.ref;
26420  Object.entries(assetKeyMap).filter(([key]) => !!ref[key]).forEach(([key, type]) => extensions.add(Object.assign(
26421    ref[key],
26422    // Allow the function to optionally define it's own
26423    // ExtensionMetadata, the use cases here is priority for LoaderParsers
26424    { extension: ref[key].extension ?? type }
26425  )));
26426}, (extension) => {
26427  const ref = extension.ref;
26428  Object.keys(assetKeyMap).filter((key) => !!ref[key]).forEach((key) => extensions.remove(ref[key]));
26429});
26430
26431class Graphics extends ViewContainer {
26432  /**
26433   * Creates a new Graphics object.
26434   * @param options - Options for the Graphics.
26435   */
26436  constructor(options) {
26437    if (options instanceof GraphicsContext) {
26438      options = { context: options };
26439    }
26440    const { context, roundPixels, ...rest } = options || {};
26441    super({
26442      label: "Graphics",
26443      ...rest
26444    });
26445    /** @internal */
26446    this.renderPipeId = "graphics";
26447    if (!context) {
26448      this.context = this._ownedContext = new GraphicsContext();
26449      this.context.autoGarbageCollect = this.autoGarbageCollect;
26450    } else {
26451      this.context = context;
26452    }
26453    this.didViewUpdate = true;
26454    this.allowChildren = false;
26455    this.roundPixels = roundPixels ?? false;
26456  }
26457  set context(context) {
26458    if (context === this._context)
26459      return;
26460    if (this._context) {
26461      this._context.off("update", this.onViewUpdate, this);
26462      this._context.off("unload", this.unload, this);
26463    }
26464    this._context = context;
26465    this._context.on("update", this.onViewUpdate, this);
26466    this._context.on("unload", this.unload, this);
26467    this.onViewUpdate();
26468  }
26469  /**
26470   * The underlying graphics context used for drawing operations.
26471   * Controls how shapes and paths are rendered.
26472   * @example
26473   * ```ts
26474   * // Create a shared context
26475   * const sharedContext = new GraphicsContext();
26476   *
26477   * // Create graphics objects sharing the same context
26478   * const graphics1 = new Graphics();
26479   * const graphics2 = new Graphics();
26480   *
26481   * // Assign shared context
26482   * graphics1.context = sharedContext;
26483   * graphics2.context = sharedContext;
26484   *
26485   * // Both graphics will show the same shapes
26486   * sharedContext
26487   *     .rect(0, 0, 100, 100)
26488   *     .fill({ color: 0xff0000 });
26489   * ```
26490   * @see {@link GraphicsContext} For drawing operations
26491   * @see {@link GraphicsOptions} For context configuration
26492   */
26493  get context() {
26494    return this._context;
26495  }
26496  /**
26497   * The local bounds of the graphics object.
26498   * Returns the boundaries after all graphical operations but before any transforms.
26499   * @example
26500   * ```ts
26501   * const graphics = new Graphics();
26502   *
26503   * // Draw a shape
26504   * graphics
26505   *     .rect(0, 0, 100, 100)
26506   *     .fill({ color: 0xff0000 });
26507   *
26508   * // Get bounds information
26509   * const bounds = graphics.bounds;
26510   * console.log(bounds.width);  // 100
26511   * console.log(bounds.height); // 100
26512   * ```
26513   * @readonly
26514   * @see {@link Bounds} For bounds operations
26515   * @see {@link Container#getBounds} For transformed bounds
26516   */
26517  get bounds() {
26518    return this._context.bounds;
26519  }
26520  /**
26521   * Graphics objects do not need to update their bounds as the context handles this.
26522   * @private
26523   */
26524  updateBounds() {
26525  }
26526  /**
26527   * Checks if the object contains the given point.
26528   * Returns true if the point lies within the Graphics object's rendered area.
26529   * @example
26530   * ```ts
26531   * const graphics = new Graphics();
26532   *
26533   * // Draw a shape
26534   * graphics
26535   *     .rect(0, 0, 100, 100)
26536   *     .fill({ color: 0xff0000 });
26537   *
26538   * // Check point intersection
26539   * if (graphics.containsPoint({ x: 50, y: 50 })) {
26540   *     console.log('Point is inside rectangle!');
26541   * }
26542   * ```
26543   * @param point - The point to check in local coordinates
26544   * @returns True if the point is inside the Graphics object
26545   * @see {@link Graphics#bounds} For bounding box checks
26546   * @see {@link PointData} For point data structure
26547   */
26548  containsPoint(point) {
26549    return this._context.containsPoint(point);
26550  }
26551  /**
26552   * Destroys this graphics renderable and optionally its context.
26553   * @param options - Options parameter. A boolean will act as if all options
26554   *
26555   * If the context was created by this graphics and `destroy(false)` or `destroy()` is called
26556   * then the context will still be destroyed.
26557   *
26558   * If you want to explicitly not destroy this context that this graphics created,
26559   * then you should pass destroy({ context: false })
26560   *
26561   * If the context was passed in as an argument to the constructor then it will not be destroyed
26562   * @example
26563   * ```ts
26564   * // Destroy the graphics and its context
26565   * graphics.destroy();
26566   * graphics.destroy(true);
26567   * graphics.destroy({ context: true, texture: true, textureSource: true });
26568   * ```
26569   */
26570  destroy(options) {
26571    if (this._ownedContext && !options) {
26572      this._ownedContext.destroy(options);
26573    } else if (options === true || options?.context === true) {
26574      this._context.destroy(options);
26575    }
vendor: 18,393 bytes, lines 26575-27188
26575
26576    this._ownedContext = null;
26577    this._context = null;
26578    super.destroy(options);
26579  }
26580  /**
26581   * @param now - The current time in milliseconds.
26582   * @internal
26583   */
26584  _onTouch(now) {
26585    this._gcLastUsed = now;
26586    this._context._gcLastUsed = now;
26587  }
26588  _callContextMethod(method, args) {
26589    this.context[method](...args);
26590    return this;
26591  }
26592  // --------------------------------------- GraphicsContext methods ---------------------------------------
26593  /**
26594   * Sets the current fill style of the graphics context.
26595   * The fill style can be a color, gradient, pattern, or a complex style object.
26596   * @example
26597   * ```ts
26598   * const graphics = new Graphics();
26599   *
26600   * // Basic color fill
26601   * graphics
26602   *     .setFillStyle({ color: 0xff0000 }) // Red fill
26603   *     .rect(0, 0, 100, 100)
26604   *     .fill();
26605   *
26606   * // Gradient fill
26607   * const gradient = new FillGradient({
26608   *    end: { x: 1, y: 0 },
26609   *    colorStops: [
26610   *         { offset: 0, color: 0xff0000 }, // Red at start
26611   *         { offset: 0.5, color: 0x00ff00 }, // Green at middle
26612   *         { offset: 1, color: 0x0000ff }, // Blue at end
26613   *    ],
26614   * });
26615   *
26616   * graphics
26617   *     .setFillStyle(gradient)
26618   *     .circle(100, 100, 50)
26619   *     .fill();
26620   *
26621   * // Pattern fill
26622   * const pattern = new FillPattern(texture);
26623   * graphics
26624   *     .setFillStyle({
26625   *         fill: pattern,
26626   *         alpha: 0.5
26627   *     })
26628   *     .rect(0, 0, 200, 200)
26629   *     .fill();
26630   * ```
26631   * @param {FillInput} args - The fill style to apply
26632   * @returns The Graphics instance for chaining
26633   * @see {@link FillStyle} For fill style options
26634   * @see {@link FillGradient} For gradient fills
26635   * @see {@link FillPattern} For pattern fills
26636   */
26637  setFillStyle(...args) {
26638    return this._callContextMethod("setFillStyle", args);
26639  }
26640  /**
26641   * Sets the current stroke style of the graphics context.
26642   * Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations.
26643   * @example
26644   * ```ts
26645   * const graphics = new Graphics();
26646   *
26647   * // Basic color stroke
26648   * graphics
26649   *     .setStrokeStyle({
26650   *         width: 2,
26651   *         color: 0x000000
26652   *     })
26653   *     .rect(0, 0, 100, 100)
26654   *     .stroke();
26655   *
26656   * // Complex stroke style
26657   * graphics
26658   *     .setStrokeStyle({
26659   *         width: 4,
26660   *         color: 0xff0000,
26661   *         alpha: 0.5,
26662   *         join: 'round',
26663   *         cap: 'round',
26664   *         alignment: 0.5
26665   *     })
26666   *     .circle(100, 100, 50)
26667   *     .stroke();
26668   *
26669   * // Gradient stroke
26670   * const gradient = new FillGradient({
26671   *    end: { x: 1, y: 0 },
26672   *    colorStops: [
26673   *         { offset: 0, color: 0xff0000 }, // Red at start
26674   *         { offset: 0.5, color: 0x00ff00 }, // Green at middle
26675   *         { offset: 1, color: 0x0000ff }, // Blue at end
26676   *    ],
26677   * });
26678   *
26679   * graphics
26680   *     .setStrokeStyle({
26681   *         width: 10,
26682   *         fill: gradient
26683   *     })
26684   *     .poly([0,0, 100,50, 0,100])
26685   *     .stroke();
26686   * ```
26687   * @param {StrokeInput} args - The stroke style to apply
26688   * @returns The Graphics instance for chaining
26689   * @see {@link StrokeStyle} For stroke style options
26690   * @see {@link FillGradient} For gradient strokes
26691   * @see {@link FillPattern} For pattern strokes
26692   */
26693  setStrokeStyle(...args) {
26694    return this._callContextMethod("setStrokeStyle", args);
26695  }
26696  fill(...args) {
26697    return this._callContextMethod("fill", args);
26698  }
26699  /**
26700   * Strokes the current path with the current stroke style or specified style.
26701   * Outlines the shape using the stroke settings.
26702   * @example
26703   * ```ts
26704   * const graphics = new Graphics();
26705   *
26706   * // Stroke with direct color
26707   * graphics
26708   *     .circle(50, 50, 25)
26709   *     .stroke({
26710   *         width: 2,
26711   *         color: 0xff0000
26712   *     }); // 2px red stroke
26713   *
26714   * // Fill with texture
26715   * graphics
26716   *    .rect(0, 0, 100, 100)
26717   *    .stroke(myTexture); // Fill with texture
26718   *
26719   * // Stroke with gradient
26720   * const gradient = new FillGradient({
26721   *     end: { x: 1, y: 0 },
26722   *     colorStops: [
26723   *         { offset: 0, color: 0xff0000 },
26724   *         { offset: 0.5, color: 0x00ff00 },
26725   *         { offset: 1, color: 0x0000ff },
26726   *     ],
26727   * });
26728   *
26729   * graphics
26730   *     .rect(0, 0, 100, 100)
26731   *     .stroke({
26732   *         width: 4,
26733   *         fill: gradient,
26734   *         alignment: 0.5,
26735   *         join: 'round'
26736   *     });
26737   * ```
26738   * @param {StrokeStyle} args - Optional stroke style to apply. Can be:
26739   * - A stroke style object with width, color, etc.
26740   * - A gradient
26741   * - A pattern
26742   * If omitted, uses current stroke style.
26743   * @returns The Graphics instance for chaining
26744   * @see {@link StrokeStyle} For stroke style options
26745   * @see {@link FillGradient} For gradient strokes
26746   * @see {@link setStrokeStyle} For setting default stroke style
26747   */
26748  stroke(...args) {
26749    return this._callContextMethod("stroke", args);
26750  }
26751  texture(...args) {
26752    return this._callContextMethod("texture", args);
26753  }
26754  /**
26755   * Resets the current path. Any previous path and its commands are discarded and a new path is
26756   * started. This is typically called before beginning a new shape or series of drawing commands.
26757   * @example
26758   * ```ts
26759   * const graphics = new Graphics();
26760   * graphics
26761   *     .circle(150, 150, 50)
26762   *     .fill({ color: 0x00ff00 })
26763   *     .beginPath() // Starts a new path
26764   *     .circle(250, 150, 50)
26765   *     .fill({ color: 0x0000ff });
26766   * ```
26767   * @returns The Graphics instance for chaining
26768   * @see {@link Graphics#moveTo} For starting a new subpath
26769   * @see {@link Graphics#closePath} For closing the current path
26770   */
26771  beginPath() {
26772    return this._callContextMethod("beginPath", []);
26773  }
26774  /**
26775   * Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by
26776   * subtracting a path from the previously drawn path.
26777   *
26778   * If a hole is not completely in a shape, it will fail to cut correctly.
26779   * @example
26780   * ```ts
26781   * const graphics = new Graphics();
26782   *
26783   * // Draw outer circle
26784   * graphics
26785   *     .circle(100, 100, 50)
26786   *     .fill({ color: 0xff0000 });
26787   *     .circle(100, 100, 25) // Inner circle
26788   *     .cut() // Cuts out the inner circle from the outer circle
26789   * ```
26790   */
26791  cut() {
26792    return this._callContextMethod("cut", []);
26793  }
26794  arc(...args) {
26795    return this._callContextMethod("arc", args);
26796  }
26797  arcTo(...args) {
26798    return this._callContextMethod("arcTo", args);
26799  }
26800  arcToSvg(...args) {
26801    return this._callContextMethod("arcToSvg", args);
26802  }
26803  bezierCurveTo(...args) {
26804    return this._callContextMethod("bezierCurveTo", args);
26805  }
26806  /**
26807   * Closes the current path by drawing a straight line back to the start point.
26808   *
26809   * This is useful for completing shapes and ensuring they are properly closed for fills.
26810   * @example
26811   * ```ts
26812   * // Create a triangle with closed path
26813   * const graphics = new Graphics();
26814   * graphics
26815   *     .moveTo(50, 50)
26816   *     .lineTo(100, 100)
26817   *     .lineTo(0, 100)
26818   *     .closePath()
26819   * ```
26820   * @returns The Graphics instance for method chaining
26821   * @see {@link Graphics#beginPath} For starting a new path
26822   * @see {@link Graphics#fill} For filling closed paths
26823   * @see {@link Graphics#stroke} For stroking paths
26824   */
26825  closePath() {
26826    return this._callContextMethod("closePath", []);
26827  }
26828  ellipse(...args) {
26829    return this._callContextMethod("ellipse", args);
26830  }
26831  circle(...args) {
26832    return this._callContextMethod("circle", args);
26833  }
26834  path(...args) {
26835    return this._callContextMethod("path", args);
26836  }
26837  lineTo(...args) {
26838    return this._callContextMethod("lineTo", args);
26839  }
26840  moveTo(...args) {
26841    return this._callContextMethod("moveTo", args);
26842  }
26843  quadraticCurveTo(...args) {
26844    return this._callContextMethod("quadraticCurveTo", args);
26845  }
26846  rect(...args) {
26847    return this._callContextMethod("rect", args);
26848  }
26849  roundRect(...args) {
26850    return this._callContextMethod("roundRect", args);
26851  }
26852  poly(...args) {
26853    return this._callContextMethod("poly", args);
26854  }
26855  regularPoly(...args) {
26856    return this._callContextMethod("regularPoly", args);
26857  }
26858  roundPoly(...args) {
26859    return this._callContextMethod("roundPoly", args);
26860  }
26861  roundShape(...args) {
26862    return this._callContextMethod("roundShape", args);
26863  }
26864  filletRect(...args) {
26865    return this._callContextMethod("filletRect", args);
26866  }
26867  chamferRect(...args) {
26868    return this._callContextMethod("chamferRect", args);
26869  }
26870  star(...args) {
26871    return this._callContextMethod("star", args);
26872  }
26873  svg(...args) {
26874    return this._callContextMethod("svg", args);
26875  }
26876  restore(...args) {
26877    return this._callContextMethod("restore", args);
26878  }
26879  /**
26880   * Saves the current graphics state onto a stack. The state includes:
26881   * - Current transformation matrix
26882   * - Current fill style
26883   * - Current stroke style
26884   * @example
26885   * ```ts
26886   * const graphics = new Graphics();
26887   *
26888   * // Save state before complex operations
26889   * graphics.save();
26890   *
26891   * // Create transformed and styled shape
26892   * graphics
26893   *     .translateTransform(100, 100)
26894   *     .rotateTransform(Math.PI / 4)
26895   *     .setFillStyle({
26896   *         color: 0xff0000,
26897   *         alpha: 0.5
26898   *     })
26899   *     .rect(-25, -25, 50, 50)
26900   *     .fill();
26901   *
26902   * // Restore to original state
26903   * graphics.restore();
26904   *
26905   * // Continue drawing with previous state
26906   * graphics
26907   *     .circle(50, 50, 25)
26908   *     .fill();
26909   * ```
26910   * @returns The Graphics instance for method chaining
26911   * @see {@link Graphics#restore} For restoring the saved state
26912   * @see {@link Graphics#setTransform} For setting transformations
26913   */
26914  save() {
26915    return this._callContextMethod("save", []);
26916  }
26917  /**
26918   * Returns the current transformation matrix of the graphics context.
26919   * This matrix represents all accumulated transformations including translate, scale, and rotate.
26920   * @example
26921   * ```ts
26922   * const graphics = new Graphics();
26923   *
26924   * // Apply some transformations
26925   * graphics
26926   *     .translateTransform(100, 100)
26927   *     .rotateTransform(Math.PI / 4);
26928   *
26929   * // Get the current transform matrix
26930   * const matrix = graphics.getTransform();
26931   * console.log(matrix.tx, matrix.ty); // 100, 100
26932   *
26933   * // Use the matrix for other operations
26934   * graphics
26935   *     .setTransform(matrix)
26936   *     .circle(0, 0, 50)
26937   *     .fill({ color: 0xff0000 });
26938   * ```
26939   * @returns The current transformation matrix.
26940   * @see {@link Graphics#setTransform} For setting the transform matrix
26941   * @see {@link Matrix} For matrix operations
26942   */
26943  getTransform() {
26944    return this.context.getTransform();
26945  }
26946  /**
26947   * Resets the current transformation matrix to the identity matrix, effectively removing
26948   * any transformations (rotation, scaling, translation) previously applied.
26949   * @example
26950   * ```ts
26951   * const graphics = new Graphics();
26952   *
26953   * // Apply transformations
26954   * graphics
26955   *     .translateTransform(100, 100)
26956   *     .scaleTransform(2, 2)
26957   *     .circle(0, 0, 25)
26958   *     .fill({ color: 0xff0000 });
26959   * // Reset transform to default state
26960   * graphics
26961   *     .resetTransform()
26962   *     .circle(50, 50, 25) // Will draw at actual coordinates
26963   *     .fill({ color: 0x00ff00 });
26964   * ```
26965   * @returns The Graphics instance for method chaining
26966   * @see {@link Graphics#getTransform} For getting the current transform
26967   * @see {@link Graphics#setTransform} For setting a specific transform
26968   * @see {@link Graphics#save} For saving the current transform state
26969   * @see {@link Graphics#restore} For restoring a previous transform state
26970   */
26971  resetTransform() {
26972    return this._callContextMethod("resetTransform", []);
26973  }
26974  rotateTransform(...args) {
26975    return this._callContextMethod("rotate", args);
26976  }
26977  scaleTransform(...args) {
26978    return this._callContextMethod("scale", args);
26979  }
26980  setTransform(...args) {
26981    return this._callContextMethod("setTransform", args);
26982  }
26983  transform(...args) {
26984    return this._callContextMethod("transform", args);
26985  }
26986  translateTransform(...args) {
26987    return this._callContextMethod("translate", args);
26988  }
26989  /**
26990   * Clears all drawing commands from the graphics context, effectively resetting it.
26991   * This includes clearing the current path, fill style, stroke style, and transformations.
26992   *
26993   * > [!NOTE] Graphics objects are not designed to be continuously cleared and redrawn.
26994   * > Instead, they are intended to be used for static or semi-static graphics that
26995   * > can be redrawn as needed. Frequent clearing and redrawing may lead to performance issues.
26996   * @example
26997   * ```ts
26998   * const graphics = new Graphics();
26999   *
27000   * // Draw some shapes
27001   * graphics
27002   *     .circle(100, 100, 50)
27003   *     .fill({ color: 0xff0000 })
27004   *     .rect(200, 100, 100, 50)
27005   *     .fill({ color: 0x00ff00 });
27006   *
27007   * // Clear all graphics
27008   * graphics.clear();
27009   *
27010   * // Start fresh with new shapes
27011   * graphics
27012   *     .circle(150, 150, 30)
27013   *     .fill({ color: 0x0000ff });
27014   * ```
27015   * @returns The Graphics instance for method chaining
27016   * @see {@link Graphics#beginPath} For starting a new path without clearing styles
27017   * @see {@link Graphics#save} For saving the current state
27018   * @see {@link Graphics#restore} For restoring a previous state
27019   */
27020  clear() {
27021    return this._callContextMethod("clear", []);
27022  }
27023  /**
27024   * Gets or sets the current fill style for the graphics context. The fill style determines
27025   * how shapes are filled when using the fill() method.
27026   * @example
27027   * ```ts
27028   * const graphics = new Graphics();
27029   *
27030   * // Basic color fill
27031   * graphics.fillStyle = {
27032   *     color: 0xff0000,  // Red
27033   *     alpha: 1
27034   * };
27035   *
27036   * // Using gradients
27037   * const gradient = new FillGradient({
27038   *     end: { x: 0, y: 1 }, // Vertical gradient
27039   *     stops: [
27040   *         { offset: 0, color: 0xff0000, alpha: 1 }, // Start color
27041   *         { offset: 1, color: 0x0000ff, alpha: 1 }  // End color
27042   *     ]
27043   * });
27044   *
27045   * graphics.fillStyle = {
27046   *     fill: gradient,
27047   *     alpha: 0.8
27048   * };
27049   *
27050   * // Using patterns
27051   * graphics.fillStyle = {
27052   *     texture: myTexture,
27053   *     alpha: 1,
27054   *     matrix: new Matrix()
27055   *         .scale(0.5, 0.5)
27056   *         .rotate(Math.PI / 4)
27057   * };
27058   * ```
27059   * @type {ConvertedFillStyle}
27060   * @see {@link FillStyle} For all available fill style options
27061   * @see {@link FillGradient} For creating gradient fills
27062   * @see {@link Graphics#fill} For applying the fill to paths
27063   */
27064  get fillStyle() {
27065    return this._context.fillStyle;
27066  }
27067  set fillStyle(value) {
27068    this._context.fillStyle = value;
27069  }
27070  /**
27071   * Gets or sets the current stroke style for the graphics context. The stroke style determines
27072   * how paths are outlined when using the stroke() method.
27073   * @example
27074   * ```ts
27075   * const graphics = new Graphics();
27076   *
27077   * // Basic stroke style
27078   * graphics.strokeStyle = {
27079   *     width: 2,
27080   *     color: 0xff0000,
27081   *     alpha: 1
27082   * };
27083   *
27084   * // Using with gradients
27085   * const gradient = new FillGradient({
27086   *   end: { x: 0, y: 1 },
27087   *   stops: [
27088   *       { offset: 0, color: 0xff0000, alpha: 1 },
27089   *       { offset: 1, color: 0x0000ff, alpha: 1 }
27090   *   ]
27091   * });
27092   *
27093   * graphics.strokeStyle = {
27094   *     width: 4,
27095   *     fill: gradient,
27096   *     alignment: 0.5,
27097   *     join: 'round',
27098   *     cap: 'round'
27099   * };
27100   *
27101   * // Complex stroke settings
27102   * graphics.strokeStyle = {
27103   *     width: 6,
27104   *     color: 0x00ff00,
27105   *     alpha: 0.5,
27106   *     join: 'miter',
27107   *     miterLimit: 10,
27108   * };
27109   * ```
27110   * @see {@link StrokeStyle} For all available stroke style options
27111   * @see {@link Graphics#stroke} For applying the stroke to paths
27112   */
27113  get strokeStyle() {
27114    return this._context.strokeStyle;
27115  }
27116  set strokeStyle(value) {
27117    this._context.strokeStyle = value;
27118  }
27119  /**
27120   * Creates a new Graphics object that copies the current graphics content.
27121   * The clone can either share the same context (shallow clone) or have its own independent
27122   * context (deep clone).
27123   * @example
27124   * ```ts
27125   * const graphics = new Graphics();
27126   *
27127   * // Create original graphics content
27128   * graphics
27129   *     .circle(100, 100, 50)
27130   *     .fill({ color: 0xff0000 });
27131   *
27132   * // Create a shallow clone (shared context)
27133   * const shallowClone = graphics.clone();
27134   *
27135   * // Changes to original affect the clone
27136   * graphics
27137   *     .circle(200, 100, 30)
27138   *     .fill({ color: 0x00ff00 });
27139   *
27140   * // Create a deep clone (independent context)
27141   * const deepClone = graphics.clone(true);
27142   *
27143   * // Modify deep clone independently
27144   * deepClone
27145   *     .translateTransform(100, 100)
27146   *     .circle(0, 0, 40)
27147   *     .fill({ color: 0x0000ff });
27148   * ```
27149   * @param deep - Whether to create a deep clone of the graphics object.
27150   *              If false (default), the context will be shared between objects.
27151   *              If true, creates an independent copy of the context.
27152   * @returns A new Graphics instance with either shared or copied context
27153   * @see {@link Graphics#context} For accessing the underlying graphics context
27154   * @see {@link GraphicsContext} For understanding the shared context behavior
27155   */
27156  clone(deep = false) {
27157    if (deep) {
27158      return new Graphics(this._context.clone());
27159    }
27160    this._ownedContext = null;
27161    const clone = new Graphics(this._context);
27162    return clone;
27163  }
27164  // -------- v7 deprecations ---------
27165  /**
27166   * @param width
27167   * @param color
27168   * @param alpha
27169   * @deprecated since 8.0.0 Use {@link Graphics#setStrokeStyle} instead
27170   */
27171  lineStyle(width, color, alpha) {
27172    deprecation(v8_0_0, "Graphics#lineStyle is no longer needed. Use Graphics#setStrokeStyle to set the stroke style.");
27173    const strokeStyle = {};
27174    width && (strokeStyle.width = width);
27175    color && (strokeStyle.color = color);
27176    alpha && (strokeStyle.alpha = alpha);
27177    this.context.strokeStyle = strokeStyle;
27178    return this;
27179  }
27180  /**
27181   * @param color
27182   * @param alpha
27183   * @deprecated since 8.0.0 Use {@link Graphics#fill} instead
27184   */
27185  beginFill(color, alpha) {
27186    deprecation(v8_0_0, "Graphics#beginFill is no longer needed. Use Graphics#fill to fill the shape with the desired style.");
27187    const fillStyle = {};
27188    if (color !== void 0)
27188
27189      fillStyle.color = color;
27190    if (alpha !== void 0)
27191      fillStyle.alpha = alpha;
27192    this.context.fillStyle = fillStyle;
27193    return this;
27194  }
27195  /**
27196   * @deprecated since 8.0.0 Use {@link Graphics#fill} instead
27197   */
27198  endFill() {
27199    deprecation(v8_0_0, "Graphics#endFill is no longer needed. Use Graphics#fill to fill the shape with the desired style.");
27200    this.context.fill();
27201    const strokeStyle = this.context.strokeStyle;
27202    if (strokeStyle.width !== GraphicsContext.defaultStrokeStyle.width || strokeStyle.color !== GraphicsContext.defaultStrokeStyle.color || strokeStyle.alpha !== GraphicsContext.defaultStrokeStyle.alpha) {
27203      this.context.stroke();
27204    }
27205    return this;
27206  }
27207  /**
27208   * @param {...any} args
27209   * @deprecated since 8.0.0 Use {@link Graphics#circle} instead
27210   */
27211  drawCircle(...args) {
27212    deprecation(v8_0_0, "Graphics#drawCircle has been renamed to Graphics#circle");
27213    return this._callContextMethod("circle", args);
27214  }
27215  /**
27216   * @param {...any} args
27217   * @deprecated since 8.0.0 Use {@link Graphics#ellipse} instead
27218   */
27219  drawEllipse(...args) {
27220    deprecation(v8_0_0, "Graphics#drawEllipse has been renamed to Graphics#ellipse");
27221    return this._callContextMethod("ellipse", args);
27222  }
27223  /**
27224   * @param {...any} args
27225   * @deprecated since 8.0.0 Use {@link Graphics#poly} instead
27226   */
27227  drawPolygon(...args) {
27228    deprecation(v8_0_0, "Graphics#drawPolygon has been renamed to Graphics#poly");
27229    return this._callContextMethod("poly", args);
27230  }
27231  /**
27232   * @param {...any} args
27233   * @deprecated since 8.0.0 Use {@link Graphics#rect} instead
27234   */
27235  drawRect(...args) {
27236    deprecation(v8_0_0, "Graphics#drawRect has been renamed to Graphics#rect");
27237    return this._callContextMethod("rect", args);
27238  }
27239  /**
27240   * @param {...any} args
27241   * @deprecated since 8.0.0 Use {@link Graphics#roundRect} instead
27242   */
27243  drawRoundedRect(...args) {
27244    deprecation(v8_0_0, "Graphics#drawRoundedRect has been renamed to Graphics#roundRect");
27245    return this._callContextMethod("roundRect", args);
27246  }
27247  /**
27248   * @param {...any} args
27249   * @deprecated since 8.0.0 Use {@link Graphics#star} instead
27250   */
27251  drawStar(...args) {
27252    deprecation(v8_0_0, "Graphics#drawStar has been renamed to Graphics#star");
27253    return this._callContextMethod("star", args);
27254  }
27255}
27256
27257class RenderTexture extends Texture {
27258  /**
27259   * Creates a RenderTexture. Pass `dynamic: true` in options to allow resizing after creation.
27260   * @param options - Options for the RenderTexture, including width, height, and dynamic.
27261   * @returns A new RenderTexture instance.
27262   * @example
27263   * const rt = RenderTexture.create({ width: 100, height: 100, dynamic: true });
27264   * rt.resize(500, 500);
27265   */
27266  static create(options) {
27267    const { dynamic, ...rest } = options;
27268    return new RenderTexture({
27269      source: new TextureSource(rest),
27270      dynamic: dynamic ?? false
27271    });
27272  }
27273  /**
27274   * Resizes the render texture.
27275   * @param width - The new width of the render texture.
27276   * @param height - The new height of the render texture.
27277   * @param resolution - The new resolution of the render texture.
27278   * @returns This texture.
27279   */
27280  resize(width, height, resolution) {
27281    this.source.resize(width, height, resolution);
27282    return this;
27283  }
27284}
27285
27286var DEPRECATED_WRAP_MODES = /* @__PURE__ */ ((DEPRECATED_WRAP_MODES2) => {
27287  DEPRECATED_WRAP_MODES2["CLAMP"] = "clamp-to-edge";
27288  DEPRECATED_WRAP_MODES2["REPEAT"] = "repeat";
27289  DEPRECATED_WRAP_MODES2["MIRRORED_REPEAT"] = "mirror-repeat";
27290  return DEPRECATED_WRAP_MODES2;
27291})(DEPRECATED_WRAP_MODES || {});
27292new Proxy(DEPRECATED_WRAP_MODES, {
27293  get(target, prop) {
27294    deprecation(v8_0_0, `DRAW_MODES.${prop} is deprecated, use '${DEPRECATED_WRAP_MODES[prop]}' instead`);
27295    return target[prop];
27296  }
27297});
27298var DEPRECATED_SCALE_MODES = /* @__PURE__ */ ((DEPRECATED_SCALE_MODES2) => {
27299  DEPRECATED_SCALE_MODES2["NEAREST"] = "nearest";
27300  DEPRECATED_SCALE_MODES2["LINEAR"] = "linear";
27301  return DEPRECATED_SCALE_MODES2;
27302})(DEPRECATED_SCALE_MODES || {});
27303const SCALE_MODES = new Proxy(DEPRECATED_SCALE_MODES, {
27304  get(target, prop) {
27305    deprecation(v8_0_0, `DRAW_MODES.${prop} is deprecated, use '${DEPRECATED_SCALE_MODES[prop]}' instead`);
27306    return target[prop];
27307  }
27308});
27309
27310extensions.add(browserExt, webworkerExt);
27311
27312function clamp(n, min, max) {
27313  return n < min ? min : n > max ? max : n;
27314}
27315function get_canvas_blob(renderer, obj, bounds) {
27316  return new Promise((resolve) => {
27317    if (!obj) {
27318      resolve(null);
27319      return;
27320    }
27321    const image_bounds = obj.getLocalBounds();
27322    const frame = bounds ? new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height) : new Rectangle(0, 0, image_bounds.width, image_bounds.height);
27323    const src_canvas = renderer.extract.canvas({
27324      target: obj,
27325      resolution: 1,
27326      frame
27327    });
27328    src_canvas.toBlob?.((blob) => {
27329      if (!blob) {
27330        resolve(null);
27331      }
27332      resolve(blob);
27333    });
27334  });
27335}
27336
27337var root$6 = from_html(`<div class="color-gradient svelte-qx84u5"><div class="marker svelte-qx84u5"></div></div> <div class="opacity-slider svelte-qx84u5"><div class="opacity-marker svelte-qx84u5"></div></div> <div class="hue-slider svelte-qx84u5"><div class="marker svelte-qx84u5"></div></div>`, 1);
27338
27339function ColorPicker($$anchor, $$props) {
27340	push($$props, false);
27341
27342	let color = prop($$props, 'color', 12, "rgb(255, 255, 255)");
27343	let sl_marker_pos = mutable_source([0, 0]);
27344	let sl_rect = null;
27345	let sl_moving = false;
27346	let sl = [0, 0];
27347	let hue = mutable_source(0);
27348	let hue_marker_pos = mutable_source(0);
27349	let hue_rect = null;
27350	let hue_moving = false;
27351	let opacity_moving = false;
27352
27353	function handle_hue_down(event) {
27354		hue_rect = event.currentTarget.getBoundingClientRect();
27355		hue_moving = true;
27356		update_hue_from_mouse(event.clientX);
27357	}
27358
27359	function update_hue_from_mouse(x) {
27360		if (!hue_rect) return;
27361
27362		const _x = clamp(x - hue_rect.left, 0, hue_rect.width); // get the x-coordinate relative to the box
27363
27364		set(hue_marker_pos, _x);
27365
27366		const _hue = _x / hue_rect.width * 360; // scale the x position to a hue value (0-360)
27367
27368		set(hue, _hue);
27369		color(hsva_to_rgba({ h: _hue, s: sl[0], v: sl[1], a: 1 }));
27370	}
27371
27372	function hsva_to_rgba(hsva) {
27373		const saturation = hsva.s;
27374		const value = hsva.v;
27375		let chroma = saturation * value;
27376		const hue_by_60 = hsva.h / 60;
27377		let x = chroma * (1 - Math.abs(hue_by_60 % 2 - 1));
27378		const m = value - chroma;
27379
27380		chroma = chroma + m;
27381		x = x + m;
27382
27383		const index = Math.floor(hue_by_60) % 6;
27384		const red = [chroma, x, m, m, x, chroma][index];
27385		const green = [x, chroma, chroma, x, m, m][index];
27386		const blue = [m, m, x, chroma, chroma, x][index];
27387
27388		return `rgba(${red * 255}, ${green * 255}, ${blue * 255}, ${hsva.a}
27388)`;
27389	}
27390
27391	function update_color_from_mouse(x, y) {
27392		if (!sl_rect) return;
27393
27394		const _x = clamp(x - sl_rect.left, 0, sl_rect.width);
27395		const _y = clamp(y - sl_rect.top, 0, sl_rect.height);
27396
27397		set(sl_marker_pos, [_x, _y]);
27398
27399		const _hsva = {
27400			h: get(hue) * 1,
27401			s: _x / sl_rect.width,
27402			v: 1 - _y / sl_rect.height,
27403			a: 1
27404		};
27405
27406		sl = [_hsva.s, _hsva.v];
27407		color(hsva_to_rgba(_hsva));
27408	}
27409
27410	function handle_sl_down(event) {
27411		sl_moving = true;
27412		sl_rect = event.currentTarget.getBoundingClientRect();
27413		update_color_from_mouse(event.clientX, event.clientY);
27414	}
27415
27416	function handle_move(event) {
27417		if (sl_moving) update_color_from_mouse(event.clientX, event.clientY);
27418		if (hue_moving) update_hue_from_mouse(event.clientX);
27419		if (opacity_moving) update_opacity_from_mouse(event.clientX);
27420	}
27421
27422	function handle_end() {
27423		if (sl_moving) sl_moving = false;
27424		if (hue_moving) hue_moving = false;
27425		if (opacity_moving) opacity_moving = false;
27426	}
27427
27428	async function update_mouse_from_color(color) {
27429		if (sl_moving || hue_moving) return;
27430
27431		await tick();
27432
27433		if (!color) return;
27434
27435		if (!sl_rect) {
27436			sl_rect = get(sl_wrap).getBoundingClientRect();
27437		}
27438
27439		if (!hue_rect) {
27440			hue_rect = get(hue_wrap).getBoundingClientRect();
27441		}
27442
27443		const hsva = tinycolor(color).toHsv();
27444		const _x = hsva.s * sl_rect.width;
27445		const _y = (1 - hsva.v) * sl_rect.height;
27446
27447		set(sl_marker_pos, [_x, _y]);
27448		sl = [hsva.s, hsva.v];
27449		set(hue, hsva.h);
27450		set(hue_marker_pos, hsva.h / 360 * hue_rect.width);
27451	}
27452
27453	let sl_wrap = mutable_source();
27454	let hue_wrap = mutable_source();
27455	let opacity = prop($$props, 'opacity', 12, 1);
27456	let opacity_wrap = mutable_source();
27457
27458	function handle_opacity_down(event) {
27459		set(opacity_rect, event.currentTarget.getBoundingClientRect());
27460		opacity_moving = true;
27461		update_opacity_from_mouse(event.clientX);
27462	}
27463
27464	let opacity_rect = mutable_source(null);
27465
27466	function update_opacity_from_mouse(x) {
27467		if (!get(opacity_rect)) return;
27468
27469		const _x = clamp(x - get(opacity_rect).left, 0, get(opacity_rect).width);
27470
27471		opacity(_x / get(opacity_rect).width);
27472	}
27473
27474	onMount(() => {
27475		set(opacity_rect, get(opacity_wrap).getBoundingClientRect());
27476	});
27477
27478	legacy_pre_effect(() => (deep_read_state(color())), () => {
27479		update_mouse_from_color(color());
27480	});
27481
27482	legacy_pre_effect_reset();
27483
27484	var $$exports = {
27485		get color() {
27486			return color();
27487		},
27488
27489		set color($$value) {
27490			color($$value);
27491			flushSync();
27492		},
27493
27494		get opacity() {
27495			return opacity();
27496		},
27497
27498		set opacity($$value) {
27499			opacity($$value);
27500			flushSync();
27501		}
27502	};
27503
27504	init$1();
27505
27506	var fragment = root$6();
27507
27508	event('mousemove', $window, handle_move);
27509	event('mouseup', $window, handle_end);
27510
27511	var div = first_child(fragment);
27512	var div_1 = child(div);
27513	let styles;
27514
27515	reset(div);
27516	bind_this(div, ($$value) => set(sl_wrap, $$value), () => get(sl_wrap));
27517
27518	var div_2 = sibling(div, 2);
27519	var div_3 = child(div_2);
27520	let styles_1;
27521
27522	reset(div_2);
27523	bind_this(div_2, ($$value) => set(opacity_wrap, $$value), () => get(opacity_wrap));
27524
27525	var div_4 = sibling(div_2, 2);
27526	var div_5 = child(div_4);
27527	let styles_2;
27528
27529	reset(div_4);
27530	bind_this(div_4, ($$value) => set(hue_wrap, $$value), () => get(hue_wrap));
27531
27532	template_effect(() => {
27533		set_style(div, `--hue:${get(hue) ?? ''}`);
27534
27535		styles = set_style(div_1, '', styles, {
27536			transform: `translate(${(
27537				get(sl_marker_pos),
27538				untrack(() => get(sl_marker_pos)[0])
27539			) ?? ''}px,${(
27540				get(sl_marker_pos),
27541				untrack(() => get(sl_marker_pos)[1])
27542			) ?? ''}px)`,
27543			background: color()
27544		});
27545
27546		set_style(div_2, `--color:${(color() === 'auto' ? 'transparent' : color()) ?? ''}`);
27547
27548		styles_1 = set_style(div_3, '', styles_1, {
27549			background: 'white',
27550			transform: `translateX(${(
27551				deep_read_state(opacity()),
27552				get(opacity_rect),
27553				untrack(() => opacity() * (get(opacity_rect)?.width ?? 1))
27554			) ?? ''}px)`
27555		});
27556
27557		styles_2 = set_style(div_5, '', styles_2, {
27558			background: "hsl(" + get(hue) + ", 100%, 50%)",
27559			transform: `translateX(${get(hue_marker_pos) ?? ''}px)`
27560		});
27561	});
27562
27563	event('mousedown', div, handle_sl_down);
27564	event('mousedown', div_2, handle_opacity_down);
27565	event('mousedown', div_4, handle_hue_down);
27566	append($$anchor, fragment);
27567
27568	return pop($$exports);
27569}
27570
27571var root_1$4 = from_html(`<span></span>`);
27572var root_4$2 = from_html(`<button></button>`);
27573var root_2$5 = from_html(`<div class="swatch svelte-q8yof7"><!> <!> <button></button></div>`);
27574var root_5$3 = from_html(`<button></button>`);
27575var root$5 = from_html(`<!> <div class="swatch-wrap svelte-q8yof7"><div class="swatch-container svelte-q8yof7"><!> <menu class="swatch svelte-q8yof7"></menu></div></div>`, 1);
27576
27577function ColorSwatch($$anchor, $$props) {
27578	push($$props, false);
27579
27580	const _colors = mutable_source();
27581	const selected_opacity = mutable_source();
27582	let selected_color = prop($$props, 'selected_color', 12);
27583	let colors = prop($$props, 'colors', 12);
27584	let user_colors = prop($$props, 'user_colors', 28, () => []);
27585	let show_empty = prop($$props, 'show_empty', 12, false);
27586	let current_mode = prop($$props, 'current_mode', 12, "hex");
27587	let color_picker = prop($$props, 'color_picker', 12, false);
27588	const dispatch = createEventDispatcher();
27589
27590	function get_color(color_input) {
27591		if (Array.isArray(color_input)) {
27592			return color_input[0];
27593		}
27594
27595		return color_input;
27596	}
27597
27598	function get_opacity(color_input) {
27599		if (Array.isArray(color_input)) {
27600			return color_input[1];
27601		}
27602
27603		const color = tinycolor(color_input);
27604
27605		return color.getAlpha();
27606	}
27607
27608	function get_formatted_color(color, mode) {
27609		const color_value = get_color(color);
27610
27611		if (mode === "hex") {
27612			return tinycolor(color_value).toHexString();
27613		} else if (mode === "rgb") {
27614			return tinycolor(color_value).toRgbString();
27615		}
27616
27617		return tinycolor(color_value).toHslString();
27618	}
27619
27620	`select-${colors().findIndex((c) => get_formatted_color(c, current_mode()) === get_formatted_color(selected_color(), current_mode()))}`;
27621
27622	function handle_select(type, detail) {
27623		`${type}-${detail.index}`;
27624		dispatch(type, detail);
27625	}
27626
27627	function handle_picker_click() {
27628		dispatch("select", { index: null, color: selected_color() });
27629		color_picker(!color_picker());
27630	}
27631
27632	legacy_pre_effect(() => (deep_read_state(show_empty()), deep_read_state(colors())), () => {
27633		set(_colors, show_empty() ? colors() : colors().filter((c) => c));
27634	});
27635
27636	legacy_pre_effect(() => (deep_read_state(selected_color())), () => {
27637		set(selected_opacity, get_opacity(selected_color()));
27638	});
27639
27640	legacy_pre_effect_reset();
27641
27642	var $$exports = {
27643		get selected_color() {
27644			return selected_color();
27645		},
27646
27647		set selected_color($$value) {
27648			selected_color($$value);
27649			flushSync();
27650		},
27651
27652		get colors() {
27653			return colors();
27654		},
27655
27656		set colors($$value) {
27657			colors($$value);
27658			flushSync();
27659		},
27660
27661		get user_colors() {
27662			return user_colors();
27663		},
27664
27665		set user_colors($$value) {
27666			user_colors($$value);
27667			flushSync();
27668		},
27669
27670		get show_empty() {
27671			return show_empty();
27672		},
27673
27674		set show_empty($$value) {
27675			show_empty($$value);
27676			flushSync();
27677		},
27678
27679		get current_mode() {
27680			return current_mode();
27681		},
27682
27683		set current_mode($$value) {
27684			current_mode($$value);
27685			flushSync();
27686		},
27687
27688		get color_picker() {
27689			return color_picker();
27690		},
27691
27692		set color_picker($$value) {
27693			color_picker($$value);
27694			flushSync();
27695		}
27696	};
27697
27698	init$1();
27699
27700	var fragment = root$5();
27701	var node = first_child(fragment);
27702
27703	{
27704		var consequent = ($$anchor) => {
27705			var span = root_1$4();
27706			let classes;
27707
27708			template_effect(() => classes = set_class(span, 1, 'svelte-q8yof7', null, classes, { lg: user_colors() }));
27709			append($$anchor, span);
27710		};
27711
27712		if_block(node, ($$render) => {
27713			if (!color_picker()) $$render(consequent);
27714		});
27715	}
27716
27717	var div = sibling(node, 2);
27718	var div_1 = child(div);
27719	var node_1 = child(div_1);
27720
27721	{
27722		var consequent_2 = ($$anchor) => {
27723			var div_2 = root_2$5();
27724			var node_2 = child(div_2);
27725
27726			{
27727				var consequent_1 = ($$anchor) => {
27728					{
27729						let $0 = derived_safe_equal(() => (
27730							deep_read_state(selected_color()),
27731							untrack(() => get_color(selected_color()))
27732						));
27733
27734						IconButton($$anchor, {
27735							get Icon() {
27736								return Plus;
27737							},
27738							onclick: () => dispatch("add_color"),
27739							roundedness: 'very',
27740							get background() {
27741								return get($0);
27742							},
27743							color: 'white'
27744						});
27745					}
27746				};
27747
27748				if_block(node_2, ($$render) => {
27749					if (color_picker()) $$render(consequent_1);
27750				});
27751			}
27752
27753			var node_3 = sibling(node_2, 2);
27754
27755			each(node_3, 1, user_colors, index, ($$anchor, color, i) => {
27756				const color_string = derived_safe_equal(() => (get(color), untrack(() => get_color(get(color)))));
27757				const opacity = derived_safe_equal(() => (get(color), untrack(() => get_opacity(get(color)))));
27758				var button = root_4$2();
27759				let classes_1;
27760				let styles;
27761
27762				template_effect(() => {
27763					classes_1 = set_class(button, 1, 'color svelte-q8yof7', null, classes_1, {
27764						empty: get(color) === null,
27765						selected: `${get(color_string)}-${get(opacity)}` === `${selected_color()}-${get(selected_opacity)}`
27766					});
27767
27768					styles = set_style(button, `background-color: ${get(color_string) ?? ''}`, styles, { opacity: get(opacity) });
27769				});
27770
27771				event('click', button, () => handle_select("edit", {
27772					index: i,
27773					color: get(color_string),
27774					opacity: get(opacity)
27775				}));
27776
27777				append($$anchor, button);
27778			});
27779
27780			var button_1 = sibling(node_3, 2);
27781			let classes_2;
27782
27783			reset(div_2);
27784			template_effect(() => classes_2 = set_class(button_1, 1, 'color colorpicker svelte-q8yof7', null, classes_2, { hidden: !color_picker() }));
27785			event('click', button_1, handle_picker_click);
27786			append($$anchor, div_2);
27787		};
27788
27789		if_block(node_1, ($$render) => {
27790			if (user_colors()) $$render(consequent_2);
27791		});
27792	}
27793
27794	var menu = sibling(node_1, 2);
27795
27796	each(menu, 5, () => get(_colors), index, ($$anchor, color_item, i) => {
27797		const color_string = derived_safe_equal(() => (
27798			get(color_item),
27799			untrack(() => get_color(get(color_item)))
27800		));
27801
27802		const opacity = derived_safe_equal(() => (
27803			get(color_item),
27804			untrack(() => get_opacity(get(color_item)))
27805		));
27806
27807		var button_2 = root_5$3();
27808		let classes_3;
27809		let styles_1;
27810
27811		template_effect(() => {
27812			classes_3 = set_class(button_2, 1, 'color svelte-q8yof7', null, classes_3, {
27813				empty: get(color_item) === null,
27814				selected: `${get(color_string)}-${get(opacity)}` === `${selected_color()}-${get(selected_opacity)}`
27815			});
27816
27817			styles_1 = set_style(button_2, `background-color: ${get(color_string) ?? ''}`, styles_1, { opacity: get(opacity) });
27818		});
27819
27820		event('click', button_2, () => handle_select("select", {
27821			index: i,
27822			color: get(color_string),
27823			opacity: get(opacity)
27824		}));
27825
27826		append($$anchor, button_2);
27827	});
27828
27829	reset(menu);
27830	reset(div_1);
27831	reset(div);
27832	append($$anchor, fragment);
27833
27834	return pop($$exports);
27835}
27836
27837var root_2$4 = from_html(`<button> </button>`);
27838var root$4 = from_html(`<div class="input svelte-yn52fn"><button class="swatch svelte-yn52fn"></button> <div><div class="input-wrap svelte-yn52fn"><input type="text" class="svelte-yn52fn"/> <button class="eyedropper svelte-yn52fn"><!></button></div> <div class="buttons svelte-yn52fn"></div></div></div>`);
27839
27840function ColorField($$anchor, $$props) {
27841	push($$props, false);
27842
27843	const color_string = mutable_source();
27844	let color = prop($$props, 'color', 12);
27845	let current_mode = prop($$props, 'current_mode', 12, "hex");
27846	const dispatch = createEventDispatcher();
27847	const modes = [["Hex", "hex"], ["RGB", "rgb"], ["HSL", "hsl"]];
27848
27849	function format_color(color, mode) {
27850		if (mode === "hex") {
27851			return tinycolor(color).toHexString();
27852		} else if (mode === "rgb") {
27853			return tinycolor(color).toRgbString();
27854		}
27855
27856		return tinycolor(color).toHslString();
27857	}
27858
27859	function request_eyedropper() {
27860		// @ts-ignore
27861		const eyeDropper = new EyeDropper();
27862
27863		eyeDropper.open().then((result) => {
27864			color(result.sRGBHex);
27865		});
27866	}
27867
27868	//@ts-ignore
27869	const eyedropper_supported = !!window.EyeDropper;
27870
27871	function handle_click() {
27872		dispatch("selected", get(color_string));
27873		dispatch("close");
27874	}
27875
27876	legacy_pre_effect(
27877		() => (
27878			deep_read_state(color()),
27879			deep_read_state(current_mode())
27880		),
27881		() => {
27882			set(color_string, format_color(color(), current_mode()));
27883		}
27884	);
27885
27886	legacy_pre_effect(() => (get(color_string)), () => {
27887		get(color_string) && dispatch("selected", get(color_string));
27888	});
27889
27890	legacy_pre_effect_reset();
27891
27892	var $$exports = {
27893		get color() {
27894			return color();
27895		},
27896
27897		set color($$value) {
27898			color($$value);
27899			flushSync();
27900		},
27901
27902		get current_mode() {
27903			return current_mode();
27904		},
27905
27906		set current_mode($$value) {
27907			current_mode($$value);
27908			flushSync();
27909		}
27910	};
27911
27912	init$1();
27913
27914	var div = root$4();
27915	var button = child(div);
27916	let styles;
27917	var div_1 = sibling(button, 2);
27918	var div_2 = child(div_1);
27919	var input = child(div_2);
27920
27921	remove_input_defaults(input);
27922
27923	var button_1 = sibling(input, 2);
27924	var node = child(button_1);
27925
27926	{
27927		var consequent = ($$anchor) => {
27928			Eyedropper($$anchor);
27929		};
27930
27931		if_block(node, ($$render) => {
27932			if (eyedropper_supported) $$render(consequent);
27933		});
27934	}
27935
27936	reset(button_1);
27937	reset(div_2);
27938
27939	var div_3 = sibling(div_2, 2);
27940
27941	each(div_3, 5, () => modes, index, ($$anchor, $$item) => {
27942		var $$array = user_derived(() => to_array(get($$item), 2));
27943		let label = () => get($$array)[0];
27944		let value = () => get($$array)[1];
27945		var button_2 = root_2$4();
27946		let classes;
27947		var text = child(button_2, true);
27948
27949		reset(button_2);
27950
27951		template_effect(() => {
27952			classes = set_class(button_2, 1, 'button svelte-yn52fn', null, classes, { active: current_mode() === value() });
27953			set_text(text, label());
27954		});
27955
27956		event('click', button_2, () => current_mode(value()));
27957		append($$anchor, button_2);
27958	});
27959
27960	reset(div_3);
27961	reset(div_1);
27962	reset(div);
27963
27964	template_effect(() => {
27965		styles = set_style(button, '', styles, { background: color() });
27966		set_value(input, get(color_string));
27967	});
27968
27969	event('click', button, handle_click);
27970	event('change', input, (e) => color(e.currentTarget.value));
27971	event('click', button_1, request_eyedropper);
27972	append($$anchor, div);
27973
27974	return pop($$exports);
27975}
27976
27977var root$3 = from_html(`<div class="wrap svelte-1y25b79"><span class="svelte-1y25b79"><!></span> <input type="range" class="svelte-1y25b79"/></div>`);
27978
27979function BrushSize_1($$anchor, $$props) {
27980	push($$props, false);
27981
27982	let selected_size = prop($$props, 'selected_size', 12);
27983	let min = prop($$props, 'min', 12);
27984	let max = prop($$props, 'max', 12);
27985	const dispatch = createEventDispatcher();
27986
27987	var $$exports = {
27988		get selected_size() {
27989			return selected_size();
27990		},
27991
27992		set selected_size($$value) {
27993			selected_size($$value);
27994			flushSync();
27995		},
27996
27997		get min() {
27998			return min();
27999		},
28000
28001		set min($$value) {
28002			min($$value);
28003			flushSync();
28004		},
28005
28006		get max() {
28007			return max();
28008		},
28009
28010		set max($$value) {
28011			max($$value);
28012			flushSync();
28013		}
28014	};
28015
28016	init$1();
28017
28018	var div = root$3();
28019	var span = child(div);
28020	var node = child(span);
28021
28022	BrushSize(node);
28023	reset(span);
28024
28025	var input = sibling(span, 2);
28026
28027	remove_input_defaults(input);
28028	set_attribute(input, 'step', 1);
28029	reset(div);
28030	action(div, ($$node, $$action_arg) => click_outside?.($$node, $$action_arg), () => () => dispatch("click_outside"));
28031
28032	template_effect(() => {
28033		set_attribute(input, 'min', min());
28034		set_attribute(input, 'max', max());
28035	});
28036
28037	bind_value(input, selected_size);
28038	append($$anchor, div);
28039
28040	return pop($$exports);
28041}
28042
28043var root_2$3 = from_html(`<!> <!>`, 1);
28044var root$2 = from_html(`<div><!> <!> <!></div>`);
28045
28046function BrushOptions($$anchor, $$props) {
28047	push($$props, false);
28048
28049	let colors = prop($$props, 'colors', 12);
28050	let selected_color = prop($$props, 'selected_color', 12);
28051	let color_mode = prop($$props, 'color_mode', 12, undefined);
28052	let recent_colors = prop($$props, 'recent_colors', 28, () => []);
28053	let selected_size = prop($$props, 'selected_size', 12);
28054	let selected_opacity = prop($$props, 'selected_opacity', 12);
28055	let show_swatch = prop($$props, 'show_swatch', 12);
28056	let show_size = prop($$props, 'show_size', 12);
28057	let mode = prop($$props, 'mode', 12, "brush");
28058	let color_picker = mutable_source(false);
28059	let current_mode = mutable_source("hex");
28060	let editing_index = null;
28061	const dispatch = createEventDispatcher();
28062
28063	function handle_color_selection({ index, color, opacity }, type) {
28064		if (type === "user" && !color) {
28065			editing_index = index;
28066			set(color_picker, true);
28067		}
28068
28069		if (!color) return;
28070
28071		selected_color(color);
28072
28073		if (opacity !== undefined) {
28074			selected_opacity(opacity);
28075		}
28076
28077		if (type === "core") {
28078			set(color_picker, false);
28079		}
28080	}
28081
28082	function handle_color_change(color) {
28083		if (editing_index === null) return;
28084
28085		recent_colors(recent_colors()[editing_index] = color, true);
28086	}
28087
28088	let width = mutable_source(0);
28089	let height = mutable_source(0);
28090
28091	function debounce(func, delay) {
28092		let timeout;
28093
28094		return function (...args) {
28095			clearTimeout(timeout);
28096			timeout = setTimeout(() => func(...args), delay);
28097		};
28098	}
28099
28100	let preview = prop($$props, 'preview', 12, false);
28101
28102	function handle_preview() {
28103		if (!preview()) {
28104			preview(true);
28105			debounced_close_preview();
28106		} else {
28107			debounced_close_preview();
28108		}
28109	}
28110
28111	function handle_close_preview() {
28112		preview(false);
28113	}
28114
28115	const debounced_close_preview = debounce(handle_close_preview, 1000);
28116
28117	function handle_select(color) {
28118		selected_color(color);
28119	}
28120
28121	function handle_add_color() {
28122		// limit to 5
28123		if (recent_colors().length >= 5) {
28124			recent_colors().pop();
28125		}
28126
28127		// check if the color is already in the array
28128		if (recent_colors().some((color) => {
28129			if (Array.isArray(color)) {
28130				return color[0] === selected_color() && color[1] === selected_opacity();
28131			}
28132
28133			return color === selected_color();
28134		})) {
28135			return;
28136		}
28137
28138		recent_colors().push([selected_color(), selected_opacity()]);
28139		recent_colors(recent_colors());
28140	}
28141
28142	legacy_pre_effect(() => (deep_read_state(selected_color())), () => {
28143		handle_color_change(selected_color());
28144	});
28145
28146	legacy_pre_effect(
28147		() => (
28148			deep_read_state(selected_size()),
28149			deep_read_state(selected_color())
28150		),
28151		() => {
28152			(selected_size(), selected_color(), handle_preview());
28153		}
28154	);
28155
28156	legacy_pre_effect_reset();
28157
28158	var $$exports = {
28159		get colors() {
28160			return colors();
28161		},
28162
28163		set colors($$value) {
28164			colors($$value);
28165			flushSync();
28166		},
28167
28168		get selected_color() {
28169			return selected_color();
28170		},
28171
28172		set selected_color($$value) {
28173			selected_color($$value);
28174			flushSync();
28175		},
28176
28177		get color_mode() {
28178			return color_mode();
28179		},
28180
28181		set color_mode($$value) {
28182			color_mode($$value);
28183			flushSync();
28184		},
28185
28186		get recent_colors() {
28187			return recent_colors();
28188		},
28189
28190		set recent_colors($$value) {
28191			recent_colors($$value);
28192			flushSync();
28193		},
28194
28195		get selected_size() {
28196			return selected_size();
28197		},
28198
28199		set selected_size($$value) {
28200			selected_size($$value);
28201			flushSync();
28202		},
28203
28204		get selected_opacity() {
28205			return selected_opacity();
28206		},
28207
28208		set selected_opacity($$value) {
28209			selected_opacity($$value);
28210			flushSync();
28211		},
28212
28213		get show_swatch() {
28214			return show_swatch();
28215		},
28216
28217		set show_swatch($$value) {
28218			show_swatch($$value);
28219			flushSync();
28220		},
28221
28222		get show_size() {
28223			return show_size();
28224		},
28225
28226		set show_size($$value) {
28227			show_size($$value);
28228			flushSync();
28229		},
28230
28231		get mode() {
28232			return mode();
28233		},
28234
28235		set mode($$value) {
28236			mode($$value);
28237			flushSync();
28238		},
28239
28240		get preview() {
28241			return preview();
28242		},
28243
28244		set preview($$value) {
28245			preview($$value);
28246			flushSync();
28247		}
28248	};
28249
28250	init$1();
28251
28252	var div = root$2();
28253	let classes;
28254	var node = child(div);
28255
28256	{
28257		var consequent_1 = ($$anchor) => {
28258			var fragment = comment();
28259			var node_1 = first_child(fragment);
28260
28261			{
28262				var consequent = ($$anchor) => {
28263					var fragment_1 = root_2$3();
28264					var node_2 = first_child(fragment_1);
28265
28266					ColorPicker(node_2, {
28267						get color() {
28268							return selected_color();
28269						},
28270
28271						set color($$value) {
28272							selected_color($$value);
28273						},
28274
28275						get opacity() {
28276							return selected_opacity();
28277						},
28278
28279						set opacity($$value) {
28280							selected_opacity($$value);
28281						},
28282						$$legacy: true
28283					});
28284
28285					var node_3 = sibling(node_2, 2);
28286
28287					ColorField(node_3, {
28288						get color() {
28289							return selected_color();
28290						},
28291
28292						get current_mode() {
28293							return get(current_mode);
28294						},
28295
28296						set current_mode($$value) {
28297							set(current_mode, $$value);
28298						},
28299
28300						$$events: {
28301							close: () => set(color_picker, false),
28302							selected: ({ detail }) => handle_select(detail)
28303						},
28304						$$legacy: true
28305					});
28306
28307					append($$anchor, fragment_1);
28308				};
28309
28310				if_block(node_1, ($$render) => {
28311					if (get(color_picker)) $$render(consequent);
28312				});
28313			}
28314
28315			append($$anchor, fragment);
28316		};
28317
28318		if_block(node, ($$render) => {
28319			if (color_mode() === "defaults") $$render(consequent_1);
28320		});
28321	}
28322
28323	var node_4 = sibling(node, 2);
28324
28325	{
28326		var consequent_2 = ($$anchor) => {
28327			{
28328				let $0 = derived_safe_equal(() => color_mode() === "defaults" ? recent_colors() : null);
28329
28330				ColorSwatch($$anchor, {
28331					get colors() {
28332						return colors();
28333					},
28334
28335					get user_colors() {
28336						return get($0);
28337					},
28338
28339					get selected_color() {
28340						return selected_color();
28341					},
28342
28343					get current_mode() {
28344						return get(current_mode);
28345					},
28346
28347					get color_picker() {
28348						return get(color_picker);
28349					},
28350
28351					set color_picker($$value) {
28352						set(color_picker, $$value);
28353					},
28354
28355					$$events: {
28356						select: ({ detail }) => handle_color_selection(detail, "core"),
28357						edit: ({ detail }) => handle_color_selection(detail, "user"),
28358						add_color: handle_add_color
28359					},
28360					$$legacy: true
28361				});
28362			}
28363		};
28364
28365		if_block(node_4, ($$render) => {
28366			if (show_swatch()) $$render(consequent_2);
28367		});
28368	}
28369
28370	var node_5 = sibling(node_4, 2);
28371
28372	{
28373		var consequent_3 = ($$anchor) => {
28374			BrushSize_1($$anchor, {
28375				max: 100,
28376				min: 1,
28377				get selected_size() {
28378					return selected_size();
28379				},
28380
28381				set selected_size($$value) {
28382					selected_size($$value);
28383				},
28384				$$legacy: true
28385			});
28386		};
28387
28388		if_block(node_5, ($$render) => {
28389			if (show_size()) $$render(consequent_3);
28390		});
28391	}
28392
28393	reset(div);
28394	action(div, ($$node, $$action_arg) => click_outside?.($$node, $$action_arg), () => () => dispatch("click_outside"));
28395
28396	template_effect(() => classes = set_class(div, 1, 'wrap svelte-1l3f0ck', null, classes, {
28397		padded: !get(color_picker),
28398		color_picker: get(color_picker),
28399		size_picker: show_size() && mode() === "brush",
28400		eraser_picker: mode() === "eraser"
28401	}));
28402
28403	bind_window_size('innerHeight', ($$value) => set(height, $$value));
28404	bind_window_size('innerWidth', ($$value) => set(width, $$value));
28405	append($$anchor, div);
28406
28407	return pop($$exports);
28408}
28409
28410var root_5$2 = from_html(`<!> <!>`, 1);
28411var root_8$1 = from_html(`<!> <!> <!>`, 1);
28412var root_12 = from_html(`<!> <!> <!>`, 1);
28413var root$1 = from_html(`<div class="toolbar-wrap svelte-1uf24la"><div class="half-container svelte-1uf24la"><!> <!> <!></div> <div><!> <!> <!> <!></div></div>`);
28414
28415function Toolbar($$anchor, $$props) {
28416	push($$props, false);
28417
28418	const can_crop = mutable_source();
28419	const can_resize = mutable_source();
28420	const can_upload = mutable_source();
28421	const can_webcam = mutable_source();
28422	const can_paste = mutable_source();
28423	let tool = prop($$props, 'tool', 12, "image");
28424	let subtool = prop($$props, 'subtool', 12, null);
28425	let background = prop($$props, 'background', 12, false);
28426	let brush_options = prop($$props, 'brush_options', 12);
28427	let selected_size = prop($$props, 'selected_size', 28, () => brush_options() && typeof brush_options().default_size === "number" ? brush_options().default_size : 25);
28428	let eraser_options = prop($$props, 'eraser_options', 12);
28429	let selected_eraser_size = prop($$props, 'selected_eraser_size', 28, () => eraser_options() && typeof eraser_options().default_size === "number" ? eraser_options().default_size : 25);
28430
28431	// Handle default_color including potential color-opacity tuple
28432	let selected_color = prop($$props, 'selected_color', 28, () => brush_options() && (() => {
28433		const default_color = brush_options().default_color;
28434
28435		if (Array.isArray(default_color)) {
28436			return default_color[0];
28437		}
28438
28439		return default_color;
28440	})());
28441
28442	// Set default opacity based on default_color if it's a tuple
28443	let selected_opacity = prop($$props, 'selected_opacity', 28, () => brush_options() && (() => {
28444		const default_color = brush_options().default_color;
28445
28446		if (Array.isArray(default_color)) {
28447			return default_color[1];
28448		}
28449
28450		// Check if color string has opacity
28451		const color = tinycolor(default_color);
28452
28453		if (color.getAlpha() < 1) {
28454			return color.getAlpha();
28455		}
28456
28457		return 1;
28458	})());
28459
28460	let preview = prop($$props, 'preview', 12, false);
28461	let show_brush_color = prop($$props, 'show_brush_color', 12, false);
28462	let show_brush_size = prop($$props, 'show_brush_size', 12, false);
28463	let show_eraser_size = prop($$props, 'show_eraser_size', 12, false);
28464	let sources = prop($$props, 'sources', 12);
28465	let transforms = prop($$props, 'transforms', 12);
28466	let recent_colors = [];
28467	const dispatch = createEventDispatcher();
28468
28469	/**
28470	 * Handles tool click events
28471	 * @param {Tool} _tool - The selected tool
28472	 */
28473	function handle_tool_click(e, _tool) {
28474		e.stopPropagation();
28475		dispatch("tool_change", { tool: _tool });
28476	}
28477
28478	/**
28479	 * Handles subtool click events
28480	 * @param {Event} e - The click event
28481	 * @param {Subtool} _subtool - The selected subtool
28482	 */
28483	function handle_subtool_click(e, _subtool) {
28484		e.stopPropagation();
28485		dispatch("subtool_change", { tool: tool(), subtool: _subtool });
28486	}
28487
28488	legacy_pre_effect(() => (deep_read_state(tool()), deep_read_state(subtool())), () => {
28489		show_brush_size(tool() === "draw" && subtool() === "size");
28490	});
28491
28492	legacy_pre_effect(() => (deep_read_state(tool()), deep_read_state(subtool())), () => {
28493		show_brush_color(tool() === "draw" && subtool() === "color");
28494	});
28495
28496	legacy_pre_effect(() => (deep_read_state(tool()), deep_read_state(subtool())), () => {
28497		show_eraser_size(tool() === "erase" && subtool() === "size");
28498	});
28499
28500	legacy_pre_effect(() => (deep_read_state(transforms())), () => {
28501		set(can_crop, transforms().includes("crop"));
28502	});
28503
28504	legacy_pre_effect(() => (deep_read_state(transforms())), () => {
28505		set(can_resize, transforms().includes("resize"));
28506	});
28507
28508	legacy_pre_effect(() => (deep_read_state(sources())), () => {
28509		set(can_upload, sources().includes("upload"));
28510	});
28511
28512	legacy_pre_effect(() => (deep_read_state(sources())), () => {
28513		set(can_webcam, sources().includes("webcam"));
28514	});
28515
28516	legacy_pre_effect(() => (deep_read_state(sources())), () => {
28517		set(can_paste, sources().includes("clipboard"));
28518	});
28519
28520	legacy_pre_effect_reset();
28521
28522	var $$exports = {
28523		get tool() {
28524			return tool();
28525		},
28526
28527		set tool($$value) {
28528			tool($$value);
28529			flushSync();
28530		},
28531
28532		get subtool() {
28533			return subtool();
28534		},
28535
28536		set subtool($$value) {
28537			subtool($$value);
28538			flushSync();
28539		},
28540
28541		get background() {
28542			return background();
28543		},
28544
28545		set background($$value) {
28546			background($$value);
28547			flushSync();
28548		},
28549
28550		get brush_options() {
28551			return brush_options();
28552		},
28553
28554		set brush_options($$value) {
28555			brush_options($$value);
28556			flushSync();
28557		},
28558
28559		get selected_size() {
28560			return selected_size();
28561		},
28562
28563		set selected_size($$value) {
28564			selected_size($$value);
28565			flushSync();
28566		},
28567
28568		get eraser_options() {
28569			return eraser_options();
28570		},
28571
28572		set eraser_options($$value) {
28573			eraser_options($$value);
28574			flushSync();
28575		},
28576
28577		get selected_eraser_size() {
28578			return selected_eraser_size();
28579		},
28580
28581		set selected_eraser_size($$value) {
28582			selected_eraser_size($$value);
28583			flushSync();
28584		},
28585
28586		get selected_color() {
28587			return selected_color();
28588		},
28589
28590		set selected_color($$value) {
28591			selected_color($$value);
28592			flushSync();
28593		},
28594
28595		get selected_opacity() {
28596			return selected_opacity();
28597		},
28598
28599		set selected_opacity($$value) {
28600			selected_opacity($$value);
28601			flushSync();
28602		},
28603
28604		get preview() {
28605			return preview();
28606		},
28607
28608		set preview($$value) {
28609			preview($$value);
28610			flushSync();
28611		},
28612
28613		get show_brush_color() {
28614			return show_brush_color();
28615		},
28616
28617		set show_brush_color($$value) {
28618			show_brush_color($$value);
28619			flushSync();
28620		},
28621
28622		get show_brush_size() {
28623			return show_brush_size();
28624		},
28625
28626		set show_brush_size($$value) {
28627			show_brush_size($$value);
28628			flushSync();
28629		},
28630
28631		get show_eraser_size() {
28632			return show_eraser_size();
28633		},
28634
28635		set show_eraser_size($$value) {
28636			show_eraser_size($$value);
28637			flushSync();
28638		},
28639
28640		get sources() {
28641			return sources();
28642		},
28643
28644		set sources($$value) {
28645			sources($$value);
28646			flushSync();
28647		},
28648
28649		get transforms() {
28650			return transforms();
28651		},
28652
28653		set transforms($$value) {
28654			transforms($$value);
28655			flushSync();
28656		}
28657	};
28658
28659	init$1();
28660
28661	var div = root$1();
28662	var div_1 = child(div);
28663	var node = child(div_1);
28664
28665	{
28666		var consequent = ($$anchor) => {
28667			{
28668				let $0 = derived_safe_equal(() => tool() === "image");
28669
28670				IconButton($$anchor, {
28671					get Icon() {
28672						return Image$1;
28673					},
28674					label: 'Image',
28675					get highlight() {
28676						return get($0);
28677					},
28678					onclick: (e) => handle_tool_click(e, "image"),
28679					size: 'medium',
28680					padded: false,
28681					transparent: true
28682				});
28683			}
28684		};
28685
28686		if_block(node, ($$render) => {
28687			if ((
28688				deep_read_state(sources()),
28689				untrack(() => sources().length > 0)
28690			)) $$render(consequent);
28691		});
28692	}
28693
28694	var node_1 = sibling(node, 2);
28695
28696	{
28697		var consequent_1 = ($$anchor) => {
28698			{
28699				let $0 = derived_safe_equal(() => tool() === "draw");
28700
28701				IconButton($$anchor, {
28702					get Icon() {
28703						return Brush;
28704					},
28705					label: 'Brush',
28706					onclick: (e) => handle_tool_click(e, "draw"),
28707					get highlight() {
28708						return get($0);
28709					},
28710					size: 'medium',
28711					padded: false,
28712					transparent: true
28713				});
28714			}
28715		};
28716
28717		if_block(node_1, ($$render) => {
28718			if (brush_options()) $$render(consequent_1);
28719		});
28720	}
28721
28722	var node_2 = sibling(node_1, 2);
28723
28724	{
28725		var consequent_2 = ($$anchor) => {
28726			{
28727				let $0 = derived_safe_equal(() => tool() === "erase");
28728
28729				IconButton($$anchor, {
28730					get Icon() {
28731						return Erase;
28732					},
28733					label: 'Erase',
28734					onclick: (e) => handle_tool_click(e, "erase"),
28735					get highlight() {
28736						return get($0);
28737					},
28738					size: 'medium',
28739					padded: false,
28740					transparent: true
28741				});
28742			}
28743		};
28744
28745		if_block(node_2, ($$render) => {
28746			if (eraser_options()) $$render(consequent_2);
28747		});
28748	}
28749
28750	reset(div_1);
28751
28752	var div_2 = sibling(div_1, 2);
28753	let classes;
28754	var node_3 = child(div_2);
28755
28756	{
28757		var consequent_9 = ($$anchor) => {
28758			var fragment_3 = comment();
28759			var node_4 = first_child(fragment_3);
28760
28761			{
28762				var consequent_5 = ($$anchor) => {
28763					var fragment_4 = root_5$2();
28764					var node_5 = first_child(fragment_4);
28765
28766					{
28767						var consequent_3 = ($$anchor) => {
28768							{
28769								let $0 = derived_safe_equal(() => subtool() === "crop");
28770
28771								IconButton($$anchor, {
28772									get Icon() {
28773										return Crop;
28774									},
28775									label: 'Crop',
28776									onclick: (e) => handle_subtool_click(e, "crop"),
28777									get highlight() {
28778										return get($0);
28779									},
28780									size: 'medium',
28781									padded: false,
28782									transparent: true,
28783									offset: 0
28784								});
28785							}
28786						};
28787
28788						if_block(node_5, ($$render) => {
28789							if (get(can_crop)) $$render(consequent_3);
28790						});
28791					}
28792
28793					var node_6 = sibling(node_5, 2);
28794
28795					{
28796						var consequent_4 = ($$anchor) => {
28797							{
28798								let $0 = derived_safe_equal(() => subtool() === "size");
28799
28800								IconButton($$anchor, {
28801									get Icon() {
28802										return Resize;
28803									},
28804									label: 'Resize',
28805									onclick: (e) => handle_subtool_click(e, "size"),
28806									get highlight() {
28807										return get($0);
28808									},
28809									size: 'medium',
28810									padded: false,
28811									transparent: true,
28812									offset: 0
28813								});
28814							}
28815						};
28816
28817						if_block(node_6, ($$render) => {
28818							if (get(can_resize)) $$render(consequent_4);
28819						});
28820					}
28821
28822					append($$anchor, fragment_4);
28823				};
28824
28825				var alternate = ($$anchor) => {
28826					var fragment_7 = root_8$1();
28827					var node_7 = first_child(fragment_7);
28828
28829					{
28830						var consequent_6 = ($$anchor) => {
28831							{
28832								let $0 = derived_safe_equal(() => subtool() === "upload");
28833
28834								IconButton($$anchor, {
28835									get Icon() {
28836										return Upload;
28837									},
28838									label: 'Upload',
28839									onclick: (e) => handle_subtool_click(e, "upload"),
28840									get highlight() {
28841										return get($0);
28842									},
28843									size: 'medium',
28844									padded: false,
28845									transparent: true,
28846									offset: 0
28847								});
28848							}
28849						};
28850
28851						if_block(node_7, ($$render) => {
28852							if (get(can_upload)) $$render(consequent_6);
28853						});
28854					}
28855
28856					var node_8 = sibling(node_7, 2);
28857
28858					{
28859						var consequent_7 = ($$anchor) => {
28860							{
28861								let $0 = derived_safe_equal(() => subtool() === "paste");
28862
28863								IconButton($$anchor, {
28864									get Icon() {
28865										return ImagePaste;
28866									},
28867									label: 'Paste',
28868									onclick: (e) => handle_subtool_click(e, "paste"),
28869									get highlight() {
28870										return get($0);
28871									},
28872									size: 'large',
28873									padded: false,
28874									transparent: true,
28875									offset: 0
28876								});
28877							}
28878						};
28879
28880						if_block(node_8, ($$render) => {
28881							if (get(can_paste)) $$render(consequent_7);
28882						});
28883					}
28884
28885					var node_9 = sibling(node_8, 2);
28886
28887					{
28888						var consequent_8 = ($$anchor) => {
28889							{
28890								let $0 = derived_safe_equal(() => subtool() === "webcam");
28891
28892								IconButton($$anchor, {
28893									get Icon() {
28894										return Webcam;
28895									},
28896									label: 'Webcam',
28897									onclick: (e) => handle_subtool_click(e, "webcam"),
28898									get highlight() {
28899										return get($0);
28900									},
28901									size: 'medium',
28902									padded: false,
28903									transparent: true,
28904									offset: 0
28905								});
28906							}
28907						};
28908
28909						if_block(node_9, ($$render) => {
28910							if (get(can_webcam)) $$render(consequent_8);
28911						});
28912					}
28913
28914					append($$anchor, fragment_7);
28915				};
28916
28917				if_block(node_4, ($$render) => {
28918					if (background()) $$render(consequent_5); else $$render(alternate, false);
28919				});
28920			}
28921
28922			append($$anchor, fragment_3);
28923		};
28924
28925		if_block(node_3, ($$render) => {
28926			if (tool() === "image") $$render(consequent_9);
28927		});
28928	}
28929
28930	var node_10 = sibling(node_3, 2);
28931
28932	{
28933		var consequent_11 = ($$anchor) => {
28934			var fragment_11 = root_12();
28935			var node_11 = first_child(fragment_11);
28936
28937			IconButton(node_11, {
28938				get Icon() {
28939					return ColorPickerSolid;
28940				},
28941				label: 'Color',
28942				get color() {
28943					return selected_color();
28944				},
28945				onclick: (e) => handle_subtool_click(e, "color"),
28946				size: 'medium',
28947				padded: false,
28948				transparent: true,
28949				offset: 0
28950			});
28951
28952			var node_12 = sibling(node_11, 2);
28953
28954			{
28955				let $0 = derived_safe_equal(() => subtool() === "size");
28956
28957				IconButton(node_12, {
28958					get Icon() {
28959						return Circle$1;
28960					},
28961					label: 'Brush Size',
28962					onclick: (e) => handle_subtool_click(e, "size"),
28963					get highlight() {
28964						return get($0);
28965					},
28966					size: 'medium',
28967					padded: false,
28968					transparent: true,
28969					offset: 0
28970				});
28971			}
28972
28973			var node_13 = sibling(node_12, 2);
28974
28975			{
28976				var consequent_10 = ($$anchor) => {
28977					BrushOptions($$anchor, {
28978						get colors() {
28979							return (
28980								deep_read_state(brush_options()),
28981								untrack(() => brush_options().colors)
28982							);
28983						},
28984
28985						get color_mode() {
28986							return (
28987								deep_read_state(brush_options()),
28988								untrack(() => brush_options().color_mode)
28989							);
28990						},
28991
28992						get recent_colors() {
28993							return recent_colors;
28994						},
28995
28996						get show_swatch() {
28997							return show_brush_color();
28998						},
28999
29000						get show_size() {
29001							return show_brush_size();
29002						},
29003						mode: 'brush',
29004						get selected_size() {
29005							return selected_size();
29006						},
29007
29008						set selected_size($$value) {
29009							selected_size($$value);
29010						},
29011
29012						get selected_color() {
29013							return selected_color();
29014						},
29015
29016						set selected_color($$value) {
29017							selected_color($$value);
29018						},
29019
29020						get selected_opacity() {
29021							return selected_opacity();
29022						},
29023
29024						set selected_opacity($$value) {
29025							selected_opacity($$value);
29026						},
29027
29028						get preview() {
29029							return preview();
29030						},
29031
29032						set preview($$value) {
29033							preview($$value);
29034						},
29035
29036						$$events: {
29037							click_outside: (e) => {
29038								e.stopPropagation();
29039								preview(false);
29040								show_brush_color(false);
29041								show_brush_size(false);
29042								handle_subtool_click(e, null);
29043							}
29044						},
29045						$$legacy: true
29046					});
29047				};
29048
29049				if_block(node_13, ($$render) => {
29050					if (show_brush_color() || show_brush_size()) $$render(consequent_10);
29051				});
29052			}
29053
29054			append($$anchor, fragment_11);
29055		};
29056
29057		if_block(node_10, ($$render) => {
29058			if (tool() === "draw" && brush_options()) $$render(consequent_11);
29059		});
29060	}
29061
29062	var node_14 = sibling(node_10, 2);
29063
29064	{
29065		var consequent_12 = ($$anchor) => {
29066			{
29067				let $0 = derived_safe_equal(() => subtool() === "size");
29068
29069				IconButton($$anchor, {
29070					get Icon() {
29071						return Circle$1;
29072					},
29073					label: 'Eraser Size',
29074					onclick: (e) => handle_subtool_click(e, "size"),
29075					get highlight() {
29076						return get($0);
29077					},
29078					size: 'medium',
29079					padded: false,
29080					transparent: true,
29081					offset: 0
29082				});
29083			}
29084		};
29085
29086		if_block(node_14, ($$render) => {
29087			if (tool() === "erase" && eraser_options()) $$render(consequent_12);
29088		});
29089	}
29090
29091	var node_15 = sibling(node_14, 2);
29092
29093	{
29094		var consequent_13 = ($$anchor) => {
29095			BrushOptions($$anchor, {
29096				colors: [],
29097				show_swatch: false,
29098				show_size: true,
29099				mode: 'eraser',
29100				get selected_size() {
29101					return selected_eraser_size();
29102				},
29103
29104				set selected_size($$value) {
29105					selected_eraser_size($$value);
29106				},
29107
29108				get selected_color() {
29109					return selected_color();
29110				},
29111
29112				set selected_color($$value) {
29113					selected_color($$value);
29114				},
29115
29116				get selected_opacity() {
29117					return selected_opacity();
29118				},
29119
29120				set selected_opacity($$value) {
29121					selected_opacity($$value);
29122				},
29123
29124				get preview() {
29125					return preview();
29126				},
29127
29128				set preview($$value) {
29129					preview($$value);
29130				},
29131
29132				$$events: {
29133					click_outside: (e) => {
29134						e.stopPropagation();
29135						preview(false);
29136						show_eraser_size(false);
29137						handle_subtool_click(e, null);
29138					}
29139				},
29140				$$legacy: true
29141			});
29142		};
29143
29144		if_block(node_15, ($$render) => {
29145			if (show_eraser_size()) $$render(consequent_13);
29146		});
29147	}
29148
29149	reset(div_2);
29150	reset(div);
29151
29152	template_effect(() => classes = set_class(div_2, 1, 'half-container right svelte-1uf24la', null, classes, {
29153		hide: tool() === "pan" || tool() === "image" && !background() && sources().length === 0 || tool() === "image" && background() && transforms().length === 0
29154	}));
29155
29156	append($$anchor, div);
29157
29158	return pop($$exports);
29159}
29160
29161var vertex = "in vec2 aPosition;\nout vec2 vTextureCoord;\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\n\nvec4 filterVertexPosition( void )\n{\n    vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n    \n    position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n    position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n    return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n    return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvoid main(void)\n{\n    gl_Position = filterVertexPosition();\n    vTextureCoord = filterTextureCoord();\n}\n";
29162
29163var wgslVertex = "struct GlobalFilterUniforms {\n  uInputSize:vec4<f32>,\n  uInputPixel:vec4<f32>,\n  uInputClamp:vec4<f32>,\n  uOutputFrame:vec4<f32>,\n  uGlobalFrame:vec4<f32>,\n  uOutputTexture:vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;\n\nstruct VSOutput {\n    @builtin(position) position: vec4<f32>,\n    @location(0) uv : vec2<f32>\n  };\n\nfn filterVertexPosition(aPosition:vec2<f32>) -> vec4<f32>\n{\n    var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n    position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n    position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n    return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2<f32> ) -> vec2<f32>\n{\n    return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2<f32> ) -> vec2<f32>\n{\n  return  (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw);  \n}\n\nfn getSize() -> vec2<f32>\n{\n  return gfu.uGlobalFrame.zw;\n}\n  \n@vertex\nfn mainVertex(\n  @location(0) aPosition : vec2<f32>, \n) -> VSOutput {\n  return VSOutput(\n   filterVertexPosition(aPosition),\n   filterTextureCoord(aPosition)\n  );\n}";
29164
29165var fragment$1 = "\nin vec2 vTextureCoord;\nout vec4 finalColor;\n\nuniform sampler2D uTexture;\nuniform vec2 uOffset;\n\nvoid main(void)\n{\n    vec4 color = vec4(0.0);\n\n    // Sample top left pixel\n    color += texture(uTexture, vec2(vTextureCoord.x - uOffset.x, vTextureCoord.y + uOffset.y));\n\n    // Sample top right pixel\n    color += texture(uTexture, vec2(vTextureCoord.x + uOffset.x, vTextureCoord.y + uOffset.y));\n\n    // Sample bottom right pixel\n    color += texture(uTexture, vec2(vTextureCoord.x + uOffset.x, vTextureCoord.y - uOffset.y));\n\n    // Sample bottom left pixel\n    color += texture(uTexture, vec2(vTextureCoord.x - uOffset.x, vTextureCoord.y - uOffset.y));\n\n    // Average\n    color *= 0.25;\n\n    finalColor = color;\n}";
29166
29167var source$1 = "struct KawaseBlurUniforms {\n  uOffset:vec2<f32>,\n};\n\n@group(0) @binding(1) var uTexture: texture_2d<f32>; \n@group(0) @binding(2) var uSampler: sampler;\n@group(1) @binding(0) var<uniform> kawaseBlurUniforms : KawaseBlurUniforms;\n\n@fragment\nfn mainFragment(\n  @builtin(position) position: vec4<f32>,\n  @location(0) uv : vec2<f32>\n) -> @location(0) vec4<f32> {\n  let uOffset = kawaseBlurUniforms.uOffset;\n  var color: vec4<f32> = vec4<f32>(0.0);\n\n  // Sample top left pixel\n  color += textureSample(uTexture, uSampler, vec2<f32>(uv.x - uOffset.x, uv.y + uOffset.y));\n  // Sample top right pixel\n  color += textureSample(uTexture, uSampler, vec2<f32>(uv.x + uOffset.x, uv.y + uOffset.y));\n  // Sample bottom right pixel\n  color += textureSample(uTexture, uSampler, vec2<f32>(uv.x + uOffset.x, uv.y - uOffset.y));\n  // Sample bottom left pixel\n  color += textureSample(uTexture, uSampler, vec2<f32>(uv.x - uOffset.x, uv.y - uOffset.y));\n  // Average\n  color *= 0.25;\n\n  return color;\n}";
29168
29169var fragmentClamp = "\nprecision highp float;\nin vec2 vTextureCoord;\nout vec4 finalColor;\n\nuniform sampler2D uTexture;\nuniform vec2 uOffset;\n\nuniform vec4 uInputClamp;\n\nvoid main(void)\n{\n    vec4 color = vec4(0.0);\n\n    // Sample top left pixel\n    color += texture(uTexture, clamp(vec2(vTextureCoord.x - uOffset.x, vTextureCoord.y + uOffset.y), uInputClamp.xy, uInputClamp.zw));\n\n    // Sample top right pixel\n    color += texture(uTexture, clamp(vec2(vTextureCoord.x + uOffset.x, vTextureCoord.y + uOffset.y), uInputClamp.xy, uInputClamp.zw));\n\n    // Sample bottom right pixel\n    color += texture(uTexture, clamp(vec2(vTextureCoord.x + uOffset.x, vTextureCoord.y - uOffset.y), uInputClamp.xy, uInputClamp.zw));\n\n    // Sample bottom left pixel\n    color += texture(uTexture, clamp(vec2(vTextureCoord.x - uOffset.x, vTextureCoord.y - uOffset.y), uInputClamp.xy, uInputClamp.zw));\n\n    // Average\n    color *= 0.25;\n\n    finalColor = color;\n}\n";
29170
29171var sourceClamp = "struct KawaseBlurUniforms {\n  uOffset:vec2<f32>,\n};\n\nstruct GlobalFilterUniforms {\n  uInputSize:vec4<f32>,\n  uInputPixel:vec4<f32>,\n  uInputClamp:vec4<f32>,\n  uOutputFrame:vec4<f32>,\n  uGlobalFrame:vec4<f32>,\n  uOutputTexture:vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;\n\n@group(0) @binding(1) var uTexture: texture_2d<f32>; \n@group(0) @binding(2) var uSampler: sampler;\n@group(1) @binding(0) var<uniform> kawaseBlurUniforms : KawaseBlurUniforms;\n\n@fragment\nfn mainFragment(\n  @builtin(position) position: vec4<f32>,\n  @location(0) uv : vec2<f32>\n) -> @location(0) vec4<f32> {\n  let uOffset = kawaseBlurUniforms.uOffset;\n  var color: vec4<f32> = vec4(0.0);\n\n  // Sample top left pixel\n  color += textureSample(uTexture, uSampler, clamp(vec2<f32>(uv.x - uOffset.x, uv.y + uOffset.y), gfu.uInputClamp.xy, gfu.uInputClamp.zw));\n  // Sample top right pixel\n  color += textureSample(uTexture, uSampler, clamp(vec2<f32>(uv.x + uOffset.x, uv.y + uOffset.y), gfu.uInputClamp.xy, gfu.uInputClamp.zw));\n  // Sample bottom right pixel\n  color += textureSample(uTexture, uSampler, clamp(vec2<f32>(uv.x + uOffset.x, uv.y - uOffset.y), gfu.uInputClamp.xy, gfu.uInputClamp.zw));\n  // Sample bottom left pixel\n  color += textureSample(uTexture, uSampler, clamp(vec2<f32>(uv.x - uOffset.x, uv.y - uOffset.y), gfu.uInputClamp.xy, gfu.uInputClamp.zw));\n  // Average\n  color *= 0.25;\n    \n  return color;\n}";
29172
29173var __defProp$1 = Object.defineProperty;
29174var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
29175var __publicField$1 = (obj, key, value) => {
29176  __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
29177  return value;
29178};
29179const _KawaseBlurFilter = class _KawaseBlurFilter extends Filter {
29180  /** @ignore */
29181  constructor(...args) {
29182    let options = args[0] ?? {};
29183    if (typeof options === "number" || Array.isArray(options)) {
29184      deprecation("6.0.0", "KawaseBlurFilter constructor params are now options object. See params: { strength, quality, clamp, pixelSize }");
29185      options = { strength: options };
29186      if (args[1] !== void 0)
29187        options.quality = args[1];
29188      if (args[2] !== void 0)
29189        options.clamp = args[2];
29190    }
29191    options = { ..._KawaseBlurFilter.DEFAULT_OPTIONS, ...options };
29192    const gpuProgram = GpuProgram.from({
29193      vertex: {
29194        source: wgslVertex,
29195        entryPoint: "mainVertex"
29196      },
29197      fragment: {
29198        source: options?.clamp ? sourceClamp : source$1,
29199        entryPoint: "mainFragment"
29200      }
29201    });
29202    const glProgram = GlProgram.from({
29203      vertex,
29204      fragment: options?.clamp ? fragmentClamp : fragment$1,
29205      name: "kawase-blur-filter"
29206    });
29207    super({
29208      gpuProgram,
29209      glProgram,
29210      resources: {
29211        kawaseBlurUniforms: {
29212          uOffset: { value: new Float32Array(2), type: "vec2<f32>" }
29213        }
29214      }
29215    });
29216    __publicField$1(this, "uniforms");
29217    __publicField$1(this, "_pixelSize", { x: 0, y: 0 });
29218    __publicField$1(this, "_clamp");
29219    __publicField$1(this, "_kernels", []);
29220    __publicField$1(this, "_blur");
29221    __publicField$1(this, "_quality");
29222    this.uniforms = this.resources.kawaseBlurUniforms.uniforms;
29223    this.pixelSize = options.pixelSize ?? { x: 1, y: 1 };
29224    if (Array.isArray(options.strength)) {
29225      this.kernels = options.strength;
29226    } else if (typeof options.strength === "number") {
29227      this._blur = options.strength;
29228      this.quality = options.quality ?? 3;
29229    }
29230    this._clamp = !!options.clamp;
29231  }
29232  /**
29233   * Override existing apply method in `Filter`
29234   * @override
29235   * @ignore
29236   */
29237  apply(filterManager, input, output, clearMode) {
29238    const uvX = this.pixelSizeX / input.source.width;
29239    const uvY = this.pixelSizeY / input.source.height;
29240    let offset;
29241    if (this._quality === 1 || this._blur === 0) {
29242      offset = this._kernels[0] + 0.5;
29243      this.uniforms.uOffset[0] = offset * uvX;
29244      this.uniforms.uOffset[1] = offset * uvY;
29245      filterManager.applyFilter(this, input, output, clearMode);
29246    } else {
29247      const renderTarget = TexturePool.getSameSizeTexture(input);
29248      let source2 = input;
29249      let target = renderTarget;
29250      let tmp;
29251      const last = this._quality - 1;
29252      for (let i = 0; i < last; i++) {
29253        offset = this._kernels[i] + 0.5;
29254        this.uniforms.uOffset[0] = offset * uvX;
29255        this.uniforms.uOffset[1] = offset * uvY;
29256        filterManager.applyFilter(this, source2, target, true);
29257        tmp = source2;
29258        source2 = target;
29259        target = tmp;
29260      }
29261      offset = this._kernels[last] + 0.5;
29262      this.uniforms.uOffset[0] = offset * uvX;
29263      this.uniforms.uOffset[1] = offset * uvY;
29264      filterManager.applyFilter(this, source2, output, clearMode);
29265      TexturePool.returnTexture(renderTarget);
29266    }
29267  }
29268  /**
29269    * The amount of blur, value greater than `0`.
29270    * @default 4
29271    */
29272  get strength() {
29273    return this._blur;
29274  }
29275  set strength(value) {
29276    this._blur = value;
29277    this._generateKernels();
29278  }
29279  /**
29280    * The quality of the filter, integer greater than `1`.
29281    * @default 3
29282    */
29283  get quality() {
29284    return this._quality;
29285  }
29286  set quality(value) {
29287    this._quality = Math.max(1, Math.round(value));
29288    this._generateKernels();
29289  }
29290  /**
29291    * The kernel size of the blur filter, for advanced usage
29292    * @default [0]
29293    */
29294  get kernels() {
29295    return this._kernels;
29296  }
29297  set kernels(value) {
29298    if (Array.isArray(value) && value.length > 0) {
29299      this._kernels = value;
29300      this._quality = value.length;
29301      this._blur = Math.max(...value);
29302    } else {
29303      this._kernels = [0];
29304      this._quality = 1;
29305    }
29306  }
29307  /**
29308    * The size of the pixels. Large size is blurrier. For advanced usage.
29309    * @default {x:1,y:1}
29310    */
29311  get pixelSize() {
29312    return this._pixelSize;
29313  }
29314  set pixelSize(value) {
29315    if (typeof value === "number") {
29316      this.pixelSizeX = this.pixelSizeY = value;
29317      return;
29318    }
29319    if (Array.isArray(value)) {
29320      this.pixelSizeX = value[0];
29321      this.pixelSizeY = value[1];
29322      return;
29323    }
29324    this._pixelSize = value;
29325  }
29326  /**
29327    * The size of the pixels on the `x` axis. Large size is blurrier. For advanced usage.
29328    * @default 1
29329    */
29330  get pixelSizeX() {
29331    return this.pixelSize.x;
29332  }
29333  set pixelSizeX(value) {
29334    this.pixelSize.x = value;
29335  }
29336  /**
29337    * The size of the pixels on the `y` axis. Large size is blurrier. For advanced usage.
29338    * @default 1
29339    */
29340  get pixelSizeY() {
29341    return this.pixelSize.y;
29342  }
29343  set pixelSizeY(value) {
29344    this.pixelSize.y = value;
29345  }
29346  /**
29347    * Get the if the filter is clamped
29348    * @default false
29349    */
29350  get clamp() {
29351    return this._clamp;
29352  }
29353  /** Update padding based on kernel data */
29354  _updatePadding() {
29355    this.padding = Math.ceil(this._kernels.reduce((acc, v) => acc + v + 0.5, 0));
29356  }
29357  /** Auto generate kernels by blur & quality */
29358  _generateKernels() {
29359    const blur = this._blur;
29360    const quality = this._quality;
29361    const kernels = [blur];
29362    if (blur > 0) {
29363      let k = blur;
29364      const step = blur / quality;
29365      for (let i = 1; i < quality; i++) {
29366        k -= step;
29367        kernels.push(k);
29368      }
29369    }
29370    this._kernels = kernels;
29371    this._updatePadding();
29372  }
29373};
29374/** Default values for options. */
29375__publicField$1(_KawaseBlurFilter, "DEFAULT_OPTIONS", {
29376  strength: 4,
29377  quality: 3,
29378  clamp: false,
29379  pixelSize: { x: 1, y: 1 }
29380});
29381let KawaseBlurFilter = _KawaseBlurFilter;
29382
29383var fragment = "precision highp float;\nin vec2 vTextureCoord;\nout vec4 finalColor;\n\nuniform sampler2D uTexture;\nuniform float uAlpha;\nuniform vec3 uColor;\nuniform vec2 uOffset;\n\nuniform vec4 uInputSize;\n\nvoid main(void){\n    vec4 sample = texture(uTexture, vTextureCoord - uOffset * uInputSize.zw);\n\n    // Premultiply alpha\n    sample.rgb = uColor.rgb * sample.a;\n\n    // alpha user alpha\n    sample *= uAlpha;\n\n    finalColor = sample;\n}";
29384
29385var source = "struct DropShadowUniforms {\n  uAlpha: f32,\n  uColor: vec3<f32>,\n  uOffset: vec2<f32>,\n};\n\nstruct GlobalFilterUniforms {\n  uInputSize:vec4<f32>,\n  uInputPixel:vec4<f32>,\n  uInputClamp:vec4<f32>,\n  uOutputFrame:vec4<f32>,\n  uGlobalFrame:vec4<f32>,\n  uOutputTexture:vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;\n\n@group(0) @binding(1) var uTexture: texture_2d<f32>; \n@group(0) @binding(2) var uSampler: sampler;\n@group(1) @binding(0) var<uniform> dropShadowUniforms : DropShadowUniforms;\n\n@fragment\nfn mainFragment(\n  @builtin(position) position: vec4<f32>,\n  @location(0) uv : vec2<f32>\n) -> @location(0) vec4<f32> {\n  var color: vec4<f32> = textureSample(uTexture, uSampler, uv - dropShadowUniforms.uOffset * gfu.uInputSize.zw);\n\n  // Premultiply alpha\n  color = vec4<f32>(vec3<f32>(dropShadowUniforms.uColor.rgb * color.a), color.a);\n  // alpha user alpha\n  color *= dropShadowUniforms.uAlpha;\n\n  return color;\n}";
29386
29387var __defProp = Object.defineProperty;
29388var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
29389var __publicField = (obj, key, value) => {
29390  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
29391  return value;
29392};
29393const _DropShadowFilter = class _DropShadowFilter extends Filter {
29394  /**
29395   * @param options - Options for the DropShadowFilter constructor.
29396   */
29397  constructor(options) {
29398    options = { ..._DropShadowFilter.DEFAULT_OPTIONS, ...options };
29399    const gpuProgram = GpuProgram.from({
29400      vertex: {
29401        source: wgslVertex,
29402        entryPoint: "mainVertex"
29403      },
29404      fragment: {
29405        source,
29406        entryPoint: "mainFragment"
29407      }
29408    });
29409    const glProgram = GlProgram.from({
29410      vertex,
29411      fragment,
29412      name: "drop-shadow-filter"
29413    });
29414    super({
29415      gpuProgram,
29416      glProgram,
29417      resources: {
29418        dropShadowUniforms: {
29419          uAlpha: { value: options.alpha, type: "f32" },
29420          uColor: { value: new Float32Array(3), type: "vec3<f32>" },
29421          uOffset: { value: options.offset, type: "vec2<f32>" }
29422        }
29423      },
29424      resolution: options.resolution
29425    });
29426    __publicField(this, "uniforms");
29427    /**
29428     * Hide the contents, only show the shadow.
29429     * @default false
29430     */
29431    __publicField(this, "shadowOnly", false);
29432    __publicField(this, "_color");
29433    __publicField(this, "_blurFilter");
29434    __publicField(this, "_basePass");
29435    this.uniforms = this.resources.dropShadowUniforms.uniforms;
29436    this._color = new Color();
29437    this.color = options.color ?? 0;
29438    this._blurFilter = new KawaseBlurFilter({
29439      strength: options.kernels ?? options.blur,
29440      quality: options.kernels ? void 0 : options.quality
29441    });
29442    this._basePass = new Filter({
29443      gpuProgram: GpuProgram.from({
29444        vertex: {
29445          source: wgslVertex,
29446          entryPoint: "mainVertex"
29447        },
29448        fragment: {
29449          source: `
29450                    @group(0) @binding(1) var uTexture: texture_2d<f32>; 
29451                    @group(0) @binding(2) var uSampler: sampler;
29452                    @fragment
29453                    fn mainFragment(
29454                        @builtin(position) position: vec4<f32>,
29455                        @location(0) uv : vec2<f32>
29456                    ) -> @location(0) vec4<f32> {
29457                        return textureSample(uTexture, uSampler, uv);
29458                    }
29459                    `,
29460          entryPoint: "mainFragment"
29461        }
29462      }),
29463      glProgram: GlProgram.from({
29464        vertex,
29465        fragment: `
29466                in vec2 vTextureCoord;
29467                out vec4 finalColor;
29468                uniform sampler2D uTexture;
29469
29470                void main(void){
29471                    finalColor = texture(uTexture, vTextureCoord);
29472                }
29473                `,
29474        name: "drop-shadow-filter"
29475      }),
29476      resources: {}
29477    });
29478    Object.assign(this, options);
29479  }
29480  /**
29481   * Override existing apply method in `Filter`
29482   * @override
29483   * @ignore
29484   */
29485  apply(filterManager, input, output, clearMode) {
29486    const renderTarget = TexturePool.getSameSizeTexture(input);
29487    filterManager.applyFilter(this, input, renderTarget, true);
29488    this._blurFilter.apply(filterManager, renderTarget, output, clearMode);
29489    if (!this.shadowOnly) {
29490      filterManager.applyFilter(this._basePass, input, output, false);
29491    }
29492    TexturePool.returnTexture(renderTarget);
29493  }
29494  /**
29495   * Set the offset position of the drop-shadow relative to the original image.
29496   * @default [4,4]
29497   */
29498  get offset() {
29499    return this.uniforms.uOffset;
29500  }
29501  set offset(value) {
29502    this.uniforms.uOffset = value;
29503    this._updatePadding();
29504  }
29505  /**
29506   * Set the offset position of the drop-shadow relative to the original image on the `x` axis
29507   * @default 4
29508   */
29509  get offsetX() {
29510    return this.offset.x;
29511  }
29512  set offsetX(value) {
29513    this.offset.x = value;
29514    this._updatePadding();
29515  }
29516  /**
29517   * Set the offset position of the drop-shadow relative to the original image on the `y` axis
29518   * @default 4
29519   */
29520  get offsetY() {
29521    return this.offset.y;
29522  }
29523  set offsetY(value) {
29524    this.offset.y = value;
29525    this._updatePadding();
29526  }
29527  /**
29528   * The color value of shadow.
29529   * @example [0.0, 0.0, 0.0] = 0x000000
29530   * @default 0x000000
29531   */
29532  get color() {
29533    return this._color.value;
29534  }
29535  set color(value) {
29536    this._color.setValue(value);
29537    const [r, g, b] = this._color.toArray();
29538    this.uniforms.uColor[0] = r;
29539    this.uniforms.uColor[1] = g;
29540    this.uniforms.uColor[2] = b;
29541  }
29542  /**
29543   * Coefficient for alpha multiplication
29544   * @default 1
29545   */
29546  get alpha() {
29547    return this.uniforms.uAlpha;
29548  }
29549  set alpha(value) {
29550    this.uniforms.uAlpha = value;
29551  }
29552  /**
29553   * The strength of the shadow's blur.
29554   * @default 2
29555   */
29556  get blur() {
29557    return this._blurFilter.strength;
29558  }
29559  set blur(value) {
29560    this._blurFilter.strength = value;
29561    this._updatePadding();
29562  }
29563  /**
29564   * Sets the quality of the Blur Filter
29565   * @default 4
29566   */
29567  get quality() {
29568    return this._blurFilter.quality;
29569  }
29570  set quality(value) {
29571    this._blurFilter.quality = value;
29572    this._updatePadding();
29573  }
29574  /** Sets the kernels of the Blur Filter */
29575  get kernels() {
29576    return this._blurFilter.kernels;
29577  }
29578  set kernels(value) {
29579    this._blurFilter.kernels = value;
29580  }
29581  /**
29582   * Sets the pixelSize of the Kawase Blur filter
29583   * @default [1,1]
29584   */
29585  get pixelSize() {
29586    return this._blurFilter.pixelSize;
29587  }
29588  set pixelSize(value) {
29589    if (typeof value === "number") {
29590      value = { x: value, y: value };
29591    }
29592    if (Array.isArray(value)) {
29593      value = { x: value[0], y: value[1] };
29594    }
29595    this._blurFilter.pixelSize = value;
29596  }
29597  /**
29598   * Sets the pixelSize of the Kawase Blur filter on the `x` axis
29599   * @default 1
29600   */
29601  get pixelSizeX() {
29602    return this._blurFilter.pixelSizeX;
29603  }
29604  set pixelSizeX(value) {
29605    this._blurFilter.pixelSizeX = value;
29606  }
29607  /**
29608   * Sets the pixelSize of the Kawase Blur filter on the `y` axis
29609   * @default 1
29610   */
29611  get pixelSizeY() {
29612    return this._blurFilter.pixelSizeY;
29613  }
29614  set pixelSizeY(value) {
29615    this._blurFilter.pixelSizeY = value;
29616  }
29617  /**
29618   * Recalculate the proper padding amount.
29619   * @private
29620   */
29621  _updatePadding() {
29622    const offsetPadding = Math.max(
29623      Math.abs(this.offsetX),
29624      Math.abs(this.offsetY)
29625    );
29626    this.padding = offsetPadding + this.blur * 2 + this.quality * 4;
29627  }
29628};
29629/** Default values for options. */
29630__publicField(_DropShadowFilter, "DEFAULT_OPTIONS", {
29631  offset: { x: 4, y: 4 },
29632  color: 0,
29633  alpha: 0.5,
29634  shadowOnly: false,
29635  kernels: void 0,
29636  blur: 2,
29637  quality: 3,
29638  pixelSize: { x: 1, y: 1 },
29639  resolution: 1
29640});
29641let DropShadowFilter = _DropShadowFilter;
29642
29643class ImageTool {
29644  name = "image";
29645  context;
29646  current_tool;
29647  current_subtool;
29648  async setup(context, tool, subtool) {
29649    this.context = context;
29650    this.current_tool = tool;
29651    this.current_subtool = subtool;
29652  }
29653  cleanup() {
29654  }
29655  async add_image({
29656    image,
29657    fixed_canvas,
29658    border_region = 0
29659  }) {
29660    const image_command = new AddImageCommand(
29661      this.context,
29662      image,
29663      fixed_canvas,
29664      border_region
29665    );
29666    await this.context.execute_command(image_command);
29667  }
29668  set_tool(tool, subtool) {
29669    this.current_tool = tool;
29670    this.current_subtool = subtool;
29671  }
29672}
29673class AddImageCommand {
29674  sprite = null;
29675  fixed_canvas;
29676  context;
29677  background;
29678  current_canvas_size;
29679  computed_dimensions;
29680  current_scale;
29681  current_position;
29682  border_region;
29683  scaled_width;
29684  scaled_height;
29685  previous_image = null;
29686  name;
29687  constructor(context, background, fixed_canvas, border_region = 0) {
29688    this.name = "AddImage";
29689    this.context = context;
29690    this.background = background;
29691    this.fixed_canvas = fixed_canvas;
29692    this.border_region = border_region;
29693    this.current_canvas_size = get$1(this.context.dimensions);
29694    this.current_scale = get$1(this.context.scale);
29695    this.current_position = get$1(this.context.position);
29696    this.computed_dimensions = { width: 0, height: 0 };
29697    if (this.context.background_image && this.context.background_image.texture) {
29698      const bg = this.context.background_image;
29699      const stored_border_region = bg.borderRegion || 0;
29700      this.previous_image = {
29701        texture: this.clone_texture(bg.texture),
29702        width: bg.width,
29703        height: bg.height,
29704        x: bg.position.x,
29705        y: bg.position.y,
29706        border_region: stored_border_region
29707      };
29708    }
29709  }
29710  /**
29711   * Creates a copy of a texture to avoid reference issues
29712   */
29713  clone_texture(texture) {
29714    const render_texture = RenderTexture.create({
29715      width: texture.width,
29716      height: texture.height,
29717      resolution: window.devicePixelRatio || 1
29718    });
29719    const sprite = new Sprite(texture);
29720    const container = new Container();
29721    container.addChild(sprite);
29722    this.context.app.renderer.render(container, {
29723      renderTexture: render_texture
29724    });
29725    container.destroy({ children: true });
29726    return render_texture;
29727  }
29728  async start() {
29729    let image_texture;
29730    if (this.background instanceof Texture) {
29731      image_texture = this.background;
29732    } else {
29733      const img = await createImageBitmap(this.background);
29734      image_texture = Texture.from(img);
29735    }
29736    this.sprite = new Sprite(image_texture);
29737    const [width, height] = this.handle_image();
29738    this.computed_dimensions = { width, height };
29739  }
29740  handle_image() {
29741    if (this.sprite === null) {
29742      return [0, 0];
29743    }
29744    if (this.fixed_canvas) {
29745      const effectiveCanvasWidth = Math.max(
29746        this.current_canvas_size.width - this.border_region * 2,
29747        10
29748      );
29749      const effectiveCanvasHeight = Math.max(
29750        this.current_canvas_size.height - this.border_region * 2,
29751        10
29752      );
29753      const { width, height, x, y } = fit_image_to_canvas(
29754        this.sprite.width,
29755        this.sprite.height,
29756        effectiveCanvasWidth,
29757        effectiveCanvasHeight
29758      );
29759      this.sprite.width = width;
29760      this.sprite.height = height;
29761      this.sprite.x = x + this.border_region;
29762      this.sprite.y = y + this.border_region;
29763    } else {
29764      const width = this.sprite.width;
29765      const height = this.sprite.height;
29766      this.sprite.x = this.border_region;
29767      this.sprite.y = this.border_region;
29768      return [width + this.border_region * 2, height + this.border_region * 2];
29769    }
29770    return [this.current_canvas_size.width, this.current_canvas_size.height];
29771  }
29772  async execute(context) {
29773    if (context) {
29774      this.context = context;
29775    }
29776    await this.start();
29777    if (this.sprite === null) {
29778      return;
29779    }
29780    const { width, height } = this.computed_dimensions;
29781    await this.context.set_image_properties({
29782      scale: 1,
29783      position: {
29784        x: this.context.app.screen.width / 2,
29785        y: this.context.app.screen.height / 2
29786      },
29787      width: this.fixed_canvas ? this.current_canvas_size.width : width,
29788      height: this.fixed_canvas ? this.current_canvas_size.height : height
29789    });
29790    const background_layer = this.context.layer_manager.create_background_layer(
29791      this.fixed_canvas ? this.current_canvas_size.width : width,
29792      this.fixed_canvas ? this.current_canvas_size.height : height
29793    );
29794    this.sprite.zIndex = 0;
29795    background_layer.addChild(this.sprite);
29796    this.context.layer_manager.reset_layers(
29797      this.fixed_canvas ? this.current_canvas_size.width : width,
29798      this.fixed_canvas ? this.current_canvas_size.height : height,
29799      true
29800    );
29801    if (this.border_region > 0) {
29802      this.sprite.borderRegion = this.border_region;
29803    }
29804    this.context.set_background_image(this.sprite);
29805    this.context.reset();
29806  }
29807  async undo() {
29808    if (this.sprite) {
29809      this.sprite.destroy();
29810      if (this.previous_image) {
29811        const previous_sprite = new Sprite(this.previous_image.texture);
29812        previous_sprite.width = this.previous_image.width;
29813        previous_sprite.height = this.previous_image.height;
29814        previous_sprite.position.set(
29815          this.previous_image.x,
29816          this.previous_image.y
29817        );
29818        if (this.previous_image.border_region > 0) {
29819          previous_sprite.borderRegion = this.previous_image.border_region;
29820        }
29821        await this.context.set_image_properties({
29822          scale: 1,
29823          position: {
29824            x: this.context.app.screen.width / 2,
29825            y: this.context.app.screen.height / 2
29826          },
29827          width: this.previous_image.width,
29828          height: this.previous_image.height
29829        });
29830        const background_layer = this.context.layer_manager.create_background_layer(
29831          this.previous_image.width,
29832          this.previous_image.height
29833        );
29834        previous_sprite.zIndex = 0;
29835        background_layer.addChild(previous_sprite);
29836        this.context.layer_manager.reset_layers(
29837          this.previous_image.width,
29838          this.previous_image.height,
29839          true
29840        );
29841        this.context.set_background_image(previous_sprite);
29842      } else {
29843        await this.context.set_image_properties({
29844          scale: 1,
29845          position: {
29846            x: this.context.app.screen.width / 2,
29847            y: this.context.app.screen.height / 2
29848          },
29849          width: this.current_canvas_size.width,
29850          height: this.current_canvas_size.height
29851        });
29852        this.context.layer_manager.create_background_layer(
29853          this.current_canvas_size.width,
29854          this.current_canvas_size.height
29855        );
29856        this.context.layer_manager.reset_layers(
29857          this.current_canvas_size.width,
29858          this.current_canvas_size.height
29859        );
29860      }
29861      this.context.reset();
29862    }
29863  }
29864}
29865function fit_image_to_canvas(image_width, image_height, canvas_width, canvas_height) {
29866  const image_aspect_ratio = image_width / image_height;
29867  const canvas_aspect_ratio = canvas_width / canvas_height;
29868  let new_width;
29869  let new_height;
29870  if (image_aspect_ratio > canvas_aspect_ratio) {
29871    new_width = canvas_width;
29872    new_height = canvas_width / image_aspect_ratio;
29873  } else {
29874    new_height = canvas_height;
29875    new_width = canvas_height * image_aspect_ratio;
29876  }
29877  const x = Math.round((canvas_width - new_width) / 2);
29878  const y = Math.round((canvas_height - new_height) / 2);
29879  return {
29880    width: Math.round(new_width),
29881    height: Math.round(new_height),
29882    x,
29883    y
29884  };
29885}
29886
29887class ZoomTool {
29888  name = "zoom";
29889  min_zoom = writable(true);
29890  image_editor_context;
29891  max_zoom = 10;
29892  border_padding = 30;
29893  pad_bottom = 0;
29894  is_pinching = false;
29895  is_dragging = false;
29896  is_pointer_dragging = false;
29897  last_touch_position = null;
29898  last_pinch_distance = 0;
29899  drag_start = new Point();
29900  current_tool;
29901  current_subtool;
29902  local_scale = 1;
29903  local_dimensions = {
29904    width: 0,
29905    height: 0
29906  };
29907  local_position = { x: 0, y: 0 };
29908  /**
29909   * Prevents default behavior for events
29910   * @param {FederatedWheelEvent | FederatedPointerEvent} event - The event to prevent default behavior for
29911   */
29912  prevent_default(event) {
29913    event.preventDefault();
29914    event.stopPropagation();
29915  }
29916  /**
29917   * Sets the current tool and subtool
29918   * @param {ToolbarTool} tool - The tool to set
29919   * @param {Subtool} subtool - The subtool to set
29920   */
29921  set_tool(tool, subtool) {
29922    this.current_tool = tool;
29923    this.current_subtool = subtool;
29924  }
29925  /**
29926   * Sets the zoom level to a specific percentage or fits the image to the screen
29927   * @param {number | 'fit'} zoom_level - The zoom level to set (0-1) or 'fit' to use min_zoom
29928   * @returns {void}
29929   */
29930  set_zoom(zoom_level) {
29931    const fit_zoom = this.calculate_min_zoom(
29932      this.local_dimensions.width,
29933      this.local_dimensions.height
29934    );
29935    let target_zoom;
29936    const is_fit_zoom = zoom_level === "fit";
29937    if (is_fit_zoom) {
29938      target_zoom = fit_zoom;
29939    } else {
29940      target_zoom = Math.max(0, Math.min(this.max_zoom, zoom_level));
29941    }
29942    const canvas = this.image_editor_context.app.screen;
29943    const canvas_width = canvas.width;
29944    const canvas_height = canvas.height;
29945    let center_point;
29946    center_point = {
29947      x: canvas_width / 2,
29948      y: canvas_height / 2
29949    };
29950    this.zoom_to_point(target_zoom, center_point, true, is_fit_zoom);
29951  }
29952  /**
29953   * Sets up the zoom tool with the given context and state
29954   * @param {ImageEditorContext} context - The image editor context
29955   * @param {ToolbarTool} tool - The current tool
29956   * @param {Subtool} subtool - The current subtool
29957   * @returns {Promise<void>}
29958   */
29959  async setup(context, tool, subtool) {
29960    this.image_editor_context = context;
29961    this.current_tool = tool;
29962    this.current_subtool = subtool;
29963    this.pad_bottom = context.pad_bottom;
29964    const { width, height } = await this.get_container_dimensions();
29965    const fit_zoom = this.calculate_min_zoom(width, height);
29966    const min_zoom = Math.min(fit_zoom, 1);
29967    this.local_scale = min_zoom;
29968    const canvas = this.image_editor_context.app.screen;
29969    const center_x = canvas.width / 2;
29970    const center_y = canvas.height / 2;
29971    const scaled_width = width * min_zoom;
29972    const scaled_height = height * min_zoom;
29973    const initial_x = center_x - scaled_width / 2;
29974    const initial_y = center_y - scaled_height / 2;
29975    this.local_position = { x: initial_x, y: initial_y };
29976    this.setup_event_listeners();
29977    await this.image_editor_context.set_image_properties({
29978      scale: min_zoom,
29979      position: { x: initial_x, y: initial_y }
29980    });
29981    this.image_editor_context.dimensions.subscribe((dimensions) => {
29982      this.local_dimensions = dimensions;
29983    });
29984    this.image_editor_context.scale.subscribe((scale) => {
29985      this.local_scale = scale;
29986    });
29987    this.image_editor_context.position.subscribe((position) => {
29988      this.local_position = position;
29989    });
29990  }
29991  /**
29992   * Sets up event listeners for zoom and pan functionality
29993   * @private
29994   */
29995  setup_event_listeners() {
29996    const stage = this.image_editor_context.app.stage;
29997    const canvas = this.image_editor_context.app.canvas;
29998    canvas.addEventListener("wheel", this.prevent_default, { passive: false });
29999    stage.eventMode = "static";
30000    stage.hitArea = this.image_editor_context.app.screen;
30001    const wheel_listener = this.handle_wheel.bind(this);
30002    stage.addEventListener("wheel", wheel_listener, { passive: false });
30003    if ("ontouchstart" in window) {
30004      stage.addEventListener(
30005        "touchstart",
30006        this.handle_touch_start.bind(this)
30007      );
30008      stage.addEventListener(
30009        "touchmove",
30010        this.handle_touch_move.bind(this)
30011      );
30012      stage.addEventListener(
30013        "touchend",
30014        this.handle_touch_end.bind(this)
30015      );
30016    } else {
30017      stage.addEventListener(
30018        "pointerdown",
30019        this.handle_pointer_down.bind(this)
30020      );
30021      stage.addEventListener(
30022        "pointermove",
30023        this.handle_pointer_move.bind(this)
30024      );
30025      stage.addEventListener("pointerup", this.handle_pointer_up.bind(this));
30026      stage.addEventListener(
30027        "pointerupoutside",
30028        this.handle_pointer_up.bind(this)
30029      );
30030    }
30031  }
30032  /**
30033   * Handles wheel events for zooming
30034   * @param {FederatedWheelEvent} event - The wheel event
30035   * @private
30036   */
30037  handle_wheel(event) {
30038    const is_trackpad = event.deltaMode === 0 && Math.abs(event.deltaY) < 50;
30039    const scroll_speed = is_trackpad ? 30 : 10;
30040    if (event.altKey || event.metaKey) {
30041      const local_point = this.image_editor_context.app.stage.toLocal(
30042        event.global
30043      );
30044      const zoom_delta = -event.deltaY * (is_trackpad ? 1e-3 : 5e-4);
30045      const new_scale = this.local_scale * (1 + zoom_delta);
30046      this.zoom_to_point(new_scale, local_point, true);
30047    } else {
30048      const delta_x = event.deltaX;
30049      const delta_y = event.deltaY;
30050      const raw_position = {
30051        x: this.local_position.x - delta_x / 100 * scroll_speed,
30052        y: this.local_position.y - delta_y / 100 * scroll_speed
30053      };
30054      const canvas = this.image_editor_context.app.screen;
30055      const scaled_width = this.local_dimensions.width * this.local_scale;
30056      const scaled_height = this.local_dimensions.height * this.local_scale;
30057      const available_width = canvas.width - this.border_padding * 2;
30058      const available_height = canvas.height - this.border_padding * 2 - this.pad_bottom;
30059      let final_position = { ...raw_position };
30060      if (scaled_width <= available_width) {
30061        final_position.x = (canvas.width - scaled_width) / 2;
30062      } else {
30063        const max_offset = canvas.width / 2;
30064        const left_bound = max_offset;
30065        const right_bound = canvas.width - max_offset - scaled_width;
30066        final_position.x = Math.min(
30067          Math.max(raw_position.x, right_bound),
30068          left_bound
30069        );
30070      }
30071      if (scaled_height <= available_height) {
30072        final_position.y = (canvas.height - this.pad_bottom - scaled_height) / 2;
30073      } else {
30074        const max_offset = (canvas.height - this.pad_bottom) / 2;
30075        const top_bound = max_offset;
30076        const bottom_bound = canvas.height - this.pad_bottom - max_offset - scaled_height;
30077        final_position.y = Math.min(
30078          Math.max(raw_position.y, bottom_bound),
30079          top_bound
30080        );
30081      }
30082      this.image_editor_context.set_image_properties({
30083        scale: this.local_scale,
30084        position: final_position
30085      });
30086    }
30087  }
30088  /**
30089   * Cleans up resources and event listeners
30090   */
30091  cleanup() {
30092    const stage = this?.image_editor_context?.app?.stage;
30093    if (!stage) return;
30094    stage.removeEventListener("wheel", this.handle_wheel.bind(this));
30095    if ("ontouchstart" in window) {
30096      stage.removeEventListener(
30097        "touchstart",
30098        this.handle_touch_start.bind(this)
30099      );
30100      stage.removeEventListener(
30101        "touchmove",
30102        this.handle_touch_move.bind(this)
30103      );
30104      stage.removeEventListener(
30105        "touchend",
30106        this.handle_touch_end.bind(this)
30107      );
30108    } else {
30109      stage.removeEventListener(
30110        "pointerdown",
30111        this.handle_pointer_down.bind(this)
30112      );
30113      stage.removeEventListener(
30114        "pointermove",
30115        this.handle_pointer_move.bind(this)
30116      );
30117      stage.removeEventListener("pointerup", this.handle_pointer_up.bind(this));
30118      stage.removeEventListener(
30119        "pointerupoutside",
30120        this.handle_pointer_up.bind(this)
30121      );
30122    }
30123  }
30124  /**
30125   * Gets the dimensions of the container
30126   * @returns {Promise<{width: number, height: number}>} The container dimensions
30127   * @private
30128   */
30129  async get_container_dimensions() {
30130    const bounds = this.image_editor_context.image_container.getLocalBounds();
30131    return {
30132      width: bounds.width,
30133      height: bounds.height
30134    };
30135  }
30136  /**
30137   * Calculates the minimum zoom level to fit the image in the viewport
30138   * @param {number} container_width - The width of the container
30139   * @param {number} container_height - The height of the container
30140   * @returns {number} The minimum zoom level
30141   * @private
30142   */
30143  calculate_min_zoom(container_width, container_height) {
30144    const canvas = this.image_editor_context.app.screen;
30145    const viewport_width = canvas.width;
30146    const viewport_height = canvas.height;
30147    if (!container_width || !viewport_width || !container_height || !viewport_height) {
30148      return 1;
30149    }
30150    const available_width = viewport_width - this.border_padding * 2;
30151    const available_height = viewport_height - this.border_padding * 2 - this.pad_bottom;
30152    const width_ratio = available_width / container_width;
30153    const height_ratio = available_height / container_height;
30154    return Math.min(width_ratio, height_ratio);
30155  }
30156  /**
30157   * Handles touch start events
30158   * @param {TouchEvent} event - The touch event
30159   * @private
30160   */
30161  handle_touch_start(event) {
30162    event.preventDefault();
30163    const touchEvent = event;
30164    if (touchEvent.touches && touchEvent.touches.length === 2) {
30165      this.is_pinching = true;
30166      this.last_pinch_distance = Math.hypot(
30167        touchEvent.touches[0].pageX - touchEvent.touches[1].pageX,
30168        touchEvent.touches[0].pageY - touchEvent.touches[1].pageY
30169      );
30170    } else if (touchEvent.touches && touchEvent.touches.length === 1) {
30171      this.is_dragging = true;
30172      const rect = this.image_editor_context.app.view.getBoundingClientRect();
30173      this.last_touch_position = new Point(
30174        touchEvent.touches[0].pageX - rect.left,
30175        touchEvent.touches[0].pageY - rect.top
30176      );
30177    }
30178  }
30179  /**
30180   * Handles touch move events
30181   * @param {TouchEvent} event - The touch event
30182   * @private
30183   */
30184  handle_touch_move(event) {
30185    event.preventDefault();
30186    const touchEvent = event;
30187    if (this.is_pinching && touchEvent.touches && touchEvent.touches.length === 2) {
30188      const rect = this.image_editor_context.app.view.getBoundingClientRect();
30189      const current_distance = Math.hypot(
30190        touchEvent.touches[0].pageX - touchEvent.touches[1].pageX,
30191        touchEvent.touches[0].pageY - touchEvent.touches[1].pageY
30192      );
30193      const pinch_center = {
30194        x: (touchEvent.touches[0].pageX + touchEvent.touches[1].pageX) / 2 - rect.left,
30195        y: (touchEvent.touches[0].pageY + touchEvent.touches[1].pageY) / 2 - rect.top
30196      };
30197      const scale_val = current_distance / this.last_pinch_distance;
30198      this.last_pinch_distance = current_distance;
30199      this.zoom_to_point(this.local_scale * scale_val, pinch_center);
30200    } else if (this.is_dragging && touchEvent.touches && touchEvent.touches.length === 1 && this.last_touch_position) {
30201      const rect = this.image_editor_context.app.view.getBoundingClientRect();
30202      const current_position = new Point(
30203        touchEvent.touches[0].pageX - rect.left,
30204        touchEvent.touches[0].pageY - rect.top
30205      );
30206      const dx = current_position.x - this.last_touch_position.x;
30207      const dy = current_position.y - this.last_touch_position.y;
30208      this.image_editor_context.set_image_properties({
30209        position: {
30210          x: this.local_position.x + dx,
30211          y: this.local_position.y + dy
30212        }
30213      });
30214      this.last_touch_position = current_position;
30215    }
30216  }
30217  /**
30218   * Handles touch end events
30219   * @param {TouchEvent} event - The touch event
30220   * @private
30221   */
30222  handle_touch_end(event) {
30223    event.preventDefault();
30224    const touchEvent = event;
30225    if (touchEvent.touches && touchEvent.touches.length < 2) {
30226      this.is_pinching = false;
30227      if (touchEvent.touches && touchEvent.touches.length === 1) {
30228        const rect = this.image_editor_context.app.view.getBoundingClientRect();
30229        this.last_touch_position = new Point(
30230          touchEvent.touches[0].pageX - rect.left,
30231          touchEvent.touches[0].pageY - rect.top
30232        );
30233        this.is_dragging = true;
30234      }
30235    }
30236    if (touchEvent.touches && touchEvent.touches.length === 0) {
30237      this.is_dragging = false;
30238      this.last_touch_position = null;
30239      this.last_pinch_distance = 0;
30240    }
30241  }
30242  /**
30243   * Gets the bounded position to keep the image within the viewport
30244   * @param {Object} position - The position to bound
30245   * @param {number} position.x - The x coordinate
30246   * @param {number} position.y - The y coordinate
30247   * @returns {Object} The bounded position
30248   * @private
30249   */
30250  get_bounded_position(position) {
30251    const canvas = this.image_editor_context.app.screen;
30252    const scaled_width = this.local_dimensions.width * this.local_scale;
30253    const scaled_height = this.local_dimensions.height * this.local_scale;
30254    const center_position = {
30255      x: (canvas.width - scaled_width) / 2,
30256      y: (canvas.height - scaled_height - this.pad_bottom) / 2
30257    };
30258    if (scaled_width <= canvas.width && scaled_height <= canvas.height) {
30259      return center_position;
30260    }
30261    let x = position.x;
30262    let y = position.y;
30263    if (scaled_width <= canvas.width) {
30264      x = center_position.x;
30265    } else {
30266      const min_x = canvas.width - scaled_width;
30267      const max_x = 0;
30268      x = Math.max(min_x, Math.min(max_x, position.x));
30269    }
30270    if (scaled_height <= canvas.height - this.pad_bottom) {
30271      y = center_position.y;
30272    } else {
30273      const min_y = canvas.height - scaled_height - this.pad_bottom;
30274      const max_y = 0;
30275      y = Math.max(min_y, Math.min(max_y, position.y));
30276    }
30277    return { x, y };
30278  }
30279  /**
30280   * Zooms to a specific point with a new zoom level
30281   * @param {number} new_zoom - The new zoom level
30282   * @param {Object} point - The point to zoom to
30283   * @param {number} point.x - The x coordinate of the point
30284   * @param {number} point.y - The y coordinate of the point
30285   * @param {boolean} hard - Whether to apply a hard zoom (no animation)
30286   * @param {boolean} is_fit_zoom - Whether this is a fit zoom operation
30287   * @private
30288   */
30289  zoom_to_point(new_zoom, point, hard, is_fit_zoom) {
30290    const container = this.image_editor_context.image_container;
30291    const current_world_pos = container.getGlobalPosition();
30292    const current_scale = container.scale.x;
30293    const cursor_image_pixel = {
30294      x: (point.x - current_world_pos.x) / current_scale,
30295      y: (point.y - current_world_pos.y) / current_scale
30296    };
30297    const fit_zoom = this.calculate_min_zoom(
30298      this.local_dimensions.width,
30299      this.local_dimensions.height
30300    );
30301    const min_zoom = Math.min(fit_zoom, 1);
30302    new_zoom = Math.min(Math.max(new_zoom, min_zoom), this.max_zoom);
30303    const new_container_world_pos = {
30304      x: point.x - cursor_image_pixel.x * new_zoom,
30305      y: point.y - cursor_image_pixel.y * new_zoom
30306    };
30307    if (new_zoom === min_zoom || is_fit_zoom) {
30308      const canvas_width = this.image_editor_context.app.screen.width;
30309      const canvas_height = this.image_editor_context.app.screen.height;
30310      const new_scaled_width = this.local_dimensions.width * new_zoom;
30311      const new_scaled_height = this.local_dimensions.height * new_zoom;
30312      new_container_world_pos.x = (canvas_width - new_scaled_width) / 2;
30313      new_container_world_pos.y = (canvas_height - this.pad_bottom - new_scaled_height) / 2;
30314    }
30315    this.image_editor_context.set_image_properties({
30316      scale: new_zoom,
30317      position: new_container_world_pos,
30318      animate: typeof hard === "boolean" ? !hard : new_zoom === min_zoom
30319    });
30320    this.min_zoom.set(new_zoom === min_zoom);
30321  }
30322  /**
30323   * Handles pointer down events
30324   * @param {FederatedPointerEvent} event - The pointer event
30325   * @private
30326   */
30327  handle_pointer_down(event) {
30328    if (event.button === 0 && this.current_tool === "pan") {
30329      this.is_pointer_dragging = true;
30330      this.drag_start.copyFrom(event.global);
30331      this.drag_start.x -= this.local_position.x;
30332      this.drag_start.y -= this.local_position.y;
30333    }
30334  }
30335  /**
30336   * Handles pointer move events
30337   * @param {FederatedPointerEvent} event - The pointer event
30338   * @private
30339   */
30340  handle_pointer_move(event) {
30341    if (this.is_pointer_dragging && this.current_tool === "pan") {
30342      const raw_position = {
30343        x: event.global.x - this.drag_start.x,
30344        y: event.global.y - this.drag_start.y
30345      };
30346      const canvas = this.image_editor_context.app.screen;
30347      const scaled_width = this.local_dimensions.width * this.local_scale;
30348      const scaled_height = this.local_dimensions.height * this.local_scale;
30349      const available_width = canvas.width - this.border_padding * 2;
30350      const available_height = canvas.height - this.border_padding * 2 - this.pad_bottom;
30351      let final_position = { ...raw_position };
30352      if (scaled_width <= available_width) {
30353        final_position.x = (canvas.width - scaled_width) / 2;
30354      } else {
30355        final_position.x = raw_position.x;
30356      }
30357      if (scaled_height <= available_height) {
30358        final_position.y = (canvas.height - this.pad_bottom - scaled_height) / 2;
30359      } else {
30360        final_position.y = raw_position.y;
30361      }
30362      this.image_editor_context.set_image_properties({
30363        scale: this.local_scale,
30364        position: final_position
30365      });
30366    }
30367  }
30368  /**
30369   * Handles pointer up events
30370   * @param {FederatedPointerEvent} event - The pointer event
30371   * @private
30372   */
30373  handle_pointer_up(event) {
30374    if (this.is_pointer_dragging && this.current_tool === "pan") {
30375      this.is_pointer_dragging = false;
30376      const raw_position = {
30377        x: event.global.x - this.drag_start.x,
30378        y: event.global.y - this.drag_start.y
30379      };
30380      const canvas = this.image_editor_context.app.screen;
30381      const scaled_width = this.local_dimensions.width * this.local_scale;
30382      const scaled_height = this.local_dimensions.height * this.local_scale;
30383      const available_width = canvas.width - this.border_padding * 2;
30384      const available_height = canvas.height - this.border_padding * 2 - this.pad_bottom;
30385      let final_position = { ...raw_position };
30386      if (scaled_width <= available_width) {
30387        final_position.x = (canvas.width - scaled_width) / 2;
30388      } else {
30389        const max_offset = canvas.width / 2;
30390        const left_bound = max_offset;
30391        const right_bound = canvas.width - max_offset - scaled_width;
30392        final_position.x = Math.min(
30393          Math.max(raw_position.x, right_bound),
30394          left_bound
30395        );
30396      }
30397      if (scaled_height <= available_height) {
30398        final_position.y = (canvas.height - this.pad_bottom - scaled_height) / 2;
30399      } else {
30400        const max_offset = (canvas.height - this.pad_bottom) / 2;
30401        const top_bound = max_offset;
30402        const bottom_bound = canvas.height - this.pad_bottom - max_offset - scaled_height;
30403        final_position.y = Math.min(
30404          Math.max(raw_position.y, bottom_bound),
30405          top_bound
30406        );
30407      }
30408      this.image_editor_context.set_image_properties({
30409        scale: this.local_scale,
30410        position: final_position,
30411        animate: true
30412      });
30413    }
30414  }
30415}
30416
30417class LayerManager {
30418  layers = [];
30419  active_layer = null;
30420  active_layer_id = null;
30421  draw_textures = /* @__PURE__ */ new Map();
30422  layer_store = writable({
30423    active_layer: "",
30424    layers: []
30425  });
30426  background_layer = null;
30427  image_container;
30428  app;
30429  fixed_canvas;
30430  dark;
30431  border_region;
30432  layer_options;
30433  constructor(image_container, app, fixed_canvas, dark, border_region, layer_options) {
30434    this.image_container = image_container;
30435    this.app = app;
30436    this.fixed_canvas = fixed_canvas;
30437    this.dark = dark;
30438    this.border_region = border_region;
30439    this.layer_options = layer_options;
30440  }
30441  toggle_layer_visibility(id) {
30442    const layer = this.layers.find((l) => l.id === id);
30443    if (layer) {
30444      layer.container.visible = !layer.container.visible;
30445      layer.visible = layer.container.visible;
30446      this.layer_store.update((state) => ({
30447        active_layer: state.active_layer,
30448        layers: this.layers
30449      }));
30450    }
30451  }
30452  create_background_layer(width, height) {
30453    if (this.background_layer) {
30454      this.background_layer.destroy();
30455    }
30456    const layer = new Container();
30457    this.background_layer = layer;
30458    const bg_texture = RenderTexture.create({
30459      width,
30460      height,
30461      resolution: window.devicePixelRatio,
30462      antialias: true,
30463      scaleMode: SCALE_MODES.NEAREST
30464    });
30465    const bg_sprite = new Sprite(bg_texture);
30466    layer.addChild(bg_sprite);
30467    const clear_graphics = new Graphics();
30468    clear_graphics.clear();
30469    clear_graphics.rect(0, 0, width, height).fill({ color: this.dark ? 3355443 : 16777215, alpha: 1 });
30470    this.app.renderer.render({
30471      container: clear_graphics,
30472      target: bg_texture,
30473      clear: true
30474    });
30475    this.image_container.addChild(layer);
30476    layer.zIndex = -1;
30477    this.update_layer_order();
30478    return layer;
30479  }
30480  set_layer_options(layer_options, width, height) {
30481    this.layer_options = layer_options;
30482    this.reset_layers(width, height);
30483  }
30484  /**
30485   * Creates a background layer from an image URL
30486   * @param url The URL of the image to use as background
30487   * @param width The width of the layer (if fixed_canvas is true)
30488   * @param height The height of the layer (if fixed_canvas is true)
30489   * @param borderRegion Minimum border region to add around the image for outpainting (in pixels)
30490   * @returns A Promise that resolves to the background Container when the image is loaded
30491   */
30492  async create_background_layer_from_url(url, width, height) {
30493    const layer = this.create_background_layer(
30494      width || this.image_container.width,
30495      height || this.image_container.height
30496    );
30497    try {
30498      const texture = await Texture.from(url);
30499      const sprite = new Sprite(texture);
30500      const imageWidth = sprite.texture.width;
30501      const imageHeight = sprite.texture.height;
30502      const containerWidth = width || this.image_container.width;
30503      const containerHeight = height || this.image_container.height;
30504      if (this.fixed_canvas) {
30505        const effectiveContainerWidth = Math.max(
30506          containerWidth - this.border_region * 2,
30507          10
30508        );
30509        const effectiveContainerHeight = Math.max(
30510          containerHeight - this.border_region * 2,
30511          10
30512        );
30513        const imageAspectRatio = imageWidth / imageHeight;
30514        const containerAspectRatio = effectiveContainerWidth / effectiveContainerHeight;
30515        let finalWidth, finalHeight;
30516        let posX = this.border_region, posY = this.border_region;
30517        if (imageWidth <= effectiveContainerWidth && imageHeight <= effectiveContainerHeight) {
30518          finalWidth = imageWidth;
30519          finalHeight = imageHeight;
30520        } else {
30521          if (imageAspectRatio > containerAspectRatio) {
30522            finalWidth = effectiveContainerWidth;
30523            finalHeight = effectiveContainerWidth / imageAspectRatio;
30524          } else {
30525            finalHeight = effectiveContainerHeight;
30526            finalWidth = effectiveContainerHeight * imageAspectRatio;
30527          }
30528        }
30529        posX += Math.round((effectiveContainerWidth - finalWidth) / 2);
30530        posY += Math.round((effectiveContainerHeight - finalHeight) / 2);
30531        sprite.width = finalWidth;
30532        sprite.height = finalHeight;
30533        sprite.position.set(posX, posY);
30534      } else {
30535        sprite.position.set(this.border_region, this.border_region);
30536        if (this.background_layer) {
30537          this.background_layer.destroy();
30538        }
30539        const totalWidth = imageWidth + this.border_region * 2;
30540        const totalHeight = imageHeight + this.border_region * 2;
30541        const newLayer = this.create_background_layer(totalWidth, totalHeight);
30542        sprite.width = imageWidth;
30543        sprite.height = imageHeight;
30544        newLayer.addChild(sprite);
30545        return newLayer;
30546      }
30547      layer.addChild(sprite);
30548      return layer;
30549    } catch (error) {
30550      console.error("Error loading image from URL:", error);
30551      return layer;
30552    }
30553  }
30554  create_layer({
30555    width,
30556    height,
30557    layer_name,
30558    user_created,
30559    layer_id = void 0,
30560    make_active = false
30561  }) {
30562    const layer = new Container();
30563    const _layer_id = layer_id || Math.random().toString(36).substring(2, 15);
30564    const _layer_name = layer_name || `Layer ${this.layers.length + 1}`;
30565    this.layers.push({
30566      name: _layer_name,
30567      id: _layer_id,
30568      container: layer,
30569      user_created,
30570      visible: true
30571    });
30572    this.image_container.addChild(layer);
30573    const draw_texture = RenderTexture.create({
30574      width,
30575      height,
30576      resolution: window.devicePixelRatio,
30577      antialias: true,
30578      scaleMode: SCALE_MODES.NEAREST
30579    });
30580    const canvas_sprite = new Sprite(draw_texture);
30581    layer.addChild(canvas_sprite);
30582    const clear_graphics = new Graphics();
30583    clear_graphics.clear();
30584    clear_graphics.beginFill(0, 0);
30585    clear_graphics.drawRect(0, 0, width, height);
30586    clear_graphics.endFill();
30587    this.app.renderer.render({
30588      container: clear_graphics,
30589      target: draw_texture,
30590      clear: true
30591    });
30592    this.draw_textures.set(layer, draw_texture);
30593    this.update_layer_order();
30594    if (make_active) {
30595      this.set_active_layer(_layer_id);
30596    }
30597    this.layer_store.set({
30598      active_layer: this.active_layer_id || "",
30599      layers: this.layers
30600    });
30601    return layer;
30602  }
30603  /**
30604   * Creates a new layer with an image loaded from a URL
30605   * @param url The URL of the image to load
30606   * @param borderRegion Minimum border region to add around the image for outpainting (in pixels)
30607   * @returns A Promise that resolves to the layer ID
30608   */
30609  async add_layer_from_url(url) {
30610    const { width, height } = this.image_container.getLocalBounds();
30611    const layer = this.create_layer({
30612      width,
30613      height,
30614      layer_name: "Layer 1",
30615      user_created: true
30616    });
30617    const layerIndex = this.layers.findIndex((l) => l.container === layer);
30618    if (layerIndex === -1) {
30619      console.error("Could not find newly created layer");
30620      return "";
30621    }
30622    const layerId = this.layers[layerIndex].id;
30623    try {
30624      const texture = await Assets.load(url);
30625      const drawTexture = this.draw_textures.get(layer);
30626      if (!drawTexture) {
30627        console.error("No draw texture found for layer");
30628        return layerId;
30629      }
30630      const sprite = new Sprite(texture);
30631      const imageWidth = sprite.width;
30632      const imageHeight = sprite.height;
30633      let posX = this.border_region;
30634      let posY = this.border_region;
30635      const effectiveWidth = this.fixed_canvas ? width - this.border_region * 2 : width;
30636      const effectiveHeight = this.fixed_canvas ? height - this.border_region * 2 : height;
30637      if (imageWidth < effectiveWidth || imageHeight < effectiveHeight) {
30638        posX = Math.floor((effectiveWidth - imageWidth) / 2);
30639        posY = Math.floor((effectiveHeight - imageHeight) / 2);
30640      }
30641      sprite.position.set(posX, posY);
30642      if (imageWidth > effectiveWidth || imageHeight > effectiveHeight) {
30643        const imageAspectRatio = imageWidth / imageHeight;
30644        const areaAspectRatio = effectiveWidth / effectiveHeight;
30645        let finalWidth, finalHeight;
30646        if (imageAspectRatio > areaAspectRatio) {
30647          finalWidth = effectiveWidth;
30648          finalHeight = effectiveWidth / imageAspectRatio;
30649        } else {
30650          finalHeight = effectiveHeight;
30651          finalWidth = effectiveHeight * imageAspectRatio;
30652        }
30653        sprite.width = finalWidth;
30654        sprite.height = finalHeight;
30655        posX = this.border_region + Math.floor((effectiveWidth - finalWidth) / 2);
30656        posY = this.border_region + Math.floor((effectiveHeight - finalHeight) / 2);
30657        sprite.position.set(posX, posY);
30658      }
30659      this.app.renderer.render(sprite, { renderTexture: drawTexture });
30660      this.set_active_layer(layerId);
30661      return layerId;
30662    } catch (error) {
30663      console.error("Error loading image from URL:", error);
30664      return layerId;
30665    }
30666  }
30667  get_active_layer() {
30668    return this.active_layer;
30669  }
30670  set_active_layer(id) {
30671    if (this.layers.some((l) => l.id === id)) {
30672      this.active_layer = this.layers.find((l) => l.id === id)?.container || this.layers[0]?.container || null;
30673      this.active_layer_id = id;
30674      this.layer_store.set({
30675        active_layer: id,
30676        layers: this.layers
30677      });
30678    }
30679  }
30680  get_layers() {
30681    return this.layers;
30682  }
30683  get_layer_textures(id) {
30684    const layer = this.layers.find((l) => l.id === id);
30685    if (layer) {
30686      const draw = this.draw_textures.get(layer.container);
30687      if (draw) {
30688        return { draw };
30689      }
30690    }
30691    return null;
30692  }
30693  delete_layer(id) {
30694    const index = this.layers.findIndex((l) => l.id === id);
30695    if (index > -1) {
30696      const draw_texture = this.draw_textures.get(this.layers[index].container);
30697      if (draw_texture) {
30698        draw_texture.destroy();
30699        this.draw_textures.delete(this.layers[index].container);
30700      }
30701      this.layers[index].container.destroy();
30702      if (this.active_layer === this.layers[index].container) {
30703        const new_active_layer = this.layers[Math.max(0, index - 1)] || null;
30704        this.active_layer = new_active_layer?.container || null;
30705        this.active_layer_id = new_active_layer?.id || null;
30706      }
30707      this.layers = this.layers.filter((l) => l.id !== id);
30708      this.layer_store.update((_layers) => ({
30709        active_layer: _layers.active_layer === id ? this.layers[this.layers.length - 1]?.id : _layers.active_layer,
30710        layers: this.layers
30711      }));
30712      this.update_layer_order();
30713    }
30714  }
30715  update_layer_order() {
30716    if (this.background_layer) {
30717      this.background_layer.zIndex = -1;
30718    }
30719    this.layers.forEach((layer, index) => {
30720      layer.container.zIndex = index;
30721    });
30722  }
30723  move_layer(id, direction) {
30724    const index = this.layers.findIndex((l) => l.id === id);
30725    if (index > -1) {
30726      const new_index = direction === "up" ? index - 1 : index + 1;
30727      this.layers = this.layers.map((l, i) => {
30728        if (i === index) {
30729          return this.layers[new_index];
30730        }
30731        if (i === new_index) {
30732          return this.layers[index];
30733        }
30734        return l;
30735      });
30736      this.update_layer_order();
30737      this.layer_store.update((_layers) => ({
30738        active_layer: id,
30739        layers: this.layers
30740      }));
30741    }
30742  }
30743  /**
30744   * Resizes all layers to a new width and height
30745   * @param newWidth The new width of the layers
30746   * @param newHeight The new height of the layers
30747   * @param scale If true, scales the layer content to fit the new dimensions. If false, keeps the content size unchanged.
30748   * @param anchor The anchor point to position the content relative to the new size
30749   */
30750  resize_all_layers(newWidth, newHeight, scale, anchor, oldCanvasWidth, oldCanvasHeight) {
30751    new Map(this.layers.map((l) => [l.id, l]));
30752    const oldBackgroundLayer = this.background_layer;
30753    const calculateOffset = () => {
30754      let offsetX = 0;
30755      let offsetY = 0;
30756      const deltaWidth = newWidth - oldCanvasWidth;
30757      const deltaHeight = newHeight - oldCanvasHeight;
30758      if (anchor.includes("left")) {
30759        offsetX = 0;
30760      } else if (anchor.includes("right")) {
30761        offsetX = deltaWidth;
30762      } else {
30763        offsetX = Math.floor(deltaWidth / 2);
30764      }
30765      if (anchor.includes("top")) {
30766        offsetY = 0;
30767      } else if (anchor.includes("bottom")) {
30768        offsetY = deltaHeight;
30769      } else {
30770        offsetY = Math.floor(deltaHeight / 2);
30771      }
30772      return { offsetX, offsetY };
30773    };
30774    this.background_layer = this._resize_background_layer(
30775      oldBackgroundLayer,
30776      newWidth,
30777      newHeight,
30778      scale,
30779      calculateOffset
30780    );
30781    const processedLayers = [];
30782    const oldLayerData = this.layers.map((oldLayer) => ({
30783      id: oldLayer.id,
30784      name: oldLayer.name,
30785      user_created: oldLayer.user_created,
30786      texture: this.draw_textures.get(oldLayer.container),
30787      container: oldLayer.container
30788    }));
30789    this.layers = [];
30790    this.draw_textures.clear();
30791    for (const oldData of oldLayerData) {
30792      const newLayer = this._resize_single_layer(
30793        oldData,
30794        newWidth,
30795        newHeight,
30796        scale,
30797        calculateOffset
30798      );
30799      if (newLayer) {
30800        processedLayers.push(newLayer);
30801      }
30802    }
30803    const currentActiveId = get$1(this.layer_store).active_layer;
30804    const activeLayerExists = processedLayers.some(
30805      (l) => l.id === currentActiveId
30806    );
30807    if (!activeLayerExists && processedLayers.length > 0) {
30808      this.set_active_layer(processedLayers[0].id);
30809    } else if (processedLayers.length === 0) {
30810      this.layer_store.update((s) => ({ ...s, active_layer: "" }));
30811    } else {
30812      this.layer_store.update((s) => ({ ...s }));
30813    }
30814    this.update_layer_order();
30815    setTimeout(() => {
30816      Assets.cache.reset();
30817      this.app.renderer.textureGC.run();
30818    }, 100);
30819  }
30820  /**
30821   * Helper method to resize the background layer.
30822   */
30823  _resize_background_layer(oldBackgroundLayer, newWidth, newHeight, scale, calculateOffset) {
30824    if (!oldBackgroundLayer) {
30825      return this.create_background_layer(newWidth, newHeight);
30826    }
30827    let backgroundImage = oldBackgroundLayer.children.find(
30828      (child) => child instanceof Sprite && child.texture !== oldBackgroundLayer.children[0]?.texture
30829    );
30830    const newBackgroundLayer = this.create_background_layer(
30831      newWidth,
30832      newHeight
30833    );
30834    if (backgroundImage) {
30835      const newBgImage = new Sprite(backgroundImage.texture);
30836      newBgImage.width = backgroundImage.width;
30837      newBgImage.height = backgroundImage.height;
30838      if (scale) {
30839        newBgImage.width = newWidth;
30840        newBgImage.height = newHeight;
30841        newBgImage.position.set(0, 0);
30842      } else {
30843        const { offsetX, offsetY } = calculateOffset();
30844        newBgImage.position.set(
30845          backgroundImage.x + offsetX,
30846          backgroundImage.y + offsetY
30847        );
30848      }
30849      newBackgroundLayer.addChild(newBgImage);
30850    }
30851    return newBackgroundLayer;
30852  }
30853  /**
30854   * Helper method to resize a single regular layer.
30855   */
30856  _resize_single_layer(oldData, newWidth, newHeight, scale, calculateOffset) {
30857    if (!oldData.texture) {
30858      console.warn(
30859        `No texture found for layer ${oldData.id}, skipping cleanup.`
30860      );
30861      if (oldData.container && !oldData.container.destroyed) {
30862        if (this.image_container.children.includes(oldData.container)) {
30863          this.image_container.removeChild(oldData.container);
30864        }
30865        oldData.container.destroy({ children: true });
30866      }
30867      return null;
30868    }
30869    const newContainer = this.create_layer({
30870      width: newWidth,
30871      height: newHeight,
30872      layer_name: oldData.name,
30873      user_created: oldData.user_created
30874    });
30875    const newLayer = this.layers[this.layers.length - 1];
30876    newLayer.id = oldData.id;
30877    const newTexture = this.draw_textures.get(newContainer);
30878    if (!newTexture) {
30879      console.error(
30880        `Failed to get texture for newly created layer ${newLayer.id}. Cleaning up.`
30881      );
30882      if (newContainer && !newContainer.destroyed) {
30883        if (this.image_container.children.includes(newContainer)) {
30884          this.image_container.removeChild(newContainer);
30885        }
30886        newContainer.destroy({ children: true });
30887        const idx = this.layers.findIndex((l) => l.container === newContainer);
30888        if (idx > -1) this.layers.splice(idx, 1);
30889        this.draw_textures.delete(newContainer);
30890      }
30891      if (oldData.texture && !oldData.texture.destroyed)
30892        oldData.texture.destroy(true);
30893      if (oldData.container && !oldData.container.destroyed)
30894        oldData.container.destroy({ children: true });
30895      return null;
30896    }
30897    this.app.renderer.clear({ target: newTexture, clearColor: [0, 0, 0, 0] });
30898    const sprite = new Sprite(oldData.texture);
30899    if (scale) {
30900      sprite.width = newWidth;
30901      sprite.height = newHeight;
30902      sprite.position.set(0, 0);
30903    } else {
30904      const { offsetX, offsetY } = calculateOffset();
30905      sprite.position.set(offsetX, offsetY);
30906    }
30907    this.app.renderer.render(sprite, { renderTexture: newTexture });
30908    sprite.destroy();
30909    if (oldData.texture && !oldData.texture.destroyed) {
30910      oldData.texture.destroy(true);
30911    }
30912    if (oldData.container && !oldData.container.destroyed) {
30913      if (this.image_container.children.includes(oldData.container)) {
30914        this.image_container.removeChild(oldData.container);
30915      }
30916      oldData.container.destroy({ children: true });
30917    }
30918    return newLayer;
30919  }
30920  async get_blobs(width, height) {
30921    const blobs = {
30922      background: await get_canvas_blob(
30923        this.app.renderer,
30924        this.background_layer,
30925        {
30926          width,
30927          height,
30928          x: 0,
30929          y: 0
30930        }
30931      ),
30932      layers: await Promise.all(
30933        this.layers.map(async (layer) => {
30934          const blob = await get_canvas_blob(
30935            this.app.renderer,
30936            layer.container,
30937            {
30938              width,
30939              height,
30940              x: 0,
30941              y: 0
30942            }
30943          );
30944          if (blob) {
30945            return blob;
30946          }
30947          return null;
30948        })
30949      ),
30950      composite: await get_canvas_blob(
30951        this.app.renderer,
30952        this.image_container,
30953        {
30954          width,
30955          height,
30956          x: 0,
30957          y: 0
30958        }
30959      )
30960    };
30961    return blobs;
30962  }
30963  reset_layers(width, height, persist = false) {
30964    const _layers_to_recreate = persist ? this.layers.map((layer) => [layer.name, layer.id]) : this.layer_options.layers.map((layer) => [layer, void 0]);
30965    this.layers.forEach((layer) => {
30966      this.delete_layer(layer.id);
30967    });
30968    for (const [layer_name, layer_id] of _layers_to_recreate) {
30969      this.create_layer({
30970        width,
30971        height,
30972        layer_name,
30973        user_created: this.layer_options.layers.find((l) => l === layer_name) ? false : true,
30974        layer_id
30975      });
30976    }
30977    if (!persist) {
30978      this.active_layer = this.layers[0].container;
30979      this.active_layer_id = this.layers[0].id;
30980    } else {
30981      this.active_layer = this.layers.find((l) => l.id === this.active_layer_id)?.container || this.layers[0]?.container;
30982      if (!this.active_layer) return;
30983    }
30984    this.layer_store.update((state) => ({
30985      active_layer: this.active_layer_id || this.layers[0].id,
30986      layers: this.layers
30987    }));
30988  }
30989  init_layers(width, height) {
30990    for (const layer of this.layers) {
30991      this.delete_layer(layer.id);
30992    }
30993    let i = 0;
30994    for (const layer of this.layer_options.layers) {
30995      this.create_layer({
30996        width,
30997        height,
30998        layer_name: layer,
30999        user_created: false,
31000        layer_id: `layer-${i}`
31001      });
31002      i++;
31003    }
31004    this.active_layer = this.layers[0].container;
31005    this.active_layer_id = this.layers[0].id;
31006    this.layer_store.update((_layers) => ({
31007      active_layer: this.layers[0].id,
31008      layers: this.layers
31009    }));
31010  }
31011}
31012class AddLayerCommand {
31013  constructor(context, options) {
31014    this.context = context;
31015    this.width = options.width;
31016    this.height = options.height;
31017    this.layer_name = options.layer_name || `Layer ${this.context.layer_manager.get_layers().length + 1}`;
31018    this.user_created = options.user_created;
31019    this.layer_id = options.layer_id || Math.random().toString(36).substring(2, 15);
31020    this.make_active = options.make_active || false;
31021    this.name = "AddLayer";
31022    const current_layers = this.context.layer_manager.get_layers();
31023    const current_active = current_layers.find(
31024      (l) => l.container === this.context.layer_manager.get_active_layer()
31025    );
31026    this.previous_active_layer = current_active?.id || null;
31027  }
31028  layer_id;
31029  layer_name;
31030  width;
31031  height;
31032  user_created;
31033  make_active;
31034  previous_active_layer = null;
31035  name;
31036  async execute(context) {
31037    if (context) {
31038      this.context = context;
31039    }
31040    this.context.layer_manager.create_layer({
31041      width: this.width,
31042      height: this.height,
31043      layer_name: this.layer_name,
31044      user_created: this.user_created,
31045      layer_id: this.layer_id,
31046      make_active: this.make_active
31047    });
31048  }
31049  async undo() {
31050    this.context.layer_manager.delete_layer(this.layer_id);
31051    if (this.previous_active_layer) {
31052      this.context.layer_manager.set_active_layer(this.previous_active_layer);
31053    }
31054  }
31055}
31056class RemoveLayerCommand {
31057  constructor(context, layer_id) {
31058    this.context = context;
31059    this.name = "RemoveLayer";
31060    const layers = this.context.layer_manager.get_layers();
31061    const layer_to_remove = layers.find((l) => l.id === layer_id);
31062    if (!layer_to_remove) {
31063      throw new Error(`Layer with ID ${layer_id} not found`);
31064    }
31065    const active_layer = this.context.layer_manager.get_active_layer();
31066    const was_active = layer_to_remove.container === active_layer;
31067    if (was_active) {
31068      const layer_index = layers.findIndex((l) => l.id === layer_id);
31069      const next_active = layers[Math.max(0, layer_index - 1)];
31070      this.previous_active_layer = next_active?.id || null;
31071    }
31072    this.layer_data = {
31073      id: layer_to_remove.id,
31074      name: layer_to_remove.name,
31075      user_created: layer_to_remove.user_created,
31076      visible: layer_to_remove.visible,
31077      was_active
31078    };
31079    this.captureTextureData(layer_id);
31080  }
31081  layer_data;
31082  previous_active_layer = null;
31083  texture_copy = null;
31084  name;
31085  /**
31086   * Create a direct copy of the layer's texture
31087   */
31088  captureTextureData(layer_id) {
31089    const layer_textures = this.context.layer_manager.get_layer_textures(layer_id);
31090    if (!layer_textures) return;
31091    try {
31092      const original_texture = layer_textures.draw;
31093      const texture_copy = RenderTexture.create({
31094        width: original_texture.width,
31095        height: original_texture.height,
31096        resolution: window.devicePixelRatio || 1
31097      });
31098      const sprite = new Sprite(original_texture);
31099      this.context.app.renderer.render(sprite, { renderTexture: texture_copy });
31100      this.texture_copy = texture_copy;
31101      sprite.destroy();
31102    } catch (e) {
31103      console.error("Failed to copy layer texture:", e);
31104      this.texture_copy = null;
31105    }
31106  }
31107  async execute(context) {
31108    if (context) {
31109      this.context = context;
31110    }
31111    this.context.layer_manager.delete_layer(this.layer_data.id);
31112  }
31113  async undo() {
31114    const dimensions = get$1(this.context.dimensions);
31115    this.context.layer_manager.create_layer({
31116      width: dimensions.width,
31117      height: dimensions.height,
31118      layer_name: this.layer_data.name,
31119      user_created: this.layer_data.user_created,
31120      layer_id: this.layer_data.id,
31121      make_active: this.layer_data.was_active
31122    });
31123    if (this.texture_copy) {
31124      try {
31125        const layer_textures = this.context.layer_manager.get_layer_textures(
31126          this.layer_data.id
31127        );
31128        if (layer_textures) {
31129          const sprite = new Sprite(this.texture_copy);
31130          this.context.app.renderer.render(sprite, {
31131            renderTexture: layer_textures.draw
31132          });
31133          sprite.destroy();
31134        }
31135      } catch (e) {
31136        console.error("Failed to restore layer content:", e);
31137      }
31138    }
31139    if (!this.layer_data.visible) {
31140      this.context.layer_manager.toggle_layer_visibility(this.layer_data.id);
31141    }
31142    if (!this.layer_data.was_active && this.previous_active_layer) {
31143      this.context.layer_manager.set_active_layer(this.previous_active_layer);
31144    }
31145  }
31146  /**
31147   * Clean up resources when the command is no longer needed
31148   * This would need to be called by a garbage collection mechanism
31149   */
31150  destroy() {
31151    if (this.texture_copy) {
31152      this.texture_copy.destroy();
31153      this.texture_copy = null;
31154    }
31155  }
31156}
31157class ReorderLayerCommand {
31158  constructor(context, layer_id, direction) {
31159    this.context = context;
31160    this.layer_id = layer_id;
31161    this.direction = direction;
31162    this.name = "ReorderLayer";
31163    const layers = this.context.layer_manager.get_layers();
31164    this.original_order = layers.map((l) => l.id);
31165    const index = layers.findIndex((l) => l.id === layer_id);
31166    if (index === -1) {
31167      throw new Error(`Layer with ID ${layer_id} not found`);
31168    }
31169    const new_index = direction === "up" ? index - 1 : index + 1;
31170    if (new_index < 0 || new_index >= layers.length) {
31171      this.new_order = [...this.original_order];
31172    } else {
31173      this.new_order = [...this.original_order];
31174      [this.new_order[index], this.new_order[new_index]] = [
31175        this.new_order[new_index],
31176        this.new_order[index]
31177      ];
31178    }
31179  }
31180  original_order;
31181  new_order;
31182  layer_id;
31183  direction;
31184  name;
31185  async execute(context) {
31186    if (context) {
31187      this.context = context;
31188    }
31189    this.context.layer_manager.move_layer(this.layer_id, this.direction);
31190  }
31191  async undo() {
31192    const current_layers = this.context.layer_manager.get_layers();
31193    if (this.original_order.join(",") === current_layers.map((l) => l.id).join(",")) {
31194      return;
31195    }
31196    const layer_index = current_layers.findIndex((l) => l.id === this.layer_id);
31197    if (layer_index === -1) return;
31198    const original_index = this.original_order.indexOf(this.layer_id);
31199    const move_direction = layer_index > original_index ? "up" : "down";
31200    this.context.layer_manager.move_layer(this.layer_id, move_direction);
31201  }
31202}
31203
31204const core_tool_map = {
31205  image: () => new ImageTool(),
31206  zoom: () => new ZoomTool()
31207};
31208const spring_config = {
31209  stiffness: 0.45,
31210  damping: 0.8
31211};
31212class EditorState {
31213  state;
31214  scale;
31215  position;
31216  subscribers;
31217  constructor(editor) {
31218    if (!(editor instanceof ImageEditor)) {
31219      throw new Error("EditorState must be created by ImageEditor");
31220    }
31221    this.scale = 1;
31222    this.position = { x: 0, y: 0 };
31223    this.subscribers = /* @__PURE__ */ new Set();
31224    this.state = Object.freeze({
31225      get position() {
31226        return { ...this.position };
31227      },
31228      get scale() {
31229        return this.scale;
31230      },
31231      get current_tool() {
31232        return this.current_tool;
31233      },
31234      get current_subtool() {
31235        return this.current_subtool;
31236      },
31237      subscribe: this.subscribe.bind(this)
31238    });
31239  }
31240  _set_position(x, y) {
31241    const oldPosition = { ...this.position };
31242    this.position = { x, y };
31243    this._notify_subscribers("position", oldPosition, this.position);
31244  }
31245  _set_scale(scale) {
31246    const oldScale = this.scale;
31247    this.scale = scale;
31248    this._notify_subscribers("scale", oldScale, scale);
31249  }
31250  _notify_subscribers(property, oldValue, newValue) {
31251    this.subscribers.forEach((callback) => {
31252      callback({
31253        property,
31254        oldValue,
31255        newValue,
31256        timestamp: Date.now()
31257      });
31258    });
31259  }
31260  subscribe(callback) {
31261    this.subscribers.add(callback);
31262    return () => this.subscribers.delete(callback);
31263  }
31264}
31265class ImageEditor {
31266  ready;
31267  background_image_present = writable(false);
31268  min_zoom = writable(true);
31269  app;
31270  ui_container;
31271  image_container;
31272  command_manager;
31273  layer_manager;
31274  tools = /* @__PURE__ */ new Map();
31275  current_tool;
31276  current_subtool;
31277  target_element;
31278  width;
31279  height;
31280  original_width;
31281  original_height;
31282  dimensions;
31283  scale;
31284  position;
31285  state;
31286  scale_value = 1;
31287  position_value = { x: 0, y: 0 };
31288  dimensions_value = {
31289    width: 0,
31290    height: 0
31291  };
31292  layers = writable({
31293    active_layer: "",
31294    layers: []
31295  });
31296  outline_container;
31297  outline_graphics;
31298  background_image;
31299  ready_resolve;
31300  event_callbacks = /* @__PURE__ */ new Map();
31301  fixed_canvas;
31302  dark;
31303  border_region;
31304  layer_options;
31305  overlay_container;
31306  overlay_graphics;
31307  pad_bottom;
31308  theme_mode;
31309  constructor(options) {
31310    this.pad_bottom = options.pad_bottom || 0;
31311    this.dark = options.dark || false;
31312    this.theme_mode = options.theme_mode || "dark";
31313    this.target_element = options.target_element;
31314    this.width = options.width;
31315    this.height = options.height;
31316    this.original_width = options.width;
31317    this.original_height = options.height;
31318    this.command_manager = new CommandManager();
31319    this.ready = new Promise((resolve) => {
31320      this.ready_resolve = resolve;
31321    });
31322    this.fixed_canvas = options.fixed_canvas || false;
31323    this.tools = new Map(
31324      options.tools.map((tool) => {
31325        if (typeof tool === "string") {
31326          return [tool, core_tool_map[tool]()];
31327        }
31328        return [tool.name, tool];
31329      })
31330    );
31331    for (const tool of this.tools.values()) {
31332      if (tool?.on) {
31333        tool.on("change", () => {
31334          this.notify("change");
31335        });
31336      }
31337    }
31338    this.dimensions = spring(
31339      { width: this.width, height: this.height },
31340      spring_config
31341    );
31342    this.scale = spring(1, spring_config);
31343    this.position = spring({ x: 0, y: 0 }, spring_config);
31344    this.state = new EditorState(this);
31345    this.border_region = options.border_region || 0;
31346    this.layer_options = options.layer_options || {
31347      allow_additional_layers: true,
31348      layers: ["Layer 1"],
31349      disabled: false
31350    };
31351    this.scale.subscribe((scale) => {
31352      this.state._set_scale(scale);
31353    });
31354    this.position.subscribe((position) => {
31355      this.state._set_position(position.x, position.y);
31356    });
31357    this.init();
31358  }
31359  get context() {
31360    const editor = this;
31361    return {
31362      app: this.app,
31363      ui_container: this.ui_container,
31364      image_container: this.image_container,
31365      get background_image() {
31366        return editor.background_image;
31367      },
31368      pad_bottom: this.pad_bottom,
31369      command_manager: this.command_manager,
31370      layer_manager: this.layer_manager,
31371      dimensions: { subscribe: this.dimensions.subscribe },
31372      scale: { subscribe: this.scale.subscribe },
31373      position: { subscribe: this.position.subscribe },
31374      set_image_properties: this.set_image_properties.bind(this),
31375      execute_command: this.execute_command.bind(this),
31376      resize_canvas: this.resize_canvas.bind(this),
31377      reset: this.reset.bind(this),
31378      set_background_image: this.set_background_image.bind(this)
31379    };
31380  }
31381  async init() {
31382    const container_box = this.target_element.getBoundingClientRect();
31383    new ResizeObserver((entries) => {
31384      entries.forEach((entry) => {
31385        this.resize_canvas(
31386          entry.contentBoxSize[0].inlineSize,
31387          entry.contentBoxSize[0].blockSize
31388        );
31389      });
31390    }).observe(this.target_element);
31391    this.app = new Application();
31392    if (!this.dark) {
31393      globalThis.__PIXI_APP__ = this.app;
31394    }
31395    await this.app.init({
31396      width: container_box.width,
31397      height: container_box.height,
31398      backgroundAlpha: this.dark ? 0 : 1,
31399      backgroundColor: this.theme_mode === "dark" ? "#27272a" : "#ffffff",
31400      resolution: window.devicePixelRatio,
31401      autoDensity: true,
31402      antialias: true,
31403      powerPreference: "high-performance"
31404    });
31405    const canvas = this.app.canvas;
31406    canvas.style.background = "transparent";
31407    this.setup_containers();
31408    this.layer_manager.create_background_layer(this.width, this.height);
31409    this.layer_manager.init_layers(this.width, this.height);
31410    for (const tool of this.tools.values()) {
31411      await tool.setup(this.context, this.current_tool, this.current_subtool);
31412    }
31413    const zoom = this.tools.get("zoom");
31414    if (zoom) {
31415      zoom.min_zoom.subscribe((is_min_zoom) => {
31416        this.min_zoom.set(is_min_zoom);
31417      });
31418    }
31419    this.target_element.appendChild(canvas);
31420    this.target_element.style.background = "transparent";
31421    this.dimensions.subscribe((dimensions) => {
31422      this.dimensions_value = dimensions;
31423      this.image_container.width = dimensions.width;
31424      this.image_container.height = dimensions.height;
31425    });
31426    this.scale.subscribe((scale) => {
31427      this.scale_value = scale;
31428    });
31429    this.position.subscribe((position) => {
31430      this.position_value = position;
31431    });
31432    this.app.ticker.add(() => {
31433      this.image_container.scale.set(this.scale_value);
31434      this.image_container.position.set(
31435        this.position_value.x,
31436        this.position_value.y
31437      );
31438      const effective_width = this.dimensions_value.width * this.scale_value;
31439      const effective_height = this.dimensions_value.height * this.scale_value;
31440      const local_x = Math.round(
31441        this.image_container.position.x - this.outline_container.position.x
31442      );
31443      const local_y = Math.round(
31444        this.image_container.position.y - this.outline_container.position.y
31445      );
31446      this.overlay_container.position.set(
31447        this.outline_container.position.x,
31448        this.outline_container.position.y
31449      );
31450      this.outline_graphics.clear();
31451      this.outline_graphics.rect(local_x, local_y, effective_width, effective_height).fill({
31452        color: this.dark ? 3355443 : 16777215,
31453        alpha: 1
31454      });
31455      if (this.border_region > 0) {
31456        const scaled_border = this.border_region * this.scale_value;
31457        const border_x = local_x + scaled_border - 1;
31458        const border_y = local_y + scaled_border - 1;
31459        const border_width = effective_width - scaled_border * 2 + 1;
31460        const border_height = effective_height - scaled_border * 2 + 1;
31461        this.overlay_graphics.clear();
31462        this.overlay_graphics.rect(border_x, border_y, border_width, border_height).stroke({
31463          color: 10066329,
31464          width: 1,
31465          alpha: 0,
31466          pixelLine: true
31467        });
31468        const dashLength = 5;
31469        const gapLength = 5;
31470        const totalLength = dashLength + gapLength;
31471        const lineColor = 10066329;
31472        for (let x = border_x; x < border_x + border_width; x += totalLength) {
31473          this.overlay_graphics.rect(
31474            x,
31475            border_y,
31476            Math.min(dashLength, border_x + border_width - x),
31477            1
31478          ).fill({ color: lineColor, alpha: 0.7 });
31479          this.overlay_graphics.rect(
31480            x,
31481            border_y + border_height,
31482            Math.min(dashLength, border_x + border_width - x),
31483            1
31484          ).fill({ color: lineColor, alpha: 0.7 });
31485        }
31486        for (let y = border_y; y < border_y + border_height; y += totalLength) {
31487          this.overlay_graphics.rect(
31488            border_x,
31489            y,
31490            1,
31491            Math.min(dashLength, border_y + border_height - y)
31492          ).fill({ color: lineColor, alpha: 0.7 });
31493          this.overlay_graphics.rect(
31494            border_x + border_width,
31495            y,
31496            1,
31497            Math.min(dashLength, border_y + border_height - y)
31498          ).fill({ color: lineColor, alpha: 0.7 });
31499        }
31500      } else {
31501        this.overlay_graphics.clear();
31502      }
31503    });
31504    this.ready_resolve();
31505  }
31506  setup_containers() {
31507    this.image_container = new Container({
31508      eventMode: "static",
31509      sortableChildren: true
31510    });
31511    this.image_container.width = this.width;
31512    this.image_container.height = this.height;
31513    this.app.stage.sortableChildren = true;
31514    this.app.stage.alpha = 1;
31515    this.app.stage.addChild(this.image_container);
31516    this.image_container.scale.set(1);
31517    this.ui_container = new Container({
31518      eventMode: "static"
31519    });
31520    this.app.stage.addChild(this.ui_container);
31521    this.ui_container.width = this.width;
31522    this.ui_container.height = this.height;
31523    this.outline_container = new Container();
31524    this.outline_container.zIndex = -10;
31525    this.outline_graphics = new Graphics();
31526    this.outline_graphics.rect(0, 0, this.width, this.height).fill({
31527      color: this.dark ? 3355443 : 16777215,
31528      alpha: 0
31529    });
31530    if (!this.dark) {
31531      const blurFilter = new DropShadowFilter({
31532        alpha: 0.1,
31533        blur: 2,
31534        color: 0,
31535        offset: { x: 0, y: 0 },
31536        quality: 4,
31537        shadowOnly: false
31538      });
31539      this.outline_graphics.filters = [blurFilter];
31540    }
31541    this.outline_container.addChild(this.outline_graphics);
31542    this.app.stage.addChild(this.outline_container);
31543    this.overlay_container = new Container();
31544    this.app.stage.addChild(this.overlay_container);
31545    this.overlay_container.width = this.width;
31546    this.overlay_container.height = this.height;
31547    this.overlay_container.zIndex = 999;
31548    this.overlay_container.eventMode = "static";
31549    this.overlay_container.interactiveChildren = true;
31550    this.overlay_graphics = new Graphics();
31551    this.overlay_container.addChild(this.overlay_graphics);
31552    this.overlay_graphics.rect(0, 0, this.width, this.height).fill({
31553      color: 0,
31554      alpha: 0
31555    });
31556    const app_center_x = this.app.screen.width / 2;
31557    const app_center_y = this.app.screen.height / 2;
31558    this.image_container.position.set(app_center_x, app_center_y);
31559    this.outline_container.position.set(app_center_x, app_center_y);
31560    this.layer_manager = new LayerManager(
31561      this.image_container,
31562      this.app,
31563      this.fixed_canvas,
31564      this.dark,
31565      this.border_region,
31566      this.layer_options
31567    );
31568    this.layers = this.layer_manager.layer_store;
31569  }
31570  resize_canvas(width, height) {
31571    if (this.app.renderer) {
31572      this.app.renderer.resize(width, height);
31573    }
31574    const app_center_x = width / 2;
31575    const app_center_y = height / 2;
31576    if (this.image_container) {
31577      this.image_container.position.set(app_center_x, app_center_y);
31578    }
31579    if (this.outline_container) {
31580      this.outline_container.position.set(app_center_x, app_center_y);
31581    }
31582  }
31583  reset() {
31584    const zoom = this.tools.get("zoom");
31585    if (zoom) {
31586      zoom.cleanup();
31587      zoom.setup(this.context, this.current_tool, this.current_subtool);
31588    }
31589    const brush = this.tools.get("brush");
31590    if (brush) {
31591      this.set_tool("draw");
31592    }
31593  }
31594  async set_image_properties(properties) {
31595    let hard = typeof properties.animate !== "undefined" ? !properties.animate : true;
31596    if (properties.position) {
31597      const pos = this.position.set(properties.position, { hard });
31598      if (hard) {
31599        await pos;
31600      }
31601    }
31602    if (properties.scale) {
31603      const scale = this.scale.set(properties.scale, { hard });
31604      if (hard) {
31605        await scale;
31606      }
31607    }
31608    if (properties.width && properties.height) {
31609      this.width = properties.width;
31610      this.height = properties.height;
31611      const dimensions = this.dimensions.set(
31612        { width: properties.width, height: properties.height },
31613        { hard }
31614      );
31615      if (hard) {
31616        await dimensions;
31617      }
31618    }
31619  }
31620  async execute_command(command) {
31621    await this.command_manager.execute(command, this.context);
31622  }
31623  undo() {
31624    this.command_manager.undo();
31625    this.notify("change");
31626  }
31627  redo() {
31628    this.command_manager.redo(this.context);
31629    this.notify("change");
31630  }
31631  async add_image({
31632    image,
31633    resize = true
31634  }) {
31635    const image_tool = this.tools.get("image");
31636    await image_tool.add_image({
31637      image,
31638      fixed_canvas: this.fixed_canvas,
31639      border_region: this.border_region
31640    });
31641  }
31642  /**
31643   * Adds an image from a URL as the background layer
31644   * @param url The URL of the image to add
31645   */
31646  async add_image_from_url(url) {
31647    const image_tool = this.tools.get("image");
31648    const texture = await Assets.load(url);
31649    await image_tool.add_image({
31650      image: texture,
31651      fixed_canvas: this.fixed_canvas,
31652      border_region: this.border_region
31653    });
31654    const resize_tool = this.tools.get("resize");
31655    if (resize_tool && typeof resize_tool.set_border_region === "function") {
31656      resize_tool.set_border_region(this.border_region);
31657    }
31658  }
31659  set_tool(tool) {
31660    this.current_tool = tool;
31661    for (const tool2 of this.tools.values()) {
31662      tool2.set_tool(this.current_tool, this.current_subtool);
31663    }
31664  }
31665  set_subtool(subtool) {
31666    this.current_subtool = subtool;
31667    for (const tool of this.tools.values()) {
31668      tool.set_tool(this.current_tool, this.current_subtool);
31669    }
31670  }
31671  set_background_image(image) {
31672    this.background_image = image;
31673  }
31674  async reset_canvas() {
31675    this.width = this.original_width;
31676    this.height = this.original_height;
31677    this.layer_manager.reset_layers(this.width, this.height);
31678    this.background_image = void 0;
31679    this.background_image_present.set(false);
31680    this.command_manager.reset();
31681    await this.set_image_properties({
31682      width: this.width,
31683      height: this.height,
31684      scale: 1,
31685      position: { x: 0, y: 0 },
31686      animate: false
31687    });
31688    this.layer_manager.create_background_layer(this.width, this.height);
31689    for (const tool of this.tools.values()) {
31690      tool.cleanup();
31691      tool.setup(this.context, this.current_tool, this.current_subtool);
31692    }
31693    const zoom_tool = this.tools.get("zoom");
31694    if (zoom_tool) {
31695      zoom_tool.min_zoom.subscribe((is_min_zoom) => {
31696        this.min_zoom.set(is_min_zoom);
31697      });
31698    }
31699    this.notify("change");
31700  }
31701  add_layer() {
31702    const add_layer_command = new AddLayerCommand(this.context, {
31703      width: this.width,
31704      height: this.height,
31705      user_created: true,
31706      make_active: true
31707    });
31708    this.execute_command(add_layer_command);
31709    this.notify("change");
31710  }
31711  /**
31712   * Adds a new layer with an image loaded from a URL
31713   * @param layer_urls The URLs of the images to load
31714   * @returns A Promise that resolves when all layers are added
31715   */
31716  async add_layers_from_url(layer_urls) {
31717    this.command_manager.reset();
31718    const _layers = this.layer_manager.get_layers();
31719    _layers.forEach((l) => this.layer_manager.delete_layer(l.id));
31720    if (layer_urls === void 0 || layer_urls.length === 0) {
31721      this.layer_manager.create_layer({
31722        width: this.width,
31723        height: this.height,
31724        layer_name: void 0,
31725        user_created: false
31726      });
31727      return;
31728    }
31729    const created_layer_ids = [];
31730    for await (const url of layer_urls) {
31731      const layer_id = await this.layer_manager.add_layer_from_url(url);
31732      if (layer_id) {
31733        created_layer_ids.push(layer_id);
31734      }
31735    }
31736    if (created_layer_ids.length > 0) {
31737      this.layer_manager.set_active_layer(created_layer_ids[0]);
31738    }
31739    const resize_tool = this.tools.get("resize");
31740    if (resize_tool && typeof resize_tool.set_border_region === "function") {
31741      resize_tool.set_border_region(this.border_region);
31742    }
31743    this.notify("change");
31744    this.notify("input");
31745  }
31746  set_layer(id) {
31747    this.layer_manager.set_active_layer(id);
31748    this.notify("change");
31749  }
31750  move_layer(id, direction) {
31751    const reorder_layer_command = new ReorderLayerCommand(
31752      this.context,
31753      id,
31754      direction
31755    );
31756    this.execute_command(reorder_layer_command);
31757    this.notify("change");
31758  }
31759  delete_layer(id) {
31760    const remove_layer_command = new RemoveLayerCommand(this.context, id);
31761    this.execute_command(remove_layer_command);
31762    this.notify("change");
31763  }
31764  modify_canvas_size(width, height, anchor, scale) {
31765    const oldWidth = this.width;
31766    const oldHeight = this.height;
31767    this.layer_manager.resize_all_layers(
31768      width,
31769      height,
31770      scale,
31771      anchor,
31772      oldWidth,
31773      oldHeight
31774    );
31775    this.width = width;
31776    this.height = height;
31777    this.set_image_properties({
31778      width,
31779      height,
31780      scale: 1,
31781      position: { x: 0, y: 0 },
31782      animate: false
31783    });
31784    this.notify("change");
31785  }
31786  async get_blobs() {
31787    const blobs = await this.layer_manager.get_blobs(this.width, this.height);
31788    return blobs;
31789  }
31790  on(event, callback) {
31791    this.event_callbacks.set(event, [
31792      ...this.event_callbacks.get(event) || [],
31793      callback
31794    ]);
31795  }
31796  off(event, callback) {
31797    this.event_callbacks.set(
31798      event,
31799      this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []
31800    );
31801  }
31802  notify(event) {
31803    for (const callback of this.event_callbacks.get(event) || []) {
31804      callback();
31805    }
31806  }
31807  destroy() {
31808    if (!this.app) return;
31809    this.app?.destroy();
31810  }
31811  resize(width, height) {
31812    this.set_image_properties({
31813      width,
31814      height
31815    });
31816    this.reset();
31817  }
31818  async get_crop_bounds() {
31819    const crop_tool = this.tools.get("crop");
31820    const crop_bounds = crop_tool.get_crop_bounds();
31821    const image = await crop_tool.get_image();
31822    return {
31823      image,
31824      ...crop_bounds
31825    };
31826  }
31827  get background_image_sprite() {
31828    return this.background_image;
31829  }
31830  set_layer_options(layer_options) {
31831    this.layer_options = layer_options;
31832    this.layer_manager.set_layer_options(
31833      layer_options,
31834      this.width,
31835      this.height
31836    );
31837  }
31838  toggle_layer_visibility(id) {
31839    this.layer_manager.toggle_layer_visibility(id);
31840  }
31841}
31842
31843class CropTool {
31844  name = "crop";
31845  image_editor_context;
31846  current_tool = "image";
31847  current_subtool = "crop";
31848  // Constants
31849  CORNER_SIZE = 25;
31850  LINE_THICKNESS = 5;
31851  HANDLE_COLOR = 0;
31852  HIT_AREA_SIZE = 40;
31853  // State
31854  is_dragging = false;
31855  selected_handle = null;
31856  active_corner_index = -1;
31857  active_edge_index = -1;
31858  last_pointer_position = null;
31859  crop_bounds = { x: 0, y: 0, width: 0, height: 0 };
31860  crop_ui_container = null;
31861  crop_mask = null;
31862  dimensions = {
31863    width: 0,
31864    height: 0
31865  };
31866  position = { x: 0, y: 0 };
31867  scale = 1;
31868  is_dragging_window = false;
31869  drag_start_position = null;
31870  drag_start_bounds = null;
31871  background_image_watcher = null;
31872  has_been_manually_changed = false;
31873  event_callbacks = /* @__PURE__ */ new Map();
31874  /**
31875   * @method setup
31876   * @async
31877   * @description Sets up the crop tool with the given context and state
31878   * @param {ImageEditorContext} context - The image editor context
31879   * @param {ToolbarTool} tool - The current tool
31880   * @param {Subtool} subtool - The current subtool
31881   * @returns {Promise<void>}
31882   */
31883  async setup(context, tool, subtool) {
31884    this.image_editor_context = context;
31885    this.current_tool = tool;
31886    this.current_subtool = subtool;
31887    context.dimensions.subscribe((dimensions) => {
31888      this.dimensions = dimensions;
31889      this.crop_bounds = {
31890        x: 0,
31891        y: 0,
31892        width: dimensions.width,
31893        height: dimensions.height
31894      };
31895      if (this.crop_ui_container) {
31896        this.update_crop_ui();
31897      }
31898      this.set_crop_mask();
31899      this.update_crop_mask();
31900    });
31901    context.position.subscribe((position) => {
31902      this.position = position;
31903    });
31904    context.scale.subscribe((scale) => {
31905      this.scale = scale;
31906    });
31907    this.background_image_watcher = () => {
31908      if (this.crop_mask && this.current_tool === "image" && this.current_subtool === "crop") {
31909        if (this.image_editor_context.background_image) {
31910          this.image_editor_context.background_image.setMask(this.crop_mask);
31911        }
31912      }
31913    };
31914    this.image_editor_context.app.ticker.add(this.background_image_watcher);
31915    await this.init_crop_ui();
31916    this.setup_event_listeners();
31917    const isCropActive = tool === "image" && subtool === "crop";
31918    if (this.crop_ui_container) {
31919      this.crop_ui_container.visible = isCropActive;
31920    }
31921  }
31922  /**
31923   * @method cleanup
31924   * @description Cleans up the crop tool resources and removes event listeners
31925   * @returns {void}
31926   */
31927  cleanup() {
31928    if (this.crop_ui_container) {
31929      this.image_editor_context.app.stage.removeChild(this.crop_ui_container);
31930    }
31931    if (this.crop_mask) {
31932      if (this.crop_mask.parent) {
31933        this.crop_mask.parent.removeChild(this.crop_mask);
31934      }
31935      if (this.image_editor_context.background_image && this.image_editor_context.background_image.mask === this.crop_mask) {
31936        this.image_editor_context.background_image.mask = null;
31937      }
31938      if (this.image_editor_context.image_container.mask === this.crop_mask) {
31939        this.image_editor_context.image_container.mask = null;
31940      }
31941    }
31942    this.crop_mask = null;
31943    this.crop_ui_container = null;
31944    if (this.background_image_watcher) {
31945      this.image_editor_context.app.ticker.remove(
31946        this.background_image_watcher
31947      );
31948      this.background_image_watcher = null;
31949    }
31950    this.cleanup_event_listeners();
31951  }
31952  /**
31953   * @method set_tool
31954   * @description Sets the current tool and subtool, updating UI visibility
31955   * @param {ToolbarTool} tool - The tool to set
31956   * @param {Subtool} subtool - The subtool to set
31957   * @returns {void}
31958   */
31959  set_tool(tool, subtool) {
31960    this.current_tool = tool;
31961    this.current_subtool = subtool;
31962    const isCropActive = tool === "image" && subtool === "crop";
31963    if (this.crop_ui_container) {
31964      this.crop_ui_container.visible = isCropActive;
31965    }
31966    if (isCropActive && this.crop_mask) {
31967      this.update_crop_mask();
31968    }
31969  }
31970  /**
31971   * @method set_crop_mask
31972   * @private
31973   * @description Sets up the crop mask for the image
31974   * @returns {void}
31975   */
31976  set_crop_mask() {
31977    if (this.crop_mask) {
31978      if (this.crop_mask.parent) {
31979        this.crop_mask.parent.removeChild(this.crop_mask);
31980      }
31981      if (this.image_editor_context.image_container.mask === this.crop_mask) {
31982        this.image_editor_context.image_container.mask = null;
31983      }
31984      if (this.image_editor_context.background_image && this.image_editor_context.background_image.mask === this.crop_mask) {
31985        this.image_editor_context.background_image.mask = null;
31986      }
31987    }
31988    this.crop_mask = new Graphics();
31989    this.image_editor_context.image_container.addChild(this.crop_mask);
31990  }
31991  /**
31992   * @method init_crop_ui
31993   * @private
31994   * @async
31995   * @description Initializes the crop UI elements and sets up the update loop
31996   * @returns {Promise<void>}
31997   */
31998  async init_crop_ui() {
31999    const { width, height } = this.image_editor_context.background_image?.getLocalBounds() || {
32000      width: false,
32001      height: false
32002    };
32003    this.crop_ui_container = this.make_crop_ui(
32004      this.dimensions.width * this.scale,
32005      this.dimensions.height * this.scale
32006    );
32007    this.crop_ui_container.position.set(this.position.x, this.position.y);
32008    this.crop_ui_container.visible = false;
32009    this.image_editor_context.app.stage.addChild(this.crop_ui_container);
32010    this.set_crop_mask();
32011    this.image_editor_context.app.ticker.add(this.update_crop_ui.bind(this));
32012    this.update_crop_mask();
32013  }
32014  /**
32015   * @method make_crop_ui
32016   * @private
32017   * @description Creates the crop UI with the specified dimensions
32018   * @param {number} width - Width of the crop area
32019   * @param {number} height - Height of the crop area
32020   * @returns {Container} The created crop UI container
32021   */
32022  make_crop_ui(width, height) {
32023    const crop_container = new Container();
32024    crop_container.eventMode = "static";
32025    crop_container.interactiveChildren = true;
32026    const crop_outline = new Graphics().rect(0, 0, width, height).stroke({ width: 1, color: 0, alignment: 0, alpha: 0.5 });
32027    crop_outline.eventMode = "static";
32028    crop_outline.cursor = "move";
32029    crop_outline.hitArea = new Rectangle(0, 0, width, height);
32030    crop_outline.on("pointerdown", this.handle_window_drag_start.bind(this));
32031    crop_container.addChild(crop_outline);
32032    this.create_corner_handles(crop_container, width, height);
32033    this.create_edge_handles(crop_container, width, height);
32034    return crop_container;
32035  }
32036  /**
32037   * @method create_handle
32038   * @private
32039   * @description Creates a handle for the crop UI
32040   * @param {boolean} [is_edge=false] - Whether the handle is an edge handle
32041   * @returns {Container} The created handle container
32042   */
32043  create_handle(is_edge = false) {
32044    const handle = new Container();
32045    handle.eventMode = "static";
32046    const handle_graphics = new Graphics();
32047    if (is_edge) {
32048      handle_graphics.rect(0, 0, this.CORNER_SIZE * 1.5, this.LINE_THICKNESS).fill(this.HANDLE_COLOR);
32049    } else {
32050      handle_graphics.rect(0, 0, this.CORNER_SIZE, this.LINE_THICKNESS).rect(0, 0, this.LINE_THICKNESS, this.CORNER_SIZE).fill(this.HANDLE_COLOR);
32051    }
32052    handle.addChild(handle_graphics);
32053    const hit_size = is_edge ? this.HIT_AREA_SIZE * 1.5 : this.HIT_AREA_SIZE;
32054    handle.hitArea = new Rectangle(
32055      -hit_size / 2 + this.LINE_THICKNESS,
32056      -hit_size / 2 + this.LINE_THICKNESS,
32057      hit_size,
32058      hit_size
32059    );
32060    return handle;
32061  }
32062  /**
32063   * @method create_edge_handles
32064   * @private
32065   * @description Creates edge handles for the crop UI
32066   * @param {Container} container - The container to add handles to
32067   * @param {number} width - Width of the crop area
32068   * @param {number} height - Height of the crop area
32069   * @returns {void}
32070   */
32071  create_edge_handles(container, width, height) {
32072    [-1, 1].forEach((i, index) => {
32073      const handle = this.create_handle(true);
32074      handle.rotation = 0;
32075      handle.on("pointerdown", (event) => {
32076        this.handle_pointer_down(event, handle, -1, index);
32077      });
32078      handle.x = width / 2 - this.CORNER_SIZE * 3 / 2;
32079      handle.y = i < 0 ? -this.LINE_THICKNESS : height;
32080      handle.cursor = "ns-resize";
32081      container.addChild(handle);
32082    });
32083    [-1, 1].forEach((i, index) => {
32084      const handle = this.create_handle(true);
32085      handle.rotation = Math.PI / 2;
32086      handle.on("pointerdown", (event) => {
32087        this.handle_pointer_down(event, handle, -1, index + 2);
32088      });
32089      handle.x = i < 0 ? -this.LINE_THICKNESS : width;
32090      handle.y = height / 2 - this.CORNER_SIZE * 3 / 2;
32091      handle.cursor = "ew-resize";
32092      container.addChild(handle);
32093    });
32094  }
32095  /**
32096   * @method create_corner_handles
32097   * @private
32098   * @description Creates corner handles for the crop UI
32099   * @param {Container} container - The container to add handles to
32100   * @param {number} width - Width of the crop area
32101   * @param {number} height - Height of the crop area
32102   * @returns {void}
32103   */
32104  create_corner_handles(container, width, height) {
32105    const corners = [
32106      { x: 0, y: 0, xScale: 1, yScale: 1, cursor: "nwse-resize" },
32107      // Top-left
32108      { x: width, y: 0, xScale: -1, yScale: 1, cursor: "nesw-resize" },
32109      // Top-right
32110      { x: 0, y: height, xScale: 1, yScale: -1, cursor: "nesw-resize" },
32111      // Bottom-left
32112      { x: width, y: height, xScale: -1, yScale: -1, cursor: "nwse-resize" }
32113      // Bottom-right
32114    ];
32115    corners.forEach(({ x, y, xScale, yScale, cursor }, i) => {
32116      const corner = this.create_handle(false);
32117      corner.x = x - (xScale < 0 ? -this.LINE_THICKNESS : this.LINE_THICKNESS);
32118      corner.y = y - (yScale < 0 ? -this.LINE_THICKNESS : this.LINE_THICKNESS);
32119      corner.scale.set(xScale, yScale);
32120      corner.on("pointerdown", (event) => {
32121        this.handle_pointer_down(event, corner, i, -1);
32122      });
32123      corner.cursor = cursor;
32124      container.addChild(corner);
32125    });
32126  }
32127  /**
32128   * @method handle_pointer_down
32129   * @private
32130   * @description Handles pointer down events on handles
32131   * @param {FederatedPointerEvent} event - The pointer event
32132   * @param {Container} handle - The handle that was clicked
32133   * @param {number} corner_index - Index of the corner (-1 if not a corner)
32134   * @param {number} edge_index - Index of the edge (-1 if not an edge)
32135   * @returns {void}
32136   */
32137  handle_pointer_down(event, handle, corner_index, edge_index) {
32138    if (this.current_subtool !== "crop") return;
32139    event.stopPropagation();
32140    this.is_dragging = true;
32141    this.selected_handle = handle;
32142    this.active_corner_index = corner_index;
32143    this.active_edge_index = edge_index;
32144    const local_pos = this.image_editor_context.image_container.toLocal(
32145      event.global
32146    );
32147    this.last_pointer_position = new Point(local_pos.x, local_pos.y);
32148  }
32149  /**
32150   * updates the crop bounds based on pointer movement
32151   * @param delta - the change in position
32152   */
32153  update_crop_bounds(delta) {
32154    this.has_been_manually_changed = true;
32155    const scaled_delta = new Point(delta.x, delta.y);
32156    const original = {
32157      x: this.crop_bounds.x,
32158      y: this.crop_bounds.y,
32159      width: this.crop_bounds.width,
32160      height: this.crop_bounds.height
32161    };
32162    if (this.active_corner_index !== -1) {
32163      switch (this.active_corner_index) {
32164        case 0:
32165          const newWidth0 = Math.max(20, original.width - scaled_delta.x);
32166          const widthDiff0 = original.width - newWidth0;
32167          this.crop_bounds.width = newWidth0;
32168          this.crop_bounds.x = original.x + widthDiff0;
32169          const newHeight0 = Math.max(20, original.height - scaled_delta.y);
32170          const heightDiff0 = original.height - newHeight0;
32171          this.crop_bounds.height = newHeight0;
32172          this.crop_bounds.y = original.y + heightDiff0;
32173          break;
32174        case 1:
32175          this.crop_bounds.width = Math.max(
32176            20,
32177            original.width + scaled_delta.x
32178          );
32179          const newHeight1 = Math.max(20, original.height - scaled_delta.y);
32180          const heightDiff1 = original.height - newHeight1;
32181          this.crop_bounds.height = newHeight1;
32182          this.crop_bounds.y = original.y + heightDiff1;
32183          break;
32184        case 2:
32185          const newWidth2 = Math.max(20, original.width - scaled_delta.x);
32186          const widthDiff2 = original.width - newWidth2;
32187          this.crop_bounds.width = newWidth2;
32188          this.crop_bounds.x = original.x + widthDiff2;
32189          this.crop_bounds.height = Math.max(
32190            20,
32191            original.height + scaled_delta.y
32192          );
32193          break;
32194        case 3:
32195          this.crop_bounds.width = Math.max(
32196            20,
32197            original.width + scaled_delta.x
32198          );
32199          this.crop_bounds.height = Math.max(
32200            20,
32201            original.height + scaled_delta.y
32202          );
32203          break;
32204      }
32205    } else if (this.active_edge_index !== -1) {
32206      switch (this.active_edge_index) {
32207        case 0:
32208          const newHeight = Math.max(20, original.height - scaled_delta.y);
32209          const heightDiff = original.height - newHeight;
32210          this.crop_bounds.height = newHeight;
32211          this.crop_bounds.y = original.y + heightDiff;
32212          break;
32213        case 1:
32214          this.crop_bounds.height = Math.max(
32215            20,
32216            original.height + scaled_delta.y
32217          );
32218          break;
32219        case 2:
32220          const newWidth = Math.max(20, original.width - scaled_delta.x);
32221          const widthDiff = original.width - newWidth;
32222          this.crop_bounds.width = newWidth;
32223          this.crop_bounds.x = original.x + widthDiff;
32224          break;
32225        case 3:
32226          this.crop_bounds.width = Math.max(
32227            20,
32228            original.width + scaled_delta.x
32229          );
32230          break;
32231      }
32232    }
32233    this.constrain_crop_bounds();
32234    this.update_crop_ui();
32235    this.update_crop_mask();
32236    if (this.crop_mask && this.image_editor_context.background_image) {
32237      this.image_editor_context.background_image.setMask(this.crop_mask);
32238    }
32239  }
32240  /**
32241   * constrains the crop bounds to the image dimensions
32242   */
32243  constrain_crop_bounds() {
32244    ({
32245      x: this.crop_bounds.x,
32246      y: this.crop_bounds.y,
32247      width: this.crop_bounds.width,
32248      height: this.crop_bounds.height
32249    });
32250    this.crop_bounds.width = Math.max(
32251      20,
32252      Math.min(this.crop_bounds.width, this.dimensions.width)
32253    );
32254    this.crop_bounds.height = Math.max(
32255      20,
32256      Math.min(this.crop_bounds.height, this.dimensions.height)
32257    );
32258    const oldX = this.crop_bounds.x;
32259    this.crop_bounds.x = Math.max(
32260      0,
32261      Math.min(
32262        this.crop_bounds.x,
32263        this.dimensions.width - this.crop_bounds.width
32264      )
32265    );
32266    if (this.crop_bounds.x !== oldX && this.active_corner_index === 0 || this.active_edge_index === 2) {
32267      const xDiff = this.crop_bounds.x - oldX;
32268      this.crop_bounds.width -= xDiff;
32269    }
32270    const oldY = this.crop_bounds.y;
32271    this.crop_bounds.y = Math.max(
32272      0,
32273      Math.min(
32274        this.crop_bounds.y,
32275        this.dimensions.height - this.crop_bounds.height
32276      )
32277    );
32278    if (this.crop_bounds.y !== oldY && (this.active_corner_index === 0 || this.active_corner_index === 1 || this.active_edge_index === 0)) {
32279      const yDiff = this.crop_bounds.y - oldY;
32280      this.crop_bounds.height -= yDiff;
32281    }
32282    this.crop_bounds.width = Math.max(
32283      20,
32284      Math.min(
32285        this.crop_bounds.width,
32286        this.dimensions.width - this.crop_bounds.x
32287      )
32288    );
32289    this.crop_bounds.height = Math.max(
32290      20,
32291      Math.min(
32292        this.crop_bounds.height,
32293        this.dimensions.height - this.crop_bounds.y
32294      )
32295    );
32296  }
32297  /**
32298   * updates the crop mask graphics
32299   */
32300  update_crop_mask() {
32301    if (!this.crop_mask) return;
32302    this.crop_mask.clear();
32303    const { width, height } = this.image_editor_context.background_image?.getLocalBounds() ?? {
32304      width: 0,
32305      height: 0
32306    };
32307    this.crop_mask.rect(0, 0, width, height).fill({ color: 0, alpha: 0.4 }).rect(
32308      this.crop_bounds.x,
32309      this.crop_bounds.y,
32310      this.crop_bounds.width,
32311      this.crop_bounds.height
32312    ).cut();
32313    if (this.image_editor_context.background_image) {
32314      this.image_editor_context.background_image.mask = null;
32315    }
32316    if (this.image_editor_context.image_container.mask === this.crop_mask) {
32317      this.image_editor_context.image_container.mask = null;
32318    }
32319  }
32320  /**
32321   * updates the crop ui position and dimensions
32322   */
32323  update_crop_ui() {
32324    if (!this.crop_mask || !this.crop_ui_container) return;
32325    this.crop_ui_container.position.set(
32326      this.position.x + this.crop_bounds.x * this.scale,
32327      this.position.y + this.crop_bounds.y * this.scale
32328    );
32329    const scaled_width = this.crop_bounds.width * this.scale;
32330    const scaled_height = this.crop_bounds.height * this.scale;
32331    const outline = this.crop_ui_container.getChildAt(0);
32332    outline.clear().rect(0, 0, scaled_width, scaled_height).stroke({ width: 1, color: 0, alignment: 0, alpha: 0.5 });
32333    outline.hitArea = new Rectangle(0, 0, scaled_width, scaled_height);
32334    this.update_handle_positions(scaled_width, scaled_height);
32335  }
32336  /**
32337   * updates the positions of all handles
32338   * @param width - width of the crop area
32339   * @param height - height of the crop area
32340   */
32341  update_handle_positions(width, height) {
32342    if (!this.crop_ui_container) return;
32343    const handles = this.crop_ui_container.children.slice(1);
32344    const corners = handles.slice(0, 4);
32345    const cornerPositions = [
32346      { x: 0, y: 0 },
32347      // Top-left
32348      { x: width, y: 0 },
32349      // Top-right
32350      { x: 0, y: height },
32351      // Bottom-left
32352      { x: width, y: height }
32353      // Bottom-right
32354    ];
32355    corners.forEach((handle, i) => {
32356      const xScale = i % 2 === 0 ? 1 : -1;
32357      const yScale = i < 2 ? 1 : -1;
32358      handle.position.set(
32359        cornerPositions[i].x - (xScale < 0 ? -this.LINE_THICKNESS : this.LINE_THICKNESS),
32360        cornerPositions[i].y - (yScale < 0 ? -this.LINE_THICKNESS : this.LINE_THICKNESS)
32361      );
32362    });
32363    const edges = handles.slice(4);
32364    edges.forEach((handle, i) => {
32365      if (i < 2) {
32366        handle.position.set(
32367          width / 2 - this.CORNER_SIZE * 1.5 / 2,
32368          // Changed from 3 to 2
32369          i === 0 ? -this.LINE_THICKNESS : height
32370        );
32371      } else {
32372        handle.position.set(
32373          i === 2 ? 0 : width + this.LINE_THICKNESS,
32374          height / 2 - this.CORNER_SIZE * 1.5 / 2
32375          // Changed from 3 to 2
32376        );
32377      }
32378    });
32379  }
32380  /**
32381   * sets up event listeners for the crop tool
32382   */
32383  setup_event_listeners() {
32384    const stage = this.image_editor_context.app.stage;
32385    stage.eventMode = "static";
32386    stage.on("pointermove", this.handle_pointer_move.bind(this));
32387    stage.on("pointerup", this.handle_pointer_up.bind(this));
32388    stage.on("pointerupoutside", this.handle_pointer_up.bind(this));
32389  }
32390  /**
32391   * removes event listeners for the crop tool
32392   */
32393  cleanup_event_listeners() {
32394    const stage = this.image_editor_context.app.stage;
32395    stage.off("pointermove", this.handle_pointer_move.bind(this));
32396    stage.off("pointerup", this.handle_pointer_up.bind(this));
32397    stage.off("pointerupoutside", this.handle_pointer_up.bind(this));
32398  }
32399  /**
32400   * handles pointer move events
32401   * @param event - the pointer event
32402   */
32403  handle_pointer_move(event) {
32404    if (this.current_subtool !== "crop") return;
32405    if (this.is_dragging_window && this.drag_start_position && this.drag_start_bounds) {
32406      const local_pos = this.image_editor_context.image_container.toLocal(
32407        event.global
32408      );
32409      const delta = new Point(
32410        local_pos.x - this.drag_start_position.x,
32411        local_pos.y - this.drag_start_position.y
32412      );
32413      this.crop_bounds = {
32414        x: this.drag_start_bounds.x + delta.x,
32415        y: this.drag_start_bounds.y + delta.y,
32416        width: this.drag_start_bounds.width,
32417        height: this.drag_start_bounds.height
32418      };
32419      this.constrain_crop_bounds();
32420      this.update_crop_mask();
32421      if (this.crop_mask && this.image_editor_context.background_image) {
32422        this.image_editor_context.background_image.setMask(this.crop_mask);
32423      }
32424      return;
32425    }
32426    if (this.is_dragging && this.selected_handle && this.last_pointer_position) {
32427      const local_pos = this.image_editor_context.image_container.toLocal(
32428        event.global
32429      );
32430      const current_position = new Point(local_pos.x, local_pos.y);
32431      const delta = new Point(
32432        current_position.x - this.last_pointer_position.x,
32433        current_position.y - this.last_pointer_position.y
32434      );
32435      this.update_crop_bounds(delta);
32436      this.last_pointer_position = current_position;
32437      this.update_crop_mask();
32438    }
32439  }
32440  /**
32441   * handles pointer up events
32442   */
32443  handle_pointer_up() {
32444    if (this.current_subtool !== "crop") return;
32445    this.is_dragging = false;
32446    this.is_dragging_window = false;
32447    this.selected_handle = null;
32448    this.last_pointer_position = null;
32449    this.drag_start_position = null;
32450    this.drag_start_bounds = null;
32451    this.active_corner_index = -1;
32452    this.active_edge_index = -1;
32453    this.notify("change");
32454  }
32455  /**
32456   * handles the start of window dragging
32457   * @param event - the pointer event
32458   */
32459  handle_window_drag_start(event) {
32460    if (this.current_subtool !== "crop") return;
32461    event.stopPropagation();
32462    this.is_dragging_window = true;
32463    this.has_been_manually_changed = true;
32464    const local_pos = this.image_editor_context.image_container.toLocal(
32465      event.global
32466    );
32467    this.drag_start_position = new Point(local_pos.x, local_pos.y);
32468    this.drag_start_bounds = { ...this.crop_bounds };
32469  }
32470  /**
32471   * @returns whether the crop has been manually changed by the user
32472   */
32473  get crop_manually_changed() {
32474    return this.has_been_manually_changed;
32475  }
32476  on(event, callback) {
32477    this.event_callbacks.set(event, [
32478      ...this.event_callbacks.get(event) || [],
32479      callback
32480    ]);
32481  }
32482  off(event, callback) {
32483    this.event_callbacks.set(
32484      event,
32485      this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []
32486    );
32487  }
32488  notify(event) {
32489    for (const callback of this.event_callbacks.get(event) || []) {
32490      callback();
32491    }
32492  }
32493  get_crop_bounds() {
32494    const { width, height } = this.image_editor_context.background_image?.getLocalBounds() ?? {
32495      width: 0,
32496      height: 0
32497    };
32498    return {
32499      x: this.crop_bounds.x,
32500      y: this.crop_bounds.y,
32501      crop_dimensions: {
32502        width: this.crop_bounds.width,
32503        height: this.crop_bounds.height
32504      },
32505      image_dimensions: {
32506        width,
32507        height
32508      }
32509    };
32510  }
32511  get_image() {
32512    if (!this.image_editor_context.background_image)
32513      return Promise.resolve(null);
32514    const container = new Container();
32515    const sprite = new Sprite(
32516      this.image_editor_context.background_image.texture
32517    );
32518    container.addChild(sprite);
32519    return get_canvas_blob(
32520      this.image_editor_context.app.renderer,
32521      container,
32522      this.crop_bounds
32523    );
32524  }
32525}
32526
32527class ResizeCommand {
32528  constructor(context, original_state, new_state, reset_ui) {
32529    this.context = context;
32530    this.name = "Resize";
32531    this.original_width = original_state.width;
32532    this.original_height = original_state.height;
32533    this.original_x = original_state.x;
32534    this.original_y = original_state.y;
32535    this.new_width = new_state.width;
32536    this.new_height = new_state.height;
32537    this.new_x = new_state.x;
32538    this.new_y = new_state.y;
32539    this.reset_ui = reset_ui;
32540  }
32541  original_width;
32542  original_height;
32543  original_x;
32544  original_y;
32545  new_width;
32546  new_height;
32547  new_x;
32548  new_y;
32549  reset_ui;
32550  name;
32551  async execute(context) {
32552    if (context) {
32553      this.context = context;
32554    }
32555    if (!this.context.background_image) return;
32556    this.context.background_image.width = this.new_width;
32557    this.context.background_image.height = this.new_height;
32558    this.context.background_image.position.set(this.new_x, this.new_y);
32559    this.reset_ui();
32560  }
32561  async undo() {
32562    if (!this.context.background_image) return;
32563    this.context.background_image.width = this.original_width;
32564    this.context.background_image.height = this.original_height;
32565    this.context.background_image.position.set(
32566      this.original_x,
32567      this.original_y
32568    );
32569    this.reset_ui();
32570  }
32571}
32572class ResizeTool {
32573  name = "resize";
32574  image_editor_context;
32575  current_tool = "image";
32576  current_subtool = "size";
32577  current_cursor = "unset";
32578  CORNER_SIZE = 25;
32579  LINE_THICKNESS = 5;
32580  HANDLE_COLOR = 0;
32581  HIT_AREA_SIZE = 40;
32582  borderRegion = 0;
32583  is_dragging = false;
32584  selected_handle = null;
32585  active_corner_index = -1;
32586  active_edge_index = -1;
32587  last_pointer_position = null;
32588  resize_ui_container = null;
32589  dimensions = {
32590    width: 0,
32591    height: 0
32592  };
32593  position = { x: 0, y: 0 };
32594  scale = 1;
32595  is_moving = false;
32596  dom_mousedown_handler = null;
32597  dom_mousemove_handler = null;
32598  dom_mouseup_handler = null;
32599  event_callbacks = /* @__PURE__ */ new Map();
32600  last_scale = 1;
32601  original_state = null;
32602  /**
32603   * @method setup
32604   * @async
32605   * @description Sets up the resize tool with the given context and tool/subtool
32606   * @param {ImageEditorContext} context - The image editor context
32607   * @param {ToolbarTool} tool - The current tool
32608   * @param {Subtool} subtool - The current subtool
32609   * @returns {Promise<void>}
32610   */
32611  async setup(context, tool, subtool) {
32612    this.image_editor_context = context;
32613    this.current_tool = tool;
32614    this.current_subtool = subtool;
32615    if (context.background_image) {
32616      if (context.background_image.borderRegion !== void 0) {
32617        this.borderRegion = context.background_image.borderRegion;
32618      } else {
32619        const bg = context.background_image;
32620        const container = context.image_container;
32621        const isHorizontallyCentered = Math.abs(bg.position.x - (container.width - bg.width) / 2) < 2;
32622        const isVerticallyCentered = Math.abs(bg.position.y - (container.height - bg.height) / 2) < 2;
32623        if (!isHorizontallyCentered || !isVerticallyCentered) {
32624          this.borderRegion = Math.max(bg.position.x, bg.position.y);
32625          context.background_image.borderRegion = this.borderRegion;
32626        }
32627      }
32628    }
32629    context.dimensions.subscribe((dimensions) => {
32630      this.dimensions = dimensions;
32631      if (this.resize_ui_container) {
32632        this.update_resize_ui();
32633      }
32634    });
32635    context.position.subscribe((position) => {
32636      this.position = position;
32637      if (this.resize_ui_container && this.current_tool === "image" && this.current_subtool === "size") {
32638        this.update_resize_ui();
32639      }
32640    });
32641    context.scale.subscribe((scale) => {
32642      this.scale = scale;
32643      if (this.resize_ui_container && this.current_tool === "image" && this.current_subtool === "size") {
32644        this.update_resize_ui();
32645      }
32646    });
32647    await this.init_resize_ui();
32648    this.setup_event_listeners();
32649    if (this.current_subtool === "size" && this.image_editor_context.background_image) {
32650      const bg = this.image_editor_context.background_image;
32651      const container = this.image_editor_context.image_container;
32652      const bg_width = bg.width;
32653      const bg_height = bg.height;
32654      const container_bounds = container.getLocalBounds();
32655      const container_width = container_bounds.width;
32656      const container_height = container_bounds.height;
32657      const min_x = -(bg_width - container_width);
32658      const min_y = -(bg_height - container_height);
32659      const max_x = 0;
32660      const max_y = 0;
32661      const x_out_of_bounds = bg_width > container_width && (bg.position.x < min_x || bg.position.x > max_x);
32662      const y_out_of_bounds = bg_height > container_height && (bg.position.y < min_y || bg.position.y > max_y);
32663      if (x_out_of_bounds || y_out_of_bounds) {
32664        this.apply_boundary_constraints();
32665      }
32666    }
32667  }
32668  /**
32669   * @method cleanup
32670   * @description Cleans up the resize tool resources and removes event listeners
32671   * @returns {void}
32672   */
32673  cleanup() {
32674    if (this.resize_ui_container) {
32675      this.image_editor_context.app.stage.removeChild(this.resize_ui_container);
32676    }
32677    this.cleanup_event_listeners();
32678  }
32679  /**
32680   * @method set_tool
32681   * @description Sets the current tool and subtool, updating UI visibility
32682   * @param {ToolbarTool} tool - The tool to set
32683   * @param {Subtool} subtool - The subtool to set
32684   * @returns {void}
32685   */
32686  set_tool(tool, subtool) {
32687    this.current_tool = tool;
32688    this.current_subtool = subtool;
32689    if (tool === "image" && subtool === "size") {
32690      if (this.image_editor_context.background_image) {
32691        const storedBorderRegion = this.image_editor_context.background_image.borderRegion;
32692        if (typeof storedBorderRegion === "number" && storedBorderRegion > 0) {
32693          this.borderRegion = storedBorderRegion;
32694        }
32695      }
32696      this.show_resize_ui();
32697      if (this.image_editor_context.background_image) {
32698        const bg = this.image_editor_context.background_image;
32699        const container = this.image_editor_context.image_container;
32700        const bg_width = bg.width;
32701        const bg_height = bg.height;
32702        const container_bounds = container.getLocalBounds();
32703        const container_width = container_bounds.width;
32704        const container_height = container_bounds.height;
32705        const effectiveWidth = container_width - this.borderRegion * 2;
32706        const effectiveHeight = container_height - this.borderRegion * 2;
32707        const min_x = bg_width > effectiveWidth ? -(bg_width - effectiveWidth) + this.borderRegion : this.borderRegion;
32708        const max_x = bg_width > effectiveWidth ? this.borderRegion : container_width - bg_width - this.borderRegion;
32709        const min_y = bg_height > effectiveHeight ? -(bg_height - effectiveHeight) + this.borderRegion : this.borderRegion;
32710        const max_y = bg_height > effectiveHeight ? this.borderRegion : container_height - bg_height - this.borderRegion;
32711        const x_out_of_bounds = bg.position.x < min_x || bg.position.x > max_x;
32712        const y_out_of_bounds = bg.position.y < min_y || bg.position.y > max_y;
32713        if (x_out_of_bounds || y_out_of_bounds) {
32714          this.apply_boundary_constraints();
32715        }
32716      }
32717    } else {
32718      this.hide_resize_ui();
32719    }
32720  }
32721  /**
32722   * @method init_resize_ui
32723   * @private
32724   * @async
32725   * @description Initializes the resize UI elements and sets up the update loop
32726   * @returns {Promise<void>}
32727   */
32728  async init_resize_ui() {
32729    this.resize_ui_container = this.make_resize_ui(
32730      this.dimensions.width * this.scale,
32731      this.dimensions.height * this.scale
32732    );
32733    this.resize_ui_container.position.set(this.position.x, this.position.y);
32734    this.resize_ui_container.visible = false;
32735    this.image_editor_context.app.stage.addChild(this.resize_ui_container);
32736  }
32737  /**
32738   * @method make_resize_ui
32739   * @private
32740   * @description Creates the resize UI with the specified dimensions
32741   * @param {number} width - Width of the resize area
32742   * @param {number} height - Height of the resize area
32743   * @returns {Container} The created resize UI container
32744   */
32745  make_resize_ui(width, height) {
32746    const resize_container = new Container();
32747    resize_container.eventMode = "static";
32748    resize_container.interactiveChildren = true;
32749    const outline = new Graphics().rect(0, 0, width, height).stroke({
32750      width: 1,
32751      color: 0,
32752      alignment: 0,
32753      alpha: 0.3
32754    });
32755    const dotted_outline = new Graphics();
32756    dotted_outline.rect(0, 0, width, height).stroke({
32757      width: 1,
32758      color: 0,
32759      alpha: 0.7,
32760      pixelLine: true
32761    });
32762    const dash_length = 5;
32763    const gap_length = 5;
32764    const total_length = dash_length + gap_length;
32765    for (let x = 0; x < width; x += total_length * 2) {
32766      dotted_outline.rect(x, 0, Math.min(dash_length, width - x), 1).fill(0);
32767      dotted_outline.rect(x, height - 1, Math.min(dash_length, width - x), 1).fill(0);
32768    }
32769    for (let y = 0; y < height; y += total_length * 2) {
32770      dotted_outline.rect(0, y, 1, Math.min(dash_length, height - y)).fill(0);
32771      dotted_outline.rect(width - 1, y, 1, Math.min(dash_length, height - y)).fill(0);
32772    }
32773    resize_container.addChild(outline);
32774    resize_container.addChild(dotted_outline);
32775    const move_area = new Graphics().rect(0, 0, width, height).fill(16777215, 0);
32776    move_area.eventMode = "static";
32777    move_area.cursor = "move";
32778    resize_container.addChild(move_area);
32779    this.create_corner_handles(resize_container, width, height);
32780    this.create_edge_handles(resize_container, width, height);
32781    return resize_container;
32782  }
32783  /**
32784   * @method create_handle
32785   * @private
32786   * @description Creates a handle for the resize UI
32787   * @param {boolean} [is_edge=false] - Whether the handle is an edge handle
32788   * @returns {Container} The created handle container
32789   */
32790  create_handle(is_edge = false) {
32791    const handle = new Container();
32792    handle.eventMode = "static";
32793    handle.cursor = "pointer";
32794    const handle_graphics = new Graphics();
32795    const handle_size = is_edge ? 8 : 10;
32796    handle_graphics.rect(-handle_size / 2, -handle_size / 2, handle_size, handle_size).fill(16777215).stroke({ width: 1, color: this.HANDLE_COLOR });
32797    handle.addChild(handle_graphics);
32798    const hit_size = is_edge ? this.HIT_AREA_SIZE * 1.5 : this.HIT_AREA_SIZE;
32799    handle.hitArea = new Rectangle(
32800      -hit_size / 2,
32801      -hit_size / 2,
32802      hit_size,
32803      hit_size
32804    );
32805    return handle;
32806  }
32807  /**
32808   * @method create_edge_handles
32809   * @private
32810   * @description Creates edge handles for the resize UI
32811   * @param {Container} container - The container to add handles to
32812   * @param {number} width - Width of the resize area
32813   * @param {number} height - Height of the resize area
32814   * @returns {void}
32815   */
32816  create_edge_handles(container, width, height) {
32817    [-1, 1].forEach((i, index) => {
32818      const handle = this.create_handle(true);
32819      handle.rotation = 0;
32820      handle.on("pointerdown", (event) => {
32821        this.handle_pointer_down(event, handle, -1, index);
32822      });
32823      handle.x = width / 2;
32824      handle.y = i < 0 ? 0 : height;
32825      handle.cursor = "ns-resize";
32826      container.addChild(handle);
32827    });
32828    [-1, 1].forEach((i, index) => {
32829      const handle = this.create_handle(true);
32830      handle.rotation = 0;
32831      handle.on("pointerdown", (event) => {
32832        this.handle_pointer_down(event, handle, -1, index + 2);
32833      });
32834      handle.x = i < 0 ? 0 : width;
32835      handle.y = height / 2;
32836      handle.cursor = "ew-resize";
32837      container.addChild(handle);
32838    });
32839  }
32840  /**
32841   * @method create_corner_handles
32842   * @private
32843   * @description Creates corner handles for the resize UI
32844   * @param {Container} container - The container to add handles to
32845   * @param {number} width - Width of the resize area
32846   * @param {number} height - Height of the resize area
32847   * @returns {void}
32848   */
32849  create_corner_handles(container, width, height) {
32850    const corners = [
32851      { x: 0, y: 0, cursor: "nwse-resize" },
32852      { x: width, y: 0, cursor: "nesw-resize" },
32853      { x: 0, y: height, cursor: "nesw-resize" },
32854      { x: width, y: height, cursor: "nwse-resize" }
32855    ];
32856    corners.forEach(({ x, y, cursor }, i) => {
32857      const corner = this.create_handle(false);
32858      corner.x = x;
32859      corner.y = y;
32860      corner.on("pointerdown", (event) => {
32861        this.handle_pointer_down(event, corner, i, -1);
32862      });
32863      corner.cursor = cursor;
32864      container.addChild(corner);
32865    });
32866  }
32867  /**
32868   * @method handle_pointer_down
32869   * @private
32870   * @description Handles pointer down events on handles
32871   * @param {FederatedPointerEvent} event - The pointer event
32872   * @param {Container} handle - The handle that was clicked
32873   * @param {number} corner_index - Index of the corner (-1 if not a corner)
32874   * @param {number} edge_index - Index of the edge (-1 if not an edge)
32875   * @returns {void}
32876   */
32877  handle_pointer_down(event, handle, corner_index, edge_index) {
32878    if (this.current_subtool !== "size") return;
32879    event.stopPropagation();
32880    this.is_dragging = true;
32881    this.selected_handle = handle;
32882    this.active_corner_index = corner_index;
32883    this.active_edge_index = edge_index;
32884    const local_pos = this.image_editor_context.image_container.toLocal(
32885      event.global
32886    );
32887    this.last_pointer_position = new Point(local_pos.x, local_pos.y);
32888    if (this.image_editor_context.background_image) {
32889      const bg = this.image_editor_context.background_image;
32890      this.original_state = {
32891        width: bg.width,
32892        height: bg.height,
32893        x: bg.position.x,
32894        y: bg.position.y
32895      };
32896    }
32897  }
32898  /**
32899   * Maintains aspect ratio during resizing
32900   * @param new_width - The new width
32901   * @param new_height - The new height
32902   * @param original_aspect_ratio - The original aspect ratio
32903   * @param delta - The delta to apply
32904   * @returns The adjusted dimensions
32905   */
32906  maintain_aspect_ratio(new_width, new_height, original_aspect_ratio, delta) {
32907    const abs_delta_x = Math.abs(delta.x);
32908    const abs_delta_y = Math.abs(delta.y);
32909    if (abs_delta_x > abs_delta_y * 1.2) {
32910      new_height = new_width / original_aspect_ratio;
32911    } else if (abs_delta_y > abs_delta_x * 1.2) {
32912      new_width = new_height * original_aspect_ratio;
32913    } else {
32914      if (new_width / original_aspect_ratio > new_height) {
32915        new_height = new_width / original_aspect_ratio;
32916      } else {
32917        new_width = new_height * original_aspect_ratio;
32918      }
32919    }
32920    return { width: new_width, height: new_height };
32921  }
32922  /**
32923   * Limits dimensions based on constraints
32924   * @param width - The width to limit
32925   * @param height - The height to limit
32926   * @param maintain_aspect_ratio - Whether to maintain aspect ratio
32927   * @returns The limited dimensions
32928   */
32929  limit_dimensions(width, height, maintain_aspect_ratio) {
32930    const max_width = this.dimensions.width;
32931    const max_height = this.dimensions.height;
32932    let new_width = width;
32933    let new_height = height;
32934    if (new_width > max_width) {
32935      if (maintain_aspect_ratio && this.active_corner_index !== -1) {
32936        new_height = max_width / new_width * new_height;
32937      }
32938      new_width = max_width;
32939    }
32940    if (new_height > max_height) {
32941      if (maintain_aspect_ratio && this.active_corner_index !== -1) {
32942        new_width = max_height / new_height * new_width;
32943      }
32944      new_height = max_height;
32945    }
32946    new_width = Math.max(20, new_width);
32947    new_height = Math.max(20, new_height);
32948    return { width: new_width, height: new_height };
32949  }
32950  /**
32951   * Calculates position deltas based on dimension changes
32952   * @param old_width - The original width
32953   * @param old_height - The original height
32954   * @param new_width - The new width
32955   * @param new_height - The new height
32956   * @returns Object containing x and y position deltas
32957   */
32958  calculate_position_deltas(old_width, old_height, new_width, new_height) {
32959    let delta_x = 0;
32960    let delta_y = 0;
32961    if (this.active_corner_index === 0 || this.active_edge_index === 2) {
32962      delta_x = old_width - new_width;
32963    } else if (this.active_corner_index === 2) {
32964      delta_x = old_width - new_width;
32965      delta_y = 0;
32966    }
32967    if (this.active_corner_index === 0 || this.active_corner_index === 1 || this.active_edge_index === 0) {
32968      delta_y = old_height - new_height;
32969    }
32970    return { x: delta_x, y: delta_y };
32971  }
32972  /**
32973   * Applies boundary constraints to keep the image within the container
32974   * @private
32975   */
32976  apply_boundary_constraints() {
32977    if (!this.image_editor_context.background_image) return;
32978    const bg = this.image_editor_context.background_image;
32979    const image_container = this.image_editor_context.image_container;
32980    let new_x = bg.position.x;
32981    let new_y = bg.position.y;
32982    const container_width = image_container.width;
32983    const container_height = image_container.height;
32984    const bg_width = bg.width;
32985    const bg_height = bg.height;
32986    const effectiveWidth = container_width - this.borderRegion * 2;
32987    const effectiveHeight = container_height - this.borderRegion * 2;
32988    if (bg_width > effectiveWidth) {
32989      const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
32990      const max_x = this.borderRegion;
32991      new_x = Math.max(min_x, Math.min(max_x, new_x));
32992    } else {
32993      const min_x = this.borderRegion;
32994      const max_x = container_width - bg_width - this.borderRegion;
32995      if (new_x < min_x || new_x > max_x) {
32996        new_x = Math.max(min_x, Math.min(max_x, new_x));
32997      }
32998    }
32999    if (bg_height > effectiveHeight) {
33000      const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
33001      const max_y = this.borderRegion;
33002      new_y = Math.max(min_y, Math.min(max_y, new_y));
33003    } else {
33004      const min_y = this.borderRegion;
33005      const max_y = container_height - bg_height - this.borderRegion;
33006      if (new_y < min_y || new_y > max_y) {
33007        new_y = Math.max(min_y, Math.min(max_y, new_y));
33008      }
33009    }
33010    bg.position.set(new_x, new_y);
33011    if (this.resize_ui_container) {
33012      const total_x = image_container.position.x + new_x * this.scale;
33013      const total_y = image_container.position.y + new_y * this.scale;
33014      this.resize_ui_container.position.set(total_x, total_y);
33015    }
33016  }
33017  /**
33018   * Updates the resize bounds based on the delta
33019   * @param delta - The delta to apply
33020   * @param maintain_aspect_ratio - Whether to maintain the aspect ratio
33021   */
33022  update_resize_bounds(delta, maintain_aspect_ratio = false) {
33023    const background_image = this.image_editor_context.background_image;
33024    if (!background_image) return;
33025    const original_width = background_image.width;
33026    const original_height = background_image.height;
33027    const original_x = background_image.position.x;
33028    const original_y = background_image.position.y;
33029    const original_aspect_ratio = original_width / original_height;
33030    let current_position;
33031    if (this.active_corner_index !== -1) {
33032      switch (this.active_corner_index) {
33033        case 0:
33034          current_position = new Point(
33035            original_x - delta.x,
33036            original_y - delta.y
33037          );
33038          break;
33039        case 1:
33040          current_position = new Point(
33041            original_x + original_width + delta.x,
33042            original_y - delta.y
33043          );
33044          break;
33045        case 2:
33046          current_position = new Point(
33047            original_x - delta.x,
33048            original_y + original_height + delta.y
33049          );
33050          break;
33051        case 3:
33052          current_position = new Point(
33053            original_x + original_width + delta.x,
33054            original_y + original_height + delta.y
33055          );
33056          break;
33057        default:
33058          return;
33059      }
33060      const dimensions = this.handle_direct_corner_resize(
33061        current_position,
33062        original_width,
33063        original_height,
33064        original_x,
33065        original_y,
33066        original_aspect_ratio,
33067        maintain_aspect_ratio
33068      );
33069      background_image.width = dimensions.width;
33070      background_image.height = dimensions.height;
33071      background_image.position.set(dimensions.x, dimensions.y);
33072    } else if (this.active_edge_index !== -1) {
33073      switch (this.active_edge_index) {
33074        case 0:
33075          current_position = new Point(
33076            original_x + original_width / 2,
33077            original_y - delta.y
33078          );
33079          break;
33080        case 1:
33081          current_position = new Point(
33082            original_x + original_width / 2,
33083            original_y + original_height + delta.y
33084          );
33085          break;
33086        case 2:
33087          current_position = new Point(
33088            original_x - delta.x,
33089            original_y + original_height / 2
33090          );
33091          break;
33092        case 3:
33093          current_position = new Point(
33094            original_x + original_width + delta.x,
33095            original_y + original_height / 2
33096          );
33097          break;
33098        default:
33099          return;
33100      }
33101      const dimensions = this.handle_direct_edge_resize(
33102        current_position,
33103        original_width,
33104        original_height,
33105        original_x,
33106        original_y
33107      );
33108      background_image.width = dimensions.width;
33109      background_image.height = dimensions.height;
33110      background_image.position.set(dimensions.x, dimensions.y);
33111    }
33112    this.update_resize_ui();
33113  }
33114  /**
33115   * updates the resize ui position and dimensions
33116   */
33117  update_resize_ui() {
33118    if (!this.resize_ui_container || !this.image_editor_context.background_image || !this.image_editor_context.background_image.position)
33119      return;
33120    const background_image = this.image_editor_context.background_image;
33121    const image_container = this.image_editor_context.image_container;
33122    const container_global_pos = image_container.getGlobalPosition();
33123    const bg_pos_x = background_image.position.x * this.scale;
33124    const bg_pos_y = background_image.position.y * this.scale;
33125    this.resize_ui_container.position.set(
33126      container_global_pos.x + bg_pos_x,
33127      container_global_pos.y + bg_pos_y
33128    );
33129    const scaled_width = background_image.width * this.scale;
33130    const scaled_height = background_image.height * this.scale;
33131    const outline = this.resize_ui_container.getChildAt(0);
33132    outline.clear().rect(0, 0, scaled_width, scaled_height).stroke({ width: 1, color: 0, alignment: 0, alpha: 0.3 });
33133    const dotted_outline = this.resize_ui_container.getChildAt(1);
33134    dotted_outline.clear();
33135    dotted_outline.rect(0, 0, scaled_width, scaled_height).stroke({
33136      width: 1,
33137      color: 0,
33138      alpha: 0.7,
33139      pixelLine: true
33140    });
33141    if (!this.is_dragging && !this.is_moving && Math.abs(this.scale - this.last_scale) < 1e-3) {
33142      const dash_length = 5;
33143      const gap_length = 5;
33144      const total_length = dash_length + gap_length;
33145      for (let x = 0; x < scaled_width; x += total_length * 2) {
33146        dotted_outline.rect(x, 0, Math.min(dash_length, scaled_width - x), 1).fill(0);
33147        dotted_outline.rect(
33148          x,
33149          scaled_height - 1,
33150          Math.min(dash_length, scaled_width - x),
33151          1
33152        ).fill(0);
33153      }
33154      for (let y = 0; y < scaled_height; y += total_length * 2) {
33155        dotted_outline.rect(0, y, 1, Math.min(dash_length, scaled_height - y)).fill(0);
33156        dotted_outline.rect(
33157          scaled_width - 1,
33158          y,
33159          1,
33160          Math.min(dash_length, scaled_height - y)
33161        ).fill(0);
33162      }
33163    }
33164    this.last_scale = this.scale;
33165    const move_area = this.resize_ui_container.getChildAt(2);
33166    move_area.clear().rect(0, 0, scaled_width, scaled_height).fill(16777215, 0);
33167    this.update_handle_positions(scaled_width, scaled_height);
33168  }
33169  /**
33170   * updates the positions of all handles
33171   * @param width - width of the resize area
33172   * @param height - height of the resize area
33173   */
33174  update_handle_positions(width, height) {
33175    if (!this.resize_ui_container) return;
33176    const handles = this.resize_ui_container.children.slice(3);
33177    const move_area = this.resize_ui_container.getChildAt(2);
33178    move_area.clear().rect(0, 0, width, height).fill(16777215, 0);
33179    const corners = handles.slice(0, 4);
33180    const cornerPositions = [
33181      { x: 0, y: 0 },
33182      { x: width, y: 0 },
33183      { x: 0, y: height },
33184      { x: width, y: height }
33185    ];
33186    corners.forEach((handle, i) => {
33187      handle.position.set(cornerPositions[i].x, cornerPositions[i].y);
33188    });
33189    const edges = handles.slice(4);
33190    edges.forEach((handle, i) => {
33191      if (i < 2) {
33192        handle.position.set(width / 2, i === 0 ? 0 : height);
33193      } else {
33194        handle.position.set(i === 2 ? 0 : width, height / 2);
33195      }
33196    });
33197  }
33198  /**
33199   * sets up event listeners for the resize tool
33200   */
33201  setup_event_listeners() {
33202    const stage = this.image_editor_context.app.stage;
33203    stage.eventMode = "static";
33204    stage.on("pointermove", this.handle_pointer_move.bind(this));
33205    stage.on("pointerup", this.handle_pointer_up.bind(this));
33206    stage.on("pointerupoutside", this.handle_pointer_up.bind(this));
33207    this.setup_dom_event_listeners();
33208  }
33209  /**
33210   * Sets up direct DOM event listeners for dragging the background image
33211   */
33212  setup_dom_event_listeners() {
33213    this.cleanup_dom_event_listeners();
33214    const canvas = this.image_editor_context.app.canvas;
33215    this.dom_mousedown_handler = (e) => {
33216      if (this.current_subtool !== "size") return;
33217      if (this.is_dragging || this.selected_handle) return;
33218      if (!this.image_editor_context.background_image) return;
33219      const bg = this.image_editor_context.background_image;
33220      const container = this.image_editor_context.image_container;
33221      const containerBounds = container.getBounds();
33222      const rect = canvas.getBoundingClientRect();
33223      const x = e.clientX - rect.left;
33224      const y = e.clientY - rect.top;
33225      const localX = (x - containerBounds.x) / this.scale;
33226      const localY = (y - containerBounds.y) / this.scale;
33227      if (localX >= bg.position.x && localX <= bg.position.x + bg.width && localY >= bg.position.y && localY <= bg.position.y + bg.height) {
33228        this.is_moving = true;
33229        this.last_pointer_position = new Point(localX, localY);
33230        this.original_state = {
33231          width: bg.width,
33232          height: bg.height,
33233          x: bg.position.x,
33234          y: bg.position.y
33235        };
33236        e.preventDefault();
33237        e.stopPropagation();
33238      }
33239    };
33240    this.dom_mousemove_handler = (e) => {
33241      if (this.current_subtool !== "size" || !this.is_moving) return;
33242      if (!this.last_pointer_position || !this.image_editor_context.background_image)
33243        return;
33244      const container = this.image_editor_context.image_container;
33245      const containerBounds = container.getBounds();
33246      const rect = canvas.getBoundingClientRect();
33247      const x = e.clientX - rect.left;
33248      const y = e.clientY - rect.top;
33249      const localX = (x - containerBounds.x) / this.scale;
33250      const localY = (y - containerBounds.y) / this.scale;
33251      const current_position = new Point(localX, localY);
33252      this.handle_image_dragging(current_position);
33253      this.last_pointer_position = current_position;
33254      e.preventDefault();
33255      e.stopPropagation();
33256    };
33257    this.dom_mouseup_handler = (e) => {
33258      if (this.current_subtool !== "size") return;
33259      if (this.original_state && this.image_editor_context.background_image) {
33260        const bg = this.image_editor_context.background_image;
33261        const new_state = {
33262          width: bg.width,
33263          height: bg.height,
33264          x: bg.position.x,
33265          y: bg.position.y
33266        };
33267        if (this.original_state.width !== new_state.width || this.original_state.height !== new_state.height || this.original_state.x !== new_state.x || this.original_state.y !== new_state.y) {
33268          const resize_command = new ResizeCommand(
33269            this.image_editor_context,
33270            this.original_state,
33271            new_state,
33272            this.update_resize_ui.bind(this)
33273          );
33274          this.image_editor_context.execute_command(resize_command);
33275        }
33276        this.original_state = null;
33277      }
33278      this.is_moving = false;
33279      this.last_pointer_position = null;
33280      this.update_resize_ui();
33281      e.preventDefault();
33282      e.stopPropagation();
33283    };
33284    canvas.addEventListener("mousedown", this.dom_mousedown_handler);
33285    window.addEventListener("mousemove", this.dom_mousemove_handler);
33286    window.addEventListener("mouseup", this.dom_mouseup_handler);
33287  }
33288  /**
33289   * Cleans up DOM event listeners
33290   */
33291  cleanup_dom_event_listeners() {
33292    const canvas = this.image_editor_context.app.canvas;
33293    if (this.dom_mousedown_handler) {
33294      canvas.removeEventListener("mousedown", this.dom_mousedown_handler);
33295      this.dom_mousedown_handler = null;
33296    }
33297    if (this.dom_mousemove_handler) {
33298      window.removeEventListener("mousemove", this.dom_mousemove_handler);
33299      this.dom_mousemove_handler = null;
33300    }
33301    if (this.dom_mouseup_handler) {
33302      window.removeEventListener("mouseup", this.dom_mouseup_handler);
33303      this.dom_mouseup_handler = null;
33304    }
33305  }
33306  /**
33307   * removes event listeners for the resize tool
33308   */
33309  cleanup_event_listeners() {
33310    const stage = this.image_editor_context.app.stage;
33311    stage.off("pointermove", this.handle_pointer_move.bind(this));
33312    stage.off("pointerup", this.handle_pointer_up.bind(this));
33313    stage.off("pointerupoutside", this.handle_pointer_up.bind(this));
33314    this.cleanup_dom_event_listeners();
33315  }
33316  /**
33317   * Handles pointer move events
33318   */
33319  handle_pointer_move(event) {
33320    if (this.current_subtool !== "size") return;
33321    const local_pos = this.image_editor_context.image_container.toLocal(
33322      event.global
33323    );
33324    const current_position = new Point(local_pos.x, local_pos.y);
33325    if (this.is_moving && this.last_pointer_position) {
33326      this.handle_image_dragging(current_position);
33327      this.last_pointer_position = current_position;
33328    } else if (this.is_dragging && this.selected_handle) {
33329      if (!this.image_editor_context.background_image) return;
33330      const bg = this.image_editor_context.background_image;
33331      const maintain_aspect_ratio = this.active_corner_index !== -1 && !event.shiftKey;
33332      const original_width = bg.width;
33333      const original_height = bg.height;
33334      const original_x = bg.position.x;
33335      const original_y = bg.position.y;
33336      const original_aspect_ratio = original_width / original_height;
33337      let dimensions;
33338      if (this.active_corner_index !== -1) {
33339        dimensions = this.handle_direct_corner_resize(
33340          current_position,
33341          original_width,
33342          original_height,
33343          original_x,
33344          original_y,
33345          original_aspect_ratio,
33346          maintain_aspect_ratio
33347        );
33348      } else if (this.active_edge_index !== -1) {
33349        dimensions = this.handle_direct_edge_resize(
33350          current_position,
33351          original_width,
33352          original_height,
33353          original_x,
33354          original_y
33355        );
33356      } else {
33357        return;
33358      }
33359      dimensions = this.limit_dimensions_to_container(
33360        dimensions,
33361        original_x,
33362        original_y,
33363        original_width,
33364        original_height,
33365        maintain_aspect_ratio
33366      );
33367      bg.width = dimensions.width;
33368      bg.height = dimensions.height;
33369      bg.position.set(dimensions.x, dimensions.y);
33370      this.update_resize_ui();
33371      this.last_pointer_position = current_position;
33372    }
33373  }
33374  /**
33375   * handles pointer up events
33376   */
33377  handle_pointer_up() {
33378    if (this.current_subtool !== "size") return;
33379    this.is_dragging = false;
33380    this.is_moving = false;
33381    this.selected_handle = null;
33382    this.last_pointer_position = null;
33383    this.active_corner_index = -1;
33384    this.active_edge_index = -1;
33385    this.update_resize_ui();
33386    this.notify("change");
33387  }
33388  /**
33389   * Handles dragging of the background image
33390   * @param current_position - The current pointer position
33391   * @private
33392   */
33393  handle_image_dragging(current_position) {
33394    if (!this.last_pointer_position || !this.image_editor_context.background_image) {
33395      return;
33396    }
33397    const bg = this.image_editor_context.background_image;
33398    const image_container = this.image_editor_context.image_container.getLocalBounds();
33399    const dx = current_position.x - this.last_pointer_position.x;
33400    const dy = current_position.y - this.last_pointer_position.y;
33401    let new_x = bg.position.x + dx;
33402    let new_y = bg.position.y + dy;
33403    const container_width = image_container.width;
33404    const container_height = image_container.height;
33405    const bg_width = bg.width;
33406    const bg_height = bg.height;
33407    const effectiveWidth = container_width - this.borderRegion * 2;
33408    const effectiveHeight = container_height - this.borderRegion * 2;
33409    if (bg_width > effectiveWidth) {
33410      const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
33411      const max_x = this.borderRegion;
33412      new_x = Math.max(min_x, Math.min(max_x, new_x));
33413    } else {
33414      const min_x = this.borderRegion;
33415      const max_x = container_width - bg_width - this.borderRegion;
33416      new_x = Math.max(min_x, Math.min(max_x, new_x));
33417    }
33418    if (bg_height > effectiveHeight) {
33419      const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
33420      const max_y = this.borderRegion;
33421      new_y = Math.max(min_y, Math.min(max_y, new_y));
33422    } else {
33423      const min_y = this.borderRegion;
33424      const max_y = container_height - bg_height - this.borderRegion;
33425      new_y = Math.max(min_y, Math.min(max_y, new_y));
33426    }
33427    bg.position.set(new_x, new_y);
33428    this.update_resize_ui();
33429  }
33430  /**
33431   * Handles corner resizing
33432   * @param current_position - The current mouse position
33433   * @param original_width - The original width
33434   * @param original_height - The original height
33435   * @param original_x - The original x position
33436   * @param original_y - The original y position
33437   * @param original_aspect_ratio - The original aspect ratio
33438   * @param maintain_aspect_ratio - Whether to maintain aspect ratio
33439   * @returns The new dimensions and position
33440   * @private
33441   */
33442  handle_direct_corner_resize(current_position, original_width, original_height, original_x, original_y, original_aspect_ratio, maintain_aspect_ratio) {
33443    let raw_width = 0;
33444    let raw_height = 0;
33445    let new_x = original_x;
33446    let new_y = original_y;
33447    switch (this.active_corner_index) {
33448      case 0:
33449        raw_width = original_x + original_width - current_position.x;
33450        raw_height = original_y + original_height - current_position.y;
33451        break;
33452      case 1:
33453        raw_width = current_position.x - original_x;
33454        raw_height = original_y + original_height - current_position.y;
33455        break;
33456      case 2:
33457        raw_width = original_x + original_width - current_position.x;
33458        raw_height = current_position.y - original_y;
33459        break;
33460      case 3:
33461        raw_width = current_position.x - original_x;
33462        raw_height = current_position.y - original_y;
33463        break;
33464    }
33465    raw_width = Math.max(20, raw_width);
33466    raw_height = Math.max(20, raw_height);
33467    let new_width = raw_width;
33468    let new_height = raw_height;
33469    if (maintain_aspect_ratio) {
33470      const diagonal_vector = {
33471        x: current_position.x - (original_x + original_width / 2),
33472        y: current_position.y - (original_y + original_height / 2)
33473      };
33474      const angle = Math.abs(Math.atan2(diagonal_vector.y, diagonal_vector.x));
33475      if (angle < Math.PI / 4 || angle > 3 * Math.PI / 4) {
33476        new_height = new_width / original_aspect_ratio;
33477      } else {
33478        new_width = new_height * original_aspect_ratio;
33479      }
33480    }
33481    switch (this.active_corner_index) {
33482      case 0:
33483        new_x = original_x + original_width - new_width;
33484        new_y = original_y + original_height - new_height;
33485        break;
33486      case 1:
33487        new_y = original_y + original_height - new_height;
33488        break;
33489      case 2:
33490        new_x = original_x + original_width - new_width;
33491        break;
33492    }
33493    return { width: new_width, height: new_height, x: new_x, y: new_y };
33494  }
33495  /**
33496   * Handles edge resizing
33497   * @param current_position - The current mouse position
33498   * @param original_width - The original width
33499   * @param original_height - The original height
33500   * @param original_x - The original x position
33501   * @param original_y - The original y position
33502   * @returns The new dimensions and position
33503   * @private
33504   */
33505  handle_direct_edge_resize(current_position, original_width, original_height, original_x, original_y) {
33506    let new_width = original_width;
33507    let new_height = original_height;
33508    let new_x = original_x;
33509    let new_y = original_y;
33510    switch (this.active_edge_index) {
33511      case 0:
33512        new_height = Math.max(
33513          20,
33514          original_y + original_height - current_position.y
33515        );
33516        new_y = current_position.y;
33517        break;
33518      case 1:
33519        new_height = Math.max(20, current_position.y - original_y);
33520        break;
33521      case 2:
33522        new_width = Math.max(
33523          20,
33524          original_x + original_width - current_position.x
33525        );
33526        new_x = current_position.x;
33527        break;
33528      case 3:
33529        new_width = Math.max(20, current_position.x - original_x);
33530        break;
33531    }
33532    return { width: new_width, height: new_height, x: new_x, y: new_y };
33533  }
33534  /**
33535   * Limits dimensions to container size
33536   * @param dimensions - The dimensions to limit
33537   * @param original_x - The original x position
33538   * @param original_y - The original y position
33539   * @param original_width - The original width
33540   * @param original_height - The original height
33541   * @param maintain_aspect_ratio - Whether to maintain aspect ratio
33542   * @returns The limited dimensions and position
33543   * @private
33544   */
33545  limit_dimensions_to_container(dimensions, original_x, original_y, original_width, original_height, maintain_aspect_ratio) {
33546    let {
33547      width: new_width,
33548      height: new_height,
33549      x: new_x,
33550      y: new_y
33551    } = dimensions;
33552    const effective_container_width = this.dimensions.width - this.borderRegion * 2;
33553    const effective_container_height = this.dimensions.height - this.borderRegion * 2;
33554    const pre_limit_width = new_width;
33555    const pre_limit_height = new_height;
33556    const size_limited = this.apply_size_limits(
33557      new_width,
33558      new_height,
33559      effective_container_width,
33560      effective_container_height,
33561      maintain_aspect_ratio,
33562      pre_limit_width / pre_limit_height
33563    );
33564    new_width = size_limited.width;
33565    new_height = size_limited.height;
33566    const position_adjusted = this.adjust_position_for_resizing(
33567      new_width,
33568      new_height,
33569      original_x,
33570      original_y,
33571      original_width,
33572      original_height
33573    );
33574    new_x = position_adjusted.x;
33575    new_y = position_adjusted.y;
33576    const border_constrained = this.apply_border_constraints(
33577      new_width,
33578      new_height,
33579      new_x,
33580      new_y,
33581      maintain_aspect_ratio,
33582      pre_limit_width / pre_limit_height
33583    );
33584    return border_constrained;
33585  }
33586  /**
33587   * Apply size limits to ensure dimensions don't exceed container
33588   * @private
33589   */
33590  apply_size_limits(width, height, max_width, max_height, maintain_aspect_ratio, aspect_ratio) {
33591    let new_width = width;
33592    let new_height = height;
33593    const needs_width_limit = new_width > max_width;
33594    const needs_height_limit = new_height > max_height;
33595    if (needs_width_limit && needs_height_limit) {
33596      const width_scale = max_width / new_width;
33597      const height_scale = max_height / new_height;
33598      if (width_scale < height_scale) {
33599        new_width = max_width;
33600        if (maintain_aspect_ratio) {
33601          new_height = new_width / aspect_ratio;
33602        }
33603      } else {
33604        new_height = max_height;
33605        if (maintain_aspect_ratio) {
33606          new_width = new_height * aspect_ratio;
33607        }
33608      }
33609    } else if (needs_width_limit) {
33610      new_width = max_width;
33611      if (maintain_aspect_ratio) {
33612        new_height = new_width / aspect_ratio;
33613      }
33614    } else if (needs_height_limit) {
33615      new_height = max_height;
33616      if (maintain_aspect_ratio) {
33617        new_width = new_height * aspect_ratio;
33618      }
33619    }
33620    return { width: new_width, height: new_height };
33621  }
33622  /**
33623   * Adjust position based on which handles are being dragged
33624   * @private
33625   */
33626  adjust_position_for_resizing(new_width, new_height, original_x, original_y, original_width, original_height) {
33627    let new_x = original_x;
33628    let new_y = original_y;
33629    switch (this.active_corner_index) {
33630      case 0:
33631        new_x = original_x + original_width - new_width;
33632        new_y = original_y + original_height - new_height;
33633        break;
33634      case 1:
33635        new_y = original_y + original_height - new_height;
33636        break;
33637      case 2:
33638        new_x = original_x + original_width - new_width;
33639        break;
33640    }
33641    if (this.active_edge_index !== -1) {
33642      switch (this.active_edge_index) {
33643        case 0:
33644          new_y = original_y + original_height - new_height;
33645          break;
33646        case 2:
33647          new_x = original_x + original_width - new_width;
33648          break;
33649      }
33650    }
33651    return { x: new_x, y: new_y };
33652  }
33653  /**
33654   * Apply border region constraints to prevent image from intruding on border
33655   * @private
33656   */
33657  apply_border_constraints(width, height, x, y, maintain_aspect_ratio, aspect_ratio) {
33658    const top_left_constrained = this.apply_top_left_constraints(
33659      width,
33660      height,
33661      x,
33662      y,
33663      maintain_aspect_ratio,
33664      aspect_ratio
33665    );
33666    const all_constrained = this.apply_bottom_right_constraints(
33667      top_left_constrained.width,
33668      top_left_constrained.height,
33669      top_left_constrained.x,
33670      top_left_constrained.y,
33671      maintain_aspect_ratio,
33672      aspect_ratio
33673    );
33674    all_constrained.width = Math.max(20, all_constrained.width);
33675    all_constrained.height = Math.max(20, all_constrained.height);
33676    return all_constrained;
33677  }
33678  /**
33679   * Apply top and left border constraints
33680   * @private
33681   */
33682  apply_top_left_constraints(width, height, x, y, maintain_aspect_ratio, aspect_ratio) {
33683    let new_width = width;
33684    let new_height = height;
33685    let new_x = x;
33686    let new_y = y;
33687    if (new_x < this.borderRegion) {
33688      if (this.active_corner_index === 0 || this.active_corner_index === 2 || this.active_edge_index === 2) {
33689        new_width -= this.borderRegion - new_x;
33690        if (maintain_aspect_ratio) {
33691          new_height = new_width / aspect_ratio;
33692        }
33693      }
33694      new_x = this.borderRegion;
33695    }
33696    if (new_y < this.borderRegion) {
33697      if (this.active_corner_index === 0 || this.active_corner_index === 1 || this.active_edge_index === 0) {
33698        new_height -= this.borderRegion - new_y;
33699        if (maintain_aspect_ratio) {
33700          new_width = new_height * aspect_ratio;
33701        }
33702      }
33703      new_y = this.borderRegion;
33704    }
33705    return { width: new_width, height: new_height, x: new_x, y: new_y };
33706  }
33707  /**
33708   * Apply bottom and right border constraints
33709   * @private
33710   */
33711  apply_bottom_right_constraints(width, height, x, y, maintain_aspect_ratio, aspect_ratio) {
33712    let new_width = width;
33713    let new_height = height;
33714    let new_x = x;
33715    let new_y = y;
33716    if (new_x + new_width > this.dimensions.width - this.borderRegion) {
33717      if (this.active_corner_index === 1 || this.active_corner_index === 3 || this.active_edge_index === 3) {
33718        new_width = this.dimensions.width - this.borderRegion - new_x;
33719        if (maintain_aspect_ratio) {
33720          new_height = new_width / aspect_ratio;
33721        }
33722      } else {
33723        const right_edge = new_x + new_width;
33724        const excess = right_edge - (this.dimensions.width - this.borderRegion);
33725        new_x = Math.max(this.borderRegion, new_x - excess);
33726      }
33727    }
33728    if (new_y + new_height > this.dimensions.height - this.borderRegion) {
33729      if (this.active_corner_index === 2 || this.active_corner_index === 3 || this.active_edge_index === 1) {
33730        new_height = this.dimensions.height - this.borderRegion - new_y;
33731        if (maintain_aspect_ratio) {
33732          new_width = new_height * aspect_ratio;
33733        }
33734      } else {
33735        const bottom_edge = new_y + new_height;
33736        const excess = bottom_edge - (this.dimensions.height - this.borderRegion);
33737        new_y = Math.max(this.borderRegion, new_y - excess);
33738      }
33739    }
33740    return { width: new_width, height: new_height, x: new_x, y: new_y };
33741  }
33742  /**
33743   * Shows the resize UI
33744   * @private
33745   */
33746  show_resize_ui() {
33747    if (this.resize_ui_container) {
33748      this.resize_ui_container.visible = true;
33749      this.update_resize_ui();
33750    }
33751    if (this.image_editor_context.background_image) {
33752      const bg = this.image_editor_context.background_image;
33753      this.current_cursor = bg.cursor;
33754      bg.cursor = "move";
33755    }
33756    this.setup_dom_event_listeners();
33757  }
33758  /**
33759   * Hides the resize UI
33760   * @private
33761   */
33762  hide_resize_ui() {
33763    if (this.resize_ui_container) {
33764      this.resize_ui_container.visible = false;
33765    }
33766    if (this.image_editor_context.background_image) {
33767      const bg = this.image_editor_context.background_image;
33768      bg.cursor = this.current_cursor;
33769    }
33770    this.cleanup_dom_event_listeners();
33771  }
33772  on(event, callback) {
33773    this.event_callbacks.set(event, [
33774      ...this.event_callbacks.get(event) || [],
33775      callback
33776    ]);
33777  }
33778  off(event, callback) {
33779    this.event_callbacks.set(
33780      event,
33781      this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []
33782    );
33783  }
33784  notify(event) {
33785    for (const callback of this.event_callbacks.get(event) || []) {
33786      callback();
33787    }
33788  }
33789  /**
33790   * Sets the border region explicitly
33791   * @param borderRegion The border region size in pixels
33792   */
33793  set_border_region(borderRegion) {
33794    this.borderRegion = borderRegion;
33795    if (this.image_editor_context?.background_image) {
33796      this.image_editor_context.background_image.borderRegion = borderRegion;
33797    }
33798    if (this.resize_ui_container) {
33799      this.update_resize_ui();
33800    }
33801    if (this.current_subtool === "size" && this.image_editor_context?.background_image) {
33802      this.apply_boundary_constraints();
33803    }
33804  }
33805}
33806
33807function set_cursor(child, cursor) {
33808  if (child instanceof Container) {
33809    child.cursor = cursor;
33810  } else if ("cursor" in child) {
33811    child.cursor = cursor;
33812  }
33813}
33814function recurse_set_cursor(children, cursor) {
33815  for (const child of children) {
33816    set_cursor(child, cursor);
33817    if (child instanceof Container && child.children.length > 0) {
33818      recurse_set_cursor(
33819        child.children,
33820        cursor
33821      );
33822    }
33823  }
33824}
33825function clear_timeout(timeout) {
33826  if (timeout !== null) {
33827    window.clearTimeout(timeout);
33828  }
33829  return null;
33830}
33831
33832class BrushCursor {
33833  constructor(image_editor_context, state, scale) {
33834    this.image_editor_context = image_editor_context;
33835    this.state = state;
33836    this.scale = scale;
33837    this.initialize_cursor();
33838    this.initialize_brush_preview();
33839  }
33840  cursor_graphics = null;
33841  cursor_container = null;
33842  brush_preview_container = null;
33843  brush_preview_graphics = null;
33844  is_preview_visible = false;
33845  is_cursor_over_image = false;
33846  cursor_position_check_timeout = null;
33847  is_brush_or_erase_active = false;
33848  _bound_update_cursor = null;
33849  _bound_check_cursor_over_image = null;
33850  /**
33851   * Returns whether the cursor is currently over the image.
33852   */
33853  is_over_image() {
33854    return this.is_cursor_over_image;
33855  }
33856  /**
33857   * Sets up cursor-related event listeners.
33858   */
33859  setup_event_listeners() {
33860    this.cleanup_event_listeners();
33861    this._bound_update_cursor = this.update_cursor_position.bind(this);
33862    this._bound_check_cursor_over_image = this.check_cursor_over_image.bind(this);
33863    const stage = this.image_editor_context.app.stage;
33864    stage.on("pointermove", this._bound_update_cursor);
33865    stage.on("pointermove", this._bound_check_cursor_over_image);
33866    this.image_editor_context.image_container.on(
33867      "pointerenter",
33868      this.on_image_container_pointer_enter.bind(this)
33869    );
33870    this.image_editor_context.image_container.on(
33871      "pointerleave",
33872      this.on_image_container_pointer_leave.bind(this)
33873    );
33874  }
33875  /**
33876   * Removes all cursor-related event listeners.
33877   */
33878  cleanup_event_listeners() {
33879    const stage = this.image_editor_context.app.stage;
33880    if (this._bound_update_cursor) {
33881      stage.off("pointermove", this._bound_update_cursor);
33882      this._bound_update_cursor = null;
33883    }
33884    if (this._bound_check_cursor_over_image) {
33885      stage.off("pointermove", this._bound_check_cursor_over_image);
33886      this._bound_check_cursor_over_image = null;
33887    }
33888    this.image_editor_context.image_container.off("pointerenter");
33889    this.image_editor_context.image_container.off("pointerleave");
33890    this.cursor_position_check_timeout = clear_timeout(
33891      this.cursor_position_check_timeout
33892    );
33893  }
33894  /**
33895   * initializes the cursor for the brush tool.
33896   */
33897  initialize_cursor() {
33898    if (this.cursor_container) {
33899      if (this.cursor_container.parent) {
33900        this.cursor_container.parent.removeChild(this.cursor_container);
33901      }
33902      this.cursor_container.destroy({ children: true });
33903      this.cursor_container = null;
33904    }
33905    this.cursor_container = new Container();
33906    this.image_editor_context.ui_container.addChild(this.cursor_container);
33907    this.cursor_graphics = new Graphics();
33908    this.cursor_container.addChild(this.cursor_graphics);
33909    this.update_cursor_appearance();
33910  }
33911  /**
33912   * updates the appearance of the cursor.
33913   */
33914  update_cursor_appearance() {
33915    if (!this.cursor_graphics) return;
33916    this.cursor_graphics.clear();
33917    const cursor_color = this.state.mode === "draw" ? tinycolor(this.state.color).toString() : 16777215;
33918    this.cursor_graphics.circle(0, 0, this.state.brush_size * this.scale).stroke({
33919      width: 1.5,
33920      color: cursor_color,
33921      alpha: 0.8
33922    });
33923    this.cursor_graphics.circle(0, 0, 1).fill({
33924      color: cursor_color,
33925      alpha: 0.8
33926    });
33927  }
33928  /**
33929   * updates the position of the cursor.
33930   * @param {FederatedPointerEvent} event - The pointer event.
33931   */
33932  update_cursor_position(event) {
33933    if (!this.cursor_container) return;
33934    const local_pos = this.image_editor_context.image_container.toLocal(
33935      event.global
33936    );
33937    const ui_pos = this.image_editor_context.ui_container.toLocal(
33938      this.image_editor_context.image_container.toGlobal(local_pos)
33939    );
33940    this.cursor_container.position.set(ui_pos.x, ui_pos.y);
33941  }
33942  /**
33943   * checks if the cursor is over the image container.
33944   * @param {FederatedPointerEvent} event - The pointer event.
33945   */
33946  check_cursor_over_image(event) {
33947    if (this.cursor_position_check_timeout !== null) {
33948      window.clearTimeout(this.cursor_position_check_timeout);
33949      this.cursor_position_check_timeout = null;
33950    }
33951    const image_container = this.image_editor_context.image_container;
33952    const bounds = image_container.getBounds();
33953    const was_over_image = this.is_cursor_over_image;
33954    this.is_cursor_over_image = event.global.x >= bounds.x && event.global.x <= bounds.x + bounds.width && event.global.y >= bounds.y && event.global.y <= bounds.y + bounds.height;
33955    if (was_over_image !== this.is_cursor_over_image) {
33956      this.update_cursor_and_preview_visibility();
33957    }
33958  }
33959  /**
33960   * updates the size of the cursor.
33961   */
33962  update_cursor_size() {
33963    this.update_cursor_appearance();
33964  }
33965  /**
33966   * Updates whether brush or erase tool is active
33967   */
33968  set_active(is_active) {
33969    this.is_brush_or_erase_active = is_active;
33970    this.update_cursor_and_preview_visibility();
33971  }
33972  /**
33973   * updates the visibility of the cursor.
33974   */
33975  update_cursor_visibility() {
33976    if (this.cursor_container) {
33977      this.cursor_container.visible = this.is_cursor_over_image && this.is_brush_or_erase_active;
33978    }
33979  }
33980  /**
33981   * Shows or hides the brush preview.
33982   * @param {boolean} show - Whether to show the preview.
33983   */
33984  preview_brush(show) {
33985    this.is_preview_visible = show;
33986    if (this.brush_preview_container) {
33987      this.brush_preview_container.visible = show && this.is_brush_or_erase_active;
33988    }
33989    if (show) {
33990      this.update_brush_preview();
33991      this.update_brush_preview_position();
33992    }
33993  }
33994  /**
33995   * initializes the brush preview.
33996   */
33997  initialize_brush_preview() {
33998    if (this.brush_preview_container) {
33999      if (this.brush_preview_container.parent) {
34000        this.brush_preview_container.parent.removeChild(
34001          this.brush_preview_container
34002        );
34003      }
34004      this.brush_preview_container.destroy({ children: true });
34005      this.brush_preview_container = null;
34006    }
34007    this.brush_preview_container = new Container();
34008    this.image_editor_context.ui_container.addChild(
34009      this.brush_preview_container
34010    );
34011    this.brush_preview_graphics = new Graphics();
34012    this.brush_preview_container.addChild(this.brush_preview_graphics);
34013    this.brush_preview_container.visible = false;
34014  }
34015  /**
34016   * updates the brush preview appearance.
34017   */
34018  update_brush_preview() {
34019    if (!this.brush_preview_graphics) return;
34020    this.brush_preview_graphics.clear();
34021    const preview_size = this.state.brush_size * this.scale;
34022    const preview_color = this.state.mode === "draw" ? tinycolor(this.state.color).setAlpha(this.state.opacity).toString() : 16777215;
34023    this.brush_preview_graphics.circle(0, 0, preview_size).fill({
34024      color: preview_color,
34025      alpha: this.state.mode === "draw" ? this.state.opacity : 0.3
34026    });
34027    this.brush_preview_graphics.circle(0, 0, preview_size + 1).stroke({
34028      width: 1,
34029      color: 0,
34030      alpha: 0.5
34031    });
34032  }
34033  /**
34034   * updates the position of the brush preview to center of canvas.
34035   */
34036  update_brush_preview_position() {
34037    if (!this.brush_preview_container) return;
34038    const image_container = this.image_editor_context.image_container;
34039    const center_x = image_container.width / 2;
34040    const center_y = image_container.height / 2;
34041    const global_pos = image_container.toGlobal({ x: center_x, y: center_y });
34042    const ui_pos = this.image_editor_context.ui_container.toLocal(global_pos);
34043    this.brush_preview_container.position.set(ui_pos.x, ui_pos.y);
34044  }
34045  /**
34046   * Called when the pointer enters the image container.
34047   */
34048  on_image_container_pointer_enter() {
34049    this.is_cursor_over_image = true;
34050    this.update_cursor_and_preview_visibility();
34051  }
34052  /**
34053   * Called when the pointer leaves the image container.
34054   */
34055  on_image_container_pointer_leave() {
34056    this.is_cursor_over_image = false;
34057    this.update_cursor_and_preview_visibility();
34058  }
34059  /**
34060   * Updates both cursor and preview visibility based on cursor position.
34061   */
34062  update_cursor_and_preview_visibility() {
34063    this.update_cursor_visibility();
34064    if (this.brush_preview_container) {
34065      this.brush_preview_container.visible = this.is_preview_visible && this.is_brush_or_erase_active;
34066    }
34067  }
34068  /**
34069   * Cleans up all cursor resources.
34070   */
34071  cleanup() {
34072    this.is_brush_or_erase_active = false;
34073    if (this.cursor_container) {
34074      if (this.cursor_container.parent) {
34075        this.cursor_container.parent.removeChild(this.cursor_container);
34076      }
34077      this.cursor_container.destroy({ children: true });
34078      this.cursor_container = null;
34079    }
34080    if (this.brush_preview_container) {
34081      if (this.brush_preview_container.parent) {
34082        this.brush_preview_container.parent.removeChild(
34083          this.brush_preview_container
34084        );
34085      }
34086      this.brush_preview_container.destroy({ children: true });
34087      this.brush_preview_container = null;
34088    }
34089    if (this.cursor_position_check_timeout !== null) {
34090      window.clearTimeout(this.cursor_position_check_timeout);
34091      this.cursor_position_check_timeout = null;
34092    }
34093  }
34094  /**
34095   * Updates the brush state.
34096   */
34097  update_state(state, scale) {
34098    this.state = state;
34099    this.scale = scale;
34100    this.update_cursor_appearance();
34101    if (this.is_preview_visible) {
34102      this.update_brush_preview();
34103    }
34104  }
34105}
34106
34107class BrushCommand {
34108  stroke_data;
34109  context;
34110  original_texture = null;
34111  name;
34112  constructor(context, stroke_data, original_texture) {
34113    this.name = "Draw";
34114    this.stroke_data = stroke_data;
34115    this.context = context;
34116    if (original_texture) {
34117      this.original_texture = this.create_texture_from(original_texture);
34118    }
34119  }
34120  /**
34121   * Creates a new texture with the same content as the source texture
34122   */
34123  create_texture_from(source) {
34124    const texture = RenderTexture.create({
34125      width: source.width,
34126      height: source.height,
34127      resolution: window.devicePixelRatio || 1
34128    });
34129    const sprite = new Sprite(source);
34130    const container = new Container();
34131    container.addChild(sprite);
34132    this.context.app.renderer.render(container, {
34133      renderTexture: texture
34134    });
34135    container.destroy({ children: true });
34136    return texture;
34137  }
34138  /**
34139   * Recreates the stroke by rendering all segments from the stored parameters
34140   * Preserves the exact order of draw/erase operations
34141   */
34142  render_stroke_from_data(stroke_data, target_texture) {
34143    const draw_segments = stroke_data.segments.filter((s) => s.mode === "draw");
34144    const erase_segments = stroke_data.segments.filter(
34145      (s) => s.mode === "erase"
34146    );
34147    if (draw_segments.length > 0) {
34148      const graphics = new Graphics();
34149      const container = new Container();
34150      container.addChild(graphics);
34151      let alpha = 1;
34152      for (const segment of draw_segments) {
34153        if (segment.opacity < alpha) {
34154          alpha = segment.opacity;
34155        }
34156        let colorValue = 16777215;
34157        if (segment.color.startsWith("#")) {
34158          colorValue = parseInt(segment.color.replace("#", "0x"), 16);
34159        }
34160        graphics.setFillStyle({
34161          color: colorValue,
34162          alpha: 1
34163        });
34164        this.render_segment_to_graphics(graphics, segment);
34165      }
34166      const alpha_sprite_texture = RenderTexture.create({
34167        width: target_texture.width,
34168        height: target_texture.height,
34169        resolution: window.devicePixelRatio || 1
34170      });
34171      const alpha_sprite = new Sprite(alpha_sprite_texture);
34172      this.context.app.renderer.render({
34173        container,
34174        target: alpha_sprite_texture
34175      });
34176      alpha_sprite.alpha = alpha;
34177      this.context.app.renderer.render({
34178        container: alpha_sprite,
34179        target: target_texture,
34180        clear: false
34181      });
34182      container.destroy({ children: true });
34183      alpha_sprite.destroy();
34184      alpha_sprite_texture.destroy();
34185    }
34186    if (erase_segments.length > 0) {
34187      const temp_content_texture = RenderTexture.create({
34188        width: target_texture.width,
34189        height: target_texture.height,
34190        resolution: window.devicePixelRatio || 1
34191      });
34192      const copy_sprite = new Sprite(target_texture);
34193      const copy_container = new Container();
34194      copy_container.addChild(copy_sprite);
34195      this.context.app.renderer.render({
34196        container: copy_container,
34197        target: temp_content_texture,
34198        clear: true
34199      });
34200      const erase_graphics = new Graphics();
34201      const erase_container = new Container();
34202      erase_container.addChild(erase_graphics);
34203      erase_graphics.setFillStyle({
34204        color: 16777215,
34205        alpha: 1
34206      });
34207      for (const segment of erase_segments) {
34208        this.render_segment_to_graphics(erase_graphics, segment);
34209      }
34210      const mask_texture = RenderTexture.create({
34211        width: target_texture.width,
34212        height: target_texture.height,
34213        resolution: window.devicePixelRatio || 1
34214      });
34215      this.context.app.renderer.render({
34216        container: erase_container,
34217        target: mask_texture,
34218        clear: true
34219      });
34220      const content_sprite = new Sprite(temp_content_texture);
34221      const mask_sprite = new Sprite(mask_texture);
34222      const masked_container = new Container();
34223      masked_container.addChild(content_sprite);
34224      masked_container.setMask({ mask: mask_sprite, inverse: true });
34225      this.context.app.renderer.render({
34226        container: masked_container,
34227        target: target_texture,
34228        clear: true
34229      });
34230      copy_container.destroy({ children: true });
34231      erase_container.destroy({ children: true });
34232      masked_container.destroy({ children: true });
34233      temp_content_texture.destroy();
34234      mask_texture.destroy();
34235    }
34236  }
34237  /**
34238   * Renders a segment to a graphics object (extracted from renderSegment)
34239   */
34240  render_segment_to_graphics(graphics, segment) {
34241    const distance = Math.sqrt(
34242      Math.pow(segment.to_x - segment.from_x, 2) + Math.pow(segment.to_y - segment.from_y, 2)
34243    );
34244    if (distance < 0.1) {
34245      graphics.circle(segment.from_x, segment.from_y, segment.size).fill();
34246    } else {
34247      const spacing = Math.max(segment.size / 3, 2);
34248      const steps = Math.max(Math.ceil(distance / spacing), 2);
34249      for (let i = 0; i < steps; i++) {
34250        const t = i / (steps - 1);
34251        const x = segment.from_x + (segment.to_x - segment.from_x) * t;
34252        const y = segment.from_y + (segment.to_y - segment.from_y) * t;
34253        graphics.circle(x, y, segment.size).fill();
34254      }
34255    }
34256  }
34257  async execute(context) {
34258    if (context) {
34259      this.context = context;
34260    }
34261    let layer_textures = this.context.layer_manager.get_layer_textures(
34262      this.stroke_data.layer_id
34263    );
34264    if (!layer_textures) {
34265      const all_layers = this.context.layer_manager.get_layers();
34266      const top_layer = all_layers[all_layers.length - 1];
34267      layer_textures = this.context.layer_manager.get_layer_textures(
34268        top_layer.id
34269      );
34270    }
34271    if (!layer_textures) return;
34272    const temp_texture = RenderTexture.create({
34273      width: layer_textures.draw.width,
34274      height: layer_textures.draw.height,
34275      resolution: window.devicePixelRatio || 1
34276    });
34277    const current_sprite = new Sprite(layer_textures.draw);
34278    const temp_container = new Container();
34279    temp_container.addChild(current_sprite);
34280    this.context.app.renderer.render({
34281      container: temp_container,
34282      target: temp_texture,
34283      clear: true
34284    });
34285    temp_container.destroy({ children: true });
34286    this.render_stroke_from_data(this.stroke_data, temp_texture);
34287    const final_sprite = new Sprite(temp_texture);
34288    const final_container = new Container();
34289    final_container.addChild(final_sprite);
34290    this.context.app.renderer.render({
34291      container: final_container,
34292      target: layer_textures.draw,
34293      clear: true
34294    });
34295    final_container.destroy({ children: true });
34296    temp_texture.destroy();
34297  }
34298  async undo() {
34299    if (!this.original_texture) return;
34300    const layer_textures = this.context.layer_manager.get_layer_textures(
34301      this.stroke_data.layer_id
34302    );
34303    if (!layer_textures) return;
34304    const temp_sprite = new Sprite(this.original_texture);
34305    const temp_container = new Container();
34306    temp_container.addChild(temp_sprite);
34307    this.context.app.renderer.render({
34308      container: temp_container,
34309      target: layer_textures.draw,
34310      clear: true
34311    });
34312    temp_container.destroy({ children: true });
34313  }
34314}
34315class BrushTextures {
34316  stroke_texture = null;
34317  erase_texture = null;
34318  display_container = null;
34319  stroke_container = null;
34320  stroke_graphics = null;
34321  preview_sprite = null;
34322  erase_graphics = null;
34323  dimensions;
34324  image_editor_context;
34325  app;
34326  is_new_stroke = true;
34327  current_opacity = 1;
34328  original_layer_texture = null;
34329  active_layer_id = null;
34330  current_stroke_segments = [];
34331  constructor(image_editor_context, app) {
34332    this.image_editor_context = image_editor_context;
34333    this.app = app;
34334    this.dimensions = {
34335      width: this.image_editor_context.image_container.width,
34336      height: this.image_editor_context.image_container.height
34337    };
34338  }
34339  /**
34340   * Initializes textures needed for the brush tool.
34341   */
34342  initialize_textures() {
34343    this.cleanup_textures();
34344    const local_bounds = this.image_editor_context.image_container.getLocalBounds();
34345    this.dimensions = {
34346      width: local_bounds.width,
34347      height: local_bounds.height
34348    };
34349    this.stroke_texture = RenderTexture.create({
34350      width: this.dimensions.width,
34351      height: this.dimensions.height,
34352      resolution: window.devicePixelRatio || 1
34353    });
34354    this.erase_texture = RenderTexture.create({
34355      width: this.dimensions.width,
34356      height: this.dimensions.height,
34357      resolution: window.devicePixelRatio || 1
34358    });
34359    this.display_container = new Container();
34360    this.image_editor_context.image_container.addChild(this.display_container);
34361    this.stroke_container = new Container();
34362    this.stroke_graphics = new Graphics();
34363    this.stroke_container.addChild(this.stroke_graphics);
34364    this.erase_graphics = new Graphics();
34365    this.preview_sprite = new Sprite(this.stroke_texture);
34366    this.preview_sprite.alpha = 0;
34367    this.display_container.addChild(this.preview_sprite);
34368    const clear_container = new Container();
34369    this.app.renderer.render(clear_container, {
34370      renderTexture: this.stroke_texture
34371    });
34372    this.app.renderer.render(clear_container, {
34373      renderTexture: this.erase_texture
34374    });
34375    clear_container.destroy();
34376    this.is_new_stroke = true;
34377    this.current_opacity = 1;
34378  }
34379  /**
34380   * Reinitializes textures when needed (e.g., after resizing).
34381   */
34382  reinitialize() {
34383    if (this.image_editor_context.image_container.width !== this.dimensions.width || this.image_editor_context.image_container.height !== this.dimensions.height) {
34384      this.initialize_textures();
34385    }
34386  }
34387  /**
34388   * Cleans up texture resources.
34389   */
34390  cleanup_textures() {
34391    if (this.stroke_texture) {
34392      this.stroke_texture.destroy();
34393      this.stroke_texture = null;
34394    }
34395    if (this.erase_texture) {
34396      this.erase_texture.destroy();
34397      this.erase_texture = null;
34398    }
34399    if (this.display_container) {
34400      if (this.display_container.parent) {
34401        this.display_container.parent.removeChild(this.display_container);
34402      }
34403      this.display_container.destroy({ children: true });
34404      this.display_container = null;
34405    }
34406    if (this.original_layer_texture) {
34407      this.original_layer_texture.destroy();
34408      this.original_layer_texture = null;
34409    }
34410    this.stroke_container = null;
34411    this.stroke_graphics = null;
34412    this.preview_sprite = null;
34413    this.erase_graphics = null;
34414    this.active_layer_id = null;
34415  }
34416  /**
34417   * Preserve the canvas state when starting a new stroke.
34418   */
34419  preserve_canvas_state() {
34420    const active_layer = this.image_editor_context.layer_manager.get_active_layer();
34421    if (!active_layer) return;
34422    const layers = this.image_editor_context.layer_manager.get_layers();
34423    const layer = layers.find((l) => l.container === active_layer);
34424    if (!layer) return;
34425    this.active_layer_id = layer.id;
34426    const layer_textures = this.image_editor_context.layer_manager.get_layer_textures(layer.id);
34427    if (!layer_textures) return;
34428    if (this.original_layer_texture) {
34429      this.original_layer_texture.destroy();
34430    }
34431    this.original_layer_texture = RenderTexture.create({
34432      width: this.dimensions.width,
34433      height: this.dimensions.height,
34434      resolution: window.devicePixelRatio || 1
34435    });
34436    const temp_sprite = new Sprite(layer_textures.draw);
34437    const temp_container = new Container();
34438    temp_container.addChild(temp_sprite);
34439    this.app.renderer.render(temp_container, {
34440      renderTexture: this.original_layer_texture
34441    });
34442    temp_container.destroy({ children: true });
34443    this.is_new_stroke = true;
34444  }
34445  /**
34446   * Resets the eraser mask.
34447   */
34448  reset_eraser_mask() {
34449    if (!this.erase_graphics || !this.erase_texture) return;
34450    this.erase_graphics.clear();
34451    this.erase_graphics.setFillStyle({ color: 16777215, alpha: 1 });
34452    this.erase_graphics.rect(0, 0, this.dimensions.width, this.dimensions.height).fill();
34453    this.erase_graphics.endFill();
34454    this.app.renderer.render(this.erase_graphics, {
34455      renderTexture: this.erase_texture
34456    });
34457  }
34458  /**
34459   * Commits the current stroke to the active layer and returns a command for undo/redo.
34460   */
34461  commit_stroke() {
34462    if (!this.stroke_texture || !this.preview_sprite || !this.stroke_graphics || !this.original_layer_texture || !this.active_layer_id)
34463      return;
34464    this.preview_sprite.visible = false;
34465    const active_layer = this.image_editor_context.layer_manager.get_active_layer();
34466    if (!active_layer) return;
34467    const layers = this.image_editor_context.layer_manager.get_layers();
34468    const layer = layers.find((l) => l.container === active_layer);
34469    if (!layer) return;
34470    if (layer.id !== this.active_layer_id) return;
34471    const layer_textures = this.image_editor_context.layer_manager.get_layer_textures(layer.id);
34472    if (!layer_textures) return;
34473    const stroke_data = {
34474      segments: [...this.current_stroke_segments],
34475      layer_id: this.active_layer_id
34476    };
34477    const brush_command = new BrushCommand(
34478      this.image_editor_context,
34479      stroke_data,
34480      this.original_layer_texture
34481    );
34482    if (this.stroke_graphics) {
34483      this.stroke_graphics.clear();
34484    }
34485    const clear_container = new Container();
34486    this.app.renderer.render(clear_container, {
34487      renderTexture: this.stroke_texture
34488    });
34489    clear_container.destroy();
34490    this.is_new_stroke = true;
34491    this.original_layer_texture = null;
34492    this.active_layer_id = null;
34493    this.current_stroke_segments = [];
34494    this.image_editor_context.command_manager.execute(
34495      brush_command,
34496      this.image_editor_context
34497    );
34498  }
34499  /**
34500   * Calculates the distance between two points.
34501   */
34502  calculate_distance(x1, y1, x2, y2) {
34503    return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
34504  }
34505  /**
34506   * Draws a segment for the current stroke using a simple "stamp" approach.
34507   */
34508  draw_segment(from_x, from_y, to_x, to_y, size, color, opacity, mode) {
34509    if (!this.stroke_graphics || !this.stroke_texture || !this.stroke_container || !this.preview_sprite)
34510      return;
34511    if (this.is_new_stroke && !this.original_layer_texture) {
34512      this.preserve_canvas_state();
34513    }
34514    this.current_opacity = mode === "draw" ? Math.min(Math.max(opacity, 0), 1) : 0.5;
34515    const scaled_size = size;
34516    if (this.is_new_stroke) {
34517      this.stroke_graphics.clear();
34518      this.current_stroke_segments = [];
34519      const clear_container = new Container();
34520      this.app.renderer.render(clear_container, {
34521        renderTexture: this.stroke_texture
34522      });
34523      clear_container.destroy();
34524      this.is_new_stroke = false;
34525    }
34526    this.current_stroke_segments.push({
34527      from_x,
34528      from_y,
34529      to_x,
34530      to_y,
34531      size: scaled_size,
34532      color,
34533      opacity: this.current_opacity,
34534      mode
34535    });
34536    if (mode === "draw") {
34537      let colorValue = 16777215;
34538      try {
34539        if (color.startsWith("#")) {
34540          colorValue = parseInt(color.replace("#", "0x"), 16);
34541        }
34542      } catch (e) {
34543        colorValue = 16777215;
34544      }
34545      this.stroke_graphics.setFillStyle({
34546        color: colorValue,
34547        alpha: 1
34548      });
34549    } else {
34550      this.stroke_graphics.setFillStyle({
34551        color: 16777215,
34552        alpha: 1
34553      });
34554    }
34555    const distance = this.calculate_distance(from_x, from_y, to_x, to_y);
34556    if (distance < 0.1) {
34557      this.stroke_graphics.circle(from_x, from_y, scaled_size).fill();
34558    } else {
34559      const spacing = Math.max(scaled_size / 3, 2);
34560      const steps = Math.max(Math.ceil(distance / spacing), 2);
34561      for (let i = 0; i < steps; i++) {
34562        const t = i / (steps - 1);
34563        const x = from_x + (to_x - from_x) * t;
34564        const y = from_y + (to_y - from_y) * t;
34565        this.stroke_graphics.circle(x, y, scaled_size).fill();
34566      }
34567    }
34568    this.stroke_graphics.endFill();
34569    this.app.renderer.render(this.stroke_container, {
34570      renderTexture: this.stroke_texture
34571    });
34572    if (mode === "draw") {
34573      this.preview_sprite.texture = this.stroke_texture;
34574      this.preview_sprite.alpha = this.current_opacity;
34575      this.preview_sprite.tint = 16777215;
34576    } else {
34577      const active_layer = this.image_editor_context.layer_manager.get_active_layer();
34578      if (!active_layer) return;
34579      const layers = this.image_editor_context.layer_manager.get_layers();
34580      const layer = layers.find((l) => l.container === active_layer);
34581      if (!layer) return;
34582      const layer_textures = this.image_editor_context.layer_manager.get_layer_textures(layer.id);
34583      if (!layer_textures) return;
34584      const preview_texture = RenderTexture.create({
34585        width: this.dimensions.width,
34586        height: this.dimensions.height,
34587        resolution: window.devicePixelRatio || 1
34588      });
34589      const base_container = new Container();
34590      const content_sprite = new Sprite(layer_textures.draw);
34591      base_container.addChild(content_sprite);
34592      this.app.renderer.render(base_container, {
34593        renderTexture: preview_texture
34594      });
34595      base_container.destroy({ children: true });
34596      const preview_container = new Container();
34597      const mask_sprite = new Sprite(this.stroke_texture);
34598      const overlay = new Graphics();
34599      overlay.setFillStyle({ color: 16777215, alpha: 0.5 });
34600      overlay.rect(0, 0, this.dimensions.width, this.dimensions.height).fill();
34601      overlay.setMask({ mask: mask_sprite, inverse: false });
34602      preview_container.addChild(overlay);
34603      this.app.renderer.render(preview_container, {
34604        renderTexture: preview_texture
34605      });
34606      this.preview_sprite.texture = preview_texture;
34607      this.preview_sprite.alpha = 1;
34608      preview_container.destroy({ children: true });
34609      preview_texture.destroy();
34610    }
34611    this.preview_sprite.visible = true;
34612  }
34613  /**
34614   * Gets current dimensions of the texture.
34615   */
34616  get_dimensions() {
34617    return this.dimensions;
34618  }
34619  /**
34620   * Checks if textures are properly initialized.
34621   */
34622  get textures_initialized() {
34623    return !!(this.stroke_texture && this.display_container && this.preview_sprite);
34624  }
34625  /**
34626   * Cleanup all resources.
34627   */
34628  cleanup() {
34629    this.cleanup_textures();
34630  }
34631}
34632
34633class BrushTool {
34634  /**
34635   * The name of the tool.
34636   * @type {string}
34637   */
34638  name = "brush";
34639  /**
34640   * The context of the image editor.
34641   * @private
34642   */
34643  image_editor_context;
34644  /**
34645   * The current tool selected in the toolbar.
34646   * @private
34647   */
34648  current_tool;
34649  /**
34650   * The current subtool selected in the toolbar.
34651   * @private
34652   */
34653  current_subtool;
34654  /**
34655   * The state of the brush tool.
34656   * @private
34657   */
34658  state = {
34659    opacity: 1,
34660    brush_size: 10,
34661    color: "#000000",
34662    mode: "draw"
34663  };
34664  /**
34665   * Size settings for brush and eraser
34666   * @private
34667   */
34668  brush_size = 10;
34669  eraser_size = 20;
34670  /**
34671   * Drawing state
34672   * @private
34673   */
34674  is_drawing = false;
34675  last_x = 0;
34676  last_y = 0;
34677  scale = 1;
34678  /**
34679   * Event listeners
34680   * @private
34681   */
34682  _bound_pointer_down = null;
34683  _bound_pointer_move = null;
34684  _bound_pointer_up = null;
34685  event_callbacks = /* @__PURE__ */ new Map();
34686  /**
34687   * Utility modules
34688   * @private
34689   */
34690  brush_cursor = null;
34691  brush_textures = null;
34692  /**
34693   * Sets up the brush tool with the given context, tool, and subtool.
34694   * @param {ImageEditorContext} context - The image editor context.
34695   * @param {ToolbarTool} tool - The tool from the toolbar.
34696   * @param {Subtool} subtool - The subtool from the toolbar.
34697   * @returns {Promise<void>}
34698   */
34699  async setup(context, tool, subtool) {
34700    this.image_editor_context = context;
34701    this.current_tool = tool;
34702    this.current_subtool = subtool;
34703    this.state.mode = tool === "erase" ? "erase" : "draw";
34704    if (this.state.mode === "draw") {
34705      this.state.brush_size = this.brush_size;
34706    } else {
34707      this.state.brush_size = this.eraser_size;
34708    }
34709    context.scale.subscribe((scale) => {
34710      this.scale = scale;
34711      if (this.brush_cursor) {
34712        this.brush_cursor.update_state(this.state, this.scale);
34713      }
34714    });
34715    this.brush_cursor = new BrushCursor(
34716      this.image_editor_context,
34717      this.state,
34718      this.scale
34719    );
34720    this.brush_cursor.set_active(tool === "draw" || tool === "erase");
34721    this.brush_textures = new BrushTextures(
34722      this.image_editor_context,
34723      context.app
34724    );
34725    this.brush_textures.initialize_textures();
34726    this.setup_event_listeners();
34727    this.handle_cursors(tool);
34728  }
34729  /**
34730   * Handles cursor styles for the brush tool.
34731   * @param {ToolbarTool} tool - The current tool.
34732   * @private
34733   */
34734  handle_cursors(tool) {
34735    recurse_set_cursor(
34736      this.image_editor_context.image_container.children,
34737      "none"
34738    );
34739  }
34740  /**
34741   * Sets the current tool and subtool.
34742   * @param {ToolbarTool} tool - The current tool.
34743   * @param {Subtool} subtool - The current subtool.
34744   */
34745  set_tool(tool, subtool) {
34746    this.current_tool = tool;
34747    this.current_subtool = subtool;
34748    if (this.current_tool !== "erase" && this.current_tool !== "draw") {
34749      this.commit_pending_changes();
34750    }
34751    if (this.brush_cursor) {
34752      const should_be_active = tool === "draw" || tool === "erase";
34753      this.brush_cursor.set_active(should_be_active);
34754    }
34755    const new_mode = tool === "erase" ? "erase" : "draw";
34756    const mode_changed = this.state.mode !== new_mode;
34757    const needs_brush_tool = tool === "erase" || tool === "draw";
34758    const textures_initialized = this.brush_textures?.textures_initialized ?? false;
34759    if (needs_brush_tool && (mode_changed || !textures_initialized)) {
34760      this.brush_textures?.initialize_textures();
34761    }
34762    if (this.state.mode !== new_mode) {
34763      this.state.mode = new_mode;
34764      if (this.state.mode === "draw") {
34765        this.state.brush_size = this.brush_size;
34766      } else {
34767        this.state.brush_size = this.eraser_size;
34768      }
34769      if (this.brush_cursor) {
34770        this.brush_cursor.update_state(this.state, this.scale);
34771      }
34772    }
34773  }
34774  /**
34775   * Commits any pending changes to the canvas.
34776   * @private
34777   */
34778  commit_pending_changes() {
34779    if (this.is_drawing) {
34780      this.on_pointer_up();
34781    }
34782  }
34783  /**
34784   * Called when the user presses the mouse button.
34785   * @param {FederatedPointerEvent} event - The pointer event.
34786   * @private
34787   */
34788  on_pointer_down(event) {
34789    const current_layer = this.image_editor_context.layer_manager.get_active_layer();
34790    if (!current_layer?.visible || this.current_tool !== "erase" && this.current_tool !== "draw") {
34791      return;
34792    }
34793    if (this.brush_cursor && !this.brush_cursor.is_over_image()) {
34794      return;
34795    }
34796    if (this.brush_textures) {
34797      this.brush_textures.preserve_canvas_state();
34798    }
34799    const local_pos = this.image_editor_context.image_container.toLocal(
34800      event.global
34801    );
34802    this.is_drawing = true;
34803    this.last_x = local_pos.x;
34804    this.last_y = local_pos.y;
34805    if (this.brush_textures) {
34806      this.brush_textures.draw_segment(
34807        local_pos.x,
34808        local_pos.y,
34809        local_pos.x,
34810        local_pos.y,
34811        this.state.brush_size,
34812        this.state.color,
34813        this.state.opacity,
34814        this.state.mode
34815      );
34816    }
34817  }
34818  /**
34819   * Called when the user moves the mouse while drawing.
34820   * @param {FederatedPointerEvent} event - The pointer event.
34821   * @private
34822   */
34823  on_pointer_move(event) {
34824    if (this.brush_cursor) {
34825      this.brush_cursor.update_cursor_position(event);
34826    }
34827    if (!this.is_drawing) return;
34828    const local_pos = this.image_editor_context.image_container.toLocal(
34829      event.global
34830    );
34831    if (this.brush_textures) {
34832      this.brush_textures.draw_segment(
34833        this.last_x,
34834        this.last_y,
34835        local_pos.x,
34836        local_pos.y,
34837        this.state.brush_size,
34838        this.state.color,
34839        this.state.opacity,
34840        this.state.mode
34841      );
34842    }
34843    this.last_x = local_pos.x;
34844    this.last_y = local_pos.y;
34845  }
34846  /**
34847   * Called when the user releases the mouse button.
34848   * @private
34849   */
34850  on_pointer_up() {
34851    if (!this.is_drawing) return;
34852    this.is_drawing = false;
34853    if (this.brush_textures) {
34854      this.brush_textures.commit_stroke();
34855    }
34856    this.notify("change");
34857  }
34858  /**
34859   * Sets up event listeners for drawing.
34860   * @private
34861   */
34862  setup_event_listeners() {
34863    this.cleanup_event_listeners();
34864    this._bound_pointer_down = this.on_pointer_down.bind(this);
34865    this._bound_pointer_move = this.on_pointer_move.bind(this);
34866    this._bound_pointer_up = this.on_pointer_up.bind(this);
34867    const image_container = this.image_editor_context.image_container;
34868    image_container.eventMode = "static";
34869    image_container.interactiveChildren = true;
34870    const stage = this.image_editor_context.app.stage;
34871    stage.eventMode = "static";
34872    stage.on("pointerdown", this._bound_pointer_down);
34873    stage.on("pointermove", this._bound_pointer_move);
34874    stage.on("pointerup", this._bound_pointer_up);
34875    stage.on("pointerupoutside", this._bound_pointer_up);
34876    if (this.brush_cursor) {
34877      this.brush_cursor.setup_event_listeners();
34878    }
34879  }
34880  /**
34881   * Cleans up event listeners.
34882   * @private
34883   */
34884  cleanup_event_listeners() {
34885    const stage = this.image_editor_context.app.stage;
34886    if (this._bound_pointer_down) {
34887      stage.off("pointerdown", this._bound_pointer_down);
34888      this._bound_pointer_down = null;
34889    }
34890    if (this._bound_pointer_move) {
34891      stage.off("pointermove", this._bound_pointer_move);
34892      this._bound_pointer_move = null;
34893    }
34894    if (this._bound_pointer_up) {
34895      stage.off("pointerup", this._bound_pointer_up);
34896      stage.off("pointerupoutside", this._bound_pointer_up);
34897      this._bound_pointer_up = null;
34898    }
34899    if (this.brush_cursor) {
34900      this.brush_cursor.cleanup_event_listeners();
34901    }
34902  }
34903  /**
34904   * Sets the brush size.
34905   * @param {number} size - The new brush size.
34906   */
34907  set_brush_size(size) {
34908    this.brush_size = size;
34909    if (this.state.mode === "draw") {
34910      this.state.brush_size = size;
34911      if (this.brush_cursor) {
34912        this.brush_cursor.update_state(this.state, this.scale);
34913      }
34914    }
34915  }
34916  /**
34917   * Sets the brush color.
34918   * @param {string|ColorInput} color - The new brush color.
34919   */
34920  set_brush_color(color) {
34921    const color_string = tinycolor(color).toHexString();
34922    this.state.color = color_string;
34923    if (this.brush_cursor) {
34924      this.brush_cursor.update_state(this.state, this.scale);
34925    }
34926  }
34927  /**
34928   * Sets the brush opacity.
34929   * @param {number} opacity - The new brush opacity.
34930   */
34931  set_brush_opacity(opacity) {
34932    const clamped_opacity = Math.max(0, Math.min(1, opacity));
34933    this.state.opacity = clamped_opacity;
34934    if (this.brush_cursor) {
34935      this.brush_cursor.update_state(this.state, this.scale);
34936    }
34937  }
34938  /**
34939   * Sets the eraser size.
34940   * @param {number} size - The new eraser size.
34941   */
34942  set_eraser_size(size) {
34943    this.eraser_size = size;
34944    if (this.state.mode === "erase") {
34945      this.state.brush_size = size;
34946      if (this.brush_cursor) {
34947        this.brush_cursor.update_state(this.state, this.scale);
34948      }
34949    }
34950  }
34951  /**
34952   * Gets the current size based on mode (brush or eraser).
34953   * @returns {number} The current size.
34954   */
34955  get_current_size() {
34956    return this.state.mode === "draw" ? this.brush_size : this.eraser_size;
34957  }
34958  /**
34959   * Shows or hides the brush preview.
34960   * @param {boolean} show - Whether to show the preview.
34961   */
34962  preview_brush(show) {
34963    if (this.brush_cursor) {
34964      this.brush_cursor.preview_brush(show);
34965    }
34966  }
34967  /**
34968   * Cleans up resources used by the brush tool.
34969   */
34970  cleanup() {
34971    this.commit_pending_changes();
34972    this.cleanup_event_listeners();
34973    if (this.brush_textures) {
34974      this.brush_textures.cleanup();
34975      this.brush_textures = null;
34976    }
34977    if (this.brush_cursor) {
34978      this.brush_cursor.cleanup();
34979      this.brush_cursor = null;
34980    }
34981  }
34982  on(event, callback) {
34983    this.event_callbacks.set(event, [
34984      ...this.event_callbacks.get(event) || [],
34985      callback
34986    ]);
34987  }
34988  off(event, callback) {
34989    this.event_callbacks.set(
34990      event,
34991      this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []
34992    );
34993  }
34994  notify(event) {
34995    for (const callback of this.event_callbacks.get(event) || []) {
34996      callback();
34997    }
34998  }
34999}
35000
35001var root_6$1 = from_html(`<div class="svelte-189k4ah"><!> <!> <!></div>`);
35002var root_3$2 = from_html(`<li class="svelte-189k4ah"><!> <button> </button> <!></li>`);
35003var root_10$1 = from_html(`<li class="add-layer svelte-189k4ah"><!></li>`);
35004var root_2$2 = from_html(`<ul class="svelte-189k4ah"><!> <!></ul>`);
35005var root_1$3 = from_html(`<div><button class="layer-title-button svelte-189k4ah" aria-label="Show Layers"> <span class="icon svelte-189k4ah"><!></span></button> <!></div>`);
35006
35007function Layers_1($$anchor, $$props) {
35008	push($$props, false);
35009
35010	const $layers = () => store_get(layers(), '$layers', $$stores);
35011	const [$$stores, $$cleanup] = setup_stores();
35012	const dispatch = createEventDispatcher();
35013	let layers = prop($$props, 'layers', 12);
35014	let enable_additional_layers = prop($$props, 'enable_additional_layers', 12, true);
35015	let enable_layers = prop($$props, 'enable_layers', 12, true);
35016	let show_layers = prop($$props, 'show_layers', 12, false);
35017
35018	function new_layer() {
35019		dispatch("new_layer");
35020	}
35021
35022	function change_layer(id) {
35023		dispatch("change_layer", id);
35024		show_layers(false);
35025	}
35026
35027	function move_layer(id, direction) {
35028		dispatch("move_layer", { id, direction });
35029	}
35030
35031	function delete_layer(id) {
35032		dispatch("delete_layer", id);
35033	}
35034
35035	var $$exports = {
35036		get layers() {
35037			return layers();
35038		},
35039
35040		set layers($$value) {
35041			layers($$value);
35042			flushSync();
35043		},
35044
35045		get enable_additional_layers() {
35046			return enable_additional_layers();
35047		},
35048
35049		set enable_additional_layers($$value) {
35050			enable_additional_layers($$value);
35051			flushSync();
35052		},
35053
35054		get enable_layers() {
35055			return enable_layers();
35056		},
35057
35058		set enable_layers($$value) {
35059			enable_layers($$value);
35060			flushSync();
35061		},
35062
35063		get show_layers() {
35064			return show_layers();
35065		},
35066
35067		set show_layers($$value) {
35068			show_layers($$value);
35069			flushSync();
35070		}
35071	};
35072
35073	init$1();
35074
35075	var fragment = comment();
35076	var node = first_child(fragment);
35077
35078	{
35079		var consequent_7 = ($$anchor) => {
35080			var div = root_1$3();
35081			let classes;
35082			var button = child(div);
35083			var text = child(button);
35084			var span = sibling(text);
35085			var node_1 = child(span);
35086
35087			Layers(node_1);
35088			reset(span);
35089			reset(button);
35090
35091			var node_2 = sibling(button, 2);
35092
35093			{
35094				var consequent_6 = ($$anchor) => {
35095					var ul = root_2$2();
35096					var node_3 = child(ul);
35097
35098					each(node_3, 1, () => ($layers(), untrack(() => $layers().layers)), index, ($$anchor, $$item, i) => {
35099						let id = () => get($$item).id;
35100						let name = () => get($$item).name;
35101						let user_created = () => get($$item).user_created;
35102						let visible = () => get($$item).visible;
35103						var li = root_3$2();
35104						var node_4 = child(li);
35105
35106						{
35107							var consequent = ($$anchor) => {
35108								IconButton$1($$anchor, {
35109									get Icon() {
35110										return VisibilityOff;
35111									},
35112									size: 'small',
35113									onclick: (e) => {
35114										e.stopPropagation();
35115										dispatch("toggle_layer_visibility", id());
35116									}
35117								});
35118							};
35119
35120							var alternate = ($$anchor) => {
35121								IconButton$1($$anchor, {
35122									get Icon() {
35123										return Visibility;
35124									},
35125									size: 'small',
35126									onclick: (e) => {
35127										e.stopPropagation();
35128										dispatch("toggle_layer_visibility", id());
35129									}
35130								});
35131							};
35132
35133							if_block(node_4, ($$render) => {
35134								if (!visible()) $$render(consequent); else $$render(alternate, false);
35135							});
35136						}
35137
35138						var button_1 = sibling(node_4, 2);
35139
35140						set_attribute(button_1, 'aria-label', `layer-${i + 1}`);
35141
35142						let classes_1;
35143						var text_1 = child(button_1, true);
35144
35145						reset(button_1);
35146
35147						var node_5 = sibling(button_1, 2);
35148
35149						{
35150							var consequent_4 = ($$anchor) => {
35151								var div_1 = root_6$1();
35152								var node_6 = child(div_1);
35153
35154								{
35155									var consequent_1 = ($$anchor) => {
35156										IconButton$1($$anchor, {
35157											onclick: (e) => {
35158												e.stopPropagation();
35159												move_layer(id(), "up");
35160											},
35161
35162											get Icon() {
35163												return ArrowUp;
35164											},
35165											size: 'x-small'
35166										});
35167									};
35168
35169									if_block(node_6, ($$render) => {
35170										if (i > 0) $$render(consequent_1);
35171									});
35172								}
35173
35174								var node_7 = sibling(node_6, 2);
35175
35176								{
35177									var consequent_2 = ($$anchor) => {
35178										IconButton$1($$anchor, {
35179											onclick: (e) => {
35180												e.stopPropagation();
35181												move_layer(id(), "down");
35182											},
35183
35184											get Icon() {
35185												return ArrowDown;
35186											},
35187											size: 'x-small'
35188										});
35189									};
35190
35191									if_block(node_7, ($$render) => {
35192										if ((
35193											$layers(),
35194											untrack(() => i < $layers().layers.length - 1)
35195										)) $$render(consequent_2);
35196									});
35197								}
35198
35199								var node_8 = sibling(node_7, 2);
35200
35201								{
35202									var consequent_3 = ($$anchor) => {
35203										IconButton$1($$anchor, {
35204											onclick: (e) => {
35205												e.stopPropagation();
35206												delete_layer(id());
35207											},
35208
35209											get Icon() {
35210												return Clear;
35211											},
35212											size: 'x-small'
35213										});
35214									};
35215
35216									if_block(node_8, ($$render) => {
35217										if ((
35218											$layers(),
35219											user_created(),
35220											untrack(() => $layers().layers.length > 1 && user_created())
35221										)) $$render(consequent_3);
35222									});
35223								}
35224
35225								reset(div_1);
35226								append($$anchor, div_1);
35227							};
35228
35229							if_block(node_5, ($$render) => {
35230								if (($layers(), untrack(() => $layers().layers.length > 1))) $$render(consequent_4);
35231							});
35232						}
35233
35234						reset(li);
35235
35236						template_effect(() => {
35237							classes_1 = set_class(button_1, 1, 'svelte-189k4ah', null, classes_1, { selected_layer: $layers().active_layer === id() });
35238							set_text(text_1, name());
35239						});
35240
35241						event('click', button_1, stopPropagation(() => change_layer(id())));
35242						append($$anchor, li);
35243					});
35244
35245					var node_9 = sibling(node_3, 2);
35246
35247					{
35248						var consequent_5 = ($$anchor) => {
35249							var li_1 = root_10$1();
35250							var node_10 = child(li_1);
35251
35252							IconButton$1(node_10, {
35253								get Icon() {
35254									return Plus;
35255								},
35256								label: 'Add Layer',
35257								onclick: (e) => {
35258									e.stopPropagation();
35259									new_layer();
35260								},
35261								size: 'x-small'
35262							});
35263
35264							reset(li_1);
35265							append($$anchor, li_1);
35266						};
35267
35268						if_block(node_9, ($$render) => {
35269							if (enable_additional_layers()) $$render(consequent_5);
35270						});
35271					}
35272
35273					reset(ul);
35274					append($$anchor, ul);
35275				};
35276
35277				if_block(node_2, ($$render) => {
35278					if (show_layers()) $$render(consequent_6);
35279				});
35280			}
35281
35282			reset(div);
35283			action(div, ($$node, $$action_arg) => click_outside?.($$node, $$action_arg), () => () => show_layers(false));
35284
35285			template_effect(
35286				($0) => {
35287					classes = set_class(div, 1, 'layer-wrap svelte-189k4ah', null, classes, { closed: !show_layers() });
35288					set_text(text, `${$0 ?? ''} `);
35289				},
35290				[
35291					() => (
35292						deep_read_state(show_layers()),
35293						$layers(),
35294						untrack(() => show_layers()
35295							? "Layers"
35296							: $layers().layers.find((l) => l.id === $layers().active_layer)?.name)
35297					)
35298				]
35299			);
35300
35301			event('click', button, stopPropagation(() => show_layers(!show_layers())));
35302			append($$anchor, div);
35303		};
35304
35305		if_block(node, ($$render) => {
35306			if (enable_layers()) $$render(consequent_7);
35307		});
35308	}
35309
35310	append($$anchor, fragment);
35311
35312	var $$pop = pop($$exports);
35313
35314	$$cleanup();
35315
35316	return $$pop;
35317}
35318
35319var root = from_html(`<div class="toolbar-wrap-wrap svelte-1357rcu"><div class="toolbar-wrap svelte-1357rcu"><!></div></div>`);
35320
35321function SecondaryToolbar($$anchor, $$props) {
35322	push($$props, false);
35323
35324	let layers = prop($$props, 'layers', 12);
35325	let enable_additional_layers = prop($$props, 'enable_additional_layers', 12, true);
35326	let enable_layers = prop($$props, 'enable_layers', 12, true);
35327	let show_layers = prop($$props, 'show_layers', 12, false);
35328
35329	var $$exports = {
35330		get layers() {
35331			return layers();
35332		},
35333
35334		set layers($$value) {
35335			layers($$value);
35336			flushSync();
35337		},
35338
35339		get enable_additional_layers() {
35340			return enable_additional_layers();
35341		},
35342
35343		set enable_additional_layers($$value) {
35344			enable_additional_layers($$value);
35345			flushSync();
35346		},
35347
35348		get enable_layers() {
35349			return enable_layers();
35350		},
35351
35352		set enable_layers($$value) {
35353			enable_layers($$value);
35354			flushSync();
35355		},
35356
35357		get show_layers() {
35358			return show_layers();
35359		},
35360
35361		set show_layers($$value) {
35362			show_layers($$value);
35363			flushSync();
35364		}
35365	};
35366
35367	var div = root();
35368	var div_1 = child(div);
35369	var node = child(div_1);
35370
35371	{
35372		var consequent = ($$anchor) => {
35373			Layers_1($$anchor, {
35374				get layers() {
35375					return layers();
35376				},
35377
35378				get enable_additional_layers() {
35379					return enable_additional_layers();
35380				},
35381
35382				get enable_layers() {
35383					return enable_layers();
35384				},
35385
35386				get show_layers() {
35387					return show_layers();
35388				},
35389
35390				$$events: {
35391					new_layer($$arg) {
35392						bubble_event.call(this, $$props, $$arg);
35393					},
35394
35395					change_layer($$arg) {
35396						bubble_event.call(this, $$props, $$arg);
35397					},
35398
35399					move_layer($$arg) {
35400						bubble_event.call(this, $$props, $$arg);
35401					},
35402
35403					delete_layer($$arg) {
35404						bubble_event.call(this, $$props, $$arg);
35405					},
35406
35407					toggle_layer_visibility($$arg) {
35408						bubble_event.call(this, $$props, $$arg);
35409					}
35410				}
35411			});
35412		};
35413
35414		if_block(node, ($$render) => {
35415			if (enable_layers()) $$render(consequent);
35416		});
35417	}
35418
35419	reset(div_1);
35420	reset(div);
35421	append($$anchor, div);
35422
35423	return pop($$exports);
35424}
35425
35426var root_4$1 = from_html(`<li class="svelte-o0h5ih"><button class="svelte-o0h5ih"> </button></li>`);
35427var root_3$1 = from_html(`<div class="zoom-controls svelte-o0h5ih"><ul class="svelte-o0h5ih"><li class="svelte-o0h5ih"><button class="svelte-o0h5ih">Fit to screen</button></li> <!></ul></div>`);
35428var root_1$2 = from_html(`<!> <!> <!> <!> <div class="zoom-number svelte-o0h5ih"><span role="button" tabindex="0" class="svelte-o0h5ih"> </span> <!></div> <div class="separator svelte-o0h5ih"></div> <!> <!> <!> <!>`, 1);
35429
35430function Controls($$anchor, $$props) {
35431	push($$props, false);
35432
35433	const formatted_zoom = mutable_source();
35434	let can_save = prop($$props, 'can_save', 12, false);
35435	let changeable = prop($$props, 'changeable', 12, false);
35436	let current_zoom = prop($$props, 'current_zoom', 12, 1);
35437	let tool = prop($$props, 'tool', 12);
35438	let min_zoom = prop($$props, 'min_zoom', 12, true);
35439	let enable_download = prop($$props, 'enable_download', 12, false);
35440	let can_undo = prop($$props, 'can_undo', 12);
35441	let can_redo = prop($$props, 'can_redo', 12);
35442	const dispatch = createEventDispatcher();
35443	let show_zoom_popup = mutable_source(false);
35444
35445	function handle_zoom_click(e) {
35446		e.stopPropagation();
35447		set(show_zoom_popup, !get(show_zoom_popup));
35448	}
35449
35450	function handle_zoom_change(zoom) {
35451		dispatch("set_zoom", zoom);
35452		set(show_zoom_popup, false);
35453	}
35454
35455	function handle_zoom_keydown(e) {
35456		if (e.key === "Enter") {
35457			handle_zoom_change(current_zoom());
35458		}
35459	}
35460
35461	legacy_pre_effect(() => (deep_read_state(current_zoom())), () => {
35462		set(formatted_zoom, Math.round(current_zoom() * 100));
35463	});
35464
35465	legacy_pre_effect_reset();
35466
35467	var $$exports = {
35468		get can_save() {
35469			return can_save();
35470		},
35471
35472		set can_save($$value) {
35473			can_save($$value);
35474			flushSync();
35475		},
35476
35477		get changeable() {
35478			return changeable();
35479		},
35480
35481		set changeable($$value) {
35482			changeable($$value);
35483			flushSync();
35484		},
35485
35486		get current_zoom() {
35487			return current_zoom();
35488		},
35489
35490		set current_zoom($$value) {
35491			current_zoom($$value);
35492			flushSync();
35493		},
35494
35495		get tool() {
35496			return tool();
35497		},
35498
35499		set tool($$value) {
35500			tool($$value);
35501			flushSync();
35502		},
35503
35504		get min_zoom() {
35505			return min_zoom();
35506		},
35507
35508		set min_zoom($$value) {
35509			min_zoom($$value);
35510			flushSync();
35511		},
35512
35513		get enable_download() {
35514			return enable_download();
35515		},
35516
35517		set enable_download($$value) {
35518			enable_download($$value);
35519			flushSync();
35520		},
35521
35522		get can_undo() {
35523			return can_undo();
35524		},
35525
35526		set can_undo($$value) {
35527			can_undo($$value);
35528			flushSync();
35529		},
35530
35531		get can_redo() {
35532			return can_redo();
35533		},
35534
35535		set can_redo($$value) {
35536			can_redo($$value);
35537			flushSync();
35538		}
35539	};
35540
35541	init$1();
35542
35543	IconButtonWrapper($$anchor, {
35544		children: ($$anchor, $$slotProps) => {
35545			var fragment_1 = root_1$2();
35546			var node = first_child(fragment_1);
35547
35548			{
35549				var consequent = ($$anchor) => {
35550					IconButton$1($$anchor, {
35551						get Icon() {
35552							return Download;
35553						},
35554						label: 'Download',
35555						onclick: (event) => {
35556							dispatch("download");
35557							event.stopPropagation();
35558						}
35559					});
35560				};
35561
35562				if_block(node, ($$render) => {
35563					if (enable_download()) $$render(consequent);
35564				});
35565			}
35566
35567			var node_1 = sibling(node, 2);
35568
35569			{
35570				let $0 = derived_safe_equal(() => tool() === "pan");
35571
35572				IconButton$1(node_1, {
35573					get Icon() {
35574						return Pan;
35575					},
35576					label: 'Pan',
35577					onclick: (e) => {
35578						e.stopPropagation();
35579						dispatch("pan");
35580					},
35581
35582					get highlight() {
35583						return get($0);
35584					},
35585					size: 'small',
35586					padded: false,
35587					transparent: true,
35588					get disabled() {
35589						return min_zoom();
35590					}
35591				});
35592			}
35593
35594			var node_2 = sibling(node_1, 2);
35595
35596			IconButton$1(node_2, {
35597				get Icon() {
35598					return ZoomOut;
35599				},
35600				label: 'Zoom out',
35601				onclick: (event) => {
35602					dispatch("zoom_out");
35603					event.stopPropagation();
35604				}
35605			});
35606
35607			var node_3 = sibling(node_2, 2);
35608
35609			IconButton$1(node_3, {
35610				get Icon() {
35611					return ZoomIn;
35612				},
35613				label: 'Zoom in',
35614				onclick: (event) => {
35615					dispatch("zoom_in");
35616					event.stopPropagation();
35617				}
35618			});
35619
35620			var div = sibling(node_3, 2);
35621			var span = child(div);
35622			var text = child(span);
35623
35624			reset(span);
35625
35626			var node_4 = sibling(span, 2);
35627
35628			{
35629				var consequent_1 = ($$anchor) => {
35630					var div_1 = root_3$1();
35631					var ul = child(div_1);
35632					var li = child(ul);
35633					var button = child(li);
35634
35635					reset(li);
35636
35637					var node_5 = sibling(li, 2);
35638
35639					each(node_5, 0, () => [0.25, 0.5, 1, 2, 4], index, ($$anchor, zoom) => {
35640						var li_1 = root_4$1();
35641						var button_1 = child(li_1);
35642						var text_1 = child(button_1);
35643
35644						reset(button_1);
35645						reset(li_1);
35646						template_effect(() => set_text(text_1, `${zoom * 100}%`));
35647						event('click', button_1, stopPropagation(() => handle_zoom_change(zoom)));
35648						append($$anchor, li_1);
35649					});
35650
35651					reset(ul);
35652					reset(div_1);
35653					event('click', button, stopPropagation(() => handle_zoom_change("fit")));
35654					append($$anchor, div_1);
35655				};
35656
35657				if_block(node_4, ($$render) => {
35658					if (get(show_zoom_popup)) $$render(consequent_1);
35659				});
35660			}
35661
35662			reset(div);
35663
35664			var node_6 = sibling(div, 4);
35665
35666			{
35667				let $0 = derived_safe_equal(() => !can_undo());
35668
35669				IconButton$1(node_6, {
35670					get Icon() {
35671						return Undo;
35672					},
35673					label: 'Undo',
35674					onclick: (event) => {
35675						dispatch("undo");
35676						event.stopPropagation();
35677					},
35678
35679					get disabled() {
35680						return get($0);
35681					}
35682				});
35683			}
35684
35685			var node_7 = sibling(node_6, 2);
35686
35687			{
35688				let $0 = derived_safe_equal(() => !can_redo());
35689
35690				IconButton$1(node_7, {
35691					get Icon() {
35692						return Redo;
35693					},
35694					label: 'Redo',
35695					onclick: (event) => {
35696						dispatch("redo");
35697						event.stopPropagation();
35698					},
35699
35700					get disabled() {
35701						return get($0);
35702					}
35703				});
35704			}
35705
35706			var node_8 = sibling(node_7, 2);
35707
35708			{
35709				var consequent_2 = ($$anchor) => {
35710					{
35711						let $0 = derived_safe_equal(() => !can_save());
35712
35713						IconButton$1($$anchor, {
35714							get disabled() {
35715								return get($0);
35716							},
35717
35718							get Icon() {
35719								return Check;
35720							},
35721							label: 'Save changes',
35722							onclick: (event) => {
35723								dispatch("save");
35724								event.stopPropagation();
35725							},
35726							color: 'var(--color-accent)'
35727						});
35728					}
35729				};
35730
35731				if_block(node_8, ($$render) => {
35732					if (changeable()) $$render(consequent_2);
35733				});
35734			}
35735
35736			var node_9 = sibling(node_8, 2);
35737
35738			IconButton$1(node_9, {
35739				get Icon() {
35740					return Trash;
35741				},
35742				label: 'Clear canvas',
35743				onclick: (event) => {
35744					dispatch("remove_image");
35745					event.stopPropagation();
35746				}
35747			});
35748
35749			template_effect(() => set_text(text, `${get(formatted_zoom) ?? ''}%`));
35750			event('click', span, handle_zoom_click);
35751			event('keydown', span, handle_zoom_keydown);
35752			append($$anchor, fragment_1);
35753		},
35754		$$slots: { default: true }
35755	});
35756
35757	return pop($$exports);
35758}
35759
35760var root_5$1 = from_html(`<div class="modal svelte-6fnl1z"><div class="modal-inner svelte-6fnl1z"><!></div></div>`);
35761var root_2$1 = from_html(`<!> <!> <!> <!>`, 1);
35762var root_7$1 = from_html(`<div class="crop-confirm-button svelte-6fnl1z"><!></div>`);
35763var root_1$1 = from_html(`<div data-testid="image" aria-dropeffect="copy"><!> <div></div> <div></div> <!> <!></div>`);
35764
35765function ImageEditor_1($$anchor, $$props) {
35766	push($$props, false);
35767
35768	const disable_click = mutable_source();
35769	const { drag, open_file_upload } = create_drag();
35770	const dispatch = createEventDispatcher();
35771	const antialias = true;
35772	let changeable = prop($$props, 'changeable', 12, false);
35773	let sources = prop($$props, 'sources', 28, () => ["upload", "webcam", "clipboard"]);
35774	let transforms = prop($$props, 'transforms', 28, () => ["crop", "resize"]);
35775	let canvas_size = prop($$props, 'canvas_size', 12);
35776	let is_dragging = prop($$props, 'is_dragging', 12, false);
35777	let background_image = prop($$props, 'background_image', 12, false);
35778	let brush_options = prop($$props, 'brush_options', 12);
35779	let eraser_options = prop($$props, 'eraser_options', 12);
35780	let fixed_canvas = prop($$props, 'fixed_canvas', 12, false);
35781	let root = prop($$props, 'root', 12);
35782	let i18n = prop($$props, 'i18n', 12);
35783	let upload = prop($$props, 'upload', 12);
35784	let composite = prop($$props, 'composite', 12);
35785	let layers = prop($$props, 'layers', 12);
35786	let background = prop($$props, 'background', 12);
35787	let border_region = prop($$props, 'border_region', 12, 0);
35788	let layer_options = prop($$props, 'layer_options', 12);
35789	let current_tool = prop($$props, 'current_tool', 12);
35790	let webcam_options = prop($$props, 'webcam_options', 12);
35791	let show_download_button = prop($$props, 'show_download_button', 12, false);
35792	let theme_mode = prop($$props, 'theme_mode', 12);
35793	let full_history = prop($$props, 'full_history', 12, null);
35794	let pixi_target = mutable_source();
35795	let pixi_target_crop = mutable_source();
35796
35797	function refresh_tools() {
35798		if (!get(editor) || !get(ready)) return;
35799
35800		get(editor).set_tool(current_tool());
35801		get(editor).set_subtool(get(current_subtool));
35802	}
35803
35804	/**
35805	 * Refreshes tools when layer state changes
35806	 */
35807	function refresh_tools_for_layer_changes(current_layers) {
35808		if (!get(editor) || !get(ready)) return;
35809
35810		if (current_layers.layers.length > 0 && !current_layers.active_layer) {
35811			get(editor).set_layer(current_layers.layers[0].id);
35812		}
35813
35814		// reapply current tool to ensure it targets the correct layer
35815		if (current_tool()) {
35816			get(editor).set_tool(current_tool());
35817
35818			if (get(current_subtool)) {
35819				get(editor).set_subtool(get(current_subtool));
35820			}
35821		}
35822
35823		if (get(brush) && (current_tool() === "draw" || current_tool() === "erase")) {
35824			get(brush).set_tool(current_tool(), get(current_subtool));
35825		}
35826	}
35827
35828	function check_if_should_init() {
35829		return layer_options() && get(editor) && get(ready);
35830	}
35831
35832	let has_drawn = prop($$props, 'has_drawn', 12, false);
35833
35834	/**
35835	 * Gets the image blobs from the editor
35836	 * @returns {Promise<ImageBlobs>} Object containing background, layers, and composite image blobs
35837	 */
35838	async function get_blobs() {
35839		if (!get(editor)) return { background: null, layers: [], composite: null };
35840		if (!background_image() && !has_drawn() && !layers().length) return { background: null, layers: [], composite: null };
35841
35842		const blobs = await get(editor).get_blobs();
35843
35844		return blobs;
35845	}
35846
35847	let editor = mutable_source();
35848
35849	/**
35850	 * Adds an image to the editor
35851	 * @param {Blob | File} image - The image to add
35852	 */
35853	function add_image(image) {
35854		get(editor).add_image({ image });
35855	}
35856
35857	let pending_bg;
35858
35859	/**
35860	 * Adds an image to the editor from a URL
35861	 * @param {string | FileData} source - The URL of the image or a FileData object
35862	 * @returns {Promise<void>}
35863	 */
35864	async function add_image_from_url(source) {
35865		if (!get(editor) || !source || !check_if_should_init()) return;
35866
35867		let url;
35868
35869		if (typeof source === "string") {
35870			url = source;
35871		} else if (source?.meta?._type === "gradio.FileData" && source?.url) {
35872			url = source.url;
35873		} else {
35874			console.warn("Invalid source provided to add_image_from_url:", source);
35875
35876			return;
35877		}
35878
35879		try {
35880			pending_bg = get(editor).add_image_from_url(url);
35881
35882			let pending_crop = crop.add_image_from_url(url);
35883
35884			await Promise.all([pending_bg, pending_crop]);
35885			crop.set_tool("image");
35886			crop.set_subtool("crop");
35887			background_image(true);
35888			get(zoom).set_zoom("fit");
35889			dispatch("upload");
35890			dispatch("input");
35891		} catch(error) {
35892			console.error("Error adding image from URL:", error);
35893		}
35894	}
35895
35896	/**
35897	 * Adds a new layer with an image loaded from a URL
35898	 * @param {string | FileData} source - The URL of the image or a FileData object
35899	 * @returns {Promise<string | null>} - The ID of the created layer, or null if failed
35900	 */
35901	async function add_layers_from_url(source) {
35902		if (!get(editor) || !source.length || !check_if_should_init()) return;
35903
35904		if (Array.isArray(source) && source.every((item) => item?.meta?._type === "gradio.FileData")) {
35905			try {
35906				await pending_bg;
35907				await get(editor).add_layers_from_url(source.map((item) => item.url));
35908				dispatch("change");
35909				dispatch("input");
35910			} catch(error) {
35911				console.error("Error adding layer from URL:", error);
35912			}
35913		}
35914	}
35915
35916	let brush = mutable_source();
35917	let zoom = mutable_source();
35918	let zoom_level = mutable_source(1);
35919	let ready = mutable_source(false);
35920	let min_zoom = mutable_source(true);
35921	let last_dimensions = { width: 0, height: 0 };
35922
35923	/**
35924	 * Handles visibility changes and resets zoom if dimensions have changed
35925	 */
35926	async function handle_visibility_change() {
35927		if (!get(editor) || !get(ready) || !get(zoom)) return;
35928
35929		await tick();
35930
35931		const is_visible = get(pixi_target).offsetParent !== null;
35932
35933		if (is_visible) {
35934			const current_dimensions = get(pixi_target).getBoundingClientRect();
35935
35936			if (current_dimensions.width !== last_dimensions.width || current_dimensions.height !== last_dimensions.height) {
35937				get(zoom).set_zoom("fit");
35938
35939				last_dimensions = {
35940					width: current_dimensions.width,
35941					height: current_dimensions.height
35942				};
35943			}
35944		}
35945	}
35946
35947	onMount(() => {
35948		let intersection_observer;
35949		let resize_observer;
35950
35951		init_image_editor().then(() => {
35952
35953			intersection_observer = new IntersectionObserver(() => {
35954				handle_visibility_change();
35955			});
35956
35957			resize_observer = new ResizeObserver(() => {
35958				handle_visibility_change();
35959			});
35960
35961			intersection_observer.observe(get(pixi_target));
35962			resize_observer.observe(get(pixi_target));
35963
35964			setTimeout(
35965				() => {
35966					if (full_history() && get(editor)) {
35967						get(editor).command_manager.replay(full_history(), get(editor).context).then(() => {
35968							refresh_tools_after_history();
35969						});
35970					}
35971				},
35972				0
35973			);
35974		});
35975
35976		if (typeof window !== "undefined") {
35977			window.editor = get(editor);
35978		}
35979
35980		return () => {
35981			if (intersection_observer) {
35982				intersection_observer.disconnect();
35983			}
35984
35985			if (resize_observer) {
35986				resize_observer.disconnect();
35987			}
35988
35989			if (get(editor)) {
35990				get(editor).destroy();
35991			}
35992		};
35993	});
35994
35995	/**
35996	 * Refreshes tool state after history replay to ensure proper layer targeting
35997	 */
35998	function refresh_tools_after_history() {
35999		if (!get(editor) || !get(ready)) return;
36000
36001		const current_layers = get$1(get(editor).layers);
36002
36003		if (current_layers.layers.length > 0 && !current_layers.active_layer) {
36004			get(editor).set_layer(current_layers.layers[0].id);
36005		}
36006
36007		if (current_tool()) {
36008			get(editor).set_tool(current_tool());
36009
36010			if (get(current_subtool)) {
36011				get(editor).set_subtool(get(current_subtool));
36012			}
36013		}
36014
36015		if (get(brush) && (current_tool() === "draw" || current_tool() === "erase")) {
36016			get(brush).set_tool(current_tool(), get(current_subtool));
36017		}
36018
36019		full_history(get(editor).command_manager.history);
36020	}
36021
36022	let crop;
36023	let crop_zoom = mutable_source();
36024	let can_undo = prop($$props, 'can_undo', 12, false);
36025	let can_redo = mutable_source(false);
36026
36027	async function init_image_editor() {
36028		set(brush, new BrushTool());
36029		set(zoom, new ZoomTool());
36030
36031		set(editor, new ImageEditor({
36032			target_element: get(pixi_target),
36033			width: canvas_size()[0],
36034			height: canvas_size()[1],
36035			tools: ["image", get(zoom), new ResizeTool(), get(brush)],
36036			fixed_canvas: fixed_canvas(),
36037			border_region: border_region(),
36038			layer_options: layer_options(),
36039			theme_mode: theme_mode()
36040		}));
36041
36042		get(brush).on("change", () => {
36043			has_drawn(true);
36044		});
36045
36046		set(crop_zoom, new ZoomTool());
36047
36048		crop = new ImageEditor({
36049			target_element: get(pixi_target_crop),
36050			width: canvas_size()[0],
36051			height: canvas_size()[1],
36052			tools: ["image", get(crop_zoom), new CropTool()],
36053			dark: true,
36054			fixed_canvas: false,
36055			border_region: 0,
36056			pad_bottom: 40
36057		});
36058
36059		get(editor).scale.subscribe((_scale) => {
36060			set(zoom_level, _scale);
36061		});
36062
36063		get(editor).min_zoom.subscribe((is_min_zoom) => {
36064			set(min_zoom, is_min_zoom);
36065		});
36066
36067		get(editor).dimensions.subscribe((dimensions) => {
36068			last_dimensions = { ...dimensions };
36069		});
36070
36071		get(editor).command_manager.current_history.subscribe((history) => {
36072			can_undo(history.previous !== null);
36073			set(can_redo, history.next !== null);
36074		});
36075
36076		await Promise.all([get(editor).ready, crop.ready]).then(() => {
36077			handle_tool_change({ tool: "image" });
36078			set(ready, true);
36079
36080			if (sources().length > 0) {
36081				handle_tool_change({ tool: "image" });
36082			} else {
36083				handle_tool_change({ tool: "draw" });
36084			}
36085
36086			crop.set_subtool("crop");
36087		});
36088
36089		get(editor).on("change", () => {
36090			dispatch("change");
36091			dispatch("input");
36092			full_history(get(editor).command_manager.history);
36093		});
36094
36095		if (background() || layers().length > 0) {
36096			if (background()) {
36097				await add_image_from_url(background());
36098			}
36099
36100			if (layers().length > 0) {
36101				await add_layers_from_url(layers());
36102			}
36103
36104			handle_tool_change({ tool: "draw" });
36105		} else if (composite()) {
36106			await add_image_from_url(composite());
36107			handle_tool_change({ tool: "draw" });
36108		}
36109
36110		refresh_tools();
36111	}
36112
36113	/**
36114	 * Handles file uploads
36115	 * @param {File[]} files - The uploaded files
36116	 */
36117	async function handle_files(files) {
36118		if (files == null) return;
36119		if (!sources().includes("upload")) return;
36120
36121		get(editor).reset_canvas();
36122
36123		const _file = Array.isArray(files) ? files[0] : files;
36124
36125		await get(editor).add_image({ image: _file });
36126		await crop.add_image({ image: _file });
36127		crop.reset();
36128		background_image(true);
36129		get(zoom).set_zoom("fit");
36130		handle_tool_change({ tool: "draw" });
36131		dispatch("upload");
36132	}
36133
36134	/**
36135	 * Handles tool change events
36136	 * @param {{ tool: ToolbarTool }} param0 - Object containing the selected tool
36137	 */
36138	function handle_tool_change({ tool }) {
36139		get(editor).set_tool(tool);
36140		current_tool(tool);
36141
36142		if (tool === "image") {
36143			crop.set_tool("image");
36144			crop.set_subtool("crop");
36145		}
36146	}
36147
36148	/**
36149	 * Handles subtool change events
36150	 * @param {{ tool: ToolbarTool, subtool: Subtool }} param0 - Object containing the selected tool and subtool
36151	 */
36152	function handle_subtool_change({ tool, subtool }) {
36153		get(editor).set_subtool(subtool);
36154		set(current_subtool, subtool);
36155
36156		if (subtool === null) {
36157			return;
36158		}
36159
36160		if (tool === "draw") {
36161			if (subtool === "size") {
36162				set(brush_size_visible, true);
36163			} else if (subtool === "color") {
36164				set(brush_color_visible, true);
36165			}
36166		}
36167
36168		if (tool === "erase" && subtool === "size") {
36169			set(eraser_size_visible, true);
36170		}
36171
36172		if (tool === "image" && subtool === "paste") {
36173			process_clipboard();
36174		}
36175
36176		if (tool === "image" && subtool === "upload") {
36177			tick().then(() => {
36178				set(disable_click, false);
36179				open_file_upload();
36180			});
36181		}
36182	}
36183
36184	let eraser_size_visible = mutable_source(false);
36185	let selected_color = mutable_source();
36186	let selected_size = mutable_source();
36187	let selected_opacity = mutable_source(1);
36188	let selected_eraser_size = mutable_source();
36189
36190	function update_brush_options() {
36191		const default_color = brush_options().default_color === "auto"
36192			? brush_options().colors[0]
36193			: brush_options().default_color;
36194
36195		// color is already a tuple [color, opacity]
36196		if (Array.isArray(default_color)) {
36197			set(selected_color, default_color[0]);
36198			set(selected_opacity, default_color[1]);
36199		} else {
36200			set(selected_color, default_color);
36201
36202			// color is a string, check if it has opacity info
36203			const color = tinycolor(default_color);
36204
36205			if (color.getAlpha() < 1) {
36206				set(selected_opacity, color.getAlpha());
36207			} else {
36208				set(selected_opacity, 1);
36209			}
36210		}
36211
36212		set(selected_size, typeof brush_options().default_size === "number" ? brush_options().default_size : 25);
36213	}
36214
36215	function update_eraser_options() {
36216		set(selected_eraser_size, eraser_options().default_size === "auto" ? 25 : eraser_options().default_size);
36217	}
36218
36219	let brush_size_visible = mutable_source(false);
36220	let brush_color_visible = mutable_source(false);
36221	let current_subtool = mutable_source(null);
36222	let preview = mutable_source(false);
36223
36224	function handle_zoom_change(zoom_level) {
36225		get(zoom).set_zoom(zoom_level);
36226	}
36227
36228	function zoom_in_out(direction) {
36229		get(zoom).set_zoom(direction === "in"
36230			? get(zoom_level) + (get(zoom_level) < 1 ? 0.1 : get(zoom_level) * 0.1)
36231			: get(zoom_level) - (get(zoom_level) < 1 ? 0.1 : get(zoom_level) * 0.1));
36232	}
36233
36234	async function process_clipboard() {
36235		const items = await navigator.clipboard.read();
36236
36237		for (let i = 0; i < items.length; i++) {
36238			const type = items[i].types.find((t) => t.startsWith("image/"));
36239
36240			if (type) {
36241				const blob = await items[i].getType(type);
36242
36243				handle_files(blob);
36244			}
36245		}
36246	}
36247
36248	function handle_capture(e) {
36249		if (e.detail !== null) {
36250			handle_files(e.detail);
36251		}
36252
36253		handle_subtool_change({ tool: current_tool(), subtool: null });
36254	}
36255
36256	function handle_save() {
36257		dispatch("save");
36258	}
36259
36260	async function handle_crop_confirm() {
36261		const { image } = await crop.get_crop_bounds();
36262
36263		if (!image) return;
36264
36265		await get(editor).add_image({ image, resize: false });
36266		handle_subtool_change({ tool: "image", subtool: null });
36267		dispatch("change");
36268		dispatch("input");
36269	}
36270
36271	async function handle_download() {
36272		const blobs = await get(editor).get_blobs();
36273		const blob = blobs.composite;
36274
36275		if (!blob) {
36276			dispatch("download_error", "Unable to generate image to download.");
36277
36278			return;
36279		}
36280
36281		const url = URL.createObjectURL(blob);
36282		const link = document.createElement("a");
36283
36284		link.href = url;
36285		link.download = "image.png";
36286		link.click();
36287		URL.revokeObjectURL(url);
36288	}
36289
36290	function handle_undo() {
36291		get(editor).undo();
36292	}
36293
36294	function handle_redo() {
36295		get(editor).redo();
36296	}
36297
36298	let old_background = mutable_source(background());
36299
36300	legacy_pre_effect(() => (deep_read_state(layer_options()), get(editor)), () => {
36301		if (layer_options()) {
36302			if (check_if_should_init()) {
36303				get(editor).set_layer_options(layer_options());
36304				refresh_tools();
36305			}
36306		}
36307	});
36308
36309	legacy_pre_effect(() => (get(editor), get(ready), get$1), () => {
36310		if (get(editor) && get(ready) && get(editor).layers) {
36311			const current_layers = get$1(get(editor).layers);
36312
36313			if (current_layers.layers.length > 0 && !current_layers.active_layer) {
36314				refresh_tools_for_layer_changes(current_layers);
36315			}
36316		}
36317	});
36318
36319	legacy_pre_effect(
36320		() => (
36321			deep_read_state(background()),
36322			deep_read_state(layers()),
36323			deep_read_state(composite()),
36324			get(editor),
36325			get(ready),
36326			get(zoom)
36327		),
36328		() => {
36329			if (background() == null && layers().length == 0 && composite() == null && get(editor) && get(ready)) {
36330				get(editor).reset_canvas();
36331				get(zoom).set_zoom("fit");
36332				handle_tool_change({ tool: "image" });
36333				background_image(false);
36334				has_drawn(false);
36335			}
36336		}
36337	);
36338
36339	legacy_pre_effect(() => (get(old_background), deep_read_state(background())), () => {
36340		if (get(old_background) !== background()) {
36341			handle_tool_change({ tool: "draw" });
36342			handle_subtool_change({ tool: "draw", subtool: null });
36343			current_tool("draw");
36344			set(old_background, background());
36345		}
36346	});
36347
36348	legacy_pre_effect(
36349		() => (
36350			deep_read_state(current_tool()),
36351			get(current_subtool),
36352			get(crop_zoom)
36353		),
36354		() => {
36355			current_tool() === "image" && get(current_subtool) === "crop" && get(crop_zoom).set_zoom("fit");
36356		}
36357	);
36358
36359	legacy_pre_effect(() => (deep_read_state(can_undo()), get(editor)), () => {
36360		background_image(can_undo() && get(editor).command_manager.contains("AddImage"));
36361	});
36362
36363	legacy_pre_effect(
36364		() => (
36365			deep_read_state(brush_options()),
36366			deep_read_state(eraser_options())
36367		),
36368		() => {
36369			if (brush_options()) {
36370				update_brush_options();
36371			}
36372
36373			if (eraser_options()) {
36374				update_eraser_options();
36375			}
36376		}
36377	);
36378
36379	legacy_pre_effect(
36380		() => (
36381			get(brush),
36382			get(selected_color),
36383			deep_read_state(brush_options())
36384		),
36385		() => {
36386			get(brush)?.set_brush_color((() => {
36387				let color_value;
36388
36389				if (get(selected_color) === "auto") {
36390					const default_color = brush_options().colors.find((color) => Array.isArray(color)
36391						? color[0] === brush_options().default_color
36392						: color === brush_options().default_color) || brush_options().colors[0];
36393
36394					color_value = Array.isArray(default_color) ? default_color[0] : default_color;
36395				} else {
36396					color_value = get(selected_color);
36397				}
36398
36399				return color_value;
36400			})());
36401		}
36402	);
36403
36404	legacy_pre_effect(() => (get(brush), get(selected_size)), () => {
36405		get(brush)?.set_brush_size(typeof get(selected_size) === "number" ? get(selected_size) : 25);
36406	});
36407
36408	legacy_pre_effect(() => (get(brush), get(selected_eraser_size)), () => {
36409		get(brush)?.set_eraser_size(typeof get(selected_eraser_size) === "number" ? get(selected_eraser_size) : 25);
36410	});
36411
36412	legacy_pre_effect(
36413		() => (
36414			deep_read_state(current_tool()),
36415			deep_read_state(background_image()),
36416			get(current_subtool),
36417			deep_read_state(sources())
36418		),
36419		() => {
36420			set(disable_click, current_tool() !== "image" || current_tool() === "image" && background_image() || current_tool() === "image" && get(current_subtool) === "webcam" || !sources().includes("upload"));
36421		}
36422	);
36423
36424	legacy_pre_effect(() => (get(brush), get(preview)), () => {
36425		get(brush)?.preview_brush(get(preview));
36426	});
36427
36428	legacy_pre_effect(() => (get(brush), get(selected_opacity)), () => {
36429		get(brush)?.set_brush_opacity(get(selected_opacity));
36430	});
36431
36432	legacy_pre_effect(
36433		() => (
36434			deep_read_state(composite()),
36435			deep_read_state(background())
36436		),
36437		() => {
36438			add_image_from_url(composite() || background());
36439		}
36440	);
36441
36442	legacy_pre_effect(() => (deep_read_state(layers())), () => {
36443		add_layers_from_url(layers());
36444	});
36445
36446	legacy_pre_effect_reset();
36447
36448	var $$exports = {
36449		antialias,
36450		get_blobs,
36451		add_image,
36452		add_image_from_url,
36453		add_layers_from_url,
36454		get changeable() {
36455			return changeable();
36456		},
36457
36458		set changeable($$value) {
36459			changeable($$value);
36460			flushSync();
36461		},
36462
36463		get sources() {
36464			return sources();
36465		},
36466
36467		set sources($$value) {
36468			sources($$value);
36469			flushSync();
36470		},
36471
36472		get transforms() {
36473			return transforms();
36474		},
36475
36476		set transforms($$value) {
36477			transforms($$value);
36478			flushSync();
36479		},
36480
36481		get canvas_size() {
36482			return canvas_size();
36483		},
36484
36485		set canvas_size($$value) {
36486			canvas_size($$value);
36487			flushSync();
36488		},
36489
36490		get is_dragging() {
36491			return is_dragging();
36492		},
36493
36494		set is_dragging($$value) {
36495			is_dragging($$value);
36496			flushSync();
36497		},
36498
36499		get background_image() {
36500			return background_image();
36501		},
36502
36503		set background_image($$value) {
36504			background_image($$value);
36505			flushSync();
36506		},
36507
36508		get brush_options() {
36509			return brush_options();
36510		},
36511
36512		set brush_options($$value) {
36513			brush_options($$value);
36514			flushSync();
36515		},
36516
36517		get eraser_options() {
36518			return eraser_options();
36519		},
36520
36521		set eraser_options($$value) {
36522			eraser_options($$value);
36523			flushSync();
36524		},
36525
36526		get fixed_canvas() {
36527			return fixed_canvas();
36528		},
36529
36530		set fixed_canvas($$value) {
36531			fixed_canvas($$value);
36532			flushSync();
36533		},
36534
36535		get root() {
36536			return root();
36537		},
36538
36539		set root($$value) {
36540			root($$value);
36541			flushSync();
36542		},
36543
36544		get i18n() {
36545			return i18n();
36546		},
36547
36548		set i18n($$value) {
36549			i18n($$value);
36550			flushSync();
36551		},
36552
36553		get upload() {
36554			return upload();
36555		},
36556
36557		set upload($$value) {
36558			upload($$value);
36559			flushSync();
36560		},
36561
36562		get composite() {
36563			return composite();
36564		},
36565
36566		set composite($$value) {
36567			composite($$value);
36568			flushSync();
36569		},
36570
36571		get layers() {
36572			return layers();
36573		},
36574
36575		set layers($$value) {
36576			layers($$value);
36577			flushSync();
36578		},
36579
36580		get background() {
36581			return background();
36582		},
36583
36584		set background($$value) {
36585			background($$value);
36586			flushSync();
36587		},
36588
36589		get border_region() {
36590			return border_region();
36591		},
36592
36593		set border_region($$value) {
36594			border_region($$value);
36595			flushSync();
36596		},
36597
36598		get layer_options() {
36599			return layer_options();
36600		},
36601
36602		set layer_options($$value) {
36603			layer_options($$value);
36604			flushSync();
36605		},
36606
36607		get current_tool() {
36608			return current_tool();
36609		},
36610
36611		set current_tool($$value) {
36612			current_tool($$value);
36613			flushSync();
36614		},
36615
36616		get webcam_options() {
36617			return webcam_options();
36618		},
36619
36620		set webcam_options($$value) {
36621			webcam_options($$value);
36622			flushSync();
36623		},
36624
36625		get show_download_button() {
36626			return show_download_button();
36627		},
36628
36629		set show_download_button($$value) {
36630			show_download_button($$value);
36631			flushSync();
36632		},
36633
36634		get theme_mode() {
36635			return theme_mode();
36636		},
36637
36638		set theme_mode($$value) {
36639			theme_mode($$value);
36640			flushSync();
36641		},
36642
36643		get full_history() {
36644			return full_history();
36645		},
36646
36647		set full_history($$value) {
36648			full_history($$value);
36649			flushSync();
36650		},
36651
36652		get has_drawn() {
36653			return has_drawn();
36654		},
36655
36656		set has_drawn($$value) {
36657			has_drawn($$value);
36658			flushSync();
36659		},
36660
36661		get can_undo() {
36662			return can_undo();
36663		},
36664
36665		set can_undo($$value) {
36666			can_undo($$value);
36667			flushSync();
36668		}
36669	};
36670
36671	init$1();
36672
36673	var div = root_1$1();
36674	let classes;
36675
36676	set_attribute(div, 'aria-label', "Click to upload or drop files");
36677
36678	var node = child(div);
36679
36680	{
36681		var consequent_4 = ($$anchor) => {
36682			var fragment = root_2$1();
36683			var node_1 = first_child(fragment);
36684
36685			{
36686				var consequent = ($$anchor) => {
36687					Controls($$anchor, {
36688						get changeable() {
36689							return changeable();
36690						},
36691
36692						get min_zoom() {
36693							return get(min_zoom);
36694						},
36695
36696						get current_zoom() {
36697							return get(zoom_level);
36698						},
36699
36700						get tool() {
36701							return current_tool();
36702						},
36703						can_save: true,
36704						get enable_download() {
36705							return show_download_button();
36706						},
36707
36708						get can_undo() {
36709							return can_undo();
36710						},
36711
36712						get can_redo() {
36713							return get(can_redo);
36714						},
36715
36716						$$events: {
36717							set_zoom: (e) => handle_zoom_change(e.detail),
36718							zoom_in: () => zoom_in_out("in"),
36719							zoom_out: () => zoom_in_out("out"),
36720							remove_image: () => {
36721								dispatch("clear");
36722								get(editor).reset_canvas();
36723								handle_tool_change({ tool: "image" });
36724								background_image(false);
36725								has_drawn(false);
36726							},
36727							save: handle_save,
36728							pan: (e) => {
36729								handle_tool_change({ tool: "pan" });
36730							},
36731							download: () => handle_download(),
36732							undo: handle_undo,
36733							redo: handle_redo
36734						}
36735					});
36736				};
36737
36738				if_block(node_1, ($$render) => {
36739					if (get(current_subtool) !== "crop") $$render(consequent);
36740				});
36741			}
36742
36743			var node_2 = sibling(node_1, 2);
36744
36745			{
36746				var consequent_1 = ($$anchor) => {
36747					Toolbar($$anchor, {
36748						get sources() {
36749							return sources();
36750						},
36751
36752						get transforms() {
36753							return transforms();
36754						},
36755
36756						get background() {
36757							return background_image();
36758						},
36759
36760						get show_brush_size() {
36761							return get(brush_size_visible);
36762						},
36763
36764						get show_brush_color() {
36765							return get(brush_color_visible);
36766						},
36767
36768						get show_eraser_size() {
36769							return get(eraser_size_visible);
36770						},
36771
36772						get brush_options() {
36773							return brush_options();
36774						},
36775
36776						get eraser_options() {
36777							return eraser_options();
36778						},
36779
36780						get tool() {
36781							return current_tool();
36782						},
36783
36784						get subtool() {
36785							return get(current_subtool);
36786						},
36787
36788						get selected_color() {
36789							return get(selected_color);
36790						},
36791
36792						set selected_color($$value) {
36793							set(selected_color, $$value);
36794						},
36795
36796						get selected_size() {
36797							return get(selected_size);
36798						},
36799
36800						set selected_size($$value) {
36801							set(selected_size, $$value);
36802						},
36803
36804						get selected_eraser_size() {
36805							return get(selected_eraser_size);
36806						},
36807
36808						set selected_eraser_size($$value) {
36809							set(selected_eraser_size, $$value);
36810						},
36811
36812						get selected_opacity() {
36813							return get(selected_opacity);
36814						},
36815
36816						set selected_opacity($$value) {
36817							set(selected_opacity, $$value);
36818						},
36819
36820						get preview() {
36821							return get(preview);
36822						},
36823
36824						set preview($$value) {
36825							set(preview, $$value);
36826						},
36827
36828						$$events: {
36829							tool_change: (e) => handle_tool_change(e.detail),
36830							subtool_change: (e) => handle_subtool_change(e.detail)
36831						},
36832						$$legacy: true
36833					});
36834				};
36835
36836				if_block(node_2, ($$render) => {
36837					if (get(current_subtool) !== "crop") $$render(consequent_1);
36838				});
36839			}
36840
36841			var node_3 = sibling(node_2, 2);
36842
36843			{
36844				var consequent_2 = ($$anchor) => {
36845					var div_1 = root_5$1();
36846					var div_2 = child(div_1);
36847					var node_4 = child(div_2);
36848
36849					Webcam$1(node_4, {
36850						get upload() {
36851							return upload();
36852						},
36853
36854						get root() {
36855							return root();
36856						},
36857						streaming: false,
36858						mode: 'image',
36859						include_audio: false,
36860						get i18n() {
36861							return i18n();
36862						},
36863
36864						get mirror_webcam() {
36865							return (
36866								deep_read_state(webcam_options()),
36867								untrack(() => webcam_options().mirror)
36868							);
36869						},
36870
36871						get webcam_constraints() {
36872							return (
36873								deep_read_state(webcam_options()),
36874								untrack(() => webcam_options().constraints)
36875							);
36876						},
36877
36878						$$events: {
36879							capture: handle_capture,
36880							error($$arg) {
36881								bubble_event.call(this, $$props, $$arg);
36882							},
36883
36884							drag($$arg) {
36885								bubble_event.call(this, $$props, $$arg);
36886							}
36887						}
36888					});
36889
36890					reset(div_2);
36891					reset(div_1);
36892					append($$anchor, div_1);
36893				};
36894
36895				if_block(node_3, ($$render) => {
36896					if (current_tool() === "image" && get(current_subtool) === "webcam") $$render(consequent_2);
36897				});
36898			}
36899
36900			var node_5 = sibling(node_3, 2);
36901
36902			{
36903				var consequent_3 = ($$anchor) => {
36904					SecondaryToolbar($$anchor, {
36905						get enable_additional_layers() {
36906							return (
36907								deep_read_state(layer_options()),
36908								untrack(() => layer_options().allow_additional_layers)
36909							);
36910						},
36911
36912						get layers() {
36913							return (get(editor), untrack(() => get(editor).layers));
36914						},
36915
36916						$$events: {
36917							new_layer: () => {
36918								get(editor).add_layer();
36919							},
36920
36921							change_layer: (e) => {
36922								get(editor).set_layer(e.detail);
36923
36924								if (current_tool() === "draw") {
36925									handle_tool_change({ tool: "draw" });
36926								}
36927							},
36928
36929							move_layer: (e) => {
36930								get(editor).move_layer(e.detail.id, e.detail.direction);
36931							},
36932
36933							delete_layer: (e) => {
36934								get(editor).delete_layer(e.detail);
36935							},
36936
36937							toggle_layer_visibility: (e) => {
36938								get(editor).toggle_layer_visibility(e.detail);
36939							}
36940						}
36941					});
36942				};
36943
36944				if_block(node_5, ($$render) => {
36945					if ((
36946						get(current_subtool),
36947						deep_read_state(layer_options()),
36948						untrack(() => get(current_subtool) !== "crop" && !layer_options().disabled)
36949					)) $$render(consequent_3);
36950				});
36951			}
36952
36953			append($$anchor, fragment);
36954		};
36955
36956		if_block(node, ($$render) => {
36957			if (get(ready)) $$render(consequent_4);
36958		});
36959	}
36960
36961	var div_3 = sibling(node, 2);
36962	let classes_1;
36963
36964	bind_this(div_3, ($$value) => set(pixi_target, $$value), () => get(pixi_target));
36965
36966	var div_4 = sibling(div_3, 2);
36967	let classes_2;
36968
36969	bind_this(div_4, ($$value) => set(pixi_target_crop, $$value), () => get(pixi_target_crop));
36970
36971	var node_6 = sibling(div_4, 2);
36972
36973	{
36974		var consequent_5 = ($$anchor) => {
36975			var div_5 = root_7$1();
36976			var node_7 = child(div_5);
36977
36978			IconButton(node_7, {
36979				get Icon() {
36980					return Check;
36981				},
36982				label: 'Confirm crop',
36983				show_label: true,
36984				size: 'large',
36985				padded: true,
36986				color: 'white',
36987				background: 'var(--color-green-500)',
36988				label_position: 'right',
36989				onclick: handle_crop_confirm
36990			});
36991
36992			reset(div_5);
36993			append($$anchor, div_5);
36994		};
36995
36996		if_block(node_6, ($$render) => {
36997			if (get(current_subtool) === "crop") $$render(consequent_5);
36998		});
36999	}
37000
37001	var node_8 = sibling(node_6, 2);
37002
37003	slot(node_8, $$props, 'default', {}, null);
37004	reset(div);
37005
37006	action(div, ($$node, $$action_arg) => drag?.($$node, $$action_arg), () => ({
37007		on_drag_change: (dragging) => is_dragging(dragging),
37008		on_files: handle_files,
37009		accepted_types: "image/*",
37010		disable_click: get(disable_click)
37011	}));
37012
37013	template_effect(() => {
37014		classes = set_class(div, 1, 'image-container svelte-6fnl1z', null, classes, { 'dark-bg': get(current_subtool) === "crop" });
37015		classes_1 = set_class(div_3, 1, 'pixi-target svelte-6fnl1z', null, classes_1, { visible: get(current_subtool) !== "crop" });
37016		classes_2 = set_class(div_4, 1, 'pixi-target-crop svelte-6fnl1z', null, classes_2, { visible: get(current_subtool) === "crop" });
37017	});
37018
37019	append($$anchor, div);
37020	bind_prop($$props, 'antialias', antialias);
37021	bind_prop($$props, 'get_blobs', get_blobs);
37022	bind_prop($$props, 'add_image', add_image);
37023	bind_prop($$props, 'add_image_from_url', add_image_from_url);
37024	bind_prop($$props, 'add_layers_from_url', add_layers_from_url);
37025
37026	return pop($$exports);
37027}
37028
37029const RE_HEADING = /^(#\s*)(.+)$/m;
37030function inject(text) {
37031  const trimmed_text = text.trim();
37032  const heading_match = trimmed_text.match(RE_HEADING);
37033  if (!heading_match) {
37034    return [false, trimmed_text || false];
37035  }
37036  const [full_match, , heading_content] = heading_match;
37037  const _heading = heading_content.trim();
37038  if (trimmed_text === full_match) {
37039    return [_heading, false];
37040  }
37041  const heading_end_index = heading_match.index !== void 0 ? heading_match.index + full_match.length : 0;
37042  const remaining_text = trimmed_text.substring(heading_end_index).trim();
37043  const _paragraph = remaining_text || false;
37044  return [_heading, _paragraph];
37045}
37046
37047var root_6 = from_html(`<h2 class="svelte-b4yllx"> </h2>`);
37048var root_7 = from_html(`<p class="svelte-b4yllx"> </p>`);
37049var root_5 = from_html(`<!> <!>`, 1);
37050var root_8 = from_html(`<div>Upload an image</div>`);
37051var root_9 = from_html(`<div class="or svelte-b4yllx">or</div>`);
37052var root_10 = from_html(`<div>select the draw tool to start</div>`);
37053var root_3 = from_html(`<div class="empty wrap svelte-b4yllx"><!> <!> <!></div>`);
37054var root_1 = from_html(`<!> <!>`, 1);
37055
37056function InteractiveImageEditor($$anchor, $$props) {
37057	push($$props, false);
37058
37059	const heading = mutable_source();
37060	const paragraph = mutable_source();
37061	let brush = prop($$props, 'brush', 12);
37062	let eraser = prop($$props, 'eraser', 12);
37063	let sources = prop($$props, 'sources', 12);
37064	let i18n = prop($$props, 'i18n', 12);
37065	let root = prop($$props, 'root', 12);
37066	let label = prop($$props, 'label', 12, undefined);
37067	let show_label = prop($$props, 'show_label', 12);
37068	let changeable = prop($$props, 'changeable', 12, false);
37069	let theme_mode = prop($$props, 'theme_mode', 12);
37070	let layers = prop($$props, 'layers', 12);
37071	let composite = prop($$props, 'composite', 12);
37072	let background = prop($$props, 'background', 12);
37073	let layer_options = prop($$props, 'layer_options', 12);
37074	let transforms = prop($$props, 'transforms', 12);
37075	let accept_blobs = prop($$props, 'accept_blobs', 12);
37076	let canvas_size = prop($$props, 'canvas_size', 12);
37077	let fixed_canvas = prop($$props, 'fixed_canvas', 12, false);
37078	let realtime = prop($$props, 'realtime', 12);
37079	let upload = prop($$props, 'upload', 12);
37080	let is_dragging = prop($$props, 'is_dragging', 12);
37081	let placeholder = prop($$props, 'placeholder', 12, undefined);
37082	let border_region = prop($$props, 'border_region', 12);
37083	let full_history = prop($$props, 'full_history', 12, null);
37084	let webcam_options = prop($$props, 'webcam_options', 12);
37085	let show_download_button = prop($$props, 'show_download_button', 12, false);
37086	const dispatch = createEventDispatcher();
37087	let editor = mutable_source();
37088	let has_drawn = mutable_source(false);
37089
37090	function is_not_null(o) {
37091		return !!o;
37092	}
37093
37094	function is_file_data(o) {
37095		return !!o;
37096	}
37097
37098	async function get_data() {
37099		let blobs;
37100
37101		try {
37102			blobs = await get(editor).get_blobs();
37103		} catch(e) {
37104			return { background: null, layers: [], composite: null };
37105		}
37106
37107		const bg = blobs.background
37108			? upload()(await prepare_files([new File([blobs.background], "background.png")]), root())
37109			: Promise.resolve(null);
37110
37111		const layers = blobs.layers.filter(is_not_null).map(async (blob, i) => upload()(await prepare_files([new File([blob], `layer_${i}.png`)]), root()));
37112
37113		const composite = blobs.composite
37114			? upload()(await prepare_files([new File([blobs.composite], "composite.png")]), root())
37115			: Promise.resolve(null);
37116
37117		const [background, composite_, ...layers_] = await Promise.all([bg, composite, ...layers]);
37118
37119		return {
37120			background: Array.isArray(background) ? background[0] : background,
37121			layers: layers_.flatMap((layer) => Array.isArray(layer) ? layer : [layer]).filter(is_file_data),
37122			composite: Array.isArray(composite_) ? composite_[0] : composite_
37123		};
37124	}
37125
37126	function handle_value(value) {
37127		if (!get(editor)) return;
37128
37129		if (value == null) {
37130			get(editor).handle_remove();
37131			dispatch("receive_null");
37132		}
37133	}
37134
37135	let background_image = mutable_source(false);
37136	let can_undo = mutable_source();
37137	let image_id = prop($$props, 'image_id', 12, null);
37138
37139	function nextframe() {
37140		return new Promise((resolve) => setTimeout(() => resolve(), 30));
37141	}
37142
37143	let uploading = false;
37144	let pending = false;
37145
37146	async function handle_change(e) {
37147		if (!realtime()) return;
37148
37149		if (uploading) {
37150			pending = true;
37151
37152			return;
37153		}
37154
37155		uploading = true;
37156		await nextframe();
37157
37158		const blobs = await get(editor).get_blobs();
37159		const images = [];
37160		let id = Math.random().toString(36).substring(2);
37161
37162		if (blobs.background) images.push([
37163			id,
37164			"background",
37165			new File([blobs.background], "background.png"),
37166			null
37167		]);
37168
37169		if (blobs.composite) images.push([
37170			id,
37171			"composite",
37172			new File([blobs.composite], "composite.png"),
37173			null
37174		]);
37175
37176		blobs.layers.forEach((layer, i) => {
37177			if (layer) images.push([id, `layer`, new File([layer], `layer_${i}.png`), i]);
37178		});
37179
37180		await Promise.all(images.map(async ([image_id, type, data, index]) => {
37181			return accept_blobs()({
37182				binary: true,
37183				data: { file: data, id: image_id, type, index }
37184			});
37185		}));
37186
37187		image_id(id);
37188		dispatch("change");
37189		await nextframe();
37190		uploading = false;
37191
37192		if (pending) {
37193			pending = false;
37194			uploading = false;
37195			handle_change();
37196		}
37197	}
37198
37199	let current_tool = mutable_source();
37200
37201	legacy_pre_effect(() => (get(background_image)), () => {
37202		if (get(background_image)) dispatch("upload");
37203	});
37204
37205	legacy_pre_effect(
37206		() => (
37207			deep_read_state(layers()),
37208			deep_read_state(composite()),
37209			deep_read_state(background())
37210		),
37211		() => {
37212			handle_value({
37213				layers: layers(),
37214				composite: composite(),
37215				background: background()
37216			});
37217		}
37218	);
37219
37220	legacy_pre_effect(
37221		() => (
37222			get(heading),
37223			get(paragraph),
37224			deep_read_state(placeholder()),
37225			inject
37226		),
37227		() => {
37228			(($$value) => {
37229				var $$array = to_array($$value, 2);
37230
37231				set(heading, $$array[0]);
37232				set(paragraph, $$array[1]);
37233			})(placeholder() ? inject(placeholder()) : [false, false]);
37234		}
37235	);
37236
37237	legacy_pre_effect_reset();
37238
37239	var $$exports = {
37240		get_data,
37241		get brush() {
37242			return brush();
37243		},
37244
37245		set brush($$value) {
37246			brush($$value);
37247			flushSync();
37248		},
37249
37250		get eraser() {
37251			return eraser();
37252		},
37253
37254		set eraser($$value) {
37255			eraser($$value);
37256			flushSync();
37257		},
37258
37259		get sources() {
37260			return sources();
37261		},
37262
37263		set sources($$value) {
37264			sources($$value);
37265			flushSync();
37266		},
37267
37268		get i18n() {
37269			return i18n();
37270		},
37271
37272		set i18n($$value) {
37273			i18n($$value);
37274			flushSync();
37275		},
37276
37277		get root() {
37278			return root();
37279		},
37280
37281		set root($$value) {
37282			root($$value);
37283			flushSync();
37284		},
37285
37286		get label() {
37287			return label();
37288		},
37289
37290		set label($$value) {
37291			label($$value);
37292			flushSync();
37293		},
37294
37295		get show_label() {
37296			return show_label();
37297		},
37298
37299		set show_label($$value) {
37300			show_label($$value);
37301			flushSync();
37302		},
37303
37304		get changeable() {
37305			return changeable();
37306		},
37307
37308		set changeable($$value) {
37309			changeable($$value);
37310			flushSync();
37311		},
37312
37313		get theme_mode() {
37314			return theme_mode();
37315		},
37316
37317		set theme_mode($$value) {
37318			theme_mode($$value);
37319			flushSync();
37320		},
37321
37322		get layers() {
37323			return layers();
37324		},
37325
37326		set layers($$value) {
37327			layers($$value);
37328			flushSync();
37329		},
37330
37331		get composite() {
37332			return composite();
37333		},
37334
37335		set composite($$value) {
37336			composite($$value);
37337			flushSync();
37338		},
37339
37340		get background() {
37341			return background();
37342		},
37343
37344		set background($$value) {
37345			background($$value);
37346			flushSync();
37347		},
37348
37349		get layer_options() {
37350			return layer_options();
37351		},
37352
37353		set layer_options($$value) {
37354			layer_options($$value);
37355			flushSync();
37356		},
37357
37358		get transforms() {
37359			return transforms();
37360		},
37361
37362		set transforms($$value) {
37363			transforms($$value);
37364			flushSync();
37365		},
37366
37367		get accept_blobs() {
37368			return accept_blobs();
37369		},
37370
37371		set accept_blobs($$value) {
37372			accept_blobs($$value);
37373			flushSync();
37374		},
37375
37376		get canvas_size() {
37377			return canvas_size();
37378		},
37379
37380		set canvas_size($$value) {
37381			canvas_size($$value);
37382			flushSync();
37383		},
37384
37385		get fixed_canvas() {
37386			return fixed_canvas();
37387		},
37388
37389		set fixed_canvas($$value) {
37390			fixed_canvas($$value);
37391			flushSync();
37392		},
37393
37394		get realtime() {
37395			return realtime();
37396		},
37397
37398		set realtime($$value) {
37399			realtime($$value);
37400			flushSync();
37401		},
37402
37403		get upload() {
37404			return upload();
37405		},
37406
37407		set upload($$value) {
37408			upload($$value);
37409			flushSync();
37410		},
37411
37412		get is_dragging() {
37413			return is_dragging();
37414		},
37415
37416		set is_dragging($$value) {
37417			is_dragging($$value);
37418			flushSync();
37419		},
37420
37421		get placeholder() {
37422			return placeholder();
37423		},
37424
37425		set placeholder($$value) {
37426			placeholder($$value);
37427			flushSync();
37428		},
37429
37430		get border_region() {
37431			return border_region();
37432		},
37433
37434		set border_region($$value) {
37435			border_region($$value);
37436			flushSync();
37437		},
37438
37439		get full_history() {
37440			return full_history();
37441		},
37442
37443		set full_history($$value) {
37444			full_history($$value);
37445			flushSync();
37446		},
37447
37448		get webcam_options() {
37449			return webcam_options();
37450		},
37451
37452		set webcam_options($$value) {
37453			webcam_options($$value);
37454			flushSync();
37455		},
37456
37457		get show_download_button() {
37458			return show_download_button();
37459		},
37460
37461		set show_download_button($$value) {
37462			show_download_button($$value);
37463			flushSync();
37464		},
37465
37466		get image_id() {
37467			return image_id();
37468		},
37469
37470		set image_id($$value) {
37471			image_id($$value);
37472			flushSync();
37473		}
37474	};
37475
37476	init$1();
37477
37478	var fragment = root_1();
37479	var node = first_child(fragment);
37480
37481	{
37482		let $0 = derived_safe_equal(() => (
37483			deep_read_state(label()),
37484			deep_read_state(i18n()),
37485			untrack(() => label() || i18n()("image.image"))
37486		));
37487
37488		BlockLabel(node, {
37489			get show_label() {
37490				return show_label();
37491			},
37492
37493			get Icon() {
37494				return Image$1;
37495			},
37496
37497			get label() {
37498				return get($0);
37499			}
37500		});
37501	}
37502
37503	var node_1 = sibling(node, 2);
37504
37505	bind_this(
37506		ImageEditor_1(node_1, {
37507			get transforms() {
37508				return transforms();
37509			},
37510
37511			get composite() {
37512				return composite();
37513			},
37514
37515			get layers() {
37516				return layers();
37517			},
37518
37519			get background() {
37520				return background();
37521			},
37522
37523			get canvas_size() {
37524				return canvas_size();
37525			},
37526
37527			get changeable() {
37528				return changeable();
37529			},
37530
37531			get sources() {
37532				return sources();
37533			},
37534
37535			get brush_options() {
37536				return brush();
37537			},
37538
37539			get eraser_options() {
37540				return eraser();
37541			},
37542
37543			get fixed_canvas() {
37544				return fixed_canvas();
37545			},
37546
37547			get border_region() {
37548				return border_region();
37549			},
37550
37551			get layer_options() {
37552				return layer_options();
37553			},
37554
37555			get i18n() {
37556				return i18n();
37557			},
37558
37559			get root() {
37560				return root();
37561			},
37562
37563			get upload() {
37564				return upload();
37565			},
37566
37567			get webcam_options() {
37568				return webcam_options();
37569			},
37570
37571			get show_download_button() {
37572				return show_download_button();
37573			},
37574
37575			get theme_mode() {
37576				return theme_mode();
37577			},
37578
37579			get background_image() {
37580				return get(background_image);
37581			},
37582
37583			set background_image($$value) {
37584				set(background_image, $$value);
37585			},
37586
37587			get current_tool() {
37588				return get(current_tool);
37589			},
37590
37591			set current_tool($$value) {
37592				set(current_tool, $$value);
37593			},
37594
37595			get is_dragging() {
37596				return is_dragging();
37597			},
37598
37599			set is_dragging($$value) {
37600				is_dragging($$value);
37601			},
37602
37603			get has_drawn() {
37604				return get(has_drawn);
37605			},
37606
37607			set has_drawn($$value) {
37608				set(has_drawn, $$value);
37609			},
37610
37611			get can_undo() {
37612				return get(can_undo);
37613			},
37614
37615			set can_undo($$value) {
37616				set(can_undo, $$value);
37617			},
37618
37619			get full_history() {
37620				return full_history();
37621			},
37622
37623			set full_history($$value) {
37624				full_history($$value);
37625			},
37626
37627			$$events: {
37628				history($$arg) {
37629					bubble_event.call(this, $$props, $$arg);
37630				},
37631
37632				save($$arg) {
37633					bubble_event.call(this, $$props, $$arg);
37634				},
37635
37636				input($$arg) {
37637					bubble_event.call(this, $$props, $$arg);
37638				},
37639				change: handle_change,
37640				clear: () => dispatch("clear"),
37641				download_error($$arg) {
37642					bubble_event.call(this, $$props, $$arg);
37643				}
37644			},
37645
37646			children: ($$anchor, $$slotProps) => {
37647				var fragment_1 = comment();
37648				var node_2 = first_child(fragment_1);
37649
37650				{
37651					var consequent_6 = ($$anchor) => {
37652						var div = root_3();
37653						var node_3 = child(div);
37654
37655						{
37656							var consequent_3 = ($$anchor) => {
37657								var fragment_2 = comment();
37658								var node_4 = first_child(fragment_2);
37659
37660								{
37661									var consequent_2 = ($$anchor) => {
37662										var fragment_3 = root_5();
37663										var node_5 = first_child(fragment_3);
37664
37665										{
37666											var consequent = ($$anchor) => {
37667												var h2 = root_6();
37668												var text = child(h2, true);
37669
37670												reset(h2);
37671												template_effect(() => set_text(text, get(heading)));
37672												append($$anchor, h2);
37673											};
37674
37675											if_block(node_5, ($$render) => {
37676												if (get(heading)) $$render(consequent);
37677											});
37678										}
37679
37680										var node_6 = sibling(node_5, 2);
37681
37682										{
37683											var consequent_1 = ($$anchor) => {
37684												var p = root_7();
37685												var text_1 = child(p, true);
37686
37687												reset(p);
37688												template_effect(() => set_text(text_1, get(paragraph)));
37689												append($$anchor, p);
37690											};
37691
37692											if_block(node_6, ($$render) => {
37693												if (get(paragraph)) $$render(consequent_1);
37694											});
37695										}
37696
37697										append($$anchor, fragment_3);
37698									};
37699
37700									var alternate = ($$anchor) => {
37701										var div_1 = root_8();
37702
37703										append($$anchor, div_1);
37704									};
37705
37706									if_block(node_4, ($$render) => {
37707										if (get(heading) || get(paragraph)) $$render(consequent_2); else $$render(alternate, false);
37708									});
37709								}
37710
37711								append($$anchor, fragment_2);
37712							};
37713
37714							if_block(node_3, ($$render) => {
37715								if ((
37716									deep_read_state(sources()),
37717									untrack(() => sources() && sources().length)
37718								)) $$render(consequent_3);
37719							});
37720						}
37721
37722						var node_7 = sibling(node_3, 2);
37723
37724						{
37725							var consequent_4 = ($$anchor) => {
37726								var div_2 = root_9();
37727
37728								append($$anchor, div_2);
37729							};
37730
37731							if_block(node_7, ($$render) => {
37732								if ((
37733									deep_read_state(sources()),
37734									deep_read_state(brush()),
37735									deep_read_state(placeholder()),
37736									untrack(() => sources() && sources().length && brush() && !placeholder())
37737								)) $$render(consequent_4);
37738							});
37739						}
37740
37741						var node_8 = sibling(node_7, 2);
37742
37743						{
37744							var consequent_5 = ($$anchor) => {
37745								var div_3 = root_10();
37746
37747								append($$anchor, div_3);
37748							};
37749
37750							if_block(node_8, ($$render) => {
37751								if (brush() && !placeholder()) $$render(consequent_5);
37752							});
37753						}
37754
37755						reset(div);
37756						append($$anchor, div);
37757					};
37758
37759					if_block(node_2, ($$render) => {
37760						if (get(current_tool) === "image" && !get(can_undo)) $$render(consequent_6);
37761					});
37762				}
37763
37764				append($$anchor, fragment_1);
37765			},
37766			$$slots: { default: true },
37767			$$legacy: true
37768		}),
37769		($$value) => set(editor, $$value),
37770		() => get(editor)
37771	);
37772
37773	append($$anchor, fragment);
37774	bind_prop($$props, 'get_data', get_data);
37775
37776	return pop($$exports);
37777}
37778
37779var root_2 = from_html(`<!> <!>`, 1);
37780var root_4 = from_html(`<!> <!>`, 1);
37781
37782function Index($$anchor, $$props) {
37783	push($$props, true);
37784
37785	let props = rest_props($$props, ['$$slots', '$$events', '$$legacy']);
37786
37787	class ImageEditorGradio extends Gradio {
37788		async get_data() {
37789			const data = await super.get_data();
37790			const value = await _get_value();
37791
37792			return { ...data, value };
37793		}
37794	}
37795
37796	const gradio = new ImageEditorGradio(props, {
37797		server: { accept_blobs: () => {} },
37798		buttons: [],
37799		height: 350,
37800		border_region: 0
37801	});
37802
37803	let editor_instance;
37804	let image_id = state(null);
37805	let has_run_change = false;
37806	let has_run_input = false;
37807
37808	async function _get_value() {
37809		if (get(image_id)) {
37810			const val = { id: get(image_id) };
37811
37812			set(image_id, null);
37813
37814			return val;
37815		}
37816
37817		const blobs = await editor_instance.get_data();
37818
37819		return blobs;
37820	}
37821
37822	let is_dragging;
37823
37824	// function handle_change(): void {
37825	// 	if (gradio.props.value) gradio.dispatch("change");
37826	// }
37827	function handle_save() {
37828		gradio.dispatch("apply");
37829	}
37830
37831	// function handle_history_change(): void {
37832	// 	gradio.dispatch("change");
37833	// 	gradio.dispatch("input");
37834	// }
37835	let has_value = user_derived(() => gradio.props.value?.background || gradio.props.value?.layers?.length || gradio.props.value?.composite);
37836
37837	let normalised_background = user_derived(() => gradio.props.value?.background ? new FileData(gradio.props.value.background) : null);
37838	let normalised_composite = user_derived(() => gradio.props.value?.composite ? new FileData(gradio.props.value.composite) : null);
37839	let normalised_layers = user_derived(() => gradio.props.value?.layers?.map((layer) => new FileData(layer)) || []);
37840	var fragment = comment();
37841	var node = first_child(fragment);
37842
37843	{
37844		var consequent = ($$anchor) => {
37845			{
37846				let $0 = user_derived(() => is_dragging ? "focus" : "base");
37847
37848				Block($$anchor, {
37849					get visible() {
37850						return gradio.shared.visible;
37851					},
37852					variant: "solid",
37853					get border_mode() {
37854						return get($0);
37855					},
37856					padding: false,
37857					get elem_id() {
37858						return gradio.shared.elem_id;
37859					},
37860
37861					get elem_classes() {
37862						return gradio.shared.elem_classes;
37863					},
37864
37865					get height() {
37866						return gradio.props.height;
37867					},
37868
37869					get width() {
37870						return gradio.props.width;
37871					},
37872					allow_overflow: true,
37873					overflow_behavior: 'visible',
37874					get container() {
37875						return gradio.shared.container;
37876					},
37877
37878					get scale() {
37879						return gradio.shared.scale;
37880					},
37881
37882					get min_width() {
37883						return gradio.shared.min_width;
37884					},
37885
37886					children: ($$anchor, $$slotProps) => {
37887						var fragment_2 = root_2();
37888						var node_1 = first_child(fragment_2);
37889
37890						Static(node_1, spread_props(
37891							{
37892								get autoscroll() {
37893									return gradio.shared.autoscroll;
37894								},
37895
37896								get i18n() {
37897									return gradio.i18n;
37898								}
37899							},
37900							() => gradio.shared.loading_status,
37901							{
37902								on_clear_status: () => gradio.dispatch("clear_status", gradio.shared.loading_status)
37903							}
37904						));
37905
37906						var node_2 = sibling(node_1, 2);
37907
37908						{
37909							let $0 = user_derived(() => gradio.props.value?.composite || null);
37910
37911							ImagePreview(node_2, {
37912								get value() {
37913									return get($0);
37914								},
37915
37916								get label() {
37917									return gradio.shared.label;
37918								},
37919
37920								get show_label() {
37921									return gradio.shared.show_label;
37922								},
37923
37924								get buttons() {
37925									return gradio.props.buttons;
37926								},
37927
37928								get selectable() {
37929									return gradio.props._selectable;
37930								},
37931
37932								get i18n() {
37933									return gradio.i18n;
37934								},
37935
37936								$$events: {
37937									select: ({ detail }) => gradio.dispatch("select", detail),
37938									share: ({ detail }) => gradio.dispatch("share", detail),
37939									error: ({ detail }) => gradio.dispatch("error", detail)
37940								}
37941							});
37942						}
37943
37944						append($$anchor, fragment_2);
37945					},
37946					$$slots: { default: true }
37947				});
37948			}
37949		};
37950
37951		var alternate = ($$anchor) => {
37952			{
37953				let $0 = user_derived(() => get(has_value) ? "solid" : "dashed");
37954				let $1 = user_derived(() => is_dragging ? "focus" : "base");
37955
37956				Block($$anchor, {
37957					get visible() {
37958						return gradio.shared.visible;
37959					},
37960
37961					get variant() {
37962						return get($0);
37963					},
37964
37965					get border_mode() {
37966						return get($1);
37967					},
37968					padding: false,
37969					get elem_id() {
37970						return gradio.shared.elem_id;
37971					},
37972
37973					get elem_classes() {
37974						return gradio.shared.elem_classes;
37975					},
37976
37977					get height() {
37978						return gradio.props.height;
37979					},
37980
37981					get width() {
37982						return gradio.props.width;
37983					},
37984					allow_overflow: true,
37985					overflow_behavior: 'visible',
37986					get container() {
37987						return gradio.shared.container;
37988					},
37989
37990					get scale() {
37991						return gradio.shared.scale;
37992					},
37993
37994					get min_width() {
37995						return gradio.shared.min_width;
37996					},
37997
37998					children: ($$anchor, $$slotProps) => {
37999						var fragment_4 = root_4();
38000						var node_3 = first_child(fragment_4);
38001
38002						Static(node_3, spread_props(
38003							{
38004								get autoscroll() {
38005									return gradio.shared.autoscroll;
38006								},
38007
38008								get i18n() {
38009									return gradio.i18n;
38010								}
38011							},
38012							() => gradio.shared.loading_status,
38013							{
38014								on_clear_status: () => gradio.dispatch("clear_status", gradio.shared.loading_status)
38015							}
38016						));
38017
38018						var node_4 = sibling(node_3, 2);
38019
38020						{
38021							let $0 = user_derived(() => gradio.shared.attached_events.includes("apply"));
38022							let $1 = user_derived(() => gradio.shared.attached_events.includes("change") || gradio.shared.attached_events.includes("input"));
38023							let $2 = user_derived(() => gradio.props.buttons === null ? true : gradio.props.buttons.includes("download"));
38024
38025							bind_this(
38026								InteractiveImageEditor(node_4, {
38027									get border_region() {
38028										return gradio.props.border_region;
38029									},
38030
38031									get canvas_size() {
38032										return gradio.props.canvas_size;
38033									},
38034
38035									get layers() {
38036										return get(normalised_layers);
38037									},
38038
38039									get composite() {
38040										return get(normalised_composite);
38041									},
38042
38043									get background() {
38044										return get(normalised_background);
38045									},
38046
38047									get root() {
38048										return gradio.shared.root;
38049									},
38050
38051									get sources() {
38052										return gradio.props.sources;
38053									},
38054
38055									get label() {
38056										return gradio.shared.label;
38057									},
38058
38059									get show_label() {
38060										return gradio.shared.show_label;
38061									},
38062
38063									get fixed_canvas() {
38064										return gradio.props.fixed_canvas;
38065									},
38066
38067									get brush() {
38068										return gradio.props.brush;
38069									},
38070
38071									get eraser() {
38072										return gradio.props.eraser;
38073									},
38074
38075									get changeable() {
38076										return get($0);
38077									},
38078
38079									get realtime() {
38080										return get($1);
38081									},
38082
38083									get i18n() {
38084										return gradio.i18n;
38085									},
38086
38087									get transforms() {
38088										return gradio.props.transforms;
38089									},
38090
38091									get accept_blobs() {
38092										return gradio.shared.server.accept_blobs;
38093									},
38094
38095									get layer_options() {
38096										return gradio.props.layers;
38097									},
38098									upload: (...args) => gradio.shared.client.upload(...args),
38099									get placeholder() {
38100										return gradio.props.placeholder;
38101									},
38102
38103									get webcam_options() {
38104										return gradio.props.webcam_options;
38105									},
38106
38107									get show_download_button() {
38108										return get($2);
38109									},
38110
38111									get theme_mode() {
38112										return gradio.shared.theme_mode;
38113									},
38114
38115									get is_dragging() {
38116										return is_dragging;
38117									},
38118
38119									set is_dragging($$value) {
38120										is_dragging = $$value;
38121									},
38122
38123									get image_id() {
38124										return get(image_id);
38125									},
38126
38127									set image_id($$value) {
38128										set(image_id, $$value, true);
38129									},
38130
38131									$$events: {
38132										input: () => {
38133											if (!has_run_input) {
38134												has_run_input = true;
38135											} else {
38136												gradio.dispatch("input");
38137											}
38138										},
38139										save: (e) => handle_save(),
38140										edit: () => gradio.dispatch("edit"),
38141										clear: () => gradio.dispatch("clear"),
38142										drag: ({ detail }) => is_dragging = detail,
38143										upload: () => gradio.dispatch("upload"),
38144										share: ({ detail }) => gradio.dispatch("share", detail),
38145										error: [
38146											({ detail }) => {
38147												gradio.shared.loading_status = gradio.shared.loading_status || {};
38148												gradio.shared.loading_status.status = "error";
38149												gradio.dispatch("error", detail);
38150											},
38151
38152											function ($$arg) {
38153												bubble_event.call(this, $$props, $$arg);
38154											}
38155										],
38156										receive_null: () => gradio.props.value = { background: null, layers: [], composite: null },
38157										change: () => {
38158											if (!has_run_change) {
38159												has_run_change = true;
38160											} else {
38161												gradio.dispatch("change");
38162											}
38163										},
38164										download_error: (e) => gradio.dispatch("error", e.detail)
38165									}
38166								}),
38167								($$value) => editor_instance = $$value,
38168								() => editor_instance
38169							);
38170						}
38171
38172						append($$anchor, fragment_4);
38173					},
38174					$$slots: { default: true }
38175				});
38176			}
38177		};
38178
38179		if_block(node, ($$render) => {
38180			if (!gradio.shared.interactive) $$render(consequent); else $$render(alternate, false);
38181		});
38182	}
38183
38184	append($$anchor, fragment);
38185	pop();
38186}
38187
38188const Index$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
38189	__proto__: null,
38190	default: Index
38191}, Symbol.toStringTag, { value: 'Module' }));
38192
38193export { UPDATE_BLEND as $, AbstractRenderer as A, BufferUsage as B, Container as C, DOMAdapter as D, ExtensionType as E, Filter as F, GCManagedHash as G, BigPool as H, getGlobalBounds as I, Bounds as J, TexturePool as K, FilterEffect as L, Matrix as M, Sprite as N, getAttributeInfoFromFormat as O, Point as P, unsafeEvalSupported as Q, RendererType as R, State as S, Ticker as T, UPDATE_PRIORITY as U, GlobalResourceRegistry as V, Rectangle as W, SystemRunner as X, multiplyColors as Y, UPDATE_VISIBLE as Z, UPDATE_COLOR as _, EventEmitter as a, TextureStyle as a0, Color as a1, RenderGroup as a2, getL
38193ocalBounds as a3, RenderTexture as a4, VERSION as a5, deprecation as a6, v8_0_0 as a7, RendererInitHook as a8, Geometry as a9, checkMaxIfStatementsInShader as aa, compileHighShaderGlProgram as ab, colorBitGl as ac, roundPixelsBitGl as ad, generateTextureBatchBitGl as ae, getBatchSamplersUniformGroup as af, nextPow2 as ag, CanvasTextMetrics as ah, fontStringFromTextStyle as ai, getCanvasFillStyle as aj, BatchableGraphics as ak, getAdjustedBlendModeBlend as al, ViewableBuffer as am, BitmapFontManager as an, getBitmapTextLayout as ao, Cache as ap, Graphics as aq, updateQuadBounds as ar, TextStyle as as, GraphicsContextSystem as at, Index$1 as au, STENCIL_MODES as b, Buffer as c, BindGroup as d, extensions as e, fastCopy as f, getTextureBatchBindGroup as g, createIdFromString as h, CLEAR as i, CanvasSource as j, TextureSource as k, UniformGroup as l, CanvasPool as m, compileHighShaderGpuProgram as n, colorBit as o, roundPixelsBit as p, generateTextureBatchBit as q, removeItems as r, Shader as s, Texture as t, uid as u, TextureMatrix as v, warn as w, GpuProgram as x, GlProgram as y, DefaultBatcher as z };
38194//# sourceMappingURL=Index-DC3EL6G-.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.