@rr0/ufoathome 0.81.4 → 0.81.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2643,7 +2643,7 @@ canvas[data-cursor="rotate"] {
|
|
|
2643
2643
|
}
|
|
2644
2644
|
`;class Qa{constructor(e){this.points=e}static STATIONARY_M=5;static of(e){const t=[];for(const i of e.observerTrack.allKeyframes){const{lat:n,lng:s,headingDeg:r}=i.pose;n===void 0||s===void 0||t.push({t:i.t,lat:n,lng:s,headingDeg:r})}if(t.length===0){const i=e.event.place?.[0];if(!i)return;t.push({t:0,lat:i.lat,lng:i.lng})}return new Qa(t)}static TRAVEL_M=50;get travels(){return this.spanM>Qa.TRAVEL_M}get moved(){return this.spanM>Qa.STATIONARY_M}get spanM(){const e=this.box,t=(e.south+e.north)/2,i=(e.west+e.east)/2;return Math.max(Math.abs(pi(t,e.east,t,e.west).x),Math.abs(pi(e.north,i,e.south,i).z))}get center(){const e=this.box;return{lat:(e.south+e.north)/2,lng:(e.west+e.east)/2}}boundsAround(e,t){const{lat:i,lng:n}=this.center,s=Math.max(this.spanM*(1+t),e)/2;return{north:Ns(0,-s,i,n).lat,south:Ns(0,s,i,n).lat,east:Ns(s,0,i,n).lng,west:Ns(-s,0,i,n).lng}}get box(){const e=this.points.map(i=>i.lat),t=this.points.map(i=>i.lng);return{south:Math.min(...e),north:Math.max(...e),west:Math.min(...t),east:Math.max(...t)}}}class _n{constructor(e,t){this.rotationPassed=e,this.scale=t}static SWAY_M=.012;static BREATH_M=.003;static DRIFT_YAW_DEG=.5;static DRIFT_PITCH_DEG=.4;static DRIFT_ROLL_DEG=.3;static GAZE_DRIFT_SHARE=.35;static HAND_HELD_S=.5;static HZ=[.11,.23,.17,.29,.37,.07];static of(e){const t=e.event.tags?.includes("paralysis")||(e.exposure??0)>_n.HAND_HELD_S,i=e.sway??(t?0:1);if(i>0)return new _n(Math.max(1-(e.instrument.stabilization??0),_n.GAZE_DRIFT_SHARE),i)}offsetAt(e){const t=e/1e3,i=this.scale,n=(d,u)=>Math.sin(2*Math.PI*d*t+u),[s,r,o,l,c,h]=_n.HZ;return{eastM:i*_n.SWAY_M*(.7*n(s,.3)+.3*n(l,1.9)),northM:i*_n.SWAY_M*(.6*n(o,2.2)+.4*n(c,.8)),upM:i*_n.BREATH_M*n(r,0),yawDeg:i*this.rotationPassed*_n.DRIFT_YAW_DEG*(.6*n(h,1.1)+.4*n(l,2.7)),pitchDeg:i*this.rotationPassed*_n.DRIFT_PITCH_DEG*(.5*n(r,1.6)+.5*n(o,.4)),rollDeg:i*this.rotationPassed*_n.DRIFT_ROLL_DEG*n(s,2.9)}}}class Tt{constructor(e,t){this.stretches=e,this.rotationPassed=t}static REFERENCE_SPEED_M_PER_S=1.4;static REFERENCE_STEP_HZ=1.9;static CADENCE_EXPONENT=.43;static RISE_M_PER_M_PER_S=.0175;static SWAY_M=.0175;static SLOWEST_WALK_M_PER_S=.2;static FASTEST_WALK_M_PER_S=2.2;static HEAD_ROLL_DEG=1.5;static HEAD_PITCH_DEG=1.25;static HEAD_YAW_DEG=1;static STOP_LURCH_M=.035;static STOP_PITCH_DEG=3;static START_LURCH_M=.015;static START_PITCH_DEG=1.2;static JOLT_HZ=1.4;static JOLT_DECAY_S=.35;static JOLT_LASTS_MS=1500;static STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(e){const t=Qa.of(e);if(!t||t.points.length<2)return;const i=[];let n=0;for(let s=1;s<t.points.length;s++){const r=t.points[s-1],o=t.points[s],l=(o.t-r.t)/1e3;if(l<=0)continue;const c=pi(o.lat,o.lng,r.lat,r.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),p=u/l,f=Tt.stepHzFor(p);i.push({startMs:r.t,endMs:o.t,speedMPerS:p,eastUnit:u>0?h/u:0,northUnit:u>0?d/u:0,stepHz:f,stepsBefore:n,rampsIn:!0,rampsOut:!0}),n+=f*l}for(let s=0;s<i.length;s++){const r=i[s-1],o=i[s+1];i[s].rampsIn=r===void 0||r.stepHz===0,i[s].rampsOut=o===void 0||o.stepHz===0}if(i.some(s=>s.stepHz>0))return new Tt(i,1-(e.instrument.stabilization??0))}static bodyAt(e,t,i=!1){const n=Tt.of(e)?.offsetAt(t)??Tt.STILL;return i?n:Tt.add(n,_n.of(e)?.offsetAt(t))}static stepHzFor(e){return e<Tt.SLOWEST_WALK_M_PER_S||e>Tt.FASTEST_WALK_M_PER_S?0:Tt.REFERENCE_STEP_HZ*Math.pow(e/Tt.REFERENCE_SPEED_M_PER_S,Tt.CADENCE_EXPONENT)}offsetAt(e){return Tt.add(this.cycleAt(e),this.joltAt(e))}joltAt(e){for(const t of this.stretches){if(t.stepHz===0)continue;const i=t.speedMPerS/Tt.REFERENCE_SPEED_M_PER_S,n=(o,l,c,h)=>{if(o<0||o>Tt.JOLT_LASTS_MS)return;const d=o/1e3,u=Math.exp(-d/Tt.JOLT_DECAY_S)*Math.sin(2*Math.PI*Tt.JOLT_HZ*d),p=h*l*i*u;return{eastM:p*t.eastUnit,northM:p*t.northUnit,upM:-Math.abs(p)*.3,rollDeg:0,pitchDeg:-h*c*i*u*this.rotationPassed,yawDeg:0}},s=t.rampsOut?n(e-t.endMs,Tt.STOP_LURCH_M,Tt.STOP_PITCH_DEG,1):void 0;if(s)return s;const r=t.rampsIn?n(e-t.startMs,Tt.START_LURCH_M,Tt.START_PITCH_DEG,-1):void 0;if(r)return r}}static add(e,t){return t?{eastM:e.eastM+t.eastM,northM:e.northM+t.northM,upM:e.upM+t.upM,rollDeg:e.rollDeg+t.rollDeg,pitchDeg:e.pitchDeg+t.pitchDeg,yawDeg:e.yawDeg+t.yawDeg}:e}cycleAt(e){const t=this.stretches.find(l=>e>=l.startMs&&e<=l.endMs);if(!t||t.stepHz===0)return Tt.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),r=Tt.SWAY_M*s*Math.cos(n/2),o=s*this.rotationPassed*t.speedMPerS/Tt.REFERENCE_SPEED_M_PER_S;return{eastM:r*t.northUnit,northM:-r*t.eastUnit,upM:Tt.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:Tt.HEAD_ROLL_DEG*o*Math.cos(n/2),pitchDeg:-1.25*o*Math.cos(n),yawDeg:Tt.HEAD_YAW_DEG*o*Math.cos(n/2)}}amplitudeAt(e,t){const i=1e3/e.stepHz,n=Math.min(i,(e.endMs-e.startMs)/2);if(n<=0)return 1;const s=t-e.startMs,r=e.endMs-t,o=Math.min(e.rampsIn?s/n:1,e.rampsOut?r/n:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class Ly{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const i of this.timeline.sourceIds){const n=this.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}this.onFrame(e,t)}}class Qr{static sessionDeclared=!1;static unlock(e){if(Qr.declarePlayback(),e.state!=="running")try{Promise.resolve(e.resume()).catch(()=>{});const t=e.createBufferSource();t.buffer=e.createBuffer(1,1,22050),t.connect(e.destination),t.start(0)}catch{}}static declarePlayback(){if(!(Qr.sessionDeclared||typeof navigator>"u")){Qr.sessionDeclared=!0;try{const e=navigator.audioSession;e&&(e.type="playback")}catch{}}}}const _0=.05,A0=.08,T0=2,I0=3,ky=14,Oy=.02;class Ny{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;level=1;master;output(e){return this.master||(this.master=e.createGain(),this.master.gain.value=this.level,this.master.connect(e.destination)),this.master}setLevel(e){this.level=e,this.master&&(this.master.gain.value=e)}resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",e);return}}Qr.unlock(this.context)}setSound(e){if(this.requested=e,!this.context)return;const t=this.keyFor(e);if(!t){this.silence();return}if(this.voice?.key!==t){this.stopVoice();const i=++this.voiceToken;t.startsWith("src:")?this.buildRecordedVoice(e.src,t,i):this.voice=this.buildSynthesizedVoice(e,t)}this.applyTo(this.voice,e)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.master=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(e){if(!(e.volume<=0))return e.src?`src:${e.src}`:e.kind==="none"?void 0:e.kind}applyTo(e,t){const i=this.context;if(!e||!i)return;const n=i.currentTime;e.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,t.volume))*e.gainScale,n,_0);for(const{param:s,ratio:r}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*r,i.sampleRate/2-1),n,_0)}buildSynthesizedVoice(e,t){const i=this.context;if(i)switch(e.kind){case"hum":return this.buildHum(i,t);case"whistle":return this.buildWhistle(i,t);case"rumble":return this.buildRumble(i,t);case"crackle":return this.buildCrackle(i,t);default:return}}buildHum(e,t){const i=e.createOscillator();i.type="sawtooth";const n=e.createOscillator();n.type="sawtooth";const s=e.createBiquadFilter();s.type="lowpass",s.Q.value=1;const r=e.createGain();return r.gain.value=0,i.connect(s),n.connect(s),s.connect(r).connect(this.output(e)),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.16,pitch:[{param:i.frequency,ratio:1},{param:n.frequency,ratio:1.006},{param:s.frequency,ratio:6}]}}buildWhistle(e,t){const i=e.createOscillator();i.type="sine";const n=e.createOscillator();n.type="sine",n.frequency.value=5;const s=e.createGain(),r=e.createGain();return r.gain.value=0,n.connect(s).connect(i.frequency),i.connect(r).connect(this.output(e)),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.22,pitch:[{param:i.frequency,ratio:1},{param:s.gain,ratio:.012}]}}buildRumble(e,t){const i=e.createBufferSource();i.buffer=this.brownNoise(e),i.loop=!0;const n=e.createBiquadFilter();n.type="lowpass",n.Q.value=.8;const s=e.createGain();return s.gain.value=0,i.connect(n).connect(s).connect(this.output(e)),i.start(),{key:t,nodes:[n,s],sources:[i],volumeParam:s.gain,gainScale:1.4,pitch:[{param:n.frequency,ratio:1}]}}buildCrackle(e,t){const i=e.createBufferSource();i.buffer=this.whiteNoise(e),i.loop=!0;const n=e.createBiquadFilter();n.type="bandpass",n.Q.value=1.2;const s=e.createGain();s.gain.value=0;const r=e.createBufferSource();r.buffer=this.crackleEnvelope(e),r.loop=!0;const o=e.createGain();return o.gain.value=0,r.connect(o).connect(s.gain),i.connect(n).connect(s).connect(this.output(e)),i.start(),r.start(),{key:t,nodes:[n,s,o],sources:[i,r],volumeParam:o.gain,gainScale:.9,pitch:[{param:n.frequency,ratio:4}]}}async buildRecordedVoice(e,t,i){const n=this.context;if(!n)return;let s;try{s=await this.getBuffer(e)}catch(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(i!==this.voiceToken||!this.context)return;const r=n.createBufferSource();r.buffer=s,r.loop=!0;const o=n.createGain();o.gain.value=0,r.connect(o).connect(this.output(n)),r.start(),this.voice={key:t,nodes:[o],sources:[r],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let i=this.buffers.get(e);return i||(i=fetch(e).then(n=>{if(!n.ok)throw new Error(`Audio fetch failed (${n.status}): ${e}`);return n.arrayBuffer()}).then(n=>t.decodeAudioData(n)),this.buffers.set(e,i)),i}whiteNoise(e){if(!this.noiseBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*T0),e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;this.noiseBuffer=t}return this.noiseBuffer}brownNoise(e){const t=e.createBuffer(1,Math.floor(e.sampleRate*T0),e.sampleRate),i=t.getChannelData(0);let n=0,s=0;for(let r=0;r<i.length;r++)n=(n+.02*(Math.random()*2-1))/1.02,i[r]=n,s=Math.max(s,Math.abs(n));if(s>0)for(let r=0;r<i.length;r++)i[r]/=s;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*I0),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(Oy*e.sampleRate)),s=Math.round(I0*ky);for(let r=0;r<s;r++){const o=Math.floor(Math.random()*i.length),l=.4+Math.random()*.6;for(let c=0;c<n&&o+c<i.length;c++)i[o+c]=Math.max(i[o+c],l*(1-c/n))}this.crackleBuffer=t}return this.crackleBuffer}stopVoice(){const e=this.voice,t=this.context;if(this.voice=void 0,!e||!t)return;const i=t.currentTime;e.volumeParam.cancelScheduledValues(i),e.volumeParam.setTargetAtTime(0,i,A0/3);const n=i+A0;for(const s of e.sources){s.onended=()=>{s.disconnect();for(const r of e.nodes)r.disconnect()};try{s.stop(n)}catch{s.disconnect();for(const r of e.nodes)r.disconnect()}}}}class je{static svg(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="currentColor"/></svg>`}static outline(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round"/></svg>`}static solid(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="currentColor" fill-rule="evenodd"/></svg>`}static BOOKMARK="M6 3.5h12a.5.5 0 0 1 .5.5v16.5L12 16.8l-6.5 3.7V4a.5.5 0 0 1 .5-.5z";static PICTURE="M5 4h14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z";static PICTURE_HILLS="M4.5 17.5l4.5-6 3 4 2.5-3 5 5z";static PIN="M12 21.5s-6.5-5.7-6.5-10.8a6.5 6.5 0 0 1 13 0c0 5.1-6.5 10.8-6.5 10.8z";static PIN_HOLE="M12 8.4a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8z";static MILESTONES_OFF=je.outline(je.BOOKMARK);static MILESTONES_ON=je.solid(je.BOOKMARK);static PICTURES_OFF=je.outline(`${je.PICTURE}${je.PICTURE_HILLS}`);static PICTURES_ON=je.solid(`${je.PICTURE}${je.PICTURE_HILLS}`);static MAP_OFF=je.outline(`${je.PIN}${je.PIN_HOLE}`);static MAP_ON=je.solid(`${je.PIN}${je.PIN_HOLE}`);static SHARE=je.outline("M14 4l7 7-7 7v-4.2C8.6 13.8 5.4 15.6 3.5 19c.6-5.6 3.4-9.6 10.5-10.8V4z");static FOLDED=je.svg("M15.4 7.4 14 6l-6 6 6 6 1.4-1.4L10.8 12z");static UNFOLDED=je.svg("M8.6 16.6 10 18l6-6-6-6-1.4 1.4 4.6 4.6z");static PLAY=je.svg("M7.4 5v14l11-7z");static PAUSE=je.svg("M6 5h4v14H6zM14 5h4v14h-4z");static VOLUME=je.svg("M2 9.5v5h3.5l4.5 4.5V5L5.5 9.5H2zm12.5 2.5A4 4 0 0 0 12.5 8.5v7A4 4 0 0 0 14.5 12zM12.5 4.2v2.1a6.5 6.5 0 0 1 0 11.4v2.1a8.6 8.6 0 0 0 0-15.6z");static MUTED=je.svg("M2 9.5v5h3.5l4.5 4.5V5L5.5 9.5H2zM15 9.8l1.4-1.4 2.6 2.6 2.6-2.6 1.4 1.4-2.6 2.6 2.6 2.6-1.4 1.4-2.6-2.6-2.6 2.6-1.4-1.4 2.6-2.6z");static ENTER_FULLSCREEN=je.svg("M5 5h5v2H7v3H5V5zm9 0h5v5h-2V7h-3V5zM5 14h2v3h3v2H5v-5zm12 0h2v5h-5v-2h3v-3z");static EXIT_FULLSCREEN=je.svg("M8 5h2v5H5V8h3V5zm6 0h2v3h3v2h-5V5zM5 14h5v5H8v-3H5v-2zm9 0h5v2h-3v3h-2v-5z")}function ea(a){return[...a].sort((e,t)=>e.t-t.t)}function ed(a,e){let t;for(const i of ea(a)){if(i.t>e)break;t=i}return t}const td=Math.PI/180,Bl=180/Math.PI;class Vn{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*td/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new Vn(e.projection,t,i)}degToPx(e){const t=e*td;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*Bl:2*Math.atan(e/(2*this.focalPx))*Bl}angleDegToRadiusPx(e){const t=e*td;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}halfWidthAngleDeg(e){return this.radiusPxToAngleDeg(this.canvasHeightPx*e/2)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*Bl:Math.atan(e/this.focalPx)*Bl}ofBounds(e){return{widthDeg:this.pxToDeg(e.width),heightDeg:this.pxToDeg(e.height)}}toBoundsSize(e){return{width:this.degToPx(e.widthDeg),height:this.degToPx(e.heightDeg)}}widthPx(e){return this.degToPx(Qe.angularWidthDeg(e))}}class wi{static fovOf(e,t){return st(e,t)?.fovDeg??St.fieldOfViewDeg(e.instrument)}static toAngular(e){this.eachKeyframe(e,(t,i)=>({...t,angular:i.ofBounds(t.bounds)}))}static toBounds(e){this.eachKeyframe(e,(t,i)=>{if(!t.angular)return t;const{width:n,height:s}=i.toBoundsSize(t.angular),r={x:t.bounds.x+(t.bounds.width-n)/2,y:t.bounds.y+(t.bounds.height-s)/2,width:n,height:s};if(t.kind!=="polygon")return{...t,bounds:r};const o=t.bounds.width===0?1:n/t.bounds.width,l=t.bounds.height===0?1:s/t.bounds.height;return{...t,bounds:r,points:t.points.map(c=>({x:c.x*o,y:c.y*l}))}})}static toAim(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);if(s&&Math.abs(s.x-t.bounds.x)<wi.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<wi.UNMOVED_PX)return t;const r=this.aimFrom(e,i,n,t.bounds);return r?{...t,aim:r}:t})}static UNMOVED_PX=.01;static aimOf(e,t,i){const n=Vn.of(e.instrument,Qe.CANVAS_HEIGHT_PX,this.fovOf(e,t));return this.aimFrom(e,n,st(e,t),i)}static aimFrom(e,t,i,n){if(i?.headingDeg===void 0)return;const s=this.frameCentre(e),r=t.radiusPxToAngleDeg(n.x+n.width/2-s.x),o=t.radiusPxToAngleDeg(s.y-(n.y+n.height/2));return{azimuthDeg:((i.headingDeg+r)%360+360)%360,altitudeDeg:i.pitchDeg+o}}static toPosition(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);return s?{...t,bounds:{...t.bounds,...s}}:t})}static positionOf(e,t,i,n,s){if(s?.headingDeg===void 0)return;const r=this.frameCentre(e),o=this.shortestArc(t.azimuthDeg-s.headingDeg),l=t.altitudeDeg-s.pitchDeg;return{x:r.x+this.offAxisPx(n,o)-i.width/2,y:r.y-this.offAxisPx(n,l)-i.height/2}}static frameCentre(e){return{x:St.frameWidthPx(e.instrument,Qe.CANVAS_HEIGHT_PX)/2,y:Qe.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=wi.OFF_CANVAS_DEG?Math.sign(t)*wi.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=St.frameWidthPx(t,Qe.CANVAS_HEIGHT_PX)/2,s=St.frameWidthPx(e.instrument,Qe.CANVAS_HEIGHT_PX)/2,r=Qe.CANVAS_HEIGHT_PX/2;for(const o of[...e.timeline.allKeyframes]){const l=this.fovOf(e,o.t),c=i?.get(o.t)??l,h=Vn.of(t,Qe.CANVAS_HEIGHT_PX,c),d=Vn.of(e.instrument,Qe.CANVAS_HEIGHT_PX,l);e.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:p}=u.shape,f=p.x+p.width/2-n,v=p.y+p.height/2-r,m=Math.hypot(f,v),g=m===0?1:d.angleDegToRadiusPx(h.radiusPxToAngleDeg(m))/m,x={...p,x:s+f*g-p.width/2,y:r+v*g-p.height/2};return{...u,shape:{...u.shape,bounds:x}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:i}=e;for(const n of[...i.allKeyframes]){const s=st(e,n.t),r=this.fovOf(e,n.t),o=Vn.of(e.instrument,Qe.CANVAS_HEIGHT_PX,r);i.addKeyframe(n.t,n.shapes.map(l=>({...l,shape:t(l.shape,o,s)})))}}}function Fy(a){return vn.restore(Pu(a),a.provenance)}function Pu(a){return wi.toAngular(a),wi.toAim(a),{version:1,id:a.id,time:a.event.time,endTime:a.event.endTime,durationSeconds:a.event.durationSeconds,utcOffsetHours:a.event.utcOffsetHours,timeZone:a.event.timeZone,place:a.event.place,observer:a.observer,account:a.account,sources:a.sources?.length?a.sources:void 0,sway:a.sway,vehicle:a.vehicle,description:a.event.description,tags:a.event.tags,timeline:a.timeline.toJSON(),observerTrack:a.observerTrack.toJSON(),weatherTrack:a.weatherTrack.toJSON(),soundTrack:a.soundTrack.toJSON(),weather:a.weather,decor:a.decor,milestones:a.milestones.length>0?a.milestones:void 0,references:a.references.length>0?a.references:void 0,roads:a.roads.length>0?a.roads:void 0,weatherSource:a.weatherSource,instrument:a.instrumentId,exposureSeconds:a.exposureSeconds,iso:a.iso,lightPollution:a.lightPollution,interpretation:a.interpretation}}function Uy(a){const{recording:e,provenance:t}=vn.strip(a),i=By(e);return i.provenance=t,i}function By(a){const e=[],t=new Lh({eventType:"sighting",time:a.time,endTime:a.endTime,durationSeconds:a.durationSeconds,utcOffsetHours:a.utcOffsetHours,timeZone:a.timeZone,place:a.place,description:a.description,tags:a.tags},Ph.fromJSON(a.timeline,e),a.observerTrack?fl.fromJSON(a.observerTrack):new fl,a.weatherTrack?ml.fromJSON(a.weatherTrack):new ml,a.soundTrack?gl.fromJSON(a.soundTrack):new gl,a.observer&&{id:a.observer.id,title:a.observer.title,lastName:a.observer.lastName,firstNames:a.observer.firstNames},a.id,a.weather,a.decor??[],a.weatherSource,a.instrument,a.exposureSeconds??a.observerTrack?.keyframes.map(i=>i.pose.exposureSeconds).find(i=>i!==void 0),ea(a.milestones??[]),a.references??[],a.roads??[]);return t.account=a.account,t.sources=a.sources,t.sway=a.sway,t.iso=a.iso,t.lightPollution=a.lightPollution,t.vehicle=a.vehicle,t.interpretation=a.interpretation,t.loadIssues=e,wi.toBounds(t),wi.toPosition(t),t}class po{static EN={"unknown-key":"{path}: unknown key, ignored","wrong-type":"{path}: expected {expected}, found {found}","not-in-list":"{path}: {found} is not one of {expected}",defaulted:"{path}: missing, {expected} used",unplaced:"{path}: no aim (and no bounds), drawn at the middle of the view",unsized:"{path}: no angular size (and no bounds), drawn one pixel wide","pixels-over-angles":"{path}: bounds restated without aim or angular, so the shape follows the pixels"};static text(e,t=po.EN){return t[e.kind].replace("{path}",e.path).replace("{expected}",e.expected??"").replace("{found}",e.found??"")}static warn(e,t){if(e.length===0)return;const i=e.map(n=>` ${po.text(n)}`);console.warn(`UFO@home: ${e.length} problem(s) in ${t??"this recording"}:
|
|
2645
2645
|
${i.join(`
|
|
2646
|
-
`)}`)}}class On{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!On.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:On.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const o=i?`${i}.${s}`:s,l=t[s];l?this.field(r,l,o,n):n.push({kind:"unknown-key",path:o})}}field(e,t,i,n){if(On.isStated(e)){this.field(e.value,t,i,n);return}if(!(t.loose||e===void 0||e===null))switch(t.type){case"enum":(typeof e!="string"||!t.values?.includes(e))&&n.push({kind:"not-in-list",path:i,expected:(t.values??[]).map(s=>`"${s}"`).join(", "),found:JSON.stringify(e)});return;case"object":t.fields?this.object(e,t.fields,i,n):On.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:On.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const o=On.isStated(s)?s.value:s;this.object(o,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:On.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:On.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return On.isObject(e)&&"value"in e&&Object.keys(e).every(t=>t==="value"||t==="basis"||t==="rationale")}static typeOf(e){return Array.isArray(e)?"array":e===null?"null":typeof e}}class Bn{static PIXELS_PER_INSTANT=2;static INSTANTS_PER_FLASH=2;static MAX_INSTANTS=512;static windowEndingAt(e,t){const i=Number.isFinite(t)?Math.max(0,t):0,n=Math.max(0,e-i*1e3),s=Math.max(0,e-n);return{fromMs:n,toMs:e,ms:s,seconds:s/1e3}}static instants(e,t,i,n,s){if(!(n>0))return 1;const r=s>0?Bn.travelDegOver(e,t,i,i+n*1e3)/s/Bn.PIXELS_PER_INSTANT:0,o=Bn.flashesOver(e,n)*Bn.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(r,o));return l<2?1:Math.min(Bn.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const o=Bn.directionAt(r,t,i),l=Bn.directionAt(r,t,n),c=Math.min(1,Math.max(-1,o.x*l.x+o.y*l.y+o.z*l.z));s=Math.max(s,Math.acos(c)*180/Math.PI)}return s}static flashesOver(e,t){let i=0;for(const n of e)for(const s of n.lights??[])s.pattern.kind==="flash"&&(i=Math.max(i,s.pattern.perMinute));return i/60*t}static directionAt(e,t,i){const n=yr(e,i),s=n.eastM,r=n.altitudeM-t,o=n.northM,l=Math.hypot(s,r,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:r/l,z:o/l}}}function rh(a,e){for(const t of a){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class ta{static preferencesFor(e){const t=navigator.languages??[],i=this.declaredFor(e);return i?[i,...t]:t}static declaredFor(e){for(let t=e;t;){const i=t.closest("[lang]")?.getAttribute("lang")?.trim();if(i)return i;const n=t.getRootNode();t=n instanceof ShadowRoot?n.host:void 0}}}class fs{constructor(e){this.preferences=e}read(e){if(e===void 0||typeof e=="string")return e===""?void 0:e;for(const i of this.preferences){const n=e[i]??e[fs.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>fs.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(fs.base(i)))return n;return Object.values(e).find(i=>i)??void 0}write(e,t,i){const n=t?.trim()?t.trim():void 0;if(e===void 0||typeof e=="string")return n;const s={...e},r=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>fs.base(o)===fs.base(i))??i;return n===void 0?delete s[r]:s[r]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const Hv={play:"Play",pause:"Pause",noDuration:"No observation duration",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",mute:"Mute",unmute:"Unmute",moments:"Moments",moreControls:"More controls",fewerControls:"Fewer controls",showObserverMap:"Show where the observer was",hideObserverMap:"Hide where the observer was",mapImageryUnavailable:"Aerial imagery unavailable",zoomMapIn:"Zoom in",zoomMapOut:"Zoom out",fitMap:"Back to the whole path",placeObserverHere:"Put the observer here",showReferences:"Show the pictures of the place",hideReferences:"Hide the pictures of the place",referenceOpacity:"Picture opacity",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",observerHere:"The observer, here",decorHere:"Scenery",recordingIssues:"{count} problem(s) in this recording: it may not play as its author meant",issueTemplates:po.EN},zy=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Hv},Symbol.toStringTag,{value:"Module"})),Gv=Math.PI/180,Hy=180/Math.PI,Gy=256,Vv=19,Vy=1.25;function Wv(a,e){return 156543.03392*Math.cos(a*Gv)/2**e}function Xv(a,e){return Wv(a,e)*Gy}function Wy(a,e){let t=0;for(;t<Vv&&Xv(a,t+1)>=e;)t++;return t}function Yv(a,e){return Xv(a,e)}function Xy(a,e){const t=e*Vy;let i=0;for(;i<Vv&&Wv(a,i)>t;)i++;return i}function Bc(a){const e=a*Gv;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function qv(a,e,t){const i=2**t;return{x:(a+180)/360*i,y:Bc(e)*i}}function $v(a,e,t){const i=Bc(a.north),n=Bc(a.south);return{x:(e-a.west)/(a.east-a.west),y:(Bc(t)-i)/(n-i)}}function Yy(a,e,t){const{x:i,y:n}=qv(a,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function qy(a){const e=2**a.z,t=a.x/e*360-180,i=(a.x+1)/e*360-180,n=D0(a.y,e);return{south:D0(a.y+1,e),north:n,west:t,east:i}}function D0(a,e){const t=a/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*Hy}function Kv(a,e,t,i){const n=Yy(a,e,t),s=Math.floor(i/2),r=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)r.push({x:n.x+h,y:n.y+c,z:t});const o=r.map(qy),l={south:Math.min(...o.map(c=>c.south)),north:Math.max(...o.map(c=>c.north)),west:Math.min(...o.map(c=>c.west)),east:Math.max(...o.map(c=>c.east))};return{tiles:r,bounds:l}}class Br{constructor(e){this.ctx=e}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(e,t){let i,n=1/0;for(const s of this.targets){const r=Math.hypot(s.x-e,s.y-t);r>s.radius||r>n||(i=s,n=r)}return i}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(e){const{ctx:t}=this;t.save(),this.targets=[],t.clearRect(0,0,this.width,this.height),this.paintGround(e),this.paintNight(e.nightFraction),this.paintPath(e),e.position?(this.paintCone(e,e.position),this.paintDecor(e),this.paintMarkers(e),this.paintObserver(e,e.position)):(this.paintDecor(e),this.paintMarkers(e)),this.paintNorth(),this.paintFooter(e.bounds,e.attribution),t.restore()}toCanvas(e,t,i){const n=$v(e,i,t);return{x:n.x*this.width,y:n.y*this.height}}paintGround(e){const{ctx:t}=this;t.fillStyle="#1d2321",t.fillRect(0,0,this.width,this.height);const i=e.imagery;if(!i)return;const n=this.toCanvas(e.bounds,i.bounds.north,i.bounds.west),s=this.toCanvas(e.bounds,i.bounds.south,i.bounds.east);t.drawImage(i.source,n.x,n.y,s.x-n.x,s.y-n.y)}paintNight(e=0){if(e<=0)return;const{ctx:t}=this;t.save(),t.fillStyle=`rgba(6, 12, 30, ${Math.min(e,1)*.78})`,t.fillRect(0,0,this.width,this.height),t.restore()}paintDecor(e){const{ctx:t}=this;for(const i of e.decor){const n=this.toCanvas(e.bounds,i.lat,i.lng);if(i.label&&this.targets.push({kind:"decor",label:i.label,lat:i.lat,lng:i.lng,x:n.x,y:n.y,radius:7}),t.beginPath(),t.rect(n.x-3.5,n.y-3.5,7,7),t.fillStyle="rgba(120, 220, 255, 0.85)",t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.7)",t.stroke(),i.headingDeg===void 0)continue;const s=(i.headingDeg-90)*Math.PI/180;t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(n.x+Math.cos(s)*10,n.y+Math.sin(s)*10),t.lineWidth=3,t.strokeStyle="rgba(0, 0, 0, 0.6)",t.stroke(),t.lineWidth=1.5,t.strokeStyle="rgba(120, 220, 255, 0.9)",t.stroke()}}paintPath(e){if(!e.path.moved)return;const{ctx:t}=this,i=e.path.points.map(n=>this.toCanvas(e.bounds,n.lat,n.lng));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(const n of i.slice(1))t.lineTo(n.x,n.y);t.lineJoin="round",t.lineCap="round",t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=5,t.stroke(),t.strokeStyle="rgba(255, 255, 255, 0.9)",t.lineWidth=2,t.stroke()}paintCone(e,t){const{headingDeg:i,coneHalfAngleDeg:n}=e;if(i===void 0||n===void 0)return;const{ctx:s}=this,r=this.toCanvas(e.bounds,t.lat,t.lng),o=Math.hypot(this.width,this.height),l=(i-90)*Math.PI/180,c=n*Math.PI/180;s.save(),s.beginPath(),s.moveTo(r.x,r.y),s.arc(r.x,r.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(r.x,r.y,0,r.x,r.y,o);h.addColorStop(0,"rgba(255, 224, 130, 0.55)"),h.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),h.addColorStop(1,"rgba(255, 224, 130, 0)"),s.fillStyle=h,s.fill(),s.restore()}paintMarkers(e){const{ctx:t}=this;t.textAlign="center",t.textBaseline="middle",t.font="bold 10px sans-serif";for(const i of[...e.markers].sort((n,s)=>Number(n.current)-Number(s.current))){const n=this.toCanvas(e.bounds,i.lat,i.lng);this.targets.push({kind:"milestone",label:i.note?`${i.label} — ${i.note}`:i.label,lat:i.lat,lng:i.lng,t:i.t,x:n.x,y:n.y,radius:Br.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Br.MARKER_RADIUS_PX,0,Math.PI*2),t.fillStyle=i.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",t.fill(),t.lineWidth=2,t.strokeStyle="rgba(255, 255, 255, 0.9)",t.stroke(),t.fillStyle=i.current?"#1d2321":"#fff",t.fillText(i.label,n.x,n.y)}}paintObserver(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.observerLabel&&this.targets.push({kind:"observer",label:e.observerLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(r=>{if(!r.current)return!1;const o=pi(r.lat,r.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Br.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Br.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),s||(i.fillStyle="#ff5a3c",i.fill()),i.lineWidth=4,i.strokeStyle="rgba(0, 0, 0, 0.5)",i.stroke(),i.lineWidth=s?3:2.5,i.strokeStyle=s?"#ff5a3c":"#fff",i.stroke()}paintNorth(){const{ctx:e}=this,t=this.width-14,i=16;e.beginPath(),e.moveTo(t,i-9),e.lineTo(t-4.5,i+5),e.lineTo(t,i+2),e.lineTo(t+4.5,i+5),e.closePath(),e.fillStyle="rgba(255, 255, 255, 0.92)",e.strokeStyle="rgba(0, 0, 0, 0.6)",e.lineWidth=1,e.fill(),e.stroke(),e.font="bold 9px sans-serif",e.textAlign="center",e.textBaseline="top",e.strokeText("N",t,i+6),e.fillText("N",t,i+6)}paintFooter(e,t){const{ctx:i}=this;i.font="9px sans-serif",i.textBaseline="bottom";const n=t?this.wrap(t,this.width-20):[];let s=this.height-4;i.textAlign="right";for(const p of n.slice().reverse())this.paintOutlinedText(p,this.width-6,s),s-=11;const r=(e.south+e.north)/2,l=Math.abs(pi(r,e.east,r,e.west).x)/this.width,c=Br.SCALE_STEPS_M.find(p=>p/l<=this.width/3);if(c===void 0)return;const h=c/l,d=8,u=s-3;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.6)",i.beginPath(),i.moveTo(d,u),i.lineTo(d+h,u),i.stroke(),i.lineWidth=1.5,i.strokeStyle="rgba(255, 255, 255, 0.95)",i.stroke(),i.textAlign="left",this.paintOutlinedText(c>=1e3?`${c/1e3} km`:`${c} m`,d,u-3)}paintOutlinedText(e,t,i){const{ctx:n}=this;n.lineWidth=3,n.strokeStyle="rgba(0, 0, 0, 0.75)",n.strokeText(e,t,i),n.fillStyle="#fff",n.fillText(e,t,i)}wrap(e,t){const i=[];let n="";for(const s of e.split(" ")){const r=n?`${n} ${s}`:s;n&&this.ctx.measureText(r).width>t?(i.push(n),n=s):n=r}return n&&i.push(n),i}}class Qt{static MIN_ZOOM=.25;static MAX_ZOOM=64;zoom=1;centerX=.5;centerY=.5;get changed(){return this.zoom!==1||this.centerX!==.5||this.centerY!==.5}get zoomFactor(){return this.zoom}reset(){this.zoom=1,this.centerX=.5,this.centerY=.5}zoomAt(e,t,i){const n=Math.min(Qt.MAX_ZOOM,Math.max(Qt.MIN_ZOOM,this.zoom*i)),s=this.centerX+(e-.5)/this.zoom,r=this.centerY+(t-.5)/this.zoom;this.centerX=s-(e-.5)/n,this.centerY=r-(t-.5)/n,this.zoom=n}panBy(e,t){this.centerX-=e/this.zoom,this.centerY-=t/this.zoom}refit(e,t){if(!this.changed)return;const i=this.boundsWithin(e),n=Qt.mercatorY01(t.north),s=Qt.mercatorY01(t.south),r=Qt.mercatorY01(i.north),o=Qt.mercatorY01(i.south);this.zoom=(t.east-t.west)/(i.east-i.west),this.centerX=((i.west+i.east)/2-t.west)/(t.east-t.west),this.centerY=((r+o)/2-n)/(s-n)}keepInView(e,t,i){if(!this.changed)return!1;const n=this.boundsWithin(e),s=Qt.mercatorY01(n.north),r=Qt.mercatorY01(n.south),o=(t.lng-n.west)/(n.east-n.west),l=(Qt.mercatorY01(t.lat)-s)/(r-s);return o>=i&&o<=1-i&&l>=i&&l<=1-i?!1:(this.panBy(.5-o,.5-l),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=Qt.mercatorY01(e.north),n=Qt.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:Qt.latitudeAt(i+(this.centerY-t)*(n-i)),south:Qt.latitudeAt(i+(this.centerY+t)*(n-i))}}static expand(e,t){const i=e.east-e.west,n=e.north-e.south;return{west:e.west-i*t,east:e.east+i*t,south:e.south-n*t,north:e.north+n*t}}static pointAt(e,t,i){const n=Qt.mercatorY01(e.north),s=Qt.mercatorY01(e.south);return{lat:Qt.latitudeAt(n+i*(s-n)),lng:e.west+t*(e.east-e.west)}}static mercatorY01(e){const t=e*Math.PI/180;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}static latitudeAt(e){return Math.atan(Math.sinh(Math.PI*(1-2*e)))*180/Math.PI}}const Sn=256,R0=3,$y=32768;class Zv{fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"}async getElevationGrid(e,t){const i=(e.south+e.north)/2,n=(e.west+e.east)/2,s=Math.abs(pi(i,e.east,i,e.west).x),r=Math.abs(pi(e.north,n,e.south,n).z),o=Wy(i,Math.max(s,r)),{tiles:l}=Kv(n,i,o,R0),c=R0*Sn,h=new Float32Array(c*c),d=l[0];await Promise.all(l.map(p=>this.decodeTileInto(p,d,h,c)));const u=new Float32Array(t.width*t.height);for(let p=0;p<t.height;p++){const f=e.north+(e.south-e.north)*(p/(t.height-1));for(let v=0;v<t.width;v++){const m=e.west+(e.east-e.west)*(v/(t.width-1));u[p*t.width+v]=this.sampleAt(f,m,o,d,h,c)}}return{heights:u,width:t.width,height:t.height,bounds:e}}async decodeTileInto(e,t,i,n){const s=this.tileUrlTemplate.replace("{z}",String(e.z)).replace("{x}",String(e.x)).replace("{y}",String(e.y)),r=await this.fetchImpl(s);if(!r.ok)throw new Error(`Elevation tile fetch failed (${r.status}): ${s}`);const o=await createImageBitmap(await r.blob()),l=document.createElement("canvas");l.width=Sn,l.height=Sn;const c=l.getContext("2d");if(!c)throw new Error("2D canvas context unavailable");c.drawImage(o,0,0);const{data:h}=c.getImageData(0,0,Sn,Sn),d=(e.x-t.x)*Sn,u=(e.y-t.y)*Sn;for(let p=0;p<Sn;p++)for(let f=0;f<Sn;f++){const v=(p*Sn+f)*4,m=h[v]*256+h[v+1]+h[v+2]/256-$y,g=d+f,x=u+p;i[x*n+g]=m}}sampleAt(e,t,i,n,s,r){const{x:o,y:l}=qv(t,e,i),c=(o-n.x)*Sn,h=(l-n.y)*Sn,d=Math.max(0,Math.min(r-1,Math.floor(c))),u=Math.max(0,Math.min(r-1,Math.floor(h))),p=Math.min(r-1,d+1),f=Math.min(r-1,u+1),v=c-d,m=h-u,g=s[u*r+d],x=s[u*r+p],y=s[f*r+d],b=s[f*r+p],w=g+(x-g)*v,E=y+(b-y)*v;return w+(E-w)*m}}const zc=256,Ky=5;async function jv(a,e,t,i){const n=(a.south+a.north)/2,s=(a.west+a.east)/2,r=Math.abs(pi(n,a.east,n,a.west).x),o=Math.abs(pi(a.north,s,a.south,s).z),l=Math.max(r,o),{zoom:c,gridSize:h}=Zy(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=Kv(s,n,c,h),p=h*zc,f=document.createElement("canvas");f.width=p,f.height=p;const v=f.getContext("2d");if(!v)throw new Error("2D canvas context unavailable");const m=d[0];await Promise.all(d.map(async b=>{const w=t(b),E=await i(w);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${w}`);const T=await createImageBitmap(await E.blob());v.drawImage(T,(b.x-m.x)*zc,(b.y-m.y)*zc)}));const g=Math.min(Math.max(e.width,e.height),p);if(g===p)return{source:f,width:p,height:p,bounds:u};const x=document.createElement("canvas");x.width=g,x.height=g;const y=x.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(f,0,0,p,p,0,0,g,g),{source:x,width:g,height:g,bounds:u}}function Zy(a,e,t){let i=Xy(a,e/Math.max(1,t)),n=C0(a,i,e);for(;n>Ky&&i>0;)i--,n=C0(a,i,e);return{zoom:i,gridSize:n,mPerPixel:Yv(a,i)/zc}}function C0(a,e,t){const i=Math.ceil(t/2/Yv(a,e));return Math.max(3,2*i+1)}class Jv{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(e,t){return jv(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class Nn{attribution="Roads © OpenStreetMap contributors, as surveyed today";contemporary=!0;static DRIVABLE=["motorway","trunk","primary","secondary","tertiary","unclassified","residential","service","track","motorway_link","trunk_link","primary_link","secondary_link","tertiary_link"];static WIDTH_M={motorway:11,trunk:9,primary:8,secondary:7,tertiary:6.5,unclassified:5.5,residential:5.5,service:4,track:3.5};static DEFAULT_WIDTH_M=5;static ENDPOINTS=["https://overpass-api.de/api/interpreter","https://overpass.kumi.systems/api/interpreter","https://overpass.private.coffee/api/interpreter"];fetchImpl;endpoints;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.endpoints=e.endpoints??Nn.ENDPOINTS}static queue=Promise.resolve();static RETRY_MS=4e3;async getRoads(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];way["highway"~"^(${Nn.DRIVABLE.join("|")})$"](${t});out geom;`;return this.waysFrom(await this.queued(()=>this.ask(i)))}waysFrom(e){const t=[];for(const i of e.elements??[]){const n=i.geometry,s=i.tags;!s||!n||n.length<2||t.push({id:`osm-${i.id??t.length}`,name:s.name,surface:Nn.surfaceOf(s),widthM:Nn.widthOf(s),points:n.map(r=>({lat:r.lat,lng:r.lon}))})}return t}queued(e){const t=Nn.queue.then(e,e);return Nn.queue=t.catch(()=>{}),t}async ask(e){let t;for(const i of this.endpoints)for(let n=0;n<2;n++){try{const s=await this.fetchImpl(`${i}?data=${encodeURIComponent(e)}`);if(s.ok)return await s.json();if(t=new Error(`${new URL(i).host} refused (${s.status})`),s.status!==429&&s.status!==504)break}catch(s){t=new Error(`${new URL(i).host} unreachable (${String(s)})`);break}await new Promise(s=>setTimeout(s,Nn.RETRY_MS))}throw t??new Error("No Overpass instance answered")}static widthOf(e){const t=Number.parseFloat(e.width??e.est_width??"");if(Number.isFinite(t)&&t>0)return t;const i=Number.parseInt(e.lanes??"",10);return Number.isFinite(i)&&i>0?i*3.5:Nn.WIDTH_M[e.highway]??Nn.DEFAULT_WIDTH_M}static surfaceOf(e){const t=e.surface;return t==="dirt"||t==="earth"||t==="mud"?"dirt":t==="gravel"||t==="fine_gravel"||t==="compacted"||t==="ground"||t==="sand"||t==="unpaved"?"gravel":t?"paved":e.highway==="track"?"gravel":"paved"}}const id="https://ufoathome.org/roads/index.json";class hf{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??hf.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Roads © OpenStreetMap contributors"}get contemporary(){return this.contemporaryRead??this.fallback?.contemporary??!0}async getRoads(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getRoads(e);return this.attributionRead=this.fallback.attribution,this.contemporaryRead=this.fallback.contemporary,i}async held(e){let t;try{t=await this.load()}catch{return}const i=t.find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.ways)?(this.attributionRead=s.attribution,this.contemporaryRead=s.contemporary,s.ways):void 0}catch{return}}load(){return this.index??=this.fetchFirstAvailable(),this.index}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.81.4")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==id?[t,id]:[id]).map(i=>i+e)}}function jy(){return{elevation:new Zv,imagery:Qv(),roads:Jy()}}function Qv(){return new Jv}function Jy(){return new hf({fallback:new Nn})}const Qy=new Set,ia="show-observer-map",eo="hide-milestones";class bt extends HTMLElement{static get observedAttributes(){return["src",ia,eo]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;observerMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new Qt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;timePill;muteButton;volumeInput;controlsRow;momentsBox;momentsButton;momentsCurrent;momentsMenu;moreButton;controlsRight;seekSegments;seekDot;seekPreview;seekPreviewCanvas;seekPreviewTitle;seekPreviewTime;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Lh.create();issues=[];issueCheck=0;sightingAudio=new Ny;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Hv;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.updateMuteButton(),this.resizeObserverMap()};controlsFitObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.fitControls());controlsMutations;fitControls(){const e=this.controlsRow,t=e.classList.contains("more-open"),i=()=>e.scrollWidth>e.clientWidth+1,n=!this.momentsMenu.hidden;if(this.momentsMenu.hidden=!0,e.classList.remove("narrow","more-open","nomoments","compact","tiny"),e.clientWidth>0&&i()){e.classList.add("narrow");for(const s of["nomoments","compact","tiny"]){if(!i())break;e.classList.add(s)}t&&e.classList.add("more-open")}this.momentsMenu.hidden=!n,this.updateMoreButton()}setControlsFolded(e){this.controlsRow.classList.toggle("more-open",!e),this.updateMoreButton()}updateMoreButton(){const e=this.controlsRow.classList.contains("more-open");bt.setIcon(this.moreButton,e?je.UNFOLDED:je.FOLDED);const t=e?this.messages.fewerControls:this.messages.moreControls;this.moreButton.title=t,this.moreButton.setAttribute("aria-label",t),this.moreButton.setAttribute("aria-expanded",String(e))}addControl(e){this.controlsRight.insertBefore(e,this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null),this.fitControls()}observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<bt.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=Qt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Qt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},bt.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=Qt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Py}</style>${Cy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new vl(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",t=>{t.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Br(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.timePill=this.shadow.getElementById("time"),this.muteButton=this.shadow.getElementById("mute"),this.controlsRight=this.shadow.getElementById("controls-right"),this.seekSegments=this.shadow.getElementById("seek-segments"),this.seekDot=this.shadow.getElementById("seek-dot"),this.seekPreview=this.shadow.getElementById("seek-preview"),this.seekPreviewCanvas=this.shadow.getElementById("seek-preview-canvas"),this.seekPreviewTitle=this.shadow.getElementById("seek-preview-title"),this.seekPreviewTime=this.shadow.getElementById("seek-preview-time"),this.timePill.addEventListener("click",()=>this.toggleTimeDisplay()),this.timePill.addEventListener("keydown",t=>{t.key!=="Enter"&&t.key!==" "||(t.preventDefault(),this.toggleTimeDisplay())}),this.volumeInput=this.shadow.getElementById("volume"),this.controlsRow=this.shadow.querySelector(".controls"),this.momentsBox=this.shadow.getElementById("moments"),this.momentsButton=this.shadow.getElementById("moments-button"),this.momentsCurrent=this.shadow.getElementById("moments-current"),this.momentsMenu=this.shadow.getElementById("moments-menu"),this.momentsButton.addEventListener("click",()=>this.toggleMomentsMenu()),this.moreButton=this.shadow.getElementById("controls-more"),this.moreButton.addEventListener("click",()=>this.setControlsFolded(this.controlsRow.classList.contains("more-open"))),this.muteButton.addEventListener("click",()=>this.toggleMuted()),this.volumeInput.addEventListener("input",()=>{this.setVolume(Number(this.volumeInput.value))}),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.addEventListener("keydown",this.handleFullscreenKey),this.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(t),this.beginTouchScrub(t)}),this.seekInput.addEventListener("pointerup",t=>{this.seekSnapArmed=!1,this.endTouchScrub(t)}),this.seekInput.addEventListener("pointercancel",t=>this.endTouchScrub(t)),this.seekInput.addEventListener("pointermove",t=>{this.nameMarkUnder(t),this.hoverSeek(t),this.touchScrub(t)}),this.seekInput.addEventListener("pointerleave",()=>{this.seekInput.removeAttribute("title"),this.leaveSeek()}),this.seekInput.addEventListener("keydown",this.handleSeekKey),this.stageElement.addEventListener("pointerdown",()=>{this.handlePointerDown(),this.touchedStage()},!0);for(const t of["touchend","click"])this.stageElement.addEventListener(t,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",t=>{if(this.enableClickToPlay){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),document.addEventListener("pointerdown",this.handleMomentsDismiss,!0),document.addEventListener("keydown",this.handleMomentsDismiss,!0),document.addEventListener("visibilitychange",this.handleVisibility),window.addEventListener("pagehide",this.handleHidden),document.addEventListener("pointerup",this.handlePointerRelease,!0),document.addEventListener("pointercancel",this.handlePointerRelease,!0),this.observerMapResizeObserver?.observe(this.observerMapPanel),this.controlsFitObserver?.observe(this.controlsRow),this.controlsFitObserver?.observe(this.controlsRow.querySelector(".controls-left")),typeof MutationObserver<"u"&&(this.controlsMutations=new MutationObserver(()=>this.fitControls()),this.controlsMutations.observe(this.controlsRight,{attributes:!0,attributeFilter:["hidden"],childList:!0,subtree:!0})),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),document.removeEventListener("pointerdown",this.handleMomentsDismiss,!0),document.removeEventListener("keydown",this.handleMomentsDismiss,!0),document.removeEventListener("visibilitychange",this.handleVisibility),window.removeEventListener("pagehide",this.handleHidden),document.removeEventListener("pointerup",this.handlePointerRelease,!0),document.removeEventListener("pointercancel",this.handlePointerRelease,!0),window.clearTimeout(this.controlsIdleTimer),this.observerMapResizeObserver?.disconnect(),this.controlsFitObserver?.disconnect(),this.controlsMutations?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===ia&&this.applyObserverMapDefault(),e===eo&&this.setMilestonesShown(!this.hasAttribute(eo))}async loadFromSrc(e){this.sightingData=await of.json(e)}get sightingData(){return Fy(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=Uy(e),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await gt(async()=>{const{default:r}=await import("./sightingSchema-DOOSOVWK.js");return{default:r}},[],import.meta.url);n=new On(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],po.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(e2,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=po.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e),e?this.controlsRight.appendChild(this.fullscreenButton):this.cornerButtons.appendChild(this.fullscreenButton)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referencesButton,this.referenceOpacityInput,this.observerMapButton];if(e)for(const i of t)e.appendChild(i);else{const i=this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null;for(const n of t)this.controlsRight.insertBefore(n,i)}}get referencesShown(){return this.referencesShownState}get referenceOpacity(){return Number(this.referenceOpacityInput.value)}toggleReferences(){this.referencesShownState=!this.referencesShownState,this.updateReferencesButton(),this.dispatchReferenceView()}updateReferences(){const e=this.currentSighting.references;!this.referenceOpacityTouched&&e.length>0&&(this.referenceOpacityInput.value=String(e[0].opacity)),this.updateReferencesButton()}updateReferencesButton(){const e=this.currentSighting.references.length>0;this.referencesButton.hidden=!e,this.referenceOpacityInput.hidden=!e||!this.referencesShownState,this.referencesButton.setAttribute("aria-pressed",String(this.referencesShownState)),bt.setIcon(this.referencesButton,this.referencesShownState?je.PICTURES_ON:je.PICTURES_OFF);const t=this.referencesShownState?this.messages.hideReferences:this.messages.showReferences;this.referencesButton.title=t,this.referencesButton.setAttribute("aria-label",t),this.referenceOpacityInput.title=this.messages.referenceOpacity,this.referenceOpacityInput.setAttribute("aria-label",this.messages.referenceOpacity)}dispatchReferenceView(){this.dispatchEvent(new CustomEvent("referenceview",{detail:{shown:this.referencesShown,opacity:this.referenceOpacity}}))}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?ea(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),o=r?`${s} — ${r}`:s;return n.title=o,n.setAttribute("aria-label",o),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n})),this.refreshSeekSegments(e,t.map(i=>i.t)),this.refreshMomentsPicker(t)}refreshMomentsPicker(e){const t=this.milestonesShown&&e.length>0;this.momentsBox.hidden=!t,this.closeMomentsMenu(),this.momentsMenu.replaceChildren(...e.map(i=>{const n=document.createElement("li"),s=document.createElement("button");s.type="button",s.dataset.t=String(i.t);const r=this.said.read(i.label)??"",o=this.said.read(i.note);return s.textContent=o?`${r} — ${o}`:r,s.addEventListener("click",()=>{this.player.seek(i.t),this.closeMomentsMenu()}),n.appendChild(s),n})),this.momentsButton.title=this.messages.moments,this.showCurrentMoment(void 0)}showCurrentMoment(e){const i=(e?this.said.read(e.label)??"":"")||this.messages.moments;this.momentsCurrent.textContent!==i&&(this.momentsCurrent.textContent=i);for(const n of this.momentsMenu.querySelectorAll("button")){const s=e!==void 0&&Number(n.dataset.t)===e.t;n.getAttribute("aria-current")==="true"!==s&&n.setAttribute("aria-current",String(s))}}toggleMomentsMenu(){this.momentsMenu.hidden?(this.momentsMenu.hidden=!1,this.momentsButton.setAttribute("aria-expanded","true")):this.closeMomentsMenu()}closeMomentsMenu(){this.momentsMenu.hidden=!0,this.momentsButton.setAttribute("aria-expanded","false")}handleMomentsDismiss=e=>{if(!this.momentsMenu.hidden){if(e instanceof KeyboardEvent){e.key==="Escape"&&this.closeMomentsMenu();return}e.composedPath().some(t=>t===this.momentsMenu||t===this.momentsButton)||this.closeMomentsMenu()}};seekSegmentList=[];refreshSeekSegments(e,t){const n=[0,...this.milestonesShown&&e>0?[...new Set(t.filter(r=>r>0&&r<e))].sort((r,o)=>r-o):[],Math.max(e,0)];this.seekSegmentList=[];const s=[];for(let r=0;r<n.length-1;r++){const o=document.createElement("div");o.className="seek-segment",o.style.flexGrow=String(Math.max(n[r+1]-n[r],1));const l=document.createElement("i");l.className="seek-hover";const c=document.createElement("i");c.className="seek-played",o.append(l,c),s.push(o),this.seekSegmentList.push({start:n[r],end:n[r+1],element:o,played:c,hover:l})}this.seekSegments.replaceChildren(...s),this.paintSeekProgress(this.player.time)}static fillOf(e,t){const i=e.end-e.start,n=i>0?(t-e.start)/i:t>=e.end?1:0;return`${Math.min(Math.max(n,0),1)*100}%`}paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=bt.fillOf(i,e);const t=this.player.seekableDuration;this.seekDot.style.left=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`}hoverSeek(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1),s=n*i,r=this.seekSegmentList.length-1;this.seekSegmentList.forEach((p,f)=>{p.hover.style.width=bt.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const o=this.milestonesShown?this.sighting.milestones:[],l=o.length>0?ed(o,s):void 0,c=l?this.said.read(l.label)??"":"",h=l?this.said.read(l.note):void 0,d=h?`${c} — ${h}`:c;this.seekPreviewTitle.textContent=d,this.seekPreviewTime.textContent=this.formatPosition(s),this.seekPreview.hidden=!1;const u=this.seekPreview.offsetWidth;this.seekPreview.style.left=`${Math.min(Math.max(n*t.width-u/2,0),Math.max(t.width-u,0))}px`,this.paintSeekPreview(s)}leaveSeek(){for(const e of this.seekSegmentList)e.hover.style.width="0",e.element.classList.remove("hovered");this.seekPreview.hidden=!0}seekPreviewPainter;paintSeekPreview(e){const t=this.seekPreviewPainter;this.seekPreviewCanvas.hidden=t===void 0,t?.(e,this.seekPreviewCanvas)}scrubbingPointer;beginTouchScrub(e){if(e.pointerType!=="mouse"){this.scrubbingPointer=e.pointerId;try{this.seekInput.setPointerCapture(e.pointerId)}catch{}this.seekToPointer(e)}}touchScrub(e){e.pointerId===this.scrubbingPointer&&this.seekToPointer(e)}endTouchScrub(e){e.pointerId===this.scrubbingPointer&&(this.scrubbingPointer=void 0,this.seekToPointer(e))}seekToPointer(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1);this.player.seek(this.snapSeekToMark(n*i))}seekSnapArmed=!1;static MARK_SNAP_PX=6;markNear(e){const t=this.player.seekableDuration,i=this.seekInput.getBoundingClientRect().width;if(t<=0||i<=0)return;const n=bt.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const o=Number(r.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?ed(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,this.showCurrentMoment(t),!t)return;const i=document.createElement("b");i.textContent=this.said.read(t.label)??"";const n=this.said.read(t.note),s=n?` — ${n}`:"";this.milestoneCaption.replaceChildren(i,document.createTextNode(s))}exposureInstants(e){const t=this.exposureTimes(e),i=1/t.length;return t.map(n=>({shapes:this.shapesAt(n),share:i}))}exposureTimes(e=this.currentTime){const t=Bn.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<bt.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(bt.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/bt.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/bt.EXPOSURE_STEP_PX),s=Math.min(bt.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let o=0;o<s;o++)r.push(t.fromMs+t.ms*o/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,o]of i){const l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),h=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=St.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const i=this.playbackState!=="playing"?this.highlightedSourceIds:Qy;this.canvasRenderer.setStarPoints(St.starPointsOf(this.sighting.instrument));const n=(st(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,l]of o.shapes){const c=o.share,h=c===1?l:{...l,transparency:1-(1-l.transparency)*c};this.canvasRenderer.paintShape(this.shifted(h,r))}for(const[o,l]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...l,selected:!0}):this.canvasRenderer.paintMemberOutline(l):this.canvasRenderer.paintSelectionOnly(this.shifted(l,r)));if(this.selectionShown&&i.size>1){const o=yt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds));this.canvasRenderer.paintGroupHandles(o)}this.overlayPainter?.(this.canvasRenderer),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(l=>l.transparency<1).map(l=>this.shifted(l,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.paintSeekProgress(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(af(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}shifted(e,t){return t.x===0&&t.y===0?e:{...e,bounds:{...e.bounds,x:e.bounds.x+t.x,y:e.bounds.y+t.y}}}createPlayer(){const e=new Ly(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleFullscreenKey=e=>{e.key.toLowerCase()!=="f"||e.altKey||e.ctrlKey||e.metaKey||!this.enableClickToPlay||e.composedPath().some(i=>i instanceof HTMLElement&&(i.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(i.tagName)&&i.type!=="range"))||(e.preventDefault(),this.toggleFullscreen())};handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*bt.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton(),this.flashPlayback())}flashPlayback(){const e=this.playbackFlash;e.innerHTML=this.player.playbackState==="playing"?je.PLAY:je.PAUSE,e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}static icons=new WeakMap;static setIcon(e,t){bt.icons.get(e)!==t&&(bt.icons.set(e,t),e.innerHTML=t)}set captionHeld(e){this.stageElement.classList.toggle("caption-held",e)}static CONTROLS_IDLE_MS=3e3;controlsIdleTimer;wasPlaying=!1;tapRevealedOnly=!1;get hoverless(){return typeof matchMedia=="function"&&matchMedia("(hover: none)").matches}touchedStage(){if(!this.hoverless)return;this.player.playbackState==="playing"&&!this.stageElement.classList.contains("touched")&&(this.tapRevealedOnly=!0),this.revealControls()}pointerHeld=!1;handlePointerDown=()=>{this.pointerHeld=!0};resumeAfterScrub=!1;suspendForScrub(){this.player.playbackState==="playing"&&(this.resumeAfterScrub=!0,this.stopPlaying())}stopPlaying(){this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton()}resumeFromScrub(){this.resumeAfterScrub&&(this.resumeAfterScrub=!1,this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton())}handleHidden=()=>{this.player.playbackState==="playing"&&this.stopPlaying(),this.resumeAfterScrub=!1};handleVisibility=()=>{document.visibilityState==="hidden"&&this.handleHidden()};handlePointerRelease=()=>{this.resumeAfterScrub&&window.setTimeout(()=>this.resumeFromScrub(),0),this.pointerHeld&&(this.pointerHeld=!1,this.hoverless&&this.stageElement.classList.contains("touched")&&this.revealControls())};revealControls(){this.stageElement.classList.add("touched"),window.clearTimeout(this.controlsIdleTimer),this.player.playbackState==="playing"&&(this.pointerHeld||!this.momentsMenu.hidden||(this.controlsIdleTimer=window.setTimeout(()=>{this.stageElement.classList.remove("touched"),this.tapRevealedOnly=!1},bt.CONTROLS_IDLE_MS)))}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.stageElement.classList.toggle("paused",!e),e!==this.wasPlaying&&e&&this.hoverless&&this.revealControls(),this.wasPlaying=e,bt.setIcon(this.playPauseButton,e?je.PAUSE:je.PLAY);const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const i=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=i,this.playPauseButton.setAttribute("aria-label",i)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.player.loop=this.loopEnabled}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear()}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(ia),t=e===null?this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const e=!this.observerMapPanel.hidden;bt.setIcon(this.observerMapButton,e?je.MAP_ON:je.MAP_OFF);const t=e?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=Qa.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.observerPath.boundsAround(bt.OBSERVER_MAP_MIN_SPAN_M,bt.OBSERVER_MAP_MARGIN);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,t))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,t),this.observerMapBounds=t,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||!this.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Qt.expand(e,bt.OBSERVER_MAP_IMAGERY_AHEAD):e,n=bt.OBSERVER_MAP_IMAGERY_PX*(t?1+2*bt.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Qv();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=s.attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?Qt.expand(e,bt.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=bt.OBSERVER_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",o&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=st(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},bt.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?ed(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?ea(this.currentSighting.milestones):[]){const o=st(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:o.lat,lng:o.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?Vn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(St.aspectOf(i)):void 0,markers:n,decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.momentsBox.hidden=!e||this.sighting.milestones.length===0,this.refreshSeekSegments(this.player.seekableDuration,ea(this.sighting.milestones).map(t=>t.t)),this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0,this.momentsButton.title=this.messages.moments,bt.setIcon(this.milestonesButton,this.milestonesShown?je.MILESTONES_ON:je.MILESTONES_OFF);const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=st(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=yr(i,e),{lat:s,lng:r}=Ns(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=st(this.currentSighting,this.currentTime),s=Vn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return Tt.bodyAt(this.currentSighting,e,this.steadyObserver)}mutedState=!1;get muted(){return this.mutedState}set muted(e){e!==this.mutedState&&(this.mutedState=e,!e&&this.volumeState===0&&(this.volumeState=bt.RESTORED_VOLUME),this.applyLevel())}volumeState=1;static RESTORED_VOLUME=.5;get volume(){return this.volumeState}set volume(e){this.setVolume(e)}setVolume(e){this.volumeState=Math.min(Math.max(e,0),1),this.mutedState=this.volumeState===0,this.applyLevel()}get level(){return this.mutedState?0:this.volumeState}applyLevel(){this.sightingAudio.setLevel(this.level),this.updateMuteButton(),this.dispatchEvent(new CustomEvent("mutedchange",{bubbles:!0,composed:!0,detail:{muted:this.mutedState,volume:this.volumeState}}))}toggleMuted(){this.muted=!this.mutedState}updateMuteButton(){bt.setIcon(this.muteButton,this.mutedState?je.MUTED:je.VOLUME);const e=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=e,this.muteButton.setAttribute("aria-label",e),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;bt.setIcon(this.fullscreenButton,e?je.EXIT_FULLSCREEN:je.ENTER_FULLSCREEN),this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new fs(ta.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=rh(ta.preferencesFor(this),eh);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await dy(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t,this.timePill.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(this.timePill.setAttribute("role","button"),this.timePill.setAttribute("tabindex","0")):(this.timePill.removeAttribute("role"),this.timePill.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=uo(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?_u(e.time):void 0;const i=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&i>0?i/this.realDurationMs:1,this.player.durationOverrideMs=i>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return zl(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?L0(P0(this.realStartMs+i)):zl(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?zl(this.currentSighting.timeline.duration):this.canShowClockTime?L0(P0(this.realStartMs+this.realDurationMs)):zl(this.realDurationMs)}}function P0(a){const e=new Date(a);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function L0(a){if(a.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return a.second?`${e(a.hour)}:${e(a.minute??0)}:${e(a.second)}`:`${e(a.hour)}:${e(a.minute??0)}`}function zl(a){const e=Math.round(a/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const Lu="rr0-ufo";function kh(){customElements.get(Lu)||customElements.define(Lu,bt)}const e2="recordingissues";class sl{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+sl.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-sl.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=sl.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}const t2=.08,k0=.15,O0=2,i2=2e3;class n2{context;noiseBuffer;voices=new Map;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}Qr.unlock(this.context),this.apply()}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<i2),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(e.destination);const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const o=e.createGain(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(o).connect(n);const c=()=>{const w=e.createBufferSource();return w.buffer=this.noise(e),w.loop=!0,w.loopStart=Math.random()*O0*.5,w},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const p=c(),f=e.createBiquadFilter();f.type="lowpass",f.frequency.value=600;const v=e.createGain();p.connect(f).connect(v).connect(n);const m=c(),g=e.createBiquadFilter();g.type="bandpass",g.frequency.value=1400,g.Q.value=.7;const x=e.createGain();m.connect(g).connect(x).connect(n);const y=[s,r,h,p,m];for(const w of y)w.start();const b={nodes:[i,n,o,l,d,u,f,v,g,x,...y],sources:y,firing:s,crank:r,knock:d,engineGain:o,knockGain:u,roadGain:v,windGain:x,cabin:n,output:i};return this.voices.set(t,b),b}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:o,distanceM:l}){const c=e.currentTime,h=(x,y)=>x.setTargetAtTime(y,c,t2),d=n.rpm/60,u=d*i.cylinders/2,p=Math.min(1,n.rpm/i.shiftUpRpm),f=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*p)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*f**1.5*.8);const v=!r||o;h(t.windGain.gain,f**2*(v?.5:.06));const m=r?o?3500:700:8e3/(1+l/60);h(t.cabin.frequency,m);const g=r?.5:Math.min(1,8/Math.max(l,1));h(t.output.gain,g*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,k0/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},k0*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*O0,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class bl{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return bl.BY_KIND[e??"generic"]??bl.BY_KIND.generic}}class Xr{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=Xr.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const o=[];let l=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<Xr.STANDING_MPS){l=1,o.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,p=g=>u/e.kmhPer1000Rpm[g-1]*1e3;for(;l<e.kmhPer1000Rpm.length&&p(l)>e.shiftUpRpm;)l++;for(;l>1&&p(l)<e.shiftDownRpm;)l--;const f=Math.max(p(l),e.idleRpm*(1+.3*Math.min(1,h/3))),v=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,v+d/2.5):d<-.3?.05:v;o.push({speedMps:h,rpm:f,load:m,gear:l})}return new Xr(o,i)}at(e){const t=(e-this.startMs)/Xr.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class ah{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const p=pi(d,u,n.lat,n.lng);return{eastM:p.x,northM:-p.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),o=e.vehicle;if(o){const c=bl.of(o.kind),h=this.drive(`observer:${o.kind}`,ah.trackKey(e),()=>Xr.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:o.noise??1,inside:!0,windowsOpen:o.windowsOpen===!0,distanceM:0})}const l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=bl.of(c.engine.kind),d=v=>{const m=yr(c,v);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,ah.decorKey(c,r),()=>Xr.of(h,d,0,r));if(!u)continue;const p=yr(c,t),f=Math.hypot(p.eastM-l.eastM,p.northM-l.northM,p.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:f})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const s2=`
|
|
2646
|
+
`)}`)}}class On{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!On.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:On.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const o=i?`${i}.${s}`:s,l=t[s];l?this.field(r,l,o,n):n.push({kind:"unknown-key",path:o})}}field(e,t,i,n){if(On.isStated(e)){this.field(e.value,t,i,n);return}if(!(t.loose||e===void 0||e===null))switch(t.type){case"enum":(typeof e!="string"||!t.values?.includes(e))&&n.push({kind:"not-in-list",path:i,expected:(t.values??[]).map(s=>`"${s}"`).join(", "),found:JSON.stringify(e)});return;case"object":t.fields?this.object(e,t.fields,i,n):On.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:On.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const o=On.isStated(s)?s.value:s;this.object(o,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:On.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:On.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return On.isObject(e)&&"value"in e&&Object.keys(e).every(t=>t==="value"||t==="basis"||t==="rationale")}static typeOf(e){return Array.isArray(e)?"array":e===null?"null":typeof e}}class Bn{static PIXELS_PER_INSTANT=2;static INSTANTS_PER_FLASH=2;static MAX_INSTANTS=512;static windowEndingAt(e,t){const i=Number.isFinite(t)?Math.max(0,t):0,n=Math.max(0,e-i*1e3),s=Math.max(0,e-n);return{fromMs:n,toMs:e,ms:s,seconds:s/1e3}}static instants(e,t,i,n,s){if(!(n>0))return 1;const r=s>0?Bn.travelDegOver(e,t,i,i+n*1e3)/s/Bn.PIXELS_PER_INSTANT:0,o=Bn.flashesOver(e,n)*Bn.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(r,o));return l<2?1:Math.min(Bn.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const o=Bn.directionAt(r,t,i),l=Bn.directionAt(r,t,n),c=Math.min(1,Math.max(-1,o.x*l.x+o.y*l.y+o.z*l.z));s=Math.max(s,Math.acos(c)*180/Math.PI)}return s}static flashesOver(e,t){let i=0;for(const n of e)for(const s of n.lights??[])s.pattern.kind==="flash"&&(i=Math.max(i,s.pattern.perMinute));return i/60*t}static directionAt(e,t,i){const n=yr(e,i),s=n.eastM,r=n.altitudeM-t,o=n.northM,l=Math.hypot(s,r,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:r/l,z:o/l}}}function rh(a,e){for(const t of a){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class ta{static preferencesFor(e){const t=navigator.languages??[],i=this.declaredFor(e);return i?[i,...t]:t}static declaredFor(e){for(let t=e;t;){const i=t.closest("[lang]")?.getAttribute("lang")?.trim();if(i)return i;const n=t.getRootNode();t=n instanceof ShadowRoot?n.host:void 0}}}class fs{constructor(e){this.preferences=e}read(e){if(e===void 0||typeof e=="string")return e===""?void 0:e;for(const i of this.preferences){const n=e[i]??e[fs.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>fs.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(fs.base(i)))return n;return Object.values(e).find(i=>i)??void 0}write(e,t,i){const n=t?.trim()?t.trim():void 0;if(e===void 0||typeof e=="string")return n;const s={...e},r=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>fs.base(o)===fs.base(i))??i;return n===void 0?delete s[r]:s[r]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const Hv={play:"Play",pause:"Pause",noDuration:"No observation duration",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",mute:"Mute",unmute:"Unmute",moments:"Moments",moreControls:"More controls",fewerControls:"Fewer controls",showObserverMap:"Show where the observer was",hideObserverMap:"Hide where the observer was",mapImageryUnavailable:"Aerial imagery unavailable",zoomMapIn:"Zoom in",zoomMapOut:"Zoom out",fitMap:"Back to the whole path",placeObserverHere:"Put the observer here",showReferences:"Show the pictures of the place",hideReferences:"Hide the pictures of the place",referenceOpacity:"Picture opacity",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",observerHere:"The observer, here",decorHere:"Scenery",recordingIssues:"{count} problem(s) in this recording: it may not play as its author meant",issueTemplates:po.EN},zy=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Hv},Symbol.toStringTag,{value:"Module"})),Gv=Math.PI/180,Hy=180/Math.PI,Gy=256,Vv=19,Vy=1.25;function Wv(a,e){return 156543.03392*Math.cos(a*Gv)/2**e}function Xv(a,e){return Wv(a,e)*Gy}function Wy(a,e){let t=0;for(;t<Vv&&Xv(a,t+1)>=e;)t++;return t}function Yv(a,e){return Xv(a,e)}function Xy(a,e){const t=e*Vy;let i=0;for(;i<Vv&&Wv(a,i)>t;)i++;return i}function Bc(a){const e=a*Gv;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function qv(a,e,t){const i=2**t;return{x:(a+180)/360*i,y:Bc(e)*i}}function $v(a,e,t){const i=Bc(a.north),n=Bc(a.south);return{x:(e-a.west)/(a.east-a.west),y:(Bc(t)-i)/(n-i)}}function Yy(a,e,t){const{x:i,y:n}=qv(a,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function qy(a){const e=2**a.z,t=a.x/e*360-180,i=(a.x+1)/e*360-180,n=D0(a.y,e);return{south:D0(a.y+1,e),north:n,west:t,east:i}}function D0(a,e){const t=a/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*Hy}function Kv(a,e,t,i){const n=Yy(a,e,t),s=Math.floor(i/2),r=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)r.push({x:n.x+h,y:n.y+c,z:t});const o=r.map(qy),l={south:Math.min(...o.map(c=>c.south)),north:Math.max(...o.map(c=>c.north)),west:Math.min(...o.map(c=>c.west)),east:Math.max(...o.map(c=>c.east))};return{tiles:r,bounds:l}}class Br{constructor(e){this.ctx=e}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(e,t){let i,n=1/0;for(const s of this.targets){const r=Math.hypot(s.x-e,s.y-t);r>s.radius||r>n||(i=s,n=r)}return i}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(e){const{ctx:t}=this;t.save(),this.targets=[],t.clearRect(0,0,this.width,this.height),this.paintGround(e),this.paintNight(e.nightFraction),this.paintPath(e),e.position?(this.paintCone(e,e.position),this.paintDecor(e),this.paintMarkers(e),this.paintObserver(e,e.position)):(this.paintDecor(e),this.paintMarkers(e)),this.paintNorth(),this.paintFooter(e.bounds,e.attribution),t.restore()}toCanvas(e,t,i){const n=$v(e,i,t);return{x:n.x*this.width,y:n.y*this.height}}paintGround(e){const{ctx:t}=this;t.fillStyle="#1d2321",t.fillRect(0,0,this.width,this.height);const i=e.imagery;if(!i)return;const n=this.toCanvas(e.bounds,i.bounds.north,i.bounds.west),s=this.toCanvas(e.bounds,i.bounds.south,i.bounds.east);t.drawImage(i.source,n.x,n.y,s.x-n.x,s.y-n.y)}paintNight(e=0){if(e<=0)return;const{ctx:t}=this;t.save(),t.fillStyle=`rgba(6, 12, 30, ${Math.min(e,1)*.78})`,t.fillRect(0,0,this.width,this.height),t.restore()}paintDecor(e){const{ctx:t}=this;for(const i of e.decor){const n=this.toCanvas(e.bounds,i.lat,i.lng);if(i.label&&this.targets.push({kind:"decor",label:i.label,lat:i.lat,lng:i.lng,x:n.x,y:n.y,radius:7}),t.beginPath(),t.rect(n.x-3.5,n.y-3.5,7,7),t.fillStyle="rgba(120, 220, 255, 0.85)",t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.7)",t.stroke(),i.headingDeg===void 0)continue;const s=(i.headingDeg-90)*Math.PI/180;t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(n.x+Math.cos(s)*10,n.y+Math.sin(s)*10),t.lineWidth=3,t.strokeStyle="rgba(0, 0, 0, 0.6)",t.stroke(),t.lineWidth=1.5,t.strokeStyle="rgba(120, 220, 255, 0.9)",t.stroke()}}paintPath(e){if(!e.path.moved)return;const{ctx:t}=this,i=e.path.points.map(n=>this.toCanvas(e.bounds,n.lat,n.lng));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(const n of i.slice(1))t.lineTo(n.x,n.y);t.lineJoin="round",t.lineCap="round",t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=5,t.stroke(),t.strokeStyle="rgba(255, 255, 255, 0.9)",t.lineWidth=2,t.stroke()}paintCone(e,t){const{headingDeg:i,coneHalfAngleDeg:n}=e;if(i===void 0||n===void 0)return;const{ctx:s}=this,r=this.toCanvas(e.bounds,t.lat,t.lng),o=Math.hypot(this.width,this.height),l=(i-90)*Math.PI/180,c=n*Math.PI/180;s.save(),s.beginPath(),s.moveTo(r.x,r.y),s.arc(r.x,r.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(r.x,r.y,0,r.x,r.y,o);h.addColorStop(0,"rgba(255, 224, 130, 0.55)"),h.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),h.addColorStop(1,"rgba(255, 224, 130, 0)"),s.fillStyle=h,s.fill(),s.restore()}paintMarkers(e){const{ctx:t}=this;t.textAlign="center",t.textBaseline="middle",t.font="bold 10px sans-serif";for(const i of[...e.markers].sort((n,s)=>Number(n.current)-Number(s.current))){const n=this.toCanvas(e.bounds,i.lat,i.lng);this.targets.push({kind:"milestone",label:i.note?`${i.label} — ${i.note}`:i.label,lat:i.lat,lng:i.lng,t:i.t,x:n.x,y:n.y,radius:Br.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Br.MARKER_RADIUS_PX,0,Math.PI*2),t.fillStyle=i.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",t.fill(),t.lineWidth=2,t.strokeStyle="rgba(255, 255, 255, 0.9)",t.stroke(),t.fillStyle=i.current?"#1d2321":"#fff",t.fillText(i.label,n.x,n.y)}}paintObserver(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.observerLabel&&this.targets.push({kind:"observer",label:e.observerLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(r=>{if(!r.current)return!1;const o=pi(r.lat,r.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Br.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Br.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),s||(i.fillStyle="#ff5a3c",i.fill()),i.lineWidth=4,i.strokeStyle="rgba(0, 0, 0, 0.5)",i.stroke(),i.lineWidth=s?3:2.5,i.strokeStyle=s?"#ff5a3c":"#fff",i.stroke()}paintNorth(){const{ctx:e}=this,t=this.width-14,i=16;e.beginPath(),e.moveTo(t,i-9),e.lineTo(t-4.5,i+5),e.lineTo(t,i+2),e.lineTo(t+4.5,i+5),e.closePath(),e.fillStyle="rgba(255, 255, 255, 0.92)",e.strokeStyle="rgba(0, 0, 0, 0.6)",e.lineWidth=1,e.fill(),e.stroke(),e.font="bold 9px sans-serif",e.textAlign="center",e.textBaseline="top",e.strokeText("N",t,i+6),e.fillText("N",t,i+6)}paintFooter(e,t){const{ctx:i}=this;i.font="9px sans-serif",i.textBaseline="bottom";const n=t?this.wrap(t,this.width-20):[];let s=this.height-4;i.textAlign="right";for(const p of n.slice().reverse())this.paintOutlinedText(p,this.width-6,s),s-=11;const r=(e.south+e.north)/2,l=Math.abs(pi(r,e.east,r,e.west).x)/this.width,c=Br.SCALE_STEPS_M.find(p=>p/l<=this.width/3);if(c===void 0)return;const h=c/l,d=8,u=s-3;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.6)",i.beginPath(),i.moveTo(d,u),i.lineTo(d+h,u),i.stroke(),i.lineWidth=1.5,i.strokeStyle="rgba(255, 255, 255, 0.95)",i.stroke(),i.textAlign="left",this.paintOutlinedText(c>=1e3?`${c/1e3} km`:`${c} m`,d,u-3)}paintOutlinedText(e,t,i){const{ctx:n}=this;n.lineWidth=3,n.strokeStyle="rgba(0, 0, 0, 0.75)",n.strokeText(e,t,i),n.fillStyle="#fff",n.fillText(e,t,i)}wrap(e,t){const i=[];let n="";for(const s of e.split(" ")){const r=n?`${n} ${s}`:s;n&&this.ctx.measureText(r).width>t?(i.push(n),n=s):n=r}return n&&i.push(n),i}}class Qt{static MIN_ZOOM=.25;static MAX_ZOOM=64;zoom=1;centerX=.5;centerY=.5;get changed(){return this.zoom!==1||this.centerX!==.5||this.centerY!==.5}get zoomFactor(){return this.zoom}reset(){this.zoom=1,this.centerX=.5,this.centerY=.5}zoomAt(e,t,i){const n=Math.min(Qt.MAX_ZOOM,Math.max(Qt.MIN_ZOOM,this.zoom*i)),s=this.centerX+(e-.5)/this.zoom,r=this.centerY+(t-.5)/this.zoom;this.centerX=s-(e-.5)/n,this.centerY=r-(t-.5)/n,this.zoom=n}panBy(e,t){this.centerX-=e/this.zoom,this.centerY-=t/this.zoom}refit(e,t){if(!this.changed)return;const i=this.boundsWithin(e),n=Qt.mercatorY01(t.north),s=Qt.mercatorY01(t.south),r=Qt.mercatorY01(i.north),o=Qt.mercatorY01(i.south);this.zoom=(t.east-t.west)/(i.east-i.west),this.centerX=((i.west+i.east)/2-t.west)/(t.east-t.west),this.centerY=((r+o)/2-n)/(s-n)}keepInView(e,t,i){if(!this.changed)return!1;const n=this.boundsWithin(e),s=Qt.mercatorY01(n.north),r=Qt.mercatorY01(n.south),o=(t.lng-n.west)/(n.east-n.west),l=(Qt.mercatorY01(t.lat)-s)/(r-s);return o>=i&&o<=1-i&&l>=i&&l<=1-i?!1:(this.panBy(.5-o,.5-l),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=Qt.mercatorY01(e.north),n=Qt.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:Qt.latitudeAt(i+(this.centerY-t)*(n-i)),south:Qt.latitudeAt(i+(this.centerY+t)*(n-i))}}static expand(e,t){const i=e.east-e.west,n=e.north-e.south;return{west:e.west-i*t,east:e.east+i*t,south:e.south-n*t,north:e.north+n*t}}static pointAt(e,t,i){const n=Qt.mercatorY01(e.north),s=Qt.mercatorY01(e.south);return{lat:Qt.latitudeAt(n+i*(s-n)),lng:e.west+t*(e.east-e.west)}}static mercatorY01(e){const t=e*Math.PI/180;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}static latitudeAt(e){return Math.atan(Math.sinh(Math.PI*(1-2*e)))*180/Math.PI}}const Sn=256,R0=3,$y=32768;class Zv{fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"}async getElevationGrid(e,t){const i=(e.south+e.north)/2,n=(e.west+e.east)/2,s=Math.abs(pi(i,e.east,i,e.west).x),r=Math.abs(pi(e.north,n,e.south,n).z),o=Wy(i,Math.max(s,r)),{tiles:l}=Kv(n,i,o,R0),c=R0*Sn,h=new Float32Array(c*c),d=l[0];await Promise.all(l.map(p=>this.decodeTileInto(p,d,h,c)));const u=new Float32Array(t.width*t.height);for(let p=0;p<t.height;p++){const f=e.north+(e.south-e.north)*(p/(t.height-1));for(let v=0;v<t.width;v++){const m=e.west+(e.east-e.west)*(v/(t.width-1));u[p*t.width+v]=this.sampleAt(f,m,o,d,h,c)}}return{heights:u,width:t.width,height:t.height,bounds:e}}async decodeTileInto(e,t,i,n){const s=this.tileUrlTemplate.replace("{z}",String(e.z)).replace("{x}",String(e.x)).replace("{y}",String(e.y)),r=await this.fetchImpl(s);if(!r.ok)throw new Error(`Elevation tile fetch failed (${r.status}): ${s}`);const o=await createImageBitmap(await r.blob()),l=document.createElement("canvas");l.width=Sn,l.height=Sn;const c=l.getContext("2d");if(!c)throw new Error("2D canvas context unavailable");c.drawImage(o,0,0);const{data:h}=c.getImageData(0,0,Sn,Sn),d=(e.x-t.x)*Sn,u=(e.y-t.y)*Sn;for(let p=0;p<Sn;p++)for(let f=0;f<Sn;f++){const v=(p*Sn+f)*4,m=h[v]*256+h[v+1]+h[v+2]/256-$y,g=d+f,x=u+p;i[x*n+g]=m}}sampleAt(e,t,i,n,s,r){const{x:o,y:l}=qv(t,e,i),c=(o-n.x)*Sn,h=(l-n.y)*Sn,d=Math.max(0,Math.min(r-1,Math.floor(c))),u=Math.max(0,Math.min(r-1,Math.floor(h))),p=Math.min(r-1,d+1),f=Math.min(r-1,u+1),v=c-d,m=h-u,g=s[u*r+d],x=s[u*r+p],y=s[f*r+d],b=s[f*r+p],w=g+(x-g)*v,E=y+(b-y)*v;return w+(E-w)*m}}const zc=256,Ky=5;async function jv(a,e,t,i){const n=(a.south+a.north)/2,s=(a.west+a.east)/2,r=Math.abs(pi(n,a.east,n,a.west).x),o=Math.abs(pi(a.north,s,a.south,s).z),l=Math.max(r,o),{zoom:c,gridSize:h}=Zy(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=Kv(s,n,c,h),p=h*zc,f=document.createElement("canvas");f.width=p,f.height=p;const v=f.getContext("2d");if(!v)throw new Error("2D canvas context unavailable");const m=d[0];await Promise.all(d.map(async b=>{const w=t(b),E=await i(w);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${w}`);const T=await createImageBitmap(await E.blob());v.drawImage(T,(b.x-m.x)*zc,(b.y-m.y)*zc)}));const g=Math.min(Math.max(e.width,e.height),p);if(g===p)return{source:f,width:p,height:p,bounds:u};const x=document.createElement("canvas");x.width=g,x.height=g;const y=x.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(f,0,0,p,p,0,0,g,g),{source:x,width:g,height:g,bounds:u}}function Zy(a,e,t){let i=Xy(a,e/Math.max(1,t)),n=C0(a,i,e);for(;n>Ky&&i>0;)i--,n=C0(a,i,e);return{zoom:i,gridSize:n,mPerPixel:Yv(a,i)/zc}}function C0(a,e,t){const i=Math.ceil(t/2/Yv(a,e));return Math.max(3,2*i+1)}class Jv{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(e,t){return jv(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class Nn{attribution="Roads © OpenStreetMap contributors, as surveyed today";contemporary=!0;static DRIVABLE=["motorway","trunk","primary","secondary","tertiary","unclassified","residential","service","track","motorway_link","trunk_link","primary_link","secondary_link","tertiary_link"];static WIDTH_M={motorway:11,trunk:9,primary:8,secondary:7,tertiary:6.5,unclassified:5.5,residential:5.5,service:4,track:3.5};static DEFAULT_WIDTH_M=5;static ENDPOINTS=["https://overpass-api.de/api/interpreter","https://overpass.kumi.systems/api/interpreter","https://overpass.private.coffee/api/interpreter"];fetchImpl;endpoints;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.endpoints=e.endpoints??Nn.ENDPOINTS}static queue=Promise.resolve();static RETRY_MS=4e3;async getRoads(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];way["highway"~"^(${Nn.DRIVABLE.join("|")})$"](${t});out geom;`;return this.waysFrom(await this.queued(()=>this.ask(i)))}waysFrom(e){const t=[];for(const i of e.elements??[]){const n=i.geometry,s=i.tags;!s||!n||n.length<2||t.push({id:`osm-${i.id??t.length}`,name:s.name,surface:Nn.surfaceOf(s),widthM:Nn.widthOf(s),points:n.map(r=>({lat:r.lat,lng:r.lon}))})}return t}queued(e){const t=Nn.queue.then(e,e);return Nn.queue=t.catch(()=>{}),t}async ask(e){let t;for(const i of this.endpoints)for(let n=0;n<2;n++){try{const s=await this.fetchImpl(`${i}?data=${encodeURIComponent(e)}`);if(s.ok)return await s.json();if(t=new Error(`${new URL(i).host} refused (${s.status})`),s.status!==429&&s.status!==504)break}catch(s){t=new Error(`${new URL(i).host} unreachable (${String(s)})`);break}await new Promise(s=>setTimeout(s,Nn.RETRY_MS))}throw t??new Error("No Overpass instance answered")}static widthOf(e){const t=Number.parseFloat(e.width??e.est_width??"");if(Number.isFinite(t)&&t>0)return t;const i=Number.parseInt(e.lanes??"",10);return Number.isFinite(i)&&i>0?i*3.5:Nn.WIDTH_M[e.highway]??Nn.DEFAULT_WIDTH_M}static surfaceOf(e){const t=e.surface;return t==="dirt"||t==="earth"||t==="mud"?"dirt":t==="gravel"||t==="fine_gravel"||t==="compacted"||t==="ground"||t==="sand"||t==="unpaved"?"gravel":t?"paved":e.highway==="track"?"gravel":"paved"}}const id="https://ufoathome.org/roads/index.json";class hf{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??hf.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Roads © OpenStreetMap contributors"}get contemporary(){return this.contemporaryRead??this.fallback?.contemporary??!0}async getRoads(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getRoads(e);return this.attributionRead=this.fallback.attribution,this.contemporaryRead=this.fallback.contemporary,i}async held(e){let t;try{t=await this.load()}catch{return}const i=t.find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.ways)?(this.attributionRead=s.attribution,this.contemporaryRead=s.contemporary,s.ways):void 0}catch{return}}load(){return this.index??=this.fetchFirstAvailable(),this.index}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.81.5")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==id?[t,id]:[id]).map(i=>i+e)}}function jy(){return{elevation:new Zv,imagery:Qv(),roads:Jy()}}function Qv(){return new Jv}function Jy(){return new hf({fallback:new Nn})}const Qy=new Set,ia="show-observer-map",eo="hide-milestones";class bt extends HTMLElement{static get observedAttributes(){return["src",ia,eo]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;observerMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new Qt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;timePill;muteButton;volumeInput;controlsRow;momentsBox;momentsButton;momentsCurrent;momentsMenu;moreButton;controlsRight;seekSegments;seekDot;seekPreview;seekPreviewCanvas;seekPreviewTitle;seekPreviewTime;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Lh.create();issues=[];issueCheck=0;sightingAudio=new Ny;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Hv;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.updateMuteButton(),this.resizeObserverMap()};controlsFitObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.fitControls());controlsMutations;fitControls(){const e=this.controlsRow,t=e.classList.contains("more-open"),i=()=>e.scrollWidth>e.clientWidth+1,n=!this.momentsMenu.hidden;if(this.momentsMenu.hidden=!0,e.classList.remove("narrow","more-open","nomoments","compact","tiny"),e.clientWidth>0&&i()){e.classList.add("narrow");for(const s of["nomoments","compact","tiny"]){if(!i())break;e.classList.add(s)}t&&e.classList.add("more-open")}this.momentsMenu.hidden=!n,this.updateMoreButton()}setControlsFolded(e){this.controlsRow.classList.toggle("more-open",!e),this.updateMoreButton()}updateMoreButton(){const e=this.controlsRow.classList.contains("more-open");bt.setIcon(this.moreButton,e?je.UNFOLDED:je.FOLDED);const t=e?this.messages.fewerControls:this.messages.moreControls;this.moreButton.title=t,this.moreButton.setAttribute("aria-label",t),this.moreButton.setAttribute("aria-expanded",String(e))}addControl(e){this.controlsRight.insertBefore(e,this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null),this.fitControls()}observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<bt.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=Qt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Qt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},bt.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=Qt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Py}</style>${Cy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new vl(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",t=>{t.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Br(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.timePill=this.shadow.getElementById("time"),this.muteButton=this.shadow.getElementById("mute"),this.controlsRight=this.shadow.getElementById("controls-right"),this.seekSegments=this.shadow.getElementById("seek-segments"),this.seekDot=this.shadow.getElementById("seek-dot"),this.seekPreview=this.shadow.getElementById("seek-preview"),this.seekPreviewCanvas=this.shadow.getElementById("seek-preview-canvas"),this.seekPreviewTitle=this.shadow.getElementById("seek-preview-title"),this.seekPreviewTime=this.shadow.getElementById("seek-preview-time"),this.timePill.addEventListener("click",()=>this.toggleTimeDisplay()),this.timePill.addEventListener("keydown",t=>{t.key!=="Enter"&&t.key!==" "||(t.preventDefault(),this.toggleTimeDisplay())}),this.volumeInput=this.shadow.getElementById("volume"),this.controlsRow=this.shadow.querySelector(".controls"),this.momentsBox=this.shadow.getElementById("moments"),this.momentsButton=this.shadow.getElementById("moments-button"),this.momentsCurrent=this.shadow.getElementById("moments-current"),this.momentsMenu=this.shadow.getElementById("moments-menu"),this.momentsButton.addEventListener("click",()=>this.toggleMomentsMenu()),this.moreButton=this.shadow.getElementById("controls-more"),this.moreButton.addEventListener("click",()=>this.setControlsFolded(this.controlsRow.classList.contains("more-open"))),this.muteButton.addEventListener("click",()=>this.toggleMuted()),this.volumeInput.addEventListener("input",()=>{this.setVolume(Number(this.volumeInput.value))}),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.addEventListener("keydown",this.handleFullscreenKey),this.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(t),this.beginTouchScrub(t)}),this.seekInput.addEventListener("pointerup",t=>{this.seekSnapArmed=!1,this.endTouchScrub(t)}),this.seekInput.addEventListener("pointercancel",t=>this.endTouchScrub(t)),this.seekInput.addEventListener("pointermove",t=>{this.nameMarkUnder(t),this.hoverSeek(t),this.touchScrub(t)}),this.seekInput.addEventListener("pointerleave",()=>{this.seekInput.removeAttribute("title"),this.leaveSeek()}),this.seekInput.addEventListener("keydown",this.handleSeekKey),this.stageElement.addEventListener("pointerdown",()=>{this.handlePointerDown(),this.touchedStage()},!0);for(const t of["touchend","click"])this.stageElement.addEventListener(t,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",t=>{if(this.enableClickToPlay){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),document.addEventListener("pointerdown",this.handleMomentsDismiss,!0),document.addEventListener("keydown",this.handleMomentsDismiss,!0),document.addEventListener("visibilitychange",this.handleVisibility),window.addEventListener("pagehide",this.handleHidden),document.addEventListener("pointerup",this.handlePointerRelease,!0),document.addEventListener("pointercancel",this.handlePointerRelease,!0),this.observerMapResizeObserver?.observe(this.observerMapPanel),this.controlsFitObserver?.observe(this.controlsRow),this.controlsFitObserver?.observe(this.controlsRow.querySelector(".controls-left")),typeof MutationObserver<"u"&&(this.controlsMutations=new MutationObserver(()=>this.fitControls()),this.controlsMutations.observe(this.controlsRight,{attributes:!0,attributeFilter:["hidden"],childList:!0,subtree:!0})),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),document.removeEventListener("pointerdown",this.handleMomentsDismiss,!0),document.removeEventListener("keydown",this.handleMomentsDismiss,!0),document.removeEventListener("visibilitychange",this.handleVisibility),window.removeEventListener("pagehide",this.handleHidden),document.removeEventListener("pointerup",this.handlePointerRelease,!0),document.removeEventListener("pointercancel",this.handlePointerRelease,!0),window.clearTimeout(this.controlsIdleTimer),this.observerMapResizeObserver?.disconnect(),this.controlsFitObserver?.disconnect(),this.controlsMutations?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===ia&&this.applyObserverMapDefault(),e===eo&&this.setMilestonesShown(!this.hasAttribute(eo))}async loadFromSrc(e){this.sightingData=await of.json(e)}get sightingData(){return Fy(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=Uy(e),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await gt(async()=>{const{default:r}=await import("./sightingSchema-DOOSOVWK.js");return{default:r}},[],import.meta.url);n=new On(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],po.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(e2,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=po.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e),e?this.controlsRight.appendChild(this.fullscreenButton):this.cornerButtons.appendChild(this.fullscreenButton)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referencesButton,this.referenceOpacityInput,this.observerMapButton];if(e)for(const i of t)e.appendChild(i);else{const i=this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null;for(const n of t)this.controlsRight.insertBefore(n,i)}}get referencesShown(){return this.referencesShownState}get referenceOpacity(){return Number(this.referenceOpacityInput.value)}toggleReferences(){this.referencesShownState=!this.referencesShownState,this.updateReferencesButton(),this.dispatchReferenceView()}updateReferences(){const e=this.currentSighting.references;!this.referenceOpacityTouched&&e.length>0&&(this.referenceOpacityInput.value=String(e[0].opacity)),this.updateReferencesButton()}updateReferencesButton(){const e=this.currentSighting.references.length>0;this.referencesButton.hidden=!e,this.referenceOpacityInput.hidden=!e||!this.referencesShownState,this.referencesButton.setAttribute("aria-pressed",String(this.referencesShownState)),bt.setIcon(this.referencesButton,this.referencesShownState?je.PICTURES_ON:je.PICTURES_OFF);const t=this.referencesShownState?this.messages.hideReferences:this.messages.showReferences;this.referencesButton.title=t,this.referencesButton.setAttribute("aria-label",t),this.referenceOpacityInput.title=this.messages.referenceOpacity,this.referenceOpacityInput.setAttribute("aria-label",this.messages.referenceOpacity)}dispatchReferenceView(){this.dispatchEvent(new CustomEvent("referenceview",{detail:{shown:this.referencesShown,opacity:this.referenceOpacity}}))}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?ea(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),o=r?`${s} — ${r}`:s;return n.title=o,n.setAttribute("aria-label",o),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n})),this.refreshSeekSegments(e,t.map(i=>i.t)),this.refreshMomentsPicker(t)}refreshMomentsPicker(e){const t=this.milestonesShown&&e.length>0;this.momentsBox.hidden=!t,this.closeMomentsMenu(),this.momentsMenu.replaceChildren(...e.map(i=>{const n=document.createElement("li"),s=document.createElement("button");s.type="button",s.dataset.t=String(i.t);const r=this.said.read(i.label)??"",o=this.said.read(i.note);return s.textContent=o?`${r} — ${o}`:r,s.addEventListener("click",()=>{this.player.seek(i.t),this.closeMomentsMenu()}),n.appendChild(s),n})),this.momentsButton.title=this.messages.moments,this.showCurrentMoment(void 0)}showCurrentMoment(e){const i=(e?this.said.read(e.label)??"":"")||this.messages.moments;this.momentsCurrent.textContent!==i&&(this.momentsCurrent.textContent=i);for(const n of this.momentsMenu.querySelectorAll("button")){const s=e!==void 0&&Number(n.dataset.t)===e.t;n.getAttribute("aria-current")==="true"!==s&&n.setAttribute("aria-current",String(s))}}toggleMomentsMenu(){this.momentsMenu.hidden?(this.momentsMenu.hidden=!1,this.momentsButton.setAttribute("aria-expanded","true")):this.closeMomentsMenu()}closeMomentsMenu(){this.momentsMenu.hidden=!0,this.momentsButton.setAttribute("aria-expanded","false")}handleMomentsDismiss=e=>{if(!this.momentsMenu.hidden){if(e instanceof KeyboardEvent){e.key==="Escape"&&this.closeMomentsMenu();return}e.composedPath().some(t=>t===this.momentsMenu||t===this.momentsButton)||this.closeMomentsMenu()}};seekSegmentList=[];refreshSeekSegments(e,t){const n=[0,...this.milestonesShown&&e>0?[...new Set(t.filter(r=>r>0&&r<e))].sort((r,o)=>r-o):[],Math.max(e,0)];this.seekSegmentList=[];const s=[];for(let r=0;r<n.length-1;r++){const o=document.createElement("div");o.className="seek-segment",o.style.flexGrow=String(Math.max(n[r+1]-n[r],1));const l=document.createElement("i");l.className="seek-hover";const c=document.createElement("i");c.className="seek-played",o.append(l,c),s.push(o),this.seekSegmentList.push({start:n[r],end:n[r+1],element:o,played:c,hover:l})}this.seekSegments.replaceChildren(...s),this.paintSeekProgress(this.player.time)}static fillOf(e,t){const i=e.end-e.start,n=i>0?(t-e.start)/i:t>=e.end?1:0;return`${Math.min(Math.max(n,0),1)*100}%`}paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=bt.fillOf(i,e);const t=this.player.seekableDuration;this.seekDot.style.left=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`}hoverSeek(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1),s=n*i,r=this.seekSegmentList.length-1;this.seekSegmentList.forEach((p,f)=>{p.hover.style.width=bt.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const o=this.milestonesShown?this.sighting.milestones:[],l=o.length>0?ed(o,s):void 0,c=l?this.said.read(l.label)??"":"",h=l?this.said.read(l.note):void 0,d=h?`${c} — ${h}`:c;this.seekPreviewTitle.textContent=d,this.seekPreviewTime.textContent=this.formatPosition(s),this.seekPreview.hidden=!1;const u=this.seekPreview.offsetWidth;this.seekPreview.style.left=`${Math.min(Math.max(n*t.width-u/2,0),Math.max(t.width-u,0))}px`,this.paintSeekPreview(s)}leaveSeek(){for(const e of this.seekSegmentList)e.hover.style.width="0",e.element.classList.remove("hovered");this.seekPreview.hidden=!0}seekPreviewPainter;paintSeekPreview(e){const t=this.seekPreviewPainter;this.seekPreviewCanvas.hidden=t===void 0,t?.(e,this.seekPreviewCanvas)}scrubbingPointer;beginTouchScrub(e){if(e.pointerType!=="mouse"){this.scrubbingPointer=e.pointerId;try{this.seekInput.setPointerCapture(e.pointerId)}catch{}this.seekToPointer(e)}}touchScrub(e){e.pointerId===this.scrubbingPointer&&this.seekToPointer(e)}endTouchScrub(e){e.pointerId===this.scrubbingPointer&&(this.scrubbingPointer=void 0,this.seekToPointer(e))}seekToPointer(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1);this.player.seek(this.snapSeekToMark(n*i))}seekSnapArmed=!1;static MARK_SNAP_PX=6;markNear(e){const t=this.player.seekableDuration,i=this.seekInput.getBoundingClientRect().width;if(t<=0||i<=0)return;const n=bt.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const o=Number(r.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?ed(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,this.showCurrentMoment(t),!t)return;const i=document.createElement("b");i.textContent=this.said.read(t.label)??"";const n=this.said.read(t.note),s=n?` — ${n}`:"";this.milestoneCaption.replaceChildren(i,document.createTextNode(s))}exposureInstants(e){const t=this.exposureTimes(e),i=1/t.length;return t.map(n=>({shapes:this.shapesAt(n),share:i}))}exposureTimes(e=this.currentTime){const t=Bn.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<bt.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(bt.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/bt.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/bt.EXPOSURE_STEP_PX),s=Math.min(bt.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let o=0;o<s;o++)r.push(t.fromMs+t.ms*o/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,o]of i){const l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),h=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=St.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const i=this.playbackState!=="playing"?this.highlightedSourceIds:Qy;this.canvasRenderer.setStarPoints(St.starPointsOf(this.sighting.instrument));const n=(st(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,l]of o.shapes){const c=o.share,h=c===1?l:{...l,transparency:1-(1-l.transparency)*c};this.canvasRenderer.paintShape(this.shifted(h,r))}for(const[o,l]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...l,selected:!0}):this.canvasRenderer.paintMemberOutline(l):this.canvasRenderer.paintSelectionOnly(this.shifted(l,r)));if(this.selectionShown&&i.size>1){const o=yt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds));this.canvasRenderer.paintGroupHandles(o)}this.overlayPainter?.(this.canvasRenderer),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(l=>l.transparency<1).map(l=>this.shifted(l,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.paintSeekProgress(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(af(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}shifted(e,t){return t.x===0&&t.y===0?e:{...e,bounds:{...e.bounds,x:e.bounds.x+t.x,y:e.bounds.y+t.y}}}createPlayer(){const e=new Ly(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleFullscreenKey=e=>{e.key.toLowerCase()!=="f"||e.altKey||e.ctrlKey||e.metaKey||!this.enableClickToPlay||e.composedPath().some(i=>i instanceof HTMLElement&&(i.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(i.tagName)&&i.type!=="range"))||(e.preventDefault(),this.toggleFullscreen())};handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*bt.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton(),this.flashPlayback())}flashPlayback(){const e=this.playbackFlash;e.innerHTML=this.player.playbackState==="playing"?je.PLAY:je.PAUSE,e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}static icons=new WeakMap;static setIcon(e,t){bt.icons.get(e)!==t&&(bt.icons.set(e,t),e.innerHTML=t)}set captionHeld(e){this.stageElement.classList.toggle("caption-held",e)}static CONTROLS_IDLE_MS=3e3;controlsIdleTimer;wasPlaying=!1;tapRevealedOnly=!1;get hoverless(){return typeof matchMedia=="function"&&matchMedia("(hover: none)").matches}touchedStage(){if(!this.hoverless)return;this.player.playbackState==="playing"&&!this.stageElement.classList.contains("touched")&&(this.tapRevealedOnly=!0),this.revealControls()}pointerHeld=!1;handlePointerDown=()=>{this.pointerHeld=!0};resumeAfterScrub=!1;suspendForScrub(){this.player.playbackState==="playing"&&(this.resumeAfterScrub=!0,this.stopPlaying())}stopPlaying(){this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton()}resumeFromScrub(){this.resumeAfterScrub&&(this.resumeAfterScrub=!1,this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton())}handleHidden=()=>{this.player.playbackState==="playing"&&this.stopPlaying(),this.resumeAfterScrub=!1};handleVisibility=()=>{document.visibilityState==="hidden"&&this.handleHidden()};handlePointerRelease=()=>{this.resumeAfterScrub&&window.setTimeout(()=>this.resumeFromScrub(),0),this.pointerHeld&&(this.pointerHeld=!1,this.hoverless&&this.stageElement.classList.contains("touched")&&this.revealControls())};revealControls(){this.stageElement.classList.add("touched"),window.clearTimeout(this.controlsIdleTimer),this.player.playbackState==="playing"&&(this.pointerHeld||!this.momentsMenu.hidden||(this.controlsIdleTimer=window.setTimeout(()=>{this.stageElement.classList.remove("touched"),this.tapRevealedOnly=!1},bt.CONTROLS_IDLE_MS)))}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.stageElement.classList.toggle("paused",!e),e!==this.wasPlaying&&e&&this.hoverless&&this.revealControls(),this.wasPlaying=e,bt.setIcon(this.playPauseButton,e?je.PAUSE:je.PLAY);const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const i=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=i,this.playPauseButton.setAttribute("aria-label",i)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.player.loop=this.loopEnabled}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear()}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(ia),t=e===null?this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const e=!this.observerMapPanel.hidden;bt.setIcon(this.observerMapButton,e?je.MAP_ON:je.MAP_OFF);const t=e?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=Qa.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.observerPath.boundsAround(bt.OBSERVER_MAP_MIN_SPAN_M,bt.OBSERVER_MAP_MARGIN);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,t))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,t),this.observerMapBounds=t,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||!this.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Qt.expand(e,bt.OBSERVER_MAP_IMAGERY_AHEAD):e,n=bt.OBSERVER_MAP_IMAGERY_PX*(t?1+2*bt.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Qv();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=s.attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?Qt.expand(e,bt.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=bt.OBSERVER_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",o&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=st(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},bt.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?ed(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?ea(this.currentSighting.milestones):[]){const o=st(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:o.lat,lng:o.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?Vn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(St.aspectOf(i)):void 0,markers:n,decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.momentsBox.hidden=!e||this.sighting.milestones.length===0,this.refreshSeekSegments(this.player.seekableDuration,ea(this.sighting.milestones).map(t=>t.t)),this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0,this.momentsButton.title=this.messages.moments,bt.setIcon(this.milestonesButton,this.milestonesShown?je.MILESTONES_ON:je.MILESTONES_OFF);const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=st(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=yr(i,e),{lat:s,lng:r}=Ns(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=st(this.currentSighting,this.currentTime),s=Vn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return Tt.bodyAt(this.currentSighting,e,this.steadyObserver)}mutedState=!1;get muted(){return this.mutedState}set muted(e){e!==this.mutedState&&(this.mutedState=e,!e&&this.volumeState===0&&(this.volumeState=bt.RESTORED_VOLUME),this.applyLevel())}volumeState=1;static RESTORED_VOLUME=.5;get volume(){return this.volumeState}set volume(e){this.setVolume(e)}setVolume(e){this.volumeState=Math.min(Math.max(e,0),1),this.mutedState=this.volumeState===0,this.applyLevel()}get level(){return this.mutedState?0:this.volumeState}applyLevel(){this.sightingAudio.setLevel(this.level),this.updateMuteButton(),this.dispatchEvent(new CustomEvent("mutedchange",{bubbles:!0,composed:!0,detail:{muted:this.mutedState,volume:this.volumeState}}))}toggleMuted(){this.muted=!this.mutedState}updateMuteButton(){bt.setIcon(this.muteButton,this.mutedState?je.MUTED:je.VOLUME);const e=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=e,this.muteButton.setAttribute("aria-label",e),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;bt.setIcon(this.fullscreenButton,e?je.EXIT_FULLSCREEN:je.ENTER_FULLSCREEN),this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new fs(ta.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=rh(ta.preferencesFor(this),eh);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await dy(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t,this.timePill.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(this.timePill.setAttribute("role","button"),this.timePill.setAttribute("tabindex","0")):(this.timePill.removeAttribute("role"),this.timePill.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=uo(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?_u(e.time):void 0;const i=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&i>0?i/this.realDurationMs:1,this.player.durationOverrideMs=i>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return zl(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?L0(P0(this.realStartMs+i)):zl(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?zl(this.currentSighting.timeline.duration):this.canShowClockTime?L0(P0(this.realStartMs+this.realDurationMs)):zl(this.realDurationMs)}}function P0(a){const e=new Date(a);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function L0(a){if(a.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return a.second?`${e(a.hour)}:${e(a.minute??0)}:${e(a.second)}`:`${e(a.hour)}:${e(a.minute??0)}`}function zl(a){const e=Math.round(a/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const Lu="rr0-ufo";function kh(){customElements.get(Lu)||customElements.define(Lu,bt)}const e2="recordingissues";class sl{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+sl.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-sl.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=sl.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}const t2=.08,k0=.15,O0=2,i2=2e3;class n2{context;noiseBuffer;voices=new Map;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}Qr.unlock(this.context),this.apply()}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<i2),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(e.destination);const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const o=e.createGain(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(o).connect(n);const c=()=>{const w=e.createBufferSource();return w.buffer=this.noise(e),w.loop=!0,w.loopStart=Math.random()*O0*.5,w},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const p=c(),f=e.createBiquadFilter();f.type="lowpass",f.frequency.value=600;const v=e.createGain();p.connect(f).connect(v).connect(n);const m=c(),g=e.createBiquadFilter();g.type="bandpass",g.frequency.value=1400,g.Q.value=.7;const x=e.createGain();m.connect(g).connect(x).connect(n);const y=[s,r,h,p,m];for(const w of y)w.start();const b={nodes:[i,n,o,l,d,u,f,v,g,x,...y],sources:y,firing:s,crank:r,knock:d,engineGain:o,knockGain:u,roadGain:v,windGain:x,cabin:n,output:i};return this.voices.set(t,b),b}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:o,distanceM:l}){const c=e.currentTime,h=(x,y)=>x.setTargetAtTime(y,c,t2),d=n.rpm/60,u=d*i.cylinders/2,p=Math.min(1,n.rpm/i.shiftUpRpm),f=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*p)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*f**1.5*.8);const v=!r||o;h(t.windGain.gain,f**2*(v?.5:.06));const m=r?o?3500:700:8e3/(1+l/60);h(t.cabin.frequency,m);const g=r?.5:Math.min(1,8/Math.max(l,1));h(t.output.gain,g*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,k0/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},k0*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*O0,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class bl{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return bl.BY_KIND[e??"generic"]??bl.BY_KIND.generic}}class Xr{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=Xr.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const o=[];let l=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<Xr.STANDING_MPS){l=1,o.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,p=g=>u/e.kmhPer1000Rpm[g-1]*1e3;for(;l<e.kmhPer1000Rpm.length&&p(l)>e.shiftUpRpm;)l++;for(;l>1&&p(l)<e.shiftDownRpm;)l--;const f=Math.max(p(l),e.idleRpm*(1+.3*Math.min(1,h/3))),v=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,v+d/2.5):d<-.3?.05:v;o.push({speedMps:h,rpm:f,load:m,gear:l})}return new Xr(o,i)}at(e){const t=(e-this.startMs)/Xr.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class ah{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const p=pi(d,u,n.lat,n.lng);return{eastM:p.x,northM:-p.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),o=e.vehicle;if(o){const c=bl.of(o.kind),h=this.drive(`observer:${o.kind}`,ah.trackKey(e),()=>Xr.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:o.noise??1,inside:!0,windowsOpen:o.windowsOpen===!0,distanceM:0})}const l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=bl.of(c.engine.kind),d=v=>{const m=yr(c,v);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,ah.decorKey(c,r),()=>Xr.of(h,d,0,r));if(!u)continue;const p=yr(c,t),f=Math.hypot(p.eastM-l.eastM,p.northM-l.northM,p.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:f})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const s2=`
|
|
2647
2647
|
<div class="stage" id="stage">
|
|
2648
2648
|
<div class="frame" id="frame">
|
|
2649
2649
|
<canvas id="scene-canvas"></canvas>
|
|
@@ -8763,7 +8763,7 @@ void main()
|
|
|
8763
8763
|
if (ice <= 0.0) discard;
|
|
8764
8764
|
gl_FragColor = vec4(iceHaloLight(dir, ice), 1.0);
|
|
8765
8765
|
}
|
|
8766
|
-
`,transparent:!0,blending:Yi,depthWrite:!1,side:Zt,fog:!1}),this.object=new De(new mi(Fi.RADIUS,64,32),this.material),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1;const i=this.material.uniforms;this.deckUniforms={uSource:i.uSource,uUp:i.uUp,uStrength:i.uStrength,uTint:i.uTint,uMap:i.uMap,uGain:i.uGain,uHaloShown:{value:0}}}deckUniforms;shown=!1;hostedByDeck=!1;set hosted(e){this.hostedByDeck=e,this.show(this.shown)}show(e){this.shown=e,this.object.visible=e&&!this.hostedByDeck,this.deckUniforms.uHaloShown.value=e?1:0}get awaitingFirstDisplay(){return this.tracing&&!this.everDisplayed}setUrgent(e){this.urgent=e}set onReady(e){this.onRepaint=e}update(e,t,i,n,s,r){const o=this.material.uniforms;if(i<=0){o.uStrength.value=0,this.show(!1),this.stopWork();return}o.uSource.value.set(e.x,e.y,e.z).normalize(),o.uStrength.value=i,o.uTint.value.set(n[0],n[1],n[2]),o.uIceCover.value=s.cover,o.uIceHeight.value=s.layerHeight,this.show(this.everDisplayed),this.requestMap(t,r)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Fi.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Fi.ALIGNMENT_STEP))){this.next=void 0;return}if(this.tracing){if(this.tracingFor(e,t)){this.next=void 0;return}if(!(!(Math.abs(e-this.pendingAltitudeDeg)<Fi.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Fi.LEAP_ALIGNMENT))){this.next={sourceAltitudeDeg:e,alignment:t};return}}this.next=void 0,this.pendingAltitudeDeg=e,this.pendingAlignment=t,this.sky.begin(e,t),this.tracing=!0,this.scheduleWork()}pendingAltitudeDeg=Number.NaN;pendingAlignment=Number.NaN;next;tracingFor(e,t){return this.tracing&&Math.abs(e-this.pendingAltitudeDeg)<Fi.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Fi.ALIGNMENT_STEP}scheduleWork(){if(this.workHandle!==void 0)return;const e=()=>{if(this.workHandle=void 0,!this.tracing)return;const t=performance.now(),i=this.urgent?Fi.URGENT_BUDGET_MS:Fi.BUDGET_MS;do this.sky.trace(Fi.RAYS_PER_BATCH);while(this.sky.tracedRays<Fi.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Fi.RAYS){this.publish(),this.tracing=!1,this.mappedAltitudeDeg=this.pendingAltitudeDeg,this.mappedAlignment=this.pendingAlignment;const s=this.next;s&&this.requestMap(s.sourceAltitudeDeg,s.alignment);return}this.workHandle=requestAnimationFrame(e)};this.workHandle=requestAnimationFrame(e)}stopWork(){this.workHandle!==void 0&&cancelAnimationFrame(this.workHandle),this.workHandle=void 0,this.tracing=!1,this.next=void 0}publish(){const t=this.sky.harvest().data;for(let i=0,n=0;n<t.length;i+=4,n+=3)this.texels[i]=Xn.toHalfFloat(t[n]),this.texels[i+1]=Xn.toHalfFloat(t[n+1]),this.texels[i+2]=Xn.toHalfFloat(t[n+2]),this.texels[i+3]=1;this.texture.needsUpdate=!0,this.everDisplayed=!0,this.show(this.material.uniforms.uStrength.value>0),this.onRepaint?.()}dispose(){this.stopWork(),this.texture.dispose(),this.object.geometry.dispose(),this.material.dispose()}}class Ai{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Ai.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Ai.ACROSS-1)*6;static RADIUS=840;static MIN_HALF_WIDTH_DEG=.05;static BRIGHT_HALF_WIDTH_DEG=.22;object;positions;colors;meteors=[];radiantAltitudeDeg=0;radiantAzimuthDeg=0;constructor(){const e=Ai.MAX_CONCURRENT*Ai.SEGMENTS*Ai.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new dt;t.setAttribute("position",new nt(this.positions,3)),t.setAttribute("color",new nt(this.colors,3)),t.setDrawRange(0,0),this.object=new De(t,new Rn({vertexColors:!0,transparent:!0,blending:Yi,depthWrite:!1,fog:!1,side:cn})),this.object.frustumCulled=!1}get schedule(){return this.meteors}setShower(e,t,i){this.meteors=e,this.radiantAltitudeDeg=t,this.radiantAzimuthDeg=i}update(e){const t=this.object.geometry;if(this.meteors.length===0){t.setDrawRange(0,0);return}const i=sa.aliveAt(this.meteors,e).slice(0,Ai.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:r}of i){const o=this.radiantOf(s);if(!o)continue;const l=bi(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+r*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),p=Math.min(1,(1-r)*2.5),f=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*p,m=y=>v*y**1.6,g=y=>this.onSphere(l,f,u+(d-u)*y),x=y=>Ai.RADIUS*((Ai.MIN_HALF_WIDTH_DEG+Ai.BRIGHT_HALF_WIDTH_DEG*s.brightness*y)*Math.PI/180);for(let y=1;y<Ai.POINTS;y++){const b=(y-1)/Ai.SEGMENTS,w=y/Ai.SEGMENTS;n=this.pushRibbon(n,g(b),g(w),x(b),x(w),m(b),m(w))}}t.setDrawRange(0,n),t.getAttribute("position").needsUpdate=!0,t.getAttribute("color").needsUpdate=!0}midpointOf(e){const t=this.radiantOf(e);if(!t)return;const i=bi(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),r=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,r,e.fromRadiantDeg+e.lengthDeg/2),l=Math.hypot(o.x,o.y,o.z)||1;return{altitudeDeg:Math.asin(o.y/l)*180/Math.PI,azimuthDeg:(Math.atan2(o.x,-o.z)*180/Math.PI+360)%360}}radiantOf(e){return e.radiant?e.radiant:this.radiantAltitudeDeg>0?{altitudeDeg:this.radiantAltitudeDeg,azimuthDeg:this.radiantAzimuthDeg}:void 0}pushRibbon(e,t,i,n,s,r,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Ai.ACROSS-1;c++){const h=c-1,d=c,u=this.across(t,l,n,h),p=this.across(i,l,s,h),f=this.across(t,l,n,d),v=this.across(i,l,s,d),m=(g,x)=>g===0?x:0;e=this.push(e,u,m(h,r)),e=this.push(e,p,m(h,o)),e=this.push(e,f,m(d,r)),e=this.push(e,p,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,f,m(d,r))}return e}across(e,t,i,n){return{x:e.x+t.x*i*n,y:e.y+t.y*i*n,z:e.z+t.z*i*n}}push(e,t,i){const n=e*3;this.positions[n]=t.x,this.positions[n+1]=t.y,this.positions[n+2]=t.z;const s=Ae.relativeOfResponse(Math.max(0,Math.min(1,i)));return this.colors[n]=s*.9,this.colors[n+1]=s*.95,this.colors[n+2]=s,e+1}turn(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:e.x*s+t.x*r,y:e.y*s+t.y*r,z:e.z*s+t.z*r}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:(e.x*s+t.x*r)*Ai.RADIUS,y:(e.y*s+t.y*r)*Ai.RADIUS,z:(e.z*s+t.z*r)*Ai.RADIUS}}basisAround(e){const t=Math.abs(e.y)<.9?{x:0,y:1,z:0}:{x:1,y:0,z:0},i=this.normalise(this.cross(e,t));return[i,this.normalise(this.cross(e,i))]}subtract(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}cross(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}normalise(e){const t=Math.hypot(e.x,e.y,e.z)||1;return{x:e.x/t,y:e.y/t,z:e.z/t}}dispose(){this.object.geometry.dispose(),this.object.material.dispose()}}class Bt{constructor(e,t={}){this.size=e,this.span=Math.min(e.lengthM,Math.max(0,t.spanM??e.lengthM)),this.bank=(e.lengthM-this.span)/2,this.deck=Math.min(e.heightM,t.deckThicknessM??Bt.DEFAULT_DECK_THICKNESS_M),this.railHeight=t.railing?.heightM??Bt.DEFAULT_RAILING_HEIGHT_M,this.postSpacing=Math.max(.3,t.railing?.postSpacingM??Bt.DEFAULT_POST_SPACING_M),this.rails=Math.max(0,Math.round(t.railing?.rails??Bt.DEFAULT_RAILS))}static DEFAULT_DECK_THICKNESS_M=1.2;static DEFAULT_RAILING_HEIGHT_M=1.05;static DEFAULT_POST_SPACING_M=1.5;static DEFAULT_RAILS=2;static BANK_SLOPE=1.5;static ABUTMENT_THICKNESS_M=1;static POST_SIDE_M=.08;static RAIL_HEIGHT_M=.08;static RAIL_DEPTH_M=.06;static BANK_STATIONS=12;static NATURAL_SIZE=Object.freeze({widthM:9,lengthM:40,heightM:6.5});span;bank;deck;railHeight;postSpacing;rails;roadAt(e){const t=Math.abs(e)-this.span/2;return t<=0?this.size.heightM:this.bank<=0?0:Math.max(0,this.size.heightM*(1-t/this.bank))}build(){const{widthM:e,heightM:t}=this.size,i=this.span/2,n=[];if(this.span>0){n.push(Bt.box(e,this.deck,this.span,0,t-this.deck/2,0));for(const l of[-1,1]){const c=t-this.deck;c>0&&this.bank>0&&n.push(Bt.box(e+1,c,Bt.ABUTMENT_THICKNESS_M,0,c/2,l*(i-Bt.ABUTMENT_THICKNESS_M/2)))}}const s=[];this.span>0&&s.push(Bt.box(e,.04,this.span,0,t+.02,0));const r=[];for(const l of[-1,1])this.bank<=0||(r.push(this.embankment(l)),s.push(this.bankRoad(l)));const o=[];for(const l of[-1,1])o.push(...this.railing(l*(e/2-Bt.POST_SIDE_M)));return{concrete:Bt.merge(n),road:Bt.merge(s),earth:Bt.merge(r),railing:Bt.merge(o)}}embankment(e){const t=this.size.widthM/2,i=this.stations(e),n=[],s=(c,h,d,u)=>{n.push(c.x,c.y,c.z,h.x,h.y,h.z,d.x,d.y,d.z,c.x,c.y,c.z,d.x,d.y,d.z,u.x,u.y,u.z)},r=c=>{const h=this.roadAt(c),d=t+Bt.BANK_SLOPE*h;return{leftFoot:new R(-d,0,c),leftTop:new R(-t,h,c),rightTop:new R(t,h,c),rightFoot:new R(d,0,c)}};for(let c=1;c<i.length;c++){const h=r(i[c-1]),d=r(i[c]),u=e>0,p=(f,v,m,g)=>u?s(f,v,m,g):s(g,m,v,f);p(h.leftFoot,h.leftTop,d.leftTop,d.leftFoot),p(h.leftTop,h.rightTop,d.rightTop,d.leftTop),p(h.rightTop,h.rightFoot,d.rightFoot,d.rightTop)}const o=r(i[0]);e>0?s(o.leftFoot,o.rightFoot,o.rightTop,o.leftTop):s(o.leftTop,o.rightTop,o.rightFoot,o.leftFoot);const l=new dt;return l.setAttribute("position",new vt(n,3)),l.computeVertexNormals(),l}bankRoad(e){const t=this.size.widthM/2,i=this.stations(e),n=[];for(let r=1;r<i.length;r++){const o=i[r-1],l=i[r],c=this.roadAt(o)+.02,h=this.roadAt(l)+.02,d=e>0?[[-t,c,o],[t,c,o],[t,h,l],[-t,h,l]]:[[-t,h,l],[t,h,l],[t,c,o],[-t,c,o]];for(const u of[0,2,1,0,3,2])n.push(...d[u])}const s=new dt;return s.setAttribute("position",new vt(n,3)),s.computeVertexNormals(),s}stations(e){const t=[];for(let i=0;i<=Bt.BANK_STATIONS;i++)t.push(e*(this.span/2+this.bank*i/Bt.BANK_STATIONS));return t}railing(e){const t=this.size.lengthM/2,i=Math.max(1,Math.round(this.size.lengthM/this.postSpacing)),n=[];for(let o=0;o<=i;o++)n.push(-t+this.size.lengthM*o/i);const s=[],r=Bt.POST_SIDE_M;for(const o of n)s.push(Bt.box(r,this.railHeight,r,e,this.roadAt(o)+this.railHeight/2,o));for(let o=1;o<=this.rails;o++){const l=this.railHeight*o/this.rails-Bt.RAIL_HEIGHT_M/2;for(let c=1;c<n.length;c++){const h=new R(e,this.roadAt(n[c-1])+l,n[c-1]),d=new R(e,this.roadAt(n[c])+l,n[c]);s.push(Bt.beam(h,d))}}return s}static box(e,t,i,n,s,r){return new en(e,t,i).translate(n,s,r)}static beam(e,t){const i=new R().subVectors(t,e),n=new en(Bt.RAIL_DEPTH_M,Bt.RAIL_HEIGHT_M,i.length()),s=new Xi().setFromUnitVectors(new R(0,0,1),i.clone().normalize()),r=new R().addVectors(e,t).multiplyScalar(.5);return n.applyMatrix4(new $e().compose(r,s,new R(1,1,1)))}static merge(e){const t=e.map(l=>l.getIndex()?l.toNonIndexed():l),i=t.reduce((l,c)=>l+c.getAttribute("position").count,0),n=new Float32Array(i*3),s=new Float32Array(i*3);let r=0;for(const l of t){const c=l.getAttribute("position"),h=l.getAttribute("normal");n.set(c.array,r*3),h&&s.set(h.array,r*3),r+=c.count}const o=new dt;o.setAttribute("position",new vt(n,3)),o.setAttribute("normal",new vt(s,3));for(const l of e)l.dispose();for(const l of t)l.dispose();return o}}const qd=Math.PI/180;function Gt(a,e,t,i,n=!1){const s=n?new Rn({color:new se(...t)}):new wo({color:new se(...t)}),r=new De(e,s);return r.position.y=i,r.userData={emissive:n},r.castShadow=!n,r.receiveShadow=!n,a.add(r),r}const $d=.25,sA=.002,rA=100,aA=1500,oA=.25,lA=1e3,cA=.15;function hA(a){const e=/^#([0-9a-f]{6})$/i.exec(a);if(!e)return[1,1,1];const t=parseInt(e[1],16);return[(t>>16&255)/255,(t>>8&255)/255,(t&255)/255]}const dA=[.32,.32,.3],pb=[1,.85,.5],uA=[.25,.25,.25],fb=[1,.97,.85],pA=[.55,.68,.72],cs=.03,hs=.05,mb={front:0,behind:Math.PI,left:Math.PI/2,right:-Math.PI/2,"front-left":Math.PI/2,"behind-left":Math.PI/2,"front-right":-Math.PI/2,"behind-right":-Math.PI/2};function Zi(a,e){return a?.[e]}function ds(a,e,t,i,n,s,r,o){if(o===void 0)return;const l=new wo({color:new se(...pA),transparent:!0,opacity:o/100}),c=new De(new en(e,t,i),l);c.position.set(n,s,r),a.add(c)}const Tn=.05,Kd=[.6,.58,.53],yg=[.5,.5,.48];function Ka(a,e,t,i,n,s,r,o){const l=new wo({color:new se(...o),side:Zt}),c=new De(new en(e,t,i),l);c.position.set(n,s,r),a.add(c)}function fA(a,e,t,i,n,s,r,o,l){const c=(i-s)/2,h=(f,v,m,g)=>{f<=0||(e?Ka(a,f,v,Tn,m,o+g,r,l):Ka(a,Tn,v,f,r,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((f,v)=>f.center-v.center);let u=-t/2;for(const f of d){const m=f.center-f.main/2-u;h(m,s,u+m/2,0),u=f.center+f.main/2}const p=t/2-u;h(p,s,u+p/2,0)}function gb(a,e,t,i,n,s,r,o,l,c){const h=(i+n)/2,d=n-i,u=(p,f,v,m)=>{p.length===0?f?Ka(a,v,d,Tn,0,h,m,Kd):Ka(a,Tn,d,v,m,h,0,Kd):fA(a,f,v,d,p,s,m,h,Kd)};u(r,!0,e*2,-t+Tn),u(o,!0,e*2,t-Tn),u(l,!1,t*2,-e+Tn),u(c,!1,t*2,e-Tn),Ka(a,e*2,Tn,t*2,0,i+Tn,0,yg),Ka(a,e*2,Tn,t*2,0,n-Tn,0,yg)}function jp(a,e,t,i){switch(a){case"front":return{x:0,z:-(t-i)};case"behind":return{x:0,z:t-i};case"left":return{x:-(e-i),z:0};case"right":return{x:e-i,z:0};case"front-left":return{x:-(e-i),z:-us};case"front-right":return{x:e-i,z:-us};case"behind-left":return{x:-(e-i),z:us};case"behind-right":return{x:e-i,z:us}}}function vb(a,e,t,i,n,s,r){const o=ef();o.scale.setScalar(i),o.rotation.y=mb[e];const{x:l,z:c}=jp(e,n,s,r);o.position.set(l,t,c),a.add(o)}const Jp=6,Qp=6,dr=3,Ho=1.4,Go=dr*.5,bb=1.2,yb=.35,mA=.75,gA=1.6;function Mg(a,e,t,i){const n=new Nt,s=Math.max(1,a+1),r=s*dr;Gt(n,new en(Jp,r,Qp),[.55,.54,.5],r/2);const o=Jp/2,l=Qp/2;for(let c=0;c<s;c++){const h=c*dr+dr/2;ds(n,Ho,Go,hs,0,h,-l-cs,Zi(e,"front")),ds(n,Ho,Go,hs,0,h,l+cs,Zi(e,"behind")),ds(n,hs,Go,Ho,-o-cs,h,0,Zi(e,"left")),ds(n,hs,Go,Ho,o+cs,h,0,Zi(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);vb(n,t,c*dr,.72,o,l,bb);const h=d=>Zi(e,d)!==void 0?[{main:Ho,center:0}]:[];gb(n,o,l,c*dr,(c+1)*dr,Go,h("front"),h("behind"),h("left"),h("right"))}return n}function wg(){const a=new Nt,e=[[0,0,.55,.5],[.4,.1,.35,.4],[-.35,-.2,.4,.38],[.1,-.4,.35,.36],[-.15,.4,.3,.34]];for(const[t,i,n,s]of e){const r=new mi(n,10,7);r.scale(1,.85,1),r.translate(t,0,i),Gt(a,r,[.26,.28,.17],s)}return a}function vA(a){const e=new Nt,t=new Bt(ui.sizeOf(a),a.bridge).build();Gt(e,t.concrete,[.6,.6,.58],0);const i=Gt(e,t.road,[.2,.2,.21],0),n=Gt(e,t.earth,[.3,.36,.22],0);for(const s of[i,n])s.material.side=cn;return Gt(e,t.railing,[.5,.52,.55],0),e}function xg(){const a=new Nt;return Gt(a,new Ss(.15,.22,2,8),[.32,.22,.14],1),Gt(a,new Mo(1.4,3,8),[.16,.32,.14],3.5),a}const Sg=.275,bA=.35,yA=.9,MA=34,wA=8,xA=1.4;function Eg(){const a=new Nt,e=new mi(Sg,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,bA/Sg,1);const t=ui.repeatOnGrid(e,MA,yA,wA,xA),i=Gt(a,t,[.26,.28,.22],0);return i.castShadow=!0,i.userData.cropBasePositions=t.getAttribute("position").array.slice(),i.userData.cropVerticesPerClump=e.getAttribute("position").count,e.dispose(),a}function _g(){const a=new Nt,e=new mi(1,7,3,0,Math.PI*2,0,Math.PI/2);return Gt(a,e,[.38,.36,.32],0),a}function Ag(a){const e=new Nt;return Gt(e,new Ss(.05,.08,5,8),[.28,.28,.3],2.5),Gt(e,new mi(.25,12,8),a?pb:dA,5.1,!0),e}const SA=[.08,.08,.08],ar=.8,Ha=1,ts=1.7,Zd=.6,Cr=.4,_c=1.2,Na=.7,us=.5,EA=ts;function Tg(a,e,t){const i=new Nt;Gt(i,new en(1.8,1.4,4.2),[.45,.14,.14],.7),Gt(i,new en(ar*2,Zd,Ha*2),[.4,.12,.12],ts);for(const s of[-1.4,1.4])for(const r of[-.95,.95]){const o=Gt(i,new Ss(.4,.4,.3,12),SA,.4);o.rotation.z=Math.PI/2,o.position.x=r,o.position.z=s}const n=a?fb:uA;for(const s of[-.7,.7])Gt(i,new mi(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(ds(i,_c,Cr,hs,0,ts,-Ha-cs,Zi(e,"front")),ds(i,_c,Cr,hs,0,ts,Ha+cs,Zi(e,"behind")),ds(i,hs,Cr,Na,-ar-cs,ts,-us,Zi(e,"front-left")),ds(i,hs,Cr,Na,-ar-cs,ts,us,Zi(e,"behind-left")),ds(i,hs,Cr,Na,ar+cs,ts,-us,Zi(e,"front-right")),ds(i,hs,Cr,Na,ar+cs,ts,us,Zi(e,"behind-right")),t){vb(i,t,mA,.5,ar,Ha,yb);const s=(l,c)=>{const h=[];return Zi(e,l)!==void 0&&h.push({main:Na,center:-us}),Zi(e,c)!==void 0&&h.push({main:Na,center:us}),h},r=Zi(e,"front")!==void 0?[{main:_c,center:0}]:[],o=Zi(e,"behind")!==void 0?[{main:_c,center:0}]:[];gb(i,ar,Ha,ts-Zd/2,ts+Zd/2,Cr,r,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const Ig=new Map;function Dg(a,e){if(!a)return;const t=[[a.lengthM,e.lengthM],[a.heightM,e.heightM],[a.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function jd(a,e){return e>1e-6?a/e:1}const Mb=[.95,.9,.82],Rg=[.72,.74,.78];function Cg(){const a=new Nt;Gt(a,new Ss(.25,.3,1.5,10),Rg,.75),Gt(a,new mi(.26,10,8),Rg,1.74);const e=Gt(a,new Mo(.07,.16,8),Mb,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,a}function ef(){const a=new Nt;Gt(a,new Ss(.25,.3,1.5,10),[.3,.3,.35],.75),Gt(a,new mi(.22,10,8),[.62,.52,.46],1.72);const e=Gt(a,new Mo(.07,.16,8),Mb,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,a}const _A=17,Pg=18.5,Jd=[.16,.16,.18];function Lg(){const a=new Nt,e=Gt(a,new Ss(1.8,1.8,Pg*2,10),Jd,0);e.rotation.x=Math.PI/2,Gt(a,new en(_A*2,.5,4.5),Jd,0);const t=Gt(a,new en(.4,5.5,4),Jd,3);return t.position.z=-Pg+2,a}class ui{static GROWS_FROM_GROUND=new Set(["tree","shrub","crop","mound"]);static groundUnderFootprint(e,t,i,n,s,r=0){const o=ui.sizeOf(e);if(r>o.heightM)return s(t,i);const l=(n??0)*Math.PI/180,c=Math.min(21,Math.max(2,Math.ceil(o.lengthM/.75)+1)),h=Math.min(21,Math.max(2,Math.ceil(o.widthM/.75)+1)),d=ui.GROWS_FROM_GROUND.has(e.kind);let u=d?1/0:-1/0;for(let p=0;p<c;p++){const f=(p/(c-1)-.5)*o.lengthM;for(let v=0;v<h;v++){const m=(v/(h-1)-.5)*o.widthM,g=t+Math.sin(l)*f+Math.cos(l)*m,x=i-Math.cos(l)*f+Math.sin(l)*m;u=d?Math.min(u,s(g,x)):Math.max(u,s(g,x))}}return Number.isFinite(u)?u:s(t,i)}static occupantView(e){const t=e.observerSide;if(!t)throw new Error("occupantView requires object.observerSide to be set");const{x:i,z:n}=e.kind==="vehicle"?jp(t,ar,Ha,yb):jp(t,Jp/2,Qp/2,bb),s=e.kind==="vehicle"?EA:(e.occupiedFloor??0)*dr+gA,r=(e.headingDeg??0)-mb[t]/qd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:r}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ui.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const r=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=r.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),p=new Uint32Array(l.count*h),f=-((t-1)*i)/2,v=-((n-1)*s)/2;let m=0;for(let x=0;x<n;x++)for(let y=0;y<t;y++,m++){const b=v+x*s,w=f+y*i;for(let E=0;E<c;E++){const T=(m*c+E)*3;d[T]=r.getX(E)+b,d[T+1]=r.getY(E),d[T+2]=r.getZ(E)+w,u[T]=o.getX(E),u[T+1]=o.getY(E),u[T+2]=o.getZ(E)}for(let E=0;E<l.count;E++)p[m*l.count+E]=l.getX(E)+m*c}const g=new dt;return g.setAttribute("position",new vt(d,3)),g.setAttribute("normal",new vt(u,3)),g.setIndex(new Af(p,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,r=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!r||!o)return;const l=s.geometry.getAttribute("position"),c=new $e().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(r[h*3],0,r[h*3+2]).applyMatrix4(s.matrixWorld),i.y=t(i.x,i.z),i.applyMatrix4(c);for(let d=h;d<h+o;d++)l.setY(d,r[d*3+1]+i.y)}l.needsUpdate=!0,s.geometry.computeBoundingBox(),s.geometry.computeBoundingSphere()})}static paint(e,t){e.traverse(i=>{const n=i,s=n.userData;if(!n.isMesh||s?.emissive)return;const r=n.material;r?.color&&r.color.set(t)})}static build(e,t){const i=e.kind==="building"?Mg(e.floors??Ja,e.windows,e.observerSide,e.occupiedFloor):e.kind==="tree"?xg():e.kind==="shrub"?wg():e.kind==="bridge"?vA(e):e.kind==="crop"?Eg():e.kind==="mound"?_g():e.kind==="streetlight"?Ag(t):e.kind==="vehicle"?Tg(t,e.windows,e.observerSide):e.kind==="aircraft"?Lg():e.kind==="entity"?Cg():ef();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Nt;return i.name=ui.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*qd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t||e.kind==="bridge")return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:jd(t.widthM,i.widthM),t.heightM===void 0?1:jd(t.heightM,i.heightM),t.lengthM===void 0?1:jd(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Ja)+1):0;if(e==="bridge")return Bt.NATURAL_SIZE;const n=`${e}:${i}`,s=Ig.get(n);if(s)return s;const r=e==="building"?Mg(i-1,void 0,void 0,void 0):e==="tree"?xg():e==="shrub"?wg():e==="crop"?Eg():e==="mound"?_g():e==="streetlight"?Ag(!1):e==="vehicle"?Tg(!1,void 0,void 0):e==="aircraft"?Lg():e==="entity"?Cg():ef(),o=new Qi().setFromObject(r),l={widthM:o.max.x-o.min.x,heightM:o.max.y-o.min.y,lengthM:o.max.z-o.min.z};return this.dispose(r),Ig.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.observerSide!==void 0&&Va(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Nt;r.name=ui.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*qd,i.updateMatrixWorld(!0);const o=new Qi().setFromObject(i),l={lengthM:o.max.z-o.min.z,heightM:o.max.y-o.min.y,widthM:o.max.x-o.min.x},c=Dg(t.sizeM,l)??Dg(n.depictedSizeM,l)??1;i.scale.multiplyScalar(c);const h=o.getCenter(new R).multiplyScalar(c);i.position.x-=h.x,i.position.z-=h.z,i.position.y-=t.kind==="aircraft"?h.y:o.min.y*c,i.traverse(d=>{d instanceof De&&(d.castShadow=!0,d.receiveShadow=!0)}),r.add(i),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s)}static sizeOf(e){const t=this.naturalSize(e.kind,e.floors);return{widthM:e.sizeM?.widthM??t.widthM,lengthM:e.sizeM?.lengthM??t.lengthM,heightM:e.sizeM?.heightM??t.heightM}}static addLights(e,t){for(const i of t??[]){const n=hA(i.color),s=Gt(e,new mi($d,8,6),n,i.offsetM.y,!0);s.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),s.userData={emissive:!0,lightId:i.id,lightColor:n},s.visible=!1}}static setLights(e,t,i,n,s=0,r=1,o=sA){if(!t||t.length===0)return;const l=new Map(t.map(u=>[u.id,u])),c=Math.max(1,n*o/$d),h=$d*c,d=r/(Math.PI*h*h);for(const u of e.children){if(!(u instanceof De))continue;const p=u.userData.lightId;if(!p)continue;const f=l.get(p);if(!f)continue;const v=s>0?zv(f,i,i+s):Ay(f,i)?1:0;u.visible=v>0;const m=u.material,g=u.userData.lightColor;if(g){const x=v*rA*(f.intensity??1)*d;m.color.setRGB(g[0]*x,g[1]*x,g[2]*x)}u.scale.setScalar(c)}}static setLit(e,t,i,n=1){const[s,r,o]=t==="streetlight"?[aA,oA,pb]:[lA,cA,fb],l=i?s*n/(Math.PI*r*r):0,c=[o[0]*l,o[1]*l,o[2]*l];e.traverse(h=>{const d=h.userData;!(h instanceof De)||!d.emissive||d.lightId||h.material.color.setRGB(...c)})}static dispose(e){e.traverse(t=>{if(!(t instanceof De))return;t.geometry.dispose();const i=t.material;for(const n of Array.isArray(i)?i:[i])n.dispose()})}}const Qd="https://ufoathome.org/models/index.json";class AA{attribution="3D models re-hosted by UFO@home, each under its own author's licence";fetchImpl;indexUrls;loading;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??TA()}async entries(e){const t=await this.load();return e?t.filter(i=>i.kind===e):t}async entry(e){return(await this.load()).find(t=>t.id===e)}load(){return this.loading??=this.fetchFirstAvailable(),this.loading}async fetchFirstAvailable(){for(const e of this.indexUrls){const t=await this.fetchIndex(e);if(t)return t}return[]}async fetchIndex(e){try{const t=await this.fetchImpl(e);if(!t.ok)return;const i=await t.json();return Array.isArray(i?.models)?i.models.map(({file:n,...s})=>({...s,url:new URL(n,e).href})):void 0}catch{return}}}function TA(){const a=`?v=${encodeURIComponent("0.81.4")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==Qd?[e,Qd]:[Qd]).map(t=>t+a)}const Kc=[{id:"ufoathome",name:"UFO@home",credit:"3D models re-hosted by UFO@home, each under its own author's licence",creditUrl:"https://ufoathome.org/models/",create:()=>new AA}];async function kg(a){const{GLTFLoader:e}=await gt(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-DSbhUTN3.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(a,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${a}: ${s instanceof Error?s.message:String(s)}`)))})}const wb=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class yn{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new De(new tn(2,2),new ut({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:yn.VERTEX,fragmentShader:yn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Yi,toneMapped:!1})),this.mesh.name=e,this.mesh.frustumCulled=!1,this.mesh.renderOrder=t,this.mesh.visible=!1}shine(e,t,i){this.mesh.visible=!0,this.mesh.position.set(e.x,e.y,e.z),this.mesh.material.uniforms.uRadius.value=t,this.mesh.material.uniforms.uColor.value.setRGB(i[0],i[1],i[2])}hide(){this.mesh.visible=!1}dispose(){this.mesh.geometry.dispose(),this.mesh.material.dispose()}static spread(e,t,i,n){const s=Math.max(e,n*yn.MIN_ANGLE_RAD),r=yn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(yn.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(yn.MAX_SHARE*yn.FILL*Math.PI))}static VERTEX=`
|
|
8766
|
+
`,transparent:!0,blending:Yi,depthWrite:!1,side:Zt,fog:!1}),this.object=new De(new mi(Fi.RADIUS,64,32),this.material),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1;const i=this.material.uniforms;this.deckUniforms={uSource:i.uSource,uUp:i.uUp,uStrength:i.uStrength,uTint:i.uTint,uMap:i.uMap,uGain:i.uGain,uHaloShown:{value:0}}}deckUniforms;shown=!1;hostedByDeck=!1;set hosted(e){this.hostedByDeck=e,this.show(this.shown)}show(e){this.shown=e,this.object.visible=e&&!this.hostedByDeck,this.deckUniforms.uHaloShown.value=e?1:0}get awaitingFirstDisplay(){return this.tracing&&!this.everDisplayed}setUrgent(e){this.urgent=e}set onReady(e){this.onRepaint=e}update(e,t,i,n,s,r){const o=this.material.uniforms;if(i<=0){o.uStrength.value=0,this.show(!1),this.stopWork();return}o.uSource.value.set(e.x,e.y,e.z).normalize(),o.uStrength.value=i,o.uTint.value.set(n[0],n[1],n[2]),o.uIceCover.value=s.cover,o.uIceHeight.value=s.layerHeight,this.show(this.everDisplayed),this.requestMap(t,r)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Fi.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Fi.ALIGNMENT_STEP))){this.next=void 0;return}if(this.tracing){if(this.tracingFor(e,t)){this.next=void 0;return}if(!(!(Math.abs(e-this.pendingAltitudeDeg)<Fi.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Fi.LEAP_ALIGNMENT))){this.next={sourceAltitudeDeg:e,alignment:t};return}}this.next=void 0,this.pendingAltitudeDeg=e,this.pendingAlignment=t,this.sky.begin(e,t),this.tracing=!0,this.scheduleWork()}pendingAltitudeDeg=Number.NaN;pendingAlignment=Number.NaN;next;tracingFor(e,t){return this.tracing&&Math.abs(e-this.pendingAltitudeDeg)<Fi.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Fi.ALIGNMENT_STEP}scheduleWork(){if(this.workHandle!==void 0)return;const e=()=>{if(this.workHandle=void 0,!this.tracing)return;const t=performance.now(),i=this.urgent?Fi.URGENT_BUDGET_MS:Fi.BUDGET_MS;do this.sky.trace(Fi.RAYS_PER_BATCH);while(this.sky.tracedRays<Fi.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Fi.RAYS){this.publish(),this.tracing=!1,this.mappedAltitudeDeg=this.pendingAltitudeDeg,this.mappedAlignment=this.pendingAlignment;const s=this.next;s&&this.requestMap(s.sourceAltitudeDeg,s.alignment);return}this.workHandle=requestAnimationFrame(e)};this.workHandle=requestAnimationFrame(e)}stopWork(){this.workHandle!==void 0&&cancelAnimationFrame(this.workHandle),this.workHandle=void 0,this.tracing=!1,this.next=void 0}publish(){const t=this.sky.harvest().data;for(let i=0,n=0;n<t.length;i+=4,n+=3)this.texels[i]=Xn.toHalfFloat(t[n]),this.texels[i+1]=Xn.toHalfFloat(t[n+1]),this.texels[i+2]=Xn.toHalfFloat(t[n+2]),this.texels[i+3]=1;this.texture.needsUpdate=!0,this.everDisplayed=!0,this.show(this.material.uniforms.uStrength.value>0),this.onRepaint?.()}dispose(){this.stopWork(),this.texture.dispose(),this.object.geometry.dispose(),this.material.dispose()}}class Ai{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Ai.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Ai.ACROSS-1)*6;static RADIUS=840;static MIN_HALF_WIDTH_DEG=.05;static BRIGHT_HALF_WIDTH_DEG=.22;object;positions;colors;meteors=[];radiantAltitudeDeg=0;radiantAzimuthDeg=0;constructor(){const e=Ai.MAX_CONCURRENT*Ai.SEGMENTS*Ai.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new dt;t.setAttribute("position",new nt(this.positions,3)),t.setAttribute("color",new nt(this.colors,3)),t.setDrawRange(0,0),this.object=new De(t,new Rn({vertexColors:!0,transparent:!0,blending:Yi,depthWrite:!1,fog:!1,side:cn})),this.object.frustumCulled=!1}get schedule(){return this.meteors}setShower(e,t,i){this.meteors=e,this.radiantAltitudeDeg=t,this.radiantAzimuthDeg=i}update(e){const t=this.object.geometry;if(this.meteors.length===0){t.setDrawRange(0,0);return}const i=sa.aliveAt(this.meteors,e).slice(0,Ai.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:r}of i){const o=this.radiantOf(s);if(!o)continue;const l=bi(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+r*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),p=Math.min(1,(1-r)*2.5),f=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*p,m=y=>v*y**1.6,g=y=>this.onSphere(l,f,u+(d-u)*y),x=y=>Ai.RADIUS*((Ai.MIN_HALF_WIDTH_DEG+Ai.BRIGHT_HALF_WIDTH_DEG*s.brightness*y)*Math.PI/180);for(let y=1;y<Ai.POINTS;y++){const b=(y-1)/Ai.SEGMENTS,w=y/Ai.SEGMENTS;n=this.pushRibbon(n,g(b),g(w),x(b),x(w),m(b),m(w))}}t.setDrawRange(0,n),t.getAttribute("position").needsUpdate=!0,t.getAttribute("color").needsUpdate=!0}midpointOf(e){const t=this.radiantOf(e);if(!t)return;const i=bi(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),r=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,r,e.fromRadiantDeg+e.lengthDeg/2),l=Math.hypot(o.x,o.y,o.z)||1;return{altitudeDeg:Math.asin(o.y/l)*180/Math.PI,azimuthDeg:(Math.atan2(o.x,-o.z)*180/Math.PI+360)%360}}radiantOf(e){return e.radiant?e.radiant:this.radiantAltitudeDeg>0?{altitudeDeg:this.radiantAltitudeDeg,azimuthDeg:this.radiantAzimuthDeg}:void 0}pushRibbon(e,t,i,n,s,r,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Ai.ACROSS-1;c++){const h=c-1,d=c,u=this.across(t,l,n,h),p=this.across(i,l,s,h),f=this.across(t,l,n,d),v=this.across(i,l,s,d),m=(g,x)=>g===0?x:0;e=this.push(e,u,m(h,r)),e=this.push(e,p,m(h,o)),e=this.push(e,f,m(d,r)),e=this.push(e,p,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,f,m(d,r))}return e}across(e,t,i,n){return{x:e.x+t.x*i*n,y:e.y+t.y*i*n,z:e.z+t.z*i*n}}push(e,t,i){const n=e*3;this.positions[n]=t.x,this.positions[n+1]=t.y,this.positions[n+2]=t.z;const s=Ae.relativeOfResponse(Math.max(0,Math.min(1,i)));return this.colors[n]=s*.9,this.colors[n+1]=s*.95,this.colors[n+2]=s,e+1}turn(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:e.x*s+t.x*r,y:e.y*s+t.y*r,z:e.z*s+t.z*r}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:(e.x*s+t.x*r)*Ai.RADIUS,y:(e.y*s+t.y*r)*Ai.RADIUS,z:(e.z*s+t.z*r)*Ai.RADIUS}}basisAround(e){const t=Math.abs(e.y)<.9?{x:0,y:1,z:0}:{x:1,y:0,z:0},i=this.normalise(this.cross(e,t));return[i,this.normalise(this.cross(e,i))]}subtract(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}cross(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}normalise(e){const t=Math.hypot(e.x,e.y,e.z)||1;return{x:e.x/t,y:e.y/t,z:e.z/t}}dispose(){this.object.geometry.dispose(),this.object.material.dispose()}}class Bt{constructor(e,t={}){this.size=e,this.span=Math.min(e.lengthM,Math.max(0,t.spanM??e.lengthM)),this.bank=(e.lengthM-this.span)/2,this.deck=Math.min(e.heightM,t.deckThicknessM??Bt.DEFAULT_DECK_THICKNESS_M),this.railHeight=t.railing?.heightM??Bt.DEFAULT_RAILING_HEIGHT_M,this.postSpacing=Math.max(.3,t.railing?.postSpacingM??Bt.DEFAULT_POST_SPACING_M),this.rails=Math.max(0,Math.round(t.railing?.rails??Bt.DEFAULT_RAILS))}static DEFAULT_DECK_THICKNESS_M=1.2;static DEFAULT_RAILING_HEIGHT_M=1.05;static DEFAULT_POST_SPACING_M=1.5;static DEFAULT_RAILS=2;static BANK_SLOPE=1.5;static ABUTMENT_THICKNESS_M=1;static POST_SIDE_M=.08;static RAIL_HEIGHT_M=.08;static RAIL_DEPTH_M=.06;static BANK_STATIONS=12;static NATURAL_SIZE=Object.freeze({widthM:9,lengthM:40,heightM:6.5});span;bank;deck;railHeight;postSpacing;rails;roadAt(e){const t=Math.abs(e)-this.span/2;return t<=0?this.size.heightM:this.bank<=0?0:Math.max(0,this.size.heightM*(1-t/this.bank))}build(){const{widthM:e,heightM:t}=this.size,i=this.span/2,n=[];if(this.span>0){n.push(Bt.box(e,this.deck,this.span,0,t-this.deck/2,0));for(const l of[-1,1]){const c=t-this.deck;c>0&&this.bank>0&&n.push(Bt.box(e+1,c,Bt.ABUTMENT_THICKNESS_M,0,c/2,l*(i-Bt.ABUTMENT_THICKNESS_M/2)))}}const s=[];this.span>0&&s.push(Bt.box(e,.04,this.span,0,t+.02,0));const r=[];for(const l of[-1,1])this.bank<=0||(r.push(this.embankment(l)),s.push(this.bankRoad(l)));const o=[];for(const l of[-1,1])o.push(...this.railing(l*(e/2-Bt.POST_SIDE_M)));return{concrete:Bt.merge(n),road:Bt.merge(s),earth:Bt.merge(r),railing:Bt.merge(o)}}embankment(e){const t=this.size.widthM/2,i=this.stations(e),n=[],s=(c,h,d,u)=>{n.push(c.x,c.y,c.z,h.x,h.y,h.z,d.x,d.y,d.z,c.x,c.y,c.z,d.x,d.y,d.z,u.x,u.y,u.z)},r=c=>{const h=this.roadAt(c),d=t+Bt.BANK_SLOPE*h;return{leftFoot:new R(-d,0,c),leftTop:new R(-t,h,c),rightTop:new R(t,h,c),rightFoot:new R(d,0,c)}};for(let c=1;c<i.length;c++){const h=r(i[c-1]),d=r(i[c]),u=e>0,p=(f,v,m,g)=>u?s(f,v,m,g):s(g,m,v,f);p(h.leftFoot,h.leftTop,d.leftTop,d.leftFoot),p(h.leftTop,h.rightTop,d.rightTop,d.leftTop),p(h.rightTop,h.rightFoot,d.rightFoot,d.rightTop)}const o=r(i[0]);e>0?s(o.leftFoot,o.rightFoot,o.rightTop,o.leftTop):s(o.leftTop,o.rightTop,o.rightFoot,o.leftFoot);const l=new dt;return l.setAttribute("position",new vt(n,3)),l.computeVertexNormals(),l}bankRoad(e){const t=this.size.widthM/2,i=this.stations(e),n=[];for(let r=1;r<i.length;r++){const o=i[r-1],l=i[r],c=this.roadAt(o)+.02,h=this.roadAt(l)+.02,d=e>0?[[-t,c,o],[t,c,o],[t,h,l],[-t,h,l]]:[[-t,h,l],[t,h,l],[t,c,o],[-t,c,o]];for(const u of[0,2,1,0,3,2])n.push(...d[u])}const s=new dt;return s.setAttribute("position",new vt(n,3)),s.computeVertexNormals(),s}stations(e){const t=[];for(let i=0;i<=Bt.BANK_STATIONS;i++)t.push(e*(this.span/2+this.bank*i/Bt.BANK_STATIONS));return t}railing(e){const t=this.size.lengthM/2,i=Math.max(1,Math.round(this.size.lengthM/this.postSpacing)),n=[];for(let o=0;o<=i;o++)n.push(-t+this.size.lengthM*o/i);const s=[],r=Bt.POST_SIDE_M;for(const o of n)s.push(Bt.box(r,this.railHeight,r,e,this.roadAt(o)+this.railHeight/2,o));for(let o=1;o<=this.rails;o++){const l=this.railHeight*o/this.rails-Bt.RAIL_HEIGHT_M/2;for(let c=1;c<n.length;c++){const h=new R(e,this.roadAt(n[c-1])+l,n[c-1]),d=new R(e,this.roadAt(n[c])+l,n[c]);s.push(Bt.beam(h,d))}}return s}static box(e,t,i,n,s,r){return new en(e,t,i).translate(n,s,r)}static beam(e,t){const i=new R().subVectors(t,e),n=new en(Bt.RAIL_DEPTH_M,Bt.RAIL_HEIGHT_M,i.length()),s=new Xi().setFromUnitVectors(new R(0,0,1),i.clone().normalize()),r=new R().addVectors(e,t).multiplyScalar(.5);return n.applyMatrix4(new $e().compose(r,s,new R(1,1,1)))}static merge(e){const t=e.map(l=>l.getIndex()?l.toNonIndexed():l),i=t.reduce((l,c)=>l+c.getAttribute("position").count,0),n=new Float32Array(i*3),s=new Float32Array(i*3);let r=0;for(const l of t){const c=l.getAttribute("position"),h=l.getAttribute("normal");n.set(c.array,r*3),h&&s.set(h.array,r*3),r+=c.count}const o=new dt;o.setAttribute("position",new vt(n,3)),o.setAttribute("normal",new vt(s,3));for(const l of e)l.dispose();for(const l of t)l.dispose();return o}}const qd=Math.PI/180;function Gt(a,e,t,i,n=!1){const s=n?new Rn({color:new se(...t)}):new wo({color:new se(...t)}),r=new De(e,s);return r.position.y=i,r.userData={emissive:n},r.castShadow=!n,r.receiveShadow=!n,a.add(r),r}const $d=.25,sA=.002,rA=100,aA=1500,oA=.25,lA=1e3,cA=.15;function hA(a){const e=/^#([0-9a-f]{6})$/i.exec(a);if(!e)return[1,1,1];const t=parseInt(e[1],16);return[(t>>16&255)/255,(t>>8&255)/255,(t&255)/255]}const dA=[.32,.32,.3],pb=[1,.85,.5],uA=[.25,.25,.25],fb=[1,.97,.85],pA=[.55,.68,.72],cs=.03,hs=.05,mb={front:0,behind:Math.PI,left:Math.PI/2,right:-Math.PI/2,"front-left":Math.PI/2,"behind-left":Math.PI/2,"front-right":-Math.PI/2,"behind-right":-Math.PI/2};function Zi(a,e){return a?.[e]}function ds(a,e,t,i,n,s,r,o){if(o===void 0)return;const l=new wo({color:new se(...pA),transparent:!0,opacity:o/100}),c=new De(new en(e,t,i),l);c.position.set(n,s,r),a.add(c)}const Tn=.05,Kd=[.6,.58,.53],yg=[.5,.5,.48];function Ka(a,e,t,i,n,s,r,o){const l=new wo({color:new se(...o),side:Zt}),c=new De(new en(e,t,i),l);c.position.set(n,s,r),a.add(c)}function fA(a,e,t,i,n,s,r,o,l){const c=(i-s)/2,h=(f,v,m,g)=>{f<=0||(e?Ka(a,f,v,Tn,m,o+g,r,l):Ka(a,Tn,v,f,r,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((f,v)=>f.center-v.center);let u=-t/2;for(const f of d){const m=f.center-f.main/2-u;h(m,s,u+m/2,0),u=f.center+f.main/2}const p=t/2-u;h(p,s,u+p/2,0)}function gb(a,e,t,i,n,s,r,o,l,c){const h=(i+n)/2,d=n-i,u=(p,f,v,m)=>{p.length===0?f?Ka(a,v,d,Tn,0,h,m,Kd):Ka(a,Tn,d,v,m,h,0,Kd):fA(a,f,v,d,p,s,m,h,Kd)};u(r,!0,e*2,-t+Tn),u(o,!0,e*2,t-Tn),u(l,!1,t*2,-e+Tn),u(c,!1,t*2,e-Tn),Ka(a,e*2,Tn,t*2,0,i+Tn,0,yg),Ka(a,e*2,Tn,t*2,0,n-Tn,0,yg)}function jp(a,e,t,i){switch(a){case"front":return{x:0,z:-(t-i)};case"behind":return{x:0,z:t-i};case"left":return{x:-(e-i),z:0};case"right":return{x:e-i,z:0};case"front-left":return{x:-(e-i),z:-us};case"front-right":return{x:e-i,z:-us};case"behind-left":return{x:-(e-i),z:us};case"behind-right":return{x:e-i,z:us}}}function vb(a,e,t,i,n,s,r){const o=ef();o.scale.setScalar(i),o.rotation.y=mb[e];const{x:l,z:c}=jp(e,n,s,r);o.position.set(l,t,c),a.add(o)}const Jp=6,Qp=6,dr=3,Ho=1.4,Go=dr*.5,bb=1.2,yb=.35,mA=.75,gA=1.6;function Mg(a,e,t,i){const n=new Nt,s=Math.max(1,a+1),r=s*dr;Gt(n,new en(Jp,r,Qp),[.55,.54,.5],r/2);const o=Jp/2,l=Qp/2;for(let c=0;c<s;c++){const h=c*dr+dr/2;ds(n,Ho,Go,hs,0,h,-l-cs,Zi(e,"front")),ds(n,Ho,Go,hs,0,h,l+cs,Zi(e,"behind")),ds(n,hs,Go,Ho,-o-cs,h,0,Zi(e,"left")),ds(n,hs,Go,Ho,o+cs,h,0,Zi(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);vb(n,t,c*dr,.72,o,l,bb);const h=d=>Zi(e,d)!==void 0?[{main:Ho,center:0}]:[];gb(n,o,l,c*dr,(c+1)*dr,Go,h("front"),h("behind"),h("left"),h("right"))}return n}function wg(){const a=new Nt,e=[[0,0,.55,.5],[.4,.1,.35,.4],[-.35,-.2,.4,.38],[.1,-.4,.35,.36],[-.15,.4,.3,.34]];for(const[t,i,n,s]of e){const r=new mi(n,10,7);r.scale(1,.85,1),r.translate(t,0,i),Gt(a,r,[.26,.28,.17],s)}return a}function vA(a){const e=new Nt,t=new Bt(ui.sizeOf(a),a.bridge).build();Gt(e,t.concrete,[.6,.6,.58],0);const i=Gt(e,t.road,[.2,.2,.21],0),n=Gt(e,t.earth,[.3,.36,.22],0);for(const s of[i,n])s.material.side=cn;return Gt(e,t.railing,[.5,.52,.55],0),e}function xg(){const a=new Nt;return Gt(a,new Ss(.15,.22,2,8),[.32,.22,.14],1),Gt(a,new Mo(1.4,3,8),[.16,.32,.14],3.5),a}const Sg=.275,bA=.35,yA=.9,MA=34,wA=8,xA=1.4;function Eg(){const a=new Nt,e=new mi(Sg,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,bA/Sg,1);const t=ui.repeatOnGrid(e,MA,yA,wA,xA),i=Gt(a,t,[.26,.28,.22],0);return i.castShadow=!0,i.userData.cropBasePositions=t.getAttribute("position").array.slice(),i.userData.cropVerticesPerClump=e.getAttribute("position").count,e.dispose(),a}function _g(){const a=new Nt,e=new mi(1,7,3,0,Math.PI*2,0,Math.PI/2);return Gt(a,e,[.38,.36,.32],0),a}function Ag(a){const e=new Nt;return Gt(e,new Ss(.05,.08,5,8),[.28,.28,.3],2.5),Gt(e,new mi(.25,12,8),a?pb:dA,5.1,!0),e}const SA=[.08,.08,.08],ar=.8,Ha=1,ts=1.7,Zd=.6,Cr=.4,_c=1.2,Na=.7,us=.5,EA=ts;function Tg(a,e,t){const i=new Nt;Gt(i,new en(1.8,1.4,4.2),[.45,.14,.14],.7),Gt(i,new en(ar*2,Zd,Ha*2),[.4,.12,.12],ts);for(const s of[-1.4,1.4])for(const r of[-.95,.95]){const o=Gt(i,new Ss(.4,.4,.3,12),SA,.4);o.rotation.z=Math.PI/2,o.position.x=r,o.position.z=s}const n=a?fb:uA;for(const s of[-.7,.7])Gt(i,new mi(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(ds(i,_c,Cr,hs,0,ts,-Ha-cs,Zi(e,"front")),ds(i,_c,Cr,hs,0,ts,Ha+cs,Zi(e,"behind")),ds(i,hs,Cr,Na,-ar-cs,ts,-us,Zi(e,"front-left")),ds(i,hs,Cr,Na,-ar-cs,ts,us,Zi(e,"behind-left")),ds(i,hs,Cr,Na,ar+cs,ts,-us,Zi(e,"front-right")),ds(i,hs,Cr,Na,ar+cs,ts,us,Zi(e,"behind-right")),t){vb(i,t,mA,.5,ar,Ha,yb);const s=(l,c)=>{const h=[];return Zi(e,l)!==void 0&&h.push({main:Na,center:-us}),Zi(e,c)!==void 0&&h.push({main:Na,center:us}),h},r=Zi(e,"front")!==void 0?[{main:_c,center:0}]:[],o=Zi(e,"behind")!==void 0?[{main:_c,center:0}]:[];gb(i,ar,Ha,ts-Zd/2,ts+Zd/2,Cr,r,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const Ig=new Map;function Dg(a,e){if(!a)return;const t=[[a.lengthM,e.lengthM],[a.heightM,e.heightM],[a.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function jd(a,e){return e>1e-6?a/e:1}const Mb=[.95,.9,.82],Rg=[.72,.74,.78];function Cg(){const a=new Nt;Gt(a,new Ss(.25,.3,1.5,10),Rg,.75),Gt(a,new mi(.26,10,8),Rg,1.74);const e=Gt(a,new Mo(.07,.16,8),Mb,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,a}function ef(){const a=new Nt;Gt(a,new Ss(.25,.3,1.5,10),[.3,.3,.35],.75),Gt(a,new mi(.22,10,8),[.62,.52,.46],1.72);const e=Gt(a,new Mo(.07,.16,8),Mb,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,a}const _A=17,Pg=18.5,Jd=[.16,.16,.18];function Lg(){const a=new Nt,e=Gt(a,new Ss(1.8,1.8,Pg*2,10),Jd,0);e.rotation.x=Math.PI/2,Gt(a,new en(_A*2,.5,4.5),Jd,0);const t=Gt(a,new en(.4,5.5,4),Jd,3);return t.position.z=-Pg+2,a}class ui{static GROWS_FROM_GROUND=new Set(["tree","shrub","crop","mound"]);static groundUnderFootprint(e,t,i,n,s,r=0){const o=ui.sizeOf(e);if(r>o.heightM)return s(t,i);const l=(n??0)*Math.PI/180,c=Math.min(21,Math.max(2,Math.ceil(o.lengthM/.75)+1)),h=Math.min(21,Math.max(2,Math.ceil(o.widthM/.75)+1)),d=ui.GROWS_FROM_GROUND.has(e.kind);let u=d?1/0:-1/0;for(let p=0;p<c;p++){const f=(p/(c-1)-.5)*o.lengthM;for(let v=0;v<h;v++){const m=(v/(h-1)-.5)*o.widthM,g=t+Math.sin(l)*f+Math.cos(l)*m,x=i-Math.cos(l)*f+Math.sin(l)*m;u=d?Math.min(u,s(g,x)):Math.max(u,s(g,x))}}return Number.isFinite(u)?u:s(t,i)}static occupantView(e){const t=e.observerSide;if(!t)throw new Error("occupantView requires object.observerSide to be set");const{x:i,z:n}=e.kind==="vehicle"?jp(t,ar,Ha,yb):jp(t,Jp/2,Qp/2,bb),s=e.kind==="vehicle"?EA:(e.occupiedFloor??0)*dr+gA,r=(e.headingDeg??0)-mb[t]/qd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:r}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ui.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const r=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=r.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),p=new Uint32Array(l.count*h),f=-((t-1)*i)/2,v=-((n-1)*s)/2;let m=0;for(let x=0;x<n;x++)for(let y=0;y<t;y++,m++){const b=v+x*s,w=f+y*i;for(let E=0;E<c;E++){const T=(m*c+E)*3;d[T]=r.getX(E)+b,d[T+1]=r.getY(E),d[T+2]=r.getZ(E)+w,u[T]=o.getX(E),u[T+1]=o.getY(E),u[T+2]=o.getZ(E)}for(let E=0;E<l.count;E++)p[m*l.count+E]=l.getX(E)+m*c}const g=new dt;return g.setAttribute("position",new vt(d,3)),g.setAttribute("normal",new vt(u,3)),g.setIndex(new Af(p,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,r=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!r||!o)return;const l=s.geometry.getAttribute("position"),c=new $e().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(r[h*3],0,r[h*3+2]).applyMatrix4(s.matrixWorld),i.y=t(i.x,i.z),i.applyMatrix4(c);for(let d=h;d<h+o;d++)l.setY(d,r[d*3+1]+i.y)}l.needsUpdate=!0,s.geometry.computeBoundingBox(),s.geometry.computeBoundingSphere()})}static paint(e,t){e.traverse(i=>{const n=i,s=n.userData;if(!n.isMesh||s?.emissive)return;const r=n.material;r?.color&&r.color.set(t)})}static build(e,t){const i=e.kind==="building"?Mg(e.floors??Ja,e.windows,e.observerSide,e.occupiedFloor):e.kind==="tree"?xg():e.kind==="shrub"?wg():e.kind==="bridge"?vA(e):e.kind==="crop"?Eg():e.kind==="mound"?_g():e.kind==="streetlight"?Ag(t):e.kind==="vehicle"?Tg(t,e.windows,e.observerSide):e.kind==="aircraft"?Lg():e.kind==="entity"?Cg():ef();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Nt;return i.name=ui.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*qd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t||e.kind==="bridge")return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:jd(t.widthM,i.widthM),t.heightM===void 0?1:jd(t.heightM,i.heightM),t.lengthM===void 0?1:jd(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Ja)+1):0;if(e==="bridge")return Bt.NATURAL_SIZE;const n=`${e}:${i}`,s=Ig.get(n);if(s)return s;const r=e==="building"?Mg(i-1,void 0,void 0,void 0):e==="tree"?xg():e==="shrub"?wg():e==="crop"?Eg():e==="mound"?_g():e==="streetlight"?Ag(!1):e==="vehicle"?Tg(!1,void 0,void 0):e==="aircraft"?Lg():e==="entity"?Cg():ef(),o=new Qi().setFromObject(r),l={widthM:o.max.x-o.min.x,heightM:o.max.y-o.min.y,lengthM:o.max.z-o.min.z};return this.dispose(r),Ig.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.observerSide!==void 0&&Va(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Nt;r.name=ui.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*qd,i.updateMatrixWorld(!0);const o=new Qi().setFromObject(i),l={lengthM:o.max.z-o.min.z,heightM:o.max.y-o.min.y,widthM:o.max.x-o.min.x},c=Dg(t.sizeM,l)??Dg(n.depictedSizeM,l)??1;i.scale.multiplyScalar(c);const h=o.getCenter(new R).multiplyScalar(c);i.position.x-=h.x,i.position.z-=h.z,i.position.y-=t.kind==="aircraft"?h.y:o.min.y*c,i.traverse(d=>{d instanceof De&&(d.castShadow=!0,d.receiveShadow=!0)}),r.add(i),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s)}static sizeOf(e){const t=this.naturalSize(e.kind,e.floors);return{widthM:e.sizeM?.widthM??t.widthM,lengthM:e.sizeM?.lengthM??t.lengthM,heightM:e.sizeM?.heightM??t.heightM}}static addLights(e,t){for(const i of t??[]){const n=hA(i.color),s=Gt(e,new mi($d,8,6),n,i.offsetM.y,!0);s.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),s.userData={emissive:!0,lightId:i.id,lightColor:n},s.visible=!1}}static setLights(e,t,i,n,s=0,r=1,o=sA){if(!t||t.length===0)return;const l=new Map(t.map(u=>[u.id,u])),c=Math.max(1,n*o/$d),h=$d*c,d=r/(Math.PI*h*h);for(const u of e.children){if(!(u instanceof De))continue;const p=u.userData.lightId;if(!p)continue;const f=l.get(p);if(!f)continue;const v=s>0?zv(f,i,i+s):Ay(f,i)?1:0;u.visible=v>0;const m=u.material,g=u.userData.lightColor;if(g){const x=v*rA*(f.intensity??1)*d;m.color.setRGB(g[0]*x,g[1]*x,g[2]*x)}u.scale.setScalar(c)}}static setLit(e,t,i,n=1){const[s,r,o]=t==="streetlight"?[aA,oA,pb]:[lA,cA,fb],l=i?s*n/(Math.PI*r*r):0,c=[o[0]*l,o[1]*l,o[2]*l];e.traverse(h=>{const d=h.userData;!(h instanceof De)||!d.emissive||d.lightId||h.material.color.setRGB(...c)})}static dispose(e){e.traverse(t=>{if(!(t instanceof De))return;t.geometry.dispose();const i=t.material;for(const n of Array.isArray(i)?i:[i])n.dispose()})}}const Qd="https://ufoathome.org/models/index.json";class AA{attribution="3D models re-hosted by UFO@home, each under its own author's licence";fetchImpl;indexUrls;loading;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??TA()}async entries(e){const t=await this.load();return e?t.filter(i=>i.kind===e):t}async entry(e){return(await this.load()).find(t=>t.id===e)}load(){return this.loading??=this.fetchFirstAvailable(),this.loading}async fetchFirstAvailable(){for(const e of this.indexUrls){const t=await this.fetchIndex(e);if(t)return t}return[]}async fetchIndex(e){try{const t=await this.fetchImpl(e);if(!t.ok)return;const i=await t.json();return Array.isArray(i?.models)?i.models.map(({file:n,...s})=>({...s,url:new URL(n,e).href})):void 0}catch{return}}}function TA(){const a=`?v=${encodeURIComponent("0.81.5")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==Qd?[e,Qd]:[Qd]).map(t=>t+a)}const Kc=[{id:"ufoathome",name:"UFO@home",credit:"3D models re-hosted by UFO@home, each under its own author's licence",creditUrl:"https://ufoathome.org/models/",create:()=>new AA}];async function kg(a){const{GLTFLoader:e}=await gt(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-DSbhUTN3.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(a,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${a}: ${s instanceof Error?s.message:String(s)}`)))})}const wb=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class yn{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new De(new tn(2,2),new ut({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:yn.VERTEX,fragmentShader:yn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Yi,toneMapped:!1})),this.mesh.name=e,this.mesh.frustumCulled=!1,this.mesh.renderOrder=t,this.mesh.visible=!1}shine(e,t,i){this.mesh.visible=!0,this.mesh.position.set(e.x,e.y,e.z),this.mesh.material.uniforms.uRadius.value=t,this.mesh.material.uniforms.uColor.value.setRGB(i[0],i[1],i[2])}hide(){this.mesh.visible=!1}dispose(){this.mesh.geometry.dispose(),this.mesh.material.dispose()}static spread(e,t,i,n){const s=Math.max(e,n*yn.MIN_ANGLE_RAD),r=yn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(yn.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(yn.MAX_SHARE*yn.FILL*Math.PI))}static VERTEX=`
|
|
8767
8767
|
uniform float uRadius;
|
|
8768
8768
|
varying vec2 vOffset;
|
|
8769
8769
|
void main() {
|
|
@@ -9326,7 +9326,7 @@ cloudPoint = (modelMatrix * vec4(transformed, 1.0)).xyz - cameraPosition;`);let
|
|
|
9326
9326
|
vec3 light = texture2D(uMap, vec2((along * (uSteps - 1.0) + 0.5) / uSteps, 0.5)).rgb;
|
|
9327
9327
|
gl_FragColor = vec4(light, 1.0);
|
|
9328
9328
|
}
|
|
9329
|
-
`,transparent:!0,blending:Yi,depthWrite:!1,side:Zt,fog:!1});this.object=new De(new mi(Zf.RADIUS,128,32,0,Math.PI*2,0,Mt.MAX_ANGLE_RAD),t),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1}show(e,t){if(!e||!t){this.object.visible=!1;return}this.towards.set(e.x,e.y,e.z).normalize(),this.object.quaternion.setFromUnitVectors(this.up,this.towards),this.object.material.uniforms.uSource.value.copy(this.towards);for(let i=0;i<Mt.STEPS;i++){for(let n=0;n<3;n++)this.texels[i*4+n]=Xn.toHalfFloat(Math.min(t[i*3+n],6e4));this.texels[i*4+3]=Xn.toHalfFloat(1)}this.texture.needsUpdate=!0,this.object.visible=!0}dispose(){this.object.geometry.dispose(),this.object.material.dispose(),this.texture.dispose()}}class Th{constructor(e){this.generator=e}static SIZE=128;target=new Al(Th.SIZE,{type:ti,generateMipmaps:!1,minFilter:ht,magFilter:ht});camera=new Lf(.1,2e5,this.target);filteredTarget;capturedAtMs=-1/0;capturedVersion;get sharp(){return this.target.texture}get filtered(){return this.filteredTarget?.texture}capture(e,t,i,n=[]){const s=e.shadowMap.autoUpdate;e.shadowMap.autoUpdate=!1;const r=n.map(o=>o.visible);for(const o of n)o.visible=!1;this.camera.position.copy(i),this.camera.update(e,t),n.forEach((o,l)=>{o.visible=r[l]}),e.shadowMap.autoUpdate=s,this.filteredTarget=this.generator.fromCubemap(this.target.texture,this.filteredTarget),this.capturedAtMs=performance.now()}dispose(){this.target.dispose(),this.filteredTarget?.dispose()}}class gn{static REFRESH_MS=250;generator;eye;probes=new Map;constructor(e){gn.install(),this.generator=new Sp(e),this.eye=new Th(this.generator)}static decor;refresh(e,t,i,n,s,r,o,l=[],c,h=gn.REFRESH_MS){for(const m of[...this.probes.keys()])n.some(g=>g.id===m&&g.shiny)||(this.probes.get(m).dispose(),this.probes.delete(m));const d=[{probe:this.eye}];for(const m of n){if(!m.shiny)continue;let g=this.probes.get(m.id);g||(g=new Th(this.generator),this.probes.set(m.id,g)),d.push({probe:g,reflector:m})}const u=performance.now(),p=()=>d.filter(({probe:m})=>m.capturedVersion!==o),f=p().filter(({probe:m})=>u-m.capturedAtMs>=h);if(f.length>0){const m=f.reduce((g,x)=>x.probe.capturedAtMs<g.probe.capturedAtMs?x:g);if(m.reflector){const g=m.reflector.holder.getWorldPosition(this.scratch);m.probe.capture(e,t,g,[m.reflector.holder,...s]),gn.reflectOn(m.reflector.holder,m.probe)}else{m.probe.capture(e,t,i,[...s,...l]),c?.(m.probe.sharp),gn.decor=m.probe.filtered;for(const g of r)gn.reflectOn(g,m.probe);for(const g of n)g.shiny||gn.reflectOn(g.holder,m.probe)}m.probe.capturedVersion=o}const v=p();if(v.length!==0)return Math.max(16,Math.min(...v.map(({probe:m})=>h-(u-m.capturedAtMs))))}scratch=new R;static reflectOn(e,t={filtered:gn.decor}){e.traverse(i=>{const n=i.material;for(const s of Array.isArray(n)?n:n?[n]:[])s instanceof Il?t.sharp&&s.setSurroundings(t.sharp):s instanceof Hr&&t.filtered&&s.envMap!==t.filtered&&(s.envMap=t.filtered,s.needsUpdate=!0)})}dispose(){this.eye.dispose();for(const e of this.probes.values())e.dispose();this.generator.dispose()}static installed=!1;static install(){if(gn.installed)return;gn.installed=!0;const e="iblIrradiance += getIBLIrradiance( geometryNormal );";if(!Ye.lights_fragment_maps.includes(e)){console.warn("Reflections: three's lights_fragment_maps changed; the environment will light diffusely too");return}Ye.lights_fragment_maps=Ye.lights_fragment_maps.replace(e,"// The sky's diffuse light is the hemisphere light's (see Reflections).")}}function RA(a,e){return JSON.stringify(a.cloudLayers)===JSON.stringify(e.cloudLayers)&&a.cloudCover===e.cloudCover&&a.cloudDarkness===e.cloudDarkness&&a.cloudBaseM===e.cloudBaseM&&a.highCloudCover===e.highCloudCover&&a.iceCrystalAlignment===e.iceCrystalAlignment&&a.lowerCloudCover===e.lowerCloudCover&&a.relativeHumidity===e.relativeHumidity&&a.precipitationType===e.precipitationType&&a.precipitationIntensity===e.precipitationIntensity&&a.precipitationAmount===e.precipitationAmount&&a.windDirectionDeg===e.windDirectionDeg&&a.windSpeed===e.windSpeed&&a.storm===e.storm}const Ic=900,eu=1,Og=4,Ng=5,Fg=.5,nl=900,CA=6371e3,PA=3e4;function Ug(a){const e=Math.sqrt(2*CA*Math.max(a,0));return Math.max(nl,Math.min(e,PA))}const tu=.5,iu=850,Bg=.03,pn=850,Wo=300,nr=120,LA=700,kA=.04,Xo=4,nu=1.5,zg=-1,OA=6,tf=-4,NA=2,FA=20,Dc=2600,UA=.97,BA=.38,Rc=tf,Hg=1500,zA=400,HA=[0,45,90,135,180,225,270,315],GA=2,VA=16,Gg=4e3,Vg=880,Cc=40,Wg=60,WA=10,XA=1,YA=150,qA=55,$A=.5,KA=17,Xg=.265,Yg=[[0,0],...Array.from({length:8},(a,e)=>{const t=e*Math.PI/4;return[Math.cos(t)*Xg,Math.sin(t)*Xg]})],su=700,ru=250/1e3,ZA=12,qg=5,au=6,Pr=[.92,.92,.95],ou=[.15,.15,.19],lu={snow:{fallSpeedMPerS:2,size:.3,color:new se(1,1,1),driftSensitivity:1,radiusM:25,topYM:20,opacity:.75,poolSize:2400},hail:{fallSpeedMPerS:14,size:.5,color:new se(.95,.98,1),driftSensitivity:.25,radiusM:25,topYM:10,opacity:.95,poolSize:1200}},Ga=0,cu=0,jA=150,hu=3e3,Yo=20,JA=15,$g=Ga,QA=40,Kg=1,eT=9;function du(a){return Kg+(eT-Kg)*a}const Zg=new se(.75,.82,.92),tT=10,iT=.5;function jg(a){return Za(a/tT,iT,1)}const Lr=200,Jg=.18,Qg=.32,nT=.35,ev=new se(.85,.9,.98),tv=5,iv=.85,nv=.01;function sT(a,e,t=1){return Math.round(a*(nv+(1-nv)*e)*t)}function rT(a,e){return JSON.stringify(a.cloudLayers)===JSON.stringify(e.cloudLayers)&&a.cloudCover===e.cloudCover&&a.cloudDarkness===e.cloudDarkness&&a.cloudBaseM===e.cloudBaseM&&a.highCloudCover===e.highCloudCover&&a.iceCrystalAlignment===e.iceCrystalAlignment&&a.lowerCloudCover===e.lowerCloudCover}const sv=230,rv=.7,av=.05,aT=.6,oT=.7,lT=.15,ov=.35,cT=3,hT=1/12,dT=.5,es=new se(.82,.86,1),uT=2500,pT=1.6,uu=15,fT={Venus:new se(1,.96,.85),Mars:new se(1,.55,.4),Jupiter:new se(.95,.87,.72),Saturn:new se(.92,.86,.68)};class Wt{renderer;scene=new Hs;phenomena=new Un(this.scene);references=new Fn(this.scene,()=>this.render());camera;celestialGroup=new Nt;skyMesh;skyGradientMaterial;scatteredSky;lastAstronomy;skyColoursStale=!1;scatteredSkyFrameId=null;skyHoldUntilMs=void 0;onFirstFrameHold;firstFrameReady(){return(!this.scatteredSky||this.scatteredSky.ambient!==void 0)&&!this.iceHalos?.awaitingFirstDisplay}sceneSettled(){return this.firstFrameReady()&&this.arrivalsPending===0&&this.eyeSettledVersion>=this.lastArrivalVersion}arrivalsPending=0;eyeSettledVersion=-1;lastArrivalVersion=0;awaitArrival(e,t){if(this.skyHoldUntilMs===null)return e;this.arrivalsPending++;let i=!0;const n=()=>{i&&(i=!1,this.arrivalsPending--,this.lastArrivalVersion=this.sceneVersion,this.skyHoldUntilMs&&this.redrawHeld())},s=t===void 0?void 0:setTimeout(n,t);return e.finally(()=>{clearTimeout(s),n()})}static ROADS_PATIENCE_MS=1500;redrawHeld(){this.frameDirty=!0,!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()}))}holdForNewScene(e){this.surroundings=void 0,this.scatteredSky?.resetSurroundings(),this.startHold(),e&&this.awaitArrival(e).catch(()=>{})}startHold(){this.skyHoldUntilMs=performance.now()+Gg,this.eyeSettledVersion=-1,this.lastArrivalVersion=this.sceneVersion+1,this.iceHalos?.setUrgent(!0),this.onFirstFrameHold?.(!0),setTimeout(()=>this.render(),Gg+20)}groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;roadSystem=new An;roadAttribution;roadWays;statedRoads=[];decorModelProvider=Kc[0].create();decorModelToken=0;decorModelCredits=new Map;bodySystem=new ft(e=>this.awaitArrival(this.loadBodyModel(e)),()=>this.render(),()=>this.relativeScale);bodyOrigin={x:0,z:0};starTiers=[];bodyMeshes=new Map;hitAreas=new Map;glareSprites=new Map;decorObjects=[];decorGroups=new Map;decorReferencePose;decorCurrentPose;decorTime=0;celestialLight=new Im(16777215,0);celestialLightTarget=new Vt;skyLight=new iw(16777215,0,0);streetlightLights=new Map;compassSprites=[];showCompass=!1;compassHovered=!1;compassForced=!1;animationFrameId=null;resolution;cssSize={width:1,height:1};frameDirty=!1;flushFrameId=null;framesDriven=!1;lastFrameTimeMs;raycaster=new Yc;weather=sf;observerElevationM=0;siteElevationM=0;air=new In;groundRadius=nl;terrainRadius=nl;cloudRendering="volume";layeredClouds;cloudLayerOffsetsM={};cloudOffsetM=new R;cloudFieldOffset=new R;cloudMesh;cirrusMesh;cirrusMaterial;cirrusUniforms;cloudMaterial;cloudUniforms;precipitationPoints;precipitationPositions;precipitationPhase;precipitationSpeedJitter;precipitationWobbleFreqJitter;precipitationWobbleAmpJitter;precipitationBounceRemaining;precipitationBounceHeight;precipitationBounceDuration;precipitationBounceVelX;precipitationBounceVelZ;precipitationUniforms;rainSystem;rainSplashSystem;rainCameraDirection=new R;rainLastVerticalFacing=-1;rainViewportSize=new ye;lightningArmed=!1;lightningFlashes=[];lightningLevel=0;lightningBolt;lightningLight=new Im(16777215,0);baseFogColor=[0,0,0];lastSunPosition;lensFlare;sunUnhiddenFraction=1;lensFlareArtifactIntensity=0;sunWorldPosition=new R;sunVisible=!1;lensFlareScratch=new R;sunOcclusionRaycaster=new Yc;sunOcclusionDirectionScratch=new R;sunOcclusionRightScratch=new R;sunOcclusionUpScratch=new R;ufoOcclusionRaycaster=new Yc;projectionKind="rectilinear";starPoints=0;namedStars=[];equidistantPass;depthOfFieldPass;finishPass;exposureAccumulation;exposureInstants=1;get developingExposure(){return this.exposureInstants>1&&this.exposureInstantAt!==void 0}static EXPOSURE_BUDGET_MS=12;static exposureInstantsPerFrame(e){return Math.max(3,Math.round(6e6/Math.max(1,e)))}exposureFrameId=null;exposureInstantsDone=0;exposureInstantAt;restatingExposure=!1;lensOptics;instrumentMagnitudeGain=0;lightPollution;meteorSystem;cometTail;satelliteField;iceHalos;lunarDiffraction;lunarDiffractionKey="";rainbow;skyGlow;cometKey;novaKeys=new Set;constructor(e,t=jy()){this.renderer=new m_({canvas:e,antialias:!0}),this.resolution=new xt(Math.min(window.devicePixelRatio||1,2),this.renderer.getContext()),this.renderer.setPixelRatio(this.resolution.pixelRatio),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=rl,this.camera=new ln(60,e.width/Math.max(e.height,1),.1,Ic*1.2),this.camera.position.set(0,1.6,0),this.terrainProviders=t,this.celestialLight.shadow.mapSize.set(1024,1024),this.celestialLight.shadow.camera.left=-nr,this.celestialLight.shadow.camera.right=nr,this.celestialLight.shadow.camera.top=nr,this.celestialLight.shadow.camera.bottom=-nr,this.celestialLight.shadow.camera.near=pn-Wo,this.celestialLight.shadow.camera.far=pn+Wo,this.celestialLight.shadow.bias=-.03/(2*Wo),this.celestialLight.shadow.normalBias=kA,this.celestialLight.target=this.celestialLightTarget,this.lightningLight.color.copy(es),this.scene.add(this.celestialLight,this.celestialLightTarget,this.skyLight,this.lightningLight),this.scene.add(this.celestialGroup),this.scene.add(this.bodySystem.group),this.scene.add(this.roadSystem.group);const i=new zt(this.renderer,()=>{this.skyColoursStale=!0,this.render()});i.supported?this.scatteredSky=i:i.dispose()}get currentTerrainAttribution(){return this.terrainAttribution}get currentRoadAttribution(){return this.roadAttribution}resize(e,t){this.cssSize.width=e,this.cssSize.height=t,this.renderer.setSize(e,t,!1),this.camera.aspect=e/Math.max(t,1),this.camera.updateProjectionMatrix(),this.render()}setCloudRendering(e){e!==this.cloudRendering&&(this.cloudTransmissionMemo.clear(),this.cloudRendering=e,this.layeredClouds?.dispose(),this.layeredClouds=void 0,this.buildClouds(),this.buildCirrus(),this.render())}setCloudOffset(e,t={}){this.cloudOffsetM.set(e.x,0,e.z),this.cloudLayerOffsetsM=t,this.layeredClouds?.setOffsets(e,t),this.cloudFieldOffset.set(e.x*ru,0,e.z*ru);for(const i of[this.cloudUniforms,this.cirrusUniforms])i?.fieldOffset.value.copy(this.cloudFieldOffset);this.iceHalos?.setCloudOffset(this.layeredClouds?.cirrusMask?.offset??this.cloudFieldOffset)}setObserverPose(e){const t=((e.rollDeg??0)+this.gaitOffset.rollDeg)*sn,i=(e.pitchDeg+this.lookPitchDeg+this.gaitOffset.pitchDeg)*sn;if(e.headingDeg!==void 0?this.camera.rotation.set(i,-(e.headingDeg+this.lookYawDeg+this.gaitOffset.yawDeg)*sn,t,"YXZ"):this.camera.rotation.set(i,this.camera.rotation.y,t,"YXZ"),e.elevationM!==this.observerElevationM&&(this.observerElevationM=e.elevationM,this.syncCloudLayer(),Math.abs(Ug(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/nl),this.camera.far=Math.max(Ic*1.2,this.groundRadius*2.5),this.camera.updateProjectionMatrix())),this.terrainMesh&&this.terrainOrigin&&e.lat!==void 0&&e.lng!==void 0){const n=pi(this.terrainOrigin.lat,this.terrainOrigin.lng,e.lat,e.lng);this.terrainMesh.position.x=n.x,this.terrainMesh.position.z=n.z}this.poseCameraY=this.groundYUnder(0,0)+1.6+e.elevationM,this.camera.position.y=this.poseCameraY,this.celestialGroup.position.y=this.camera.position.y,this.camera.fov!==e.fovDeg&&(this.camera.fov=e.fovDeg,this.camera.updateProjectionMatrix(),this.updateCompassScale())}setTerrainProviders(e){this.stopTerrainFade(),this.terrainProviders=e,this.disposeMesh(this.terrainMesh),this.terrainMesh=void 0,this.terrainOrigin=void 0,this.terrainAttribution=void 0,this.roadSystem.clear(),this.roadAttribution=void 0,this.roadWays=void 0,this.applyGroundDepthWrite(),this.terrainBuildToken++}setTerrainOrigin(e,t,i){if(e===void 0||t===void 0)return;const n=this.groundRadius;if(this.terrainOrigin&&Math.abs(this.terrainRadius-n)<n*.1){const{x:l,z:c}=pi(e,t,this.terrainOrigin.lat,this.terrainOrigin.lng);if(Math.sqrt(l*l+c*c)<YA*(n/nl))return}const s=this.terrainOrigin,r=this.terrainRadius;(!s||s.lat!==e||s.lng!==t)&&(this.roadSystem.clear(),this.roadAttribution=void 0),this.terrainOrigin={lat:e,lng:t},this.terrainRadius=n;const o=++this.terrainBuildToken;this.awaitArrival(z5(e,t,this.terrainProviders,n)).then(({mesh:l,attribution:c,originElevationM:h})=>{if(o!==this.terrainBuildToken)return;this.setSiteElevation(h??0);const d=this.terrainMesh,u=s?pi(e,t,s.lat,s.lng):void 0;d&&u&&Math.hypot(u.x,u.z)<=r&&(l.position.y=d.position.y+this.groundYOfPatch(d,u.x,u.z)),d&&u&&Math.hypot(u.x,u.z)<=r?this.crossfadeTerrain(l,d):this.disposeMesh(this.terrainMesh),l.renderOrder=XA,this.terrainMesh=l,this.compileNextFrameOffThread(),this.terrainAttribution=c,this.scene.add(l),this.applyGroundDepthWrite(),this.decorCurrentPose&&(this.setObserverPose(this.decorCurrentPose),this.updateDecorAnchoring(this.decorReferencePose,this.decorCurrentPose,this.decorTime)),this.buildRoads(e,t,n),this.drapeStatedRoads(),this.render(),i?.()}).catch(l=>{console.warn("Terrain build failed, keeping the flat ground fallback:",l),i?.()})}static TERRAIN_CROSSFADE_MS=1200;fadingTerrain;crossfadeTerrain(e,t){this.stopTerrainFade();const i=e.material;i.opacity=0,i.polygonOffset=!0,i.polygonOffsetFactor=-1,i.polygonOffsetUnits=-4;const n=performance.now(),s=()=>{const r=Math.min(1,(performance.now()-n)/Wt.TERRAIN_CROSSFADE_MS);if(i.opacity=r*r*(3-2*r),this.render(),r<1&&this.fadingTerrain){this.fadingTerrain.frameId=requestAnimationFrame(s);return}i.polygonOffset=!1,i.opacity=1,this.fadingTerrain&&this.disposeMesh(this.fadingTerrain.mesh),this.fadingTerrain=void 0,this.render()};this.fadingTerrain={mesh:t,frameId:requestAnimationFrame(s)}}stopTerrainFade(){if(!this.fadingTerrain)return;cancelAnimationFrame(this.fadingTerrain.frameId),this.disposeMesh(this.fadingTerrain.mesh),this.fadingTerrain=void 0;const e=this.terrainMesh?.material;e&&(e.opacity=1,e.polygonOffset=!1)}static ROADS_MAX_RADIUS_M=5e3;buildRoads(e,t,i){const n=this.terrainProviders.roads;if(!n||i>Wt.ROADS_MAX_RADIUS_M){this.roadSystem.clear(),this.roadAttribution=void 0;return}const s=this.roadWays;if(s&&s.lat===e&&s.lng===t&&s.radiusM===i){this.drapeRoads(s.ways,e,t,n.contemporary);return}this.awaitArrival(n.getRoads(ub(e,t,i)),Wt.ROADS_PATIENCE_MS).then(r=>{this.terrainOrigin?.lat!==e||this.terrainOrigin?.lng!==t||(this.roadWays={lat:e,lng:t,radiusM:i,ways:r},this.drapeRoads(r,e,t,n.contemporary))}).catch(r=>{console.warn("Roads unavailable, drawing none:",r)})}setStatedRoads(e){e!==this.statedRoads&&(this.statedRoads=e,this.drapeStatedRoads(),this.render())}drapeStatedRoads(){const e=this.terrainMesh;e&&(this.roadSystem.group.position.copy(e.position),this.roadSystem.setStated(this.statedRoads,(t,i)=>this.groundYOfPatch(e,t,i)))}drapeRoads(e,t,i,n){const s=this.terrainMesh;s&&(this.roadSystem.group.position.copy(s.position),this.roadSystem.set(e,t,i,(r,o)=>this.groundYOfPatch(s,r,o),n),this.roadAttribution=e.length>0?this.terrainProviders.roads?.attribution:void 0,this.compileNextFrameOffThread(),this.render())}setShowCompass(e){this.showCompass!==e&&(this.showCompass=e,this.disposeCompassLabels(),e&&this.buildCompassLabels(),this.render())}setCompassPoints(e){this.compassPoints!==e&&(this.compassPoints=e,this.showCompass&&(this.disposeCompassLabels(),this.buildCompassLabels(),this.render()))}compassPoints;applyLensFlareTint(e){if(!this.lensFlare)return;const t=qA*(this.sunArriving[1]/KA);this.lensFlare.uniforms.uColorGain.value.setRGB(t*e[0],t*e[1],t*e[2]),this.applyDazzleStrength()}applyDazzleStrength(){if(!this.lensFlare)return;const[e,t,i]=this.sunArriving,n=Math.max(t,1e-30);this.lensFlare.uniforms.uDazzleColour.value.setRGB(e/n,1,i/n),this.lensFlare.uniforms.uVeilStrength.value=Gi.K*t*this.sunUnhiddenFraction}sunArriving=[0,0,0];setCompassHovered(e){this.compassHovered!==e&&(this.compassHovered=e,this.updateCompassVisibility())}setCompassForced(e){this.compassForced!==e&&(this.compassForced=e,this.updateCompassVisibility())}updateCompassVisibility(){const e=this.compassHovered||this.compassForced;if(this.compassSprites.some(t=>t.visible!==e)){for(const t of this.compassSprites)t.visible=e;this.developingExposure?this.exposureInstantsDone>0&&this.presentExposure():this.render()}}setWeather(e){if(RA(this.weather,e))return;this.cloudTransmissionMemo.clear();const t=this.weather;this.weather=e;const i=!rT(t,e),n=t.precipitationType!==e.precipitationType,s=t.precipitationIntensity!==e.precipitationIntensity||t.precipitationAmount!==e.precipitationAmount;(t.relativeHumidity!==e.relativeHumidity||n||s)&&(this.air=In.of(e),this.applyAir(),this.scatteredSky?.setConditions({aerosolOpticalDepth:e.relativeHumidity===void 0?void 0:Us.steppedOpticalDepth(e.relativeHumidity)}),this.pumpScatteredSky()),i&&(this.buildClouds(),this.buildCirrus()),n?this.buildPrecipitation():s&&this.updatePrecipitationDensity(),this.lastAstronomy&&(i&&(this.buildIceHalos(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildLunarDiffraction(this.lastAstronomy.moon)),(i||n||s)&&this.buildRainbow(this.lastAstronomy.sun,this.lastAstronomy.moon)),this.lightningArmed=e.storm&&e.cloudDarkness>=dT,this.syncAnimationLoop(),this.render()}setDecor(e){if(this.decorObjects!==e){this.decorObjects=e,this.decorModelToken++,this.decorModelCredits.clear();for(const t of this.decorGroups.values())t.removeFromParent(),ui.dispose(t);this.decorGroups.clear(),this.streetlightLights.clear();for(const t of e){const i=ui.build(t,t.lit??!1);this.scene.add(i),this.decorGroups.set(t.id,i),t.kind==="streetlight"&&this.addStreetlightLight(t.id,i),this.awaitArrival(this.loadDecorModel(t,this.decorModelToken))}this.celestialLight.castShadow=this.decorGroups.size>0||this.bodySystem.any}}setDecorModelProvider(e){this.decorModelProvider=e;const t=this.decorObjects;this.decorObjects=[],this.setDecor(t)}get currentDecorModelCredits(){return[...this.decorModelCredits.values(),...this.bodySystem.modelCredits]}get bodyGround(){const{x:e,z:t}=this.bodyOrigin,i=this.groundYUnder(e,t);return{heightAt:(n,s)=>this.groundYUnder(e+n,t-s)-i}}setBodies(e,t=0,i=e.map(r=>r.id),n=[],s){const{x:r,z:o}=this.bodyOrigin,l=this.scatteredSky;this.bodySystem.set(e,{originX:r,originZ:o,originGroundY:this.groundYUnder(r,o),eye:this.camera.position,groundYAt:(h,d)=>this.groundYUnder(h,d),wind:s,light:this.plumeLight(),occluders:[...this.decorGroups.values()],transmittance:(h,d,u)=>this.transmittanceTo(h,d,u)},t,l?(h,d)=>l.relativeOfLuminance(h,d):void 0,i),this.bodySystem.setSmoke(n,t),this.bodySystem.any&&(this.celestialLight.castShadow=!0),this.fitShadowToBodies();const c=this.bodySystem.furthestFrom(this.camera.position)*1.2;c>this.camera.far&&(this.camera.far=c,this.camera.updateProjectionMatrix())}plumeLight(){const e=this.celestialLight.color,t=this.celestialLight.intensity*.5,i=this.skyLight.color,n=this.skyLight.groundColor,s=this.skyLight.intensity,r=(o,l,c)=>(o*t+(l+c)/2*s)/Math.PI;return[r(e.r,i.r,n.r),r(e.g,i.g,n.g),r(e.b,i.b,n.b)]}bodyOutline(e,t){return this.bodySystem.outlineOf(e,t)}documentUrl;modelUrl(e){return e.url?this.documentRelative(e.url):void 0}documentRelative(e){if(e.startsWith("data:"))return e;const t=this.documentUrl??(typeof location>"u"?void 0:location.href);return t?new URL(e,t).href:e}async loadBodyModel(e){const t=e.url?void 0:e.id?await this.decorModelProvider.entry(e.id):void 0,i=this.modelUrl(e)??t?.url,n=e.credit??t?.credit;if(!(!i||!n))return{scene:await kg(i),credit:n,headingOffsetDeg:e.headingOffsetDeg??t?.headingOffsetDeg}}async loadDecorModel(e,t){const i=e.model;if(!(!i||!ui.usesModel(e)))try{const n=i.url?void 0:i.id?await this.decorModelProvider.entry(i.id):void 0,s=this.modelUrl(i)??n?.url,r=i.credit??n?.credit;if(!s||!r)return;const o=await kg(s);if(t!==this.decorModelToken)return;const l=this.decorGroups.get(e.id);if(!l)return;gn.reflectOn(o),ui.applyModel(l,e,o,{headingOffsetDeg:i.headingOffsetDeg??n?.headingOffsetDeg,depictedSizeM:n?.sizeM}),this.decorModelCredits.set(e.id,r),this.render()}catch(n){console.warn(`Keeping the built-in shape for decor "${e.id}":`,n)}}updateDecorAnchoring(e,t,i=0){this.decorReferencePose=e,this.decorCurrentPose=t,this.decorTime=i;const n=e?.lat!==void 0&&e.lng!==void 0&&t?.lat!==void 0&&t?.lng!==void 0?pi(e.lat,e.lng,t.lat,t.lng):{x:0,z:0},s=this.decorObjects.find(c=>c.observerSide!==void 0&&Va(c.kind));if(this.terrainMesh&&this.terrainOrigin&&t?.lat!==void 0&&t.lng!==void 0){const c=pi(this.terrainOrigin.lat,this.terrainOrigin.lng,t.lat,t.lng);this.terrainMesh.position.x=c.x-(s?0:this.gaitOffset.eastM),this.terrainMesh.position.z=c.z+(s?0:this.gaitOffset.northM),this.roadSystem.group.position.copy(this.terrainMesh.position)}s||(n.x-=this.gaitOffset.eastM,n.z+=this.gaitOffset.northM,this.camera.position.y=this.poseCameraY+this.gaitOffset.upM,this.celestialGroup.position.y=this.camera.position.y);const r={x:0,z:0};let o=0;if(s){const c=ui.occupantView(s),h=-(s.headingDeg??0)*sn,d=c.x*Math.cos(h)+c.z*Math.sin(h),u=-c.x*Math.sin(h)+c.z*Math.cos(h),p=s.eastM+n.x,f=-s.northM+n.z;r.x=-(p+d),r.z=-(f+u),this.camera.position.y=this.groundYUnder(p,f)+c.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*sn,-(c.headingDeg+this.indoorLookYawDeg)*sn,0,"YXZ")}this.bodyOrigin={x:n.x+r.x,z:n.z+r.z},this.placeShadowFrustum();for(const c of this.decorObjects){const h=this.decorGroups.get(c.id);if(!h)continue;const d=yr(c,i),u=d.eastM+n.x+r.x,p=-d.northM+n.z+r.z,f=this.terrainMesh,v=`${f?.geometry.uuid}:${(u-(f?.position.x??0)).toFixed(3)}:${(p-(f?.position.z??0)).toFixed(3)}:${(f?.position.y??0).toFixed(3)}:${d.headingDeg}:${d.altitudeM}`;if(h.userData.footprintKey!==v&&(h.userData.footprintGroundY=this.groundUnderFootprint(c,u,p,d.headingDeg,d.altitudeM),h.userData.footprintKey=v),h.position.set(u,h.userData.footprintGroundY+d.altitudeM,p),c.kind==="crop"){const m=Math.hypot(u,p);h.visible=m<=jA}if(d.headingDeg!==void 0&&(h.rotation.y=-d.headingDeg*sn),c.kind==="crop"&&h.visible){const m=this.terrainMesh,g=[m?.geometry.uuid,u-(m?.position.x??0),p-(m?.position.z??0),h.position.y-(m?.position.y??0),d.headingDeg,d.altitudeM].map(x=>typeof x=="number"?x.toFixed(4):x).join(":");h.userData.cropGroundKey!==g&&(ui.fitCropToGround(h,(x,y)=>this.groundYUnder(x,y)+d.altitudeM),h.userData.cropGroundKey=g)}o=Math.max(o,h.position.distanceTo(this.camera.position))}const l=Math.max(Ic*1.2,this.groundRadius*2.5,o*1.2);Math.abs(this.camera.far-l)>1&&(this.camera.far=l,this.camera.updateProjectionMatrix())}groundYUnder(e,t){const i=this.terrainMesh;return i?i.position.y+this.groundYOfPatch(i,e-i.position.x,t-i.position.z):cu}groundYOfPatch(e,t,i){const n=e.geometry.getAttribute("position"),s=Math.round(Math.sqrt(n.count));if(s<2||s*s!==n.count)return cu;const r=n.getX(0),o=n.getX(s-1),l=n.getZ(0),c=n.getZ((s-1)*s),h=Za((t-r)/(o-r)*(s-1),0,s-1),d=Za((i-l)/(c-l)*(s-1),0,s-1);if(!Number.isFinite(h)||!Number.isFinite(d))return cu;const u=Math.min(Math.floor(h),s-2),p=Math.min(Math.floor(d),s-2),f=h-u,v=d-p,m=(w,E)=>n.getY(w*s+E),g=m(p,u),x=m(p,u+1),y=m(p+1,u),b=m(p+1,u+1);return f+v<=1?g+(x-g)*f+(y-g)*v:b+(y-b)*(1-f)+(x-b)*(1-v)}groundUnderFootprint(e,t,i,n,s=0){return ui.groundUnderFootprint(e,t,i,n,(r,o)=>this.groundYUnder(r,o),s)}lookYawDeg=0;lookPitchDeg=0;poseCameraY=1.6;setGait(e){this.gaitOffset=e}gaitOffset={eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0};setLookOffset(e,t){this.lookYawDeg=e,this.lookPitchDeg=t}indoorLookYawDeg=0;indoorLookPitchDeg=0;setIndoorLook(e,t){this.indoorLookYawDeg=e,this.indoorLookPitchDeg=t}medium;setMedium(e,t,i,n){const s=e&&t!==void 0&&t>0&&i!==void 0&&i>0?{kind:e.kind,iso:n??e.iso,autoExposure:e.autoExposure===!0,exposureSeconds:t,fNumber:i}:void 0;JSON.stringify(s)!==JSON.stringify(this.medium)&&(this.medium=s,this.render())}applyMedium(){const e=this.medium,t=Math.max(this.relativeScale,1e-30);if(!e){ji.uMedium.value=0;return}ji.uMedium.value=e.kind==="negative"?1:e.kind==="slide"?2:3;const i=e.autoExposure?this.scatteredSky?.sceneAdaptation:void 0;ji.uMediumExposure.value=i!==void 0&&i>0?1/(t*i):.065*e.exposureSeconds*e.iso/(e.fNumber*e.fNumber*t)}grain;lampBloomRadiusRad(){if(!this.grain)return;const e=this.camera.fov*Math.PI/180,t=this.grain.frameHeightMm/(2*Math.tan(e/2)),i=Math.max(1,this.renderer.getDrawingBufferSize(this.bloomSize).y);return Math.max(this.grain.detailUm*.001/t,e/i)}bloomSize=new ye;updateDecorLitState(e,t=0){const i=this.lampBloomRadiusRad();for(const n of this.decorObjects){const s=this.decorGroups.get(n.id);if(s&&n.lights?.length&&ui.setLights(s,n.lights,e,s.position.distanceTo(this.camera.position),t,this.relativeScale,i),n.kind!=="streetlight"&&n.kind!=="vehicle")continue;const r=this.decorGroups.get(n.id);if(!r)continue;const o=nh(n,e);ui.setLit(r,n.kind,o,this.relativeScale);const l=this.streetlightLights.get(n.id);l&&(l.visible=o)}}addStreetlightLight(e,t){const i=ui.bodyOf(t),n=i.children.find(r=>r.userData.emissive);if(!n)return;const s=new P1(16764006,Hg*this.relativeScale,zA);s.position.copy(n.position).multiply(i.scale),s.castShadow=!0,s.shadow.mapSize.set(512,512),s.shadow.bias=-.002,t.add(s),this.streetlightLights.set(e,s)}setAstronomy(e){(!this.lastAstronomy||Math.abs(this.lastAstronomy.sun.altitudeDeg-e.sun.altitudeDeg)>2)&&(this.surroundings=void 0,this.scatteredSky?.resetSurroundings()),this.lastAstronomy=e,this.scatteredSky?.update(this.scatteredSkyState(e)),this.skyHoldUntilMs===void 0&&this.startHold(),this.pumpScatteredSky(),this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround();const t=this.magnitudeLimitOf(e);this.buildStars(e.stars,t),this.placeBodies(e,t),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon),this.applySkyColours(e),this.syncAnimationLoop(),this.render()}static relativeSkyOf(e,t){const i=r=>{const o=Gn.luminanceOf(r);if(!(o>0))return[0,0,0];const l=Ae.relativeOfResponse(o)/o;return[r[0]*l,r[1]*l,r[2]*l]},n=i(e.zenith),s=i(e.horizon);return{zenith:n,horizon:s,airlight:s,skyIrradiance:[n[0]*Math.PI,n[1]*Math.PI,n[2]*Math.PI],scale:t}}placeBodies(e,t){this.bodiesScale=this.relativeScale,this.setBodyMesh("sun",e.sun,Xo,new se(1,.96,.88),e.sun.magnitude),this.setMoonMesh(e.moon,t),this.buildPlanets(e.planets,t),this.buildComet(e.comet,t),this.buildNovae(e.novae??[],t)}bodiesScale=0;applySkyColours(e){this.skyColoursStale=!1,this.bodiesScale!==this.relativeScale&&(this.placeBodies(e,this.magnitudeLimitOf(e)),this.applyDazzleStrength(),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon));const t=this.scatteredSky?.ambient??Wt.relativeSkyOf(Pp(e.sun.altitudeDeg),this.relativeScale),i=t,n=this.lightUnderClouds(e),s=r=>n?[0,1,2].map(o=>r[o]*n.through+n.radiance[o]*(1-n.through)):[r[0],r[1],r[2]];this.baseFogColor=[i.horizon[0],i.horizon[1],i.horizon[2]],this.airlight=s(i.airlight),this.skyMesh&&this.skyGradientMaterial&&(this.skyMesh.material=this.scatteredSky?.ready?this.scatteredSky.material:this.skyGradientMaterial),this.updateCelestialLight(e,t),this.updateCloudLighting(e.sun,this.baseFogColor),ot.setEye(this.scatteredSky?.rodShare??0,this.relativeScale),this.updateTwinkle(this.twinkleSeconds);for(const r of this.streetlightLights.values())r.intensity=Hg*this.relativeScale;this.buildSkyGlow(e,i.zenith),this.applyAir(),this.buildLunarDiffraction(e.moon)}reflections;sceneVersion=0;reflectionTimer;buildLunarDiffraction(e){const t=this.scatteredSky?.ready?this.scatteredSky:void 0;if(e.altitudeDeg<-.5||!t){this.lunarDiffraction?.show(void 0),this.lunarDiffractionKey="";return}const i=this.weather.relativeHumidity,n=(i===void 0?Ne.DEFAULT_AEROSOL_OPTICAL_DEPTH:Us.opticalDepth(i))*Ne.aerosolDensity(this.siteElevationM+this.observerElevationM),s=this.cloudTransmission(e),r=-Math.log(Math.max(this.cloudTransmission(e,!0),1e-4)),o=Mt.aureoleScale(n,Mt.airMass(e.altitudeDeg))*s,l=Mt.coronaScale(r),c=Mt.illuminanceOf(e.magnitude),h=[e.altitudeDeg.toFixed(1),e.azimuthDeg.toFixed(1),e.magnitude.toFixed(2),o.toExponential(2),l.toExponential(2),Math.log10(Math.max(t.adaptation,1e-12)).toFixed(2)].join(":");if(h===this.lunarDiffractionKey)return;this.lunarDiffractionKey=h,this.lunarDiffraction||(this.lunarDiffraction=new Zf,this.celestialGroup.add(this.lunarDiffraction.object));const d=Mt.profile(Mt.HAZE_COARSE_MODE),u=Mt.profile(Mt.CLOUD_DROPLETS),p=sg(e.altitudeDeg),f=new Float32Array(Mt.STEPS*3);let v=0;for(let y=0;y<Mt.STEPS;y++){const b=y*3,w=[0,1,2].map(T=>c*p[T]*(o*d[b+T]+l*u[b+T])),E=Gn.shown(w,Gn.luminanceOf(w),(T,M)=>t.relativeOfLuminance(T,M));f.set(E,b),v=Math.max(v,Gn.response(E))}const{x:m,y:g,z:x}=bi(e.altitudeDeg,e.azimuthDeg,1);this.lunarDiffraction.show(v>1/200?{x:m,y:g,z:x}:void 0,f)}applyAir(){this.scene.fog instanceof ra||(this.scene.fog=new ra),this.scene.fog.setAir(this.air,this.siteElevationM,this.airlight)}airlight=[0,0,0];transmittanceTo(e,t,i){const n=this.camera.position;return this.air.transmittance(this.siteElevationM+n.y,this.siteElevationM+t,Math.hypot(e-n.x,t-n.y,i-n.z))}setSiteElevation(e){Math.abs(e-this.siteElevationM)<1||(this.siteElevationM=e,this.applyAir(),this.lastAstronomy&&this.scatteredSky?.update(this.scatteredSkyState(this.lastAstronomy)))}scatteredSkyState(e){return{altitudeM:this.siteElevationM+this.observerElevationM+1.6,lightPollution:this.lightPollution,sun:{altitudeDeg:e.sun.altitudeDeg,azimuthDeg:e.sun.azimuthDeg,magnitude:e.sun.magnitude},moon:{altitudeDeg:e.moon.altitudeDeg,azimuthDeg:e.moon.azimuthDeg,magnitude:e.moon.magnitude,phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction)}}}pumpScatteredSky(){const e=this.scatteredSky;!e||e.tables.ready||this.scatteredSkyFrameId!==null||this.contextReleased||(this.scatteredSkyFrameId=requestAnimationFrame(()=>{this.scatteredSkyFrameId=null,!this.contextReleased&&(e.advance(this.skyHoldUntilMs?VA:GA),this.pumpScatteredSky())}))}cancelScatteredSkyPump(){this.scatteredSkyFrameId!==null&&(cancelAnimationFrame(this.scatteredSkyFrameId),this.scatteredSkyFrameId=null)}setSatellites(e){if(e.length===0&&!this.satelliteField?.count)return;this.satelliteField||(this.satelliteField=new nA(iu),this.celestialGroup.add(this.satelliteField.object));const t=this.lastAstronomy?this.magnitudeLimitOf(this.lastAstronomy):Kr(-90,this.instrumentMagnitudeGain);this.satelliteField.set(e,t,i=>this.cloudTransmission(i),(i,n)=>this.arrivingIlluminance(i,n,1)),this.render()}pickSatelliteAt(e,t){if(!this.satelliteField?.count)return;this.aimAtScreenPoint(this.raycaster,e,t);const i=Math.cos(Bg*this.camera.fov*Math.PI/180),n=this.satelliteField.nearest(this.raycaster.ray.direction,i);return n&&this.groundHides(e,t)?void 0:n}updateCelestialLight(e,t){const i=this.relativeScale,n=e.sun.altitudeDeg>=Rc,s=!n&&e.moon.altitudeDeg>=Rc,r=n?e.sun:e.moon,o=[0,0,0],l=Math.max(0,Math.sin(Math.max(r.altitudeDeg,0)*sn)),c=n||s?this.cloudTransmission(r):1,h=-Math.log(Math.max(c,1e-6)),d=Math.max(0,1/(1+.1125*h)-c),u=[0,0,0];if(n||s){const x=Mt.illuminanceOf(r.magnitude)*i,y=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,r.altitudeDeg);for(let T=0;T<3;T++)o[T]=x*y[T]*c,u[T]=x*y[T]*d*l,this.cloudBeam[T]=x*y[T];const{x:b,y:w,z:E}=bi(r.altitudeDeg,r.azimuthDeg,1);this.cloudBeamDirection.set(b,w,E)}else this.cloudBeam.fill(0);const p=this.surroundings;if(p)this.skyLight.color.setRGB(p.up[0]*i,p.up[1]*i,p.up[2]*i),this.skyLight.groundColor.setRGB(p.down[0]*i,p.down[1]*i,p.down[2]*i);else{const x=this.skyCloudTransmission(),y=t.skyIrradiance,b=[0,1,2].map(E=>y[E]*x+u[E]);this.skyLight.color.setRGB(b[0],b[1],b[2]);const w=We.GROUND_ALBEDO;this.skyLight.groundColor.setRGB(w*(b[0]+o[0]*l),w*(b[1]+o[1]*l),w*(b[2]+o[2]*l))}if(this.skyLight.intensity=1,!n&&!s){this.celestialLight.intensity=0;return}const{x:f,y:v,z:m}=bi(r.altitudeDeg,r.azimuthDeg,1);this.lightDirection.set(f,v,m),this.placeShadowFrustum();const g=Math.max(o[0],o[1],o[2],1e-30);this.celestialLight.color.setRGB(o[0]/g,o[1]/g,o[2]/g),this.celestialLight.intensity=g,this.celestialLight.castShadow=this.decorGroups.size>0||this.bodySystem.any}lightDirection=new R(0,1,0);placeShadowFrustum(){const e=this.lightDirection,t=this.celestialLight.shadow.camera,i=(t.right-t.left)/this.celestialLight.shadow.mapSize.x,n=this.shadowAxisX.crossVectors(this.shadowUp,e);n.lengthSq()<1e-8&&n.set(1,0,0),n.normalize();const s=this.shadowAxisY.crossVectors(e,n).normalize(),r=this.shadowWorld.set(this.bodyOrigin.x,0,this.bodyOrigin.z),o=c=>{const h=r.dot(c)/i;return(h-Math.round(h))*i},l=this.celestialLightTarget.position.set(0,0,0).addScaledVector(n,o(n)).addScaledVector(s,o(s));this.celestialLightTarget.updateMatrixWorld(),this.celestialLight.position.copy(l).addScaledVector(e,this.shadowLightDistanceM)}fitShadowToBodies(){const e=this.bodySystem.reachFrom(this.camera.position),t=Math.min(LA,Math.max(nr,Math.ceil(e*1.25/20)*20));if(t===this.shadowHalfExtentM)return;this.shadowHalfExtentM=t;const i=this.celestialLight.shadow,n=i.camera;n.left=-t,n.right=t,n.top=t,n.bottom=-t;const s=Math.max(Wo,t*(Wo/nr));this.shadowLightDistanceM=Math.max(pn,s+10),n.near=this.shadowLightDistanceM-s,n.far=this.shadowLightDistanceM+s,n.updateProjectionMatrix(),i.bias=-.03/(2*s);const r=t>2*nr?2048:1024;i.mapSize.x!==r&&(i.mapSize.set(r,r),i.map?.dispose(),i.map=null),this.placeShadowFrustum()}shadowHalfExtentM=nr;shadowLightDistanceM=pn;shadowUp=new R(0,1,0);shadowAxisX=new R;shadowAxisY=new R;shadowWorld=new R;skyCloudTransmission(){let e=this.cloudTransmission({altitudeDeg:90,azimuthDeg:0}),t=1;for(const i of[20,45,70]){const n=i*sn,s=Math.sin(n)*Math.cos(n)*2;for(let r=0;r<360;r+=60)e+=s*this.cloudTransmission({altitudeDeg:i,azimuthDeg:r}),t+=s}return e/t}cloudBeam=[0,0,0];cloudBeamDirection=new R(0,1,0);get relativeScale(){return this.scatteredSky?.relativeScale??1/Ae.semiSaturation(Ae.DAYLIGHT_ANCHOR_CD_M2)}setMeteorShower(e,t,i){this.meteorSystem||(this.meteorSystem=new Ai,this.celestialGroup.add(this.meteorSystem.object)),this.meteorSystem.setShower(e,t,i)}get meteorSchedule(){return this.meteorSystem?.schedule??[]}meteorMidpoint(e){return this.meteorSystem?.midpointOf(e)}updateMeteors(e){this.meteorSystem?.update(e)}setInstrument(e){this.projectionKind=e.projection,this.grain=e.detailUm!==void 0&&e.frame?{detailUm:e.detailUm,frameHeightMm:e.frame.heightMm}:void 0;const t=St.starPointsOf(e),i=St.flareArtifactsOf(e);t===this.starPoints&&i===this.lensFlareArtifactIntensity||(this.starPoints=t,this.lensFlareArtifactIntensity=i,this.lensFlare&&(this.lensFlare.uniforms.uStarPoints.value=t,this.lensFlare.uniforms.uFlareIntensity.value=i),this.render())}magnitudeLimitOf(e){const t=le.starLossMagnitudes(e.sun.altitudeDeg,{phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction),altitudeDeg:e.moon.altitudeDeg},this.lightPollution);return Kr(e.sun.altitudeDeg,this.instrumentMagnitudeGain,t)}setLightPollution(e){e!==this.lightPollution&&(this.lightPollution=e,this.lastAstronomy&&this.scatteredSky?.update(this.scatteredSkyState(this.lastAstronomy)))}setInstrumentGain(e,t=!1){this.instrumentMagnitudeGain=Number.isFinite(e)?e:0,this.scatteredSky?.setInstrument(t,this.instrumentMagnitudeGain)}render(){this.restatingExposure||(this.sceneVersion++,this.frameDirty=!0,this.resolution.noteChange(performance.now()),!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()})))}drawIfDirty(){if(!(!this.frameDirty||this.contextReleased)){if(this.skyColoursStale&&this.lastAstronomy&&this.applySkyColours(this.lastAstronomy),this.compileBeforeNextDraw){this.compileBeforeNextDraw=!1;const e=this.compileOffThread().then(()=>{this.compiling===e&&(this.compiling=void 0,this.render())});this.compiling=e;return}if(!this.compiling){if(this.skyHoldUntilMs){if(!this.sceneSettled()&&performance.now()<this.skyHoldUntilMs){this.firstFrameReady()&&this.refreshReflections(!0);return}this.skyHoldUntilMs=null,this.iceHalos?.setUrgent(!1),this.onFirstFrameHold?.(!1),this.skyColoursStale&&this.lastAstronomy&&this.applySkyColours(this.lastAstronomy)}this.frameDirty=!1,this.refreshReflections(),this.resolution.beginDrawing();try{this.renderOnce(),this.drawn++}finally{this.resolution.endDrawing()}this.developingExposure&&this.startExposure()}}}drawn=0;get framesDrawn(){return this.drawn}compileBeforeNextDraw=!1;compiling;static COMPILE_WAIT_MS=1500;compileOffThread(){const e=this.renderer.compile(this.scene,this.camera);if(!this.renderer.extensions.get("KHR_parallel_shader_compile"))return Promise.resolve();const t=this.renderer.properties,i=performance.now()+Wt.COMPILE_WAIT_MS;return new Promise(n=>{const s=()=>{if(this.contextReleased)return n();for(const r of e){const o=t.get(r)?.currentProgram;(!o||o.isReady())&&e.delete(r)}e.size===0||performance.now()>i?n():setTimeout(s,10)};s()})}compileNextFrameOffThread(){this.compileBeforeNextDraw=!0}contextReleased=!1;releaseContext(){this.contextReleased||(this.contextReleased=!0,this.stopTwinkle(),this.cancelFlush(),this.cancelExposure(),this.compiling=void 0,this.cancelScatteredSkyPump(),this.renderer.forceContextLoss())}restoreContext(){this.contextReleased&&(this.contextReleased=!1,this.renderer.forceContextRestore(),this.scatteredSky?.invalidate(),this.pumpScatteredSky(),this.compileNextFrameOffThread(),this.syncAnimationLoop(),this.render())}cancelFlush(){this.flushFrameId!==null&&(cancelAnimationFrame(this.flushFrameId),this.flushFrameId=null)}frame(e){if(this.animationsRunning){const t=this.lastFrameTimeMs===void 0?0:e-this.lastFrameTimeMs;this.lastFrameTimeMs=e,this.hasAnimations()&&this.animate(e,t);const i=this.resolution.ratioFor(e,t);i!==this.renderer.getPixelRatio()&&this.applyPixelRatio(i)}this.drawIfDirty()}refreshReflections(e=!1){this.reflections??=new gn(this.renderer);const t=[...this.compassSprites,...this.lensFlare?[this.lensFlare.mesh]:[],...[...this.glareSprites.values()].map(o=>o.mesh)],i=[...this.starTiers.map(o=>o.points),...[...this.bodyMeshes.values()].filter(o=>o instanceof ks),...[this.rainSystem?.points,this.precipitationPoints,this.rainSplashSystem?.points].filter(o=>o!==void 0)],n=this.relativeScale,s=this.sceneVersion,r=this.reflections.refresh(this.renderer,this.scene,this.camera.position,this.bodySystem.reflectors,t,[...this.decorGroups.values()],s,i,o=>this.measureSurroundings(o,n,s),e?0:gn.REFRESH_MS);r===void 0||this.reflectionTimer||(this.reflectionTimer=setTimeout(()=>{this.reflectionTimer=void 0,this.frameDirty=!0,!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()}))},r))}measureSurroundings(e,t,i){const n=this.lastAstronomy;if(!n)return;this.probeIrradiance??=new hl;const s=[n.sun,n.moon].filter(r=>r.altitudeDeg>-2).map(r=>bi(r.altitudeDeg,r.azimuthDeg,1));this.probeIrradiance.measure(this.renderer,e,s,r=>{const o=p=>p.map(f=>f/t),l=this.surroundings,c={up:o(r.up),down:o(r.down)},h=(p,f)=>p.some((v,m)=>Math.abs(v-f[m])>.03*Math.max(Math.abs(v),Math.abs(f[m]),1e-12)),d=!l||h(l.up,c.up)||h(l.down,c.down);d&&(this.surroundings=c),this.eyeSettledVersion=-1;const u=this.scatteredSky?.adaptToSurroundings(Math.exp(r.logAverage)/t)??!1;if(!d&&!u){this.eyeSettledVersion=i,this.skyHoldUntilMs&&this.redrawHeld();return}this.skyColoursStale=!0,this.render()})}lightUnderClouds(e){const t=this.surroundings;if(!t)return;const i=e.sun.altitudeDeg>=Rc?e.sun:e.moon.altitudeDeg>=Rc?e.moon:void 0;if(!i)return;const n=this.relativeScale/Math.PI;return{through:this.cloudTransmission(i),radiance:[t.up[0]*n,t.up[1]*n,t.up[2]*n]}}probeIrradiance;surroundings;animate(e,t){const i=Math.min(t/1e3,hT);this.twinkleSeconds=e/1e3,this.updateTwinkle(this.twinkleSeconds),this.updatePrecipitation(e/1e3,i),this.updateRain(i),this.lensFlare&&(this.lensFlare.uniforms.uTime.value=e/1e3),this.developingExposure||(this.frameDirty=!0)}setMaxPixelRatio(e){this.resolution.maximum=e,this.renderer.getPixelRatio()>this.resolution.maximum&&this.applyPixelRatio(this.resolution.maximum)}applyPixelRatio(e){this.renderer.setPixelRatio(e),this.renderer.setSize(this.cssSize.width,this.cssSize.height,!1),this.frameDirty=!0}setExposure(e,t){const i=t?Math.max(1,Math.round(e)):1;if(i===this.exposureInstants&&(i===1||t===this.exposureInstantAt)){this.exposureInstantAt=t;return}this.exposureInstants=i,this.exposureInstantAt=t,i===1&&(this.cancelExposure(),this.exposureAccumulation?.dispose(),this.exposureAccumulation=void 0),this.render()}renderOnce(e){if(this.onMapSubjectBounds){const r=this.mapSubjectBounds();e?this.exposureSubjectBounds.push(...r):this.onMapSubjectBounds(r)}const t=this.usableEquidistantPass();this.phenomena.setCloudLayers(this.layeredClouds?.volumes??[]),this.phenomena.place(this.camera,(r,o,l)=>this.directionAtScreenPoint(r,o,l),r=>this.screenPointOf(r)),this.references.place(this.camera);const i=Math.max(this.phenomena.furthestM,this.references.furthestM)*1.2;i>this.camera.far&&(this.camera.far=i,this.camera.updateProjectionMatrix()),ji.uRodShare.value=this.scatteredSky?.rodShare??0,ji.uRelativeScale.value=this.relativeScale,this.applyMedium();const n=this.references.any||this.phenomena.any||this.compassSprites.some(r=>r.visible);ji.uHasOverlay.value=n?1:0;const s=n?r=>this.renderOverlayPasses(r):void 0;if(this.skyGlow?.setDestinationEncoded(!1),!t){this.updateLensFlarePosition();const r=this.usableDepthOfFieldPass();r?r.render(this.renderer,this.scene,this.camera,s,e):this.usableFinishPass().render(this.renderer,this.scene,this.camera,s,void 0,e);return}t.render(this.renderer,this.scene,this.camera,this.camera.fov,()=>this.updateLensFlarePosition(),s,r=>{this.lensFlare&&(this.lensFlare.mesh.visible=r?!1:this.sunVisible)},()=>this.renderDazzleOnResample(),e)}renderOverlayPasses(e=this.camera){this.renderReferencesPass(e),this.renderPhenomenaPass(e),this.renderHudPass(e)}renderHudPass(e){if(!this.compassSprites.some(n=>n.visible))return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,e.layers.set(Ng),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}renderReferencesPass(e){if(!this.references.any)return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,e.layers.set(Sb),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}setReferences(e,t){this.references.set(e.map(i=>{const n=this.documentRelative(i.src);return n===i.src?i:{...i,src:n}}),void 0,t)}referenceAspect(e){return this.references.aspectOf(e)}directionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}referenceFailedToLoad(e){return this.references.failedToLoad(this.documentRelative(e))}setReferencesShown(e){this.references.setShown(e),this.render()}setReferenceView(e,t){this.references.setView(e,t),this.render()}renderPhenomenaPass(e){if(!this.phenomena.any)return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,this.renderer.clearDepth();for(const n of this.decorObjects){const s=this.decorGroups.get(n.id);s&&Wt.markDecorDepth(s,n.kind!=="crop")}e.layers.set(eu),this.scene.overrideMaterial=this.decorDepthMaterial,this.renderer.render(this.scene,e),this.scene.overrideMaterial=null,e.layers.set(xb),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}decorDepthMaterial=new Rn({colorWrite:!1});static markDecorDepth(e,t){e.traverse(i=>{if(!(i instanceof De))return;const n=Array.isArray(i.material)?i.material[0]:i.material;t&&n.depthWrite!==!1&&n.blending!==Yi?i.layers.enable(eu):i.layers.disable(eu)})}startExposure(){this.cancelExposure(),this.exposureSubjectBounds=[];const e=this.renderer.getDrawingBufferSize(new ye),t=Math.max(1,Math.round(e.x)),i=Math.max(1,Math.round(e.y));this.exposureAccumulation||(this.exposureAccumulation=new S_(t,i)),this.exposureAccumulation.resize(t,i),this.exposureAccumulation.open(this.renderer),this.exposureInstantsDone=0,this.exposureFrameId=requestAnimationFrame(()=>this.developExposure())}developExposure(){this.exposureFrameId=null;const e=this.exposureInstantAt,t=this.exposureAccumulation;if(!e||!t)return;const i=1/this.exposureInstants;this.resolution.beginDrawing();const n=performance.now()+Wt.EXPOSURE_BUDGET_MS,s=Wt.exposureInstantsPerFrame(t.instantTarget.scene.width*t.instantTarget.scene.height);let r=0;do{this.restateAt(e,this.exposureInstantsDone);const o=this.compassSprites.map(l=>l.visible);for(const l of this.compassSprites)l.visible=!1;try{this.renderOnce(t.instantTarget)}finally{this.compassSprites.forEach((l,c)=>{l.visible=o[c]})}t.add(this.renderer,i),this.exposureInstantsDone++,r++}while(this.exposureInstantsDone<this.exposureInstants&&performance.now()<n&&r<s);this.restateAt(e,0),this.presentExposure(),this.resolution.endDrawing(),this.exposureInstantsDone<this.exposureInstants&&(this.exposureFrameId=requestAnimationFrame(()=>this.developExposure()))}presentExposure(){if(ji.uRodShare.value=this.scatteredSky?.rodShare??0,ji.uHasOverlay.value=1,ji.uRelativeScale.value=this.relativeScale,this.applyMedium(),this.exposureAccumulation?.develop(this.renderer,this.exposureInstants/this.exposureInstantsDone),this.onMapSubjectBounds?.(this.exposureSubjectBounds),!this.compassSprites.some(s=>s.visible))return;const e=new Hs,t=new Yn(-1,1,1,-1,0,2),i=Cc/(Vg*Math.tan(Wg*sn/2));for(const s of this.compassSprites){if(!s.visible)continue;const r=s.getWorldPosition(new R).sub(this.camera.position).normalize(),o=this.screenPointOf(r);if(!o)continue;const l=new Po(s.material);l.position.set(o.ndcX,o.ndcY,-1),l.scale.set(i/this.camera.aspect,i,1),e.add(l)}const n=this.renderer.autoClear;this.renderer.autoClear=!1;try{this.renderer.render(e,t)}finally{this.renderer.autoClear=n}}onMapSubjectBounds;exposureSubjectBounds=[];static MAP_SUBJECT_NEAREST_M=8;mapSubjectBounds(){const e=[],t=new Qi,i=new R,n=this.camera.getWorldDirection(new R),s=this.decorObjects.filter(o=>o.track?.length||o.kind==="aircraft"||o.kind==="vehicle").map(o=>this.decorGroups.get(o.id)),r=this.bodySystem.reflectors.map(o=>o.holder);for(const o of[...s,...r]){if(!o?.visible||(t.setFromObject(o),t.isEmpty())||t.distanceToPoint(this.camera.position)<Wt.MAP_SUBJECT_NEAREST_M)continue;let l=1/0,c=-1/0,h=1/0,d=-1/0;for(const u of[t.min.x,t.max.x])for(const p of[t.min.y,t.max.y])for(const f of[t.min.z,t.max.z]){if(i.set(u,p,f).sub(this.camera.position).normalize(),i.dot(n)<=0)continue;const v=this.screenPointOf(i);if(!v)continue;const m=(v.ndcX+1)/2,g=(1-v.ndcY)/2;l=Math.min(l,m),c=Math.max(c,m),h=Math.min(h,g),d=Math.max(d,g)}l=Math.max(l,0),c=Math.min(c,1),h=Math.max(h,0),d=Math.min(d,1),c>=l&&d>=h&&e.push({x:l,y:h,width:c-l,height:d-h})}return e}restateAt(e,t){this.restatingExposure=!0;try{e(t)}finally{this.restatingExposure=!1}}cancelExposure(){this.exposureFrameId!==null&&(cancelAnimationFrame(this.exposureFrameId),this.exposureFrameId=null)}setLensOptics(e){const t=this.lensOptics;this.lensOptics=e,(t===void 0!=(e===void 0)||t&&e&&(t.focalLengthMm!==e.focalLengthMm||t.fNumber!==e.fNumber||t.focusDistance!==e.focusDistance||t.frameHeightMm!==e.frameHeightMm))&&(e&&this.depthOfFieldPass&&this.depthOfFieldPass.setLens(e.focalLengthMm,e.fNumber,e.focusDistance,e.frameHeightMm),this.render())}usableDepthOfFieldPass(){const e=this.lensOptics;if(!e)return;const t=this.renderer.getDrawingBufferSize(new ye),i=Math.max(1,Math.round(t.x)),n=Math.max(1,Math.round(t.y));return this.depthOfFieldPass||(this.depthOfFieldPass=new dl(i,n)),this.depthOfFieldPass.resize(i,n),this.depthOfFieldPass.setLens(e.focalLengthMm,e.fNumber,e.focusDistance,e.frameHeightMm),this.depthOfFieldPass}usableFinishPass(){const e=this.renderer.getDrawingBufferSize(new ye);return this.finishPass?this.finishPass.resize(e.x,e.y):this.finishPass=new Kf(e.x,e.y),this.finishPass}usableEquidistantPass(){if(this.projectionKind!=="equidistant")return;const e=this.renderer.getDrawingBufferSize(new ye);return this.equidistantPass?this.equidistantPass.resize(e.x,e.y):this.equidistantPass=new Jt(e.x,e.y),this.equidistantPass}aimAtScreenPoint(e,t,i){const n=this.projectionKind==="equidistant"?this.equidistantPass:void 0;if(!n){e.setFromCamera(new ye(t,i),this.camera);return}const s=n.directionFor(t,i,this.camera.fov);this.camera.updateMatrixWorld(),e.ray.origin.setFromMatrixPosition(this.camera.matrixWorld),e.ray.direction.set(s.x,s.y,s.z).applyQuaternion(this.camera.quaternion).normalize(),e.camera=this.camera}pickCloudAt(e,t){return this.layeredClouds?.pickInstance(this.cloudDirectionAt(e,t))}cloudDirectionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}directionAtScreenPoint(e,t,i){return this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t),i.copy(this.ufoOcclusionRaycaster.ray.direction)}screenPointOf(e){this.camera.updateMatrixWorld();const t=this.screenPointScratch.copy(e).applyQuaternion(this.screenPointQuaternion.copy(this.camera.quaternion).invert()),i=this.renderer.getDrawingBufferSize(this.screenPointSize),n=i.x/Math.max(i.y,1);if(this.projectionKind==="equidistant"&&this.equidistantPass)return Jt.ndcFor(t,this.camera.fov,n);if(t.z>=0)return;const s=Math.tan(this.camera.fov/2*sn);return{ndcX:t.x/-t.z/(n*s),ndcY:t.y/-t.z/s}}screenPointScratch=new R;screenPointQuaternion=new Xi;screenPointSize=new ye;updateLensFlarePosition(){const e=this.lensFlare;if(!e)return;if(!this.sunVisible){e.mesh.visible=!1;return}if(this.camera.updateMatrixWorld(),this.lensFlareScratch.copy(this.sunWorldPosition).applyMatrix4(this.camera.matrixWorldInverse),this.lensFlareScratch.z>0){e.mesh.visible=!1;return}const t=this.sunVisibleFraction();if(t<=0){e.mesh.visible=!1;return}e.mesh.visible=!0,this.sunUnhiddenFraction=t,this.applyDazzleStrength();const i=this.lensFlareScratch.copy(this.sunWorldPosition).project(this.camera);e.uniforms.uLensPosition.value.set(i.x,i.y);const n=this.renderer.domElement.width,s=this.renderer.domElement.height;e.uniforms.uResolution.value.set(n,s),e.uniforms.uTanHalfFov.value=Math.tan(this.camera.fov*Math.PI/360)}renderDazzleOnResample(){const e=this.lensFlare;if(!e||!this.sunVisible)return;const t=this.sunVisibleFraction();if(t<=0)return;this.sunUnhiddenFraction=t,this.applyDazzleStrength();const i=this.screenPointOf(this.lensFlareScratch.copy(this.sunWorldPosition).sub(this.camera.position).normalize());if(!i)return;const n=this.renderer.getDrawingBufferSize(this.screenPointSize);e.uniforms.uLensPosition.value.set(i.ndcX,i.ndcY),e.uniforms.uResolution.value.set(n.x,n.y),e.uniforms.uEquidistant.value=1,e.uniforms.uHalfFovRad.value=this.camera.fov*Math.PI/360;const s=this.renderer.autoClear;this.renderer.autoClear=!1,e.mesh.visible=!0,e.mesh.layers.enable(Og),this.camera.layers.set(Og),this.renderer.render(this.scene,this.camera),this.camera.layers.set(0),e.mesh.visible=!1,e.uniforms.uEquidistant.value=0,this.renderer.autoClear=s}sunVisibleFraction(){this.raycastableDecor().length>0&&this.scene.updateMatrixWorld();let e=0;for(const t of Yg)this.isSunOccluded(t[0],t[1])||e++;return e/Yg.length}raycastableDecor(){const e=[];for(const t of this.decorObjects){if(t.kind==="crop")continue;const i=this.decorGroups.get(t.id);i&&e.push(i)}return e}isSunOccluded(e=0,t=0){const i=this.raycastableDecor();if(!this.groundMesh&&!this.terrainMesh&&i.length===0)return!1;const n=this.sunOcclusionDirectionScratch.copy(this.sunWorldPosition).sub(this.camera.position),s=n.length();if(n.normalize(),e!==0||t!==0){const o=Math.PI/180,l=this.sunOcclusionRightScratch.set(n.z,0,-n.x);l.lengthSq()<1e-9&&l.set(1,0,0),l.normalize();const c=this.sunOcclusionUpScratch.crossVectors(l,n).normalize();n.addScaledVector(l,e*o).addScaledVector(c,t*o).normalize()}if(this.groundBlocks(this.camera.position,n,s))return!0;if(i.length===0)return!1;this.sunOcclusionRaycaster.set(this.camera.position,n);const r=this.sunOcclusionRaycaster.intersectObjects(i,!0)[0];return r!==void 0&&r.distance<s}groundBlocks(e,t,i,n=0){const s=this.groundMesh;if(s&&t.y<0){const m=(s.position.y-e.y)/t.y;if(m>n&&m<i){const g=e.x+t.x*m-s.position.x,x=e.z+t.z*m-s.position.z;if(Math.hypot(g,x)<=this.groundRadius)return!0}}const r=this.terrainMesh;if(!r)return!1;const o=r.geometry.getAttribute("position"),l=Math.round(Math.sqrt(o.count));if(l<2||l*l!==o.count)return!1;const c=[r.position.x+o.getX(0),r.position.x+o.getX(l-1)],h=[r.position.z+o.getZ(0),r.position.z+o.getZ((l-1)*l)],d=Math.min(Math.abs(c[1]-c[0]),Math.abs(h[1]-h[0]))/(l-1);if(!(d>0))return!1;let u=n,p=i;for(const[m,g,x]of[[e.x,t.x,c],[e.z,t.z,h]]){const y=Math.min(x[0],x[1]),b=Math.max(x[0],x[1]);if(Math.abs(g)<1e-12){if(m<y||m>b)return!1;continue}const w=(y-m)/g,E=(b-m)/g;u=Math.max(u,Math.min(w,E)),p=Math.min(p,Math.max(w,E))}if(!(p>u))return!1;const f=Math.hypot(t.x,t.z),v=Math.min(4096,Math.max(8,Math.ceil((p-u)*f/(d/2))));for(let m=0;m<=v;m++){const g=u+(p-u)*m/v,x=e.x+t.x*g,y=e.y+t.y*g,b=e.z+t.z*g;if(y<=this.groundYUnder(x,b))return!0}return!1}setPhenomena(e,t){this.phenomena.set(e,t)}decorDistancesAt(e,t,i){this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t),this.ufoOcclusionRaycaster.near=Fg;let n,s;for(const r of this.decorObjects){if(r.kind==="crop")continue;const o=this.decorGroups.get(r.id);if(!o)continue;const l=this.ufoOcclusionRaycaster.intersectObject(o,!0)[0];l&&(r.occludesSourceIds?.includes(i)?n=n===void 0?l.distance:Math.max(n,l.distance):s=s===void 0?l.distance:Math.min(s,l.distance))}return{behindM:n,inFrontM:s}}pickBodyAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.hitAreas.entries()],n=this.raycaster.intersectObjects(i.map(([,s])=>s))[0];if(n&&!this.groundHides(e,t))return i.find(([,s])=>s===n.object)?.[0]}pickStarAt(e,t){if(this.namedStars.length===0)return;this.aimAtScreenPoint(this.raycaster,e,t);const i=this.raycaster.ray.direction,n=Math.cos(Bg*this.camera.fov*Math.PI/180);let s,r=n;for(const o of this.namedStars){const l=o.direction.dot(i);l>r&&(r=l,s={star:o.star,altitudeDeg:o.altitudeDeg})}return s&&this.groundHides(e,t)?void 0:s}groundHides(e,t){if(!this.terrainMesh&&!this.groundMesh)return!1;this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t);const i=this.ufoOcclusionRaycaster.ray;return this.groundBlocks(i.origin,i.direction,1/0,Fg)}bodyScreenBox(e){const t=this.bodySystem.boxOf(e,this.bodyBoxScratch);if(!t)return;let i=1/0,n=1/0,s=-1/0,r=-1/0;for(let o=0;o<8;o++){const l=this.bodyCornerScratch.set(o&1?t.max.x:t.min.x,o&2?t.max.y:t.min.y,o&4?t.max.z:t.min.z),c=this.screenPointOf(l.sub(this.camera.position).normalize());if(!c)return;i=Math.min(i,c.ndcX),s=Math.max(s,c.ndcX),n=Math.min(n,c.ndcY),r=Math.max(r,c.ndcY)}return{minX:i,minY:n,maxX:s,maxY:r}}bodyBoxScratch=new Qi;bodyCornerScratch=new R;pickPlacedBodyAt(e,t){return this.aimAtScreenPoint(this.raycaster,e,t),this.bodySystem.pick(this.raycaster)}pickDecorAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.decorGroups.entries()],n=this.raycaster.intersectObjects(i.map(([,r])=>r),!0)[0];if(!n)return;let s=n.object;for(;s&&!i.some(([,r])=>r===s);)s=s.parent;return i.find(([,r])=>r===s)?.[0]}dispose(){this.stopTerrainFade(),this.stopTwinkle(),this.cancelFlush(),this.phenomena.clear(),this.references.disposeAll(),this.terrainBuildToken++,this.cancelScatteredSkyPump(),this.skyMesh&&this.skyGradientMaterial&&(this.skyMesh.material=this.skyGradientMaterial),this.scatteredSky?.dispose(),this.scatteredSky=void 0,this.disposeMesh(this.skyMesh),this.disposeMesh(this.groundMesh),this.disposeMesh(this.terrainMesh),this.roadSystem.dispose(),this.disposeCloudSystem(),this.disposeCirrus(),this.disposePrecipitationPoints(),this.disposeRain(),this.disposeStarTiers(),this.meteorSystem?.dispose(),this.meteorSystem=void 0,this.iceHalos?.dispose(),this.iceHalos=void 0,this.rainbow?.dispose(),this.rainbow=void 0,this.skyGlow?.dispose(),this.skyGlow=void 0,this.depthOfFieldPass?.dispose(),this.finishPass?.dispose(),this.depthOfFieldPass=void 0,this.cancelExposure(),this.exposureAccumulation?.dispose(),this.exposureAccumulation=void 0,this.cometTail?.dispose(),this.cometTail=void 0,this.satelliteField?.dispose(),this.satelliteField=void 0,this.lightningBolt?.dispose(),this.lightningBolt=void 0;for(const e of this.bodyMeshes.values())this.disposeMesh(e);this.bodyMeshes.clear(),this.novaKeys.clear();for(const e of[...this.hitAreas.keys()])this.disposeHitArea(e);for(const e of[...this.glareSprites.keys()])this.disposeGlare(e);this.disposeCompassLabels();for(const e of this.decorGroups.values())ui.dispose(e);this.decorGroups.clear(),this.streetlightLights.clear(),this.lensFlare&&(this.lensFlare.mesh.removeFromParent(),this.lensFlare.mesh.geometry.dispose(),this.lensFlare.mesh.material.dispose(),this.lensFlare=void 0),this.renderer.dispose()}animationsRunning=!1;startTwinkle(){if(this.animationFrameId!==null||!this.needsAnimationLoop())return;this.cancelFlush();const e=t=>{this.frame(t),this.animationFrameId=requestAnimationFrame(e)};this.animationFrameId=requestAnimationFrame(e)}setAnimationsRunning(e,t=!1){const i=e&&t;if(e===this.animationsRunning&&i===this.framesDriven)return;const n=this.animationsRunning;this.animationsRunning=e,this.framesDriven=i,e&&!n&&(this.lastFrameTimeMs=void 0,this.resolution.reset());const s=!e&&this.renderer.getPixelRatio()!==this.resolution.maximum;s&&this.applyPixelRatio(this.resolution.maximum),this.syncAnimationLoop(),(!e||this.frameDirty)&&this.render(),s&&this.drawIfDirty()}stopTwinkle(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}needsAnimationLoop(){return this.animationsRunning&&!this.framesDriven&&this.hasAnimations()}hasAnimations(){return this.starTiers.length>0||this.precipitationPoints!==void 0||this.rainSystem!==void 0||this.lensFlare!==void 0&&this.sunVisible}syncAnimationLoop(){this.needsAnimationLoop()?this.startTwinkle():this.stopTwinkle()}buildSky(e){const t=this.skyMesh?.geometry??new mi(Ic,32,16),i=t.attributes.position,n=t.getAttribute("color")??new vt(new Float32Array(i.count*3),3);for(let r=0;r<i.count;r++){const{altitudeDeg:o,azimuthDeg:l}=A_(i.getX(r),i.getY(r),i.getZ(r)),c=k_(o,l,e.azimuthDeg,e.altitudeDeg);n.setXYZ(r,c[0],c[1],c[2])}if(n.needsUpdate=!0,this.skyMesh)return;t.setAttribute("color",n);const s=new Rn({vertexColors:!0,side:Zt,fog:!1,depthWrite:!1});this.skyGradientMaterial=s,this.skyMesh=new De(t,s),this.skyMesh.renderOrder=-1,this.celestialGroup.add(this.skyMesh)}buildGround(){const e=Ug(this.observerElevationM);if(this.groundMesh&&e===this.groundRadius)return;this.disposeMesh(this.groundMesh),this.groundRadius=e;const t=new If(this.groundRadius,48),i=new wo({color:new se(tu,tu,tu),fog:!0});ra.fadeRim(i,this.groundRadius*.75,this.groundRadius),this.groundMesh=new De(t,i),this.groundMesh.rotation.x=-Math.PI/2,this.groundMesh.position.y=0,this.groundMesh.receiveShadow=!0,this.scene.add(this.groundMesh),this.applyGroundDepthWrite()}applyGroundDepthWrite(){const e=this.groundMesh?.material;if(!e)return;const t=this.terrainMesh===void 0;e.depthWrite!==t&&(e.depthWrite=t,e.needsUpdate=!0)}cloudTransmission(e,t=!1){const i=`${e.altitudeDeg.toFixed(1)}:${e.azimuthDeg.toFixed(1)}:${Math.round(this.cloudOffsetM.x/10)}:${Math.round(this.cloudOffsetM.z/10)}:${Math.round(this.observerElevationM)}:`+Object.entries(this.cloudLayerOffsetsM).map(([r,o])=>`${r}=${Math.round(o.x/10)},${Math.round(o.z/10)}`).join(";")+(t?":water":""),n=this.cloudTransmissionMemo.get(i);if(n!==void 0)return n;this.cloudTransmissionMemo.size>256&&this.cloudTransmissionMemo.clear();const s=this.computeCloudTransmission(e,t);return this.cloudTransmissionMemo.set(i,s),s}cloudTransmissionMemo=new Map;computeCloudTransmission(e,t=!1){const i=bi(e.altitudeDeg,e.azimuthDeg,1);if(this.layeredClouds)return this.layeredClouds.transmissionAt(i,t);let n=1;const s=this.lowerCloudCover();s>0&&(n*=1-wt.alphaAt(i,Math.abs(this.cloudLayerOffset()),s,this.cloudFieldOffset)*UA);const r=t?0:this.weather.highCloudCover??0;return r>0&&(n*=1-wt.iceAlphaAt(i,Dc,r,this.cloudFieldOffset)*BA),Math.max(0,Math.min(1,n))}setBodyMesh(e,t,i,n,s){if(t.altitudeDeg<tf){this.disposeMesh(this.bodyMeshes.get(e)),this.bodyMeshes.delete(e),this.disposeHitArea(e),this.disposeGlare(e),e==="sun"&&(this.sunVisible=!1);return}const{x:r,y:o,z:l}=bi(t.altitudeDeg,t.azimuthDeg,pn),c=sg(t.altitudeDeg),h=this.cloudTransmission(t),d=this.arrivingIlluminance(t,s,h),u=Wt.unitLuminance(n);let p=this.bodyMeshes.get(e);if(e==="sun"){const f=Math.PI*(i/pn)**2,v=[0,1,2].map(m=>Math.min(Wt.BRIGHTEST_RELATIVE,u[m]*d[m]/f));p instanceof De||(this.disposeMesh(p),p=new De(new mi(1,16,16),new Rn({fog:!1})),this.celestialGroup.add(p),this.bodyMeshes.set(e,p)),p.material.color.setRGB(v[0],v[1],v[2]),p.scale.setScalar(i)}else{if(!(p instanceof ks)){this.disposeMesh(p);const m=new dt;m.setAttribute("position",new nt(new Float32Array(3),3)),m.setAttribute("color",new nt(new Float32Array(3),3)),p=new ks(m,ot.material(cl[cl.length-1].size,!1)),ot.track(p),this.celestialGroup.add(p),this.bodyMeshes.set(e,p)}const f=p.geometry.getAttribute("color"),v=e.startsWith("comet:")?this.pointToPixel():1;f.setXYZ(0,u[0]*d[0]*v,u[1]*d[1]*v,u[2]*d[2]*v),f.needsUpdate=!0}if(p.position.set(r,o,l),this.setHitArea(e,r,o,l,i),e==="sun"&&(this.sunArriving=d),e.startsWith("comet:")?this.disposeGlare(e):e!=="sun"&&this.setGlare(e,r,o,l,d,i/pn*(180/Math.PI),Wt.POINT_VEIL_HOLD_DEG),e==="sun"){this.sunVisible=!0;const f=this.celestialGroup.scale.x;this.sunWorldPosition.set(r*f,o*f+this.celestialGroup.position.y,l*f),this.lensFlare||(this.lensFlare=iA(),this.lensFlare.uniforms.uOpacity.value=$A,this.lensFlare.uniforms.uFlareIntensity.value=this.lensFlareArtifactIntensity,this.lensFlare.uniforms.uStarPoints.value=this.starPoints,this.scene.add(this.lensFlare.mesh)),this.applyLensFlareTint(c)}}setMoonMesh(e,t){const i="moon";if(e.magnitude>t||e.altitudeDeg<tf){this.disposeMesh(this.bodyMeshes.get(i)),this.bodyMeshes.delete(i),this.disposeHitArea(i),this.disposeGlare(i);return}const{x:n,y:s,z:r}=bi(e.altitudeDeg,e.azimuthDeg,pn),o=this.cloudTransmission(e),l=this.arrivingIlluminance(e,e.magnitude,o),c=Math.max(Za(e.phase.illuminatedFraction,0,1),.01)*Math.PI*(Xo/pn)**2,h=new se(...l.map(p=>Math.min(Wt.BRIGHTEST_RELATIVE,p/c))),d=`${e.phase.phaseFraction<.5}:${Math.round(Za(e.phase.illuminatedFraction,0,1)*500)}`;let u=this.bodyMeshes.get(i);if(u instanceof Po)u.userData.phaseKey!==d&&(u.material.map?.dispose(),u.material.map=cv(e.phase),u.userData.phaseKey=d),u.material.color.copy(h);else{this.disposeMesh(u),u=new Po(new Xc({map:cv(e.phase),color:h,fog:!1})),u.userData.phaseKey=d;const p=Xo*2;u.scale.set(p,p,1),this.celestialGroup.add(u),this.bodyMeshes.set(i,u)}u.position.set(n,s,r),this.setHitArea(i,n,s,r,Xo),this.setGlare(i,n,s,r,l,Xo/pn*(180/Math.PI))}bentLight(e,t,i=NA){const[n,s,r]=this.arrivingIlluminance(e,t,1),o=Math.max(s,1e-30);return{scale:s/i,tint:[n/o,1,r/o]}}static litBySky(e,t){return Wt.litScratch.setRGB(e.r*t[0],e.g*t[1],e.b*t[2])}static litScratch=new se;pointToPixel(){const e=Math.max(1,this.renderer.getDrawingBufferSize(this.pointScratch).y),t=(this.camera.fov*Math.PI/180/e)**2;return Math.min(1,ot.CONE_SOLID_ANGLE/t)}pointScratch=new ye;static POINT_VEIL_HOLD_DEG=.25;static BRIGHTEST_RELATIVE=6e4;arrivingIlluminance(e,t,i){const n=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,e.altitudeDeg),s=Mt.illuminanceOf(t)*i*this.relativeScale;return[n[0]*s,n[1]*s,n[2]*s]}static unitLuminance(e){const t=Gn.luminanceOf([e.r,e.g,e.b]);return t>0?[e.r/t,e.g/t,e.b/t]:[1,1,1]}setHitArea(e,t,i,n,s){let r=this.hitAreas.get(e);if(!r){const l=new Xc({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1});r=new Po(l),this.celestialGroup.add(r),this.hitAreas.set(e,r)}r.position.set(t,i,n);const o=s*OA;r.scale.set(o,o,1)}disposeHitArea(e){const t=this.hitAreas.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.hitAreas.delete(e))}setGlare(e,t,i,n,s,r,o=0){if(Gi.radiusDeg(Gi.K*Math.max(...s))<=Math.max(r,o)){this.disposeGlare(e);return}let l=this.glareSprites.get(e);l||(l=new Gi(`veil:${e}`),this.celestialGroup.add(l.mesh),this.glareSprites.set(e,l)),l.shine({x:t,y:i,z:n},s,r,o)}disposeGlare(e){const t=this.glareSprites.get(e);t&&(t.mesh.removeFromParent(),t.dispose(),this.glareSprites.delete(e))}buildComet(e,t){const i=e&&`comet:${e.id}`;if(!e||!i||e.magnitude>t){this.disposeComet();return}this.cometKey&&this.cometKey!==i&&this.disposeComet();const n=el(e.magnitude,t),s=Vd(n);this.cometKey=i,this.setBodyMesh(i,e.position,nu*(.5+.5*n),new se(s,s,s),e.magnitude),this.cometTail||(this.cometTail=new mn(pn),this.celestialGroup.add(this.cometTail.object)),this.cometTail.set(e.position,e.tailEnd,n)}buildIceHalos(e,t){this.iceHalos||(this.iceHalos=new Fi,this.iceHalos.setCloudOffset(this.cloudFieldOffset),this.iceHalos.onReady=()=>this.render(),this.celestialGroup.add(this.iceHalos.object)),this.iceHalos.setUrgent(!!this.skyHoldUntilMs);const i=this.highCloudCover(),n=this.layeredClouds?.cirrusMask;if(this.iceHalos.setCloudOffset(n?.offset??this.cloudFieldOffset),this.layeredClouds?.hostHalo(this.iceHalos),i===void 0){this.iceHalos.update({x:0,y:1,z:0},-1,0,[1,1,1],{cover:0,layerHeight:Dc},0);return}const s=this.lowerCloudCover(),r=lt.deckLitUntilDeg(lt.DECK_HEIGHT_M),o=e.altitudeDeg>-r?e:t,l=this.bentLight(o,o===e?zt.SUN_MAGNITUDE:t.magnitude),c=lt.strength(i,s,o.altitudeDeg,r)*l.scale,{x:h,y:d,z:u}=bi(o.altitudeDeg,o.azimuthDeg,1);this.iceHalos.update({x:h,y:d,z:u},o.altitudeDeg,c,l.tint,{cover:n?.cover??i,layerHeight:n?.layerHeight??Dc},n?.iceCrystalAlignment??this.weather.iceCrystalAlignment??.65)}buildRainbow(e,t){this.rainbow||(this.rainbow=new $r,this.celestialGroup.add(this.rainbow.object));const i=this.weather.precipitationType==="rain"?this.weather.precipitationIntensity*(this.weather.precipitationAmount??1):0,n=e.altitudeDeg>0,s=n?e:t,r=this.lowerCloudCover(),o=this.bentLight(s,n?zt.SUN_MAGNITUDE:t.magnitude,FA),l=zi.strength(i,r,s.altitudeDeg)*o.scale;if(l<=0){this.rainbow.update({x:0,y:1,z:0},0,[1,1,1],!0);return}const{x:c,y:h,z:d}=bi(s.altitudeDeg,s.azimuthDeg,1);this.rainbow.update({x:c,y:h,z:d},l,o.tint,n)}buildSkyGlow(e,t){if(!e.frame){this.skyGlow?.hide();return}this.skyGlow||(this.skyGlow=new lr,this.skyGlow.onReady=()=>this.render(),this.celestialGroup.add(this.skyGlow.object));const{date:i,observer:n}=e.frame,s=bn.galactic(i,n),r=Wt.directionOf(s.centre),o=Wt.directionOf(s.rotation),l=Wt.directionOf(s.pole),c=new Xe().set(r.x,r.y,r.z,o.x,o.y,o.z,l.x,l.y,l.z),h=Wt.directionOf(bn.eclipticPole(i,n)),d=Wt.directionOf(e.sun),u=d.clone().addScaledVector(h,-d.dot(h)).normalize();this.skyGlow.update({galactic:c,eclipticPole:h,sunLongitude:u,sunDirection:d,sunAltitudeDeg:e.sun.altitudeDeg,moon:{direction:Wt.directionOf(e.moon),altitudeDeg:e.moon.altitudeDeg,phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction)},lightPollution:this.lightPollution,skyColor:new R(t[0],t[1],t[2])})}static directionOf(e){const{x:t,y:i,z:n}=bi(e.altitudeDeg,e.azimuthDeg,1);return new R(t,i,n).normalize()}buildNovae(e,t){const i=new Set;for(const n of e){if(n.magnitude>t)continue;const s=`nova:${n.id}`;i.add(s);const r=el(n.magnitude,t),o=Vd(r);this.setBodyMesh(s,n.position,nu*(.5+.5*r),new se(o,o,o),n.magnitude)}for(const n of this.novaKeys)i.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n));this.novaKeys=i}disposeComet(){this.cometTail?.set(void 0,void 0,0),this.cometKey&&(this.disposeMesh(this.bodyMeshes.get(this.cometKey)),this.bodyMeshes.delete(this.cometKey),this.disposeHitArea(this.cometKey),this.disposeGlare(this.cometKey),this.cometKey=void 0)}buildPlanets(e,t){const i=new Set(["sun","moon",...this.cometKey?[this.cometKey]:[],...this.novaKeys]);for(const n of e){if(n.magnitude>t)continue;i.add(n.body);const s=el(n.magnitude,t),r=Vd(s),o=fT[n.body]??new se(1,1,1),l=new se(o.r*r,o.g*r,o.b*r),c=nu*(.5+.5*s);this.setBodyMesh(n.body,n.position,c,l,n.magnitude)}for(const n of[...this.bodyMeshes.keys()])i.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n))}buildStars(e,t){if(this.namedStars=[],!e||e.catalog.count===0){this.disposeStarTiers(),this.syncAnimationLoop();return}const{catalog:i,date:n,observer:s}=e,r=new zn(n,s),o=pu(1337),l=[],c=this.siteElevationM+this.observerElevationM;for(let h=0;h<i.count;h++){const d=i.mag[h];if(d>t)break;const{altitudeDeg:u,azimuthDeg:p}=r.position(i.ra[h],i.dec[h]);if(u<zg)continue;const{x:f,y:v,z:m}=bi(u,p,iu);l.push({x:f,y:v,z:m,brightness:el(d,t),illuminance:this.air.transmittanceFromSpace(c,u).map(g=>g*Mt.illuminanceOf(d)),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}for(const h of O5){if(h.mag>t)continue;const{altitudeDeg:d,azimuthDeg:u}=r.position(h.raHours,h.decDeg);if(d<zg)continue;const{x:p,y:f,z:v}=bi(d,u,iu);this.namedStars.push({star:h,direction:new R(p,f,v).normalize(),altitudeDeg:d})}this.starTiers=cl.map((h,d)=>{const u=l.filter(w=>Y1(w.brightness)===d),p=new Float32Array(u.length*3),f=new Float32Array(u.length),v=new Float32Array(u.length*3),m=new Float32Array(u.length),g=new Float32Array(u.length);u.forEach((w,E)=>{p[E*3]=w.x,p[E*3+1]=w.y,p[E*3+2]=w.z,f[E]=w.brightness,v.set(w.illuminance,E*3),m[E]=w.phase,g[E]=w.speedFactor});const x=new dt;x.setAttribute("position",new nt(p,3)),x.setAttribute("seed",new nt(m.map(w=>w/(2*Math.PI)),1));const y=new nt(new Float32Array(u.length*3),3);x.setAttribute("color",y);let b=this.starTiers[d]?.points;return b?(b.geometry.dispose(),b.geometry=x):(b=new ks(x,ot.material(h.size)),ot.track(b),this.celestialGroup.add(b)),{points:b,colorAttribute:y,brightness:f,illuminance:v,phase:m,speedFactor:g}}),this.starColoursRelative=Number.NaN,this.updateTwinkle(this.twinkleSeconds),this.startTwinkle()}twinkleSeconds=0;starColoursRelative=Number.NaN;updateTwinkle(e){ot.setTime(e);const t=this.relativeScale;if(t!==this.starColoursRelative){this.starColoursRelative=t;for(const i of this.starTiers){const n=i.colorAttribute.array;for(let s=0;s<i.brightness.length;s++)n[s*3]=i.illuminance[s*3]*t,n[s*3+1]=i.illuminance[s*3+1]*t,n[s*3+2]=i.illuminance[s*3+2]*t;i.colorAttribute.needsUpdate=!0}}}buildClouds(){if(this.cloudRendering==="volume"||this.weather.cloudLayers!==void 0){this.layeredClouds||(this.disposeCloudSystem(),this.layeredClouds=new so(this.celestialGroup,su,this.cloudRendering)),this.disposeCirrus(),this.layeredClouds.hostHalo(this.iceHalos),this.layeredClouds.update(this.weather,this.observerElevationM+1.6),this.layeredClouds.setOffsets(this.cloudOffsetM,this.cloudLayerOffsetsM),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}const e=this.lowerCloudCover();if(e<=0){this.disposeCloudSystem();return}const t=this.weather.cloudDarkness,i=new se(Pr[0]+(ou[0]-Pr[0])*t,Pr[1]+(ou[1]-Pr[1])*t,Pr[2]+(ou[2]-Pr[2])*t);if(this.cloudMesh&&this.cloudUniforms){this.cloudUniforms.coverage.value=e,this.cloudUniforms.baseColor.value.copy(i),this.syncCloudLayer(),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}this.disposeCloudSystem();const{material:n,uniforms:s}=Tp(i,e,Math.abs(this.cloudLayerOffset()));this.cloudMaterial=n,this.cloudUniforms=s,s.fieldOffset.value.copy(this.cloudFieldOffset),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);const r=Ip(su);this.cloudMesh=new De(r,n),this.faceCloudDeck(this.cloudMesh,this.cloudLayerOffset()),this.celestialGroup.add(this.cloudMesh)}lowerCloudCover(){return this.weather.cloudLayers?1-this.weather.cloudLayers.filter(e=>e.type!=="cirrus").reduce((e,t)=>e*(1-t.coverage),1):this.weather.lowerCloudCover??this.weather.cloudCover}highCloudCover(){return this.weather.cloudLayers?1-this.weather.cloudLayers.filter(e=>e.type==="cirrus").reduce((e,t)=>e*(1-t.coverage),1):this.weather.highCloudCover}buildCirrus(){const e=this.weather.highCloudCover??0;if(this.layeredClouds||e<=0){this.disposeCirrus();return}if(this.cirrusMesh&&this.cirrusUniforms){this.cirrusUniforms.coverage.value=e,this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}const{material:t,uniforms:i}=Tp(new se(...Pr),e,Dc,1);this.cirrusMaterial=t,this.cirrusUniforms=i,i.fieldOffset.value.copy(this.cloudFieldOffset),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.cirrusMesh=new De(Ip(su*1.04),t),this.cirrusMesh.renderOrder=qg-1,this.celestialGroup.add(this.cirrusMesh)}disposeCirrus(){this.cirrusMesh&&(this.cirrusMesh.removeFromParent(),this.cirrusMesh.geometry.dispose()),this.cirrusMaterial?.dispose(),this.cirrusMesh=void 0,this.cirrusMaterial=void 0,this.cirrusUniforms=void 0}syncCloudLayer(){this.layeredClouds?.update(this.weather,this.observerElevationM+1.6);const e=this.cloudLayerOffset();this.cloudUniforms&&(this.cloudUniforms.layerHeight.value=Math.abs(e)),this.cloudMesh&&this.faceCloudDeck(this.cloudMesh,e)}faceCloudDeck(e,t){const i=t<0;e.scale.y=i?-1:1,e.renderOrder=i?so.deckOrder(!0,0,1):qg}cloudLayerOffset(){const t=((this.weather.cloudBaseM??1e3)-this.observerElevationM)*ru;return Math.sign(t||1)*Math.max(Math.abs(t),ZA)}updateCloudLighting(e,t){const i=[this.cloudUniforms,this.cirrusUniforms].filter(d=>d!==void 0);if(i.length===0&&!this.layeredClouds)return;const n=this.cloudBeamDirection,[s,r,o]=this.cloudBeam.map(d=>d/Math.PI),l=this.lightningLevel*uu*this.relativeScale/Math.PI,c=new se(t[0]+es.r*l,t[1]+es.g*l,t[2]+es.b*l);this.layeredClouds?.setLighting(n.clone(),new se(s,r,o),c,c);const h=Math.max(0,n.y);for(const d of i)d.sunDir.value.copy(n),d.sunColor.value.setRGB(s*h,r*h,o*h),d.ambientColor.value.copy(c)}disposeCloudSystem(){this.layeredClouds?.dispose(),this.layeredClouds=void 0,this.cloudMesh&&(this.cloudMesh.removeFromParent(),this.cloudMesh.geometry.dispose()),this.cloudMaterial?.dispose(),this.cloudMesh=void 0,this.cloudMaterial=void 0,this.cloudUniforms=void 0}disposePrecipitationPoints(){this.precipitationPoints&&(this.precipitationPoints.removeFromParent(),this.precipitationPoints.geometry.dispose(),this.precipitationPoints.material.dispose()),this.precipitationPoints=void 0,this.precipitationPositions=void 0,this.precipitationPhase=void 0,this.precipitationSpeedJitter=void 0,this.precipitationWobbleFreqJitter=void 0,this.precipitationWobbleAmpJitter=void 0,this.precipitationBounceRemaining=void 0,this.precipitationBounceHeight=void 0,this.precipitationBounceDuration=void 0,this.precipitationBounceVelX=void 0,this.precipitationBounceVelZ=void 0,this.precipitationUniforms=void 0}precipitationCount(e){return sT(e,this.weather.precipitationIntensity,this.weather.precipitationAmount??1)}updatePrecipitationDensity(){const e=[[this.rainSystem?.points,hu],[this.rainSplashSystem?.points,Lr],[this.precipitationPoints,this.weather.precipitationType==="none"||this.weather.precipitationType==="rain"?void 0:lu[this.weather.precipitationType].poolSize]];for(const[t,i]of e)t&&i!==void 0&&t.geometry.setDrawRange(0,this.precipitationCount(i));this.rainSystem&&(this.rainSystem.uniforms.uSpeedStreak.value=jg(du(this.weather.precipitationIntensity)))}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=lu[e],i=t.poolSize,n=pu(9001),s=new Float32Array(i*3),r=new Float32Array(i),o=new Float32Array(i),l=new Float32Array(i),c=new Float32Array(i),h=new Float32Array(i),d=new Float32Array(i),u=new Float32Array(i),p=new Float32Array(i),f=new Float32Array(i);for(let y=0;y<i;y++){const b=n()*Math.PI*2,w=n()*t.radiusM;s[y*3]=Math.cos(b)*w,s[y*3+1]=Ga+n()*(t.topYM-Ga),s[y*3+2]=Math.sin(b)*w,r[y]=n()*Math.PI*2,o[y]=.7+n()*.6,l[y]=.6+n()*.8,c[y]=.4+n()*1.2}const v=new dt;v.setAttribute("position",new nt(s,3)),v.setDrawRange(0,this.precipitationCount(i));const m={uSize:{value:t.size},uScale:{value:.5*this.renderer.domElement.height},uOpacity:{value:t.opacity},uColor:{value:t.color.clone()},uHazeColor:{value:new se(...this.baseFogColor)},uTexture:{value:mT(e)},uNearFocusDistance:{value:tv},uHazeDistance:{value:t.radiusM*iv}},g=new ut({uniforms:m,vertexShader:wT,fragmentShader:xT,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const x=new ks(v,g);x.renderOrder=au,this.scene.add(x),this.precipitationPoints=x,this.precipitationPositions=s,this.precipitationPhase=r,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=d,this.precipitationBounceDuration=u,this.precipitationBounceVelX=p,this.precipitationBounceVelZ=f}updatePrecipitation(e,t){if(!this.precipitationPoints||!this.precipitationPositions||!this.precipitationPhase||!this.precipitationSpeedJitter)return;this.precipitationUniforms?.uHazeColor.value.setRGB(...this.baseFogColor);const i=this.weather.precipitationType;if(i==="none"||i==="rain")return;const n=lu[i];if(this.precipitationUniforms?.uColor.value.copy(Wt.litBySky(n.color,this.baseFogColor)),t<=0)return;const s=this.weather.windDirectionDeg*sn,r=Math.sin(s)*this.weather.windSpeed*n.driftSensitivity,o=-Math.cos(s)*this.weather.windSpeed*n.driftSensitivity,l=this.precipitationPositions,c=this.precipitationPhase,h=this.precipitationSpeedJitter,d=this.precipitationWobbleFreqJitter,u=this.precipitationWobbleAmpJitter,p=this.precipitationBounceRemaining,f=this.precipitationBounceHeight,v=this.precipitationBounceDuration,m=this.precipitationBounceVelX,g=this.precipitationBounceVelZ,x=i==="snow"&&d&&u,y=i==="hail"&&p&&f&&v&&m&&g;for(let b=0,w=0;b<l.length;b+=3,w++){if(y&&p[w]>0){if(p[w]-=t,p[w]<=0){const M=Math.random()*Math.PI*2,I=Math.sqrt(Math.random())*n.radiusM;l[b]=Math.cos(M)*I,l[b+1]=n.topYM,l[b+2]=Math.sin(M)*I,p[w]=0}else{const M=1-p[w]/v[w];l[b+1]=Ga+Math.sin(Math.PI*M)*f[w],l[b]=Pc(l[b]+(r+m[w])*t,n.radiusM),l[b+2]=Pc(l[b+2]+(o+g[w])*t,n.radiusM)}continue}let E=0,T=0;if(x){const M=aT*d[w],I=oT*u[w];E=Math.sin(e*M*Math.PI*2+c[w])*I,T=Math.sin(e*M*Math.PI*2+c[w]+Math.PI/2)*I}if(l[b]+=(r+E)*t,l[b+1]-=n.fallSpeedMPerS*h[w]*t,l[b+2]+=(o+T)*t,l[b]=Pc(l[b],n.radiusM),l[b+2]=Pc(l[b+2],n.radiusM),l[b+1]<Ga){if(y){f[w]=ov*(.2+Math.random()*1.6),v[w]=lT*(.5+Math.random()),p[w]=v[w];const A=Math.random()*Math.PI*2,D=cT*(f[w]/ov);m[w]=Math.cos(A)*D,g[w]=Math.sin(A)*D,l[b+1]=Ga;continue}const M=Math.random()*Math.PI*2,I=Math.sqrt(Math.random())*n.radiusM;l[b]=Math.cos(M)*I,l[b+1]=n.topYM,l[b+2]=Math.sin(M)*I}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=du(this.weather.precipitationIntensity);this.rainSystem=J5({count:hu,radiusM:Yo,heightM:JA,bottomYM:$g,overallSpeed:QA,color:Zg.clone(),opacity:.85,visibleCount:this.precipitationCount(hu),speedStreak:jg(e),pixelSize:sv,seed:9001,nearFocusDistanceM:tv,hazeDistanceM:Yo*iv,hazeColor:new se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.syncRainSlant(),this.rainSystem.points.renderOrder=au,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=pu(4242),t=new Float32Array(Lr*3),i=new Float32Array(Lr),n=new Float32Array(Lr),s=new Float32Array(Lr);for(let h=0;h<Lr;h++){const d=e()*Math.PI*2,u=Math.sqrt(e())*Yo;t[h*3]=Math.cos(d)*u,t[h*3+1]=$g,t[h*3+2]=Math.sin(d)*u,n[h]=lv(Jg,Qg),i[h]=e()*n[h],s[h]=i[h]/n[h]}const r=new dt;r.setAttribute("position",new nt(t,3));const o=new nt(s,1);r.setAttribute("aLife",o),r.setDrawRange(0,this.precipitationCount(Lr));const l=new ut({uniforms:{uMaxSize:{value:nT},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:ev.clone()},uTexture:{value:bT()}},vertexShader:yT,fragmentShader:MT,transparent:!0,depthWrite:!1}),c=new ks(r,l);c.renderOrder=au,this.scene.add(c),this.rainSplashSystem={points:c,positions:t,life:i,duration:n,lifeAttribute:o}}disposeRain(){this.rainSystem&&(this.rainSystem.points.removeFromParent(),this.rainSystem.points.geometry.dispose(),this.rainSystem.points.material.dispose()),this.rainSystem=void 0,this.disposeRainSplashes()}disposeRainSplashes(){this.rainSplashSystem&&(this.rainSplashSystem.points.removeFromParent(),this.rainSplashSystem.points.geometry.dispose(),this.rainSplashSystem.points.material.dispose()),this.rainSplashSystem=void 0}syncRainSlant(){const e=this.rainSystem;if(!e)return;const t=this.weather.windDirectionDeg*sn;e.uniforms.uDropVelocity.value.set(Math.sin(t)*this.weather.windSpeed,-du(this.weather.precipitationIntensity),-Math.cos(t)*this.weather.windSpeed);const i=this.renderer.getSize(this.rainViewportSize);e.uniforms.uAspect.value=i.x/Math.max(i.y,1)}updateRain(e){const t=this.rainSystem;if(!t)return;t.uniforms.uTime.value+=e,t.uniforms.uHazeColor.value.setRGB(...this.baseFogColor),t.uniforms.uColor.value.copy(Wt.litBySky(Zg,this.baseFogColor));const i=this.rainSplashSystem?.points.material?.uniforms?.uColor;i&&i.value.copy(Wt.litBySky(ev,this.baseFogColor));const n=this.weather.windDirectionDeg*sn,s=Math.sin(n)*this.weather.windSpeed,r=-Math.cos(n)*this.weather.windSpeed,o=Yo,l=h=>(h%(o*2)+o*3)%(o*2)-o;t.uniforms.uWindOffset.value.set(l(t.uniforms.uWindOffset.value.x+s*e),l(t.uniforms.uWindOffset.value.y+r*e)),this.syncRainSlant(),this.camera.getWorldDirection(this.rainCameraDirection);const c=Math.abs(this.rainCameraDirection.y);if(Math.abs(c-this.rainLastVerticalFacing)>.001){this.rainLastVerticalFacing=c;const h=rv+(1-rv)*(1-c),d=av+(1-av)*(1-c);t.uniforms.uUvSquash.value=d,t.uniforms.uPixelSize.value=sv*h*(.5+.5*d)}this.updateRainSplashes(e)}updateRainSplashes(e){const t=this.rainSplashSystem;if(!t||e<=0)return;const{positions:i,life:n,duration:s,lifeAttribute:r}=t,o=r.array;for(let l=0,c=0;l<i.length;l+=3,c++){if(n[c]+=e,n[c]>=s[c]){const h=Math.random()*Math.PI*2,d=Math.sqrt(Math.random())*Yo;i[l]=Math.cos(h)*d,i[l+2]=Math.sin(h)*d,s[c]=lv(Jg,Qg),n[c]=0}o[c]=n[c]/s[c]}t.points.geometry.attributes.position.needsUpdate=!0,r.needsUpdate=!0}setLightning(e){this.lightningFlashes=e}updateLightning(e){let t,i=0;if(this.lightningArmed)for(const l of this.lightningFlashes){if(l.t>e)break;const c=yi.brightnessAt(l,e);c>i&&(t=l,i=c)}const n=t?Math.min(1,Math.max(.1,(uT/t.distanceM)**2)):0,s=i*n*(t?.cloudToGround?1:.6);if(s===this.lightningLevel&&!t?.cloudToGround)return;if(this.lightningLevel=s,this.scene.fog){const l=this.scene.fog,c=s*uu*this.relativeScale/Math.PI;l.color.setRGB(this.airlight[0]+es.r*c,this.airlight[1]+es.g*c,this.airlight[2]+es.b*c)}const r=this.skyMesh?.material?.color;if(r){const l=s*pT;r.setRGB(1+l*es.r,1+l*es.g,1+l*es.b)}if(this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.lightningLight.intensity=s*uu*this.relativeScale,t){const{x:l,y:c,z:h}=bi(25,t.azimuthDeg,pn);this.lightningLight.position.set(l,c,h)}this.lightningBolt||(this.lightningBolt=new hr(pn*.95),this.celestialGroup.add(this.lightningBolt.object));const o=this.weather.cloudBaseM??1500;this.lightningBolt.set(t,i,o),this.render()}buildCompassLabels(){this.compassSprites=HA.map(e=>{const t=na.point(e,this.compassPoints),i=new Xc({map:ST(t),depthTest:!1,fog:!1}),n=new Po(i),{x:s,y:r,z:o}=bi(0,e,Vg);return n.position.set(s,r,o),n.scale.set(Cc,Cc,1),n.renderOrder=WA,n.layers.set(Ng),n.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(n),n}),this.updateCompassScale()}updateCompassScale(){const e=this.camera.fov*Math.PI/360,t=Wg*Math.PI/360,i=Cc*Math.tan(e)/Math.tan(t);for(const n of this.compassSprites)n.scale.set(i,i,1)}disposeCompassLabels(){for(const e of this.compassSprites)e.removeFromParent(),e.material.map?.dispose(),e.material.dispose();this.compassSprites=[]}disposeStarTiers(){for(const e of this.starTiers)this.disposeMesh(e.points);this.starTiers=[]}disposeMesh(e){if(!e)return;e.removeFromParent(),"geometry"in e&&e.geometry.dispose();const t=e.material;for(const i of Array.isArray(t)?t:[t]){const n=i.map;n&&n.dispose(),i.dispose()}}}const sn=Math.PI/180;function Za(a,e,t){return Math.max(e,Math.min(t,a))}function lv(a,e){return a+Math.random()*(e-a)}function Pc(a,e){return(a%(e*2)+e*3)%(e*2)-e}let Lc,kc;function mT(a){switch(a){case"snow":return gT();case"hail":return vT()}}function gT(){if(Lc)return Lc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,0.95)"),i.addColorStop(.5,"rgba(255,255,255,0.55)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),Lc=new Xs(e),Lc}function vT(){if(kc)return kc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,1)"),i.addColorStop(.55,"rgba(255,255,255,0.95)"),i.addColorStop(.8,"rgba(255,255,255,0.4)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),kc=new Xs(e),kc}let Oc;function bT(){if(Oc)return Oc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,0)"),i.addColorStop(.4,"rgba(255,255,255,0)"),i.addColorStop(.6,"rgba(255,255,255,0.9)"),i.addColorStop(.8,"rgba(255,255,255,0.25)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),Oc=new Xs(e),Oc}const yT=`
|
|
9329
|
+
`,transparent:!0,blending:Yi,depthWrite:!1,side:Zt,fog:!1});this.object=new De(new mi(Zf.RADIUS,128,32,0,Math.PI*2,0,Mt.MAX_ANGLE_RAD),t),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1}show(e,t){if(!e||!t){this.object.visible=!1;return}this.towards.set(e.x,e.y,e.z).normalize(),this.object.quaternion.setFromUnitVectors(this.up,this.towards),this.object.material.uniforms.uSource.value.copy(this.towards);for(let i=0;i<Mt.STEPS;i++){for(let n=0;n<3;n++)this.texels[i*4+n]=Xn.toHalfFloat(Math.min(t[i*3+n],6e4));this.texels[i*4+3]=Xn.toHalfFloat(1)}this.texture.needsUpdate=!0,this.object.visible=!0}dispose(){this.object.geometry.dispose(),this.object.material.dispose(),this.texture.dispose()}}class Th{constructor(e){this.generator=e}static SIZE=128;target=new Al(Th.SIZE,{type:ti,generateMipmaps:!1,minFilter:ht,magFilter:ht});camera=new Lf(.1,2e5,this.target);filteredTarget;capturedAtMs=-1/0;capturedVersion;get sharp(){return this.target.texture}get filtered(){return this.filteredTarget?.texture}capture(e,t,i,n=[]){const s=e.shadowMap.autoUpdate;let r=!1;t.traverse(l=>{const c=l;c.isLight&&c.castShadow&&c.shadow&&!c.shadow.map&&(r=!0)}),e.shadowMap.autoUpdate=r?s:!1;const o=n.map(l=>l.visible);for(const l of n)l.visible=!1;this.camera.position.copy(i),this.camera.update(e,t),n.forEach((l,c)=>{l.visible=o[c]}),e.shadowMap.autoUpdate=s,this.filteredTarget=this.generator.fromCubemap(this.target.texture,this.filteredTarget),this.capturedAtMs=performance.now()}dispose(){this.target.dispose(),this.filteredTarget?.dispose()}}class gn{static REFRESH_MS=250;generator;eye;probes=new Map;constructor(e){gn.install(),this.generator=new Sp(e),this.eye=new Th(this.generator)}static decor;refresh(e,t,i,n,s,r,o,l=[],c,h=gn.REFRESH_MS){for(const m of[...this.probes.keys()])n.some(g=>g.id===m&&g.shiny)||(this.probes.get(m).dispose(),this.probes.delete(m));const d=[{probe:this.eye}];for(const m of n){if(!m.shiny)continue;let g=this.probes.get(m.id);g||(g=new Th(this.generator),this.probes.set(m.id,g)),d.push({probe:g,reflector:m})}const u=performance.now(),p=()=>d.filter(({probe:m})=>m.capturedVersion!==o),f=p().filter(({probe:m})=>u-m.capturedAtMs>=h);if(f.length>0){const m=f.reduce((g,x)=>x.probe.capturedAtMs<g.probe.capturedAtMs?x:g);if(m.reflector){const g=m.reflector.holder.getWorldPosition(this.scratch);m.probe.capture(e,t,g,[m.reflector.holder,...s]),gn.reflectOn(m.reflector.holder,m.probe)}else{m.probe.capture(e,t,i,[...s,...l]),c?.(m.probe.sharp),gn.decor=m.probe.filtered;for(const g of r)gn.reflectOn(g,m.probe);for(const g of n)g.shiny||gn.reflectOn(g.holder,m.probe)}m.probe.capturedVersion=o}const v=p();if(v.length!==0)return Math.max(16,Math.min(...v.map(({probe:m})=>h-(u-m.capturedAtMs))))}scratch=new R;static reflectOn(e,t={filtered:gn.decor}){e.traverse(i=>{const n=i.material;for(const s of Array.isArray(n)?n:n?[n]:[])s instanceof Il?t.sharp&&s.setSurroundings(t.sharp):s instanceof Hr&&t.filtered&&s.envMap!==t.filtered&&(s.envMap=t.filtered,s.needsUpdate=!0)})}dispose(){this.eye.dispose();for(const e of this.probes.values())e.dispose();this.generator.dispose()}static installed=!1;static install(){if(gn.installed)return;gn.installed=!0;const e="iblIrradiance += getIBLIrradiance( geometryNormal );";if(!Ye.lights_fragment_maps.includes(e)){console.warn("Reflections: three's lights_fragment_maps changed; the environment will light diffusely too");return}Ye.lights_fragment_maps=Ye.lights_fragment_maps.replace(e,"// The sky's diffuse light is the hemisphere light's (see Reflections).")}}function RA(a,e){return JSON.stringify(a.cloudLayers)===JSON.stringify(e.cloudLayers)&&a.cloudCover===e.cloudCover&&a.cloudDarkness===e.cloudDarkness&&a.cloudBaseM===e.cloudBaseM&&a.highCloudCover===e.highCloudCover&&a.iceCrystalAlignment===e.iceCrystalAlignment&&a.lowerCloudCover===e.lowerCloudCover&&a.relativeHumidity===e.relativeHumidity&&a.precipitationType===e.precipitationType&&a.precipitationIntensity===e.precipitationIntensity&&a.precipitationAmount===e.precipitationAmount&&a.windDirectionDeg===e.windDirectionDeg&&a.windSpeed===e.windSpeed&&a.storm===e.storm}const Ic=900,eu=1,Og=4,Ng=5,Fg=.5,nl=900,CA=6371e3,PA=3e4;function Ug(a){const e=Math.sqrt(2*CA*Math.max(a,0));return Math.max(nl,Math.min(e,PA))}const tu=.5,iu=850,Bg=.03,pn=850,Wo=300,nr=120,LA=700,kA=.04,Xo=4,nu=1.5,zg=-1,OA=6,tf=-4,NA=2,FA=20,Dc=2600,UA=.97,BA=.38,Rc=tf,Hg=1500,zA=400,HA=[0,45,90,135,180,225,270,315],GA=2,VA=16,Gg=4e3,Vg=880,Cc=40,Wg=60,WA=10,XA=1,YA=150,qA=55,$A=.5,KA=17,Xg=.265,Yg=[[0,0],...Array.from({length:8},(a,e)=>{const t=e*Math.PI/4;return[Math.cos(t)*Xg,Math.sin(t)*Xg]})],su=700,ru=250/1e3,ZA=12,qg=5,au=6,Pr=[.92,.92,.95],ou=[.15,.15,.19],lu={snow:{fallSpeedMPerS:2,size:.3,color:new se(1,1,1),driftSensitivity:1,radiusM:25,topYM:20,opacity:.75,poolSize:2400},hail:{fallSpeedMPerS:14,size:.5,color:new se(.95,.98,1),driftSensitivity:.25,radiusM:25,topYM:10,opacity:.95,poolSize:1200}},Ga=0,cu=0,jA=150,hu=3e3,Yo=20,JA=15,$g=Ga,QA=40,Kg=1,eT=9;function du(a){return Kg+(eT-Kg)*a}const Zg=new se(.75,.82,.92),tT=10,iT=.5;function jg(a){return Za(a/tT,iT,1)}const Lr=200,Jg=.18,Qg=.32,nT=.35,ev=new se(.85,.9,.98),tv=5,iv=.85,nv=.01;function sT(a,e,t=1){return Math.round(a*(nv+(1-nv)*e)*t)}function rT(a,e){return JSON.stringify(a.cloudLayers)===JSON.stringify(e.cloudLayers)&&a.cloudCover===e.cloudCover&&a.cloudDarkness===e.cloudDarkness&&a.cloudBaseM===e.cloudBaseM&&a.highCloudCover===e.highCloudCover&&a.iceCrystalAlignment===e.iceCrystalAlignment&&a.lowerCloudCover===e.lowerCloudCover}const sv=230,rv=.7,av=.05,aT=.6,oT=.7,lT=.15,ov=.35,cT=3,hT=1/12,dT=.5,es=new se(.82,.86,1),uT=2500,pT=1.6,uu=15,fT={Venus:new se(1,.96,.85),Mars:new se(1,.55,.4),Jupiter:new se(.95,.87,.72),Saturn:new se(.92,.86,.68)};class Wt{renderer;scene=new Hs;phenomena=new Un(this.scene);references=new Fn(this.scene,()=>this.render());camera;celestialGroup=new Nt;skyMesh;skyGradientMaterial;scatteredSky;lastAstronomy;skyColoursStale=!1;scatteredSkyFrameId=null;skyHoldUntilMs=void 0;onFirstFrameHold;firstFrameReady(){return(!this.scatteredSky||this.scatteredSky.ambient!==void 0)&&!this.iceHalos?.awaitingFirstDisplay}sceneSettled(){return this.firstFrameReady()&&this.arrivalsPending===0&&this.eyeSettledVersion>=this.lastArrivalVersion}arrivalsPending=0;eyeSettledVersion=-1;lastArrivalVersion=0;awaitArrival(e,t){if(this.skyHoldUntilMs===null)return e;this.arrivalsPending++;let i=!0;const n=()=>{i&&(i=!1,this.arrivalsPending--,this.lastArrivalVersion=this.sceneVersion,this.skyHoldUntilMs&&this.redrawHeld())},s=t===void 0?void 0:setTimeout(n,t);return e.finally(()=>{clearTimeout(s),n()})}static ROADS_PATIENCE_MS=1500;redrawHeld(){this.frameDirty=!0,!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()}))}holdForNewScene(e){this.surroundings=void 0,this.scatteredSky?.resetSurroundings(),this.startHold(),e&&this.awaitArrival(e).catch(()=>{})}startHold(){this.skyHoldUntilMs=performance.now()+Gg,this.eyeSettledVersion=-1,this.lastArrivalVersion=this.sceneVersion+1,this.iceHalos?.setUrgent(!0),this.onFirstFrameHold?.(!0),setTimeout(()=>this.render(),Gg+20)}groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;roadSystem=new An;roadAttribution;roadWays;statedRoads=[];decorModelProvider=Kc[0].create();decorModelToken=0;decorModelCredits=new Map;bodySystem=new ft(e=>this.awaitArrival(this.loadBodyModel(e)),()=>this.render(),()=>this.relativeScale);bodyOrigin={x:0,z:0};starTiers=[];bodyMeshes=new Map;hitAreas=new Map;glareSprites=new Map;decorObjects=[];decorGroups=new Map;decorReferencePose;decorCurrentPose;decorTime=0;celestialLight=new Im(16777215,0);celestialLightTarget=new Vt;skyLight=new iw(16777215,0,0);streetlightLights=new Map;compassSprites=[];showCompass=!1;compassHovered=!1;compassForced=!1;animationFrameId=null;resolution;cssSize={width:1,height:1};frameDirty=!1;flushFrameId=null;framesDriven=!1;lastFrameTimeMs;raycaster=new Yc;weather=sf;observerElevationM=0;siteElevationM=0;air=new In;groundRadius=nl;terrainRadius=nl;cloudRendering="volume";layeredClouds;cloudLayerOffsetsM={};cloudOffsetM=new R;cloudFieldOffset=new R;cloudMesh;cirrusMesh;cirrusMaterial;cirrusUniforms;cloudMaterial;cloudUniforms;precipitationPoints;precipitationPositions;precipitationPhase;precipitationSpeedJitter;precipitationWobbleFreqJitter;precipitationWobbleAmpJitter;precipitationBounceRemaining;precipitationBounceHeight;precipitationBounceDuration;precipitationBounceVelX;precipitationBounceVelZ;precipitationUniforms;rainSystem;rainSplashSystem;rainCameraDirection=new R;rainLastVerticalFacing=-1;rainViewportSize=new ye;lightningArmed=!1;lightningFlashes=[];lightningLevel=0;lightningBolt;lightningLight=new Im(16777215,0);baseFogColor=[0,0,0];lastSunPosition;lensFlare;sunUnhiddenFraction=1;lensFlareArtifactIntensity=0;sunWorldPosition=new R;sunVisible=!1;lensFlareScratch=new R;sunOcclusionRaycaster=new Yc;sunOcclusionDirectionScratch=new R;sunOcclusionRightScratch=new R;sunOcclusionUpScratch=new R;ufoOcclusionRaycaster=new Yc;projectionKind="rectilinear";starPoints=0;namedStars=[];equidistantPass;depthOfFieldPass;finishPass;exposureAccumulation;exposureInstants=1;get developingExposure(){return this.exposureInstants>1&&this.exposureInstantAt!==void 0}static EXPOSURE_BUDGET_MS=12;static exposureInstantsPerFrame(e){return Math.max(3,Math.round(6e6/Math.max(1,e)))}exposureFrameId=null;exposureInstantsDone=0;exposureInstantAt;restatingExposure=!1;lensOptics;instrumentMagnitudeGain=0;lightPollution;meteorSystem;cometTail;satelliteField;iceHalos;lunarDiffraction;lunarDiffractionKey="";rainbow;skyGlow;cometKey;novaKeys=new Set;constructor(e,t=jy()){this.renderer=new m_({canvas:e,antialias:!0}),this.resolution=new xt(Math.min(window.devicePixelRatio||1,2),this.renderer.getContext()),this.renderer.setPixelRatio(this.resolution.pixelRatio),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=rl,this.camera=new ln(60,e.width/Math.max(e.height,1),.1,Ic*1.2),this.camera.position.set(0,1.6,0),this.terrainProviders=t,this.celestialLight.shadow.mapSize.set(1024,1024),this.celestialLight.shadow.camera.left=-nr,this.celestialLight.shadow.camera.right=nr,this.celestialLight.shadow.camera.top=nr,this.celestialLight.shadow.camera.bottom=-nr,this.celestialLight.shadow.camera.near=pn-Wo,this.celestialLight.shadow.camera.far=pn+Wo,this.celestialLight.shadow.bias=-.03/(2*Wo),this.celestialLight.shadow.normalBias=kA,this.celestialLight.target=this.celestialLightTarget,this.lightningLight.color.copy(es),this.scene.add(this.celestialLight,this.celestialLightTarget,this.skyLight,this.lightningLight),this.scene.add(this.celestialGroup),this.scene.add(this.bodySystem.group),this.scene.add(this.roadSystem.group);const i=new zt(this.renderer,()=>{this.skyColoursStale=!0,this.render()});i.supported?this.scatteredSky=i:i.dispose()}get currentTerrainAttribution(){return this.terrainAttribution}get currentRoadAttribution(){return this.roadAttribution}resize(e,t){this.cssSize.width=e,this.cssSize.height=t,this.renderer.setSize(e,t,!1),this.camera.aspect=e/Math.max(t,1),this.camera.updateProjectionMatrix(),this.render()}setCloudRendering(e){e!==this.cloudRendering&&(this.cloudTransmissionMemo.clear(),this.cloudRendering=e,this.layeredClouds?.dispose(),this.layeredClouds=void 0,this.buildClouds(),this.buildCirrus(),this.render())}setCloudOffset(e,t={}){this.cloudOffsetM.set(e.x,0,e.z),this.cloudLayerOffsetsM=t,this.layeredClouds?.setOffsets(e,t),this.cloudFieldOffset.set(e.x*ru,0,e.z*ru);for(const i of[this.cloudUniforms,this.cirrusUniforms])i?.fieldOffset.value.copy(this.cloudFieldOffset);this.iceHalos?.setCloudOffset(this.layeredClouds?.cirrusMask?.offset??this.cloudFieldOffset)}setObserverPose(e){const t=((e.rollDeg??0)+this.gaitOffset.rollDeg)*sn,i=(e.pitchDeg+this.lookPitchDeg+this.gaitOffset.pitchDeg)*sn;if(e.headingDeg!==void 0?this.camera.rotation.set(i,-(e.headingDeg+this.lookYawDeg+this.gaitOffset.yawDeg)*sn,t,"YXZ"):this.camera.rotation.set(i,this.camera.rotation.y,t,"YXZ"),e.elevationM!==this.observerElevationM&&(this.observerElevationM=e.elevationM,this.syncCloudLayer(),Math.abs(Ug(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/nl),this.camera.far=Math.max(Ic*1.2,this.groundRadius*2.5),this.camera.updateProjectionMatrix())),this.terrainMesh&&this.terrainOrigin&&e.lat!==void 0&&e.lng!==void 0){const n=pi(this.terrainOrigin.lat,this.terrainOrigin.lng,e.lat,e.lng);this.terrainMesh.position.x=n.x,this.terrainMesh.position.z=n.z}this.poseCameraY=this.groundYUnder(0,0)+1.6+e.elevationM,this.camera.position.y=this.poseCameraY,this.celestialGroup.position.y=this.camera.position.y,this.camera.fov!==e.fovDeg&&(this.camera.fov=e.fovDeg,this.camera.updateProjectionMatrix(),this.updateCompassScale())}setTerrainProviders(e){this.stopTerrainFade(),this.terrainProviders=e,this.disposeMesh(this.terrainMesh),this.terrainMesh=void 0,this.terrainOrigin=void 0,this.terrainAttribution=void 0,this.roadSystem.clear(),this.roadAttribution=void 0,this.roadWays=void 0,this.applyGroundDepthWrite(),this.terrainBuildToken++}setTerrainOrigin(e,t,i){if(e===void 0||t===void 0)return;const n=this.groundRadius;if(this.terrainOrigin&&Math.abs(this.terrainRadius-n)<n*.1){const{x:l,z:c}=pi(e,t,this.terrainOrigin.lat,this.terrainOrigin.lng);if(Math.sqrt(l*l+c*c)<YA*(n/nl))return}const s=this.terrainOrigin,r=this.terrainRadius;(!s||s.lat!==e||s.lng!==t)&&(this.roadSystem.clear(),this.roadAttribution=void 0),this.terrainOrigin={lat:e,lng:t},this.terrainRadius=n;const o=++this.terrainBuildToken;this.awaitArrival(z5(e,t,this.terrainProviders,n)).then(({mesh:l,attribution:c,originElevationM:h})=>{if(o!==this.terrainBuildToken)return;this.setSiteElevation(h??0);const d=this.terrainMesh,u=s?pi(e,t,s.lat,s.lng):void 0;d&&u&&Math.hypot(u.x,u.z)<=r&&(l.position.y=d.position.y+this.groundYOfPatch(d,u.x,u.z)),d&&u&&Math.hypot(u.x,u.z)<=r?this.crossfadeTerrain(l,d):this.disposeMesh(this.terrainMesh),l.renderOrder=XA,this.terrainMesh=l,this.compileNextFrameOffThread(),this.terrainAttribution=c,this.scene.add(l),this.applyGroundDepthWrite(),this.decorCurrentPose&&(this.setObserverPose(this.decorCurrentPose),this.updateDecorAnchoring(this.decorReferencePose,this.decorCurrentPose,this.decorTime)),this.buildRoads(e,t,n),this.drapeStatedRoads(),this.render(),i?.()}).catch(l=>{console.warn("Terrain build failed, keeping the flat ground fallback:",l),i?.()})}static TERRAIN_CROSSFADE_MS=1200;fadingTerrain;crossfadeTerrain(e,t){this.stopTerrainFade();const i=e.material;i.opacity=0,i.polygonOffset=!0,i.polygonOffsetFactor=-1,i.polygonOffsetUnits=-4;const n=performance.now(),s=()=>{const r=Math.min(1,(performance.now()-n)/Wt.TERRAIN_CROSSFADE_MS);if(i.opacity=r*r*(3-2*r),this.render(),r<1&&this.fadingTerrain){this.fadingTerrain.frameId=requestAnimationFrame(s);return}i.polygonOffset=!1,i.opacity=1,this.fadingTerrain&&this.disposeMesh(this.fadingTerrain.mesh),this.fadingTerrain=void 0,this.render()};this.fadingTerrain={mesh:t,frameId:requestAnimationFrame(s)}}stopTerrainFade(){if(!this.fadingTerrain)return;cancelAnimationFrame(this.fadingTerrain.frameId),this.disposeMesh(this.fadingTerrain.mesh),this.fadingTerrain=void 0;const e=this.terrainMesh?.material;e&&(e.opacity=1,e.polygonOffset=!1)}static ROADS_MAX_RADIUS_M=5e3;buildRoads(e,t,i){const n=this.terrainProviders.roads;if(!n||i>Wt.ROADS_MAX_RADIUS_M){this.roadSystem.clear(),this.roadAttribution=void 0;return}const s=this.roadWays;if(s&&s.lat===e&&s.lng===t&&s.radiusM===i){this.drapeRoads(s.ways,e,t,n.contemporary);return}this.awaitArrival(n.getRoads(ub(e,t,i)),Wt.ROADS_PATIENCE_MS).then(r=>{this.terrainOrigin?.lat!==e||this.terrainOrigin?.lng!==t||(this.roadWays={lat:e,lng:t,radiusM:i,ways:r},this.drapeRoads(r,e,t,n.contemporary))}).catch(r=>{console.warn("Roads unavailable, drawing none:",r)})}setStatedRoads(e){e!==this.statedRoads&&(this.statedRoads=e,this.drapeStatedRoads(),this.render())}drapeStatedRoads(){const e=this.terrainMesh;e&&(this.roadSystem.group.position.copy(e.position),this.roadSystem.setStated(this.statedRoads,(t,i)=>this.groundYOfPatch(e,t,i)))}drapeRoads(e,t,i,n){const s=this.terrainMesh;s&&(this.roadSystem.group.position.copy(s.position),this.roadSystem.set(e,t,i,(r,o)=>this.groundYOfPatch(s,r,o),n),this.roadAttribution=e.length>0?this.terrainProviders.roads?.attribution:void 0,this.compileNextFrameOffThread(),this.render())}setShowCompass(e){this.showCompass!==e&&(this.showCompass=e,this.disposeCompassLabels(),e&&this.buildCompassLabels(),this.render())}setCompassPoints(e){this.compassPoints!==e&&(this.compassPoints=e,this.showCompass&&(this.disposeCompassLabels(),this.buildCompassLabels(),this.render()))}compassPoints;applyLensFlareTint(e){if(!this.lensFlare)return;const t=qA*(this.sunArriving[1]/KA);this.lensFlare.uniforms.uColorGain.value.setRGB(t*e[0],t*e[1],t*e[2]),this.applyDazzleStrength()}applyDazzleStrength(){if(!this.lensFlare)return;const[e,t,i]=this.sunArriving,n=Math.max(t,1e-30);this.lensFlare.uniforms.uDazzleColour.value.setRGB(e/n,1,i/n),this.lensFlare.uniforms.uVeilStrength.value=Gi.K*t*this.sunUnhiddenFraction}sunArriving=[0,0,0];setCompassHovered(e){this.compassHovered!==e&&(this.compassHovered=e,this.updateCompassVisibility())}setCompassForced(e){this.compassForced!==e&&(this.compassForced=e,this.updateCompassVisibility())}updateCompassVisibility(){const e=this.compassHovered||this.compassForced;if(this.compassSprites.some(t=>t.visible!==e)){for(const t of this.compassSprites)t.visible=e;this.developingExposure?this.exposureInstantsDone>0&&this.presentExposure():this.render()}}setWeather(e){if(RA(this.weather,e))return;this.cloudTransmissionMemo.clear();const t=this.weather;this.weather=e;const i=!rT(t,e),n=t.precipitationType!==e.precipitationType,s=t.precipitationIntensity!==e.precipitationIntensity||t.precipitationAmount!==e.precipitationAmount;(t.relativeHumidity!==e.relativeHumidity||n||s)&&(this.air=In.of(e),this.applyAir(),this.scatteredSky?.setConditions({aerosolOpticalDepth:e.relativeHumidity===void 0?void 0:Us.steppedOpticalDepth(e.relativeHumidity)}),this.pumpScatteredSky()),i&&(this.buildClouds(),this.buildCirrus()),n?this.buildPrecipitation():s&&this.updatePrecipitationDensity(),this.lastAstronomy&&(i&&(this.buildIceHalos(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildLunarDiffraction(this.lastAstronomy.moon)),(i||n||s)&&this.buildRainbow(this.lastAstronomy.sun,this.lastAstronomy.moon)),this.lightningArmed=e.storm&&e.cloudDarkness>=dT,this.syncAnimationLoop(),this.render()}setDecor(e){if(this.decorObjects!==e){this.decorObjects=e,this.decorModelToken++,this.decorModelCredits.clear();for(const t of this.decorGroups.values())t.removeFromParent(),ui.dispose(t);this.decorGroups.clear(),this.streetlightLights.clear();for(const t of e){const i=ui.build(t,t.lit??!1);this.scene.add(i),this.decorGroups.set(t.id,i),t.kind==="streetlight"&&this.addStreetlightLight(t.id,i),this.awaitArrival(this.loadDecorModel(t,this.decorModelToken))}this.celestialLight.castShadow=this.decorGroups.size>0||this.bodySystem.any}}setDecorModelProvider(e){this.decorModelProvider=e;const t=this.decorObjects;this.decorObjects=[],this.setDecor(t)}get currentDecorModelCredits(){return[...this.decorModelCredits.values(),...this.bodySystem.modelCredits]}get bodyGround(){const{x:e,z:t}=this.bodyOrigin,i=this.groundYUnder(e,t);return{heightAt:(n,s)=>this.groundYUnder(e+n,t-s)-i}}setBodies(e,t=0,i=e.map(r=>r.id),n=[],s){const{x:r,z:o}=this.bodyOrigin,l=this.scatteredSky;this.bodySystem.set(e,{originX:r,originZ:o,originGroundY:this.groundYUnder(r,o),eye:this.camera.position,groundYAt:(h,d)=>this.groundYUnder(h,d),wind:s,light:this.plumeLight(),occluders:[...this.decorGroups.values()],transmittance:(h,d,u)=>this.transmittanceTo(h,d,u)},t,l?(h,d)=>l.relativeOfLuminance(h,d):void 0,i),this.bodySystem.setSmoke(n,t),this.bodySystem.any&&(this.celestialLight.castShadow=!0),this.fitShadowToBodies();const c=this.bodySystem.furthestFrom(this.camera.position)*1.2;c>this.camera.far&&(this.camera.far=c,this.camera.updateProjectionMatrix())}plumeLight(){const e=this.celestialLight.color,t=this.celestialLight.intensity*.5,i=this.skyLight.color,n=this.skyLight.groundColor,s=this.skyLight.intensity,r=(o,l,c)=>(o*t+(l+c)/2*s)/Math.PI;return[r(e.r,i.r,n.r),r(e.g,i.g,n.g),r(e.b,i.b,n.b)]}bodyOutline(e,t){return this.bodySystem.outlineOf(e,t)}documentUrl;modelUrl(e){return e.url?this.documentRelative(e.url):void 0}documentRelative(e){if(e.startsWith("data:"))return e;const t=this.documentUrl??(typeof location>"u"?void 0:location.href);return t?new URL(e,t).href:e}async loadBodyModel(e){const t=e.url?void 0:e.id?await this.decorModelProvider.entry(e.id):void 0,i=this.modelUrl(e)??t?.url,n=e.credit??t?.credit;if(!(!i||!n))return{scene:await kg(i),credit:n,headingOffsetDeg:e.headingOffsetDeg??t?.headingOffsetDeg}}async loadDecorModel(e,t){const i=e.model;if(!(!i||!ui.usesModel(e)))try{const n=i.url?void 0:i.id?await this.decorModelProvider.entry(i.id):void 0,s=this.modelUrl(i)??n?.url,r=i.credit??n?.credit;if(!s||!r)return;const o=await kg(s);if(t!==this.decorModelToken)return;const l=this.decorGroups.get(e.id);if(!l)return;gn.reflectOn(o),ui.applyModel(l,e,o,{headingOffsetDeg:i.headingOffsetDeg??n?.headingOffsetDeg,depictedSizeM:n?.sizeM}),this.decorModelCredits.set(e.id,r),this.render()}catch(n){console.warn(`Keeping the built-in shape for decor "${e.id}":`,n)}}updateDecorAnchoring(e,t,i=0){this.decorReferencePose=e,this.decorCurrentPose=t,this.decorTime=i;const n=e?.lat!==void 0&&e.lng!==void 0&&t?.lat!==void 0&&t?.lng!==void 0?pi(e.lat,e.lng,t.lat,t.lng):{x:0,z:0},s=this.decorObjects.find(c=>c.observerSide!==void 0&&Va(c.kind));if(this.terrainMesh&&this.terrainOrigin&&t?.lat!==void 0&&t.lng!==void 0){const c=pi(this.terrainOrigin.lat,this.terrainOrigin.lng,t.lat,t.lng);this.terrainMesh.position.x=c.x-(s?0:this.gaitOffset.eastM),this.terrainMesh.position.z=c.z+(s?0:this.gaitOffset.northM),this.roadSystem.group.position.copy(this.terrainMesh.position)}s||(n.x-=this.gaitOffset.eastM,n.z+=this.gaitOffset.northM,this.camera.position.y=this.poseCameraY+this.gaitOffset.upM,this.celestialGroup.position.y=this.camera.position.y);const r={x:0,z:0};let o=0;if(s){const c=ui.occupantView(s),h=-(s.headingDeg??0)*sn,d=c.x*Math.cos(h)+c.z*Math.sin(h),u=-c.x*Math.sin(h)+c.z*Math.cos(h),p=s.eastM+n.x,f=-s.northM+n.z;r.x=-(p+d),r.z=-(f+u),this.camera.position.y=this.groundYUnder(p,f)+c.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*sn,-(c.headingDeg+this.indoorLookYawDeg)*sn,0,"YXZ")}this.bodyOrigin={x:n.x+r.x,z:n.z+r.z},this.placeShadowFrustum();for(const c of this.decorObjects){const h=this.decorGroups.get(c.id);if(!h)continue;const d=yr(c,i),u=d.eastM+n.x+r.x,p=-d.northM+n.z+r.z,f=this.terrainMesh,v=`${f?.geometry.uuid}:${(u-(f?.position.x??0)).toFixed(3)}:${(p-(f?.position.z??0)).toFixed(3)}:${(f?.position.y??0).toFixed(3)}:${d.headingDeg}:${d.altitudeM}`;if(h.userData.footprintKey!==v&&(h.userData.footprintGroundY=this.groundUnderFootprint(c,u,p,d.headingDeg,d.altitudeM),h.userData.footprintKey=v),h.position.set(u,h.userData.footprintGroundY+d.altitudeM,p),c.kind==="crop"){const m=Math.hypot(u,p);h.visible=m<=jA}if(d.headingDeg!==void 0&&(h.rotation.y=-d.headingDeg*sn),c.kind==="crop"&&h.visible){const m=this.terrainMesh,g=[m?.geometry.uuid,u-(m?.position.x??0),p-(m?.position.z??0),h.position.y-(m?.position.y??0),d.headingDeg,d.altitudeM].map(x=>typeof x=="number"?x.toFixed(4):x).join(":");h.userData.cropGroundKey!==g&&(ui.fitCropToGround(h,(x,y)=>this.groundYUnder(x,y)+d.altitudeM),h.userData.cropGroundKey=g)}o=Math.max(o,h.position.distanceTo(this.camera.position))}const l=Math.max(Ic*1.2,this.groundRadius*2.5,o*1.2);Math.abs(this.camera.far-l)>1&&(this.camera.far=l,this.camera.updateProjectionMatrix())}groundYUnder(e,t){const i=this.terrainMesh;return i?i.position.y+this.groundYOfPatch(i,e-i.position.x,t-i.position.z):cu}groundYOfPatch(e,t,i){const n=e.geometry.getAttribute("position"),s=Math.round(Math.sqrt(n.count));if(s<2||s*s!==n.count)return cu;const r=n.getX(0),o=n.getX(s-1),l=n.getZ(0),c=n.getZ((s-1)*s),h=Za((t-r)/(o-r)*(s-1),0,s-1),d=Za((i-l)/(c-l)*(s-1),0,s-1);if(!Number.isFinite(h)||!Number.isFinite(d))return cu;const u=Math.min(Math.floor(h),s-2),p=Math.min(Math.floor(d),s-2),f=h-u,v=d-p,m=(w,E)=>n.getY(w*s+E),g=m(p,u),x=m(p,u+1),y=m(p+1,u),b=m(p+1,u+1);return f+v<=1?g+(x-g)*f+(y-g)*v:b+(y-b)*(1-f)+(x-b)*(1-v)}groundUnderFootprint(e,t,i,n,s=0){return ui.groundUnderFootprint(e,t,i,n,(r,o)=>this.groundYUnder(r,o),s)}lookYawDeg=0;lookPitchDeg=0;poseCameraY=1.6;setGait(e){this.gaitOffset=e}gaitOffset={eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0};setLookOffset(e,t){this.lookYawDeg=e,this.lookPitchDeg=t}indoorLookYawDeg=0;indoorLookPitchDeg=0;setIndoorLook(e,t){this.indoorLookYawDeg=e,this.indoorLookPitchDeg=t}medium;setMedium(e,t,i,n){const s=e&&t!==void 0&&t>0&&i!==void 0&&i>0?{kind:e.kind,iso:n??e.iso,autoExposure:e.autoExposure===!0,exposureSeconds:t,fNumber:i}:void 0;JSON.stringify(s)!==JSON.stringify(this.medium)&&(this.medium=s,this.render())}applyMedium(){const e=this.medium,t=Math.max(this.relativeScale,1e-30);if(!e){ji.uMedium.value=0;return}ji.uMedium.value=e.kind==="negative"?1:e.kind==="slide"?2:3;const i=e.autoExposure?this.scatteredSky?.sceneAdaptation:void 0;ji.uMediumExposure.value=i!==void 0&&i>0?1/(t*i):.065*e.exposureSeconds*e.iso/(e.fNumber*e.fNumber*t)}grain;lampBloomRadiusRad(){if(!this.grain)return;const e=this.camera.fov*Math.PI/180,t=this.grain.frameHeightMm/(2*Math.tan(e/2)),i=Math.max(1,this.renderer.getDrawingBufferSize(this.bloomSize).y);return Math.max(this.grain.detailUm*.001/t,e/i)}bloomSize=new ye;updateDecorLitState(e,t=0){const i=this.lampBloomRadiusRad();for(const n of this.decorObjects){const s=this.decorGroups.get(n.id);if(s&&n.lights?.length&&ui.setLights(s,n.lights,e,s.position.distanceTo(this.camera.position),t,this.relativeScale,i),n.kind!=="streetlight"&&n.kind!=="vehicle")continue;const r=this.decorGroups.get(n.id);if(!r)continue;const o=nh(n,e);ui.setLit(r,n.kind,o,this.relativeScale);const l=this.streetlightLights.get(n.id);l&&(l.visible=o)}}addStreetlightLight(e,t){const i=ui.bodyOf(t),n=i.children.find(r=>r.userData.emissive);if(!n)return;const s=new P1(16764006,Hg*this.relativeScale,zA);s.position.copy(n.position).multiply(i.scale),s.castShadow=!0,s.shadow.mapSize.set(512,512),s.shadow.bias=-.002,t.add(s),this.streetlightLights.set(e,s)}setAstronomy(e){(!this.lastAstronomy||Math.abs(this.lastAstronomy.sun.altitudeDeg-e.sun.altitudeDeg)>2)&&(this.surroundings=void 0,this.scatteredSky?.resetSurroundings()),this.lastAstronomy=e,this.scatteredSky?.update(this.scatteredSkyState(e)),this.skyHoldUntilMs===void 0&&this.startHold(),this.pumpScatteredSky(),this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround();const t=this.magnitudeLimitOf(e);this.buildStars(e.stars,t),this.placeBodies(e,t),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon),this.applySkyColours(e),this.syncAnimationLoop(),this.render()}static relativeSkyOf(e,t){const i=r=>{const o=Gn.luminanceOf(r);if(!(o>0))return[0,0,0];const l=Ae.relativeOfResponse(o)/o;return[r[0]*l,r[1]*l,r[2]*l]},n=i(e.zenith),s=i(e.horizon);return{zenith:n,horizon:s,airlight:s,skyIrradiance:[n[0]*Math.PI,n[1]*Math.PI,n[2]*Math.PI],scale:t}}placeBodies(e,t){this.bodiesScale=this.relativeScale,this.setBodyMesh("sun",e.sun,Xo,new se(1,.96,.88),e.sun.magnitude),this.setMoonMesh(e.moon,t),this.buildPlanets(e.planets,t),this.buildComet(e.comet,t),this.buildNovae(e.novae??[],t)}bodiesScale=0;applySkyColours(e){this.skyColoursStale=!1,this.bodiesScale!==this.relativeScale&&(this.placeBodies(e,this.magnitudeLimitOf(e)),this.applyDazzleStrength(),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon));const t=this.scatteredSky?.ambient??Wt.relativeSkyOf(Pp(e.sun.altitudeDeg),this.relativeScale),i=t,n=this.lightUnderClouds(e),s=r=>n?[0,1,2].map(o=>r[o]*n.through+n.radiance[o]*(1-n.through)):[r[0],r[1],r[2]];this.baseFogColor=[i.horizon[0],i.horizon[1],i.horizon[2]],this.airlight=s(i.airlight),this.skyMesh&&this.skyGradientMaterial&&(this.skyMesh.material=this.scatteredSky?.ready?this.scatteredSky.material:this.skyGradientMaterial),this.updateCelestialLight(e,t),this.updateCloudLighting(e.sun,this.baseFogColor),ot.setEye(this.scatteredSky?.rodShare??0,this.relativeScale),this.updateTwinkle(this.twinkleSeconds);for(const r of this.streetlightLights.values())r.intensity=Hg*this.relativeScale;this.buildSkyGlow(e,i.zenith),this.applyAir(),this.buildLunarDiffraction(e.moon)}reflections;sceneVersion=0;reflectionTimer;buildLunarDiffraction(e){const t=this.scatteredSky?.ready?this.scatteredSky:void 0;if(e.altitudeDeg<-.5||!t){this.lunarDiffraction?.show(void 0),this.lunarDiffractionKey="";return}const i=this.weather.relativeHumidity,n=(i===void 0?Ne.DEFAULT_AEROSOL_OPTICAL_DEPTH:Us.opticalDepth(i))*Ne.aerosolDensity(this.siteElevationM+this.observerElevationM),s=this.cloudTransmission(e),r=-Math.log(Math.max(this.cloudTransmission(e,!0),1e-4)),o=Mt.aureoleScale(n,Mt.airMass(e.altitudeDeg))*s,l=Mt.coronaScale(r),c=Mt.illuminanceOf(e.magnitude),h=[e.altitudeDeg.toFixed(1),e.azimuthDeg.toFixed(1),e.magnitude.toFixed(2),o.toExponential(2),l.toExponential(2),Math.log10(Math.max(t.adaptation,1e-12)).toFixed(2)].join(":");if(h===this.lunarDiffractionKey)return;this.lunarDiffractionKey=h,this.lunarDiffraction||(this.lunarDiffraction=new Zf,this.celestialGroup.add(this.lunarDiffraction.object));const d=Mt.profile(Mt.HAZE_COARSE_MODE),u=Mt.profile(Mt.CLOUD_DROPLETS),p=sg(e.altitudeDeg),f=new Float32Array(Mt.STEPS*3);let v=0;for(let y=0;y<Mt.STEPS;y++){const b=y*3,w=[0,1,2].map(T=>c*p[T]*(o*d[b+T]+l*u[b+T])),E=Gn.shown(w,Gn.luminanceOf(w),(T,M)=>t.relativeOfLuminance(T,M));f.set(E,b),v=Math.max(v,Gn.response(E))}const{x:m,y:g,z:x}=bi(e.altitudeDeg,e.azimuthDeg,1);this.lunarDiffraction.show(v>1/200?{x:m,y:g,z:x}:void 0,f)}applyAir(){this.scene.fog instanceof ra||(this.scene.fog=new ra),this.scene.fog.setAir(this.air,this.siteElevationM,this.airlight)}airlight=[0,0,0];transmittanceTo(e,t,i){const n=this.camera.position;return this.air.transmittance(this.siteElevationM+n.y,this.siteElevationM+t,Math.hypot(e-n.x,t-n.y,i-n.z))}setSiteElevation(e){Math.abs(e-this.siteElevationM)<1||(this.siteElevationM=e,this.applyAir(),this.lastAstronomy&&this.scatteredSky?.update(this.scatteredSkyState(this.lastAstronomy)))}scatteredSkyState(e){return{altitudeM:this.siteElevationM+this.observerElevationM+1.6,lightPollution:this.lightPollution,sun:{altitudeDeg:e.sun.altitudeDeg,azimuthDeg:e.sun.azimuthDeg,magnitude:e.sun.magnitude},moon:{altitudeDeg:e.moon.altitudeDeg,azimuthDeg:e.moon.azimuthDeg,magnitude:e.moon.magnitude,phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction)}}}pumpScatteredSky(){const e=this.scatteredSky;!e||e.tables.ready||this.scatteredSkyFrameId!==null||this.contextReleased||(this.scatteredSkyFrameId=requestAnimationFrame(()=>{this.scatteredSkyFrameId=null,!this.contextReleased&&(e.advance(this.skyHoldUntilMs?VA:GA),this.pumpScatteredSky())}))}cancelScatteredSkyPump(){this.scatteredSkyFrameId!==null&&(cancelAnimationFrame(this.scatteredSkyFrameId),this.scatteredSkyFrameId=null)}setSatellites(e){if(e.length===0&&!this.satelliteField?.count)return;this.satelliteField||(this.satelliteField=new nA(iu),this.celestialGroup.add(this.satelliteField.object));const t=this.lastAstronomy?this.magnitudeLimitOf(this.lastAstronomy):Kr(-90,this.instrumentMagnitudeGain);this.satelliteField.set(e,t,i=>this.cloudTransmission(i),(i,n)=>this.arrivingIlluminance(i,n,1)),this.render()}pickSatelliteAt(e,t){if(!this.satelliteField?.count)return;this.aimAtScreenPoint(this.raycaster,e,t);const i=Math.cos(Bg*this.camera.fov*Math.PI/180),n=this.satelliteField.nearest(this.raycaster.ray.direction,i);return n&&this.groundHides(e,t)?void 0:n}updateCelestialLight(e,t){const i=this.relativeScale,n=e.sun.altitudeDeg>=Rc,s=!n&&e.moon.altitudeDeg>=Rc,r=n?e.sun:e.moon,o=[0,0,0],l=Math.max(0,Math.sin(Math.max(r.altitudeDeg,0)*sn)),c=n||s?this.cloudTransmission(r):1,h=-Math.log(Math.max(c,1e-6)),d=Math.max(0,1/(1+.1125*h)-c),u=[0,0,0];if(n||s){const x=Mt.illuminanceOf(r.magnitude)*i,y=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,r.altitudeDeg);for(let T=0;T<3;T++)o[T]=x*y[T]*c,u[T]=x*y[T]*d*l,this.cloudBeam[T]=x*y[T];const{x:b,y:w,z:E}=bi(r.altitudeDeg,r.azimuthDeg,1);this.cloudBeamDirection.set(b,w,E)}else this.cloudBeam.fill(0);const p=this.surroundings;if(p)this.skyLight.color.setRGB(p.up[0]*i,p.up[1]*i,p.up[2]*i),this.skyLight.groundColor.setRGB(p.down[0]*i,p.down[1]*i,p.down[2]*i);else{const x=this.skyCloudTransmission(),y=t.skyIrradiance,b=[0,1,2].map(E=>y[E]*x+u[E]);this.skyLight.color.setRGB(b[0],b[1],b[2]);const w=We.GROUND_ALBEDO;this.skyLight.groundColor.setRGB(w*(b[0]+o[0]*l),w*(b[1]+o[1]*l),w*(b[2]+o[2]*l))}if(this.skyLight.intensity=1,!n&&!s){this.celestialLight.intensity=0;return}const{x:f,y:v,z:m}=bi(r.altitudeDeg,r.azimuthDeg,1);this.lightDirection.set(f,v,m),this.placeShadowFrustum();const g=Math.max(o[0],o[1],o[2],1e-30);this.celestialLight.color.setRGB(o[0]/g,o[1]/g,o[2]/g),this.celestialLight.intensity=g,this.celestialLight.castShadow=this.decorGroups.size>0||this.bodySystem.any}lightDirection=new R(0,1,0);placeShadowFrustum(){const e=this.lightDirection,t=this.celestialLight.shadow.camera,i=(t.right-t.left)/this.celestialLight.shadow.mapSize.x,n=this.shadowAxisX.crossVectors(this.shadowUp,e);n.lengthSq()<1e-8&&n.set(1,0,0),n.normalize();const s=this.shadowAxisY.crossVectors(e,n).normalize(),r=this.shadowWorld.set(this.bodyOrigin.x,0,this.bodyOrigin.z),o=c=>{const h=r.dot(c)/i;return(h-Math.round(h))*i},l=this.celestialLightTarget.position.set(0,0,0).addScaledVector(n,o(n)).addScaledVector(s,o(s));this.celestialLightTarget.updateMatrixWorld(),this.celestialLight.position.copy(l).addScaledVector(e,this.shadowLightDistanceM)}fitShadowToBodies(){const e=this.bodySystem.reachFrom(this.camera.position),t=Math.min(LA,Math.max(nr,Math.ceil(e*1.25/20)*20));if(t===this.shadowHalfExtentM)return;this.shadowHalfExtentM=t;const i=this.celestialLight.shadow,n=i.camera;n.left=-t,n.right=t,n.top=t,n.bottom=-t;const s=Math.max(Wo,t*(Wo/nr));this.shadowLightDistanceM=Math.max(pn,s+10),n.near=this.shadowLightDistanceM-s,n.far=this.shadowLightDistanceM+s,n.updateProjectionMatrix(),i.bias=-.03/(2*s);const r=t>2*nr?2048:1024;i.mapSize.x!==r&&(i.mapSize.set(r,r),i.map?.dispose(),i.map=null),this.placeShadowFrustum()}shadowHalfExtentM=nr;shadowLightDistanceM=pn;shadowUp=new R(0,1,0);shadowAxisX=new R;shadowAxisY=new R;shadowWorld=new R;skyCloudTransmission(){let e=this.cloudTransmission({altitudeDeg:90,azimuthDeg:0}),t=1;for(const i of[20,45,70]){const n=i*sn,s=Math.sin(n)*Math.cos(n)*2;for(let r=0;r<360;r+=60)e+=s*this.cloudTransmission({altitudeDeg:i,azimuthDeg:r}),t+=s}return e/t}cloudBeam=[0,0,0];cloudBeamDirection=new R(0,1,0);get relativeScale(){return this.scatteredSky?.relativeScale??1/Ae.semiSaturation(Ae.DAYLIGHT_ANCHOR_CD_M2)}setMeteorShower(e,t,i){this.meteorSystem||(this.meteorSystem=new Ai,this.celestialGroup.add(this.meteorSystem.object)),this.meteorSystem.setShower(e,t,i)}get meteorSchedule(){return this.meteorSystem?.schedule??[]}meteorMidpoint(e){return this.meteorSystem?.midpointOf(e)}updateMeteors(e){this.meteorSystem?.update(e)}setInstrument(e){this.projectionKind=e.projection,this.grain=e.detailUm!==void 0&&e.frame?{detailUm:e.detailUm,frameHeightMm:e.frame.heightMm}:void 0;const t=St.starPointsOf(e),i=St.flareArtifactsOf(e);t===this.starPoints&&i===this.lensFlareArtifactIntensity||(this.starPoints=t,this.lensFlareArtifactIntensity=i,this.lensFlare&&(this.lensFlare.uniforms.uStarPoints.value=t,this.lensFlare.uniforms.uFlareIntensity.value=i),this.render())}magnitudeLimitOf(e){const t=le.starLossMagnitudes(e.sun.altitudeDeg,{phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction),altitudeDeg:e.moon.altitudeDeg},this.lightPollution);return Kr(e.sun.altitudeDeg,this.instrumentMagnitudeGain,t)}setLightPollution(e){e!==this.lightPollution&&(this.lightPollution=e,this.lastAstronomy&&this.scatteredSky?.update(this.scatteredSkyState(this.lastAstronomy)))}setInstrumentGain(e,t=!1){this.instrumentMagnitudeGain=Number.isFinite(e)?e:0,this.scatteredSky?.setInstrument(t,this.instrumentMagnitudeGain)}render(){this.restatingExposure||(this.sceneVersion++,this.frameDirty=!0,this.resolution.noteChange(performance.now()),!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()})))}drawIfDirty(){if(!(!this.frameDirty||this.contextReleased)){if(this.skyColoursStale&&this.lastAstronomy&&this.applySkyColours(this.lastAstronomy),this.compileBeforeNextDraw){this.compileBeforeNextDraw=!1;const e=this.compileOffThread().then(()=>{this.compiling===e&&(this.compiling=void 0,this.render())});this.compiling=e;return}if(!this.compiling){if(this.skyHoldUntilMs){if(!this.sceneSettled()&&performance.now()<this.skyHoldUntilMs){this.firstFrameReady()&&this.refreshReflections(!0);return}this.skyHoldUntilMs=null,this.iceHalos?.setUrgent(!1),this.onFirstFrameHold?.(!1),this.skyColoursStale&&this.lastAstronomy&&this.applySkyColours(this.lastAstronomy)}this.frameDirty=!1,this.refreshReflections(),this.resolution.beginDrawing();try{this.renderOnce(),this.drawn++}finally{this.resolution.endDrawing()}this.developingExposure&&this.startExposure()}}}drawn=0;get framesDrawn(){return this.drawn}compileBeforeNextDraw=!1;compiling;static COMPILE_WAIT_MS=1500;compileOffThread(){const e=this.renderer.compile(this.scene,this.camera);if(!this.renderer.extensions.get("KHR_parallel_shader_compile"))return Promise.resolve();const t=this.renderer.properties,i=performance.now()+Wt.COMPILE_WAIT_MS;return new Promise(n=>{const s=()=>{if(this.contextReleased)return n();for(const r of e){const o=t.get(r)?.currentProgram;(!o||o.isReady())&&e.delete(r)}e.size===0||performance.now()>i?n():setTimeout(s,10)};s()})}compileNextFrameOffThread(){this.compileBeforeNextDraw=!0}contextReleased=!1;releaseContext(){this.contextReleased||(this.contextReleased=!0,this.stopTwinkle(),this.cancelFlush(),this.cancelExposure(),this.compiling=void 0,this.cancelScatteredSkyPump(),this.renderer.forceContextLoss())}restoreContext(){this.contextReleased&&(this.contextReleased=!1,this.renderer.forceContextRestore(),this.scatteredSky?.invalidate(),this.pumpScatteredSky(),this.compileNextFrameOffThread(),this.syncAnimationLoop(),this.render())}cancelFlush(){this.flushFrameId!==null&&(cancelAnimationFrame(this.flushFrameId),this.flushFrameId=null)}frame(e){if(this.animationsRunning){const t=this.lastFrameTimeMs===void 0?0:e-this.lastFrameTimeMs;this.lastFrameTimeMs=e,this.hasAnimations()&&this.animate(e,t);const i=this.resolution.ratioFor(e,t);i!==this.renderer.getPixelRatio()&&this.applyPixelRatio(i)}this.drawIfDirty()}refreshReflections(e=!1){this.reflections??=new gn(this.renderer);const t=[...this.compassSprites,...this.lensFlare?[this.lensFlare.mesh]:[],...[...this.glareSprites.values()].map(o=>o.mesh)],i=[...this.starTiers.map(o=>o.points),...[...this.bodyMeshes.values()].filter(o=>o instanceof ks),...[this.rainSystem?.points,this.precipitationPoints,this.rainSplashSystem?.points].filter(o=>o!==void 0)],n=this.relativeScale,s=this.sceneVersion,r=this.reflections.refresh(this.renderer,this.scene,this.camera.position,this.bodySystem.reflectors,t,[...this.decorGroups.values()],s,i,o=>this.measureSurroundings(o,n,s),e?0:gn.REFRESH_MS);r===void 0||this.reflectionTimer||(this.reflectionTimer=setTimeout(()=>{this.reflectionTimer=void 0,this.frameDirty=!0,!(this.animationFrameId!==null||this.framesDriven||this.flushFrameId!==null)&&(this.flushFrameId=requestAnimationFrame(()=>{this.flushFrameId=null,this.drawIfDirty()}))},r))}measureSurroundings(e,t,i){const n=this.lastAstronomy;if(!n)return;this.probeIrradiance??=new hl;const s=[n.sun,n.moon].filter(r=>r.altitudeDeg>-2).map(r=>bi(r.altitudeDeg,r.azimuthDeg,1));this.probeIrradiance.measure(this.renderer,e,s,r=>{const o=p=>p.map(f=>f/t),l=this.surroundings,c={up:o(r.up),down:o(r.down)},h=(p,f)=>p.some((v,m)=>Math.abs(v-f[m])>.03*Math.max(Math.abs(v),Math.abs(f[m]),1e-12)),d=!l||h(l.up,c.up)||h(l.down,c.down);d&&(this.surroundings=c),this.eyeSettledVersion=-1;const u=this.scatteredSky?.adaptToSurroundings(Math.exp(r.logAverage)/t)??!1;if(!d&&!u){this.eyeSettledVersion=i,this.skyHoldUntilMs&&this.redrawHeld();return}this.skyColoursStale=!0,this.render()})}lightUnderClouds(e){const t=this.surroundings;if(!t)return;const i=e.sun.altitudeDeg>=Rc?e.sun:e.moon.altitudeDeg>=Rc?e.moon:void 0;if(!i)return;const n=this.relativeScale/Math.PI;return{through:this.cloudTransmission(i),radiance:[t.up[0]*n,t.up[1]*n,t.up[2]*n]}}probeIrradiance;surroundings;animate(e,t){const i=Math.min(t/1e3,hT);this.twinkleSeconds=e/1e3,this.updateTwinkle(this.twinkleSeconds),this.updatePrecipitation(e/1e3,i),this.updateRain(i),this.lensFlare&&(this.lensFlare.uniforms.uTime.value=e/1e3),this.developingExposure||(this.frameDirty=!0)}setMaxPixelRatio(e){this.resolution.maximum=e,this.renderer.getPixelRatio()>this.resolution.maximum&&this.applyPixelRatio(this.resolution.maximum)}applyPixelRatio(e){this.renderer.setPixelRatio(e),this.renderer.setSize(this.cssSize.width,this.cssSize.height,!1),this.frameDirty=!0}setExposure(e,t){const i=t?Math.max(1,Math.round(e)):1;if(i===this.exposureInstants&&(i===1||t===this.exposureInstantAt)){this.exposureInstantAt=t;return}this.exposureInstants=i,this.exposureInstantAt=t,i===1&&(this.cancelExposure(),this.exposureAccumulation?.dispose(),this.exposureAccumulation=void 0),this.render()}renderOnce(e){if(this.onMapSubjectBounds){const r=this.mapSubjectBounds();e?this.exposureSubjectBounds.push(...r):this.onMapSubjectBounds(r)}const t=this.usableEquidistantPass();this.phenomena.setCloudLayers(this.layeredClouds?.volumes??[]),this.phenomena.place(this.camera,(r,o,l)=>this.directionAtScreenPoint(r,o,l),r=>this.screenPointOf(r)),this.references.place(this.camera);const i=Math.max(this.phenomena.furthestM,this.references.furthestM)*1.2;i>this.camera.far&&(this.camera.far=i,this.camera.updateProjectionMatrix()),ji.uRodShare.value=this.scatteredSky?.rodShare??0,ji.uRelativeScale.value=this.relativeScale,this.applyMedium();const n=this.references.any||this.phenomena.any||this.compassSprites.some(r=>r.visible);ji.uHasOverlay.value=n?1:0;const s=n?r=>this.renderOverlayPasses(r):void 0;if(this.skyGlow?.setDestinationEncoded(!1),!t){this.updateLensFlarePosition();const r=this.usableDepthOfFieldPass();r?r.render(this.renderer,this.scene,this.camera,s,e):this.usableFinishPass().render(this.renderer,this.scene,this.camera,s,void 0,e);return}t.render(this.renderer,this.scene,this.camera,this.camera.fov,()=>this.updateLensFlarePosition(),s,r=>{this.lensFlare&&(this.lensFlare.mesh.visible=r?!1:this.sunVisible)},()=>this.renderDazzleOnResample(),e)}renderOverlayPasses(e=this.camera){this.renderReferencesPass(e),this.renderPhenomenaPass(e),this.renderHudPass(e)}renderHudPass(e){if(!this.compassSprites.some(n=>n.visible))return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,e.layers.set(Ng),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}renderReferencesPass(e){if(!this.references.any)return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,e.layers.set(Sb),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}setReferences(e,t){this.references.set(e.map(i=>{const n=this.documentRelative(i.src);return n===i.src?i:{...i,src:n}}),void 0,t)}referenceAspect(e){return this.references.aspectOf(e)}directionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}referenceFailedToLoad(e){return this.references.failedToLoad(this.documentRelative(e))}setReferencesShown(e){this.references.setShown(e),this.render()}setReferenceView(e,t){this.references.setView(e,t),this.render()}renderPhenomenaPass(e){if(!this.phenomena.any)return;const t=this.renderer.autoClear,i=this.renderer.shadowMap.autoUpdate;this.renderer.autoClear=!1,this.renderer.shadowMap.autoUpdate=!1,this.renderer.clearDepth();for(const n of this.decorObjects){const s=this.decorGroups.get(n.id);s&&Wt.markDecorDepth(s,n.kind!=="crop")}e.layers.set(eu),this.scene.overrideMaterial=this.decorDepthMaterial,this.renderer.render(this.scene,e),this.scene.overrideMaterial=null,e.layers.set(xb),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}decorDepthMaterial=new Rn({colorWrite:!1});static markDecorDepth(e,t){e.traverse(i=>{if(!(i instanceof De))return;const n=Array.isArray(i.material)?i.material[0]:i.material;t&&n.depthWrite!==!1&&n.blending!==Yi?i.layers.enable(eu):i.layers.disable(eu)})}startExposure(){this.cancelExposure(),this.exposureSubjectBounds=[];const e=this.renderer.getDrawingBufferSize(new ye),t=Math.max(1,Math.round(e.x)),i=Math.max(1,Math.round(e.y));this.exposureAccumulation||(this.exposureAccumulation=new S_(t,i)),this.exposureAccumulation.resize(t,i),this.exposureAccumulation.open(this.renderer),this.exposureInstantsDone=0,this.exposureFrameId=requestAnimationFrame(()=>this.developExposure())}developExposure(){this.exposureFrameId=null;const e=this.exposureInstantAt,t=this.exposureAccumulation;if(!e||!t)return;const i=1/this.exposureInstants;this.resolution.beginDrawing();const n=performance.now()+Wt.EXPOSURE_BUDGET_MS,s=Wt.exposureInstantsPerFrame(t.instantTarget.scene.width*t.instantTarget.scene.height);let r=0;do{this.restateAt(e,this.exposureInstantsDone);const o=this.compassSprites.map(l=>l.visible);for(const l of this.compassSprites)l.visible=!1;try{this.renderOnce(t.instantTarget)}finally{this.compassSprites.forEach((l,c)=>{l.visible=o[c]})}t.add(this.renderer,i),this.exposureInstantsDone++,r++}while(this.exposureInstantsDone<this.exposureInstants&&performance.now()<n&&r<s);this.restateAt(e,0),this.presentExposure(),this.resolution.endDrawing(),this.exposureInstantsDone<this.exposureInstants&&(this.exposureFrameId=requestAnimationFrame(()=>this.developExposure()))}presentExposure(){if(ji.uRodShare.value=this.scatteredSky?.rodShare??0,ji.uHasOverlay.value=1,ji.uRelativeScale.value=this.relativeScale,this.applyMedium(),this.exposureAccumulation?.develop(this.renderer,this.exposureInstants/this.exposureInstantsDone),this.onMapSubjectBounds?.(this.exposureSubjectBounds),!this.compassSprites.some(s=>s.visible))return;const e=new Hs,t=new Yn(-1,1,1,-1,0,2),i=Cc/(Vg*Math.tan(Wg*sn/2));for(const s of this.compassSprites){if(!s.visible)continue;const r=s.getWorldPosition(new R).sub(this.camera.position).normalize(),o=this.screenPointOf(r);if(!o)continue;const l=new Po(s.material);l.position.set(o.ndcX,o.ndcY,-1),l.scale.set(i/this.camera.aspect,i,1),e.add(l)}const n=this.renderer.autoClear;this.renderer.autoClear=!1;try{this.renderer.render(e,t)}finally{this.renderer.autoClear=n}}onMapSubjectBounds;exposureSubjectBounds=[];static MAP_SUBJECT_NEAREST_M=8;mapSubjectBounds(){const e=[],t=new Qi,i=new R,n=this.camera.getWorldDirection(new R),s=this.decorObjects.filter(o=>o.track?.length||o.kind==="aircraft"||o.kind==="vehicle").map(o=>this.decorGroups.get(o.id)),r=this.bodySystem.reflectors.map(o=>o.holder);for(const o of[...s,...r]){if(!o?.visible||(t.setFromObject(o),t.isEmpty())||t.distanceToPoint(this.camera.position)<Wt.MAP_SUBJECT_NEAREST_M)continue;let l=1/0,c=-1/0,h=1/0,d=-1/0;for(const u of[t.min.x,t.max.x])for(const p of[t.min.y,t.max.y])for(const f of[t.min.z,t.max.z]){if(i.set(u,p,f).sub(this.camera.position).normalize(),i.dot(n)<=0)continue;const v=this.screenPointOf(i);if(!v)continue;const m=(v.ndcX+1)/2,g=(1-v.ndcY)/2;l=Math.min(l,m),c=Math.max(c,m),h=Math.min(h,g),d=Math.max(d,g)}l=Math.max(l,0),c=Math.min(c,1),h=Math.max(h,0),d=Math.min(d,1),c>=l&&d>=h&&e.push({x:l,y:h,width:c-l,height:d-h})}return e}restateAt(e,t){this.restatingExposure=!0;try{e(t)}finally{this.restatingExposure=!1}}cancelExposure(){this.exposureFrameId!==null&&(cancelAnimationFrame(this.exposureFrameId),this.exposureFrameId=null)}setLensOptics(e){const t=this.lensOptics;this.lensOptics=e,(t===void 0!=(e===void 0)||t&&e&&(t.focalLengthMm!==e.focalLengthMm||t.fNumber!==e.fNumber||t.focusDistance!==e.focusDistance||t.frameHeightMm!==e.frameHeightMm))&&(e&&this.depthOfFieldPass&&this.depthOfFieldPass.setLens(e.focalLengthMm,e.fNumber,e.focusDistance,e.frameHeightMm),this.render())}usableDepthOfFieldPass(){const e=this.lensOptics;if(!e)return;const t=this.renderer.getDrawingBufferSize(new ye),i=Math.max(1,Math.round(t.x)),n=Math.max(1,Math.round(t.y));return this.depthOfFieldPass||(this.depthOfFieldPass=new dl(i,n)),this.depthOfFieldPass.resize(i,n),this.depthOfFieldPass.setLens(e.focalLengthMm,e.fNumber,e.focusDistance,e.frameHeightMm),this.depthOfFieldPass}usableFinishPass(){const e=this.renderer.getDrawingBufferSize(new ye);return this.finishPass?this.finishPass.resize(e.x,e.y):this.finishPass=new Kf(e.x,e.y),this.finishPass}usableEquidistantPass(){if(this.projectionKind!=="equidistant")return;const e=this.renderer.getDrawingBufferSize(new ye);return this.equidistantPass?this.equidistantPass.resize(e.x,e.y):this.equidistantPass=new Jt(e.x,e.y),this.equidistantPass}aimAtScreenPoint(e,t,i){const n=this.projectionKind==="equidistant"?this.equidistantPass:void 0;if(!n){e.setFromCamera(new ye(t,i),this.camera);return}const s=n.directionFor(t,i,this.camera.fov);this.camera.updateMatrixWorld(),e.ray.origin.setFromMatrixPosition(this.camera.matrixWorld),e.ray.direction.set(s.x,s.y,s.z).applyQuaternion(this.camera.quaternion).normalize(),e.camera=this.camera}pickCloudAt(e,t){return this.layeredClouds?.pickInstance(this.cloudDirectionAt(e,t))}cloudDirectionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}directionAtScreenPoint(e,t,i){return this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t),i.copy(this.ufoOcclusionRaycaster.ray.direction)}screenPointOf(e){this.camera.updateMatrixWorld();const t=this.screenPointScratch.copy(e).applyQuaternion(this.screenPointQuaternion.copy(this.camera.quaternion).invert()),i=this.renderer.getDrawingBufferSize(this.screenPointSize),n=i.x/Math.max(i.y,1);if(this.projectionKind==="equidistant"&&this.equidistantPass)return Jt.ndcFor(t,this.camera.fov,n);if(t.z>=0)return;const s=Math.tan(this.camera.fov/2*sn);return{ndcX:t.x/-t.z/(n*s),ndcY:t.y/-t.z/s}}screenPointScratch=new R;screenPointQuaternion=new Xi;screenPointSize=new ye;updateLensFlarePosition(){const e=this.lensFlare;if(!e)return;if(!this.sunVisible){e.mesh.visible=!1;return}if(this.camera.updateMatrixWorld(),this.lensFlareScratch.copy(this.sunWorldPosition).applyMatrix4(this.camera.matrixWorldInverse),this.lensFlareScratch.z>0){e.mesh.visible=!1;return}const t=this.sunVisibleFraction();if(t<=0){e.mesh.visible=!1;return}e.mesh.visible=!0,this.sunUnhiddenFraction=t,this.applyDazzleStrength();const i=this.lensFlareScratch.copy(this.sunWorldPosition).project(this.camera);e.uniforms.uLensPosition.value.set(i.x,i.y);const n=this.renderer.domElement.width,s=this.renderer.domElement.height;e.uniforms.uResolution.value.set(n,s),e.uniforms.uTanHalfFov.value=Math.tan(this.camera.fov*Math.PI/360)}renderDazzleOnResample(){const e=this.lensFlare;if(!e||!this.sunVisible)return;const t=this.sunVisibleFraction();if(t<=0)return;this.sunUnhiddenFraction=t,this.applyDazzleStrength();const i=this.screenPointOf(this.lensFlareScratch.copy(this.sunWorldPosition).sub(this.camera.position).normalize());if(!i)return;const n=this.renderer.getDrawingBufferSize(this.screenPointSize);e.uniforms.uLensPosition.value.set(i.ndcX,i.ndcY),e.uniforms.uResolution.value.set(n.x,n.y),e.uniforms.uEquidistant.value=1,e.uniforms.uHalfFovRad.value=this.camera.fov*Math.PI/360;const s=this.renderer.autoClear;this.renderer.autoClear=!1,e.mesh.visible=!0,e.mesh.layers.enable(Og),this.camera.layers.set(Og),this.renderer.render(this.scene,this.camera),this.camera.layers.set(0),e.mesh.visible=!1,e.uniforms.uEquidistant.value=0,this.renderer.autoClear=s}sunVisibleFraction(){this.raycastableDecor().length>0&&this.scene.updateMatrixWorld();let e=0;for(const t of Yg)this.isSunOccluded(t[0],t[1])||e++;return e/Yg.length}raycastableDecor(){const e=[];for(const t of this.decorObjects){if(t.kind==="crop")continue;const i=this.decorGroups.get(t.id);i&&e.push(i)}return e}isSunOccluded(e=0,t=0){const i=this.raycastableDecor();if(!this.groundMesh&&!this.terrainMesh&&i.length===0)return!1;const n=this.sunOcclusionDirectionScratch.copy(this.sunWorldPosition).sub(this.camera.position),s=n.length();if(n.normalize(),e!==0||t!==0){const o=Math.PI/180,l=this.sunOcclusionRightScratch.set(n.z,0,-n.x);l.lengthSq()<1e-9&&l.set(1,0,0),l.normalize();const c=this.sunOcclusionUpScratch.crossVectors(l,n).normalize();n.addScaledVector(l,e*o).addScaledVector(c,t*o).normalize()}if(this.groundBlocks(this.camera.position,n,s))return!0;if(i.length===0)return!1;this.sunOcclusionRaycaster.set(this.camera.position,n);const r=this.sunOcclusionRaycaster.intersectObjects(i,!0)[0];return r!==void 0&&r.distance<s}groundBlocks(e,t,i,n=0){const s=this.groundMesh;if(s&&t.y<0){const m=(s.position.y-e.y)/t.y;if(m>n&&m<i){const g=e.x+t.x*m-s.position.x,x=e.z+t.z*m-s.position.z;if(Math.hypot(g,x)<=this.groundRadius)return!0}}const r=this.terrainMesh;if(!r)return!1;const o=r.geometry.getAttribute("position"),l=Math.round(Math.sqrt(o.count));if(l<2||l*l!==o.count)return!1;const c=[r.position.x+o.getX(0),r.position.x+o.getX(l-1)],h=[r.position.z+o.getZ(0),r.position.z+o.getZ((l-1)*l)],d=Math.min(Math.abs(c[1]-c[0]),Math.abs(h[1]-h[0]))/(l-1);if(!(d>0))return!1;let u=n,p=i;for(const[m,g,x]of[[e.x,t.x,c],[e.z,t.z,h]]){const y=Math.min(x[0],x[1]),b=Math.max(x[0],x[1]);if(Math.abs(g)<1e-12){if(m<y||m>b)return!1;continue}const w=(y-m)/g,E=(b-m)/g;u=Math.max(u,Math.min(w,E)),p=Math.min(p,Math.max(w,E))}if(!(p>u))return!1;const f=Math.hypot(t.x,t.z),v=Math.min(4096,Math.max(8,Math.ceil((p-u)*f/(d/2))));for(let m=0;m<=v;m++){const g=u+(p-u)*m/v,x=e.x+t.x*g,y=e.y+t.y*g,b=e.z+t.z*g;if(y<=this.groundYUnder(x,b))return!0}return!1}setPhenomena(e,t){this.phenomena.set(e,t)}decorDistancesAt(e,t,i){this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t),this.ufoOcclusionRaycaster.near=Fg;let n,s;for(const r of this.decorObjects){if(r.kind==="crop")continue;const o=this.decorGroups.get(r.id);if(!o)continue;const l=this.ufoOcclusionRaycaster.intersectObject(o,!0)[0];l&&(r.occludesSourceIds?.includes(i)?n=n===void 0?l.distance:Math.max(n,l.distance):s=s===void 0?l.distance:Math.min(s,l.distance))}return{behindM:n,inFrontM:s}}pickBodyAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.hitAreas.entries()],n=this.raycaster.intersectObjects(i.map(([,s])=>s))[0];if(n&&!this.groundHides(e,t))return i.find(([,s])=>s===n.object)?.[0]}pickStarAt(e,t){if(this.namedStars.length===0)return;this.aimAtScreenPoint(this.raycaster,e,t);const i=this.raycaster.ray.direction,n=Math.cos(Bg*this.camera.fov*Math.PI/180);let s,r=n;for(const o of this.namedStars){const l=o.direction.dot(i);l>r&&(r=l,s={star:o.star,altitudeDeg:o.altitudeDeg})}return s&&this.groundHides(e,t)?void 0:s}groundHides(e,t){if(!this.terrainMesh&&!this.groundMesh)return!1;this.aimAtScreenPoint(this.ufoOcclusionRaycaster,e,t);const i=this.ufoOcclusionRaycaster.ray;return this.groundBlocks(i.origin,i.direction,1/0,Fg)}bodyScreenBox(e){const t=this.bodySystem.boxOf(e,this.bodyBoxScratch);if(!t)return;let i=1/0,n=1/0,s=-1/0,r=-1/0;for(let o=0;o<8;o++){const l=this.bodyCornerScratch.set(o&1?t.max.x:t.min.x,o&2?t.max.y:t.min.y,o&4?t.max.z:t.min.z),c=this.screenPointOf(l.sub(this.camera.position).normalize());if(!c)return;i=Math.min(i,c.ndcX),s=Math.max(s,c.ndcX),n=Math.min(n,c.ndcY),r=Math.max(r,c.ndcY)}return{minX:i,minY:n,maxX:s,maxY:r}}bodyBoxScratch=new Qi;bodyCornerScratch=new R;pickPlacedBodyAt(e,t){return this.aimAtScreenPoint(this.raycaster,e,t),this.bodySystem.pick(this.raycaster)}pickDecorAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.decorGroups.entries()],n=this.raycaster.intersectObjects(i.map(([,r])=>r),!0)[0];if(!n)return;let s=n.object;for(;s&&!i.some(([,r])=>r===s);)s=s.parent;return i.find(([,r])=>r===s)?.[0]}dispose(){this.stopTerrainFade(),this.stopTwinkle(),this.cancelFlush(),this.phenomena.clear(),this.references.disposeAll(),this.terrainBuildToken++,this.cancelScatteredSkyPump(),this.skyMesh&&this.skyGradientMaterial&&(this.skyMesh.material=this.skyGradientMaterial),this.scatteredSky?.dispose(),this.scatteredSky=void 0,this.disposeMesh(this.skyMesh),this.disposeMesh(this.groundMesh),this.disposeMesh(this.terrainMesh),this.roadSystem.dispose(),this.disposeCloudSystem(),this.disposeCirrus(),this.disposePrecipitationPoints(),this.disposeRain(),this.disposeStarTiers(),this.meteorSystem?.dispose(),this.meteorSystem=void 0,this.iceHalos?.dispose(),this.iceHalos=void 0,this.rainbow?.dispose(),this.rainbow=void 0,this.skyGlow?.dispose(),this.skyGlow=void 0,this.depthOfFieldPass?.dispose(),this.finishPass?.dispose(),this.depthOfFieldPass=void 0,this.cancelExposure(),this.exposureAccumulation?.dispose(),this.exposureAccumulation=void 0,this.cometTail?.dispose(),this.cometTail=void 0,this.satelliteField?.dispose(),this.satelliteField=void 0,this.lightningBolt?.dispose(),this.lightningBolt=void 0;for(const e of this.bodyMeshes.values())this.disposeMesh(e);this.bodyMeshes.clear(),this.novaKeys.clear();for(const e of[...this.hitAreas.keys()])this.disposeHitArea(e);for(const e of[...this.glareSprites.keys()])this.disposeGlare(e);this.disposeCompassLabels();for(const e of this.decorGroups.values())ui.dispose(e);this.decorGroups.clear(),this.streetlightLights.clear(),this.lensFlare&&(this.lensFlare.mesh.removeFromParent(),this.lensFlare.mesh.geometry.dispose(),this.lensFlare.mesh.material.dispose(),this.lensFlare=void 0),this.renderer.dispose()}animationsRunning=!1;startTwinkle(){if(this.animationFrameId!==null||!this.needsAnimationLoop())return;this.cancelFlush();const e=t=>{this.frame(t),this.animationFrameId=requestAnimationFrame(e)};this.animationFrameId=requestAnimationFrame(e)}setAnimationsRunning(e,t=!1){const i=e&&t;if(e===this.animationsRunning&&i===this.framesDriven)return;const n=this.animationsRunning;this.animationsRunning=e,this.framesDriven=i,e&&!n&&(this.lastFrameTimeMs=void 0,this.resolution.reset());const s=!e&&this.renderer.getPixelRatio()!==this.resolution.maximum;s&&this.applyPixelRatio(this.resolution.maximum),this.syncAnimationLoop(),(!e||this.frameDirty)&&this.render(),s&&this.drawIfDirty()}stopTwinkle(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}needsAnimationLoop(){return this.animationsRunning&&!this.framesDriven&&this.hasAnimations()}hasAnimations(){return this.starTiers.length>0||this.precipitationPoints!==void 0||this.rainSystem!==void 0||this.lensFlare!==void 0&&this.sunVisible}syncAnimationLoop(){this.needsAnimationLoop()?this.startTwinkle():this.stopTwinkle()}buildSky(e){const t=this.skyMesh?.geometry??new mi(Ic,32,16),i=t.attributes.position,n=t.getAttribute("color")??new vt(new Float32Array(i.count*3),3);for(let r=0;r<i.count;r++){const{altitudeDeg:o,azimuthDeg:l}=A_(i.getX(r),i.getY(r),i.getZ(r)),c=k_(o,l,e.azimuthDeg,e.altitudeDeg);n.setXYZ(r,c[0],c[1],c[2])}if(n.needsUpdate=!0,this.skyMesh)return;t.setAttribute("color",n);const s=new Rn({vertexColors:!0,side:Zt,fog:!1,depthWrite:!1});this.skyGradientMaterial=s,this.skyMesh=new De(t,s),this.skyMesh.renderOrder=-1,this.celestialGroup.add(this.skyMesh)}buildGround(){const e=Ug(this.observerElevationM);if(this.groundMesh&&e===this.groundRadius)return;this.disposeMesh(this.groundMesh),this.groundRadius=e;const t=new If(this.groundRadius,48),i=new wo({color:new se(tu,tu,tu),fog:!0});ra.fadeRim(i,this.groundRadius*.75,this.groundRadius),this.groundMesh=new De(t,i),this.groundMesh.rotation.x=-Math.PI/2,this.groundMesh.position.y=0,this.groundMesh.receiveShadow=!0,this.scene.add(this.groundMesh),this.applyGroundDepthWrite()}applyGroundDepthWrite(){const e=this.groundMesh?.material;if(!e)return;const t=this.terrainMesh===void 0;e.depthWrite!==t&&(e.depthWrite=t,e.needsUpdate=!0)}cloudTransmission(e,t=!1){const i=`${e.altitudeDeg.toFixed(1)}:${e.azimuthDeg.toFixed(1)}:${Math.round(this.cloudOffsetM.x/10)}:${Math.round(this.cloudOffsetM.z/10)}:${Math.round(this.observerElevationM)}:`+Object.entries(this.cloudLayerOffsetsM).map(([r,o])=>`${r}=${Math.round(o.x/10)},${Math.round(o.z/10)}`).join(";")+(t?":water":""),n=this.cloudTransmissionMemo.get(i);if(n!==void 0)return n;this.cloudTransmissionMemo.size>256&&this.cloudTransmissionMemo.clear();const s=this.computeCloudTransmission(e,t);return this.cloudTransmissionMemo.set(i,s),s}cloudTransmissionMemo=new Map;computeCloudTransmission(e,t=!1){const i=bi(e.altitudeDeg,e.azimuthDeg,1);if(this.layeredClouds)return this.layeredClouds.transmissionAt(i,t);let n=1;const s=this.lowerCloudCover();s>0&&(n*=1-wt.alphaAt(i,Math.abs(this.cloudLayerOffset()),s,this.cloudFieldOffset)*UA);const r=t?0:this.weather.highCloudCover??0;return r>0&&(n*=1-wt.iceAlphaAt(i,Dc,r,this.cloudFieldOffset)*BA),Math.max(0,Math.min(1,n))}setBodyMesh(e,t,i,n,s){if(t.altitudeDeg<tf){this.disposeMesh(this.bodyMeshes.get(e)),this.bodyMeshes.delete(e),this.disposeHitArea(e),this.disposeGlare(e),e==="sun"&&(this.sunVisible=!1);return}const{x:r,y:o,z:l}=bi(t.altitudeDeg,t.azimuthDeg,pn),c=sg(t.altitudeDeg),h=this.cloudTransmission(t),d=this.arrivingIlluminance(t,s,h),u=Wt.unitLuminance(n);let p=this.bodyMeshes.get(e);if(e==="sun"){const f=Math.PI*(i/pn)**2,v=[0,1,2].map(m=>Math.min(Wt.BRIGHTEST_RELATIVE,u[m]*d[m]/f));p instanceof De||(this.disposeMesh(p),p=new De(new mi(1,16,16),new Rn({fog:!1})),this.celestialGroup.add(p),this.bodyMeshes.set(e,p)),p.material.color.setRGB(v[0],v[1],v[2]),p.scale.setScalar(i)}else{if(!(p instanceof ks)){this.disposeMesh(p);const m=new dt;m.setAttribute("position",new nt(new Float32Array(3),3)),m.setAttribute("color",new nt(new Float32Array(3),3)),p=new ks(m,ot.material(cl[cl.length-1].size,!1)),ot.track(p),this.celestialGroup.add(p),this.bodyMeshes.set(e,p)}const f=p.geometry.getAttribute("color"),v=e.startsWith("comet:")?this.pointToPixel():1;f.setXYZ(0,u[0]*d[0]*v,u[1]*d[1]*v,u[2]*d[2]*v),f.needsUpdate=!0}if(p.position.set(r,o,l),this.setHitArea(e,r,o,l,i),e==="sun"&&(this.sunArriving=d),e.startsWith("comet:")?this.disposeGlare(e):e!=="sun"&&this.setGlare(e,r,o,l,d,i/pn*(180/Math.PI),Wt.POINT_VEIL_HOLD_DEG),e==="sun"){this.sunVisible=!0;const f=this.celestialGroup.scale.x;this.sunWorldPosition.set(r*f,o*f+this.celestialGroup.position.y,l*f),this.lensFlare||(this.lensFlare=iA(),this.lensFlare.uniforms.uOpacity.value=$A,this.lensFlare.uniforms.uFlareIntensity.value=this.lensFlareArtifactIntensity,this.lensFlare.uniforms.uStarPoints.value=this.starPoints,this.scene.add(this.lensFlare.mesh)),this.applyLensFlareTint(c)}}setMoonMesh(e,t){const i="moon";if(e.magnitude>t||e.altitudeDeg<tf){this.disposeMesh(this.bodyMeshes.get(i)),this.bodyMeshes.delete(i),this.disposeHitArea(i),this.disposeGlare(i);return}const{x:n,y:s,z:r}=bi(e.altitudeDeg,e.azimuthDeg,pn),o=this.cloudTransmission(e),l=this.arrivingIlluminance(e,e.magnitude,o),c=Math.max(Za(e.phase.illuminatedFraction,0,1),.01)*Math.PI*(Xo/pn)**2,h=new se(...l.map(p=>Math.min(Wt.BRIGHTEST_RELATIVE,p/c))),d=`${e.phase.phaseFraction<.5}:${Math.round(Za(e.phase.illuminatedFraction,0,1)*500)}`;let u=this.bodyMeshes.get(i);if(u instanceof Po)u.userData.phaseKey!==d&&(u.material.map?.dispose(),u.material.map=cv(e.phase),u.userData.phaseKey=d),u.material.color.copy(h);else{this.disposeMesh(u),u=new Po(new Xc({map:cv(e.phase),color:h,fog:!1})),u.userData.phaseKey=d;const p=Xo*2;u.scale.set(p,p,1),this.celestialGroup.add(u),this.bodyMeshes.set(i,u)}u.position.set(n,s,r),this.setHitArea(i,n,s,r,Xo),this.setGlare(i,n,s,r,l,Xo/pn*(180/Math.PI))}bentLight(e,t,i=NA){const[n,s,r]=this.arrivingIlluminance(e,t,1),o=Math.max(s,1e-30);return{scale:s/i,tint:[n/o,1,r/o]}}static litBySky(e,t){return Wt.litScratch.setRGB(e.r*t[0],e.g*t[1],e.b*t[2])}static litScratch=new se;pointToPixel(){const e=Math.max(1,this.renderer.getDrawingBufferSize(this.pointScratch).y),t=(this.camera.fov*Math.PI/180/e)**2;return Math.min(1,ot.CONE_SOLID_ANGLE/t)}pointScratch=new ye;static POINT_VEIL_HOLD_DEG=.25;static BRIGHTEST_RELATIVE=6e4;arrivingIlluminance(e,t,i){const n=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,e.altitudeDeg),s=Mt.illuminanceOf(t)*i*this.relativeScale;return[n[0]*s,n[1]*s,n[2]*s]}static unitLuminance(e){const t=Gn.luminanceOf([e.r,e.g,e.b]);return t>0?[e.r/t,e.g/t,e.b/t]:[1,1,1]}setHitArea(e,t,i,n,s){let r=this.hitAreas.get(e);if(!r){const l=new Xc({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1});r=new Po(l),this.celestialGroup.add(r),this.hitAreas.set(e,r)}r.position.set(t,i,n);const o=s*OA;r.scale.set(o,o,1)}disposeHitArea(e){const t=this.hitAreas.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.hitAreas.delete(e))}setGlare(e,t,i,n,s,r,o=0){if(Gi.radiusDeg(Gi.K*Math.max(...s))<=Math.max(r,o)){this.disposeGlare(e);return}let l=this.glareSprites.get(e);l||(l=new Gi(`veil:${e}`),this.celestialGroup.add(l.mesh),this.glareSprites.set(e,l)),l.shine({x:t,y:i,z:n},s,r,o)}disposeGlare(e){const t=this.glareSprites.get(e);t&&(t.mesh.removeFromParent(),t.dispose(),this.glareSprites.delete(e))}buildComet(e,t){const i=e&&`comet:${e.id}`;if(!e||!i||e.magnitude>t){this.disposeComet();return}this.cometKey&&this.cometKey!==i&&this.disposeComet();const n=el(e.magnitude,t),s=Vd(n);this.cometKey=i,this.setBodyMesh(i,e.position,nu*(.5+.5*n),new se(s,s,s),e.magnitude),this.cometTail||(this.cometTail=new mn(pn),this.celestialGroup.add(this.cometTail.object)),this.cometTail.set(e.position,e.tailEnd,n)}buildIceHalos(e,t){this.iceHalos||(this.iceHalos=new Fi,this.iceHalos.setCloudOffset(this.cloudFieldOffset),this.iceHalos.onReady=()=>this.render(),this.celestialGroup.add(this.iceHalos.object)),this.iceHalos.setUrgent(!!this.skyHoldUntilMs);const i=this.highCloudCover(),n=this.layeredClouds?.cirrusMask;if(this.iceHalos.setCloudOffset(n?.offset??this.cloudFieldOffset),this.layeredClouds?.hostHalo(this.iceHalos),i===void 0){this.iceHalos.update({x:0,y:1,z:0},-1,0,[1,1,1],{cover:0,layerHeight:Dc},0);return}const s=this.lowerCloudCover(),r=lt.deckLitUntilDeg(lt.DECK_HEIGHT_M),o=e.altitudeDeg>-r?e:t,l=this.bentLight(o,o===e?zt.SUN_MAGNITUDE:t.magnitude),c=lt.strength(i,s,o.altitudeDeg,r)*l.scale,{x:h,y:d,z:u}=bi(o.altitudeDeg,o.azimuthDeg,1);this.iceHalos.update({x:h,y:d,z:u},o.altitudeDeg,c,l.tint,{cover:n?.cover??i,layerHeight:n?.layerHeight??Dc},n?.iceCrystalAlignment??this.weather.iceCrystalAlignment??.65)}buildRainbow(e,t){this.rainbow||(this.rainbow=new $r,this.celestialGroup.add(this.rainbow.object));const i=this.weather.precipitationType==="rain"?this.weather.precipitationIntensity*(this.weather.precipitationAmount??1):0,n=e.altitudeDeg>0,s=n?e:t,r=this.lowerCloudCover(),o=this.bentLight(s,n?zt.SUN_MAGNITUDE:t.magnitude,FA),l=zi.strength(i,r,s.altitudeDeg)*o.scale;if(l<=0){this.rainbow.update({x:0,y:1,z:0},0,[1,1,1],!0);return}const{x:c,y:h,z:d}=bi(s.altitudeDeg,s.azimuthDeg,1);this.rainbow.update({x:c,y:h,z:d},l,o.tint,n)}buildSkyGlow(e,t){if(!e.frame){this.skyGlow?.hide();return}this.skyGlow||(this.skyGlow=new lr,this.skyGlow.onReady=()=>this.render(),this.celestialGroup.add(this.skyGlow.object));const{date:i,observer:n}=e.frame,s=bn.galactic(i,n),r=Wt.directionOf(s.centre),o=Wt.directionOf(s.rotation),l=Wt.directionOf(s.pole),c=new Xe().set(r.x,r.y,r.z,o.x,o.y,o.z,l.x,l.y,l.z),h=Wt.directionOf(bn.eclipticPole(i,n)),d=Wt.directionOf(e.sun),u=d.clone().addScaledVector(h,-d.dot(h)).normalize();this.skyGlow.update({galactic:c,eclipticPole:h,sunLongitude:u,sunDirection:d,sunAltitudeDeg:e.sun.altitudeDeg,moon:{direction:Wt.directionOf(e.moon),altitudeDeg:e.moon.altitudeDeg,phaseAngleDeg:le.phaseAngleOf(e.moon.phase.illuminatedFraction)},lightPollution:this.lightPollution,skyColor:new R(t[0],t[1],t[2])})}static directionOf(e){const{x:t,y:i,z:n}=bi(e.altitudeDeg,e.azimuthDeg,1);return new R(t,i,n).normalize()}buildNovae(e,t){const i=new Set;for(const n of e){if(n.magnitude>t)continue;const s=`nova:${n.id}`;i.add(s);const r=el(n.magnitude,t),o=Vd(r);this.setBodyMesh(s,n.position,nu*(.5+.5*r),new se(o,o,o),n.magnitude)}for(const n of this.novaKeys)i.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n));this.novaKeys=i}disposeComet(){this.cometTail?.set(void 0,void 0,0),this.cometKey&&(this.disposeMesh(this.bodyMeshes.get(this.cometKey)),this.bodyMeshes.delete(this.cometKey),this.disposeHitArea(this.cometKey),this.disposeGlare(this.cometKey),this.cometKey=void 0)}buildPlanets(e,t){const i=new Set(["sun","moon",...this.cometKey?[this.cometKey]:[],...this.novaKeys]);for(const n of e){if(n.magnitude>t)continue;i.add(n.body);const s=el(n.magnitude,t),r=Vd(s),o=fT[n.body]??new se(1,1,1),l=new se(o.r*r,o.g*r,o.b*r),c=nu*(.5+.5*s);this.setBodyMesh(n.body,n.position,c,l,n.magnitude)}for(const n of[...this.bodyMeshes.keys()])i.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n))}buildStars(e,t){if(this.namedStars=[],!e||e.catalog.count===0){this.disposeStarTiers(),this.syncAnimationLoop();return}const{catalog:i,date:n,observer:s}=e,r=new zn(n,s),o=pu(1337),l=[],c=this.siteElevationM+this.observerElevationM;for(let h=0;h<i.count;h++){const d=i.mag[h];if(d>t)break;const{altitudeDeg:u,azimuthDeg:p}=r.position(i.ra[h],i.dec[h]);if(u<zg)continue;const{x:f,y:v,z:m}=bi(u,p,iu);l.push({x:f,y:v,z:m,brightness:el(d,t),illuminance:this.air.transmittanceFromSpace(c,u).map(g=>g*Mt.illuminanceOf(d)),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}for(const h of O5){if(h.mag>t)continue;const{altitudeDeg:d,azimuthDeg:u}=r.position(h.raHours,h.decDeg);if(d<zg)continue;const{x:p,y:f,z:v}=bi(d,u,iu);this.namedStars.push({star:h,direction:new R(p,f,v).normalize(),altitudeDeg:d})}this.starTiers=cl.map((h,d)=>{const u=l.filter(w=>Y1(w.brightness)===d),p=new Float32Array(u.length*3),f=new Float32Array(u.length),v=new Float32Array(u.length*3),m=new Float32Array(u.length),g=new Float32Array(u.length);u.forEach((w,E)=>{p[E*3]=w.x,p[E*3+1]=w.y,p[E*3+2]=w.z,f[E]=w.brightness,v.set(w.illuminance,E*3),m[E]=w.phase,g[E]=w.speedFactor});const x=new dt;x.setAttribute("position",new nt(p,3)),x.setAttribute("seed",new nt(m.map(w=>w/(2*Math.PI)),1));const y=new nt(new Float32Array(u.length*3),3);x.setAttribute("color",y);let b=this.starTiers[d]?.points;return b?(b.geometry.dispose(),b.geometry=x):(b=new ks(x,ot.material(h.size)),ot.track(b),this.celestialGroup.add(b)),{points:b,colorAttribute:y,brightness:f,illuminance:v,phase:m,speedFactor:g}}),this.starColoursRelative=Number.NaN,this.updateTwinkle(this.twinkleSeconds),this.startTwinkle()}twinkleSeconds=0;starColoursRelative=Number.NaN;updateTwinkle(e){ot.setTime(e);const t=this.relativeScale;if(t!==this.starColoursRelative){this.starColoursRelative=t;for(const i of this.starTiers){const n=i.colorAttribute.array;for(let s=0;s<i.brightness.length;s++)n[s*3]=i.illuminance[s*3]*t,n[s*3+1]=i.illuminance[s*3+1]*t,n[s*3+2]=i.illuminance[s*3+2]*t;i.colorAttribute.needsUpdate=!0}}}buildClouds(){if(this.cloudRendering==="volume"||this.weather.cloudLayers!==void 0){this.layeredClouds||(this.disposeCloudSystem(),this.layeredClouds=new so(this.celestialGroup,su,this.cloudRendering)),this.disposeCirrus(),this.layeredClouds.hostHalo(this.iceHalos),this.layeredClouds.update(this.weather,this.observerElevationM+1.6),this.layeredClouds.setOffsets(this.cloudOffsetM,this.cloudLayerOffsetsM),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}const e=this.lowerCloudCover();if(e<=0){this.disposeCloudSystem();return}const t=this.weather.cloudDarkness,i=new se(Pr[0]+(ou[0]-Pr[0])*t,Pr[1]+(ou[1]-Pr[1])*t,Pr[2]+(ou[2]-Pr[2])*t);if(this.cloudMesh&&this.cloudUniforms){this.cloudUniforms.coverage.value=e,this.cloudUniforms.baseColor.value.copy(i),this.syncCloudLayer(),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}this.disposeCloudSystem();const{material:n,uniforms:s}=Tp(i,e,Math.abs(this.cloudLayerOffset()));this.cloudMaterial=n,this.cloudUniforms=s,s.fieldOffset.value.copy(this.cloudFieldOffset),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);const r=Ip(su);this.cloudMesh=new De(r,n),this.faceCloudDeck(this.cloudMesh,this.cloudLayerOffset()),this.celestialGroup.add(this.cloudMesh)}lowerCloudCover(){return this.weather.cloudLayers?1-this.weather.cloudLayers.filter(e=>e.type!=="cirrus").reduce((e,t)=>e*(1-t.coverage),1):this.weather.lowerCloudCover??this.weather.cloudCover}highCloudCover(){return this.weather.cloudLayers?1-this.weather.cloudLayers.filter(e=>e.type==="cirrus").reduce((e,t)=>e*(1-t.coverage),1):this.weather.highCloudCover}buildCirrus(){const e=this.weather.highCloudCover??0;if(this.layeredClouds||e<=0){this.disposeCirrus();return}if(this.cirrusMesh&&this.cirrusUniforms){this.cirrusUniforms.coverage.value=e,this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);return}const{material:t,uniforms:i}=Tp(new se(...Pr),e,Dc,1);this.cirrusMaterial=t,this.cirrusUniforms=i,i.fieldOffset.value.copy(this.cloudFieldOffset),this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.cirrusMesh=new De(Ip(su*1.04),t),this.cirrusMesh.renderOrder=qg-1,this.celestialGroup.add(this.cirrusMesh)}disposeCirrus(){this.cirrusMesh&&(this.cirrusMesh.removeFromParent(),this.cirrusMesh.geometry.dispose()),this.cirrusMaterial?.dispose(),this.cirrusMesh=void 0,this.cirrusMaterial=void 0,this.cirrusUniforms=void 0}syncCloudLayer(){this.layeredClouds?.update(this.weather,this.observerElevationM+1.6);const e=this.cloudLayerOffset();this.cloudUniforms&&(this.cloudUniforms.layerHeight.value=Math.abs(e)),this.cloudMesh&&this.faceCloudDeck(this.cloudMesh,e)}faceCloudDeck(e,t){const i=t<0;e.scale.y=i?-1:1,e.renderOrder=i?so.deckOrder(!0,0,1):qg}cloudLayerOffset(){const t=((this.weather.cloudBaseM??1e3)-this.observerElevationM)*ru;return Math.sign(t||1)*Math.max(Math.abs(t),ZA)}updateCloudLighting(e,t){const i=[this.cloudUniforms,this.cirrusUniforms].filter(d=>d!==void 0);if(i.length===0&&!this.layeredClouds)return;const n=this.cloudBeamDirection,[s,r,o]=this.cloudBeam.map(d=>d/Math.PI),l=this.lightningLevel*uu*this.relativeScale/Math.PI,c=new se(t[0]+es.r*l,t[1]+es.g*l,t[2]+es.b*l);this.layeredClouds?.setLighting(n.clone(),new se(s,r,o),c,c);const h=Math.max(0,n.y);for(const d of i)d.sunDir.value.copy(n),d.sunColor.value.setRGB(s*h,r*h,o*h),d.ambientColor.value.copy(c)}disposeCloudSystem(){this.layeredClouds?.dispose(),this.layeredClouds=void 0,this.cloudMesh&&(this.cloudMesh.removeFromParent(),this.cloudMesh.geometry.dispose()),this.cloudMaterial?.dispose(),this.cloudMesh=void 0,this.cloudMaterial=void 0,this.cloudUniforms=void 0}disposePrecipitationPoints(){this.precipitationPoints&&(this.precipitationPoints.removeFromParent(),this.precipitationPoints.geometry.dispose(),this.precipitationPoints.material.dispose()),this.precipitationPoints=void 0,this.precipitationPositions=void 0,this.precipitationPhase=void 0,this.precipitationSpeedJitter=void 0,this.precipitationWobbleFreqJitter=void 0,this.precipitationWobbleAmpJitter=void 0,this.precipitationBounceRemaining=void 0,this.precipitationBounceHeight=void 0,this.precipitationBounceDuration=void 0,this.precipitationBounceVelX=void 0,this.precipitationBounceVelZ=void 0,this.precipitationUniforms=void 0}precipitationCount(e){return sT(e,this.weather.precipitationIntensity,this.weather.precipitationAmount??1)}updatePrecipitationDensity(){const e=[[this.rainSystem?.points,hu],[this.rainSplashSystem?.points,Lr],[this.precipitationPoints,this.weather.precipitationType==="none"||this.weather.precipitationType==="rain"?void 0:lu[this.weather.precipitationType].poolSize]];for(const[t,i]of e)t&&i!==void 0&&t.geometry.setDrawRange(0,this.precipitationCount(i));this.rainSystem&&(this.rainSystem.uniforms.uSpeedStreak.value=jg(du(this.weather.precipitationIntensity)))}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=lu[e],i=t.poolSize,n=pu(9001),s=new Float32Array(i*3),r=new Float32Array(i),o=new Float32Array(i),l=new Float32Array(i),c=new Float32Array(i),h=new Float32Array(i),d=new Float32Array(i),u=new Float32Array(i),p=new Float32Array(i),f=new Float32Array(i);for(let y=0;y<i;y++){const b=n()*Math.PI*2,w=n()*t.radiusM;s[y*3]=Math.cos(b)*w,s[y*3+1]=Ga+n()*(t.topYM-Ga),s[y*3+2]=Math.sin(b)*w,r[y]=n()*Math.PI*2,o[y]=.7+n()*.6,l[y]=.6+n()*.8,c[y]=.4+n()*1.2}const v=new dt;v.setAttribute("position",new nt(s,3)),v.setDrawRange(0,this.precipitationCount(i));const m={uSize:{value:t.size},uScale:{value:.5*this.renderer.domElement.height},uOpacity:{value:t.opacity},uColor:{value:t.color.clone()},uHazeColor:{value:new se(...this.baseFogColor)},uTexture:{value:mT(e)},uNearFocusDistance:{value:tv},uHazeDistance:{value:t.radiusM*iv}},g=new ut({uniforms:m,vertexShader:wT,fragmentShader:xT,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const x=new ks(v,g);x.renderOrder=au,this.scene.add(x),this.precipitationPoints=x,this.precipitationPositions=s,this.precipitationPhase=r,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=d,this.precipitationBounceDuration=u,this.precipitationBounceVelX=p,this.precipitationBounceVelZ=f}updatePrecipitation(e,t){if(!this.precipitationPoints||!this.precipitationPositions||!this.precipitationPhase||!this.precipitationSpeedJitter)return;this.precipitationUniforms?.uHazeColor.value.setRGB(...this.baseFogColor);const i=this.weather.precipitationType;if(i==="none"||i==="rain")return;const n=lu[i];if(this.precipitationUniforms?.uColor.value.copy(Wt.litBySky(n.color,this.baseFogColor)),t<=0)return;const s=this.weather.windDirectionDeg*sn,r=Math.sin(s)*this.weather.windSpeed*n.driftSensitivity,o=-Math.cos(s)*this.weather.windSpeed*n.driftSensitivity,l=this.precipitationPositions,c=this.precipitationPhase,h=this.precipitationSpeedJitter,d=this.precipitationWobbleFreqJitter,u=this.precipitationWobbleAmpJitter,p=this.precipitationBounceRemaining,f=this.precipitationBounceHeight,v=this.precipitationBounceDuration,m=this.precipitationBounceVelX,g=this.precipitationBounceVelZ,x=i==="snow"&&d&&u,y=i==="hail"&&p&&f&&v&&m&&g;for(let b=0,w=0;b<l.length;b+=3,w++){if(y&&p[w]>0){if(p[w]-=t,p[w]<=0){const M=Math.random()*Math.PI*2,I=Math.sqrt(Math.random())*n.radiusM;l[b]=Math.cos(M)*I,l[b+1]=n.topYM,l[b+2]=Math.sin(M)*I,p[w]=0}else{const M=1-p[w]/v[w];l[b+1]=Ga+Math.sin(Math.PI*M)*f[w],l[b]=Pc(l[b]+(r+m[w])*t,n.radiusM),l[b+2]=Pc(l[b+2]+(o+g[w])*t,n.radiusM)}continue}let E=0,T=0;if(x){const M=aT*d[w],I=oT*u[w];E=Math.sin(e*M*Math.PI*2+c[w])*I,T=Math.sin(e*M*Math.PI*2+c[w]+Math.PI/2)*I}if(l[b]+=(r+E)*t,l[b+1]-=n.fallSpeedMPerS*h[w]*t,l[b+2]+=(o+T)*t,l[b]=Pc(l[b],n.radiusM),l[b+2]=Pc(l[b+2],n.radiusM),l[b+1]<Ga){if(y){f[w]=ov*(.2+Math.random()*1.6),v[w]=lT*(.5+Math.random()),p[w]=v[w];const A=Math.random()*Math.PI*2,D=cT*(f[w]/ov);m[w]=Math.cos(A)*D,g[w]=Math.sin(A)*D,l[b+1]=Ga;continue}const M=Math.random()*Math.PI*2,I=Math.sqrt(Math.random())*n.radiusM;l[b]=Math.cos(M)*I,l[b+1]=n.topYM,l[b+2]=Math.sin(M)*I}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=du(this.weather.precipitationIntensity);this.rainSystem=J5({count:hu,radiusM:Yo,heightM:JA,bottomYM:$g,overallSpeed:QA,color:Zg.clone(),opacity:.85,visibleCount:this.precipitationCount(hu),speedStreak:jg(e),pixelSize:sv,seed:9001,nearFocusDistanceM:tv,hazeDistanceM:Yo*iv,hazeColor:new se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.syncRainSlant(),this.rainSystem.points.renderOrder=au,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=pu(4242),t=new Float32Array(Lr*3),i=new Float32Array(Lr),n=new Float32Array(Lr),s=new Float32Array(Lr);for(let h=0;h<Lr;h++){const d=e()*Math.PI*2,u=Math.sqrt(e())*Yo;t[h*3]=Math.cos(d)*u,t[h*3+1]=$g,t[h*3+2]=Math.sin(d)*u,n[h]=lv(Jg,Qg),i[h]=e()*n[h],s[h]=i[h]/n[h]}const r=new dt;r.setAttribute("position",new nt(t,3));const o=new nt(s,1);r.setAttribute("aLife",o),r.setDrawRange(0,this.precipitationCount(Lr));const l=new ut({uniforms:{uMaxSize:{value:nT},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:ev.clone()},uTexture:{value:bT()}},vertexShader:yT,fragmentShader:MT,transparent:!0,depthWrite:!1}),c=new ks(r,l);c.renderOrder=au,this.scene.add(c),this.rainSplashSystem={points:c,positions:t,life:i,duration:n,lifeAttribute:o}}disposeRain(){this.rainSystem&&(this.rainSystem.points.removeFromParent(),this.rainSystem.points.geometry.dispose(),this.rainSystem.points.material.dispose()),this.rainSystem=void 0,this.disposeRainSplashes()}disposeRainSplashes(){this.rainSplashSystem&&(this.rainSplashSystem.points.removeFromParent(),this.rainSplashSystem.points.geometry.dispose(),this.rainSplashSystem.points.material.dispose()),this.rainSplashSystem=void 0}syncRainSlant(){const e=this.rainSystem;if(!e)return;const t=this.weather.windDirectionDeg*sn;e.uniforms.uDropVelocity.value.set(Math.sin(t)*this.weather.windSpeed,-du(this.weather.precipitationIntensity),-Math.cos(t)*this.weather.windSpeed);const i=this.renderer.getSize(this.rainViewportSize);e.uniforms.uAspect.value=i.x/Math.max(i.y,1)}updateRain(e){const t=this.rainSystem;if(!t)return;t.uniforms.uTime.value+=e,t.uniforms.uHazeColor.value.setRGB(...this.baseFogColor),t.uniforms.uColor.value.copy(Wt.litBySky(Zg,this.baseFogColor));const i=this.rainSplashSystem?.points.material?.uniforms?.uColor;i&&i.value.copy(Wt.litBySky(ev,this.baseFogColor));const n=this.weather.windDirectionDeg*sn,s=Math.sin(n)*this.weather.windSpeed,r=-Math.cos(n)*this.weather.windSpeed,o=Yo,l=h=>(h%(o*2)+o*3)%(o*2)-o;t.uniforms.uWindOffset.value.set(l(t.uniforms.uWindOffset.value.x+s*e),l(t.uniforms.uWindOffset.value.y+r*e)),this.syncRainSlant(),this.camera.getWorldDirection(this.rainCameraDirection);const c=Math.abs(this.rainCameraDirection.y);if(Math.abs(c-this.rainLastVerticalFacing)>.001){this.rainLastVerticalFacing=c;const h=rv+(1-rv)*(1-c),d=av+(1-av)*(1-c);t.uniforms.uUvSquash.value=d,t.uniforms.uPixelSize.value=sv*h*(.5+.5*d)}this.updateRainSplashes(e)}updateRainSplashes(e){const t=this.rainSplashSystem;if(!t||e<=0)return;const{positions:i,life:n,duration:s,lifeAttribute:r}=t,o=r.array;for(let l=0,c=0;l<i.length;l+=3,c++){if(n[c]+=e,n[c]>=s[c]){const h=Math.random()*Math.PI*2,d=Math.sqrt(Math.random())*Yo;i[l]=Math.cos(h)*d,i[l+2]=Math.sin(h)*d,s[c]=lv(Jg,Qg),n[c]=0}o[c]=n[c]/s[c]}t.points.geometry.attributes.position.needsUpdate=!0,r.needsUpdate=!0}setLightning(e){this.lightningFlashes=e}updateLightning(e){let t,i=0;if(this.lightningArmed)for(const l of this.lightningFlashes){if(l.t>e)break;const c=yi.brightnessAt(l,e);c>i&&(t=l,i=c)}const n=t?Math.min(1,Math.max(.1,(uT/t.distanceM)**2)):0,s=i*n*(t?.cloudToGround?1:.6);if(s===this.lightningLevel&&!t?.cloudToGround)return;if(this.lightningLevel=s,this.scene.fog){const l=this.scene.fog,c=s*uu*this.relativeScale/Math.PI;l.color.setRGB(this.airlight[0]+es.r*c,this.airlight[1]+es.g*c,this.airlight[2]+es.b*c)}const r=this.skyMesh?.material?.color;if(r){const l=s*pT;r.setRGB(1+l*es.r,1+l*es.g,1+l*es.b)}if(this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.lightningLight.intensity=s*uu*this.relativeScale,t){const{x:l,y:c,z:h}=bi(25,t.azimuthDeg,pn);this.lightningLight.position.set(l,c,h)}this.lightningBolt||(this.lightningBolt=new hr(pn*.95),this.celestialGroup.add(this.lightningBolt.object));const o=this.weather.cloudBaseM??1500;this.lightningBolt.set(t,i,o),this.render()}buildCompassLabels(){this.compassSprites=HA.map(e=>{const t=na.point(e,this.compassPoints),i=new Xc({map:ST(t),depthTest:!1,fog:!1}),n=new Po(i),{x:s,y:r,z:o}=bi(0,e,Vg);return n.position.set(s,r,o),n.scale.set(Cc,Cc,1),n.renderOrder=WA,n.layers.set(Ng),n.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(n),n}),this.updateCompassScale()}updateCompassScale(){const e=this.camera.fov*Math.PI/360,t=Wg*Math.PI/360,i=Cc*Math.tan(e)/Math.tan(t);for(const n of this.compassSprites)n.scale.set(i,i,1)}disposeCompassLabels(){for(const e of this.compassSprites)e.removeFromParent(),e.material.map?.dispose(),e.material.dispose();this.compassSprites=[]}disposeStarTiers(){for(const e of this.starTiers)this.disposeMesh(e.points);this.starTiers=[]}disposeMesh(e){if(!e)return;e.removeFromParent(),"geometry"in e&&e.geometry.dispose();const t=e.material;for(const i of Array.isArray(t)?t:[t]){const n=i.map;n&&n.dispose(),i.dispose()}}}const sn=Math.PI/180;function Za(a,e,t){return Math.max(e,Math.min(t,a))}function lv(a,e){return a+Math.random()*(e-a)}function Pc(a,e){return(a%(e*2)+e*3)%(e*2)-e}let Lc,kc;function mT(a){switch(a){case"snow":return gT();case"hail":return vT()}}function gT(){if(Lc)return Lc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,0.95)"),i.addColorStop(.5,"rgba(255,255,255,0.55)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),Lc=new Xs(e),Lc}function vT(){if(kc)return kc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,1)"),i.addColorStop(.55,"rgba(255,255,255,0.95)"),i.addColorStop(.8,"rgba(255,255,255,0.4)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),kc=new Xs(e),kc}let Oc;function bT(){if(Oc)return Oc;const a=64,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createRadialGradient(a/2,a/2,0,a/2,a/2,a/2);return i.addColorStop(0,"rgba(255,255,255,0)"),i.addColorStop(.4,"rgba(255,255,255,0)"),i.addColorStop(.6,"rgba(255,255,255,0.9)"),i.addColorStop(.8,"rgba(255,255,255,0.25)"),i.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=i,t.fillRect(0,0,a,a),Oc=new Xs(e),Oc}const yT=`
|
|
9330
9330
|
uniform float uMaxSize;
|
|
9331
9331
|
uniform float uScale;
|
|
9332
9332
|
attribute float aLife;
|