@rr0/ufoathome 0.65.3 → 0.65.4
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.
|
@@ -1989,7 +1989,7 @@ input[type=range] {
|
|
|
1989
1989
|
pointer-events: none;
|
|
1990
1990
|
white-space: nowrap;
|
|
1991
1991
|
}
|
|
1992
|
-
`;class tl{constructor(e){this.points=e}static STATIONARY_M=5;static of(e){const t=[];for(const i of e.witnessTrack.allKeyframes){const{lat:n,lng:s,headingDeg:a}=i.pose;n===void 0||s===void 0||t.push({t:i.t,lat:n,lng:s,headingDeg:a})}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 tl(t)}get moved(){return this.spanM>tl.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:Ds(0,-s,i,n).lat,south:Ds(0,s,i,n).lat,east:Ds(s,0,i,n).lng,west:Ds(-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 hi{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 STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(e){const t=tl.of(e);if(!t||t.points.length<2)return;const i=[];let n=0;for(let s=1;s<t.points.length;s++){const a=t.points[s-1],o=t.points[s],l=(o.t-a.t)/1e3;if(l<=0)continue;const c=pi(o.lat,o.lng,a.lat,a.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),f=u/l,p=hi.stepHzFor(f);i.push({startMs:a.t,endMs:o.t,speedMPerS:f,eastUnit:u>0?h/u:0,northUnit:u>0?d/u:0,stepHz:p,stepsBefore:n,rampsIn:!0,rampsOut:!0}),n+=p*l}for(let s=0;s<i.length;s++){const a=i[s-1],o=i[s+1];i[s].rampsIn=a===void 0||a.stepHz===0,i[s].rampsOut=o===void 0||o.stepHz===0}if(i.some(s=>s.stepHz>0))return new hi(i,1-(e.instrument.stabilization??0))}static stepHzFor(e){return e<hi.SLOWEST_WALK_M_PER_S||e>hi.FASTEST_WALK_M_PER_S?0:hi.REFERENCE_STEP_HZ*Math.pow(e/hi.REFERENCE_SPEED_M_PER_S,hi.CADENCE_EXPONENT)}offsetAt(e){const t=this.stretches.find(l=>e>=l.startMs&&e<=l.endMs);if(!t||t.stepHz===0)return hi.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),a=hi.SWAY_M*s*Math.cos(n/2),o=s*this.rotationPassed*t.speedMPerS/hi.REFERENCE_SPEED_M_PER_S;return{eastM:a*t.northUnit,northM:-a*t.eastUnit,upM:hi.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:hi.HEAD_ROLL_DEG*o*Math.cos(n/2),pitchDeg:-1.25*o*Math.cos(n),yawDeg:hi.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,a=e.endMs-t,o=Math.min(e.rampsIn?s/n:1,e.rampsOut?a/n:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class Qy{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)}}const jp=.05,Jp=.08,Qp=2,e0=3,e2=14,t2=.02;class i2{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;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}}this.context.state==="suspended"&&this.context.resume()}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.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,jp);for(const{param:s,ratio:a}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*a,i.sampleRate/2-1),n,jp)}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 a=e.createGain();return a.gain.value=0,i.connect(s),n.connect(s),s.connect(a).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,a],sources:[i,n],volumeParam:a.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(),a=e.createGain();return a.gain.value=0,n.connect(s).connect(i.frequency),i.connect(a).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,a],sources:[i,n],volumeParam:a.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(e.destination),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 a=e.createBufferSource();a.buffer=this.crackleEnvelope(e),a.loop=!0;const o=e.createGain();return o.gain.value=0,a.connect(o).connect(s.gain),i.connect(n).connect(s).connect(e.destination),i.start(),a.start(),{key:t,nodes:[n,s,o],sources:[i,a],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 a=n.createBufferSource();a.buffer=s,a.loop=!0;const o=n.createGain();o.gain.value=0,a.connect(o).connect(n.destination),a.start(),this.voice={key:t,nodes:[o],sources:[a],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*Qp),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*Qp),e.sampleRate),i=t.getChannelData(0);let n=0,s=0;for(let a=0;a<i.length;a++)n=(n+.02*(Math.random()*2-1))/1.02,i[a]=n,s=Math.max(s,Math.abs(n));if(s>0)for(let a=0;a<i.length;a++)i[a]/=s;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*e0),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(t2*e.sampleRate)),s=Math.round(e0*e2);for(let a=0;a<s;a++){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,Jp/3);const n=i+Jp;for(const s of e.sources){s.onended=()=>{s.disconnect();for(const a of e.nodes)a.disconnect()};try{s.stop(n)}catch{s.disconnect();for(const a of e.nodes)a.disconnect()}}}}function jr(r){return[...r].sort((e,t)=>e.t-t.t)}function t0(r,e){let t;for(const i of jr(r)){if(i.t>e)break;t=i}return t}const Dh=Math.PI/180,wl=180/Math.PI;class Fn{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*Dh/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new Fn(e.projection,t,i)}degToPx(e){const t=e*Dh;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*wl:2*Math.atan(e/(2*this.focalPx))*wl}angleDegToRadiusPx(e){const t=e*Dh;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*wl:Math.atan(e/this.focalPx)*wl}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(rt.angularWidthDeg(e))}}class mi{static fovOf(e,t){return tt(e,t)?.fovDeg??vt.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),a={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:a};const o=t.bounds.width===0?1:n/t.bounds.width,l=t.bounds.height===0?1:s/t.bounds.height;return{...t,bounds:a,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)<mi.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<mi.UNMOVED_PX)return t;const a=this.aimFrom(e,i,n,t.bounds);return a?{...t,aim:a}:t})}static UNMOVED_PX=.01;static aimOf(e,t,i){const n=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,this.fovOf(e,t));return this.aimFrom(e,n,tt(e,t),i)}static aimFrom(e,t,i,n){if(i?.headingDeg===void 0)return;const s=this.frameCentre(e),a=t.radiusPxToAngleDeg(n.x+n.width/2-s.x),o=t.radiusPxToAngleDeg(s.y-(n.y+n.height/2));return{azimuthDeg:((i.headingDeg+a)%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 a=this.frameCentre(e),o=this.shortestArc(t.azimuthDeg-s.headingDeg),l=t.altitudeDeg-s.pitchDeg;return{x:a.x+this.offAxisPx(n,o)-i.width/2,y:a.y-this.offAxisPx(n,l)-i.height/2}}static frameCentre(e){return{x:vt.frameWidthPx(e.instrument,rt.CANVAS_HEIGHT_PX)/2,y:rt.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=mi.OFF_CANVAS_DEG?Math.sign(t)*mi.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=vt.frameWidthPx(t,rt.CANVAS_HEIGHT_PX)/2,s=vt.frameWidthPx(e.instrument,rt.CANVAS_HEIGHT_PX)/2,a=rt.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=Fn.of(t,rt.CANVAS_HEIGHT_PX,c),d=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,l);e.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,p=f.x+f.width/2-n,v=f.y+f.height/2-a,m=Math.hypot(p,v),g=m===0?1:d.angleDegToRadiusPx(h.radiusPxToAngleDeg(m))/m,w={...f,x:s+p*g-f.width/2,y:a+v*g-f.height/2};return{...u,shape:{...u.shape,bounds:w}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:i}=e;for(const n of[...i.allKeyframes]){const s=tt(e,n.t),a=this.fovOf(e,n.t),o=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,a);i.addKeyframe(n.t,n.shapes.map(l=>({...l,shape:t(l.shape,o,s)})))}}}function n2(r){return on.restore(au(r),r.provenance)}function au(r){return mi.toAngular(r),mi.toAim(r),{version:1,id:r.id,time:r.event.time,endTime:r.event.endTime,durationSeconds:r.event.durationSeconds,utcOffsetHours:r.event.utcOffsetHours,timeZone:r.event.timeZone,place:r.event.place,witness:r.witness,testimony:r.testimony,description:r.event.description,tags:r.event.tags,timeline:r.timeline.toJSON(),witnessTrack:r.witnessTrack.toJSON(),weatherTrack:r.weatherTrack.toJSON(),soundTrack:r.soundTrack.toJSON(),weather:r.weather,decor:r.decor,milestones:r.milestones.length>0?r.milestones:void 0,references:r.references.length>0?r.references:void 0,roads:r.roads.length>0?r.roads:void 0,weatherSource:r.weatherSource,instrument:r.instrumentId,exposureSeconds:r.exposureSeconds,interpretation:r.interpretation}}function s2(r){const{recording:e,provenance:t}=on.strip(r),i=a2(e);return i.provenance=t,i}function a2(r){const e=new lh({eventType:"sighting",time:r.time,endTime:r.endTime,durationSeconds:r.durationSeconds,utcOffsetHours:r.utcOffsetHours,timeZone:r.timeZone,place:r.place,description:r.description,tags:r.tags},oh.fromJSON(r.timeline),r.witnessTrack?jo.fromJSON(r.witnessTrack):new jo,r.weatherTrack?Jo.fromJSON(r.weatherTrack):new Jo,r.soundTrack?Qo.fromJSON(r.soundTrack):new Qo,r.witness&&{id:r.witness.id,title:r.witness.title,lastName:r.witness.lastName,firstNames:r.witness.firstNames},r.id,r.weather,r.decor??[],r.weatherSource,r.instrument,r.exposureSeconds??r.witnessTrack?.keyframes.map(t=>t.pose.exposureSeconds).find(t=>t!==void 0),jr(r.milestones??[]),r.references??[],r.roads??[]);return e.testimony=r.testimony,e.interpretation=r.interpretation,mi.toBounds(e),mi.toPosition(e),e}class Ln{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 a=s>0?Ln.travelDegOver(e,t,i,i+n*1e3)/s/Ln.PIXELS_PER_INSTANT:0,o=Ln.flashesOver(e,n)*Ln.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(a,o));return l<2?1:Math.min(Ln.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const a of e){if(!a.track||a.track.length<2)continue;const o=Ln.directionAt(a,t,i),l=Ln.directionAt(a,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=Zr(e,i),s=n.eastM,a=n.altitudeM-t,o=n.northM,l=Math.hypot(s,a,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:a/l,z:o/l}}}function Jr(r,e){for(const t of r){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class oa{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 os{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[os.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>os.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(os.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},a=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>os.base(o)===os.base(i))??i;return n===void 0?delete s[a]:s[a]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const ru=["en","fr"],r2={en:()=>Sn(()=>Promise.resolve().then(()=>d2),void 0,import.meta.url).then(r=>r.ufoMessages_en),fr:()=>Sn(()=>import("./UfoMessages_fr-CnenjNH2.js"),[],import.meta.url).then(r=>r.ufoMessages_fr)};function o2(r){return r2[r]()}const l2={en:()=>Sn(()=>Promise.resolve().then(()=>XT),void 0,import.meta.url).then(r=>r.sightingEditorMessages_en),fr:()=>Sn(()=>import("./SightingEditorMessages_fr-DbyQTXpg.js"),[],import.meta.url).then(r=>r.sightingEditorMessages_fr)};function c2(r){return l2[r]()}function h2(r){return r==="fr"?Sn(()=>import("./TagNames_fr-Dnqq6HQ5.js"),[],import.meta.url).then(e=>e.tagNames_fr):Promise.resolve({})}const d1={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",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",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",witnessHere:"The witness, here",decorHere:"Scenery"},d2=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:d1},Symbol.toStringTag,{value:"Module"})),u1=Math.PI/180,u2=180/Math.PI,f2=256,f1=19,p2=1.25;function p1(r,e){return 156543.03392*Math.cos(r*u1)/2**e}function m1(r,e){return p1(r,e)*f2}function m2(r,e){let t=0;for(;t<f1&&m1(r,t+1)>=e;)t++;return t}function g1(r,e){return m1(r,e)}function g2(r,e){const t=e*p2;let i=0;for(;i<f1&&p1(r,i)>t;)i++;return i}function wc(r){const e=r*u1;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function v1(r,e,t){const i=2**t;return{x:(r+180)/360*i,y:wc(e)*i}}function y1(r,e,t){const i=wc(r.north),n=wc(r.south);return{x:(e-r.west)/(r.east-r.west),y:(wc(t)-i)/(n-i)}}function v2(r,e,t){const{x:i,y:n}=v1(r,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function y2(r){const e=2**r.z,t=r.x/e*360-180,i=(r.x+1)/e*360-180,n=i0(r.y,e);return{south:i0(r.y+1,e),north:n,west:t,east:i}}function i0(r,e){const t=r/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*u2}function b1(r,e,t,i){const n=v2(r,e,t),s=Math.floor(i/2),a=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)a.push({x:n.x+h,y:n.y+c,z:t});const o=a.map(y2),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:a,bounds:l}}class Ia{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 a=Math.hypot(s.x-e,s.y-t);a>s.radius||a>n||(i=s,n=a)}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.paintWitness(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=y1(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,a=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(a.x,a.y),s.arc(a.x,a.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(a.x,a.y,0,a.x,a.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:Ia.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Ia.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)}}paintWitness(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.witnessLabel&&this.targets.push({kind:"witness",label:e.witnessLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(a=>{if(!a.current)return!1;const o=pi(a.lat,a.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Ia.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Ia.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 f of n.slice().reverse())this.paintOutlinedText(f,this.width-6,s),s-=11;const a=(e.south+e.north)/2,l=Math.abs(pi(a,e.east,a,e.west).x)/this.width,c=Ia.SCALE_STEPS_M.find(f=>f/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 a=n?`${n} ${s}`:s;n&&this.ctx.measureText(a).width>t?(i.push(n),n=s):n=a}return n&&i.push(n),i}}const mn=256,n0=3,b2=32768;class M1{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),a=Math.abs(pi(e.north,n,e.south,n).z),o=m2(i,Math.max(s,a)),{tiles:l}=b1(n,i,o,n0),c=n0*mn,h=new Float32Array(c*c),d=l[0];await Promise.all(l.map(f=>this.decodeTileInto(f,d,h,c)));const u=new Float32Array(t.width*t.height);for(let f=0;f<t.height;f++){const p=e.north+(e.south-e.north)*(f/(t.height-1));for(let v=0;v<t.width;v++){const m=e.west+(e.east-e.west)*(v/(t.width-1));u[f*t.width+v]=this.sampleAt(p,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)),a=await this.fetchImpl(s);if(!a.ok)throw new Error(`Elevation tile fetch failed (${a.status}): ${s}`);const o=await createImageBitmap(await a.blob()),l=document.createElement("canvas");l.width=mn,l.height=mn;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,mn,mn),d=(e.x-t.x)*mn,u=(e.y-t.y)*mn;for(let f=0;f<mn;f++)for(let p=0;p<mn;p++){const v=(f*mn+p)*4,m=h[v]*256+h[v+1]+h[v+2]/256-b2,g=d+p,w=u+f;i[w*n+g]=m}}sampleAt(e,t,i,n,s,a){const{x:o,y:l}=v1(t,e,i),c=(o-n.x)*mn,h=(l-n.y)*mn,d=Math.max(0,Math.min(a-1,Math.floor(c))),u=Math.max(0,Math.min(a-1,Math.floor(h))),f=Math.min(a-1,d+1),p=Math.min(a-1,u+1),v=c-d,m=h-u,g=s[u*a+d],w=s[u*a+f],x=s[p*a+d],y=s[p*a+f],S=g+(w-g)*v,E=x+(y-x)*v;return S+(E-S)*m}}const xc=256,M2=5;async function w1(r,e,t,i){const n=(r.south+r.north)/2,s=(r.west+r.east)/2,a=Math.abs(pi(n,r.east,n,r.west).x),o=Math.abs(pi(r.north,s,r.south,s).z),l=Math.max(a,o),{zoom:c,gridSize:h}=w2(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=b1(s,n,c,h),f=h*xc,p=document.createElement("canvas");p.width=f,p.height=f;const v=p.getContext("2d");if(!v)throw new Error("2D canvas context unavailable");const m=d[0];await Promise.all(d.map(async y=>{const S=t(y),E=await i(S);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${S}`);const I=await createImageBitmap(await E.blob());v.drawImage(I,(y.x-m.x)*xc,(y.y-m.y)*xc)}));const g=Math.min(Math.max(e.width,e.height),f);if(g===f)return{source:p,width:f,height:f,bounds:u};const w=document.createElement("canvas");w.width=g,w.height=g;const x=w.getContext("2d");if(!x)throw new Error("2D canvas context unavailable");return x.drawImage(p,0,0,f,f,0,0,g,g),{source:w,width:g,height:g,bounds:u}}function w2(r,e,t){let i=g2(r,e/Math.max(1,t)),n=s0(r,i,e);for(;n>M2&&i>0;)i--,n=s0(r,i,e);return{zoom:i,gridSize:n,mPerPixel:g1(r,i)/xc}}function s0(r,e,t){const i=Math.ceil(t/2/g1(r,e));return Math.max(3,2*i+1)}class x1{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 w1(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class Cn{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??Cn.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"~"^(${Cn.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:Cn.surfaceOf(s),widthM:Cn.widthOf(s),points:n.map(a=>({lat:a.lat,lng:a.lon}))})}return t}queued(e){const t=Cn.queue.then(e,e);return Cn.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,Cn.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:Cn.WIDTH_M[e.highway]??Cn.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 Ch="https://ufoathome.org/roads/index.json";class Uf{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??Uf.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.65.3")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==Ch?[t,Ch]:[Ch]).map(i=>i+e)}}function x2(){return{elevation:new M1,imagery:S1(),roads:S2()}}function S1(){return new x1}function S2(){return new Uf({fallback:new Cn})}const E2=new Set,za="show-witness-map",Ur="hide-milestones";class sn extends HTMLElement{static get observedAttributes(){return["src",za,Ur]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;witnessMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=lh.create();sightingAudio=new i2;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=d1;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(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`};handleWitnessMapPointerMove=e=>{const t=this.witnessMapTargetFrom(e);if(!t){this.tooltip.hidden=!0,this.witnessMapCanvas.style.cursor="default";return}this.witnessMapCanvas.style.cursor="pointer",this.tooltip.textContent=t.label,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`};handleWitnessMapClick=e=>{const t=this.witnessMapTargetFrom(e);if(t){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}))}};witnessMapTargetFrom(e){const t=this.witnessMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.witnessMapRenderer.hitTest((e.clientX-t.left)/t.width*this.witnessMapCanvas.width,(e.clientY-t.top)/t.height*this.witnessMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Jy}</style>${jy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new el(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new Ia(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),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");for(const t of[this.timeStartLabel,this.timeEndLabel])t.addEventListener("click",()=>this.toggleTimeDisplay()),t.addEventListener("keydown",i=>{i.key!=="Enter"&&i.key!==" "||(i.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapCanvas.addEventListener("pointermove",this.handleWitnessMapPointerMove),this.witnessMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.witnessMapCanvas.addEventListener("click",this.handleWitnessMapClick),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.seekSnapArmed=!0,this.nameMarkUnder(t)}),this.seekInput.addEventListener("pointerup",()=>{this.seekSnapArmed=!1}),this.seekInput.addEventListener("pointermove",t=>this.nameMarkUnder(t)),this.seekInput.addEventListener("pointerleave",()=>this.seekInput.removeAttribute("title")),this.canvas.addEventListener("click",t=>{this.enableClickToPlay&&(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),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===za&&this.applyWitnessMapDefault(),e===Ur&&this.setMilestonesShown(!this.hasAttribute(Ur))}async loadFromSrc(e){this.sightingData=await kf.json(e)}get sightingData(){return n2(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=s2(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}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)}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.updateWitnessMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referenceOpacityInput,this.referencesButton,this.witnessMapButton];if(e)for(const i of t)e.appendChild(i);else for(const i of t)this.toolbar.appendChild(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));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?jr(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)??"",a=this.said.read(i.note),o=a?`${s} — ${a}`: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}))}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=sn.MARK_SNAP_PX/i*t;let s;for(const a of this.milestoneMarks.children){const o=Number(a.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:a.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?t0(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,!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=Ln.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<sn.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(sn.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/sn.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/sn.EXPOSURE_STEP_PX),s=Math.min(sn.MAX_TRAVEL_STEPS,Math.max(i,n)),a=[];for(let o=0;o<s;o++)a.push(t.fromMs+t.ms*o/(s-1));return a}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[a,o]of i){const l=n.get(a);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=vt.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:E2;this.canvasRenderer.setStarPoints(vt.starPointsOf(this.sighting.instrument));const n=(tt(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),a=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,a))}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,a)));if(this.selectionShown&&i.size>1){const o=gt.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,a).bounds)),this.keepWitnessMapClear(),this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintWitnessMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Of(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 Qy(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}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())}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.playPauseButton.textContent=e?"⏸":"▶";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()}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()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(za)&&this.witnessPath!==void 0)}setWitnessMapOpen(e){this.witnessMapPanel.hidden=!e,this.witnessMapPanel.removeAttribute("title"),this.witnessMapButton.setAttribute("aria-pressed",String(e)),this.updateWitnessMapButton(),e&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const t=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=t,this.witnessMapButton.setAttribute("aria-label",t)}updateWitnessMap(){const e=this.witnessPath!==void 0;if(this.witnessPath=tl.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!e&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const t=this.witnessPath.boundsAround(sn.WITNESS_MAP_MIN_SPAN_M,sn.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,t))&&(this.witnessMapBounds=t,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}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 loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const e=S1(),t=this.witnessMapBounds;try{const i=await e.getImageryTexture(t,{width:sn.WITNESS_MAP_IMAGERY_PX,height:sn.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==t)return;this.witnessMapImagery=i,this.witnessMapImageryCredit=e.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}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.keepWitnessMapClear()}keepWitnessMapClear(){if(this.witnessMapPanel.hidden)return;const e=this.witnessMapBoxPx;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],a=sn.WITNESS_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.witnessMapPanel.classList.toggle("subject-overlap",o&&l),this.witnessMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],a))&&this.witnessMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.witnessMapPanel.classList.add("on-the-left")}witnessMapBoxPx;static WITNESS_MAP_CLEARANCE_PX=24;sizeWitnessMapCanvas(){const e=this.witnessMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureWitnessMapBox(),this.witnessMapCanvas.width!==t&&(this.witnessMapCanvas.width=t,this.witnessMapCanvas.height=t)}measureWitnessMapBox(){const e=this.witnessMapPanel.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.witnessMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintWitnessMap(e){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const t=tt(this.currentSighting,e),i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?t0(this.currentSighting.milestones,e):void 0;for(const a of this.milestonesShown?jr(this.currentSighting.milestones):[]){const o=tt(this.currentSighting,a.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(a.label)??"",note:this.said.read(a.note),t:a.t,lat:o.lat,lng:o.lng,current:a===s})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,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?Fn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(vt.aspectOf(i)):void 0,markers:n,decor:this.witnessMapDecorAt(e),witnessLabel:this.said.read(this.currentSighting.witness?.title)??this.messages.witnessHere,nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}witnessMapDecorAt(e){const t=tt(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=Zr(i,e),{lat:s,lng:a}=Ds(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:a,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}witnessMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=t,this.paintWitnessMap(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=tt(this.currentSighting,this.currentTime),s=Fn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return hi.of(this.currentSighting)?.offsetAt(e)??hi.STILL}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new os(oa.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=Jr(oa.preferencesFor(this),ru);e!=="en"&&this.applyMessages(await o2(e))}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;for(const i of[this.timeStartLabel,this.timeEndLabel])i.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(i.setAttribute("role","button"),i.setAttribute("tabindex","0")):(i.removeAttribute("role"),i.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateReferencesButton(),this.updateMilestonesButton()}updateTimeLabels(){const e=this.currentSighting.event,t=Kr(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Jd(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 xl(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?r0(a0(this.realStartMs+i)):xl(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?xl(this.currentSighting.timeline.duration):this.canShowClockTime?r0(a0(this.realStartMs+this.realDurationMs)):xl(this.realDurationMs)}}function a0(r){const e=new Date(r);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function r0(r){if(r.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return r.second?`${e(r.hour)}:${e(r.minute??0)}:${e(r.second)}`:`${e(r.hour)}:${e(r.minute??0)}`}function xl(r){const e=Math.round(r/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const ou="rr0-ufo";function ch(){customElements.get(ou)||customElements.define(ou,sn)}const _2=`
|
|
1992
|
+
`;class tl{constructor(e){this.points=e}static STATIONARY_M=5;static of(e){const t=[];for(const i of e.witnessTrack.allKeyframes){const{lat:n,lng:s,headingDeg:a}=i.pose;n===void 0||s===void 0||t.push({t:i.t,lat:n,lng:s,headingDeg:a})}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 tl(t)}get moved(){return this.spanM>tl.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:Ds(0,-s,i,n).lat,south:Ds(0,s,i,n).lat,east:Ds(s,0,i,n).lng,west:Ds(-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 hi{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 STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(e){const t=tl.of(e);if(!t||t.points.length<2)return;const i=[];let n=0;for(let s=1;s<t.points.length;s++){const a=t.points[s-1],o=t.points[s],l=(o.t-a.t)/1e3;if(l<=0)continue;const c=pi(o.lat,o.lng,a.lat,a.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),f=u/l,p=hi.stepHzFor(f);i.push({startMs:a.t,endMs:o.t,speedMPerS:f,eastUnit:u>0?h/u:0,northUnit:u>0?d/u:0,stepHz:p,stepsBefore:n,rampsIn:!0,rampsOut:!0}),n+=p*l}for(let s=0;s<i.length;s++){const a=i[s-1],o=i[s+1];i[s].rampsIn=a===void 0||a.stepHz===0,i[s].rampsOut=o===void 0||o.stepHz===0}if(i.some(s=>s.stepHz>0))return new hi(i,1-(e.instrument.stabilization??0))}static stepHzFor(e){return e<hi.SLOWEST_WALK_M_PER_S||e>hi.FASTEST_WALK_M_PER_S?0:hi.REFERENCE_STEP_HZ*Math.pow(e/hi.REFERENCE_SPEED_M_PER_S,hi.CADENCE_EXPONENT)}offsetAt(e){const t=this.stretches.find(l=>e>=l.startMs&&e<=l.endMs);if(!t||t.stepHz===0)return hi.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),a=hi.SWAY_M*s*Math.cos(n/2),o=s*this.rotationPassed*t.speedMPerS/hi.REFERENCE_SPEED_M_PER_S;return{eastM:a*t.northUnit,northM:-a*t.eastUnit,upM:hi.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:hi.HEAD_ROLL_DEG*o*Math.cos(n/2),pitchDeg:-1.25*o*Math.cos(n),yawDeg:hi.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,a=e.endMs-t,o=Math.min(e.rampsIn?s/n:1,e.rampsOut?a/n:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class Qy{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)}}const jp=.05,Jp=.08,Qp=2,e0=3,e2=14,t2=.02;class i2{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;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}}this.context.state==="suspended"&&this.context.resume()}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.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,jp);for(const{param:s,ratio:a}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*a,i.sampleRate/2-1),n,jp)}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 a=e.createGain();return a.gain.value=0,i.connect(s),n.connect(s),s.connect(a).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,a],sources:[i,n],volumeParam:a.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(),a=e.createGain();return a.gain.value=0,n.connect(s).connect(i.frequency),i.connect(a).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,a],sources:[i,n],volumeParam:a.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(e.destination),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 a=e.createBufferSource();a.buffer=this.crackleEnvelope(e),a.loop=!0;const o=e.createGain();return o.gain.value=0,a.connect(o).connect(s.gain),i.connect(n).connect(s).connect(e.destination),i.start(),a.start(),{key:t,nodes:[n,s,o],sources:[i,a],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 a=n.createBufferSource();a.buffer=s,a.loop=!0;const o=n.createGain();o.gain.value=0,a.connect(o).connect(n.destination),a.start(),this.voice={key:t,nodes:[o],sources:[a],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*Qp),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*Qp),e.sampleRate),i=t.getChannelData(0);let n=0,s=0;for(let a=0;a<i.length;a++)n=(n+.02*(Math.random()*2-1))/1.02,i[a]=n,s=Math.max(s,Math.abs(n));if(s>0)for(let a=0;a<i.length;a++)i[a]/=s;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*e0),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(t2*e.sampleRate)),s=Math.round(e0*e2);for(let a=0;a<s;a++){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,Jp/3);const n=i+Jp;for(const s of e.sources){s.onended=()=>{s.disconnect();for(const a of e.nodes)a.disconnect()};try{s.stop(n)}catch{s.disconnect();for(const a of e.nodes)a.disconnect()}}}}function jr(r){return[...r].sort((e,t)=>e.t-t.t)}function t0(r,e){let t;for(const i of jr(r)){if(i.t>e)break;t=i}return t}const Dh=Math.PI/180,wl=180/Math.PI;class Fn{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*Dh/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new Fn(e.projection,t,i)}degToPx(e){const t=e*Dh;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*wl:2*Math.atan(e/(2*this.focalPx))*wl}angleDegToRadiusPx(e){const t=e*Dh;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*wl:Math.atan(e/this.focalPx)*wl}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(rt.angularWidthDeg(e))}}class mi{static fovOf(e,t){return tt(e,t)?.fovDeg??vt.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),a={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:a};const o=t.bounds.width===0?1:n/t.bounds.width,l=t.bounds.height===0?1:s/t.bounds.height;return{...t,bounds:a,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)<mi.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<mi.UNMOVED_PX)return t;const a=this.aimFrom(e,i,n,t.bounds);return a?{...t,aim:a}:t})}static UNMOVED_PX=.01;static aimOf(e,t,i){const n=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,this.fovOf(e,t));return this.aimFrom(e,n,tt(e,t),i)}static aimFrom(e,t,i,n){if(i?.headingDeg===void 0)return;const s=this.frameCentre(e),a=t.radiusPxToAngleDeg(n.x+n.width/2-s.x),o=t.radiusPxToAngleDeg(s.y-(n.y+n.height/2));return{azimuthDeg:((i.headingDeg+a)%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 a=this.frameCentre(e),o=this.shortestArc(t.azimuthDeg-s.headingDeg),l=t.altitudeDeg-s.pitchDeg;return{x:a.x+this.offAxisPx(n,o)-i.width/2,y:a.y-this.offAxisPx(n,l)-i.height/2}}static frameCentre(e){return{x:vt.frameWidthPx(e.instrument,rt.CANVAS_HEIGHT_PX)/2,y:rt.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=mi.OFF_CANVAS_DEG?Math.sign(t)*mi.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=vt.frameWidthPx(t,rt.CANVAS_HEIGHT_PX)/2,s=vt.frameWidthPx(e.instrument,rt.CANVAS_HEIGHT_PX)/2,a=rt.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=Fn.of(t,rt.CANVAS_HEIGHT_PX,c),d=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,l);e.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,p=f.x+f.width/2-n,v=f.y+f.height/2-a,m=Math.hypot(p,v),g=m===0?1:d.angleDegToRadiusPx(h.radiusPxToAngleDeg(m))/m,w={...f,x:s+p*g-f.width/2,y:a+v*g-f.height/2};return{...u,shape:{...u.shape,bounds:w}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:i}=e;for(const n of[...i.allKeyframes]){const s=tt(e,n.t),a=this.fovOf(e,n.t),o=Fn.of(e.instrument,rt.CANVAS_HEIGHT_PX,a);i.addKeyframe(n.t,n.shapes.map(l=>({...l,shape:t(l.shape,o,s)})))}}}function n2(r){return on.restore(au(r),r.provenance)}function au(r){return mi.toAngular(r),mi.toAim(r),{version:1,id:r.id,time:r.event.time,endTime:r.event.endTime,durationSeconds:r.event.durationSeconds,utcOffsetHours:r.event.utcOffsetHours,timeZone:r.event.timeZone,place:r.event.place,witness:r.witness,testimony:r.testimony,description:r.event.description,tags:r.event.tags,timeline:r.timeline.toJSON(),witnessTrack:r.witnessTrack.toJSON(),weatherTrack:r.weatherTrack.toJSON(),soundTrack:r.soundTrack.toJSON(),weather:r.weather,decor:r.decor,milestones:r.milestones.length>0?r.milestones:void 0,references:r.references.length>0?r.references:void 0,roads:r.roads.length>0?r.roads:void 0,weatherSource:r.weatherSource,instrument:r.instrumentId,exposureSeconds:r.exposureSeconds,interpretation:r.interpretation}}function s2(r){const{recording:e,provenance:t}=on.strip(r),i=a2(e);return i.provenance=t,i}function a2(r){const e=new lh({eventType:"sighting",time:r.time,endTime:r.endTime,durationSeconds:r.durationSeconds,utcOffsetHours:r.utcOffsetHours,timeZone:r.timeZone,place:r.place,description:r.description,tags:r.tags},oh.fromJSON(r.timeline),r.witnessTrack?jo.fromJSON(r.witnessTrack):new jo,r.weatherTrack?Jo.fromJSON(r.weatherTrack):new Jo,r.soundTrack?Qo.fromJSON(r.soundTrack):new Qo,r.witness&&{id:r.witness.id,title:r.witness.title,lastName:r.witness.lastName,firstNames:r.witness.firstNames},r.id,r.weather,r.decor??[],r.weatherSource,r.instrument,r.exposureSeconds??r.witnessTrack?.keyframes.map(t=>t.pose.exposureSeconds).find(t=>t!==void 0),jr(r.milestones??[]),r.references??[],r.roads??[]);return e.testimony=r.testimony,e.interpretation=r.interpretation,mi.toBounds(e),mi.toPosition(e),e}class Ln{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 a=s>0?Ln.travelDegOver(e,t,i,i+n*1e3)/s/Ln.PIXELS_PER_INSTANT:0,o=Ln.flashesOver(e,n)*Ln.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(a,o));return l<2?1:Math.min(Ln.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const a of e){if(!a.track||a.track.length<2)continue;const o=Ln.directionAt(a,t,i),l=Ln.directionAt(a,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=Zr(e,i),s=n.eastM,a=n.altitudeM-t,o=n.northM,l=Math.hypot(s,a,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:a/l,z:o/l}}}function Jr(r,e){for(const t of r){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class oa{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 os{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[os.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>os.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(os.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},a=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>os.base(o)===os.base(i))??i;return n===void 0?delete s[a]:s[a]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const ru=["en","fr"],r2={en:()=>Sn(()=>Promise.resolve().then(()=>d2),void 0,import.meta.url).then(r=>r.ufoMessages_en),fr:()=>Sn(()=>import("./UfoMessages_fr-CnenjNH2.js"),[],import.meta.url).then(r=>r.ufoMessages_fr)};function o2(r){return r2[r]()}const l2={en:()=>Sn(()=>Promise.resolve().then(()=>XT),void 0,import.meta.url).then(r=>r.sightingEditorMessages_en),fr:()=>Sn(()=>import("./SightingEditorMessages_fr-DbyQTXpg.js"),[],import.meta.url).then(r=>r.sightingEditorMessages_fr)};function c2(r){return l2[r]()}function h2(r){return r==="fr"?Sn(()=>import("./TagNames_fr-Dnqq6HQ5.js"),[],import.meta.url).then(e=>e.tagNames_fr):Promise.resolve({})}const d1={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",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",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",witnessHere:"The witness, here",decorHere:"Scenery"},d2=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:d1},Symbol.toStringTag,{value:"Module"})),u1=Math.PI/180,u2=180/Math.PI,f2=256,f1=19,p2=1.25;function p1(r,e){return 156543.03392*Math.cos(r*u1)/2**e}function m1(r,e){return p1(r,e)*f2}function m2(r,e){let t=0;for(;t<f1&&m1(r,t+1)>=e;)t++;return t}function g1(r,e){return m1(r,e)}function g2(r,e){const t=e*p2;let i=0;for(;i<f1&&p1(r,i)>t;)i++;return i}function wc(r){const e=r*u1;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function v1(r,e,t){const i=2**t;return{x:(r+180)/360*i,y:wc(e)*i}}function y1(r,e,t){const i=wc(r.north),n=wc(r.south);return{x:(e-r.west)/(r.east-r.west),y:(wc(t)-i)/(n-i)}}function v2(r,e,t){const{x:i,y:n}=v1(r,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function y2(r){const e=2**r.z,t=r.x/e*360-180,i=(r.x+1)/e*360-180,n=i0(r.y,e);return{south:i0(r.y+1,e),north:n,west:t,east:i}}function i0(r,e){const t=r/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*u2}function b1(r,e,t,i){const n=v2(r,e,t),s=Math.floor(i/2),a=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)a.push({x:n.x+h,y:n.y+c,z:t});const o=a.map(y2),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:a,bounds:l}}class Ia{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 a=Math.hypot(s.x-e,s.y-t);a>s.radius||a>n||(i=s,n=a)}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.paintWitness(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=y1(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,a=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(a.x,a.y),s.arc(a.x,a.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(a.x,a.y,0,a.x,a.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:Ia.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Ia.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)}}paintWitness(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.witnessLabel&&this.targets.push({kind:"witness",label:e.witnessLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(a=>{if(!a.current)return!1;const o=pi(a.lat,a.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Ia.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Ia.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 f of n.slice().reverse())this.paintOutlinedText(f,this.width-6,s),s-=11;const a=(e.south+e.north)/2,l=Math.abs(pi(a,e.east,a,e.west).x)/this.width,c=Ia.SCALE_STEPS_M.find(f=>f/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 a=n?`${n} ${s}`:s;n&&this.ctx.measureText(a).width>t?(i.push(n),n=s):n=a}return n&&i.push(n),i}}const mn=256,n0=3,b2=32768;class M1{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),a=Math.abs(pi(e.north,n,e.south,n).z),o=m2(i,Math.max(s,a)),{tiles:l}=b1(n,i,o,n0),c=n0*mn,h=new Float32Array(c*c),d=l[0];await Promise.all(l.map(f=>this.decodeTileInto(f,d,h,c)));const u=new Float32Array(t.width*t.height);for(let f=0;f<t.height;f++){const p=e.north+(e.south-e.north)*(f/(t.height-1));for(let v=0;v<t.width;v++){const m=e.west+(e.east-e.west)*(v/(t.width-1));u[f*t.width+v]=this.sampleAt(p,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)),a=await this.fetchImpl(s);if(!a.ok)throw new Error(`Elevation tile fetch failed (${a.status}): ${s}`);const o=await createImageBitmap(await a.blob()),l=document.createElement("canvas");l.width=mn,l.height=mn;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,mn,mn),d=(e.x-t.x)*mn,u=(e.y-t.y)*mn;for(let f=0;f<mn;f++)for(let p=0;p<mn;p++){const v=(f*mn+p)*4,m=h[v]*256+h[v+1]+h[v+2]/256-b2,g=d+p,w=u+f;i[w*n+g]=m}}sampleAt(e,t,i,n,s,a){const{x:o,y:l}=v1(t,e,i),c=(o-n.x)*mn,h=(l-n.y)*mn,d=Math.max(0,Math.min(a-1,Math.floor(c))),u=Math.max(0,Math.min(a-1,Math.floor(h))),f=Math.min(a-1,d+1),p=Math.min(a-1,u+1),v=c-d,m=h-u,g=s[u*a+d],w=s[u*a+f],x=s[p*a+d],y=s[p*a+f],S=g+(w-g)*v,E=x+(y-x)*v;return S+(E-S)*m}}const xc=256,M2=5;async function w1(r,e,t,i){const n=(r.south+r.north)/2,s=(r.west+r.east)/2,a=Math.abs(pi(n,r.east,n,r.west).x),o=Math.abs(pi(r.north,s,r.south,s).z),l=Math.max(a,o),{zoom:c,gridSize:h}=w2(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=b1(s,n,c,h),f=h*xc,p=document.createElement("canvas");p.width=f,p.height=f;const v=p.getContext("2d");if(!v)throw new Error("2D canvas context unavailable");const m=d[0];await Promise.all(d.map(async y=>{const S=t(y),E=await i(S);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${S}`);const I=await createImageBitmap(await E.blob());v.drawImage(I,(y.x-m.x)*xc,(y.y-m.y)*xc)}));const g=Math.min(Math.max(e.width,e.height),f);if(g===f)return{source:p,width:f,height:f,bounds:u};const w=document.createElement("canvas");w.width=g,w.height=g;const x=w.getContext("2d");if(!x)throw new Error("2D canvas context unavailable");return x.drawImage(p,0,0,f,f,0,0,g,g),{source:w,width:g,height:g,bounds:u}}function w2(r,e,t){let i=g2(r,e/Math.max(1,t)),n=s0(r,i,e);for(;n>M2&&i>0;)i--,n=s0(r,i,e);return{zoom:i,gridSize:n,mPerPixel:g1(r,i)/xc}}function s0(r,e,t){const i=Math.ceil(t/2/g1(r,e));return Math.max(3,2*i+1)}class x1{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 w1(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class Cn{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??Cn.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"~"^(${Cn.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:Cn.surfaceOf(s),widthM:Cn.widthOf(s),points:n.map(a=>({lat:a.lat,lng:a.lon}))})}return t}queued(e){const t=Cn.queue.then(e,e);return Cn.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,Cn.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:Cn.WIDTH_M[e.highway]??Cn.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 Ch="https://ufoathome.org/roads/index.json";class Uf{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??Uf.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.65.4")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==Ch?[t,Ch]:[Ch]).map(i=>i+e)}}function x2(){return{elevation:new M1,imagery:S1(),roads:S2()}}function S1(){return new x1}function S2(){return new Uf({fallback:new Cn})}const E2=new Set,za="show-witness-map",Ur="hide-milestones";class sn extends HTMLElement{static get observedAttributes(){return["src",za,Ur]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;witnessMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=lh.create();sightingAudio=new i2;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=d1;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(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`};handleWitnessMapPointerMove=e=>{const t=this.witnessMapTargetFrom(e);if(!t){this.tooltip.hidden=!0,this.witnessMapCanvas.style.cursor="default";return}this.witnessMapCanvas.style.cursor="pointer",this.tooltip.textContent=t.label,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`};handleWitnessMapClick=e=>{const t=this.witnessMapTargetFrom(e);if(t){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}))}};witnessMapTargetFrom(e){const t=this.witnessMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.witnessMapRenderer.hitTest((e.clientX-t.left)/t.width*this.witnessMapCanvas.width,(e.clientY-t.top)/t.height*this.witnessMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Jy}</style>${jy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new el(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new Ia(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),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");for(const t of[this.timeStartLabel,this.timeEndLabel])t.addEventListener("click",()=>this.toggleTimeDisplay()),t.addEventListener("keydown",i=>{i.key!=="Enter"&&i.key!==" "||(i.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapCanvas.addEventListener("pointermove",this.handleWitnessMapPointerMove),this.witnessMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.witnessMapCanvas.addEventListener("click",this.handleWitnessMapClick),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.seekSnapArmed=!0,this.nameMarkUnder(t)}),this.seekInput.addEventListener("pointerup",()=>{this.seekSnapArmed=!1}),this.seekInput.addEventListener("pointermove",t=>this.nameMarkUnder(t)),this.seekInput.addEventListener("pointerleave",()=>this.seekInput.removeAttribute("title")),this.canvas.addEventListener("click",t=>{this.enableClickToPlay&&(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),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===za&&this.applyWitnessMapDefault(),e===Ur&&this.setMilestonesShown(!this.hasAttribute(Ur))}async loadFromSrc(e){this.sightingData=await kf.json(e)}get sightingData(){return n2(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=s2(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}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)}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.updateWitnessMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referenceOpacityInput,this.referencesButton,this.witnessMapButton];if(e)for(const i of t)e.appendChild(i);else for(const i of t)this.toolbar.appendChild(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));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?jr(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)??"",a=this.said.read(i.note),o=a?`${s} — ${a}`: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}))}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=sn.MARK_SNAP_PX/i*t;let s;for(const a of this.milestoneMarks.children){const o=Number(a.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:a.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?t0(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,!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=Ln.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<sn.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(sn.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/sn.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/sn.EXPOSURE_STEP_PX),s=Math.min(sn.MAX_TRAVEL_STEPS,Math.max(i,n)),a=[];for(let o=0;o<s;o++)a.push(t.fromMs+t.ms*o/(s-1));return a}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[a,o]of i){const l=n.get(a);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=vt.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:E2;this.canvasRenderer.setStarPoints(vt.starPointsOf(this.sighting.instrument));const n=(tt(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),a=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,a))}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,a)));if(this.selectionShown&&i.size>1){const o=gt.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,a).bounds)),this.keepWitnessMapClear(),this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintWitnessMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Of(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 Qy(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}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())}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.playPauseButton.textContent=e?"⏸":"▶";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()}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()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(za)&&this.witnessPath!==void 0)}setWitnessMapOpen(e){this.witnessMapPanel.hidden=!e,this.witnessMapPanel.removeAttribute("title"),this.witnessMapButton.setAttribute("aria-pressed",String(e)),this.updateWitnessMapButton(),e&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const t=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=t,this.witnessMapButton.setAttribute("aria-label",t)}updateWitnessMap(){const e=this.witnessPath!==void 0;if(this.witnessPath=tl.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!e&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const t=this.witnessPath.boundsAround(sn.WITNESS_MAP_MIN_SPAN_M,sn.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,t))&&(this.witnessMapBounds=t,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}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 loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const e=S1(),t=this.witnessMapBounds;try{const i=await e.getImageryTexture(t,{width:sn.WITNESS_MAP_IMAGERY_PX,height:sn.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==t)return;this.witnessMapImagery=i,this.witnessMapImageryCredit=e.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}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.keepWitnessMapClear()}keepWitnessMapClear(){if(this.witnessMapPanel.hidden)return;const e=this.witnessMapBoxPx;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],a=sn.WITNESS_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.witnessMapPanel.classList.toggle("subject-overlap",o&&l),this.witnessMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],a))&&this.witnessMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.witnessMapPanel.classList.add("on-the-left")}witnessMapBoxPx;static WITNESS_MAP_CLEARANCE_PX=24;sizeWitnessMapCanvas(){const e=this.witnessMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureWitnessMapBox(),this.witnessMapCanvas.width!==t&&(this.witnessMapCanvas.width=t,this.witnessMapCanvas.height=t)}measureWitnessMapBox(){const e=this.witnessMapPanel.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.witnessMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintWitnessMap(e){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const t=tt(this.currentSighting,e),i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?t0(this.currentSighting.milestones,e):void 0;for(const a of this.milestonesShown?jr(this.currentSighting.milestones):[]){const o=tt(this.currentSighting,a.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(a.label)??"",note:this.said.read(a.note),t:a.t,lat:o.lat,lng:o.lng,current:a===s})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,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?Fn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(vt.aspectOf(i)):void 0,markers:n,decor:this.witnessMapDecorAt(e),witnessLabel:this.said.read(this.currentSighting.witness?.title)??this.messages.witnessHere,nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}witnessMapDecorAt(e){const t=tt(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=Zr(i,e),{lat:s,lng:a}=Ds(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:a,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}witnessMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=t,this.paintWitnessMap(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=tt(this.currentSighting,this.currentTime),s=Fn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return hi.of(this.currentSighting)?.offsetAt(e)??hi.STILL}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new os(oa.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=Jr(oa.preferencesFor(this),ru);e!=="en"&&this.applyMessages(await o2(e))}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;for(const i of[this.timeStartLabel,this.timeEndLabel])i.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(i.setAttribute("role","button"),i.setAttribute("tabindex","0")):(i.removeAttribute("role"),i.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateReferencesButton(),this.updateMilestonesButton()}updateTimeLabels(){const e=this.currentSighting.event,t=Kr(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Jd(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 xl(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?r0(a0(this.realStartMs+i)):xl(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?xl(this.currentSighting.timeline.duration):this.canShowClockTime?r0(a0(this.realStartMs+this.realDurationMs)):xl(this.realDurationMs)}}function a0(r){const e=new Date(r);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function r0(r){if(r.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return r.second?`${e(r.hour)}:${e(r.minute??0)}:${e(r.second)}`:`${e(r.hour)}:${e(r.minute??0)}`}function xl(r){const e=Math.round(r/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const ou="rr0-ufo";function ch(){customElements.get(ou)||customElements.define(ou,sn)}const _2=`
|
|
1993
1993
|
<div class="stage" id="stage">
|
|
1994
1994
|
<div class="frame" id="frame">
|
|
1995
1995
|
<canvas id="scene-canvas"></canvas>
|
|
@@ -6738,12 +6738,13 @@ float densityAt(vec3 p) {
|
|
|
6738
6738
|
if (mask < 0.001) return 0.0;
|
|
6739
6739
|
float top = mix(0.45 + 0.55 * billow, 0.96, flatness);
|
|
6740
6740
|
float profile = smoothstep(0.0, 0.09, h) * (1.0 - smoothstep(top - 0.22, top, h));
|
|
6741
|
-
|
|
6742
|
-
//
|
|
6743
|
-
//
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6741
|
+
float erosion = mix(max(0.0, mask - (1.0 - billow) * 0.38 - (1.0 - detail) * 0.10), mask * 0.8, flatness);
|
|
6742
|
+
// A deck thins away with the distance rather than stopping where its averaged cover can no longer
|
|
6743
|
+
// pay for the billows' bite, which left a hard line of cloud at thirty kilometres (Valensole). A
|
|
6744
|
+
// deck closing up to overcast keeps its far part: that is a ceiling, and a ceiling reaches the
|
|
6745
|
+
// horizon.
|
|
6746
|
+
float fade = mix(1.0 - distant, 1.0, smoothstep(0.5, 0.92, coverage));
|
|
6747
|
+
return profile * erosion * density * carve * fade;
|
|
6747
6748
|
}
|
|
6748
6749
|
void main() {
|
|
6749
6750
|
vec3 dir = normalize(vCloudDirection);
|
|
@@ -6828,7 +6829,7 @@ float transmissionAt(vec3 dir, float distanceM) {
|
|
|
6828
6829
|
if (transmission < 0.015) break;
|
|
6829
6830
|
}
|
|
6830
6831
|
return transmission;
|
|
6831
|
-
}`,i=/\b(noiseMap|sunDir|sunColor|ambientColor|hazeColor|offsetM|seedOffset|localCenter|localSize|localRotation|baseM|thicknessM|eyeM|coverage|sizeM|density|darkness|flatness|SLICE|MAX_HOLES|holeCount|holeCenter|holeSize|holeRotation|R|roots|altitude|coverageThreshold|ellipsoidRadius|clipLocal|densityAt|transmissionAt)\b/g;return(e+t).replace(i,n=>r+n)}class jc{mesh;uniforms;constructor(e,t){this.uniforms={noiseMap:{value:e},sunDir:{value:new R(0,1,0)},sunColor:{value:new se(1,1,1)},ambientColor:{value:new se(.4,.45,.5)},hazeColor:{value:new se(.5,.6,.7)},offsetM:{value:new R},seedOffset:{value:new R},localCenter:{value:new R},localSize:{value:new R},localRotation:{value:0},holeCount:{value:0},holeCenter:{value:Array.from({length:_o},()=>new R)},holeSize:{value:Array.from({length:_o},()=>new R)},holeRotation:{value:new Array(_o).fill(0)},baseM:{value:1e3},thicknessM:{value:650},eyeM:{value:1.6},coverage:{value:.5},sizeM:{value:1400},density:{value:1},darkness:{value:0},flatness:{value:0}};const i=new lt({uniforms:this.uniforms,vertexShader:HE,fragmentShader:af,transparent:!0,depthWrite:!1,depthTest:!0,side:Wt});this.mesh=new De(new ai(t,32,16),i),this.mesh.frustumCulled=!1}update(e,t,i,n=[]){const s=this.uniforms;s.localSize.value.set(i?.widthM??0,i?.thicknessM??0,i?.depthM??0),s.localCenter.value.set(i?.eastM??0,i?i.baseM+i.thicknessM/2:0,-(i?.northM??0)),s.localRotation.value=(i?.rotationDeg??0)*Math.PI/180,s.holeCount.value=Math.min(_o,n.length),n.slice(0,_o).forEach((o,l)=>{s.holeCenter.value[l].set(o.eastM,o.baseM+o.thicknessM/2,-o.northM),s.holeSize.value[l].set(o.widthM,o.thicknessM,o.depthM),s.holeRotation.value[l]=o.rotationDeg*Math.PI/180}),s.baseM.value=Math.max(0,e.baseM),s.thicknessM.value=Math.max(10,e.thicknessM),s.coverage.value=Math.max(0,Math.min(1,e.coverage)),s.sizeM.value=Math.max(50,e.sizeM),s.density.value=Math.max(0,Math.min(2,e.density)),s.darkness.value=Math.max(0,Math.min(1,e.darkness??0)),s.eyeM.value=Math.max(.1,t),s.flatness.value=e.type==="stratus"?1:e.type==="stratocumulus"?.45:0;const a=sf(e);s.seedOffset.value.set(a%97/97,a%61/61,a%37/37),this.mesh.visible=s.density.value>0&&(i!==void 0||s.coverage.value>0)}static thresholdFor(e){const t=Math.min(.999,Math.max(.001,e));return .564-.2516*(Math.sqrt(3)/Math.PI)*Math.log(t/(1-t))}densityAt(e){const t=this.uniforms,i=(L,N,F)=>{const V=Math.max(0,Math.min(1,(F-L)/(N-L)));return V*V*(3-2*V)},n=(L,N,F)=>L+(N-L)*F,s=t.eyeM.value+e.y,a=2*sa*s+e.x**2+s**2+e.z**2,o=a/(Math.sqrt(sa**2+a)+sa),l=(o-t.baseM.value)/t.thicknessM.value,c=t.localSize.value.x>0;if(l<=0||l>=1||t.coverage.value<=0&&!c)return 0;const h=(L,N,F)=>{const V=e.x+t.offsetM.value.x-L.x,J=e.z+t.offsetM.value.z-L.z,K=Math.cos(F),ie=Math.sin(F);return Math.hypot((K*V-ie*J)/(N.x*.5),(o-L.y)/(N.y*.5),(ie*V+K*J)/(N.z*.5))},d=c?h(t.localCenter.value,t.localSize.value,t.localRotation.value):0;if(d>1.3)return 0;const u=[e.x,e.y,e.z].map((L,N)=>(L+t.offsetM.value.getComponent(N))/(t.sizeM.value*4)+t.seedOffset.value.getComponent(N)),f=t.noiseMap.value.image.data,p=(L,N,F)=>{const V=[L,N,F].map(re=>(re%1+1)%1*ni-.5),J=V.map(Math.floor),K=V.map((re,Ge)=>re-J[Ge]);let ie=0;for(let re=0;re<2;re++)for(let Ge=0;Ge<2;Ge++)for(let je=0;je<2;je++){const Ye=(J[0]+je+ni)%ni,$=(J[1]+Ge+ni)%ni,ae=(J[2]+re+ni)%ni;ie+=f[Ye+ni*($+ni*ae)]/255*(je?K[0]:1-K[0])*(Ge?K[1]:1-K[1])*(re?K[2]:1-K[2])}return ie},v=[u[0]*.173,gd,u[2]*.173];u[0]+=(p(v[0]+.13,v[1]+.27,v[2]+.41)-.5)*1.3,u[2]+=(p(v[0]+.67,v[1]+.53,v[2]+.19)-.5)*1.3;const m=(u[0]*.731+u[2]*.682)*1.371,g=(-u[0]*.682+u[2]*.731)*1.371;let w=i(.2,.8,p(u[0],gd,u[2])*.65+p(m,gd+.37,g)*.35);const x=i(8e3,4e4,Math.hypot(e.x,e.y,e.z)),y=n(p(u[0]*2,u[1]*3,u[2]*2),.5,x),S=n(p(u[0]*7,u[1]*7,u[2]*7),.5,x);let E=jc.thresholdFor(t.coverage.value),I=1,b=0;if(c){const L=d+(y-.5)*.18;b=1-i(.35,1,L),I=1-i(.85,1.15,L),w=n(w,1,b),E=Math.min(E,jc.thresholdFor(.5))}for(let L=0;L<t.holeCount.value;L++)I*=i(.85,1.15,h(t.holeCenter.value[L],t.holeSize.value[L],t.holeRotation.value[L])+(y-.5)*.18);if(I<.001)return 0;const T=(1-t.flatness.value)*.3*l*l;let A=i(E-.1+T,E+.1+T,w+(y-.5)*.12*l);if(A=n(A,1,i(.92,1,t.coverage.value)),A=n(A,Math.max(t.coverage.value,b),x),A<.001)return 0;const D=n(.45+.55*y,.96,t.flatness.value),P=i(0,.09,l)*(1-i(D-.22,D,l)),B=1-x,G=n(Math.max(0,A-((1-y)*.38+(1-S)*.1)*B),A*.8,t.flatness.value);return P*G*t.density.value*I}transmissionAt(e,t=1/0){const i=this.uniforms,n=zE(e.y,i.eyeM.value,i.baseM.value,i.thicknessM.value);if(!n)return 1;let[s,a]=n;if(i.localSize.value.x>0){const h=i.localCenter.value.clone().sub(i.offsetM.value);h.y-=i.eyeM.value+(h.x**2+h.z**2)/(2*sa);const d=i.localSize.value.length()*.5+25,u=e.x*h.x+e.y*h.y+e.z*h.z,f=u*u-h.lengthSq()+d*d;if(f<0)return 1;s=Math.max(s,u-Math.sqrt(f)),a=Math.min(a,u+Math.sqrt(f))}const o=Math.min(a,t,s+18e3);if(o<=s)return 1;const l=(o-s)/48;let c=1;for(let h=0;h<48;h++){const d=s+(h+.5)*l;if(c*=Math.exp(-this.densityAt({x:e.x*d,y:e.y*d,z:e.z*d})*l*.006),c<.015)break}return c}dispose(){this.mesh.removeFromParent(),this.mesh.geometry.dispose(),this.mesh.material.dispose()}}class fi{static VIOLET_NM=400;static DEEP_RED_NM=700;static SAMPLES=fi.build(24);static build(e){const t=[];let i=0,n=0,s=0;for(let a=0;a<e;a++){const o=fi.VIOLET_NM+(a+.5)/e*(fi.DEEP_RED_NM-fi.VIOLET_NM),[l,c,h]=fi.colourMatching(o),d={wavelengthNm:o,r:Math.max(0,3.2406*l-1.5372*c-.4986*h),g:Math.max(0,-.9689*l+1.8758*c+.0415*h),b:Math.max(0,.0557*l-.204*c+1.057*h)};i+=d.r,n+=d.g,s+=d.b,t.push(d)}for(const a of t)a.r/=i,a.g/=n,a.b/=s;return t}static tristimulus(e,t,i){let n=0,s=0,a=0;for(let l=0;l<e.length;l++){const[c,h,d]=fi.colourMatching(e[l]);n+=c*t[l],s+=h*t[l],a+=d*t[l]}const o=683*i;return[n*o,s*o,a*o]}static scotopic(e){const t=e/1e3;return .992*Math.exp(-321.9*(t-.5)**2)}static linearSrgbOf([e,t,i]){return[3.2406*e-1.5372*t-.4986*i,-.9689*e+1.8758*t+.0415*i,.0557*e-.204*t+1.057*i]}static colourMatching(e){const t=(i,n,s)=>{const a=(e-i)/(e<i?n:s);return Math.exp(-.5*a*a)};return[1.056*t(599.8,37.9,31)+.362*t(442,16,26.7)-.065*t(501.1,20.4,26.2),.821*t(568.8,46.9,40.5)+.286*t(530.9,16.3,31.1),1.217*t(437,11.8,36)+.681*t(459,26,13.8)]}}class Jc{static reflectance(e,t,i){const s=t/i*Math.sqrt(Math.max(0,1-e*e));if(s>=1)return 1;const a=Math.sqrt(Math.max(0,1-s*s)),o=(i*e-t*a)/(i*e+t*a),l=(t*e-i*a)/(t*e+i*a);return(o*o+l*l)/2}}class xi{static RED_NM=656.3;static BLUE_NM=435.8;static ANCHOR_RED=1.3312;static ANCHOR_BLUE=1.3404;static B=(xi.ANCHOR_BLUE-xi.ANCHOR_RED)/(1/(xi.BLUE_NM*xi.BLUE_NM)-1/(xi.RED_NM*xi.RED_NM));static A=xi.ANCHOR_RED-xi.B/(xi.RED_NM*xi.RED_NM);static indexAt(e){return xi.A+xi.B/(e*e)}}class Hr{static MAX_CHORDS=4;index=1.333;set refractiveIndex(e){this.index=e}trace(e,t){const i=Math.min(1,Math.max(0,e)),n=Math.sqrt(Math.max(0,1-i*i)),s=i/this.index,a=Math.sqrt(Math.max(0,1-s*s)),o=Math.asin(i),l=Math.asin(s),c=Jc.reflectance(n,1,this.index);let h=0;t[h].chords=0,t[h].scatteringAngle=Hr.folded(Math.PI-2*o),t[h].weight=c,h++;const d=Jc.reflectance(a,this.index,1);let u=1-c;for(let f=1;f<=Hr.MAX_CHORDS&&h<t.length;f++){const p=2*(o-l)+(f-1)*(Math.PI-2*l);t[h].chords=f,t[h].scatteringAngle=Hr.folded(p),t[h].weight=u*(1-d),h++,u*=d}return h}static folded(e){const t=2*Math.PI;let i=e%t;return i<0&&(i+=t),i>Math.PI?t-i:i}}class ii{static BINS=1800;static SOURCE_RADIUS_DEG=.265;static FULL_UNTIL_DEG=60;static FADED_BY_DEG=90;static IMPACT_STEPS=4096;drop=new Hr;previous=ii.emergences();current=ii.emergences();tally=new Float32Array(ii.BINS*3);radiance=new Float32Array(ii.BINS*3);scratch=new Float32Array(ii.BINS*3);static emergences(){return Array.from({length:Hr.MAX_CHORDS+1},()=>({chords:0,scatteringAngle:0,weight:0}))}compute(){this.tally.fill(0);for(const e of fi.SAMPLES)this.sweep(e);return this.toRadiance(),this.blurBySourceDisc(),{bins:ii.BINS,data:this.radiance,binDeg:180/ii.BINS}}sweep(e){this.drop.refractiveIndex=xi.indexAt(e.wavelengthNm);const t=ii.IMPACT_STEPS;let i=this.drop.trace(0,this.previous);const n=1/t;for(let s=1;s<=t;s++){const a=this.drop.trace(Math.sqrt(s/t),this.current);for(let o=0;o<Math.min(a,i);o++){const l=this.previous[o],c=this.current[o];l.chords===c.chords&&this.deposit(l.scatteringAngle,c.scatteringAngle,(l.weight+c.weight)/2*n,e)}i=a,this.copyInto(this.current,this.previous)}}copyInto(e,t){for(let i=0;i<e.length;i++)t[i].chords=e[i].chords,t[i].scatteringAngle=e[i].scatteringAngle,t[i].weight=e[i].weight}deposit(e,t,i,n){if(i<=0)return;const s=ii.BINS,a=Math.PI/s,o=Math.min(e,t),l=Math.max(e,t),c=l-o,h=Math.min(s-1,Math.max(0,Math.floor(o/a))),d=Math.min(s-1,Math.max(0,Math.floor(l/a)));if(c<=0||h===d){this.add(h,i,n);return}for(let u=h;u<=d;u++){const f=Math.min(l,(u+1)*a)-Math.max(o,u*a);f>0&&this.add(u,i*f/c,n)}}add(e,t,i){const n=e*3,s=t*fi.SAMPLES.length;this.tally[n]+=s*i.r,this.tally[n+1]+=s*i.g,this.tally[n+2]+=s*i.b}toRadiance(){const e=ii.BINS,t=Math.PI/e;for(let i=0;i<e;i++){const n=2*Math.PI*(Math.cos(i*t)-Math.cos((i+1)*t)),s=n>0?this.backwardShare((i+.5)*(180/e))/n:0,a=i*3;this.radiance[a]=this.tally[a]*s,this.radiance[a+1]=this.tally[a+1]*s,this.radiance[a+2]=this.tally[a+2]*s}}backwardShare(e){const t=180-e;if(t<=ii.FULL_UNTIL_DEG)return 1;if(t>=ii.FADED_BY_DEG)return 0;const i=(t-ii.FULL_UNTIL_DEG)/(ii.FADED_BY_DEG-ii.FULL_UNTIL_DEG);return Math.cos(i*Math.PI/2)**2}blurBySourceDisc(){const e=ii.BINS,t=ii.SOURCE_RADIUS_DEG*e/180,i=Math.max(1,Math.ceil(t)),n=[];let s=0;for(let a=-i;a<=i;a++){const o=Math.max(0,t*t-a*a),l=Math.sqrt(o);n.push(l),s+=l}if(!(s<=0)){this.scratch.set(this.radiance);for(let a=0;a<e;a++){let o=0,l=0,c=0;for(let d=-i;d<=i;d++){const u=Math.min(e-1,Math.max(0,a+d))*3,f=n[d+i];o+=this.scratch[u]*f,l+=this.scratch[u+1]*f,c+=this.scratch[u+2]*f}const h=a*3;this.radiance[h]=o/s,this.radiance[h+1]=l/s,this.radiance[h+2]=c/s}}}}class Fa{static RADIUS=880;static RENDER_ORDER=5.5;static GAIN=.22;static MOON_SATURATION=.15;object;material;sky=new ii;texture;texels;computed=!1;constructor(){this.texels=new Uint16Array(ii.BINS*4),this.texture=new pa(this.texels,ii.BINS,1,Ai,qt),this.texture.minFilter=ot,this.texture.magFilter=ot,this.texture.wrapS=_i,this.texture.wrapT=_i,this.texture.needsUpdate=!0,this.material=new lt({uniforms:{uSource:{value:new R(0,1,0)},uStrength:{value:0},uTint:{value:new R(1,1,1)},uSaturation:{value:1},uProfile:{value:this.texture},uGain:{value:Fa.GAIN}},vertexShader:`
|
|
6832
|
+
}`,i=/\b(noiseMap|sunDir|sunColor|ambientColor|hazeColor|offsetM|seedOffset|localCenter|localSize|localRotation|baseM|thicknessM|eyeM|coverage|sizeM|density|darkness|flatness|SLICE|MAX_HOLES|holeCount|holeCenter|holeSize|holeRotation|R|roots|altitude|coverageThreshold|ellipsoidRadius|clipLocal|densityAt|transmissionAt)\b/g;return(e+t).replace(i,n=>r+n)}class jc{mesh;uniforms;constructor(e,t){this.uniforms={noiseMap:{value:e},sunDir:{value:new R(0,1,0)},sunColor:{value:new se(1,1,1)},ambientColor:{value:new se(.4,.45,.5)},hazeColor:{value:new se(.5,.6,.7)},offsetM:{value:new R},seedOffset:{value:new R},localCenter:{value:new R},localSize:{value:new R},localRotation:{value:0},holeCount:{value:0},holeCenter:{value:Array.from({length:_o},()=>new R)},holeSize:{value:Array.from({length:_o},()=>new R)},holeRotation:{value:new Array(_o).fill(0)},baseM:{value:1e3},thicknessM:{value:650},eyeM:{value:1.6},coverage:{value:.5},sizeM:{value:1400},density:{value:1},darkness:{value:0},flatness:{value:0}};const i=new lt({uniforms:this.uniforms,vertexShader:HE,fragmentShader:af,transparent:!0,depthWrite:!1,depthTest:!0,side:Wt});this.mesh=new De(new ai(t,32,16),i),this.mesh.frustumCulled=!1}update(e,t,i,n=[]){const s=this.uniforms;s.localSize.value.set(i?.widthM??0,i?.thicknessM??0,i?.depthM??0),s.localCenter.value.set(i?.eastM??0,i?i.baseM+i.thicknessM/2:0,-(i?.northM??0)),s.localRotation.value=(i?.rotationDeg??0)*Math.PI/180,s.holeCount.value=Math.min(_o,n.length),n.slice(0,_o).forEach((o,l)=>{s.holeCenter.value[l].set(o.eastM,o.baseM+o.thicknessM/2,-o.northM),s.holeSize.value[l].set(o.widthM,o.thicknessM,o.depthM),s.holeRotation.value[l]=o.rotationDeg*Math.PI/180}),s.baseM.value=Math.max(0,e.baseM),s.thicknessM.value=Math.max(10,e.thicknessM),s.coverage.value=Math.max(0,Math.min(1,e.coverage)),s.sizeM.value=Math.max(50,e.sizeM),s.density.value=Math.max(0,Math.min(2,e.density)),s.darkness.value=Math.max(0,Math.min(1,e.darkness??0)),s.eyeM.value=Math.max(.1,t),s.flatness.value=e.type==="stratus"?1:e.type==="stratocumulus"?.45:0;const a=sf(e);s.seedOffset.value.set(a%97/97,a%61/61,a%37/37),this.mesh.visible=s.density.value>0&&(i!==void 0||s.coverage.value>0)}static thresholdFor(e){const t=Math.min(.999,Math.max(.001,e));return .564-.2516*(Math.sqrt(3)/Math.PI)*Math.log(t/(1-t))}densityAt(e){const t=this.uniforms,i=(L,N,F)=>{const V=Math.max(0,Math.min(1,(F-L)/(N-L)));return V*V*(3-2*V)},n=(L,N,F)=>L+(N-L)*F,s=t.eyeM.value+e.y,a=2*sa*s+e.x**2+s**2+e.z**2,o=a/(Math.sqrt(sa**2+a)+sa),l=(o-t.baseM.value)/t.thicknessM.value,c=t.localSize.value.x>0;if(l<=0||l>=1||t.coverage.value<=0&&!c)return 0;const h=(L,N,F)=>{const V=e.x+t.offsetM.value.x-L.x,J=e.z+t.offsetM.value.z-L.z,K=Math.cos(F),ie=Math.sin(F);return Math.hypot((K*V-ie*J)/(N.x*.5),(o-L.y)/(N.y*.5),(ie*V+K*J)/(N.z*.5))},d=c?h(t.localCenter.value,t.localSize.value,t.localRotation.value):0;if(d>1.3)return 0;const u=[e.x,e.y,e.z].map((L,N)=>(L+t.offsetM.value.getComponent(N))/(t.sizeM.value*4)+t.seedOffset.value.getComponent(N)),f=t.noiseMap.value.image.data,p=(L,N,F)=>{const V=[L,N,F].map(re=>(re%1+1)%1*ni-.5),J=V.map(Math.floor),K=V.map((re,Ge)=>re-J[Ge]);let ie=0;for(let re=0;re<2;re++)for(let Ge=0;Ge<2;Ge++)for(let je=0;je<2;je++){const Ye=(J[0]+je+ni)%ni,$=(J[1]+Ge+ni)%ni,ae=(J[2]+re+ni)%ni;ie+=f[Ye+ni*($+ni*ae)]/255*(je?K[0]:1-K[0])*(Ge?K[1]:1-K[1])*(re?K[2]:1-K[2])}return ie},v=[u[0]*.173,gd,u[2]*.173];u[0]+=(p(v[0]+.13,v[1]+.27,v[2]+.41)-.5)*1.3,u[2]+=(p(v[0]+.67,v[1]+.53,v[2]+.19)-.5)*1.3;const m=(u[0]*.731+u[2]*.682)*1.371,g=(-u[0]*.682+u[2]*.731)*1.371;let w=i(.2,.8,p(u[0],gd,u[2])*.65+p(m,gd+.37,g)*.35);const x=i(8e3,4e4,Math.hypot(e.x,e.y,e.z)),y=n(p(u[0]*2,u[1]*3,u[2]*2),.5,x),S=n(p(u[0]*7,u[1]*7,u[2]*7),.5,x);let E=jc.thresholdFor(t.coverage.value),I=1,b=0;if(c){const L=d+(y-.5)*.18;b=1-i(.35,1,L),I=1-i(.85,1.15,L),w=n(w,1,b),E=Math.min(E,jc.thresholdFor(.5))}for(let L=0;L<t.holeCount.value;L++)I*=i(.85,1.15,h(t.holeCenter.value[L],t.holeSize.value[L],t.holeRotation.value[L])+(y-.5)*.18);if(I<.001)return 0;const T=(1-t.flatness.value)*.3*l*l;let A=i(E-.1+T,E+.1+T,w+(y-.5)*.12*l);if(A=n(A,1,i(.92,1,t.coverage.value)),A=n(A,Math.max(t.coverage.value,b),x),A<.001)return 0;const D=n(.45+.55*y,.96,t.flatness.value),P=i(0,.09,l)*(1-i(D-.22,D,l)),B=n(Math.max(0,A-(1-y)*.38-(1-S)*.1),A*.8,t.flatness.value),G=n(1-x,1,i(.5,.92,t.coverage.value));return P*B*t.density.value*I*G}transmissionAt(e,t=1/0){const i=this.uniforms,n=zE(e.y,i.eyeM.value,i.baseM.value,i.thicknessM.value);if(!n)return 1;let[s,a]=n;if(i.localSize.value.x>0){const h=i.localCenter.value.clone().sub(i.offsetM.value);h.y-=i.eyeM.value+(h.x**2+h.z**2)/(2*sa);const d=i.localSize.value.length()*.5+25,u=e.x*h.x+e.y*h.y+e.z*h.z,f=u*u-h.lengthSq()+d*d;if(f<0)return 1;s=Math.max(s,u-Math.sqrt(f)),a=Math.min(a,u+Math.sqrt(f))}const o=Math.min(a,t,s+18e3);if(o<=s)return 1;const l=(o-s)/48;let c=1;for(let h=0;h<48;h++){const d=s+(h+.5)*l;if(c*=Math.exp(-this.densityAt({x:e.x*d,y:e.y*d,z:e.z*d})*l*.006),c<.015)break}return c}dispose(){this.mesh.removeFromParent(),this.mesh.geometry.dispose(),this.mesh.material.dispose()}}class fi{static VIOLET_NM=400;static DEEP_RED_NM=700;static SAMPLES=fi.build(24);static build(e){const t=[];let i=0,n=0,s=0;for(let a=0;a<e;a++){const o=fi.VIOLET_NM+(a+.5)/e*(fi.DEEP_RED_NM-fi.VIOLET_NM),[l,c,h]=fi.colourMatching(o),d={wavelengthNm:o,r:Math.max(0,3.2406*l-1.5372*c-.4986*h),g:Math.max(0,-.9689*l+1.8758*c+.0415*h),b:Math.max(0,.0557*l-.204*c+1.057*h)};i+=d.r,n+=d.g,s+=d.b,t.push(d)}for(const a of t)a.r/=i,a.g/=n,a.b/=s;return t}static tristimulus(e,t,i){let n=0,s=0,a=0;for(let l=0;l<e.length;l++){const[c,h,d]=fi.colourMatching(e[l]);n+=c*t[l],s+=h*t[l],a+=d*t[l]}const o=683*i;return[n*o,s*o,a*o]}static scotopic(e){const t=e/1e3;return .992*Math.exp(-321.9*(t-.5)**2)}static linearSrgbOf([e,t,i]){return[3.2406*e-1.5372*t-.4986*i,-.9689*e+1.8758*t+.0415*i,.0557*e-.204*t+1.057*i]}static colourMatching(e){const t=(i,n,s)=>{const a=(e-i)/(e<i?n:s);return Math.exp(-.5*a*a)};return[1.056*t(599.8,37.9,31)+.362*t(442,16,26.7)-.065*t(501.1,20.4,26.2),.821*t(568.8,46.9,40.5)+.286*t(530.9,16.3,31.1),1.217*t(437,11.8,36)+.681*t(459,26,13.8)]}}class Jc{static reflectance(e,t,i){const s=t/i*Math.sqrt(Math.max(0,1-e*e));if(s>=1)return 1;const a=Math.sqrt(Math.max(0,1-s*s)),o=(i*e-t*a)/(i*e+t*a),l=(t*e-i*a)/(t*e+i*a);return(o*o+l*l)/2}}class xi{static RED_NM=656.3;static BLUE_NM=435.8;static ANCHOR_RED=1.3312;static ANCHOR_BLUE=1.3404;static B=(xi.ANCHOR_BLUE-xi.ANCHOR_RED)/(1/(xi.BLUE_NM*xi.BLUE_NM)-1/(xi.RED_NM*xi.RED_NM));static A=xi.ANCHOR_RED-xi.B/(xi.RED_NM*xi.RED_NM);static indexAt(e){return xi.A+xi.B/(e*e)}}class Hr{static MAX_CHORDS=4;index=1.333;set refractiveIndex(e){this.index=e}trace(e,t){const i=Math.min(1,Math.max(0,e)),n=Math.sqrt(Math.max(0,1-i*i)),s=i/this.index,a=Math.sqrt(Math.max(0,1-s*s)),o=Math.asin(i),l=Math.asin(s),c=Jc.reflectance(n,1,this.index);let h=0;t[h].chords=0,t[h].scatteringAngle=Hr.folded(Math.PI-2*o),t[h].weight=c,h++;const d=Jc.reflectance(a,this.index,1);let u=1-c;for(let f=1;f<=Hr.MAX_CHORDS&&h<t.length;f++){const p=2*(o-l)+(f-1)*(Math.PI-2*l);t[h].chords=f,t[h].scatteringAngle=Hr.folded(p),t[h].weight=u*(1-d),h++,u*=d}return h}static folded(e){const t=2*Math.PI;let i=e%t;return i<0&&(i+=t),i>Math.PI?t-i:i}}class ii{static BINS=1800;static SOURCE_RADIUS_DEG=.265;static FULL_UNTIL_DEG=60;static FADED_BY_DEG=90;static IMPACT_STEPS=4096;drop=new Hr;previous=ii.emergences();current=ii.emergences();tally=new Float32Array(ii.BINS*3);radiance=new Float32Array(ii.BINS*3);scratch=new Float32Array(ii.BINS*3);static emergences(){return Array.from({length:Hr.MAX_CHORDS+1},()=>({chords:0,scatteringAngle:0,weight:0}))}compute(){this.tally.fill(0);for(const e of fi.SAMPLES)this.sweep(e);return this.toRadiance(),this.blurBySourceDisc(),{bins:ii.BINS,data:this.radiance,binDeg:180/ii.BINS}}sweep(e){this.drop.refractiveIndex=xi.indexAt(e.wavelengthNm);const t=ii.IMPACT_STEPS;let i=this.drop.trace(0,this.previous);const n=1/t;for(let s=1;s<=t;s++){const a=this.drop.trace(Math.sqrt(s/t),this.current);for(let o=0;o<Math.min(a,i);o++){const l=this.previous[o],c=this.current[o];l.chords===c.chords&&this.deposit(l.scatteringAngle,c.scatteringAngle,(l.weight+c.weight)/2*n,e)}i=a,this.copyInto(this.current,this.previous)}}copyInto(e,t){for(let i=0;i<e.length;i++)t[i].chords=e[i].chords,t[i].scatteringAngle=e[i].scatteringAngle,t[i].weight=e[i].weight}deposit(e,t,i,n){if(i<=0)return;const s=ii.BINS,a=Math.PI/s,o=Math.min(e,t),l=Math.max(e,t),c=l-o,h=Math.min(s-1,Math.max(0,Math.floor(o/a))),d=Math.min(s-1,Math.max(0,Math.floor(l/a)));if(c<=0||h===d){this.add(h,i,n);return}for(let u=h;u<=d;u++){const f=Math.min(l,(u+1)*a)-Math.max(o,u*a);f>0&&this.add(u,i*f/c,n)}}add(e,t,i){const n=e*3,s=t*fi.SAMPLES.length;this.tally[n]+=s*i.r,this.tally[n+1]+=s*i.g,this.tally[n+2]+=s*i.b}toRadiance(){const e=ii.BINS,t=Math.PI/e;for(let i=0;i<e;i++){const n=2*Math.PI*(Math.cos(i*t)-Math.cos((i+1)*t)),s=n>0?this.backwardShare((i+.5)*(180/e))/n:0,a=i*3;this.radiance[a]=this.tally[a]*s,this.radiance[a+1]=this.tally[a+1]*s,this.radiance[a+2]=this.tally[a+2]*s}}backwardShare(e){const t=180-e;if(t<=ii.FULL_UNTIL_DEG)return 1;if(t>=ii.FADED_BY_DEG)return 0;const i=(t-ii.FULL_UNTIL_DEG)/(ii.FADED_BY_DEG-ii.FULL_UNTIL_DEG);return Math.cos(i*Math.PI/2)**2}blurBySourceDisc(){const e=ii.BINS,t=ii.SOURCE_RADIUS_DEG*e/180,i=Math.max(1,Math.ceil(t)),n=[];let s=0;for(let a=-i;a<=i;a++){const o=Math.max(0,t*t-a*a),l=Math.sqrt(o);n.push(l),s+=l}if(!(s<=0)){this.scratch.set(this.radiance);for(let a=0;a<e;a++){let o=0,l=0,c=0;for(let d=-i;d<=i;d++){const u=Math.min(e-1,Math.max(0,a+d))*3,f=n[d+i];o+=this.scratch[u]*f,l+=this.scratch[u+1]*f,c+=this.scratch[u+2]*f}const h=a*3;this.radiance[h]=o/s,this.radiance[h+1]=l/s,this.radiance[h+2]=c/s}}}}class Fa{static RADIUS=880;static RENDER_ORDER=5.5;static GAIN=.22;static MOON_SATURATION=.15;object;material;sky=new ii;texture;texels;computed=!1;constructor(){this.texels=new Uint16Array(ii.BINS*4),this.texture=new pa(this.texels,ii.BINS,1,Ai,qt),this.texture.minFilter=ot,this.texture.magFilter=ot,this.texture.wrapS=_i,this.texture.wrapT=_i,this.texture.needsUpdate=!0,this.material=new lt({uniforms:{uSource:{value:new R(0,1,0)},uStrength:{value:0},uTint:{value:new R(1,1,1)},uSaturation:{value:1},uProfile:{value:this.texture},uGain:{value:Fa.GAIN}},vertexShader:`
|
|
6832
6833
|
varying vec3 vDirection;
|
|
6833
6834
|
void main() {
|
|
6834
6835
|
vDirection = normalize((modelMatrix * vec4(position, 1.0)).xyz - cameraPosition);
|
|
@@ -7954,7 +7955,7 @@ void main()
|
|
|
7954
7955
|
if (ice <= 0.0) discard;
|
|
7955
7956
|
gl_FragColor = vec4(iceHaloLight(dir, ice), 1.0);
|
|
7956
7957
|
}
|
|
7957
|
-
`,transparent:!0,blending:Fi,depthWrite:!1,side:Wt,fog:!1}),this.object=new De(new ai(Pi.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,a){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,a)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Pi.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Pi.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)<Pi.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Pi.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)<Pi.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Pi.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?Pi.URGENT_BUDGET_MS:Pi.BUDGET_MS;do this.sky.trace(Pi.RAYS_PER_BATCH);while(this.sky.tracedRays<Pi.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Pi.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]=Bn.toHalfFloat(t[n]),this.texels[i+1]=Bn.toHalfFloat(t[n+1]),this.texels[i+2]=Bn.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 Mi{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Mi.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Mi.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=Mi.MAX_CONCURRENT*Mi.SEGMENTS*Mi.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new pt;t.setAttribute("position",new it(this.positions,3)),t.setAttribute("color",new it(this.colors,3)),t.setDrawRange(0,0),this.object=new De(t,new En({vertexColors:!0,transparent:!0,blending:Fi,depthWrite:!1,fog:!1,side:dn})),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=Ha.aliveAt(this.meteors,e).slice(0,Mi.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:a}of i){const o=this.radiantOf(s);if(!o)continue;const l=ci(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+a*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),f=Math.min(1,(1-a)*2.5),p=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*f,m=x=>v*x**1.6,g=x=>this.onSphere(l,p,u+(d-u)*x),w=x=>Mi.RADIUS*((Mi.MIN_HALF_WIDTH_DEG+Mi.BRIGHT_HALF_WIDTH_DEG*s.brightness*x)*Math.PI/180);for(let x=1;x<Mi.POINTS;x++){const y=(x-1)/Mi.SEGMENTS,S=x/Mi.SEGMENTS;n=this.pushRibbon(n,g(y),g(S),w(y),w(S),m(y),m(S))}}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=ci(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),a=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,a,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,a,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Mi.ACROSS-1;c++){const h=c-1,d=c,u=this.across(t,l,n,h),f=this.across(i,l,s,h),p=this.across(t,l,n,d),v=this.across(i,l,s,d),m=(g,w)=>g===0?w:0;e=this.push(e,u,m(h,a)),e=this.push(e,f,m(h,o)),e=this.push(e,p,m(d,a)),e=this.push(e,f,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,p,m(d,a))}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=_e.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),a=Math.sin(n);return{x:e.x*s+t.x*a,y:e.y*s+t.y*a,z:e.z*s+t.z*a}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),a=Math.sin(n);return{x:(e.x*s+t.x*a)*Mi.RADIUS,y:(e.y*s+t.y*a)*Mi.RADIUS,z:(e.z*s+t.z*a)*Mi.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()}}const xd=Math.PI/180;function Qt(r,e,t,i,n=!1){const s=n?new En({color:new se(...t)}):new ro({color:new se(...t)}),a=new De(e,s);return a.position.y=i,a.userData={emissive:n},a.castShadow=!n,a.receiveShadow=!n,r.add(a),a}const Sd=.25,A5=.002,T5=100,I5=1500,D5=.25,C5=1e3,R5=.15;function P5(r){const e=/^#([0-9a-f]{6})$/i.exec(r);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 L5=[.32,.32,.3],Fv=[1,.85,.5],O5=[.25,.25,.25],Uv=[1,.97,.85],k5=[.55,.68,.72],is=.03,ns=.05,Bv={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 Hi(r,e){return r?.[e]}function ss(r,e,t,i,n,s,a,o){if(o===void 0)return;const l=new ro({color:new se(...k5),transparent:!0,opacity:o/100}),c=new De(new fn(e,t,i),l);c.position.set(n,s,a),r.add(c)}const bn=.05,Ed=[.6,.58,.53],Wm=[.5,.5,.48];function Or(r,e,t,i,n,s,a,o){const l=new ro({color:new se(...o),side:Wt}),c=new De(new fn(e,t,i),l);c.position.set(n,s,a),r.add(c)}function N5(r,e,t,i,n,s,a,o,l){const c=(i-s)/2,h=(p,v,m,g)=>{p<=0||(e?Or(r,p,v,bn,m,o+g,a,l):Or(r,bn,v,p,a,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((p,v)=>p.center-v.center);let u=-t/2;for(const p of d){const m=p.center-p.main/2-u;h(m,s,u+m/2,0),u=p.center+p.main/2}const f=t/2-u;h(f,s,u+f/2,0)}function zv(r,e,t,i,n,s,a,o,l,c){const h=(i+n)/2,d=n-i,u=(f,p,v,m)=>{f.length===0?p?Or(r,v,d,bn,0,h,m,Ed):Or(r,bn,d,v,m,h,0,Ed):N5(r,p,v,d,f,s,m,h,Ed)};u(a,!0,e*2,-t+bn),u(o,!0,e*2,t-bn),u(l,!1,t*2,-e+bn),u(c,!1,t*2,e-bn),Or(r,e*2,bn,t*2,0,i+bn,0,Wm),Or(r,e*2,bn,t*2,0,n-bn,0,Wm)}function Af(r,e,t,i){switch(r){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:-as};case"front-right":return{x:e-i,z:-as};case"behind-left":return{x:-(e-i),z:as};case"behind-right":return{x:e-i,z:as}}}function Hv(r,e,t,i,n,s,a){const o=Df();o.scale.setScalar(i),o.rotation.y=Bv[e];const{x:l,z:c}=Af(e,n,s,a);o.position.set(l,t,c),r.add(o)}const Tf=6,If=6,ea=3,To=1.4,Io=ea*.5,Gv=1.2,Vv=.35,F5=.75,U5=1.6;function Xm(r,e,t,i){const n=new Ot,s=Math.max(1,r+1),a=s*ea;Qt(n,new fn(Tf,a,If),[.55,.54,.5],a/2);const o=Tf/2,l=If/2;for(let c=0;c<s;c++){const h=c*ea+ea/2;ss(n,To,Io,ns,0,h,-l-is,Hi(e,"front")),ss(n,To,Io,ns,0,h,l+is,Hi(e,"behind")),ss(n,ns,Io,To,-o-is,h,0,Hi(e,"left")),ss(n,ns,Io,To,o+is,h,0,Hi(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);Hv(n,t,c*ea,.72,o,l,Gv);const h=d=>Hi(e,d)!==void 0?[{main:To,center:0}]:[];zv(n,o,l,c*ea,(c+1)*ea,Io,h("front"),h("behind"),h("left"),h("right"))}return n}function qm(){const r=new Ot,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 a=new ai(n,10,7);a.scale(1,.85,1),a.translate(t,0,i),Qt(r,a,[.26,.28,.17],s)}return r}function Ym(){const r=new Ot;return Qt(r,new gs(.15,.22,2,8),[.32,.22,.14],1),Qt(r,new ao(1.4,3,8),[.16,.32,.14],3.5),r}const $m=.275,B5=.35,z5=.9,H5=34,G5=8,V5=1.4;function Km(){const r=new Ot,e=new ai($m,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,B5/$m,1);const t=ui.repeatOnGrid(e,H5,z5,G5,V5),i=Qt(r,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(),r}function Zm(){const r=new Ot,e=new ai(1,7,3,0,Math.PI*2,0,Math.PI/2);return Qt(r,e,[.38,.36,.32],0),r}function jm(r){const e=new Ot;return Qt(e,new gs(.05,.08,5,8),[.28,.28,.3],2.5),Qt(e,new ai(.25,12,8),r?Fv:L5,5.1,!0),e}const W5=[.08,.08,.08],js=.8,Ar=1,Kn=1.7,_d=.6,xa=.4,oc=1.2,br=.7,as=.5,X5=Kn;function Jm(r,e,t){const i=new Ot;Qt(i,new fn(1.8,1.4,4.2),[.45,.14,.14],.7),Qt(i,new fn(js*2,_d,Ar*2),[.4,.12,.12],Kn);for(const s of[-1.4,1.4])for(const a of[-.95,.95]){const o=Qt(i,new gs(.4,.4,.3,12),W5,.4);o.rotation.z=Math.PI/2,o.position.x=a,o.position.z=s}const n=r?Uv:O5;for(const s of[-.7,.7])Qt(i,new ai(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(ss(i,oc,xa,ns,0,Kn,-Ar-is,Hi(e,"front")),ss(i,oc,xa,ns,0,Kn,Ar+is,Hi(e,"behind")),ss(i,ns,xa,br,-js-is,Kn,-as,Hi(e,"front-left")),ss(i,ns,xa,br,-js-is,Kn,as,Hi(e,"behind-left")),ss(i,ns,xa,br,js+is,Kn,-as,Hi(e,"front-right")),ss(i,ns,xa,br,js+is,Kn,as,Hi(e,"behind-right")),t){Hv(i,t,F5,.5,js,Ar,Vv);const s=(l,c)=>{const h=[];return Hi(e,l)!==void 0&&h.push({main:br,center:-as}),Hi(e,c)!==void 0&&h.push({main:br,center:as}),h},a=Hi(e,"front")!==void 0?[{main:oc,center:0}]:[],o=Hi(e,"behind")!==void 0?[{main:oc,center:0}]:[];zv(i,js,Ar,Kn-_d/2,Kn+_d/2,xa,a,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const Qm=new Map;function eg(r,e){if(!r)return;const t=[[r.lengthM,e.lengthM],[r.heightM,e.heightM],[r.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function Ad(r,e){return e>1e-6?r/e:1}const Wv=[.95,.9,.82],tg=[.72,.74,.78];function ig(){const r=new Ot;Qt(r,new gs(.25,.3,1.5,10),tg,.75),Qt(r,new ai(.26,10,8),tg,1.74);const e=Qt(r,new ao(.07,.16,8),Wv,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,r}function Df(){const r=new Ot;Qt(r,new gs(.25,.3,1.5,10),[.3,.3,.35],.75),Qt(r,new ai(.22,10,8),[.62,.52,.46],1.72);const e=Qt(r,new ao(.07,.16,8),Wv,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,r}const q5=17,ng=18.5,Td=[.16,.16,.18];function sg(){const r=new Ot,e=Qt(r,new gs(1.8,1.8,ng*2,10),Td,0);e.rotation.x=Math.PI/2,Qt(r,new fn(q5*2,.5,4.5),Td,0);const t=Qt(r,new fn(.4,5.5,4),Td,3);return t.position.z=-ng+2,r}class ui{static groundUnderFootprint(e,t,i,n,s,a=0){const o=ui.sizeOf(e);if(a>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));let d=-1/0;for(let u=0;u<c;u++){const f=(u/(c-1)-.5)*o.lengthM;for(let p=0;p<h;p++){const v=(p/(h-1)-.5)*o.widthM,m=t+Math.sin(l)*f+Math.cos(l)*v,g=i-Math.cos(l)*f+Math.sin(l)*v;d=Math.max(d,s(m,g))}}return Number.isFinite(d)?d:s(t,i)}static occupantView(e){const t=e.witnessSide;if(!t)throw new Error("occupantView requires object.witnessSide to be set");const{x:i,z:n}=e.kind==="vehicle"?Af(t,js,Ar,Vv):Af(t,Tf/2,If/2,Gv),s=e.kind==="vehicle"?X5:(e.occupiedFloor??0)*ea+U5,a=(e.headingDeg??0)-Bv[t]/xd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:a}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ui.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const a=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=a.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),f=new Uint32Array(l.count*h),p=-((t-1)*i)/2,v=-((n-1)*s)/2;let m=0;for(let w=0;w<n;w++)for(let x=0;x<t;x++,m++){const y=v+w*s,S=p+x*i;for(let E=0;E<c;E++){const I=(m*c+E)*3;d[I]=a.getX(E)+y,d[I+1]=a.getY(E),d[I+2]=a.getZ(E)+S,u[I]=o.getX(E),u[I+1]=o.getY(E),u[I+2]=o.getZ(E)}for(let E=0;E<l.count;E++)f[m*l.count+E]=l.getX(E)+m*c}const g=new pt;return g.setAttribute("position",new At(d,3)),g.setAttribute("normal",new At(u,3)),g.setIndex(new ep(f,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,a=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!a||!o)return;const l=s.geometry.getAttribute("position"),c=new Ze().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(a[h*3],0,a[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,a[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 a=n.material;a?.color&&a.color.set(t)})}static build(e,t){const i=e.kind==="building"?Xm(e.floors??Fr,e.windows,e.witnessSide,e.occupiedFloor):e.kind==="tree"?Ym():e.kind==="shrub"?qm():e.kind==="crop"?Km():e.kind==="mound"?Zm():e.kind==="streetlight"?jm(t):e.kind==="vehicle"?Jm(t,e.windows,e.witnessSide):e.kind==="aircraft"?sg():e.kind==="entity"?ig():Df();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Ot;return i.name=ui.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*xd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t)return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:Ad(t.widthM,i.widthM),t.heightM===void 0?1:Ad(t.heightM,i.heightM),t.lengthM===void 0?1:Ad(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Fr)+1):0,n=`${e}:${i}`,s=Qm.get(n);if(s)return s;const a=e==="building"?Xm(i-1,void 0,void 0,void 0):e==="tree"?Ym():e==="shrub"?qm():e==="crop"?Km():e==="mound"?Zm():e==="streetlight"?jm(!1):e==="vehicle"?Jm(!1,void 0,void 0):e==="aircraft"?sg():e==="entity"?ig():Df(),o=new An().setFromObject(a),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(a),Qm.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.witnessSide!==void 0&&Dr(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),a=new Ot;a.name=ui.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*xd,i.updateMatrixWorld(!0);const o=new An().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=eg(t.sizeM,l)??eg(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)}),a.add(i),s.parent?(s.parent.add(a),s.removeFromParent()):e.add(a),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=P5(i.color),s=Qt(e,new ai(Sd,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,a=1){if(!t||t.length===0)return;const o=new Map(t.map(d=>[d.id,d])),l=Math.max(1,n*A5/Sd),c=Sd*l,h=a/(Math.PI*c*c);for(const d of e.children){if(!(d instanceof De))continue;const u=d.userData.lightId;if(!u)continue;const f=o.get(u);if(!f)continue;const p=s>0?h1(f,i,i+s):Wy(f,i)?1:0;d.visible=p>0;const v=d.material,m=d.userData.lightColor;if(m){const g=p*T5*(f.intensity??1)*h;v.color.setRGB(m[0]*g,m[1]*g,m[2]*g)}d.scale.setScalar(l)}}static setLit(e,t,i,n=1){const[s,a,o]=t==="streetlight"?[I5,D5,Fv]:[C5,R5,Uv],l=i?s*n/(Math.PI*a*a):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 Id="https://ufoathome.org/models/index.json";class Y5{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??$5()}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 $5(){const r=`?v=${encodeURIComponent("0.65.3")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==Id?[e,Id]:[Id]).map(t=>t+r)}const Cc=[{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 Y5}];async function ag(r){const{GLTFLoader:e}=await Sn(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-BeVCsKtR.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(r,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${r}: ${s instanceof Error?s.message:String(s)}`)))})}const Xv=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class cn{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 Xi(2,2),new lt({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:cn.VERTEX,fragmentShader:cn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Fi,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*cn.MIN_ANGLE_RAD),a=cn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(cn.MAX_SHARE,t/a)}}static conserving(e){return Math.sqrt(e/(cn.MAX_SHARE*cn.FILL*Math.PI))}static VERTEX=`
|
|
7958
|
+
`,transparent:!0,blending:Fi,depthWrite:!1,side:Wt,fog:!1}),this.object=new De(new ai(Pi.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,a){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,a)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Pi.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Pi.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)<Pi.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Pi.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)<Pi.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Pi.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?Pi.URGENT_BUDGET_MS:Pi.BUDGET_MS;do this.sky.trace(Pi.RAYS_PER_BATCH);while(this.sky.tracedRays<Pi.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Pi.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]=Bn.toHalfFloat(t[n]),this.texels[i+1]=Bn.toHalfFloat(t[n+1]),this.texels[i+2]=Bn.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 Mi{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Mi.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Mi.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=Mi.MAX_CONCURRENT*Mi.SEGMENTS*Mi.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new pt;t.setAttribute("position",new it(this.positions,3)),t.setAttribute("color",new it(this.colors,3)),t.setDrawRange(0,0),this.object=new De(t,new En({vertexColors:!0,transparent:!0,blending:Fi,depthWrite:!1,fog:!1,side:dn})),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=Ha.aliveAt(this.meteors,e).slice(0,Mi.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:a}of i){const o=this.radiantOf(s);if(!o)continue;const l=ci(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+a*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),f=Math.min(1,(1-a)*2.5),p=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*f,m=x=>v*x**1.6,g=x=>this.onSphere(l,p,u+(d-u)*x),w=x=>Mi.RADIUS*((Mi.MIN_HALF_WIDTH_DEG+Mi.BRIGHT_HALF_WIDTH_DEG*s.brightness*x)*Math.PI/180);for(let x=1;x<Mi.POINTS;x++){const y=(x-1)/Mi.SEGMENTS,S=x/Mi.SEGMENTS;n=this.pushRibbon(n,g(y),g(S),w(y),w(S),m(y),m(S))}}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=ci(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),a=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,a,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,a,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Mi.ACROSS-1;c++){const h=c-1,d=c,u=this.across(t,l,n,h),f=this.across(i,l,s,h),p=this.across(t,l,n,d),v=this.across(i,l,s,d),m=(g,w)=>g===0?w:0;e=this.push(e,u,m(h,a)),e=this.push(e,f,m(h,o)),e=this.push(e,p,m(d,a)),e=this.push(e,f,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,p,m(d,a))}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=_e.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),a=Math.sin(n);return{x:e.x*s+t.x*a,y:e.y*s+t.y*a,z:e.z*s+t.z*a}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),a=Math.sin(n);return{x:(e.x*s+t.x*a)*Mi.RADIUS,y:(e.y*s+t.y*a)*Mi.RADIUS,z:(e.z*s+t.z*a)*Mi.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()}}const xd=Math.PI/180;function Qt(r,e,t,i,n=!1){const s=n?new En({color:new se(...t)}):new ro({color:new se(...t)}),a=new De(e,s);return a.position.y=i,a.userData={emissive:n},a.castShadow=!n,a.receiveShadow=!n,r.add(a),a}const Sd=.25,A5=.002,T5=100,I5=1500,D5=.25,C5=1e3,R5=.15;function P5(r){const e=/^#([0-9a-f]{6})$/i.exec(r);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 L5=[.32,.32,.3],Fv=[1,.85,.5],O5=[.25,.25,.25],Uv=[1,.97,.85],k5=[.55,.68,.72],is=.03,ns=.05,Bv={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 Hi(r,e){return r?.[e]}function ss(r,e,t,i,n,s,a,o){if(o===void 0)return;const l=new ro({color:new se(...k5),transparent:!0,opacity:o/100}),c=new De(new fn(e,t,i),l);c.position.set(n,s,a),r.add(c)}const bn=.05,Ed=[.6,.58,.53],Wm=[.5,.5,.48];function Or(r,e,t,i,n,s,a,o){const l=new ro({color:new se(...o),side:Wt}),c=new De(new fn(e,t,i),l);c.position.set(n,s,a),r.add(c)}function N5(r,e,t,i,n,s,a,o,l){const c=(i-s)/2,h=(p,v,m,g)=>{p<=0||(e?Or(r,p,v,bn,m,o+g,a,l):Or(r,bn,v,p,a,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((p,v)=>p.center-v.center);let u=-t/2;for(const p of d){const m=p.center-p.main/2-u;h(m,s,u+m/2,0),u=p.center+p.main/2}const f=t/2-u;h(f,s,u+f/2,0)}function zv(r,e,t,i,n,s,a,o,l,c){const h=(i+n)/2,d=n-i,u=(f,p,v,m)=>{f.length===0?p?Or(r,v,d,bn,0,h,m,Ed):Or(r,bn,d,v,m,h,0,Ed):N5(r,p,v,d,f,s,m,h,Ed)};u(a,!0,e*2,-t+bn),u(o,!0,e*2,t-bn),u(l,!1,t*2,-e+bn),u(c,!1,t*2,e-bn),Or(r,e*2,bn,t*2,0,i+bn,0,Wm),Or(r,e*2,bn,t*2,0,n-bn,0,Wm)}function Af(r,e,t,i){switch(r){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:-as};case"front-right":return{x:e-i,z:-as};case"behind-left":return{x:-(e-i),z:as};case"behind-right":return{x:e-i,z:as}}}function Hv(r,e,t,i,n,s,a){const o=Df();o.scale.setScalar(i),o.rotation.y=Bv[e];const{x:l,z:c}=Af(e,n,s,a);o.position.set(l,t,c),r.add(o)}const Tf=6,If=6,ea=3,To=1.4,Io=ea*.5,Gv=1.2,Vv=.35,F5=.75,U5=1.6;function Xm(r,e,t,i){const n=new Ot,s=Math.max(1,r+1),a=s*ea;Qt(n,new fn(Tf,a,If),[.55,.54,.5],a/2);const o=Tf/2,l=If/2;for(let c=0;c<s;c++){const h=c*ea+ea/2;ss(n,To,Io,ns,0,h,-l-is,Hi(e,"front")),ss(n,To,Io,ns,0,h,l+is,Hi(e,"behind")),ss(n,ns,Io,To,-o-is,h,0,Hi(e,"left")),ss(n,ns,Io,To,o+is,h,0,Hi(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);Hv(n,t,c*ea,.72,o,l,Gv);const h=d=>Hi(e,d)!==void 0?[{main:To,center:0}]:[];zv(n,o,l,c*ea,(c+1)*ea,Io,h("front"),h("behind"),h("left"),h("right"))}return n}function qm(){const r=new Ot,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 a=new ai(n,10,7);a.scale(1,.85,1),a.translate(t,0,i),Qt(r,a,[.26,.28,.17],s)}return r}function Ym(){const r=new Ot;return Qt(r,new gs(.15,.22,2,8),[.32,.22,.14],1),Qt(r,new ao(1.4,3,8),[.16,.32,.14],3.5),r}const $m=.275,B5=.35,z5=.9,H5=34,G5=8,V5=1.4;function Km(){const r=new Ot,e=new ai($m,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,B5/$m,1);const t=ui.repeatOnGrid(e,H5,z5,G5,V5),i=Qt(r,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(),r}function Zm(){const r=new Ot,e=new ai(1,7,3,0,Math.PI*2,0,Math.PI/2);return Qt(r,e,[.38,.36,.32],0),r}function jm(r){const e=new Ot;return Qt(e,new gs(.05,.08,5,8),[.28,.28,.3],2.5),Qt(e,new ai(.25,12,8),r?Fv:L5,5.1,!0),e}const W5=[.08,.08,.08],js=.8,Ar=1,Kn=1.7,_d=.6,xa=.4,oc=1.2,br=.7,as=.5,X5=Kn;function Jm(r,e,t){const i=new Ot;Qt(i,new fn(1.8,1.4,4.2),[.45,.14,.14],.7),Qt(i,new fn(js*2,_d,Ar*2),[.4,.12,.12],Kn);for(const s of[-1.4,1.4])for(const a of[-.95,.95]){const o=Qt(i,new gs(.4,.4,.3,12),W5,.4);o.rotation.z=Math.PI/2,o.position.x=a,o.position.z=s}const n=r?Uv:O5;for(const s of[-.7,.7])Qt(i,new ai(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(ss(i,oc,xa,ns,0,Kn,-Ar-is,Hi(e,"front")),ss(i,oc,xa,ns,0,Kn,Ar+is,Hi(e,"behind")),ss(i,ns,xa,br,-js-is,Kn,-as,Hi(e,"front-left")),ss(i,ns,xa,br,-js-is,Kn,as,Hi(e,"behind-left")),ss(i,ns,xa,br,js+is,Kn,-as,Hi(e,"front-right")),ss(i,ns,xa,br,js+is,Kn,as,Hi(e,"behind-right")),t){Hv(i,t,F5,.5,js,Ar,Vv);const s=(l,c)=>{const h=[];return Hi(e,l)!==void 0&&h.push({main:br,center:-as}),Hi(e,c)!==void 0&&h.push({main:br,center:as}),h},a=Hi(e,"front")!==void 0?[{main:oc,center:0}]:[],o=Hi(e,"behind")!==void 0?[{main:oc,center:0}]:[];zv(i,js,Ar,Kn-_d/2,Kn+_d/2,xa,a,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const Qm=new Map;function eg(r,e){if(!r)return;const t=[[r.lengthM,e.lengthM],[r.heightM,e.heightM],[r.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function Ad(r,e){return e>1e-6?r/e:1}const Wv=[.95,.9,.82],tg=[.72,.74,.78];function ig(){const r=new Ot;Qt(r,new gs(.25,.3,1.5,10),tg,.75),Qt(r,new ai(.26,10,8),tg,1.74);const e=Qt(r,new ao(.07,.16,8),Wv,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,r}function Df(){const r=new Ot;Qt(r,new gs(.25,.3,1.5,10),[.3,.3,.35],.75),Qt(r,new ai(.22,10,8),[.62,.52,.46],1.72);const e=Qt(r,new ao(.07,.16,8),Wv,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,r}const q5=17,ng=18.5,Td=[.16,.16,.18];function sg(){const r=new Ot,e=Qt(r,new gs(1.8,1.8,ng*2,10),Td,0);e.rotation.x=Math.PI/2,Qt(r,new fn(q5*2,.5,4.5),Td,0);const t=Qt(r,new fn(.4,5.5,4),Td,3);return t.position.z=-ng+2,r}class ui{static groundUnderFootprint(e,t,i,n,s,a=0){const o=ui.sizeOf(e);if(a>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));let d=-1/0;for(let u=0;u<c;u++){const f=(u/(c-1)-.5)*o.lengthM;for(let p=0;p<h;p++){const v=(p/(h-1)-.5)*o.widthM,m=t+Math.sin(l)*f+Math.cos(l)*v,g=i-Math.cos(l)*f+Math.sin(l)*v;d=Math.max(d,s(m,g))}}return Number.isFinite(d)?d:s(t,i)}static occupantView(e){const t=e.witnessSide;if(!t)throw new Error("occupantView requires object.witnessSide to be set");const{x:i,z:n}=e.kind==="vehicle"?Af(t,js,Ar,Vv):Af(t,Tf/2,If/2,Gv),s=e.kind==="vehicle"?X5:(e.occupiedFloor??0)*ea+U5,a=(e.headingDeg??0)-Bv[t]/xd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:a}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ui.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const a=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=a.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),f=new Uint32Array(l.count*h),p=-((t-1)*i)/2,v=-((n-1)*s)/2;let m=0;for(let w=0;w<n;w++)for(let x=0;x<t;x++,m++){const y=v+w*s,S=p+x*i;for(let E=0;E<c;E++){const I=(m*c+E)*3;d[I]=a.getX(E)+y,d[I+1]=a.getY(E),d[I+2]=a.getZ(E)+S,u[I]=o.getX(E),u[I+1]=o.getY(E),u[I+2]=o.getZ(E)}for(let E=0;E<l.count;E++)f[m*l.count+E]=l.getX(E)+m*c}const g=new pt;return g.setAttribute("position",new At(d,3)),g.setAttribute("normal",new At(u,3)),g.setIndex(new ep(f,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,a=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!a||!o)return;const l=s.geometry.getAttribute("position"),c=new Ze().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(a[h*3],0,a[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,a[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 a=n.material;a?.color&&a.color.set(t)})}static build(e,t){const i=e.kind==="building"?Xm(e.floors??Fr,e.windows,e.witnessSide,e.occupiedFloor):e.kind==="tree"?Ym():e.kind==="shrub"?qm():e.kind==="crop"?Km():e.kind==="mound"?Zm():e.kind==="streetlight"?jm(t):e.kind==="vehicle"?Jm(t,e.windows,e.witnessSide):e.kind==="aircraft"?sg():e.kind==="entity"?ig():Df();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Ot;return i.name=ui.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*xd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t)return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:Ad(t.widthM,i.widthM),t.heightM===void 0?1:Ad(t.heightM,i.heightM),t.lengthM===void 0?1:Ad(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Fr)+1):0,n=`${e}:${i}`,s=Qm.get(n);if(s)return s;const a=e==="building"?Xm(i-1,void 0,void 0,void 0):e==="tree"?Ym():e==="shrub"?qm():e==="crop"?Km():e==="mound"?Zm():e==="streetlight"?jm(!1):e==="vehicle"?Jm(!1,void 0,void 0):e==="aircraft"?sg():e==="entity"?ig():Df(),o=new An().setFromObject(a),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(a),Qm.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.witnessSide!==void 0&&Dr(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),a=new Ot;a.name=ui.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*xd,i.updateMatrixWorld(!0);const o=new An().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=eg(t.sizeM,l)??eg(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)}),a.add(i),s.parent?(s.parent.add(a),s.removeFromParent()):e.add(a),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=P5(i.color),s=Qt(e,new ai(Sd,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,a=1){if(!t||t.length===0)return;const o=new Map(t.map(d=>[d.id,d])),l=Math.max(1,n*A5/Sd),c=Sd*l,h=a/(Math.PI*c*c);for(const d of e.children){if(!(d instanceof De))continue;const u=d.userData.lightId;if(!u)continue;const f=o.get(u);if(!f)continue;const p=s>0?h1(f,i,i+s):Wy(f,i)?1:0;d.visible=p>0;const v=d.material,m=d.userData.lightColor;if(m){const g=p*T5*(f.intensity??1)*h;v.color.setRGB(m[0]*g,m[1]*g,m[2]*g)}d.scale.setScalar(l)}}static setLit(e,t,i,n=1){const[s,a,o]=t==="streetlight"?[I5,D5,Fv]:[C5,R5,Uv],l=i?s*n/(Math.PI*a*a):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 Id="https://ufoathome.org/models/index.json";class Y5{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??$5()}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 $5(){const r=`?v=${encodeURIComponent("0.65.4")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==Id?[e,Id]:[Id]).map(t=>t+r)}const Cc=[{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 Y5}];async function ag(r){const{GLTFLoader:e}=await Sn(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-BeVCsKtR.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(r,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${r}: ${s instanceof Error?s.message:String(s)}`)))})}const Xv=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class cn{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 Xi(2,2),new lt({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:cn.VERTEX,fragmentShader:cn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Fi,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*cn.MIN_ANGLE_RAD),a=cn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(cn.MAX_SHARE,t/a)}}static conserving(e){return Math.sqrt(e/(cn.MAX_SHARE*cn.FILL*Math.PI))}static VERTEX=`
|
|
7958
7959
|
uniform float uRadius;
|
|
7959
7960
|
varying vec2 vOffset;
|
|
7960
7961
|
void main() {
|
|
@@ -8454,11 +8455,9 @@ cloudPoint = (modelMatrix * vec4(transformed, 1.0)).xyz - cameraPosition;`);let
|
|
|
8454
8455
|
gl_FragColor = vec4(relativeOf(light.xyz, light.w), 1.0);
|
|
8455
8456
|
#include <colorspace_fragment>
|
|
8456
8457
|
}
|
|
8457
|
-
`})}get supported(){return this.tables.supported}get ready(){return this.hasViews}get ambient(){return this.ready?this.ambientColours:void 0}setInstrument(e,t){const i=e?10**(.4*t):1;i===this.exposureScale&&this.seenByEye===!e||(this.exposureScale=i,this.seenByEye=!e,this.material.uniforms.uExposureScale.value=i,this.applyAdaptation(this.adaptingLuminance),this.onChange())}setConditions(e){this.tables.setMedium(new ke(e)),this.tables.ready||(this.drawnKey="")}advance(e){!this.supported||this.tables.ready||this.tables.advance(e)&&this.state&&this.draw(this.state)}invalidate(){this.tables.invalidate(),this.drawnKey="",this.hasViews=!1}update(e){this.state=e,this.tables.ready?this.draw(e):this.hasViews||this.applyAdaptation(li.predictedZenithLuminance(e))}dispose(){this.tables.dispose(),this.material.dispose()}draw(e){const t=[e.altitudeM.toFixed(0),e.sun.altitudeDeg.toFixed(3),e.moon.altitudeDeg.toFixed(3)].join(":"),i=this.material.uniforms;if(i.uSunAzimuth.value.copy(li.azimuthVector(e.sun.azimuthDeg)),i.uMoonAzimuth.value.copy(li.azimuthVector(e.moon.azimuthDeg)),i.uMoonScale.value=10**(-.4*(e.moon.magnitude-li.SUN_MAGNITUDE)),i.uObserverRadius.value=ke.GROUND_RADIUS_M+Math.max(e.altitudeM,2),t!==this.drawnKey){this.tables.renderSkyViews(e.altitudeM,e.sun.altitudeDeg,e.moon.altitudeDeg);const n=!this.hasViews;this.hasViews=!0,this.drawnKey=t,n&&!this.ambientColours&&this.adaptFromViews(this.tables.readSkyViewNow("sun"),this.tables.readSkyViewNow("moon"),e),this.readBack()}this.onChange()}async readBack(){if(this.reading){this.readAgain=!0;return}this.reading=!0;try{const[e,t]=await Promise.all([this.tables.readSkyView("sun"),this.tables.readSkyView("moon")]),i=this.state;i&&this.adaptFromViews(e,t,i)}finally{this.reading=!1}this.readAgain&&(this.readAgain=!1,this.readBack())}adaptToSurroundings(e){if(!(e>0)||!(this.clearAdaptation>0))return!1;const t=e/this.clearAdaptation;return Math.abs(Math.log(t/this.surroundingsFactor))<.05?!1:(this.surroundingsFactor=t,this.views&&this.state?this.adaptFromViews(this.views.sun,this.views.moon,this.state):this.applyAdaptation(this.clearAdaptation*t),!0)}resetSurroundings(){this.surroundingsFactor=1}surroundingsFactor=1;clearAdaptation=0;views;adaptFromViews(e,t,i){this.views={sun:e,moon:t};const n=10**(-.4*(i.moon.magnitude-li.SUN_MAGNITUDE)),s=li.airglowXyzs(),a=(m,g,w=!0)=>{const x=(90-m)*Math.PI/180,y=Ve.skyViewUv(i.altitudeM,x,li.azimuthBetween(g,i.sun.azimuthDeg)),S=Ve.skyViewUv(i.altitudeM,x,li.azimuthBetween(g,i.moon.azimuthDeg)),E=li.bilinear(e,y.u,y.v),I=li.bilinear(t,S.u,S.v),b=w&&m>0?li.airglowFactor(m):0;return[0,1,2,3].map(T=>E[T]+n*I[T]+s[T]*b)};let o=0,l=0;for(let m=5;m<90;m+=10){const g=Math.cos(m*Math.PI/180);for(let w=0;w<360;w+=30)o+=g*Math.log(Math.max(a(m,w)[1],1e-9)),l+=g}this.clearAdaptation=Math.exp(o/l),this.applyAdaptation(this.clearAdaptation*this.surroundingsFactor);const c=([m,g,w,x])=>{const y=this.exposureScale,S=this.adaptingLuminance*y;return _e.relativeOf([m*y,g*y,w*y],x*y,S,0)},h=(m,g,w=!0)=>c(a(m,g,w)),d=[0,0,0,0],u=5*Math.PI/180,f=20*Math.PI/180;for(let m=2.5;m<90;m+=5){const g=m*Math.PI/180,w=Math.sin(g)*Math.cos(g)*u*f;for(let x=10;x<360;x+=20){const y=a(m,x);for(let S=0;S<4;S++)d[S]+=y[S]*w}}const p=[0,0,0],v=[0,0,0];for(let m=0;m<360;m+=20){const g=h(2,m),w=h(2,m,!1);for(let x=0;x<3;x++)p[x]+=g[x]/18,v[x]+=w[x]/18}this.ambientColours={zenith:h(90,0),horizon:p,airlight:v,skyIrradiance:c(d),scale:this.relativeScale},this.onChange()}relativeOfLuminance(e,t){const[i,n,s]=e,a=.2126*i+.7152*n+.0722*s,o=a>0?t/a:0,l=[o*(.4124*i+.3576*n+.1805*s),t,o*(.0193*i+.1192*n+.9505*s)],c=this.exposureScale,h=this.adaptingLuminance*c;return _e.relativeOf([l[0]*c,l[1]*c,l[2]*c],l[1]*c,h,0)}get rodShare(){const e=this.adaptingLuminance*this.exposureScale;return this.seenByEye?_e.rodShare(e):0}get relativeScale(){const e=this.adaptingLuminance*this.exposureScale;return this.exposureScale/_e.semiSaturation(e)}applyAdaptation(e){this.adaptingLuminance=e;const t=e*this.exposureScale;this.material.uniforms.uRodShare.value=0,this.material.uniforms.uInverseSemiSaturation.value=1/_e.semiSaturation(t)}static predictedZenithLuminance(e){const t=n=>90-n.altitudeDeg,i=We.magPerArcsec2({altitudeDeg:e.sun.altitudeDeg,separationDeg:t(e.sun)},{phaseAngleDeg:e.moon.phaseAngleDeg,altitudeDeg:e.moon.altitudeDeg,separationDeg:t(e.moon)},90);return We.toNanolamberts(i)*li.CANDELAS_PER_NANOLAMBERT}static CANDELAS_PER_NANOLAMBERT=1e-5/Math.PI;static airglowXyzs(){const e=We.toNanolamberts(We.AIRGLOW_MAG_PER_ARCSEC2)*li.CANDELAS_PER_NANOLAMBERT;return[e*.3127/.329,e,e*(1-.3127-.329)/.329,e]}static airglowFactor(e){const t=Math.cos(e*Math.PI/180)**2,i=ke.GROUND_RADIUS_M/(ke.GROUND_RADIUS_M+li.AIRGLOW_LAYER_ALTITUDE_M),n=1/Math.sqrt(Math.max(1-i*i*t,1e-4)),s=1/Math.sqrt(Math.max(1-.96*t,.001));return n*10**(-.4*We.EXTINCTION_PER_AIR_MASS*(s-1))}static azimuthVector(e){const t=e*Math.PI/180;return new ye(Math.sin(t),-Math.cos(t))}static azimuthBetween(e,t){const i=Math.abs(e-t)%360;return(i>180?360-i:i)*Math.PI/180}static bilinear(e,t,i){const n=Ve.SKY_VIEW_WIDTH,s=Ve.SKY_VIEW_HEIGHT,a=Math.min(Math.max(t*n-.5,0),n-1),o=Math.min(Math.max(i*s-.5,0),s-1),l=Math.floor(a),c=Math.floor(o),h=Math.min(l+1,n-1),d=Math.min(c+1,s-1),u=a-l,f=o-c,p=(v,m,g)=>e[(v*n+m)*4+g];return[0,1,2,3].map(v=>(p(c,l,v)*(1-u)+p(c,h,v)*u)*(1-f)+(p(d,l,v)*(1-u)+p(d,h,v)*u)*f)}}class Rs{static DRY_OPTICAL_DEPTH=.08;static GROWTH_EXPONENT=.3;static MAX_RELATIVE_HUMIDITY=.95;static OPTICAL_DEPTH_STEP=.02;static relativeHumidity(e,t){const i=n=>Math.exp(17.625*n/(243.04+n));return Math.min(Math.max(i(t)/i(e),0),1)}static opticalDepth(e){const t=Math.min(Math.max(e,0),Rs.MAX_RELATIVE_HUMIDITY);return Rs.DRY_OPTICAL_DEPTH*(1-t)**-.3}static steppedOpticalDepth(e){const t=Rs.OPTICAL_DEPTH_STEP;return Math.round(Rs.opticalDepth(e)/t)*t}}class Mn{static CHANNEL_WAVELENGTHS_NM=[680,550,440];static KOSCHMIEDER=-Math.log(.02);static PRECIPITATION_EXTINCTION_PER_KM={rain:{a:.365,b:.63},snow:{a:1.023,b:.72},hail:{a:.365,b:.63}};static FULL_INTENSITY_MM_PER_H=8;rayleigh;aerosol;constructor(e=ke.DEFAULT_AEROSOL_OPTICAL_DEPTH,t=0){const[i,n,s]=Mn.CHANNEL_WAVELENGTHS_NM;this.rayleigh=[ke.rayleighAt(i),ke.rayleighAt(n),ke.rayleighAt(s)];const a=o=>e/ke.AEROSOL_SCALE_HEIGHT_M*(o/550)**-1.3;this.aerosol=[a(i)+t,a(n)+t,a(s)+t],this.precipitationPerM=t}precipitationPerM;static of(e){const t=e.relativeHumidity===void 0?void 0:Rs.opticalDepth(e.relativeHumidity);return new Mn(t,Mn.precipitationExtinction(e))}static precipitationExtinction(e){if(e.precipitationType==="none"||!(e.precipitationIntensity>0))return 0;const t=Mn.PRECIPITATION_EXTINCTION_PER_KM[e.precipitationType],i=e.precipitationIntensity*Mn.FULL_INTENSITY_MM_PER_H;return t.a*i**t.b/1e3}transmittance(e,t,i){const n=Mn.meanDensity(e,t,ke.RAYLEIGH_SCALE_HEIGHT_M),s=Mn.meanDensity(e,t,ke.AEROSOL_SCALE_HEIGHT_M);return[0,1,2].map(a=>Math.exp(-(this.rayleigh[a]*n+this.aerosol[a]*s)*i))}transmittanceFromSpace(e,t){const i=Math.max(t,0),n=1/(Math.sin(i*Math.PI/180)+.50572*(i+6.07995)**-1.6364),s=ke.RAYLEIGH_SCALE_HEIGHT_M*ke.rayleighDensity(e),a=ke.AEROSOL_SCALE_HEIGHT_M*ke.aerosolDensity(e);return[0,1,2].map(o=>Math.exp(-(this.rayleigh[o]*s+(this.aerosol[o]-this.precipitationPerM)*a)*n))}extinctionAt(e){return{rayleigh:this.rayleigh[1]*ke.rayleighDensity(e),aerosol:this.aerosol[1]*ke.aerosolDensity(e)}}visibilityM(e){const{rayleigh:t,aerosol:i}=this.extinctionAt(e);return Mn.KOSCHMIEDER/(t+i)}get rayleighRatios(){return[this.rayleigh[0]/this.rayleigh[1],1,this.rayleigh[2]/this.rayleigh[1]]}static meanDensity(e,t,i){const n=Math.max(e,0),s=Math.max(t,0),a=s-n;return Math.abs(a)<.01?Math.exp(-n/i):i*(Math.exp(-n/i)-Math.exp(-s/i))/a}}class Ga extends Qf{constructor(){super(new se(0,0,0),0,0),Ga.install()}setAir(e,t,i){const{rayleigh:n,aerosol:s}=e.extinctionAt(t);this.near=n,this.far=s,this.color.setRGB(i[0],i[1],i[2])}static fadeRim(e,t,i
|
|
8458
|
-
uniform vec3 aerialHorizon;`).replace("#include <fog_fragment>",`#include <fog_fragment>
|
|
8458
|
+
`})}get supported(){return this.tables.supported}get ready(){return this.hasViews}get ambient(){return this.ready?this.ambientColours:void 0}setInstrument(e,t){const i=e?10**(.4*t):1;i===this.exposureScale&&this.seenByEye===!e||(this.exposureScale=i,this.seenByEye=!e,this.material.uniforms.uExposureScale.value=i,this.applyAdaptation(this.adaptingLuminance),this.onChange())}setConditions(e){this.tables.setMedium(new ke(e)),this.tables.ready||(this.drawnKey="")}advance(e){!this.supported||this.tables.ready||this.tables.advance(e)&&this.state&&this.draw(this.state)}invalidate(){this.tables.invalidate(),this.drawnKey="",this.hasViews=!1}update(e){this.state=e,this.tables.ready?this.draw(e):this.hasViews||this.applyAdaptation(li.predictedZenithLuminance(e))}dispose(){this.tables.dispose(),this.material.dispose()}draw(e){const t=[e.altitudeM.toFixed(0),e.sun.altitudeDeg.toFixed(3),e.moon.altitudeDeg.toFixed(3)].join(":"),i=this.material.uniforms;if(i.uSunAzimuth.value.copy(li.azimuthVector(e.sun.azimuthDeg)),i.uMoonAzimuth.value.copy(li.azimuthVector(e.moon.azimuthDeg)),i.uMoonScale.value=10**(-.4*(e.moon.magnitude-li.SUN_MAGNITUDE)),i.uObserverRadius.value=ke.GROUND_RADIUS_M+Math.max(e.altitudeM,2),t!==this.drawnKey){this.tables.renderSkyViews(e.altitudeM,e.sun.altitudeDeg,e.moon.altitudeDeg);const n=!this.hasViews;this.hasViews=!0,this.drawnKey=t,n&&!this.ambientColours&&this.adaptFromViews(this.tables.readSkyViewNow("sun"),this.tables.readSkyViewNow("moon"),e),this.readBack()}this.onChange()}async readBack(){if(this.reading){this.readAgain=!0;return}this.reading=!0;try{const[e,t]=await Promise.all([this.tables.readSkyView("sun"),this.tables.readSkyView("moon")]),i=this.state;i&&this.adaptFromViews(e,t,i)}finally{this.reading=!1}this.readAgain&&(this.readAgain=!1,this.readBack())}adaptToSurroundings(e){if(!(e>0)||!(this.clearAdaptation>0))return!1;const t=e/this.clearAdaptation;return Math.abs(Math.log(t/this.surroundingsFactor))<.05?!1:(this.surroundingsFactor=t,this.views&&this.state?this.adaptFromViews(this.views.sun,this.views.moon,this.state):this.applyAdaptation(this.clearAdaptation*t),!0)}resetSurroundings(){this.surroundingsFactor=1}surroundingsFactor=1;clearAdaptation=0;views;adaptFromViews(e,t,i){this.views={sun:e,moon:t};const n=10**(-.4*(i.moon.magnitude-li.SUN_MAGNITUDE)),s=li.airglowXyzs(),a=(m,g,w=!0)=>{const x=(90-m)*Math.PI/180,y=Ve.skyViewUv(i.altitudeM,x,li.azimuthBetween(g,i.sun.azimuthDeg)),S=Ve.skyViewUv(i.altitudeM,x,li.azimuthBetween(g,i.moon.azimuthDeg)),E=li.bilinear(e,y.u,y.v),I=li.bilinear(t,S.u,S.v),b=w&&m>0?li.airglowFactor(m):0;return[0,1,2,3].map(T=>E[T]+n*I[T]+s[T]*b)};let o=0,l=0;for(let m=5;m<90;m+=10){const g=Math.cos(m*Math.PI/180);for(let w=0;w<360;w+=30)o+=g*Math.log(Math.max(a(m,w)[1],1e-9)),l+=g}this.clearAdaptation=Math.exp(o/l),this.applyAdaptation(this.clearAdaptation*this.surroundingsFactor);const c=([m,g,w,x])=>{const y=this.exposureScale,S=this.adaptingLuminance*y;return _e.relativeOf([m*y,g*y,w*y],x*y,S,0)},h=(m,g,w=!0)=>c(a(m,g,w)),d=[0,0,0,0],u=5*Math.PI/180,f=20*Math.PI/180;for(let m=2.5;m<90;m+=5){const g=m*Math.PI/180,w=Math.sin(g)*Math.cos(g)*u*f;for(let x=10;x<360;x+=20){const y=a(m,x);for(let S=0;S<4;S++)d[S]+=y[S]*w}}const p=[0,0,0],v=[0,0,0];for(let m=0;m<360;m+=20){const g=h(2,m),w=h(2,m,!1);for(let x=0;x<3;x++)p[x]+=g[x]/18,v[x]+=w[x]/18}this.ambientColours={zenith:h(90,0),horizon:p,airlight:v,skyIrradiance:c(d),scale:this.relativeScale},this.onChange()}relativeOfLuminance(e,t){const[i,n,s]=e,a=.2126*i+.7152*n+.0722*s,o=a>0?t/a:0,l=[o*(.4124*i+.3576*n+.1805*s),t,o*(.0193*i+.1192*n+.9505*s)],c=this.exposureScale,h=this.adaptingLuminance*c;return _e.relativeOf([l[0]*c,l[1]*c,l[2]*c],l[1]*c,h,0)}get rodShare(){const e=this.adaptingLuminance*this.exposureScale;return this.seenByEye?_e.rodShare(e):0}get relativeScale(){const e=this.adaptingLuminance*this.exposureScale;return this.exposureScale/_e.semiSaturation(e)}applyAdaptation(e){this.adaptingLuminance=e;const t=e*this.exposureScale;this.material.uniforms.uRodShare.value=0,this.material.uniforms.uInverseSemiSaturation.value=1/_e.semiSaturation(t)}static predictedZenithLuminance(e){const t=n=>90-n.altitudeDeg,i=We.magPerArcsec2({altitudeDeg:e.sun.altitudeDeg,separationDeg:t(e.sun)},{phaseAngleDeg:e.moon.phaseAngleDeg,altitudeDeg:e.moon.altitudeDeg,separationDeg:t(e.moon)},90);return We.toNanolamberts(i)*li.CANDELAS_PER_NANOLAMBERT}static CANDELAS_PER_NANOLAMBERT=1e-5/Math.PI;static airglowXyzs(){const e=We.toNanolamberts(We.AIRGLOW_MAG_PER_ARCSEC2)*li.CANDELAS_PER_NANOLAMBERT;return[e*.3127/.329,e,e*(1-.3127-.329)/.329,e]}static airglowFactor(e){const t=Math.cos(e*Math.PI/180)**2,i=ke.GROUND_RADIUS_M/(ke.GROUND_RADIUS_M+li.AIRGLOW_LAYER_ALTITUDE_M),n=1/Math.sqrt(Math.max(1-i*i*t,1e-4)),s=1/Math.sqrt(Math.max(1-.96*t,.001));return n*10**(-.4*We.EXTINCTION_PER_AIR_MASS*(s-1))}static azimuthVector(e){const t=e*Math.PI/180;return new ye(Math.sin(t),-Math.cos(t))}static azimuthBetween(e,t){const i=Math.abs(e-t)%360;return(i>180?360-i:i)*Math.PI/180}static bilinear(e,t,i){const n=Ve.SKY_VIEW_WIDTH,s=Ve.SKY_VIEW_HEIGHT,a=Math.min(Math.max(t*n-.5,0),n-1),o=Math.min(Math.max(i*s-.5,0),s-1),l=Math.floor(a),c=Math.floor(o),h=Math.min(l+1,n-1),d=Math.min(c+1,s-1),u=a-l,f=o-c,p=(v,m,g)=>e[(v*n+m)*4+g];return[0,1,2,3].map(v=>(p(c,l,v)*(1-u)+p(c,h,v)*u)*(1-f)+(p(d,l,v)*(1-u)+p(d,h,v)*u)*f)}}class Rs{static DRY_OPTICAL_DEPTH=.08;static GROWTH_EXPONENT=.3;static MAX_RELATIVE_HUMIDITY=.95;static OPTICAL_DEPTH_STEP=.02;static relativeHumidity(e,t){const i=n=>Math.exp(17.625*n/(243.04+n));return Math.min(Math.max(i(t)/i(e),0),1)}static opticalDepth(e){const t=Math.min(Math.max(e,0),Rs.MAX_RELATIVE_HUMIDITY);return Rs.DRY_OPTICAL_DEPTH*(1-t)**-.3}static steppedOpticalDepth(e){const t=Rs.OPTICAL_DEPTH_STEP;return Math.round(Rs.opticalDepth(e)/t)*t}}class Mn{static CHANNEL_WAVELENGTHS_NM=[680,550,440];static KOSCHMIEDER=-Math.log(.02);static PRECIPITATION_EXTINCTION_PER_KM={rain:{a:.365,b:.63},snow:{a:1.023,b:.72},hail:{a:.365,b:.63}};static FULL_INTENSITY_MM_PER_H=8;rayleigh;aerosol;constructor(e=ke.DEFAULT_AEROSOL_OPTICAL_DEPTH,t=0){const[i,n,s]=Mn.CHANNEL_WAVELENGTHS_NM;this.rayleigh=[ke.rayleighAt(i),ke.rayleighAt(n),ke.rayleighAt(s)];const a=o=>e/ke.AEROSOL_SCALE_HEIGHT_M*(o/550)**-1.3;this.aerosol=[a(i)+t,a(n)+t,a(s)+t],this.precipitationPerM=t}precipitationPerM;static of(e){const t=e.relativeHumidity===void 0?void 0:Rs.opticalDepth(e.relativeHumidity);return new Mn(t,Mn.precipitationExtinction(e))}static precipitationExtinction(e){if(e.precipitationType==="none"||!(e.precipitationIntensity>0))return 0;const t=Mn.PRECIPITATION_EXTINCTION_PER_KM[e.precipitationType],i=e.precipitationIntensity*Mn.FULL_INTENSITY_MM_PER_H;return t.a*i**t.b/1e3}transmittance(e,t,i){const n=Mn.meanDensity(e,t,ke.RAYLEIGH_SCALE_HEIGHT_M),s=Mn.meanDensity(e,t,ke.AEROSOL_SCALE_HEIGHT_M);return[0,1,2].map(a=>Math.exp(-(this.rayleigh[a]*n+this.aerosol[a]*s)*i))}transmittanceFromSpace(e,t){const i=Math.max(t,0),n=1/(Math.sin(i*Math.PI/180)+.50572*(i+6.07995)**-1.6364),s=ke.RAYLEIGH_SCALE_HEIGHT_M*ke.rayleighDensity(e),a=ke.AEROSOL_SCALE_HEIGHT_M*ke.aerosolDensity(e);return[0,1,2].map(o=>Math.exp(-(this.rayleigh[o]*s+(this.aerosol[o]-this.precipitationPerM)*a)*n))}extinctionAt(e){return{rayleigh:this.rayleigh[1]*ke.rayleighDensity(e),aerosol:this.aerosol[1]*ke.aerosolDensity(e)}}visibilityM(e){const{rayleigh:t,aerosol:i}=this.extinctionAt(e);return Mn.KOSCHMIEDER/(t+i)}get rayleighRatios(){return[this.rayleigh[0]/this.rayleigh[1],1,this.rayleigh[2]/this.rayleigh[1]]}static meanDensity(e,t,i){const n=Math.max(e,0),s=Math.max(t,0),a=s-n;return Math.abs(a)<.01?Math.exp(-n/i):i*(Math.exp(-n/i)-Math.exp(-s/i))/a}}class Ga extends Qf{constructor(){super(new se(0,0,0),0,0),Ga.install()}setAir(e,t,i){const{rayleigh:n,aerosol:s}=e.extinctionAt(t);this.near=n,this.far=s,this.color.setRGB(i[0],i[1],i[2])}static fadeRim(e,t,i){e.transparent=!0,e.onBeforeCompile=n=>{n.fragmentShader=n.fragmentShader.replace("#include <fog_fragment>",`#include <fog_fragment>
|
|
8459
8459
|
#ifdef USE_FOG
|
|
8460
|
-
|
|
8461
|
-
gl_FragColor.rgb = mix(gl_FragColor.rgb, aerialHorizon, aerialRim);
|
|
8460
|
+
gl_FragColor.a *= 1.0 - smoothstep(${t.toFixed(1)}, ${i.toFixed(1)}, length(vAerialWorld.xz - cameraPosition.xz));
|
|
8462
8461
|
#endif`)},e.customProgramCacheKey=()=>`aerial-rim-${t.toFixed(1)}-${i.toFixed(1)}`,e.needsUpdate=!0}static installed=!1;static install(){if(Ga.installed)return;Ga.installed=!0;const e=new Mn(1),t=e.rayleighRatios,i=[e.aerosol[0]/e.aerosol[1],1,e.aerosol[2]/e.aerosol[1]],n=o=>`vec3(${o.map(l=>l.toFixed(4)).join(", ")})`,s=ke.RAYLEIGH_SCALE_HEIGHT_M.toFixed(1),a=ke.AEROSOL_SCALE_HEIGHT_M.toFixed(1);qe.fog_pars_vertex=`
|
|
8463
8462
|
#ifdef USE_FOG
|
|
8464
8463
|
varying vec3 vAerialWorld;
|
|
@@ -8515,7 +8514,7 @@ cloudPoint = (modelMatrix * vec4(transformed, 1.0)).xyz - cameraPosition;`);let
|
|
|
8515
8514
|
vec3 light = texture2D(uMap, vec2((along * (uSteps - 1.0) + 0.5) / uSteps, 0.5)).rgb;
|
|
8516
8515
|
gl_FragColor = vec4(light, 1.0);
|
|
8517
8516
|
}
|
|
8518
|
-
`,transparent:!0,blending:Fi,depthWrite:!1,side:Wt,fog:!1});this.object=new De(new ai(Ep.RADIUS,128,32,0,Math.PI*2,0,ut.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<ut.STEPS;i++){for(let n=0;n<3;n++)this.texels[i*4+n]=Bn.toHalfFloat(Math.min(t[i*3+n],6e4));this.texels[i*4+3]=Bn.toHalfFloat(1)}this.texture.needsUpdate=!0,this.object.visible=!0}dispose(){this.object.geometry.dispose(),this.object.material.dispose(),this.texture.dispose()}}class ah{constructor(e){this.generator=e}static SIZE=128;target=new cl(ah.SIZE,{type:qt,generateMipmaps:!1,minFilter:ot,magFilter:ot});camera=new op(.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 a=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=a[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 rn{static REFRESH_MS=250;generator;eye;probes=new Map;constructor(e){rn.install(),this.generator=new ju(e),this.eye=new ah(this.generator)}static decor;refresh(e,t,i,n,s,a,o,l=[],c){for(const v of[...this.probes.keys()])n.some(m=>m.id===v&&m.shiny)||(this.probes.get(v).dispose(),this.probes.delete(v));const h=[{probe:this.eye}];for(const v of n){if(!v.shiny)continue;let m=this.probes.get(v.id);m||(m=new ah(this.generator),this.probes.set(v.id,m)),h.push({probe:m,reflector:v})}const d=performance.now(),u=()=>h.filter(({probe:v})=>v.capturedVersion!==o),f=u().filter(({probe:v})=>d-v.capturedAtMs>=rn.REFRESH_MS);if(f.length>0){const v=f.reduce((m,g)=>g.probe.capturedAtMs<m.probe.capturedAtMs?g:m);if(v.reflector){const m=v.reflector.holder.getWorldPosition(this.scratch);v.probe.capture(e,t,m,[v.reflector.holder,...s]),rn.reflectOn(v.reflector.holder,v.probe)}else{v.probe.capture(e,t,i,[...s,...l]),c?.(v.probe.sharp),rn.decor=v.probe.filtered;for(const m of a)rn.reflectOn(m,v.probe);for(const m of n)m.shiny||rn.reflectOn(m.holder,v.probe)}v.probe.capturedVersion=o}const p=u();if(p.length!==0)return Math.max(16,Math.min(...p.map(({probe:v})=>rn.REFRESH_MS-(d-v.capturedAtMs))))}scratch=new R;static reflectOn(e,t={filtered:rn.decor}){e.traverse(i=>{const n=i.material;for(const s of Array.isArray(n)?n:n?[n]:[])s instanceof dl?t.sharp&&s.setSurroundings(t.sharp):s instanceof Ca&&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(rn.installed)return;rn.installed=!0;const e="iblIrradiance += getIBLIrradiance( geometryNormal );";if(!qe.lights_fragment_maps.includes(e)){console.warn("Reflections: three's lights_fragment_maps changed; the environment will light diffusely too");return}qe.lights_fragment_maps=qe.lights_fragment_maps.replace(e,"// The sky's diffuse light is the hemisphere light's (see Reflections).")}}function j5(r,e){return JSON.stringify(r.cloudLayers)===JSON.stringify(e.cloudLayers)&&r.cloudCover===e.cloudCover&&r.cloudDarkness===e.cloudDarkness&&r.cloudBaseM===e.cloudBaseM&&r.highCloudCover===e.highCloudCover&&r.iceCrystalAlignment===e.iceCrystalAlignment&&r.lowerCloudCover===e.lowerCloudCover&&r.relativeHumidity===e.relativeHumidity&&r.precipitationType===e.precipitationType&&r.precipitationIntensity===e.precipitationIntensity&&r.windDirectionDeg===e.windDirectionDeg&&r.windSpeed===e.windSpeed&&r.storm===e.storm}const hc=900,Dd=1,rg=4,og=5,lg=.5,Ho=900,J5=6371e3,Q5=3e4;function cg(r){const e=Math.sqrt(2*J5*Math.max(r,0));return Math.max(Ho,Math.min(e,Q5))}const Cd=.5,Rd=850,hg=.03,vn=850,Pd=300,eA=.04,Co=4,Ld=1.5,dg=-1,tA=6,Cf=-4,iA=2,nA=20,dc=2600,sA=.97,aA=.38,uc=Cf,ug=1500,rA=400,oA=[0,45,90,135,180,225,270,315],lA={en:["N","NE","E","SE","S","SW","W","NW"],fr:["N","NE","E","SE","S","SO","O","NO"]},cA=["en","fr"],hA=2,dA=16,fg=2500,pg=880,fc=40,mg=60,uA=10,fA=1,pA=150,mA=55,gA=.5,vA=17,gg=.265,vg=[[0,0],...Array.from({length:8},(r,e)=>{const t=e*Math.PI/4;return[Math.cos(t)*gg,Math.sin(t)*gg]})],Od=700,kd=250/1e3,yA=12,yg=5,Nd=6,Sa=[.92,.92,.95],Fd=[.15,.15,.19],bg={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}},Tr=0,Ud=0,bA=150,Mg=3e3,Ro=20,MA=15,wg=Tr,wA=40,xg=1,xA=9;function Sg(r){return xg+(xA-xg)*r}const Eg=new se(.75,.82,.92),SA=10,EA=.5;function _A(r){return kr(r/SA,EA,1)}const Mr=200,_g=.18,Ag=.32,AA=.35,Tg=new se(.85,.9,.98),Ig=5,Dg=.85,Cg=.01;function Bd(r,e){return Math.round(r*(Cg+(1-Cg)*e))}const Rg=230,Pg=.7,Lg=.05,TA=.6,IA=.7,DA=.15,Og=.35,CA=3,RA=1/12,PA=.5,$n=new se(.82,.86,1),LA=2500,OA=1.6,zd=15,kA={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 Xt{renderer;scene=new Os;phenomena=new Rn(this.scene);references=new Oa(this.scene,()=>this.render());camera;celestialGroup=new Ot;skyMesh;skyGradientMaterial;scatteredSky;lastAstronomy;skyColoursStale=!1;scatteredSkyFrameId=null;skyHoldUntilMs=void 0;onFirstFrameHold;firstFrameReady(){return(!this.scatteredSky||this.scatteredSky.ambient!==void 0)&&!this.iceHalos?.awaitingFirstDisplay}groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;roadSystem=new yn;roadAttribution;roadWays;statedRoads=[];decorModelProvider=Cc[0].create();decorModelToken=0;decorModelCredits=new Map;bodySystem=new ht(e=>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 Q0(16777215,0);celestialLightTarget=new Nt;skyLight=new SM(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 cd;weather=n1;observerElevationM=0;siteElevationM=0;air=new Mn;groundRadius=Ho;terrainRadius=Ho;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 Q0(16777215,0);baseFogColor=[0,0,0];lastSunPosition;lensFlare;sunUnhiddenFraction=1;lensFlareArtifactIntensity=0;sunWorldPosition=new R;sunVisible=!1;lensFlareScratch=new R;sunOcclusionRaycaster=new cd;sunOcclusionDirectionScratch=new R;sunOcclusionRightScratch=new R;sunOcclusionUpScratch=new R;ufoOcclusionRaycaster=new cd;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;meteorSystem;cometTail;satelliteField;iceHalos;lunarDiffraction;lunarDiffractionKey="";rainbow;skyGlow;cometKey;novaKeys=new Set;constructor(e,t=x2()){this.renderer=new NE({canvas:e,antialias:!0}),this.resolution=new mt(Math.min(window.devicePixelRatio||1,2),this.renderer.getContext()),this.renderer.setPixelRatio(this.resolution.pixelRatio),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=Go,this.camera=new Ji(60,e.width/Math.max(e.height,1),.1,hc*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=-120,this.celestialLight.shadow.camera.right=120,this.celestialLight.shadow.camera.top=120,this.celestialLight.shadow.camera.bottom=-120,this.celestialLight.shadow.camera.near=vn-Pd,this.celestialLight.shadow.camera.far=vn+Pd,this.celestialLight.shadow.bias=-.03/(2*Pd),this.celestialLight.shadow.normalBias=eA,this.celestialLight.target=this.celestialLightTarget,this.lightningLight.color.copy($n),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 li(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*kd,0,e.z*kd);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)*Yi,i=(e.pitchDeg+this.lookPitchDeg+this.gaitOffset.pitchDeg)*Yi;if(e.headingDeg!==void 0?this.camera.rotation.set(i,-(e.headingDeg+this.lookYawDeg+this.gaitOffset.yawDeg)*Yi,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(cg(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/Ho),this.camera.far=Math.max(hc*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.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)<pA*(n/Ho))return}const s=this.terrainOrigin,a=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;h5(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)<=a&&(l.position.y=d.position.y+this.groundYOfPatch(d,u.x,u.z)),this.disposeMesh(this.terrainMesh),l.renderOrder=fA,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 ROADS_MAX_RADIUS_M=5e3;buildRoads(e,t,i){const n=this.terrainProviders.roads;if(!n||i>Xt.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}n.getRoads(Nv(e,t,i)).then(a=>{this.terrainOrigin?.lat!==e||this.terrainOrigin?.lng!==t||(this.roadWays={lat:e,lng:t,radiusM:i,ways:a},this.drapeRoads(a,e,t,n.contemporary))}).catch(a=>{console.warn("Roads unavailable, drawing none:",a)})}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,(a,o)=>this.groundYOfPatch(s,a,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())}applyLensFlareTint(e){if(!this.lensFlare)return;const t=mA*(this.sunArriving[1]/vA);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){this.cloudTransmissionMemo.clear(),!j5(this.weather,e)&&(this.weather=e,this.air=Mn.of(e),this.applyAir(),this.scatteredSky?.setConditions({aerosolOpticalDepth:e.relativeHumidity===void 0?void 0:Rs.steppedOpticalDepth(e.relativeHumidity)}),this.pumpScatteredSky(),this.buildClouds(),this.buildCirrus(),this.buildPrecipitation(),this.lastAstronomy&&(this.buildIceHalos(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildRainbow(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildLunarDiffraction(this.lastAstronomy.moon)),this.lightningArmed=e.storm&&e.cloudDarkness>=PA,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.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(a=>a.id),n=[],s){const{x:a,z:o}=this.bodyOrigin,l=this.scatteredSky;this.bodySystem.set(e,{originX:a,originZ:o,originGroundY:this.groundYUnder(a,o),eye:this.camera.position,groundYAt:(h,d)=>this.groundYUnder(h,d),wind:s,light:this.plumeLight(),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);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,a=(o,l,c)=>(o*t+(l+c)/2*s)/Math.PI;return[a(e.r,i.r,n.r),a(e.g,i.g,n.g),a(e.b,i.b,n.b)]}bodyOutline(e,t){return this.bodySystem.outlineOf(e,t)}async loadBodyModel(e){const t=e.url?void 0:e.id?await this.decorModelProvider.entry(e.id):void 0,i=e.url??t?.url,n=e.credit??t?.credit;if(!(!i||!n))return{scene:await ag(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=i.url??n?.url,a=i.credit??n?.credit;if(!s||!a)return;const o=await ag(s);if(t!==this.decorModelToken)return;const l=this.decorGroups.get(e.id);if(!l)return;rn.reflectOn(o),ui.applyModel(l,e,o,{headingOffsetDeg:i.headingOffsetDeg??n?.headingOffsetDeg,depictedSizeM:n?.sizeM}),this.decorModelCredits.set(e.id,a),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.witnessSide!==void 0&&Dr(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 a={x:0,z:0};let o=0;if(s){const c=ui.occupantView(s),h=-(s.headingDeg??0)*Yi,d=c.x*Math.cos(h)+c.z*Math.sin(h),u=-c.x*Math.sin(h)+c.z*Math.cos(h),f=s.eastM+n.x,p=-s.northM+n.z;a.x=-(f+d),a.z=-(p+u),this.camera.position.y=this.groundYUnder(f,p)+c.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*Yi,-(c.headingDeg+this.indoorLookYawDeg)*Yi,0,"YXZ")}this.bodyOrigin={x:n.x+a.x,z:n.z+a.z},this.placeShadowFrustum();for(const c of this.decorObjects){const h=this.decorGroups.get(c.id);if(!h)continue;const d=Zr(c,i),u=d.eastM+n.x+a.x,f=-d.northM+n.z+a.z,p=this.terrainMesh,v=`${p?.geometry.uuid}:${(u-(p?.position.x??0)).toFixed(3)}:${(f-(p?.position.z??0)).toFixed(3)}:${(p?.position.y??0).toFixed(3)}:${d.headingDeg}:${d.altitudeM}`;if(h.userData.footprintKey!==v&&(h.userData.footprintGroundY=this.groundUnderFootprint(c,u,f,d.headingDeg,d.altitudeM),h.userData.footprintKey=v),h.position.set(u,h.userData.footprintGroundY+d.altitudeM,f),c.kind==="crop"){const m=Math.hypot(u,f);h.visible=m<=bA}if(d.headingDeg!==void 0&&(h.rotation.y=-d.headingDeg*Yi),c.kind==="crop"&&h.visible){const m=this.terrainMesh,g=[m?.geometry.uuid,u-(m?.position.x??0),f-(m?.position.z??0),h.position.y-(m?.position.y??0),d.headingDeg,d.altitudeM].map(w=>typeof w=="number"?w.toFixed(4):w).join(":");h.userData.cropGroundKey!==g&&(ui.fitCropToGround(h,(w,x)=>this.groundYUnder(w,x)+d.altitudeM),h.userData.cropGroundKey=g)}o=Math.max(o,h.position.distanceTo(this.camera.position))}const l=Math.max(hc*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):Ud}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 Ud;const a=n.getX(0),o=n.getX(s-1),l=n.getZ(0),c=n.getZ((s-1)*s),h=kr((t-a)/(o-a)*(s-1),0,s-1),d=kr((i-l)/(c-l)*(s-1),0,s-1);if(!Number.isFinite(h)||!Number.isFinite(d))return Ud;const u=Math.min(Math.floor(h),s-2),f=Math.min(Math.floor(d),s-2),p=h-u,v=d-f,m=(S,E)=>n.getY(S*s+E),g=m(f,u),w=m(f,u+1),x=m(f+1,u),y=m(f+1,u+1);return p+v<=1?g+(w-g)*p+(x-g)*v:y+(x-y)*(1-p)+(w-y)*(1-v)}groundUnderFootprint(e,t,i,n,s=0){return ui.groundUnderFootprint(e,t,i,n,(a,o)=>this.groundYUnder(a,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}updateDecorLitState(e,t=0){for(const i of this.decorObjects){const n=this.decorGroups.get(i.id);if(n&&i.lights?.length&&ui.setLights(n,i.lights,e,n.position.distanceTo(this.camera.position),t,this.relativeScale),i.kind!=="streetlight"&&i.kind!=="vehicle")continue;const s=this.decorGroups.get(i.id);if(!s)continue;const a=Nc(i,e);ui.setLit(s,i.kind,a,this.relativeScale);const o=this.streetlightLights.get(i.id);o&&(o.visible=a)}}addStreetlightLight(e,t){const i=ui.bodyOf(t),n=i.children.find(a=>a.userData.emissive);if(!n)return;const s=new iv(16764006,ug*this.relativeScale,rA);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.skyHoldUntilMs=performance.now()+fg,this.onFirstFrameHold?.(!0),setTimeout(()=>this.render(),fg+20)),this.pumpScatteredSky(),this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround();const t=aa(e.sun.altitudeDeg,this.instrumentMagnitudeGain);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=a=>{const o=Nn.luminanceOf(a);if(!(o>0))return[0,0,0];const l=_e.relativeOfResponse(o)/o;return[a[0]*l,a[1]*l,a[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,Co,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,aa(e.sun.altitudeDeg,this.instrumentMagnitudeGain)),this.applyDazzleStrength(),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon));const t=this.scatteredSky?.ambient??Xt.relativeSkyOf(of(e.sun.altitudeDeg),this.relativeScale),i=t,n=this.lightUnderClouds(e),s=a=>n?[0,1,2].map(o=>a[o]*n.through+n.radiance[o]*(1-n.through)):[a[0],a[1],a[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),Vt.setEye(this.scatteredSky?.rodShare??0,this.relativeScale),this.updateTwinkle(this.twinkleSeconds);for(const a of this.streetlightLights.values())a.intensity=ug*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?ke.DEFAULT_AEROSOL_OPTICAL_DEPTH:Rs.opticalDepth(i))*ke.aerosolDensity(this.siteElevationM+this.observerElevationM),s=this.cloudTransmission(e),a=-Math.log(Math.max(this.cloudTransmission(e,!0),1e-4)),o=ut.aureoleScale(n,ut.airMass(e.altitudeDeg))*s,l=ut.coronaScale(a),c=ut.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 Ep,this.celestialGroup.add(this.lunarDiffraction.object));const d=ut.profile(ut.HAZE_COARSE_MODE),u=ut.profile(ut.CLOUD_DROPLETS),f=Dm(e.altitudeDeg),p=new Float32Array(ut.STEPS*3);let v=0;for(let x=0;x<ut.STEPS;x++){const y=x*3,S=[0,1,2].map(I=>c*f[I]*(o*d[y+I]+l*u[y+I])),E=Nn.shown(S,Nn.luminanceOf(S),(I,b)=>t.relativeOfLuminance(I,b));p.set(E,y),v=Math.max(v,Nn.response(E))}const{x:m,y:g,z:w}=ci(e.altitudeDeg,e.azimuthDeg,1);this.lunarDiffraction.show(v>1/200?{x:m,y:g,z:w}:void 0,p)}applyAir(){this.scene.fog instanceof Ga||(this.scene.fog=new Ga),this.scene.fog.setAir(this.air,this.siteElevationM,this.airlight),this.horizonColour.setRGB(this.baseFogColor[0],this.baseFogColor[1],this.baseFogColor[2])}airlight=[0,0,0];horizonColour=new se(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,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:We.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?dA:hA),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 _5(Rd),this.celestialGroup.add(this.satelliteField.object));const t=aa(this.lastSunPosition?.altitudeDeg??-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(hg*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>=uc,s=!n&&e.moon.altitudeDeg>=uc,a=n?e.sun:e.moon,o=[0,0,0],l=Math.max(0,Math.sin(Math.max(a.altitudeDeg,0)*Yi)),c=n||s?this.cloudTransmission(a):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 w=ut.illuminanceOf(a.magnitude)*i,x=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,a.altitudeDeg);for(let I=0;I<3;I++)o[I]=w*x[I]*c,u[I]=w*x[I]*d*l,this.cloudBeam[I]=w*x[I];const{x:y,y:S,z:E}=ci(a.altitudeDeg,a.azimuthDeg,1);this.cloudBeamDirection.set(y,S,E)}else this.cloudBeam.fill(0);const f=this.surroundings;if(f)this.skyLight.color.setRGB(f.up[0]*i,f.up[1]*i,f.up[2]*i),this.skyLight.groundColor.setRGB(f.down[0]*i,f.down[1]*i,f.down[2]*i);else{const w=this.skyCloudTransmission(),x=t.skyIrradiance,y=[0,1,2].map(E=>x[E]*w+u[E]);this.skyLight.color.setRGB(y[0],y[1],y[2]);const S=Ve.GROUND_ALBEDO;this.skyLight.groundColor.setRGB(S*(y[0]+o[0]*l),S*(y[1]+o[1]*l),S*(y[2]+o[2]*l))}if(this.skyLight.intensity=1,!n&&!s){this.celestialLight.intensity=0;return}const{x:p,y:v,z:m}=ci(a.altitudeDeg,a.azimuthDeg,1);this.lightDirection.set(p,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(),a=this.shadowWorld.set(this.bodyOrigin.x,0,this.bodyOrigin.z),o=c=>{const h=a.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,vn)}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*Yi,s=Math.sin(n)*Math.cos(n)*2;for(let a=0;a<360;a+=60)e+=s*this.cloudTransmission({altitudeDeg:i,azimuthDeg:a}),t+=s}return e/t}cloudBeam=[0,0,0];cloudBeamDirection=new R(0,1,0);get relativeScale(){return this.scatteredSky?.relativeScale??1/_e.semiSaturation(_e.DAYLIGHT_ANCHOR_CD_M2)}setMeteorShower(e,t,i){this.meteorSystem||(this.meteorSystem=new Mi,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;const t=vt.starPointsOf(e),i=vt.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())}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.skyHoldUntilMs){if(!this.firstFrameReady()&&performance.now()<this.skyHoldUntilMs)return;this.skyHoldUntilMs=null,this.iceHalos?.setUrgent(!1),this.onFirstFrameHold?.(!1)}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){this.frameDirty=!1,this.refreshReflections(),this.resolution.beginDrawing();try{this.renderOnce()}finally{this.resolution.endDrawing()}this.developingExposure&&this.startExposure()}}}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()+Xt.COMPILE_WAIT_MS;return new Promise(n=>{const s=()=>{if(this.contextReleased)return n();for(const a of e){const o=t.get(a)?.currentProgram;(!o||o.isReady())&&e.delete(a)}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(){this.reflections??=new rn(this.renderer);const e=[...this.compassSprites,...this.lensFlare?[this.lensFlare.mesh]:[],...[...this.glareSprites.values()].map(s=>s.mesh)],t=[...this.starTiers.map(s=>s.points),...[...this.bodyMeshes.values()].filter(s=>s instanceof Ts),...[this.rainSystem?.points,this.precipitationPoints,this.rainSplashSystem?.points].filter(s=>s!==void 0)],i=this.relativeScale,n=this.reflections.refresh(this.renderer,this.scene,this.camera.position,this.bodySystem.reflectors,e,[...this.decorGroups.values()],this.sceneVersion,t,s=>this.measureSurroundings(s,i));n===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()}))},n))}measureSurroundings(e,t){const i=this.lastAstronomy;if(!i)return;this.probeIrradiance??=new Yo;const n=[i.sun,i.moon].filter(s=>s.altitudeDeg>-2).map(s=>ci(s.altitudeDeg,s.azimuthDeg,1));this.probeIrradiance.measure(this.renderer,e,n,s=>{const a=u=>u.map(f=>f/t),o=this.surroundings,l={up:a(s.up),down:a(s.down)},c=(u,f)=>u.some((p,v)=>Math.abs(p-f[v])>.03*Math.max(Math.abs(p),Math.abs(f[v]),1e-12)),h=!o||c(o.up,l.up)||c(o.down,l.down);h&&(this.surroundings=l);const d=this.scatteredSky?.adaptToSurroundings(Math.exp(s.logAverage)/t)??!1;!h&&!d||(this.skyColoursStale=!0,this.render())})}lightUnderClouds(e){const t=this.surroundings;if(!t)return;const i=e.sun.altitudeDeg>=uc?e.sun:e.moon.altitudeDeg>=uc?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,RA);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 a=this.mapSubjectBounds();e?this.exposureSubjectBounds.push(...a):this.onMapSubjectBounds(a)}const t=this.usableEquidistantPass();this.phenomena.setCloudLayers(this.layeredClouds?.volumes??[]),this.phenomena.place(this.camera,(a,o,l)=>this.directionAtScreenPoint(a,o,l),a=>this.screenPointOf(a)),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()),wn.uRodShare.value=this.scatteredSky?.rodShare??0,wn.uRelativeScale.value=this.relativeScale;const n=this.references.any||this.phenomena.any||this.compassSprites.some(a=>a.visible);wn.uHasOverlay.value=n?1:0;const s=n?a=>this.renderOverlayPasses(a):void 0;if(this.skyGlow?.setDestinationEncoded(!1),!t){this.updateLensFlarePosition();const a=this.usableDepthOfFieldPass();a?a.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,a=>{this.lensFlare&&(this.lensFlare.mesh.visible=a?!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(og),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(Yv),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}setReferences(e){this.references.set(e)}referenceAspect(e){return this.references.aspectOf(e)}directionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}referenceFailedToLoad(e){return this.references.failedToLoad(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&&Xt.markDecorDepth(s,n.kind!=="crop")}e.layers.set(Dd),this.scene.overrideMaterial=this.decorDepthMaterial,this.renderer.render(this.scene,e),this.scene.overrideMaterial=null,e.layers.set(qv),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}decorDepthMaterial=new En({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!==Fi?i.layers.enable(Dd):i.layers.disable(Dd)})}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 WE(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()+Xt.EXPOSURE_BUDGET_MS,s=Xt.exposureInstantsPerFrame(t.instantTarget.scene.width*t.instantTarget.scene.height);let a=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++,a++}while(this.exposureInstantsDone<this.exposureInstants&&performance.now()<n&&a<s);this.restateAt(e,0),this.presentExposure(),this.resolution.endDrawing(),this.exposureInstantsDone<this.exposureInstants&&(this.exposureFrameId=requestAnimationFrame(()=>this.developExposure()))}presentExposure(){if(wn.uRodShare.value=this.scatteredSky?.rodShare??0,wn.uHasOverlay.value=1,wn.uRelativeScale.value=this.relativeScale,this.exposureAccumulation?.develop(this.renderer,this.exposureInstants/this.exposureInstantsDone),this.onMapSubjectBounds?.(this.exposureSubjectBounds),!this.compassSprites.some(s=>s.visible))return;const e=new Os,t=new zn(-1,1,1,-1,0,2),i=fc/(pg*Math.tan(mg*Yi/2));for(const s of this.compassSprites){if(!s.visible)continue;const a=s.getWorldPosition(new R).sub(this.camera.position).normalize(),o=this.screenPointOf(a);if(!o)continue;const l=new yo(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 An,i=new R,n=this.decorObjects.filter(a=>a.track?.length||a.kind==="aircraft"||a.kind==="vehicle").map(a=>this.decorGroups.get(a.id)),s=this.bodySystem.reflectors.map(a=>a.holder);for(const a of[...n,...s]){if(!a?.visible||(t.setFromObject(a),t.isEmpty())||t.distanceToPoint(this.camera.position)<Xt.MAP_SUBJECT_NEAREST_M)continue;let o=1/0,l=-1/0,c=1/0,h=-1/0;for(const d of[t.min.x,t.max.x])for(const u of[t.min.y,t.max.y])for(const f of[t.min.z,t.max.z]){i.set(d,u,f).sub(this.camera.position).normalize();const p=this.screenPointOf(i);if(!p)continue;const v=(p.ndcX+1)/2,m=(1-p.ndcY)/2;o=Math.min(o,v),l=Math.max(l,v),c=Math.min(c,m),h=Math.max(h,m)}Number.isFinite(o)&&e.push({x:o,y:c,width:l-o,height:h-c})}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 $o(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 Sp(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 wi(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 wi.ndcFor(t,this.camera.fov,n);if(t.z>=0)return;const s=Math.tan(this.camera.fov/2*Yi);return{ndcX:t.x/-t.z/(n*s),ndcY:t.y/-t.z/s}}screenPointScratch=new R;screenPointQuaternion=new Wi;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(rg),this.camera.layers.set(rg),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 vg)this.isSunOccluded(t[0],t[1])||e++;return e/vg.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 a=this.sunOcclusionRaycaster.intersectObjects(i,!0)[0];return a!==void 0&&a.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,w=e.z+t.z*m-s.position.z;if(Math.hypot(g,w)<=this.groundRadius)return!0}}const a=this.terrainMesh;if(!a)return!1;const o=a.geometry.getAttribute("position"),l=Math.round(Math.sqrt(o.count));if(l<2||l*l!==o.count)return!1;const c=[a.position.x+o.getX(0),a.position.x+o.getX(l-1)],h=[a.position.z+o.getZ(0),a.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,f=i;for(const[m,g,w]of[[e.x,t.x,c],[e.z,t.z,h]]){const x=Math.min(w[0],w[1]),y=Math.max(w[0],w[1]);if(Math.abs(g)<1e-12){if(m<x||m>y)return!1;continue}const S=(x-m)/g,E=(y-m)/g;u=Math.max(u,Math.min(S,E)),f=Math.min(f,Math.max(S,E))}if(!(f>u))return!1;const p=Math.hypot(t.x,t.z),v=Math.min(4096,Math.max(8,Math.ceil((f-u)*p/(d/2))));for(let m=0;m<=v;m++){const g=u+(f-u)*m/v,w=e.x+t.x*g,x=e.y+t.y*g,y=e.z+t.z*g;if(x<=this.groundYUnder(w,y))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=lg;let n,s;for(const a of this.decorObjects){if(a.kind==="crop")continue;const o=this.decorGroups.get(a.id);if(!o)continue;const l=this.ufoOcclusionRaycaster.intersectObject(o,!0)[0];l&&(a.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(hg*this.camera.fov*Math.PI/180);let s,a=n;for(const o of this.namedStars){const l=o.direction.dot(i);l>a&&(a=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,lg)}pickDecorAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.decorGroups.entries()],n=this.raycaster.intersectObjects(i.map(([,a])=>a),!0)[0];if(!n)return;let s=n.object;for(;s&&!i.some(([,a])=>a===s);)s=s.parent;return i.find(([,a])=>a===s)?.[0]}dispose(){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()),!e&&this.renderer.getPixelRatio()!==this.resolution.maximum&&this.applyPixelRatio(this.resolution.maximum),this.syncAnimationLoop(),(!e||this.frameDirty)&&this.render()}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 ai(hc,32,16),i=t.attributes.position,n=t.getAttribute("color")??new At(new Float32Array(i.count*3),3);for(let a=0;a<i.count;a++){const{altitudeDeg:o,azimuthDeg:l}=YE(i.getX(a),i.getY(a),i.getZ(a)),c=t_(o,l,e.azimuthDeg,e.altitudeDeg);n.setXYZ(a,c[0],c[1],c[2])}if(n.needsUpdate=!0,this.skyMesh)return;t.setAttribute("color",n);const s=new En({vertexColors:!0,side:Wt,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=cg(this.observerElevationM);if(this.groundMesh&&e===this.groundRadius)return;this.disposeMesh(this.groundMesh),this.groundRadius=e;const t=new ip(this.groundRadius,48),i=new ro({color:new se(Cd,Cd,Cd),fog:!0});Ga.fadeRim(i,this.groundRadius*.75,this.groundRadius,this.horizonColour),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(([a,o])=>`${a}=${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=ci(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-ft.alphaAt(i,Math.abs(this.cloudLayerOffset()),s,this.cloudFieldOffset)*sA);const a=t?0:this.weather.highCloudCover??0;return a>0&&(n*=1-ft.iceAlphaAt(i,dc,a,this.cloudFieldOffset)*aA),Math.max(0,Math.min(1,n))}setBodyMesh(e,t,i,n,s){if(t.altitudeDeg<Cf){this.disposeMesh(this.bodyMeshes.get(e)),this.bodyMeshes.delete(e),this.disposeHitArea(e),this.disposeGlare(e),e==="sun"&&(this.sunVisible=!1);return}const{x:a,y:o,z:l}=ci(t.altitudeDeg,t.azimuthDeg,vn),c=Dm(t.altitudeDeg),h=this.cloudTransmission(t),d=this.arrivingIlluminance(t,s,h),u=Xt.unitLuminance(n);let f=this.bodyMeshes.get(e);if(e==="sun"){const p=Math.PI*(i/vn)**2,v=[0,1,2].map(m=>Math.min(Xt.BRIGHTEST_RELATIVE,u[m]*d[m]/p));f instanceof De||(this.disposeMesh(f),f=new De(new ai(1,16,16),new En({fog:!1})),this.celestialGroup.add(f),this.bodyMeshes.set(e,f)),f.material.color.setRGB(v[0],v[1],v[2]),f.scale.setScalar(i)}else{if(!(f instanceof Ts)){this.disposeMesh(f);const m=new pt;m.setAttribute("position",new it(new Float32Array(3),3)),m.setAttribute("color",new it(new Float32Array(3),3)),f=new Ts(m,Vt.material(qo[qo.length-1].size)),Vt.track(f),this.celestialGroup.add(f),this.bodyMeshes.set(e,f)}const p=f.geometry.getAttribute("color"),v=e.startsWith("comet:")?this.pointToPixel():1;p.setXYZ(0,u[0]*d[0]*v,u[1]*d[1]*v,u[2]*d[2]*v),p.needsUpdate=!0}if(f.position.set(a,o,l),this.setHitArea(e,a,o,l,i),e==="sun"&&(this.sunArriving=d),e.startsWith("comet:")?this.disposeGlare(e):e!=="sun"&&this.setGlare(e,a,o,l,d,i/vn*(180/Math.PI),Xt.POINT_VEIL_HOLD_DEG),e==="sun"){this.sunVisible=!0;const p=this.celestialGroup.scale.x;this.sunWorldPosition.set(a*p,o*p+this.celestialGroup.position.y,l*p),this.lensFlare||(this.lensFlare=E5(),this.lensFlare.uniforms.uOpacity.value=gA,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<Cf){this.disposeMesh(this.bodyMeshes.get(i)),this.bodyMeshes.delete(i),this.disposeHitArea(i),this.disposeGlare(i);return}const{x:n,y:s,z:a}=ci(e.altitudeDeg,e.azimuthDeg,vn),o=this.cloudTransmission(e),l=this.arrivingIlluminance(e,e.magnitude,o),c=Math.max(kr(e.phase.illuminatedFraction,0,1),.01)*Math.PI*(Co/vn)**2,h=new se(...l.map(f=>Math.min(Xt.BRIGHTEST_RELATIVE,f/c))),d=`${e.phase.phaseFraction<.5}:${Math.round(kr(e.phase.illuminatedFraction,0,1)*500)}`;let u=this.bodyMeshes.get(i);if(u instanceof yo)u.userData.phaseKey!==d&&(u.material.map?.dispose(),u.material.map=Ng(e.phase),u.userData.phaseKey=d),u.material.color.copy(h);else{this.disposeMesh(u),u=new yo(new Tc({map:Ng(e.phase),color:h,fog:!1})),u.userData.phaseKey=d;const f=Co*2;u.scale.set(f,f,1),this.celestialGroup.add(u),this.bodyMeshes.set(i,u)}u.position.set(n,s,a),this.setHitArea(i,n,s,a,Co),this.setGlare(i,n,s,a,l,Co/vn*(180/Math.PI))}bentLight(e,t,i=iA){const[n,s,a]=this.arrivingIlluminance(e,t,1),o=Math.max(s,1e-30);return{scale:s/i,tint:[n/o,1,a/o]}}static litBySky(e,t){return Xt.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,Vt.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=ut.illuminanceOf(t)*i*this.relativeScale;return[n[0]*s,n[1]*s,n[2]*s]}static unitLuminance(e){const t=Nn.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 a=this.hitAreas.get(e);if(!a){const l=new Tc({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1});a=new yo(l),this.celestialGroup.add(a),this.hitAreas.set(e,a)}a.position.set(t,i,n);const o=s*tA;a.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,a,o=0){if(Gi.radiusDeg(Gi.K*Math.max(...s))<=Math.max(a,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,a,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=Uo(e.magnitude,t),s=yd(n);this.cometKey=i,this.setBodyMesh(i,e.position,Ld*(.5+.5*n),new se(s,s,s),e.magnitude),this.cometTail||(this.cometTail=new an(vn),this.celestialGroup.add(this.cometTail.object)),this.cometTail.set(e.position,e.tailEnd,n)}buildIceHalos(e,t){this.iceHalos||(this.iceHalos=new Pi,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(),a=st.deckLitUntilDeg(st.DECK_HEIGHT_M),o=e.altitudeDeg>-a?e:t,l=this.bentLight(o,o===e?li.SUN_MAGNITUDE:t.magnitude),c=st.strength(i,s,o.altitudeDeg,a)*l.scale,{x:h,y:d,z:u}=ci(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 Fa,this.celestialGroup.add(this.rainbow.object));const i=this.weather.precipitationType==="rain"?this.weather.precipitationIntensity:0,n=e.altitudeDeg>0,s=n?e:t,a=this.lowerCloudCover(),o=this.bentLight(s,n?li.SUN_MAGNITUDE:t.magnitude,nA),l=Oi.strength(i,a,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}=ci(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 Js,this.skyGlow.onReady=()=>this.render(),this.celestialGroup.add(this.skyGlow.object));const{date:i,observer:n}=e.frame,s=ln.galactic(i,n),a=Xt.directionOf(s.centre),o=Xt.directionOf(s.rotation),l=Xt.directionOf(s.pole),c=new Xe().set(a.x,a.y,a.z,o.x,o.y,o.z,l.x,l.y,l.z),h=Xt.directionOf(ln.eclipticPole(i,n)),d=Xt.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:Xt.directionOf(e.moon),altitudeDeg:e.moon.altitudeDeg,phaseAngleDeg:We.phaseAngleOf(e.moon.phase.illuminatedFraction)},skyColor:new R(t[0],t[1],t[2])})}static directionOf(e){const{x:t,y:i,z:n}=ci(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 a=Uo(n.magnitude,t),o=yd(a);this.setBodyMesh(s,n.position,Ld*(.5+.5*a),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=Uo(n.magnitude,t),a=yd(s),o=kA[n.body]??new se(1,1,1),l=new se(o.r*a,o.g*a,o.b*a),c=Ld*(.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,a=new On(n,s),o=Hd(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:f}=a.position(i.ra[h],i.dec[h]);if(u<dg)continue;const{x:p,y:v,z:m}=ci(u,f,Rd);l.push({x:p,y:v,z:m,brightness:Uo(d,t),illuminance:this.air.transmittanceFromSpace(c,u).map(g=>g*ut.illuminanceOf(d)),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}for(const h of a5){if(h.mag>t)continue;const{altitudeDeg:d,azimuthDeg:u}=a.position(h.raHours,h.decDeg);if(d<dg)continue;const{x:f,y:p,z:v}=ci(d,u,Rd);this.namedStars.push({star:h,direction:new R(f,p,v).normalize(),altitudeDeg:d})}this.starTiers=qo.map((h,d)=>{const u=l.filter(S=>gv(S.brightness)===d),f=new Float32Array(u.length*3),p=new Float32Array(u.length),v=new Float32Array(u.length*3),m=new Float32Array(u.length),g=new Float32Array(u.length);u.forEach((S,E)=>{f[E*3]=S.x,f[E*3+1]=S.y,f[E*3+2]=S.z,p[E]=S.brightness,v.set(S.illuminance,E*3),m[E]=S.phase,g[E]=S.speedFactor});const w=new pt;w.setAttribute("position",new it(f,3));const x=new it(new Float32Array(u.length*3),3);w.setAttribute("color",x);let y=this.starTiers[d]?.points;return y?(y.geometry.dispose(),y.geometry=w):(y=new Ts(w,Vt.material(h.size)),Vt.track(y),this.celestialGroup.add(y)),{points:y,colorAttribute:x,brightness:p,illuminance:v,phase:m,speedFactor:g}}),this.updateTwinkle(0),this.startTwinkle()}twinkleSeconds=0;updateTwinkle(e){const t=this.relativeScale;for(const i of this.starTiers){const n=i.colorAttribute.array;for(let s=0;s<i.brightness.length;s++){const a=r_(i.brightness[s],{phase:i.phase[s],speedFactor:i.speedFactor[s]},e),o=t*a;n[s*3]=i.illuminance[s*3]*o,n[s*3+1]=i.illuminance[s*3+1]*o,n[s*3+2]=i.illuminance[s*3+2]*o}i.colorAttribute.needsUpdate=!0}}buildClouds(){if(this.cloudRendering==="volume"||this.weather.cloudLayers!==void 0){this.layeredClouds||(this.disposeCloudSystem(),this.layeredClouds=new Gr(this.celestialGroup,Od,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(Sa[0]+(Fd[0]-Sa[0])*t,Sa[1]+(Fd[1]-Sa[1])*t,Sa[2]+(Fd[2]-Sa[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}=tf(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 a=nf(Od);this.cloudMesh=new De(a,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}=tf(new se(...Sa),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(nf(Od*1.04),t),this.cirrusMesh.renderOrder=yg-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?Gr.deckOrder(!0,0,1):yg}cloudLayerOffset(){const t=((this.weather.cloudBaseM??1e3)-this.observerElevationM)*kd;return Math.sign(t||1)*Math.max(Math.abs(t),yA)}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,a,o]=this.cloudBeam.map(d=>d/Math.PI),l=this.lightningLevel*zd*this.relativeScale/Math.PI,c=new se(t[0]+$n.r*l,t[1]+$n.g*l,t[2]+$n.b*l);this.layeredClouds?.setLighting(n.clone(),new se(s,a,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,a*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}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=bg[e],i=t.poolSize,n=Hd(9001),s=new Float32Array(i*3),a=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),f=new Float32Array(i),p=new Float32Array(i);for(let x=0;x<i;x++){const y=n()*Math.PI*2,S=n()*t.radiusM;s[x*3]=Math.cos(y)*S,s[x*3+1]=Tr+n()*(t.topYM-Tr),s[x*3+2]=Math.sin(y)*S,a[x]=n()*Math.PI*2,o[x]=.7+n()*.6,l[x]=.6+n()*.8,c[x]=.4+n()*1.2}const v=new pt;v.setAttribute("position",new it(s,3)),v.setDrawRange(0,Bd(i,this.weather.precipitationIntensity));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:NA(e)},uNearFocusDistance:{value:Ig},uHazeDistance:{value:t.radiusM*Dg}},g=new lt({uniforms:m,vertexShader:GA,fragmentShader:VA,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const w=new Ts(v,g);w.renderOrder=Nd,this.scene.add(w),this.precipitationPoints=w,this.precipitationPositions=s,this.precipitationPhase=a,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=d,this.precipitationBounceDuration=u,this.precipitationBounceVelX=f,this.precipitationBounceVelZ=p}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=bg[i];if(this.precipitationUniforms?.uColor.value.copy(Xt.litBySky(n.color,this.baseFogColor)),t<=0)return;const s=this.weather.windDirectionDeg*Yi,a=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,f=this.precipitationBounceRemaining,p=this.precipitationBounceHeight,v=this.precipitationBounceDuration,m=this.precipitationBounceVelX,g=this.precipitationBounceVelZ,w=i==="snow"&&d&&u,x=i==="hail"&&f&&p&&v&&m&&g;for(let y=0,S=0;y<l.length;y+=3,S++){if(x&&f[S]>0){if(f[S]-=t,f[S]<=0){const b=Math.random()*Math.PI*2,T=Math.sqrt(Math.random())*n.radiusM;l[y]=Math.cos(b)*T,l[y+1]=n.topYM,l[y+2]=Math.sin(b)*T,f[S]=0}else{const b=1-f[S]/v[S];l[y+1]=Tr+Math.sin(Math.PI*b)*p[S],l[y]=pc(l[y]+(a+m[S])*t,n.radiusM),l[y+2]=pc(l[y+2]+(o+g[S])*t,n.radiusM)}continue}let E=0,I=0;if(w){const b=TA*d[S],T=IA*u[S];E=Math.sin(e*b*Math.PI*2+c[S])*T,I=Math.sin(e*b*Math.PI*2+c[S]+Math.PI/2)*T}if(l[y]+=(a+E)*t,l[y+1]-=n.fallSpeedMPerS*h[S]*t,l[y+2]+=(o+I)*t,l[y]=pc(l[y],n.radiusM),l[y+2]=pc(l[y+2],n.radiusM),l[y+1]<Tr){if(x){p[S]=Og*(.2+Math.random()*1.6),v[S]=DA*(.5+Math.random()),f[S]=v[S];const A=Math.random()*Math.PI*2,D=CA*(p[S]/Og);m[S]=Math.cos(A)*D,g[S]=Math.sin(A)*D,l[y+1]=Tr;continue}const b=Math.random()*Math.PI*2,T=Math.sqrt(Math.random())*n.radiusM;l[y]=Math.cos(b)*T,l[y+1]=n.topYM,l[y+2]=Math.sin(b)*T}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=Sg(this.weather.precipitationIntensity);this.rainSystem=M5({count:Mg,radiusM:Ro,heightM:MA,bottomYM:wg,overallSpeed:wA,color:Eg.clone(),opacity:.85,visibleCount:Bd(Mg,this.weather.precipitationIntensity),speedStreak:_A(e),pixelSize:Rg,seed:9001,nearFocusDistanceM:Ig,hazeDistanceM:Ro*Dg,hazeColor:new se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.syncRainSlant(),this.rainSystem.points.renderOrder=Nd,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=Hd(4242),t=new Float32Array(Mr*3),i=new Float32Array(Mr),n=new Float32Array(Mr),s=new Float32Array(Mr);for(let h=0;h<Mr;h++){const d=e()*Math.PI*2,u=Math.sqrt(e())*Ro;t[h*3]=Math.cos(d)*u,t[h*3+1]=wg,t[h*3+2]=Math.sin(d)*u,n[h]=kg(_g,Ag),i[h]=e()*n[h],s[h]=i[h]/n[h]}const a=new pt;a.setAttribute("position",new it(t,3));const o=new it(s,1);a.setAttribute("aLife",o),a.setDrawRange(0,Bd(Mr,this.weather.precipitationIntensity));const l=new lt({uniforms:{uMaxSize:{value:AA},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:Tg.clone()},uTexture:{value:BA()}},vertexShader:zA,fragmentShader:HA,transparent:!0,depthWrite:!1}),c=new Ts(a,l);c.renderOrder=Nd,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*Yi;e.uniforms.uDropVelocity.value.set(Math.sin(t)*this.weather.windSpeed,-Sg(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(Xt.litBySky(Eg,this.baseFogColor));const i=this.rainSplashSystem?.points.material?.uniforms?.uColor;i&&i.value.copy(Xt.litBySky(Tg,this.baseFogColor));const n=this.weather.windDirectionDeg*Yi,s=Math.sin(n)*this.weather.windSpeed,a=-Math.cos(n)*this.weather.windSpeed,o=Ro,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+a*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=Pg+(1-Pg)*(1-c),d=Lg+(1-Lg)*(1-c);t.uniforms.uUvSquash.value=d,t.uniforms.uPixelSize.value=Rg*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:a}=t,o=a.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())*Ro;i[l]=Math.cos(h)*d,i[l+2]=Math.sin(h)*d,s[c]=kg(_g,Ag),n[c]=0}o[c]=n[c]/s[c]}t.points.geometry.attributes.position.needsUpdate=!0,a.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=di.brightnessAt(l,e);c>i&&(t=l,i=c)}const n=t?Math.min(1,Math.max(.1,(LA/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*zd*this.relativeScale/Math.PI;l.color.setRGB(this.airlight[0]+$n.r*c,this.airlight[1]+$n.g*c,this.airlight[2]+$n.b*c)}const a=this.skyMesh?.material?.color;if(a){const l=s*OA;a.setRGB(1+l*$n.r,1+l*$n.g,1+l*$n.b)}if(this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.lightningLight.intensity=s*zd*this.relativeScale,t){const{x:l,y:c,z:h}=ci(25,t.azimuthDeg,vn);this.lightningLight.position.set(l,c,h)}this.lightningBolt||(this.lightningBolt=new Qs(vn*.95),this.celestialGroup.add(this.lightningBolt.object));const o=this.weather.cloudBaseM??1500;this.lightningBolt.set(t,i,o),this.render()}buildCompassLabels(){const e=Jr(navigator.languages,cA),t=lA[e];this.compassSprites=oA.map((i,n)=>{const s=t[n],a=new Tc({map:WA(s),depthTest:!1,fog:!1}),o=new yo(a),{x:l,y:c,z:h}=ci(0,i,pg);return o.position.set(l,c,h),o.scale.set(fc,fc,1),o.renderOrder=uA,o.layers.set(og),o.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(o),o}),this.updateCompassScale()}updateCompassScale(){const e=this.camera.fov*Math.PI/360,t=mg*Math.PI/360,i=fc*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 Yi=Math.PI/180;function kr(r,e,t){return Math.max(e,Math.min(t,r))}function kg(r,e){return r+Math.random()*(e-r)}function pc(r,e){return(r%(e*2)+e*3)%(e*2)-e}let mc,gc;function NA(r){switch(r){case"snow":return FA();case"hail":return UA()}}function FA(){if(mc)return mc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),mc=new Us(e),mc}function UA(){if(gc)return gc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),gc=new Us(e),gc}let vc;function BA(){if(vc)return vc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),vc=new Us(e),vc}const zA=`
|
|
8517
|
+
`,transparent:!0,blending:Fi,depthWrite:!1,side:Wt,fog:!1});this.object=new De(new ai(Ep.RADIUS,128,32,0,Math.PI*2,0,ut.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<ut.STEPS;i++){for(let n=0;n<3;n++)this.texels[i*4+n]=Bn.toHalfFloat(Math.min(t[i*3+n],6e4));this.texels[i*4+3]=Bn.toHalfFloat(1)}this.texture.needsUpdate=!0,this.object.visible=!0}dispose(){this.object.geometry.dispose(),this.object.material.dispose(),this.texture.dispose()}}class ah{constructor(e){this.generator=e}static SIZE=128;target=new cl(ah.SIZE,{type:qt,generateMipmaps:!1,minFilter:ot,magFilter:ot});camera=new op(.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 a=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=a[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 rn{static REFRESH_MS=250;generator;eye;probes=new Map;constructor(e){rn.install(),this.generator=new ju(e),this.eye=new ah(this.generator)}static decor;refresh(e,t,i,n,s,a,o,l=[],c){for(const v of[...this.probes.keys()])n.some(m=>m.id===v&&m.shiny)||(this.probes.get(v).dispose(),this.probes.delete(v));const h=[{probe:this.eye}];for(const v of n){if(!v.shiny)continue;let m=this.probes.get(v.id);m||(m=new ah(this.generator),this.probes.set(v.id,m)),h.push({probe:m,reflector:v})}const d=performance.now(),u=()=>h.filter(({probe:v})=>v.capturedVersion!==o),f=u().filter(({probe:v})=>d-v.capturedAtMs>=rn.REFRESH_MS);if(f.length>0){const v=f.reduce((m,g)=>g.probe.capturedAtMs<m.probe.capturedAtMs?g:m);if(v.reflector){const m=v.reflector.holder.getWorldPosition(this.scratch);v.probe.capture(e,t,m,[v.reflector.holder,...s]),rn.reflectOn(v.reflector.holder,v.probe)}else{v.probe.capture(e,t,i,[...s,...l]),c?.(v.probe.sharp),rn.decor=v.probe.filtered;for(const m of a)rn.reflectOn(m,v.probe);for(const m of n)m.shiny||rn.reflectOn(m.holder,v.probe)}v.probe.capturedVersion=o}const p=u();if(p.length!==0)return Math.max(16,Math.min(...p.map(({probe:v})=>rn.REFRESH_MS-(d-v.capturedAtMs))))}scratch=new R;static reflectOn(e,t={filtered:rn.decor}){e.traverse(i=>{const n=i.material;for(const s of Array.isArray(n)?n:n?[n]:[])s instanceof dl?t.sharp&&s.setSurroundings(t.sharp):s instanceof Ca&&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(rn.installed)return;rn.installed=!0;const e="iblIrradiance += getIBLIrradiance( geometryNormal );";if(!qe.lights_fragment_maps.includes(e)){console.warn("Reflections: three's lights_fragment_maps changed; the environment will light diffusely too");return}qe.lights_fragment_maps=qe.lights_fragment_maps.replace(e,"// The sky's diffuse light is the hemisphere light's (see Reflections).")}}function j5(r,e){return JSON.stringify(r.cloudLayers)===JSON.stringify(e.cloudLayers)&&r.cloudCover===e.cloudCover&&r.cloudDarkness===e.cloudDarkness&&r.cloudBaseM===e.cloudBaseM&&r.highCloudCover===e.highCloudCover&&r.iceCrystalAlignment===e.iceCrystalAlignment&&r.lowerCloudCover===e.lowerCloudCover&&r.relativeHumidity===e.relativeHumidity&&r.precipitationType===e.precipitationType&&r.precipitationIntensity===e.precipitationIntensity&&r.windDirectionDeg===e.windDirectionDeg&&r.windSpeed===e.windSpeed&&r.storm===e.storm}const hc=900,Dd=1,rg=4,og=5,lg=.5,Ho=900,J5=6371e3,Q5=3e4;function cg(r){const e=Math.sqrt(2*J5*Math.max(r,0));return Math.max(Ho,Math.min(e,Q5))}const Cd=.5,Rd=850,hg=.03,vn=850,Pd=300,eA=.04,Co=4,Ld=1.5,dg=-1,tA=6,Cf=-4,iA=2,nA=20,dc=2600,sA=.97,aA=.38,uc=Cf,ug=1500,rA=400,oA=[0,45,90,135,180,225,270,315],lA={en:["N","NE","E","SE","S","SW","W","NW"],fr:["N","NE","E","SE","S","SO","O","NO"]},cA=["en","fr"],hA=2,dA=16,fg=2500,pg=880,fc=40,mg=60,uA=10,fA=1,pA=150,mA=55,gA=.5,vA=17,gg=.265,vg=[[0,0],...Array.from({length:8},(r,e)=>{const t=e*Math.PI/4;return[Math.cos(t)*gg,Math.sin(t)*gg]})],Od=700,kd=250/1e3,yA=12,yg=5,Nd=6,Sa=[.92,.92,.95],Fd=[.15,.15,.19],bg={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}},Tr=0,Ud=0,bA=150,Mg=3e3,Ro=20,MA=15,wg=Tr,wA=40,xg=1,xA=9;function Sg(r){return xg+(xA-xg)*r}const Eg=new se(.75,.82,.92),SA=10,EA=.5;function _A(r){return kr(r/SA,EA,1)}const Mr=200,_g=.18,Ag=.32,AA=.35,Tg=new se(.85,.9,.98),Ig=5,Dg=.85,Cg=.01;function Bd(r,e){return Math.round(r*(Cg+(1-Cg)*e))}const Rg=230,Pg=.7,Lg=.05,TA=.6,IA=.7,DA=.15,Og=.35,CA=3,RA=1/12,PA=.5,$n=new se(.82,.86,1),LA=2500,OA=1.6,zd=15,kA={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 Xt{renderer;scene=new Os;phenomena=new Rn(this.scene);references=new Oa(this.scene,()=>this.render());camera;celestialGroup=new Ot;skyMesh;skyGradientMaterial;scatteredSky;lastAstronomy;skyColoursStale=!1;scatteredSkyFrameId=null;skyHoldUntilMs=void 0;onFirstFrameHold;firstFrameReady(){return(!this.scatteredSky||this.scatteredSky.ambient!==void 0)&&!this.iceHalos?.awaitingFirstDisplay}groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;roadSystem=new yn;roadAttribution;roadWays;statedRoads=[];decorModelProvider=Cc[0].create();decorModelToken=0;decorModelCredits=new Map;bodySystem=new ht(e=>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 Q0(16777215,0);celestialLightTarget=new Nt;skyLight=new SM(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 cd;weather=n1;observerElevationM=0;siteElevationM=0;air=new Mn;groundRadius=Ho;terrainRadius=Ho;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 Q0(16777215,0);baseFogColor=[0,0,0];lastSunPosition;lensFlare;sunUnhiddenFraction=1;lensFlareArtifactIntensity=0;sunWorldPosition=new R;sunVisible=!1;lensFlareScratch=new R;sunOcclusionRaycaster=new cd;sunOcclusionDirectionScratch=new R;sunOcclusionRightScratch=new R;sunOcclusionUpScratch=new R;ufoOcclusionRaycaster=new cd;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;meteorSystem;cometTail;satelliteField;iceHalos;lunarDiffraction;lunarDiffractionKey="";rainbow;skyGlow;cometKey;novaKeys=new Set;constructor(e,t=x2()){this.renderer=new NE({canvas:e,antialias:!0}),this.resolution=new mt(Math.min(window.devicePixelRatio||1,2),this.renderer.getContext()),this.renderer.setPixelRatio(this.resolution.pixelRatio),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=Go,this.camera=new Ji(60,e.width/Math.max(e.height,1),.1,hc*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=-120,this.celestialLight.shadow.camera.right=120,this.celestialLight.shadow.camera.top=120,this.celestialLight.shadow.camera.bottom=-120,this.celestialLight.shadow.camera.near=vn-Pd,this.celestialLight.shadow.camera.far=vn+Pd,this.celestialLight.shadow.bias=-.03/(2*Pd),this.celestialLight.shadow.normalBias=eA,this.celestialLight.target=this.celestialLightTarget,this.lightningLight.color.copy($n),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 li(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*kd,0,e.z*kd);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)*Yi,i=(e.pitchDeg+this.lookPitchDeg+this.gaitOffset.pitchDeg)*Yi;if(e.headingDeg!==void 0?this.camera.rotation.set(i,-(e.headingDeg+this.lookYawDeg+this.gaitOffset.yawDeg)*Yi,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(cg(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/Ho),this.camera.far=Math.max(hc*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.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)<pA*(n/Ho))return}const s=this.terrainOrigin,a=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;h5(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)<=a&&(l.position.y=d.position.y+this.groundYOfPatch(d,u.x,u.z)),this.disposeMesh(this.terrainMesh),l.renderOrder=fA,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 ROADS_MAX_RADIUS_M=5e3;buildRoads(e,t,i){const n=this.terrainProviders.roads;if(!n||i>Xt.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}n.getRoads(Nv(e,t,i)).then(a=>{this.terrainOrigin?.lat!==e||this.terrainOrigin?.lng!==t||(this.roadWays={lat:e,lng:t,radiusM:i,ways:a},this.drapeRoads(a,e,t,n.contemporary))}).catch(a=>{console.warn("Roads unavailable, drawing none:",a)})}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,(a,o)=>this.groundYOfPatch(s,a,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())}applyLensFlareTint(e){if(!this.lensFlare)return;const t=mA*(this.sunArriving[1]/vA);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){this.cloudTransmissionMemo.clear(),!j5(this.weather,e)&&(this.weather=e,this.air=Mn.of(e),this.applyAir(),this.scatteredSky?.setConditions({aerosolOpticalDepth:e.relativeHumidity===void 0?void 0:Rs.steppedOpticalDepth(e.relativeHumidity)}),this.pumpScatteredSky(),this.buildClouds(),this.buildCirrus(),this.buildPrecipitation(),this.lastAstronomy&&(this.buildIceHalos(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildRainbow(this.lastAstronomy.sun,this.lastAstronomy.moon),this.buildLunarDiffraction(this.lastAstronomy.moon)),this.lightningArmed=e.storm&&e.cloudDarkness>=PA,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.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(a=>a.id),n=[],s){const{x:a,z:o}=this.bodyOrigin,l=this.scatteredSky;this.bodySystem.set(e,{originX:a,originZ:o,originGroundY:this.groundYUnder(a,o),eye:this.camera.position,groundYAt:(h,d)=>this.groundYUnder(h,d),wind:s,light:this.plumeLight(),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);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,a=(o,l,c)=>(o*t+(l+c)/2*s)/Math.PI;return[a(e.r,i.r,n.r),a(e.g,i.g,n.g),a(e.b,i.b,n.b)]}bodyOutline(e,t){return this.bodySystem.outlineOf(e,t)}async loadBodyModel(e){const t=e.url?void 0:e.id?await this.decorModelProvider.entry(e.id):void 0,i=e.url??t?.url,n=e.credit??t?.credit;if(!(!i||!n))return{scene:await ag(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=i.url??n?.url,a=i.credit??n?.credit;if(!s||!a)return;const o=await ag(s);if(t!==this.decorModelToken)return;const l=this.decorGroups.get(e.id);if(!l)return;rn.reflectOn(o),ui.applyModel(l,e,o,{headingOffsetDeg:i.headingOffsetDeg??n?.headingOffsetDeg,depictedSizeM:n?.sizeM}),this.decorModelCredits.set(e.id,a),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.witnessSide!==void 0&&Dr(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 a={x:0,z:0};let o=0;if(s){const c=ui.occupantView(s),h=-(s.headingDeg??0)*Yi,d=c.x*Math.cos(h)+c.z*Math.sin(h),u=-c.x*Math.sin(h)+c.z*Math.cos(h),f=s.eastM+n.x,p=-s.northM+n.z;a.x=-(f+d),a.z=-(p+u),this.camera.position.y=this.groundYUnder(f,p)+c.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*Yi,-(c.headingDeg+this.indoorLookYawDeg)*Yi,0,"YXZ")}this.bodyOrigin={x:n.x+a.x,z:n.z+a.z},this.placeShadowFrustum();for(const c of this.decorObjects){const h=this.decorGroups.get(c.id);if(!h)continue;const d=Zr(c,i),u=d.eastM+n.x+a.x,f=-d.northM+n.z+a.z,p=this.terrainMesh,v=`${p?.geometry.uuid}:${(u-(p?.position.x??0)).toFixed(3)}:${(f-(p?.position.z??0)).toFixed(3)}:${(p?.position.y??0).toFixed(3)}:${d.headingDeg}:${d.altitudeM}`;if(h.userData.footprintKey!==v&&(h.userData.footprintGroundY=this.groundUnderFootprint(c,u,f,d.headingDeg,d.altitudeM),h.userData.footprintKey=v),h.position.set(u,h.userData.footprintGroundY+d.altitudeM,f),c.kind==="crop"){const m=Math.hypot(u,f);h.visible=m<=bA}if(d.headingDeg!==void 0&&(h.rotation.y=-d.headingDeg*Yi),c.kind==="crop"&&h.visible){const m=this.terrainMesh,g=[m?.geometry.uuid,u-(m?.position.x??0),f-(m?.position.z??0),h.position.y-(m?.position.y??0),d.headingDeg,d.altitudeM].map(w=>typeof w=="number"?w.toFixed(4):w).join(":");h.userData.cropGroundKey!==g&&(ui.fitCropToGround(h,(w,x)=>this.groundYUnder(w,x)+d.altitudeM),h.userData.cropGroundKey=g)}o=Math.max(o,h.position.distanceTo(this.camera.position))}const l=Math.max(hc*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):Ud}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 Ud;const a=n.getX(0),o=n.getX(s-1),l=n.getZ(0),c=n.getZ((s-1)*s),h=kr((t-a)/(o-a)*(s-1),0,s-1),d=kr((i-l)/(c-l)*(s-1),0,s-1);if(!Number.isFinite(h)||!Number.isFinite(d))return Ud;const u=Math.min(Math.floor(h),s-2),f=Math.min(Math.floor(d),s-2),p=h-u,v=d-f,m=(S,E)=>n.getY(S*s+E),g=m(f,u),w=m(f,u+1),x=m(f+1,u),y=m(f+1,u+1);return p+v<=1?g+(w-g)*p+(x-g)*v:y+(x-y)*(1-p)+(w-y)*(1-v)}groundUnderFootprint(e,t,i,n,s=0){return ui.groundUnderFootprint(e,t,i,n,(a,o)=>this.groundYUnder(a,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}updateDecorLitState(e,t=0){for(const i of this.decorObjects){const n=this.decorGroups.get(i.id);if(n&&i.lights?.length&&ui.setLights(n,i.lights,e,n.position.distanceTo(this.camera.position),t,this.relativeScale),i.kind!=="streetlight"&&i.kind!=="vehicle")continue;const s=this.decorGroups.get(i.id);if(!s)continue;const a=Nc(i,e);ui.setLit(s,i.kind,a,this.relativeScale);const o=this.streetlightLights.get(i.id);o&&(o.visible=a)}}addStreetlightLight(e,t){const i=ui.bodyOf(t),n=i.children.find(a=>a.userData.emissive);if(!n)return;const s=new iv(16764006,ug*this.relativeScale,rA);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.skyHoldUntilMs=performance.now()+fg,this.onFirstFrameHold?.(!0),setTimeout(()=>this.render(),fg+20)),this.pumpScatteredSky(),this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround();const t=aa(e.sun.altitudeDeg,this.instrumentMagnitudeGain);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=a=>{const o=Nn.luminanceOf(a);if(!(o>0))return[0,0,0];const l=_e.relativeOfResponse(o)/o;return[a[0]*l,a[1]*l,a[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,Co,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,aa(e.sun.altitudeDeg,this.instrumentMagnitudeGain)),this.applyDazzleStrength(),this.buildIceHalos(e.sun,e.moon),this.buildRainbow(e.sun,e.moon));const t=this.scatteredSky?.ambient??Xt.relativeSkyOf(of(e.sun.altitudeDeg),this.relativeScale),i=t,n=this.lightUnderClouds(e),s=a=>n?[0,1,2].map(o=>a[o]*n.through+n.radiance[o]*(1-n.through)):[a[0],a[1],a[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),Vt.setEye(this.scatteredSky?.rodShare??0,this.relativeScale),this.updateTwinkle(this.twinkleSeconds);for(const a of this.streetlightLights.values())a.intensity=ug*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?ke.DEFAULT_AEROSOL_OPTICAL_DEPTH:Rs.opticalDepth(i))*ke.aerosolDensity(this.siteElevationM+this.observerElevationM),s=this.cloudTransmission(e),a=-Math.log(Math.max(this.cloudTransmission(e,!0),1e-4)),o=ut.aureoleScale(n,ut.airMass(e.altitudeDeg))*s,l=ut.coronaScale(a),c=ut.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 Ep,this.celestialGroup.add(this.lunarDiffraction.object));const d=ut.profile(ut.HAZE_COARSE_MODE),u=ut.profile(ut.CLOUD_DROPLETS),f=Dm(e.altitudeDeg),p=new Float32Array(ut.STEPS*3);let v=0;for(let x=0;x<ut.STEPS;x++){const y=x*3,S=[0,1,2].map(I=>c*f[I]*(o*d[y+I]+l*u[y+I])),E=Nn.shown(S,Nn.luminanceOf(S),(I,b)=>t.relativeOfLuminance(I,b));p.set(E,y),v=Math.max(v,Nn.response(E))}const{x:m,y:g,z:w}=ci(e.altitudeDeg,e.azimuthDeg,1);this.lunarDiffraction.show(v>1/200?{x:m,y:g,z:w}:void 0,p)}applyAir(){this.scene.fog instanceof Ga||(this.scene.fog=new Ga),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,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:We.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?dA:hA),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 _5(Rd),this.celestialGroup.add(this.satelliteField.object));const t=aa(this.lastSunPosition?.altitudeDeg??-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(hg*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>=uc,s=!n&&e.moon.altitudeDeg>=uc,a=n?e.sun:e.moon,o=[0,0,0],l=Math.max(0,Math.sin(Math.max(a.altitudeDeg,0)*Yi)),c=n||s?this.cloudTransmission(a):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 w=ut.illuminanceOf(a.magnitude)*i,x=this.air.transmittanceFromSpace(this.siteElevationM+this.observerElevationM,a.altitudeDeg);for(let I=0;I<3;I++)o[I]=w*x[I]*c,u[I]=w*x[I]*d*l,this.cloudBeam[I]=w*x[I];const{x:y,y:S,z:E}=ci(a.altitudeDeg,a.azimuthDeg,1);this.cloudBeamDirection.set(y,S,E)}else this.cloudBeam.fill(0);const f=this.surroundings;if(f)this.skyLight.color.setRGB(f.up[0]*i,f.up[1]*i,f.up[2]*i),this.skyLight.groundColor.setRGB(f.down[0]*i,f.down[1]*i,f.down[2]*i);else{const w=this.skyCloudTransmission(),x=t.skyIrradiance,y=[0,1,2].map(E=>x[E]*w+u[E]);this.skyLight.color.setRGB(y[0],y[1],y[2]);const S=Ve.GROUND_ALBEDO;this.skyLight.groundColor.setRGB(S*(y[0]+o[0]*l),S*(y[1]+o[1]*l),S*(y[2]+o[2]*l))}if(this.skyLight.intensity=1,!n&&!s){this.celestialLight.intensity=0;return}const{x:p,y:v,z:m}=ci(a.altitudeDeg,a.azimuthDeg,1);this.lightDirection.set(p,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(),a=this.shadowWorld.set(this.bodyOrigin.x,0,this.bodyOrigin.z),o=c=>{const h=a.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,vn)}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*Yi,s=Math.sin(n)*Math.cos(n)*2;for(let a=0;a<360;a+=60)e+=s*this.cloudTransmission({altitudeDeg:i,azimuthDeg:a}),t+=s}return e/t}cloudBeam=[0,0,0];cloudBeamDirection=new R(0,1,0);get relativeScale(){return this.scatteredSky?.relativeScale??1/_e.semiSaturation(_e.DAYLIGHT_ANCHOR_CD_M2)}setMeteorShower(e,t,i){this.meteorSystem||(this.meteorSystem=new Mi,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;const t=vt.starPointsOf(e),i=vt.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())}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.skyHoldUntilMs){if(!this.firstFrameReady()&&performance.now()<this.skyHoldUntilMs)return;this.skyHoldUntilMs=null,this.iceHalos?.setUrgent(!1),this.onFirstFrameHold?.(!1)}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){this.frameDirty=!1,this.refreshReflections(),this.resolution.beginDrawing();try{this.renderOnce()}finally{this.resolution.endDrawing()}this.developingExposure&&this.startExposure()}}}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()+Xt.COMPILE_WAIT_MS;return new Promise(n=>{const s=()=>{if(this.contextReleased)return n();for(const a of e){const o=t.get(a)?.currentProgram;(!o||o.isReady())&&e.delete(a)}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(){this.reflections??=new rn(this.renderer);const e=[...this.compassSprites,...this.lensFlare?[this.lensFlare.mesh]:[],...[...this.glareSprites.values()].map(s=>s.mesh)],t=[...this.starTiers.map(s=>s.points),...[...this.bodyMeshes.values()].filter(s=>s instanceof Ts),...[this.rainSystem?.points,this.precipitationPoints,this.rainSplashSystem?.points].filter(s=>s!==void 0)],i=this.relativeScale,n=this.reflections.refresh(this.renderer,this.scene,this.camera.position,this.bodySystem.reflectors,e,[...this.decorGroups.values()],this.sceneVersion,t,s=>this.measureSurroundings(s,i));n===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()}))},n))}measureSurroundings(e,t){const i=this.lastAstronomy;if(!i)return;this.probeIrradiance??=new Yo;const n=[i.sun,i.moon].filter(s=>s.altitudeDeg>-2).map(s=>ci(s.altitudeDeg,s.azimuthDeg,1));this.probeIrradiance.measure(this.renderer,e,n,s=>{const a=u=>u.map(f=>f/t),o=this.surroundings,l={up:a(s.up),down:a(s.down)},c=(u,f)=>u.some((p,v)=>Math.abs(p-f[v])>.03*Math.max(Math.abs(p),Math.abs(f[v]),1e-12)),h=!o||c(o.up,l.up)||c(o.down,l.down);h&&(this.surroundings=l);const d=this.scatteredSky?.adaptToSurroundings(Math.exp(s.logAverage)/t)??!1;!h&&!d||(this.skyColoursStale=!0,this.render())})}lightUnderClouds(e){const t=this.surroundings;if(!t)return;const i=e.sun.altitudeDeg>=uc?e.sun:e.moon.altitudeDeg>=uc?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,RA);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 a=this.mapSubjectBounds();e?this.exposureSubjectBounds.push(...a):this.onMapSubjectBounds(a)}const t=this.usableEquidistantPass();this.phenomena.setCloudLayers(this.layeredClouds?.volumes??[]),this.phenomena.place(this.camera,(a,o,l)=>this.directionAtScreenPoint(a,o,l),a=>this.screenPointOf(a)),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()),wn.uRodShare.value=this.scatteredSky?.rodShare??0,wn.uRelativeScale.value=this.relativeScale;const n=this.references.any||this.phenomena.any||this.compassSprites.some(a=>a.visible);wn.uHasOverlay.value=n?1:0;const s=n?a=>this.renderOverlayPasses(a):void 0;if(this.skyGlow?.setDestinationEncoded(!1),!t){this.updateLensFlarePosition();const a=this.usableDepthOfFieldPass();a?a.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,a=>{this.lensFlare&&(this.lensFlare.mesh.visible=a?!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(og),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(Yv),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}setReferences(e){this.references.set(e)}referenceAspect(e){return this.references.aspectOf(e)}directionAt(e,t){return this.directionAtScreenPoint(e,t,new R)}referenceFailedToLoad(e){return this.references.failedToLoad(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&&Xt.markDecorDepth(s,n.kind!=="crop")}e.layers.set(Dd),this.scene.overrideMaterial=this.decorDepthMaterial,this.renderer.render(this.scene,e),this.scene.overrideMaterial=null,e.layers.set(qv),this.renderer.render(this.scene,e),e.layers.set(0),this.renderer.autoClear=t,this.renderer.shadowMap.autoUpdate=i}decorDepthMaterial=new En({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!==Fi?i.layers.enable(Dd):i.layers.disable(Dd)})}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 WE(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()+Xt.EXPOSURE_BUDGET_MS,s=Xt.exposureInstantsPerFrame(t.instantTarget.scene.width*t.instantTarget.scene.height);let a=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++,a++}while(this.exposureInstantsDone<this.exposureInstants&&performance.now()<n&&a<s);this.restateAt(e,0),this.presentExposure(),this.resolution.endDrawing(),this.exposureInstantsDone<this.exposureInstants&&(this.exposureFrameId=requestAnimationFrame(()=>this.developExposure()))}presentExposure(){if(wn.uRodShare.value=this.scatteredSky?.rodShare??0,wn.uHasOverlay.value=1,wn.uRelativeScale.value=this.relativeScale,this.exposureAccumulation?.develop(this.renderer,this.exposureInstants/this.exposureInstantsDone),this.onMapSubjectBounds?.(this.exposureSubjectBounds),!this.compassSprites.some(s=>s.visible))return;const e=new Os,t=new zn(-1,1,1,-1,0,2),i=fc/(pg*Math.tan(mg*Yi/2));for(const s of this.compassSprites){if(!s.visible)continue;const a=s.getWorldPosition(new R).sub(this.camera.position).normalize(),o=this.screenPointOf(a);if(!o)continue;const l=new yo(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 An,i=new R,n=this.decorObjects.filter(a=>a.track?.length||a.kind==="aircraft"||a.kind==="vehicle").map(a=>this.decorGroups.get(a.id)),s=this.bodySystem.reflectors.map(a=>a.holder);for(const a of[...n,...s]){if(!a?.visible||(t.setFromObject(a),t.isEmpty())||t.distanceToPoint(this.camera.position)<Xt.MAP_SUBJECT_NEAREST_M)continue;let o=1/0,l=-1/0,c=1/0,h=-1/0;for(const d of[t.min.x,t.max.x])for(const u of[t.min.y,t.max.y])for(const f of[t.min.z,t.max.z]){i.set(d,u,f).sub(this.camera.position).normalize();const p=this.screenPointOf(i);if(!p)continue;const v=(p.ndcX+1)/2,m=(1-p.ndcY)/2;o=Math.min(o,v),l=Math.max(l,v),c=Math.min(c,m),h=Math.max(h,m)}Number.isFinite(o)&&e.push({x:o,y:c,width:l-o,height:h-c})}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 $o(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 Sp(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 wi(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 wi.ndcFor(t,this.camera.fov,n);if(t.z>=0)return;const s=Math.tan(this.camera.fov/2*Yi);return{ndcX:t.x/-t.z/(n*s),ndcY:t.y/-t.z/s}}screenPointScratch=new R;screenPointQuaternion=new Wi;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(rg),this.camera.layers.set(rg),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 vg)this.isSunOccluded(t[0],t[1])||e++;return e/vg.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 a=this.sunOcclusionRaycaster.intersectObjects(i,!0)[0];return a!==void 0&&a.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,w=e.z+t.z*m-s.position.z;if(Math.hypot(g,w)<=this.groundRadius)return!0}}const a=this.terrainMesh;if(!a)return!1;const o=a.geometry.getAttribute("position"),l=Math.round(Math.sqrt(o.count));if(l<2||l*l!==o.count)return!1;const c=[a.position.x+o.getX(0),a.position.x+o.getX(l-1)],h=[a.position.z+o.getZ(0),a.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,f=i;for(const[m,g,w]of[[e.x,t.x,c],[e.z,t.z,h]]){const x=Math.min(w[0],w[1]),y=Math.max(w[0],w[1]);if(Math.abs(g)<1e-12){if(m<x||m>y)return!1;continue}const S=(x-m)/g,E=(y-m)/g;u=Math.max(u,Math.min(S,E)),f=Math.min(f,Math.max(S,E))}if(!(f>u))return!1;const p=Math.hypot(t.x,t.z),v=Math.min(4096,Math.max(8,Math.ceil((f-u)*p/(d/2))));for(let m=0;m<=v;m++){const g=u+(f-u)*m/v,w=e.x+t.x*g,x=e.y+t.y*g,y=e.z+t.z*g;if(x<=this.groundYUnder(w,y))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=lg;let n,s;for(const a of this.decorObjects){if(a.kind==="crop")continue;const o=this.decorGroups.get(a.id);if(!o)continue;const l=this.ufoOcclusionRaycaster.intersectObject(o,!0)[0];l&&(a.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(hg*this.camera.fov*Math.PI/180);let s,a=n;for(const o of this.namedStars){const l=o.direction.dot(i);l>a&&(a=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,lg)}pickDecorAt(e,t){this.aimAtScreenPoint(this.raycaster,e,t);const i=[...this.decorGroups.entries()],n=this.raycaster.intersectObjects(i.map(([,a])=>a),!0)[0];if(!n)return;let s=n.object;for(;s&&!i.some(([,a])=>a===s);)s=s.parent;return i.find(([,a])=>a===s)?.[0]}dispose(){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()),!e&&this.renderer.getPixelRatio()!==this.resolution.maximum&&this.applyPixelRatio(this.resolution.maximum),this.syncAnimationLoop(),(!e||this.frameDirty)&&this.render()}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 ai(hc,32,16),i=t.attributes.position,n=t.getAttribute("color")??new At(new Float32Array(i.count*3),3);for(let a=0;a<i.count;a++){const{altitudeDeg:o,azimuthDeg:l}=YE(i.getX(a),i.getY(a),i.getZ(a)),c=t_(o,l,e.azimuthDeg,e.altitudeDeg);n.setXYZ(a,c[0],c[1],c[2])}if(n.needsUpdate=!0,this.skyMesh)return;t.setAttribute("color",n);const s=new En({vertexColors:!0,side:Wt,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=cg(this.observerElevationM);if(this.groundMesh&&e===this.groundRadius)return;this.disposeMesh(this.groundMesh),this.groundRadius=e;const t=new ip(this.groundRadius,48),i=new ro({color:new se(Cd,Cd,Cd),fog:!0});Ga.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(([a,o])=>`${a}=${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=ci(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-ft.alphaAt(i,Math.abs(this.cloudLayerOffset()),s,this.cloudFieldOffset)*sA);const a=t?0:this.weather.highCloudCover??0;return a>0&&(n*=1-ft.iceAlphaAt(i,dc,a,this.cloudFieldOffset)*aA),Math.max(0,Math.min(1,n))}setBodyMesh(e,t,i,n,s){if(t.altitudeDeg<Cf){this.disposeMesh(this.bodyMeshes.get(e)),this.bodyMeshes.delete(e),this.disposeHitArea(e),this.disposeGlare(e),e==="sun"&&(this.sunVisible=!1);return}const{x:a,y:o,z:l}=ci(t.altitudeDeg,t.azimuthDeg,vn),c=Dm(t.altitudeDeg),h=this.cloudTransmission(t),d=this.arrivingIlluminance(t,s,h),u=Xt.unitLuminance(n);let f=this.bodyMeshes.get(e);if(e==="sun"){const p=Math.PI*(i/vn)**2,v=[0,1,2].map(m=>Math.min(Xt.BRIGHTEST_RELATIVE,u[m]*d[m]/p));f instanceof De||(this.disposeMesh(f),f=new De(new ai(1,16,16),new En({fog:!1})),this.celestialGroup.add(f),this.bodyMeshes.set(e,f)),f.material.color.setRGB(v[0],v[1],v[2]),f.scale.setScalar(i)}else{if(!(f instanceof Ts)){this.disposeMesh(f);const m=new pt;m.setAttribute("position",new it(new Float32Array(3),3)),m.setAttribute("color",new it(new Float32Array(3),3)),f=new Ts(m,Vt.material(qo[qo.length-1].size)),Vt.track(f),this.celestialGroup.add(f),this.bodyMeshes.set(e,f)}const p=f.geometry.getAttribute("color"),v=e.startsWith("comet:")?this.pointToPixel():1;p.setXYZ(0,u[0]*d[0]*v,u[1]*d[1]*v,u[2]*d[2]*v),p.needsUpdate=!0}if(f.position.set(a,o,l),this.setHitArea(e,a,o,l,i),e==="sun"&&(this.sunArriving=d),e.startsWith("comet:")?this.disposeGlare(e):e!=="sun"&&this.setGlare(e,a,o,l,d,i/vn*(180/Math.PI),Xt.POINT_VEIL_HOLD_DEG),e==="sun"){this.sunVisible=!0;const p=this.celestialGroup.scale.x;this.sunWorldPosition.set(a*p,o*p+this.celestialGroup.position.y,l*p),this.lensFlare||(this.lensFlare=E5(),this.lensFlare.uniforms.uOpacity.value=gA,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<Cf){this.disposeMesh(this.bodyMeshes.get(i)),this.bodyMeshes.delete(i),this.disposeHitArea(i),this.disposeGlare(i);return}const{x:n,y:s,z:a}=ci(e.altitudeDeg,e.azimuthDeg,vn),o=this.cloudTransmission(e),l=this.arrivingIlluminance(e,e.magnitude,o),c=Math.max(kr(e.phase.illuminatedFraction,0,1),.01)*Math.PI*(Co/vn)**2,h=new se(...l.map(f=>Math.min(Xt.BRIGHTEST_RELATIVE,f/c))),d=`${e.phase.phaseFraction<.5}:${Math.round(kr(e.phase.illuminatedFraction,0,1)*500)}`;let u=this.bodyMeshes.get(i);if(u instanceof yo)u.userData.phaseKey!==d&&(u.material.map?.dispose(),u.material.map=Ng(e.phase),u.userData.phaseKey=d),u.material.color.copy(h);else{this.disposeMesh(u),u=new yo(new Tc({map:Ng(e.phase),color:h,fog:!1})),u.userData.phaseKey=d;const f=Co*2;u.scale.set(f,f,1),this.celestialGroup.add(u),this.bodyMeshes.set(i,u)}u.position.set(n,s,a),this.setHitArea(i,n,s,a,Co),this.setGlare(i,n,s,a,l,Co/vn*(180/Math.PI))}bentLight(e,t,i=iA){const[n,s,a]=this.arrivingIlluminance(e,t,1),o=Math.max(s,1e-30);return{scale:s/i,tint:[n/o,1,a/o]}}static litBySky(e,t){return Xt.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,Vt.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=ut.illuminanceOf(t)*i*this.relativeScale;return[n[0]*s,n[1]*s,n[2]*s]}static unitLuminance(e){const t=Nn.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 a=this.hitAreas.get(e);if(!a){const l=new Tc({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1});a=new yo(l),this.celestialGroup.add(a),this.hitAreas.set(e,a)}a.position.set(t,i,n);const o=s*tA;a.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,a,o=0){if(Gi.radiusDeg(Gi.K*Math.max(...s))<=Math.max(a,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,a,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=Uo(e.magnitude,t),s=yd(n);this.cometKey=i,this.setBodyMesh(i,e.position,Ld*(.5+.5*n),new se(s,s,s),e.magnitude),this.cometTail||(this.cometTail=new an(vn),this.celestialGroup.add(this.cometTail.object)),this.cometTail.set(e.position,e.tailEnd,n)}buildIceHalos(e,t){this.iceHalos||(this.iceHalos=new Pi,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(),a=st.deckLitUntilDeg(st.DECK_HEIGHT_M),o=e.altitudeDeg>-a?e:t,l=this.bentLight(o,o===e?li.SUN_MAGNITUDE:t.magnitude),c=st.strength(i,s,o.altitudeDeg,a)*l.scale,{x:h,y:d,z:u}=ci(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 Fa,this.celestialGroup.add(this.rainbow.object));const i=this.weather.precipitationType==="rain"?this.weather.precipitationIntensity:0,n=e.altitudeDeg>0,s=n?e:t,a=this.lowerCloudCover(),o=this.bentLight(s,n?li.SUN_MAGNITUDE:t.magnitude,nA),l=Oi.strength(i,a,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}=ci(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 Js,this.skyGlow.onReady=()=>this.render(),this.celestialGroup.add(this.skyGlow.object));const{date:i,observer:n}=e.frame,s=ln.galactic(i,n),a=Xt.directionOf(s.centre),o=Xt.directionOf(s.rotation),l=Xt.directionOf(s.pole),c=new Xe().set(a.x,a.y,a.z,o.x,o.y,o.z,l.x,l.y,l.z),h=Xt.directionOf(ln.eclipticPole(i,n)),d=Xt.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:Xt.directionOf(e.moon),altitudeDeg:e.moon.altitudeDeg,phaseAngleDeg:We.phaseAngleOf(e.moon.phase.illuminatedFraction)},skyColor:new R(t[0],t[1],t[2])})}static directionOf(e){const{x:t,y:i,z:n}=ci(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 a=Uo(n.magnitude,t),o=yd(a);this.setBodyMesh(s,n.position,Ld*(.5+.5*a),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=Uo(n.magnitude,t),a=yd(s),o=kA[n.body]??new se(1,1,1),l=new se(o.r*a,o.g*a,o.b*a),c=Ld*(.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,a=new On(n,s),o=Hd(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:f}=a.position(i.ra[h],i.dec[h]);if(u<dg)continue;const{x:p,y:v,z:m}=ci(u,f,Rd);l.push({x:p,y:v,z:m,brightness:Uo(d,t),illuminance:this.air.transmittanceFromSpace(c,u).map(g=>g*ut.illuminanceOf(d)),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}for(const h of a5){if(h.mag>t)continue;const{altitudeDeg:d,azimuthDeg:u}=a.position(h.raHours,h.decDeg);if(d<dg)continue;const{x:f,y:p,z:v}=ci(d,u,Rd);this.namedStars.push({star:h,direction:new R(f,p,v).normalize(),altitudeDeg:d})}this.starTiers=qo.map((h,d)=>{const u=l.filter(S=>gv(S.brightness)===d),f=new Float32Array(u.length*3),p=new Float32Array(u.length),v=new Float32Array(u.length*3),m=new Float32Array(u.length),g=new Float32Array(u.length);u.forEach((S,E)=>{f[E*3]=S.x,f[E*3+1]=S.y,f[E*3+2]=S.z,p[E]=S.brightness,v.set(S.illuminance,E*3),m[E]=S.phase,g[E]=S.speedFactor});const w=new pt;w.setAttribute("position",new it(f,3));const x=new it(new Float32Array(u.length*3),3);w.setAttribute("color",x);let y=this.starTiers[d]?.points;return y?(y.geometry.dispose(),y.geometry=w):(y=new Ts(w,Vt.material(h.size)),Vt.track(y),this.celestialGroup.add(y)),{points:y,colorAttribute:x,brightness:p,illuminance:v,phase:m,speedFactor:g}}),this.updateTwinkle(0),this.startTwinkle()}twinkleSeconds=0;updateTwinkle(e){const t=this.relativeScale;for(const i of this.starTiers){const n=i.colorAttribute.array;for(let s=0;s<i.brightness.length;s++){const a=r_(i.brightness[s],{phase:i.phase[s],speedFactor:i.speedFactor[s]},e),o=t*a;n[s*3]=i.illuminance[s*3]*o,n[s*3+1]=i.illuminance[s*3+1]*o,n[s*3+2]=i.illuminance[s*3+2]*o}i.colorAttribute.needsUpdate=!0}}buildClouds(){if(this.cloudRendering==="volume"||this.weather.cloudLayers!==void 0){this.layeredClouds||(this.disposeCloudSystem(),this.layeredClouds=new Gr(this.celestialGroup,Od,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(Sa[0]+(Fd[0]-Sa[0])*t,Sa[1]+(Fd[1]-Sa[1])*t,Sa[2]+(Fd[2]-Sa[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}=tf(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 a=nf(Od);this.cloudMesh=new De(a,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}=tf(new se(...Sa),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(nf(Od*1.04),t),this.cirrusMesh.renderOrder=yg-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?Gr.deckOrder(!0,0,1):yg}cloudLayerOffset(){const t=((this.weather.cloudBaseM??1e3)-this.observerElevationM)*kd;return Math.sign(t||1)*Math.max(Math.abs(t),yA)}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,a,o]=this.cloudBeam.map(d=>d/Math.PI),l=this.lightningLevel*zd*this.relativeScale/Math.PI,c=new se(t[0]+$n.r*l,t[1]+$n.g*l,t[2]+$n.b*l);this.layeredClouds?.setLighting(n.clone(),new se(s,a,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,a*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}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=bg[e],i=t.poolSize,n=Hd(9001),s=new Float32Array(i*3),a=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),f=new Float32Array(i),p=new Float32Array(i);for(let x=0;x<i;x++){const y=n()*Math.PI*2,S=n()*t.radiusM;s[x*3]=Math.cos(y)*S,s[x*3+1]=Tr+n()*(t.topYM-Tr),s[x*3+2]=Math.sin(y)*S,a[x]=n()*Math.PI*2,o[x]=.7+n()*.6,l[x]=.6+n()*.8,c[x]=.4+n()*1.2}const v=new pt;v.setAttribute("position",new it(s,3)),v.setDrawRange(0,Bd(i,this.weather.precipitationIntensity));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:NA(e)},uNearFocusDistance:{value:Ig},uHazeDistance:{value:t.radiusM*Dg}},g=new lt({uniforms:m,vertexShader:GA,fragmentShader:VA,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const w=new Ts(v,g);w.renderOrder=Nd,this.scene.add(w),this.precipitationPoints=w,this.precipitationPositions=s,this.precipitationPhase=a,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=d,this.precipitationBounceDuration=u,this.precipitationBounceVelX=f,this.precipitationBounceVelZ=p}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=bg[i];if(this.precipitationUniforms?.uColor.value.copy(Xt.litBySky(n.color,this.baseFogColor)),t<=0)return;const s=this.weather.windDirectionDeg*Yi,a=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,f=this.precipitationBounceRemaining,p=this.precipitationBounceHeight,v=this.precipitationBounceDuration,m=this.precipitationBounceVelX,g=this.precipitationBounceVelZ,w=i==="snow"&&d&&u,x=i==="hail"&&f&&p&&v&&m&&g;for(let y=0,S=0;y<l.length;y+=3,S++){if(x&&f[S]>0){if(f[S]-=t,f[S]<=0){const b=Math.random()*Math.PI*2,T=Math.sqrt(Math.random())*n.radiusM;l[y]=Math.cos(b)*T,l[y+1]=n.topYM,l[y+2]=Math.sin(b)*T,f[S]=0}else{const b=1-f[S]/v[S];l[y+1]=Tr+Math.sin(Math.PI*b)*p[S],l[y]=pc(l[y]+(a+m[S])*t,n.radiusM),l[y+2]=pc(l[y+2]+(o+g[S])*t,n.radiusM)}continue}let E=0,I=0;if(w){const b=TA*d[S],T=IA*u[S];E=Math.sin(e*b*Math.PI*2+c[S])*T,I=Math.sin(e*b*Math.PI*2+c[S]+Math.PI/2)*T}if(l[y]+=(a+E)*t,l[y+1]-=n.fallSpeedMPerS*h[S]*t,l[y+2]+=(o+I)*t,l[y]=pc(l[y],n.radiusM),l[y+2]=pc(l[y+2],n.radiusM),l[y+1]<Tr){if(x){p[S]=Og*(.2+Math.random()*1.6),v[S]=DA*(.5+Math.random()),f[S]=v[S];const A=Math.random()*Math.PI*2,D=CA*(p[S]/Og);m[S]=Math.cos(A)*D,g[S]=Math.sin(A)*D,l[y+1]=Tr;continue}const b=Math.random()*Math.PI*2,T=Math.sqrt(Math.random())*n.radiusM;l[y]=Math.cos(b)*T,l[y+1]=n.topYM,l[y+2]=Math.sin(b)*T}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=Sg(this.weather.precipitationIntensity);this.rainSystem=M5({count:Mg,radiusM:Ro,heightM:MA,bottomYM:wg,overallSpeed:wA,color:Eg.clone(),opacity:.85,visibleCount:Bd(Mg,this.weather.precipitationIntensity),speedStreak:_A(e),pixelSize:Rg,seed:9001,nearFocusDistanceM:Ig,hazeDistanceM:Ro*Dg,hazeColor:new se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.syncRainSlant(),this.rainSystem.points.renderOrder=Nd,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=Hd(4242),t=new Float32Array(Mr*3),i=new Float32Array(Mr),n=new Float32Array(Mr),s=new Float32Array(Mr);for(let h=0;h<Mr;h++){const d=e()*Math.PI*2,u=Math.sqrt(e())*Ro;t[h*3]=Math.cos(d)*u,t[h*3+1]=wg,t[h*3+2]=Math.sin(d)*u,n[h]=kg(_g,Ag),i[h]=e()*n[h],s[h]=i[h]/n[h]}const a=new pt;a.setAttribute("position",new it(t,3));const o=new it(s,1);a.setAttribute("aLife",o),a.setDrawRange(0,Bd(Mr,this.weather.precipitationIntensity));const l=new lt({uniforms:{uMaxSize:{value:AA},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:Tg.clone()},uTexture:{value:BA()}},vertexShader:zA,fragmentShader:HA,transparent:!0,depthWrite:!1}),c=new Ts(a,l);c.renderOrder=Nd,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*Yi;e.uniforms.uDropVelocity.value.set(Math.sin(t)*this.weather.windSpeed,-Sg(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(Xt.litBySky(Eg,this.baseFogColor));const i=this.rainSplashSystem?.points.material?.uniforms?.uColor;i&&i.value.copy(Xt.litBySky(Tg,this.baseFogColor));const n=this.weather.windDirectionDeg*Yi,s=Math.sin(n)*this.weather.windSpeed,a=-Math.cos(n)*this.weather.windSpeed,o=Ro,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+a*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=Pg+(1-Pg)*(1-c),d=Lg+(1-Lg)*(1-c);t.uniforms.uUvSquash.value=d,t.uniforms.uPixelSize.value=Rg*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:a}=t,o=a.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())*Ro;i[l]=Math.cos(h)*d,i[l+2]=Math.sin(h)*d,s[c]=kg(_g,Ag),n[c]=0}o[c]=n[c]/s[c]}t.points.geometry.attributes.position.needsUpdate=!0,a.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=di.brightnessAt(l,e);c>i&&(t=l,i=c)}const n=t?Math.min(1,Math.max(.1,(LA/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*zd*this.relativeScale/Math.PI;l.color.setRGB(this.airlight[0]+$n.r*c,this.airlight[1]+$n.g*c,this.airlight[2]+$n.b*c)}const a=this.skyMesh?.material?.color;if(a){const l=s*OA;a.setRGB(1+l*$n.r,1+l*$n.g,1+l*$n.b)}if(this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor),this.lightningLight.intensity=s*zd*this.relativeScale,t){const{x:l,y:c,z:h}=ci(25,t.azimuthDeg,vn);this.lightningLight.position.set(l,c,h)}this.lightningBolt||(this.lightningBolt=new Qs(vn*.95),this.celestialGroup.add(this.lightningBolt.object));const o=this.weather.cloudBaseM??1500;this.lightningBolt.set(t,i,o),this.render()}buildCompassLabels(){const e=Jr(navigator.languages,cA),t=lA[e];this.compassSprites=oA.map((i,n)=>{const s=t[n],a=new Tc({map:WA(s),depthTest:!1,fog:!1}),o=new yo(a),{x:l,y:c,z:h}=ci(0,i,pg);return o.position.set(l,c,h),o.scale.set(fc,fc,1),o.renderOrder=uA,o.layers.set(og),o.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(o),o}),this.updateCompassScale()}updateCompassScale(){const e=this.camera.fov*Math.PI/360,t=mg*Math.PI/360,i=fc*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 Yi=Math.PI/180;function kr(r,e,t){return Math.max(e,Math.min(t,r))}function kg(r,e){return r+Math.random()*(e-r)}function pc(r,e){return(r%(e*2)+e*3)%(e*2)-e}let mc,gc;function NA(r){switch(r){case"snow":return FA();case"hail":return UA()}}function FA(){if(mc)return mc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),mc=new Us(e),mc}function UA(){if(gc)return gc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),gc=new Us(e),gc}let vc;function BA(){if(vc)return vc;const r=64,e=document.createElement("canvas");e.width=r,e.height=r;const t=e.getContext("2d"),i=t.createRadialGradient(r/2,r/2,0,r/2,r/2,r/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,r,r),vc=new Us(e),vc}const zA=`
|
|
8519
8518
|
uniform float uMaxSize;
|
|
8520
8519
|
uniform float uScale;
|
|
8521
8520
|
attribute float aLife;
|