chart2txt 0.7.1 → 0.8.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/core/dispositors.js +2 -0
- package/dist/core/orbResolver.d.ts +38 -0
- package/dist/core/orbResolver.js +167 -0
- package/dist/humandesign2txt.d.ts +239 -0
- package/dist/humandesign2txt.js +1253 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/package.json +1 -1
|
@@ -0,0 +1,1253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* humandesign2txt
|
|
4
|
+
* Converts Human Design chart data to human-readable text for LLM consumption.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ALL_CENTERS = exports.STRATEGY_BY_TYPE = exports.INCARNATION_CROSSES = exports.PROFILE_NAMES = exports.CHANNELS = exports.GATE_CENTERS = exports.GATE_NAMES = exports.GATES = void 0;
|
|
8
|
+
exports.longitudeToGateLine = longitudeToGateLine;
|
|
9
|
+
exports.oppositeGate = oppositeGate;
|
|
10
|
+
exports.calculateActivations = calculateActivations;
|
|
11
|
+
exports.getAllGates = getAllGates;
|
|
12
|
+
exports.getActiveChannels = getActiveChannels;
|
|
13
|
+
exports.getCenterStatus = getCenterStatus;
|
|
14
|
+
exports.calculateType = calculateType;
|
|
15
|
+
exports.calculateAuthority = calculateAuthority;
|
|
16
|
+
exports.calculateDefinition = calculateDefinition;
|
|
17
|
+
exports.getDefinitionIslands = getDefinitionIslands;
|
|
18
|
+
exports.getHangingGates = getHangingGates;
|
|
19
|
+
exports.calculateProfile = calculateProfile;
|
|
20
|
+
exports.getIncarnationCross = getIncarnationCross;
|
|
21
|
+
exports.humandesign2txt = humandesign2txt;
|
|
22
|
+
exports.analyzePartnership = analyzePartnership;
|
|
23
|
+
exports.buildChart = buildChart;
|
|
24
|
+
exports.humandesignPartnership2txt = humandesignPartnership2txt;
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// CONSTANTS
|
|
27
|
+
// ============================================================================
|
|
28
|
+
/**
|
|
29
|
+
* The 64 gates in I Ching wheel order (starting from 0° Aries after 58° adjustment)
|
|
30
|
+
*/
|
|
31
|
+
exports.GATES = [
|
|
32
|
+
41, 19, 13, 49, 30, 55, 37, 63, 22, 36, 25, 17, 21, 51, 42, 3,
|
|
33
|
+
27, 24, 2, 23, 8, 20, 16, 35, 45, 12, 15, 52, 39, 53, 62, 56,
|
|
34
|
+
31, 33, 7, 4, 29, 59, 40, 64, 47, 6, 46, 18, 48, 57, 32, 50,
|
|
35
|
+
28, 44, 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Gate names (I Ching / Human Design names)
|
|
39
|
+
*/
|
|
40
|
+
exports.GATE_NAMES = {
|
|
41
|
+
1: 'Self-Expression',
|
|
42
|
+
2: 'Direction of the Self',
|
|
43
|
+
3: 'Ordering',
|
|
44
|
+
4: 'Formulization',
|
|
45
|
+
5: 'Fixed Rhythms',
|
|
46
|
+
6: 'Friction',
|
|
47
|
+
7: 'The Role of the Self',
|
|
48
|
+
8: 'Contribution',
|
|
49
|
+
9: 'Focus',
|
|
50
|
+
10: 'Behavior of the Self',
|
|
51
|
+
11: 'Ideas',
|
|
52
|
+
12: 'Caution',
|
|
53
|
+
13: 'The Listener',
|
|
54
|
+
14: 'Power Skills',
|
|
55
|
+
15: 'Extremes',
|
|
56
|
+
16: 'Skills',
|
|
57
|
+
17: 'Opinions',
|
|
58
|
+
18: 'Correction',
|
|
59
|
+
19: 'Wanting',
|
|
60
|
+
20: 'The Now',
|
|
61
|
+
21: 'The Hunter',
|
|
62
|
+
22: 'Openness',
|
|
63
|
+
23: 'Assimilation',
|
|
64
|
+
24: 'Rationalization',
|
|
65
|
+
25: 'Innocence',
|
|
66
|
+
26: 'The Egoist',
|
|
67
|
+
27: 'Caring',
|
|
68
|
+
28: 'The Game Player',
|
|
69
|
+
29: 'Perseverance',
|
|
70
|
+
30: 'Feelings',
|
|
71
|
+
31: 'Influence',
|
|
72
|
+
32: 'Continuity',
|
|
73
|
+
33: 'Privacy',
|
|
74
|
+
34: 'Power',
|
|
75
|
+
35: 'Change',
|
|
76
|
+
36: 'Crisis',
|
|
77
|
+
37: 'Friendship',
|
|
78
|
+
38: 'The Fighter',
|
|
79
|
+
39: 'Provocation',
|
|
80
|
+
40: 'Aloneness',
|
|
81
|
+
41: 'Contraction',
|
|
82
|
+
42: 'Growth',
|
|
83
|
+
43: 'Insight',
|
|
84
|
+
44: 'Alertness',
|
|
85
|
+
45: 'The Gatherer',
|
|
86
|
+
46: 'Determination',
|
|
87
|
+
47: 'Realization',
|
|
88
|
+
48: 'Depth',
|
|
89
|
+
49: 'Principles',
|
|
90
|
+
50: 'Values',
|
|
91
|
+
51: 'Shock',
|
|
92
|
+
52: 'Stillness',
|
|
93
|
+
53: 'Beginnings',
|
|
94
|
+
54: 'Ambition',
|
|
95
|
+
55: 'Spirit',
|
|
96
|
+
56: 'Stimulation',
|
|
97
|
+
57: 'Intuition',
|
|
98
|
+
58: 'Vitality',
|
|
99
|
+
59: 'Sexuality',
|
|
100
|
+
60: 'Limitation',
|
|
101
|
+
61: 'Mystery',
|
|
102
|
+
62: 'Details',
|
|
103
|
+
63: 'Doubt',
|
|
104
|
+
64: 'Confusion'
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Which center each gate belongs to
|
|
108
|
+
*/
|
|
109
|
+
exports.GATE_CENTERS = {
|
|
110
|
+
// Head Center
|
|
111
|
+
64: 'Head', 61: 'Head', 63: 'Head',
|
|
112
|
+
// Ajna Center
|
|
113
|
+
47: 'Ajna', 24: 'Ajna', 4: 'Ajna', 17: 'Ajna', 43: 'Ajna', 11: 'Ajna',
|
|
114
|
+
// Throat Center
|
|
115
|
+
62: 'Throat', 23: 'Throat', 56: 'Throat', 35: 'Throat', 12: 'Throat',
|
|
116
|
+
45: 'Throat', 33: 'Throat', 8: 'Throat', 31: 'Throat', 20: 'Throat',
|
|
117
|
+
16: 'Throat',
|
|
118
|
+
// G Center (Self)
|
|
119
|
+
7: 'G Center', 1: 'G Center', 13: 'G Center', 25: 'G Center', 46: 'G Center',
|
|
120
|
+
2: 'G Center', 15: 'G Center', 10: 'G Center',
|
|
121
|
+
// Heart/Ego Center
|
|
122
|
+
21: 'Ego', 40: 'Ego', 26: 'Ego', 51: 'Ego',
|
|
123
|
+
// Solar Plexus Center
|
|
124
|
+
36: 'Solar Plexus', 22: 'Solar Plexus', 37: 'Solar Plexus', 6: 'Solar Plexus',
|
|
125
|
+
49: 'Solar Plexus', 55: 'Solar Plexus', 30: 'Solar Plexus',
|
|
126
|
+
// Sacral Center
|
|
127
|
+
34: 'Sacral', 5: 'Sacral', 14: 'Sacral', 29: 'Sacral', 59: 'Sacral',
|
|
128
|
+
9: 'Sacral', 3: 'Sacral', 42: 'Sacral', 27: 'Sacral',
|
|
129
|
+
// Spleen Center
|
|
130
|
+
48: 'Spleen', 57: 'Spleen', 44: 'Spleen', 50: 'Spleen', 32: 'Spleen',
|
|
131
|
+
28: 'Spleen', 18: 'Spleen',
|
|
132
|
+
// Root Center
|
|
133
|
+
58: 'Root', 38: 'Root', 54: 'Root', 53: 'Root', 60: 'Root', 52: 'Root',
|
|
134
|
+
19: 'Root', 39: 'Root', 41: 'Root'
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* All 36 channels with their gate pairs, names, and connected centers
|
|
138
|
+
*/
|
|
139
|
+
exports.CHANNELS = [
|
|
140
|
+
// Head to Ajna
|
|
141
|
+
{ gates: [64, 47], name: 'Abstraction', centers: ['Head', 'Ajna'] },
|
|
142
|
+
{ gates: [61, 24], name: 'Awareness', centers: ['Head', 'Ajna'] },
|
|
143
|
+
{ gates: [63, 4], name: 'Logic', centers: ['Head', 'Ajna'] },
|
|
144
|
+
// Ajna to Throat
|
|
145
|
+
{ gates: [17, 62], name: 'Acceptance', centers: ['Ajna', 'Throat'] },
|
|
146
|
+
{ gates: [43, 23], name: 'Structuring', centers: ['Ajna', 'Throat'] },
|
|
147
|
+
{ gates: [11, 56], name: 'Curiosity', centers: ['Ajna', 'Throat'] },
|
|
148
|
+
// G Center to Throat
|
|
149
|
+
{ gates: [7, 31], name: 'The Alpha', centers: ['G Center', 'Throat'] },
|
|
150
|
+
{ gates: [1, 8], name: 'Inspiration', centers: ['G Center', 'Throat'] },
|
|
151
|
+
{ gates: [13, 33], name: 'The Prodigal', centers: ['G Center', 'Throat'] },
|
|
152
|
+
{ gates: [10, 20], name: 'Awakening', centers: ['G Center', 'Throat'] },
|
|
153
|
+
// G Center to Sacral
|
|
154
|
+
{ gates: [15, 5], name: 'Rhythm', centers: ['G Center', 'Sacral'] },
|
|
155
|
+
{ gates: [2, 14], name: 'The Beat', centers: ['G Center', 'Sacral'] },
|
|
156
|
+
{ gates: [46, 29], name: 'Discovery', centers: ['G Center', 'Sacral'] },
|
|
157
|
+
// G Center to Spleen
|
|
158
|
+
{ gates: [10, 57], name: 'Perfected Form', centers: ['G Center', 'Spleen'] },
|
|
159
|
+
// G Center to Ego
|
|
160
|
+
{ gates: [25, 51], name: 'Initiation', centers: ['G Center', 'Ego'] },
|
|
161
|
+
// Ego to Throat
|
|
162
|
+
{ gates: [21, 45], name: 'Money', centers: ['Ego', 'Throat'] },
|
|
163
|
+
// Ego to Solar Plexus
|
|
164
|
+
{ gates: [37, 40], name: 'Community', centers: ['Ego', 'Solar Plexus'] },
|
|
165
|
+
// Ego to Spleen
|
|
166
|
+
{ gates: [26, 44], name: 'Surrender', centers: ['Ego', 'Spleen'] },
|
|
167
|
+
// Sacral to Throat
|
|
168
|
+
{ gates: [34, 20], name: 'Charisma', centers: ['Sacral', 'Throat'] },
|
|
169
|
+
// Sacral to Spleen
|
|
170
|
+
{ gates: [34, 57], name: 'Power', centers: ['Sacral', 'Spleen'] },
|
|
171
|
+
{ gates: [27, 50], name: 'Preservation', centers: ['Sacral', 'Spleen'] },
|
|
172
|
+
// Sacral to Solar Plexus
|
|
173
|
+
{ gates: [59, 6], name: 'Intimacy', centers: ['Sacral', 'Solar Plexus'] },
|
|
174
|
+
// Sacral to Root
|
|
175
|
+
{ gates: [42, 53], name: 'Maturation', centers: ['Sacral', 'Root'] },
|
|
176
|
+
{ gates: [3, 60], name: 'Mutation', centers: ['Sacral', 'Root'] },
|
|
177
|
+
{ gates: [9, 52], name: 'Concentration', centers: ['Sacral', 'Root'] },
|
|
178
|
+
// Spleen to Throat
|
|
179
|
+
{ gates: [16, 48], name: 'The Wavelength', centers: ['Spleen', 'Throat'] },
|
|
180
|
+
{ gates: [57, 20], name: 'The Brainwave', centers: ['Spleen', 'Throat'] },
|
|
181
|
+
// Spleen to Root
|
|
182
|
+
{ gates: [18, 58], name: 'Judgment', centers: ['Spleen', 'Root'] },
|
|
183
|
+
{ gates: [28, 38], name: 'Struggle', centers: ['Spleen', 'Root'] },
|
|
184
|
+
{ gates: [32, 54], name: 'Transformation', centers: ['Spleen', 'Root'] },
|
|
185
|
+
// Solar Plexus to Throat
|
|
186
|
+
{ gates: [12, 22], name: 'Openness', centers: ['Solar Plexus', 'Throat'] },
|
|
187
|
+
{ gates: [35, 36], name: 'Transitoriness', centers: ['Solar Plexus', 'Throat'] },
|
|
188
|
+
// Solar Plexus to Root
|
|
189
|
+
{ gates: [19, 49], name: 'Synthesis', centers: ['Solar Plexus', 'Root'] },
|
|
190
|
+
{ gates: [39, 55], name: 'Emoting', centers: ['Solar Plexus', 'Root'] },
|
|
191
|
+
{ gates: [41, 30], name: 'Recognition', centers: ['Solar Plexus', 'Root'] },
|
|
192
|
+
// Sacral to G Center (via 10-34, often grouped with Sacral-Spleen)
|
|
193
|
+
{ gates: [34, 10], name: 'Exploration', centers: ['Sacral', 'G Center'] }
|
|
194
|
+
];
|
|
195
|
+
/**
|
|
196
|
+
* Profile names by line combination
|
|
197
|
+
*/
|
|
198
|
+
exports.PROFILE_NAMES = {
|
|
199
|
+
'1/3': 'Investigator/Martyr',
|
|
200
|
+
'1/4': 'Investigator/Opportunist',
|
|
201
|
+
'2/4': 'Hermit/Opportunist',
|
|
202
|
+
'2/5': 'Hermit/Heretic',
|
|
203
|
+
'3/5': 'Martyr/Heretic',
|
|
204
|
+
'3/6': 'Martyr/Role Model',
|
|
205
|
+
'4/6': 'Opportunist/Role Model',
|
|
206
|
+
'4/1': 'Opportunist/Investigator',
|
|
207
|
+
'5/1': 'Heretic/Investigator',
|
|
208
|
+
'5/2': 'Heretic/Hermit',
|
|
209
|
+
'6/2': 'Role Model/Hermit',
|
|
210
|
+
'6/3': 'Role Model/Martyr'
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Incarnation Cross names by Sun gate (simplified - Right Angle crosses)
|
|
214
|
+
* Format: { gateNumber: "Cross Name" }
|
|
215
|
+
*/
|
|
216
|
+
exports.INCARNATION_CROSSES = {
|
|
217
|
+
1: 'The Sphinx',
|
|
218
|
+
2: 'The Sphinx',
|
|
219
|
+
3: 'Laws',
|
|
220
|
+
4: 'Explanation',
|
|
221
|
+
5: 'Consciousness',
|
|
222
|
+
6: 'Eden',
|
|
223
|
+
7: 'The Sphinx',
|
|
224
|
+
8: 'Contagion',
|
|
225
|
+
9: 'Planning',
|
|
226
|
+
10: 'Vessel of Love',
|
|
227
|
+
11: 'Eden',
|
|
228
|
+
12: 'Eden',
|
|
229
|
+
13: 'The Sphinx',
|
|
230
|
+
14: 'Contagion',
|
|
231
|
+
15: 'Vessel of Love',
|
|
232
|
+
16: 'Planning',
|
|
233
|
+
17: 'Service',
|
|
234
|
+
18: 'Service',
|
|
235
|
+
19: 'The Four Ways',
|
|
236
|
+
20: 'The Sleeping Phoenix',
|
|
237
|
+
21: 'Tension',
|
|
238
|
+
22: 'Rulership',
|
|
239
|
+
23: 'Explanation',
|
|
240
|
+
24: 'The Four Ways',
|
|
241
|
+
25: 'Vessel of Love',
|
|
242
|
+
26: 'Rulership',
|
|
243
|
+
27: 'The Unexpected',
|
|
244
|
+
28: 'The Unexpected',
|
|
245
|
+
29: 'Contagion',
|
|
246
|
+
30: 'Contagion',
|
|
247
|
+
31: 'The Unexpected',
|
|
248
|
+
32: 'Maya',
|
|
249
|
+
33: 'The Four Ways',
|
|
250
|
+
34: 'The Sleeping Phoenix',
|
|
251
|
+
35: 'Consciousness',
|
|
252
|
+
36: 'Eden',
|
|
253
|
+
37: 'Planning',
|
|
254
|
+
38: 'Tension',
|
|
255
|
+
39: 'Tension',
|
|
256
|
+
40: 'Planning',
|
|
257
|
+
41: 'The Unexpected',
|
|
258
|
+
42: 'Maya',
|
|
259
|
+
43: 'Explanation',
|
|
260
|
+
44: 'The Four Ways',
|
|
261
|
+
45: 'Rulership',
|
|
262
|
+
46: 'Vessel of Love',
|
|
263
|
+
47: 'Rulership',
|
|
264
|
+
48: 'Tension',
|
|
265
|
+
49: 'Explanation',
|
|
266
|
+
50: 'Laws',
|
|
267
|
+
51: 'Penetration',
|
|
268
|
+
52: 'Service',
|
|
269
|
+
53: 'Penetration',
|
|
270
|
+
54: 'Penetration',
|
|
271
|
+
55: 'The Sleeping Phoenix',
|
|
272
|
+
56: 'Laws',
|
|
273
|
+
57: 'Penetration',
|
|
274
|
+
58: 'Service',
|
|
275
|
+
59: 'The Sleeping Phoenix',
|
|
276
|
+
60: 'Laws',
|
|
277
|
+
61: 'Maya',
|
|
278
|
+
62: 'Maya',
|
|
279
|
+
63: 'Consciousness',
|
|
280
|
+
64: 'Consciousness'
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Strategy by Type
|
|
284
|
+
*/
|
|
285
|
+
exports.STRATEGY_BY_TYPE = {
|
|
286
|
+
'Generator': 'Wait to Respond',
|
|
287
|
+
'Manifesting Generator': 'Wait to Respond',
|
|
288
|
+
'Manifestor': 'Inform Before Acting',
|
|
289
|
+
'Projector': 'Wait for the Invitation',
|
|
290
|
+
'Reflector': 'Wait a Lunar Cycle'
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* All 9 centers
|
|
294
|
+
*/
|
|
295
|
+
exports.ALL_CENTERS = [
|
|
296
|
+
'Head', 'Ajna', 'Throat', 'G Center', 'Ego', 'Solar Plexus', 'Sacral', 'Spleen', 'Root'
|
|
297
|
+
];
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// CALCULATION FUNCTIONS
|
|
300
|
+
// ============================================================================
|
|
301
|
+
/**
|
|
302
|
+
* Convert a planetary longitude to gate and line
|
|
303
|
+
*/
|
|
304
|
+
function longitudeToGateLine(longitude) {
|
|
305
|
+
const adjustment = 58.0;
|
|
306
|
+
const adjustedLongitude = (longitude + adjustment) % 360;
|
|
307
|
+
const percentage = adjustedLongitude / 360;
|
|
308
|
+
const gateIndex = Math.floor(percentage * 64);
|
|
309
|
+
const gate = exports.GATES[gateIndex];
|
|
310
|
+
// Line calculation: 384 = 64 gates * 6 lines
|
|
311
|
+
const linePosition = (percentage * 384) % 6;
|
|
312
|
+
const line = Math.floor(linePosition) + 1;
|
|
313
|
+
return { gate, line };
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Get the opposite gate (180° across the wheel)
|
|
317
|
+
*/
|
|
318
|
+
function oppositeGate(gate) {
|
|
319
|
+
const index = exports.GATES.indexOf(gate);
|
|
320
|
+
const oppositeIndex = (index + 32) % 64;
|
|
321
|
+
return exports.GATES[oppositeIndex];
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Calculate all activations from planetary positions
|
|
325
|
+
*/
|
|
326
|
+
function calculateActivations(planets) {
|
|
327
|
+
const activations = [];
|
|
328
|
+
for (const planet of planets) {
|
|
329
|
+
const { gate, line } = longitudeToGateLine(planet.longitude);
|
|
330
|
+
activations.push({ planet: planet.name, gate, line });
|
|
331
|
+
// Add Earth (opposite of Sun) and South Node (opposite of North Node)
|
|
332
|
+
if (planet.name === 'Sun') {
|
|
333
|
+
const earthGate = oppositeGate(gate);
|
|
334
|
+
const earthLine = line; // Same line as Sun
|
|
335
|
+
activations.push({ planet: 'Earth', gate: earthGate, line: earthLine });
|
|
336
|
+
}
|
|
337
|
+
else if (planet.name === 'North Node') {
|
|
338
|
+
const southNodeGate = oppositeGate(gate);
|
|
339
|
+
const southNodeLine = line;
|
|
340
|
+
activations.push({ planet: 'South Node', gate: southNodeGate, line: southNodeLine });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return activations;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Get all unique gates from activations, tracking their sources
|
|
347
|
+
*/
|
|
348
|
+
function getAllGates(personalityActivations, designActivations) {
|
|
349
|
+
const gates = new Map();
|
|
350
|
+
for (const activation of personalityActivations) {
|
|
351
|
+
const existing = gates.get(activation.gate);
|
|
352
|
+
if (existing) {
|
|
353
|
+
if (!existing.sources.includes('Personality')) {
|
|
354
|
+
existing.sources.push('Personality');
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
gates.set(activation.gate, {
|
|
359
|
+
center: exports.GATE_CENTERS[activation.gate],
|
|
360
|
+
sources: ['Personality']
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
for (const activation of designActivations) {
|
|
365
|
+
const existing = gates.get(activation.gate);
|
|
366
|
+
if (existing) {
|
|
367
|
+
if (!existing.sources.includes('Design')) {
|
|
368
|
+
existing.sources.push('Design');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
gates.set(activation.gate, {
|
|
373
|
+
center: exports.GATE_CENTERS[activation.gate],
|
|
374
|
+
sources: ['Design']
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return gates;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Determine which channels are active (both gates present)
|
|
382
|
+
*/
|
|
383
|
+
function getActiveChannels(allGates) {
|
|
384
|
+
const activeChannels = [];
|
|
385
|
+
for (const channel of exports.CHANNELS) {
|
|
386
|
+
const [gate1, gate2] = channel.gates;
|
|
387
|
+
if (allGates.has(gate1) && allGates.has(gate2)) {
|
|
388
|
+
activeChannels.push(channel);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return activeChannels;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Determine center status: Defined, Undefined, or Open
|
|
395
|
+
*/
|
|
396
|
+
function getCenterStatus(activeChannels, allGates) {
|
|
397
|
+
const definedCenters = new Set();
|
|
398
|
+
const centersWithGates = new Set();
|
|
399
|
+
// Centers are defined if they have at least one complete channel
|
|
400
|
+
for (const channel of activeChannels) {
|
|
401
|
+
definedCenters.add(channel.centers[0]);
|
|
402
|
+
definedCenters.add(channel.centers[1]);
|
|
403
|
+
}
|
|
404
|
+
// Track which centers have any gates (for undefined vs open)
|
|
405
|
+
for (const [, info] of allGates) {
|
|
406
|
+
centersWithGates.add(info.center);
|
|
407
|
+
}
|
|
408
|
+
const defined = [];
|
|
409
|
+
const undefined = [];
|
|
410
|
+
const open = [];
|
|
411
|
+
for (const center of exports.ALL_CENTERS) {
|
|
412
|
+
if (definedCenters.has(center)) {
|
|
413
|
+
defined.push(center);
|
|
414
|
+
}
|
|
415
|
+
else if (centersWithGates.has(center)) {
|
|
416
|
+
undefined.push(center);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
open.push(center);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { defined, undefined, open };
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Check if there's a motor connected to Throat
|
|
426
|
+
*/
|
|
427
|
+
function hasMotorToThroat(activeChannels, definedCenters) {
|
|
428
|
+
const motors = ['Sacral', 'Solar Plexus', 'Ego', 'Root'];
|
|
429
|
+
// Build a graph of connected centers
|
|
430
|
+
const connections = new Map();
|
|
431
|
+
for (const center of exports.ALL_CENTERS) {
|
|
432
|
+
connections.set(center, new Set());
|
|
433
|
+
}
|
|
434
|
+
for (const channel of activeChannels) {
|
|
435
|
+
connections.get(channel.centers[0]).add(channel.centers[1]);
|
|
436
|
+
connections.get(channel.centers[1]).add(channel.centers[0]);
|
|
437
|
+
}
|
|
438
|
+
// BFS from each motor to see if it reaches Throat
|
|
439
|
+
for (const motor of motors) {
|
|
440
|
+
if (!definedCenters.includes(motor))
|
|
441
|
+
continue;
|
|
442
|
+
const visited = new Set();
|
|
443
|
+
const queue = [motor];
|
|
444
|
+
while (queue.length > 0) {
|
|
445
|
+
const current = queue.shift();
|
|
446
|
+
if (current === 'Throat')
|
|
447
|
+
return true;
|
|
448
|
+
if (visited.has(current))
|
|
449
|
+
continue;
|
|
450
|
+
visited.add(current);
|
|
451
|
+
for (const neighbor of connections.get(current) || []) {
|
|
452
|
+
if (!visited.has(neighbor)) {
|
|
453
|
+
queue.push(neighbor);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Calculate Human Design Type
|
|
462
|
+
*/
|
|
463
|
+
function calculateType(definedCenters, activeChannels) {
|
|
464
|
+
const hasSacral = definedCenters.includes('Sacral');
|
|
465
|
+
const motorToThroat = hasMotorToThroat(activeChannels, definedCenters);
|
|
466
|
+
if (hasSacral) {
|
|
467
|
+
return motorToThroat ? 'Manifesting Generator' : 'Generator';
|
|
468
|
+
}
|
|
469
|
+
if (motorToThroat) {
|
|
470
|
+
return 'Manifestor';
|
|
471
|
+
}
|
|
472
|
+
if (definedCenters.length === 0) {
|
|
473
|
+
return 'Reflector';
|
|
474
|
+
}
|
|
475
|
+
return 'Projector';
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Calculate Inner Authority
|
|
479
|
+
*/
|
|
480
|
+
function calculateAuthority(definedCenters, activeChannels) {
|
|
481
|
+
// Check in order of hierarchy
|
|
482
|
+
if (definedCenters.includes('Solar Plexus')) {
|
|
483
|
+
return 'Emotional (Solar Plexus)';
|
|
484
|
+
}
|
|
485
|
+
if (definedCenters.includes('Sacral')) {
|
|
486
|
+
return 'Sacral';
|
|
487
|
+
}
|
|
488
|
+
if (definedCenters.includes('Spleen')) {
|
|
489
|
+
return 'Splenic';
|
|
490
|
+
}
|
|
491
|
+
// Ego authority requires Ego connected to Throat or G Center
|
|
492
|
+
if (definedCenters.includes('Ego')) {
|
|
493
|
+
const egoToThroat = activeChannels.some(ch => ch.centers.includes('Ego') && ch.centers.includes('Throat'));
|
|
494
|
+
const egoToG = activeChannels.some(ch => ch.centers.includes('Ego') && ch.centers.includes('G Center'));
|
|
495
|
+
if (egoToThroat || egoToG) {
|
|
496
|
+
return 'Ego';
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
// Self-Projected: G Center connected to Throat
|
|
500
|
+
if (definedCenters.includes('G Center')) {
|
|
501
|
+
const gToThroat = activeChannels.some(ch => ch.centers.includes('G Center') && ch.centers.includes('Throat'));
|
|
502
|
+
if (gToThroat) {
|
|
503
|
+
return 'Self-Projected';
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
// Mental/Environment authority (Head/Ajna defined, or just Ajna to Throat)
|
|
507
|
+
if (definedCenters.includes('Ajna') || definedCenters.includes('Head')) {
|
|
508
|
+
return 'Mental (Outer Authority)';
|
|
509
|
+
}
|
|
510
|
+
// Lunar authority for Reflectors
|
|
511
|
+
return 'Lunar';
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Calculate Definition type (how centers are connected)
|
|
515
|
+
*/
|
|
516
|
+
function calculateDefinition(activeChannels, definedCenters) {
|
|
517
|
+
if (definedCenters.length === 0) {
|
|
518
|
+
return 'None';
|
|
519
|
+
}
|
|
520
|
+
const islands = getDefinitionIslands(activeChannels, definedCenters);
|
|
521
|
+
switch (islands.length) {
|
|
522
|
+
case 1: return 'Single Definition';
|
|
523
|
+
case 2: return 'Split Definition';
|
|
524
|
+
case 3: return 'Triple Split Definition';
|
|
525
|
+
case 4: return 'Quadruple Split Definition';
|
|
526
|
+
default: return `${islands.length}-Split Definition`;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Get definition islands - groups of connected defined centers
|
|
531
|
+
*/
|
|
532
|
+
function getDefinitionIslands(activeChannels, definedCenters) {
|
|
533
|
+
if (definedCenters.length === 0) {
|
|
534
|
+
return [];
|
|
535
|
+
}
|
|
536
|
+
// Build connection groups using Union-Find approach
|
|
537
|
+
const parent = new Map();
|
|
538
|
+
for (const center of definedCenters) {
|
|
539
|
+
parent.set(center, center);
|
|
540
|
+
}
|
|
541
|
+
function find(x) {
|
|
542
|
+
if (parent.get(x) !== x) {
|
|
543
|
+
parent.set(x, find(parent.get(x)));
|
|
544
|
+
}
|
|
545
|
+
return parent.get(x);
|
|
546
|
+
}
|
|
547
|
+
function union(x, y) {
|
|
548
|
+
const px = find(x);
|
|
549
|
+
const py = find(y);
|
|
550
|
+
if (px !== py) {
|
|
551
|
+
parent.set(px, py);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
// Connect centers through channels
|
|
555
|
+
for (const channel of activeChannels) {
|
|
556
|
+
const [c1, c2] = channel.centers;
|
|
557
|
+
if (parent.has(c1) && parent.has(c2)) {
|
|
558
|
+
union(c1, c2);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// Group centers by their root
|
|
562
|
+
const islandMap = new Map();
|
|
563
|
+
for (const center of definedCenters) {
|
|
564
|
+
const root = find(center);
|
|
565
|
+
if (!islandMap.has(root)) {
|
|
566
|
+
islandMap.set(root, []);
|
|
567
|
+
}
|
|
568
|
+
islandMap.get(root).push(center);
|
|
569
|
+
}
|
|
570
|
+
// Return as array of arrays, sorted by first center in each island
|
|
571
|
+
return Array.from(islandMap.values()).sort((a, b) => {
|
|
572
|
+
const indexA = exports.ALL_CENTERS.indexOf(a[0]);
|
|
573
|
+
const indexB = exports.ALL_CENTERS.indexOf(b[0]);
|
|
574
|
+
return indexA - indexB;
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get hanging gates - gates that are not part of a complete channel
|
|
579
|
+
*/
|
|
580
|
+
function getHangingGates(allGates, activeChannels) {
|
|
581
|
+
// Get all gates that are part of complete channels
|
|
582
|
+
const channelGates = new Set();
|
|
583
|
+
for (const channel of activeChannels) {
|
|
584
|
+
channelGates.add(channel.gates[0]);
|
|
585
|
+
channelGates.add(channel.gates[1]);
|
|
586
|
+
}
|
|
587
|
+
// Find gates that are NOT in complete channels
|
|
588
|
+
const hangingGates = [];
|
|
589
|
+
for (const [gate, info] of allGates) {
|
|
590
|
+
if (!channelGates.has(gate)) {
|
|
591
|
+
hangingGates.push({ gate, center: info.center });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// Sort by gate number
|
|
595
|
+
return hangingGates.sort((a, b) => a.gate - b.gate);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Calculate Profile from personality and design sun lines
|
|
599
|
+
*/
|
|
600
|
+
function calculateProfile(personalitySunLine, designSunLine) {
|
|
601
|
+
return `${personalitySunLine}/${designSunLine}`;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Get Incarnation Cross
|
|
605
|
+
*/
|
|
606
|
+
function getIncarnationCross(personalitySunGate, personalityEarthGate, designSunGate, designEarthGate, personalitySunLine, designSunLine) {
|
|
607
|
+
const crossName = exports.INCARNATION_CROSSES[personalitySunGate] || 'Unknown';
|
|
608
|
+
// Determine cross type based on lines
|
|
609
|
+
let crossType;
|
|
610
|
+
if (personalitySunLine < 4) {
|
|
611
|
+
crossType = 'Right Angle Cross';
|
|
612
|
+
}
|
|
613
|
+
else if (personalitySunLine >= 5) {
|
|
614
|
+
crossType = 'Left Angle Cross';
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
// Line 4 with specific design line combinations
|
|
618
|
+
crossType = designSunLine < 4 ? 'Juxtaposition Cross' : 'Left Angle Cross';
|
|
619
|
+
}
|
|
620
|
+
return `${crossType} of ${crossName} (${personalitySunGate}/${personalityEarthGate} | ${designSunGate}/${designEarthGate})`;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Main entry point: Convert Human Design API response to formatted text
|
|
624
|
+
*/
|
|
625
|
+
function humandesign2txt(apiResponse, options = {}) {
|
|
626
|
+
const { personality, design } = apiResponse;
|
|
627
|
+
// Calculate activations
|
|
628
|
+
const personalityActivations = calculateActivations(personality.planets);
|
|
629
|
+
const designActivations = calculateActivations(design.planets);
|
|
630
|
+
// Get all gates
|
|
631
|
+
const allGates = getAllGates(personalityActivations, designActivations);
|
|
632
|
+
// Get active channels
|
|
633
|
+
const activeChannels = getActiveChannels(allGates);
|
|
634
|
+
// Get center status
|
|
635
|
+
const { defined, undefined: undefinedCenters, open } = getCenterStatus(activeChannels, allGates);
|
|
636
|
+
// Calculate Type, Authority, Definition
|
|
637
|
+
const type = calculateType(defined, activeChannels);
|
|
638
|
+
const strategy = exports.STRATEGY_BY_TYPE[type];
|
|
639
|
+
const authority = calculateAuthority(defined, activeChannels);
|
|
640
|
+
const definition = calculateDefinition(activeChannels, defined);
|
|
641
|
+
const definitionIslands = getDefinitionIslands(activeChannels, defined);
|
|
642
|
+
// Calculate hanging gates
|
|
643
|
+
const hangingGates = getHangingGates(allGates, activeChannels);
|
|
644
|
+
// Calculate Profile
|
|
645
|
+
const personalitySun = personalityActivations.find(a => a.planet === 'Sun');
|
|
646
|
+
const designSun = designActivations.find(a => a.planet === 'Sun');
|
|
647
|
+
const profile = calculateProfile(personalitySun.line, designSun.line);
|
|
648
|
+
const profileName = exports.PROFILE_NAMES[profile] || '';
|
|
649
|
+
// Get Incarnation Cross
|
|
650
|
+
const personalityEarth = personalityActivations.find(a => a.planet === 'Earth');
|
|
651
|
+
const designEarth = designActivations.find(a => a.planet === 'Earth');
|
|
652
|
+
const incarnationCross = getIncarnationCross(personalitySun.gate, personalityEarth.gate, designSun.gate, designEarth.gate, personalitySun.line, designSun.line);
|
|
653
|
+
// Build the chart object
|
|
654
|
+
const chart = {
|
|
655
|
+
name: options.name || 'Chart',
|
|
656
|
+
location: options.location || `${personality.location.latitude}, ${personality.location.longitude}`,
|
|
657
|
+
date: personality.date,
|
|
658
|
+
time: personality.time,
|
|
659
|
+
type,
|
|
660
|
+
strategy,
|
|
661
|
+
authority,
|
|
662
|
+
definition,
|
|
663
|
+
definitionIslands,
|
|
664
|
+
profile,
|
|
665
|
+
profileName,
|
|
666
|
+
incarnationCross,
|
|
667
|
+
definedCenters: defined,
|
|
668
|
+
undefinedCenters,
|
|
669
|
+
openCenters: open,
|
|
670
|
+
activeChannels,
|
|
671
|
+
hangingGates,
|
|
672
|
+
allGates,
|
|
673
|
+
personalityActivations,
|
|
674
|
+
designActivations
|
|
675
|
+
};
|
|
676
|
+
// Format to text
|
|
677
|
+
return formatHumanDesignToText(chart);
|
|
678
|
+
}
|
|
679
|
+
// ============================================================================
|
|
680
|
+
// TEXT FORMATTER
|
|
681
|
+
// ============================================================================
|
|
682
|
+
function formatHumanDesignToText(chart) {
|
|
683
|
+
const lines = [];
|
|
684
|
+
// Metadata
|
|
685
|
+
lines.push('[METADATA]');
|
|
686
|
+
lines.push('chart_type: human_design');
|
|
687
|
+
lines.push('');
|
|
688
|
+
// Chart header
|
|
689
|
+
lines.push(`[CHART: ${chart.name}]`);
|
|
690
|
+
lines.push(`[BIRTHDATA] ${chart.location} | ${chart.date} | ${chart.time}`);
|
|
691
|
+
lines.push('');
|
|
692
|
+
// Type section
|
|
693
|
+
lines.push('[TYPE]');
|
|
694
|
+
lines.push(`Type: ${chart.type}`);
|
|
695
|
+
lines.push(`Strategy: ${chart.strategy}`);
|
|
696
|
+
lines.push(`Authority: ${chart.authority}`);
|
|
697
|
+
lines.push(`Definition: ${chart.definition}`);
|
|
698
|
+
if (chart.definitionIslands.length > 1) {
|
|
699
|
+
const islandStrs = chart.definitionIslands.map(island => `[${island.join('+')}]`);
|
|
700
|
+
lines.push(`Definition Islands: ${islandStrs.join(' + ')}`);
|
|
701
|
+
}
|
|
702
|
+
lines.push(`Profile: ${chart.profile}${chart.profileName ? ` (${chart.profileName})` : ''}`);
|
|
703
|
+
lines.push(`Incarnation Cross: ${chart.incarnationCross}`);
|
|
704
|
+
lines.push('');
|
|
705
|
+
// Centers
|
|
706
|
+
lines.push('[CENTERS]');
|
|
707
|
+
if (chart.definedCenters.length > 0) {
|
|
708
|
+
lines.push(`Defined: ${chart.definedCenters.join(', ')}`);
|
|
709
|
+
}
|
|
710
|
+
if (chart.undefinedCenters.length > 0) {
|
|
711
|
+
lines.push(`Undefined: ${chart.undefinedCenters.join(', ')}`);
|
|
712
|
+
}
|
|
713
|
+
if (chart.openCenters.length > 0) {
|
|
714
|
+
lines.push(`Open: ${chart.openCenters.join(', ')}`);
|
|
715
|
+
}
|
|
716
|
+
lines.push('');
|
|
717
|
+
// Channels
|
|
718
|
+
lines.push('[CHANNELS]');
|
|
719
|
+
if (chart.activeChannels.length > 0) {
|
|
720
|
+
for (const channel of chart.activeChannels) {
|
|
721
|
+
lines.push(`${channel.gates[0]}-${channel.gates[1]} (${channel.name}): ${channel.centers[0]} ↔ ${channel.centers[1]}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
lines.push('None');
|
|
726
|
+
}
|
|
727
|
+
lines.push('');
|
|
728
|
+
// Hanging Gates
|
|
729
|
+
lines.push('[HANGING GATES]');
|
|
730
|
+
lines.push('(Gates not part of a complete channel)');
|
|
731
|
+
if (chart.hangingGates.length > 0) {
|
|
732
|
+
for (const hg of chart.hangingGates) {
|
|
733
|
+
lines.push(`${hg.gate}: ${exports.GATE_NAMES[hg.gate]} | ${hg.center}`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
lines.push('None');
|
|
738
|
+
}
|
|
739
|
+
lines.push('');
|
|
740
|
+
// Gates
|
|
741
|
+
lines.push('[GATES]');
|
|
742
|
+
const sortedGates = Array.from(chart.allGates.entries()).sort((a, b) => a[0] - b[0]);
|
|
743
|
+
for (const [gate, info] of sortedGates) {
|
|
744
|
+
const sourcesLabel = info.sources.length === 2 ? 'Both' : info.sources[0];
|
|
745
|
+
lines.push(`${gate}: ${exports.GATE_NAMES[gate]} | ${info.center} (${sourcesLabel})`);
|
|
746
|
+
}
|
|
747
|
+
lines.push('');
|
|
748
|
+
// Personality Activations
|
|
749
|
+
lines.push('[PERSONALITY ACTIVATIONS]');
|
|
750
|
+
const pSun = chart.personalityActivations.find(a => a.planet === 'Sun');
|
|
751
|
+
const pEarth = chart.personalityActivations.find(a => a.planet === 'Earth');
|
|
752
|
+
const pMoon = chart.personalityActivations.find(a => a.planet === 'Moon');
|
|
753
|
+
const pNorth = chart.personalityActivations.find(a => a.planet === 'North Node');
|
|
754
|
+
const pSouth = chart.personalityActivations.find(a => a.planet === 'South Node');
|
|
755
|
+
const pMercury = chart.personalityActivations.find(a => a.planet === 'Mercury');
|
|
756
|
+
const pVenus = chart.personalityActivations.find(a => a.planet === 'Venus');
|
|
757
|
+
const pMars = chart.personalityActivations.find(a => a.planet === 'Mars');
|
|
758
|
+
const pJupiter = chart.personalityActivations.find(a => a.planet === 'Jupiter');
|
|
759
|
+
const pSaturn = chart.personalityActivations.find(a => a.planet === 'Saturn');
|
|
760
|
+
const pUranus = chart.personalityActivations.find(a => a.planet === 'Uranus');
|
|
761
|
+
const pNeptune = chart.personalityActivations.find(a => a.planet === 'Neptune');
|
|
762
|
+
const pPluto = chart.personalityActivations.find(a => a.planet === 'Pluto');
|
|
763
|
+
lines.push(`Sun: ${pSun.gate}.${pSun.line} Earth: ${pEarth.gate}.${pEarth.line}`);
|
|
764
|
+
lines.push(`Moon: ${pMoon.gate}.${pMoon.line}`);
|
|
765
|
+
lines.push(`North Node: ${pNorth.gate}.${pNorth.line} South Node: ${pSouth.gate}.${pSouth.line}`);
|
|
766
|
+
lines.push(`Mercury: ${pMercury.gate}.${pMercury.line} Venus: ${pVenus.gate}.${pVenus.line} Mars: ${pMars.gate}.${pMars.line}`);
|
|
767
|
+
lines.push(`Jupiter: ${pJupiter.gate}.${pJupiter.line} Saturn: ${pSaturn.gate}.${pSaturn.line}`);
|
|
768
|
+
lines.push(`Uranus: ${pUranus.gate}.${pUranus.line} Neptune: ${pNeptune.gate}.${pNeptune.line} Pluto: ${pPluto.gate}.${pPluto.line}`);
|
|
769
|
+
lines.push('');
|
|
770
|
+
// Design Activations
|
|
771
|
+
lines.push('[DESIGN ACTIVATIONS]');
|
|
772
|
+
const dSun = chart.designActivations.find(a => a.planet === 'Sun');
|
|
773
|
+
const dEarth = chart.designActivations.find(a => a.planet === 'Earth');
|
|
774
|
+
const dMoon = chart.designActivations.find(a => a.planet === 'Moon');
|
|
775
|
+
const dNorth = chart.designActivations.find(a => a.planet === 'North Node');
|
|
776
|
+
const dSouth = chart.designActivations.find(a => a.planet === 'South Node');
|
|
777
|
+
const dMercury = chart.designActivations.find(a => a.planet === 'Mercury');
|
|
778
|
+
const dVenus = chart.designActivations.find(a => a.planet === 'Venus');
|
|
779
|
+
const dMars = chart.designActivations.find(a => a.planet === 'Mars');
|
|
780
|
+
const dJupiter = chart.designActivations.find(a => a.planet === 'Jupiter');
|
|
781
|
+
const dSaturn = chart.designActivations.find(a => a.planet === 'Saturn');
|
|
782
|
+
const dUranus = chart.designActivations.find(a => a.planet === 'Uranus');
|
|
783
|
+
const dNeptune = chart.designActivations.find(a => a.planet === 'Neptune');
|
|
784
|
+
const dPluto = chart.designActivations.find(a => a.planet === 'Pluto');
|
|
785
|
+
lines.push(`Sun: ${dSun.gate}.${dSun.line} Earth: ${dEarth.gate}.${dEarth.line}`);
|
|
786
|
+
lines.push(`Moon: ${dMoon.gate}.${dMoon.line}`);
|
|
787
|
+
lines.push(`North Node: ${dNorth.gate}.${dNorth.line} South Node: ${dSouth.gate}.${dSouth.line}`);
|
|
788
|
+
lines.push(`Mercury: ${dMercury.gate}.${dMercury.line} Venus: ${dVenus.gate}.${dVenus.line} Mars: ${dMars.gate}.${dMars.line}`);
|
|
789
|
+
lines.push(`Jupiter: ${dJupiter.gate}.${dJupiter.line} Saturn: ${dSaturn.gate}.${dSaturn.line}`);
|
|
790
|
+
lines.push(`Uranus: ${dUranus.gate}.${dUranus.line} Neptune: ${dNeptune.gate}.${dNeptune.line} Pluto: ${dPluto.gate}.${dPluto.line}`);
|
|
791
|
+
return lines.join('\n');
|
|
792
|
+
}
|
|
793
|
+
// ============================================================================
|
|
794
|
+
// PARTNERSHIP ANALYSIS FUNCTIONS
|
|
795
|
+
// ============================================================================
|
|
796
|
+
/**
|
|
797
|
+
* Analyze the relationship between two Human Design charts
|
|
798
|
+
*/
|
|
799
|
+
function analyzePartnership(chart1, chart2) {
|
|
800
|
+
const electromagneticChannels = [];
|
|
801
|
+
const companionshipChannels = [];
|
|
802
|
+
const dominanceChannels = [];
|
|
803
|
+
const compromiseChannels = [];
|
|
804
|
+
// Get all gates for each person
|
|
805
|
+
const person1Gates = new Set(chart1.allGates.keys());
|
|
806
|
+
const person2Gates = new Set(chart2.allGates.keys());
|
|
807
|
+
// Get channels for each person
|
|
808
|
+
const person1ChannelGates = new Set();
|
|
809
|
+
for (const ch of chart1.activeChannels) {
|
|
810
|
+
person1ChannelGates.add(`${ch.gates[0]}-${ch.gates[1]}`);
|
|
811
|
+
}
|
|
812
|
+
const person2ChannelGates = new Set();
|
|
813
|
+
for (const ch of chart2.activeChannels) {
|
|
814
|
+
person2ChannelGates.add(`${ch.gates[0]}-${ch.gates[1]}`);
|
|
815
|
+
}
|
|
816
|
+
// Analyze each potential channel
|
|
817
|
+
for (const channel of exports.CHANNELS) {
|
|
818
|
+
const [gateA, gateB] = channel.gates;
|
|
819
|
+
const channelKey = `${gateA}-${gateB}`;
|
|
820
|
+
const p1HasGateA = person1Gates.has(gateA);
|
|
821
|
+
const p1HasGateB = person1Gates.has(gateB);
|
|
822
|
+
const p2HasGateA = person2Gates.has(gateA);
|
|
823
|
+
const p2HasGateB = person2Gates.has(gateB);
|
|
824
|
+
const p1HasChannel = p1HasGateA && p1HasGateB;
|
|
825
|
+
const p2HasChannel = p2HasGateA && p2HasGateB;
|
|
826
|
+
// Companionship: Both have the full channel
|
|
827
|
+
if (p1HasChannel && p2HasChannel) {
|
|
828
|
+
companionshipChannels.push({
|
|
829
|
+
channel,
|
|
830
|
+
type: 'companionship',
|
|
831
|
+
person1Gates: [gateA, gateB],
|
|
832
|
+
person2Gates: [gateA, gateB],
|
|
833
|
+
description: `Both ${chart1.name} and ${chart2.name} have the complete ${channel.name} channel - deep mutual understanding in this area`
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
// Electromagnetic: Together they complete a channel neither has alone
|
|
837
|
+
else if (!p1HasChannel && !p2HasChannel) {
|
|
838
|
+
// Check if together they form the channel
|
|
839
|
+
const togetherHasA = p1HasGateA || p2HasGateA;
|
|
840
|
+
const togetherHasB = p1HasGateB || p2HasGateB;
|
|
841
|
+
if (togetherHasA && togetherHasB) {
|
|
842
|
+
// One contributes gate A, other contributes gate B (or both contribute to both)
|
|
843
|
+
const p1Contributes = [];
|
|
844
|
+
const p2Contributes = [];
|
|
845
|
+
if (p1HasGateA)
|
|
846
|
+
p1Contributes.push(gateA);
|
|
847
|
+
if (p1HasGateB)
|
|
848
|
+
p1Contributes.push(gateB);
|
|
849
|
+
if (p2HasGateA)
|
|
850
|
+
p2Contributes.push(gateA);
|
|
851
|
+
if (p2HasGateB)
|
|
852
|
+
p2Contributes.push(gateB);
|
|
853
|
+
// True electromagnetic: each person contributes at least one unique gate
|
|
854
|
+
const p1Unique = p1Contributes.filter(g => !p2Contributes.includes(g));
|
|
855
|
+
const p2Unique = p2Contributes.filter(g => !p1Contributes.includes(g));
|
|
856
|
+
if (p1Unique.length > 0 && p2Unique.length > 0) {
|
|
857
|
+
electromagneticChannels.push({
|
|
858
|
+
channel,
|
|
859
|
+
type: 'electromagnetic',
|
|
860
|
+
person1Gates: p1Contributes,
|
|
861
|
+
person2Gates: p2Contributes,
|
|
862
|
+
description: `${chart1.name} brings gate ${p1Unique.join(', ')}, ${chart2.name} brings gate ${p2Unique.join(', ')} - magnetic attraction`
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
// Compromise: Both have hanging gates that connect
|
|
866
|
+
else if (p1Contributes.length > 0 && p2Contributes.length > 0) {
|
|
867
|
+
compromiseChannels.push({
|
|
868
|
+
channel,
|
|
869
|
+
type: 'compromise',
|
|
870
|
+
person1Gates: p1Contributes,
|
|
871
|
+
person2Gates: p2Contributes,
|
|
872
|
+
description: `Both contribute to the ${channel.name} channel but share gate(s) - requires compromise`
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
// Dominance: One has full channel, other has hanging gate(s)
|
|
878
|
+
else if (p1HasChannel && !p2HasChannel && (p2HasGateA || p2HasGateB)) {
|
|
879
|
+
const p2Gates = [];
|
|
880
|
+
if (p2HasGateA)
|
|
881
|
+
p2Gates.push(gateA);
|
|
882
|
+
if (p2HasGateB)
|
|
883
|
+
p2Gates.push(gateB);
|
|
884
|
+
dominanceChannels.push({
|
|
885
|
+
channel,
|
|
886
|
+
type: 'dominance',
|
|
887
|
+
person1Gates: [gateA, gateB],
|
|
888
|
+
person2Gates: p2Gates,
|
|
889
|
+
description: `${chart1.name} has the complete ${channel.name} channel and leads; ${chart2.name} has gate ${p2Gates.join(', ')}`
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
else if (p2HasChannel && !p1HasChannel && (p1HasGateA || p1HasGateB)) {
|
|
893
|
+
const p1Gates = [];
|
|
894
|
+
if (p1HasGateA)
|
|
895
|
+
p1Gates.push(gateA);
|
|
896
|
+
if (p1HasGateB)
|
|
897
|
+
p1Gates.push(gateB);
|
|
898
|
+
dominanceChannels.push({
|
|
899
|
+
channel,
|
|
900
|
+
type: 'dominance',
|
|
901
|
+
person1Gates: p1Gates,
|
|
902
|
+
person2Gates: [gateA, gateB],
|
|
903
|
+
description: `${chart2.name} has the complete ${channel.name} channel and leads; ${chart1.name} has gate ${p1Gates.join(', ')}`
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
// Calculate composite chart (combined gates and channels)
|
|
908
|
+
const compositeGates = new Map();
|
|
909
|
+
for (const [gate, info] of chart1.allGates) {
|
|
910
|
+
compositeGates.set(gate, { center: info.center, sources: [chart1.name] });
|
|
911
|
+
}
|
|
912
|
+
for (const [gate, info] of chart2.allGates) {
|
|
913
|
+
const existing = compositeGates.get(gate);
|
|
914
|
+
if (existing) {
|
|
915
|
+
existing.sources.push(chart2.name);
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
compositeGates.set(gate, { center: info.center, sources: [chart2.name] });
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const compositeChannels = getActiveChannels(compositeGates);
|
|
922
|
+
const compositeCenterStatus = getCenterStatus(compositeChannels, compositeGates);
|
|
923
|
+
const compositeType = calculateType(compositeCenterStatus.defined, compositeChannels);
|
|
924
|
+
const compositeStrategy = exports.STRATEGY_BY_TYPE[compositeType];
|
|
925
|
+
const compositeDefinition = calculateDefinition(compositeChannels, compositeCenterStatus.defined);
|
|
926
|
+
const compositeDefinitionIslands = getDefinitionIslands(compositeChannels, compositeCenterStatus.defined);
|
|
927
|
+
// Calculate channel breakdown
|
|
928
|
+
const person1ChannelKeys = new Set(chart1.activeChannels.map(ch => `${ch.gates[0]}-${ch.gates[1]}`));
|
|
929
|
+
const person2ChannelKeys = new Set(chart2.activeChannels.map(ch => `${ch.gates[0]}-${ch.gates[1]}`));
|
|
930
|
+
const channelsPerson1Only = [];
|
|
931
|
+
const channelsPerson2Only = [];
|
|
932
|
+
const channelsOnlyTogether = [];
|
|
933
|
+
for (const channel of compositeChannels) {
|
|
934
|
+
const key = `${channel.gates[0]}-${channel.gates[1]}`;
|
|
935
|
+
const p1Has = person1ChannelKeys.has(key);
|
|
936
|
+
const p2Has = person2ChannelKeys.has(key);
|
|
937
|
+
if (p1Has && !p2Has) {
|
|
938
|
+
channelsPerson1Only.push(channel);
|
|
939
|
+
}
|
|
940
|
+
else if (p2Has && !p1Has) {
|
|
941
|
+
channelsPerson2Only.push(channel);
|
|
942
|
+
}
|
|
943
|
+
else if (!p1Has && !p2Has) {
|
|
944
|
+
// Neither has it individually - only together
|
|
945
|
+
channelsOnlyTogether.push(channel);
|
|
946
|
+
}
|
|
947
|
+
// If both have it (companionship), it's not unique to anyone
|
|
948
|
+
}
|
|
949
|
+
// Categorize gates
|
|
950
|
+
const sharedGates = [];
|
|
951
|
+
const uniqueGatesPerson1 = [];
|
|
952
|
+
const uniqueGatesPerson2 = [];
|
|
953
|
+
for (const [gate, info] of compositeGates) {
|
|
954
|
+
const gateConnection = {
|
|
955
|
+
gate,
|
|
956
|
+
gateName: exports.GATE_NAMES[gate],
|
|
957
|
+
center: info.center,
|
|
958
|
+
type: info.sources.length === 2 ? 'shared' :
|
|
959
|
+
info.sources[0] === chart1.name ? 'unique_person1' : 'unique_person2'
|
|
960
|
+
};
|
|
961
|
+
if (gateConnection.type === 'shared') {
|
|
962
|
+
sharedGates.push(gateConnection);
|
|
963
|
+
}
|
|
964
|
+
else if (gateConnection.type === 'unique_person1') {
|
|
965
|
+
uniqueGatesPerson1.push(gateConnection);
|
|
966
|
+
}
|
|
967
|
+
else {
|
|
968
|
+
uniqueGatesPerson2.push(gateConnection);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
person1: chart1,
|
|
973
|
+
person2: chart2,
|
|
974
|
+
electromagneticChannels,
|
|
975
|
+
companionshipChannels,
|
|
976
|
+
dominanceChannels,
|
|
977
|
+
compromiseChannels,
|
|
978
|
+
compositeDefinedCenters: compositeCenterStatus.defined,
|
|
979
|
+
compositeUndefinedCenters: compositeCenterStatus.undefined,
|
|
980
|
+
compositeOpenCenters: compositeCenterStatus.open,
|
|
981
|
+
compositeChannels,
|
|
982
|
+
compositeType,
|
|
983
|
+
compositeStrategy,
|
|
984
|
+
compositeDefinition,
|
|
985
|
+
compositeDefinitionIslands,
|
|
986
|
+
channelsPerson1Only,
|
|
987
|
+
channelsPerson2Only,
|
|
988
|
+
channelsOnlyTogether,
|
|
989
|
+
sharedGates,
|
|
990
|
+
uniqueGatesPerson1,
|
|
991
|
+
uniqueGatesPerson2
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Build a HumanDesignChart object from API response
|
|
996
|
+
*/
|
|
997
|
+
function buildChart(apiResponse, options = {}) {
|
|
998
|
+
const { personality, design } = apiResponse;
|
|
999
|
+
const personalityActivations = calculateActivations(personality.planets);
|
|
1000
|
+
const designActivations = calculateActivations(design.planets);
|
|
1001
|
+
const allGates = getAllGates(personalityActivations, designActivations);
|
|
1002
|
+
const activeChannels = getActiveChannels(allGates);
|
|
1003
|
+
const { defined, undefined: undefinedCenters, open } = getCenterStatus(activeChannels, allGates);
|
|
1004
|
+
const type = calculateType(defined, activeChannels);
|
|
1005
|
+
const strategy = exports.STRATEGY_BY_TYPE[type];
|
|
1006
|
+
const authority = calculateAuthority(defined, activeChannels);
|
|
1007
|
+
const definition = calculateDefinition(activeChannels, defined);
|
|
1008
|
+
const definitionIslands = getDefinitionIslands(activeChannels, defined);
|
|
1009
|
+
const hangingGates = getHangingGates(allGates, activeChannels);
|
|
1010
|
+
const personalitySun = personalityActivations.find(a => a.planet === 'Sun');
|
|
1011
|
+
const designSun = designActivations.find(a => a.planet === 'Sun');
|
|
1012
|
+
const profile = calculateProfile(personalitySun.line, designSun.line);
|
|
1013
|
+
const profileName = exports.PROFILE_NAMES[profile] || '';
|
|
1014
|
+
const personalityEarth = personalityActivations.find(a => a.planet === 'Earth');
|
|
1015
|
+
const designEarth = designActivations.find(a => a.planet === 'Earth');
|
|
1016
|
+
const incarnationCross = getIncarnationCross(personalitySun.gate, personalityEarth.gate, designSun.gate, designEarth.gate, personalitySun.line, designSun.line);
|
|
1017
|
+
return {
|
|
1018
|
+
name: options.name || 'Chart',
|
|
1019
|
+
location: options.location || `${personality.location.latitude}, ${personality.location.longitude}`,
|
|
1020
|
+
date: personality.date,
|
|
1021
|
+
time: personality.time,
|
|
1022
|
+
type,
|
|
1023
|
+
strategy,
|
|
1024
|
+
authority,
|
|
1025
|
+
definition,
|
|
1026
|
+
definitionIslands,
|
|
1027
|
+
profile,
|
|
1028
|
+
profileName,
|
|
1029
|
+
incarnationCross,
|
|
1030
|
+
definedCenters: defined,
|
|
1031
|
+
undefinedCenters,
|
|
1032
|
+
openCenters: open,
|
|
1033
|
+
activeChannels,
|
|
1034
|
+
hangingGates,
|
|
1035
|
+
allGates,
|
|
1036
|
+
personalityActivations,
|
|
1037
|
+
designActivations
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Main entry point for partnership analysis: Convert two Human Design API responses to formatted text
|
|
1042
|
+
*/
|
|
1043
|
+
function humandesignPartnership2txt(apiResponse1, apiResponse2, options = {}) {
|
|
1044
|
+
const chart1 = buildChart(apiResponse1, {
|
|
1045
|
+
name: options.person1Name || 'Person 1',
|
|
1046
|
+
location: options.person1Location
|
|
1047
|
+
});
|
|
1048
|
+
const chart2 = buildChart(apiResponse2, {
|
|
1049
|
+
name: options.person2Name || 'Person 2',
|
|
1050
|
+
location: options.person2Location
|
|
1051
|
+
});
|
|
1052
|
+
const partnership = analyzePartnership(chart1, chart2);
|
|
1053
|
+
return formatPartnershipToText(partnership);
|
|
1054
|
+
}
|
|
1055
|
+
// ============================================================================
|
|
1056
|
+
// PARTNERSHIP TEXT FORMATTER
|
|
1057
|
+
// ============================================================================
|
|
1058
|
+
function formatPartnershipToText(partnership) {
|
|
1059
|
+
const lines = [];
|
|
1060
|
+
const { person1, person2 } = partnership;
|
|
1061
|
+
// Metadata
|
|
1062
|
+
lines.push('[METADATA]');
|
|
1063
|
+
lines.push('chart_type: human_design_partnership');
|
|
1064
|
+
lines.push('');
|
|
1065
|
+
// Individual Chart Summaries
|
|
1066
|
+
lines.push(`[CHART: ${person1.name}]`);
|
|
1067
|
+
lines.push(`[BIRTHDATA] ${person1.location} | ${person1.date} | ${person1.time}`);
|
|
1068
|
+
lines.push(`Type: ${person1.type} | Strategy: ${person1.strategy} | Authority: ${person1.authority}`);
|
|
1069
|
+
lines.push(`Definition: ${person1.definition} | Profile: ${person1.profile}${person1.profileName ? ` (${person1.profileName})` : ''}`);
|
|
1070
|
+
if (person1.definitionIslands.length > 1) {
|
|
1071
|
+
const islandStrs = person1.definitionIslands.map(island => `[${island.join('+')}]`);
|
|
1072
|
+
lines.push(`Definition Islands: ${islandStrs.join(' + ')}`);
|
|
1073
|
+
}
|
|
1074
|
+
lines.push(`Defined Centers: ${person1.definedCenters.join(', ') || 'None'}`);
|
|
1075
|
+
if (person1.hangingGates.length > 0) {
|
|
1076
|
+
const hangingStr = person1.hangingGates.map(hg => `${hg.gate} (${hg.center})`).join(', ');
|
|
1077
|
+
lines.push(`Hanging Gates: ${hangingStr}`);
|
|
1078
|
+
}
|
|
1079
|
+
lines.push('');
|
|
1080
|
+
lines.push(`[CHART: ${person2.name}]`);
|
|
1081
|
+
lines.push(`[BIRTHDATA] ${person2.location} | ${person2.date} | ${person2.time}`);
|
|
1082
|
+
lines.push(`Type: ${person2.type} | Strategy: ${person2.strategy} | Authority: ${person2.authority}`);
|
|
1083
|
+
lines.push(`Definition: ${person2.definition} | Profile: ${person2.profile}${person2.profileName ? ` (${person2.profileName})` : ''}`);
|
|
1084
|
+
if (person2.definitionIslands.length > 1) {
|
|
1085
|
+
const islandStrs = person2.definitionIslands.map(island => `[${island.join('+')}]`);
|
|
1086
|
+
lines.push(`Definition Islands: ${islandStrs.join(' + ')}`);
|
|
1087
|
+
}
|
|
1088
|
+
lines.push(`Defined Centers: ${person2.definedCenters.join(', ') || 'None'}`);
|
|
1089
|
+
if (person2.hangingGates.length > 0) {
|
|
1090
|
+
const hangingStr = person2.hangingGates.map(hg => `${hg.gate} (${hg.center})`).join(', ');
|
|
1091
|
+
lines.push(`Hanging Gates: ${hangingStr}`);
|
|
1092
|
+
}
|
|
1093
|
+
lines.push('');
|
|
1094
|
+
// Partnership Analysis Header
|
|
1095
|
+
lines.push(`[PARTNERSHIP: ${person1.name} & ${person2.name}]`);
|
|
1096
|
+
lines.push('');
|
|
1097
|
+
// Composite Overview
|
|
1098
|
+
lines.push('[COMPOSITE OVERVIEW]');
|
|
1099
|
+
lines.push(`Composite Type: ${partnership.compositeType}`);
|
|
1100
|
+
lines.push(`Composite Strategy: ${partnership.compositeStrategy}`);
|
|
1101
|
+
lines.push(`Composite Definition: ${partnership.compositeDefinition}`);
|
|
1102
|
+
if (partnership.compositeDefinitionIslands.length > 1) {
|
|
1103
|
+
const islandStrs = partnership.compositeDefinitionIslands.map(island => `[${island.join('+')}]`);
|
|
1104
|
+
lines.push(`Composite Definition Islands: ${islandStrs.join(' + ')}`);
|
|
1105
|
+
}
|
|
1106
|
+
if (partnership.compositeDefinedCenters.length > 0) {
|
|
1107
|
+
lines.push(`Defined Centers Together: ${partnership.compositeDefinedCenters.join(', ')}`);
|
|
1108
|
+
}
|
|
1109
|
+
if (partnership.compositeUndefinedCenters.length > 0) {
|
|
1110
|
+
lines.push(`Undefined Centers Together: ${partnership.compositeUndefinedCenters.join(', ')}`);
|
|
1111
|
+
}
|
|
1112
|
+
if (partnership.compositeOpenCenters.length > 0) {
|
|
1113
|
+
lines.push(`Open Centers Together: ${partnership.compositeOpenCenters.join(', ')}`);
|
|
1114
|
+
}
|
|
1115
|
+
lines.push('');
|
|
1116
|
+
// Electromagnetic Connections (Attraction)
|
|
1117
|
+
lines.push('[ELECTROMAGNETIC CONNECTIONS]');
|
|
1118
|
+
lines.push('(Channels completed together - magnetic attraction and chemistry)');
|
|
1119
|
+
if (partnership.electromagneticChannels.length > 0) {
|
|
1120
|
+
for (const conn of partnership.electromagneticChannels) {
|
|
1121
|
+
lines.push(`${conn.channel.gates[0]}-${conn.channel.gates[1]} (${conn.channel.name}): ${conn.channel.centers[0]} ↔ ${conn.channel.centers[1]}`);
|
|
1122
|
+
lines.push(` ${conn.description}`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
lines.push('None');
|
|
1127
|
+
}
|
|
1128
|
+
lines.push('');
|
|
1129
|
+
// Companionship Connections (Understanding)
|
|
1130
|
+
lines.push('[COMPANIONSHIP CONNECTIONS]');
|
|
1131
|
+
lines.push('(Channels both have individually - deep mutual understanding)');
|
|
1132
|
+
if (partnership.companionshipChannels.length > 0) {
|
|
1133
|
+
for (const conn of partnership.companionshipChannels) {
|
|
1134
|
+
lines.push(`${conn.channel.gates[0]}-${conn.channel.gates[1]} (${conn.channel.name}): ${conn.channel.centers[0]} ↔ ${conn.channel.centers[1]}`);
|
|
1135
|
+
lines.push(` ${conn.description}`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
lines.push('None');
|
|
1140
|
+
}
|
|
1141
|
+
lines.push('');
|
|
1142
|
+
// Dominance Connections (Leadership)
|
|
1143
|
+
lines.push('[DOMINANCE CONNECTIONS]');
|
|
1144
|
+
lines.push('(One person has full channel, the other has hanging gate - one naturally leads)');
|
|
1145
|
+
if (partnership.dominanceChannels.length > 0) {
|
|
1146
|
+
for (const conn of partnership.dominanceChannels) {
|
|
1147
|
+
lines.push(`${conn.channel.gates[0]}-${conn.channel.gates[1]} (${conn.channel.name}): ${conn.channel.centers[0]} ↔ ${conn.channel.centers[1]}`);
|
|
1148
|
+
lines.push(` ${conn.description}`);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
lines.push('None');
|
|
1153
|
+
}
|
|
1154
|
+
lines.push('');
|
|
1155
|
+
// Compromise Connections
|
|
1156
|
+
lines.push('[COMPROMISE CONNECTIONS]');
|
|
1157
|
+
lines.push('(Both have hanging gates in same channel - requires negotiation)');
|
|
1158
|
+
if (partnership.compromiseChannels.length > 0) {
|
|
1159
|
+
for (const conn of partnership.compromiseChannels) {
|
|
1160
|
+
lines.push(`${conn.channel.gates[0]}-${conn.channel.gates[1]} (${conn.channel.name}): ${conn.channel.centers[0]} ↔ ${conn.channel.centers[1]}`);
|
|
1161
|
+
lines.push(` ${conn.description}`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
lines.push('None');
|
|
1166
|
+
}
|
|
1167
|
+
lines.push('');
|
|
1168
|
+
// Shared Gates
|
|
1169
|
+
lines.push('[SHARED GATES]');
|
|
1170
|
+
lines.push('(Gates both people have - common ground)');
|
|
1171
|
+
if (partnership.sharedGates.length > 0) {
|
|
1172
|
+
const sortedShared = [...partnership.sharedGates].sort((a, b) => a.gate - b.gate);
|
|
1173
|
+
for (const g of sortedShared) {
|
|
1174
|
+
lines.push(`${g.gate}: ${g.gateName} | ${g.center}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
lines.push('None');
|
|
1179
|
+
}
|
|
1180
|
+
lines.push('');
|
|
1181
|
+
// Unique Gates
|
|
1182
|
+
lines.push(`[UNIQUE GATES: ${person1.name}]`);
|
|
1183
|
+
lines.push(`(Gates only ${person1.name} brings to the relationship)`);
|
|
1184
|
+
if (partnership.uniqueGatesPerson1.length > 0) {
|
|
1185
|
+
const sorted = [...partnership.uniqueGatesPerson1].sort((a, b) => a.gate - b.gate);
|
|
1186
|
+
for (const g of sorted) {
|
|
1187
|
+
lines.push(`${g.gate}: ${g.gateName} | ${g.center}`);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
lines.push('None');
|
|
1192
|
+
}
|
|
1193
|
+
lines.push('');
|
|
1194
|
+
lines.push(`[UNIQUE GATES: ${person2.name}]`);
|
|
1195
|
+
lines.push(`(Gates only ${person2.name} brings to the relationship)`);
|
|
1196
|
+
if (partnership.uniqueGatesPerson2.length > 0) {
|
|
1197
|
+
const sorted = [...partnership.uniqueGatesPerson2].sort((a, b) => a.gate - b.gate);
|
|
1198
|
+
for (const g of sorted) {
|
|
1199
|
+
lines.push(`${g.gate}: ${g.gateName} | ${g.center}`);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
lines.push('None');
|
|
1204
|
+
}
|
|
1205
|
+
lines.push('');
|
|
1206
|
+
// Channel Breakdown
|
|
1207
|
+
lines.push(`[CHANNELS: ${person1.name} Only]`);
|
|
1208
|
+
lines.push(`(Channels only ${person1.name} has)`);
|
|
1209
|
+
if (partnership.channelsPerson1Only.length > 0) {
|
|
1210
|
+
for (const channel of partnership.channelsPerson1Only) {
|
|
1211
|
+
lines.push(`${channel.gates[0]}-${channel.gates[1]} (${channel.name}): ${channel.centers[0]} ↔ ${channel.centers[1]}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
lines.push('None');
|
|
1216
|
+
}
|
|
1217
|
+
lines.push('');
|
|
1218
|
+
lines.push(`[CHANNELS: ${person2.name} Only]`);
|
|
1219
|
+
lines.push(`(Channels only ${person2.name} has)`);
|
|
1220
|
+
if (partnership.channelsPerson2Only.length > 0) {
|
|
1221
|
+
for (const channel of partnership.channelsPerson2Only) {
|
|
1222
|
+
lines.push(`${channel.gates[0]}-${channel.gates[1]} (${channel.name}): ${channel.centers[0]} ↔ ${channel.centers[1]}`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
lines.push('None');
|
|
1227
|
+
}
|
|
1228
|
+
lines.push('');
|
|
1229
|
+
lines.push('[CHANNELS: Only Together]');
|
|
1230
|
+
lines.push('(Channels formed only when both are together - neither has individually)');
|
|
1231
|
+
if (partnership.channelsOnlyTogether.length > 0) {
|
|
1232
|
+
for (const channel of partnership.channelsOnlyTogether) {
|
|
1233
|
+
lines.push(`${channel.gates[0]}-${channel.gates[1]} (${channel.name}): ${channel.centers[0]} ↔ ${channel.centers[1]}`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
lines.push('None');
|
|
1238
|
+
}
|
|
1239
|
+
lines.push('');
|
|
1240
|
+
// Composite Channels (all channels when together)
|
|
1241
|
+
lines.push('[COMPOSITE CHANNELS]');
|
|
1242
|
+
lines.push('(All channels active when together)');
|
|
1243
|
+
if (partnership.compositeChannels.length > 0) {
|
|
1244
|
+
for (const channel of partnership.compositeChannels) {
|
|
1245
|
+
lines.push(`${channel.gates[0]}-${channel.gates[1]} (${channel.name}): ${channel.centers[0]} ↔ ${channel.centers[1]}`);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
lines.push('None');
|
|
1250
|
+
}
|
|
1251
|
+
return lines.join('\n');
|
|
1252
|
+
}
|
|
1253
|
+
exports.default = humandesign2txt;
|