@rr0/ufoathome 0.75.1 → 0.75.2
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.
|
@@ -2197,7 +2197,7 @@ input[type=range] {
|
|
|
2197
2197
|
}
|
|
2198
2198
|
`;class Za{constructor(e){this.points=e}static STATIONARY_M=5;static of(e){const t=[];for(const i of e.observerTrack.allKeyframes){const{lat:n,lng:s,headingDeg:r}=i.pose;n===void 0||s===void 0||t.push({t:i.t,lat:n,lng:s,headingDeg:r})}if(t.length===0){const i=e.event.place?.[0];if(!i)return;t.push({t:0,lat:i.lat,lng:i.lng})}return new Za(t)}static TRAVEL_M=50;get travels(){return this.spanM>Za.TRAVEL_M}get moved(){return this.spanM>Za.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(hi(t,e.east,t,e.west).x),Math.abs(hi(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:ks(0,-s,i,n).lat,south:ks(0,s,i,n).lat,east:ks(s,0,i,n).lng,west:ks(-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 En{constructor(e,t){this.rotationPassed=e,this.scale=t}static SWAY_M=.012;static BREATH_M=.003;static DRIFT_YAW_DEG=.5;static DRIFT_PITCH_DEG=.4;static DRIFT_ROLL_DEG=.3;static GAZE_DRIFT_SHARE=.35;static HAND_HELD_S=.5;static HZ=[.11,.23,.17,.29,.37,.07];static of(e){const t=e.event.tags?.includes("paralysis")||(e.exposure??0)>En.HAND_HELD_S,i=e.sway??(t?0:1);if(i>0)return new En(Math.max(1-(e.instrument.stabilization??0),En.GAZE_DRIFT_SHARE),i)}offsetAt(e){const t=e/1e3,i=this.scale,n=(d,u)=>Math.sin(2*Math.PI*d*t+u),[s,r,o,l,c,h]=En.HZ;return{eastM:i*En.SWAY_M*(.7*n(s,.3)+.3*n(l,1.9)),northM:i*En.SWAY_M*(.6*n(o,2.2)+.4*n(c,.8)),upM:i*En.BREATH_M*n(r,0),yawDeg:i*this.rotationPassed*En.DRIFT_YAW_DEG*(.6*n(h,1.1)+.4*n(l,2.7)),pitchDeg:i*this.rotationPassed*En.DRIFT_PITCH_DEG*(.5*n(r,1.6)+.5*n(o,.4)),rollDeg:i*this.rotationPassed*En.DRIFT_ROLL_DEG*n(s,2.9)}}}class _t{constructor(e,t){this.stretches=e,this.rotationPassed=t}static REFERENCE_SPEED_M_PER_S=1.4;static REFERENCE_STEP_HZ=1.9;static CADENCE_EXPONENT=.43;static RISE_M_PER_M_PER_S=.0175;static SWAY_M=.0175;static SLOWEST_WALK_M_PER_S=.2;static FASTEST_WALK_M_PER_S=2.2;static HEAD_ROLL_DEG=1.5;static HEAD_PITCH_DEG=1.25;static HEAD_YAW_DEG=1;static STOP_LURCH_M=.035;static STOP_PITCH_DEG=3;static START_LURCH_M=.015;static START_PITCH_DEG=1.2;static JOLT_HZ=1.4;static JOLT_DECAY_S=.35;static JOLT_LASTS_MS=1500;static STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(e){const t=Za.of(e);if(!t||t.points.length<2)return;const i=[];let n=0;for(let s=1;s<t.points.length;s++){const r=t.points[s-1],o=t.points[s],l=(o.t-r.t)/1e3;if(l<=0)continue;const c=hi(o.lat,o.lng,r.lat,r.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),f=u/l,p=_t.stepHzFor(f);i.push({startMs:r.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 r=i[s-1],o=i[s+1];i[s].rampsIn=r===void 0||r.stepHz===0,i[s].rampsOut=o===void 0||o.stepHz===0}if(i.some(s=>s.stepHz>0))return new _t(i,1-(e.instrument.stabilization??0))}static bodyAt(e,t,i=!1){const n=_t.of(e)?.offsetAt(t)??_t.STILL;return i?n:_t.add(n,En.of(e)?.offsetAt(t))}static stepHzFor(e){return e<_t.SLOWEST_WALK_M_PER_S||e>_t.FASTEST_WALK_M_PER_S?0:_t.REFERENCE_STEP_HZ*Math.pow(e/_t.REFERENCE_SPEED_M_PER_S,_t.CADENCE_EXPONENT)}offsetAt(e){return _t.add(this.cycleAt(e),this.joltAt(e))}joltAt(e){for(const t of this.stretches){if(t.stepHz===0)continue;const i=t.speedMPerS/_t.REFERENCE_SPEED_M_PER_S,n=(o,l,c,h)=>{if(o<0||o>_t.JOLT_LASTS_MS)return;const d=o/1e3,u=Math.exp(-d/_t.JOLT_DECAY_S)*Math.sin(2*Math.PI*_t.JOLT_HZ*d),f=h*l*i*u;return{eastM:f*t.eastUnit,northM:f*t.northUnit,upM:-Math.abs(f)*.3,rollDeg:0,pitchDeg:-h*c*i*u*this.rotationPassed,yawDeg:0}},s=t.rampsOut?n(e-t.endMs,_t.STOP_LURCH_M,_t.STOP_PITCH_DEG,1):void 0;if(s)return s;const r=t.rampsIn?n(e-t.startMs,_t.START_LURCH_M,_t.START_PITCH_DEG,-1):void 0;if(r)return r}}static add(e,t){return t?{eastM:e.eastM+t.eastM,northM:e.northM+t.northM,upM:e.upM+t.upM,rollDeg:e.rollDeg+t.rollDeg,pitchDeg:e.pitchDeg+t.pitchDeg,yawDeg:e.yawDeg+t.yawDeg}:e}cycleAt(e){const t=this.stretches.find(l=>e>=l.startMs&&e<=l.endMs);if(!t||t.stepHz===0)return _t.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),r=_t.SWAY_M*s*Math.cos(n/2),o=s*this.rotationPassed*t.speedMPerS/_t.REFERENCE_SPEED_M_PER_S;return{eastM:r*t.northUnit,northM:-r*t.eastUnit,upM:_t.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:_t.HEAD_ROLL_DEG*o*Math.cos(n/2),pitchDeg:-1.25*o*Math.cos(n),yawDeg:_t.HEAD_YAW_DEG*o*Math.cos(n/2)}}amplitudeAt(e,t){const i=1e3/e.stepHz,n=Math.min(i,(e.endMs-e.startMs)/2);if(n<=0)return 1;const s=t-e.startMs,r=e.endMs-t,o=Math.min(e.rampsIn?s/n:1,e.rampsOut?r/n:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class Ry{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 w0=.05,x0=.08,S0=2,E0=3,Cy=14,Py=.02;class Ly{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,w0);for(const{param:s,ratio:r}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*r,i.sampleRate/2-1),n,w0)}buildSynthesizedVoice(e,t){const i=this.context;if(i)switch(e.kind){case"hum":return this.buildHum(i,t);case"whistle":return this.buildWhistle(i,t);case"rumble":return this.buildRumble(i,t);case"crackle":return this.buildCrackle(i,t);default:return}}buildHum(e,t){const i=e.createOscillator();i.type="sawtooth";const n=e.createOscillator();n.type="sawtooth";const s=e.createBiquadFilter();s.type="lowpass",s.Q.value=1;const r=e.createGain();return r.gain.value=0,i.connect(s),n.connect(s),s.connect(r).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.16,pitch:[{param:i.frequency,ratio:1},{param:n.frequency,ratio:1.006},{param:s.frequency,ratio:6}]}}buildWhistle(e,t){const i=e.createOscillator();i.type="sine";const n=e.createOscillator();n.type="sine",n.frequency.value=5;const s=e.createGain(),r=e.createGain();return r.gain.value=0,n.connect(s).connect(i.frequency),i.connect(r).connect(e.destination),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.22,pitch:[{param:i.frequency,ratio:1},{param:s.gain,ratio:.012}]}}buildRumble(e,t){const i=e.createBufferSource();i.buffer=this.brownNoise(e),i.loop=!0;const n=e.createBiquadFilter();n.type="lowpass",n.Q.value=.8;const s=e.createGain();return s.gain.value=0,i.connect(n).connect(s).connect(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 r=e.createBufferSource();r.buffer=this.crackleEnvelope(e),r.loop=!0;const o=e.createGain();return o.gain.value=0,r.connect(o).connect(s.gain),i.connect(n).connect(s).connect(e.destination),i.start(),r.start(),{key:t,nodes:[n,s,o],sources:[i,r],volumeParam:o.gain,gainScale:.9,pitch:[{param:n.frequency,ratio:4}]}}async buildRecordedVoice(e,t,i){const n=this.context;if(!n)return;let s;try{s=await this.getBuffer(e)}catch(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(i!==this.voiceToken||!this.context)return;const r=n.createBufferSource();r.buffer=s,r.loop=!0;const o=n.createGain();o.gain.value=0,r.connect(o).connect(n.destination),r.start(),this.voice={key:t,nodes:[o],sources:[r],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let i=this.buffers.get(e);return i||(i=fetch(e).then(n=>{if(!n.ok)throw new Error(`Audio fetch failed (${n.status}): ${e}`);return n.arrayBuffer()}).then(n=>t.decodeAudioData(n)),this.buffers.set(e,i)),i}whiteNoise(e){if(!this.noiseBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*S0),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*S0),e.sampleRate),i=t.getChannelData(0);let n=0,s=0;for(let r=0;r<i.length;r++)n=(n+.02*(Math.random()*2-1))/1.02,i[r]=n,s=Math.max(s,Math.abs(n));if(s>0)for(let r=0;r<i.length;r++)i[r]/=s;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*E0),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(Py*e.sampleRate)),s=Math.round(E0*Cy);for(let r=0;r<s;r++){const o=Math.floor(Math.random()*i.length),l=.4+Math.random()*.6;for(let c=0;c<n&&o+c<i.length;c++)i[o+c]=Math.max(i[o+c],l*(1-c/n))}this.crackleBuffer=t}return this.crackleBuffer}stopVoice(){const e=this.voice,t=this.context;if(this.voice=void 0,!e||!t)return;const i=t.currentTime;e.volumeParam.cancelScheduledValues(i),e.volumeParam.setTargetAtTime(0,i,x0/3);const n=i+x0;for(const s of e.sources){s.onended=()=>{s.disconnect();for(const r of e.nodes)r.disconnect()};try{s.stop(n)}catch{s.disconnect();for(const r of e.nodes)r.disconnect()}}}}function co(a){return[...a].sort((e,t)=>e.t-t.t)}function _0(a,e){let t;for(const i of co(a)){if(i.t>e)break;t=i}return t}const Zh=Math.PI/180,Fl=180/Math.PI;class Gn{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*Zh/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new Gn(e.projection,t,i)}degToPx(e){const t=e*Zh;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*Fl:2*Math.atan(e/(2*this.focalPx))*Fl}angleDegToRadiusPx(e){const t=e*Zh;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*Fl:Math.atan(e/this.focalPx)*Fl}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(Je.angularWidthDeg(e))}}class yi{static fovOf(e,t){return nt(e,t)?.fovDeg??wt.fieldOfViewDeg(e.instrument)}static toAngular(e){this.eachKeyframe(e,(t,i)=>({...t,angular:i.ofBounds(t.bounds)}))}static toBounds(e){this.eachKeyframe(e,(t,i)=>{if(!t.angular)return t;const{width:n,height:s}=i.toBoundsSize(t.angular),r={x:t.bounds.x+(t.bounds.width-n)/2,y:t.bounds.y+(t.bounds.height-s)/2,width:n,height:s};if(t.kind!=="polygon")return{...t,bounds:r};const o=t.bounds.width===0?1:n/t.bounds.width,l=t.bounds.height===0?1:s/t.bounds.height;return{...t,bounds:r,points:t.points.map(c=>({x:c.x*o,y:c.y*l}))}})}static toAim(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);if(s&&Math.abs(s.x-t.bounds.x)<yi.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<yi.UNMOVED_PX)return t;const r=this.aimFrom(e,i,n,t.bounds);return r?{...t,aim:r}:t})}static UNMOVED_PX=.01;static aimOf(e,t,i){const n=Gn.of(e.instrument,Je.CANVAS_HEIGHT_PX,this.fovOf(e,t));return this.aimFrom(e,n,nt(e,t),i)}static aimFrom(e,t,i,n){if(i?.headingDeg===void 0)return;const s=this.frameCentre(e),r=t.radiusPxToAngleDeg(n.x+n.width/2-s.x),o=t.radiusPxToAngleDeg(s.y-(n.y+n.height/2));return{azimuthDeg:((i.headingDeg+r)%360+360)%360,altitudeDeg:i.pitchDeg+o}}static toPosition(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);return s?{...t,bounds:{...t.bounds,...s}}:t})}static positionOf(e,t,i,n,s){if(s?.headingDeg===void 0)return;const r=this.frameCentre(e),o=this.shortestArc(t.azimuthDeg-s.headingDeg),l=t.altitudeDeg-s.pitchDeg;return{x:r.x+this.offAxisPx(n,o)-i.width/2,y:r.y-this.offAxisPx(n,l)-i.height/2}}static frameCentre(e){return{x:wt.frameWidthPx(e.instrument,Je.CANVAS_HEIGHT_PX)/2,y:Je.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=yi.OFF_CANVAS_DEG?Math.sign(t)*yi.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=wt.frameWidthPx(t,Je.CANVAS_HEIGHT_PX)/2,s=wt.frameWidthPx(e.instrument,Je.CANVAS_HEIGHT_PX)/2,r=Je.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=Gn.of(t,Je.CANVAS_HEIGHT_PX,c),d=Gn.of(e.instrument,Je.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-r,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:r+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=nt(e,n.t),r=this.fovOf(e,n.t),o=Gn.of(e.instrument,Je.CANVAS_HEIGHT_PX,r);i.addKeyframe(n.t,n.shapes.map(l=>({...l,shape:t(l.shape,o,s)})))}}}function Oy(a){return gn.restore(Tu(a),a.provenance)}function Tu(a){return yi.toAngular(a),yi.toAim(a),{version:1,id:a.id,time:a.event.time,endTime:a.event.endTime,durationSeconds:a.event.durationSeconds,utcOffsetHours:a.event.utcOffsetHours,timeZone:a.event.timeZone,place:a.event.place,observer:a.observer,account:a.account,sources:a.sources?.length?a.sources:void 0,sway:a.sway,vehicle:a.vehicle,description:a.event.description,tags:a.event.tags,timeline:a.timeline.toJSON(),observerTrack:a.observerTrack.toJSON(),weatherTrack:a.weatherTrack.toJSON(),soundTrack:a.soundTrack.toJSON(),weather:a.weather,decor:a.decor,milestones:a.milestones.length>0?a.milestones:void 0,references:a.references.length>0?a.references:void 0,roads:a.roads.length>0?a.roads:void 0,weatherSource:a.weatherSource,instrument:a.instrumentId,exposureSeconds:a.exposureSeconds,iso:a.iso,lightPollution:a.lightPollution,interpretation:a.interpretation}}function ky(a){const{recording:e,provenance:t}=gn.strip(a),i=Ny(e);return i.provenance=t,i}function Ny(a){const e=[],t=new Dh({eventType:"sighting",time:a.time,endTime:a.endTime,durationSeconds:a.durationSeconds,utcOffsetHours:a.utcOffsetHours,timeZone:a.timeZone,place:a.place,description:a.description,tags:a.tags},Ih.fromJSON(a.timeline,e),a.observerTrack?ul.fromJSON(a.observerTrack):new ul,a.weatherTrack?fl.fromJSON(a.weatherTrack):new fl,a.soundTrack?pl.fromJSON(a.soundTrack):new pl,a.observer&&{id:a.observer.id,title:a.observer.title,lastName:a.observer.lastName,firstNames:a.observer.firstNames},a.id,a.weather,a.decor??[],a.weatherSource,a.instrument,a.exposureSeconds??a.observerTrack?.keyframes.map(i=>i.pose.exposureSeconds).find(i=>i!==void 0),co(a.milestones??[]),a.references??[],a.roads??[]);return t.account=a.account,t.sources=a.sources,t.sway=a.sway,t.iso=a.iso,t.lightPollution=a.lightPollution,t.vehicle=a.vehicle,t.interpretation=a.interpretation,t.loadIssues=e,yi.toBounds(t),yi.toPosition(t),t}class ho{static EN={"unknown-key":"{path}: unknown key, ignored","wrong-type":"{path}: expected {expected}, found {found}","not-in-list":"{path}: {found} is not one of {expected}",defaulted:"{path}: missing, {expected} used",unplaced:"{path}: no aim (and no bounds), drawn at the middle of the view",unsized:"{path}: no angular size (and no bounds), drawn one pixel wide","pixels-over-angles":"{path}: bounds restated without aim or angular, so the shape follows the pixels"};static text(e,t=ho.EN){return t[e.kind].replace("{path}",e.path).replace("{expected}",e.expected??"").replace("{found}",e.found??"")}static warn(e,t){if(e.length===0)return;const i=e.map(n=>` ${ho.text(n)}`);console.warn(`UFO@home: ${e.length} problem(s) in ${t??"this recording"}:
|
|
2199
2199
|
${i.join(`
|
|
2200
|
-
`)}`)}}class On{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!On.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:On.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const o=i?`${i}.${s}`:s,l=t[s];l?this.field(r,l,o,n):n.push({kind:"unknown-key",path:o})}}field(e,t,i,n){if(On.isStated(e)){this.field(e.value,t,i,n);return}if(!(t.loose||e===void 0||e===null))switch(t.type){case"enum":(typeof e!="string"||!t.values?.includes(e))&&n.push({kind:"not-in-list",path:i,expected:(t.values??[]).map(s=>`"${s}"`).join(", "),found:JSON.stringify(e)});return;case"object":t.fields?this.object(e,t.fields,i,n):On.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:On.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const o=On.isStated(s)?s.value:s;this.object(o,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:On.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:On.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return On.isObject(e)&&"value"in e&&Object.keys(e).every(t=>t==="value"||t==="basis"||t==="rationale")}static typeOf(e){return Array.isArray(e)?"array":e===null?"null":typeof e}}class Un{static PIXELS_PER_INSTANT=2;static INSTANTS_PER_FLASH=2;static MAX_INSTANTS=512;static windowEndingAt(e,t){const i=Number.isFinite(t)?Math.max(0,t):0,n=Math.max(0,e-i*1e3),s=Math.max(0,e-n);return{fromMs:n,toMs:e,ms:s,seconds:s/1e3}}static instants(e,t,i,n,s){if(!(n>0))return 1;const r=s>0?Un.travelDegOver(e,t,i,i+n*1e3)/s/Un.PIXELS_PER_INSTANT:0,o=Un.flashesOver(e,n)*Un.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(r,o));return l<2?1:Math.min(Un.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const o=Un.directionAt(r,t,i),l=Un.directionAt(r,t,n),c=Math.min(1,Math.max(-1,o.x*l.x+o.y*l.y+o.z*l.z));s=Math.max(s,Math.acos(c)*180/Math.PI)}return s}static flashesOver(e,t){let i=0;for(const n of e)for(const s of n.lights??[])s.pattern.kind==="flash"&&(i=Math.max(i,s.pattern.perMinute));return i/60*t}static directionAt(e,t,i){const n=vr(e,i),s=n.eastM,r=n.altitudeM-t,o=n.northM,l=Math.hypot(s,r,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:r/l,z:o/l}}}function nh(a,e){for(const t of a){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class Jr{static preferencesFor(e){const t=navigator.languages??[],i=this.declaredFor(e);return i?[i,...t]:t}static declaredFor(e){for(let t=e;t;){const i=t.closest("[lang]")?.getAttribute("lang")?.trim();if(i)return i;const n=t.getRootNode();t=n instanceof ShadowRoot?n.host:void 0}}}class fs{constructor(e){this.preferences=e}read(e){if(e===void 0||typeof e=="string")return e===""?void 0:e;for(const i of this.preferences){const n=e[i]??e[fs.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>fs.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(fs.base(i)))return n;return Object.values(e).find(i=>i)??void 0}write(e,t,i){const n=t?.trim()?t.trim():void 0;if(e===void 0||typeof e=="string")return n;const s={...e},r=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>fs.base(o)===fs.base(i))??i;return n===void 0?delete s[r]:s[r]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const Uv={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",showObserverMap:"Show where the observer was",hideObserverMap:"Hide where the observer was",mapImageryUnavailable:"Aerial imagery unavailable",zoomMapIn:"Zoom in",zoomMapOut:"Zoom out",fitMap:"Back to the whole path",placeObserverHere:"Put the observer here",showReferences:"Show the pictures of the place",hideReferences:"Hide the pictures of the place",referenceOpacity:"Picture opacity",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",observerHere:"The observer, here",decorHere:"Scenery",recordingIssues:"{count} problem(s) in this recording: it may not play as its author meant",issueTemplates:ho.EN},Fy=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Uv},Symbol.toStringTag,{value:"Module"})),Bv=Math.PI/180,Uy=180/Math.PI,By=256,zv=19,zy=1.25;function Hv(a,e){return 156543.03392*Math.cos(a*Bv)/2**e}function Gv(a,e){return Hv(a,e)*By}function Hy(a,e){let t=0;for(;t<zv&&Gv(a,t+1)>=e;)t++;return t}function Vv(a,e){return Gv(a,e)}function Gy(a,e){const t=e*zy;let i=0;for(;i<zv&&Hv(a,i)>t;)i++;return i}function Fc(a){const e=a*Bv;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function Wv(a,e,t){const i=2**t;return{x:(a+180)/360*i,y:Fc(e)*i}}function Xv(a,e,t){const i=Fc(a.north),n=Fc(a.south);return{x:(e-a.west)/(a.east-a.west),y:(Fc(t)-i)/(n-i)}}function Vy(a,e,t){const{x:i,y:n}=Wv(a,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function Wy(a){const e=2**a.z,t=a.x/e*360-180,i=(a.x+1)/e*360-180,n=A0(a.y,e);return{south:A0(a.y+1,e),north:n,west:t,east:i}}function A0(a,e){const t=a/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*Uy}function Yv(a,e,t,i){const n=Vy(a,e,t),s=Math.floor(i/2),r=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)r.push({x:n.x+h,y:n.y+c,z:t});const o=r.map(Wy),l={south:Math.min(...o.map(c=>c.south)),north:Math.max(...o.map(c=>c.north)),west:Math.min(...o.map(c=>c.west)),east:Math.max(...o.map(c=>c.east))};return{tiles:r,bounds:l}}class Fr{constructor(e){this.ctx=e}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(e,t){let i,n=1/0;for(const s of this.targets){const r=Math.hypot(s.x-e,s.y-t);r>s.radius||r>n||(i=s,n=r)}return i}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(e){const{ctx:t}=this;t.save(),this.targets=[],t.clearRect(0,0,this.width,this.height),this.paintGround(e),this.paintNight(e.nightFraction),this.paintPath(e),e.position?(this.paintCone(e,e.position),this.paintDecor(e),this.paintMarkers(e),this.paintObserver(e,e.position)):(this.paintDecor(e),this.paintMarkers(e)),this.paintNorth(),this.paintFooter(e.bounds,e.attribution),t.restore()}toCanvas(e,t,i){const n=Xv(e,i,t);return{x:n.x*this.width,y:n.y*this.height}}paintGround(e){const{ctx:t}=this;t.fillStyle="#1d2321",t.fillRect(0,0,this.width,this.height);const i=e.imagery;if(!i)return;const n=this.toCanvas(e.bounds,i.bounds.north,i.bounds.west),s=this.toCanvas(e.bounds,i.bounds.south,i.bounds.east);t.drawImage(i.source,n.x,n.y,s.x-n.x,s.y-n.y)}paintNight(e=0){if(e<=0)return;const{ctx:t}=this;t.save(),t.fillStyle=`rgba(6, 12, 30, ${Math.min(e,1)*.78})`,t.fillRect(0,0,this.width,this.height),t.restore()}paintDecor(e){const{ctx:t}=this;for(const i of e.decor){const n=this.toCanvas(e.bounds,i.lat,i.lng);if(i.label&&this.targets.push({kind:"decor",label:i.label,lat:i.lat,lng:i.lng,x:n.x,y:n.y,radius:7}),t.beginPath(),t.rect(n.x-3.5,n.y-3.5,7,7),t.fillStyle="rgba(120, 220, 255, 0.85)",t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.7)",t.stroke(),i.headingDeg===void 0)continue;const s=(i.headingDeg-90)*Math.PI/180;t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(n.x+Math.cos(s)*10,n.y+Math.sin(s)*10),t.lineWidth=3,t.strokeStyle="rgba(0, 0, 0, 0.6)",t.stroke(),t.lineWidth=1.5,t.strokeStyle="rgba(120, 220, 255, 0.9)",t.stroke()}}paintPath(e){if(!e.path.moved)return;const{ctx:t}=this,i=e.path.points.map(n=>this.toCanvas(e.bounds,n.lat,n.lng));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(const n of i.slice(1))t.lineTo(n.x,n.y);t.lineJoin="round",t.lineCap="round",t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=5,t.stroke(),t.strokeStyle="rgba(255, 255, 255, 0.9)",t.lineWidth=2,t.stroke()}paintCone(e,t){const{headingDeg:i,coneHalfAngleDeg:n}=e;if(i===void 0||n===void 0)return;const{ctx:s}=this,r=this.toCanvas(e.bounds,t.lat,t.lng),o=Math.hypot(this.width,this.height),l=(i-90)*Math.PI/180,c=n*Math.PI/180;s.save(),s.beginPath(),s.moveTo(r.x,r.y),s.arc(r.x,r.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(r.x,r.y,0,r.x,r.y,o);h.addColorStop(0,"rgba(255, 224, 130, 0.55)"),h.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),h.addColorStop(1,"rgba(255, 224, 130, 0)"),s.fillStyle=h,s.fill(),s.restore()}paintMarkers(e){const{ctx:t}=this;t.textAlign="center",t.textBaseline="middle",t.font="bold 10px sans-serif";for(const i of[...e.markers].sort((n,s)=>Number(n.current)-Number(s.current))){const n=this.toCanvas(e.bounds,i.lat,i.lng);this.targets.push({kind:"milestone",label:i.note?`${i.label} — ${i.note}`:i.label,lat:i.lat,lng:i.lng,t:i.t,x:n.x,y:n.y,radius:Fr.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Fr.MARKER_RADIUS_PX,0,Math.PI*2),t.fillStyle=i.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",t.fill(),t.lineWidth=2,t.strokeStyle="rgba(255, 255, 255, 0.9)",t.stroke(),t.fillStyle=i.current?"#1d2321":"#fff",t.fillText(i.label,n.x,n.y)}}paintObserver(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.observerLabel&&this.targets.push({kind:"observer",label:e.observerLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(r=>{if(!r.current)return!1;const o=hi(r.lat,r.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Fr.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Fr.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 r=(e.south+e.north)/2,l=Math.abs(hi(r,e.east,r,e.west).x)/this.width,c=Fr.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 r=n?`${n} ${s}`:s;n&&this.ctx.measureText(r).width>t?(i.push(n),n=s):n=r}return n&&i.push(n),i}}class Zt{static MIN_ZOOM=.25;static MAX_ZOOM=64;zoom=1;centerX=.5;centerY=.5;get changed(){return this.zoom!==1||this.centerX!==.5||this.centerY!==.5}get zoomFactor(){return this.zoom}reset(){this.zoom=1,this.centerX=.5,this.centerY=.5}zoomAt(e,t,i){const n=Math.min(Zt.MAX_ZOOM,Math.max(Zt.MIN_ZOOM,this.zoom*i)),s=this.centerX+(e-.5)/this.zoom,r=this.centerY+(t-.5)/this.zoom;this.centerX=s-(e-.5)/n,this.centerY=r-(t-.5)/n,this.zoom=n}panBy(e,t){this.centerX-=e/this.zoom,this.centerY-=t/this.zoom}refit(e,t){if(!this.changed)return;const i=this.boundsWithin(e),n=Zt.mercatorY01(t.north),s=Zt.mercatorY01(t.south),r=Zt.mercatorY01(i.north),o=Zt.mercatorY01(i.south);this.zoom=(t.east-t.west)/(i.east-i.west),this.centerX=((i.west+i.east)/2-t.west)/(t.east-t.west),this.centerY=((r+o)/2-n)/(s-n)}keepInView(e,t,i){if(!this.changed)return!1;const n=this.boundsWithin(e),s=Zt.mercatorY01(n.north),r=Zt.mercatorY01(n.south),o=(t.lng-n.west)/(n.east-n.west),l=(Zt.mercatorY01(t.lat)-s)/(r-s);return o>=i&&o<=1-i&&l>=i&&l<=1-i?!1:(this.panBy(.5-o,.5-l),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=Zt.mercatorY01(e.north),n=Zt.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:Zt.latitudeAt(i+(this.centerY-t)*(n-i)),south:Zt.latitudeAt(i+(this.centerY+t)*(n-i))}}static expand(e,t){const i=e.east-e.west,n=e.north-e.south;return{west:e.west-i*t,east:e.east+i*t,south:e.south-n*t,north:e.north+n*t}}static pointAt(e,t,i){const n=Zt.mercatorY01(e.north),s=Zt.mercatorY01(e.south);return{lat:Zt.latitudeAt(n+i*(s-n)),lng:e.west+t*(e.east-e.west)}}static mercatorY01(e){const t=e*Math.PI/180;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}static latitudeAt(e){return Math.atan(Math.sinh(Math.PI*(1-2*e)))*180/Math.PI}}const xn=256,T0=3,Xy=32768;class qv{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(hi(i,e.east,i,e.west).x),r=Math.abs(hi(e.north,n,e.south,n).z),o=Hy(i,Math.max(s,r)),{tiles:l}=Yv(n,i,o,T0),c=T0*xn,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)),r=await this.fetchImpl(s);if(!r.ok)throw new Error(`Elevation tile fetch failed (${r.status}): ${s}`);const o=await createImageBitmap(await r.blob()),l=document.createElement("canvas");l.width=xn,l.height=xn;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,xn,xn),d=(e.x-t.x)*xn,u=(e.y-t.y)*xn;for(let f=0;f<xn;f++)for(let p=0;p<xn;p++){const v=(f*xn+p)*4,m=h[v]*256+h[v+1]+h[v+2]/256-Xy,g=d+p,w=u+f;i[w*n+g]=m}}sampleAt(e,t,i,n,s,r){const{x:o,y:l}=Wv(t,e,i),c=(o-n.x)*xn,h=(l-n.y)*xn,d=Math.max(0,Math.min(r-1,Math.floor(c))),u=Math.max(0,Math.min(r-1,Math.floor(h))),f=Math.min(r-1,d+1),p=Math.min(r-1,u+1),v=c-d,m=h-u,g=s[u*r+d],w=s[u*r+f],y=s[p*r+d],b=s[p*r+f],x=g+(w-g)*v,E=y+(b-y)*v;return x+(E-x)*m}}const Uc=256,Yy=5;async function $v(a,e,t,i){const n=(a.south+a.north)/2,s=(a.west+a.east)/2,r=Math.abs(hi(n,a.east,n,a.west).x),o=Math.abs(hi(a.north,s,a.south,s).z),l=Math.max(r,o),{zoom:c,gridSize:h}=qy(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=Yv(s,n,c,h),f=h*Uc,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 b=>{const x=t(b),E=await i(x);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${x}`);const I=await createImageBitmap(await E.blob());v.drawImage(I,(b.x-m.x)*Uc,(b.y-m.y)*Uc)}));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 y=w.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(p,0,0,f,f,0,0,g,g),{source:w,width:g,height:g,bounds:u}}function qy(a,e,t){let i=Gy(a,e/Math.max(1,t)),n=I0(a,i,e);for(;n>Yy&&i>0;)i--,n=I0(a,i,e);return{zoom:i,gridSize:n,mPerPixel:Vv(a,i)/Uc}}function I0(a,e,t){const i=Math.ceil(t/2/Vv(a,e));return Math.max(3,2*i+1)}class Kv{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 $v(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class kn{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??kn.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"~"^(${kn.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:kn.surfaceOf(s),widthM:kn.widthOf(s),points:n.map(r=>({lat:r.lat,lng:r.lon}))})}return t}queued(e){const t=kn.queue.then(e,e);return kn.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,kn.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:kn.WIDTH_M[e.highway]??kn.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 jh="https://ufoathome.org/roads/index.json";class ap{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??ap.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.75.1")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==jh?[t,jh]:[jh]).map(i=>i+e)}}function $y(){return{elevation:new qv,imagery:Zv(),roads:Ky()}}function Zv(){return new Kv}function Ky(){return new ap({fallback:new kn})}const Zy=new Set,Qr="show-observer-map",ja="hide-milestones";class gi extends HTMLElement{static get observedAttributes(){return["src",Qr,ja]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;fullscreenButton;observerMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new Zt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Dh.create();issues=[];issueCheck=0;sightingAudio=new Ly;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Uv;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeObserverMap()};observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<gi.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=Zt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Zt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},gi.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=Zt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Dy}</style>${Iy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new ml(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",t=>{t.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Fr(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");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.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.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.seekInput.addEventListener("keydown",this.handleSeekKey),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.observerMapResizeObserver?.observe(this.observerMapPanel),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.observerMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===Qr&&this.applyObserverMapDefault(),e===ja&&this.setMilestonesShown(!this.hasAttribute(ja))}async loadFromSrc(e){this.sightingData=await np.json(e)}get sightingData(){return Oy(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=ky(e),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await mt(async()=>{const{default:r}=await import("./sightingSchema-DOOSOVWK.js");return{default:r}},[],import.meta.url);n=new On(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],ho.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(jy,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=ho.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referenceOpacityInput,this.referencesButton,this.observerMapButton];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?co(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),o=r?`${s} — ${r}`:s;return n.title=o,n.setAttribute("aria-label",o),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n}))}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=gi.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const o=Number(r.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?_0(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=Un.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<gi.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(gi.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/gi.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/gi.EXPOSURE_STEP_PX),s=Math.min(gi.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let o=0;o<s;o++)r.push(t.fromMs+t.ms*o/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,o]of i){const l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),h=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=wt.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:Zy;this.canvasRenderer.setStarPoints(wt.starPointsOf(this.sighting.instrument));const n=(nt(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,l]of o.shapes){const c=o.share,h=c===1?l:{...l,transparency:1-(1-l.transparency)*c};this.canvasRenderer.paintShape(this.shifted(h,r))}for(const[o,l]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...l,selected:!0}):this.canvasRenderer.paintMemberOutline(l):this.canvasRenderer.paintSelectionOnly(this.shifted(l,r)));if(this.selectionShown&&i.size>1){const o=vt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds));this.canvasRenderer.paintGroupHandles(o)}this.overlayPainter?.(this.canvasRenderer),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(l=>l.transparency<1).map(l=>this.shifted(l,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(ip(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 Ry(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*gi.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton(),this.flashPlayback())}flashPlayback(){const e=this.playbackFlash;e.textContent=this.player.playbackState==="playing"?"▶":"⏸",e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}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()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear()}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(Qr),t=e===null?this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const t=!this.observerMapPanel.hidden?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=Za.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.observerPath.boundsAround(gi.OBSERVER_MAP_MIN_SPAN_M,gi.OBSERVER_MAP_MARGIN);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,t))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,t),this.observerMapBounds=t,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||!this.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Zt.expand(e,gi.OBSERVER_MAP_IMAGERY_AHEAD):e,n=gi.OBSERVER_MAP_IMAGERY_PX*(t?1+2*gi.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Zv();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=s.attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?Zt.expand(e,gi.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=gi.OBSERVER_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",o&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=nt(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},gi.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?_0(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?co(this.currentSighting.milestones):[]){const o=nt(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:o.lat,lng:o.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?Gn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(wt.aspectOf(i)):void 0,markers:n,decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=nt(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=vr(i,e),{lat:s,lng:r}=ks(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=nt(this.currentSighting,this.currentTime),s=Gn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return _t.bodyAt(this.currentSighting,e,this.steadyObserver)}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 fs(Jr.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=nh(Jr.preferencesFor(this),Jc);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await ly(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t;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.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=lo(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Mu(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 Ul(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?R0(D0(this.realStartMs+i)):Ul(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?Ul(this.currentSighting.timeline.duration):this.canShowClockTime?R0(D0(this.realStartMs+this.realDurationMs)):Ul(this.realDurationMs)}}function D0(a){const e=new Date(a);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function R0(a){if(a.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return a.second?`${e(a.hour)}:${e(a.minute??0)}:${e(a.second)}`:`${e(a.hour)}:${e(a.minute??0)}`}function Ul(a){const e=Math.round(a/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const Iu="rr0-ufo";function Rh(){customElements.get(Iu)||customElements.define(Iu,gi)}const jy="recordingissues";class il{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+il.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-il.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=il.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}const Jy=.08,C0=.15,P0=2,Qy=2e3;class e2{context;noiseBuffer;voices=new Map;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}this.context.state==="suspended"&&this.context.resume(),this.apply()}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<Qy),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(e.destination);const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const o=e.createGain(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(o).connect(n);const c=()=>{const x=e.createBufferSource();return x.buffer=this.noise(e),x.loop=!0,x.loopStart=Math.random()*P0*.5,x},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const f=c(),p=e.createBiquadFilter();p.type="lowpass",p.frequency.value=600;const v=e.createGain();f.connect(p).connect(v).connect(n);const m=c(),g=e.createBiquadFilter();g.type="bandpass",g.frequency.value=1400,g.Q.value=.7;const w=e.createGain();m.connect(g).connect(w).connect(n);const y=[s,r,h,f,m];for(const x of y)x.start();const b={nodes:[i,n,o,l,d,u,p,v,g,w,...y],sources:y,firing:s,crank:r,knock:d,engineGain:o,knockGain:u,roadGain:v,windGain:w,cabin:n,output:i};return this.voices.set(t,b),b}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:o,distanceM:l}){const c=e.currentTime,h=(w,y)=>w.setTargetAtTime(y,c,Jy),d=n.rpm/60,u=d*i.cylinders/2,f=Math.min(1,n.rpm/i.shiftUpRpm),p=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*f)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*p**1.5*.8);const v=!r||o;h(t.windGain.gain,p**2*(v?.5:.06));const m=r?o?3500:700:8e3/(1+l/60);h(t.cabin.frequency,m);const g=r?.5:Math.min(1,8/Math.max(l,1));h(t.output.gain,g*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,C0/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},C0*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*P0,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class gl{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return gl.BY_KIND[e??"generic"]??gl.BY_KIND.generic}}class Wr{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=Wr.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const o=[];let l=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<Wr.STANDING_MPS){l=1,o.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,f=g=>u/e.kmhPer1000Rpm[g-1]*1e3;for(;l<e.kmhPer1000Rpm.length&&f(l)>e.shiftUpRpm;)l++;for(;l>1&&f(l)<e.shiftDownRpm;)l--;const p=Math.max(f(l),e.idleRpm*(1+.3*Math.min(1,h/3))),v=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,v+d/2.5):d<-.3?.05:v;o.push({speedMps:h,rpm:p,load:m,gear:l})}return new Wr(o,i)}at(e){const t=(e-this.startMs)/Wr.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class sh{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const f=hi(d,u,n.lat,n.lng);return{eastM:f.x,northM:-f.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),o=e.vehicle;if(o){const c=gl.of(o.kind),h=this.drive(`observer:${o.kind}`,sh.trackKey(e),()=>Wr.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:o.noise??1,inside:!0,windowsOpen:o.windowsOpen===!0,distanceM:0})}const l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=gl.of(c.engine.kind),d=v=>{const m=vr(c,v);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,sh.decorKey(c,r),()=>Wr.of(h,d,0,r));if(!u)continue;const f=vr(c,t),p=Math.hypot(f.eastM-l.eastM,f.northM-l.northM,f.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:p})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const t2=`
|
|
2200
|
+
`)}`)}}class On{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!On.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:On.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const o=i?`${i}.${s}`:s,l=t[s];l?this.field(r,l,o,n):n.push({kind:"unknown-key",path:o})}}field(e,t,i,n){if(On.isStated(e)){this.field(e.value,t,i,n);return}if(!(t.loose||e===void 0||e===null))switch(t.type){case"enum":(typeof e!="string"||!t.values?.includes(e))&&n.push({kind:"not-in-list",path:i,expected:(t.values??[]).map(s=>`"${s}"`).join(", "),found:JSON.stringify(e)});return;case"object":t.fields?this.object(e,t.fields,i,n):On.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:On.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const o=On.isStated(s)?s.value:s;this.object(o,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:On.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:On.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return On.isObject(e)&&"value"in e&&Object.keys(e).every(t=>t==="value"||t==="basis"||t==="rationale")}static typeOf(e){return Array.isArray(e)?"array":e===null?"null":typeof e}}class Un{static PIXELS_PER_INSTANT=2;static INSTANTS_PER_FLASH=2;static MAX_INSTANTS=512;static windowEndingAt(e,t){const i=Number.isFinite(t)?Math.max(0,t):0,n=Math.max(0,e-i*1e3),s=Math.max(0,e-n);return{fromMs:n,toMs:e,ms:s,seconds:s/1e3}}static instants(e,t,i,n,s){if(!(n>0))return 1;const r=s>0?Un.travelDegOver(e,t,i,i+n*1e3)/s/Un.PIXELS_PER_INSTANT:0,o=Un.flashesOver(e,n)*Un.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(r,o));return l<2?1:Math.min(Un.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const o=Un.directionAt(r,t,i),l=Un.directionAt(r,t,n),c=Math.min(1,Math.max(-1,o.x*l.x+o.y*l.y+o.z*l.z));s=Math.max(s,Math.acos(c)*180/Math.PI)}return s}static flashesOver(e,t){let i=0;for(const n of e)for(const s of n.lights??[])s.pattern.kind==="flash"&&(i=Math.max(i,s.pattern.perMinute));return i/60*t}static directionAt(e,t,i){const n=vr(e,i),s=n.eastM,r=n.altitudeM-t,o=n.northM,l=Math.hypot(s,r,o);return l===0?{x:0,y:1,z:0}:{x:s/l,y:r/l,z:o/l}}}function nh(a,e){for(const t of a){const i=t.toLowerCase().split("-")[0];if(e.includes(i))return i}return"en"}class Jr{static preferencesFor(e){const t=navigator.languages??[],i=this.declaredFor(e);return i?[i,...t]:t}static declaredFor(e){for(let t=e;t;){const i=t.closest("[lang]")?.getAttribute("lang")?.trim();if(i)return i;const n=t.getRootNode();t=n instanceof ShadowRoot?n.host:void 0}}}class fs{constructor(e){this.preferences=e}read(e){if(e===void 0||typeof e=="string")return e===""?void 0:e;for(const i of this.preferences){const n=e[i]??e[fs.base(i)];if(n)return n}const t=new Set(this.preferences.map(i=>fs.base(i)));for(const[i,n]of Object.entries(e))if(n&&t.has(fs.base(i)))return n;return Object.values(e).find(i=>i)??void 0}write(e,t,i){const n=t?.trim()?t.trim():void 0;if(e===void 0||typeof e=="string")return n;const s={...e},r=Object.keys(s).find(o=>o===i)??Object.keys(s).find(o=>fs.base(o)===fs.base(i))??i;return n===void 0?delete s[r]:s[r]=n,Object.keys(s).length===0?void 0:s}static base(e){return e.toLowerCase().split("-")[0]}}const Uv={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",showObserverMap:"Show where the observer was",hideObserverMap:"Hide where the observer was",mapImageryUnavailable:"Aerial imagery unavailable",zoomMapIn:"Zoom in",zoomMapOut:"Zoom out",fitMap:"Back to the whole path",placeObserverHere:"Put the observer here",showReferences:"Show the pictures of the place",hideReferences:"Hide the pictures of the place",referenceOpacity:"Picture opacity",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",observerHere:"The observer, here",decorHere:"Scenery",recordingIssues:"{count} problem(s) in this recording: it may not play as its author meant",issueTemplates:ho.EN},Fy=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Uv},Symbol.toStringTag,{value:"Module"})),Bv=Math.PI/180,Uy=180/Math.PI,By=256,zv=19,zy=1.25;function Hv(a,e){return 156543.03392*Math.cos(a*Bv)/2**e}function Gv(a,e){return Hv(a,e)*By}function Hy(a,e){let t=0;for(;t<zv&&Gv(a,t+1)>=e;)t++;return t}function Vv(a,e){return Gv(a,e)}function Gy(a,e){const t=e*zy;let i=0;for(;i<zv&&Hv(a,i)>t;)i++;return i}function Fc(a){const e=a*Bv;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function Wv(a,e,t){const i=2**t;return{x:(a+180)/360*i,y:Fc(e)*i}}function Xv(a,e,t){const i=Fc(a.north),n=Fc(a.south);return{x:(e-a.west)/(a.east-a.west),y:(Fc(t)-i)/(n-i)}}function Vy(a,e,t){const{x:i,y:n}=Wv(a,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function Wy(a){const e=2**a.z,t=a.x/e*360-180,i=(a.x+1)/e*360-180,n=A0(a.y,e);return{south:A0(a.y+1,e),north:n,west:t,east:i}}function A0(a,e){const t=a/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*Uy}function Yv(a,e,t,i){const n=Vy(a,e,t),s=Math.floor(i/2),r=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)r.push({x:n.x+h,y:n.y+c,z:t});const o=r.map(Wy),l={south:Math.min(...o.map(c=>c.south)),north:Math.max(...o.map(c=>c.north)),west:Math.min(...o.map(c=>c.west)),east:Math.max(...o.map(c=>c.east))};return{tiles:r,bounds:l}}class Fr{constructor(e){this.ctx=e}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(e,t){let i,n=1/0;for(const s of this.targets){const r=Math.hypot(s.x-e,s.y-t);r>s.radius||r>n||(i=s,n=r)}return i}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(e){const{ctx:t}=this;t.save(),this.targets=[],t.clearRect(0,0,this.width,this.height),this.paintGround(e),this.paintNight(e.nightFraction),this.paintPath(e),e.position?(this.paintCone(e,e.position),this.paintDecor(e),this.paintMarkers(e),this.paintObserver(e,e.position)):(this.paintDecor(e),this.paintMarkers(e)),this.paintNorth(),this.paintFooter(e.bounds,e.attribution),t.restore()}toCanvas(e,t,i){const n=Xv(e,i,t);return{x:n.x*this.width,y:n.y*this.height}}paintGround(e){const{ctx:t}=this;t.fillStyle="#1d2321",t.fillRect(0,0,this.width,this.height);const i=e.imagery;if(!i)return;const n=this.toCanvas(e.bounds,i.bounds.north,i.bounds.west),s=this.toCanvas(e.bounds,i.bounds.south,i.bounds.east);t.drawImage(i.source,n.x,n.y,s.x-n.x,s.y-n.y)}paintNight(e=0){if(e<=0)return;const{ctx:t}=this;t.save(),t.fillStyle=`rgba(6, 12, 30, ${Math.min(e,1)*.78})`,t.fillRect(0,0,this.width,this.height),t.restore()}paintDecor(e){const{ctx:t}=this;for(const i of e.decor){const n=this.toCanvas(e.bounds,i.lat,i.lng);if(i.label&&this.targets.push({kind:"decor",label:i.label,lat:i.lat,lng:i.lng,x:n.x,y:n.y,radius:7}),t.beginPath(),t.rect(n.x-3.5,n.y-3.5,7,7),t.fillStyle="rgba(120, 220, 255, 0.85)",t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.7)",t.stroke(),i.headingDeg===void 0)continue;const s=(i.headingDeg-90)*Math.PI/180;t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(n.x+Math.cos(s)*10,n.y+Math.sin(s)*10),t.lineWidth=3,t.strokeStyle="rgba(0, 0, 0, 0.6)",t.stroke(),t.lineWidth=1.5,t.strokeStyle="rgba(120, 220, 255, 0.9)",t.stroke()}}paintPath(e){if(!e.path.moved)return;const{ctx:t}=this,i=e.path.points.map(n=>this.toCanvas(e.bounds,n.lat,n.lng));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(const n of i.slice(1))t.lineTo(n.x,n.y);t.lineJoin="round",t.lineCap="round",t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=5,t.stroke(),t.strokeStyle="rgba(255, 255, 255, 0.9)",t.lineWidth=2,t.stroke()}paintCone(e,t){const{headingDeg:i,coneHalfAngleDeg:n}=e;if(i===void 0||n===void 0)return;const{ctx:s}=this,r=this.toCanvas(e.bounds,t.lat,t.lng),o=Math.hypot(this.width,this.height),l=(i-90)*Math.PI/180,c=n*Math.PI/180;s.save(),s.beginPath(),s.moveTo(r.x,r.y),s.arc(r.x,r.y,o,l-c,l+c),s.closePath();const h=s.createRadialGradient(r.x,r.y,0,r.x,r.y,o);h.addColorStop(0,"rgba(255, 224, 130, 0.55)"),h.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),h.addColorStop(1,"rgba(255, 224, 130, 0)"),s.fillStyle=h,s.fill(),s.restore()}paintMarkers(e){const{ctx:t}=this;t.textAlign="center",t.textBaseline="middle",t.font="bold 10px sans-serif";for(const i of[...e.markers].sort((n,s)=>Number(n.current)-Number(s.current))){const n=this.toCanvas(e.bounds,i.lat,i.lng);this.targets.push({kind:"milestone",label:i.note?`${i.label} — ${i.note}`:i.label,lat:i.lat,lng:i.lng,t:i.t,x:n.x,y:n.y,radius:Fr.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Fr.MARKER_RADIUS_PX,0,Math.PI*2),t.fillStyle=i.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",t.fill(),t.lineWidth=2,t.strokeStyle="rgba(255, 255, 255, 0.9)",t.stroke(),t.fillStyle=i.current?"#1d2321":"#fff",t.fillText(i.label,n.x,n.y)}}paintObserver(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.observerLabel&&this.targets.push({kind:"observer",label:e.observerLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(r=>{if(!r.current)return!1;const o=hi(r.lat,r.lng,t.lat,t.lng);return Math.hypot(o.x,o.z)<=Fr.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Fr.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 r=(e.south+e.north)/2,l=Math.abs(hi(r,e.east,r,e.west).x)/this.width,c=Fr.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 r=n?`${n} ${s}`:s;n&&this.ctx.measureText(r).width>t?(i.push(n),n=s):n=r}return n&&i.push(n),i}}class Zt{static MIN_ZOOM=.25;static MAX_ZOOM=64;zoom=1;centerX=.5;centerY=.5;get changed(){return this.zoom!==1||this.centerX!==.5||this.centerY!==.5}get zoomFactor(){return this.zoom}reset(){this.zoom=1,this.centerX=.5,this.centerY=.5}zoomAt(e,t,i){const n=Math.min(Zt.MAX_ZOOM,Math.max(Zt.MIN_ZOOM,this.zoom*i)),s=this.centerX+(e-.5)/this.zoom,r=this.centerY+(t-.5)/this.zoom;this.centerX=s-(e-.5)/n,this.centerY=r-(t-.5)/n,this.zoom=n}panBy(e,t){this.centerX-=e/this.zoom,this.centerY-=t/this.zoom}refit(e,t){if(!this.changed)return;const i=this.boundsWithin(e),n=Zt.mercatorY01(t.north),s=Zt.mercatorY01(t.south),r=Zt.mercatorY01(i.north),o=Zt.mercatorY01(i.south);this.zoom=(t.east-t.west)/(i.east-i.west),this.centerX=((i.west+i.east)/2-t.west)/(t.east-t.west),this.centerY=((r+o)/2-n)/(s-n)}keepInView(e,t,i){if(!this.changed)return!1;const n=this.boundsWithin(e),s=Zt.mercatorY01(n.north),r=Zt.mercatorY01(n.south),o=(t.lng-n.west)/(n.east-n.west),l=(Zt.mercatorY01(t.lat)-s)/(r-s);return o>=i&&o<=1-i&&l>=i&&l<=1-i?!1:(this.panBy(.5-o,.5-l),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=Zt.mercatorY01(e.north),n=Zt.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:Zt.latitudeAt(i+(this.centerY-t)*(n-i)),south:Zt.latitudeAt(i+(this.centerY+t)*(n-i))}}static expand(e,t){const i=e.east-e.west,n=e.north-e.south;return{west:e.west-i*t,east:e.east+i*t,south:e.south-n*t,north:e.north+n*t}}static pointAt(e,t,i){const n=Zt.mercatorY01(e.north),s=Zt.mercatorY01(e.south);return{lat:Zt.latitudeAt(n+i*(s-n)),lng:e.west+t*(e.east-e.west)}}static mercatorY01(e){const t=e*Math.PI/180;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}static latitudeAt(e){return Math.atan(Math.sinh(Math.PI*(1-2*e)))*180/Math.PI}}const xn=256,T0=3,Xy=32768;class qv{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(hi(i,e.east,i,e.west).x),r=Math.abs(hi(e.north,n,e.south,n).z),o=Hy(i,Math.max(s,r)),{tiles:l}=Yv(n,i,o,T0),c=T0*xn,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)),r=await this.fetchImpl(s);if(!r.ok)throw new Error(`Elevation tile fetch failed (${r.status}): ${s}`);const o=await createImageBitmap(await r.blob()),l=document.createElement("canvas");l.width=xn,l.height=xn;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,xn,xn),d=(e.x-t.x)*xn,u=(e.y-t.y)*xn;for(let f=0;f<xn;f++)for(let p=0;p<xn;p++){const v=(f*xn+p)*4,m=h[v]*256+h[v+1]+h[v+2]/256-Xy,g=d+p,w=u+f;i[w*n+g]=m}}sampleAt(e,t,i,n,s,r){const{x:o,y:l}=Wv(t,e,i),c=(o-n.x)*xn,h=(l-n.y)*xn,d=Math.max(0,Math.min(r-1,Math.floor(c))),u=Math.max(0,Math.min(r-1,Math.floor(h))),f=Math.min(r-1,d+1),p=Math.min(r-1,u+1),v=c-d,m=h-u,g=s[u*r+d],w=s[u*r+f],y=s[p*r+d],b=s[p*r+f],x=g+(w-g)*v,E=y+(b-y)*v;return x+(E-x)*m}}const Uc=256,Yy=5;async function $v(a,e,t,i){const n=(a.south+a.north)/2,s=(a.west+a.east)/2,r=Math.abs(hi(n,a.east,n,a.west).x),o=Math.abs(hi(a.north,s,a.south,s).z),l=Math.max(r,o),{zoom:c,gridSize:h}=qy(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=Yv(s,n,c,h),f=h*Uc,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 b=>{const x=t(b),E=await i(x);if(!E.ok)throw new Error(`Imagery tile fetch failed (${E.status}): ${x}`);const I=await createImageBitmap(await E.blob());v.drawImage(I,(b.x-m.x)*Uc,(b.y-m.y)*Uc)}));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 y=w.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(p,0,0,f,f,0,0,g,g),{source:w,width:g,height:g,bounds:u}}function qy(a,e,t){let i=Gy(a,e/Math.max(1,t)),n=I0(a,i,e);for(;n>Yy&&i>0;)i--,n=I0(a,i,e);return{zoom:i,gridSize:n,mPerPixel:Vv(a,i)/Uc}}function I0(a,e,t){const i=Math.ceil(t/2/Vv(a,e));return Math.max(3,2*i+1)}class Kv{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 $v(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class kn{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??kn.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"~"^(${kn.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:kn.surfaceOf(s),widthM:kn.widthOf(s),points:n.map(r=>({lat:r.lat,lng:r.lon}))})}return t}queued(e){const t=kn.queue.then(e,e);return kn.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,kn.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:kn.WIDTH_M[e.highway]??kn.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 jh="https://ufoathome.org/roads/index.json";class ap{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??ap.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.75.2")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==jh?[t,jh]:[jh]).map(i=>i+e)}}function $y(){return{elevation:new qv,imagery:Zv(),roads:Ky()}}function Zv(){return new Kv}function Ky(){return new ap({fallback:new kn})}const Zy=new Set,Qr="show-observer-map",ja="hide-milestones";class gi extends HTMLElement{static get observedAttributes(){return["src",Qr,ja]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;fullscreenButton;observerMapButton;referencesButton;referenceOpacityInput;referencesShownState=!0;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new Zt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Dh.create();issues=[];issueCheck=0;sightingAudio=new Ly;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Uv;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeObserverMap()};observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<gi.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=Zt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Zt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},gi.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=Zt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Dy}</style>${Iy}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new ml(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",t=>{t.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Fr(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");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.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",t=>{this.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.seekInput.addEventListener("keydown",this.handleSeekKey),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.observerMapResizeObserver?.observe(this.observerMapPanel),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.observerMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===Qr&&this.applyObserverMapDefault(),e===ja&&this.setMilestonesShown(!this.hasAttribute(ja))}async loadFromSrc(e){this.sightingData=await np.json(e)}get sightingData(){return Oy(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=ky(e),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await mt(async()=>{const{default:r}=await import("./sightingSchema-DOOSOVWK.js");return{default:r}},[],import.meta.url);n=new On(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],ho.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(jy,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=ho.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.player.seek(this.player.time)}hostControls(e){const t=[this.milestonesButton,this.referenceOpacityInput,this.referencesButton,this.observerMapButton];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?co(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),o=r?`${s} — ${r}`:s;return n.title=o,n.setAttribute("aria-label",o),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n}))}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=gi.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const o=Number(r.dataset.t);Math.abs(o-e)>n||(!s||Math.abs(o-e)<Math.abs(s.t-e))&&(s={t:o,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?_0(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=Un.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<gi.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(gi.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/gi.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/gi.EXPOSURE_STEP_PX),s=Math.min(gi.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let o=0;o<s;o++)r.push(t.fromMs+t.ms*o/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,o]of i){const l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),h=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=wt.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:Zy;this.canvasRenderer.setStarPoints(wt.starPointsOf(this.sighting.instrument));const n=(nt(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,l]of o.shapes){const c=o.share,h=c===1?l:{...l,transparency:1-(1-l.transparency)*c};this.canvasRenderer.paintShape(this.shifted(h,r))}for(const[o,l]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...l,selected:!0}):this.canvasRenderer.paintMemberOutline(l):this.canvasRenderer.paintSelectionOnly(this.shifted(l,r)));if(this.selectionShown&&i.size>1){const o=vt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds));this.canvasRenderer.paintGroupHandles(o)}this.overlayPainter?.(this.canvasRenderer),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(l=>l.transparency<1).map(l=>this.shifted(l,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(ip(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 Ry(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*gi.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton(),this.flashPlayback())}flashPlayback(){const e=this.playbackFlash;e.textContent=this.player.playbackState==="playing"?"▶":"⏸",e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}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()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear()}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(Qr),t=e===null?this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const t=!this.observerMapPanel.hidden?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=Za.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.observerPath.boundsAround(gi.OBSERVER_MAP_MIN_SPAN_M,gi.OBSERVER_MAP_MARGIN);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,t))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,t),this.observerMapBounds=t,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||!this.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Zt.expand(e,gi.OBSERVER_MAP_IMAGERY_AHEAD):e,n=gi.OBSERVER_MAP_IMAGERY_PX*(t?1+2*gi.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Zv();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=s.attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?Zt.expand(e,gi.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=gi.OBSERVER_MAP_CLEARANCE_PX,o=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",o&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!o&&(l||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=nt(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},gi.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?_0(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?co(this.currentSighting.milestones):[]){const o=nt(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:o.lat,lng:o.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?Gn.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(wt.aspectOf(i)):void 0,markers:n,decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=nt(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=vr(i,e),{lat:s,lng:r}=ks(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=nt(this.currentSighting,this.currentTime),s=Gn.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return _t.bodyAt(this.currentSighting,e,this.steadyObserver)}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 fs(Jr.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=nh(Jr.preferencesFor(this),Jc);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await ly(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t;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.updateObserverMapButton(),this.updateReferencesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=lo(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Mu(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 Ul(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?R0(D0(this.realStartMs+i)):Ul(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?Ul(this.currentSighting.timeline.duration):this.canShowClockTime?R0(D0(this.realStartMs+this.realDurationMs)):Ul(this.realDurationMs)}}function D0(a){const e=new Date(a);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function R0(a){if(a.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return a.second?`${e(a.hour)}:${e(a.minute??0)}:${e(a.second)}`:`${e(a.hour)}:${e(a.minute??0)}`}function Ul(a){const e=Math.round(a/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const Iu="rr0-ufo";function Rh(){customElements.get(Iu)||customElements.define(Iu,gi)}const jy="recordingissues";class il{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+il.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-il.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=il.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}const Jy=.08,C0=.15,P0=2,Qy=2e3;class e2{context;noiseBuffer;voices=new Map;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}this.context.state==="suspended"&&this.context.resume(),this.apply()}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<Qy),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(e.destination);const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const o=e.createGain(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(o).connect(n);const c=()=>{const x=e.createBufferSource();return x.buffer=this.noise(e),x.loop=!0,x.loopStart=Math.random()*P0*.5,x},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const f=c(),p=e.createBiquadFilter();p.type="lowpass",p.frequency.value=600;const v=e.createGain();f.connect(p).connect(v).connect(n);const m=c(),g=e.createBiquadFilter();g.type="bandpass",g.frequency.value=1400,g.Q.value=.7;const w=e.createGain();m.connect(g).connect(w).connect(n);const y=[s,r,h,f,m];for(const x of y)x.start();const b={nodes:[i,n,o,l,d,u,p,v,g,w,...y],sources:y,firing:s,crank:r,knock:d,engineGain:o,knockGain:u,roadGain:v,windGain:w,cabin:n,output:i};return this.voices.set(t,b),b}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:o,distanceM:l}){const c=e.currentTime,h=(w,y)=>w.setTargetAtTime(y,c,Jy),d=n.rpm/60,u=d*i.cylinders/2,f=Math.min(1,n.rpm/i.shiftUpRpm),p=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*f)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*p**1.5*.8);const v=!r||o;h(t.windGain.gain,p**2*(v?.5:.06));const m=r?o?3500:700:8e3/(1+l/60);h(t.cabin.frequency,m);const g=r?.5:Math.min(1,8/Math.max(l,1));h(t.output.gain,g*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,C0/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},C0*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*P0,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class gl{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return gl.BY_KIND[e??"generic"]??gl.BY_KIND.generic}}class Wr{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=Wr.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const o=[];let l=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<Wr.STANDING_MPS){l=1,o.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,f=g=>u/e.kmhPer1000Rpm[g-1]*1e3;for(;l<e.kmhPer1000Rpm.length&&f(l)>e.shiftUpRpm;)l++;for(;l>1&&f(l)<e.shiftDownRpm;)l--;const p=Math.max(f(l),e.idleRpm*(1+.3*Math.min(1,h/3))),v=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,v+d/2.5):d<-.3?.05:v;o.push({speedMps:h,rpm:p,load:m,gear:l})}return new Wr(o,i)}at(e){const t=(e-this.startMs)/Wr.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class sh{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const f=hi(d,u,n.lat,n.lng);return{eastM:f.x,northM:-f.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),o=e.vehicle;if(o){const c=gl.of(o.kind),h=this.drive(`observer:${o.kind}`,sh.trackKey(e),()=>Wr.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:o.noise??1,inside:!0,windowsOpen:o.windowsOpen===!0,distanceM:0})}const l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=gl.of(c.engine.kind),d=v=>{const m=vr(c,v);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,sh.decorKey(c,r),()=>Wr.of(h,d,0,r));if(!u)continue;const f=vr(c,t),p=Math.hypot(f.eastM-l.eastM,f.northM-l.northM,f.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:p})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const t2=`
|
|
2201
2201
|
<div class="stage" id="stage">
|
|
2202
2202
|
<div class="frame" id="frame">
|
|
2203
2203
|
<canvas id="scene-canvas"></canvas>
|
|
@@ -7461,7 +7461,7 @@ void main() {
|
|
|
7461
7461
|
float blur = max(vNearBlur, vHaze);
|
|
7462
7462
|
gl_FragColor = vec4(color, tex.a * uOpacity * vNearFade * (1.0 - blur * ${H5.toFixed(2)}));
|
|
7463
7463
|
}
|
|
7464
|
-
`;function Y5(a){let e=a;return function(){e|=0,e=e+1831565813|0;let i=Math.imul(e^e>>>15,1|e);return i=i+Math.imul(i^i>>>7,61|i)^i,((i^i>>>14)>>>0)/4294967296}}const q5=.14,mg=4.5;let wc;function $5(){if(wc)return wc;const a=128,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createImageData(a,a),n=a/2,s=a*q5,r=a-s;for(let o=0;o<a;o++)for(let l=0;l<a;l++){const c=l+.5-n,h=o+.5<s?o+.5-s:o+.5>r?o+.5-r:0,d=Math.hypot(c,h),u=Math.exp(-(d*d)/(2*mg*mg)),f=(o*a+l)*4;i.data[f]=255,i.data[f+1]=255,i.data[f+2]=255,i.data[f+3]=Math.round(255*Math.min(1,u))}return t.putImageData(i,0,0),wc=new Ws(e),wc}function K5(a){const e=Y5(a.seed),t=new Float32Array(a.count*3),i=new Float32Array(a.count),n=a.radiusM;for(let c=0;c<a.count;c++){const h=e()*Math.PI*2,d=Math.sqrt(e())*a.radiusM;t[c*3]=Math.cos(h)*d,t[c*3+1]=e()*a.heightM,t[c*3+2]=Math.sin(h)*d,i[c]=.85+e()*.3}const s=new ht;s.setAttribute("position",new it(t,3)),s.setAttribute("aSpeed",new it(i,1)),s.setDrawRange(0,a.visibleCount);const r={uTime:{value:0},uOverallSpeed:{value:a.overallSpeed},uPixelSize:{value:a.pixelSize},uOpacity:{value:a.opacity},uColor:{value:a.color},uUvSquash:{value:1},uSpeedStreak:{value:a.speedStreak},uWindOffset:{value:new ye(0,0)},uDropVelocity:{value:new R(0,-1,0)},uAspect:{value:1},uHeight:{value:a.heightM},uBottomY:{value:a.bottomYM},uHalfWidth:{value:n},uTexture:{value:$5()},uNearFocusDistance:{value:a.nearFocusDistanceM},uHazeDistance:{value:a.hazeDistanceM},uHazeColor:{value:a.hazeColor}},o=new dt({uniforms:r,vertexShader:W5,fragmentShader:X5,transparent:!0,depthWrite:!1,depthTest:!0}),l=new Ls(s,o);return l.frustumCulled=!1,{points:l,uniforms:r}}class Hs{static S10_MAG_PER_ARCSEC2=10+2.5*Math.log10(3600*3600);static toMagPerArcsec2(e){return Hs.S10_MAG_PER_ARCSEC2-2.5*Math.log10(Math.max(e,1e-12))}static fromMagPerArcsec2(e){return 10**(.4*(Hs.S10_MAG_PER_ARCSEC2-e))}static latitudeOfRowCoord(e){const t=2*e-1;return 90*Math.sign(t)*t*t}static rowCoordOfLatitude(e){return(Math.sign(e)*Math.sqrt(Math.min(Math.abs(e),90)/90)+1)/2}static angleOfColumnCoord(e){return 180*e*e}static columnCoordOfAngle(e){return Math.sqrt(Math.min(Math.max(e,0),180)/180)}}class st{static LONGITUDE_STEPS=256;static LATITUDE_STEPS=128;static SUN_RADIUS_KPC=8.2;static SUN_HEIGHT_KPC=.02;static DISK_SCALE_LENGTH_KPC=2.6;static DISK_SCALE_HEIGHT_KPC=.3;static BULGE_SCALE_KPC=.5;static BULGE_FLATTENING=.6;static BULGE_TO_LOCAL_DISK=20;static DUST_SCALE_LENGTH_KPC=3.5;static DUST_SCALE_HEIGHT_KPC=.1;static EXTINCTION_MAG_PER_KPC=.8;static NEAR_LIMIT_KPC=.01;static FAR_LIMIT_KPC=40;static STEPS=192;static OPAQUE_MAG=15;static PEAK_MAG_PER_ARCSEC2=21;static PEAK_RADIANCE=6.278;static PEAK_LONGITUDE_DEG=0;static PEAK_LATITUDE_DEG=4.5;surfaceBrightnessS10(e,t){return this.radianceTowards(e,t)*st.anchor()}static anchor(){return Hs.fromMagPerArcsec2(st.PEAK_MAG_PER_ARCSEC2)/st.PEAK_RADIANCE}radianceTowards(e,t){return this.trace(e,t).light}extinctionTowards(e,t){return this.trace(e,t).extinctionMag}trace(e,t){const i=e*Math.PI/180,n=t*Math.PI/180,s=-Math.cos(n)*Math.cos(i),r=Math.cos(n)*Math.sin(i),o=Math.sin(n);let l=0,c=0,h=0;const d=Math.log(st.NEAR_LIMIT_KPC),u=Math.log(st.FAR_LIMIT_KPC)-d;for(let f=0;f<st.STEPS;f++){const p=Math.exp(d+u*f/(st.STEPS-1)),v=p-h;h=p;const m=p-v/2,g=st.SUN_RADIUS_KPC+m*s,w=m*r,y=st.SUN_HEIGHT_KPC+m*o,b=Math.hypot(g,w),x=Math.exp(-(b-st.SUN_RADIUS_KPC)/st.DISK_SCALE_LENGTH_KPC-Math.abs(y)/st.DISK_SCALE_HEIGHT_KPC),E=Math.hypot(b,y/st.BULGE_FLATTENING),I=st.BULGE_TO_LOCAL_DISK*Math.exp(-E/st.BULGE_SCALE_KPC);if(l+=(x+I)*v*10**(-.4*c),c+=st.EXTINCTION_MAG_PER_KPC*Math.exp(-(b-st.SUN_RADIUS_KPC)/st.DUST_SCALE_LENGTH_KPC-(Math.abs(y)-st.SUN_HEIGHT_KPC)/st.DUST_SCALE_HEIGHT_KPC)*v,c>st.OPAQUE_MAG)break}return{light:l,extinctionMag:c}}values=new Float32Array(st.LONGITUDE_STEPS*st.LATITUDE_STEPS);walked=0;get done(){return this.walked>=st.LATITUDE_STEPS}walk(e){const t=st.LONGITUDE_STEPS,i=Math.min(st.LATITUDE_STEPS,this.walked+e);for(;this.walked<i;this.walked++){const n=Hs.latitudeOfRowCoord((this.walked+.5)/st.LATITUDE_STEPS);for(let s=0;s<t;s++)this.values[this.walked*t+s]=this.radianceTowards(360*(s+.5)/t,n)}}harvest(){let e=Number.POSITIVE_INFINITY;for(const n of this.values)n<e&&(e=n);const t=st.anchor(),i=new Float32Array(this.values.length);for(let n=0;n<i.length;n++)i[n]=(this.values[n]-e)*t;return{width:st.LONGITUDE_STEPS,height:st.LATITUDE_STEPS,data:i}}}class Yt{static LONGITUDE_STEPS=128;static LATITUDE_STEPS=96;static RADIAL_FALLOFF=1.3;static FAN_EXPONENT=2.6;static SUBLIMATION_AU=.1;static NEAR_LIMIT_AU=.004;static FAR_LIMIT_AU=8;static STEPS=224;static LOBE_ASYMMETRY=[.7,-.2,-.81];static LOBE_WEIGHT=[.665,.33,.00541];static RIGHT_ANGLE_S10=200;static POLE_S10=77;surfaceBrightnessS10(e,t){return this.radianceTowards(e,t)*this.anchor()}anchored;anchor(){return this.anchored===void 0&&(this.anchored=Yt.RIGHT_ANGLE_S10/this.radianceTowards(90,0)),this.anchored}radianceTowards(e,t){const i=e*Math.PI/180,n=t*Math.PI/180,s=-Math.cos(n)*Math.cos(i),r=-Math.cos(n)*Math.sin(i),o=Math.sin(n),l=-s;let c=0,h=0;const d=Math.log(Yt.NEAR_LIMIT_AU),u=Math.log(Yt.FAR_LIMIT_AU)-d;for(let f=0;f<Yt.STEPS;f++){const p=Math.exp(d+u*f/(Yt.STEPS-1)),v=p-h;h=p;const m=p-v/2,g=1+m*s,w=m*r,y=m*o,b=Math.hypot(g,w,y);if(b<Yt.SUBLIMATION_AU)continue;const x=y/b,E=b**-1.3*Math.exp(-2.6*Math.abs(x)),I=(l-m)/b;c+=E*this.scattering(I)*v/(b*b)}return c}scattering(e){let t=0;for(let i=0;i<Yt.LOBE_ASYMMETRY.length;i++){const n=Yt.LOBE_ASYMMETRY[i],s=1+n*n-2*n*e;t+=Yt.LOBE_WEIGHT[i]*(1-n*n)/(4*Math.PI*s**1.5)}return t}values=new Float32Array(Yt.LONGITUDE_STEPS*Yt.LATITUDE_STEPS);walked=0;get done(){return this.walked>=Yt.LATITUDE_STEPS}walk(e){const t=Yt.LONGITUDE_STEPS,i=Math.min(Yt.LATITUDE_STEPS,this.walked+e);for(;this.walked<i;this.walked++){const n=Hs.latitudeOfRowCoord((this.walked+.5)/Yt.LATITUDE_STEPS);for(let s=0;s<t;s++){const r=Hs.angleOfColumnCoord((s+.5)/t);this.values[this.walked*t+s]=this.radianceTowards(r,n)}}}harvest(){const e=this.anchor();let t=Number.POSITIVE_INFINITY;for(const n of this.values)n<t&&(t=n);const i=new Float32Array(this.values.length);for(let n=0;n<i.length;n++)i[n]=(this.values[n]-t)*e;return{width:Yt.LONGITUDE_STEPS,height:Yt.LATITUDE_STEPS,data:i}}}class le{static get NANOLAMBERTS_PER_S10(){return le.toNanolamberts(10+2.5*Math.log10(3600*3600))}static nanolambertsOfS10(e){return e*le.NANOLAMBERTS_PER_S10}static TWILIGHT=[{sunAltitudeDeg:90,magPerArcsec2:3.5},{sunAltitudeDeg:10,magPerArcsec2:4.2},{sunAltitudeDeg:0,magPerArcsec2:7.5},...Array.from({length:15},(e,t)=>{const i=-4-t;return{sunAltitudeDeg:i,magPerArcsec2:le.patatV(i)}}),{sunAltitudeDeg:-24,magPerArcsec2:22}];static patatV(e){const t=90-e-95;return 11.84+1.518*t-.057*t*t}static moonlessMagPerArcsec2(e){const t=le.TWILIGHT;for(let i=0;i<t.length-1;i++){const n=t[i],s=t[i+1];if(e<=n.sunAltitudeDeg&&e>=s.sunAltitudeDeg){const r=(n.sunAltitudeDeg-e)/(n.sunAltitudeDeg-s.sunAltitudeDeg);return n.magPerArcsec2+(s.magPerArcsec2-n.magPerArcsec2)*r}}return e>0?t[0].magPerArcsec2:t[t.length-1].magPerArcsec2}static toNanolamberts(e){return 34.08*Math.exp(le.NANOLAMBERT_OFFSET-le.PER_MAGNITUDE*e)}static fromNanolamberts(e){return(le.NANOLAMBERT_OFFSET-Math.log(Math.max(e,1e-6)/34.08))/le.PER_MAGNITUDE}static NANOLAMBERT_OFFSET=20.7233;static PER_MAGNITUDE=Math.LN10/2.5;static airMass(e){const t=Math.cos(Math.max(e,0)*Math.PI/180);return 1/Math.sqrt(Math.max(1-.96*t*t,.001))}static EXTINCTION_PER_AIR_MASS=.172;static scatteringAt(e){const t=Math.min(Math.max(e,0),180)*Math.PI/180;return 10**5.36*(1.06+Math.cos(t)**2)+10**(6.15-Math.min(e,180)/40)}static scatteredFraction(e){return 1-10**(-.4*le.EXTINCTION_PER_AIR_MASS*le.airMass(e))}static AIRGLOW_MAG_PER_ARCSEC2=22;static twilightExcessNanolamberts(e){return Math.max(0,le.toNanolamberts(le.moonlessMagPerArcsec2(e))-le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2))}static twilightOutput(e){const t=le.scatteringAt(90-e)*le.scatteredFraction(90);return le.twilightExcessNanolamberts(e)/t}static moonNanolamberts(e,t,i,n){return le.scatteringAt(i)*le.moonOutput(e,t)*le.scatteredFraction(n)}static moonOutput(e,t){if(t<=0)return 0;const i=Math.min(Math.abs(e),180);return 10**(-.4*(3.84+.026*i+4e-9*i**4))*10**(-.4*le.EXTINCTION_PER_AIR_MASS*le.airMass(t))}static phaseAngleOf(e){return Math.acos(Math.min(Math.max(2*e-1,-1),1))*180/Math.PI}static magPerArcsec2(e,t,i,n){const s=le.scatteredFraction(i),r=le.scatteringAt(e.separationDeg)*le.twilightOutput(e.altitudeDeg)*s,o=le.scatteringAt(t.separationDeg)*le.moonOutput(t.phaseAngleDeg,t.altitudeDeg)*s;return le.fromNanolamberts(le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2)+r+o+le.artificialNanolamberts(n,i))}static artificialZenithNanolamberts(e){return e===void 0||!Number.isFinite(e)?0:Math.max(0,le.toNanolamberts(e)-le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2))}static artificialNanolamberts(e,t){const i=le.artificialZenithNanolamberts(e);return i>0?i*le.artificialShape(t):0}static artificialShape(e){return le.scatteredFraction(e)/le.scatteredFraction(90)}static nakedEyeLimit(e){return 7.93-5*Math.log10(10**(4.316-e/5)+1)}static starLossMagnitudes(e,t,i){const n=le.moonlessMagPerArcsec2(e),s=le.fromNanolamberts(le.toNanolamberts(n)+le.scatteringAt(90-t.altitudeDeg)*le.moonOutput(t.phaseAngleDeg,t.altitudeDeg)*le.scatteredFraction(90)+le.artificialZenithNanolamberts(i));return Math.max(0,le.nakedEyeLimit(n)-le.nakedEyeLimit(s))}}class or{static RADIUS=890;static TOO_BRIGHT_MAG_PER_ARCSEC2=16;static MILKY_WAY_TINT=[.9,.94,1];static ZODIACAL_TINT=[1,.97,.9];object;material;maps=Gr.shared;milkyWayTexture;zodiacalTexture;milkyWayTexels;zodiacalTexels;repaint;constructor(){this.milkyWayTexels=this.maps.milkyWayTexels,this.milkyWayTexture=or.buildTexture(this.milkyWayTexels,st.LONGITUDE_STEPS,st.LATITUDE_STEPS,yl),this.zodiacalTexels=this.maps.zodiacalTexels,this.zodiacalTexture=or.buildTexture(this.zodiacalTexels,Yt.LONGITUDE_STEPS,Yt.LATITUDE_STEPS,Di),this.material=new dt({uniforms:{uMilkyWay:{value:this.milkyWayTexture},uZodiacal:{value:this.zodiacalTexture},uGalactic:{value:new Xe},uEclipticPole:{value:new R(0,1,0)},uSunLongitude:{value:new R(1,0,0)},uMoonDirection:{value:new R(0,-1,0)},uMoonOutput:{value:0},uSunDirection:{value:new R(0,-1,0)},uTwilightOutput:{value:0},uAirglowNanolamberts:{value:le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2)},uArtificialNanolamberts:{value:0},uSkyColor:{value:new R(0,0,0)},uMilkyWayTint:{value:new R(...or.MILKY_WAY_TINT)},uZodiacalTint:{value:new R(...or.ZODIACAL_TINT)},uReady:{value:this.maps.done?1:0},uEncodeDestination:{value:1}},vertexShader:`
|
|
7464
|
+
`;function Y5(a){let e=a;return function(){e|=0,e=e+1831565813|0;let i=Math.imul(e^e>>>15,1|e);return i=i+Math.imul(i^i>>>7,61|i)^i,((i^i>>>14)>>>0)/4294967296}}const q5=.14,mg=2.25;let wc;function $5(){if(wc)return wc;const a=128,e=document.createElement("canvas");e.width=a,e.height=a;const t=e.getContext("2d"),i=t.createImageData(a,a),n=a/2,s=a*q5,r=a-s;for(let o=0;o<a;o++)for(let l=0;l<a;l++){const c=l+.5-n,h=o+.5<s?o+.5-s:o+.5>r?o+.5-r:0,d=Math.hypot(c,h),u=Math.exp(-(d*d)/(2*mg*mg)),f=(o*a+l)*4;i.data[f]=255,i.data[f+1]=255,i.data[f+2]=255,i.data[f+3]=Math.round(255*Math.min(1,u))}return t.putImageData(i,0,0),wc=new Ws(e),wc}function K5(a){const e=Y5(a.seed),t=new Float32Array(a.count*3),i=new Float32Array(a.count),n=a.radiusM;for(let c=0;c<a.count;c++){const h=e()*Math.PI*2,d=Math.sqrt(e())*a.radiusM;t[c*3]=Math.cos(h)*d,t[c*3+1]=e()*a.heightM,t[c*3+2]=Math.sin(h)*d,i[c]=.85+e()*.3}const s=new ht;s.setAttribute("position",new it(t,3)),s.setAttribute("aSpeed",new it(i,1)),s.setDrawRange(0,a.visibleCount);const r={uTime:{value:0},uOverallSpeed:{value:a.overallSpeed},uPixelSize:{value:a.pixelSize},uOpacity:{value:a.opacity},uColor:{value:a.color},uUvSquash:{value:1},uSpeedStreak:{value:a.speedStreak},uWindOffset:{value:new ye(0,0)},uDropVelocity:{value:new R(0,-1,0)},uAspect:{value:1},uHeight:{value:a.heightM},uBottomY:{value:a.bottomYM},uHalfWidth:{value:n},uTexture:{value:$5()},uNearFocusDistance:{value:a.nearFocusDistanceM},uHazeDistance:{value:a.hazeDistanceM},uHazeColor:{value:a.hazeColor}},o=new dt({uniforms:r,vertexShader:W5,fragmentShader:X5,transparent:!0,depthWrite:!1,depthTest:!0}),l=new Ls(s,o);return l.frustumCulled=!1,{points:l,uniforms:r}}class Hs{static S10_MAG_PER_ARCSEC2=10+2.5*Math.log10(3600*3600);static toMagPerArcsec2(e){return Hs.S10_MAG_PER_ARCSEC2-2.5*Math.log10(Math.max(e,1e-12))}static fromMagPerArcsec2(e){return 10**(.4*(Hs.S10_MAG_PER_ARCSEC2-e))}static latitudeOfRowCoord(e){const t=2*e-1;return 90*Math.sign(t)*t*t}static rowCoordOfLatitude(e){return(Math.sign(e)*Math.sqrt(Math.min(Math.abs(e),90)/90)+1)/2}static angleOfColumnCoord(e){return 180*e*e}static columnCoordOfAngle(e){return Math.sqrt(Math.min(Math.max(e,0),180)/180)}}class st{static LONGITUDE_STEPS=256;static LATITUDE_STEPS=128;static SUN_RADIUS_KPC=8.2;static SUN_HEIGHT_KPC=.02;static DISK_SCALE_LENGTH_KPC=2.6;static DISK_SCALE_HEIGHT_KPC=.3;static BULGE_SCALE_KPC=.5;static BULGE_FLATTENING=.6;static BULGE_TO_LOCAL_DISK=20;static DUST_SCALE_LENGTH_KPC=3.5;static DUST_SCALE_HEIGHT_KPC=.1;static EXTINCTION_MAG_PER_KPC=.8;static NEAR_LIMIT_KPC=.01;static FAR_LIMIT_KPC=40;static STEPS=192;static OPAQUE_MAG=15;static PEAK_MAG_PER_ARCSEC2=21;static PEAK_RADIANCE=6.278;static PEAK_LONGITUDE_DEG=0;static PEAK_LATITUDE_DEG=4.5;surfaceBrightnessS10(e,t){return this.radianceTowards(e,t)*st.anchor()}static anchor(){return Hs.fromMagPerArcsec2(st.PEAK_MAG_PER_ARCSEC2)/st.PEAK_RADIANCE}radianceTowards(e,t){return this.trace(e,t).light}extinctionTowards(e,t){return this.trace(e,t).extinctionMag}trace(e,t){const i=e*Math.PI/180,n=t*Math.PI/180,s=-Math.cos(n)*Math.cos(i),r=Math.cos(n)*Math.sin(i),o=Math.sin(n);let l=0,c=0,h=0;const d=Math.log(st.NEAR_LIMIT_KPC),u=Math.log(st.FAR_LIMIT_KPC)-d;for(let f=0;f<st.STEPS;f++){const p=Math.exp(d+u*f/(st.STEPS-1)),v=p-h;h=p;const m=p-v/2,g=st.SUN_RADIUS_KPC+m*s,w=m*r,y=st.SUN_HEIGHT_KPC+m*o,b=Math.hypot(g,w),x=Math.exp(-(b-st.SUN_RADIUS_KPC)/st.DISK_SCALE_LENGTH_KPC-Math.abs(y)/st.DISK_SCALE_HEIGHT_KPC),E=Math.hypot(b,y/st.BULGE_FLATTENING),I=st.BULGE_TO_LOCAL_DISK*Math.exp(-E/st.BULGE_SCALE_KPC);if(l+=(x+I)*v*10**(-.4*c),c+=st.EXTINCTION_MAG_PER_KPC*Math.exp(-(b-st.SUN_RADIUS_KPC)/st.DUST_SCALE_LENGTH_KPC-(Math.abs(y)-st.SUN_HEIGHT_KPC)/st.DUST_SCALE_HEIGHT_KPC)*v,c>st.OPAQUE_MAG)break}return{light:l,extinctionMag:c}}values=new Float32Array(st.LONGITUDE_STEPS*st.LATITUDE_STEPS);walked=0;get done(){return this.walked>=st.LATITUDE_STEPS}walk(e){const t=st.LONGITUDE_STEPS,i=Math.min(st.LATITUDE_STEPS,this.walked+e);for(;this.walked<i;this.walked++){const n=Hs.latitudeOfRowCoord((this.walked+.5)/st.LATITUDE_STEPS);for(let s=0;s<t;s++)this.values[this.walked*t+s]=this.radianceTowards(360*(s+.5)/t,n)}}harvest(){let e=Number.POSITIVE_INFINITY;for(const n of this.values)n<e&&(e=n);const t=st.anchor(),i=new Float32Array(this.values.length);for(let n=0;n<i.length;n++)i[n]=(this.values[n]-e)*t;return{width:st.LONGITUDE_STEPS,height:st.LATITUDE_STEPS,data:i}}}class Yt{static LONGITUDE_STEPS=128;static LATITUDE_STEPS=96;static RADIAL_FALLOFF=1.3;static FAN_EXPONENT=2.6;static SUBLIMATION_AU=.1;static NEAR_LIMIT_AU=.004;static FAR_LIMIT_AU=8;static STEPS=224;static LOBE_ASYMMETRY=[.7,-.2,-.81];static LOBE_WEIGHT=[.665,.33,.00541];static RIGHT_ANGLE_S10=200;static POLE_S10=77;surfaceBrightnessS10(e,t){return this.radianceTowards(e,t)*this.anchor()}anchored;anchor(){return this.anchored===void 0&&(this.anchored=Yt.RIGHT_ANGLE_S10/this.radianceTowards(90,0)),this.anchored}radianceTowards(e,t){const i=e*Math.PI/180,n=t*Math.PI/180,s=-Math.cos(n)*Math.cos(i),r=-Math.cos(n)*Math.sin(i),o=Math.sin(n),l=-s;let c=0,h=0;const d=Math.log(Yt.NEAR_LIMIT_AU),u=Math.log(Yt.FAR_LIMIT_AU)-d;for(let f=0;f<Yt.STEPS;f++){const p=Math.exp(d+u*f/(Yt.STEPS-1)),v=p-h;h=p;const m=p-v/2,g=1+m*s,w=m*r,y=m*o,b=Math.hypot(g,w,y);if(b<Yt.SUBLIMATION_AU)continue;const x=y/b,E=b**-1.3*Math.exp(-2.6*Math.abs(x)),I=(l-m)/b;c+=E*this.scattering(I)*v/(b*b)}return c}scattering(e){let t=0;for(let i=0;i<Yt.LOBE_ASYMMETRY.length;i++){const n=Yt.LOBE_ASYMMETRY[i],s=1+n*n-2*n*e;t+=Yt.LOBE_WEIGHT[i]*(1-n*n)/(4*Math.PI*s**1.5)}return t}values=new Float32Array(Yt.LONGITUDE_STEPS*Yt.LATITUDE_STEPS);walked=0;get done(){return this.walked>=Yt.LATITUDE_STEPS}walk(e){const t=Yt.LONGITUDE_STEPS,i=Math.min(Yt.LATITUDE_STEPS,this.walked+e);for(;this.walked<i;this.walked++){const n=Hs.latitudeOfRowCoord((this.walked+.5)/Yt.LATITUDE_STEPS);for(let s=0;s<t;s++){const r=Hs.angleOfColumnCoord((s+.5)/t);this.values[this.walked*t+s]=this.radianceTowards(r,n)}}}harvest(){const e=this.anchor();let t=Number.POSITIVE_INFINITY;for(const n of this.values)n<t&&(t=n);const i=new Float32Array(this.values.length);for(let n=0;n<i.length;n++)i[n]=(this.values[n]-t)*e;return{width:Yt.LONGITUDE_STEPS,height:Yt.LATITUDE_STEPS,data:i}}}class le{static get NANOLAMBERTS_PER_S10(){return le.toNanolamberts(10+2.5*Math.log10(3600*3600))}static nanolambertsOfS10(e){return e*le.NANOLAMBERTS_PER_S10}static TWILIGHT=[{sunAltitudeDeg:90,magPerArcsec2:3.5},{sunAltitudeDeg:10,magPerArcsec2:4.2},{sunAltitudeDeg:0,magPerArcsec2:7.5},...Array.from({length:15},(e,t)=>{const i=-4-t;return{sunAltitudeDeg:i,magPerArcsec2:le.patatV(i)}}),{sunAltitudeDeg:-24,magPerArcsec2:22}];static patatV(e){const t=90-e-95;return 11.84+1.518*t-.057*t*t}static moonlessMagPerArcsec2(e){const t=le.TWILIGHT;for(let i=0;i<t.length-1;i++){const n=t[i],s=t[i+1];if(e<=n.sunAltitudeDeg&&e>=s.sunAltitudeDeg){const r=(n.sunAltitudeDeg-e)/(n.sunAltitudeDeg-s.sunAltitudeDeg);return n.magPerArcsec2+(s.magPerArcsec2-n.magPerArcsec2)*r}}return e>0?t[0].magPerArcsec2:t[t.length-1].magPerArcsec2}static toNanolamberts(e){return 34.08*Math.exp(le.NANOLAMBERT_OFFSET-le.PER_MAGNITUDE*e)}static fromNanolamberts(e){return(le.NANOLAMBERT_OFFSET-Math.log(Math.max(e,1e-6)/34.08))/le.PER_MAGNITUDE}static NANOLAMBERT_OFFSET=20.7233;static PER_MAGNITUDE=Math.LN10/2.5;static airMass(e){const t=Math.cos(Math.max(e,0)*Math.PI/180);return 1/Math.sqrt(Math.max(1-.96*t*t,.001))}static EXTINCTION_PER_AIR_MASS=.172;static scatteringAt(e){const t=Math.min(Math.max(e,0),180)*Math.PI/180;return 10**5.36*(1.06+Math.cos(t)**2)+10**(6.15-Math.min(e,180)/40)}static scatteredFraction(e){return 1-10**(-.4*le.EXTINCTION_PER_AIR_MASS*le.airMass(e))}static AIRGLOW_MAG_PER_ARCSEC2=22;static twilightExcessNanolamberts(e){return Math.max(0,le.toNanolamberts(le.moonlessMagPerArcsec2(e))-le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2))}static twilightOutput(e){const t=le.scatteringAt(90-e)*le.scatteredFraction(90);return le.twilightExcessNanolamberts(e)/t}static moonNanolamberts(e,t,i,n){return le.scatteringAt(i)*le.moonOutput(e,t)*le.scatteredFraction(n)}static moonOutput(e,t){if(t<=0)return 0;const i=Math.min(Math.abs(e),180);return 10**(-.4*(3.84+.026*i+4e-9*i**4))*10**(-.4*le.EXTINCTION_PER_AIR_MASS*le.airMass(t))}static phaseAngleOf(e){return Math.acos(Math.min(Math.max(2*e-1,-1),1))*180/Math.PI}static magPerArcsec2(e,t,i,n){const s=le.scatteredFraction(i),r=le.scatteringAt(e.separationDeg)*le.twilightOutput(e.altitudeDeg)*s,o=le.scatteringAt(t.separationDeg)*le.moonOutput(t.phaseAngleDeg,t.altitudeDeg)*s;return le.fromNanolamberts(le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2)+r+o+le.artificialNanolamberts(n,i))}static artificialZenithNanolamberts(e){return e===void 0||!Number.isFinite(e)?0:Math.max(0,le.toNanolamberts(e)-le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2))}static artificialNanolamberts(e,t){const i=le.artificialZenithNanolamberts(e);return i>0?i*le.artificialShape(t):0}static artificialShape(e){return le.scatteredFraction(e)/le.scatteredFraction(90)}static nakedEyeLimit(e){return 7.93-5*Math.log10(10**(4.316-e/5)+1)}static starLossMagnitudes(e,t,i){const n=le.moonlessMagPerArcsec2(e),s=le.fromNanolamberts(le.toNanolamberts(n)+le.scatteringAt(90-t.altitudeDeg)*le.moonOutput(t.phaseAngleDeg,t.altitudeDeg)*le.scatteredFraction(90)+le.artificialZenithNanolamberts(i));return Math.max(0,le.nakedEyeLimit(n)-le.nakedEyeLimit(s))}}class or{static RADIUS=890;static TOO_BRIGHT_MAG_PER_ARCSEC2=16;static MILKY_WAY_TINT=[.9,.94,1];static ZODIACAL_TINT=[1,.97,.9];object;material;maps=Gr.shared;milkyWayTexture;zodiacalTexture;milkyWayTexels;zodiacalTexels;repaint;constructor(){this.milkyWayTexels=this.maps.milkyWayTexels,this.milkyWayTexture=or.buildTexture(this.milkyWayTexels,st.LONGITUDE_STEPS,st.LATITUDE_STEPS,yl),this.zodiacalTexels=this.maps.zodiacalTexels,this.zodiacalTexture=or.buildTexture(this.zodiacalTexels,Yt.LONGITUDE_STEPS,Yt.LATITUDE_STEPS,Di),this.material=new dt({uniforms:{uMilkyWay:{value:this.milkyWayTexture},uZodiacal:{value:this.zodiacalTexture},uGalactic:{value:new Xe},uEclipticPole:{value:new R(0,1,0)},uSunLongitude:{value:new R(1,0,0)},uMoonDirection:{value:new R(0,-1,0)},uMoonOutput:{value:0},uSunDirection:{value:new R(0,-1,0)},uTwilightOutput:{value:0},uAirglowNanolamberts:{value:le.toNanolamberts(le.AIRGLOW_MAG_PER_ARCSEC2)},uArtificialNanolamberts:{value:0},uSkyColor:{value:new R(0,0,0)},uMilkyWayTint:{value:new R(...or.MILKY_WAY_TINT)},uZodiacalTint:{value:new R(...or.ZODIACAL_TINT)},uReady:{value:this.maps.done?1:0},uEncodeDestination:{value:1}},vertexShader:`
|
|
7465
7465
|
varying vec3 vDirection;
|
|
7466
7466
|
void main() {
|
|
7467
7467
|
vDirection = normalize((modelMatrix * vec4(position, 1.0)).xyz - cameraPosition);
|
|
@@ -8251,7 +8251,7 @@ void main()
|
|
|
8251
8251
|
if (ice <= 0.0) discard;
|
|
8252
8252
|
gl_FragColor = vec4(iceHaloLight(dir, ice), 1.0);
|
|
8253
8253
|
}
|
|
8254
|
-
`,transparent:!0,blending:Xi,depthWrite:!1,side:$t,fog:!1}),this.object=new De(new ui(Ni.RADIUS,64,32),this.material),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1;const i=this.material.uniforms;this.deckUniforms={uSource:i.uSource,uUp:i.uUp,uStrength:i.uStrength,uTint:i.uTint,uMap:i.uMap,uGain:i.uGain,uHaloShown:{value:0}}}deckUniforms;shown=!1;hostedByDeck=!1;set hosted(e){this.hostedByDeck=e,this.show(this.shown)}show(e){this.shown=e,this.object.visible=e&&!this.hostedByDeck,this.deckUniforms.uHaloShown.value=e?1:0}get awaitingFirstDisplay(){return this.tracing&&!this.everDisplayed}setUrgent(e){this.urgent=e}set onReady(e){this.onRepaint=e}update(e,t,i,n,s,r){const o=this.material.uniforms;if(i<=0){o.uStrength.value=0,this.show(!1),this.stopWork();return}o.uSource.value.set(e.x,e.y,e.z).normalize(),o.uStrength.value=i,o.uTint.value.set(n[0],n[1],n[2]),o.uIceCover.value=s.cover,o.uIceHeight.value=s.layerHeight,this.show(this.everDisplayed),this.requestMap(t,r)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Ni.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Ni.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)<Ni.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Ni.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)<Ni.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Ni.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?Ni.URGENT_BUDGET_MS:Ni.BUDGET_MS;do this.sky.trace(Ni.RAYS_PER_BATCH);while(this.sky.tracedRays<Ni.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Ni.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]=Wn.toHalfFloat(t[n]),this.texels[i+1]=Wn.toHalfFloat(t[n+1]),this.texels[i+2]=Wn.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 Ei{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Ei.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Ei.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=Ei.MAX_CONCURRENT*Ei.SEGMENTS*Ei.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new ht;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 Dn({vertexColors:!0,transparent:!0,blending:Xi,depthWrite:!1,fog:!1,side:ln})),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=ta.aliveAt(this.meteors,e).slice(0,Ei.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:r}of i){const o=this.radiantOf(s);if(!o)continue;const l=mi(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+r*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),f=Math.min(1,(1-r)*2.5),p=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*f,m=y=>v*y**1.6,g=y=>this.onSphere(l,p,u+(d-u)*y),w=y=>Ei.RADIUS*((Ei.MIN_HALF_WIDTH_DEG+Ei.BRIGHT_HALF_WIDTH_DEG*s.brightness*y)*Math.PI/180);for(let y=1;y<Ei.POINTS;y++){const b=(y-1)/Ei.SEGMENTS,x=y/Ei.SEGMENTS;n=this.pushRibbon(n,g(b),g(x),w(b),w(x),m(b),m(x))}}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=mi(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),r=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,r,e.fromRadiantDeg+e.lengthDeg/2),l=Math.hypot(o.x,o.y,o.z)||1;return{altitudeDeg:Math.asin(o.y/l)*180/Math.PI,azimuthDeg:(Math.atan2(o.x,-o.z)*180/Math.PI+360)%360}}radiantOf(e){return e.radiant?e.radiant:this.radiantAltitudeDeg>0?{altitudeDeg:this.radiantAltitudeDeg,azimuthDeg:this.radiantAzimuthDeg}:void 0}pushRibbon(e,t,i,n,s,r,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Ei.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,r)),e=this.push(e,f,m(h,o)),e=this.push(e,p,m(d,r)),e=this.push(e,f,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,p,m(d,r))}return e}across(e,t,i,n){return{x:e.x+t.x*i*n,y:e.y+t.y*i*n,z:e.z+t.z*i*n}}push(e,t,i){const n=e*3;this.positions[n]=t.x,this.positions[n+1]=t.y,this.positions[n+2]=t.z;const s=Ae.relativeOfResponse(Math.max(0,Math.min(1,i)));return this.colors[n]=s*.9,this.colors[n+1]=s*.95,this.colors[n+2]=s,e+1}turn(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:e.x*s+t.x*r,y:e.y*s+t.y*r,z:e.z*s+t.z*r}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:(e.x*s+t.x*r)*Ei.RADIUS,y:(e.y*s+t.y*r)*Ei.RADIUS,z:(e.z*s+t.z*r)*Ei.RADIUS}}basisAround(e){const t=Math.abs(e.y)<.9?{x:0,y:1,z:0}:{x:1,y:0,z:0},i=this.normalise(this.cross(e,t));return[i,this.normalise(this.cross(e,i))]}subtract(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}cross(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}normalise(e){const t=Math.hypot(e.x,e.y,e.z)||1;return{x:e.x/t,y:e.y/t,z:e.z/t}}dispose(){this.object.geometry.dispose(),this.object.material.dispose()}}class Ft{constructor(e,t={}){this.size=e,this.span=Math.min(e.lengthM,Math.max(0,t.spanM??e.lengthM)),this.bank=(e.lengthM-this.span)/2,this.deck=Math.min(e.heightM,t.deckThicknessM??Ft.DEFAULT_DECK_THICKNESS_M),this.railHeight=t.railing?.heightM??Ft.DEFAULT_RAILING_HEIGHT_M,this.postSpacing=Math.max(.3,t.railing?.postSpacingM??Ft.DEFAULT_POST_SPACING_M),this.rails=Math.max(0,Math.round(t.railing?.rails??Ft.DEFAULT_RAILS))}static DEFAULT_DECK_THICKNESS_M=1.2;static DEFAULT_RAILING_HEIGHT_M=1.05;static DEFAULT_POST_SPACING_M=1.5;static DEFAULT_RAILS=2;static BANK_SLOPE=1.5;static ABUTMENT_THICKNESS_M=1;static POST_SIDE_M=.08;static RAIL_HEIGHT_M=.08;static RAIL_DEPTH_M=.06;static BANK_STATIONS=12;static NATURAL_SIZE=Object.freeze({widthM:9,lengthM:40,heightM:6.5});span;bank;deck;railHeight;postSpacing;rails;roadAt(e){const t=Math.abs(e)-this.span/2;return t<=0?this.size.heightM:this.bank<=0?0:Math.max(0,this.size.heightM*(1-t/this.bank))}build(){const{widthM:e,heightM:t}=this.size,i=this.span/2,n=[];if(this.span>0){n.push(Ft.box(e,this.deck,this.span,0,t-this.deck/2,0));for(const l of[-1,1]){const c=t-this.deck;c>0&&this.bank>0&&n.push(Ft.box(e+1,c,Ft.ABUTMENT_THICKNESS_M,0,c/2,l*(i-Ft.ABUTMENT_THICKNESS_M/2)))}}const s=[];this.span>0&&s.push(Ft.box(e,.04,this.span,0,t+.02,0));const r=[];for(const l of[-1,1])this.bank<=0||(r.push(this.embankment(l)),s.push(this.bankRoad(l)));const o=[];for(const l of[-1,1])o.push(...this.railing(l*(e/2-Ft.POST_SIDE_M)));return{concrete:Ft.merge(n),road:Ft.merge(s),earth:Ft.merge(r),railing:Ft.merge(o)}}embankment(e){const t=this.size.widthM/2,i=this.stations(e),n=[],s=(c,h,d,u)=>{n.push(c.x,c.y,c.z,h.x,h.y,h.z,d.x,d.y,d.z,c.x,c.y,c.z,d.x,d.y,d.z,u.x,u.y,u.z)},r=c=>{const h=this.roadAt(c),d=t+Ft.BANK_SLOPE*h;return{leftFoot:new R(-d,0,c),leftTop:new R(-t,h,c),rightTop:new R(t,h,c),rightFoot:new R(d,0,c)}};for(let c=1;c<i.length;c++){const h=r(i[c-1]),d=r(i[c]),u=e>0,f=(p,v,m,g)=>u?s(p,v,m,g):s(g,m,v,p);f(h.leftFoot,h.leftTop,d.leftTop,d.leftFoot),f(h.leftTop,h.rightTop,d.rightTop,d.leftTop),f(h.rightTop,h.rightFoot,d.rightFoot,d.rightTop)}const o=r(i[0]);e>0?s(o.leftFoot,o.rightFoot,o.rightTop,o.leftTop):s(o.leftTop,o.rightTop,o.rightFoot,o.leftFoot);const l=new ht;return l.setAttribute("position",new gt(n,3)),l.computeVertexNormals(),l}bankRoad(e){const t=this.size.widthM/2,i=this.stations(e),n=[];for(let r=1;r<i.length;r++){const o=i[r-1],l=i[r],c=this.roadAt(o)+.02,h=this.roadAt(l)+.02,d=e>0?[[-t,c,o],[t,c,o],[t,h,l],[-t,h,l]]:[[-t,h,l],[t,h,l],[t,c,o],[-t,c,o]];for(const u of[0,2,1,0,3,2])n.push(...d[u])}const s=new ht;return s.setAttribute("position",new gt(n,3)),s.computeVertexNormals(),s}stations(e){const t=[];for(let i=0;i<=Ft.BANK_STATIONS;i++)t.push(e*(this.span/2+this.bank*i/Ft.BANK_STATIONS));return t}railing(e){const t=this.size.lengthM/2,i=Math.max(1,Math.round(this.size.lengthM/this.postSpacing)),n=[];for(let o=0;o<=i;o++)n.push(-t+this.size.lengthM*o/i);const s=[],r=Ft.POST_SIDE_M;for(const o of n)s.push(Ft.box(r,this.railHeight,r,e,this.roadAt(o)+this.railHeight/2,o));for(let o=1;o<=this.rails;o++){const l=this.railHeight*o/this.rails-Ft.RAIL_HEIGHT_M/2;for(let c=1;c<n.length;c++){const h=new R(e,this.roadAt(n[c-1])+l,n[c-1]),d=new R(e,this.roadAt(n[c])+l,n[c]);s.push(Ft.beam(h,d))}}return s}static box(e,t,i,n,s,r){return new Qi(e,t,i).translate(n,s,r)}static beam(e,t){const i=new R().subVectors(t,e),n=new Qi(Ft.RAIL_DEPTH_M,Ft.RAIL_HEIGHT_M,i.length()),s=new Wi().setFromUnitVectors(new R(0,0,1),i.clone().normalize()),r=new R().addVectors(e,t).multiplyScalar(.5);return n.applyMatrix4(new $e().compose(r,s,new R(1,1,1)))}static merge(e){const t=e.map(l=>l.getIndex()?l.toNonIndexed():l),i=t.reduce((l,c)=>l+c.getAttribute("position").count,0),n=new Float32Array(i*3),s=new Float32Array(i*3);let r=0;for(const l of t){const c=l.getAttribute("position"),h=l.getAttribute("normal");n.set(c.array,r*3),h&&s.set(h.array,r*3),r+=c.count}const o=new ht;o.setAttribute("position",new gt(n,3)),o.setAttribute("normal",new gt(s,3));for(const l of e)l.dispose();for(const l of t)l.dispose();return o}}const Gd=Math.PI/180;function zt(a,e,t,i,n=!1){const s=n?new Dn({color:new se(...t)}):new yo({color:new se(...t)}),r=new De(e,s);return r.position.y=i,r.userData={emissive:n},r.castShadow=!n,r.receiveShadow=!n,a.add(r),r}const Vd=.25,tA=.002,iA=100,nA=1500,sA=.25,rA=1e3,aA=.15;function oA(a){const e=/^#([0-9a-f]{6})$/i.exec(a);if(!e)return[1,1,1];const t=parseInt(e[1],16);return[(t>>16&255)/255,(t>>8&255)/255,(t&255)/255]}const lA=[.32,.32,.3],hb=[1,.85,.5],cA=[.25,.25,.25],db=[1,.97,.85],hA=[.55,.68,.72],ls=.03,cs=.05,ub={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 Ki(a,e){return a?.[e]}function hs(a,e,t,i,n,s,r,o){if(o===void 0)return;const l=new yo({color:new se(...hA),transparent:!0,opacity:o/100}),c=new De(new Qi(e,t,i),l);c.position.set(n,s,r),a.add(c)}const An=.05,Wd=[.6,.58,.53],gg=[.5,.5,.48];function Ya(a,e,t,i,n,s,r,o){const l=new yo({color:new se(...o),side:$t}),c=new De(new Qi(e,t,i),l);c.position.set(n,s,r),a.add(c)}function dA(a,e,t,i,n,s,r,o,l){const c=(i-s)/2,h=(p,v,m,g)=>{p<=0||(e?Ya(a,p,v,An,m,o+g,r,l):Ya(a,An,v,p,r,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((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 fb(a,e,t,i,n,s,r,o,l,c){const h=(i+n)/2,d=n-i,u=(f,p,v,m)=>{f.length===0?p?Ya(a,v,d,An,0,h,m,Wd):Ya(a,An,d,v,m,h,0,Wd):dA(a,p,v,d,f,s,m,h,Wd)};u(r,!0,e*2,-t+An),u(o,!0,e*2,t-An),u(l,!1,t*2,-e+An),u(c,!1,t*2,e-An),Ya(a,e*2,An,t*2,0,i+An,0,gg),Ya(a,e*2,An,t*2,0,n-An,0,gg)}function qf(a,e,t,i){switch(a){case"front":return{x:0,z:-(t-i)};case"behind":return{x:0,z:t-i};case"left":return{x:-(e-i),z:0};case"right":return{x:e-i,z:0};case"front-left":return{x:-(e-i),z:-ds};case"front-right":return{x:e-i,z:-ds};case"behind-left":return{x:-(e-i),z:ds};case"behind-right":return{x:e-i,z:ds}}}function pb(a,e,t,i,n,s,r){const o=Zf();o.scale.setScalar(i),o.rotation.y=ub[e];const{x:l,z:c}=qf(e,n,s,r);o.position.set(l,t,c),a.add(o)}const $f=6,Kf=6,cr=3,Bo=1.4,zo=cr*.5,mb=1.2,gb=.35,uA=.75,fA=1.6;function vg(a,e,t,i){const n=new Ot,s=Math.max(1,a+1),r=s*cr;zt(n,new Qi($f,r,Kf),[.55,.54,.5],r/2);const o=$f/2,l=Kf/2;for(let c=0;c<s;c++){const h=c*cr+cr/2;hs(n,Bo,zo,cs,0,h,-l-ls,Ki(e,"front")),hs(n,Bo,zo,cs,0,h,l+ls,Ki(e,"behind")),hs(n,cs,zo,Bo,-o-ls,h,0,Ki(e,"left")),hs(n,cs,zo,Bo,o+ls,h,0,Ki(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);pb(n,t,c*cr,.72,o,l,mb);const h=d=>Ki(e,d)!==void 0?[{main:Bo,center:0}]:[];fb(n,o,l,c*cr,(c+1)*cr,zo,h("front"),h("behind"),h("left"),h("right"))}return n}function bg(){const a=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 r=new ui(n,10,7);r.scale(1,.85,1),r.translate(t,0,i),zt(a,r,[.26,.28,.17],s)}return a}function pA(a){const e=new Ot,t=new Ft(ci.sizeOf(a),a.bridge).build();zt(e,t.concrete,[.6,.6,.58],0);const i=zt(e,t.road,[.2,.2,.21],0),n=zt(e,t.earth,[.3,.36,.22],0);for(const s of[i,n])s.material.side=ln;return zt(e,t.railing,[.5,.52,.55],0),e}function yg(){const a=new Ot;return zt(a,new xs(.15,.22,2,8),[.32,.22,.14],1),zt(a,new bo(1.4,3,8),[.16,.32,.14],3.5),a}const Mg=.275,mA=.35,gA=.9,vA=34,bA=8,yA=1.4;function wg(){const a=new Ot,e=new ui(Mg,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,mA/Mg,1);const t=ci.repeatOnGrid(e,vA,gA,bA,yA),i=zt(a,t,[.26,.28,.22],0);return i.castShadow=!0,i.userData.cropBasePositions=t.getAttribute("position").array.slice(),i.userData.cropVerticesPerClump=e.getAttribute("position").count,e.dispose(),a}function xg(){const a=new Ot,e=new ui(1,7,3,0,Math.PI*2,0,Math.PI/2);return zt(a,e,[.38,.36,.32],0),a}function Sg(a){const e=new Ot;return zt(e,new xs(.05,.08,5,8),[.28,.28,.3],2.5),zt(e,new ui(.25,12,8),a?hb:lA,5.1,!0),e}const MA=[.08,.08,.08],rr=.8,Ua=1,es=1.7,Xd=.6,Dr=.4,Sc=1.2,La=.7,ds=.5,wA=es;function Eg(a,e,t){const i=new Ot;zt(i,new Qi(1.8,1.4,4.2),[.45,.14,.14],.7),zt(i,new Qi(rr*2,Xd,Ua*2),[.4,.12,.12],es);for(const s of[-1.4,1.4])for(const r of[-.95,.95]){const o=zt(i,new xs(.4,.4,.3,12),MA,.4);o.rotation.z=Math.PI/2,o.position.x=r,o.position.z=s}const n=a?db:cA;for(const s of[-.7,.7])zt(i,new ui(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(hs(i,Sc,Dr,cs,0,es,-Ua-ls,Ki(e,"front")),hs(i,Sc,Dr,cs,0,es,Ua+ls,Ki(e,"behind")),hs(i,cs,Dr,La,-rr-ls,es,-ds,Ki(e,"front-left")),hs(i,cs,Dr,La,-rr-ls,es,ds,Ki(e,"behind-left")),hs(i,cs,Dr,La,rr+ls,es,-ds,Ki(e,"front-right")),hs(i,cs,Dr,La,rr+ls,es,ds,Ki(e,"behind-right")),t){pb(i,t,uA,.5,rr,Ua,gb);const s=(l,c)=>{const h=[];return Ki(e,l)!==void 0&&h.push({main:La,center:-ds}),Ki(e,c)!==void 0&&h.push({main:La,center:ds}),h},r=Ki(e,"front")!==void 0?[{main:Sc,center:0}]:[],o=Ki(e,"behind")!==void 0?[{main:Sc,center:0}]:[];fb(i,rr,Ua,es-Xd/2,es+Xd/2,Dr,r,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const _g=new Map;function Ag(a,e){if(!a)return;const t=[[a.lengthM,e.lengthM],[a.heightM,e.heightM],[a.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function Yd(a,e){return e>1e-6?a/e:1}const vb=[.95,.9,.82],Tg=[.72,.74,.78];function Ig(){const a=new Ot;zt(a,new xs(.25,.3,1.5,10),Tg,.75),zt(a,new ui(.26,10,8),Tg,1.74);const e=zt(a,new bo(.07,.16,8),vb,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,a}function Zf(){const a=new Ot;zt(a,new xs(.25,.3,1.5,10),[.3,.3,.35],.75),zt(a,new ui(.22,10,8),[.62,.52,.46],1.72);const e=zt(a,new bo(.07,.16,8),vb,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,a}const xA=17,Dg=18.5,qd=[.16,.16,.18];function Rg(){const a=new Ot,e=zt(a,new xs(1.8,1.8,Dg*2,10),qd,0);e.rotation.x=Math.PI/2,zt(a,new Qi(xA*2,.5,4.5),qd,0);const t=zt(a,new Qi(.4,5.5,4),qd,3);return t.position.z=-Dg+2,a}class ci{static GROWS_FROM_GROUND=new Set(["tree","shrub","crop","mound"]);static groundUnderFootprint(e,t,i,n,s,r=0){const o=ci.sizeOf(e);if(r>o.heightM)return s(t,i);const l=(n??0)*Math.PI/180,c=Math.min(21,Math.max(2,Math.ceil(o.lengthM/.75)+1)),h=Math.min(21,Math.max(2,Math.ceil(o.widthM/.75)+1)),d=ci.GROWS_FROM_GROUND.has(e.kind);let u=d?1/0:-1/0;for(let f=0;f<c;f++){const p=(f/(c-1)-.5)*o.lengthM;for(let v=0;v<h;v++){const m=(v/(h-1)-.5)*o.widthM,g=t+Math.sin(l)*p+Math.cos(l)*m,w=i-Math.cos(l)*p+Math.sin(l)*m;u=d?Math.min(u,s(g,w)):Math.max(u,s(g,w))}}return Number.isFinite(u)?u:s(t,i)}static occupantView(e){const t=e.observerSide;if(!t)throw new Error("occupantView requires object.observerSide to be set");const{x:i,z:n}=e.kind==="vehicle"?qf(t,rr,Ua,gb):qf(t,$f/2,Kf/2,mb),s=e.kind==="vehicle"?wA:(e.occupiedFloor??0)*cr+fA,r=(e.headingDeg??0)-ub[t]/Gd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:r}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ci.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const r=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=r.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),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 y=0;y<t;y++,m++){const b=v+w*s,x=p+y*i;for(let E=0;E<c;E++){const I=(m*c+E)*3;d[I]=r.getX(E)+b,d[I+1]=r.getY(E),d[I+2]=r.getZ(E)+x,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 ht;return g.setAttribute("position",new gt(d,3)),g.setAttribute("normal",new gt(u,3)),g.setIndex(new xp(f,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,r=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!r||!o)return;const l=s.geometry.getAttribute("position"),c=new $e().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(r[h*3],0,r[h*3+2]).applyMatrix4(s.matrixWorld),i.y=t(i.x,i.z),i.applyMatrix4(c);for(let d=h;d<h+o;d++)l.setY(d,r[d*3+1]+i.y)}l.needsUpdate=!0,s.geometry.computeBoundingBox(),s.geometry.computeBoundingSphere()})}static paint(e,t){e.traverse(i=>{const n=i,s=n.userData;if(!n.isMesh||s?.emissive)return;const r=n.material;r?.color&&r.color.set(t)})}static build(e,t){const i=e.kind==="building"?vg(e.floors??Ka,e.windows,e.observerSide,e.occupiedFloor):e.kind==="tree"?yg():e.kind==="shrub"?bg():e.kind==="bridge"?pA(e):e.kind==="crop"?wg():e.kind==="mound"?xg():e.kind==="streetlight"?Sg(t):e.kind==="vehicle"?Eg(t,e.windows,e.observerSide):e.kind==="aircraft"?Rg():e.kind==="entity"?Ig():Zf();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Ot;return i.name=ci.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*Gd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t||e.kind==="bridge")return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:Yd(t.widthM,i.widthM),t.heightM===void 0?1:Yd(t.heightM,i.heightM),t.lengthM===void 0?1:Yd(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Ka)+1):0;if(e==="bridge")return Ft.NATURAL_SIZE;const n=`${e}:${i}`,s=_g.get(n);if(s)return s;const r=e==="building"?vg(i-1,void 0,void 0,void 0):e==="tree"?yg():e==="shrub"?bg():e==="crop"?wg():e==="mound"?xg():e==="streetlight"?Sg(!1):e==="vehicle"?Eg(!1,void 0,void 0):e==="aircraft"?Rg():e==="entity"?Ig():Zf(),o=new Ji().setFromObject(r),l={widthM:o.max.x-o.min.x,heightM:o.max.y-o.min.y,lengthM:o.max.z-o.min.z};return this.dispose(r),_g.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.observerSide!==void 0&&za(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Ot;r.name=ci.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*Gd,i.updateMatrixWorld(!0);const o=new Ji().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=Ag(t.sizeM,l)??Ag(n.depictedSizeM,l)??1;i.scale.multiplyScalar(c);const h=o.getCenter(new R).multiplyScalar(c);i.position.x-=h.x,i.position.z-=h.z,i.position.y-=t.kind==="aircraft"?h.y:o.min.y*c,i.traverse(d=>{d instanceof De&&(d.castShadow=!0,d.receiveShadow=!0)}),r.add(i),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s)}static sizeOf(e){const t=this.naturalSize(e.kind,e.floors);return{widthM:e.sizeM?.widthM??t.widthM,lengthM:e.sizeM?.lengthM??t.lengthM,heightM:e.sizeM?.heightM??t.heightM}}static addLights(e,t){for(const i of t??[]){const n=oA(i.color),s=zt(e,new ui(Vd,8,6),n,i.offsetM.y,!0);s.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),s.userData={emissive:!0,lightId:i.id,lightColor:n},s.visible=!1}}static setLights(e,t,i,n,s=0,r=1,o=tA){if(!t||t.length===0)return;const l=new Map(t.map(u=>[u.id,u])),c=Math.max(1,n*o/Vd),h=Vd*c,d=r/(Math.PI*h*h);for(const u of e.children){if(!(u instanceof De))continue;const f=u.userData.lightId;if(!f)continue;const p=l.get(f);if(!p)continue;const v=s>0?Fv(p,i,i+s):Sy(p,i)?1:0;u.visible=v>0;const m=u.material,g=u.userData.lightColor;if(g){const w=v*iA*(p.intensity??1)*d;m.color.setRGB(g[0]*w,g[1]*w,g[2]*w)}u.scale.setScalar(c)}}static setLit(e,t,i,n=1){const[s,r,o]=t==="streetlight"?[nA,sA,hb]:[rA,aA,db],l=i?s*n/(Math.PI*r*r):0,c=[o[0]*l,o[1]*l,o[2]*l];e.traverse(h=>{const d=h.userData;!(h instanceof De)||!d.emissive||d.lightId||h.material.color.setRGB(...c)})}static dispose(e){e.traverse(t=>{if(!(t instanceof De))return;t.geometry.dispose();const i=t.material;for(const n of Array.isArray(i)?i:[i])n.dispose()})}}const $d="https://ufoathome.org/models/index.json";class SA{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??EA()}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 EA(){const a=`?v=${encodeURIComponent("0.75.1")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==$d?[e,$d]:[$d]).map(t=>t+a)}const qc=[{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 SA}];async function Cg(a){const{GLTFLoader:e}=await mt(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-DSbhUTN3.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(a,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${a}: ${s instanceof Error?s.message:String(s)}`)))})}const bb=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class bn{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 en(2,2),new dt({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:bn.VERTEX,fragmentShader:bn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Xi,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*bn.MIN_ANGLE_RAD),r=bn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(bn.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(bn.MAX_SHARE*bn.FILL*Math.PI))}static VERTEX=`
|
|
8254
|
+
`,transparent:!0,blending:Xi,depthWrite:!1,side:$t,fog:!1}),this.object=new De(new ui(Ni.RADIUS,64,32),this.material),this.object.renderOrder=-1,this.object.frustumCulled=!1,this.object.visible=!1;const i=this.material.uniforms;this.deckUniforms={uSource:i.uSource,uUp:i.uUp,uStrength:i.uStrength,uTint:i.uTint,uMap:i.uMap,uGain:i.uGain,uHaloShown:{value:0}}}deckUniforms;shown=!1;hostedByDeck=!1;set hosted(e){this.hostedByDeck=e,this.show(this.shown)}show(e){this.shown=e,this.object.visible=e&&!this.hostedByDeck,this.deckUniforms.uHaloShown.value=e?1:0}get awaitingFirstDisplay(){return this.tracing&&!this.everDisplayed}setUrgent(e){this.urgent=e}set onReady(e){this.onRepaint=e}update(e,t,i,n,s,r){const o=this.material.uniforms;if(i<=0){o.uStrength.value=0,this.show(!1),this.stopWork();return}o.uSource.value.set(e.x,e.y,e.z).normalize(),o.uStrength.value=i,o.uTint.value.set(n[0],n[1],n[2]),o.uIceCover.value=s.cover,o.uIceHeight.value=s.layerHeight,this.show(this.everDisplayed),this.requestMap(t,r)}requestMap(e,t){if(!(!(Math.abs(e-this.mappedAltitudeDeg)<Ni.ALTITUDE_STEP_DEG)||!(Math.abs(t-this.mappedAlignment)<Ni.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)<Ni.LEAP_ALTITUDE_DEG)||!(Math.abs(t-this.pendingAlignment)<Ni.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)<Ni.ALTITUDE_STEP_DEG&&Math.abs(t-this.pendingAlignment)<Ni.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?Ni.URGENT_BUDGET_MS:Ni.BUDGET_MS;do this.sky.trace(Ni.RAYS_PER_BATCH);while(this.sky.tracedRays<Ni.RAYS&&performance.now()-t<i);if(this.sky.tracedRays>=Ni.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]=Wn.toHalfFloat(t[n]),this.texels[i+1]=Wn.toHalfFloat(t[n+1]),this.texels[i+2]=Wn.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 Ei{static MAX_CONCURRENT=24;static POINTS=8;static SEGMENTS=Ei.POINTS-1;static ACROSS=3;static VERTICES_PER_SEGMENT=(Ei.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=Ei.MAX_CONCURRENT*Ei.SEGMENTS*Ei.VERTICES_PER_SEGMENT;this.positions=new Float32Array(e*3),this.colors=new Float32Array(e*3);const t=new ht;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 Dn({vertexColors:!0,transparent:!0,blending:Xi,depthWrite:!1,fog:!1,side:ln})),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=ta.aliveAt(this.meteors,e).slice(0,Ei.MAX_CONCURRENT);let n=0;for(const{meteor:s,progress:r}of i){const o=this.radiantOf(s);if(!o)continue;const l=mi(o.altitudeDeg,o.azimuthDeg,1),[c,h]=this.basisAround(l),d=s.fromRadiantDeg+r*s.lengthDeg,u=Math.max(s.fromRadiantDeg,d-s.lengthDeg*.6),f=Math.min(1,(1-r)*2.5),p=this.turn(c,h,s.bearingDeg),v=(.15+.85*s.brightness)*f,m=y=>v*y**1.6,g=y=>this.onSphere(l,p,u+(d-u)*y),w=y=>Ei.RADIUS*((Ei.MIN_HALF_WIDTH_DEG+Ei.BRIGHT_HALF_WIDTH_DEG*s.brightness*y)*Math.PI/180);for(let y=1;y<Ei.POINTS;y++){const b=(y-1)/Ei.SEGMENTS,x=y/Ei.SEGMENTS;n=this.pushRibbon(n,g(b),g(x),w(b),w(x),m(b),m(x))}}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=mi(t.altitudeDeg,t.azimuthDeg,1),[n,s]=this.basisAround(i),r=this.turn(n,s,e.bearingDeg),o=this.onSphere(i,r,e.fromRadiantDeg+e.lengthDeg/2),l=Math.hypot(o.x,o.y,o.z)||1;return{altitudeDeg:Math.asin(o.y/l)*180/Math.PI,azimuthDeg:(Math.atan2(o.x,-o.z)*180/Math.PI+360)%360}}radiantOf(e){return e.radiant?e.radiant:this.radiantAltitudeDeg>0?{altitudeDeg:this.radiantAltitudeDeg,azimuthDeg:this.radiantAzimuthDeg}:void 0}pushRibbon(e,t,i,n,s,r,o){const l=this.normalise(this.cross(this.normalise(t),this.normalise(this.subtract(i,t))));for(let c=0;c<Ei.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,r)),e=this.push(e,f,m(h,o)),e=this.push(e,p,m(d,r)),e=this.push(e,f,m(h,o)),e=this.push(e,v,m(d,o)),e=this.push(e,p,m(d,r))}return e}across(e,t,i,n){return{x:e.x+t.x*i*n,y:e.y+t.y*i*n,z:e.z+t.z*i*n}}push(e,t,i){const n=e*3;this.positions[n]=t.x,this.positions[n+1]=t.y,this.positions[n+2]=t.z;const s=Ae.relativeOfResponse(Math.max(0,Math.min(1,i)));return this.colors[n]=s*.9,this.colors[n+1]=s*.95,this.colors[n+2]=s,e+1}turn(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:e.x*s+t.x*r,y:e.y*s+t.y*r,z:e.z*s+t.z*r}}onSphere(e,t,i){const n=i*Math.PI/180,s=Math.cos(n),r=Math.sin(n);return{x:(e.x*s+t.x*r)*Ei.RADIUS,y:(e.y*s+t.y*r)*Ei.RADIUS,z:(e.z*s+t.z*r)*Ei.RADIUS}}basisAround(e){const t=Math.abs(e.y)<.9?{x:0,y:1,z:0}:{x:1,y:0,z:0},i=this.normalise(this.cross(e,t));return[i,this.normalise(this.cross(e,i))]}subtract(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}cross(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}normalise(e){const t=Math.hypot(e.x,e.y,e.z)||1;return{x:e.x/t,y:e.y/t,z:e.z/t}}dispose(){this.object.geometry.dispose(),this.object.material.dispose()}}class Ft{constructor(e,t={}){this.size=e,this.span=Math.min(e.lengthM,Math.max(0,t.spanM??e.lengthM)),this.bank=(e.lengthM-this.span)/2,this.deck=Math.min(e.heightM,t.deckThicknessM??Ft.DEFAULT_DECK_THICKNESS_M),this.railHeight=t.railing?.heightM??Ft.DEFAULT_RAILING_HEIGHT_M,this.postSpacing=Math.max(.3,t.railing?.postSpacingM??Ft.DEFAULT_POST_SPACING_M),this.rails=Math.max(0,Math.round(t.railing?.rails??Ft.DEFAULT_RAILS))}static DEFAULT_DECK_THICKNESS_M=1.2;static DEFAULT_RAILING_HEIGHT_M=1.05;static DEFAULT_POST_SPACING_M=1.5;static DEFAULT_RAILS=2;static BANK_SLOPE=1.5;static ABUTMENT_THICKNESS_M=1;static POST_SIDE_M=.08;static RAIL_HEIGHT_M=.08;static RAIL_DEPTH_M=.06;static BANK_STATIONS=12;static NATURAL_SIZE=Object.freeze({widthM:9,lengthM:40,heightM:6.5});span;bank;deck;railHeight;postSpacing;rails;roadAt(e){const t=Math.abs(e)-this.span/2;return t<=0?this.size.heightM:this.bank<=0?0:Math.max(0,this.size.heightM*(1-t/this.bank))}build(){const{widthM:e,heightM:t}=this.size,i=this.span/2,n=[];if(this.span>0){n.push(Ft.box(e,this.deck,this.span,0,t-this.deck/2,0));for(const l of[-1,1]){const c=t-this.deck;c>0&&this.bank>0&&n.push(Ft.box(e+1,c,Ft.ABUTMENT_THICKNESS_M,0,c/2,l*(i-Ft.ABUTMENT_THICKNESS_M/2)))}}const s=[];this.span>0&&s.push(Ft.box(e,.04,this.span,0,t+.02,0));const r=[];for(const l of[-1,1])this.bank<=0||(r.push(this.embankment(l)),s.push(this.bankRoad(l)));const o=[];for(const l of[-1,1])o.push(...this.railing(l*(e/2-Ft.POST_SIDE_M)));return{concrete:Ft.merge(n),road:Ft.merge(s),earth:Ft.merge(r),railing:Ft.merge(o)}}embankment(e){const t=this.size.widthM/2,i=this.stations(e),n=[],s=(c,h,d,u)=>{n.push(c.x,c.y,c.z,h.x,h.y,h.z,d.x,d.y,d.z,c.x,c.y,c.z,d.x,d.y,d.z,u.x,u.y,u.z)},r=c=>{const h=this.roadAt(c),d=t+Ft.BANK_SLOPE*h;return{leftFoot:new R(-d,0,c),leftTop:new R(-t,h,c),rightTop:new R(t,h,c),rightFoot:new R(d,0,c)}};for(let c=1;c<i.length;c++){const h=r(i[c-1]),d=r(i[c]),u=e>0,f=(p,v,m,g)=>u?s(p,v,m,g):s(g,m,v,p);f(h.leftFoot,h.leftTop,d.leftTop,d.leftFoot),f(h.leftTop,h.rightTop,d.rightTop,d.leftTop),f(h.rightTop,h.rightFoot,d.rightFoot,d.rightTop)}const o=r(i[0]);e>0?s(o.leftFoot,o.rightFoot,o.rightTop,o.leftTop):s(o.leftTop,o.rightTop,o.rightFoot,o.leftFoot);const l=new ht;return l.setAttribute("position",new gt(n,3)),l.computeVertexNormals(),l}bankRoad(e){const t=this.size.widthM/2,i=this.stations(e),n=[];for(let r=1;r<i.length;r++){const o=i[r-1],l=i[r],c=this.roadAt(o)+.02,h=this.roadAt(l)+.02,d=e>0?[[-t,c,o],[t,c,o],[t,h,l],[-t,h,l]]:[[-t,h,l],[t,h,l],[t,c,o],[-t,c,o]];for(const u of[0,2,1,0,3,2])n.push(...d[u])}const s=new ht;return s.setAttribute("position",new gt(n,3)),s.computeVertexNormals(),s}stations(e){const t=[];for(let i=0;i<=Ft.BANK_STATIONS;i++)t.push(e*(this.span/2+this.bank*i/Ft.BANK_STATIONS));return t}railing(e){const t=this.size.lengthM/2,i=Math.max(1,Math.round(this.size.lengthM/this.postSpacing)),n=[];for(let o=0;o<=i;o++)n.push(-t+this.size.lengthM*o/i);const s=[],r=Ft.POST_SIDE_M;for(const o of n)s.push(Ft.box(r,this.railHeight,r,e,this.roadAt(o)+this.railHeight/2,o));for(let o=1;o<=this.rails;o++){const l=this.railHeight*o/this.rails-Ft.RAIL_HEIGHT_M/2;for(let c=1;c<n.length;c++){const h=new R(e,this.roadAt(n[c-1])+l,n[c-1]),d=new R(e,this.roadAt(n[c])+l,n[c]);s.push(Ft.beam(h,d))}}return s}static box(e,t,i,n,s,r){return new Qi(e,t,i).translate(n,s,r)}static beam(e,t){const i=new R().subVectors(t,e),n=new Qi(Ft.RAIL_DEPTH_M,Ft.RAIL_HEIGHT_M,i.length()),s=new Wi().setFromUnitVectors(new R(0,0,1),i.clone().normalize()),r=new R().addVectors(e,t).multiplyScalar(.5);return n.applyMatrix4(new $e().compose(r,s,new R(1,1,1)))}static merge(e){const t=e.map(l=>l.getIndex()?l.toNonIndexed():l),i=t.reduce((l,c)=>l+c.getAttribute("position").count,0),n=new Float32Array(i*3),s=new Float32Array(i*3);let r=0;for(const l of t){const c=l.getAttribute("position"),h=l.getAttribute("normal");n.set(c.array,r*3),h&&s.set(h.array,r*3),r+=c.count}const o=new ht;o.setAttribute("position",new gt(n,3)),o.setAttribute("normal",new gt(s,3));for(const l of e)l.dispose();for(const l of t)l.dispose();return o}}const Gd=Math.PI/180;function zt(a,e,t,i,n=!1){const s=n?new Dn({color:new se(...t)}):new yo({color:new se(...t)}),r=new De(e,s);return r.position.y=i,r.userData={emissive:n},r.castShadow=!n,r.receiveShadow=!n,a.add(r),r}const Vd=.25,tA=.002,iA=100,nA=1500,sA=.25,rA=1e3,aA=.15;function oA(a){const e=/^#([0-9a-f]{6})$/i.exec(a);if(!e)return[1,1,1];const t=parseInt(e[1],16);return[(t>>16&255)/255,(t>>8&255)/255,(t&255)/255]}const lA=[.32,.32,.3],hb=[1,.85,.5],cA=[.25,.25,.25],db=[1,.97,.85],hA=[.55,.68,.72],ls=.03,cs=.05,ub={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 Ki(a,e){return a?.[e]}function hs(a,e,t,i,n,s,r,o){if(o===void 0)return;const l=new yo({color:new se(...hA),transparent:!0,opacity:o/100}),c=new De(new Qi(e,t,i),l);c.position.set(n,s,r),a.add(c)}const An=.05,Wd=[.6,.58,.53],gg=[.5,.5,.48];function Ya(a,e,t,i,n,s,r,o){const l=new yo({color:new se(...o),side:$t}),c=new De(new Qi(e,t,i),l);c.position.set(n,s,r),a.add(c)}function dA(a,e,t,i,n,s,r,o,l){const c=(i-s)/2,h=(p,v,m,g)=>{p<=0||(e?Ya(a,p,v,An,m,o+g,r,l):Ya(a,An,v,p,r,o+g,m,l))};h(t,c,0,i/2-c/2),h(t,c,0,-(i/2-c/2));const d=[...n].sort((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 fb(a,e,t,i,n,s,r,o,l,c){const h=(i+n)/2,d=n-i,u=(f,p,v,m)=>{f.length===0?p?Ya(a,v,d,An,0,h,m,Wd):Ya(a,An,d,v,m,h,0,Wd):dA(a,p,v,d,f,s,m,h,Wd)};u(r,!0,e*2,-t+An),u(o,!0,e*2,t-An),u(l,!1,t*2,-e+An),u(c,!1,t*2,e-An),Ya(a,e*2,An,t*2,0,i+An,0,gg),Ya(a,e*2,An,t*2,0,n-An,0,gg)}function qf(a,e,t,i){switch(a){case"front":return{x:0,z:-(t-i)};case"behind":return{x:0,z:t-i};case"left":return{x:-(e-i),z:0};case"right":return{x:e-i,z:0};case"front-left":return{x:-(e-i),z:-ds};case"front-right":return{x:e-i,z:-ds};case"behind-left":return{x:-(e-i),z:ds};case"behind-right":return{x:e-i,z:ds}}}function pb(a,e,t,i,n,s,r){const o=Zf();o.scale.setScalar(i),o.rotation.y=ub[e];const{x:l,z:c}=qf(e,n,s,r);o.position.set(l,t,c),a.add(o)}const $f=6,Kf=6,cr=3,Bo=1.4,zo=cr*.5,mb=1.2,gb=.35,uA=.75,fA=1.6;function vg(a,e,t,i){const n=new Ot,s=Math.max(1,a+1),r=s*cr;zt(n,new Qi($f,r,Kf),[.55,.54,.5],r/2);const o=$f/2,l=Kf/2;for(let c=0;c<s;c++){const h=c*cr+cr/2;hs(n,Bo,zo,cs,0,h,-l-ls,Ki(e,"front")),hs(n,Bo,zo,cs,0,h,l+ls,Ki(e,"behind")),hs(n,cs,zo,Bo,-o-ls,h,0,Ki(e,"left")),hs(n,cs,zo,Bo,o+ls,h,0,Ki(e,"right"))}if(t){const c=Math.min(Math.max(i??0,0),s-1);pb(n,t,c*cr,.72,o,l,mb);const h=d=>Ki(e,d)!==void 0?[{main:Bo,center:0}]:[];fb(n,o,l,c*cr,(c+1)*cr,zo,h("front"),h("behind"),h("left"),h("right"))}return n}function bg(){const a=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 r=new ui(n,10,7);r.scale(1,.85,1),r.translate(t,0,i),zt(a,r,[.26,.28,.17],s)}return a}function pA(a){const e=new Ot,t=new Ft(ci.sizeOf(a),a.bridge).build();zt(e,t.concrete,[.6,.6,.58],0);const i=zt(e,t.road,[.2,.2,.21],0),n=zt(e,t.earth,[.3,.36,.22],0);for(const s of[i,n])s.material.side=ln;return zt(e,t.railing,[.5,.52,.55],0),e}function yg(){const a=new Ot;return zt(a,new xs(.15,.22,2,8),[.32,.22,.14],1),zt(a,new bo(1.4,3,8),[.16,.32,.14],3.5),a}const Mg=.275,mA=.35,gA=.9,vA=34,bA=8,yA=1.4;function wg(){const a=new Ot,e=new ui(Mg,7,3,0,Math.PI*2,0,Math.PI/2);e.scale(1,mA/Mg,1);const t=ci.repeatOnGrid(e,vA,gA,bA,yA),i=zt(a,t,[.26,.28,.22],0);return i.castShadow=!0,i.userData.cropBasePositions=t.getAttribute("position").array.slice(),i.userData.cropVerticesPerClump=e.getAttribute("position").count,e.dispose(),a}function xg(){const a=new Ot,e=new ui(1,7,3,0,Math.PI*2,0,Math.PI/2);return zt(a,e,[.38,.36,.32],0),a}function Sg(a){const e=new Ot;return zt(e,new xs(.05,.08,5,8),[.28,.28,.3],2.5),zt(e,new ui(.25,12,8),a?hb:lA,5.1,!0),e}const MA=[.08,.08,.08],rr=.8,Ua=1,es=1.7,Xd=.6,Dr=.4,Sc=1.2,La=.7,ds=.5,wA=es;function Eg(a,e,t){const i=new Ot;zt(i,new Qi(1.8,1.4,4.2),[.45,.14,.14],.7),zt(i,new Qi(rr*2,Xd,Ua*2),[.4,.12,.12],es);for(const s of[-1.4,1.4])for(const r of[-.95,.95]){const o=zt(i,new xs(.4,.4,.3,12),MA,.4);o.rotation.z=Math.PI/2,o.position.x=r,o.position.z=s}const n=a?db:cA;for(const s of[-.7,.7])zt(i,new ui(.15,8,6),n,.7,!0).position.set(s,.7,-2.1);if(hs(i,Sc,Dr,cs,0,es,-Ua-ls,Ki(e,"front")),hs(i,Sc,Dr,cs,0,es,Ua+ls,Ki(e,"behind")),hs(i,cs,Dr,La,-rr-ls,es,-ds,Ki(e,"front-left")),hs(i,cs,Dr,La,-rr-ls,es,ds,Ki(e,"behind-left")),hs(i,cs,Dr,La,rr+ls,es,-ds,Ki(e,"front-right")),hs(i,cs,Dr,La,rr+ls,es,ds,Ki(e,"behind-right")),t){pb(i,t,uA,.5,rr,Ua,gb);const s=(l,c)=>{const h=[];return Ki(e,l)!==void 0&&h.push({main:La,center:-ds}),Ki(e,c)!==void 0&&h.push({main:La,center:ds}),h},r=Ki(e,"front")!==void 0?[{main:Sc,center:0}]:[],o=Ki(e,"behind")!==void 0?[{main:Sc,center:0}]:[];fb(i,rr,Ua,es-Xd/2,es+Xd/2,Dr,r,o,s("front-left","behind-left"),s("front-right","behind-right"))}return i}const _g=new Map;function Ag(a,e){if(!a)return;const t=[[a.lengthM,e.lengthM],[a.heightM,e.heightM],[a.widthM,e.widthM]];for(const[i,n]of t)if(i!==void 0&&n>1e-6)return i/n}function Yd(a,e){return e>1e-6?a/e:1}const vb=[.95,.9,.82],Tg=[.72,.74,.78];function Ig(){const a=new Ot;zt(a,new xs(.25,.3,1.5,10),Tg,.75),zt(a,new ui(.26,10,8),Tg,1.74);const e=zt(a,new bo(.07,.16,8),vb,1.74);return e.rotation.x=-Math.PI/2,e.position.z=-.22,a}function Zf(){const a=new Ot;zt(a,new xs(.25,.3,1.5,10),[.3,.3,.35],.75),zt(a,new ui(.22,10,8),[.62,.52,.46],1.72);const e=zt(a,new bo(.07,.16,8),vb,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,a}const xA=17,Dg=18.5,qd=[.16,.16,.18];function Rg(){const a=new Ot,e=zt(a,new xs(1.8,1.8,Dg*2,10),qd,0);e.rotation.x=Math.PI/2,zt(a,new Qi(xA*2,.5,4.5),qd,0);const t=zt(a,new Qi(.4,5.5,4),qd,3);return t.position.z=-Dg+2,a}class ci{static GROWS_FROM_GROUND=new Set(["tree","shrub","crop","mound"]);static groundUnderFootprint(e,t,i,n,s,r=0){const o=ci.sizeOf(e);if(r>o.heightM)return s(t,i);const l=(n??0)*Math.PI/180,c=Math.min(21,Math.max(2,Math.ceil(o.lengthM/.75)+1)),h=Math.min(21,Math.max(2,Math.ceil(o.widthM/.75)+1)),d=ci.GROWS_FROM_GROUND.has(e.kind);let u=d?1/0:-1/0;for(let f=0;f<c;f++){const p=(f/(c-1)-.5)*o.lengthM;for(let v=0;v<h;v++){const m=(v/(h-1)-.5)*o.widthM,g=t+Math.sin(l)*p+Math.cos(l)*m,w=i-Math.cos(l)*p+Math.sin(l)*m;u=d?Math.min(u,s(g,w)):Math.max(u,s(g,w))}}return Number.isFinite(u)?u:s(t,i)}static occupantView(e){const t=e.observerSide;if(!t)throw new Error("occupantView requires object.observerSide to be set");const{x:i,z:n}=e.kind==="vehicle"?qf(t,rr,Ua,gb):qf(t,$f/2,Kf/2,mb),s=e.kind==="vehicle"?wA:(e.occupiedFloor??0)*cr+fA,r=(e.headingDeg??0)-ub[t]/Gd,o=this.scaleFor(e);return{x:i*o.x,z:n*o.z,eyeY:s*o.y,headingDeg:r}}static BODY_NAME="decor-body";static bodyOf(e){return e.getObjectByName(ci.BODY_NAME)??e}static repeatOnGrid(e,t,i,n,s){const r=e.getAttribute("position"),o=e.getAttribute("normal"),l=e.getIndex();if(!l)throw new Error("repeatOnGrid needs an indexed geometry");const c=r.count,h=t*n,d=new Float32Array(c*h*3),u=new Float32Array(c*h*3),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 y=0;y<t;y++,m++){const b=v+w*s,x=p+y*i;for(let E=0;E<c;E++){const I=(m*c+E)*3;d[I]=r.getX(E)+b,d[I+1]=r.getY(E),d[I+2]=r.getZ(E)+x,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 ht;return g.setAttribute("position",new gt(d,3)),g.setAttribute("normal",new gt(u,3)),g.setIndex(new xp(f,1)),g}static fitCropToGround(e,t){e.updateMatrixWorld(!0);const i=new R;e.traverse(n=>{const s=n,r=s.userData.cropBasePositions,o=s.userData.cropVerticesPerClump;if(!s.isMesh||!r||!o)return;const l=s.geometry.getAttribute("position"),c=new $e().copy(s.matrixWorld).invert();for(let h=0;h<l.count;h+=o){i.set(r[h*3],0,r[h*3+2]).applyMatrix4(s.matrixWorld),i.y=t(i.x,i.z),i.applyMatrix4(c);for(let d=h;d<h+o;d++)l.setY(d,r[d*3+1]+i.y)}l.needsUpdate=!0,s.geometry.computeBoundingBox(),s.geometry.computeBoundingSphere()})}static paint(e,t){e.traverse(i=>{const n=i,s=n.userData;if(!n.isMesh||s?.emissive)return;const r=n.material;r?.color&&r.color.set(t)})}static build(e,t){const i=e.kind==="building"?vg(e.floors??Ka,e.windows,e.observerSide,e.occupiedFloor):e.kind==="tree"?yg():e.kind==="shrub"?bg():e.kind==="bridge"?pA(e):e.kind==="crop"?wg():e.kind==="mound"?xg():e.kind==="streetlight"?Sg(t):e.kind==="vehicle"?Eg(t,e.windows,e.observerSide):e.kind==="aircraft"?Rg():e.kind==="entity"?Ig():Zf();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Ot;return i.name=ci.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*Gd),this.addLights(s,e.lights),s}static scaleFor(e){const t=e.sizeM;if(!t||e.kind==="bridge")return new R(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new R(t.widthM===void 0?1:Yd(t.widthM,i.widthM),t.heightM===void 0?1:Yd(t.heightM,i.heightM),t.lengthM===void 0?1:Yd(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Ka)+1):0;if(e==="bridge")return Ft.NATURAL_SIZE;const n=`${e}:${i}`,s=_g.get(n);if(s)return s;const r=e==="building"?vg(i-1,void 0,void 0,void 0):e==="tree"?yg():e==="shrub"?bg():e==="crop"?wg():e==="mound"?xg():e==="streetlight"?Sg(!1):e==="vehicle"?Eg(!1,void 0,void 0):e==="aircraft"?Rg():e==="entity"?Ig():Zf(),o=new Ji().setFromObject(r),l={widthM:o.max.x-o.min.x,heightM:o.max.y-o.min.y,lengthM:o.max.z-o.min.z};return this.dispose(r),_g.set(n,l),l}static usesModel(e){return e.model!==void 0&&!(e.observerSide!==void 0&&za(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Ot;r.name=ci.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*Gd,i.updateMatrixWorld(!0);const o=new Ji().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=Ag(t.sizeM,l)??Ag(n.depictedSizeM,l)??1;i.scale.multiplyScalar(c);const h=o.getCenter(new R).multiplyScalar(c);i.position.x-=h.x,i.position.z-=h.z,i.position.y-=t.kind==="aircraft"?h.y:o.min.y*c,i.traverse(d=>{d instanceof De&&(d.castShadow=!0,d.receiveShadow=!0)}),r.add(i),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s)}static sizeOf(e){const t=this.naturalSize(e.kind,e.floors);return{widthM:e.sizeM?.widthM??t.widthM,lengthM:e.sizeM?.lengthM??t.lengthM,heightM:e.sizeM?.heightM??t.heightM}}static addLights(e,t){for(const i of t??[]){const n=oA(i.color),s=zt(e,new ui(Vd,8,6),n,i.offsetM.y,!0);s.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),s.userData={emissive:!0,lightId:i.id,lightColor:n},s.visible=!1}}static setLights(e,t,i,n,s=0,r=1,o=tA){if(!t||t.length===0)return;const l=new Map(t.map(u=>[u.id,u])),c=Math.max(1,n*o/Vd),h=Vd*c,d=r/(Math.PI*h*h);for(const u of e.children){if(!(u instanceof De))continue;const f=u.userData.lightId;if(!f)continue;const p=l.get(f);if(!p)continue;const v=s>0?Fv(p,i,i+s):Sy(p,i)?1:0;u.visible=v>0;const m=u.material,g=u.userData.lightColor;if(g){const w=v*iA*(p.intensity??1)*d;m.color.setRGB(g[0]*w,g[1]*w,g[2]*w)}u.scale.setScalar(c)}}static setLit(e,t,i,n=1){const[s,r,o]=t==="streetlight"?[nA,sA,hb]:[rA,aA,db],l=i?s*n/(Math.PI*r*r):0,c=[o[0]*l,o[1]*l,o[2]*l];e.traverse(h=>{const d=h.userData;!(h instanceof De)||!d.emissive||d.lightId||h.material.color.setRGB(...c)})}static dispose(e){e.traverse(t=>{if(!(t instanceof De))return;t.geometry.dispose();const i=t.material;for(const n of Array.isArray(i)?i:[i])n.dispose()})}}const $d="https://ufoathome.org/models/index.json";class SA{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??EA()}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 EA(){const a=`?v=${encodeURIComponent("0.75.2")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==$d?[e,$d]:[$d]).map(t=>t+a)}const qc=[{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 SA}];async function Cg(a){const{GLTFLoader:e}=await mt(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-DSbhUTN3.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(a,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${a}: ${s instanceof Error?s.message:String(s)}`)))})}const bb=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class bn{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 en(2,2),new dt({uniforms:{uColor:{value:new se(0,0,0)},uRadius:{value:1}},vertexShader:bn.VERTEX,fragmentShader:bn.FRAGMENT,transparent:!0,depthWrite:!1,blending:Xi,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*bn.MIN_ANGLE_RAD),r=bn.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(bn.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(bn.MAX_SHARE*bn.FILL*Math.PI))}static VERTEX=`
|
|
8255
8255
|
uniform float uRadius;
|
|
8256
8256
|
varying vec2 vOffset;
|
|
8257
8257
|
void main() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rr0/ufoathome",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.75.
|
|
4
|
+
"version": "0.75.2",
|
|
5
5
|
"description": "UFO@home — record and replay a UFO sighting's shape, movement and appearance",
|
|
6
6
|
"author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
|
|
7
7
|
"license": "MIT",
|