ascii-side-of-the-moon 1.0.8 → 1.0.9

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.
package/dist/index.d.ts CHANGED
@@ -11,6 +11,13 @@ interface MoonPhase {
11
11
  illuminatedFraction: number;
12
12
  /** If true, bright limb on the right (waxing). If false, bright limb on the left (waning). */
13
13
  isWaxing?: boolean;
14
+ /**
15
+ * Position angle of the bright limb in degrees.
16
+ * Measured from celestial north, eastward (counterclockwise when looking at the sky).
17
+ * 0° = north, 90° = east, 180° = south, 270° = west.
18
+ * This determines the orientation of the terminator line.
19
+ */
20
+ brightLimbAngle?: number;
14
21
  }
15
22
  interface MoonSize {
16
23
  /** Geocentric distance (km). */
@@ -69,10 +76,19 @@ declare function getMoonPhase(moonState: MoonState): string;
69
76
 
70
77
  /**
71
78
  * Render a 60×29 moon using pre-rendered ASCII art.
72
- * - Uses nearest pre-rendered moon for distance and libration
73
- * - Applies phase masking via Lambertian lighting
74
- * - Fallback: if nothing lights up (ultra-thin crescent), select the top
75
- * illuminatedFraction of disc cells by raw incidence (n·s) to force a sliver.
79
+ *
80
+ * Algorithm:
81
+ * 1. Select the nearest pre-rendered moon texture (based on distance and libration)
82
+ * 2. Calculate sun direction in CELESTIAL frame (standard north-up orientation)
83
+ * 3. Apply Lambertian phase lighting to create illumination mask (in celestial frame)
84
+ * 4. Combine texture with phase mask (both in celestial frame)
85
+ * 5. Rotate the COMBINED result by parallactic angle to match observer's view
86
+ * 6. Optionally overlay horizon line
87
+ *
88
+ * Key insight: The phase illumination is a property of the moon itself (which physical
89
+ * areas are lit by the sun). When we rotate the moon's appearance for the observer,
90
+ * the illuminated areas rotate WITH the texture because they're physically attached
91
+ * to the lunar surface.
76
92
  */
77
93
  declare function renderMoon(state: MoonState, _options?: RenderOptions): string;
78
94
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import*as T from'astronomy-engine';var W=T.default??T;function P($,g){let n=new Date($.getTime());return n.setDate(n.getDate()+g),n}function p($){return ($%360+360)%360}function X($){let g=($%24+24)%24;return g>12&&(g-=24),g}function N($){let g=W.Illumination(W.Body.Moon,$).phase_fraction;return W.Illumination(W.Body.Moon,P($,1)).phase_fraction>g}function D($,g,n){let _=W.SiderealTime($),t=g.longitude/15,B=X(t+_-n.ra)*15*W.DEG2RAD,a=g.latitude*W.DEG2RAD,R=n.dec*W.DEG2RAD,M=Math.sin(B),o=Math.tan(a)*Math.cos(R)-Math.sin(R)*Math.cos(B);return Math.atan2(M,o)*W.RAD2DEG}function Y($,g){let n=new W.Observer(g.latitude,g.longitude,g.elevationMeters??0),_=W.Equator(W.Body.Moon,$,n,true,true),t=W.Horizon($,n,_.ra,_.dec,"normal");return {azimuth:p(t.azimuth),altitude:t.altitude,parallacticAngle:D($,g,_)}}function H($,g){let n=W.Illumination(W.Body.Moon,$),_=W.Libration($);return {date:$,phase:{phaseAngleDeg:n.phase_angle,illuminatedFraction:n.phase_fraction,isWaxing:N($)},size:{distanceKm:_.dist_km,angularDiameterDeg:_.diam_deg},libration:{elon:_.elon,elat:_.elat},position:g?Y($,g):void 0}}function v($){let{phaseAngleDeg:g,illuminatedFraction:n}=$.phase,_=$.phase.isWaxing??g>90,t=(g%360+360)%360,i=t>180?360-t:t,B=10,a=10,R=8,M=.98,o=.02,r=.02;return i<=B||n>=M?"Full Moon":i>=180-a||n<=o?"New Moon":Math.abs(i-90)<=R||Math.abs(n-.5)<=r?_?"First Quarter":"Last Quarter":i<90?_?"Waxing Gibbous":"Waning Gibbous":_?"Waxing Crescent":"Waning Crescent"}var K={moons:[{index:0,distance_km:405493.5,libration_elat:-0.888,libration_elon:-0.412,ascii:`
1
+ import*as T from'astronomy-engine';var s=T.default??T;function p($,g){let n=new Date($.getTime());return n.setDate(n.getDate()+g),n}function z($){return ($%360+360)%360}function P($){let g=s.GeoVector(s.Body.Sun,$,true),n=s.GeoVector(s.Body.Moon,$,true),t=s.EquatorFromVector(g),_=s.EquatorFromVector(n),B=(t.ra-_.ra)*15*s.DEG2RAD,M=_.dec*s.DEG2RAD,a=t.dec*s.DEG2RAD,o=Math.sin(B),i=Math.cos(B),e=o,m=Math.cos(M)*Math.tan(a)-Math.sin(M)*i,A=Math.atan2(e,m)*s.RAD2DEG;return z(A)}function D($){let g=($%24+24)%24;return g>12&&(g-=24),g}function X($){let g=s.Illumination(s.Body.Moon,$).phase_fraction;return s.Illumination(s.Body.Moon,p($,1)).phase_fraction>g}function N($,g,n){let t=s.SiderealTime($),_=g.longitude/15,B=D(_+t-n.ra)*15*s.DEG2RAD,M=g.latitude*s.DEG2RAD,a=n.dec*s.DEG2RAD,o=Math.sin(B),i=Math.tan(M)*Math.cos(a)-Math.sin(a)*Math.cos(B);return Math.atan2(o,i)*s.RAD2DEG}function Y($,g){let n=new s.Observer(g.latitude,g.longitude,g.elevationMeters??0),t=s.Equator(s.Body.Moon,$,n,true,true),_=s.Horizon($,n,t.ra,t.dec,"normal");return {azimuth:z(_.azimuth),altitude:_.altitude,parallacticAngle:N($,g,t)}}function G($,g){let n=s.Illumination(s.Body.Moon,$),t=s.Libration($);return {date:$,phase:{phaseAngleDeg:n.phase_angle,illuminatedFraction:n.phase_fraction,isWaxing:X($),brightLimbAngle:P($)},size:{distanceKm:t.dist_km,angularDiameterDeg:t.diam_deg},libration:{elon:t.elon,elat:t.elat},position:g?Y($,g):void 0}}function H($){let{phaseAngleDeg:g,illuminatedFraction:n}=$.phase,t=$.phase.isWaxing??g>90,_=(g%360+360)%360,R=_>180?360-_:_,B=10,M=10,a=8,o=.98,i=.02,e=.02;return R<=B||n>=o?"Full Moon":R>=180-M||n<=i?"New Moon":Math.abs(R-90)<=a||Math.abs(n-.5)<=e?t?"First Quarter":"Last Quarter":R<90?t?"Waxing Gibbous":"Waning Gibbous":t?"Waxing Crescent":"Waning Crescent"}var K={moons:[{index:0,distance_km:405493.5,libration_elat:-0.888,libration_elon:-0.412,ascii:`
2
2
 
3
3
  ._ . - .
4
4
  ,_++> a, +. \`-.
@@ -894,12 +894,12 @@ import*as T from'astronomy-engine';var W=T.default??T;function P($,g){let n=new
894
894
  'FFF'"Z"@MGR^^'\`
895
895
 
896
896
 
897
- `}]};var E=60,w=29,J=90;function V($){let g=$.split(`
898
- `),n=1/0,_=-1/0,t=1/0,i=-1/0;if(g.forEach((R,M)=>{let o=R.search(/\S/);if(o!==-1){let r=R.search(/\s+$/);n=Math.min(n,o),_=Math.max(_,r===-1?R.length-1:r-1),t===1/0&&(t=M),i=M;}}),n===1/0)return {width:E,height:w,centerX:Math.floor(E/2),centerY:Math.floor(w/2)};let B=_-n+1,a=i-t+1;return {width:B,height:a,centerX:n+Math.floor(B/2),centerY:t+Math.floor(a/2)}}function j($,g,n){return Math.min(n,Math.max(g,$))}function z($){return $*Math.PI/180}function Q($,g,n=0){let _=z($),t=z(n),B=(g?1:-1)*Math.sin(_),a=0,R=Math.cos(_),M=-R*Math.sin(t),o=R*Math.cos(t);a=M,R=o;let r=Math.sqrt(B*B+a*a+R*R);return {sx:B/r,sy:a/r,sz:R/r}}function I($,g,n,_,t){let i=1/E,B=1/w,a=[-0.5,-0.25,0,.25,.5],R=0,M=0;for(let o of a)for(let r of a){let A=$+o*i,u=g+r*B,c=A*A+u*u;if(c>1)continue;let d=Math.sqrt(Math.max(0,1-c)),b=A*n+u*_+d*t;R+=Math.max(0,b),M++;}return M===0?0:R/M}function S($){let _=0,t=1/0;for(let i=0;i<K.moons.length;i++){let B=K.moons[i],a=Math.abs(B.distance_km-$.size.distanceKm)*1,R=Math.abs(B.libration_elat-$.libration.elat)*1e4,M=Math.abs(B.libration_elon-$.libration.elon)*1e4,o=a+R+M;o<t&&(t=o,_=i);}return K.moons[_]}var k=22/10;function C($,g,n,_){let t=(g%360+360)%360;if(Math.abs(t)<.1)return $;let i=$.split(`
899
- `),B=i.length,a=i[0]?.length??0,R=n??(a-1)/2,M=_??(B-1)/2,o=t*Math.PI/180,r=Math.cos(o),A=Math.sin(o),u=Array.from({length:B},()=>Array(a).fill(" "));for(let c=0;c<B;c++)for(let d=0;d<a;d++){let b=d-R,f=(c-M)*k,x=b*r-f*A,e=b*A+f*r,s=Math.round(x+R),m=Math.round(e/k+M);if(s>=0&&s<a&&m>=0&&m<B){let y=i[m]?.[s]??" ";u[c][d]=y;}}return u.map(c=>c.join("")).join(`
900
- `)}function q($,g={}){let _=(g??{}).showHorizon!==false,t=S($),i=t.ascii.split(`
901
- `),B=V(t.ascii),{sx:a,sy:R,sz:M}=Q($.phase.phaseAngleDeg,$.phase.isWaxing,$.libration.elat),o=Array.from({length:w},()=>Array(E).fill(0)),r=Array.from({length:w},()=>Array(E).fill(-1)),A=Array.from({length:w},()=>Array(E).fill(false)),u=0,c=0;for(let e=0;e<w;e++)for(let s=0;s<E;s++){let m=s-B.centerX,y=e-B.centerY,l=m/(B.width/2),F=y/(B.height/2),Z=l*l+F*F;if(Z>1){o[e][s]=0,r[e][s]=-1;continue}A[e][s]=true,u++;let h=I(l,F,a,R,M);o[e][s]=h,h>0&&c++;let L=Math.sqrt(Math.max(0,1-Z));r[e][s]=l*a+F*R+L*M;}let d=Array.from({length:w},()=>Array(E).fill(false));if(c>0)for(let e=0;e<w;e++)for(let s=0;s<E;s++)d[e][s]=o[e][s]>0;else {let e=j($.phase.illuminatedFraction??0,0,1),s=Math.max(1,Math.round(e*u)),m=[];for(let y=0;y<w;y++)for(let l=0;l<E;l++)A[y][l]&&m.push({ix:l,iy:y,v:r[y][l]});m.sort((y,l)=>l.v-y.v);for(let y=0;y<Math.min(s,m.length);y++){let{ix:l,iy:F}=m[y];d[F][l]=true;}}let b=[];for(let e=0;e<w;e++){let s="",m=i[e]??"";for(let y=0;y<E;y++)d[e][y]?s+=m[y]??" ":s+=" ";b.push(s);}let f=b.join(`
902
- `),x=f;if($.position?.parallacticAngle!==void 0){let e=B;x=C(f,$.position.parallacticAngle+J,e.centerX,e.centerY);}return _?U(x,$,B):x}function U($,g,n){let _=g.position;if(!_||g.size.angularDiameterDeg<=0)return $;let t=_.altitude??0,i=g.size.angularDiameterDeg/2,B=t+i;if(t-i>=0)return $;let R=$.split(`
903
- `),M=g.size.angularDiameterDeg/Math.max(1,n.height),o=n.centerY+t/M;o=Math.round(j(o,0,w-1));let r;return B<=0?r=`${Math.abs(t).toFixed(1).replace(/\.0$/,"")}-deg-below-horizon`:r="horizon",R[o]=O(r),R.join(`
904
- `)}function O($){let n=`--${$.trim().replace(/\s+/g,"-")}--`;if(n.length>E&&(n=n.slice(0,E)),n.length===E)return n;let _=E-n.length,t=Math.floor(_/2),i=_-t;return "-".repeat(t)+n+"-".repeat(i)}export{v as getMoonPhase,H as getMoonState,q as renderMoon};//# sourceMappingURL=index.mjs.map
897
+ `}]};var c=60,d=29,J=-45;function V($){let g=$.split(`
898
+ `),n=1/0,t=-1/0,_=1/0,R=-1/0;if(g.forEach((a,o)=>{let i=a.search(/\S/);if(i!==-1){let e=a.search(/\s+$/);n=Math.min(n,i),t=Math.max(t,e===-1?a.length-1:e-1),_===1/0&&(_=o),R=o;}}),n===1/0)return {width:c,height:d,centerX:Math.floor(c/2),centerY:Math.floor(d/2)};let B=t-n+1,M=R-_+1;return {width:B,height:M,centerX:n+Math.floor(B/2),centerY:_+Math.floor(M/2)}}function L($,g,n){return Math.min(n,Math.max(g,$))}function x($){return $*Math.PI/180}function Q($,g,n,t=0){let _=x($),R=x(t),B;g!==void 0?B=x(g):B=x(n?270:90);let M=-Math.sin(B),a=-Math.cos(B),o=M*Math.sin(_),i=a*Math.sin(_),e=Math.cos(_),m=i*Math.cos(R)-e*Math.sin(R),A=i*Math.sin(R)+e*Math.cos(R);i=m,e=A;let W=Math.sqrt(o*o+i*i+e*e);return {sx:o/W,sy:i/W,sz:e/W}}function I($,g,n,t,_){let R=1/c,B=1/d,M=[-0.5,-0.25,0,.25,.5],a=0,o=0;for(let i of M)for(let e of M){let m=$+i*R,A=g+e*B,W=m*m+A*A;if(W>1)continue;let w=Math.sqrt(Math.max(0,1-W)),b=m*n+A*t+w*_;a+=Math.max(0,b),o++;}return o===0?0:a/o}function S($){let t=0,_=1/0;for(let R=0;R<K.moons.length;R++){let B=K.moons[R],M=Math.abs(B.distance_km-$.size.distanceKm)*1,a=Math.abs(B.libration_elat-$.libration.elat)*1e4,o=Math.abs(B.libration_elon-$.libration.elon)*1e4,i=M+a+o;i<_&&(_=i,t=R);}return K.moons[t]}var k=22/10;function q($,g,n,t){let _=(g%360+360)%360;if(Math.abs(_)<.1)return $;let R=$.split(`
899
+ `),B=R.length,M=R[0]?.length??0,a=n??(M-1)/2,o=t??(B-1)/2,i=_*Math.PI/180,e=Math.cos(i),m=Math.sin(i),A=Array.from({length:B},()=>Array(M).fill(" "));for(let W=0;W<B;W++)for(let w=0;w<M;w++){let b=w-a,F=(W-o)*k,r=b*e-F*m,E=b*m+F*e,u=Math.round(r+a),y=Math.round(E/k+o);if(u>=0&&u<M&&y>=0&&y<B){let l=R[y]?.[u]??" ";A[W][w]=l;}}return A.map(W=>W.join("")).join(`
900
+ `)}function C($,g={}){let t=(g??{}).showHorizon!==false,_=S($),R=_.ascii.split(`
901
+ `),B=V(_.ascii),{sx:M,sy:a,sz:o}=Q($.phase.phaseAngleDeg,$.phase.brightLimbAngle,$.phase.isWaxing,$.libration.elat),i=Array.from({length:d},()=>Array(c).fill(0)),e=Array.from({length:d},()=>Array(c).fill(-1)),m=Array.from({length:d},()=>Array(c).fill(false)),A=0,W=0;for(let r=0;r<d;r++)for(let E=0;E<c;E++){let u=E-B.centerX,y=r-B.centerY,l=u/(B.width/2),f=y/(B.height/2),h=l*l+f*f;if(h>1){i[r][E]=0,e[r][E]=-1;continue}m[r][E]=true,A++;let Z=I(l,f,M,a,o);i[r][E]=Z,Z>0&&W++;let j=Math.sqrt(Math.max(0,1-h));e[r][E]=l*M+f*a+j*o;}let w=Array.from({length:d},()=>Array(c).fill(false));if(W>0)for(let r=0;r<d;r++)for(let E=0;E<c;E++)w[r][E]=i[r][E]>0;else {let r=L($.phase.illuminatedFraction??0,0,1),E=Math.max(1,Math.round(r*A)),u=[];for(let y=0;y<d;y++)for(let l=0;l<c;l++)m[y][l]&&u.push({ix:l,iy:y,v:e[y][l]});u.sort((y,l)=>l.v-y.v);for(let y=0;y<Math.min(E,u.length);y++){let{ix:l,iy:f}=u[y];w[f][l]=true;}}let b=[];for(let r=0;r<d;r++){let E="",u=R[r]??"";for(let y=0;y<c;y++)w[r][y]?E+=u[y]??" ":E+=" ";b.push(E);}let F=b.join(`
902
+ `);if($.position?.parallacticAngle!==void 0){let r=-$.position.parallacticAngle+J;Math.abs(r)>.1&&(F=q(F,r,B.centerX,B.centerY));}return t?U(F,$,B):F}function U($,g,n){let t=g.position;if(!t||g.size.angularDiameterDeg<=0)return $;let _=t.altitude??0,R=g.size.angularDiameterDeg/2,B=_+R;if(_-R>=0)return $;let a=$.split(`
903
+ `),o=g.size.angularDiameterDeg/Math.max(1,n.height),i=n.centerY+_/o;i=Math.round(L(i,0,d-1));let e;return B<=0?e=`${Math.abs(_).toFixed(1).replace(/\.0$/,"")}-deg-below-horizon`:e="horizon",a[i]=O(e),a.join(`
904
+ `)}function O($){let n=`--${$.trim().replace(/\s+/g,"-")}--`;if(n.length>c&&(n=n.slice(0,c)),n.length===c)return n;let t=c-n.length,_=Math.floor(t/2),R=t-_;return "-".repeat(_)+n+"-".repeat(R)}export{H as getMoonPhase,G as getMoonState,C as renderMoon};//# sourceMappingURL=index.mjs.map
905
905
  //# sourceMappingURL=index.mjs.map