ascii-side-of-the-moon 1.0.7 → 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/README.md +36 -2
- package/dist/cli.cjs +523 -318
- package/dist/index.cjs +9 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -6
- package/dist/index.d.ts +42 -6
- package/dist/index.mjs +9 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
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). */
|
|
@@ -18,12 +25,30 @@ interface MoonSize {
|
|
|
18
25
|
/** Apparent angular diameter (deg). */
|
|
19
26
|
angularDiameterDeg: number;
|
|
20
27
|
}
|
|
28
|
+
interface ObserverLocation {
|
|
29
|
+
/** Latitude in degrees (positive north). */
|
|
30
|
+
latitude: number;
|
|
31
|
+
/** Longitude in degrees (positive east). */
|
|
32
|
+
longitude: number;
|
|
33
|
+
/** Elevation above mean sea level in meters. Defaults to 0 if omitted. */
|
|
34
|
+
elevationMeters?: number;
|
|
35
|
+
}
|
|
36
|
+
interface MoonPosition {
|
|
37
|
+
/** Azimuth (degrees): 0=North, 90=East, etc. */
|
|
38
|
+
azimuth: number;
|
|
39
|
+
/** Altitude (degrees): +90=Zenith, 0=Horizon, -90=Nadir. */
|
|
40
|
+
altitude: number;
|
|
41
|
+
/** Parallactic angle (degrees): Rotation of the moon's disk relative to the zenith (positive = clockwise). */
|
|
42
|
+
parallacticAngle: number;
|
|
43
|
+
}
|
|
21
44
|
interface MoonState {
|
|
22
45
|
date: Date;
|
|
23
46
|
/** Phase angle (deg): 0=full, 180=new (per Astronomy Engine convention for phase angle). */
|
|
24
47
|
phase: MoonPhase;
|
|
25
48
|
size: MoonSize;
|
|
26
49
|
libration: MoonLibration;
|
|
50
|
+
/** Observer-dependent position. Undefined if no location provided. */
|
|
51
|
+
position?: MoonPosition;
|
|
27
52
|
}
|
|
28
53
|
interface RenderOptions {
|
|
29
54
|
/** Number of text rows to use. (Renderer currently fixes output to FRAME_H.) */
|
|
@@ -32,9 +57,11 @@ interface RenderOptions {
|
|
|
32
57
|
invert?: boolean;
|
|
33
58
|
/** If true, prefer legacy block elements instead of twelfth-circle glyphs. (Unused now.) */
|
|
34
59
|
forceLegacySymbols?: boolean;
|
|
60
|
+
/** Controls whether the renderer draws the horizon overlay (default true). */
|
|
61
|
+
showHorizon?: boolean;
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
declare function getMoonState(date: Date): MoonState;
|
|
64
|
+
declare function getMoonState(date: Date, observerLocation?: ObserverLocation): MoonState;
|
|
38
65
|
/**
|
|
39
66
|
* Returns the English name for the moon phase using phase angle (preferred) with small tolerances.
|
|
40
67
|
* Conventions (Astronomy Engine):
|
|
@@ -49,11 +76,20 @@ declare function getMoonPhase(moonState: MoonState): string;
|
|
|
49
76
|
|
|
50
77
|
/**
|
|
51
78
|
* Render a 60×29 moon using pre-rendered ASCII art.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
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.
|
|
56
92
|
*/
|
|
57
93
|
declare function renderMoon(state: MoonState, _options?: RenderOptions): string;
|
|
58
94
|
|
|
59
|
-
export { type MoonLibration, type MoonPhase, type MoonSize, type MoonState, type RenderOptions, getMoonPhase, getMoonState, renderMoon };
|
|
95
|
+
export { type MoonLibration, type MoonPhase, type MoonPosition, type MoonSize, type MoonState, type ObserverLocation, type RenderOptions, getMoonPhase, getMoonState, renderMoon };
|
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). */
|
|
@@ -18,12 +25,30 @@ interface MoonSize {
|
|
|
18
25
|
/** Apparent angular diameter (deg). */
|
|
19
26
|
angularDiameterDeg: number;
|
|
20
27
|
}
|
|
28
|
+
interface ObserverLocation {
|
|
29
|
+
/** Latitude in degrees (positive north). */
|
|
30
|
+
latitude: number;
|
|
31
|
+
/** Longitude in degrees (positive east). */
|
|
32
|
+
longitude: number;
|
|
33
|
+
/** Elevation above mean sea level in meters. Defaults to 0 if omitted. */
|
|
34
|
+
elevationMeters?: number;
|
|
35
|
+
}
|
|
36
|
+
interface MoonPosition {
|
|
37
|
+
/** Azimuth (degrees): 0=North, 90=East, etc. */
|
|
38
|
+
azimuth: number;
|
|
39
|
+
/** Altitude (degrees): +90=Zenith, 0=Horizon, -90=Nadir. */
|
|
40
|
+
altitude: number;
|
|
41
|
+
/** Parallactic angle (degrees): Rotation of the moon's disk relative to the zenith (positive = clockwise). */
|
|
42
|
+
parallacticAngle: number;
|
|
43
|
+
}
|
|
21
44
|
interface MoonState {
|
|
22
45
|
date: Date;
|
|
23
46
|
/** Phase angle (deg): 0=full, 180=new (per Astronomy Engine convention for phase angle). */
|
|
24
47
|
phase: MoonPhase;
|
|
25
48
|
size: MoonSize;
|
|
26
49
|
libration: MoonLibration;
|
|
50
|
+
/** Observer-dependent position. Undefined if no location provided. */
|
|
51
|
+
position?: MoonPosition;
|
|
27
52
|
}
|
|
28
53
|
interface RenderOptions {
|
|
29
54
|
/** Number of text rows to use. (Renderer currently fixes output to FRAME_H.) */
|
|
@@ -32,9 +57,11 @@ interface RenderOptions {
|
|
|
32
57
|
invert?: boolean;
|
|
33
58
|
/** If true, prefer legacy block elements instead of twelfth-circle glyphs. (Unused now.) */
|
|
34
59
|
forceLegacySymbols?: boolean;
|
|
60
|
+
/** Controls whether the renderer draws the horizon overlay (default true). */
|
|
61
|
+
showHorizon?: boolean;
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
declare function getMoonState(date: Date): MoonState;
|
|
64
|
+
declare function getMoonState(date: Date, observerLocation?: ObserverLocation): MoonState;
|
|
38
65
|
/**
|
|
39
66
|
* Returns the English name for the moon phase using phase angle (preferred) with small tolerances.
|
|
40
67
|
* Conventions (Astronomy Engine):
|
|
@@ -49,11 +76,20 @@ declare function getMoonPhase(moonState: MoonState): string;
|
|
|
49
76
|
|
|
50
77
|
/**
|
|
51
78
|
* Render a 60×29 moon using pre-rendered ASCII art.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
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.
|
|
56
92
|
*/
|
|
57
93
|
declare function renderMoon(state: MoonState, _options?: RenderOptions): string;
|
|
58
94
|
|
|
59
|
-
export { type MoonLibration, type MoonPhase, type MoonSize, type MoonState, type RenderOptions, getMoonPhase, getMoonState, renderMoon };
|
|
95
|
+
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
|
|
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,8 +894,12 @@ import*as K from'astronomy-engine';var c=K.default??K;function u($,t){let n=new
|
|
|
894
894
|
'FFF'"Z"@MGR^^'\`
|
|
895
895
|
|
|
896
896
|
|
|
897
|
-
`}]};var
|
|
898
|
-
`),n=1/0,
|
|
899
|
-
`),
|
|
900
|
-
`)}
|
|
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
|
|
901
905
|
//# sourceMappingURL=index.mjs.map
|