chart2txt 0.5.2 → 0.7.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/README.md +101 -34
- package/dist/chart2txt.d.ts +9 -0
- package/dist/chart2txt.js +30 -0
- package/dist/chart2txt.min.js +1 -1
- package/dist/config/ChartSettings.d.ts +13 -6
- package/dist/config/ChartSettings.js +36 -10
- package/dist/constants.d.ts +17 -2
- package/dist/constants.js +301 -32
- package/dist/core/analysis.d.ts +6 -0
- package/dist/core/analysis.js +235 -0
- package/dist/core/aspectPatterns.d.ts +10 -0
- package/dist/core/aspectPatterns.js +460 -0
- package/dist/core/aspects.d.ts +14 -11
- package/dist/core/aspects.js +142 -40
- package/dist/core/astrology.d.ts +8 -2
- package/dist/core/astrology.js +23 -6
- package/dist/core/dignities.d.ts +2 -0
- package/dist/core/dignities.js +71 -0
- package/dist/core/dispositors.d.ts +9 -0
- package/dist/core/dispositors.js +57 -0
- package/dist/core/grouping.d.ts +9 -0
- package/dist/core/grouping.js +45 -0
- package/dist/core/signDistributions.d.ts +21 -0
- package/dist/core/signDistributions.js +50 -0
- package/dist/core/stelliums.d.ts +10 -0
- package/dist/core/stelliums.js +108 -0
- package/dist/formatters/text/sections/angles.js +4 -4
- package/dist/formatters/text/sections/aspectPatterns.d.ts +9 -0
- package/dist/formatters/text/sections/aspectPatterns.js +199 -0
- package/dist/formatters/text/sections/aspects.d.ts +3 -6
- package/dist/formatters/text/sections/aspects.js +35 -49
- package/dist/formatters/text/sections/birthdata.js +1 -1
- package/dist/formatters/text/sections/dispositors.d.ts +8 -0
- package/dist/formatters/text/sections/dispositors.js +19 -0
- package/dist/formatters/text/sections/houseOverlays.d.ts +11 -6
- package/dist/formatters/text/sections/houseOverlays.js +38 -69
- package/dist/formatters/text/sections/houses.d.ts +6 -0
- package/dist/formatters/text/sections/houses.js +36 -0
- package/dist/formatters/text/sections/metadata.d.ts +2 -0
- package/dist/formatters/text/sections/metadata.js +54 -0
- package/dist/formatters/text/sections/planets.d.ts +3 -5
- package/dist/formatters/text/sections/planets.js +12 -38
- package/dist/formatters/text/sections/signDistributions.d.ts +9 -0
- package/dist/formatters/text/sections/signDistributions.js +21 -0
- package/dist/formatters/text/textFormatter.d.ts +4 -5
- package/dist/formatters/text/textFormatter.js +86 -112
- package/dist/index.d.ts +7 -4
- package/dist/index.js +11 -6
- package/dist/types.d.ts +159 -13
- package/dist/types.js +15 -0
- package/dist/utils/formatting.d.ts +10 -0
- package/dist/utils/formatting.js +56 -0
- package/dist/utils/houseCalculations.d.ts +10 -0
- package/dist/utils/houseCalculations.js +23 -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
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectAspectPatterns = detectAspectPatterns;
|
|
4
|
+
const astrology_1 = require("./astrology");
|
|
5
|
+
/**
|
|
6
|
+
* Create a consistent key for planet+chart combinations
|
|
7
|
+
*/
|
|
8
|
+
function createPlanetKey(planetName, chartName) {
|
|
9
|
+
return chartName ? `${planetName}-${chartName}` : planetName;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a lookup map for aspect relationships between planets
|
|
13
|
+
*/
|
|
14
|
+
function createAspectLookup(aspects) {
|
|
15
|
+
const lookup = new Map();
|
|
16
|
+
aspects.forEach((aspect) => {
|
|
17
|
+
const keyA = createPlanetKey(aspect.planetA, aspect.p1ChartName);
|
|
18
|
+
const keyB = createPlanetKey(aspect.planetB, aspect.p2ChartName);
|
|
19
|
+
if (!lookup.has(keyA)) {
|
|
20
|
+
lookup.set(keyA, new Map());
|
|
21
|
+
}
|
|
22
|
+
if (!lookup.has(keyB)) {
|
|
23
|
+
lookup.set(keyB, new Map());
|
|
24
|
+
}
|
|
25
|
+
lookup.get(keyA).set(keyB, aspect);
|
|
26
|
+
lookup.get(keyB).set(keyA, aspect);
|
|
27
|
+
});
|
|
28
|
+
return lookup;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert Point to PlanetPosition for aspect patterns
|
|
32
|
+
* Note: House information is optional for aspect patterns (except stelliums which are handled separately)
|
|
33
|
+
*/
|
|
34
|
+
function pointToPlanetPosition(point, houseCusps, chartName) {
|
|
35
|
+
const sign = (0, astrology_1.getDegreeSign)(point.degree);
|
|
36
|
+
// For multi-chart patterns, house information may not be meaningful
|
|
37
|
+
// Only calculate house if houseCusps are provided and we're dealing with a single-chart context
|
|
38
|
+
const house = houseCusps && houseCusps.length === 12
|
|
39
|
+
? (() => {
|
|
40
|
+
// Simple house calculation without importing the utility
|
|
41
|
+
const normalizedDegree = point.degree % 360;
|
|
42
|
+
for (let i = 0; i < 12; i++) {
|
|
43
|
+
const currentCusp = houseCusps[i];
|
|
44
|
+
const nextCusp = houseCusps[(i + 1) % 12];
|
|
45
|
+
if (nextCusp > currentCusp) {
|
|
46
|
+
// Normal case: house doesn't cross 0°
|
|
47
|
+
if (normalizedDegree >= currentCusp &&
|
|
48
|
+
normalizedDegree < nextCusp) {
|
|
49
|
+
return i + 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// House crosses 0° (e.g., 350° to 20°)
|
|
54
|
+
if (normalizedDegree >= currentCusp ||
|
|
55
|
+
normalizedDegree < nextCusp) {
|
|
56
|
+
return i + 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
})()
|
|
62
|
+
: undefined;
|
|
63
|
+
return {
|
|
64
|
+
name: point.name,
|
|
65
|
+
degree: point.degree,
|
|
66
|
+
sign,
|
|
67
|
+
house,
|
|
68
|
+
chartName,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Determine the modality (Cardinal, Fixed, Mutable) of a sign
|
|
73
|
+
*/
|
|
74
|
+
function getSignModality(sign) {
|
|
75
|
+
const cardinal = ['Aries', 'Cancer', 'Libra', 'Capricorn'];
|
|
76
|
+
const fixed = ['Taurus', 'Leo', 'Scorpio', 'Aquarius'];
|
|
77
|
+
if (cardinal.includes(sign))
|
|
78
|
+
return 'Cardinal';
|
|
79
|
+
if (fixed.includes(sign))
|
|
80
|
+
return 'Fixed';
|
|
81
|
+
return 'Mutable';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Determine the element (Fire, Earth, Air, Water) of a sign
|
|
85
|
+
*/
|
|
86
|
+
function getSignElement(sign) {
|
|
87
|
+
const fire = ['Aries', 'Leo', 'Sagittarius'];
|
|
88
|
+
const earth = ['Taurus', 'Virgo', 'Capricorn'];
|
|
89
|
+
const air = ['Gemini', 'Libra', 'Aquarius'];
|
|
90
|
+
if (fire.includes(sign))
|
|
91
|
+
return 'Fire';
|
|
92
|
+
if (earth.includes(sign))
|
|
93
|
+
return 'Earth';
|
|
94
|
+
if (air.includes(sign))
|
|
95
|
+
return 'Air';
|
|
96
|
+
return 'Water';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if two planets have a specific aspect type using the pre-calculated aspects
|
|
100
|
+
*/
|
|
101
|
+
function hasSpecificAspect(p1, p2, aspectType, aspectLookup) {
|
|
102
|
+
const key1 = createPlanetKey(p1[0].name, p1[1]);
|
|
103
|
+
const key2 = createPlanetKey(p2[0].name, p2[1]);
|
|
104
|
+
const planet1Aspects = aspectLookup.get(key1);
|
|
105
|
+
if (!planet1Aspects)
|
|
106
|
+
return false;
|
|
107
|
+
const aspectData = planet1Aspects.get(key2);
|
|
108
|
+
return aspectData !== undefined && aspectData.aspectType === aspectType;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get aspect data between two planets if it exists
|
|
112
|
+
*/
|
|
113
|
+
function getAspectBetween(p1, p2, aspectLookup) {
|
|
114
|
+
const key1 = createPlanetKey(p1[0].name, p1[1]);
|
|
115
|
+
const key2 = createPlanetKey(p2[0].name, p2[1]);
|
|
116
|
+
const planet1Aspects = aspectLookup.get(key1);
|
|
117
|
+
if (!planet1Aspects)
|
|
118
|
+
return undefined;
|
|
119
|
+
return planet1Aspects.get(key2);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Detect T-Square patterns
|
|
123
|
+
*/
|
|
124
|
+
function detectTSquares(unionedPoints, aspectLookup, houseCusps) {
|
|
125
|
+
const patterns = [];
|
|
126
|
+
for (let i = 0; i < unionedPoints.length; i++) {
|
|
127
|
+
for (let j = i + 1; j < unionedPoints.length; j++) {
|
|
128
|
+
// Check for opposition
|
|
129
|
+
if (hasSpecificAspect(unionedPoints[i], unionedPoints[j], 'opposition', aspectLookup)) {
|
|
130
|
+
// Look for a third planet that squares both
|
|
131
|
+
for (let k = 0; k < unionedPoints.length; k++) {
|
|
132
|
+
if (k === i || k === j)
|
|
133
|
+
continue;
|
|
134
|
+
if (hasSpecificAspect(unionedPoints[i], unionedPoints[k], 'square', aspectLookup) &&
|
|
135
|
+
hasSpecificAspect(unionedPoints[j], unionedPoints[k], 'square', aspectLookup)) {
|
|
136
|
+
const [pApex, cApex] = unionedPoints[k];
|
|
137
|
+
const [pOpp1, cOpp1] = unionedPoints[i];
|
|
138
|
+
const [pOpp2, cOpp2] = unionedPoints[j];
|
|
139
|
+
const apex = pointToPlanetPosition(pApex, houseCusps, cApex);
|
|
140
|
+
const opp1 = pointToPlanetPosition(pOpp1, houseCusps, cOpp1);
|
|
141
|
+
const opp2 = pointToPlanetPosition(pOpp2, houseCusps, cOpp2);
|
|
142
|
+
// Get actual orbs from pre-calculated aspects
|
|
143
|
+
const oppAspect = getAspectBetween(unionedPoints[i], unionedPoints[j], aspectLookup);
|
|
144
|
+
const square1Aspect = getAspectBetween(unionedPoints[i], unionedPoints[k], aspectLookup);
|
|
145
|
+
const square2Aspect = getAspectBetween(unionedPoints[j], unionedPoints[k], aspectLookup);
|
|
146
|
+
const averageOrb = (oppAspect.orb + square1Aspect.orb + square2Aspect.orb) / 3;
|
|
147
|
+
// Determine modality from apex planet
|
|
148
|
+
const mode = getSignModality(apex.sign);
|
|
149
|
+
patterns.push({
|
|
150
|
+
type: 'T-Square',
|
|
151
|
+
apex,
|
|
152
|
+
opposition: [opp1, opp2],
|
|
153
|
+
mode,
|
|
154
|
+
averageOrb,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return patterns;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Detect Grand Trine patterns
|
|
165
|
+
*/
|
|
166
|
+
function detectGrandTrines(unionedPoints, aspectLookup, houseCusps) {
|
|
167
|
+
const patterns = [];
|
|
168
|
+
for (let i = 0; i < unionedPoints.length; i++) {
|
|
169
|
+
for (let j = i + 1; j < unionedPoints.length; j++) {
|
|
170
|
+
for (let k = j + 1; k < unionedPoints.length; k++) {
|
|
171
|
+
// Check if all three planets form trines with each other
|
|
172
|
+
if (hasSpecificAspect(unionedPoints[i], unionedPoints[j], 'trine', aspectLookup) &&
|
|
173
|
+
hasSpecificAspect(unionedPoints[j], unionedPoints[k], 'trine', aspectLookup) &&
|
|
174
|
+
hasSpecificAspect(unionedPoints[k], unionedPoints[i], 'trine', aspectLookup)) {
|
|
175
|
+
const [p1, c1] = unionedPoints[i];
|
|
176
|
+
const [p2, c2] = unionedPoints[j];
|
|
177
|
+
const [p3, c3] = unionedPoints[k];
|
|
178
|
+
const planet1 = pointToPlanetPosition(p1, houseCusps, c1);
|
|
179
|
+
const planet2 = pointToPlanetPosition(p2, houseCusps, c2);
|
|
180
|
+
const planet3 = pointToPlanetPosition(p3, houseCusps, c3);
|
|
181
|
+
// Get actual orbs from pre-calculated aspects
|
|
182
|
+
const trine1Aspect = getAspectBetween(unionedPoints[i], unionedPoints[j], aspectLookup);
|
|
183
|
+
const trine2Aspect = getAspectBetween(unionedPoints[j], unionedPoints[k], aspectLookup);
|
|
184
|
+
const trine3Aspect = getAspectBetween(unionedPoints[k], unionedPoints[i], aspectLookup);
|
|
185
|
+
const averageOrb = (trine1Aspect.orb + trine2Aspect.orb + trine3Aspect.orb) / 3;
|
|
186
|
+
// Determine element from the planets (should be same element for proper grand trine)
|
|
187
|
+
const element = getSignElement(planet1.sign);
|
|
188
|
+
patterns.push({
|
|
189
|
+
type: 'Grand Trine',
|
|
190
|
+
planets: [planet1, planet2, planet3],
|
|
191
|
+
element,
|
|
192
|
+
averageOrb,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return patterns;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Detect Grand Cross patterns
|
|
202
|
+
*/
|
|
203
|
+
function detectGrandCrosses(unionedPoints, aspectLookup, houseCusps) {
|
|
204
|
+
const patterns = [];
|
|
205
|
+
for (let i = 0; i < unionedPoints.length; i++) {
|
|
206
|
+
for (let j = i + 1; j < unionedPoints.length; j++) {
|
|
207
|
+
for (let k = j + 1; k < unionedPoints.length; k++) {
|
|
208
|
+
for (let l = k + 1; l < unionedPoints.length; l++) {
|
|
209
|
+
const group = [
|
|
210
|
+
unionedPoints[i],
|
|
211
|
+
unionedPoints[j],
|
|
212
|
+
unionedPoints[k],
|
|
213
|
+
unionedPoints[l],
|
|
214
|
+
];
|
|
215
|
+
// Check if planets form two oppositions and four squares
|
|
216
|
+
const pairs = [
|
|
217
|
+
[0, 1],
|
|
218
|
+
[2, 3],
|
|
219
|
+
];
|
|
220
|
+
const otherPairs = [
|
|
221
|
+
[0, 2],
|
|
222
|
+
[1, 3],
|
|
223
|
+
[0, 3],
|
|
224
|
+
[1, 2],
|
|
225
|
+
];
|
|
226
|
+
// Check for two oppositions
|
|
227
|
+
let oppositions = 0;
|
|
228
|
+
let squares = 0;
|
|
229
|
+
const aspectData = [];
|
|
230
|
+
pairs.forEach(([a, b]) => {
|
|
231
|
+
if (hasSpecificAspect(group[a], group[b], 'opposition', aspectLookup)) {
|
|
232
|
+
oppositions++;
|
|
233
|
+
aspectData.push(getAspectBetween(group[a], group[b], aspectLookup));
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
otherPairs.forEach(([a, b]) => {
|
|
237
|
+
if (hasSpecificAspect(group[a], group[b], 'square', aspectLookup)) {
|
|
238
|
+
squares++;
|
|
239
|
+
aspectData.push(getAspectBetween(group[a], group[b], aspectLookup));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
if (oppositions === 2 && squares === 4) {
|
|
243
|
+
const [p1, c1] = group[0];
|
|
244
|
+
const [p2, c2] = group[1];
|
|
245
|
+
const [p3, c3] = group[2];
|
|
246
|
+
const [p4, c4] = group[3];
|
|
247
|
+
const planet1 = pointToPlanetPosition(p1, houseCusps, c1);
|
|
248
|
+
const planet2 = pointToPlanetPosition(p2, houseCusps, c2);
|
|
249
|
+
const planet3 = pointToPlanetPosition(p3, houseCusps, c3);
|
|
250
|
+
const planet4 = pointToPlanetPosition(p4, houseCusps, c4);
|
|
251
|
+
// Calculate average orb from actual aspect data
|
|
252
|
+
const totalOrb = aspectData.reduce((sum, aspect) => sum + aspect.orb, 0);
|
|
253
|
+
const averageOrb = totalOrb / aspectData.length;
|
|
254
|
+
const mode = getSignModality(planet1.sign); // Determine from first planet
|
|
255
|
+
patterns.push({
|
|
256
|
+
type: 'Grand Cross',
|
|
257
|
+
planets: [planet1, planet2, planet3, planet4],
|
|
258
|
+
mode,
|
|
259
|
+
averageOrb,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return patterns;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Detect Yod patterns (two quincunxes to apex planet and one sextile between base planets)
|
|
270
|
+
*/
|
|
271
|
+
function detectYods(unionedPoints, aspectLookup, houseCusps) {
|
|
272
|
+
const patterns = [];
|
|
273
|
+
for (let i = 0; i < unionedPoints.length; i++) {
|
|
274
|
+
for (let j = i + 1; j < unionedPoints.length; j++) {
|
|
275
|
+
// Check for sextile between base planets
|
|
276
|
+
if (hasSpecificAspect(unionedPoints[i], unionedPoints[j], 'sextile', aspectLookup)) {
|
|
277
|
+
// Look for apex planet that forms quincunxes with both
|
|
278
|
+
for (let k = 0; k < unionedPoints.length; k++) {
|
|
279
|
+
if (k === i || k === j)
|
|
280
|
+
continue;
|
|
281
|
+
if (hasSpecificAspect(unionedPoints[i], unionedPoints[k], 'quincunx', aspectLookup) &&
|
|
282
|
+
hasSpecificAspect(unionedPoints[j], unionedPoints[k], 'quincunx', aspectLookup)) {
|
|
283
|
+
const [pApex, cApex] = unionedPoints[k];
|
|
284
|
+
const [pBase1, cBase1] = unionedPoints[i];
|
|
285
|
+
const [pBase2, cBase2] = unionedPoints[j];
|
|
286
|
+
const apex = pointToPlanetPosition(pApex, houseCusps, cApex);
|
|
287
|
+
const base1 = pointToPlanetPosition(pBase1, houseCusps, cBase1);
|
|
288
|
+
const base2 = pointToPlanetPosition(pBase2, houseCusps, cBase2);
|
|
289
|
+
// Get actual orbs from pre-calculated aspects
|
|
290
|
+
const sextileAspect = getAspectBetween(unionedPoints[i], unionedPoints[j], aspectLookup);
|
|
291
|
+
const quincunx1Aspect = getAspectBetween(unionedPoints[i], unionedPoints[k], aspectLookup);
|
|
292
|
+
const quincunx2Aspect = getAspectBetween(unionedPoints[j], unionedPoints[k], aspectLookup);
|
|
293
|
+
const averageOrb = (sextileAspect.orb + quincunx1Aspect.orb + quincunx2Aspect.orb) /
|
|
294
|
+
3;
|
|
295
|
+
patterns.push({
|
|
296
|
+
type: 'Yod',
|
|
297
|
+
apex,
|
|
298
|
+
base: [base1, base2],
|
|
299
|
+
averageOrb,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return patterns;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Detect Mystic Rectangle patterns (two oppositions with sextiles and trines)
|
|
310
|
+
*/
|
|
311
|
+
function detectMysticRectangles(unionedPoints, aspectLookup, houseCusps) {
|
|
312
|
+
const patterns = [];
|
|
313
|
+
for (let i = 0; i < unionedPoints.length; i++) {
|
|
314
|
+
for (let j = i + 1; j < unionedPoints.length; j++) {
|
|
315
|
+
for (let k = j + 1; k < unionedPoints.length; k++) {
|
|
316
|
+
for (let l = k + 1; l < unionedPoints.length; l++) {
|
|
317
|
+
const group = [
|
|
318
|
+
unionedPoints[i],
|
|
319
|
+
unionedPoints[j],
|
|
320
|
+
unionedPoints[k],
|
|
321
|
+
unionedPoints[l],
|
|
322
|
+
];
|
|
323
|
+
// Check for two oppositions and appropriate sextiles/trines
|
|
324
|
+
const combinations = [
|
|
325
|
+
{
|
|
326
|
+
oppositions: [
|
|
327
|
+
[0, 1],
|
|
328
|
+
[2, 3],
|
|
329
|
+
],
|
|
330
|
+
sextiles: [
|
|
331
|
+
[0, 2],
|
|
332
|
+
[0, 3],
|
|
333
|
+
[1, 2],
|
|
334
|
+
[1, 3],
|
|
335
|
+
],
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
oppositions: [
|
|
339
|
+
[0, 2],
|
|
340
|
+
[1, 3],
|
|
341
|
+
],
|
|
342
|
+
sextiles: [
|
|
343
|
+
[0, 1],
|
|
344
|
+
[0, 3],
|
|
345
|
+
[2, 1],
|
|
346
|
+
[2, 3],
|
|
347
|
+
],
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
oppositions: [
|
|
351
|
+
[0, 3],
|
|
352
|
+
[1, 2],
|
|
353
|
+
],
|
|
354
|
+
sextiles: [
|
|
355
|
+
[0, 1],
|
|
356
|
+
[0, 2],
|
|
357
|
+
[3, 1],
|
|
358
|
+
[3, 2],
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
];
|
|
362
|
+
for (const combo of combinations) {
|
|
363
|
+
let validOppositions = 0;
|
|
364
|
+
let validSextiles = 0;
|
|
365
|
+
const aspectData = [];
|
|
366
|
+
combo.oppositions.forEach(([a, b]) => {
|
|
367
|
+
if (hasSpecificAspect(group[a], group[b], 'opposition', aspectLookup)) {
|
|
368
|
+
validOppositions++;
|
|
369
|
+
aspectData.push(getAspectBetween(group[a], group[b], aspectLookup));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
combo.sextiles.forEach(([a, b]) => {
|
|
373
|
+
const sextileAspect = getAspectBetween(group[a], group[b], aspectLookup);
|
|
374
|
+
if (sextileAspect &&
|
|
375
|
+
(sextileAspect.aspectType === 'sextile' ||
|
|
376
|
+
sextileAspect.aspectType === 'trine')) {
|
|
377
|
+
validSextiles++;
|
|
378
|
+
aspectData.push(sextileAspect);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
if (validOppositions === 2 && validSextiles === 4) {
|
|
382
|
+
const [p1, c1] = group[combo.oppositions[0][0]];
|
|
383
|
+
const [p2, c2] = group[combo.oppositions[0][1]];
|
|
384
|
+
const [p3, c3] = group[combo.oppositions[1][0]];
|
|
385
|
+
const [p4, c4] = group[combo.oppositions[1][1]];
|
|
386
|
+
const pos1 = pointToPlanetPosition(p1, houseCusps, c1);
|
|
387
|
+
const pos2 = pointToPlanetPosition(p2, houseCusps, c2);
|
|
388
|
+
const pos3 = pointToPlanetPosition(p3, houseCusps, c3);
|
|
389
|
+
const pos4 = pointToPlanetPosition(p4, houseCusps, c4);
|
|
390
|
+
// Calculate average orb from actual aspect data
|
|
391
|
+
const totalOrb = aspectData.reduce((sum, aspect) => sum + aspect.orb, 0);
|
|
392
|
+
const averageOrb = totalOrb / aspectData.length;
|
|
393
|
+
patterns.push({
|
|
394
|
+
type: 'Mystic Rectangle',
|
|
395
|
+
oppositions: [
|
|
396
|
+
[pos1, pos2],
|
|
397
|
+
[pos3, pos4],
|
|
398
|
+
],
|
|
399
|
+
averageOrb,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return patterns;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Detect Kite patterns (Grand Trine with one opposition)
|
|
411
|
+
*/
|
|
412
|
+
function detectKites(unionedPoints, aspectLookup, houseCusps) {
|
|
413
|
+
const patterns = [];
|
|
414
|
+
const grandTrines = detectGrandTrines(unionedPoints, aspectLookup, houseCusps);
|
|
415
|
+
grandTrines.forEach((grandTrine) => {
|
|
416
|
+
// For each planet in the grand trine, look for opposition to another planet
|
|
417
|
+
grandTrine.planets.forEach((trinePoint) => {
|
|
418
|
+
unionedPoints.forEach((unionedPoint) => {
|
|
419
|
+
const [planet, chartName] = unionedPoint;
|
|
420
|
+
const isPartOfTrine = grandTrine.planets.some((tp) => tp.name === planet.name && tp.chartName === chartName);
|
|
421
|
+
if (!isPartOfTrine) {
|
|
422
|
+
// Find the original UnionedPoint for this trine point
|
|
423
|
+
const trineUnionedPoint = unionedPoints.find(([p, c]) => p.name === trinePoint.name && c === trinePoint.chartName);
|
|
424
|
+
if (trineUnionedPoint &&
|
|
425
|
+
hasSpecificAspect(trineUnionedPoint, unionedPoint, 'opposition', aspectLookup)) {
|
|
426
|
+
const oppositionPlanet = pointToPlanetPosition(planet, houseCusps, chartName);
|
|
427
|
+
const oppositionAspect = getAspectBetween(trineUnionedPoint, unionedPoint, aspectLookup);
|
|
428
|
+
const averageOrb = (grandTrine.averageOrb + oppositionAspect.orb) / 2;
|
|
429
|
+
patterns.push({
|
|
430
|
+
type: 'Kite',
|
|
431
|
+
grandTrine: grandTrine.planets,
|
|
432
|
+
opposition: oppositionPlanet,
|
|
433
|
+
averageOrb,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
return patterns;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Main function to detect aspect patterns (excluding stelliums which are handled separately)
|
|
444
|
+
* This function works with both single-chart and multi-chart scenarios
|
|
445
|
+
* @param planets Array of planets to analyze
|
|
446
|
+
* @param aspects Pre-calculated aspects between planets
|
|
447
|
+
* @param houseCusps Optional house cusps for single-chart reference
|
|
448
|
+
* @param planetChartMap Optional mapping from planet name to chart name for multichart ownership context
|
|
449
|
+
*/
|
|
450
|
+
function detectAspectPatterns(unionedPoints, aspects, houseCusps) {
|
|
451
|
+
const patterns = [];
|
|
452
|
+
const aspectLookup = createAspectLookup(aspects);
|
|
453
|
+
patterns.push(...detectTSquares(unionedPoints, aspectLookup, houseCusps));
|
|
454
|
+
patterns.push(...detectGrandTrines(unionedPoints, aspectLookup, houseCusps));
|
|
455
|
+
patterns.push(...detectGrandCrosses(unionedPoints, aspectLookup, houseCusps));
|
|
456
|
+
patterns.push(...detectYods(unionedPoints, aspectLookup, houseCusps));
|
|
457
|
+
patterns.push(...detectMysticRectangles(unionedPoints, aspectLookup, houseCusps));
|
|
458
|
+
patterns.push(...detectKites(unionedPoints, aspectLookup, houseCusps));
|
|
459
|
+
return patterns;
|
|
460
|
+
}
|
package/dist/core/aspects.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Aspect, AspectData, UnionedPoint } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Unified aspect calculation function that handles both single-chart and multi-chart scenarios
|
|
4
4
|
* @param aspectDefinitions Array of aspect types to check for.
|
|
5
|
-
* @param
|
|
5
|
+
* @param unionedPlanets Array of UnionedPoint pairs to analyze.
|
|
6
|
+
* @param skipOutOfSignAspects Whether to skip aspects that cross sign boundaries.
|
|
7
|
+
* @param aspectStrengthThresholds Thresholds for classifying aspect strength.
|
|
8
|
+
* @param forceChartType Optional override for chart type determination.
|
|
6
9
|
* @returns Array of found aspects.
|
|
7
10
|
*/
|
|
8
|
-
export declare function calculateAspects(aspectDefinitions: Aspect[],
|
|
11
|
+
export declare function calculateAspects(aspectDefinitions: Aspect[], unionedPlanets: UnionedPoint[], skipOutOfSignAspects?: boolean): AspectData[];
|
|
9
12
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @param
|
|
13
|
-
* @param
|
|
14
|
-
* @param
|
|
15
|
-
* @returns Array of found aspects
|
|
13
|
+
* Calculates aspects in a multi-chart context (synastry, transits, etc.)
|
|
14
|
+
* @param aspectDefinitions Array of aspect types to check for
|
|
15
|
+
* @param unionedPlanets Array of UnionedPoint pairs to analyze
|
|
16
|
+
* @param skipOutOfSignAspects Whether to skip aspects that cross sign boundaries
|
|
17
|
+
* @param aspectStrengthThresholds Thresholds for classifying aspect strength
|
|
18
|
+
* @returns Array of found aspects
|
|
16
19
|
*/
|
|
17
|
-
export declare function calculateMultichartAspects(aspectDefinitions: Aspect[],
|
|
20
|
+
export declare function calculateMultichartAspects(aspectDefinitions: Aspect[], unionedPlanets: UnionedPoint[], skipOutOfSignAspects?: boolean): AspectData[];
|