diff --git a/Entech Plating/fusion_tasks/__init__.py b/Entech Plating/fusion_tasks/__init__.py new file mode 100644 index 00000000..86043519 --- /dev/null +++ b/Entech Plating/fusion_tasks/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from . import models + + +def _fusion_tasks_post_init(env): + """Post-install hook for fusion_tasks. + + 1. Sets default ICP values (upsert - safe if keys already exist). + 2. Adds all active internal users to group_field_technician so + the Field Service menus are visible immediately after install. + """ + ICP = env['ir.config_parameter'].sudo() + defaults = { + 'fusion_claims.google_maps_api_key': '', + 'fusion_claims.store_open_hour': '9.0', + 'fusion_claims.store_close_hour': '18.0', + 'fusion_claims.push_enabled': 'False', + 'fusion_claims.push_advance_minutes': '30', + 'fusion_claims.sync_instance_id': '', + 'fusion_claims.technician_start_address': '', + } + for key, default_value in defaults.items(): + if not ICP.get_param(key): + ICP.set_param(key, default_value) + + # Add all active internal users to Field Technician group + ft_group = env.ref('fusion_tasks.group_field_technician', raise_if_not_found=False) + if ft_group: + internal_users = env['res.users'].search([ + ('active', '=', True), + ('share', '=', False), + ]) + ft_group.write({'user_ids': [(4, u.id) for u in internal_users]}) diff --git a/Entech Plating/fusion_tasks/__manifest__.py b/Entech Plating/fusion_tasks/__manifest__.py new file mode 100644 index 00000000..717e31ce --- /dev/null +++ b/Entech Plating/fusion_tasks/__manifest__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +{ + 'name': 'Fusion Tasks', + 'version': '19.0.1.0.0', + 'category': 'Services/Field Service', + 'summary': 'Technician scheduling, route planning, GPS tracking, and cross-instance sync.', + 'author': 'Nexa Systems Inc.', + 'website': 'https://www.nexasystems.ca', + 'license': 'OPL-1', + 'depends': [ + 'base', + 'mail', + 'calendar', + 'sales_team', + ], + 'data': [ + 'security/security.xml', + 'security/ir.model.access.csv', + 'data/ir_cron_data.xml', + 'views/technician_task_views.xml', + 'views/task_sync_views.xml', + 'views/technician_location_views.xml', + 'views/res_config_settings_views.xml', + ], + 'post_init_hook': '_fusion_tasks_post_init', + 'assets': { + 'web.assets_backend': [ + 'fusion_tasks/static/src/css/fusion_task_map_view.scss', + 'fusion_tasks/static/src/js/fusion_task_map_view.js', + 'fusion_tasks/static/src/xml/fusion_task_map_view.xml', + ], + }, + 'installable': True, + 'application': True, +} diff --git a/Entech Plating/fusion_tasks/__pycache__/__init__.cpython-312.pyc b/Entech Plating/fusion_tasks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..a097d621 Binary files /dev/null and b/Entech Plating/fusion_tasks/__pycache__/__init__.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/__pycache__/__manifest__.cpython-312.pyc b/Entech Plating/fusion_tasks/__pycache__/__manifest__.cpython-312.pyc new file mode 100644 index 00000000..592deb73 Binary files /dev/null and b/Entech Plating/fusion_tasks/__pycache__/__manifest__.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/data/ir_config_parameter_data.xml b/Entech Plating/fusion_tasks/data/ir_config_parameter_data.xml new file mode 100644 index 00000000..6ca041be --- /dev/null +++ b/Entech Plating/fusion_tasks/data/ir_config_parameter_data.xml @@ -0,0 +1,50 @@ + + + + + + + + fusion_claims.google_maps_api_key + + + + + + fusion_claims.store_open_hour + 9.0 + + + fusion_claims.store_close_hour + 18.0 + + + + + fusion_claims.push_enabled + False + + + fusion_claims.push_advance_minutes + 30 + + + + + fusion_claims.sync_instance_id + + + + + + fusion_claims.technician_start_address + + + + + diff --git a/Entech Plating/fusion_tasks/data/ir_cron_data.xml b/Entech Plating/fusion_tasks/data/ir_cron_data.xml new file mode 100644 index 00000000..abdd009b --- /dev/null +++ b/Entech Plating/fusion_tasks/data/ir_cron_data.xml @@ -0,0 +1,78 @@ + + + + + + + + Fusion Tasks: Calculate Technician Travel Times + + code + model._cron_calculate_travel_times() + 15 + minutes + True + + + + + Fusion Tasks: Technician Push Notifications + + code + model._cron_send_push_notifications() + 15 + minutes + True + + + + + Fusion Tasks: Sync Remote Tasks (Pull) + + code + model._cron_pull_remote_tasks() + 2 + minutes + True + + + + + Fusion Tasks: Cleanup Old Shadow Tasks + + code + model._cron_cleanup_old_shadows() + 1 + days + True + + + + + + Fusion Tasks: Check Late Technician Arrivals + + code + model._cron_check_late_arrivals() + 10 + minutes + True + + + + + Fusion Tasks: Cleanup Old Locations + + code + model._cron_cleanup_old_locations() + 1 + days + True + + + + + diff --git a/Entech Plating/fusion_tasks/models/__init__.py b/Entech Plating/fusion_tasks/models/__init__.py new file mode 100644 index 00000000..ecfb3fa6 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/__init__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from . import email_builder_mixin +from . import res_partner +from . import res_company +from . import res_users +from . import res_config_settings +from . import technician_task +from . import task_sync +from . import technician_location +from . import push_subscription diff --git a/Entech Plating/fusion_tasks/models/__pycache__/__init__.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..0765f79a Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/email_builder_mixin.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/email_builder_mixin.cpython-312.pyc new file mode 100644 index 00000000..4e1eacc5 Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/email_builder_mixin.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/push_subscription.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/push_subscription.cpython-312.pyc new file mode 100644 index 00000000..2980dc10 Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/push_subscription.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/res_company.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/res_company.cpython-312.pyc new file mode 100644 index 00000000..373fb797 Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/res_company.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/res_config_settings.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/res_config_settings.cpython-312.pyc new file mode 100644 index 00000000..d668d74f Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/res_config_settings.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/res_partner.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/res_partner.cpython-312.pyc new file mode 100644 index 00000000..ba2e9bde Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/res_partner.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/res_users.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/res_users.cpython-312.pyc new file mode 100644 index 00000000..bd1effcc Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/res_users.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/task_sync.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/task_sync.cpython-312.pyc new file mode 100644 index 00000000..2e88cca1 Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/task_sync.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/technician_location.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/technician_location.cpython-312.pyc new file mode 100644 index 00000000..12a1e8ed Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/technician_location.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/__pycache__/technician_task.cpython-312.pyc b/Entech Plating/fusion_tasks/models/__pycache__/technician_task.cpython-312.pyc new file mode 100644 index 00000000..1b16b45b Binary files /dev/null and b/Entech Plating/fusion_tasks/models/__pycache__/technician_task.cpython-312.pyc differ diff --git a/Entech Plating/fusion_tasks/models/email_builder_mixin.py b/Entech Plating/fusion_tasks/models/email_builder_mixin.py new file mode 100644 index 00000000..a762dafe --- /dev/null +++ b/Entech Plating/fusion_tasks/models/email_builder_mixin.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- +# Fusion Claims - Professional Email Builder Mixin +# Provides consistent, dark/light mode safe email templates across all modules. + +from odoo import models + + +class FusionEmailBuilderMixin(models.AbstractModel): + _name = 'fusion.email.builder.mixin' + _description = 'Fusion Email Builder Mixin' + + # ------------------------------------------------------------------ + # Color constants + # ------------------------------------------------------------------ + _EMAIL_COLORS = { + 'info': '#2B6CB0', + 'success': '#38a169', + 'attention': '#d69e2e', + 'urgent': '#c53030', + } + + # ------------------------------------------------------------------ + # Public API + # ------------------------------------------------------------------ + + def _email_build( + self, + title, + summary, + sections=None, + note=None, + note_color=None, + email_type='info', + attachments_note=None, + button_url=None, + button_text='View Case Details', + sender_name=None, + extra_html='', + ): + """Build a complete professional email HTML string. + + Args: + title: Email heading (e.g. "Application Approved") + summary: One-sentence summary HTML (may contain tags) + sections: list of (heading, rows) where rows is list of (label, value) + e.g. [('Case Details', [('Client', 'John'), ('Case', 'S30073')])] + note: Optional note/next-steps text (plain or HTML) + note_color: Override left-border color for note (default uses email_type) + email_type: 'info' | 'success' | 'attention' | 'urgent' + attachments_note: Optional string listing attached files + button_url: Optional CTA button URL + button_text: CTA button label + sender_name: Name for sign-off (defaults to current user) + extra_html: Any additional HTML to insert before sign-off + """ + accent = self._EMAIL_COLORS.get(email_type, self._EMAIL_COLORS['info']) + company = self._get_company_info() + + parts = [] + # -- Wrapper open + accent bar (no forced bg/color so it adapts to dark/light) + parts.append( + f'
' + f'
' + f'
' + ) + + # -- Company name (accent color works in both themes) + parts.append( + f'

{company["name"]}

' + ) + + # -- Title (inherits text color from container) + parts.append( + f'

{title}

' + ) + + # -- Summary (muted via opacity) + parts.append( + f'

{summary}

' + ) + + # -- Sections (details tables) + if sections: + for heading, rows in sections: + parts.append(self._email_section(heading, rows)) + + # -- Note / Next Steps + if note: + nc = note_color or accent + parts.append(self._email_note(note, nc)) + + # -- Extra HTML + if extra_html: + parts.append(extra_html) + + # -- Attachment note + if attachments_note: + parts.append(self._email_attachment_note(attachments_note)) + + # -- CTA Button + if button_url: + parts.append(self._email_button(button_url, button_text, accent)) + + # -- Sign-off + signer = sender_name or (self.env.user.name if self.env.user else '') + parts.append( + f'

' + f'Best regards,
' + f'{signer}
' + f'{company["name"]}

' + ) + + # -- Close content card + parts.append('
') + + # -- Footer + footer_parts = [company['name']] + if company['phone']: + footer_parts.append(company['phone']) + if company['email']: + footer_parts.append(company['email']) + footer_text = ' · '.join(footer_parts) + + parts.append( + f'
' + f'

' + f'{footer_text}
' + f'This is an automated notification from the ADP Claims Management System.

' + f'
' + ) + + # -- Close wrapper + parts.append('
') + + return ''.join(parts) + + # ------------------------------------------------------------------ + # Building blocks + # ------------------------------------------------------------------ + + def _email_section(self, heading, rows): + """Build a labeled details table section. + + Args: + heading: Section title (e.g. "Case Details") + rows: list of (label, value) tuples. Value can be plain text or HTML. + """ + if not rows: + return '' + + html = ( + '' + f'' + ) + + for label, value in rows: + if value is None or value == '' or value is False: + continue + html += ( + f'' + f'' + f'' + f'' + ) + + html += '
{heading}
{label}{value}
' + return html + + def _email_note(self, text, color='#2B6CB0'): + """Build a left-border accent note block.""" + return ( + f'
' + f'

{text}

' + f'
' + ) + + def _email_button(self, url, text='View Case Details', color='#2B6CB0'): + """Build a centered CTA button.""" + return ( + f'

' + f'{text}

' + ) + + def _email_attachment_note(self, description): + """Build a dashed-border attachment callout. + + Args: + description: e.g. "ADP Application (PDF), XML Data File" + """ + return ( + f'
' + f'

' + f'Attached: {description}

' + f'
' + ) + + def _email_status_badge(self, label, color='#2B6CB0'): + """Return an inline status badge/pill HTML snippet.""" + bg_map = { + '#38a169': 'rgba(56,161,105,0.12)', + '#2B6CB0': 'rgba(43,108,176,0.12)', + '#d69e2e': 'rgba(214,158,46,0.12)', + '#c53030': 'rgba(197,48,48,0.12)', + } + bg = bg_map.get(color, 'rgba(43,108,176,0.12)') + return ( + f'' + f'{label}' + ) + + # ------------------------------------------------------------------ + # Helpers + # ------------------------------------------------------------------ + + def _get_company_info(self): + """Return company name, phone, email for email templates.""" + company = getattr(self, 'company_id', None) or self.env.company + return { + 'name': company.name or 'Our Company', + 'phone': company.phone or '', + 'email': company.email or '', + } + + def _email_is_enabled(self): + """Check if email notifications are enabled in settings.""" + ICP = self.env['ir.config_parameter'].sudo() + val = ICP.get_param('fusion_claims.enable_email_notifications', 'True') + return val.lower() in ('true', '1', 'yes') diff --git a/Entech Plating/fusion_tasks/models/push_subscription.py b/Entech Plating/fusion_tasks/models/push_subscription.py new file mode 100644 index 00000000..19f90338 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/push_subscription.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +""" +Web Push Subscription model for storing browser push notification subscriptions. +""" + +from odoo import models, fields, api +import logging + +_logger = logging.getLogger(__name__) + + +class FusionPushSubscription(models.Model): + _name = 'fusion.push.subscription' + _description = 'Web Push Subscription' + _order = 'create_date desc' + + user_id = fields.Many2one( + 'res.users', + string='User', + required=True, + ondelete='cascade', + index=True, + ) + endpoint = fields.Text( + string='Endpoint URL', + required=True, + ) + p256dh_key = fields.Text( + string='P256DH Key', + required=True, + ) + auth_key = fields.Text( + string='Auth Key', + required=True, + ) + browser_info = fields.Char( + string='Browser Info', + help='User agent or browser identification', + ) + active = fields.Boolean( + default=True, + ) + + _constraints = [ + models.Constraint( + 'unique(endpoint)', + 'This push subscription endpoint already exists.', + ), + ] + + @api.model + def register_subscription(self, user_id, endpoint, p256dh_key, auth_key, browser_info=None): + """Register or update a push subscription.""" + existing = self.sudo().search([('endpoint', '=', endpoint)], limit=1) + if existing: + existing.write({ + 'user_id': user_id, + 'p256dh_key': p256dh_key, + 'auth_key': auth_key, + 'browser_info': browser_info or existing.browser_info, + 'active': True, + }) + return existing + return self.sudo().create({ + 'user_id': user_id, + 'endpoint': endpoint, + 'p256dh_key': p256dh_key, + 'auth_key': auth_key, + 'browser_info': browser_info, + }) diff --git a/Entech Plating/fusion_tasks/models/res_company.py b/Entech Plating/fusion_tasks/models/res_company.py new file mode 100644 index 00000000..398561de --- /dev/null +++ b/Entech Plating/fusion_tasks/models/res_company.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import models, fields + + +class ResCompany(models.Model): + _inherit = 'res.company' + + x_fc_google_review_url = fields.Char( + string='Google Review URL', + help='Google Business Profile review link sent to clients after service completion', + ) diff --git a/Entech Plating/fusion_tasks/models/res_config_settings.py b/Entech Plating/fusion_tasks/models/res_config_settings.py new file mode 100644 index 00000000..ec177b13 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/res_config_settings.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import models, fields + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + # Google Maps API Settings + fc_google_maps_api_key = fields.Char( + string='Google Maps API Key', + config_parameter='fusion_claims.google_maps_api_key', + help='API key for Google Maps Places autocomplete in address fields', + ) + fc_google_review_url = fields.Char( + related='company_id.x_fc_google_review_url', + readonly=False, + string='Google Review URL', + ) + + # Technician Management + fc_store_open_hour = fields.Float( + string='Store Open Time', + config_parameter='fusion_claims.store_open_hour', + help='Store opening time for technician scheduling (e.g. 9.0 = 9:00 AM)', + ) + fc_store_close_hour = fields.Float( + string='Store Close Time', + config_parameter='fusion_claims.store_close_hour', + help='Store closing time for technician scheduling (e.g. 18.0 = 6:00 PM)', + ) + fc_google_distance_matrix_enabled = fields.Boolean( + string='Enable Distance Matrix', + config_parameter='fusion_claims.google_distance_matrix_enabled', + help='Enable Google Distance Matrix API for travel time calculations between technician tasks', + ) + fc_technician_start_address = fields.Char( + string='Technician Start Address', + config_parameter='fusion_claims.technician_start_address', + help='Default start location for technician travel calculations (e.g. warehouse/office address)', + ) + fc_location_retention_days = fields.Char( + string='Location History Retention (Days)', + config_parameter='fusion_claims.location_retention_days', + help='How many days to keep technician location history. ' + 'Leave empty = 30 days (1 month). ' + '0 = delete at end of each day. ' + '1+ = keep for that many days.', + ) + + # Web Push Notifications + fc_push_enabled = fields.Boolean( + string='Enable Push Notifications', + config_parameter='fusion_claims.push_enabled', + help='Enable web push notifications for technician tasks', + ) + fc_vapid_public_key = fields.Char( + string='VAPID Public Key', + config_parameter='fusion_claims.vapid_public_key', + help='Public key for Web Push VAPID authentication (auto-generated)', + ) + fc_vapid_private_key = fields.Char( + string='VAPID Private Key', + config_parameter='fusion_claims.vapid_private_key', + help='Private key for Web Push VAPID authentication (auto-generated)', + ) + fc_push_advance_minutes = fields.Integer( + string='Notification Advance (min)', + config_parameter='fusion_claims.push_advance_minutes', + help='Send push notifications this many minutes before a scheduled task', + ) diff --git a/Entech Plating/fusion_tasks/models/res_partner.py b/Entech Plating/fusion_tasks/models/res_partner.py new file mode 100644 index 00000000..72f8e978 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/res_partner.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +import logging +import requests +from odoo import models, fields, api + +_logger = logging.getLogger(__name__) + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + x_fc_start_address = fields.Char( + string='Start Location', + help='Technician daily start location (home, warehouse, etc.). ' + 'Used as origin for first travel time calculation. ' + 'If empty, the company default HQ address is used.', + ) + x_fc_start_address_lat = fields.Float( + string='Start Latitude', digits=(10, 7), + ) + x_fc_start_address_lng = fields.Float( + string='Start Longitude', digits=(10, 7), + ) + + def _geocode_start_address(self, address): + if not address or not address.strip(): + return 0.0, 0.0 + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.google_maps_api_key', '') + if not api_key: + return 0.0, 0.0 + try: + resp = requests.get( + 'https://maps.googleapis.com/maps/api/geocode/json', + params={'address': address.strip(), 'key': api_key, 'region': 'ca'}, + timeout=10, + ) + data = resp.json() + if data.get('status') == 'OK' and data.get('results'): + loc = data['results'][0]['geometry']['location'] + return loc['lat'], loc['lng'] + except Exception as e: + _logger.warning("Start address geocoding failed for '%s': %s", address, e) + return 0.0, 0.0 + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + for rec, vals in zip(records, vals_list): + addr = vals.get('x_fc_start_address') + if addr: + lat, lng = rec._geocode_start_address(addr) + if lat and lng: + rec.write({ + 'x_fc_start_address_lat': lat, + 'x_fc_start_address_lng': lng, + }) + return records + + def write(self, vals): + res = super().write(vals) + if 'x_fc_start_address' in vals: + addr = vals['x_fc_start_address'] + if addr and addr.strip(): + lat, lng = self._geocode_start_address(addr) + if lat and lng: + super().write({ + 'x_fc_start_address_lat': lat, + 'x_fc_start_address_lng': lng, + }) + else: + super().write({ + 'x_fc_start_address_lat': 0.0, + 'x_fc_start_address_lng': 0.0, + }) + return res diff --git a/Entech Plating/fusion_tasks/models/res_users.py b/Entech Plating/fusion_tasks/models/res_users.py new file mode 100644 index 00000000..7d82b551 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/res_users.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import models, fields + + +class ResUsers(models.Model): + _inherit = 'res.users' + + x_fc_is_field_staff = fields.Boolean( + string='Field Staff', + default=False, + help='Check this to show the user in the Technician/Field Staff dropdown when scheduling tasks.', + ) + x_fc_start_address = fields.Char( + related='partner_id.x_fc_start_address', + readonly=False, + string='Start Location', + ) + x_fc_tech_sync_id = fields.Char( + string='Tech Sync ID', + help='Shared identifier for this technician across Odoo instances. ' + 'Must be the same value on all instances for the same person.', + copy=False, + ) diff --git a/Entech Plating/fusion_tasks/models/task_sync.py b/Entech Plating/fusion_tasks/models/task_sync.py new file mode 100644 index 00000000..99ee3684 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/task_sync.py @@ -0,0 +1,748 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +""" +Cross-instance technician task sync. + +Enables two Odoo instances (e.g. Westin and Mobility) that share the same +field technicians to see each other's delivery tasks, preventing double-booking. + +Remote tasks appear as read-only "shadow" records in the local calendar. +The existing _find_next_available_slot() automatically sees shadow tasks, +so collision detection works without changes to the scheduling algorithm. + +Technicians are matched across instances using the x_fc_tech_sync_id field +on res.users. Set the same value (e.g. "gordy") on both instances for the +same person -- no mapping table needed. +""" + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError +import logging +import requests +from datetime import timedelta + +_logger = logging.getLogger(__name__) + +SYNC_TASK_FIELDS = [ + 'x_fc_sync_uuid', 'name', 'technician_id', 'additional_technician_ids', + 'task_type', 'status', + 'scheduled_date', 'time_start', 'time_end', 'duration_hours', + 'address_street', 'address_street2', 'address_city', 'address_zip', + 'address_state_id', 'address_buzz_code', + 'address_lat', 'address_lng', 'priority', 'partner_id', 'partner_phone', + 'pod_required', 'description', + 'travel_time_minutes', 'travel_distance_km', 'travel_origin', + 'completed_latitude', 'completed_longitude', + 'action_latitude', 'action_longitude', + 'completion_datetime', +] + +TERMINAL_STATUSES = ('completed', 'cancelled') + + +class FusionTaskSyncConfig(models.Model): + _name = 'fusion.task.sync.config' + _description = 'Task Sync Remote Instance' + + name = fields.Char('Instance Name', required=True, + help='e.g. Westin Healthcare, Mobility Specialties') + instance_id = fields.Char('Instance ID', required=True, + help='Short identifier, e.g. westin or mobility') + url = fields.Char('Odoo URL', required=True, + help='e.g. http://192.168.1.40:8069') + database = fields.Char('Database', required=True) + username = fields.Char('API Username', required=True) + api_key = fields.Char('API Key', required=True) + active = fields.Boolean(default=True) + last_sync = fields.Datetime('Last Successful Sync', readonly=True) + last_sync_error = fields.Text('Last Error', readonly=True) + + # ------------------------------------------------------------------ + # JSON-RPC helpers (uses /jsonrpc dispatch, muted on receiving side) + # ------------------------------------------------------------------ + + def _jsonrpc(self, service, method, args): + """Execute a JSON-RPC call against the remote Odoo instance.""" + self.ensure_one() + url = f"{self.url.rstrip('/')}/jsonrpc" + payload = { + 'jsonrpc': '2.0', + 'method': 'call', + 'id': 1, + 'params': { + 'service': service, + 'method': method, + 'args': args, + }, + } + try: + resp = requests.post(url, json=payload, timeout=15) + resp.raise_for_status() + result = resp.json() + if result.get('error'): + err = result['error'].get('data', {}).get('message', str(result['error'])) + raise UserError(f"Remote error: {err}") + return result.get('result') + except requests.exceptions.ConnectionError: + _logger.warning("Task sync: cannot connect to %s", self.url) + return None + except requests.exceptions.Timeout: + _logger.warning("Task sync: timeout connecting to %s", self.url) + return None + + def _authenticate(self): + """Authenticate with the remote instance and return the uid.""" + self.ensure_one() + uid = self._jsonrpc('common', 'authenticate', + [self.database, self.username, self.api_key, {}]) + if not uid: + _logger.error("Task sync: authentication failed for %s", self.name) + return uid + + def _rpc(self, model, method, args, kwargs=None): + """Execute a method on the remote instance via execute_kw.""" + self.ensure_one() + uid = self._authenticate() + if not uid: + return None + call_args = [self.database, uid, self.api_key, model, method, args] + if kwargs: + call_args.append(kwargs) + return self._jsonrpc('object', 'execute_kw', call_args) + + # ------------------------------------------------------------------ + # Tech sync ID helpers + # ------------------------------------------------------------------ + + def _get_local_tech_map(self): + """Build {local_user_id: x_fc_tech_sync_id} for all local field staff.""" + techs = self.env['res.users'].sudo().search([ + ('x_fc_is_field_staff', '=', True), + ('x_fc_tech_sync_id', '!=', False), + ('active', '=', True), + ]) + return {u.id: u.x_fc_tech_sync_id for u in techs} + + def _get_remote_tech_map(self): + """Build {x_fc_tech_sync_id: remote_user_id} from the remote instance.""" + self.ensure_one() + remote_users = self._rpc('res.users', 'search_read', [ + [('x_fc_is_field_staff', '=', True), + ('x_fc_tech_sync_id', '!=', False), + ('active', '=', True)], + ], {'fields': ['id', 'x_fc_tech_sync_id']}) + if not remote_users: + return {} + return { + ru['x_fc_tech_sync_id']: ru['id'] + for ru in remote_users + if ru.get('x_fc_tech_sync_id') + } + + def _get_local_syncid_to_uid(self): + """Build {x_fc_tech_sync_id: local_user_id} for local field staff.""" + techs = self.env['res.users'].sudo().search([ + ('x_fc_is_field_staff', '=', True), + ('x_fc_tech_sync_id', '!=', False), + ('active', '=', True), + ]) + return {u.x_fc_tech_sync_id: u.id for u in techs} + + # ------------------------------------------------------------------ + # Connection test + # ------------------------------------------------------------------ + + def action_test_connection(self): + """Test the connection to the remote instance.""" + self.ensure_one() + uid = self._authenticate() + if uid: + remote_map = self._get_remote_tech_map() + local_map = self._get_local_tech_map() + matched = set(local_map.values()) & set(remote_map.keys()) + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Connection Successful', + 'message': f'Connected to {self.name}. ' + f'{len(matched)} technician(s) matched by sync ID.', + 'type': 'success', + 'sticky': False, + }, + } + raise UserError(f"Cannot connect to {self.name}. Check URL, database, and API key.") + + # ------------------------------------------------------------------ + # PUSH: send local task changes to remote instance + # ------------------------------------------------------------------ + + def _get_local_instance_id(self): + """Return this instance's own ID from config parameters.""" + return self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.sync_instance_id', '') + + @api.model + def _push_tasks(self, tasks, operation='create'): + """Push local task changes to all active remote instances. + Called from technician_task create/write overrides. + Non-blocking: errors are logged, not raised. + """ + configs = self.sudo().search([('active', '=', True)]) + if not configs: + return + local_id = configs[0]._get_local_instance_id() + if not local_id: + return + for config in configs: + try: + config._push_tasks_to_remote(tasks, operation, local_id) + except Exception: + _logger.exception("Task sync push to %s failed", config.name) + + def _push_tasks_to_remote(self, tasks, operation, local_instance_id): + """Push task data to a single remote instance. + + Maps additional_technician_ids via sync IDs so the remote instance + also blocks those technicians' schedules. + """ + self.ensure_one() + local_map = self._get_local_tech_map() + remote_map = self._get_remote_tech_map() + if not local_map or not remote_map: + return + + ctx = {'context': {'skip_task_sync': True, 'skip_travel_recalc': True}} + + for task in tasks: + sync_id = local_map.get(task.technician_id.id) + if not sync_id: + continue + remote_tech_uid = remote_map.get(sync_id) + if not remote_tech_uid: + continue + + # Map additional technicians to remote user IDs + remote_additional_ids = [] + for tech in task.additional_technician_ids: + add_sync_id = local_map.get(tech.id) + if add_sync_id: + remote_add_uid = remote_map.get(add_sync_id) + if remote_add_uid: + remote_additional_ids.append(remote_add_uid) + + task_data = { + 'x_fc_sync_uuid': task.x_fc_sync_uuid, + 'x_fc_sync_source': local_instance_id, + 'x_fc_sync_remote_id': task.id, + 'name': f"[{local_instance_id.upper()}] {task.name}", + 'technician_id': remote_tech_uid, + 'additional_technician_ids': [(6, 0, remote_additional_ids)], + 'task_type': task.task_type, + 'status': task.status, + 'scheduled_date': str(task.scheduled_date) if task.scheduled_date else False, + 'time_start': task.time_start, + 'time_end': task.time_end, + 'duration_hours': task.duration_hours, + 'address_street': task.address_street or '', + 'address_street2': task.address_street2 or '', + 'address_city': task.address_city or '', + 'address_zip': task.address_zip or '', + 'address_lat': float(task.address_lat or 0), + 'address_lng': float(task.address_lng or 0), + 'priority': task.priority or 'normal', + 'x_fc_sync_client_name': task.partner_id.name if task.partner_id else '', + 'travel_time_minutes': task.travel_time_minutes or 0, + 'travel_distance_km': float(task.travel_distance_km or 0), + 'travel_origin': task.travel_origin or '', + 'completed_latitude': float(task.completed_latitude or 0), + 'completed_longitude': float(task.completed_longitude or 0), + 'action_latitude': float(task.action_latitude or 0), + 'action_longitude': float(task.action_longitude or 0), + } + if task.completion_datetime: + task_data['completion_datetime'] = str(task.completion_datetime) + + existing = self._rpc( + 'fusion.technician.task', 'search', + [[('x_fc_sync_uuid', '=', task.x_fc_sync_uuid)]], + {'limit': 1}) + + if operation in ('create', 'write'): + if existing: + self._rpc('fusion.technician.task', 'write', + [existing, task_data], ctx) + elif operation == 'create': + task_data['sale_order_id'] = False + self._rpc('fusion.technician.task', 'create', + [[task_data]], ctx) + + elif operation == 'unlink' and existing: + self._rpc('fusion.technician.task', 'write', + [existing, {'status': 'cancelled', 'active': False}], ctx) + + @api.model + def _push_shadow_status(self, shadow_tasks): + """Push local status changes on shadow tasks back to their source instance. + + When a tech changes a shadow task status locally, update the original + task on the remote instance and trigger the appropriate client emails + there. Only the parent (originating) instance sends client-facing + emails -- the child instance skips them via x_fc_sync_source guards. + """ + configs = self.sudo().search([('active', '=', True)]) + config_by_instance = {c.instance_id: c for c in configs} + ctx = {'context': {'skip_task_sync': True, 'skip_travel_recalc': True}} + + for task in shadow_tasks: + config = config_by_instance.get(task.x_fc_sync_source) + if not config or not task.x_fc_sync_remote_id: + continue + try: + update_vals = {'status': task.status} + if task.status == 'completed' and task.completion_datetime: + update_vals['completion_datetime'] = str(task.completion_datetime) + if task.completed_latitude and task.completed_longitude: + update_vals['completed_latitude'] = task.completed_latitude + update_vals['completed_longitude'] = task.completed_longitude + if task.action_latitude and task.action_longitude: + update_vals['action_latitude'] = task.action_latitude + update_vals['action_longitude'] = task.action_longitude + config._rpc( + 'fusion.technician.task', 'write', + [[task.x_fc_sync_remote_id], update_vals], ctx) + _logger.info( + "Pushed status '%s' for shadow task %s back to %s (remote id %d)", + task.status, task.name, config.name, task.x_fc_sync_remote_id) + self._trigger_parent_notifications(config, task) + except Exception: + _logger.exception( + "Failed to push status for shadow task %s to %s", + task.name, config.name) + + @api.model + def _push_technician_location(self, user_id, latitude, longitude, accuracy=0): + """Push a technician's location update to all remote instances. + + Called when a technician performs a task action (en_route, complete) + so the other instance immediately knows where the tech is, without + waiting for the next pull cron cycle. + """ + configs = self.sudo().search([('active', '=', True)]) + if not configs: + return + local_map = configs[0]._get_local_tech_map() + sync_id = local_map.get(user_id) + if not sync_id: + return + for config in configs: + try: + remote_map = config._get_remote_tech_map() + remote_uid = remote_map.get(sync_id) + if not remote_uid: + continue + # Create location record on remote instance + config._rpc( + 'fusion.technician.location', 'create', + [[{ + 'user_id': remote_uid, + 'latitude': latitude, + 'longitude': longitude, + 'accuracy': accuracy, + 'source': 'sync', + 'sync_instance': configs[0]._get_local_instance_id(), + }]]) + except Exception: + _logger.warning( + "Failed to push location for tech %s to %s", + user_id, config.name) + + def _trigger_parent_notifications(self, config, task): + """After pushing a shadow status, trigger appropriate emails and + notifications on the parent instance so the client gets notified + exactly once (from the originating instance only).""" + remote_id = task.x_fc_sync_remote_id + if task.status == 'completed': + for method in ('_notify_scheduler_on_completion', + '_send_task_completion_email'): + try: + config._rpc('fusion.technician.task', method, [[remote_id]]) + except Exception: + _logger.warning( + "Could not call %s on remote for %s", method, task.name) + elif task.status == 'en_route': + try: + config._rpc( + 'fusion.technician.task', + '_send_task_en_route_email', [[remote_id]]) + except Exception: + _logger.warning( + "Could not trigger en-route email on remote for %s", + task.name) + elif task.status == 'cancelled': + try: + config._rpc( + 'fusion.technician.task', + '_send_task_cancelled_email', [[remote_id]]) + except Exception: + _logger.warning( + "Could not trigger cancel email on remote for %s", + task.name) + + # ------------------------------------------------------------------ + # PULL: cron-based full reconciliation + # ------------------------------------------------------------------ + + @api.model + def _cron_pull_remote_tasks(self): + """Cron job: pull tasks and technician locations from all active remote instances.""" + configs = self.sudo().search([('active', '=', True)]) + for config in configs: + try: + config._pull_tasks_from_remote() + config._pull_technician_locations() + config.sudo().write({ + 'last_sync': fields.Datetime.now(), + 'last_sync_error': False, + }) + except Exception as e: + _logger.exception("Task sync pull from %s failed", config.name) + config.sudo().write({'last_sync_error': str(e)}) + + def _pull_tasks_from_remote(self): + """Pull all active tasks for matched technicians from the remote instance. + + After syncing, recalculates travel chains for all affected tech+date + combos so route planning accounts for both local and shadow tasks. + """ + self.ensure_one() + local_syncid_to_uid = self._get_local_syncid_to_uid() + if not local_syncid_to_uid: + return + + remote_map = self._get_remote_tech_map() + if not remote_map: + return + + matched_sync_ids = set(local_syncid_to_uid.keys()) & set(remote_map.keys()) + if not matched_sync_ids: + _logger.info("Task sync: no matched technicians between local and %s", self.name) + return + + remote_tech_ids = [remote_map[sid] for sid in matched_sync_ids] + remote_syncid_by_uid = {v: k for k, v in remote_map.items()} + + cutoff = fields.Date.today() - timedelta(days=7) + remote_tasks = self._rpc( + 'fusion.technician.task', 'search_read', + [[ + '|', + ('technician_id', 'in', remote_tech_ids), + ('additional_technician_ids', 'in', remote_tech_ids), + ('scheduled_date', '>=', str(cutoff)), + ('x_fc_sync_source', '=', False), + ]], + {'fields': SYNC_TASK_FIELDS + ['id']}) + + if remote_tasks is None: + return + + Task = self.env['fusion.technician.task'].sudo().with_context( + skip_task_sync=True, skip_travel_recalc=True) + + remote_uuids = set() + affected_combos = set() + + for rt in remote_tasks: + sync_uuid = rt.get('x_fc_sync_uuid') + if not sync_uuid: + continue + remote_uuids.add(sync_uuid) + + remote_tech_raw = rt['technician_id'] + remote_uid = remote_tech_raw[0] if isinstance(remote_tech_raw, (list, tuple)) else remote_tech_raw + tech_sync_id = remote_syncid_by_uid.get(remote_uid) + local_uid = local_syncid_to_uid.get(tech_sync_id) if tech_sync_id else None + if not local_uid: + continue + + partner_raw = rt.get('partner_id') + client_name = partner_raw[1] if isinstance(partner_raw, (list, tuple)) and len(partner_raw) > 1 else '' + client_phone = rt.get('partner_phone', '') or '' + + state_raw = rt.get('address_state_id') + state_name = '' + if isinstance(state_raw, (list, tuple)) and len(state_raw) > 1: + state_name = state_raw[1] + + # Map additional technicians from remote to local + local_additional_ids = [] + remote_add_raw = rt.get('additional_technician_ids', []) + if remote_add_raw and isinstance(remote_add_raw, list): + for add_uid in remote_add_raw: + add_sync_id = remote_syncid_by_uid.get(add_uid) + if add_sync_id: + local_add_uid = local_syncid_to_uid.get(add_sync_id) + if local_add_uid: + local_additional_ids.append(local_add_uid) + + sched_date = rt.get('scheduled_date') + + vals = { + 'x_fc_sync_uuid': sync_uuid, + 'x_fc_sync_source': self.instance_id, + 'x_fc_sync_remote_id': rt['id'], + 'name': f"[{self.instance_id.upper()}] {rt.get('name', '')}", + 'technician_id': local_uid, + 'additional_technician_ids': [(6, 0, local_additional_ids)], + 'task_type': rt.get('task_type', 'delivery'), + 'status': rt.get('status', 'scheduled'), + 'scheduled_date': sched_date, + 'time_start': rt.get('time_start', 9.0), + 'time_end': rt.get('time_end', 10.0), + 'duration_hours': rt.get('duration_hours', 1.0), + 'address_street': rt.get('address_street', ''), + 'address_street2': rt.get('address_street2', ''), + 'address_city': rt.get('address_city', ''), + 'address_zip': rt.get('address_zip', ''), + 'address_buzz_code': rt.get('address_buzz_code', ''), + 'address_lat': rt.get('address_lat', 0), + 'address_lng': rt.get('address_lng', 0), + 'priority': rt.get('priority', 'normal'), + 'pod_required': rt.get('pod_required', False), + 'description': rt.get('description', ''), + 'x_fc_sync_client_name': client_name, + 'x_fc_sync_client_phone': client_phone, + 'travel_time_minutes': rt.get('travel_time_minutes', 0), + 'travel_distance_km': rt.get('travel_distance_km', 0), + 'travel_origin': rt.get('travel_origin', ''), + 'completed_latitude': rt.get('completed_latitude', 0), + 'completed_longitude': rt.get('completed_longitude', 0), + 'action_latitude': rt.get('action_latitude', 0), + 'action_longitude': rt.get('action_longitude', 0), + } + if rt.get('completion_datetime'): + vals['completion_datetime'] = rt['completion_datetime'] + + if state_name: + state_rec = self.env['res.country.state'].sudo().search( + [('name', '=', state_name)], limit=1) + if state_rec: + vals['address_state_id'] = state_rec.id + + existing = Task.search([('x_fc_sync_uuid', '=', sync_uuid)], limit=1) + if existing: + if existing.status in TERMINAL_STATUSES: + vals.pop('status', None) + existing.write(vals) + else: + vals['sale_order_id'] = False + Task.create([vals]) + + if sched_date: + affected_combos.add((local_uid, sched_date)) + for add_uid in local_additional_ids: + affected_combos.add((add_uid, sched_date)) + + stale_shadows = Task.search([ + ('x_fc_sync_source', '=', self.instance_id), + ('x_fc_sync_uuid', 'not in', list(remote_uuids)), + ('scheduled_date', '>=', str(cutoff)), + ('active', '=', True), + ]) + if stale_shadows: + for st in stale_shadows: + if st.scheduled_date and st.technician_id: + affected_combos.add((st.technician_id.id, st.scheduled_date)) + for tech in st.additional_technician_ids: + if st.scheduled_date: + affected_combos.add((tech.id, st.scheduled_date)) + stale_shadows.write({'active': False, 'status': 'cancelled'}) + _logger.info("Deactivated %d stale shadow tasks from %s", + len(stale_shadows), self.instance_id) + + if affected_combos: + today = fields.Date.today() + today_str = str(today) + future_combos = set() + for tid, d in affected_combos: + if not d: + continue + d_str = str(d) if not isinstance(d, str) else d + if d_str >= today_str: + future_combos.add((tid, d_str)) + if future_combos: + TaskModel = self.env['fusion.technician.task'].sudo() + try: + ungeocode = TaskModel.search([ + ('x_fc_sync_source', '=', self.instance_id), + ('active', '=', True), + ('scheduled_date', '>=', today_str), + ('status', 'not in', ['cancelled']), + '|', + ('address_lat', '=', 0), ('address_lat', '=', False), + ]) + geocoded = 0 + for shadow in ungeocode: + if shadow.address_display: + if shadow.with_context(skip_travel_recalc=True)._geocode_address(): + geocoded += 1 + if geocoded: + _logger.info("Geocoded %d shadow tasks from %s", + geocoded, self.name) + except Exception: + _logger.exception( + "Shadow task geocoding after sync from %s failed", self.name) + + try: + TaskModel._recalculate_combos_travel(future_combos) + _logger.info( + "Recalculated travel for %d tech+date combos after sync from %s", + len(future_combos), self.name) + except Exception: + _logger.exception( + "Travel recalculation after sync from %s failed", self.name) + + # ------------------------------------------------------------------ + # PULL: technician locations from remote instance + # ------------------------------------------------------------------ + + def _pull_technician_locations(self): + """Pull latest GPS locations for matched technicians from the remote instance. + + Creates local location records with source='sync' so the map view + shows technician positions from both instances. Only keeps the single + most recent synced location per technician (replaces older synced + records to avoid clutter). + """ + self.ensure_one() + local_syncid_to_uid = self._get_local_syncid_to_uid() + if not local_syncid_to_uid: + return + + remote_map = self._get_remote_tech_map() + if not remote_map: + return + + matched_sync_ids = set(local_syncid_to_uid.keys()) & set(remote_map.keys()) + if not matched_sync_ids: + return + + remote_tech_ids = [remote_map[sid] for sid in matched_sync_ids] + remote_syncid_by_uid = {v: k for k, v in remote_map.items()} + + remote_locations = self._rpc( + 'fusion.technician.location', 'search_read', + [[ + ('user_id', 'in', remote_tech_ids), + ('logged_at', '>', str(fields.Datetime.subtract( + fields.Datetime.now(), hours=24))), + ('source', '!=', 'sync'), + ]], + { + 'fields': ['user_id', 'latitude', 'longitude', + 'accuracy', 'logged_at'], + 'order': 'logged_at desc', + }) + + if not remote_locations: + return + + Location = self.env['fusion.technician.location'].sudo() + + seen_techs = set() + synced_count = 0 + for rloc in remote_locations: + remote_uid_raw = rloc['user_id'] + remote_uid = (remote_uid_raw[0] + if isinstance(remote_uid_raw, (list, tuple)) + else remote_uid_raw) + if remote_uid in seen_techs: + continue + seen_techs.add(remote_uid) + + sync_id = remote_syncid_by_uid.get(remote_uid) + local_uid = local_syncid_to_uid.get(sync_id) if sync_id else None + if not local_uid: + continue + + lat = rloc.get('latitude', 0) + lng = rloc.get('longitude', 0) + if not lat or not lng: + continue + + old_synced = Location.search([ + ('user_id', '=', local_uid), + ('source', '=', 'sync'), + ('sync_instance', '=', self.instance_id), + ]) + if old_synced: + old_synced.unlink() + + Location.create({ + 'user_id': local_uid, + 'latitude': lat, + 'longitude': lng, + 'accuracy': rloc.get('accuracy', 0), + 'logged_at': rloc.get('logged_at', fields.Datetime.now()), + 'source': 'sync', + 'sync_instance': self.instance_id, + }) + synced_count += 1 + + if synced_count: + _logger.info("Synced %d technician location(s) from %s", + synced_count, self.name) + + # ------------------------------------------------------------------ + # CLEANUP + # ------------------------------------------------------------------ + + @api.model + def _cron_cleanup_old_shadows(self): + """Remove shadow tasks older than 30 days (completed/cancelled).""" + cutoff = fields.Date.today() - timedelta(days=30) + old_shadows = self.env['fusion.technician.task'].sudo().search([ + ('x_fc_sync_source', '!=', False), + ('scheduled_date', '<', str(cutoff)), + ('status', 'in', ['completed', 'cancelled']), + ]) + if old_shadows: + count = len(old_shadows) + old_shadows.unlink() + _logger.info("Cleaned up %d old shadow tasks", count) + + # ------------------------------------------------------------------ + # Manual trigger + # ------------------------------------------------------------------ + + def action_sync_now(self): + """Manually trigger a full sync for this config.""" + self.ensure_one() + self._pull_tasks_from_remote() + self._pull_technician_locations() + self.sudo().write({ + 'last_sync': fields.Datetime.now(), + 'last_sync_error': False, + }) + shadow_count = self.env['fusion.technician.task'].sudo().search_count([ + ('x_fc_sync_source', '=', self.instance_id), + ]) + loc_count = self.env['fusion.technician.location'].sudo().search_count([ + ('source', '=', 'sync'), + ('sync_instance', '=', self.instance_id), + ]) + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Sync Complete', + 'message': (f'Synced from {self.name}. ' + f'{shadow_count} shadow task(s), ' + f'{loc_count} technician location(s) visible.'), + 'type': 'success', + 'sticky': False, + }, + } diff --git a/Entech Plating/fusion_tasks/models/technician_location.py b/Entech Plating/fusion_tasks/models/technician_location.py new file mode 100644 index 00000000..f2c79b2b --- /dev/null +++ b/Entech Plating/fusion_tasks/models/technician_location.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +""" +Fusion Technician Location +GPS location logging for field technicians. +""" + +from odoo import models, fields, api, _ +import logging + +_logger = logging.getLogger(__name__) + + +class FusionTechnicianLocation(models.Model): + _name = 'fusion.technician.location' + _description = 'Technician Location Log' + _order = 'logged_at desc' + + user_id = fields.Many2one( + 'res.users', + string='Technician', + required=True, + index=True, + ondelete='cascade', + ) + latitude = fields.Float( + string='Latitude', + digits=(10, 7), + required=True, + ) + longitude = fields.Float( + string='Longitude', + digits=(10, 7), + required=True, + ) + accuracy = fields.Float( + string='Accuracy (m)', + help='GPS accuracy in meters', + ) + logged_at = fields.Datetime( + string='Logged At', + default=fields.Datetime.now, + required=True, + index=True, + ) + source = fields.Selection([ + ('portal', 'Portal'), + ('app', 'Mobile App'), + ('sync', 'Synced'), + ], string='Source', default='portal') + sync_instance = fields.Char( + 'Sync Instance', index=True, + help='Source instance ID if synced (e.g. westin, mobility)', + ) + + @api.model + def log_location(self, latitude, longitude, accuracy=None): + """Log the current user's location. Called from portal JS.""" + return self.sudo().create({ + 'user_id': self.env.user.id, + 'latitude': latitude, + 'longitude': longitude, + 'accuracy': accuracy or 0, + 'source': 'portal', + }) + + @api.model + def get_latest_locations(self): + """Get the most recent location for each technician (for map view). + + Includes both local GPS pings and synced locations from remote + instances, so the map shows all shared technicians regardless of + which Odoo instance they are clocked into. + """ + self.env.cr.execute(""" + SELECT DISTINCT ON (user_id) + user_id, latitude, longitude, accuracy, logged_at, + COALESCE(sync_instance, '') AS sync_instance + FROM fusion_technician_location + WHERE logged_at > NOW() - INTERVAL '24 hours' + ORDER BY user_id, logged_at DESC + """) + rows = self.env.cr.dictfetchall() + local_id = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.sync_instance_id', '') + result = [] + for row in rows: + user = self.env['res.users'].sudo().browse(row['user_id']) + src = row.get('sync_instance') or local_id + result.append({ + 'user_id': row['user_id'], + 'name': user.name, + 'latitude': row['latitude'], + 'longitude': row['longitude'], + 'accuracy': row['accuracy'], + 'logged_at': str(row['logged_at']), + 'sync_instance': src, + }) + return result + + @api.model + def _cron_cleanup_old_locations(self): + """Remove location logs based on configurable retention setting. + + Setting (fusion_claims.location_retention_days): + - Empty / not set => keep 30 days (default) + - "0" => delete at end of day (keep today only) + - "1" .. "N" => keep for N days + """ + ICP = self.env['ir.config_parameter'].sudo() + raw = (ICP.get_param('fusion_claims.location_retention_days') or '').strip() + + if raw == '': + retention_days = 30 # default: 1 month + else: + try: + retention_days = max(int(raw), 0) + except (ValueError, TypeError): + retention_days = 30 + + cutoff = fields.Datetime.subtract(fields.Datetime.now(), days=retention_days) + old_records = self.search([('logged_at', '<', cutoff)]) + count = len(old_records) + if count: + old_records.unlink() + _logger.info( + "Cleaned up %d technician location records (retention=%d days)", + count, retention_days, + ) diff --git a/Entech Plating/fusion_tasks/models/technician_task.py b/Entech Plating/fusion_tasks/models/technician_task.py new file mode 100644 index 00000000..768d9ca4 --- /dev/null +++ b/Entech Plating/fusion_tasks/models/technician_task.py @@ -0,0 +1,3028 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +""" +Fusion Technician Task +Scheduling and task management for field technicians. +Replaces Monday.com for technician schedule tracking. +""" + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError +from odoo.osv import expression +from markupsafe import Markup +import logging +import json +import uuid +import requests +from datetime import datetime as dt_datetime, timedelta +import urllib.parse + +_logger = logging.getLogger(__name__) + + +class FusionTechnicianTask(models.Model): + _name = 'fusion.technician.task' + _description = 'Technician Task' + _order = 'scheduled_date, sequence, time_start, id' + _inherit = ['mail.thread', 'mail.activity.mixin', 'fusion.email.builder.mixin'] + _rec_name = 'name' + + def _compute_display_name(self): + """Richer display name: Client - Type | 9:00 AM - 10:00 AM [+2 techs].""" + type_labels = dict(self._fields['task_type'].selection) + for task in self: + client = task.x_fc_sync_client_name if task.x_fc_sync_source else (task.partner_id.name or '') + ttype = type_labels.get(task.task_type, task.task_type or '') + start = self._float_to_time_str(task.time_start) + end = self._float_to_time_str(task.time_end) + parts = [client, ttype] + label = ' - '.join(p for p in parts if p) + if start and end: + label += f' | {start} - {end}' + extra = len(task.additional_technician_ids) + if extra: + label += f' [+{extra} tech{"s" if extra > 1 else ""}]' + task.display_name = label or task.name + + # ------------------------------------------------------------------ + # STORE HOURS HELPER + # ------------------------------------------------------------------ + def _get_store_hours(self): + """Return (open_hour, close_hour) from settings. Defaults 9.0 / 18.0.""" + ICP = self.env['ir.config_parameter'].sudo() + try: + open_h = float(ICP.get_param('fusion_claims.store_open_hour', '9.0') or '9.0') + except (ValueError, TypeError): + open_h = 9.0 + try: + close_h = float(ICP.get_param('fusion_claims.store_close_hour', '18.0') or '18.0') + except (ValueError, TypeError): + close_h = 18.0 + return (open_h, close_h) + + # ------------------------------------------------------------------ + # CORE FIELDS + # ------------------------------------------------------------------ + name = fields.Char( + string='Task Reference', + required=True, + copy=False, + readonly=True, + default=lambda self: _('New'), + ) + active = fields.Boolean(default=True) + + # Cross-instance sync fields + x_fc_sync_source = fields.Char( + 'Source Instance', readonly=True, index=True, + help='Origin instance ID if this is a synced shadow task (e.g. westin, mobility)', + ) + x_fc_sync_remote_id = fields.Integer( + 'Remote Task ID', readonly=True, + help='ID of the task on the remote instance', + ) + x_fc_sync_uuid = fields.Char( + 'Sync UUID', readonly=True, index=True, copy=False, + help='Unique ID for cross-instance deduplication', + ) + x_fc_is_shadow = fields.Boolean( + 'Shadow Task', compute='_compute_is_shadow', store=True, + help='True if this task was synced from another instance', + ) + x_fc_sync_client_name = fields.Char( + 'Synced Client Name', readonly=True, + help='Client name from the remote instance (shadow tasks only)', + ) + x_fc_sync_client_phone = fields.Char( + 'Synced Client Phone', readonly=True, + help='Client phone from the remote instance (shadow tasks only)', + ) + + client_display_name = fields.Char( + compute='_compute_client_display', string='Client Name (Display)', + ) + client_display_phone = fields.Char( + compute='_compute_client_display', string='Client Phone (Display)', + ) + + x_fc_source_label = fields.Char( + 'Source', compute='_compute_is_shadow', store=True, + ) + + @api.depends('x_fc_sync_source') + def _compute_is_shadow(self): + local_id = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.sync_instance_id', '') + for task in self: + task.x_fc_is_shadow = bool(task.x_fc_sync_source) + task.x_fc_source_label = task.x_fc_sync_source or local_id + + @api.depends('x_fc_sync_source', 'x_fc_sync_client_name', + 'x_fc_sync_client_phone', 'partner_id') + def _compute_client_display(self): + for task in self: + if task.x_fc_sync_source: + task.client_display_name = task.x_fc_sync_client_name or task.name or '' + task.client_display_phone = task.x_fc_sync_client_phone or '' + else: + task.client_display_name = task.partner_id.name if task.partner_id else '' + task.client_display_phone = task.partner_id.phone if task.partner_id else '' + + technician_id = fields.Many2one( + 'res.users', + string='Technician', + required=True, + tracking=True, + domain="[('x_fc_is_field_staff', '=', True)]", + help='Lead technician responsible for this task', + ) + technician_name = fields.Char( + related='technician_id.name', + string='Technician Name', + store=True, + ) + additional_technician_ids = fields.Many2many( + 'res.users', + 'technician_task_additional_tech_rel', + 'task_id', + 'user_id', + string='Additional Technicians', + domain="[('x_fc_is_field_staff', '=', True)]", + tracking=True, + help='Additional technicians assigned to assist on this task', + ) + all_technician_ids = fields.Many2many( + 'res.users', + compute='_compute_all_technician_ids', + string='All Technicians', + help='Lead + additional technicians combined', + ) + additional_tech_count = fields.Integer( + compute='_compute_all_technician_ids', + string='Extra Techs', + ) + all_technician_names = fields.Char( + compute='_compute_all_technician_ids', + string='All Technician Names', + ) + + @api.depends('technician_id', 'additional_technician_ids') + def _compute_all_technician_ids(self): + for task in self: + all_techs = task.technician_id | task.additional_technician_ids + task.all_technician_ids = all_techs + task.additional_tech_count = len(task.additional_technician_ids) + task.all_technician_names = ', '.join(all_techs.mapped('name')) + + + task_type = fields.Selection([ + ('delivery', 'Delivery'), + ('repair', 'Repair'), + ('pickup', 'Pickup'), + ('troubleshoot', 'Troubleshooting'), + ('assessment', 'Assessment'), + ('installation', 'Installation'), + ('maintenance', 'Maintenance'), + ('ltc_visit', 'LTC Visit'), + ('other', 'Other'), + ], string='Task Type', required=True, default='delivery', tracking=True) + + # ------------------------------------------------------------------ + # SCHEDULING + # ------------------------------------------------------------------ + scheduled_date = fields.Date( + string='Scheduled Date', + tracking=True, + default=fields.Date.context_today, + index=True, + ) + time_start = fields.Float( + string='Start Time', + help='Start time in hours (e.g. 9.5 = 9:30 AM)', + default=9.0, + ) + time_end = fields.Float( + string='End Time', + help='End time in hours (e.g. 10.5 = 10:30 AM)', + default=10.0, + ) + time_start_display = fields.Char( + string='Start', + compute='_compute_time_displays', + ) + time_end_display = fields.Char( + string='End', + compute='_compute_time_displays', + ) + # Legacy 12h selection fields -- kept for DB compatibility, hidden on form + time_start_12h = fields.Selection( + selection='_get_time_selection', + string='Start Time (12h)', + compute='_compute_time_12h', + inverse='_inverse_time_start_12h', + store=True, + ) + time_end_12h = fields.Selection( + selection='_get_time_selection', + string='End Time (12h)', + compute='_compute_time_12h', + inverse='_inverse_time_end_12h', + store=True, + ) + sequence = fields.Integer( + string='Sequence', + default=10, + help='Order of task within the day', + ) + duration_hours = fields.Float( + string='Duration', + default=1.0, + help='Task duration in hours. Auto-calculates end time.', + ) + + # Task type -> default duration mapping + TASK_TYPE_DURATIONS = { + 'delivery': 1.0, + 'repair': 2.0, + 'pickup': 0.5, + 'troubleshoot': 1.5, + 'assessment': 1.5, + 'installation': 2.0, + 'maintenance': 1.5, + 'ltc_visit': 3.0, + 'other': 1.0, + } + + # Previous task travel warning banner + prev_task_summary_html = fields.Html( + string='Previous Task', + compute='_compute_prev_task_summary', + sanitize=False, + ) + + # Datetime fields for calendar view (computed from date + float time) + datetime_start = fields.Datetime( + string='Start', + compute='_compute_datetimes', + inverse='_inverse_datetime_start', + store=True, + help='Combined start datetime for calendar display', + ) + datetime_end = fields.Datetime( + string='End', + compute='_compute_datetimes', + inverse='_inverse_datetime_end', + store=True, + help='Combined end datetime for calendar display', + ) + + calendar_event_id = fields.Many2one( + 'calendar.event', + string='Calendar Event', + copy=False, + ondelete='set null', + help='Linked calendar event for external calendar sync', + ) + + # Schedule info helper for the form + schedule_info_html = fields.Html( + string='Schedule Info', + compute='_compute_schedule_info', + sanitize=False, + ) + + # ------------------------------------------------------------------ + # STATUS + # ------------------------------------------------------------------ + status = fields.Selection([ + ('pending', 'Pending'), + ('scheduled', 'Scheduled'), + ('en_route', 'En Route'), + ('in_progress', 'In Progress'), + ('completed', 'Completed'), + ('cancelled', 'Cancelled'), + ('rescheduled', 'Rescheduled'), + ], string='Status', default='scheduled', required=True, tracking=True, index=True) + + priority = fields.Selection([ + ('0', 'Normal'), + ('1', 'Urgent'), + ('2', 'Emergency'), + ], string='Priority', default='0') + + color = fields.Integer( + string='Color Index', + compute='_compute_color', + ) + + # ------------------------------------------------------------------ + # CLIENT / ADDRESS + # ------------------------------------------------------------------ + partner_id = fields.Many2one( + 'res.partner', + string='Client Name', + tracking=True, + help='Client for this task', + ) + partner_phone = fields.Char( + related='partner_id.phone', + string='Client Phone', + ) + + # Address fields - computed from shipping address or manually set + address_partner_id = fields.Many2one( + 'res.partner', + string='Task Address', + help='Partner record containing the task address (usually shipping address)', + ) + address_street = fields.Char(string='Street') + address_street2 = fields.Char(string='Unit/Suite #') + address_city = fields.Char(string='City') + address_state_id = fields.Many2one('res.country.state', string='Province') + address_zip = fields.Char(string='Postal Code') + address_buzz_code = fields.Char(string='Buzz Code', help='Building buzzer code for entry') + address_display = fields.Text( + string='Full Address', + compute='_compute_address_display', + ) + + # In-store flag -- uses company address instead of client address + is_in_store = fields.Boolean( + string='In Store', + default=False, + help='Task takes place at the store/office. Uses company address automatically.', + ) + + # Geocoding + address_lat = fields.Float(string='Latitude', digits=(10, 7)) + address_lng = fields.Float(string='Longitude', digits=(10, 7)) + + # ------------------------------------------------------------------ + # TASK DETAILS + # ------------------------------------------------------------------ + description = fields.Text( + string='Task Description', + required=True, + help='What needs to be done', + ) + equipment_needed = fields.Text( + string='Equipment / Materials Needed', + help='Tools and materials the technician should bring', + ) + pod_required = fields.Boolean( + string='POD Required', + default=False, + help='Proof of Delivery signature required', + ) + pod_signature = fields.Binary( + string='POD Signature', attachment=True, + ) + pod_client_name = fields.Char(string='POD Signer Name') + pod_signature_date = fields.Date(string='POD Signature Date') + pod_signed_by_user_id = fields.Many2one( + 'res.users', string='POD Collected By', readonly=True, + ) + pod_signed_datetime = fields.Datetime( + string='POD Collected At', readonly=True, + ) + + # ------------------------------------------------------------------ + # CLIENT EMAIL / REVIEW OPTIONS + # ------------------------------------------------------------------ + x_fc_send_client_updates = fields.Boolean( + string='Send Client Email Updates', + default=True, + help='Send automatic emails to the client when the technician is en route and when the task is completed', + ) + x_fc_ask_google_review = fields.Boolean( + string='Request Google Review', + default=True, + help='Include a Google review request in the completion email to the client', + ) + x_fc_late_notified = fields.Boolean( + string='Late Notification Sent', + default=False, + readonly=True, + help='Internal flag: whether a late-arrival notification has already been sent for this task', + ) + + # ------------------------------------------------------------------ + # COMPLETION + # ------------------------------------------------------------------ + completion_notes = fields.Html( + string='Completion Notes', + help='Notes from the technician about what was done', + ) + completion_datetime = fields.Datetime( + string='Completed At', + tracking=True, + ) + + # GPS location captured at task actions + started_latitude = fields.Float( + string='Started Latitude', digits=(10, 7), readonly=True, + ) + started_longitude = fields.Float( + string='Started Longitude', digits=(10, 7), readonly=True, + ) + completed_latitude = fields.Float( + string='Completed Latitude', digits=(10, 7), readonly=True, + ) + completed_longitude = fields.Float( + string='Completed Longitude', digits=(10, 7), readonly=True, + ) + action_latitude = fields.Float( + string='Last Action Latitude', digits=(10, 7), readonly=True, + ) + action_longitude = fields.Float( + string='Last Action Longitude', digits=(10, 7), readonly=True, + ) + action_location_accuracy = fields.Float( + string='Location Accuracy (m)', readonly=True, + ) + + voice_note_audio = fields.Binary( + string='Voice Recording', + attachment=True, + ) + voice_note_transcription = fields.Text( + string='Voice Transcription', + ) + + # ------------------------------------------------------------------ + # TRAVEL + # ------------------------------------------------------------------ + travel_time_minutes = fields.Integer( + string='Travel Time (min)', + help='Estimated travel time from previous task in minutes', + ) + travel_distance_km = fields.Float( + string='Travel Distance (km)', + digits=(8, 1), + ) + travel_origin = fields.Char( + string='Travel From', + help='Origin address for travel calculation', + ) + previous_task_id = fields.Many2one( + 'fusion.technician.task', + string='Previous Task', + help='The task before this one in the schedule (for travel calculation)', + ) + + # ------------------------------------------------------------------ + # PUSH NOTIFICATION TRACKING + # ------------------------------------------------------------------ + push_notified = fields.Boolean( + string='Push Notified', + default=False, + help='Whether a push notification was sent for this task', + ) + push_notified_datetime = fields.Datetime( + string='Notified At', + ) + + # ------------------------------------------------------------------ + # COMPUTED FIELDS + # ------------------------------------------------------------------ + + # ------------------------------------------------------------------ + # SLOT AVAILABILITY HELPERS + # ------------------------------------------------------------------ + + def _get_calendar_busy_intervals(self, tech_id, date): + """Return busy intervals from calendar.event for a technician on a date. + + Queries events where the technician is an attendee, excluding events + already linked to a fusion task (to avoid double-counting). + Returns list of (start_float, end_float) in local time. + """ + if not tech_id or not date: + return [] + user = self.env['res.users'].browse(tech_id) + if not user.exists(): + return [] + partner_id = user.partner_id.id + + import pytz + tz = self._get_local_tz() + day_start = tz.localize(dt_datetime.combine(date, dt_datetime.min.time())) + day_end = day_start + timedelta(days=1) + day_start_utc = day_start.astimezone(pytz.utc).replace(tzinfo=None) + day_end_utc = day_end.astimezone(pytz.utc).replace(tzinfo=None) + + task_event_ids = self.sudo().search([ + ('calendar_event_id', '!=', False), + ('scheduled_date', '=', date), + ]).mapped('calendar_event_id').ids + + domain = [ + ('partner_ids', 'in', [partner_id]), + ('start', '<', day_end_utc), + ('stop', '>', day_start_utc), + ('active', '=', True), + ] + if task_event_ids: + domain.append(('id', 'not in', task_event_ids)) + + events = self.env['calendar.event'].sudo().search(domain) + intervals = [] + for ev in events: + ev_start = pytz.utc.localize(ev.start).astimezone(tz) + ev_end = pytz.utc.localize(ev.stop).astimezone(tz) + start_float = ev_start.hour + ev_start.minute / 60.0 + end_float = ev_end.hour + ev_end.minute / 60.0 + if ev_start.date() < date: + start_float = 0.0 + if ev_end.date() > date: + end_float = 24.0 + intervals.append((start_float, end_float)) + return sorted(intervals, key=lambda x: x[0]) + + def _find_next_available_slot(self, tech_id, date, preferred_start=9.0, + duration=1.0, exclude_task_id=False, + dest_lat=0, dest_lng=0): + """Find the next available time slot for a technician on a given date. + + Scans all non-cancelled tasks for that tech+date, sorts them, and + walks through the day (9 AM - 6 PM) looking for a gap that fits + the requested duration PLUS travel time from the previous task. + + :param tech_id: res.users id of the technician + :param date: date object for the day to check + :param preferred_start: float hour to start looking from (default 9.0) + :param duration: required slot length in hours (default 1.0) + :param exclude_task_id: task id to exclude (when editing an existing task) + :param dest_lat: latitude of the destination (new task location) + :param dest_lng: longitude of the destination (new task location) + :returns: (start_float, end_float) or (False, False) if fully booked + """ + STORE_OPEN, STORE_CLOSE = self._get_store_hours() + + if not tech_id or not date: + return (preferred_start, preferred_start + duration) + + domain = [ + '|', + ('technician_id', '=', tech_id), + ('additional_technician_ids', 'in', [tech_id]), + ('scheduled_date', '=', date), + ('status', 'not in', ['cancelled']), + ] + if exclude_task_id: + domain.append(('id', '!=', exclude_task_id)) + + booked = self.sudo().search(domain, order='time_start') + + # Build sorted list of (start, end, lat, lng) intervals + intervals = [] + for b in booked: + intervals.append(( + max(b.time_start, STORE_OPEN), + min(b.time_end, STORE_CLOSE), + b.address_lat or 0, + b.address_lng or 0, + )) + + for cal_start, cal_end in self._get_calendar_busy_intervals(tech_id, date): + clamped_start = max(cal_start, STORE_OPEN) + clamped_end = min(cal_end, STORE_CLOSE) + if clamped_start < clamped_end: + intervals.append((clamped_start, clamped_end, 0, 0)) + intervals.sort(key=lambda x: x[0]) + + def _travel_hours(from_lat, from_lng, to_lat, to_lng): + """Calculate travel time in hours between two locations. + Returns 0 if coordinates are missing. Rounds up to 15-min.""" + if not from_lat or not from_lng or not to_lat or not to_lng: + return 0 + travel_min = self._quick_travel_time( + from_lat, from_lng, to_lat, to_lng) + if travel_min > 0: + import math + return math.ceil(travel_min / 15.0) * 0.25 + return 0 + + def _travel_from_prev(iv_lat, iv_lng): + """Travel from a previous booked task TO the new task.""" + return _travel_hours(iv_lat, iv_lng, dest_lat, dest_lng) + + def _travel_to_next(next_lat, next_lng): + """Travel FROM the new task TO the next booked task.""" + return _travel_hours(dest_lat, dest_lng, next_lat, next_lng) + + def _check_gap_fits(cursor, dur, idx): + """Check if a slot at 'cursor' for 'dur' hours fits before + the interval at index 'idx' (accounting for travel TO that task).""" + if idx >= len(intervals): + return cursor + dur <= STORE_CLOSE + next_start, _ne, next_lat, next_lng = intervals[idx] + travel_fwd = _travel_to_next(next_lat, next_lng) + return cursor + dur + travel_fwd <= next_start + + # Walk through gaps, starting from preferred_start + cursor = max(preferred_start, STORE_OPEN) + + for i, (iv_start, iv_end, iv_lat, iv_lng) in enumerate(intervals): + if cursor + duration <= iv_start: + # Check travel time from new task end TO next booked task + if _check_gap_fits(cursor, duration, i): + return (cursor, cursor + duration) + # Not enough travel time -- try pushing start earlier or skip + # If we can't fit here, fall through to jump past this interval + # Jump past this booked interval + travel buffer from prev to new + new_cursor = max(cursor, iv_end) + travel = _travel_from_prev(iv_lat, iv_lng) + new_cursor += travel + # Snap to nearest 15 min + new_cursor = round(new_cursor * 4) / 4 + cursor = new_cursor + + # Check gap after last interval (no next task, so no forward travel needed) + if cursor + duration <= STORE_CLOSE: + return (cursor, cursor + duration) + + # No gap found from preferred_start onward -- wrap and try from start + if preferred_start > STORE_OPEN: + cursor = STORE_OPEN + for i, (iv_start, iv_end, iv_lat, iv_lng) in enumerate(intervals): + if cursor + duration <= iv_start: + if _check_gap_fits(cursor, duration, i): + return (cursor, cursor + duration) + new_cursor = max(cursor, iv_end) + travel = _travel_from_prev(iv_lat, iv_lng) + new_cursor += travel + new_cursor = round(new_cursor * 4) / 4 + cursor = new_cursor + if cursor + duration <= STORE_CLOSE: + return (cursor, cursor + duration) + + return (False, False) + + def _get_available_gaps(self, tech_id, date, exclude_task_id=False): + """Return a list of available (start, end) gaps for a technician on a date. + + Used by schedule_info_html to show green "available" badges. + Considers tasks where the tech is either lead or additional. + """ + STORE_OPEN, STORE_CLOSE = self._get_store_hours() + + if not tech_id or not date: + return [(STORE_OPEN, STORE_CLOSE)] + + domain = [ + '|', + ('technician_id', '=', tech_id), + ('additional_technician_ids', 'in', [tech_id]), + ('scheduled_date', '=', date), + ('status', 'not in', ['cancelled']), + ] + if exclude_task_id: + domain.append(('id', '!=', exclude_task_id)) + + booked = self.sudo().search(domain, order='time_start') + intervals = [(max(b.time_start, STORE_OPEN), min(b.time_end, STORE_CLOSE)) + for b in booked] + + gaps = [] + cursor = STORE_OPEN + for iv_start, iv_end in intervals: + if cursor < iv_start: + gaps.append((cursor, iv_start)) + cursor = max(cursor, iv_end) + if cursor < STORE_CLOSE: + gaps.append((cursor, STORE_CLOSE)) + return gaps + + @api.model + def _get_time_selection(self): + """Generate 12-hour time slots every 15 minutes, store hours only (9 AM - 6 PM).""" + times = [] + for hour in range(9, 18): # 9 AM to 5:45 PM + for minute in (0, 15, 30, 45): + float_val = hour + minute / 60.0 + key = f'{float_val:.2f}' + period = 'AM' if hour < 12 else 'PM' + display_hour = hour % 12 or 12 + label = f'{display_hour}:{minute:02d} {period}' + times.append((key, label)) + # Add 6:00 PM as end-time option + times.append(('18.00', '6:00 PM')) + return times + + @api.depends('time_start', 'time_end') + def _compute_time_12h(self): + """Sync the 12h selection fields from the raw float values.""" + for task in self: + task.time_start_12h = f'{(task.time_start or 9.0):.2f}' + task.time_end_12h = f'{(task.time_end or 10.0):.2f}' + + def _inverse_time_start_12h(self): + for task in self: + if task.time_start_12h: + task.time_start = float(task.time_start_12h) + + def _inverse_time_end_12h(self): + for task in self: + if task.time_end_12h: + task.time_end = float(task.time_end_12h) + + @api.depends('time_start', 'time_end') + def _compute_time_displays(self): + """Convert float hours to readable time strings.""" + for task in self: + task.time_start_display = self._float_to_time_str(task.time_start) + task.time_end_display = self._float_to_time_str(task.time_end) + + @api.onchange('task_type') + def _onchange_task_type_duration(self): + """Set default duration based on task type.""" + if self.task_type: + self.duration_hours = self.TASK_TYPE_DURATIONS.get(self.task_type, 1.0) + # Also recalculate end time + if self.time_start: + _open, close = self._get_store_hours() + self.time_end = min(self.time_start + self.duration_hours, close) + + @api.onchange('time_start', 'duration_hours') + def _onchange_compute_end_time(self): + """Auto-compute end time from start + duration. Also run overlap check.""" + if self.time_start and self.duration_hours: + _open, close = self._get_store_hours() + new_end = min(self.time_start + self.duration_hours, close) + self.time_end = new_end + # Run overlap snap if we have enough data + if self.technician_id and self.scheduled_date and self.time_start and self.time_end: + result = self._snap_if_overlap() + if result: + return result + + @api.depends('scheduled_date', 'time_start', 'time_end') + def _compute_datetimes(self): + """Combine date + float time into proper Datetime fields for calendar. + time_start/time_end are LOCAL hours; datetime_start/end must be UTC for Odoo.""" + import pytz + user_tz = self._get_local_tz() + for task in self: + if task.scheduled_date: + # Build local datetime, then convert to UTC + base = dt_datetime.combine(task.scheduled_date, dt_datetime.min.time()) + store_open, _close = task._get_store_hours() + local_start = user_tz.localize(base + timedelta(hours=task.time_start or store_open)) + local_end = user_tz.localize(base + timedelta(hours=task.time_end or (store_open + 1.0))) + task.datetime_start = local_start.astimezone(pytz.utc).replace(tzinfo=None) + task.datetime_end = local_end.astimezone(pytz.utc).replace(tzinfo=None) + else: + task.datetime_start = False + task.datetime_end = False + + def _inverse_datetime_start(self): + """When datetime_start is changed (e.g. from calendar drag), update date + time.""" + import pytz + user_tz = pytz.timezone(self.env.user.tz or 'UTC') + for task in self: + if task.datetime_start: + local_dt = pytz.utc.localize(task.datetime_start).astimezone(user_tz) + task.scheduled_date = local_dt.date() + task.time_start = local_dt.hour + local_dt.minute / 60.0 + + def _inverse_datetime_end(self): + """When datetime_end is changed (e.g. from calendar resize), update time_end.""" + import pytz + user_tz = pytz.timezone(self.env.user.tz or 'UTC') + for task in self: + if task.datetime_end: + local_dt = pytz.utc.localize(task.datetime_end).astimezone(user_tz) + task.time_end = local_dt.hour + local_dt.minute / 60.0 + + @api.depends('technician_id', 'scheduled_date') + def _compute_schedule_info(self): + """Show booked + available time slots for the technician on the selected date.""" + for task in self: + if not task.technician_id or not task.scheduled_date: + task.schedule_info_html = '' + continue + + exclude_id = task.id if task.id else 0 + # Find other tasks for the same technician+date (lead or additional) + others = self.sudo().search([ + '|', + ('technician_id', '=', task.technician_id.id), + ('additional_technician_ids', 'in', [task.technician_id.id]), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', exclude_id), + ], order='time_start') + + if not others: + s_open, s_close = self._get_store_hours() + open_str = self._float_to_time_str(s_open) + close_str = self._float_to_time_str(s_close) + task.schedule_info_html = Markup( + f'
' + f' All slots available ({open_str} - {close_str})
' + ) + continue + + # Booked badges + booked_lines = [] + for o in others: + start_str = self._float_to_time_str(o.time_start) + end_str = self._float_to_time_str(o.time_end) + type_label = dict(self._fields['task_type'].selection).get(o.task_type, o.task_type) + client_name = o.partner_id.name or '' + booked_lines.append( + f'' + f'{start_str} - {end_str} ({type_label}{" - " + client_name if client_name else ""})' + f'' + ) + + # Available gaps badges + gaps = self._get_available_gaps( + task.technician_id.id, task.scheduled_date, + exclude_task_id=exclude_id, + ) + avail_lines = [] + for g_start, g_end in gaps: + # Only show gaps >= 15 min + if g_end - g_start >= 0.25: + avail_lines.append( + f'' + f'{self._float_to_time_str(g_start)} - {self._float_to_time_str(g_end)}' + f'' + ) + + html_parts = [ + '
', + ' Booked: ', + ' '.join(booked_lines), + ] + if avail_lines: + html_parts.append( + '
' + 'Available: ' + + ' '.join(avail_lines) + ) + elif not avail_lines: + html_parts.append( + '
' + 'Fully booked' + ) + html_parts.append('
') + + task.schedule_info_html = Markup(''.join(html_parts)) + + @api.depends('technician_id', 'scheduled_date', 'time_start', + 'address_lat', 'address_lng', 'address_street') + def _compute_prev_task_summary(self): + """Show previous task info + travel time warning with color coding.""" + for task in self: + if not task.technician_id or not task.scheduled_date: + task.prev_task_summary_html = '' + continue + + exclude_id = task.id if task.id else 0 + # Find the task that ends just before this one starts (lead or additional) + prev_tasks = self.sudo().search([ + '|', + ('technician_id', '=', task.technician_id.id), + ('additional_technician_ids', 'in', [task.technician_id.id]), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', exclude_id), + ('time_end', '<=', task.time_start or 99.0), + ], order='time_end desc', limit=1) + + if not prev_tasks: + # Check if this is the first task of the day -- show start location info + task.prev_task_summary_html = Markup( + '
' + ' First task of the day -- ' + 'travel calculated from start location.
' + ) + continue + + prev = prev_tasks[0] + prev_start = self._float_to_time_str(prev.time_start) + prev_end = self._float_to_time_str(prev.time_end) + type_label = dict(self._fields['task_type'].selection).get( + prev.task_type, prev.task_type or '') + client_name = prev.partner_id.name or '' + prev_addr = prev.address_display or 'No address' + + # Calculate gap between prev task end and this task start + s_open, _s_close = self._get_store_hours() + gap_hours = (task.time_start or s_open) - (prev.time_end or s_open) + gap_minutes = int(gap_hours * 60) + + # Try to get travel time if both have coordinates + travel_minutes = 0 + travel_text = '' + if (prev.address_lat and prev.address_lng and + task.address_lat and task.address_lng): + travel_minutes = self._quick_travel_time( + prev.address_lat, prev.address_lng, + task.address_lat, task.address_lng, + ) + if travel_minutes > 0: + travel_text = f'{travel_minutes} min drive' + else: + travel_text = 'Could not calculate travel time' + elif prev.address_street and task.address_street: + travel_text = 'Save to calculate travel time' + else: + travel_text = 'Address missing -- cannot calculate travel' + + # Determine color coding + if travel_minutes > 0 and gap_minutes >= travel_minutes: + bg_class = 'alert-success' # Green -- enough time + icon = 'fa-check-circle' + status_text = ( + f'{gap_minutes} min gap -- enough travel time ' + f'(~{travel_minutes} min drive)' + ) + elif travel_minutes > 0 and gap_minutes > 0: + bg_class = 'alert-warning' # Yellow -- tight + icon = 'fa-exclamation-triangle' + status_text = ( + f'{gap_minutes} min gap -- tight! ' + f'Travel is ~{travel_minutes} min drive' + ) + elif travel_minutes > 0 and gap_minutes <= 0: + bg_class = 'alert-danger' # Red -- impossible + icon = 'fa-times-circle' + status_text = ( + f'No gap! Previous task ends at {prev_end}. ' + f'Travel is ~{travel_minutes} min drive' + ) + else: + bg_class = 'alert-info' # Blue -- no travel data yet + icon = 'fa-info-circle' + status_text = travel_text + + html = ( + f'
' + f' ' + f'Previous: {prev.name} ' + f'({type_label}) {prev_start} - {prev_end}' + f'{" -- " + client_name if client_name else ""}' + f'
' + f' {prev_addr}' + f'
' + f' {status_text}' + f'
' + ) + task.prev_task_summary_html = Markup(html) + + def _quick_travel_time(self, from_lat, from_lng, to_lat, to_lng): + """Quick inline travel time calculation using Google Distance Matrix API. + Returns travel time in minutes, or 0 if unavailable.""" + try: + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.google_maps_api_key', '') + if not api_key: + return 0 + + url = 'https://maps.googleapis.com/maps/api/distancematrix/json' + params = { + 'origins': f'{from_lat},{from_lng}', + 'destinations': f'{to_lat},{to_lng}', + 'mode': 'driving', + 'avoid': 'tolls', + 'departure_time': 'now', + 'key': api_key, + } + resp = requests.get(url, params=params, timeout=5) + data = resp.json() + if data.get('status') == 'OK': + elements = data['rows'][0]['elements'][0] + if elements.get('status') == 'OK': + # Use duration_in_traffic if available, else duration + duration = elements.get( + 'duration_in_traffic', elements.get('duration', {})) + seconds = duration.get('value', 0) + return max(1, int(seconds / 60)) + except Exception: + _logger.warning('Failed to calculate travel time', exc_info=True) + return 0 + + @api.depends('status') + def _compute_color(self): + color_map = { + 'pending': 5, # purple + 'scheduled': 0, # grey + 'en_route': 4, # blue + 'in_progress': 2, # orange + 'completed': 10, # green + 'cancelled': 1, # red + 'rescheduled': 3, # yellow + } + for task in self: + task.color = color_map.get(task.status, 0) + + @api.depends('address_street', 'address_street2', 'address_city', + 'address_state_id', 'address_zip') + def _compute_address_display(self): + for task in self: + street = task.address_street or '' + # If the street field already contains a full address (has a comma), + # use it directly -- Google Places stores the formatted address here. + if ',' in street and ( + (task.address_city and task.address_city in street) or + (task.address_zip and task.address_zip in street) + ): + # Street already has full address; just append unit if separate + if task.address_street2 and task.address_street2 not in street: + task.address_display = f"{street}, {task.address_street2}" + else: + task.address_display = street + else: + # Build from components (manual entry or legacy data) + parts = [ + street, + task.address_street2, + task.address_city, + task.address_state_id.name if task.address_state_id else '', + task.address_zip, + ] + task.address_display = ', '.join([p for p in parts if p]) + + # ------------------------------------------------------------------ + # ONCHANGE - Auto-fill address from client + # ------------------------------------------------------------------ + + @api.onchange('is_in_store') + def _onchange_is_in_store(self): + """Auto-fill company address when task is marked as in-store.""" + if self.is_in_store: + company_partner = self.env.company.partner_id + if company_partner and company_partner.street: + self._fill_address_from_partner(company_partner) + else: + self.address_street = self.env.company.name or 'In Store' + + @api.onchange('partner_id') + def _onchange_partner_id(self): + """Auto-fill address fields from the selected client's address.""" + if self.is_in_store: + return + if self.partner_id: + addr = self.partner_id + self.address_partner_id = addr.id + self.address_street = addr.street or '' + self.address_street2 = addr.street2 or '' + self.address_city = addr.city or '' + self.address_state_id = addr.state_id.id if addr.state_id else False + self.address_zip = addr.zip or '' + self.address_lat = addr.x_fc_latitude if hasattr(addr, 'x_fc_latitude') and addr.x_fc_latitude else 0 + self.address_lng = addr.x_fc_longitude if hasattr(addr, 'x_fc_longitude') and addr.x_fc_longitude else 0 + + def _fill_address_from_partner(self, addr): + """Populate address fields from a partner record.""" + if not addr: + return + self.address_partner_id = addr.id + self.address_street = addr.street or '' + self.address_street2 = addr.street2 or '' + self.address_city = addr.city or '' + self.address_state_id = addr.state_id.id if addr.state_id else False + self.address_zip = addr.zip or '' + self.address_lat = addr.x_fc_latitude if hasattr(addr, 'x_fc_latitude') and addr.x_fc_latitude else 0 + self.address_lng = addr.x_fc_longitude if hasattr(addr, 'x_fc_longitude') and addr.x_fc_longitude else 0 + + # ------------------------------------------------------------------ + # CONSTRAINTS + VALIDATION + # ------------------------------------------------------------------ + + @api.constrains('address_street', 'address_lat', 'address_lng', 'is_in_store') + def _check_address_required(self): + """Non-in-store tasks must have a geocoded address.""" + for task in self: + if task.x_fc_sync_source: + continue + if task.is_in_store: + continue + if not task.address_street: + raise ValidationError(_( + "A valid address is required. If this task is at the store, " + "please check the 'In Store' option." + )) + + @api.constrains('technician_id', 'additional_technician_ids', + 'scheduled_date', 'time_start', 'time_end') + def _check_no_overlap(self): + """Prevent overlapping bookings for the same technician on the same date. + + Checks both the lead technician and all additional technicians. + """ + for task in self: + if task.status == 'cancelled': + continue + if task.x_fc_sync_source: + continue + # Validate time range + if task.time_start >= task.time_end: + raise ValidationError(_("Start time must be before end time.")) + # Validate store hours + s_open, s_close = self._get_store_hours() + if task.time_start < s_open or task.time_end > s_close: + open_str = self._float_to_time_str(s_open) + close_str = self._float_to_time_str(s_close) + raise ValidationError(_( + "Tasks must be scheduled within store hours (%s - %s)." + ) % (open_str, close_str)) + # Validate not in the past (only for new/scheduled local tasks) + if task.status == 'scheduled' and task.scheduled_date and not task.x_fc_sync_source: + local_now = self._local_now() + today = local_now.date() + if task.scheduled_date < today: + raise ValidationError(_("Cannot schedule tasks in the past.")) + if task.scheduled_date == today: + current_hour = local_now.hour + local_now.minute / 60.0 + if task.time_start < current_hour: + pass # Allow editing existing tasks that started earlier today + # Check overlap for lead + additional technicians + all_tech_ids = (task.technician_id | task.additional_technician_ids).ids + for tech_id in all_tech_ids: + tech_name = self.env['res.users'].browse(tech_id).name + overlapping = self.sudo().search([ + '|', + ('technician_id', '=', tech_id), + ('additional_technician_ids', 'in', [tech_id]), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', task.id), + ('time_start', '<', task.time_end), + ('time_end', '>', task.time_start), + ], limit=1) + if overlapping: + start_str = self._float_to_time_str(overlapping.time_start) + end_str = self._float_to_time_str(overlapping.time_end) + raise ValidationError(_( + "%(tech)s has a time conflict with %(task)s " + "(%(start)s - %(end)s). Please choose a different time.", + tech=tech_name, + task=overlapping.name, + start=start_str, + end=end_str, + )) + + for cal_start, cal_end in self._get_calendar_busy_intervals( + tech_id, task.scheduled_date + ): + if cal_start < task.time_end and cal_end > task.time_start: + raise ValidationError(_( + "%(tech)s has a calendar event conflict " + "(%(start)s - %(end)s). Please choose a different time.", + tech=tech_name, + start=self._float_to_time_str(cal_start), + end=self._float_to_time_str(cal_end), + )) + + # Check travel time gaps for lead technician only + # (additional techs travel with the lead, same destination) + next_task = self.sudo().search([ + '|', + ('technician_id', '=', task.technician_id.id), + ('additional_technician_ids', 'in', [task.technician_id.id]), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', task.id), + ('time_start', '>=', task.time_end), + ], order='time_start', limit=1) + if next_task and task.address_lat and task.address_lng and \ + next_task.address_lat and next_task.address_lng: + travel_min = self._quick_travel_time( + task.address_lat, task.address_lng, + next_task.address_lat, next_task.address_lng, + ) + if travel_min > 0: + gap_min = int((next_task.time_start - task.time_end) * 60) + if gap_min < travel_min: + raise ValidationError(_( + "Not enough travel time to the next task!\n\n" + "This task ends at %(end)s, and %(next)s starts " + "at %(next_start)s (%(gap)d min gap).\n" + "Travel time is ~%(travel)d minutes.\n\n" + "Please allow at least %(travel)d minutes between tasks.", + end=self._float_to_time_str(task.time_end), + next=next_task.name, + next_start=self._float_to_time_str(next_task.time_start), + gap=gap_min, + travel=travel_min, + )) + + prev_task = self.sudo().search([ + '|', + ('technician_id', '=', task.technician_id.id), + ('additional_technician_ids', 'in', [task.technician_id.id]), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', task.id), + ('time_end', '<=', task.time_start), + ], order='time_end desc', limit=1) + if prev_task and task.address_lat and task.address_lng and \ + prev_task.address_lat and prev_task.address_lng: + travel_min = self._quick_travel_time( + prev_task.address_lat, prev_task.address_lng, + task.address_lat, task.address_lng, + ) + if travel_min > 0: + gap_min = int((task.time_start - prev_task.time_end) * 60) + if gap_min < travel_min: + raise ValidationError(_( + "Not enough travel time from the previous task!\n\n" + "%(prev)s ends at %(prev_end)s, and this task starts " + "at %(start)s (%(gap)d min gap).\n" + "Travel time is ~%(travel)d minutes.\n\n" + "Please allow at least %(travel)d minutes between tasks.", + prev=prev_task.name, + prev_end=self._float_to_time_str(prev_task.time_end), + start=self._float_to_time_str(task.time_start), + gap=gap_min, + travel=travel_min, + )) + + @api.onchange('technician_id', 'scheduled_date') + def _onchange_technician_date_autoset(self): + """Auto-set start/end time to the first available slot when tech+date change.""" + if not self.technician_id or not self.scheduled_date: + return + exclude_id = self._origin.id if self._origin else False + duration = self.duration_hours or 1.0 + s_open, _s_close = self._get_store_hours() + preferred = self.time_start or s_open + start, end = self._find_next_available_slot( + self.technician_id.id, + self.scheduled_date, + preferred_start=preferred, + duration=duration, + exclude_task_id=exclude_id, + dest_lat=self.address_lat or 0, + dest_lng=self.address_lng or 0, + ) + if start is not False: + self.time_start = start + self.time_end = end + self.duration_hours = end - start + else: + return {'warning': { + 'title': _('Fully Booked'), + 'message': _( + '%s is fully booked on %s. No available slots.' + ) % (self.technician_id.name, + self.scheduled_date.strftime('%B %d, %Y')), + }} + + def _snap_if_overlap(self): + """Check if current time_start/time_end overlaps with another task. + If so, auto-snap to the next available slot and return a warning dict.""" + if not self.technician_id or not self.scheduled_date or not self.time_start: + return None + exclude_id = self._origin.id if self._origin else 0 + duration = max(self.duration_hours or 1.0, 0.25) + + all_tech_ids = (self.technician_id | self.additional_technician_ids).ids + overlapping = self.sudo().search([ + '|', + ('technician_id', 'in', all_tech_ids), + ('additional_technician_ids', 'in', all_tech_ids), + ('scheduled_date', '=', self.scheduled_date), + ('status', 'not in', ['cancelled']), + ('id', '!=', exclude_id), + ('time_start', '<', self.time_end), + ('time_end', '>', self.time_start), + ], limit=1) + if overlapping: + conflict_name = overlapping.name + conflict_start = self._float_to_time_str(overlapping.time_start) + conflict_end = self._float_to_time_str(overlapping.time_end) + start, end = self._find_next_available_slot( + self.technician_id.id, + self.scheduled_date, + preferred_start=self.time_start, + duration=duration, + exclude_task_id=exclude_id, + dest_lat=self.address_lat or 0, + dest_lng=self.address_lng or 0, + ) + if start is not False: + new_start_str = self._float_to_time_str(start) + new_end_str = self._float_to_time_str(end) + self.time_start = start + self.time_end = end + self.duration_hours = end - start + return {'warning': { + 'title': _('Moved to Available Slot'), + 'message': _( + 'The selected time conflicts with %s (%s - %s).\n' + 'Automatically moved to: %s - %s.' + ) % (conflict_name, conflict_start, conflict_end, + new_start_str, new_end_str), + }} + else: + return {'warning': { + 'title': _('No Available Slots'), + 'message': _( + 'The selected time conflicts with %s (%s - %s) ' + 'and no other slots are available on this day.' + ) % (conflict_name, conflict_start, conflict_end), + }} + return None + + # ------------------------------------------------------------------ + # DEFAULT_GET - Calendar pre-fill + # ------------------------------------------------------------------ + + def _snap_to_quarter(self, hour_float): + """Round a float hour to the nearest 15-minute slot and clamp to store hours.""" + s_open, s_close = self._get_store_hours() + snapped = round(hour_float * 4) / 4 + return max(s_open, min(s_close, snapped)) + + @api.model + def default_get(self, fields_list): + """Handle calendar time range selection: pre-fill date + times from context.""" + res = super().default_get(fields_list) + ctx = self.env.context + + # Set duration default based on task type from context + task_type = ctx.get('default_task_type', res.get('task_type', 'delivery')) + if 'duration_hours' not in res or not res.get('duration_hours'): + res['duration_hours'] = self.TASK_TYPE_DURATIONS.get(task_type, 1.0) + + # When user clicks a time range on the calendar, Odoo passes + # default_datetime_start/end in UTC + dt_start_utc = None + dt_end_utc = None + if ctx.get('default_datetime_start'): + try: + dt_start_utc = fields.Datetime.from_string(ctx['default_datetime_start']) + except (ValueError, TypeError): + pass + if ctx.get('default_datetime_end'): + try: + dt_end_utc = fields.Datetime.from_string(ctx['default_datetime_end']) + except (ValueError, TypeError): + pass + + if dt_start_utc or dt_end_utc: + import pytz + user_tz = pytz.timezone(self.env.user.tz or 'UTC') + + if dt_start_utc: + dt_start_local = pytz.utc.localize(dt_start_utc).astimezone(user_tz) + res['scheduled_date'] = dt_start_local.date() + start_float = self._snap_to_quarter( + dt_start_local.hour + dt_start_local.minute / 60.0) + res['time_start'] = start_float + + if dt_end_utc: + dt_end_local = pytz.utc.localize(dt_end_utc).astimezone(user_tz) + end_float = self._snap_to_quarter( + dt_end_local.hour + dt_end_local.minute / 60.0) + if 'time_start' in res and end_float <= res['time_start']: + end_float = res['time_start'] + 1.0 + res['time_end'] = end_float + # Compute duration from the calendar drag + if 'time_start' in res: + res['duration_hours'] = end_float - res['time_start'] + + # Always compute end from start + duration if not already set + if 'time_end' not in res and 'time_start' in res and 'duration_hours' in res: + _open, close = self._get_store_hours() + res['time_end'] = min( + res['time_start'] + res['duration_hours'], close) + + return res + + # ------------------------------------------------------------------ + # CRUD OVERRIDES + # ------------------------------------------------------------------ + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('name', _('New')) == _('New'): + vals['name'] = self.env['ir.sequence'].next_by_code('fusion.technician.task') or _('New') + if not vals.get('x_fc_sync_uuid') and not vals.get('x_fc_sync_source'): + vals['x_fc_sync_uuid'] = str(uuid.uuid4()) + # In-store tasks: auto-fill company address + if vals.get('is_in_store') and not vals.get('address_street'): + company_partner = self.env.company.partner_id + if company_partner and company_partner.street: + self._fill_address_vals(vals, company_partner) + else: + vals['address_street'] = self.env.company.name or 'In Store' + # Hook: fill address from linked records (overridden by fusion_claims) + self._create_vals_fill(vals) + records = super().create(vals_list) + # Hook: post-create actions for linked records + records._on_create_post_actions() + # Auto-calculate travel times for the full day chain + if not self.env.context.get('skip_travel_recalc'): + records._recalculate_day_travel_chains() + # Send "Appointment Scheduled" email + for rec in records: + rec._send_task_scheduled_email() + # Push new local tasks to remote instances + local_records = records.filtered(lambda r: not r.x_fc_sync_source) + if local_records and not self.env.context.get('skip_task_sync'): + self.env['fusion.task.sync.config']._push_tasks(local_records, 'create') + # Sync to calendar for external calendar integrations + records._sync_calendar_event() + return records + + def _create_vals_fill(self, vals): + """Hook: fill address from linked records during create. + + Base implementation fills from partner_id. Override in fusion_claims + to also fill from sale_order_id or purchase_order_id. + """ + if vals.get('partner_id') and not vals.get('address_street'): + partner = self.env['res.partner'].browse(vals['partner_id']) + if partner.street: + self._fill_address_vals(vals, partner) + + def _on_create_post_actions(self): + """Hook: post-create side-effects for linked records. + + Override in fusion_claims to post chatter messages to linked orders, + mark sale orders as ready for delivery, etc. + """ + pass + + def write(self, vals): + if self.env.context.get('skip_travel_recalc'): + res = super().write(vals) + if ('status' in vals and vals['status'] in ('completed', 'cancelled') + and not self.env.context.get('skip_task_sync')): + shadow_records = self.filtered(lambda r: r.x_fc_sync_source) + if shadow_records: + self.env['fusion.task.sync.config']._push_shadow_status(shadow_records) + local_records = self.filtered(lambda r: not r.x_fc_sync_source) + if local_records: + self.env['fusion.task.sync.config']._push_tasks(local_records, 'write') + return res + + # Safety: ensure time_end is consistent when start/duration change + # but time_end wasn't sent (readonly field in view may not save) + if ('time_start' in vals or 'duration_hours' in vals) and 'time_end' not in vals: + _open, close = self._get_store_hours() + start = vals.get('time_start', self[:1].time_start if len(self) == 1 else 9.0) + dur = vals.get('duration_hours', self[:1].duration_hours if len(self) == 1 else 1.0) or 1.0 + vals['time_end'] = min(start + dur, close) + + # Detect reschedule mode: capture old values BEFORE write + reschedule_mode = self.env.context.get('reschedule_mode') + old_schedule = {} + schedule_fields = {'scheduled_date', 'time_start', 'time_end', + 'duration_hours', 'technician_id'} + schedule_changed = schedule_fields & set(vals.keys()) + if reschedule_mode and schedule_changed: + for task in self: + old_schedule[task.id] = { + 'date': task.scheduled_date, + 'time_start': task.time_start, + 'time_end': task.time_end, + } + + # Capture old tech+date combos BEFORE write for travel recalc + travel_fields = {'address_street', 'address_city', 'address_zip', 'address_lat', 'address_lng', + 'scheduled_date', 'sequence', 'time_start', 'technician_id', + 'additional_technician_ids'} + needs_travel_recalc = travel_fields & set(vals.keys()) + old_combos = set() + if needs_travel_recalc: + for t in self: + old_combos.add((t.technician_id.id, t.scheduled_date)) + for tech in t.additional_technician_ids: + old_combos.add((tech.id, t.scheduled_date)) + res = super().write(vals) + if needs_travel_recalc: + new_combos = set() + for t in self: + new_combos.add((t.technician_id.id, t.scheduled_date)) + for tech in t.additional_technician_ids: + new_combos.add((tech.id, t.scheduled_date)) + all_combos = old_combos | new_combos + self._recalculate_combos_travel(all_combos) + + # After write: send reschedule email if schedule actually changed + if reschedule_mode and old_schedule: + for task in self: + old = old_schedule.get(task.id, {}) + if old and ( + old['date'] != task.scheduled_date + or abs(old['time_start'] - task.time_start) > 0.01 + or abs(old['time_end'] - task.time_end) > 0.01 + ): + task._post_status_message('rescheduled') + task._send_task_rescheduled_email( + old_date=old['date'], + old_start=old['time_start'], + old_end=old['time_end'], + ) + # Push updates to remote instances for local tasks + sync_fields = {'technician_id', 'additional_technician_ids', + 'scheduled_date', 'time_start', 'time_end', + 'duration_hours', 'status', 'task_type', 'address_street', + 'address_city', 'address_zip', 'address_lat', 'address_lng', + 'partner_id'} + if sync_fields & set(vals.keys()) and not self.env.context.get('skip_task_sync'): + local_records = self.filtered(lambda r: not r.x_fc_sync_source) + if local_records: + self.env['fusion.task.sync.config']._push_tasks(local_records, 'write') + if 'status' in vals and vals['status'] in ('completed', 'cancelled'): + shadow_records = self.filtered(lambda r: r.x_fc_sync_source) + if shadow_records: + self.env['fusion.task.sync.config']._push_shadow_status(shadow_records) + # Re-sync calendar event when schedule fields change + cal_fields = {'scheduled_date', 'time_start', 'time_end', + 'duration_hours', 'technician_id', 'task_type', + 'partner_id', 'address_street', 'address_city', 'notes'} + if cal_fields & set(vals.keys()): + self._sync_calendar_event() + return res + + def _sync_calendar_event(self): + """Create or update a linked calendar.event for external calendar sync. + + Only syncs tasks that have a scheduled date and an assigned technician. + Uses sudo() because portal users should not need calendar write access. + Falls back gracefully if external calendar validation fails (e.g. + Microsoft Calendar requires the organizer to have Outlook synced). + """ + silent_ctx = { + 'dont_notify': True, + 'mail_create_nosubscribe': True, + 'mail_create_nolog': True, + 'no_mail_notification': True, + 'no_mail_to_attendees': True, + 'skip_attendee_notification': True, + } + CalendarEvent = self.env['calendar.event'].sudo().with_context(**silent_ctx) + for task in self: + if not task.datetime_start or not task.datetime_end or not task.technician_id: + if task.calendar_event_id: + task.calendar_event_id.with_context(**silent_ctx).unlink() + task.with_context(skip_travel_recalc=True).write({'calendar_event_id': False}) + continue + + order = task._get_linked_order() + partner = task.partner_id or (order.partner_id if order else False) + client_name = partner.name if partner else '' + type_label = dict(self._fields['task_type'].selection).get(task.task_type, task.task_type or '') + + event_name = f"{type_label}: {client_name}" if client_name else f"{type_label} - {task.name}" + location_parts = [task.address_street, task.address_city] + location = ', '.join(p for p in location_parts if p) or '' + + description_parts = [] + if order: + description_parts.append(f"Ref: {order.name}") + if task.description: + description_parts.append(task.description) + + vals = { + 'name': event_name, + 'start': task.datetime_start, + 'stop': task.datetime_end, + 'user_id': task.technician_id.id, + 'location': location, + 'partner_ids': [(6, 0, (task.technician_id | task.additional_technician_ids).mapped('partner_id').ids)], + 'show_as': 'busy', + 'description': '\n'.join(description_parts), + } + + try: + if task.calendar_event_id: + task.calendar_event_id.with_context(**silent_ctx).write(vals) + else: + event = CalendarEvent.create(vals) + task.with_context(skip_travel_recalc=True).write({'calendar_event_id': event.id}) + except Exception as e: + _logger.warning( + "Calendar sync skipped for task %s (tech=%s): %s", + task.name, task.technician_id.name, e, + ) + if not task.calendar_event_id: + try: + vals['user_id'] = self.env.uid + event = CalendarEvent.create(vals) + task.with_context(skip_travel_recalc=True).write({'calendar_event_id': event.id}) + except Exception: + pass + + @api.model + def _fill_address_vals(self, vals, partner): + """Helper to fill address vals dict from a partner record.""" + vals.update({ + 'address_partner_id': partner.id, + 'address_street': partner.street or '', + 'address_street2': partner.street2 or '', + 'address_city': partner.city or '', + 'address_state_id': partner.state_id.id if partner.state_id else False, + 'address_zip': partner.zip or '', + 'address_lat': partner.x_fc_latitude if hasattr(partner, 'x_fc_latitude') else 0, + 'address_lng': partner.x_fc_longitude if hasattr(partner, 'x_fc_longitude') else 0, + }) + + def _post_task_created_to_linked_order(self): + """Hook: post task creation notice to linked order chatter. + Override in fusion_claims.""" + pass + + def _mark_sale_order_ready_for_delivery(self): + """Hook: mark linked sale orders as ready for delivery. + Override in fusion_claims.""" + pass + + def _recalculate_day_travel_chains(self): + """Recalculate travel for all tech+date combos affected by these tasks. + + Includes combos for additional technicians so their schedules update too. + """ + combos = set() + for t in self: + if not t.scheduled_date: + continue + if t.technician_id: + combos.add((t.technician_id.id, t.scheduled_date)) + for tech in t.additional_technician_ids: + combos.add((tech.id, t.scheduled_date)) + self._recalculate_combos_travel(combos) + + def _get_technician_start_address(self, tech_id): + """Get the start address for a technician. + + Priority: + 1. Technician's personal x_fc_start_address (if set) + 2. Company default HQ address (fusion_claims.technician_start_address) + Returns the address string or ''. + """ + tech_user = self.env['res.users'].sudo().browse(tech_id) + if tech_user.exists() and tech_user.x_fc_start_address: + return tech_user.x_fc_start_address.strip() + # Fallback to company default + return (self.env['ir.config_parameter'].sudo() + .get_param('fusion_claims.technician_start_address', '') or '').strip() + + def _geocode_address_string(self, address, api_key): + """Geocode an address string and return (lat, lng) or (0.0, 0.0).""" + if not address or not api_key: + return 0.0, 0.0 + try: + url = 'https://maps.googleapis.com/maps/api/geocode/json' + params = {'address': address, 'key': api_key, 'region': 'ca'} + resp = requests.get(url, params=params, timeout=10) + data = resp.json() + if data.get('status') == 'OK' and data.get('results'): + loc = data['results'][0]['geometry']['location'] + return loc['lat'], loc['lng'] + except Exception as e: + _logger.warning("Address geocoding failed for '%s': %s", address, e) + return 0.0, 0.0 + + def _recalculate_combos_travel(self, combos): + """Recalculate travel for a set of (tech_id, date) combinations. + + Start-point priority per technician (for today only): + 1. Latest GPS location (from periodic tracking or task actions) + 2. Actual GPS from today's fusion_clock check-in + 3. Personal start address (x_fc_start_address) + 4. Company default HQ address + For future dates, only 3 and 4 apply. + """ + ICP = self.env['ir.config_parameter'].sudo() + enabled = ICP.get_param('fusion_claims.google_distance_matrix_enabled', False) + if not enabled: + return + api_key = self._get_google_maps_api_key() + + start_coords_cache = {} + today = self._local_now().date() + today_str = str(today) + + today_tech_ids = {tid for tid, d in combos + if tid and str(d) == today_str} + clock_locations = {} + if today_tech_ids: + clock_locations = self._get_clock_in_locations(today_tech_ids, today) + + for tech_id, date in combos: + if not tech_id or not date: + continue + + cache_key = (tech_id, str(date)) + if cache_key not in start_coords_cache: + if str(date) == today_str: + # Try latest GPS first (most accurate real-time position) + lat, lng = self._get_tech_current_location(tech_id) + if lat and lng: + start_coords_cache[cache_key] = (lat, lng) + elif tech_id in clock_locations: + cl = clock_locations[tech_id] + start_coords_cache[cache_key] = (cl['lat'], cl['lng']) + else: + addr = self._get_technician_start_address(tech_id) + start_coords_cache[cache_key] = self._geocode_address_string(addr, api_key) + else: + addr = self._get_technician_start_address(tech_id) + start_coords_cache[cache_key] = self._geocode_address_string(addr, api_key) + + all_day_tasks = self.sudo().search([ + '|', + ('technician_id', '=', tech_id), + ('additional_technician_ids', 'in', [tech_id]), + ('scheduled_date', '=', date), + ('status', 'not in', ['cancelled']), + ], order='time_start, sequence, id') + if not all_day_tasks: + continue + + prev_lat, prev_lng = start_coords_cache[cache_key] + for i, task in enumerate(all_day_tasks): + if not (task.address_lat and task.address_lng): + task._geocode_address() + travel_vals = {} + if prev_lat and prev_lng and task.address_lat and task.address_lng: + task.with_context(skip_travel_recalc=True)._calculate_travel_time(prev_lat, prev_lng) + travel_vals['previous_task_id'] = all_day_tasks[i - 1].id if i > 0 else False + travel_vals['travel_origin'] = 'Clock-In Location' if i == 0 and str(date) == today_str and tech_id in clock_locations else ('Start Location' if i == 0 else f'Task {all_day_tasks[i - 1].name}') + if travel_vals: + task.with_context(skip_travel_recalc=True).write(travel_vals) + prev_lat = task.address_lat or prev_lat + prev_lng = task.address_lng or prev_lng + + # ------------------------------------------------------------------ + # LIVE TRAVEL RECALCULATION (uses tech's current GPS position) + # ------------------------------------------------------------------ + + def _get_tech_current_location(self, tech_id): + """Get the technician's most recent GPS location. + + Priority: + 1. Latest fusion.technician.location record from last 30 min + 2. Latest action_latitude/longitude from today's tasks + 3. Clock-in location + 4. None (caller falls back to start address) + """ + Location = self.env['fusion.technician.location'].sudo() + cutoff = fields.Datetime.subtract(fields.Datetime.now(), minutes=30) + latest = Location.search([ + ('user_id', '=', tech_id), + ('logged_at', '>', cutoff), + ('source', '!=', 'sync'), + ], order='logged_at desc', limit=1) + if latest and latest.latitude and latest.longitude: + return latest.latitude, latest.longitude + + # Fallback: last completed task's location today + today = self._local_now().date() + last_completed = self.sudo().search([ + ('technician_id', '=', tech_id), + ('scheduled_date', '=', today), + ('status', '=', 'completed'), + ('completed_latitude', '!=', 0), + ('completed_longitude', '!=', 0), + ], order='completion_datetime desc', limit=1) + if last_completed: + return last_completed.completed_latitude, last_completed.completed_longitude + + # Fallback: clock-in location + clock_locs = self._get_clock_in_locations({tech_id}, today) + if tech_id in clock_locs: + cl = clock_locs[tech_id] + return cl['lat'], cl['lng'] + + return None, None + + def _recalculate_travel_from_current_location(self): + """Recalculate travel time for THIS task from the tech's current GPS. + + Called when tech starts en_route to get a live ETA. + """ + self.ensure_one() + ICP = self.env['ir.config_parameter'].sudo() + if not ICP.get_param('fusion_claims.google_distance_matrix_enabled', False): + return + tech_id = self.technician_id.id + if not tech_id: + return + lat, lng = self._get_tech_current_location(tech_id) + if lat and lng and self.address_lat and self.address_lng: + self.with_context(skip_travel_recalc=True)._calculate_travel_time(lat, lng) + self.with_context(skip_travel_recalc=True).write({ + 'travel_origin': 'Current Location (Live)', + }) + + def _recalculate_remaining_tasks_travel(self): + """After completing a task, recalculate travel for all remaining tasks + in the chain using the completion location as the new origin. + + This ensures ETAs update in real-time as the tech progresses through + their schedule, and the route reflects their actual position. + """ + self.ensure_one() + ICP = self.env['ir.config_parameter'].sudo() + if not ICP.get_param('fusion_claims.google_distance_matrix_enabled', False): + return + + tech_id = self.technician_id.id + if not tech_id or not self.scheduled_date: + return + + # Use completion GPS as origin for next task + origin_lat = self.completed_latitude or self.action_latitude + origin_lng = self.completed_longitude or self.action_longitude + + # If no GPS from completion, try task address (tech was physically there) + if not origin_lat or not origin_lng: + origin_lat = self.address_lat + origin_lng = self.address_lng + + if not origin_lat or not origin_lng: + return + + remaining = self.sudo().search([ + '|', + ('technician_id', '=', tech_id), + ('additional_technician_ids', 'in', [tech_id]), + ('scheduled_date', '=', self.scheduled_date), + ('status', 'not in', ['completed', 'cancelled']), + ('time_start', '>=', self.time_start), + ], order='time_start, sequence, id') + + if not remaining: + return + + prev_lat, prev_lng = origin_lat, origin_lng + for i, task in enumerate(remaining): + if not (task.address_lat and task.address_lng): + task._geocode_address() + if prev_lat and prev_lng and task.address_lat and task.address_lng: + task.with_context(skip_travel_recalc=True)._calculate_travel_time( + prev_lat, prev_lng) + origin_label = (f'Completed: {self.name}' if i == 0 + else f'Task {remaining[i - 1].name}') + task.with_context(skip_travel_recalc=True).write({ + 'previous_task_id': self.id if i == 0 else remaining[i - 1].id, + 'travel_origin': origin_label, + }) + prev_lat = task.address_lat or prev_lat + prev_lng = task.address_lng or prev_lng + + # ------------------------------------------------------------------ + # STATUS ACTIONS + # ------------------------------------------------------------------ + + def _check_previous_tasks_completed(self): + """Check that all earlier tasks for the same technician+date are completed. + + Considers tasks where the technician is either lead or additional. + """ + self.ensure_one() + earlier_incomplete = self.sudo().search([ + '|', + ('technician_id', '=', self.technician_id.id), + ('additional_technician_ids', 'in', [self.technician_id.id]), + ('scheduled_date', '=', self.scheduled_date), + ('time_start', '<', self.time_start), + ('status', 'not in', ['completed', 'cancelled']), + ('id', '!=', self.id), + ], limit=1) + if earlier_incomplete: + raise UserError(_( + "Please complete previous task %s first before starting this one." + ) % earlier_incomplete.name) + + def _write_action_location(self, extra_vals=None): + """Write GPS coordinates from context onto the task record.""" + ctx = self.env.context + lat = ctx.get('action_latitude', 0) + lng = ctx.get('action_longitude', 0) + acc = ctx.get('action_accuracy', 0) + vals = { + 'action_latitude': lat, + 'action_longitude': lng, + 'action_location_accuracy': acc, + } + if extra_vals: + vals.update(extra_vals) + if lat and lng: + self.with_context(skip_travel_recalc=True).write(vals) + + def action_start_en_route(self): + """Mark task as En Route.""" + for task in self: + if task.status != 'scheduled': + raise UserError(_("Only scheduled tasks can be marked as En Route.")) + task._check_previous_tasks_completed() + task.status = 'en_route' + task._write_action_location() + task._post_status_message('en_route') + task._send_task_en_route_email() + # Recalculate travel from tech's current location to THIS task + task._recalculate_travel_from_current_location() + if task.x_fc_sync_source: + try: + self.env['fusion.task.sync.config']._push_shadow_status(task) + except Exception: + _logger.exception( + "Failed to push en_route for shadow %s", task.name) + try: + remaining = self.sudo().search_count([ + ('technician_id', '=', task.technician_id.id), + ('scheduled_date', '=', task.scheduled_date), + ('status', 'in', ['scheduled', 'en_route']), + ('id', '!=', task.id), + ]) + client = task.client_display_name or 'your next client' + ttype = dict(self._fields['task_type'].selection).get( + task.task_type, task.task_type or 'Task') + task._send_push_notification( + f'En Route to {client}', + f'{ttype} at {task.address_display or "scheduled location"}. ' + f'{remaining} more task(s) today.', + ) + except Exception: + pass + + def action_start_task(self): + """Mark task as In Progress.""" + for task in self: + if task.status not in ('scheduled', 'en_route'): + raise UserError(_("Task must be scheduled or en route to start.")) + task._check_previous_tasks_completed() + task.status = 'in_progress' + ctx = self.env.context + task._write_action_location({ + 'started_latitude': ctx.get('action_latitude', 0), + 'started_longitude': ctx.get('action_longitude', 0), + }) + task._post_status_message('in_progress') + + def action_complete_task(self): + """Mark task as Completed.""" + for task in self: + if task.status not in ('in_progress', 'en_route', 'scheduled'): + raise UserError(_("Task must be in progress to complete.")) + + task._check_completion_requirements() + + ctx = self.env.context + task.with_context(skip_travel_recalc=True).write({ + 'status': 'completed', + 'completion_datetime': fields.Datetime.now(), + 'completed_latitude': ctx.get('action_latitude', 0), + 'completed_longitude': ctx.get('action_longitude', 0), + 'action_latitude': ctx.get('action_latitude', 0), + 'action_longitude': ctx.get('action_longitude', 0), + 'action_location_accuracy': ctx.get('action_accuracy', 0), + }) + task._post_status_message('completed') + task._post_completion_to_linked_order() + task._notify_scheduler_on_completion() + task._send_task_completion_email() + + # Recalculate travel for remaining tasks from this completion location + task._recalculate_remaining_tasks_travel() + + task._on_complete_extra() + + def _check_completion_requirements(self): + """Hook: check additional requirements before task completion. + Override in fusion_claims for rental inspection checks.""" + pass + + def _on_complete_extra(self): + """Hook: additional side-effects after task completion. + Override in fusion_claims for ODSP advancement and rental inspection.""" + pass + + def action_cancel_task(self): + """Cancel the task. Sends cancellation email and runs cancel hooks.""" + for task in self: + if task.status == 'completed': + raise UserError(_("Cannot cancel a completed task.")) + task.status = 'cancelled' + task._write_action_location() + task._post_status_message('cancelled') + if task.x_fc_sync_source: + try: + self.env['fusion.task.sync.config']._push_shadow_status(task) + except Exception: + _logger.exception( + "Failed to push cancel for shadow %s", task.name) + else: + task._on_cancel_extra() + + def _on_cancel_extra(self): + """Hook: additional side-effects after task cancellation. + Override in fusion_claims for sale order revert and email.""" + self._send_task_cancelled_email() + + def action_reschedule(self): + """Open the reschedule form for this task. + Saves old schedule info, then opens the same task form for editing. + On save, the write() method detects the reschedule and sends emails.""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'fusion.technician.task', + 'res_id': self.id, + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'reschedule_mode': True, + 'old_date': str(self.scheduled_date) if self.scheduled_date else '', + 'old_time_start': self.time_start, + 'old_time_end': self.time_end, + }, + } + + def action_reset_to_scheduled(self): + """Reset task back to scheduled.""" + for task in self: + task.status = 'scheduled' + + # ------------------------------------------------------------------ + # CHATTER / NOTIFICATIONS + # ------------------------------------------------------------------ + + def _post_status_message(self, new_status): + """Post a status change message to the task chatter.""" + self.ensure_one() + status_labels = dict(self._fields['status'].selection) + label = status_labels.get(new_status, new_status) + icons = { + 'en_route': 'fa-road', + 'in_progress': 'fa-wrench', + 'completed': 'fa-check-circle', + 'cancelled': 'fa-times-circle', + 'rescheduled': 'fa-calendar', + } + icon = icons.get(new_status, 'fa-info-circle') + body = Markup( + f'

Task status changed to ' + f'{label} by {self.env.user.name}

' + ) + self.message_post(body=body, message_type='notification', subtype_xmlid='mail.mt_note') + + def _post_completion_to_linked_order(self): + """Hook: post completion notes to linked order chatter. + Override in fusion_claims.""" + pass + + def _notify_scheduler_on_completion(self): + """Send an Odoo notification to the person who scheduled the task. + + Shadow tasks skip this -- the push-back to the source instance + triggers the notification there where the real scheduler exists. + """ + self.ensure_one() + if self.x_fc_sync_source: + return + + recipient = None + order = self._get_linked_order() + if order and order.user_id: + recipient = order.user_id + elif self.create_uid: + recipient = self.create_uid + + if not recipient or recipient in self.all_technician_ids: + return + + task_type_label = dict(self._fields['task_type'].selection).get(self.task_type, self.task_type) + task_url = f'/web#id={self.id}&model=fusion.technician.task&view_type=form' + client_name = self.client_display_name or 'N/A' + order = self._get_linked_order() + case_ref = order.name if order else '' + addr_parts = [p for p in [ + self.address_street, + self.address_street2, + self.address_city, + self.address_state_id.name if self.address_state_id else '', + self.address_zip, + ] if p] + address_str = ', '.join(addr_parts) or 'No address' + subject = f'Task Completed: {client_name}' + if case_ref: + subject += f' ({case_ref})' + body = Markup( + f'
' + f'

' + f'{task_type_label} Completed

' + f'' + f'' + f'' + f'' + f'' + f'' + f'' + f'' + f'' + f'' + f'' + f'
Client:{client_name}
Case:{case_ref or "N/A"}
Task:{self.name}
Technician(s):{self.all_technician_names or self.technician_id.name}
Location:{address_str}
' + f'

View Task

' + f'
' + ) + self.env['mail.thread'].sudo().message_notify( + partner_ids=[recipient.partner_id.id], + body=body, + subject=subject, + ) + + # ------------------------------------------------------------------ + # TASK EMAIL NOTIFICATIONS + # ------------------------------------------------------------------ + + def _get_task_email_details(self): + """Build common detail rows for task emails.""" + self.ensure_one() + type_label = dict(self._fields['task_type'].selection).get( + self.task_type, self.task_type or '') + rows = [ + ('Task', f'{self.name} ({type_label})'), + ('Client', self.partner_id.name or 'N/A'), + ] + if self.scheduled_date: + date_str = self.scheduled_date.strftime('%B %d, %Y') + start_str = self._float_to_time_str(self.time_start) + end_str = self._float_to_time_str(self.time_end) + rows.append(('Scheduled', f'{date_str}, {start_str} - {end_str}')) + if self.technician_id: + rows.append(('Technician', self.all_technician_names or self.technician_id.name)) + if self.address_display: + rows.append(('Address', self.address_display)) + return rows + + def _get_task_email_recipients(self): + """Get email recipients for task notifications. + Returns dict with 'to' (client), 'cc' (technician, sales rep, office).""" + self.ensure_one() + to_emails = [] + cc_emails = [] + + # Client email + if self.partner_id and self.partner_id.email: + to_emails.append(self.partner_id.email) + + # Technician emails (lead + additional) + for tech in (self.technician_id | self.additional_technician_ids): + if tech.email: + cc_emails.append(tech.email) + + return {'to': to_emails, 'cc': list(set(cc_emails))} + + def _send_task_cancelled_email(self): + """Send cancellation email. Base: no-op. Override in fusion_claims.""" + return False + + def _send_task_scheduled_email(self): + """Send scheduled email. Base: no-op. Override in fusion_claims.""" + return False + + def _send_task_rescheduled_email(self, old_date=None, old_start=None, old_end=None): + """Send rescheduled email. Base: no-op. Override in fusion_claims.""" + return False + + # ------------------------------------------------------------------ + # CLIENT UPDATE EMAILS (en-route + completion) + # ------------------------------------------------------------------ + + def _get_email_builder(self): + """Return a record that has the _email_build mixin. + + Base: returns self (task model inherits mixin). + Override in fusion_claims to prefer linked sale order. + """ + return self + + def _is_email_notifications_enabled(self): + """Check if email notifications are enabled. + + Base: always True. Override in fusion_claims to check + linked sale order's notification settings. + """ + return True + + def _get_linked_order(self): + """Return the linked order record (SO or PO), or False. + + Base: always False. Override in fusion_claims to return + sale_order_id or purchase_order_id. + """ + return False + + def _send_task_en_route_email(self): + """Email the client that the technician is on the way.""" + self.ensure_one() + if self.x_fc_sync_source or not self.x_fc_send_client_updates: + return False + if not self.partner_id or not self.partner_id.email: + return False + if not self._is_email_notifications_enabled(): + return False + + client_name = self.client_display_name or self.partner_id.name or 'Client' + tech_name = self.all_technician_names or (self.technician_id.name if self.technician_id else 'Our technician') + type_label = dict(self._fields['task_type'].selection).get( + self.task_type, self.task_type or 'service') + company = self.env.company + + detail_rows = self._get_task_email_details() + builder = self._get_email_builder() + + time_range = '' + if self.scheduled_date and self.time_start is not None: + time_range = ( + f'{self.scheduled_date.strftime("%B %d, %Y")}, ' + f'{self._float_to_time_str(self.time_start)} - ' + f'{self._float_to_time_str(self.time_end or self.time_start + 1.0)}' + ) + + preparation_note = ( + 'Please prepare for the visit:
' + '
    ' + '
  • Ensure the area where service is needed is accessible and clear.
  • ' + '
  • If applicable, secure pets away from the work area.
  • ' + '
  • Have any relevant documents or information ready for the technician.
  • ' + '
  • An adult (18+) must be present during the visit.
  • ' + '
' + 'Important: Our technicians will not ask for credit card ' + 'details or request payment directly unless payment on arrival has been ' + 'previously agreed upon. If a cash payment was arranged, please have ' + 'the payment ready in an envelope.' + ) + + body_html = builder._email_build( + title='Your Technician Is On The Way', + summary=( + f'Our technician {tech_name} is heading to your ' + f'location and is expected to arrive between ' + f'{time_range or "the scheduled time"}.' + ), + email_type='info', + sections=[('Visit Details', detail_rows)], + note=preparation_note, + note_color='#2B6CB0', + sender_name=f'The {company.name} Team', + ) + + recipients = self._get_task_email_recipients() + to_emails = recipients.get('to', []) + cc_emails = recipients.get('cc', []) + if not to_emails: + return False + + email_to = ', '.join(to_emails) + email_cc = ', '.join(cc_emails) + order = self._get_linked_order() + case_ref = order.name if order else self.name + + try: + mail_vals = { + 'subject': f'Your Technician Is On The Way - {client_name} - {case_ref}', + 'body_html': body_html, + 'email_to': email_to, + 'email_cc': email_cc, + } + if order: + mail_vals['model'] = order._name + mail_vals['res_id'] = order.id + self.env['mail.mail'].sudo().create(mail_vals).send() + _logger.info("Sent en-route email for task %s to %s", self.name, email_to) + return True + except Exception as e: + _logger.error("Failed to send en-route email for %s: %s", self.name, e) + return False + + def _send_task_completion_email(self): + """Email the client that the visit is complete. + + Sends one of two variants depending on x_fc_ask_google_review: + - With Google review request (default) + - Standard thank-you without review request + """ + self.ensure_one() + if self.x_fc_sync_source or not self.x_fc_send_client_updates: + return False + if not self.partner_id or not self.partner_id.email: + return False + if not self._is_email_notifications_enabled(): + return False + + client_name = self.client_display_name or self.partner_id.name or 'Client' + tech_name = self.all_technician_names or (self.technician_id.name if self.technician_id else 'Our technician') + type_label = dict(self._fields['task_type'].selection).get( + self.task_type, self.task_type or 'service') + company = self.env.company + builder = self._get_email_builder() + + summary_rows = [ + ('Client', client_name), + ('Service', type_label.title()), + ] + if self.scheduled_date: + summary_rows.append(('Date', self.scheduled_date.strftime('%B %d, %Y'))) + if self.technician_id: + summary_rows.append(('Technician', tech_name)) + + contact_note = ( + 'If you have any questions or concerns about the service provided, ' + f'please don\'t hesitate to contact us at ' + f'{company.phone or company.email or "our office"}.' + ) + + google_url = company.x_fc_google_review_url or '' + include_review = self.x_fc_ask_google_review and google_url + + extra_html = '' + button_url = '' + button_text = '' + + if include_review: + extra_html = builder._email_note( + 'We Value Your Feedback

' + 'We hope you had a great experience! Your feedback helps us ' + 'improve and serve you better. We would truly appreciate it ' + 'if you could take a moment to share your experience.', + '#38a169', + ) + button_url = google_url + button_text = 'Leave a Review' + else: + company_website = company.website or '' + if company_website: + button_url = company_website + button_text = 'Visit Our Website' + + body_html = builder._email_build( + title='Service Visit Completed', + summary=( + f'Our technician {tech_name} has completed the ' + f'{type_label.lower()} at your location. ' + f'Thank you for choosing {company.name}.' + ), + email_type='success', + sections=[('Visit Summary', summary_rows)], + extra_html=extra_html, + note=contact_note, + note_color='#38a169', + button_url=button_url, + button_text=button_text, + sender_name=f'The {company.name} Team', + ) + + recipients = self._get_task_email_recipients() + to_emails = recipients.get('to', []) + cc_emails = recipients.get('cc', []) + if not to_emails: + return False + + email_to = ', '.join(to_emails) + email_cc = ', '.join(cc_emails) + order = self._get_linked_order() + case_ref = order.name if order else self.name + + try: + mail_vals = { + 'subject': f'Service Visit Completed - {client_name} - {case_ref}', + 'body_html': body_html, + 'email_to': email_to, + 'email_cc': email_cc, + } + if order: + mail_vals['model'] = order._name + mail_vals['res_id'] = order.id + self.env['mail.mail'].sudo().create(mail_vals).send() + _logger.info("Sent completion email for task %s to %s", self.name, email_to) + return True + except Exception as e: + _logger.error("Failed to send completion email for %s: %s", self.name, e) + return False + + def get_next_task_for_technician(self): + """Get the next task in sequence for the same technician+date after this one. + + Considers tasks where the technician is either lead or additional. + """ + self.ensure_one() + return self.sudo().search([ + '|', + ('technician_id', '=', self.technician_id.id), + ('additional_technician_ids', 'in', [self.technician_id.id]), + ('scheduled_date', '=', self.scheduled_date), + ('time_start', '>=', self.time_start), + ('status', 'in', ['scheduled', 'en_route']), + ('id', '!=', self.id), + ], order='time_start, sequence, id', limit=1) + + # ------------------------------------------------------------------ + # GOOGLE MAPS INTEGRATION + # ------------------------------------------------------------------ + + def _get_google_maps_api_key(self): + """Get the Google Maps API key from config.""" + return self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.google_maps_api_key', '' + ) + + @api.model + def get_map_data(self, domain=None): + """Return task data, technician locations, and Google Maps API key. + + Args: + domain: optional extra domain from the search bar filters. + """ + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.google_maps_api_key', '') + local_instance = self.env['ir.config_parameter'].sudo().get_param( + 'fusion_claims.sync_instance_id', '') + base_domain = [ + ('status', 'not in', ['cancelled']), + ] + if domain: + base_domain = expression.AND([base_domain, domain]) + tasks = self.sudo().search_read( + base_domain, + ['name', 'partner_id', 'technician_id', 'task_type', + 'address_lat', 'address_lng', 'address_display', + 'time_start', 'time_end', 'time_start_display', 'time_end_display', + 'status', 'scheduled_date', 'travel_time_minutes', + 'x_fc_sync_client_name', 'x_fc_is_shadow', 'x_fc_sync_source'], + order='scheduled_date asc NULLS LAST, time_start asc', + limit=500, + ) + locations = self.env['fusion.technician.location'].get_latest_locations() + tech_starts = self._get_tech_start_locations(tasks, api_key) + return { + 'api_key': api_key, + 'tasks': tasks, + 'locations': locations, + 'local_instance_id': local_instance, + 'tech_start_locations': tech_starts, + } + + @api.model + def _get_tech_start_locations(self, tasks, api_key): + """Build a dict of technician start locations for route origins. + + Priority per technician: + 1. Today's fusion_clock check-in location (if module installed) + 2. Personal start address (x_fc_start_address with cached lat/lng) + 3. Company default HQ address + """ + tech_ids = { + t['technician_id'][0] + for t in tasks + if t.get('technician_id') + } + if not tech_ids: + return {} + + result = {} + today = self._local_now().date() + + clock_locations = self._get_clock_in_locations(tech_ids, today) + + hq_address = ( + self.env['ir.config_parameter'].sudo() + .get_param('fusion_claims.technician_start_address', '') or '' + ).strip() + hq_lat, hq_lng = 0.0, 0.0 + + for uid in tech_ids: + if uid in clock_locations: + result[uid] = clock_locations[uid] + continue + + user = self.env['res.users'].sudo().browse(uid) + if not user.exists(): + continue + partner = user.partner_id + + if partner.x_fc_start_address and partner.x_fc_start_address.strip(): + lat = partner.x_fc_start_address_lat + lng = partner.x_fc_start_address_lng + if not lat or not lng: + lat, lng = self._geocode_address_string( + partner.x_fc_start_address, api_key) + if lat and lng: + partner.sudo().write({ + 'x_fc_start_address_lat': lat, + 'x_fc_start_address_lng': lng, + }) + if lat and lng: + result[uid] = { + 'lat': lat, 'lng': lng, + 'address': partner.x_fc_start_address.strip(), + 'source': 'start_address', + } + continue + + if hq_address: + if not hq_lat and not hq_lng: + hq_lat, hq_lng = self._geocode_address_string( + hq_address, api_key) + if hq_lat and hq_lng: + result[uid] = { + 'lat': hq_lat, 'lng': hq_lng, + 'address': hq_address, + 'source': 'company_hq', + } + + return result + + @api.model + def _get_clock_in_locations(self, tech_ids, today): + """Get today's clock-in lat/lng from fusion_clock if installed. + + Uses the technician's actual GPS position at the moment they clocked + in (from the activity log), not the geofenced location's fixed + coordinates. Falls back to the geofence center if no activity-log + GPS is available. + """ + result = {} + try: + module = self.env['ir.module.module'].sudo().search([ + ('name', '=', 'fusion_clock'), + ('state', '=', 'installed'), + ], limit=1) + if not module: + return result + except Exception: + return result + + try: + Attendance = self.env['hr.attendance'].sudo() + Employee = self.env['hr.employee'].sudo() + ActivityLog = self.env['fusion.clock.activity.log'].sudo() + except KeyError: + return result + + employees = Employee.search([ + ('user_id', 'in', list(tech_ids)), + ]) + emp_to_user = {e.id: e.user_id.id for e in employees} + + if not employees: + return result + + today_start = dt_datetime.combine(today, dt_datetime.min.time()) + today_end = today_start + timedelta(days=1) + + attendances = Attendance.search([ + ('employee_id', 'in', employees.ids), + ('check_in', '>=', today_start), + ('check_in', '<', today_end), + ], order='check_in asc') + + for att in attendances: + uid = emp_to_user.get(att.employee_id.id) + if not uid or uid in result: + continue + + lat, lng, address = 0, 0, '' + + log = ActivityLog.search([ + ('attendance_id', '=', att.id), + ('log_type', '=', 'clock_in'), + ('latitude', '!=', 0), + ('longitude', '!=', 0), + ], limit=1) + if log: + lat, lng = log.latitude, log.longitude + loc = att.x_fclk_location_id if hasattr(att, 'x_fclk_location_id') else False + address = (loc.address or loc.name) if loc else '' + + if not lat or not lng: + loc = att.x_fclk_location_id if hasattr(att, 'x_fclk_location_id') else False + if loc and loc.latitude and loc.longitude: + lat, lng = loc.latitude, loc.longitude + address = loc.address or loc.name or '' + + if lat and lng: + result[uid] = { + 'lat': lat, + 'lng': lng, + 'address': address, + 'source': 'clock_in', + } + + return result + + def _geocode_address(self): + """Geocode the task address using Google Geocoding API.""" + self.ensure_one() + api_key = self._get_google_maps_api_key() + if not api_key or not self.address_display: + return False + + try: + url = 'https://maps.googleapis.com/maps/api/geocode/json' + params = { + 'address': self.address_display, + 'key': api_key, + 'region': 'ca', + } + resp = requests.get(url, params=params, timeout=10) + data = resp.json() + if data.get('status') == 'OK' and data.get('results'): + location = data['results'][0]['geometry']['location'] + self.write({ + 'address_lat': location['lat'], + 'address_lng': location['lng'], + }) + return True + except Exception as e: + _logger.warning(f"Geocoding failed for task {self.name}: {e}") + return False + + def _calculate_travel_time(self, origin_lat, origin_lng): + """Calculate travel time from origin to this task using Distance Matrix API.""" + self.ensure_one() + api_key = self._get_google_maps_api_key() + if not api_key: + return False + if not (origin_lat and origin_lng and self.address_lat and self.address_lng): + return False + + try: + url = 'https://maps.googleapis.com/maps/api/distancematrix/json' + params = { + 'origins': f'{origin_lat},{origin_lng}', + 'destinations': f'{self.address_lat},{self.address_lng}', + 'key': api_key, + 'mode': 'driving', + 'avoid': 'tolls', + 'traffic_model': 'best_guess', + 'departure_time': 'now', + } + resp = requests.get(url, params=params, timeout=10) + data = resp.json() + if data.get('status') == 'OK': + element = data['rows'][0]['elements'][0] + if element.get('status') == 'OK': + duration_seconds = element['duration_in_traffic']['value'] if 'duration_in_traffic' in element else element['duration']['value'] + distance_meters = element['distance']['value'] + self.write({ + 'travel_time_minutes': round(duration_seconds / 60), + 'travel_distance_km': round(distance_meters / 1000, 1), + }) + return True + except Exception as e: + _logger.warning(f"Travel time calculation failed for task {self.name}: {e}") + return False + + def action_calculate_travel_times(self): + """Calculate travel times for a day's schedule. Called from backend button or cron.""" + self._do_calculate_travel_times() + # Return False to stay on the current form without navigation + return False + + def _do_calculate_travel_times(self): + """Internal: calculate travel times for tasks. Does not return an action. + + For today's tasks: uses the tech's current GPS location as origin + for the first non-completed task, so ETAs reflect reality. + For future tasks: uses personal start address or company HQ. + """ + # Group tasks by technician and date + task_groups = {} + for task in self: + key = (task.technician_id.id, task.scheduled_date) + if key not in task_groups: + task_groups[key] = self.env['fusion.technician.task'] + task_groups[key] |= task + + api_key = self._get_google_maps_api_key() + today = self._local_now().date() + + for (tech_id, date), tasks in task_groups.items(): + sorted_tasks = tasks.sorted(lambda t: (t.sequence, t.time_start)) + + # For today: try current GPS, then clock-in, then start address + # For future: use start address + if date == today: + lat, lng = self._get_tech_current_location(tech_id) + if lat and lng: + prev_lat, prev_lng = lat, lng + origin_label = 'Current Location' + else: + clock_locs = self._get_clock_in_locations({tech_id}, today) + if tech_id in clock_locs: + cl = clock_locs[tech_id] + prev_lat, prev_lng = cl['lat'], cl['lng'] + origin_label = 'Clock-In Location' + else: + addr = self._get_technician_start_address(tech_id) + prev_lat, prev_lng = self._geocode_address_string(addr, api_key) + origin_label = 'Start Location' + else: + addr = self._get_technician_start_address(tech_id) + prev_lat, prev_lng = self._geocode_address_string(addr, api_key) + origin_label = 'Start Location' + + # Skip already-completed tasks for today (chain starts from + # last completed task's location instead) + first_pending_idx = 0 + if date == today: + for idx, task in enumerate(sorted_tasks): + if task.status == 'completed': + if task.completed_latitude and task.completed_longitude: + prev_lat = task.completed_latitude + prev_lng = task.completed_longitude + elif task.address_lat and task.address_lng: + prev_lat = task.address_lat + prev_lng = task.address_lng + origin_label = f'Completed: {task.name}' + first_pending_idx = idx + 1 + else: + break + + for i, task in enumerate(sorted_tasks): + if i < first_pending_idx: + continue + # Geocode task if needed + if not (task.address_lat and task.address_lng): + task._geocode_address() + + if prev_lat and prev_lng and task.address_lat and task.address_lng: + task._calculate_travel_time(prev_lat, prev_lng) + task.previous_task_id = sorted_tasks[i - 1].id if i > 0 else False + task.travel_origin = origin_label if i == first_pending_idx else f'Task {sorted_tasks[i - 1].name}' + + prev_lat = task.address_lat or prev_lat + prev_lng = task.address_lng or prev_lng + + @api.model + def _cron_calculate_travel_times(self): + """Cron job: Calculate travel times for today and tomorrow. + + Runs every 15 minutes. For today's tasks, uses the tech's latest + GPS location so ETAs stay accurate as technicians move. + Includes completed tasks in the search so the chain can skip + them and use their completion location as origin. + """ + today = fields.Date.context_today(self) + tomorrow = today + timedelta(days=1) + tasks = self.search([ + ('scheduled_date', 'in', [today, tomorrow]), + ('status', 'not in', ['cancelled']), + ]) + if tasks: + tasks._do_calculate_travel_times() + _logger.info(f"Calculated travel times for {len(tasks)} tasks") + + @api.model + def _cron_check_late_arrivals(self): + """Cron: detect tasks where the technician hasn't started and the + scheduled start time has passed. Sends a push notification to the + tech and an in-app notification to the office (once per task). + """ + ICP = self.env['ir.config_parameter'].sudo() + push_enabled = ICP.get_param('fusion_claims.push_enabled', 'False') + if push_enabled.lower() not in ('true', '1', 'yes'): + return + + local_now = self._local_now() + today = local_now.date() + current_hour = local_now.hour + local_now.minute / 60.0 + + late_tasks = self.sudo().search([ + ('scheduled_date', '=', today), + ('status', '=', 'scheduled'), + ('time_start', '<', current_hour), + ('x_fc_late_notified', '=', False), + ('x_fc_sync_source', '=', False), + ('technician_id', '!=', False), + ]) + + for task in late_tasks: + minutes_late = int((current_hour - task.time_start) * 60) + if minutes_late < 5: + continue + + client = task.client_display_name or 'Client' + try: + task._send_push_notification( + f'Running Late - {client}', + f'Your {task._float_to_time_str(task.time_start)} ' + f'{dict(self._fields["task_type"].selection).get(task.task_type, "task")} ' + f'is {minutes_late} min overdue. Please update your status.', + ) + except Exception: + pass + + try: + order = task._get_linked_order() + if order and order.user_id: + recipient = order.user_id + elif task.create_uid: + recipient = task.create_uid + else: + recipient = None + if recipient: + self.env['mail.thread'].sudo().message_notify( + partner_ids=[recipient.partner_id.id], + subject=f'Late: {task.name} - {client}', + body=Markup( + f'

' + f'{task.technician_id.name} has not started ' + f'{task.name} for {client}, ' + f'scheduled at {task._float_to_time_str(task.time_start)}. ' + f'Currently {minutes_late} min overdue.

' + ), + ) + except Exception: + _logger.warning("Failed to notify office about late task %s", task.name) + + task.with_context(skip_travel_recalc=True).write({ + 'x_fc_late_notified': True, + }) + + if late_tasks: + _logger.info("Late arrival notifications sent for %d tasks", len(late_tasks)) + + # ------------------------------------------------------------------ + # PORTAL HELPERS + # ------------------------------------------------------------------ + + def get_technician_tasks_for_date(self, user_id, date): + """Get all tasks for a technician on a given date, ordered by sequence.""" + return self.sudo().search([ + ('technician_id', '=', user_id), + ('scheduled_date', '=', date), + ('status', '!=', 'cancelled'), + ], order='sequence, time_start, id') + + def get_next_task(self, user_id): + """Get the next upcoming task for a technician.""" + today = fields.Date.context_today(self) + return self.sudo().search([ + ('technician_id', '=', user_id), + ('scheduled_date', '>=', today), + ('status', 'in', ['scheduled', 'en_route']), + ], order='scheduled_date, sequence, time_start', limit=1) + + def get_current_task(self, user_id): + """Get the current in-progress task for a technician.""" + today = fields.Date.context_today(self) + return self.sudo().search([ + ('technician_id', '=', user_id), + ('scheduled_date', '=', today), + ('status', '=', 'in_progress'), + ], limit=1) + + # ------------------------------------------------------------------ + # PUSH NOTIFICATIONS + # ------------------------------------------------------------------ + + def _send_push_notification(self, title, body_text, url=None): + """Send a web push notification for this task.""" + self.ensure_one() + PushSub = self.env['fusion.push.subscription'].sudo() + subscriptions = PushSub.search([ + ('user_id', '=', self.technician_id.id), + ('active', '=', True), + ]) + if not subscriptions: + return + + ICP = self.env['ir.config_parameter'].sudo() + vapid_private = ICP.get_param('fusion_claims.vapid_private_key', '') + vapid_public = ICP.get_param('fusion_claims.vapid_public_key', '') + if not vapid_private or not vapid_public: + _logger.warning("VAPID keys not configured, cannot send push notification") + return + + try: + from pywebpush import webpush, WebPushException + except ImportError: + _logger.warning("pywebpush not installed, cannot send push notifications") + return + + payload = json.dumps({ + 'title': title, + 'body': body_text, + 'url': url or f'/my/technician/task/{self.id}', + 'task_id': self.id, + 'task_type': self.task_type, + }) + + for sub in subscriptions: + try: + webpush( + subscription_info={ + 'endpoint': sub.endpoint, + 'keys': { + 'p256dh': sub.p256dh_key, + 'auth': sub.auth_key, + }, + }, + data=payload, + vapid_private_key=vapid_private, + vapid_claims={'sub': 'mailto:support@nexasystems.ca'}, + ) + except Exception as e: + _logger.warning(f"Push notification failed for subscription {sub.id}: {e}") + # Deactivate invalid subscriptions + if 'gone' in str(e).lower() or '410' in str(e): + sub.active = False + + self.write({ + 'push_notified': True, + 'push_notified_datetime': fields.Datetime.now(), + }) + + @api.model + def _cron_send_push_notifications(self): + """Cron: Send push notifications for upcoming tasks.""" + ICP = self.env['ir.config_parameter'].sudo() + if not ICP.get_param('fusion_claims.push_enabled', False): + return + + advance_minutes = int(ICP.get_param('fusion_claims.push_advance_minutes', '30')) + local_now = self._local_now() + + tasks = self.search([ + ('scheduled_date', '=', local_now.date()), + ('status', '=', 'scheduled'), + ('push_notified', '=', False), + ]) + + for task in tasks: + task_start_hour = int(task.time_start) + task_start_min = int((task.time_start % 1) * 60) + task_start_dt = local_now.replace( + hour=task_start_hour, minute=task_start_min, second=0, microsecond=0) + + minutes_until = (task_start_dt - local_now).total_seconds() / 60 + if 0 <= minutes_until <= advance_minutes: + task_type_label = dict(self._fields['task_type'].selection).get(task.task_type, task.task_type) + title = f'Upcoming: {task_type_label}' + body_text = f'{task.partner_id.name or "Task"} - {task.time_start_display}' + if task.travel_time_minutes: + body_text += f' ({task.travel_time_minutes} min drive)' + task._send_push_notification(title, body_text) + + # ------------------------------------------------------------------ + # HELPERS + # ------------------------------------------------------------------ + + def _get_local_tz(self): + """Return the pytz timezone for local time calculations. + Priority: company resource calendar > user tz > UTC.""" + import pytz + tz_name = ( + self.env.company.resource_calendar_id.tz + or self.env.user.tz + or 'UTC' + ) + try: + return pytz.timezone(tz_name) + except pytz.UnknownTimeZoneError: + return pytz.timezone('UTC') + + def _utc_to_local(self, dt_utc): + """Convert a naive UTC datetime to a timezone-aware local datetime.""" + import pytz + if not dt_utc: + return None + return pytz.utc.localize(dt_utc).astimezone(self._get_local_tz()) + + def _local_now(self): + """Current datetime in the local (company) timezone.""" + return self._utc_to_local(fields.Datetime.now()) + + @staticmethod + def _float_to_time_str(value): + """Convert float hours to time string like '9:30 AM'.""" + if not value and value != 0: + return '' + hours = int(value) + minutes = int(round((value % 1) * 60)) + period = 'AM' if hours < 12 else 'PM' + display_hour = hours % 12 or 12 + return f'{display_hour}:{minutes:02d} {period}' + + def get_google_maps_url(self): + """Get Google Maps navigation URL using the text address so the + destination shows a proper street name instead of raw coordinates. + Returns a google.com/maps URL that Android auto-opens in the app; + iOS handling is done client-side via JS to launch comgooglemaps://.""" + self.ensure_one() + if self.address_display: + addr = urllib.parse.quote(self.address_display) + return f'https://www.google.com/maps/dir/?api=1&destination={addr}&travelmode=driving' + if self.address_lat and self.address_lng: + return f'https://www.google.com/maps/dir/?api=1&destination={self.address_lat},{self.address_lng}&travelmode=driving' + return '' diff --git a/Entech Plating/fusion_tasks/security/ir.model.access.csv b/Entech Plating/fusion_tasks/security/ir.model.access.csv new file mode 100644 index 00000000..dad063b7 --- /dev/null +++ b/Entech Plating/fusion_tasks/security/ir.model.access.csv @@ -0,0 +1,12 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fusion_technician_task_user,fusion.technician.task.user,model_fusion_technician_task,sales_team.group_sale_salesman,1,1,1,0 +access_fusion_technician_task_manager,fusion.technician.task.manager,model_fusion_technician_task,sales_team.group_sale_manager,1,1,1,1 +access_fusion_technician_task_technician,fusion.technician.task.technician,model_fusion_technician_task,fusion_tasks.group_field_technician,1,1,0,0 +access_fusion_technician_task_portal,fusion.technician.task.portal,model_fusion_technician_task,base.group_portal,1,0,0,0 +access_fusion_push_subscription_user,fusion.push.subscription.user,model_fusion_push_subscription,base.group_user,1,1,1,0 +access_fusion_push_subscription_portal,fusion.push.subscription.portal,model_fusion_push_subscription,base.group_portal,1,1,1,0 +access_fusion_technician_location_manager,fusion.technician.location.manager,model_fusion_technician_location,sales_team.group_sale_manager,1,1,1,1 +access_fusion_technician_location_user,fusion.technician.location.user,model_fusion_technician_location,sales_team.group_sale_salesman,1,0,0,0 +access_fusion_technician_location_portal,fusion.technician.location.portal,model_fusion_technician_location,base.group_portal,0,0,1,0 +access_fusion_task_sync_config_manager,fusion.task.sync.config.manager,model_fusion_task_sync_config,sales_team.group_sale_manager,1,1,1,1 +access_fusion_task_sync_config_user,fusion.task.sync.config.user,model_fusion_task_sync_config,sales_team.group_sale_salesman,1,0,0,0 diff --git a/Entech Plating/fusion_tasks/security/security.xml b/Entech Plating/fusion_tasks/security/security.xml new file mode 100644 index 00000000..7edf97aa --- /dev/null +++ b/Entech Plating/fusion_tasks/security/security.xml @@ -0,0 +1,103 @@ + + + + + + + Fusion Tasks + 46 + + + + + + + Fusion Tasks + 46 + + + + + + + + + + + Field Technician + + + + + + + + + + Technician Task: Manager Full Access + + [(1, '=', 1)] + + + + + + + + + + Technician Task: Sales User Access + + [(1, '=', 1)] + + + + + + + + + + Technician Task: Technician Own Tasks + + [('technician_id', '=', user.id)] + + + + + + + + + + Technician Task: Portal Technician Access + + [('technician_id', '=', user.id)] + + + + + + + + + + + + + + Push Subscription: Own Only + + [('user_id', '=', user.id)] + + + + + + Push Subscription: Portal Own Only + + [('user_id', '=', user.id)] + + + + diff --git a/Entech Plating/fusion_tasks/static/description/icon.png b/Entech Plating/fusion_tasks/static/description/icon.png new file mode 100644 index 00000000..5738185d Binary files /dev/null and b/Entech Plating/fusion_tasks/static/description/icon.png differ diff --git a/Entech Plating/fusion_tasks/static/src/css/fusion_task_map_view.scss b/Entech Plating/fusion_tasks/static/src/css/fusion_task_map_view.scss new file mode 100644 index 00000000..f3b9b097 --- /dev/null +++ b/Entech Plating/fusion_tasks/static/src/css/fusion_task_map_view.scss @@ -0,0 +1,488 @@ +// ===================================================================== +// Fusion Task Map View - Sidebar + Google Maps +// Theme-aware: uses Odoo/Bootstrap variables for dark mode support +// ===================================================================== + +$sidebar-width: 340px; +$transition-speed: .25s; + +.o_fusion_task_map_view { + height: 100%; + + .o_content { + height: 100%; + display: flex; + flex-direction: column; + } +} + +// ── Main wrapper: sidebar + map side by side ──────────────────────── +.fc_map_wrapper { + display: flex; + flex-direction: row; + height: 100%; + min-height: 0; + overflow: hidden; + position: relative; +} + +// ── Sidebar ───────────────────────────────────────────────────────── +.fc_sidebar { + width: $sidebar-width; + min-width: $sidebar-width; + max-width: $sidebar-width; + background: var(--o-view-background-color, $o-view-background-color); + border-right: 1px solid $border-color; + display: flex; + flex-direction: column; + transition: width $transition-speed ease, min-width $transition-speed ease, + max-width $transition-speed ease, opacity $transition-speed ease; + overflow: hidden; + + &--collapsed { + width: 0; + min-width: 0; + max-width: 0; + opacity: 0; + border-right: none; + } +} + +.fc_sidebar_header { + padding: 14px 16px 12px; + border-bottom: 1px solid $border-color; + flex-shrink: 0; + + h6 { + font-size: 14px; + color: $headings-color; + } +} + +.fc_sidebar_body { + flex: 1 1 auto; + overflow-y: auto; + overflow-x: hidden; + padding: 6px 0; + + &::-webkit-scrollbar { width: 5px; } + &::-webkit-scrollbar-track { background: transparent; } + &::-webkit-scrollbar-thumb { background: $border-color; border-radius: 4px; } +} + +.fc_sidebar_footer { + padding: 10px 16px; + border-top: 1px solid $border-color; + flex-shrink: 0; +} + +.fc_sidebar_empty { + text-align: center; + padding: 40px 20px; + color: $text-muted; +} + +// ── Day filter chips ──────────────────────────────────────────────── +.fc_day_filters { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.fc_day_chip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 10px; + font-size: 11px; + font-weight: 600; + border: 1px solid $border-color; + border-radius: 12px; + background: transparent; + color: $text-muted; + cursor: pointer; + transition: all .15s; + line-height: 18px; + + &:hover { + border-color: rgba($primary, .3); + color: $body-color; + } + + &--active { + color: #fff !important; + border-color: transparent !important; + } + + &--all { + color: $body-color; + font-weight: 500; + &:hover { background: rgba($primary, .1); } + } +} + +.fc_day_chip_count { + font-size: 10px; + opacity: .8; +} + +.fc_group_hidden_tag { + font-size: 9px; + text-transform: uppercase; + letter-spacing: .5px; + color: $text-muted; + background: rgba($secondary, .1); + padding: 0 5px; + border-radius: 3px; + margin-left: 4px; + font-weight: 500; +} + +// ── Technician filter chips ───────────────────────────────────────── +.fc_tech_filters { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.fc_tech_chip { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 10px 3px 4px; + font-size: 11px; + font-weight: 600; + border: 1px solid $border-color; + border-radius: 14px; + background: transparent; + color: $text-muted; + cursor: pointer; + transition: all .15s; + line-height: 18px; + max-width: 100%; + overflow: hidden; + + &:hover { + border-color: rgba($primary, .35); + color: $body-color; + background: rgba($primary, .06); + } + + &--active { + background: $primary !important; + color: #fff !important; + border-color: $primary !important; + + .fc_tech_chip_avatar { + background: rgba(#fff, .25); + color: #fff; + } + } + + &--all { + padding: 3px 10px; + color: $body-color; + font-weight: 500; + &:hover { background: rgba($primary, .1); } + } +} + +.fc_tech_chip_avatar { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 50%; + background: rgba($secondary, .15); + color: $body-color; + font-size: 9px; + font-weight: 700; + flex-shrink: 0; +} + +.fc_tech_chip_name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +// Collapsed toggle button (floating) +.fc_sidebar_toggle_btn { + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + z-index: 15; + background: var(--o-view-background-color, $o-view-background-color); + border: 1px solid $border-color; + border-left: none; + border-radius: 0 8px 8px 0; + padding: 12px 6px; + cursor: pointer; + box-shadow: 2px 0 6px rgba(0,0,0,.08); + color: $text-muted; + transition: background .15s; + + &:hover { + background: $o-gray-100; + color: $body-color; + } +} + +// ── Group headers ─────────────────────────────────────────────────── +.fc_group_header { + display: flex; + align-items: center; + padding: 8px 16px; + cursor: pointer; + user-select: none; + font-weight: 600; + font-size: 12px; + color: $text-muted; + text-transform: uppercase; + letter-spacing: .5px; + background: rgba($secondary, .08); + border-bottom: 1px solid $border-color; + transition: background .15s; + + &:hover { + background: rgba($secondary, .15); + } + + .fa-caret-right, + .fa-caret-down { + width: 14px; + text-align: center; + font-size: 13px; + } +} + +.fc_group_label { + flex: 1; +} + +.fc_group_badge { + background: rgba($secondary, .2); + color: $body-color; + font-size: 10px; + font-weight: 700; + padding: 1px 7px; + border-radius: 10px; + min-width: 20px; + text-align: center; +} + +// ── Task cards ────────────────────────────────────────────────────── +.fc_group_tasks { + padding: 4px 0; +} + +.fc_task_card { + margin: 3px 10px; + padding: 10px 12px; + background: var(--o-view-background-color, $o-view-background-color); + border: 1px solid $border-color; + border-radius: 8px; + cursor: pointer; + transition: all .15s; + position: relative; + + &:hover { + background: rgba($primary, .05); + border-color: rgba($primary, .2); + box-shadow: 0 1px 4px rgba(0,0,0,.06); + } + + &--active { + background: rgba($primary, .1) !important; + border-color: rgba($primary, .35) !important; + box-shadow: 0 0 0 2px rgba($primary, .15); + } +} + +.fc_task_card_top { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; +} + +.fc_task_num { + display: inline-block; + color: #fff; + font-size: 11px; + font-weight: 700; + padding: 1px 8px; + border-radius: 4px; + line-height: 18px; +} + +.fc_task_status { + font-size: 11px; + font-weight: 600; +} + +.fc_task_client { + font-size: 13px; + font-weight: 600; + color: $headings-color; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.fc_task_meta { + display: flex; + gap: 12px; + font-size: 11px; + color: $body-color; + margin-bottom: 3px; + + .fa { opacity: .5; } +} + +.fc_task_date { + font-size: 11px; + color: #6366f1; + font-weight: 600; + margin-bottom: 3px; + .fa { opacity: .5; } +} + +.fc_task_detail { + font-size: 11px; + color: $body-color; + margin-bottom: 2px; + .fa { opacity: .5; } +} + +.fc_task_address { + font-size: 10px; + color: $text-muted; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 2px; +} + +.fc_task_bottom_row { + display: flex; + align-items: center; + gap: 6px; + margin-top: 4px; + flex-wrap: wrap; +} + +.fc_task_travel { + display: inline-flex; + align-items: center; + font-size: 10px; + color: $body-color; + background: rgba($secondary, .1); + padding: 1px 8px; + border-radius: 4px; + .fa { opacity: .5; } +} + +.fc_task_source { + display: inline-flex; + align-items: center; + font-size: 10px; + color: #fff; + font-weight: 600; + padding: 1px 8px; + border-radius: 4px; + .fa { opacity: .8; } +} + +.fc_task_edit_btn { + display: inline-flex; + align-items: center; + font-size: 10px; + font-weight: 600; + color: var(--btn-primary-color, #fff); + background: var(--btn-primary-bg, #{$primary}); + padding: 2px 10px; + border-radius: 4px; + cursor: pointer; + margin-left: auto; + transition: all .15s; + + &:hover { + opacity: .85; + filter: brightness(1.15); + } +} + +// ── Map area ──────────────────────────────────────────────────────── +.fc_map_area { + flex: 1 1 auto; + display: flex; + flex-direction: column; + min-width: 0; + position: relative; +} + +.fc_map_legend_bar { + flex: 0 0 auto; + font-size: 12px; + min-height: 40px; +} + +.fc_map_container { + flex: 1 1 auto; + position: relative; + min-height: 400px; +} + +// ── Google Maps InfoWindow override ────────────────────────────────── +.gm-style-iw-d { + overflow: auto !important; +} +.gm-style .gm-style-iw-c { + padding: 0 !important; + border-radius: 10px !important; + overflow: hidden !important; + box-shadow: 0 4px 20px rgba(0,0,0,.15) !important; +} +.gm-style .gm-style-iw-tc { + display: none !important; +} +.gm-style .gm-ui-hover-effect { + display: none !important; +} + +// ── Responsive ────────────────────────────────────────────────────── +@media (max-width: 768px) { + .fc_map_wrapper { + flex-direction: column; + } + .fc_sidebar { + width: 100% !important; + min-width: 100% !important; + max-width: 100% !important; + max-height: 40vh; + border-right: none; + border-bottom: 1px solid $border-color; + + &--collapsed { + max-height: 0; + opacity: 0; + } + } + .fc_sidebar_toggle_btn { + top: auto; + bottom: 10px; + left: 50%; + transform: translateX(-50%); + border-radius: 8px; + border: 1px solid $border-color; + padding: 8px 16px; + } + .fc_map_area { + flex: 1; + min-height: 300px; + } +} diff --git a/Entech Plating/fusion_tasks/static/src/js/fusion_task_map_view.js b/Entech Plating/fusion_tasks/static/src/js/fusion_task_map_view.js new file mode 100644 index 00000000..38bec401 --- /dev/null +++ b/Entech Plating/fusion_tasks/static/src/js/fusion_task_map_view.js @@ -0,0 +1,1200 @@ +/** @odoo-module **/ +// Fusion Tasks - Google Maps Task View with Sidebar +// Copyright 2024-2026 Nexa Systems Inc. +// License OPL-1 + +import { registry } from "@web/core/registry"; +import { standardViewProps } from "@web/views/standard_view_props"; +import { useService } from "@web/core/utils/hooks"; +import { useModelWithSampleData } from "@web/model/model"; +import { useSetupAction } from "@web/search/action_hook"; +import { usePager } from "@web/search/pager_hook"; +import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler"; +import { RelationalModel } from "@web/model/relational_model/relational_model"; +import { Layout } from "@web/search/layout"; +import { SearchBar } from "@web/search/search_bar/search_bar"; +import { CogMenu } from "@web/search/cog_menu/cog_menu"; +import { _t } from "@web/core/l10n/translation"; +import { + Component, + onMounted, + onPatched, + onWillUnmount, + useRef, + useState, +} from "@odoo/owl"; + +// ── Constants ─────────────────────────────────────────────────────── +const STATUS_COLORS = { + pending: "#f59e0b", + scheduled: "#3b82f6", + en_route: "#f59e0b", + in_progress: "#8b5cf6", + completed: "#10b981", + cancelled: "#ef4444", + rescheduled: "#f97316", +}; +const STATUS_LABELS = { + pending: "Pending", + scheduled: "Scheduled", + en_route: "En Route", + in_progress: "In Progress", + completed: "Completed", + cancelled: "Cancelled", + rescheduled: "Rescheduled", +}; +const STATUS_ICONS = { + pending: "fa-hourglass-half", + scheduled: "fa-clock-o", + en_route: "fa-truck", + in_progress: "fa-wrench", + completed: "fa-check-circle", + cancelled: "fa-times-circle", + rescheduled: "fa-calendar", +}; + +// Date group keys +const GROUP_PENDING = "pending"; +const GROUP_YESTERDAY = "yesterday"; +const GROUP_TODAY = "today"; +const GROUP_TOMORROW = "tomorrow"; +const GROUP_THIS_WEEK = "this_week"; +const GROUP_LATER = "later"; +const GROUP_LABELS = { + [GROUP_PENDING]: "Pending", + [GROUP_YESTERDAY]: "Yesterday", + [GROUP_TODAY]: "Today", + [GROUP_TOMORROW]: "Tomorrow", + [GROUP_THIS_WEEK]: "This Week", + [GROUP_LATER]: "Upcoming", +}; + +// Pin colours by day group +const DAY_COLORS = { + [GROUP_PENDING]: "#f59e0b", // Amber + [GROUP_YESTERDAY]: "#9ca3af", // Gray + [GROUP_TODAY]: "#ef4444", // Red + [GROUP_TOMORROW]: "#3b82f6", // Blue + [GROUP_THIS_WEEK]: "#10b981", // Green + [GROUP_LATER]: "#a855f7", // Purple +}; +const DAY_ICONS = { + [GROUP_PENDING]: "fa-hourglass-half", + [GROUP_YESTERDAY]: "fa-history", + [GROUP_TODAY]: "fa-exclamation-circle", + [GROUP_TOMORROW]: "fa-arrow-right", + [GROUP_THIS_WEEK]: "fa-calendar", + [GROUP_LATER]: "fa-calendar-o", +}; + +// ── SVG numbered pin ──────────────────────────────────────────────── +function numberedPinSvg(fill, number) { + const txt = String(number); + const fontSize = txt.length > 2 ? 13 : 16; + return ( + `` + + `` + + `` + + `#${txt}` + + `` + ); +} +function numberedPinUri(fill, number) { + return "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(numberedPinSvg(fill, number)); +} + +// ── Helpers ───────────────────────────────────────────────────────── +let _gmapsPromise = null; +function loadGoogleMaps(apiKey) { + if (window.google && window.google.maps) return Promise.resolve(); + if (_gmapsPromise) return _gmapsPromise; + _gmapsPromise = new Promise((resolve, reject) => { + const cb = "_fc_gmap_" + Date.now(); + window[cb] = () => { delete window[cb]; resolve(); }; + const s = document.createElement("script"); + s.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&callback=${cb}`; + s.async = true; s.defer = true; + s.onerror = () => { _gmapsPromise = null; reject(new Error("Google Maps script failed")); }; + document.head.appendChild(s); + }); + return _gmapsPromise; +} + +function initialsOf(name) { + if (!name) return "?"; + const p = name.trim().split(/\s+/); + return p.length >= 2 + ? (p[0][0] + p[p.length - 1][0]).toUpperCase() + : p[0].substring(0, 2).toUpperCase(); +} + +/** Return "YYYY-MM-DD" for a JS Date in local tz */ +function localDateStr(d) { + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; +} + +/** Convert float hours (e.g. 13.5) to "1:30 PM" */ +function floatToTime12(flt) { + if (!flt && flt !== 0) return ""; + let h = Math.floor(flt); + let m = Math.round((flt - h) * 60); + if (m === 60) { h++; m = 0; } + const ampm = h >= 12 ? "PM" : "AM"; + const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h; + return `${h12}:${String(m).padStart(2, "0")} ${ampm}`; +} + +/** Classify a task into one of our group keys based on status and date */ +function classifyTask(task) { + if (task.status === "pending") return GROUP_PENDING; + return classifyDate(task.scheduled_date); +} + +function classifyDate(dateStr) { + if (!dateStr) return GROUP_PENDING; + const now = new Date(); + const todayStr = localDateStr(now); + + const yest = new Date(now); + yest.setDate(yest.getDate() - 1); + const yesterdayStr = localDateStr(yest); + + const tmr = new Date(now); + tmr.setDate(tmr.getDate() + 1); + const tomorrowStr = localDateStr(tmr); + + const endOfWeek = new Date(now); + endOfWeek.setDate(endOfWeek.getDate() + (7 - endOfWeek.getDay())); + const endOfWeekStr = localDateStr(endOfWeek); + + if (dateStr === yesterdayStr) return GROUP_YESTERDAY; + if (dateStr === todayStr) return GROUP_TODAY; + if (dateStr === tomorrowStr) return GROUP_TOMORROW; + if (dateStr <= endOfWeekStr && dateStr > tomorrowStr) return GROUP_THIS_WEEK; + if (dateStr < yesterdayStr) return GROUP_YESTERDAY; + return GROUP_LATER; +} + +const SOURCE_COLORS = { + westin: "#0d6efd", + mobility: "#198754", +}; + +/** Extract unique technicians from task data, sorted by name */ +function extractTechnicians(tasksData) { + const map = {}; + for (const t of tasksData) { + if (t.technician_id) { + const [id, name] = t.technician_id; + if (!map[id]) { + map[id] = { id, name, initials: initialsOf(name) }; + } + } + } + return Object.values(map).sort((a, b) => a.name.localeCompare(b.name)); +} + +/** Group + sort tasks, returning { groupKey: { label, tasks[], count } } */ +function groupTasks(tasksData, localInstanceId, visibleTechIds) { + const sorted = [...tasksData].sort((a, b) => { + const da = a.scheduled_date || ""; + const db = b.scheduled_date || ""; + if (da !== db) return da < db ? -1 : 1; + return (a.time_start || 0) - (b.time_start || 0); + }); + + const hasTechFilter = visibleTechIds && Object.keys(visibleTechIds).length > 0; + + const groups = {}; + const order = [GROUP_PENDING, GROUP_YESTERDAY, GROUP_TODAY, GROUP_TOMORROW, GROUP_THIS_WEEK, GROUP_LATER]; + for (const key of order) { + groups[key] = { + key, + label: GROUP_LABELS[key], + dayColor: DAY_COLORS[key] || "#6b7280", + dayIcon: DAY_ICONS[key] || "fa-circle", + tasks: [], + count: 0, + }; + } + + const dayCounters = {}; + for (const task of sorted) { + const techId = task.technician_id ? task.technician_id[0] : 0; + if (hasTechFilter && !visibleTechIds[techId]) continue; + + const g = classifyTask(task); + const dayKey = task.scheduled_date || "none"; + dayCounters[dayKey] = (dayCounters[dayKey] || 0) + 1; + task._scheduleNum = dayCounters[dayKey]; + task._group = g; + task._dayColor = DAY_COLORS[g] || "#6b7280"; + task._statusColor = STATUS_COLORS[task.status] || "#6b7280"; + task._statusLabel = STATUS_LABELS[task.status] || task.status || ""; + task._statusIcon = STATUS_ICONS[task.status] || "fa-circle"; + task._clientName = task.x_fc_sync_client_name || (task.partner_id ? task.partner_id[1] : "N/A"); + task._techName = task.technician_id ? task.technician_id[1] : "Unassigned"; + task._typeLbl = task.task_type + ? task.task_type.charAt(0).toUpperCase() + task.task_type.slice(1).replace("_", " ") + : "Task"; + task._timeRange = `${task.time_start_display || floatToTime12(task.time_start)} - ${task.time_end_display || ""}`; + const src = task.x_fc_sync_source || localInstanceId || ""; + task._sourceLabel = src ? src.charAt(0).toUpperCase() + src.slice(1) : ""; + task._sourceColor = SOURCE_COLORS[src] || "#6c757d"; + task._dateLabel = task.scheduled_date + ? new Date(task.scheduled_date + "T12:00:00").toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }) + : "No date"; + task._hasCoords = task.address_lat && task.address_lng && task.address_lat !== 0 && task.address_lng !== 0; + groups[g].tasks.push(task); + groups[g].count++; + } + + return order.map((k) => groups[k]).filter((g) => g.count > 0); +} + + +// ── Controller ────────────────────────────────────────────────────── +export class FusionTaskMapController extends Component { + static template = "fusion_tasks.FusionTaskMapView"; + static components = { Layout, SearchBar, CogMenu }; + static props = { + ...standardViewProps, + Model: Function, + modelParams: Object, + Renderer: { type: Function, optional: true }, + buttonTemplate: String, + }; + + setup() { + this.orm = useService("orm"); + this.actionService = useService("action"); + this.mapRef = useRef("mapContainer"); + + this.state = useState({ + loading: true, + error: null, + showTasks: true, + showTechnicians: true, + showTraffic: true, + showRoute: true, + taskCount: 0, + techCount: 0, + sidebarOpen: true, + groups: [], + collapsedGroups: {}, + activeTaskId: null, + visibleGroups: { + [GROUP_YESTERDAY]: false, + [GROUP_TODAY]: true, + [GROUP_TOMORROW]: false, + [GROUP_THIS_WEEK]: false, + [GROUP_LATER]: false, + }, + allTechnicians: [], + visibleTechIds: {}, + }); + + // Yesterday collapsed by default in sidebar list + this.state.collapsedGroups[GROUP_YESTERDAY] = true; + this.state.collapsedGroups[GROUP_LATER] = true; + + this.map = null; + this.taskMarkers = []; + this.taskMarkerMap = {}; // id → marker + this.techMarkers = []; + this.routeLines = []; // route polylines + this.routeLabels = []; // travel time overlay labels + this.routeAnimFrameId = null; + this.infoWindow = null; + this.techStartLocations = {}; + this.apiKey = ""; + this.tasksData = []; + this.locationsData = []; + + const Model = this.props.Model; + this.model = useModelWithSampleData(Model, this.props.modelParams); + useSetupAction({ getLocalState: () => this._meta() }); + usePager(() => ({ + offset: this._meta().offset || 0, + limit: this._meta().limit || 80, + total: this.model.data?.count || this._meta().resCount || 0, + onUpdate: ({ offset, limit }) => this.model.load({ offset, limit }), + })); + this.searchBarToggler = useSearchBarToggler(); + this.display = { controlPanel: {} }; + this._lastDomainStr = ""; + + onMounted(async () => { + window.__fusionMapOpenTask = (id) => this.openTask(id); + await this._loadAndRender(); + this._lastDomainStr = JSON.stringify(this._getDomain()); + }); + onPatched(() => { + const cur = JSON.stringify(this._getDomain()); + if (cur !== this._lastDomainStr && this.map) { + this._lastDomainStr = cur; + this._onModelUpdate(); + } + }); + onWillUnmount(() => { + this._clearMarkers(); + this._clearRoute(); + window.__fusionMapOpenTask = () => {}; + }); + } + + // ── Model helpers (safe access across different Model types) ──── + _meta() { + // RelationalModel uses .config, MapModel uses .metaData + return this.model.metaData || this.model.config || {}; + } + _getDomain() { + const m = this._meta(); + return m.domain || []; + } + + // ── Data ───────────────────────────────────────────────────────── + _storeResult(result) { + this.localInstanceId = result.local_instance_id || this.localInstanceId || ""; + this.tasksData = result.tasks || []; + this.locationsData = result.locations || []; + this.techStartLocations = result.tech_start_locations || {}; + this.state.allTechnicians = extractTechnicians(this.tasksData); + this._rebuildGroups(); + } + + _rebuildGroups() { + this.state.groups = groupTasks( + this.tasksData, this.localInstanceId, this.state.visibleTechIds, + ); + const filteredCount = this.state.groups.reduce((s, g) => s + g.count, 0); + this.state.taskCount = filteredCount; + this.state.techCount = this.locationsData.length; + } + + async _loadAndRender() { + try { + const domain = this._getDomain(); + const result = await this.orm.call("fusion.technician.task", "get_map_data", [domain]); + this.apiKey = result.api_key; + this._storeResult(result); + + if (!this.apiKey) { + this.state.error = _t("Google Maps API key not configured. Go to Settings > Fusion Claims."); + this.state.loading = false; + return; + } + await loadGoogleMaps(this.apiKey); + if (this.map) { + this._renderMarkers(); + } else if (this.mapRef.el) { + this._initMap(); + } + this.state.loading = false; + } catch (e) { + console.error("FusionTaskMap load error:", e); + this.state.error = String(e); + this.state.loading = false; + } + } + + async _softRefresh() { + if (!this.map) return; + try { + const center = this.map.getCenter(); + const zoom = this.map.getZoom(); + + const domain = this._getDomain(); + const result = await this.orm.call("fusion.technician.task", "get_map_data", [domain]); + this._storeResult(result); + + this._placeMarkers(); + + if (center && zoom != null) { + this.map.setCenter(center); + this.map.setZoom(zoom); + } + } catch (e) { + console.error("FusionTaskMap soft refresh error:", e); + } + } + + async _onModelUpdate() { + if (!this.map) return; + try { + const domain = this._getDomain(); + const result = await this.orm.call("fusion.technician.task", "get_map_data", [domain]); + this._storeResult(result); + this._renderMarkers(); + } catch (e) { + console.error("FusionTaskMap update error:", e); + } + } + + // ── Map ────────────────────────────────────────────────────────── + _initMap() { + if (!this.mapRef.el) return; + this.map = new google.maps.Map(this.mapRef.el, { + zoom: 10, + center: { lat: 43.7, lng: -79.4 }, + mapTypeControl: true, + streetViewControl: false, + fullscreenControl: true, + zoomControl: true, + styles: [{ featureType: "poi", stylers: [{ visibility: "off" }] }], + }); + // Traffic layer (on by default, toggleable) + this.trafficLayer = new google.maps.TrafficLayer(); + this.trafficLayer.setMap(this.map); + + this.infoWindow = new google.maps.InfoWindow(); + // Close popup when clicking anywhere on the map + this.map.addListener("click", () => { + this.infoWindow.close(); + }); + // Clear sidebar highlight when popup closes (by any means) + this.infoWindow.addListener("closeclick", () => { + this.state.activeTaskId = null; + }); + this._renderMarkers(); + } + + _clearMarkers() { + for (const m of this.taskMarkers) m.setMap(null); + for (const m of this.techMarkers) m.setMap(null); + this.taskMarkers = []; + this.taskMarkerMap = {}; + this.techMarkers = []; + } + + _clearRoute() { + if (this.routeAnimFrameId) { + cancelAnimationFrame(this.routeAnimFrameId); + this.routeAnimFrameId = null; + } + for (const l of this.routeLines) l.setMap(null); + this.routeLines = []; + for (const lb of this.routeLabels) lb.setMap(null); + this.routeLabels = []; + } + + _placeMarkers() { + for (const m of this.taskMarkers) m.setMap(null); + for (const m of this.techMarkers) m.setMap(null); + this.taskMarkers = []; + this.taskMarkerMap = {}; + this.techMarkers = []; + + const bounds = new google.maps.LatLngBounds(); + let hasBounds = false; + + if (this.state.showTasks) { + for (const group of this.state.groups) { + const groupVisible = this.state.visibleGroups[group.key] !== false; + for (const task of group.tasks) { + if (!task.address_lat || !task.address_lng) continue; + if (!groupVisible) continue; + const pos = { lat: task.address_lat, lng: task.address_lng }; + const num = task._scheduleNum; + const color = task._dayColor; + + const marker = new google.maps.Marker({ + position: pos, + map: this.map, + title: `#${num} ${task.name} - ${task._clientName}`, + icon: { + url: numberedPinUri(color, num), + scaledSize: new google.maps.Size(38, 50), + anchor: new google.maps.Point(19, 50), + }, + zIndex: 10 + num, + }); + + marker.addListener("click", () => this._openTaskPopup(task, marker)); + this.taskMarkers.push(marker); + this.taskMarkerMap[task.id] = marker; + bounds.extend(pos); + hasBounds = true; + } + } + } + + if (this.state.showTechnicians) { + for (const loc of this.locationsData) { + if (!loc.latitude || !loc.longitude) continue; + const pos = { lat: loc.latitude, lng: loc.longitude }; + const initials = initialsOf(loc.name); + const src = loc.sync_instance || this.localInstanceId || ""; + const isRemote = src && src !== this.localInstanceId; + const pinColor = isRemote + ? (SOURCE_COLORS[src] || "#6c757d") + : "#1d4ed8"; + const srcLabel = src ? src.charAt(0).toUpperCase() + src.slice(1) : ""; + const svg = + `` + + `` + + `${initials}` + + ``; + const marker = new google.maps.Marker({ + position: pos, + map: this.map, + title: loc.name + (isRemote ? ` [${srcLabel}]` : ""), + icon: { + url: "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(svg), + scaledSize: new google.maps.Size(44, 44), + anchor: new google.maps.Point(22, 22), + }, + zIndex: 100, + }); + marker.addListener("click", () => { + this.infoWindow.setContent(` +
+
+ ${loc.name} + ${srcLabel ? `${srcLabel}` : ""} +
+
+
Last seen: ${loc.logged_at || "Unknown"}
+
Accuracy: ${loc.accuracy ? Math.round(loc.accuracy) + "m" : "N/A"}
+
+
`); + this.infoWindow.open(this.map, marker); + }); + this.techMarkers.push(marker); + bounds.extend(pos); + hasBounds = true; + } + } + + const starts = this.techStartLocations || {}; + for (const uid of Object.keys(starts)) { + const sl = starts[uid]; + if (sl && sl.lat && sl.lng) { + bounds.extend({ lat: sl.lat, lng: sl.lng }); + hasBounds = true; + } + } + + return { bounds, hasBounds }; + } + + _renderMarkers() { + this._clearRoute(); + const { bounds, hasBounds } = this._placeMarkers(); + + if (this.state.showRoute && this.state.showTasks) { + this._renderRoute(); + } + + if (hasBounds) { + try { + this.map.fitBounds(bounds); + if (this.taskMarkers.length + this.techMarkers.length === 1) { + this.map.setZoom(14); + } + } catch (_e) { + // bounds not ready yet + } + } + } + + _renderRoute() { + this._clearRoute(); + + const routeSegments = {}; + for (const group of this.state.groups) { + if (this.state.visibleGroups[group.key] === false) continue; + for (const task of group.tasks) { + if (!task._hasCoords) continue; + const techId = task.technician_id ? task.technician_id[0] : 0; + if (!techId) continue; + const dayKey = task.scheduled_date || "none"; + const segKey = `${techId}_${dayKey}`; + if (!routeSegments[segKey]) { + routeSegments[segKey] = { + name: task._techName, day: dayKey, + techId, tasks: [], + }; + } + routeSegments[segKey].tasks.push(task); + } + } + + const LEG_COLORS = [ + "#3b82f6", "#f59e0b", "#8b5cf6", "#ec4899", + "#f97316", "#0ea5e9", "#d946ef", "#06b6d4", + "#a855f7", "#6366f1", "#eab308", "#0284c7", + "#c026d3", "#7c3aed", "#2563eb", "#db2777", + "#9333ea", "#0891b2", "#4f46e5", "#be185d", + ]; + let globalLegIdx = 0; + + if (!this._directionsService) { + this._directionsService = new google.maps.DirectionsService(); + } + + const allAnimLines = []; + const starts = this.techStartLocations || {}; + + for (const segKey of Object.keys(routeSegments)) { + const seg = routeSegments[segKey]; + const tasks = seg.tasks; + tasks.sort((a, b) => (a.time_start || 0) - (b.time_start || 0)); + + const startLoc = starts[seg.techId]; + const hasStart = startLoc && startLoc.lat && startLoc.lng; + + if (tasks.length < 2 && !hasStart) continue; + if (tasks.length < 1) continue; + + const segBaseColor = LEG_COLORS[globalLegIdx % LEG_COLORS.length]; + + let origin, destination, waypoints, hasStartLeg; + + if (hasStart) { + origin = { lat: startLoc.lat, lng: startLoc.lng }; + destination = { + lat: tasks[tasks.length - 1].address_lat, + lng: tasks[tasks.length - 1].address_lng, + }; + waypoints = tasks.slice(0, -1).map(t => ({ + location: { lat: t.address_lat, lng: t.address_lng }, + stopover: true, + })); + hasStartLeg = true; + } else { + origin = { lat: tasks[0].address_lat, lng: tasks[0].address_lng }; + destination = { + lat: tasks[tasks.length - 1].address_lat, + lng: tasks[tasks.length - 1].address_lng, + }; + waypoints = tasks.slice(1, -1).map(t => ({ + location: { lat: t.address_lat, lng: t.address_lng }, + stopover: true, + })); + hasStartLeg = false; + } + + if (hasStart) { + const startSvg = + `` + + `` + + `` + + ``; + const startMarker = new google.maps.Marker({ + position: origin, + map: this.map, + title: `${seg.name} - Start`, + icon: { + url: "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(startSvg), + scaledSize: new google.maps.Size(32, 32), + anchor: new google.maps.Point(16, 16), + }, + zIndex: 5, + }); + startMarker.addListener("click", () => { + this.infoWindow.setContent(` +
+
+ ${seg.name} - Start +
+
+ ${startLoc.address || 'Start location'} +
${startLoc.source === 'clock_in' ? 'Clock-in location' : startLoc.source === 'start_address' ? 'Home address' : 'Company HQ'}
+
+
`); + this.infoWindow.open(this.map, startMarker); + }); + this.routeLines.push(startMarker); + } + + this._directionsService.route({ + origin, + destination, + waypoints, + optimizeWaypoints: false, + travelMode: google.maps.TravelMode.DRIVING, + avoidTolls: true, + drivingOptions: { + departureTime: new Date(), + trafficModel: "bestguess", + }, + }, (result, status) => { + if (status !== "OK" || !result.routes || !result.routes[0]) return; + + const route = result.routes[0]; + + for (let li = 0; li < route.legs.length; li++) { + const leg = route.legs[li]; + const legColor = LEG_COLORS[globalLegIdx % LEG_COLORS.length]; + globalLegIdx++; + + const legPath = []; + for (const step of leg.steps) { + for (const pt of step.path) legPath.push(pt); + } + if (legPath.length < 2) continue; + + const baseLine = new google.maps.Polyline({ + path: legPath, map: this.map, + strokeColor: legColor, strokeOpacity: 0.25, strokeWeight: 6, + zIndex: 1, + }); + this.routeLines.push(baseLine); + + const animLine = new google.maps.Polyline({ + path: legPath, map: this.map, + strokeOpacity: 0, strokeWeight: 0, zIndex: 2, + icons: [{ + icon: { + path: "M 0,-0.5 0,0.5", + strokeOpacity: 0.8, strokeColor: legColor, + strokeWeight: 3, scale: 4, + }, + offset: "0%", repeat: "16px", + }], + }); + this.routeLines.push(animLine); + allAnimLines.push(animLine); + + const arrowLine = new google.maps.Polyline({ + path: legPath, map: this.map, + strokeOpacity: 0, strokeWeight: 0, zIndex: 3, + icons: [{ + icon: { + path: google.maps.SymbolPath.FORWARD_OPEN_ARROW, + scale: 3, strokeColor: legColor, + strokeOpacity: 0.9, strokeWeight: 2.5, + }, + offset: "0%", repeat: "80px", + }], + }); + this.routeLines.push(arrowLine); + allAnimLines.push(arrowLine); + + const dur = leg.duration_in_traffic || leg.duration; + const dist = leg.distance; + if (dur) { + const totalMins = Math.round(dur.value / 60); + const totalKm = dist ? (dist.value / 1000).toFixed(1) : null; + + const destIdx = hasStartLeg ? li : li + 1; + const destTask = destIdx < tasks.length ? tasks[destIdx] : tasks[tasks.length - 1]; + const etaFloat = destTask.time_start || 0; + const etaStr = etaFloat ? floatToTime12(etaFloat) : ""; + + const techName = seg.name; + this.routeLabels.push(this._createTravelLabel( + legPath, totalMins, totalKm, legColor, techName, etaStr, + )); + } + } + + if (!this.routeAnimFrameId) { + this._startRouteAnimation(allAnimLines); + } + }); + } + } + + _pointAlongLeg(leg, fraction) { + const points = []; + for (const step of leg.steps) { + for (const pt of step.path) { + points.push(pt); + } + } + if (points.length < 2) return leg.start_location; + + const segDists = []; + let totalDist = 0; + for (let i = 1; i < points.length; i++) { + const d = google.maps.geometry + ? google.maps.geometry.spherical.computeDistanceBetween(points[i - 1], points[i]) + : this._haversine(points[i - 1], points[i]); + segDists.push(d); + totalDist += d; + } + + const target = totalDist * fraction; + let acc = 0; + for (let i = 0; i < segDists.length; i++) { + if (acc + segDists[i] >= target) { + const remain = target - acc; + const ratio = segDists[i] > 0 ? remain / segDists[i] : 0; + return new google.maps.LatLng( + points[i].lat() + (points[i + 1].lat() - points[i].lat()) * ratio, + points[i].lng() + (points[i + 1].lng() - points[i].lng()) * ratio, + ); + } + acc += segDists[i]; + } + return points[points.length - 1]; + } + + _haversine(a, b) { + const R = 6371000; + const dLat = (b.lat() - a.lat()) * Math.PI / 180; + const dLng = (b.lng() - a.lng()) * Math.PI / 180; + const s = Math.sin(dLat / 2) ** 2 + + Math.cos(a.lat() * Math.PI / 180) * Math.cos(b.lat() * Math.PI / 180) * + Math.sin(dLng / 2) ** 2; + return R * 2 * Math.atan2(Math.sqrt(s), Math.sqrt(1 - s)); + } + + _createTravelLabel(legPath, mins, km, color, techName, eta) { + if (!this._TravelLabel) { + this._TravelLabel = class extends google.maps.OverlayView { + constructor(path, html) { + super(); + this._path = path; + this._html = html; + this._div = null; + } + onAdd() { + this._div = document.createElement("div"); + this._div.style.position = "absolute"; + this._div.style.whiteSpace = "nowrap"; + this._div.style.pointerEvents = "none"; + this._div.style.zIndex = "50"; + this._div.style.transition = "left .3s ease, top .3s ease"; + this._div.innerHTML = this._html; + this.getPanes().floatPane.appendChild(this._div); + } + draw() { + const proj = this.getProjection(); + if (!proj || !this._div) return; + const map = this.getMap(); + if (!map) return; + const bounds = map.getBounds(); + if (!bounds) return; + + const visible = this._path.filter(p => bounds.contains(p)); + if (visible.length === 0) { + this._div.style.display = "none"; + return; + } + this._div.style.display = ""; + + const anchor = visible[Math.floor(visible.length / 2)]; + + const px = proj.fromLatLngToDivPixel(anchor); + if (px) { + this._div.style.left = (px.x - this._div.offsetWidth / 2) + "px"; + this._div.style.top = (px.y - this._div.offsetHeight - 8) + "px"; + } + } + onRemove() { + if (this._div && this._div.parentNode) { + this._div.parentNode.removeChild(this._div); + } + this._div = null; + } + }; + } + + const timeStr = mins < 60 + ? `${mins} min` + : `${Math.floor(mins / 60)}h ${mins % 60}m`; + const distStr = km ? `${km} km` : ""; + + const firstName = techName ? techName.split(" ")[0] : ""; + const html = `
${firstName ? `${firstName}|` : ""}🚗${timeStr}${distStr ? `· ${distStr}` : ""}${eta ? `|ETA ${eta}` : ""}
`; + + const label = new this._TravelLabel(legPath, html); + label.setMap(this.map); + return label; + } + + _startRouteAnimation(animLines) { + let off = 0; + let last = 0; + const animate = (ts) => { + this.routeAnimFrameId = requestAnimationFrame(animate); + if (ts - last < 50) return; + last = ts; + off = (off + 0.08) % 100; + const pct = off + "%"; + for (const line of animLines) { + const icons = line.get("icons"); + if (icons && icons.length > 0) { + icons[0].offset = pct; + line.set("icons", icons); + } + } + }; + this.routeAnimFrameId = requestAnimationFrame(animate); + } + + _openTaskPopup(task, marker) { + const c = task._dayColor; + const sc = task._statusColor; + const navDest = task.address_lat && task.address_lng + ? `${task.address_lat},${task.address_lng}` + : encodeURIComponent(task.address_display || ""); + const html = ` +
+
+
+ #${task._scheduleNum} ${task.name} + ${task._statusLabel} +
+
${task._clientName}
+
+
+ + ${task._typeLbl} + + + ${task._timeRange} + + ${task.travel_time_minutes ? `${task.travel_time_minutes} min` : ""} +
+
+
👤${task._techName}
+
📅${task.scheduled_date || "No date"}
+ ${task.address_display ? `
📍${task.address_display}
` : ""} +
+
+ + + Navigate → + +
+
`; + this.infoWindow.setContent(html); + this.infoWindow.open(this.map, marker); + } + + // ── Sidebar actions ───────────────────────────────────────────── + toggleSidebar() { + this.state.sidebarOpen = !this.state.sidebarOpen; + // Trigger map resize after CSS transition + if (this.map) { + setTimeout(() => google.maps.event.trigger(this.map, "resize"), 320); + } + } + + toggleGroup(groupKey) { + this.state.collapsedGroups[groupKey] = !this.state.collapsedGroups[groupKey]; + } + + isGroupCollapsed(groupKey) { + return !!this.state.collapsedGroups[groupKey]; + } + + focusTask(taskId) { + this.state.activeTaskId = taskId; + const marker = this.taskMarkerMap[taskId]; + if (marker && this.map) { + this.map.panTo(marker.getPosition()); + this.map.setZoom(15); + // Find the task data + for (const g of this.state.groups) { + for (const t of g.tasks) { + if (t.id === taskId) { + this._openTaskPopup(t, marker); + return; + } + } + } + } + } + + // ── Day filter toggle ──────────────────────────────────────────── + toggleDayFilter(groupKey) { + this.state.visibleGroups[groupKey] = !this.state.visibleGroups[groupKey]; + this._renderMarkers(); + } + + isGroupVisible(groupKey) { + return this.state.visibleGroups[groupKey] !== false; + } + + showAllDays() { + for (const k of Object.keys(this.state.visibleGroups)) { + this.state.visibleGroups[k] = true; + } + this._renderMarkers(); + } + + showTodayOnly() { + for (const k of Object.keys(this.state.visibleGroups)) { + this.state.visibleGroups[k] = k === GROUP_TODAY; + } + this._renderMarkers(); + } + + // ── Technician filter ───────────────────────────────────────────── + toggleTechFilter(techId) { + if (this.state.visibleTechIds[techId]) { + delete this.state.visibleTechIds[techId]; + } else { + this.state.visibleTechIds[techId] = true; + } + this._rebuildGroups(); + this._renderMarkers(); + } + + isTechVisible(techId) { + const hasFilter = Object.keys(this.state.visibleTechIds).length > 0; + return !hasFilter || !!this.state.visibleTechIds[techId]; + } + + showAllTechs() { + this.state.visibleTechIds = {}; + this._rebuildGroups(); + this._renderMarkers(); + } + + // ── Top bar actions ───────────────────────────────────────────── + toggleTraffic() { + this.state.showTraffic = !this.state.showTraffic; + if (this.trafficLayer) { + this.trafficLayer.setMap(this.state.showTraffic ? this.map : null); + } + } + toggleTasks() { + this.state.showTasks = !this.state.showTasks; + this._renderMarkers(); + } + toggleTechnicians() { + this.state.showTechnicians = !this.state.showTechnicians; + this._renderMarkers(); + } + toggleRoute() { + this.state.showRoute = !this.state.showRoute; + if (this.state.showRoute) { + this._renderRoute(); + } else { + this._clearRoute(); + } + } + onRefresh() { + this.state.loading = true; + this._loadAndRender(); + } + async openTask(taskId) { + if (!taskId) return; + try { + await this.actionService.doAction( + { + type: "ir.actions.act_window", + res_model: "fusion.technician.task", + res_id: taskId, + view_mode: "form", + views: [[false, "form"]], + target: "new", + context: { dialog_size: "extra-large" }, + }, + { onClose: () => this._softRefresh() }, + ); + } catch (e) { + console.error("[FusionMap] openTask failed:", e); + this.actionService.doAction({ + type: "ir.actions.act_window", + res_model: "fusion.technician.task", + res_id: taskId, + view_mode: "form", + views: [[false, "form"]], + target: "current", + }); + } + } + async createNewTask() { + try { + await this.actionService.doAction( + { + type: "ir.actions.act_window", + res_model: "fusion.technician.task", + view_mode: "form", + views: [[false, "form"]], + target: "new", + context: { default_task_type: "delivery", dialog_size: "extra-large" }, + }, + { onClose: () => this._softRefresh() }, + ); + } catch (e) { + console.error("[FusionMap] createNewTask failed:", e); + this.actionService.doAction({ + type: "ir.actions.act_window", + res_model: "fusion.technician.task", + view_mode: "form", + views: [[false, "form"]], + target: "current", + context: { default_task_type: "delivery" }, + }); + } + } +} + +window.__fusionMapOpenTask = () => {}; + +// ── Minimal ArchParser for tags (no web_map dependency) ─────── +class FusionMapArchParser { + parse(xmlDoc, models, modelName) { + const fieldNames = []; + const activeFields = {}; + if (xmlDoc && xmlDoc.querySelectorAll) { + for (const fieldEl of xmlDoc.querySelectorAll("field")) { + const name = fieldEl.getAttribute("name"); + if (name) { + fieldNames.push(name); + activeFields[name] = { attrs: {}, options: {} }; + } + } + } + return { fieldNames, activeFields }; + } +} + +// ── View registration (self-contained, no @web_map dependency) ────── +const fusionTaskMapView = { + type: "map", + display_name: _t("Map"), + icon: "oi-view-map", + multiRecord: true, + searchMenuTypes: ["filter", "groupBy", "favorite"], + Controller: FusionTaskMapController, + Model: RelationalModel, + ArchParser: FusionMapArchParser, + buttonTemplate: "fusion_tasks.FusionTaskMapView.Buttons", + props(genericProps, view, config) { + const { resModel, fields } = genericProps; + let archInfo = { fieldNames: [], activeFields: {} }; + if (view && view.arch) { + archInfo = new FusionMapArchParser().parse(view.arch); + } + return { + ...genericProps, + buttonTemplate: "fusion_tasks.FusionTaskMapView.Buttons", + Model: RelationalModel, + modelParams: { + config: { + resModel, + fields, + activeFields: archInfo.activeFields || {}, + isMonoRecord: false, + }, + state: { + domain: genericProps.domain || [], + context: genericProps.context || {}, + groupBy: genericProps.groupBy || [], + orderBy: genericProps.orderBy || [], + }, + }, + }; + }, +}; +registry.category("views").add("fusion_task_map", fusionTaskMapView); diff --git a/Entech Plating/fusion_tasks/static/src/xml/fusion_task_map_view.xml b/Entech Plating/fusion_tasks/static/src/xml/fusion_task_map_view.xml new file mode 100644 index 00000000..7e99b8fe --- /dev/null +++ b/Entech Plating/fusion_tasks/static/src/xml/fusion_task_map_view.xml @@ -0,0 +1,255 @@ + + + + +
+ + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ Deliveries + +
+ +
+ + + + +
+ + + + +
+ + + +
+ + + + +
+
+
+ + +
+ + +
+ + + + hidden + +
+ + +
+ +
+ + +
+ + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+
+ + +
+ + +
+ + + min travel + + + + + + + Edit + +
+
+ +
+
+ + +
+ + No tasks found +
+
+ + + +
+ + + + + +
+ +
+ + + + Pins: + Pending + Today + Tomorrow + This Week + Upcoming + Yesterday + + + + +
+ + +
+
+ + +
+
+ + Loading Google Maps... +
+
+ + +
+ +
+ + +
+
+ +
No locations to show
+

Try adjusting the filters or date range.

+
+
+
+
+
+ +
+ + + + + diff --git a/Entech Plating/fusion_tasks/views/res_config_settings_views.xml b/Entech Plating/fusion_tasks/views/res_config_settings_views.xml new file mode 100644 index 00000000..4247bf5e --- /dev/null +++ b/Entech Plating/fusion_tasks/views/res_config_settings_views.xml @@ -0,0 +1,156 @@ + + + + + res.config.settings.view.form.fusion.tasks + res.config.settings + + + + + +

Technician Management

+ +
+ +
+
+ Google Maps API +
+ API key for Google Maps Places autocomplete in address fields and Distance Matrix travel calculations. +
+
+ +
+ +
+
+ +
+
+ Google Business Review URL +
+ Link to your Google Business Profile review page. + Sent to clients after service completion (when "Request Google Review" is enabled on the task). +
+
+ +
+
+
+ +
+
+ Store / Scheduling Hours +
+ Operating hours for technician task scheduling. Tasks can only be booked + within these hours. Calendar view is also restricted to this range. +
+
+ + to + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+ Default HQ / Fallback Address +
+ Company default start location used when a technician has no personal + start address set. Each technician can set their own start location + in their user profile or from the portal. +
+
+ +
+
+
+ +
+
+ Location History Retention +
+ How many days to keep technician GPS location history before automatic cleanup. +
+
+ + days +
+
+ Leave empty = 30 days. Enter 0 = delete at end of each day. 1+ = keep that many days. +
+
+
+
+ +

Push Notifications

+ +
+ +
+
+ +
+
+
+
+ +
+
+ Notification Advance Time +
+ Send push notification this many minutes before a scheduled task. +
+
+ minutes +
+
+
+ +
+
+ VAPID Public Key +
+ +
+
+
+ +
+
+ VAPID Private Key +
+ +
+
+
+
+ +
+
+
+
+
diff --git a/Entech Plating/fusion_tasks/views/task_sync_views.xml b/Entech Plating/fusion_tasks/views/task_sync_views.xml new file mode 100644 index 00000000..ac5a8f9c --- /dev/null +++ b/Entech Plating/fusion_tasks/views/task_sync_views.xml @@ -0,0 +1,80 @@ + + + + + + + + fusion.task.sync.config.form + fusion.task.sync.config + +
+
+
+ +
+

+
+ + + + + + + + + + + + + + +
+ + Technicians are matched across instances by their + Tech Sync ID field (Settings > Users). + Set the same ID (e.g. "gordy") on both instances for each shared technician. +
+
+ +
+
+ + + + + + fusion.task.sync.config.list + fusion.task.sync.config + + + + + + + + + + + + + + + + + Task Sync Instances + fusion.task.sync.config + list,form + + + + +
diff --git a/Entech Plating/fusion_tasks/views/technician_location_views.xml b/Entech Plating/fusion_tasks/views/technician_location_views.xml new file mode 100644 index 00000000..6248f104 --- /dev/null +++ b/Entech Plating/fusion_tasks/views/technician_location_views.xml @@ -0,0 +1,102 @@ + + + + + + + + fusion.technician.location.list + fusion.technician.location + + + + + + + + + + + + + + + + + fusion.technician.location.form + fusion.technician.location + +
+ + + + + + + + + + + + + + +
+
+
+ + + + + + fusion.technician.location.search + fusion.technician.location + + + + + + + + + + + + + + + + + + + + Location History + fusion.technician.location + list,form + + { + 'search_default_filter_today': 1, + 'search_default_group_user': 1, + } + +

+ No location data logged yet. +

+

Technician locations are automatically logged when they use the portal.

+
+
+ + + + + + +
diff --git a/Entech Plating/fusion_tasks/views/technician_task_views.xml b/Entech Plating/fusion_tasks/views/technician_task_views.xml new file mode 100644 index 00000000..0406458a --- /dev/null +++ b/Entech Plating/fusion_tasks/views/technician_task_views.xml @@ -0,0 +1,507 @@ + + + + + + + + Technician Task + fusion.technician.task + TASK- + 5 + 1 + + + + + + + res.users.form.field.staff + res.users + + + + + + + + + + + + + + + fusion.technician.task.search + fusion.technician.task + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fusion.technician.task.form + fusion.technician.task + +
+ + +
+
+ + + +
+
+ + + +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + fusion.technician.task.list + fusion.technician.task + + + + + + + + + + + + + + + + + + + + + + + + + fusion.technician.task.kanban + fusion.technician.task + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + +
+ +
+
+ + - +
+
+ + + + + + + +
+
+ + + min + +
+
+ + + technician(s) +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + fusion.technician.task.calendar + fusion.technician.task + + + + + + + + + + + + + + + + + + + + + + + + fusion.technician.task.map + fusion.technician.task + + + + + + + + + + + + + + + + + + + + Technician Tasks + fusion.technician.task + list,kanban,form,calendar,map + + {'search_default_filter_active': 1} + +

+ Create your first technician task +

+

Schedule deliveries, repairs, and other field tasks for your technicians.

+
+
+ + + + Schedule + fusion.technician.task + map,calendar,list,kanban,form + + {'search_default_filter_active': 1} + + + + + Task Map + fusion.technician.task + map,list,kanban,form,calendar + + {'search_default_filter_active': 1} + + + + + Today's Tasks + fusion.technician.task + kanban,list,form,map + + {'search_default_filter_today': 1, 'search_default_filter_active': 1} + + + + + My Tasks + fusion.technician.task + list,kanban,form,calendar,map + + {'search_default_filter_my_tasks': 1, 'search_default_filter_active': 1} + + + + + Pending Tasks + fusion.technician.task + list,kanban,form + + {'search_default_filter_pending': 1} + + + + + Task Calendar + fusion.technician.task + calendar,list,kanban,form,map + + {'search_default_filter_active': 1} + + + + + + + + + + + + + + + + + + + + + +
diff --git a/fusion-plating/CLAUDE.md b/fusion-plating/CLAUDE.md index 0a0bb067..5e8d6a3f 100644 --- a/fusion-plating/CLAUDE.md +++ b/fusion-plating/CLAUDE.md @@ -3,9 +3,11 @@ ## Project Fusion Plating is a multi-module Odoo 19 ERP for electroless nickel plating and metal finishing shops. Built by Nexa Systems for EN Technologies (the client). Replaces Steelhead Software. -## Module Structure +## Module Structure (30 modules) ``` fusion_plating/ — Core: facilities, process types, tanks, baths, chemistry, recipes +fusion_plating_batch/ — Rack/barrel batch tracking (FpBatch, FpBatchChemistry) +fusion_plating_kpi/ — KPI definitions, daily auto-compute, dashboard views fusion_plating_configurator/ — Quotation configurator, pricing engine, part catalog, 3D viewer fusion_plating_receiving/ — Parts receiving, inspection, damage logging fusion_plating_invoicing/ — Invoice strategies (deposit/progress/net/COD), account holds @@ -15,6 +17,8 @@ fusion_plating_shopfloor/ — Tablet UI, plant overview kanban, proces fusion_plating_portal/ — Customer portal + self-service configurator wizard fusion_plating_reports/ — PDF reports (WO margin, discharge sample, CoC, etc.) fusion_plating_compliance/ — Compliance framework, jurisdictions +fusion_plating_compliance_on/ — Ontario compliance reference data (data-only, no menus) +fusion_plating_compliance_tor/ — Toronto bylaw discharge limits (data-only, no menus) fusion_plating_aerospace/ — AS9100 / Nadcap fusion_plating_nuclear/ — CSA N299 / CNSC fusion_plating_cgp/ — Controlled Goods Program @@ -22,9 +26,10 @@ fusion_plating_safety/ — SDS, WHMIS, JHSC fusion_plating_quality/ — QMS (NCR, CAPA, calibration) fusion_plating_logistics/ — Pickup & delivery, chain of custody fusion_plating_culture/ — Values / fundamentals -fusion_plating_bridge_mrp/ — MRP integration (recipe→WO, portal job, delivery bridge) +fusion_plating_bridge_mrp/ — MRP integration (recipe→WO, portal job, work order priorities) fusion_plating_bridge_sign/ — Digital signatures fusion_plating_bridge_quality/ — Quality bridge +fusion_plating_bridge_documents/ — Odoo Documents integration (NCR, CAPA, FAIR, Doc Control) fusion_plating_process_en/ — Electroless nickel process pack fusion_plating_process_chrome/ — Chrome process pack fusion_plating_process_anodize/ — Anodizing process pack @@ -32,6 +37,32 @@ fusion_plating_process_black_oxide/ — Black oxide process pack fusion_tasks/ — Local delivery dispatch (GPS, maps, driver scheduling) ``` +## Menu Structure (Plating App) +The Plating app (`menu_fp_root`, seq 46) has these top-level menus: + +| Seq | Menu | Module | Children | +|-----|------|--------|----------| +| 3 | KPIs | fusion_plating_kpi | KPIs, KPI History, Production/Quality/Finance dashboards | +| 5 | Sales | fusion_plating_configurator + portal | Quotations, Sale Orders, Customers, Part Catalog, Quote Requests, Portal Jobs | +| 8 | Configurator | fusion_plating_configurator | New Quote, Coating Configs, Pricing Rules, Treatments | +| 12 | Shop Floor | fusion_plating_shopfloor | Plant Overview, Tablet Station, Bake Windows, First-Piece Gates | +| 15 | Receiving | fusion_plating_receiving | All Receiving, Pending Inspection, Discrepancies | +| 18 | Operations | fusion_plating (core) | Process Recipes, Production Priorities (bridge_mrp), Batches (batch), Baths, Chemistry Logs, Tanks | +| 25 | Certificates | fusion_plating_certificates | All, CoC, Thickness Reports | +| 30 | Quality | fusion_plating_quality | Holds, NCRs, CAPAs, FAIR, Audits, Doc Control | +| 40 | Compliance | fusion_plating_compliance | Permits, Discharge, Waste, Calendar, Spills, Config | +| 45 | Safety | fusion_plating_safety | SDS, Training, Exposure, JHSC, Incidents, PPE | +| 50 | Logistics | fusion_plating_logistics + fusion_tasks | Pickups, Deliveries, Routes, CoC, POD, Field Tasks, Task Map, Task Calendar | +| 60 | Aerospace | fusion_plating_aerospace | AS9100, Nadcap, Counterfeit, Config Items, Risk | +| 65 | Nuclear | fusion_plating_nuclear | Program, ITP, 10CFR21, Pedigree, CNSC | +| 70 | CGP | fusion_plating_cgp | Registration, AI, PSA, Visitors, Goods, Shipments, Security, Access Log | +| 80 | Culture | fusion_plating_culture | Values, Recognitions | +| 90 | Configuration | fusion_plating (core) + many | Facilities, Work Centres, Process Categories/Types, Bath Params, Stations, Ovens, Invoice Strategy, Account Holds, Training Types, Chemicals, Notification Templates/Log, Calibration, Specs, AVL, Value Sets/Rotations, N299 Levels, Vehicles | + +**Field Service** (`fusion_tasks`) also has its own standalone root app (seq 45) with Map View, Tasks, Calendar, Configuration. The same task actions are also accessible under Plating > Logistics. + +**Key rule**: Sales menu is unified in `fusion_plating_configurator`. Portal module adds Quote Requests + Portal Jobs as children (referencing `fusion_plating_configurator.menu_fp_sales`). Do NOT create a separate Sales menu in portal. + ## Critical Rules — Odoo 19 1. **NEVER code from memory** — Read reference files from the server first. 2. **Backend OWL**: `static template`, `static props = ["*"]`, standalone `rpc()` from `@web/core/network/rpc`. NOT `useService("rpc")`. @@ -297,6 +328,8 @@ Project: `nexasystems` (id: `ikvdlqkbqsitabxidvnq`) | `fusion.plating.ncr` | `fusion_plating_quality` | Non-conformance reports | | `fusion.plating.capa` | `fusion_plating_quality` | Corrective actions | | `fusion.plating.batch` | `fusion_plating_batch` | Rack/barrel batch tracking | +| `fusion.plating.kpi` | `fusion_plating_kpi` | KPI definition (OTD, yield, throughput, etc.) | +| `fusion.plating.kpi.value` | `fusion_plating_kpi` | KPI daily value (auto-computed or manual) | | `fusion.plating.delivery` | `fusion_plating_logistics` | Delivery with chain of custody | | `fusion.plating.pickup.request` | `fusion_plating_logistics` | Customer pickup requests | | `fusion.plating.route` | `fusion_plating_logistics` | Driver routes with stops | diff --git a/fusion-plating/fusion_plating/views/fp_menu.xml b/fusion-plating/fusion_plating/views/fp_menu.xml index 8a4f3876..5d044a99 100644 --- a/fusion-plating/fusion_plating/views/fp_menu.xml +++ b/fusion-plating/fusion_plating/views/fp_menu.xml @@ -17,7 +17,7 @@ + sequence="18"/> + + + + + + AL Tanks + + + + Specialty Tanks + + + + Steel Tanks + + + + Waste Water + + + + Waste Water Treatment + + + + common + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_sequence_data.xml b/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_sequence_data.xml new file mode 100644 index 00000000..bafe2e13 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_sequence_data.xml @@ -0,0 +1,14 @@ + + + + + + Maintenance Plan + fp.maintenance.plan + MPLAN/ + 4 + + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_stage_data.xml b/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_stage_data.xml new file mode 100644 index 00000000..425de550 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/data/fp_maintenance_stage_data.xml @@ -0,0 +1,28 @@ + + + + + + + New + + + + + + + Active + + + + + + + Completed + + + + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/__init__.py b/fusion-plating/fusion_plating_bridge_maintenance/models/__init__.py new file mode 100644 index 00000000..90e29899 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from . import fp_maintenance_plan +from . import fp_maintenance_node +from . import fp_maintenance_label +from . import maintenance_request +from . import maintenance_equipment diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_label.py b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_label.py new file mode 100644 index 00000000..219331ac --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_label.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import fields, models + + +class FpMaintenanceLabel(models.Model): + """Simple tag model for equipment labels.""" + _name = 'fp.maintenance.label' + _description = 'Fusion Plating — Equipment Label' + _order = 'name' + + name = fields.Char(string='Name', required=True) + color = fields.Integer(string='Colour') + + _sql_constraints = [ + ('name_uniq', 'unique(name)', 'Label name must be unique.'), + ] diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_node.py b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_node.py new file mode 100644 index 00000000..7004ccd0 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_node.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import api, fields, models + + +class FpMaintenanceNode(models.Model): + """Maintenance checklist item. + + Individual task or check within a maintenance plan. + Auto-numbered on creation. + """ + _name = 'fp.maintenance.node' + _description = 'Fusion Plating — Maintenance Node' + _order = 'number desc' + + name = fields.Char( + string='Name', + required=True, + ) + number = fields.Integer( + string='Number', + readonly=True, + copy=False, + ) + plan_id = fields.Many2one( + 'fp.maintenance.plan', + string='Plan', + ondelete='set null', + ) + active = fields.Boolean(default=True) + company_id = fields.Many2one( + 'res.company', + string='Company', + default=lambda self: self.env.company, + ) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get('number'): + last = self.sudo().search([], order='number desc', limit=1) + vals['number'] = (last.number if last else 0) + 1 + return super().create(vals_list) diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_plan.py b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_plan.py new file mode 100644 index 00000000..50710f98 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/fp_maintenance_plan.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import api, fields, models + + +class FpMaintenancePlan(models.Model): + """Maintenance plan template. + + Groups checklist nodes and links to an equipment category. + Plans are selected when creating maintenance events. + """ + _name = 'fp.maintenance.plan' + _description = 'Fusion Plating — Maintenance Plan' + _inherit = ['mail.thread'] + _order = 'name' + + name = fields.Char( + string='Name', + required=True, + tracking=True, + help='e.g. "Tank A-10 Nickel Nichem HP 1170 - Daily Titration"', + ) + equipment_category_id = fields.Many2one( + 'maintenance.equipment.category', + string='Equipment Type', + ondelete='set null', + tracking=True, + ) + description = fields.Html(string='Description') + default_assignee_id = fields.Many2one( + 'res.users', + string='Default Assignee', + ) + node_ids = fields.One2many( + 'fp.maintenance.node', + 'plan_id', + string='Checklist Items', + ) + node_count = fields.Integer( + string='Items', + compute='_compute_node_count', + ) + active = fields.Boolean(default=True) + company_id = fields.Many2one( + 'res.company', + string='Company', + default=lambda self: self.env.company, + ) + + def _compute_node_count(self): + for plan in self: + plan.node_count = len(plan.node_ids) + + def action_view_nodes(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': f'Items — {self.name}', + 'res_model': 'fp.maintenance.node', + 'view_mode': 'list,form', + 'domain': [('plan_id', '=', self.id)], + 'context': {'default_plan_id': self.id}, + } diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_equipment.py b/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_equipment.py new file mode 100644 index 00000000..54c8a80c --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_equipment.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +from odoo import fields, models + + +class MaintenanceEquipment(models.Model): + """Extend standard maintenance.equipment with plating links.""" + _inherit = 'maintenance.equipment' + + x_fc_tank_id = fields.Many2one( + 'fusion.plating.tank', + string='Plating Tank', + help='Link this equipment to a Fusion Plating tank.', + ) + x_fc_facility_id = fields.Many2one( + 'fusion.plating.facility', + string='Facility', + ) + x_fc_location_name = fields.Char( + string='Sub-Location', + help='e.g. "PLANT1.BoilerRoom", "PLANT1.TankLine"', + ) + x_fc_label_ids = fields.Many2many( + 'fp.maintenance.label', + 'fp_maintenance_equipment_label_rel', + 'equipment_id', + 'label_id', + string='Labels', + ) diff --git a/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_request.py b/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_request.py new file mode 100644 index 00000000..a7bf59e2 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/models/maintenance_request.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +import logging +from datetime import timedelta + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class MaintenanceRequest(models.Model): + """Extend standard maintenance.request with plating-specific fields.""" + _inherit = 'maintenance.request' + + x_fc_plan_id = fields.Many2one( + 'fp.maintenance.plan', + string='Plan', + ) + x_fc_node_id = fields.Many2one( + 'fp.maintenance.node', + string='Checklist Item', + ) + x_fc_labour_cost = fields.Monetary( + string='Labour Cost', + currency_field='x_fc_currency_id', + ) + x_fc_currency_id = fields.Many2one( + 'res.currency', + string='Currency', + default=lambda self: self.env.company.currency_id, + ) + x_fc_completed_at = fields.Datetime( + string='Completed At', + readonly=True, + ) + x_fc_from_last = fields.Boolean( + string='From Last Maintenance', + help='When checked, the next recurrence is scheduled relative to ' + 'completion date instead of a fixed calendar interval.', + ) + x_fc_recurrence_days = fields.Integer( + string='Recurrence Days', + help='Number of days after completion to schedule the next event ' + '(only used with "From Last Maintenance").', + ) + + def write(self, vals): + res = super().write(vals) + if 'stage_id' in vals: + for request in self: + if request.stage_id.done and not request.x_fc_completed_at: + request.x_fc_completed_at = fields.Datetime.now() + self._maybe_schedule_from_last(request) + elif not request.stage_id.done: + request.x_fc_completed_at = False + return res + + def _maybe_schedule_from_last(self, request): + """Schedule next maintenance from completion date.""" + if not request.x_fc_from_last or not request.x_fc_recurrence_days: + return + next_date = fields.Datetime.now() + timedelta( + days=request.x_fc_recurrence_days, + ) + request.copy({ + 'schedule_date': next_date, + 'x_fc_completed_at': False, + 'stage_id': self.env['maintenance.stage'].search( + [('done', '=', False)], order='sequence', limit=1, + ).id, + }) + _logger.info( + 'Scheduled next from-last maintenance for %s on %s', + request.name, next_date, + ) diff --git a/fusion-plating/fusion_plating_bridge_maintenance/security/fp_maintenance_security.xml b/fusion-plating/fusion_plating_bridge_maintenance/security/fp_maintenance_security.xml new file mode 100644 index 00000000..9c626d68 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/security/fp_maintenance_security.xml @@ -0,0 +1,20 @@ + + + + + + Maintenance Plan: multi-company + + + [('company_id', 'in', company_ids + [False])] + + + + Maintenance Node: multi-company + + + [('company_id', 'in', company_ids + [False])] + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/security/ir.model.access.csv b/fusion-plating/fusion_plating_bridge_maintenance/security/ir.model.access.csv new file mode 100644 index 00000000..b7379272 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/security/ir.model.access.csv @@ -0,0 +1,10 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fp_maintenance_plan_operator,fp.maintenance.plan.operator,model_fp_maintenance_plan,fusion_plating.group_fusion_plating_operator,1,0,0,0 +access_fp_maintenance_plan_supervisor,fp.maintenance.plan.supervisor,model_fp_maintenance_plan,fusion_plating.group_fusion_plating_supervisor,1,1,1,0 +access_fp_maintenance_plan_manager,fp.maintenance.plan.manager,model_fp_maintenance_plan,fusion_plating.group_fusion_plating_manager,1,1,1,1 +access_fp_maintenance_node_operator,fp.maintenance.node.operator,model_fp_maintenance_node,fusion_plating.group_fusion_plating_operator,1,0,0,0 +access_fp_maintenance_node_supervisor,fp.maintenance.node.supervisor,model_fp_maintenance_node,fusion_plating.group_fusion_plating_supervisor,1,1,1,0 +access_fp_maintenance_node_manager,fp.maintenance.node.manager,model_fp_maintenance_node,fusion_plating.group_fusion_plating_manager,1,1,1,1 +access_fp_maintenance_label_operator,fp.maintenance.label.operator,model_fp_maintenance_label,fusion_plating.group_fusion_plating_operator,1,0,0,0 +access_fp_maintenance_label_supervisor,fp.maintenance.label.supervisor,model_fp_maintenance_label,fusion_plating.group_fusion_plating_supervisor,1,1,1,0 +access_fp_maintenance_label_manager,fp.maintenance.label.manager,model_fp_maintenance_label,fusion_plating.group_fusion_plating_manager,1,1,1,1 diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_dashboard_views.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_dashboard_views.xml new file mode 100644 index 00000000..8029709a --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_dashboard_views.xml @@ -0,0 +1,41 @@ + + + + + + Active Events + maintenance.request + list,kanban,form,calendar + [('archive', '=', False), ('stage_id.done', '=', False)] + {'search_default_group_stage': 1} + + + + + Completed Events + maintenance.request + list,form + [('stage_id.done', '=', True)] + {} + + + + + All Events + maintenance.request + list,kanban,form,calendar + +

+ Create a maintenance event +

+
+
+ + + + Equipment + maintenance.equipment + list,kanban,form + + +
diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_menu.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_menu.xml new file mode 100644 index 00000000..0071982f --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_menu.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_node_views.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_node_views.xml new file mode 100644 index 00000000..cd67f828 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_node_views.xml @@ -0,0 +1,51 @@ + + + + + + fp.maintenance.node.list + fp.maintenance.node + + + + + + + + + + + + fp.maintenance.node.form + fp.maintenance.node + +
+ + + + + + + + + + + +
+
+
+ + + + Checklist Items + fp.maintenance.node + list,form + +

+ Create a checklist item +

+

Checklist items are individual tasks within a maintenance plan.

+
+
+ +
diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_plan_views.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_plan_views.xml new file mode 100644 index 00000000..6c98dcc7 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/fp_maintenance_plan_views.xml @@ -0,0 +1,96 @@ + + + + + + fp.maintenance.plan.form + fp.maintenance.plan + +
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + fp.maintenance.plan.list + fp.maintenance.plan + + + + + + + + + + + + + fp.maintenance.plan.search + fp.maintenance.plan + + + + + + + + + + + + + + Maintenance Plans + fp.maintenance.plan + list,form + + +

+ Create a maintenance plan +

+

Plans are templates for recurring maintenance tasks linked to equipment types.

+
+
+ +
diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_equipment_views.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_equipment_views.xml new file mode 100644 index 00000000..ead3cbb7 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_equipment_views.xml @@ -0,0 +1,23 @@ + + + + + + maintenance.equipment.form.fp.bridge + maintenance.equipment + + + + + + + + + + + + + + diff --git a/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_request_views.xml b/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_request_views.xml new file mode 100644 index 00000000..96eaaef5 --- /dev/null +++ b/fusion-plating/fusion_plating_bridge_maintenance/views/maintenance_request_views.xml @@ -0,0 +1,49 @@ + + + + + + maintenance.request.form.fp.bridge + maintenance.request + + + + + + + + + + + + + + + + + + + + + + maintenance.request.list.fp.bridge + maintenance.request + + + + + + + + + + + + + + + + + + diff --git a/fusion-plating/fusion_plating_bridge_mrp/__manifest__.py b/fusion-plating/fusion_plating_bridge_mrp/__manifest__.py index 8a0d6446..718cdf50 100644 --- a/fusion-plating/fusion_plating_bridge_mrp/__manifest__.py +++ b/fusion-plating/fusion_plating_bridge_mrp/__manifest__.py @@ -53,6 +53,7 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved. 'views/mrp_production_views.xml', 'views/fp_quality_hold_views.xml', 'views/fp_batch_views.xml', + 'views/fp_workorder_priority_views.xml', ], 'installable': True, 'application': False, diff --git a/fusion-plating/fusion_plating_shopfloor/views/fp_workorder_priority_views.xml b/fusion-plating/fusion_plating_bridge_mrp/views/fp_workorder_priority_views.xml similarity index 92% rename from fusion-plating/fusion_plating_shopfloor/views/fp_workorder_priority_views.xml rename to fusion-plating/fusion_plating_bridge_mrp/views/fp_workorder_priority_views.xml index 0ca6a0ac..883f32b6 100644 --- a/fusion-plating/fusion_plating_shopfloor/views/fp_workorder_priority_views.xml +++ b/fusion-plating/fusion_plating_bridge_mrp/views/fp_workorder_priority_views.xml @@ -91,4 +91,12 @@ (0, 0, {'view_mode': 'list', 'view_id': ref('view_mrp_workorder_fp_list')})]"/> + + + diff --git a/fusion-plating/fusion_plating_certificates/views/fp_certificates_menu.xml b/fusion-plating/fusion_plating_certificates/views/fp_certificates_menu.xml index 0aed7576..77023bc8 100644 --- a/fusion-plating/fusion_plating_certificates/views/fp_certificates_menu.xml +++ b/fusion-plating/fusion_plating_certificates/views/fp_certificates_menu.xml @@ -20,7 +20,7 @@ - + diff --git a/fusion-plating/fusion_plating_configurator/__manifest__.py b/fusion-plating/fusion_plating_configurator/__manifest__.py index 05123a79..ff5ffc94 100644 --- a/fusion-plating/fusion_plating_configurator/__manifest__.py +++ b/fusion-plating/fusion_plating_configurator/__manifest__.py @@ -46,15 +46,13 @@ Provides: 'views/sale_order_views.xml', 'views/fp_configurator_menu.xml', ], - # 3D viewer assets temporarily disabled — causes 'registry already declared' - # error in Odoo 19 asset bundler. Needs investigation. - # 'assets': { - # 'web.assets_backend': [ - # 'fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss', - # 'fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml', - # 'fusion_plating_configurator/static/src/js/fp_3d_viewer.js', - # ], - # }, + 'assets': { + 'web.assets_backend': [ + 'fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss', + 'fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml', + 'fusion_plating_configurator/static/src/js/fp_3d_viewer.js', + ], + }, 'installable': True, 'application': False, 'auto_install': False, diff --git a/fusion-plating/fusion_plating_configurator/controllers/configurator_controller.py b/fusion-plating/fusion_plating_configurator/controllers/configurator_controller.py index f5044de0..c21306e3 100644 --- a/fusion-plating/fusion_plating_configurator/controllers/configurator_controller.py +++ b/fusion-plating/fusion_plating_configurator/controllers/configurator_controller.py @@ -15,6 +15,55 @@ _logger = logging.getLogger(__name__) class FpConfiguratorController(http.Controller): + @http.route('/fp/3d-viewer', type='http', auth='user', website=False) + def viewer_3d(self, **kw): + """Serve the standalone 3D viewer HTML page. + + Query params: id (attachment ID), name (filename for format detection). + The HTML page loads Online3DViewer and renders the model. + """ + from odoo.modules.module import get_module_path + import os + mod_path = get_module_path('fusion_plating_configurator') + html_path = os.path.join( + mod_path, 'static', 'src', 'html', '3d_viewer.html', + ) + with open(html_path, 'r', encoding='utf-8') as f: + content = f.read() + return request.make_response(content, headers=[ + ('Content-Type', 'text/html; charset=utf-8'), + ]) + + @http.route('/fp/3d-model//', + type='http', auth='user', website=False) + def serve_3d_model(self, attachment_id, filename, **kw): + """Serve a 3D model file from ir.attachment. + + This bypasses the /web/content auth issues when loading inside + an iframe. The filename in the URL ensures Online3DViewer can + detect the format from the extension. + """ + attachment = request.env['ir.attachment'].browse(attachment_id) + if not attachment.exists(): + return request.not_found() + raw = base64.b64decode(attachment.datas) + # Map common CAD extensions to MIME types + mime_map = { + '.step': 'application/step', '.stp': 'application/step', + '.iges': 'application/iges', '.igs': 'application/iges', + '.stl': 'application/sla', + '.brep': 'application/octet-stream', '.brp': 'application/octet-stream', + '.obj': 'text/plain', '.gltf': 'model/gltf+json', '.glb': 'model/gltf-binary', + } + import os + ext = os.path.splitext(filename)[1].lower() + content_type = mime_map.get(ext, 'application/octet-stream') + return request.make_response(raw, headers=[ + ('Content-Type', content_type), + ('Content-Disposition', f'inline; filename="{filename}"'), + ('Content-Length', str(len(raw))), + ]) + @http.route('/fp/configurator/calculate_surface_area', type='jsonrpc', auth='user') def calculate_surface_area(self, attachment_id, **kw): """Calculate surface area from an uploaded STL file using trimesh.""" diff --git a/fusion-plating/fusion_plating_configurator/models/fp_part_catalog.py b/fusion-plating/fusion_plating_configurator/models/fp_part_catalog.py index f4e89a27..4a50a3d0 100644 --- a/fusion-plating/fusion_plating_configurator/models/fp_part_catalog.py +++ b/fusion-plating/fusion_plating_configurator/models/fp_part_catalog.py @@ -59,43 +59,190 @@ class FpPartCatalog(models.Model): notes = fields.Html(string='Notes') active = fields.Boolean(string='Active', default=True) + sale_order_count = fields.Integer( + string='Sale Orders', compute='_compute_sale_order_count', + ) + configurator_count = fields.Integer( + string='Quotes', compute='_compute_configurator_count', + ) + _sql_constraints = [ ('fp_part_catalog_partner_partnum_uniq', 'unique(partner_id, part_number)', 'Part number must be unique per customer.'), ] + def _compute_sale_order_count(self): + for part in self: + part.sale_order_count = self.env['sale.order'].search_count( + [('x_fc_part_catalog_id', '=', part.id)]) + + def _compute_configurator_count(self): + for part in self: + part.configurator_count = self.env['fp.quote.configurator'].search_count( + [('part_catalog_id', '=', part.id)]) + + def action_view_sale_orders(self): + self.ensure_one() + orders = self.env['sale.order'].search([('x_fc_part_catalog_id', '=', self.id)]) + if len(orders) == 1: + return { + 'type': 'ir.actions.act_window', + 'res_model': 'sale.order', + 'res_id': orders.id, + 'view_mode': 'form', + 'target': 'current', + } + return { + 'type': 'ir.actions.act_window', + 'name': _('Sale Orders'), + 'res_model': 'sale.order', + 'view_mode': 'list,form', + 'domain': [('x_fc_part_catalog_id', '=', self.id)], + 'target': 'current', + } + + def action_view_configurators(self): + self.ensure_one() + cfgs = self.env['fp.quote.configurator'].search([('part_catalog_id', '=', self.id)]) + if len(cfgs) == 1: + return { + 'type': 'ir.actions.act_window', + 'res_model': 'fp.quote.configurator', + 'res_id': cfgs.id, + 'view_mode': 'form', + 'target': 'current', + } + return { + 'type': 'ir.actions.act_window', + 'name': _('Configurator Quotes'), + 'res_model': 'fp.quote.configurator', + 'view_mode': 'list,form', + 'domain': [('part_catalog_id', '=', self.id)], + 'target': 'current', + } + + @api.onchange('model_attachment_id') + def _onchange_model_attachment_id(self): + """Auto-calculate surface area when a 3D model is attached.""" + if self.model_attachment_id: + self._compute_surface_area_from_model() + def action_calculate_surface_area(self): - """Calculate surface area from the uploaded 3D model file.""" + """Button: calculate surface area from the uploaded 3D model file.""" self.ensure_one() if not self.model_attachment_id: from odoo.exceptions import UserError raise UserError(_('No 3D model file uploaded.')) - - try: - import trimesh - except ImportError: + result = self._compute_surface_area_from_model() + if result.get('error'): from odoo.exceptions import UserError - raise UserError(_('trimesh library not installed on the server. Contact your administrator.')) - - import base64 - import io - - raw = base64.b64decode(self.model_attachment_id.datas) - mesh = trimesh.load(io.BytesIO(raw), file_type='stl') - area_mm2 = mesh.area - area_sqin = area_mm2 / 645.16 - - self.surface_area = round(area_sqin, 4) - self.surface_area_uom = 'sq_in' - self.geometry_source = '3d_model' - + raise UserError(result['error']) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Surface Area Calculated'), - 'message': _('%.4f sq in (%.2f mm\u00b2) from %d faces') % (area_sqin, area_mm2, len(mesh.faces)), + 'message': result.get('message', 'Done'), 'type': 'success', 'sticky': False, }, } + + def _compute_surface_area_from_model(self): + """Calculate surface area from the 3D model attachment. + + Uses OCC (OpenCASCADE) for STEP/IGES/BREP files (exact B-Rep area). + Falls back to trimesh for STL files (mesh-based area). + Returns dict with result or error. + """ + self.ensure_one() + if not self.model_attachment_id: + return {'error': 'No 3D model file attached.'} + + import base64 + import tempfile + import os + import logging + _logger = logging.getLogger(__name__) + + raw = base64.b64decode(self.model_attachment_id.datas) + fname = (self.model_attachment_id.name or '').lower() + ext = os.path.splitext(fname)[1] + + area_mm2 = 0.0 + volume_mm3 = 0.0 + bbox_dims = None + method = 'unknown' + + if ext in ('.step', '.stp', '.iges', '.igs', '.brep', '.brp'): + # OCC (OpenCASCADE) for CAD formats -- exact B-Rep area + try: + from OCP.STEPControl import STEPControl_Reader + from OCP.IGESControl import IGESControl_Reader + from OCP.GProp import GProp_GProps + from OCP.BRepGProp import BRepGProp + from OCP.Bnd import Bnd_Box + from OCP.BRepBndLib import BRepBndLib + + with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp: + tmp.write(raw) + tmp_path = tmp.name + + try: + if ext in ('.step', '.stp'): + reader = STEPControl_Reader() + else: + reader = IGESControl_Reader() + reader.ReadFile(tmp_path) + reader.TransferRoots() + shape = reader.OneShape() + + props = GProp_GProps() + BRepGProp.SurfaceProperties_s(shape, props) + area_mm2 = props.Mass() + + vol_props = GProp_GProps() + BRepGProp.VolumeProperties_s(shape, vol_props) + volume_mm3 = vol_props.Mass() + + bbox = Bnd_Box() + BRepBndLib.Add_s(shape, bbox) + xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() + bbox_dims = (xmax - xmin, ymax - ymin, zmax - zmin) + method = 'occ_brep' + finally: + os.unlink(tmp_path) + + except ImportError: + return {'error': 'OCC (cadquery) not installed. Cannot process STEP/IGES files.'} + except Exception as e: + _logger.warning('OCC surface area calculation failed: %s', e) + return {'error': f'OCC error: {e}'} + + elif ext == '.stl': + # trimesh for STL files + try: + import trimesh + import io + mesh = trimesh.load(io.BytesIO(raw), file_type='stl') + area_mm2 = mesh.area + volume_mm3 = mesh.volume + bbox_dims = tuple(float(x) for x in mesh.bounding_box.extents) + method = 'trimesh_mesh' + except ImportError: + return {'error': 'trimesh not installed. Cannot process STL files.'} + except Exception as e: + _logger.warning('trimesh surface area calculation failed: %s', e) + return {'error': f'trimesh error: {e}'} + else: + return {'error': f'Unsupported file format: {ext}'} + + area_sqin = area_mm2 / 645.16 + self.surface_area = round(area_sqin, 4) + self.surface_area_uom = 'sq_in' + self.geometry_source = '3d_model' + + msg = '%.4f sq in (%.2f mm\u00b2) via %s' % (area_sqin, area_mm2, method) + _logger.info('Part %s: surface area = %s', self.name, msg) + return {'message': msg, 'area_sqin': area_sqin, 'area_mm2': area_mm2, + 'volume_mm3': volume_mm3, 'bbox': bbox_dims} diff --git a/fusion-plating/fusion_plating_configurator/models/fp_quote_configurator.py b/fusion-plating/fusion_plating_configurator/models/fp_quote_configurator.py index 1a8ee8ab..05409650 100644 --- a/fusion-plating/fusion_plating_configurator/models/fp_quote_configurator.py +++ b/fusion-plating/fusion_plating_configurator/models/fp_quote_configurator.py @@ -35,6 +35,25 @@ class FpQuoteConfigurator(models.Model): domain="[('partner_id', '=', partner_id)]", help="Select from this customer's part catalog, or leave blank for a one-off.", ) + model_attachment_id = fields.Many2one( + related='part_catalog_id.model_attachment_id', + string='3D Model', + readonly=True, + ) + # -- Quick file upload (creates/updates part catalog automatically) -- + upload_3d_file = fields.Binary( + string='Upload 3D File', + attachment=False, + help='Upload a STEP, IGES, or STL file. Auto-creates or updates the part catalog entry.', + ) + upload_3d_filename = fields.Char(string='3D Filename') + upload_drawing = fields.Binary( + string='Upload Drawing', + attachment=False, + help='Upload a PDF drawing. Attaches to the part catalog entry.', + ) + upload_drawing_filename = fields.Char(string='Drawing Filename') + coating_config_id = fields.Many2one( 'fp.coating.config', string='Coating Configuration', required=True, ) @@ -350,5 +369,126 @@ class FpQuoteConfigurator(models.Model): 'target': 'current', } + @api.onchange('upload_3d_file') + def _onchange_upload_3d_file(self): + """When a 3D file is uploaded, auto-create/update part catalog entry.""" + if not self.upload_3d_file or not self.partner_id: + return + import base64 + import os + + fname = self.upload_3d_filename or 'model.step' + raw = base64.b64decode(self.upload_3d_file) + + # Create attachment + att = self.env['ir.attachment'].create({ + 'name': fname, + 'datas': self.upload_3d_file, + 'mimetype': 'application/octet-stream', + }) + + # Auto-create or update part catalog + part_name = os.path.splitext(fname)[0].replace('_', ' ').replace('-', ' ').title() + if self.part_catalog_id: + # Update existing part + self.part_catalog_id.model_attachment_id = att.id + self.part_catalog_id._compute_surface_area_from_model() + self.surface_area = self.part_catalog_id.surface_area + self.surface_area_uom = self.part_catalog_id.surface_area_uom + else: + # Create new part catalog entry + part = self.env['fp.part.catalog'].create({ + 'name': part_name, + 'partner_id': self.partner_id.id, + 'part_number': fname, + 'model_attachment_id': att.id, + }) + self.part_catalog_id = part.id + # Calculate surface area + part._compute_surface_area_from_model() + self.surface_area = part.surface_area + self.surface_area_uom = part.surface_area_uom + + # Clear the upload field (data is now on the part catalog) + self.upload_3d_file = False + self.upload_3d_filename = False + + @api.onchange('upload_drawing') + def _onchange_upload_drawing(self): + """When a drawing is uploaded, attach to part catalog entry.""" + if not self.upload_drawing or not self.partner_id: + return + + fname = self.upload_drawing_filename or 'drawing.pdf' + + att = self.env['ir.attachment'].create({ + 'name': fname, + 'datas': self.upload_drawing, + 'mimetype': 'application/pdf', + }) + + if self.part_catalog_id: + self.part_catalog_id.drawing_attachment_ids = [(4, att.id)] + else: + import os + part_name = os.path.splitext(fname)[0].replace('_', ' ').replace('-', ' ').title() + part = self.env['fp.part.catalog'].create({ + 'name': part_name, + 'partner_id': self.partner_id.id, + 'part_number': fname, + 'drawing_attachment_ids': [(4, att.id)], + }) + self.part_catalog_id = part.id + + self.upload_drawing = False + self.upload_drawing_filename = False + + def action_recalculate_price(self): + """Recalculate surface area from 3D model and recompute price.""" + self.ensure_one() + # Recalculate surface area from part catalog's 3D model + if self.part_catalog_id and self.part_catalog_id.model_attachment_id: + result = self.part_catalog_id._compute_surface_area_from_model() + if not result.get('error'): + self.surface_area = self.part_catalog_id.surface_area + self.surface_area_uom = self.part_catalog_id.surface_area_uom + # Price recomputes automatically via _compute_price dependency + def action_cancel(self): self.write({'state': 'cancelled'}) + + def action_reset_draft(self): + self.write({'state': 'draft'}) + + def action_open_3d_fullscreen(self): + """Open the 3D model viewer in a new browser tab (full screen).""" + self.ensure_one() + att = self.model_attachment_id + if not att: + return + url = f'/fp/3d-viewer?id={att.id}&name={att.name}' + return { + 'type': 'ir.actions.act_url', + 'url': url, + 'target': 'new', + } + + def action_view_sale_order(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'sale.order', + 'res_id': self.sale_order_id.id, + 'view_mode': 'form', + 'target': 'current', + } + + def action_view_part_catalog(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'fp.part.catalog', + 'res_id': self.part_catalog_id.id, + 'view_mode': 'form', + 'target': 'current', + } diff --git a/fusion-plating/fusion_plating_configurator/static/lib/OrbitControls.js b/fusion-plating/fusion_plating_configurator/static/lib/OrbitControls.js deleted file mode 100644 index 7360aab9..00000000 --- a/fusion-plating/fusion_plating_configurator/static/lib/OrbitControls.js +++ /dev/null @@ -1,1523 +0,0 @@ -import { - Controls, - MOUSE, - Quaternion, - Spherical, - TOUCH, - Vector2, - Vector3, - Plane, - Ray, - MathUtils -} from 'three'; - -// OrbitControls performs orbiting, dollying (zooming), and panning. -// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). -// -// Orbit - left mouse / touch: one-finger move -// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish -// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move - -const _changeEvent = { type: 'change' }; -const _startEvent = { type: 'start' }; -const _endEvent = { type: 'end' }; -const _ray = new Ray(); -const _plane = new Plane(); -const _TILT_LIMIT = Math.cos( 70 * MathUtils.DEG2RAD ); - -const _v = new Vector3(); -const _twoPI = 2 * Math.PI; - -const _STATE = { - NONE: - 1, - ROTATE: 0, - DOLLY: 1, - PAN: 2, - TOUCH_ROTATE: 3, - TOUCH_PAN: 4, - TOUCH_DOLLY_PAN: 5, - TOUCH_DOLLY_ROTATE: 6 -}; -const _EPS = 0.000001; - -class OrbitControls extends Controls { - - constructor( object, domElement = null ) { - - super( object, domElement ); - - this.state = _STATE.NONE; - - // Set to false to disable this control - this.enabled = true; - - // "target" sets the location of focus, where the object orbits around - this.target = new Vector3(); - - // Sets the 3D cursor (similar to Blender), from which the maxTargetRadius takes effect - this.cursor = new Vector3(); - - // How far you can dolly in and out ( PerspectiveCamera only ) - this.minDistance = 0; - this.maxDistance = Infinity; - - // How far you can zoom in and out ( OrthographicCamera only ) - this.minZoom = 0; - this.maxZoom = Infinity; - - // Limit camera target within a spherical area around the cursor - this.minTargetRadius = 0; - this.maxTargetRadius = Infinity; - - // How far you can orbit vertically, upper and lower limits. - // Range is 0 to Math.PI radians. - this.minPolarAngle = 0; // radians - this.maxPolarAngle = Math.PI; // radians - - // How far you can orbit horizontally, upper and lower limits. - // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) - this.minAzimuthAngle = - Infinity; // radians - this.maxAzimuthAngle = Infinity; // radians - - // Set to true to enable damping (inertia) - // If damping is enabled, you must call controls.update() in your animation loop - this.enableDamping = false; - this.dampingFactor = 0.05; - - // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. - // Set to false to disable zooming - this.enableZoom = true; - this.zoomSpeed = 1.0; - - // Set to false to disable rotating - this.enableRotate = true; - this.rotateSpeed = 1.0; - - // Set to false to disable panning - this.enablePan = true; - this.panSpeed = 1.0; - this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up - this.keyPanSpeed = 7.0; // pixels moved per arrow key push - this.zoomToCursor = false; - - // Set to true to automatically rotate around the target - // If auto-rotate is enabled, you must call controls.update() in your animation loop - this.autoRotate = false; - this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 - - // The four arrow keys - this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; - - // Mouse buttons - this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; - - // Touch fingers - this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; - - // for reset - this.target0 = this.target.clone(); - this.position0 = this.object.position.clone(); - this.zoom0 = this.object.zoom; - - // the target DOM element for key events - this._domElementKeyEvents = null; - - // internals - - this._lastPosition = new Vector3(); - this._lastQuaternion = new Quaternion(); - this._lastTargetPosition = new Vector3(); - - // so camera.up is the orbit axis - this._quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) ); - this._quatInverse = this._quat.clone().invert(); - - // current position in spherical coordinates - this._spherical = new Spherical(); - this._sphericalDelta = new Spherical(); - - this._scale = 1; - this._panOffset = new Vector3(); - - this._rotateStart = new Vector2(); - this._rotateEnd = new Vector2(); - this._rotateDelta = new Vector2(); - - this._panStart = new Vector2(); - this._panEnd = new Vector2(); - this._panDelta = new Vector2(); - - this._dollyStart = new Vector2(); - this._dollyEnd = new Vector2(); - this._dollyDelta = new Vector2(); - - this._dollyDirection = new Vector3(); - this._mouse = new Vector2(); - this._performCursorZoom = false; - - this._pointers = []; - this._pointerPositions = {}; - - this._controlActive = false; - - // event listeners - - this._onPointerMove = onPointerMove.bind( this ); - this._onPointerDown = onPointerDown.bind( this ); - this._onPointerUp = onPointerUp.bind( this ); - this._onContextMenu = onContextMenu.bind( this ); - this._onMouseWheel = onMouseWheel.bind( this ); - this._onKeyDown = onKeyDown.bind( this ); - - this._onTouchStart = onTouchStart.bind( this ); - this._onTouchMove = onTouchMove.bind( this ); - - this._onMouseDown = onMouseDown.bind( this ); - this._onMouseMove = onMouseMove.bind( this ); - - this._interceptControlDown = interceptControlDown.bind( this ); - this._interceptControlUp = interceptControlUp.bind( this ); - - // - - if ( this.domElement !== null ) { - - this.connect(); - - } - - this.update(); - - } - - connect() { - - this.domElement.addEventListener( 'pointerdown', this._onPointerDown ); - this.domElement.addEventListener( 'pointercancel', this._onPointerUp ); - - this.domElement.addEventListener( 'contextmenu', this._onContextMenu ); - this.domElement.addEventListener( 'wheel', this._onMouseWheel, { passive: false } ); - - const document = this.domElement.getRootNode(); // offscreen canvas compatibility - document.addEventListener( 'keydown', this._interceptControlDown, { passive: true, capture: true } ); - - this.domElement.style.touchAction = 'none'; // disable touch scroll - - } - - disconnect() { - - this.domElement.removeEventListener( 'pointerdown', this._onPointerDown ); - this.domElement.removeEventListener( 'pointermove', this._onPointerMove ); - this.domElement.removeEventListener( 'pointerup', this._onPointerUp ); - this.domElement.removeEventListener( 'pointercancel', this._onPointerUp ); - - this.domElement.removeEventListener( 'wheel', this._onMouseWheel ); - this.domElement.removeEventListener( 'contextmenu', this._onContextMenu ); - - this.stopListenToKeyEvents(); - - const document = this.domElement.getRootNode(); // offscreen canvas compatibility - document.removeEventListener( 'keydown', this._interceptControlDown, { capture: true } ); - - this.domElement.style.touchAction = 'auto'; - - } - - dispose() { - - this.disconnect(); - - } - - getPolarAngle() { - - return this._spherical.phi; - - } - - getAzimuthalAngle() { - - return this._spherical.theta; - - } - - getDistance() { - - return this.object.position.distanceTo( this.target ); - - } - - listenToKeyEvents( domElement ) { - - domElement.addEventListener( 'keydown', this._onKeyDown ); - this._domElementKeyEvents = domElement; - - } - - stopListenToKeyEvents() { - - if ( this._domElementKeyEvents !== null ) { - - this._domElementKeyEvents.removeEventListener( 'keydown', this._onKeyDown ); - this._domElementKeyEvents = null; - - } - - } - - saveState() { - - this.target0.copy( this.target ); - this.position0.copy( this.object.position ); - this.zoom0 = this.object.zoom; - - } - - reset() { - - this.target.copy( this.target0 ); - this.object.position.copy( this.position0 ); - this.object.zoom = this.zoom0; - - this.object.updateProjectionMatrix(); - this.dispatchEvent( _changeEvent ); - - this.update(); - - this.state = _STATE.NONE; - - } - - update( deltaTime = null ) { - - const position = this.object.position; - - _v.copy( position ).sub( this.target ); - - // rotate offset to "y-axis-is-up" space - _v.applyQuaternion( this._quat ); - - // angle from z-axis around y-axis - this._spherical.setFromVector3( _v ); - - if ( this.autoRotate && this.state === _STATE.NONE ) { - - this._rotateLeft( this._getAutoRotationAngle( deltaTime ) ); - - } - - if ( this.enableDamping ) { - - this._spherical.theta += this._sphericalDelta.theta * this.dampingFactor; - this._spherical.phi += this._sphericalDelta.phi * this.dampingFactor; - - } else { - - this._spherical.theta += this._sphericalDelta.theta; - this._spherical.phi += this._sphericalDelta.phi; - - } - - // restrict theta to be between desired limits - - let min = this.minAzimuthAngle; - let max = this.maxAzimuthAngle; - - if ( isFinite( min ) && isFinite( max ) ) { - - if ( min < - Math.PI ) min += _twoPI; else if ( min > Math.PI ) min -= _twoPI; - - if ( max < - Math.PI ) max += _twoPI; else if ( max > Math.PI ) max -= _twoPI; - - if ( min <= max ) { - - this._spherical.theta = Math.max( min, Math.min( max, this._spherical.theta ) ); - - } else { - - this._spherical.theta = ( this._spherical.theta > ( min + max ) / 2 ) ? - Math.max( min, this._spherical.theta ) : - Math.min( max, this._spherical.theta ); - - } - - } - - // restrict phi to be between desired limits - this._spherical.phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, this._spherical.phi ) ); - - this._spherical.makeSafe(); - - - // move target to panned location - - if ( this.enableDamping === true ) { - - this.target.addScaledVector( this._panOffset, this.dampingFactor ); - - } else { - - this.target.add( this._panOffset ); - - } - - // Limit the target distance from the cursor to create a sphere around the center of interest - this.target.sub( this.cursor ); - this.target.clampLength( this.minTargetRadius, this.maxTargetRadius ); - this.target.add( this.cursor ); - - let zoomChanged = false; - // adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera - // we adjust zoom later in these cases - if ( this.zoomToCursor && this._performCursorZoom || this.object.isOrthographicCamera ) { - - this._spherical.radius = this._clampDistance( this._spherical.radius ); - - } else { - - const prevRadius = this._spherical.radius; - this._spherical.radius = this._clampDistance( this._spherical.radius * this._scale ); - zoomChanged = prevRadius != this._spherical.radius; - - } - - _v.setFromSpherical( this._spherical ); - - // rotate offset back to "camera-up-vector-is-up" space - _v.applyQuaternion( this._quatInverse ); - - position.copy( this.target ).add( _v ); - - this.object.lookAt( this.target ); - - if ( this.enableDamping === true ) { - - this._sphericalDelta.theta *= ( 1 - this.dampingFactor ); - this._sphericalDelta.phi *= ( 1 - this.dampingFactor ); - - this._panOffset.multiplyScalar( 1 - this.dampingFactor ); - - } else { - - this._sphericalDelta.set( 0, 0, 0 ); - - this._panOffset.set( 0, 0, 0 ); - - } - - // adjust camera position - if ( this.zoomToCursor && this._performCursorZoom ) { - - let newRadius = null; - if ( this.object.isPerspectiveCamera ) { - - // move the camera down the pointer ray - // this method avoids floating point error - const prevRadius = _v.length(); - newRadius = this._clampDistance( prevRadius * this._scale ); - - const radiusDelta = prevRadius - newRadius; - this.object.position.addScaledVector( this._dollyDirection, radiusDelta ); - this.object.updateMatrixWorld(); - - zoomChanged = !! radiusDelta; - - } else if ( this.object.isOrthographicCamera ) { - - // adjust the ortho camera position based on zoom changes - const mouseBefore = new Vector3( this._mouse.x, this._mouse.y, 0 ); - mouseBefore.unproject( this.object ); - - const prevZoom = this.object.zoom; - this.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / this._scale ) ); - this.object.updateProjectionMatrix(); - - zoomChanged = prevZoom !== this.object.zoom; - - const mouseAfter = new Vector3( this._mouse.x, this._mouse.y, 0 ); - mouseAfter.unproject( this.object ); - - this.object.position.sub( mouseAfter ).add( mouseBefore ); - this.object.updateMatrixWorld(); - - newRadius = _v.length(); - - } else { - - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' ); - this.zoomToCursor = false; - - } - - // handle the placement of the target - if ( newRadius !== null ) { - - if ( this.screenSpacePanning ) { - - // position the orbit target in front of the new camera position - this.target.set( 0, 0, - 1 ) - .transformDirection( this.object.matrix ) - .multiplyScalar( newRadius ) - .add( this.object.position ); - - } else { - - // get the ray and translation plane to compute target - _ray.origin.copy( this.object.position ); - _ray.direction.set( 0, 0, - 1 ).transformDirection( this.object.matrix ); - - // if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid - // extremely large values - if ( Math.abs( this.object.up.dot( _ray.direction ) ) < _TILT_LIMIT ) { - - this.object.lookAt( this.target ); - - } else { - - _plane.setFromNormalAndCoplanarPoint( this.object.up, this.target ); - _ray.intersectPlane( _plane, this.target ); - - } - - } - - } - - } else if ( this.object.isOrthographicCamera ) { - - const prevZoom = this.object.zoom; - this.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / this._scale ) ); - - if ( prevZoom !== this.object.zoom ) { - - this.object.updateProjectionMatrix(); - zoomChanged = true; - - } - - } - - this._scale = 1; - this._performCursorZoom = false; - - // update condition is: - // min(camera displacement, camera rotation in radians)^2 > EPS - // using small-angle approximation cos(x/2) = 1 - x^2 / 8 - - if ( zoomChanged || - this._lastPosition.distanceToSquared( this.object.position ) > _EPS || - 8 * ( 1 - this._lastQuaternion.dot( this.object.quaternion ) ) > _EPS || - this._lastTargetPosition.distanceToSquared( this.target ) > _EPS ) { - - this.dispatchEvent( _changeEvent ); - - this._lastPosition.copy( this.object.position ); - this._lastQuaternion.copy( this.object.quaternion ); - this._lastTargetPosition.copy( this.target ); - - return true; - - } - - return false; - - } - - _getAutoRotationAngle( deltaTime ) { - - if ( deltaTime !== null ) { - - return ( _twoPI / 60 * this.autoRotateSpeed ) * deltaTime; - - } else { - - return _twoPI / 60 / 60 * this.autoRotateSpeed; - - } - - } - - _getZoomScale( delta ) { - - const normalizedDelta = Math.abs( delta * 0.01 ); - return Math.pow( 0.95, this.zoomSpeed * normalizedDelta ); - - } - - _rotateLeft( angle ) { - - this._sphericalDelta.theta -= angle; - - } - - _rotateUp( angle ) { - - this._sphericalDelta.phi -= angle; - - } - - _panLeft( distance, objectMatrix ) { - - _v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix - _v.multiplyScalar( - distance ); - - this._panOffset.add( _v ); - - } - - _panUp( distance, objectMatrix ) { - - if ( this.screenSpacePanning === true ) { - - _v.setFromMatrixColumn( objectMatrix, 1 ); - - } else { - - _v.setFromMatrixColumn( objectMatrix, 0 ); - _v.crossVectors( this.object.up, _v ); - - } - - _v.multiplyScalar( distance ); - - this._panOffset.add( _v ); - - } - - // deltaX and deltaY are in pixels; right and down are positive - _pan( deltaX, deltaY ) { - - const element = this.domElement; - - if ( this.object.isPerspectiveCamera ) { - - // perspective - const position = this.object.position; - _v.copy( position ).sub( this.target ); - let targetDistance = _v.length(); - - // half of the fov is center to top of screen - targetDistance *= Math.tan( ( this.object.fov / 2 ) * Math.PI / 180.0 ); - - // we use only clientHeight here so aspect ratio does not distort speed - this._panLeft( 2 * deltaX * targetDistance / element.clientHeight, this.object.matrix ); - this._panUp( 2 * deltaY * targetDistance / element.clientHeight, this.object.matrix ); - - } else if ( this.object.isOrthographicCamera ) { - - // orthographic - this._panLeft( deltaX * ( this.object.right - this.object.left ) / this.object.zoom / element.clientWidth, this.object.matrix ); - this._panUp( deltaY * ( this.object.top - this.object.bottom ) / this.object.zoom / element.clientHeight, this.object.matrix ); - - } else { - - // camera neither orthographic nor perspective - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); - this.enablePan = false; - - } - - } - - _dollyOut( dollyScale ) { - - if ( this.object.isPerspectiveCamera || this.object.isOrthographicCamera ) { - - this._scale /= dollyScale; - - } else { - - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - this.enableZoom = false; - - } - - } - - _dollyIn( dollyScale ) { - - if ( this.object.isPerspectiveCamera || this.object.isOrthographicCamera ) { - - this._scale *= dollyScale; - - } else { - - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - this.enableZoom = false; - - } - - } - - _updateZoomParameters( x, y ) { - - if ( ! this.zoomToCursor ) { - - return; - - } - - this._performCursorZoom = true; - - const rect = this.domElement.getBoundingClientRect(); - const dx = x - rect.left; - const dy = y - rect.top; - const w = rect.width; - const h = rect.height; - - this._mouse.x = ( dx / w ) * 2 - 1; - this._mouse.y = - ( dy / h ) * 2 + 1; - - this._dollyDirection.set( this._mouse.x, this._mouse.y, 1 ).unproject( this.object ).sub( this.object.position ).normalize(); - - } - - _clampDistance( dist ) { - - return Math.max( this.minDistance, Math.min( this.maxDistance, dist ) ); - - } - - // - // event callbacks - update the object state - // - - _handleMouseDownRotate( event ) { - - this._rotateStart.set( event.clientX, event.clientY ); - - } - - _handleMouseDownDolly( event ) { - - this._updateZoomParameters( event.clientX, event.clientX ); - this._dollyStart.set( event.clientX, event.clientY ); - - } - - _handleMouseDownPan( event ) { - - this._panStart.set( event.clientX, event.clientY ); - - } - - _handleMouseMoveRotate( event ) { - - this._rotateEnd.set( event.clientX, event.clientY ); - - this._rotateDelta.subVectors( this._rotateEnd, this._rotateStart ).multiplyScalar( this.rotateSpeed ); - - const element = this.domElement; - - this._rotateLeft( _twoPI * this._rotateDelta.x / element.clientHeight ); // yes, height - - this._rotateUp( _twoPI * this._rotateDelta.y / element.clientHeight ); - - this._rotateStart.copy( this._rotateEnd ); - - this.update(); - - } - - _handleMouseMoveDolly( event ) { - - this._dollyEnd.set( event.clientX, event.clientY ); - - this._dollyDelta.subVectors( this._dollyEnd, this._dollyStart ); - - if ( this._dollyDelta.y > 0 ) { - - this._dollyOut( this._getZoomScale( this._dollyDelta.y ) ); - - } else if ( this._dollyDelta.y < 0 ) { - - this._dollyIn( this._getZoomScale( this._dollyDelta.y ) ); - - } - - this._dollyStart.copy( this._dollyEnd ); - - this.update(); - - } - - _handleMouseMovePan( event ) { - - this._panEnd.set( event.clientX, event.clientY ); - - this._panDelta.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.panSpeed ); - - this._pan( this._panDelta.x, this._panDelta.y ); - - this._panStart.copy( this._panEnd ); - - this.update(); - - } - - _handleMouseWheel( event ) { - - this._updateZoomParameters( event.clientX, event.clientY ); - - if ( event.deltaY < 0 ) { - - this._dollyIn( this._getZoomScale( event.deltaY ) ); - - } else if ( event.deltaY > 0 ) { - - this._dollyOut( this._getZoomScale( event.deltaY ) ); - - } - - this.update(); - - } - - _handleKeyDown( event ) { - - let needsUpdate = false; - - switch ( event.code ) { - - case this.keys.UP: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - this._rotateUp( _twoPI * this.rotateSpeed / this.domElement.clientHeight ); - - } else { - - this._pan( 0, this.keyPanSpeed ); - - } - - needsUpdate = true; - break; - - case this.keys.BOTTOM: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - this._rotateUp( - _twoPI * this.rotateSpeed / this.domElement.clientHeight ); - - } else { - - this._pan( 0, - this.keyPanSpeed ); - - } - - needsUpdate = true; - break; - - case this.keys.LEFT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - this._rotateLeft( _twoPI * this.rotateSpeed / this.domElement.clientHeight ); - - } else { - - this._pan( this.keyPanSpeed, 0 ); - - } - - needsUpdate = true; - break; - - case this.keys.RIGHT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - this._rotateLeft( - _twoPI * this.rotateSpeed / this.domElement.clientHeight ); - - } else { - - this._pan( - this.keyPanSpeed, 0 ); - - } - - needsUpdate = true; - break; - - } - - if ( needsUpdate ) { - - // prevent the browser from scrolling on cursor keys - event.preventDefault(); - - this.update(); - - } - - - } - - _handleTouchStartRotate( event ) { - - if ( this._pointers.length === 1 ) { - - this._rotateStart.set( event.pageX, event.pageY ); - - } else { - - const position = this._getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - this._rotateStart.set( x, y ); - - } - - } - - _handleTouchStartPan( event ) { - - if ( this._pointers.length === 1 ) { - - this._panStart.set( event.pageX, event.pageY ); - - } else { - - const position = this._getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - this._panStart.set( x, y ); - - } - - } - - _handleTouchStartDolly( event ) { - - const position = this._getSecondPointerPosition( event ); - - const dx = event.pageX - position.x; - const dy = event.pageY - position.y; - - const distance = Math.sqrt( dx * dx + dy * dy ); - - this._dollyStart.set( 0, distance ); - - } - - _handleTouchStartDollyPan( event ) { - - if ( this.enableZoom ) this._handleTouchStartDolly( event ); - - if ( this.enablePan ) this._handleTouchStartPan( event ); - - } - - _handleTouchStartDollyRotate( event ) { - - if ( this.enableZoom ) this._handleTouchStartDolly( event ); - - if ( this.enableRotate ) this._handleTouchStartRotate( event ); - - } - - _handleTouchMoveRotate( event ) { - - if ( this._pointers.length == 1 ) { - - this._rotateEnd.set( event.pageX, event.pageY ); - - } else { - - const position = this._getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - this._rotateEnd.set( x, y ); - - } - - this._rotateDelta.subVectors( this._rotateEnd, this._rotateStart ).multiplyScalar( this.rotateSpeed ); - - const element = this.domElement; - - this._rotateLeft( _twoPI * this._rotateDelta.x / element.clientHeight ); // yes, height - - this._rotateUp( _twoPI * this._rotateDelta.y / element.clientHeight ); - - this._rotateStart.copy( this._rotateEnd ); - - } - - _handleTouchMovePan( event ) { - - if ( this._pointers.length === 1 ) { - - this._panEnd.set( event.pageX, event.pageY ); - - } else { - - const position = this._getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - this._panEnd.set( x, y ); - - } - - this._panDelta.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.panSpeed ); - - this._pan( this._panDelta.x, this._panDelta.y ); - - this._panStart.copy( this._panEnd ); - - } - - _handleTouchMoveDolly( event ) { - - const position = this._getSecondPointerPosition( event ); - - const dx = event.pageX - position.x; - const dy = event.pageY - position.y; - - const distance = Math.sqrt( dx * dx + dy * dy ); - - this._dollyEnd.set( 0, distance ); - - this._dollyDelta.set( 0, Math.pow( this._dollyEnd.y / this._dollyStart.y, this.zoomSpeed ) ); - - this._dollyOut( this._dollyDelta.y ); - - this._dollyStart.copy( this._dollyEnd ); - - const centerX = ( event.pageX + position.x ) * 0.5; - const centerY = ( event.pageY + position.y ) * 0.5; - - this._updateZoomParameters( centerX, centerY ); - - } - - _handleTouchMoveDollyPan( event ) { - - if ( this.enableZoom ) this._handleTouchMoveDolly( event ); - - if ( this.enablePan ) this._handleTouchMovePan( event ); - - } - - _handleTouchMoveDollyRotate( event ) { - - if ( this.enableZoom ) this._handleTouchMoveDolly( event ); - - if ( this.enableRotate ) this._handleTouchMoveRotate( event ); - - } - - // pointers - - _addPointer( event ) { - - this._pointers.push( event.pointerId ); - - } - - _removePointer( event ) { - - delete this._pointerPositions[ event.pointerId ]; - - for ( let i = 0; i < this._pointers.length; i ++ ) { - - if ( this._pointers[ i ] == event.pointerId ) { - - this._pointers.splice( i, 1 ); - return; - - } - - } - - } - - _isTrackingPointer( event ) { - - for ( let i = 0; i < this._pointers.length; i ++ ) { - - if ( this._pointers[ i ] == event.pointerId ) return true; - - } - - return false; - - } - - _trackPointer( event ) { - - let position = this._pointerPositions[ event.pointerId ]; - - if ( position === undefined ) { - - position = new Vector2(); - this._pointerPositions[ event.pointerId ] = position; - - } - - position.set( event.pageX, event.pageY ); - - } - - _getSecondPointerPosition( event ) { - - const pointerId = ( event.pointerId === this._pointers[ 0 ] ) ? this._pointers[ 1 ] : this._pointers[ 0 ]; - - return this._pointerPositions[ pointerId ]; - - } - - // - - _customWheelEvent( event ) { - - const mode = event.deltaMode; - - // minimal wheel event altered to meet delta-zoom demand - const newEvent = { - clientX: event.clientX, - clientY: event.clientY, - deltaY: event.deltaY, - }; - - switch ( mode ) { - - case 1: // LINE_MODE - newEvent.deltaY *= 16; - break; - - case 2: // PAGE_MODE - newEvent.deltaY *= 100; - break; - - } - - // detect if event was triggered by pinching - if ( event.ctrlKey && ! this._controlActive ) { - - newEvent.deltaY *= 10; - - } - - return newEvent; - - } - -} - -function onPointerDown( event ) { - - if ( this.enabled === false ) return; - - if ( this._pointers.length === 0 ) { - - this.domElement.setPointerCapture( event.pointerId ); - - this.domElement.addEventListener( 'pointermove', this._onPointerMove ); - this.domElement.addEventListener( 'pointerup', this._onPointerUp ); - - } - - // - - if ( this._isTrackingPointer( event ) ) return; - - // - - this._addPointer( event ); - - if ( event.pointerType === 'touch' ) { - - this._onTouchStart( event ); - - } else { - - this._onMouseDown( event ); - - } - -} - -function onPointerMove( event ) { - - if ( this.enabled === false ) return; - - if ( event.pointerType === 'touch' ) { - - this._onTouchMove( event ); - - } else { - - this._onMouseMove( event ); - - } - -} - -function onPointerUp( event ) { - - this._removePointer( event ); - - switch ( this._pointers.length ) { - - case 0: - - this.domElement.releasePointerCapture( event.pointerId ); - - this.domElement.removeEventListener( 'pointermove', this._onPointerMove ); - this.domElement.removeEventListener( 'pointerup', this._onPointerUp ); - - this.dispatchEvent( _endEvent ); - - this.state = _STATE.NONE; - - break; - - case 1: - - const pointerId = this._pointers[ 0 ]; - const position = this._pointerPositions[ pointerId ]; - - // minimal placeholder event - allows state correction on pointer-up - this._onTouchStart( { pointerId: pointerId, pageX: position.x, pageY: position.y } ); - - break; - - } - -} - -function onMouseDown( event ) { - - let mouseAction; - - switch ( event.button ) { - - case 0: - - mouseAction = this.mouseButtons.LEFT; - break; - - case 1: - - mouseAction = this.mouseButtons.MIDDLE; - break; - - case 2: - - mouseAction = this.mouseButtons.RIGHT; - break; - - default: - - mouseAction = - 1; - - } - - switch ( mouseAction ) { - - case MOUSE.DOLLY: - - if ( this.enableZoom === false ) return; - - this._handleMouseDownDolly( event ); - - this.state = _STATE.DOLLY; - - break; - - case MOUSE.ROTATE: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( this.enablePan === false ) return; - - this._handleMouseDownPan( event ); - - this.state = _STATE.PAN; - - } else { - - if ( this.enableRotate === false ) return; - - this._handleMouseDownRotate( event ); - - this.state = _STATE.ROTATE; - - } - - break; - - case MOUSE.PAN: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( this.enableRotate === false ) return; - - this._handleMouseDownRotate( event ); - - this.state = _STATE.ROTATE; - - } else { - - if ( this.enablePan === false ) return; - - this._handleMouseDownPan( event ); - - this.state = _STATE.PAN; - - } - - break; - - default: - - this.state = _STATE.NONE; - - } - - if ( this.state !== _STATE.NONE ) { - - this.dispatchEvent( _startEvent ); - - } - -} - -function onMouseMove( event ) { - - switch ( this.state ) { - - case _STATE.ROTATE: - - if ( this.enableRotate === false ) return; - - this._handleMouseMoveRotate( event ); - - break; - - case _STATE.DOLLY: - - if ( this.enableZoom === false ) return; - - this._handleMouseMoveDolly( event ); - - break; - - case _STATE.PAN: - - if ( this.enablePan === false ) return; - - this._handleMouseMovePan( event ); - - break; - - } - -} - -function onMouseWheel( event ) { - - if ( this.enabled === false || this.enableZoom === false || this.state !== _STATE.NONE ) return; - - event.preventDefault(); - - this.dispatchEvent( _startEvent ); - - this._handleMouseWheel( this._customWheelEvent( event ) ); - - this.dispatchEvent( _endEvent ); - -} - -function onKeyDown( event ) { - - if ( this.enabled === false || this.enablePan === false ) return; - - this._handleKeyDown( event ); - -} - -function onTouchStart( event ) { - - this._trackPointer( event ); - - switch ( this._pointers.length ) { - - case 1: - - switch ( this.touches.ONE ) { - - case TOUCH.ROTATE: - - if ( this.enableRotate === false ) return; - - this._handleTouchStartRotate( event ); - - this.state = _STATE.TOUCH_ROTATE; - - break; - - case TOUCH.PAN: - - if ( this.enablePan === false ) return; - - this._handleTouchStartPan( event ); - - this.state = _STATE.TOUCH_PAN; - - break; - - default: - - this.state = _STATE.NONE; - - } - - break; - - case 2: - - switch ( this.touches.TWO ) { - - case TOUCH.DOLLY_PAN: - - if ( this.enableZoom === false && this.enablePan === false ) return; - - this._handleTouchStartDollyPan( event ); - - this.state = _STATE.TOUCH_DOLLY_PAN; - - break; - - case TOUCH.DOLLY_ROTATE: - - if ( this.enableZoom === false && this.enableRotate === false ) return; - - this._handleTouchStartDollyRotate( event ); - - this.state = _STATE.TOUCH_DOLLY_ROTATE; - - break; - - default: - - this.state = _STATE.NONE; - - } - - break; - - default: - - this.state = _STATE.NONE; - - } - - if ( this.state !== _STATE.NONE ) { - - this.dispatchEvent( _startEvent ); - - } - -} - -function onTouchMove( event ) { - - this._trackPointer( event ); - - switch ( this.state ) { - - case _STATE.TOUCH_ROTATE: - - if ( this.enableRotate === false ) return; - - this._handleTouchMoveRotate( event ); - - this.update(); - - break; - - case _STATE.TOUCH_PAN: - - if ( this.enablePan === false ) return; - - this._handleTouchMovePan( event ); - - this.update(); - - break; - - case _STATE.TOUCH_DOLLY_PAN: - - if ( this.enableZoom === false && this.enablePan === false ) return; - - this._handleTouchMoveDollyPan( event ); - - this.update(); - - break; - - case _STATE.TOUCH_DOLLY_ROTATE: - - if ( this.enableZoom === false && this.enableRotate === false ) return; - - this._handleTouchMoveDollyRotate( event ); - - this.update(); - - break; - - default: - - this.state = _STATE.NONE; - - } - -} - -function onContextMenu( event ) { - - if ( this.enabled === false ) return; - - event.preventDefault(); - -} - -function interceptControlDown( event ) { - - if ( event.key === 'Control' ) { - - this._controlActive = true; - - const document = this.domElement.getRootNode(); // offscreen canvas compatibility - - document.addEventListener( 'keyup', this._interceptControlUp, { passive: true, capture: true } ); - - } - -} - -function interceptControlUp( event ) { - - if ( event.key === 'Control' ) { - - this._controlActive = false; - - const document = this.domElement.getRootNode(); // offscreen canvas compatibility - - document.removeEventListener( 'keyup', this._interceptControlUp, { passive: true, capture: true } ); - - } - -} - -export { OrbitControls }; diff --git a/fusion-plating/fusion_plating_configurator/static/lib/STLLoader.js b/fusion-plating/fusion_plating_configurator/static/lib/STLLoader.js deleted file mode 100644 index b94b8069..00000000 --- a/fusion-plating/fusion_plating_configurator/static/lib/STLLoader.js +++ /dev/null @@ -1,411 +0,0 @@ -import { - BufferAttribute, - BufferGeometry, - Color, - FileLoader, - Float32BufferAttribute, - Loader, - Vector3, - SRGBColorSpace -} from 'three'; - -/** - * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. - * - * Supports both binary and ASCII encoded files, with automatic detection of type. - * - * The loader returns a non-indexed buffer geometry. - * - * Limitations: - * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). - * There is perhaps some question as to how valid it is to always assume little-endian-ness. - * ASCII decoding assumes file is UTF-8. - * - * Usage: - * const loader = new STLLoader(); - * loader.load( './models/stl/slotted_disk.stl', function ( geometry ) { - * scene.add( new THREE.Mesh( geometry ) ); - * }); - * - * For binary STLs geometry might contain colors for vertices. To use it: - * // use the same code to load STL as above - * if (geometry.hasColors) { - * material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: true }); - * } else { .... } - * const mesh = new THREE.Mesh( geometry, material ); - * - * For ASCII STLs containing multiple solids, each solid is assigned to a different group. - * Groups can be used to assign a different color by defining an array of materials with the same length of - * geometry.groups and passing it to the Mesh constructor: - * - * const mesh = new THREE.Mesh( geometry, material ); - * - * For example: - * - * const materials = []; - * const nGeometryGroups = geometry.groups.length; - * - * const colorMap = ...; // Some logic to index colors. - * - * for (let i = 0; i < nGeometryGroups; i++) { - * - * const material = new THREE.MeshPhongMaterial({ - * color: colorMap[i], - * wireframe: false - * }); - * - * } - * - * materials.push(material); - * const mesh = new THREE.Mesh(geometry, materials); - */ - - -class STLLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( text ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - } - - parse( data ) { - - function isBinary( data ) { - - const reader = new DataView( data ); - const face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 ); - const n_faces = reader.getUint32( 80, true ); - const expect = 80 + ( 32 / 8 ) + ( n_faces * face_size ); - - if ( expect === reader.byteLength ) { - - return true; - - } - - // An ASCII STL data must begin with 'solid ' as the first six bytes. - // However, ASCII STLs lacking the SPACE after the 'd' are known to be - // plentiful. So, check the first 5 bytes for 'solid'. - - // Several encodings, such as UTF-8, precede the text with up to 5 bytes: - // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding - // Search for "solid" to start anywhere after those prefixes. - - // US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd' - - const solid = [ 115, 111, 108, 105, 100 ]; - - for ( let off = 0; off < 5; off ++ ) { - - // If "solid" text is matched to the current offset, declare it to be an ASCII STL. - - if ( matchDataViewAt( solid, reader, off ) ) return false; - - } - - // Couldn't find "solid" text at the beginning; it is binary STL. - - return true; - - } - - function matchDataViewAt( query, reader, offset ) { - - // Check if each byte in query matches the corresponding byte from the current offset - - for ( let i = 0, il = query.length; i < il; i ++ ) { - - if ( query[ i ] !== reader.getUint8( offset + i ) ) return false; - - } - - return true; - - } - - function parseBinary( data ) { - - const reader = new DataView( data ); - const faces = reader.getUint32( 80, true ); - - let r, g, b, hasColors = false, colors; - let defaultR, defaultG, defaultB, alpha; - - // process STL header - // check for default color in header ("COLOR=rgba" sequence). - - for ( let index = 0; index < 80 - 10; index ++ ) { - - if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) && - ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) && - ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) { - - hasColors = true; - colors = new Float32Array( faces * 3 * 3 ); - - defaultR = reader.getUint8( index + 6 ) / 255; - defaultG = reader.getUint8( index + 7 ) / 255; - defaultB = reader.getUint8( index + 8 ) / 255; - alpha = reader.getUint8( index + 9 ) / 255; - - } - - } - - const dataOffset = 84; - const faceLength = 12 * 4 + 2; - - const geometry = new BufferGeometry(); - - const vertices = new Float32Array( faces * 3 * 3 ); - const normals = new Float32Array( faces * 3 * 3 ); - - const color = new Color(); - - for ( let face = 0; face < faces; face ++ ) { - - const start = dataOffset + face * faceLength; - const normalX = reader.getFloat32( start, true ); - const normalY = reader.getFloat32( start + 4, true ); - const normalZ = reader.getFloat32( start + 8, true ); - - if ( hasColors ) { - - const packedColor = reader.getUint16( start + 48, true ); - - if ( ( packedColor & 0x8000 ) === 0 ) { - - // facet has its own unique color - - r = ( packedColor & 0x1F ) / 31; - g = ( ( packedColor >> 5 ) & 0x1F ) / 31; - b = ( ( packedColor >> 10 ) & 0x1F ) / 31; - - } else { - - r = defaultR; - g = defaultG; - b = defaultB; - - } - - } - - for ( let i = 1; i <= 3; i ++ ) { - - const vertexstart = start + i * 12; - const componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 ); - - vertices[ componentIdx ] = reader.getFloat32( vertexstart, true ); - vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true ); - vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true ); - - normals[ componentIdx ] = normalX; - normals[ componentIdx + 1 ] = normalY; - normals[ componentIdx + 2 ] = normalZ; - - if ( hasColors ) { - - color.setRGB( r, g, b, SRGBColorSpace ); - - colors[ componentIdx ] = color.r; - colors[ componentIdx + 1 ] = color.g; - colors[ componentIdx + 2 ] = color.b; - - } - - } - - } - - geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - - if ( hasColors ) { - - geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) ); - geometry.hasColors = true; - geometry.alpha = alpha; - - } - - return geometry; - - } - - function parseASCII( data ) { - - const geometry = new BufferGeometry(); - const patternSolid = /solid([\s\S]*?)endsolid/g; - const patternFace = /facet([\s\S]*?)endfacet/g; - const patternName = /solid\s(.+)/; - let faceCounter = 0; - - const patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source; - const patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' ); - const patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' ); - - const vertices = []; - const normals = []; - const groupNames = []; - - const normal = new Vector3(); - - let result; - - let groupCount = 0; - let startVertex = 0; - let endVertex = 0; - - while ( ( result = patternSolid.exec( data ) ) !== null ) { - - startVertex = endVertex; - - const solid = result[ 0 ]; - - const name = ( result = patternName.exec( solid ) ) !== null ? result[ 1 ] : ''; - groupNames.push( name ); - - while ( ( result = patternFace.exec( solid ) ) !== null ) { - - let vertexCountPerFace = 0; - let normalCountPerFace = 0; - - const text = result[ 0 ]; - - while ( ( result = patternNormal.exec( text ) ) !== null ) { - - normal.x = parseFloat( result[ 1 ] ); - normal.y = parseFloat( result[ 2 ] ); - normal.z = parseFloat( result[ 3 ] ); - normalCountPerFace ++; - - } - - while ( ( result = patternVertex.exec( text ) ) !== null ) { - - vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); - normals.push( normal.x, normal.y, normal.z ); - vertexCountPerFace ++; - endVertex ++; - - } - - // every face have to own ONE valid normal - - if ( normalCountPerFace !== 1 ) { - - console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter ); - - } - - // each face have to own THREE valid vertices - - if ( vertexCountPerFace !== 3 ) { - - console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter ); - - } - - faceCounter ++; - - } - - const start = startVertex; - const count = endVertex - startVertex; - - geometry.userData.groupNames = groupNames; - - geometry.addGroup( start, count, groupCount ); - groupCount ++; - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - - return geometry; - - } - - function ensureString( buffer ) { - - if ( typeof buffer !== 'string' ) { - - return new TextDecoder().decode( buffer ); - - } - - return buffer; - - } - - function ensureBinary( buffer ) { - - if ( typeof buffer === 'string' ) { - - const array_buffer = new Uint8Array( buffer.length ); - for ( let i = 0; i < buffer.length; i ++ ) { - - array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian - - } - - return array_buffer.buffer || array_buffer; - - } else { - - return buffer; - - } - - } - - // start - - const binData = ensureBinary( data ); - - return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) ); - - } - -} - -export { STLLoader }; diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion.jpg new file mode 100644 index 00000000..56df3d33 Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negx.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negx.jpg new file mode 100644 index 00000000..fdc5671d Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negx.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negy.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negy.jpg new file mode 100644 index 00000000..2e6157a7 Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negy.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negz.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negz.jpg new file mode 100644 index 00000000..687e817a Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/negz.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posx.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posx.jpg new file mode 100644 index 00000000..c2d08956 Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posx.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posy.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posy.jpg new file mode 100644 index 00000000..bea16774 Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posy.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posz.jpg b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posz.jpg new file mode 100644 index 00000000..cbe3c0df Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/envmaps/fishermans_bastion/posz.jpg differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js-worker.js b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js-worker.js new file mode 100644 index 00000000..af06c3ac --- /dev/null +++ b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js-worker.js @@ -0,0 +1,13 @@ +importScripts ('occt-import-js.js'); + +onmessage = async function (ev) +{ + let modulOverrides = { + locateFile: function (path) { + return path; + } + }; + let occt = await occtimportjs (modulOverrides); + let result = occt.ReadFile (ev.data.format, ev.data.buffer, ev.data.params); + postMessage (result); +}; diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.js b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.js new file mode 100644 index 00000000..abec0c5e --- /dev/null +++ b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.js @@ -0,0 +1,19 @@ + +var occtimportjs = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename; + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";if(runtimeInitialized){___trap()}var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="occt-import-js.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["_"];updateMemoryViews();wasmTable=wasmExports["ba"];addOnInit(wasmExports["$"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var noExitRuntime=Module["noExitRuntime"]||true;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var zeroMemory=(address,size)=>{HEAPU8.fill(0,address,address+size)};var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(ptr)zeroMemory(ptr,size);return ptr};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url).then(arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},err=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,readFiles:{},FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+24>>2]=tempI64[0],HEAP32[buf+28>>2]=tempI64[1];HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+56>>2]=tempI64[0],HEAP32[buf+60>>2]=tempI64[1];HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+72>>2]=tempI64[0],HEAP32[buf+76>>2]=tempI64[1];HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+88>>2]=tempI64[0],HEAP32[buf+92>>2]=tempI64[1];return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_chmod(path,mode){try{path=SYSCALLS.getStr(path);FS.chmod(path,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function syscallGetVarargI(){var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret}var syscallGetVarargP=syscallGetVarargI;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.lstat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.doStat(nofollow?FS.lstat:FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(flags===0){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{abort("Invalid flags passed to unlinkat")}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>{abort("")};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{};var embind_init_charCodes=()=>{var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes};var embind_charCodes;var readLatin1String=ptr=>{var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret};var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var BindingError;var throwBindingError=message=>{throw new BindingError(message)};var InternalError;var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var GenericWireTypeSize=8;var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=readLatin1String(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},argPackAdvance:GenericWireTypeSize,readValueFromPointer:function(pointer){return this["fromWireType"](HEAPU8[pointer])},destructorFunction:null})};var emval_freelist=[];var emval_handles=[];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var count_emval_handles=()=>emval_handles.length/2-5-emval_freelist.length;var init_emval=()=>{emval_handles.push(0,1,undefined,1,null,1,true,1,false,1);Module["count_emval_handles"]=count_emval_handles};var Emval={toValue:handle=>{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};function readPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 8:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=readLatin1String(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var createNamedFunction=(name,body)=>Object.defineProperty(body,"name",{value:name});var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function usesDestructorStack(argTypes){for(var i=1;i{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i>2])}return array};var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var dynCallLegacy=(sig,ptr,args)=>{sig=sig.replace(/p/g,"i");var f=Module["dynCall_"+sig];return f(ptr,...args)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var dynCall=(sig,ptr,args=[])=>{if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr)(...args);return rtn};var getDynCaller=(sig,ptr)=>(...args)=>dynCall(sig,ptr,args);var embind__requireFunction=(signature,rawFunction)=>{signature=readLatin1String(signature);function makeDynCaller(){if(signature.includes("j")){return getDynCaller(signature,rawFunction)}return getWasmTableEntry(rawFunction)}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};var extendError=(baseErrorType,errorName)=>{var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return`${this.name}: ${this.message}`}};return errorClass};var UnboundTypeError;var getTypeName=type=>{var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex!==-1){return signature.substr(0,argsIndex)}else{return signature}};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift}var isUnsignedType=name.includes("unsigned");var checkAssertions=(value,toTypeName)=>{};var toWireType;if(isUnsignedType){toWireType=function(destructors,value){checkAssertions(value,this.name);return value>>>0}}else{toWireType=function(destructors,value){checkAssertions(value,this.name);return value}}registerType(primitiveType,{name,fromWireType,toWireType,argPackAdvance:GenericWireTypeSize,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name,fromWireType:decodeMemoryView,argPackAdvance:GenericWireTypeSize,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __embind_register_std_string=(rawType,name)=>{name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){var decodeStartPtr=payload;for(var i=0;i<=length;++i){var currentBytePtr=payload+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}else{for(var i=0;i{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=readLatin1String(name);var decodeString,encodeString,readCharAt,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;readCharAt=pointer=>HEAPU16[pointer>>1]}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;readCharAt=pointer=>HEAPU32[pointer>>2]}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||readCharAt(currentBytePtr)==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_void=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{isVoid:true,name,argPackAdvance:0,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var nowIsMonotonic=1;var __emscripten_get_now_is_monotonic=()=>nowIsMonotonic;var inetPton4=str=>{var b=str.split(".");for(var i=0;i<4;i++){var tmp=Number(b[i]);if(isNaN(tmp))return null;b[i]=tmp}return(b[0]|b[1]<<8|b[2]<<16|b[3]<<24)>>>0};var jstoi_q=str=>parseInt(str);var inetPton6=str=>{var words;var w,offset,z;var valid6regx=/^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i;var parts=[];if(!valid6regx.test(str)){return null}if(str==="::"){return[0,0,0,0,0,0,0,0]}if(str.startsWith("::")){str=str.replace("::","Z:")}else{str=str.replace("::",":Z:")}if(str.indexOf(".")>0){str=str.replace(new RegExp("[.]","g"),":");words=str.split(":");words[words.length-4]=jstoi_q(words[words.length-4])+jstoi_q(words[words.length-3])*256;words[words.length-3]=jstoi_q(words[words.length-2])+jstoi_q(words[words.length-1])*256;words=words.slice(0,words.length-2)}else{words=str.split(":")}offset=0;z=0;for(w=0;w{var nameString=UTF8ToString(name);return inetPton4(DNS.lookup_name(nameString))};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var emval_returnValue=(returnType,destructorsRef,handle)=>{var destructors=[];var result=returnType["toWireType"](destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var __emval_as=(handle,returnType,destructorsRef)=>{handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");return emval_returnValue(returnType,destructorsRef,handle)};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol};var emval_methodCallers=[];var __emval_call_method=(caller,objHandle,methodName,destructorsRef,args)=>{caller=emval_methodCallers[caller];objHandle=Emval.toValue(objHandle);methodName=getStringOrSymbol(methodName);return caller(objHandle,objHandle[methodName],destructorsRef,args)};var emval_get_global=()=>{if(typeof globalThis=="object"){return globalThis}return function(){return Function}()("return this")()};var __emval_get_global=name=>{if(name===0){return Emval.toHandle(emval_get_global())}else{name=getStringOrSymbol(name);return Emval.toHandle(emval_get_global()[name])}};var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],"parameter "+i)}return a};var reflectConstruct=Reflect.construct;var __emval_get_method_caller=(argCount,argTypes,kind)=>{var types=emval_lookupTypes(argCount,argTypes);var retType=types.shift();argCount--;var functionBody=`return function (obj, func, destructorsRef, args) {\n`;var offset=0;var argsList=[];if(kind===0){argsList.push("obj")}var params=["retType"];var args=[retType];for(var i=0;it.name).join(", ")}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>9){emval_handles[handle+1]+=1}};var __emval_new_array=()=>Emval.toHandle([]);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var __emval_take_value=(type,arg)=>{type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](arg);return Emval.toHandle(v)};var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function __localtime_js(time_low,time_high,tmPtr){var time=convertI32PairToI53Checked(time_low,time_high);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function __munmap_js(addr,len,prot,flags,fd,offset_low,offset_high){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetDate.now();function jsStackTrace(){return(new Error).stack.toString()}var warnOnce=text=>{warnOnce.shown||={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;if(ENVIRONMENT_IS_NODE)text="warning: "+text;err(text)}};function getCallstack(flags){var callstack=jsStackTrace();var iThisFunc=callstack.lastIndexOf("_emscripten_log");var iThisFunc2=callstack.lastIndexOf("_emscripten_get_callstack");var iNextLine=callstack.indexOf("\n",Math.max(iThisFunc,iThisFunc2))+1;callstack=callstack.slice(iNextLine);if(flags&8&&typeof emscripten_source_map=="undefined"){warnOnce('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.');flags^=8;flags|=16}var lines=callstack.split("\n");callstack="";var newFirefoxRe=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)");var firefoxRe=new RegExp("\\s*(.*?)@(.*):(.*)(:(.*))?");var chromeRe=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var l in lines){var line=lines[l];var symbolName="";var file="";var lineno=0;var column=0;var parts=chromeRe.exec(line);if(parts&&parts.length==5){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]}else{parts=newFirefoxRe.exec(line)||firefoxRe.exec(line);if(parts&&parts.length>=4){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]|0}else{callstack+=line+"\n";continue}}var haveSourceMap=false;if(flags&8){var orig=emscripten_source_map.originalPositionFor({line:lineno,column});haveSourceMap=orig?.source;if(haveSourceMap){if(flags&64){orig.source=orig.source.substring(orig.source.replace(/\\/g,"/").lastIndexOf("/")+1)}callstack+=` at ${symbolName} (${orig.source}:${orig.line}:${orig.column})\n`}}if(flags&16||!haveSourceMap){if(flags&64){file=file.substring(file.replace(/\\/g,"/").lastIndexOf("/")+1)}callstack+=(haveSourceMap?` = ${symbolName}`:` at ${symbolName}`)+` (${file}:${lineno}:${column})\n`}}callstack=callstack.replace(/\s+$/,"");return callstack}function _emscripten_get_callstack(flags,str,maxbytes){var callstack=getCallstack(flags);if(!str||maxbytes<=0){return lengthBytesUTF8(callstack)+1}var bytesWrittenExcludingNull=stringToUTF8(callstack,str,maxbytes);return bytesWrittenExcludingNull+1}var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();embind_init_charCodes();BindingError=Module["BindingError"]=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};InternalError=Module["InternalError"]=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};init_emval();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");var wasmImports={M:___syscall_chmod,N:___syscall_faccessat,h:___syscall_fcntl64,I:___syscall_fstat64,Q:___syscall_ioctl,F:___syscall_lstat64,G:___syscall_newfstatat,j:___syscall_openat,B:___syscall_rmdir,H:___syscall_stat64,C:___syscall_unlinkat,A:__abort_js,w:__embind_register_bigint,V:__embind_register_bool,U:__embind_register_emval,n:__embind_register_float,f:__embind_register_function,b:__embind_register_integer,a:__embind_register_memory_view,m:__embind_register_std_string,i:__embind_register_std_wstring,W:__embind_register_void,J:__emscripten_get_now_is_monotonic,x:__emscripten_lookup_name,L:__emscripten_memcpy_js,e:__emval_as,q:__emval_call_method,T:__emval_decref,u:__emval_get_global,p:__emval_get_method_caller,k:__emval_get_property,Y:__emval_incref,X:__emval_new_array,r:__emval_new_cstring,Z:__emval_new_object,o:__emval_run_destructors,c:__emval_set_property,d:__emval_take_value,t:__localtime_js,s:__munmap_js,O:__tzset_js,K:_emscripten_date_now,R:_emscripten_get_callstack,z:_emscripten_get_heap_max,y:_emscripten_resize_heap,D:_environ_get,E:_environ_sizes_get,S:_exit,g:_fd_close,P:_fd_read,v:_fd_seek,l:_fd_write};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["$"])();var ___getTypeName=a0=>(___getTypeName=wasmExports["aa"])(a0);var _malloc=a0=>(_malloc=wasmExports["ca"])(a0);var _free=a0=>(_free=wasmExports["da"])(a0);var _htons=a0=>(_htons=wasmExports["ea"])(a0);var _emscripten_builtin_memalign=(a0,a1)=>(_emscripten_builtin_memalign=wasmExports["fa"])(a0,a1);var ___trap=()=>(___trap=wasmExports["ga"])();var dynCall_viijii=Module["dynCall_viijii"]=(a0,a1,a2,a3,a4,a5,a6)=>(dynCall_viijii=Module["dynCall_viijii"]=wasmExports["ha"])(a0,a1,a2,a3,a4,a5,a6);var dynCall_viiiiji=Module["dynCall_viiiiji"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(dynCall_viiiiji=Module["dynCall_viiiiji"]=wasmExports["ia"])(a0,a1,a2,a3,a4,a5,a6,a7);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["ja"])(a0,a1,a2,a3,a4);var dynCall_iiiiij=Module["dynCall_iiiiij"]=(a0,a1,a2,a3,a4,a5,a6)=>(dynCall_iiiiij=Module["dynCall_iiiiij"]=wasmExports["ka"])(a0,a1,a2,a3,a4,a5,a6);var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=wasmExports["la"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)=>(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=wasmExports["ma"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; + + + return moduleRtn; +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = occtimportjs; +else if (typeof define === 'function' && define['amd']) + define([], () => occtimportjs); diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.wasm b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.wasm new file mode 100644 index 00000000..29458885 Binary files /dev/null and b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/libs/loaders/occt-import-js.wasm differ diff --git a/fusion-plating/fusion_plating_configurator/static/lib/o3dv/o3dv.min.js b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/o3dv.min.js new file mode 100644 index 00000000..71f769a7 --- /dev/null +++ b/fusion-plating/fusion_plating_configurator/static/lib/o3dv/o3dv.min.js @@ -0,0 +1,3985 @@ +var OV=(()=>{var dd=Object.defineProperty;var kx=Object.getOwnPropertyDescriptor;var Bx=Object.getOwnPropertyNames;var Gx=Object.prototype.hasOwnProperty;var Vx=(r,e)=>{for(var t in e)dd(r,t,{get:e[t],enumerable:!0})},zx=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Bx(e))!Gx.call(r,i)&&i!==t&&dd(r,i,{get:()=>e[i],enumerable:!(n=kx(e,i))||n.enumerable});return r};var Hx=r=>zx(dd({},"__esModule",{value:!0}),r);var nA={};Vx(nA,{AddCoord2D:()=>ig,AddCoord3D:()=>Eu,AddDiv:()=>bx,AddDomElement:()=>Wm,ArrayBufferToAsciiString:()=>fg,ArrayBufferToUtf8String:()=>Sn,ArrayToCoord3D:()=>Ui,ArrayToQuaternion:()=>Zo,ArrayToRGBColor:()=>ag,AsciiStringToArrayBuffer:()=>dg,Base64DataURIToArrayBuffer:()=>qs,BezierTweenFunction:()=>xg,BigEps:()=>eg,BinaryReader:()=>oi,BinaryWriter:()=>Ji,BoundingBoxCalculator3D:()=>ia,Box3D:()=>Ks,CalculateSurfaceArea:()=>Tx,CalculateTriangleNormal:()=>Ys,CalculateVolume:()=>Gm,Camera:()=>Or,CameraIsEqual3D:()=>Qf,CameraValidator:()=>ru,CheckModel:()=>ky,ClearDomElement:()=>Sx,ClickDetector:()=>iu,ColorComponentFromFloat:()=>Fn,ColorComponentToFloat:()=>Ws,ColorToMaterialConverter:()=>di,ConvertColorToThreeColor:()=>Dn,ConvertMeshToMeshBuffer:()=>qo,ConvertModelToThreeObject:()=>ed,ConvertThreeColorToColor:()=>Fc,ConvertThreeGeometryToMesh:()=>ur,Coord2D:()=>Nt,Coord3D:()=>Ye,Coord4D:()=>$i,CoordDistance2D:()=>Hs,CoordDistance3D:()=>ni,CoordIsEqual2D:()=>ji,CoordIsEqual3D:()=>bi,CopyObjectAttributes:()=>Fo,CreateDiv:()=>Ox,CreateDomElement:()=>rd,CreateHighlightMaterial:()=>Zp,CreateHighlightMaterials:()=>Pf,CreateModelUrlParameters:()=>Ex,CreateObjectUrl:()=>js,CreateObjectUrlWithMimeType:()=>Su,CreateOcctWorker:()=>Wo,CreateUrlBuilder:()=>Hm,CreateUrlParser:()=>Mx,CrossVector3D:()=>Zi,DegRad:()=>xr,Direction:()=>_t,DisposeThreeObjects:()=>Nf,DotVector2D:()=>Rl,DotVector3D:()=>Nl,EdgeSettings:()=>Io,EmbeddedViewer:()=>Al,EnvironmentSettings:()=>Ro,Eps:()=>Qm,EscapeHtmlChars:()=>hu,EventNotifier:()=>fu,ExportedFile:()=>sn,Exporter:()=>Pu,Exporter3dm:()=>Yo,ExporterBase:()=>kn,ExporterBim:()=>Ko,ExporterGltf:()=>Jo,ExporterModel:()=>$o,ExporterObj:()=>Qo,ExporterOff:()=>ea,ExporterPly:()=>ta,ExporterSettings:()=>Ul,ExporterStl:()=>na,FLoc:()=>Uo,FaceMaterial:()=>zo,FileFormat:()=>dn,FileSource:()=>Ki,FinalizeModel:()=>Of,FlipMeshTrianglesOrientation:()=>Ru,FormatString:()=>uu,GenerateCone:()=>Um,GenerateCuboid:()=>yx,GenerateCylinder:()=>xx,GeneratePlatonicSolid:()=>_x,GenerateSphere:()=>vx,Generator:()=>ks,GeneratorHelper:()=>yl,GeneratorParams:()=>Kc,GetBoundingBox:()=>yd,GetDefaultCamera:()=>sd,GetDefaultMaterials:()=>_g,GetDomElementClientCoordinates:()=>ps,GetDomElementExternalHeight:()=>nd,GetDomElementExternalWidth:()=>td,GetDomElementInnerDimensions:()=>id,GetDomElementOuterHeight:()=>Px,GetDomElementOuterWidth:()=>Rx,GetFileExtension:()=>Tr,GetFileExtensionFromMimeType:()=>xs,GetFileName:()=>Rn,GetIntegerFromStyle:()=>zn,GetLineSegmentsProjectedDistance:()=>Lf,GetShadingType:()=>Rf,GetShadingTypeOfObject:()=>jm,GetTetrahedronSignedVolume:()=>Bm,GetTopology:()=>xd,GetTriangleArea:()=>km,HasHighpDriverIssue:()=>If,HexStringToRGBAColor:()=>og,HexStringToRGBColor:()=>sg,ImportError:()=>Us,ImportErrorCode:()=>Lr,ImportResult:()=>qc,ImportSettings:()=>ml,Importer:()=>gl,Importer3dm:()=>Ka,Importer3ds:()=>Za,ImporterBase:()=>Jt,ImporterBim:()=>rl,ImporterFcstd:()=>pl,ImporterFile:()=>Zs,ImporterFileAccessor:()=>jc,ImporterFileList:()=>Js,ImporterGltf:()=>Ja,ImporterIfc:()=>$a,ImporterObj:()=>Qa,ImporterOcct:()=>nl,ImporterOff:()=>el,ImporterPly:()=>tl,ImporterStl:()=>il,ImporterThree3mf:()=>hl,ImporterThreeAmf:()=>fl,ImporterThreeBase:()=>mr,ImporterThreeDae:()=>cl,ImporterThreeFbx:()=>ll,ImporterThreeSvg:()=>$f,ImporterThreeWrl:()=>ul,Init3DViewerElements:()=>Fx,Init3DViewerFromFileList:()=>Dx,Init3DViewerFromUrlList:()=>Ym,InputFile:()=>aa,InputFilesFromFileObjects:()=>Du,InputFilesFromUrls:()=>Ou,InsertDomElementAfter:()=>wx,InsertDomElementBefore:()=>Ax,IntegerToHexString:()=>In,IntersectionMode:()=>Vm,IsDefined:()=>Do,IsDomElementVisible:()=>Ix,IsEmptyMesh:()=>vs,IsEqual:()=>Ut,IsEqualEps:()=>ng,IsGreater:()=>yu,IsGreaterOrEqual:()=>Vo,IsLower:()=>Bo,IsLowerOrEqual:()=>Go,IsModelEmpty:()=>ca,IsNegative:()=>Br,IsObjectEmpty:()=>cu,IsPositive:()=>Di,IsPowerOfTwo:()=>pd,IsTwoManifold:()=>vd,IsUrl:()=>hg,IsZero:()=>kr,Line:()=>hs,LinearToSRGB:()=>Xs,LinearTweenFunction:()=>vg,LoadExternalLibrary:()=>_r,LoadExternalLibraryFromUrl:()=>Ho,Loc:()=>nt,MaterialBase:()=>Pl,MaterialGeometryType:()=>Vs,MaterialSource:()=>bn,MaterialType:()=>Wn,Matrix:()=>xn,MatrixIsEqual:()=>Iu,Mesh:()=>tn,MeshBuffer:()=>Ol,MeshInstance:()=>Hr,MeshInstanceId:()=>ki,MeshPrimitiveBuffer:()=>Ll,Model:()=>br,ModelObject3D:()=>Er,ModelToThreeConversionOutput:()=>_l,ModelToThreeConversionParams:()=>vl,MouseInteraction:()=>tu,NameFromLine:()=>zr,Navigation:()=>El,NavigationMode:()=>Bs,NavigationType:()=>li,NextPowerOfTwo:()=>ug,Node:()=>Bn,Object3D:()=>Dl,Octree:()=>sa,OctreeNode:()=>kl,ParabolicTweenFunction:()=>Lu,ParameterConverter:()=>rn,ParameterListBuilder:()=>Zc,ParameterListParser:()=>Jc,ParametersFromLine:()=>Yi,PhongMaterial:()=>En,PhysicalMaterial:()=>ys,ProjectPointToSegment2D:()=>md,ProjectionMode:()=>ei,Property:()=>cn,PropertyGroup:()=>Kn,PropertyToString:()=>wu,PropertyType:()=>Yt,Quaternion:()=>ii,QuaternionFromAxisAngle:()=>Cu,QuaternionFromXYZ:()=>mg,QuaternionIsEqual:()=>pg,RGBAColor:()=>Gr,RGBAColorToHexString:()=>rg,RGBColor:()=>St,RGBColorFromFloatComponents:()=>Ei,RGBColorIsEqual:()=>Vr,RGBColorToHexString:()=>xu,RadDeg:()=>tg,ReadFile:()=>Tu,ReadLines:()=>Fi,ReplaceDefaultMaterialsColor:()=>Tg,RequestUrl:()=>_u,RevokeObjectUrl:()=>Au,RunTaskAsync:()=>Il,RunTasks:()=>pu,RunTasksBatch:()=>mu,SRGBToLinear:()=>vr,Segment2D:()=>ra,SegmentPointDistance2D:()=>Nu,SetDomElementHeight:()=>qm,SetDomElementOuterHeight:()=>Lx,SetDomElementOuterWidth:()=>Nx,SetDomElementWidth:()=>Xm,SetLanguageCode:()=>$m,SetLocalizedStrings:()=>Jm,SetThreeMeshPolygonOffset:()=>zm,ShadingModel:()=>bl,ShadingType:()=>vi,ShowDomElement:()=>Cx,SubCoord2D:()=>ms,SubCoord3D:()=>Un,TaskRunner:()=>ko,TextWriter:()=>mi,TextureIsEqual:()=>lg,TextureMap:()=>fi,TextureMapIsEqual:()=>gs,ThreeColorConverter:()=>ja,ThreeConversionStateHandler:()=>$c,ThreeLinearToSRGBColorConverter:()=>Cf,ThreeMaterialHandler:()=>eu,ThreeMeshMaterialHandler:()=>Tl,ThreeModelLoader:()=>Ml,ThreeNodeTree:()=>Qc,ThreeSRGBToLinearColorConverter:()=>Ya,Topology:()=>la,TopologyEdge:()=>Gl,TopologyTriangle:()=>zl,TopologyTriangleEdge:()=>Vl,TopologyVertex:()=>Bl,TouchInteraction:()=>nu,TransformFileHostUrls:()=>Mu,TransformMesh:()=>Mr,Transformation:()=>pn,TransformationIsEqual:()=>gg,TraverseThreeObject:()=>od,Triangle:()=>Qt,TweenCoord3D:()=>oa,Unit:()=>_s,UpVector:()=>su,UpdateMaterialTransparency:()=>pi,Utf8StringToArrayBuffer:()=>jo,ValueOrDefault:()=>Zm,VectorAngle3D:()=>bu,VectorLength3D:()=>Xo,Viewer:()=>Sl,ViewerMainModel:()=>xl,ViewerModel:()=>Gs,WaitWhile:()=>gu});function Do(r){return r!=null}function Zm(r,e){return r??e}function Fo(r,e){if(Do(r))for(let t of Object.keys(r))Do(r[t])&&(e[t]=r[t])}function cu(r){return Object.keys(r).length===0}function uu(r,...e){return r.replace(/{([0-9]+)}/g,(t,n)=>e[n]===void 0?t:e[n])}function hu(r){return r.replace(//g,">")}var fu=class{constructor(){this.eventListeners=new Map}AddEventListener(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,[]),this.eventListeners.get(e).push(t)}HasEventListener(e){return this.eventListeners.has(e)}GetEventNotifier(e){return()=>{this.NotifyEventListeners(e)}}NotifyEventListeners(e,...t){if(!this.eventListeners.has(e))return;let n=this.eventListeners.get(e);for(let i of n)i(...t)}};var Cl=null,du=null;function Jm(r){Cl=r}function $m(r){du=r}function nt(r){return Cl===null||du===null||!Cl[r]||!Cl[r][du]?r:Cl[r][du]}function Uo(r,...e){return uu(nt(r),...e)}var ko=class{constructor(){this.count=null,this.current=null,this.callbacks=null}Run(e,t){this.count=e,this.current=0,this.callbacks=t,e===0?this.TaskReady():this.RunOnce()}RunBatch(e,t,n){let i=0;e>0&&(i=parseInt((e-1)/t,10)+1),this.Run(i,{runTask:(s,o)=>{let a=s*t,l=Math.min((s+1)*t,e)-1;n.runTask(a,l,o)},onReady:n.onReady})}RunOnce(){setTimeout(()=>{this.callbacks.runTask(this.current,this.TaskReady.bind(this))},0)}TaskReady(){this.current+=1,this.current{r()},10)}function pu(r,e){new ko().Run(r,e)}function mu(r,e,t){new ko().RunBatch(r,e,t)}function gu(r){function e(t){t()&&setTimeout(()=>{e(t)},10)}e(r)}var Qm=1e-8,eg=1e-4,tg=57.29577951308232,xr=.017453292519943;function kr(r){return Math.abs(r)<1e-8}function Bo(r,e){return e-r>1e-8}function yu(r,e){return r-e>1e-8}function Go(r,e){return e-r>-1e-8}function Vo(r,e){return r-e>-1e-8}function Ut(r,e){return Math.abs(e-r)<1e-8}function ng(r,e,t){return Math.abs(e-r)1e-8}function Br(r){return r<-1e-8}var _t={X:1,Y:2,Z:3};var Nt=class r{constructor(e,t){this.x=e,this.y=t}Clone(){return new r(this.x,this.y)}};function ji(r,e){return Ut(r.x,e.x)&&Ut(r.y,e.y)}function ig(r,e){return new Nt(r.x+e.x,r.y+e.y)}function ms(r,e){return new Nt(r.x-e.x,r.y-e.y)}function Hs(r,e){return Math.sqrt((r.x-e.x)*(r.x-e.x)+(r.y-e.y)*(r.y-e.y))}function Rl(r,e){return r.x*e.x+r.y*e.y}var St=class r{constructor(e,t,n){this.r=e,this.g=t,this.b=n}Set(e,t,n){this.r=e,this.g=t,this.b=n}Clone(){return new r(this.r,this.g,this.b)}},Gr=class r{constructor(e,t,n,i){this.r=e,this.g=t,this.b=n,this.a=i}Set(e,t,n,i){this.r=e,this.g=t,this.b=n,this.a=i}Clone(){return new r(this.r,this.g,this.b,this.a)}};function Fn(r){return parseInt(Math.round(r*255),10)}function Ws(r){return r/255}function Ei(r,e,t){return new St(Fn(r),Fn(e),Fn(t))}function vr(r){return r<.04045?r*.0773993808:Math.pow(r*.9478672986+.0521327014,2.4)}function Xs(r){return r<.0031308?r*12.92:1.055*Math.pow(r,.41666)-.055}function In(r){let e=parseInt(r,10).toString(16);for(;e.length<2;)e="0"+e;return e}function xu(r){let e=In(r.r),t=In(r.g),n=In(r.b);return e+t+n}function rg(r){let e=In(r.r),t=In(r.g),n=In(r.b),i=In(r.a);return e+t+n+i}function sg(r){if(r.length!==6)return null;let e=parseInt(r.substring(0,2),16),t=parseInt(r.substring(2,4),16),n=parseInt(r.substring(4,6),16);return new St(e,t,n)}function og(r){if(r.length!==6&&r.length!==8)return null;let e=parseInt(r.substring(0,2),16),t=parseInt(r.substring(2,4),16),n=parseInt(r.substring(4,6),16),i=255;return r.length===8&&(i=parseInt(r.substring(6,8),16)),new Gr(e,t,n,i)}function ag(r){return new St(r[0],r[1],r[2])}function Vr(r,e){return r.r===e.r&&r.g===e.g&&r.b===e.b}var fi=class{constructor(){this.name=null,this.mimeType=null,this.buffer=null,this.offset=new Nt(0,0),this.scale=new Nt(1,1),this.rotation=0}IsValid(){return this.name!==null&&this.buffer!==null}HasTransformation(){return!ji(this.offset,new Nt(0,0))||!ji(this.scale,new Nt(1,1))||!Ut(this.rotation,0)}IsEqual(e){return!(this.name!==e.name||this.mimeType!==e.mimeType||!ji(this.offset,e.offset)||!ji(this.scale,e.scale)||!Ut(this.rotation,e.rotation))}};function gs(r,e){return r===null&&e===null?!0:r===null||e===null?!1:r.IsEqual(e)}var Wn={Phong:1,Physical:2},bn={Model:1,DefaultFace:2,DefaultLine:3},Pl=class{constructor(e){this.type=e,this.source=bn.Model,this.name="",this.color=new St(0,0,0),this.vertexColors=!1}IsEqual(e){return!(this.type!==e.type||this.source!==e.source||this.name!==e.name||!Vr(this.color,e.color)||this.vertexColors!==e.vertexColors)}},zo=class extends Pl{constructor(e){super(e),this.emissive=new St(0,0,0),this.opacity=1,this.transparent=!1,this.diffuseMap=null,this.bumpMap=null,this.normalMap=null,this.emissiveMap=null,this.alphaTest=0,this.multiplyDiffuseMap=!1}IsEqual(e){return!(!super.IsEqual(e)||!Vr(this.emissive,e.emissive)||!Ut(this.opacity,e.opacity)||this.transparent!==e.transparent||!gs(this.diffuseMap,e.diffuseMap)||!gs(this.bumpMap,e.bumpMap)||!gs(this.normalMap,e.normalMap)||!gs(this.emissiveMap,e.emissiveMap)||!Ut(this.alphaTest,e.alphaTest)||this.multiplyDiffuseMap!==e.multiplyDiffuseMap)}},En=class extends zo{constructor(){super(Wn.Phong),this.ambient=new St(0,0,0),this.specular=new St(0,0,0),this.shininess=0,this.specularMap=null}IsEqual(e){return!(!super.IsEqual(e)||!Vr(this.ambient,e.ambient)||!Vr(this.specular,e.specular)||!Ut(this.shininess,e.shininess)||!gs(this.specularMap,e.specularMap))}},ys=class extends zo{constructor(){super(Wn.Physical),this.metalness=0,this.roughness=1,this.metalnessMap=null}IsEqual(e){return!(!super.IsEqual(e)||!Ut(this.metalness,e.metalness)||!Ut(this.roughness,e.roughness)||!gs(this.metalnessMap,e.metalnessMap))}};function lg(r,e){return!(r.name!==e.name||r.mimeType!==e.mimeType||!ji(r.offset,e.offset)||!ji(r.scale,e.scale)||!Ut(r.rotation,e.rotation))}var cg=new Set;function Ho(r){return new Promise((e,t)=>{if(cg.has(r)){e();return}let n=document.createElement("script");n.type="text/javascript",n.src=r,n.onload=()=>{cg.add(r),e()},n.onerror=()=>{t()},document.head.appendChild(n)})}function zr(r,e,t){let n=r.substring(e),i=n.indexOf(t);return i!==-1&&(n=n.substring(0,i)),n.trim()}function Yi(r,e){if(e!==null){let t=r.indexOf(e);t!==-1&&(r=r.substring(0,t).trim())}return r.split(/\s+/u)}function Fi(r,e){function t(s,o){let a=s.trim();a.length>0&&o(a)}let n=0,i=r.indexOf(` +`,n);for(;i!==-1;)t(r.substring(n,i),e),n=i+1,i=r.indexOf(` +`,n);t(r.substring(n),e)}function pd(r){return(r&r-1)===0}function ug(r){if(pd(r))return r;let e=Math.pow(2,Math.ceil(Math.log(r)/Math.log(2)));return parseInt(e,10)}function pi(r){r.transparent=!1,Bo(r.opacity,1)&&(r.transparent=!0)}var di=class{constructor(e){this.model=e,this.colorToMaterialIndex=new Map}GetMaterialIndex(e,t,n,i){let s=In(e)+In(t)+In(n),o=i!=null;if(o&&(s+=In(i)),this.colorToMaterialIndex.has(s))return this.colorToMaterialIndex.get(s);{let a=new En;a.name=s.toUpperCase(),a.color=new St(e,t,n),o&&i<255&&(a.opacity=i/255,pi(a));let l=this.model.AddMaterial(a);return this.colorToMaterialIndex.set(s,l),l}}},vu=null;function Wo(r){return new Promise((e,t)=>{if(vu!==null){e(new Worker(vu));return}let n="https://cdn.jsdelivr.net/npm/occt-import-js@0.0.22/dist/";fetch(n+"occt-import-js-worker.js").then(i=>i.ok?i.text():t()).then(i=>{i=i.replace("occt-import-js.js",n+"occt-import-js.js"),i=i.replace("return path","return '"+n+"occt-import-js.wasm'");let s=new Blob([i],{type:"text/javascript"});return vu=URL.createObjectURL(s),e(new Worker(vu))}).catch(t)})}function _r(r){return r==="rhino3dm"?Ho("https://cdn.jsdelivr.net/npm/rhino3dm@8.17.0/rhino3dm.min.js"):r==="webifc"?Ho("https://cdn.jsdelivr.net/npm/web-ifc@0.0.68/web-ifc-api-iife.js"):r==="draco3d"?Ho("https://cdn.jsdelivr.net/npm/draco3d@1.5.7/draco_decoder_nodejs.min.js"):null}var Ki={Url:1,File:2,Decompressed:3},dn={Text:1,Binary:2};function Rn(r){let e=r,t=e.indexOf("?");t!==-1&&(e=e.substring(0,t));let n=e.lastIndexOf("/");return n===-1&&(n=e.lastIndexOf("\\")),n!==-1&&(e=e.substring(n+1)),decodeURI(e)}function Tr(r){let e=Rn(r),t=e.lastIndexOf(".");return t===-1?"":e.substring(t+1).toLowerCase()}function _u(r,e){return new Promise((t,n)=>{let i=new XMLHttpRequest;i.open("GET",r,!0),i.onprogress=s=>{e(s.loaded,s.total)},i.onload=()=>{i.status===200?t(i.response):n()},i.onerror=()=>{n()},i.responseType="arraybuffer",i.send(null)})}function Tu(r,e){return new Promise((t,n)=>{let i=new FileReader;i.onprogress=s=>{e(s.loaded,s.total)},i.onloadend=s=>{s.target.readyState===FileReader.DONE&&t(s.target.result)},i.onerror=()=>{n()},i.readAsArrayBuffer(r)})}function Mu(r){for(let e=0;e0&&this.MultiplyScalar(1/e),this}Offset(e,t){let n=e.Clone().Normalize();return this.x+=n.x*t,this.y+=n.y*t,this.z+=n.z*t,this}Rotate(e,t,n){let i=e.Clone().Normalize(),s=i.x,o=i.y,a=i.z,l=this.x-n.x,c=this.y-n.y,u=this.z-n.z,h=Math.sin(t),f=Math.cos(t);return this.x=-s*(-s*l-o*c-a*u)*(1-f)+l*f+(-a*c+o*u)*h,this.y=-o*(-s*l-o*c-a*u)*(1-f)+c*f+(a*l-s*u)*h,this.z=-a*(-s*l-o*c-a*u)*(1-f)+u*f+(-o*l+s*c)*h,this.x+=n.x,this.y+=n.y,this.z+=n.z,this}Clone(){return new r(this.x,this.y,this.z)}};function bi(r,e){return Ut(r.x,e.x)&&Ut(r.y,e.y)&&Ut(r.z,e.z)}function Eu(r,e){return new Ye(r.x+e.x,r.y+e.y,r.z+e.z)}function Un(r,e){return new Ye(r.x-e.x,r.y-e.y,r.z-e.z)}function ni(r,e){return Math.sqrt((r.x-e.x)*(r.x-e.x)+(r.y-e.y)*(r.y-e.y)+(r.z-e.z)*(r.z-e.z))}function Nl(r,e){return r.x*e.x+r.y*e.y+r.z*e.z}function bu(r,e){let t=r.Clone().Normalize(),n=e.Clone().Normalize();if(bi(t,n))return 0;let i=Nl(t,n);return Math.acos(i)}function Zi(r,e){let t=new Ye(0,0,0);return t.x=r.y*e.z-r.z*e.y,t.y=r.z*e.x-r.x*e.z,t.z=r.x*e.y-r.y*e.x,t}function Xo(r,e,t){return Math.sqrt(r*r+e*e+t*t)}function Ui(r){return new Ye(r[0],r[1],r[2])}var Ll=class{constructor(){this.indices=[],this.vertices=[],this.colors=[],this.normals=[],this.uvs=[],this.material=null}GetBounds(){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let n=0;n0,v=y.TextureUVCount()>0,x=y.GetVertex(d.vertex),S=y.GetNormal(d.normal),I=m.vertices.length/3;m.indices.push(I),m.vertices.push(x.x,x.y,x.z);let A=h(y,d.color,_);A!==null&&m.colors.push(A.r/255,A.g/255,A.b/255),m.normals.push(S.x,S.y,S.z);let C=f(y,d.uv,v);return C!==null&&m.uvs.push(C.x,C.y),{index:I,color:A,normal:S,uv:C}}function g(y,d,m){function _(S,I,A){if(A===null&&I===null)return!0;let C=h(S,I,!0);return Vr(A,C)}function v(S,I,A){let C=S.GetNormal(I);return bi(A,C)}function x(S,I,A){if(A===null&&I===null)return!0;let C=f(S,I,!0);return ji(A,C)}for(let S=0;S{let c=r.GetTriangle(a),u=r.GetTriangle(l);return c.mat-u.mat});let s=null,o=null;for(let a=0;a{n(i)})}ExportContent(e,t,n,i){}GetExportedMaterialName(e){return this.GetExportedName(e,nt("Material"))}GetExportedMeshName(e){return this.GetExportedName(e,nt("Mesh"))}GetExportedName(e,t){return e.length===0?t:e}};var Yo=class extends kn{constructor(){super(),this.rhino=null}CanExport(e,t){return e===dn.Binary&&t==="3dm"}ExportContent(e,t,n,i){this.rhino===null?_r("rhino3dm").then(()=>{rhino3dm().then(s=>{this.rhino=s,this.ExportRhinoContent(e,n,i)})}).catch(()=>{i()}):this.ExportRhinoContent(e,n,i)}ExportRhinoContent(e,t,n){function i(c){return{r:c.r,g:c.g,b:c.b,a:255}}let s=new sn("model.3dm");t.push(s);let o=new this.rhino.File3dm;e.EnumerateTransformedMeshInstances(c=>{let u=qo(c);for(let h=0;h{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}var Ko=class extends kn{constructor(){super()}CanExport(e,t){return e===dn.Text&&t==="bim"}ExportContent(e,t,n,i){let s={schema_version:"1.1.0",meshes:[],elements:[],info:{}};this.ExportProperties(e.GetModel(),s.info);let o=0;e.EnumerateTransformedMeshInstances(l=>{let c={mesh_id:o,coordinates:[],indices:[]};l.EnumerateVertices(g=>{c.coordinates.push(g.x,g.y,g.z)}),l.EnumerateTriangleVertexIndices((g,y,d)=>{c.indices.push(g,y,d)});let u={mesh_id:o,type:"Other",color:{r:200,g:200,b:200,a:255},vector:{x:0,y:0,z:0},rotation:{qx:0,qy:0,qz:0,qw:1},guid:Wx(),info:{}},h=null,f=!0,p=[];for(let g=0;g=this.arrayBuffer.byteLength}GetBuffer(){return this.arrayBuffer}WriteArrayBuffer(e){let t=new Uint8Array(e);new Uint8Array(this.arrayBuffer).set(t,this.position),this.position+=e.byteLength}WriteBoolean8(e){this.dataView.setInt8(this.position,e?1:0),this.position=this.position+1}WriteCharacter8(e){this.dataView.setInt8(this.position,e),this.position=this.position+1}WriteUnsignedCharacter8(e){this.dataView.setUint8(this.position,e),this.position=this.position+1}WriteInteger16(e){this.dataView.setInt16(this.position,e,this.isLittleEndian),this.position=this.position+2}WriteUnsignedInteger16(e){this.dataView.setUint16(this.position,e,this.isLittleEndian),this.position=this.position+2}WriteInteger32(e){this.dataView.setInt32(this.position,e,this.isLittleEndian),this.position=this.position+4}WriteUnsignedInteger32(e){this.dataView.setUint32(this.position,e,this.isLittleEndian),this.position=this.position+4}WriteFloat32(e){this.dataView.setFloat32(this.position,e,this.isLittleEndian),this.position=this.position+4}WriteDouble64(e){this.dataView.setFloat64(this.position,e,this.isLittleEndian),this.position=this.position+8}};var $i=class r{constructor(e,t,n,i){this.x=e,this.y=t,this.z=n,this.w=i}Clone(){return new r(this.x,this.y,this.z,this.w)}};var ii=class{constructor(e,t,n,i){this.x=e,this.y=t,this.z=n,this.w=i}};function pg(r,e){return Ut(r.x,e.x)&&Ut(r.y,e.y)&&Ut(r.z,e.z)&&Ut(r.w,e.w)}function Zo(r){return new ii(r[0],r[1],r[2],r[3])}function Cu(r,e){let t=e/2,n=Math.sin(t);return new ii(r.x*n,r.y*n,r.z*n,Math.cos(t))}function mg(r,e,t,n){let i=Math.cos(r/2),s=Math.cos(e/2),o=Math.cos(t/2),a=Math.sin(r/2),l=Math.sin(e/2),c=Math.sin(t/2),u=new ii(0,0,0,1);if(n==="XYZ")u.x=a*s*o+i*l*c,u.y=i*l*o-a*s*c,u.z=i*s*c+a*l*o,u.w=i*s*o-a*l*c;else if(n==="YXZ")u.x=a*s*o+i*l*c,u.y=i*l*o-a*s*c,u.z=i*s*c-a*l*o,u.w=i*s*o+a*l*c;else if(n==="ZXY")u.x=a*s*o-i*l*c,u.y=i*l*o+a*s*c,u.z=i*s*c+a*l*o,u.w=i*s*o-a*l*c;else if(n==="ZYX")u.x=a*s*o-i*l*c,u.y=i*l*o+a*s*c,u.z=i*s*c-a*l*o,u.w=i*s*o+a*l*c;else if(n==="YZX")u.x=a*s*o+i*l*c,u.y=i*l*o+a*s*c,u.z=i*s*c-a*l*o,u.w=i*s*o-a*l*c;else if(n==="XZY")u.x=a*s*o-i*l*c,u.y=i*l*o-a*s*c,u.z=i*s*c+a*l*o,u.w=i*s*o+a*l*c;else return null;return u}var xn=class r{constructor(e){this.matrix=null,e!=null&&(this.matrix=e)}IsValid(){return this.matrix!==null}Set(e){return this.matrix=e,this}Get(){return this.matrix}Clone(){let e=[this.matrix[0],this.matrix[1],this.matrix[2],this.matrix[3],this.matrix[4],this.matrix[5],this.matrix[6],this.matrix[7],this.matrix[8],this.matrix[9],this.matrix[10],this.matrix[11],this.matrix[12],this.matrix[13],this.matrix[14],this.matrix[15]];return new r(e)}CreateIdentity(){return this.matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],this}IsIdentity(){let e=new r().CreateIdentity().Get();for(let t=0;t<16;t++)if(!Ut(this.matrix[t],e[t]))return!1;return!0}CreateTranslation(e,t,n){return this.matrix=[1,0,0,0,0,1,0,0,0,0,1,0,e,t,n,1],this}CreateRotation(e,t,n,i){let s=e+e,o=t+t,a=n+n,l=e*s,c=e*o,u=e*a,h=t*o,f=t*a,p=n*a,g=i*s,y=i*o,d=i*a;return this.matrix=[1-(h+p),c+d,u-y,0,c-d,1-(l+p),f+g,0,u+y,f-g,1-(l+h),0,0,0,0,1],this}CreateRotationAxisAngle(e,t){let n=Cu(e,t);return this.CreateRotation(n.x,n.y,n.z,n.w)}CreateScale(e,t,n){return this.matrix=[e,0,0,0,0,t,0,0,0,0,n,0,0,0,0,1],this}ComposeTRS(e,t,n){let i=e.x,s=e.y,o=e.z,a=t.x,l=t.y,c=t.z,u=t.w,h=n.x,f=n.y,p=n.z,g=a+a,y=l+l,d=c+c,m=a*g,_=a*y,v=a*d,x=l*y,S=l*d,I=c*d,A=u*g,C=u*y,E=u*d;return this.matrix=[(1-(x+I))*h,(_+E)*h,(v-C)*h,0,(_-E)*f,(1-(m+I))*f,(S+A)*f,0,(v+C)*p,(S-A)*p,(1-(m+x))*p,0,i,s,o,1],this}DecomposeTRS(){let e=new Ye(this.matrix[12],this.matrix[13],this.matrix[14]),t=Xo(this.matrix[0],this.matrix[1],this.matrix[2]),n=Xo(this.matrix[4],this.matrix[5],this.matrix[6]),i=Xo(this.matrix[8],this.matrix[9],this.matrix[10]),s=this.Determinant();Br(s)&&(t*=-1);let o=new Ye(t,n,i),a=this.matrix[0]/t,l=this.matrix[4]/n,c=this.matrix[8]/i,u=this.matrix[1]/t,h=this.matrix[5]/n,f=this.matrix[9]/i,p=this.matrix[2]/t,g=this.matrix[6]/n,y=this.matrix[10]/i,d=null,m=a+h+y;if(m>0){let _=Math.sqrt(m+1)*2;d=new ii((g-f)/_,(c-p)/_,(u-l)/_,.25*_)}else if(a>h&&a>y){let _=Math.sqrt(1+a-h-y)*2;d=new ii(.25*_,(l+u)/_,(c+p)/_,(g-f)/_)}else if(h>y){let _=Math.sqrt(1+h-a-y)*2;d=new ii((l+u)/_,.25*_,(f+g)/_,(c-p)/_)}else{let _=Math.sqrt(1+y-a-h)*2;d=new ii((c+p)/_,(f+g)/_,.25*_,(u-l)/_)}return{translation:e,rotation:d,scale:o}}Determinant(){let e=this.matrix[0],t=this.matrix[1],n=this.matrix[2],i=this.matrix[3],s=this.matrix[4],o=this.matrix[5],a=this.matrix[6],l=this.matrix[7],c=this.matrix[8],u=this.matrix[9],h=this.matrix[10],f=this.matrix[11],p=this.matrix[12],g=this.matrix[13],y=this.matrix[14],d=this.matrix[15],m=e*o-t*s,_=e*a-n*s,v=e*l-i*s,x=t*a-n*o,S=t*l-i*o,I=n*l-i*a,A=c*g-u*p,C=c*y-h*p,E=c*d-f*p,b=u*y-h*g,R=u*d-f*g,N=h*d-f*y;return m*N-_*R+v*b+x*E-S*C+I*A}Invert(){let e=this.matrix[0],t=this.matrix[1],n=this.matrix[2],i=this.matrix[3],s=this.matrix[4],o=this.matrix[5],a=this.matrix[6],l=this.matrix[7],c=this.matrix[8],u=this.matrix[9],h=this.matrix[10],f=this.matrix[11],p=this.matrix[12],g=this.matrix[13],y=this.matrix[14],d=this.matrix[15],m=e*o-t*s,_=e*a-n*s,v=e*l-i*s,x=t*a-n*o,S=t*l-i*o,I=n*l-i*a,A=c*g-u*p,C=c*y-h*p,E=c*d-f*p,b=u*y-h*g,R=u*d-f*g,N=h*d-f*y,B=m*N-_*R+v*b+x*E-S*C+I*A;if(Ut(B,0))return null;let V=[(o*N-a*R+l*b)/B,(n*R-t*N-i*b)/B,(g*I-y*S+d*x)/B,(h*S-u*I-f*x)/B,(a*E-s*N-l*C)/B,(e*N-n*E+i*C)/B,(y*v-p*I-d*_)/B,(c*I-h*v+f*_)/B,(s*R-o*E+l*A)/B,(t*E-e*R-i*A)/B,(p*S-g*v+d*m)/B,(u*v-c*S-f*m)/B,(o*C-s*b-a*A)/B,(e*b-t*C+n*A)/B,(g*_-p*x-y*m)/B,(c*x-u*_+h*m)/B];return new r(V)}Transpose(){let e=[this.matrix[0],this.matrix[4],this.matrix[8],this.matrix[12],this.matrix[1],this.matrix[5],this.matrix[9],this.matrix[13],this.matrix[2],this.matrix[6],this.matrix[10],this.matrix[14],this.matrix[3],this.matrix[7],this.matrix[11],this.matrix[15]];return new r(e)}InvertTranspose(){let e=this.Invert();return e===null?null:e.Transpose()}MultiplyVector(e){let t=e.x,n=e.y,i=e.z,s=e.w,o=this.matrix[0],a=this.matrix[1],l=this.matrix[2],c=this.matrix[3],u=this.matrix[4],h=this.matrix[5],f=this.matrix[6],p=this.matrix[7],g=this.matrix[8],y=this.matrix[9],d=this.matrix[10],m=this.matrix[11],_=this.matrix[12],v=this.matrix[13],x=this.matrix[14],S=this.matrix[15];return new $i(t*o+n*u+i*g+s*_,t*a+n*h+i*y+s*v,t*l+n*f+i*d+s*x,t*c+n*p+i*m+s*S)}MultiplyMatrix(e){let t=this.matrix[0],n=this.matrix[1],i=this.matrix[2],s=this.matrix[3],o=this.matrix[4],a=this.matrix[5],l=this.matrix[6],c=this.matrix[7],u=this.matrix[8],h=this.matrix[9],f=this.matrix[10],p=this.matrix[11],g=this.matrix[12],y=this.matrix[13],d=this.matrix[14],m=this.matrix[15],_=e.matrix[0],v=e.matrix[1],x=e.matrix[2],S=e.matrix[3],I=e.matrix[4],A=e.matrix[5],C=e.matrix[6],E=e.matrix[7],b=e.matrix[8],R=e.matrix[9],N=e.matrix[10],B=e.matrix[11],V=e.matrix[12],ie=e.matrix[13],Y=e.matrix[14],te=e.matrix[15],q=[t*_+n*I+i*b+s*V,t*v+n*A+i*R+s*ie,t*x+n*C+i*N+s*Y,t*S+n*E+i*B+s*te,o*_+a*I+l*b+c*V,o*v+a*A+l*R+c*ie,o*x+a*C+l*N+c*Y,o*S+a*E+l*B+c*te,u*_+h*I+f*b+p*V,u*v+h*A+f*R+p*ie,u*x+h*C+f*N+p*Y,u*S+h*E+f*B+p*te,g*_+y*I+d*b+m*V,g*v+y*A+d*R+m*ie,g*x+y*C+d*N+m*Y,g*S+y*E+d*B+m*te];return new r(q)}};function Iu(r,e){let t=r.Get(),n=e.Get();for(let i=0;i<16;i++)if(!Ut(t[i],n[i]))return!1;return!0}var pn=class r{constructor(e){e!=null?this.matrix=e:(this.matrix=new xn,this.matrix.CreateIdentity())}SetMatrix(e){return this.matrix=e,this}GetMatrix(){return this.matrix}IsIdentity(){return this.matrix.IsIdentity()}AppendMatrix(e){return this.matrix=this.matrix.MultiplyMatrix(e),this}Append(e){return this.AppendMatrix(e.GetMatrix()),this}TransformCoord3D(e){let t=new $i(e.x,e.y,e.z,1),n=this.matrix.MultiplyVector(t);return new Ye(n.x,n.y,n.z)}Clone(){let e=this.matrix.Clone();return new r(e)}};function gg(r,e){return Iu(r.GetMatrix(),e.GetMatrix())}function vs(r){return r.LineCount()===0&&r.TriangleCount()===0}function Ys(r,e,t){let n=Un(e,r),i=Un(t,r),s=Zi(n,i);return s.Normalize(),s}function Mr(r,e){if(!e.IsIdentity()){for(let t=0;t0){let t=e.GetMatrix().InvertTranspose();if(t!==null){let n=new pn(t);for(let i=0;i{let i=t.TransformCoord3D(n);e(i)})}EnumerateTriangleVertexIndices(e){this.mesh.EnumerateTriangleVertexIndices(e)}EnumerateTriangleVertices(e){let t=this.node.GetWorldTransformation();t.IsIdentity()?this.mesh.EnumerateTriangleVertices(e):this.mesh.EnumerateTriangleVertices((n,i,s)=>{let o=t.TransformCoord3D(n),a=t.TransformCoord3D(i),l=t.TransformCoord3D(s);e(o,a,l)})}PropertyGroupCount(){return this.mesh.PropertyGroupCount()}AddPropertyGroup(e){return this.mesh.AddPropertyGroup(e)}GetPropertyGroup(e){return this.mesh.GetPropertyGroup(e)}GetTransformedMesh(){let e=this.node.GetWorldTransformation(),t=this.mesh.Clone();return Mr(t,e),t}};var yg={UNSIGNED_INT:5125,FLOAT:5126},Fl={ARRAY_BUFFER:34962,ELEMENT_ARRAY_BUFFER:34963},Jo=class extends kn{constructor(){super(),this.components={index:{type:yg.UNSIGNED_INT,size:4},number:{type:yg.FLOAT,size:4}}}CanExport(e,t){return e===dn.Text&&t==="gltf"||e===dn.Binary&&t==="glb"}ExportContent(e,t,n,i){t===dn.Text?this.ExportAsciiContent(e,n):t===dn.Binary&&this.ExportBinaryContent(e,n),i()}ExportAsciiContent(e,t){let n=new sn("model.gltf"),i=new sn("model.bin");t.push(n),t.push(i);let s=this.GetMeshData(e),o=this.GetMainBuffer(s),a=this.GetMainJson(e,s);a.buffers.push({uri:i.GetName(),byteLength:o.byteLength});let l=new Map;this.ExportMaterials(e,a,c=>{let u=Rn(c.name);if(l.has(u))return l.get(u);{let h=new sn(u);h.SetBufferContent(c.buffer),t.push(h);let f=a.textures.length;return l.set(u,f),a.images.push({uri:u}),a.textures.push({source:f}),f}}),n.SetTextContent(JSON.stringify(a,null,4)),i.SetBufferContent(o)}ExportBinaryContent(e,t){function n(x){let S=x%4;return S===0?x:x+(4-S)}function i(x,S,I){for(let A=0;A{let S=Rn(x.name),I=Tr(x.name);if(h.has(S))return h.get(S);{let A=l.bufferViews.length,C=l.textures.length;h.set(S,C);let E=x.buffer;return c.push(E),l.bufferViews.push({buffer:0,byteOffset:u,byteLength:E.byteLength}),u+=E.byteLength,l.images.push({bufferView:A,mimeType:"image/"+I}),l.textures.push({source:C}),C}});let f=a.byteLength;for(let x=0;x{let i=qo(n);t.push({name:n.GetName(),buffer:i,offsets:[],sizes:[]})}),t}GetMainBuffer(e){let t=0;for(let i of e)t+=i.buffer.GetByteLength(this.components.index.size,this.components.number.size);let n=new Ji(t,!0);for(let i of e)for(let s=0;s0&&(g.name=y),p.GetTransformation().IsIdentity()||(g.matrix=p.GetTransformation().GetMatrix().Get()),f.push(g),h.push(f.length-1),g.children=[],a(u,g.children,f,p)}}function o(u,h,f,p,g,y){let d=new ki(p.GetId(),g);if(!u.IsMeshInstanceVisible(d))return;let m={mesh:u.MapMeshIndex(g)};y&&(p.GetTransformation().IsIdentity()||(m.matrix=p.GetTransformation().GetMatrix().Get())),f.push(m),h.push(f.length-1)}function a(u,h,f,p){for(let g of p.GetChildNodes())s(u,h,f,g);for(let g of p.GetMeshIndices())o(u,h,f,p,g,!1)}let l={asset:{generator:"https://3dviewer.net",version:"2.0"},scene:0,scenes:[{nodes:[]}],nodes:[],materials:[],meshes:[],buffers:[],bufferViews:[],accessors:[]},c=e.GetModel().GetRootNode();a(e,l.scenes[0].nodes,l.nodes,c);for(let u of t){let h={name:this.GetExportedMeshName(u.name),primitives:[]},f=u.buffer.primitives;for(let p=0;p0&&(_=y.AddBufferView(g.colors.length*this.components.number.size,Fl.ARRAY_BUFFER));let v=y.AddBufferView(g.normals.length*this.components.number.size,Fl.ARRAY_BUFFER),x=null;g.uvs.length>0&&(x=y.AddBufferView(g.uvs.length*this.components.number.size,Fl.ARRAY_BUFFER));let S={attributes:{},mode:4,material:g.material},I=g.GetBounds();l.accessors.push({bufferView:d,byteOffset:0,componentType:this.components.index.type,count:g.indices.length,type:"SCALAR"}),S.indices=l.accessors.length-1,l.accessors.push({bufferView:m,byteOffset:0,componentType:this.components.number.type,count:g.vertices.length/3,min:I.min,max:I.max,type:"VEC3"}),S.attributes.POSITION=l.accessors.length-1,_!==null&&(l.accessors.push({bufferView:_,byteOffset:0,componentType:this.components.number.type,count:g.colors.length/3,type:"VEC3"}),S.attributes.COLOR_0=l.accessors.length-1),l.accessors.push({bufferView:v,byteOffset:0,componentType:this.components.number.type,count:g.normals.length/3,type:"VEC3"}),S.attributes.NORMAL=l.accessors.length-1,x!==null&&(l.accessors.push({bufferView:x,byteOffset:0,componentType:this.components.number.type,count:g.uvs.length/2,type:"VEC2"}),S.attributes.TEXCOORD_0=l.accessors.length-1),h.primitives.push(S)}l.meshes.push(h)}return l}ExportMaterials(e,t,n){function i(s,o,a,l){function c(d,m){return[vr(d.r/255),vr(d.g/255),vr(d.b/255),m]}function u(d){return[vr(d.r/255),vr(d.g/255),vr(d.b/255)]}function h(d,m,_){if(m===null||!m.IsValid())return null;d.images===void 0&&(d.images=[]),d.textures===void 0&&(d.textures=[]);let x={index:_(m)};if(m.HasTransformation()){let S="KHR_texture_transform";d.extensionsUsed===void 0&&(d.extensionsUsed=[]),d.extensionsUsed.indexOf(S)===-1&&d.extensionsUsed.push(S),x.extensions={KHR_texture_transform:{offset:[m.offset.x,-m.offset.y],scale:[m.scale.x,m.scale.y],rotation:-m.rotation}}}return x}let f={name:s.GetExportedMaterialName(a.name),pbrMetallicRoughness:{baseColorFactor:c(a.color,a.opacity)},emissiveFactor:u(a.emissive),doubleSided:!0,alphaMode:"OPAQUE"};a.transparent&&(f.alphaMode="BLEND");let p=h(o,a.diffuseMap,l);if(p!==null&&(a.multiplyDiffuseMap||(f.pbrMetallicRoughness.baseColorFactor=c(new St(255,255,255),a.opacity)),f.pbrMetallicRoughness.baseColorTexture=p),a.type===Wn.Physical){let d=h(o,a.metalnessMap,l);d!==null?f.pbrMetallicRoughness.metallicRoughnessTexture=d:(f.pbrMetallicRoughness.metallicFactor=a.metalness,f.pbrMetallicRoughness.roughnessFactor=a.roughness)}let g=h(o,a.normalMap,l);g!==null&&(f.normalTexture=g);let y=h(o,a.emissiveMap,l);y!==null&&(f.emissiveTexture=y),o.materials.push(f)}for(let s=0;s!0,Fo(e,this)}},$o=class{constructor(e,t){this.model=e,this.settings=t||new Ul,this.visibleMeshes=null,this.meshToVisibleMeshIndex=null}GetModel(){return this.model}MaterialCount(){return this.model.MaterialCount()}GetMaterial(e){return this.model.GetMaterial(e)}VertexCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.VertexCount()}),e}TriangleCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.TriangleCount()}),e}MeshCount(){let e=0;return this.EnumerateMeshes(t=>{e+=1}),e}EnumerateMeshes(e){this.FillVisibleMeshCache();for(let t=0;t{e+=1}),e}EnumerateMeshInstances(e){this.model.EnumerateMeshInstances(t=>{this.settings.isMeshVisible(t.GetId())&&e(t)})}EnumerateTransformedMeshInstances(e){this.EnumerateMeshInstances(t=>{let n=t.GetTransformation();this.settings.transformation.IsIdentity()||n.Append(this.settings.transformation);let s=t.GetMesh().Clone();n.IsIdentity()||Mr(s,n),e(s)})}EnumerateVerticesAndTriangles(e){let t=[];this.EnumerateTransformedMeshInstances(i=>{t.push(i)});for(let i of t)i.EnumerateVertices(s=>{e.onVertex(s.x,s.y,s.z)});let n=0;for(let i of t)i.EnumerateTriangleVertexIndices((s,o,a)=>{e.onTriangle(s+n,o+n,a+n)}),n+=i.VertexCount()}EnumerateTrianglesWithNormals(e){this.EnumerateTransformedMeshInstances(t=>{t.EnumerateTriangleVertices((n,i,s)=>{let o=Ys(n,i,s);e(n,i,s,o)})})}FillVisibleMeshCache(){if(this.visibleMeshes!==null&&this.meshToVisibleMeshIndex!==null)return;this.visibleMeshes=new Set,this.model.EnumerateMeshInstances(t=>{let n=t.GetId();this.settings.isMeshVisible(n)&&this.visibleMeshes.add(n.meshIndex)}),this.meshToVisibleMeshIndex=new Map;let e=0;for(let t=0;tx.GetName()===_)===-1){let x=new sn(_);x.SetBufferContent(d.buffer),m.push(x)}}let o=new sn("model.mtl"),a=new sn("model.obj");n.push(o),n.push(a);let l=new mi;l.WriteLine(this.GetHeaderText());for(let g=0;g{c.WriteArrayLine(["g",this.GetExportedMeshName(g.GetName())]);for(let y=0;y{i.WriteArrayLine(["facet","normal",l.x,l.y,l.z]),i.Indent(1),i.WriteLine("outer loop"),i.Indent(1),i.WriteArrayLine(["vertex",s.x,s.y,s.z]),i.WriteArrayLine(["vertex",o.x,o.y,o.z]),i.WriteArrayLine(["vertex",a.x,a.y,a.z]),i.Indent(-1),i.WriteLine("endloop"),i.Indent(-1),i.WriteLine("endfacet")}),i.WriteLine("endsolid Model"),n.SetTextContent(i.GetText())}ExportBinary(e,t){let n=new sn("model.stl");t.push(n);let i=e.TriangleCount(),s=80,o=s+4+i*50,a=new Ji(o,!0);for(let l=0;l{a.WriteFloat32(h.x),a.WriteFloat32(h.y),a.WriteFloat32(h.z),a.WriteFloat32(l.x),a.WriteFloat32(l.y),a.WriteFloat32(l.z),a.WriteFloat32(c.x),a.WriteFloat32(c.y),a.WriteFloat32(c.z),a.WriteFloat32(u.x),a.WriteFloat32(u.y),a.WriteFloat32(u.z),a.WriteUnsignedInteger16(0)}),n.SetBufferContent(a.GetBuffer())}};var Pu=class{constructor(){this.exporters=[new Qo,new na,new ta,new ea,new Jo,new Yo,new Ko]}AddExporter(e){this.exporters.push(e)}Export(e,t,n,i,s){let o=null;for(let l=0;l{l.length===0?s.onError():s.onSuccess(l)})}};var Ks=class{constructor(e,t){this.min=e,this.max=t}GetMin(){return this.min}GetMax(){return this.max}GetCenter(){return new Ye((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,(this.min.z+this.max.z)/2)}},ia=class{constructor(){this.box=new Ks(new Ye(1/0,1/0,1/0),new Ye(-1/0,-1/0,-1/0)),this.isValid=!1}GetBox(){return this.isValid?this.box:null}AddPoint(e){this.box.min.x=Math.min(this.box.min.x,e.x),this.box.min.y=Math.min(this.box.min.y,e.y),this.box.min.z=Math.min(this.box.min.z,e.z),this.box.max.x=Math.max(this.box.max.x,e.x),this.box.max.y=Math.max(this.box.max.y,e.y),this.box.max.z=Math.max(this.box.max.z,e.z),this.isValid=!0}};var ra=class r{constructor(e,t){this.beg=e,this.end=t}Clone(){return new r(this.beg,this.end)}};function md(r,e){let t=ms(r.end,r.beg),n=ms(e,r.beg),i=Rl(t,n),s=Rl(t,t);if(kr(s))return r.beg.Clone();let o=i/s;return o=Math.max(0,Math.min(1,o)),new Nt(r.beg.x+o*t.x,r.beg.y+o*t.y)}function Nu(r,e){let t=md(r,e);return Hs(t,e)}var kl=class r{constructor(e,t){this.boundingBox=e,this.level=t,this.pointItems=[],this.childNodes=[]}AddPoint(e,t,n){let i=this.FindNodeForPoint(e);if(i===null||i.FindPointDirectly(e)!==null)return!1;if(i.pointItems.length=n.maxTreeDepth)return i.AddPointDirectly(e,t),!0;{i.CreateChildNodes();let s=i.pointItems;i.pointItems=[];for(let o=0;o{e.onFileListProgress(t,this.files.length),this.GetFileContent(this.files[t],{onReady:n,onProgress:e.onFileLoadProgress})},onReady:e.onReady})}ContainsFileByPath(e){return this.FindFileByPath(e)!==null}FindFileByPath(e){let t=Rn(e).toLowerCase();for(let n=0;n{e.SetContent(i)}).catch(()=>{}).finally(()=>{t.onReady()})}};var gd=class{constructor(){this.nextId=0}GenerateId(){let e=this.nextId;return this.nextId+=1,e}},Bn=class{constructor(){this.name="",this.parent=null,this.transformation=new pn,this.childNodes=[],this.meshIndices=[],this.idGenerator=new gd,this.id=this.idGenerator.GenerateId()}IsEmpty(){return this.childNodes.length===0&&this.meshIndices.length===0}IsMeshNode(){return this.childNodes.length===0&&this.meshIndices.length===1}GetId(){return this.id}GetName(){return this.name}SetName(e){this.name=e}HasParent(){return this.parent!==null}GetParent(){return this.parent}GetTransformation(){return this.transformation}GetWorldTransformation(){let e=this.transformation.Clone(),t=this.parent;for(;t!==null;)e.Append(t.transformation),t=t.parent;return e}SetTransformation(e){this.transformation=e}AddChildNode(e){return e.parent=this,e.idGenerator=this.idGenerator,e.id=e.idGenerator.GenerateId(),this.childNodes.push(e),this.childNodes.length-1}RemoveChildNode(e){e.parent=null;let t=this.childNodes.indexOf(e);this.childNodes.splice(t,1)}GetChildNodes(){return this.childNodes}ChildNodeCount(){return this.childNodes.length}GetChildNode(e){return this.childNodes[e]}AddMeshIndex(e){return this.meshIndices.push(e),this.meshIndices.length-1}MeshIndexCount(){return this.meshIndices.length}GetMeshIndex(e){return this.meshIndices[e]}GetMeshIndices(){return this.meshIndices}Enumerate(e){e(this);for(let t of this.childNodes)t.Enumerate(e)}EnumerateChildren(e){for(let t of this.childNodes)e(t),t.EnumerateChildren(e)}EnumerateMeshIndices(e){for(let t of this.meshIndices)e(t);for(let t of this.childNodes)t.EnumerateMeshIndices(e)}};var _s={Unknown:0,Millimeter:1,Centimeter:2,Meter:3,Inch:4,Foot:5};var br=class extends Er{constructor(){super(),this.unit=_s.Unknown,this.root=new Bn,this.materials=[],this.meshes=[]}GetUnit(){return this.unit}SetUnit(e){this.unit=e}GetRootNode(){return this.root}NodeCount(){let e=0;return this.root.Enumerate(t=>{e+=1}),e-1}MaterialCount(){return this.materials.length}MeshCount(){return this.meshes.length}MeshInstanceCount(){let e=0;return this.root.Enumerate(t=>{e+=t.MeshIndexCount()}),e}VertexCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.VertexCount()}),e}VertexColorCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.VertexColorCount()}),e}NormalCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.NormalCount()}),e}TextureUVCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.TextureUVCount()}),e}LineCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.LineCount()}),e}LineSegmentCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.LineSegmentCount()}),e}TriangleCount(){let e=0;return this.EnumerateMeshInstances(t=>{e+=t.TriangleCount()}),e}AddMaterial(e){return this.materials.push(e),this.materials.length-1}GetMaterial(e){return this.materials[e]}AddMesh(e){return this.meshes.push(e),this.meshes.length-1}AddMeshToRootNode(e){let t=this.AddMesh(e);return this.root.AddMeshIndex(t),t}RemoveMesh(e){this.meshes.splice(e,1),this.root.Enumerate(t=>{for(let n=0;ne&&(t.meshIndices[n]-=1)})}GetMesh(e){return this.meshes[e]}GetMeshInstance(e){let t=null;if(this.root.Enumerate(o=>{o.GetId()===e.nodeId&&(t=o)}),t===null||t.GetMeshIndices().indexOf(e.meshIndex)===-1)return null;let i=this.GetMesh(e.meshIndex),s=new ki(t.GetId(),e.meshIndex);return new Hr(s,t,i)}EnumerateMeshes(e){for(let t of this.meshes)e(t)}EnumerateMeshInstances(e){this.root.Enumerate(t=>{for(let n of t.GetMeshIndices()){let i=new ki(t.GetId(),n),s=this.GetMesh(n),o=new Hr(i,t,s);e(o)}})}EnumerateTransformedMeshInstances(e){this.EnumerateMeshInstances(t=>{let n=t.GetTransformedMesh();e(n)})}EnumerateVertices(e){this.EnumerateMeshInstances(t=>{t.EnumerateVertices(e)})}EnumerateTriangleVertexIndices(e){this.EnumerateMeshInstances(t=>{t.EnumerateTriangleVertexIndices(e)})}EnumerateTriangleVertices(e){this.EnumerateMeshInstances(t=>{t.EnumerateTriangleVertices(e)})}};var Bl=class{constructor(){this.edges=[],this.triangles=[]}},Gl=class{constructor(e,t){this.vertex1=e,this.vertex2=t,this.triangles=[]}},Vl=class{constructor(e,t){this.edge=e,this.reversed=t}},zl=class{constructor(){this.triEdge1=null,this.triEdge2=null,this.triEdge3=null}},la=class{constructor(){this.vertices=[],this.edges=[],this.triangleEdges=[],this.triangles=[],this.edgeStartToEndVertexMap=new Map}AddVertex(){return this.vertices.push(new Bl),this.vertices.length-1}AddTriangle(e,t,n){function i(c,u,h){c[u].triangles.push(h)}function s(c,u,h,f){let p=c[h],g=u[f];p.edges.push(g.edge)}function o(c,u,h,f){let p=u[h];c[p.edge].triangles.push(f)}let a=this.triangles.length,l=new zl;l.triEdge1=this.AddTriangleEdge(e,t),l.triEdge2=this.AddTriangleEdge(t,n),l.triEdge3=this.AddTriangleEdge(n,e),i(this.vertices,e,a),i(this.vertices,t,a),i(this.vertices,n,a),s(this.vertices,this.triangleEdges,e,l.triEdge1),s(this.vertices,this.triangleEdges,t,l.triEdge2),s(this.vertices,this.triangleEdges,n,l.triEdge3),o(this.edges,this.triangleEdges,l.triEdge1,a),o(this.edges,this.triangleEdges,l.triEdge2,a),o(this.edges,this.triangleEdges,l.triEdge3,a),this.triangles.push(l)}AddTriangleEdge(e,t){let n=e,i=t,s=!1;t{vs(t)||(e=!1)}),e}function yd(r){let e=new ia;return r.EnumerateVertices(t=>{e.AddPoint(t)}),e.GetBox()}function xd(r){function e(s,o,a){let l=o.FindPoint(s);return l===null&&(l=a.AddVertex(),o.AddPoint(s,l)),l}let t=yd(r),n=new sa(t),i=new la;return r.EnumerateTriangleVertices((s,o,a)=>{let l=e(s,n,i),c=e(o,n,i),u=e(a,n,i);i.AddTriangle(l,c,u)}),i}function vd(r){function e(t,n,i){let s=t.triangles[n],o=t.triangleEdges[s.triEdge1],a=t.triangleEdges[s.triEdge2],l=t.triangleEdges[s.triEdge3];return o.edge===i?o.reversed:a.edge===i?a.reversed:l.edge===i?l.reversed:null}if(r instanceof br){let t=!0;return r.EnumerateMeshInstances(n=>{t&&(t=vd(n))}),t}else{let t=xd(r);for(let n=0;n>8&255]+Zn[r>>16&255]+Zn[r>>24&255]+"-"+Zn[e&255]+Zn[e>>8&255]+"-"+Zn[e>>16&15|64]+Zn[e>>24&255]+"-"+Zn[t&63|128]+Zn[t>>8&255]+"-"+Zn[t>>16&255]+Zn[t>>24&255]+Zn[n&255]+Zn[n>>8&255]+Zn[n>>16&255]+Zn[n>>24&255]).toLowerCase()}function Pt(r,e,t){return Math.max(e,Math.min(t,r))}function Cp(r,e){return(r%e+e)%e}function Xx(r,e,t,n,i){return n+(r-e)*(i-n)/(t-e)}function qx(r,e,t){return r!==e?(t-r)/(e-r):0}function Zl(r,e,t){return(1-t)*r+t*e}function jx(r,e,t,n){return Zl(r,e,1-Math.exp(-t*n))}function Yx(r,e=1){return e-Math.abs(Cp(r,e*2)-e)}function Kx(r,e,t){return r<=e?0:r>=t?1:(r=(r-e)/(t-e),r*r*(3-2*r))}function Zx(r,e,t){return r<=e?0:r>=t?1:(r=(r-e)/(t-e),r*r*r*(r*(r*6-15)+10))}function Jx(r,e){return r+Math.floor(Math.random()*(e-r+1))}function $x(r,e){return r+Math.random()*(e-r)}function Qx(r){return r*(.5-Math.random())}function ev(r){r!==void 0&&(Mg=r);let e=Mg+=1831565813;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}function tv(r){return r*Ea}function nv(r){return r*oo}function iv(r){return(r&r-1)===0&&r!==0}function rv(r){return Math.pow(2,Math.ceil(Math.log(r)/Math.LN2))}function sv(r){return Math.pow(2,Math.floor(Math.log(r)/Math.LN2))}function ov(r,e,t,n,i){let s=Math.cos,o=Math.sin,a=s(t/2),l=o(t/2),c=s((e+n)/2),u=o((e+n)/2),h=s((e-n)/2),f=o((e-n)/2),p=s((n-e)/2),g=o((n-e)/2);switch(i){case"XYX":r.set(a*u,l*h,l*f,a*c);break;case"YZY":r.set(l*f,a*u,l*h,a*c);break;case"ZXZ":r.set(l*h,l*f,a*u,a*c);break;case"XZX":r.set(a*u,l*g,l*p,a*c);break;case"YXY":r.set(l*p,a*u,l*g,a*c);break;case"ZYZ":r.set(l*g,l*p,a*u,a*c);break;default:console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+i)}}function Ma(r,e){switch(e.constructor){case Float32Array:return r;case Uint32Array:return r/4294967295;case Uint16Array:return r/65535;case Uint8Array:return r/255;case Int32Array:return Math.max(r/2147483647,-1);case Int16Array:return Math.max(r/32767,-1);case Int8Array:return Math.max(r/127,-1);default:throw new Error("Invalid component type.")}}function ri(r,e){switch(e.constructor){case Float32Array:return r;case Uint32Array:return Math.round(r*4294967295);case Uint16Array:return Math.round(r*65535);case Uint8Array:return Math.round(r*255);case Int32Array:return Math.round(r*2147483647);case Int16Array:return Math.round(r*32767);case Int8Array:return Math.round(r*127);default:throw new Error("Invalid component type.")}}var yn={DEG2RAD:Ea,RAD2DEG:oo,generateUUID:us,clamp:Pt,euclideanModulo:Cp,mapLinear:Xx,inverseLerp:qx,lerp:Zl,damp:jx,pingpong:Yx,smoothstep:Kx,smootherstep:Zx,randInt:Jx,randFloat:$x,randFloatSpread:Qx,seededRandom:ev,degToRad:tv,radToDeg:nv,isPowerOfTwo:iv,ceilPowerOfTwo:rv,floorPowerOfTwo:sv,setQuaternionFromProperEuler:ov,normalize:ri,denormalize:Ma},Pe=class r{constructor(e=0,t=0){r.prototype.isVector2=!0,this.x=e,this.y=t}get width(){return this.x}set width(e){this.x=e}get height(){return this.y}set height(e){this.y=e}set(e,t){return this.x=e,this.y=t,this}setScalar(e){return this.x=e,this.y=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setComponent(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;default:throw new Error("index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y)}copy(e){return this.x=e.x,this.y=e.y,this}add(e){return this.x+=e.x,this.y+=e.y,this}addScalar(e){return this.x+=e,this.y+=e,this}addVectors(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this}addScaledVector(e,t){return this.x+=e.x*t,this.y+=e.y*t,this}sub(e){return this.x-=e.x,this.y-=e.y,this}subScalar(e){return this.x-=e,this.y-=e,this}subVectors(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this}multiply(e){return this.x*=e.x,this.y*=e.y,this}multiplyScalar(e){return this.x*=e,this.y*=e,this}divide(e){return this.x/=e.x,this.y/=e.y,this}divideScalar(e){return this.multiplyScalar(1/e)}applyMatrix3(e){let t=this.x,n=this.y,i=e.elements;return this.x=i[0]*t+i[3]*n+i[6],this.y=i[1]*t+i[4]*n+i[7],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this}clamp(e,t){return this.x=Pt(this.x,e.x,t.x),this.y=Pt(this.y,e.y,t.y),this}clampScalar(e,t){return this.x=Pt(this.x,e,t),this.y=Pt(this.y,e,t),this}clampLength(e,t){let n=this.length();return this.divideScalar(n||1).multiplyScalar(Pt(n,e,t))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(e){return this.x*e.x+this.y*e.y}cross(e){return this.x*e.y-this.y*e.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(e){let t=Math.sqrt(this.lengthSq()*e.lengthSq());if(t===0)return Math.PI/2;let n=this.dot(e)/t;return Math.acos(Pt(n,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){let t=this.x-e.x,n=this.y-e.y;return t*t+n*n}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this}lerpVectors(e,t,n){return this.x=e.x+(t.x-e.x)*n,this.y=e.y+(t.y-e.y)*n,this}equals(e){return e.x===this.x&&e.y===this.y}fromArray(e,t=0){return this.x=e[t],this.y=e[t+1],this}toArray(e=[],t=0){return e[t]=this.x,e[t+1]=this.y,e}fromBufferAttribute(e,t){return this.x=e.getX(t),this.y=e.getY(t),this}rotateAround(e,t){let n=Math.cos(t),i=Math.sin(t),s=this.x-e.x,o=this.y-e.y;return this.x=s*n-o*i+e.x,this.y=s*i+o*n+e.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}},gt=class r{constructor(e,t,n,i,s,o,a,l,c){r.prototype.isMatrix3=!0,this.elements=[1,0,0,0,1,0,0,0,1],e!==void 0&&this.set(e,t,n,i,s,o,a,l,c)}set(e,t,n,i,s,o,a,l,c){let u=this.elements;return u[0]=e,u[1]=i,u[2]=a,u[3]=t,u[4]=s,u[5]=l,u[6]=n,u[7]=o,u[8]=c,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(e){let t=this.elements,n=e.elements;return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],this}extractBasis(e,t,n){return e.setFromMatrix3Column(this,0),t.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this}setFromMatrix4(e){let t=e.elements;return this.set(t[0],t[4],t[8],t[1],t[5],t[9],t[2],t[6],t[10]),this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,t){let n=e.elements,i=t.elements,s=this.elements,o=n[0],a=n[3],l=n[6],c=n[1],u=n[4],h=n[7],f=n[2],p=n[5],g=n[8],y=i[0],d=i[3],m=i[6],_=i[1],v=i[4],x=i[7],S=i[2],I=i[5],A=i[8];return s[0]=o*y+a*_+l*S,s[3]=o*d+a*v+l*I,s[6]=o*m+a*x+l*A,s[1]=c*y+u*_+h*S,s[4]=c*d+u*v+h*I,s[7]=c*m+u*x+h*A,s[2]=f*y+p*_+g*S,s[5]=f*d+p*v+g*I,s[8]=f*m+p*x+g*A,this}multiplyScalar(e){let t=this.elements;return t[0]*=e,t[3]*=e,t[6]*=e,t[1]*=e,t[4]*=e,t[7]*=e,t[2]*=e,t[5]*=e,t[8]*=e,this}determinant(){let e=this.elements,t=e[0],n=e[1],i=e[2],s=e[3],o=e[4],a=e[5],l=e[6],c=e[7],u=e[8];return t*o*u-t*a*c-n*s*u+n*a*l+i*s*c-i*o*l}invert(){let e=this.elements,t=e[0],n=e[1],i=e[2],s=e[3],o=e[4],a=e[5],l=e[6],c=e[7],u=e[8],h=u*o-a*c,f=a*l-u*s,p=c*s-o*l,g=t*h+n*f+i*p;if(g===0)return this.set(0,0,0,0,0,0,0,0,0);let y=1/g;return e[0]=h*y,e[1]=(i*c-u*n)*y,e[2]=(a*n-i*o)*y,e[3]=f*y,e[4]=(u*t-i*l)*y,e[5]=(i*s-a*t)*y,e[6]=p*y,e[7]=(n*l-c*t)*y,e[8]=(o*t-n*s)*y,this}transpose(){let e,t=this.elements;return e=t[1],t[1]=t[3],t[3]=e,e=t[2],t[2]=t[6],t[6]=e,e=t[5],t[5]=t[7],t[7]=e,this}getNormalMatrix(e){return this.setFromMatrix4(e).invert().transpose()}transposeIntoArray(e){let t=this.elements;return e[0]=t[0],e[1]=t[3],e[2]=t[6],e[3]=t[1],e[4]=t[4],e[5]=t[7],e[6]=t[2],e[7]=t[5],e[8]=t[8],this}setUvTransform(e,t,n,i,s,o,a){let l=Math.cos(s),c=Math.sin(s);return this.set(n*l,n*c,-n*(l*o+c*a)+o+e,-i*c,i*l,-i*(-c*o+l*a)+a+t,0,0,1),this}scale(e,t){return this.premultiply(_d.makeScale(e,t)),this}rotate(e){return this.premultiply(_d.makeRotation(-e)),this}translate(e,t){return this.premultiply(_d.makeTranslation(e,t)),this}makeTranslation(e,t){return e.isVector2?this.set(1,0,e.x,0,1,e.y,0,0,1):this.set(1,0,e,0,1,t,0,0,1),this}makeRotation(e){let t=Math.cos(e),n=Math.sin(e);return this.set(t,-n,0,n,t,0,0,0,1),this}makeScale(e,t){return this.set(e,0,0,0,t,0,0,0,1),this}equals(e){let t=this.elements,n=e.elements;for(let i=0;i<9;i++)if(t[i]!==n[i])return!1;return!0}fromArray(e,t=0){for(let n=0;n<9;n++)this.elements[n]=e[n+t];return this}toArray(e=[],t=0){let n=this.elements;return e[t]=n[0],e[t+1]=n[1],e[t+2]=n[2],e[t+3]=n[3],e[t+4]=n[4],e[t+5]=n[5],e[t+6]=n[6],e[t+7]=n[7],e[t+8]=n[8],e}clone(){return new this.constructor().fromArray(this.elements)}},_d=new gt;function Ip(r){for(let e=r.length-1;e>=0;--e)if(r[e]>=65535)return!0;return!1}function Aa(r){return document.createElementNS("http://www.w3.org/1999/xhtml",r)}function $0(){let r=Aa("canvas");return r.style.display="block",r}var Eg={};function Oc(r){r in Eg||(Eg[r]=!0,console.warn(r))}function Q0(r,e,t){return new Promise(function(n,i){function s(){switch(r.clientWaitSync(e,r.SYNC_FLUSH_COMMANDS_BIT,0)){case r.WAIT_FAILED:i();break;case r.TIMEOUT_EXPIRED:setTimeout(s,t);break;default:n()}}setTimeout(s,t)})}function ey(r){let e=r.elements;e[2]=.5*e[2]+.5*e[3],e[6]=.5*e[6]+.5*e[7],e[10]=.5*e[10]+.5*e[11],e[14]=.5*e[14]+.5*e[15]}function ty(r){let e=r.elements;e[11]===-1?(e[10]=-e[10]-1,e[14]=-e[14]):(e[10]=-e[10],e[14]=-e[14]+1)}var bg=new gt().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),Sg=new gt().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function av(){let r={enabled:!0,workingColorSpace:Ci,spaces:{},convert:function(i,s,o){return this.enabled===!1||s===o||!s||!o||(this.spaces[s].transfer===Kt&&(i.r=Jr(i.r),i.g=Jr(i.g),i.b=Jr(i.b)),this.spaces[s].primaries!==this.spaces[o].primaries&&(i.applyMatrix3(this.spaces[s].toXYZ),i.applyMatrix3(this.spaces[o].fromXYZ)),this.spaces[o].transfer===Kt&&(i.r=ba(i.r),i.g=ba(i.g),i.b=ba(i.b))),i},fromWorkingColorSpace:function(i,s){return this.convert(i,this.workingColorSpace,s)},toWorkingColorSpace:function(i,s){return this.convert(i,s,this.workingColorSpace)},getPrimaries:function(i){return this.spaces[i].primaries},getTransfer:function(i){return i===cs?ec:this.spaces[i].transfer},getLuminanceCoefficients:function(i,s=this.workingColorSpace){return i.fromArray(this.spaces[s].luminanceCoefficients)},define:function(i){Object.assign(this.spaces,i)},_getMatrix:function(i,s,o){return i.copy(this.spaces[s].toXYZ).multiply(this.spaces[o].fromXYZ)},_getDrawingBufferColorSpace:function(i){return this.spaces[i].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(i=this.workingColorSpace){return this.spaces[i].workingColorSpaceConfig.unpackColorSpace}},e=[.64,.33,.3,.6,.15,.06],t=[.2126,.7152,.0722],n=[.3127,.329];return r.define({[Ci]:{primaries:e,whitePoint:n,transfer:ec,toXYZ:bg,fromXYZ:Sg,luminanceCoefficients:t,workingColorSpaceConfig:{unpackColorSpace:yt},outputColorSpaceConfig:{drawingBufferColorSpace:yt}},[yt]:{primaries:e,whitePoint:n,transfer:Kt,toXYZ:bg,fromXYZ:Sg,luminanceCoefficients:t,outputColorSpaceConfig:{drawingBufferColorSpace:yt}}}),r}var mt=av();function Jr(r){return r<.04045?r*.0773993808:Math.pow(r*.9478672986+.0521327014,2.4)}function ba(r){return r<.0031308?r*12.92:1.055*Math.pow(r,.41666)-.055}var ua,ao=class{static getDataURL(e,t="image/png"){if(/^data:/i.test(e.src)||typeof HTMLCanvasElement>"u")return e.src;let n;if(e instanceof HTMLCanvasElement)n=e;else{ua===void 0&&(ua=Aa("canvas")),ua.width=e.width,ua.height=e.height;let i=ua.getContext("2d");e instanceof ImageData?i.putImageData(e,0,0):i.drawImage(e,0,0,e.width,e.height),n=ua}return n.toDataURL(t)}static sRGBToLinear(e){if(typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&e instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&e instanceof ImageBitmap){let t=Aa("canvas");t.width=e.width,t.height=e.height;let n=t.getContext("2d");n.drawImage(e,0,0,e.width,e.height);let i=n.getImageData(0,0,e.width,e.height),s=i.data;for(let o=0;o0&&(n.userData=this.userData),t||(e.textures[this.uuid]=n),n}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(e){if(this.mapping!==yp)return e;if(e.applyMatrix3(this.matrix),e.x<0||e.x>1)switch(this.wrapS){case on:e.x=e.x-Math.floor(e.x);break;case un:e.x=e.x<0?0:1;break;case so:Math.abs(Math.floor(e.x)%2)===1?e.x=Math.ceil(e.x)-e.x:e.x=e.x-Math.floor(e.x);break}if(e.y<0||e.y>1)switch(this.wrapT){case on:e.y=e.y-Math.floor(e.y);break;case un:e.y=e.y<0?0:1;break;case so:Math.abs(Math.floor(e.y)%2)===1?e.y=Math.ceil(e.y)-e.y:e.y=e.y-Math.floor(e.y);break}return this.flipY&&(e.y=1-e.y),e}set needsUpdate(e){e===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(e){e===!0&&this.pmremVersion++}};qn.DEFAULT_IMAGE=null;qn.DEFAULT_MAPPING=yp;qn.DEFAULT_ANISOTROPY=1;var wt=class r{constructor(e=0,t=0,n=0,i=1){r.prototype.isVector4=!0,this.x=e,this.y=t,this.z=n,this.w=i}get width(){return this.z}set width(e){this.z=e}get height(){return this.w}set height(e){this.w=e}set(e,t,n,i){return this.x=e,this.y=t,this.z=n,this.w=i,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this.w=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setW(e){return this.w=e,this}setComponent(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;case 3:this.w=t;break;default:throw new Error("index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w!==void 0?e.w:1,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this.w+=e.w,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this.w+=e,this}addVectors(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this.w=e.w+t.w,this}addScaledVector(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this.w+=e.w*t,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this.w-=e.w,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this.w-=e,this}subVectors(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this.w=e.w-t.w,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this.w*=e.w,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this.w*=e,this}applyMatrix4(e){let t=this.x,n=this.y,i=this.z,s=this.w,o=e.elements;return this.x=o[0]*t+o[4]*n+o[8]*i+o[12]*s,this.y=o[1]*t+o[5]*n+o[9]*i+o[13]*s,this.z=o[2]*t+o[6]*n+o[10]*i+o[14]*s,this.w=o[3]*t+o[7]*n+o[11]*i+o[15]*s,this}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this.w/=e.w,this}divideScalar(e){return this.multiplyScalar(1/e)}setAxisAngleFromQuaternion(e){this.w=2*Math.acos(e.w);let t=Math.sqrt(1-e.w*e.w);return t<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=e.x/t,this.y=e.y/t,this.z=e.z/t),this}setAxisAngleFromRotationMatrix(e){let t,n,i,s,l=e.elements,c=l[0],u=l[4],h=l[8],f=l[1],p=l[5],g=l[9],y=l[2],d=l[6],m=l[10];if(Math.abs(u-f)<.01&&Math.abs(h-y)<.01&&Math.abs(g-d)<.01){if(Math.abs(u+f)<.1&&Math.abs(h+y)<.1&&Math.abs(g+d)<.1&&Math.abs(c+p+m-3)<.1)return this.set(1,0,0,0),this;t=Math.PI;let v=(c+1)/2,x=(p+1)/2,S=(m+1)/2,I=(u+f)/4,A=(h+y)/4,C=(g+d)/4;return v>x&&v>S?v<.01?(n=0,i=.707106781,s=.707106781):(n=Math.sqrt(v),i=I/n,s=A/n):x>S?x<.01?(n=.707106781,i=0,s=.707106781):(i=Math.sqrt(x),n=I/i,s=C/i):S<.01?(n=.707106781,i=.707106781,s=0):(s=Math.sqrt(S),n=A/s,i=C/s),this.set(n,i,s,t),this}let _=Math.sqrt((d-g)*(d-g)+(h-y)*(h-y)+(f-u)*(f-u));return Math.abs(_)<.001&&(_=1),this.x=(d-g)/_,this.y=(h-y)/_,this.z=(f-u)/_,this.w=Math.acos((c+p+m-1)/2),this}setFromMatrixPosition(e){let t=e.elements;return this.x=t[12],this.y=t[13],this.z=t[14],this.w=t[15],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this.w=Math.min(this.w,e.w),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this.w=Math.max(this.w,e.w),this}clamp(e,t){return this.x=Pt(this.x,e.x,t.x),this.y=Pt(this.y,e.y,t.y),this.z=Pt(this.z,e.z,t.z),this.w=Pt(this.w,e.w,t.w),this}clampScalar(e,t){return this.x=Pt(this.x,e,t),this.y=Pt(this.y,e,t),this.z=Pt(this.z,e,t),this.w=Pt(this.w,e,t),this}clampLength(e,t){let n=this.length();return this.divideScalar(n||1).multiplyScalar(Pt(n,e,t))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z+this.w*e.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this.w+=(e.w-this.w)*t,this}lerpVectors(e,t,n){return this.x=e.x+(t.x-e.x)*n,this.y=e.y+(t.y-e.y)*n,this.z=e.z+(t.z-e.z)*n,this.w=e.w+(t.w-e.w)*n,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z&&e.w===this.w}fromArray(e,t=0){return this.x=e[t],this.y=e[t+1],this.z=e[t+2],this.w=e[t+3],this}toArray(e=[],t=0){return e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e[t+3]=this.w,e}fromBufferAttribute(e,t){return this.x=e.getX(t),this.y=e.getY(t),this.z=e.getZ(t),this.w=e.getW(t),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}},mh=class extends Qr{constructor(e=1,t=1,n={}){super(),this.isRenderTarget=!0,this.width=e,this.height=t,this.depth=n.depth?n.depth:1,this.scissor=new wt(0,0,e,t),this.scissorTest=!1,this.viewport=new wt(0,0,e,t);let i={width:e,height:t,depth:this.depth};n=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Pn,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,multiview:!1},n);let s=new qn(i,n.mapping,n.wrapS,n.wrapT,n.magFilter,n.minFilter,n.format,n.type,n.anisotropy,n.colorSpace);s.flipY=!1,s.generateMipmaps=n.generateMipmaps,s.internalFormat=n.internalFormat,this.textures=[];let o=n.count;for(let a=0;a=0?1:-1,v=1-m*m;if(v>Number.EPSILON){let S=Math.sqrt(v),I=Math.atan2(S,m*_);d=Math.sin(d*I)/S,a=Math.sin(a*I)/S}let x=a*_;if(l=l*d+f*x,c=c*d+p*x,u=u*d+g*x,h=h*d+y*x,d===1-a){let S=1/Math.sqrt(l*l+c*c+u*u+h*h);l*=S,c*=S,u*=S,h*=S}}e[t]=l,e[t+1]=c,e[t+2]=u,e[t+3]=h}static multiplyQuaternionsFlat(e,t,n,i,s,o){let a=n[i],l=n[i+1],c=n[i+2],u=n[i+3],h=s[o],f=s[o+1],p=s[o+2],g=s[o+3];return e[t]=a*g+u*h+l*p-c*f,e[t+1]=l*g+u*f+c*h-a*p,e[t+2]=c*g+u*p+a*f-l*h,e[t+3]=u*g-a*h-l*f-c*p,e}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get w(){return this._w}set w(e){this._w=e,this._onChangeCallback()}set(e,t,n,i){return this._x=e,this._y=t,this._z=n,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(e){return this._x=e.x,this._y=e.y,this._z=e.z,this._w=e.w,this._onChangeCallback(),this}setFromEuler(e,t=!0){let n=e._x,i=e._y,s=e._z,o=e._order,a=Math.cos,l=Math.sin,c=a(n/2),u=a(i/2),h=a(s/2),f=l(n/2),p=l(i/2),g=l(s/2);switch(o){case"XYZ":this._x=f*u*h+c*p*g,this._y=c*p*h-f*u*g,this._z=c*u*g+f*p*h,this._w=c*u*h-f*p*g;break;case"YXZ":this._x=f*u*h+c*p*g,this._y=c*p*h-f*u*g,this._z=c*u*g-f*p*h,this._w=c*u*h+f*p*g;break;case"ZXY":this._x=f*u*h-c*p*g,this._y=c*p*h+f*u*g,this._z=c*u*g+f*p*h,this._w=c*u*h-f*p*g;break;case"ZYX":this._x=f*u*h-c*p*g,this._y=c*p*h+f*u*g,this._z=c*u*g-f*p*h,this._w=c*u*h+f*p*g;break;case"YZX":this._x=f*u*h+c*p*g,this._y=c*p*h+f*u*g,this._z=c*u*g-f*p*h,this._w=c*u*h-f*p*g;break;case"XZY":this._x=f*u*h-c*p*g,this._y=c*p*h-f*u*g,this._z=c*u*g+f*p*h,this._w=c*u*h+f*p*g;break;default:console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: "+o)}return t===!0&&this._onChangeCallback(),this}setFromAxisAngle(e,t){let n=t/2,i=Math.sin(n);return this._x=e.x*i,this._y=e.y*i,this._z=e.z*i,this._w=Math.cos(n),this._onChangeCallback(),this}setFromRotationMatrix(e){let t=e.elements,n=t[0],i=t[4],s=t[8],o=t[1],a=t[5],l=t[9],c=t[2],u=t[6],h=t[10],f=n+a+h;if(f>0){let p=.5/Math.sqrt(f+1);this._w=.25/p,this._x=(u-l)*p,this._y=(s-c)*p,this._z=(o-i)*p}else if(n>a&&n>h){let p=2*Math.sqrt(1+n-a-h);this._w=(u-l)/p,this._x=.25*p,this._y=(i+o)/p,this._z=(s+c)/p}else if(a>h){let p=2*Math.sqrt(1+a-n-h);this._w=(s-c)/p,this._x=(i+o)/p,this._y=.25*p,this._z=(l+u)/p}else{let p=2*Math.sqrt(1+h-n-a);this._w=(o-i)/p,this._x=(s+c)/p,this._y=(l+u)/p,this._z=.25*p}return this._onChangeCallback(),this}setFromUnitVectors(e,t){let n=e.dot(t)+1;return nMath.abs(e.z)?(this._x=-e.y,this._y=e.x,this._z=0,this._w=n):(this._x=0,this._y=-e.z,this._z=e.y,this._w=n)):(this._x=e.y*t.z-e.z*t.y,this._y=e.z*t.x-e.x*t.z,this._z=e.x*t.y-e.y*t.x,this._w=n),this.normalize()}angleTo(e){return 2*Math.acos(Math.abs(Pt(this.dot(e),-1,1)))}rotateTowards(e,t){let n=this.angleTo(e);if(n===0)return this;let i=Math.min(1,t/n);return this.slerp(e,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(e){return this._x*e._x+this._y*e._y+this._z*e._z+this._w*e._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let e=this.length();return e===0?(this._x=0,this._y=0,this._z=0,this._w=1):(e=1/e,this._x=this._x*e,this._y=this._y*e,this._z=this._z*e,this._w=this._w*e),this._onChangeCallback(),this}multiply(e){return this.multiplyQuaternions(this,e)}premultiply(e){return this.multiplyQuaternions(e,this)}multiplyQuaternions(e,t){let n=e._x,i=e._y,s=e._z,o=e._w,a=t._x,l=t._y,c=t._z,u=t._w;return this._x=n*u+o*a+i*c-s*l,this._y=i*u+o*l+s*a-n*c,this._z=s*u+o*c+n*l-i*a,this._w=o*u-n*a-i*l-s*c,this._onChangeCallback(),this}slerp(e,t){if(t===0)return this;if(t===1)return this.copy(e);let n=this._x,i=this._y,s=this._z,o=this._w,a=o*e._w+n*e._x+i*e._y+s*e._z;if(a<0?(this._w=-e._w,this._x=-e._x,this._y=-e._y,this._z=-e._z,a=-a):this.copy(e),a>=1)return this._w=o,this._x=n,this._y=i,this._z=s,this;let l=1-a*a;if(l<=Number.EPSILON){let p=1-t;return this._w=p*o+t*this._w,this._x=p*n+t*this._x,this._y=p*i+t*this._y,this._z=p*s+t*this._z,this.normalize(),this}let c=Math.sqrt(l),u=Math.atan2(c,a),h=Math.sin((1-t)*u)/c,f=Math.sin(t*u)/c;return this._w=o*h+this._w*f,this._x=n*h+this._x*f,this._y=i*h+this._y*f,this._z=s*h+this._z*f,this._onChangeCallback(),this}slerpQuaternions(e,t,n){return this.copy(e).slerp(t,n)}random(){let e=2*Math.PI*Math.random(),t=2*Math.PI*Math.random(),n=Math.random(),i=Math.sqrt(1-n),s=Math.sqrt(n);return this.set(i*Math.sin(e),i*Math.cos(e),s*Math.sin(t),s*Math.cos(t))}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._w===this._w}fromArray(e,t=0){return this._x=e[t],this._y=e[t+1],this._z=e[t+2],this._w=e[t+3],this._onChangeCallback(),this}toArray(e=[],t=0){return e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._w,e}fromBufferAttribute(e,t){return this._x=e.getX(t),this._y=e.getY(t),this._z=e.getZ(t),this._w=e.getW(t),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}},se=class r{constructor(e=0,t=0,n=0){r.prototype.isVector3=!0,this.x=e,this.y=t,this.z=n}set(e,t,n){return n===void 0&&(n=this.z),this.x=e,this.y=t,this.z=n,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setComponent(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;default:throw new Error("index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this}addVectors(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this}addScaledVector(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this}subVectors(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this}multiplyVectors(e,t){return this.x=e.x*t.x,this.y=e.y*t.y,this.z=e.z*t.z,this}applyEuler(e){return this.applyQuaternion(Ag.setFromEuler(e))}applyAxisAngle(e,t){return this.applyQuaternion(Ag.setFromAxisAngle(e,t))}applyMatrix3(e){let t=this.x,n=this.y,i=this.z,s=e.elements;return this.x=s[0]*t+s[3]*n+s[6]*i,this.y=s[1]*t+s[4]*n+s[7]*i,this.z=s[2]*t+s[5]*n+s[8]*i,this}applyNormalMatrix(e){return this.applyMatrix3(e).normalize()}applyMatrix4(e){let t=this.x,n=this.y,i=this.z,s=e.elements,o=1/(s[3]*t+s[7]*n+s[11]*i+s[15]);return this.x=(s[0]*t+s[4]*n+s[8]*i+s[12])*o,this.y=(s[1]*t+s[5]*n+s[9]*i+s[13])*o,this.z=(s[2]*t+s[6]*n+s[10]*i+s[14])*o,this}applyQuaternion(e){let t=this.x,n=this.y,i=this.z,s=e.x,o=e.y,a=e.z,l=e.w,c=2*(o*i-a*n),u=2*(a*t-s*i),h=2*(s*n-o*t);return this.x=t+l*c+o*h-a*u,this.y=n+l*u+a*c-s*h,this.z=i+l*h+s*u-o*c,this}project(e){return this.applyMatrix4(e.matrixWorldInverse).applyMatrix4(e.projectionMatrix)}unproject(e){return this.applyMatrix4(e.projectionMatrixInverse).applyMatrix4(e.matrixWorld)}transformDirection(e){let t=this.x,n=this.y,i=this.z,s=e.elements;return this.x=s[0]*t+s[4]*n+s[8]*i,this.y=s[1]*t+s[5]*n+s[9]*i,this.z=s[2]*t+s[6]*n+s[10]*i,this.normalize()}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this}divideScalar(e){return this.multiplyScalar(1/e)}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this}clamp(e,t){return this.x=Pt(this.x,e.x,t.x),this.y=Pt(this.y,e.y,t.y),this.z=Pt(this.z,e.z,t.z),this}clampScalar(e,t){return this.x=Pt(this.x,e,t),this.y=Pt(this.y,e,t),this.z=Pt(this.z,e,t),this}clampLength(e,t){let n=this.length();return this.divideScalar(n||1).multiplyScalar(Pt(n,e,t))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this}lerpVectors(e,t,n){return this.x=e.x+(t.x-e.x)*n,this.y=e.y+(t.y-e.y)*n,this.z=e.z+(t.z-e.z)*n,this}cross(e){return this.crossVectors(this,e)}crossVectors(e,t){let n=e.x,i=e.y,s=e.z,o=t.x,a=t.y,l=t.z;return this.x=i*l-s*a,this.y=s*o-n*l,this.z=n*a-i*o,this}projectOnVector(e){let t=e.lengthSq();if(t===0)return this.set(0,0,0);let n=e.dot(this)/t;return this.copy(e).multiplyScalar(n)}projectOnPlane(e){return Md.copy(this).projectOnVector(e),this.sub(Md)}reflect(e){return this.sub(Md.copy(e).multiplyScalar(2*this.dot(e)))}angleTo(e){let t=Math.sqrt(this.lengthSq()*e.lengthSq());if(t===0)return Math.PI/2;let n=this.dot(e)/t;return Math.acos(Pt(n,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){let t=this.x-e.x,n=this.y-e.y,i=this.z-e.z;return t*t+n*n+i*i}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)+Math.abs(this.z-e.z)}setFromSpherical(e){return this.setFromSphericalCoords(e.radius,e.phi,e.theta)}setFromSphericalCoords(e,t,n){let i=Math.sin(t)*e;return this.x=i*Math.sin(n),this.y=Math.cos(t)*e,this.z=i*Math.cos(n),this}setFromCylindrical(e){return this.setFromCylindricalCoords(e.radius,e.theta,e.y)}setFromCylindricalCoords(e,t,n){return this.x=e*Math.sin(t),this.y=n,this.z=e*Math.cos(t),this}setFromMatrixPosition(e){let t=e.elements;return this.x=t[12],this.y=t[13],this.z=t[14],this}setFromMatrixScale(e){let t=this.setFromMatrixColumn(e,0).length(),n=this.setFromMatrixColumn(e,1).length(),i=this.setFromMatrixColumn(e,2).length();return this.x=t,this.y=n,this.z=i,this}setFromMatrixColumn(e,t){return this.fromArray(e.elements,t*4)}setFromMatrix3Column(e,t){return this.fromArray(e.elements,t*3)}setFromEuler(e){return this.x=e._x,this.y=e._y,this.z=e._z,this}setFromColor(e){return this.x=e.r,this.y=e.g,this.z=e.b,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z}fromArray(e,t=0){return this.x=e[t],this.y=e[t+1],this.z=e[t+2],this}toArray(e=[],t=0){return e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e}fromBufferAttribute(e,t){return this.x=e.getX(t),this.y=e.getY(t),this.z=e.getZ(t),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){let e=Math.random()*Math.PI*2,t=Math.random()*2-1,n=Math.sqrt(1-t*t);return this.x=n*Math.cos(e),this.y=t,this.z=n*Math.sin(e),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}},Md=new se,Ag=new an,Gi=class{constructor(e=new se(1/0,1/0,1/0),t=new se(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=e,this.max=t}set(e,t){return this.min.copy(e),this.max.copy(t),this}setFromArray(e){this.makeEmpty();for(let t=0,n=e.length;t=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y&&e.z>=this.min.z&&e.z<=this.max.z}containsBox(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y&&this.min.z<=e.min.z&&e.max.z<=this.max.z}getParameter(e,t){return t.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y),(e.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(e){return e.max.x>=this.min.x&&e.min.x<=this.max.x&&e.max.y>=this.min.y&&e.min.y<=this.max.y&&e.max.z>=this.min.z&&e.min.z<=this.max.z}intersectsSphere(e){return this.clampPoint(e.center,Qi),Qi.distanceToSquared(e.center)<=e.radius*e.radius}intersectsPlane(e){let t,n;return e.normal.x>0?(t=e.normal.x*this.min.x,n=e.normal.x*this.max.x):(t=e.normal.x*this.max.x,n=e.normal.x*this.min.x),e.normal.y>0?(t+=e.normal.y*this.min.y,n+=e.normal.y*this.max.y):(t+=e.normal.y*this.max.y,n+=e.normal.y*this.min.y),e.normal.z>0?(t+=e.normal.z*this.min.z,n+=e.normal.z*this.max.z):(t+=e.normal.z*this.max.z,n+=e.normal.z*this.min.z),t<=-e.constant&&n>=-e.constant}intersectsTriangle(e){if(this.isEmpty())return!1;this.getCenter(Hl),Uu.subVectors(this.max,Hl),ha.subVectors(e.a,Hl),fa.subVectors(e.b,Hl),da.subVectors(e.c,Hl),Ts.subVectors(fa,ha),Ms.subVectors(da,fa),$s.subVectors(ha,da);let t=[0,-Ts.z,Ts.y,0,-Ms.z,Ms.y,0,-$s.z,$s.y,Ts.z,0,-Ts.x,Ms.z,0,-Ms.x,$s.z,0,-$s.x,-Ts.y,Ts.x,0,-Ms.y,Ms.x,0,-$s.y,$s.x,0];return!Ed(t,ha,fa,da,Uu)||(t=[1,0,0,0,1,0,0,0,1],!Ed(t,ha,fa,da,Uu))?!1:(ku.crossVectors(Ts,Ms),t=[ku.x,ku.y,ku.z],Ed(t,ha,fa,da,Uu))}clampPoint(e,t){return t.copy(e).clamp(this.min,this.max)}distanceToPoint(e){return this.clampPoint(e,Qi).distanceTo(e)}getBoundingSphere(e){return this.isEmpty()?e.makeEmpty():(this.getCenter(e.center),e.radius=this.getSize(Qi).length()*.5),e}intersect(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this}union(e){return this.min.min(e.min),this.max.max(e.max),this}applyMatrix4(e){return this.isEmpty()?this:(Wr[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(e),Wr[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(e),Wr[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(e),Wr[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(e),Wr[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(e),Wr[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(e),Wr[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(e),Wr[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(e),this.setFromPoints(Wr),this)}translate(e){return this.min.add(e),this.max.add(e),this}equals(e){return e.min.equals(this.min)&&e.max.equals(this.max)}},Wr=[new se,new se,new se,new se,new se,new se,new se,new se],Qi=new se,Fu=new Gi,ha=new se,fa=new se,da=new se,Ts=new se,Ms=new se,$s=new se,Hl=new se,Uu=new se,ku=new se,Qs=new se;function Ed(r,e,t,n,i){for(let s=0,o=r.length-3;s<=o;s+=3){Qs.fromArray(r,s);let a=i.x*Math.abs(Qs.x)+i.y*Math.abs(Qs.y)+i.z*Math.abs(Qs.z),l=e.dot(Qs),c=t.dot(Qs),u=n.dot(Qs);if(Math.max(-Math.max(l,c,u),Math.min(l,c,u))>a)return!1}return!0}var uv=new Gi,Wl=new se,bd=new se,Vi=class{constructor(e=new se,t=-1){this.isSphere=!0,this.center=e,this.radius=t}set(e,t){return this.center.copy(e),this.radius=t,this}setFromPoints(e,t){let n=this.center;t!==void 0?n.copy(t):uv.setFromPoints(e).getCenter(n);let i=0;for(let s=0,o=e.length;sthis.radius*this.radius&&(t.sub(this.center).normalize(),t.multiplyScalar(this.radius).add(this.center)),t}getBoundingBox(e){return this.isEmpty()?(e.makeEmpty(),e):(e.set(this.center,this.center),e.expandByScalar(this.radius),e)}applyMatrix4(e){return this.center.applyMatrix4(e),this.radius=this.radius*e.getMaxScaleOnAxis(),this}translate(e){return this.center.add(e),this}expandByPoint(e){if(this.isEmpty())return this.center.copy(e),this.radius=0,this;Wl.subVectors(e,this.center);let t=Wl.lengthSq();if(t>this.radius*this.radius){let n=Math.sqrt(t),i=(n-this.radius)*.5;this.center.addScaledVector(Wl,i/n),this.radius+=i}return this}union(e){return e.isEmpty()?this:this.isEmpty()?(this.copy(e),this):(this.center.equals(e.center)===!0?this.radius=Math.max(this.radius,e.radius):(bd.subVectors(e.center,this.center).setLength(e.radius),this.expandByPoint(Wl.copy(e.center).add(bd)),this.expandByPoint(Wl.copy(e.center).sub(bd))),this)}equals(e){return e.center.equals(this.center)&&e.radius===this.radius}clone(){return new this.constructor().copy(this)}},Xr=new se,Sd=new se,Bu=new se,Es=new se,Ad=new se,Gu=new se,wd=new se,Cs=class{constructor(e=new se,t=new se(0,0,-1)){this.origin=e,this.direction=t}set(e,t){return this.origin.copy(e),this.direction.copy(t),this}copy(e){return this.origin.copy(e.origin),this.direction.copy(e.direction),this}at(e,t){return t.copy(this.origin).addScaledVector(this.direction,e)}lookAt(e){return this.direction.copy(e).sub(this.origin).normalize(),this}recast(e){return this.origin.copy(this.at(e,Xr)),this}closestPointToPoint(e,t){t.subVectors(e,this.origin);let n=t.dot(this.direction);return n<0?t.copy(this.origin):t.copy(this.origin).addScaledVector(this.direction,n)}distanceToPoint(e){return Math.sqrt(this.distanceSqToPoint(e))}distanceSqToPoint(e){let t=Xr.subVectors(e,this.origin).dot(this.direction);return t<0?this.origin.distanceToSquared(e):(Xr.copy(this.origin).addScaledVector(this.direction,t),Xr.distanceToSquared(e))}distanceSqToSegment(e,t,n,i){Sd.copy(e).add(t).multiplyScalar(.5),Bu.copy(t).sub(e).normalize(),Es.copy(this.origin).sub(Sd);let s=e.distanceTo(t)*.5,o=-this.direction.dot(Bu),a=Es.dot(this.direction),l=-Es.dot(Bu),c=Es.lengthSq(),u=Math.abs(1-o*o),h,f,p,g;if(u>0)if(h=o*l-a,f=o*a-l,g=s*u,h>=0)if(f>=-g)if(f<=g){let y=1/u;h*=y,f*=y,p=h*(h+o*f+2*a)+f*(o*h+f+2*l)+c}else f=s,h=Math.max(0,-(o*f+a)),p=-h*h+f*(f+2*l)+c;else f=-s,h=Math.max(0,-(o*f+a)),p=-h*h+f*(f+2*l)+c;else f<=-g?(h=Math.max(0,-(-o*s+a)),f=h>0?-s:Math.min(Math.max(-s,-l),s),p=-h*h+f*(f+2*l)+c):f<=g?(h=0,f=Math.min(Math.max(-s,-l),s),p=f*(f+2*l)+c):(h=Math.max(0,-(o*s+a)),f=h>0?s:Math.min(Math.max(-s,-l),s),p=-h*h+f*(f+2*l)+c);else f=o>0?-s:s,h=Math.max(0,-(o*f+a)),p=-h*h+f*(f+2*l)+c;return n&&n.copy(this.origin).addScaledVector(this.direction,h),i&&i.copy(Sd).addScaledVector(Bu,f),p}intersectSphere(e,t){Xr.subVectors(e.center,this.origin);let n=Xr.dot(this.direction),i=Xr.dot(Xr)-n*n,s=e.radius*e.radius;if(i>s)return null;let o=Math.sqrt(s-i),a=n-o,l=n+o;return l<0?null:a<0?this.at(l,t):this.at(a,t)}intersectsSphere(e){return this.distanceSqToPoint(e.center)<=e.radius*e.radius}distanceToPlane(e){let t=e.normal.dot(this.direction);if(t===0)return e.distanceToPoint(this.origin)===0?0:null;let n=-(this.origin.dot(e.normal)+e.constant)/t;return n>=0?n:null}intersectPlane(e,t){let n=this.distanceToPlane(e);return n===null?null:this.at(n,t)}intersectsPlane(e){let t=e.distanceToPoint(this.origin);return t===0||e.normal.dot(this.direction)*t<0}intersectBox(e,t){let n,i,s,o,a,l,c=1/this.direction.x,u=1/this.direction.y,h=1/this.direction.z,f=this.origin;return c>=0?(n=(e.min.x-f.x)*c,i=(e.max.x-f.x)*c):(n=(e.max.x-f.x)*c,i=(e.min.x-f.x)*c),u>=0?(s=(e.min.y-f.y)*u,o=(e.max.y-f.y)*u):(s=(e.max.y-f.y)*u,o=(e.min.y-f.y)*u),n>o||s>i||((s>n||isNaN(n))&&(n=s),(o=0?(a=(e.min.z-f.z)*h,l=(e.max.z-f.z)*h):(a=(e.max.z-f.z)*h,l=(e.min.z-f.z)*h),n>l||a>i)||((a>n||n!==n)&&(n=a),(l=0?n:i,t)}intersectsBox(e){return this.intersectBox(e,Xr)!==null}intersectTriangle(e,t,n,i,s){Ad.subVectors(t,e),Gu.subVectors(n,e),wd.crossVectors(Ad,Gu);let o=this.direction.dot(wd),a;if(o>0){if(i)return null;a=1}else if(o<0)a=-1,o=-o;else return null;Es.subVectors(this.origin,e);let l=a*this.direction.dot(Gu.crossVectors(Es,Gu));if(l<0)return null;let c=a*this.direction.dot(Ad.cross(Es));if(c<0||l+c>o)return null;let u=-a*Es.dot(wd);return u<0?null:this.at(u/o,s)}applyMatrix4(e){return this.origin.applyMatrix4(e),this.direction.transformDirection(e),this}equals(e){return e.origin.equals(this.origin)&&e.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}},Je=class r{constructor(e,t,n,i,s,o,a,l,c,u,h,f,p,g,y,d){r.prototype.isMatrix4=!0,this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],e!==void 0&&this.set(e,t,n,i,s,o,a,l,c,u,h,f,p,g,y,d)}set(e,t,n,i,s,o,a,l,c,u,h,f,p,g,y,d){let m=this.elements;return m[0]=e,m[4]=t,m[8]=n,m[12]=i,m[1]=s,m[5]=o,m[9]=a,m[13]=l,m[2]=c,m[6]=u,m[10]=h,m[14]=f,m[3]=p,m[7]=g,m[11]=y,m[15]=d,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}clone(){return new r().fromArray(this.elements)}copy(e){let t=this.elements,n=e.elements;return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],this}copyPosition(e){let t=this.elements,n=e.elements;return t[12]=n[12],t[13]=n[13],t[14]=n[14],this}setFromMatrix3(e){let t=e.elements;return this.set(t[0],t[3],t[6],0,t[1],t[4],t[7],0,t[2],t[5],t[8],0,0,0,0,1),this}extractBasis(e,t,n){return e.setFromMatrixColumn(this,0),t.setFromMatrixColumn(this,1),n.setFromMatrixColumn(this,2),this}makeBasis(e,t,n){return this.set(e.x,t.x,n.x,0,e.y,t.y,n.y,0,e.z,t.z,n.z,0,0,0,0,1),this}extractRotation(e){let t=this.elements,n=e.elements,i=1/pa.setFromMatrixColumn(e,0).length(),s=1/pa.setFromMatrixColumn(e,1).length(),o=1/pa.setFromMatrixColumn(e,2).length();return t[0]=n[0]*i,t[1]=n[1]*i,t[2]=n[2]*i,t[3]=0,t[4]=n[4]*s,t[5]=n[5]*s,t[6]=n[6]*s,t[7]=0,t[8]=n[8]*o,t[9]=n[9]*o,t[10]=n[10]*o,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this}makeRotationFromEuler(e){let t=this.elements,n=e.x,i=e.y,s=e.z,o=Math.cos(n),a=Math.sin(n),l=Math.cos(i),c=Math.sin(i),u=Math.cos(s),h=Math.sin(s);if(e.order==="XYZ"){let f=o*u,p=o*h,g=a*u,y=a*h;t[0]=l*u,t[4]=-l*h,t[8]=c,t[1]=p+g*c,t[5]=f-y*c,t[9]=-a*l,t[2]=y-f*c,t[6]=g+p*c,t[10]=o*l}else if(e.order==="YXZ"){let f=l*u,p=l*h,g=c*u,y=c*h;t[0]=f+y*a,t[4]=g*a-p,t[8]=o*c,t[1]=o*h,t[5]=o*u,t[9]=-a,t[2]=p*a-g,t[6]=y+f*a,t[10]=o*l}else if(e.order==="ZXY"){let f=l*u,p=l*h,g=c*u,y=c*h;t[0]=f-y*a,t[4]=-o*h,t[8]=g+p*a,t[1]=p+g*a,t[5]=o*u,t[9]=y-f*a,t[2]=-o*c,t[6]=a,t[10]=o*l}else if(e.order==="ZYX"){let f=o*u,p=o*h,g=a*u,y=a*h;t[0]=l*u,t[4]=g*c-p,t[8]=f*c+y,t[1]=l*h,t[5]=y*c+f,t[9]=p*c-g,t[2]=-c,t[6]=a*l,t[10]=o*l}else if(e.order==="YZX"){let f=o*l,p=o*c,g=a*l,y=a*c;t[0]=l*u,t[4]=y-f*h,t[8]=g*h+p,t[1]=h,t[5]=o*u,t[9]=-a*u,t[2]=-c*u,t[6]=p*h+g,t[10]=f-y*h}else if(e.order==="XZY"){let f=o*l,p=o*c,g=a*l,y=a*c;t[0]=l*u,t[4]=-h,t[8]=c*u,t[1]=f*h+y,t[5]=o*u,t[9]=p*h-g,t[2]=g*h-p,t[6]=a*u,t[10]=y*h+f}return t[3]=0,t[7]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this}makeRotationFromQuaternion(e){return this.compose(hv,e,fv)}lookAt(e,t,n){let i=this.elements;return Si.subVectors(e,t),Si.lengthSq()===0&&(Si.z=1),Si.normalize(),bs.crossVectors(n,Si),bs.lengthSq()===0&&(Math.abs(n.z)===1?Si.x+=1e-4:Si.z+=1e-4,Si.normalize(),bs.crossVectors(n,Si)),bs.normalize(),Vu.crossVectors(Si,bs),i[0]=bs.x,i[4]=Vu.x,i[8]=Si.x,i[1]=bs.y,i[5]=Vu.y,i[9]=Si.y,i[2]=bs.z,i[6]=Vu.z,i[10]=Si.z,this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,t){let n=e.elements,i=t.elements,s=this.elements,o=n[0],a=n[4],l=n[8],c=n[12],u=n[1],h=n[5],f=n[9],p=n[13],g=n[2],y=n[6],d=n[10],m=n[14],_=n[3],v=n[7],x=n[11],S=n[15],I=i[0],A=i[4],C=i[8],E=i[12],b=i[1],R=i[5],N=i[9],B=i[13],V=i[2],ie=i[6],Y=i[10],te=i[14],q=i[3],le=i[7],ye=i[11],Te=i[15];return s[0]=o*I+a*b+l*V+c*q,s[4]=o*A+a*R+l*ie+c*le,s[8]=o*C+a*N+l*Y+c*ye,s[12]=o*E+a*B+l*te+c*Te,s[1]=u*I+h*b+f*V+p*q,s[5]=u*A+h*R+f*ie+p*le,s[9]=u*C+h*N+f*Y+p*ye,s[13]=u*E+h*B+f*te+p*Te,s[2]=g*I+y*b+d*V+m*q,s[6]=g*A+y*R+d*ie+m*le,s[10]=g*C+y*N+d*Y+m*ye,s[14]=g*E+y*B+d*te+m*Te,s[3]=_*I+v*b+x*V+S*q,s[7]=_*A+v*R+x*ie+S*le,s[11]=_*C+v*N+x*Y+S*ye,s[15]=_*E+v*B+x*te+S*Te,this}multiplyScalar(e){let t=this.elements;return t[0]*=e,t[4]*=e,t[8]*=e,t[12]*=e,t[1]*=e,t[5]*=e,t[9]*=e,t[13]*=e,t[2]*=e,t[6]*=e,t[10]*=e,t[14]*=e,t[3]*=e,t[7]*=e,t[11]*=e,t[15]*=e,this}determinant(){let e=this.elements,t=e[0],n=e[4],i=e[8],s=e[12],o=e[1],a=e[5],l=e[9],c=e[13],u=e[2],h=e[6],f=e[10],p=e[14],g=e[3],y=e[7],d=e[11],m=e[15];return g*(+s*l*h-i*c*h-s*a*f+n*c*f+i*a*p-n*l*p)+y*(+t*l*p-t*c*f+s*o*f-i*o*p+i*c*u-s*l*u)+d*(+t*c*h-t*a*p-s*o*h+n*o*p+s*a*u-n*c*u)+m*(-i*a*u-t*l*h+t*a*f+i*o*h-n*o*f+n*l*u)}transpose(){let e=this.elements,t;return t=e[1],e[1]=e[4],e[4]=t,t=e[2],e[2]=e[8],e[8]=t,t=e[6],e[6]=e[9],e[9]=t,t=e[3],e[3]=e[12],e[12]=t,t=e[7],e[7]=e[13],e[13]=t,t=e[11],e[11]=e[14],e[14]=t,this}setPosition(e,t,n){let i=this.elements;return e.isVector3?(i[12]=e.x,i[13]=e.y,i[14]=e.z):(i[12]=e,i[13]=t,i[14]=n),this}invert(){let e=this.elements,t=e[0],n=e[1],i=e[2],s=e[3],o=e[4],a=e[5],l=e[6],c=e[7],u=e[8],h=e[9],f=e[10],p=e[11],g=e[12],y=e[13],d=e[14],m=e[15],_=h*d*c-y*f*c+y*l*p-a*d*p-h*l*m+a*f*m,v=g*f*c-u*d*c-g*l*p+o*d*p+u*l*m-o*f*m,x=u*y*c-g*h*c+g*a*p-o*y*p-u*a*m+o*h*m,S=g*h*l-u*y*l-g*a*f+o*y*f+u*a*d-o*h*d,I=t*_+n*v+i*x+s*S;if(I===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);let A=1/I;return e[0]=_*A,e[1]=(y*f*s-h*d*s-y*i*p+n*d*p+h*i*m-n*f*m)*A,e[2]=(a*d*s-y*l*s+y*i*c-n*d*c-a*i*m+n*l*m)*A,e[3]=(h*l*s-a*f*s-h*i*c+n*f*c+a*i*p-n*l*p)*A,e[4]=v*A,e[5]=(u*d*s-g*f*s+g*i*p-t*d*p-u*i*m+t*f*m)*A,e[6]=(g*l*s-o*d*s-g*i*c+t*d*c+o*i*m-t*l*m)*A,e[7]=(o*f*s-u*l*s+u*i*c-t*f*c-o*i*p+t*l*p)*A,e[8]=x*A,e[9]=(g*h*s-u*y*s-g*n*p+t*y*p+u*n*m-t*h*m)*A,e[10]=(o*y*s-g*a*s+g*n*c-t*y*c-o*n*m+t*a*m)*A,e[11]=(u*a*s-o*h*s-u*n*c+t*h*c+o*n*p-t*a*p)*A,e[12]=S*A,e[13]=(u*y*i-g*h*i+g*n*f-t*y*f-u*n*d+t*h*d)*A,e[14]=(g*a*i-o*y*i-g*n*l+t*y*l+o*n*d-t*a*d)*A,e[15]=(o*h*i-u*a*i+u*n*l-t*h*l-o*n*f+t*a*f)*A,this}scale(e){let t=this.elements,n=e.x,i=e.y,s=e.z;return t[0]*=n,t[4]*=i,t[8]*=s,t[1]*=n,t[5]*=i,t[9]*=s,t[2]*=n,t[6]*=i,t[10]*=s,t[3]*=n,t[7]*=i,t[11]*=s,this}getMaxScaleOnAxis(){let e=this.elements,t=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],n=e[4]*e[4]+e[5]*e[5]+e[6]*e[6],i=e[8]*e[8]+e[9]*e[9]+e[10]*e[10];return Math.sqrt(Math.max(t,n,i))}makeTranslation(e,t,n){return e.isVector3?this.set(1,0,0,e.x,0,1,0,e.y,0,0,1,e.z,0,0,0,1):this.set(1,0,0,e,0,1,0,t,0,0,1,n,0,0,0,1),this}makeRotationX(e){let t=Math.cos(e),n=Math.sin(e);return this.set(1,0,0,0,0,t,-n,0,0,n,t,0,0,0,0,1),this}makeRotationY(e){let t=Math.cos(e),n=Math.sin(e);return this.set(t,0,n,0,0,1,0,0,-n,0,t,0,0,0,0,1),this}makeRotationZ(e){let t=Math.cos(e),n=Math.sin(e);return this.set(t,-n,0,0,n,t,0,0,0,0,1,0,0,0,0,1),this}makeRotationAxis(e,t){let n=Math.cos(t),i=Math.sin(t),s=1-n,o=e.x,a=e.y,l=e.z,c=s*o,u=s*a;return this.set(c*o+n,c*a-i*l,c*l+i*a,0,c*a+i*l,u*a+n,u*l-i*o,0,c*l-i*a,u*l+i*o,s*l*l+n,0,0,0,0,1),this}makeScale(e,t,n){return this.set(e,0,0,0,0,t,0,0,0,0,n,0,0,0,0,1),this}makeShear(e,t,n,i,s,o){return this.set(1,n,s,0,e,1,o,0,t,i,1,0,0,0,0,1),this}compose(e,t,n){let i=this.elements,s=t._x,o=t._y,a=t._z,l=t._w,c=s+s,u=o+o,h=a+a,f=s*c,p=s*u,g=s*h,y=o*u,d=o*h,m=a*h,_=l*c,v=l*u,x=l*h,S=n.x,I=n.y,A=n.z;return i[0]=(1-(y+m))*S,i[1]=(p+x)*S,i[2]=(g-v)*S,i[3]=0,i[4]=(p-x)*I,i[5]=(1-(f+m))*I,i[6]=(d+_)*I,i[7]=0,i[8]=(g+v)*A,i[9]=(d-_)*A,i[10]=(1-(f+y))*A,i[11]=0,i[12]=e.x,i[13]=e.y,i[14]=e.z,i[15]=1,this}decompose(e,t,n){let i=this.elements,s=pa.set(i[0],i[1],i[2]).length(),o=pa.set(i[4],i[5],i[6]).length(),a=pa.set(i[8],i[9],i[10]).length();this.determinant()<0&&(s=-s),e.x=i[12],e.y=i[13],e.z=i[14],er.copy(this);let c=1/s,u=1/o,h=1/a;return er.elements[0]*=c,er.elements[1]*=c,er.elements[2]*=c,er.elements[4]*=u,er.elements[5]*=u,er.elements[6]*=u,er.elements[8]*=h,er.elements[9]*=h,er.elements[10]*=h,t.setFromRotationMatrix(er),n.x=s,n.y=o,n.z=a,this}makePerspective(e,t,n,i,s,o,a=Ar){let l=this.elements,c=2*s/(t-e),u=2*s/(n-i),h=(t+e)/(t-e),f=(n+i)/(n-i),p,g;if(a===Ar)p=-(o+s)/(o-s),g=-2*o*s/(o-s);else if(a===tc)p=-o/(o-s),g=-o*s/(o-s);else throw new Error("THREE.Matrix4.makePerspective(): Invalid coordinate system: "+a);return l[0]=c,l[4]=0,l[8]=h,l[12]=0,l[1]=0,l[5]=u,l[9]=f,l[13]=0,l[2]=0,l[6]=0,l[10]=p,l[14]=g,l[3]=0,l[7]=0,l[11]=-1,l[15]=0,this}makeOrthographic(e,t,n,i,s,o,a=Ar){let l=this.elements,c=1/(t-e),u=1/(n-i),h=1/(o-s),f=(t+e)*c,p=(n+i)*u,g,y;if(a===Ar)g=(o+s)*h,y=-2*h;else if(a===tc)g=s*h,y=-1*h;else throw new Error("THREE.Matrix4.makeOrthographic(): Invalid coordinate system: "+a);return l[0]=2*c,l[4]=0,l[8]=0,l[12]=-f,l[1]=0,l[5]=2*u,l[9]=0,l[13]=-p,l[2]=0,l[6]=0,l[10]=y,l[14]=-g,l[3]=0,l[7]=0,l[11]=0,l[15]=1,this}equals(e){let t=this.elements,n=e.elements;for(let i=0;i<16;i++)if(t[i]!==n[i])return!1;return!0}fromArray(e,t=0){for(let n=0;n<16;n++)this.elements[n]=e[n+t];return this}toArray(e=[],t=0){let n=this.elements;return e[t]=n[0],e[t+1]=n[1],e[t+2]=n[2],e[t+3]=n[3],e[t+4]=n[4],e[t+5]=n[5],e[t+6]=n[6],e[t+7]=n[7],e[t+8]=n[8],e[t+9]=n[9],e[t+10]=n[10],e[t+11]=n[11],e[t+12]=n[12],e[t+13]=n[13],e[t+14]=n[14],e[t+15]=n[15],e}},pa=new se,er=new Je,hv=new se(0,0,0),fv=new se(1,1,1),bs=new se,Vu=new se,Si=new se,wg=new Je,Cg=new an,vn=class r{constructor(e=0,t=0,n=0,i=r.DEFAULT_ORDER){this.isEuler=!0,this._x=e,this._y=t,this._z=n,this._order=i}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get order(){return this._order}set order(e){this._order=e,this._onChangeCallback()}set(e,t,n,i=this._order){return this._x=e,this._y=t,this._z=n,this._order=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(e){return this._x=e._x,this._y=e._y,this._z=e._z,this._order=e._order,this._onChangeCallback(),this}setFromRotationMatrix(e,t=this._order,n=!0){let i=e.elements,s=i[0],o=i[4],a=i[8],l=i[1],c=i[5],u=i[9],h=i[2],f=i[6],p=i[10];switch(t){case"XYZ":this._y=Math.asin(Pt(a,-1,1)),Math.abs(a)<.9999999?(this._x=Math.atan2(-u,p),this._z=Math.atan2(-o,s)):(this._x=Math.atan2(f,c),this._z=0);break;case"YXZ":this._x=Math.asin(-Pt(u,-1,1)),Math.abs(u)<.9999999?(this._y=Math.atan2(a,p),this._z=Math.atan2(l,c)):(this._y=Math.atan2(-h,s),this._z=0);break;case"ZXY":this._x=Math.asin(Pt(f,-1,1)),Math.abs(f)<.9999999?(this._y=Math.atan2(-h,p),this._z=Math.atan2(-o,c)):(this._y=0,this._z=Math.atan2(l,s));break;case"ZYX":this._y=Math.asin(-Pt(h,-1,1)),Math.abs(h)<.9999999?(this._x=Math.atan2(f,p),this._z=Math.atan2(l,s)):(this._x=0,this._z=Math.atan2(-o,c));break;case"YZX":this._z=Math.asin(Pt(l,-1,1)),Math.abs(l)<.9999999?(this._x=Math.atan2(-u,c),this._y=Math.atan2(-h,s)):(this._x=0,this._y=Math.atan2(a,p));break;case"XZY":this._z=Math.asin(-Pt(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(f,c),this._y=Math.atan2(a,s)):(this._x=Math.atan2(-u,p),this._y=0);break;default:console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: "+t)}return this._order=t,n===!0&&this._onChangeCallback(),this}setFromQuaternion(e,t,n){return wg.makeRotationFromQuaternion(e),this.setFromRotationMatrix(wg,t,n)}setFromVector3(e,t=this._order){return this.set(e.x,e.y,e.z,t)}reorder(e){return Cg.setFromEuler(this),this.setFromQuaternion(Cg,e)}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._order===this._order}fromArray(e){return this._x=e[0],this._y=e[1],this._z=e[2],e[3]!==void 0&&(this._order=e[3]),this._onChangeCallback(),this}toArray(e=[],t=0){return e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._order,e}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._order}};vn.DEFAULT_ORDER="XYZ";var Ca=class{constructor(){this.mask=1}set(e){this.mask=(1<>>0}enable(e){this.mask|=1<1){for(let t=0;t1){for(let n=0;n0&&(i.userData=this.userData),i.layers=this.layers.mask,i.matrix=this.matrix.toArray(),i.up=this.up.toArray(),this.matrixAutoUpdate===!1&&(i.matrixAutoUpdate=!1),this.isInstancedMesh&&(i.type="InstancedMesh",i.count=this.count,i.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(i.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(i.type="BatchedMesh",i.perObjectFrustumCulled=this.perObjectFrustumCulled,i.sortObjects=this.sortObjects,i.drawRanges=this._drawRanges,i.reservedRanges=this._reservedRanges,i.geometryInfo=this._geometryInfo.map(a=>({...a,boundingBox:a.boundingBox?{min:a.boundingBox.min.toArray(),max:a.boundingBox.max.toArray()}:void 0,boundingSphere:a.boundingSphere?{radius:a.boundingSphere.radius,center:a.boundingSphere.center.toArray()}:void 0})),i.instanceInfo=this._instanceInfo.map(a=>({...a})),i.availableInstanceIds=this._availableInstanceIds.slice(),i.availableGeometryIds=this._availableGeometryIds.slice(),i.nextIndexStart=this._nextIndexStart,i.nextVertexStart=this._nextVertexStart,i.geometryCount=this._geometryCount,i.maxInstanceCount=this._maxInstanceCount,i.maxVertexCount=this._maxVertexCount,i.maxIndexCount=this._maxIndexCount,i.geometryInitialized=this._geometryInitialized,i.matricesTexture=this._matricesTexture.toJSON(e),i.indirectTexture=this._indirectTexture.toJSON(e),this._colorsTexture!==null&&(i.colorsTexture=this._colorsTexture.toJSON(e)),this.boundingSphere!==null&&(i.boundingSphere={center:this.boundingSphere.center.toArray(),radius:this.boundingSphere.radius}),this.boundingBox!==null&&(i.boundingBox={min:this.boundingBox.min.toArray(),max:this.boundingBox.max.toArray()}));function s(a,l){return a[l.uuid]===void 0&&(a[l.uuid]=l.toJSON(e)),l.uuid}if(this.isScene)this.background&&(this.background.isColor?i.background=this.background.toJSON():this.background.isTexture&&(i.background=this.background.toJSON(e).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(i.environment=this.environment.toJSON(e).uuid);else if(this.isMesh||this.isLine||this.isPoints){i.geometry=s(e.geometries,this.geometry);let a=this.geometry.parameters;if(a!==void 0&&a.shapes!==void 0){let l=a.shapes;if(Array.isArray(l))for(let c=0,u=l.length;c0){i.children=[];for(let a=0;a0){i.animations=[];for(let a=0;a0&&(n.geometries=a),l.length>0&&(n.materials=l),c.length>0&&(n.textures=c),u.length>0&&(n.images=u),h.length>0&&(n.shapes=h),f.length>0&&(n.skeletons=f),p.length>0&&(n.animations=p),g.length>0&&(n.nodes=g)}return n.object=i,n;function o(a){let l=[];for(let c in a){let u=a[c];delete u.metadata,l.push(u)}return l}}clone(e){return new this.constructor().copy(this,e)}copy(e,t=!0){if(this.name=e.name,this.up.copy(e.up),this.position.copy(e.position),this.rotation.order=e.rotation.order,this.quaternion.copy(e.quaternion),this.scale.copy(e.scale),this.matrix.copy(e.matrix),this.matrixWorld.copy(e.matrixWorld),this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrixWorldAutoUpdate=e.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=e.matrixWorldNeedsUpdate,this.layers.mask=e.layers.mask,this.visible=e.visible,this.castShadow=e.castShadow,this.receiveShadow=e.receiveShadow,this.frustumCulled=e.frustumCulled,this.renderOrder=e.renderOrder,this.animations=e.animations.slice(),this.userData=JSON.parse(JSON.stringify(e.userData)),t===!0)for(let n=0;n0?i.multiplyScalar(1/Math.sqrt(s)):i.set(0,0,0)}static getBarycoord(e,t,n,i,s){tr.subVectors(i,t),jr.subVectors(n,t),Id.subVectors(e,t);let o=tr.dot(tr),a=tr.dot(jr),l=tr.dot(Id),c=jr.dot(jr),u=jr.dot(Id),h=o*c-a*a;if(h===0)return s.set(0,0,0),null;let f=1/h,p=(c*l-a*u)*f,g=(o*u-a*l)*f;return s.set(1-p-g,g,p)}static containsPoint(e,t,n,i){return this.getBarycoord(e,t,n,i,Yr)===null?!1:Yr.x>=0&&Yr.y>=0&&Yr.x+Yr.y<=1}static getInterpolation(e,t,n,i,s,o,a,l){return this.getBarycoord(e,t,n,i,Yr)===null?(l.x=0,l.y=0,"z"in l&&(l.z=0),"w"in l&&(l.w=0),null):(l.setScalar(0),l.addScaledVector(s,Yr.x),l.addScaledVector(o,Yr.y),l.addScaledVector(a,Yr.z),l)}static getInterpolatedAttribute(e,t,n,i,s,o){return Ld.setScalar(0),Od.setScalar(0),Dd.setScalar(0),Ld.fromBufferAttribute(e,t),Od.fromBufferAttribute(e,n),Dd.fromBufferAttribute(e,i),o.setScalar(0),o.addScaledVector(Ld,s.x),o.addScaledVector(Od,s.y),o.addScaledVector(Dd,s.z),o}static isFrontFacing(e,t,n,i){return tr.subVectors(n,t),jr.subVectors(e,t),tr.cross(jr).dot(i)<0}set(e,t,n){return this.a.copy(e),this.b.copy(t),this.c.copy(n),this}setFromPointsAndIndices(e,t,n,i){return this.a.copy(e[t]),this.b.copy(e[n]),this.c.copy(e[i]),this}setFromAttributeAndIndices(e,t,n,i){return this.a.fromBufferAttribute(e,t),this.b.fromBufferAttribute(e,n),this.c.fromBufferAttribute(e,i),this}clone(){return new this.constructor().copy(this)}copy(e){return this.a.copy(e.a),this.b.copy(e.b),this.c.copy(e.c),this}getArea(){return tr.subVectors(this.c,this.b),jr.subVectors(this.a,this.b),tr.cross(jr).length()*.5}getMidpoint(e){return e.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(e){return r.getNormal(this.a,this.b,this.c,e)}getPlane(e){return e.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(e,t){return r.getBarycoord(e,this.a,this.b,this.c,t)}getInterpolation(e,t,n,i,s){return r.getInterpolation(e,this.a,this.b,this.c,t,n,i,s)}containsPoint(e){return r.containsPoint(e,this.a,this.b,this.c)}isFrontFacing(e){return r.isFrontFacing(this.a,this.b,this.c,e)}intersectsBox(e){return e.intersectsTriangle(this)}closestPointToPoint(e,t){let n=this.a,i=this.b,s=this.c,o,a;ya.subVectors(i,n),xa.subVectors(s,n),Rd.subVectors(e,n);let l=ya.dot(Rd),c=xa.dot(Rd);if(l<=0&&c<=0)return t.copy(n);Pd.subVectors(e,i);let u=ya.dot(Pd),h=xa.dot(Pd);if(u>=0&&h<=u)return t.copy(i);let f=l*h-u*c;if(f<=0&&l>=0&&u<=0)return o=l/(l-u),t.copy(n).addScaledVector(ya,o);Nd.subVectors(e,s);let p=ya.dot(Nd),g=xa.dot(Nd);if(g>=0&&p<=g)return t.copy(s);let y=p*c-l*g;if(y<=0&&c>=0&&g<=0)return a=c/(c-g),t.copy(n).addScaledVector(xa,a);let d=u*g-p*h;if(d<=0&&h-u>=0&&p-g>=0)return Og.subVectors(s,i),a=(h-u)/(h-u+(p-g)),t.copy(i).addScaledVector(Og,a);let m=1/(d+y+f);return o=y*m,a=f*m,t.copy(n).addScaledVector(ya,o).addScaledVector(xa,a)}equals(e){return e.a.equals(this.a)&&e.b.equals(this.b)&&e.c.equals(this.c)}},ny={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Ss={h:0,s:0,l:0},Hu={h:0,s:0,l:0};function Fd(r,e,t){return t<0&&(t+=1),t>1&&(t-=1),t<1/6?r+(e-r)*6*t:t<1/2?e:t<2/3?r+(e-r)*6*(2/3-t):r}var He=class{constructor(e,t,n){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(e,t,n)}set(e,t,n){if(t===void 0&&n===void 0){let i=e;i&&i.isColor?this.copy(i):typeof i=="number"?this.setHex(i):typeof i=="string"&&this.setStyle(i)}else this.setRGB(e,t,n);return this}setScalar(e){return this.r=e,this.g=e,this.b=e,this}setHex(e,t=yt){return e=Math.floor(e),this.r=(e>>16&255)/255,this.g=(e>>8&255)/255,this.b=(e&255)/255,mt.toWorkingColorSpace(this,t),this}setRGB(e,t,n,i=mt.workingColorSpace){return this.r=e,this.g=t,this.b=n,mt.toWorkingColorSpace(this,i),this}setHSL(e,t,n,i=mt.workingColorSpace){if(e=Cp(e,1),t=Pt(t,0,1),n=Pt(n,0,1),t===0)this.r=this.g=this.b=n;else{let s=n<=.5?n*(1+t):n+t-n*t,o=2*n-s;this.r=Fd(o,s,e+1/3),this.g=Fd(o,s,e),this.b=Fd(o,s,e-1/3)}return mt.toWorkingColorSpace(this,i),this}setStyle(e,t=yt){function n(s){s!==void 0&&parseFloat(s)<1&&console.warn("THREE.Color: Alpha component of "+e+" will be ignored.")}let i;if(i=/^(\w+)\(([^\)]*)\)/.exec(e)){let s,o=i[1],a=i[2];switch(o){case"rgb":case"rgba":if(s=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(s[4]),this.setRGB(Math.min(255,parseInt(s[1],10))/255,Math.min(255,parseInt(s[2],10))/255,Math.min(255,parseInt(s[3],10))/255,t);if(s=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(s[4]),this.setRGB(Math.min(100,parseInt(s[1],10))/100,Math.min(100,parseInt(s[2],10))/100,Math.min(100,parseInt(s[3],10))/100,t);break;case"hsl":case"hsla":if(s=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(s[4]),this.setHSL(parseFloat(s[1])/360,parseFloat(s[2])/100,parseFloat(s[3])/100,t);break;default:console.warn("THREE.Color: Unknown color model "+e)}}else if(i=/^\#([A-Fa-f\d]+)$/.exec(e)){let s=i[1],o=s.length;if(o===3)return this.setRGB(parseInt(s.charAt(0),16)/15,parseInt(s.charAt(1),16)/15,parseInt(s.charAt(2),16)/15,t);if(o===6)return this.setHex(parseInt(s,16),t);console.warn("THREE.Color: Invalid hex color "+e)}else if(e&&e.length>0)return this.setColorName(e,t);return this}setColorName(e,t=yt){let n=ny[e.toLowerCase()];return n!==void 0?this.setHex(n,t):console.warn("THREE.Color: Unknown color "+e),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(e){return this.r=e.r,this.g=e.g,this.b=e.b,this}copySRGBToLinear(e){return this.r=Jr(e.r),this.g=Jr(e.g),this.b=Jr(e.b),this}copyLinearToSRGB(e){return this.r=ba(e.r),this.g=ba(e.g),this.b=ba(e.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(e=yt){return mt.fromWorkingColorSpace(Jn.copy(this),e),Math.round(Pt(Jn.r*255,0,255))*65536+Math.round(Pt(Jn.g*255,0,255))*256+Math.round(Pt(Jn.b*255,0,255))}getHexString(e=yt){return("000000"+this.getHex(e).toString(16)).slice(-6)}getHSL(e,t=mt.workingColorSpace){mt.fromWorkingColorSpace(Jn.copy(this),t);let n=Jn.r,i=Jn.g,s=Jn.b,o=Math.max(n,i,s),a=Math.min(n,i,s),l,c,u=(a+o)/2;if(a===o)l=0,c=0;else{let h=o-a;switch(c=u<=.5?h/(o+a):h/(2-o-a),o){case n:l=(i-s)/h+(i0!=e>0&&this.version++,this._alphaTest=e}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(e){if(e!==void 0)for(let t in e){let n=e[t];if(n===void 0){console.warn(`THREE.Material: parameter '${t}' has value of undefined.`);continue}let i=this[t];if(i===void 0){console.warn(`THREE.Material: '${t}' is not a property of THREE.${this.type}.`);continue}i&&i.isColor?i.set(n):i&&i.isVector3&&n&&n.isVector3?i.copy(n):this[t]=n}}toJSON(e){let t=e===void 0||typeof e=="string";t&&(e={textures:{},images:{}});let n={metadata:{version:4.6,type:"Material",generator:"Material.toJSON"}};n.uuid=this.uuid,n.type=this.type,this.name!==""&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),this.roughness!==void 0&&(n.roughness=this.roughness),this.metalness!==void 0&&(n.metalness=this.metalness),this.sheen!==void 0&&(n.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(n.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(n.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(n.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(n.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(n.shininess=this.shininess),this.clearcoat!==void 0&&(n.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(e).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(e).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(e).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.dispersion!==void 0&&(n.dispersion=this.dispersion),this.iridescence!==void 0&&(n.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(n.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(n.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(n.iridescenceMap=this.iridescenceMap.toJSON(e).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(n.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(e).uuid),this.anisotropy!==void 0&&(n.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(n.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(n.anisotropyMap=this.anisotropyMap.toJSON(e).uuid),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(e).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(e).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(e).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(e).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(e).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(e).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(e).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(e).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(e).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(e).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(e).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(e).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(n.specularIntensityMap=this.specularIntensityMap.toJSON(e).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(n.specularColorMap=this.specularColorMap.toJSON(e).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(e).uuid,this.combine!==void 0&&(n.combine=this.combine)),this.envMapRotation!==void 0&&(n.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(n.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(n.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(n.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(e).uuid),this.transmission!==void 0&&(n.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(n.transmissionMap=this.transmissionMap.toJSON(e).uuid),this.thickness!==void 0&&(n.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(n.thicknessMap=this.thicknessMap.toJSON(e).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(n.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(n.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(n.size=this.size),this.shadowSide!==null&&(n.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==io&&(n.blending=this.blending),this.side!==wi&&(n.side=this.side),this.vertexColors===!0&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),this.transparent===!0&&(n.transparent=!0),this.blendSrc!==fh&&(n.blendSrc=this.blendSrc),this.blendDst!==dh&&(n.blendDst=this.blendDst),this.blendEquation!==ws&&(n.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(n.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(n.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(n.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(n.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(n.blendAlpha=this.blendAlpha),this.depthFunc!==ro&&(n.depthFunc=this.depthFunc),this.depthTest===!1&&(n.depthTest=this.depthTest),this.depthWrite===!1&&(n.depthWrite=this.depthWrite),this.colorWrite===!1&&(n.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(n.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==Qd&&(n.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(n.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(n.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==no&&(n.stencilFail=this.stencilFail),this.stencilZFail!==no&&(n.stencilZFail=this.stencilZFail),this.stencilZPass!==no&&(n.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(n.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(n.rotation=this.rotation),this.polygonOffset===!0&&(n.polygonOffset=!0),this.polygonOffsetFactor!==0&&(n.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(n.linewidth=this.linewidth),this.dashSize!==void 0&&(n.dashSize=this.dashSize),this.gapSize!==void 0&&(n.gapSize=this.gapSize),this.scale!==void 0&&(n.scale=this.scale),this.dithering===!0&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),this.alphaHash===!0&&(n.alphaHash=!0),this.alphaToCoverage===!0&&(n.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(n.premultipliedAlpha=!0),this.forceSinglePass===!0&&(n.forceSinglePass=!0),this.wireframe===!0&&(n.wireframe=!0),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(n.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(n.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(n.flatShading=!0),this.visible===!1&&(n.visible=!1),this.toneMapped===!1&&(n.toneMapped=!1),this.fog===!1&&(n.fog=!1),Object.keys(this.userData).length>0&&(n.userData=this.userData);function i(s){let o=[];for(let a in s){let l=s[a];delete l.metadata,o.push(l)}return o}if(t){let s=i(e.textures),o=i(e.images);s.length>0&&(n.textures=s),o.length>0&&(n.images=o)}return n}clone(){return new this.constructor().copy(this)}copy(e){this.name=e.name,this.blending=e.blending,this.side=e.side,this.vertexColors=e.vertexColors,this.opacity=e.opacity,this.transparent=e.transparent,this.blendSrc=e.blendSrc,this.blendDst=e.blendDst,this.blendEquation=e.blendEquation,this.blendSrcAlpha=e.blendSrcAlpha,this.blendDstAlpha=e.blendDstAlpha,this.blendEquationAlpha=e.blendEquationAlpha,this.blendColor.copy(e.blendColor),this.blendAlpha=e.blendAlpha,this.depthFunc=e.depthFunc,this.depthTest=e.depthTest,this.depthWrite=e.depthWrite,this.stencilWriteMask=e.stencilWriteMask,this.stencilFunc=e.stencilFunc,this.stencilRef=e.stencilRef,this.stencilFuncMask=e.stencilFuncMask,this.stencilFail=e.stencilFail,this.stencilZFail=e.stencilZFail,this.stencilZPass=e.stencilZPass,this.stencilWrite=e.stencilWrite;let t=e.clippingPlanes,n=null;if(t!==null){let i=t.length;n=new Array(i);for(let s=0;s!==i;++s)n[s]=t[s].clone()}return this.clippingPlanes=n,this.clipIntersection=e.clipIntersection,this.clipShadows=e.clipShadows,this.shadowSide=e.shadowSide,this.colorWrite=e.colorWrite,this.precision=e.precision,this.polygonOffset=e.polygonOffset,this.polygonOffsetFactor=e.polygonOffsetFactor,this.polygonOffsetUnits=e.polygonOffsetUnits,this.dithering=e.dithering,this.alphaTest=e.alphaTest,this.alphaHash=e.alphaHash,this.alphaToCoverage=e.alphaToCoverage,this.premultipliedAlpha=e.premultipliedAlpha,this.forceSinglePass=e.forceSinglePass,this.visible=e.visible,this.toneMapped=e.toneMapped,this.userData=JSON.parse(JSON.stringify(e.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(e){e===!0&&this.version++}},Ii=class extends zi{constructor(e){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new He(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new vn,this.combine=Sc,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.lightMap=e.lightMap,this.lightMapIntensity=e.lightMapIntensity,this.aoMap=e.aoMap,this.aoMapIntensity=e.aoMapIntensity,this.specularMap=e.specularMap,this.alphaMap=e.alphaMap,this.envMap=e.envMap,this.envMapRotation.copy(e.envMapRotation),this.combine=e.combine,this.reflectivity=e.reflectivity,this.refractionRatio=e.refractionRatio,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.wireframeLinecap=e.wireframeLinecap,this.wireframeLinejoin=e.wireframeLinejoin,this.fog=e.fog,this}};var An=new se,Wu=new Pe,xv=0,Vn=class{constructor(e,t,n=!1){if(Array.isArray(e))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:xv++}),this.name="",this.array=e,this.itemSize=t,this.count=e!==void 0?e.length/t:0,this.normalized=n,this.usage=ep,this.updateRanges=[],this.gpuType=cr,this.version=0}onUploadCallback(){}set needsUpdate(e){e===!0&&this.version++}setUsage(e){return this.usage=e,this}addUpdateRange(e,t){this.updateRanges.push({start:e,count:t})}clearUpdateRanges(){this.updateRanges.length=0}copy(e){return this.name=e.name,this.array=new e.array.constructor(e.array),this.itemSize=e.itemSize,this.count=e.count,this.normalized=e.normalized,this.usage=e.usage,this.gpuType=e.gpuType,this}copyAt(e,t,n){e*=this.itemSize,n*=t.itemSize;for(let i=0,s=this.itemSize;it.count&&console.warn("THREE.BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),t.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new Gi);let e=this.attributes.position,t=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){console.error("THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new se(-1/0,-1/0,-1/0),new se(1/0,1/0,1/0));return}if(e!==void 0){if(this.boundingBox.setFromBufferAttribute(e),t)for(let n=0,i=t.length;n0&&(e.userData=this.userData),this.parameters!==void 0){let l=this.parameters;for(let c in l)l[c]!==void 0&&(e[c]=l[c]);return e}e.data={attributes:{}};let t=this.index;t!==null&&(e.data.index={type:t.array.constructor.name,array:Array.prototype.slice.call(t.array)});let n=this.attributes;for(let l in n){let c=n[l];e.data.attributes[l]=c.toJSON(e.data)}let i={},s=!1;for(let l in this.morphAttributes){let c=this.morphAttributes[l],u=[];for(let h=0,f=c.length;h0&&(i[l]=u,s=!0)}s&&(e.data.morphAttributes=i,e.data.morphTargetsRelative=this.morphTargetsRelative);let o=this.groups;o.length>0&&(e.data.groups=JSON.parse(JSON.stringify(o)));let a=this.boundingSphere;return a!==null&&(e.data.boundingSphere={center:a.center.toArray(),radius:a.radius}),e}clone(){return new this.constructor().copy(this)}copy(e){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;let t={};this.name=e.name;let n=e.index;n!==null&&this.setIndex(n.clone());let i=e.attributes;for(let c in i){let u=i[c];this.setAttribute(c,u.clone(t))}let s=e.morphAttributes;for(let c in s){let u=[],h=s[c];for(let f=0,p=h.length;f0){let i=t[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,o=i.length;s(e.far-e.near)**2))&&(Dg.copy(s).invert(),eo.copy(e.ray).applyMatrix4(Dg),!(n.boundingBox!==null&&eo.intersectsBox(n.boundingBox)===!1)&&this._computeIntersections(e,t,eo)))}_computeIntersections(e,t,n){let i,s=this.geometry,o=this.material,a=s.index,l=s.attributes.position,c=s.attributes.uv,u=s.attributes.uv1,h=s.attributes.normal,f=s.groups,p=s.drawRange;if(a!==null)if(Array.isArray(o))for(let g=0,y=f.length;gt.far?null:{distance:c,point:Zu.clone(),object:r}}function Ju(r,e,t,n,i,s,o,a,l,c){r.getVertexPosition(a,qu),r.getVertexPosition(l,ju),r.getVertexPosition(c,Yu);let u=_v(r,e,t,n,qu,ju,Yu,Ug);if(u){let h=new se;Zr.getBarycoord(Ug,qu,ju,Yu,h),i&&(u.uv=Zr.getInterpolatedAttribute(i,a,l,c,h,new Pe)),s&&(u.uv1=Zr.getInterpolatedAttribute(s,a,l,c,h,new Pe)),o&&(u.normal=Zr.getInterpolatedAttribute(o,a,l,c,h,new se),u.normal.dot(n.direction)>0&&u.normal.multiplyScalar(-1));let f={a,b:l,c,normal:new se,materialIndex:0};Zr.getNormal(qu,ju,Yu,f.normal),u.face=f,u.barycoord=h}return u}var Is=class r extends It{constructor(e=1,t=1,n=1,i=1,s=1,o=1){super(),this.type="BoxGeometry",this.parameters={width:e,height:t,depth:n,widthSegments:i,heightSegments:s,depthSegments:o};let a=this;i=Math.floor(i),s=Math.floor(s),o=Math.floor(o);let l=[],c=[],u=[],h=[],f=0,p=0;g("z","y","x",-1,-1,n,t,e,o,s,0),g("z","y","x",1,-1,n,t,-e,o,s,1),g("x","z","y",1,1,e,n,t,i,o,2),g("x","z","y",1,-1,e,n,-t,i,o,3),g("x","y","z",1,-1,e,t,n,i,s,4),g("x","y","z",-1,-1,e,t,-n,i,s,5),this.setIndex(l),this.setAttribute("position",new We(c,3)),this.setAttribute("normal",new We(u,3)),this.setAttribute("uv",new We(h,2));function g(y,d,m,_,v,x,S,I,A,C,E){let b=x/A,R=S/C,N=x/2,B=S/2,V=I/2,ie=A+1,Y=C+1,te=0,q=0,le=new se;for(let ye=0;ye0?1:-1,u.push(le.x,le.y,le.z),h.push(Ae/A),h.push(1-ye/C),te+=1}}for(let ye=0;ye0&&(t.defines=this.defines),t.vertexShader=this.vertexShader,t.fragmentShader=this.fragmentShader,t.lights=this.lights,t.clipping=this.clipping;let n={};for(let i in this.extensions)this.extensions[i]===!0&&(n[i]=!0);return Object.keys(n).length>0&&(t.extensions=n),t}},rc=class extends Lt{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new Je,this.projectionMatrix=new Je,this.projectionMatrixInverse=new Je,this.coordinateSystem=Ar}copy(e,t){return super.copy(e,t),this.matrixWorldInverse.copy(e.matrixWorldInverse),this.projectionMatrix.copy(e.projectionMatrix),this.projectionMatrixInverse.copy(e.projectionMatrixInverse),this.coordinateSystem=e.coordinateSystem,this}getWorldDirection(e){return super.getWorldDirection(e).negate()}updateMatrixWorld(e){super.updateMatrixWorld(e),this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(e,t){super.updateWorldMatrix(e,t),this.matrixWorldInverse.copy(this.matrixWorld).invert()}clone(){return new this.constructor().copy(this)}},As=new se,kg=new Pe,Bg=new Pe,nn=class extends rc{constructor(e=50,t=1,n=.1,i=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=e,this.zoom=1,this.near=n,this.far=i,this.focus=10,this.aspect=t,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(e,t){return super.copy(e,t),this.fov=e.fov,this.zoom=e.zoom,this.near=e.near,this.far=e.far,this.focus=e.focus,this.aspect=e.aspect,this.view=e.view===null?null:Object.assign({},e.view),this.filmGauge=e.filmGauge,this.filmOffset=e.filmOffset,this}setFocalLength(e){let t=.5*this.getFilmHeight()/e;this.fov=oo*2*Math.atan(t),this.updateProjectionMatrix()}getFocalLength(){let e=Math.tan(Ea*.5*this.fov);return .5*this.getFilmHeight()/e}getEffectiveFOV(){return oo*2*Math.atan(Math.tan(Ea*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(e,t,n){As.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),t.set(As.x,As.y).multiplyScalar(-e/As.z),As.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(As.x,As.y).multiplyScalar(-e/As.z)}getViewSize(e,t){return this.getViewBounds(e,kg,Bg),t.subVectors(Bg,kg)}setViewOffset(e,t,n,i,s,o){this.aspect=e/t,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=e,this.view.fullHeight=t,this.view.offsetX=n,this.view.offsetY=i,this.view.width=s,this.view.height=o,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){let e=this.near,t=e*Math.tan(Ea*.5*this.fov)/this.zoom,n=2*t,i=this.aspect*n,s=-.5*i,o=this.view;if(this.view!==null&&this.view.enabled){let l=o.fullWidth,c=o.fullHeight;s+=o.offsetX*i/l,t-=o.offsetY*n/c,i*=o.width/l,n*=o.height/c}let a=this.filmOffset;a!==0&&(s+=e*a/this.getFilmWidth()),this.projectionMatrix.makePerspective(s,s+i,t,t-n,e,this.far,this.coordinateSystem),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(e){let t=super.toJSON(e);return t.object.fov=this.fov,t.object.zoom=this.zoom,t.object.near=this.near,t.object.far=this.far,t.object.focus=this.focus,t.object.aspect=this.aspect,this.view!==null&&(t.object.view=Object.assign({},this.view)),t.object.filmGauge=this.filmGauge,t.object.filmOffset=this.filmOffset,t}},_a=-90,Ta=1,yh=class extends Lt{constructor(e,t,n){super(),this.type="CubeCamera",this.renderTarget=n,this.coordinateSystem=null,this.activeMipmapLevel=0;let i=new nn(_a,Ta,e,t);i.layers=this.layers,this.add(i);let s=new nn(_a,Ta,e,t);s.layers=this.layers,this.add(s);let o=new nn(_a,Ta,e,t);o.layers=this.layers,this.add(o);let a=new nn(_a,Ta,e,t);a.layers=this.layers,this.add(a);let l=new nn(_a,Ta,e,t);l.layers=this.layers,this.add(l);let c=new nn(_a,Ta,e,t);c.layers=this.layers,this.add(c)}updateCoordinateSystem(){let e=this.coordinateSystem,t=this.children.concat(),[n,i,s,o,a,l]=t;for(let c of t)this.remove(c);if(e===Ar)n.up.set(0,1,0),n.lookAt(1,0,0),i.up.set(0,1,0),i.lookAt(-1,0,0),s.up.set(0,0,-1),s.lookAt(0,1,0),o.up.set(0,0,1),o.lookAt(0,-1,0),a.up.set(0,1,0),a.lookAt(0,0,1),l.up.set(0,1,0),l.lookAt(0,0,-1);else if(e===tc)n.up.set(0,-1,0),n.lookAt(-1,0,0),i.up.set(0,-1,0),i.lookAt(1,0,0),s.up.set(0,0,1),s.lookAt(0,1,0),o.up.set(0,0,-1),o.lookAt(0,-1,0),a.up.set(0,-1,0),a.lookAt(0,0,1),l.up.set(0,-1,0),l.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+e);for(let c of t)this.add(c),c.updateMatrixWorld()}update(e,t){this.parent===null&&this.updateMatrixWorld();let{renderTarget:n,activeMipmapLevel:i}=this;this.coordinateSystem!==e.coordinateSystem&&(this.coordinateSystem=e.coordinateSystem,this.updateCoordinateSystem());let[s,o,a,l,c,u]=this.children,h=e.getRenderTarget(),f=e.getActiveCubeFace(),p=e.getActiveMipmapLevel(),g=e.xr.enabled;e.xr.enabled=!1;let y=n.texture.generateMipmaps;n.texture.generateMipmaps=!1,e.setRenderTarget(n,0,i),e.render(t,s),e.setRenderTarget(n,1,i),e.render(t,o),e.setRenderTarget(n,2,i),e.render(t,a),e.setRenderTarget(n,3,i),e.render(t,l),e.setRenderTarget(n,4,i),e.render(t,c),n.texture.generateMipmaps=y,e.setRenderTarget(n,5,i),e.render(t,u),e.setRenderTarget(h,f,p),e.xr.enabled=g,n.texture.needsPMREMUpdate=!0}},Ia=class extends qn{constructor(e=[],t=_o,n,i,s,o,a,l,c,u){super(e,t,n,i,s,o,a,l,c,u),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(e){this.image=e}},xh=class extends wr{constructor(e=1,t={}){super(e,e,t),this.isWebGLCubeRenderTarget=!0;let n={width:e,height:e,depth:1},i=[n,n,n,n,n,n];this.texture=new Ia(i,t.mapping,t.wrapS,t.wrapT,t.magFilter,t.minFilter,t.format,t.type,t.anisotropy,t.colorSpace),this.texture.isRenderTargetTexture=!0,this.texture.generateMipmaps=t.generateMipmaps!==void 0?t.generateMipmaps:!1,this.texture.minFilter=t.minFilter!==void 0?t.minFilter:Pn}fromEquirectangularTexture(e,t){this.texture.type=t.type,this.texture.colorSpace=t.colorSpace,this.texture.generateMipmaps=t.generateMipmaps,this.texture.minFilter=t.minFilter,this.texture.magFilter=t.magFilter;let n={uniforms:{tEquirect:{value:null}},vertexShader:` + + varying vec3 vWorldDirection; + + vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); + + } + + void main() { + + vWorldDirection = transformDirection( position, modelMatrix ); + + #include + #include + + } + `,fragmentShader:` + + uniform sampler2D tEquirect; + + varying vec3 vWorldDirection; + + #include + + void main() { + + vec3 direction = normalize( vWorldDirection ); + + vec2 sampleUV = equirectUv( direction ); + + gl_FragColor = texture2D( tEquirect, sampleUV ); + + } + `},i=new Is(5,5,5),s=new nr({name:"CubemapFromEquirect",uniforms:Mo(n.uniforms),vertexShader:n.vertexShader,fragmentShader:n.fragmentShader,side:_n,blending:as});s.uniforms.tEquirect.value=t;let o=new Dt(i,s),a=t.minFilter;return t.minFilter===xi&&(t.minFilter=Pn),new yh(1,10,this).update(e,o),t.minFilter=a,o.geometry.dispose(),o.material.dispose(),this}clear(e,t=!0,n=!0,i=!0){let s=e.getRenderTarget();for(let o=0;o<6;o++)e.setRenderTarget(this,o),e.clear(t,n,i);e.setRenderTarget(s)}},gn=class extends Lt{constructor(){super(),this.isGroup=!0,this.type="Group"}},bv={type:"move"},Ra=class{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return this._hand===null&&(this._hand=new gn,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand}getTargetRaySpace(){return this._targetRay===null&&(this._targetRay=new gn,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1,this._targetRay.hasLinearVelocity=!1,this._targetRay.linearVelocity=new se,this._targetRay.hasAngularVelocity=!1,this._targetRay.angularVelocity=new se),this._targetRay}getGripSpace(){return this._grip===null&&(this._grip=new gn,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1,this._grip.hasLinearVelocity=!1,this._grip.linearVelocity=new se,this._grip.hasAngularVelocity=!1,this._grip.angularVelocity=new se),this._grip}dispatchEvent(e){return this._targetRay!==null&&this._targetRay.dispatchEvent(e),this._grip!==null&&this._grip.dispatchEvent(e),this._hand!==null&&this._hand.dispatchEvent(e),this}connect(e){if(e&&e.hand){let t=this._hand;if(t)for(let n of e.hand.values())this._getHandJoint(t,n)}return this.dispatchEvent({type:"connected",data:e}),this}disconnect(e){return this.dispatchEvent({type:"disconnected",data:e}),this._targetRay!==null&&(this._targetRay.visible=!1),this._grip!==null&&(this._grip.visible=!1),this._hand!==null&&(this._hand.visible=!1),this}update(e,t,n){let i=null,s=null,o=null,a=this._targetRay,l=this._grip,c=this._hand;if(e&&t.session.visibilityState!=="visible-blurred"){if(c&&e.hand){o=!0;for(let y of e.hand.values()){let d=t.getJointPose(y,n),m=this._getHandJoint(c,y);d!==null&&(m.matrix.fromArray(d.transform.matrix),m.matrix.decompose(m.position,m.rotation,m.scale),m.matrixWorldNeedsUpdate=!0,m.jointRadius=d.radius),m.visible=d!==null}let u=c.joints["index-finger-tip"],h=c.joints["thumb-tip"],f=u.position.distanceTo(h.position),p=.02,g=.005;c.inputState.pinching&&f>p+g?(c.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:e.handedness,target:this})):!c.inputState.pinching&&f<=p-g&&(c.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:e.handedness,target:this}))}else l!==null&&e.gripSpace&&(s=t.getPose(e.gripSpace,n),s!==null&&(l.matrix.fromArray(s.transform.matrix),l.matrix.decompose(l.position,l.rotation,l.scale),l.matrixWorldNeedsUpdate=!0,s.linearVelocity?(l.hasLinearVelocity=!0,l.linearVelocity.copy(s.linearVelocity)):l.hasLinearVelocity=!1,s.angularVelocity?(l.hasAngularVelocity=!0,l.angularVelocity.copy(s.angularVelocity)):l.hasAngularVelocity=!1));a!==null&&(i=t.getPose(e.targetRaySpace,n),i===null&&s!==null&&(i=s),i!==null&&(a.matrix.fromArray(i.transform.matrix),a.matrix.decompose(a.position,a.rotation,a.scale),a.matrixWorldNeedsUpdate=!0,i.linearVelocity?(a.hasLinearVelocity=!0,a.linearVelocity.copy(i.linearVelocity)):a.hasLinearVelocity=!1,i.angularVelocity?(a.hasAngularVelocity=!0,a.angularVelocity.copy(i.angularVelocity)):a.hasAngularVelocity=!1,this.dispatchEvent(bv)))}return a!==null&&(a.visible=i!==null),l!==null&&(l.visible=s!==null),c!==null&&(c.visible=o!==null),this}_getHandJoint(e,t){if(e.joints[t.jointName]===void 0){let n=new gn;n.matrixAutoUpdate=!1,n.visible=!1,e.joints[t.jointName]=n,e.add(n)}return e.joints[t.jointName]}};var ir=class extends Lt{constructor(){super(),this.isScene=!0,this.type="Scene",this.background=null,this.environment=null,this.fog=null,this.backgroundBlurriness=0,this.backgroundIntensity=1,this.backgroundRotation=new vn,this.environmentIntensity=1,this.environmentRotation=new vn,this.overrideMaterial=null,typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}copy(e,t){return super.copy(e,t),e.background!==null&&(this.background=e.background.clone()),e.environment!==null&&(this.environment=e.environment.clone()),e.fog!==null&&(this.fog=e.fog.clone()),this.backgroundBlurriness=e.backgroundBlurriness,this.backgroundIntensity=e.backgroundIntensity,this.backgroundRotation.copy(e.backgroundRotation),this.environmentIntensity=e.environmentIntensity,this.environmentRotation.copy(e.environmentRotation),e.overrideMaterial!==null&&(this.overrideMaterial=e.overrideMaterial.clone()),this.matrixAutoUpdate=e.matrixAutoUpdate,this}toJSON(e){let t=super.toJSON(e);return this.fog!==null&&(t.object.fog=this.fog.toJSON()),this.backgroundBlurriness>0&&(t.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(t.object.backgroundIntensity=this.backgroundIntensity),t.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(t.object.environmentIntensity=this.environmentIntensity),t.object.environmentRotation=this.environmentRotation.toArray(),t}};var Gg=new se,Vg=new wt,zg=new wt,Sv=new se,Hg=new Je,$u=new se,Bd=new Vi,Wg=new Je,Gd=new Cs,co=class extends Dt{constructor(e,t){super(e,t),this.isSkinnedMesh=!0,this.type="SkinnedMesh",this.bindMode=Kd,this.bindMatrix=new Je,this.bindMatrixInverse=new Je,this.boundingBox=null,this.boundingSphere=null}computeBoundingBox(){let e=this.geometry;this.boundingBox===null&&(this.boundingBox=new Gi),this.boundingBox.makeEmpty();let t=e.getAttribute("position");for(let n=0;n1?null:t.copy(e.start).addScaledVector(n,s)}intersectsLine(e){let t=this.distanceToPoint(e.start),n=this.distanceToPoint(e.end);return t<0&&n>0||n<0&&t>0}intersectsBox(e){return e.intersectsPlane(this)}intersectsSphere(e){return e.intersectsPlane(this)}coplanarPoint(e){return e.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(e,t){let n=t||Cv.getNormalMatrix(e),i=this.coplanarPoint(Vd).applyMatrix4(e),s=this.normal.applyMatrix3(n).normalize();return this.constant=-i.dot(s),this}translate(e){return this.constant-=e.dot(this.normal),this}equals(e){return e.normal.equals(this.normal)&&e.constant===this.constant}clone(){return new this.constructor().copy(this)}},to=new Vi,Qu=new se,Pa=class{constructor(e=new Sr,t=new Sr,n=new Sr,i=new Sr,s=new Sr,o=new Sr){this.planes=[e,t,n,i,s,o]}set(e,t,n,i,s,o){let a=this.planes;return a[0].copy(e),a[1].copy(t),a[2].copy(n),a[3].copy(i),a[4].copy(s),a[5].copy(o),this}copy(e){let t=this.planes;for(let n=0;n<6;n++)t[n].copy(e.planes[n]);return this}setFromProjectionMatrix(e,t=Ar){let n=this.planes,i=e.elements,s=i[0],o=i[1],a=i[2],l=i[3],c=i[4],u=i[5],h=i[6],f=i[7],p=i[8],g=i[9],y=i[10],d=i[11],m=i[12],_=i[13],v=i[14],x=i[15];if(n[0].setComponents(l-s,f-c,d-p,x-m).normalize(),n[1].setComponents(l+s,f+c,d+p,x+m).normalize(),n[2].setComponents(l+o,f+u,d+g,x+_).normalize(),n[3].setComponents(l-o,f-u,d-g,x-_).normalize(),n[4].setComponents(l-a,f-h,d-y,x-v).normalize(),t===Ar)n[5].setComponents(l+a,f+h,d+y,x+v).normalize();else if(t===tc)n[5].setComponents(a,h,y,v).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+t);return this}intersectsObject(e){if(e.boundingSphere!==void 0)e.boundingSphere===null&&e.computeBoundingSphere(),to.copy(e.boundingSphere).applyMatrix4(e.matrixWorld);else{let t=e.geometry;t.boundingSphere===null&&t.computeBoundingSphere(),to.copy(t.boundingSphere).applyMatrix4(e.matrixWorld)}return this.intersectsSphere(to)}intersectsSprite(e){return to.center.set(0,0,0),to.radius=.7071067811865476,to.applyMatrix4(e.matrixWorld),this.intersectsSphere(to)}intersectsSphere(e){let t=this.planes,n=e.center,i=-e.radius;for(let s=0;s<6;s++)if(t[s].distanceToPoint(n)0?e.max.x:e.min.x,Qu.y=i.normal.y>0?e.max.y:e.min.y,Qu.z=i.normal.z>0?e.max.z:e.min.z,i.distanceToPoint(Qu)<0)return!1}return!0}containsPoint(e){let t=this.planes;for(let n=0;n<6;n++)if(t[n].distanceToPoint(e)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}};var jn=class extends zi{constructor(e){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new He(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.linewidth=e.linewidth,this.linecap=e.linecap,this.linejoin=e.linejoin,this.fog=e.fog,this}},vh=new se,_h=new se,qg=new Je,jl=new Cs,eh=new Vi,zd=new se,jg=new se,Rs=class extends Lt{constructor(e=new It,t=new jn){super(),this.isLine=!0,this.type="Line",this.geometry=e,this.material=t,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(e,t){return super.copy(e,t),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}computeLineDistances(){let e=this.geometry;if(e.index===null){let t=e.attributes.position,n=[0];for(let i=1,s=t.count;i0){let i=t[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,o=i.length;sn)return;zd.applyMatrix4(r.matrixWorld);let c=e.ray.origin.distanceTo(zd);if(!(ce.far))return{distance:c,point:jg.clone().applyMatrix4(r.matrixWorld),index:o,face:null,faceIndex:null,barycoord:null,object:r}}var Yg=new se,Kg=new se,rr=class extends Rs{constructor(e,t){super(e,t),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){let e=this.geometry;if(e.index===null){let t=e.attributes.position,n=[];for(let i=0,s=t.count;i0){let i=t[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,o=i.length;si.far)return;s.push({distance:c,distanceToRay:Math.sqrt(a),point:l,index:e,face:null,faceIndex:null,barycoord:null,object:o})}}var oc=class extends qn{constructor(e,t,n=Ls,i,s,o,a=Xn,l=Xn,c,u=Sa){if(u!==Sa&&u!==Va)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");super(null,i,s,o,a,l,u,n,c),this.isDepthTexture=!0,this.image={width:e,height:t},this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(e){return super.copy(e),this.source=new wa(Object.assign({},e.image)),this.compareFunction=e.compareFunction,this}toJSON(e){let t=super.toJSON(e);return this.compareFunction!==null&&(t.compareFunction=this.compareFunction),t}};var La=class r extends It{constructor(e=1,t=1,n=1,i=32,s=1,o=!1,a=0,l=Math.PI*2){super(),this.type="CylinderGeometry",this.parameters={radiusTop:e,radiusBottom:t,height:n,radialSegments:i,heightSegments:s,openEnded:o,thetaStart:a,thetaLength:l};let c=this;i=Math.floor(i),s=Math.floor(s);let u=[],h=[],f=[],p=[],g=0,y=[],d=n/2,m=0;_(),o===!1&&(e>0&&v(!0),t>0&&v(!1)),this.setIndex(u),this.setAttribute("position",new We(h,3)),this.setAttribute("normal",new We(f,3)),this.setAttribute("uv",new We(p,2));function _(){let x=new se,S=new se,I=0,A=(t-e)/n;for(let C=0;C<=s;C++){let E=[],b=C/s,R=b*(t-e)+e;for(let N=0;N<=i;N++){let B=N/i,V=B*l+a,ie=Math.sin(V),Y=Math.cos(V);S.x=R*ie,S.y=-b*n+d,S.z=R*Y,h.push(S.x,S.y,S.z),x.set(ie,A,Y).normalize(),f.push(x.x,x.y,x.z),p.push(B,1-b),E.push(g++)}y.push(E)}for(let C=0;C0||E!==0)&&(u.push(b,R,B),I+=3),(t>0||E!==s-1)&&(u.push(R,N,B),I+=3)}c.addGroup(m,I,0),m+=I}function v(x){let S=g,I=new Pe,A=new se,C=0,E=x===!0?e:t,b=x===!0?1:-1;for(let N=1;N<=i;N++)h.push(0,d*b,0),f.push(0,b,0),p.push(.5,.5),g++;let R=g;for(let N=0;N<=i;N++){let V=N/i*l+a,ie=Math.cos(V),Y=Math.sin(V);A.x=E*Y,A.y=d*b,A.z=E*ie,h.push(A.x,A.y,A.z),f.push(0,b,0),I.x=ie*.5+.5,I.y=Y*.5*b+.5,p.push(I.x,I.y),g++}for(let N=0;N0)l=i-1;else{l=i;break}if(i=l,n[i]===o)return i/(s-1);let u=n[i],f=n[i+1]-u,p=(o-u)/f;return(i+p)/(s-1)}getTangent(e,t){let i=e-1e-4,s=e+1e-4;i<0&&(i=0),s>1&&(s=1);let o=this.getPoint(i),a=this.getPoint(s),l=t||(o.isVector2?new Pe:new se);return l.copy(a).sub(o).normalize(),l}getTangentAt(e,t){let n=this.getUtoTmapping(e);return this.getTangent(n,t)}computeFrenetFrames(e,t=!1){let n=new se,i=[],s=[],o=[],a=new se,l=new Je;for(let p=0;p<=e;p++){let g=p/e;i[p]=this.getTangentAt(g,new se)}s[0]=new se,o[0]=new se;let c=Number.MAX_VALUE,u=Math.abs(i[0].x),h=Math.abs(i[0].y),f=Math.abs(i[0].z);u<=c&&(c=u,n.set(1,0,0)),h<=c&&(c=h,n.set(0,1,0)),f<=c&&n.set(0,0,1),a.crossVectors(i[0],n).normalize(),s[0].crossVectors(i[0],a),o[0].crossVectors(i[0],s[0]);for(let p=1;p<=e;p++){if(s[p]=s[p-1].clone(),o[p]=o[p-1].clone(),a.crossVectors(i[p-1],i[p]),a.length()>Number.EPSILON){a.normalize();let g=Math.acos(Pt(i[p-1].dot(i[p]),-1,1));s[p].applyMatrix4(l.makeRotationAxis(a,g))}o[p].crossVectors(i[p],s[p])}if(t===!0){let p=Math.acos(Pt(s[0].dot(s[e]),-1,1));p/=e,i[0].dot(a.crossVectors(s[0],s[e]))>0&&(p=-p);for(let g=1;g<=e;g++)s[g].applyMatrix4(l.makeRotationAxis(i[g],p*g)),o[g].crossVectors(i[g],s[g])}return{tangents:i,normals:s,binormals:o}}clone(){return new this.constructor().copy(this)}copy(e){return this.arcLengthDivisions=e.arcLengthDivisions,this}toJSON(){let e={metadata:{version:4.6,type:"Curve",generator:"Curve.toJSON"}};return e.arcLengthDivisions=this.arcLengthDivisions,e.type=this.type,e}fromJSON(e){return this.arcLengthDivisions=e.arcLengthDivisions,this}},Oa=class extends si{constructor(e=0,t=0,n=1,i=1,s=0,o=Math.PI*2,a=!1,l=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=e,this.aY=t,this.xRadius=n,this.yRadius=i,this.aStartAngle=s,this.aEndAngle=o,this.aClockwise=a,this.aRotation=l}getPoint(e,t=new Pe){let n=t,i=Math.PI*2,s=this.aEndAngle-this.aStartAngle,o=Math.abs(s)i;)s-=i;s0?0:(Math.floor(Math.abs(a)/s)+1)*s:l===0&&a===s-1&&(a=s-2,l=1);let c,u;this.closed||a>0?c=i[(a-1)%s]:(ah.subVectors(i[0],i[1]).add(i[0]),c=ah);let h=i[a%s],f=i[(a+1)%s];if(this.closed||a+2i.length-2?i.length-1:o+1],h=i[o>i.length-3?i.length-1:o+2];return n.set($g(a,l.x,c.x,u.x,h.x),$g(a,l.y,c.y,u.y,h.y)),n}copy(e){super.copy(e),this.points=[];for(let t=0,n=e.points.length;t=n){let o=i[s]-n,a=this.curves[s],l=a.getLength(),c=l===0?0:1-o/l;return a.getPointAt(c,t)}s++}return null}getLength(){let e=this.getCurveLengths();return e[e.length-1]}updateArcLengths(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;let e=[],t=0;for(let n=0,i=this.curves.length;n1&&!t[t.length-1].equals(t[0])&&t.push(t[0]),t}copy(e){super.copy(e),this.curves=[];for(let t=0,n=e.curves.length;t0){let h=c.getPoint(0);h.equals(this.currentPoint)||this.lineTo(h.x,h.y)}this.curves.push(c);let u=c.getPoint(1);return this.currentPoint.copy(u),this}copy(e){return super.copy(e),this.currentPoint.copy(e.currentPoint),this}toJSON(){let e=super.toJSON();return e.currentPoint=this.currentPoint.toArray(),e}fromJSON(e){return super.fromJSON(e),this.currentPoint.fromArray(e.currentPoint),this}},$r=class extends Cr{constructor(e){super(e),this.uuid=us(),this.type="Shape",this.holes=[]}getPointsHoles(e){let t=[];for(let n=0,i=this.holes.length;n80*t){a=1/0,l=1/0;let u=-1/0,h=-1/0;for(let f=t;fu&&(u=p),g>h&&(h=g)}c=Math.max(u-a,h-l),c=c!==0?32767/c:0}return dc(s,o,t,a,l,c,0),o}function ry(r,e,t,n,i){let s;if(i===$v(r,e,t,n)>0)for(let o=e;o=e;o-=n)s=Qg(o/n|0,r[o],r[o+1],s);return s&&Da(s,s.next)&&(mc(s),s=s.next),s}function fo(r,e){if(!r)return r;e||(e=r);let t=r,n;do if(n=!1,!t.steiner&&(Da(t,t.next)||mn(t.prev,t,t.next)===0)){if(mc(t),t=e=t.prev,t===t.next)break;n=!0}else t=t.next;while(n||t!==e);return e}function dc(r,e,t,n,i,s,o){if(!r)return;!o&&s&&qv(r,n,i,s);let a=r;for(;r.prev!==r.next;){let l=r.prev,c=r.next;if(s?kv(r,n,i,s):Uv(r)){e.push(l.i,r.i,c.i),mc(r),r=c.next,a=c.next;continue}if(r=c,r===a){o?o===1?(r=Bv(fo(r),e),dc(r,e,t,n,i,s,2)):o===2&&Gv(r,e,t,n,i,s):dc(fo(r),e,t,n,i,s,1);break}}}function Uv(r){let e=r.prev,t=r,n=r.next;if(mn(e,t,n)>=0)return!1;let i=e.x,s=t.x,o=n.x,a=e.y,l=t.y,c=n.y,u=Math.min(i,s,o),h=Math.min(a,l,c),f=Math.max(i,s,o),p=Math.max(a,l,c),g=n.next;for(;g!==e;){if(g.x>=u&&g.x<=f&&g.y>=h&&g.y<=p&&Kl(i,a,s,l,o,c,g.x,g.y)&&mn(g.prev,g,g.next)>=0)return!1;g=g.next}return!0}function kv(r,e,t,n){let i=r.prev,s=r,o=r.next;if(mn(i,s,o)>=0)return!1;let a=i.x,l=s.x,c=o.x,u=i.y,h=s.y,f=o.y,p=Math.min(a,l,c),g=Math.min(u,h,f),y=Math.max(a,l,c),d=Math.max(u,h,f),m=ip(p,g,e,t,n),_=ip(y,d,e,t,n),v=r.prevZ,x=r.nextZ;for(;v&&v.z>=m&&x&&x.z<=_;){if(v.x>=p&&v.x<=y&&v.y>=g&&v.y<=d&&v!==i&&v!==o&&Kl(a,u,l,h,c,f,v.x,v.y)&&mn(v.prev,v,v.next)>=0||(v=v.prevZ,x.x>=p&&x.x<=y&&x.y>=g&&x.y<=d&&x!==i&&x!==o&&Kl(a,u,l,h,c,f,x.x,x.y)&&mn(x.prev,x,x.next)>=0))return!1;x=x.nextZ}for(;v&&v.z>=m;){if(v.x>=p&&v.x<=y&&v.y>=g&&v.y<=d&&v!==i&&v!==o&&Kl(a,u,l,h,c,f,v.x,v.y)&&mn(v.prev,v,v.next)>=0)return!1;v=v.prevZ}for(;x&&x.z<=_;){if(x.x>=p&&x.x<=y&&x.y>=g&&x.y<=d&&x!==i&&x!==o&&Kl(a,u,l,h,c,f,x.x,x.y)&&mn(x.prev,x,x.next)>=0)return!1;x=x.nextZ}return!0}function Bv(r,e){let t=r;do{let n=t.prev,i=t.next.next;!Da(n,i)&&oy(n,t,t.next,i)&&pc(n,i)&&pc(i,n)&&(e.push(n.i,t.i,i.i),mc(t),mc(t.next),t=r=i),t=t.next}while(t!==r);return fo(t)}function Gv(r,e,t,n,i,s){let o=r;do{let a=o.next.next;for(;a!==o.prev;){if(o.i!==a.i&&Kv(o,a)){let l=ay(o,a);o=fo(o,o.next),l=fo(l,l.next),dc(o,e,t,n,i,s,0),dc(l,e,t,n,i,s,0);return}a=a.next}o=o.next}while(o!==r)}function Vv(r,e,t,n){let i=[];for(let s=0,o=e.length;s=t.next.y&&t.next.y!==t.y){let h=t.x+(i-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(h<=n&&h>s&&(s=h,o=t.x=t.x&&t.x>=l&&n!==t.x&&sy(io.x||t.x===o.x&&Xv(o,t)))&&(o=t,u=h)}t=t.next}while(t!==a);return o}function Xv(r,e){return mn(r.prev,r,e.prev)<0&&mn(e.next,r,r.next)<0}function qv(r,e,t,n){let i=r;do i.z===0&&(i.z=ip(i.x,i.y,e,t,n)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next;while(i!==r);i.prevZ.nextZ=null,i.prevZ=null,jv(i)}function jv(r){let e,t=1;do{let n=r,i;r=null;let s=null;for(e=0;n;){e++;let o=n,a=0;for(let c=0;c0||l>0&&o;)a!==0&&(l===0||!o||n.z<=o.z)?(i=n,n=n.nextZ,a--):(i=o,o=o.nextZ,l--),s?s.nextZ=i:r=i,i.prevZ=s,s=i;n=o}s.nextZ=null,t*=2}while(e>1);return r}function ip(r,e,t,n,i){return r=(r-t)*i|0,e=(e-n)*i|0,r=(r|r<<8)&16711935,r=(r|r<<4)&252645135,r=(r|r<<2)&858993459,r=(r|r<<1)&1431655765,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,r|e<<1}function Yv(r){let e=r,t=r;do(e.x=(r-o)*(s-a)&&(r-o)*(n-a)>=(t-o)*(e-a)&&(t-o)*(s-a)>=(i-o)*(n-a)}function Kl(r,e,t,n,i,s,o,a){return!(r===o&&e===a)&&sy(r,e,t,n,i,s,o,a)}function Kv(r,e){return r.next.i!==e.i&&r.prev.i!==e.i&&!Zv(r,e)&&(pc(r,e)&&pc(e,r)&&Jv(r,e)&&(mn(r.prev,r,e.prev)||mn(r,e.prev,e))||Da(r,e)&&mn(r.prev,r,r.next)>0&&mn(e.prev,e,e.next)>0)}function mn(r,e,t){return(e.y-r.y)*(t.x-e.x)-(e.x-r.x)*(t.y-e.y)}function Da(r,e){return r.x===e.x&&r.y===e.y}function oy(r,e,t,n){let i=ch(mn(r,e,t)),s=ch(mn(r,e,n)),o=ch(mn(t,n,r)),a=ch(mn(t,n,e));return!!(i!==s&&o!==a||i===0&&lh(r,t,e)||s===0&&lh(r,n,e)||o===0&&lh(t,r,n)||a===0&&lh(t,e,n))}function lh(r,e,t){return e.x<=Math.max(r.x,t.x)&&e.x>=Math.min(r.x,t.x)&&e.y<=Math.max(r.y,t.y)&&e.y>=Math.min(r.y,t.y)}function ch(r){return r>0?1:r<0?-1:0}function Zv(r,e){let t=r;do{if(t.i!==r.i&&t.next.i!==r.i&&t.i!==e.i&&t.next.i!==e.i&&oy(t,t.next,r,e))return!0;t=t.next}while(t!==r);return!1}function pc(r,e){return mn(r.prev,r,r.next)<0?mn(r,e,r.next)>=0&&mn(r,r.prev,e)>=0:mn(r,e,r.prev)<0||mn(r,r.next,e)<0}function Jv(r,e){let t=r,n=!1,i=(r.x+e.x)/2,s=(r.y+e.y)/2;do t.y>s!=t.next.y>s&&t.next.y!==t.y&&i<(t.next.x-t.x)*(s-t.y)/(t.next.y-t.y)+t.x&&(n=!n),t=t.next;while(t!==r);return n}function ay(r,e){let t=rp(r.i,r.x,r.y),n=rp(e.i,e.x,e.y),i=r.next,s=e.prev;return r.next=e,e.prev=r,t.next=i,i.prev=t,n.next=t,t.prev=n,s.next=n,n.prev=s,n}function Qg(r,e,t,n){let i=rp(r,e,t);return n?(i.next=n.next,i.prev=n,n.next.prev=i,n.next=i):(i.prev=i,i.next=i),i}function mc(r){r.next.prev=r.prev,r.prev.next=r.next,r.prevZ&&(r.prevZ.nextZ=r.nextZ),r.nextZ&&(r.nextZ.prevZ=r.prevZ)}function rp(r,e,t){return{i:r,x:e,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function $v(r,e,t,n){let i=0;for(let s=e,o=t-n;s2&&r[e-1].equals(r[0])&&r.pop()}function t0(r,e){for(let t=0;tNumber.EPSILON){let Q=Math.sqrt(D),ne=Math.sqrt(L*L+O*O),oe=ae.x-H/Q,he=ae.y+ve/Q,ge=pe.x-O/ne,de=pe.y+L/ne,Ee=((ge-oe)*O-(de-he)*L)/(ve*O-H*L);ue=oe+ve*Ee-w.x,ee=he+H*Ee-w.y;let Re=ue*ue+ee*ee;if(Re<=2)return new Pe(ue,ee);$=Math.sqrt(Re/2)}else{let Q=!1;ve>Number.EPSILON?L>Number.EPSILON&&(Q=!0):ve<-Number.EPSILON?L<-Number.EPSILON&&(Q=!0):Math.sign(H)===Math.sign(O)&&(Q=!0),Q?(ue=-H,ee=ve,$=Math.sqrt(D)):(ue=ve,ee=H,$=Math.sqrt(D/2))}return new Pe(ue/$,ee/$)}let le=[];for(let w=0,ae=ie.length,pe=ae-1,ue=w+1;w=0;w--){let ae=w/d,pe=p*Math.cos(ae*Math.PI/2),ue=g*Math.sin(ae*Math.PI/2)+y;for(let ee=0,$=ie.length;ee<$;ee++){let ve=Y(ie[ee],le[ee],ue);j(ve.x,ve.y,h+pe)}for(let ee=0,$=R.length;ee<$;ee++){let ve=R[ee];Te=ye[ee];for(let H=0,L=ve.length;H=0;){let ue=pe,ee=pe-1;ee<0&&(ee=w.length-1);for(let $=0,ve=u+d*2;$0)&&p.push(v,x,I),(m!==n-1||l=s)){let a=t[1];e=s)break t}o=n,n=0;break n}break e}for(;n>>1;et;)--o;if(++o,s!==0||o!==i){s>=o&&(o=Math.max(o,1),s=o-1);let a=this.getValueSize();this.times=n.slice(s,o),this.values=this.values.slice(s*a,o*a)}return this}validate(){let e=!0,t=this.getValueSize();t-Math.floor(t)!==0&&(console.error("THREE.KeyframeTrack: Invalid value size in track.",this),e=!1);let n=this.times,i=this.values,s=n.length;s===0&&(console.error("THREE.KeyframeTrack: Track is empty.",this),e=!1);let o=null;for(let a=0;a!==s;a++){let l=n[a];if(typeof l=="number"&&isNaN(l)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,a,l),e=!1;break}if(o!==null&&o>l){console.error("THREE.KeyframeTrack: Out of order keys.",this,a,l,o),e=!1;break}o=l}if(i!==void 0&&t_(i))for(let a=0,l=i.length;a!==l;++a){let c=i[a];if(isNaN(c)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,a,c),e=!1;break}}return e}optimize(){let e=this.times.slice(),t=this.values.slice(),n=this.getValueSize(),i=this.getInterpolation()===hh,s=e.length-1,o=1;for(let a=1;a0){e[o]=e[s];for(let a=s*n,l=o*n,c=0;c!==n;++c)t[l+c]=t[a+c];++o}return o!==e.length?(this.times=e.slice(0,o),this.values=t.slice(0,o*n)):(this.times=e,this.values=t),this}clone(){let e=this.times.slice(),t=this.values.slice(),n=this.constructor,i=new n(this.name,e,t);return i.createInterpolant=this.createInterpolant,i}};yi.prototype.ValueTypeName="";yi.prototype.TimeBufferType=Float32Array;yi.prototype.ValueBufferType=Float32Array;yi.prototype.DefaultInterpolation=ph;var ns=class extends yi{constructor(e,t,n){super(e,t,n)}};ns.prototype.ValueTypeName="bool";ns.prototype.ValueBufferType=Array;ns.prototype.DefaultInterpolation=Ql;ns.prototype.InterpolantFactoryMethodLinear=void 0;ns.prototype.InterpolantFactoryMethodSmooth=void 0;var yc=class extends yi{constructor(e,t,n,i){super(e,t,n,i)}};yc.prototype.ValueTypeName="color";var is=class extends yi{constructor(e,t,n,i){super(e,t,n,i)}};is.prototype.ValueTypeName="number";var Nh=class extends yo{constructor(e,t,n,i){super(e,t,n,i)}interpolate_(e,t,n,i){let s=this.resultBuffer,o=this.sampleValues,a=this.valueSize,l=(n-t)/(i-t),c=e*a;for(let u=c+a;c!==u;c+=4)an.slerpFlat(s,0,o,c-a,o,c,l);return s}},Hi=class extends yi{constructor(e,t,n,i){super(e,t,n,i)}InterpolantFactoryMethodLinear(e){return new Nh(this.times,this.values,this.getValueSize(),e)}};Hi.prototype.ValueTypeName="quaternion";Hi.prototype.InterpolantFactoryMethodSmooth=void 0;var rs=class extends yi{constructor(e,t,n){super(e,t,n)}};rs.prototype.ValueTypeName="string";rs.prototype.ValueBufferType=Array;rs.prototype.DefaultInterpolation=Ql;rs.prototype.InterpolantFactoryMethodLinear=void 0;rs.prototype.InterpolantFactoryMethodSmooth=void 0;var Wi=class extends yi{constructor(e,t,n,i){super(e,t,n,i)}};Wi.prototype.ValueTypeName="vector";var Ps=class{constructor(e="",t=-1,n=[],i=V0){this.name=e,this.tracks=n,this.duration=t,this.blendMode=i,this.uuid=us(),this.duration<0&&this.resetDuration()}static parse(e){let t=[],n=e.tracks,i=1/(e.fps||1);for(let o=0,a=n.length;o!==a;++o)t.push(r_(n[o]).scale(i));let s=new this(e.name,e.duration,t,e.blendMode);return s.uuid=e.uuid,s}static toJSON(e){let t=[],n=e.tracks,i={name:e.name,duration:e.duration,tracks:t,uuid:e.uuid,blendMode:e.blendMode};for(let s=0,o=n.length;s!==o;++s)t.push(yi.toJSON(n[s]));return i}static CreateFromMorphTargetSequence(e,t,n,i){let s=t.length,o=[];for(let a=0;a1){let h=u[1],f=i[h];f||(i[h]=f=[]),f.push(c)}}let o=[];for(let a in i)o.push(this.CreateFromMorphTargetSequence(a,i[a],t,n));return o}static parseAnimation(e,t){if(console.warn("THREE.AnimationClip: parseAnimation() is deprecated and will be removed with r185"),!e)return console.error("THREE.AnimationClip: No animation in JSONLoader data."),null;let n=function(h,f,p,g,y){if(p.length!==0){let d=[],m=[];ly(p,d,m,g),d.length!==0&&y.push(new h(f,d,m))}},i=[],s=e.name||"default",o=e.fps||30,a=e.blendMode,l=e.length||-1,c=e.hierarchy||[];for(let h=0;h{t&&t(s),this.manager.itemEnd(e)},0),s;if(Kr[e]!==void 0){Kr[e].push({onLoad:t,onProgress:n,onError:i});return}Kr[e]=[],Kr[e].push({onLoad:t,onProgress:n,onError:i});let o=new Request(e,{headers:new Headers(this.requestHeader),credentials:this.withCredentials?"include":"same-origin"}),a=this.mimeType,l=this.responseType;fetch(o).then(c=>{if(c.status===200||c.status===0){if(c.status===0&&console.warn("THREE.FileLoader: HTTP Status 0 received."),typeof ReadableStream>"u"||c.body===void 0||c.body.getReader===void 0)return c;let u=Kr[e],h=c.body.getReader(),f=c.headers.get("X-File-Size")||c.headers.get("Content-Length"),p=f?parseInt(f):0,g=p!==0,y=0,d=new ReadableStream({start(m){_();function _(){h.read().then(({done:v,value:x})=>{if(v)m.close();else{y+=x.byteLength;let S=new ProgressEvent("progress",{lengthComputable:g,loaded:y,total:p});for(let I=0,A=u.length;I{m.error(v)})}}});return new Response(d)}else throw new op(`fetch for "${c.url}" responded with ${c.status}: ${c.statusText}`,c)}).then(c=>{switch(l){case"arraybuffer":return c.arrayBuffer();case"blob":return c.blob();case"document":return c.text().then(u=>new DOMParser().parseFromString(u,a));case"json":return c.json();default:if(a==="")return c.text();{let h=/charset="?([^;"\s]*)"?/i.exec(a),f=h&&h[1]?h[1].toLowerCase():void 0,p=new TextDecoder(f);return c.arrayBuffer().then(g=>p.decode(g))}}}).then(c=>{xc.add(e,c);let u=Kr[e];delete Kr[e];for(let h=0,f=u.length;h{let u=Kr[e];if(u===void 0)throw this.manager.itemError(e),c;delete Kr[e];for(let h=0,f=u.length;h{this.manager.itemEnd(e)}),this.manager.itemStart(e)}setResponseType(e){return this.responseType=e,this}setMimeType(e){return this.mimeType=e,this}};var vc=class extends zt{constructor(e){super(e)}load(e,t,n,i){this.path!==void 0&&(e=this.path+e),e=this.manager.resolveURL(e);let s=this,o=xc.get(e);if(o!==void 0)return s.manager.itemStart(e),setTimeout(function(){t&&t(o),s.manager.itemEnd(e)},0),o;let a=Aa("img");function l(){u(),xc.add(e,this),t&&t(this),s.manager.itemEnd(e)}function c(h){u(),i&&i(h),s.manager.itemError(e),s.manager.itemEnd(e)}function u(){a.removeEventListener("load",l,!1),a.removeEventListener("error",c,!1)}return a.addEventListener("load",l,!1),a.addEventListener("error",c,!1),e.slice(0,5)!=="data:"&&this.crossOrigin!==void 0&&(a.crossOrigin=this.crossOrigin),s.manager.itemStart(e),a.src=e,a}},_c=class extends zt{constructor(e){super(e)}load(e,t,n,i){let s=new Ia;s.colorSpace=yt;let o=new vc(this.manager);o.setCrossOrigin(this.crossOrigin),o.setPath(this.path);let a=0;function l(c){o.load(e[c],function(u){s.images[c]=u,a++,a===6&&(s.needsUpdate=!0,t&&t(s))},void 0,i)}for(let c=0;c=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y}containsBox(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y}getParameter(e,t){return t.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y))}intersectsBox(e){return e.max.x>=this.min.x&&e.min.x<=this.max.x&&e.max.y>=this.min.y&&e.min.y<=this.max.y}clampPoint(e,t){return t.copy(e).clamp(this.min,this.max)}distanceToPoint(e){return this.clampPoint(e,l0).distanceTo(e)}intersect(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this}union(e){return this.min.min(e.min),this.max.max(e.max),this}translate(e){return this.min.add(e),this.max.add(e),this}equals(e){return e.min.equals(this.min)&&e.max.equals(this.max)}};var ar=class{constructor(){this.type="ShapePath",this.color=new He,this.subPaths=[],this.currentPath=null}moveTo(e,t){return this.currentPath=new Cr,this.subPaths.push(this.currentPath),this.currentPath.moveTo(e,t),this}lineTo(e,t){return this.currentPath.lineTo(e,t),this}quadraticCurveTo(e,t,n,i){return this.currentPath.quadraticCurveTo(e,t,n,i),this}bezierCurveTo(e,t,n,i,s,o){return this.currentPath.bezierCurveTo(e,t,n,i,s,o),this}splineThru(e){return this.currentPath.splineThru(e),this}toShapes(e){function t(m){let _=[];for(let v=0,x=m.length;vNumber.EPSILON){if(b<0&&(A=_[I],E=-E,C=_[S],b=-b),m.yC.y)continue;if(m.y===A.y){if(m.x===A.x)return!0}else{let R=b*(m.x-A.x)-E*(m.y-A.y);if(R===0)return!0;if(R<0)continue;x=!x}}else{if(m.y!==A.y)continue;if(C.x<=m.x&&m.x<=A.x||A.x<=m.x&&m.x<=C.x)return!0}}return x}let i=gi.isClockWise,s=this.subPaths;if(s.length===0)return[];let o,a,l,c=[];if(s.length===1)return a=s[0],l=new $r,l.curves=a.curves,c.push(l),c;let u=!i(s[0].getPoints());u=e?!u:u;let h=[],f=[],p=[],g=0,y;f[g]=void 0,p[g]=[];for(let m=0,_=s.length;m<_;m++)a=s[m],y=a.getPoints(),o=i(y),o=e?!o:o,o?(!u&&f[g]&&g++,f[g]={s:new $r,p:y},f[g].s.curves=a.curves,u&&g++,p[g]=[]):p[g].push({h:a,p:y[0]});if(!f[0])return t(s);if(f.length>1){let m=!1,_=0;for(let v=0,x=f.length;v0&&m===!1&&(p=h)}let d;for(let m=0,_=f.length;m<_;m++){l=f[m].s,c.push(l),d=p[m];for(let v=0,x=d.length;vp.start-g.start);let f=0;for(let p=1;p 0 + vec4 plane; + #ifdef ALPHA_TO_COVERAGE + float distanceToPlane, distanceGradient; + float clipOpacity = 1.0; + #pragma unroll_loop_start + for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; + distanceGradient = fwidth( distanceToPlane ) / 2.0; + clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); + if ( clipOpacity == 0.0 ) discard; + } + #pragma unroll_loop_end + #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES + float unionClipOpacity = 1.0; + #pragma unroll_loop_start + for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; + distanceGradient = fwidth( distanceToPlane ) / 2.0; + unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); + } + #pragma unroll_loop_end + clipOpacity *= 1.0 - unionClipOpacity; + #endif + diffuseColor.a *= clipOpacity; + if ( diffuseColor.a == 0.0 ) discard; + #else + #pragma unroll_loop_start + for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; + } + #pragma unroll_loop_end + #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES + bool clipped = true; + #pragma unroll_loop_start + for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; + } + #pragma unroll_loop_end + if ( clipped ) discard; + #endif + #endif +#endif`,P_=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; + uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; +#endif`,N_=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; +#endif`,L_=`#if NUM_CLIPPING_PLANES > 0 + vClipPosition = - mvPosition.xyz; +#endif`,O_=`#if defined( USE_COLOR_ALPHA ) + diffuseColor *= vColor; +#elif defined( USE_COLOR ) + diffuseColor.rgb *= vColor; +#endif`,D_=`#if defined( USE_COLOR_ALPHA ) + varying vec4 vColor; +#elif defined( USE_COLOR ) + varying vec3 vColor; +#endif`,F_=`#if defined( USE_COLOR_ALPHA ) + varying vec4 vColor; +#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) + varying vec3 vColor; +#endif`,U_=`#if defined( USE_COLOR_ALPHA ) + vColor = vec4( 1.0 ); +#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) + vColor = vec3( 1.0 ); +#endif +#ifdef USE_COLOR + vColor *= color; +#endif +#ifdef USE_INSTANCING_COLOR + vColor.xyz *= instanceColor.xyz; +#endif +#ifdef USE_BATCHING_COLOR + vec3 batchingColor = getBatchingColor( getIndirectIndex( gl_DrawID ) ); + vColor.xyz *= batchingColor.xyz; +#endif`,k_=`#define PI 3.141592653589793 +#define PI2 6.283185307179586 +#define PI_HALF 1.5707963267948966 +#define RECIPROCAL_PI 0.3183098861837907 +#define RECIPROCAL_PI2 0.15915494309189535 +#define EPSILON 1e-6 +#ifndef saturate +#define saturate( a ) clamp( a, 0.0, 1.0 ) +#endif +#define whiteComplement( a ) ( 1.0 - saturate( a ) ) +float pow2( const in float x ) { return x*x; } +vec3 pow2( const in vec3 x ) { return x*x; } +float pow3( const in float x ) { return x*x*x; } +float pow4( const in float x ) { float x2 = x*x; return x2*x2; } +float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } +float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } +highp float rand( const in vec2 uv ) { + const highp float a = 12.9898, b = 78.233, c = 43758.5453; + highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); + return fract( sin( sn ) * c ); +} +#ifdef HIGH_PRECISION + float precisionSafeLength( vec3 v ) { return length( v ); } +#else + float precisionSafeLength( vec3 v ) { + float maxComponent = max3( abs( v ) ); + return length( v / maxComponent ) * maxComponent; + } +#endif +struct IncidentLight { + vec3 color; + vec3 direction; + bool visible; +}; +struct ReflectedLight { + vec3 directDiffuse; + vec3 directSpecular; + vec3 indirectDiffuse; + vec3 indirectSpecular; +}; +#ifdef USE_ALPHAHASH + varying vec3 vPosition; +#endif +vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); +} +vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { + return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); +} +mat3 transposeMat3( const in mat3 m ) { + mat3 tmp; + tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x ); + tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y ); + tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z ); + return tmp; +} +bool isPerspectiveMatrix( mat4 m ) { + return m[ 2 ][ 3 ] == - 1.0; +} +vec2 equirectUv( in vec3 dir ) { + float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; + float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; + return vec2( u, v ); +} +vec3 BRDF_Lambert( const in vec3 diffuseColor ) { + return RECIPROCAL_PI * diffuseColor; +} +vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { + float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); + return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); +} +float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { + float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); + return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); +} // validated`,B_=`#ifdef ENVMAP_TYPE_CUBE_UV + #define cubeUV_minMipLevel 4.0 + #define cubeUV_minTileSize 16.0 + float getFace( vec3 direction ) { + vec3 absDirection = abs( direction ); + float face = - 1.0; + if ( absDirection.x > absDirection.z ) { + if ( absDirection.x > absDirection.y ) + face = direction.x > 0.0 ? 0.0 : 3.0; + else + face = direction.y > 0.0 ? 1.0 : 4.0; + } else { + if ( absDirection.z > absDirection.y ) + face = direction.z > 0.0 ? 2.0 : 5.0; + else + face = direction.y > 0.0 ? 1.0 : 4.0; + } + return face; + } + vec2 getUV( vec3 direction, float face ) { + vec2 uv; + if ( face == 0.0 ) { + uv = vec2( direction.z, direction.y ) / abs( direction.x ); + } else if ( face == 1.0 ) { + uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); + } else if ( face == 2.0 ) { + uv = vec2( - direction.x, direction.y ) / abs( direction.z ); + } else if ( face == 3.0 ) { + uv = vec2( - direction.z, direction.y ) / abs( direction.x ); + } else if ( face == 4.0 ) { + uv = vec2( - direction.x, direction.z ) / abs( direction.y ); + } else { + uv = vec2( direction.x, direction.y ) / abs( direction.z ); + } + return 0.5 * ( uv + 1.0 ); + } + vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { + float face = getFace( direction ); + float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); + mipInt = max( mipInt, cubeUV_minMipLevel ); + float faceSize = exp2( mipInt ); + highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; + if ( face > 2.0 ) { + uv.y += faceSize; + face -= 3.0; + } + uv.x += face * faceSize; + uv.x += filterInt * 3.0 * cubeUV_minTileSize; + uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); + uv.x *= CUBEUV_TEXEL_WIDTH; + uv.y *= CUBEUV_TEXEL_HEIGHT; + #ifdef texture2DGradEXT + return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; + #else + return texture2D( envMap, uv ).rgb; + #endif + } + #define cubeUV_r0 1.0 + #define cubeUV_m0 - 2.0 + #define cubeUV_r1 0.8 + #define cubeUV_m1 - 1.0 + #define cubeUV_r4 0.4 + #define cubeUV_m4 2.0 + #define cubeUV_r5 0.305 + #define cubeUV_m5 3.0 + #define cubeUV_r6 0.21 + #define cubeUV_m6 4.0 + float roughnessToMip( float roughness ) { + float mip = 0.0; + if ( roughness >= cubeUV_r1 ) { + mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; + } else if ( roughness >= cubeUV_r4 ) { + mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; + } else if ( roughness >= cubeUV_r5 ) { + mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; + } else if ( roughness >= cubeUV_r6 ) { + mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; + } else { + mip = - 2.0 * log2( 1.16 * roughness ); } + return mip; + } + vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { + float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); + float mipF = fract( mip ); + float mipInt = floor( mip ); + vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); + if ( mipF == 0.0 ) { + return vec4( color0, 1.0 ); + } else { + vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); + return vec4( mix( color0, color1, mipF ), 1.0 ); + } + } +#endif`,G_=`vec3 transformedNormal = objectNormal; +#ifdef USE_TANGENT + vec3 transformedTangent = objectTangent; +#endif +#ifdef USE_BATCHING + mat3 bm = mat3( batchingMatrix ); + transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); + transformedNormal = bm * transformedNormal; + #ifdef USE_TANGENT + transformedTangent = bm * transformedTangent; + #endif +#endif +#ifdef USE_INSTANCING + mat3 im = mat3( instanceMatrix ); + transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); + transformedNormal = im * transformedNormal; + #ifdef USE_TANGENT + transformedTangent = im * transformedTangent; + #endif +#endif +transformedNormal = normalMatrix * transformedNormal; +#ifdef FLIP_SIDED + transformedNormal = - transformedNormal; +#endif +#ifdef USE_TANGENT + transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; + #ifdef FLIP_SIDED + transformedTangent = - transformedTangent; + #endif +#endif`,V_=`#ifdef USE_DISPLACEMENTMAP + uniform sampler2D displacementMap; + uniform float displacementScale; + uniform float displacementBias; +#endif`,z_=`#ifdef USE_DISPLACEMENTMAP + transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); +#endif`,H_=`#ifdef USE_EMISSIVEMAP + vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); + #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE + emissiveColor = sRGBTransferEOTF( emissiveColor ); + #endif + totalEmissiveRadiance *= emissiveColor.rgb; +#endif`,W_=`#ifdef USE_EMISSIVEMAP + uniform sampler2D emissiveMap; +#endif`,X_="gl_FragColor = linearToOutputTexel( gl_FragColor );",q_=`vec4 LinearTransferOETF( in vec4 value ) { + return value; +} +vec4 sRGBTransferEOTF( in vec4 value ) { + return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a ); +} +vec4 sRGBTransferOETF( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); +}`,j_=`#ifdef USE_ENVMAP + #ifdef ENV_WORLDPOS + vec3 cameraToFrag; + if ( isOrthographic ) { + cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); + } else { + cameraToFrag = normalize( vWorldPosition - cameraPosition ); + } + vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); + #ifdef ENVMAP_MODE_REFLECTION + vec3 reflectVec = reflect( cameraToFrag, worldNormal ); + #else + vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); + #endif + #else + vec3 reflectVec = vReflect; + #endif + #ifdef ENVMAP_TYPE_CUBE + vec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); + #else + vec4 envColor = vec4( 0.0 ); + #endif + #ifdef ENVMAP_BLENDING_MULTIPLY + outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); + #elif defined( ENVMAP_BLENDING_MIX ) + outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); + #elif defined( ENVMAP_BLENDING_ADD ) + outgoingLight += envColor.xyz * specularStrength * reflectivity; + #endif +#endif`,Y_=`#ifdef USE_ENVMAP + uniform float envMapIntensity; + uniform float flipEnvMap; + uniform mat3 envMapRotation; + #ifdef ENVMAP_TYPE_CUBE + uniform samplerCube envMap; + #else + uniform sampler2D envMap; + #endif + +#endif`,K_=`#ifdef USE_ENVMAP + uniform float reflectivity; + #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) + #define ENV_WORLDPOS + #endif + #ifdef ENV_WORLDPOS + varying vec3 vWorldPosition; + uniform float refractionRatio; + #else + varying vec3 vReflect; + #endif +#endif`,Z_=`#ifdef USE_ENVMAP + #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) + #define ENV_WORLDPOS + #endif + #ifdef ENV_WORLDPOS + + varying vec3 vWorldPosition; + #else + varying vec3 vReflect; + uniform float refractionRatio; + #endif +#endif`,J_=`#ifdef USE_ENVMAP + #ifdef ENV_WORLDPOS + vWorldPosition = worldPosition.xyz; + #else + vec3 cameraToVertex; + if ( isOrthographic ) { + cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); + } else { + cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); + } + vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); + #ifdef ENVMAP_MODE_REFLECTION + vReflect = reflect( cameraToVertex, worldNormal ); + #else + vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); + #endif + #endif +#endif`,$_=`#ifdef USE_FOG + vFogDepth = - mvPosition.z; +#endif`,Q_=`#ifdef USE_FOG + varying float vFogDepth; +#endif`,eT=`#ifdef USE_FOG + #ifdef FOG_EXP2 + float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); + #else + float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); + #endif + gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); +#endif`,tT=`#ifdef USE_FOG + uniform vec3 fogColor; + varying float vFogDepth; + #ifdef FOG_EXP2 + uniform float fogDensity; + #else + uniform float fogNear; + uniform float fogFar; + #endif +#endif`,nT=`#ifdef USE_GRADIENTMAP + uniform sampler2D gradientMap; +#endif +vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { + float dotNL = dot( normal, lightDirection ); + vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); + #ifdef USE_GRADIENTMAP + return vec3( texture2D( gradientMap, coord ).r ); + #else + vec2 fw = fwidth( coord ) * 0.5; + return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); + #endif +}`,iT=`#ifdef USE_LIGHTMAP + uniform sampler2D lightMap; + uniform float lightMapIntensity; +#endif`,rT=`LambertMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.specularStrength = specularStrength;`,sT=`varying vec3 vViewPosition; +struct LambertMaterial { + vec3 diffuseColor; + float specularStrength; +}; +void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_Lambert +#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,oT=`uniform bool receiveShadow; +uniform vec3 ambientLightColor; +#if defined( USE_LIGHT_PROBES ) + uniform vec3 lightProbe[ 9 ]; +#endif +vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { + float x = normal.x, y = normal.y, z = normal.z; + vec3 result = shCoefficients[ 0 ] * 0.886227; + result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; + result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; + result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; + result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; + result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; + result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); + result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; + result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); + return result; +} +vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { + vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); + vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); + return irradiance; +} +vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { + vec3 irradiance = ambientLightColor; + return irradiance; +} +float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { + float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); + if ( cutoffDistance > 0.0 ) { + distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); + } + return distanceFalloff; +} +float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { + return smoothstep( coneCosine, penumbraCosine, angleCosine ); +} +#if NUM_DIR_LIGHTS > 0 + struct DirectionalLight { + vec3 direction; + vec3 color; + }; + uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; + void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { + light.color = directionalLight.color; + light.direction = directionalLight.direction; + light.visible = true; + } +#endif +#if NUM_POINT_LIGHTS > 0 + struct PointLight { + vec3 position; + vec3 color; + float distance; + float decay; + }; + uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; + void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { + vec3 lVector = pointLight.position - geometryPosition; + light.direction = normalize( lVector ); + float lightDistance = length( lVector ); + light.color = pointLight.color; + light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); + light.visible = ( light.color != vec3( 0.0 ) ); + } +#endif +#if NUM_SPOT_LIGHTS > 0 + struct SpotLight { + vec3 position; + vec3 direction; + vec3 color; + float distance; + float decay; + float coneCos; + float penumbraCos; + }; + uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; + void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { + vec3 lVector = spotLight.position - geometryPosition; + light.direction = normalize( lVector ); + float angleCos = dot( light.direction, spotLight.direction ); + float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); + if ( spotAttenuation > 0.0 ) { + float lightDistance = length( lVector ); + light.color = spotLight.color * spotAttenuation; + light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); + light.visible = ( light.color != vec3( 0.0 ) ); + } else { + light.color = vec3( 0.0 ); + light.visible = false; + } + } +#endif +#if NUM_RECT_AREA_LIGHTS > 0 + struct RectAreaLight { + vec3 color; + vec3 position; + vec3 halfWidth; + vec3 halfHeight; + }; + uniform sampler2D ltc_1; uniform sampler2D ltc_2; + uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; +#endif +#if NUM_HEMI_LIGHTS > 0 + struct HemisphereLight { + vec3 direction; + vec3 skyColor; + vec3 groundColor; + }; + uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; + vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { + float dotNL = dot( normal, hemiLight.direction ); + float hemiDiffuseWeight = 0.5 * dotNL + 0.5; + vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); + return irradiance; + } +#endif`,aT=`#ifdef USE_ENVMAP + vec3 getIBLIrradiance( const in vec3 normal ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); + vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); + return PI * envMapColor.rgb * envMapIntensity; + #else + return vec3( 0.0 ); + #endif + } + vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 reflectVec = reflect( - viewDir, normal ); + reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) ); + reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); + vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); + return envMapColor.rgb * envMapIntensity; + #else + return vec3( 0.0 ); + #endif + } + #ifdef USE_ANISOTROPY + vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 bentNormal = cross( bitangent, viewDir ); + bentNormal = normalize( cross( bentNormal, bitangent ) ); + bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); + return getIBLRadiance( viewDir, bentNormal, roughness ); + #else + return vec3( 0.0 ); + #endif + } + #endif +#endif`,lT=`ToonMaterial material; +material.diffuseColor = diffuseColor.rgb;`,cT=`varying vec3 vViewPosition; +struct ToonMaterial { + vec3 diffuseColor; +}; +void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { + vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_Toon +#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,uT=`BlinnPhongMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.specularColor = specular; +material.specularShininess = shininess; +material.specularStrength = specularStrength;`,hT=`varying vec3 vViewPosition; +struct BlinnPhongMaterial { + vec3 diffuseColor; + vec3 specularColor; + float specularShininess; + float specularStrength; +}; +void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); + reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; +} +void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_BlinnPhong +#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,fT=`PhysicalMaterial material; +material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); +vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); +float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); +material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; +material.roughness = min( material.roughness, 1.0 ); +#ifdef IOR + material.ior = ior; + #ifdef USE_SPECULAR + float specularIntensityFactor = specularIntensity; + vec3 specularColorFactor = specularColor; + #ifdef USE_SPECULAR_COLORMAP + specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; + #endif + #ifdef USE_SPECULAR_INTENSITYMAP + specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; + #endif + material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); + #else + float specularIntensityFactor = 1.0; + vec3 specularColorFactor = vec3( 1.0 ); + material.specularF90 = 1.0; + #endif + material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); +#else + material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); + material.specularF90 = 1.0; +#endif +#ifdef USE_CLEARCOAT + material.clearcoat = clearcoat; + material.clearcoatRoughness = clearcoatRoughness; + material.clearcoatF0 = vec3( 0.04 ); + material.clearcoatF90 = 1.0; + #ifdef USE_CLEARCOATMAP + material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; + #endif + #ifdef USE_CLEARCOAT_ROUGHNESSMAP + material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; + #endif + material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); + material.clearcoatRoughness += geometryRoughness; + material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); +#endif +#ifdef USE_DISPERSION + material.dispersion = dispersion; +#endif +#ifdef USE_IRIDESCENCE + material.iridescence = iridescence; + material.iridescenceIOR = iridescenceIOR; + #ifdef USE_IRIDESCENCEMAP + material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; + #endif + #ifdef USE_IRIDESCENCE_THICKNESSMAP + material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; + #else + material.iridescenceThickness = iridescenceThicknessMaximum; + #endif +#endif +#ifdef USE_SHEEN + material.sheenColor = sheenColor; + #ifdef USE_SHEEN_COLORMAP + material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; + #endif + material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); + #ifdef USE_SHEEN_ROUGHNESSMAP + material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; + #endif +#endif +#ifdef USE_ANISOTROPY + #ifdef USE_ANISOTROPYMAP + mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); + vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; + vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; + #else + vec2 anisotropyV = anisotropyVector; + #endif + material.anisotropy = length( anisotropyV ); + if( material.anisotropy == 0.0 ) { + anisotropyV = vec2( 1.0, 0.0 ); + } else { + anisotropyV /= material.anisotropy; + material.anisotropy = saturate( material.anisotropy ); + } + material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); + material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; + material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; +#endif`,dT=`struct PhysicalMaterial { + vec3 diffuseColor; + float roughness; + vec3 specularColor; + float specularF90; + float dispersion; + #ifdef USE_CLEARCOAT + float clearcoat; + float clearcoatRoughness; + vec3 clearcoatF0; + float clearcoatF90; + #endif + #ifdef USE_IRIDESCENCE + float iridescence; + float iridescenceIOR; + float iridescenceThickness; + vec3 iridescenceFresnel; + vec3 iridescenceF0; + #endif + #ifdef USE_SHEEN + vec3 sheenColor; + float sheenRoughness; + #endif + #ifdef IOR + float ior; + #endif + #ifdef USE_TRANSMISSION + float transmission; + float transmissionAlpha; + float thickness; + float attenuationDistance; + vec3 attenuationColor; + #endif + #ifdef USE_ANISOTROPY + float anisotropy; + float alphaT; + vec3 anisotropyT; + vec3 anisotropyB; + #endif +}; +vec3 clearcoatSpecularDirect = vec3( 0.0 ); +vec3 clearcoatSpecularIndirect = vec3( 0.0 ); +vec3 sheenSpecularDirect = vec3( 0.0 ); +vec3 sheenSpecularIndirect = vec3(0.0 ); +vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { + float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); + float x2 = x * x; + float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); + return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); +} +float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { + float a2 = pow2( alpha ); + float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); + float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); + return 0.5 / max( gv + gl, EPSILON ); +} +float D_GGX( const in float alpha, const in float dotNH ) { + float a2 = pow2( alpha ); + float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; + return RECIPROCAL_PI * a2 / pow2( denom ); +} +#ifdef USE_ANISOTROPY + float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { + float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); + float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); + float v = 0.5 / ( gv + gl ); + return saturate(v); + } + float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { + float a2 = alphaT * alphaB; + highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); + highp float v2 = dot( v, v ); + float w2 = a2 / v2; + return RECIPROCAL_PI * a2 * pow2 ( w2 ); + } +#endif +#ifdef USE_CLEARCOAT + vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { + vec3 f0 = material.clearcoatF0; + float f90 = material.clearcoatF90; + float roughness = material.clearcoatRoughness; + float alpha = pow2( roughness ); + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float dotVH = saturate( dot( viewDir, halfDir ) ); + vec3 F = F_Schlick( f0, f90, dotVH ); + float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); + float D = D_GGX( alpha, dotNH ); + return F * ( V * D ); + } +#endif +vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { + vec3 f0 = material.specularColor; + float f90 = material.specularF90; + float roughness = material.roughness; + float alpha = pow2( roughness ); + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float dotVH = saturate( dot( viewDir, halfDir ) ); + vec3 F = F_Schlick( f0, f90, dotVH ); + #ifdef USE_IRIDESCENCE + F = mix( F, material.iridescenceFresnel, material.iridescence ); + #endif + #ifdef USE_ANISOTROPY + float dotTL = dot( material.anisotropyT, lightDir ); + float dotTV = dot( material.anisotropyT, viewDir ); + float dotTH = dot( material.anisotropyT, halfDir ); + float dotBL = dot( material.anisotropyB, lightDir ); + float dotBV = dot( material.anisotropyB, viewDir ); + float dotBH = dot( material.anisotropyB, halfDir ); + float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); + float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); + #else + float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); + float D = D_GGX( alpha, dotNH ); + #endif + return F * ( V * D ); +} +vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { + const float LUT_SIZE = 64.0; + const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; + const float LUT_BIAS = 0.5 / LUT_SIZE; + float dotNV = saturate( dot( N, V ) ); + vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); + uv = uv * LUT_SCALE + LUT_BIAS; + return uv; +} +float LTC_ClippedSphereFormFactor( const in vec3 f ) { + float l = length( f ); + return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); +} +vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { + float x = dot( v1, v2 ); + float y = abs( x ); + float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; + float b = 3.4175940 + ( 4.1616724 + y ) * y; + float v = a / b; + float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; + return cross( v1, v2 ) * theta_sintheta; +} +vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { + vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; + vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; + vec3 lightNormal = cross( v1, v2 ); + if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); + vec3 T1, T2; + T1 = normalize( V - N * dot( V, N ) ); + T2 = - cross( N, T1 ); + mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) ); + vec3 coords[ 4 ]; + coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); + coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); + coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); + coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); + coords[ 0 ] = normalize( coords[ 0 ] ); + coords[ 1 ] = normalize( coords[ 1 ] ); + coords[ 2 ] = normalize( coords[ 2 ] ); + coords[ 3 ] = normalize( coords[ 3 ] ); + vec3 vectorFormFactor = vec3( 0.0 ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); + float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); + return vec3( result ); +} +#if defined( USE_SHEEN ) +float D_Charlie( float roughness, float dotNH ) { + float alpha = pow2( roughness ); + float invAlpha = 1.0 / alpha; + float cos2h = dotNH * dotNH; + float sin2h = max( 1.0 - cos2h, 0.0078125 ); + return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); +} +float V_Neubelt( float dotNV, float dotNL ) { + return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); +} +vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float D = D_Charlie( sheenRoughness, dotNH ); + float V = V_Neubelt( dotNV, dotNL ); + return sheenColor * ( D * V ); +} +#endif +float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { + float dotNV = saturate( dot( normal, viewDir ) ); + float r2 = roughness * roughness; + float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; + float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; + float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); + return saturate( DG * RECIPROCAL_PI ); +} +vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { + float dotNV = saturate( dot( normal, viewDir ) ); + const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); + const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); + vec4 r = roughness * c0 + c1; + float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; + vec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw; + return fab; +} +vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { + vec2 fab = DFGApprox( normal, viewDir, roughness ); + return specularColor * fab.x + specularF90 * fab.y; +} +#ifdef USE_IRIDESCENCE +void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { +#else +void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { +#endif + vec2 fab = DFGApprox( normal, viewDir, roughness ); + #ifdef USE_IRIDESCENCE + vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); + #else + vec3 Fr = specularColor; + #endif + vec3 FssEss = Fr * fab.x + specularF90 * fab.y; + float Ess = fab.x + fab.y; + float Ems = 1.0 - Ess; + vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); + singleScatter += FssEss; + multiScatter += Fms * Ems; +} +#if NUM_RECT_AREA_LIGHTS > 0 + void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + vec3 normal = geometryNormal; + vec3 viewDir = geometryViewDir; + vec3 position = geometryPosition; + vec3 lightPos = rectAreaLight.position; + vec3 halfWidth = rectAreaLight.halfWidth; + vec3 halfHeight = rectAreaLight.halfHeight; + vec3 lightColor = rectAreaLight.color; + float roughness = material.roughness; + vec3 rectCoords[ 4 ]; + rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; + rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; + rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; + vec2 uv = LTC_Uv( normal, viewDir, roughness ); + vec4 t1 = texture2D( ltc_1, uv ); + vec4 t2 = texture2D( ltc_2, uv ); + mat3 mInv = mat3( + vec3( t1.x, 0, t1.y ), + vec3( 0, 1, 0 ), + vec3( t1.z, 0, t1.w ) + ); + vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); + reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); + reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); + } +#endif +void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + #ifdef USE_CLEARCOAT + float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); + vec3 ccIrradiance = dotNLcc * directLight.color; + clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); + #endif + #ifdef USE_SHEEN + sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); + #endif + reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material ); + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { + #ifdef USE_CLEARCOAT + clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); + #endif + #ifdef USE_SHEEN + sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); + #endif + vec3 singleScattering = vec3( 0.0 ); + vec3 multiScattering = vec3( 0.0 ); + vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; + #ifdef USE_IRIDESCENCE + computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); + #else + computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); + #endif + vec3 totalScattering = singleScattering + multiScattering; + vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); + reflectedLight.indirectSpecular += radiance * singleScattering; + reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; + reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; +} +#define RE_Direct RE_Direct_Physical +#define RE_Direct_RectArea RE_Direct_RectArea_Physical +#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical +#define RE_IndirectSpecular RE_IndirectSpecular_Physical +float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { + return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); +}`,pT=` +vec3 geometryPosition = - vViewPosition; +vec3 geometryNormal = normal; +vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); +vec3 geometryClearcoatNormal = vec3( 0.0 ); +#ifdef USE_CLEARCOAT + geometryClearcoatNormal = clearcoatNormal; +#endif +#ifdef USE_IRIDESCENCE + float dotNVi = saturate( dot( normal, geometryViewDir ) ); + if ( material.iridescenceThickness == 0.0 ) { + material.iridescence = 0.0; + } else { + material.iridescence = saturate( material.iridescence ); + } + if ( material.iridescence > 0.0 ) { + material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); + material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); + } +#endif +IncidentLight directLight; +#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) + PointLight pointLight; + #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 + PointLightShadow pointLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { + pointLight = pointLights[ i ]; + getPointLightInfo( pointLight, geometryPosition, directLight ); + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) + pointLightShadow = pointLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) + SpotLight spotLight; + vec4 spotColor; + vec3 spotLightCoord; + bool inSpotLightMap; + #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 + SpotLightShadow spotLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { + spotLight = spotLights[ i ]; + getSpotLightInfo( spotLight, geometryPosition, directLight ); + #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) + #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX + #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS + #else + #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) + #endif + #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) + spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; + inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); + spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); + directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; + #endif + #undef SPOT_LIGHT_MAP_INDEX + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + spotLightShadow = spotLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) + DirectionalLight directionalLight; + #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 + DirectionalLightShadow directionalLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { + directionalLight = directionalLights[ i ]; + getDirectionalLightInfo( directionalLight, directLight ); + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) + directionalLightShadow = directionalLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) + RectAreaLight rectAreaLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { + rectAreaLight = rectAreaLights[ i ]; + RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if defined( RE_IndirectDiffuse ) + vec3 iblIrradiance = vec3( 0.0 ); + vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); + #if defined( USE_LIGHT_PROBES ) + irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); + #endif + #if ( NUM_HEMI_LIGHTS > 0 ) + #pragma unroll_loop_start + for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { + irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); + } + #pragma unroll_loop_end + #endif +#endif +#if defined( RE_IndirectSpecular ) + vec3 radiance = vec3( 0.0 ); + vec3 clearcoatRadiance = vec3( 0.0 ); +#endif`,mT=`#if defined( RE_IndirectDiffuse ) + #ifdef USE_LIGHTMAP + vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); + vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; + irradiance += lightMapIrradiance; + #endif + #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) + iblIrradiance += getIBLIrradiance( geometryNormal ); + #endif +#endif +#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) + #ifdef USE_ANISOTROPY + radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); + #else + radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); + #endif + #ifdef USE_CLEARCOAT + clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); + #endif +#endif`,gT=`#if defined( RE_IndirectDiffuse ) + RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); +#endif +#if defined( RE_IndirectSpecular ) + RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); +#endif`,yT=`#if defined( USE_LOGDEPTHBUF ) + gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; +#endif`,xT=`#if defined( USE_LOGDEPTHBUF ) + uniform float logDepthBufFC; + varying float vFragDepth; + varying float vIsPerspective; +#endif`,vT=`#ifdef USE_LOGDEPTHBUF + varying float vFragDepth; + varying float vIsPerspective; +#endif`,_T=`#ifdef USE_LOGDEPTHBUF + vFragDepth = 1.0 + gl_Position.w; + vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); +#endif`,TT=`#ifdef USE_MAP + vec4 sampledDiffuseColor = texture2D( map, vMapUv ); + #ifdef DECODE_VIDEO_TEXTURE + sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); + #endif + diffuseColor *= sampledDiffuseColor; +#endif`,MT=`#ifdef USE_MAP + uniform sampler2D map; +#endif`,ET=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) + #if defined( USE_POINTS_UV ) + vec2 uv = vUv; + #else + vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; + #endif +#endif +#ifdef USE_MAP + diffuseColor *= texture2D( map, uv ); +#endif +#ifdef USE_ALPHAMAP + diffuseColor.a *= texture2D( alphaMap, uv ).g; +#endif`,bT=`#if defined( USE_POINTS_UV ) + varying vec2 vUv; +#else + #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) + uniform mat3 uvTransform; + #endif +#endif +#ifdef USE_MAP + uniform sampler2D map; +#endif +#ifdef USE_ALPHAMAP + uniform sampler2D alphaMap; +#endif`,ST=`float metalnessFactor = metalness; +#ifdef USE_METALNESSMAP + vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); + metalnessFactor *= texelMetalness.b; +#endif`,AT=`#ifdef USE_METALNESSMAP + uniform sampler2D metalnessMap; +#endif`,wT=`#ifdef USE_INSTANCING_MORPH + float morphTargetInfluences[ MORPHTARGETS_COUNT ]; + float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; + } +#endif`,CT=`#if defined( USE_MORPHCOLORS ) + vColor *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + #if defined( USE_COLOR_ALPHA ) + if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; + #elif defined( USE_COLOR ) + if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; + #endif + } +#endif`,IT=`#ifdef USE_MORPHNORMALS + objectNormal *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; + } +#endif`,RT=`#ifdef USE_MORPHTARGETS + #ifndef USE_INSTANCING_MORPH + uniform float morphTargetBaseInfluence; + uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; + #endif + uniform sampler2DArray morphTargetsTexture; + uniform ivec2 morphTargetsTextureSize; + vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { + int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; + int y = texelIndex / morphTargetsTextureSize.x; + int x = texelIndex - y * morphTargetsTextureSize.x; + ivec3 morphUV = ivec3( x, y, morphTargetIndex ); + return texelFetch( morphTargetsTexture, morphUV, 0 ); + } +#endif`,PT=`#ifdef USE_MORPHTARGETS + transformed *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; + } +#endif`,NT=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; +#ifdef FLAT_SHADED + vec3 fdx = dFdx( vViewPosition ); + vec3 fdy = dFdy( vViewPosition ); + vec3 normal = normalize( cross( fdx, fdy ) ); +#else + vec3 normal = normalize( vNormal ); + #ifdef DOUBLE_SIDED + normal *= faceDirection; + #endif +#endif +#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) + #ifdef USE_TANGENT + mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); + #else + mat3 tbn = getTangentFrame( - vViewPosition, normal, + #if defined( USE_NORMALMAP ) + vNormalMapUv + #elif defined( USE_CLEARCOAT_NORMALMAP ) + vClearcoatNormalMapUv + #else + vUv + #endif + ); + #endif + #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) + tbn[0] *= faceDirection; + tbn[1] *= faceDirection; + #endif +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + #ifdef USE_TANGENT + mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); + #else + mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); + #endif + #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) + tbn2[0] *= faceDirection; + tbn2[1] *= faceDirection; + #endif +#endif +vec3 nonPerturbedNormal = normal;`,LT=`#ifdef USE_NORMALMAP_OBJECTSPACE + normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; + #ifdef FLIP_SIDED + normal = - normal; + #endif + #ifdef DOUBLE_SIDED + normal = normal * faceDirection; + #endif + normal = normalize( normalMatrix * normal ); +#elif defined( USE_NORMALMAP_TANGENTSPACE ) + vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; + mapN.xy *= normalScale; + normal = normalize( tbn * mapN ); +#elif defined( USE_BUMPMAP ) + normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); +#endif`,OT=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,DT=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,FT=`#ifndef FLAT_SHADED + vNormal = normalize( transformedNormal ); + #ifdef USE_TANGENT + vTangent = normalize( transformedTangent ); + vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); + #endif +#endif`,UT=`#ifdef USE_NORMALMAP + uniform sampler2D normalMap; + uniform vec2 normalScale; +#endif +#ifdef USE_NORMALMAP_OBJECTSPACE + uniform mat3 normalMatrix; +#endif +#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) + mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { + vec3 q0 = dFdx( eye_pos.xyz ); + vec3 q1 = dFdy( eye_pos.xyz ); + vec2 st0 = dFdx( uv.st ); + vec2 st1 = dFdy( uv.st ); + vec3 N = surf_norm; + vec3 q1perp = cross( q1, N ); + vec3 q0perp = cross( N, q0 ); + vec3 T = q1perp * st0.x + q0perp * st1.x; + vec3 B = q1perp * st0.y + q0perp * st1.y; + float det = max( dot( T, T ), dot( B, B ) ); + float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); + return mat3( T * scale, B * scale, N ); + } +#endif`,kT=`#ifdef USE_CLEARCOAT + vec3 clearcoatNormal = nonPerturbedNormal; +#endif`,BT=`#ifdef USE_CLEARCOAT_NORMALMAP + vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; + clearcoatMapN.xy *= clearcoatNormalScale; + clearcoatNormal = normalize( tbn2 * clearcoatMapN ); +#endif`,GT=`#ifdef USE_CLEARCOATMAP + uniform sampler2D clearcoatMap; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + uniform sampler2D clearcoatNormalMap; + uniform vec2 clearcoatNormalScale; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + uniform sampler2D clearcoatRoughnessMap; +#endif`,VT=`#ifdef USE_IRIDESCENCEMAP + uniform sampler2D iridescenceMap; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + uniform sampler2D iridescenceThicknessMap; +#endif`,zT=`#ifdef OPAQUE +diffuseColor.a = 1.0; +#endif +#ifdef USE_TRANSMISSION +diffuseColor.a *= material.transmissionAlpha; +#endif +gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,HT=`vec3 packNormalToRGB( const in vec3 normal ) { + return normalize( normal ) * 0.5 + 0.5; +} +vec3 unpackRGBToNormal( const in vec3 rgb ) { + return 2.0 * rgb.xyz - 1.0; +} +const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.; +const float Inv255 = 1. / 255.; +const vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 ); +const vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g ); +const vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b ); +const vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a ); +vec4 packDepthToRGBA( const in float v ) { + if( v <= 0.0 ) + return vec4( 0., 0., 0., 0. ); + if( v >= 1.0 ) + return vec4( 1., 1., 1., 1. ); + float vuf; + float af = modf( v * PackFactors.a, vuf ); + float bf = modf( vuf * ShiftRight8, vuf ); + float gf = modf( vuf * ShiftRight8, vuf ); + return vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af ); +} +vec3 packDepthToRGB( const in float v ) { + if( v <= 0.0 ) + return vec3( 0., 0., 0. ); + if( v >= 1.0 ) + return vec3( 1., 1., 1. ); + float vuf; + float bf = modf( v * PackFactors.b, vuf ); + float gf = modf( vuf * ShiftRight8, vuf ); + return vec3( vuf * Inv255, gf * PackUpscale, bf ); +} +vec2 packDepthToRG( const in float v ) { + if( v <= 0.0 ) + return vec2( 0., 0. ); + if( v >= 1.0 ) + return vec2( 1., 1. ); + float vuf; + float gf = modf( v * 256., vuf ); + return vec2( vuf * Inv255, gf ); +} +float unpackRGBAToDepth( const in vec4 v ) { + return dot( v, UnpackFactors4 ); +} +float unpackRGBToDepth( const in vec3 v ) { + return dot( v, UnpackFactors3 ); +} +float unpackRGToDepth( const in vec2 v ) { + return v.r * UnpackFactors2.r + v.g * UnpackFactors2.g; +} +vec4 pack2HalfToRGBA( const in vec2 v ) { + vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); + return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); +} +vec2 unpackRGBATo2Half( const in vec4 v ) { + return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); +} +float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { + return ( viewZ + near ) / ( near - far ); +} +float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { + return depth * ( near - far ) - near; +} +float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { + return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); +} +float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { + return ( near * far ) / ( ( far - near ) * depth - far ); +}`,WT=`#ifdef PREMULTIPLIED_ALPHA + gl_FragColor.rgb *= gl_FragColor.a; +#endif`,XT=`vec4 mvPosition = vec4( transformed, 1.0 ); +#ifdef USE_BATCHING + mvPosition = batchingMatrix * mvPosition; +#endif +#ifdef USE_INSTANCING + mvPosition = instanceMatrix * mvPosition; +#endif +mvPosition = modelViewMatrix * mvPosition; +gl_Position = projectionMatrix * mvPosition;`,qT=`#ifdef DITHERING + gl_FragColor.rgb = dithering( gl_FragColor.rgb ); +#endif`,jT=`#ifdef DITHERING + vec3 dithering( vec3 color ) { + float grid_position = rand( gl_FragCoord.xy ); + vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); + dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); + return color + dither_shift_RGB; + } +#endif`,YT=`float roughnessFactor = roughness; +#ifdef USE_ROUGHNESSMAP + vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); + roughnessFactor *= texelRoughness.g; +#endif`,KT=`#ifdef USE_ROUGHNESSMAP + uniform sampler2D roughnessMap; +#endif`,ZT=`#if NUM_SPOT_LIGHT_COORDS > 0 + varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; +#endif +#if NUM_SPOT_LIGHT_MAPS > 0 + uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; +#endif +#ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; + varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; + struct DirectionalLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; + struct SpotLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; + varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; + struct PointLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + float shadowCameraNear; + float shadowCameraFar; + }; + uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; + #endif + float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { + return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) ); + } + vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { + return unpackRGBATo2Half( texture2D( shadow, uv ) ); + } + float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ + float occlusion = 1.0; + vec2 distribution = texture2DDistribution( shadow, uv ); + float hard_shadow = step( compare , distribution.x ); + if (hard_shadow != 1.0 ) { + float distance = compare - distribution.x ; + float variance = max( 0.00000, distribution.y * distribution.y ); + float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); + } + return occlusion; + } + float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { + float shadow = 1.0; + shadowCoord.xyz /= shadowCoord.w; + shadowCoord.z += shadowBias; + bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; + bool frustumTest = inFrustum && shadowCoord.z <= 1.0; + if ( frustumTest ) { + #if defined( SHADOWMAP_TYPE_PCF ) + vec2 texelSize = vec2( 1.0 ) / shadowMapSize; + float dx0 = - texelSize.x * shadowRadius; + float dy0 = - texelSize.y * shadowRadius; + float dx1 = + texelSize.x * shadowRadius; + float dy1 = + texelSize.y * shadowRadius; + float dx2 = dx0 / 2.0; + float dy2 = dy0 / 2.0; + float dx3 = dx1 / 2.0; + float dy3 = dy1 / 2.0; + shadow = ( + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + + texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) + ) * ( 1.0 / 17.0 ); + #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) + vec2 texelSize = vec2( 1.0 ) / shadowMapSize; + float dx = texelSize.x; + float dy = texelSize.y; + vec2 uv = shadowCoord.xy; + vec2 f = fract( uv * shadowMapSize + 0.5 ); + uv -= f * texelSize; + shadow = ( + texture2DCompare( shadowMap, uv, shadowCoord.z ) + + texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + + texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + + texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), + f.x ) + + mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), + f.x ) + + mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), + f.y ) + + mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), + f.y ) + + mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), + f.x ), + mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), + texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), + f.x ), + f.y ) + ) * ( 1.0 / 9.0 ); + #elif defined( SHADOWMAP_TYPE_VSM ) + shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); + #else + shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); + #endif + } + return mix( 1.0, shadow, shadowIntensity ); + } + vec2 cubeToUV( vec3 v, float texelSizeY ) { + vec3 absV = abs( v ); + float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); + absV *= scaleToCube; + v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); + vec2 planar = v.xy; + float almostATexel = 1.5 * texelSizeY; + float almostOne = 1.0 - almostATexel; + if ( absV.z >= almostOne ) { + if ( v.z > 0.0 ) + planar.x = 4.0 - v.x; + } else if ( absV.x >= almostOne ) { + float signX = sign( v.x ); + planar.x = v.z * signX + 2.0 * signX; + } else if ( absV.y >= almostOne ) { + float signY = sign( v.y ); + planar.x = v.x + 2.0 * signY + 2.0; + planar.y = v.z * signY - 2.0; + } + return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); + } + float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { + float shadow = 1.0; + vec3 lightToPosition = shadowCoord.xyz; + + float lightToPositionLength = length( lightToPosition ); + if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) { + float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; + vec3 bd3D = normalize( lightToPosition ); + vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); + #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) + vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; + shadow = ( + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + + texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) + ) * ( 1.0 / 9.0 ); + #else + shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); + #endif + } + return mix( 1.0, shadow, shadowIntensity ); + } +#endif`,JT=`#if NUM_SPOT_LIGHT_COORDS > 0 + uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; + varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; +#endif +#ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; + varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; + struct DirectionalLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + struct SpotLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; + varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; + struct PointLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + float shadowCameraNear; + float shadowCameraFar; + }; + uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; + #endif +#endif`,$T=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) + vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); + vec4 shadowWorldPosition; +#endif +#if defined( USE_SHADOWMAP ) + #if NUM_DIR_LIGHT_SHADOWS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { + shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); + vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { + shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); + vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end + #endif +#endif +#if NUM_SPOT_LIGHT_COORDS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { + shadowWorldPosition = worldPosition; + #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; + #endif + vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end +#endif`,QT=`float getShadowMask() { + float shadow = 1.0; + #ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + DirectionalLightShadow directionalLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { + directionalLight = directionalLightShadows[ i ]; + shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; + } + #pragma unroll_loop_end + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + SpotLightShadow spotLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { + spotLight = spotLightShadows[ i ]; + shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; + } + #pragma unroll_loop_end + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + PointLightShadow pointLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { + pointLight = pointLightShadows[ i ]; + shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; + } + #pragma unroll_loop_end + #endif + #endif + return shadow; +}`,eM=`#ifdef USE_SKINNING + mat4 boneMatX = getBoneMatrix( skinIndex.x ); + mat4 boneMatY = getBoneMatrix( skinIndex.y ); + mat4 boneMatZ = getBoneMatrix( skinIndex.z ); + mat4 boneMatW = getBoneMatrix( skinIndex.w ); +#endif`,tM=`#ifdef USE_SKINNING + uniform mat4 bindMatrix; + uniform mat4 bindMatrixInverse; + uniform highp sampler2D boneTexture; + mat4 getBoneMatrix( const in float i ) { + int size = textureSize( boneTexture, 0 ).x; + int j = int( i ) * 4; + int x = j % size; + int y = j / size; + vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); + vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); + vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); + vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); + return mat4( v1, v2, v3, v4 ); + } +#endif`,nM=`#ifdef USE_SKINNING + vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); + vec4 skinned = vec4( 0.0 ); + skinned += boneMatX * skinVertex * skinWeight.x; + skinned += boneMatY * skinVertex * skinWeight.y; + skinned += boneMatZ * skinVertex * skinWeight.z; + skinned += boneMatW * skinVertex * skinWeight.w; + transformed = ( bindMatrixInverse * skinned ).xyz; +#endif`,iM=`#ifdef USE_SKINNING + mat4 skinMatrix = mat4( 0.0 ); + skinMatrix += skinWeight.x * boneMatX; + skinMatrix += skinWeight.y * boneMatY; + skinMatrix += skinWeight.z * boneMatZ; + skinMatrix += skinWeight.w * boneMatW; + skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; + objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; + #ifdef USE_TANGENT + objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; + #endif +#endif`,rM=`float specularStrength; +#ifdef USE_SPECULARMAP + vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); + specularStrength = texelSpecular.r; +#else + specularStrength = 1.0; +#endif`,sM=`#ifdef USE_SPECULARMAP + uniform sampler2D specularMap; +#endif`,oM=`#if defined( TONE_MAPPING ) + gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); +#endif`,aM=`#ifndef saturate +#define saturate( a ) clamp( a, 0.0, 1.0 ) +#endif +uniform float toneMappingExposure; +vec3 LinearToneMapping( vec3 color ) { + return saturate( toneMappingExposure * color ); +} +vec3 ReinhardToneMapping( vec3 color ) { + color *= toneMappingExposure; + return saturate( color / ( vec3( 1.0 ) + color ) ); +} +vec3 CineonToneMapping( vec3 color ) { + color *= toneMappingExposure; + color = max( vec3( 0.0 ), color - 0.004 ); + return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); +} +vec3 RRTAndODTFit( vec3 v ) { + vec3 a = v * ( v + 0.0245786 ) - 0.000090537; + vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; + return a / b; +} +vec3 ACESFilmicToneMapping( vec3 color ) { + const mat3 ACESInputMat = mat3( + vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), + vec3( 0.04823, 0.01566, 0.83777 ) + ); + const mat3 ACESOutputMat = mat3( + vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), + vec3( -0.07367, -0.00605, 1.07602 ) + ); + color *= toneMappingExposure / 0.6; + color = ACESInputMat * color; + color = RRTAndODTFit( color ); + color = ACESOutputMat * color; + return saturate( color ); +} +const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( + vec3( 1.6605, - 0.1246, - 0.0182 ), + vec3( - 0.5876, 1.1329, - 0.1006 ), + vec3( - 0.0728, - 0.0083, 1.1187 ) +); +const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( + vec3( 0.6274, 0.0691, 0.0164 ), + vec3( 0.3293, 0.9195, 0.0880 ), + vec3( 0.0433, 0.0113, 0.8956 ) +); +vec3 agxDefaultContrastApprox( vec3 x ) { + vec3 x2 = x * x; + vec3 x4 = x2 * x2; + return + 15.5 * x4 * x2 + - 40.14 * x4 * x + + 31.96 * x4 + - 6.868 * x2 * x + + 0.4298 * x2 + + 0.1191 * x + - 0.00232; +} +vec3 AgXToneMapping( vec3 color ) { + const mat3 AgXInsetMatrix = mat3( + vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), + vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), + vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) + ); + const mat3 AgXOutsetMatrix = mat3( + vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), + vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), + vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) + ); + const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; + color *= toneMappingExposure; + color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; + color = AgXInsetMatrix * color; + color = max( color, 1e-10 ); color = log2( color ); + color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); + color = clamp( color, 0.0, 1.0 ); + color = agxDefaultContrastApprox( color ); + color = AgXOutsetMatrix * color; + color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); + color = LINEAR_REC2020_TO_LINEAR_SRGB * color; + color = clamp( color, 0.0, 1.0 ); + return color; +} +vec3 NeutralToneMapping( vec3 color ) { + const float StartCompression = 0.8 - 0.04; + const float Desaturation = 0.15; + color *= toneMappingExposure; + float x = min( color.r, min( color.g, color.b ) ); + float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; + color -= offset; + float peak = max( color.r, max( color.g, color.b ) ); + if ( peak < StartCompression ) return color; + float d = 1. - StartCompression; + float newPeak = 1. - d * d / ( peak + d - StartCompression ); + color *= newPeak / peak; + float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); + return mix( color, vec3( newPeak ), g ); +} +vec3 CustomToneMapping( vec3 color ) { return color; }`,lM=`#ifdef USE_TRANSMISSION + material.transmission = transmission; + material.transmissionAlpha = 1.0; + material.thickness = thickness; + material.attenuationDistance = attenuationDistance; + material.attenuationColor = attenuationColor; + #ifdef USE_TRANSMISSIONMAP + material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; + #endif + #ifdef USE_THICKNESSMAP + material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; + #endif + vec3 pos = vWorldPosition; + vec3 v = normalize( cameraPosition - pos ); + vec3 n = inverseTransformDirection( normal, viewMatrix ); + vec4 transmitted = getIBLVolumeRefraction( + n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, + pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, + material.attenuationColor, material.attenuationDistance ); + material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); + totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); +#endif`,cM=`#ifdef USE_TRANSMISSION + uniform float transmission; + uniform float thickness; + uniform float attenuationDistance; + uniform vec3 attenuationColor; + #ifdef USE_TRANSMISSIONMAP + uniform sampler2D transmissionMap; + #endif + #ifdef USE_THICKNESSMAP + uniform sampler2D thicknessMap; + #endif + uniform vec2 transmissionSamplerSize; + uniform sampler2D transmissionSamplerMap; + uniform mat4 modelMatrix; + uniform mat4 projectionMatrix; + varying vec3 vWorldPosition; + float w0( float a ) { + return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); + } + float w1( float a ) { + return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); + } + float w2( float a ){ + return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); + } + float w3( float a ) { + return ( 1.0 / 6.0 ) * ( a * a * a ); + } + float g0( float a ) { + return w0( a ) + w1( a ); + } + float g1( float a ) { + return w2( a ) + w3( a ); + } + float h0( float a ) { + return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); + } + float h1( float a ) { + return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); + } + vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { + uv = uv * texelSize.zw + 0.5; + vec2 iuv = floor( uv ); + vec2 fuv = fract( uv ); + float g0x = g0( fuv.x ); + float g1x = g1( fuv.x ); + float h0x = h0( fuv.x ); + float h1x = h1( fuv.x ); + float h0y = h0( fuv.y ); + float h1y = h1( fuv.y ); + vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; + vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; + vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; + vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; + return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); + } + vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { + vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); + vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); + vec2 fLodSizeInv = 1.0 / fLodSize; + vec2 cLodSizeInv = 1.0 / cLodSize; + vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); + vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); + return mix( fSample, cSample, fract( lod ) ); + } + vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { + vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); + vec3 modelScale; + modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); + modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); + modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); + return normalize( refractionVector ) * thickness * modelScale; + } + float applyIorToRoughness( const in float roughness, const in float ior ) { + return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); + } + vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { + float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); + return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); + } + vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { + if ( isinf( attenuationDistance ) ) { + return vec3( 1.0 ); + } else { + vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; + vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; + } + } + vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, + const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, + const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, + const in vec3 attenuationColor, const in float attenuationDistance ) { + vec4 transmittedLight; + vec3 transmittance; + #ifdef USE_DISPERSION + float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; + vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); + for ( int i = 0; i < 3; i ++ ) { + vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); + vec3 refractedRayExit = position + transmissionRay; + vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); + vec2 refractionCoords = ndcPos.xy / ndcPos.w; + refractionCoords += 1.0; + refractionCoords /= 2.0; + vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); + transmittedLight[ i ] = transmissionSample[ i ]; + transmittedLight.a += transmissionSample.a; + transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; + } + transmittedLight.a /= 3.0; + #else + vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); + vec3 refractedRayExit = position + transmissionRay; + vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); + vec2 refractionCoords = ndcPos.xy / ndcPos.w; + refractionCoords += 1.0; + refractionCoords /= 2.0; + transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); + transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); + #endif + vec3 attenuatedColor = transmittance * transmittedLight.rgb; + vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); + float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; + return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); + } +#endif`,uM=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + varying vec2 vUv; +#endif +#ifdef USE_MAP + varying vec2 vMapUv; +#endif +#ifdef USE_ALPHAMAP + varying vec2 vAlphaMapUv; +#endif +#ifdef USE_LIGHTMAP + varying vec2 vLightMapUv; +#endif +#ifdef USE_AOMAP + varying vec2 vAoMapUv; +#endif +#ifdef USE_BUMPMAP + varying vec2 vBumpMapUv; +#endif +#ifdef USE_NORMALMAP + varying vec2 vNormalMapUv; +#endif +#ifdef USE_EMISSIVEMAP + varying vec2 vEmissiveMapUv; +#endif +#ifdef USE_METALNESSMAP + varying vec2 vMetalnessMapUv; +#endif +#ifdef USE_ROUGHNESSMAP + varying vec2 vRoughnessMapUv; +#endif +#ifdef USE_ANISOTROPYMAP + varying vec2 vAnisotropyMapUv; +#endif +#ifdef USE_CLEARCOATMAP + varying vec2 vClearcoatMapUv; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + varying vec2 vClearcoatNormalMapUv; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + varying vec2 vClearcoatRoughnessMapUv; +#endif +#ifdef USE_IRIDESCENCEMAP + varying vec2 vIridescenceMapUv; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + varying vec2 vIridescenceThicknessMapUv; +#endif +#ifdef USE_SHEEN_COLORMAP + varying vec2 vSheenColorMapUv; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + varying vec2 vSheenRoughnessMapUv; +#endif +#ifdef USE_SPECULARMAP + varying vec2 vSpecularMapUv; +#endif +#ifdef USE_SPECULAR_COLORMAP + varying vec2 vSpecularColorMapUv; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + varying vec2 vSpecularIntensityMapUv; +#endif +#ifdef USE_TRANSMISSIONMAP + uniform mat3 transmissionMapTransform; + varying vec2 vTransmissionMapUv; +#endif +#ifdef USE_THICKNESSMAP + uniform mat3 thicknessMapTransform; + varying vec2 vThicknessMapUv; +#endif`,hM=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + varying vec2 vUv; +#endif +#ifdef USE_MAP + uniform mat3 mapTransform; + varying vec2 vMapUv; +#endif +#ifdef USE_ALPHAMAP + uniform mat3 alphaMapTransform; + varying vec2 vAlphaMapUv; +#endif +#ifdef USE_LIGHTMAP + uniform mat3 lightMapTransform; + varying vec2 vLightMapUv; +#endif +#ifdef USE_AOMAP + uniform mat3 aoMapTransform; + varying vec2 vAoMapUv; +#endif +#ifdef USE_BUMPMAP + uniform mat3 bumpMapTransform; + varying vec2 vBumpMapUv; +#endif +#ifdef USE_NORMALMAP + uniform mat3 normalMapTransform; + varying vec2 vNormalMapUv; +#endif +#ifdef USE_DISPLACEMENTMAP + uniform mat3 displacementMapTransform; + varying vec2 vDisplacementMapUv; +#endif +#ifdef USE_EMISSIVEMAP + uniform mat3 emissiveMapTransform; + varying vec2 vEmissiveMapUv; +#endif +#ifdef USE_METALNESSMAP + uniform mat3 metalnessMapTransform; + varying vec2 vMetalnessMapUv; +#endif +#ifdef USE_ROUGHNESSMAP + uniform mat3 roughnessMapTransform; + varying vec2 vRoughnessMapUv; +#endif +#ifdef USE_ANISOTROPYMAP + uniform mat3 anisotropyMapTransform; + varying vec2 vAnisotropyMapUv; +#endif +#ifdef USE_CLEARCOATMAP + uniform mat3 clearcoatMapTransform; + varying vec2 vClearcoatMapUv; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + uniform mat3 clearcoatNormalMapTransform; + varying vec2 vClearcoatNormalMapUv; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + uniform mat3 clearcoatRoughnessMapTransform; + varying vec2 vClearcoatRoughnessMapUv; +#endif +#ifdef USE_SHEEN_COLORMAP + uniform mat3 sheenColorMapTransform; + varying vec2 vSheenColorMapUv; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + uniform mat3 sheenRoughnessMapTransform; + varying vec2 vSheenRoughnessMapUv; +#endif +#ifdef USE_IRIDESCENCEMAP + uniform mat3 iridescenceMapTransform; + varying vec2 vIridescenceMapUv; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + uniform mat3 iridescenceThicknessMapTransform; + varying vec2 vIridescenceThicknessMapUv; +#endif +#ifdef USE_SPECULARMAP + uniform mat3 specularMapTransform; + varying vec2 vSpecularMapUv; +#endif +#ifdef USE_SPECULAR_COLORMAP + uniform mat3 specularColorMapTransform; + varying vec2 vSpecularColorMapUv; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + uniform mat3 specularIntensityMapTransform; + varying vec2 vSpecularIntensityMapUv; +#endif +#ifdef USE_TRANSMISSIONMAP + uniform mat3 transmissionMapTransform; + varying vec2 vTransmissionMapUv; +#endif +#ifdef USE_THICKNESSMAP + uniform mat3 thicknessMapTransform; + varying vec2 vThicknessMapUv; +#endif`,fM=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + vUv = vec3( uv, 1 ).xy; +#endif +#ifdef USE_MAP + vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ALPHAMAP + vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_LIGHTMAP + vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_AOMAP + vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_BUMPMAP + vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_NORMALMAP + vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_DISPLACEMENTMAP + vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_EMISSIVEMAP + vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_METALNESSMAP + vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ROUGHNESSMAP + vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ANISOTROPYMAP + vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOATMAP + vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_IRIDESCENCEMAP + vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SHEEN_COLORMAP + vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULARMAP + vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULAR_COLORMAP + vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_TRANSMISSIONMAP + vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_THICKNESSMAP + vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; +#endif`,dM=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 + vec4 worldPosition = vec4( transformed, 1.0 ); + #ifdef USE_BATCHING + worldPosition = batchingMatrix * worldPosition; + #endif + #ifdef USE_INSTANCING + worldPosition = instanceMatrix * worldPosition; + #endif + worldPosition = modelMatrix * worldPosition; +#endif`,pM=`varying vec2 vUv; +uniform mat3 uvTransform; +void main() { + vUv = ( uvTransform * vec3( uv, 1 ) ).xy; + gl_Position = vec4( position.xy, 1.0, 1.0 ); +}`,mM=`uniform sampler2D t2D; +uniform float backgroundIntensity; +varying vec2 vUv; +void main() { + vec4 texColor = texture2D( t2D, vUv ); + #ifdef DECODE_VIDEO_TEXTURE + texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); + #endif + texColor.rgb *= backgroundIntensity; + gl_FragColor = texColor; + #include + #include +}`,gM=`varying vec3 vWorldDirection; +#include +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include + #include + gl_Position.z = gl_Position.w; +}`,yM=`#ifdef ENVMAP_TYPE_CUBE + uniform samplerCube envMap; +#elif defined( ENVMAP_TYPE_CUBE_UV ) + uniform sampler2D envMap; +#endif +uniform float flipEnvMap; +uniform float backgroundBlurriness; +uniform float backgroundIntensity; +uniform mat3 backgroundRotation; +varying vec3 vWorldDirection; +#include +void main() { + #ifdef ENVMAP_TYPE_CUBE + vec4 texColor = textureCube( envMap, backgroundRotation * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); + #elif defined( ENVMAP_TYPE_CUBE_UV ) + vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); + #else + vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + #endif + texColor.rgb *= backgroundIntensity; + gl_FragColor = texColor; + #include + #include +}`,xM=`varying vec3 vWorldDirection; +#include +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include + #include + gl_Position.z = gl_Position.w; +}`,vM=`uniform samplerCube tCube; +uniform float tFlip; +uniform float opacity; +varying vec3 vWorldDirection; +void main() { + vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); + gl_FragColor = texColor; + gl_FragColor.a *= opacity; + #include + #include +}`,_M=`#include +#include +#include +#include +#include +#include +#include +#include +varying vec2 vHighPrecisionZW; +void main() { + #include + #include + #include + #include + #ifdef USE_DISPLACEMENTMAP + #include + #include + #include + #endif + #include + #include + #include + #include + #include + #include + #include + vHighPrecisionZW = gl_Position.zw; +}`,TM=`#if DEPTH_PACKING == 3200 + uniform float opacity; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +varying vec2 vHighPrecisionZW; +void main() { + vec4 diffuseColor = vec4( 1.0 ); + #include + #if DEPTH_PACKING == 3200 + diffuseColor.a = opacity; + #endif + #include + #include + #include + #include + #include + float fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5; + #if DEPTH_PACKING == 3200 + gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); + #elif DEPTH_PACKING == 3201 + gl_FragColor = packDepthToRGBA( fragCoordZ ); + #elif DEPTH_PACKING == 3202 + gl_FragColor = vec4( packDepthToRGB( fragCoordZ ), 1.0 ); + #elif DEPTH_PACKING == 3203 + gl_FragColor = vec4( packDepthToRG( fragCoordZ ), 0.0, 1.0 ); + #endif +}`,MM=`#define DISTANCE +varying vec3 vWorldPosition; +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #ifdef USE_DISPLACEMENTMAP + #include + #include + #include + #endif + #include + #include + #include + #include + #include + #include + #include + vWorldPosition = worldPosition.xyz; +}`,EM=`#define DISTANCE +uniform vec3 referencePosition; +uniform float nearDistance; +uniform float farDistance; +varying vec3 vWorldPosition; +#include +#include +#include +#include +#include +#include +#include +#include +void main () { + vec4 diffuseColor = vec4( 1.0 ); + #include + #include + #include + #include + #include + float dist = length( vWorldPosition - referencePosition ); + dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); + dist = saturate( dist ); + gl_FragColor = packDepthToRGBA( dist ); +}`,bM=`varying vec3 vWorldDirection; +#include +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include + #include +}`,SM=`uniform sampler2D tEquirect; +varying vec3 vWorldDirection; +#include +void main() { + vec3 direction = normalize( vWorldDirection ); + vec2 sampleUV = equirectUv( direction ); + gl_FragColor = texture2D( tEquirect, sampleUV ); + #include + #include +}`,AM=`uniform float scale; +attribute float lineDistance; +varying float vLineDistance; +#include +#include +#include +#include +#include +#include +#include +void main() { + vLineDistance = scale * lineDistance; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +}`,wM=`uniform vec3 diffuse; +uniform float opacity; +uniform float dashSize; +uniform float totalSize; +varying float vLineDistance; +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + if ( mod( vLineDistance, totalSize ) > dashSize ) { + discard; + } + vec3 outgoingLight = vec3( 0.0 ); + #include + #include + #include + outgoingLight = diffuseColor.rgb; + #include + #include + #include + #include + #include +}`,CM=`#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) + #include + #include + #include + #include + #include + #endif + #include + #include + #include + #include + #include + #include + #include + #include + #include +}`,IM=`uniform vec3 diffuse; +uniform float opacity; +#ifndef FLAT_SHADED + varying vec3 vNormal; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + #include + #include + #include + #include + #include + #include + #include + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + #ifdef USE_LIGHTMAP + vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); + reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; + #else + reflectedLight.indirectDiffuse += vec3( 1.0 ); + #endif + #include + reflectedLight.indirectDiffuse *= diffuseColor.rgb; + vec3 outgoingLight = reflectedLight.indirectDiffuse; + #include + #include + #include + #include + #include + #include + #include +}`,RM=`#define LAMBERT +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; + #include + #include + #include + #include +}`,PM=`#define LAMBERT +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; + #include + #include + #include + #include + #include + #include + #include +}`,NM=`#define MATCAP +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; +}`,LM=`#define MATCAP +uniform vec3 diffuse; +uniform float opacity; +uniform sampler2D matcap; +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + #include + #include + #include + #include + #include + #include + #include + #include + vec3 viewDir = normalize( vViewPosition ); + vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); + vec3 y = cross( viewDir, x ); + vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; + #ifdef USE_MATCAP + vec4 matcapColor = texture2D( matcap, uv ); + #else + vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); + #endif + vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; + #include + #include + #include + #include + #include + #include +}`,OM=`#define NORMAL +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + varying vec3 vViewPosition; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + vViewPosition = - mvPosition.xyz; +#endif +}`,DM=`#define NORMAL +uniform float opacity; +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + varying vec3 vViewPosition; +#endif +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); + #include + #include + #include + #include + gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a ); + #ifdef OPAQUE + gl_FragColor.a = 1.0; + #endif +}`,FM=`#define PHONG +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; + #include + #include + #include + #include +}`,UM=`#define PHONG +uniform vec3 diffuse; +uniform vec3 emissive; +uniform vec3 specular; +uniform float shininess; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + #include + #include + #include + #include + #include + #include + #include +}`,kM=`#define STANDARD +varying vec3 vViewPosition; +#ifdef USE_TRANSMISSION + varying vec3 vWorldPosition; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; + #include + #include + #include +#ifdef USE_TRANSMISSION + vWorldPosition = worldPosition.xyz; +#endif +}`,BM=`#define STANDARD +#ifdef PHYSICAL + #define IOR + #define USE_SPECULAR +#endif +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float roughness; +uniform float metalness; +uniform float opacity; +#ifdef IOR + uniform float ior; +#endif +#ifdef USE_SPECULAR + uniform float specularIntensity; + uniform vec3 specularColor; + #ifdef USE_SPECULAR_COLORMAP + uniform sampler2D specularColorMap; + #endif + #ifdef USE_SPECULAR_INTENSITYMAP + uniform sampler2D specularIntensityMap; + #endif +#endif +#ifdef USE_CLEARCOAT + uniform float clearcoat; + uniform float clearcoatRoughness; +#endif +#ifdef USE_DISPERSION + uniform float dispersion; +#endif +#ifdef USE_IRIDESCENCE + uniform float iridescence; + uniform float iridescenceIOR; + uniform float iridescenceThicknessMinimum; + uniform float iridescenceThicknessMaximum; +#endif +#ifdef USE_SHEEN + uniform vec3 sheenColor; + uniform float sheenRoughness; + #ifdef USE_SHEEN_COLORMAP + uniform sampler2D sheenColorMap; + #endif + #ifdef USE_SHEEN_ROUGHNESSMAP + uniform sampler2D sheenRoughnessMap; + #endif +#endif +#ifdef USE_ANISOTROPY + uniform vec2 anisotropyVector; + #ifdef USE_ANISOTROPYMAP + uniform sampler2D anisotropyMap; + #endif +#endif +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; + vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; + #include + vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; + #ifdef USE_SHEEN + float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); + outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect; + #endif + #ifdef USE_CLEARCOAT + float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); + vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); + outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; + #endif + #include + #include + #include + #include + #include + #include +}`,GM=`#define TOON +varying vec3 vViewPosition; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; + #include + #include + #include +}`,VM=`#define TOON +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; + #include + #include + #include + #include + #include + #include +}`,zM=`uniform float size; +uniform float scale; +#include +#include +#include +#include +#include +#include +#ifdef USE_POINTS_UV + varying vec2 vUv; + uniform mat3 uvTransform; +#endif +void main() { + #ifdef USE_POINTS_UV + vUv = ( uvTransform * vec3( uv, 1 ) ).xy; + #endif + #include + #include + #include + #include + #include + #include + gl_PointSize = size; + #ifdef USE_SIZEATTENUATION + bool isPerspective = isPerspectiveMatrix( projectionMatrix ); + if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); + #endif + #include + #include + #include + #include +}`,HM=`uniform vec3 diffuse; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + vec3 outgoingLight = vec3( 0.0 ); + #include + #include + #include + #include + #include + outgoingLight = diffuseColor.rgb; + #include + #include + #include + #include + #include +}`,WM=`#include +#include +#include +#include +#include +#include +#include +void main() { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +}`,XM=`uniform vec3 color; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); + #include + #include + #include +}`,qM=`uniform float rotation; +uniform vec2 center; +#include +#include +#include +#include +#include +void main() { + #include + vec4 mvPosition = modelViewMatrix[ 3 ]; + vec2 scale = vec2( length( modelMatrix[ 0 ].xyz ), length( modelMatrix[ 1 ].xyz ) ); + #ifndef USE_SIZEATTENUATION + bool isPerspective = isPerspectiveMatrix( projectionMatrix ); + if ( isPerspective ) scale *= - mvPosition.z; + #endif + vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; + vec2 rotatedPosition; + rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; + rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; + mvPosition.xy += rotatedPosition; + gl_Position = projectionMatrix * mvPosition; + #include + #include + #include +}`,jM=`uniform vec3 diffuse; +uniform float opacity; +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + vec3 outgoingLight = vec3( 0.0 ); + #include + #include + #include + #include + #include + outgoingLight = diffuseColor.rgb; + #include + #include + #include + #include +}`,Ct={alphahash_fragment:m_,alphahash_pars_fragment:g_,alphamap_fragment:y_,alphamap_pars_fragment:x_,alphatest_fragment:v_,alphatest_pars_fragment:__,aomap_fragment:T_,aomap_pars_fragment:M_,batching_pars_vertex:E_,batching_vertex:b_,begin_vertex:S_,beginnormal_vertex:A_,bsdfs:w_,iridescence_fragment:C_,bumpmap_pars_fragment:I_,clipping_planes_fragment:R_,clipping_planes_pars_fragment:P_,clipping_planes_pars_vertex:N_,clipping_planes_vertex:L_,color_fragment:O_,color_pars_fragment:D_,color_pars_vertex:F_,color_vertex:U_,common:k_,cube_uv_reflection_fragment:B_,defaultnormal_vertex:G_,displacementmap_pars_vertex:V_,displacementmap_vertex:z_,emissivemap_fragment:H_,emissivemap_pars_fragment:W_,colorspace_fragment:X_,colorspace_pars_fragment:q_,envmap_fragment:j_,envmap_common_pars_fragment:Y_,envmap_pars_fragment:K_,envmap_pars_vertex:Z_,envmap_physical_pars_fragment:aT,envmap_vertex:J_,fog_vertex:$_,fog_pars_vertex:Q_,fog_fragment:eT,fog_pars_fragment:tT,gradientmap_pars_fragment:nT,lightmap_pars_fragment:iT,lights_lambert_fragment:rT,lights_lambert_pars_fragment:sT,lights_pars_begin:oT,lights_toon_fragment:lT,lights_toon_pars_fragment:cT,lights_phong_fragment:uT,lights_phong_pars_fragment:hT,lights_physical_fragment:fT,lights_physical_pars_fragment:dT,lights_fragment_begin:pT,lights_fragment_maps:mT,lights_fragment_end:gT,logdepthbuf_fragment:yT,logdepthbuf_pars_fragment:xT,logdepthbuf_pars_vertex:vT,logdepthbuf_vertex:_T,map_fragment:TT,map_pars_fragment:MT,map_particle_fragment:ET,map_particle_pars_fragment:bT,metalnessmap_fragment:ST,metalnessmap_pars_fragment:AT,morphinstance_vertex:wT,morphcolor_vertex:CT,morphnormal_vertex:IT,morphtarget_pars_vertex:RT,morphtarget_vertex:PT,normal_fragment_begin:NT,normal_fragment_maps:LT,normal_pars_fragment:OT,normal_pars_vertex:DT,normal_vertex:FT,normalmap_pars_fragment:UT,clearcoat_normal_fragment_begin:kT,clearcoat_normal_fragment_maps:BT,clearcoat_pars_fragment:GT,iridescence_pars_fragment:VT,opaque_fragment:zT,packing:HT,premultiplied_alpha_fragment:WT,project_vertex:XT,dithering_fragment:qT,dithering_pars_fragment:jT,roughnessmap_fragment:YT,roughnessmap_pars_fragment:KT,shadowmap_pars_fragment:ZT,shadowmap_pars_vertex:JT,shadowmap_vertex:$T,shadowmask_pars_fragment:QT,skinbase_vertex:eM,skinning_pars_vertex:tM,skinning_vertex:nM,skinnormal_vertex:iM,specularmap_fragment:rM,specularmap_pars_fragment:sM,tonemapping_fragment:oM,tonemapping_pars_fragment:aM,transmission_fragment:lM,transmission_pars_fragment:cM,uv_pars_fragment:uM,uv_pars_vertex:hM,uv_vertex:fM,worldpos_vertex:dM,background_vert:pM,background_frag:mM,backgroundCube_vert:gM,backgroundCube_frag:yM,cube_vert:xM,cube_frag:vM,depth_vert:_M,depth_frag:TM,distanceRGBA_vert:MM,distanceRGBA_frag:EM,equirect_vert:bM,equirect_frag:SM,linedashed_vert:AM,linedashed_frag:wM,meshbasic_vert:CM,meshbasic_frag:IM,meshlambert_vert:RM,meshlambert_frag:PM,meshmatcap_vert:NM,meshmatcap_frag:LM,meshnormal_vert:OM,meshnormal_frag:DM,meshphong_vert:FM,meshphong_frag:UM,meshphysical_vert:kM,meshphysical_frag:BM,meshtoon_vert:GM,meshtoon_frag:VM,points_vert:zM,points_frag:HM,shadow_vert:WM,shadow_frag:XM,sprite_vert:qM,sprite_frag:jM},qe={common:{diffuse:{value:new He(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new gt},alphaMap:{value:null},alphaMapTransform:{value:new gt},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new gt}},envmap:{envMap:{value:null},envMapRotation:{value:new gt},flipEnvMap:{value:-1},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new gt}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new gt}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new gt},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new gt},normalScale:{value:new Pe(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new gt},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new gt}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new gt}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new gt}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new He(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotShadowMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null}},points:{diffuse:{value:new He(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new gt},alphaTest:{value:0},uvTransform:{value:new gt}},sprite:{diffuse:{value:new He(16777215)},opacity:{value:1},center:{value:new Pe(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new gt},alphaMap:{value:null},alphaMapTransform:{value:new gt},alphaTest:{value:0}}},Rr={basic:{uniforms:Qn([qe.common,qe.specularmap,qe.envmap,qe.aomap,qe.lightmap,qe.fog]),vertexShader:Ct.meshbasic_vert,fragmentShader:Ct.meshbasic_frag},lambert:{uniforms:Qn([qe.common,qe.specularmap,qe.envmap,qe.aomap,qe.lightmap,qe.emissivemap,qe.bumpmap,qe.normalmap,qe.displacementmap,qe.fog,qe.lights,{emissive:{value:new He(0)}}]),vertexShader:Ct.meshlambert_vert,fragmentShader:Ct.meshlambert_frag},phong:{uniforms:Qn([qe.common,qe.specularmap,qe.envmap,qe.aomap,qe.lightmap,qe.emissivemap,qe.bumpmap,qe.normalmap,qe.displacementmap,qe.fog,qe.lights,{emissive:{value:new He(0)},specular:{value:new He(1118481)},shininess:{value:30}}]),vertexShader:Ct.meshphong_vert,fragmentShader:Ct.meshphong_frag},standard:{uniforms:Qn([qe.common,qe.envmap,qe.aomap,qe.lightmap,qe.emissivemap,qe.bumpmap,qe.normalmap,qe.displacementmap,qe.roughnessmap,qe.metalnessmap,qe.fog,qe.lights,{emissive:{value:new He(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Ct.meshphysical_vert,fragmentShader:Ct.meshphysical_frag},toon:{uniforms:Qn([qe.common,qe.aomap,qe.lightmap,qe.emissivemap,qe.bumpmap,qe.normalmap,qe.displacementmap,qe.gradientmap,qe.fog,qe.lights,{emissive:{value:new He(0)}}]),vertexShader:Ct.meshtoon_vert,fragmentShader:Ct.meshtoon_frag},matcap:{uniforms:Qn([qe.common,qe.bumpmap,qe.normalmap,qe.displacementmap,qe.fog,{matcap:{value:null}}]),vertexShader:Ct.meshmatcap_vert,fragmentShader:Ct.meshmatcap_frag},points:{uniforms:Qn([qe.points,qe.fog]),vertexShader:Ct.points_vert,fragmentShader:Ct.points_frag},dashed:{uniforms:Qn([qe.common,qe.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Ct.linedashed_vert,fragmentShader:Ct.linedashed_frag},depth:{uniforms:Qn([qe.common,qe.displacementmap]),vertexShader:Ct.depth_vert,fragmentShader:Ct.depth_frag},normal:{uniforms:Qn([qe.common,qe.bumpmap,qe.normalmap,qe.displacementmap,{opacity:{value:1}}]),vertexShader:Ct.meshnormal_vert,fragmentShader:Ct.meshnormal_frag},sprite:{uniforms:Qn([qe.sprite,qe.fog]),vertexShader:Ct.sprite_vert,fragmentShader:Ct.sprite_frag},background:{uniforms:{uvTransform:{value:new gt},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:Ct.background_vert,fragmentShader:Ct.background_frag},backgroundCube:{uniforms:{envMap:{value:null},flipEnvMap:{value:-1},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new gt}},vertexShader:Ct.backgroundCube_vert,fragmentShader:Ct.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:Ct.cube_vert,fragmentShader:Ct.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:Ct.equirect_vert,fragmentShader:Ct.equirect_frag},distanceRGBA:{uniforms:Qn([qe.common,qe.displacementmap,{referencePosition:{value:new se},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:Ct.distanceRGBA_vert,fragmentShader:Ct.distanceRGBA_frag},shadow:{uniforms:Qn([qe.lights,qe.fog,{color:{value:new He(0)},opacity:{value:1}}]),vertexShader:Ct.shadow_vert,fragmentShader:Ct.shadow_frag}};Rr.physical={uniforms:Qn([Rr.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new gt},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new gt},clearcoatNormalScale:{value:new Pe(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new gt},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new gt},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new gt},sheen:{value:0},sheenColor:{value:new He(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new gt},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new gt},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new gt},transmissionSamplerSize:{value:new Pe},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new gt},attenuationDistance:{value:0},attenuationColor:{value:new He(0)},specularColor:{value:new He(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new gt},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new gt},anisotropyVector:{value:new Pe},anisotropyMap:{value:null},anisotropyMapTransform:{value:new gt}}]),vertexShader:Ct.meshphysical_vert,fragmentShader:Ct.meshphysical_frag};var Ef={r:0,b:0,g:0},Eo=new vn,YM=new Je;function KM(r,e,t,n,i,s,o){let a=new He(0),l=s===!0?0:1,c,u,h=null,f=0,p=null;function g(v){let x=v.isScene===!0?v.background:null;return x&&x.isTexture&&(x=(v.backgroundBlurriness>0?t:e).get(x)),x}function y(v){let x=!1,S=g(v);S===null?m(a,l):S&&S.isColor&&(m(S,1),x=!0);let I=r.xr.getEnvironmentBlendMode();I==="additive"?n.buffers.color.setClear(0,0,0,1,o):I==="alpha-blend"&&n.buffers.color.setClear(0,0,0,0,o),(r.autoClear||x)&&(n.buffers.depth.setTest(!0),n.buffers.depth.setMask(!0),n.buffers.color.setMask(!0),r.clear(r.autoClearColor,r.autoClearDepth,r.autoClearStencil))}function d(v,x){let S=g(x);S&&(S.isCubeTexture||S.mapping===Ac)?(u===void 0&&(u=new Dt(new Is(1,1,1),new nr({name:"BackgroundCubeMaterial",uniforms:Mo(Rr.backgroundCube.uniforms),vertexShader:Rr.backgroundCube.vertexShader,fragmentShader:Rr.backgroundCube.fragmentShader,side:_n,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),u.geometry.deleteAttribute("normal"),u.geometry.deleteAttribute("uv"),u.onBeforeRender=function(I,A,C){this.matrixWorld.copyPosition(C.matrixWorld)},Object.defineProperty(u.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),i.update(u)),Eo.copy(x.backgroundRotation),Eo.x*=-1,Eo.y*=-1,Eo.z*=-1,S.isCubeTexture&&S.isRenderTargetTexture===!1&&(Eo.y*=-1,Eo.z*=-1),u.material.uniforms.envMap.value=S,u.material.uniforms.flipEnvMap.value=S.isCubeTexture&&S.isRenderTargetTexture===!1?-1:1,u.material.uniforms.backgroundBlurriness.value=x.backgroundBlurriness,u.material.uniforms.backgroundIntensity.value=x.backgroundIntensity,u.material.uniforms.backgroundRotation.value.setFromMatrix4(YM.makeRotationFromEuler(Eo)),u.material.toneMapped=mt.getTransfer(S.colorSpace)!==Kt,(h!==S||f!==S.version||p!==r.toneMapping)&&(u.material.needsUpdate=!0,h=S,f=S.version,p=r.toneMapping),u.layers.enableAll(),v.unshift(u,u.geometry,u.material,0,0,null)):S&&S.isTexture&&(c===void 0&&(c=new Dt(new po(2,2),new nr({name:"BackgroundMaterial",uniforms:Mo(Rr.background.uniforms),vertexShader:Rr.background.vertexShader,fragmentShader:Rr.background.fragmentShader,side:wi,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),Object.defineProperty(c.material,"map",{get:function(){return this.uniforms.t2D.value}}),i.update(c)),c.material.uniforms.t2D.value=S,c.material.uniforms.backgroundIntensity.value=x.backgroundIntensity,c.material.toneMapped=mt.getTransfer(S.colorSpace)!==Kt,S.matrixAutoUpdate===!0&&S.updateMatrix(),c.material.uniforms.uvTransform.value.copy(S.matrix),(h!==S||f!==S.version||p!==r.toneMapping)&&(c.material.needsUpdate=!0,h=S,f=S.version,p=r.toneMapping),c.layers.enableAll(),v.unshift(c,c.geometry,c.material,0,0,null))}function m(v,x){v.getRGB(Ef,Rp(r)),n.buffers.color.setClear(Ef.r,Ef.g,Ef.b,x,o)}function _(){u!==void 0&&(u.geometry.dispose(),u.material.dispose(),u=void 0),c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0)}return{getClearColor:function(){return a},setClearColor:function(v,x=1){a.set(v),l=x,m(a,l)},getClearAlpha:function(){return l},setClearAlpha:function(v){l=v,m(a,l)},render:y,addToRenderList:d,dispose:_}}function ZM(r,e){let t=r.getParameter(r.MAX_VERTEX_ATTRIBS),n={},i=f(null),s=i,o=!1;function a(b,R,N,B,V){let ie=!1,Y=h(B,N,R);s!==Y&&(s=Y,c(s.object)),ie=p(b,B,N,V),ie&&g(b,B,N,V),V!==null&&e.update(V,r.ELEMENT_ARRAY_BUFFER),(ie||o)&&(o=!1,x(b,R,N,B),V!==null&&r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,e.get(V).buffer))}function l(){return r.createVertexArray()}function c(b){return r.bindVertexArray(b)}function u(b){return r.deleteVertexArray(b)}function h(b,R,N){let B=N.wireframe===!0,V=n[b.id];V===void 0&&(V={},n[b.id]=V);let ie=V[R.id];ie===void 0&&(ie={},V[R.id]=ie);let Y=ie[B];return Y===void 0&&(Y=f(l()),ie[B]=Y),Y}function f(b){let R=[],N=[],B=[];for(let V=0;V=0){let ye=V[q],Te=ie[q];if(Te===void 0&&(q==="instanceMatrix"&&b.instanceMatrix&&(Te=b.instanceMatrix),q==="instanceColor"&&b.instanceColor&&(Te=b.instanceColor)),ye===void 0||ye.attribute!==Te||Te&&ye.data!==Te.data)return!0;Y++}return s.attributesNum!==Y||s.index!==B}function g(b,R,N,B){let V={},ie=R.attributes,Y=0,te=N.getAttributes();for(let q in te)if(te[q].location>=0){let ye=ie[q];ye===void 0&&(q==="instanceMatrix"&&b.instanceMatrix&&(ye=b.instanceMatrix),q==="instanceColor"&&b.instanceColor&&(ye=b.instanceColor));let Te={};Te.attribute=ye,ye&&ye.data&&(Te.data=ye.data),V[q]=Te,Y++}s.attributes=V,s.attributesNum=Y,s.index=B}function y(){let b=s.newAttributes;for(let R=0,N=b.length;R=0){let le=V[te];if(le===void 0&&(te==="instanceMatrix"&&b.instanceMatrix&&(le=b.instanceMatrix),te==="instanceColor"&&b.instanceColor&&(le=b.instanceColor)),le!==void 0){let ye=le.normalized,Te=le.itemSize,Ae=e.get(le);if(Ae===void 0)continue;let _e=Ae.buffer,G=Ae.type,P=Ae.bytesPerElement,M=G===r.INT||G===r.UNSIGNED_INT||le.gpuType===Wh;if(le.isInterleavedBufferAttribute){let T=le.data,k=T.stride,j=le.offset;if(T.isInstancedInterleavedBuffer){for(let W=0;W0&&r.getShaderPrecisionFormat(r.FRAGMENT_SHADER,r.HIGH_FLOAT).precision>0)return"highp";A="mediump"}return A==="mediump"&&r.getShaderPrecisionFormat(r.VERTEX_SHADER,r.MEDIUM_FLOAT).precision>0&&r.getShaderPrecisionFormat(r.FRAGMENT_SHADER,r.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let c=t.precision!==void 0?t.precision:"highp",u=l(c);u!==c&&(console.warn("THREE.WebGLRenderer:",c,"not supported, using",u,"instead."),c=u);let h=t.logarithmicDepthBuffer===!0,f=t.reverseDepthBuffer===!0&&e.has("EXT_clip_control"),p=r.getParameter(r.MAX_TEXTURE_IMAGE_UNITS),g=r.getParameter(r.MAX_VERTEX_TEXTURE_IMAGE_UNITS),y=r.getParameter(r.MAX_TEXTURE_SIZE),d=r.getParameter(r.MAX_CUBE_MAP_TEXTURE_SIZE),m=r.getParameter(r.MAX_VERTEX_ATTRIBS),_=r.getParameter(r.MAX_VERTEX_UNIFORM_VECTORS),v=r.getParameter(r.MAX_VARYING_VECTORS),x=r.getParameter(r.MAX_FRAGMENT_UNIFORM_VECTORS),S=g>0,I=r.getParameter(r.MAX_SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:s,getMaxPrecision:l,textureFormatReadable:o,textureTypeReadable:a,precision:c,logarithmicDepthBuffer:h,reverseDepthBuffer:f,maxTextures:p,maxVertexTextures:g,maxTextureSize:y,maxCubemapSize:d,maxAttributes:m,maxVertexUniforms:_,maxVaryings:v,maxFragmentUniforms:x,vertexTextures:S,maxSamples:I}}function QM(r){let e=this,t=null,n=0,i=!1,s=!1,o=new Sr,a=new gt,l={value:null,needsUpdate:!1};this.uniform=l,this.numPlanes=0,this.numIntersection=0,this.init=function(h,f){let p=h.length!==0||f||n!==0||i;return i=f,n=h.length,p},this.beginShadows=function(){s=!0,u(null)},this.endShadows=function(){s=!1},this.setGlobalState=function(h,f){t=u(h,f,0)},this.setState=function(h,f,p){let g=h.clippingPlanes,y=h.clipIntersection,d=h.clipShadows,m=r.get(h);if(!i||g===null||g.length===0||s&&!d)s?u(null):c();else{let _=s?0:n,v=_*4,x=m.clippingState||null;l.value=x,x=u(g,f,v,p);for(let S=0;S!==v;++S)x[S]=t[S];m.clippingState=x,this.numIntersection=y?this.numPlanes:0,this.numPlanes+=_}};function c(){l.value!==t&&(l.value=t,l.needsUpdate=n>0),e.numPlanes=n,e.numIntersection=0}function u(h,f,p,g){let y=h!==null?h.length:0,d=null;if(y!==0){if(d=l.value,g!==!0||d===null){let m=p+y*4,_=f.matrixWorldInverse;a.getNormalMatrix(_),(d===null||d.length0){let c=new xh(l.height);return c.fromEquirectangularTexture(r,o),e.set(o,c),o.addEventListener("dispose",i),t(c.texture,o.mapping)}else return null}}return o}function i(o){let a=o.target;a.removeEventListener("dispose",i);let l=e.get(a);l!==void 0&&(e.delete(a),l.dispose())}function s(){e=new WeakMap}return{get:n,dispose:s}}var Ha=4,uy=[.125,.215,.35,.446,.526,.582],Ao=20,Dp=new ss,hy=new He,Fp=null,Up=0,kp=0,Bp=!1,So=(1+Math.sqrt(5))/2,za=1/So,fy=[new se(-So,za,0),new se(So,za,0),new se(-za,0,So),new se(za,0,So),new se(0,So,-za),new se(0,So,za),new se(-1,1,-1),new se(1,1,-1),new se(-1,1,1),new se(1,1,1)],tE=new se,Af=class{constructor(e){this._renderer=e,this._pingPongRenderTarget=null,this._lodMax=0,this._cubeSize=0,this._lodPlanes=[],this._sizeLods=[],this._sigmas=[],this._blurMaterial=null,this._cubemapMaterial=null,this._equirectMaterial=null,this._compileMaterial(this._blurMaterial)}fromScene(e,t=0,n=.1,i=100,s={}){let{size:o=256,position:a=tE}=s;Fp=this._renderer.getRenderTarget(),Up=this._renderer.getActiveCubeFace(),kp=this._renderer.getActiveMipmapLevel(),Bp=this._renderer.xr.enabled,this._renderer.xr.enabled=!1,this._setSize(o);let l=this._allocateTargets();return l.depthBuffer=!0,this._sceneToCubeUV(e,n,i,l,a),t>0&&this._blur(l,0,0,t),this._applyPMREM(l),this._cleanup(l),l}fromEquirectangular(e,t=null){return this._fromTexture(e,t)}fromCubemap(e,t=null){return this._fromTexture(e,t)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=my(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=py(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose()}_setSize(e){this._lodMax=Math.floor(Math.log2(e)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let e=0;e2?x:0,x,x),h.setRenderTarget(i),d&&h.render(y,l),h.render(e,l)}y.geometry.dispose(),y.material.dispose(),h.toneMapping=p,h.autoClear=f,e.background=m}_textureToCubeUV(e,t){let n=this._renderer,i=e.mapping===_o||e.mapping===To;i?(this._cubemapMaterial===null&&(this._cubemapMaterial=my()),this._cubemapMaterial.uniforms.flipEnvMap.value=e.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=py());let s=i?this._cubemapMaterial:this._equirectMaterial,o=new Dt(this._lodPlanes[0],s),a=s.uniforms;a.envMap.value=e;let l=this._cubeSize;bf(t,0,0,3*l,2*l),n.setRenderTarget(t),n.render(o,Dp)}_applyPMREM(e){let t=this._renderer,n=t.autoClear;t.autoClear=!1;let i=this._lodPlanes.length;for(let s=1;sAo&&console.warn(`sigmaRadians, ${s}, is too large and will clip, as it requested ${d} samples when the maximum is set to ${Ao}`);let m=[],_=0;for(let A=0;Av-Ha?i-v+Ha:0),I=4*(this._cubeSize-x);bf(t,S,I,3*x,2*x),l.setRenderTarget(t),l.render(h,Dp)}};function nE(r){let e=[],t=[],n=[],i=r,s=r-Ha+1+uy.length;for(let o=0;or-Ha?l=uy[o-r+Ha-1]:o===0&&(l=0),n.push(l);let c=1/(a-2),u=-c,h=1+c,f=[u,u,h,u,h,h,u,u,h,h,u,h],p=6,g=6,y=3,d=2,m=1,_=new Float32Array(y*g*p),v=new Float32Array(d*g*p),x=new Float32Array(m*g*p);for(let I=0;I2?0:-1,E=[A,C,0,A+2/3,C,0,A+2/3,C+1,0,A,C,0,A+2/3,C+1,0,A,C+1,0];_.set(E,y*g*I),v.set(f,d*g*I);let b=[I,I,I,I,I,I];x.set(b,m*g*I)}let S=new It;S.setAttribute("position",new Vn(_,y)),S.setAttribute("uv",new Vn(v,d)),S.setAttribute("faceIndex",new Vn(x,m)),e.push(S),i>Ha&&i--}return{lodPlanes:e,sizeLods:t,sigmas:n}}function dy(r,e,t){let n=new wr(r,e,t);return n.texture.mapping=Ac,n.texture.name="PMREM.cubeUv",n.scissorTest=!0,n}function bf(r,e,t,n,i){r.viewport.set(e,t,n,i),r.scissor.set(e,t,n,i)}function iE(r,e,t){let n=new Float32Array(Ao),i=new se(0,1,0);return new nr({name:"SphericalGaussianBlur",defines:{n:Ao,CUBEUV_TEXEL_WIDTH:1/e,CUBEUV_TEXEL_HEIGHT:1/t,CUBEUV_MAX_MIP:`${r}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:n},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:i}},vertexShader:Kp(),fragmentShader:` + + precision mediump float; + precision mediump int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; + + #define ENVMAP_TYPE_CUBE_UV + #include + + vec3 getSample( float theta, vec3 axis ) { + + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); + + return bilinearCubeUV( envMap, sampleDirection, mipInt ); + + } + + void main() { + + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); + + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { + + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); + + } + + axis = normalize( axis ); + + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); + + for ( int i = 1; i < n; i++ ) { + + if ( i >= samples ) { + + break; + + } + + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); + + } + + } + `,blending:as,depthTest:!1,depthWrite:!1})}function py(){return new nr({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Kp(),fragmentShader:` + + precision mediump float; + precision mediump int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + + #include + + void main() { + + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); + + gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); + + } + `,blending:as,depthTest:!1,depthWrite:!1})}function my(){return new nr({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Kp(),fragmentShader:` + + precision mediump float; + precision mediump int; + + uniform float flipEnvMap; + + varying vec3 vOutputDirection; + + uniform samplerCube envMap; + + void main() { + + gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); + + } + `,blending:as,depthTest:!1,depthWrite:!1})}function Kp(){return` + + precision mediump float; + precision mediump int; + + attribute float faceIndex; + + varying vec3 vOutputDirection; + + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { + + uv = 2.0 * uv - 1.0; + + vec3 direction = vec3( uv, 1.0 ); + + if ( face == 0.0 ) { + + direction = direction.zyx; // ( 1, v, u ) pos x + + } else if ( face == 1.0 ) { + + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y + + } else if ( face == 2.0 ) { + + direction.x *= -1.0; // ( -u, v, 1 ) pos z + + } else if ( face == 3.0 ) { + + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x + + } else if ( face == 4.0 ) { + + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y + + } else if ( face == 5.0 ) { + + direction.z *= -1.0; // ( u, v, -1 ) neg z + + } + + return direction; + + } + + void main() { + + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); + + } + `}function rE(r){let e=new WeakMap,t=null;function n(a){if(a&&a.isTexture){let l=a.mapping,c=l===Ua||l===zh,u=l===_o||l===To;if(c||u){let h=e.get(a),f=h!==void 0?h.texture.pmremVersion:0;if(a.isRenderTargetTexture&&a.pmremVersion!==f)return t===null&&(t=new Af(r)),h=c?t.fromEquirectangular(a,h):t.fromCubemap(a,h),h.texture.pmremVersion=a.pmremVersion,e.set(a,h),h.texture;if(h!==void 0)return h.texture;{let p=a.image;return c&&p&&p.height>0||u&&p&&i(p)?(t===null&&(t=new Af(r)),h=c?t.fromEquirectangular(a):t.fromCubemap(a),h.texture.pmremVersion=a.pmremVersion,e.set(a,h),a.addEventListener("dispose",s),h.texture):null}}}return a}function i(a){let l=0,c=6;for(let u=0;ue.maxTextureSize&&(S=Math.ceil(x/e.maxTextureSize),x=e.maxTextureSize);let I=new Float32Array(x*S*4*h),A=new nc(I,x,S,h);A.type=cr,A.needsUpdate=!0;let C=v*4;for(let b=0;b0)return r;let i=e*t,s=yy[i];if(s===void 0&&(s=new Float32Array(i),yy[i]=s),e!==0){n.toArray(s,0);for(let o=1,a=0;o!==e;++o)a+=t,r[o].toArray(s,a)}return s}function Ln(r,e){if(r.length!==e.length)return!1;for(let t=0,n=r.length;t":" "} ${a}: ${t[o]}`)}return n.join(` +`)}var by=new gt;function tb(r){mt._getMatrix(by,mt.workingColorSpace,r);let e=`mat3( ${by.elements.map(t=>t.toFixed(4))} )`;switch(mt.getTransfer(r)){case ec:return[e,"LinearTransferOETF"];case Kt:return[e,"sRGBTransferOETF"];default:return console.warn("THREE.WebGLProgram: Unsupported color space: ",r),[e,"LinearTransferOETF"]}}function Sy(r,e,t){let n=r.getShaderParameter(e,r.COMPILE_STATUS),i=r.getShaderInfoLog(e).trim();if(n&&i==="")return"";let s=/ERROR: 0:(\d+)/.exec(i);if(s){let o=parseInt(s[1]);return t.toUpperCase()+` + +`+i+` + +`+eb(r.getShaderSource(e),o)}else return i}function nb(r,e){let t=tb(e);return[`vec4 ${r}( vec4 value ) {`,` return ${t[1]}( vec4( value.rgb * ${t[0]}, value.a ) );`,"}"].join(` +`)}function ib(r,e){let t;switch(e){case N0:t="Linear";break;case L0:t="Reinhard";break;case O0:t="Cineon";break;case D0:t="ACESFilmic";break;case U0:t="AgX";break;case k0:t="Neutral";break;case F0:t="Custom";break;default:console.warn("THREE.WebGLProgram: Unsupported toneMapping:",e),t="Linear"}return"vec3 "+r+"( vec3 color ) { return "+t+"ToneMapping( color ); }"}var Sf=new se;function rb(){mt.getLuminanceCoefficients(Sf);let r=Sf.x.toFixed(4),e=Sf.y.toFixed(4),t=Sf.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${r}, ${e}, ${t} );`," return dot( weights, rgb );","}"].join(` +`)}function sb(r){return[r.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",r.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(Dc).join(` +`)}function ob(r){let e=[];for(let t in r){let n=r[t];n!==!1&&e.push("#define "+t+" "+n)}return e.join(` +`)}function ab(r,e){let t={},n=r.getProgramParameter(e,r.ACTIVE_ATTRIBUTES);for(let i=0;i/gm;function Wp(r){return r.replace(lb,ub)}var cb=new Map;function ub(r,e){let t=Ct[e];if(t===void 0){let n=cb.get(e);if(n!==void 0)t=Ct[n],console.warn('THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',e,n);else throw new Error("Can not resolve #include <"+e+">")}return Wp(t)}var hb=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function Cy(r){return r.replace(hb,fb)}function fb(r,e,t,n){let i="";for(let s=parseInt(e);s0&&(d+=` +`),m=["#define SHADER_TYPE "+t.shaderType,"#define SHADER_NAME "+t.shaderName,g].filter(Dc).join(` +`),m.length>0&&(m+=` +`)):(d=[Iy(t),"#define SHADER_TYPE "+t.shaderType,"#define SHADER_NAME "+t.shaderName,g,t.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",t.batching?"#define USE_BATCHING":"",t.batchingColor?"#define USE_BATCHING_COLOR":"",t.instancing?"#define USE_INSTANCING":"",t.instancingColor?"#define USE_INSTANCING_COLOR":"",t.instancingMorph?"#define USE_INSTANCING_MORPH":"",t.useFog&&t.fog?"#define USE_FOG":"",t.useFog&&t.fogExp2?"#define FOG_EXP2":"",t.map?"#define USE_MAP":"",t.envMap?"#define USE_ENVMAP":"",t.envMap?"#define "+u:"",t.lightMap?"#define USE_LIGHTMAP":"",t.aoMap?"#define USE_AOMAP":"",t.bumpMap?"#define USE_BUMPMAP":"",t.normalMap?"#define USE_NORMALMAP":"",t.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",t.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",t.displacementMap?"#define USE_DISPLACEMENTMAP":"",t.emissiveMap?"#define USE_EMISSIVEMAP":"",t.anisotropy?"#define USE_ANISOTROPY":"",t.anisotropyMap?"#define USE_ANISOTROPYMAP":"",t.clearcoatMap?"#define USE_CLEARCOATMAP":"",t.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",t.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",t.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",t.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",t.specularMap?"#define USE_SPECULARMAP":"",t.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",t.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",t.roughnessMap?"#define USE_ROUGHNESSMAP":"",t.metalnessMap?"#define USE_METALNESSMAP":"",t.alphaMap?"#define USE_ALPHAMAP":"",t.alphaHash?"#define USE_ALPHAHASH":"",t.transmission?"#define USE_TRANSMISSION":"",t.transmissionMap?"#define USE_TRANSMISSIONMAP":"",t.thicknessMap?"#define USE_THICKNESSMAP":"",t.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",t.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",t.mapUv?"#define MAP_UV "+t.mapUv:"",t.alphaMapUv?"#define ALPHAMAP_UV "+t.alphaMapUv:"",t.lightMapUv?"#define LIGHTMAP_UV "+t.lightMapUv:"",t.aoMapUv?"#define AOMAP_UV "+t.aoMapUv:"",t.emissiveMapUv?"#define EMISSIVEMAP_UV "+t.emissiveMapUv:"",t.bumpMapUv?"#define BUMPMAP_UV "+t.bumpMapUv:"",t.normalMapUv?"#define NORMALMAP_UV "+t.normalMapUv:"",t.displacementMapUv?"#define DISPLACEMENTMAP_UV "+t.displacementMapUv:"",t.metalnessMapUv?"#define METALNESSMAP_UV "+t.metalnessMapUv:"",t.roughnessMapUv?"#define ROUGHNESSMAP_UV "+t.roughnessMapUv:"",t.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+t.anisotropyMapUv:"",t.clearcoatMapUv?"#define CLEARCOATMAP_UV "+t.clearcoatMapUv:"",t.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+t.clearcoatNormalMapUv:"",t.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+t.clearcoatRoughnessMapUv:"",t.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+t.iridescenceMapUv:"",t.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+t.iridescenceThicknessMapUv:"",t.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+t.sheenColorMapUv:"",t.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+t.sheenRoughnessMapUv:"",t.specularMapUv?"#define SPECULARMAP_UV "+t.specularMapUv:"",t.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+t.specularColorMapUv:"",t.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+t.specularIntensityMapUv:"",t.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+t.transmissionMapUv:"",t.thicknessMapUv?"#define THICKNESSMAP_UV "+t.thicknessMapUv:"",t.vertexTangents&&t.flatShading===!1?"#define USE_TANGENT":"",t.vertexColors?"#define USE_COLOR":"",t.vertexAlphas?"#define USE_COLOR_ALPHA":"",t.vertexUv1s?"#define USE_UV1":"",t.vertexUv2s?"#define USE_UV2":"",t.vertexUv3s?"#define USE_UV3":"",t.pointsUvs?"#define USE_POINTS_UV":"",t.flatShading?"#define FLAT_SHADED":"",t.skinning?"#define USE_SKINNING":"",t.morphTargets?"#define USE_MORPHTARGETS":"",t.morphNormals&&t.flatShading===!1?"#define USE_MORPHNORMALS":"",t.morphColors?"#define USE_MORPHCOLORS":"",t.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+t.morphTextureStride:"",t.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+t.morphTargetsCount:"",t.doubleSided?"#define DOUBLE_SIDED":"",t.flipSided?"#define FLIP_SIDED":"",t.shadowMapEnabled?"#define USE_SHADOWMAP":"",t.shadowMapEnabled?"#define "+l:"",t.sizeAttenuation?"#define USE_SIZEATTENUATION":"",t.numLightProbes>0?"#define USE_LIGHT_PROBES":"",t.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",t.reverseDepthBuffer?"#define USE_REVERSEDEPTHBUF":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` +`].filter(Dc).join(` +`),m=[Iy(t),"#define SHADER_TYPE "+t.shaderType,"#define SHADER_NAME "+t.shaderName,g,t.useFog&&t.fog?"#define USE_FOG":"",t.useFog&&t.fogExp2?"#define FOG_EXP2":"",t.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",t.map?"#define USE_MAP":"",t.matcap?"#define USE_MATCAP":"",t.envMap?"#define USE_ENVMAP":"",t.envMap?"#define "+c:"",t.envMap?"#define "+u:"",t.envMap?"#define "+h:"",f?"#define CUBEUV_TEXEL_WIDTH "+f.texelWidth:"",f?"#define CUBEUV_TEXEL_HEIGHT "+f.texelHeight:"",f?"#define CUBEUV_MAX_MIP "+f.maxMip+".0":"",t.lightMap?"#define USE_LIGHTMAP":"",t.aoMap?"#define USE_AOMAP":"",t.bumpMap?"#define USE_BUMPMAP":"",t.normalMap?"#define USE_NORMALMAP":"",t.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",t.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",t.emissiveMap?"#define USE_EMISSIVEMAP":"",t.anisotropy?"#define USE_ANISOTROPY":"",t.anisotropyMap?"#define USE_ANISOTROPYMAP":"",t.clearcoat?"#define USE_CLEARCOAT":"",t.clearcoatMap?"#define USE_CLEARCOATMAP":"",t.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",t.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",t.dispersion?"#define USE_DISPERSION":"",t.iridescence?"#define USE_IRIDESCENCE":"",t.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",t.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",t.specularMap?"#define USE_SPECULARMAP":"",t.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",t.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",t.roughnessMap?"#define USE_ROUGHNESSMAP":"",t.metalnessMap?"#define USE_METALNESSMAP":"",t.alphaMap?"#define USE_ALPHAMAP":"",t.alphaTest?"#define USE_ALPHATEST":"",t.alphaHash?"#define USE_ALPHAHASH":"",t.sheen?"#define USE_SHEEN":"",t.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",t.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",t.transmission?"#define USE_TRANSMISSION":"",t.transmissionMap?"#define USE_TRANSMISSIONMAP":"",t.thicknessMap?"#define USE_THICKNESSMAP":"",t.vertexTangents&&t.flatShading===!1?"#define USE_TANGENT":"",t.vertexColors||t.instancingColor||t.batchingColor?"#define USE_COLOR":"",t.vertexAlphas?"#define USE_COLOR_ALPHA":"",t.vertexUv1s?"#define USE_UV1":"",t.vertexUv2s?"#define USE_UV2":"",t.vertexUv3s?"#define USE_UV3":"",t.pointsUvs?"#define USE_POINTS_UV":"",t.gradientMap?"#define USE_GRADIENTMAP":"",t.flatShading?"#define FLAT_SHADED":"",t.doubleSided?"#define DOUBLE_SIDED":"",t.flipSided?"#define FLIP_SIDED":"",t.shadowMapEnabled?"#define USE_SHADOWMAP":"",t.shadowMapEnabled?"#define "+l:"",t.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",t.numLightProbes>0?"#define USE_LIGHT_PROBES":"",t.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",t.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",t.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",t.reverseDepthBuffer?"#define USE_REVERSEDEPTHBUF":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",t.toneMapping!==ls?"#define TONE_MAPPING":"",t.toneMapping!==ls?Ct.tonemapping_pars_fragment:"",t.toneMapping!==ls?ib("toneMapping",t.toneMapping):"",t.dithering?"#define DITHERING":"",t.opaque?"#define OPAQUE":"",Ct.colorspace_pars_fragment,nb("linearToOutputTexel",t.outputColorSpace),rb(),t.useDepthPacking?"#define DEPTH_PACKING "+t.depthPacking:"",` +`].filter(Dc).join(` +`)),o=Wp(o),o=Ay(o,t),o=wy(o,t),a=Wp(a),a=Ay(a,t),a=wy(a,t),o=Cy(o),a=Cy(a),t.isRawShaderMaterial!==!0&&(_=`#version 300 es +`,d=[p,"#define attribute in","#define varying out","#define texture2D texture"].join(` +`)+` +`+d,m=["#define varying in",t.glslVersion===wp?"":"layout(location = 0) out highp vec4 pc_fragColor;",t.glslVersion===wp?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` +`)+` +`+m);let v=_+d+o,x=_+m+a,S=Ey(i,i.VERTEX_SHADER,v),I=Ey(i,i.FRAGMENT_SHADER,x);i.attachShader(y,S),i.attachShader(y,I),t.index0AttributeName!==void 0?i.bindAttribLocation(y,0,t.index0AttributeName):t.morphTargets===!0&&i.bindAttribLocation(y,0,"position"),i.linkProgram(y);function A(R){if(r.debug.checkShaderErrors){let N=i.getProgramInfoLog(y).trim(),B=i.getShaderInfoLog(S).trim(),V=i.getShaderInfoLog(I).trim(),ie=!0,Y=!0;if(i.getProgramParameter(y,i.LINK_STATUS)===!1)if(ie=!1,typeof r.debug.onShaderError=="function")r.debug.onShaderError(i,y,S,I);else{let te=Sy(i,S,"vertex"),q=Sy(i,I,"fragment");console.error("THREE.WebGLProgram: Shader Error "+i.getError()+" - VALIDATE_STATUS "+i.getProgramParameter(y,i.VALIDATE_STATUS)+` + +Material Name: `+R.name+` +Material Type: `+R.type+` + +Program Info Log: `+N+` +`+te+` +`+q)}else N!==""?console.warn("THREE.WebGLProgram: Program Info Log:",N):(B===""||V==="")&&(Y=!1);Y&&(R.diagnostics={runnable:ie,programLog:N,vertexShader:{log:B,prefix:d},fragmentShader:{log:V,prefix:m}})}i.deleteShader(S),i.deleteShader(I),C=new Wa(i,y),E=ab(i,y)}let C;this.getUniforms=function(){return C===void 0&&A(this),C};let E;this.getAttributes=function(){return E===void 0&&A(this),E};let b=t.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return b===!1&&(b=i.getProgramParameter(y,$E)),b},this.destroy=function(){n.releaseStatesOfProgram(this),i.deleteProgram(y),this.program=void 0},this.type=t.shaderType,this.name=t.shaderName,this.id=QE++,this.cacheKey=e,this.usedTimes=1,this.program=y,this.vertexShader=S,this.fragmentShader=I,this}var vb=0,Xp=class{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(e){let t=e.vertexShader,n=e.fragmentShader,i=this._getShaderStage(t),s=this._getShaderStage(n),o=this._getShaderCacheForMaterial(e);return o.has(i)===!1&&(o.add(i),i.usedTimes++),o.has(s)===!1&&(o.add(s),s.usedTimes++),this}remove(e){let t=this.materialCache.get(e);for(let n of t)n.usedTimes--,n.usedTimes===0&&this.shaderCache.delete(n.code);return this.materialCache.delete(e),this}getVertexShaderID(e){return this._getShaderStage(e.vertexShader).id}getFragmentShaderID(e){return this._getShaderStage(e.fragmentShader).id}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(e){let t=this.materialCache,n=t.get(e);return n===void 0&&(n=new Set,t.set(e,n)),n}_getShaderStage(e){let t=this.shaderCache,n=t.get(e);return n===void 0&&(n=new qp(e),t.set(e,n)),n}},qp=class{constructor(e){this.id=vb++,this.code=e,this.usedTimes=0}};function _b(r,e,t,n,i,s,o){let a=new Ca,l=new Xp,c=new Set,u=[],h=i.logarithmicDepthBuffer,f=i.vertexTextures,p=i.precision,g={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function y(E){return c.add(E),E===0?"uv":`uv${E}`}function d(E,b,R,N,B){let V=N.fog,ie=B.geometry,Y=E.isMeshStandardMaterial?N.environment:null,te=(E.isMeshStandardMaterial?t:e).get(E.envMap||Y),q=te&&te.mapping===Ac?te.image.height:null,le=g[E.type];E.precision!==null&&(p=i.getMaxPrecision(E.precision),p!==E.precision&&console.warn("THREE.WebGLProgram.getParameters:",E.precision,"not supported, using",p,"instead."));let ye=ie.morphAttributes.position||ie.morphAttributes.normal||ie.morphAttributes.color,Te=ye!==void 0?ye.length:0,Ae=0;ie.morphAttributes.position!==void 0&&(Ae=1),ie.morphAttributes.normal!==void 0&&(Ae=2),ie.morphAttributes.color!==void 0&&(Ae=3);let _e,G,P,M;if(le){let Fe=Rr[le];_e=Fe.vertexShader,G=Fe.fragmentShader}else _e=E.vertexShader,G=E.fragmentShader,l.update(E),P=l.getVertexShaderID(E),M=l.getFragmentShaderID(E);let T=r.getRenderTarget(),k=r.state.buffers.depth.getReversed(),j=B.isInstancedMesh===!0,W=B.isBatchedMesh===!0,re=!!E.map,ce=!!E.matcap,me=!!te,w=!!E.aoMap,ae=!!E.lightMap,pe=!!E.bumpMap,ue=!!E.normalMap,ee=!!E.displacementMap,$=!!E.emissiveMap,ve=!!E.metalnessMap,H=!!E.roughnessMap,L=E.anisotropy>0,O=E.clearcoat>0,D=E.dispersion>0,z=E.iridescence>0,Q=E.sheen>0,ne=E.transmission>0,oe=L&&!!E.anisotropyMap,he=O&&!!E.clearcoatMap,ge=O&&!!E.clearcoatNormalMap,de=O&&!!E.clearcoatRoughnessMap,Ee=z&&!!E.iridescenceMap,Re=z&&!!E.iridescenceThicknessMap,De=Q&&!!E.sheenColorMap,ke=Q&&!!E.sheenRoughnessMap,at=!!E.specularMap,je=!!E.specularColorMap,Ke=!!E.specularIntensityMap,fe=ne&&!!E.transmissionMap,Le=ne&&!!E.thicknessMap,be=!!E.gradientMap,Ce=!!E.alphaMap,Be=E.alphaTest>0,Oe=!!E.alphaHash,rt=!!E.extensions,Et=ls;E.toneMapped&&(T===null||T.isXRRenderTarget===!0)&&(Et=r.toneMapping);let ft={shaderID:le,shaderType:E.type,shaderName:E.name,vertexShader:_e,fragmentShader:G,defines:E.defines,customVertexShaderID:P,customFragmentShaderID:M,isRawShaderMaterial:E.isRawShaderMaterial===!0,glslVersion:E.glslVersion,precision:p,batching:W,batchingColor:W&&B._colorsTexture!==null,instancing:j,instancingColor:j&&B.instanceColor!==null,instancingMorph:j&&B.morphTexture!==null,supportsVertexTextures:f,outputColorSpace:T===null?r.outputColorSpace:T.isXRRenderTarget===!0?T.texture.colorSpace:Ci,alphaToCoverage:!!E.alphaToCoverage,map:re,matcap:ce,envMap:me,envMapMode:me&&te.mapping,envMapCubeUVHeight:q,aoMap:w,lightMap:ae,bumpMap:pe,normalMap:ue,displacementMap:f&&ee,emissiveMap:$,normalMapObjectSpace:ue&&E.normalMapType===W0,normalMapTangentSpace:ue&&E.normalMapType===Lc,metalnessMap:ve,roughnessMap:H,anisotropy:L,anisotropyMap:oe,clearcoat:O,clearcoatMap:he,clearcoatNormalMap:ge,clearcoatRoughnessMap:de,dispersion:D,iridescence:z,iridescenceMap:Ee,iridescenceThicknessMap:Re,sheen:Q,sheenColorMap:De,sheenRoughnessMap:ke,specularMap:at,specularColorMap:je,specularIntensityMap:Ke,transmission:ne,transmissionMap:fe,thicknessMap:Le,gradientMap:be,opaque:E.transparent===!1&&E.blending===io&&E.alphaToCoverage===!1,alphaMap:Ce,alphaTest:Be,alphaHash:Oe,combine:E.combine,mapUv:re&&y(E.map.channel),aoMapUv:w&&y(E.aoMap.channel),lightMapUv:ae&&y(E.lightMap.channel),bumpMapUv:pe&&y(E.bumpMap.channel),normalMapUv:ue&&y(E.normalMap.channel),displacementMapUv:ee&&y(E.displacementMap.channel),emissiveMapUv:$&&y(E.emissiveMap.channel),metalnessMapUv:ve&&y(E.metalnessMap.channel),roughnessMapUv:H&&y(E.roughnessMap.channel),anisotropyMapUv:oe&&y(E.anisotropyMap.channel),clearcoatMapUv:he&&y(E.clearcoatMap.channel),clearcoatNormalMapUv:ge&&y(E.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:de&&y(E.clearcoatRoughnessMap.channel),iridescenceMapUv:Ee&&y(E.iridescenceMap.channel),iridescenceThicknessMapUv:Re&&y(E.iridescenceThicknessMap.channel),sheenColorMapUv:De&&y(E.sheenColorMap.channel),sheenRoughnessMapUv:ke&&y(E.sheenRoughnessMap.channel),specularMapUv:at&&y(E.specularMap.channel),specularColorMapUv:je&&y(E.specularColorMap.channel),specularIntensityMapUv:Ke&&y(E.specularIntensityMap.channel),transmissionMapUv:fe&&y(E.transmissionMap.channel),thicknessMapUv:Le&&y(E.thicknessMap.channel),alphaMapUv:Ce&&y(E.alphaMap.channel),vertexTangents:!!ie.attributes.tangent&&(ue||L),vertexColors:E.vertexColors,vertexAlphas:E.vertexColors===!0&&!!ie.attributes.color&&ie.attributes.color.itemSize===4,pointsUvs:B.isPoints===!0&&!!ie.attributes.uv&&(re||Ce),fog:!!V,useFog:E.fog===!0,fogExp2:!!V&&V.isFogExp2,flatShading:E.flatShading===!0,sizeAttenuation:E.sizeAttenuation===!0,logarithmicDepthBuffer:h,reverseDepthBuffer:k,skinning:B.isSkinnedMesh===!0,morphTargets:ie.morphAttributes.position!==void 0,morphNormals:ie.morphAttributes.normal!==void 0,morphColors:ie.morphAttributes.color!==void 0,morphTargetsCount:Te,morphTextureStride:Ae,numDirLights:b.directional.length,numPointLights:b.point.length,numSpotLights:b.spot.length,numSpotLightMaps:b.spotLightMap.length,numRectAreaLights:b.rectArea.length,numHemiLights:b.hemi.length,numDirLightShadows:b.directionalShadowMap.length,numPointLightShadows:b.pointShadowMap.length,numSpotLightShadows:b.spotShadowMap.length,numSpotLightShadowsWithMaps:b.numSpotLightShadowsWithMaps,numLightProbes:b.numLightProbes,numClippingPlanes:o.numPlanes,numClipIntersection:o.numIntersection,dithering:E.dithering,shadowMapEnabled:r.shadowMap.enabled&&R.length>0,shadowMapType:r.shadowMap.type,toneMapping:Et,decodeVideoTexture:re&&E.map.isVideoTexture===!0&&mt.getTransfer(E.map.colorSpace)===Kt,decodeVideoTextureEmissive:$&&E.emissiveMap.isVideoTexture===!0&&mt.getTransfer(E.emissiveMap.colorSpace)===Kt,premultipliedAlpha:E.premultipliedAlpha,doubleSided:E.side===Nn,flipSided:E.side===_n,useDepthPacking:E.depthPacking>=0,depthPacking:E.depthPacking||0,index0AttributeName:E.index0AttributeName,extensionClipCullDistance:rt&&E.extensions.clipCullDistance===!0&&n.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(rt&&E.extensions.multiDraw===!0||W)&&n.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:n.has("KHR_parallel_shader_compile"),customProgramCacheKey:E.customProgramCacheKey()};return ft.vertexUv1s=c.has(1),ft.vertexUv2s=c.has(2),ft.vertexUv3s=c.has(3),c.clear(),ft}function m(E){let b=[];if(E.shaderID?b.push(E.shaderID):(b.push(E.customVertexShaderID),b.push(E.customFragmentShaderID)),E.defines!==void 0)for(let R in E.defines)b.push(R),b.push(E.defines[R]);return E.isRawShaderMaterial===!1&&(_(b,E),v(b,E),b.push(r.outputColorSpace)),b.push(E.customProgramCacheKey),b.join()}function _(E,b){E.push(b.precision),E.push(b.outputColorSpace),E.push(b.envMapMode),E.push(b.envMapCubeUVHeight),E.push(b.mapUv),E.push(b.alphaMapUv),E.push(b.lightMapUv),E.push(b.aoMapUv),E.push(b.bumpMapUv),E.push(b.normalMapUv),E.push(b.displacementMapUv),E.push(b.emissiveMapUv),E.push(b.metalnessMapUv),E.push(b.roughnessMapUv),E.push(b.anisotropyMapUv),E.push(b.clearcoatMapUv),E.push(b.clearcoatNormalMapUv),E.push(b.clearcoatRoughnessMapUv),E.push(b.iridescenceMapUv),E.push(b.iridescenceThicknessMapUv),E.push(b.sheenColorMapUv),E.push(b.sheenRoughnessMapUv),E.push(b.specularMapUv),E.push(b.specularColorMapUv),E.push(b.specularIntensityMapUv),E.push(b.transmissionMapUv),E.push(b.thicknessMapUv),E.push(b.combine),E.push(b.fogExp2),E.push(b.sizeAttenuation),E.push(b.morphTargetsCount),E.push(b.morphAttributeCount),E.push(b.numDirLights),E.push(b.numPointLights),E.push(b.numSpotLights),E.push(b.numSpotLightMaps),E.push(b.numHemiLights),E.push(b.numRectAreaLights),E.push(b.numDirLightShadows),E.push(b.numPointLightShadows),E.push(b.numSpotLightShadows),E.push(b.numSpotLightShadowsWithMaps),E.push(b.numLightProbes),E.push(b.shadowMapType),E.push(b.toneMapping),E.push(b.numClippingPlanes),E.push(b.numClipIntersection),E.push(b.depthPacking)}function v(E,b){a.disableAll(),b.supportsVertexTextures&&a.enable(0),b.instancing&&a.enable(1),b.instancingColor&&a.enable(2),b.instancingMorph&&a.enable(3),b.matcap&&a.enable(4),b.envMap&&a.enable(5),b.normalMapObjectSpace&&a.enable(6),b.normalMapTangentSpace&&a.enable(7),b.clearcoat&&a.enable(8),b.iridescence&&a.enable(9),b.alphaTest&&a.enable(10),b.vertexColors&&a.enable(11),b.vertexAlphas&&a.enable(12),b.vertexUv1s&&a.enable(13),b.vertexUv2s&&a.enable(14),b.vertexUv3s&&a.enable(15),b.vertexTangents&&a.enable(16),b.anisotropy&&a.enable(17),b.alphaHash&&a.enable(18),b.batching&&a.enable(19),b.dispersion&&a.enable(20),b.batchingColor&&a.enable(21),E.push(a.mask),a.disableAll(),b.fog&&a.enable(0),b.useFog&&a.enable(1),b.flatShading&&a.enable(2),b.logarithmicDepthBuffer&&a.enable(3),b.reverseDepthBuffer&&a.enable(4),b.skinning&&a.enable(5),b.morphTargets&&a.enable(6),b.morphNormals&&a.enable(7),b.morphColors&&a.enable(8),b.premultipliedAlpha&&a.enable(9),b.shadowMapEnabled&&a.enable(10),b.doubleSided&&a.enable(11),b.flipSided&&a.enable(12),b.useDepthPacking&&a.enable(13),b.dithering&&a.enable(14),b.transmission&&a.enable(15),b.sheen&&a.enable(16),b.opaque&&a.enable(17),b.pointsUvs&&a.enable(18),b.decodeVideoTexture&&a.enable(19),b.decodeVideoTextureEmissive&&a.enable(20),b.alphaToCoverage&&a.enable(21),E.push(a.mask)}function x(E){let b=g[E.type],R;if(b){let N=Rr[b];R=iy.clone(N.uniforms)}else R=E.uniforms;return R}function S(E,b){let R;for(let N=0,B=u.length;N0?n.push(m):p.transparent===!0?i.push(m):t.push(m)}function l(h,f,p,g,y,d){let m=o(h,f,p,g,y,d);p.transmission>0?n.unshift(m):p.transparent===!0?i.unshift(m):t.unshift(m)}function c(h,f){t.length>1&&t.sort(h||Mb),n.length>1&&n.sort(f||Ry),i.length>1&&i.sort(f||Ry)}function u(){for(let h=e,f=r.length;h=s.length?(o=new Py,s.push(o)):o=s[i],o}function t(){r=new WeakMap}return{get:e,dispose:t}}function bb(){let r={};return{get:function(e){if(r[e.id]!==void 0)return r[e.id];let t;switch(e.type){case"DirectionalLight":t={direction:new se,color:new He};break;case"SpotLight":t={position:new se,direction:new se,color:new He,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":t={position:new se,color:new He,distance:0,decay:0};break;case"HemisphereLight":t={direction:new se,skyColor:new He,groundColor:new He};break;case"RectAreaLight":t={color:new He,position:new se,halfWidth:new se,halfHeight:new se};break}return r[e.id]=t,t}}}function Sb(){let r={};return{get:function(e){if(r[e.id]!==void 0)return r[e.id];let t;switch(e.type){case"DirectionalLight":t={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Pe};break;case"SpotLight":t={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Pe};break;case"PointLight":t={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Pe,shadowCameraNear:1,shadowCameraFar:1e3};break}return r[e.id]=t,t}}}var Ab=0;function wb(r,e){return(e.castShadow?2:0)-(r.castShadow?2:0)+(e.map?1:0)-(r.map?1:0)}function Cb(r){let e=new bb,t=Sb(),n={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let c=0;c<9;c++)n.probe.push(new se);let i=new se,s=new Je,o=new Je;function a(c){let u=0,h=0,f=0;for(let E=0;E<9;E++)n.probe[E].set(0,0,0);let p=0,g=0,y=0,d=0,m=0,_=0,v=0,x=0,S=0,I=0,A=0;c.sort(wb);for(let E=0,b=c.length;E0&&(r.has("OES_texture_float_linear")===!0?(n.rectAreaLTC1=qe.LTC_FLOAT_1,n.rectAreaLTC2=qe.LTC_FLOAT_2):(n.rectAreaLTC1=qe.LTC_HALF_1,n.rectAreaLTC2=qe.LTC_HALF_2)),n.ambient[0]=u,n.ambient[1]=h,n.ambient[2]=f;let C=n.hash;(C.directionalLength!==p||C.pointLength!==g||C.spotLength!==y||C.rectAreaLength!==d||C.hemiLength!==m||C.numDirectionalShadows!==_||C.numPointShadows!==v||C.numSpotShadows!==x||C.numSpotMaps!==S||C.numLightProbes!==A)&&(n.directional.length=p,n.spot.length=y,n.rectArea.length=d,n.point.length=g,n.hemi.length=m,n.directionalShadow.length=_,n.directionalShadowMap.length=_,n.pointShadow.length=v,n.pointShadowMap.length=v,n.spotShadow.length=x,n.spotShadowMap.length=x,n.directionalShadowMatrix.length=_,n.pointShadowMatrix.length=v,n.spotLightMatrix.length=x+S-I,n.spotLightMap.length=S,n.numSpotLightShadowsWithMaps=I,n.numLightProbes=A,C.directionalLength=p,C.pointLength=g,C.spotLength=y,C.rectAreaLength=d,C.hemiLength=m,C.numDirectionalShadows=_,C.numPointShadows=v,C.numSpotShadows=x,C.numSpotMaps=S,C.numLightProbes=A,n.version=Ab++)}function l(c,u){let h=0,f=0,p=0,g=0,y=0,d=u.matrixWorldInverse;for(let m=0,_=c.length;m<_;m++){let v=c[m];if(v.isDirectionalLight){let x=n.directional[h];x.direction.setFromMatrixPosition(v.matrixWorld),i.setFromMatrixPosition(v.target.matrixWorld),x.direction.sub(i),x.direction.transformDirection(d),h++}else if(v.isSpotLight){let x=n.spot[p];x.position.setFromMatrixPosition(v.matrixWorld),x.position.applyMatrix4(d),x.direction.setFromMatrixPosition(v.matrixWorld),i.setFromMatrixPosition(v.target.matrixWorld),x.direction.sub(i),x.direction.transformDirection(d),p++}else if(v.isRectAreaLight){let x=n.rectArea[g];x.position.setFromMatrixPosition(v.matrixWorld),x.position.applyMatrix4(d),o.identity(),s.copy(v.matrixWorld),s.premultiply(d),o.extractRotation(s),x.halfWidth.set(v.width*.5,0,0),x.halfHeight.set(0,v.height*.5,0),x.halfWidth.applyMatrix4(o),x.halfHeight.applyMatrix4(o),g++}else if(v.isPointLight){let x=n.point[f];x.position.setFromMatrixPosition(v.matrixWorld),x.position.applyMatrix4(d),f++}else if(v.isHemisphereLight){let x=n.hemi[y];x.direction.setFromMatrixPosition(v.matrixWorld),x.direction.transformDirection(d),y++}}}return{setup:a,setupView:l,state:n}}function Ny(r){let e=new Cb(r),t=[],n=[];function i(u){c.camera=u,t.length=0,n.length=0}function s(u){t.push(u)}function o(u){n.push(u)}function a(){e.setup(t)}function l(u){e.setupView(t,u)}let c={lightsArray:t,shadowsArray:n,camera:null,lights:e,transmissionRenderTarget:{}};return{init:i,state:c,setupLights:a,setupLightsView:l,pushLight:s,pushShadow:o}}function Ib(r){let e=new WeakMap;function t(i,s=0){let o=e.get(i),a;return o===void 0?(a=new Ny(r),e.set(i,[a])):s>=o.length?(a=new Ny(r),o.push(a)):a=o[s],a}function n(){e=new WeakMap}return{get:t,dispose:n}}var Rb=`void main() { + gl_Position = vec4( position, 1.0 ); +}`,Pb=`uniform sampler2D shadow_pass; +uniform vec2 resolution; +uniform float radius; +#include +void main() { + const float samples = float( VSM_SAMPLES ); + float mean = 0.0; + float squared_mean = 0.0; + float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); + float uvStart = samples <= 1.0 ? 0.0 : - 1.0; + for ( float i = 0.0; i < samples; i ++ ) { + float uvOffset = uvStart + i * uvStride; + #ifdef HORIZONTAL_PASS + vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); + mean += distribution.x; + squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; + #else + float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); + mean += depth; + squared_mean += depth * depth; + #endif + } + mean = mean / samples; + squared_mean = squared_mean / samples; + float std_dev = sqrt( squared_mean - mean * mean ); + gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); +}`;function Nb(r,e,t){let n=new Pa,i=new Pe,s=new Pe,o=new wt,a=new wh({depthPacking:H0}),l=new Ch,c={},u=t.maxTextureSize,h={[wi]:_n,[_n]:wi,[Nn]:Nn},f=new nr({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new Pe},radius:{value:4}},vertexShader:Rb,fragmentShader:Pb}),p=f.clone();p.defines.HORIZONTAL_PASS=1;let g=new It;g.setAttribute("position",new Vn(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));let y=new Dt(g,f),d=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=dp;let m=this.type;this.render=function(I,A,C){if(d.enabled===!1||d.autoUpdate===!1&&d.needsUpdate===!1||I.length===0)return;let E=r.getRenderTarget(),b=r.getActiveCubeFace(),R=r.getActiveMipmapLevel(),N=r.state;N.setBlending(as),N.buffers.color.setClear(1,1,1,1),N.buffers.depth.setTest(!0),N.setScissorTest(!1);let B=m!==Ir&&this.type===Ir,V=m===Ir&&this.type!==Ir;for(let ie=0,Y=I.length;ieu||i.y>u)&&(i.x>u&&(s.x=Math.floor(u/le.x),i.x=s.x*le.x,q.mapSize.x=s.x),i.y>u&&(s.y=Math.floor(u/le.y),i.y=s.y*le.y,q.mapSize.y=s.y)),q.map===null||B===!0||V===!0){let Te=this.type!==Ir?{minFilter:Xn,magFilter:Xn}:{};q.map!==null&&q.map.dispose(),q.map=new wr(i.x,i.y,Te),q.map.texture.name=te.name+".shadowMap",q.camera.updateProjectionMatrix()}r.setRenderTarget(q.map),r.clear();let ye=q.getViewportCount();for(let Te=0;Te0||A.map&&A.alphaTest>0||A.alphaToCoverage===!0){let N=b.uuid,B=A.uuid,V=c[N];V===void 0&&(V={},c[N]=V);let ie=V[B];ie===void 0&&(ie=b.clone(),V[B]=ie,A.addEventListener("dispose",S)),b=ie}if(b.visible=A.visible,b.wireframe=A.wireframe,E===Ir?b.side=A.shadowSide!==null?A.shadowSide:A.side:b.side=A.shadowSide!==null?A.shadowSide:h[A.side],b.alphaMap=A.alphaMap,b.alphaTest=A.alphaToCoverage===!0?.5:A.alphaTest,b.map=A.map,b.clipShadows=A.clipShadows,b.clippingPlanes=A.clippingPlanes,b.clipIntersection=A.clipIntersection,b.displacementMap=A.displacementMap,b.displacementScale=A.displacementScale,b.displacementBias=A.displacementBias,b.wireframeLinewidth=A.wireframeLinewidth,b.linewidth=A.linewidth,C.isPointLight===!0&&b.isMeshDistanceMaterial===!0){let N=r.properties.get(b);N.light=C}return b}function x(I,A,C,E,b){if(I.visible===!1)return;if(I.layers.test(A.layers)&&(I.isMesh||I.isLine||I.isPoints)&&(I.castShadow||I.receiveShadow&&b===Ir)&&(!I.frustumCulled||n.intersectsObject(I))){I.modelViewMatrix.multiplyMatrices(C.matrixWorldInverse,I.matrixWorld);let B=e.update(I),V=I.material;if(Array.isArray(V)){let ie=B.groups;for(let Y=0,te=ie.length;Y=1):q.indexOf("OpenGL ES")!==-1&&(te=parseFloat(/^OpenGL ES (\d)/.exec(q)[1]),Y=te>=2);let le=null,ye={},Te=r.getParameter(r.SCISSOR_BOX),Ae=r.getParameter(r.VIEWPORT),_e=new wt().fromArray(Te),G=new wt().fromArray(Ae);function P(fe,Le,be,Ce){let Be=new Uint8Array(4),Oe=r.createTexture();r.bindTexture(fe,Oe),r.texParameteri(fe,r.TEXTURE_MIN_FILTER,r.NEAREST),r.texParameteri(fe,r.TEXTURE_MAG_FILTER,r.NEAREST);for(let rt=0;rt"u"?!1:/OculusBrowser/g.test(navigator.userAgent),c=new Pe,u=new WeakMap,h,f=new WeakMap,p=!1;try{p=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function g(H,L){return p?new OffscreenCanvas(H,L):Aa("canvas")}function y(H,L,O){let D=1,z=ve(H);if((z.width>O||z.height>O)&&(D=O/Math.max(z.width,z.height)),D<1)if(typeof HTMLImageElement<"u"&&H instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&H instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&H instanceof ImageBitmap||typeof VideoFrame<"u"&&H instanceof VideoFrame){let Q=Math.floor(D*z.width),ne=Math.floor(D*z.height);h===void 0&&(h=g(Q,ne));let oe=L?g(Q,ne):h;return oe.width=Q,oe.height=ne,oe.getContext("2d").drawImage(H,0,0,Q,ne),console.warn("THREE.WebGLRenderer: Texture has been resized from ("+z.width+"x"+z.height+") to ("+Q+"x"+ne+")."),oe}else return"data"in H&&console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+z.width+"x"+z.height+")."),H;return H}function d(H){return H.generateMipmaps}function m(H){r.generateMipmap(H)}function _(H){return H.isWebGLCubeRenderTarget?r.TEXTURE_CUBE_MAP:H.isWebGL3DRenderTarget?r.TEXTURE_3D:H.isWebGLArrayRenderTarget||H.isCompressedArrayTexture?r.TEXTURE_2D_ARRAY:r.TEXTURE_2D}function v(H,L,O,D,z=!1){if(H!==null){if(r[H]!==void 0)return r[H];console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format '"+H+"'")}let Q=L;if(L===r.RED&&(O===r.FLOAT&&(Q=r.R32F),O===r.HALF_FLOAT&&(Q=r.R16F),O===r.UNSIGNED_BYTE&&(Q=r.R8)),L===r.RED_INTEGER&&(O===r.UNSIGNED_BYTE&&(Q=r.R8UI),O===r.UNSIGNED_SHORT&&(Q=r.R16UI),O===r.UNSIGNED_INT&&(Q=r.R32UI),O===r.BYTE&&(Q=r.R8I),O===r.SHORT&&(Q=r.R16I),O===r.INT&&(Q=r.R32I)),L===r.RG&&(O===r.FLOAT&&(Q=r.RG32F),O===r.HALF_FLOAT&&(Q=r.RG16F),O===r.UNSIGNED_BYTE&&(Q=r.RG8)),L===r.RG_INTEGER&&(O===r.UNSIGNED_BYTE&&(Q=r.RG8UI),O===r.UNSIGNED_SHORT&&(Q=r.RG16UI),O===r.UNSIGNED_INT&&(Q=r.RG32UI),O===r.BYTE&&(Q=r.RG8I),O===r.SHORT&&(Q=r.RG16I),O===r.INT&&(Q=r.RG32I)),L===r.RGB_INTEGER&&(O===r.UNSIGNED_BYTE&&(Q=r.RGB8UI),O===r.UNSIGNED_SHORT&&(Q=r.RGB16UI),O===r.UNSIGNED_INT&&(Q=r.RGB32UI),O===r.BYTE&&(Q=r.RGB8I),O===r.SHORT&&(Q=r.RGB16I),O===r.INT&&(Q=r.RGB32I)),L===r.RGBA_INTEGER&&(O===r.UNSIGNED_BYTE&&(Q=r.RGBA8UI),O===r.UNSIGNED_SHORT&&(Q=r.RGBA16UI),O===r.UNSIGNED_INT&&(Q=r.RGBA32UI),O===r.BYTE&&(Q=r.RGBA8I),O===r.SHORT&&(Q=r.RGBA16I),O===r.INT&&(Q=r.RGBA32I)),L===r.RGB&&O===r.UNSIGNED_INT_5_9_9_9_REV&&(Q=r.RGB9_E5),L===r.RGBA){let ne=z?ec:mt.getTransfer(D);O===r.FLOAT&&(Q=r.RGBA32F),O===r.HALF_FLOAT&&(Q=r.RGBA16F),O===r.UNSIGNED_BYTE&&(Q=ne===Kt?r.SRGB8_ALPHA8:r.RGBA8),O===r.UNSIGNED_SHORT_4_4_4_4&&(Q=r.RGBA4),O===r.UNSIGNED_SHORT_5_5_5_1&&(Q=r.RGB5_A1)}return(Q===r.R16F||Q===r.R32F||Q===r.RG16F||Q===r.RG32F||Q===r.RGBA16F||Q===r.RGBA32F)&&e.get("EXT_color_buffer_float"),Q}function x(H,L){let O;return H?L===null||L===Ls||L===Ga?O=r.DEPTH24_STENCIL8:L===cr?O=r.DEPTH32F_STENCIL8:L===ka&&(O=r.DEPTH24_STENCIL8,console.warn("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):L===null||L===Ls||L===Ga?O=r.DEPTH_COMPONENT24:L===cr?O=r.DEPTH_COMPONENT32F:L===ka&&(O=r.DEPTH_COMPONENT16),O}function S(H,L){return d(H)===!0||H.isFramebufferTexture&&H.minFilter!==Xn&&H.minFilter!==Pn?Math.log2(Math.max(L.width,L.height))+1:H.mipmaps!==void 0&&H.mipmaps.length>0?H.mipmaps.length:H.isCompressedTexture&&Array.isArray(H.image)?L.mipmaps.length:1}function I(H){let L=H.target;L.removeEventListener("dispose",I),C(L),L.isVideoTexture&&u.delete(L)}function A(H){let L=H.target;L.removeEventListener("dispose",A),b(L)}function C(H){let L=n.get(H);if(L.__webglInit===void 0)return;let O=H.source,D=f.get(O);if(D){let z=D[L.__cacheKey];z.usedTimes--,z.usedTimes===0&&E(H),Object.keys(D).length===0&&f.delete(O)}n.remove(H)}function E(H){let L=n.get(H);r.deleteTexture(L.__webglTexture);let O=H.source,D=f.get(O);delete D[L.__cacheKey],o.memory.textures--}function b(H){let L=n.get(H);if(H.depthTexture&&(H.depthTexture.dispose(),n.remove(H.depthTexture)),H.isWebGLCubeRenderTarget)for(let D=0;D<6;D++){if(Array.isArray(L.__webglFramebuffer[D]))for(let z=0;z=i.maxTextures&&console.warn("THREE.WebGLTextures: Trying to use "+H+" texture units while this GPU supports only "+i.maxTextures),R+=1,H}function V(H){let L=[];return L.push(H.wrapS),L.push(H.wrapT),L.push(H.wrapR||0),L.push(H.magFilter),L.push(H.minFilter),L.push(H.anisotropy),L.push(H.internalFormat),L.push(H.format),L.push(H.type),L.push(H.generateMipmaps),L.push(H.premultiplyAlpha),L.push(H.flipY),L.push(H.unpackAlignment),L.push(H.colorSpace),L.join()}function ie(H,L){let O=n.get(H);if(H.isVideoTexture&&ee(H),H.isRenderTargetTexture===!1&&H.version>0&&O.__version!==H.version){let D=H.image;if(D===null)console.warn("THREE.WebGLRenderer: Texture marked for update but no image data found.");else if(D.complete===!1)console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete");else{G(O,H,L);return}}t.bindTexture(r.TEXTURE_2D,O.__webglTexture,r.TEXTURE0+L)}function Y(H,L){let O=n.get(H);if(H.version>0&&O.__version!==H.version){G(O,H,L);return}t.bindTexture(r.TEXTURE_2D_ARRAY,O.__webglTexture,r.TEXTURE0+L)}function te(H,L){let O=n.get(H);if(H.version>0&&O.__version!==H.version){G(O,H,L);return}t.bindTexture(r.TEXTURE_3D,O.__webglTexture,r.TEXTURE0+L)}function q(H,L){let O=n.get(H);if(H.version>0&&O.__version!==H.version){P(O,H,L);return}t.bindTexture(r.TEXTURE_CUBE_MAP,O.__webglTexture,r.TEXTURE0+L)}let le={[on]:r.REPEAT,[un]:r.CLAMP_TO_EDGE,[so]:r.MIRRORED_REPEAT},ye={[Xn]:r.NEAREST,[G0]:r.NEAREST_MIPMAP_NEAREST,[wc]:r.NEAREST_MIPMAP_LINEAR,[Pn]:r.LINEAR,[Hh]:r.LINEAR_MIPMAP_NEAREST,[xi]:r.LINEAR_MIPMAP_LINEAR},Te={[X0]:r.NEVER,[J0]:r.ALWAYS,[q0]:r.LESS,[Ap]:r.LEQUAL,[j0]:r.EQUAL,[Z0]:r.GEQUAL,[Y0]:r.GREATER,[K0]:r.NOTEQUAL};function Ae(H,L){if(L.type===cr&&e.has("OES_texture_float_linear")===!1&&(L.magFilter===Pn||L.magFilter===Hh||L.magFilter===wc||L.magFilter===xi||L.minFilter===Pn||L.minFilter===Hh||L.minFilter===wc||L.minFilter===xi)&&console.warn("THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),r.texParameteri(H,r.TEXTURE_WRAP_S,le[L.wrapS]),r.texParameteri(H,r.TEXTURE_WRAP_T,le[L.wrapT]),(H===r.TEXTURE_3D||H===r.TEXTURE_2D_ARRAY)&&r.texParameteri(H,r.TEXTURE_WRAP_R,le[L.wrapR]),r.texParameteri(H,r.TEXTURE_MAG_FILTER,ye[L.magFilter]),r.texParameteri(H,r.TEXTURE_MIN_FILTER,ye[L.minFilter]),L.compareFunction&&(r.texParameteri(H,r.TEXTURE_COMPARE_MODE,r.COMPARE_REF_TO_TEXTURE),r.texParameteri(H,r.TEXTURE_COMPARE_FUNC,Te[L.compareFunction])),e.has("EXT_texture_filter_anisotropic")===!0){if(L.magFilter===Xn||L.minFilter!==wc&&L.minFilter!==xi||L.type===cr&&e.has("OES_texture_float_linear")===!1)return;if(L.anisotropy>1||n.get(L).__currentAnisotropy){let O=e.get("EXT_texture_filter_anisotropic");r.texParameterf(H,O.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(L.anisotropy,i.getMaxAnisotropy())),n.get(L).__currentAnisotropy=L.anisotropy}}}function _e(H,L){let O=!1;H.__webglInit===void 0&&(H.__webglInit=!0,L.addEventListener("dispose",I));let D=L.source,z=f.get(D);z===void 0&&(z={},f.set(D,z));let Q=V(L);if(Q!==H.__cacheKey){z[Q]===void 0&&(z[Q]={texture:r.createTexture(),usedTimes:0},o.memory.textures++,O=!0),z[Q].usedTimes++;let ne=z[H.__cacheKey];ne!==void 0&&(z[H.__cacheKey].usedTimes--,ne.usedTimes===0&&E(L)),H.__cacheKey=Q,H.__webglTexture=z[Q].texture}return O}function G(H,L,O){let D=r.TEXTURE_2D;(L.isDataArrayTexture||L.isCompressedArrayTexture)&&(D=r.TEXTURE_2D_ARRAY),L.isData3DTexture&&(D=r.TEXTURE_3D);let z=_e(H,L),Q=L.source;t.bindTexture(D,H.__webglTexture,r.TEXTURE0+O);let ne=n.get(Q);if(Q.version!==ne.__version||z===!0){t.activeTexture(r.TEXTURE0+O);let oe=mt.getPrimaries(mt.workingColorSpace),he=L.colorSpace===cs?null:mt.getPrimaries(L.colorSpace),ge=L.colorSpace===cs||oe===he?r.NONE:r.BROWSER_DEFAULT_WEBGL;r.pixelStorei(r.UNPACK_FLIP_Y_WEBGL,L.flipY),r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,L.premultiplyAlpha),r.pixelStorei(r.UNPACK_ALIGNMENT,L.unpackAlignment),r.pixelStorei(r.UNPACK_COLORSPACE_CONVERSION_WEBGL,ge);let de=y(L.image,!1,i.maxTextureSize);de=$(L,de);let Ee=s.convert(L.format,L.colorSpace),Re=s.convert(L.type),De=v(L.internalFormat,Ee,Re,L.colorSpace,L.isVideoTexture);Ae(D,L);let ke,at=L.mipmaps,je=L.isVideoTexture!==!0,Ke=ne.__version===void 0||z===!0,fe=Q.dataReady,Le=S(L,de);if(L.isDepthTexture)De=x(L.format===Va,L.type),Ke&&(je?t.texStorage2D(r.TEXTURE_2D,1,De,de.width,de.height):t.texImage2D(r.TEXTURE_2D,0,De,de.width,de.height,0,Ee,Re,null));else if(L.isDataTexture)if(at.length>0){je&&Ke&&t.texStorage2D(r.TEXTURE_2D,Le,De,at[0].width,at[0].height);for(let be=0,Ce=at.length;be0){let Be=Op(ke.width,ke.height,L.format,L.type);for(let Oe of L.layerUpdates){let rt=ke.data.subarray(Oe*Be/ke.data.BYTES_PER_ELEMENT,(Oe+1)*Be/ke.data.BYTES_PER_ELEMENT);t.compressedTexSubImage3D(r.TEXTURE_2D_ARRAY,be,0,0,Oe,ke.width,ke.height,1,Ee,rt)}L.clearLayerUpdates()}else t.compressedTexSubImage3D(r.TEXTURE_2D_ARRAY,be,0,0,0,ke.width,ke.height,de.depth,Ee,ke.data)}else t.compressedTexImage3D(r.TEXTURE_2D_ARRAY,be,De,ke.width,ke.height,de.depth,0,ke.data,0,0);else console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else je?fe&&t.texSubImage3D(r.TEXTURE_2D_ARRAY,be,0,0,0,ke.width,ke.height,de.depth,Ee,Re,ke.data):t.texImage3D(r.TEXTURE_2D_ARRAY,be,De,ke.width,ke.height,de.depth,0,Ee,Re,ke.data)}else{je&&Ke&&t.texStorage2D(r.TEXTURE_2D,Le,De,at[0].width,at[0].height);for(let be=0,Ce=at.length;be0){let be=Op(de.width,de.height,L.format,L.type);for(let Ce of L.layerUpdates){let Be=de.data.subarray(Ce*be/de.data.BYTES_PER_ELEMENT,(Ce+1)*be/de.data.BYTES_PER_ELEMENT);t.texSubImage3D(r.TEXTURE_2D_ARRAY,0,0,0,Ce,de.width,de.height,1,Ee,Re,Be)}L.clearLayerUpdates()}else t.texSubImage3D(r.TEXTURE_2D_ARRAY,0,0,0,0,de.width,de.height,de.depth,Ee,Re,de.data)}else t.texImage3D(r.TEXTURE_2D_ARRAY,0,De,de.width,de.height,de.depth,0,Ee,Re,de.data);else if(L.isData3DTexture)je?(Ke&&t.texStorage3D(r.TEXTURE_3D,Le,De,de.width,de.height,de.depth),fe&&t.texSubImage3D(r.TEXTURE_3D,0,0,0,0,de.width,de.height,de.depth,Ee,Re,de.data)):t.texImage3D(r.TEXTURE_3D,0,De,de.width,de.height,de.depth,0,Ee,Re,de.data);else if(L.isFramebufferTexture){if(Ke)if(je)t.texStorage2D(r.TEXTURE_2D,Le,De,de.width,de.height);else{let be=de.width,Ce=de.height;for(let Be=0;Be>=1,Ce>>=1}}else if(at.length>0){if(je&&Ke){let be=ve(at[0]);t.texStorage2D(r.TEXTURE_2D,Le,De,be.width,be.height)}for(let be=0,Ce=at.length;be0&&Le++;let Ce=ve(Ee[0]);t.texStorage2D(r.TEXTURE_CUBE_MAP,Le,at,Ce.width,Ce.height)}for(let Ce=0;Ce<6;Ce++)if(de){je?fe&&t.texSubImage2D(r.TEXTURE_CUBE_MAP_POSITIVE_X+Ce,0,0,0,Ee[Ce].width,Ee[Ce].height,De,ke,Ee[Ce].data):t.texImage2D(r.TEXTURE_CUBE_MAP_POSITIVE_X+Ce,0,at,Ee[Ce].width,Ee[Ce].height,0,De,ke,Ee[Ce].data);for(let Be=0;Be>Q),Re=Math.max(1,L.height>>Q);z===r.TEXTURE_3D||z===r.TEXTURE_2D_ARRAY?t.texImage3D(z,Q,he,Ee,Re,L.depth,0,ne,oe,null):t.texImage2D(z,Q,he,Ee,Re,0,ne,oe,null)}t.bindFramebuffer(r.FRAMEBUFFER,H),ue(L)?a.framebufferTexture2DMultisampleEXT(r.FRAMEBUFFER,D,z,de.__webglTexture,0,pe(L)):(z===r.TEXTURE_2D||z>=r.TEXTURE_CUBE_MAP_POSITIVE_X&&z<=r.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&r.framebufferTexture2D(r.FRAMEBUFFER,D,z,de.__webglTexture,Q),t.bindFramebuffer(r.FRAMEBUFFER,null)}function T(H,L,O){if(r.bindRenderbuffer(r.RENDERBUFFER,H),L.depthBuffer){let D=L.depthTexture,z=D&&D.isDepthTexture?D.type:null,Q=x(L.stencilBuffer,z),ne=L.stencilBuffer?r.DEPTH_STENCIL_ATTACHMENT:r.DEPTH_ATTACHMENT,oe=pe(L);ue(L)?a.renderbufferStorageMultisampleEXT(r.RENDERBUFFER,oe,Q,L.width,L.height):O?r.renderbufferStorageMultisample(r.RENDERBUFFER,oe,Q,L.width,L.height):r.renderbufferStorage(r.RENDERBUFFER,Q,L.width,L.height),r.framebufferRenderbuffer(r.FRAMEBUFFER,ne,r.RENDERBUFFER,H)}else{let D=L.textures;for(let z=0;z{delete L.__boundDepthTexture,delete L.__depthDisposeCallback,D.removeEventListener("dispose",z)};D.addEventListener("dispose",z),L.__depthDisposeCallback=z}L.__boundDepthTexture=D}if(H.depthTexture&&!L.__autoAllocateDepthBuffer){if(O)throw new Error("target.depthTexture not supported in Cube render targets");let D=H.texture.mipmaps;D&&D.length>0?k(L.__webglFramebuffer[0],H):k(L.__webglFramebuffer,H)}else if(O){L.__webglDepthbuffer=[];for(let D=0;D<6;D++)if(t.bindFramebuffer(r.FRAMEBUFFER,L.__webglFramebuffer[D]),L.__webglDepthbuffer[D]===void 0)L.__webglDepthbuffer[D]=r.createRenderbuffer(),T(L.__webglDepthbuffer[D],H,!1);else{let z=H.stencilBuffer?r.DEPTH_STENCIL_ATTACHMENT:r.DEPTH_ATTACHMENT,Q=L.__webglDepthbuffer[D];r.bindRenderbuffer(r.RENDERBUFFER,Q),r.framebufferRenderbuffer(r.FRAMEBUFFER,z,r.RENDERBUFFER,Q)}}else{let D=H.texture.mipmaps;if(D&&D.length>0?t.bindFramebuffer(r.FRAMEBUFFER,L.__webglFramebuffer[0]):t.bindFramebuffer(r.FRAMEBUFFER,L.__webglFramebuffer),L.__webglDepthbuffer===void 0)L.__webglDepthbuffer=r.createRenderbuffer(),T(L.__webglDepthbuffer,H,!1);else{let z=H.stencilBuffer?r.DEPTH_STENCIL_ATTACHMENT:r.DEPTH_ATTACHMENT,Q=L.__webglDepthbuffer;r.bindRenderbuffer(r.RENDERBUFFER,Q),r.framebufferRenderbuffer(r.FRAMEBUFFER,z,r.RENDERBUFFER,Q)}}t.bindFramebuffer(r.FRAMEBUFFER,null)}function W(H,L,O){let D=n.get(H);L!==void 0&&M(D.__webglFramebuffer,H,H.texture,r.COLOR_ATTACHMENT0,r.TEXTURE_2D,0),O!==void 0&&j(H)}function re(H){let L=H.texture,O=n.get(H),D=n.get(L);H.addEventListener("dispose",A);let z=H.textures,Q=H.isWebGLCubeRenderTarget===!0,ne=z.length>1;if(ne||(D.__webglTexture===void 0&&(D.__webglTexture=r.createTexture()),D.__version=L.version,o.memory.textures++),Q){O.__webglFramebuffer=[];for(let oe=0;oe<6;oe++)if(L.mipmaps&&L.mipmaps.length>0){O.__webglFramebuffer[oe]=[];for(let he=0;he0){O.__webglFramebuffer=[];for(let oe=0;oe0&&ue(H)===!1){O.__webglMultisampledFramebuffer=r.createFramebuffer(),O.__webglColorRenderbuffer=[],t.bindFramebuffer(r.FRAMEBUFFER,O.__webglMultisampledFramebuffer);for(let oe=0;oe0)for(let he=0;he0)for(let he=0;he0){if(ue(H)===!1){let L=H.textures,O=H.width,D=H.height,z=r.COLOR_BUFFER_BIT,Q=H.stencilBuffer?r.DEPTH_STENCIL_ATTACHMENT:r.DEPTH_ATTACHMENT,ne=n.get(H),oe=L.length>1;if(oe)for(let ge=0;ge0?t.bindFramebuffer(r.DRAW_FRAMEBUFFER,ne.__webglFramebuffer[0]):t.bindFramebuffer(r.DRAW_FRAMEBUFFER,ne.__webglFramebuffer);for(let ge=0;ge0&&e.has("WEBGL_multisampled_render_to_texture")===!0&&L.__useRenderToTexture!==!1}function ee(H){let L=o.render.frame;u.get(H)!==L&&(u.set(H,L),H.update())}function $(H,L){let O=H.colorSpace,D=H.format,z=H.type;return H.isCompressedTexture===!0||H.isVideoTexture===!0||O!==Ci&&O!==cs&&(mt.getTransfer(O)===Kt?(D!==Pi||z!==lr)&&console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):console.error("THREE.WebGLTextures: Unsupported texture color space:",O)),L}function ve(H){return typeof HTMLImageElement<"u"&&H instanceof HTMLImageElement?(c.width=H.naturalWidth||H.width,c.height=H.naturalHeight||H.height):typeof VideoFrame<"u"&&H instanceof VideoFrame?(c.width=H.displayWidth,c.height=H.displayHeight):(c.width=H.width,c.height=H.height),c}this.allocateTextureUnit=B,this.resetTextureUnits=N,this.setTexture2D=ie,this.setTexture2DArray=Y,this.setTexture3D=te,this.setTextureCube=q,this.rebindTextures=W,this.setupRenderTarget=re,this.updateRenderTargetMipmap=ce,this.updateMultisampleRenderTarget=ae,this.setupDepthRenderbuffer=j,this.setupFrameBufferTexture=M,this.useMultisampledRTT=ue}function Fb(r,e){function t(n,i=cs){let s,o=mt.getTransfer(i);if(n===lr)return r.UNSIGNED_BYTE;if(n===Xh)return r.UNSIGNED_SHORT_4_4_4_4;if(n===qh)return r.UNSIGNED_SHORT_5_5_5_1;if(n===_p)return r.UNSIGNED_INT_5_9_9_9_REV;if(n===xp)return r.BYTE;if(n===vp)return r.SHORT;if(n===ka)return r.UNSIGNED_SHORT;if(n===Wh)return r.INT;if(n===Ls)return r.UNSIGNED_INT;if(n===cr)return r.FLOAT;if(n===Ba)return r.HALF_FLOAT;if(n===Tp)return r.ALPHA;if(n===Mp)return r.RGB;if(n===Pi)return r.RGBA;if(n===Sa)return r.DEPTH_COMPONENT;if(n===Va)return r.DEPTH_STENCIL;if(n===Ep)return r.RED;if(n===jh)return r.RED_INTEGER;if(n===bp)return r.RG;if(n===Yh)return r.RG_INTEGER;if(n===Kh)return r.RGBA_INTEGER;if(n===Cc||n===Ic||n===Rc||n===Pc)if(o===Kt)if(s=e.get("WEBGL_compressed_texture_s3tc_srgb"),s!==null){if(n===Cc)return s.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(n===Ic)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(n===Rc)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(n===Pc)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(s=e.get("WEBGL_compressed_texture_s3tc"),s!==null){if(n===Cc)return s.COMPRESSED_RGB_S3TC_DXT1_EXT;if(n===Ic)return s.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(n===Rc)return s.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(n===Pc)return s.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(n===Zh||n===Jh||n===$h||n===Qh)if(s=e.get("WEBGL_compressed_texture_pvrtc"),s!==null){if(n===Zh)return s.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(n===Jh)return s.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(n===$h)return s.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(n===Qh)return s.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(n===ef||n===tf||n===nf)if(s=e.get("WEBGL_compressed_texture_etc"),s!==null){if(n===ef||n===tf)return o===Kt?s.COMPRESSED_SRGB8_ETC2:s.COMPRESSED_RGB8_ETC2;if(n===nf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:s.COMPRESSED_RGBA8_ETC2_EAC}else return null;if(n===rf||n===sf||n===of||n===af||n===lf||n===cf||n===uf||n===hf||n===ff||n===df||n===pf||n===mf||n===gf||n===yf)if(s=e.get("WEBGL_compressed_texture_astc"),s!==null){if(n===rf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:s.COMPRESSED_RGBA_ASTC_4x4_KHR;if(n===sf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:s.COMPRESSED_RGBA_ASTC_5x4_KHR;if(n===of)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:s.COMPRESSED_RGBA_ASTC_5x5_KHR;if(n===af)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:s.COMPRESSED_RGBA_ASTC_6x5_KHR;if(n===lf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:s.COMPRESSED_RGBA_ASTC_6x6_KHR;if(n===cf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:s.COMPRESSED_RGBA_ASTC_8x5_KHR;if(n===uf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:s.COMPRESSED_RGBA_ASTC_8x6_KHR;if(n===hf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:s.COMPRESSED_RGBA_ASTC_8x8_KHR;if(n===ff)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:s.COMPRESSED_RGBA_ASTC_10x5_KHR;if(n===df)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:s.COMPRESSED_RGBA_ASTC_10x6_KHR;if(n===pf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:s.COMPRESSED_RGBA_ASTC_10x8_KHR;if(n===mf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:s.COMPRESSED_RGBA_ASTC_10x10_KHR;if(n===gf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:s.COMPRESSED_RGBA_ASTC_12x10_KHR;if(n===yf)return o===Kt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:s.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(n===Nc||n===xf||n===vf)if(s=e.get("EXT_texture_compression_bptc"),s!==null){if(n===Nc)return o===Kt?s.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:s.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(n===xf)return s.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(n===vf)return s.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(n===Sp||n===_f||n===Tf||n===Mf)if(s=e.get("EXT_texture_compression_rgtc"),s!==null){if(n===Nc)return s.COMPRESSED_RED_RGTC1_EXT;if(n===_f)return s.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(n===Tf)return s.COMPRESSED_RED_GREEN_RGTC2_EXT;if(n===Mf)return s.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return n===Ga?r.UNSIGNED_INT_24_8:r[n]!==void 0?r[n]:null}return{convert:t}}var Ub=` +void main() { + + gl_Position = vec4( position, 1.0 ); + +}`,kb=` +uniform sampler2DArray depthColor; +uniform float depthWidth; +uniform float depthHeight; + +void main() { + + vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); + + if ( coord.x >= 1.0 ) { + + gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; + + } else { + + gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; + + } + +}`,jp=class{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(e,t,n){if(this.texture===null){let i=new qn,s=e.properties.get(i);s.__webglTexture=t.texture,(t.depthNear!==n.depthNear||t.depthFar!==n.depthFar)&&(this.depthNear=t.depthNear,this.depthFar=t.depthFar),this.texture=i}}getMesh(e){if(this.texture!==null&&this.mesh===null){let t=e.cameras[0].viewport,n=new nr({vertexShader:Ub,fragmentShader:kb,uniforms:{depthColor:{value:this.texture},depthWidth:{value:t.z},depthHeight:{value:t.w}}});this.mesh=new Dt(new po(20,20),n)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}},Yp=class extends Qr{constructor(e,t){super();let n=this,i=null,s=1,o=null,a="local-floor",l=1,c=null,u=null,h=null,f=null,p=null,g=null,y=new jp,d=t.getContextAttributes(),m=null,_=null,v=[],x=[],S=new Pe,I=null,A=new nn;A.viewport=new wt;let C=new nn;C.viewport=new wt;let E=[A,C],b=new Lh,R=null,N=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(G){let P=v[G];return P===void 0&&(P=new Ra,v[G]=P),P.getTargetRaySpace()},this.getControllerGrip=function(G){let P=v[G];return P===void 0&&(P=new Ra,v[G]=P),P.getGripSpace()},this.getHand=function(G){let P=v[G];return P===void 0&&(P=new Ra,v[G]=P),P.getHandSpace()};function B(G){let P=x.indexOf(G.inputSource);if(P===-1)return;let M=v[P];M!==void 0&&(M.update(G.inputSource,G.frame,c||o),M.dispatchEvent({type:G.type,data:G.inputSource}))}function V(){i.removeEventListener("select",B),i.removeEventListener("selectstart",B),i.removeEventListener("selectend",B),i.removeEventListener("squeeze",B),i.removeEventListener("squeezestart",B),i.removeEventListener("squeezeend",B),i.removeEventListener("end",V),i.removeEventListener("inputsourceschange",ie);for(let G=0;G=0&&(x[T]=null,v[T].disconnect(M))}for(let P=0;P=x.length){x.push(M),T=j;break}else if(x[j]===null){x[j]=M,T=j;break}if(T===-1)break}let k=v[T];k&&k.connect(M)}}let Y=new se,te=new se;function q(G,P,M){Y.setFromMatrixPosition(P.matrixWorld),te.setFromMatrixPosition(M.matrixWorld);let T=Y.distanceTo(te),k=P.projectionMatrix.elements,j=M.projectionMatrix.elements,W=k[14]/(k[10]-1),re=k[14]/(k[10]+1),ce=(k[9]+1)/k[5],me=(k[9]-1)/k[5],w=(k[8]-1)/k[0],ae=(j[8]+1)/j[0],pe=W*w,ue=W*ae,ee=T/(-w+ae),$=ee*-w;if(P.matrixWorld.decompose(G.position,G.quaternion,G.scale),G.translateX($),G.translateZ(ee),G.matrixWorld.compose(G.position,G.quaternion,G.scale),G.matrixWorldInverse.copy(G.matrixWorld).invert(),k[10]===-1)G.projectionMatrix.copy(P.projectionMatrix),G.projectionMatrixInverse.copy(P.projectionMatrixInverse);else{let ve=W+ee,H=re+ee,L=pe-$,O=ue+(T-$),D=ce*re/H*ve,z=me*re/H*ve;G.projectionMatrix.makePerspective(L,O,D,z,ve,H),G.projectionMatrixInverse.copy(G.projectionMatrix).invert()}}function le(G,P){P===null?G.matrixWorld.copy(G.matrix):G.matrixWorld.multiplyMatrices(P.matrixWorld,G.matrix),G.matrixWorldInverse.copy(G.matrixWorld).invert()}this.updateCamera=function(G){if(i===null)return;let P=G.near,M=G.far;y.texture!==null&&(y.depthNear>0&&(P=y.depthNear),y.depthFar>0&&(M=y.depthFar)),b.near=C.near=A.near=P,b.far=C.far=A.far=M,(R!==b.near||N!==b.far)&&(i.updateRenderState({depthNear:b.near,depthFar:b.far}),R=b.near,N=b.far),A.layers.mask=G.layers.mask|2,C.layers.mask=G.layers.mask|4,b.layers.mask=A.layers.mask|C.layers.mask;let T=G.parent,k=b.cameras;le(b,T);for(let j=0;j0&&(d.alphaTest.value=m.alphaTest);let _=e.get(m),v=_.envMap,x=_.envMapRotation;v&&(d.envMap.value=v,bo.copy(x),bo.x*=-1,bo.y*=-1,bo.z*=-1,v.isCubeTexture&&v.isRenderTargetTexture===!1&&(bo.y*=-1,bo.z*=-1),d.envMapRotation.value.setFromMatrix4(Bb.makeRotationFromEuler(bo)),d.flipEnvMap.value=v.isCubeTexture&&v.isRenderTargetTexture===!1?-1:1,d.reflectivity.value=m.reflectivity,d.ior.value=m.ior,d.refractionRatio.value=m.refractionRatio),m.lightMap&&(d.lightMap.value=m.lightMap,d.lightMapIntensity.value=m.lightMapIntensity,t(m.lightMap,d.lightMapTransform)),m.aoMap&&(d.aoMap.value=m.aoMap,d.aoMapIntensity.value=m.aoMapIntensity,t(m.aoMap,d.aoMapTransform))}function o(d,m){d.diffuse.value.copy(m.color),d.opacity.value=m.opacity,m.map&&(d.map.value=m.map,t(m.map,d.mapTransform))}function a(d,m){d.dashSize.value=m.dashSize,d.totalSize.value=m.dashSize+m.gapSize,d.scale.value=m.scale}function l(d,m,_,v){d.diffuse.value.copy(m.color),d.opacity.value=m.opacity,d.size.value=m.size*_,d.scale.value=v*.5,m.map&&(d.map.value=m.map,t(m.map,d.uvTransform)),m.alphaMap&&(d.alphaMap.value=m.alphaMap,t(m.alphaMap,d.alphaMapTransform)),m.alphaTest>0&&(d.alphaTest.value=m.alphaTest)}function c(d,m){d.diffuse.value.copy(m.color),d.opacity.value=m.opacity,d.rotation.value=m.rotation,m.map&&(d.map.value=m.map,t(m.map,d.mapTransform)),m.alphaMap&&(d.alphaMap.value=m.alphaMap,t(m.alphaMap,d.alphaMapTransform)),m.alphaTest>0&&(d.alphaTest.value=m.alphaTest)}function u(d,m){d.specular.value.copy(m.specular),d.shininess.value=Math.max(m.shininess,1e-4)}function h(d,m){m.gradientMap&&(d.gradientMap.value=m.gradientMap)}function f(d,m){d.metalness.value=m.metalness,m.metalnessMap&&(d.metalnessMap.value=m.metalnessMap,t(m.metalnessMap,d.metalnessMapTransform)),d.roughness.value=m.roughness,m.roughnessMap&&(d.roughnessMap.value=m.roughnessMap,t(m.roughnessMap,d.roughnessMapTransform)),m.envMap&&(d.envMapIntensity.value=m.envMapIntensity)}function p(d,m,_){d.ior.value=m.ior,m.sheen>0&&(d.sheenColor.value.copy(m.sheenColor).multiplyScalar(m.sheen),d.sheenRoughness.value=m.sheenRoughness,m.sheenColorMap&&(d.sheenColorMap.value=m.sheenColorMap,t(m.sheenColorMap,d.sheenColorMapTransform)),m.sheenRoughnessMap&&(d.sheenRoughnessMap.value=m.sheenRoughnessMap,t(m.sheenRoughnessMap,d.sheenRoughnessMapTransform))),m.clearcoat>0&&(d.clearcoat.value=m.clearcoat,d.clearcoatRoughness.value=m.clearcoatRoughness,m.clearcoatMap&&(d.clearcoatMap.value=m.clearcoatMap,t(m.clearcoatMap,d.clearcoatMapTransform)),m.clearcoatRoughnessMap&&(d.clearcoatRoughnessMap.value=m.clearcoatRoughnessMap,t(m.clearcoatRoughnessMap,d.clearcoatRoughnessMapTransform)),m.clearcoatNormalMap&&(d.clearcoatNormalMap.value=m.clearcoatNormalMap,t(m.clearcoatNormalMap,d.clearcoatNormalMapTransform),d.clearcoatNormalScale.value.copy(m.clearcoatNormalScale),m.side===_n&&d.clearcoatNormalScale.value.negate())),m.dispersion>0&&(d.dispersion.value=m.dispersion),m.iridescence>0&&(d.iridescence.value=m.iridescence,d.iridescenceIOR.value=m.iridescenceIOR,d.iridescenceThicknessMinimum.value=m.iridescenceThicknessRange[0],d.iridescenceThicknessMaximum.value=m.iridescenceThicknessRange[1],m.iridescenceMap&&(d.iridescenceMap.value=m.iridescenceMap,t(m.iridescenceMap,d.iridescenceMapTransform)),m.iridescenceThicknessMap&&(d.iridescenceThicknessMap.value=m.iridescenceThicknessMap,t(m.iridescenceThicknessMap,d.iridescenceThicknessMapTransform))),m.transmission>0&&(d.transmission.value=m.transmission,d.transmissionSamplerMap.value=_.texture,d.transmissionSamplerSize.value.set(_.width,_.height),m.transmissionMap&&(d.transmissionMap.value=m.transmissionMap,t(m.transmissionMap,d.transmissionMapTransform)),d.thickness.value=m.thickness,m.thicknessMap&&(d.thicknessMap.value=m.thicknessMap,t(m.thicknessMap,d.thicknessMapTransform)),d.attenuationDistance.value=m.attenuationDistance,d.attenuationColor.value.copy(m.attenuationColor)),m.anisotropy>0&&(d.anisotropyVector.value.set(m.anisotropy*Math.cos(m.anisotropyRotation),m.anisotropy*Math.sin(m.anisotropyRotation)),m.anisotropyMap&&(d.anisotropyMap.value=m.anisotropyMap,t(m.anisotropyMap,d.anisotropyMapTransform))),d.specularIntensity.value=m.specularIntensity,d.specularColor.value.copy(m.specularColor),m.specularColorMap&&(d.specularColorMap.value=m.specularColorMap,t(m.specularColorMap,d.specularColorMapTransform)),m.specularIntensityMap&&(d.specularIntensityMap.value=m.specularIntensityMap,t(m.specularIntensityMap,d.specularIntensityMapTransform))}function g(d,m){m.matcap&&(d.matcap.value=m.matcap)}function y(d,m){let _=e.get(m).light;d.referencePosition.value.setFromMatrixPosition(_.matrixWorld),d.nearDistance.value=_.shadow.camera.near,d.farDistance.value=_.shadow.camera.far}return{refreshFogUniforms:n,refreshMaterialUniforms:i}}function Vb(r,e,t,n){let i={},s={},o=[],a=r.getParameter(r.MAX_UNIFORM_BUFFER_BINDINGS);function l(_,v){let x=v.program;n.uniformBlockBinding(_,x)}function c(_,v){let x=i[_.id];x===void 0&&(g(_),x=u(_),i[_.id]=x,_.addEventListener("dispose",d));let S=v.program;n.updateUBOMapping(_,S);let I=e.render.frame;s[_.id]!==I&&(f(_),s[_.id]=I)}function u(_){let v=h();_.__bindingPointIndex=v;let x=r.createBuffer(),S=_.__size,I=_.usage;return r.bindBuffer(r.UNIFORM_BUFFER,x),r.bufferData(r.UNIFORM_BUFFER,S,I),r.bindBuffer(r.UNIFORM_BUFFER,null),r.bindBufferBase(r.UNIFORM_BUFFER,v,x),x}function h(){for(let _=0;_0&&(x+=S-I),_.__size=x,_.__cache={},this}function y(_){let v={boundary:0,storage:0};return typeof _=="number"||typeof _=="boolean"?(v.boundary=4,v.storage=4):_.isVector2?(v.boundary=8,v.storage=8):_.isVector3||_.isColor?(v.boundary=16,v.storage=12):_.isVector4?(v.boundary=16,v.storage=16):_.isMatrix3?(v.boundary=48,v.storage=48):_.isMatrix4?(v.boundary=64,v.storage=64):_.isTexture?console.warn("THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group."):console.warn("THREE.WebGLRenderer: Unsupported uniform value type.",_),v}function d(_){let v=_.target;v.removeEventListener("dispose",d);let x=o.indexOf(v.__bindingPointIndex);o.splice(x,1),r.deleteBuffer(i[v.id]),delete i[v.id],delete s[v.id]}function m(){for(let _ in i)r.deleteBuffer(i[_]);o=[],i={},s={}}return{bind:l,update:c,dispose:m}}var Xa=class{constructor(e={}){let{canvas:t=$0(),context:n=null,depth:i=!0,stencil:s=!1,alpha:o=!1,antialias:a=!1,premultipliedAlpha:l=!0,preserveDrawingBuffer:c=!1,powerPreference:u="default",failIfMajorPerformanceCaveat:h=!1,reverseDepthBuffer:f=!1}=e;this.isWebGLRenderer=!0;let p;if(n!==null){if(typeof WebGLRenderingContext<"u"&&n instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");p=n.getContextAttributes().alpha}else p=o;let g=new Uint32Array(4),y=new Int32Array(4),d=null,m=null,_=[],v=[];this.domElement=t,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=ls,this.toneMappingExposure=1,this.transmissionResolutionScale=1;let x=this,S=!1;this._outputColorSpace=yt;let I=0,A=0,C=null,E=-1,b=null,R=new wt,N=new wt,B=null,V=new He(0),ie=0,Y=t.width,te=t.height,q=1,le=null,ye=null,Te=new wt(0,0,Y,te),Ae=new wt(0,0,Y,te),_e=!1,G=new Pa,P=!1,M=!1,T=new Je,k=new Je,j=new se,W=new wt,re={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0},ce=!1;function me(){return C===null?q:1}let w=n;function ae(K,xe){return t.getContext(K,xe)}try{let K={alpha:!0,depth:i,stencil:s,antialias:a,premultipliedAlpha:l,preserveDrawingBuffer:c,powerPreference:u,failIfMajorPerformanceCaveat:h};if("setAttribute"in t&&t.setAttribute("data-engine",`three.js r${Oh}`),t.addEventListener("webglcontextlost",Ce,!1),t.addEventListener("webglcontextrestored",Be,!1),t.addEventListener("webglcontextcreationerror",Oe,!1),w===null){let xe="webgl2";if(w=ae(xe,K),w===null)throw ae(xe)?new Error("Error creating WebGL context with your selected attributes."):new Error("Error creating WebGL context.")}}catch(K){throw console.error("THREE.WebGLRenderer: "+K.message),K}let pe,ue,ee,$,ve,H,L,O,D,z,Q,ne,oe,he,ge,de,Ee,Re,De,ke,at,je,Ke,fe;function Le(){pe=new sE(w),pe.init(),je=new Fb(w,pe),ue=new $M(w,pe,e,je),ee=new Ob(w,pe),ue.reverseDepthBuffer&&f&&ee.buffers.depth.setReversed(!0),$=new lE(w),ve=new Tb,H=new Db(w,pe,ee,ve,ue,je,$),L=new eE(x),O=new rE(x),D=new p_(w),Ke=new ZM(w,D),z=new oE(w,D,$,Ke),Q=new uE(w,z,D,$),De=new cE(w,ue,H),de=new QM(ve),ne=new _b(x,L,O,pe,ue,Ke,de),oe=new Gb(x,ve),he=new Eb,ge=new Ib(pe),Re=new KM(x,L,O,ee,Q,p,l),Ee=new Nb(x,Q,ue),fe=new Vb(w,$,ue,ee),ke=new JM(w,pe,$),at=new aE(w,pe,$),$.programs=ne.programs,x.capabilities=ue,x.extensions=pe,x.properties=ve,x.renderLists=he,x.shadowMap=Ee,x.state=ee,x.info=$}Le();let be=new Yp(x,w);this.xr=be,this.getContext=function(){return w},this.getContextAttributes=function(){return w.getContextAttributes()},this.forceContextLoss=function(){let K=pe.get("WEBGL_lose_context");K&&K.loseContext()},this.forceContextRestore=function(){let K=pe.get("WEBGL_lose_context");K&&K.restoreContext()},this.getPixelRatio=function(){return q},this.setPixelRatio=function(K){K!==void 0&&(q=K,this.setSize(Y,te,!1))},this.getSize=function(K){return K.set(Y,te)},this.setSize=function(K,xe,Se=!0){if(be.isPresenting){console.warn("THREE.WebGLRenderer: Can't change size while VR device is presenting.");return}Y=K,te=xe,t.width=Math.floor(K*q),t.height=Math.floor(xe*q),Se===!0&&(t.style.width=K+"px",t.style.height=xe+"px"),this.setViewport(0,0,K,xe)},this.getDrawingBufferSize=function(K){return K.set(Y*q,te*q).floor()},this.setDrawingBufferSize=function(K,xe,Se){Y=K,te=xe,q=Se,t.width=Math.floor(K*Se),t.height=Math.floor(xe*Se),this.setViewport(0,0,K,xe)},this.getCurrentViewport=function(K){return K.copy(R)},this.getViewport=function(K){return K.copy(Te)},this.setViewport=function(K,xe,Se,we){K.isVector4?Te.set(K.x,K.y,K.z,K.w):Te.set(K,xe,Se,we),ee.viewport(R.copy(Te).multiplyScalar(q).round())},this.getScissor=function(K){return K.copy(Ae)},this.setScissor=function(K,xe,Se,we){K.isVector4?Ae.set(K.x,K.y,K.z,K.w):Ae.set(K,xe,Se,we),ee.scissor(N.copy(Ae).multiplyScalar(q).round())},this.getScissorTest=function(){return _e},this.setScissorTest=function(K){ee.setScissorTest(_e=K)},this.setOpaqueSort=function(K){le=K},this.setTransparentSort=function(K){ye=K},this.getClearColor=function(K){return K.copy(Re.getClearColor())},this.setClearColor=function(){Re.setClearColor(...arguments)},this.getClearAlpha=function(){return Re.getClearAlpha()},this.setClearAlpha=function(){Re.setClearAlpha(...arguments)},this.clear=function(K=!0,xe=!0,Se=!0){let we=0;if(K){let Me=!1;if(C!==null){let Ge=C.texture.format;Me=Ge===Kh||Ge===Yh||Ge===jh}if(Me){let Ge=C.texture.type,Xe=Ge===lr||Ge===Ls||Ge===ka||Ge===Ga||Ge===Xh||Ge===qh,Qe=Re.getClearColor(),tt=Re.getClearAlpha(),Tt=Qe.r,xt=Qe.g,ut=Qe.b;Xe?(g[0]=Tt,g[1]=xt,g[2]=ut,g[3]=tt,w.clearBufferuiv(w.COLOR,0,g)):(y[0]=Tt,y[1]=xt,y[2]=ut,y[3]=tt,w.clearBufferiv(w.COLOR,0,y))}else we|=w.COLOR_BUFFER_BIT}xe&&(we|=w.DEPTH_BUFFER_BIT),Se&&(we|=w.STENCIL_BUFFER_BIT,this.state.buffers.stencil.setMask(4294967295)),w.clear(we)},this.clearColor=function(){this.clear(!0,!1,!1)},this.clearDepth=function(){this.clear(!1,!0,!1)},this.clearStencil=function(){this.clear(!1,!1,!0)},this.dispose=function(){t.removeEventListener("webglcontextlost",Ce,!1),t.removeEventListener("webglcontextrestored",Be,!1),t.removeEventListener("webglcontextcreationerror",Oe,!1),Re.dispose(),he.dispose(),ge.dispose(),ve.dispose(),L.dispose(),O.dispose(),Q.dispose(),Ke.dispose(),fe.dispose(),ne.dispose(),be.dispose(),be.removeEventListener("sessionstart",st),be.removeEventListener("sessionend",ct),Ft.stop()};function Ce(K){K.preventDefault(),console.log("THREE.WebGLRenderer: Context Lost."),S=!0}function Be(){console.log("THREE.WebGLRenderer: Context Restored."),S=!1;let K=$.autoReset,xe=Ee.enabled,Se=Ee.autoUpdate,we=Ee.needsUpdate,Me=Ee.type;Le(),$.autoReset=K,Ee.enabled=xe,Ee.autoUpdate=Se,Ee.needsUpdate=we,Ee.type=Me}function Oe(K){console.error("THREE.WebGLRenderer: A WebGL context could not be created. Reason: ",K.statusMessage)}function rt(K){let xe=K.target;xe.removeEventListener("dispose",rt),Et(xe)}function Et(K){ft(K),ve.remove(K)}function ft(K){let xe=ve.get(K).programs;xe!==void 0&&(xe.forEach(function(Se){ne.releaseProgram(Se)}),K.isShaderMaterial&&ne.releaseShaderCache(K))}this.renderBufferDirect=function(K,xe,Se,we,Me,Ge){xe===null&&(xe=re);let Xe=Me.isMesh&&Me.matrixWorld.determinant()<0,Qe=ad(K,xe,Se,we,Me);ee.setMaterial(we,Xe);let tt=Se.index,Tt=1;if(we.wireframe===!0){if(tt=z.getWireframeAttribute(Se),tt===void 0)return;Tt=2}let xt=Se.drawRange,ut=Se.attributes.position,At=xt.start*Tt,kt=(xt.start+xt.count)*Tt;Ge!==null&&(At=Math.max(At,Ge.start*Tt),kt=Math.min(kt,(Ge.start+Ge.count)*Tt)),tt!==null?(At=Math.max(At,0),kt=Math.min(kt,tt.count)):ut!=null&&(At=Math.max(At,0),kt=Math.min(kt,ut.count));let hn=kt-At;if(hn<0||hn===1/0)return;Ke.setup(Me,we,Qe,Se,tt);let ln,Ot=ke;if(tt!==null&&(ln=D.get(tt),Ot=at,Ot.setIndex(ln)),Me.isMesh)we.wireframe===!0?(ee.setLineWidth(we.wireframeLinewidth*me()),Ot.setMode(w.LINES)):Ot.setMode(w.TRIANGLES);else if(Me.isLine){let dt=we.linewidth;dt===void 0&&(dt=1),ee.setLineWidth(dt*me()),Me.isLineSegments?Ot.setMode(w.LINES):Me.isLineLoop?Ot.setMode(w.LINE_LOOP):Ot.setMode(w.LINE_STRIP)}else Me.isPoints?Ot.setMode(w.POINTS):Me.isSprite&&Ot.setMode(w.TRIANGLES);if(Me.isBatchedMesh)if(Me._multiDrawInstances!==null)Oc("THREE.WebGLRenderer: renderMultiDrawInstances has been deprecated and will be removed in r184. Append to renderMultiDraw arguments and use indirection."),Ot.renderMultiDrawInstances(Me._multiDrawStarts,Me._multiDrawCounts,Me._multiDrawCount,Me._multiDrawInstances);else if(pe.get("WEBGL_multi_draw"))Ot.renderMultiDraw(Me._multiDrawStarts,Me._multiDrawCounts,Me._multiDrawCount);else{let dt=Me._multiDrawStarts,wn=Me._multiDrawCounts,Bt=Me._multiDrawCount,ui=tt?D.get(tt).bytesPerElement:1,en=ve.get(we).currentProgram.getUniforms();for(let Hn=0;Hn{function Ge(){if(we.forEach(function(Xe){ve.get(Xe).currentProgram.isReady()&&we.delete(Xe)}),we.size===0){Me(K);return}setTimeout(Ge,10)}pe.get("KHR_parallel_shader_compile")!==null?Ge():setTimeout(Ge,10)})};let $e=null;function ze(K){$e&&$e(K)}function st(){Ft.stop()}function ct(){Ft.start()}let Ft=new Ly;Ft.setAnimationLoop(ze),typeof self<"u"&&Ft.setContext(self),this.setAnimationLoop=function(K){$e=K,be.setAnimationLoop(K),K===null?Ft.stop():Ft.start()},be.addEventListener("sessionstart",st),be.addEventListener("sessionend",ct),this.render=function(K,xe){if(xe!==void 0&&xe.isCamera!==!0){console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(S===!0)return;if(K.matrixWorldAutoUpdate===!0&&K.updateMatrixWorld(),xe.parent===null&&xe.matrixWorldAutoUpdate===!0&&xe.updateMatrixWorld(),be.enabled===!0&&be.isPresenting===!0&&(be.cameraAutoUpdate===!0&&be.updateCamera(xe),xe=be.getCamera()),K.isScene===!0&&K.onBeforeRender(x,K,xe,C),m=ge.get(K,v.length),m.init(xe),v.push(m),k.multiplyMatrices(xe.projectionMatrix,xe.matrixWorldInverse),G.setFromProjectionMatrix(k),M=this.localClippingEnabled,P=de.init(this.clippingPlanes,M),d=he.get(K,_.length),d.init(),_.push(d),be.enabled===!0&&be.isPresenting===!0){let Ge=x.xr.getDepthSensingMesh();Ge!==null&&ci(Ge,xe,-1/0,x.sortObjects)}ci(K,xe,0,x.sortObjects),d.finish(),x.sortObjects===!0&&d.sort(le,ye),ce=be.enabled===!1||be.isPresenting===!1||be.hasDepthSensing()===!1,ce&&Re.addToRenderList(d,K),this.info.render.frame++,P===!0&&de.beginShadows();let Se=m.state.shadowsArray;Ee.render(Se,K,xe),P===!0&&de.endShadows(),this.info.autoReset===!0&&this.info.reset();let we=d.opaque,Me=d.transmissive;if(m.setupLights(),xe.isArrayCamera){let Ge=xe.cameras;if(Me.length>0)for(let Xe=0,Qe=Ge.length;Xe0&&Dr(we,Me,K,xe),ce&&Re.render(K),Mi(d,K,xe);C!==null&&A===0&&(H.updateMultisampleRenderTarget(C),H.updateRenderTargetMipmap(C)),K.isScene===!0&&K.onAfterRender(x,K,xe),Ke.resetDefaultState(),E=-1,b=null,v.pop(),v.length>0?(m=v[v.length-1],P===!0&&de.setGlobalState(x.clippingPlanes,m.state.camera)):m=null,_.pop(),_.length>0?d=_[_.length-1]:d=null};function ci(K,xe,Se,we){if(K.visible===!1)return;if(K.layers.test(xe.layers)){if(K.isGroup)Se=K.renderOrder;else if(K.isLOD)K.autoUpdate===!0&&K.update(xe);else if(K.isLight)m.pushLight(K),K.castShadow&&m.pushShadow(K);else if(K.isSprite){if(!K.frustumCulled||G.intersectsSprite(K)){we&&W.setFromMatrixPosition(K.matrixWorld).applyMatrix4(k);let Xe=Q.update(K),Qe=K.material;Qe.visible&&d.push(K,Xe,Qe,Se,W.z,null)}}else if((K.isMesh||K.isLine||K.isPoints)&&(!K.frustumCulled||G.intersectsObject(K))){let Xe=Q.update(K),Qe=K.material;if(we&&(K.boundingSphere!==void 0?(K.boundingSphere===null&&K.computeBoundingSphere(),W.copy(K.boundingSphere.center)):(Xe.boundingSphere===null&&Xe.computeBoundingSphere(),W.copy(Xe.boundingSphere.center)),W.applyMatrix4(K.matrixWorld).applyMatrix4(k)),Array.isArray(Qe)){let tt=Xe.groups;for(let Tt=0,xt=tt.length;Tt0&&Po(Me,xe,Se),Ge.length>0&&Po(Ge,xe,Se),Xe.length>0&&Po(Xe,xe,Se),ee.buffers.depth.setTest(!0),ee.buffers.depth.setMask(!0),ee.buffers.color.setMask(!0),ee.setPolygonOffset(!1)}function Dr(K,xe,Se,we){if((Se.isScene===!0?Se.overrideMaterial:null)!==null)return;m.state.transmissionRenderTarget[we.id]===void 0&&(m.state.transmissionRenderTarget[we.id]=new wr(1,1,{generateMipmaps:!0,type:pe.has("EXT_color_buffer_half_float")||pe.has("EXT_color_buffer_float")?Ba:lr,minFilter:xi,samples:4,stencilBuffer:s,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:mt.workingColorSpace}));let Ge=m.state.transmissionRenderTarget[we.id],Xe=we.viewport||R;Ge.setSize(Xe.z*x.transmissionResolutionScale,Xe.w*x.transmissionResolutionScale);let Qe=x.getRenderTarget();x.setRenderTarget(Ge),x.getClearColor(V),ie=x.getClearAlpha(),ie<1&&x.setClearColor(16777215,.5),x.clear(),ce&&Re.render(Se);let tt=x.toneMapping;x.toneMapping=ls;let Tt=we.viewport;if(we.viewport!==void 0&&(we.viewport=void 0),m.setupLightsView(we),P===!0&&de.setGlobalState(x.clippingPlanes,we),Po(K,Se,we),H.updateMultisampleRenderTarget(Ge),H.updateRenderTargetMipmap(Ge),pe.has("WEBGL_multisampled_render_to_texture")===!1){let xt=!1;for(let ut=0,At=xe.length;ut0),ut=!!Se.morphAttributes.position,At=!!Se.morphAttributes.normal,kt=!!Se.morphAttributes.color,hn=ls;we.toneMapped&&(C===null||C.isXRRenderTarget===!0)&&(hn=x.toneMapping);let ln=Se.morphAttributes.position||Se.morphAttributes.normal||Se.morphAttributes.color,Ot=ln!==void 0?ln.length:0,dt=ve.get(we),wn=m.state.lights;if(P===!0&&(M===!0||K!==b)){let Mn=K===b&&we.id===E;de.setState(we,K,Mn)}let Bt=!1;we.version===dt.__version?(dt.needsLights&&dt.lightsStateVersion!==wn.state.version||dt.outputColorSpace!==Qe||Me.isBatchedMesh&&dt.batching===!1||!Me.isBatchedMesh&&dt.batching===!0||Me.isBatchedMesh&&dt.batchingColor===!0&&Me.colorTexture===null||Me.isBatchedMesh&&dt.batchingColor===!1&&Me.colorTexture!==null||Me.isInstancedMesh&&dt.instancing===!1||!Me.isInstancedMesh&&dt.instancing===!0||Me.isSkinnedMesh&&dt.skinning===!1||!Me.isSkinnedMesh&&dt.skinning===!0||Me.isInstancedMesh&&dt.instancingColor===!0&&Me.instanceColor===null||Me.isInstancedMesh&&dt.instancingColor===!1&&Me.instanceColor!==null||Me.isInstancedMesh&&dt.instancingMorph===!0&&Me.morphTexture===null||Me.isInstancedMesh&&dt.instancingMorph===!1&&Me.morphTexture!==null||dt.envMap!==tt||we.fog===!0&&dt.fog!==Ge||dt.numClippingPlanes!==void 0&&(dt.numClippingPlanes!==de.numPlanes||dt.numIntersection!==de.numIntersection)||dt.vertexAlphas!==Tt||dt.vertexTangents!==xt||dt.morphTargets!==ut||dt.morphNormals!==At||dt.morphColors!==kt||dt.toneMapping!==hn||dt.morphTargetsCount!==Ot)&&(Bt=!0):(Bt=!0,dt.__version=we.version);let ui=dt.currentProgram;Bt===!0&&(ui=No(we,xe,Me));let en=!1,Hn=!1,zs=!1,$t=ui.getUniforms(),ti=dt.uniforms;if(ee.useProgram(ui.program)&&(en=!0,Hn=!0,zs=!0),we.id!==E&&(E=we.id,Hn=!0),en||b!==K){ee.buffers.depth.getReversed()?(T.copy(K.projectionMatrix),ey(T),ty(T),$t.setValue(w,"projectionMatrix",T)):$t.setValue(w,"projectionMatrix",K.projectionMatrix),$t.setValue(w,"viewMatrix",K.matrixWorldInverse);let Cn=$t.map.cameraPosition;Cn!==void 0&&Cn.setValue(w,j.setFromMatrixPosition(K.matrixWorld)),ue.logarithmicDepthBuffer&&$t.setValue(w,"logDepthBufFC",2/(Math.log(K.far+1)/Math.LN2)),(we.isMeshPhongMaterial||we.isMeshToonMaterial||we.isMeshLambertMaterial||we.isMeshBasicMaterial||we.isMeshStandardMaterial||we.isShaderMaterial)&&$t.setValue(w,"isOrthographic",K.isOrthographicCamera===!0),b!==K&&(b=K,Hn=!0,zs=!0)}if(Me.isSkinnedMesh){$t.setOptional(w,Me,"bindMatrix"),$t.setOptional(w,Me,"bindMatrixInverse");let Mn=Me.skeleton;Mn&&(Mn.boneTexture===null&&Mn.computeBoneTexture(),$t.setValue(w,"boneTexture",Mn.boneTexture,H))}Me.isBatchedMesh&&($t.setOptional(w,Me,"batchingTexture"),$t.setValue(w,"batchingTexture",Me._matricesTexture,H),$t.setOptional(w,Me,"batchingIdTexture"),$t.setValue(w,"batchingIdTexture",Me._indirectTexture,H),$t.setOptional(w,Me,"batchingColorTexture"),Me._colorsTexture!==null&&$t.setValue(w,"batchingColorTexture",Me._colorsTexture,H));let Yn=Se.morphAttributes;if((Yn.position!==void 0||Yn.normal!==void 0||Yn.color!==void 0)&&De.update(Me,Se,ui),(Hn||dt.receiveShadow!==Me.receiveShadow)&&(dt.receiveShadow=Me.receiveShadow,$t.setValue(w,"receiveShadow",Me.receiveShadow)),we.isMeshGouraudMaterial&&we.envMap!==null&&(ti.envMap.value=tt,ti.flipEnvMap.value=tt.isCubeTexture&&tt.isRenderTargetTexture===!1?-1:1),we.isMeshStandardMaterial&&we.envMap===null&&xe.environment!==null&&(ti.envMapIntensity.value=xe.environmentIntensity),Hn&&($t.setValue(w,"toneMappingExposure",x.toneMappingExposure),dt.needsLights&&ld(ti,zs),Ge&&we.fog===!0&&oe.refreshFogUniforms(ti,Ge),oe.refreshMaterialUniforms(ti,we,q,te,m.state.transmissionRenderTarget[K.id]),Wa.upload(w,ou(dt),ti,H)),we.isShaderMaterial&&we.uniformsNeedUpdate===!0&&(Wa.upload(w,ou(dt),ti,H),we.uniformsNeedUpdate=!1),we.isSpriteMaterial&&$t.setValue(w,"center",Me.center),$t.setValue(w,"modelViewMatrix",Me.modelViewMatrix),$t.setValue(w,"normalMatrix",Me.normalMatrix),$t.setValue(w,"modelMatrix",Me.matrixWorld),we.isShaderMaterial||we.isRawShaderMaterial){let Mn=we.uniformsGroups;for(let Cn=0,Lo=Mn.length;Cn0&&H.useMultisampledRTT(K)===!1?Me=ve.get(K).__webglMultisampledFramebuffer:Array.isArray(xt)?Me=xt[Se]:Me=xt,R.copy(K.viewport),N.copy(K.scissor),B=K.scissorTest}else R.copy(Te).multiplyScalar(q).floor(),N.copy(Ae).multiplyScalar(q).floor(),B=_e;if(Se!==0&&(Me=ud),ee.bindFramebuffer(w.FRAMEBUFFER,Me)&&we&&ee.drawBuffers(K,Me),ee.viewport(R),ee.scissor(N),ee.setScissorTest(B),Ge){let tt=ve.get(K.texture);w.framebufferTexture2D(w.FRAMEBUFFER,w.COLOR_ATTACHMENT0,w.TEXTURE_CUBE_MAP_POSITIVE_X+xe,tt.__webglTexture,Se)}else if(Xe){let tt=ve.get(K.texture),Tt=xe;w.framebufferTextureLayer(w.FRAMEBUFFER,w.COLOR_ATTACHMENT0,tt.__webglTexture,Se,Tt)}else if(K!==null&&Se!==0){let tt=ve.get(K.texture);w.framebufferTexture2D(w.FRAMEBUFFER,w.COLOR_ATTACHMENT0,w.TEXTURE_2D,tt.__webglTexture,Se)}E=-1},this.readRenderTargetPixels=function(K,xe,Se,we,Me,Ge,Xe){if(!(K&&K.isWebGLRenderTarget)){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");return}let Qe=ve.get(K).__webglFramebuffer;if(K.isWebGLCubeRenderTarget&&Xe!==void 0&&(Qe=Qe[Xe]),Qe){ee.bindFramebuffer(w.FRAMEBUFFER,Qe);try{let tt=K.texture,Tt=tt.format,xt=tt.type;if(!ue.textureFormatReadable(Tt)){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!ue.textureTypeReadable(xt)){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}xe>=0&&xe<=K.width-we&&Se>=0&&Se<=K.height-Me&&w.readPixels(xe,Se,we,Me,je.convert(Tt),je.convert(xt),Ge)}finally{let tt=C!==null?ve.get(C).__webglFramebuffer:null;ee.bindFramebuffer(w.FRAMEBUFFER,tt)}}},this.readRenderTargetPixelsAsync=async function(K,xe,Se,we,Me,Ge,Xe){if(!(K&&K.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let Qe=ve.get(K).__webglFramebuffer;if(K.isWebGLCubeRenderTarget&&Xe!==void 0&&(Qe=Qe[Xe]),Qe)if(xe>=0&&xe<=K.width-we&&Se>=0&&Se<=K.height-Me){ee.bindFramebuffer(w.FRAMEBUFFER,Qe);let tt=K.texture,Tt=tt.format,xt=tt.type;if(!ue.textureFormatReadable(Tt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!ue.textureTypeReadable(xt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");let ut=w.createBuffer();w.bindBuffer(w.PIXEL_PACK_BUFFER,ut),w.bufferData(w.PIXEL_PACK_BUFFER,Ge.byteLength,w.STREAM_READ),w.readPixels(xe,Se,we,Me,je.convert(Tt),je.convert(xt),0);let At=C!==null?ve.get(C).__webglFramebuffer:null;ee.bindFramebuffer(w.FRAMEBUFFER,At);let kt=w.fenceSync(w.SYNC_GPU_COMMANDS_COMPLETE,0);return w.flush(),await Q0(w,kt,4),w.bindBuffer(w.PIXEL_PACK_BUFFER,ut),w.getBufferSubData(w.PIXEL_PACK_BUFFER,0,Ge),w.deleteBuffer(ut),w.deleteSync(kt),Ge}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(K,xe=null,Se=0){let we=Math.pow(2,-Se),Me=Math.floor(K.image.width*we),Ge=Math.floor(K.image.height*we),Xe=xe!==null?xe.x:0,Qe=xe!==null?xe.y:0;H.setTexture2D(K,0),w.copyTexSubImage2D(w.TEXTURE_2D,Se,0,0,Xe,Qe,Me,Ge),ee.unbindTexture()};let hd=w.createFramebuffer(),fd=w.createFramebuffer();this.copyTextureToTexture=function(K,xe,Se=null,we=null,Me=0,Ge=null){Ge===null&&(Me!==0?(Oc("WebGLRenderer: copyTextureToTexture function signature has changed to support src and dst mipmap levels."),Ge=Me,Me=0):Ge=0);let Xe,Qe,tt,Tt,xt,ut,At,kt,hn,ln=K.isCompressedTexture?K.mipmaps[Ge]:K.image;if(Se!==null)Xe=Se.max.x-Se.min.x,Qe=Se.max.y-Se.min.y,tt=Se.isBox3?Se.max.z-Se.min.z:1,Tt=Se.min.x,xt=Se.min.y,ut=Se.isBox3?Se.min.z:0;else{let Yn=Math.pow(2,-Me);Xe=Math.floor(ln.width*Yn),Qe=Math.floor(ln.height*Yn),K.isDataArrayTexture?tt=ln.depth:K.isData3DTexture?tt=Math.floor(ln.depth*Yn):tt=1,Tt=0,xt=0,ut=0}we!==null?(At=we.x,kt=we.y,hn=we.z):(At=0,kt=0,hn=0);let Ot=je.convert(xe.format),dt=je.convert(xe.type),wn;xe.isData3DTexture?(H.setTexture3D(xe,0),wn=w.TEXTURE_3D):xe.isDataArrayTexture||xe.isCompressedArrayTexture?(H.setTexture2DArray(xe,0),wn=w.TEXTURE_2D_ARRAY):(H.setTexture2D(xe,0),wn=w.TEXTURE_2D),w.pixelStorei(w.UNPACK_FLIP_Y_WEBGL,xe.flipY),w.pixelStorei(w.UNPACK_PREMULTIPLY_ALPHA_WEBGL,xe.premultiplyAlpha),w.pixelStorei(w.UNPACK_ALIGNMENT,xe.unpackAlignment);let Bt=w.getParameter(w.UNPACK_ROW_LENGTH),ui=w.getParameter(w.UNPACK_IMAGE_HEIGHT),en=w.getParameter(w.UNPACK_SKIP_PIXELS),Hn=w.getParameter(w.UNPACK_SKIP_ROWS),zs=w.getParameter(w.UNPACK_SKIP_IMAGES);w.pixelStorei(w.UNPACK_ROW_LENGTH,ln.width),w.pixelStorei(w.UNPACK_IMAGE_HEIGHT,ln.height),w.pixelStorei(w.UNPACK_SKIP_PIXELS,Tt),w.pixelStorei(w.UNPACK_SKIP_ROWS,xt),w.pixelStorei(w.UNPACK_SKIP_IMAGES,ut);let $t=K.isDataArrayTexture||K.isData3DTexture,ti=xe.isDataArrayTexture||xe.isData3DTexture;if(K.isDepthTexture){let Yn=ve.get(K),Mn=ve.get(xe),Cn=ve.get(Yn.__renderTarget),Lo=ve.get(Mn.__renderTarget);ee.bindFramebuffer(w.READ_FRAMEBUFFER,Cn.__webglFramebuffer),ee.bindFramebuffer(w.DRAW_FRAMEBUFFER,Lo.__webglFramebuffer);for(let Fr=0;Fr=t?vi.Phong:vi.Physical}var ja=class{Convert(e){return null}},Cf=class extends ja{Convert(e){return new He().copyLinearToSRGB(e)}},Ya=class extends ja{Convert(e){return new He().copySRGBToLinear(e)}};function Fc(r){return Ei(r.r,r.g,r.b)}function Dn(r){return new He(r.r/255,r.g/255,r.b/255)}function ur(r,e,t){let n=new tn,i=r.attributes.position.array,s=r.attributes.position.itemSize||3;for(let u=0;u{if(e.isMesh||e.isLineSegments){if(Array.isArray(e.material))for(let t of e.material)t.dispose();else e.material.dispose();e.userData=null,e.geometry.dispose()}})}function Lf(r,e,t,n,i){function s(c,u,h,f,p,g){let y=new se(p[3*g],p[3*g+1],p[3*g+2]);y.applyMatrix4(f.matrixWorld);let d=y.project(c);return new Nt((d.x+1)*u/2,-(d.y-1)*h/2)}let o=n.geometry.attributes.position.array,a=o.length/6,l=1/0;for(let c=0;c{i.IsEmpty()&&n.push(i)});for(let i=0;i=o)}function i(s,o){function a(l,c,u){return!(!n(u.v0,c.VertexCount())||!n(u.v1,c.VertexCount())||!n(u.v2,c.VertexCount())||u.HasVertexColors()&&(!n(u.c0,c.VertexColorCount())||!n(u.c1,c.VertexColorCount())||!n(u.c2,c.VertexColorCount()))||!n(u.n0,c.NormalCount())||!n(u.n1,c.NormalCount())||!n(u.n2,c.NormalCount())||u.HasTextureUVs()&&(!n(u.u0,c.TextureUVCount())||!n(u.u1,c.TextureUVCount())||!n(u.u2,c.TextureUVCount()))||!n(u.mat,l.MaterialCount())||!t(u.curve))}for(let l=0;l{this.CreateResult(i)})}Clear(){this.name=null,this.extension=null,this.callbacks=null,this.model=null,this.error=null,this.message=null,this.ClearContent()}CreateResult(e){if(this.error){e.onError(),e.onComplete();return}if(ca(this.model)){this.SetError(nt("The model doesn't contain any meshes.")),e.onError(),e.onComplete();return}Of(this.model,{defaultLineMaterialColor:this.callbacks.getDefaultLineMaterialColor(),defaultMaterialColor:this.callbacks.getDefaultMaterialColor()}),e.onSuccess(),e.onComplete()}CanImportExtension(e){return!1}GetUpDirection(){return _t.Z}ClearContent(){}ResetContent(){}ImportContent(e,t){}GetModel(){return this.model}SetError(e){this.error=!0,e!=null&&(this.message=e)}WasError(){return this.error}GetErrorMessage(){return this.message}};var hs=class r{constructor(e){this.vertices=e,this.mat=null}HasVertices(){return this.vertices!==null&&this.vertices.length>=2}GetVertices(){return this.vertices}SetMaterial(e){return this.mat=e,this}SegmentCount(){return this.vertices===null?0:this.vertices.length-1}Clone(){let e=new r([...this.vertices]);return e.SetMaterial(this.mat),e}};var Ka=class extends Jt{constructor(){super(),this.rhino=null}CanImportExtension(e){return e==="3dm"}GetUpDirection(){return _t.Z}ClearContent(){this.instanceIdToObject=null,this.instanceIdToDefinition=null}ResetContent(){this.instanceIdToObject=new Map,this.instanceIdToDefinition=new Map}ImportContent(e,t){this.rhino===null?_r("rhino3dm").then(()=>{rhino3dm().then(n=>{this.rhino=n,this.ImportRhinoContent(e),t()})}).catch(()=>{this.SetError(nt("Failed to load rhino3dm.")),t()}):(this.ImportRhinoContent(e),t())}ImportRhinoContent(e){let t=this.rhino.File3dm.fromByteArray(e);if(t===null){this.SetError(nt("Failed to read Rhino file."));return}this.ImportRhinoDocument(t),ca(this.model)&&this.SetError(nt("The model doesn't contain any 3D meshes. Try to save the model while you are in shaded view in Rhino."))}ImportRhinoDocument(e){this.InitRhinoInstances(e),this.ImportRhinoUserStrings(e),this.ImportRhinoGeometry(e)}InitRhinoInstances(e){let t=e.objects();for(let i=0;i0){let n=new Kn(nt("Document user texts"));for(let i=0;i0){let u=new Kn(nt("User texts"));for(let h=0;h=0;f--){let y=i[f].geometry().xform.toFloatArray(!1),d=new xn(y);u=u.MultiplyMatrix(d)}let h=new pn(u);Mr(a,h)}this.model.AddMeshToRootNode(a)}GetMaterialIndex(e,t,n){function i(l,c,u){let h=c.attributes();if(h.materialSource===l.ObjectMaterialSource.MaterialFromObject){let f=h.materialIndex;if(f>-1)return e.materials().get(f)}else if(h.materialSource===l.ObjectMaterialSource.MaterialFromLayer){let f=h.layerIndex;if(f>-1){let p=e.layers().get(f),g=p.renderMaterialIndex;if(g>-1)return e.materials().get(g);if(c.geometry().objectType===l.ObjectType.Curve){let d=new l.Material;return d.name=p.name,d.diffuseColor=p.color,d}}}else if(h.materialSource===l.ObjectMaterialSource.MaterialFromParent&&u.length!==0)return i(l,u[0],[]);return null}function s(l,c){function u(d,m){d.Set(m.r,m.g,m.b)}function h(d){return d.r===0&&d.g===0&&d.b===0}function f(d){return d.r===255&&d.g===255&&d.b===255}let p=null,g=l.physicallyBased();g.supported?(p=new ys,p.metalness=g.metallic?1:0,p.roughness=g.roughness):(p=new En,u(p.ambient,l.ambientColor),u(p.specular,l.specularColor)),p.name=l.name,u(p.color,l.diffuseColor),p.opacity=1-l.transparency,pi(p),h(p.color)&&!f(l.reflectionColor)&&u(p.color,l.reflectionColor),h(p.color)&&!f(l.transparentColor)&&u(p.color,l.transparentColor);let y=l.getBitmapTexture();if(y){let d=new fi,m=Rn(y.fileName),_=c.getFileBuffer(m);d.name=m,d.buffer=_,p.diffuseMap=d}return p}function o(l,c,u){let h=s(c,u);for(let f=0;f=this.arrayBuffer.byteLength}ReadArrayBuffer(e){let t=new Uint8Array(this.arrayBuffer),n=new ArrayBuffer(e),i=new Uint8Array(n),s=t.subarray(this.position,this.position+e);return i.set(s,0),this.position+=e,n}ReadBoolean8(){let e=this.dataView.getInt8(this.position);return this.position=this.position+1,!!e}ReadCharacter8(){let e=this.dataView.getInt8(this.position);return this.position=this.position+1,e}ReadUnsignedCharacter8(){let e=this.dataView.getUint8(this.position);return this.position=this.position+1,e}ReadInteger16(){let e=this.dataView.getInt16(this.position,this.isLittleEndian);return this.position=this.position+2,e}ReadUnsignedInteger16(){let e=this.dataView.getUint16(this.position,this.isLittleEndian);return this.position=this.position+2,e}ReadInteger32(){let e=this.dataView.getInt32(this.position,this.isLittleEndian);return this.position=this.position+4,e}ReadUnsignedInteger32(){let e=this.dataView.getUint32(this.position,this.isLittleEndian);return this.position=this.position+4,e}ReadFloat32(){let e=this.dataView.getFloat32(this.position,this.isLittleEndian);return this.position=this.position+4,e}ReadDouble64(){let e=this.dataView.getFloat64(this.position,this.isLittleEndian);return this.position=this.position+8,e}};var Mt={MAIN3DS:19789,EDIT3DS:15677,EDIT_MATERIAL:45055,MAT_NAME:40960,MAT_AMBIENT:40976,MAT_DIFFUSE:40992,MAT_SPECULAR:41008,MAT_SHININESS:41024,MAT_SHININESS_STRENGTH:41025,MAT_TRANSPARENCY:41040,MAT_COLOR_F:16,MAT_COLOR:17,MAT_LIN_COLOR:18,MAT_LIN_COLOR_F:19,MAT_TEXMAP:41472,MAT_TEXMAP_NAME:41728,MAT_TEXMAP_UOFFSET:41816,MAT_TEXMAP_VOFFSET:41818,MAT_TEXMAP_USCALE:41812,MAT_TEXMAP_VSCALE:41814,MAT_TEXMAP_ROTATION:41820,PERCENTAGE:48,PERCENTAGE_F:49,EDIT_OBJECT:16384,OBJ_TRIMESH:16640,OBJ_LIGHT:17920,OBJ_CAMERA:18176,TRI_VERTEX:16656,TRI_TEXVERTEX:16704,TRI_FACE:16672,TRI_TRANSFORMATION:16736,TRI_MATERIAL:16688,TRI_SMOOTH:16720,KF3DS:45056,OBJECT_NODE:45058,OBJECT_HIERARCHY:45072,OBJECT_INSTANCE_NAME:45073,OBJECT_PIVOT:45075,OBJECT_POSITION:45088,OBJECT_ROTATION:45089,OBJECT_SCALE:45090,OBJECT_ID:45104},$p=class{constructor(){this.id=-1,this.name="",this.flags=-1,this.parentId=-1,this.instanceName="",this.pivot=[0,0,0],this.positions=[],this.rotations=[],this.scales=[]}},Qp=class{constructor(){this.nodes=[],this.nodeIdToNode=new Map}IsEmpty(){return this.nodes.length===0}AddNode(e){this.nodes.push(e),this.nodeIdToNode.set(e.nodeId,e)}GetNodes(){return this.nodes}},Za=class extends Jt{constructor(){super()}CanImportExtension(e){return e==="3ds"}GetUpDirection(){return _t.Z}ClearContent(){this.materialNameToIndex=null,this.meshNameToIndex=null,this.nodeList=null}ResetContent(){this.materialNameToIndex=new Map,this.meshNameToIndex=new Map,this.nodeList=new Qp}ImportContent(e,t){this.ProcessBinary(e),t()}ProcessBinary(e){let t=new oi(e,!0),n=t.GetByteLength();this.ReadChunks(t,n,(i,s)=>{i===Mt.MAIN3DS?this.ReadMainChunk(t,s):this.SkipChunk(t,s)})}ReadMainChunk(e,t){let n=this.GetChunkEnd(e,t);this.ReadChunks(e,n,(i,s)=>{i===Mt.EDIT3DS?this.ReadEditorChunk(e,s):i===Mt.KF3DS?this.ReadKeyFrameChunk(e,s):this.SkipChunk(e,s)}),this.BuildNodeHierarchy()}ReadEditorChunk(e,t){let n=this.GetChunkEnd(e,t);this.ReadChunks(e,n,(i,s)=>{i===Mt.EDIT_MATERIAL?this.ReadMaterialChunk(e,s):i===Mt.EDIT_OBJECT?this.ReadObjectChunk(e,s):this.SkipChunk(e,s)})}ReadMaterialChunk(e,t){let n=new En,i=this.GetChunkEnd(e,t),s=null,o=null;this.ReadChunks(e,i,(l,c)=>{l===Mt.MAT_NAME?n.name=this.ReadName(e):l===Mt.MAT_AMBIENT?n.ambient=this.ReadColorChunk(e,c):l===Mt.MAT_DIFFUSE?n.color=this.ReadColorChunk(e,c):l===Mt.MAT_SPECULAR?n.specular=this.ReadColorChunk(e,c):l===Mt.MAT_SHININESS?s=this.ReadPercentageChunk(e,c):l===Mt.MAT_SHININESS_STRENGTH?o=this.ReadPercentageChunk(e,c):l===Mt.MAT_TRANSPARENCY?(n.opacity=1-this.ReadPercentageChunk(e,c),pi(n)):l===Mt.MAT_TEXMAP?(n.diffuseMap=this.ReadTextureMapChunk(e,c),pi(n)):this.SkipChunk(e,c)}),s!==null&&o!==null&&(n.shininess=s*o/10);let a=this.model.AddMaterial(n);this.materialNameToIndex.set(n.name,a)}ReadTextureMapChunk(e,t){let n=new fi,i=this.GetChunkEnd(e,t);return this.ReadChunks(e,i,(s,o)=>{if(s===Mt.MAT_TEXMAP_NAME){let a=this.ReadName(e),l=this.callbacks.getFileBuffer(a);n.name=a,n.buffer=l}else s===Mt.MAT_TEXMAP_UOFFSET?n.offset.x=e.ReadFloat32():s===Mt.MAT_TEXMAP_VOFFSET?n.offset.y=e.ReadFloat32():s===Mt.MAT_TEXMAP_USCALE?n.scale.x=e.ReadFloat32():s===Mt.MAT_TEXMAP_VSCALE?n.scale.y=e.ReadFloat32():s===Mt.MAT_TEXMAP_ROTATION?n.rotation=e.ReadFloat32()*xr:this.SkipChunk(e,o)}),n}ReadColorChunk(e,t){let n=new St(0,0,0),i=this.GetChunkEnd(e,t),s=!1;return this.ReadChunks(e,i,(o,a)=>{o===Mt.MAT_COLOR?s||(n.r=e.ReadUnsignedCharacter8(),n.g=e.ReadUnsignedCharacter8(),n.b=e.ReadUnsignedCharacter8()):o===Mt.MAT_LIN_COLOR?(n.r=e.ReadUnsignedCharacter8(),n.g=e.ReadUnsignedCharacter8(),n.b=e.ReadUnsignedCharacter8(),s=!0):o===Mt.MAT_COLOR_F?s||(n.r=Fn(e.ReadFloat32()),n.g=Fn(e.ReadFloat32()),n.b=Fn(e.ReadFloat32())):o===Mt.MAT_LIN_COLOR_F?(n.r=Fn(e.ReadFloat32()),n.g=Fn(e.ReadFloat32()),n.b=Fn(e.ReadFloat32()),s=!0):this.SkipChunk(e,a)}),n}ReadPercentageChunk(e,t){let n=0,i=this.GetChunkEnd(e,t);return this.ReadChunks(e,i,(s,o)=>{s===Mt.PERCENTAGE?n=e.ReadUnsignedInteger16()/100:s===Mt.PERCENTAGE_F?n=e.ReadFloat32():this.SkipChunk(e,o)}),n}ReadObjectChunk(e,t){let n=this.GetChunkEnd(e,t),i=this.ReadName(e);this.ReadChunks(e,n,(s,o)=>{s===Mt.OBJ_TRIMESH?this.ReadMeshChunk(e,o,i):this.SkipChunk(e,o)})}ReadMeshChunk(e,t,n){function i(u,h){if(!h.IsValid())return;let f=h.Determinant(),p=Br(f);p&&(h=new xn().CreateScale(-1,1,1).MultiplyMatrix(h));let g=h.Invert();if(g===null)return;let y=new pn(g);Mr(u,y),p&&Ru(u)}let s=new tn;s.SetName(n);let o=this.GetChunkEnd(e,t),a=null;if(this.ReadChunks(e,o,(u,h)=>{u===Mt.TRI_VERTEX?this.ReadVerticesChunk(s,e):u===Mt.TRI_TEXVERTEX?this.ReadTextureVerticesChunk(s,e):u===Mt.TRI_FACE?this.ReadFacesChunk(s,e,h):u===Mt.TRI_TRANSFORMATION?a=this.ReadTransformationChunk(e):this.SkipChunk(e,h)}),s.VertexCount()===s.TextureUVCount())for(let u=0;u{o===Mt.TRI_MATERIAL?this.ReadFaceMaterialsChunk(e,t):o===Mt.TRI_SMOOTH?this.ReadFaceSmoothingGroupsChunk(e,s,t):this.SkipChunk(t,a)})}ReadFaceMaterialsChunk(e,t){let n=this.ReadName(t),i=this.materialNameToIndex.get(n),s=t.ReadUnsignedInteger16();for(let o=0;o{i===Mt.OBJECT_NODE?this.ReadObjectNodeChunk(e,s):this.SkipChunk(e,s)})}BuildNodeHierarchy(){function e(n,i){function s(c){return c.positions.length===0?[0,0,0]:c.positions[0]}function o(c){function u(f){let p=[0,0,0,1],g=Math.sqrt(f[0]*f[0]+f[1]*f[1]+f[2]*f[2]);if(g>0){let y=f[3]*-.5,d=Math.sin(y)/g;p=[d*f[0],d*f[1],d*f[2],Math.cos(y)]}return p}if(c.rotations.length===0)return[0,0,0,1];let h=c.rotations[0];return u(h)}function a(c){return c.scales.length===0?[1,1,1]:c.scales[0]}let l=new xn;if(l.ComposeTRS(Ui(s(n)),Zo(o(n)),Ui(a(n))),i){let c=n.pivot;l=new xn().CreateTranslation(-c[0],-c[1],-c[2]).MultiplyMatrix(l)}return new pn(l)}let t=this.model.GetRootNode();if(this.nodeList.IsEmpty())for(let n=0;n0&&i.name!=="$$$DUMMY"&&(s.SetName(i.name),i.instanceName.length>0&&s.SetName(s.GetName()+" "+i.instanceName)),i.parentId===65535||!n.has(i.parentId)?t.AddChildNode(s):n.get(i.parentId).AddChildNode(s),n.set(i.id,s);let o=this.meshNameToIndex.has(i.name);s.SetTransformation(e(i,o)),o&&s.AddMeshIndex(this.meshNameToIndex.get(i.name))}}}ReadObjectNodeChunk(e,t){function n(o,a,l){let c=[];a.Skip(10);let u=a.ReadInteger32();for(let h=0;h{o===Mt.OBJECT_HIERARCHY?(i.name=this.ReadName(e),i.flags=e.ReadUnsignedInteger32(),i.parentId=e.ReadUnsignedInteger16()):o===Mt.OBJECT_INSTANCE_NAME?i.instanceName=this.ReadName(e):o===Mt.OBJECT_PIVOT?i.pivot=this.ReadVector(e):o===Mt.OBJECT_POSITION?i.positions=n(this,e,Mt.OBJECT_POSITION):o===Mt.OBJECT_ROTATION?i.rotations=n(this,e,Mt.OBJECT_ROTATION):o===Mt.OBJECT_SCALE?i.scales=n(this,e,Mt.OBJECT_SCALE):o===Mt.OBJECT_ID?i.id=e.ReadUnsignedInteger16():this.SkipChunk(e,a)}),this.nodeList.AddNode(i)}ReadName(e){let t="",n=0,i=0;for(;i<64&&(n=e.ReadCharacter8(),n!==0);)t=t+String.fromCharCode(n),i=i+1;return t}ReadVector(e){return[e.ReadFloat32(),e.ReadFloat32(),e.ReadFloat32()]}ReadChunks(e,t,n){for(;e.GetPosition()<=t-6;){let i=e.ReadUnsignedInteger16(),s=e.ReadUnsignedInteger32();n(i,s)}}GetChunkEnd(e,t){return e.GetPosition()+t-6}SkipChunk(e,t){e.Skip(t-6)}};var _i={BYTE:5120,UNSIGNED_BYTE:5121,SHORT:5122,UNSIGNED_SHORT:5123,UNSIGNED_INT:5125,FLOAT:5126},hr={SCALAR:0,VEC2:1,VEC3:2,VEC4:3,MAT2:4,MAT3:5,MAT4:6},wo={POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6},em={GLTF_STRING:1179937895,JSON_CHUNK_TYPE:1313821514,BINARY_CHUNK_TYPE:5130562};function Uc(r){return Ei(Xs(r[0]),Xs(r[1]),Xs(r[2]))}function zb(r,e){function t(n,i){let s=n;return i===_i.UNSIGNED_BYTE?s/=255:i===_i.UNSIGNED_SHORT&&(s/=65535),Fn(Xs(s))}return new St(t(r[0],e),t(r[1],e),t(r[2],e))}var tm=class{constructor(e){this.reader=new oi(e,!0),this.componentType=null,this.dataType=null,this.byteStride=null,this.dataCount=null,this.sparseReader=null}SetComponentType(e){this.componentType=e}SetDataType(e){e==="SCALAR"?this.dataType=hr.SCALAR:e==="VEC2"?this.dataType=hr.VEC2:e==="VEC3"?this.dataType=hr.VEC3:e==="VEC4"?this.dataType=hr.VEC4:e==="MAT2"?this.dataType=hr.MAT2:e==="MAT3"?this.dataType=hr.MAT3:e==="MAT4"&&(this.dataType=hr.MAT4)}SetByteStride(e){this.byteStride=e}SetDataCount(e){this.dataCount=e}SetSparseReader(e,t){this.sparseReader={indexReader:e,valueReader:t}}ReadArrayBuffer(e){return this.reader.ReadArrayBuffer(e)}GetDataCount(){return this.dataCount}ReadData(){if(this.dataType===null)return null;if(this.dataType===hr.SCALAR){let e=this.ReadComponent();return this.SkipBytesByStride(1),e}else if(this.dataType===hr.VEC2){let e=this.ReadComponent(),t=this.ReadComponent();return this.SkipBytesByStride(2),new Nt(e,t)}else if(this.dataType===hr.VEC3){let e=this.ReadComponent(),t=this.ReadComponent(),n=this.ReadComponent();return this.SkipBytesByStride(3),new Ye(e,t,n)}else if(this.dataType===hr.VEC4){let e=this.ReadComponent(),t=this.ReadComponent(),n=this.ReadComponent(),i=this.ReadComponent();return this.SkipBytesByStride(4),new $i(e,t,n,i)}return null}EnumerateData(e){if(this.sparseReader===null)for(let t=0;t{DracoDecoderModule().then(n=>{this.draco=n,t.onSuccess()})}).catch(()=>{t.onError(nt("Failed to load draco decoder."))}):t.onSuccess()}GetUnsupportedExtensions(e){let t=[];if(e===void 0)return t;for(let n=0;n{i.AddVertex(N)}),m&&s(this.draco,o,p,l.attributes.NORMAL,N=>{i.AddNormal(N)}),_&&s(this.draco,o,p,l.attributes.TEXCOORD_0,N=>{N.y=-N.y,i.AddTextureUV(N)});let C=p.num_faces()*3,E=C*4,b=this.draco._malloc(E);o.GetTrianglesUInt32Array(p,E,b);let R=new Uint32Array(this.draco.HEAPU32.buffer,b,C).slice();for(let N=0;N0){this.SetError(Uo("Unsupported extension: {0}.",n.join(", "))),t();return}this.gltfExtensions.LoadLibraries(e.extensionsRequired,{onSuccess:()=>{this.ImportModel(e),t()},onError:i=>{this.SetError(i),t()}})}ImportModel(e){let t=e.materials;if(t!==void 0)for(let i of t)this.ImportMaterial(e,i);let n=e.meshes;if(n!==void 0)for(let i of n)this.ImportMesh(e,i);this.ImportProperties(this.model,e.asset,nt("Asset properties")),this.ImportScene(e)}ImportProperties(e,t,n){if(t==null)return;let i=new Kn(n);for(let s in t)if(Object.prototype.hasOwnProperty.call(t,s)){let o=null,a=t[s];typeof a=="string"?o=new cn(Yt.Text,s,a):typeof a=="number"&&(Number.isInteger(a)?o=new cn(Yt.Integer,s,a):o=new cn(Yt.Number,s,a)),o!==null&&i.AddProperty(o)}i.PropertyCount()!==0&&e.AddPropertyGroup(i)}GetDefaultScene(e){let t=e.scene||0;return t>=e.scenes.length?null:e.scenes[t]}ImportMaterial(e,t){let n=new ys;if(t.name!==void 0&&(n.name=t.name),n.color=Uc([1,1,1]),t.pbrMetallicRoughness!==void 0){let s=t.pbrMetallicRoughness.baseColorFactor;s!==void 0&&(n.color=Uc(s),n.opacity=s[3]);let o=t.pbrMetallicRoughness.metallicFactor;o!==void 0&&(n.metalness=o);let a=t.pbrMetallicRoughness.roughnessFactor;a!==void 0&&(n.roughness=a);let l=t.emissiveFactor;l!==void 0&&(n.emissive=Uc(l)),n.diffuseMap=this.ImportTexture(e,t.pbrMetallicRoughness.baseColorTexture),n.metalnessMap=this.ImportTexture(e,t.pbrMetallicRoughness.metallicRoughnessTexture),n.normalMap=this.ImportTexture(e,t.normalTexture),n.emissiveMap=this.ImportTexture(e,t.emissiveTexture),n.diffuseMap!==null&&(n.multiplyDiffuseMap=!0);let c=t.alphaMode;c!==void 0&&(c==="BLEND"?n.transparent=!0:c==="MASK"&&(n.transparent=!0,n.alphaTest=t.alphaCutoff||.5))}let i=this.gltfExtensions.ProcessMaterial(t,n,s=>this.ImportTexture(e,s));i!==null&&(n=i),this.model.AddMaterial(n)}ImportTexture(e,t){if(t==null)return null;let n=new fi,s=e.textures[t.index].source,o=e.images[s],a=null;if(this.imageIndexToTextureParams.has(s))a=this.imageIndexToTextureParams.get(s);else{a={name:null,mimeType:null,buffer:null};let l=s.toString();if(o.uri!==void 0){let c=qs(o.uri);if(c!==null)a.name="Embedded_"+l+"."+xs(c.mimeType),a.mimeType=c.mimeType,a.buffer=c.buffer;else{let u=this.callbacks.getFileBuffer(o.uri);a.name=o.uri,a.buffer=u}}else if(o.bufferView!==void 0){let c=e.bufferViews[o.bufferView],u=this.GetReaderFromBufferView(c);if(u!==null){let h=u.ReadArrayBuffer(c.byteLength);a.name="Binary_"+l+"."+xs(o.mimeType),a.mimeType=o.mimeType,a.buffer=h}}this.imageIndexToTextureParams.set(s,a)}return n.name=a.name,n.mimeType=a.mimeType,n.buffer=a.buffer,this.gltfExtensions.ProcessTexture(t,n),n}ImportMesh(e,t){let n=new tn;this.model.AddMesh(n),t.name!==void 0&&n.SetName(t.name);for(let i=0;i{n.AddVertex(v)})}else return;let y=n.VertexCount()-h;if(o){let m=e.accessors[t.attributes.COLOR_0],_=this.GetReaderFromAccessor(e,m);if(_===null)return;_.EnumerateData(v=>{let x=zb([v.x,v.y,v.z],_.componentType);n.AddVertexColor(x)}),n.VertexColorCount()-f!==y&&(o=!1)}if(a){let m=e.accessors[t.attributes.NORMAL],_=this.GetReaderFromAccessor(e,m);if(_===null)return;_.EnumerateData(v=>{n.AddNormal(v)}),n.NormalCount()-p!==y&&(a=!1)}if(l){let m=e.accessors[t.attributes.TEXCOORD_0],_=this.GetReaderFromAccessor(e,m);if(_===null)return;_.EnumerateData(v=>{v.y=-v.y,n.AddTextureUV(v)}),n.TextureUVCount()-g!==y&&(l=!1)}let d=[];if(c){let m=e.accessors[t.indices],_=this.GetReaderFromAccessor(e,m);if(_===null)return;_.EnumerateData(v=>{d.push(v)})}else{let m=n.VertexCount()-h;for(let _=0;_{this.ifc=new WebIFC.IfcAPI,this.ifc.Init().then(()=>{this.ImportIfcContent(e),t()})}).catch(()=>{this.SetError(nt("Failed to load web-ifc.")),t()}):(this.ImportIfcContent(e),t())}ImportIfcContent(e){let t=new Uint8Array(e),n=this.ifc.OpenModel(t,{COORDINATE_TO_ORIGIN:!0}),i=this.ifc.LoadAllGeometry(n);for(let s=0;s0&&this.ImportIfcMesh(n,o)}this.ImportProperties(n),this.ifc.CloseModel(n)}ImportIfcMesh(e,t){let n=new tn;n.SetName(Uo("Mesh {0}",t.expressID.toString()));let i=0,s=t.geometries;for(let o=0;o{let a=null;if(this.expressIDToMesh.has(o.value)?a=this.expressIDToMesh.get(o.value):this.ifc.GetLine(e,o.value,!0).type===WebIFC.IFCBUILDING&&(a=this.model),a===null)return;let l=s.RelatingPropertyDefinition,c=this.ifc.GetLine(e,l.value,!0);if(!c||!c.HasProperties)return;let u=new Kn(c.Name.value);c.HasProperties.forEach(h=>{if(!h||!h.Name||!h.NominalValue||!h.NominalValue.constructor||h.type!==WebIFC.IFCPROPERTYSINGLEVALUE)return;let f=this.GetIFCString(h.Name.value),p=null,g=null;switch(h.NominalValue.constructor.name){case"IfcText":case"IfcLabel":case"IfcIdentifier":case WebIFC.IFCLABEL:p=new cn(Yt.Text,f,this.GetIFCString(h.NominalValue.value));break;case"IfcBoolean":case"IfcLogical":g=nt("Unknown"),h.NominalValue.value==="T"?g=nt("True"):h.NominalValue.value==="F"&&(g=nt("False")),p=new cn(Yt.Text,f,g);break;case"IfcInteger":case"IfcCountMeasure":p=new cn(Yt.Integer,f,h.NominalValue.value);break;case"IfcReal":case"IfcLengthMeasure":case"IfcPositiveLengthMeasure":case"IfcAreaMeasure":case"IfcVolumeMeasure":case"IfcRatioMeasure":case"IfcPositiveRatioMeasure":case"IfcMassMeasure":case"IfcMassPerLengthMeasure":case"IfcPlaneAngleMeasure":case"IfcThermalTransmittanceMeasure":p=new cn(Yt.Number,f,h.NominalValue.value);break;default:console.log(h);break}p!==null&&u.AddProperty(p)}),u.PropertyCount()>0&&a.AddPropertyGroup(u)})}}GetMaterialIndexByColor(e){let t=Ei(e.x,e.y,e.z),n=parseInt(e.w*255,10);return this.colorToMaterial.GetMaterialIndex(t.r,t.g,t.b,n)}GetIFCString(e){let t=this.DecodeIFCString(e);return t.length===0&&(t="-"),t}DecodeIFCString(e){let t=/\\X2\\(.*?)\\X0\\/uig,n=e,i=t.exec(e);for(;i;){let s=String.fromCharCode(parseInt(i[1],16));n=n.replace(i[0],s),i=t.exec(e)}return n}};var im=class{constructor(e){this.mesh=e,this.globalToMeshVertices=new Map,this.globalToMeshVertexColors=new Map,this.globalToMeshNormals=new Map,this.globalToMeshUvs=new Map}AddVertex(e,t){return this.GetMeshIndex(e,t,this.globalToMeshVertices,n=>this.mesh.AddVertex(new Ye(n.x,n.y,n.z)))}AddVertexColor(e,t){return this.GetMeshIndex(e,t,this.globalToMeshVertexColors,n=>this.mesh.AddVertexColor(new St(n.r,n.g,n.b)))}AddNormal(e,t){return this.GetMeshIndex(e,t,this.globalToMeshNormals,n=>this.mesh.AddNormal(new Ye(n.x,n.y,n.z)))}AddUV(e,t){return this.GetMeshIndex(e,t,this.globalToMeshUvs,n=>this.mesh.AddTextureUV(new Nt(n.x,n.y)))}AddLine(e){this.mesh.AddLine(e)}AddTriangle(e){this.mesh.AddTriangle(e)}GetMeshIndex(e,t,n,i){if(isNaN(e)||e<0||e>=t.length)return null;if(n.has(e))return n.get(e);{let s=t[e],o=i(s);return n.set(e,o),o}}};function Df(r,e,t){return Ei(parseFloat(r),parseFloat(e),parseFloat(t))}var Qa=class extends Jt{constructor(){super()}CanImportExtension(e){return e==="obj"}GetUpDirection(){return _t.Y}ClearContent(){this.globalVertices=null,this.globalVertexColors=null,this.globalNormals=null,this.globalUvs=null,this.currentMeshConverter=null,this.currentMaterial=null,this.currentMaterialIndex=null,this.meshNameToConverter=null,this.materialNameToIndex=null}ResetContent(){this.globalVertices=[],this.globalVertexColors=[],this.globalNormals=[],this.globalUvs=[],this.currentMeshConverter=null,this.currentMaterial=null,this.currentMaterialIndex=null,this.meshNameToConverter=new Map,this.materialNameToIndex=new Map}ImportContent(e,t){let n=Sn(e);Fi(n,i=>{this.WasError()||this.ProcessLine(i)}),t()}ProcessLine(e){if(e[0]==="#")return;let t=Yi(e,"#");if(t.length===0)return;let n=t[0].toLowerCase();t.shift(),!this.ProcessMeshParameter(n,t,e)&&this.ProcessMaterialParameter(n,t,e)}AddNewMesh(e){if(this.meshNameToConverter.has(e))this.currentMeshConverter=this.meshNameToConverter.get(e);else{let t=new tn;t.SetName(e),this.model.AddMeshToRootNode(t),this.currentMeshConverter=new im(t),this.meshNameToConverter.set(e,this.currentMeshConverter)}}ProcessMeshParameter(e,t,n){if(e==="g"||e==="o"){if(t.length===0)return!0;let i=zr(n,e.length,"#");return this.AddNewMesh(i),!0}else{if(e==="v")return t.length<3||(this.globalVertices.push(new Ye(parseFloat(t[0]),parseFloat(t[1]),parseFloat(t[2]))),t.length>=6&&this.globalVertexColors.push(Df(t[3],t[4],t[5]))),!0;if(e==="vn")return t.length<3||this.globalNormals.push(new Ye(parseFloat(t[0]),parseFloat(t[1]),parseFloat(t[2]))),!0;if(e==="vt")return t.length<2||this.globalUvs.push(new Nt(parseFloat(t[0]),parseFloat(t[1]))),!0;if(e==="l"){if(t.length<2)return!0;this.ProcessLineCommand(t)}else if(e==="f")return t.length<3||this.ProcessFaceCommand(t),!0}return!1}ProcessMaterialParameter(e,t,n){function i(o){let a=new Map,l=null;for(let c=0;c0&&(l.offset.x=parseFloat(f[0])),f.length>1&&(l.offset.y=parseFloat(f[1]))}if(h.has("-s")){let f=h.get("-s");f.length>0&&(l.scale.x=parseFloat(f[0])),f.length>1&&(l.scale.y=parseFloat(f[1]))}return l}if(e==="newmtl"){if(t.length===0)return!0;let o=new En,a=zr(n,e.length,"#"),l=this.model.AddMaterial(o);return o.name=a,this.currentMaterial=o,this.materialNameToIndex.set(a,l),!0}else if(e==="usemtl"){if(t.length===0)return!0;let o=zr(n,e.length,"#");return this.materialNameToIndex.has(o)&&(this.currentMaterialIndex=this.materialNameToIndex.get(o)),!0}else if(e==="mtllib"){if(t.length===0)return!0;let o=zr(n,e.length,"#"),a=this.callbacks.getFileBuffer(o);if(a!==null){let l=Sn(a);Fi(l,c=>{this.WasError()||this.ProcessLine(c)})}return!0}else{if(e==="map_kd")return this.currentMaterial===null||t.length===0||(this.currentMaterial.diffuseMap=s(t,this.callbacks),pi(this.currentMaterial)),!0;if(e==="map_ks")return this.currentMaterial===null||t.length===0||(this.currentMaterial.specularMap=s(t,this.callbacks)),!0;if(e==="map_bump"||e==="bump")return this.currentMaterial===null||t.length===0||(this.currentMaterial.bumpMap=s(t,this.callbacks)),!0;if(e==="ka")return this.currentMaterial===null||t.length<3||(this.currentMaterial.ambient=Df(t[0],t[1],t[2])),!0;if(e==="kd")return this.currentMaterial===null||t.length<3||(this.currentMaterial.color=Df(t[0],t[1],t[2])),!0;if(e==="ks")return this.currentMaterial===null||t.length<3||(this.currentMaterial.specular=Df(t[0],t[1],t[2])),!0;if(e==="ns")return this.currentMaterial===null||t.length<1||(this.currentMaterial.shininess=parseFloat(t[0])/1e3),!0;if(e==="tr")return this.currentMaterial===null||t.length<1||(this.currentMaterial.opacity=1-parseFloat(t[0]),pi(this.currentMaterial)),!0;if(e==="d")return this.currentMaterial===null||t.length<1||(this.currentMaterial.opacity=parseFloat(t[0]),pi(this.currentMaterial)),!0}return!1}ProcessLineCommand(e){this.currentMeshConverter===null&&this.AddNewMesh("");let t=[];for(let i=0;i1&&a[1].length>0&&s.push(this.GetRelativeIndex(parseInt(a[1],10),this.globalUvs.length)),a.length>2&&a[2].length>0&&i.push(this.GetRelativeIndex(parseInt(a[2],10),this.globalNormals.length))}for(let o=0;o0?e-1:t+e}};var el=class extends Jt{constructor(){super()}CanImportExtension(e){return e==="off"}GetUpDirection(){return _t.Y}ClearContent(){this.mesh=null,this.status=null,this.colorToMaterial=null}ResetContent(){this.mesh=new tn,this.model.AddMeshToRootNode(this.mesh),this.status={vertexCount:0,faceCount:0,foundVertex:0,foundFace:0},this.colorToMaterial=new di(this.model)}ImportContent(e,t){let n=Sn(e);Fi(n,i=>{this.WasError()||this.ProcessLine(i)}),t()}ProcessLine(e){function t(s){return s.indexOf(".")!==-1?Fn(parseFloat(s)):parseInt(s,10)}if(e[0]==="#")return;let n=Yi(e,"#");if(n.length===0||n[0]==="OFF")return;if(this.status.vertexCount===0&&this.status.faceCount===0){n.length>1&&(this.status.vertexCount=parseInt(n[0],10),this.status.faceCount=parseInt(n[1],10));return}if(this.status.foundVertex=3&&(this.mesh.AddVertex(new Ye(parseFloat(n[0]),parseFloat(n[1]),parseFloat(n[2]))),this.status.foundVertex+=1),n.length>=6&&this.mesh.AddVertexColor(new St(t(n[3]),t(n[4]),t(n[5])));return}let i=this.mesh.VertexCount()===this.mesh.VertexColorCount();if(this.status.foundFace=4){let s=parseInt(n[0],10);if(n.length=s+4){let a=new St(t(n[s+1]),t(n[s+2]),t(n[s+3]));o=this.colorToMaterial.GetMaterialIndex(a.r,a.g,a.b)}for(let a=0;a0&&t.format.length>0,s=n!==null&&n.count>0&&n.format.length>0;if(!i&&!s)return Ds.NoFaces}else return Ds.UnknownError;return Ds.Ok}},sm=class{constructor(e){this.model=e,this.colorToMaterial=new Map}GetMaterialIndexByColor(e){let t="Color "+In(e[0])+In(e[1])+In(e[2])+In(e[3]);if(this.colorToMaterial.has(t))return this.colorToMaterial.get(t);{let n=new En;n.name=t,n.color=new St(e[0],e[1],e[2]),n.opacity=e[3]/255,pi(n);let i=this.model.AddMaterial(n);return this.colorToMaterial.set(t,i),i}}},tl=class extends Jt{constructor(){super()}CanImportExtension(e){return e==="ply"}GetUpDirection(){return _t.Y}ClearContent(){this.mesh=null}ResetContent(){this.mesh=new tn,this.model.AddMeshToRootNode(this.mesh)}ImportContent(e,t){let n=this.GetHeaderContent(e),i=this.ReadHeader(n),s=i.Check();if(s===Ds.Ok)if(i.format==="ascii"){let o=Sn(e);o=o.substring(n.length),this.ReadAsciiContent(i,o)}else(i.format==="binary_little_endian"||i.format==="binary_big_endian")&&this.ReadBinaryContent(i,e,n.length);else s===Ds.NoVertices?this.SetError(nt("The model contains no vertices.")):s===Ds.NoFaces?this.SetError(nt("The model contains no faces.")):this.SetError(nt("Invalid header information."));t()}GetHeaderContent(e){let t="",n=new Uint8Array(e),i=0;for(i=0;i{let i=Yi(n,null);i.length===0||i[0]==="comment"||i[0]!=="ply"&&(i[0]==="format"&&i.length>=2?t.SetFormat(i[1]):i[0]==="element"&&i.length>=3?t.AddElement(i[1],parseInt(i[2],10)):i[0]==="property"&&i.length>=3&&(i[1]==="list"&&i.length>=5?t.AddListFormat(i[2],i[3],i[4]):t.AddSingleFormat(i[1],i[2])))}),t}ReadAsciiContent(e,t){let n=e.GetElement("vertex"),i=e.GetElement("face"),s=0,o=0;Fi(t,a=>{if(this.WasError())return;let l=Yi(a,null);if(!(l.length===0||l[0]==="comment")){if(s=3&&(this.mesh.AddVertex(new Ye(parseFloat(l[0]),parseFloat(l[1]),parseFloat(l[2]))),s+=1);return}if(i!==null&&o=4){let c=parseInt(l[0],10);if(l.length0&&v.SetVertexColors(d,m,_),this.mesh.AddTriangle(v)}}else if(h.name==="tristrips")for(let f=0;f{this.worker=n,this.worker.addEventListener("message",a=>{this.ImportResultJson(a.data,t)}),this.worker.addEventListener("error",a=>{this.SetError(nt("Failed to load occt-import-js.")),t()});let i=null;if(this.extension==="stp"||this.extension==="step")i="step";else if(this.extension==="igs"||this.extension==="iges")i="iges";else if(this.extension==="brp"||this.extension==="brep")i="brep";else{t();return}(i==="step"||i==="iges")&&this.model.SetUnit(_s.Millimeter);let s={linearUnit:"millimeter",linearDeflectionType:"bounding_box_ratio",linearDeflection:.001,angularDeflection:.5},o=new Uint8Array(e);this.worker.postMessage({format:i,buffer:o,params:s})}).catch(()=>{this.SetError(nt("Failed to load occt-import-js.")),t()})}ImportResultJson(e,t){if(!e.success){t();return}let n=new di(this.model),i=this.model.GetRootNode();this.ImportNode(e,e.root,i,n),t()}ImportNode(e,t,n,i){for(let s of t.meshes){let o=e.meshes[s],a=this.ImportMesh(o,i),l=this.model.AddMesh(a);n.AddMeshIndex(l)}for(let s of t.children){let o=new Bn;o.SetName(s.name),n.AddChildNode(o),this.ImportNode(e,s,o,i)}}ImportMesh(e,t){let n=null;if(e.color){let s=Ei(e.color[0],e.color[1],e.color[2]);n=t.GetMaterialIndex(s.r,s.g,s.b,null)}let i=ur(e,n,null);e.name&&i.SetName(e.name);for(let s of e.brep_faces){if(s.color===null)continue;let o=Ei(s.color[0],s.color[1],s.color[2]),a=t.GetMaterialIndex(o.r,o.g,o.b,null);for(let l=s.first;l<=s.last;l++)i.GetTriangle(l).SetMaterial(a)}return i}};var il=class extends Jt{constructor(){super()}CanImportExtension(e){return e==="stl"}GetUpDirection(){return _t.Z}ClearContent(){this.mesh=null,this.triangle=null}ResetContent(){this.mesh=new tn,this.model.AddMeshToRootNode(this.mesh),this.triangle=null}ImportContent(e,t){if(this.IsBinaryStlFile(e))this.ProcessBinary(e);else{let n=Sn(e);Fi(n,i=>{this.WasError()||this.ProcessLine(i)})}t()}IsBinaryStlFile(e){let t=e.byteLength;if(t<84)return!1;let n=new oi(e,!0);n.Skip(80);let i=n.ReadUnsignedInteger32();return t===i*50+84}ProcessLine(e){if(e[0]==="#")return;let t=Yi(e,"#");if(t.length===0)return;let n=t[0];if(n==="solid"){if(t.length>1){let i=zr(e,n.length,"#");this.mesh.SetName(i)}return}if(n==="facet"){if(this.triangle=new Qt(-1,-1,-1),t.length>=5&&t[1]==="normal"){let i=new Ye(parseFloat(t[2]),parseFloat(t[3]),parseFloat(t[4]));if(Di(i.Length())){let s=this.mesh.AddNormal(i);this.triangle.SetNormals(s,s,s)}}return}if(n==="vertex"&&this.triangle!==null){if(t.length>=4){let i=this.mesh.AddVertex(new Ye(parseFloat(t[1]),parseFloat(t[2]),parseFloat(t[3])));this.triangle.v0===-1?this.triangle.v0=i:this.triangle.v1===-1?this.triangle.v1=i:this.triangle.v2===-1&&(this.triangle.v2=i)}return}if(n==="endfacet"&&this.triangle!==null){this.triangle.v0!==-1&&this.triangle.v1!==-1&&this.triangle.v2!==null&&this.mesh.AddTriangle(this.triangle),this.triangle=null;return}}ProcessBinary(e){function t(o){let a=new Ye;return a.x=o.ReadFloat32(),a.y=o.ReadFloat32(),a.z=o.ReadFloat32(),a}function n(o,a){let l=t(a);return o.AddVertex(l)}let i=new oi(e,!0);i.Skip(80);let s=i.ReadUnsignedInteger32();for(let o=0;oe.face_colors?this.colorToMaterial.GetMaterialIndex(e.face_colors[f*4+0],e.face_colors[f*4+1],e.face_colors[f*4+2],e.face_colors[f*4+3]):t),o=this.model.AddMesh(s),a=new Bn;a.AddMeshIndex(o);let l=new Ye(0,0,0);e.vector&&(l=new Ye(e.vector.x,e.vector.y,e.vector.z));let c=new ii(0,0,0,1);e.rotation&&(c=new ii(e.rotation.qx,e.rotation.qy,e.rotation.qz,e.rotation.qw));let u=new Ye(1,1,1),h=new xn().ComposeTRS(l,c,u);return a.SetTransformation(new pn(h)),n.AddChildNode(a),s}ImportMesh(e,t){let n=new tn;for(let i=0;i256||Y.colormap_size!==24||Y.colormap_type!==1)throw new Error("THREE.TGALoader: Invalid type colormap data for indexed type.");break;case p:case g:case d:case m:if(Y.colormap_type)throw new Error("THREE.TGALoader: Invalid type colormap data for colormap type.");break;case h:throw new Error("THREE.TGALoader: No data.");default:throw new Error("THREE.TGALoader: Invalid type "+Y.image_type)}if(Y.width<=0||Y.height<=0)throw new Error("THREE.TGALoader: Invalid image size.");if(Y.pixel_size!==8&&Y.pixel_size!==16&&Y.pixel_size!==24&&Y.pixel_size!==32)throw new Error("THREE.TGALoader: Invalid pixel size "+Y.pixel_size)}function n(Y,te,q,le,ye){let Te,Ae,_e=q.pixel_size>>3,G=q.width*q.height*_e;if(te&&(Ae=ye.subarray(le,le+=q.colormap_length*(q.colormap_size>>3))),Y){Te=new Uint8Array(G);let P,M,T,k=0,j=new Uint8Array(_e);for(;k>7,Y[(M+k*T)*4+1]=(G&992)>>2,Y[(M+k*T)*4+2]=(G&31)<<3,Y[(M+k*T)*4+3]=G&32768?0:255;return Y}function o(Y,te,q,le,ye,Te,Ae,_e){let G=0,P,M,T=b.width;for(M=te;M!==le;M+=q)for(P=ye;P!==Ae;P+=Te,G+=3)Y[(P+T*M)*4+3]=255,Y[(P+T*M)*4+2]=_e[G+0],Y[(P+T*M)*4+1]=_e[G+1],Y[(P+T*M)*4+0]=_e[G+2];return Y}function a(Y,te,q,le,ye,Te,Ae,_e){let G=0,P,M,T=b.width;for(M=te;M!==le;M+=q)for(P=ye;P!==Ae;P+=Te,G+=4)Y[(P+T*M)*4+2]=_e[G+0],Y[(P+T*M)*4+1]=_e[G+1],Y[(P+T*M)*4+0]=_e[G+2],Y[(P+T*M)*4+3]=_e[G+3];return Y}function l(Y,te,q,le,ye,Te,Ae,_e){let G,P=0,M,T,k=b.width;for(T=te;T!==le;T+=q)for(M=ye;M!==Ae;M+=Te,P++)G=_e[P],Y[(M+k*T)*4+0]=G,Y[(M+k*T)*4+1]=G,Y[(M+k*T)*4+2]=G,Y[(M+k*T)*4+3]=255;return Y}function c(Y,te,q,le,ye,Te,Ae,_e){let G=0,P,M,T=b.width;for(M=te;M!==le;M+=q)for(P=ye;P!==Ae;P+=Te,G+=2)Y[(P+T*M)*4+0]=_e[G+0],Y[(P+T*M)*4+1]=_e[G+0],Y[(P+T*M)*4+2]=_e[G+0],Y[(P+T*M)*4+3]=_e[G+1];return Y}function u(Y,te,q,le,ye){let Te,Ae,_e,G,P,M;switch((b.flags&_)>>v){default:case I:Te=0,_e=1,P=te,Ae=0,G=1,M=q;break;case x:Te=0,_e=1,P=te,Ae=q-1,G=-1,M=-1;break;case A:Te=te-1,_e=-1,P=-1,Ae=0,G=1,M=q;break;case S:Te=te-1,_e=-1,P=-1,Ae=q-1,G=-1,M=-1;break}if(B)switch(b.pixel_size){case 8:l(Y,Ae,G,M,Te,_e,P,le);break;case 16:c(Y,Ae,G,M,Te,_e,P,le);break;default:throw new Error("THREE.TGALoader: Format not supported.")}else switch(b.pixel_size){case 8:i(Y,Ae,G,M,Te,_e,P,le,ye);break;case 16:s(Y,Ae,G,M,Te,_e,P,le);break;case 24:o(Y,Ae,G,M,Te,_e,P,le);break;case 32:a(Y,Ae,G,M,Te,_e,P,le);break;default:throw new Error("THREE.TGALoader: Format not supported.")}return Y}let h=0,f=1,p=2,g=3,y=9,d=10,m=11,_=48,v=4,x=0,S=1,I=2,A=3;if(e.length<19)throw new Error("THREE.TGALoader: Not enough data to contain header.");let C=0,E=new Uint8Array(e),b={id_length:E[C++],colormap_type:E[C++],image_type:E[C++],colormap_index:E[C++]|E[C++]<<8,colormap_length:E[C++]|E[C++]<<8,colormap_size:E[C++],origin:[E[C++]|E[C++]<<8,E[C++]|E[C++]<<8],width:E[C++]|E[C++]<<8,height:E[C++]|E[C++]<<8,pixel_size:E[C++],flags:E[C++]};if(t(b),b.id_length+C>e.length)throw new Error("THREE.TGALoader: No data.");C+=b.id_length;let R=!1,N=!1,B=!1;switch(b.image_type){case y:R=!0,N=!0;break;case f:N=!0;break;case d:R=!0;break;case p:break;case m:R=!0,B=!0;break;case g:B=!0;break}let V=new Uint8Array(b.width*b.height*4),ie=n(R,N,b,C,E);return u(V,b.width,b.height,ie.pixel_data,ie.palettes),{data:V,width:b.width,height:b.height,flipY:!0,generateMipmaps:!0,minFilter:xi}}};var Ni=Uint8Array,sl=Uint16Array,Hb=Int32Array,By=new Ni([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Gy=new Ni([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Wb=new Ni([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Vy=function(r,e){for(var t=new sl(31),n=0;n<31;++n)t[n]=e+=1<>1|(Ht&21845)<<1,fs=(fs&52428)>>2|(fs&13107)<<2,fs=(fs&61680)>>4|(fs&3855)<<4,cm[Ht]=((fs&65280)>>8|(fs&255)<<8)>>1;var fs,Ht,kc=function(r,e,t){for(var n=r.length,i=0,s=new sl(e);i>l]=c}else for(a=new sl(n),i=0;i>15-r[i]);return a},Bc=new Ni(288);for(Ht=0;Ht<144;++Ht)Bc[Ht]=8;var Ht;for(Ht=144;Ht<256;++Ht)Bc[Ht]=9;var Ht;for(Ht=256;Ht<280;++Ht)Bc[Ht]=7;var Ht;for(Ht=280;Ht<288;++Ht)Bc[Ht]=8;var Ht,Xy=new Ni(32);for(Ht=0;Ht<32;++Ht)Xy[Ht]=5;var Ht;var jb=kc(Bc,9,1);var Yb=kc(Xy,5,1),om=function(r){for(var e=r[0],t=1;te&&(e=r[t]);return e},fr=function(r,e,t){var n=e/8|0;return(r[n]|r[n+1]<<8)>>(e&7)&t},am=function(r,e){var t=e/8|0;return(r[t]|r[t+1]<<8|r[t+2]<<16)>>(e&7)},Kb=function(r){return(r+7)/8|0},hm=function(r,e,t){return(e==null||e<0)&&(e=0),(t==null||t>r.length)&&(t=r.length),new Ni(r.subarray(e,t))};var Zb=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],Ti=function(r,e,t){var n=new Error(e||Zb[r]);if(n.code=r,Error.captureStackTrace&&Error.captureStackTrace(n,Ti),!t)throw n;return n},qy=function(r,e,t,n){var i=r.length,s=n?n.length:0;if(!i||e.f&&!e.l)return t||new Ni(0);var o=!t,a=o||e.i!=2,l=e.i;o&&(t=new Ni(i*3));var c=function(re){var ce=t.length;if(re>ce){var me=new Ni(Math.max(ce*2,re));me.set(t),t=me}},u=e.f||0,h=e.p||0,f=e.b||0,p=e.l,g=e.d,y=e.m,d=e.n,m=i*8;do{if(!p){u=fr(r,h,1);var _=fr(r,h+1,3);if(h+=3,_)if(_==1)p=jb,g=Yb,y=9,d=5;else if(_==2){var I=fr(r,h,31)+257,A=fr(r,h+10,15)+4,C=I+fr(r,h+5,31)+1;h+=14;for(var E=new Ni(C),b=new Ni(19),R=0;R>4;if(v<16)E[R++]=v;else{var Y=0,te=0;for(v==16?(te=3+fr(r,h,3),h+=2,Y=E[R-1]):v==17?(te=3+fr(r,h,7),h+=3):v==18&&(te=11+fr(r,h,127),h+=7);te--;)E[R++]=Y}}var q=E.subarray(0,I),le=E.subarray(I);y=om(q),d=om(le),p=kc(q,y,1),g=kc(le,d,1)}else Ti(1);else{var v=Kb(h)+4,x=r[v-4]|r[v-3]<<8,S=v+x;if(S>i){l&&Ti(0);break}a&&c(f+x),t.set(r.subarray(v,S),f),e.b=f+=x,e.p=h=S*8,e.f=u;continue}if(h>m){l&&Ti(0);break}}a&&c(f+131072);for(var ye=(1<>4;if(h+=Y&15,h>m){l&&Ti(0);break}if(Y||Ti(2),_e<256)t[f++]=_e;else if(_e==256){Ae=h,p=null;break}else{var G=_e-254;if(_e>264){var R=_e-257,P=By[R];G=fr(r,h,(1<>4;M||Ti(3),h+=M&15;var le=qb[T];if(T>3){var P=Gy[T];le+=am(r,h)&(1<m){l&&Ti(0);break}a&&c(f+131072);var k=f+G;if(f>>0},lm=function(r,e){return dr(r,e)+dr(r,e+4)*4294967296};var $b=function(r,e){return((r[0]&15)!=8||r[0]>>4>7||(r[0]<<8|r[1])%31)&&Ti(6,"invalid zlib data"),(r[1]>>5&1)==+!e&&Ti(6,"invalid zlib data: "+(r[1]&32?"need":"unexpected")+" dictionary"),(r[1]>>3&4)+2};function Qb(r,e){return qy(r,{i:2},e&&e.out,e&&e.dictionary)}function jy(r,e){return qy(r.subarray($b(r,e&&e.dictionary),-4),{i:2},e&&e.out,e&&e.dictionary)}var um=typeof TextDecoder<"u"&&new TextDecoder,eS=0;try{um.decode(Jb,{stream:!0}),eS=1}catch{}var tS=function(r){for(var e="",t=0;;){var n=r[t++],i=(n>127)+(n>223)+(n>239);if(t+i>r.length)return{s:e,r:hm(r,t-1)};i?i==3?(n=((n&15)<<18|(r[t++]&63)<<12|(r[t++]&63)<<6|r[t++]&63)-65536,e+=String.fromCharCode(55296|n>>10,56320|n&1023)):i&1?e+=String.fromCharCode((n&31)<<6|r[t++]&63):e+=String.fromCharCode((n&15)<<12|(r[t++]&63)<<6|r[t++]&63):e+=String.fromCharCode(n)}};function nS(r,e){if(e){for(var t="",n=0;n65558)&&Ti(13);var i=Pr(r,n+8);if(!i)return{};var s=dr(r,n+16),o=s==4294967295||i==65535;if(o){var a=dr(r,n-12);o=dr(r,a)==101075792,o&&(i=dr(r,a+32),s=dr(r,a+48))}for(var l=e&&e.filter,c=0;c=t[n])return n-1;if(e<=t[r])return r;let i=r,s=n,o=Math.floor((i+s)/2);for(;e=t[o+1];)e=y&&(g[p][0]=g[f][0]/a[_+1][m],d=g[p][0]*a[m][_]);let v=m>=-1?1:-m,x=h-1<=_?y-1:t-h;for(let I=v;I<=x;++I)g[p][I]=(g[f][I]-g[f][I-1])/a[_+1][m+I],d+=g[p][I]*a[m+I][_];h<=_&&(g[p][y]=-g[f][y-1]/a[_+1][h],d+=g[p][y]*a[h][_]),o[y][h]=d;let S=f;f=p,p=S}}let u=t;for(let h=1;h<=n;++h){for(let f=0;f<=t;++f)o[h][f]*=u;u*=t-h}return o}function lS(r,e,t,n,i){let s=it.toArray()),e.startKnot=this.startKnot,e.endKnot=this.endKnot,e}fromJSON(e){return super.fromJSON(e),this.degree=e.degree,this.knots=[...e.knots],this.controlPoints=e.controlPoints.map(t=>new wt(t[0],t[1],t[2],t[3])),this.startKnot=e.startKnot,this.endKnot=e.endKnot,this}};var Rt,Tn,ai,Bf=class extends zt{constructor(e){super(e)}load(e,t,n,i){let s=this,o=s.path===""?os.extractUrlBase(e):s.path,a=new $n(this.manager);a.setPath(s.path),a.setResponseType("arraybuffer"),a.setRequestHeader(s.requestHeader),a.setWithCredentials(s.withCredentials),a.load(e,function(l){try{t(s.parse(l,o))}catch(c){i?i(c):console.error(c),s.manager.itemError(e)}},n,i)}parse(e,t){if(fS(e))Rt=new vm().parse(e);else{let i=Qy(e);if(!dS(i))throw new Error("THREE.FBXLoader: Unknown format.");if(Jy(i)<7e3)throw new Error("THREE.FBXLoader: FBX version not supported, FileVersion: "+Jy(i));Rt=new xm().parse(i)}let n=new Ri(this.manager).setPath(this.resourcePath||t).setCrossOrigin(this.crossOrigin);return new mm(n,this.manager).parse(Rt)}},mm=class{constructor(e,t){this.textureLoader=e,this.manager=t}parse(){Tn=this.parseConnections();let e=this.parseImages(),t=this.parseTextures(e),n=this.parseMaterials(t),i=this.parseDeformers(),s=new gm().parse(i);return this.parseScene(i,s,n),ai}parseConnections(){let e=new Map;return"Connections"in Rt&&Rt.Connections.connections.forEach(function(n){let i=n[0],s=n[1],o=n[2];e.has(i)||e.set(i,{parents:[],children:[]});let a={ID:s,relationship:o};e.get(i).parents.push(a),e.has(s)||e.set(s,{parents:[],children:[]});let l={ID:i,relationship:o};e.get(s).children.push(l)}),e}parseImages(){let e={},t={};if("Video"in Rt.Objects){let n=Rt.Objects.Video;for(let i in n){let s=n[i],o=parseInt(i);if(e[o]=s.RelativeFilename||s.Filename,"Content"in s){let a=s.Content instanceof ArrayBuffer&&s.Content.byteLength>0,l=typeof s.Content=="string"&&s.Content!=="";if(a||l){let c=this.parseImage(n[i]);t[s.RelativeFilename||s.Filename]=c}}}}for(let n in e){let i=e[n];t[i]!==void 0?e[n]=t[i]:e[n]=e[n].split("\\").pop()}return e}parseImage(e){let t=e.Content,n=e.RelativeFilename||e.Filename,i=n.slice(n.lastIndexOf(".")+1).toLowerCase(),s;switch(i){case"bmp":s="image/bmp";break;case"jpg":case"jpeg":s="image/jpeg";break;case"png":s="image/png";break;case"tif":s="image/tiff";break;case"tga":this.manager.getHandler(".tga")===null&&console.warn("FBXLoader: TGA loader not found, skipping ",n),s="image/tga";break;default:console.warn('FBXLoader: Image type "'+i+'" is not supported.');return}if(typeof t=="string")return"data:"+s+";base64,"+t;{let o=new Uint8Array(t);return window.URL.createObjectURL(new Blob([o],{type:s}))}}parseTextures(e){let t=new Map;if("Texture"in Rt.Objects){let n=Rt.Objects.Texture;for(let i in n){let s=this.parseTexture(n[i],e);t.set(parseInt(i),s)}}return t}parseTexture(e,t){let n=this.loadTexture(e,t);n.ID=e.id,n.name=e.attrName;let i=e.WrapModeU,s=e.WrapModeV,o=i!==void 0?i.value:0,a=s!==void 0?s.value:0;if(n.wrapS=o===0?on:un,n.wrapT=a===0?on:un,"Scaling"in e){let l=e.Scaling.value;n.repeat.x=l[0],n.repeat.y=l[1]}if("Translation"in e){let l=e.Translation.value;n.offset.x=l[0],n.offset.y=l[1]}return n}loadTexture(e,t){let n=new Set(["tga","tif","tiff","exr","dds","hdr","ktx2"]),i=e.FileName.split(".").pop().toLowerCase(),s=n.has(i)?this.manager.getHandler(`.${i}`):this.textureLoader;if(!s)return console.warn(`FBXLoader: ${i.toUpperCase()} loader not found, creating placeholder texture for`,e.RelativeFilename),new qn;let o=s.path;o||s.setPath(this.textureLoader.path);let a=Tn.get(e.id).children,l;a!==void 0&&a.length>0&&t[a[0].ID]!==void 0&&(l=t[a[0].ID],(l.indexOf("blob:")===0||l.indexOf("data:")===0)&&s.setPath(void 0));let c=s.load(l);return s.setPath(o),c}parseMaterials(e){let t=new Map;if("Material"in Rt.Objects){let n=Rt.Objects.Material;for(let i in n){let s=this.parseMaterial(n[i],e);s!==null&&t.set(parseInt(i),s)}}return t}parseMaterial(e,t){let n=e.id,i=e.attrName,s=e.ShadingModel;if(typeof s=="object"&&(s=s.value),!Tn.has(n))return null;let o=this.parseParameters(e,t,n),a;switch(s.toLowerCase()){case"phong":a=new Zt;break;case"lambert":a=new go;break;default:console.warn('THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.',s),a=new Zt;break}return a.setValues(o),a.name=i,a}parseParameters(e,t,n){let i={};e.BumpFactor&&(i.bumpScale=e.BumpFactor.value),e.Diffuse?i.color=mt.toWorkingColorSpace(new He().fromArray(e.Diffuse.value),yt):e.DiffuseColor&&(e.DiffuseColor.type==="Color"||e.DiffuseColor.type==="ColorRGB")&&(i.color=mt.toWorkingColorSpace(new He().fromArray(e.DiffuseColor.value),yt)),e.DisplacementFactor&&(i.displacementScale=e.DisplacementFactor.value),e.Emissive?i.emissive=mt.toWorkingColorSpace(new He().fromArray(e.Emissive.value),yt):e.EmissiveColor&&(e.EmissiveColor.type==="Color"||e.EmissiveColor.type==="ColorRGB")&&(i.emissive=mt.toWorkingColorSpace(new He().fromArray(e.EmissiveColor.value),yt)),e.EmissiveFactor&&(i.emissiveIntensity=parseFloat(e.EmissiveFactor.value)),i.opacity=1-(e.TransparencyFactor?parseFloat(e.TransparencyFactor.value):0),(i.opacity===1||i.opacity===0)&&(i.opacity=e.Opacity?parseFloat(e.Opacity.value):null,i.opacity===null&&(i.opacity=1-(e.TransparentColor?parseFloat(e.TransparentColor.value[0]):0))),i.opacity<1&&(i.transparent=!0),e.ReflectionFactor&&(i.reflectivity=e.ReflectionFactor.value),e.Shininess&&(i.shininess=e.Shininess.value),e.Specular?i.specular=mt.toWorkingColorSpace(new He().fromArray(e.Specular.value),yt):e.SpecularColor&&e.SpecularColor.type==="Color"&&(i.specular=mt.toWorkingColorSpace(new He().fromArray(e.SpecularColor.value),yt));let s=this;return Tn.get(n).children.forEach(function(o){let a=o.relationship;switch(a){case"Bump":i.bumpMap=s.getTexture(t,o.ID);break;case"Maya|TEX_ao_map":i.aoMap=s.getTexture(t,o.ID);break;case"DiffuseColor":case"Maya|TEX_color_map":i.map=s.getTexture(t,o.ID),i.map!==void 0&&(i.map.colorSpace=yt);break;case"DisplacementColor":i.displacementMap=s.getTexture(t,o.ID);break;case"EmissiveColor":i.emissiveMap=s.getTexture(t,o.ID),i.emissiveMap!==void 0&&(i.emissiveMap.colorSpace=yt);break;case"NormalMap":case"Maya|TEX_normal_map":i.normalMap=s.getTexture(t,o.ID);break;case"ReflectionColor":i.envMap=s.getTexture(t,o.ID),i.envMap!==void 0&&(i.envMap.mapping=Ua,i.envMap.colorSpace=yt);break;case"SpecularColor":i.specularMap=s.getTexture(t,o.ID),i.specularMap!==void 0&&(i.specularMap.colorSpace=yt);break;case"TransparentColor":case"TransparencyFactor":i.alphaMap=s.getTexture(t,o.ID),i.transparent=!0;break;case"AmbientColor":case"ShininessExponent":case"SpecularFactor":case"VectorDisplacementColor":default:console.warn("THREE.FBXLoader: %s map is not supported in three.js, skipping texture.",a);break}}),i}getTexture(e,t){return"LayeredTexture"in Rt.Objects&&t in Rt.Objects.LayeredTexture&&(console.warn("THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer."),t=Tn.get(t).children[0].ID),e.get(t)}parseDeformers(){let e={},t={};if("Deformer"in Rt.Objects){let n=Rt.Objects.Deformer;for(let i in n){let s=n[i],o=Tn.get(parseInt(i));if(s.attrType==="Skin"){let a=this.parseSkeleton(o,n);a.ID=i,o.parents.length>1&&console.warn("THREE.FBXLoader: skeleton attached to more than one geometry is not supported."),a.geometryID=o.parents[0].ID,e[i]=a}else if(s.attrType==="BlendShape"){let a={id:i};a.rawTargets=this.parseMorphTargets(o,n),a.id=i,o.parents.length>1&&console.warn("THREE.FBXLoader: morph target attached to more than one geometry is not supported."),t[i]=a}}}return{skeletons:e,morphTargets:t}}parseSkeleton(e,t){let n=[];return e.children.forEach(function(i){let s=t[i.ID];if(s.attrType!=="Cluster")return;let o={ID:i.ID,indices:[],weights:[],transformLink:new Je().fromArray(s.TransformLink.a)};"Indexes"in s&&(o.indices=s.Indexes.a,o.weights=s.Weights.a),n.push(o)}),{rawBones:n,bones:[]}}parseMorphTargets(e,t){let n=[];for(let i=0;i1?o=a:a.length>0?o=a[0]:(o=new Zt({name:zt.DEFAULT_MATERIAL_NAME,color:13421772}),a.push(o)),"color"in s.attributes&&a.forEach(function(l){l.vertexColors=!0}),s.groups.length>0){let l=!1;for(let c=0,u=s.groups.length;c=a.length)&&(h.materialIndex=a.length,l=!0)}if(l){let c=new Zt;a.push(c)}}return s.FBX_Deformer?(i=new co(s,o),i.normalizeSkinWeights()):i=new Dt(s,o),i}createCurve(e,t){let n=e.children.reduce(function(s,o){return t.has(o.ID)&&(s=t.get(o.ID)),s},null),i=new jn({name:zt.DEFAULT_MATERIAL_NAME,color:3342591,linewidth:1});return new Rs(n,i)}getTransformData(e,t){let n={};"InheritType"in t&&(n.inheritType=parseInt(t.InheritType.value)),"RotationOrder"in t?n.eulerOrder=Gc(t.RotationOrder.value):n.eulerOrder=Gc(0),"Lcl_Translation"in t&&(n.translation=t.Lcl_Translation.value),"PreRotation"in t&&(n.preRotation=t.PreRotation.value),"Lcl_Rotation"in t&&(n.rotation=t.Lcl_Rotation.value),"PostRotation"in t&&(n.postRotation=t.PostRotation.value),"Lcl_Scaling"in t&&(n.scale=t.Lcl_Scaling.value),"ScalingOffset"in t&&(n.scalingOffset=t.ScalingOffset.value),"ScalingPivot"in t&&(n.scalingPivot=t.ScalingPivot.value),"RotationOffset"in t&&(n.rotationOffset=t.RotationOffset.value),"RotationPivot"in t&&(n.rotationPivot=t.RotationPivot.value),e.userData.transformData=n}setLookAtProperties(e,t){"LookAtProperty"in t&&Tn.get(e.ID).children.forEach(function(i){if(i.relationship==="LookAtProperty"){let s=Rt.Objects.Model[i.ID];if("Lcl_Translation"in s){let o=s.Lcl_Translation.value;e.target!==void 0?(e.target.position.fromArray(o),ai.add(e.target)):e.lookAt(new se().fromArray(o))}}})}bindSkeleton(e,t,n){let i=this.parsePoseNodes();for(let s in e){let o=e[s];Tn.get(parseInt(o.ID)).parents.forEach(function(l){if(t.has(l.ID)){let c=l.ID;Tn.get(c).parents.forEach(function(h){n.has(h.ID)&&n.get(h.ID).bind(new ho(o.bones),i[h.ID])})}})}}parsePoseNodes(){let e={};if("Pose"in Rt.Objects){let t=Rt.Objects.Pose;for(let n in t)if(t[n].attrType==="BindPose"&&t[n].NbPoseNodes>0){let i=t[n].PoseNode;Array.isArray(i)?i.forEach(function(s){e[s.Node]=new Je().fromArray(s.Matrix.a)}):e[i.Node]=new Je().fromArray(i.Matrix.a)}}return e}addGlobalSceneSettings(){if("GlobalSettings"in Rt){if("AmbientColor"in Rt.GlobalSettings){let e=Rt.GlobalSettings.AmbientColor.value,t=e[0],n=e[1],i=e[2];if(t!==0||n!==0||i!==0){let s=new He().setRGB(t,n,i,yt);ai.add(new or(s,1))}}"UnitScaleFactor"in Rt.GlobalSettings&&(ai.userData.unitScaleFactor=Rt.GlobalSettings.UnitScaleFactor.value)}}},gm=class{constructor(){this.negativeMaterialIndices=!1}parse(e){let t=new Map;if("Geometry"in Rt.Objects){let n=Rt.Objects.Geometry;for(let i in n){let s=Tn.get(parseInt(i)),o=this.parseGeometry(s,n[i],e);t.set(parseInt(i),o)}}return this.negativeMaterialIndices===!0&&console.warn("THREE.FBXLoader: The FBX file contains invalid (negative) material indices. The asset might not render as expected."),t}parseGeometry(e,t,n){switch(t.attrType){case"Mesh":return this.parseMeshGeometry(e,t,n);case"NurbsCurve":return this.parseNurbsGeometry(t)}}parseMeshGeometry(e,t,n){let i=n.skeletons,s=[],o=e.parents.map(function(h){return Rt.Objects.Model[h.ID]});if(o.length===0)return;let a=e.children.reduce(function(h,f){return i[f.ID]!==void 0&&(h=i[f.ID]),h},null);e.children.forEach(function(h){n.morphTargets[h.ID]!==void 0&&s.push(n.morphTargets[h.ID])});let l=o[0],c={};"RotationOrder"in l&&(c.eulerOrder=Gc(l.RotationOrder.value)),"InheritType"in l&&(c.inheritType=parseInt(l.InheritType.value)),"GeometricTranslation"in l&&(c.translation=l.GeometricTranslation.value),"GeometricRotation"in l&&(c.rotation=l.GeometricRotation.value),"GeometricScaling"in l&&(c.scale=l.GeometricScaling.value);let u=$y(c);return this.genGeometry(t,a,s,u)}genGeometry(e,t,n,i){let s=new It;e.attrName&&(s.name=e.attrName);let o=this.parseGeoNode(e,t),a=this.genBuffers(o),l=new We(a.vertex,3);if(l.applyMatrix4(i),s.setAttribute("position",l),a.colors.length>0&&s.setAttribute("color",new We(a.colors,3)),t&&(s.setAttribute("skinIndex",new lo(a.weightsIndices,4)),s.setAttribute("skinWeight",new We(a.vertexWeights,4)),s.FBX_Deformer=t),a.normal.length>0){let c=new gt().getNormalMatrix(i),u=new We(a.normal,3);u.applyNormalMatrix(c),s.setAttribute("normal",u)}if(a.uvs.forEach(function(c,u){let h=u===0?"uv":`uv${u}`;s.setAttribute(h,new We(a.uvs[u],2))}),o.material&&o.material.mappingType!=="AllSame"){let c=a.materialIndex[0],u=0;if(a.materialIndex.forEach(function(h,f){h!==c&&(s.addGroup(u,f-u,c),c=h,u=f)}),s.groups.length>0){let h=s.groups[s.groups.length-1],f=h.start+h.count;f!==a.materialIndex.length&&s.addGroup(f,a.materialIndex.length-f,c)}s.groups.length===0&&s.addGroup(0,a.materialIndex.length,a.materialIndex[0])}return this.addMorphTargets(s,e,n,i),s}parseGeoNode(e,t){let n={};if(n.vertexPositions=e.Vertices!==void 0?e.Vertices.a:[],n.vertexIndices=e.PolygonVertexIndex!==void 0?e.PolygonVertexIndex.a:[],e.LayerElementColor&&(n.color=this.parseVertexColors(e.LayerElementColor[0])),e.LayerElementMaterial&&(n.material=this.parseMaterialIndices(e.LayerElementMaterial[0])),e.LayerElementNormal&&(n.normal=this.parseNormals(e.LayerElementNormal[0])),e.LayerElementUV){n.uv=[];let i=0;for(;e.LayerElementUV[i];)e.LayerElementUV[i].UV&&n.uv.push(this.parseUVs(e.LayerElementUV[i])),i++}return n.weightTable={},t!==null&&(n.skeleton=t,t.rawBones.forEach(function(i,s){i.indices.forEach(function(o,a){n.weightTable[o]===void 0&&(n.weightTable[o]=[]),n.weightTable[o].push({id:s,weight:i.weights[a]})})})),n}genBuffers(e){let t={vertex:[],normal:[],colors:[],uvs:[],materialIndex:[],vertexWeights:[],weightsIndices:[]},n=0,i=0,s=!1,o=[],a=[],l=[],c=[],u=[],h=[],f=this;return e.vertexIndices.forEach(function(p,g){let y,d=!1;p<0&&(p=p^-1,d=!0);let m=[],_=[];if(o.push(p*3,p*3+1,p*3+2),e.color){let v=kf(g,n,p,e.color);l.push(v[0],v[1],v[2])}if(e.skeleton){if(e.weightTable[p]!==void 0&&e.weightTable[p].forEach(function(v){_.push(v.weight),m.push(v.id)}),_.length>4){s||(console.warn("THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights."),s=!0);let v=[0,0,0,0],x=[0,0,0,0];_.forEach(function(S,I){let A=S,C=m[I];x.forEach(function(E,b,R){if(A>E){R[b]=A,A=E;let N=v[b];v[b]=C,C=N}})}),m=v,_=x}for(;_.length<4;)_.push(0),m.push(0);for(let v=0;v<4;++v)u.push(_[v]),h.push(m[v])}if(e.normal){let v=kf(g,n,p,e.normal);a.push(v[0],v[1],v[2])}e.material&&e.material.mappingType!=="AllSame"&&(y=kf(g,n,p,e.material)[0],y<0&&(f.negativeMaterialIndices=!0,y=0)),e.uv&&e.uv.forEach(function(v,x){let S=kf(g,n,p,v);c[x]===void 0&&(c[x]=[]),c[x].push(S[0]),c[x].push(S[1])}),i++,d&&(f.genFace(t,e,o,y,a,l,c,u,h,i),n++,i=0,o=[],a=[],l=[],c=[],u=[],h=[])}),t}getNormalNewell(e){let t=new se(0,0,0);for(let n=0;n.5?new se(0,1,0):new se(0,0,1)).cross(t).normalize(),s=t.clone().cross(i).normalize();return{normal:t,tangent:i,bitangent:s}}flattenVertex(e,t,n){return new Pe(e.dot(t),e.dot(n))}genFace(e,t,n,i,s,o,a,l,c,u){let h;if(u>3){let f=[],p=t.baseVertexPositions||t.vertexPositions;for(let m=0;m1&&console.warn("THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.");let o=e.get(s[0].ID);n[i]={name:t[i].attrName,layer:o}}return n}addClip(e){let t=[],n=this;return e.layer.forEach(function(i){t=t.concat(n.generateTracks(i))}),new Ps(e.name,-1,t)}generateTracks(e){let t=[],n=new se,i=new se;if(e.transform&&e.transform.decompose(n,new an,i),n=n.toArray(),i=i.toArray(),e.T!==void 0&&Object.keys(e.T.curves).length>0){let s=this.generateVectorTrack(e.modelName,e.T.curves,n,"position");s!==void 0&&t.push(s)}if(e.R!==void 0&&Object.keys(e.R.curves).length>0){let s=this.generateRotationTrack(e.modelName,e.R.curves,e.preRotation,e.postRotation,e.eulerOrder);s!==void 0&&t.push(s)}if(e.S!==void 0&&Object.keys(e.S.curves).length>0){let s=this.generateVectorTrack(e.modelName,e.S.curves,i,"scale");s!==void 0&&t.push(s)}if(e.DeformPercent!==void 0){let s=this.generateMorphTrack(e);s!==void 0&&t.push(s)}return t}generateVectorTrack(e,t,n,i){let s=this.getTimesForAllAxes(t),o=this.getKeyframeTrackValues(s,t,n);return new Wi(e+"."+i,s,o)}generateRotationTrack(e,t,n,i,s){let o,a;if(t.x!==void 0&&t.y!==void 0&&t.z!==void 0){let f=this.interpolateRotations(t.x,t.y,t.z,s);o=f[0],a=f[1]}let l=Gc(0);n!==void 0&&(n=n.map(yn.degToRad),n.push(l),n=new vn().fromArray(n),n=new an().setFromEuler(n)),i!==void 0&&(i=i.map(yn.degToRad),i.push(l),i=new vn().fromArray(i),i=new an().setFromEuler(i).invert());let c=new an,u=new vn,h=[];if(!a||!o)return new Hi(e+".quaternion",[0],[0]);for(let f=0;f2&&new an().fromArray(h,(f-3)/3*4).dot(c)<0&&c.set(-c.x,-c.y,-c.z,-c.w),c.toArray(h,f/3*4);return new Hi(e+".quaternion",o,h)}generateMorphTrack(e){let t=e.DeformPercent.curves.morph,n=t.values.map(function(s){return s/100}),i=ai.getObjectByName(e.modelName).morphTargetDictionary[e.morphName];return new is(e.modelName+".morphTargetInfluences["+i+"]",t.times,n)}getTimesForAllAxes(e){let t=[];if(e.x!==void 0&&(t=t.concat(e.x.times)),e.y!==void 0&&(t=t.concat(e.y.times)),e.z!==void 0&&(t=t.concat(e.z.times)),t=t.sort(function(n,i){return n-i}),t.length>1){let n=1,i=t[0];for(let s=1;s=180||p[1]>=180||p[2]>=180){let y=Math.max(...p)/180,d=new vn(...c,i),m=new vn(...h,i),_=new an().setFromEuler(d),v=new an().setFromEuler(m);_.dot(v)&&v.set(-v.x,-v.y,-v.z,-v.w);let x=e.times[a-1],S=e.times[a]-x,I=new an,A=new vn;for(let C=0;C<1;C+=1/y)I.copy(_.clone().slerp(v.clone(),C)),s.push(x+C*S),A.setFromQuaternion(I,i),o.push(A.x),o.push(A.y),o.push(A.z)}else s.push(e.times[a]),o.push(yn.degToRad(e.values[a])),o.push(yn.degToRad(t.values[a])),o.push(yn.degToRad(n.values[a]))}return[s,o]}},xm=class{getPrevNode(){return this.nodeStack[this.currentIndent-2]}getCurrentNode(){return this.nodeStack[this.currentIndent-1]}getCurrentProp(){return this.currentProp}pushStack(e){this.nodeStack.push(e),this.currentIndent+=1}popStack(){this.nodeStack.pop(),this.currentIndent-=1}setCurrentProp(e,t){this.currentProp=e,this.currentPropName=t}parse(e){this.currentIndent=0,this.allNodes=new Vf,this.nodeStack=[],this.currentProp=[],this.currentPropName="";let t=this,n=e.split(/[\r\n]+/);return n.forEach(function(i,s){let o=i.match(/^[\s\t]*;/),a=i.match(/^[\s\t]*$/);if(o||a)return;let l=i.match("^\\t{"+t.currentIndent+"}(\\w+):(.*){",""),c=i.match("^\\t{"+t.currentIndent+"}(\\w+):[\\s\\t\\r\\n](.*)"),u=i.match("^\\t{"+(t.currentIndent-1)+"}}");l?t.parseNodeBegin(i,l):c?t.parseNodeProperty(i,c,n[++s]):u?t.popStack():i.match(/^[^\s\t}]/)&&t.parseNodePropertyContinued(i)}),this.allNodes}parseNodeBegin(e,t){let n=t[1].trim().replace(/^"/,"").replace(/"$/,""),i=t[2].split(",").map(function(l){return l.trim().replace(/^"/,"").replace(/"$/,"")}),s={name:n},o=this.parseNodeAttr(i),a=this.getCurrentNode();this.currentIndent===0?this.allNodes.add(n,s):n in a?(n==="PoseNode"?a.PoseNode.push(s):a[n].id!==void 0&&(a[n]={},a[n][a[n].id]=a[n]),o.id!==""&&(a[n][o.id]=s)):typeof o.id=="number"?(a[n]={},a[n][o.id]=s):n!=="Properties70"&&(n==="PoseNode"?a[n]=[s]:a[n]=s),typeof o.id=="number"&&(s.id=o.id),o.name!==""&&(s.attrName=o.name),o.type!==""&&(s.attrType=o.type),this.pushStack(s)}parseNodeAttr(e){let t=e[0];e[0]!==""&&(t=parseInt(e[0]),isNaN(t)&&(t=e[0]));let n="",i="";return e.length>1&&(n=e[1].replace(/^(\w+)::/,""),i=e[2]),{id:t,name:n,type:i}}parseNodeProperty(e,t,n){let i=t[1].replace(/^"/,"").replace(/"$/,"").trim(),s=t[2].replace(/^"/,"").replace(/"$/,"").trim();i==="Content"&&s===","&&(s=n.replace(/"/g,"").replace(/,$/,"").trim());let o=this.getCurrentNode();if(o.name==="Properties70"){this.parseNodeSpecialProperty(e,i,s);return}if(i==="C"){let l=s.split(",").slice(1),c=parseInt(l[0]),u=parseInt(l[1]),h=s.split(",").slice(3);h=h.map(function(f){return f.trim().replace(/^"/,"")}),i="connections",s=[c,u],gS(s,h),o[i]===void 0&&(o[i]=[])}i==="Node"&&(o.id=s),i in o&&Array.isArray(o[i])?o[i].push(s):i!=="a"?o[i]=s:o.a=s,this.setCurrentProp(o,i),i==="a"&&s.slice(-1)!==","&&(o.a=pm(s))}parseNodePropertyContinued(e){let t=this.getCurrentNode();t.a+=e,e.slice(-1)!==","&&(t.a=pm(t.a))}parseNodeSpecialProperty(e,t,n){let i=n.split('",').map(function(u){return u.trim().replace(/^\"/,"").replace(/\s/,"_")}),s=i[0],o=i[1],a=i[2],l=i[3],c=i[4];switch(o){case"int":case"enum":case"bool":case"ULongLong":case"double":case"Number":case"FieldOfView":c=parseFloat(c);break;case"Color":case"ColorRGB":case"Vector3D":case"Lcl_Translation":case"Lcl_Rotation":case"Lcl_Scaling":c=pm(c);break}this.getPrevNode()[s]={type:o,type2:a,flag:l,value:c},this.setCurrentProp(this.getPrevNode(),s)}},vm=class{parse(e){let t=new Gf(e);t.skip(23);let n=t.getUint32();if(n<6400)throw new Error("THREE.FBXLoader: FBX version not supported, FileVersion: "+n);let i=new Vf;for(;!this.endOfContent(t);){let s=this.parseNode(t,n);s!==null&&i.add(s.name,s)}return i}endOfContent(e){return e.size()%16===0?(e.getOffset()+160+16&-16)>=e.size():e.getOffset()+160+16>=e.size()}parseNode(e,t){let n={},i=t>=7500?e.getUint64():e.getUint32(),s=t>=7500?e.getUint64():e.getUint32();t>=7500?e.getUint64():e.getUint32();let o=e.getUint8(),a=e.getString(o);if(i===0)return null;let l=[];for(let f=0;f0?l[0]:"",u=l.length>1?l[1]:"",h=l.length>2?l[2]:"";for(n.singleProperty=s===1&&e.getOffset()===i;i>e.getOffset();){let f=this.parseNode(e,t);f!==null&&this.parseSubNode(a,n,f)}return n.propertyList=l,typeof c=="number"&&(n.id=c),u!==""&&(n.attrName=u),h!==""&&(n.attrType=h),a!==""&&(n.name=a),n}parseSubNode(e,t,n){if(n.singleProperty===!0){let i=n.propertyList[0];Array.isArray(i)?(t[n.name]=n,n.a=i):t[n.name]=i}else if(e==="Connections"&&n.name==="C"){let i=[];n.propertyList.forEach(function(s,o){o!==0&&i.push(s)}),t.connections===void 0&&(t.connections=[]),t.connections.push(i)}else if(n.name==="Properties70")Object.keys(n).forEach(function(s){t[s]=n[s]});else if(e==="Properties70"&&n.name==="P"){let i=n.propertyList[0],s=n.propertyList[1],o=n.propertyList[2],a=n.propertyList[3],l;i.indexOf("Lcl ")===0&&(i=i.replace("Lcl ","Lcl_")),s.indexOf("Lcl ")===0&&(s=s.replace("Lcl ","Lcl_")),s==="Color"||s==="ColorRGB"||s==="Vector"||s==="Vector3D"||s.indexOf("Lcl_")===0?l=[n.propertyList[4],n.propertyList[5],n.propertyList[6]]:l=n.propertyList[4],t[i]={type:s,type2:o,flag:a,value:l}}else t[n.name]===void 0?typeof n.id=="number"?(t[n.name]={},t[n.name][n.id]=n):t[n.name]=n:n.name==="PoseNode"?(Array.isArray(t[n.name])||(t[n.name]=[t[n.name]]),t[n.name].push(n)):t[n.name][n.id]===void 0&&(t[n.name][n.id]=n)}parseProperty(e){let t=e.getString(1),n;switch(t){case"C":return e.getBoolean();case"D":return e.getFloat64();case"F":return e.getFloat32();case"I":return e.getInt32();case"L":return e.getInt64();case"R":return n=e.getUint32(),e.getArrayBuffer(n);case"S":return n=e.getUint32(),e.getString(n);case"Y":return e.getInt16();case"b":case"c":case"d":case"f":case"i":case"l":let i=e.getUint32(),s=e.getUint32(),o=e.getUint32();if(s===0)switch(t){case"b":case"c":return e.getBooleanArray(i);case"d":return e.getFloat64Array(i);case"f":return e.getFloat32Array(i);case"i":return e.getInt32Array(i);case"l":return e.getInt64Array(i)}let a=jy(new Uint8Array(e.getArrayBuffer(o))),l=new Gf(a.buffer);switch(t){case"b":case"c":return l.getBooleanArray(i);case"d":return l.getFloat64Array(i);case"f":return l.getFloat32Array(i);case"i":return l.getInt32Array(i);case"l":return l.getInt64Array(i)}break;default:throw new Error("THREE.FBXLoader: Unknown property type "+t)}}},Gf=class{constructor(e,t){this.dv=new DataView(e),this.offset=0,this.littleEndian=t!==void 0?t:!0,this._textDecoder=new TextDecoder}getOffset(){return this.offset}size(){return this.dv.buffer.byteLength}skip(e){this.offset+=e}getBoolean(){return(this.getUint8()&1)===1}getBooleanArray(e){let t=[];for(let n=0;n=0&&(n=new Uint8Array(this.dv.buffer,t,i)),this._textDecoder.decode(n)}},Vf=class{add(e,t){this[e]=t}};function fS(r){let e="Kaydara FBX Binary \0";return r.byteLength>=e.length&&e===Qy(r,0,e.length)}function dS(r){let e=["K","a","y","d","a","r","a","\\","F","B","X","\\","B","i","n","a","r","y","\\","\\"],t=0;function n(i){let s=r[i-1];return r=r.slice(t+i),t++,s}for(let i=0;i0&&F.push(new Wi(X+".position",J,Ie)),Ne.length>0&&F.push(new Hi(X+".quaternion",J,Ne)),et.length>0&&F.push(new Wi(X+".scale",J,et)),F}function R(U,F,Z){let X,J=!0,Ie,Ne;for(Ie=0,Ne=U.length;Ie=0;){let X=U[F];if(X.value[Z]!==null)return X;F--}return null}function V(U,F,Z){for(;F>>0)+2);switch(Z=Z.toLowerCase(),Z){case"tga":F=Yn;break;default:F=ti}return F}function ne(U){let F=D(U.url),Z=F.profile.technique,X;switch(Z.type){case"phong":case"blinn":X=new Zt;break;case"lambert":X=new go;break;default:X=new Ii;break}X.name=U.name||"";function J(Ve,Ze=null){let lt=F.profile.samplers[Ve.id],Ue=null;if(lt!==void 0){let ot=F.profile.surfaces[lt.source];Ue=k(ot.init_from)}else console.warn("THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530)."),Ue=k(Ve.id);if(Ue!==null){let ot=Q(Ue);if(ot!==void 0){let it=ot.load(Ue),pt=Ve.extra;if(pt!==void 0&&pt.technique!==void 0&&c(pt.technique)===!1){let ht=pt.technique;it.wrapS=ht.wrapU?on:un,it.wrapT=ht.wrapV?on:un,it.offset.set(ht.offsetU||0,ht.offsetV||0),it.repeat.set(ht.repeatU||1,ht.repeatV||1)}else it.wrapS=on,it.wrapT=on;return Ze!==null&&(it.colorSpace=Ze),it}else return console.warn("THREE.ColladaLoader: Loader for texture %s not found.",Ue),null}else return console.warn("THREE.ColladaLoader: Couldn't create texture with ID:",Ve.id),null}let Ie=Z.parameters;for(let Ve in Ie){let Ze=Ie[Ve];switch(Ve){case"diffuse":Ze.color&&X.color.fromArray(Ze.color),Ze.texture&&(X.map=J(Ze.texture,yt));break;case"specular":Ze.color&&X.specular&&X.specular.fromArray(Ze.color),Ze.texture&&(X.specularMap=J(Ze.texture));break;case"bump":Ze.texture&&(X.normalMap=J(Ze.texture));break;case"ambient":Ze.texture&&(X.lightMap=J(Ze.texture,yt));break;case"shininess":Ze.float&&X.shininess&&(X.shininess=Ze.float);break;case"emission":Ze.color&&X.emissive&&X.emissive.fromArray(Ze.color),Ze.texture&&(X.emissiveMap=J(Ze.texture,yt));break}}mt.toWorkingColorSpace(X.color,yt),X.specular&&mt.toWorkingColorSpace(X.specular,yt),X.emissive&&mt.toWorkingColorSpace(X.emissive,yt);let Ne=Ie.transparent,et=Ie.transparency;if(et===void 0&&Ne&&(et={float:1}),Ne===void 0&&et&&(Ne={opaque:"A_ONE",data:{color:[1,1,1,1]}}),Ne&&et)if(Ne.data.texture)X.transparent=!0;else{let Ve=Ne.data.color;switch(Ne.opaque){case"A_ONE":X.opacity=Ve[3]*et.float;break;case"RGB_ZERO":X.opacity=1-Ve[0]*et.float;break;case"A_ZERO":X.opacity=1-Ve[3]*et.float;break;case"RGB_ONE":X.opacity=Ve[0]*et.float;break;default:console.warn('THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.',Ne.opaque)}X.opacity<1&&(X.transparent=!0)}if(Z.extra!==void 0&&Z.extra.technique!==void 0){let Ve=Z.extra.technique;for(let Ze in Ve){let lt=Ve[Ze];switch(Ze){case"double_sided":X.side=lt===1?Nn:wi;break;case"bump":X.normalMap=J(lt.texture),X.normalScale=new Pe(1,1);break}}}return X}function oe(U){return y(vt.materials[U],ne)}function he(U){let F={name:U.getAttribute("name")};for(let Z=0,X=U.childNodes.length;Z0?Ne+Ve:Ne;F.inputs[Ze]={id:Ie,offset:et},F.stride=Math.max(F.stride,et+1),Ne==="TEXCOORD"&&(F.hasUV=!0);break;case"vcount":F.vcount=o(J.textContent);break;case"p":F.p=o(J.textContent);break}}return F}function Oe(U){let F={};for(let Z=0;Z0&&F0&&Ue.setAttribute("position",new We(J.array,J.stride)),Ie.array.length>0&&Ue.setAttribute("normal",new We(Ie.array,Ie.stride)),Ve.array.length>0&&Ue.setAttribute("color",new We(Ve.array,Ve.stride)),Ne.array.length>0&&Ue.setAttribute("uv",new We(Ne.array,Ne.stride)),et.array.length>0&&Ue.setAttribute("uv1",new We(et.array,et.stride)),Ze.array.length>0&&Ue.setAttribute("skinIndex",new We(Ze.array,Ze.stride)),lt.array.length>0&&Ue.setAttribute("skinWeight",new We(lt.array,lt.stride)),X.data=Ue,X.type=U[0].type,X.materialKeys=ot,X}function Fe(U,F,Z,X,J=!1){let Ie=U.p,Ne=U.stride,et=U.vcount;function Ve(Ue){let ot=Ie[Ue+Z]*lt,it=ot+lt;for(;ot4)for(let ht=1,fn=pt-2;ht<=fn;ht++){let jt=Ue+Ne*0,Gt=Ue+Ne*ht,Vt=Ue+Ne*(ht+1);Ve(jt),Ve(Gt),Ve(Vt)}Ue+=Ne*pt}}else for(let Ue=0,ot=Ie.length;Ue=F.limits.max&&(F.static=!0),F.middlePosition=(F.limits.min+F.limits.max)/2,F}function Dr(U){let F={sid:U.getAttribute("sid"),name:U.getAttribute("name")||"",attachments:[],transforms:[]};for(let Z=0;Zit.limits.max||Ue()=>(e||(e={exports:{}},r(e.exports,e)),e.exports),ex=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.VERSION=void 0,r.VERSION="9.0.1"}),Wt=bt((exports,module)=>{"use strict";var __spreadArray=exports&&exports.__spreadArray||function(r,e){for(var t=0,n=e.length,i=r.length;t{(function(t,n){typeof define=="function"&&define.amd?define([],n):typeof e=="object"&&e.exports?e.exports=n():t.regexpToAst=n()})(typeof self<"u"?self:r,function(){function t(){}t.prototype.saveState=function(){return{idx:this.idx,input:this.input,groupIdx:this.groupIdx}},t.prototype.restoreState=function(d){this.idx=d.idx,this.input=d.input,this.groupIdx=d.groupIdx},t.prototype.pattern=function(d){this.idx=0,this.input=d,this.groupIdx=0,this.consumeChar("/");var m=this.disjunction();this.consumeChar("/");for(var _={type:"Flags",loc:{begin:this.idx,end:d.length},global:!1,ignoreCase:!1,multiLine:!1,unicode:!1,sticky:!1};this.isRegExpFlag();)switch(this.popChar()){case"g":l(_,"global");break;case"i":l(_,"ignoreCase");break;case"m":l(_,"multiLine");break;case"u":l(_,"unicode");break;case"y":l(_,"sticky");break}if(this.idx!==this.input.length)throw Error("Redundant input: "+this.input.substring(this.idx));return{type:"Pattern",flags:_,value:m,loc:this.loc(0)}},t.prototype.disjunction=function(){var d=[],m=this.idx;for(d.push(this.alternative());this.peekChar()==="|";)this.consumeChar("|"),d.push(this.alternative());return{type:"Disjunction",value:d,loc:this.loc(m)}},t.prototype.alternative=function(){for(var d=[],m=this.idx;this.isTerm();)d.push(this.term());return{type:"Alternative",value:d,loc:this.loc(m)}},t.prototype.term=function(){return this.isAssertion()?this.assertion():this.atom()},t.prototype.assertion=function(){var d=this.idx;switch(this.popChar()){case"^":return{type:"StartAnchor",loc:this.loc(d)};case"$":return{type:"EndAnchor",loc:this.loc(d)};case"\\":switch(this.popChar()){case"b":return{type:"WordBoundary",loc:this.loc(d)};case"B":return{type:"NonWordBoundary",loc:this.loc(d)}}throw Error("Invalid Assertion Escape");case"(":this.consumeChar("?");var m;switch(this.popChar()){case"=":m="Lookahead";break;case"!":m="NegativeLookahead";break}c(m);var _=this.disjunction();return this.consumeChar(")"),{type:m,value:_,loc:this.loc(d)}}u()},t.prototype.quantifier=function(d){var m,_=this.idx;switch(this.popChar()){case"*":m={atLeast:0,atMost:1/0};break;case"+":m={atLeast:1,atMost:1/0};break;case"?":m={atLeast:0,atMost:1};break;case"{":var v=this.integerIncludingZero();switch(this.popChar()){case"}":m={atLeast:v,atMost:v};break;case",":var x;this.isDigit()?(x=this.integerIncludingZero(),m={atLeast:v,atMost:x}):m={atLeast:v,atMost:1/0},this.consumeChar("}");break}if(d===!0&&m===void 0)return;c(m);break}if(!(d===!0&&m===void 0))return c(m),this.peekChar(0)==="?"?(this.consumeChar("?"),m.greedy=!1):m.greedy=!0,m.type="Quantifier",m.loc=this.loc(_),m},t.prototype.atom=function(){var d,m=this.idx;switch(this.peekChar()){case".":d=this.dotAll();break;case"\\":d=this.atomEscape();break;case"[":d=this.characterClass();break;case"(":d=this.group();break}return d===void 0&&this.isPatternCharacter()&&(d=this.patternCharacter()),c(d),d.loc=this.loc(m),this.isQuantifier()&&(d.quantifier=this.quantifier()),d},t.prototype.dotAll=function(){return this.consumeChar("."),{type:"Set",complement:!0,value:[o(` +`),o("\r"),o("\u2028"),o("\u2029")]}},t.prototype.atomEscape=function(){switch(this.consumeChar("\\"),this.peekChar()){case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":return this.decimalEscapeAtom();case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}},t.prototype.decimalEscapeAtom=function(){var d=this.positiveInteger();return{type:"GroupBackReference",value:d}},t.prototype.characterClassEscape=function(){var d,m=!1;switch(this.popChar()){case"d":d=f;break;case"D":d=f,m=!0;break;case"s":d=g;break;case"S":d=g,m=!0;break;case"w":d=p;break;case"W":d=p,m=!0;break}return c(d),{type:"Set",value:d,complement:m}},t.prototype.controlEscapeAtom=function(){var d;switch(this.popChar()){case"f":d=o("\f");break;case"n":d=o(` +`);break;case"r":d=o("\r");break;case"t":d=o(" ");break;case"v":d=o("\v");break}return c(d),{type:"Character",value:d}},t.prototype.controlLetterEscapeAtom=function(){this.consumeChar("c");var d=this.popChar();if(/[a-zA-Z]/.test(d)===!1)throw Error("Invalid ");var m=d.toUpperCase().charCodeAt(0)-64;return{type:"Character",value:m}},t.prototype.nulCharacterAtom=function(){return this.consumeChar("0"),{type:"Character",value:o("\0")}},t.prototype.hexEscapeSequenceAtom=function(){return this.consumeChar("x"),this.parseHexDigits(2)},t.prototype.regExpUnicodeEscapeSequenceAtom=function(){return this.consumeChar("u"),this.parseHexDigits(4)},t.prototype.identityEscapeAtom=function(){var d=this.popChar();return{type:"Character",value:o(d)}},t.prototype.classPatternCharacterAtom=function(){switch(this.peekChar()){case` +`:case"\r":case"\u2028":case"\u2029":case"\\":case"]":throw Error("TBD");default:var d=this.popChar();return{type:"Character",value:o(d)}}},t.prototype.characterClass=function(){var d=[],m=!1;for(this.consumeChar("["),this.peekChar(0)==="^"&&(this.consumeChar("^"),m=!0);this.isClassAtom();){var _=this.classAtom(),v=_.type==="Character";if(v&&this.isRangeDash()){this.consumeChar("-");var x=this.classAtom(),S=x.type==="Character";if(S){if(x.value<_.value)throw Error("Range out of order in character class");d.push({from:_.value,to:x.value})}else a(_.value,d),d.push(o("-")),a(x.value,d)}else a(_.value,d)}return this.consumeChar("]"),{type:"Set",complement:m,value:d}},t.prototype.classAtom=function(){switch(this.peekChar()){case"]":case` +`:case"\r":case"\u2028":case"\u2029":throw Error("TBD");case"\\":return this.classEscape();default:return this.classPatternCharacterAtom()}},t.prototype.classEscape=function(){switch(this.consumeChar("\\"),this.peekChar()){case"b":return this.consumeChar("b"),{type:"Character",value:o("\b")};case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}},t.prototype.group=function(){var d=!0;switch(this.consumeChar("("),this.peekChar(0)){case"?":this.consumeChar("?"),this.consumeChar(":"),d=!1;break;default:this.groupIdx++;break}var m=this.disjunction();this.consumeChar(")");var _={type:"Group",capturing:d,value:m};return d&&(_.idx=this.groupIdx),_},t.prototype.positiveInteger=function(){var d=this.popChar();if(s.test(d)===!1)throw Error("Expecting a positive integer");for(;i.test(this.peekChar(0));)d+=this.popChar();return parseInt(d,10)},t.prototype.integerIncludingZero=function(){var d=this.popChar();if(i.test(d)===!1)throw Error("Expecting an integer");for(;i.test(this.peekChar(0));)d+=this.popChar();return parseInt(d,10)},t.prototype.patternCharacter=function(){var d=this.popChar();switch(d){case` +`:case"\r":case"\u2028":case"\u2029":case"^":case"$":case"\\":case".":case"*":case"+":case"?":case"(":case")":case"[":case"|":throw Error("TBD");default:return{type:"Character",value:o(d)}}},t.prototype.isRegExpFlag=function(){switch(this.peekChar(0)){case"g":case"i":case"m":case"u":case"y":return!0;default:return!1}},t.prototype.isRangeDash=function(){return this.peekChar()==="-"&&this.isClassAtom(1)},t.prototype.isDigit=function(){return i.test(this.peekChar(0))},t.prototype.isClassAtom=function(d){switch(d===void 0&&(d=0),this.peekChar(d)){case"]":case` +`:case"\r":case"\u2028":case"\u2029":return!1;default:return!0}},t.prototype.isTerm=function(){return this.isAtom()||this.isAssertion()},t.prototype.isAtom=function(){if(this.isPatternCharacter())return!0;switch(this.peekChar(0)){case".":case"\\":case"[":case"(":return!0;default:return!1}},t.prototype.isAssertion=function(){switch(this.peekChar(0)){case"^":case"$":return!0;case"\\":switch(this.peekChar(1)){case"b":case"B":return!0;default:return!1}case"(":return this.peekChar(1)==="?"&&(this.peekChar(2)==="="||this.peekChar(2)==="!");default:return!1}},t.prototype.isQuantifier=function(){var d=this.saveState();try{return this.quantifier(!0)!==void 0}catch{return!1}finally{this.restoreState(d)}},t.prototype.isPatternCharacter=function(){switch(this.peekChar()){case"^":case"$":case"\\":case".":case"*":case"+":case"?":case"(":case")":case"[":case"|":case"/":case` +`:case"\r":case"\u2028":case"\u2029":return!1;default:return!0}},t.prototype.parseHexDigits=function(d){for(var m="",_=0;_=this.input.length)throw Error("Unexpected end of input");this.idx++},t.prototype.loc=function(d){return{begin:d,end:this.idx}};var n=/[0-9a-fA-F]/,i=/[0-9]/,s=/[1-9]/;function o(d){return d.charCodeAt(0)}function a(d,m){d.length!==void 0?d.forEach(function(_){m.push(_)}):m.push(d)}function l(d,m){if(d[m]===!0)throw"duplicate flag "+m;d[m]=!0}function c(d){if(d===void 0)throw Error("Internal Error - Should never get here!")}function u(){throw Error("Internal Error - Should never get here!")}var h,f=[];for(h=o("0");h<=o("9");h++)f.push(h);var p=[o("_")].concat(f);for(h=o("a");h<=o("z");h++)p.push(h);for(h=o("A");h<=o("Z");h++)p.push(h);var g=[o(" "),o("\f"),o(` +`),o("\r"),o(" "),o("\v"),o(" "),o("\xA0"),o("\u1680"),o("\u2000"),o("\u2001"),o("\u2002"),o("\u2003"),o("\u2004"),o("\u2005"),o("\u2006"),o("\u2007"),o("\u2008"),o("\u2009"),o("\u200A"),o("\u2028"),o("\u2029"),o("\u202F"),o("\u205F"),o("\u3000"),o("\uFEFF")];function y(){}return y.prototype.visitChildren=function(d){for(var m in d){var _=d[m];d.hasOwnProperty(m)&&(_.type!==void 0?this.visit(_):Array.isArray(_)&&_.forEach(function(v){this.visit(v)},this))}},y.prototype.visit=function(d){switch(d.type){case"Pattern":this.visitPattern(d);break;case"Flags":this.visitFlags(d);break;case"Disjunction":this.visitDisjunction(d);break;case"Alternative":this.visitAlternative(d);break;case"StartAnchor":this.visitStartAnchor(d);break;case"EndAnchor":this.visitEndAnchor(d);break;case"WordBoundary":this.visitWordBoundary(d);break;case"NonWordBoundary":this.visitNonWordBoundary(d);break;case"Lookahead":this.visitLookahead(d);break;case"NegativeLookahead":this.visitNegativeLookahead(d);break;case"Character":this.visitCharacter(d);break;case"Set":this.visitSet(d);break;case"Group":this.visitGroup(d);break;case"GroupBackReference":this.visitGroupBackReference(d);break;case"Quantifier":this.visitQuantifier(d);break}this.visitChildren(d)},y.prototype.visitPattern=function(d){},y.prototype.visitFlags=function(d){},y.prototype.visitDisjunction=function(d){},y.prototype.visitAlternative=function(d){},y.prototype.visitStartAnchor=function(d){},y.prototype.visitEndAnchor=function(d){},y.prototype.visitWordBoundary=function(d){},y.prototype.visitNonWordBoundary=function(d){},y.prototype.visitLookahead=function(d){},y.prototype.visitNegativeLookahead=function(d){},y.prototype.visitCharacter=function(d){},y.prototype.visitSet=function(d){},y.prototype.visitGroup=function(d){},y.prototype.visitGroupBackReference=function(d){},y.prototype.visitQuantifier=function(d){},{RegExpParser:t,BaseRegExpVisitor:y,VERSION:"0.5.0"}})}),Tm=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.clearRegExpParserCache=r.getRegExpAst=void 0;var e=_m(),t={},n=new e.RegExpParser;function i(o){var a=o.toString();if(t.hasOwnProperty(a))return t[a];var l=n.pattern(a);return t[a]=l,l}r.getRegExpAst=i;function s(){t={}}r.clearRegExpParserCache=s}),xS=bt(r=>{"use strict";var e=r&&r.__extends||function(){var y=function(d,m){return y=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(_,v){_.__proto__=v}||function(_,v){for(var x in v)Object.prototype.hasOwnProperty.call(v,x)&&(_[x]=v[x])},y(d,m)};return function(d,m){if(typeof m!="function"&&m!==null)throw new TypeError("Class extends value "+String(m)+" is not a constructor or null");y(d,m);function _(){this.constructor=d}d.prototype=m===null?Object.create(m):(_.prototype=m.prototype,new _)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.canMatchCharCode=r.firstCharOptimizedIndices=r.getOptimizedStartCodesIndices=r.failedOptimizationPrefixMsg=void 0;var t=_m(),n=Wt(),i=Tm(),s=tx(),o="Complement Sets are not supported for first char optimization";r.failedOptimizationPrefixMsg=`Unable to use "first char" lexer optimizations: +`;function a(y,d){d===void 0&&(d=!1);try{var m=i.getRegExpAst(y),_=l(m.value,{},m.flags.ignoreCase);return _}catch(x){if(x.message===o)d&&n.PRINT_WARNING(""+r.failedOptimizationPrefixMsg+(" Unable to optimize: < "+y.toString()+` > +`)+` Complement Sets cannot be automatically optimized. + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);else{var v="";d&&(v=` + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.`),n.PRINT_ERROR(r.failedOptimizationPrefixMsg+` +`+(" Failed parsing: < "+y.toString()+` > +`)+(" Using the regexp-to-ast library version: "+t.VERSION+` +`)+" Please open an issue at: https://github.com/bd82/regexp-to-ast/issues"+v)}}return[]}r.getOptimizedStartCodesIndices=a;function l(y,d,m){switch(y.type){case"Disjunction":for(var _=0;_=s.minOptimizationVal)for(var R=E.from>=s.minOptimizationVal?E.from:s.minOptimizationVal,N=E.to,B=s.charCodeToOptimizedIndex(R),V=s.charCodeToOptimizedIndex(N),ie=B;ie<=V;ie++)d[ie]=ie}}});break;case"Group":l(S.value,d,m);break;default:throw Error("Non Exhaustive Match")}var I=S.quantifier!==void 0&&S.quantifier.atLeast===0;if(S.type==="Group"&&f(S)===!1||S.type!=="Group"&&I===!1)break}break;default:throw Error("non exhaustive match!")}return n.values(d)}r.firstCharOptimizedIndices=l;function c(y,d,m){var _=s.charCodeToOptimizedIndex(y);d[_]=_,m===!0&&u(y,d)}function u(y,d){var m=String.fromCharCode(y),_=m.toUpperCase();if(_!==m){var v=s.charCodeToOptimizedIndex(_.charCodeAt(0));d[v]=v}else{var x=m.toLowerCase();if(x!==m){var v=s.charCodeToOptimizedIndex(x.charCodeAt(0));d[v]=v}}}function h(y,d){return n.find(y.value,function(m){if(typeof m=="number")return n.contains(d,m);var _=m;return n.find(d,function(v){return _.from<=v&&v<=_.to})!==void 0})}function f(y){return y.quantifier&&y.quantifier.atLeast===0?!0:y.value?n.isArray(y.value)?n.every(y.value,f):f(y.value):!1}var p=function(y){e(d,y);function d(m){var _=y.call(this)||this;return _.targetCharCodes=m,_.found=!1,_}return d.prototype.visitChildren=function(m){if(this.found!==!0){switch(m.type){case"Lookahead":this.visitLookahead(m);return;case"NegativeLookahead":this.visitNegativeLookahead(m);return}y.prototype.visitChildren.call(this,m)}},d.prototype.visitCharacter=function(m){n.contains(this.targetCharCodes,m.value)&&(this.found=!0)},d.prototype.visitSet=function(m){m.complement?h(m,this.targetCharCodes)===void 0&&(this.found=!0):h(m,this.targetCharCodes)!==void 0&&(this.found=!0)},d}(t.BaseRegExpVisitor);function g(y,d){if(d instanceof RegExp){var m=i.getRegExpAst(d),_=new p(y);return _.visit(m),_.found}else return n.find(d,function(v){return n.contains(y,v.charCodeAt(0))})!==void 0}r.canMatchCharCode=g}),tx=bt(r=>{"use strict";var e=r&&r.__extends||function(){var P=function(M,T){return P=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(k,j){k.__proto__=j}||function(k,j){for(var W in j)Object.prototype.hasOwnProperty.call(j,W)&&(k[W]=j[W])},P(M,T)};return function(M,T){if(typeof T!="function"&&T!==null)throw new TypeError("Class extends value "+String(T)+" is not a constructor or null");P(M,T);function k(){this.constructor=M}M.prototype=T===null?Object.create(T):(k.prototype=T.prototype,new k)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.charCodeToOptimizedIndex=r.minOptimizationVal=r.buildLineBreakIssueMessage=r.LineTerminatorOptimizedTester=r.isShortPattern=r.isCustomPattern=r.cloneEmptyGroups=r.performWarningRuntimeChecks=r.performRuntimeChecks=r.addStickyFlag=r.addStartOfInput=r.findUnreachablePatterns=r.findModesThatDoNotExist=r.findInvalidGroupType=r.findDuplicatePatterns=r.findUnsupportedFlags=r.findStartOfInputAnchor=r.findEmptyMatchRegExps=r.findEndOfInputAnchor=r.findInvalidPatterns=r.findMissingPatterns=r.validatePatterns=r.analyzeTokenTypes=r.enableSticky=r.disableSticky=r.SUPPORT_STICKY=r.MODES=r.DEFAULT_MODE=void 0;var t=_m(),n=Hf(),i=Wt(),s=xS(),o=Tm(),a="PATTERN";r.DEFAULT_MODE="defaultMode",r.MODES="modes",r.SUPPORT_STICKY=typeof new RegExp("(?:)").sticky=="boolean";function l(){r.SUPPORT_STICKY=!1}r.disableSticky=l;function c(){r.SUPPORT_STICKY=!0}r.enableSticky=c;function u(P,M){M=i.defaults(M,{useSticky:r.SUPPORT_STICKY,debug:!1,safeMode:!1,positionTracking:"full",lineTerminatorCharacters:["\r",` +`],tracer:function(O,D){return D()}});var T=M.tracer;T("initCharCodeToOptimizedIndexMap",function(){G()});var k;T("Reject Lexer.NA",function(){k=i.reject(P,function(O){return O[a]===n.Lexer.NA})});var j=!1,W;T("Transform Patterns",function(){j=!1,W=i.map(k,function(O){var D=O[a];if(i.isRegExp(D)){var z=D.source;return z.length===1&&z!=="^"&&z!=="$"&&z!=="."&&!D.ignoreCase?z:z.length===2&&z[0]==="\\"&&!i.contains(["d","D","s","S","t","r","n","t","0","c","b","B","f","v","w","W"],z[1])?z[1]:M.useSticky?N(D):R(D)}else{if(i.isFunction(D))return j=!0,{exec:D};if(i.has(D,"exec"))return j=!0,D;if(typeof D=="string"){if(D.length===1)return D;var Q=D.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&"),ne=new RegExp(Q);return M.useSticky?N(ne):R(ne)}else throw Error("non exhaustive match")}})});var re,ce,me,w,ae;T("misc mapping",function(){re=i.map(k,function(O){return O.tokenTypeIdx}),ce=i.map(k,function(O){var D=O.GROUP;if(D!==n.Lexer.SKIPPED){if(i.isString(D))return D;if(i.isUndefined(D))return!1;throw Error("non exhaustive match")}}),me=i.map(k,function(O){var D=O.LONGER_ALT;if(D){var z=i.indexOf(k,D);return z}}),w=i.map(k,function(O){return O.PUSH_MODE}),ae=i.map(k,function(O){return i.has(O,"POP_MODE")})});var pe;T("Line Terminator Handling",function(){var O=ye(M.lineTerminatorCharacters);pe=i.map(k,function(D){return!1}),M.positionTracking!=="onlyOffset"&&(pe=i.map(k,function(D){if(i.has(D,"LINE_BREAKS"))return D.LINE_BREAKS;if(q(D,O)===!1)return s.canMatchCharCode(O,D.PATTERN)}))});var ue,ee,$,ve;T("Misc Mapping #2",function(){ue=i.map(k,Y),ee=i.map(W,te),$=i.reduce(k,function(O,D){var z=D.GROUP;return i.isString(z)&&z!==n.Lexer.SKIPPED&&(O[z]=[]),O},{}),ve=i.map(W,function(O,D){return{pattern:W[D],longerAlt:me[D],canLineTerminator:pe[D],isCustom:ue[D],short:ee[D],group:ce[D],push:w[D],pop:ae[D],tokenTypeIdx:re[D],tokenType:k[D]}})});var H=!0,L=[];return M.safeMode||T("First Char Optimization",function(){L=i.reduce(k,function(O,D,z){if(typeof D.PATTERN=="string"){var Q=D.PATTERN.charCodeAt(0),ne=_e(Q);Te(O,ne,ve[z])}else if(i.isArray(D.START_CHARS_HINT)){var oe;i.forEach(D.START_CHARS_HINT,function(ge){var de=typeof ge=="string"?ge.charCodeAt(0):ge,Ee=_e(de);oe!==Ee&&(oe=Ee,Te(O,Ee,ve[z]))})}else if(i.isRegExp(D.PATTERN))if(D.PATTERN.unicode)H=!1,M.ensureOptimizations&&i.PRINT_ERROR(""+s.failedOptimizationPrefixMsg+(" Unable to analyze < "+D.PATTERN.toString()+` > pattern. +`)+` The regexp unicode flag is not currently supported by the regexp-to-ast library. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);else{var he=s.getOptimizedStartCodesIndices(D.PATTERN,M.ensureOptimizations);i.isEmpty(he)&&(H=!1),i.forEach(he,function(ge){Te(O,ge,ve[z])})}else M.ensureOptimizations&&i.PRINT_ERROR(""+s.failedOptimizationPrefixMsg+(" TokenType: <"+D.name+`> is using a custom token pattern without providing parameter. +`)+` This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`),H=!1;return O},[])}),T("ArrayPacking",function(){L=i.packArray(L)}),{emptyGroups:$,patternIdxToConfig:ve,charCodeToPatternIdxToConfig:L,hasCustom:j,canBeOptimized:H}}r.analyzeTokenTypes=u;function h(P,M){var T=[],k=p(P);T=T.concat(k.errors);var j=g(k.valid),W=j.valid;return T=T.concat(j.errors),T=T.concat(f(W)),T=T.concat(I(W)),T=T.concat(A(W,M)),T=T.concat(C(W)),T}r.validatePatterns=h;function f(P){var M=[],T=i.filter(P,function(k){return i.isRegExp(k[a])});return M=M.concat(d(T)),M=M.concat(v(T)),M=M.concat(x(T)),M=M.concat(S(T)),M=M.concat(m(T)),M}function p(P){var M=i.filter(P,function(j){return!i.has(j,a)}),T=i.map(M,function(j){return{message:"Token Type: ->"+j.name+"<- missing static 'PATTERN' property",type:n.LexerDefinitionErrorType.MISSING_PATTERN,tokenTypes:[j]}}),k=i.difference(P,M);return{errors:T,valid:k}}r.findMissingPatterns=p;function g(P){var M=i.filter(P,function(j){var W=j[a];return!i.isRegExp(W)&&!i.isFunction(W)&&!i.has(W,"exec")&&!i.isString(W)}),T=i.map(M,function(j){return{message:"Token Type: ->"+j.name+"<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",type:n.LexerDefinitionErrorType.INVALID_PATTERN,tokenTypes:[j]}}),k=i.difference(P,M);return{errors:T,valid:k}}r.findInvalidPatterns=g;var y=/[^\\][\$]/;function d(P){var M=function(j){e(W,j);function W(){var re=j!==null&&j.apply(this,arguments)||this;return re.found=!1,re}return W.prototype.visitEndAnchor=function(re){this.found=!0},W}(t.BaseRegExpVisitor),T=i.filter(P,function(j){var W=j[a];try{var re=o.getRegExpAst(W),ce=new M;return ce.visit(re),ce.found}catch{return y.test(W.source)}}),k=i.map(T,function(j){return{message:`Unexpected RegExp Anchor Error: + Token Type: ->`+j.name+`<- static 'PATTERN' cannot contain end of input anchor '$' + See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:n.LexerDefinitionErrorType.EOI_ANCHOR_FOUND,tokenTypes:[j]}});return k}r.findEndOfInputAnchor=d;function m(P){var M=i.filter(P,function(k){var j=k[a];return j.test("")}),T=i.map(M,function(k){return{message:"Token Type: ->"+k.name+"<- static 'PATTERN' must not match an empty string",type:n.LexerDefinitionErrorType.EMPTY_MATCH_PATTERN,tokenTypes:[k]}});return T}r.findEmptyMatchRegExps=m;var _=/[^\\[][\^]|^\^/;function v(P){var M=function(j){e(W,j);function W(){var re=j!==null&&j.apply(this,arguments)||this;return re.found=!1,re}return W.prototype.visitStartAnchor=function(re){this.found=!0},W}(t.BaseRegExpVisitor),T=i.filter(P,function(j){var W=j[a];try{var re=o.getRegExpAst(W),ce=new M;return ce.visit(re),ce.found}catch{return _.test(W.source)}}),k=i.map(T,function(j){return{message:`Unexpected RegExp Anchor Error: + Token Type: ->`+j.name+`<- static 'PATTERN' cannot contain start of input anchor '^' + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:n.LexerDefinitionErrorType.SOI_ANCHOR_FOUND,tokenTypes:[j]}});return k}r.findStartOfInputAnchor=v;function x(P){var M=i.filter(P,function(k){var j=k[a];return j instanceof RegExp&&(j.multiline||j.global)}),T=i.map(M,function(k){return{message:"Token Type: ->"+k.name+"<- static 'PATTERN' may NOT contain global('g') or multiline('m')",type:n.LexerDefinitionErrorType.UNSUPPORTED_FLAGS_FOUND,tokenTypes:[k]}});return T}r.findUnsupportedFlags=x;function S(P){var M=[],T=i.map(P,function(W){return i.reduce(P,function(re,ce){return W.PATTERN.source===ce.PATTERN.source&&!i.contains(M,ce)&&ce.PATTERN!==n.Lexer.NA&&(M.push(ce),re.push(ce)),re},[])});T=i.compact(T);var k=i.filter(T,function(W){return W.length>1}),j=i.map(k,function(W){var re=i.map(W,function(me){return me.name}),ce=i.first(W).PATTERN;return{message:"The same RegExp pattern ->"+ce+"<-"+("has been used in all of the following Token Types: "+re.join(", ")+" <-"),type:n.LexerDefinitionErrorType.DUPLICATE_PATTERNS_FOUND,tokenTypes:W}});return j}r.findDuplicatePatterns=S;function I(P){var M=i.filter(P,function(k){if(!i.has(k,"GROUP"))return!1;var j=k.GROUP;return j!==n.Lexer.SKIPPED&&j!==n.Lexer.NA&&!i.isString(j)}),T=i.map(M,function(k){return{message:"Token Type: ->"+k.name+"<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",type:n.LexerDefinitionErrorType.INVALID_GROUP_TYPE_FOUND,tokenTypes:[k]}});return T}r.findInvalidGroupType=I;function A(P,M){var T=i.filter(P,function(j){return j.PUSH_MODE!==void 0&&!i.contains(M,j.PUSH_MODE)}),k=i.map(T,function(j){var W="Token Type: ->"+j.name+"<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->"+j.PUSH_MODE+"<-which does not exist";return{message:W,type:n.LexerDefinitionErrorType.PUSH_MODE_DOES_NOT_EXIST,tokenTypes:[j]}});return k}r.findModesThatDoNotExist=A;function C(P){var M=[],T=i.reduce(P,function(k,j,W){var re=j.PATTERN;return re===n.Lexer.NA||(i.isString(re)?k.push({str:re,idx:W,tokenType:j}):i.isRegExp(re)&&b(re)&&k.push({str:re.source,idx:W,tokenType:j})),k},[]);return i.forEach(P,function(k,j){i.forEach(T,function(W){var re=W.str,ce=W.idx,me=W.tokenType;if(j"+k.name+"<-")+`in the lexer's definition. +See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;M.push({message:w,type:n.LexerDefinitionErrorType.UNREACHABLE_PATTERN,tokenTypes:[k,me]})}})}),M}r.findUnreachablePatterns=C;function E(P,M){if(i.isRegExp(M)){var T=M.exec(P);return T!==null&&T.index===0}else{if(i.isFunction(M))return M(P,0,[],{});if(i.has(M,"exec"))return M.exec(P,0,[],{});if(typeof M=="string")return M===P;throw Error("non exhaustive match")}}function b(P){var M=[".","\\","[","]","|","^","$","(",")","?","*","+","{"];return i.find(M,function(T){return P.source.indexOf(T)!==-1})===void 0}function R(P){var M=P.ignoreCase?"i":"";return new RegExp("^(?:"+P.source+")",M)}r.addStartOfInput=R;function N(P){var M=P.ignoreCase?"iy":"y";return new RegExp(""+P.source,M)}r.addStickyFlag=N;function B(P,M,T){var k=[];return i.has(P,r.DEFAULT_MODE)||k.push({message:"A MultiMode Lexer cannot be initialized without a <"+r.DEFAULT_MODE+`> property in its definition +`,type:n.LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),i.has(P,r.MODES)||k.push({message:"A MultiMode Lexer cannot be initialized without a <"+r.MODES+`> property in its definition +`,type:n.LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),i.has(P,r.MODES)&&i.has(P,r.DEFAULT_MODE)&&!i.has(P.modes,P.defaultMode)&&k.push({message:"A MultiMode Lexer cannot be initialized with a "+r.DEFAULT_MODE+": <"+P.defaultMode+`>which does not exist +`,type:n.LexerDefinitionErrorType.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),i.has(P,r.MODES)&&i.forEach(P.modes,function(j,W){i.forEach(j,function(re,ce){i.isUndefined(re)&&k.push({message:"A Lexer cannot be initialized using an undefined Token Type. Mode:"+("<"+W+"> at index: <"+ce+`> +`),type:n.LexerDefinitionErrorType.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED})})}),k}r.performRuntimeChecks=B;function V(P,M,T){var k=[],j=!1,W=i.compact(i.flatten(i.mapValues(P.modes,function(me){return me}))),re=i.reject(W,function(me){return me[a]===n.Lexer.NA}),ce=ye(T);return M&&i.forEach(re,function(me){var w=q(me,ce);if(w!==!1){var ae=le(me,w),pe={message:ae,type:w.issue,tokenType:me};k.push(pe)}else i.has(me,"LINE_BREAKS")?me.LINE_BREAKS===!0&&(j=!0):s.canMatchCharCode(ce,me.PATTERN)&&(j=!0)}),M&&!j&&k.push({message:`Warning: No LINE_BREAKS Found. + This Lexer has been defined to track line and column information, + But none of the Token Types can be identified as matching a line terminator. + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS + for details.`,type:n.LexerDefinitionErrorType.NO_LINE_BREAKS_FLAGS}),k}r.performWarningRuntimeChecks=V;function ie(P){var M={},T=i.keys(P);return i.forEach(T,function(k){var j=P[k];if(i.isArray(j))M[k]=[];else throw Error("non exhaustive match")}),M}r.cloneEmptyGroups=ie;function Y(P){var M=P.PATTERN;if(i.isRegExp(M))return!1;if(i.isFunction(M)||i.has(M,"exec"))return!0;if(i.isString(M))return!1;throw Error("non exhaustive match")}r.isCustomPattern=Y;function te(P){return i.isString(P)&&P.length===1?P.charCodeAt(0):!1}r.isShortPattern=te,r.LineTerminatorOptimizedTester={test:function(P){for(var M=P.length,T=this.lastIndex;T Token Type +`)+(" Root cause: "+M.errMsg+`. +`)+" For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR";if(M.issue===n.LexerDefinitionErrorType.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the option. +`+(" The problem is in the <"+P.name+`> Token Type +`)+" For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK";throw Error("non exhaustive match")}r.buildLineBreakIssueMessage=le;function ye(P){var M=i.map(P,function(T){return i.isString(T)&&T.length>0?T.charCodeAt(0):T});return M}function Te(P,M,T){P[M]===void 0?P[M]=[T]:P[M].push(T)}r.minOptimizationVal=256;var Ae=[];function _e(P){return P255?255+~~(P/255):P}}}),Vc=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.isTokenType=r.hasExtendingTokensTypesMapProperty=r.hasExtendingTokensTypesProperty=r.hasCategoriesProperty=r.hasShortKeyProperty=r.singleAssignCategoriesToksMap=r.assignCategoriesMapProp=r.assignCategoriesTokensProp=r.assignTokenDefaultProps=r.expandCategories=r.augmentTokenTypes=r.tokenIdxToClass=r.tokenShortNameIdx=r.tokenStructuredMatcherNoCategories=r.tokenStructuredMatcher=void 0;var e=Wt();function t(y,d){var m=y.tokenTypeIdx;return m===d.tokenTypeIdx?!0:d.isParent===!0&&d.categoryMatchesMap[m]===!0}r.tokenStructuredMatcher=t;function n(y,d){return y.tokenTypeIdx===d.tokenTypeIdx}r.tokenStructuredMatcherNoCategories=n,r.tokenShortNameIdx=1,r.tokenIdxToClass={};function i(y){var d=s(y);o(d),l(d),a(d),e.forEach(d,function(m){m.isParent=m.categoryMatches.length>0})}r.augmentTokenTypes=i;function s(y){for(var d=e.cloneArr(y),m=y,_=!0;_;){m=e.compact(e.flatten(e.map(m,function(x){return x.CATEGORIES})));var v=e.difference(m,d);d=d.concat(v),e.isEmpty(v)?_=!1:m=v}return d}r.expandCategories=s;function o(y){e.forEach(y,function(d){u(d)||(r.tokenIdxToClass[r.tokenShortNameIdx]=d,d.tokenTypeIdx=r.tokenShortNameIdx++),h(d)&&!e.isArray(d.CATEGORIES)&&(d.CATEGORIES=[d.CATEGORIES]),h(d)||(d.CATEGORIES=[]),f(d)||(d.categoryMatches=[]),p(d)||(d.categoryMatchesMap={})})}r.assignTokenDefaultProps=o;function a(y){e.forEach(y,function(d){d.categoryMatches=[],e.forEach(d.categoryMatchesMap,function(m,_){d.categoryMatches.push(r.tokenIdxToClass[_].tokenTypeIdx)})})}r.assignCategoriesTokensProp=a;function l(y){e.forEach(y,function(d){c([],d)})}r.assignCategoriesMapProp=l;function c(y,d){e.forEach(y,function(m){d.categoryMatchesMap[m.tokenTypeIdx]=!0}),e.forEach(d.CATEGORIES,function(m){var _=y.concat(d);e.contains(_,m)||c(_,m)})}r.singleAssignCategoriesToksMap=c;function u(y){return e.has(y,"tokenTypeIdx")}r.hasShortKeyProperty=u;function h(y){return e.has(y,"CATEGORIES")}r.hasCategoriesProperty=h;function f(y){return e.has(y,"categoryMatches")}r.hasExtendingTokensTypesProperty=f;function p(y){return e.has(y,"categoryMatchesMap")}r.hasExtendingTokensTypesMapProperty=p;function g(y){return e.has(y,"tokenTypeIdx")}r.isTokenType=g}),nx=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.defaultLexerErrorProvider=void 0,r.defaultLexerErrorProvider={buildUnableToPopLexerModeMessage:function(e){return"Unable to pop Lexer Mode after encountering Token ->"+e.image+"<- The Mode Stack is empty"},buildUnexpectedCharactersMessage:function(e,t,n,i,s){return"unexpected character: ->"+e.charAt(t)+"<- at offset: "+t+","+(" skipped "+n+" characters.")}}}),Hf=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.Lexer=r.LexerDefinitionErrorType=void 0;var e=tx(),t=Wt(),n=Vc(),i=nx(),s=Tm(),o;(function(c){c[c.MISSING_PATTERN=0]="MISSING_PATTERN",c[c.INVALID_PATTERN=1]="INVALID_PATTERN",c[c.EOI_ANCHOR_FOUND=2]="EOI_ANCHOR_FOUND",c[c.UNSUPPORTED_FLAGS_FOUND=3]="UNSUPPORTED_FLAGS_FOUND",c[c.DUPLICATE_PATTERNS_FOUND=4]="DUPLICATE_PATTERNS_FOUND",c[c.INVALID_GROUP_TYPE_FOUND=5]="INVALID_GROUP_TYPE_FOUND",c[c.PUSH_MODE_DOES_NOT_EXIST=6]="PUSH_MODE_DOES_NOT_EXIST",c[c.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE=7]="MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE",c[c.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY=8]="MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY",c[c.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST=9]="MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST",c[c.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED=10]="LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED",c[c.SOI_ANCHOR_FOUND=11]="SOI_ANCHOR_FOUND",c[c.EMPTY_MATCH_PATTERN=12]="EMPTY_MATCH_PATTERN",c[c.NO_LINE_BREAKS_FLAGS=13]="NO_LINE_BREAKS_FLAGS",c[c.UNREACHABLE_PATTERN=14]="UNREACHABLE_PATTERN",c[c.IDENTIFY_TERMINATOR=15]="IDENTIFY_TERMINATOR",c[c.CUSTOM_LINE_BREAK=16]="CUSTOM_LINE_BREAK"})(o=r.LexerDefinitionErrorType||(r.LexerDefinitionErrorType={}));var a={deferDefinitionErrorsHandling:!1,positionTracking:"full",lineTerminatorsPattern:/\n|\r\n?/g,lineTerminatorCharacters:[` +`,"\r"],ensureOptimizations:!1,safeMode:!1,errorMessageProvider:i.defaultLexerErrorProvider,traceInitPerf:!1,skipValidations:!1};Object.freeze(a);var l=function(){function c(u,h){var f=this;if(h===void 0&&(h=a),this.lexerDefinition=u,this.lexerDefinitionErrors=[],this.lexerDefinitionWarning=[],this.patternIdxToConfig={},this.charCodeToPatternIdxToConfig={},this.modes=[],this.emptyGroups={},this.config=void 0,this.trackStartLines=!0,this.trackEndLines=!0,this.hasCustom=!1,this.canModeBeOptimized={},typeof h=="boolean")throw Error(`The second argument to the Lexer constructor is now an ILexerConfig Object. +a boolean 2nd argument is no longer supported`);this.config=t.merge(a,h);var p=this.config.traceInitPerf;p===!0?(this.traceInitMaxIdent=1/0,this.traceInitPerf=!0):typeof p=="number"&&(this.traceInitMaxIdent=p,this.traceInitPerf=!0),this.traceInitIndent=-1,this.TRACE_INIT("Lexer Constructor",function(){var g,y=!0;f.TRACE_INIT("Lexer Config handling",function(){if(f.config.lineTerminatorsPattern===a.lineTerminatorsPattern)f.config.lineTerminatorsPattern=e.LineTerminatorOptimizedTester;else if(f.config.lineTerminatorCharacters===a.lineTerminatorCharacters)throw Error(`Error: Missing property on the Lexer config. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS`);if(h.safeMode&&h.ensureOptimizations)throw Error('"safeMode" and "ensureOptimizations" flags are mutually exclusive.');f.trackStartLines=/full|onlyStart/i.test(f.config.positionTracking),f.trackEndLines=/full/i.test(f.config.positionTracking),t.isArray(u)?(g={modes:{}},g.modes[e.DEFAULT_MODE]=t.cloneArr(u),g[e.DEFAULT_MODE]=e.DEFAULT_MODE):(y=!1,g=t.cloneObj(u))}),f.config.skipValidations===!1&&(f.TRACE_INIT("performRuntimeChecks",function(){f.lexerDefinitionErrors=f.lexerDefinitionErrors.concat(e.performRuntimeChecks(g,f.trackStartLines,f.config.lineTerminatorCharacters))}),f.TRACE_INIT("performWarningRuntimeChecks",function(){f.lexerDefinitionWarning=f.lexerDefinitionWarning.concat(e.performWarningRuntimeChecks(g,f.trackStartLines,f.config.lineTerminatorCharacters))})),g.modes=g.modes?g.modes:{},t.forEach(g.modes,function(v,x){g.modes[x]=t.reject(v,function(S){return t.isUndefined(S)})});var d=t.keys(g.modes);if(t.forEach(g.modes,function(v,x){f.TRACE_INIT("Mode: <"+x+"> processing",function(){if(f.modes.push(x),f.config.skipValidations===!1&&f.TRACE_INIT("validatePatterns",function(){f.lexerDefinitionErrors=f.lexerDefinitionErrors.concat(e.validatePatterns(v,d))}),t.isEmpty(f.lexerDefinitionErrors)){n.augmentTokenTypes(v);var S;f.TRACE_INIT("analyzeTokenTypes",function(){S=e.analyzeTokenTypes(v,{lineTerminatorCharacters:f.config.lineTerminatorCharacters,positionTracking:h.positionTracking,ensureOptimizations:h.ensureOptimizations,safeMode:h.safeMode,tracer:f.TRACE_INIT.bind(f)})}),f.patternIdxToConfig[x]=S.patternIdxToConfig,f.charCodeToPatternIdxToConfig[x]=S.charCodeToPatternIdxToConfig,f.emptyGroups=t.merge(f.emptyGroups,S.emptyGroups),f.hasCustom=S.hasCustom||f.hasCustom,f.canModeBeOptimized[x]=S.canBeOptimized}})}),f.defaultMode=g.defaultMode,!t.isEmpty(f.lexerDefinitionErrors)&&!f.config.deferDefinitionErrorsHandling){var m=t.map(f.lexerDefinitionErrors,function(v){return v.message}),_=m.join(`----------------------- +`);throw new Error(`Errors detected in definition of Lexer: +`+_)}t.forEach(f.lexerDefinitionWarning,function(v){t.PRINT_WARNING(v.message)}),f.TRACE_INIT("Choosing sub-methods implementations",function(){if(e.SUPPORT_STICKY?(f.chopInput=t.IDENTITY,f.match=f.matchWithTest):(f.updateLastIndex=t.NOOP,f.match=f.matchWithExec),y&&(f.handleModes=t.NOOP),f.trackStartLines===!1&&(f.computeNewColumn=t.IDENTITY),f.trackEndLines===!1&&(f.updateTokenEndLineColumnLocation=t.NOOP),/full/i.test(f.config.positionTracking))f.createTokenInstance=f.createFullToken;else if(/onlyStart/i.test(f.config.positionTracking))f.createTokenInstance=f.createStartOnlyToken;else if(/onlyOffset/i.test(f.config.positionTracking))f.createTokenInstance=f.createOffsetOnlyToken;else throw Error('Invalid config option: "'+f.config.positionTracking+'"');f.hasCustom?(f.addToken=f.addTokenUsingPush,f.handlePayload=f.handlePayloadWithCustom):(f.addToken=f.addTokenUsingMemberAccess,f.handlePayload=f.handlePayloadNoCustom)}),f.TRACE_INIT("Failed Optimization Warnings",function(){var v=t.reduce(f.canModeBeOptimized,function(x,S,I){return S===!1&&x.push(I),x},[]);if(h.ensureOptimizations&&!t.isEmpty(v))throw Error("Lexer Modes: < "+v.join(", ")+` > cannot be optimized. + Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode. + Or inspect the console log for details on how to resolve these issues.`)}),f.TRACE_INIT("clearRegExpParserCache",function(){s.clearRegExpParserCache()}),f.TRACE_INIT("toFastProperties",function(){t.toFastProperties(f)})})}return c.prototype.tokenize=function(u,h){if(h===void 0&&(h=this.defaultMode),!t.isEmpty(this.lexerDefinitionErrors)){var f=t.map(this.lexerDefinitionErrors,function(y){return y.message}),p=f.join(`----------------------- +`);throw new Error(`Unable to Tokenize because Errors detected in definition of Lexer: +`+p)}var g=this.tokenizeInternal(u,h);return g},c.prototype.tokenizeInternal=function(u,h){var f=this,p,g,y,d,m,_,v,x,S,I,A,C,E,b,R,N=u,B=N.length,V=0,ie=0,Y=this.hasCustom?0:Math.floor(u.length/10),te=new Array(Y),q=[],le=this.trackStartLines?1:void 0,ye=this.trackStartLines?1:void 0,Te=e.cloneEmptyGroups(this.emptyGroups),Ae=this.trackStartLines,_e=this.config.lineTerminatorsPattern,G=0,P=[],M=[],T=[],k=[];Object.freeze(k);var j=void 0;function W(){return P}function re(ge){var de=e.charCodeToOptimizedIndex(ge),Ee=M[de];return Ee===void 0?k:Ee}var ce=function(ge){if(T.length===1&&ge.tokenType.PUSH_MODE===void 0){var de=f.config.errorMessageProvider.buildUnableToPopLexerModeMessage(ge);q.push({offset:ge.startOffset,line:ge.startLine!==void 0?ge.startLine:void 0,column:ge.startColumn!==void 0?ge.startColumn:void 0,length:ge.image.length,message:de})}else{T.pop();var Ee=t.last(T);P=f.patternIdxToConfig[Ee],M=f.charCodeToPatternIdxToConfig[Ee],G=P.length;var Re=f.canModeBeOptimized[Ee]&&f.config.safeMode===!1;M&&Re?j=re:j=W}};function me(ge){T.push(ge),M=this.charCodeToPatternIdxToConfig[ge],P=this.patternIdxToConfig[ge],G=P.length,G=P.length;var de=this.canModeBeOptimized[ge]&&this.config.safeMode===!1;M&&de?j=re:j=W}me.call(this,h);for(var w;Vm.length&&(m=y,_=v,w=ve)}break}}if(m!==null){if(x=m.length,S=w.group,S!==void 0&&(I=w.tokenTypeIdx,A=this.createTokenInstance(m,V,I,w.tokenType,le,ye,x),this.handlePayload(A,_),S===!1?ie=this.addToken(te,ie,A):Te[S].push(A)),u=this.chopInput(u,x),V=V+x,ye=this.computeNewColumn(ye,x),Ae===!0&&w.canLineTerminator===!0){var L=0,O=void 0,D=void 0;_e.lastIndex=0;do O=_e.test(m),O===!0&&(D=_e.lastIndex-1,L++);while(O===!0);L!==0&&(le=le+L,ye=x-D,this.updateTokenEndLineColumnLocation(A,S,D,L,le,ye,x))}this.handleModes(w,ce,me,A)}else{for(var z=V,Q=le,ne=ye,oe=!1;!oe&&V <"+u+">");var p=t.timer(h),g=p.time,y=p.value,d=g>10?console.warn:console.log;return this.traceInitIndent time: "+g+"ms"),this.traceInitIndent--,y}else return h()},c.SKIPPED="This marks a skipped Token pattern, this means each token identified by it willbe consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.",c.NA=/NOT_APPLICABLE/,c}();r.Lexer=l}),Co=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.tokenMatcher=r.createTokenInstance=r.EOF=r.createToken=r.hasTokenLabel=r.tokenName=r.tokenLabel=void 0;var e=Wt(),t=Hf(),n=Vc();function i(x){return o(x)?x.LABEL:x.name}r.tokenLabel=i;function s(x){return x.name}r.tokenName=s;function o(x){return e.isString(x.LABEL)&&x.LABEL!==""}r.hasTokenLabel=o;var a="parent",l="categories",c="label",u="group",h="push_mode",f="pop_mode",p="longer_alt",g="line_breaks",y="start_chars_hint";function d(x){return m(x)}r.createToken=d;function m(x){var S=x.pattern,I={};if(I.name=x.name,e.isUndefined(S)||(I.PATTERN=S),e.has(x,a))throw`The parent property is no longer supported. +See: https://github.com/chevrotain/chevrotain/issues/564#issuecomment-349062346 for details.`;return e.has(x,l)&&(I.CATEGORIES=x[l]),n.augmentTokenTypes([I]),e.has(x,c)&&(I.LABEL=x[c]),e.has(x,u)&&(I.GROUP=x[u]),e.has(x,f)&&(I.POP_MODE=x[f]),e.has(x,h)&&(I.PUSH_MODE=x[h]),e.has(x,p)&&(I.LONGER_ALT=x[p]),e.has(x,g)&&(I.LINE_BREAKS=x[g]),e.has(x,y)&&(I.START_CHARS_HINT=x[y]),I}r.EOF=d({name:"EOF",pattern:t.Lexer.NA}),n.augmentTokenTypes([r.EOF]);function _(x,S,I,A,C,E,b,R){return{image:S,startOffset:I,endOffset:A,startLine:C,endLine:E,startColumn:b,endColumn:R,tokenTypeIdx:x.tokenTypeIdx,tokenType:x}}r.createTokenInstance=_;function v(x,S){return n.tokenStructuredMatcher(x,S)}r.tokenMatcher=v}),Li=bt(r=>{"use strict";var e=r&&r.__extends||function(){var m=function(_,v){return m=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(x,S){x.__proto__=S}||function(x,S){for(var I in S)Object.prototype.hasOwnProperty.call(S,I)&&(x[I]=S[I])},m(_,v)};return function(_,v){if(typeof v!="function"&&v!==null)throw new TypeError("Class extends value "+String(v)+" is not a constructor or null");m(_,v);function x(){this.constructor=_}_.prototype=v===null?Object.create(v):(x.prototype=v.prototype,new x)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.serializeProduction=r.serializeGrammar=r.Terminal=r.Alternation=r.RepetitionWithSeparator=r.Repetition=r.RepetitionMandatoryWithSeparator=r.RepetitionMandatory=r.Option=r.Alternative=r.Rule=r.NonTerminal=r.AbstractProduction=void 0;var t=Wt(),n=Co(),i=function(){function m(_){this._definition=_}return Object.defineProperty(m.prototype,"definition",{get:function(){return this._definition},set:function(_){this._definition=_},enumerable:!1,configurable:!0}),m.prototype.accept=function(_){_.visit(this),t.forEach(this.definition,function(v){v.accept(_)})},m}();r.AbstractProduction=i;var s=function(m){e(_,m);function _(v){var x=m.call(this,[])||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return Object.defineProperty(_.prototype,"definition",{get:function(){return this.referencedRule!==void 0?this.referencedRule.definition:[]},set:function(v){},enumerable:!1,configurable:!0}),_.prototype.accept=function(v){v.visit(this)},_}(i);r.NonTerminal=s;var o=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.orgText="",t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.Rule=o;var a=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.ignoreAmbiguities=!1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.Alternative=a;var l=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.Option=l;var c=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.RepetitionMandatory=c;var u=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.RepetitionMandatoryWithSeparator=u;var h=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.Repetition=h;var f=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return _}(i);r.RepetitionWithSeparator=f;var p=function(m){e(_,m);function _(v){var x=m.call(this,v.definition)||this;return x.idx=1,x.ignoreAmbiguities=!1,x.hasPredicates=!1,t.assign(x,t.pick(v,function(S){return S!==void 0})),x}return Object.defineProperty(_.prototype,"definition",{get:function(){return this._definition},set:function(v){this._definition=v},enumerable:!1,configurable:!0}),_}(i);r.Alternation=p;var g=function(){function m(_){this.idx=1,t.assign(this,t.pick(_,function(v){return v!==void 0}))}return m.prototype.accept=function(_){_.visit(this)},m}();r.Terminal=g;function y(m){return t.map(m,d)}r.serializeGrammar=y;function d(m){function _(S){return t.map(S,d)}if(m instanceof s)return{type:"NonTerminal",name:m.nonTerminalName,idx:m.idx};if(m instanceof a)return{type:"Alternative",definition:_(m.definition)};if(m instanceof l)return{type:"Option",idx:m.idx,definition:_(m.definition)};if(m instanceof c)return{type:"RepetitionMandatory",idx:m.idx,definition:_(m.definition)};if(m instanceof u)return{type:"RepetitionMandatoryWithSeparator",idx:m.idx,separator:d(new g({terminalType:m.separator})),definition:_(m.definition)};if(m instanceof f)return{type:"RepetitionWithSeparator",idx:m.idx,separator:d(new g({terminalType:m.separator})),definition:_(m.definition)};if(m instanceof h)return{type:"Repetition",idx:m.idx,definition:_(m.definition)};if(m instanceof p)return{type:"Alternation",idx:m.idx,definition:_(m.definition)};if(m instanceof g){var v={type:"Terminal",name:m.terminalType.name,label:n.tokenLabel(m.terminalType),idx:m.idx},x=m.terminalType.PATTERN;return m.terminalType.PATTERN&&(v.pattern=t.isRegExp(x)?x.source:x),v}else{if(m instanceof o)return{type:"Rule",name:m.name,orgText:m.orgText,definition:_(m.definition)};throw Error("non exhaustive match")}}r.serializeProduction=d}),Mm=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.RestWalker=void 0;var e=Wt(),t=Li(),n=function(){function s(){}return s.prototype.walk=function(o,a){var l=this;a===void 0&&(a=[]),e.forEach(o.definition,function(c,u){var h=e.drop(o.definition,u+1);if(c instanceof t.NonTerminal)l.walkProdRef(c,h,a);else if(c instanceof t.Terminal)l.walkTerminal(c,h,a);else if(c instanceof t.Alternative)l.walkFlat(c,h,a);else if(c instanceof t.Option)l.walkOption(c,h,a);else if(c instanceof t.RepetitionMandatory)l.walkAtLeastOne(c,h,a);else if(c instanceof t.RepetitionMandatoryWithSeparator)l.walkAtLeastOneSep(c,h,a);else if(c instanceof t.RepetitionWithSeparator)l.walkManySep(c,h,a);else if(c instanceof t.Repetition)l.walkMany(c,h,a);else if(c instanceof t.Alternation)l.walkOr(c,h,a);else throw Error("non exhaustive match")})},s.prototype.walkTerminal=function(o,a,l){},s.prototype.walkProdRef=function(o,a,l){},s.prototype.walkFlat=function(o,a,l){var c=a.concat(l);this.walk(o,c)},s.prototype.walkOption=function(o,a,l){var c=a.concat(l);this.walk(o,c)},s.prototype.walkAtLeastOne=function(o,a,l){var c=[new t.Option({definition:o.definition})].concat(a,l);this.walk(o,c)},s.prototype.walkAtLeastOneSep=function(o,a,l){var c=i(o,a,l);this.walk(o,c)},s.prototype.walkMany=function(o,a,l){var c=[new t.Option({definition:o.definition})].concat(a,l);this.walk(o,c)},s.prototype.walkManySep=function(o,a,l){var c=i(o,a,l);this.walk(o,c)},s.prototype.walkOr=function(o,a,l){var c=this,u=a.concat(l);e.forEach(o.definition,function(h){var f=new t.Alternative({definition:[h]});c.walk(f,u)})},s}();r.RestWalker=n;function i(s,o,a){var l=[new t.Option({definition:[new t.Terminal({terminalType:s.separator})].concat(s.definition)})],c=l.concat(o,a);return c}}),zc=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.GAstVisitor=void 0;var e=Li(),t=function(){function n(){}return n.prototype.visit=function(i){var s=i;switch(s.constructor){case e.NonTerminal:return this.visitNonTerminal(s);case e.Alternative:return this.visitAlternative(s);case e.Option:return this.visitOption(s);case e.RepetitionMandatory:return this.visitRepetitionMandatory(s);case e.RepetitionMandatoryWithSeparator:return this.visitRepetitionMandatoryWithSeparator(s);case e.RepetitionWithSeparator:return this.visitRepetitionWithSeparator(s);case e.Repetition:return this.visitRepetition(s);case e.Alternation:return this.visitAlternation(s);case e.Terminal:return this.visitTerminal(s);case e.Rule:return this.visitRule(s);default:throw Error("non exhaustive match")}},n.prototype.visitNonTerminal=function(i){},n.prototype.visitAlternative=function(i){},n.prototype.visitOption=function(i){},n.prototype.visitRepetition=function(i){},n.prototype.visitRepetitionMandatory=function(i){},n.prototype.visitRepetitionMandatoryWithSeparator=function(i){},n.prototype.visitRepetitionWithSeparator=function(i){},n.prototype.visitAlternation=function(i){},n.prototype.visitTerminal=function(i){},n.prototype.visitRule=function(i){},n}();r.GAstVisitor=t}),Wf=bt(r=>{"use strict";var e=r&&r.__extends||function(){var f=function(p,g){return f=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(y,d){y.__proto__=d}||function(y,d){for(var m in d)Object.prototype.hasOwnProperty.call(d,m)&&(y[m]=d[m])},f(p,g)};return function(p,g){if(typeof g!="function"&&g!==null)throw new TypeError("Class extends value "+String(g)+" is not a constructor or null");f(p,g);function y(){this.constructor=p}p.prototype=g===null?Object.create(g):(y.prototype=g.prototype,new y)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.collectMethods=r.DslMethodsCollectorVisitor=r.getProductionDslName=r.isBranchingProd=r.isOptionalProd=r.isSequenceProd=void 0;var t=Wt(),n=Li(),i=zc();function s(f){return f instanceof n.Alternative||f instanceof n.Option||f instanceof n.Repetition||f instanceof n.RepetitionMandatory||f instanceof n.RepetitionMandatoryWithSeparator||f instanceof n.RepetitionWithSeparator||f instanceof n.Terminal||f instanceof n.Rule}r.isSequenceProd=s;function o(f,p){p===void 0&&(p=[]);var g=f instanceof n.Option||f instanceof n.Repetition||f instanceof n.RepetitionWithSeparator;return g?!0:f instanceof n.Alternation?t.some(f.definition,function(y){return o(y,p)}):f instanceof n.NonTerminal&&t.contains(p,f)?!1:f instanceof n.AbstractProduction?(f instanceof n.NonTerminal&&p.push(f),t.every(f.definition,function(y){return o(y,p)})):!1}r.isOptionalProd=o;function a(f){return f instanceof n.Alternation}r.isBranchingProd=a;function l(f){if(f instanceof n.NonTerminal)return"SUBRULE";if(f instanceof n.Option)return"OPTION";if(f instanceof n.Alternation)return"OR";if(f instanceof n.RepetitionMandatory)return"AT_LEAST_ONE";if(f instanceof n.RepetitionMandatoryWithSeparator)return"AT_LEAST_ONE_SEP";if(f instanceof n.RepetitionWithSeparator)return"MANY_SEP";if(f instanceof n.Repetition)return"MANY";if(f instanceof n.Terminal)return"CONSUME";throw Error("non exhaustive match")}r.getProductionDslName=l;var c=function(f){e(p,f);function p(){var g=f!==null&&f.apply(this,arguments)||this;return g.separator="-",g.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]},g}return p.prototype.reset=function(){this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}},p.prototype.visitTerminal=function(g){var y=g.terminalType.name+this.separator+"Terminal";t.has(this.dslMethods,y)||(this.dslMethods[y]=[]),this.dslMethods[y].push(g)},p.prototype.visitNonTerminal=function(g){var y=g.nonTerminalName+this.separator+"Terminal";t.has(this.dslMethods,y)||(this.dslMethods[y]=[]),this.dslMethods[y].push(g)},p.prototype.visitOption=function(g){this.dslMethods.option.push(g)},p.prototype.visitRepetitionWithSeparator=function(g){this.dslMethods.repetitionWithSeparator.push(g)},p.prototype.visitRepetitionMandatory=function(g){this.dslMethods.repetitionMandatory.push(g)},p.prototype.visitRepetitionMandatoryWithSeparator=function(g){this.dslMethods.repetitionMandatoryWithSeparator.push(g)},p.prototype.visitRepetition=function(g){this.dslMethods.repetition.push(g)},p.prototype.visitAlternation=function(g){this.dslMethods.alternation.push(g)},p}(i.GAstVisitor);r.DslMethodsCollectorVisitor=c;var u=new c;function h(f){u.reset(),f.accept(u);var p=u.dslMethods;return u.reset(),p}r.collectMethods=h}),ix=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.firstForTerminal=r.firstForBranching=r.firstForSequence=r.first=void 0;var e=Wt(),t=Li(),n=Wf();function i(l){if(l instanceof t.NonTerminal)return i(l.referencedRule);if(l instanceof t.Terminal)return a(l);if(n.isSequenceProd(l))return s(l);if(n.isBranchingProd(l))return o(l);throw Error("non exhaustive match")}r.first=i;function s(l){for(var c=[],u=l.definition,h=0,f=u.length>h,p,g=!0;f&&g;)p=u[h],g=n.isOptionalProd(p),c=c.concat(i(p)),h=h+1,f=u.length>h;return e.uniq(c)}r.firstForSequence=s;function o(l){var c=e.map(l.definition,function(u){return i(u)});return e.uniq(e.flatten(c))}r.firstForBranching=o;function a(l){return[l.terminalType]}r.firstForTerminal=a}),rx=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.IN=void 0,r.IN="_~IN~_"}),vS=bt(r=>{"use strict";var e=r&&r.__extends||function(){var h=function(f,p){return h=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(g,y){g.__proto__=y}||function(g,y){for(var d in y)Object.prototype.hasOwnProperty.call(y,d)&&(g[d]=y[d])},h(f,p)};return function(f,p){if(typeof p!="function"&&p!==null)throw new TypeError("Class extends value "+String(p)+" is not a constructor or null");h(f,p);function g(){this.constructor=f}f.prototype=p===null?Object.create(p):(g.prototype=p.prototype,new g)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.buildInProdFollowPrefix=r.buildBetweenProdsFollowPrefix=r.computeAllProdsFollows=r.ResyncFollowsWalker=void 0;var t=Mm(),n=ix(),i=Wt(),s=rx(),o=Li(),a=function(h){e(f,h);function f(p){var g=h.call(this)||this;return g.topProd=p,g.follows={},g}return f.prototype.startWalking=function(){return this.walk(this.topProd),this.follows},f.prototype.walkTerminal=function(p,g,y){},f.prototype.walkProdRef=function(p,g,y){var d=c(p.referencedRule,p.idx)+this.topProd.name,m=g.concat(y),_=new o.Alternative({definition:m}),v=n.first(_);this.follows[d]=v},f}(t.RestWalker);r.ResyncFollowsWalker=a;function l(h){var f={};return i.forEach(h,function(p){var g=new a(p).startWalking();i.assign(f,g)}),f}r.computeAllProdsFollows=l;function c(h,f){return h.name+f+s.IN}r.buildBetweenProdsFollowPrefix=c;function u(h){var f=h.terminalType.name;return f+h.idx+s.IN}r.buildInProdFollowPrefix=u}),Xf=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.defaultGrammarValidatorErrorProvider=r.defaultGrammarResolverErrorProvider=r.defaultParserErrorProvider=void 0;var e=Co(),t=Wt(),n=Wt(),i=Li(),s=Wf();r.defaultParserErrorProvider={buildMismatchTokenMessage:function(o){var a=o.expected,l=o.actual,c=o.previous,u=o.ruleName,h=e.hasTokenLabel(a),f=h?"--> "+e.tokenLabel(a)+" <--":"token of type --> "+a.name+" <--",p="Expecting "+f+" but found --> '"+l.image+"' <--";return p},buildNotAllInputParsedMessage:function(o){var a=o.firstRedundant,l=o.ruleName;return"Redundant input, expecting EOF but found: "+a.image},buildNoViableAltMessage:function(o){var a=o.expectedPathsPerAlt,l=o.actual,c=o.previous,u=o.customUserDescription,h=o.ruleName,f="Expecting: ",p=n.first(l).image,g=` +but found: '`+p+"'";if(u)return f+u+g;var y=n.reduce(a,function(v,x){return v.concat(x)},[]),d=n.map(y,function(v){return"["+n.map(v,function(x){return e.tokenLabel(x)}).join(", ")+"]"}),m=n.map(d,function(v,x){return" "+(x+1)+". "+v}),_=`one of these possible Token sequences: +`+m.join(` +`);return f+_+g},buildEarlyExitMessage:function(o){var a=o.expectedIterationPaths,l=o.actual,c=o.customUserDescription,u=o.ruleName,h="Expecting: ",f=n.first(l).image,p=` +but found: '`+f+"'";if(c)return h+c+p;var g=n.map(a,function(d){return"["+n.map(d,function(m){return e.tokenLabel(m)}).join(",")+"]"}),y=`expecting at least one iteration which starts with one of these possible Token sequences:: + `+("<"+g.join(" ,")+">");return h+y+p}},Object.freeze(r.defaultParserErrorProvider),r.defaultGrammarResolverErrorProvider={buildRuleNotFoundError:function(o,a){var l="Invalid grammar, reference to a rule which is not defined: ->"+a.nonTerminalName+`<- +inside top level rule: ->`+o.name+"<-";return l}},r.defaultGrammarValidatorErrorProvider={buildDuplicateFoundError:function(o,a){function l(d){return d instanceof i.Terminal?d.terminalType.name:d instanceof i.NonTerminal?d.nonTerminalName:""}var c=o.name,u=n.first(a),h=u.idx,f=s.getProductionDslName(u),p=l(u),g=h>0,y="->"+f+(g?h:"")+"<- "+(p?"with argument: ->"+p+"<-":"")+` + appears more than once (`+a.length+" times) in the top level rule: ->"+c+`<-. + For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES + `;return y=y.replace(/[ \t]+/g," "),y=y.replace(/\s\s+/g,` +`),y},buildNamespaceConflictError:function(o){var a=`Namespace conflict found in grammar. +`+("The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <"+o.name+`>. +`)+`To resolve this make sure each Terminal and Non-Terminal names are unique +This is easy to accomplish by using the convention that Terminal names start with an uppercase letter +and Non-Terminal names start with a lower case letter.`;return a},buildAlternationPrefixAmbiguityError:function(o){var a=n.map(o.prefixPath,function(u){return e.tokenLabel(u)}).join(", "),l=o.alternation.idx===0?"":o.alternation.idx,c="Ambiguous alternatives: <"+o.ambiguityIndices.join(" ,")+`> due to common lookahead prefix +`+("in inside <"+o.topLevelRule.name+`> Rule, +`)+("<"+a+`> may appears as a prefix path in all these alternatives. +`)+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX +For Further details.`;return c},buildAlternationAmbiguityError:function(o){var a=n.map(o.prefixPath,function(u){return e.tokenLabel(u)}).join(", "),l=o.alternation.idx===0?"":o.alternation.idx,c="Ambiguous Alternatives Detected: <"+o.ambiguityIndices.join(" ,")+"> in "+(" inside <"+o.topLevelRule.name+`> Rule, +`)+("<"+a+`> may appears as a prefix path in all these alternatives. +`);return c=c+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,c},buildEmptyRepetitionError:function(o){var a=s.getProductionDslName(o.repetition);o.repetition.idx!==0&&(a+=o.repetition.idx);var l="The repetition <"+a+"> within Rule <"+o.topLevelRule.name+`> can never consume any tokens. +This could lead to an infinite loop.`;return l},buildTokenNameError:function(o){return"deprecated"},buildEmptyAlternationError:function(o){var a="Ambiguous empty alternative: <"+(o.emptyChoiceIdx+1)+">"+(" in inside <"+o.topLevelRule.name+`> Rule. +`)+"Only the last alternative may be an empty alternative.";return a},buildTooManyAlternativesError:function(o){var a=`An Alternation cannot have more than 256 alternatives: +`+(" inside <"+o.topLevelRule.name+`> Rule. + has `+(o.alternation.definition.length+1)+" alternatives.");return a},buildLeftRecursionError:function(o){var a=o.topLevelRule.name,l=t.map(o.leftRecursionPath,function(h){return h.name}),c=a+" --> "+l.concat([a]).join(" --> "),u=`Left Recursion found in grammar. +`+("rule: <"+a+`> can be invoked from itself (directly or indirectly) +`)+(`without consuming any Tokens. The grammar path that causes this is: + `+c+` +`)+` To fix this refactor your grammar to remove the left recursion. +see: https://en.wikipedia.org/wiki/LL_parser#Left_Factoring.`;return u},buildInvalidRuleNameError:function(o){return"deprecated"},buildDuplicateRuleNameError:function(o){var a;o.topLevelRule instanceof i.Rule?a=o.topLevelRule.name:a=o.topLevelRule;var l="Duplicate definition, rule: ->"+a+"<- is already defined in the grammar: ->"+o.grammarName+"<-";return l}}}),_S=bt(r=>{"use strict";var e=r&&r.__extends||function(){var a=function(l,c){return a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(u,h){u.__proto__=h}||function(u,h){for(var f in h)Object.prototype.hasOwnProperty.call(h,f)&&(u[f]=h[f])},a(l,c)};return function(l,c){if(typeof c!="function"&&c!==null)throw new TypeError("Class extends value "+String(c)+" is not a constructor or null");a(l,c);function u(){this.constructor=l}l.prototype=c===null?Object.create(c):(u.prototype=c.prototype,new u)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.GastRefResolverVisitor=r.resolveGrammar=void 0;var t=Xi(),n=Wt(),i=zc();function s(a,l){var c=new o(a,l);return c.resolveRefs(),c.errors}r.resolveGrammar=s;var o=function(a){e(l,a);function l(c,u){var h=a.call(this)||this;return h.nameToTopRule=c,h.errMsgProvider=u,h.errors=[],h}return l.prototype.resolveRefs=function(){var c=this;n.forEach(n.values(this.nameToTopRule),function(u){c.currTopLevel=u,u.accept(c)})},l.prototype.visitNonTerminal=function(c){var u=this.nameToTopRule[c.nonTerminalName];if(u)c.referencedRule=u;else{var h=this.errMsgProvider.buildRuleNotFoundError(this.currTopLevel,c);this.errors.push({message:h,type:t.ParserDefinitionErrorType.UNRESOLVED_SUBRULE_REF,ruleName:this.currTopLevel.name,unresolvedRefName:c.nonTerminalName})}},l}(i.GAstVisitor);r.GastRefResolverVisitor=o}),qf=bt(r=>{"use strict";var e=r&&r.__extends||function(){var d=function(m,_){return d=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(v,x){v.__proto__=x}||function(v,x){for(var S in x)Object.prototype.hasOwnProperty.call(x,S)&&(v[S]=x[S])},d(m,_)};return function(m,_){if(typeof _!="function"&&_!==null)throw new TypeError("Class extends value "+String(_)+" is not a constructor or null");d(m,_);function v(){this.constructor=m}m.prototype=_===null?Object.create(_):(v.prototype=_.prototype,new v)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.nextPossibleTokensAfter=r.possiblePathsFrom=r.NextTerminalAfterAtLeastOneSepWalker=r.NextTerminalAfterAtLeastOneWalker=r.NextTerminalAfterManySepWalker=r.NextTerminalAfterManyWalker=r.AbstractNextTerminalAfterProductionWalker=r.NextAfterTokenWalker=r.AbstractNextPossibleTokensWalker=void 0;var t=Mm(),n=Wt(),i=ix(),s=Li(),o=function(d){e(m,d);function m(_,v){var x=d.call(this)||this;return x.topProd=_,x.path=v,x.possibleTokTypes=[],x.nextProductionName="",x.nextProductionOccurrence=0,x.found=!1,x.isAtEndOfPath=!1,x}return m.prototype.startWalking=function(){if(this.found=!1,this.path.ruleStack[0]!==this.topProd.name)throw Error("The path does not start with the walker's top Rule!");return this.ruleStack=n.cloneArr(this.path.ruleStack).reverse(),this.occurrenceStack=n.cloneArr(this.path.occurrenceStack).reverse(),this.ruleStack.pop(),this.occurrenceStack.pop(),this.updateExpectedNext(),this.walk(this.topProd),this.possibleTokTypes},m.prototype.walk=function(_,v){v===void 0&&(v=[]),this.found||d.prototype.walk.call(this,_,v)},m.prototype.walkProdRef=function(_,v,x){if(_.referencedRule.name===this.nextProductionName&&_.idx===this.nextProductionOccurrence){var S=v.concat(x);this.updateExpectedNext(),this.walk(_.referencedRule,S)}},m.prototype.updateExpectedNext=function(){n.isEmpty(this.ruleStack)?(this.nextProductionName="",this.nextProductionOccurrence=0,this.isAtEndOfPath=!0):(this.nextProductionName=this.ruleStack.pop(),this.nextProductionOccurrence=this.occurrenceStack.pop())},m}(t.RestWalker);r.AbstractNextPossibleTokensWalker=o;var a=function(d){e(m,d);function m(_,v){var x=d.call(this,_,v)||this;return x.path=v,x.nextTerminalName="",x.nextTerminalOccurrence=0,x.nextTerminalName=x.path.lastTok.name,x.nextTerminalOccurrence=x.path.lastTokOccurrence,x}return m.prototype.walkTerminal=function(_,v,x){if(this.isAtEndOfPath&&_.terminalType.name===this.nextTerminalName&&_.idx===this.nextTerminalOccurrence&&!this.found){var S=v.concat(x),I=new s.Alternative({definition:S});this.possibleTokTypes=i.first(I),this.found=!0}},m}(o);r.NextAfterTokenWalker=a;var l=function(d){e(m,d);function m(_,v){var x=d.call(this)||this;return x.topRule=_,x.occurrence=v,x.result={token:void 0,occurrence:void 0,isEndOfRule:void 0},x}return m.prototype.startWalking=function(){return this.walk(this.topRule),this.result},m}(t.RestWalker);r.AbstractNextTerminalAfterProductionWalker=l;var c=function(d){e(m,d);function m(){return d!==null&&d.apply(this,arguments)||this}return m.prototype.walkMany=function(_,v,x){if(_.idx===this.occurrence){var S=n.first(v.concat(x));this.result.isEndOfRule=S===void 0,S instanceof s.Terminal&&(this.result.token=S.terminalType,this.result.occurrence=S.idx)}else d.prototype.walkMany.call(this,_,v,x)},m}(l);r.NextTerminalAfterManyWalker=c;var u=function(d){e(m,d);function m(){return d!==null&&d.apply(this,arguments)||this}return m.prototype.walkManySep=function(_,v,x){if(_.idx===this.occurrence){var S=n.first(v.concat(x));this.result.isEndOfRule=S===void 0,S instanceof s.Terminal&&(this.result.token=S.terminalType,this.result.occurrence=S.idx)}else d.prototype.walkManySep.call(this,_,v,x)},m}(l);r.NextTerminalAfterManySepWalker=u;var h=function(d){e(m,d);function m(){return d!==null&&d.apply(this,arguments)||this}return m.prototype.walkAtLeastOne=function(_,v,x){if(_.idx===this.occurrence){var S=n.first(v.concat(x));this.result.isEndOfRule=S===void 0,S instanceof s.Terminal&&(this.result.token=S.terminalType,this.result.occurrence=S.idx)}else d.prototype.walkAtLeastOne.call(this,_,v,x)},m}(l);r.NextTerminalAfterAtLeastOneWalker=h;var f=function(d){e(m,d);function m(){return d!==null&&d.apply(this,arguments)||this}return m.prototype.walkAtLeastOneSep=function(_,v,x){if(_.idx===this.occurrence){var S=n.first(v.concat(x));this.result.isEndOfRule=S===void 0,S instanceof s.Terminal&&(this.result.token=S.terminalType,this.result.occurrence=S.idx)}else d.prototype.walkAtLeastOneSep.call(this,_,v,x)},m}(l);r.NextTerminalAfterAtLeastOneSepWalker=f;function p(d,m,_){_===void 0&&(_=[]),_=n.cloneArr(_);var v=[],x=0;function S(E){return E.concat(n.drop(d,x+1))}function I(E){var b=p(S(E),m,_);return v.concat(b)}for(;_.length=0;j--){var W=te.definition[j],re={idx:V,def:W.definition.concat(n.drop(B)),ruleStack:ie,occurrenceStack:Y};R.push(re),R.push(I)}else if(te instanceof s.Alternative)R.push({idx:V,def:te.definition.concat(n.drop(B)),ruleStack:ie,occurrenceStack:Y});else if(te instanceof s.Rule)R.push(y(te,V,ie,Y));else throw Error("non exhaustive match")}}return b}r.nextPossibleTokensAfter=g;function y(d,m,_,v){var x=n.cloneArr(_);x.push(d.name);var S=n.cloneArr(v);return S.push(1),{idx:m,def:d.definition,ruleStack:x,occurrenceStack:S}}}),jf=bt(r=>{"use strict";var e=r&&r.__extends||function(){var E=function(b,R){return E=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(N,B){N.__proto__=B}||function(N,B){for(var V in B)Object.prototype.hasOwnProperty.call(B,V)&&(N[V]=B[V])},E(b,R)};return function(b,R){if(typeof R!="function"&&R!==null)throw new TypeError("Class extends value "+String(R)+" is not a constructor or null");E(b,R);function N(){this.constructor=b}b.prototype=R===null?Object.create(R):(N.prototype=R.prototype,new N)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.areTokenCategoriesNotUsed=r.isStrictPrefixOfPath=r.containsPath=r.getLookaheadPathsForOptionalProd=r.getLookaheadPathsForOr=r.lookAheadSequenceFromAlternatives=r.buildSingleAlternativeLookaheadFunction=r.buildAlternativesLookAheadFunc=r.buildLookaheadFuncForOptionalProd=r.buildLookaheadFuncForOr=r.getProdType=r.PROD_TYPE=void 0;var t=Wt(),n=qf(),i=Mm(),s=Vc(),o=Li(),a=zc(),l;(function(E){E[E.OPTION=0]="OPTION",E[E.REPETITION=1]="REPETITION",E[E.REPETITION_MANDATORY=2]="REPETITION_MANDATORY",E[E.REPETITION_MANDATORY_WITH_SEPARATOR=3]="REPETITION_MANDATORY_WITH_SEPARATOR",E[E.REPETITION_WITH_SEPARATOR=4]="REPETITION_WITH_SEPARATOR",E[E.ALTERNATION=5]="ALTERNATION"})(l=r.PROD_TYPE||(r.PROD_TYPE={}));function c(E){if(E instanceof o.Option)return l.OPTION;if(E instanceof o.Repetition)return l.REPETITION;if(E instanceof o.RepetitionMandatory)return l.REPETITION_MANDATORY;if(E instanceof o.RepetitionMandatoryWithSeparator)return l.REPETITION_MANDATORY_WITH_SEPARATOR;if(E instanceof o.RepetitionWithSeparator)return l.REPETITION_WITH_SEPARATOR;if(E instanceof o.Alternation)return l.ALTERNATION;throw Error("non exhaustive match")}r.getProdType=c;function u(E,b,R,N,B,V){var ie=x(E,b,R),Y=C(ie)?s.tokenStructuredMatcherNoCategories:s.tokenStructuredMatcher;return V(ie,N,Y,B)}r.buildLookaheadFuncForOr=u;function h(E,b,R,N,B,V){var ie=S(E,b,B,R),Y=C(ie)?s.tokenStructuredMatcherNoCategories:s.tokenStructuredMatcher;return V(ie[0],Y,N)}r.buildLookaheadFuncForOptionalProd=h;function f(E,b,R,N){var B=E.length,V=t.every(E,function(te){return t.every(te,function(q){return q.length===1})});if(b)return function(te){for(var q=t.map(te,function(k){return k.GATE}),le=0;le{"use strict";var e=r&&r.__extends||function(){var N=function(B,V){return N=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(ie,Y){ie.__proto__=Y}||function(ie,Y){for(var te in Y)Object.prototype.hasOwnProperty.call(Y,te)&&(ie[te]=Y[te])},N(B,V)};return function(B,V){if(typeof V!="function"&&V!==null)throw new TypeError("Class extends value "+String(V)+" is not a constructor or null");N(B,V);function ie(){this.constructor=B}B.prototype=V===null?Object.create(V):(ie.prototype=V.prototype,new ie)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.checkPrefixAlternativesAmbiguities=r.validateSomeNonEmptyLookaheadPath=r.validateTooManyAlts=r.RepetionCollector=r.validateAmbiguousAlternationAlternatives=r.validateEmptyOrAlternative=r.getFirstNoneTerminal=r.validateNoLeftRecursion=r.validateRuleIsOverridden=r.validateRuleDoesNotAlreadyExist=r.OccurrenceValidationCollector=r.identifyProductionForDuplicates=r.validateGrammar=void 0;var t=Wt(),n=Wt(),i=Xi(),s=Wf(),o=jf(),a=qf(),l=Li(),c=zc();function u(N,B,V,ie,Y){var te=t.map(N,function(P){return h(P,ie)}),q=t.map(N,function(P){return m(P,P,ie)}),le=[],ye=[],Te=[];n.every(q,n.isEmpty)&&(le=n.map(N,function(P){return x(P,ie)}),ye=n.map(N,function(P){return S(P,B,ie)}),Te=C(N,B,ie));var Ae=R(N,V,ie),_e=n.map(N,function(P){return A(P,ie)}),G=n.map(N,function(P){return y(P,N,Y,ie)});return t.flatten(te.concat(Te,q,le,ye,Ae,_e,G))}r.validateGrammar=u;function h(N,B){var V=new g;N.accept(V);var ie=V.allProductions,Y=t.groupBy(ie,f),te=t.pick(Y,function(le){return le.length>1}),q=t.map(t.values(te),function(le){var ye=t.first(le),Te=B.buildDuplicateFoundError(N,le),Ae=s.getProductionDslName(ye),_e={message:Te,type:i.ParserDefinitionErrorType.DUPLICATE_PRODUCTIONS,ruleName:N.name,dslName:Ae,occurrence:ye.idx},G=p(ye);return G&&(_e.parameter=G),_e});return q}function f(N){return s.getProductionDslName(N)+"_#_"+N.idx+"_#_"+p(N)}r.identifyProductionForDuplicates=f;function p(N){return N instanceof l.Terminal?N.terminalType.name:N instanceof l.NonTerminal?N.nonTerminalName:""}var g=function(N){e(B,N);function B(){var V=N!==null&&N.apply(this,arguments)||this;return V.allProductions=[],V}return B.prototype.visitNonTerminal=function(V){this.allProductions.push(V)},B.prototype.visitOption=function(V){this.allProductions.push(V)},B.prototype.visitRepetitionWithSeparator=function(V){this.allProductions.push(V)},B.prototype.visitRepetitionMandatory=function(V){this.allProductions.push(V)},B.prototype.visitRepetitionMandatoryWithSeparator=function(V){this.allProductions.push(V)},B.prototype.visitRepetition=function(V){this.allProductions.push(V)},B.prototype.visitAlternation=function(V){this.allProductions.push(V)},B.prototype.visitTerminal=function(V){this.allProductions.push(V)},B}(c.GAstVisitor);r.OccurrenceValidationCollector=g;function y(N,B,V,ie){var Y=[],te=n.reduce(B,function(le,ye){return ye.name===N.name?le+1:le},0);if(te>1){var q=ie.buildDuplicateRuleNameError({topLevelRule:N,grammarName:V});Y.push({message:q,type:i.ParserDefinitionErrorType.DUPLICATE_RULE_NAME,ruleName:N.name})}return Y}r.validateRuleDoesNotAlreadyExist=y;function d(N,B,V){var ie=[],Y;return t.contains(B,N)||(Y="Invalid rule override, rule: ->"+N+"<- cannot be overridden in the grammar: ->"+V+"<-as it is not defined in any of the super grammars ",ie.push({message:Y,type:i.ParserDefinitionErrorType.INVALID_RULE_OVERRIDE,ruleName:N})),ie}r.validateRuleIsOverridden=d;function m(N,B,V,ie){ie===void 0&&(ie=[]);var Y=[],te=_(B.definition);if(t.isEmpty(te))return[];var q=N.name,le=t.contains(te,N);le&&Y.push({message:V.buildLeftRecursionError({topLevelRule:N,leftRecursionPath:ie}),type:i.ParserDefinitionErrorType.LEFT_RECURSION,ruleName:q});var ye=t.difference(te,ie.concat([N])),Te=t.map(ye,function(Ae){var _e=t.cloneArr(ie);return _e.push(Ae),m(N,Ae,V,_e)});return Y.concat(t.flatten(Te))}r.validateNoLeftRecursion=m;function _(N){var B=[];if(t.isEmpty(N))return B;var V=t.first(N);if(V instanceof l.NonTerminal)B.push(V.referencedRule);else if(V instanceof l.Alternative||V instanceof l.Option||V instanceof l.RepetitionMandatory||V instanceof l.RepetitionMandatoryWithSeparator||V instanceof l.RepetitionWithSeparator||V instanceof l.Repetition)B=B.concat(_(V.definition));else if(V instanceof l.Alternation)B=t.flatten(t.map(V.definition,function(q){return _(q.definition)}));else if(!(V instanceof l.Terminal))throw Error("non exhaustive match");var ie=s.isOptionalProd(V),Y=N.length>1;if(ie&&Y){var te=t.drop(N);return B.concat(_(te))}else return B}r.getFirstNoneTerminal=_;var v=function(N){e(B,N);function B(){var V=N!==null&&N.apply(this,arguments)||this;return V.alternations=[],V}return B.prototype.visitAlternation=function(V){this.alternations.push(V)},B}(c.GAstVisitor);function x(N,B){var V=new v;N.accept(V);var ie=V.alternations,Y=t.reduce(ie,function(te,q){var le=t.dropRight(q.definition),ye=t.map(le,function(Te,Ae){var _e=a.nextPossibleTokensAfter([Te],[],null,1);return t.isEmpty(_e)?{message:B.buildEmptyAlternationError({topLevelRule:N,alternation:q,emptyChoiceIdx:Ae}),type:i.ParserDefinitionErrorType.NONE_LAST_EMPTY_ALT,ruleName:N.name,occurrence:q.idx,alternative:Ae+1}:null});return te.concat(t.compact(ye))},[]);return Y}r.validateEmptyOrAlternative=x;function S(N,B,V){var ie=new v;N.accept(ie);var Y=ie.alternations;Y=n.reject(Y,function(q){return q.ignoreAmbiguities===!0});var te=t.reduce(Y,function(q,le){var ye=le.idx,Te=le.maxLookahead||B,Ae=o.getLookaheadPathsForOr(ye,N,Te,le),_e=E(Ae,le,N,V),G=b(Ae,le,N,V);return q.concat(_e,G)},[]);return te}r.validateAmbiguousAlternationAlternatives=S;var I=function(N){e(B,N);function B(){var V=N!==null&&N.apply(this,arguments)||this;return V.allProductions=[],V}return B.prototype.visitRepetitionWithSeparator=function(V){this.allProductions.push(V)},B.prototype.visitRepetitionMandatory=function(V){this.allProductions.push(V)},B.prototype.visitRepetitionMandatoryWithSeparator=function(V){this.allProductions.push(V)},B.prototype.visitRepetition=function(V){this.allProductions.push(V)},B}(c.GAstVisitor);r.RepetionCollector=I;function A(N,B){var V=new v;N.accept(V);var ie=V.alternations,Y=t.reduce(ie,function(te,q){return q.definition.length>255&&te.push({message:B.buildTooManyAlternativesError({topLevelRule:N,alternation:q}),type:i.ParserDefinitionErrorType.TOO_MANY_ALTS,ruleName:N.name,occurrence:q.idx}),te},[]);return Y}r.validateTooManyAlts=A;function C(N,B,V){var ie=[];return n.forEach(N,function(Y){var te=new I;Y.accept(te);var q=te.allProductions;n.forEach(q,function(le){var ye=o.getProdType(le),Te=le.maxLookahead||B,Ae=le.idx,_e=o.getLookaheadPathsForOptionalProd(Ae,Y,ye,Te),G=_e[0];if(n.isEmpty(n.flatten(G))){var P=V.buildEmptyRepetitionError({topLevelRule:Y,repetition:le});ie.push({message:P,type:i.ParserDefinitionErrorType.NO_NON_EMPTY_LOOKAHEAD,ruleName:Y.name})}})}),ie}r.validateSomeNonEmptyLookaheadPath=C;function E(N,B,V,ie){var Y=[],te=n.reduce(N,function(le,ye,Te){return B.definition[Te].ignoreAmbiguities===!0||n.forEach(ye,function(Ae){var _e=[Te];n.forEach(N,function(G,P){Te!==P&&o.containsPath(G,Ae)&&B.definition[P].ignoreAmbiguities!==!0&&_e.push(P)}),_e.length>1&&!o.containsPath(Y,Ae)&&(Y.push(Ae),le.push({alts:_e,path:Ae}))}),le},[]),q=t.map(te,function(le){var ye=n.map(le.alts,function(Ae){return Ae+1}),Te=ie.buildAlternationAmbiguityError({topLevelRule:V,alternation:B,ambiguityIndices:ye,prefixPath:le.path});return{message:Te,type:i.ParserDefinitionErrorType.AMBIGUOUS_ALTS,ruleName:V.name,occurrence:B.idx,alternatives:[le.alts]}});return q}function b(N,B,V,ie){var Y=[],te=n.reduce(N,function(q,le,ye){var Te=n.map(le,function(Ae){return{idx:ye,path:Ae}});return q.concat(Te)},[]);return n.forEach(te,function(q){var le=B.definition[q.idx];if(le.ignoreAmbiguities!==!0){var ye=q.idx,Te=q.path,Ae=n.findAll(te,function(G){return B.definition[G.idx].ignoreAmbiguities!==!0&&G.idx{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.validateGrammar=r.resolveGrammar=void 0;var e=Wt(),t=_S(),n=sx(),i=Xf();function s(a){a=e.defaults(a,{errMsgProvider:i.defaultGrammarResolverErrorProvider});var l={};return e.forEach(a.rules,function(c){l[c.name]=c}),t.resolveGrammar(l,a.errMsgProvider)}r.resolveGrammar=s;function o(a){return a=e.defaults(a,{errMsgProvider:i.defaultGrammarValidatorErrorProvider}),n.validateGrammar(a.rules,a.maxLookahead,a.tokenTypes,a.errMsgProvider,a.grammarName)}r.validateGrammar=o}),Hc=bt(r=>{"use strict";var e=r&&r.__extends||function(){var g=function(y,d){return g=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(m,_){m.__proto__=_}||function(m,_){for(var v in _)Object.prototype.hasOwnProperty.call(_,v)&&(m[v]=_[v])},g(y,d)};return function(y,d){if(typeof d!="function"&&d!==null)throw new TypeError("Class extends value "+String(d)+" is not a constructor or null");g(y,d);function m(){this.constructor=y}y.prototype=d===null?Object.create(d):(m.prototype=d.prototype,new m)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.EarlyExitException=r.NotAllInputParsedException=r.NoViableAltException=r.MismatchedTokenException=r.isRecognitionException=void 0;var t=Wt(),n="MismatchedTokenException",i="NoViableAltException",s="EarlyExitException",o="NotAllInputParsedException",a=[n,i,s,o];Object.freeze(a);function l(g){return t.contains(a,g.name)}r.isRecognitionException=l;var c=function(g){e(y,g);function y(d,m){var _=this.constructor,v=g.call(this,d)||this;return v.token=m,v.resyncedTokens=[],Object.setPrototypeOf(v,_.prototype),Error.captureStackTrace&&Error.captureStackTrace(v,v.constructor),v}return y}(Error),u=function(g){e(y,g);function y(d,m,_){var v=g.call(this,d,m)||this;return v.previousToken=_,v.name=n,v}return y}(c);r.MismatchedTokenException=u;var h=function(g){e(y,g);function y(d,m,_){var v=g.call(this,d,m)||this;return v.previousToken=_,v.name=i,v}return y}(c);r.NoViableAltException=h;var f=function(g){e(y,g);function y(d,m){var _=g.call(this,d,m)||this;return _.name=o,_}return y}(c);r.NotAllInputParsedException=f;var p=function(g){e(y,g);function y(d,m,_){var v=g.call(this,d,m)||this;return v.previousToken=_,v.name=s,v}return y}(c);r.EarlyExitException=p}),ox=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.attemptInRepetitionRecovery=r.Recoverable=r.InRuleRecoveryException=r.IN_RULE_RECOVERY_EXCEPTION=r.EOF_FOLLOW_KEY=void 0;var e=Co(),t=Wt(),n=Hc(),i=rx(),s=Xi();r.EOF_FOLLOW_KEY={},r.IN_RULE_RECOVERY_EXCEPTION="InRuleRecoveryException";function o(c){this.name=r.IN_RULE_RECOVERY_EXCEPTION,this.message=c}r.InRuleRecoveryException=o,o.prototype=Error.prototype;var a=function(){function c(){}return c.prototype.initRecoverable=function(u){this.firstAfterRepMap={},this.resyncFollows={},this.recoveryEnabled=t.has(u,"recoveryEnabled")?u.recoveryEnabled:s.DEFAULT_PARSER_CONFIG.recoveryEnabled,this.recoveryEnabled&&(this.attemptInRepetitionRecovery=l)},c.prototype.getTokenToInsert=function(u){var h=e.createTokenInstance(u,"",NaN,NaN,NaN,NaN,NaN,NaN);return h.isInsertedInRecovery=!0,h},c.prototype.canTokenTypeBeInsertedInRecovery=function(u){return!0},c.prototype.tryInRepetitionRecovery=function(u,h,f,p){for(var g=this,y=this.findReSyncTokenType(),d=this.exportLexerState(),m=[],_=!1,v=this.LA(1),x=this.LA(1),S=function(){var I=g.LA(0),A=g.errorMessageProvider.buildMismatchTokenMessage({expected:p,actual:v,previous:I,ruleName:g.getCurrRuleFullName()}),C=new n.MismatchedTokenException(A,v,g.LA(0));C.resyncedTokens=t.dropRight(m),g.SAVE_ERROR(C)};!_;)if(this.tokenMatcher(x,p)){S();return}else if(f.call(this)){S(),u.apply(this,h);return}else this.tokenMatcher(x,y)?_=!0:(x=this.SKIP_TOKEN(),this.addToResyncTokens(x,m));this.importLexerState(d)},c.prototype.shouldInRepetitionRecoveryBeTried=function(u,h,f){return!(f===!1||u===void 0||h===void 0||this.tokenMatcher(this.LA(1),u)||this.isBackTracking()||this.canPerformInRuleRecovery(u,this.getFollowsForInRuleRecovery(u,h)))},c.prototype.getFollowsForInRuleRecovery=function(u,h){var f=this.getCurrentGrammarPath(u,h),p=this.getNextPossibleTokenTypes(f);return p},c.prototype.tryInRuleRecovery=function(u,h){if(this.canRecoverWithSingleTokenInsertion(u,h)){var f=this.getTokenToInsert(u);return f}if(this.canRecoverWithSingleTokenDeletion(u)){var p=this.SKIP_TOKEN();return this.consumeToken(),p}throw new o("sad sad panda")},c.prototype.canPerformInRuleRecovery=function(u,h){return this.canRecoverWithSingleTokenInsertion(u,h)||this.canRecoverWithSingleTokenDeletion(u)},c.prototype.canRecoverWithSingleTokenInsertion=function(u,h){var f=this;if(!this.canTokenTypeBeInsertedInRecovery(u)||t.isEmpty(h))return!1;var p=this.LA(1),g=t.find(h,function(y){return f.tokenMatcher(p,y)})!==void 0;return g},c.prototype.canRecoverWithSingleTokenDeletion=function(u){var h=this.tokenMatcher(this.LA(2),u);return h},c.prototype.isInCurrentRuleReSyncSet=function(u){var h=this.getCurrFollowKey(),f=this.getFollowSetFromFollowKey(h);return t.contains(f,u)},c.prototype.findReSyncTokenType=function(){for(var u=this.flattenFollowSet(),h=this.LA(1),f=2;;){var p=h.tokenType;if(t.contains(u,p))return p;h=this.LA(f),f++}},c.prototype.getCurrFollowKey=function(){if(this.RULE_STACK.length===1)return r.EOF_FOLLOW_KEY;var u=this.getLastExplicitRuleShortName(),h=this.getLastExplicitRuleOccurrenceIndex(),f=this.getPreviousExplicitRuleShortName();return{ruleName:this.shortRuleNameToFullName(u),idxInCallingRule:h,inRule:this.shortRuleNameToFullName(f)}},c.prototype.buildFullFollowKeyStack=function(){var u=this,h=this.RULE_STACK,f=this.RULE_OCCURRENCE_STACK;return t.map(h,function(p,g){return g===0?r.EOF_FOLLOW_KEY:{ruleName:u.shortRuleNameToFullName(p),idxInCallingRule:f[g],inRule:u.shortRuleNameToFullName(h[g-1])}})},c.prototype.flattenFollowSet=function(){var u=this,h=t.map(this.buildFullFollowKeyStack(),function(f){return u.getFollowSetFromFollowKey(f)});return t.flatten(h)},c.prototype.getFollowSetFromFollowKey=function(u){if(u===r.EOF_FOLLOW_KEY)return[e.EOF];var h=u.ruleName+u.idxInCallingRule+i.IN+u.inRule;return this.resyncFollows[h]},c.prototype.addToResyncTokens=function(u,h){return this.tokenMatcher(u,e.EOF)||h.push(u),h},c.prototype.reSyncTo=function(u){for(var h=[],f=this.LA(1);this.tokenMatcher(f,u)===!1;)f=this.SKIP_TOKEN(),this.addToResyncTokens(f,h);return t.dropRight(h)},c.prototype.attemptInRepetitionRecovery=function(u,h,f,p,g,y,d){},c.prototype.getCurrentGrammarPath=function(u,h){var f=this.getHumanReadableRuleStack(),p=t.cloneArr(this.RULE_OCCURRENCE_STACK),g={ruleStack:f,occurrenceStack:p,lastTok:u,lastTokOccurrence:h};return g},c.prototype.getHumanReadableRuleStack=function(){var u=this;return t.map(this.RULE_STACK,function(h){return u.shortRuleNameToFullName(h)})},c}();r.Recoverable=a;function l(c,u,h,f,p,g,y){var d=this.getKeyForAutomaticLookahead(f,p),m=this.firstAfterRepMap[d];if(m===void 0){var _=this.getCurrRuleFullName(),v=this.getGAstProductions()[_],x=new g(v,p);m=x.startWalking(),this.firstAfterRepMap[d]=m}var S=m.token,I=m.occurrence,A=m.isEndOfRule;this.RULE_STACK.length===1&&A&&S===void 0&&(S=e.EOF,I=1),this.shouldInRepetitionRecoveryBeTried(S,I,y)&&this.tryInRepetitionRecovery(c,u,h,S)}r.attemptInRepetitionRecovery=l}),Em=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.getKeyForAutomaticLookahead=r.AT_LEAST_ONE_SEP_IDX=r.MANY_SEP_IDX=r.AT_LEAST_ONE_IDX=r.MANY_IDX=r.OPTION_IDX=r.OR_IDX=r.BITS_FOR_ALT_IDX=r.BITS_FOR_RULE_IDX=r.BITS_FOR_OCCURRENCE_IDX=r.BITS_FOR_METHOD_TYPE=void 0,r.BITS_FOR_METHOD_TYPE=4,r.BITS_FOR_OCCURRENCE_IDX=8,r.BITS_FOR_RULE_IDX=12,r.BITS_FOR_ALT_IDX=8,r.OR_IDX=1<{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.LooksAhead=void 0;var e=jf(),t=Wt(),n=Xi(),i=Em(),s=Wf(),o=function(){function a(){}return a.prototype.initLooksAhead=function(l){this.dynamicTokensEnabled=t.has(l,"dynamicTokensEnabled")?l.dynamicTokensEnabled:n.DEFAULT_PARSER_CONFIG.dynamicTokensEnabled,this.maxLookahead=t.has(l,"maxLookahead")?l.maxLookahead:n.DEFAULT_PARSER_CONFIG.maxLookahead,this.lookAheadFuncsCache=t.isES2015MapSupported()?new Map:[],t.isES2015MapSupported()?(this.getLaFuncFromCache=this.getLaFuncFromMap,this.setLaFuncCache=this.setLaFuncCacheUsingMap):(this.getLaFuncFromCache=this.getLaFuncFromObj,this.setLaFuncCache=this.setLaFuncUsingObj)},a.prototype.preComputeLookaheadFunctions=function(l){var c=this;t.forEach(l,function(u){c.TRACE_INIT(u.name+" Rule Lookahead",function(){var h=s.collectMethods(u),f=h.alternation,p=h.repetition,g=h.option,y=h.repetitionMandatory,d=h.repetitionMandatoryWithSeparator,m=h.repetitionWithSeparator;t.forEach(f,function(_){var v=_.idx===0?"":_.idx;c.TRACE_INIT(""+s.getProductionDslName(_)+v,function(){var x=e.buildLookaheadFuncForOr(_.idx,u,_.maxLookahead||c.maxLookahead,_.hasPredicates,c.dynamicTokensEnabled,c.lookAheadBuilderForAlternatives),S=i.getKeyForAutomaticLookahead(c.fullRuleNameToShort[u.name],i.OR_IDX,_.idx);c.setLaFuncCache(S,x)})}),t.forEach(p,function(_){c.computeLookaheadFunc(u,_.idx,i.MANY_IDX,e.PROD_TYPE.REPETITION,_.maxLookahead,s.getProductionDslName(_))}),t.forEach(g,function(_){c.computeLookaheadFunc(u,_.idx,i.OPTION_IDX,e.PROD_TYPE.OPTION,_.maxLookahead,s.getProductionDslName(_))}),t.forEach(y,function(_){c.computeLookaheadFunc(u,_.idx,i.AT_LEAST_ONE_IDX,e.PROD_TYPE.REPETITION_MANDATORY,_.maxLookahead,s.getProductionDslName(_))}),t.forEach(d,function(_){c.computeLookaheadFunc(u,_.idx,i.AT_LEAST_ONE_SEP_IDX,e.PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR,_.maxLookahead,s.getProductionDslName(_))}),t.forEach(m,function(_){c.computeLookaheadFunc(u,_.idx,i.MANY_SEP_IDX,e.PROD_TYPE.REPETITION_WITH_SEPARATOR,_.maxLookahead,s.getProductionDslName(_))})})})},a.prototype.computeLookaheadFunc=function(l,c,u,h,f,p){var g=this;this.TRACE_INIT(""+p+(c===0?"":c),function(){var y=e.buildLookaheadFuncForOptionalProd(c,l,f||g.maxLookahead,g.dynamicTokensEnabled,h,g.lookAheadBuilderForOptional),d=i.getKeyForAutomaticLookahead(g.fullRuleNameToShort[l.name],u,c);g.setLaFuncCache(d,y)})},a.prototype.lookAheadBuilderForOptional=function(l,c,u){return e.buildSingleAlternativeLookaheadFunction(l,c,u)},a.prototype.lookAheadBuilderForAlternatives=function(l,c,u,h){return e.buildAlternativesLookAheadFunc(l,c,u,h)},a.prototype.getKeyForAutomaticLookahead=function(l,c){var u=this.getLastExplicitRuleShortName();return i.getKeyForAutomaticLookahead(u,l,c)},a.prototype.getLaFuncFromCache=function(l){},a.prototype.getLaFuncFromMap=function(l){return this.lookAheadFuncsCache.get(l)},a.prototype.getLaFuncFromObj=function(l){return this.lookAheadFuncsCache[l]},a.prototype.setLaFuncCache=function(l,c){},a.prototype.setLaFuncCacheUsingMap=function(l,c){this.lookAheadFuncsCache.set(l,c)},a.prototype.setLaFuncUsingObj=function(l,c){this.lookAheadFuncsCache[l]=c},a}();r.LooksAhead=o}),ES=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.addNoneTerminalToCst=r.addTerminalToCst=r.setNodeLocationFull=r.setNodeLocationOnlyOffset=void 0;function e(s,o){isNaN(s.startOffset)===!0?(s.startOffset=o.startOffset,s.endOffset=o.endOffset):s.endOffset{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.defineNameProp=r.functionName=r.classNameFromInstance=void 0;var e=Wt();function t(o){return i(o.constructor)}r.classNameFromInstance=t;var n="name";function i(o){var a=o.name;return a||"anonymous"}r.functionName=i;function s(o,a){var l=Object.getOwnPropertyDescriptor(o,n);return e.isUndefined(l)||l.configurable?(Object.defineProperty(o,n,{enumerable:!1,configurable:!0,writable:!1,value:a}),!0):!1}r.defineNameProp=s}),bS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.validateRedundantMethods=r.validateMissingCstMethods=r.validateVisitor=r.CstVisitorDefinitionError=r.createBaseVisitorConstructorWithDefaults=r.createBaseSemanticVisitorConstructor=r.defaultVisit=void 0;var e=Wt(),t=ax();function n(h,f){for(var p=e.keys(h),g=p.length,y=0;y: + `+(""+d.join(` + +`).replace(/\n/g,` + `)))}}};return p.prototype=g,p.prototype.constructor=p,p._RULE_NAMES=f,p}r.createBaseSemanticVisitorConstructor=i;function s(h,f,p){var g=function(){};t.defineNameProp(g,h+"BaseSemanticsWithDefaults");var y=Object.create(p.prototype);return e.forEach(f,function(d){y[d]=n}),g.prototype=y,g.prototype.constructor=g,g}r.createBaseVisitorConstructorWithDefaults=s;var o;(function(h){h[h.REDUNDANT_METHOD=0]="REDUNDANT_METHOD",h[h.MISSING_METHOD=1]="MISSING_METHOD"})(o=r.CstVisitorDefinitionError||(r.CstVisitorDefinitionError={}));function a(h,f){var p=l(h,f),g=u(h,f);return p.concat(g)}r.validateVisitor=a;function l(h,f){var p=e.map(f,function(g){if(!e.isFunction(h[g]))return{msg:"Missing visitor method: <"+g+"> on "+t.functionName(h.constructor)+" CST Visitor.",type:o.MISSING_METHOD,methodName:g}});return e.compact(p)}r.validateMissingCstMethods=l;var c=["constructor","visit","validateVisitor"];function u(h,f){var p=[];for(var g in h)e.isFunction(h[g])&&!e.contains(c,g)&&!e.contains(f,g)&&p.push({msg:"Redundant visitor method: <"+g+"> on "+t.functionName(h.constructor)+` CST Visitor +There is no Grammar Rule corresponding to this method's name. +`,type:o.REDUNDANT_METHOD,methodName:g});return p}r.validateRedundantMethods=u}),SS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.TreeBuilder=void 0;var e=ES(),t=Wt(),n=bS(),i=Xi(),s=function(){function o(){}return o.prototype.initTreeBuilder=function(a){if(this.CST_STACK=[],this.outputCst=a.outputCst,this.nodeLocationTracking=t.has(a,"nodeLocationTracking")?a.nodeLocationTracking:i.DEFAULT_PARSER_CONFIG.nodeLocationTracking,!this.outputCst)this.cstInvocationStateUpdate=t.NOOP,this.cstFinallyStateUpdate=t.NOOP,this.cstPostTerminal=t.NOOP,this.cstPostNonTerminal=t.NOOP,this.cstPostRule=t.NOOP;else if(/full/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=e.setNodeLocationFull,this.setNodeLocationFromNode=e.setNodeLocationFull,this.cstPostRule=t.NOOP,this.setInitialNodeLocation=this.setInitialNodeLocationFullRecovery):(this.setNodeLocationFromToken=t.NOOP,this.setNodeLocationFromNode=t.NOOP,this.cstPostRule=this.cstPostRuleFull,this.setInitialNodeLocation=this.setInitialNodeLocationFullRegular);else if(/onlyOffset/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=e.setNodeLocationOnlyOffset,this.setNodeLocationFromNode=e.setNodeLocationOnlyOffset,this.cstPostRule=t.NOOP,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRecovery):(this.setNodeLocationFromToken=t.NOOP,this.setNodeLocationFromNode=t.NOOP,this.cstPostRule=this.cstPostRuleOnlyOffset,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRegular);else if(/none/i.test(this.nodeLocationTracking))this.setNodeLocationFromToken=t.NOOP,this.setNodeLocationFromNode=t.NOOP,this.cstPostRule=t.NOOP,this.setInitialNodeLocation=t.NOOP;else throw Error('Invalid config option: "'+a.nodeLocationTracking+'"')},o.prototype.setInitialNodeLocationOnlyOffsetRecovery=function(a){a.location={startOffset:NaN,endOffset:NaN}},o.prototype.setInitialNodeLocationOnlyOffsetRegular=function(a){a.location={startOffset:this.LA(1).startOffset,endOffset:NaN}},o.prototype.setInitialNodeLocationFullRecovery=function(a){a.location={startOffset:NaN,startLine:NaN,startColumn:NaN,endOffset:NaN,endLine:NaN,endColumn:NaN}},o.prototype.setInitialNodeLocationFullRegular=function(a){var l=this.LA(1);a.location={startOffset:l.startOffset,startLine:l.startLine,startColumn:l.startColumn,endOffset:NaN,endLine:NaN,endColumn:NaN}},o.prototype.cstInvocationStateUpdate=function(a,l){var c={name:a,children:{}};this.setInitialNodeLocation(c),this.CST_STACK.push(c)},o.prototype.cstFinallyStateUpdate=function(){this.CST_STACK.pop()},o.prototype.cstPostRuleFull=function(a){var l=this.LA(0),c=a.location;c.startOffset<=l.startOffset?(c.endOffset=l.endOffset,c.endLine=l.endLine,c.endColumn=l.endColumn):(c.startOffset=NaN,c.startLine=NaN,c.startColumn=NaN)},o.prototype.cstPostRuleOnlyOffset=function(a){var l=this.LA(0),c=a.location;c.startOffset<=l.startOffset?c.endOffset=l.endOffset:c.startOffset=NaN},o.prototype.cstPostTerminal=function(a,l){var c=this.CST_STACK[this.CST_STACK.length-1];e.addTerminalToCst(c,l,a),this.setNodeLocationFromToken(c.location,l)},o.prototype.cstPostNonTerminal=function(a,l){var c=this.CST_STACK[this.CST_STACK.length-1];e.addNoneTerminalToCst(c,l,a),this.setNodeLocationFromNode(c.location,a.location)},o.prototype.getBaseCstVisitorConstructor=function(){if(t.isUndefined(this.baseCstVisitorConstructor)){var a=n.createBaseSemanticVisitorConstructor(this.className,t.keys(this.gastProductionsCache));return this.baseCstVisitorConstructor=a,a}return this.baseCstVisitorConstructor},o.prototype.getBaseCstVisitorConstructorWithDefaults=function(){if(t.isUndefined(this.baseCstVisitorWithDefaultsConstructor)){var a=n.createBaseVisitorConstructorWithDefaults(this.className,t.keys(this.gastProductionsCache),this.getBaseCstVisitorConstructor());return this.baseCstVisitorWithDefaultsConstructor=a,a}return this.baseCstVisitorWithDefaultsConstructor},o.prototype.getLastExplicitRuleShortName=function(){var a=this.RULE_STACK;return a[a.length-1]},o.prototype.getPreviousExplicitRuleShortName=function(){var a=this.RULE_STACK;return a[a.length-2]},o.prototype.getLastExplicitRuleOccurrenceIndex=function(){var a=this.RULE_OCCURRENCE_STACK;return a[a.length-1]},o}();r.TreeBuilder=s}),AS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.LexerAdapter=void 0;var e=Xi(),t=function(){function n(){}return n.prototype.initLexerAdapter=function(){this.tokVector=[],this.tokVectorLength=0,this.currIdx=-1},Object.defineProperty(n.prototype,"input",{get:function(){return this.tokVector},set:function(i){if(this.selfAnalysisDone!==!0)throw Error("Missing invocation at the end of the Parser's constructor.");this.reset(),this.tokVector=i,this.tokVectorLength=i.length},enumerable:!1,configurable:!0}),n.prototype.SKIP_TOKEN=function(){return this.currIdx<=this.tokVector.length-2?(this.consumeToken(),this.LA(1)):e.END_OF_FILE},n.prototype.LA=function(i){var s=this.currIdx+i;return s<0||this.tokVectorLength<=s?e.END_OF_FILE:this.tokVector[s]},n.prototype.consumeToken=function(){this.currIdx++},n.prototype.exportLexerState=function(){return this.currIdx},n.prototype.importLexerState=function(i){this.currIdx=i},n.prototype.resetLexerState=function(){this.currIdx=-1},n.prototype.moveToTerminatedState=function(){this.currIdx=this.tokVector.length-1},n.prototype.getLexerPosition=function(){return this.exportLexerState()},n}();r.LexerAdapter=t}),wS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.RecognizerApi=void 0;var e=Wt(),t=Hc(),n=Xi(),i=Xf(),s=sx(),o=Li(),a=function(){function l(){}return l.prototype.ACTION=function(c){return c.call(this)},l.prototype.consume=function(c,u,h){return this.consumeInternal(u,c,h)},l.prototype.subrule=function(c,u,h){return this.subruleInternal(u,c,h)},l.prototype.option=function(c,u){return this.optionInternal(u,c)},l.prototype.or=function(c,u){return this.orInternal(u,c)},l.prototype.many=function(c,u){return this.manyInternal(c,u)},l.prototype.atLeastOne=function(c,u){return this.atLeastOneInternal(c,u)},l.prototype.CONSUME=function(c,u){return this.consumeInternal(c,0,u)},l.prototype.CONSUME1=function(c,u){return this.consumeInternal(c,1,u)},l.prototype.CONSUME2=function(c,u){return this.consumeInternal(c,2,u)},l.prototype.CONSUME3=function(c,u){return this.consumeInternal(c,3,u)},l.prototype.CONSUME4=function(c,u){return this.consumeInternal(c,4,u)},l.prototype.CONSUME5=function(c,u){return this.consumeInternal(c,5,u)},l.prototype.CONSUME6=function(c,u){return this.consumeInternal(c,6,u)},l.prototype.CONSUME7=function(c,u){return this.consumeInternal(c,7,u)},l.prototype.CONSUME8=function(c,u){return this.consumeInternal(c,8,u)},l.prototype.CONSUME9=function(c,u){return this.consumeInternal(c,9,u)},l.prototype.SUBRULE=function(c,u){return this.subruleInternal(c,0,u)},l.prototype.SUBRULE1=function(c,u){return this.subruleInternal(c,1,u)},l.prototype.SUBRULE2=function(c,u){return this.subruleInternal(c,2,u)},l.prototype.SUBRULE3=function(c,u){return this.subruleInternal(c,3,u)},l.prototype.SUBRULE4=function(c,u){return this.subruleInternal(c,4,u)},l.prototype.SUBRULE5=function(c,u){return this.subruleInternal(c,5,u)},l.prototype.SUBRULE6=function(c,u){return this.subruleInternal(c,6,u)},l.prototype.SUBRULE7=function(c,u){return this.subruleInternal(c,7,u)},l.prototype.SUBRULE8=function(c,u){return this.subruleInternal(c,8,u)},l.prototype.SUBRULE9=function(c,u){return this.subruleInternal(c,9,u)},l.prototype.OPTION=function(c){return this.optionInternal(c,0)},l.prototype.OPTION1=function(c){return this.optionInternal(c,1)},l.prototype.OPTION2=function(c){return this.optionInternal(c,2)},l.prototype.OPTION3=function(c){return this.optionInternal(c,3)},l.prototype.OPTION4=function(c){return this.optionInternal(c,4)},l.prototype.OPTION5=function(c){return this.optionInternal(c,5)},l.prototype.OPTION6=function(c){return this.optionInternal(c,6)},l.prototype.OPTION7=function(c){return this.optionInternal(c,7)},l.prototype.OPTION8=function(c){return this.optionInternal(c,8)},l.prototype.OPTION9=function(c){return this.optionInternal(c,9)},l.prototype.OR=function(c){return this.orInternal(c,0)},l.prototype.OR1=function(c){return this.orInternal(c,1)},l.prototype.OR2=function(c){return this.orInternal(c,2)},l.prototype.OR3=function(c){return this.orInternal(c,3)},l.prototype.OR4=function(c){return this.orInternal(c,4)},l.prototype.OR5=function(c){return this.orInternal(c,5)},l.prototype.OR6=function(c){return this.orInternal(c,6)},l.prototype.OR7=function(c){return this.orInternal(c,7)},l.prototype.OR8=function(c){return this.orInternal(c,8)},l.prototype.OR9=function(c){return this.orInternal(c,9)},l.prototype.MANY=function(c){this.manyInternal(0,c)},l.prototype.MANY1=function(c){this.manyInternal(1,c)},l.prototype.MANY2=function(c){this.manyInternal(2,c)},l.prototype.MANY3=function(c){this.manyInternal(3,c)},l.prototype.MANY4=function(c){this.manyInternal(4,c)},l.prototype.MANY5=function(c){this.manyInternal(5,c)},l.prototype.MANY6=function(c){this.manyInternal(6,c)},l.prototype.MANY7=function(c){this.manyInternal(7,c)},l.prototype.MANY8=function(c){this.manyInternal(8,c)},l.prototype.MANY9=function(c){this.manyInternal(9,c)},l.prototype.MANY_SEP=function(c){this.manySepFirstInternal(0,c)},l.prototype.MANY_SEP1=function(c){this.manySepFirstInternal(1,c)},l.prototype.MANY_SEP2=function(c){this.manySepFirstInternal(2,c)},l.prototype.MANY_SEP3=function(c){this.manySepFirstInternal(3,c)},l.prototype.MANY_SEP4=function(c){this.manySepFirstInternal(4,c)},l.prototype.MANY_SEP5=function(c){this.manySepFirstInternal(5,c)},l.prototype.MANY_SEP6=function(c){this.manySepFirstInternal(6,c)},l.prototype.MANY_SEP7=function(c){this.manySepFirstInternal(7,c)},l.prototype.MANY_SEP8=function(c){this.manySepFirstInternal(8,c)},l.prototype.MANY_SEP9=function(c){this.manySepFirstInternal(9,c)},l.prototype.AT_LEAST_ONE=function(c){this.atLeastOneInternal(0,c)},l.prototype.AT_LEAST_ONE1=function(c){return this.atLeastOneInternal(1,c)},l.prototype.AT_LEAST_ONE2=function(c){this.atLeastOneInternal(2,c)},l.prototype.AT_LEAST_ONE3=function(c){this.atLeastOneInternal(3,c)},l.prototype.AT_LEAST_ONE4=function(c){this.atLeastOneInternal(4,c)},l.prototype.AT_LEAST_ONE5=function(c){this.atLeastOneInternal(5,c)},l.prototype.AT_LEAST_ONE6=function(c){this.atLeastOneInternal(6,c)},l.prototype.AT_LEAST_ONE7=function(c){this.atLeastOneInternal(7,c)},l.prototype.AT_LEAST_ONE8=function(c){this.atLeastOneInternal(8,c)},l.prototype.AT_LEAST_ONE9=function(c){this.atLeastOneInternal(9,c)},l.prototype.AT_LEAST_ONE_SEP=function(c){this.atLeastOneSepFirstInternal(0,c)},l.prototype.AT_LEAST_ONE_SEP1=function(c){this.atLeastOneSepFirstInternal(1,c)},l.prototype.AT_LEAST_ONE_SEP2=function(c){this.atLeastOneSepFirstInternal(2,c)},l.prototype.AT_LEAST_ONE_SEP3=function(c){this.atLeastOneSepFirstInternal(3,c)},l.prototype.AT_LEAST_ONE_SEP4=function(c){this.atLeastOneSepFirstInternal(4,c)},l.prototype.AT_LEAST_ONE_SEP5=function(c){this.atLeastOneSepFirstInternal(5,c)},l.prototype.AT_LEAST_ONE_SEP6=function(c){this.atLeastOneSepFirstInternal(6,c)},l.prototype.AT_LEAST_ONE_SEP7=function(c){this.atLeastOneSepFirstInternal(7,c)},l.prototype.AT_LEAST_ONE_SEP8=function(c){this.atLeastOneSepFirstInternal(8,c)},l.prototype.AT_LEAST_ONE_SEP9=function(c){this.atLeastOneSepFirstInternal(9,c)},l.prototype.RULE=function(c,u,h){if(h===void 0&&(h=n.DEFAULT_RULE_CONFIG),e.contains(this.definedRulesNames,c)){var f=i.defaultGrammarValidatorErrorProvider.buildDuplicateRuleNameError({topLevelRule:c,grammarName:this.className}),p={message:f,type:n.ParserDefinitionErrorType.DUPLICATE_RULE_NAME,ruleName:c};this.definitionErrors.push(p)}this.definedRulesNames.push(c);var g=this.defineRule(c,u,h);return this[c]=g,g},l.prototype.OVERRIDE_RULE=function(c,u,h){h===void 0&&(h=n.DEFAULT_RULE_CONFIG);var f=[];f=f.concat(s.validateRuleIsOverridden(c,this.definedRulesNames,this.className)),this.definitionErrors=this.definitionErrors.concat(f);var p=this.defineRule(c,u,h);return this[c]=p,p},l.prototype.BACKTRACK=function(c,u){return function(){this.isBackTrackingStack.push(1);var h=this.saveRecogState();try{return c.apply(this,u),!0}catch(f){if(t.isRecognitionException(f))return!1;throw f}finally{this.reloadRecogState(h),this.isBackTrackingStack.pop()}}},l.prototype.getGAstProductions=function(){return this.gastProductionsCache},l.prototype.getSerializedGastProductions=function(){return o.serializeGrammar(e.values(this.gastProductionsCache))},l}();r.RecognizerApi=a}),CS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.RecognizerEngine=void 0;var e=Wt(),t=Em(),n=Hc(),i=jf(),s=qf(),o=Xi(),a=ox(),l=Co(),c=Vc(),u=ax(),h=function(){function f(){}return f.prototype.initRecognizerEngine=function(p,g){if(this.className=u.classNameFromInstance(this),this.shortRuleNameToFull={},this.fullRuleNameToShort={},this.ruleShortNameIdx=256,this.tokenMatcher=c.tokenStructuredMatcherNoCategories,this.definedRulesNames=[],this.tokensMap={},this.isBackTrackingStack=[],this.RULE_STACK=[],this.RULE_OCCURRENCE_STACK=[],this.gastProductionsCache={},e.has(g,"serializedGrammar"))throw Error(`The Parser's configuration can no longer contain a property. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_6-0-0 + For Further details.`);if(e.isArray(p)){if(e.isEmpty(p))throw Error(`A Token Vocabulary cannot be empty. + Note that the first argument for the parser constructor + is no longer a Token vector (since v4.0).`);if(typeof p[0].startOffset=="number")throw Error(`The Parser constructor no longer accepts a token vector as the first argument. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_4-0-0 + For Further details.`)}if(e.isArray(p))this.tokensMap=e.reduce(p,function(_,v){return _[v.name]=v,_},{});else if(e.has(p,"modes")&&e.every(e.flatten(e.values(p.modes)),c.isTokenType)){var y=e.flatten(e.values(p.modes)),d=e.uniq(y);this.tokensMap=e.reduce(d,function(_,v){return _[v.name]=v,_},{})}else if(e.isObject(p))this.tokensMap=e.cloneObj(p);else throw new Error(" argument must be An Array of Token constructors, A dictionary of Token constructors or an IMultiModeLexerDefinition");this.tokensMap.EOF=l.EOF;var m=e.every(e.values(p),function(_){return e.isEmpty(_.categoryMatches)});this.tokenMatcher=m?c.tokenStructuredMatcherNoCategories:c.tokenStructuredMatcher,c.augmentTokenTypes(e.values(this.tokensMap))},f.prototype.defineRule=function(p,g,y){if(this.selfAnalysisDone)throw Error("Grammar rule <"+p+`> may not be defined after the 'performSelfAnalysis' method has been called' +Make sure that all grammar rule definitions are done before 'performSelfAnalysis' is called.`);var d=e.has(y,"resyncEnabled")?y.resyncEnabled:o.DEFAULT_RULE_CONFIG.resyncEnabled,m=e.has(y,"recoveryValueFunc")?y.recoveryValueFunc:o.DEFAULT_RULE_CONFIG.recoveryValueFunc,_=this.ruleShortNameIdx<g},f.prototype.orInternal=function(p,g){var y=this.getKeyForAutomaticLookahead(t.OR_IDX,g),d=e.isArray(p)?p:p.DEF,m=this.getLaFuncFromCache(y),_=m.call(this,d);if(_!==void 0){var v=d[_];return v.ALT.call(this)}this.raiseNoAltException(g,p.ERR_MSG)},f.prototype.ruleFinallyStateUpdate=function(){if(this.RULE_STACK.pop(),this.RULE_OCCURRENCE_STACK.pop(),this.cstFinallyStateUpdate(),this.RULE_STACK.length===0&&this.isAtEndOfInput()===!1){var p=this.LA(1),g=this.errorMessageProvider.buildNotAllInputParsedMessage({firstRedundant:p,ruleName:this.getCurrRuleFullName()});this.SAVE_ERROR(new n.NotAllInputParsedException(g,p))}},f.prototype.subruleInternal=function(p,g,y){var d;try{var m=y!==void 0?y.ARGS:void 0;return d=p.call(this,g,m),this.cstPostNonTerminal(d,y!==void 0&&y.LABEL!==void 0?y.LABEL:p.ruleName),d}catch(_){this.subruleInternalError(_,y,p.ruleName)}},f.prototype.subruleInternalError=function(p,g,y){throw n.isRecognitionException(p)&&p.partialCstResult!==void 0&&(this.cstPostNonTerminal(p.partialCstResult,g!==void 0&&g.LABEL!==void 0?g.LABEL:y),delete p.partialCstResult),p},f.prototype.consumeInternal=function(p,g,y){var d;try{var m=this.LA(1);this.tokenMatcher(m,p)===!0?(this.consumeToken(),d=m):this.consumeInternalError(p,m,y)}catch(_){d=this.consumeInternalRecovery(p,g,_)}return this.cstPostTerminal(y!==void 0&&y.LABEL!==void 0?y.LABEL:p.name,d),d},f.prototype.consumeInternalError=function(p,g,y){var d,m=this.LA(0);throw y!==void 0&&y.ERR_MSG?d=y.ERR_MSG:d=this.errorMessageProvider.buildMismatchTokenMessage({expected:p,actual:g,previous:m,ruleName:this.getCurrRuleFullName()}),this.SAVE_ERROR(new n.MismatchedTokenException(d,g,m))},f.prototype.consumeInternalRecovery=function(p,g,y){if(this.recoveryEnabled&&y.name==="MismatchedTokenException"&&!this.isBackTracking()){var d=this.getFollowsForInRuleRecovery(p,g);try{return this.tryInRuleRecovery(p,d)}catch(m){throw m.name===a.IN_RULE_RECOVERY_EXCEPTION?y:m}}else throw y},f.prototype.saveRecogState=function(){var p=this.errors,g=e.cloneArr(this.RULE_STACK);return{errors:p,lexerState:this.exportLexerState(),RULE_STACK:g,CST_STACK:this.CST_STACK}},f.prototype.reloadRecogState=function(p){this.errors=p.errors,this.importLexerState(p.lexerState),this.RULE_STACK=p.RULE_STACK},f.prototype.ruleInvocationStateUpdate=function(p,g,y){this.RULE_OCCURRENCE_STACK.push(y),this.RULE_STACK.push(p),this.cstInvocationStateUpdate(g,p)},f.prototype.isBackTracking=function(){return this.isBackTrackingStack.length!==0},f.prototype.getCurrRuleFullName=function(){var p=this.getLastExplicitRuleShortName();return this.shortRuleNameToFull[p]},f.prototype.shortRuleNameToFullName=function(p){return this.shortRuleNameToFull[p]},f.prototype.isAtEndOfInput=function(){return this.tokenMatcher(this.LA(1),l.EOF)},f.prototype.reset=function(){this.resetLexerState(),this.isBackTrackingStack=[],this.errors=[],this.RULE_STACK=[],this.CST_STACK=[],this.RULE_OCCURRENCE_STACK=[]},f}();r.RecognizerEngine=h}),IS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.ErrorHandler=void 0;var e=Hc(),t=Wt(),n=jf(),i=Xi(),s=function(){function o(){}return o.prototype.initErrorHandler=function(a){this._errors=[],this.errorMessageProvider=t.has(a,"errorMessageProvider")?a.errorMessageProvider:i.DEFAULT_PARSER_CONFIG.errorMessageProvider},o.prototype.SAVE_ERROR=function(a){if(e.isRecognitionException(a))return a.context={ruleStack:this.getHumanReadableRuleStack(),ruleOccurrenceStack:t.cloneArr(this.RULE_OCCURRENCE_STACK)},this._errors.push(a),a;throw Error("Trying to save an Error which is not a RecognitionException")},Object.defineProperty(o.prototype,"errors",{get:function(){return t.cloneArr(this._errors)},set:function(a){this._errors=a},enumerable:!1,configurable:!0}),o.prototype.raiseEarlyExitException=function(a,l,c){for(var u=this.getCurrRuleFullName(),h=this.getGAstProductions()[u],f=n.getLookaheadPathsForOptionalProd(a,h,l,this.maxLookahead),p=f[0],g=[],y=1;y<=this.maxLookahead;y++)g.push(this.LA(y));var d=this.errorMessageProvider.buildEarlyExitMessage({expectedIterationPaths:p,actual:g,previous:this.LA(0),customUserDescription:c,ruleName:u});throw this.SAVE_ERROR(new e.EarlyExitException(d,this.LA(1),this.LA(0)))},o.prototype.raiseNoAltException=function(a,l){for(var c=this.getCurrRuleFullName(),u=this.getGAstProductions()[c],h=n.getLookaheadPathsForOr(a,u,this.maxLookahead),f=[],p=1;p<=this.maxLookahead;p++)f.push(this.LA(p));var g=this.LA(0),y=this.errorMessageProvider.buildNoViableAltMessage({expectedPathsPerAlt:h,actual:f,previous:g,customUserDescription:l,ruleName:this.getCurrRuleFullName()});throw this.SAVE_ERROR(new e.NoViableAltException(y,this.LA(1),g))},o}();r.ErrorHandler=s}),RS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.ContentAssist=void 0;var e=qf(),t=Wt(),n=function(){function i(){}return i.prototype.initContentAssist=function(){},i.prototype.computeContentAssist=function(s,o){var a=this.gastProductionsCache[s];if(t.isUndefined(a))throw Error("Rule ->"+s+"<- does not exist in this grammar.");return e.nextPossibleTokensAfter([a],o,this.tokenMatcher,this.maxLookahead)},i.prototype.getNextPossibleTokenTypes=function(s){var o=t.first(s.ruleStack),a=this.getGAstProductions(),l=a[o],c=new e.NextAfterTokenWalker(l,s).startWalking();return c},i}();r.ContentAssist=n}),PS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.GastRecorder=void 0;var e=Wt(),t=Li(),n=Hf(),i=Vc(),s=Co(),o=Xi(),a=Em(),l={description:"This Object indicates the Parser is during Recording Phase"};Object.freeze(l);var c=!0,u=Math.pow(2,a.BITS_FOR_OCCURRENCE_IDX)-1,h=s.createToken({name:"RECORDING_PHASE_TOKEN",pattern:n.Lexer.NA});i.augmentTokenTypes([h]);var f=s.createTokenInstance(h,`This IToken indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,-1,-1,-1,-1,-1,-1);Object.freeze(f);var p={name:`This CSTNode indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,children:{}},g=function(){function v(){}return v.prototype.initGastRecorder=function(x){this.recordingProdStack=[],this.RECORDING_PHASE=!1},v.prototype.enableRecording=function(){var x=this;this.RECORDING_PHASE=!0,this.TRACE_INIT("Enable Recording",function(){for(var S=function(A){var C=A>0?A:"";x["CONSUME"+C]=function(E,b){return this.consumeInternalRecord(E,A,b)},x["SUBRULE"+C]=function(E,b){return this.subruleInternalRecord(E,A,b)},x["OPTION"+C]=function(E){return this.optionInternalRecord(E,A)},x["OR"+C]=function(E){return this.orInternalRecord(E,A)},x["MANY"+C]=function(E){this.manyInternalRecord(A,E)},x["MANY_SEP"+C]=function(E){this.manySepFirstInternalRecord(A,E)},x["AT_LEAST_ONE"+C]=function(E){this.atLeastOneInternalRecord(A,E)},x["AT_LEAST_ONE_SEP"+C]=function(E){this.atLeastOneSepFirstInternalRecord(A,E)}},I=0;I<10;I++)S(I);x.consume=function(A,C,E){return this.consumeInternalRecord(C,A,E)},x.subrule=function(A,C,E){return this.subruleInternalRecord(C,A,E)},x.option=function(A,C){return this.optionInternalRecord(C,A)},x.or=function(A,C){return this.orInternalRecord(C,A)},x.many=function(A,C){this.manyInternalRecord(A,C)},x.atLeastOne=function(A,C){this.atLeastOneInternalRecord(A,C)},x.ACTION=x.ACTION_RECORD,x.BACKTRACK=x.BACKTRACK_RECORD,x.LA=x.LA_RECORD})},v.prototype.disableRecording=function(){var x=this;this.RECORDING_PHASE=!1,this.TRACE_INIT("Deleting Recording methods",function(){for(var S=0;S<10;S++){var I=S>0?S:"";delete x["CONSUME"+I],delete x["SUBRULE"+I],delete x["OPTION"+I],delete x["OR"+I],delete x["MANY"+I],delete x["MANY_SEP"+I],delete x["AT_LEAST_ONE"+I],delete x["AT_LEAST_ONE_SEP"+I]}delete x.consume,delete x.subrule,delete x.option,delete x.or,delete x.many,delete x.atLeastOne,delete x.ACTION,delete x.BACKTRACK,delete x.LA})},v.prototype.ACTION_RECORD=function(x){},v.prototype.BACKTRACK_RECORD=function(x,S){return function(){return!0}},v.prototype.LA_RECORD=function(x){return o.END_OF_FILE},v.prototype.topLevelRuleRecord=function(x,S){try{var I=new t.Rule({definition:[],name:x});return I.name=x,this.recordingProdStack.push(I),S.call(this),this.recordingProdStack.pop(),I}catch(A){if(A.KNOWN_RECORDER_ERROR!==!0)try{A.message=A.message+` + This error was thrown during the "grammar recording phase" For more info see: + https://chevrotain.io/docs/guide/internals.html#grammar-recording`}catch{throw A}throw A}},v.prototype.optionInternalRecord=function(x,S){return y.call(this,t.Option,x,S)},v.prototype.atLeastOneInternalRecord=function(x,S){y.call(this,t.RepetitionMandatory,S,x)},v.prototype.atLeastOneSepFirstInternalRecord=function(x,S){y.call(this,t.RepetitionMandatoryWithSeparator,S,x,c)},v.prototype.manyInternalRecord=function(x,S){y.call(this,t.Repetition,S,x)},v.prototype.manySepFirstInternalRecord=function(x,S){y.call(this,t.RepetitionWithSeparator,S,x,c)},v.prototype.orInternalRecord=function(x,S){return d.call(this,x,S)},v.prototype.subruleInternalRecord=function(x,S,I){if(_(S),!x||e.has(x,"ruleName")===!1){var A=new Error(" argument is invalid"+(" expecting a Parser method reference but got: <"+JSON.stringify(x)+">")+(` + inside top level rule: <`+this.recordingProdStack[0].name+">"));throw A.KNOWN_RECORDER_ERROR=!0,A}var C=e.peek(this.recordingProdStack),E=x.ruleName,b=new t.NonTerminal({idx:S,nonTerminalName:E,referencedRule:void 0});return C.definition.push(b),this.outputCst?p:l},v.prototype.consumeInternalRecord=function(x,S,I){if(_(S),!i.hasShortKeyProperty(x)){var A=new Error(" argument is invalid"+(" expecting a TokenType reference but got: <"+JSON.stringify(x)+">")+(` + inside top level rule: <`+this.recordingProdStack[0].name+">"));throw A.KNOWN_RECORDER_ERROR=!0,A}var C=e.peek(this.recordingProdStack),E=new t.Terminal({idx:S,terminalType:x});return C.definition.push(E),f},v}();r.GastRecorder=g;function y(v,x,S,I){I===void 0&&(I=!1),_(S);var A=e.peek(this.recordingProdStack),C=e.isFunction(x)?x:x.DEF,E=new v({definition:[],idx:S});return I&&(E.separator=x.SEP),e.has(x,"MAX_LOOKAHEAD")&&(E.maxLookahead=x.MAX_LOOKAHEAD),this.recordingProdStack.push(E),C.call(this),A.definition.push(E),this.recordingProdStack.pop(),l}function d(v,x){var S=this;_(x);var I=e.peek(this.recordingProdStack),A=e.isArray(v)===!1,C=A===!1?v:v.DEF,E=new t.Alternation({definition:[],idx:x,ignoreAmbiguities:A&&v.IGNORE_AMBIGUITIES===!0});e.has(v,"MAX_LOOKAHEAD")&&(E.maxLookahead=v.MAX_LOOKAHEAD);var b=e.some(C,function(R){return e.isFunction(R.GATE)});return E.hasPredicates=b,I.definition.push(E),e.forEach(C,function(R){var N=new t.Alternative({definition:[]});E.definition.push(N),e.has(R,"IGNORE_AMBIGUITIES")?N.ignoreAmbiguities=R.IGNORE_AMBIGUITIES:e.has(R,"GATE")&&(N.ignoreAmbiguities=!0),S.recordingProdStack.push(N),R.ALT.call(S),S.recordingProdStack.pop()}),l}function m(v){return v===0?"":""+v}function _(v){if(v<0||v>u){var x=new Error("Invalid DSL Method idx value: <"+v+`> + `+("Idx value must be a none negative value smaller than "+(u+1)));throw x.KNOWN_RECORDER_ERROR=!0,x}}}),NS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.PerformanceTracer=void 0;var e=Wt(),t=Xi(),n=function(){function i(){}return i.prototype.initPerformanceTracer=function(s){if(e.has(s,"traceInitPerf")){var o=s.traceInitPerf,a=typeof o=="number";this.traceInitMaxIdent=a?o:1/0,this.traceInitPerf=a?o>0:o}else this.traceInitMaxIdent=0,this.traceInitPerf=t.DEFAULT_PARSER_CONFIG.traceInitPerf;this.traceInitIndent=-1},i.prototype.TRACE_INIT=function(s,o){if(this.traceInitPerf===!0){this.traceInitIndent++;var a=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <"+s+">");var l=e.timer(o),c=l.time,u=l.value,h=c>10?console.warn:console.log;return this.traceInitIndent time: "+c+"ms"),this.traceInitIndent--,u}else return o()},i}();r.PerformanceTracer=n}),LS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.applyMixins=void 0;function e(t,n){n.forEach(function(i){var s=i.prototype;Object.getOwnPropertyNames(s).forEach(function(o){if(o!=="constructor"){var a=Object.getOwnPropertyDescriptor(s,o);a&&(a.get||a.set)?Object.defineProperty(t.prototype,o,a):t.prototype[o]=i.prototype[o]}})})}r.applyMixins=e}),Xi=bt(r=>{"use strict";var e=r&&r.__extends||function(){var A=function(C,E){return A=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(b,R){b.__proto__=R}||function(b,R){for(var N in R)Object.prototype.hasOwnProperty.call(R,N)&&(b[N]=R[N])},A(C,E)};return function(C,E){if(typeof E!="function"&&E!==null)throw new TypeError("Class extends value "+String(E)+" is not a constructor or null");A(C,E);function b(){this.constructor=C}C.prototype=E===null?Object.create(E):(b.prototype=E.prototype,new b)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.EmbeddedActionsParser=r.CstParser=r.Parser=r.EMPTY_ALT=r.ParserDefinitionErrorType=r.DEFAULT_RULE_CONFIG=r.DEFAULT_PARSER_CONFIG=r.END_OF_FILE=void 0;var t=Wt(),n=vS(),i=Co(),s=Xf(),o=TS(),a=ox(),l=MS(),c=SS(),u=AS(),h=wS(),f=CS(),p=IS(),g=RS(),y=PS(),d=NS(),m=LS();r.END_OF_FILE=i.createTokenInstance(i.EOF,"",NaN,NaN,NaN,NaN,NaN,NaN),Object.freeze(r.END_OF_FILE),r.DEFAULT_PARSER_CONFIG=Object.freeze({recoveryEnabled:!1,maxLookahead:3,dynamicTokensEnabled:!1,outputCst:!0,errorMessageProvider:s.defaultParserErrorProvider,nodeLocationTracking:"none",traceInitPerf:!1,skipValidations:!1}),r.DEFAULT_RULE_CONFIG=Object.freeze({recoveryValueFunc:function(){},resyncEnabled:!0});var _;(function(A){A[A.INVALID_RULE_NAME=0]="INVALID_RULE_NAME",A[A.DUPLICATE_RULE_NAME=1]="DUPLICATE_RULE_NAME",A[A.INVALID_RULE_OVERRIDE=2]="INVALID_RULE_OVERRIDE",A[A.DUPLICATE_PRODUCTIONS=3]="DUPLICATE_PRODUCTIONS",A[A.UNRESOLVED_SUBRULE_REF=4]="UNRESOLVED_SUBRULE_REF",A[A.LEFT_RECURSION=5]="LEFT_RECURSION",A[A.NONE_LAST_EMPTY_ALT=6]="NONE_LAST_EMPTY_ALT",A[A.AMBIGUOUS_ALTS=7]="AMBIGUOUS_ALTS",A[A.CONFLICT_TOKENS_RULES_NAMESPACE=8]="CONFLICT_TOKENS_RULES_NAMESPACE",A[A.INVALID_TOKEN_NAME=9]="INVALID_TOKEN_NAME",A[A.NO_NON_EMPTY_LOOKAHEAD=10]="NO_NON_EMPTY_LOOKAHEAD",A[A.AMBIGUOUS_PREFIX_ALTS=11]="AMBIGUOUS_PREFIX_ALTS",A[A.TOO_MANY_ALTS=12]="TOO_MANY_ALTS"})(_=r.ParserDefinitionErrorType||(r.ParserDefinitionErrorType={}));function v(A){return A===void 0&&(A=void 0),function(){return A}}r.EMPTY_ALT=v;var x=function(){function A(C,E){this.definitionErrors=[],this.selfAnalysisDone=!1;var b=this;if(b.initErrorHandler(E),b.initLexerAdapter(),b.initLooksAhead(E),b.initRecognizerEngine(C,E),b.initRecoverable(E),b.initTreeBuilder(E),b.initContentAssist(),b.initGastRecorder(E),b.initPerformanceTracer(E),t.has(E,"ignoredIssues"))throw new Error(`The IParserConfig property has been deprecated. + Please use the flag on the relevant DSL method instead. + See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES + For further details.`);this.skipValidations=t.has(E,"skipValidations")?E.skipValidations:r.DEFAULT_PARSER_CONFIG.skipValidations}return A.performSelfAnalysis=function(C){throw Error("The **static** `performSelfAnalysis` method has been deprecated. \nUse the **instance** method with the same name instead.")},A.prototype.performSelfAnalysis=function(){var C=this;this.TRACE_INIT("performSelfAnalysis",function(){var E;C.selfAnalysisDone=!0;var b=C.className;C.TRACE_INIT("toFastProps",function(){t.toFastProperties(C)}),C.TRACE_INIT("Grammar Recording",function(){try{C.enableRecording(),t.forEach(C.definedRulesNames,function(N){var B=C[N],V=B.originalGrammarAction,ie=void 0;C.TRACE_INIT(N+" Rule",function(){ie=C.topLevelRuleRecord(N,V)}),C.gastProductionsCache[N]=ie})}finally{C.disableRecording()}});var R=[];if(C.TRACE_INIT("Grammar Resolving",function(){R=o.resolveGrammar({rules:t.values(C.gastProductionsCache)}),C.definitionErrors=C.definitionErrors.concat(R)}),C.TRACE_INIT("Grammar Validations",function(){if(t.isEmpty(R)&&C.skipValidations===!1){var N=o.validateGrammar({rules:t.values(C.gastProductionsCache),maxLookahead:C.maxLookahead,tokenTypes:t.values(C.tokensMap),errMsgProvider:s.defaultGrammarValidatorErrorProvider,grammarName:b});C.definitionErrors=C.definitionErrors.concat(N)}}),t.isEmpty(C.definitionErrors)&&(C.recoveryEnabled&&C.TRACE_INIT("computeAllProdsFollows",function(){var N=n.computeAllProdsFollows(t.values(C.gastProductionsCache));C.resyncFollows=N}),C.TRACE_INIT("ComputeLookaheadFunctions",function(){C.preComputeLookaheadFunctions(t.values(C.gastProductionsCache))})),!A.DEFER_DEFINITION_ERRORS_HANDLING&&!t.isEmpty(C.definitionErrors))throw E=t.map(C.definitionErrors,function(N){return N.message}),new Error(`Parser Definition Errors detected: + `+E.join(` +------------------------------- +`))})},A.DEFER_DEFINITION_ERRORS_HANDLING=!1,A}();r.Parser=x,m.applyMixins(x,[a.Recoverable,l.LooksAhead,c.TreeBuilder,u.LexerAdapter,f.RecognizerEngine,h.RecognizerApi,p.ErrorHandler,g.ContentAssist,y.GastRecorder,d.PerformanceTracer]);var S=function(A){e(C,A);function C(E,b){b===void 0&&(b=r.DEFAULT_PARSER_CONFIG);var R=this,N=t.cloneObj(b);return N.outputCst=!0,R=A.call(this,E,N)||this,R}return C}(x);r.CstParser=S;var I=function(A){e(C,A);function C(E,b){b===void 0&&(b=r.DEFAULT_PARSER_CONFIG);var R=this,N=t.cloneObj(b);return N.outputCst=!1,R=A.call(this,E,N)||this,R}return C}(x);r.EmbeddedActionsParser=I}),OS=bt(r=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.createSyntaxDiagramsCode=void 0;var e=ex();function t(n,i){var s=i===void 0?{}:i,o=s.resourceBase,a=o===void 0?"https://unpkg.com/chevrotain@"+e.VERSION+"/diagrams/":o,l=s.css,c=l===void 0?"https://unpkg.com/chevrotain@"+e.VERSION+"/diagrams/diagrams.css":l,u=` + + + + + +`,h=` + +`,f=` + + + + diff --git a/fusion-plating/fusion_plating_configurator/static/src/js/fp_3d_viewer.js b/fusion-plating/fusion_plating_configurator/static/src/js/fp_3d_viewer.js index 204ac0de..6aa5d370 100644 --- a/fusion-plating/fusion_plating_configurator/static/src/js/fp_3d_viewer.js +++ b/fusion-plating/fusion_plating_configurator/static/src/js/fp_3d_viewer.js @@ -1,98 +1,29 @@ /** @odoo-module **/ -// ============================================================================= -// Fusion Plating -- 3D STL Viewer (OWL field widget) +// Fusion Plating -- 3D CAD Viewer (iframe wrapper) // Copyright 2026 Nexa Systems Inc. // License OPL-1 (Odoo Proprietary License v1.0) // -// Renders STL files using Three.js inside an OWL field widget. -// Three.js (+ STLLoader + OrbitControls) are loaded lazily on first use -// via dynamic import() with a programmatic importmap so the vendored ESM -// addon files can resolve their bare `from 'three'` specifier. -// -// Registered as field widget `fp_3d_preview` for Many2one fields -// (ir.attachment). -// ============================================================================= +// Simple OWL field widget that embeds the standalone 3D viewer page +// in an iframe. The viewer page uses Online3DViewer (o3dv) which +// supports STEP, IGES, BREP, STL, OBJ, glTF, and 20+ more formats. -import { Component, useRef, onMounted, onWillUnmount, useState } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; -// --------------------------------------------------------------------------- -// Three.js lazy loader -// --------------------------------------------------------------------------- -let _threePromise = null; - -/** - * Inject an importmap so `from 'three'` inside STLLoader / OrbitControls - * resolves to our vendored three.module.min.js. Then dynamically import - * all three files and return the combined namespace. - */ -async function loadThreeJs() { - if (_threePromise) return _threePromise; - _threePromise = (async () => { - // Inject importmap (idempotent -- only once) - if (!document.querySelector('script[type="importmap"][data-fp-three]')) { - const map = document.createElement("script"); - map.type = "importmap"; - map.setAttribute("data-fp-three", "1"); - map.textContent = JSON.stringify({ - imports: { - three: "/fusion_plating_configurator/static/lib/three.module.min.js", - }, - }); - document.head.appendChild(map); - } - - // Dynamic imports -- browser resolves `from 'three'` via the importmap - const THREE = await import("/fusion_plating_configurator/static/lib/three.module.min.js"); - const { STLLoader } = await import("/fusion_plating_configurator/static/lib/STLLoader.js"); - const { OrbitControls } = await import("/fusion_plating_configurator/static/lib/OrbitControls.js"); - - // Attach for convenience - THREE.STLLoader = STLLoader; - THREE.OrbitControls = OrbitControls; - return THREE; - })(); - return _threePromise; -} - -// --------------------------------------------------------------------------- -// OWL Component -// --------------------------------------------------------------------------- - export class Fp3dViewer extends Component { static template = "fusion_plating_configurator.Fp3dViewer"; - static props = { - ...standardFieldProps, - }; + static props = { ...standardFieldProps }; setup() { - this.canvasRef = useRef("canvas3d"); - this.state = useState({ - loading: false, - error: null, - wireframe: false, - vertexCount: 0, - faceCount: 0, - hasAttachment: false, - }); - this.scene = null; - this.camera = null; - this.renderer = null; - this.controls = null; - this.mesh = null; - this.animationId = null; - - onMounted(() => this._onMounted()); - onWillUnmount(() => this._cleanup()); + this.state = useState({ hasAttachment: false, iframeSrc: "" }); + this._updateState(); } - /** Return the raw value of the Many2one field (could be [id, name] or false). */ get rawValue() { return this.props.record.data[this.props.name]; } - /** Return the attachment id (integer) or 0. */ get attachmentId() { const v = this.rawValue; if (!v) return 0; @@ -101,190 +32,28 @@ export class Fp3dViewer extends Component { return typeof v === "number" ? v : 0; } - async _onMounted() { + get attachmentName() { + const v = this.rawValue; + if (!v) return ""; + if (Array.isArray(v)) return v[1] || ""; + if (typeof v === "object" && v.display_name) return v.display_name; + return ""; + } + + _updateState() { const aid = this.attachmentId; this.state.hasAttachment = !!aid; - if (!aid || !this.canvasRef.el) return; - await this._initViewer(); - } - - async _initViewer() { - this.state.loading = true; - this.state.error = null; - - let THREE; - try { - THREE = await loadThreeJs(); - } catch (e) { - // importmap injection may fail if the page already has one -- fall - // back to loading Three.js core alone and skip addons. - this.state.error = "Three.js failed to load: " + (e.message || e); - this.state.loading = false; - return; - } - - const container = this.canvasRef.el; - const width = container.clientWidth || 500; - const height = 350; - - // ---- Scene ---- - this.scene = new THREE.Scene(); - // Respect Odoo theme -- use a neutral slightly-warm grey - this.scene.background = new THREE.Color(0xf5f5f5); - - // ---- Camera ---- - this.camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000); - this.camera.position.set(0, 0, 100); - - // ---- Renderer ---- - this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - this.renderer.setPixelRatio(window.devicePixelRatio || 1); - this.renderer.setSize(width, height); - container.appendChild(this.renderer.domElement); - - // ---- Lights ---- - const ambient = new THREE.AmbientLight(0x808080, 1.5); - this.scene.add(ambient); - const dir1 = new THREE.DirectionalLight(0xffffff, 1.0); - dir1.position.set(1, 1, 1); - this.scene.add(dir1); - const dir2 = new THREE.DirectionalLight(0xffffff, 0.4); - dir2.position.set(-1, -0.5, -1); - this.scene.add(dir2); - - // ---- Orbit controls ---- - if (THREE.OrbitControls) { - this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement); - this.controls.enableDamping = true; - this.controls.dampingFactor = 0.12; - } - - // ---- Load STL ---- - try { - const url = `/web/content/${this.attachmentId}`; - const response = await fetch(url); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - const buffer = await response.arrayBuffer(); - - let geometry; - if (THREE.STLLoader) { - const loader = new THREE.STLLoader(); - geometry = loader.parse(buffer); - } else { - // Fallback: parse binary STL manually - geometry = this._parseSTLBinary(THREE, buffer); - } - geometry.computeVertexNormals(); - - const material = new THREE.MeshPhongMaterial({ - color: 0x1a8cff, - specular: 0x333333, - shininess: 120, - wireframe: false, - }); - this.mesh = new THREE.Mesh(geometry, material); - - // Centre and auto-scale to fit viewport - geometry.computeBoundingBox(); - const box = geometry.boundingBox; - const center = box.getCenter(new THREE.Vector3()); - const size = box.getSize(new THREE.Vector3()); - const maxDim = Math.max(size.x, size.y, size.z); - const scale = 60 / (maxDim || 1); - - this.mesh.geometry.translate(-center.x, -center.y, -center.z); - this.mesh.scale.set(scale, scale, scale); - this.scene.add(this.mesh); - - this.state.vertexCount = geometry.attributes.position.count; - this.state.faceCount = Math.floor(geometry.attributes.position.count / 3); - this.state.loading = false; - - this._animate(); - } catch (e) { - this.state.error = "Failed to load STL: " + (e.message || e); - this.state.loading = false; + if (aid) { + const name = encodeURIComponent(this.attachmentName); + this.state.iframeSrc = `/fp/3d-viewer?id=${aid}&name=${name}`; } } - /** - * Minimal binary STL parser (fallback when STLLoader is unavailable). - * Binary STL: 80-byte header, 4-byte uint32 triangle count, then - * 50 bytes per triangle (12 floats for normal + 3 vertices, 2-byte attr). - */ - _parseSTLBinary(THREE, buffer) { - const dv = new DataView(buffer); - const triangles = dv.getUint32(80, true); - const positions = new Float32Array(triangles * 9); - const normals = new Float32Array(triangles * 9); - let offset = 84; - for (let i = 0; i < triangles; i++) { - const nx = dv.getFloat32(offset, true); - const ny = dv.getFloat32(offset + 4, true); - const nz = dv.getFloat32(offset + 8, true); - offset += 12; - for (let v = 0; v < 3; v++) { - const idx = i * 9 + v * 3; - positions[idx] = dv.getFloat32(offset, true); - positions[idx + 1] = dv.getFloat32(offset + 4, true); - positions[idx + 2] = dv.getFloat32(offset + 8, true); - normals[idx] = nx; - normals[idx + 1] = ny; - normals[idx + 2] = nz; - offset += 12; - } - offset += 2; // attribute byte count - } - const geo = new THREE.BufferGeometry(); - geo.setAttribute("position", new THREE.BufferAttribute(positions, 3)); - geo.setAttribute("normal", new THREE.BufferAttribute(normals, 3)); - return geo; - } - - _animate() { - this.animationId = requestAnimationFrame(() => this._animate()); - if (this.controls) this.controls.update(); - if (this.renderer && this.scene && this.camera) { - this.renderer.render(this.scene, this.camera); - } - } - - toggleWireframe() { - if (!this.mesh) return; - this.state.wireframe = !this.state.wireframe; - this.mesh.material.wireframe = this.state.wireframe; - } - - resetView() { - if (!this.camera) return; - this.camera.position.set(0, 0, 100); - this.camera.lookAt(0, 0, 0); - if (this.controls) this.controls.reset(); - } - - _cleanup() { - if (this.animationId) { - cancelAnimationFrame(this.animationId); - this.animationId = null; - } - if (this.controls) { - this.controls.dispose(); - this.controls = null; - } - if (this.renderer) { - this.renderer.dispose(); - if (this.renderer.domElement && this.renderer.domElement.parentNode) { - this.renderer.domElement.parentNode.removeChild(this.renderer.domElement); - } - this.renderer = null; - } - this.scene = null; - this.camera = null; - this.mesh = null; + onPatched() { + this._updateState(); } } -// Register as a field widget for Many2one (ir.attachment) fields registry.category("fields").add("fp_3d_preview", { component: Fp3dViewer, supportedTypes: ["many2one"], diff --git a/fusion-plating/fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss b/fusion-plating/fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss index 50014ccb..3d67416b 100644 --- a/fusion-plating/fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss +++ b/fusion-plating/fusion_plating_configurator/static/src/scss/fp_3d_viewer.scss @@ -1,62 +1,63 @@ // ============================================================================= -// Fusion Plating -- 3D Viewer Widget Styles +// Fusion Plating -- 3D Viewer + Configurator Layout // Copyright 2026 Nexa Systems Inc. // License OPL-1 (Odoo Proprietary License v1.0) // ============================================================================= +// -- Configurator two-column layout: 3/4 fields + 1/4 preview -- +.o_fp_cfg_layout { + display: grid; + grid-template-columns: 1fr 320px; + gap: 16px; + align-items: start; +} + +.o_fp_cfg_fields { + min-width: 0; +} + +.o_fp_cfg_preview { + position: sticky; + top: 16px; +} + +// Responsive: stack on narrow screens +@media (max-width: 1200px) { + .o_fp_cfg_layout { + grid-template-columns: 1fr; + } + .o_fp_cfg_preview { + position: static; + } +} + +// -- 3D viewer widget -- .o_fp_3d_viewer_root { width: 100%; } .o_fp_3d_placeholder { border: 2px dashed $border-color; - border-radius: 0.375rem; - min-height: 120px; + border-radius: 0.5rem; + min-height: 200px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--bs-secondary-color); + background-color: var(--bs-tertiary-bg); } -.o_fp_3d_toolbar { - .btn { - font-size: 0.8125rem; - padding: 0.2rem 0.5rem; - } -} - -.o_fp_3d_canvas_container { +.o_fp_3d_iframe { width: 100%; - height: 350px; + height: 500px; border: 1px solid $border-color; - border-radius: 0.375rem; - overflow: hidden; - position: relative; - background-color: var(--bs-body-bg); - - canvas { - display: block; - width: 100% !important; - height: 100% !important; - } + border-radius: 0.5rem; + background-color: #f0f2f5; + display: block; } -.o_fp_3d_loading { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: var(--bs-body-bg); - color: var(--bs-body-color); - z-index: 10; -} - -.o_fp_3d_error { - font-size: 0.875rem; +// Inside the preview column, make iframe taller +.o_fp_cfg_preview .o_fp_3d_iframe { + height: 600px; } diff --git a/fusion-plating/fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml b/fusion-plating/fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml index 2c5874ca..bdf66b62 100644 --- a/fusion-plating/fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml +++ b/fusion-plating/fusion_plating_configurator/static/src/xml/fp_3d_viewer.xml @@ -1,57 +1,19 @@ -
-
- Upload a 3D model (STL) to preview it here. + Upload a 3D model (STL, STEP, IGES) to preview it here.
- - - -
- - - - - faces - / - verts - -
- - -
- -
- - -
- -
Loading 3D model...
-
- - -
- - -
+