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

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.cts CHANGED
@@ -18,12 +18,30 @@ interface MoonSize {
18
18
  /** Apparent angular diameter (deg). */
19
19
  angularDiameterDeg: number;
20
20
  }
21
+ interface ObserverLocation {
22
+ /** Latitude in degrees (positive north). */
23
+ latitude: number;
24
+ /** Longitude in degrees (positive east). */
25
+ longitude: number;
26
+ /** Elevation above mean sea level in meters. Defaults to 0 if omitted. */
27
+ elevationMeters?: number;
28
+ }
29
+ interface MoonPosition {
30
+ /** Azimuth (degrees): 0=North, 90=East, etc. */
31
+ azimuth: number;
32
+ /** Altitude (degrees): +90=Zenith, 0=Horizon, -90=Nadir. */
33
+ altitude: number;
34
+ /** Parallactic angle (degrees): Rotation of the moon's disk relative to the zenith (positive = clockwise). */
35
+ parallacticAngle: number;
36
+ }
21
37
  interface MoonState {
22
38
  date: Date;
23
39
  /** Phase angle (deg): 0=full, 180=new (per Astronomy Engine convention for phase angle). */
24
40
  phase: MoonPhase;
25
41
  size: MoonSize;
26
42
  libration: MoonLibration;
43
+ /** Observer-dependent position. Undefined if no location provided. */
44
+ position?: MoonPosition;
27
45
  }
28
46
  interface RenderOptions {
29
47
  /** Number of text rows to use. (Renderer currently fixes output to FRAME_H.) */
@@ -32,9 +50,22 @@ interface RenderOptions {
32
50
  invert?: boolean;
33
51
  /** If true, prefer legacy block elements instead of twelfth-circle glyphs. (Unused now.) */
34
52
  forceLegacySymbols?: boolean;
53
+ /** Controls whether the renderer draws the horizon overlay (default true). */
54
+ showHorizon?: boolean;
35
55
  }
36
56
 
37
- declare function getMoonState(date: Date): MoonState;
57
+ declare function getMoonState(date: Date, observerLocation?: ObserverLocation): MoonState;
58
+ /**
59
+ * Returns the English name for the moon phase using phase angle (preferred) with small tolerances.
60
+ * Conventions (Astronomy Engine):
61
+ * - phaseAngleDeg = 0° → Full Moon
62
+ * - phaseAngleDeg = 180° → New Moon
63
+ * - phaseAngleDeg ≈ 90° → Quarter (half-illuminated)
64
+ *
65
+ * We fold angles > 180° into [0..180] symmetry and use `isWaxing` to disambiguate waxing vs waning,
66
+ * and First vs Last Quarter. Illumination fraction is used only as a secondary fallback threshold.
67
+ */
68
+ declare function getMoonPhase(moonState: MoonState): string;
38
69
 
39
70
  /**
40
71
  * Render a 60×29 moon using pre-rendered ASCII art.
@@ -45,4 +76,4 @@ declare function getMoonState(date: Date): MoonState;
45
76
  */
46
77
  declare function renderMoon(state: MoonState, _options?: RenderOptions): string;
47
78
 
48
- export { type MoonLibration, type MoonPhase, type MoonSize, type MoonState, type RenderOptions, getMoonState, renderMoon };
79
+ export { type MoonLibration, type MoonPhase, type MoonPosition, type MoonSize, type MoonState, type ObserverLocation, type RenderOptions, getMoonPhase, getMoonState, renderMoon };
package/dist/index.d.ts CHANGED
@@ -18,12 +18,30 @@ interface MoonSize {
18
18
  /** Apparent angular diameter (deg). */
19
19
  angularDiameterDeg: number;
20
20
  }
21
+ interface ObserverLocation {
22
+ /** Latitude in degrees (positive north). */
23
+ latitude: number;
24
+ /** Longitude in degrees (positive east). */
25
+ longitude: number;
26
+ /** Elevation above mean sea level in meters. Defaults to 0 if omitted. */
27
+ elevationMeters?: number;
28
+ }
29
+ interface MoonPosition {
30
+ /** Azimuth (degrees): 0=North, 90=East, etc. */
31
+ azimuth: number;
32
+ /** Altitude (degrees): +90=Zenith, 0=Horizon, -90=Nadir. */
33
+ altitude: number;
34
+ /** Parallactic angle (degrees): Rotation of the moon's disk relative to the zenith (positive = clockwise). */
35
+ parallacticAngle: number;
36
+ }
21
37
  interface MoonState {
22
38
  date: Date;
23
39
  /** Phase angle (deg): 0=full, 180=new (per Astronomy Engine convention for phase angle). */
24
40
  phase: MoonPhase;
25
41
  size: MoonSize;
26
42
  libration: MoonLibration;
43
+ /** Observer-dependent position. Undefined if no location provided. */
44
+ position?: MoonPosition;
27
45
  }
28
46
  interface RenderOptions {
29
47
  /** Number of text rows to use. (Renderer currently fixes output to FRAME_H.) */
@@ -32,9 +50,22 @@ interface RenderOptions {
32
50
  invert?: boolean;
33
51
  /** If true, prefer legacy block elements instead of twelfth-circle glyphs. (Unused now.) */
34
52
  forceLegacySymbols?: boolean;
53
+ /** Controls whether the renderer draws the horizon overlay (default true). */
54
+ showHorizon?: boolean;
35
55
  }
36
56
 
37
- declare function getMoonState(date: Date): MoonState;
57
+ declare function getMoonState(date: Date, observerLocation?: ObserverLocation): MoonState;
58
+ /**
59
+ * Returns the English name for the moon phase using phase angle (preferred) with small tolerances.
60
+ * Conventions (Astronomy Engine):
61
+ * - phaseAngleDeg = 0° → Full Moon
62
+ * - phaseAngleDeg = 180° → New Moon
63
+ * - phaseAngleDeg ≈ 90° → Quarter (half-illuminated)
64
+ *
65
+ * We fold angles > 180° into [0..180] symmetry and use `isWaxing` to disambiguate waxing vs waning,
66
+ * and First vs Last Quarter. Illumination fraction is used only as a secondary fallback threshold.
67
+ */
68
+ declare function getMoonPhase(moonState: MoonState): string;
38
69
 
39
70
  /**
40
71
  * Render a 60×29 moon using pre-rendered ASCII art.
@@ -45,4 +76,4 @@ declare function getMoonState(date: Date): MoonState;
45
76
  */
46
77
  declare function renderMoon(state: MoonState, _options?: RenderOptions): string;
47
78
 
48
- export { type MoonLibration, type MoonPhase, type MoonSize, type MoonState, type RenderOptions, getMoonState, renderMoon };
79
+ export { type MoonLibration, type MoonPhase, type MoonPosition, type MoonSize, type MoonState, type ObserverLocation, type RenderOptions, getMoonPhase, getMoonState, renderMoon };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import*as K from'astronomy-engine';var c=K.default??K;function k($,t){let n=new Date($.getTime());return n.setDate(n.getDate()+t),n}function u($){let t=c.Illumination(c.Body.Moon,$).phase_fraction;return c.Illumination(c.Body.Moon,k($,1)).phase_fraction>t}function z($){let t=c.Illumination(c.Body.Moon,$),n=c.Libration($);return {date:$,phase:{phaseAngleDeg:t.phase_angle,illuminatedFraction:t.phase_fraction,isWaxing:u($)},size:{distanceKm:n.dist_km,angularDiameterDeg:n.diam_deg},libration:{elon:n.elon,elat:n.elat}}}var d={moons:[{index:0,distance_km:405493.5,libration_elat:-0.888,libration_elon:-0.412,ascii:`
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:`
2
2
 
3
3
  ._ . - .
4
4
  ,_++> a, +. \`-.
@@ -894,8 +894,12 @@ import*as K from'astronomy-engine';var c=K.default??K;function k($,t){let n=new
894
894
  'FFF'"Z"@MGR^^'\`
895
895
 
896
896
 
897
- `}]};var r=60,l=29;function L($){let t=$.split(`
898
- `),n=1/0,s=-1/0,a=1/0,e=-1/0;if(t.forEach((B,y)=>{let E=B.search(/\S/);if(E!==-1){let o=B.search(/\s+$/);n=Math.min(n,E),s=Math.max(s,o===-1?B.length-1:o-1),a===1/0&&(a=y),e=y;}}),n===1/0)return {width:r,height:l,centerX:Math.floor(r/2),centerY:Math.floor(l/2)};let M=s-n+1,i=e-a+1;return {width:M,height:i,centerX:n+Math.floor(M/2),centerY:a+Math.floor(i/2)}}function P($,t,n){return Math.min(n,Math.max(t,$))}function x($){return $*Math.PI/180}function X($,t,n=0){let s=x($),a=x(n),M=(t?1:-1)*Math.sin(s),i=0,B=Math.cos(s),y=-B*Math.sin(a),E=B*Math.cos(a);i=y,B=E;let o=Math.sqrt(M*M+i*i+B*B);return {sx:M/o,sy:i/o,sz:B/o}}function N($,t,n,s,a){let e=1/r,M=1/l,i=[-0.5,-0.25,0,.25,.5],B=0,y=0;for(let E of i)for(let o of i){let A=$+E*e,w=t+o*M,F=A*A+w*w;if(F>1)continue;let g=Math.sqrt(Math.max(0,1-F)),_=A*n+w*s+g*a;B+=Math.max(0,_),y++;}return y===0?0:B/y}function h($){let s=0,a=1/0;for(let e=0;e<d.moons.length;e++){let M=d.moons[e],i=Math.abs(M.distance_km-$.size.distanceKm)*1,B=Math.abs(M.libration_elat-$.libration.elat)*1e4,y=Math.abs(M.libration_elon-$.libration.elon)*1e4,E=i+B+y;E<a&&(a=E,s=e);}return d.moons[s]}function Y($,t={}){let n=h($),s=n.ascii.split(`
899
- `),a=L(n.ascii),{sx:e,sy:M,sz:i}=X($.phase.phaseAngleDeg,$.phase.isWaxing,$.libration.elat),B=Array.from({length:l},()=>Array(r).fill(0)),y=Array.from({length:l},()=>Array(r).fill(-1)),E=Array.from({length:l},()=>Array(r).fill(false)),o=0,A=0;for(let g=0;g<l;g++)for(let _=0;_<r;_++){let m=_-a.centerX,R=g-a.centerY,W=m/(a.width/2),b=R/(a.height/2),T=W*W+b*b;if(T>1){B[g][_]=0,y[g][_]=-1;continue}E[g][_]=true,o++;let Z=N(W,b,e,M,i);B[g][_]=Z,Z>0&&A++;let f=Math.sqrt(Math.max(0,1-T));y[g][_]=W*e+b*M+f*i;}let w=Array.from({length:l},()=>Array(r).fill(false));if(A>0)for(let g=0;g<l;g++)for(let _=0;_<r;_++)w[g][_]=B[g][_]>0;else {let g=P($.phase.illuminatedFraction??0,0,1),_=Math.max(1,Math.round(g*o)),m=[];for(let R=0;R<l;R++)for(let W=0;W<r;W++)E[R][W]&&m.push({ix:W,iy:R,v:y[R][W]});m.sort((R,W)=>W.v-R.v);for(let R=0;R<Math.min(_,m.length);R++){let{ix:W,iy:b}=m[R];w[b][W]=true;}}let F=[];for(let g=0;g<l;g++){let _="",m=s[g]??"";for(let R=0;R<r;R++)w[g][R]?_+=m[R]??" ":_+=" ";F.push(_);}return F.join(`
900
- `)}export{z as getMoonState,Y as renderMoon};//# sourceMappingURL=index.mjs.map
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
901
905
  //# sourceMappingURL=index.mjs.map