Gabriel commited on
Commit
e07ca67
·
verified ·
1 Parent(s): 9b7a16e

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -10,14 +10,13 @@ colorFrom: blue
10
  colorTo: yellow
11
  sdk: gradio
12
  pinned: false
13
- app_file: app.py
14
  emoji: 👀
15
  sdk_version: 5.46.1
16
  ---
17
 
18
-
19
  # `gradio_polygonannotator`
20
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.1.0%20-%20orange"> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
21
 
22
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
23
 
@@ -33,85 +32,434 @@ pip install gradio_polygonannotator
33
  import gradio as gr
34
  from gradio_polygonannotator import PolygonAnnotator
35
 
36
- # Example with document regions
37
  example_data = {
38
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
39
  "polygons": [
40
  {
41
- "id": "title",
42
- "coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
43
- "color": "#FF6B6B",
44
- "mask_opacity": 0.15,
45
- "stroke_width": 1.0,
 
 
 
 
 
 
 
 
 
 
 
 
46
  "stroke_opacity": 0.8,
 
 
 
 
 
47
  },
48
  {
49
- "id": "paragraph_1",
50
- "coordinates": [[100, 400], [750, 400], [750, 600], [100, 600]],
51
- "color": "#4ECDC4",
52
- "mask_opacity": 0.15,
53
- "stroke_width": 0.7,
54
- "stroke_opacity": 0.6,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  },
56
  {
57
- "id": "paragraph_2",
58
- "coordinates": [[100, 650], [750, 650], [750, 950], [100, 950]],
59
- "color": "#4ECDC4",
60
- "mask_opacity": 0.15,
61
- "stroke_width": 0.7,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  "stroke_opacity": 0.6,
 
 
 
 
 
63
  },
64
  {
65
- "id": "signature",
66
- "coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
67
- "color": "#FFE66D",
68
- "mask_opacity": 0.2,
69
- "stroke_width": 1.5,
70
- "stroke_opacity": 0.8,
71
- }
72
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
- def handle_selection(data, evt: gr.SelectData):
76
- """Handle polygon selection and display info"""
 
 
 
 
 
 
 
 
77
  if evt.value and data:
78
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
79
- info = f"Selected {len(selected_ids)} polygon(s):\n"
80
- for poly_id in selected_ids:
81
- polygon = next((p for p in data["polygons"] if p["id"] == poly_id), None)
82
- if polygon:
83
- info += f"• {poly_id}\n"
84
- return info
85
- return "Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  with gr.Blocks() as demo:
88
  gr.Markdown("""
89
- # PolygonAnnotator - Interactive Polygon Selection
90
-
91
- Click on polygons to select them. Use **Ctrl/Cmd+Click** for multiple selections.
92
- Click selected polygons to deselect.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  """)
94
 
95
  with gr.Row():
96
- with gr.Column(scale=3):
97
- annotator = PolygonAnnotator(
98
  value=example_data,
99
- label="Document with Region Annotations",
100
  height=600,
101
  )
102
 
103
  with gr.Column(scale=1):
104
  selected_info = gr.Textbox(
105
- label="Selected Regions",
106
- lines=6,
107
- value="Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
108
  )
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  # Handle selection events
111
- annotator.select(
112
- handle_selection,
113
- inputs=[annotator],
114
- outputs=[selected_info]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  )
116
 
117
  if __name__ == "__main__":
@@ -143,7 +491,7 @@ dict | None
143
 
144
  </td>
145
  <td align="left"><code>None</code></td>
146
- <td align="left">Dictionary containing 'image' (FileData), 'polygons' (list with id, coordinates, color, opacities),</td>
147
  </tr>
148
 
149
  <tr>
@@ -156,7 +504,7 @@ str | I18nData | None
156
 
157
  </td>
158
  <td align="left"><code>None</code></td>
159
- <td align="left">Component label shown above the annotator.</td>
160
  </tr>
161
 
162
  <tr>
@@ -169,7 +517,7 @@ Timer | float | None
169
 
170
  </td>
171
  <td align="left"><code>None</code></td>
172
- <td align="left">Continuously calls `value` to recalculate it if `value` is a function.</td>
173
  </tr>
174
 
175
  <tr>
@@ -182,7 +530,7 @@ Component | Sequence[Component] | set[Component] | None
182
 
183
  </td>
184
  <td align="left"><code>None</code></td>
185
- <td align="left">Components used as inputs to calculate `value` if it's a function.</td>
186
  </tr>
187
 
188
  <tr>
@@ -195,7 +543,7 @@ bool | None
195
 
196
  </td>
197
  <td align="left"><code>None</code></td>
198
- <td align="left">Whether to display the label.</td>
199
  </tr>
200
 
201
  <tr>
@@ -208,7 +556,7 @@ bool
208
 
209
  </td>
210
  <td align="left"><code>True</code></td>
211
- <td align="left">Whether to show image download button.</td>
212
  </tr>
213
 
214
  <tr>
@@ -221,7 +569,7 @@ int | str | None
221
 
222
  </td>
223
  <td align="left"><code>None</code></td>
224
- <td align="left">Component height in pixels or CSS units.</td>
225
  </tr>
226
 
227
  <tr>
@@ -234,7 +582,7 @@ int | str | None
234
 
235
  </td>
236
  <td align="left"><code>None</code></td>
237
- <td align="left">Component width in pixels or CSS units.</td>
238
  </tr>
239
 
240
  <tr>
@@ -247,7 +595,7 @@ bool
247
 
248
  </td>
249
  <td align="left"><code>True</code></td>
250
- <td align="left">Whether to wrap component in a container with padding.</td>
251
  </tr>
252
 
253
  <tr>
@@ -260,7 +608,7 @@ int | None
260
 
261
  </td>
262
  <td align="left"><code>None</code></td>
263
- <td align="left">Relative size compared to adjacent components.</td>
264
  </tr>
265
 
266
  <tr>
@@ -273,7 +621,7 @@ int
273
 
274
  </td>
275
  <td align="left"><code>160</code></td>
276
- <td align="left">Minimum pixel width before wrapping.</td>
277
  </tr>
278
 
279
  <tr>
@@ -286,7 +634,7 @@ bool | None
286
 
287
  </td>
288
  <td align="left"><code>None</code></td>
289
- <td align="left">Whether users can interact with polygons (selection/deselection).</td>
290
  </tr>
291
 
292
  <tr>
@@ -299,7 +647,7 @@ bool | Literal["hidden"]
299
 
300
  </td>
301
  <td align="left"><code>True</code></td>
302
- <td align="left">Whether component is visible ("hidden" keeps it in DOM but invisible).</td>
303
  </tr>
304
 
305
  <tr>
@@ -312,7 +660,7 @@ str | None
312
 
313
  </td>
314
  <td align="left"><code>None</code></td>
315
- <td align="left">HTML DOM id for CSS targeting.</td>
316
  </tr>
317
 
318
  <tr>
@@ -325,7 +673,7 @@ list[str] | str | None
325
 
326
  </td>
327
  <td align="left"><code>None</code></td>
328
- <td align="left">HTML DOM classes for CSS targeting.</td>
329
  </tr>
330
 
331
  <tr>
@@ -338,7 +686,7 @@ bool
338
 
339
  </td>
340
  <td align="left"><code>True</code></td>
341
- <td align="left">Whether to render the component immediately.</td>
342
  </tr>
343
 
344
  <tr>
@@ -351,7 +699,7 @@ int | str | tuple[int | str, ...] | None
351
 
352
  </td>
353
  <td align="left"><code>None</code></td>
354
- <td align="left">Key for maintaining component identity across re-renders.</td>
355
  </tr>
356
 
357
  <tr>
@@ -364,7 +712,7 @@ list[str] | str | None
364
 
365
  </td>
366
  <td align="left"><code>"value"</code></td>
367
- <td align="left">Parameters preserved across re-renders with same key.</td>
368
  </tr>
369
  </tbody></table>
370
 
@@ -389,12 +737,12 @@ The impact on the users predict function varies depending on whether the compone
389
 
390
  The code snippet below is accurate in cases where the component is used as both an input and an output.
391
 
392
- - **As output:** Is passed, dictionary with image path, polygon data including coordinates, colors, opacities,.
393
- - **As input:** Should return, dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries.
394
 
395
  ```python
396
  def predict(
397
  value: dict | None
398
  ) -> dict | None:
399
  return value
400
- ```
 
10
  colorTo: yellow
11
  sdk: gradio
12
  pinned: false
13
+ app_file: space.py
14
  emoji: 👀
15
  sdk_version: 5.46.1
16
  ---
17
 
 
18
  # `gradio_polygonannotator`
19
+ <a href="https://pypi.org/project/gradio_polygonannotator/" target="_blank"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/gradio_polygonannotator"></a> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
20
 
21
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
22
 
 
32
  import gradio as gr
33
  from gradio_polygonannotator import PolygonAnnotator
34
 
 
35
  example_data = {
36
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
37
  "polygons": [
38
  {
39
+ "id": "complex_header",
40
+ "coordinates": [
41
+ [150, 120],
42
+ [200, 100],
43
+ [350, 110],
44
+ [450, 95],
45
+ [600, 120],
46
+ [620, 180],
47
+ [580, 220],
48
+ [400, 240],
49
+ [250, 235],
50
+ [180, 200],
51
+ [140, 160],
52
+ ],
53
+ "color": "#FF0000",
54
+ "mask_opacity": 0.3,
55
+ "stroke_width": 1.2,
56
  "stroke_opacity": 0.8,
57
+ "selected_mask_opacity": 0.6,
58
+ "selected_stroke_opacity": 1.0,
59
+ "display_text": "Complex Header",
60
+ "display_font_size": 14,
61
+ "display_text_color": "#FFFFFF",
62
  },
63
  {
64
+ "id": "overlapping_ribbon",
65
+ "coordinates": [
66
+ [300, 150],
67
+ [550, 160],
68
+ [580, 200],
69
+ [650, 220],
70
+ [680, 280],
71
+ [620, 320],
72
+ [500, 340],
73
+ [350, 330],
74
+ [200, 310],
75
+ [180, 270],
76
+ [220, 220],
77
+ [280, 190],
78
+ ],
79
+ "color": "#00FF00",
80
+ "mask_opacity": 0.25,
81
+ "stroke_width": 1.5,
82
+ "stroke_opacity": 0.7,
83
+ "selected_mask_opacity": 0.5,
84
+ "selected_stroke_opacity": 1.0,
85
+ "display_text": "Overlapping Ribbon",
86
+ "display_font_size": 14,
87
+ "display_text_color": "#000000",
88
  },
89
  {
90
+ "id": "irregular_content",
91
+ "coordinates": [
92
+ [120, 380],
93
+ [250, 350],
94
+ [400, 370],
95
+ [550, 390],
96
+ [720, 420],
97
+ [750, 500],
98
+ [780, 650],
99
+ [720, 800],
100
+ [650, 920],
101
+ [500, 950],
102
+ [300, 940],
103
+ [150, 900],
104
+ [80, 750],
105
+ [90, 600],
106
+ [110, 450],
107
+ ],
108
+ "color": "#0000FF",
109
+ "mask_opacity": 0.2,
110
+ "stroke_width": 0.8,
111
  "stroke_opacity": 0.6,
112
+ "selected_mask_opacity": 0.4,
113
+ "selected_stroke_opacity": 0.9,
114
+ "display_text": "Irregular Content",
115
+ "display_font_size": 14,
116
+ "display_text_color": "#FFFF00",
117
  },
118
  {
119
+ "id": "star_shaped",
120
+ "coordinates": [
121
+ [400, 850],
122
+ [450, 900],
123
+ [520, 910],
124
+ [480, 970],
125
+ [500, 1040],
126
+ [400, 1000],
127
+ [300, 1040],
128
+ [320, 970],
129
+ [280, 910],
130
+ [350, 900],
131
+ ],
132
+ "color": "#FF00FF",
133
+ "mask_opacity": 0.35,
134
+ "stroke_width": 2.0,
135
+ "stroke_opacity": 0.9,
136
+ "selected_mask_opacity": 0.7,
137
+ "selected_stroke_opacity": 1.0,
138
+ "display_text": "Star Shape",
139
+ "display_font_size": 14,
140
+ "display_text_color": "#FFFFFF",
141
+ },
142
+ {
143
+ "id": "overlapping_triangle",
144
+ "coordinates": [
145
+ [480, 600],
146
+ [650, 750],
147
+ [720, 850],
148
+ [600, 920],
149
+ [450, 880],
150
+ [350, 800],
151
+ [380, 700],
152
+ ],
153
+ "color": "#FFAA00",
154
+ "mask_opacity": 0.28,
155
+ "stroke_width": 1.8,
156
+ "stroke_opacity": 0.75,
157
+ "selected_mask_opacity": 0.55,
158
+ "selected_stroke_opacity": 1.0,
159
+ "display_text": "Triangle",
160
+ "display_font_size": 14,
161
+ "display_text_color": "#000000",
162
+ },
163
+ ],
164
  }
165
 
166
+ polygon_table = [
167
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
168
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
169
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
170
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
171
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
172
+ ]
173
+
174
+
175
+ def process_viewer_selection(data, evt: gr.SelectData):
176
  if evt.value and data:
177
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
178
+
179
+ highlighted_table = []
180
+ for row in polygon_table:
181
+ if row[0] in selected_ids:
182
+ highlighted_row = [
183
+ f"→ {row[0]} ←",
184
+ f" {row[1]} ",
185
+ f"→ {row[2]} ←",
186
+ f"→ {row[3]} ←",
187
+ f"→ {row[4]} ←",
188
+ f"→ {row[5]} ←",
189
+ ]
190
+ highlighted_table.append(highlighted_row)
191
+ else:
192
+ highlighted_table.append(row)
193
+
194
+ info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
195
+ for selected_id in selected_ids:
196
+ selected_polygon = next(
197
+ (p for p in data["polygons"] if p["id"] == selected_id), None
198
+ )
199
+ if selected_polygon:
200
+ info_lines.append(
201
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
202
+ )
203
+
204
+ info_text = "\n".join(info_lines)
205
+ return info_text, highlighted_table
206
+
207
+ return "No polygons selected", polygon_table
208
+
209
+
210
+ def process_dataframe_selection(selected_data, evt: gr.SelectData):
211
+ if evt.index is not None and evt.index[0] < len(polygon_table):
212
+ selected_row = polygon_table[evt.index[0]]
213
+ polygon_id = selected_row[0]
214
+
215
+ updated_data = example_data.copy()
216
+ updated_data["selected_polygons"] = [polygon_id]
217
+
218
+ highlighted_table = []
219
+ for i, row in enumerate(polygon_table):
220
+ if i == evt.index[0]:
221
+ highlighted_row = [
222
+ f"→ {row[0]} ←",
223
+ f"→ {row[1]} ←",
224
+ f"→ {row[2]} ←",
225
+ f"→ {row[3]} ←",
226
+ f"→ {row[4]} ←",
227
+ f"→ {row[5]} ←",
228
+ ]
229
+ highlighted_table.append(highlighted_row)
230
+ else:
231
+ highlighted_table.append(row)
232
+
233
+ info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
234
+ return updated_data, info_text, highlighted_table
235
+
236
+ updated_data = example_data.copy()
237
+ updated_data["selected_polygons"] = []
238
+ return updated_data, "No polygons selected", polygon_table
239
+
240
+
241
+ def clear_selection():
242
+ updated_data = example_data.copy()
243
+ updated_data["selected_polygons"] = []
244
+ return updated_data, "No polygons selected", polygon_table
245
+
246
+
247
+ def select_polygon_by_id(polygon_id):
248
+ if not polygon_id or polygon_id.strip() == "":
249
+ updated_data = example_data.copy()
250
+ updated_data["selected_polygons"] = []
251
+ return updated_data, "No polygons selected", polygon_table
252
+
253
+ polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
254
+ valid_ids = [p["id"] for p in example_data["polygons"]]
255
+
256
+ valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
257
+ invalid_ids = [id for id in polygon_ids if id not in valid_ids]
258
+
259
+ if not valid_selected_ids:
260
+ updated_data = example_data.copy()
261
+ updated_data["selected_polygons"] = []
262
+ error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
263
+ return updated_data, error_msg, polygon_table
264
+
265
+ updated_data = example_data.copy()
266
+ updated_data["selected_polygons"] = valid_selected_ids
267
+
268
+ highlighted_table = []
269
+ for row in polygon_table:
270
+ if row[0] in valid_selected_ids:
271
+ highlighted_row = [
272
+ f"→ {row[0]} ←",
273
+ f"→ {row[1]} ←",
274
+ f"→ {row[2]} ←",
275
+ f"→ {row[3]} ←",
276
+ f"→ {row[4]} ←",
277
+ f"→ {row[5]} ←",
278
+ ]
279
+ highlighted_table.append(highlighted_row)
280
+ else:
281
+ highlighted_table.append(row)
282
+
283
+ info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
284
+ for selected_id in valid_selected_ids:
285
+ selected_polygon = next(
286
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
287
+ )
288
+ if selected_polygon:
289
+ info_lines.append(
290
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
291
+ )
292
+
293
+ if invalid_ids:
294
+ info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
295
+
296
+ info_text = "\n".join(info_lines)
297
+ return updated_data, info_text, highlighted_table
298
+
299
+
300
+ def toggle_text_display(current_data, show_text):
301
+ if not current_data:
302
+ return current_data
303
+
304
+ updated_data = current_data.copy()
305
+ updated_data["polygons"] = []
306
+
307
+ for polygon in current_data.get("polygons", []):
308
+ updated_polygon = polygon.copy()
309
+ if not show_text:
310
+ updated_polygon["display_font_size"] = 0
311
+ else:
312
+ original_polygon = next(
313
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
314
+ )
315
+ if original_polygon:
316
+ updated_polygon["display_text"] = original_polygon.get(
317
+ "display_text", polygon["id"]
318
+ )
319
+ updated_polygon["display_font_size"] = original_polygon.get(
320
+ "display_font_size", 14
321
+ )
322
+ updated_polygon["display_text_color"] = original_polygon.get(
323
+ "display_text_color", "#000000"
324
+ )
325
+
326
+ updated_data["polygons"].append(updated_polygon)
327
+
328
+ return updated_data
329
+
330
 
331
  with gr.Blocks() as demo:
332
  gr.Markdown("""
333
+ # PolygonAnnotator - Advanced Interactive Demo
334
+
335
+ ## 🎮 Controls & Hotkeys
336
+
337
+ ### Selection
338
+ - **Click** on polygons or text labels to select/deselect
339
+ - **Ctrl/Cmd+Click** for multiple selection
340
+ - **Click dataframe rows** to select polygons
341
+ - **Enter polygon IDs** manually in the textbox
342
+
343
+ ### Navigation
344
+ - **Mouse Wheel** - Zoom in/out at cursor position
345
+ - **+/=** - Zoom in (10%)
346
+ - **-** - Zoom out (10%)
347
+ - **Ctrl/Cmd+0** - Reset view to original
348
+ - **Arrow Keys** - Pan view (↑↓←→)
349
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
350
+
351
+ ### Features
352
+ - **Clear button** to deselect all
353
+ - **Toggle text display** checkbox for polygon labels
354
  """)
355
 
356
  with gr.Row():
357
+ with gr.Column(scale=2):
358
+ poly_annotator = PolygonAnnotator(
359
  value=example_data,
360
+ label="Document with Interactive Polygon Annotations",
361
  height=600,
362
  )
363
 
364
  with gr.Column(scale=1):
365
  selected_info = gr.Textbox(
366
+ label="Selected Polygon Information",
367
+ lines=5,
368
+ value="Click on a polygon to see its information",
369
+ )
370
+
371
+ polygon_dataframe = gr.Dataframe(
372
+ value=polygon_table,
373
+ headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
374
+ label="Polygon Data (Click rows to select)",
375
+ interactive=True,
376
  )
377
 
378
+ clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
379
+
380
+ # Add text display toggle
381
+ with gr.Row():
382
+ show_text_checkbox = gr.Checkbox(
383
+ label="Show Polygon Text",
384
+ value=True,
385
+ info="Toggle text display on polygons",
386
+ )
387
+
388
+ with gr.Row():
389
+ polygon_id_input = gr.Textbox(
390
+ label="Select by Polygon ID(s)",
391
+ placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
392
+ scale=3,
393
+ )
394
+ select_button = gr.Button("Select", variant="primary", scale=1)
395
+
396
+ gr.Markdown("""
397
+ ### Features Demonstrated
398
+
399
+ #### 🎨 Visual Customization
400
+ - Different **mask opacity** for each polygon fill
401
+ - Variable **stroke width** (0.5px to 1.5px)
402
+ - Custom **stroke opacity** for borders
403
+ - Enhanced appearance when selected
404
+ - **Text labels** with customizable colors and sizes
405
+
406
+ #### 🖱️ Interaction Methods
407
+ 1. **Direct Click**: Click polygons in the viewer
408
+ 2. **Multi-Selection**: Ctrl/Cmd+Click for multiple
409
+ 3. **Dataframe**: Click table rows
410
+ 4. **Text Input**: Type polygon IDs
411
+ 5. **Clear All**: Reset selection
412
+ 6. **Text Toggle**: Show/hide polygon labels
413
+
414
+ #### 📝 Polygon IDs
415
+ - `date_line` - Red header area
416
+ - `salutation` - Green greeting section
417
+ - `main_text_block` - Blue main content
418
+ - `closing_signature` - Yellow signature area
419
+
420
+ #### 💡 Tips
421
+ - Hover over polygons for visual feedback
422
+ - Selected polygons have increased opacity
423
+ - Use comma-separated IDs for batch selection
424
+ - Click selected polygons to deselect them
425
+ - Toggle text display to see labels on polygons
426
+ """)
427
+
428
  # Handle selection events
429
+ poly_annotator.select(
430
+ process_viewer_selection,
431
+ inputs=[poly_annotator],
432
+ outputs=[selected_info, polygon_dataframe],
433
+ )
434
+
435
+ polygon_dataframe.select(
436
+ process_dataframe_selection,
437
+ inputs=[polygon_dataframe],
438
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
439
+ )
440
+
441
+ clear_button.click(
442
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
443
+ )
444
+
445
+ select_button.click(
446
+ select_polygon_by_id,
447
+ inputs=[polygon_id_input],
448
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
449
+ )
450
+
451
+ # Also allow Enter key in textbox
452
+ polygon_id_input.submit(
453
+ select_polygon_by_id,
454
+ inputs=[polygon_id_input],
455
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
456
+ )
457
+
458
+ # Handle text display toggle
459
+ show_text_checkbox.change(
460
+ toggle_text_display,
461
+ inputs=[poly_annotator, show_text_checkbox],
462
+ outputs=[poly_annotator],
463
  )
464
 
465
  if __name__ == "__main__":
 
491
 
492
  </td>
493
  <td align="left"><code>None</code></td>
494
+ <td align="left">None</td>
495
  </tr>
496
 
497
  <tr>
 
504
 
505
  </td>
506
  <td align="left"><code>None</code></td>
507
+ <td align="left">None</td>
508
  </tr>
509
 
510
  <tr>
 
517
 
518
  </td>
519
  <td align="left"><code>None</code></td>
520
+ <td align="left">None</td>
521
  </tr>
522
 
523
  <tr>
 
530
 
531
  </td>
532
  <td align="left"><code>None</code></td>
533
+ <td align="left">None</td>
534
  </tr>
535
 
536
  <tr>
 
543
 
544
  </td>
545
  <td align="left"><code>None</code></td>
546
+ <td align="left">None</td>
547
  </tr>
548
 
549
  <tr>
 
556
 
557
  </td>
558
  <td align="left"><code>True</code></td>
559
+ <td align="left">None</td>
560
  </tr>
561
 
562
  <tr>
 
569
 
570
  </td>
571
  <td align="left"><code>None</code></td>
572
+ <td align="left">None</td>
573
  </tr>
574
 
575
  <tr>
 
582
 
583
  </td>
584
  <td align="left"><code>None</code></td>
585
+ <td align="left">None</td>
586
  </tr>
587
 
588
  <tr>
 
595
 
596
  </td>
597
  <td align="left"><code>True</code></td>
598
+ <td align="left">None</td>
599
  </tr>
600
 
601
  <tr>
 
608
 
609
  </td>
610
  <td align="left"><code>None</code></td>
611
+ <td align="left">None</td>
612
  </tr>
613
 
614
  <tr>
 
621
 
622
  </td>
623
  <td align="left"><code>160</code></td>
624
+ <td align="left">None</td>
625
  </tr>
626
 
627
  <tr>
 
634
 
635
  </td>
636
  <td align="left"><code>None</code></td>
637
+ <td align="left">None</td>
638
  </tr>
639
 
640
  <tr>
 
647
 
648
  </td>
649
  <td align="left"><code>True</code></td>
650
+ <td align="left">None</td>
651
  </tr>
652
 
653
  <tr>
 
660
 
661
  </td>
662
  <td align="left"><code>None</code></td>
663
+ <td align="left">None</td>
664
  </tr>
665
 
666
  <tr>
 
673
 
674
  </td>
675
  <td align="left"><code>None</code></td>
676
+ <td align="left">None</td>
677
  </tr>
678
 
679
  <tr>
 
686
 
687
  </td>
688
  <td align="left"><code>True</code></td>
689
+ <td align="left">None</td>
690
  </tr>
691
 
692
  <tr>
 
699
 
700
  </td>
701
  <td align="left"><code>None</code></td>
702
+ <td align="left">None</td>
703
  </tr>
704
 
705
  <tr>
 
712
 
713
  </td>
714
  <td align="left"><code>"value"</code></td>
715
+ <td align="left">None</td>
716
  </tr>
717
  </tbody></table>
718
 
 
737
 
738
  The code snippet below is accurate in cases where the component is used as both an input and an output.
739
 
740
+ - **As output:** Is passed, the preprocessed input data sent to the user's function in the backend.
741
+ - **As input:** Should return, the output data received by the component from the user's function in the backend.
742
 
743
  ```python
744
  def predict(
745
  value: dict | None
746
  ) -> dict | None:
747
  return value
748
+ ```
__init__.py ADDED
File without changes
app.py CHANGED
@@ -1,155 +1,263 @@
1
  import gradio as gr
2
  from gradio_polygonannotator import PolygonAnnotator
3
- from typing import Literal
4
 
5
  example_data = {
6
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
7
  "polygons": [
8
  {
9
- "id": "date_line",
10
- "coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
 
 
 
 
 
 
 
 
 
 
 
 
11
  "color": "#FF0000",
12
- "mask_opacity": 0.2,
13
- "stroke_width": 0.7,
14
- "stroke_opacity": 0.6,
15
- "selected_mask_opacity": 0.5,
16
- "selected_stroke_opacity": 1.0
 
 
 
17
  },
18
  {
19
- "id": "salutation",
20
- "coordinates": [[180, 280], [680, 280], [680, 340], [180, 340]],
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  "color": "#00FF00",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  "mask_opacity": 0.2,
23
- "stroke_width": 1.0,
24
  "stroke_opacity": 0.6,
25
  "selected_mask_opacity": 0.4,
26
- "selected_stroke_opacity": 0.9
 
 
 
27
  },
28
  {
29
- "id": "main_text_block",
30
- "coordinates": [[100, 400], [750, 400], [750, 950], [100, 950]],
31
- "color": "#0000FF",
32
- "mask_opacity": 0.15,
33
- "stroke_width": 0.5,
34
- "stroke_opacity": 0.5,
35
- "selected_mask_opacity": 0.4,
36
- "selected_stroke_opacity": 0.8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  },
38
  {
39
- "id": "closing_signature",
40
- "coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
41
- "color": "#FFFF00",
42
- "mask_opacity": 0.25,
43
- "stroke_width": 1.5,
44
- "stroke_opacity": 0.7,
45
- "selected_mask_opacity": 0.6,
46
- "selected_stroke_opacity": 1.0
47
- }
48
- ]
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
- # Create dataframe data from polygon information
52
  polygon_table = [
53
- ["date_line", "Date Line", "#FF0000", 0.2, 0.7, 0.6],
54
- ["salutation", "Salutation", "#00FF00", 0.2, 1.0, 0.6],
55
- ["main_text_block", "Main Text Block", "#0000FF", 0.15, 0.5, 0.5],
56
- ["closing_signature", "Closing/Signature", "#FFFF00", 0.25, 1.5, 0.7]
 
57
  ]
58
 
 
59
  def process_viewer_selection(data, evt: gr.SelectData):
60
- """Handle polygon selection from viewer and update dataframe selection"""
61
  if evt.value and data:
62
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
63
 
64
- # Create highlighted dataframe data
65
  highlighted_table = []
66
  for row in polygon_table:
67
- if row[0] in selected_ids: # If this is a selected row
68
- # Add highlighting markers to the selected row
69
- highlighted_row = [f"→ {row[0]} ←", f"→ {row[1]} ←", f"→ {row[2]} ←", f"→ {row[3]} ←", f"→ {row[4]} ←", f"→ {row[5]} ←"]
 
 
 
 
 
 
70
  highlighted_table.append(highlighted_row)
71
  else:
72
  highlighted_table.append(row)
73
 
74
- # Create info text for all selected polygons
75
  info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
76
  for selected_id in selected_ids:
77
- selected_polygon = next((p for p in data["polygons"] if p["id"] == selected_id), None)
 
 
78
  if selected_polygon:
79
- info_lines.append(f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px")
 
 
80
 
81
  info_text = "\n".join(info_lines)
82
  return info_text, highlighted_table
83
 
84
  return "No polygons selected", polygon_table
85
 
 
86
  def process_dataframe_selection(selected_data, evt: gr.SelectData):
87
- """Handle row selection from dataframe and update viewer selection"""
88
  if evt.index is not None and evt.index[0] < len(polygon_table):
89
  selected_row = polygon_table[evt.index[0]]
90
  polygon_id = selected_row[0]
91
 
92
- # Update the viewer data with the selected polygon
93
  updated_data = example_data.copy()
94
  updated_data["selected_polygons"] = [polygon_id]
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
97
- return updated_data, info_text
98
 
99
- # Deselection
100
  updated_data = example_data.copy()
101
  updated_data["selected_polygons"] = []
102
- return updated_data, "No polygons selected"
 
103
 
104
  def clear_selection():
105
- """Clear polygon selection"""
106
  updated_data = example_data.copy()
107
  updated_data["selected_polygons"] = []
108
  return updated_data, "No polygons selected", polygon_table
109
 
 
110
  def select_polygon_by_id(polygon_id):
111
- """Select polygon by ID from textbox input"""
112
  if not polygon_id or polygon_id.strip() == "":
113
- # Empty input - clear selection
114
  updated_data = example_data.copy()
115
  updated_data["selected_polygons"] = []
116
  return updated_data, "No polygons selected", polygon_table
117
 
118
- # Handle multiple IDs (comma-separated)
119
  polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
120
  valid_ids = [p["id"] for p in example_data["polygons"]]
121
 
122
- # Filter to only valid IDs
123
  valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
124
  invalid_ids = [id for id in polygon_ids if id not in valid_ids]
125
 
126
  if not valid_selected_ids:
127
- # No valid IDs
128
  updated_data = example_data.copy()
129
  updated_data["selected_polygons"] = []
130
  error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
131
  return updated_data, error_msg, polygon_table
132
 
133
- # Valid IDs - select polygons
134
  updated_data = example_data.copy()
135
  updated_data["selected_polygons"] = valid_selected_ids
136
 
137
- # Create highlighted dataframe data
138
  highlighted_table = []
139
  for row in polygon_table:
140
- if row[0] in valid_selected_ids: # If this is a selected row
141
- # Add highlighting markers to the selected row
142
- highlighted_row = [f"→ {row[0]} ←", f"→ {row[1]} ←", f"→ {row[2]} ←", f"→ {row[3]} ←", f"→ {row[4]} ←", f"→ {row[5]} ←"]
 
 
 
 
 
 
143
  highlighted_table.append(highlighted_row)
144
  else:
145
  highlighted_table.append(row)
146
 
147
- # Create info text
148
  info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
149
  for selected_id in valid_selected_ids:
150
- selected_polygon = next((p for p in example_data["polygons"] if p["id"] == selected_id), None)
 
 
151
  if selected_polygon:
152
- info_lines.append(f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px")
 
 
153
 
154
  if invalid_ids:
155
  info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
@@ -157,16 +265,61 @@ def select_polygon_by_id(polygon_id):
157
  info_text = "\n".join(info_lines)
158
  return updated_data, info_text, highlighted_table
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  with gr.Blocks() as demo:
161
  gr.Markdown("""
162
  # PolygonAnnotator - Advanced Interactive Demo
163
 
164
- This demo showcases all the features of the PolygonAnnotator component:
165
- - **Click** on polygons to select/deselect them
 
 
166
  - **Ctrl/Cmd+Click** for multiple selection
167
  - **Click dataframe rows** to select polygons
168
  - **Enter polygon IDs** manually in the textbox
 
 
 
 
 
 
 
 
 
 
169
  - **Clear button** to deselect all
 
170
  """)
171
 
172
  with gr.Row():
@@ -181,24 +334,31 @@ with gr.Blocks() as demo:
181
  selected_info = gr.Textbox(
182
  label="Selected Polygon Information",
183
  lines=5,
184
- value="Click on a polygon to see its information"
185
  )
186
 
187
  polygon_dataframe = gr.Dataframe(
188
  value=polygon_table,
189
  headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
190
  label="Polygon Data (Click rows to select)",
191
- datatype=[Literal["str", "str", "str", "number", "number", "number"]],
192
- interactive=True
193
  )
194
 
195
  clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
196
 
 
 
 
 
 
 
 
 
197
  with gr.Row():
198
  polygon_id_input = gr.Textbox(
199
  label="Select by Polygon ID(s)",
200
  placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
201
- scale=3
202
  )
203
  select_button = gr.Button("Select", variant="primary", scale=1)
204
 
@@ -210,6 +370,7 @@ with gr.Blocks() as demo:
210
  - Variable **stroke width** (0.5px to 1.5px)
211
  - Custom **stroke opacity** for borders
212
  - Enhanced appearance when selected
 
213
 
214
  #### 🖱️ Interaction Methods
215
  1. **Direct Click**: Click polygons in the viewer
@@ -217,6 +378,7 @@ with gr.Blocks() as demo:
217
  3. **Dataframe**: Click table rows
218
  4. **Text Input**: Type polygon IDs
219
  5. **Clear All**: Reset selection
 
220
 
221
  #### 📝 Polygon IDs
222
  - `date_line` - Red header area
@@ -229,37 +391,44 @@ with gr.Blocks() as demo:
229
  - Selected polygons have increased opacity
230
  - Use comma-separated IDs for batch selection
231
  - Click selected polygons to deselect them
 
232
  """)
233
 
234
  # Handle selection events
235
  poly_annotator.select(
236
  process_viewer_selection,
237
  inputs=[poly_annotator],
238
- outputs=[selected_info, polygon_dataframe]
239
  )
240
 
241
  polygon_dataframe.select(
242
  process_dataframe_selection,
243
  inputs=[polygon_dataframe],
244
- outputs=[poly_annotator, selected_info]
245
  )
246
 
247
  clear_button.click(
248
- clear_selection,
249
- outputs=[poly_annotator, selected_info, polygon_dataframe]
250
  )
251
 
252
  select_button.click(
253
  select_polygon_by_id,
254
  inputs=[polygon_id_input],
255
- outputs=[poly_annotator, selected_info, polygon_dataframe]
256
  )
257
 
258
  # Also allow Enter key in textbox
259
  polygon_id_input.submit(
260
  select_polygon_by_id,
261
  inputs=[polygon_id_input],
262
- outputs=[poly_annotator, selected_info, polygon_dataframe]
 
 
 
 
 
 
 
263
  )
264
 
265
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  from gradio_polygonannotator import PolygonAnnotator
 
3
 
4
  example_data = {
5
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
6
  "polygons": [
7
  {
8
+ "id": "complex_header",
9
+ "coordinates": [
10
+ [150, 120],
11
+ [200, 100],
12
+ [350, 110],
13
+ [450, 95],
14
+ [600, 120],
15
+ [620, 180],
16
+ [580, 220],
17
+ [400, 240],
18
+ [250, 235],
19
+ [180, 200],
20
+ [140, 160],
21
+ ],
22
  "color": "#FF0000",
23
+ "mask_opacity": 0.3,
24
+ "stroke_width": 1.2,
25
+ "stroke_opacity": 0.8,
26
+ "selected_mask_opacity": 0.6,
27
+ "selected_stroke_opacity": 1.0,
28
+ "display_text": "Complex Header",
29
+ "display_font_size": 14,
30
+ "display_text_color": "#FFFFFF",
31
  },
32
  {
33
+ "id": "overlapping_ribbon",
34
+ "coordinates": [
35
+ [300, 150],
36
+ [550, 160],
37
+ [580, 200],
38
+ [650, 220],
39
+ [680, 280],
40
+ [620, 320],
41
+ [500, 340],
42
+ [350, 330],
43
+ [200, 310],
44
+ [180, 270],
45
+ [220, 220],
46
+ [280, 190],
47
+ ],
48
  "color": "#00FF00",
49
+ "mask_opacity": 0.25,
50
+ "stroke_width": 1.5,
51
+ "stroke_opacity": 0.7,
52
+ "selected_mask_opacity": 0.5,
53
+ "selected_stroke_opacity": 1.0,
54
+ "display_text": "Overlapping Ribbon",
55
+ "display_font_size": 14,
56
+ "display_text_color": "#000000",
57
+ },
58
+ {
59
+ "id": "irregular_content",
60
+ "coordinates": [
61
+ [120, 380],
62
+ [250, 350],
63
+ [400, 370],
64
+ [550, 390],
65
+ [720, 420],
66
+ [750, 500],
67
+ [780, 650],
68
+ [720, 800],
69
+ [650, 920],
70
+ [500, 950],
71
+ [300, 940],
72
+ [150, 900],
73
+ [80, 750],
74
+ [90, 600],
75
+ [110, 450],
76
+ ],
77
+ "color": "#0000FF",
78
  "mask_opacity": 0.2,
79
+ "stroke_width": 0.8,
80
  "stroke_opacity": 0.6,
81
  "selected_mask_opacity": 0.4,
82
+ "selected_stroke_opacity": 0.9,
83
+ "display_text": "Irregular Content",
84
+ "display_font_size": 14,
85
+ "display_text_color": "#FFFF00",
86
  },
87
  {
88
+ "id": "star_shaped",
89
+ "coordinates": [
90
+ [400, 850],
91
+ [450, 900],
92
+ [520, 910],
93
+ [480, 970],
94
+ [500, 1040],
95
+ [400, 1000],
96
+ [300, 1040],
97
+ [320, 970],
98
+ [280, 910],
99
+ [350, 900],
100
+ ],
101
+ "color": "#FF00FF",
102
+ "mask_opacity": 0.35,
103
+ "stroke_width": 2.0,
104
+ "stroke_opacity": 0.9,
105
+ "selected_mask_opacity": 0.7,
106
+ "selected_stroke_opacity": 1.0,
107
+ "display_text": "Star Shape",
108
+ "display_font_size": 14,
109
+ "display_text_color": "#FFFFFF",
110
  },
111
  {
112
+ "id": "overlapping_triangle",
113
+ "coordinates": [
114
+ [480, 600],
115
+ [650, 750],
116
+ [720, 850],
117
+ [600, 920],
118
+ [450, 880],
119
+ [350, 800],
120
+ [380, 700],
121
+ ],
122
+ "color": "#FFAA00",
123
+ "mask_opacity": 0.28,
124
+ "stroke_width": 1.8,
125
+ "stroke_opacity": 0.75,
126
+ "selected_mask_opacity": 0.55,
127
+ "selected_stroke_opacity": 1.0,
128
+ "display_text": "Triangle",
129
+ "display_font_size": 14,
130
+ "display_text_color": "#000000",
131
+ },
132
+ ],
133
  }
134
 
 
135
  polygon_table = [
136
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
137
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
138
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
139
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
140
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
141
  ]
142
 
143
+
144
  def process_viewer_selection(data, evt: gr.SelectData):
 
145
  if evt.value and data:
146
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
147
 
 
148
  highlighted_table = []
149
  for row in polygon_table:
150
+ if row[0] in selected_ids:
151
+ highlighted_row = [
152
+ f"→ {row[0]} ←",
153
+ f"→ {row[1]} ←",
154
+ f"→ {row[2]} ←",
155
+ f"→ {row[3]} ←",
156
+ f"→ {row[4]} ←",
157
+ f"→ {row[5]} ←",
158
+ ]
159
  highlighted_table.append(highlighted_row)
160
  else:
161
  highlighted_table.append(row)
162
 
 
163
  info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
164
  for selected_id in selected_ids:
165
+ selected_polygon = next(
166
+ (p for p in data["polygons"] if p["id"] == selected_id), None
167
+ )
168
  if selected_polygon:
169
+ info_lines.append(
170
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
171
+ )
172
 
173
  info_text = "\n".join(info_lines)
174
  return info_text, highlighted_table
175
 
176
  return "No polygons selected", polygon_table
177
 
178
+
179
  def process_dataframe_selection(selected_data, evt: gr.SelectData):
 
180
  if evt.index is not None and evt.index[0] < len(polygon_table):
181
  selected_row = polygon_table[evt.index[0]]
182
  polygon_id = selected_row[0]
183
 
 
184
  updated_data = example_data.copy()
185
  updated_data["selected_polygons"] = [polygon_id]
186
 
187
+ highlighted_table = []
188
+ for i, row in enumerate(polygon_table):
189
+ if i == evt.index[0]:
190
+ highlighted_row = [
191
+ f"→ {row[0]} ←",
192
+ f"→ {row[1]} ←",
193
+ f"→ {row[2]} ←",
194
+ f"→ {row[3]} ←",
195
+ f"→ {row[4]} ←",
196
+ f"→ {row[5]} ←",
197
+ ]
198
+ highlighted_table.append(highlighted_row)
199
+ else:
200
+ highlighted_table.append(row)
201
+
202
  info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
203
+ return updated_data, info_text, highlighted_table
204
 
 
205
  updated_data = example_data.copy()
206
  updated_data["selected_polygons"] = []
207
+ return updated_data, "No polygons selected", polygon_table
208
+
209
 
210
  def clear_selection():
 
211
  updated_data = example_data.copy()
212
  updated_data["selected_polygons"] = []
213
  return updated_data, "No polygons selected", polygon_table
214
 
215
+
216
  def select_polygon_by_id(polygon_id):
 
217
  if not polygon_id or polygon_id.strip() == "":
 
218
  updated_data = example_data.copy()
219
  updated_data["selected_polygons"] = []
220
  return updated_data, "No polygons selected", polygon_table
221
 
 
222
  polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
223
  valid_ids = [p["id"] for p in example_data["polygons"]]
224
 
 
225
  valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
226
  invalid_ids = [id for id in polygon_ids if id not in valid_ids]
227
 
228
  if not valid_selected_ids:
 
229
  updated_data = example_data.copy()
230
  updated_data["selected_polygons"] = []
231
  error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
232
  return updated_data, error_msg, polygon_table
233
 
 
234
  updated_data = example_data.copy()
235
  updated_data["selected_polygons"] = valid_selected_ids
236
 
 
237
  highlighted_table = []
238
  for row in polygon_table:
239
+ if row[0] in valid_selected_ids:
240
+ highlighted_row = [
241
+ f"→ {row[0]} ←",
242
+ f"→ {row[1]} ←",
243
+ f"→ {row[2]} ←",
244
+ f"→ {row[3]} ←",
245
+ f"→ {row[4]} ←",
246
+ f"→ {row[5]} ←",
247
+ ]
248
  highlighted_table.append(highlighted_row)
249
  else:
250
  highlighted_table.append(row)
251
 
 
252
  info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
253
  for selected_id in valid_selected_ids:
254
+ selected_polygon = next(
255
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
256
+ )
257
  if selected_polygon:
258
+ info_lines.append(
259
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
260
+ )
261
 
262
  if invalid_ids:
263
  info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
 
265
  info_text = "\n".join(info_lines)
266
  return updated_data, info_text, highlighted_table
267
 
268
+
269
+ def toggle_text_display(current_data, show_text):
270
+ if not current_data:
271
+ return current_data
272
+
273
+ updated_data = current_data.copy()
274
+ updated_data["polygons"] = []
275
+
276
+ for polygon in current_data.get("polygons", []):
277
+ updated_polygon = polygon.copy()
278
+ if not show_text:
279
+ updated_polygon["display_font_size"] = 0
280
+ else:
281
+ original_polygon = next(
282
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
283
+ )
284
+ if original_polygon:
285
+ updated_polygon["display_text"] = original_polygon.get(
286
+ "display_text", polygon["id"]
287
+ )
288
+ updated_polygon["display_font_size"] = original_polygon.get(
289
+ "display_font_size", 14
290
+ )
291
+ updated_polygon["display_text_color"] = original_polygon.get(
292
+ "display_text_color", "#000000"
293
+ )
294
+
295
+ updated_data["polygons"].append(updated_polygon)
296
+
297
+ return updated_data
298
+
299
+
300
  with gr.Blocks() as demo:
301
  gr.Markdown("""
302
  # PolygonAnnotator - Advanced Interactive Demo
303
 
304
+ ## 🎮 Controls & Hotkeys
305
+
306
+ ### Selection
307
+ - **Click** on polygons or text labels to select/deselect
308
  - **Ctrl/Cmd+Click** for multiple selection
309
  - **Click dataframe rows** to select polygons
310
  - **Enter polygon IDs** manually in the textbox
311
+
312
+ ### Navigation
313
+ - **Mouse Wheel** - Zoom in/out at cursor position
314
+ - **+/=** - Zoom in (10%)
315
+ - **-** - Zoom out (10%)
316
+ - **Ctrl/Cmd+0** - Reset view to original
317
+ - **Arrow Keys** - Pan view (↑↓←→)
318
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
319
+
320
+ ### Features
321
  - **Clear button** to deselect all
322
+ - **Toggle text display** checkbox for polygon labels
323
  """)
324
 
325
  with gr.Row():
 
334
  selected_info = gr.Textbox(
335
  label="Selected Polygon Information",
336
  lines=5,
337
+ value="Click on a polygon to see its information",
338
  )
339
 
340
  polygon_dataframe = gr.Dataframe(
341
  value=polygon_table,
342
  headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
343
  label="Polygon Data (Click rows to select)",
344
+ interactive=True,
 
345
  )
346
 
347
  clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
348
 
349
+ # Add text display toggle
350
+ with gr.Row():
351
+ show_text_checkbox = gr.Checkbox(
352
+ label="Show Polygon Text",
353
+ value=True,
354
+ info="Toggle text display on polygons",
355
+ )
356
+
357
  with gr.Row():
358
  polygon_id_input = gr.Textbox(
359
  label="Select by Polygon ID(s)",
360
  placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
361
+ scale=3,
362
  )
363
  select_button = gr.Button("Select", variant="primary", scale=1)
364
 
 
370
  - Variable **stroke width** (0.5px to 1.5px)
371
  - Custom **stroke opacity** for borders
372
  - Enhanced appearance when selected
373
+ - **Text labels** with customizable colors and sizes
374
 
375
  #### 🖱️ Interaction Methods
376
  1. **Direct Click**: Click polygons in the viewer
 
378
  3. **Dataframe**: Click table rows
379
  4. **Text Input**: Type polygon IDs
380
  5. **Clear All**: Reset selection
381
+ 6. **Text Toggle**: Show/hide polygon labels
382
 
383
  #### 📝 Polygon IDs
384
  - `date_line` - Red header area
 
391
  - Selected polygons have increased opacity
392
  - Use comma-separated IDs for batch selection
393
  - Click selected polygons to deselect them
394
+ - Toggle text display to see labels on polygons
395
  """)
396
 
397
  # Handle selection events
398
  poly_annotator.select(
399
  process_viewer_selection,
400
  inputs=[poly_annotator],
401
+ outputs=[selected_info, polygon_dataframe],
402
  )
403
 
404
  polygon_dataframe.select(
405
  process_dataframe_selection,
406
  inputs=[polygon_dataframe],
407
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
408
  )
409
 
410
  clear_button.click(
411
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
 
412
  )
413
 
414
  select_button.click(
415
  select_polygon_by_id,
416
  inputs=[polygon_id_input],
417
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
418
  )
419
 
420
  # Also allow Enter key in textbox
421
  polygon_id_input.submit(
422
  select_polygon_by_id,
423
  inputs=[polygon_id_input],
424
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
425
+ )
426
+
427
+ # Handle text display toggle
428
+ show_text_checkbox.change(
429
+ toggle_text_display,
430
+ inputs=[poly_annotator, show_text_checkbox],
431
+ outputs=[poly_annotator],
432
  )
433
 
434
  if __name__ == "__main__":
css.css ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html {
2
+ font-family: Inter;
3
+ font-size: 16px;
4
+ font-weight: 400;
5
+ line-height: 1.5;
6
+ -webkit-text-size-adjust: 100%;
7
+ background: #fff;
8
+ color: #323232;
9
+ -webkit-font-smoothing: antialiased;
10
+ -moz-osx-font-smoothing: grayscale;
11
+ text-rendering: optimizeLegibility;
12
+ }
13
+
14
+ :root {
15
+ --space: 1;
16
+ --vspace: calc(var(--space) * 1rem);
17
+ --vspace-0: calc(3 * var(--space) * 1rem);
18
+ --vspace-1: calc(2 * var(--space) * 1rem);
19
+ --vspace-2: calc(1.5 * var(--space) * 1rem);
20
+ --vspace-3: calc(0.5 * var(--space) * 1rem);
21
+ }
22
+
23
+ .app {
24
+ max-width: 748px !important;
25
+ }
26
+
27
+ .prose p {
28
+ margin: var(--vspace) 0;
29
+ line-height: var(--vspace * 2);
30
+ font-size: 1rem;
31
+ }
32
+
33
+ code {
34
+ font-family: "Inconsolata", sans-serif;
35
+ font-size: 16px;
36
+ }
37
+
38
+ h1,
39
+ h1 code {
40
+ font-weight: 400;
41
+ line-height: calc(2.5 / var(--space) * var(--vspace));
42
+ }
43
+
44
+ h1 code {
45
+ background: none;
46
+ border: none;
47
+ letter-spacing: 0.05em;
48
+ padding-bottom: 5px;
49
+ position: relative;
50
+ padding: 0;
51
+ }
52
+
53
+ h2 {
54
+ margin: var(--vspace-1) 0 var(--vspace-2) 0;
55
+ line-height: 1em;
56
+ }
57
+
58
+ h3,
59
+ h3 code {
60
+ margin: var(--vspace-1) 0 var(--vspace-2) 0;
61
+ line-height: 1em;
62
+ }
63
+
64
+ h4,
65
+ h5,
66
+ h6 {
67
+ margin: var(--vspace-3) 0 var(--vspace-3) 0;
68
+ line-height: var(--vspace);
69
+ }
70
+
71
+ .bigtitle,
72
+ h1,
73
+ h1 code {
74
+ font-size: calc(8px * 4.5);
75
+ word-break: break-word;
76
+ }
77
+
78
+ .title,
79
+ h2,
80
+ h2 code {
81
+ font-size: calc(8px * 3.375);
82
+ font-weight: lighter;
83
+ word-break: break-word;
84
+ border: none;
85
+ background: none;
86
+ }
87
+
88
+ .subheading1,
89
+ h3,
90
+ h3 code {
91
+ font-size: calc(8px * 1.8);
92
+ font-weight: 600;
93
+ border: none;
94
+ background: none;
95
+ letter-spacing: 0.1em;
96
+ text-transform: uppercase;
97
+ }
98
+
99
+ h2 code {
100
+ padding: 0;
101
+ position: relative;
102
+ letter-spacing: 0.05em;
103
+ }
104
+
105
+ blockquote {
106
+ font-size: calc(8px * 1.1667);
107
+ font-style: italic;
108
+ line-height: calc(1.1667 * var(--vspace));
109
+ margin: var(--vspace-2) var(--vspace-2);
110
+ }
111
+
112
+ .subheading2,
113
+ h4 {
114
+ font-size: calc(8px * 1.4292);
115
+ text-transform: uppercase;
116
+ font-weight: 600;
117
+ }
118
+
119
+ .subheading3,
120
+ h5 {
121
+ font-size: calc(8px * 1.2917);
122
+ line-height: calc(1.2917 * var(--vspace));
123
+
124
+ font-weight: lighter;
125
+ text-transform: uppercase;
126
+ letter-spacing: 0.15em;
127
+ }
128
+
129
+ h6 {
130
+ font-size: calc(8px * 1.1667);
131
+ font-size: 1.1667em;
132
+ font-weight: normal;
133
+ font-style: italic;
134
+ font-family: "le-monde-livre-classic-byol", serif !important;
135
+ letter-spacing: 0px !important;
136
+ }
137
+
138
+ #start .md > *:first-child {
139
+ margin-top: 0;
140
+ }
141
+
142
+ h2 + h3 {
143
+ margin-top: 0;
144
+ }
145
+
146
+ .md hr {
147
+ border: none;
148
+ border-top: 1px solid var(--block-border-color);
149
+ margin: var(--vspace-2) 0 var(--vspace-2) 0;
150
+ }
151
+ .prose ul {
152
+ margin: var(--vspace-2) 0 var(--vspace-1) 0;
153
+ }
154
+
155
+ .gap {
156
+ gap: 0;
157
+ }
space.py ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ from app import demo as app
4
+ import os
5
+
6
+ _docs = {'PolygonAnnotator': {'description': 'Interactive polygon annotation component for visualizing and selecting polygon regions on images.', 'members': {'__init__': {'value': {'type': 'dict | None', 'default': 'None', 'description': None}, 'label': {'type': 'str | I18nData | None', 'default': 'None', 'description': None}, 'every': {'type': 'Timer | float | None', 'default': 'None', 'description': None}, 'inputs': {'type': 'Component | Sequence[Component] | set[Component] | None', 'default': 'None', 'description': None}, 'show_label': {'type': 'bool | None', 'default': 'None', 'description': None}, 'show_download_button': {'type': 'bool', 'default': 'True', 'description': None}, 'height': {'type': 'int | str | None', 'default': 'None', 'description': None}, 'width': {'type': 'int | str | None', 'default': 'None', 'description': None}, 'container': {'type': 'bool', 'default': 'True', 'description': None}, 'scale': {'type': 'int | None', 'default': 'None', 'description': None}, 'min_width': {'type': 'int', 'default': '160', 'description': None}, 'interactive': {'type': 'bool | None', 'default': 'None', 'description': None}, 'visible': {'type': 'bool | Literal["hidden"]', 'default': 'True', 'description': None}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': None}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': None}, 'render': {'type': 'bool', 'default': 'True', 'description': None}, 'key': {'type': 'int | str | tuple[int | str, ...] | None', 'default': 'None', 'description': None}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': '"value"', 'description': None}}, 'postprocess': {'value': {'type': 'dict | None', 'description': "The output data received by the component from the user's function in the backend."}}, 'preprocess': {'return': {'type': 'dict | None', 'description': "The preprocessed input data sent to the user's function in the backend."}, 'value': None}}, 'events': {'clear': {'type': None, 'default': None, 'description': 'This listener is triggered when the user clears the PolygonAnnotator using the clear button for the component.'}, 'change': {'type': None, 'default': None, 'description': 'Triggered when the value of the PolygonAnnotator changes either because of user input (e.g. a user types in a textbox) OR because of a function update (e.g. an image receives a value from the output of an event trigger). See `.input()` for a listener that is only triggered by user input.'}, 'upload': {'type': None, 'default': None, 'description': 'This listener is triggered when the user uploads a file into the PolygonAnnotator.'}, 'select': {'type': None, 'default': None, 'description': 'Event listener for when the user selects or deselects the PolygonAnnotator. Uses event data gradio.SelectData to carry `value` referring to the label of the PolygonAnnotator, and `selected` to refer to state of the PolygonAnnotator. See EventData documentation on how to use this event data'}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'PolygonAnnotator': []}}}
7
+
8
+ abs_path = os.path.join(os.path.dirname(__file__), "css.css")
9
+
10
+ with gr.Blocks(
11
+ css=abs_path,
12
+ theme=gr.themes.Default(
13
+ font_mono=[
14
+ gr.themes.GoogleFont("Inconsolata"),
15
+ "monospace",
16
+ ],
17
+ ),
18
+ ) as demo:
19
+ gr.Markdown(
20
+ """
21
+ # `gradio_polygonannotator`
22
+
23
+ <div style="display: flex; gap: 7px;">
24
+ <a href="https://pypi.org/project/gradio_polygonannotator/" target="_blank"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/gradio_polygonannotator"></a> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
25
+ </div>
26
+
27
+ Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
28
+ """, elem_classes=["md-custom"], header_links=True)
29
+ app.render()
30
+ gr.Markdown(
31
+ """
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install gradio_polygonannotator
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```python
41
+ import gradio as gr
42
+ from gradio_polygonannotator import PolygonAnnotator
43
+
44
+ example_data = {
45
+ "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
46
+ "polygons": [
47
+ {
48
+ "id": "complex_header",
49
+ "coordinates": [
50
+ [150, 120],
51
+ [200, 100],
52
+ [350, 110],
53
+ [450, 95],
54
+ [600, 120],
55
+ [620, 180],
56
+ [580, 220],
57
+ [400, 240],
58
+ [250, 235],
59
+ [180, 200],
60
+ [140, 160],
61
+ ],
62
+ "color": "#FF0000",
63
+ "mask_opacity": 0.3,
64
+ "stroke_width": 1.2,
65
+ "stroke_opacity": 0.8,
66
+ "selected_mask_opacity": 0.6,
67
+ "selected_stroke_opacity": 1.0,
68
+ "display_text": "Complex Header",
69
+ "display_font_size": 14,
70
+ "display_text_color": "#FFFFFF",
71
+ },
72
+ {
73
+ "id": "overlapping_ribbon",
74
+ "coordinates": [
75
+ [300, 150],
76
+ [550, 160],
77
+ [580, 200],
78
+ [650, 220],
79
+ [680, 280],
80
+ [620, 320],
81
+ [500, 340],
82
+ [350, 330],
83
+ [200, 310],
84
+ [180, 270],
85
+ [220, 220],
86
+ [280, 190],
87
+ ],
88
+ "color": "#00FF00",
89
+ "mask_opacity": 0.25,
90
+ "stroke_width": 1.5,
91
+ "stroke_opacity": 0.7,
92
+ "selected_mask_opacity": 0.5,
93
+ "selected_stroke_opacity": 1.0,
94
+ "display_text": "Overlapping Ribbon",
95
+ "display_font_size": 14,
96
+ "display_text_color": "#000000",
97
+ },
98
+ {
99
+ "id": "irregular_content",
100
+ "coordinates": [
101
+ [120, 380],
102
+ [250, 350],
103
+ [400, 370],
104
+ [550, 390],
105
+ [720, 420],
106
+ [750, 500],
107
+ [780, 650],
108
+ [720, 800],
109
+ [650, 920],
110
+ [500, 950],
111
+ [300, 940],
112
+ [150, 900],
113
+ [80, 750],
114
+ [90, 600],
115
+ [110, 450],
116
+ ],
117
+ "color": "#0000FF",
118
+ "mask_opacity": 0.2,
119
+ "stroke_width": 0.8,
120
+ "stroke_opacity": 0.6,
121
+ "selected_mask_opacity": 0.4,
122
+ "selected_stroke_opacity": 0.9,
123
+ "display_text": "Irregular Content",
124
+ "display_font_size": 14,
125
+ "display_text_color": "#FFFF00",
126
+ },
127
+ {
128
+ "id": "star_shaped",
129
+ "coordinates": [
130
+ [400, 850],
131
+ [450, 900],
132
+ [520, 910],
133
+ [480, 970],
134
+ [500, 1040],
135
+ [400, 1000],
136
+ [300, 1040],
137
+ [320, 970],
138
+ [280, 910],
139
+ [350, 900],
140
+ ],
141
+ "color": "#FF00FF",
142
+ "mask_opacity": 0.35,
143
+ "stroke_width": 2.0,
144
+ "stroke_opacity": 0.9,
145
+ "selected_mask_opacity": 0.7,
146
+ "selected_stroke_opacity": 1.0,
147
+ "display_text": "Star Shape",
148
+ "display_font_size": 14,
149
+ "display_text_color": "#FFFFFF",
150
+ },
151
+ {
152
+ "id": "overlapping_triangle",
153
+ "coordinates": [
154
+ [480, 600],
155
+ [650, 750],
156
+ [720, 850],
157
+ [600, 920],
158
+ [450, 880],
159
+ [350, 800],
160
+ [380, 700],
161
+ ],
162
+ "color": "#FFAA00",
163
+ "mask_opacity": 0.28,
164
+ "stroke_width": 1.8,
165
+ "stroke_opacity": 0.75,
166
+ "selected_mask_opacity": 0.55,
167
+ "selected_stroke_opacity": 1.0,
168
+ "display_text": "Triangle",
169
+ "display_font_size": 14,
170
+ "display_text_color": "#000000",
171
+ },
172
+ ],
173
+ }
174
+
175
+ polygon_table = [
176
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
177
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
178
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
179
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
180
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
181
+ ]
182
+
183
+
184
+ def process_viewer_selection(data, evt: gr.SelectData):
185
+ if evt.value and data:
186
+ selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
187
+
188
+ highlighted_table = []
189
+ for row in polygon_table:
190
+ if row[0] in selected_ids:
191
+ highlighted_row = [
192
+ f"→ {row[0]} ←",
193
+ f"→ {row[1]} ←",
194
+ f"→ {row[2]} ←",
195
+ f"→ {row[3]} ←",
196
+ f"→ {row[4]} ←",
197
+ f"→ {row[5]} ←",
198
+ ]
199
+ highlighted_table.append(highlighted_row)
200
+ else:
201
+ highlighted_table.append(row)
202
+
203
+ info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
204
+ for selected_id in selected_ids:
205
+ selected_polygon = next(
206
+ (p for p in data["polygons"] if p["id"] == selected_id), None
207
+ )
208
+ if selected_polygon:
209
+ info_lines.append(
210
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
211
+ )
212
+
213
+ info_text = "\n".join(info_lines)
214
+ return info_text, highlighted_table
215
+
216
+ return "No polygons selected", polygon_table
217
+
218
+
219
+ def process_dataframe_selection(selected_data, evt: gr.SelectData):
220
+ if evt.index is not None and evt.index[0] < len(polygon_table):
221
+ selected_row = polygon_table[evt.index[0]]
222
+ polygon_id = selected_row[0]
223
+
224
+ updated_data = example_data.copy()
225
+ updated_data["selected_polygons"] = [polygon_id]
226
+
227
+ highlighted_table = []
228
+ for i, row in enumerate(polygon_table):
229
+ if i == evt.index[0]:
230
+ highlighted_row = [
231
+ f"→ {row[0]} ←",
232
+ f"→ {row[1]} ←",
233
+ f"→ {row[2]} ←",
234
+ f"→ {row[3]} ←",
235
+ f"→ {row[4]} ←",
236
+ f"→ {row[5]} ←",
237
+ ]
238
+ highlighted_table.append(highlighted_row)
239
+ else:
240
+ highlighted_table.append(row)
241
+
242
+ info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
243
+ return updated_data, info_text, highlighted_table
244
+
245
+ updated_data = example_data.copy()
246
+ updated_data["selected_polygons"] = []
247
+ return updated_data, "No polygons selected", polygon_table
248
+
249
+
250
+ def clear_selection():
251
+ updated_data = example_data.copy()
252
+ updated_data["selected_polygons"] = []
253
+ return updated_data, "No polygons selected", polygon_table
254
+
255
+
256
+ def select_polygon_by_id(polygon_id):
257
+ if not polygon_id or polygon_id.strip() == "":
258
+ updated_data = example_data.copy()
259
+ updated_data["selected_polygons"] = []
260
+ return updated_data, "No polygons selected", polygon_table
261
+
262
+ polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
263
+ valid_ids = [p["id"] for p in example_data["polygons"]]
264
+
265
+ valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
266
+ invalid_ids = [id for id in polygon_ids if id not in valid_ids]
267
+
268
+ if not valid_selected_ids:
269
+ updated_data = example_data.copy()
270
+ updated_data["selected_polygons"] = []
271
+ error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
272
+ return updated_data, error_msg, polygon_table
273
+
274
+ updated_data = example_data.copy()
275
+ updated_data["selected_polygons"] = valid_selected_ids
276
+
277
+ highlighted_table = []
278
+ for row in polygon_table:
279
+ if row[0] in valid_selected_ids:
280
+ highlighted_row = [
281
+ f"→ {row[0]} ←",
282
+ f"→ {row[1]} ←",
283
+ f"→ {row[2]} ←",
284
+ f"→ {row[3]} ←",
285
+ f"→ {row[4]} ←",
286
+ f"→ {row[5]} ←",
287
+ ]
288
+ highlighted_table.append(highlighted_row)
289
+ else:
290
+ highlighted_table.append(row)
291
+
292
+ info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
293
+ for selected_id in valid_selected_ids:
294
+ selected_polygon = next(
295
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
296
+ )
297
+ if selected_polygon:
298
+ info_lines.append(
299
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
300
+ )
301
+
302
+ if invalid_ids:
303
+ info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
304
+
305
+ info_text = "\n".join(info_lines)
306
+ return updated_data, info_text, highlighted_table
307
+
308
+
309
+ def toggle_text_display(current_data, show_text):
310
+ if not current_data:
311
+ return current_data
312
+
313
+ updated_data = current_data.copy()
314
+ updated_data["polygons"] = []
315
+
316
+ for polygon in current_data.get("polygons", []):
317
+ updated_polygon = polygon.copy()
318
+ if not show_text:
319
+ updated_polygon["display_font_size"] = 0
320
+ else:
321
+ original_polygon = next(
322
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
323
+ )
324
+ if original_polygon:
325
+ updated_polygon["display_text"] = original_polygon.get(
326
+ "display_text", polygon["id"]
327
+ )
328
+ updated_polygon["display_font_size"] = original_polygon.get(
329
+ "display_font_size", 14
330
+ )
331
+ updated_polygon["display_text_color"] = original_polygon.get(
332
+ "display_text_color", "#000000"
333
+ )
334
+
335
+ updated_data["polygons"].append(updated_polygon)
336
+
337
+ return updated_data
338
+
339
+
340
+ with gr.Blocks() as demo:
341
+ gr.Markdown(\"\"\"
342
+ # PolygonAnnotator - Advanced Interactive Demo
343
+
344
+ ## 🎮 Controls & Hotkeys
345
+
346
+ ### Selection
347
+ - **Click** on polygons or text labels to select/deselect
348
+ - **Ctrl/Cmd+Click** for multiple selection
349
+ - **Click dataframe rows** to select polygons
350
+ - **Enter polygon IDs** manually in the textbox
351
+
352
+ ### Navigation
353
+ - **Mouse Wheel** - Zoom in/out at cursor position
354
+ - **+/=** - Zoom in (10%)
355
+ - **-** - Zoom out (10%)
356
+ - **Ctrl/Cmd+0** - Reset view to original
357
+ - **Arrow Keys** - Pan view (↑↓��→)
358
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
359
+
360
+ ### Features
361
+ - **Clear button** to deselect all
362
+ - **Toggle text display** checkbox for polygon labels
363
+ \"\"\")
364
+
365
+ with gr.Row():
366
+ with gr.Column(scale=2):
367
+ poly_annotator = PolygonAnnotator(
368
+ value=example_data,
369
+ label="Document with Interactive Polygon Annotations",
370
+ height=600,
371
+ )
372
+
373
+ with gr.Column(scale=1):
374
+ selected_info = gr.Textbox(
375
+ label="Selected Polygon Information",
376
+ lines=5,
377
+ value="Click on a polygon to see its information",
378
+ )
379
+
380
+ polygon_dataframe = gr.Dataframe(
381
+ value=polygon_table,
382
+ headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
383
+ label="Polygon Data (Click rows to select)",
384
+ interactive=True,
385
+ )
386
+
387
+ clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
388
+
389
+ # Add text display toggle
390
+ with gr.Row():
391
+ show_text_checkbox = gr.Checkbox(
392
+ label="Show Polygon Text",
393
+ value=True,
394
+ info="Toggle text display on polygons",
395
+ )
396
+
397
+ with gr.Row():
398
+ polygon_id_input = gr.Textbox(
399
+ label="Select by Polygon ID(s)",
400
+ placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
401
+ scale=3,
402
+ )
403
+ select_button = gr.Button("Select", variant="primary", scale=1)
404
+
405
+ gr.Markdown(\"\"\"
406
+ ### Features Demonstrated
407
+
408
+ #### 🎨 Visual Customization
409
+ - Different **mask opacity** for each polygon fill
410
+ - Variable **stroke width** (0.5px to 1.5px)
411
+ - Custom **stroke opacity** for borders
412
+ - Enhanced appearance when selected
413
+ - **Text labels** with customizable colors and sizes
414
+
415
+ #### 🖱️ Interaction Methods
416
+ 1. **Direct Click**: Click polygons in the viewer
417
+ 2. **Multi-Selection**: Ctrl/Cmd+Click for multiple
418
+ 3. **Dataframe**: Click table rows
419
+ 4. **Text Input**: Type polygon IDs
420
+ 5. **Clear All**: Reset selection
421
+ 6. **Text Toggle**: Show/hide polygon labels
422
+
423
+ #### 📝 Polygon IDs
424
+ - `date_line` - Red header area
425
+ - `salutation` - Green greeting section
426
+ - `main_text_block` - Blue main content
427
+ - `closing_signature` - Yellow signature area
428
+
429
+ #### 💡 Tips
430
+ - Hover over polygons for visual feedback
431
+ - Selected polygons have increased opacity
432
+ - Use comma-separated IDs for batch selection
433
+ - Click selected polygons to deselect them
434
+ - Toggle text display to see labels on polygons
435
+ \"\"\")
436
+
437
+ # Handle selection events
438
+ poly_annotator.select(
439
+ process_viewer_selection,
440
+ inputs=[poly_annotator],
441
+ outputs=[selected_info, polygon_dataframe],
442
+ )
443
+
444
+ polygon_dataframe.select(
445
+ process_dataframe_selection,
446
+ inputs=[polygon_dataframe],
447
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
448
+ )
449
+
450
+ clear_button.click(
451
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
452
+ )
453
+
454
+ select_button.click(
455
+ select_polygon_by_id,
456
+ inputs=[polygon_id_input],
457
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
458
+ )
459
+
460
+ # Also allow Enter key in textbox
461
+ polygon_id_input.submit(
462
+ select_polygon_by_id,
463
+ inputs=[polygon_id_input],
464
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
465
+ )
466
+
467
+ # Handle text display toggle
468
+ show_text_checkbox.change(
469
+ toggle_text_display,
470
+ inputs=[poly_annotator, show_text_checkbox],
471
+ outputs=[poly_annotator],
472
+ )
473
+
474
+ if __name__ == "__main__":
475
+ demo.launch()
476
+
477
+ ```
478
+ """, elem_classes=["md-custom"], header_links=True)
479
+
480
+
481
+ gr.Markdown("""
482
+ ## `PolygonAnnotator`
483
+
484
+ ### Initialization
485
+ """, elem_classes=["md-custom"], header_links=True)
486
+
487
+ gr.ParamViewer(value=_docs["PolygonAnnotator"]["members"]["__init__"], linkify=[])
488
+
489
+
490
+ gr.Markdown("### Events")
491
+ gr.ParamViewer(value=_docs["PolygonAnnotator"]["events"], linkify=['Event'])
492
+
493
+
494
+
495
+
496
+ gr.Markdown("""
497
+
498
+ ### User function
499
+
500
+ The impact on the users predict function varies depending on whether the component is used as an input or output for an event (or both).
501
+
502
+ - When used as an Input, the component only impacts the input signature of the user function.
503
+ - When used as an output, the component only impacts the return signature of the user function.
504
+
505
+ The code snippet below is accurate in cases where the component is used as both an input and an output.
506
+
507
+ - **As input:** Is passed, the preprocessed input data sent to the user's function in the backend.
508
+ - **As output:** Should return, the output data received by the component from the user's function in the backend.
509
+
510
+ ```python
511
+ def predict(
512
+ value: dict | None
513
+ ) -> dict | None:
514
+ return value
515
+ ```
516
+ """, elem_classes=["md-custom", "PolygonAnnotator-user-fn"], header_links=True)
517
+
518
+
519
+
520
+
521
+ demo.load(None, js=r"""function() {
522
+ const refs = {};
523
+ const user_fn_refs = {
524
+ PolygonAnnotator: [], };
525
+ requestAnimationFrame(() => {
526
+
527
+ Object.entries(user_fn_refs).forEach(([key, refs]) => {
528
+ if (refs.length > 0) {
529
+ const el = document.querySelector(`.${key}-user-fn`);
530
+ if (!el) return;
531
+ refs.forEach(ref => {
532
+ el.innerHTML = el.innerHTML.replace(
533
+ new RegExp("\\b"+ref+"\\b", "g"),
534
+ `<a href="#h-${ref.toLowerCase()}">${ref}</a>`
535
+ );
536
+ })
537
+ }
538
+ })
539
+
540
+ Object.entries(refs).forEach(([key, refs]) => {
541
+ if (refs.length > 0) {
542
+ const el = document.querySelector(`.${key}`);
543
+ if (!el) return;
544
+ refs.forEach(ref => {
545
+ el.innerHTML = el.innerHTML.replace(
546
+ new RegExp("\\b"+ref+"\\b", "g"),
547
+ `<a href="#h-${ref.toLowerCase()}">${ref}</a>`
548
+ );
549
+ })
550
+ }
551
+ })
552
+ })
553
+ }
554
+
555
+ """)
556
+
557
+ demo.launch()
src/README.md CHANGED
@@ -1,6 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
  # `gradio_polygonannotator`
3
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.1.0%20-%20orange"> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
4
 
5
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
6
 
@@ -16,85 +32,434 @@ pip install gradio_polygonannotator
16
  import gradio as gr
17
  from gradio_polygonannotator import PolygonAnnotator
18
 
19
- # Example with document regions
20
  example_data = {
21
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
22
  "polygons": [
23
  {
24
- "id": "title",
25
- "coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
26
- "color": "#FF6B6B",
27
- "mask_opacity": 0.15,
28
- "stroke_width": 1.0,
 
 
 
 
 
 
 
 
 
 
 
 
29
  "stroke_opacity": 0.8,
 
 
 
 
 
30
  },
31
  {
32
- "id": "paragraph_1",
33
- "coordinates": [[100, 400], [750, 400], [750, 600], [100, 600]],
34
- "color": "#4ECDC4",
35
- "mask_opacity": 0.15,
36
- "stroke_width": 0.7,
37
- "stroke_opacity": 0.6,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  },
39
  {
40
- "id": "paragraph_2",
41
- "coordinates": [[100, 650], [750, 650], [750, 950], [100, 950]],
42
- "color": "#4ECDC4",
43
- "mask_opacity": 0.15,
44
- "stroke_width": 0.7,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  "stroke_opacity": 0.6,
 
 
 
 
 
46
  },
47
  {
48
- "id": "signature",
49
- "coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
50
- "color": "#FFE66D",
51
- "mask_opacity": 0.2,
52
- "stroke_width": 1.5,
53
- "stroke_opacity": 0.8,
54
- }
55
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
- def handle_selection(data, evt: gr.SelectData):
59
- """Handle polygon selection and display info"""
 
 
 
 
 
 
 
 
60
  if evt.value and data:
61
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
62
- info = f"Selected {len(selected_ids)} polygon(s):\n"
63
- for poly_id in selected_ids:
64
- polygon = next((p for p in data["polygons"] if p["id"] == poly_id), None)
65
- if polygon:
66
- info += f"• {poly_id}\n"
67
- return info
68
- return "Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  with gr.Blocks() as demo:
71
  gr.Markdown("""
72
- # PolygonAnnotator - Interactive Polygon Selection
73
-
74
- Click on polygons to select them. Use **Ctrl/Cmd+Click** for multiple selections.
75
- Click selected polygons to deselect.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  """)
77
 
78
  with gr.Row():
79
- with gr.Column(scale=3):
80
- annotator = PolygonAnnotator(
81
  value=example_data,
82
- label="Document with Region Annotations",
83
  height=600,
84
  )
85
 
86
  with gr.Column(scale=1):
87
  selected_info = gr.Textbox(
88
- label="Selected Regions",
89
- lines=6,
90
- value="Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
91
  )
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  # Handle selection events
94
- annotator.select(
95
- handle_selection,
96
- inputs=[annotator],
97
- outputs=[selected_info]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  )
99
 
100
  if __name__ == "__main__":
@@ -126,7 +491,7 @@ dict | None
126
 
127
  </td>
128
  <td align="left"><code>None</code></td>
129
- <td align="left">Dictionary containing 'image' (FileData), 'polygons' (list with id, coordinates, color, opacities),</td>
130
  </tr>
131
 
132
  <tr>
@@ -139,7 +504,7 @@ str | I18nData | None
139
 
140
  </td>
141
  <td align="left"><code>None</code></td>
142
- <td align="left">Component label shown above the annotator.</td>
143
  </tr>
144
 
145
  <tr>
@@ -152,7 +517,7 @@ Timer | float | None
152
 
153
  </td>
154
  <td align="left"><code>None</code></td>
155
- <td align="left">Continuously calls `value` to recalculate it if `value` is a function.</td>
156
  </tr>
157
 
158
  <tr>
@@ -165,7 +530,7 @@ Component | Sequence[Component] | set[Component] | None
165
 
166
  </td>
167
  <td align="left"><code>None</code></td>
168
- <td align="left">Components used as inputs to calculate `value` if it's a function.</td>
169
  </tr>
170
 
171
  <tr>
@@ -178,7 +543,7 @@ bool | None
178
 
179
  </td>
180
  <td align="left"><code>None</code></td>
181
- <td align="left">Whether to display the label.</td>
182
  </tr>
183
 
184
  <tr>
@@ -191,7 +556,7 @@ bool
191
 
192
  </td>
193
  <td align="left"><code>True</code></td>
194
- <td align="left">Whether to show image download button.</td>
195
  </tr>
196
 
197
  <tr>
@@ -204,7 +569,7 @@ int | str | None
204
 
205
  </td>
206
  <td align="left"><code>None</code></td>
207
- <td align="left">Component height in pixels or CSS units.</td>
208
  </tr>
209
 
210
  <tr>
@@ -217,7 +582,7 @@ int | str | None
217
 
218
  </td>
219
  <td align="left"><code>None</code></td>
220
- <td align="left">Component width in pixels or CSS units.</td>
221
  </tr>
222
 
223
  <tr>
@@ -230,7 +595,7 @@ bool
230
 
231
  </td>
232
  <td align="left"><code>True</code></td>
233
- <td align="left">Whether to wrap component in a container with padding.</td>
234
  </tr>
235
 
236
  <tr>
@@ -243,7 +608,7 @@ int | None
243
 
244
  </td>
245
  <td align="left"><code>None</code></td>
246
- <td align="left">Relative size compared to adjacent components.</td>
247
  </tr>
248
 
249
  <tr>
@@ -256,7 +621,7 @@ int
256
 
257
  </td>
258
  <td align="left"><code>160</code></td>
259
- <td align="left">Minimum pixel width before wrapping.</td>
260
  </tr>
261
 
262
  <tr>
@@ -269,7 +634,7 @@ bool | None
269
 
270
  </td>
271
  <td align="left"><code>None</code></td>
272
- <td align="left">Whether users can interact with polygons (selection/deselection).</td>
273
  </tr>
274
 
275
  <tr>
@@ -282,7 +647,7 @@ bool | Literal["hidden"]
282
 
283
  </td>
284
  <td align="left"><code>True</code></td>
285
- <td align="left">Whether component is visible ("hidden" keeps it in DOM but invisible).</td>
286
  </tr>
287
 
288
  <tr>
@@ -295,7 +660,7 @@ str | None
295
 
296
  </td>
297
  <td align="left"><code>None</code></td>
298
- <td align="left">HTML DOM id for CSS targeting.</td>
299
  </tr>
300
 
301
  <tr>
@@ -308,7 +673,7 @@ list[str] | str | None
308
 
309
  </td>
310
  <td align="left"><code>None</code></td>
311
- <td align="left">HTML DOM classes for CSS targeting.</td>
312
  </tr>
313
 
314
  <tr>
@@ -321,7 +686,7 @@ bool
321
 
322
  </td>
323
  <td align="left"><code>True</code></td>
324
- <td align="left">Whether to render the component immediately.</td>
325
  </tr>
326
 
327
  <tr>
@@ -334,7 +699,7 @@ int | str | tuple[int | str, ...] | None
334
 
335
  </td>
336
  <td align="left"><code>None</code></td>
337
- <td align="left">Key for maintaining component identity across re-renders.</td>
338
  </tr>
339
 
340
  <tr>
@@ -347,7 +712,7 @@ list[str] | str | None
347
 
348
  </td>
349
  <td align="left"><code>"value"</code></td>
350
- <td align="left">Parameters preserved across re-renders with same key.</td>
351
  </tr>
352
  </tbody></table>
353
 
@@ -372,8 +737,8 @@ The impact on the users predict function varies depending on whether the compone
372
 
373
  The code snippet below is accurate in cases where the component is used as both an input and an output.
374
 
375
- - **As output:** Is passed, dictionary with image path, polygon data including coordinates, colors, opacities,.
376
- - **As input:** Should return, dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries.
377
 
378
  ```python
379
  def predict(
@@ -381,4 +746,3 @@ The code snippet below is accurate in cases where the component is used as both
381
  ) -> dict | None:
382
  return value
383
  ```
384
-
 
1
+ ---
2
+ tags:
3
+ - gradio-custom-component
4
+ - Polygon
5
+ - viewer
6
+ - annotations
7
+ title: gradio_polygonannotator
8
+ short_description: Polygon viewer tool for Gradio
9
+ colorFrom: blue
10
+ colorTo: yellow
11
+ sdk: gradio
12
+ pinned: false
13
+ app_file: space.py
14
+ emoji: 👀
15
+ sdk_version: 5.46.1
16
+ ---
17
 
18
  # `gradio_polygonannotator`
19
+ <a href="https://pypi.org/project/gradio_polygonannotator/" target="_blank"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/gradio_polygonannotator"></a> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
20
 
21
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
22
 
 
32
  import gradio as gr
33
  from gradio_polygonannotator import PolygonAnnotator
34
 
 
35
  example_data = {
36
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
37
  "polygons": [
38
  {
39
+ "id": "complex_header",
40
+ "coordinates": [
41
+ [150, 120],
42
+ [200, 100],
43
+ [350, 110],
44
+ [450, 95],
45
+ [600, 120],
46
+ [620, 180],
47
+ [580, 220],
48
+ [400, 240],
49
+ [250, 235],
50
+ [180, 200],
51
+ [140, 160],
52
+ ],
53
+ "color": "#FF0000",
54
+ "mask_opacity": 0.3,
55
+ "stroke_width": 1.2,
56
  "stroke_opacity": 0.8,
57
+ "selected_mask_opacity": 0.6,
58
+ "selected_stroke_opacity": 1.0,
59
+ "display_text": "Complex Header",
60
+ "display_font_size": 14,
61
+ "display_text_color": "#FFFFFF",
62
  },
63
  {
64
+ "id": "overlapping_ribbon",
65
+ "coordinates": [
66
+ [300, 150],
67
+ [550, 160],
68
+ [580, 200],
69
+ [650, 220],
70
+ [680, 280],
71
+ [620, 320],
72
+ [500, 340],
73
+ [350, 330],
74
+ [200, 310],
75
+ [180, 270],
76
+ [220, 220],
77
+ [280, 190],
78
+ ],
79
+ "color": "#00FF00",
80
+ "mask_opacity": 0.25,
81
+ "stroke_width": 1.5,
82
+ "stroke_opacity": 0.7,
83
+ "selected_mask_opacity": 0.5,
84
+ "selected_stroke_opacity": 1.0,
85
+ "display_text": "Overlapping Ribbon",
86
+ "display_font_size": 14,
87
+ "display_text_color": "#000000",
88
  },
89
  {
90
+ "id": "irregular_content",
91
+ "coordinates": [
92
+ [120, 380],
93
+ [250, 350],
94
+ [400, 370],
95
+ [550, 390],
96
+ [720, 420],
97
+ [750, 500],
98
+ [780, 650],
99
+ [720, 800],
100
+ [650, 920],
101
+ [500, 950],
102
+ [300, 940],
103
+ [150, 900],
104
+ [80, 750],
105
+ [90, 600],
106
+ [110, 450],
107
+ ],
108
+ "color": "#0000FF",
109
+ "mask_opacity": 0.2,
110
+ "stroke_width": 0.8,
111
  "stroke_opacity": 0.6,
112
+ "selected_mask_opacity": 0.4,
113
+ "selected_stroke_opacity": 0.9,
114
+ "display_text": "Irregular Content",
115
+ "display_font_size": 14,
116
+ "display_text_color": "#FFFF00",
117
  },
118
  {
119
+ "id": "star_shaped",
120
+ "coordinates": [
121
+ [400, 850],
122
+ [450, 900],
123
+ [520, 910],
124
+ [480, 970],
125
+ [500, 1040],
126
+ [400, 1000],
127
+ [300, 1040],
128
+ [320, 970],
129
+ [280, 910],
130
+ [350, 900],
131
+ ],
132
+ "color": "#FF00FF",
133
+ "mask_opacity": 0.35,
134
+ "stroke_width": 2.0,
135
+ "stroke_opacity": 0.9,
136
+ "selected_mask_opacity": 0.7,
137
+ "selected_stroke_opacity": 1.0,
138
+ "display_text": "Star Shape",
139
+ "display_font_size": 14,
140
+ "display_text_color": "#FFFFFF",
141
+ },
142
+ {
143
+ "id": "overlapping_triangle",
144
+ "coordinates": [
145
+ [480, 600],
146
+ [650, 750],
147
+ [720, 850],
148
+ [600, 920],
149
+ [450, 880],
150
+ [350, 800],
151
+ [380, 700],
152
+ ],
153
+ "color": "#FFAA00",
154
+ "mask_opacity": 0.28,
155
+ "stroke_width": 1.8,
156
+ "stroke_opacity": 0.75,
157
+ "selected_mask_opacity": 0.55,
158
+ "selected_stroke_opacity": 1.0,
159
+ "display_text": "Triangle",
160
+ "display_font_size": 14,
161
+ "display_text_color": "#000000",
162
+ },
163
+ ],
164
  }
165
 
166
+ polygon_table = [
167
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
168
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
169
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
170
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
171
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
172
+ ]
173
+
174
+
175
+ def process_viewer_selection(data, evt: gr.SelectData):
176
  if evt.value and data:
177
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
178
+
179
+ highlighted_table = []
180
+ for row in polygon_table:
181
+ if row[0] in selected_ids:
182
+ highlighted_row = [
183
+ f"→ {row[0]} ←",
184
+ f" {row[1]} ",
185
+ f"→ {row[2]} ←",
186
+ f"→ {row[3]} ←",
187
+ f"→ {row[4]} ←",
188
+ f"→ {row[5]} ←",
189
+ ]
190
+ highlighted_table.append(highlighted_row)
191
+ else:
192
+ highlighted_table.append(row)
193
+
194
+ info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
195
+ for selected_id in selected_ids:
196
+ selected_polygon = next(
197
+ (p for p in data["polygons"] if p["id"] == selected_id), None
198
+ )
199
+ if selected_polygon:
200
+ info_lines.append(
201
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
202
+ )
203
+
204
+ info_text = "\n".join(info_lines)
205
+ return info_text, highlighted_table
206
+
207
+ return "No polygons selected", polygon_table
208
+
209
+
210
+ def process_dataframe_selection(selected_data, evt: gr.SelectData):
211
+ if evt.index is not None and evt.index[0] < len(polygon_table):
212
+ selected_row = polygon_table[evt.index[0]]
213
+ polygon_id = selected_row[0]
214
+
215
+ updated_data = example_data.copy()
216
+ updated_data["selected_polygons"] = [polygon_id]
217
+
218
+ highlighted_table = []
219
+ for i, row in enumerate(polygon_table):
220
+ if i == evt.index[0]:
221
+ highlighted_row = [
222
+ f"→ {row[0]} ←",
223
+ f"→ {row[1]} ←",
224
+ f"→ {row[2]} ←",
225
+ f"→ {row[3]} ←",
226
+ f"→ {row[4]} ←",
227
+ f"→ {row[5]} ←",
228
+ ]
229
+ highlighted_table.append(highlighted_row)
230
+ else:
231
+ highlighted_table.append(row)
232
+
233
+ info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
234
+ return updated_data, info_text, highlighted_table
235
+
236
+ updated_data = example_data.copy()
237
+ updated_data["selected_polygons"] = []
238
+ return updated_data, "No polygons selected", polygon_table
239
+
240
+
241
+ def clear_selection():
242
+ updated_data = example_data.copy()
243
+ updated_data["selected_polygons"] = []
244
+ return updated_data, "No polygons selected", polygon_table
245
+
246
+
247
+ def select_polygon_by_id(polygon_id):
248
+ if not polygon_id or polygon_id.strip() == "":
249
+ updated_data = example_data.copy()
250
+ updated_data["selected_polygons"] = []
251
+ return updated_data, "No polygons selected", polygon_table
252
+
253
+ polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
254
+ valid_ids = [p["id"] for p in example_data["polygons"]]
255
+
256
+ valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
257
+ invalid_ids = [id for id in polygon_ids if id not in valid_ids]
258
+
259
+ if not valid_selected_ids:
260
+ updated_data = example_data.copy()
261
+ updated_data["selected_polygons"] = []
262
+ error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
263
+ return updated_data, error_msg, polygon_table
264
+
265
+ updated_data = example_data.copy()
266
+ updated_data["selected_polygons"] = valid_selected_ids
267
+
268
+ highlighted_table = []
269
+ for row in polygon_table:
270
+ if row[0] in valid_selected_ids:
271
+ highlighted_row = [
272
+ f"→ {row[0]} ←",
273
+ f"→ {row[1]} ←",
274
+ f"→ {row[2]} ←",
275
+ f"→ {row[3]} ←",
276
+ f"→ {row[4]} ←",
277
+ f"→ {row[5]} ←",
278
+ ]
279
+ highlighted_table.append(highlighted_row)
280
+ else:
281
+ highlighted_table.append(row)
282
+
283
+ info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
284
+ for selected_id in valid_selected_ids:
285
+ selected_polygon = next(
286
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
287
+ )
288
+ if selected_polygon:
289
+ info_lines.append(
290
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
291
+ )
292
+
293
+ if invalid_ids:
294
+ info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
295
+
296
+ info_text = "\n".join(info_lines)
297
+ return updated_data, info_text, highlighted_table
298
+
299
+
300
+ def toggle_text_display(current_data, show_text):
301
+ if not current_data:
302
+ return current_data
303
+
304
+ updated_data = current_data.copy()
305
+ updated_data["polygons"] = []
306
+
307
+ for polygon in current_data.get("polygons", []):
308
+ updated_polygon = polygon.copy()
309
+ if not show_text:
310
+ updated_polygon["display_font_size"] = 0
311
+ else:
312
+ original_polygon = next(
313
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
314
+ )
315
+ if original_polygon:
316
+ updated_polygon["display_text"] = original_polygon.get(
317
+ "display_text", polygon["id"]
318
+ )
319
+ updated_polygon["display_font_size"] = original_polygon.get(
320
+ "display_font_size", 14
321
+ )
322
+ updated_polygon["display_text_color"] = original_polygon.get(
323
+ "display_text_color", "#000000"
324
+ )
325
+
326
+ updated_data["polygons"].append(updated_polygon)
327
+
328
+ return updated_data
329
+
330
 
331
  with gr.Blocks() as demo:
332
  gr.Markdown("""
333
+ # PolygonAnnotator - Advanced Interactive Demo
334
+
335
+ ## 🎮 Controls & Hotkeys
336
+
337
+ ### Selection
338
+ - **Click** on polygons or text labels to select/deselect
339
+ - **Ctrl/Cmd+Click** for multiple selection
340
+ - **Click dataframe rows** to select polygons
341
+ - **Enter polygon IDs** manually in the textbox
342
+
343
+ ### Navigation
344
+ - **Mouse Wheel** - Zoom in/out at cursor position
345
+ - **+/=** - Zoom in (10%)
346
+ - **-** - Zoom out (10%)
347
+ - **Ctrl/Cmd+0** - Reset view to original
348
+ - **Arrow Keys** - Pan view (↑↓←→)
349
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
350
+
351
+ ### Features
352
+ - **Clear button** to deselect all
353
+ - **Toggle text display** checkbox for polygon labels
354
  """)
355
 
356
  with gr.Row():
357
+ with gr.Column(scale=2):
358
+ poly_annotator = PolygonAnnotator(
359
  value=example_data,
360
+ label="Document with Interactive Polygon Annotations",
361
  height=600,
362
  )
363
 
364
  with gr.Column(scale=1):
365
  selected_info = gr.Textbox(
366
+ label="Selected Polygon Information",
367
+ lines=5,
368
+ value="Click on a polygon to see its information",
369
+ )
370
+
371
+ polygon_dataframe = gr.Dataframe(
372
+ value=polygon_table,
373
+ headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
374
+ label="Polygon Data (Click rows to select)",
375
+ interactive=True,
376
  )
377
 
378
+ clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
379
+
380
+ # Add text display toggle
381
+ with gr.Row():
382
+ show_text_checkbox = gr.Checkbox(
383
+ label="Show Polygon Text",
384
+ value=True,
385
+ info="Toggle text display on polygons",
386
+ )
387
+
388
+ with gr.Row():
389
+ polygon_id_input = gr.Textbox(
390
+ label="Select by Polygon ID(s)",
391
+ placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
392
+ scale=3,
393
+ )
394
+ select_button = gr.Button("Select", variant="primary", scale=1)
395
+
396
+ gr.Markdown("""
397
+ ### Features Demonstrated
398
+
399
+ #### 🎨 Visual Customization
400
+ - Different **mask opacity** for each polygon fill
401
+ - Variable **stroke width** (0.5px to 1.5px)
402
+ - Custom **stroke opacity** for borders
403
+ - Enhanced appearance when selected
404
+ - **Text labels** with customizable colors and sizes
405
+
406
+ #### 🖱️ Interaction Methods
407
+ 1. **Direct Click**: Click polygons in the viewer
408
+ 2. **Multi-Selection**: Ctrl/Cmd+Click for multiple
409
+ 3. **Dataframe**: Click table rows
410
+ 4. **Text Input**: Type polygon IDs
411
+ 5. **Clear All**: Reset selection
412
+ 6. **Text Toggle**: Show/hide polygon labels
413
+
414
+ #### 📝 Polygon IDs
415
+ - `date_line` - Red header area
416
+ - `salutation` - Green greeting section
417
+ - `main_text_block` - Blue main content
418
+ - `closing_signature` - Yellow signature area
419
+
420
+ #### 💡 Tips
421
+ - Hover over polygons for visual feedback
422
+ - Selected polygons have increased opacity
423
+ - Use comma-separated IDs for batch selection
424
+ - Click selected polygons to deselect them
425
+ - Toggle text display to see labels on polygons
426
+ """)
427
+
428
  # Handle selection events
429
+ poly_annotator.select(
430
+ process_viewer_selection,
431
+ inputs=[poly_annotator],
432
+ outputs=[selected_info, polygon_dataframe],
433
+ )
434
+
435
+ polygon_dataframe.select(
436
+ process_dataframe_selection,
437
+ inputs=[polygon_dataframe],
438
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
439
+ )
440
+
441
+ clear_button.click(
442
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
443
+ )
444
+
445
+ select_button.click(
446
+ select_polygon_by_id,
447
+ inputs=[polygon_id_input],
448
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
449
+ )
450
+
451
+ # Also allow Enter key in textbox
452
+ polygon_id_input.submit(
453
+ select_polygon_by_id,
454
+ inputs=[polygon_id_input],
455
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
456
+ )
457
+
458
+ # Handle text display toggle
459
+ show_text_checkbox.change(
460
+ toggle_text_display,
461
+ inputs=[poly_annotator, show_text_checkbox],
462
+ outputs=[poly_annotator],
463
  )
464
 
465
  if __name__ == "__main__":
 
491
 
492
  </td>
493
  <td align="left"><code>None</code></td>
494
+ <td align="left">None</td>
495
  </tr>
496
 
497
  <tr>
 
504
 
505
  </td>
506
  <td align="left"><code>None</code></td>
507
+ <td align="left">None</td>
508
  </tr>
509
 
510
  <tr>
 
517
 
518
  </td>
519
  <td align="left"><code>None</code></td>
520
+ <td align="left">None</td>
521
  </tr>
522
 
523
  <tr>
 
530
 
531
  </td>
532
  <td align="left"><code>None</code></td>
533
+ <td align="left">None</td>
534
  </tr>
535
 
536
  <tr>
 
543
 
544
  </td>
545
  <td align="left"><code>None</code></td>
546
+ <td align="left">None</td>
547
  </tr>
548
 
549
  <tr>
 
556
 
557
  </td>
558
  <td align="left"><code>True</code></td>
559
+ <td align="left">None</td>
560
  </tr>
561
 
562
  <tr>
 
569
 
570
  </td>
571
  <td align="left"><code>None</code></td>
572
+ <td align="left">None</td>
573
  </tr>
574
 
575
  <tr>
 
582
 
583
  </td>
584
  <td align="left"><code>None</code></td>
585
+ <td align="left">None</td>
586
  </tr>
587
 
588
  <tr>
 
595
 
596
  </td>
597
  <td align="left"><code>True</code></td>
598
+ <td align="left">None</td>
599
  </tr>
600
 
601
  <tr>
 
608
 
609
  </td>
610
  <td align="left"><code>None</code></td>
611
+ <td align="left">None</td>
612
  </tr>
613
 
614
  <tr>
 
621
 
622
  </td>
623
  <td align="left"><code>160</code></td>
624
+ <td align="left">None</td>
625
  </tr>
626
 
627
  <tr>
 
634
 
635
  </td>
636
  <td align="left"><code>None</code></td>
637
+ <td align="left">None</td>
638
  </tr>
639
 
640
  <tr>
 
647
 
648
  </td>
649
  <td align="left"><code>True</code></td>
650
+ <td align="left">None</td>
651
  </tr>
652
 
653
  <tr>
 
660
 
661
  </td>
662
  <td align="left"><code>None</code></td>
663
+ <td align="left">None</td>
664
  </tr>
665
 
666
  <tr>
 
673
 
674
  </td>
675
  <td align="left"><code>None</code></td>
676
+ <td align="left">None</td>
677
  </tr>
678
 
679
  <tr>
 
686
 
687
  </td>
688
  <td align="left"><code>True</code></td>
689
+ <td align="left">None</td>
690
  </tr>
691
 
692
  <tr>
 
699
 
700
  </td>
701
  <td align="left"><code>None</code></td>
702
+ <td align="left">None</td>
703
  </tr>
704
 
705
  <tr>
 
712
 
713
  </td>
714
  <td align="left"><code>"value"</code></td>
715
+ <td align="left">None</td>
716
  </tr>
717
  </tbody></table>
718
 
 
737
 
738
  The code snippet below is accurate in cases where the component is used as both an input and an output.
739
 
740
+ - **As output:** Is passed, the preprocessed input data sent to the user's function in the backend.
741
+ - **As input:** Should return, the output data received by the component from the user's function in the backend.
742
 
743
  ```python
744
  def predict(
 
746
  ) -> dict | None:
747
  return value
748
  ```
 
src/backend/gradio_polygonannotator/__init__.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  from .polygonannotator import PolygonAnnotator
3
 
4
- __all__ = ['PolygonAnnotator']
 
 
1
  from .polygonannotator import PolygonAnnotator
2
 
3
+ __all__ = ["PolygonAnnotator"]
src/backend/gradio_polygonannotator/polygonannotator.py CHANGED
@@ -18,34 +18,27 @@ if TYPE_CHECKING:
18
 
19
  class Polygon(GradioModel):
20
  id: str
21
- coordinates: List[List[float]] # [[x1,y1], [x2,y2], ...]
22
- color: str # hex color like "#FF0000"
23
- mask_opacity: Optional[float] = 0.2 # fill opacity from 0.0 to 1.0, default 0.2
24
- stroke_width: Optional[float] = 0.7 # stroke width in pixels, default 0.7
25
- stroke_opacity: Optional[float] = 0.6 # stroke opacity from 0.0 to 1.0, default 0.6
26
- selected_mask_opacity: Optional[float] = 0.5 # mask opacity when selected, default 0.5
27
- selected_stroke_opacity: Optional[float] = 1.0 # stroke opacity when selected, default 1.0
 
 
 
28
 
29
 
30
  class PolygonAnnotatorData(GradioModel):
31
  image: FileData
32
  polygons: List[Polygon]
33
- selected_polygons: Optional[List[str]] = None # List of IDs of the currently selected polygons
34
 
35
 
36
  class PolygonAnnotator(Component):
37
  """
38
  Interactive polygon annotation component for visualizing and selecting polygon regions on images.
39
-
40
- The PolygonAnnotator displays an image with customizable polygon overlays that users can interact with.
41
- Features include multi-selection with Ctrl/Cmd+click, hover effects, and customizable appearance including
42
- stroke width, opacity settings for both fill and stroke, with separate settings for selected states.
43
-
44
- Perfect for:
45
- - Document layout analysis and region selection
46
- - Image segmentation visualization
47
- - Interactive annotation review and editing
48
- - Object detection result visualization
49
  """
50
 
51
  EVENTS = [
@@ -79,28 +72,6 @@ class PolygonAnnotator(Component):
79
  key: int | str | tuple[int | str, ...] | None = None,
80
  preserved_by_key: list[str] | str | None = "value",
81
  ):
82
- """
83
- Parameters:
84
- value: Dictionary containing 'image' (FileData), 'polygons' (list with id, coordinates, color, opacities),
85
- and optionally 'selected_polygons' (list of selected IDs).
86
- label: Component label shown above the annotator.
87
- every: Continuously calls `value` to recalculate it if `value` is a function.
88
- inputs: Components used as inputs to calculate `value` if it's a function.
89
- show_label: Whether to display the label.
90
- show_download_button: Whether to show image download button.
91
- height: Component height in pixels or CSS units.
92
- width: Component width in pixels or CSS units.
93
- container: Whether to wrap component in a container with padding.
94
- scale: Relative size compared to adjacent components.
95
- min_width: Minimum pixel width before wrapping.
96
- interactive: Whether users can interact with polygons (selection/deselection).
97
- visible: Whether component is visible ("hidden" keeps it in DOM but invisible).
98
- elem_id: HTML DOM id for CSS targeting.
99
- elem_classes: HTML DOM classes for CSS targeting.
100
- render: Whether to render the component immediately.
101
- key: Key for maintaining component identity across re-renders.
102
- preserved_by_key: Parameters preserved across re-renders with same key.
103
- """
104
  self.show_download_button = show_download_button
105
  self.height = height
106
  self.width = width
@@ -123,13 +94,6 @@ class PolygonAnnotator(Component):
123
  )
124
 
125
  def preprocess(self, payload: PolygonAnnotatorData | None) -> dict | None:
126
- """
127
- Parameters:
128
- payload: The component data containing image and polygon annotations.
129
- Returns:
130
- Dictionary with image path, polygon data including coordinates, colors, opacities,
131
- and the list of currently selected polygon IDs.
132
- """
133
  if payload is None:
134
  return None
135
  return {
@@ -143,33 +107,26 @@ class PolygonAnnotator(Component):
143
  "stroke_width": p.stroke_width,
144
  "stroke_opacity": p.stroke_opacity,
145
  "selected_mask_opacity": p.selected_mask_opacity,
146
- "selected_stroke_opacity": p.selected_stroke_opacity
 
 
 
147
  }
148
  for p in payload.polygons
149
  ],
150
- "selected_polygons": payload.selected_polygons
151
  }
152
 
153
  def postprocess(self, value: dict | None) -> PolygonAnnotatorData | None:
154
- """
155
- Parameters:
156
- value: Dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries
157
- with id, coordinates, color, mask_opacity, stroke_width, stroke_opacity, and selection opacity settings),
158
- and optionally 'selected_polygons' (list of selected polygon IDs).
159
- Returns:
160
- Processed component data ready for display.
161
- """
162
  if value is None:
163
  return None
164
 
165
- # Handle the image path
166
  image_path = value.get("image")
167
  if isinstance(image_path, str):
168
  image_data = FileData(path=image_path)
169
  else:
170
  return None
171
 
172
- # Handle polygons
173
  polygons = []
174
  for poly in value.get("polygons", []):
175
  polygons.append(
@@ -177,18 +134,21 @@ class PolygonAnnotator(Component):
177
  id=poly["id"],
178
  coordinates=poly["coordinates"],
179
  color=poly.get("color", "#FF0000"),
180
- mask_opacity=poly.get("mask_opacity", poly.get("opacity", 0.2)), # Support old 'opacity' key for backwards compatibility
181
  stroke_width=poly.get("stroke_width", 0.7),
182
  stroke_opacity=poly.get("stroke_opacity", 0.6),
183
  selected_mask_opacity=poly.get("selected_mask_opacity", 0.5),
184
- selected_stroke_opacity=poly.get("selected_stroke_opacity", 1.0)
 
 
 
185
  )
186
  )
187
 
188
  return PolygonAnnotatorData(
189
  image=image_data,
190
  polygons=polygons,
191
- selected_polygons=value.get("selected_polygons")
192
  )
193
 
194
  def example_payload(self) -> Any:
@@ -203,9 +163,9 @@ class PolygonAnnotator(Component):
203
  "color": "#FF0000",
204
  "mask_opacity": 0.2,
205
  "stroke_width": 0.7,
206
- "stroke_opacity": 0.6
207
  }
208
- ]
209
  }
210
 
211
  def example_value(self) -> Any:
@@ -218,7 +178,7 @@ class PolygonAnnotator(Component):
218
  "color": "#FF0000",
219
  "mask_opacity": 0.2,
220
  "stroke_width": 0.7,
221
- "stroke_opacity": 0.6
222
  },
223
  {
224
  "id": "polygon2",
@@ -226,7 +186,7 @@ class PolygonAnnotator(Component):
226
  "color": "#00FF00",
227
  "mask_opacity": 0.2,
228
  "stroke_width": 1,
229
- "stroke_opacity": 0.8
230
- }
231
- ]
232
  }
 
18
 
19
  class Polygon(GradioModel):
20
  id: str
21
+ coordinates: List[List[float]]
22
+ color: str
23
+ mask_opacity: Optional[float] = 0.2
24
+ stroke_width: Optional[float] = 0.7
25
+ stroke_opacity: Optional[float] = 0.6
26
+ selected_mask_opacity: Optional[float] = 0.5
27
+ selected_stroke_opacity: Optional[float] = 1.0
28
+ display_text: Optional[str] = None
29
+ display_font_size: Optional[float] = None
30
+ display_text_color: Optional[str] = "#000000"
31
 
32
 
33
  class PolygonAnnotatorData(GradioModel):
34
  image: FileData
35
  polygons: List[Polygon]
36
+ selected_polygons: Optional[List[str]] = None
37
 
38
 
39
  class PolygonAnnotator(Component):
40
  """
41
  Interactive polygon annotation component for visualizing and selecting polygon regions on images.
 
 
 
 
 
 
 
 
 
 
42
  """
43
 
44
  EVENTS = [
 
72
  key: int | str | tuple[int | str, ...] | None = None,
73
  preserved_by_key: list[str] | str | None = "value",
74
  ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  self.show_download_button = show_download_button
76
  self.height = height
77
  self.width = width
 
94
  )
95
 
96
  def preprocess(self, payload: PolygonAnnotatorData | None) -> dict | None:
 
 
 
 
 
 
 
97
  if payload is None:
98
  return None
99
  return {
 
107
  "stroke_width": p.stroke_width,
108
  "stroke_opacity": p.stroke_opacity,
109
  "selected_mask_opacity": p.selected_mask_opacity,
110
+ "selected_stroke_opacity": p.selected_stroke_opacity,
111
+ "display_text": p.display_text,
112
+ "display_font_size": p.display_font_size,
113
+ "display_text_color": p.display_text_color,
114
  }
115
  for p in payload.polygons
116
  ],
117
+ "selected_polygons": payload.selected_polygons,
118
  }
119
 
120
  def postprocess(self, value: dict | None) -> PolygonAnnotatorData | None:
 
 
 
 
 
 
 
 
121
  if value is None:
122
  return None
123
 
 
124
  image_path = value.get("image")
125
  if isinstance(image_path, str):
126
  image_data = FileData(path=image_path)
127
  else:
128
  return None
129
 
 
130
  polygons = []
131
  for poly in value.get("polygons", []):
132
  polygons.append(
 
134
  id=poly["id"],
135
  coordinates=poly["coordinates"],
136
  color=poly.get("color", "#FF0000"),
137
+ mask_opacity=poly.get("mask_opacity", poly.get("opacity", 0.2)),
138
  stroke_width=poly.get("stroke_width", 0.7),
139
  stroke_opacity=poly.get("stroke_opacity", 0.6),
140
  selected_mask_opacity=poly.get("selected_mask_opacity", 0.5),
141
+ selected_stroke_opacity=poly.get("selected_stroke_opacity", 1.0),
142
+ display_text=poly.get("display_text", None),
143
+ display_font_size=poly.get("display_font_size", None),
144
+ display_text_color=poly.get("display_text_color", "#000000"),
145
  )
146
  )
147
 
148
  return PolygonAnnotatorData(
149
  image=image_data,
150
  polygons=polygons,
151
+ selected_polygons=value.get("selected_polygons"),
152
  )
153
 
154
  def example_payload(self) -> Any:
 
163
  "color": "#FF0000",
164
  "mask_opacity": 0.2,
165
  "stroke_width": 0.7,
166
+ "stroke_opacity": 0.6,
167
  }
168
+ ],
169
  }
170
 
171
  def example_value(self) -> Any:
 
178
  "color": "#FF0000",
179
  "mask_opacity": 0.2,
180
  "stroke_width": 0.7,
181
+ "stroke_opacity": 0.6,
182
  },
183
  {
184
  "id": "polygon2",
 
186
  "color": "#00FF00",
187
  "mask_opacity": 0.2,
188
  "stroke_width": 1,
189
+ "stroke_opacity": 0.8,
190
+ },
191
+ ],
192
  }
src/backend/gradio_polygonannotator/templates/component/Index-jQCzN2ap.js ADDED
The diff for this file is too large to render. See raw diff
 
src/backend/gradio_polygonannotator/templates/component/SharedSystems-DOa2TyTQ.js ADDED
@@ -0,0 +1,2766 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { q as He, G as ne, t as ie, u as ze, k as oe, M as v, v as le, E as u, e as O, x as S, y as We, z as F, F as b, R as L, H as ue, I as Ve, s as m, S as f, h as B, w as H, J, K as Ne, b as X, B as k, i as U, L as je, N as M, j as T, O as w, Q as $e, a as qe, V as de, W as ce, X as he, Y as fe, C as P, Z as Ke, _ as A, $ as Q, D as z, a0 as Ye, P as Je, c as Xe, T as Z, a1 as ee, a2 as Qe, a3 as Ze, a4 as et } from "./Index-jQCzN2ap.js";
2
+ import { S as pe, B as me, c as tt } from "./colorToUniform-zJcCVLeu.js";
3
+ const ve = class I extends He {
4
+ /**
5
+ * @param options - The optional parameters of this filter.
6
+ */
7
+ constructor(e) {
8
+ e = { ...I.defaultOptions, ...e }, super(e), this.enabled = !0, this._state = pe.for2d(), this.blendMode = e.blendMode, this.padding = e.padding, typeof e.antialias == "boolean" ? this.antialias = e.antialias ? "on" : "off" : this.antialias = e.antialias, this.resolution = e.resolution, this.blendRequired = e.blendRequired, this.clipToViewport = e.clipToViewport, this.addResource("uTexture", 0, 1);
9
+ }
10
+ /**
11
+ * Applies the filter
12
+ * @param filterManager - The renderer to retrieve the filter from
13
+ * @param input - The input render target.
14
+ * @param output - The target to output to.
15
+ * @param clearMode - Should the output be cleared before rendering to it
16
+ */
17
+ apply(e, t, r, s) {
18
+ e.applyFilter(this, t, r, s);
19
+ }
20
+ /**
21
+ * Get the blend mode of the filter.
22
+ * @default "normal"
23
+ */
24
+ get blendMode() {
25
+ return this._state.blendMode;
26
+ }
27
+ /** Sets the blend mode of the filter. */
28
+ set blendMode(e) {
29
+ this._state.blendMode = e;
30
+ }
31
+ /**
32
+ * A short hand function to create a filter based of a vertex and fragment shader src.
33
+ * @param options
34
+ * @returns A shiny new PixiJS filter!
35
+ */
36
+ static from(e) {
37
+ const { gpu: t, gl: r, ...s } = e;
38
+ let a, i;
39
+ return t && (a = ne.from(t)), r && (i = ie.from(r)), new I({
40
+ gpuProgram: a,
41
+ glProgram: i,
42
+ ...s
43
+ });
44
+ }
45
+ };
46
+ ve.defaultOptions = {
47
+ blendMode: "normal",
48
+ resolution: 1,
49
+ padding: 0,
50
+ antialias: "off",
51
+ blendRequired: !1,
52
+ clipToViewport: !0
53
+ };
54
+ let rt = ve;
55
+ var st = `in vec2 vMaskCoord;
56
+ in vec2 vTextureCoord;
57
+
58
+ uniform sampler2D uTexture;
59
+ uniform sampler2D uMaskTexture;
60
+
61
+ uniform float uAlpha;
62
+ uniform vec4 uMaskClamp;
63
+ uniform float uInverse;
64
+
65
+ out vec4 finalColor;
66
+
67
+ void main(void)
68
+ {
69
+ float clip = step(3.5,
70
+ step(uMaskClamp.x, vMaskCoord.x) +
71
+ step(uMaskClamp.y, vMaskCoord.y) +
72
+ step(vMaskCoord.x, uMaskClamp.z) +
73
+ step(vMaskCoord.y, uMaskClamp.w));
74
+
75
+ // TODO look into why this is needed
76
+ float npmAlpha = uAlpha;
77
+ vec4 original = texture(uTexture, vTextureCoord);
78
+ vec4 masky = texture(uMaskTexture, vMaskCoord);
79
+ float alphaMul = 1.0 - npmAlpha * (1.0 - masky.a);
80
+
81
+ float a = alphaMul * masky.r * npmAlpha * clip;
82
+
83
+ if (uInverse == 1.0) {
84
+ a = 1.0 - a;
85
+ }
86
+
87
+ finalColor = original * a;
88
+ }
89
+ `, at = `in vec2 aPosition;
90
+
91
+ out vec2 vTextureCoord;
92
+ out vec2 vMaskCoord;
93
+
94
+
95
+ uniform vec4 uInputSize;
96
+ uniform vec4 uOutputFrame;
97
+ uniform vec4 uOutputTexture;
98
+ uniform mat3 uFilterMatrix;
99
+
100
+ vec4 filterVertexPosition( vec2 aPosition )
101
+ {
102
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
103
+
104
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
105
+ position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
106
+
107
+ return vec4(position, 0.0, 1.0);
108
+ }
109
+
110
+ vec2 filterTextureCoord( vec2 aPosition )
111
+ {
112
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
113
+ }
114
+
115
+ vec2 getFilterCoord( vec2 aPosition )
116
+ {
117
+ return ( uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy;
118
+ }
119
+
120
+ void main(void)
121
+ {
122
+ gl_Position = filterVertexPosition(aPosition);
123
+ vTextureCoord = filterTextureCoord(aPosition);
124
+ vMaskCoord = getFilterCoord(aPosition);
125
+ }
126
+ `, te = `struct GlobalFilterUniforms {
127
+ uInputSize:vec4<f32>,
128
+ uInputPixel:vec4<f32>,
129
+ uInputClamp:vec4<f32>,
130
+ uOutputFrame:vec4<f32>,
131
+ uGlobalFrame:vec4<f32>,
132
+ uOutputTexture:vec4<f32>,
133
+ };
134
+
135
+ struct MaskUniforms {
136
+ uFilterMatrix:mat3x3<f32>,
137
+ uMaskClamp:vec4<f32>,
138
+ uAlpha:f32,
139
+ uInverse:f32,
140
+ };
141
+
142
+ @group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
143
+ @group(0) @binding(1) var uTexture: texture_2d<f32>;
144
+ @group(0) @binding(2) var uSampler : sampler;
145
+
146
+ @group(1) @binding(0) var<uniform> filterUniforms : MaskUniforms;
147
+ @group(1) @binding(1) var uMaskTexture: texture_2d<f32>;
148
+
149
+ struct VSOutput {
150
+ @builtin(position) position: vec4<f32>,
151
+ @location(0) uv : vec2<f32>,
152
+ @location(1) filterUv : vec2<f32>,
153
+ };
154
+
155
+ fn filterVertexPosition(aPosition:vec2<f32>) -> vec4<f32>
156
+ {
157
+ var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
158
+
159
+ position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
160
+ position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
161
+
162
+ return vec4(position, 0.0, 1.0);
163
+ }
164
+
165
+ fn filterTextureCoord( aPosition:vec2<f32> ) -> vec2<f32>
166
+ {
167
+ return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
168
+ }
169
+
170
+ fn globalTextureCoord( aPosition:vec2<f32> ) -> vec2<f32>
171
+ {
172
+ return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw);
173
+ }
174
+
175
+ fn getFilterCoord(aPosition:vec2<f32> ) -> vec2<f32>
176
+ {
177
+ return ( filterUniforms.uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy;
178
+ }
179
+
180
+ fn getSize() -> vec2<f32>
181
+ {
182
+ return gfu.uGlobalFrame.zw;
183
+ }
184
+
185
+ @vertex
186
+ fn mainVertex(
187
+ @location(0) aPosition : vec2<f32>,
188
+ ) -> VSOutput {
189
+ return VSOutput(
190
+ filterVertexPosition(aPosition),
191
+ filterTextureCoord(aPosition),
192
+ getFilterCoord(aPosition)
193
+ );
194
+ }
195
+
196
+ @fragment
197
+ fn mainFragment(
198
+ @location(0) uv: vec2<f32>,
199
+ @location(1) filterUv: vec2<f32>,
200
+ @builtin(position) position: vec4<f32>
201
+ ) -> @location(0) vec4<f32> {
202
+
203
+ var maskClamp = filterUniforms.uMaskClamp;
204
+ var uAlpha = filterUniforms.uAlpha;
205
+
206
+ var clip = step(3.5,
207
+ step(maskClamp.x, filterUv.x) +
208
+ step(maskClamp.y, filterUv.y) +
209
+ step(filterUv.x, maskClamp.z) +
210
+ step(filterUv.y, maskClamp.w));
211
+
212
+ var mask = textureSample(uMaskTexture, uSampler, filterUv);
213
+ var source = textureSample(uTexture, uSampler, uv);
214
+ var alphaMul = 1.0 - uAlpha * (1.0 - mask.a);
215
+
216
+ var a: f32 = alphaMul * mask.r * uAlpha * clip;
217
+
218
+ if (filterUniforms.uInverse == 1.0) {
219
+ a = 1.0 - a;
220
+ }
221
+
222
+ return source * a;
223
+ }
224
+ `;
225
+ class nt extends rt {
226
+ constructor(e) {
227
+ const { sprite: t, ...r } = e, s = new ze(t.texture), a = new oe({
228
+ uFilterMatrix: { value: new v(), type: "mat3x3<f32>" },
229
+ uMaskClamp: { value: s.uClampFrame, type: "vec4<f32>" },
230
+ uAlpha: { value: 1, type: "f32" },
231
+ uInverse: { value: e.inverse ? 1 : 0, type: "f32" }
232
+ }), i = ne.from({
233
+ vertex: {
234
+ source: te,
235
+ entryPoint: "mainVertex"
236
+ },
237
+ fragment: {
238
+ source: te,
239
+ entryPoint: "mainFragment"
240
+ }
241
+ }), o = ie.from({
242
+ vertex: at,
243
+ fragment: st,
244
+ name: "mask-filter"
245
+ });
246
+ super({
247
+ ...r,
248
+ gpuProgram: i,
249
+ glProgram: o,
250
+ clipToViewport: !1,
251
+ resources: {
252
+ filterUniforms: a,
253
+ uMaskTexture: t.texture.source
254
+ }
255
+ }), this.sprite = t, this._textureMatrix = s;
256
+ }
257
+ set inverse(e) {
258
+ this.resources.filterUniforms.uniforms.uInverse = e ? 1 : 0;
259
+ }
260
+ get inverse() {
261
+ return this.resources.filterUniforms.uniforms.uInverse === 1;
262
+ }
263
+ apply(e, t, r, s) {
264
+ this._textureMatrix.texture = this.sprite.texture, e.calculateSpriteMatrix(
265
+ this.resources.filterUniforms.uniforms.uFilterMatrix,
266
+ this.sprite
267
+ ).prepend(this._textureMatrix.mapCoord), this.resources.uMaskTexture = this.sprite.texture.source, e.applyFilter(this, t, r, s);
268
+ }
269
+ }
270
+ const W = class ge {
271
+ constructor(e, t) {
272
+ var r, s;
273
+ this.state = pe.for2d(), this._batchersByInstructionSet = /* @__PURE__ */ Object.create(null), this._activeBatches = /* @__PURE__ */ Object.create(null), this.renderer = e, this._adaptor = t, (s = (r = this._adaptor).init) == null || s.call(r, this);
274
+ }
275
+ static getBatcher(e) {
276
+ return new this._availableBatchers[e]();
277
+ }
278
+ buildStart(e) {
279
+ let t = this._batchersByInstructionSet[e.uid];
280
+ t || (t = this._batchersByInstructionSet[e.uid] = /* @__PURE__ */ Object.create(null), t.default || (t.default = new le({
281
+ maxTextures: this.renderer.limits.maxBatchableTextures
282
+ }))), this._activeBatches = t, this._activeBatch = this._activeBatches.default;
283
+ for (const r in this._activeBatches)
284
+ this._activeBatches[r].begin();
285
+ }
286
+ addToBatch(e, t) {
287
+ if (this._activeBatch.name !== e.batcherName) {
288
+ this._activeBatch.break(t);
289
+ let r = this._activeBatches[e.batcherName];
290
+ r || (r = this._activeBatches[e.batcherName] = ge.getBatcher(e.batcherName), r.begin()), this._activeBatch = r;
291
+ }
292
+ this._activeBatch.add(e);
293
+ }
294
+ break(e) {
295
+ this._activeBatch.break(e);
296
+ }
297
+ buildEnd(e) {
298
+ this._activeBatch.break(e);
299
+ const t = this._activeBatches;
300
+ for (const r in t) {
301
+ const s = t[r], a = s.geometry;
302
+ a.indexBuffer.setDataWithSize(s.indexBuffer, s.indexSize, !0), a.buffers[0].setDataWithSize(s.attributeBuffer.float32View, s.attributeSize, !1);
303
+ }
304
+ }
305
+ upload(e) {
306
+ const t = this._batchersByInstructionSet[e.uid];
307
+ for (const r in t) {
308
+ const s = t[r], a = s.geometry;
309
+ s.dirty && (s.dirty = !1, a.buffers[0].update(s.attributeSize * 4));
310
+ }
311
+ }
312
+ execute(e) {
313
+ if (e.action === "startBatch") {
314
+ const t = e.batcher, r = t.geometry, s = t.shader;
315
+ this._adaptor.start(this, r, s);
316
+ }
317
+ this._adaptor.execute(this, e);
318
+ }
319
+ destroy() {
320
+ this.state = null, this.renderer = null, this._adaptor = null;
321
+ for (const e in this._activeBatches)
322
+ this._activeBatches[e].destroy();
323
+ this._activeBatches = null;
324
+ }
325
+ };
326
+ W.extension = {
327
+ type: [
328
+ u.WebGLPipes,
329
+ u.WebGPUPipes,
330
+ u.CanvasPipes
331
+ ],
332
+ name: "batch"
333
+ };
334
+ W._availableBatchers = /* @__PURE__ */ Object.create(null);
335
+ let xe = W;
336
+ O.handleByMap(u.Batcher, xe._availableBatchers);
337
+ O.add(le);
338
+ const It = {
339
+ name: "texture-bit",
340
+ vertex: {
341
+ header: (
342
+ /* wgsl */
343
+ `
344
+
345
+ struct TextureUniforms {
346
+ uTextureMatrix:mat3x3<f32>,
347
+ }
348
+
349
+ @group(2) @binding(2) var<uniform> textureUniforms : TextureUniforms;
350
+ `
351
+ ),
352
+ main: (
353
+ /* wgsl */
354
+ `
355
+ uv = (textureUniforms.uTextureMatrix * vec3(uv, 1.0)).xy;
356
+ `
357
+ )
358
+ },
359
+ fragment: {
360
+ header: (
361
+ /* wgsl */
362
+ `
363
+ @group(2) @binding(0) var uTexture: texture_2d<f32>;
364
+ @group(2) @binding(1) var uSampler: sampler;
365
+
366
+
367
+ `
368
+ ),
369
+ main: (
370
+ /* wgsl */
371
+ `
372
+ outColor = textureSample(uTexture, uSampler, vUV);
373
+ `
374
+ )
375
+ }
376
+ }, Gt = {
377
+ name: "texture-bit",
378
+ vertex: {
379
+ header: (
380
+ /* glsl */
381
+ `
382
+ uniform mat3 uTextureMatrix;
383
+ `
384
+ ),
385
+ main: (
386
+ /* glsl */
387
+ `
388
+ uv = (uTextureMatrix * vec3(uv, 1.0)).xy;
389
+ `
390
+ )
391
+ },
392
+ fragment: {
393
+ header: (
394
+ /* glsl */
395
+ `
396
+ uniform sampler2D uTexture;
397
+
398
+
399
+ `
400
+ ),
401
+ main: (
402
+ /* glsl */
403
+ `
404
+ outColor = texture(uTexture, vUV);
405
+ `
406
+ )
407
+ }
408
+ }, it = new F();
409
+ class ot extends ue {
410
+ constructor() {
411
+ super(), this.filters = [new nt({
412
+ sprite: new Ve(m.EMPTY),
413
+ inverse: !1,
414
+ resolution: "inherit",
415
+ antialias: "inherit"
416
+ })];
417
+ }
418
+ get sprite() {
419
+ return this.filters[0].sprite;
420
+ }
421
+ set sprite(e) {
422
+ this.filters[0].sprite = e;
423
+ }
424
+ get inverse() {
425
+ return this.filters[0].inverse;
426
+ }
427
+ set inverse(e) {
428
+ this.filters[0].inverse = e;
429
+ }
430
+ }
431
+ class _e {
432
+ constructor(e) {
433
+ this._activeMaskStage = [], this._renderer = e;
434
+ }
435
+ push(e, t, r) {
436
+ const s = this._renderer;
437
+ if (s.renderPipes.batch.break(r), r.add({
438
+ renderPipeId: "alphaMask",
439
+ action: "pushMaskBegin",
440
+ mask: e,
441
+ inverse: t._maskOptions.inverse,
442
+ canBundle: !1,
443
+ maskedContainer: t
444
+ }), e.inverse = t._maskOptions.inverse, e.renderMaskToTexture) {
445
+ const a = e.mask;
446
+ a.includeInBuild = !0, a.collectRenderables(
447
+ r,
448
+ s,
449
+ null
450
+ ), a.includeInBuild = !1;
451
+ }
452
+ s.renderPipes.batch.break(r), r.add({
453
+ renderPipeId: "alphaMask",
454
+ action: "pushMaskEnd",
455
+ mask: e,
456
+ maskedContainer: t,
457
+ inverse: t._maskOptions.inverse,
458
+ canBundle: !1
459
+ });
460
+ }
461
+ pop(e, t, r) {
462
+ this._renderer.renderPipes.batch.break(r), r.add({
463
+ renderPipeId: "alphaMask",
464
+ action: "popMaskEnd",
465
+ mask: e,
466
+ inverse: t._maskOptions.inverse,
467
+ canBundle: !1
468
+ });
469
+ }
470
+ execute(e) {
471
+ const t = this._renderer, r = e.mask.renderMaskToTexture;
472
+ if (e.action === "pushMaskBegin") {
473
+ const s = S.get(ot);
474
+ if (s.inverse = e.inverse, r) {
475
+ e.mask.mask.measurable = !0;
476
+ const a = We(e.mask.mask, !0, it);
477
+ e.mask.mask.measurable = !1, a.ceil();
478
+ const i = t.renderTarget.renderTarget.colorTexture.source, o = b.getOptimalTexture(
479
+ a.width,
480
+ a.height,
481
+ i._resolution,
482
+ i.antialias
483
+ );
484
+ t.renderTarget.push(o, !0), t.globalUniforms.push({
485
+ offset: a,
486
+ worldColor: 4294967295
487
+ });
488
+ const l = s.sprite;
489
+ l.texture = o, l.worldTransform.tx = a.minX, l.worldTransform.ty = a.minY, this._activeMaskStage.push({
490
+ filterEffect: s,
491
+ maskedContainer: e.maskedContainer,
492
+ filterTexture: o
493
+ });
494
+ } else
495
+ s.sprite = e.mask.mask, this._activeMaskStage.push({
496
+ filterEffect: s,
497
+ maskedContainer: e.maskedContainer
498
+ });
499
+ } else if (e.action === "pushMaskEnd") {
500
+ const s = this._activeMaskStage[this._activeMaskStage.length - 1];
501
+ r && (t.type === L.WEBGL && t.renderTarget.finishRenderPass(), t.renderTarget.pop(), t.globalUniforms.pop()), t.filter.push({
502
+ renderPipeId: "filter",
503
+ action: "pushFilter",
504
+ container: s.maskedContainer,
505
+ filterEffect: s.filterEffect,
506
+ canBundle: !1
507
+ });
508
+ } else if (e.action === "popMaskEnd") {
509
+ t.filter.pop();
510
+ const s = this._activeMaskStage.pop();
511
+ r && b.returnTexture(s.filterTexture), S.return(s.filterEffect);
512
+ }
513
+ }
514
+ destroy() {
515
+ this._renderer = null, this._activeMaskStage = null;
516
+ }
517
+ }
518
+ _e.extension = {
519
+ type: [
520
+ u.WebGLPipes,
521
+ u.WebGPUPipes,
522
+ u.CanvasPipes
523
+ ],
524
+ name: "alphaMask"
525
+ };
526
+ class be {
527
+ constructor(e) {
528
+ this._colorStack = [], this._colorStackIndex = 0, this._currentColor = 0, this._renderer = e;
529
+ }
530
+ buildStart() {
531
+ this._colorStack[0] = 15, this._colorStackIndex = 1, this._currentColor = 15;
532
+ }
533
+ push(e, t, r) {
534
+ this._renderer.renderPipes.batch.break(r);
535
+ const a = this._colorStack;
536
+ a[this._colorStackIndex] = a[this._colorStackIndex - 1] & e.mask;
537
+ const i = this._colorStack[this._colorStackIndex];
538
+ i !== this._currentColor && (this._currentColor = i, r.add({
539
+ renderPipeId: "colorMask",
540
+ colorMask: i,
541
+ canBundle: !1
542
+ })), this._colorStackIndex++;
543
+ }
544
+ pop(e, t, r) {
545
+ this._renderer.renderPipes.batch.break(r);
546
+ const a = this._colorStack;
547
+ this._colorStackIndex--;
548
+ const i = a[this._colorStackIndex - 1];
549
+ i !== this._currentColor && (this._currentColor = i, r.add({
550
+ renderPipeId: "colorMask",
551
+ colorMask: i,
552
+ canBundle: !1
553
+ }));
554
+ }
555
+ execute(e) {
556
+ this._renderer.colorMask.setMask(e.colorMask);
557
+ }
558
+ destroy() {
559
+ this._renderer = null, this._colorStack = null;
560
+ }
561
+ }
562
+ be.extension = {
563
+ type: [
564
+ u.WebGLPipes,
565
+ u.WebGPUPipes,
566
+ u.CanvasPipes
567
+ ],
568
+ name: "colorMask"
569
+ };
570
+ class Te {
571
+ constructor(e) {
572
+ this._maskStackHash = {}, this._maskHash = /* @__PURE__ */ new WeakMap(), this._renderer = e;
573
+ }
574
+ push(e, t, r) {
575
+ var s;
576
+ const a = e, i = this._renderer;
577
+ i.renderPipes.batch.break(r), i.renderPipes.blendMode.setBlendMode(a.mask, "none", r), r.add({
578
+ renderPipeId: "stencilMask",
579
+ action: "pushMaskBegin",
580
+ mask: e,
581
+ inverse: t._maskOptions.inverse,
582
+ canBundle: !1
583
+ });
584
+ const o = a.mask;
585
+ o.includeInBuild = !0, this._maskHash.has(a) || this._maskHash.set(a, {
586
+ instructionsStart: 0,
587
+ instructionsLength: 0
588
+ });
589
+ const l = this._maskHash.get(a);
590
+ l.instructionsStart = r.instructionSize, o.collectRenderables(
591
+ r,
592
+ i,
593
+ null
594
+ ), o.includeInBuild = !1, i.renderPipes.batch.break(r), r.add({
595
+ renderPipeId: "stencilMask",
596
+ action: "pushMaskEnd",
597
+ mask: e,
598
+ inverse: t._maskOptions.inverse,
599
+ canBundle: !1
600
+ });
601
+ const d = r.instructionSize - l.instructionsStart - 1;
602
+ l.instructionsLength = d;
603
+ const c = i.renderTarget.renderTarget.uid;
604
+ (s = this._maskStackHash)[c] ?? (s[c] = 0);
605
+ }
606
+ pop(e, t, r) {
607
+ const s = e, a = this._renderer;
608
+ a.renderPipes.batch.break(r), a.renderPipes.blendMode.setBlendMode(s.mask, "none", r), r.add({
609
+ renderPipeId: "stencilMask",
610
+ action: "popMaskBegin",
611
+ inverse: t._maskOptions.inverse,
612
+ canBundle: !1
613
+ });
614
+ const i = this._maskHash.get(e);
615
+ for (let o = 0; o < i.instructionsLength; o++)
616
+ r.instructions[r.instructionSize++] = r.instructions[i.instructionsStart++];
617
+ r.add({
618
+ renderPipeId: "stencilMask",
619
+ action: "popMaskEnd",
620
+ canBundle: !1
621
+ });
622
+ }
623
+ execute(e) {
624
+ var t;
625
+ const r = this._renderer, s = r.renderTarget.renderTarget.uid;
626
+ let a = (t = this._maskStackHash)[s] ?? (t[s] = 0);
627
+ e.action === "pushMaskBegin" ? (r.renderTarget.ensureDepthStencil(), r.stencil.setStencilMode(f.RENDERING_MASK_ADD, a), a++, r.colorMask.setMask(0)) : e.action === "pushMaskEnd" ? (e.inverse ? r.stencil.setStencilMode(f.INVERSE_MASK_ACTIVE, a) : r.stencil.setStencilMode(f.MASK_ACTIVE, a), r.colorMask.setMask(15)) : e.action === "popMaskBegin" ? (r.colorMask.setMask(0), a !== 0 ? r.stencil.setStencilMode(f.RENDERING_MASK_REMOVE, a) : (r.renderTarget.clear(null, B.STENCIL), r.stencil.setStencilMode(f.DISABLED, a)), a--) : e.action === "popMaskEnd" && (e.inverse ? r.stencil.setStencilMode(f.INVERSE_MASK_ACTIVE, a) : r.stencil.setStencilMode(f.MASK_ACTIVE, a), r.colorMask.setMask(15)), this._maskStackHash[s] = a;
628
+ }
629
+ destroy() {
630
+ this._renderer = null, this._maskStackHash = null, this._maskHash = null;
631
+ }
632
+ }
633
+ Te.extension = {
634
+ type: [
635
+ u.WebGLPipes,
636
+ u.WebGPUPipes,
637
+ u.CanvasPipes
638
+ ],
639
+ name: "stencilMask"
640
+ };
641
+ function Et(n, e) {
642
+ for (const t in n.attributes) {
643
+ const r = n.attributes[t], s = e[t];
644
+ s ? (r.format ?? (r.format = s.format), r.offset ?? (r.offset = s.offset), r.instance ?? (r.instance = s.instance)) : H(`Attribute ${t} is not present in the shader, but is present in the geometry. Unable to infer attribute details.`);
645
+ }
646
+ lt(n);
647
+ }
648
+ function lt(n) {
649
+ const { buffers: e, attributes: t } = n, r = {}, s = {};
650
+ for (const a in e) {
651
+ const i = e[a];
652
+ r[i.uid] = 0, s[i.uid] = 0;
653
+ }
654
+ for (const a in t) {
655
+ const i = t[a];
656
+ r[i.buffer.uid] += J(i.format).stride;
657
+ }
658
+ for (const a in t) {
659
+ const i = t[a];
660
+ i.stride ?? (i.stride = r[i.buffer.uid]), i.start ?? (i.start = s[i.buffer.uid]), s[i.buffer.uid] += J(i.format).stride;
661
+ }
662
+ }
663
+ const _ = [];
664
+ _[f.NONE] = void 0;
665
+ _[f.DISABLED] = {
666
+ stencilWriteMask: 0,
667
+ stencilReadMask: 0
668
+ };
669
+ _[f.RENDERING_MASK_ADD] = {
670
+ stencilFront: {
671
+ compare: "equal",
672
+ passOp: "increment-clamp"
673
+ },
674
+ stencilBack: {
675
+ compare: "equal",
676
+ passOp: "increment-clamp"
677
+ }
678
+ };
679
+ _[f.RENDERING_MASK_REMOVE] = {
680
+ stencilFront: {
681
+ compare: "equal",
682
+ passOp: "decrement-clamp"
683
+ },
684
+ stencilBack: {
685
+ compare: "equal",
686
+ passOp: "decrement-clamp"
687
+ }
688
+ };
689
+ _[f.MASK_ACTIVE] = {
690
+ stencilWriteMask: 0,
691
+ stencilFront: {
692
+ compare: "equal",
693
+ passOp: "keep"
694
+ },
695
+ stencilBack: {
696
+ compare: "equal",
697
+ passOp: "keep"
698
+ }
699
+ };
700
+ _[f.INVERSE_MASK_ACTIVE] = {
701
+ stencilWriteMask: 0,
702
+ stencilFront: {
703
+ compare: "not-equal",
704
+ passOp: "keep"
705
+ },
706
+ stencilBack: {
707
+ compare: "not-equal",
708
+ passOp: "keep"
709
+ }
710
+ };
711
+ class Dt {
712
+ constructor(e) {
713
+ this._syncFunctionHash = /* @__PURE__ */ Object.create(null), this._adaptor = e, this._systemCheck();
714
+ }
715
+ /**
716
+ * Overridable function by `pixi.js/unsafe-eval` to silence
717
+ * throwing an error if platform doesn't support unsafe-evals.
718
+ * @private
719
+ */
720
+ _systemCheck() {
721
+ if (!Ne())
722
+ throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support.");
723
+ }
724
+ ensureUniformGroup(e) {
725
+ const t = this.getUniformGroupData(e);
726
+ e.buffer || (e.buffer = new X({
727
+ data: new Float32Array(t.layout.size / 4),
728
+ usage: k.UNIFORM | k.COPY_DST
729
+ }));
730
+ }
731
+ getUniformGroupData(e) {
732
+ return this._syncFunctionHash[e._signature] || this._initUniformGroup(e);
733
+ }
734
+ _initUniformGroup(e) {
735
+ const t = e._signature;
736
+ let r = this._syncFunctionHash[t];
737
+ if (!r) {
738
+ const s = Object.keys(e.uniformStructures).map((o) => e.uniformStructures[o]), a = this._adaptor.createUboElements(s), i = this._generateUboSync(a.uboElements);
739
+ r = this._syncFunctionHash[t] = {
740
+ layout: a,
741
+ syncFunction: i
742
+ };
743
+ }
744
+ return this._syncFunctionHash[t];
745
+ }
746
+ _generateUboSync(e) {
747
+ return this._adaptor.generateUboSync(e);
748
+ }
749
+ syncUniformGroup(e, t, r) {
750
+ const s = this.getUniformGroupData(e);
751
+ e.buffer || (e.buffer = new X({
752
+ data: new Float32Array(s.layout.size / 4),
753
+ usage: k.UNIFORM | k.COPY_DST
754
+ }));
755
+ let a = null;
756
+ return t || (t = e.buffer.data, a = e.buffer.dataInt32), r || (r = 0), s.syncFunction(e.uniforms, t, a, r), !0;
757
+ }
758
+ updateUniformGroup(e) {
759
+ if (e.isStatic && !e._dirtyId)
760
+ return !1;
761
+ e._dirtyId = 0;
762
+ const t = this.syncUniformGroup(e);
763
+ return e.buffer.update(), t;
764
+ }
765
+ destroy() {
766
+ this._syncFunctionHash = null;
767
+ }
768
+ }
769
+ const C = [
770
+ // uploading pixi matrix object to mat3
771
+ {
772
+ type: "mat3x3<f32>",
773
+ test: (n) => n.value.a !== void 0,
774
+ ubo: `
775
+ var matrix = uv[name].toArray(true);
776
+ data[offset] = matrix[0];
777
+ data[offset + 1] = matrix[1];
778
+ data[offset + 2] = matrix[2];
779
+ data[offset + 4] = matrix[3];
780
+ data[offset + 5] = matrix[4];
781
+ data[offset + 6] = matrix[5];
782
+ data[offset + 8] = matrix[6];
783
+ data[offset + 9] = matrix[7];
784
+ data[offset + 10] = matrix[8];
785
+ `,
786
+ uniform: `
787
+ gl.uniformMatrix3fv(ud[name].location, false, uv[name].toArray(true));
788
+ `
789
+ },
790
+ // uploading a pixi rectangle as a vec4
791
+ {
792
+ type: "vec4<f32>",
793
+ test: (n) => n.type === "vec4<f32>" && n.size === 1 && n.value.width !== void 0,
794
+ ubo: `
795
+ v = uv[name];
796
+ data[offset] = v.x;
797
+ data[offset + 1] = v.y;
798
+ data[offset + 2] = v.width;
799
+ data[offset + 3] = v.height;
800
+ `,
801
+ uniform: `
802
+ cv = ud[name].value;
803
+ v = uv[name];
804
+ if (cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height) {
805
+ cv[0] = v.x;
806
+ cv[1] = v.y;
807
+ cv[2] = v.width;
808
+ cv[3] = v.height;
809
+ gl.uniform4f(ud[name].location, v.x, v.y, v.width, v.height);
810
+ }
811
+ `
812
+ },
813
+ // uploading a pixi point as a vec2
814
+ {
815
+ type: "vec2<f32>",
816
+ test: (n) => n.type === "vec2<f32>" && n.size === 1 && n.value.x !== void 0,
817
+ ubo: `
818
+ v = uv[name];
819
+ data[offset] = v.x;
820
+ data[offset + 1] = v.y;
821
+ `,
822
+ uniform: `
823
+ cv = ud[name].value;
824
+ v = uv[name];
825
+ if (cv[0] !== v.x || cv[1] !== v.y) {
826
+ cv[0] = v.x;
827
+ cv[1] = v.y;
828
+ gl.uniform2f(ud[name].location, v.x, v.y);
829
+ }
830
+ `
831
+ },
832
+ // uploading a pixi color as a vec4
833
+ {
834
+ type: "vec4<f32>",
835
+ test: (n) => n.type === "vec4<f32>" && n.size === 1 && n.value.red !== void 0,
836
+ ubo: `
837
+ v = uv[name];
838
+ data[offset] = v.red;
839
+ data[offset + 1] = v.green;
840
+ data[offset + 2] = v.blue;
841
+ data[offset + 3] = v.alpha;
842
+ `,
843
+ uniform: `
844
+ cv = ud[name].value;
845
+ v = uv[name];
846
+ if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue || cv[3] !== v.alpha) {
847
+ cv[0] = v.red;
848
+ cv[1] = v.green;
849
+ cv[2] = v.blue;
850
+ cv[3] = v.alpha;
851
+ gl.uniform4f(ud[name].location, v.red, v.green, v.blue, v.alpha);
852
+ }
853
+ `
854
+ },
855
+ // uploading a pixi color as a vec3
856
+ {
857
+ type: "vec3<f32>",
858
+ test: (n) => n.type === "vec3<f32>" && n.size === 1 && n.value.red !== void 0,
859
+ ubo: `
860
+ v = uv[name];
861
+ data[offset] = v.red;
862
+ data[offset + 1] = v.green;
863
+ data[offset + 2] = v.blue;
864
+ `,
865
+ uniform: `
866
+ cv = ud[name].value;
867
+ v = uv[name];
868
+ if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue) {
869
+ cv[0] = v.red;
870
+ cv[1] = v.green;
871
+ cv[2] = v.blue;
872
+ gl.uniform3f(ud[name].location, v.red, v.green, v.blue);
873
+ }
874
+ `
875
+ }
876
+ ];
877
+ function Ot(n, e, t, r) {
878
+ const s = [`
879
+ var v = null;
880
+ var v2 = null;
881
+ var t = 0;
882
+ var index = 0;
883
+ var name = null;
884
+ var arrayOffset = null;
885
+ `];
886
+ let a = 0;
887
+ for (let o = 0; o < n.length; o++) {
888
+ const l = n[o], d = l.data.name;
889
+ let c = !1, h = 0;
890
+ for (let p = 0; p < C.length; p++)
891
+ if (C[p].test(l.data)) {
892
+ h = l.offset / 4, s.push(
893
+ `name = "${d}";`,
894
+ `offset += ${h - a};`,
895
+ C[p][e] || C[p].ubo
896
+ ), c = !0;
897
+ break;
898
+ }
899
+ if (!c)
900
+ if (l.data.size > 1)
901
+ h = l.offset / 4, s.push(t(l, h - a));
902
+ else {
903
+ const p = r[l.data.type];
904
+ h = l.offset / 4, s.push(
905
+ /* wgsl */
906
+ `
907
+ v = uv.${d};
908
+ offset += ${h - a};
909
+ ${p};
910
+ `
911
+ );
912
+ }
913
+ a = h;
914
+ }
915
+ const i = s.join(`
916
+ `);
917
+ return new Function(
918
+ "uv",
919
+ "data",
920
+ "dataInt32",
921
+ "offset",
922
+ i
923
+ );
924
+ }
925
+ function g(n, e) {
926
+ return `
927
+ for (let i = 0; i < ${n * e}; i++) {
928
+ data[offset + (((i / ${n})|0) * 4) + (i % ${n})] = v[i];
929
+ }
930
+ `;
931
+ }
932
+ const ut = {
933
+ f32: `
934
+ data[offset] = v;`,
935
+ i32: `
936
+ dataInt32[offset] = v;`,
937
+ "vec2<f32>": `
938
+ data[offset] = v[0];
939
+ data[offset + 1] = v[1];`,
940
+ "vec3<f32>": `
941
+ data[offset] = v[0];
942
+ data[offset + 1] = v[1];
943
+ data[offset + 2] = v[2];`,
944
+ "vec4<f32>": `
945
+ data[offset] = v[0];
946
+ data[offset + 1] = v[1];
947
+ data[offset + 2] = v[2];
948
+ data[offset + 3] = v[3];`,
949
+ "vec2<i32>": `
950
+ dataInt32[offset] = v[0];
951
+ dataInt32[offset + 1] = v[1];`,
952
+ "vec3<i32>": `
953
+ dataInt32[offset] = v[0];
954
+ dataInt32[offset + 1] = v[1];
955
+ dataInt32[offset + 2] = v[2];`,
956
+ "vec4<i32>": `
957
+ dataInt32[offset] = v[0];
958
+ dataInt32[offset + 1] = v[1];
959
+ dataInt32[offset + 2] = v[2];
960
+ dataInt32[offset + 3] = v[3];`,
961
+ "mat2x2<f32>": `
962
+ data[offset] = v[0];
963
+ data[offset + 1] = v[1];
964
+ data[offset + 4] = v[2];
965
+ data[offset + 5] = v[3];`,
966
+ "mat3x3<f32>": `
967
+ data[offset] = v[0];
968
+ data[offset + 1] = v[1];
969
+ data[offset + 2] = v[2];
970
+ data[offset + 4] = v[3];
971
+ data[offset + 5] = v[4];
972
+ data[offset + 6] = v[5];
973
+ data[offset + 8] = v[6];
974
+ data[offset + 9] = v[7];
975
+ data[offset + 10] = v[8];`,
976
+ "mat4x4<f32>": `
977
+ for (let i = 0; i < 16; i++) {
978
+ data[offset + i] = v[i];
979
+ }`,
980
+ "mat3x2<f32>": g(3, 2),
981
+ "mat4x2<f32>": g(4, 2),
982
+ "mat2x3<f32>": g(2, 3),
983
+ "mat4x3<f32>": g(4, 3),
984
+ "mat2x4<f32>": g(2, 4),
985
+ "mat3x4<f32>": g(3, 4)
986
+ }, Ft = {
987
+ ...ut,
988
+ "mat2x2<f32>": `
989
+ data[offset] = v[0];
990
+ data[offset + 1] = v[1];
991
+ data[offset + 2] = v[2];
992
+ data[offset + 3] = v[3];
993
+ `
994
+ };
995
+ function dt(n, e, t, r, s, a) {
996
+ const i = a ? 1 : -1;
997
+ return n.identity(), n.a = 1 / r * 2, n.d = i * (1 / s * 2), n.tx = -1 - e * n.a, n.ty = -i - t * n.d, n;
998
+ }
999
+ const x = /* @__PURE__ */ new Map();
1000
+ je.register(x);
1001
+ function ye(n, e) {
1002
+ if (!x.has(n)) {
1003
+ const t = new m({
1004
+ source: new U({
1005
+ resource: n,
1006
+ ...e
1007
+ })
1008
+ }), r = () => {
1009
+ x.get(n) === t && x.delete(n);
1010
+ };
1011
+ t.once("destroy", r), t.source.once("destroy", r), x.set(n, t);
1012
+ }
1013
+ return x.get(n);
1014
+ }
1015
+ function ct(n) {
1016
+ const e = n.colorTexture.source.resource;
1017
+ return globalThis.HTMLCanvasElement && e instanceof HTMLCanvasElement && document.body.contains(e);
1018
+ }
1019
+ const ke = class Ce {
1020
+ /**
1021
+ * @param [descriptor] - Options for creating a render target.
1022
+ */
1023
+ constructor(e = {}) {
1024
+ if (this.uid = M("renderTarget"), this.colorTextures = [], this.dirtyId = 0, this.isRoot = !1, this._size = new Float32Array(2), this._managedColorTextures = !1, e = { ...Ce.defaultOptions, ...e }, this.stencil = e.stencil, this.depth = e.depth, this.isRoot = e.isRoot, typeof e.colorTextures == "number") {
1025
+ this._managedColorTextures = !0;
1026
+ for (let t = 0; t < e.colorTextures; t++)
1027
+ this.colorTextures.push(
1028
+ new T({
1029
+ width: e.width,
1030
+ height: e.height,
1031
+ resolution: e.resolution,
1032
+ antialias: e.antialias
1033
+ })
1034
+ );
1035
+ } else {
1036
+ this.colorTextures = [...e.colorTextures.map((r) => r.source)];
1037
+ const t = this.colorTexture.source;
1038
+ this.resize(t.width, t.height, t._resolution);
1039
+ }
1040
+ this.colorTexture.source.on("resize", this.onSourceResize, this), (e.depthStencilTexture || this.stencil) && (e.depthStencilTexture instanceof m || e.depthStencilTexture instanceof T ? this.depthStencilTexture = e.depthStencilTexture.source : this.ensureDepthStencilTexture());
1041
+ }
1042
+ get size() {
1043
+ const e = this._size;
1044
+ return e[0] = this.pixelWidth, e[1] = this.pixelHeight, e;
1045
+ }
1046
+ get width() {
1047
+ return this.colorTexture.source.width;
1048
+ }
1049
+ get height() {
1050
+ return this.colorTexture.source.height;
1051
+ }
1052
+ get pixelWidth() {
1053
+ return this.colorTexture.source.pixelWidth;
1054
+ }
1055
+ get pixelHeight() {
1056
+ return this.colorTexture.source.pixelHeight;
1057
+ }
1058
+ get resolution() {
1059
+ return this.colorTexture.source._resolution;
1060
+ }
1061
+ get colorTexture() {
1062
+ return this.colorTextures[0];
1063
+ }
1064
+ onSourceResize(e) {
1065
+ this.resize(e.width, e.height, e._resolution, !0);
1066
+ }
1067
+ /**
1068
+ * This will ensure a depthStencil texture is created for this render target.
1069
+ * Most likely called by the mask system to make sure we have stencil buffer added.
1070
+ * @internal
1071
+ */
1072
+ ensureDepthStencilTexture() {
1073
+ this.depthStencilTexture || (this.depthStencilTexture = new T({
1074
+ width: this.width,
1075
+ height: this.height,
1076
+ resolution: this.resolution,
1077
+ format: "depth24plus-stencil8",
1078
+ autoGenerateMipmaps: !1,
1079
+ antialias: !1,
1080
+ mipLevelCount: 1
1081
+ // sampleCount: handled by the render target system..
1082
+ }));
1083
+ }
1084
+ resize(e, t, r = this.resolution, s = !1) {
1085
+ this.dirtyId++, this.colorTextures.forEach((a, i) => {
1086
+ s && i === 0 || a.source.resize(e, t, r);
1087
+ }), this.depthStencilTexture && this.depthStencilTexture.source.resize(e, t, r);
1088
+ }
1089
+ destroy() {
1090
+ this.colorTexture.source.off("resize", this.onSourceResize, this), this._managedColorTextures && this.colorTextures.forEach((e) => {
1091
+ e.destroy();
1092
+ }), this.depthStencilTexture && (this.depthStencilTexture.destroy(), delete this.depthStencilTexture);
1093
+ }
1094
+ };
1095
+ ke.defaultOptions = {
1096
+ /** the width of the RenderTarget */
1097
+ width: 0,
1098
+ /** the height of the RenderTarget */
1099
+ height: 0,
1100
+ /** the resolution of the RenderTarget */
1101
+ resolution: 1,
1102
+ /** an array of textures, or a number indicating how many color textures there should be */
1103
+ colorTextures: 1,
1104
+ /** should this render target have a stencil buffer? */
1105
+ stencil: !1,
1106
+ /** should this render target have a depth buffer? */
1107
+ depth: !1,
1108
+ /** should this render target be antialiased? */
1109
+ antialias: !1,
1110
+ // save on perf by default!
1111
+ /** is this a root element, true if this is gl context owners render target */
1112
+ isRoot: !1
1113
+ };
1114
+ let G = ke;
1115
+ class Lt {
1116
+ constructor(e) {
1117
+ this.rootViewPort = new w(), this.viewport = new w(), this.onRenderTargetChange = new $e("onRenderTargetChange"), this.projectionMatrix = new v(), this.defaultClearColor = [0, 0, 0, 0], this._renderSurfaceToRenderTargetHash = /* @__PURE__ */ new Map(), this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null), this._renderTargetStack = [], this._renderer = e, e.renderableGC.addManagedHash(this, "_gpuRenderTargetHash");
1118
+ }
1119
+ /** called when dev wants to finish a render pass */
1120
+ finishRenderPass() {
1121
+ this.adaptor.finishRenderPass(this.renderTarget);
1122
+ }
1123
+ /**
1124
+ * called when the renderer starts to render a scene.
1125
+ * @param options
1126
+ * @param options.target - the render target to render to
1127
+ * @param options.clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
1128
+ * @param options.clearColor - the color to clear to
1129
+ * @param options.frame - the frame to render to
1130
+ */
1131
+ renderStart({
1132
+ target: e,
1133
+ clear: t,
1134
+ clearColor: r,
1135
+ frame: s
1136
+ }) {
1137
+ var a, i;
1138
+ this._renderTargetStack.length = 0, this.push(
1139
+ e,
1140
+ t,
1141
+ r,
1142
+ s
1143
+ ), this.rootViewPort.copyFrom(this.viewport), this.rootRenderTarget = this.renderTarget, this.renderingToScreen = ct(this.rootRenderTarget), (i = (a = this.adaptor).prerender) == null || i.call(a, this.rootRenderTarget);
1144
+ }
1145
+ postrender() {
1146
+ var e, t;
1147
+ (t = (e = this.adaptor).postrender) == null || t.call(e, this.rootRenderTarget);
1148
+ }
1149
+ /**
1150
+ * Binding a render surface! This is the main function of the render target system.
1151
+ * It will take the RenderSurface (which can be a texture, canvas, or render target) and bind it to the renderer.
1152
+ * Once bound all draw calls will be rendered to the render surface.
1153
+ *
1154
+ * If a frame is not provide and the render surface is a texture, the frame of the texture will be used.
1155
+ * @param renderSurface - the render surface to bind
1156
+ * @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
1157
+ * @param clearColor - the color to clear to
1158
+ * @param frame - the frame to render to
1159
+ * @returns the render target that was bound
1160
+ */
1161
+ bind(e, t = !0, r, s) {
1162
+ const a = this.getRenderTarget(e), i = this.renderTarget !== a;
1163
+ this.renderTarget = a, this.renderSurface = e;
1164
+ const o = this.getGpuRenderTarget(a);
1165
+ (a.pixelWidth !== o.width || a.pixelHeight !== o.height) && (this.adaptor.resizeGpuRenderTarget(a), o.width = a.pixelWidth, o.height = a.pixelHeight);
1166
+ const l = a.colorTexture, d = this.viewport, c = l.pixelWidth, h = l.pixelHeight;
1167
+ if (!s && e instanceof m && (s = e.frame), s) {
1168
+ const p = l._resolution;
1169
+ d.x = s.x * p + 0.5 | 0, d.y = s.y * p + 0.5 | 0, d.width = s.width * p + 0.5 | 0, d.height = s.height * p + 0.5 | 0;
1170
+ } else
1171
+ d.x = 0, d.y = 0, d.width = c, d.height = h;
1172
+ return dt(
1173
+ this.projectionMatrix,
1174
+ 0,
1175
+ 0,
1176
+ d.width / l.resolution,
1177
+ d.height / l.resolution,
1178
+ !a.isRoot
1179
+ ), this.adaptor.startRenderPass(a, t, r, d), i && this.onRenderTargetChange.emit(a), a;
1180
+ }
1181
+ clear(e, t = B.ALL, r) {
1182
+ t && (e && (e = this.getRenderTarget(e)), this.adaptor.clear(
1183
+ e || this.renderTarget,
1184
+ t,
1185
+ r,
1186
+ this.viewport
1187
+ ));
1188
+ }
1189
+ contextChange() {
1190
+ this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
1191
+ }
1192
+ /**
1193
+ * Push a render surface to the renderer. This will bind the render surface to the renderer,
1194
+ * @param renderSurface - the render surface to push
1195
+ * @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
1196
+ * @param clearColor - the color to clear to
1197
+ * @param frame - the frame to use when rendering to the render surface
1198
+ */
1199
+ push(e, t = B.ALL, r, s) {
1200
+ const a = this.bind(e, t, r, s);
1201
+ return this._renderTargetStack.push({
1202
+ renderTarget: a,
1203
+ frame: s
1204
+ }), a;
1205
+ }
1206
+ /** Pops the current render target from the renderer and restores the previous render target. */
1207
+ pop() {
1208
+ this._renderTargetStack.pop();
1209
+ const e = this._renderTargetStack[this._renderTargetStack.length - 1];
1210
+ this.bind(e.renderTarget, !1, null, e.frame);
1211
+ }
1212
+ /**
1213
+ * Gets the render target from the provide render surface. Eg if its a texture,
1214
+ * it will return the render target for the texture.
1215
+ * If its a render target, it will return the same render target.
1216
+ * @param renderSurface - the render surface to get the render target for
1217
+ * @returns the render target for the render surface
1218
+ */
1219
+ getRenderTarget(e) {
1220
+ return e.isTexture && (e = e.source), this._renderSurfaceToRenderTargetHash.get(e) ?? this._initRenderTarget(e);
1221
+ }
1222
+ /**
1223
+ * Copies a render surface to another texture.
1224
+ *
1225
+ * NOTE:
1226
+ * for sourceRenderSurfaceTexture, The render target must be something that is written too by the renderer
1227
+ *
1228
+ * The following is not valid:
1229
+ * @example
1230
+ * const canvas = document.createElement('canvas')
1231
+ * canvas.width = 200;
1232
+ * canvas.height = 200;
1233
+ *
1234
+ * const ctx = canvas2.getContext('2d')!
1235
+ * ctx.fillStyle = 'red'
1236
+ * ctx.fillRect(0, 0, 200, 200);
1237
+ *
1238
+ * const texture = RenderTexture.create({
1239
+ * width: 200,
1240
+ * height: 200,
1241
+ * })
1242
+ * const renderTarget = renderer.renderTarget.getRenderTarget(canvas2);
1243
+ *
1244
+ * renderer.renderTarget.copyToTexture(renderTarget,texture, {x:0,y:0},{width:200,height:200},{x:0,y:0});
1245
+ *
1246
+ * The best way to copy a canvas is to create a texture from it. Then render with that.
1247
+ *
1248
+ * Parsing in a RenderTarget canvas context (with a 2d context)
1249
+ * @param sourceRenderSurfaceTexture - the render surface to copy from
1250
+ * @param destinationTexture - the texture to copy to
1251
+ * @param originSrc - the origin of the copy
1252
+ * @param originSrc.x - the x origin of the copy
1253
+ * @param originSrc.y - the y origin of the copy
1254
+ * @param size - the size of the copy
1255
+ * @param size.width - the width of the copy
1256
+ * @param size.height - the height of the copy
1257
+ * @param originDest - the destination origin (top left to paste from!)
1258
+ * @param originDest.x - the x origin of the paste
1259
+ * @param originDest.y - the y origin of the paste
1260
+ */
1261
+ copyToTexture(e, t, r, s, a) {
1262
+ r.x < 0 && (s.width += r.x, a.x -= r.x, r.x = 0), r.y < 0 && (s.height += r.y, a.y -= r.y, r.y = 0);
1263
+ const { pixelWidth: i, pixelHeight: o } = e;
1264
+ return s.width = Math.min(s.width, i - r.x), s.height = Math.min(s.height, o - r.y), this.adaptor.copyToTexture(
1265
+ e,
1266
+ t,
1267
+ r,
1268
+ s,
1269
+ a
1270
+ );
1271
+ }
1272
+ /**
1273
+ * ensures that we have a depth stencil buffer available to render to
1274
+ * This is used by the mask system to make sure we have a stencil buffer.
1275
+ */
1276
+ ensureDepthStencil() {
1277
+ this.renderTarget.stencil || (this.renderTarget.stencil = !0, this.adaptor.startRenderPass(this.renderTarget, !1, null, this.viewport));
1278
+ }
1279
+ /** nukes the render target system */
1280
+ destroy() {
1281
+ this._renderer = null, this._renderSurfaceToRenderTargetHash.forEach((e, t) => {
1282
+ e !== t && e.destroy();
1283
+ }), this._renderSurfaceToRenderTargetHash.clear(), this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
1284
+ }
1285
+ _initRenderTarget(e) {
1286
+ let t = null;
1287
+ return U.test(e) && (e = ye(e).source), e instanceof G ? t = e : e instanceof T && (t = new G({
1288
+ colorTextures: [e]
1289
+ }), e.source instanceof U && (t.isRoot = !0), e.once("destroy", () => {
1290
+ t.destroy(), this._renderSurfaceToRenderTargetHash.delete(e);
1291
+ const r = this._gpuRenderTargetHash[t.uid];
1292
+ r && (this._gpuRenderTargetHash[t.uid] = null, this.adaptor.destroyGpuRenderTarget(r));
1293
+ })), this._renderSurfaceToRenderTargetHash.set(e, t), t;
1294
+ }
1295
+ getGpuRenderTarget(e) {
1296
+ return this._gpuRenderTargetHash[e.uid] || (this._gpuRenderTargetHash[e.uid] = this.adaptor.initGpuRenderTarget(e));
1297
+ }
1298
+ resetState() {
1299
+ this.renderTarget = null, this.renderSurface = null;
1300
+ }
1301
+ }
1302
+ class Ht extends qe {
1303
+ /**
1304
+ * Create a new Buffer Resource.
1305
+ * @param options - The options for the buffer resource
1306
+ * @param options.buffer - The underlying buffer that this resource is using
1307
+ * @param options.offset - The offset of the buffer this resource is using.
1308
+ * If not provided, then it will use the offset of the buffer.
1309
+ * @param options.size - The size of the buffer this resource is using.
1310
+ * If not provided, then it will use the size of the buffer.
1311
+ */
1312
+ constructor({ buffer: e, offset: t, size: r }) {
1313
+ super(), this.uid = M("buffer"), this._resourceType = "bufferResource", this._touched = 0, this._resourceId = M("resource"), this._bufferResource = !0, this.destroyed = !1, this.buffer = e, this.offset = t | 0, this.size = r, this.buffer.on("change", this.onBufferChange, this);
1314
+ }
1315
+ onBufferChange() {
1316
+ this._resourceId = M("resource"), this.emit("change", this);
1317
+ }
1318
+ /**
1319
+ * Destroys this resource. Make sure the underlying buffer is not used anywhere else
1320
+ * if you want to destroy it as well, or code will explode
1321
+ * @param destroyBuffer - Should the underlying buffer be destroyed as well?
1322
+ */
1323
+ destroy(e = !1) {
1324
+ this.destroyed = !0, e && this.buffer.destroy(), this.emit("change", this), this.buffer = null;
1325
+ }
1326
+ }
1327
+ class Me {
1328
+ constructor(e) {
1329
+ this._renderer = e;
1330
+ }
1331
+ updateRenderable() {
1332
+ }
1333
+ destroyRenderable() {
1334
+ }
1335
+ validateRenderable() {
1336
+ return !1;
1337
+ }
1338
+ addRenderable(e, t) {
1339
+ this._renderer.renderPipes.batch.break(t), t.add(e);
1340
+ }
1341
+ execute(e) {
1342
+ e.isRenderable && e.render(this._renderer);
1343
+ }
1344
+ destroy() {
1345
+ this._renderer = null;
1346
+ }
1347
+ }
1348
+ Me.extension = {
1349
+ type: [
1350
+ u.WebGLPipes,
1351
+ u.WebGPUPipes,
1352
+ u.CanvasPipes
1353
+ ],
1354
+ name: "customRender"
1355
+ };
1356
+ function E(n, e) {
1357
+ const t = n.instructionSet, r = t.instructions;
1358
+ for (let s = 0; s < t.instructionSize; s++) {
1359
+ const a = r[s];
1360
+ e[a.renderPipeId].execute(a);
1361
+ }
1362
+ }
1363
+ const ht = new v();
1364
+ class Se {
1365
+ constructor(e) {
1366
+ this._renderer = e;
1367
+ }
1368
+ addRenderGroup(e, t) {
1369
+ e.isCachedAsTexture ? this._addRenderableCacheAsTexture(e, t) : this._addRenderableDirect(e, t);
1370
+ }
1371
+ execute(e) {
1372
+ e.isRenderable && (e.isCachedAsTexture ? this._executeCacheAsTexture(e) : this._executeDirect(e));
1373
+ }
1374
+ destroy() {
1375
+ this._renderer = null;
1376
+ }
1377
+ _addRenderableDirect(e, t) {
1378
+ this._renderer.renderPipes.batch.break(t), e._batchableRenderGroup && (S.return(e._batchableRenderGroup), e._batchableRenderGroup = null), t.add(e);
1379
+ }
1380
+ _addRenderableCacheAsTexture(e, t) {
1381
+ const r = e._batchableRenderGroup ?? (e._batchableRenderGroup = S.get(me));
1382
+ r.renderable = e.root, r.transform = e.root.relativeGroupTransform, r.texture = e.texture, r.bounds = e._textureBounds, t.add(e), this._renderer.renderPipes.blendMode.pushBlendMode(e, e.root.groupBlendMode, t), this._renderer.renderPipes.batch.addToBatch(r, t), this._renderer.renderPipes.blendMode.popBlendMode(t);
1383
+ }
1384
+ _executeCacheAsTexture(e) {
1385
+ if (e.textureNeedsUpdate) {
1386
+ e.textureNeedsUpdate = !1;
1387
+ const t = ht.identity().translate(
1388
+ -e._textureBounds.x,
1389
+ -e._textureBounds.y
1390
+ );
1391
+ this._renderer.renderTarget.push(e.texture, !0, null, e.texture.frame), this._renderer.globalUniforms.push({
1392
+ worldTransformMatrix: t,
1393
+ worldColor: 4294967295,
1394
+ offset: { x: 0, y: 0 }
1395
+ }), E(e, this._renderer.renderPipes), this._renderer.renderTarget.finishRenderPass(), this._renderer.renderTarget.pop(), this._renderer.globalUniforms.pop();
1396
+ }
1397
+ e._batchableRenderGroup._batcher.updateElement(e._batchableRenderGroup), e._batchableRenderGroup._batcher.geometry.buffers[0].update();
1398
+ }
1399
+ _executeDirect(e) {
1400
+ this._renderer.globalUniforms.push({
1401
+ worldTransformMatrix: e.inverseParentTextureTransform,
1402
+ worldColor: e.worldColorAlpha
1403
+ }), E(e, this._renderer.renderPipes), this._renderer.globalUniforms.pop();
1404
+ }
1405
+ }
1406
+ Se.extension = {
1407
+ type: [
1408
+ u.WebGLPipes,
1409
+ u.WebGPUPipes,
1410
+ u.CanvasPipes
1411
+ ],
1412
+ name: "renderGroup"
1413
+ };
1414
+ function D(n, e) {
1415
+ e || (e = 0);
1416
+ for (let t = e; t < n.length && n[t]; t++)
1417
+ n[t] = null;
1418
+ }
1419
+ const ft = new P(), re = ce | he | fe;
1420
+ function we(n, e = !1) {
1421
+ pt(n);
1422
+ const t = n.childrenToUpdate, r = n.updateTick++;
1423
+ for (const s in t) {
1424
+ const a = Number(s), i = t[s], o = i.list, l = i.index;
1425
+ for (let d = 0; d < l; d++) {
1426
+ const c = o[d];
1427
+ c.parentRenderGroup === n && c.relativeRenderGroupDepth === a && Pe(c, r, 0);
1428
+ }
1429
+ D(o, l), i.index = 0;
1430
+ }
1431
+ if (e)
1432
+ for (let s = 0; s < n.renderGroupChildren.length; s++)
1433
+ we(n.renderGroupChildren[s], e);
1434
+ }
1435
+ function pt(n) {
1436
+ const e = n.root;
1437
+ let t;
1438
+ if (n.renderGroupParent) {
1439
+ const r = n.renderGroupParent;
1440
+ n.worldTransform.appendFrom(
1441
+ e.relativeGroupTransform,
1442
+ r.worldTransform
1443
+ ), n.worldColor = de(
1444
+ e.groupColor,
1445
+ r.worldColor
1446
+ ), t = e.groupAlpha * r.worldAlpha;
1447
+ } else
1448
+ n.worldTransform.copyFrom(e.localTransform), n.worldColor = e.localColor, t = e.localAlpha;
1449
+ t = t < 0 ? 0 : t > 1 ? 1 : t, n.worldAlpha = t, n.worldColorAlpha = n.worldColor + ((t * 255 | 0) << 24);
1450
+ }
1451
+ function Pe(n, e, t) {
1452
+ if (e === n.updateTick)
1453
+ return;
1454
+ n.updateTick = e, n.didChange = !1;
1455
+ const r = n.localTransform;
1456
+ n.updateLocalTransform();
1457
+ const s = n.parent;
1458
+ if (s && !s.renderGroup ? (t |= n._updateFlags, n.relativeGroupTransform.appendFrom(
1459
+ r,
1460
+ s.relativeGroupTransform
1461
+ ), t & re && se(n, s, t)) : (t = n._updateFlags, n.relativeGroupTransform.copyFrom(r), t & re && se(n, ft, t)), !n.renderGroup) {
1462
+ const a = n.children, i = a.length;
1463
+ for (let d = 0; d < i; d++)
1464
+ Pe(a[d], e, t);
1465
+ const o = n.parentRenderGroup, l = n;
1466
+ l.renderPipeId && !o.structureDidChange && o.updateRenderable(l);
1467
+ }
1468
+ }
1469
+ function se(n, e, t) {
1470
+ if (t & he) {
1471
+ n.groupColor = de(
1472
+ n.localColor,
1473
+ e.groupColor
1474
+ );
1475
+ let r = n.localAlpha * e.groupAlpha;
1476
+ r = r < 0 ? 0 : r > 1 ? 1 : r, n.groupAlpha = r, n.groupColorAlpha = n.groupColor + ((r * 255 | 0) << 24);
1477
+ }
1478
+ t & fe && (n.groupBlendMode = n.localBlendMode === "inherit" ? e.groupBlendMode : n.localBlendMode), t & ce && (n.globalDisplayStatus = n.localDisplayStatus & e.globalDisplayStatus), n._updateFlags = 0;
1479
+ }
1480
+ function mt(n, e) {
1481
+ const { list: t, index: r } = n.childrenRenderablesToUpdate;
1482
+ let s = !1;
1483
+ for (let a = 0; a < r; a++) {
1484
+ const i = t[a];
1485
+ if (s = e[i.renderPipeId].validateRenderable(i), s)
1486
+ break;
1487
+ }
1488
+ return n.structureDidChange = s, s;
1489
+ }
1490
+ const vt = new v();
1491
+ class Re {
1492
+ constructor(e) {
1493
+ this._renderer = e;
1494
+ }
1495
+ render({ container: e, transform: t }) {
1496
+ const r = e.parent, s = e.renderGroup.renderGroupParent;
1497
+ e.parent = null, e.renderGroup.renderGroupParent = null;
1498
+ const a = this._renderer, i = vt;
1499
+ t && (i.copyFrom(e.renderGroup.localTransform), e.renderGroup.localTransform.copyFrom(t));
1500
+ const o = a.renderPipes;
1501
+ this._updateCachedRenderGroups(e.renderGroup, null), this._updateRenderGroups(e.renderGroup), a.globalUniforms.start({
1502
+ worldTransformMatrix: t ? e.renderGroup.localTransform : e.renderGroup.worldTransform,
1503
+ worldColor: e.renderGroup.worldColorAlpha
1504
+ }), E(e.renderGroup, o), o.uniformBatch && o.uniformBatch.renderEnd(), t && e.renderGroup.localTransform.copyFrom(i), e.parent = r, e.renderGroup.renderGroupParent = s;
1505
+ }
1506
+ destroy() {
1507
+ this._renderer = null;
1508
+ }
1509
+ _updateCachedRenderGroups(e, t) {
1510
+ if (e._parentCacheAsTextureRenderGroup = t, e.isCachedAsTexture) {
1511
+ if (!e.textureNeedsUpdate)
1512
+ return;
1513
+ t = e;
1514
+ }
1515
+ for (let r = e.renderGroupChildren.length - 1; r >= 0; r--)
1516
+ this._updateCachedRenderGroups(e.renderGroupChildren[r], t);
1517
+ if (e.invalidateMatrices(), e.isCachedAsTexture) {
1518
+ if (e.textureNeedsUpdate) {
1519
+ const r = e.root.getLocalBounds();
1520
+ r.ceil();
1521
+ const s = e.texture;
1522
+ e.texture && b.returnTexture(e.texture, !0);
1523
+ const a = this._renderer, i = e.textureOptions.resolution || a.view.resolution, o = e.textureOptions.antialias ?? a.view.antialias, l = e.textureOptions.scaleMode ?? "linear", d = b.getOptimalTexture(
1524
+ r.width,
1525
+ r.height,
1526
+ i,
1527
+ o
1528
+ );
1529
+ d._source.style = new Ke({ scaleMode: l }), e.texture = d, e._textureBounds || (e._textureBounds = new F()), e._textureBounds.copyFrom(r), s !== e.texture && e.renderGroupParent && (e.renderGroupParent.structureDidChange = !0);
1530
+ }
1531
+ } else e.texture && (b.returnTexture(e.texture, !0), e.texture = null);
1532
+ }
1533
+ _updateRenderGroups(e) {
1534
+ const t = this._renderer, r = t.renderPipes;
1535
+ if (e.runOnRender(t), e.instructionSet.renderPipes = r, e.structureDidChange ? D(e.childrenRenderablesToUpdate.list, 0) : mt(e, r), we(e), e.structureDidChange ? (e.structureDidChange = !1, this._buildInstructions(e, t)) : this._updateRenderables(e), e.childrenRenderablesToUpdate.index = 0, t.renderPipes.batch.upload(e.instructionSet), !(e.isCachedAsTexture && !e.textureNeedsUpdate))
1536
+ for (let s = 0; s < e.renderGroupChildren.length; s++)
1537
+ this._updateRenderGroups(e.renderGroupChildren[s]);
1538
+ }
1539
+ _updateRenderables(e) {
1540
+ const { list: t, index: r } = e.childrenRenderablesToUpdate;
1541
+ for (let s = 0; s < r; s++) {
1542
+ const a = t[s];
1543
+ a.didViewUpdate && e.updateRenderable(a);
1544
+ }
1545
+ D(t, r);
1546
+ }
1547
+ _buildInstructions(e, t) {
1548
+ const r = e.root, s = e.instructionSet;
1549
+ s.reset();
1550
+ const a = t.renderPipes ? t : t.batch.renderer, i = a.renderPipes;
1551
+ i.batch.buildStart(s), i.blendMode.buildStart(), i.colorMask.buildStart(), r.sortableChildren && r.sortChildren(), r.collectRenderablesWithEffects(s, a, null), i.batch.buildEnd(s), i.blendMode.buildEnd(s);
1552
+ }
1553
+ }
1554
+ Re.extension = {
1555
+ type: [
1556
+ u.WebGLSystem,
1557
+ u.WebGPUSystem,
1558
+ u.CanvasSystem
1559
+ ],
1560
+ name: "renderGroup"
1561
+ };
1562
+ class Be {
1563
+ constructor(e) {
1564
+ this._renderer = e;
1565
+ }
1566
+ addRenderable(e, t) {
1567
+ const r = this._getGpuSprite(e);
1568
+ e.didViewUpdate && this._updateBatchableSprite(e, r), this._renderer.renderPipes.batch.addToBatch(r, t);
1569
+ }
1570
+ updateRenderable(e) {
1571
+ const t = this._getGpuSprite(e);
1572
+ e.didViewUpdate && this._updateBatchableSprite(e, t), t._batcher.updateElement(t);
1573
+ }
1574
+ validateRenderable(e) {
1575
+ const t = this._getGpuSprite(e);
1576
+ return !t._batcher.checkAndUpdateTexture(
1577
+ t,
1578
+ e._texture
1579
+ );
1580
+ }
1581
+ _updateBatchableSprite(e, t) {
1582
+ t.bounds = e.visualBounds, t.texture = e._texture;
1583
+ }
1584
+ _getGpuSprite(e) {
1585
+ return e._gpuData[this._renderer.uid] || this._initGPUSprite(e);
1586
+ }
1587
+ _initGPUSprite(e) {
1588
+ const t = new me();
1589
+ return t.renderable = e, t.transform = e.groupTransform, t.texture = e._texture, t.bounds = e.visualBounds, t.roundPixels = this._renderer._roundPixels | e._roundPixels, e._gpuData[this._renderer.uid] = t, t;
1590
+ }
1591
+ destroy() {
1592
+ this._renderer = null;
1593
+ }
1594
+ }
1595
+ Be.extension = {
1596
+ type: [
1597
+ u.WebGLPipes,
1598
+ u.WebGPUPipes,
1599
+ u.CanvasPipes
1600
+ ],
1601
+ name: "sprite"
1602
+ };
1603
+ const V = class Ue {
1604
+ constructor() {
1605
+ this.clearBeforeRender = !0, this._backgroundColor = new A(0), this.color = this._backgroundColor, this.alpha = 1;
1606
+ }
1607
+ /**
1608
+ * initiates the background system
1609
+ * @param options - the options for the background colors
1610
+ */
1611
+ init(e) {
1612
+ e = { ...Ue.defaultOptions, ...e }, this.clearBeforeRender = e.clearBeforeRender, this.color = e.background || e.backgroundColor || this._backgroundColor, this.alpha = e.backgroundAlpha, this._backgroundColor.setAlpha(e.backgroundAlpha);
1613
+ }
1614
+ /** The background color to fill if not transparent */
1615
+ get color() {
1616
+ return this._backgroundColor;
1617
+ }
1618
+ set color(e) {
1619
+ A.shared.setValue(e).alpha < 1 && this._backgroundColor.alpha === 1 && H(
1620
+ "Cannot set a transparent background on an opaque canvas. To enable transparency, set backgroundAlpha < 1 when initializing your Application."
1621
+ ), this._backgroundColor.setValue(e);
1622
+ }
1623
+ /** The background color alpha. Setting this to 0 will make the canvas transparent. */
1624
+ get alpha() {
1625
+ return this._backgroundColor.alpha;
1626
+ }
1627
+ set alpha(e) {
1628
+ this._backgroundColor.setAlpha(e);
1629
+ }
1630
+ /** The background color as an [R, G, B, A] array. */
1631
+ get colorRgba() {
1632
+ return this._backgroundColor.toArray();
1633
+ }
1634
+ /**
1635
+ * destroys the background system
1636
+ * @internal
1637
+ */
1638
+ destroy() {
1639
+ }
1640
+ };
1641
+ V.extension = {
1642
+ type: [
1643
+ u.WebGLSystem,
1644
+ u.WebGPUSystem,
1645
+ u.CanvasSystem
1646
+ ],
1647
+ name: "background",
1648
+ priority: 0
1649
+ };
1650
+ V.defaultOptions = {
1651
+ /**
1652
+ * {@link WebGLOptions.backgroundAlpha}
1653
+ * @default 1
1654
+ */
1655
+ backgroundAlpha: 1,
1656
+ /**
1657
+ * {@link WebGLOptions.backgroundColor}
1658
+ * @default 0x000000
1659
+ */
1660
+ backgroundColor: 0,
1661
+ /**
1662
+ * {@link WebGLOptions.clearBeforeRender}
1663
+ * @default true
1664
+ */
1665
+ clearBeforeRender: !0
1666
+ };
1667
+ let gt = V;
1668
+ const y = {};
1669
+ O.handle(u.BlendMode, (n) => {
1670
+ if (!n.name)
1671
+ throw new Error("BlendMode extension must have a name property");
1672
+ y[n.name] = n.ref;
1673
+ }, (n) => {
1674
+ delete y[n.name];
1675
+ });
1676
+ class Ae {
1677
+ constructor(e) {
1678
+ this._blendModeStack = [], this._isAdvanced = !1, this._filterHash = /* @__PURE__ */ Object.create(null), this._renderer = e, this._renderer.runners.prerender.add(this);
1679
+ }
1680
+ prerender() {
1681
+ this._activeBlendMode = "normal", this._isAdvanced = !1;
1682
+ }
1683
+ /**
1684
+ * Push a blend mode onto the internal stack and apply it to the instruction set if needed.
1685
+ * @param renderable - The renderable or {@link RenderGroup} associated with the change.
1686
+ * @param blendMode - The blend mode to activate.
1687
+ * @param instructionSet - The instruction set being built.
1688
+ */
1689
+ pushBlendMode(e, t, r) {
1690
+ this._blendModeStack.push(t), this.setBlendMode(e, t, r);
1691
+ }
1692
+ /**
1693
+ * Pop the last blend mode from the stack and apply the new top-of-stack mode.
1694
+ * @param instructionSet - The instruction set being built.
1695
+ */
1696
+ popBlendMode(e) {
1697
+ this._blendModeStack.pop();
1698
+ const t = this._blendModeStack[this._activeBlendMode.length - 1] ?? "normal";
1699
+ this.setBlendMode(null, t, e);
1700
+ }
1701
+ /**
1702
+ * Ensure a blend mode switch is added to the instruction set when the mode changes.
1703
+ * If an advanced blend mode is active, subsequent renderables will be collected so they can be
1704
+ * rendered within a single filter pass.
1705
+ * @param renderable - The renderable or {@link RenderGroup} to associate with the change, or null when unwinding.
1706
+ * @param blendMode - The target blend mode.
1707
+ * @param instructionSet - The instruction set being built.
1708
+ */
1709
+ setBlendMode(e, t, r) {
1710
+ var a;
1711
+ const s = e instanceof Q;
1712
+ if (this._activeBlendMode === t) {
1713
+ this._isAdvanced && e && !s && ((a = this._renderableList) == null || a.push(e));
1714
+ return;
1715
+ }
1716
+ this._isAdvanced && this._endAdvancedBlendMode(r), this._activeBlendMode = t, e && (this._isAdvanced = !!y[t], this._isAdvanced && this._beginAdvancedBlendMode(e, r));
1717
+ }
1718
+ _beginAdvancedBlendMode(e, t) {
1719
+ this._renderer.renderPipes.batch.break(t);
1720
+ const r = this._activeBlendMode;
1721
+ if (!y[r]) {
1722
+ H(`Unable to assign BlendMode: '${r}'. You may want to include: import 'pixi.js/advanced-blend-modes'`);
1723
+ return;
1724
+ }
1725
+ const s = this._ensureFilterEffect(r), a = e instanceof Q, i = {
1726
+ renderPipeId: "filter",
1727
+ action: "pushFilter",
1728
+ filterEffect: s,
1729
+ renderables: a ? null : [e],
1730
+ container: a ? e.root : null,
1731
+ canBundle: !1
1732
+ };
1733
+ this._renderableList = i.renderables, t.add(i);
1734
+ }
1735
+ _ensureFilterEffect(e) {
1736
+ let t = this._filterHash[e];
1737
+ return t || (t = this._filterHash[e] = new ue(), t.filters = [new y[e]()]), t;
1738
+ }
1739
+ _endAdvancedBlendMode(e) {
1740
+ this._isAdvanced = !1, this._renderableList = null, this._renderer.renderPipes.batch.break(e), e.add({
1741
+ renderPipeId: "filter",
1742
+ action: "popFilter",
1743
+ canBundle: !1
1744
+ });
1745
+ }
1746
+ /**
1747
+ * called when the instruction build process is starting this will reset internally to the default blend mode
1748
+ * @internal
1749
+ */
1750
+ buildStart() {
1751
+ this._isAdvanced = !1;
1752
+ }
1753
+ /**
1754
+ * called when the instruction build process is finished, ensuring that if there is an advanced blend mode
1755
+ * active, we add the final render instructions added to the instruction set
1756
+ * @param instructionSet - The instruction set we are adding to
1757
+ * @internal
1758
+ */
1759
+ buildEnd(e) {
1760
+ this._isAdvanced && this._endAdvancedBlendMode(e);
1761
+ }
1762
+ /** @internal */
1763
+ destroy() {
1764
+ this._renderer = null, this._renderableList = null;
1765
+ for (const e in this._filterHash)
1766
+ this._filterHash[e].destroy();
1767
+ this._filterHash = null;
1768
+ }
1769
+ }
1770
+ Ae.extension = {
1771
+ type: [
1772
+ u.WebGLPipes,
1773
+ u.WebGPUPipes,
1774
+ u.CanvasPipes
1775
+ ],
1776
+ name: "blendMode"
1777
+ };
1778
+ const R = {
1779
+ png: "image/png",
1780
+ jpg: "image/jpeg",
1781
+ webp: "image/webp"
1782
+ }, N = class Ie {
1783
+ /** @param renderer - The renderer this System works for. */
1784
+ constructor(e) {
1785
+ this._renderer = e;
1786
+ }
1787
+ _normalizeOptions(e, t = {}) {
1788
+ return e instanceof P || e instanceof m ? {
1789
+ target: e,
1790
+ ...t
1791
+ } : {
1792
+ ...t,
1793
+ ...e
1794
+ };
1795
+ }
1796
+ /**
1797
+ * Creates an IImage from a display object or texture.
1798
+ * @param options - Options for creating the image, or the target to extract
1799
+ * @returns Promise that resolves with the generated IImage
1800
+ * @example
1801
+ * ```ts
1802
+ * // Basic usage with a sprite
1803
+ * const sprite = new Sprite(texture);
1804
+ * const image = await renderer.extract.image(sprite);
1805
+ * document.body.appendChild(image);
1806
+ *
1807
+ * // Advanced usage with options
1808
+ * const image = await renderer.extract.image({
1809
+ * target: container,
1810
+ * format: 'webp',
1811
+ * quality: 0.8,
1812
+ * frame: new Rectangle(0, 0, 100, 100),
1813
+ * resolution: 2,
1814
+ * clearColor: '#ff0000',
1815
+ * antialias: true
1816
+ * });
1817
+ *
1818
+ * // Extract directly from a texture
1819
+ * const texture = Texture.from('myTexture.png');
1820
+ * const image = await renderer.extract.image(texture);
1821
+ * ```
1822
+ * @see {@link ExtractImageOptions} For detailed options
1823
+ * @see {@link ExtractSystem.base64} For base64 string output
1824
+ * @see {@link ExtractSystem.canvas} For canvas output
1825
+ * @see {@link ImageLike} For the image interface
1826
+ * @category rendering
1827
+ */
1828
+ async image(e) {
1829
+ const t = z.get().createImage();
1830
+ return t.src = await this.base64(e), t;
1831
+ }
1832
+ /**
1833
+ * Converts the target into a base64 encoded string.
1834
+ *
1835
+ * This method works by first creating
1836
+ * a canvas using `Extract.canvas` and then converting it to a base64 string.
1837
+ * @param options - The options for creating the base64 string, or the target to extract
1838
+ * @returns Promise that resolves with the base64 encoded string
1839
+ * @example
1840
+ * ```ts
1841
+ * // Basic usage with a sprite
1842
+ * const sprite = new Sprite(texture);
1843
+ * const base64 = await renderer.extract.base64(sprite);
1844
+ * console.log(base64); // data:image/png;base64,...
1845
+ *
1846
+ * // Advanced usage with options
1847
+ * const base64 = await renderer.extract.base64({
1848
+ * target: container,
1849
+ * format: 'webp',
1850
+ * quality: 0.8,
1851
+ * frame: new Rectangle(0, 0, 100, 100),
1852
+ * resolution: 2
1853
+ * });
1854
+ * ```
1855
+ * @throws Will throw an error if the platform doesn't support any of:
1856
+ * - ICanvas.toDataURL
1857
+ * - ICanvas.toBlob
1858
+ * - ICanvas.convertToBlob
1859
+ * @see {@link ExtractImageOptions} For detailed options
1860
+ * @see {@link ExtractSystem.canvas} For canvas output
1861
+ * @see {@link ExtractSystem.image} For HTMLImage output
1862
+ * @category rendering
1863
+ */
1864
+ async base64(e) {
1865
+ e = this._normalizeOptions(
1866
+ e,
1867
+ Ie.defaultImageOptions
1868
+ );
1869
+ const { format: t, quality: r } = e, s = this.canvas(e);
1870
+ if (s.toBlob !== void 0)
1871
+ return new Promise((a, i) => {
1872
+ s.toBlob((o) => {
1873
+ if (!o) {
1874
+ i(new Error("ICanvas.toBlob failed!"));
1875
+ return;
1876
+ }
1877
+ const l = new FileReader();
1878
+ l.onload = () => a(l.result), l.onerror = i, l.readAsDataURL(o);
1879
+ }, R[t], r);
1880
+ });
1881
+ if (s.toDataURL !== void 0)
1882
+ return s.toDataURL(R[t], r);
1883
+ if (s.convertToBlob !== void 0) {
1884
+ const a = await s.convertToBlob({ type: R[t], quality: r });
1885
+ return new Promise((i, o) => {
1886
+ const l = new FileReader();
1887
+ l.onload = () => i(l.result), l.onerror = o, l.readAsDataURL(a);
1888
+ });
1889
+ }
1890
+ throw new Error("Extract.base64() requires ICanvas.toDataURL, ICanvas.toBlob, or ICanvas.convertToBlob to be implemented");
1891
+ }
1892
+ /**
1893
+ * Creates a Canvas element, renders the target to it and returns it.
1894
+ * This method is useful for creating static images or when you need direct canvas access.
1895
+ * @param options - The options for creating the canvas, or the target to extract
1896
+ * @returns A Canvas element with the texture rendered on
1897
+ * @example
1898
+ * ```ts
1899
+ * // Basic canvas extraction from a sprite
1900
+ * const sprite = new Sprite(texture);
1901
+ * const canvas = renderer.extract.canvas(sprite);
1902
+ * document.body.appendChild(canvas);
1903
+ *
1904
+ * // Extract with custom region
1905
+ * const canvas = renderer.extract.canvas({
1906
+ * target: container,
1907
+ * frame: new Rectangle(0, 0, 100, 100)
1908
+ * });
1909
+ *
1910
+ * // Extract with high resolution
1911
+ * const canvas = renderer.extract.canvas({
1912
+ * target: sprite,
1913
+ * resolution: 2,
1914
+ * clearColor: '#ff0000'
1915
+ * });
1916
+ *
1917
+ * // Extract directly from a texture
1918
+ * const texture = Texture.from('myTexture.png');
1919
+ * const canvas = renderer.extract.canvas(texture);
1920
+ *
1921
+ * // Extract with anti-aliasing
1922
+ * const canvas = renderer.extract.canvas({
1923
+ * target: graphics,
1924
+ * antialias: true
1925
+ * });
1926
+ * ```
1927
+ * @see {@link ExtractOptions} For detailed options
1928
+ * @see {@link ExtractSystem.image} For HTMLImage output
1929
+ * @see {@link ExtractSystem.pixels} For raw pixel data
1930
+ * @category rendering
1931
+ */
1932
+ canvas(e) {
1933
+ e = this._normalizeOptions(e);
1934
+ const t = e.target, r = this._renderer;
1935
+ if (t instanceof m)
1936
+ return r.texture.generateCanvas(t);
1937
+ const s = r.textureGenerator.generateTexture(e), a = r.texture.generateCanvas(s);
1938
+ return s.destroy(!0), a;
1939
+ }
1940
+ /**
1941
+ * Returns a one-dimensional array containing the pixel data of the entire texture in RGBA order,
1942
+ * with integer values between 0 and 255 (inclusive).
1943
+ * > [!NOE] The returned array is a flat Uint8Array where every 4 values represent RGBA
1944
+ * @param options - The options for extracting the image, or the target to extract
1945
+ * @returns One-dimensional Uint8Array containing the pixel data in RGBA format
1946
+ * @example
1947
+ * ```ts
1948
+ * // Basic pixel extraction
1949
+ * const sprite = new Sprite(texture);
1950
+ * const pixels = renderer.extract.pixels(sprite);
1951
+ * console.log(pixels[0], pixels[1], pixels[2], pixels[3]); // R,G,B,A values
1952
+ *
1953
+ * // Extract with custom region
1954
+ * const pixels = renderer.extract.pixels({
1955
+ * target: sprite,
1956
+ * frame: new Rectangle(0, 0, 100, 100)
1957
+ * });
1958
+ *
1959
+ * // Extract with high resolution
1960
+ * const pixels = renderer.extract.pixels({
1961
+ * target: sprite,
1962
+ * resolution: 2
1963
+ * });
1964
+ * ```
1965
+ * @see {@link ExtractOptions} For detailed options
1966
+ * @see {@link ExtractSystem.canvas} For canvas output
1967
+ * @see {@link ExtractSystem.image} For image output
1968
+ * @category rendering
1969
+ */
1970
+ pixels(e) {
1971
+ e = this._normalizeOptions(e);
1972
+ const t = e.target, r = this._renderer, s = t instanceof m ? t : r.textureGenerator.generateTexture(e), a = r.texture.getPixels(s);
1973
+ return t instanceof P && s.destroy(!0), a;
1974
+ }
1975
+ /**
1976
+ * Creates a texture from a display object or existing texture.
1977
+ *
1978
+ * This is useful for creating
1979
+ * reusable textures from rendered content or making copies of existing textures.
1980
+ * > [!NOTE] The returned texture should be destroyed when no longer needed
1981
+ * @param options - The options for creating the texture, or the target to extract
1982
+ * @returns A new texture containing the extracted content
1983
+ * @example
1984
+ * ```ts
1985
+ * // Basic texture extraction from a sprite
1986
+ * const sprite = new Sprite(texture);
1987
+ * const extractedTexture = renderer.extract.texture(sprite);
1988
+ *
1989
+ * // Extract with custom region
1990
+ * const regionTexture = renderer.extract.texture({
1991
+ * target: container,
1992
+ * frame: new Rectangle(0, 0, 100, 100)
1993
+ * });
1994
+ *
1995
+ * // Extract with high resolution
1996
+ * const hiResTexture = renderer.extract.texture({
1997
+ * target: sprite,
1998
+ * resolution: 2,
1999
+ * clearColor: '#ff0000'
2000
+ * });
2001
+ *
2002
+ * // Create a new sprite from extracted texture
2003
+ * const newSprite = new Sprite(
2004
+ * renderer.extract.texture({
2005
+ * target: graphics,
2006
+ * antialias: true
2007
+ * })
2008
+ * );
2009
+ *
2010
+ * // Clean up when done
2011
+ * extractedTexture.destroy(true);
2012
+ * ```
2013
+ * @see {@link ExtractOptions} For detailed options
2014
+ * @see {@link Texture} For texture management
2015
+ * @see {@link GenerateTextureSystem} For texture generation
2016
+ * @category rendering
2017
+ */
2018
+ texture(e) {
2019
+ return e = this._normalizeOptions(e), e.target instanceof m ? e.target : this._renderer.textureGenerator.generateTexture(e);
2020
+ }
2021
+ /**
2022
+ * Extracts and downloads content from the renderer as an image file.
2023
+ * This is a convenient way to save screenshots or export rendered content.
2024
+ * > [!NOTE] The download will use PNG format regardless of the filename extension
2025
+ * @param options - The options for downloading and extracting the image, or the target to extract
2026
+ * @example
2027
+ * ```ts
2028
+ * // Basic download with default filename
2029
+ * const sprite = new Sprite(texture);
2030
+ * renderer.extract.download(sprite); // Downloads as 'image.png'
2031
+ *
2032
+ * // Download with custom filename
2033
+ * renderer.extract.download({
2034
+ * target: sprite,
2035
+ * filename: 'screenshot.png'
2036
+ * });
2037
+ *
2038
+ * // Download with custom region
2039
+ * renderer.extract.download({
2040
+ * target: container,
2041
+ * filename: 'region.png',
2042
+ * frame: new Rectangle(0, 0, 100, 100)
2043
+ * });
2044
+ *
2045
+ * // Download with high resolution and background
2046
+ * renderer.extract.download({
2047
+ * target: stage,
2048
+ * filename: 'hd-screenshot.png',
2049
+ * resolution: 2,
2050
+ * clearColor: '#ff0000'
2051
+ * });
2052
+ *
2053
+ * // Download with anti-aliasing
2054
+ * renderer.extract.download({
2055
+ * target: graphics,
2056
+ * filename: 'smooth.png',
2057
+ * antialias: true
2058
+ * });
2059
+ * ```
2060
+ * @see {@link ExtractDownloadOptions} For detailed options
2061
+ * @see {@link ExtractSystem.image} For creating images without download
2062
+ * @see {@link ExtractSystem.canvas} For canvas output
2063
+ * @category rendering
2064
+ */
2065
+ download(e) {
2066
+ e = this._normalizeOptions(e);
2067
+ const t = this.canvas(e), r = document.createElement("a");
2068
+ r.download = e.filename ?? "image.png", r.href = t.toDataURL("image/png"), document.body.appendChild(r), r.click(), document.body.removeChild(r);
2069
+ }
2070
+ /**
2071
+ * Logs the target to the console as an image. This is a useful way to debug what's happening in the renderer.
2072
+ * The image will be displayed in the browser's console using CSS background images.
2073
+ * @param options - The options for logging the image, or the target to log
2074
+ * @param options.width - The width of the logged image preview in the console (in pixels)
2075
+ * @example
2076
+ * ```ts
2077
+ * // Basic usage
2078
+ * const sprite = new Sprite(texture);
2079
+ * renderer.extract.log(sprite);
2080
+ * ```
2081
+ * @see {@link ExtractSystem.canvas} For getting raw canvas output
2082
+ * @see {@link ExtractSystem.pixels} For raw pixel data
2083
+ * @category rendering
2084
+ * @advanced
2085
+ */
2086
+ log(e) {
2087
+ const t = e.width ?? 200;
2088
+ e = this._normalizeOptions(e);
2089
+ const r = this.canvas(e), s = r.toDataURL();
2090
+ console.log(`[Pixi Texture] ${r.width}px ${r.height}px`);
2091
+ const a = [
2092
+ "font-size: 1px;",
2093
+ `padding: ${t}px 300px;`,
2094
+ `background: url(${s}) no-repeat;`,
2095
+ "background-size: contain;"
2096
+ ].join(" ");
2097
+ console.log("%c ", a);
2098
+ }
2099
+ destroy() {
2100
+ this._renderer = null;
2101
+ }
2102
+ };
2103
+ N.extension = {
2104
+ type: [
2105
+ u.WebGLSystem,
2106
+ u.WebGPUSystem
2107
+ ],
2108
+ name: "extract"
2109
+ };
2110
+ N.defaultImageOptions = {
2111
+ format: "png",
2112
+ quality: 1
2113
+ };
2114
+ let xt = N;
2115
+ class j extends m {
2116
+ static create(e) {
2117
+ return new j({
2118
+ source: new T(e)
2119
+ });
2120
+ }
2121
+ /**
2122
+ * Resizes the render texture.
2123
+ * @param width - The new width of the render texture.
2124
+ * @param height - The new height of the render texture.
2125
+ * @param resolution - The new resolution of the render texture.
2126
+ * @returns This texture.
2127
+ */
2128
+ resize(e, t, r) {
2129
+ return this.source.resize(e, t, r), this;
2130
+ }
2131
+ }
2132
+ const _t = new w(), bt = new F(), Tt = [0, 0, 0, 0];
2133
+ class Ge {
2134
+ constructor(e) {
2135
+ this._renderer = e;
2136
+ }
2137
+ /**
2138
+ * Creates a texture from a display object that can be used for creating sprites and other textures.
2139
+ * This is particularly useful for optimizing performance when a complex container needs to be reused.
2140
+ * @param options - Generate texture options or a container to convert to texture
2141
+ * @returns A new RenderTexture containing the rendered display object
2142
+ * @example
2143
+ * ```ts
2144
+ * // Basic usage with a container
2145
+ * const container = new Container();
2146
+ * container.addChild(
2147
+ * new Graphics()
2148
+ * .circle(0, 0, 50)
2149
+ * .fill('red')
2150
+ * );
2151
+ *
2152
+ * const texture = renderer.textureGenerator.generateTexture(container);
2153
+ *
2154
+ * // Advanced usage with options
2155
+ * const texture = renderer.textureGenerator.generateTexture({
2156
+ * target: container,
2157
+ * frame: new Rectangle(0, 0, 100, 100), // Specific region
2158
+ * resolution: 2, // High DPI
2159
+ * clearColor: '#ff0000', // Red background
2160
+ * antialias: true // Smooth edges
2161
+ * });
2162
+ *
2163
+ * // Create a sprite from the generated texture
2164
+ * const sprite = new Sprite(texture);
2165
+ *
2166
+ * // Clean up when done
2167
+ * texture.destroy(true);
2168
+ * ```
2169
+ * @see {@link GenerateTextureOptions} For detailed texture generation options
2170
+ * @see {@link RenderTexture} For the type of texture created
2171
+ * @category rendering
2172
+ */
2173
+ generateTexture(e) {
2174
+ var d;
2175
+ e instanceof P && (e = {
2176
+ target: e,
2177
+ frame: void 0,
2178
+ textureSourceOptions: {},
2179
+ resolution: void 0
2180
+ });
2181
+ const t = e.resolution || this._renderer.resolution, r = e.antialias || this._renderer.view.antialias, s = e.target;
2182
+ let a = e.clearColor;
2183
+ a ? a = Array.isArray(a) && a.length === 4 ? a : A.shared.setValue(a).toArray() : a = Tt;
2184
+ const i = ((d = e.frame) == null ? void 0 : d.copyTo(_t)) || Ye(s, bt).rectangle;
2185
+ i.width = Math.max(i.width, 1 / t) | 0, i.height = Math.max(i.height, 1 / t) | 0;
2186
+ const o = j.create({
2187
+ ...e.textureSourceOptions,
2188
+ width: i.width,
2189
+ height: i.height,
2190
+ resolution: t,
2191
+ antialias: r
2192
+ }), l = v.shared.translate(-i.x, -i.y);
2193
+ return this._renderer.render({
2194
+ container: s,
2195
+ transform: l,
2196
+ target: o,
2197
+ clearColor: a
2198
+ }), o.source.updateMipmaps(), o;
2199
+ }
2200
+ destroy() {
2201
+ this._renderer = null;
2202
+ }
2203
+ }
2204
+ Ge.extension = {
2205
+ type: [
2206
+ u.WebGLSystem,
2207
+ u.WebGPUSystem
2208
+ ],
2209
+ name: "textureGenerator"
2210
+ };
2211
+ class Ee {
2212
+ constructor(e) {
2213
+ this._stackIndex = 0, this._globalUniformDataStack = [], this._uniformsPool = [], this._activeUniforms = [], this._bindGroupPool = [], this._activeBindGroups = [], this._renderer = e;
2214
+ }
2215
+ reset() {
2216
+ this._stackIndex = 0;
2217
+ for (let e = 0; e < this._activeUniforms.length; e++)
2218
+ this._uniformsPool.push(this._activeUniforms[e]);
2219
+ for (let e = 0; e < this._activeBindGroups.length; e++)
2220
+ this._bindGroupPool.push(this._activeBindGroups[e]);
2221
+ this._activeUniforms.length = 0, this._activeBindGroups.length = 0;
2222
+ }
2223
+ start(e) {
2224
+ this.reset(), this.push(e);
2225
+ }
2226
+ bind({
2227
+ size: e,
2228
+ projectionMatrix: t,
2229
+ worldTransformMatrix: r,
2230
+ worldColor: s,
2231
+ offset: a
2232
+ }) {
2233
+ const i = this._renderer.renderTarget.renderTarget, o = this._stackIndex ? this._globalUniformDataStack[this._stackIndex - 1] : {
2234
+ worldTransformMatrix: new v(),
2235
+ worldColor: 4294967295,
2236
+ offset: new Je()
2237
+ }, l = {
2238
+ projectionMatrix: t || this._renderer.renderTarget.projectionMatrix,
2239
+ resolution: e || i.size,
2240
+ worldTransformMatrix: r || o.worldTransformMatrix,
2241
+ worldColor: s || o.worldColor,
2242
+ offset: a || o.offset,
2243
+ bindGroup: null
2244
+ }, d = this._uniformsPool.pop() || this._createUniforms();
2245
+ this._activeUniforms.push(d);
2246
+ const c = d.uniforms;
2247
+ c.uProjectionMatrix = l.projectionMatrix, c.uResolution = l.resolution, c.uWorldTransformMatrix.copyFrom(l.worldTransformMatrix), c.uWorldTransformMatrix.tx -= l.offset.x, c.uWorldTransformMatrix.ty -= l.offset.y, tt(
2248
+ l.worldColor,
2249
+ c.uWorldColorAlpha,
2250
+ 0
2251
+ ), d.update();
2252
+ let h;
2253
+ this._renderer.renderPipes.uniformBatch ? h = this._renderer.renderPipes.uniformBatch.getUniformBindGroup(d, !1) : (h = this._bindGroupPool.pop() || new Xe(), this._activeBindGroups.push(h), h.setResource(d, 0)), l.bindGroup = h, this._currentGlobalUniformData = l;
2254
+ }
2255
+ push(e) {
2256
+ this.bind(e), this._globalUniformDataStack[this._stackIndex++] = this._currentGlobalUniformData;
2257
+ }
2258
+ pop() {
2259
+ this._currentGlobalUniformData = this._globalUniformDataStack[--this._stackIndex - 1], this._renderer.type === L.WEBGL && this._currentGlobalUniformData.bindGroup.resources[0].update();
2260
+ }
2261
+ get bindGroup() {
2262
+ return this._currentGlobalUniformData.bindGroup;
2263
+ }
2264
+ get globalUniformData() {
2265
+ return this._currentGlobalUniformData;
2266
+ }
2267
+ get uniformGroup() {
2268
+ return this._currentGlobalUniformData.bindGroup.resources[0];
2269
+ }
2270
+ _createUniforms() {
2271
+ return new oe({
2272
+ uProjectionMatrix: { value: new v(), type: "mat3x3<f32>" },
2273
+ uWorldTransformMatrix: { value: new v(), type: "mat3x3<f32>" },
2274
+ // TODO - someone smart - set this to be a unorm8x4 rather than a vec4<f32>
2275
+ uWorldColorAlpha: { value: new Float32Array(4), type: "vec4<f32>" },
2276
+ uResolution: { value: [0, 0], type: "vec2<f32>" }
2277
+ }, {
2278
+ isStatic: !0
2279
+ });
2280
+ }
2281
+ destroy() {
2282
+ this._renderer = null, this._globalUniformDataStack.length = 0, this._uniformsPool.length = 0, this._activeUniforms.length = 0, this._bindGroupPool.length = 0, this._activeBindGroups.length = 0, this._currentGlobalUniformData = null;
2283
+ }
2284
+ }
2285
+ Ee.extension = {
2286
+ type: [
2287
+ u.WebGLSystem,
2288
+ u.WebGPUSystem,
2289
+ u.CanvasSystem
2290
+ ],
2291
+ name: "globalUniforms"
2292
+ };
2293
+ let yt = 1;
2294
+ class De {
2295
+ constructor() {
2296
+ this._tasks = [], this._offset = 0;
2297
+ }
2298
+ /** Initializes the scheduler system and starts the ticker. */
2299
+ init() {
2300
+ Z.system.add(this._update, this);
2301
+ }
2302
+ /**
2303
+ * Schedules a repeating task.
2304
+ * @param func - The function to execute.
2305
+ * @param duration - The interval duration in milliseconds.
2306
+ * @param useOffset - this will spread out tasks so that they do not all run at the same time
2307
+ * @returns The unique identifier for the scheduled task.
2308
+ */
2309
+ repeat(e, t, r = !0) {
2310
+ const s = yt++;
2311
+ let a = 0;
2312
+ return r && (this._offset += 1e3, a = this._offset), this._tasks.push({
2313
+ func: e,
2314
+ duration: t,
2315
+ start: performance.now(),
2316
+ offset: a,
2317
+ last: performance.now(),
2318
+ repeat: !0,
2319
+ id: s
2320
+ }), s;
2321
+ }
2322
+ /**
2323
+ * Cancels a scheduled task.
2324
+ * @param id - The unique identifier of the task to cancel.
2325
+ */
2326
+ cancel(e) {
2327
+ for (let t = 0; t < this._tasks.length; t++)
2328
+ if (this._tasks[t].id === e) {
2329
+ this._tasks.splice(t, 1);
2330
+ return;
2331
+ }
2332
+ }
2333
+ /**
2334
+ * Updates and executes the scheduled tasks.
2335
+ * @private
2336
+ */
2337
+ _update() {
2338
+ const e = performance.now();
2339
+ for (let t = 0; t < this._tasks.length; t++) {
2340
+ const r = this._tasks[t];
2341
+ if (e - r.offset - r.last >= r.duration) {
2342
+ const s = e - r.start;
2343
+ r.func(s), r.last = e;
2344
+ }
2345
+ }
2346
+ }
2347
+ /**
2348
+ * Destroys the scheduler system and removes all tasks.
2349
+ * @internal
2350
+ */
2351
+ destroy() {
2352
+ Z.system.remove(this._update, this), this._tasks.length = 0;
2353
+ }
2354
+ }
2355
+ De.extension = {
2356
+ type: [
2357
+ u.WebGLSystem,
2358
+ u.WebGPUSystem,
2359
+ u.CanvasSystem
2360
+ ],
2361
+ name: "scheduler",
2362
+ priority: 0
2363
+ };
2364
+ let ae = !1;
2365
+ function kt(n) {
2366
+ if (!ae) {
2367
+ if (z.get().getNavigator().userAgent.toLowerCase().indexOf("chrome") > -1) {
2368
+ const e = [
2369
+ `%c %c %c %c %c PixiJS %c v${ee} (${n}) http://www.pixijs.com/
2370
+
2371
+ `,
2372
+ "background: #E72264; padding:5px 0;",
2373
+ "background: #6CA2EA; padding:5px 0;",
2374
+ "background: #B5D33D; padding:5px 0;",
2375
+ "background: #FED23F; padding:5px 0;",
2376
+ "color: #FFFFFF; background: #E72264; padding:5px 0;",
2377
+ "color: #E72264; background: #FFFFFF; padding:5px 0;"
2378
+ ];
2379
+ globalThis.console.log(...e);
2380
+ } else globalThis.console && globalThis.console.log(`PixiJS ${ee} - ${n} - http://www.pixijs.com/`);
2381
+ ae = !0;
2382
+ }
2383
+ }
2384
+ class $ {
2385
+ constructor(e) {
2386
+ this._renderer = e;
2387
+ }
2388
+ /**
2389
+ * It all starts here! This initiates every system, passing in the options for any system by name.
2390
+ * @param options - the config for the renderer and all its systems
2391
+ */
2392
+ init(e) {
2393
+ if (e.hello) {
2394
+ let t = this._renderer.name;
2395
+ this._renderer.type === L.WEBGL && (t += ` ${this._renderer.context.webGLVersion}`), kt(t);
2396
+ }
2397
+ }
2398
+ }
2399
+ $.extension = {
2400
+ type: [
2401
+ u.WebGLSystem,
2402
+ u.WebGPUSystem,
2403
+ u.CanvasSystem
2404
+ ],
2405
+ name: "hello",
2406
+ priority: -2
2407
+ };
2408
+ $.defaultOptions = {
2409
+ /** {@link WebGLOptions.hello} */
2410
+ hello: !1
2411
+ };
2412
+ function Ct(n) {
2413
+ let e = !1;
2414
+ for (const r in n)
2415
+ if (n[r] == null) {
2416
+ e = !0;
2417
+ break;
2418
+ }
2419
+ if (!e)
2420
+ return n;
2421
+ const t = /* @__PURE__ */ Object.create(null);
2422
+ for (const r in n) {
2423
+ const s = n[r];
2424
+ s && (t[r] = s);
2425
+ }
2426
+ return t;
2427
+ }
2428
+ function Mt(n) {
2429
+ let e = 0;
2430
+ for (let t = 0; t < n.length; t++)
2431
+ n[t] == null ? e++ : n[t - e] = n[t];
2432
+ return n.length -= e, n;
2433
+ }
2434
+ let St = 0;
2435
+ const q = class Oe {
2436
+ /**
2437
+ * Creates a new RenderableGCSystem instance.
2438
+ * @param renderer - The renderer this garbage collection system works for
2439
+ */
2440
+ constructor(e) {
2441
+ this._managedRenderables = [], this._managedHashes = [], this._managedArrays = [], this._renderer = e;
2442
+ }
2443
+ /**
2444
+ * Initializes the garbage collection system with the provided options.
2445
+ * @param options - Configuration options for the renderer
2446
+ */
2447
+ init(e) {
2448
+ e = { ...Oe.defaultOptions, ...e }, this.maxUnusedTime = e.renderableGCMaxUnusedTime, this._frequency = e.renderableGCFrequency, this.enabled = e.renderableGCActive;
2449
+ }
2450
+ /**
2451
+ * Gets whether the garbage collection system is currently enabled.
2452
+ * @returns True if GC is enabled, false otherwise
2453
+ */
2454
+ get enabled() {
2455
+ return !!this._handler;
2456
+ }
2457
+ /**
2458
+ * Enables or disables the garbage collection system.
2459
+ * When enabled, schedules periodic cleanup of resources.
2460
+ * When disabled, cancels all scheduled cleanups.
2461
+ */
2462
+ set enabled(e) {
2463
+ this.enabled !== e && (e ? (this._handler = this._renderer.scheduler.repeat(
2464
+ () => this.run(),
2465
+ this._frequency,
2466
+ !1
2467
+ ), this._hashHandler = this._renderer.scheduler.repeat(
2468
+ () => {
2469
+ for (const t of this._managedHashes)
2470
+ t.context[t.hash] = Ct(t.context[t.hash]);
2471
+ },
2472
+ this._frequency
2473
+ ), this._arrayHandler = this._renderer.scheduler.repeat(
2474
+ () => {
2475
+ for (const t of this._managedArrays)
2476
+ Mt(t.context[t.hash]);
2477
+ },
2478
+ this._frequency
2479
+ )) : (this._renderer.scheduler.cancel(this._handler), this._renderer.scheduler.cancel(this._hashHandler), this._renderer.scheduler.cancel(this._arrayHandler)));
2480
+ }
2481
+ /**
2482
+ * Adds a hash table to be managed by the garbage collector.
2483
+ * @param context - The object containing the hash table
2484
+ * @param hash - The property name of the hash table
2485
+ */
2486
+ addManagedHash(e, t) {
2487
+ this._managedHashes.push({ context: e, hash: t });
2488
+ }
2489
+ /**
2490
+ * Adds an array to be managed by the garbage collector.
2491
+ * @param context - The object containing the array
2492
+ * @param hash - The property name of the array
2493
+ */
2494
+ addManagedArray(e, t) {
2495
+ this._managedArrays.push({ context: e, hash: t });
2496
+ }
2497
+ /**
2498
+ * Updates the GC timestamp and tracking before rendering.
2499
+ * @param options - The render options
2500
+ * @param options.container - The container to render
2501
+ */
2502
+ prerender({
2503
+ container: e
2504
+ }) {
2505
+ this._now = performance.now(), e.renderGroup.gcTick = St++, this._updateInstructionGCTick(e.renderGroup, e.renderGroup.gcTick);
2506
+ }
2507
+ /**
2508
+ * Starts tracking a renderable for garbage collection.
2509
+ * @param renderable - The renderable to track
2510
+ */
2511
+ addRenderable(e) {
2512
+ this.enabled && (e._lastUsed === -1 && (this._managedRenderables.push(e), e.once("destroyed", this._removeRenderable, this)), e._lastUsed = this._now);
2513
+ }
2514
+ /**
2515
+ * Performs garbage collection by cleaning up unused renderables.
2516
+ * Removes renderables that haven't been used for longer than maxUnusedTime.
2517
+ */
2518
+ run() {
2519
+ var a;
2520
+ const e = this._now, t = this._managedRenderables, r = this._renderer.renderPipes;
2521
+ let s = 0;
2522
+ for (let i = 0; i < t.length; i++) {
2523
+ const o = t[i];
2524
+ if (o === null) {
2525
+ s++;
2526
+ continue;
2527
+ }
2528
+ const l = o.renderGroup ?? o.parentRenderGroup, d = ((a = l == null ? void 0 : l.instructionSet) == null ? void 0 : a.gcTick) ?? -1;
2529
+ if (((l == null ? void 0 : l.gcTick) ?? 0) === d && (o._lastUsed = e), e - o._lastUsed > this.maxUnusedTime) {
2530
+ if (!o.destroyed) {
2531
+ const c = r;
2532
+ l && (l.structureDidChange = !0), c[o.renderPipeId].destroyRenderable(o);
2533
+ }
2534
+ o._lastUsed = -1, s++, o.off("destroyed", this._removeRenderable, this);
2535
+ } else
2536
+ t[i - s] = o;
2537
+ }
2538
+ t.length -= s;
2539
+ }
2540
+ /** Cleans up the garbage collection system. Disables GC and removes all tracked resources. */
2541
+ destroy() {
2542
+ this.enabled = !1, this._renderer = null, this._managedRenderables.length = 0, this._managedHashes.length = 0, this._managedArrays.length = 0;
2543
+ }
2544
+ /**
2545
+ * Removes a renderable from being tracked when it's destroyed.
2546
+ * @param renderable - The renderable to stop tracking
2547
+ */
2548
+ _removeRenderable(e) {
2549
+ const t = this._managedRenderables.indexOf(e);
2550
+ t >= 0 && (e.off("destroyed", this._removeRenderable, this), this._managedRenderables[t] = null);
2551
+ }
2552
+ /**
2553
+ * Updates the GC tick counter for a render group and its children.
2554
+ * @param renderGroup - The render group to update
2555
+ * @param gcTick - The new tick value
2556
+ */
2557
+ _updateInstructionGCTick(e, t) {
2558
+ e.instructionSet.gcTick = t;
2559
+ for (const r of e.renderGroupChildren)
2560
+ this._updateInstructionGCTick(r, t);
2561
+ }
2562
+ };
2563
+ q.extension = {
2564
+ type: [
2565
+ u.WebGLSystem,
2566
+ u.WebGPUSystem
2567
+ ],
2568
+ name: "renderableGC",
2569
+ priority: 0
2570
+ };
2571
+ q.defaultOptions = {
2572
+ /** Enable/disable the garbage collector */
2573
+ renderableGCActive: !0,
2574
+ /** Time in ms before an unused resource is collected (default 1 minute) */
2575
+ renderableGCMaxUnusedTime: 6e4,
2576
+ /** How often to run garbage collection in ms (default 30 seconds) */
2577
+ renderableGCFrequency: 3e4
2578
+ };
2579
+ let wt = q;
2580
+ const K = class Fe {
2581
+ /** @param renderer - The renderer this System works for. */
2582
+ constructor(e) {
2583
+ this._renderer = e, this.count = 0, this.checkCount = 0;
2584
+ }
2585
+ init(e) {
2586
+ e = { ...Fe.defaultOptions, ...e }, this.checkCountMax = e.textureGCCheckCountMax, this.maxIdle = e.textureGCAMaxIdle ?? e.textureGCMaxIdle, this.active = e.textureGCActive;
2587
+ }
2588
+ /**
2589
+ * Checks to see when the last time a texture was used.
2590
+ * If the texture has not been used for a specified amount of time, it will be removed from the GPU.
2591
+ */
2592
+ postrender() {
2593
+ this._renderer.renderingToScreen && (this.count++, this.active && (this.checkCount++, this.checkCount > this.checkCountMax && (this.checkCount = 0, this.run())));
2594
+ }
2595
+ /**
2596
+ * Checks to see when the last time a texture was used.
2597
+ * If the texture has not been used for a specified amount of time, it will be removed from the GPU.
2598
+ */
2599
+ run() {
2600
+ const e = this._renderer.texture.managedTextures;
2601
+ for (let t = 0; t < e.length; t++) {
2602
+ const r = e[t];
2603
+ r.autoGarbageCollect && r.resource && r._touched > -1 && this.count - r._touched > this.maxIdle && (r._touched = -1, r.unload());
2604
+ }
2605
+ }
2606
+ destroy() {
2607
+ this._renderer = null;
2608
+ }
2609
+ };
2610
+ K.extension = {
2611
+ type: [
2612
+ u.WebGLSystem,
2613
+ u.WebGPUSystem
2614
+ ],
2615
+ name: "textureGC"
2616
+ };
2617
+ K.defaultOptions = {
2618
+ /**
2619
+ * If set to true, this will enable the garbage collector on the GPU.
2620
+ * @default true
2621
+ */
2622
+ textureGCActive: !0,
2623
+ /**
2624
+ * @deprecated since 8.3.0
2625
+ * @see {@link TextureGCSystemOptions.textureGCMaxIdle}
2626
+ */
2627
+ textureGCAMaxIdle: null,
2628
+ /**
2629
+ * The maximum idle frames before a texture is destroyed by garbage collection.
2630
+ * @default 60 * 60
2631
+ */
2632
+ textureGCMaxIdle: 60 * 60,
2633
+ /**
2634
+ * Frames between two garbage collections.
2635
+ * @default 600
2636
+ */
2637
+ textureGCCheckCountMax: 600
2638
+ };
2639
+ let Pt = K;
2640
+ const Y = class Le {
2641
+ /**
2642
+ * Whether CSS dimensions of canvas view should be resized to screen dimensions automatically.
2643
+ * This is only supported for HTMLCanvasElement and will be ignored if the canvas is an OffscreenCanvas.
2644
+ * @type {boolean}
2645
+ */
2646
+ get autoDensity() {
2647
+ return this.texture.source.autoDensity;
2648
+ }
2649
+ set autoDensity(e) {
2650
+ this.texture.source.autoDensity = e;
2651
+ }
2652
+ /** The resolution / device pixel ratio of the renderer. */
2653
+ get resolution() {
2654
+ return this.texture.source._resolution;
2655
+ }
2656
+ set resolution(e) {
2657
+ this.texture.source.resize(
2658
+ this.texture.source.width,
2659
+ this.texture.source.height,
2660
+ e
2661
+ );
2662
+ }
2663
+ /**
2664
+ * initiates the view system
2665
+ * @param options - the options for the view
2666
+ */
2667
+ init(e) {
2668
+ e = {
2669
+ ...Le.defaultOptions,
2670
+ ...e
2671
+ }, e.view && (Qe(Ze, "ViewSystem.view has been renamed to ViewSystem.canvas"), e.canvas = e.view), this.screen = new w(0, 0, e.width, e.height), this.canvas = e.canvas || z.get().createCanvas(), this.antialias = !!e.antialias, this.texture = ye(this.canvas, e), this.renderTarget = new G({
2672
+ colorTextures: [this.texture],
2673
+ depth: !!e.depth,
2674
+ isRoot: !0
2675
+ }), this.texture.source.transparent = e.backgroundAlpha < 1, this.resolution = e.resolution;
2676
+ }
2677
+ /**
2678
+ * Resizes the screen and canvas to the specified dimensions.
2679
+ * @param desiredScreenWidth - The new width of the screen.
2680
+ * @param desiredScreenHeight - The new height of the screen.
2681
+ * @param resolution
2682
+ */
2683
+ resize(e, t, r) {
2684
+ this.texture.source.resize(e, t, r), this.screen.width = this.texture.frame.width, this.screen.height = this.texture.frame.height;
2685
+ }
2686
+ /**
2687
+ * Destroys this System and optionally removes the canvas from the dom.
2688
+ * @param {options | false} options - The options for destroying the view, or "false".
2689
+ * @example
2690
+ * viewSystem.destroy();
2691
+ * viewSystem.destroy(true);
2692
+ * viewSystem.destroy({ removeView: true });
2693
+ */
2694
+ destroy(e = !1) {
2695
+ (typeof e == "boolean" ? e : !!(e != null && e.removeView)) && this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas), this.texture.destroy();
2696
+ }
2697
+ };
2698
+ Y.extension = {
2699
+ type: [
2700
+ u.WebGLSystem,
2701
+ u.WebGPUSystem,
2702
+ u.CanvasSystem
2703
+ ],
2704
+ name: "view",
2705
+ priority: 0
2706
+ };
2707
+ Y.defaultOptions = {
2708
+ /**
2709
+ * {@link WebGLOptions.width}
2710
+ * @default 800
2711
+ */
2712
+ width: 800,
2713
+ /**
2714
+ * {@link WebGLOptions.height}
2715
+ * @default 600
2716
+ */
2717
+ height: 600,
2718
+ /**
2719
+ * {@link WebGLOptions.autoDensity}
2720
+ * @default false
2721
+ */
2722
+ autoDensity: !1,
2723
+ /**
2724
+ * {@link WebGLOptions.antialias}
2725
+ * @default false
2726
+ */
2727
+ antialias: !1
2728
+ };
2729
+ let Rt = Y;
2730
+ const zt = [
2731
+ gt,
2732
+ Ee,
2733
+ $,
2734
+ Rt,
2735
+ Re,
2736
+ Pt,
2737
+ Ge,
2738
+ xt,
2739
+ et,
2740
+ wt,
2741
+ De
2742
+ ], Wt = [
2743
+ Ae,
2744
+ xe,
2745
+ Be,
2746
+ Se,
2747
+ _e,
2748
+ Te,
2749
+ be,
2750
+ Me
2751
+ ];
2752
+ export {
2753
+ Ht as B,
2754
+ _ as G,
2755
+ Lt as R,
2756
+ zt as S,
2757
+ Dt as U,
2758
+ Wt as a,
2759
+ ut as b,
2760
+ Ot as c,
2761
+ C as d,
2762
+ Et as e,
2763
+ Gt as f,
2764
+ It as t,
2765
+ Ft as u
2766
+ };
src/backend/gradio_polygonannotator/templates/component/WebGLRenderer-DbjK-Gvj.js ADDED
@@ -0,0 +1,2643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { E as d, B, w as m, D as S, J as K, t as Be, q as U, s as b, a5 as Ae, j as $, a6 as Ne, S as p, O as H, i as A, h as N, k as F, M as z, a7 as Y, a8 as ye, a9 as Ce, aa as q, ab as Ie, A as De, R as Ge, e as T } from "./Index-jQCzN2ap.js";
2
+ import { S as O, b as Z } from "./colorToUniform-zJcCVLeu.js";
3
+ import { e as Ue, G as Fe, c as Oe, b as Pe, U as Me, R as Le, B as J, d as y, f as He, S as we, a as Ve } from "./SharedSystems-DOa2TyTQ.js";
4
+ class Q {
5
+ constructor() {
6
+ this._tempState = O.for2d(), this._didUploadHash = {};
7
+ }
8
+ init(e) {
9
+ e.renderer.runners.contextChange.add(this);
10
+ }
11
+ contextChange() {
12
+ this._didUploadHash = {};
13
+ }
14
+ start(e, r, s) {
15
+ const n = e.renderer, i = this._didUploadHash[s.uid];
16
+ n.shader.bind(s, i), i || (this._didUploadHash[s.uid] = !0), n.shader.updateUniformGroup(n.globalUniforms.uniformGroup), n.geometry.bind(r, s.glProgram);
17
+ }
18
+ execute(e, r) {
19
+ const s = e.renderer;
20
+ this._tempState.blendMode = r.blendMode, s.state.set(this._tempState);
21
+ const n = r.textures.textures;
22
+ for (let i = 0; i < r.textures.count; i++)
23
+ s.texture.bind(n[i], i);
24
+ s.geometry.draw(r.topology, r.size, r.start);
25
+ }
26
+ }
27
+ Q.extension = {
28
+ type: [
29
+ d.WebGLPipesAdaptor
30
+ ],
31
+ name: "batch"
32
+ };
33
+ var x = /* @__PURE__ */ ((t) => (t[t.ELEMENT_ARRAY_BUFFER = 34963] = "ELEMENT_ARRAY_BUFFER", t[t.ARRAY_BUFFER = 34962] = "ARRAY_BUFFER", t[t.UNIFORM_BUFFER = 35345] = "UNIFORM_BUFFER", t))(x || {});
34
+ class ke {
35
+ constructor(e, r) {
36
+ this._lastBindBaseLocation = -1, this._lastBindCallId = -1, this.buffer = e || null, this.updateID = -1, this.byteLength = -1, this.type = r;
37
+ }
38
+ }
39
+ class ee {
40
+ /**
41
+ * @param {Renderer} renderer - The renderer this System works for.
42
+ */
43
+ constructor(e) {
44
+ this._gpuBuffers = /* @__PURE__ */ Object.create(null), this._boundBufferBases = /* @__PURE__ */ Object.create(null), this._minBaseLocation = 0, this._nextBindBaseIndex = this._minBaseLocation, this._bindCallId = 0, this._renderer = e, this._renderer.renderableGC.addManagedHash(this, "_gpuBuffers");
45
+ }
46
+ /** @ignore */
47
+ destroy() {
48
+ this._renderer = null, this._gl = null, this._gpuBuffers = null, this._boundBufferBases = null;
49
+ }
50
+ /** Sets up the renderer context and necessary buffers. */
51
+ contextChange() {
52
+ this._gl = this._renderer.gl, this._gpuBuffers = /* @__PURE__ */ Object.create(null), this._maxBindings = this._renderer.limits.maxUniformBindings;
53
+ }
54
+ getGlBuffer(e) {
55
+ return this._gpuBuffers[e.uid] || this.createGLBuffer(e);
56
+ }
57
+ /**
58
+ * This binds specified buffer. On first run, it will create the webGL buffers for the context too
59
+ * @param buffer - the buffer to bind to the renderer
60
+ */
61
+ bind(e) {
62
+ const { _gl: r } = this, s = this.getGlBuffer(e);
63
+ r.bindBuffer(s.type, s.buffer);
64
+ }
65
+ /**
66
+ * Binds an uniform buffer to at the given index.
67
+ *
68
+ * A cache is used so a buffer will not be bound again if already bound.
69
+ * @param glBuffer - the buffer to bind
70
+ * @param index - the base index to bind it to.
71
+ */
72
+ bindBufferBase(e, r) {
73
+ const { _gl: s } = this;
74
+ this._boundBufferBases[r] !== e && (this._boundBufferBases[r] = e, e._lastBindBaseLocation = r, s.bindBufferBase(s.UNIFORM_BUFFER, r, e.buffer));
75
+ }
76
+ nextBindBase(e) {
77
+ this._bindCallId++, this._minBaseLocation = 0, e && (this._boundBufferBases[0] = null, this._minBaseLocation = 1, this._nextBindBaseIndex < 1 && (this._nextBindBaseIndex = 1));
78
+ }
79
+ freeLocationForBufferBase(e) {
80
+ let r = this.getLastBindBaseLocation(e);
81
+ if (r >= this._minBaseLocation)
82
+ return e._lastBindCallId = this._bindCallId, r;
83
+ let s = 0, n = this._nextBindBaseIndex;
84
+ for (; s < 2; ) {
85
+ n >= this._maxBindings && (n = this._minBaseLocation, s++);
86
+ const i = this._boundBufferBases[n];
87
+ if (i && i._lastBindCallId === this._bindCallId) {
88
+ n++;
89
+ continue;
90
+ }
91
+ break;
92
+ }
93
+ return r = n, this._nextBindBaseIndex = n + 1, s >= 2 ? -1 : (e._lastBindCallId = this._bindCallId, this._boundBufferBases[r] = null, r);
94
+ }
95
+ getLastBindBaseLocation(e) {
96
+ const r = e._lastBindBaseLocation;
97
+ return this._boundBufferBases[r] === e ? r : -1;
98
+ }
99
+ /**
100
+ * Binds a buffer whilst also binding its range.
101
+ * This will make the buffer start from the offset supplied rather than 0 when it is read.
102
+ * @param glBuffer - the buffer to bind
103
+ * @param index - the base index to bind at, defaults to 0
104
+ * @param offset - the offset to bind at (this is blocks of 256). 0 = 0, 1 = 256, 2 = 512 etc
105
+ * @param size - the size to bind at (this is blocks of 256).
106
+ */
107
+ bindBufferRange(e, r, s, n) {
108
+ const { _gl: i } = this;
109
+ s || (s = 0), r || (r = 0), this._boundBufferBases[r] = null, i.bindBufferRange(i.UNIFORM_BUFFER, r || 0, e.buffer, s * 256, n || 256);
110
+ }
111
+ /**
112
+ * Will ensure the data in the buffer is uploaded to the GPU.
113
+ * @param {Buffer} buffer - the buffer to update
114
+ */
115
+ updateBuffer(e) {
116
+ const { _gl: r } = this, s = this.getGlBuffer(e);
117
+ if (e._updateID === s.updateID)
118
+ return s;
119
+ s.updateID = e._updateID, r.bindBuffer(s.type, s.buffer);
120
+ const n = e.data, i = e.descriptor.usage & B.STATIC ? r.STATIC_DRAW : r.DYNAMIC_DRAW;
121
+ return n ? s.byteLength >= n.byteLength ? r.bufferSubData(s.type, 0, n, 0, e._updateSize / n.BYTES_PER_ELEMENT) : (s.byteLength = n.byteLength, r.bufferData(s.type, n, i)) : (s.byteLength = e.descriptor.size, r.bufferData(s.type, s.byteLength, i)), s;
122
+ }
123
+ /** dispose all WebGL resources of all managed buffers */
124
+ destroyAll() {
125
+ const e = this._gl;
126
+ for (const r in this._gpuBuffers)
127
+ e.deleteBuffer(this._gpuBuffers[r].buffer);
128
+ this._gpuBuffers = /* @__PURE__ */ Object.create(null);
129
+ }
130
+ /**
131
+ * Disposes buffer
132
+ * @param {Buffer} buffer - buffer with data
133
+ * @param {boolean} [contextLost=false] - If context was lost, we suppress deleteVertexArray
134
+ */
135
+ onBufferDestroy(e, r) {
136
+ const s = this._gpuBuffers[e.uid], n = this._gl;
137
+ r || n.deleteBuffer(s.buffer), this._gpuBuffers[e.uid] = null;
138
+ }
139
+ /**
140
+ * creates and attaches a GLBuffer object tied to the current context.
141
+ * @param buffer
142
+ * @protected
143
+ */
144
+ createGLBuffer(e) {
145
+ const { _gl: r } = this;
146
+ let s = x.ARRAY_BUFFER;
147
+ e.descriptor.usage & B.INDEX ? s = x.ELEMENT_ARRAY_BUFFER : e.descriptor.usage & B.UNIFORM && (s = x.UNIFORM_BUFFER);
148
+ const n = new ke(r.createBuffer(), s);
149
+ return this._gpuBuffers[e.uid] = n, e.on("destroy", this.onBufferDestroy, this), n;
150
+ }
151
+ resetState() {
152
+ this._boundBufferBases = /* @__PURE__ */ Object.create(null);
153
+ }
154
+ }
155
+ ee.extension = {
156
+ type: [
157
+ d.WebGLSystem
158
+ ],
159
+ name: "buffer"
160
+ };
161
+ const P = class te {
162
+ /** @param renderer - The renderer this System works for. */
163
+ constructor(e) {
164
+ this.supports = {
165
+ /** Support for 32-bit indices buffer. */
166
+ uint32Indices: !0,
167
+ /** Support for UniformBufferObjects */
168
+ uniformBufferObject: !0,
169
+ /** Support for VertexArrayObjects */
170
+ vertexArrayObject: !0,
171
+ /** Support for SRGB texture format */
172
+ srgbTextures: !0,
173
+ /** Support for wrapping modes if a texture is non-power of two */
174
+ nonPowOf2wrapping: !0,
175
+ /** Support for MSAA (antialiasing of dynamic textures) */
176
+ msaa: !0,
177
+ /** Support for mipmaps if a texture is non-power of two */
178
+ nonPowOf2mipmaps: !0
179
+ }, this._renderer = e, this.extensions = /* @__PURE__ */ Object.create(null), this.handleContextLost = this.handleContextLost.bind(this), this.handleContextRestored = this.handleContextRestored.bind(this);
180
+ }
181
+ /**
182
+ * `true` if the context is lost
183
+ * @readonly
184
+ */
185
+ get isLost() {
186
+ return !this.gl || this.gl.isContextLost();
187
+ }
188
+ /**
189
+ * Handles the context change event.
190
+ * @param {WebGLRenderingContext} gl - New WebGL context.
191
+ */
192
+ contextChange(e) {
193
+ this.gl = e, this._renderer.gl = e;
194
+ }
195
+ init(e) {
196
+ e = { ...te.defaultOptions, ...e };
197
+ let r = this.multiView = e.multiView;
198
+ if (e.context && r && (m("Renderer created with both a context and multiview enabled. Disabling multiView as both cannot work together."), r = !1), r ? this.canvas = S.get().createCanvas(this._renderer.canvas.width, this._renderer.canvas.height) : this.canvas = this._renderer.view.canvas, e.context)
199
+ this.initFromContext(e.context);
200
+ else {
201
+ const s = this._renderer.background.alpha < 1, n = e.premultipliedAlpha ?? !0, i = e.antialias && !this._renderer.backBuffer.useBackBuffer;
202
+ this.createContext(e.preferWebGLVersion, {
203
+ alpha: s,
204
+ premultipliedAlpha: n,
205
+ antialias: i,
206
+ stencil: !0,
207
+ preserveDrawingBuffer: e.preserveDrawingBuffer,
208
+ powerPreference: e.powerPreference ?? "default"
209
+ });
210
+ }
211
+ }
212
+ ensureCanvasSize(e) {
213
+ if (!this.multiView) {
214
+ e !== this.canvas && m("multiView is disabled, but targetCanvas is not the main canvas");
215
+ return;
216
+ }
217
+ const { canvas: r } = this;
218
+ (r.width < e.width || r.height < e.height) && (r.width = Math.max(e.width, e.width), r.height = Math.max(e.height, e.height));
219
+ }
220
+ /**
221
+ * Initializes the context.
222
+ * @protected
223
+ * @param {WebGLRenderingContext} gl - WebGL context
224
+ */
225
+ initFromContext(e) {
226
+ this.gl = e, this.webGLVersion = e instanceof S.get().getWebGLRenderingContext() ? 1 : 2, this.getExtensions(), this.validateContext(e), this._renderer.runners.contextChange.emit(e);
227
+ const r = this._renderer.view.canvas;
228
+ r.addEventListener("webglcontextlost", this.handleContextLost, !1), r.addEventListener("webglcontextrestored", this.handleContextRestored, !1);
229
+ }
230
+ /**
231
+ * Initialize from context options
232
+ * @protected
233
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
234
+ * @param preferWebGLVersion
235
+ * @param {object} options - context attributes
236
+ */
237
+ createContext(e, r) {
238
+ let s;
239
+ const n = this.canvas;
240
+ if (e === 2 && (s = n.getContext("webgl2", r)), !s && (s = n.getContext("webgl", r), !s))
241
+ throw new Error("This browser does not support WebGL. Try using the canvas renderer");
242
+ this.gl = s, this.initFromContext(this.gl);
243
+ }
244
+ /** Auto-populate the {@link GlContextSystem.extensions extensions}. */
245
+ getExtensions() {
246
+ const { gl: e } = this, r = {
247
+ anisotropicFiltering: e.getExtension("EXT_texture_filter_anisotropic"),
248
+ floatTextureLinear: e.getExtension("OES_texture_float_linear"),
249
+ s3tc: e.getExtension("WEBGL_compressed_texture_s3tc"),
250
+ s3tc_sRGB: e.getExtension("WEBGL_compressed_texture_s3tc_srgb"),
251
+ // eslint-disable-line camelcase
252
+ etc: e.getExtension("WEBGL_compressed_texture_etc"),
253
+ etc1: e.getExtension("WEBGL_compressed_texture_etc1"),
254
+ pvrtc: e.getExtension("WEBGL_compressed_texture_pvrtc") || e.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"),
255
+ atc: e.getExtension("WEBGL_compressed_texture_atc"),
256
+ astc: e.getExtension("WEBGL_compressed_texture_astc"),
257
+ bptc: e.getExtension("EXT_texture_compression_bptc"),
258
+ rgtc: e.getExtension("EXT_texture_compression_rgtc"),
259
+ loseContext: e.getExtension("WEBGL_lose_context")
260
+ };
261
+ if (this.webGLVersion === 1)
262
+ this.extensions = {
263
+ ...r,
264
+ drawBuffers: e.getExtension("WEBGL_draw_buffers"),
265
+ depthTexture: e.getExtension("WEBGL_depth_texture"),
266
+ vertexArrayObject: e.getExtension("OES_vertex_array_object") || e.getExtension("MOZ_OES_vertex_array_object") || e.getExtension("WEBKIT_OES_vertex_array_object"),
267
+ uint32ElementIndex: e.getExtension("OES_element_index_uint"),
268
+ // Floats and half-floats
269
+ floatTexture: e.getExtension("OES_texture_float"),
270
+ floatTextureLinear: e.getExtension("OES_texture_float_linear"),
271
+ textureHalfFloat: e.getExtension("OES_texture_half_float"),
272
+ textureHalfFloatLinear: e.getExtension("OES_texture_half_float_linear"),
273
+ vertexAttribDivisorANGLE: e.getExtension("ANGLE_instanced_arrays"),
274
+ srgb: e.getExtension("EXT_sRGB")
275
+ };
276
+ else {
277
+ this.extensions = {
278
+ ...r,
279
+ colorBufferFloat: e.getExtension("EXT_color_buffer_float")
280
+ };
281
+ const s = e.getExtension("WEBGL_provoking_vertex");
282
+ s && s.provokingVertexWEBGL(s.FIRST_VERTEX_CONVENTION_WEBGL);
283
+ }
284
+ }
285
+ /**
286
+ * Handles a lost webgl context
287
+ * @param {WebGLContextEvent} event - The context lost event.
288
+ */
289
+ handleContextLost(e) {
290
+ e.preventDefault(), this._contextLossForced && (this._contextLossForced = !1, setTimeout(() => {
291
+ var r;
292
+ this.gl.isContextLost() && ((r = this.extensions.loseContext) == null || r.restoreContext());
293
+ }, 0));
294
+ }
295
+ /** Handles a restored webgl context. */
296
+ handleContextRestored() {
297
+ this.getExtensions(), this._renderer.runners.contextChange.emit(this.gl);
298
+ }
299
+ destroy() {
300
+ var r;
301
+ const e = this._renderer.view.canvas;
302
+ this._renderer = null, e.removeEventListener("webglcontextlost", this.handleContextLost), e.removeEventListener("webglcontextrestored", this.handleContextRestored), this.gl.useProgram(null), (r = this.extensions.loseContext) == null || r.loseContext();
303
+ }
304
+ /**
305
+ * this function can be called to force a webGL context loss
306
+ * this will release all resources on the GPU.
307
+ * Useful if you need to put Pixi to sleep, and save some GPU memory
308
+ *
309
+ * As soon as render is called - all resources will be created again.
310
+ */
311
+ forceContextLoss() {
312
+ var e;
313
+ (e = this.extensions.loseContext) == null || e.loseContext(), this._contextLossForced = !0;
314
+ }
315
+ /**
316
+ * Validate context.
317
+ * @param {WebGLRenderingContext} gl - Render context.
318
+ */
319
+ validateContext(e) {
320
+ const r = e.getContextAttributes();
321
+ r && !r.stencil && m("Provided WebGL context does not have a stencil buffer, masks may not render correctly");
322
+ const s = this.supports, n = this.webGLVersion === 2, i = this.extensions;
323
+ s.uint32Indices = n || !!i.uint32ElementIndex, s.uniformBufferObject = n, s.vertexArrayObject = n || !!i.vertexArrayObject, s.srgbTextures = n || !!i.srgb, s.nonPowOf2wrapping = n, s.nonPowOf2mipmaps = n, s.msaa = n, s.uint32Indices || m("Provided WebGL context does not support 32 index buffer, large scenes may not render correctly");
324
+ }
325
+ };
326
+ P.extension = {
327
+ type: [
328
+ d.WebGLSystem
329
+ ],
330
+ name: "context"
331
+ };
332
+ P.defaultOptions = {
333
+ /**
334
+ * {@link WebGLOptions.context}
335
+ * @default null
336
+ */
337
+ context: null,
338
+ /**
339
+ * {@link WebGLOptions.premultipliedAlpha}
340
+ * @default true
341
+ */
342
+ premultipliedAlpha: !0,
343
+ /**
344
+ * {@link WebGLOptions.preserveDrawingBuffer}
345
+ * @default false
346
+ */
347
+ preserveDrawingBuffer: !1,
348
+ /**
349
+ * {@link WebGLOptions.powerPreference}
350
+ * @default default
351
+ */
352
+ powerPreference: void 0,
353
+ /**
354
+ * {@link WebGLOptions.webGLVersion}
355
+ * @default 2
356
+ */
357
+ preferWebGLVersion: 2,
358
+ /**
359
+ * {@link WebGLOptions.multiView}
360
+ * @default false
361
+ */
362
+ multiView: !1
363
+ };
364
+ let Xe = P;
365
+ var D = /* @__PURE__ */ ((t) => (t[t.RGBA = 6408] = "RGBA", t[t.RGB = 6407] = "RGB", t[t.RG = 33319] = "RG", t[t.RED = 6403] = "RED", t[t.RGBA_INTEGER = 36249] = "RGBA_INTEGER", t[t.RGB_INTEGER = 36248] = "RGB_INTEGER", t[t.RG_INTEGER = 33320] = "RG_INTEGER", t[t.RED_INTEGER = 36244] = "RED_INTEGER", t[t.ALPHA = 6406] = "ALPHA", t[t.LUMINANCE = 6409] = "LUMINANCE", t[t.LUMINANCE_ALPHA = 6410] = "LUMINANCE_ALPHA", t[t.DEPTH_COMPONENT = 6402] = "DEPTH_COMPONENT", t[t.DEPTH_STENCIL = 34041] = "DEPTH_STENCIL", t))(D || {}), re = /* @__PURE__ */ ((t) => (t[t.TEXTURE_2D = 3553] = "TEXTURE_2D", t[t.TEXTURE_CUBE_MAP = 34067] = "TEXTURE_CUBE_MAP", t[t.TEXTURE_2D_ARRAY = 35866] = "TEXTURE_2D_ARRAY", t[t.TEXTURE_CUBE_MAP_POSITIVE_X = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X", t[t.TEXTURE_CUBE_MAP_NEGATIVE_X = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X", t[t.TEXTURE_CUBE_MAP_POSITIVE_Y = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y", t[t.TEXTURE_CUBE_MAP_NEGATIVE_Y = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y", t[t.TEXTURE_CUBE_MAP_POSITIVE_Z = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z", t[t.TEXTURE_CUBE_MAP_NEGATIVE_Z = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z", t))(re || {}), h = /* @__PURE__ */ ((t) => (t[t.UNSIGNED_BYTE = 5121] = "UNSIGNED_BYTE", t[t.UNSIGNED_SHORT = 5123] = "UNSIGNED_SHORT", t[t.UNSIGNED_SHORT_5_6_5 = 33635] = "UNSIGNED_SHORT_5_6_5", t[t.UNSIGNED_SHORT_4_4_4_4 = 32819] = "UNSIGNED_SHORT_4_4_4_4", t[t.UNSIGNED_SHORT_5_5_5_1 = 32820] = "UNSIGNED_SHORT_5_5_5_1", t[t.UNSIGNED_INT = 5125] = "UNSIGNED_INT", t[t.UNSIGNED_INT_10F_11F_11F_REV = 35899] = "UNSIGNED_INT_10F_11F_11F_REV", t[t.UNSIGNED_INT_2_10_10_10_REV = 33640] = "UNSIGNED_INT_2_10_10_10_REV", t[t.UNSIGNED_INT_24_8 = 34042] = "UNSIGNED_INT_24_8", t[t.UNSIGNED_INT_5_9_9_9_REV = 35902] = "UNSIGNED_INT_5_9_9_9_REV", t[t.BYTE = 5120] = "BYTE", t[t.SHORT = 5122] = "SHORT", t[t.INT = 5124] = "INT", t[t.FLOAT = 5126] = "FLOAT", t[t.FLOAT_32_UNSIGNED_INT_24_8_REV = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV", t[t.HALF_FLOAT = 36193] = "HALF_FLOAT", t))(h || {});
366
+ const w = {
367
+ uint8x2: h.UNSIGNED_BYTE,
368
+ uint8x4: h.UNSIGNED_BYTE,
369
+ sint8x2: h.BYTE,
370
+ sint8x4: h.BYTE,
371
+ unorm8x2: h.UNSIGNED_BYTE,
372
+ unorm8x4: h.UNSIGNED_BYTE,
373
+ snorm8x2: h.BYTE,
374
+ snorm8x4: h.BYTE,
375
+ uint16x2: h.UNSIGNED_SHORT,
376
+ uint16x4: h.UNSIGNED_SHORT,
377
+ sint16x2: h.SHORT,
378
+ sint16x4: h.SHORT,
379
+ unorm16x2: h.UNSIGNED_SHORT,
380
+ unorm16x4: h.UNSIGNED_SHORT,
381
+ snorm16x2: h.SHORT,
382
+ snorm16x4: h.SHORT,
383
+ float16x2: h.HALF_FLOAT,
384
+ float16x4: h.HALF_FLOAT,
385
+ float32: h.FLOAT,
386
+ float32x2: h.FLOAT,
387
+ float32x3: h.FLOAT,
388
+ float32x4: h.FLOAT,
389
+ uint32: h.UNSIGNED_INT,
390
+ uint32x2: h.UNSIGNED_INT,
391
+ uint32x3: h.UNSIGNED_INT,
392
+ uint32x4: h.UNSIGNED_INT,
393
+ sint32: h.INT,
394
+ sint32x2: h.INT,
395
+ sint32x3: h.INT,
396
+ sint32x4: h.INT
397
+ };
398
+ function We(t) {
399
+ return w[t] ?? w.float32;
400
+ }
401
+ const je = {
402
+ "point-list": 0,
403
+ "line-list": 1,
404
+ "line-strip": 3,
405
+ "triangle-list": 4,
406
+ "triangle-strip": 5
407
+ };
408
+ class se {
409
+ /** @param renderer - The renderer this System works for. */
410
+ constructor(e) {
411
+ this._geometryVaoHash = /* @__PURE__ */ Object.create(null), this._renderer = e, this._activeGeometry = null, this._activeVao = null, this.hasVao = !0, this.hasInstance = !0, this._renderer.renderableGC.addManagedHash(this, "_geometryVaoHash");
412
+ }
413
+ /** Sets up the renderer context and necessary buffers. */
414
+ contextChange() {
415
+ const e = this.gl = this._renderer.gl;
416
+ if (!this._renderer.context.supports.vertexArrayObject)
417
+ throw new Error("[PixiJS] Vertex Array Objects are not supported on this device");
418
+ const r = this._renderer.context.extensions.vertexArrayObject;
419
+ r && (e.createVertexArray = () => r.createVertexArrayOES(), e.bindVertexArray = (n) => r.bindVertexArrayOES(n), e.deleteVertexArray = (n) => r.deleteVertexArrayOES(n));
420
+ const s = this._renderer.context.extensions.vertexAttribDivisorANGLE;
421
+ s && (e.drawArraysInstanced = (n, i, a, o) => {
422
+ s.drawArraysInstancedANGLE(n, i, a, o);
423
+ }, e.drawElementsInstanced = (n, i, a, o, c) => {
424
+ s.drawElementsInstancedANGLE(n, i, a, o, c);
425
+ }, e.vertexAttribDivisor = (n, i) => s.vertexAttribDivisorANGLE(n, i)), this._activeGeometry = null, this._activeVao = null, this._geometryVaoHash = /* @__PURE__ */ Object.create(null);
426
+ }
427
+ /**
428
+ * Binds geometry so that is can be drawn. Creating a Vao if required
429
+ * @param geometry - Instance of geometry to bind.
430
+ * @param program - Instance of program to use vao for.
431
+ */
432
+ bind(e, r) {
433
+ const s = this.gl;
434
+ this._activeGeometry = e;
435
+ const n = this.getVao(e, r);
436
+ this._activeVao !== n && (this._activeVao = n, s.bindVertexArray(n)), this.updateBuffers();
437
+ }
438
+ /** Reset and unbind any active VAO and geometry. */
439
+ resetState() {
440
+ this.unbind();
441
+ }
442
+ /** Update buffers of the currently bound geometry. */
443
+ updateBuffers() {
444
+ const e = this._activeGeometry, r = this._renderer.buffer;
445
+ for (let s = 0; s < e.buffers.length; s++) {
446
+ const n = e.buffers[s];
447
+ r.updateBuffer(n);
448
+ }
449
+ }
450
+ /**
451
+ * Check compatibility between a geometry and a program
452
+ * @param geometry - Geometry instance.
453
+ * @param program - Program instance.
454
+ */
455
+ checkCompatibility(e, r) {
456
+ const s = e.attributes, n = r._attributeData;
457
+ for (const i in n)
458
+ if (!s[i])
459
+ throw new Error(`shader and geometry incompatible, geometry missing the "${i}" attribute`);
460
+ }
461
+ /**
462
+ * Takes a geometry and program and generates a unique signature for them.
463
+ * @param geometry - To get signature from.
464
+ * @param program - To test geometry against.
465
+ * @returns - Unique signature of the geometry and program
466
+ */
467
+ getSignature(e, r) {
468
+ const s = e.attributes, n = r._attributeData, i = ["g", e.uid];
469
+ for (const a in s)
470
+ n[a] && i.push(a, n[a].location);
471
+ return i.join("-");
472
+ }
473
+ getVao(e, r) {
474
+ var s;
475
+ return ((s = this._geometryVaoHash[e.uid]) == null ? void 0 : s[r._key]) || this.initGeometryVao(e, r);
476
+ }
477
+ /**
478
+ * Creates or gets Vao with the same structure as the geometry and stores it on the geometry.
479
+ * If vao is created, it is bound automatically. We use a shader to infer what and how to set up the
480
+ * attribute locations.
481
+ * @param geometry - Instance of geometry to to generate Vao for.
482
+ * @param program
483
+ * @param _incRefCount - Increment refCount of all geometry buffers.
484
+ */
485
+ initGeometryVao(e, r, s = !0) {
486
+ const n = this._renderer.gl, i = this._renderer.buffer;
487
+ this._renderer.shader._getProgramData(r), this.checkCompatibility(e, r);
488
+ const a = this.getSignature(e, r);
489
+ this._geometryVaoHash[e.uid] || (this._geometryVaoHash[e.uid] = /* @__PURE__ */ Object.create(null), e.on("destroy", this.onGeometryDestroy, this));
490
+ const o = this._geometryVaoHash[e.uid];
491
+ let c = o[a];
492
+ if (c)
493
+ return o[r._key] = c, c;
494
+ Ue(e, r._attributeData);
495
+ const u = e.buffers;
496
+ c = n.createVertexArray(), n.bindVertexArray(c);
497
+ for (let _ = 0; _ < u.length; _++) {
498
+ const f = u[_];
499
+ i.bind(f);
500
+ }
501
+ return this.activateVao(e, r), o[r._key] = c, o[a] = c, n.bindVertexArray(null), c;
502
+ }
503
+ /**
504
+ * Disposes geometry.
505
+ * @param geometry - Geometry with buffers. Only VAO will be disposed
506
+ * @param [contextLost=false] - If context was lost, we suppress deleteVertexArray
507
+ */
508
+ onGeometryDestroy(e, r) {
509
+ const s = this._geometryVaoHash[e.uid], n = this.gl;
510
+ if (s) {
511
+ if (r)
512
+ for (const i in s)
513
+ this._activeVao !== s[i] && this.unbind(), n.deleteVertexArray(s[i]);
514
+ this._geometryVaoHash[e.uid] = null;
515
+ }
516
+ }
517
+ /**
518
+ * Dispose all WebGL resources of all managed geometries.
519
+ * @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls
520
+ */
521
+ destroyAll(e = !1) {
522
+ const r = this.gl;
523
+ for (const s in this._geometryVaoHash) {
524
+ if (e)
525
+ for (const n in this._geometryVaoHash[s]) {
526
+ const i = this._geometryVaoHash[s];
527
+ this._activeVao !== i && this.unbind(), r.deleteVertexArray(i[n]);
528
+ }
529
+ this._geometryVaoHash[s] = null;
530
+ }
531
+ }
532
+ /**
533
+ * Activate vertex array object.
534
+ * @param geometry - Geometry instance.
535
+ * @param program - Shader program instance.
536
+ */
537
+ activateVao(e, r) {
538
+ var o;
539
+ const s = this._renderer.gl, n = this._renderer.buffer, i = e.attributes;
540
+ e.indexBuffer && n.bind(e.indexBuffer);
541
+ let a = null;
542
+ for (const c in i) {
543
+ const u = i[c], _ = u.buffer, f = n.getGlBuffer(_), l = r._attributeData[c];
544
+ if (l) {
545
+ a !== f && (n.bind(_), a = f);
546
+ const E = l.location;
547
+ s.enableVertexAttribArray(E);
548
+ const v = K(u.format), L = We(u.format);
549
+ if (((o = l.format) == null ? void 0 : o.substring(1, 4)) === "int" ? s.vertexAttribIPointer(
550
+ E,
551
+ v.size,
552
+ L,
553
+ u.stride,
554
+ u.offset
555
+ ) : s.vertexAttribPointer(
556
+ E,
557
+ v.size,
558
+ L,
559
+ v.normalised,
560
+ u.stride,
561
+ u.offset
562
+ ), u.instance)
563
+ if (this.hasInstance) {
564
+ const ve = u.divisor ?? 1;
565
+ s.vertexAttribDivisor(E, ve);
566
+ } else
567
+ throw new Error("geometry error, GPU Instancing is not supported on this device");
568
+ }
569
+ }
570
+ }
571
+ /**
572
+ * Draws the currently bound geometry.
573
+ * @param topology - The type primitive to render.
574
+ * @param size - The number of elements to be rendered. If not specified, all vertices after the
575
+ * starting vertex will be drawn.
576
+ * @param start - The starting vertex in the geometry to start drawing from. If not specified,
577
+ * drawing will start from the first vertex.
578
+ * @param instanceCount - The number of instances of the set of elements to execute. If not specified,
579
+ * all instances will be drawn.
580
+ * @returns This instance of the geometry system.
581
+ */
582
+ draw(e, r, s, n) {
583
+ const { gl: i } = this._renderer, a = this._activeGeometry, o = je[e || a.topology];
584
+ if (n ?? (n = a.instanceCount), a.indexBuffer) {
585
+ const c = a.indexBuffer.data.BYTES_PER_ELEMENT, u = c === 2 ? i.UNSIGNED_SHORT : i.UNSIGNED_INT;
586
+ n > 1 ? i.drawElementsInstanced(o, r || a.indexBuffer.data.length, u, (s || 0) * c, n) : i.drawElements(o, r || a.indexBuffer.data.length, u, (s || 0) * c);
587
+ } else n > 1 ? i.drawArraysInstanced(o, s || 0, r || a.getSize(), n) : i.drawArrays(o, s || 0, r || a.getSize());
588
+ return this;
589
+ }
590
+ /** Unbind/reset everything. */
591
+ unbind() {
592
+ this.gl.bindVertexArray(null), this._activeVao = null, this._activeGeometry = null;
593
+ }
594
+ destroy() {
595
+ this._renderer = null, this.gl = null, this._activeVao = null, this._activeGeometry = null, this._geometryVaoHash = null;
596
+ }
597
+ }
598
+ se.extension = {
599
+ type: [
600
+ d.WebGLSystem
601
+ ],
602
+ name: "geometry"
603
+ };
604
+ const Ke = new Ae({
605
+ attributes: {
606
+ aPosition: [
607
+ -1,
608
+ -1,
609
+ // Bottom left corner
610
+ 3,
611
+ -1,
612
+ // Bottom right corner, extending beyond right edge
613
+ -1,
614
+ 3
615
+ // Top left corner, extending beyond top edge
616
+ ]
617
+ }
618
+ }), M = class ne {
619
+ constructor(e) {
620
+ this.useBackBuffer = !1, this._useBackBufferThisRender = !1, this._renderer = e;
621
+ }
622
+ init(e = {}) {
623
+ const { useBackBuffer: r, antialias: s } = { ...ne.defaultOptions, ...e };
624
+ this.useBackBuffer = r, this._antialias = s, this._renderer.context.supports.msaa || (m("antialiasing, is not supported on when using the back buffer"), this._antialias = !1), this._state = O.for2d();
625
+ const n = new Be({
626
+ vertex: `
627
+ attribute vec2 aPosition;
628
+ out vec2 vUv;
629
+
630
+ void main() {
631
+ gl_Position = vec4(aPosition, 0.0, 1.0);
632
+
633
+ vUv = (aPosition + 1.0) / 2.0;
634
+
635
+ // flip dem UVs
636
+ vUv.y = 1.0 - vUv.y;
637
+ }`,
638
+ fragment: `
639
+ in vec2 vUv;
640
+ out vec4 finalColor;
641
+
642
+ uniform sampler2D uTexture;
643
+
644
+ void main() {
645
+ finalColor = texture(uTexture, vUv);
646
+ }`,
647
+ name: "big-triangle"
648
+ });
649
+ this._bigTriangleShader = new U({
650
+ glProgram: n,
651
+ resources: {
652
+ uTexture: b.WHITE.source
653
+ }
654
+ });
655
+ }
656
+ /**
657
+ * This is called before the RenderTargetSystem is started. This is where
658
+ * we replace the target with the back buffer if required.
659
+ * @param options - The options for this render.
660
+ */
661
+ renderStart(e) {
662
+ const r = this._renderer.renderTarget.getRenderTarget(e.target);
663
+ if (this._useBackBufferThisRender = this.useBackBuffer && !!r.isRoot, this._useBackBufferThisRender) {
664
+ const s = this._renderer.renderTarget.getRenderTarget(e.target);
665
+ this._targetTexture = s.colorTexture, e.target = this._getBackBufferTexture(s.colorTexture);
666
+ }
667
+ }
668
+ renderEnd() {
669
+ this._presentBackBuffer();
670
+ }
671
+ _presentBackBuffer() {
672
+ const e = this._renderer;
673
+ e.renderTarget.finishRenderPass(), this._useBackBufferThisRender && (e.renderTarget.bind(this._targetTexture, !1), this._bigTriangleShader.resources.uTexture = this._backBufferTexture.source, e.encoder.draw({
674
+ geometry: Ke,
675
+ shader: this._bigTriangleShader,
676
+ state: this._state
677
+ }));
678
+ }
679
+ _getBackBufferTexture(e) {
680
+ return this._backBufferTexture = this._backBufferTexture || new b({
681
+ source: new $({
682
+ width: e.width,
683
+ height: e.height,
684
+ resolution: e._resolution,
685
+ antialias: this._antialias
686
+ })
687
+ }), this._backBufferTexture.source.resize(
688
+ e.width,
689
+ e.height,
690
+ e._resolution
691
+ ), this._backBufferTexture;
692
+ }
693
+ /** destroys the back buffer */
694
+ destroy() {
695
+ this._backBufferTexture && (this._backBufferTexture.destroy(), this._backBufferTexture = null);
696
+ }
697
+ };
698
+ M.extension = {
699
+ type: [
700
+ d.WebGLSystem
701
+ ],
702
+ name: "backBuffer",
703
+ priority: 1
704
+ };
705
+ M.defaultOptions = {
706
+ /** if true will use the back buffer where required */
707
+ useBackBuffer: !1
708
+ };
709
+ let $e = M;
710
+ class ie {
711
+ constructor(e) {
712
+ this._colorMaskCache = 15, this._renderer = e;
713
+ }
714
+ setMask(e) {
715
+ this._colorMaskCache !== e && (this._colorMaskCache = e, this._renderer.gl.colorMask(
716
+ !!(e & 8),
717
+ !!(e & 4),
718
+ !!(e & 2),
719
+ !!(e & 1)
720
+ ));
721
+ }
722
+ }
723
+ ie.extension = {
724
+ type: [
725
+ d.WebGLSystem
726
+ ],
727
+ name: "colorMask"
728
+ };
729
+ class ae {
730
+ constructor(e) {
731
+ this.commandFinished = Promise.resolve(), this._renderer = e;
732
+ }
733
+ setGeometry(e, r) {
734
+ this._renderer.geometry.bind(e, r.glProgram);
735
+ }
736
+ finishRenderPass() {
737
+ }
738
+ draw(e) {
739
+ const r = this._renderer, { geometry: s, shader: n, state: i, skipSync: a, topology: o, size: c, start: u, instanceCount: _ } = e;
740
+ r.shader.bind(n, a), r.geometry.bind(s, r.shader._activeProgram), i && r.state.set(i), r.geometry.draw(o, c, u, _ ?? s.instanceCount);
741
+ }
742
+ destroy() {
743
+ this._renderer = null;
744
+ }
745
+ }
746
+ ae.extension = {
747
+ type: [
748
+ d.WebGLSystem
749
+ ],
750
+ name: "encoder"
751
+ };
752
+ class oe {
753
+ constructor(e) {
754
+ this._renderer = e;
755
+ }
756
+ contextChange() {
757
+ const e = this._renderer.gl;
758
+ this.maxTextures = e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS), this.maxBatchableTextures = Ne(this.maxTextures, e);
759
+ const r = this._renderer.context.webGLVersion === 2;
760
+ this.maxUniformBindings = r ? e.getParameter(e.MAX_UNIFORM_BUFFER_BINDINGS) : 0;
761
+ }
762
+ destroy() {
763
+ }
764
+ }
765
+ oe.extension = {
766
+ type: [
767
+ d.WebGLSystem
768
+ ],
769
+ name: "limits"
770
+ };
771
+ class ze {
772
+ constructor() {
773
+ this.width = -1, this.height = -1, this.msaa = !1, this.msaaRenderBuffer = [];
774
+ }
775
+ }
776
+ class ce {
777
+ constructor(e) {
778
+ this._stencilCache = {
779
+ enabled: !1,
780
+ stencilReference: 0,
781
+ stencilMode: p.NONE
782
+ }, this._renderTargetStencilState = /* @__PURE__ */ Object.create(null), e.renderTarget.onRenderTargetChange.add(this);
783
+ }
784
+ contextChange(e) {
785
+ this._gl = e, this._comparisonFuncMapping = {
786
+ always: e.ALWAYS,
787
+ never: e.NEVER,
788
+ equal: e.EQUAL,
789
+ "not-equal": e.NOTEQUAL,
790
+ less: e.LESS,
791
+ "less-equal": e.LEQUAL,
792
+ greater: e.GREATER,
793
+ "greater-equal": e.GEQUAL
794
+ }, this._stencilOpsMapping = {
795
+ keep: e.KEEP,
796
+ zero: e.ZERO,
797
+ replace: e.REPLACE,
798
+ invert: e.INVERT,
799
+ "increment-clamp": e.INCR,
800
+ "decrement-clamp": e.DECR,
801
+ "increment-wrap": e.INCR_WRAP,
802
+ "decrement-wrap": e.DECR_WRAP
803
+ }, this.resetState();
804
+ }
805
+ onRenderTargetChange(e) {
806
+ if (this._activeRenderTarget === e)
807
+ return;
808
+ this._activeRenderTarget = e;
809
+ let r = this._renderTargetStencilState[e.uid];
810
+ r || (r = this._renderTargetStencilState[e.uid] = {
811
+ stencilMode: p.DISABLED,
812
+ stencilReference: 0
813
+ }), this.setStencilMode(r.stencilMode, r.stencilReference);
814
+ }
815
+ resetState() {
816
+ this._stencilCache.enabled = !1, this._stencilCache.stencilMode = p.NONE, this._stencilCache.stencilReference = 0;
817
+ }
818
+ setStencilMode(e, r) {
819
+ const s = this._renderTargetStencilState[this._activeRenderTarget.uid], n = this._gl, i = Fe[e], a = this._stencilCache;
820
+ if (s.stencilMode = e, s.stencilReference = r, e === p.DISABLED) {
821
+ this._stencilCache.enabled && (this._stencilCache.enabled = !1, n.disable(n.STENCIL_TEST));
822
+ return;
823
+ }
824
+ this._stencilCache.enabled || (this._stencilCache.enabled = !0, n.enable(n.STENCIL_TEST)), (e !== a.stencilMode || a.stencilReference !== r) && (a.stencilMode = e, a.stencilReference = r, n.stencilFunc(this._comparisonFuncMapping[i.stencilBack.compare], r, 255), n.stencilOp(n.KEEP, n.KEEP, this._stencilOpsMapping[i.stencilBack.passOp]));
825
+ }
826
+ }
827
+ ce.extension = {
828
+ type: [
829
+ d.WebGLSystem
830
+ ],
831
+ name: "stencil"
832
+ };
833
+ const ue = {
834
+ f32: 4,
835
+ i32: 4,
836
+ "vec2<f32>": 8,
837
+ "vec3<f32>": 12,
838
+ "vec4<f32>": 16,
839
+ "vec2<i32>": 8,
840
+ "vec3<i32>": 12,
841
+ "vec4<i32>": 16,
842
+ "mat2x2<f32>": 16 * 2,
843
+ "mat3x3<f32>": 16 * 3,
844
+ "mat4x4<f32>": 16 * 4
845
+ // TODO - not essential for now but support these in the future
846
+ // int: 4,
847
+ // ivec2: 8,
848
+ // ivec3: 12,
849
+ // ivec4: 16,
850
+ // uint: 4,
851
+ // uvec2: 8,
852
+ // uvec3: 12,
853
+ // uvec4: 16,
854
+ // bool: 4,
855
+ // bvec2: 8,
856
+ // bvec3: 12,
857
+ // bvec4: 16,
858
+ // mat2: 16 * 2,
859
+ // mat3: 16 * 3,
860
+ // mat4: 16 * 4,
861
+ };
862
+ function Ye(t) {
863
+ const e = t.map((i) => ({
864
+ data: i,
865
+ offset: 0,
866
+ size: 0
867
+ })), r = 16;
868
+ let s = 0, n = 0;
869
+ for (let i = 0; i < e.length; i++) {
870
+ const a = e[i];
871
+ if (s = ue[a.data.type], !s)
872
+ throw new Error(`Unknown type ${a.data.type}`);
873
+ a.data.size > 1 && (s = Math.max(s, r) * a.data.size);
874
+ const o = s === 12 ? 16 : s;
875
+ a.size = s;
876
+ const c = n % r;
877
+ c > 0 && r - c < o ? n += (r - c) % 16 : n += (s - c % s) % s, a.offset = n, n += s;
878
+ }
879
+ return n = Math.ceil(n / 16) * 16, { uboElements: e, size: n };
880
+ }
881
+ function qe(t, e) {
882
+ const r = Math.max(ue[t.data.type] / 16, 1), s = t.data.value.length / t.data.size, n = (4 - s % 4) % 4, i = t.data.type.indexOf("i32") >= 0 ? "dataInt32" : "data";
883
+ return `
884
+ v = uv.${t.data.name};
885
+ offset += ${e};
886
+
887
+ arrayOffset = offset;
888
+
889
+ t = 0;
890
+
891
+ for(var i=0; i < ${t.data.size * r}; i++)
892
+ {
893
+ for(var j = 0; j < ${s}; j++)
894
+ {
895
+ ${i}[arrayOffset++] = v[t++];
896
+ }
897
+ ${n !== 0 ? `arrayOffset += ${n};` : ""}
898
+ }
899
+ `;
900
+ }
901
+ function Ze(t) {
902
+ return Oe(
903
+ t,
904
+ "uboStd40",
905
+ qe,
906
+ Pe
907
+ );
908
+ }
909
+ class _e extends Me {
910
+ constructor() {
911
+ super({
912
+ createUboElements: Ye,
913
+ generateUboSync: Ze
914
+ });
915
+ }
916
+ }
917
+ _e.extension = {
918
+ type: [d.WebGLSystem],
919
+ name: "ubo"
920
+ };
921
+ class Je {
922
+ constructor() {
923
+ this._clearColorCache = [0, 0, 0, 0], this._viewPortCache = new H();
924
+ }
925
+ init(e, r) {
926
+ this._renderer = e, this._renderTargetSystem = r, e.runners.contextChange.add(this);
927
+ }
928
+ contextChange() {
929
+ this._clearColorCache = [0, 0, 0, 0], this._viewPortCache = new H();
930
+ }
931
+ copyToTexture(e, r, s, n, i) {
932
+ const a = this._renderTargetSystem, o = this._renderer, c = a.getGpuRenderTarget(e), u = o.gl;
933
+ return this.finishRenderPass(e), u.bindFramebuffer(u.FRAMEBUFFER, c.resolveTargetFramebuffer), o.texture.bind(r, 0), u.copyTexSubImage2D(
934
+ u.TEXTURE_2D,
935
+ 0,
936
+ i.x,
937
+ i.y,
938
+ s.x,
939
+ s.y,
940
+ n.width,
941
+ n.height
942
+ ), r;
943
+ }
944
+ startRenderPass(e, r = !0, s, n) {
945
+ const i = this._renderTargetSystem, a = e.colorTexture, o = i.getGpuRenderTarget(e);
946
+ let c = n.y;
947
+ e.isRoot && (c = a.pixelHeight - n.height), e.colorTextures.forEach((f) => {
948
+ this._renderer.texture.unbind(f);
949
+ });
950
+ const u = this._renderer.gl;
951
+ u.bindFramebuffer(u.FRAMEBUFFER, o.framebuffer);
952
+ const _ = this._viewPortCache;
953
+ (_.x !== n.x || _.y !== c || _.width !== n.width || _.height !== n.height) && (_.x = n.x, _.y = c, _.width = n.width, _.height = n.height, u.viewport(
954
+ n.x,
955
+ c,
956
+ n.width,
957
+ n.height
958
+ )), !o.depthStencilRenderBuffer && (e.stencil || e.depth) && this._initStencil(o), this.clear(e, r, s);
959
+ }
960
+ finishRenderPass(e) {
961
+ const s = this._renderTargetSystem.getGpuRenderTarget(e);
962
+ if (!s.msaa)
963
+ return;
964
+ const n = this._renderer.gl;
965
+ n.bindFramebuffer(n.FRAMEBUFFER, s.resolveTargetFramebuffer), n.bindFramebuffer(n.READ_FRAMEBUFFER, s.framebuffer), n.blitFramebuffer(
966
+ 0,
967
+ 0,
968
+ s.width,
969
+ s.height,
970
+ 0,
971
+ 0,
972
+ s.width,
973
+ s.height,
974
+ n.COLOR_BUFFER_BIT,
975
+ n.NEAREST
976
+ ), n.bindFramebuffer(n.FRAMEBUFFER, s.framebuffer);
977
+ }
978
+ initGpuRenderTarget(e) {
979
+ const s = this._renderer.gl, n = new ze();
980
+ return e.colorTexture instanceof A ? (this._renderer.context.ensureCanvasSize(e.colorTexture.resource), n.framebuffer = null, n) : (this._initColor(e, n), s.bindFramebuffer(s.FRAMEBUFFER, null), n);
981
+ }
982
+ destroyGpuRenderTarget(e) {
983
+ const r = this._renderer.gl;
984
+ e.framebuffer && (r.deleteFramebuffer(e.framebuffer), e.framebuffer = null), e.resolveTargetFramebuffer && (r.deleteFramebuffer(e.resolveTargetFramebuffer), e.resolveTargetFramebuffer = null), e.depthStencilRenderBuffer && (r.deleteRenderbuffer(e.depthStencilRenderBuffer), e.depthStencilRenderBuffer = null), e.msaaRenderBuffer.forEach((s) => {
985
+ r.deleteRenderbuffer(s);
986
+ }), e.msaaRenderBuffer = null;
987
+ }
988
+ clear(e, r, s) {
989
+ if (!r)
990
+ return;
991
+ const n = this._renderTargetSystem;
992
+ typeof r == "boolean" && (r = r ? N.ALL : N.NONE);
993
+ const i = this._renderer.gl;
994
+ if (r & N.COLOR) {
995
+ s ?? (s = n.defaultClearColor);
996
+ const a = this._clearColorCache, o = s;
997
+ (a[0] !== o[0] || a[1] !== o[1] || a[2] !== o[2] || a[3] !== o[3]) && (a[0] = o[0], a[1] = o[1], a[2] = o[2], a[3] = o[3], i.clearColor(o[0], o[1], o[2], o[3]));
998
+ }
999
+ i.clear(r);
1000
+ }
1001
+ resizeGpuRenderTarget(e) {
1002
+ if (e.isRoot)
1003
+ return;
1004
+ const s = this._renderTargetSystem.getGpuRenderTarget(e);
1005
+ this._resizeColor(e, s), (e.stencil || e.depth) && this._resizeStencil(s);
1006
+ }
1007
+ _initColor(e, r) {
1008
+ const s = this._renderer, n = s.gl, i = n.createFramebuffer();
1009
+ if (r.resolveTargetFramebuffer = i, n.bindFramebuffer(n.FRAMEBUFFER, i), r.width = e.colorTexture.source.pixelWidth, r.height = e.colorTexture.source.pixelHeight, e.colorTextures.forEach((a, o) => {
1010
+ const c = a.source;
1011
+ c.antialias && (s.context.supports.msaa ? r.msaa = !0 : m("[RenderTexture] Antialiasing on textures is not supported in WebGL1")), s.texture.bindSource(c, 0);
1012
+ const _ = s.texture.getGlSource(c).texture;
1013
+ n.framebufferTexture2D(
1014
+ n.FRAMEBUFFER,
1015
+ n.COLOR_ATTACHMENT0 + o,
1016
+ 3553,
1017
+ // texture.target,
1018
+ _,
1019
+ 0
1020
+ );
1021
+ }), r.msaa) {
1022
+ const a = n.createFramebuffer();
1023
+ r.framebuffer = a, n.bindFramebuffer(n.FRAMEBUFFER, a), e.colorTextures.forEach((o, c) => {
1024
+ const u = n.createRenderbuffer();
1025
+ r.msaaRenderBuffer[c] = u;
1026
+ });
1027
+ } else
1028
+ r.framebuffer = i;
1029
+ this._resizeColor(e, r);
1030
+ }
1031
+ _resizeColor(e, r) {
1032
+ const s = e.colorTexture.source;
1033
+ if (r.width = s.pixelWidth, r.height = s.pixelHeight, e.colorTextures.forEach((n, i) => {
1034
+ i !== 0 && n.source.resize(s.width, s.height, s._resolution);
1035
+ }), r.msaa) {
1036
+ const n = this._renderer, i = n.gl, a = r.framebuffer;
1037
+ i.bindFramebuffer(i.FRAMEBUFFER, a), e.colorTextures.forEach((o, c) => {
1038
+ const u = o.source;
1039
+ n.texture.bindSource(u, 0);
1040
+ const f = n.texture.getGlSource(u).internalFormat, l = r.msaaRenderBuffer[c];
1041
+ i.bindRenderbuffer(
1042
+ i.RENDERBUFFER,
1043
+ l
1044
+ ), i.renderbufferStorageMultisample(
1045
+ i.RENDERBUFFER,
1046
+ 4,
1047
+ f,
1048
+ u.pixelWidth,
1049
+ u.pixelHeight
1050
+ ), i.framebufferRenderbuffer(
1051
+ i.FRAMEBUFFER,
1052
+ i.COLOR_ATTACHMENT0 + c,
1053
+ i.RENDERBUFFER,
1054
+ l
1055
+ );
1056
+ });
1057
+ }
1058
+ }
1059
+ _initStencil(e) {
1060
+ if (e.framebuffer === null)
1061
+ return;
1062
+ const r = this._renderer.gl, s = r.createRenderbuffer();
1063
+ e.depthStencilRenderBuffer = s, r.bindRenderbuffer(
1064
+ r.RENDERBUFFER,
1065
+ s
1066
+ ), r.framebufferRenderbuffer(
1067
+ r.FRAMEBUFFER,
1068
+ r.DEPTH_STENCIL_ATTACHMENT,
1069
+ r.RENDERBUFFER,
1070
+ s
1071
+ ), this._resizeStencil(e);
1072
+ }
1073
+ _resizeStencil(e) {
1074
+ const r = this._renderer.gl;
1075
+ r.bindRenderbuffer(
1076
+ r.RENDERBUFFER,
1077
+ e.depthStencilRenderBuffer
1078
+ ), e.msaa ? r.renderbufferStorageMultisample(
1079
+ r.RENDERBUFFER,
1080
+ 4,
1081
+ r.DEPTH24_STENCIL8,
1082
+ e.width,
1083
+ e.height
1084
+ ) : r.renderbufferStorage(
1085
+ r.RENDERBUFFER,
1086
+ this._renderer.context.webGLVersion === 2 ? r.DEPTH24_STENCIL8 : r.DEPTH_STENCIL,
1087
+ e.width,
1088
+ e.height
1089
+ );
1090
+ }
1091
+ prerender(e) {
1092
+ const r = e.colorTexture.resource;
1093
+ this._renderer.context.multiView && A.test(r) && this._renderer.context.ensureCanvasSize(r);
1094
+ }
1095
+ postrender(e) {
1096
+ if (this._renderer.context.multiView && A.test(e.colorTexture.resource)) {
1097
+ const r = this._renderer.context.canvas, s = e.colorTexture;
1098
+ s.context2D.drawImage(
1099
+ r,
1100
+ 0,
1101
+ s.pixelHeight - r.height
1102
+ );
1103
+ }
1104
+ }
1105
+ }
1106
+ class he extends Le {
1107
+ constructor(e) {
1108
+ super(e), this.adaptor = new Je(), this.adaptor.init(e, this);
1109
+ }
1110
+ }
1111
+ he.extension = {
1112
+ type: [d.WebGLSystem],
1113
+ name: "renderTarget"
1114
+ };
1115
+ function Qe(t, e) {
1116
+ const r = [], s = [`
1117
+ var g = s.groups;
1118
+ var sS = r.shader;
1119
+ var p = s.glProgram;
1120
+ var ugS = r.uniformGroup;
1121
+ var resources;
1122
+ `];
1123
+ let n = !1, i = 0;
1124
+ const a = e._getProgramData(t.glProgram);
1125
+ for (const c in t.groups) {
1126
+ const u = t.groups[c];
1127
+ r.push(`
1128
+ resources = g[${c}].resources;
1129
+ `);
1130
+ for (const _ in u.resources) {
1131
+ const f = u.resources[_];
1132
+ if (f instanceof F)
1133
+ if (f.ubo) {
1134
+ const l = t._uniformBindMap[c][Number(_)];
1135
+ r.push(`
1136
+ sS.bindUniformBlock(
1137
+ resources[${_}],
1138
+ '${l}',
1139
+ ${t.glProgram._uniformBlockData[l].index}
1140
+ );
1141
+ `);
1142
+ } else
1143
+ r.push(`
1144
+ ugS.updateUniformGroup(resources[${_}], p, sD);
1145
+ `);
1146
+ else if (f instanceof J) {
1147
+ const l = t._uniformBindMap[c][Number(_)];
1148
+ r.push(`
1149
+ sS.bindUniformBlock(
1150
+ resources[${_}],
1151
+ '${l}',
1152
+ ${t.glProgram._uniformBlockData[l].index}
1153
+ );
1154
+ `);
1155
+ } else if (f instanceof $) {
1156
+ const l = t._uniformBindMap[c][_], E = a.uniformData[l];
1157
+ E && (n || (n = !0, s.push(`
1158
+ var tS = r.texture;
1159
+ `)), e._gl.uniform1i(E.location, i), r.push(`
1160
+ tS.bind(resources[${_}], ${i});
1161
+ `), i++);
1162
+ }
1163
+ }
1164
+ }
1165
+ const o = [...s, ...r].join(`
1166
+ `);
1167
+ return new Function("r", "s", "sD", o);
1168
+ }
1169
+ class et {
1170
+ /**
1171
+ * Makes a new Pixi program.
1172
+ * @param program - webgl program
1173
+ * @param uniformData - uniforms
1174
+ */
1175
+ constructor(e, r) {
1176
+ this.program = e, this.uniformData = r, this.uniformGroups = {}, this.uniformDirtyGroups = {}, this.uniformBlockBindings = {};
1177
+ }
1178
+ /** Destroys this program. */
1179
+ destroy() {
1180
+ this.uniformData = null, this.uniformGroups = null, this.uniformDirtyGroups = null, this.uniformBlockBindings = null, this.program = null;
1181
+ }
1182
+ }
1183
+ function V(t, e, r) {
1184
+ const s = t.createShader(e);
1185
+ return t.shaderSource(s, r), t.compileShader(s), s;
1186
+ }
1187
+ function C(t) {
1188
+ const e = new Array(t);
1189
+ for (let r = 0; r < e.length; r++)
1190
+ e[r] = !1;
1191
+ return e;
1192
+ }
1193
+ function fe(t, e) {
1194
+ switch (t) {
1195
+ case "float":
1196
+ return 0;
1197
+ case "vec2":
1198
+ return new Float32Array(2 * e);
1199
+ case "vec3":
1200
+ return new Float32Array(3 * e);
1201
+ case "vec4":
1202
+ return new Float32Array(4 * e);
1203
+ case "int":
1204
+ case "uint":
1205
+ case "sampler2D":
1206
+ case "sampler2DArray":
1207
+ return 0;
1208
+ case "ivec2":
1209
+ return new Int32Array(2 * e);
1210
+ case "ivec3":
1211
+ return new Int32Array(3 * e);
1212
+ case "ivec4":
1213
+ return new Int32Array(4 * e);
1214
+ case "uvec2":
1215
+ return new Uint32Array(2 * e);
1216
+ case "uvec3":
1217
+ return new Uint32Array(3 * e);
1218
+ case "uvec4":
1219
+ return new Uint32Array(4 * e);
1220
+ case "bool":
1221
+ return !1;
1222
+ case "bvec2":
1223
+ return C(2 * e);
1224
+ case "bvec3":
1225
+ return C(3 * e);
1226
+ case "bvec4":
1227
+ return C(4 * e);
1228
+ case "mat2":
1229
+ return new Float32Array([
1230
+ 1,
1231
+ 0,
1232
+ 0,
1233
+ 1
1234
+ ]);
1235
+ case "mat3":
1236
+ return new Float32Array([
1237
+ 1,
1238
+ 0,
1239
+ 0,
1240
+ 0,
1241
+ 1,
1242
+ 0,
1243
+ 0,
1244
+ 0,
1245
+ 1
1246
+ ]);
1247
+ case "mat4":
1248
+ return new Float32Array([
1249
+ 1,
1250
+ 0,
1251
+ 0,
1252
+ 0,
1253
+ 0,
1254
+ 1,
1255
+ 0,
1256
+ 0,
1257
+ 0,
1258
+ 0,
1259
+ 1,
1260
+ 0,
1261
+ 0,
1262
+ 0,
1263
+ 0,
1264
+ 1
1265
+ ]);
1266
+ }
1267
+ return null;
1268
+ }
1269
+ let g = null;
1270
+ const k = {
1271
+ FLOAT: "float",
1272
+ FLOAT_VEC2: "vec2",
1273
+ FLOAT_VEC3: "vec3",
1274
+ FLOAT_VEC4: "vec4",
1275
+ INT: "int",
1276
+ INT_VEC2: "ivec2",
1277
+ INT_VEC3: "ivec3",
1278
+ INT_VEC4: "ivec4",
1279
+ UNSIGNED_INT: "uint",
1280
+ UNSIGNED_INT_VEC2: "uvec2",
1281
+ UNSIGNED_INT_VEC3: "uvec3",
1282
+ UNSIGNED_INT_VEC4: "uvec4",
1283
+ BOOL: "bool",
1284
+ BOOL_VEC2: "bvec2",
1285
+ BOOL_VEC3: "bvec3",
1286
+ BOOL_VEC4: "bvec4",
1287
+ FLOAT_MAT2: "mat2",
1288
+ FLOAT_MAT3: "mat3",
1289
+ FLOAT_MAT4: "mat4",
1290
+ SAMPLER_2D: "sampler2D",
1291
+ INT_SAMPLER_2D: "sampler2D",
1292
+ UNSIGNED_INT_SAMPLER_2D: "sampler2D",
1293
+ SAMPLER_CUBE: "samplerCube",
1294
+ INT_SAMPLER_CUBE: "samplerCube",
1295
+ UNSIGNED_INT_SAMPLER_CUBE: "samplerCube",
1296
+ SAMPLER_2D_ARRAY: "sampler2DArray",
1297
+ INT_SAMPLER_2D_ARRAY: "sampler2DArray",
1298
+ UNSIGNED_INT_SAMPLER_2D_ARRAY: "sampler2DArray"
1299
+ }, tt = {
1300
+ float: "float32",
1301
+ vec2: "float32x2",
1302
+ vec3: "float32x3",
1303
+ vec4: "float32x4",
1304
+ int: "sint32",
1305
+ ivec2: "sint32x2",
1306
+ ivec3: "sint32x3",
1307
+ ivec4: "sint32x4",
1308
+ uint: "uint32",
1309
+ uvec2: "uint32x2",
1310
+ uvec3: "uint32x3",
1311
+ uvec4: "uint32x4",
1312
+ bool: "uint32",
1313
+ bvec2: "uint32x2",
1314
+ bvec3: "uint32x3",
1315
+ bvec4: "uint32x4"
1316
+ };
1317
+ function le(t, e) {
1318
+ if (!g) {
1319
+ const r = Object.keys(k);
1320
+ g = {};
1321
+ for (let s = 0; s < r.length; ++s) {
1322
+ const n = r[s];
1323
+ g[t[n]] = k[n];
1324
+ }
1325
+ }
1326
+ return g[e];
1327
+ }
1328
+ function rt(t, e) {
1329
+ const r = le(t, e);
1330
+ return tt[r] || "float32";
1331
+ }
1332
+ function st(t, e, r = !1) {
1333
+ const s = {}, n = e.getProgramParameter(t, e.ACTIVE_ATTRIBUTES);
1334
+ for (let a = 0; a < n; a++) {
1335
+ const o = e.getActiveAttrib(t, a);
1336
+ if (o.name.startsWith("gl_"))
1337
+ continue;
1338
+ const c = rt(e, o.type);
1339
+ s[o.name] = {
1340
+ location: 0,
1341
+ // set further down..
1342
+ format: c,
1343
+ stride: K(c).stride,
1344
+ offset: 0,
1345
+ instance: !1,
1346
+ start: 0
1347
+ };
1348
+ }
1349
+ const i = Object.keys(s);
1350
+ if (r) {
1351
+ i.sort((a, o) => a > o ? 1 : -1);
1352
+ for (let a = 0; a < i.length; a++)
1353
+ s[i[a]].location = a, e.bindAttribLocation(t, a, i[a]);
1354
+ e.linkProgram(t);
1355
+ } else
1356
+ for (let a = 0; a < i.length; a++)
1357
+ s[i[a]].location = e.getAttribLocation(t, i[a]);
1358
+ return s;
1359
+ }
1360
+ function nt(t, e) {
1361
+ if (!e.ACTIVE_UNIFORM_BLOCKS)
1362
+ return {};
1363
+ const r = {}, s = e.getProgramParameter(t, e.ACTIVE_UNIFORM_BLOCKS);
1364
+ for (let n = 0; n < s; n++) {
1365
+ const i = e.getActiveUniformBlockName(t, n), a = e.getUniformBlockIndex(t, i), o = e.getActiveUniformBlockParameter(t, n, e.UNIFORM_BLOCK_DATA_SIZE);
1366
+ r[i] = {
1367
+ name: i,
1368
+ index: a,
1369
+ size: o
1370
+ };
1371
+ }
1372
+ return r;
1373
+ }
1374
+ function it(t, e) {
1375
+ const r = {}, s = e.getProgramParameter(t, e.ACTIVE_UNIFORMS);
1376
+ for (let n = 0; n < s; n++) {
1377
+ const i = e.getActiveUniform(t, n), a = i.name.replace(/\[.*?\]$/, ""), o = !!i.name.match(/\[.*?\]$/), c = le(e, i.type);
1378
+ r[a] = {
1379
+ name: a,
1380
+ index: n,
1381
+ type: c,
1382
+ size: i.size,
1383
+ isArray: o,
1384
+ value: fe(c, i.size)
1385
+ };
1386
+ }
1387
+ return r;
1388
+ }
1389
+ function X(t, e) {
1390
+ const r = t.getShaderSource(e).split(`
1391
+ `).map((u, _) => `${_}: ${u}`), s = t.getShaderInfoLog(e), n = s.split(`
1392
+ `), i = {}, a = n.map((u) => parseFloat(u.replace(/^ERROR\: 0\:([\d]+)\:.*$/, "$1"))).filter((u) => u && !i[u] ? (i[u] = !0, !0) : !1), o = [""];
1393
+ a.forEach((u) => {
1394
+ r[u - 1] = `%c${r[u - 1]}%c`, o.push("background: #FF0000; color:#FFFFFF; font-size: 10px", "font-size: 10px");
1395
+ });
1396
+ const c = r.join(`
1397
+ `);
1398
+ o[0] = c, console.error(s), console.groupCollapsed("click to view full shader code"), console.warn(...o), console.groupEnd();
1399
+ }
1400
+ function at(t, e, r, s) {
1401
+ t.getProgramParameter(e, t.LINK_STATUS) || (t.getShaderParameter(r, t.COMPILE_STATUS) || X(t, r), t.getShaderParameter(s, t.COMPILE_STATUS) || X(t, s), console.error("PixiJS Error: Could not initialize shader."), t.getProgramInfoLog(e) !== "" && console.warn("PixiJS Warning: gl.getProgramInfoLog()", t.getProgramInfoLog(e)));
1402
+ }
1403
+ function ot(t, e) {
1404
+ const r = V(t, t.VERTEX_SHADER, e.vertex), s = V(t, t.FRAGMENT_SHADER, e.fragment), n = t.createProgram();
1405
+ t.attachShader(n, r), t.attachShader(n, s);
1406
+ const i = e.transformFeedbackVaryings;
1407
+ i && (typeof t.transformFeedbackVaryings != "function" ? m("TransformFeedback is not supported but TransformFeedbackVaryings are given.") : t.transformFeedbackVaryings(
1408
+ n,
1409
+ i.names,
1410
+ i.bufferMode === "separate" ? t.SEPARATE_ATTRIBS : t.INTERLEAVED_ATTRIBS
1411
+ )), t.linkProgram(n), t.getProgramParameter(n, t.LINK_STATUS) || at(t, n, r, s), e._attributeData = st(
1412
+ n,
1413
+ t,
1414
+ !/^[ \t]*#[ \t]*version[ \t]+300[ \t]+es[ \t]*$/m.test(e.vertex)
1415
+ ), e._uniformData = it(n, t), e._uniformBlockData = nt(n, t), t.deleteShader(r), t.deleteShader(s);
1416
+ const a = {};
1417
+ for (const c in e._uniformData) {
1418
+ const u = e._uniformData[c];
1419
+ a[c] = {
1420
+ location: t.getUniformLocation(n, c),
1421
+ value: fe(u.type, u.size)
1422
+ };
1423
+ }
1424
+ return new et(n, a);
1425
+ }
1426
+ const R = {
1427
+ textureCount: 0,
1428
+ blockIndex: 0
1429
+ };
1430
+ class de {
1431
+ constructor(e) {
1432
+ this._activeProgram = null, this._programDataHash = /* @__PURE__ */ Object.create(null), this._shaderSyncFunctions = /* @__PURE__ */ Object.create(null), this._renderer = e, this._renderer.renderableGC.addManagedHash(this, "_programDataHash");
1433
+ }
1434
+ contextChange(e) {
1435
+ this._gl = e, this._programDataHash = /* @__PURE__ */ Object.create(null), this._shaderSyncFunctions = /* @__PURE__ */ Object.create(null), this._activeProgram = null;
1436
+ }
1437
+ /**
1438
+ * Changes the current shader to the one given in parameter.
1439
+ * @param shader - the new shader
1440
+ * @param skipSync - false if the shader should automatically sync its uniforms.
1441
+ * @returns the glProgram that belongs to the shader.
1442
+ */
1443
+ bind(e, r) {
1444
+ if (this._setProgram(e.glProgram), r)
1445
+ return;
1446
+ R.textureCount = 0, R.blockIndex = 0;
1447
+ let s = this._shaderSyncFunctions[e.glProgram._key];
1448
+ s || (s = this._shaderSyncFunctions[e.glProgram._key] = this._generateShaderSync(e, this)), this._renderer.buffer.nextBindBase(!!e.glProgram.transformFeedbackVaryings), s(this._renderer, e, R);
1449
+ }
1450
+ /**
1451
+ * Updates the uniform group.
1452
+ * @param uniformGroup - the uniform group to update
1453
+ */
1454
+ updateUniformGroup(e) {
1455
+ this._renderer.uniformGroup.updateUniformGroup(e, this._activeProgram, R);
1456
+ }
1457
+ /**
1458
+ * Binds a uniform block to the shader.
1459
+ * @param uniformGroup - the uniform group to bind
1460
+ * @param name - the name of the uniform block
1461
+ * @param index - the index of the uniform block
1462
+ */
1463
+ bindUniformBlock(e, r, s = 0) {
1464
+ const n = this._renderer.buffer, i = this._getProgramData(this._activeProgram), a = e._bufferResource;
1465
+ a || this._renderer.ubo.updateUniformGroup(e);
1466
+ const o = e.buffer, c = n.updateBuffer(o), u = n.freeLocationForBufferBase(c);
1467
+ if (a) {
1468
+ const { offset: f, size: l } = e;
1469
+ f === 0 && l === o.data.byteLength ? n.bindBufferBase(c, u) : n.bindBufferRange(c, u, f);
1470
+ } else n.getLastBindBaseLocation(c) !== u && n.bindBufferBase(c, u);
1471
+ const _ = this._activeProgram._uniformBlockData[r].index;
1472
+ i.uniformBlockBindings[s] !== u && (i.uniformBlockBindings[s] = u, this._renderer.gl.uniformBlockBinding(i.program, _, u));
1473
+ }
1474
+ _setProgram(e) {
1475
+ if (this._activeProgram === e)
1476
+ return;
1477
+ this._activeProgram = e;
1478
+ const r = this._getProgramData(e);
1479
+ this._gl.useProgram(r.program);
1480
+ }
1481
+ /**
1482
+ * @param program - the program to get the data for
1483
+ * @internal
1484
+ */
1485
+ _getProgramData(e) {
1486
+ return this._programDataHash[e._key] || this._createProgramData(e);
1487
+ }
1488
+ _createProgramData(e) {
1489
+ const r = e._key;
1490
+ return this._programDataHash[r] = ot(this._gl, e), this._programDataHash[r];
1491
+ }
1492
+ destroy() {
1493
+ for (const e of Object.keys(this._programDataHash))
1494
+ this._programDataHash[e].destroy(), this._programDataHash[e] = null;
1495
+ this._programDataHash = null, this._shaderSyncFunctions = null, this._activeProgram = null, this._renderer = null, this._gl = null;
1496
+ }
1497
+ /**
1498
+ * Creates a function that can be executed that will sync the shader as efficiently as possible.
1499
+ * Overridden by the unsafe eval package if you don't want eval used in your project.
1500
+ * @param shader - the shader to generate the sync function for
1501
+ * @param shaderSystem - the shader system to use
1502
+ * @returns - the generated sync function
1503
+ * @ignore
1504
+ */
1505
+ _generateShaderSync(e, r) {
1506
+ return Qe(e, r);
1507
+ }
1508
+ resetState() {
1509
+ this._activeProgram = null;
1510
+ }
1511
+ }
1512
+ de.extension = {
1513
+ type: [
1514
+ d.WebGLSystem
1515
+ ],
1516
+ name: "shader"
1517
+ };
1518
+ const ct = {
1519
+ f32: `if (cv !== v) {
1520
+ cu.value = v;
1521
+ gl.uniform1f(location, v);
1522
+ }`,
1523
+ "vec2<f32>": `if (cv[0] !== v[0] || cv[1] !== v[1]) {
1524
+ cv[0] = v[0];
1525
+ cv[1] = v[1];
1526
+ gl.uniform2f(location, v[0], v[1]);
1527
+ }`,
1528
+ "vec3<f32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) {
1529
+ cv[0] = v[0];
1530
+ cv[1] = v[1];
1531
+ cv[2] = v[2];
1532
+ gl.uniform3f(location, v[0], v[1], v[2]);
1533
+ }`,
1534
+ "vec4<f32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) {
1535
+ cv[0] = v[0];
1536
+ cv[1] = v[1];
1537
+ cv[2] = v[2];
1538
+ cv[3] = v[3];
1539
+ gl.uniform4f(location, v[0], v[1], v[2], v[3]);
1540
+ }`,
1541
+ i32: `if (cv !== v) {
1542
+ cu.value = v;
1543
+ gl.uniform1i(location, v);
1544
+ }`,
1545
+ "vec2<i32>": `if (cv[0] !== v[0] || cv[1] !== v[1]) {
1546
+ cv[0] = v[0];
1547
+ cv[1] = v[1];
1548
+ gl.uniform2i(location, v[0], v[1]);
1549
+ }`,
1550
+ "vec3<i32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) {
1551
+ cv[0] = v[0];
1552
+ cv[1] = v[1];
1553
+ cv[2] = v[2];
1554
+ gl.uniform3i(location, v[0], v[1], v[2]);
1555
+ }`,
1556
+ "vec4<i32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) {
1557
+ cv[0] = v[0];
1558
+ cv[1] = v[1];
1559
+ cv[2] = v[2];
1560
+ cv[3] = v[3];
1561
+ gl.uniform4i(location, v[0], v[1], v[2], v[3]);
1562
+ }`,
1563
+ u32: `if (cv !== v) {
1564
+ cu.value = v;
1565
+ gl.uniform1ui(location, v);
1566
+ }`,
1567
+ "vec2<u32>": `if (cv[0] !== v[0] || cv[1] !== v[1]) {
1568
+ cv[0] = v[0];
1569
+ cv[1] = v[1];
1570
+ gl.uniform2ui(location, v[0], v[1]);
1571
+ }`,
1572
+ "vec3<u32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) {
1573
+ cv[0] = v[0];
1574
+ cv[1] = v[1];
1575
+ cv[2] = v[2];
1576
+ gl.uniform3ui(location, v[0], v[1], v[2]);
1577
+ }`,
1578
+ "vec4<u32>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) {
1579
+ cv[0] = v[0];
1580
+ cv[1] = v[1];
1581
+ cv[2] = v[2];
1582
+ cv[3] = v[3];
1583
+ gl.uniform4ui(location, v[0], v[1], v[2], v[3]);
1584
+ }`,
1585
+ bool: `if (cv !== v) {
1586
+ cu.value = v;
1587
+ gl.uniform1i(location, v);
1588
+ }`,
1589
+ "vec2<bool>": `if (cv[0] !== v[0] || cv[1] !== v[1]) {
1590
+ cv[0] = v[0];
1591
+ cv[1] = v[1];
1592
+ gl.uniform2i(location, v[0], v[1]);
1593
+ }`,
1594
+ "vec3<bool>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) {
1595
+ cv[0] = v[0];
1596
+ cv[1] = v[1];
1597
+ cv[2] = v[2];
1598
+ gl.uniform3i(location, v[0], v[1], v[2]);
1599
+ }`,
1600
+ "vec4<bool>": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) {
1601
+ cv[0] = v[0];
1602
+ cv[1] = v[1];
1603
+ cv[2] = v[2];
1604
+ cv[3] = v[3];
1605
+ gl.uniform4i(location, v[0], v[1], v[2], v[3]);
1606
+ }`,
1607
+ "mat2x2<f32>": "gl.uniformMatrix2fv(location, false, v);",
1608
+ "mat3x3<f32>": "gl.uniformMatrix3fv(location, false, v);",
1609
+ "mat4x4<f32>": "gl.uniformMatrix4fv(location, false, v);"
1610
+ }, ut = {
1611
+ f32: "gl.uniform1fv(location, v);",
1612
+ "vec2<f32>": "gl.uniform2fv(location, v);",
1613
+ "vec3<f32>": "gl.uniform3fv(location, v);",
1614
+ "vec4<f32>": "gl.uniform4fv(location, v);",
1615
+ "mat2x2<f32>": "gl.uniformMatrix2fv(location, false, v);",
1616
+ "mat3x3<f32>": "gl.uniformMatrix3fv(location, false, v);",
1617
+ "mat4x4<f32>": "gl.uniformMatrix4fv(location, false, v);",
1618
+ i32: "gl.uniform1iv(location, v);",
1619
+ "vec2<i32>": "gl.uniform2iv(location, v);",
1620
+ "vec3<i32>": "gl.uniform3iv(location, v);",
1621
+ "vec4<i32>": "gl.uniform4iv(location, v);",
1622
+ u32: "gl.uniform1iv(location, v);",
1623
+ "vec2<u32>": "gl.uniform2iv(location, v);",
1624
+ "vec3<u32>": "gl.uniform3iv(location, v);",
1625
+ "vec4<u32>": "gl.uniform4iv(location, v);",
1626
+ bool: "gl.uniform1iv(location, v);",
1627
+ "vec2<bool>": "gl.uniform2iv(location, v);",
1628
+ "vec3<bool>": "gl.uniform3iv(location, v);",
1629
+ "vec4<bool>": "gl.uniform4iv(location, v);"
1630
+ };
1631
+ function _t(t, e) {
1632
+ const r = [`
1633
+ var v = null;
1634
+ var cv = null;
1635
+ var cu = null;
1636
+ var t = 0;
1637
+ var gl = renderer.gl;
1638
+ var name = null;
1639
+ `];
1640
+ for (const s in t.uniforms) {
1641
+ if (!e[s]) {
1642
+ t.uniforms[s] instanceof F ? t.uniforms[s].ubo ? r.push(`
1643
+ renderer.shader.bindUniformBlock(uv.${s}, "${s}");
1644
+ `) : r.push(`
1645
+ renderer.shader.updateUniformGroup(uv.${s});
1646
+ `) : t.uniforms[s] instanceof J && r.push(`
1647
+ renderer.shader.bindBufferResource(uv.${s}, "${s}");
1648
+ `);
1649
+ continue;
1650
+ }
1651
+ const n = t.uniformStructures[s];
1652
+ let i = !1;
1653
+ for (let a = 0; a < y.length; a++) {
1654
+ const o = y[a];
1655
+ if (n.type === o.type && o.test(n)) {
1656
+ r.push(`name = "${s}";`, y[a].uniform), i = !0;
1657
+ break;
1658
+ }
1659
+ }
1660
+ if (!i) {
1661
+ const o = (n.size === 1 ? ct : ut)[n.type].replace("location", `ud["${s}"].location`);
1662
+ r.push(`
1663
+ cu = ud["${s}"];
1664
+ cv = cu.value;
1665
+ v = uv["${s}"];
1666
+ ${o};`);
1667
+ }
1668
+ }
1669
+ return new Function("ud", "uv", "renderer", "syncData", r.join(`
1670
+ `));
1671
+ }
1672
+ class me {
1673
+ /** @param renderer - The renderer this System works for. */
1674
+ constructor(e) {
1675
+ this._cache = {}, this._uniformGroupSyncHash = {}, this._renderer = e, this.gl = null, this._cache = {};
1676
+ }
1677
+ contextChange(e) {
1678
+ this.gl = e;
1679
+ }
1680
+ /**
1681
+ * Uploads the uniforms values to the currently bound shader.
1682
+ * @param group - the uniforms values that be applied to the current shader
1683
+ * @param program
1684
+ * @param syncData
1685
+ * @param syncData.textureCount
1686
+ */
1687
+ updateUniformGroup(e, r, s) {
1688
+ const n = this._renderer.shader._getProgramData(r);
1689
+ (!e.isStatic || e._dirtyId !== n.uniformDirtyGroups[e.uid]) && (n.uniformDirtyGroups[e.uid] = e._dirtyId, this._getUniformSyncFunction(e, r)(n.uniformData, e.uniforms, this._renderer, s));
1690
+ }
1691
+ /**
1692
+ * Overridable by the pixi.js/unsafe-eval package to use static syncUniforms instead.
1693
+ * @param group
1694
+ * @param program
1695
+ */
1696
+ _getUniformSyncFunction(e, r) {
1697
+ var s;
1698
+ return ((s = this._uniformGroupSyncHash[e._signature]) == null ? void 0 : s[r._key]) || this._createUniformSyncFunction(e, r);
1699
+ }
1700
+ _createUniformSyncFunction(e, r) {
1701
+ const s = this._uniformGroupSyncHash[e._signature] || (this._uniformGroupSyncHash[e._signature] = {}), n = this._getSignature(e, r._uniformData, "u");
1702
+ return this._cache[n] || (this._cache[n] = this._generateUniformsSync(e, r._uniformData)), s[r._key] = this._cache[n], s[r._key];
1703
+ }
1704
+ _generateUniformsSync(e, r) {
1705
+ return _t(e, r);
1706
+ }
1707
+ /**
1708
+ * Takes a uniform group and data and generates a unique signature for them.
1709
+ * @param group - The uniform group to get signature of
1710
+ * @param group.uniforms
1711
+ * @param uniformData - Uniform information generated by the shader
1712
+ * @param preFix
1713
+ * @returns Unique signature of the uniform group
1714
+ */
1715
+ _getSignature(e, r, s) {
1716
+ const n = e.uniforms, i = [`${s}-`];
1717
+ for (const a in n)
1718
+ i.push(a), r[a] && i.push(r[a].type);
1719
+ return i.join("-");
1720
+ }
1721
+ /** Destroys this System and removes all its textures. */
1722
+ destroy() {
1723
+ this._renderer = null, this._cache = null;
1724
+ }
1725
+ }
1726
+ me.extension = {
1727
+ type: [
1728
+ d.WebGLSystem
1729
+ ],
1730
+ name: "uniformGroup"
1731
+ };
1732
+ function ht(t) {
1733
+ const e = {};
1734
+ if (e.normal = [t.ONE, t.ONE_MINUS_SRC_ALPHA], e.add = [t.ONE, t.ONE], e.multiply = [t.DST_COLOR, t.ONE_MINUS_SRC_ALPHA, t.ONE, t.ONE_MINUS_SRC_ALPHA], e.screen = [t.ONE, t.ONE_MINUS_SRC_COLOR, t.ONE, t.ONE_MINUS_SRC_ALPHA], e.none = [0, 0], e["normal-npm"] = [t.SRC_ALPHA, t.ONE_MINUS_SRC_ALPHA, t.ONE, t.ONE_MINUS_SRC_ALPHA], e["add-npm"] = [t.SRC_ALPHA, t.ONE, t.ONE, t.ONE], e["screen-npm"] = [t.SRC_ALPHA, t.ONE_MINUS_SRC_COLOR, t.ONE, t.ONE_MINUS_SRC_ALPHA], e.erase = [t.ZERO, t.ONE_MINUS_SRC_ALPHA], !(t instanceof S.get().getWebGLRenderingContext()))
1735
+ e.min = [t.ONE, t.ONE, t.ONE, t.ONE, t.MIN, t.MIN], e.max = [t.ONE, t.ONE, t.ONE, t.ONE, t.MAX, t.MAX];
1736
+ else {
1737
+ const s = t.getExtension("EXT_blend_minmax");
1738
+ s && (e.min = [t.ONE, t.ONE, t.ONE, t.ONE, s.MIN_EXT, s.MIN_EXT], e.max = [t.ONE, t.ONE, t.ONE, t.ONE, s.MAX_EXT, s.MAX_EXT]);
1739
+ }
1740
+ return e;
1741
+ }
1742
+ const ft = 0, lt = 1, dt = 2, mt = 3, Et = 4, bt = 5, Ee = class G {
1743
+ constructor(e) {
1744
+ this._invertFrontFace = !1, this.gl = null, this.stateId = 0, this.polygonOffset = 0, this.blendMode = "none", this._blendEq = !1, this.map = [], this.map[ft] = this.setBlend, this.map[lt] = this.setOffset, this.map[dt] = this.setCullFace, this.map[mt] = this.setDepthTest, this.map[Et] = this.setFrontFace, this.map[bt] = this.setDepthMask, this.checks = [], this.defaultState = O.for2d(), e.renderTarget.onRenderTargetChange.add(this);
1745
+ }
1746
+ onRenderTargetChange(e) {
1747
+ this._invertFrontFace = !e.isRoot, this._cullFace ? this.setFrontFace(this._frontFace) : this._frontFaceDirty = !0;
1748
+ }
1749
+ contextChange(e) {
1750
+ this.gl = e, this.blendModesMap = ht(e), this.resetState();
1751
+ }
1752
+ /**
1753
+ * Sets the current state
1754
+ * @param {*} state - The state to set.
1755
+ */
1756
+ set(e) {
1757
+ if (e || (e = this.defaultState), this.stateId !== e.data) {
1758
+ let r = this.stateId ^ e.data, s = 0;
1759
+ for (; r; )
1760
+ r & 1 && this.map[s].call(this, !!(e.data & 1 << s)), r >>= 1, s++;
1761
+ this.stateId = e.data;
1762
+ }
1763
+ for (let r = 0; r < this.checks.length; r++)
1764
+ this.checks[r](this, e);
1765
+ }
1766
+ /**
1767
+ * Sets the state, when previous state is unknown.
1768
+ * @param {*} state - The state to set
1769
+ */
1770
+ forceState(e) {
1771
+ e || (e = this.defaultState);
1772
+ for (let r = 0; r < this.map.length; r++)
1773
+ this.map[r].call(this, !!(e.data & 1 << r));
1774
+ for (let r = 0; r < this.checks.length; r++)
1775
+ this.checks[r](this, e);
1776
+ this.stateId = e.data;
1777
+ }
1778
+ /**
1779
+ * Sets whether to enable or disable blending.
1780
+ * @param value - Turn on or off WebGl blending.
1781
+ */
1782
+ setBlend(e) {
1783
+ this._updateCheck(G._checkBlendMode, e), this.gl[e ? "enable" : "disable"](this.gl.BLEND);
1784
+ }
1785
+ /**
1786
+ * Sets whether to enable or disable polygon offset fill.
1787
+ * @param value - Turn on or off webgl polygon offset testing.
1788
+ */
1789
+ setOffset(e) {
1790
+ this._updateCheck(G._checkPolygonOffset, e), this.gl[e ? "enable" : "disable"](this.gl.POLYGON_OFFSET_FILL);
1791
+ }
1792
+ /**
1793
+ * Sets whether to enable or disable depth test.
1794
+ * @param value - Turn on or off webgl depth testing.
1795
+ */
1796
+ setDepthTest(e) {
1797
+ this.gl[e ? "enable" : "disable"](this.gl.DEPTH_TEST);
1798
+ }
1799
+ /**
1800
+ * Sets whether to enable or disable depth mask.
1801
+ * @param value - Turn on or off webgl depth mask.
1802
+ */
1803
+ setDepthMask(e) {
1804
+ this.gl.depthMask(e);
1805
+ }
1806
+ /**
1807
+ * Sets whether to enable or disable cull face.
1808
+ * @param {boolean} value - Turn on or off webgl cull face.
1809
+ */
1810
+ setCullFace(e) {
1811
+ this._cullFace = e, this.gl[e ? "enable" : "disable"](this.gl.CULL_FACE), this._cullFace && this._frontFaceDirty && this.setFrontFace(this._frontFace);
1812
+ }
1813
+ /**
1814
+ * Sets the gl front face.
1815
+ * @param {boolean} value - true is clockwise and false is counter-clockwise
1816
+ */
1817
+ setFrontFace(e) {
1818
+ this._frontFace = e, this._frontFaceDirty = !1;
1819
+ const r = this._invertFrontFace ? !e : e;
1820
+ this._glFrontFace !== r && (this._glFrontFace = r, this.gl.frontFace(this.gl[r ? "CW" : "CCW"]));
1821
+ }
1822
+ /**
1823
+ * Sets the blend mode.
1824
+ * @param {number} value - The blend mode to set to.
1825
+ */
1826
+ setBlendMode(e) {
1827
+ if (this.blendModesMap[e] || (e = "normal"), e === this.blendMode)
1828
+ return;
1829
+ this.blendMode = e;
1830
+ const r = this.blendModesMap[e], s = this.gl;
1831
+ r.length === 2 ? s.blendFunc(r[0], r[1]) : s.blendFuncSeparate(r[0], r[1], r[2], r[3]), r.length === 6 ? (this._blendEq = !0, s.blendEquationSeparate(r[4], r[5])) : this._blendEq && (this._blendEq = !1, s.blendEquationSeparate(s.FUNC_ADD, s.FUNC_ADD));
1832
+ }
1833
+ /**
1834
+ * Sets the polygon offset.
1835
+ * @param {number} value - the polygon offset
1836
+ * @param {number} scale - the polygon offset scale
1837
+ */
1838
+ setPolygonOffset(e, r) {
1839
+ this.gl.polygonOffset(e, r);
1840
+ }
1841
+ /** Resets all the logic and disables the VAOs. */
1842
+ resetState() {
1843
+ this._glFrontFace = !1, this._frontFace = !1, this._cullFace = !1, this._frontFaceDirty = !1, this._invertFrontFace = !1, this.gl.frontFace(this.gl.CCW), this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, !1), this.forceState(this.defaultState), this._blendEq = !0, this.blendMode = "", this.setBlendMode("normal");
1844
+ }
1845
+ /**
1846
+ * Checks to see which updates should be checked based on which settings have been activated.
1847
+ *
1848
+ * For example, if blend is enabled then we should check the blend modes each time the state is changed
1849
+ * or if polygon fill is activated then we need to check if the polygon offset changes.
1850
+ * The idea is that we only check what we have too.
1851
+ * @param func - the checking function to add or remove
1852
+ * @param value - should the check function be added or removed.
1853
+ */
1854
+ _updateCheck(e, r) {
1855
+ const s = this.checks.indexOf(e);
1856
+ r && s === -1 ? this.checks.push(e) : !r && s !== -1 && this.checks.splice(s, 1);
1857
+ }
1858
+ /**
1859
+ * A private little wrapper function that we call to check the blend mode.
1860
+ * @param system - the System to perform the state check on
1861
+ * @param state - the state that the blendMode will pulled from
1862
+ */
1863
+ static _checkBlendMode(e, r) {
1864
+ e.setBlendMode(r.blendMode);
1865
+ }
1866
+ /**
1867
+ * A private little wrapper function that we call to check the polygon offset.
1868
+ * @param system - the System to perform the state check on
1869
+ * @param state - the state that the blendMode will pulled from
1870
+ */
1871
+ static _checkPolygonOffset(e, r) {
1872
+ e.setPolygonOffset(1, r.polygonOffset);
1873
+ }
1874
+ /** @ignore */
1875
+ destroy() {
1876
+ this.gl = null, this.checks.length = 0;
1877
+ }
1878
+ };
1879
+ Ee.extension = {
1880
+ type: [
1881
+ d.WebGLSystem
1882
+ ],
1883
+ name: "state"
1884
+ };
1885
+ let St = Ee;
1886
+ class pt {
1887
+ constructor(e) {
1888
+ this.target = re.TEXTURE_2D, this.texture = e, this.width = -1, this.height = -1, this.type = h.UNSIGNED_BYTE, this.internalFormat = D.RGBA, this.format = D.RGBA, this.samplerType = 0;
1889
+ }
1890
+ }
1891
+ const gt = {
1892
+ id: "buffer",
1893
+ upload(t, e, r) {
1894
+ e.width === t.width || e.height === t.height ? r.texSubImage2D(
1895
+ r.TEXTURE_2D,
1896
+ 0,
1897
+ 0,
1898
+ 0,
1899
+ t.width,
1900
+ t.height,
1901
+ e.format,
1902
+ e.type,
1903
+ t.resource
1904
+ ) : r.texImage2D(
1905
+ e.target,
1906
+ 0,
1907
+ e.internalFormat,
1908
+ t.width,
1909
+ t.height,
1910
+ 0,
1911
+ e.format,
1912
+ e.type,
1913
+ t.resource
1914
+ ), e.width = t.width, e.height = t.height;
1915
+ }
1916
+ }, Rt = {
1917
+ "bc1-rgba-unorm": !0,
1918
+ "bc1-rgba-unorm-srgb": !0,
1919
+ "bc2-rgba-unorm": !0,
1920
+ "bc2-rgba-unorm-srgb": !0,
1921
+ "bc3-rgba-unorm": !0,
1922
+ "bc3-rgba-unorm-srgb": !0,
1923
+ "bc4-r-unorm": !0,
1924
+ "bc4-r-snorm": !0,
1925
+ "bc5-rg-unorm": !0,
1926
+ "bc5-rg-snorm": !0,
1927
+ "bc6h-rgb-ufloat": !0,
1928
+ "bc6h-rgb-float": !0,
1929
+ "bc7-rgba-unorm": !0,
1930
+ "bc7-rgba-unorm-srgb": !0,
1931
+ // ETC2 compressed formats usable if "texture-compression-etc2" is both
1932
+ // supported by the device/user agent and enabled in requestDevice.
1933
+ "etc2-rgb8unorm": !0,
1934
+ "etc2-rgb8unorm-srgb": !0,
1935
+ "etc2-rgb8a1unorm": !0,
1936
+ "etc2-rgb8a1unorm-srgb": !0,
1937
+ "etc2-rgba8unorm": !0,
1938
+ "etc2-rgba8unorm-srgb": !0,
1939
+ "eac-r11unorm": !0,
1940
+ "eac-r11snorm": !0,
1941
+ "eac-rg11unorm": !0,
1942
+ "eac-rg11snorm": !0,
1943
+ // ASTC compressed formats usable if "texture-compression-astc" is both
1944
+ // supported by the device/user agent and enabled in requestDevice.
1945
+ "astc-4x4-unorm": !0,
1946
+ "astc-4x4-unorm-srgb": !0,
1947
+ "astc-5x4-unorm": !0,
1948
+ "astc-5x4-unorm-srgb": !0,
1949
+ "astc-5x5-unorm": !0,
1950
+ "astc-5x5-unorm-srgb": !0,
1951
+ "astc-6x5-unorm": !0,
1952
+ "astc-6x5-unorm-srgb": !0,
1953
+ "astc-6x6-unorm": !0,
1954
+ "astc-6x6-unorm-srgb": !0,
1955
+ "astc-8x5-unorm": !0,
1956
+ "astc-8x5-unorm-srgb": !0,
1957
+ "astc-8x6-unorm": !0,
1958
+ "astc-8x6-unorm-srgb": !0,
1959
+ "astc-8x8-unorm": !0,
1960
+ "astc-8x8-unorm-srgb": !0,
1961
+ "astc-10x5-unorm": !0,
1962
+ "astc-10x5-unorm-srgb": !0,
1963
+ "astc-10x6-unorm": !0,
1964
+ "astc-10x6-unorm-srgb": !0,
1965
+ "astc-10x8-unorm": !0,
1966
+ "astc-10x8-unorm-srgb": !0,
1967
+ "astc-10x10-unorm": !0,
1968
+ "astc-10x10-unorm-srgb": !0,
1969
+ "astc-12x10-unorm": !0,
1970
+ "astc-12x10-unorm-srgb": !0,
1971
+ "astc-12x12-unorm": !0,
1972
+ "astc-12x12-unorm-srgb": !0
1973
+ }, xt = {
1974
+ id: "compressed",
1975
+ upload(t, e, r) {
1976
+ r.pixelStorei(r.UNPACK_ALIGNMENT, 4);
1977
+ let s = t.pixelWidth, n = t.pixelHeight;
1978
+ const i = !!Rt[t.format];
1979
+ for (let a = 0; a < t.resource.length; a++) {
1980
+ const o = t.resource[a];
1981
+ i ? r.compressedTexImage2D(
1982
+ r.TEXTURE_2D,
1983
+ a,
1984
+ e.internalFormat,
1985
+ s,
1986
+ n,
1987
+ 0,
1988
+ o
1989
+ ) : r.texImage2D(
1990
+ r.TEXTURE_2D,
1991
+ a,
1992
+ e.internalFormat,
1993
+ s,
1994
+ n,
1995
+ 0,
1996
+ e.format,
1997
+ e.type,
1998
+ o
1999
+ ), s = Math.max(s >> 1, 1), n = Math.max(n >> 1, 1);
2000
+ }
2001
+ }
2002
+ }, be = {
2003
+ id: "image",
2004
+ upload(t, e, r, s) {
2005
+ const n = e.width, i = e.height, a = t.pixelWidth, o = t.pixelHeight, c = t.resourceWidth, u = t.resourceHeight;
2006
+ c < a || u < o ? ((n !== a || i !== o) && r.texImage2D(
2007
+ e.target,
2008
+ 0,
2009
+ e.internalFormat,
2010
+ a,
2011
+ o,
2012
+ 0,
2013
+ e.format,
2014
+ e.type,
2015
+ null
2016
+ ), s === 2 ? r.texSubImage2D(
2017
+ r.TEXTURE_2D,
2018
+ 0,
2019
+ 0,
2020
+ 0,
2021
+ c,
2022
+ u,
2023
+ e.format,
2024
+ e.type,
2025
+ t.resource
2026
+ ) : r.texSubImage2D(
2027
+ r.TEXTURE_2D,
2028
+ 0,
2029
+ 0,
2030
+ 0,
2031
+ e.format,
2032
+ e.type,
2033
+ t.resource
2034
+ )) : n === a && i === o ? r.texSubImage2D(
2035
+ r.TEXTURE_2D,
2036
+ 0,
2037
+ 0,
2038
+ 0,
2039
+ e.format,
2040
+ e.type,
2041
+ t.resource
2042
+ ) : s === 2 ? r.texImage2D(
2043
+ e.target,
2044
+ 0,
2045
+ e.internalFormat,
2046
+ a,
2047
+ o,
2048
+ 0,
2049
+ e.format,
2050
+ e.type,
2051
+ t.resource
2052
+ ) : r.texImage2D(
2053
+ e.target,
2054
+ 0,
2055
+ e.internalFormat,
2056
+ e.format,
2057
+ e.type,
2058
+ t.resource
2059
+ ), e.width = a, e.height = o;
2060
+ }
2061
+ }, Tt = {
2062
+ id: "video",
2063
+ upload(t, e, r, s) {
2064
+ if (!t.isValid) {
2065
+ r.texImage2D(
2066
+ e.target,
2067
+ 0,
2068
+ e.internalFormat,
2069
+ 1,
2070
+ 1,
2071
+ 0,
2072
+ e.format,
2073
+ e.type,
2074
+ null
2075
+ );
2076
+ return;
2077
+ }
2078
+ be.upload(t, e, r, s);
2079
+ }
2080
+ }, W = {
2081
+ linear: 9729,
2082
+ nearest: 9728
2083
+ }, vt = {
2084
+ linear: {
2085
+ linear: 9987,
2086
+ nearest: 9985
2087
+ },
2088
+ nearest: {
2089
+ linear: 9986,
2090
+ nearest: 9984
2091
+ }
2092
+ }, I = {
2093
+ "clamp-to-edge": 33071,
2094
+ repeat: 10497,
2095
+ "mirror-repeat": 33648
2096
+ }, Bt = {
2097
+ never: 512,
2098
+ less: 513,
2099
+ equal: 514,
2100
+ "less-equal": 515,
2101
+ greater: 516,
2102
+ "not-equal": 517,
2103
+ "greater-equal": 518,
2104
+ always: 519
2105
+ };
2106
+ function j(t, e, r, s, n, i, a, o) {
2107
+ const c = i;
2108
+ if (!o || t.addressModeU !== "repeat" || t.addressModeV !== "repeat" || t.addressModeW !== "repeat") {
2109
+ const u = I[a ? "clamp-to-edge" : t.addressModeU], _ = I[a ? "clamp-to-edge" : t.addressModeV], f = I[a ? "clamp-to-edge" : t.addressModeW];
2110
+ e[n](c, e.TEXTURE_WRAP_S, u), e[n](c, e.TEXTURE_WRAP_T, _), e.TEXTURE_WRAP_R && e[n](c, e.TEXTURE_WRAP_R, f);
2111
+ }
2112
+ if ((!o || t.magFilter !== "linear") && e[n](c, e.TEXTURE_MAG_FILTER, W[t.magFilter]), r) {
2113
+ if (!o || t.mipmapFilter !== "linear") {
2114
+ const u = vt[t.minFilter][t.mipmapFilter];
2115
+ e[n](c, e.TEXTURE_MIN_FILTER, u);
2116
+ }
2117
+ } else
2118
+ e[n](c, e.TEXTURE_MIN_FILTER, W[t.minFilter]);
2119
+ if (s && t.maxAnisotropy > 1) {
2120
+ const u = Math.min(t.maxAnisotropy, e.getParameter(s.MAX_TEXTURE_MAX_ANISOTROPY_EXT));
2121
+ e[n](c, s.TEXTURE_MAX_ANISOTROPY_EXT, u);
2122
+ }
2123
+ t.compare && e[n](c, e.TEXTURE_COMPARE_FUNC, Bt[t.compare]);
2124
+ }
2125
+ function At(t) {
2126
+ return {
2127
+ // 8-bit formats
2128
+ r8unorm: t.RED,
2129
+ r8snorm: t.RED,
2130
+ r8uint: t.RED,
2131
+ r8sint: t.RED,
2132
+ // 16-bit formats
2133
+ r16uint: t.RED,
2134
+ r16sint: t.RED,
2135
+ r16float: t.RED,
2136
+ rg8unorm: t.RG,
2137
+ rg8snorm: t.RG,
2138
+ rg8uint: t.RG,
2139
+ rg8sint: t.RG,
2140
+ // 32-bit formats
2141
+ r32uint: t.RED,
2142
+ r32sint: t.RED,
2143
+ r32float: t.RED,
2144
+ rg16uint: t.RG,
2145
+ rg16sint: t.RG,
2146
+ rg16float: t.RG,
2147
+ rgba8unorm: t.RGBA,
2148
+ "rgba8unorm-srgb": t.RGBA,
2149
+ // Packed 32-bit formats
2150
+ rgba8snorm: t.RGBA,
2151
+ rgba8uint: t.RGBA,
2152
+ rgba8sint: t.RGBA,
2153
+ bgra8unorm: t.RGBA,
2154
+ "bgra8unorm-srgb": t.RGBA,
2155
+ rgb9e5ufloat: t.RGB,
2156
+ rgb10a2unorm: t.RGBA,
2157
+ rg11b10ufloat: t.RGB,
2158
+ // 64-bit formats
2159
+ rg32uint: t.RG,
2160
+ rg32sint: t.RG,
2161
+ rg32float: t.RG,
2162
+ rgba16uint: t.RGBA,
2163
+ rgba16sint: t.RGBA,
2164
+ rgba16float: t.RGBA,
2165
+ // 128-bit formats
2166
+ rgba32uint: t.RGBA,
2167
+ rgba32sint: t.RGBA,
2168
+ rgba32float: t.RGBA,
2169
+ // Depth/stencil formats
2170
+ stencil8: t.STENCIL_INDEX8,
2171
+ depth16unorm: t.DEPTH_COMPONENT,
2172
+ depth24plus: t.DEPTH_COMPONENT,
2173
+ "depth24plus-stencil8": t.DEPTH_STENCIL,
2174
+ depth32float: t.DEPTH_COMPONENT,
2175
+ "depth32float-stencil8": t.DEPTH_STENCIL
2176
+ };
2177
+ }
2178
+ function Nt(t, e) {
2179
+ let r = {}, s = t.RGBA;
2180
+ return t instanceof S.get().getWebGLRenderingContext() ? e.srgb && (r = {
2181
+ "rgba8unorm-srgb": e.srgb.SRGB8_ALPHA8_EXT,
2182
+ "bgra8unorm-srgb": e.srgb.SRGB8_ALPHA8_EXT
2183
+ }) : (r = {
2184
+ "rgba8unorm-srgb": t.SRGB8_ALPHA8,
2185
+ "bgra8unorm-srgb": t.SRGB8_ALPHA8
2186
+ }, s = t.RGBA8), {
2187
+ // 8-bit formats
2188
+ r8unorm: t.R8,
2189
+ r8snorm: t.R8_SNORM,
2190
+ r8uint: t.R8UI,
2191
+ r8sint: t.R8I,
2192
+ // 16-bit formats
2193
+ r16uint: t.R16UI,
2194
+ r16sint: t.R16I,
2195
+ r16float: t.R16F,
2196
+ rg8unorm: t.RG8,
2197
+ rg8snorm: t.RG8_SNORM,
2198
+ rg8uint: t.RG8UI,
2199
+ rg8sint: t.RG8I,
2200
+ // 32-bit formats
2201
+ r32uint: t.R32UI,
2202
+ r32sint: t.R32I,
2203
+ r32float: t.R32F,
2204
+ rg16uint: t.RG16UI,
2205
+ rg16sint: t.RG16I,
2206
+ rg16float: t.RG16F,
2207
+ rgba8unorm: t.RGBA,
2208
+ ...r,
2209
+ // Packed 32-bit formats
2210
+ rgba8snorm: t.RGBA8_SNORM,
2211
+ rgba8uint: t.RGBA8UI,
2212
+ rgba8sint: t.RGBA8I,
2213
+ bgra8unorm: s,
2214
+ rgb9e5ufloat: t.RGB9_E5,
2215
+ rgb10a2unorm: t.RGB10_A2,
2216
+ rg11b10ufloat: t.R11F_G11F_B10F,
2217
+ // 64-bit formats
2218
+ rg32uint: t.RG32UI,
2219
+ rg32sint: t.RG32I,
2220
+ rg32float: t.RG32F,
2221
+ rgba16uint: t.RGBA16UI,
2222
+ rgba16sint: t.RGBA16I,
2223
+ rgba16float: t.RGBA16F,
2224
+ // 128-bit formats
2225
+ rgba32uint: t.RGBA32UI,
2226
+ rgba32sint: t.RGBA32I,
2227
+ rgba32float: t.RGBA32F,
2228
+ // Depth/stencil formats
2229
+ stencil8: t.STENCIL_INDEX8,
2230
+ depth16unorm: t.DEPTH_COMPONENT16,
2231
+ depth24plus: t.DEPTH_COMPONENT24,
2232
+ "depth24plus-stencil8": t.DEPTH24_STENCIL8,
2233
+ depth32float: t.DEPTH_COMPONENT32F,
2234
+ "depth32float-stencil8": t.DEPTH32F_STENCIL8,
2235
+ // Compressed formats
2236
+ ...e.s3tc ? {
2237
+ "bc1-rgba-unorm": e.s3tc.COMPRESSED_RGBA_S3TC_DXT1_EXT,
2238
+ "bc2-rgba-unorm": e.s3tc.COMPRESSED_RGBA_S3TC_DXT3_EXT,
2239
+ "bc3-rgba-unorm": e.s3tc.COMPRESSED_RGBA_S3TC_DXT5_EXT
2240
+ } : {},
2241
+ ...e.s3tc_sRGB ? {
2242
+ "bc1-rgba-unorm-srgb": e.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
2243
+ "bc2-rgba-unorm-srgb": e.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
2244
+ "bc3-rgba-unorm-srgb": e.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
2245
+ } : {},
2246
+ ...e.rgtc ? {
2247
+ "bc4-r-unorm": e.rgtc.COMPRESSED_RED_RGTC1_EXT,
2248
+ "bc4-r-snorm": e.rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT,
2249
+ "bc5-rg-unorm": e.rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT,
2250
+ "bc5-rg-snorm": e.rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
2251
+ } : {},
2252
+ ...e.bptc ? {
2253
+ "bc6h-rgb-float": e.bptc.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT,
2254
+ "bc6h-rgb-ufloat": e.bptc.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT,
2255
+ "bc7-rgba-unorm": e.bptc.COMPRESSED_RGBA_BPTC_UNORM_EXT,
2256
+ "bc7-rgba-unorm-srgb": e.bptc.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT
2257
+ } : {},
2258
+ ...e.etc ? {
2259
+ "etc2-rgb8unorm": e.etc.COMPRESSED_RGB8_ETC2,
2260
+ "etc2-rgb8unorm-srgb": e.etc.COMPRESSED_SRGB8_ETC2,
2261
+ "etc2-rgb8a1unorm": e.etc.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
2262
+ "etc2-rgb8a1unorm-srgb": e.etc.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
2263
+ "etc2-rgba8unorm": e.etc.COMPRESSED_RGBA8_ETC2_EAC,
2264
+ "etc2-rgba8unorm-srgb": e.etc.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
2265
+ "eac-r11unorm": e.etc.COMPRESSED_R11_EAC,
2266
+ // 'eac-r11snorm'
2267
+ "eac-rg11unorm": e.etc.COMPRESSED_SIGNED_RG11_EAC
2268
+ // 'eac-rg11snorm'
2269
+ } : {},
2270
+ ...e.astc ? {
2271
+ "astc-4x4-unorm": e.astc.COMPRESSED_RGBA_ASTC_4x4_KHR,
2272
+ "astc-4x4-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,
2273
+ "astc-5x4-unorm": e.astc.COMPRESSED_RGBA_ASTC_5x4_KHR,
2274
+ "astc-5x4-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,
2275
+ "astc-5x5-unorm": e.astc.COMPRESSED_RGBA_ASTC_5x5_KHR,
2276
+ "astc-5x5-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,
2277
+ "astc-6x5-unorm": e.astc.COMPRESSED_RGBA_ASTC_6x5_KHR,
2278
+ "astc-6x5-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,
2279
+ "astc-6x6-unorm": e.astc.COMPRESSED_RGBA_ASTC_6x6_KHR,
2280
+ "astc-6x6-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,
2281
+ "astc-8x5-unorm": e.astc.COMPRESSED_RGBA_ASTC_8x5_KHR,
2282
+ "astc-8x5-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,
2283
+ "astc-8x6-unorm": e.astc.COMPRESSED_RGBA_ASTC_8x6_KHR,
2284
+ "astc-8x6-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,
2285
+ "astc-8x8-unorm": e.astc.COMPRESSED_RGBA_ASTC_8x8_KHR,
2286
+ "astc-8x8-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,
2287
+ "astc-10x5-unorm": e.astc.COMPRESSED_RGBA_ASTC_10x5_KHR,
2288
+ "astc-10x5-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,
2289
+ "astc-10x6-unorm": e.astc.COMPRESSED_RGBA_ASTC_10x6_KHR,
2290
+ "astc-10x6-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,
2291
+ "astc-10x8-unorm": e.astc.COMPRESSED_RGBA_ASTC_10x8_KHR,
2292
+ "astc-10x8-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,
2293
+ "astc-10x10-unorm": e.astc.COMPRESSED_RGBA_ASTC_10x10_KHR,
2294
+ "astc-10x10-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
2295
+ "astc-12x10-unorm": e.astc.COMPRESSED_RGBA_ASTC_12x10_KHR,
2296
+ "astc-12x10-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,
2297
+ "astc-12x12-unorm": e.astc.COMPRESSED_RGBA_ASTC_12x12_KHR,
2298
+ "astc-12x12-unorm-srgb": e.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR
2299
+ } : {}
2300
+ };
2301
+ }
2302
+ function yt(t) {
2303
+ return {
2304
+ // 8-bit formats
2305
+ r8unorm: t.UNSIGNED_BYTE,
2306
+ r8snorm: t.BYTE,
2307
+ r8uint: t.UNSIGNED_BYTE,
2308
+ r8sint: t.BYTE,
2309
+ // 16-bit formats
2310
+ r16uint: t.UNSIGNED_SHORT,
2311
+ r16sint: t.SHORT,
2312
+ r16float: t.HALF_FLOAT,
2313
+ rg8unorm: t.UNSIGNED_BYTE,
2314
+ rg8snorm: t.BYTE,
2315
+ rg8uint: t.UNSIGNED_BYTE,
2316
+ rg8sint: t.BYTE,
2317
+ // 32-bit formats
2318
+ r32uint: t.UNSIGNED_INT,
2319
+ r32sint: t.INT,
2320
+ r32float: t.FLOAT,
2321
+ rg16uint: t.UNSIGNED_SHORT,
2322
+ rg16sint: t.SHORT,
2323
+ rg16float: t.HALF_FLOAT,
2324
+ rgba8unorm: t.UNSIGNED_BYTE,
2325
+ "rgba8unorm-srgb": t.UNSIGNED_BYTE,
2326
+ // Packed 32-bit formats
2327
+ rgba8snorm: t.BYTE,
2328
+ rgba8uint: t.UNSIGNED_BYTE,
2329
+ rgba8sint: t.BYTE,
2330
+ bgra8unorm: t.UNSIGNED_BYTE,
2331
+ "bgra8unorm-srgb": t.UNSIGNED_BYTE,
2332
+ rgb9e5ufloat: t.UNSIGNED_INT_5_9_9_9_REV,
2333
+ rgb10a2unorm: t.UNSIGNED_INT_2_10_10_10_REV,
2334
+ rg11b10ufloat: t.UNSIGNED_INT_10F_11F_11F_REV,
2335
+ // 64-bit formats
2336
+ rg32uint: t.UNSIGNED_INT,
2337
+ rg32sint: t.INT,
2338
+ rg32float: t.FLOAT,
2339
+ rgba16uint: t.UNSIGNED_SHORT,
2340
+ rgba16sint: t.SHORT,
2341
+ rgba16float: t.HALF_FLOAT,
2342
+ // 128-bit formats
2343
+ rgba32uint: t.UNSIGNED_INT,
2344
+ rgba32sint: t.INT,
2345
+ rgba32float: t.FLOAT,
2346
+ // Depth/stencil formats
2347
+ stencil8: t.UNSIGNED_BYTE,
2348
+ depth16unorm: t.UNSIGNED_SHORT,
2349
+ depth24plus: t.UNSIGNED_INT,
2350
+ "depth24plus-stencil8": t.UNSIGNED_INT_24_8,
2351
+ depth32float: t.FLOAT,
2352
+ "depth32float-stencil8": t.FLOAT_32_UNSIGNED_INT_24_8_REV
2353
+ };
2354
+ }
2355
+ const Ct = 4;
2356
+ class Se {
2357
+ constructor(e) {
2358
+ this.managedTextures = [], this._glTextures = /* @__PURE__ */ Object.create(null), this._glSamplers = /* @__PURE__ */ Object.create(null), this._boundTextures = [], this._activeTextureLocation = -1, this._boundSamplers = /* @__PURE__ */ Object.create(null), this._uploads = {
2359
+ image: be,
2360
+ buffer: gt,
2361
+ video: Tt,
2362
+ compressed: xt
2363
+ }, this._premultiplyAlpha = !1, this._useSeparateSamplers = !1, this._renderer = e, this._renderer.renderableGC.addManagedHash(this, "_glTextures"), this._renderer.renderableGC.addManagedHash(this, "_glSamplers");
2364
+ }
2365
+ contextChange(e) {
2366
+ this._gl = e, this._mapFormatToInternalFormat || (this._mapFormatToInternalFormat = Nt(e, this._renderer.context.extensions), this._mapFormatToType = yt(e), this._mapFormatToFormat = At(e)), this._glTextures = /* @__PURE__ */ Object.create(null), this._glSamplers = /* @__PURE__ */ Object.create(null), this._boundSamplers = /* @__PURE__ */ Object.create(null), this._premultiplyAlpha = !1;
2367
+ for (let r = 0; r < 16; r++)
2368
+ this.bind(b.EMPTY, r);
2369
+ }
2370
+ /**
2371
+ * Initializes a texture source, if it has already been initialized nothing will happen.
2372
+ * @param source - The texture source to initialize.
2373
+ * @returns The initialized texture source.
2374
+ */
2375
+ initSource(e) {
2376
+ this.bind(e);
2377
+ }
2378
+ bind(e, r = 0) {
2379
+ const s = e.source;
2380
+ e ? (this.bindSource(s, r), this._useSeparateSamplers && this._bindSampler(s.style, r)) : (this.bindSource(null, r), this._useSeparateSamplers && this._bindSampler(null, r));
2381
+ }
2382
+ bindSource(e, r = 0) {
2383
+ const s = this._gl;
2384
+ if (e._touched = this._renderer.textureGC.count, this._boundTextures[r] !== e) {
2385
+ this._boundTextures[r] = e, this._activateLocation(r), e || (e = b.EMPTY.source);
2386
+ const n = this.getGlSource(e);
2387
+ s.bindTexture(n.target, n.texture);
2388
+ }
2389
+ }
2390
+ _bindSampler(e, r = 0) {
2391
+ const s = this._gl;
2392
+ if (!e) {
2393
+ this._boundSamplers[r] = null, s.bindSampler(r, null);
2394
+ return;
2395
+ }
2396
+ const n = this._getGlSampler(e);
2397
+ this._boundSamplers[r] !== n && (this._boundSamplers[r] = n, s.bindSampler(r, n));
2398
+ }
2399
+ unbind(e) {
2400
+ const r = e.source, s = this._boundTextures, n = this._gl;
2401
+ for (let i = 0; i < s.length; i++)
2402
+ if (s[i] === r) {
2403
+ this._activateLocation(i);
2404
+ const a = this.getGlSource(r);
2405
+ n.bindTexture(a.target, null), s[i] = null;
2406
+ }
2407
+ }
2408
+ _activateLocation(e) {
2409
+ this._activeTextureLocation !== e && (this._activeTextureLocation = e, this._gl.activeTexture(this._gl.TEXTURE0 + e));
2410
+ }
2411
+ _initSource(e) {
2412
+ const r = this._gl, s = new pt(r.createTexture());
2413
+ if (s.type = this._mapFormatToType[e.format], s.internalFormat = this._mapFormatToInternalFormat[e.format], s.format = this._mapFormatToFormat[e.format], e.autoGenerateMipmaps && (this._renderer.context.supports.nonPowOf2mipmaps || e.isPowerOfTwo)) {
2414
+ const n = Math.max(e.width, e.height);
2415
+ e.mipLevelCount = Math.floor(Math.log2(n)) + 1;
2416
+ }
2417
+ return this._glTextures[e.uid] = s, this.managedTextures.includes(e) || (e.on("update", this.onSourceUpdate, this), e.on("resize", this.onSourceUpdate, this), e.on("styleChange", this.onStyleChange, this), e.on("destroy", this.onSourceDestroy, this), e.on("unload", this.onSourceUnload, this), e.on("updateMipmaps", this.onUpdateMipmaps, this), this.managedTextures.push(e)), this.onSourceUpdate(e), this.updateStyle(e, !1), s;
2418
+ }
2419
+ onStyleChange(e) {
2420
+ this.updateStyle(e, !1);
2421
+ }
2422
+ updateStyle(e, r) {
2423
+ const s = this._gl, n = this.getGlSource(e);
2424
+ s.bindTexture(s.TEXTURE_2D, n.texture), this._boundTextures[this._activeTextureLocation] = e, j(
2425
+ e.style,
2426
+ s,
2427
+ e.mipLevelCount > 1,
2428
+ this._renderer.context.extensions.anisotropicFiltering,
2429
+ "texParameteri",
2430
+ s.TEXTURE_2D,
2431
+ // will force a clamp to edge if the texture is not a power of two
2432
+ !this._renderer.context.supports.nonPowOf2wrapping && !e.isPowerOfTwo,
2433
+ r
2434
+ );
2435
+ }
2436
+ onSourceUnload(e) {
2437
+ const r = this._glTextures[e.uid];
2438
+ r && (this.unbind(e), this._glTextures[e.uid] = null, this._gl.deleteTexture(r.texture));
2439
+ }
2440
+ onSourceUpdate(e) {
2441
+ const r = this._gl, s = this.getGlSource(e);
2442
+ r.bindTexture(r.TEXTURE_2D, s.texture), this._boundTextures[this._activeTextureLocation] = e;
2443
+ const n = e.alphaMode === "premultiply-alpha-on-upload";
2444
+ this._premultiplyAlpha !== n && (this._premultiplyAlpha = n, r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL, n)), this._uploads[e.uploadMethodId] ? this._uploads[e.uploadMethodId].upload(e, s, r, this._renderer.context.webGLVersion) : r.texImage2D(r.TEXTURE_2D, 0, r.RGBA, e.pixelWidth, e.pixelHeight, 0, r.RGBA, r.UNSIGNED_BYTE, null), e.autoGenerateMipmaps && e.mipLevelCount > 1 && this.onUpdateMipmaps(e, !1);
2445
+ }
2446
+ onUpdateMipmaps(e, r = !0) {
2447
+ r && this.bindSource(e, 0);
2448
+ const s = this.getGlSource(e);
2449
+ this._gl.generateMipmap(s.target);
2450
+ }
2451
+ onSourceDestroy(e) {
2452
+ e.off("destroy", this.onSourceDestroy, this), e.off("update", this.onSourceUpdate, this), e.off("resize", this.onSourceUpdate, this), e.off("unload", this.onSourceUnload, this), e.off("styleChange", this.onStyleChange, this), e.off("updateMipmaps", this.onUpdateMipmaps, this), this.managedTextures.splice(this.managedTextures.indexOf(e), 1), this.onSourceUnload(e);
2453
+ }
2454
+ _initSampler(e) {
2455
+ const r = this._gl, s = this._gl.createSampler();
2456
+ return this._glSamplers[e._resourceId] = s, j(
2457
+ e,
2458
+ r,
2459
+ this._boundTextures[this._activeTextureLocation].mipLevelCount > 1,
2460
+ this._renderer.context.extensions.anisotropicFiltering,
2461
+ "samplerParameteri",
2462
+ s,
2463
+ !1,
2464
+ !0
2465
+ ), this._glSamplers[e._resourceId];
2466
+ }
2467
+ _getGlSampler(e) {
2468
+ return this._glSamplers[e._resourceId] || this._initSampler(e);
2469
+ }
2470
+ getGlSource(e) {
2471
+ return this._glTextures[e.uid] || this._initSource(e);
2472
+ }
2473
+ generateCanvas(e) {
2474
+ const { pixels: r, width: s, height: n } = this.getPixels(e), i = S.get().createCanvas();
2475
+ i.width = s, i.height = n;
2476
+ const a = i.getContext("2d");
2477
+ if (a) {
2478
+ const o = a.createImageData(s, n);
2479
+ o.data.set(r), a.putImageData(o, 0, 0);
2480
+ }
2481
+ return i;
2482
+ }
2483
+ getPixels(e) {
2484
+ const r = e.source.resolution, s = e.frame, n = Math.max(Math.round(s.width * r), 1), i = Math.max(Math.round(s.height * r), 1), a = new Uint8Array(Ct * n * i), o = this._renderer, c = o.renderTarget.getRenderTarget(e), u = o.renderTarget.getGpuRenderTarget(c), _ = o.gl;
2485
+ return _.bindFramebuffer(_.FRAMEBUFFER, u.resolveTargetFramebuffer), _.readPixels(
2486
+ Math.round(s.x * r),
2487
+ Math.round(s.y * r),
2488
+ n,
2489
+ i,
2490
+ _.RGBA,
2491
+ _.UNSIGNED_BYTE,
2492
+ a
2493
+ ), { pixels: new Uint8ClampedArray(a.buffer), width: n, height: i };
2494
+ }
2495
+ destroy() {
2496
+ this.managedTextures.slice().forEach((e) => this.onSourceDestroy(e)), this.managedTextures = null, this._glTextures = null, this._glSamplers = null, this._boundTextures = null, this._boundSamplers = null, this._mapFormatToInternalFormat = null, this._mapFormatToType = null, this._mapFormatToFormat = null, this._uploads = null, this._renderer = null;
2497
+ }
2498
+ resetState() {
2499
+ this._activeTextureLocation = -1, this._boundTextures.fill(b.EMPTY.source), this._boundSamplers = /* @__PURE__ */ Object.create(null);
2500
+ const e = this._gl;
2501
+ this._premultiplyAlpha = !1, e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._premultiplyAlpha);
2502
+ }
2503
+ }
2504
+ Se.extension = {
2505
+ type: [
2506
+ d.WebGLSystem
2507
+ ],
2508
+ name: "texture"
2509
+ };
2510
+ class pe {
2511
+ contextChange(e) {
2512
+ const r = new F({
2513
+ uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
2514
+ uTransformMatrix: { value: new z(), type: "mat3x3<f32>" },
2515
+ uRound: { value: 0, type: "f32" }
2516
+ }), s = e.limits.maxBatchableTextures, n = Y({
2517
+ name: "graphics",
2518
+ bits: [
2519
+ ye,
2520
+ Ce(s),
2521
+ Z,
2522
+ q
2523
+ ]
2524
+ });
2525
+ this.shader = new U({
2526
+ glProgram: n,
2527
+ resources: {
2528
+ localUniforms: r,
2529
+ batchSamplers: Ie(s)
2530
+ }
2531
+ });
2532
+ }
2533
+ execute(e, r) {
2534
+ const s = r.context, n = s.customShader || this.shader, i = e.renderer, a = i.graphicsContext, {
2535
+ batcher: o,
2536
+ instructions: c
2537
+ } = a.getContextRenderData(s);
2538
+ n.groups[0] = i.globalUniforms.bindGroup, i.state.set(e.state), i.shader.bind(n), i.geometry.bind(o.geometry, n.glProgram);
2539
+ const u = c.instructions;
2540
+ for (let _ = 0; _ < c.instructionSize; _++) {
2541
+ const f = u[_];
2542
+ if (f.size) {
2543
+ for (let l = 0; l < f.textures.count; l++)
2544
+ i.texture.bind(f.textures.textures[l], l);
2545
+ i.geometry.draw(f.topology, f.size, f.start);
2546
+ }
2547
+ }
2548
+ }
2549
+ destroy() {
2550
+ this.shader.destroy(!0), this.shader = null;
2551
+ }
2552
+ }
2553
+ pe.extension = {
2554
+ type: [
2555
+ d.WebGLPipesAdaptor
2556
+ ],
2557
+ name: "graphics"
2558
+ };
2559
+ class ge {
2560
+ init() {
2561
+ const e = Y({
2562
+ name: "mesh",
2563
+ bits: [
2564
+ Z,
2565
+ He,
2566
+ q
2567
+ ]
2568
+ });
2569
+ this._shader = new U({
2570
+ glProgram: e,
2571
+ resources: {
2572
+ uTexture: b.EMPTY.source,
2573
+ textureUniforms: {
2574
+ uTextureMatrix: { type: "mat3x3<f32>", value: new z() }
2575
+ }
2576
+ }
2577
+ });
2578
+ }
2579
+ execute(e, r) {
2580
+ const s = e.renderer;
2581
+ let n = r._shader;
2582
+ if (n) {
2583
+ if (!n.glProgram) {
2584
+ m("Mesh shader has no glProgram", r.shader);
2585
+ return;
2586
+ }
2587
+ } else {
2588
+ n = this._shader;
2589
+ const i = r.texture, a = i.source;
2590
+ n.resources.uTexture = a, n.resources.uSampler = a.style, n.resources.textureUniforms.uniforms.uTextureMatrix = i.textureMatrix.mapCoord;
2591
+ }
2592
+ n.groups[100] = s.globalUniforms.bindGroup, n.groups[101] = e.localUniformsBindGroup, s.encoder.draw({
2593
+ geometry: r._geometry,
2594
+ shader: n,
2595
+ state: r.state
2596
+ });
2597
+ }
2598
+ destroy() {
2599
+ this._shader.destroy(!0), this._shader = null;
2600
+ }
2601
+ }
2602
+ ge.extension = {
2603
+ type: [
2604
+ d.WebGLPipesAdaptor
2605
+ ],
2606
+ name: "mesh"
2607
+ };
2608
+ const It = [
2609
+ ...we,
2610
+ _e,
2611
+ $e,
2612
+ Xe,
2613
+ oe,
2614
+ ee,
2615
+ Se,
2616
+ he,
2617
+ se,
2618
+ me,
2619
+ de,
2620
+ ae,
2621
+ St,
2622
+ ce,
2623
+ ie
2624
+ ], Dt = [...Ve], Gt = [Q, ge, pe], Re = [], xe = [], Te = [];
2625
+ T.handleByNamedList(d.WebGLSystem, Re);
2626
+ T.handleByNamedList(d.WebGLPipes, xe);
2627
+ T.handleByNamedList(d.WebGLPipesAdaptor, Te);
2628
+ T.add(...It, ...Dt, ...Gt);
2629
+ class Pt extends De {
2630
+ constructor() {
2631
+ const e = {
2632
+ name: "webgl",
2633
+ type: Ge.WEBGL,
2634
+ systems: Re,
2635
+ renderPipes: xe,
2636
+ renderPipeAdaptors: Te
2637
+ };
2638
+ super(e);
2639
+ }
2640
+ }
2641
+ export {
2642
+ Pt as WebGLRenderer
2643
+ };
src/backend/gradio_polygonannotator/templates/component/WebGPURenderer-D3ZRw0IF.js ADDED
@@ -0,0 +1,1655 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { g as E, E as p, f as ae, D as B, S as C, B as T, b as ue, c as L, d as M, w as v, h as y, i as ce, j as de, k as A, l as w, M as k, m as D, n as he, o as pe, p as H, q as z, s as R, A as le, R as fe, e as S } from "./Index-jQCzN2ap.js";
2
+ import { S as F, l as ge, a as me } from "./colorToUniform-zJcCVLeu.js";
3
+ import { c as _e, u as be, U as ye, B as xe, G as Ge, e as Be, R as Se, t as Pe, S as Te, a as Ce } from "./SharedSystems-DOa2TyTQ.js";
4
+ const x = F.for2d();
5
+ class O {
6
+ start(e, t, r) {
7
+ const s = e.renderer, i = s.encoder, n = r.gpuProgram;
8
+ this._shader = r, this._geometry = t, i.setGeometry(t, n), x.blendMode = "normal", s.pipeline.getPipeline(
9
+ t,
10
+ n,
11
+ x
12
+ );
13
+ const o = s.globalUniforms.bindGroup;
14
+ i.resetBindGroup(1), i.setBindGroup(0, o, n);
15
+ }
16
+ execute(e, t) {
17
+ const r = this._shader.gpuProgram, s = e.renderer, i = s.encoder;
18
+ if (!t.bindGroup) {
19
+ const u = t.textures;
20
+ t.bindGroup = E(
21
+ u.textures,
22
+ u.count,
23
+ s.limits.maxBatchableTextures
24
+ );
25
+ }
26
+ x.blendMode = t.blendMode;
27
+ const n = s.bindGroup.getBindGroup(
28
+ t.bindGroup,
29
+ r,
30
+ 1
31
+ ), o = s.pipeline.getPipeline(
32
+ this._geometry,
33
+ r,
34
+ x,
35
+ t.topology
36
+ );
37
+ t.bindGroup._touch(s.textureGC.count), i.setPipeline(o), i.renderPassEncoder.setBindGroup(1, n), i.renderPassEncoder.drawIndexed(t.size, 1, t.start);
38
+ }
39
+ }
40
+ O.extension = {
41
+ type: [
42
+ p.WebGPUPipesAdaptor
43
+ ],
44
+ name: "batch"
45
+ };
46
+ class I {
47
+ constructor(e) {
48
+ this._hash = /* @__PURE__ */ Object.create(null), this._renderer = e, this._renderer.renderableGC.addManagedHash(this, "_hash");
49
+ }
50
+ contextChange(e) {
51
+ this._gpu = e;
52
+ }
53
+ getBindGroup(e, t, r) {
54
+ return e._updateKey(), this._hash[e._key] || this._createBindGroup(e, t, r);
55
+ }
56
+ _createBindGroup(e, t, r) {
57
+ const s = this._gpu.device, i = t.layout[r], n = [], o = this._renderer;
58
+ for (const l in i) {
59
+ const h = e.resources[l] ?? e.resources[i[l]];
60
+ let f;
61
+ if (h._resourceType === "uniformGroup") {
62
+ const d = h;
63
+ o.ubo.updateUniformGroup(d);
64
+ const _ = d.buffer;
65
+ f = {
66
+ buffer: o.buffer.getGPUBuffer(_),
67
+ offset: 0,
68
+ size: _.descriptor.size
69
+ };
70
+ } else if (h._resourceType === "buffer") {
71
+ const d = h;
72
+ f = {
73
+ buffer: o.buffer.getGPUBuffer(d),
74
+ offset: 0,
75
+ size: d.descriptor.size
76
+ };
77
+ } else if (h._resourceType === "bufferResource") {
78
+ const d = h;
79
+ f = {
80
+ buffer: o.buffer.getGPUBuffer(d.buffer),
81
+ offset: d.offset,
82
+ size: d.size
83
+ };
84
+ } else if (h._resourceType === "textureSampler") {
85
+ const d = h;
86
+ f = o.texture.getGpuSampler(d);
87
+ } else if (h._resourceType === "textureSource") {
88
+ const d = h;
89
+ f = o.texture.getGpuSource(d).createView({});
90
+ }
91
+ n.push({
92
+ binding: i[l],
93
+ resource: f
94
+ });
95
+ }
96
+ const u = o.shader.getProgramData(t).bindGroups[r], c = s.createBindGroup({
97
+ layout: u,
98
+ entries: n
99
+ });
100
+ return this._hash[e._key] = c, c;
101
+ }
102
+ destroy() {
103
+ for (const e of Object.keys(this._hash))
104
+ this._hash[e] = null;
105
+ this._hash = null, this._renderer = null;
106
+ }
107
+ }
108
+ I.extension = {
109
+ type: [
110
+ p.WebGPUSystem
111
+ ],
112
+ name: "bindGroup"
113
+ };
114
+ class W {
115
+ constructor(e) {
116
+ this._gpuBuffers = /* @__PURE__ */ Object.create(null), this._managedBuffers = [], e.renderableGC.addManagedHash(this, "_gpuBuffers");
117
+ }
118
+ contextChange(e) {
119
+ this._gpu = e;
120
+ }
121
+ getGPUBuffer(e) {
122
+ return this._gpuBuffers[e.uid] || this.createGPUBuffer(e);
123
+ }
124
+ updateBuffer(e) {
125
+ const t = this._gpuBuffers[e.uid] || this.createGPUBuffer(e), r = e.data;
126
+ return e._updateID && r && (e._updateID = 0, this._gpu.device.queue.writeBuffer(
127
+ t,
128
+ 0,
129
+ r.buffer,
130
+ 0,
131
+ // round to the nearest 4 bytes
132
+ (e._updateSize || r.byteLength) + 3 & -4
133
+ )), t;
134
+ }
135
+ /** dispose all WebGL resources of all managed buffers */
136
+ destroyAll() {
137
+ for (const e in this._gpuBuffers)
138
+ this._gpuBuffers[e].destroy();
139
+ this._gpuBuffers = {};
140
+ }
141
+ createGPUBuffer(e) {
142
+ this._gpuBuffers[e.uid] || (e.on("update", this.updateBuffer, this), e.on("change", this.onBufferChange, this), e.on("destroy", this.onBufferDestroy, this), this._managedBuffers.push(e));
143
+ const t = this._gpu.device.createBuffer(e.descriptor);
144
+ return e._updateID = 0, e.data && (ae(e.data.buffer, t.getMappedRange()), t.unmap()), this._gpuBuffers[e.uid] = t, t;
145
+ }
146
+ onBufferChange(e) {
147
+ this._gpuBuffers[e.uid].destroy(), e._updateID = 0, this._gpuBuffers[e.uid] = this.createGPUBuffer(e);
148
+ }
149
+ /**
150
+ * Disposes buffer
151
+ * @param buffer - buffer with data
152
+ */
153
+ onBufferDestroy(e) {
154
+ this._managedBuffers.splice(this._managedBuffers.indexOf(e), 1), this._destroyBuffer(e);
155
+ }
156
+ destroy() {
157
+ this._managedBuffers.forEach((e) => this._destroyBuffer(e)), this._managedBuffers = null, this._gpuBuffers = null;
158
+ }
159
+ _destroyBuffer(e) {
160
+ this._gpuBuffers[e.uid].destroy(), e.off("update", this.updateBuffer, this), e.off("change", this.onBufferChange, this), e.off("destroy", this.onBufferDestroy, this), this._gpuBuffers[e.uid] = null;
161
+ }
162
+ }
163
+ W.extension = {
164
+ type: [
165
+ p.WebGPUSystem
166
+ ],
167
+ name: "buffer"
168
+ };
169
+ class ve {
170
+ constructor({ minUniformOffsetAlignment: e }) {
171
+ this._minUniformOffsetAlignment = 256, this.byteIndex = 0, this._minUniformOffsetAlignment = e, this.data = new Float32Array(65535);
172
+ }
173
+ clear() {
174
+ this.byteIndex = 0;
175
+ }
176
+ addEmptyGroup(e) {
177
+ if (e > this._minUniformOffsetAlignment / 4)
178
+ throw new Error(`UniformBufferBatch: array is too large: ${e * 4}`);
179
+ const t = this.byteIndex;
180
+ let r = t + e * 4;
181
+ if (r = Math.ceil(r / this._minUniformOffsetAlignment) * this._minUniformOffsetAlignment, r > this.data.length * 4)
182
+ throw new Error("UniformBufferBatch: ubo batch got too big");
183
+ return this.byteIndex = r, t;
184
+ }
185
+ addGroup(e) {
186
+ const t = this.addEmptyGroup(e.length);
187
+ for (let r = 0; r < e.length; r++)
188
+ this.data[t / 4 + r] = e[r];
189
+ return t;
190
+ }
191
+ destroy() {
192
+ this.data = null;
193
+ }
194
+ }
195
+ class V {
196
+ constructor(e) {
197
+ this._colorMaskCache = 15, this._renderer = e;
198
+ }
199
+ setMask(e) {
200
+ this._colorMaskCache !== e && (this._colorMaskCache = e, this._renderer.pipeline.setColorMask(e));
201
+ }
202
+ destroy() {
203
+ this._renderer = null, this._colorMaskCache = null;
204
+ }
205
+ }
206
+ V.extension = {
207
+ type: [
208
+ p.WebGPUSystem
209
+ ],
210
+ name: "colorMask"
211
+ };
212
+ class U {
213
+ /**
214
+ * @param {WebGPURenderer} renderer - The renderer this System works for.
215
+ */
216
+ constructor(e) {
217
+ this._renderer = e;
218
+ }
219
+ async init(e) {
220
+ return this._initPromise ? this._initPromise : (this._initPromise = (e.gpu ? Promise.resolve(e.gpu) : this._createDeviceAndAdaptor(e)).then((t) => {
221
+ this.gpu = t, this._renderer.runners.contextChange.emit(this.gpu);
222
+ }), this._initPromise);
223
+ }
224
+ /**
225
+ * Handle the context change event
226
+ * @param gpu
227
+ */
228
+ contextChange(e) {
229
+ this._renderer.gpu = e;
230
+ }
231
+ /**
232
+ * Helper class to create a WebGL Context
233
+ * @param {object} options - An options object that gets passed in to the canvas element containing the
234
+ * context attributes
235
+ * @see https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/getContext
236
+ * @returns {WebGLRenderingContext} the WebGL context
237
+ */
238
+ async _createDeviceAndAdaptor(e) {
239
+ const t = await B.get().getNavigator().gpu.requestAdapter({
240
+ powerPreference: e.powerPreference,
241
+ forceFallbackAdapter: e.forceFallbackAdapter
242
+ }), r = [
243
+ "texture-compression-bc",
244
+ "texture-compression-astc",
245
+ "texture-compression-etc2"
246
+ ].filter((i) => t.features.has(i)), s = await t.requestDevice({
247
+ requiredFeatures: r
248
+ });
249
+ return { adapter: t, device: s };
250
+ }
251
+ destroy() {
252
+ this.gpu = null, this._renderer = null;
253
+ }
254
+ }
255
+ U.extension = {
256
+ type: [
257
+ p.WebGPUSystem
258
+ ],
259
+ name: "device"
260
+ };
261
+ U.defaultOptions = {
262
+ /**
263
+ * {@link WebGPUOptions.powerPreference}
264
+ * @default default
265
+ */
266
+ powerPreference: void 0,
267
+ /**
268
+ * Force the use of the fallback adapter
269
+ * @default false
270
+ */
271
+ forceFallbackAdapter: !1
272
+ };
273
+ class N {
274
+ constructor(e) {
275
+ this._boundBindGroup = /* @__PURE__ */ Object.create(null), this._boundVertexBuffer = /* @__PURE__ */ Object.create(null), this._renderer = e;
276
+ }
277
+ renderStart() {
278
+ this.commandFinished = new Promise((e) => {
279
+ this._resolveCommandFinished = e;
280
+ }), this.commandEncoder = this._renderer.gpu.device.createCommandEncoder();
281
+ }
282
+ beginRenderPass(e) {
283
+ this.endRenderPass(), this._clearCache(), this.renderPassEncoder = this.commandEncoder.beginRenderPass(e.descriptor);
284
+ }
285
+ endRenderPass() {
286
+ this.renderPassEncoder && this.renderPassEncoder.end(), this.renderPassEncoder = null;
287
+ }
288
+ setViewport(e) {
289
+ this.renderPassEncoder.setViewport(e.x, e.y, e.width, e.height, 0, 1);
290
+ }
291
+ setPipelineFromGeometryProgramAndState(e, t, r, s) {
292
+ const i = this._renderer.pipeline.getPipeline(e, t, r, s);
293
+ this.setPipeline(i);
294
+ }
295
+ setPipeline(e) {
296
+ this._boundPipeline !== e && (this._boundPipeline = e, this.renderPassEncoder.setPipeline(e));
297
+ }
298
+ _setVertexBuffer(e, t) {
299
+ this._boundVertexBuffer[e] !== t && (this._boundVertexBuffer[e] = t, this.renderPassEncoder.setVertexBuffer(e, this._renderer.buffer.updateBuffer(t)));
300
+ }
301
+ _setIndexBuffer(e) {
302
+ if (this._boundIndexBuffer === e)
303
+ return;
304
+ this._boundIndexBuffer = e;
305
+ const t = e.data.BYTES_PER_ELEMENT === 2 ? "uint16" : "uint32";
306
+ this.renderPassEncoder.setIndexBuffer(this._renderer.buffer.updateBuffer(e), t);
307
+ }
308
+ resetBindGroup(e) {
309
+ this._boundBindGroup[e] = null;
310
+ }
311
+ setBindGroup(e, t, r) {
312
+ if (this._boundBindGroup[e] === t)
313
+ return;
314
+ this._boundBindGroup[e] = t, t._touch(this._renderer.textureGC.count);
315
+ const s = this._renderer.bindGroup.getBindGroup(t, r, e);
316
+ this.renderPassEncoder.setBindGroup(e, s);
317
+ }
318
+ setGeometry(e, t) {
319
+ const r = this._renderer.pipeline.getBufferNamesToBind(e, t);
320
+ for (const s in r)
321
+ this._setVertexBuffer(parseInt(s, 10), e.attributes[r[s]].buffer);
322
+ e.indexBuffer && this._setIndexBuffer(e.indexBuffer);
323
+ }
324
+ _setShaderBindGroups(e, t) {
325
+ for (const r in e.groups) {
326
+ const s = e.groups[r];
327
+ t || this._syncBindGroup(s), this.setBindGroup(r, s, e.gpuProgram);
328
+ }
329
+ }
330
+ _syncBindGroup(e) {
331
+ for (const t in e.resources) {
332
+ const r = e.resources[t];
333
+ r.isUniformGroup && this._renderer.ubo.updateUniformGroup(r);
334
+ }
335
+ }
336
+ draw(e) {
337
+ const { geometry: t, shader: r, state: s, topology: i, size: n, start: o, instanceCount: u, skipSync: c } = e;
338
+ this.setPipelineFromGeometryProgramAndState(t, r.gpuProgram, s, i), this.setGeometry(t, r.gpuProgram), this._setShaderBindGroups(r, c), t.indexBuffer ? this.renderPassEncoder.drawIndexed(
339
+ n || t.indexBuffer.data.length,
340
+ u ?? t.instanceCount,
341
+ o || 0
342
+ ) : this.renderPassEncoder.draw(n || t.getSize(), u ?? t.instanceCount, o || 0);
343
+ }
344
+ finishRenderPass() {
345
+ this.renderPassEncoder && (this.renderPassEncoder.end(), this.renderPassEncoder = null);
346
+ }
347
+ postrender() {
348
+ this.finishRenderPass(), this._gpu.device.queue.submit([this.commandEncoder.finish()]), this._resolveCommandFinished(), this.commandEncoder = null;
349
+ }
350
+ // restores a render pass if finishRenderPass was called
351
+ // not optimised as really used for debugging!
352
+ // used when we want to stop drawing and log a texture..
353
+ restoreRenderPass() {
354
+ const e = this._renderer.renderTarget.adaptor.getDescriptor(
355
+ this._renderer.renderTarget.renderTarget,
356
+ !1,
357
+ [0, 0, 0, 1]
358
+ );
359
+ this.renderPassEncoder = this.commandEncoder.beginRenderPass(e);
360
+ const t = this._boundPipeline, r = { ...this._boundVertexBuffer }, s = this._boundIndexBuffer, i = { ...this._boundBindGroup };
361
+ this._clearCache();
362
+ const n = this._renderer.renderTarget.viewport;
363
+ this.renderPassEncoder.setViewport(n.x, n.y, n.width, n.height, 0, 1), this.setPipeline(t);
364
+ for (const o in r)
365
+ this._setVertexBuffer(o, r[o]);
366
+ for (const o in i)
367
+ this.setBindGroup(o, i[o], null);
368
+ this._setIndexBuffer(s);
369
+ }
370
+ _clearCache() {
371
+ for (let e = 0; e < 16; e++)
372
+ this._boundBindGroup[e] = null, this._boundVertexBuffer[e] = null;
373
+ this._boundIndexBuffer = null, this._boundPipeline = null;
374
+ }
375
+ destroy() {
376
+ this._renderer = null, this._gpu = null, this._boundBindGroup = null, this._boundVertexBuffer = null, this._boundIndexBuffer = null, this._boundPipeline = null;
377
+ }
378
+ contextChange(e) {
379
+ this._gpu = e;
380
+ }
381
+ }
382
+ N.extension = {
383
+ type: [p.WebGPUSystem],
384
+ name: "encoder",
385
+ priority: 1
386
+ };
387
+ class j {
388
+ constructor(e) {
389
+ this._renderer = e;
390
+ }
391
+ contextChange() {
392
+ this.maxTextures = this._renderer.device.gpu.device.limits.maxSampledTexturesPerShaderStage, this.maxBatchableTextures = this.maxTextures;
393
+ }
394
+ destroy() {
395
+ }
396
+ }
397
+ j.extension = {
398
+ type: [
399
+ p.WebGPUSystem
400
+ ],
401
+ name: "limits"
402
+ };
403
+ class K {
404
+ constructor(e) {
405
+ this._renderTargetStencilState = /* @__PURE__ */ Object.create(null), this._renderer = e, e.renderTarget.onRenderTargetChange.add(this);
406
+ }
407
+ onRenderTargetChange(e) {
408
+ let t = this._renderTargetStencilState[e.uid];
409
+ t || (t = this._renderTargetStencilState[e.uid] = {
410
+ stencilMode: C.DISABLED,
411
+ stencilReference: 0
412
+ }), this._activeRenderTarget = e, this.setStencilMode(t.stencilMode, t.stencilReference);
413
+ }
414
+ setStencilMode(e, t) {
415
+ const r = this._renderTargetStencilState[this._activeRenderTarget.uid];
416
+ r.stencilMode = e, r.stencilReference = t;
417
+ const s = this._renderer;
418
+ s.pipeline.setStencilMode(e), s.encoder.renderPassEncoder.setStencilReference(t);
419
+ }
420
+ destroy() {
421
+ this._renderer.renderTarget.onRenderTargetChange.remove(this), this._renderer = null, this._activeRenderTarget = null, this._renderTargetStencilState = null;
422
+ }
423
+ }
424
+ K.extension = {
425
+ type: [
426
+ p.WebGPUSystem
427
+ ],
428
+ name: "stencil"
429
+ };
430
+ const G = {
431
+ i32: { align: 4, size: 4 },
432
+ u32: { align: 4, size: 4 },
433
+ f32: { align: 4, size: 4 },
434
+ f16: { align: 2, size: 2 },
435
+ "vec2<i32>": { align: 8, size: 8 },
436
+ "vec2<u32>": { align: 8, size: 8 },
437
+ "vec2<f32>": { align: 8, size: 8 },
438
+ "vec2<f16>": { align: 4, size: 4 },
439
+ "vec3<i32>": { align: 16, size: 12 },
440
+ "vec3<u32>": { align: 16, size: 12 },
441
+ "vec3<f32>": { align: 16, size: 12 },
442
+ "vec3<f16>": { align: 8, size: 6 },
443
+ "vec4<i32>": { align: 16, size: 16 },
444
+ "vec4<u32>": { align: 16, size: 16 },
445
+ "vec4<f32>": { align: 16, size: 16 },
446
+ "vec4<f16>": { align: 8, size: 8 },
447
+ "mat2x2<f32>": { align: 8, size: 16 },
448
+ "mat2x2<f16>": { align: 4, size: 8 },
449
+ "mat3x2<f32>": { align: 8, size: 24 },
450
+ "mat3x2<f16>": { align: 4, size: 12 },
451
+ "mat4x2<f32>": { align: 8, size: 32 },
452
+ "mat4x2<f16>": { align: 4, size: 16 },
453
+ "mat2x3<f32>": { align: 16, size: 32 },
454
+ "mat2x3<f16>": { align: 8, size: 16 },
455
+ "mat3x3<f32>": { align: 16, size: 48 },
456
+ "mat3x3<f16>": { align: 8, size: 24 },
457
+ "mat4x3<f32>": { align: 16, size: 64 },
458
+ "mat4x3<f16>": { align: 8, size: 32 },
459
+ "mat2x4<f32>": { align: 16, size: 32 },
460
+ "mat2x4<f16>": { align: 8, size: 16 },
461
+ "mat3x4<f32>": { align: 16, size: 48 },
462
+ "mat3x4<f16>": { align: 8, size: 24 },
463
+ "mat4x4<f32>": { align: 16, size: 64 },
464
+ "mat4x4<f16>": { align: 8, size: 32 }
465
+ };
466
+ function Ue(a) {
467
+ const e = a.map((r) => ({
468
+ data: r,
469
+ offset: 0,
470
+ size: 0
471
+ }));
472
+ let t = 0;
473
+ for (let r = 0; r < e.length; r++) {
474
+ const s = e[r];
475
+ let i = G[s.data.type].size;
476
+ const n = G[s.data.type].align;
477
+ if (!G[s.data.type])
478
+ throw new Error(`[Pixi.js] WebGPU UniformBuffer: Unknown type ${s.data.type}`);
479
+ s.data.size > 1 && (i = Math.max(i, n) * s.data.size), t = Math.ceil(t / n) * n, s.size = i, s.offset = t, t += i;
480
+ }
481
+ return t = Math.ceil(t / 16) * 16, { uboElements: e, size: t };
482
+ }
483
+ function Me(a, e) {
484
+ const { size: t, align: r } = G[a.data.type], s = (r - t) / 4, i = a.data.type.indexOf("i32") >= 0 ? "dataInt32" : "data";
485
+ return `
486
+ v = uv.${a.data.name};
487
+ ${e !== 0 ? `offset += ${e};` : ""}
488
+
489
+ arrayOffset = offset;
490
+
491
+ t = 0;
492
+
493
+ for(var i=0; i < ${a.data.size * (t / 4)}; i++)
494
+ {
495
+ for(var j = 0; j < ${t / 4}; j++)
496
+ {
497
+ ${i}[arrayOffset++] = v[t++];
498
+ }
499
+ ${s !== 0 ? `arrayOffset += ${s};` : ""}
500
+ }
501
+ `;
502
+ }
503
+ function we(a) {
504
+ return _e(
505
+ a,
506
+ "uboWgsl",
507
+ Me,
508
+ be
509
+ );
510
+ }
511
+ class q extends ye {
512
+ constructor() {
513
+ super({
514
+ createUboElements: Ue,
515
+ generateUboSync: we
516
+ });
517
+ }
518
+ }
519
+ q.extension = {
520
+ type: [p.WebGPUSystem],
521
+ name: "ubo"
522
+ };
523
+ const b = 128;
524
+ class Y {
525
+ constructor(e) {
526
+ this._bindGroupHash = /* @__PURE__ */ Object.create(null), this._buffers = [], this._bindGroups = [], this._bufferResources = [], this._renderer = e, this._renderer.renderableGC.addManagedHash(this, "_bindGroupHash"), this._batchBuffer = new ve({ minUniformOffsetAlignment: b });
527
+ const t = 256 / b;
528
+ for (let r = 0; r < t; r++) {
529
+ let s = T.UNIFORM | T.COPY_DST;
530
+ r === 0 && (s |= T.COPY_SRC), this._buffers.push(new ue({
531
+ data: this._batchBuffer.data,
532
+ usage: s
533
+ }));
534
+ }
535
+ }
536
+ renderEnd() {
537
+ this._uploadBindGroups(), this._resetBindGroups();
538
+ }
539
+ _resetBindGroups() {
540
+ for (const e in this._bindGroupHash)
541
+ this._bindGroupHash[e] = null;
542
+ this._batchBuffer.clear();
543
+ }
544
+ // just works for single bind groups for now
545
+ getUniformBindGroup(e, t) {
546
+ if (!t && this._bindGroupHash[e.uid])
547
+ return this._bindGroupHash[e.uid];
548
+ this._renderer.ubo.ensureUniformGroup(e);
549
+ const r = e.buffer.data, s = this._batchBuffer.addEmptyGroup(r.length);
550
+ return this._renderer.ubo.syncUniformGroup(e, this._batchBuffer.data, s / 4), this._bindGroupHash[e.uid] = this._getBindGroup(s / b), this._bindGroupHash[e.uid];
551
+ }
552
+ getUboResource(e) {
553
+ this._renderer.ubo.updateUniformGroup(e);
554
+ const t = e.buffer.data, r = this._batchBuffer.addGroup(t);
555
+ return this._getBufferResource(r / b);
556
+ }
557
+ getArrayBindGroup(e) {
558
+ const t = this._batchBuffer.addGroup(e);
559
+ return this._getBindGroup(t / b);
560
+ }
561
+ getArrayBufferResource(e) {
562
+ const r = this._batchBuffer.addGroup(e) / b;
563
+ return this._getBufferResource(r);
564
+ }
565
+ _getBufferResource(e) {
566
+ if (!this._bufferResources[e]) {
567
+ const t = this._buffers[e % 2];
568
+ this._bufferResources[e] = new xe({
569
+ buffer: t,
570
+ offset: (e / 2 | 0) * 256,
571
+ size: b
572
+ });
573
+ }
574
+ return this._bufferResources[e];
575
+ }
576
+ _getBindGroup(e) {
577
+ if (!this._bindGroups[e]) {
578
+ const t = new L({
579
+ 0: this._getBufferResource(e)
580
+ });
581
+ this._bindGroups[e] = t;
582
+ }
583
+ return this._bindGroups[e];
584
+ }
585
+ _uploadBindGroups() {
586
+ const e = this._renderer.buffer, t = this._buffers[0];
587
+ t.update(this._batchBuffer.byteIndex), e.updateBuffer(t);
588
+ const r = this._renderer.gpu.device.createCommandEncoder();
589
+ for (let s = 1; s < this._buffers.length; s++) {
590
+ const i = this._buffers[s];
591
+ r.copyBufferToBuffer(
592
+ e.getGPUBuffer(t),
593
+ b,
594
+ e.getGPUBuffer(i),
595
+ 0,
596
+ this._batchBuffer.byteIndex
597
+ );
598
+ }
599
+ this._renderer.gpu.device.queue.submit([r.finish()]);
600
+ }
601
+ destroy() {
602
+ var e;
603
+ for (let t = 0; t < this._bindGroups.length; t++)
604
+ (e = this._bindGroups[t]) == null || e.destroy();
605
+ this._bindGroups = null, this._bindGroupHash = null;
606
+ for (let t = 0; t < this._buffers.length; t++)
607
+ this._buffers[t].destroy();
608
+ this._buffers = null;
609
+ for (let t = 0; t < this._bufferResources.length; t++)
610
+ this._bufferResources[t].destroy();
611
+ this._bufferResources = null, this._batchBuffer.destroy(), this._bindGroupHash = null, this._renderer = null;
612
+ }
613
+ }
614
+ Y.extension = {
615
+ type: [
616
+ p.WebGPUPipes
617
+ ],
618
+ name: "uniformBatch"
619
+ };
620
+ const Re = {
621
+ "point-list": 0,
622
+ "line-list": 1,
623
+ "line-strip": 2,
624
+ "triangle-list": 3,
625
+ "triangle-strip": 4
626
+ };
627
+ function Ee(a, e, t, r, s) {
628
+ return a << 24 | e << 16 | t << 10 | r << 5 | s;
629
+ }
630
+ function Le(a, e, t, r) {
631
+ return t << 6 | a << 3 | r << 1 | e;
632
+ }
633
+ class $ {
634
+ constructor(e) {
635
+ this._moduleCache = /* @__PURE__ */ Object.create(null), this._bufferLayoutsCache = /* @__PURE__ */ Object.create(null), this._bindingNamesCache = /* @__PURE__ */ Object.create(null), this._pipeCache = /* @__PURE__ */ Object.create(null), this._pipeStateCaches = /* @__PURE__ */ Object.create(null), this._colorMask = 15, this._multisampleCount = 1, this._renderer = e;
636
+ }
637
+ contextChange(e) {
638
+ this._gpu = e, this.setStencilMode(C.DISABLED), this._updatePipeHash();
639
+ }
640
+ setMultisampleCount(e) {
641
+ this._multisampleCount !== e && (this._multisampleCount = e, this._updatePipeHash());
642
+ }
643
+ setRenderTarget(e) {
644
+ this._multisampleCount = e.msaaSamples, this._depthStencilAttachment = e.descriptor.depthStencilAttachment ? 1 : 0, this._updatePipeHash();
645
+ }
646
+ setColorMask(e) {
647
+ this._colorMask !== e && (this._colorMask = e, this._updatePipeHash());
648
+ }
649
+ setStencilMode(e) {
650
+ this._stencilMode !== e && (this._stencilMode = e, this._stencilState = Ge[e], this._updatePipeHash());
651
+ }
652
+ setPipeline(e, t, r, s) {
653
+ const i = this.getPipeline(e, t, r);
654
+ s.setPipeline(i);
655
+ }
656
+ getPipeline(e, t, r, s) {
657
+ e._layoutKey || (Be(e, t.attributeData), this._generateBufferKey(e)), s || (s = e.topology);
658
+ const i = Ee(
659
+ e._layoutKey,
660
+ t._layoutKey,
661
+ r.data,
662
+ r._blendModeId,
663
+ Re[s]
664
+ );
665
+ return this._pipeCache[i] ? this._pipeCache[i] : (this._pipeCache[i] = this._createPipeline(e, t, r, s), this._pipeCache[i]);
666
+ }
667
+ _createPipeline(e, t, r, s) {
668
+ const i = this._gpu.device, n = this._createVertexBufferLayouts(e, t), o = this._renderer.state.getColorTargets(r);
669
+ o[0].writeMask = this._stencilMode === C.RENDERING_MASK_ADD ? 0 : this._colorMask;
670
+ const u = this._renderer.shader.getProgramData(t).pipeline, c = {
671
+ // TODO later check if its helpful to create..
672
+ // layout,
673
+ vertex: {
674
+ module: this._getModule(t.vertex.source),
675
+ entryPoint: t.vertex.entryPoint,
676
+ // geometry..
677
+ buffers: n
678
+ },
679
+ fragment: {
680
+ module: this._getModule(t.fragment.source),
681
+ entryPoint: t.fragment.entryPoint,
682
+ targets: o
683
+ },
684
+ primitive: {
685
+ topology: s,
686
+ cullMode: r.cullMode
687
+ },
688
+ layout: u,
689
+ multisample: {
690
+ count: this._multisampleCount
691
+ },
692
+ // depthStencil,
693
+ label: "PIXI Pipeline"
694
+ };
695
+ return this._depthStencilAttachment && (c.depthStencil = {
696
+ ...this._stencilState,
697
+ format: "depth24plus-stencil8",
698
+ depthWriteEnabled: r.depthTest,
699
+ depthCompare: r.depthTest ? "less" : "always"
700
+ }), i.createRenderPipeline(c);
701
+ }
702
+ _getModule(e) {
703
+ return this._moduleCache[e] || this._createModule(e);
704
+ }
705
+ _createModule(e) {
706
+ const t = this._gpu.device;
707
+ return this._moduleCache[e] = t.createShaderModule({
708
+ code: e
709
+ }), this._moduleCache[e];
710
+ }
711
+ _generateBufferKey(e) {
712
+ const t = [];
713
+ let r = 0;
714
+ const s = Object.keys(e.attributes).sort();
715
+ for (let n = 0; n < s.length; n++) {
716
+ const o = e.attributes[s[n]];
717
+ t[r++] = o.offset, t[r++] = o.format, t[r++] = o.stride, t[r++] = o.instance;
718
+ }
719
+ const i = t.join("|");
720
+ return e._layoutKey = M(i, "geometry"), e._layoutKey;
721
+ }
722
+ _generateAttributeLocationsKey(e) {
723
+ const t = [];
724
+ let r = 0;
725
+ const s = Object.keys(e.attributeData).sort();
726
+ for (let n = 0; n < s.length; n++) {
727
+ const o = e.attributeData[s[n]];
728
+ t[r++] = o.location;
729
+ }
730
+ const i = t.join("|");
731
+ return e._attributeLocationsKey = M(i, "programAttributes"), e._attributeLocationsKey;
732
+ }
733
+ /**
734
+ * Returns a hash of buffer names mapped to bind locations.
735
+ * This is used to bind the correct buffer to the correct location in the shader.
736
+ * @param geometry - The geometry where to get the buffer names
737
+ * @param program - The program where to get the buffer names
738
+ * @returns An object of buffer names mapped to the bind location.
739
+ */
740
+ getBufferNamesToBind(e, t) {
741
+ const r = e._layoutKey << 16 | t._attributeLocationsKey;
742
+ if (this._bindingNamesCache[r])
743
+ return this._bindingNamesCache[r];
744
+ const s = this._createVertexBufferLayouts(e, t), i = /* @__PURE__ */ Object.create(null), n = t.attributeData;
745
+ for (let o = 0; o < s.length; o++) {
746
+ const c = Object.values(s[o].attributes)[0].shaderLocation;
747
+ for (const l in n)
748
+ if (n[l].location === c) {
749
+ i[o] = l;
750
+ break;
751
+ }
752
+ }
753
+ return this._bindingNamesCache[r] = i, i;
754
+ }
755
+ _createVertexBufferLayouts(e, t) {
756
+ t._attributeLocationsKey || this._generateAttributeLocationsKey(t);
757
+ const r = e._layoutKey << 16 | t._attributeLocationsKey;
758
+ if (this._bufferLayoutsCache[r])
759
+ return this._bufferLayoutsCache[r];
760
+ const s = [];
761
+ return e.buffers.forEach((i) => {
762
+ const n = {
763
+ arrayStride: 0,
764
+ stepMode: "vertex",
765
+ attributes: []
766
+ }, o = n.attributes;
767
+ for (const u in t.attributeData) {
768
+ const c = e.attributes[u];
769
+ (c.divisor ?? 1) !== 1 && v(`Attribute ${u} has an invalid divisor value of '${c.divisor}'. WebGPU only supports a divisor value of 1`), c.buffer === i && (n.arrayStride = c.stride, n.stepMode = c.instance ? "instance" : "vertex", o.push({
770
+ shaderLocation: t.attributeData[u].location,
771
+ offset: c.offset,
772
+ format: c.format
773
+ }));
774
+ }
775
+ o.length && s.push(n);
776
+ }), this._bufferLayoutsCache[r] = s, s;
777
+ }
778
+ _updatePipeHash() {
779
+ const e = Le(
780
+ this._stencilMode,
781
+ this._multisampleCount,
782
+ this._colorMask,
783
+ this._depthStencilAttachment
784
+ );
785
+ this._pipeStateCaches[e] || (this._pipeStateCaches[e] = /* @__PURE__ */ Object.create(null)), this._pipeCache = this._pipeStateCaches[e];
786
+ }
787
+ destroy() {
788
+ this._renderer = null, this._bufferLayoutsCache = null;
789
+ }
790
+ }
791
+ $.extension = {
792
+ type: [p.WebGPUSystem],
793
+ name: "pipeline"
794
+ };
795
+ class Ae {
796
+ constructor() {
797
+ this.contexts = [], this.msaaTextures = [], this.msaaSamples = 1;
798
+ }
799
+ }
800
+ class ke {
801
+ init(e, t) {
802
+ this._renderer = e, this._renderTargetSystem = t;
803
+ }
804
+ copyToTexture(e, t, r, s, i) {
805
+ const n = this._renderer, o = this._getGpuColorTexture(
806
+ e
807
+ ), u = n.texture.getGpuSource(
808
+ t.source
809
+ );
810
+ return n.encoder.commandEncoder.copyTextureToTexture(
811
+ {
812
+ texture: o,
813
+ origin: r
814
+ },
815
+ {
816
+ texture: u,
817
+ origin: i
818
+ },
819
+ s
820
+ ), t;
821
+ }
822
+ startRenderPass(e, t = !0, r, s) {
823
+ const n = this._renderTargetSystem.getGpuRenderTarget(e), o = this.getDescriptor(e, t, r);
824
+ n.descriptor = o, this._renderer.pipeline.setRenderTarget(n), this._renderer.encoder.beginRenderPass(n), this._renderer.encoder.setViewport(s);
825
+ }
826
+ finishRenderPass() {
827
+ this._renderer.encoder.endRenderPass();
828
+ }
829
+ /**
830
+ * returns the gpu texture for the first color texture in the render target
831
+ * mainly used by the filter manager to get copy the texture for blending
832
+ * @param renderTarget
833
+ * @returns a gpu texture
834
+ */
835
+ _getGpuColorTexture(e) {
836
+ const t = this._renderTargetSystem.getGpuRenderTarget(e);
837
+ return t.contexts[0] ? t.contexts[0].getCurrentTexture() : this._renderer.texture.getGpuSource(
838
+ e.colorTextures[0].source
839
+ );
840
+ }
841
+ getDescriptor(e, t, r) {
842
+ typeof t == "boolean" && (t = t ? y.ALL : y.NONE);
843
+ const s = this._renderTargetSystem, i = s.getGpuRenderTarget(e), n = e.colorTextures.map(
844
+ (c, l) => {
845
+ const h = i.contexts[l];
846
+ let f, d;
847
+ h ? f = h.getCurrentTexture().createView() : f = this._renderer.texture.getGpuSource(c).createView({
848
+ mipLevelCount: 1
849
+ }), i.msaaTextures[l] && (d = f, f = this._renderer.texture.getTextureView(
850
+ i.msaaTextures[l]
851
+ ));
852
+ const _ = t & y.COLOR ? "clear" : "load";
853
+ return r ?? (r = s.defaultClearColor), {
854
+ view: f,
855
+ resolveTarget: d,
856
+ clearValue: r,
857
+ storeOp: "store",
858
+ loadOp: _
859
+ };
860
+ }
861
+ );
862
+ let o;
863
+ if ((e.stencil || e.depth) && !e.depthStencilTexture && (e.ensureDepthStencilTexture(), e.depthStencilTexture.source.sampleCount = i.msaa ? 4 : 1), e.depthStencilTexture) {
864
+ const c = t & y.STENCIL ? "clear" : "load", l = t & y.DEPTH ? "clear" : "load";
865
+ o = {
866
+ view: this._renderer.texture.getGpuSource(e.depthStencilTexture.source).createView(),
867
+ stencilStoreOp: "store",
868
+ stencilLoadOp: c,
869
+ depthClearValue: 1,
870
+ depthLoadOp: l,
871
+ depthStoreOp: "store"
872
+ };
873
+ }
874
+ return {
875
+ colorAttachments: n,
876
+ depthStencilAttachment: o
877
+ };
878
+ }
879
+ clear(e, t = !0, r, s) {
880
+ if (!t)
881
+ return;
882
+ const { gpu: i, encoder: n } = this._renderer, o = i.device;
883
+ if (n.commandEncoder === null) {
884
+ const c = o.createCommandEncoder(), l = this.getDescriptor(e, t, r), h = c.beginRenderPass(l);
885
+ h.setViewport(s.x, s.y, s.width, s.height, 0, 1), h.end();
886
+ const f = c.finish();
887
+ o.queue.submit([f]);
888
+ } else
889
+ this.startRenderPass(e, t, r, s);
890
+ }
891
+ initGpuRenderTarget(e) {
892
+ e.isRoot = !0;
893
+ const t = new Ae();
894
+ return e.colorTextures.forEach((r, s) => {
895
+ if (r instanceof ce) {
896
+ const i = r.resource.getContext(
897
+ "webgpu"
898
+ ), n = r.transparent ? "premultiplied" : "opaque";
899
+ try {
900
+ i.configure({
901
+ device: this._renderer.gpu.device,
902
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
903
+ format: "bgra8unorm",
904
+ alphaMode: n
905
+ });
906
+ } catch (o) {
907
+ console.error(o);
908
+ }
909
+ t.contexts[s] = i;
910
+ }
911
+ if (t.msaa = r.source.antialias, r.source.antialias) {
912
+ const i = new de({
913
+ width: 0,
914
+ height: 0,
915
+ sampleCount: 4
916
+ });
917
+ t.msaaTextures[s] = i;
918
+ }
919
+ }), t.msaa && (t.msaaSamples = 4, e.depthStencilTexture && (e.depthStencilTexture.source.sampleCount = 4)), t;
920
+ }
921
+ destroyGpuRenderTarget(e) {
922
+ e.contexts.forEach((t) => {
923
+ t.unconfigure();
924
+ }), e.msaaTextures.forEach((t) => {
925
+ t.destroy();
926
+ }), e.msaaTextures.length = 0, e.contexts.length = 0;
927
+ }
928
+ ensureDepthStencilTexture(e) {
929
+ const t = this._renderTargetSystem.getGpuRenderTarget(e);
930
+ e.depthStencilTexture && t.msaa && (e.depthStencilTexture.source.sampleCount = 4);
931
+ }
932
+ resizeGpuRenderTarget(e) {
933
+ const t = this._renderTargetSystem.getGpuRenderTarget(e);
934
+ t.width = e.width, t.height = e.height, t.msaa && e.colorTextures.forEach((r, s) => {
935
+ const i = t.msaaTextures[s];
936
+ i == null || i.resize(
937
+ r.source.width,
938
+ r.source.height,
939
+ r.source._resolution
940
+ );
941
+ });
942
+ }
943
+ }
944
+ class X extends Se {
945
+ constructor(e) {
946
+ super(e), this.adaptor = new ke(), this.adaptor.init(e, this);
947
+ }
948
+ }
949
+ X.extension = {
950
+ type: [p.WebGPUSystem],
951
+ name: "renderTarget"
952
+ };
953
+ class Z {
954
+ constructor() {
955
+ this._gpuProgramData = /* @__PURE__ */ Object.create(null);
956
+ }
957
+ contextChange(e) {
958
+ this._gpu = e;
959
+ }
960
+ getProgramData(e) {
961
+ return this._gpuProgramData[e._layoutKey] || this._createGPUProgramData(e);
962
+ }
963
+ _createGPUProgramData(e) {
964
+ const t = this._gpu.device, r = e.gpuLayout.map((i) => t.createBindGroupLayout({ entries: i })), s = { bindGroupLayouts: r };
965
+ return this._gpuProgramData[e._layoutKey] = {
966
+ bindGroups: r,
967
+ pipeline: t.createPipelineLayout(s)
968
+ }, this._gpuProgramData[e._layoutKey];
969
+ }
970
+ destroy() {
971
+ this._gpu = null, this._gpuProgramData = null;
972
+ }
973
+ }
974
+ Z.extension = {
975
+ type: [
976
+ p.WebGPUSystem
977
+ ],
978
+ name: "shader"
979
+ };
980
+ const g = {};
981
+ g.normal = {
982
+ alpha: {
983
+ srcFactor: "one",
984
+ dstFactor: "one-minus-src-alpha",
985
+ operation: "add"
986
+ },
987
+ color: {
988
+ srcFactor: "one",
989
+ dstFactor: "one-minus-src-alpha",
990
+ operation: "add"
991
+ }
992
+ };
993
+ g.add = {
994
+ alpha: {
995
+ srcFactor: "src-alpha",
996
+ dstFactor: "one-minus-src-alpha",
997
+ operation: "add"
998
+ },
999
+ color: {
1000
+ srcFactor: "one",
1001
+ dstFactor: "one",
1002
+ operation: "add"
1003
+ }
1004
+ };
1005
+ g.multiply = {
1006
+ alpha: {
1007
+ srcFactor: "one",
1008
+ dstFactor: "one-minus-src-alpha",
1009
+ operation: "add"
1010
+ },
1011
+ color: {
1012
+ srcFactor: "dst",
1013
+ dstFactor: "one-minus-src-alpha",
1014
+ operation: "add"
1015
+ }
1016
+ };
1017
+ g.screen = {
1018
+ alpha: {
1019
+ srcFactor: "one",
1020
+ dstFactor: "one-minus-src-alpha",
1021
+ operation: "add"
1022
+ },
1023
+ color: {
1024
+ srcFactor: "one",
1025
+ dstFactor: "one-minus-src",
1026
+ operation: "add"
1027
+ }
1028
+ };
1029
+ g.overlay = {
1030
+ alpha: {
1031
+ srcFactor: "one",
1032
+ dstFactor: "one-minus-src-alpha",
1033
+ operation: "add"
1034
+ },
1035
+ color: {
1036
+ srcFactor: "one",
1037
+ dstFactor: "one-minus-src",
1038
+ operation: "add"
1039
+ }
1040
+ };
1041
+ g.none = {
1042
+ alpha: {
1043
+ srcFactor: "one",
1044
+ dstFactor: "one-minus-src-alpha",
1045
+ operation: "add"
1046
+ },
1047
+ color: {
1048
+ srcFactor: "zero",
1049
+ dstFactor: "zero",
1050
+ operation: "add"
1051
+ }
1052
+ };
1053
+ g["normal-npm"] = {
1054
+ alpha: {
1055
+ srcFactor: "one",
1056
+ dstFactor: "one-minus-src-alpha",
1057
+ operation: "add"
1058
+ },
1059
+ color: {
1060
+ srcFactor: "src-alpha",
1061
+ dstFactor: "one-minus-src-alpha",
1062
+ operation: "add"
1063
+ }
1064
+ };
1065
+ g["add-npm"] = {
1066
+ alpha: {
1067
+ srcFactor: "one",
1068
+ dstFactor: "one",
1069
+ operation: "add"
1070
+ },
1071
+ color: {
1072
+ srcFactor: "src-alpha",
1073
+ dstFactor: "one",
1074
+ operation: "add"
1075
+ }
1076
+ };
1077
+ g["screen-npm"] = {
1078
+ alpha: {
1079
+ srcFactor: "one",
1080
+ dstFactor: "one-minus-src-alpha",
1081
+ operation: "add"
1082
+ },
1083
+ color: {
1084
+ srcFactor: "src-alpha",
1085
+ dstFactor: "one-minus-src",
1086
+ operation: "add"
1087
+ }
1088
+ };
1089
+ g.erase = {
1090
+ alpha: {
1091
+ srcFactor: "zero",
1092
+ dstFactor: "one-minus-src-alpha",
1093
+ operation: "add"
1094
+ },
1095
+ color: {
1096
+ srcFactor: "zero",
1097
+ dstFactor: "one-minus-src",
1098
+ operation: "add"
1099
+ }
1100
+ };
1101
+ g.min = {
1102
+ alpha: {
1103
+ srcFactor: "one",
1104
+ dstFactor: "one",
1105
+ operation: "min"
1106
+ },
1107
+ color: {
1108
+ srcFactor: "one",
1109
+ dstFactor: "one",
1110
+ operation: "min"
1111
+ }
1112
+ };
1113
+ g.max = {
1114
+ alpha: {
1115
+ srcFactor: "one",
1116
+ dstFactor: "one",
1117
+ operation: "max"
1118
+ },
1119
+ color: {
1120
+ srcFactor: "one",
1121
+ dstFactor: "one",
1122
+ operation: "max"
1123
+ }
1124
+ };
1125
+ class J {
1126
+ constructor() {
1127
+ this.defaultState = new F(), this.defaultState.blend = !0;
1128
+ }
1129
+ contextChange(e) {
1130
+ this.gpu = e;
1131
+ }
1132
+ /**
1133
+ * Gets the blend mode data for the current state
1134
+ * @param state - The state to get the blend mode from
1135
+ */
1136
+ getColorTargets(e) {
1137
+ return [
1138
+ {
1139
+ format: "bgra8unorm",
1140
+ writeMask: 0,
1141
+ blend: g[e.blendMode] || g.normal
1142
+ }
1143
+ ];
1144
+ }
1145
+ destroy() {
1146
+ this.gpu = null;
1147
+ }
1148
+ }
1149
+ J.extension = {
1150
+ type: [
1151
+ p.WebGPUSystem
1152
+ ],
1153
+ name: "state"
1154
+ };
1155
+ const De = {
1156
+ type: "image",
1157
+ upload(a, e, t) {
1158
+ const r = a.resource, s = (a.pixelWidth | 0) * (a.pixelHeight | 0), i = r.byteLength / s;
1159
+ t.device.queue.writeTexture(
1160
+ { texture: e },
1161
+ r,
1162
+ {
1163
+ offset: 0,
1164
+ rowsPerImage: a.pixelHeight,
1165
+ bytesPerRow: a.pixelHeight * i
1166
+ },
1167
+ {
1168
+ width: a.pixelWidth,
1169
+ height: a.pixelHeight,
1170
+ depthOrArrayLayers: 1
1171
+ }
1172
+ );
1173
+ }
1174
+ }, Q = {
1175
+ "bc1-rgba-unorm": { blockBytes: 8, blockWidth: 4, blockHeight: 4 },
1176
+ "bc2-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 },
1177
+ "bc3-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 },
1178
+ "bc7-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 },
1179
+ "etc1-rgb-unorm": { blockBytes: 8, blockWidth: 4, blockHeight: 4 },
1180
+ "etc2-rgba8unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 },
1181
+ "astc-4x4-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 }
1182
+ }, He = { blockBytes: 4, blockWidth: 1, blockHeight: 1 }, ze = {
1183
+ type: "compressed",
1184
+ upload(a, e, t) {
1185
+ let r = a.pixelWidth, s = a.pixelHeight;
1186
+ const i = Q[a.format] || He;
1187
+ for (let n = 0; n < a.resource.length; n++) {
1188
+ const o = a.resource[n], u = Math.ceil(r / i.blockWidth) * i.blockBytes;
1189
+ t.device.queue.writeTexture(
1190
+ {
1191
+ texture: e,
1192
+ mipLevel: n
1193
+ },
1194
+ o,
1195
+ {
1196
+ offset: 0,
1197
+ bytesPerRow: u
1198
+ },
1199
+ {
1200
+ width: Math.ceil(r / i.blockWidth) * i.blockWidth,
1201
+ height: Math.ceil(s / i.blockHeight) * i.blockHeight,
1202
+ depthOrArrayLayers: 1
1203
+ }
1204
+ ), r = Math.max(r >> 1, 1), s = Math.max(s >> 1, 1);
1205
+ }
1206
+ }
1207
+ }, ee = {
1208
+ type: "image",
1209
+ upload(a, e, t) {
1210
+ const r = a.resource;
1211
+ if (!r)
1212
+ return;
1213
+ if (globalThis.HTMLImageElement && r instanceof HTMLImageElement) {
1214
+ const o = B.get().createCanvas(r.width, r.height);
1215
+ o.getContext("2d").drawImage(r, 0, 0, r.width, r.height), a.resource = o, v("ImageSource: Image element passed, converting to canvas and replacing resource.");
1216
+ }
1217
+ const s = Math.min(e.width, a.resourceWidth || a.pixelWidth), i = Math.min(e.height, a.resourceHeight || a.pixelHeight), n = a.alphaMode === "premultiply-alpha-on-upload";
1218
+ t.device.queue.copyExternalImageToTexture(
1219
+ { source: r },
1220
+ { texture: e, premultipliedAlpha: n },
1221
+ {
1222
+ width: s,
1223
+ height: i
1224
+ }
1225
+ );
1226
+ }
1227
+ }, Fe = {
1228
+ type: "video",
1229
+ upload(a, e, t) {
1230
+ ee.upload(a, e, t);
1231
+ }
1232
+ };
1233
+ class Oe {
1234
+ constructor(e) {
1235
+ this.device = e, this.sampler = e.createSampler({ minFilter: "linear" }), this.pipelines = {};
1236
+ }
1237
+ _getMipmapPipeline(e) {
1238
+ let t = this.pipelines[e];
1239
+ return t || (this.mipmapShaderModule || (this.mipmapShaderModule = this.device.createShaderModule({
1240
+ code: (
1241
+ /* wgsl */
1242
+ `
1243
+ var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
1244
+ vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));
1245
+
1246
+ struct VertexOutput {
1247
+ @builtin(position) position : vec4<f32>,
1248
+ @location(0) texCoord : vec2<f32>,
1249
+ };
1250
+
1251
+ @vertex
1252
+ fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
1253
+ var output : VertexOutput;
1254
+ output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
1255
+ output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
1256
+ return output;
1257
+ }
1258
+
1259
+ @group(0) @binding(0) var imgSampler : sampler;
1260
+ @group(0) @binding(1) var img : texture_2d<f32>;
1261
+
1262
+ @fragment
1263
+ fn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {
1264
+ return textureSample(img, imgSampler, texCoord);
1265
+ }
1266
+ `
1267
+ )
1268
+ })), t = this.device.createRenderPipeline({
1269
+ layout: "auto",
1270
+ vertex: {
1271
+ module: this.mipmapShaderModule,
1272
+ entryPoint: "vertexMain"
1273
+ },
1274
+ fragment: {
1275
+ module: this.mipmapShaderModule,
1276
+ entryPoint: "fragmentMain",
1277
+ targets: [{ format: e }]
1278
+ }
1279
+ }), this.pipelines[e] = t), t;
1280
+ }
1281
+ /**
1282
+ * Generates mipmaps for the given GPUTexture from the data in level 0.
1283
+ * @param {module:External.GPUTexture} texture - Texture to generate mipmaps for.
1284
+ * @returns {module:External.GPUTexture} - The originally passed texture
1285
+ */
1286
+ generateMipmap(e) {
1287
+ const t = this._getMipmapPipeline(e.format);
1288
+ if (e.dimension === "3d" || e.dimension === "1d")
1289
+ throw new Error("Generating mipmaps for non-2d textures is currently unsupported!");
1290
+ let r = e;
1291
+ const s = e.depthOrArrayLayers || 1, i = e.usage & GPUTextureUsage.RENDER_ATTACHMENT;
1292
+ if (!i) {
1293
+ const u = {
1294
+ size: {
1295
+ width: Math.ceil(e.width / 2),
1296
+ height: Math.ceil(e.height / 2),
1297
+ depthOrArrayLayers: s
1298
+ },
1299
+ format: e.format,
1300
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
1301
+ mipLevelCount: e.mipLevelCount - 1
1302
+ };
1303
+ r = this.device.createTexture(u);
1304
+ }
1305
+ const n = this.device.createCommandEncoder({}), o = t.getBindGroupLayout(0);
1306
+ for (let u = 0; u < s; ++u) {
1307
+ let c = e.createView({
1308
+ baseMipLevel: 0,
1309
+ mipLevelCount: 1,
1310
+ dimension: "2d",
1311
+ baseArrayLayer: u,
1312
+ arrayLayerCount: 1
1313
+ }), l = i ? 1 : 0;
1314
+ for (let h = 1; h < e.mipLevelCount; ++h) {
1315
+ const f = r.createView({
1316
+ baseMipLevel: l++,
1317
+ mipLevelCount: 1,
1318
+ dimension: "2d",
1319
+ baseArrayLayer: u,
1320
+ arrayLayerCount: 1
1321
+ }), d = n.beginRenderPass({
1322
+ colorAttachments: [{
1323
+ view: f,
1324
+ storeOp: "store",
1325
+ loadOp: "clear",
1326
+ clearValue: { r: 0, g: 0, b: 0, a: 0 }
1327
+ }]
1328
+ }), _ = this.device.createBindGroup({
1329
+ layout: o,
1330
+ entries: [{
1331
+ binding: 0,
1332
+ resource: this.sampler
1333
+ }, {
1334
+ binding: 1,
1335
+ resource: c
1336
+ }]
1337
+ });
1338
+ d.setPipeline(t), d.setBindGroup(0, _), d.draw(3, 1, 0, 0), d.end(), c = f;
1339
+ }
1340
+ }
1341
+ if (!i) {
1342
+ const u = {
1343
+ width: Math.ceil(e.width / 2),
1344
+ height: Math.ceil(e.height / 2),
1345
+ depthOrArrayLayers: s
1346
+ };
1347
+ for (let c = 1; c < e.mipLevelCount; ++c)
1348
+ n.copyTextureToTexture({
1349
+ texture: r,
1350
+ mipLevel: c - 1
1351
+ }, {
1352
+ texture: e,
1353
+ mipLevel: c
1354
+ }, u), u.width = Math.ceil(u.width / 2), u.height = Math.ceil(u.height / 2);
1355
+ }
1356
+ return this.device.queue.submit([n.finish()]), i || r.destroy(), e;
1357
+ }
1358
+ }
1359
+ class te {
1360
+ constructor(e) {
1361
+ this.managedTextures = [], this._gpuSources = /* @__PURE__ */ Object.create(null), this._gpuSamplers = /* @__PURE__ */ Object.create(null), this._bindGroupHash = /* @__PURE__ */ Object.create(null), this._textureViewHash = /* @__PURE__ */ Object.create(null), this._uploads = {
1362
+ image: ee,
1363
+ buffer: De,
1364
+ video: Fe,
1365
+ compressed: ze
1366
+ }, this._renderer = e, e.renderableGC.addManagedHash(this, "_gpuSources"), e.renderableGC.addManagedHash(this, "_gpuSamplers"), e.renderableGC.addManagedHash(this, "_bindGroupHash"), e.renderableGC.addManagedHash(this, "_textureViewHash");
1367
+ }
1368
+ contextChange(e) {
1369
+ this._gpu = e;
1370
+ }
1371
+ /**
1372
+ * Initializes a texture source, if it has already been initialized nothing will happen.
1373
+ * @param source - The texture source to initialize.
1374
+ * @returns The initialized texture source.
1375
+ */
1376
+ initSource(e) {
1377
+ return this._gpuSources[e.uid] ? this._gpuSources[e.uid] : this._initSource(e);
1378
+ }
1379
+ _initSource(e) {
1380
+ if (e.autoGenerateMipmaps) {
1381
+ const u = Math.max(e.pixelWidth, e.pixelHeight);
1382
+ e.mipLevelCount = Math.floor(Math.log2(u)) + 1;
1383
+ }
1384
+ let t = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
1385
+ e.uploadMethodId !== "compressed" && (t |= GPUTextureUsage.RENDER_ATTACHMENT, t |= GPUTextureUsage.COPY_SRC);
1386
+ const r = Q[e.format] || { blockWidth: 1, blockHeight: 1 }, s = Math.ceil(e.pixelWidth / r.blockWidth) * r.blockWidth, i = Math.ceil(e.pixelHeight / r.blockHeight) * r.blockHeight, n = {
1387
+ label: e.label,
1388
+ size: { width: s, height: i },
1389
+ format: e.format,
1390
+ sampleCount: e.sampleCount,
1391
+ mipLevelCount: e.mipLevelCount,
1392
+ dimension: e.dimension,
1393
+ usage: t
1394
+ }, o = this._gpuSources[e.uid] = this._gpu.device.createTexture(n);
1395
+ return this.managedTextures.includes(e) || (e.on("update", this.onSourceUpdate, this), e.on("resize", this.onSourceResize, this), e.on("destroy", this.onSourceDestroy, this), e.on("unload", this.onSourceUnload, this), e.on("updateMipmaps", this.onUpdateMipmaps, this), this.managedTextures.push(e)), this.onSourceUpdate(e), o;
1396
+ }
1397
+ onSourceUpdate(e) {
1398
+ const t = this.getGpuSource(e);
1399
+ t && (this._uploads[e.uploadMethodId] && this._uploads[e.uploadMethodId].upload(e, t, this._gpu), e.autoGenerateMipmaps && e.mipLevelCount > 1 && this.onUpdateMipmaps(e));
1400
+ }
1401
+ onSourceUnload(e) {
1402
+ const t = this._gpuSources[e.uid];
1403
+ t && (this._gpuSources[e.uid] = null, t.destroy());
1404
+ }
1405
+ onUpdateMipmaps(e) {
1406
+ this._mipmapGenerator || (this._mipmapGenerator = new Oe(this._gpu.device));
1407
+ const t = this.getGpuSource(e);
1408
+ this._mipmapGenerator.generateMipmap(t);
1409
+ }
1410
+ onSourceDestroy(e) {
1411
+ e.off("update", this.onSourceUpdate, this), e.off("unload", this.onSourceUnload, this), e.off("destroy", this.onSourceDestroy, this), e.off("resize", this.onSourceResize, this), e.off("updateMipmaps", this.onUpdateMipmaps, this), this.managedTextures.splice(this.managedTextures.indexOf(e), 1), this.onSourceUnload(e);
1412
+ }
1413
+ onSourceResize(e) {
1414
+ const t = this._gpuSources[e.uid];
1415
+ t ? (t.width !== e.pixelWidth || t.height !== e.pixelHeight) && (this._textureViewHash[e.uid] = null, this._bindGroupHash[e.uid] = null, this.onSourceUnload(e), this.initSource(e)) : this.initSource(e);
1416
+ }
1417
+ _initSampler(e) {
1418
+ return this._gpuSamplers[e._resourceId] = this._gpu.device.createSampler(e), this._gpuSamplers[e._resourceId];
1419
+ }
1420
+ getGpuSampler(e) {
1421
+ return this._gpuSamplers[e._resourceId] || this._initSampler(e);
1422
+ }
1423
+ getGpuSource(e) {
1424
+ return this._gpuSources[e.uid] || this.initSource(e);
1425
+ }
1426
+ /**
1427
+ * this returns s bind group for a specific texture, the bind group contains
1428
+ * - the texture source
1429
+ * - the texture style
1430
+ * - the texture matrix
1431
+ * This is cached so the bind group should only be created once per texture
1432
+ * @param texture - the texture you want the bindgroup for
1433
+ * @returns the bind group for the texture
1434
+ */
1435
+ getTextureBindGroup(e) {
1436
+ return this._bindGroupHash[e.uid] ?? this._createTextureBindGroup(e);
1437
+ }
1438
+ _createTextureBindGroup(e) {
1439
+ const t = e.source;
1440
+ return this._bindGroupHash[e.uid] = new L({
1441
+ 0: t,
1442
+ 1: t.style,
1443
+ 2: new A({
1444
+ uTextureMatrix: { type: "mat3x3<f32>", value: e.textureMatrix.mapCoord }
1445
+ })
1446
+ }), this._bindGroupHash[e.uid];
1447
+ }
1448
+ getTextureView(e) {
1449
+ const t = e.source;
1450
+ return this._textureViewHash[t.uid] ?? this._createTextureView(t);
1451
+ }
1452
+ _createTextureView(e) {
1453
+ return this._textureViewHash[e.uid] = this.getGpuSource(e).createView(), this._textureViewHash[e.uid];
1454
+ }
1455
+ generateCanvas(e) {
1456
+ const t = this._renderer, r = t.gpu.device.createCommandEncoder(), s = B.get().createCanvas();
1457
+ s.width = e.source.pixelWidth, s.height = e.source.pixelHeight;
1458
+ const i = s.getContext("webgpu");
1459
+ return i.configure({
1460
+ device: t.gpu.device,
1461
+ usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC,
1462
+ format: B.get().getNavigator().gpu.getPreferredCanvasFormat(),
1463
+ alphaMode: "premultiplied"
1464
+ }), r.copyTextureToTexture({
1465
+ texture: t.texture.getGpuSource(e.source),
1466
+ origin: {
1467
+ x: 0,
1468
+ y: 0
1469
+ }
1470
+ }, {
1471
+ texture: i.getCurrentTexture()
1472
+ }, {
1473
+ width: s.width,
1474
+ height: s.height
1475
+ }), t.gpu.device.queue.submit([r.finish()]), s;
1476
+ }
1477
+ getPixels(e) {
1478
+ const t = this.generateCanvas(e), r = w.getOptimalCanvasAndContext(t.width, t.height), s = r.context;
1479
+ s.drawImage(t, 0, 0);
1480
+ const { width: i, height: n } = t, o = s.getImageData(0, 0, i, n), u = new Uint8ClampedArray(o.data.buffer);
1481
+ return w.returnCanvasAndContext(r), { pixels: u, width: i, height: n };
1482
+ }
1483
+ destroy() {
1484
+ this.managedTextures.slice().forEach((e) => this.onSourceDestroy(e)), this.managedTextures = null;
1485
+ for (const e of Object.keys(this._bindGroupHash)) {
1486
+ const t = Number(e), r = this._bindGroupHash[t];
1487
+ r == null || r.destroy(), this._bindGroupHash[t] = null;
1488
+ }
1489
+ this._gpu = null, this._mipmapGenerator = null, this._gpuSources = null, this._bindGroupHash = null, this._textureViewHash = null, this._gpuSamplers = null;
1490
+ }
1491
+ }
1492
+ te.extension = {
1493
+ type: [
1494
+ p.WebGPUSystem
1495
+ ],
1496
+ name: "texture"
1497
+ };
1498
+ class re {
1499
+ constructor() {
1500
+ this._maxTextures = 0;
1501
+ }
1502
+ contextChange(e) {
1503
+ const t = new A({
1504
+ uTransformMatrix: { value: new k(), type: "mat3x3<f32>" },
1505
+ uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
1506
+ uRound: { value: 0, type: "f32" }
1507
+ });
1508
+ this._maxTextures = e.limits.maxBatchableTextures;
1509
+ const r = D({
1510
+ name: "graphics",
1511
+ bits: [
1512
+ he,
1513
+ pe(this._maxTextures),
1514
+ ge,
1515
+ H
1516
+ ]
1517
+ });
1518
+ this.shader = new z({
1519
+ gpuProgram: r,
1520
+ resources: {
1521
+ // added on the fly!
1522
+ localUniforms: t
1523
+ }
1524
+ });
1525
+ }
1526
+ execute(e, t) {
1527
+ const r = t.context, s = r.customShader || this.shader, i = e.renderer, n = i.graphicsContext, {
1528
+ batcher: o,
1529
+ instructions: u
1530
+ } = n.getContextRenderData(r), c = i.encoder;
1531
+ c.setGeometry(o.geometry, s.gpuProgram);
1532
+ const l = i.globalUniforms.bindGroup;
1533
+ c.setBindGroup(0, l, s.gpuProgram);
1534
+ const h = i.renderPipes.uniformBatch.getUniformBindGroup(s.resources.localUniforms, !0);
1535
+ c.setBindGroup(2, h, s.gpuProgram);
1536
+ const f = u.instructions;
1537
+ let d = null;
1538
+ for (let _ = 0; _ < u.instructionSize; _++) {
1539
+ const m = f[_];
1540
+ if (m.topology !== d && (d = m.topology, c.setPipelineFromGeometryProgramAndState(
1541
+ o.geometry,
1542
+ s.gpuProgram,
1543
+ e.state,
1544
+ m.topology
1545
+ )), s.groups[1] = m.bindGroup, !m.gpuBindGroup) {
1546
+ const P = m.textures;
1547
+ m.bindGroup = E(
1548
+ P.textures,
1549
+ P.count,
1550
+ this._maxTextures
1551
+ ), m.gpuBindGroup = i.bindGroup.getBindGroup(
1552
+ m.bindGroup,
1553
+ s.gpuProgram,
1554
+ 1
1555
+ );
1556
+ }
1557
+ c.setBindGroup(1, m.bindGroup, s.gpuProgram), c.renderPassEncoder.drawIndexed(m.size, 1, m.start);
1558
+ }
1559
+ }
1560
+ destroy() {
1561
+ this.shader.destroy(!0), this.shader = null;
1562
+ }
1563
+ }
1564
+ re.extension = {
1565
+ type: [
1566
+ p.WebGPUPipesAdaptor
1567
+ ],
1568
+ name: "graphics"
1569
+ };
1570
+ class se {
1571
+ init() {
1572
+ const e = D({
1573
+ name: "mesh",
1574
+ bits: [
1575
+ me,
1576
+ Pe,
1577
+ H
1578
+ ]
1579
+ });
1580
+ this._shader = new z({
1581
+ gpuProgram: e,
1582
+ resources: {
1583
+ uTexture: R.EMPTY._source,
1584
+ uSampler: R.EMPTY._source.style,
1585
+ textureUniforms: {
1586
+ uTextureMatrix: { type: "mat3x3<f32>", value: new k() }
1587
+ }
1588
+ }
1589
+ });
1590
+ }
1591
+ execute(e, t) {
1592
+ const r = e.renderer;
1593
+ let s = t._shader;
1594
+ if (!s)
1595
+ s = this._shader, s.groups[2] = r.texture.getTextureBindGroup(t.texture);
1596
+ else if (!s.gpuProgram) {
1597
+ v("Mesh shader has no gpuProgram", t.shader);
1598
+ return;
1599
+ }
1600
+ const i = s.gpuProgram;
1601
+ if (i.autoAssignGlobalUniforms && (s.groups[0] = r.globalUniforms.bindGroup), i.autoAssignLocalUniforms) {
1602
+ const n = e.localUniforms;
1603
+ s.groups[1] = r.renderPipes.uniformBatch.getUniformBindGroup(n, !0);
1604
+ }
1605
+ r.encoder.draw({
1606
+ geometry: t._geometry,
1607
+ shader: s,
1608
+ state: t.state
1609
+ });
1610
+ }
1611
+ destroy() {
1612
+ this._shader.destroy(!0), this._shader = null;
1613
+ }
1614
+ }
1615
+ se.extension = {
1616
+ type: [
1617
+ p.WebGPUPipesAdaptor
1618
+ ],
1619
+ name: "mesh"
1620
+ };
1621
+ const Ie = [
1622
+ ...Te,
1623
+ q,
1624
+ N,
1625
+ U,
1626
+ j,
1627
+ W,
1628
+ te,
1629
+ X,
1630
+ Z,
1631
+ J,
1632
+ $,
1633
+ V,
1634
+ K,
1635
+ I
1636
+ ], We = [...Ce, Y], Ve = [O, se, re], ie = [], ne = [], oe = [];
1637
+ S.handleByNamedList(p.WebGPUSystem, ie);
1638
+ S.handleByNamedList(p.WebGPUPipes, ne);
1639
+ S.handleByNamedList(p.WebGPUPipesAdaptor, oe);
1640
+ S.add(...Ie, ...We, ...Ve);
1641
+ class qe extends le {
1642
+ constructor() {
1643
+ const e = {
1644
+ name: "webgpu",
1645
+ type: fe.WEBGPU,
1646
+ systems: ie,
1647
+ renderPipes: ne,
1648
+ renderPipeAdaptors: oe
1649
+ };
1650
+ super(e);
1651
+ }
1652
+ }
1653
+ export {
1654
+ qe as WebGPURenderer
1655
+ };
src/backend/gradio_polygonannotator/templates/component/browserAll-DD82MfdK.js ADDED
@@ -0,0 +1,1869 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { T as A, U as Z, P as g, r as te, E as b, a as ie, w as y, e as w, C as V } from "./Index-jQCzN2ap.js";
2
+ import "./webworkerAll-BVEtD__r.js";
3
+ class q {
4
+ constructor(e) {
5
+ this._lastTransform = "", this._observer = null, this._tickerAttached = !1, this.updateTranslation = () => {
6
+ if (!this._canvas)
7
+ return;
8
+ const t = this._canvas.getBoundingClientRect(), i = this._canvas.width, n = this._canvas.height, s = t.width / i * this._renderer.resolution, o = t.height / n * this._renderer.resolution, r = t.left, l = t.top, d = `translate(${r}px, ${l}px) scale(${s}, ${o})`;
9
+ d !== this._lastTransform && (this._domElement.style.transform = d, this._lastTransform = d);
10
+ }, this._domElement = e.domElement, this._renderer = e.renderer, !(globalThis.OffscreenCanvas && this._renderer.canvas instanceof OffscreenCanvas) && (this._canvas = this._renderer.canvas, this._attachObserver());
11
+ }
12
+ /** The canvas element that this CanvasObserver is associated with. */
13
+ get canvas() {
14
+ return this._canvas;
15
+ }
16
+ /** Attaches the DOM element to the canvas parent if it is not already attached. */
17
+ ensureAttached() {
18
+ !this._domElement.parentNode && this._canvas.parentNode && (this._canvas.parentNode.appendChild(this._domElement), this.updateTranslation());
19
+ }
20
+ /** Sets up a ResizeObserver if available. This ensures that the DOM element is kept in sync with the canvas size . */
21
+ _attachObserver() {
22
+ "ResizeObserver" in globalThis ? (this._observer && (this._observer.disconnect(), this._observer = null), this._observer = new ResizeObserver((e) => {
23
+ for (const t of e) {
24
+ if (t.target !== this._canvas)
25
+ continue;
26
+ const i = this.canvas.width, n = this.canvas.height, s = t.contentRect.width / i * this._renderer.resolution, o = t.contentRect.height / n * this._renderer.resolution;
27
+ (this._lastScaleX !== s || this._lastScaleY !== o) && (this.updateTranslation(), this._lastScaleX = s, this._lastScaleY = o);
28
+ }
29
+ }), this._observer.observe(this._canvas)) : this._tickerAttached || A.shared.add(this.updateTranslation, this, Z.HIGH);
30
+ }
31
+ /** Destroys the CanvasObserver instance, cleaning up observers and Ticker. */
32
+ destroy() {
33
+ this._observer ? (this._observer.disconnect(), this._observer = null) : this._tickerAttached && A.shared.remove(this.updateTranslation), this._domElement = null, this._renderer = null, this._canvas = null, this._tickerAttached = !1, this._lastTransform = "", this._lastScaleX = null, this._lastScaleY = null;
34
+ }
35
+ }
36
+ class M {
37
+ /**
38
+ * @param manager - The event boundary which manages this event. Propagation can only occur
39
+ * within the boundary's jurisdiction.
40
+ */
41
+ constructor(e) {
42
+ this.bubbles = !0, this.cancelBubble = !0, this.cancelable = !1, this.composed = !1, this.defaultPrevented = !1, this.eventPhase = M.prototype.NONE, this.propagationStopped = !1, this.propagationImmediatelyStopped = !1, this.layer = new g(), this.page = new g(), this.NONE = 0, this.CAPTURING_PHASE = 1, this.AT_TARGET = 2, this.BUBBLING_PHASE = 3, this.manager = e;
43
+ }
44
+ /** @readonly */
45
+ get layerX() {
46
+ return this.layer.x;
47
+ }
48
+ /** @readonly */
49
+ get layerY() {
50
+ return this.layer.y;
51
+ }
52
+ /** @readonly */
53
+ get pageX() {
54
+ return this.page.x;
55
+ }
56
+ /** @readonly */
57
+ get pageY() {
58
+ return this.page.y;
59
+ }
60
+ /**
61
+ * Fallback for the deprecated `InteractionEvent.data`.
62
+ * @deprecated since 7.0.0
63
+ */
64
+ get data() {
65
+ return this;
66
+ }
67
+ /**
68
+ * The propagation path for this event. Alias for {@link EventBoundary.propagationPath}.
69
+ * @advanced
70
+ */
71
+ composedPath() {
72
+ return this.manager && (!this.path || this.path[this.path.length - 1] !== this.target) && (this.path = this.target ? this.manager.propagationPath(this.target) : []), this.path;
73
+ }
74
+ /**
75
+ * Unimplemented method included for implementing the DOM interface `Event`. It will throw an `Error`.
76
+ * @deprecated
77
+ * @ignore
78
+ * @param _type
79
+ * @param _bubbles
80
+ * @param _cancelable
81
+ */
82
+ initEvent(e, t, i) {
83
+ throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.");
84
+ }
85
+ /**
86
+ * Unimplemented method included for implementing the DOM interface `UIEvent`. It will throw an `Error`.
87
+ * @ignore
88
+ * @deprecated
89
+ * @param _typeArg
90
+ * @param _bubblesArg
91
+ * @param _cancelableArg
92
+ * @param _viewArg
93
+ * @param _detailArg
94
+ */
95
+ initUIEvent(e, t, i, n, s) {
96
+ throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.");
97
+ }
98
+ /**
99
+ * Prevent default behavior of both PixiJS and the user agent.
100
+ * @example
101
+ * ```ts
102
+ * sprite.on('click', (event) => {
103
+ * // Prevent both browser's default click behavior
104
+ * // and PixiJS's default handling
105
+ * event.preventDefault();
106
+ *
107
+ * // Custom handling
108
+ * customClickHandler();
109
+ * });
110
+ * ```
111
+ * @remarks
112
+ * - Only works if the native event is cancelable
113
+ * - Does not stop event propagation
114
+ */
115
+ preventDefault() {
116
+ this.nativeEvent instanceof Event && this.nativeEvent.cancelable && this.nativeEvent.preventDefault(), this.defaultPrevented = !0;
117
+ }
118
+ /**
119
+ * Stop this event from propagating to any additional listeners, including those
120
+ * on the current target and any following targets in the propagation path.
121
+ * @example
122
+ * ```ts
123
+ * container.on('pointerdown', (event) => {
124
+ * // Stop all further event handling
125
+ * event.stopImmediatePropagation();
126
+ *
127
+ * // These handlers won't be called:
128
+ * // - Other pointerdown listeners on this container
129
+ * // - Any pointerdown listeners on parent containers
130
+ * });
131
+ * ```
132
+ * @remarks
133
+ * - Immediately stops all event propagation
134
+ * - Prevents other listeners on same target from being called
135
+ * - More aggressive than stopPropagation()
136
+ */
137
+ stopImmediatePropagation() {
138
+ this.propagationImmediatelyStopped = !0;
139
+ }
140
+ /**
141
+ * Stop this event from propagating to the next target in the propagation path.
142
+ * The rest of the listeners on the current target will still be notified.
143
+ * @example
144
+ * ```ts
145
+ * child.on('pointermove', (event) => {
146
+ * // Handle event on child
147
+ * updateChild();
148
+ *
149
+ * // Prevent parent handlers from being called
150
+ * event.stopPropagation();
151
+ * });
152
+ *
153
+ * // This won't be called if child handles the event
154
+ * parent.on('pointermove', (event) => {
155
+ * updateParent();
156
+ * });
157
+ * ```
158
+ * @remarks
159
+ * - Stops event bubbling to parent containers
160
+ * - Does not prevent other listeners on same target
161
+ * - Less aggressive than stopImmediatePropagation()
162
+ */
163
+ stopPropagation() {
164
+ this.propagationStopped = !0;
165
+ }
166
+ }
167
+ var I = /iPhone/i, C = /iPod/i, L = /iPad/i, U = /\biOS-universal(?:.+)Mac\b/i, k = /\bAndroid(?:.+)Mobile\b/i, R = /Android/i, E = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i, O = /Silk/i, m = /Windows Phone/i, X = /\bWindows(?:.+)ARM\b/i, Y = /BlackBerry/i, H = /BB10/i, F = /Opera Mini/i, N = /\b(CriOS|Chrome)(?:.+)Mobile/i, $ = /Mobile(?:.+)Firefox\b/i, K = function(a) {
168
+ return typeof a < "u" && a.platform === "MacIntel" && typeof a.maxTouchPoints == "number" && a.maxTouchPoints > 1 && typeof MSStream > "u";
169
+ };
170
+ function ne(a) {
171
+ return function(e) {
172
+ return e.test(a);
173
+ };
174
+ }
175
+ function G(a) {
176
+ var e = {
177
+ userAgent: "",
178
+ platform: "",
179
+ maxTouchPoints: 0
180
+ };
181
+ !a && typeof navigator < "u" ? e = {
182
+ userAgent: navigator.userAgent,
183
+ platform: navigator.platform,
184
+ maxTouchPoints: navigator.maxTouchPoints || 0
185
+ } : typeof a == "string" ? e.userAgent = a : a && a.userAgent && (e = {
186
+ userAgent: a.userAgent,
187
+ platform: a.platform,
188
+ maxTouchPoints: a.maxTouchPoints || 0
189
+ });
190
+ var t = e.userAgent, i = t.split("[FBAN");
191
+ typeof i[1] < "u" && (t = i[0]), i = t.split("Twitter"), typeof i[1] < "u" && (t = i[0]);
192
+ var n = ne(t), s = {
193
+ apple: {
194
+ phone: n(I) && !n(m),
195
+ ipod: n(C),
196
+ tablet: !n(I) && (n(L) || K(e)) && !n(m),
197
+ universal: n(U),
198
+ device: (n(I) || n(C) || n(L) || n(U) || K(e)) && !n(m)
199
+ },
200
+ amazon: {
201
+ phone: n(E),
202
+ tablet: !n(E) && n(O),
203
+ device: n(E) || n(O)
204
+ },
205
+ android: {
206
+ phone: !n(m) && n(E) || !n(m) && n(k),
207
+ tablet: !n(m) && !n(E) && !n(k) && (n(O) || n(R)),
208
+ device: !n(m) && (n(E) || n(O) || n(k) || n(R)) || n(/\bokhttp\b/i)
209
+ },
210
+ windows: {
211
+ phone: n(m),
212
+ tablet: n(X),
213
+ device: n(m) || n(X)
214
+ },
215
+ other: {
216
+ blackberry: n(Y),
217
+ blackberry10: n(H),
218
+ opera: n(F),
219
+ firefox: n($),
220
+ chrome: n(N),
221
+ device: n(Y) || n(H) || n(F) || n($) || n(N)
222
+ },
223
+ any: !1,
224
+ phone: !1,
225
+ tablet: !1
226
+ };
227
+ return s.any = s.apple.device || s.android.device || s.windows.device || s.other.device, s.phone = s.apple.phone || s.android.phone || s.windows.phone, s.tablet = s.apple.tablet || s.android.tablet || s.windows.tablet, s;
228
+ }
229
+ const se = G.default ?? G, oe = se(globalThis.navigator), re = 9, W = 100, ae = 0, he = 0, j = 2, z = 1, le = -1e3, ce = -1e3, de = 2, S = class J {
230
+ // eslint-disable-next-line jsdoc/require-param
231
+ /**
232
+ * @param {WebGLRenderer|WebGPURenderer} renderer - A reference to the current renderer
233
+ */
234
+ constructor(e, t = oe) {
235
+ this._mobileInfo = t, this.debug = !1, this._activateOnTab = !0, this._deactivateOnMouseMove = !0, this._isActive = !1, this._isMobileAccessibility = !1, this._div = null, this._pool = [], this._renderId = 0, this._children = [], this._androidUpdateCount = 0, this._androidUpdateFrequency = 500, this._hookDiv = null, (t.tablet || t.phone) && this._createTouchHook(), this._renderer = e;
236
+ }
237
+ /**
238
+ * Value of `true` if accessibility is currently active and accessibility layers are showing.
239
+ * @type {boolean}
240
+ * @readonly
241
+ */
242
+ get isActive() {
243
+ return this._isActive;
244
+ }
245
+ /**
246
+ * Value of `true` if accessibility is enabled for touch devices.
247
+ * @type {boolean}
248
+ * @readonly
249
+ */
250
+ get isMobileAccessibility() {
251
+ return this._isMobileAccessibility;
252
+ }
253
+ /**
254
+ * The DOM element that will sit over the PixiJS element. This is where the div overlays will go.
255
+ * @readonly
256
+ */
257
+ get hookDiv() {
258
+ return this._hookDiv;
259
+ }
260
+ /**
261
+ * Creates the touch hooks.
262
+ * @private
263
+ */
264
+ _createTouchHook() {
265
+ const e = document.createElement("button");
266
+ e.style.width = `${z}px`, e.style.height = `${z}px`, e.style.position = "absolute", e.style.top = `${le}px`, e.style.left = `${ce}px`, e.style.zIndex = de.toString(), e.style.backgroundColor = "#FF0000", e.title = "select to enable accessibility for this content", e.addEventListener("focus", () => {
267
+ this._isMobileAccessibility = !0, this._activate(), this._destroyTouchHook();
268
+ }), document.body.appendChild(e), this._hookDiv = e;
269
+ }
270
+ /**
271
+ * Destroys the touch hooks.
272
+ * @private
273
+ */
274
+ _destroyTouchHook() {
275
+ this._hookDiv && (document.body.removeChild(this._hookDiv), this._hookDiv = null);
276
+ }
277
+ /**
278
+ * Activating will cause the Accessibility layer to be shown.
279
+ * This is called when a user presses the tab key.
280
+ * @private
281
+ */
282
+ _activate() {
283
+ if (this._isActive)
284
+ return;
285
+ this._isActive = !0, this._div || (this._div = document.createElement("div"), this._div.style.position = "absolute", this._div.style.top = `${ae}px`, this._div.style.left = `${he}px`, this._div.style.pointerEvents = "none", this._div.style.zIndex = j.toString(), this._canvasObserver = new q({
286
+ domElement: this._div,
287
+ renderer: this._renderer
288
+ })), this._activateOnTab && (this._onKeyDown = this._onKeyDown.bind(this), globalThis.addEventListener("keydown", this._onKeyDown, !1)), this._deactivateOnMouseMove && (this._onMouseMove = this._onMouseMove.bind(this), globalThis.document.addEventListener("mousemove", this._onMouseMove, !0));
289
+ const e = this._renderer.view.canvas;
290
+ if (e.parentNode)
291
+ this._canvasObserver.ensureAttached(), this._initAccessibilitySetup();
292
+ else {
293
+ const t = new MutationObserver(() => {
294
+ e.parentNode && (t.disconnect(), this._canvasObserver.ensureAttached(), this._initAccessibilitySetup());
295
+ });
296
+ t.observe(document.body, { childList: !0, subtree: !0 });
297
+ }
298
+ }
299
+ // New method to handle initialization after div is ready
300
+ _initAccessibilitySetup() {
301
+ this._renderer.runners.postrender.add(this), this._renderer.lastObjectRendered && this._updateAccessibleObjects(this._renderer.lastObjectRendered);
302
+ }
303
+ /**
304
+ * Deactivates the accessibility system. Removes listeners and accessibility elements.
305
+ * @private
306
+ */
307
+ _deactivate() {
308
+ if (!(!this._isActive || this._isMobileAccessibility)) {
309
+ this._isActive = !1, globalThis.document.removeEventListener("mousemove", this._onMouseMove, !0), this._activateOnTab && globalThis.addEventListener("keydown", this._onKeyDown, !1), this._renderer.runners.postrender.remove(this);
310
+ for (const e of this._children)
311
+ e._accessibleDiv && e._accessibleDiv.parentNode && (e._accessibleDiv.parentNode.removeChild(e._accessibleDiv), e._accessibleDiv = null), e._accessibleActive = !1;
312
+ this._pool.forEach((e) => {
313
+ e.parentNode && e.parentNode.removeChild(e);
314
+ }), this._div && this._div.parentNode && this._div.parentNode.removeChild(this._div), this._pool = [], this._children = [];
315
+ }
316
+ }
317
+ /**
318
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
319
+ * @private
320
+ * @param {Container} container - The Container to check.
321
+ */
322
+ _updateAccessibleObjects(e) {
323
+ if (!e.visible || !e.accessibleChildren)
324
+ return;
325
+ e.accessible && (e._accessibleActive || this._addChild(e), e._renderId = this._renderId);
326
+ const t = e.children;
327
+ if (t)
328
+ for (let i = 0; i < t.length; i++)
329
+ this._updateAccessibleObjects(t[i]);
330
+ }
331
+ /**
332
+ * Runner init called, view is available at this point.
333
+ * @ignore
334
+ */
335
+ init(e) {
336
+ const i = {
337
+ accessibilityOptions: {
338
+ ...J.defaultOptions,
339
+ ...(e == null ? void 0 : e.accessibilityOptions) || {}
340
+ }
341
+ };
342
+ this.debug = i.accessibilityOptions.debug, this._activateOnTab = i.accessibilityOptions.activateOnTab, this._deactivateOnMouseMove = i.accessibilityOptions.deactivateOnMouseMove, i.accessibilityOptions.enabledByDefault ? this._activate() : this._activateOnTab && (this._onKeyDown = this._onKeyDown.bind(this), globalThis.addEventListener("keydown", this._onKeyDown, !1)), this._renderer.runners.postrender.remove(this);
343
+ }
344
+ /**
345
+ * Updates the accessibility layer during rendering.
346
+ * - Removes divs for containers no longer in the scene
347
+ * - Updates the position and dimensions of the root div
348
+ * - Updates positions of active accessibility divs
349
+ * Only fires while the accessibility system is active.
350
+ * @ignore
351
+ */
352
+ postrender() {
353
+ const e = performance.now();
354
+ if (this._mobileInfo.android.device && e < this._androidUpdateCount || (this._androidUpdateCount = e + this._androidUpdateFrequency, !this._renderer.renderingToScreen || !this._renderer.view.canvas))
355
+ return;
356
+ const t = /* @__PURE__ */ new Set();
357
+ if (this._renderer.lastObjectRendered) {
358
+ this._updateAccessibleObjects(this._renderer.lastObjectRendered);
359
+ for (const i of this._children)
360
+ i._renderId === this._renderId && t.add(this._children.indexOf(i));
361
+ }
362
+ for (let i = this._children.length - 1; i >= 0; i--) {
363
+ const n = this._children[i];
364
+ t.has(i) || (n._accessibleDiv && n._accessibleDiv.parentNode && (n._accessibleDiv.parentNode.removeChild(n._accessibleDiv), this._pool.push(n._accessibleDiv), n._accessibleDiv = null), n._accessibleActive = !1, te(this._children, i, 1));
365
+ }
366
+ this._renderer.renderingToScreen && this._canvasObserver.ensureAttached();
367
+ for (let i = 0; i < this._children.length; i++) {
368
+ const n = this._children[i];
369
+ if (!n._accessibleActive || !n._accessibleDiv)
370
+ continue;
371
+ const s = n._accessibleDiv, o = n.hitArea || n.getBounds().rectangle;
372
+ if (n.hitArea) {
373
+ const r = n.worldTransform;
374
+ s.style.left = `${r.tx + o.x * r.a}px`, s.style.top = `${r.ty + o.y * r.d}px`, s.style.width = `${o.width * r.a}px`, s.style.height = `${o.height * r.d}px`;
375
+ } else
376
+ this._capHitArea(o), s.style.left = `${o.x}px`, s.style.top = `${o.y}px`, s.style.width = `${o.width}px`, s.style.height = `${o.height}px`;
377
+ }
378
+ this._renderId++;
379
+ }
380
+ /**
381
+ * private function that will visually add the information to the
382
+ * accessibility div
383
+ * @param {HTMLElement} div -
384
+ */
385
+ _updateDebugHTML(e) {
386
+ e.innerHTML = `type: ${e.type}</br> title : ${e.title}</br> tabIndex: ${e.tabIndex}`;
387
+ }
388
+ /**
389
+ * Adjust the hit area based on the bounds of a display object
390
+ * @param {Rectangle} hitArea - Bounds of the child
391
+ */
392
+ _capHitArea(e) {
393
+ e.x < 0 && (e.width += e.x, e.x = 0), e.y < 0 && (e.height += e.y, e.y = 0);
394
+ const { width: t, height: i } = this._renderer;
395
+ e.x + e.width > t && (e.width = t - e.x), e.y + e.height > i && (e.height = i - e.y);
396
+ }
397
+ /**
398
+ * Creates or reuses a div element for a Container and adds it to the accessibility layer.
399
+ * Sets up ARIA attributes, event listeners, and positioning based on the container's properties.
400
+ * @private
401
+ * @param {Container} container - The child to make accessible.
402
+ */
403
+ _addChild(e) {
404
+ let t = this._pool.pop();
405
+ t || (e.accessibleType === "button" ? t = document.createElement("button") : (t = document.createElement(e.accessibleType), t.style.cssText = `
406
+ color: transparent;
407
+ pointer-events: none;
408
+ padding: 0;
409
+ margin: 0;
410
+ border: 0;
411
+ outline: 0;
412
+ background: transparent;
413
+ box-sizing: border-box;
414
+ user-select: none;
415
+ -webkit-user-select: none;
416
+ -moz-user-select: none;
417
+ -ms-user-select: none;
418
+ `, e.accessibleText && (t.innerText = e.accessibleText)), t.style.width = `${W}px`, t.style.height = `${W}px`, t.style.backgroundColor = this.debug ? "rgba(255,255,255,0.5)" : "transparent", t.style.position = "absolute", t.style.zIndex = j.toString(), t.style.borderStyle = "none", navigator.userAgent.toLowerCase().includes("chrome") ? t.setAttribute("aria-live", "off") : t.setAttribute("aria-live", "polite"), navigator.userAgent.match(/rv:.*Gecko\//) ? t.setAttribute("aria-relevant", "additions") : t.setAttribute("aria-relevant", "text"), t.addEventListener("click", this._onClick.bind(this)), t.addEventListener("focus", this._onFocus.bind(this)), t.addEventListener("focusout", this._onFocusOut.bind(this))), t.style.pointerEvents = e.accessiblePointerEvents, t.type = e.accessibleType, e.accessibleTitle && e.accessibleTitle !== null ? t.title = e.accessibleTitle : (!e.accessibleHint || e.accessibleHint === null) && (t.title = `container ${e.tabIndex}`), e.accessibleHint && e.accessibleHint !== null && t.setAttribute("aria-label", e.accessibleHint), e.interactive ? t.tabIndex = e.tabIndex : t.tabIndex = 0, this.debug && this._updateDebugHTML(t), e._accessibleActive = !0, e._accessibleDiv = t, t.container = e, this._children.push(e), this._div.appendChild(e._accessibleDiv);
419
+ }
420
+ /**
421
+ * Dispatch events with the EventSystem.
422
+ * @param e
423
+ * @param type
424
+ * @private
425
+ */
426
+ _dispatchEvent(e, t) {
427
+ const { container: i } = e.target, n = this._renderer.events.rootBoundary, s = Object.assign(new M(n), { target: i });
428
+ n.rootTarget = this._renderer.lastObjectRendered, t.forEach((o) => n.dispatchEvent(s, o));
429
+ }
430
+ /**
431
+ * Maps the div button press to pixi's EventSystem (click)
432
+ * @private
433
+ * @param {MouseEvent} e - The click event.
434
+ */
435
+ _onClick(e) {
436
+ this._dispatchEvent(e, ["click", "pointertap", "tap"]);
437
+ }
438
+ /**
439
+ * Maps the div focus events to pixi's EventSystem (mouseover)
440
+ * @private
441
+ * @param {FocusEvent} e - The focus event.
442
+ */
443
+ _onFocus(e) {
444
+ e.target.getAttribute("aria-live") || e.target.setAttribute("aria-live", "assertive"), this._dispatchEvent(e, ["mouseover"]);
445
+ }
446
+ /**
447
+ * Maps the div focus events to pixi's EventSystem (mouseout)
448
+ * @private
449
+ * @param {FocusEvent} e - The focusout event.
450
+ */
451
+ _onFocusOut(e) {
452
+ e.target.getAttribute("aria-live") || e.target.setAttribute("aria-live", "polite"), this._dispatchEvent(e, ["mouseout"]);
453
+ }
454
+ /**
455
+ * Is called when a key is pressed
456
+ * @private
457
+ * @param {KeyboardEvent} e - The keydown event.
458
+ */
459
+ _onKeyDown(e) {
460
+ e.keyCode !== re || !this._activateOnTab || this._activate();
461
+ }
462
+ /**
463
+ * Is called when the mouse moves across the renderer element
464
+ * @private
465
+ * @param {MouseEvent} e - The mouse event.
466
+ */
467
+ _onMouseMove(e) {
468
+ e.movementX === 0 && e.movementY === 0 || this._deactivate();
469
+ }
470
+ /**
471
+ * Destroys the accessibility system. Removes all elements and listeners.
472
+ * > [!IMPORTANT] This is typically called automatically when the {@link Application} is destroyed.
473
+ * > A typically user should not need to call this method directly.
474
+ */
475
+ destroy() {
476
+ var e;
477
+ this._deactivate(), this._destroyTouchHook(), (e = this._canvasObserver) == null || e.destroy(), this._canvasObserver = null, this._div = null, this._pool = null, this._children = null, this._renderer = null, this._activateOnTab && globalThis.removeEventListener("keydown", this._onKeyDown);
478
+ }
479
+ /**
480
+ * Enables or disables the accessibility system.
481
+ * @param enabled - Whether to enable or disable accessibility.
482
+ * @example
483
+ * ```js
484
+ * app.renderer.accessibility.setAccessibilityEnabled(true); // Enable accessibility
485
+ * app.renderer.accessibility.setAccessibilityEnabled(false); // Disable accessibility
486
+ * ```
487
+ */
488
+ setAccessibilityEnabled(e) {
489
+ e ? this._activate() : this._deactivate();
490
+ }
491
+ };
492
+ S.extension = {
493
+ type: [
494
+ b.WebGLSystem,
495
+ b.WebGPUSystem
496
+ ],
497
+ name: "accessibility"
498
+ };
499
+ S.defaultOptions = {
500
+ /**
501
+ * Whether to enable accessibility features on initialization
502
+ * @default false
503
+ */
504
+ enabledByDefault: !1,
505
+ /**
506
+ * Whether to visually show the accessibility divs for debugging
507
+ * @default false
508
+ */
509
+ debug: !1,
510
+ /**
511
+ * Whether to activate accessibility when tab key is pressed
512
+ * @default true
513
+ */
514
+ activateOnTab: !0,
515
+ /**
516
+ * Whether to deactivate accessibility when mouse moves
517
+ * @default true
518
+ */
519
+ deactivateOnMouseMove: !0
520
+ };
521
+ let ue = S;
522
+ const pe = {
523
+ accessible: !1,
524
+ accessibleTitle: null,
525
+ accessibleHint: null,
526
+ tabIndex: 0,
527
+ accessibleType: "button",
528
+ accessibleText: null,
529
+ accessiblePointerEvents: "auto",
530
+ accessibleChildren: !0,
531
+ _accessibleActive: !1,
532
+ _accessibleDiv: null,
533
+ _renderId: -1
534
+ };
535
+ class Q {
536
+ /**
537
+ * Constructor for the DOMPipe class.
538
+ * @param renderer - The renderer instance that this DOMPipe will be associated with.
539
+ */
540
+ constructor(e) {
541
+ this._attachedDomElements = [], this._renderer = e, this._renderer.runners.postrender.add(this), this._renderer.runners.init.add(this), this._domElement = document.createElement("div"), this._domElement.style.position = "absolute", this._domElement.style.top = "0", this._domElement.style.left = "0", this._domElement.style.pointerEvents = "none", this._domElement.style.zIndex = "1000";
542
+ }
543
+ /** Initializes the DOMPipe, setting up the main DOM element and adding it to the document body. */
544
+ init() {
545
+ this._canvasObserver = new q({
546
+ domElement: this._domElement,
547
+ renderer: this._renderer
548
+ });
549
+ }
550
+ /**
551
+ * Adds a renderable DOM container to the list of attached elements.
552
+ * @param domContainer - The DOM container to be added.
553
+ * @param _instructionSet - The instruction set (unused).
554
+ */
555
+ addRenderable(e, t) {
556
+ this._attachedDomElements.includes(e) || this._attachedDomElements.push(e);
557
+ }
558
+ /**
559
+ * Updates a renderable DOM container.
560
+ * @param _domContainer - The DOM container to be updated (unused).
561
+ */
562
+ updateRenderable(e) {
563
+ }
564
+ /**
565
+ * Validates a renderable DOM container.
566
+ * @param _domContainer - The DOM container to be validated (unused).
567
+ * @returns Always returns true as validation is not required.
568
+ */
569
+ validateRenderable(e) {
570
+ return !0;
571
+ }
572
+ /** Handles the post-rendering process, ensuring DOM elements are correctly positioned and visible. */
573
+ postrender() {
574
+ const e = this._attachedDomElements;
575
+ if (e.length === 0) {
576
+ this._domElement.remove();
577
+ return;
578
+ }
579
+ this._canvasObserver.ensureAttached();
580
+ for (let t = 0; t < e.length; t++) {
581
+ const i = e[t], n = i.element;
582
+ if (!i.parent || i.globalDisplayStatus < 7)
583
+ n == null || n.remove(), e.splice(t, 1), t--;
584
+ else {
585
+ this._domElement.contains(n) || (n.style.position = "absolute", n.style.pointerEvents = "auto", this._domElement.appendChild(n));
586
+ const s = i.worldTransform, o = i._anchor, r = i.width * o.x, l = i.height * o.y;
587
+ n.style.transformOrigin = `${r}px ${l}px`, n.style.transform = `matrix(${s.a}, ${s.b}, ${s.c}, ${s.d}, ${s.tx - r}, ${s.ty - l})`, n.style.opacity = i.groupAlpha.toString();
588
+ }
589
+ }
590
+ }
591
+ /** Destroys the DOMPipe, removing all attached DOM elements and cleaning up resources. */
592
+ destroy() {
593
+ var e;
594
+ this._renderer.runners.postrender.remove(this);
595
+ for (let t = 0; t < this._attachedDomElements.length; t++)
596
+ (e = this._attachedDomElements[t].element) == null || e.remove();
597
+ this._attachedDomElements.length = 0, this._domElement.remove(), this._canvasObserver.destroy(), this._renderer = null;
598
+ }
599
+ }
600
+ Q.extension = {
601
+ type: [
602
+ b.WebGLPipes,
603
+ b.WebGPUPipes,
604
+ b.CanvasPipes
605
+ ],
606
+ name: "dom"
607
+ };
608
+ class ve {
609
+ constructor() {
610
+ this.interactionFrequency = 10, this._deltaTime = 0, this._didMove = !1, this._tickerAdded = !1, this._pauseUpdate = !0;
611
+ }
612
+ /**
613
+ * Initializes the event ticker.
614
+ * @param events - The event system.
615
+ */
616
+ init(e) {
617
+ this.removeTickerListener(), this.events = e, this.interactionFrequency = 10, this._deltaTime = 0, this._didMove = !1, this._tickerAdded = !1, this._pauseUpdate = !0;
618
+ }
619
+ /** Whether to pause the update checks or not. */
620
+ get pauseUpdate() {
621
+ return this._pauseUpdate;
622
+ }
623
+ set pauseUpdate(e) {
624
+ this._pauseUpdate = e;
625
+ }
626
+ /** Adds the ticker listener. */
627
+ addTickerListener() {
628
+ this._tickerAdded || !this.domElement || (A.system.add(this._tickerUpdate, this, Z.INTERACTION), this._tickerAdded = !0);
629
+ }
630
+ /** Removes the ticker listener. */
631
+ removeTickerListener() {
632
+ this._tickerAdded && (A.system.remove(this._tickerUpdate, this), this._tickerAdded = !1);
633
+ }
634
+ /** Sets flag to not fire extra events when the user has already moved there mouse */
635
+ pointerMoved() {
636
+ this._didMove = !0;
637
+ }
638
+ /** Updates the state of interactive objects. */
639
+ _update() {
640
+ if (!this.domElement || this._pauseUpdate)
641
+ return;
642
+ if (this._didMove) {
643
+ this._didMove = !1;
644
+ return;
645
+ }
646
+ const e = this.events._rootPointerEvent;
647
+ this.events.supportsTouchEvents && e.pointerType === "touch" || globalThis.document.dispatchEvent(this.events.supportsPointerEvents ? new PointerEvent("pointermove", {
648
+ clientX: e.clientX,
649
+ clientY: e.clientY,
650
+ pointerType: e.pointerType,
651
+ pointerId: e.pointerId
652
+ }) : new MouseEvent("mousemove", {
653
+ clientX: e.clientX,
654
+ clientY: e.clientY
655
+ }));
656
+ }
657
+ /**
658
+ * Updates the state of interactive objects if at least {@link interactionFrequency}
659
+ * milliseconds have passed since the last invocation.
660
+ *
661
+ * Invoked by a throttled ticker update from {@link Ticker.system}.
662
+ * @param ticker - The throttled ticker.
663
+ */
664
+ _tickerUpdate(e) {
665
+ this._deltaTime += e.deltaTime, !(this._deltaTime < this.interactionFrequency) && (this._deltaTime = 0, this._update());
666
+ }
667
+ /** Destroys the event ticker. */
668
+ destroy() {
669
+ this.removeTickerListener(), this.events = null, this.domElement = null, this._deltaTime = 0, this._didMove = !1, this._tickerAdded = !1, this._pauseUpdate = !0;
670
+ }
671
+ }
672
+ const _ = new ve();
673
+ class D extends M {
674
+ constructor() {
675
+ super(...arguments), this.client = new g(), this.movement = new g(), this.offset = new g(), this.global = new g(), this.screen = new g();
676
+ }
677
+ /** @readonly */
678
+ get clientX() {
679
+ return this.client.x;
680
+ }
681
+ /** @readonly */
682
+ get clientY() {
683
+ return this.client.y;
684
+ }
685
+ /**
686
+ * Alias for {@link FederatedMouseEvent.clientX this.clientX}.
687
+ * @readonly
688
+ */
689
+ get x() {
690
+ return this.clientX;
691
+ }
692
+ /**
693
+ * Alias for {@link FederatedMouseEvent.clientY this.clientY}.
694
+ * @readonly
695
+ */
696
+ get y() {
697
+ return this.clientY;
698
+ }
699
+ /** @readonly */
700
+ get movementX() {
701
+ return this.movement.x;
702
+ }
703
+ /** @readonly */
704
+ get movementY() {
705
+ return this.movement.y;
706
+ }
707
+ /** @readonly */
708
+ get offsetX() {
709
+ return this.offset.x;
710
+ }
711
+ /** @readonly */
712
+ get offsetY() {
713
+ return this.offset.y;
714
+ }
715
+ /** @readonly */
716
+ get globalX() {
717
+ return this.global.x;
718
+ }
719
+ /** @readonly */
720
+ get globalY() {
721
+ return this.global.y;
722
+ }
723
+ /**
724
+ * The pointer coordinates in the renderer's screen. Alias for `screen.x`.
725
+ * @readonly
726
+ */
727
+ get screenX() {
728
+ return this.screen.x;
729
+ }
730
+ /**
731
+ * The pointer coordinates in the renderer's screen. Alias for `screen.y`.
732
+ * @readonly
733
+ */
734
+ get screenY() {
735
+ return this.screen.y;
736
+ }
737
+ /**
738
+ * Converts global coordinates into container-local coordinates.
739
+ *
740
+ * This method transforms coordinates from world space to a container's local space,
741
+ * useful for precise positioning and hit testing.
742
+ * @param container - The Container to get local coordinates for
743
+ * @param point - Optional Point object to store the result. If not provided, a new Point will be created
744
+ * @param globalPos - Optional custom global coordinates. If not provided, the event's global position is used
745
+ * @returns The local coordinates as a Point object
746
+ * @example
747
+ * ```ts
748
+ * // Basic usage - get local coordinates relative to a container
749
+ * sprite.on('pointermove', (event: FederatedMouseEvent) => {
750
+ * // Get position relative to the sprite
751
+ * const localPos = event.getLocalPosition(sprite);
752
+ * console.log('Local position:', localPos.x, localPos.y);
753
+ * });
754
+ * // Using custom global coordinates
755
+ * const customGlobal = new Point(100, 100);
756
+ * sprite.on('pointermove', (event: FederatedMouseEvent) => {
757
+ * // Transform custom coordinates
758
+ * const localPos = event.getLocalPosition(sprite, undefined, customGlobal);
759
+ * console.log('Custom local position:', localPos.x, localPos.y);
760
+ * });
761
+ * ```
762
+ * @see {@link Container.worldTransform} For the transformation matrix
763
+ * @see {@link Point} For the point class used to store coordinates
764
+ */
765
+ getLocalPosition(e, t, i) {
766
+ return e.worldTransform.applyInverse(i || this.global, t);
767
+ }
768
+ /**
769
+ * Whether the modifier key was pressed when this event natively occurred.
770
+ * @param key - The modifier key.
771
+ */
772
+ getModifierState(e) {
773
+ return "getModifierState" in this.nativeEvent && this.nativeEvent.getModifierState(e);
774
+ }
775
+ /**
776
+ * Not supported.
777
+ * @param _typeArg
778
+ * @param _canBubbleArg
779
+ * @param _cancelableArg
780
+ * @param _viewArg
781
+ * @param _detailArg
782
+ * @param _screenXArg
783
+ * @param _screenYArg
784
+ * @param _clientXArg
785
+ * @param _clientYArg
786
+ * @param _ctrlKeyArg
787
+ * @param _altKeyArg
788
+ * @param _shiftKeyArg
789
+ * @param _metaKeyArg
790
+ * @param _buttonArg
791
+ * @param _relatedTargetArg
792
+ * @deprecated since 7.0.0
793
+ * @ignore
794
+ */
795
+ // eslint-disable-next-line max-params
796
+ initMouseEvent(e, t, i, n, s, o, r, l, d, p, u, h, v, c, Ee) {
797
+ throw new Error("Method not implemented.");
798
+ }
799
+ }
800
+ class f extends D {
801
+ constructor() {
802
+ super(...arguments), this.width = 0, this.height = 0, this.isPrimary = !1;
803
+ }
804
+ /**
805
+ * Only included for completeness for now
806
+ * @ignore
807
+ */
808
+ getCoalescedEvents() {
809
+ return this.type === "pointermove" || this.type === "mousemove" || this.type === "touchmove" ? [this] : [];
810
+ }
811
+ /**
812
+ * Only included for completeness for now
813
+ * @ignore
814
+ */
815
+ getPredictedEvents() {
816
+ throw new Error("getPredictedEvents is not supported!");
817
+ }
818
+ }
819
+ class T extends D {
820
+ constructor() {
821
+ super(...arguments), this.DOM_DELTA_PIXEL = 0, this.DOM_DELTA_LINE = 1, this.DOM_DELTA_PAGE = 2;
822
+ }
823
+ }
824
+ T.DOM_DELTA_PIXEL = 0;
825
+ T.DOM_DELTA_LINE = 1;
826
+ T.DOM_DELTA_PAGE = 2;
827
+ const fe = 2048, me = new g(), P = new g();
828
+ class _e {
829
+ /**
830
+ * @param rootTarget - The holder of the event boundary.
831
+ */
832
+ constructor(e) {
833
+ this.dispatch = new ie(), this.moveOnAll = !1, this.enableGlobalMoveEvents = !0, this.mappingState = {
834
+ trackingData: {}
835
+ }, this.eventPool = /* @__PURE__ */ new Map(), this._allInteractiveElements = [], this._hitElements = [], this._isPointerMoveEvent = !1, this.rootTarget = e, this.hitPruneFn = this.hitPruneFn.bind(this), this.hitTestFn = this.hitTestFn.bind(this), this.mapPointerDown = this.mapPointerDown.bind(this), this.mapPointerMove = this.mapPointerMove.bind(this), this.mapPointerOut = this.mapPointerOut.bind(this), this.mapPointerOver = this.mapPointerOver.bind(this), this.mapPointerUp = this.mapPointerUp.bind(this), this.mapPointerUpOutside = this.mapPointerUpOutside.bind(this), this.mapWheel = this.mapWheel.bind(this), this.mappingTable = {}, this.addEventMapping("pointerdown", this.mapPointerDown), this.addEventMapping("pointermove", this.mapPointerMove), this.addEventMapping("pointerout", this.mapPointerOut), this.addEventMapping("pointerleave", this.mapPointerOut), this.addEventMapping("pointerover", this.mapPointerOver), this.addEventMapping("pointerup", this.mapPointerUp), this.addEventMapping("pointerupoutside", this.mapPointerUpOutside), this.addEventMapping("wheel", this.mapWheel);
836
+ }
837
+ /**
838
+ * Adds an event mapping for the event `type` handled by `fn`.
839
+ *
840
+ * Event mappings can be used to implement additional or custom events. They take an event
841
+ * coming from the upstream scene (or directly from the {@link EventSystem}) and dispatch new downstream events
842
+ * generally trickling down and bubbling up to {@link EventBoundary.rootTarget this.rootTarget}.
843
+ *
844
+ * To modify the semantics of existing events, the built-in mapping methods of EventBoundary should be overridden
845
+ * instead.
846
+ * @param type - The type of upstream event to map.
847
+ * @param fn - The mapping method. The context of this function must be bound manually, if desired.
848
+ */
849
+ addEventMapping(e, t) {
850
+ this.mappingTable[e] || (this.mappingTable[e] = []), this.mappingTable[e].push({
851
+ fn: t,
852
+ priority: 0
853
+ }), this.mappingTable[e].sort((i, n) => i.priority - n.priority);
854
+ }
855
+ /**
856
+ * Dispatches the given event
857
+ * @param e - The event to dispatch.
858
+ * @param type - The type of event to dispatch. Defaults to `e.type`.
859
+ */
860
+ dispatchEvent(e, t) {
861
+ e.propagationStopped = !1, e.propagationImmediatelyStopped = !1, this.propagate(e, t), this.dispatch.emit(t || e.type, e);
862
+ }
863
+ /**
864
+ * Maps the given upstream event through the event boundary and propagates it downstream.
865
+ * @param e - The event to map.
866
+ */
867
+ mapEvent(e) {
868
+ if (!this.rootTarget)
869
+ return;
870
+ const t = this.mappingTable[e.type];
871
+ if (t)
872
+ for (let i = 0, n = t.length; i < n; i++)
873
+ t[i].fn(e);
874
+ else
875
+ y(`[EventBoundary]: Event mapping not defined for ${e.type}`);
876
+ }
877
+ /**
878
+ * Finds the Container that is the target of a event at the given coordinates.
879
+ *
880
+ * The passed (x,y) coordinates are in the world space above this event boundary.
881
+ * @param x - The x coordinate of the event.
882
+ * @param y - The y coordinate of the event.
883
+ */
884
+ hitTest(e, t) {
885
+ _.pauseUpdate = !0;
886
+ const n = this._isPointerMoveEvent && this.enableGlobalMoveEvents ? "hitTestMoveRecursive" : "hitTestRecursive", s = this[n](
887
+ this.rootTarget,
888
+ this.rootTarget.eventMode,
889
+ me.set(e, t),
890
+ this.hitTestFn,
891
+ this.hitPruneFn
892
+ );
893
+ return s && s[0];
894
+ }
895
+ /**
896
+ * Propagate the passed event from from {@link EventBoundary.rootTarget this.rootTarget} to its
897
+ * target `e.target`.
898
+ * @param e - The event to propagate.
899
+ * @param type - The type of event to propagate. Defaults to `e.type`.
900
+ */
901
+ propagate(e, t) {
902
+ if (!e.target)
903
+ return;
904
+ const i = e.composedPath();
905
+ e.eventPhase = e.CAPTURING_PHASE;
906
+ for (let n = 0, s = i.length - 1; n < s; n++)
907
+ if (e.currentTarget = i[n], this.notifyTarget(e, t), e.propagationStopped || e.propagationImmediatelyStopped)
908
+ return;
909
+ if (e.eventPhase = e.AT_TARGET, e.currentTarget = e.target, this.notifyTarget(e, t), !(e.propagationStopped || e.propagationImmediatelyStopped)) {
910
+ e.eventPhase = e.BUBBLING_PHASE;
911
+ for (let n = i.length - 2; n >= 0; n--)
912
+ if (e.currentTarget = i[n], this.notifyTarget(e, t), e.propagationStopped || e.propagationImmediatelyStopped)
913
+ return;
914
+ }
915
+ }
916
+ /**
917
+ * Emits the event `e` to all interactive containers. The event is propagated in the bubbling phase always.
918
+ *
919
+ * This is used in the `globalpointermove` event.
920
+ * @param e - The emitted event.
921
+ * @param type - The listeners to notify.
922
+ * @param targets - The targets to notify.
923
+ */
924
+ all(e, t, i = this._allInteractiveElements) {
925
+ if (i.length === 0)
926
+ return;
927
+ e.eventPhase = e.BUBBLING_PHASE;
928
+ const n = Array.isArray(t) ? t : [t];
929
+ for (let s = i.length - 1; s >= 0; s--)
930
+ n.forEach((o) => {
931
+ e.currentTarget = i[s], this.notifyTarget(e, o);
932
+ });
933
+ }
934
+ /**
935
+ * Finds the propagation path from {@link EventBoundary.rootTarget rootTarget} to the passed
936
+ * `target`. The last element in the path is `target`.
937
+ * @param target - The target to find the propagation path to.
938
+ */
939
+ propagationPath(e) {
940
+ const t = [e];
941
+ for (let i = 0; i < fe && e !== this.rootTarget && e.parent; i++) {
942
+ if (!e.parent)
943
+ throw new Error("Cannot find propagation path to disconnected target");
944
+ t.push(e.parent), e = e.parent;
945
+ }
946
+ return t.reverse(), t;
947
+ }
948
+ hitTestMoveRecursive(e, t, i, n, s, o = !1) {
949
+ let r = !1;
950
+ if (this._interactivePrune(e))
951
+ return null;
952
+ if ((e.eventMode === "dynamic" || t === "dynamic") && (_.pauseUpdate = !1), e.interactiveChildren && e.children) {
953
+ const p = e.children;
954
+ for (let u = p.length - 1; u >= 0; u--) {
955
+ const h = p[u], v = this.hitTestMoveRecursive(
956
+ h,
957
+ this._isInteractive(t) ? t : h.eventMode,
958
+ i,
959
+ n,
960
+ s,
961
+ o || s(e, i)
962
+ );
963
+ if (v) {
964
+ if (v.length > 0 && !v[v.length - 1].parent)
965
+ continue;
966
+ const c = e.isInteractive();
967
+ (v.length > 0 || c) && (c && this._allInteractiveElements.push(e), v.push(e)), this._hitElements.length === 0 && (this._hitElements = v), r = !0;
968
+ }
969
+ }
970
+ }
971
+ const l = this._isInteractive(t), d = e.isInteractive();
972
+ return d && d && this._allInteractiveElements.push(e), o || this._hitElements.length > 0 ? null : r ? this._hitElements : l && !s(e, i) && n(e, i) ? d ? [e] : [] : null;
973
+ }
974
+ /**
975
+ * Recursive implementation for {@link EventBoundary.hitTest hitTest}.
976
+ * @param currentTarget - The Container that is to be hit tested.
977
+ * @param eventMode - The event mode for the `currentTarget` or one of its parents.
978
+ * @param location - The location that is being tested for overlap.
979
+ * @param testFn - Callback that determines whether the target passes hit testing. This callback
980
+ * can assume that `pruneFn` failed to prune the container.
981
+ * @param pruneFn - Callback that determiness whether the target and all of its children
982
+ * cannot pass the hit test. It is used as a preliminary optimization to prune entire subtrees
983
+ * of the scene graph.
984
+ * @returns An array holding the hit testing target and all its ancestors in order. The first element
985
+ * is the target itself and the last is {@link EventBoundary.rootTarget rootTarget}. This is the opposite
986
+ * order w.r.t. the propagation path. If no hit testing target is found, null is returned.
987
+ */
988
+ hitTestRecursive(e, t, i, n, s) {
989
+ if (this._interactivePrune(e) || s(e, i))
990
+ return null;
991
+ if ((e.eventMode === "dynamic" || t === "dynamic") && (_.pauseUpdate = !1), e.interactiveChildren && e.children) {
992
+ const l = e.children, d = i;
993
+ for (let p = l.length - 1; p >= 0; p--) {
994
+ const u = l[p], h = this.hitTestRecursive(
995
+ u,
996
+ this._isInteractive(t) ? t : u.eventMode,
997
+ d,
998
+ n,
999
+ s
1000
+ );
1001
+ if (h) {
1002
+ if (h.length > 0 && !h[h.length - 1].parent)
1003
+ continue;
1004
+ const v = e.isInteractive();
1005
+ return (h.length > 0 || v) && h.push(e), h;
1006
+ }
1007
+ }
1008
+ }
1009
+ const o = this._isInteractive(t), r = e.isInteractive();
1010
+ return o && n(e, i) ? r ? [e] : [] : null;
1011
+ }
1012
+ _isInteractive(e) {
1013
+ return e === "static" || e === "dynamic";
1014
+ }
1015
+ _interactivePrune(e) {
1016
+ return !e || !e.visible || !e.renderable || !e.measurable || e.eventMode === "none" || e.eventMode === "passive" && !e.interactiveChildren;
1017
+ }
1018
+ /**
1019
+ * Checks whether the container or any of its children cannot pass the hit test at all.
1020
+ *
1021
+ * {@link EventBoundary}'s implementation uses the {@link Container.hitArea hitArea}
1022
+ * and {@link Container._maskEffect} for pruning.
1023
+ * @param container - The container to prune.
1024
+ * @param location - The location to test for overlap.
1025
+ */
1026
+ hitPruneFn(e, t) {
1027
+ if (e.hitArea && (e.worldTransform.applyInverse(t, P), !e.hitArea.contains(P.x, P.y)))
1028
+ return !0;
1029
+ if (e.effects && e.effects.length)
1030
+ for (let i = 0; i < e.effects.length; i++) {
1031
+ const n = e.effects[i];
1032
+ if (n.containsPoint && !n.containsPoint(t, this.hitTestFn))
1033
+ return !0;
1034
+ }
1035
+ return !1;
1036
+ }
1037
+ /**
1038
+ * Checks whether the container passes hit testing for the given location.
1039
+ * @param container - The container to test.
1040
+ * @param location - The location to test for overlap.
1041
+ * @returns - Whether `container` passes hit testing for `location`.
1042
+ */
1043
+ hitTestFn(e, t) {
1044
+ return e.hitArea ? !0 : e != null && e.containsPoint ? (e.worldTransform.applyInverse(t, P), e.containsPoint(P)) : !1;
1045
+ }
1046
+ /**
1047
+ * Notify all the listeners to the event's `currentTarget`.
1048
+ *
1049
+ * If the `currentTarget` contains the property `on<type>`, then it is called here,
1050
+ * simulating the behavior from version 6.x and prior.
1051
+ * @param e - The event passed to the target.
1052
+ * @param type - The type of event to notify. Defaults to `e.type`.
1053
+ */
1054
+ notifyTarget(e, t) {
1055
+ var s, o;
1056
+ if (!e.currentTarget.isInteractive())
1057
+ return;
1058
+ t ?? (t = e.type);
1059
+ const i = `on${t}`;
1060
+ (o = (s = e.currentTarget)[i]) == null || o.call(s, e);
1061
+ const n = e.eventPhase === e.CAPTURING_PHASE || e.eventPhase === e.AT_TARGET ? `${t}capture` : t;
1062
+ this._notifyListeners(e, n), e.eventPhase === e.AT_TARGET && this._notifyListeners(e, t);
1063
+ }
1064
+ /**
1065
+ * Maps the upstream `pointerdown` events to a downstream `pointerdown` event.
1066
+ *
1067
+ * `touchstart`, `rightdown`, `mousedown` events are also dispatched for specific pointer types.
1068
+ * @param from - The upstream `pointerdown` event.
1069
+ */
1070
+ mapPointerDown(e) {
1071
+ if (!(e instanceof f)) {
1072
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1073
+ return;
1074
+ }
1075
+ const t = this.createPointerEvent(e);
1076
+ if (this.dispatchEvent(t, "pointerdown"), t.pointerType === "touch")
1077
+ this.dispatchEvent(t, "touchstart");
1078
+ else if (t.pointerType === "mouse" || t.pointerType === "pen") {
1079
+ const n = t.button === 2;
1080
+ this.dispatchEvent(t, n ? "rightdown" : "mousedown");
1081
+ }
1082
+ const i = this.trackingData(e.pointerId);
1083
+ i.pressTargetsByButton[e.button] = t.composedPath(), this.freeEvent(t);
1084
+ }
1085
+ /**
1086
+ * Maps the upstream `pointermove` to downstream `pointerout`, `pointerover`, and `pointermove` events, in that order.
1087
+ *
1088
+ * The tracking data for the specific pointer has an updated `overTarget`. `mouseout`, `mouseover`,
1089
+ * `mousemove`, and `touchmove` events are fired as well for specific pointer types.
1090
+ * @param from - The upstream `pointermove` event.
1091
+ */
1092
+ mapPointerMove(e) {
1093
+ var l, d;
1094
+ if (!(e instanceof f)) {
1095
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1096
+ return;
1097
+ }
1098
+ this._allInteractiveElements.length = 0, this._hitElements.length = 0, this._isPointerMoveEvent = !0;
1099
+ const t = this.createPointerEvent(e);
1100
+ this._isPointerMoveEvent = !1;
1101
+ const i = t.pointerType === "mouse" || t.pointerType === "pen", n = this.trackingData(e.pointerId), s = this.findMountedTarget(n.overTargets);
1102
+ if (((l = n.overTargets) == null ? void 0 : l.length) > 0 && s !== t.target) {
1103
+ const p = e.type === "mousemove" ? "mouseout" : "pointerout", u = this.createPointerEvent(e, p, s);
1104
+ if (this.dispatchEvent(u, "pointerout"), i && this.dispatchEvent(u, "mouseout"), !t.composedPath().includes(s)) {
1105
+ const h = this.createPointerEvent(e, "pointerleave", s);
1106
+ for (h.eventPhase = h.AT_TARGET; h.target && !t.composedPath().includes(h.target); )
1107
+ h.currentTarget = h.target, this.notifyTarget(h), i && this.notifyTarget(h, "mouseleave"), h.target = h.target.parent;
1108
+ this.freeEvent(h);
1109
+ }
1110
+ this.freeEvent(u);
1111
+ }
1112
+ if (s !== t.target) {
1113
+ const p = e.type === "mousemove" ? "mouseover" : "pointerover", u = this.clonePointerEvent(t, p);
1114
+ this.dispatchEvent(u, "pointerover"), i && this.dispatchEvent(u, "mouseover");
1115
+ let h = s == null ? void 0 : s.parent;
1116
+ for (; h && h !== this.rootTarget.parent && h !== t.target; )
1117
+ h = h.parent;
1118
+ if (!h || h === this.rootTarget.parent) {
1119
+ const c = this.clonePointerEvent(t, "pointerenter");
1120
+ for (c.eventPhase = c.AT_TARGET; c.target && c.target !== s && c.target !== this.rootTarget.parent; )
1121
+ c.currentTarget = c.target, this.notifyTarget(c), i && this.notifyTarget(c, "mouseenter"), c.target = c.target.parent;
1122
+ this.freeEvent(c);
1123
+ }
1124
+ this.freeEvent(u);
1125
+ }
1126
+ const o = [], r = this.enableGlobalMoveEvents ?? !0;
1127
+ this.moveOnAll ? o.push("pointermove") : this.dispatchEvent(t, "pointermove"), r && o.push("globalpointermove"), t.pointerType === "touch" && (this.moveOnAll ? o.splice(1, 0, "touchmove") : this.dispatchEvent(t, "touchmove"), r && o.push("globaltouchmove")), i && (this.moveOnAll ? o.splice(1, 0, "mousemove") : this.dispatchEvent(t, "mousemove"), r && o.push("globalmousemove"), this.cursor = (d = t.target) == null ? void 0 : d.cursor), o.length > 0 && this.all(t, o), this._allInteractiveElements.length = 0, this._hitElements.length = 0, n.overTargets = t.composedPath(), this.freeEvent(t);
1128
+ }
1129
+ /**
1130
+ * Maps the upstream `pointerover` to downstream `pointerover` and `pointerenter` events, in that order.
1131
+ *
1132
+ * The tracking data for the specific pointer gets a new `overTarget`.
1133
+ * @param from - The upstream `pointerover` event.
1134
+ */
1135
+ mapPointerOver(e) {
1136
+ var o;
1137
+ if (!(e instanceof f)) {
1138
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1139
+ return;
1140
+ }
1141
+ const t = this.trackingData(e.pointerId), i = this.createPointerEvent(e), n = i.pointerType === "mouse" || i.pointerType === "pen";
1142
+ this.dispatchEvent(i, "pointerover"), n && this.dispatchEvent(i, "mouseover"), i.pointerType === "mouse" && (this.cursor = (o = i.target) == null ? void 0 : o.cursor);
1143
+ const s = this.clonePointerEvent(i, "pointerenter");
1144
+ for (s.eventPhase = s.AT_TARGET; s.target && s.target !== this.rootTarget.parent; )
1145
+ s.currentTarget = s.target, this.notifyTarget(s), n && this.notifyTarget(s, "mouseenter"), s.target = s.target.parent;
1146
+ t.overTargets = i.composedPath(), this.freeEvent(i), this.freeEvent(s);
1147
+ }
1148
+ /**
1149
+ * Maps the upstream `pointerout` to downstream `pointerout`, `pointerleave` events, in that order.
1150
+ *
1151
+ * The tracking data for the specific pointer is cleared of a `overTarget`.
1152
+ * @param from - The upstream `pointerout` event.
1153
+ */
1154
+ mapPointerOut(e) {
1155
+ if (!(e instanceof f)) {
1156
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1157
+ return;
1158
+ }
1159
+ const t = this.trackingData(e.pointerId);
1160
+ if (t.overTargets) {
1161
+ const i = e.pointerType === "mouse" || e.pointerType === "pen", n = this.findMountedTarget(t.overTargets), s = this.createPointerEvent(e, "pointerout", n);
1162
+ this.dispatchEvent(s), i && this.dispatchEvent(s, "mouseout");
1163
+ const o = this.createPointerEvent(e, "pointerleave", n);
1164
+ for (o.eventPhase = o.AT_TARGET; o.target && o.target !== this.rootTarget.parent; )
1165
+ o.currentTarget = o.target, this.notifyTarget(o), i && this.notifyTarget(o, "mouseleave"), o.target = o.target.parent;
1166
+ t.overTargets = null, this.freeEvent(s), this.freeEvent(o);
1167
+ }
1168
+ this.cursor = null;
1169
+ }
1170
+ /**
1171
+ * Maps the upstream `pointerup` event to downstream `pointerup`, `pointerupoutside`,
1172
+ * and `click`/`rightclick`/`pointertap` events, in that order.
1173
+ *
1174
+ * The `pointerupoutside` event bubbles from the original `pointerdown` target to the most specific
1175
+ * ancestor of the `pointerdown` and `pointerup` targets, which is also the `click` event's target. `touchend`,
1176
+ * `rightup`, `mouseup`, `touchendoutside`, `rightupoutside`, `mouseupoutside`, and `tap` are fired as well for
1177
+ * specific pointer types.
1178
+ * @param from - The upstream `pointerup` event.
1179
+ */
1180
+ mapPointerUp(e) {
1181
+ if (!(e instanceof f)) {
1182
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1183
+ return;
1184
+ }
1185
+ const t = performance.now(), i = this.createPointerEvent(e);
1186
+ if (this.dispatchEvent(i, "pointerup"), i.pointerType === "touch")
1187
+ this.dispatchEvent(i, "touchend");
1188
+ else if (i.pointerType === "mouse" || i.pointerType === "pen") {
1189
+ const r = i.button === 2;
1190
+ this.dispatchEvent(i, r ? "rightup" : "mouseup");
1191
+ }
1192
+ const n = this.trackingData(e.pointerId), s = this.findMountedTarget(n.pressTargetsByButton[e.button]);
1193
+ let o = s;
1194
+ if (s && !i.composedPath().includes(s)) {
1195
+ let r = s;
1196
+ for (; r && !i.composedPath().includes(r); ) {
1197
+ if (i.currentTarget = r, this.notifyTarget(i, "pointerupoutside"), i.pointerType === "touch")
1198
+ this.notifyTarget(i, "touchendoutside");
1199
+ else if (i.pointerType === "mouse" || i.pointerType === "pen") {
1200
+ const l = i.button === 2;
1201
+ this.notifyTarget(i, l ? "rightupoutside" : "mouseupoutside");
1202
+ }
1203
+ r = r.parent;
1204
+ }
1205
+ delete n.pressTargetsByButton[e.button], o = r;
1206
+ }
1207
+ if (o) {
1208
+ const r = this.clonePointerEvent(i, "click");
1209
+ r.target = o, r.path = null, n.clicksByButton[e.button] || (n.clicksByButton[e.button] = {
1210
+ clickCount: 0,
1211
+ target: r.target,
1212
+ timeStamp: t
1213
+ });
1214
+ const l = n.clicksByButton[e.button];
1215
+ if (l.target === r.target && t - l.timeStamp < 200 ? ++l.clickCount : l.clickCount = 1, l.target = r.target, l.timeStamp = t, r.detail = l.clickCount, r.pointerType === "mouse") {
1216
+ const d = r.button === 2;
1217
+ this.dispatchEvent(r, d ? "rightclick" : "click");
1218
+ } else r.pointerType === "touch" && this.dispatchEvent(r, "tap");
1219
+ this.dispatchEvent(r, "pointertap"), this.freeEvent(r);
1220
+ }
1221
+ this.freeEvent(i);
1222
+ }
1223
+ /**
1224
+ * Maps the upstream `pointerupoutside` event to a downstream `pointerupoutside` event, bubbling from the original
1225
+ * `pointerdown` target to `rootTarget`.
1226
+ *
1227
+ * (The most specific ancestor of the `pointerdown` event and the `pointerup` event must the
1228
+ * `{@link EventBoundary}'s root because the `pointerup` event occurred outside of the boundary.)
1229
+ *
1230
+ * `touchendoutside`, `mouseupoutside`, and `rightupoutside` events are fired as well for specific pointer
1231
+ * types. The tracking data for the specific pointer is cleared of a `pressTarget`.
1232
+ * @param from - The upstream `pointerupoutside` event.
1233
+ */
1234
+ mapPointerUpOutside(e) {
1235
+ if (!(e instanceof f)) {
1236
+ y("EventBoundary cannot map a non-pointer event as a pointer event");
1237
+ return;
1238
+ }
1239
+ const t = this.trackingData(e.pointerId), i = this.findMountedTarget(t.pressTargetsByButton[e.button]), n = this.createPointerEvent(e);
1240
+ if (i) {
1241
+ let s = i;
1242
+ for (; s; )
1243
+ n.currentTarget = s, this.notifyTarget(n, "pointerupoutside"), n.pointerType === "touch" ? this.notifyTarget(n, "touchendoutside") : (n.pointerType === "mouse" || n.pointerType === "pen") && this.notifyTarget(n, n.button === 2 ? "rightupoutside" : "mouseupoutside"), s = s.parent;
1244
+ delete t.pressTargetsByButton[e.button];
1245
+ }
1246
+ this.freeEvent(n);
1247
+ }
1248
+ /**
1249
+ * Maps the upstream `wheel` event to a downstream `wheel` event.
1250
+ * @param from - The upstream `wheel` event.
1251
+ */
1252
+ mapWheel(e) {
1253
+ if (!(e instanceof T)) {
1254
+ y("EventBoundary cannot map a non-wheel event as a wheel event");
1255
+ return;
1256
+ }
1257
+ const t = this.createWheelEvent(e);
1258
+ this.dispatchEvent(t), this.freeEvent(t);
1259
+ }
1260
+ /**
1261
+ * Finds the most specific event-target in the given propagation path that is still mounted in the scene graph.
1262
+ *
1263
+ * This is used to find the correct `pointerup` and `pointerout` target in the case that the original `pointerdown`
1264
+ * or `pointerover` target was unmounted from the scene graph.
1265
+ * @param propagationPath - The propagation path was valid in the past.
1266
+ * @returns - The most specific event-target still mounted at the same location in the scene graph.
1267
+ */
1268
+ findMountedTarget(e) {
1269
+ if (!e)
1270
+ return null;
1271
+ let t = e[0];
1272
+ for (let i = 1; i < e.length && e[i].parent === t; i++)
1273
+ t = e[i];
1274
+ return t;
1275
+ }
1276
+ /**
1277
+ * Creates an event whose `originalEvent` is `from`, with an optional `type` and `target` override.
1278
+ *
1279
+ * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
1280
+ * @param from - The `originalEvent` for the returned event.
1281
+ * @param [type=from.type] - The type of the returned event.
1282
+ * @param target - The target of the returned event.
1283
+ */
1284
+ createPointerEvent(e, t, i) {
1285
+ const n = this.allocateEvent(f);
1286
+ return this.copyPointerData(e, n), this.copyMouseData(e, n), this.copyData(e, n), n.nativeEvent = e.nativeEvent, n.originalEvent = e, n.target = i ?? this.hitTest(n.global.x, n.global.y) ?? this._hitElements[0], typeof t == "string" && (n.type = t), n;
1287
+ }
1288
+ /**
1289
+ * Creates a wheel event whose `originalEvent` is `from`.
1290
+ *
1291
+ * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
1292
+ * @param from - The upstream wheel event.
1293
+ */
1294
+ createWheelEvent(e) {
1295
+ const t = this.allocateEvent(T);
1296
+ return this.copyWheelData(e, t), this.copyMouseData(e, t), this.copyData(e, t), t.nativeEvent = e.nativeEvent, t.originalEvent = e, t.target = this.hitTest(t.global.x, t.global.y), t;
1297
+ }
1298
+ /**
1299
+ * Clones the event `from`, with an optional `type` override.
1300
+ *
1301
+ * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
1302
+ * @param from - The event to clone.
1303
+ * @param [type=from.type] - The type of the returned event.
1304
+ */
1305
+ clonePointerEvent(e, t) {
1306
+ const i = this.allocateEvent(f);
1307
+ return i.nativeEvent = e.nativeEvent, i.originalEvent = e.originalEvent, this.copyPointerData(e, i), this.copyMouseData(e, i), this.copyData(e, i), i.target = e.target, i.path = e.composedPath().slice(), i.type = t ?? i.type, i;
1308
+ }
1309
+ /**
1310
+ * Copies wheel {@link FederatedWheelEvent} data from `from` into `to`.
1311
+ *
1312
+ * The following properties are copied:
1313
+ * + deltaMode
1314
+ * + deltaX
1315
+ * + deltaY
1316
+ * + deltaZ
1317
+ * @param from - The event to copy data from.
1318
+ * @param to - The event to copy data into.
1319
+ */
1320
+ copyWheelData(e, t) {
1321
+ t.deltaMode = e.deltaMode, t.deltaX = e.deltaX, t.deltaY = e.deltaY, t.deltaZ = e.deltaZ;
1322
+ }
1323
+ /**
1324
+ * Copies pointer {@link FederatedPointerEvent} data from `from` into `to`.
1325
+ *
1326
+ * The following properties are copied:
1327
+ * + pointerId
1328
+ * + width
1329
+ * + height
1330
+ * + isPrimary
1331
+ * + pointerType
1332
+ * + pressure
1333
+ * + tangentialPressure
1334
+ * + tiltX
1335
+ * + tiltY
1336
+ * @param from - The event to copy data from.
1337
+ * @param to - The event to copy data into.
1338
+ */
1339
+ copyPointerData(e, t) {
1340
+ e instanceof f && t instanceof f && (t.pointerId = e.pointerId, t.width = e.width, t.height = e.height, t.isPrimary = e.isPrimary, t.pointerType = e.pointerType, t.pressure = e.pressure, t.tangentialPressure = e.tangentialPressure, t.tiltX = e.tiltX, t.tiltY = e.tiltY, t.twist = e.twist);
1341
+ }
1342
+ /**
1343
+ * Copies mouse {@link FederatedMouseEvent} data from `from` to `to`.
1344
+ *
1345
+ * The following properties are copied:
1346
+ * + altKey
1347
+ * + button
1348
+ * + buttons
1349
+ * + clientX
1350
+ * + clientY
1351
+ * + metaKey
1352
+ * + movementX
1353
+ * + movementY
1354
+ * + pageX
1355
+ * + pageY
1356
+ * + x
1357
+ * + y
1358
+ * + screen
1359
+ * + shiftKey
1360
+ * + global
1361
+ * @param from - The event to copy data from.
1362
+ * @param to - The event to copy data into.
1363
+ */
1364
+ copyMouseData(e, t) {
1365
+ e instanceof D && t instanceof D && (t.altKey = e.altKey, t.button = e.button, t.buttons = e.buttons, t.client.copyFrom(e.client), t.ctrlKey = e.ctrlKey, t.metaKey = e.metaKey, t.movement.copyFrom(e.movement), t.screen.copyFrom(e.screen), t.shiftKey = e.shiftKey, t.global.copyFrom(e.global));
1366
+ }
1367
+ /**
1368
+ * Copies base {@link FederatedEvent} data from `from` into `to`.
1369
+ *
1370
+ * The following properties are copied:
1371
+ * + isTrusted
1372
+ * + srcElement
1373
+ * + timeStamp
1374
+ * + type
1375
+ * @param from - The event to copy data from.
1376
+ * @param to - The event to copy data into.
1377
+ */
1378
+ copyData(e, t) {
1379
+ t.isTrusted = e.isTrusted, t.srcElement = e.srcElement, t.timeStamp = performance.now(), t.type = e.type, t.detail = e.detail, t.view = e.view, t.which = e.which, t.layer.copyFrom(e.layer), t.page.copyFrom(e.page);
1380
+ }
1381
+ /**
1382
+ * @param id - The pointer ID.
1383
+ * @returns The tracking data stored for the given pointer. If no data exists, a blank
1384
+ * state will be created.
1385
+ */
1386
+ trackingData(e) {
1387
+ return this.mappingState.trackingData[e] || (this.mappingState.trackingData[e] = {
1388
+ pressTargetsByButton: {},
1389
+ clicksByButton: {},
1390
+ overTarget: null
1391
+ }), this.mappingState.trackingData[e];
1392
+ }
1393
+ /**
1394
+ * Allocate a specific type of event from {@link EventBoundary#eventPool this.eventPool}.
1395
+ *
1396
+ * This allocation is constructor-agnostic, as long as it only takes one argument - this event
1397
+ * boundary.
1398
+ * @param constructor - The event's constructor.
1399
+ * @returns An event of the given type.
1400
+ */
1401
+ allocateEvent(e) {
1402
+ this.eventPool.has(e) || this.eventPool.set(e, []);
1403
+ const t = this.eventPool.get(e).pop() || new e(this);
1404
+ return t.eventPhase = t.NONE, t.currentTarget = null, t.defaultPrevented = !1, t.path = null, t.target = null, t;
1405
+ }
1406
+ /**
1407
+ * Frees the event and puts it back into the event pool.
1408
+ *
1409
+ * It is illegal to reuse the event until it is allocated again, using `this.allocateEvent`.
1410
+ *
1411
+ * It is also advised that events not allocated from {@link EventBoundary#allocateEvent this.allocateEvent}
1412
+ * not be freed. This is because of the possibility that the same event is freed twice, which can cause
1413
+ * it to be allocated twice & result in overwriting.
1414
+ * @param event - The event to be freed.
1415
+ * @throws Error if the event is managed by another event boundary.
1416
+ */
1417
+ freeEvent(e) {
1418
+ if (e.manager !== this)
1419
+ throw new Error("It is illegal to free an event not managed by this EventBoundary!");
1420
+ const t = e.constructor;
1421
+ this.eventPool.has(t) || this.eventPool.set(t, []), this.eventPool.get(t).push(e);
1422
+ }
1423
+ /**
1424
+ * Similar to {@link EventEmitter.emit}, except it stops if the `propagationImmediatelyStopped` flag
1425
+ * is set on the event.
1426
+ * @param e - The event to call each listener with.
1427
+ * @param type - The event key.
1428
+ */
1429
+ _notifyListeners(e, t) {
1430
+ const i = e.currentTarget._events[t];
1431
+ if (i)
1432
+ if ("fn" in i)
1433
+ i.once && e.currentTarget.removeListener(t, i.fn, void 0, !0), i.fn.call(i.context, e);
1434
+ else
1435
+ for (let n = 0, s = i.length; n < s && !e.propagationImmediatelyStopped; n++)
1436
+ i[n].once && e.currentTarget.removeListener(t, i[n].fn, void 0, !0), i[n].fn.call(i[n].context, e);
1437
+ }
1438
+ }
1439
+ const ge = 1, ye = {
1440
+ touchstart: "pointerdown",
1441
+ touchend: "pointerup",
1442
+ touchendoutside: "pointerupoutside",
1443
+ touchmove: "pointermove",
1444
+ touchcancel: "pointercancel"
1445
+ }, B = class x {
1446
+ /**
1447
+ * @param {Renderer} renderer
1448
+ */
1449
+ constructor(e) {
1450
+ this.supportsTouchEvents = "ontouchstart" in globalThis, this.supportsPointerEvents = !!globalThis.PointerEvent, this.domElement = null, this.resolution = 1, this.renderer = e, this.rootBoundary = new _e(null), _.init(this), this.autoPreventDefault = !0, this._eventsAdded = !1, this._rootPointerEvent = new f(null), this._rootWheelEvent = new T(null), this.cursorStyles = {
1451
+ default: "inherit",
1452
+ pointer: "pointer"
1453
+ }, this.features = new Proxy({ ...x.defaultEventFeatures }, {
1454
+ set: (t, i, n) => (i === "globalMove" && (this.rootBoundary.enableGlobalMoveEvents = n), t[i] = n, !0)
1455
+ }), this._onPointerDown = this._onPointerDown.bind(this), this._onPointerMove = this._onPointerMove.bind(this), this._onPointerUp = this._onPointerUp.bind(this), this._onPointerOverOut = this._onPointerOverOut.bind(this), this.onWheel = this.onWheel.bind(this);
1456
+ }
1457
+ /**
1458
+ * The default interaction mode for all display objects.
1459
+ * @see Container.eventMode
1460
+ * @type {EventMode}
1461
+ * @readonly
1462
+ * @since 7.2.0
1463
+ */
1464
+ static get defaultEventMode() {
1465
+ return this._defaultEventMode;
1466
+ }
1467
+ /**
1468
+ * Runner init called, view is available at this point.
1469
+ * @ignore
1470
+ */
1471
+ init(e) {
1472
+ const { canvas: t, resolution: i } = this.renderer;
1473
+ this.setTargetElement(t), this.resolution = i, x._defaultEventMode = e.eventMode ?? "passive", Object.assign(this.features, e.eventFeatures ?? {}), this.rootBoundary.enableGlobalMoveEvents = this.features.globalMove;
1474
+ }
1475
+ /**
1476
+ * Handle changing resolution.
1477
+ * @ignore
1478
+ */
1479
+ resolutionChange(e) {
1480
+ this.resolution = e;
1481
+ }
1482
+ /** Destroys all event listeners and detaches the renderer. */
1483
+ destroy() {
1484
+ _.destroy(), this.setTargetElement(null), this.renderer = null, this._currentCursor = null;
1485
+ }
1486
+ /**
1487
+ * Sets the current cursor mode, handling any callbacks or CSS style changes.
1488
+ * The cursor can be a CSS cursor string, a custom callback function, or a key from the cursorStyles dictionary.
1489
+ * @param mode - Cursor mode to set. Can be:
1490
+ * - A CSS cursor string (e.g., 'pointer', 'grab')
1491
+ * - A key from the cursorStyles dictionary
1492
+ * - null/undefined to reset to default
1493
+ * @example
1494
+ * ```ts
1495
+ * // Using predefined cursor styles
1496
+ * app.renderer.events.setCursor('pointer'); // Set standard pointer cursor
1497
+ * app.renderer.events.setCursor('grab'); // Set grab cursor
1498
+ * app.renderer.events.setCursor(null); // Reset to default
1499
+ *
1500
+ * // Using custom cursor styles
1501
+ * app.renderer.events.cursorStyles.custom = 'url("cursor.png"), auto';
1502
+ * app.renderer.events.setCursor('custom'); // Apply custom cursor
1503
+ *
1504
+ * // Using callback-based cursor
1505
+ * app.renderer.events.cursorStyles.dynamic = (mode) => {
1506
+ * document.body.style.cursor = mode === 'hover' ? 'pointer' : 'default';
1507
+ * };
1508
+ * app.renderer.events.setCursor('dynamic'); // Trigger cursor callback
1509
+ * ```
1510
+ * @remarks
1511
+ * - Has no effect on OffscreenCanvas except for callback-based cursors
1512
+ * - Caches current cursor to avoid unnecessary DOM updates
1513
+ * - Supports CSS cursor values, style objects, and callback functions
1514
+ * @see {@link EventSystem.cursorStyles} For defining custom cursor styles
1515
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} MDN Cursor Reference
1516
+ */
1517
+ setCursor(e) {
1518
+ e || (e = "default");
1519
+ let t = !0;
1520
+ if (globalThis.OffscreenCanvas && this.domElement instanceof OffscreenCanvas && (t = !1), this._currentCursor === e)
1521
+ return;
1522
+ this._currentCursor = e;
1523
+ const i = this.cursorStyles[e];
1524
+ if (i)
1525
+ switch (typeof i) {
1526
+ case "string":
1527
+ t && (this.domElement.style.cursor = i);
1528
+ break;
1529
+ case "function":
1530
+ i(e);
1531
+ break;
1532
+ case "object":
1533
+ t && Object.assign(this.domElement.style, i);
1534
+ break;
1535
+ }
1536
+ else t && typeof e == "string" && !Object.prototype.hasOwnProperty.call(this.cursorStyles, e) && (this.domElement.style.cursor = e);
1537
+ }
1538
+ /**
1539
+ * The global pointer event instance containing the most recent pointer state.
1540
+ * This is useful for accessing pointer information without listening to events.
1541
+ * @example
1542
+ * ```ts
1543
+ * // Access current pointer position at any time
1544
+ * const eventSystem = app.renderer.events;
1545
+ * const pointer = eventSystem.pointer;
1546
+ *
1547
+ * // Get global coordinates
1548
+ * console.log('Position:', pointer.global.x, pointer.global.y);
1549
+ *
1550
+ * // Check button state
1551
+ * console.log('Buttons pressed:', pointer.buttons);
1552
+ *
1553
+ * // Get pointer type and pressure
1554
+ * console.log('Type:', pointer.pointerType);
1555
+ * console.log('Pressure:', pointer.pressure);
1556
+ * ```
1557
+ * @readonly
1558
+ * @since 7.2.0
1559
+ * @see {@link FederatedPointerEvent} For all available pointer properties
1560
+ */
1561
+ get pointer() {
1562
+ return this._rootPointerEvent;
1563
+ }
1564
+ /**
1565
+ * Event handler for pointer down events on {@link EventSystem#domElement this.domElement}.
1566
+ * @param nativeEvent - The native mouse/pointer/touch event.
1567
+ */
1568
+ _onPointerDown(e) {
1569
+ if (!this.features.click)
1570
+ return;
1571
+ this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;
1572
+ const t = this._normalizeToPointerData(e);
1573
+ this.autoPreventDefault && t[0].isNormalized && (e.cancelable || !("cancelable" in e)) && e.preventDefault();
1574
+ for (let i = 0, n = t.length; i < n; i++) {
1575
+ const s = t[i], o = this._bootstrapEvent(this._rootPointerEvent, s);
1576
+ this.rootBoundary.mapEvent(o);
1577
+ }
1578
+ this.setCursor(this.rootBoundary.cursor);
1579
+ }
1580
+ /**
1581
+ * Event handler for pointer move events on on {@link EventSystem#domElement this.domElement}.
1582
+ * @param nativeEvent - The native mouse/pointer/touch events.
1583
+ */
1584
+ _onPointerMove(e) {
1585
+ if (!this.features.move)
1586
+ return;
1587
+ this.rootBoundary.rootTarget = this.renderer.lastObjectRendered, _.pointerMoved();
1588
+ const t = this._normalizeToPointerData(e);
1589
+ for (let i = 0, n = t.length; i < n; i++) {
1590
+ const s = this._bootstrapEvent(this._rootPointerEvent, t[i]);
1591
+ this.rootBoundary.mapEvent(s);
1592
+ }
1593
+ this.setCursor(this.rootBoundary.cursor);
1594
+ }
1595
+ /**
1596
+ * Event handler for pointer up events on {@link EventSystem#domElement this.domElement}.
1597
+ * @param nativeEvent - The native mouse/pointer/touch event.
1598
+ */
1599
+ _onPointerUp(e) {
1600
+ if (!this.features.click)
1601
+ return;
1602
+ this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;
1603
+ let t = e.target;
1604
+ e.composedPath && e.composedPath().length > 0 && (t = e.composedPath()[0]);
1605
+ const i = t !== this.domElement ? "outside" : "", n = this._normalizeToPointerData(e);
1606
+ for (let s = 0, o = n.length; s < o; s++) {
1607
+ const r = this._bootstrapEvent(this._rootPointerEvent, n[s]);
1608
+ r.type += i, this.rootBoundary.mapEvent(r);
1609
+ }
1610
+ this.setCursor(this.rootBoundary.cursor);
1611
+ }
1612
+ /**
1613
+ * Event handler for pointer over & out events on {@link EventSystem#domElement this.domElement}.
1614
+ * @param nativeEvent - The native mouse/pointer/touch event.
1615
+ */
1616
+ _onPointerOverOut(e) {
1617
+ if (!this.features.click)
1618
+ return;
1619
+ this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;
1620
+ const t = this._normalizeToPointerData(e);
1621
+ for (let i = 0, n = t.length; i < n; i++) {
1622
+ const s = this._bootstrapEvent(this._rootPointerEvent, t[i]);
1623
+ this.rootBoundary.mapEvent(s);
1624
+ }
1625
+ this.setCursor(this.rootBoundary.cursor);
1626
+ }
1627
+ /**
1628
+ * Passive handler for `wheel` events on {@link EventSystem.domElement this.domElement}.
1629
+ * @param nativeEvent - The native wheel event.
1630
+ */
1631
+ onWheel(e) {
1632
+ if (!this.features.wheel)
1633
+ return;
1634
+ const t = this.normalizeWheelEvent(e);
1635
+ this.rootBoundary.rootTarget = this.renderer.lastObjectRendered, this.rootBoundary.mapEvent(t);
1636
+ }
1637
+ /**
1638
+ * Sets the {@link EventSystem#domElement domElement} and binds event listeners.
1639
+ * This method manages the DOM event bindings for the event system, allowing you to
1640
+ * change or remove the target element that receives input events.
1641
+ * > [!IMPORTANT] This will default to the canvas element of the renderer, so you
1642
+ * > should not need to call this unless you are using a custom element.
1643
+ * @param element - The new DOM element to bind events to, or null to remove all event bindings
1644
+ * @example
1645
+ * ```ts
1646
+ * // Set a new canvas element as the target
1647
+ * const canvas = document.createElement('canvas');
1648
+ * app.renderer.events.setTargetElement(canvas);
1649
+ *
1650
+ * // Remove all event bindings
1651
+ * app.renderer.events.setTargetElement(null);
1652
+ *
1653
+ * // Switch to a different canvas
1654
+ * const newCanvas = document.querySelector('#game-canvas');
1655
+ * app.renderer.events.setTargetElement(newCanvas);
1656
+ * ```
1657
+ * @remarks
1658
+ * - Automatically removes event listeners from previous element
1659
+ * - Required for the event system to function
1660
+ * - Safe to call multiple times
1661
+ * @see {@link EventSystem#domElement} The current DOM element
1662
+ * @see {@link EventsTicker} For the ticker system that tracks pointer movement
1663
+ */
1664
+ setTargetElement(e) {
1665
+ this._removeEvents(), this.domElement = e, _.domElement = e, this._addEvents();
1666
+ }
1667
+ /** Register event listeners on {@link Renderer#domElement this.domElement}. */
1668
+ _addEvents() {
1669
+ if (this._eventsAdded || !this.domElement)
1670
+ return;
1671
+ _.addTickerListener();
1672
+ const e = this.domElement.style;
1673
+ e && (globalThis.navigator.msPointerEnabled ? (e.msContentZooming = "none", e.msTouchAction = "none") : this.supportsPointerEvents && (e.touchAction = "none")), this.supportsPointerEvents ? (globalThis.document.addEventListener("pointermove", this._onPointerMove, !0), this.domElement.addEventListener("pointerdown", this._onPointerDown, !0), this.domElement.addEventListener("pointerleave", this._onPointerOverOut, !0), this.domElement.addEventListener("pointerover", this._onPointerOverOut, !0), globalThis.addEventListener("pointerup", this._onPointerUp, !0)) : (globalThis.document.addEventListener("mousemove", this._onPointerMove, !0), this.domElement.addEventListener("mousedown", this._onPointerDown, !0), this.domElement.addEventListener("mouseout", this._onPointerOverOut, !0), this.domElement.addEventListener("mouseover", this._onPointerOverOut, !0), globalThis.addEventListener("mouseup", this._onPointerUp, !0), this.supportsTouchEvents && (this.domElement.addEventListener("touchstart", this._onPointerDown, !0), this.domElement.addEventListener("touchend", this._onPointerUp, !0), this.domElement.addEventListener("touchmove", this._onPointerMove, !0))), this.domElement.addEventListener("wheel", this.onWheel, {
1674
+ passive: !0,
1675
+ capture: !0
1676
+ }), this._eventsAdded = !0;
1677
+ }
1678
+ /** Unregister event listeners on {@link EventSystem#domElement this.domElement}. */
1679
+ _removeEvents() {
1680
+ if (!this._eventsAdded || !this.domElement)
1681
+ return;
1682
+ _.removeTickerListener();
1683
+ const e = this.domElement.style;
1684
+ e && (globalThis.navigator.msPointerEnabled ? (e.msContentZooming = "", e.msTouchAction = "") : this.supportsPointerEvents && (e.touchAction = "")), this.supportsPointerEvents ? (globalThis.document.removeEventListener("pointermove", this._onPointerMove, !0), this.domElement.removeEventListener("pointerdown", this._onPointerDown, !0), this.domElement.removeEventListener("pointerleave", this._onPointerOverOut, !0), this.domElement.removeEventListener("pointerover", this._onPointerOverOut, !0), globalThis.removeEventListener("pointerup", this._onPointerUp, !0)) : (globalThis.document.removeEventListener("mousemove", this._onPointerMove, !0), this.domElement.removeEventListener("mousedown", this._onPointerDown, !0), this.domElement.removeEventListener("mouseout", this._onPointerOverOut, !0), this.domElement.removeEventListener("mouseover", this._onPointerOverOut, !0), globalThis.removeEventListener("mouseup", this._onPointerUp, !0), this.supportsTouchEvents && (this.domElement.removeEventListener("touchstart", this._onPointerDown, !0), this.domElement.removeEventListener("touchend", this._onPointerUp, !0), this.domElement.removeEventListener("touchmove", this._onPointerMove, !0))), this.domElement.removeEventListener("wheel", this.onWheel, !0), this.domElement = null, this._eventsAdded = !1;
1685
+ }
1686
+ /**
1687
+ * Maps coordinates from DOM/screen space into PixiJS normalized coordinates.
1688
+ * This takes into account the current scale, position, and resolution of the DOM element.
1689
+ * @param point - The point to store the mapped coordinates in
1690
+ * @param x - The x coordinate in DOM/client space
1691
+ * @param y - The y coordinate in DOM/client space
1692
+ * @example
1693
+ * ```ts
1694
+ * // Map mouse coordinates to PixiJS space
1695
+ * const point = new Point();
1696
+ * app.renderer.events.mapPositionToPoint(
1697
+ * point,
1698
+ * event.clientX,
1699
+ * event.clientY
1700
+ * );
1701
+ * console.log('Mapped position:', point.x, point.y);
1702
+ *
1703
+ * // Using with pointer events
1704
+ * sprite.on('pointermove', (event) => {
1705
+ * // event.global already contains mapped coordinates
1706
+ * console.log('Global:', event.global.x, event.global.y);
1707
+ *
1708
+ * // Map to local coordinates
1709
+ * const local = event.getLocalPosition(sprite);
1710
+ * console.log('Local:', local.x, local.y);
1711
+ * });
1712
+ * ```
1713
+ * @remarks
1714
+ * - Accounts for element scaling and positioning
1715
+ * - Adjusts for device pixel ratio/resolution
1716
+ */
1717
+ mapPositionToPoint(e, t, i) {
1718
+ const n = this.domElement.isConnected ? this.domElement.getBoundingClientRect() : {
1719
+ width: this.domElement.width,
1720
+ height: this.domElement.height,
1721
+ left: 0,
1722
+ top: 0
1723
+ }, s = 1 / this.resolution;
1724
+ e.x = (t - n.left) * (this.domElement.width / n.width) * s, e.y = (i - n.top) * (this.domElement.height / n.height) * s;
1725
+ }
1726
+ /**
1727
+ * Ensures that the original event object contains all data that a regular pointer event would have
1728
+ * @param event - The original event data from a touch or mouse event
1729
+ * @returns An array containing a single normalized pointer event, in the case of a pointer
1730
+ * or mouse event, or a multiple normalized pointer events if there are multiple changed touches
1731
+ */
1732
+ _normalizeToPointerData(e) {
1733
+ const t = [];
1734
+ if (this.supportsTouchEvents && e instanceof TouchEvent)
1735
+ for (let i = 0, n = e.changedTouches.length; i < n; i++) {
1736
+ const s = e.changedTouches[i];
1737
+ typeof s.button > "u" && (s.button = 0), typeof s.buttons > "u" && (s.buttons = 1), typeof s.isPrimary > "u" && (s.isPrimary = e.touches.length === 1 && e.type === "touchstart"), typeof s.width > "u" && (s.width = s.radiusX || 1), typeof s.height > "u" && (s.height = s.radiusY || 1), typeof s.tiltX > "u" && (s.tiltX = 0), typeof s.tiltY > "u" && (s.tiltY = 0), typeof s.pointerType > "u" && (s.pointerType = "touch"), typeof s.pointerId > "u" && (s.pointerId = s.identifier || 0), typeof s.pressure > "u" && (s.pressure = s.force || 0.5), typeof s.twist > "u" && (s.twist = 0), typeof s.tangentialPressure > "u" && (s.tangentialPressure = 0), typeof s.layerX > "u" && (s.layerX = s.offsetX = s.clientX), typeof s.layerY > "u" && (s.layerY = s.offsetY = s.clientY), s.isNormalized = !0, s.type = e.type, t.push(s);
1738
+ }
1739
+ else if (!globalThis.MouseEvent || e instanceof MouseEvent && (!this.supportsPointerEvents || !(e instanceof globalThis.PointerEvent))) {
1740
+ const i = e;
1741
+ typeof i.isPrimary > "u" && (i.isPrimary = !0), typeof i.width > "u" && (i.width = 1), typeof i.height > "u" && (i.height = 1), typeof i.tiltX > "u" && (i.tiltX = 0), typeof i.tiltY > "u" && (i.tiltY = 0), typeof i.pointerType > "u" && (i.pointerType = "mouse"), typeof i.pointerId > "u" && (i.pointerId = ge), typeof i.pressure > "u" && (i.pressure = 0.5), typeof i.twist > "u" && (i.twist = 0), typeof i.tangentialPressure > "u" && (i.tangentialPressure = 0), i.isNormalized = !0, t.push(i);
1742
+ } else
1743
+ t.push(e);
1744
+ return t;
1745
+ }
1746
+ /**
1747
+ * Normalizes the native {@link https://w3c.github.io/uievents/#interface-wheelevent WheelEvent}.
1748
+ *
1749
+ * The returned {@link FederatedWheelEvent} is a shared instance. It will not persist across
1750
+ * multiple native wheel events.
1751
+ * @param nativeEvent - The native wheel event that occurred on the canvas.
1752
+ * @returns A federated wheel event.
1753
+ */
1754
+ normalizeWheelEvent(e) {
1755
+ const t = this._rootWheelEvent;
1756
+ return this._transferMouseData(t, e), t.deltaX = e.deltaX, t.deltaY = e.deltaY, t.deltaZ = e.deltaZ, t.deltaMode = e.deltaMode, this.mapPositionToPoint(t.screen, e.clientX, e.clientY), t.global.copyFrom(t.screen), t.offset.copyFrom(t.screen), t.nativeEvent = e, t.type = e.type, t;
1757
+ }
1758
+ /**
1759
+ * Normalizes the `nativeEvent` into a federateed {@link FederatedPointerEvent}.
1760
+ * @param event
1761
+ * @param nativeEvent
1762
+ */
1763
+ _bootstrapEvent(e, t) {
1764
+ return e.originalEvent = null, e.nativeEvent = t, e.pointerId = t.pointerId, e.width = t.width, e.height = t.height, e.isPrimary = t.isPrimary, e.pointerType = t.pointerType, e.pressure = t.pressure, e.tangentialPressure = t.tangentialPressure, e.tiltX = t.tiltX, e.tiltY = t.tiltY, e.twist = t.twist, this._transferMouseData(e, t), this.mapPositionToPoint(e.screen, t.clientX, t.clientY), e.global.copyFrom(e.screen), e.offset.copyFrom(e.screen), e.isTrusted = t.isTrusted, e.type === "pointerleave" && (e.type = "pointerout"), e.type.startsWith("mouse") && (e.type = e.type.replace("mouse", "pointer")), e.type.startsWith("touch") && (e.type = ye[e.type] || e.type), e;
1765
+ }
1766
+ /**
1767
+ * Transfers base & mouse event data from the `nativeEvent` to the federated event.
1768
+ * @param event
1769
+ * @param nativeEvent
1770
+ */
1771
+ _transferMouseData(e, t) {
1772
+ e.isTrusted = t.isTrusted, e.srcElement = t.srcElement, e.timeStamp = performance.now(), e.type = t.type, e.altKey = t.altKey, e.button = t.button, e.buttons = t.buttons, e.client.x = t.clientX, e.client.y = t.clientY, e.ctrlKey = t.ctrlKey, e.metaKey = t.metaKey, e.movement.x = t.movementX, e.movement.y = t.movementY, e.page.x = t.pageX, e.page.y = t.pageY, e.relatedTarget = null, e.shiftKey = t.shiftKey;
1773
+ }
1774
+ };
1775
+ B.extension = {
1776
+ name: "events",
1777
+ type: [
1778
+ b.WebGLSystem,
1779
+ b.CanvasSystem,
1780
+ b.WebGPUSystem
1781
+ ],
1782
+ priority: -1
1783
+ };
1784
+ B.defaultEventFeatures = {
1785
+ /** Enables pointer events associated with pointer movement. */
1786
+ move: !0,
1787
+ /** Enables global pointer move events. */
1788
+ globalMove: !0,
1789
+ /** Enables pointer events associated with clicking. */
1790
+ click: !0,
1791
+ /** Enables wheel events. */
1792
+ wheel: !0
1793
+ };
1794
+ let ee = B;
1795
+ const be = {
1796
+ onclick: null,
1797
+ onmousedown: null,
1798
+ onmouseenter: null,
1799
+ onmouseleave: null,
1800
+ onmousemove: null,
1801
+ onglobalmousemove: null,
1802
+ onmouseout: null,
1803
+ onmouseover: null,
1804
+ onmouseup: null,
1805
+ onmouseupoutside: null,
1806
+ onpointercancel: null,
1807
+ onpointerdown: null,
1808
+ onpointerenter: null,
1809
+ onpointerleave: null,
1810
+ onpointermove: null,
1811
+ onglobalpointermove: null,
1812
+ onpointerout: null,
1813
+ onpointerover: null,
1814
+ onpointertap: null,
1815
+ onpointerup: null,
1816
+ onpointerupoutside: null,
1817
+ onrightclick: null,
1818
+ onrightdown: null,
1819
+ onrightup: null,
1820
+ onrightupoutside: null,
1821
+ ontap: null,
1822
+ ontouchcancel: null,
1823
+ ontouchend: null,
1824
+ ontouchendoutside: null,
1825
+ ontouchmove: null,
1826
+ onglobaltouchmove: null,
1827
+ ontouchstart: null,
1828
+ onwheel: null,
1829
+ get interactive() {
1830
+ return this.eventMode === "dynamic" || this.eventMode === "static";
1831
+ },
1832
+ set interactive(a) {
1833
+ this.eventMode = a ? "static" : "passive";
1834
+ },
1835
+ _internalEventMode: void 0,
1836
+ get eventMode() {
1837
+ return this._internalEventMode ?? ee.defaultEventMode;
1838
+ },
1839
+ set eventMode(a) {
1840
+ this._internalEventMode = a;
1841
+ },
1842
+ isInteractive() {
1843
+ return this.eventMode === "static" || this.eventMode === "dynamic";
1844
+ },
1845
+ interactiveChildren: !0,
1846
+ hitArea: null,
1847
+ addEventListener(a, e, t) {
1848
+ const i = typeof t == "boolean" && t || typeof t == "object" && t.capture, n = typeof t == "object" ? t.signal : void 0, s = typeof t == "object" ? t.once === !0 : !1, o = typeof e == "function" ? void 0 : e;
1849
+ a = i ? `${a}capture` : a;
1850
+ const r = typeof e == "function" ? e : e.handleEvent, l = this;
1851
+ n && n.addEventListener("abort", () => {
1852
+ l.off(a, r, o);
1853
+ }), s ? l.once(a, r, o) : l.on(a, r, o);
1854
+ },
1855
+ removeEventListener(a, e, t) {
1856
+ const i = typeof t == "boolean" && t || typeof t == "object" && t.capture, n = typeof e == "function" ? void 0 : e;
1857
+ a = i ? `${a}capture` : a, e = typeof e == "function" ? e : e.handleEvent, this.off(a, e, n);
1858
+ },
1859
+ dispatchEvent(a) {
1860
+ if (!(a instanceof M))
1861
+ throw new Error("Container cannot propagate events outside of the Federated Events API");
1862
+ return a.defaultPrevented = !1, a.path = null, a.target = this, a.manager.dispatchEvent(a), !a.defaultPrevented;
1863
+ }
1864
+ };
1865
+ w.add(ue);
1866
+ w.mixin(V, pe);
1867
+ w.add(ee);
1868
+ w.mixin(V, be);
1869
+ w.add(Q);
src/backend/gradio_polygonannotator/templates/component/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ap as f } from "./Index-Ct_1BCRd.js";
2
  export {
3
  f as default
4
  };
 
1
+ import { aq as f } from "./Index-jQCzN2ap.js";
2
  export {
3
  f as default
4
  };
src/backend/gradio_polygonannotator/templates/component/webworkerAll-BVEtD__r.js ADDED
The diff for this file is too large to render. See raw diff
 
src/demo/app.py CHANGED
@@ -1,85 +1,434 @@
1
  import gradio as gr
2
  from gradio_polygonannotator import PolygonAnnotator
3
 
4
- # Example with document regions
5
  example_data = {
6
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
7
  "polygons": [
8
  {
9
- "id": "title",
10
- "coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
11
- "color": "#FF6B6B",
12
- "mask_opacity": 0.15,
13
- "stroke_width": 1.0,
 
 
 
 
 
 
 
 
 
 
 
 
14
  "stroke_opacity": 0.8,
 
 
 
 
 
15
  },
16
  {
17
- "id": "paragraph_1",
18
- "coordinates": [[100, 400], [750, 400], [750, 600], [100, 600]],
19
- "color": "#4ECDC4",
20
- "mask_opacity": 0.15,
21
- "stroke_width": 0.7,
22
- "stroke_opacity": 0.6,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  },
24
  {
25
- "id": "paragraph_2",
26
- "coordinates": [[100, 650], [750, 650], [750, 950], [100, 950]],
27
- "color": "#4ECDC4",
28
- "mask_opacity": 0.15,
29
- "stroke_width": 0.7,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  "stroke_opacity": 0.6,
 
 
 
 
 
31
  },
32
  {
33
- "id": "signature",
34
- "coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
35
- "color": "#FFE66D",
36
- "mask_opacity": 0.2,
37
- "stroke_width": 1.5,
38
- "stroke_opacity": 0.8,
39
- }
40
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
- def handle_selection(data, evt: gr.SelectData):
44
- """Handle polygon selection and display info"""
 
 
 
 
 
 
 
 
45
  if evt.value and data:
46
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
47
- info = f"Selected {len(selected_ids)} polygon(s):\n"
48
- for poly_id in selected_ids:
49
- polygon = next((p for p in data["polygons"] if p["id"] == poly_id), None)
50
- if polygon:
51
- info += f"• {poly_id}\n"
52
- return info
53
- return "Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  with gr.Blocks() as demo:
56
  gr.Markdown("""
57
- # PolygonAnnotator - Interactive Polygon Selection
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- Click on polygons to select them. Use **Ctrl/Cmd+Click** for multiple selections.
60
- Click selected polygons to deselect.
 
61
  """)
62
 
63
  with gr.Row():
64
- with gr.Column(scale=3):
65
- annotator = PolygonAnnotator(
66
  value=example_data,
67
- label="Document with Region Annotations",
68
  height=600,
69
  )
70
 
71
  with gr.Column(scale=1):
72
  selected_info = gr.Textbox(
73
- label="Selected Regions",
74
- lines=6,
75
- value="Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
76
  )
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  # Handle selection events
79
- annotator.select(
80
- handle_selection,
81
- inputs=[annotator],
82
- outputs=[selected_info]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  )
84
 
85
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  from gradio_polygonannotator import PolygonAnnotator
3
 
 
4
  example_data = {
5
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
6
  "polygons": [
7
  {
8
+ "id": "complex_header",
9
+ "coordinates": [
10
+ [150, 120],
11
+ [200, 100],
12
+ [350, 110],
13
+ [450, 95],
14
+ [600, 120],
15
+ [620, 180],
16
+ [580, 220],
17
+ [400, 240],
18
+ [250, 235],
19
+ [180, 200],
20
+ [140, 160],
21
+ ],
22
+ "color": "#FF0000",
23
+ "mask_opacity": 0.3,
24
+ "stroke_width": 1.2,
25
  "stroke_opacity": 0.8,
26
+ "selected_mask_opacity": 0.6,
27
+ "selected_stroke_opacity": 1.0,
28
+ "display_text": "Complex Header",
29
+ "display_font_size": 14,
30
+ "display_text_color": "#FFFFFF",
31
  },
32
  {
33
+ "id": "overlapping_ribbon",
34
+ "coordinates": [
35
+ [300, 150],
36
+ [550, 160],
37
+ [580, 200],
38
+ [650, 220],
39
+ [680, 280],
40
+ [620, 320],
41
+ [500, 340],
42
+ [350, 330],
43
+ [200, 310],
44
+ [180, 270],
45
+ [220, 220],
46
+ [280, 190],
47
+ ],
48
+ "color": "#00FF00",
49
+ "mask_opacity": 0.25,
50
+ "stroke_width": 1.5,
51
+ "stroke_opacity": 0.7,
52
+ "selected_mask_opacity": 0.5,
53
+ "selected_stroke_opacity": 1.0,
54
+ "display_text": "Overlapping Ribbon",
55
+ "display_font_size": 14,
56
+ "display_text_color": "#000000",
57
  },
58
  {
59
+ "id": "irregular_content",
60
+ "coordinates": [
61
+ [120, 380],
62
+ [250, 350],
63
+ [400, 370],
64
+ [550, 390],
65
+ [720, 420],
66
+ [750, 500],
67
+ [780, 650],
68
+ [720, 800],
69
+ [650, 920],
70
+ [500, 950],
71
+ [300, 940],
72
+ [150, 900],
73
+ [80, 750],
74
+ [90, 600],
75
+ [110, 450],
76
+ ],
77
+ "color": "#0000FF",
78
+ "mask_opacity": 0.2,
79
+ "stroke_width": 0.8,
80
  "stroke_opacity": 0.6,
81
+ "selected_mask_opacity": 0.4,
82
+ "selected_stroke_opacity": 0.9,
83
+ "display_text": "Irregular Content",
84
+ "display_font_size": 14,
85
+ "display_text_color": "#FFFF00",
86
  },
87
  {
88
+ "id": "star_shaped",
89
+ "coordinates": [
90
+ [400, 850],
91
+ [450, 900],
92
+ [520, 910],
93
+ [480, 970],
94
+ [500, 1040],
95
+ [400, 1000],
96
+ [300, 1040],
97
+ [320, 970],
98
+ [280, 910],
99
+ [350, 900],
100
+ ],
101
+ "color": "#FF00FF",
102
+ "mask_opacity": 0.35,
103
+ "stroke_width": 2.0,
104
+ "stroke_opacity": 0.9,
105
+ "selected_mask_opacity": 0.7,
106
+ "selected_stroke_opacity": 1.0,
107
+ "display_text": "Star Shape",
108
+ "display_font_size": 14,
109
+ "display_text_color": "#FFFFFF",
110
+ },
111
+ {
112
+ "id": "overlapping_triangle",
113
+ "coordinates": [
114
+ [480, 600],
115
+ [650, 750],
116
+ [720, 850],
117
+ [600, 920],
118
+ [450, 880],
119
+ [350, 800],
120
+ [380, 700],
121
+ ],
122
+ "color": "#FFAA00",
123
+ "mask_opacity": 0.28,
124
+ "stroke_width": 1.8,
125
+ "stroke_opacity": 0.75,
126
+ "selected_mask_opacity": 0.55,
127
+ "selected_stroke_opacity": 1.0,
128
+ "display_text": "Triangle",
129
+ "display_font_size": 14,
130
+ "display_text_color": "#000000",
131
+ },
132
+ ],
133
  }
134
 
135
+ polygon_table = [
136
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
137
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
138
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
139
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
140
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
141
+ ]
142
+
143
+
144
+ def process_viewer_selection(data, evt: gr.SelectData):
145
  if evt.value and data:
146
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
147
+
148
+ highlighted_table = []
149
+ for row in polygon_table:
150
+ if row[0] in selected_ids:
151
+ highlighted_row = [
152
+ f"→ {row[0]} ←",
153
+ f" {row[1]} ",
154
+ f"→ {row[2]} ←",
155
+ f"→ {row[3]} ←",
156
+ f"→ {row[4]} ←",
157
+ f"→ {row[5]} ←",
158
+ ]
159
+ highlighted_table.append(highlighted_row)
160
+ else:
161
+ highlighted_table.append(row)
162
+
163
+ info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
164
+ for selected_id in selected_ids:
165
+ selected_polygon = next(
166
+ (p for p in data["polygons"] if p["id"] == selected_id), None
167
+ )
168
+ if selected_polygon:
169
+ info_lines.append(
170
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
171
+ )
172
+
173
+ info_text = "\n".join(info_lines)
174
+ return info_text, highlighted_table
175
+
176
+ return "No polygons selected", polygon_table
177
+
178
+
179
+ def process_dataframe_selection(selected_data, evt: gr.SelectData):
180
+ if evt.index is not None and evt.index[0] < len(polygon_table):
181
+ selected_row = polygon_table[evt.index[0]]
182
+ polygon_id = selected_row[0]
183
+
184
+ updated_data = example_data.copy()
185
+ updated_data["selected_polygons"] = [polygon_id]
186
+
187
+ highlighted_table = []
188
+ for i, row in enumerate(polygon_table):
189
+ if i == evt.index[0]:
190
+ highlighted_row = [
191
+ f"→ {row[0]} ←",
192
+ f"→ {row[1]} ←",
193
+ f"→ {row[2]} ←",
194
+ f"→ {row[3]} ←",
195
+ f"→ {row[4]} ←",
196
+ f"→ {row[5]} ←",
197
+ ]
198
+ highlighted_table.append(highlighted_row)
199
+ else:
200
+ highlighted_table.append(row)
201
+
202
+ info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
203
+ return updated_data, info_text, highlighted_table
204
+
205
+ updated_data = example_data.copy()
206
+ updated_data["selected_polygons"] = []
207
+ return updated_data, "No polygons selected", polygon_table
208
+
209
+
210
+ def clear_selection():
211
+ updated_data = example_data.copy()
212
+ updated_data["selected_polygons"] = []
213
+ return updated_data, "No polygons selected", polygon_table
214
+
215
+
216
+ def select_polygon_by_id(polygon_id):
217
+ if not polygon_id or polygon_id.strip() == "":
218
+ updated_data = example_data.copy()
219
+ updated_data["selected_polygons"] = []
220
+ return updated_data, "No polygons selected", polygon_table
221
+
222
+ polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
223
+ valid_ids = [p["id"] for p in example_data["polygons"]]
224
+
225
+ valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
226
+ invalid_ids = [id for id in polygon_ids if id not in valid_ids]
227
+
228
+ if not valid_selected_ids:
229
+ updated_data = example_data.copy()
230
+ updated_data["selected_polygons"] = []
231
+ error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
232
+ return updated_data, error_msg, polygon_table
233
+
234
+ updated_data = example_data.copy()
235
+ updated_data["selected_polygons"] = valid_selected_ids
236
+
237
+ highlighted_table = []
238
+ for row in polygon_table:
239
+ if row[0] in valid_selected_ids:
240
+ highlighted_row = [
241
+ f"→ {row[0]} ←",
242
+ f"→ {row[1]} ←",
243
+ f"→ {row[2]} ←",
244
+ f"→ {row[3]} ←",
245
+ f"→ {row[4]} ←",
246
+ f"→ {row[5]} ←",
247
+ ]
248
+ highlighted_table.append(highlighted_row)
249
+ else:
250
+ highlighted_table.append(row)
251
+
252
+ info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
253
+ for selected_id in valid_selected_ids:
254
+ selected_polygon = next(
255
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
256
+ )
257
+ if selected_polygon:
258
+ info_lines.append(
259
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
260
+ )
261
+
262
+ if invalid_ids:
263
+ info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
264
+
265
+ info_text = "\n".join(info_lines)
266
+ return updated_data, info_text, highlighted_table
267
+
268
+
269
+ def toggle_text_display(current_data, show_text):
270
+ if not current_data:
271
+ return current_data
272
+
273
+ updated_data = current_data.copy()
274
+ updated_data["polygons"] = []
275
+
276
+ for polygon in current_data.get("polygons", []):
277
+ updated_polygon = polygon.copy()
278
+ if not show_text:
279
+ updated_polygon["display_font_size"] = 0
280
+ else:
281
+ original_polygon = next(
282
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
283
+ )
284
+ if original_polygon:
285
+ updated_polygon["display_text"] = original_polygon.get(
286
+ "display_text", polygon["id"]
287
+ )
288
+ updated_polygon["display_font_size"] = original_polygon.get(
289
+ "display_font_size", 14
290
+ )
291
+ updated_polygon["display_text_color"] = original_polygon.get(
292
+ "display_text_color", "#000000"
293
+ )
294
+
295
+ updated_data["polygons"].append(updated_polygon)
296
+
297
+ return updated_data
298
+
299
 
300
  with gr.Blocks() as demo:
301
  gr.Markdown("""
302
+ # PolygonAnnotator - Advanced Interactive Demo
303
+
304
+ ## 🎮 Controls & Hotkeys
305
+
306
+ ### Selection
307
+ - **Click** on polygons or text labels to select/deselect
308
+ - **Ctrl/Cmd+Click** for multiple selection
309
+ - **Click dataframe rows** to select polygons
310
+ - **Enter polygon IDs** manually in the textbox
311
+
312
+ ### Navigation
313
+ - **Mouse Wheel** - Zoom in/out at cursor position
314
+ - **+/=** - Zoom in (10%)
315
+ - **-** - Zoom out (10%)
316
+ - **Ctrl/Cmd+0** - Reset view to original
317
+ - **Arrow Keys** - Pan view (↑↓←→)
318
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
319
 
320
+ ### Features
321
+ - **Clear button** to deselect all
322
+ - **Toggle text display** checkbox for polygon labels
323
  """)
324
 
325
  with gr.Row():
326
+ with gr.Column(scale=2):
327
+ poly_annotator = PolygonAnnotator(
328
  value=example_data,
329
+ label="Document with Interactive Polygon Annotations",
330
  height=600,
331
  )
332
 
333
  with gr.Column(scale=1):
334
  selected_info = gr.Textbox(
335
+ label="Selected Polygon Information",
336
+ lines=5,
337
+ value="Click on a polygon to see its information",
338
  )
339
 
340
+ polygon_dataframe = gr.Dataframe(
341
+ value=polygon_table,
342
+ headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
343
+ label="Polygon Data (Click rows to select)",
344
+ interactive=True,
345
+ )
346
+
347
+ clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
348
+
349
+ # Add text display toggle
350
+ with gr.Row():
351
+ show_text_checkbox = gr.Checkbox(
352
+ label="Show Polygon Text",
353
+ value=True,
354
+ info="Toggle text display on polygons",
355
+ )
356
+
357
+ with gr.Row():
358
+ polygon_id_input = gr.Textbox(
359
+ label="Select by Polygon ID(s)",
360
+ placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
361
+ scale=3,
362
+ )
363
+ select_button = gr.Button("Select", variant="primary", scale=1)
364
+
365
+ gr.Markdown("""
366
+ ### Features Demonstrated
367
+
368
+ #### 🎨 Visual Customization
369
+ - Different **mask opacity** for each polygon fill
370
+ - Variable **stroke width** (0.5px to 1.5px)
371
+ - Custom **stroke opacity** for borders
372
+ - Enhanced appearance when selected
373
+ - **Text labels** with customizable colors and sizes
374
+
375
+ #### 🖱️ Interaction Methods
376
+ 1. **Direct Click**: Click polygons in the viewer
377
+ 2. **Multi-Selection**: Ctrl/Cmd+Click for multiple
378
+ 3. **Dataframe**: Click table rows
379
+ 4. **Text Input**: Type polygon IDs
380
+ 5. **Clear All**: Reset selection
381
+ 6. **Text Toggle**: Show/hide polygon labels
382
+
383
+ #### 📝 Polygon IDs
384
+ - `date_line` - Red header area
385
+ - `salutation` - Green greeting section
386
+ - `main_text_block` - Blue main content
387
+ - `closing_signature` - Yellow signature area
388
+
389
+ #### 💡 Tips
390
+ - Hover over polygons for visual feedback
391
+ - Selected polygons have increased opacity
392
+ - Use comma-separated IDs for batch selection
393
+ - Click selected polygons to deselect them
394
+ - Toggle text display to see labels on polygons
395
+ """)
396
+
397
  # Handle selection events
398
+ poly_annotator.select(
399
+ process_viewer_selection,
400
+ inputs=[poly_annotator],
401
+ outputs=[selected_info, polygon_dataframe],
402
+ )
403
+
404
+ polygon_dataframe.select(
405
+ process_dataframe_selection,
406
+ inputs=[polygon_dataframe],
407
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
408
+ )
409
+
410
+ clear_button.click(
411
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
412
+ )
413
+
414
+ select_button.click(
415
+ select_polygon_by_id,
416
+ inputs=[polygon_id_input],
417
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
418
+ )
419
+
420
+ # Also allow Enter key in textbox
421
+ polygon_id_input.submit(
422
+ select_polygon_by_id,
423
+ inputs=[polygon_id_input],
424
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
425
+ )
426
+
427
+ # Handle text display toggle
428
+ show_text_checkbox.change(
429
+ toggle_text_display,
430
+ inputs=[poly_annotator, show_text_checkbox],
431
+ outputs=[poly_annotator],
432
  )
433
 
434
  if __name__ == "__main__":
src/demo/space.py CHANGED
@@ -3,7 +3,7 @@ import gradio as gr
3
  from app import demo as app
4
  import os
5
 
6
- _docs = {'PolygonAnnotator': {'description': 'Interactive polygon annotation component for visualizing and selecting polygon regions on images.\n\nThe PolygonAnnotator displays an image with customizable polygon overlays that users can interact with.\nFeatures include multi-selection with Ctrl/Cmd+click, hover effects, and customizable appearance including\nstroke width, opacity settings for both fill and stroke, with separate settings for selected states.\n\nPerfect for:\n- Document layout analysis and region selection\n- Image segmentation visualization\n- Interactive annotation review and editing\n- Object detection result visualization', 'members': {'__init__': {'value': {'type': 'dict | None', 'default': 'None', 'description': "Dictionary containing 'image' (FileData), 'polygons' (list with id, coordinates, color, opacities),"}, 'label': {'type': 'str | I18nData | None', 'default': 'None', 'description': 'Component label shown above the annotator.'}, 'every': {'type': 'Timer | float | None', 'default': 'None', 'description': 'Continuously calls `value` to recalculate it if `value` is a function.'}, 'inputs': {'type': 'Component | Sequence[Component] | set[Component] | None', 'default': 'None', 'description': "Components used as inputs to calculate `value` if it's a function."}, 'show_label': {'type': 'bool | None', 'default': 'None', 'description': 'Whether to display the label.'}, 'show_download_button': {'type': 'bool', 'default': 'True', 'description': 'Whether to show image download button.'}, 'height': {'type': 'int | str | None', 'default': 'None', 'description': 'Component height in pixels or CSS units.'}, 'width': {'type': 'int | str | None', 'default': 'None', 'description': 'Component width in pixels or CSS units.'}, 'container': {'type': 'bool', 'default': 'True', 'description': 'Whether to wrap component in a container with padding.'}, 'scale': {'type': 'int | None', 'default': 'None', 'description': 'Relative size compared to adjacent components.'}, 'min_width': {'type': 'int', 'default': '160', 'description': 'Minimum pixel width before wrapping.'}, 'interactive': {'type': 'bool | None', 'default': 'None', 'description': 'Whether users can interact with polygons (selection/deselection).'}, 'visible': {'type': 'bool | Literal["hidden"]', 'default': 'True', 'description': 'Whether component is visible ("hidden" keeps it in DOM but invisible).'}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': 'HTML DOM id for CSS targeting.'}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': 'HTML DOM classes for CSS targeting.'}, 'render': {'type': 'bool', 'default': 'True', 'description': 'Whether to render the component immediately.'}, 'key': {'type': 'int | str | tuple[int | str, ...] | None', 'default': 'None', 'description': 'Key for maintaining component identity across re-renders.'}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': '"value"', 'description': 'Parameters preserved across re-renders with same key.'}}, 'postprocess': {'value': {'type': 'dict | None', 'description': "Dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries"}}, 'preprocess': {'return': {'type': 'dict | None', 'description': 'Dictionary with image path, polygon data including coordinates, colors, opacities,'}, 'value': None}}, 'events': {'clear': {'type': None, 'default': None, 'description': 'This listener is triggered when the user clears the PolygonAnnotator using the clear button for the component.'}, 'change': {'type': None, 'default': None, 'description': 'Triggered when the value of the PolygonAnnotator changes either because of user input (e.g. a user types in a textbox) OR because of a function update (e.g. an image receives a value from the output of an event trigger). See `.input()` for a listener that is only triggered by user input.'}, 'upload': {'type': None, 'default': None, 'description': 'This listener is triggered when the user uploads a file into the PolygonAnnotator.'}, 'select': {'type': None, 'default': None, 'description': 'Event listener for when the user selects or deselects the PolygonAnnotator. Uses event data gradio.SelectData to carry `value` referring to the label of the PolygonAnnotator, and `selected` to refer to state of the PolygonAnnotator. See EventData documentation on how to use this event data'}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'PolygonAnnotator': []}}}
7
 
8
  abs_path = os.path.join(os.path.dirname(__file__), "css.css")
9
 
@@ -21,7 +21,7 @@ with gr.Blocks(
21
  # `gradio_polygonannotator`
22
 
23
  <div style="display: flex; gap: 7px;">
24
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.1.0%20-%20orange"> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
25
  </div>
26
 
27
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
@@ -41,85 +41,434 @@ pip install gradio_polygonannotator
41
  import gradio as gr
42
  from gradio_polygonannotator import PolygonAnnotator
43
 
44
- # Example with document regions
45
  example_data = {
46
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
47
  "polygons": [
48
  {
49
- "id": "title",
50
- "coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
51
- "color": "#FF6B6B",
52
- "mask_opacity": 0.15,
53
- "stroke_width": 1.0,
 
 
 
 
 
 
 
 
 
 
 
 
54
  "stroke_opacity": 0.8,
 
 
 
 
 
55
  },
56
  {
57
- "id": "paragraph_1",
58
- "coordinates": [[100, 400], [750, 400], [750, 600], [100, 600]],
59
- "color": "#4ECDC4",
60
- "mask_opacity": 0.15,
61
- "stroke_width": 0.7,
62
- "stroke_opacity": 0.6,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  },
64
  {
65
- "id": "paragraph_2",
66
- "coordinates": [[100, 650], [750, 650], [750, 950], [100, 950]],
67
- "color": "#4ECDC4",
68
- "mask_opacity": 0.15,
69
- "stroke_width": 0.7,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  "stroke_opacity": 0.6,
 
 
 
 
 
71
  },
72
  {
73
- "id": "signature",
74
- "coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
75
- "color": "#FFE66D",
76
- "mask_opacity": 0.2,
77
- "stroke_width": 1.5,
78
- "stroke_opacity": 0.8,
79
- }
80
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
- def handle_selection(data, evt: gr.SelectData):
84
- \"\"\"Handle polygon selection and display info\"\"\"
 
 
 
 
 
 
 
 
85
  if evt.value and data:
86
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
87
- info = f"Selected {len(selected_ids)} polygon(s):\n"
88
- for poly_id in selected_ids:
89
- polygon = next((p for p in data["polygons"] if p["id"] == poly_id), None)
90
- if polygon:
91
- info += f"• {poly_id}\n"
92
- return info
93
- return "Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  with gr.Blocks() as demo:
96
  gr.Markdown(\"\"\"
97
- # PolygonAnnotator - Interactive Polygon Selection
98
-
99
- Click on polygons to select them. Use **Ctrl/Cmd+Click** for multiple selections.
100
- Click selected polygons to deselect.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  \"\"\")
102
 
103
  with gr.Row():
104
- with gr.Column(scale=3):
105
- annotator = PolygonAnnotator(
106
  value=example_data,
107
- label="Document with Region Annotations",
108
  height=600,
109
  )
110
 
111
  with gr.Column(scale=1):
112
  selected_info = gr.Textbox(
113
- label="Selected Regions",
114
- lines=6,
115
- value="Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
 
 
 
 
 
 
 
116
  )
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  # Handle selection events
119
- annotator.select(
120
- handle_selection,
121
- inputs=[annotator],
122
- outputs=[selected_info]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  )
124
 
125
  if __name__ == "__main__":
@@ -155,8 +504,8 @@ The impact on the users predict function varies depending on whether the compone
155
 
156
  The code snippet below is accurate in cases where the component is used as both an input and an output.
157
 
158
- - **As input:** Is passed, dictionary with image path, polygon data including coordinates, colors, opacities,.
159
- - **As output:** Should return, dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries.
160
 
161
  ```python
162
  def predict(
 
3
  from app import demo as app
4
  import os
5
 
6
+ _docs = {'PolygonAnnotator': {'description': 'Interactive polygon annotation component for visualizing and selecting polygon regions on images.', 'members': {'__init__': {'value': {'type': 'dict | None', 'default': 'None', 'description': None}, 'label': {'type': 'str | I18nData | None', 'default': 'None', 'description': None}, 'every': {'type': 'Timer | float | None', 'default': 'None', 'description': None}, 'inputs': {'type': 'Component | Sequence[Component] | set[Component] | None', 'default': 'None', 'description': None}, 'show_label': {'type': 'bool | None', 'default': 'None', 'description': None}, 'show_download_button': {'type': 'bool', 'default': 'True', 'description': None}, 'height': {'type': 'int | str | None', 'default': 'None', 'description': None}, 'width': {'type': 'int | str | None', 'default': 'None', 'description': None}, 'container': {'type': 'bool', 'default': 'True', 'description': None}, 'scale': {'type': 'int | None', 'default': 'None', 'description': None}, 'min_width': {'type': 'int', 'default': '160', 'description': None}, 'interactive': {'type': 'bool | None', 'default': 'None', 'description': None}, 'visible': {'type': 'bool | Literal["hidden"]', 'default': 'True', 'description': None}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': None}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': None}, 'render': {'type': 'bool', 'default': 'True', 'description': None}, 'key': {'type': 'int | str | tuple[int | str, ...] | None', 'default': 'None', 'description': None}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': '"value"', 'description': None}}, 'postprocess': {'value': {'type': 'dict | None', 'description': "The output data received by the component from the user's function in the backend."}}, 'preprocess': {'return': {'type': 'dict | None', 'description': "The preprocessed input data sent to the user's function in the backend."}, 'value': None}}, 'events': {'clear': {'type': None, 'default': None, 'description': 'This listener is triggered when the user clears the PolygonAnnotator using the clear button for the component.'}, 'change': {'type': None, 'default': None, 'description': 'Triggered when the value of the PolygonAnnotator changes either because of user input (e.g. a user types in a textbox) OR because of a function update (e.g. an image receives a value from the output of an event trigger). See `.input()` for a listener that is only triggered by user input.'}, 'upload': {'type': None, 'default': None, 'description': 'This listener is triggered when the user uploads a file into the PolygonAnnotator.'}, 'select': {'type': None, 'default': None, 'description': 'Event listener for when the user selects or deselects the PolygonAnnotator. Uses event data gradio.SelectData to carry `value` referring to the label of the PolygonAnnotator, and `selected` to refer to state of the PolygonAnnotator. See EventData documentation on how to use this event data'}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'PolygonAnnotator': []}}}
7
 
8
  abs_path = os.path.join(os.path.dirname(__file__), "css.css")
9
 
 
21
  # `gradio_polygonannotator`
22
 
23
  <div style="display: flex; gap: 7px;">
24
+ <a href="https://pypi.org/project/gradio_polygonannotator/" target="_blank"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/gradio_polygonannotator"></a> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
25
  </div>
26
 
27
  Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
 
41
  import gradio as gr
42
  from gradio_polygonannotator import PolygonAnnotator
43
 
 
44
  example_data = {
45
  "image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
46
  "polygons": [
47
  {
48
+ "id": "complex_header",
49
+ "coordinates": [
50
+ [150, 120],
51
+ [200, 100],
52
+ [350, 110],
53
+ [450, 95],
54
+ [600, 120],
55
+ [620, 180],
56
+ [580, 220],
57
+ [400, 240],
58
+ [250, 235],
59
+ [180, 200],
60
+ [140, 160],
61
+ ],
62
+ "color": "#FF0000",
63
+ "mask_opacity": 0.3,
64
+ "stroke_width": 1.2,
65
  "stroke_opacity": 0.8,
66
+ "selected_mask_opacity": 0.6,
67
+ "selected_stroke_opacity": 1.0,
68
+ "display_text": "Complex Header",
69
+ "display_font_size": 14,
70
+ "display_text_color": "#FFFFFF",
71
  },
72
  {
73
+ "id": "overlapping_ribbon",
74
+ "coordinates": [
75
+ [300, 150],
76
+ [550, 160],
77
+ [580, 200],
78
+ [650, 220],
79
+ [680, 280],
80
+ [620, 320],
81
+ [500, 340],
82
+ [350, 330],
83
+ [200, 310],
84
+ [180, 270],
85
+ [220, 220],
86
+ [280, 190],
87
+ ],
88
+ "color": "#00FF00",
89
+ "mask_opacity": 0.25,
90
+ "stroke_width": 1.5,
91
+ "stroke_opacity": 0.7,
92
+ "selected_mask_opacity": 0.5,
93
+ "selected_stroke_opacity": 1.0,
94
+ "display_text": "Overlapping Ribbon",
95
+ "display_font_size": 14,
96
+ "display_text_color": "#000000",
97
  },
98
  {
99
+ "id": "irregular_content",
100
+ "coordinates": [
101
+ [120, 380],
102
+ [250, 350],
103
+ [400, 370],
104
+ [550, 390],
105
+ [720, 420],
106
+ [750, 500],
107
+ [780, 650],
108
+ [720, 800],
109
+ [650, 920],
110
+ [500, 950],
111
+ [300, 940],
112
+ [150, 900],
113
+ [80, 750],
114
+ [90, 600],
115
+ [110, 450],
116
+ ],
117
+ "color": "#0000FF",
118
+ "mask_opacity": 0.2,
119
+ "stroke_width": 0.8,
120
  "stroke_opacity": 0.6,
121
+ "selected_mask_opacity": 0.4,
122
+ "selected_stroke_opacity": 0.9,
123
+ "display_text": "Irregular Content",
124
+ "display_font_size": 14,
125
+ "display_text_color": "#FFFF00",
126
  },
127
  {
128
+ "id": "star_shaped",
129
+ "coordinates": [
130
+ [400, 850],
131
+ [450, 900],
132
+ [520, 910],
133
+ [480, 970],
134
+ [500, 1040],
135
+ [400, 1000],
136
+ [300, 1040],
137
+ [320, 970],
138
+ [280, 910],
139
+ [350, 900],
140
+ ],
141
+ "color": "#FF00FF",
142
+ "mask_opacity": 0.35,
143
+ "stroke_width": 2.0,
144
+ "stroke_opacity": 0.9,
145
+ "selected_mask_opacity": 0.7,
146
+ "selected_stroke_opacity": 1.0,
147
+ "display_text": "Star Shape",
148
+ "display_font_size": 14,
149
+ "display_text_color": "#FFFFFF",
150
+ },
151
+ {
152
+ "id": "overlapping_triangle",
153
+ "coordinates": [
154
+ [480, 600],
155
+ [650, 750],
156
+ [720, 850],
157
+ [600, 920],
158
+ [450, 880],
159
+ [350, 800],
160
+ [380, 700],
161
+ ],
162
+ "color": "#FFAA00",
163
+ "mask_opacity": 0.28,
164
+ "stroke_width": 1.8,
165
+ "stroke_opacity": 0.75,
166
+ "selected_mask_opacity": 0.55,
167
+ "selected_stroke_opacity": 1.0,
168
+ "display_text": "Triangle",
169
+ "display_font_size": 14,
170
+ "display_text_color": "#000000",
171
+ },
172
+ ],
173
  }
174
 
175
+ polygon_table = [
176
+ ["complex_header", "Complex Header", "#FF0000", 0.3, 1.2, 0.8],
177
+ ["overlapping_ribbon", "Overlapping Ribbon", "#00FF00", 0.25, 1.5, 0.7],
178
+ ["irregular_content", "Irregular Content", "#0000FF", 0.2, 0.8, 0.6],
179
+ ["star_shaped", "Star Shape", "#FF00FF", 0.35, 2.0, 0.9],
180
+ ["overlapping_triangle", "Triangle", "#FFAA00", 0.28, 1.8, 0.75],
181
+ ]
182
+
183
+
184
+ def process_viewer_selection(data, evt: gr.SelectData):
185
  if evt.value and data:
186
  selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
187
+
188
+ highlighted_table = []
189
+ for row in polygon_table:
190
+ if row[0] in selected_ids:
191
+ highlighted_row = [
192
+ f"→ {row[0]} ←",
193
+ f" {row[1]} ",
194
+ f"→ {row[2]} ←",
195
+ f"→ {row[3]} ←",
196
+ f"→ {row[4]} ←",
197
+ f"→ {row[5]} ←",
198
+ ]
199
+ highlighted_table.append(highlighted_row)
200
+ else:
201
+ highlighted_table.append(row)
202
+
203
+ info_lines = [f"Selected {len(selected_ids)} polygon(s):"]
204
+ for selected_id in selected_ids:
205
+ selected_polygon = next(
206
+ (p for p in data["polygons"] if p["id"] == selected_id), None
207
+ )
208
+ if selected_polygon:
209
+ info_lines.append(
210
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
211
+ )
212
+
213
+ info_text = "\n".join(info_lines)
214
+ return info_text, highlighted_table
215
+
216
+ return "No polygons selected", polygon_table
217
+
218
+
219
+ def process_dataframe_selection(selected_data, evt: gr.SelectData):
220
+ if evt.index is not None and evt.index[0] < len(polygon_table):
221
+ selected_row = polygon_table[evt.index[0]]
222
+ polygon_id = selected_row[0]
223
+
224
+ updated_data = example_data.copy()
225
+ updated_data["selected_polygons"] = [polygon_id]
226
+
227
+ highlighted_table = []
228
+ for i, row in enumerate(polygon_table):
229
+ if i == evt.index[0]:
230
+ highlighted_row = [
231
+ f"→ {row[0]} ←",
232
+ f"→ {row[1]} ←",
233
+ f"→ {row[2]} ←",
234
+ f"→ {row[3]} ←",
235
+ f"→ {row[4]} ←",
236
+ f"→ {row[5]} ←",
237
+ ]
238
+ highlighted_table.append(highlighted_row)
239
+ else:
240
+ highlighted_table.append(row)
241
+
242
+ info_text = f"Selected polygon: {polygon_id}\nName: {selected_row[1]}\nColor: {selected_row[2]}\nMask Opacity: {selected_row[3]}\nStroke Width: {selected_row[4]}\nStroke Opacity: {selected_row[5]}"
243
+ return updated_data, info_text, highlighted_table
244
+
245
+ updated_data = example_data.copy()
246
+ updated_data["selected_polygons"] = []
247
+ return updated_data, "No polygons selected", polygon_table
248
+
249
+
250
+ def clear_selection():
251
+ updated_data = example_data.copy()
252
+ updated_data["selected_polygons"] = []
253
+ return updated_data, "No polygons selected", polygon_table
254
+
255
+
256
+ def select_polygon_by_id(polygon_id):
257
+ if not polygon_id or polygon_id.strip() == "":
258
+ updated_data = example_data.copy()
259
+ updated_data["selected_polygons"] = []
260
+ return updated_data, "No polygons selected", polygon_table
261
+
262
+ polygon_ids = [id.strip() for id in polygon_id.split(",") if id.strip()]
263
+ valid_ids = [p["id"] for p in example_data["polygons"]]
264
+
265
+ valid_selected_ids = [id for id in polygon_ids if id in valid_ids]
266
+ invalid_ids = [id for id in polygon_ids if id not in valid_ids]
267
+
268
+ if not valid_selected_ids:
269
+ updated_data = example_data.copy()
270
+ updated_data["selected_polygons"] = []
271
+ error_msg = f"Invalid polygon ID(s): {', '.join(invalid_ids)}. Valid IDs: {', '.join(valid_ids)}"
272
+ return updated_data, error_msg, polygon_table
273
+
274
+ updated_data = example_data.copy()
275
+ updated_data["selected_polygons"] = valid_selected_ids
276
+
277
+ highlighted_table = []
278
+ for row in polygon_table:
279
+ if row[0] in valid_selected_ids:
280
+ highlighted_row = [
281
+ f"→ {row[0]} ←",
282
+ f"→ {row[1]} ←",
283
+ f"→ {row[2]} ←",
284
+ f"→ {row[3]} ←",
285
+ f"→ {row[4]} ←",
286
+ f"→ {row[5]} ←",
287
+ ]
288
+ highlighted_table.append(highlighted_row)
289
+ else:
290
+ highlighted_table.append(row)
291
+
292
+ info_lines = [f"Selected {len(valid_selected_ids)} polygon(s):"]
293
+ for selected_id in valid_selected_ids:
294
+ selected_polygon = next(
295
+ (p for p in example_data["polygons"] if p["id"] == selected_id), None
296
+ )
297
+ if selected_polygon:
298
+ info_lines.append(
299
+ f"• {selected_id}: {selected_polygon['color']}, mask: {selected_polygon.get('mask_opacity', 0.2)}, stroke: {selected_polygon.get('stroke_width', 0.7)}px"
300
+ )
301
+
302
+ if invalid_ids:
303
+ info_lines.append(f"\nInvalid IDs: {', '.join(invalid_ids)}")
304
+
305
+ info_text = "\n".join(info_lines)
306
+ return updated_data, info_text, highlighted_table
307
+
308
+
309
+ def toggle_text_display(current_data, show_text):
310
+ if not current_data:
311
+ return current_data
312
+
313
+ updated_data = current_data.copy()
314
+ updated_data["polygons"] = []
315
+
316
+ for polygon in current_data.get("polygons", []):
317
+ updated_polygon = polygon.copy()
318
+ if not show_text:
319
+ updated_polygon["display_font_size"] = 0
320
+ else:
321
+ original_polygon = next(
322
+ (p for p in example_data["polygons"] if p["id"] == polygon["id"]), None
323
+ )
324
+ if original_polygon:
325
+ updated_polygon["display_text"] = original_polygon.get(
326
+ "display_text", polygon["id"]
327
+ )
328
+ updated_polygon["display_font_size"] = original_polygon.get(
329
+ "display_font_size", 14
330
+ )
331
+ updated_polygon["display_text_color"] = original_polygon.get(
332
+ "display_text_color", "#000000"
333
+ )
334
+
335
+ updated_data["polygons"].append(updated_polygon)
336
+
337
+ return updated_data
338
+
339
 
340
  with gr.Blocks() as demo:
341
  gr.Markdown(\"\"\"
342
+ # PolygonAnnotator - Advanced Interactive Demo
343
+
344
+ ## 🎮 Controls & Hotkeys
345
+
346
+ ### Selection
347
+ - **Click** on polygons or text labels to select/deselect
348
+ - **Ctrl/Cmd+Click** for multiple selection
349
+ - **Click dataframe rows** to select polygons
350
+ - **Enter polygon IDs** manually in the textbox
351
+
352
+ ### Navigation
353
+ - **Mouse Wheel** - Zoom in/out at cursor position
354
+ - **+/=** - Zoom in (10%)
355
+ - **-** - Zoom out (10%)
356
+ - **Ctrl/Cmd+0** - Reset view to original
357
+ - **Arrow Keys** - Pan view (↑↓←→)
358
+ - **Middle Mouse / Shift+Drag** - Pan view with mouse
359
+
360
+ ### Features
361
+ - **Clear button** to deselect all
362
+ - **Toggle text display** checkbox for polygon labels
363
  \"\"\")
364
 
365
  with gr.Row():
366
+ with gr.Column(scale=2):
367
+ poly_annotator = PolygonAnnotator(
368
  value=example_data,
369
+ label="Document with Interactive Polygon Annotations",
370
  height=600,
371
  )
372
 
373
  with gr.Column(scale=1):
374
  selected_info = gr.Textbox(
375
+ label="Selected Polygon Information",
376
+ lines=5,
377
+ value="Click on a polygon to see its information",
378
+ )
379
+
380
+ polygon_dataframe = gr.Dataframe(
381
+ value=polygon_table,
382
+ headers=["ID", "Name", "Color", "Mask", "Stroke W", "Stroke O"],
383
+ label="Polygon Data (Click rows to select)",
384
+ interactive=True,
385
  )
386
 
387
+ clear_button = gr.Button("🗑️ Clear All Selections", variant="secondary")
388
+
389
+ # Add text display toggle
390
+ with gr.Row():
391
+ show_text_checkbox = gr.Checkbox(
392
+ label="Show Polygon Text",
393
+ value=True,
394
+ info="Toggle text display on polygons",
395
+ )
396
+
397
+ with gr.Row():
398
+ polygon_id_input = gr.Textbox(
399
+ label="Select by Polygon ID(s)",
400
+ placeholder="Enter single ID or comma-separated IDs (e.g., 'date_line' or 'date_line, salutation')",
401
+ scale=3,
402
+ )
403
+ select_button = gr.Button("Select", variant="primary", scale=1)
404
+
405
+ gr.Markdown(\"\"\"
406
+ ### Features Demonstrated
407
+
408
+ #### 🎨 Visual Customization
409
+ - Different **mask opacity** for each polygon fill
410
+ - Variable **stroke width** (0.5px to 1.5px)
411
+ - Custom **stroke opacity** for borders
412
+ - Enhanced appearance when selected
413
+ - **Text labels** with customizable colors and sizes
414
+
415
+ #### 🖱️ Interaction Methods
416
+ 1. **Direct Click**: Click polygons in the viewer
417
+ 2. **Multi-Selection**: Ctrl/Cmd+Click for multiple
418
+ 3. **Dataframe**: Click table rows
419
+ 4. **Text Input**: Type polygon IDs
420
+ 5. **Clear All**: Reset selection
421
+ 6. **Text Toggle**: Show/hide polygon labels
422
+
423
+ #### 📝 Polygon IDs
424
+ - `date_line` - Red header area
425
+ - `salutation` - Green greeting section
426
+ - `main_text_block` - Blue main content
427
+ - `closing_signature` - Yellow signature area
428
+
429
+ #### 💡 Tips
430
+ - Hover over polygons for visual feedback
431
+ - Selected polygons have increased opacity
432
+ - Use comma-separated IDs for batch selection
433
+ - Click selected polygons to deselect them
434
+ - Toggle text display to see labels on polygons
435
+ \"\"\")
436
+
437
  # Handle selection events
438
+ poly_annotator.select(
439
+ process_viewer_selection,
440
+ inputs=[poly_annotator],
441
+ outputs=[selected_info, polygon_dataframe],
442
+ )
443
+
444
+ polygon_dataframe.select(
445
+ process_dataframe_selection,
446
+ inputs=[polygon_dataframe],
447
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
448
+ )
449
+
450
+ clear_button.click(
451
+ clear_selection, outputs=[poly_annotator, selected_info, polygon_dataframe]
452
+ )
453
+
454
+ select_button.click(
455
+ select_polygon_by_id,
456
+ inputs=[polygon_id_input],
457
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
458
+ )
459
+
460
+ # Also allow Enter key in textbox
461
+ polygon_id_input.submit(
462
+ select_polygon_by_id,
463
+ inputs=[polygon_id_input],
464
+ outputs=[poly_annotator, selected_info, polygon_dataframe],
465
+ )
466
+
467
+ # Handle text display toggle
468
+ show_text_checkbox.change(
469
+ toggle_text_display,
470
+ inputs=[poly_annotator, show_text_checkbox],
471
+ outputs=[poly_annotator],
472
  )
473
 
474
  if __name__ == "__main__":
 
504
 
505
  The code snippet below is accurate in cases where the component is used as both an input and an output.
506
 
507
+ - **As input:** Is passed, the preprocessed input data sent to the user's function in the backend.
508
+ - **As output:** Should return, the output data received by the component from the user's function in the backend.
509
 
510
  ```python
511
  def predict(
src/frontend/Index.svelte CHANGED
@@ -2,7 +2,15 @@
2
 
3
  <script lang="ts">
4
  import { onMount, onDestroy, tick } from "svelte";
5
- import * as PIXI from "pixi.js";
 
 
 
 
 
 
 
 
6
  import type { Gradio } from "@gradio/utils";
7
  import { Block, BlockLabel } from "@gradio/atoms";
8
  import { Image as ImageIcon } from "@gradio/icons";
@@ -24,6 +32,9 @@
24
  stroke_opacity?: number;
25
  selected_mask_opacity?: number;
26
  selected_stroke_opacity?: number;
 
 
 
27
  }>;
28
  selected_polygons?: string[] | null;
29
  } | null = null;
@@ -46,10 +57,20 @@
46
  }>;
47
 
48
  let canvasContainer: HTMLDivElement;
49
- let app: PIXI.Application;
50
- let imageSprite: PIXI.Sprite | null = null;
51
- let polygonGraphics: Map<string, PIXI.Graphics> = new Map();
 
 
52
  let selectedPolygonIds: string[] = [];
 
 
 
 
 
 
 
 
53
  type ImageRect = {
54
  left: number;
55
  top: number;
@@ -60,6 +81,7 @@
60
  naturalWidth: number;
61
  naturalHeight: number;
62
  };
 
63
  let imageRect: ImageRect = {
64
  left: 0,
65
  top: 0,
@@ -94,20 +116,81 @@
94
 
95
  graphics.clear();
96
  if (newSelectedIds.includes(polygonId)) {
97
- drawPolygonPath(graphics, polygon, imageSprite!, selectedMaskAlpha, strokeWidth, selectedStrokeAlpha);
 
 
 
 
 
 
98
  } else {
99
- drawPolygonPath(graphics, polygon, imageSprite!, originalMaskAlpha, strokeWidth, originalStrokeAlpha);
 
 
 
 
 
 
100
  }
101
  });
102
  }
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  async function initPixiApp() {
105
  if (!canvasContainer) return;
106
 
107
  const containerWidth = canvasContainer.clientWidth || 800;
108
  const containerHeight = canvasContainer.clientHeight || 600;
109
 
110
- app = new PIXI.Application();
111
  await app.init({
112
  width: containerWidth,
113
  height: containerHeight,
@@ -121,13 +204,171 @@
121
 
122
  app.stage.eventMode = "static";
123
  app.stage.hitArea = app.screen;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  }
125
 
126
  async function renderAnnotations() {
127
- if (!app || !value) return;
128
 
129
- app.stage.removeChildren();
130
  polygonGraphics.clear();
 
 
 
 
 
 
 
 
131
  if (value.image) {
132
  let imageUrl = "";
133
 
@@ -157,8 +398,8 @@
157
  );
158
 
159
  const loadedImage = await imageLoadPromise;
160
- const texture = PIXI.Texture.from(loadedImage);
161
- imageSprite = new PIXI.Sprite(texture);
162
 
163
  const scaleX = app.screen.width / texture.width;
164
  const scaleY = app.screen.height / texture.height;
@@ -183,9 +424,16 @@
183
  naturalHeight: texture.height,
184
  };
185
 
186
- app.stage.addChild(imageSprite);
 
 
 
 
 
 
 
 
187
  } catch (error) {
188
- console.error("Failed to load image:", error);
189
  return;
190
  }
191
  }
@@ -193,7 +441,7 @@
193
 
194
  if (value.polygons && value.polygons.length > 0 && imageSprite) {
195
  value.polygons.forEach((polygon) => {
196
- const graphics = new PIXI.Graphics();
197
 
198
  let color = 0xff0000;
199
  try {
@@ -202,36 +450,32 @@
202
  color = parseInt(colorStr, 16);
203
  }
204
  } catch (e) {
205
- console.error("Error parsing color:", e);
206
  }
207
 
208
  const polygonMaskOpacity = polygon.mask_opacity ?? 0.2;
209
  const selectedMaskAlpha = polygon.selected_mask_opacity ?? 0.5;
210
  const polygonStrokeOpacity = polygon.stroke_opacity ?? 0.6;
211
- const selectedStrokeAlpha = polygon.selected_stroke_opacity ?? 1.0;
 
212
  const strokeWidth = polygon.stroke_width ?? 0.7;
213
  const initialMaskAlpha = selectedPolygonIds.includes(polygon.id)
214
  ? selectedMaskAlpha
215
  : polygonMaskOpacity;
216
- const initialStrokeAlpha = selectedPolygonIds.includes(polygon.id)
 
 
217
  ? selectedStrokeAlpha
218
  : polygonStrokeOpacity;
219
 
220
  if (polygon.coordinates && polygon.coordinates.length > 0) {
221
- const displayCoords = polygon.coordinates.map((coord) => {
222
- return [
223
- (coord[0] / (imageRect.naturalWidth - 1)) *
224
- imageRect.width +
225
- imageRect.left,
226
- (coord[1] / (imageRect.naturalHeight - 1)) *
227
- imageRect.height +
228
- imageRect.top,
229
- ];
230
- });
231
-
232
- graphics.poly(displayCoords.flat());
233
- graphics.fill({ color: color, alpha: initialMaskAlpha });
234
- graphics.stroke({ width: strokeWidth, color: color, alpha: initialStrokeAlpha });
235
  }
236
 
237
  graphics.eventMode = "static";
@@ -239,7 +483,10 @@
239
 
240
  const originalMaskAlpha = polygonMaskOpacity;
241
  const hoverMaskAlpha = Math.min(polygonMaskOpacity + 0.1, 1.0);
242
- const hoverStrokeAlpha = Math.min(polygonStrokeOpacity + 0.2, 1.0);
 
 
 
243
 
244
  graphics.on("pointerover", () => {
245
  if (!selectedPolygonIds.includes(polygon.id)) {
@@ -247,7 +494,6 @@
247
  drawPolygonPath(
248
  graphics,
249
  polygon,
250
- imageSprite!,
251
  hoverMaskAlpha,
252
  strokeWidth,
253
  hoverStrokeAlpha,
@@ -261,7 +507,6 @@
261
  drawPolygonPath(
262
  graphics,
263
  polygon,
264
- imageSprite!,
265
  originalMaskAlpha,
266
  strokeWidth,
267
  polygonStrokeOpacity,
@@ -270,75 +515,75 @@
270
  });
271
 
272
  graphics.on("pointerdown", (event) => {
273
- // Check if Ctrl/Cmd key is held for multi-selection
274
- const isMultiSelect = event.ctrlKey || event.metaKey;
275
 
276
- if (selectedPolygonIds.includes(polygon.id)) {
277
- // Deselect this polygon
278
- const newSelectedIds = selectedPolygonIds.filter(
279
- (id) => id !== polygon.id,
280
- );
281
- updateSelection(newSelectedIds);
282
- selectedPolygonIds = newSelectedIds;
283
-
284
- // Dispatch deselection event to Gradio
285
- gradio.dispatch("select", {
286
- index:
287
- newSelectedIds.length > 0
288
- ? value.polygons.findIndex(
289
- (p) =>
290
- p.id ===
291
- newSelectedIds[
292
- newSelectedIds.length - 1
293
- ],
294
- )
295
- : null,
296
- value:
297
- newSelectedIds.length > 0
298
- ? newSelectedIds
299
- : null,
300
- });
301
- return;
302
- }
303
 
304
- // Select polygon
305
- let newSelectedIds: string[];
306
- if (isMultiSelect) {
307
- // Add to existing selection
308
- newSelectedIds = [...selectedPolygonIds, polygon.id];
309
- } else {
310
- // Replace selection
311
- newSelectedIds = [polygon.id];
312
- }
313
 
314
- updateSelection(newSelectedIds);
315
- selectedPolygonIds = newSelectedIds;
316
 
317
- // Dispatch select event to Gradio
318
- gradio.dispatch("select", {
319
- index: value.polygons.findIndex(
320
- (p) => p.id === polygon.id,
321
- ),
322
- value: newSelectedIds,
323
  });
324
- });
325
 
326
- app.stage.addChild(graphics);
327
- polygonGraphics.set(polygon.id, graphics);
 
328
  });
329
  }
330
  }
331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  function drawPolygonPath(
333
- graphics: PIXI.Graphics,
334
  polygon: any,
335
- imageSprite: PIXI.Sprite,
336
  maskAlpha: number = 0.2,
337
  strokeWidth: number = 0.7,
338
  strokeAlpha: number = 0.6,
339
  ) {
340
  if (polygon.coordinates && polygon.coordinates.length > 0) {
341
- // Transform coordinates from natural image space to display space
342
  const displayCoords = polygon.coordinates.map((coord: number[]) => {
343
  return [
344
  (coord[0] / (imageRect.naturalWidth - 1)) *
@@ -357,13 +602,16 @@
357
  color = parseInt(colorStr, 16);
358
  }
359
  } catch (e) {
360
- console.error("Error parsing color in drawPolygonPath:", e);
361
  }
362
 
363
- // Use Pixi.js 8 drawing API
364
  graphics.poly(displayCoords.flat());
365
  graphics.fill({ color: color, alpha: maskAlpha });
366
- graphics.stroke({ width: strokeWidth, color: color, alpha: strokeAlpha });
 
 
 
 
367
  }
368
  }
369
 
@@ -377,11 +625,12 @@
377
 
378
  onDestroy(() => {
379
  if (app) {
 
 
380
  app.destroy(true, { children: true, texture: true });
381
  }
382
  });
383
 
384
- // Handle canvas resize
385
  async function handleResize() {
386
  if (!canvasContainer || !app) return;
387
 
@@ -390,7 +639,6 @@
390
 
391
  if (newWidth !== app.screen.width || newHeight !== app.screen.height) {
392
  app.renderer.resize(newWidth, newHeight);
393
- // Re-render annotations with updated canvas dimensions
394
  await renderAnnotations();
395
  }
396
  }
 
2
 
3
  <script lang="ts">
4
  import { onMount, onDestroy, tick } from "svelte";
5
+ import {
6
+ Application,
7
+ Container,
8
+ Graphics,
9
+ Text,
10
+ Sprite,
11
+ Texture,
12
+ TextStyle,
13
+ } from "pixi.js";
14
  import type { Gradio } from "@gradio/utils";
15
  import { Block, BlockLabel } from "@gradio/atoms";
16
  import { Image as ImageIcon } from "@gradio/icons";
 
32
  stroke_opacity?: number;
33
  selected_mask_opacity?: number;
34
  selected_stroke_opacity?: number;
35
+ display_text?: string | null;
36
+ display_font_size?: number | null;
37
+ display_text_color?: string;
38
  }>;
39
  selected_polygons?: string[] | null;
40
  } | null = null;
 
57
  }>;
58
 
59
  let canvasContainer: HTMLDivElement;
60
+ let app: Application;
61
+ let imageSprite: Sprite | null = null;
62
+ let polygonGraphics: Map<string, Graphics> = new Map();
63
+ let polygonTexts: Map<string, Text> = new Map();
64
+ let textContainer: Container | null = null;
65
  let selectedPolygonIds: string[] = [];
66
+ let viewportContainer: Container | null = null;
67
+ let isDragging = false;
68
+ let lastPointerPosition = { x: 0, y: 0 };
69
+ let initialScale = 1;
70
+ let initialPosition = { x: 0, y: 0 };
71
+ let minScale = 0.5;
72
+ let maxScale = 3;
73
+
74
  type ImageRect = {
75
  left: number;
76
  top: number;
 
81
  naturalWidth: number;
82
  naturalHeight: number;
83
  };
84
+
85
  let imageRect: ImageRect = {
86
  left: 0,
87
  top: 0,
 
116
 
117
  graphics.clear();
118
  if (newSelectedIds.includes(polygonId)) {
119
+ drawPolygonPath(
120
+ graphics,
121
+ polygon,
122
+ selectedMaskAlpha,
123
+ strokeWidth,
124
+ selectedStrokeAlpha,
125
+ );
126
  } else {
127
+ drawPolygonPath(
128
+ graphics,
129
+ polygon,
130
+ originalMaskAlpha,
131
+ strokeWidth,
132
+ originalStrokeAlpha,
133
+ );
134
  }
135
  });
136
  }
137
 
138
+ function handlePolygonSelection(polygonId: string, event: any) {
139
+ if (!value) return;
140
+
141
+ const isMultiSelect = event.ctrlKey || event.metaKey;
142
+
143
+ if (selectedPolygonIds.includes(polygonId)) {
144
+ const newSelectedIds = selectedPolygonIds.filter(
145
+ (id) => id !== polygonId,
146
+ );
147
+ updateSelection(newSelectedIds);
148
+ selectedPolygonIds = newSelectedIds;
149
+
150
+ gradio.dispatch("select", {
151
+ index:
152
+ newSelectedIds.length > 0
153
+ ? value.polygons.findIndex(
154
+ (p) =>
155
+ p.id ===
156
+ newSelectedIds[
157
+ newSelectedIds.length - 1
158
+ ],
159
+ )
160
+ : null,
161
+ value:
162
+ newSelectedIds.length > 0
163
+ ? newSelectedIds
164
+ : null,
165
+ });
166
+ return;
167
+ }
168
+
169
+ let newSelectedIds: string[];
170
+ if (isMultiSelect) {
171
+ newSelectedIds = [...selectedPolygonIds, polygonId];
172
+ } else {
173
+ newSelectedIds = [polygonId];
174
+ }
175
+
176
+ updateSelection(newSelectedIds);
177
+ selectedPolygonIds = newSelectedIds;
178
+
179
+ gradio.dispatch("select", {
180
+ index: value.polygons.findIndex(
181
+ (p) => p.id === polygonId,
182
+ ),
183
+ value: newSelectedIds,
184
+ });
185
+ }
186
+
187
  async function initPixiApp() {
188
  if (!canvasContainer) return;
189
 
190
  const containerWidth = canvasContainer.clientWidth || 800;
191
  const containerHeight = canvasContainer.clientHeight || 600;
192
 
193
+ app = new Application();
194
  await app.init({
195
  width: containerWidth,
196
  height: containerHeight,
 
204
 
205
  app.stage.eventMode = "static";
206
  app.stage.hitArea = app.screen;
207
+
208
+ viewportContainer = new Container();
209
+ viewportContainer.eventMode = "static";
210
+ app.stage.addChild(viewportContainer);
211
+
212
+ setupControls();
213
+ }
214
+
215
+ function setupControls() {
216
+ if (!app || !viewportContainer) return;
217
+
218
+ window.addEventListener("keydown", handleKeydown);
219
+
220
+ app.stage.on("pointerdown", (event) => {
221
+ if (event.button === 1 || (event.button === 0 && event.shiftKey)) {
222
+ isDragging = true;
223
+ lastPointerPosition = { x: event.global.x, y: event.global.y };
224
+ app.canvas.style.cursor = "grabbing";
225
+ }
226
+ });
227
+
228
+ app.stage.on("pointermove", (event) => {
229
+ if (isDragging && viewportContainer) {
230
+ const dx = event.global.x - lastPointerPosition.x;
231
+ const dy = event.global.y - lastPointerPosition.y;
232
+
233
+ viewportContainer.x += dx;
234
+ viewportContainer.y += dy;
235
+
236
+ lastPointerPosition = { x: event.global.x, y: event.global.y };
237
+ }
238
+ });
239
+
240
+ app.stage.on("pointerup", () => {
241
+ isDragging = false;
242
+ app.canvas.style.cursor = "default";
243
+ });
244
+
245
+ app.stage.on("pointerupoutside", () => {
246
+ isDragging = false;
247
+ app.canvas.style.cursor = "default";
248
+ });
249
+
250
+ app.canvas.addEventListener("wheel", handleWheel, { passive: false });
251
+ }
252
+
253
+ function handleKeydown(event: KeyboardEvent) {
254
+ if (!viewportContainer) return;
255
+
256
+ switch(event.key) {
257
+ case "=":
258
+ case "+":
259
+ zoomIn();
260
+ event.preventDefault();
261
+ break;
262
+ case "-":
263
+ case "_":
264
+ zoomOut();
265
+ event.preventDefault();
266
+ break;
267
+ case "0":
268
+ if (event.ctrlKey || event.metaKey) {
269
+ resetView();
270
+ event.preventDefault();
271
+ }
272
+ break;
273
+ case "ArrowLeft":
274
+ viewportContainer!.x += 20;
275
+ event.preventDefault();
276
+ break;
277
+ case "ArrowRight":
278
+ viewportContainer!.x -= 20;
279
+ event.preventDefault();
280
+ break;
281
+ case "ArrowUp":
282
+ viewportContainer!.y += 20;
283
+ event.preventDefault();
284
+ break;
285
+ case "ArrowDown":
286
+ viewportContainer!.y -= 20;
287
+ event.preventDefault();
288
+ break;
289
+ }
290
+ }
291
+
292
+ function handleWheel(event: WheelEvent) {
293
+ if (!viewportContainer || !app) return;
294
+
295
+ event.preventDefault();
296
+
297
+ const delta = event.deltaY < 0 ? 1.05 : 0.95;
298
+ const newScale = viewportContainer!.scale.x * delta;
299
+
300
+ if (newScale >= minScale && newScale <= maxScale) {
301
+ const worldPos = {
302
+ x: (event.offsetX - viewportContainer!.x) / viewportContainer!.scale.x,
303
+ y: (event.offsetY - viewportContainer!.y) / viewportContainer!.scale.y,
304
+ };
305
+
306
+ viewportContainer!.scale.x = newScale;
307
+ viewportContainer!.scale.y = newScale;
308
+
309
+ viewportContainer!.x = event.offsetX - worldPos.x * newScale;
310
+ viewportContainer!.y = event.offsetY - worldPos.y * newScale;
311
+ }
312
+ }
313
+
314
+ function zoomIn() {
315
+ if (!viewportContainer || !app) return;
316
+
317
+ const newScale = Math.min(viewportContainer!.scale.x * 1.1, maxScale);
318
+ const center = { x: app.screen.width / 2, y: app.screen.height / 2 };
319
+
320
+ const worldPos = {
321
+ x: (center.x - viewportContainer!.x) / viewportContainer!.scale.x,
322
+ y: (center.y - viewportContainer!.y) / viewportContainer!.scale.y,
323
+ };
324
+
325
+ viewportContainer!.scale.x = newScale;
326
+ viewportContainer!.scale.y = newScale;
327
+
328
+ viewportContainer!.x = center.x - worldPos.x * newScale;
329
+ viewportContainer!.y = center.y - worldPos.y * newScale;
330
+ }
331
+
332
+ function zoomOut() {
333
+ if (!viewportContainer || !app) return;
334
+
335
+ const newScale = Math.max(viewportContainer!.scale.x * 0.9, minScale);
336
+ const center = { x: app.screen.width / 2, y: app.screen.height / 2 };
337
+
338
+ const worldPos = {
339
+ x: (center.x - viewportContainer!.x) / viewportContainer!.scale.x,
340
+ y: (center.y - viewportContainer!.y) / viewportContainer!.scale.y,
341
+ };
342
+
343
+ viewportContainer!.scale.x = newScale;
344
+ viewportContainer!.scale.y = newScale;
345
+
346
+ viewportContainer!.x = center.x - worldPos.x * newScale;
347
+ viewportContainer!.y = center.y - worldPos.y * newScale;
348
+ }
349
+
350
+ function resetView() {
351
+ if (!viewportContainer) return;
352
+
353
+ viewportContainer!.scale.x = initialScale;
354
+ viewportContainer!.scale.y = initialScale;
355
+ viewportContainer!.x = initialPosition.x;
356
+ viewportContainer!.y = initialPosition.y;
357
  }
358
 
359
  async function renderAnnotations() {
360
+ if (!app || !value || !viewportContainer) return;
361
 
362
+ viewportContainer!.removeChildren();
363
  polygonGraphics.clear();
364
+ polygonTexts.forEach((text) => text.destroy());
365
+ polygonTexts.clear();
366
+
367
+ textContainer = new Container();
368
+ textContainer.zIndex = 1000;
369
+ viewportContainer!.sortableChildren = true;
370
+ viewportContainer!.addChild(textContainer);
371
+
372
  if (value.image) {
373
  let imageUrl = "";
374
 
 
398
  );
399
 
400
  const loadedImage = await imageLoadPromise;
401
+ const texture = Texture.from(loadedImage);
402
+ imageSprite = new Sprite(texture);
403
 
404
  const scaleX = app.screen.width / texture.width;
405
  const scaleY = app.screen.height / texture.height;
 
424
  naturalHeight: texture.height,
425
  };
426
 
427
+ viewportContainer!.addChild(imageSprite);
428
+
429
+ initialScale = 1;
430
+ initialPosition.x = 0;
431
+ initialPosition.y = 0;
432
+ viewportContainer!.scale.x = 1;
433
+ viewportContainer!.scale.y = 1;
434
+ viewportContainer!.x = 0;
435
+ viewportContainer!.y = 0;
436
  } catch (error) {
 
437
  return;
438
  }
439
  }
 
441
 
442
  if (value.polygons && value.polygons.length > 0 && imageSprite) {
443
  value.polygons.forEach((polygon) => {
444
+ const graphics = new Graphics();
445
 
446
  let color = 0xff0000;
447
  try {
 
450
  color = parseInt(colorStr, 16);
451
  }
452
  } catch (e) {
453
+ color = 0xff0000;
454
  }
455
 
456
  const polygonMaskOpacity = polygon.mask_opacity ?? 0.2;
457
  const selectedMaskAlpha = polygon.selected_mask_opacity ?? 0.5;
458
  const polygonStrokeOpacity = polygon.stroke_opacity ?? 0.6;
459
+ const selectedStrokeAlpha =
460
+ polygon.selected_stroke_opacity ?? 1.0;
461
  const strokeWidth = polygon.stroke_width ?? 0.7;
462
  const initialMaskAlpha = selectedPolygonIds.includes(polygon.id)
463
  ? selectedMaskAlpha
464
  : polygonMaskOpacity;
465
+ const initialStrokeAlpha = selectedPolygonIds.includes(
466
+ polygon.id,
467
+ )
468
  ? selectedStrokeAlpha
469
  : polygonStrokeOpacity;
470
 
471
  if (polygon.coordinates && polygon.coordinates.length > 0) {
472
+ drawPolygonPath(
473
+ graphics,
474
+ polygon,
475
+ initialMaskAlpha,
476
+ strokeWidth,
477
+ initialStrokeAlpha,
478
+ );
 
 
 
 
 
 
 
479
  }
480
 
481
  graphics.eventMode = "static";
 
483
 
484
  const originalMaskAlpha = polygonMaskOpacity;
485
  const hoverMaskAlpha = Math.min(polygonMaskOpacity + 0.1, 1.0);
486
+ const hoverStrokeAlpha = Math.min(
487
+ polygonStrokeOpacity + 0.2,
488
+ 1.0,
489
+ );
490
 
491
  graphics.on("pointerover", () => {
492
  if (!selectedPolygonIds.includes(polygon.id)) {
 
494
  drawPolygonPath(
495
  graphics,
496
  polygon,
 
497
  hoverMaskAlpha,
498
  strokeWidth,
499
  hoverStrokeAlpha,
 
507
  drawPolygonPath(
508
  graphics,
509
  polygon,
 
510
  originalMaskAlpha,
511
  strokeWidth,
512
  polygonStrokeOpacity,
 
515
  });
516
 
517
  graphics.on("pointerdown", (event) => {
518
+ handlePolygonSelection(polygon.id, event);
519
+ });
520
 
521
+ viewportContainer!.addChild(graphics);
522
+ polygonGraphics.set(polygon.id, graphics);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
 
524
+ if (polygon.display_text && polygon.display_font_size && polygon.display_font_size > 0) {
525
+ const text = createPolygonText(polygon);
526
+ const center = calculatePolygonCenter(polygon.coordinates);
527
+
528
+ text.anchor.set(0.5, 0.5);
529
+ text.x = center.x;
530
+ text.y = center.y;
 
 
531
 
532
+ text.eventMode = "static";
533
+ text.cursor = "pointer";
534
 
535
+ text.on("pointerdown", (event) => {
536
+ handlePolygonSelection(polygon.id, event);
 
 
 
 
537
  });
 
538
 
539
+ textContainer!.addChild(text);
540
+ polygonTexts.set(polygon.id, text);
541
+ }
542
  });
543
  }
544
  }
545
 
546
+ function createPolygonText(polygon: any): Text {
547
+ const style = new TextStyle({
548
+ fontSize: polygon.display_font_size || 14,
549
+ fill: polygon.display_text_color || "#000000",
550
+ fontWeight: "bold",
551
+ align: "center"
552
+ });
553
+
554
+ return new Text({
555
+ text: polygon.display_text,
556
+ style: style
557
+ });
558
+ }
559
+
560
+ function calculatePolygonCenter(coordinates: number[][]): { x: number; y: number } {
561
+ const displayCoords = coordinates.map((coord) => [
562
+ (coord[0] / (imageRect.naturalWidth - 1)) * imageRect.width + imageRect.left,
563
+ (coord[1] / (imageRect.naturalHeight - 1)) * imageRect.height + imageRect.top,
564
+ ]);
565
+
566
+ let centerX = 0;
567
+ let centerY = 0;
568
+ displayCoords.forEach((coord) => {
569
+ centerX += coord[0];
570
+ centerY += coord[1];
571
+ });
572
+
573
+ return {
574
+ x: centerX / displayCoords.length,
575
+ y: centerY / displayCoords.length
576
+ };
577
+ }
578
+
579
  function drawPolygonPath(
580
+ graphics: Graphics,
581
  polygon: any,
 
582
  maskAlpha: number = 0.2,
583
  strokeWidth: number = 0.7,
584
  strokeAlpha: number = 0.6,
585
  ) {
586
  if (polygon.coordinates && polygon.coordinates.length > 0) {
 
587
  const displayCoords = polygon.coordinates.map((coord: number[]) => {
588
  return [
589
  (coord[0] / (imageRect.naturalWidth - 1)) *
 
602
  color = parseInt(colorStr, 16);
603
  }
604
  } catch (e) {
605
+ color = 0xff0000;
606
  }
607
 
 
608
  graphics.poly(displayCoords.flat());
609
  graphics.fill({ color: color, alpha: maskAlpha });
610
+ graphics.stroke({
611
+ width: strokeWidth,
612
+ color: color,
613
+ alpha: strokeAlpha,
614
+ });
615
  }
616
  }
617
 
 
625
 
626
  onDestroy(() => {
627
  if (app) {
628
+ app.canvas.removeEventListener("wheel", handleWheel);
629
+ window.removeEventListener("keydown", handleKeydown);
630
  app.destroy(true, { children: true, texture: true });
631
  }
632
  });
633
 
 
634
  async function handleResize() {
635
  if (!canvasContainer || !app) return;
636
 
 
639
 
640
  if (newWidth !== app.screen.width || newHeight !== app.screen.height) {
641
  app.renderer.resize(newWidth, newHeight);
 
642
  await renderAnnotations();
643
  }
644
  }
src/pyproject.toml CHANGED
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
 
9
  [project]
10
  name = "gradio_polygonannotator"
11
- version = "0.1.0"
12
  description = "Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance"
13
  readme = "README.md"
14
  license = "Apache-2.0"
 
8
 
9
  [project]
10
  name = "gradio_polygonannotator"
11
+ version = "0.2.0"
12
  description = "Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance"
13
  readme = "README.md"
14
  license = "Apache-2.0"