chart2txt 0.6.0 → 0.7.1

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.
Files changed (49) hide show
  1. package/README.md +103 -34
  2. package/dist/chart2txt.d.ts +9 -0
  3. package/dist/chart2txt.js +30 -0
  4. package/dist/chart2txt.min.js +1 -1
  5. package/dist/config/ChartSettings.d.ts +10 -6
  6. package/dist/config/ChartSettings.js +22 -11
  7. package/dist/constants.d.ts +17 -2
  8. package/dist/constants.js +303 -34
  9. package/dist/core/analysis.d.ts +6 -0
  10. package/dist/core/analysis.js +237 -0
  11. package/dist/core/aspectPatterns.d.ts +8 -3
  12. package/dist/core/aspectPatterns.js +234 -218
  13. package/dist/core/aspects.d.ts +14 -11
  14. package/dist/core/aspects.js +49 -32
  15. package/dist/core/dignities.d.ts +2 -27
  16. package/dist/core/dignities.js +56 -121
  17. package/dist/core/dispositors.d.ts +7 -19
  18. package/dist/core/dispositors.js +152 -126
  19. package/dist/core/grouping.d.ts +9 -0
  20. package/dist/core/grouping.js +45 -0
  21. package/dist/core/signDistributions.d.ts +20 -30
  22. package/dist/core/signDistributions.js +25 -122
  23. package/dist/core/stelliums.d.ts +10 -0
  24. package/dist/core/stelliums.js +108 -0
  25. package/dist/formatters/text/sections/aspectPatterns.d.ts +3 -1
  26. package/dist/formatters/text/sections/aspectPatterns.js +118 -94
  27. package/dist/formatters/text/sections/aspects.d.ts +3 -6
  28. package/dist/formatters/text/sections/aspects.js +35 -52
  29. package/dist/formatters/text/sections/dispositors.d.ts +4 -3
  30. package/dist/formatters/text/sections/dispositors.js +12 -8
  31. package/dist/formatters/text/sections/houseOverlays.d.ts +11 -6
  32. package/dist/formatters/text/sections/houseOverlays.js +37 -44
  33. package/dist/formatters/text/sections/metadata.d.ts +2 -0
  34. package/dist/formatters/text/sections/metadata.js +54 -0
  35. package/dist/formatters/text/sections/planets.d.ts +3 -5
  36. package/dist/formatters/text/sections/planets.js +11 -22
  37. package/dist/formatters/text/sections/signDistributions.d.ts +9 -25
  38. package/dist/formatters/text/sections/signDistributions.js +9 -55
  39. package/dist/formatters/text/textFormatter.d.ts +4 -5
  40. package/dist/formatters/text/textFormatter.js +86 -142
  41. package/dist/index.d.ts +7 -4
  42. package/dist/index.js +11 -6
  43. package/dist/types.d.ts +102 -15
  44. package/dist/types.js +15 -0
  45. package/dist/utils/formatting.d.ts +4 -0
  46. package/dist/utils/formatting.js +43 -0
  47. package/dist/utils/houseCalculations.d.ts +10 -13
  48. package/dist/utils/houseCalculations.js +15 -57
  49. package/package.json +1 -1
@@ -2,60 +2,53 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateHouseOverlaysOutput = generateHouseOverlaysOutput;
4
4
  const formatting_1 = require("../../../utils/formatting");
5
- const houseCalculations_1 = require("../../../utils/houseCalculations");
6
5
  /**
7
6
  * Generates the [HOUSE OVERLAYS] section for synastry.
8
- * @param chart1 The first chart data.
9
- * @param chart2 The second chart data.
10
- * @param settings The chart settings, containing the house system calculator.
7
+ * @param overlays The pre-calculated house overlay data.
8
+ * @param chart1Name The name of the first chart.
9
+ * @param chart2Name The name of the second chart.
11
10
  * @returns An array of strings for the output.
12
11
  */
13
- function generateHouseOverlaysOutput(chart1, chart2, settings) {
12
+ function generateHouseOverlaysOutput(overlays, chart1Name, chart2Name) {
14
13
  const output = ['[HOUSE OVERLAYS]'];
15
- const c1Name = chart1.name;
16
- const c2Name = chart2.name;
17
- // Chart 1's planets in Chart 2's houses
18
- if (chart2.houseCusps && chart2.houseCusps.length === 12) {
19
- output.push(`${c1Name}'s planets in ${c2Name}'s houses:`);
20
- if (chart1.planets && chart1.planets.length > 0) {
21
- chart1.planets.forEach((planet) => {
22
- const houseNumber = (0, houseCalculations_1.getHouseForPoint)(planet.degree, chart2.houseCusps);
23
- if (houseNumber) {
24
- output.push(`- ${planet.name}: ${(0, formatting_1.getOrdinal)(houseNumber)}`);
25
- }
26
- else {
27
- output.push(`- ${planet.name}: (Could not determine house in ${c2Name})`);
28
- }
29
- });
30
- }
31
- else {
32
- output.push('(No planets listed for overlay)');
33
- }
14
+ output.push(`${chart1Name}'s planets in ${chart2Name}'s houses:`);
15
+ if (Object.keys(overlays.chart1InChart2Houses).length > 0) {
16
+ output.push(formatHouseOverlayCompact(overlays.chart1InChart2Houses));
34
17
  }
35
18
  else {
36
- output.push(`${c1Name}'s planets in ${c2Name}'s houses: (${c2Name} house cusps not available)`);
19
+ output.push(`(${chart2Name} house cusps not available)`);
37
20
  }
38
- output.push(''); // Blank line between the two overlay sections
39
- // Chart 2's planets in Chart 1's houses
40
- if (chart1.houseCusps && chart1.houseCusps.length === 12) {
41
- output.push(`${c2Name}'s planets in ${c1Name}'s houses:`);
42
- if (chart2.planets && chart2.planets.length > 0) {
43
- chart2.planets.forEach((planet) => {
44
- const houseNumber = (0, houseCalculations_1.getHouseForPoint)(planet.degree, chart1.houseCusps);
45
- if (houseNumber) {
46
- output.push(`- ${planet.name}: ${(0, formatting_1.getOrdinal)(houseNumber)}`);
47
- }
48
- else {
49
- output.push(`- ${planet.name}: (Could not determine house in ${c1Name})`);
50
- }
51
- });
52
- }
53
- else {
54
- output.push('(No planets listed for overlay)');
55
- }
21
+ output.push(`${chart2Name}'s planets in ${chart1Name}'s houses:`);
22
+ if (Object.keys(overlays.chart2InChart1Houses).length > 0) {
23
+ output.push(formatHouseOverlayCompact(overlays.chart2InChart1Houses));
56
24
  }
57
25
  else {
58
- output.push(`${c2Name}'s planets in ${c1Name}'s houses: (${c1Name} house cusps not available)`);
26
+ output.push(`(${chart1Name} house cusps not available)`);
59
27
  }
60
28
  return output;
61
29
  }
30
+ /**
31
+ * Formats house overlays in a compact format, grouping by house.
32
+ * @param overlays Object mapping planet names to house numbers.
33
+ * @returns A compact string representation.
34
+ */
35
+ function formatHouseOverlayCompact(overlays) {
36
+ // Group planets by house
37
+ const houseGroups = {};
38
+ for (const planet in overlays) {
39
+ const house = overlays[planet];
40
+ if (!houseGroups[house]) {
41
+ houseGroups[house] = [];
42
+ }
43
+ houseGroups[house].push(planet);
44
+ }
45
+ // Sort houses numerically and format
46
+ const sortedHouses = Object.keys(houseGroups)
47
+ .map(Number)
48
+ .sort((a, b) => a - b);
49
+ const houseStrings = sortedHouses.map((house) => {
50
+ const planets = houseGroups[house].join(', ');
51
+ return `${(0, formatting_1.getOrdinal)(house)}: ${planets}`;
52
+ });
53
+ return houseStrings.join(' | ');
54
+ }
@@ -1,4 +1,6 @@
1
1
  import { ChartSettings } from '../../../config/ChartSettings';
2
+ import { MultiChartData } from '../../../types';
3
+ export declare function determineChartType(data: MultiChartData): string;
2
4
  /**
3
5
  * Generates the [METADATA] section of the chart output.
4
6
  * @param settings The chart settings.
@@ -1,6 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.determineChartType = determineChartType;
3
4
  exports.generateMetadataOutput = generateMetadataOutput;
5
+ function determineChartType(data) {
6
+ let baseChartString = 'natal';
7
+ let suffixString = '';
8
+ const natalCharts = data.filter(({ chartType }) => chartType !== 'transit' && chartType !== 'event');
9
+ const eventCharts = data.filter(({ chartType }) => chartType === 'event');
10
+ const transitCharts = data.filter(({ chartType }) => chartType === 'transit');
11
+ if (transitCharts.length > 1) {
12
+ throw new Error('Must provide at most one transit chart');
13
+ }
14
+ const hasTransit = transitCharts.length > 0;
15
+ // first determine suffix
16
+ if (natalCharts.length > 0) {
17
+ if (eventCharts.length === 0) {
18
+ if (hasTransit) {
19
+ suffixString = '_with_transit';
20
+ }
21
+ }
22
+ else if (eventCharts.length === 1) {
23
+ suffixString = hasTransit ? '_with_event_and_transit' : '_with_event';
24
+ }
25
+ else {
26
+ suffixString = hasTransit ? '_with_events_and_transit' : '_with_events';
27
+ }
28
+ }
29
+ else {
30
+ // base event charts can have transits
31
+ if (hasTransit) {
32
+ suffixString = '_with_transit';
33
+ }
34
+ }
35
+ // then determine base string
36
+ if (natalCharts.length === 0) {
37
+ if (eventCharts.length === 0) {
38
+ throw new Error('Must provide at least one non-transit chart');
39
+ }
40
+ else if (eventCharts.length === 1) {
41
+ baseChartString = 'event';
42
+ }
43
+ else {
44
+ baseChartString = 'multi_event';
45
+ }
46
+ }
47
+ else if (natalCharts.length === 1) {
48
+ baseChartString = 'natal';
49
+ }
50
+ else if (natalCharts.length === 2) {
51
+ baseChartString = 'synastry';
52
+ }
53
+ else {
54
+ baseChartString = 'group_synastry';
55
+ }
56
+ return baseChartString + suffixString;
57
+ }
4
58
  /**
5
59
  * Generates the [METADATA] section of the chart output.
6
60
  * @param settings The chart settings.
@@ -1,10 +1,8 @@
1
- import { Point } from '../../../types';
2
- import { ChartSettings } from '../../../config/ChartSettings';
1
+ import { PlanetPosition } from '../../../types';
3
2
  /**
4
3
  * Generates the [PLANETS] section of the chart output.
5
- * @param planets Array of planet points.
6
- * @param houseCusps Array of 12 house cusp degrees, or undefined if not available.
4
+ * @param placements Array of planet positions.
7
5
  * @param settings The chart settings.
8
6
  * @returns An array of strings for the output.
9
7
  */
10
- export declare function generatePlanetsOutput(planets: Point[], houseCusps: number[] | undefined, settings: ChartSettings): string[];
8
+ export declare function generatePlanetsOutput(placements: PlanetPosition[]): string[];
@@ -1,37 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generatePlanetsOutput = generatePlanetsOutput;
4
- const astrology_1 = require("../../../core/astrology");
5
- const dignities_1 = require("../../../core/dignities");
6
4
  const formatting_1 = require("../../../utils/formatting");
7
- const houseCalculations_1 = require("../../../utils/houseCalculations");
5
+ const dignities_1 = require("../../../core/dignities");
8
6
  /**
9
7
  * Generates the [PLANETS] section of the chart output.
10
- * @param planets Array of planet points.
11
- * @param houseCusps Array of 12 house cusp degrees, or undefined if not available.
8
+ * @param placements Array of planet positions.
12
9
  * @param settings The chart settings.
13
10
  * @returns An array of strings for the output.
14
11
  */
15
- function generatePlanetsOutput(planets, houseCusps, settings) {
12
+ function generatePlanetsOutput(placements) {
16
13
  const output = ['[PLANETS]'];
17
- planets.forEach((planet) => {
18
- const sign = (0, astrology_1.getDegreeSign)(planet.degree);
19
- const degInSign = Math.floor((0, astrology_1.getDegreeInSign)(planet.degree));
20
- const retrogradeIndicator = planet.speed !== undefined && planet.speed < 0 ? ' Retrograde' : '';
21
- const dignities = (0, dignities_1.formatPlanetWithDignities)(planet, houseCusps);
22
- let line = `${planet.name}: ${degInSign}° ${sign}${retrogradeIndicator}`;
23
- if (dignities) {
24
- line += ` ${dignities}`;
25
- }
26
- if (houseCusps && houseCusps.length === 12) {
27
- const houseNumber = (0, houseCalculations_1.getHouseForPoint)(planet.degree, houseCusps);
28
- if (houseNumber) {
29
- line += `, ${(0, formatting_1.getOrdinal)(houseNumber)} house`;
30
- }
14
+ placements.forEach((planet) => {
15
+ const dignities = (0, dignities_1.formatPlanetWithDignities)(planet);
16
+ const retrograde = planet.speed && planet.speed < 0 ? ' Retrograde' : '';
17
+ let line = `${planet.name}: ${Math.floor(planet.degree % 30)}° ${planet.sign}${retrograde} ${dignities}`;
18
+ if (planet.house) {
19
+ line += `, ${(0, formatting_1.getOrdinal)(planet.house)} house`;
31
20
  }
32
- output.push(line);
21
+ output.push(line.replace(/\s+/g, ' ').trim());
33
22
  });
34
- if (planets.length === 0) {
23
+ if (placements.length === 0) {
35
24
  output.push('No planets listed.');
36
25
  }
37
26
  return output;
@@ -1,25 +1,9 @@
1
- import { Point } from '../../../types';
2
- /**
3
- * Generates the [ELEMENT DISTRIBUTION] section of the chart output.
4
- * @param planets Array of planet points.
5
- * @param chartName Optional chart name for the header.
6
- * @param ascendant Optional ascendant degree to include in analysis.
7
- * @returns An array of strings for the output.
8
- */
9
- export declare function generateElementDistributionOutput(planets: Point[], chartName?: string, ascendant?: number): string[];
10
- /**
11
- * Generates the [MODALITY DISTRIBUTION] section of the chart output.
12
- * @param planets Array of planet points.
13
- * @param chartName Optional chart name for the header.
14
- * @param ascendant Optional ascendant degree to include in analysis.
15
- * @returns An array of strings for the output.
16
- */
17
- export declare function generateModalityDistributionOutput(planets: Point[], chartName?: string, ascendant?: number): string[];
18
- /**
19
- * Generates the [POLARITY] section of the chart output.
20
- * @param planets Array of planet points.
21
- * @param chartName Optional chart name for the header.
22
- * @param ascendant Optional ascendant degree to include in analysis.
23
- * @returns An array of strings for the output.
24
- */
25
- export declare function generatePolarityOutput(planets: Point[], chartName?: string, ascendant?: number): string[];
1
+ export declare function generateElementDistributionOutput(elements: {
2
+ [key: string]: string[];
3
+ }): string[];
4
+ export declare function generateModalityDistributionOutput(modalities: {
5
+ [key: string]: number;
6
+ }): string[];
7
+ export declare function generatePolarityOutput(polarities: {
8
+ [key: string]: number;
9
+ }): string[];
@@ -4,64 +4,18 @@ exports.generateElementDistributionOutput = generateElementDistributionOutput;
4
4
  exports.generateModalityDistributionOutput = generateModalityDistributionOutput;
5
5
  exports.generatePolarityOutput = generatePolarityOutput;
6
6
  const signDistributions_1 = require("../../../core/signDistributions");
7
- /**
8
- * Generates the [ELEMENT DISTRIBUTION] section of the chart output.
9
- * @param planets Array of planet points.
10
- * @param chartName Optional chart name for the header.
11
- * @param ascendant Optional ascendant degree to include in analysis.
12
- * @returns An array of strings for the output.
13
- */
14
- function generateElementDistributionOutput(planets, chartName, ascendant) {
15
- const header = chartName
16
- ? `[ELEMENT DISTRIBUTION: ${chartName}]`
17
- : '[ELEMENT DISTRIBUTION]';
18
- const output = [header];
19
- if (planets.length === 0) {
20
- output.push('No planets available for element analysis.');
21
- return output;
22
- }
23
- const distributions = (0, signDistributions_1.analyzeSignDistributions)(planets, ascendant);
24
- const formattedElements = (0, signDistributions_1.formatElementDistribution)(distributions.elements);
25
- output.push(...formattedElements);
7
+ function generateElementDistributionOutput(elements) {
8
+ const output = ['[ELEMENT DISTRIBUTION]'];
9
+ output.push(...(0, signDistributions_1.formatElementDistribution)(elements));
26
10
  return output;
27
11
  }
28
- /**
29
- * Generates the [MODALITY DISTRIBUTION] section of the chart output.
30
- * @param planets Array of planet points.
31
- * @param chartName Optional chart name for the header.
32
- * @param ascendant Optional ascendant degree to include in analysis.
33
- * @returns An array of strings for the output.
34
- */
35
- function generateModalityDistributionOutput(planets, chartName, ascendant) {
36
- const header = chartName
37
- ? `[MODALITY DISTRIBUTION: ${chartName}]`
38
- : '[MODALITY DISTRIBUTION]';
39
- const output = [header];
40
- if (planets.length === 0) {
41
- output.push('No planets available for modality analysis.');
42
- return output;
43
- }
44
- const distributions = (0, signDistributions_1.analyzeSignDistributions)(planets, ascendant);
45
- const formattedModalities = (0, signDistributions_1.formatModalityDistribution)(distributions.modalities);
46
- output.push(...formattedModalities);
12
+ function generateModalityDistributionOutput(modalities) {
13
+ const output = ['[MODALITY DISTRIBUTION]'];
14
+ output.push(...(0, signDistributions_1.formatModalityDistribution)(modalities));
47
15
  return output;
48
16
  }
49
- /**
50
- * Generates the [POLARITY] section of the chart output.
51
- * @param planets Array of planet points.
52
- * @param chartName Optional chart name for the header.
53
- * @param ascendant Optional ascendant degree to include in analysis.
54
- * @returns An array of strings for the output.
55
- */
56
- function generatePolarityOutput(planets, chartName, ascendant) {
57
- const header = chartName ? `[POLARITY: ${chartName}]` : '[POLARITY]';
58
- const output = [header];
59
- if (planets.length === 0) {
60
- output.push('No planets available for polarity analysis.');
61
- return output;
62
- }
63
- const distributions = (0, signDistributions_1.analyzeSignDistributions)(planets, ascendant);
64
- const formattedPolarities = (0, signDistributions_1.formatPolarityDistribution)(distributions.polarities);
65
- output.push(...formattedPolarities);
17
+ function generatePolarityOutput(polarities) {
18
+ const output = ['[POLARITY]'];
19
+ output.push(...(0, signDistributions_1.formatPolarityDistribution)(polarities));
66
20
  return output;
67
21
  }
@@ -1,8 +1,7 @@
1
- import { ChartData, MultiChartData, PartialSettings } from '../../types';
1
+ import { AstrologicalReport } from '../../types';
2
2
  /**
3
- * Orchestrates the generation of a complete astrological chart report in text format.
4
- * @param data The chart data, can be for a single chart or multiple charts (synastry, transits).
5
- * @param partialSettings Optional: Custom settings to override defaults.
3
+ * Formats a pre-computed and pre-grouped AstrologicalReport into a human-readable text string.
4
+ * @param report The AstrologicalReport object, with aspects already grouped.
6
5
  * @returns A string representing the full chart report.
7
6
  */
8
- export declare function formatChartToText(data: ChartData | MultiChartData, partialSettings?: PartialSettings): string;
7
+ export declare function formatReportToText(report: AstrologicalReport): string;
@@ -1,24 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatChartToText = formatChartToText;
4
- const types_1 = require("../../types");
5
- const ChartSettings_1 = require("../../config/ChartSettings");
6
- const validation_1 = require("../../utils/validation");
7
- const aspects_1 = require("../../core/aspects");
8
- const aspectPatterns_1 = require("../../core/aspectPatterns");
9
- /**
10
- * Helper function to get all points (planets + angles) from a chart for aspect calculation
11
- */
12
- function getAllPointsFromChart(chartData) {
13
- const allPoints = [...chartData.planets];
14
- if (chartData.ascendant !== undefined) {
15
- allPoints.push({ name: 'Ascendant', degree: chartData.ascendant });
16
- }
17
- if (chartData.midheaven !== undefined) {
18
- allPoints.push({ name: 'Midheaven', degree: chartData.midheaven });
19
- }
20
- return allPoints;
21
- }
3
+ exports.formatReportToText = formatReportToText;
22
4
  const metadata_1 = require("./sections/metadata");
23
5
  const chartHeader_1 = require("./sections/chartHeader");
24
6
  const birthdata_1 = require("./sections/birthdata");
@@ -26,160 +8,122 @@ const angles_1 = require("./sections/angles");
26
8
  const houses_1 = require("./sections/houses");
27
9
  const planets_1 = require("./sections/planets");
28
10
  const dispositors_1 = require("./sections/dispositors");
29
- const aspects_2 = require("./sections/aspects");
30
- const aspectPatterns_2 = require("./sections/aspectPatterns");
11
+ const aspects_1 = require("./sections/aspects");
12
+ const aspectPatterns_1 = require("./sections/aspectPatterns");
31
13
  const houseOverlays_1 = require("./sections/houseOverlays");
32
14
  const signDistributions_1 = require("./sections/signDistributions");
33
- const processSingleChartOutput = (settings, chartData, chartTitlePrefix) => {
15
+ const stelliums_1 = require("../../core/stelliums");
16
+ const processSingleChartOutput = (analysis, settings, chartTitlePrefix) => {
34
17
  const outputLines = [];
35
- outputLines.push(...(0, chartHeader_1.generateChartHeaderOutput)(chartData.name, chartTitlePrefix));
36
- outputLines.push(...(0, birthdata_1.generateBirthdataOutput)(chartData.location, chartData.timestamp, settings));
37
- outputLines.push(...(0, angles_1.generateAnglesOutput)(chartData.ascendant, chartData.midheaven));
38
- outputLines.push(...(0, houses_1.generateHousesOutput)(chartData.houseCusps));
39
- outputLines.push(...(0, planets_1.generatePlanetsOutput)(chartData.planets, chartData.houseCusps, settings));
40
- outputLines.push(...(0, dispositors_1.generateDispositorsOutput)(chartData.planets));
41
- outputLines.push(...(0, signDistributions_1.generateElementDistributionOutput)(chartData.planets, undefined, chartData.ascendant));
42
- outputLines.push(...(0, signDistributions_1.generateModalityDistributionOutput)(chartData.planets, undefined, chartData.ascendant));
43
- outputLines.push(...(0, signDistributions_1.generatePolarityOutput)(chartData.planets, undefined, chartData.ascendant));
44
- const aspects = (0, aspects_1.calculateAspects)(settings.aspectDefinitions, getAllPointsFromChart(chartData), settings.skipOutOfSignAspects);
45
- // For single chart, p1ChartName and p2ChartName are not needed for aspect string generation
46
- outputLines.push(...(0, aspects_2.generateAspectsOutput)('[ASPECTS]', aspects, settings));
47
- // Detect and display aspect patterns (if enabled)
18
+ const { chart, groupedAspects, patterns, stelliums, signDistributions, dispositors, placements, } = analysis;
19
+ outputLines.push(...(0, chartHeader_1.generateChartHeaderOutput)(chart.name, chartTitlePrefix));
20
+ outputLines.push(...(0, birthdata_1.generateBirthdataOutput)(chart.location, chart.timestamp, settings));
21
+ outputLines.push(...(0, angles_1.generateAnglesOutput)(chart.ascendant, chart.midheaven));
22
+ outputLines.push(...(0, houses_1.generateHousesOutput)(chart.houseCusps));
23
+ outputLines.push(...(0, planets_1.generatePlanetsOutput)(placements.planets));
24
+ if (settings.includeDispositors) {
25
+ outputLines.push(...(0, dispositors_1.generateDispositorsOutput)(dispositors));
26
+ }
27
+ if (settings.includeSignDistributions) {
28
+ outputLines.push(...(0, signDistributions_1.generateElementDistributionOutput)(signDistributions.elements));
29
+ outputLines.push(...(0, signDistributions_1.generateModalityDistributionOutput)(signDistributions.modalities));
30
+ outputLines.push(...(0, signDistributions_1.generatePolarityOutput)(signDistributions.polarities));
31
+ }
32
+ outputLines.push(...(0, aspects_1.generateAspectsOutput)('[ASPECTS]', groupedAspects));
48
33
  if (settings.includeAspectPatterns) {
49
- const aspectPatterns = (0, aspectPatterns_1.detectAspectPatterns)(chartData.planets, chartData.houseCusps);
50
- outputLines.push(...(0, aspectPatterns_2.generateAspectPatternsOutput)(aspectPatterns));
34
+ outputLines.push(...(0, aspectPatterns_1.generateAspectPatternsOutput)(patterns, undefined, false));
35
+ if (stelliums.length > 0) {
36
+ stelliums.forEach((stellium) => {
37
+ outputLines.push(...(0, stelliums_1.formatStellium)(stellium));
38
+ });
39
+ }
40
+ else {
41
+ outputLines.push('No Stelliums detected.');
42
+ }
51
43
  }
52
44
  outputLines.push('');
53
45
  return outputLines;
54
46
  };
55
- const processChartPairOutput = (settings, chart1, chart2) => {
47
+ const processChartPairOutput = (analysis, settings) => {
56
48
  const outputLines = [];
49
+ const { chart1, chart2, groupedSynastryAspects, compositePatterns, houseOverlays, } = analysis;
57
50
  const header = chart1.chartType === 'event' && chart2.chartType === 'event'
58
51
  ? 'EVENT_RELATIONSHIP'
59
52
  : chart1.chartType === 'event' || chart2.chartType === 'event'
60
53
  ? 'NATAL_EVENT'
61
54
  : 'SYNASTRY';
62
55
  outputLines.push(...(0, chartHeader_1.generateChartHeaderOutput)(`${chart1.name}-${chart2.name}`, header));
63
- const synastryAspects = (0, aspects_1.calculateMultichartAspects)(settings.aspectDefinitions, getAllPointsFromChart(chart1), getAllPointsFromChart(chart2), settings.skipOutOfSignAspects);
64
- outputLines.push(...(0, aspects_2.generateAspectsOutput)('[PLANET-PLANET ASPECTS]', synastryAspects, settings, chart1.name, chart2.name));
65
- outputLines.push('');
66
- outputLines.push(...(0, houseOverlays_1.generateHouseOverlaysOutput)(chart1, chart2, settings));
56
+ outputLines.push(...(0, aspects_1.generateAspectsOutput)('[PLANET-PLANET ASPECTS]', groupedSynastryAspects, chart1.name, chart2.name));
57
+ if (settings.includeAspectPatterns && compositePatterns.length > 0) {
58
+ outputLines.push(...(0, aspectPatterns_1.generateAspectPatternsOutput)(compositePatterns, `${chart1.name}-${chart2.name} Composite`, true));
59
+ }
67
60
  outputLines.push('');
61
+ if (settings.includeHouseOverlays) {
62
+ outputLines.push(...(0, houseOverlays_1.generateHouseOverlaysOutput)(houseOverlays, chart1.name, chart2.name));
63
+ outputLines.push('');
64
+ }
68
65
  return outputLines;
69
66
  };
70
- const processTransitChartInfoOutput = (settings, transitData) => {
67
+ const processGlobalPatternsOutput = (analysis, isTransit = false) => {
71
68
  const outputLines = [];
72
- outputLines.push(...(0, chartHeader_1.generateChartHeaderOutput)(transitData.name, 'TRANSIT'));
73
- outputLines.push(...(0, birthdata_1.generateBirthdataOutput)(transitData.location, transitData.timestamp, settings, '[DATETIME]'));
74
- // For transit chart's own planets, houses are usually not shown unless it's a full natal chart for that moment.
75
- outputLines.push(...(0, planets_1.generatePlanetsOutput)(transitData.planets, transitData.houseCusps, settings));
76
- outputLines.push('');
77
- return outputLines;
78
- };
79
- const determineChartType = (data) => {
80
- let baseChartString = 'natal';
81
- let suffixString = '';
82
- const natalCharts = data.filter(({ chartType }) => chartType !== 'transit' && chartType !== 'event');
83
- const eventCharts = data.filter(({ chartType }) => chartType === 'event');
84
- const transitCharts = data.filter(({ chartType }) => chartType === 'transit');
85
- if (transitCharts.length > 1) {
86
- throw new Error('Must provide at most one transit chart');
87
- }
88
- const hasTransit = transitCharts.length > 0;
89
- // first determine suffix
90
- if (natalCharts.length > 0) {
91
- if (eventCharts.length === 0) {
92
- if (hasTransit) {
93
- suffixString = '_with_transit';
94
- }
95
- }
96
- else if (eventCharts.length === 1) {
97
- suffixString = hasTransit ? '_with_event_and_transit' : '_with_event';
98
- }
99
- else {
100
- suffixString = hasTransit ? '_with_events_and_transit' : '_with_events';
101
- }
102
- }
103
- else {
104
- // base event charts can have transits
105
- if (hasTransit) {
106
- suffixString = '_with_transit';
107
- }
108
- }
109
- // then determine base string
110
- if (natalCharts.length === 0) {
111
- if (eventCharts.length === 0) {
112
- throw new Error('Must provide at least one non-transit chart');
69
+ const { charts, patterns } = analysis;
70
+ if (patterns.length > 0) {
71
+ const chartNames = charts.map((c) => c.name).join('-');
72
+ let title = `${chartNames} Global Composite`;
73
+ if (isTransit) {
74
+ title = `${chartNames} Global Transit Composite`;
113
75
  }
114
- else if (eventCharts.length === 1) {
115
- baseChartString = 'event';
116
- }
117
- else {
118
- baseChartString = 'multi_event';
119
- }
120
- }
121
- else if (natalCharts.length === 1) {
122
- baseChartString = 'natal';
123
- }
124
- else if (natalCharts.length === 2) {
125
- baseChartString = 'synastry';
76
+ outputLines.push(...(0, aspectPatterns_1.generateAspectPatternsOutput)(patterns, title, true));
77
+ outputLines.push('');
126
78
  }
127
- else {
128
- baseChartString = 'group_synastry';
129
- }
130
- return baseChartString + suffixString;
79
+ return outputLines;
131
80
  };
132
81
  /**
133
- * Orchestrates the generation of a complete astrological chart report in text format.
134
- * @param data The chart data, can be for a single chart or multiple charts (synastry, transits).
135
- * @param partialSettings Optional: Custom settings to override defaults.
82
+ * Formats a pre-computed and pre-grouped AstrologicalReport into a human-readable text string.
83
+ * @param report The AstrologicalReport object, with aspects already grouped.
136
84
  * @returns A string representing the full chart report.
137
85
  */
138
- function formatChartToText(data, partialSettings = {}) {
139
- // Validate input data
140
- const validationError = (0, validation_1.validateInputData)(data);
141
- if (validationError) {
142
- throw new Error(`Invalid chart data: ${validationError}`);
143
- }
144
- const settings = new ChartSettings_1.ChartSettings(partialSettings);
145
- const houseSystemName = settings.houseSystemName;
86
+ function formatReportToText(report) {
87
+ const { chartAnalyses, pairwiseAnalyses, globalAnalysis, transitAnalyses, globalTransitAnalysis, } = report;
88
+ const settings = report.settings;
146
89
  const outputLines = [];
147
- if (!(0, types_1.isMultiChartData)(data)) {
148
- // single chart or event, legacy usage
149
- if (data.chartType === 'transit') {
150
- throw new Error('Single chart data must not be transit.');
151
- }
152
- outputLines.push(...(0, metadata_1.generateMetadataOutput)(settings, data.chartType || 'natal', houseSystemName));
153
- outputLines.push(''); // Blank line after metadata
154
- outputLines.push(...processSingleChartOutput(settings, data));
155
- return outputLines.join('\n').trimEnd();
90
+ const originalCharts = chartAnalyses.map((ca) => ca.chart);
91
+ const chartType = (0, metadata_1.determineChartType)(originalCharts);
92
+ outputLines.push(...(0, metadata_1.generateMetadataOutput)(settings, chartType, settings.houseSystemName));
93
+ outputLines.push('');
94
+ // 1. Process individual non-transit charts
95
+ const nonTransitAnalyses = chartAnalyses.filter((a) => a.chart.chartType !== 'transit');
96
+ for (const analysis of nonTransitAnalyses) {
97
+ outputLines.push(...processSingleChartOutput(analysis, settings));
156
98
  }
157
- // multi-chart analysis proceeds from here on
158
- // generate metadata
159
- const chartType = determineChartType(data);
160
- outputLines.push(...(0, metadata_1.generateMetadataOutput)(settings, chartType, houseSystemName));
161
- outputLines.push(''); // Blank line after metadata
162
- const nonTransitCharts = data.filter(({ chartType }) => chartType !== 'transit');
163
- const transitChart = data.find(({ chartType }) => chartType === 'transit');
164
- // first, process each chart individually
165
- for (const chart of nonTransitCharts) {
166
- outputLines.push(...processSingleChartOutput(settings, chart));
99
+ // 2. Process pairwise analyses
100
+ for (const analysis of pairwiseAnalyses) {
101
+ outputLines.push(...processChartPairOutput(analysis, settings));
167
102
  }
168
- // then, process each pairwise chart
169
- for (let i = 0; i < nonTransitCharts.length; i++) {
170
- for (let j = i + 1; j < nonTransitCharts.length; j++) {
171
- outputLines.push(...processChartPairOutput(settings, nonTransitCharts[i], nonTransitCharts[j]));
172
- }
103
+ // 3. Process global patterns (non-transit)
104
+ if (globalAnalysis) {
105
+ outputLines.push(...processGlobalPatternsOutput(globalAnalysis, false));
173
106
  }
174
- // finally, process transit against each non-transit chart
175
- if (transitChart) {
176
- outputLines.push(...processTransitChartInfoOutput(settings, transitChart));
177
- for (const chart of nonTransitCharts) {
178
- // Transit Aspects to Chart 1
179
- const transitAspectsC1 = (0, aspects_1.calculateMultichartAspects)(settings.aspectDefinitions, getAllPointsFromChart(chart), getAllPointsFromChart(transitChart), settings.skipOutOfSignAspects);
180
- outputLines.push(...(0, aspects_2.generateAspectsOutput)(`[TRANSIT ASPECTS: ${chart.name}]`, transitAspectsC1, settings, chart.name, transitChart.name, true));
107
+ // 4. Process transit analyses
108
+ if (transitAnalyses.length > 0) {
109
+ const transitChartAnalysis = chartAnalyses.find((a) => a.chart.chartType === 'transit');
110
+ if (transitChartAnalysis) {
111
+ outputLines.push(...(0, chartHeader_1.generateChartHeaderOutput)(transitChartAnalysis.chart.name, 'TRANSIT'));
112
+ outputLines.push(...(0, birthdata_1.generateBirthdataOutput)(transitChartAnalysis.chart.location, transitChartAnalysis.chart.timestamp, settings, '[DATETIME]'));
113
+ outputLines.push(...(0, planets_1.generatePlanetsOutput)(transitChartAnalysis.placements.planets));
181
114
  outputLines.push('');
182
115
  }
116
+ for (const analysis of transitAnalyses) {
117
+ outputLines.push(...(0, aspects_1.generateAspectsOutput)(`[TRANSIT ASPECTS: ${analysis.natalChart.name}]`, analysis.groupedAspects, analysis.natalChart.name, analysis.transitChart.name, true));
118
+ if (settings.includeAspectPatterns) {
119
+ outputLines.push(...(0, aspectPatterns_1.generateAspectPatternsOutput)(analysis.patterns, `Transit to ${analysis.natalChart.name}`, true));
120
+ }
121
+ outputLines.push('');
122
+ }
123
+ }
124
+ // 5. Process global transit patterns
125
+ if (globalTransitAnalysis) {
126
+ outputLines.push(...processGlobalPatternsOutput(globalTransitAnalysis, true));
183
127
  }
184
128
  return outputLines.join('\n').trimEnd();
185
129
  }