chart2txt 0.1.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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +39 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +161 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.js +2 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# chart2txt
|
|
2
|
+
|
|
3
|
+
A TypeScript library that converts astrological chart data to human-readable text.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Convert planet positions to text descriptions of their zodiac signs
|
|
8
|
+
- Calculate and describe house placements when an ascendant is provided
|
|
9
|
+
- Calculate and describe aspects between planets
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install chart2txt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { chart2txt } from 'chart2txt';
|
|
21
|
+
|
|
22
|
+
const chartData = {
|
|
23
|
+
planets: [
|
|
24
|
+
{ name: 'Sun', longitude: 35 }, // 5° Taurus
|
|
25
|
+
{ name: 'Moon', longitude: 120 }, // 0° Leo
|
|
26
|
+
{ name: 'Mercury', longitude: 75 } // 15° Gemini
|
|
27
|
+
],
|
|
28
|
+
ascendant: 0 // 0° Aries
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const settings = {
|
|
32
|
+
// modified settings go here
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const textDescription = chart2txt(chartData, settings);
|
|
36
|
+
console.log(textDescription);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Settings
|
|
40
|
+
|
|
41
|
+
The conversion to text is configurable through the settings object. Any number of provided settings can be specified, and will overwrite the defaults when provided. Explanations of each setting can be found in the [types.ts](src/types.ts) file, and the default settings can be found in the [constants.ts](src/constants.ts) file. See the [tests file](tests/index.test.ts) for example uses of each setting.
|
|
42
|
+
|
|
43
|
+
**NOTE**: Overriding aspects must be done by replacing the entire object, i.e. by modifying the following data object to suit your needs:
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
[
|
|
47
|
+
{ name: 'conjunction', angle: 0, orb: 5 },
|
|
48
|
+
{ name: 'opposition', angle: 180, orb: 5 },
|
|
49
|
+
{ name: 'trine', angle: 120, orb: 5 },
|
|
50
|
+
{ name: 'square', angle: 90, orb: 5 },
|
|
51
|
+
{ name: 'sextile', angle: 60, orb: 3 },
|
|
52
|
+
]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Install dependencies
|
|
59
|
+
npm install
|
|
60
|
+
|
|
61
|
+
# Run tests
|
|
62
|
+
npm test
|
|
63
|
+
|
|
64
|
+
# Build the library
|
|
65
|
+
npm run build
|
|
66
|
+
|
|
67
|
+
# Lint code
|
|
68
|
+
npm run lint
|
|
69
|
+
|
|
70
|
+
# Format code
|
|
71
|
+
npm run format
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_SETTINGS = exports.DEFAULT_ASPECTS = exports.ZODIAC_SIGNS = void 0;
|
|
4
|
+
exports.ZODIAC_SIGNS = [
|
|
5
|
+
'Aries',
|
|
6
|
+
'Taurus',
|
|
7
|
+
'Gemini',
|
|
8
|
+
'Cancer',
|
|
9
|
+
'Leo',
|
|
10
|
+
'Virgo',
|
|
11
|
+
'Libra',
|
|
12
|
+
'Scorpio',
|
|
13
|
+
'Sagittarius',
|
|
14
|
+
'Capricorn',
|
|
15
|
+
'Aquarius',
|
|
16
|
+
'Pisces',
|
|
17
|
+
];
|
|
18
|
+
exports.DEFAULT_ASPECTS = [
|
|
19
|
+
{ name: 'conjunction', angle: 0, orb: 5 },
|
|
20
|
+
{ name: 'opposition', angle: 180, orb: 5 },
|
|
21
|
+
{ name: 'trine', angle: 120, orb: 5 },
|
|
22
|
+
{ name: 'square', angle: 90, orb: 5 },
|
|
23
|
+
{ name: 'sextile', angle: 60, orb: 3 },
|
|
24
|
+
];
|
|
25
|
+
exports.DEFAULT_SETTINGS = {
|
|
26
|
+
// sign settings
|
|
27
|
+
includeSignDegree: true,
|
|
28
|
+
omitSigns: false,
|
|
29
|
+
// house settings
|
|
30
|
+
houseSystem: 'equal',
|
|
31
|
+
includeHouseDegree: false,
|
|
32
|
+
omitHouses: false,
|
|
33
|
+
// point settings
|
|
34
|
+
includeAscendant: true,
|
|
35
|
+
omitPoints: false,
|
|
36
|
+
// orb + aspect settings
|
|
37
|
+
aspectDefinitions: exports.DEFAULT_ASPECTS,
|
|
38
|
+
omitAspects: false,
|
|
39
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chart2txt
|
|
3
|
+
* A library to convert astrological chart data to human-readable text
|
|
4
|
+
*/
|
|
5
|
+
import type { Point, ChartData, Settings } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Main function to convert chart data to text
|
|
8
|
+
*/
|
|
9
|
+
export declare function chart2txt(data: ChartData, settings?: Partial<Settings>): string;
|
|
10
|
+
export { ChartData, Point, Settings };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* chart2txt
|
|
4
|
+
* A library to convert astrological chart data to human-readable text
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.chart2txt = chart2txt;
|
|
8
|
+
const constants_1 = require("./constants");
|
|
9
|
+
/**
|
|
10
|
+
* Determines the zodiac sign for a given longitude
|
|
11
|
+
*/
|
|
12
|
+
function getLongitudeSign(longitude) {
|
|
13
|
+
const signIndex = Math.floor(longitude / 30) % 12;
|
|
14
|
+
return constants_1.ZODIAC_SIGNS[signIndex];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Calculates the house for a given longitude, based on the ascendant
|
|
18
|
+
*/
|
|
19
|
+
function getHousePosition(houseSystem, longitude, ascendant) {
|
|
20
|
+
switch (houseSystem) {
|
|
21
|
+
case 'equal': {
|
|
22
|
+
// House 1 starts at the ascendant
|
|
23
|
+
const housePosition = (longitude - ascendant + 360) % 360;
|
|
24
|
+
const house = Math.floor(housePosition / 30) + 1;
|
|
25
|
+
const degree = housePosition % 30;
|
|
26
|
+
return { house, degree };
|
|
27
|
+
}
|
|
28
|
+
case 'whole_sign': {
|
|
29
|
+
// House 1 starts at beginning of ascendant sign
|
|
30
|
+
const house1SignCusp = (Math.floor(ascendant / 30) % 12) * 30;
|
|
31
|
+
// Computation proceeds same as equal, using sign cusp
|
|
32
|
+
const housePosition = (longitude - house1SignCusp + 360) % 360;
|
|
33
|
+
const house = Math.floor(housePosition / 30) + 1;
|
|
34
|
+
const degree = housePosition % 30;
|
|
35
|
+
return { house, degree };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Identifies aspects between planets
|
|
41
|
+
*/
|
|
42
|
+
function calculateAspects(aspectDefinitions, planets) {
|
|
43
|
+
const aspects = [];
|
|
44
|
+
// Compare each planet with every other planet
|
|
45
|
+
for (let i = 0; i < planets.length; i++) {
|
|
46
|
+
for (let j = i + 1; j < planets.length; j++) {
|
|
47
|
+
const planetA = planets[i];
|
|
48
|
+
const planetB = planets[j];
|
|
49
|
+
// Calculate the angular difference
|
|
50
|
+
let diff = Math.abs(planetA.longitude - planetB.longitude);
|
|
51
|
+
if (diff > 180)
|
|
52
|
+
diff = 360 - diff;
|
|
53
|
+
// Check against each aspect type
|
|
54
|
+
for (const aspectType of aspectDefinitions) {
|
|
55
|
+
const orb = Math.abs(diff - aspectType.angle);
|
|
56
|
+
if (orb <= aspectType.orb) {
|
|
57
|
+
aspects.push({
|
|
58
|
+
planetA: planetA.name,
|
|
59
|
+
planetB: planetB.name,
|
|
60
|
+
aspectType: aspectType.name,
|
|
61
|
+
orb,
|
|
62
|
+
});
|
|
63
|
+
break; // Only record the strongest aspect between two planets
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return aspects;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Formats planet sign positions as text
|
|
72
|
+
*/
|
|
73
|
+
function formatPlanetSigns(planets, ascendant, points = [], includeDegree = constants_1.DEFAULT_SETTINGS.includeSignDegree) {
|
|
74
|
+
const ascPoint = ascendant
|
|
75
|
+
? [{ name: 'Ascendant', longitude: ascendant }]
|
|
76
|
+
: [];
|
|
77
|
+
const output = [...ascPoint, ...planets, ...points]
|
|
78
|
+
.map((planet) => {
|
|
79
|
+
const sign = getLongitudeSign(planet.longitude);
|
|
80
|
+
if (includeDegree) {
|
|
81
|
+
const degree = Math.floor(planet.longitude % 30);
|
|
82
|
+
return `${planet.name} is at ${degree}° ${sign}`;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return `${planet.name} is in ${sign}`;
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
.join('. ');
|
|
89
|
+
return output ? `${output}.` : '';
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Formats planet house positions as text
|
|
93
|
+
*/
|
|
94
|
+
function formatPlanetHouses(houseSystem, ascendant, planets, points = [], includeDegree = constants_1.DEFAULT_SETTINGS.includeHouseDegree) {
|
|
95
|
+
// TODO: house systems
|
|
96
|
+
const output = [...planets, ...points]
|
|
97
|
+
.map((planet) => {
|
|
98
|
+
const houseData = getHousePosition(houseSystem, planet.longitude, ascendant);
|
|
99
|
+
if (includeDegree) {
|
|
100
|
+
return `${planet.name} is at ${houseData.degree}° in house ${houseData.house}`;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
return `${planet.name} is in house ${houseData.house}`;
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
.join('. ');
|
|
107
|
+
return output ? `${output}.` : '';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Formats aspects between planets as text
|
|
111
|
+
*/
|
|
112
|
+
function formatAspects(aspects) {
|
|
113
|
+
const output = aspects
|
|
114
|
+
.map((aspect) => {
|
|
115
|
+
return `${aspect.planetA} is in ${aspect.aspectType} with ${aspect.planetB} (orb: ${aspect.orb.toFixed(1)}°)`;
|
|
116
|
+
})
|
|
117
|
+
.join('. ');
|
|
118
|
+
return output ? `${output}.` : '';
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Formats provided location and time, if present, as text
|
|
122
|
+
*/
|
|
123
|
+
function formatLocationAndDate(location, timestamp) {
|
|
124
|
+
const locationString = location ? `location: ${location}` : '';
|
|
125
|
+
const timestampString = timestamp ? `at: ${timestamp.toISOString()}` : '';
|
|
126
|
+
return [locationString, timestampString].filter((s) => s !== '').join(', ');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Main function to convert chart data to text
|
|
130
|
+
*/
|
|
131
|
+
function chart2txt(data, settings = {}) {
|
|
132
|
+
// override default settings with any provided settings data
|
|
133
|
+
const fullSettings = Object.assign({}, constants_1.DEFAULT_SETTINGS, settings);
|
|
134
|
+
// format header
|
|
135
|
+
let result = 'Astrology Chart';
|
|
136
|
+
const locationAndDate = formatLocationAndDate(data.location, data.timestamp);
|
|
137
|
+
if (locationAndDate) {
|
|
138
|
+
result += ` (${locationAndDate})`;
|
|
139
|
+
}
|
|
140
|
+
result += ':\n\n';
|
|
141
|
+
// format planets
|
|
142
|
+
if (!fullSettings.omitSigns) {
|
|
143
|
+
result += formatPlanetSigns(data.planets, fullSettings.includeAscendant && data.ascendant
|
|
144
|
+
? data.ascendant
|
|
145
|
+
: undefined, fullSettings.omitPoints ? [] : data.points, fullSettings.includeSignDegree);
|
|
146
|
+
}
|
|
147
|
+
// format houses
|
|
148
|
+
if (!fullSettings.omitHouses && data.ascendant !== undefined) {
|
|
149
|
+
result +=
|
|
150
|
+
'\n\n' +
|
|
151
|
+
formatPlanetHouses(fullSettings.houseSystem, data.ascendant, data.planets, fullSettings.omitPoints ? [] : data.points, fullSettings.includeHouseDegree);
|
|
152
|
+
}
|
|
153
|
+
// format aspects
|
|
154
|
+
if (!fullSettings.omitAspects) {
|
|
155
|
+
const aspects = calculateAspects(fullSettings.aspectDefinitions, data.planets);
|
|
156
|
+
if (aspects.length > 0) {
|
|
157
|
+
result += '\n\n' + formatAspects(aspects);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface Point {
|
|
2
|
+
name: string;
|
|
3
|
+
longitude: number;
|
|
4
|
+
}
|
|
5
|
+
export interface ChartData {
|
|
6
|
+
planets: Point[];
|
|
7
|
+
ascendant?: number;
|
|
8
|
+
points?: Point[];
|
|
9
|
+
timestamp?: Date;
|
|
10
|
+
location?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Aspect {
|
|
13
|
+
name: string;
|
|
14
|
+
angle: number;
|
|
15
|
+
orb: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AspectData {
|
|
18
|
+
planetA: string;
|
|
19
|
+
planetB: string;
|
|
20
|
+
aspectType: string;
|
|
21
|
+
orb: number;
|
|
22
|
+
}
|
|
23
|
+
export type HouseSystem = 'whole_sign' | 'equal';
|
|
24
|
+
export interface Settings {
|
|
25
|
+
includeSignDegree: boolean;
|
|
26
|
+
omitSigns: boolean;
|
|
27
|
+
includeAscendant: boolean;
|
|
28
|
+
omitPoints: boolean;
|
|
29
|
+
includeHouseDegree: boolean;
|
|
30
|
+
houseSystem: HouseSystem;
|
|
31
|
+
omitHouses: boolean;
|
|
32
|
+
aspectDefinitions: Aspect[];
|
|
33
|
+
omitAspects: boolean;
|
|
34
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chart2txt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert astrological chart data to human-readable text",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"lint": "eslint src/**/*.ts",
|
|
11
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\""
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"astrology",
|
|
15
|
+
"chart",
|
|
16
|
+
"text"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/simpolism/chart2txt",
|
|
19
|
+
"author": "simpolism <simpolism@gmail.com>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/jest": "^29.5.0",
|
|
23
|
+
"@types/node": "^18.0.0",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
25
|
+
"@typescript-eslint/parser": "^5.0.0",
|
|
26
|
+
"eslint": "^8.0.0",
|
|
27
|
+
"eslint-config-prettier": "^8.0.0",
|
|
28
|
+
"jest": "^29.0.0",
|
|
29
|
+
"prettier": "^2.0.0",
|
|
30
|
+
"ts-jest": "^29.1.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|