chart2txt 0.5.2 → 0.6.0
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/chart2txt.min.js +1 -1
- package/dist/config/ChartSettings.d.ts +1 -0
- package/dist/config/ChartSettings.js +1 -0
- package/dist/constants.js +4 -2
- package/dist/core/aspectPatterns.d.ts +5 -0
- package/dist/core/aspectPatterns.js +444 -0
- package/dist/core/aspects.js +94 -9
- package/dist/core/astrology.d.ts +8 -2
- package/dist/core/astrology.js +23 -6
- package/dist/core/dignities.d.ts +27 -0
- package/dist/core/dignities.js +136 -0
- package/dist/core/dispositors.d.ts +22 -0
- package/dist/core/dispositors.js +143 -0
- package/dist/core/signDistributions.d.ts +31 -0
- package/dist/core/signDistributions.js +150 -0
- package/dist/formatters/text/sections/angles.js +4 -4
- package/dist/formatters/text/sections/aspectPatterns.d.ts +7 -0
- package/dist/formatters/text/sections/aspectPatterns.js +175 -0
- package/dist/formatters/text/sections/aspects.js +7 -4
- package/dist/formatters/text/sections/birthdata.js +1 -1
- package/dist/formatters/text/sections/dispositors.d.ts +7 -0
- package/dist/formatters/text/sections/dispositors.js +20 -0
- package/dist/formatters/text/sections/houseOverlays.js +10 -34
- package/dist/formatters/text/sections/houses.d.ts +6 -0
- package/dist/formatters/text/sections/houses.js +36 -0
- package/dist/formatters/text/sections/planets.js +11 -26
- package/dist/formatters/text/sections/signDistributions.d.ts +25 -0
- package/dist/formatters/text/sections/signDistributions.js +67 -0
- package/dist/formatters/text/textFormatter.js +37 -3
- package/dist/types.d.ts +61 -0
- package/dist/utils/formatting.d.ts +6 -0
- package/dist/utils/formatting.js +13 -0
- package/dist/utils/houseCalculations.d.ts +13 -0
- package/dist/utils/houseCalculations.js +65 -0
- package/dist/utils/precision.d.ts +49 -0
- package/dist/utils/precision.js +71 -0
- package/dist/utils/validation.d.ts +37 -0
- package/dist/utils/validation.js +181 -0
- package/package.json +2 -1
package/dist/core/aspects.js
CHANGED
|
@@ -2,31 +2,116 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.calculateAspects = calculateAspects;
|
|
4
4
|
exports.calculateMultichartAspects = calculateMultichartAspects;
|
|
5
|
+
const astrology_1 = require("./astrology");
|
|
6
|
+
const precision_1 = require("../utils/precision");
|
|
7
|
+
/**
|
|
8
|
+
* Gets the expected sign difference for a given aspect angle
|
|
9
|
+
* @param aspectAngle The aspect angle in degrees
|
|
10
|
+
* @returns The expected sign difference
|
|
11
|
+
*/
|
|
12
|
+
function getExpectedSignDifference(aspectAngle) {
|
|
13
|
+
// Normalize aspect angle to 0-180 range for sign difference calculation
|
|
14
|
+
const normalizedAngle = aspectAngle <= 180 ? aspectAngle : 360 - aspectAngle;
|
|
15
|
+
// Calculate how many 30-degree signs this aspect spans
|
|
16
|
+
switch (normalizedAngle) {
|
|
17
|
+
case 0:
|
|
18
|
+
return 0; // Conjunction: same sign
|
|
19
|
+
case 30:
|
|
20
|
+
return 1; // Semi-sextile: 1 sign apart
|
|
21
|
+
case 60:
|
|
22
|
+
return 2; // Sextile: 2 signs apart
|
|
23
|
+
case 90:
|
|
24
|
+
return 3; // Square: 3 signs apart
|
|
25
|
+
case 120:
|
|
26
|
+
return 4; // Trine: 4 signs apart
|
|
27
|
+
case 150:
|
|
28
|
+
return 5; // Quincunx: 5 signs apart
|
|
29
|
+
case 180:
|
|
30
|
+
return 6; // Opposition: 6 signs apart
|
|
31
|
+
default:
|
|
32
|
+
// For non-standard aspects, calculate based on 30-degree segments
|
|
33
|
+
return Math.round(normalizedAngle / 30);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Determines if an aspect is applying or separating based on planet speeds
|
|
38
|
+
* @param planetA First planet
|
|
39
|
+
* @param planetB Second planet
|
|
40
|
+
* @param aspectAngle The aspect angle (0, 60, 90, 120, 180, etc.)
|
|
41
|
+
* @returns 'applying', 'separating', or 'exact'
|
|
42
|
+
*/
|
|
43
|
+
function determineAspectApplication(planetA, planetB, aspectAngle) {
|
|
44
|
+
// If either planet doesn't have speed data, we can't determine application
|
|
45
|
+
if (planetA.speed === undefined || planetB.speed === undefined) {
|
|
46
|
+
return 'exact';
|
|
47
|
+
}
|
|
48
|
+
const speedA = planetA.speed;
|
|
49
|
+
const speedB = planetB.speed;
|
|
50
|
+
// Calculate current angular distance (handle wraparound properly)
|
|
51
|
+
const degreeA = (0, astrology_1.normalizeDegree)(planetA.degree);
|
|
52
|
+
const degreeB = (0, astrology_1.normalizeDegree)(planetB.degree);
|
|
53
|
+
let currentDistance = Math.abs(degreeA - degreeB);
|
|
54
|
+
if (currentDistance > 180) {
|
|
55
|
+
currentDistance = 360 - currentDistance;
|
|
56
|
+
}
|
|
57
|
+
// If very close to exact, consider it exact
|
|
58
|
+
const orbFromExact = Math.abs(currentDistance - aspectAngle);
|
|
59
|
+
if ((0, precision_1.isExactAspect)(orbFromExact)) {
|
|
60
|
+
return 'exact';
|
|
61
|
+
}
|
|
62
|
+
// Calculate relative speed (how fast the angle between planets is changing)
|
|
63
|
+
const relativeSpeed = speedA - speedB;
|
|
64
|
+
if (relativeSpeed === 0) {
|
|
65
|
+
return 'exact'; // Planets moving at same speed
|
|
66
|
+
}
|
|
67
|
+
// Use a small, consistent time increment (e.g., 0.1 days) rather than degree-based increment
|
|
68
|
+
const timeIncrement = 0.1; // days
|
|
69
|
+
// Calculate future positions after the same time period for both planets
|
|
70
|
+
const futureA = (0, astrology_1.normalizeDegree)(degreeA + speedA * timeIncrement);
|
|
71
|
+
const futureB = (0, astrology_1.normalizeDegree)(degreeB + speedB * timeIncrement);
|
|
72
|
+
// Calculate current and future angular distances for this aspect
|
|
73
|
+
const currentAspectDistance = Math.abs(currentDistance - aspectAngle);
|
|
74
|
+
let futureSeparation = Math.abs(futureA - futureB);
|
|
75
|
+
if (futureSeparation > 180) {
|
|
76
|
+
futureSeparation = 360 - futureSeparation;
|
|
77
|
+
}
|
|
78
|
+
const futureAspectDistance = Math.abs(futureSeparation - aspectAngle);
|
|
79
|
+
// If future distance to exact aspect is smaller, it's applying
|
|
80
|
+
// If future distance to exact aspect is larger, it's separating
|
|
81
|
+
const isApplying = futureAspectDistance < currentAspectDistance;
|
|
82
|
+
return isApplying ? 'applying' : 'separating';
|
|
83
|
+
}
|
|
5
84
|
function findTightestAspect(aspectDefinitions, planetA, planetB, skipOutOfSignAspects) {
|
|
6
|
-
|
|
85
|
+
const degreeA = (0, precision_1.roundDegrees)((0, astrology_1.normalizeDegree)(planetA.degree));
|
|
86
|
+
const degreeB = (0, precision_1.roundDegrees)((0, astrology_1.normalizeDegree)(planetB.degree));
|
|
87
|
+
let diff = Math.abs(degreeA - degreeB);
|
|
7
88
|
if (diff > 180)
|
|
8
89
|
diff = 360 - diff;
|
|
9
90
|
let tightestAspect = null;
|
|
10
91
|
for (const aspectType of aspectDefinitions) {
|
|
11
|
-
const orb = Math.abs(diff - aspectType.angle);
|
|
92
|
+
const orb = (0, precision_1.roundDegrees)(Math.abs(diff - aspectType.angle));
|
|
12
93
|
if (skipOutOfSignAspects) {
|
|
13
|
-
const planetASign = Math.floor(
|
|
14
|
-
const planetBSign = Math.floor(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
94
|
+
const planetASign = Math.floor(degreeA / 30);
|
|
95
|
+
const planetBSign = Math.floor(degreeB / 30);
|
|
96
|
+
// Calculate expected sign difference for this aspect
|
|
97
|
+
// For major aspects: 0° = 0 signs, 60° = 2 signs, 90° = 3 signs, 120° = 4 signs, 180° = 6 signs
|
|
98
|
+
const expectedSignDiff = getExpectedSignDifference(aspectType.angle);
|
|
99
|
+
let actualSignDiff = Math.abs(planetASign - planetBSign);
|
|
100
|
+
if (actualSignDiff > 6)
|
|
101
|
+
actualSignDiff = 12 - actualSignDiff;
|
|
102
|
+
if (actualSignDiff !== expectedSignDiff) {
|
|
20
103
|
continue;
|
|
21
104
|
}
|
|
22
105
|
}
|
|
23
106
|
if (orb <= aspectType.orb) {
|
|
24
107
|
if (!tightestAspect || orb < tightestAspect.orb) {
|
|
108
|
+
const application = determineAspectApplication(planetA, planetB, aspectType.angle);
|
|
25
109
|
tightestAspect = {
|
|
26
110
|
planetA: planetA.name,
|
|
27
111
|
planetB: planetB.name,
|
|
28
112
|
aspectType: aspectType.name,
|
|
29
113
|
orb,
|
|
114
|
+
application,
|
|
30
115
|
};
|
|
31
116
|
}
|
|
32
117
|
}
|
package/dist/core/astrology.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a degree value to the 0-359.999... range.
|
|
3
|
+
* @param degree The degree value to normalize.
|
|
4
|
+
* @returns The normalized degree value.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeDegree(degree: number): number;
|
|
1
7
|
/**
|
|
2
8
|
* Determines the zodiac sign for a given degree.
|
|
3
|
-
* @param degree The absolute degree (
|
|
9
|
+
* @param degree The absolute degree (any value, will be normalized).
|
|
4
10
|
* @returns The zodiac sign name.
|
|
5
11
|
*/
|
|
6
12
|
export declare function getDegreeSign(degree: number): string;
|
|
7
13
|
/**
|
|
8
14
|
* Calculates the degree within its 30-degree sign (0-29.99...).
|
|
9
|
-
* @param degree The absolute degree (
|
|
15
|
+
* @param degree The absolute degree (any value, will be normalized).
|
|
10
16
|
* @returns The degree within the sign.
|
|
11
17
|
*/
|
|
12
18
|
export declare function getDegreeInSign(degree: number): number;
|
package/dist/core/astrology.js
CHANGED
|
@@ -1,27 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeDegree = normalizeDegree;
|
|
3
4
|
exports.getDegreeSign = getDegreeSign;
|
|
4
5
|
exports.getDegreeInSign = getDegreeInSign;
|
|
5
6
|
const constants_1 = require("../constants");
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes a degree value to the 0-359.999... range.
|
|
9
|
+
* @param degree The degree value to normalize.
|
|
10
|
+
* @returns The normalized degree value.
|
|
11
|
+
*/
|
|
12
|
+
function normalizeDegree(degree) {
|
|
13
|
+
if (!isFinite(degree)) {
|
|
14
|
+
throw new Error(`Invalid degree value: ${degree}`);
|
|
15
|
+
}
|
|
16
|
+
let normalized = degree % 360;
|
|
17
|
+
if (normalized < 0) {
|
|
18
|
+
normalized += 360;
|
|
19
|
+
}
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
6
22
|
/**
|
|
7
23
|
* Determines the zodiac sign for a given degree.
|
|
8
|
-
* @param degree The absolute degree (
|
|
24
|
+
* @param degree The absolute degree (any value, will be normalized).
|
|
9
25
|
* @returns The zodiac sign name.
|
|
10
26
|
*/
|
|
11
27
|
function getDegreeSign(degree) {
|
|
12
|
-
const
|
|
28
|
+
const normalizedDegree = normalizeDegree(degree);
|
|
29
|
+
const signIndex = Math.floor(normalizedDegree / 30);
|
|
13
30
|
if (signIndex < 0 || signIndex >= constants_1.ZODIAC_SIGNS.length) {
|
|
14
|
-
|
|
15
|
-
console.error(`Invalid sign index computed: ${signIndex} for degree ${degree}`);
|
|
31
|
+
console.error(`Invalid sign index computed: ${signIndex} for normalized degree ${normalizedDegree}`);
|
|
16
32
|
return 'Unknown Sign';
|
|
17
33
|
}
|
|
18
34
|
return constants_1.ZODIAC_SIGNS[signIndex];
|
|
19
35
|
}
|
|
20
36
|
/**
|
|
21
37
|
* Calculates the degree within its 30-degree sign (0-29.99...).
|
|
22
|
-
* @param degree The absolute degree (
|
|
38
|
+
* @param degree The absolute degree (any value, will be normalized).
|
|
23
39
|
* @returns The degree within the sign.
|
|
24
40
|
*/
|
|
25
41
|
function getDegreeInSign(degree) {
|
|
26
|
-
|
|
42
|
+
const normalizedDegree = normalizeDegree(degree);
|
|
43
|
+
return normalizedDegree % 30;
|
|
27
44
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Point } from '../types';
|
|
2
|
+
export interface DignityInfo {
|
|
3
|
+
rulers: string[];
|
|
4
|
+
exaltation?: string;
|
|
5
|
+
detriment?: string;
|
|
6
|
+
fall?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Gets the essential dignities for a planet in a specific sign
|
|
10
|
+
* @param planetName Name of the planet
|
|
11
|
+
* @param sign The zodiac sign
|
|
12
|
+
* @returns Array of dignity descriptions
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPlanetDignities(planetName: string, sign: string): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Gets the ruler(s) of a zodiac sign
|
|
17
|
+
* @param sign The zodiac sign
|
|
18
|
+
* @returns Array of ruling planets
|
|
19
|
+
*/
|
|
20
|
+
export declare function getSignRulers(sign: string): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Formats planet dignities for display
|
|
23
|
+
* @param planet The planet point
|
|
24
|
+
* @param houseCusps Array of house cusps (optional)
|
|
25
|
+
* @returns Formatted string with dignities
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatPlanetWithDignities(planet: Point, houseCusps?: number[]): string;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPlanetDignities = getPlanetDignities;
|
|
4
|
+
exports.getSignRulers = getSignRulers;
|
|
5
|
+
exports.formatPlanetWithDignities = formatPlanetWithDignities;
|
|
6
|
+
const astrology_1 = require("./astrology");
|
|
7
|
+
// Essential dignity mappings
|
|
8
|
+
const SIGN_DIGNITIES = {
|
|
9
|
+
Aries: {
|
|
10
|
+
rulers: ['Mars'],
|
|
11
|
+
exaltation: 'Sun',
|
|
12
|
+
detriment: 'Venus',
|
|
13
|
+
fall: 'Saturn',
|
|
14
|
+
},
|
|
15
|
+
Taurus: {
|
|
16
|
+
rulers: ['Venus'],
|
|
17
|
+
exaltation: 'Moon',
|
|
18
|
+
detriment: 'Mars',
|
|
19
|
+
fall: 'Uranus',
|
|
20
|
+
},
|
|
21
|
+
Gemini: {
|
|
22
|
+
rulers: ['Mercury'],
|
|
23
|
+
detriment: 'Jupiter',
|
|
24
|
+
},
|
|
25
|
+
Cancer: {
|
|
26
|
+
rulers: ['Moon'],
|
|
27
|
+
exaltation: 'Jupiter',
|
|
28
|
+
detriment: 'Saturn',
|
|
29
|
+
fall: 'Mars',
|
|
30
|
+
},
|
|
31
|
+
Leo: {
|
|
32
|
+
rulers: ['Sun'],
|
|
33
|
+
detriment: 'Saturn',
|
|
34
|
+
fall: 'Neptune',
|
|
35
|
+
},
|
|
36
|
+
Virgo: {
|
|
37
|
+
rulers: ['Mercury'],
|
|
38
|
+
exaltation: 'Mercury',
|
|
39
|
+
detriment: 'Jupiter',
|
|
40
|
+
fall: 'Venus',
|
|
41
|
+
},
|
|
42
|
+
Libra: {
|
|
43
|
+
rulers: ['Venus'],
|
|
44
|
+
exaltation: 'Saturn',
|
|
45
|
+
detriment: 'Mars',
|
|
46
|
+
fall: 'Sun',
|
|
47
|
+
},
|
|
48
|
+
Scorpio: {
|
|
49
|
+
rulers: ['Mars'],
|
|
50
|
+
detriment: 'Venus',
|
|
51
|
+
fall: 'Moon',
|
|
52
|
+
},
|
|
53
|
+
Sagittarius: {
|
|
54
|
+
rulers: ['Jupiter'],
|
|
55
|
+
detriment: 'Mercury',
|
|
56
|
+
},
|
|
57
|
+
Capricorn: {
|
|
58
|
+
rulers: ['Saturn'],
|
|
59
|
+
exaltation: 'Mars',
|
|
60
|
+
detriment: 'Moon',
|
|
61
|
+
fall: 'Jupiter',
|
|
62
|
+
},
|
|
63
|
+
Aquarius: {
|
|
64
|
+
rulers: ['Saturn'],
|
|
65
|
+
detriment: 'Sun',
|
|
66
|
+
fall: 'Neptune',
|
|
67
|
+
},
|
|
68
|
+
Pisces: {
|
|
69
|
+
rulers: ['Jupiter'],
|
|
70
|
+
exaltation: 'Venus',
|
|
71
|
+
detriment: 'Mercury',
|
|
72
|
+
fall: 'Mercury',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Gets the essential dignities for a planet in a specific sign
|
|
77
|
+
* @param planetName Name of the planet
|
|
78
|
+
* @param sign The zodiac sign
|
|
79
|
+
* @returns Array of dignity descriptions
|
|
80
|
+
*/
|
|
81
|
+
function getPlanetDignities(planetName, sign) {
|
|
82
|
+
const dignities = [];
|
|
83
|
+
const normalizedSign = sign.trim();
|
|
84
|
+
const signInfo = SIGN_DIGNITIES[normalizedSign];
|
|
85
|
+
if (!signInfo)
|
|
86
|
+
return dignities;
|
|
87
|
+
// Check for rulership (domicile)
|
|
88
|
+
if (signInfo.rulers.includes(planetName)) {
|
|
89
|
+
dignities.push(`Domicile`);
|
|
90
|
+
}
|
|
91
|
+
// Check for exaltation
|
|
92
|
+
if (signInfo.exaltation === planetName) {
|
|
93
|
+
dignities.push(`Exaltation`);
|
|
94
|
+
}
|
|
95
|
+
// Check for detriment
|
|
96
|
+
if (signInfo.detriment === planetName) {
|
|
97
|
+
dignities.push(`Detriment`);
|
|
98
|
+
}
|
|
99
|
+
// Check for fall
|
|
100
|
+
if (signInfo.fall && signInfo.fall === planetName) {
|
|
101
|
+
dignities.push(`Fall`);
|
|
102
|
+
}
|
|
103
|
+
return dignities;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Gets the ruler(s) of a zodiac sign
|
|
107
|
+
* @param sign The zodiac sign
|
|
108
|
+
* @returns Array of ruling planets
|
|
109
|
+
*/
|
|
110
|
+
function getSignRulers(sign) {
|
|
111
|
+
const normalizedSign = sign.trim();
|
|
112
|
+
const signInfo = SIGN_DIGNITIES[normalizedSign];
|
|
113
|
+
return signInfo ? signInfo.rulers : [];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Formats planet dignities for display
|
|
117
|
+
* @param planet The planet point
|
|
118
|
+
* @param houseCusps Array of house cusps (optional)
|
|
119
|
+
* @returns Formatted string with dignities
|
|
120
|
+
*/
|
|
121
|
+
function formatPlanetWithDignities(planet, houseCusps) {
|
|
122
|
+
const sign = (0, astrology_1.getDegreeSign)(planet.degree);
|
|
123
|
+
const dignities = getPlanetDignities(planet.name, sign);
|
|
124
|
+
const rulers = getSignRulers(sign);
|
|
125
|
+
let dignitiesStr = '';
|
|
126
|
+
if (dignities.length > 0 && dignities.includes('Domicile')) {
|
|
127
|
+
dignitiesStr = `[${dignities.join(', ')}]`;
|
|
128
|
+
}
|
|
129
|
+
else if (dignities.length > 0 && rulers.length > 0) {
|
|
130
|
+
dignitiesStr = `[${dignities.join(', ')} | Ruler: ${rulers.join(', ')}]`;
|
|
131
|
+
}
|
|
132
|
+
else if (rulers.length > 0) {
|
|
133
|
+
dignitiesStr = `[Ruler: ${rulers.join(', ')}]`;
|
|
134
|
+
}
|
|
135
|
+
return dignitiesStr;
|
|
136
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Point } from '../types';
|
|
2
|
+
export interface DispositorChain {
|
|
3
|
+
planet: string;
|
|
4
|
+
disposedBy: string[];
|
|
5
|
+
disposes: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface DispositorAnalysis {
|
|
8
|
+
chains: DispositorChain[];
|
|
9
|
+
finalDispositors: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds dispositor chains for all planets
|
|
13
|
+
* @param planets Array of planet points
|
|
14
|
+
* @returns Complete dispositor analysis
|
|
15
|
+
*/
|
|
16
|
+
export declare function analyzeDispositors(planets: Point[]): DispositorAnalysis;
|
|
17
|
+
/**
|
|
18
|
+
* Formats the dispositor analysis for display
|
|
19
|
+
* @param analysis The dispositor analysis
|
|
20
|
+
* @returns Array of formatted strings
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatDispositorAnalysis(analysis: DispositorAnalysis): string[];
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeDispositors = analyzeDispositors;
|
|
4
|
+
exports.formatDispositorAnalysis = formatDispositorAnalysis;
|
|
5
|
+
const astrology_1 = require("./astrology");
|
|
6
|
+
const dignities_1 = require("./dignities");
|
|
7
|
+
/**
|
|
8
|
+
* Builds dispositor chains for all planets
|
|
9
|
+
* @param planets Array of planet points
|
|
10
|
+
* @returns Complete dispositor analysis
|
|
11
|
+
*/
|
|
12
|
+
function analyzeDispositors(planets) {
|
|
13
|
+
const dispositorMap = new Map();
|
|
14
|
+
const disposedByMap = new Map();
|
|
15
|
+
// Build the dispositor relationships
|
|
16
|
+
planets.forEach((planet) => {
|
|
17
|
+
const sign = (0, astrology_1.getDegreeSign)(planet.degree);
|
|
18
|
+
const rulers = (0, dignities_1.getSignRulers)(sign);
|
|
19
|
+
dispositorMap.set(planet.name, rulers);
|
|
20
|
+
rulers.forEach((ruler) => {
|
|
21
|
+
if (!disposedByMap.has(ruler)) {
|
|
22
|
+
disposedByMap.set(ruler, []);
|
|
23
|
+
}
|
|
24
|
+
disposedByMap.get(ruler).push(planet.name);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// Find final dispositors - these are the roots of dispositor trees
|
|
28
|
+
const finalDispositors = [];
|
|
29
|
+
const planetNames = new Set(planets.map((p) => p.name));
|
|
30
|
+
planets.forEach((planet) => {
|
|
31
|
+
const rulers = dispositorMap.get(planet.name) || [];
|
|
32
|
+
const isRuledBySelf = rulers.includes(planet.name);
|
|
33
|
+
const isRuledByPlanetNotInChart = rulers.length > 0 && !rulers.some((ruler) => planetNames.has(ruler));
|
|
34
|
+
// A planet is a final dispositor if:
|
|
35
|
+
// 1. It rules itself (traditional dignity)
|
|
36
|
+
// 2. It's ruled by a planet not in the chart
|
|
37
|
+
if (isRuledBySelf || isRuledByPlanetNotInChart) {
|
|
38
|
+
finalDispositors.push(planet.name);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
// Build chains
|
|
42
|
+
const chains = planets.map((planet) => ({
|
|
43
|
+
planet: planet.name,
|
|
44
|
+
disposedBy: dispositorMap.get(planet.name) || [],
|
|
45
|
+
disposes: disposedByMap.get(planet.name) || [],
|
|
46
|
+
}));
|
|
47
|
+
return {
|
|
48
|
+
chains,
|
|
49
|
+
finalDispositors,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detect cycles in dispositor chains
|
|
54
|
+
*/
|
|
55
|
+
function findDispositorCycles(chains) {
|
|
56
|
+
const cycles = [];
|
|
57
|
+
const visited = new Set();
|
|
58
|
+
const path = new Set();
|
|
59
|
+
function dfs(planet, currentPath) {
|
|
60
|
+
if (path.has(planet)) {
|
|
61
|
+
// Found a cycle
|
|
62
|
+
const cycleStart = currentPath.indexOf(planet);
|
|
63
|
+
if (cycleStart !== -1) {
|
|
64
|
+
const cycle = currentPath.slice(cycleStart);
|
|
65
|
+
cycle.push(planet); // Complete the cycle
|
|
66
|
+
if (cycle.length > 2) {
|
|
67
|
+
cycles.push(cycle);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (visited.has(planet))
|
|
73
|
+
return;
|
|
74
|
+
visited.add(planet);
|
|
75
|
+
path.add(planet);
|
|
76
|
+
currentPath.push(planet);
|
|
77
|
+
const chain = chains.find((c) => c.planet === planet);
|
|
78
|
+
if (chain) {
|
|
79
|
+
// Follow the dispositor chain (only follow planets in the chart)
|
|
80
|
+
const inChartRulers = chain.disposedBy.filter((ruler) => chains.some((c) => c.planet === ruler));
|
|
81
|
+
for (const ruler of inChartRulers) {
|
|
82
|
+
dfs(ruler, [...currentPath]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
path.delete(planet);
|
|
86
|
+
currentPath.pop();
|
|
87
|
+
}
|
|
88
|
+
// Start DFS from each planet
|
|
89
|
+
chains.forEach((chain) => {
|
|
90
|
+
if (!visited.has(chain.planet)) {
|
|
91
|
+
dfs(chain.planet, []);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return cycles;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Builds the full dispositor chain for a planet
|
|
98
|
+
* @param planet Starting planet
|
|
99
|
+
* @param chains All dispositor chains
|
|
100
|
+
* @param pathSoFar Array to track the current path (prevents infinite loops)
|
|
101
|
+
* @returns Array of planets in the chain (excluding the starting planet)
|
|
102
|
+
*/
|
|
103
|
+
function buildDispositorChain(planet, chains, pathSoFar = []) {
|
|
104
|
+
const chain = chains.find((c) => c.planet === planet);
|
|
105
|
+
if (!chain || chain.disposedBy.length === 0) {
|
|
106
|
+
return ['(final)'];
|
|
107
|
+
}
|
|
108
|
+
// Get the first in-chart ruler
|
|
109
|
+
const inChartRulers = chain.disposedBy.filter((ruler) => chains.some((c) => c.planet === ruler));
|
|
110
|
+
if (inChartRulers.length === 0) {
|
|
111
|
+
return ['(final)'];
|
|
112
|
+
}
|
|
113
|
+
const ruler = inChartRulers[0];
|
|
114
|
+
// If the ruler is the same as the planet (self-ruler), it's final
|
|
115
|
+
if (ruler === planet) {
|
|
116
|
+
return ['(final)'];
|
|
117
|
+
}
|
|
118
|
+
// If we've seen this ruler in the current path, we have a cycle
|
|
119
|
+
if (pathSoFar.includes(ruler)) {
|
|
120
|
+
return ['(cycle)'];
|
|
121
|
+
}
|
|
122
|
+
// Continue the chain
|
|
123
|
+
const newPath = [...pathSoFar, planet];
|
|
124
|
+
const nextChain = buildDispositorChain(ruler, chains, newPath);
|
|
125
|
+
// If the next chain ends in (final), this whole chain ends in (final)
|
|
126
|
+
// If the next chain ends in (cycle), this chain also ends in (cycle)
|
|
127
|
+
return [ruler, ...nextChain];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Formats the dispositor analysis for display
|
|
131
|
+
* @param analysis The dispositor analysis
|
|
132
|
+
* @returns Array of formatted strings
|
|
133
|
+
*/
|
|
134
|
+
function formatDispositorAnalysis(analysis) {
|
|
135
|
+
const output = [];
|
|
136
|
+
// Build and format chains for each planet
|
|
137
|
+
analysis.chains.forEach((chainInfo) => {
|
|
138
|
+
const chainRest = buildDispositorChain(chainInfo.planet, analysis.chains);
|
|
139
|
+
const chainString = chainRest.join(' → ');
|
|
140
|
+
output.push(`${chainInfo.planet} → ${chainString}`);
|
|
141
|
+
});
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Point } from '../types';
|
|
2
|
+
export interface SignDistributions {
|
|
3
|
+
elements: Record<string, string[]>;
|
|
4
|
+
modalities: Record<string, number>;
|
|
5
|
+
polarities: Record<string, number>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Analyzes the distribution of planets across elements, modalities, and polarities
|
|
9
|
+
* @param planets Array of planet points
|
|
10
|
+
* @param includeAscendant Optional ascendant degree to include in analysis
|
|
11
|
+
* @returns Sign distribution analysis
|
|
12
|
+
*/
|
|
13
|
+
export declare function analyzeSignDistributions(planets: Point[], includeAscendant?: number): SignDistributions;
|
|
14
|
+
/**
|
|
15
|
+
* Formats element distribution for display
|
|
16
|
+
* @param elements Element distribution data
|
|
17
|
+
* @returns Array of formatted strings
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatElementDistribution(elements: Record<string, string[]>): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Formats modality distribution for display
|
|
22
|
+
* @param modalities Modality distribution data
|
|
23
|
+
* @returns Array of formatted strings
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatModalityDistribution(modalities: Record<string, number>): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Formats polarity distribution for display
|
|
28
|
+
* @param polarities Polarity distribution data
|
|
29
|
+
* @returns Array of formatted strings
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatPolarityDistribution(polarities: Record<string, number>): string[];
|