tonus 0.1.8 → 0.5.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/BIBLIOGRAPHY.md +132 -108
- package/CHANGELOG.md +598 -1
- package/LICENSE +133 -29
- package/README.md +106 -83
- package/dist/data/am.js +2666 -11196
- package/dist/data/ams.d.ts +5 -0
- package/dist/data/ams.js +122 -0
- package/dist/data/attestation.d.ts +19 -0
- package/dist/data/attestation.js +15716 -0
- package/dist/data/attestation.json +15711 -0
- package/dist/data/cadentiae.d.ts +43 -0
- package/dist/data/cadentiae.js +174 -0
- package/dist/data/cal.js +48 -0
- package/dist/data/census.d.ts +12 -0
- package/dist/data/census.js +36 -0
- package/dist/data/commune-office.d.ts +4 -0
- package/dist/data/commune-office.js +2371 -0
- package/dist/data/commune-office.json +2365 -0
- package/dist/data/commune.js +181 -11
- package/dist/data/corpus-overlap.d.ts +17 -0
- package/dist/data/corpus-overlap.js +299 -5
- package/dist/data/cot.d.ts +5 -0
- package/dist/data/cot.js +172 -0
- package/dist/data/cse.d.ts +5 -0
- package/dist/data/cse.js +122 -0
- package/dist/data/gabc-glyphs.d.ts +45 -0
- package/dist/data/gabc-glyphs.js +122 -0
- package/dist/data/gr.js +754 -6394
- package/dist/data/kyriale.js +116 -116
- package/dist/data/la.js +799 -13419
- package/dist/data/lh.js +113 -3473
- package/dist/data/lu.js +931 -17631
- package/dist/data/nocturnale-romanum.js +1659 -10411
- package/dist/data/office-ferial.d.ts +4 -0
- package/dist/data/office-ferial.js +396 -0
- package/dist/data/office-ferial.json +391 -0
- package/dist/data/office-monastic.d.ts +17 -1
- package/dist/data/office-monastic.js +1403 -466
- package/dist/data/office-psalms-monastic.d.ts +13 -1
- package/dist/data/office-psalms-monastic.js +9 -0
- package/dist/data/propers.js +1 -1
- package/dist/data/psalms.js +22919 -5
- package/dist/data/psm.d.ts +5 -0
- package/dist/data/psm.js +122 -0
- package/dist/data/seasonal-respbreve.d.ts +5 -0
- package/dist/data/seasonal-respbreve.js +41 -0
- package/dist/data/seasonal-respbreve.json +35 -0
- package/dist/data/smufl-glyphs.d.ts +17 -0
- package/dist/data/smufl-glyphs.js +1546 -0
- package/dist/data/smufl-glyphs.json +1530 -0
- package/dist/engines/cal/calendar.d.ts +3 -2
- package/dist/engines/cal/calendar.js +105 -29
- package/dist/engines/cal/data/eras.d.ts +35 -0
- package/dist/engines/cal/data/eras.js +128 -0
- package/dist/engines/cal/date.js +44 -0
- package/dist/engines/cal/types.d.ts +15 -3
- package/dist/engines/cal/types.js +5 -5
- package/dist/engines/census/census.d.ts +7 -0
- package/dist/engines/census/census.js +179 -0
- package/dist/engines/census/types.d.ts +55 -0
- package/dist/engines/census/types.js +8 -0
- package/dist/engines/chant/attest.d.ts +39 -0
- package/dist/engines/chant/attest.js +90 -0
- package/dist/engines/chant/chant.d.ts +16 -4
- package/dist/engines/chant/chant.js +220 -30
- package/dist/engines/chant/data/compline.js +2 -1
- package/dist/engines/chant/data/masses.d.ts +56 -4
- package/dist/engines/chant/data/masses.js +305 -80
- package/dist/engines/chant/data/prime.js +1 -1
- package/dist/engines/chant/hour.js +279 -58
- package/dist/engines/chant/ordinary.d.ts +2 -0
- package/dist/engines/chant/ordinary.js +336 -56
- package/dist/engines/chant/propers.js +55 -5
- package/dist/engines/chant/psalm.d.ts +4 -4
- package/dist/engines/chant/psalm.js +25 -11
- package/dist/engines/chant/syllabify.d.ts +1 -0
- package/dist/engines/chant/syllabify.js +90 -17
- package/dist/engines/chant/types.d.ts +115 -12
- package/dist/engines/chant/types.js +38 -3
- package/dist/engines/harmonia/api.js +4 -0
- package/dist/engines/harmonia/data/doctrines.js +3 -1
- package/dist/engines/harmonia/tabula.d.ts +3 -0
- package/dist/engines/harmonia/tabula.js +1 -0
- package/dist/engines/harmonia/voice.d.ts +4 -0
- package/dist/engines/harmonia/voice.js +8 -4
- package/dist/engines/imprint.js +14 -1
- package/dist/engines/planet/orbital.js +4 -4
- package/dist/engines/planet/planet.d.ts +10 -0
- package/dist/engines/planet/planet.js +30 -3
- package/dist/engines/planet/position.js +13 -10
- package/dist/engines/planet/types.d.ts +1 -0
- package/dist/engines/score/api.d.ts +2 -13
- package/dist/engines/score/api.js +21 -8
- package/dist/engines/score/articulation.js +2 -2
- package/dist/engines/score/cadence.d.ts +76 -0
- package/dist/engines/score/cadence.js +96 -0
- package/dist/engines/score/emitters/accidentals.d.ts +21 -0
- package/dist/engines/score/emitters/accidentals.js +88 -0
- package/dist/engines/score/emitters/atramentum.d.ts +107 -0
- package/dist/engines/score/emitters/atramentum.js +239 -0
- package/dist/engines/score/emitters/breaking.d.ts +62 -0
- package/dist/engines/score/emitters/breaking.js +80 -0
- package/dist/engines/score/emitters/moderna.d.ts +38 -0
- package/dist/engines/score/emitters/moderna.js +612 -0
- package/dist/engines/score/emitters/svg.d.ts +143 -0
- package/dist/engines/score/emitters/svg.js +1328 -0
- package/dist/engines/score/emitters/tracks.d.ts +104 -0
- package/dist/engines/score/emitters/tracks.js +728 -0
- package/dist/engines/score/infer.d.ts +3 -3
- package/dist/engines/score/infer.js +2 -2
- package/dist/engines/score/inscriptio.d.ts +69 -0
- package/dist/engines/score/inscriptio.js +138 -0
- package/dist/engines/score/ir.d.ts +2 -2
- package/dist/engines/score/ir.js +59 -12
- package/dist/engines/score/lyric.d.ts +23 -0
- package/dist/engines/score/lyric.js +234 -0
- package/dist/engines/score/meta.d.ts +2 -2
- package/dist/engines/score/modulation.d.ts +12 -0
- package/dist/engines/score/modulation.js +49 -0
- package/dist/engines/score/neume.js +35 -4
- package/dist/engines/score/parse.js +150 -9
- package/dist/engines/score/phrasing.js +4 -3
- package/dist/engines/score/prosody.d.ts +38 -0
- package/dist/engines/score/prosody.js +70 -6
- package/dist/engines/score/tabula.d.ts +37 -5
- package/dist/engines/score/tabula.js +18 -0
- package/dist/engines/score/types.d.ts +88 -1
- package/dist/engines/temper/api.d.ts +4 -1
- package/dist/engines/temper/api.js +28 -5
- package/dist/engines/temper/data/guido.js +6 -2
- package/dist/engines/temper/data/modes.d.ts +6 -0
- package/dist/engines/temper/data/modes.js +42 -0
- package/dist/engines/temper/data/tones.d.ts +1 -1
- package/dist/engines/temper/data/tones.js +20 -11
- package/dist/engines/temper/interval.js +4 -3
- package/dist/engines/temper/modality.d.ts +11 -2
- package/dist/engines/temper/modality.js +74 -2
- package/dist/engines/temper/modes.d.ts +1 -1
- package/dist/engines/temper/pitch.d.ts +1 -1
- package/dist/engines/temper/pitch.js +12 -2
- package/dist/engines/temper/scale.d.ts +53 -0
- package/dist/engines/temper/scale.js +107 -8
- package/dist/index.d.ts +26 -8
- package/dist/index.js +37 -4
- package/docs/api/calendar.md +279 -0
- package/docs/api/census.md +288 -0
- package/docs/api/chant.md +657 -0
- package/docs/api/heavens.md +346 -0
- package/docs/api/index.md +263 -0
- package/docs/api/score.md +873 -0
- package/docs/api/tuning.md +619 -0
- package/package.json +11 -5
- package/dist/data/office-matins-roman.d.ts +0 -19
- package/dist/data/office-matins-roman.js +0 -4383
- package/dist/data/office-psalms-roman.d.ts +0 -15
- package/dist/data/office-psalms-roman.js +0 -28
- package/dist/data/office-roman.d.ts +0 -19
- package/dist/data/office-roman.js +0 -13792
- package/dist/engines/chant/matutinum.d.ts +0 -33
- package/dist/engines/chant/matutinum.js +0 -81
- package/dist/engines/score/emitters/midi.d.ts +0 -65
- package/dist/engines/score/emitters/midi.js +0 -162
- package/dist/engines/score/emitters/musicxml.d.ts +0 -18
- package/dist/engines/score/emitters/musicxml.js +0 -166
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
// engines/chant/psalm — psalm and canticle retrieval as intoned Chant[]
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
import { PSALMS } from "../../data/psalms.js";
|
|
5
|
-
import {
|
|
6
|
-
import { OFFICE_PSALMS_MONASTIC } from "../../data/office-psalms-monastic.js";
|
|
5
|
+
import { OFFICE_PSALMS_MONASTIC, } from "../../data/office-psalms-monastic.js";
|
|
7
6
|
import { intone } from "./intone.js";
|
|
8
|
-
import {
|
|
7
|
+
import { MODI } from "./types.js";
|
|
8
|
+
// Canticle numbers follow the generated psalms.json numbering (verified against
|
|
9
|
+
// the incipits: 231 "Benedíctus Dóminus", 232 "Magníficat", 233 "Nunc dimíttis",
|
|
10
|
+
// 210 "Benedícite, ómnia ópera"). The Te Deum is not psalmody — it carries its
|
|
11
|
+
// own melody, has no rows in the psalter data, and is not addressable here.
|
|
9
12
|
const CANTICLE_NAMES = {
|
|
10
13
|
benedictus: 231,
|
|
11
|
-
magnificat:
|
|
12
|
-
"nunc dimittis":
|
|
13
|
-
"te deum": 240,
|
|
14
|
+
magnificat: 232,
|
|
15
|
+
"nunc dimittis": 233,
|
|
14
16
|
benedicite: 210,
|
|
15
17
|
};
|
|
16
18
|
function lookupVerses(psalm, verse) {
|
|
@@ -37,15 +39,20 @@ function verseToChant(v, mode, differentia, intonation, inDirectum, solemn) {
|
|
|
37
39
|
office: "ps",
|
|
38
40
|
genus: "Psalmus",
|
|
39
41
|
mode: String(mode),
|
|
40
|
-
modus: mode === 0 ? "Tonus Peregrinus" : (
|
|
42
|
+
modus: mode === 0 ? "Tonus Peregrinus" : (MODI[String(mode)] ?? `Modus ${mode}`),
|
|
41
43
|
pages: [],
|
|
42
44
|
source: {
|
|
43
45
|
book: "Psalterium",
|
|
44
|
-
|
|
46
|
+
// Deterministic: intoned psalmody is computed, not dated — the library's
|
|
47
|
+
// determinism doctrine forbids a wall-clock stamp here.
|
|
48
|
+
year: null,
|
|
45
49
|
editor: "tonus",
|
|
46
50
|
},
|
|
47
51
|
};
|
|
48
52
|
}
|
|
53
|
+
const PSALMUS_QUERY_KEYS = new Set([
|
|
54
|
+
"psalm", "verse", "mode", "differentia", "intonatio", "inDirectum", "solemn",
|
|
55
|
+
]);
|
|
49
56
|
/**
|
|
50
57
|
* Psalm and canticle retrieval (`tonus.psalmus`) from the Psalterium,
|
|
51
58
|
* intoned to the psalm tones (modes 1-8 plus tonus peregrinus) as GABC.
|
|
@@ -53,6 +60,13 @@ function verseToChant(v, mode, differentia, intonation, inDirectum, solemn) {
|
|
|
53
60
|
export function getPsalm(query) {
|
|
54
61
|
if (!query || Object.keys(query).length === 0)
|
|
55
62
|
return [];
|
|
63
|
+
// The same door policy as cantus and officium: an unknown key throws, so a
|
|
64
|
+
// stale or misspelled option is learned immediately, not silently ignored.
|
|
65
|
+
const unknown = Object.keys(query).filter((k) => !PSALMUS_QUERY_KEYS.has(k));
|
|
66
|
+
if (unknown.length) {
|
|
67
|
+
throw new Error(`psalmus: unknown query key(s) ${unknown.map((k) => `"${k}"`).join(", ")} ` +
|
|
68
|
+
`(expected ${[...PSALMUS_QUERY_KEYS].join(", ")}).`);
|
|
69
|
+
}
|
|
56
70
|
const verses = lookupVerses(query.psalm ?? 0, query.verse);
|
|
57
71
|
if (!verses.length)
|
|
58
72
|
return [];
|
|
@@ -83,12 +97,12 @@ export function intonePortion(p, mode = 8) {
|
|
|
83
97
|
}
|
|
84
98
|
/**
|
|
85
99
|
* The little-hours psalmody for one hour on a given weekday (0 = Sunday), from
|
|
86
|
-
* the extracted DO
|
|
100
|
+
* the extracted DO monastic scheme (`office-psalms-monastic.ts`). Prefers the
|
|
87
101
|
* weekday-specific entry, then the ferial default (weekday null), then the
|
|
88
102
|
* feast set; returns the psalm portions (not yet intoned).
|
|
89
103
|
*/
|
|
90
|
-
export function officePsalmPortions(hour, weekday
|
|
91
|
-
const scheme =
|
|
104
|
+
export function officePsalmPortions(hour, weekday) {
|
|
105
|
+
const scheme = OFFICE_PSALMS_MONASTIC;
|
|
92
106
|
const forHour = scheme.filter((e) => e.hour === hour);
|
|
93
107
|
const exact = forHour.find((e) => e.weekday === weekday && !e.festis);
|
|
94
108
|
const ferial = forHour.find((e) => e.weekday === null && !e.festis);
|
|
@@ -15,6 +15,7 @@ export declare function hyphenateWord(word: string): string;
|
|
|
15
15
|
export declare function selectVowel(text: string): {
|
|
16
16
|
vowel: string;
|
|
17
17
|
accent: boolean;
|
|
18
|
+
diphthong: string | null;
|
|
18
19
|
};
|
|
19
20
|
export declare function detectVowelAccent(text: string): boolean;
|
|
20
21
|
//# sourceMappingURL=syllabify.d.ts.map
|
|
@@ -3,21 +3,30 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
//
|
|
5
5
|
// Rules applied (in order):
|
|
6
|
-
// 1.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
6
|
+
// 1. Diphthongs ae, oe, au → single vowel unit (never split). ei and ui are
|
|
7
|
+
// NOT diphthongs in ecclesiastical Latin (De-i, e-le-i-son, fu-it, su-i)
|
|
8
|
+
// — except the pronoun stems cui/hui (cui, huic), where ui is true.
|
|
9
|
+
// eu is NOT a diphthong either (de-us, me-us split normally).
|
|
10
|
+
// 2. qu → single consonant (u is silent after q); likewise the ngu glide
|
|
11
|
+
// (lin-gua, sán-guis) — but not gu elsewhere (e-xi-gu-us, ar-gu-it).
|
|
12
|
+
// 3. i between vowels, or word-initial before a vowel, is consonantal (the
|
|
13
|
+
// j sound): e-ius, ma-ior, al-le-lu-ia, Ie-su — it opens the next
|
|
14
|
+
// syllable rather than closing this one.
|
|
15
|
+
// 4. All other adjacent vowels split (ecclesiastical): fi-li-a, glo-ri-a
|
|
16
|
+
// 5. Single consonant between vowels → goes with following vowel (V·CV)
|
|
17
|
+
// 6. Consonant clusters: muta cum liquida (tr, pr, br, gr, dr, cr, fr, pl, bl,
|
|
18
|
+
// cl, gl, fl) and the Greek digraphs th, ph, ch stay with the following
|
|
19
|
+
// vowel; all other clusters split after the first consonant
|
|
13
20
|
//
|
|
14
21
|
// Diacritics (áéíóúàèìòùâêîôû etc.) are treated as their base vowel throughout.
|
|
15
22
|
// ── Character classes ──
|
|
16
23
|
const VOWELS = new Set("aeiouyáéíóúàèìòùâêîôûäëïöüæœý");
|
|
17
24
|
const SOFT_HYPHEN = "\u00ad";
|
|
18
|
-
// Latin diphthongs
|
|
19
|
-
//
|
|
20
|
-
|
|
25
|
+
// Latin diphthongs treated as single syllables. ei and ui are deliberately
|
|
26
|
+
// absent: in ecclesiastical Latin they are hiatus (De-i, fu-it, su-i) — ui is
|
|
27
|
+
// a true diphthong only in the pronoun stems cui/hui, handled below. eu is not
|
|
28
|
+
// a classical Latin diphthong (de-us, me-us split normally).
|
|
29
|
+
const DIPHTHONGS = new Set(["ae", "oe", "au"]);
|
|
21
30
|
// Muta cum liquida pairs that stay with the following vowel
|
|
22
31
|
const MUTA_CUM_LIQUIDA = new Set([
|
|
23
32
|
"tr",
|
|
@@ -58,16 +67,40 @@ function isConsonantBase(ch) {
|
|
|
58
67
|
* "ánima" → ["á", "ni", "ma"]
|
|
59
68
|
*/
|
|
60
69
|
export function syllabifyWord(word) {
|
|
61
|
-
if (word.length <=
|
|
70
|
+
if (word.length <= 1)
|
|
62
71
|
return [word];
|
|
63
72
|
const chars = Array.from(word);
|
|
64
73
|
const n = chars.length;
|
|
65
|
-
|
|
74
|
+
const lower = chars.map(baseChar).join("");
|
|
75
|
+
// A glide u carries no syllable: after q always (qui), and in the ngu
|
|
76
|
+
// cluster before a vowel (lin-gua, sán-guis) — but a u after plain g is a
|
|
77
|
+
// real vowel (e-xi-gu-us, ar-gu-it).
|
|
78
|
+
const isGlideU = (i) => {
|
|
79
|
+
if (baseChar(chars[i]) !== "u")
|
|
80
|
+
return false;
|
|
81
|
+
const prev = i > 0 ? baseChar(chars[i - 1]) : "";
|
|
82
|
+
if (prev === "q")
|
|
83
|
+
return true;
|
|
84
|
+
const prev2 = i > 1 ? baseChar(chars[i - 2]) : "";
|
|
85
|
+
return prev === "g" && prev2 === "n" && i + 1 < n && isVowelBase(chars[i + 1]);
|
|
86
|
+
};
|
|
87
|
+
// A consonantal i (the j sound) carries no syllable of its own — it opens
|
|
88
|
+
// the following vowel's: between real vowels (e-ius, ma-ior, al-le-lu-ia)
|
|
89
|
+
// or word-initial before a vowel (Ie-su, iu-bi-lá-te).
|
|
90
|
+
const isConsonantalI = (i) => {
|
|
91
|
+
if (baseChar(chars[i]) !== "i")
|
|
92
|
+
return false;
|
|
93
|
+
if (i + 1 >= n || !isVowelBase(chars[i + 1]))
|
|
94
|
+
return false;
|
|
95
|
+
if (i === 0)
|
|
96
|
+
return true;
|
|
97
|
+
return isVowelBase(chars[i - 1]) && !isGlideU(i - 1);
|
|
98
|
+
};
|
|
99
|
+
// Find all vowel positions (base-char aware; glides and consonantal i out).
|
|
66
100
|
const vpos = [];
|
|
67
101
|
for (let i = 0; i < n; i++) {
|
|
68
102
|
if (isVowelBase(chars[i])) {
|
|
69
|
-
|
|
70
|
-
if (baseChar(chars[i]) === "u" && prev === "q")
|
|
103
|
+
if (isGlideU(i) || isConsonantalI(i))
|
|
71
104
|
continue;
|
|
72
105
|
vpos.push(i);
|
|
73
106
|
}
|
|
@@ -84,6 +117,10 @@ export function syllabifyWord(word) {
|
|
|
84
117
|
const pair = baseChar(chars[v1]) + baseChar(chars[v2]);
|
|
85
118
|
if (DIPHTHONGS.has(pair))
|
|
86
119
|
continue;
|
|
120
|
+
// ui is a true diphthong only in the pronoun stems (cui, huic, and the
|
|
121
|
+
// cui- compounds); everywhere else it is hiatus (fu-it, su-i).
|
|
122
|
+
if (pair === "ui" && /^(cui|hui)/.test(lower))
|
|
123
|
+
continue;
|
|
87
124
|
// All other adjacent vowels split in ecclesiastical Latin
|
|
88
125
|
splits.add(v2);
|
|
89
126
|
continue;
|
|
@@ -170,21 +207,57 @@ export function selectVowel(text) {
|
|
|
170
207
|
const nfd = expanded.normalize("NFD");
|
|
171
208
|
let firstVowel = "";
|
|
172
209
|
let accentedVowel = "";
|
|
210
|
+
// Every vowel in order with the index it sat at, so the diphthong pass below
|
|
211
|
+
// can ask whether two of them are adjacent in the source.
|
|
212
|
+
const found = [];
|
|
173
213
|
for (let i = 0; i < nfd.length; i++) {
|
|
174
214
|
const ch = nfd[i];
|
|
175
215
|
const base = ch.replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
176
216
|
if (!isVowelBase(base))
|
|
177
217
|
continue;
|
|
178
218
|
const v = base === "y" ? "i" : base;
|
|
219
|
+
found.push({ v, at: i });
|
|
179
220
|
if (!firstVowel)
|
|
180
221
|
firstVowel = v;
|
|
181
222
|
if (!accentedVowel && i + 1 < nfd.length && ACCENTED.test(nfd[i + 1])) {
|
|
182
223
|
accentedVowel = v;
|
|
183
224
|
}
|
|
184
225
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
226
|
+
const vowel = accentedVowel || firstVowel;
|
|
227
|
+
return { vowel, accent: Boolean(accentedVowel), diphthong: findDiphthong(nfd, found, vowel) };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The diphthong the SUNG vowel belongs to, or null. `vowel` is the nucleus — what
|
|
231
|
+
* a singer sustains — and a diphthong's second element is a late off-glide; this
|
|
232
|
+
* reports the pair so a renderer can flick toward it without re-deriving which
|
|
233
|
+
* pairs are real.
|
|
234
|
+
*
|
|
235
|
+
* It reuses the syllabifier's own DIPHTHONGS set, so "diphthong" here means
|
|
236
|
+
* exactly what it means when the word is split: ae · oe · au, plus ui in the
|
|
237
|
+
* cui/hui stems only. ei, ui elsewhere, and eu are hiatus (De-i, fu-it, de-us).
|
|
238
|
+
*
|
|
239
|
+
* THE ACCENT IS THE DISCRIMINATOR, which is why this cannot be recovered
|
|
240
|
+
* downstream from the lyric alone. `cae` is one syllable (nucleus a, glide e);
|
|
241
|
+
* `sa-é` is two — the same letters with the accent on the second, and no glide.
|
|
242
|
+
* A substring scan confuses the two, and also misreads `quae`, where `qu` is a
|
|
243
|
+
* consonantal glide: the nucleus is u, and the `ae` is not the sung pair.
|
|
244
|
+
*/
|
|
245
|
+
function findDiphthong(nfd, found, vowel) {
|
|
246
|
+
const lower = [...nfd].map(baseChar).join("");
|
|
247
|
+
for (let i = 0; i < found.length - 1; i++) {
|
|
248
|
+
const a = found[i];
|
|
249
|
+
const b = found[i + 1];
|
|
250
|
+
// Adjacent in the source, and the pair's nucleus is the vowel we selected —
|
|
251
|
+
// anything else belongs to a different syllable.
|
|
252
|
+
if (b.at !== a.at + 1 || a.v !== vowel)
|
|
253
|
+
continue;
|
|
254
|
+
const pair = a.v + b.v;
|
|
255
|
+
if (DIPHTHONGS.has(pair))
|
|
256
|
+
return pair;
|
|
257
|
+
if (pair === "ui" && /^(cui|hui)/.test(lower))
|
|
258
|
+
return pair;
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
188
261
|
}
|
|
189
262
|
export function detectVowelAccent(text) {
|
|
190
263
|
return selectVowel(text).accent;
|
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import type { Season, Grade, Feast } from "../cal/types.js";
|
|
2
2
|
export type { Season, Grade, Feast };
|
|
3
|
-
export type OfficeCode = "an" | "al" | "ca" | "co" | "gr" | "hy" | "in" | "of" | "ps" | "re" | "rb" | "se" | "tr" | "tp" | "or";
|
|
3
|
+
export type OfficeCode = "an" | "al" | "ca" | "co" | "gr" | "hy" | "in" | "of" | "ps" | "re" | "rb" | "se" | "tr" | "tp" | "or" | "im" | "pa" | "su";
|
|
4
4
|
export type OrdinaryCode = "ky" | "gl" | "cr" | "sa" | "ag" | "be" | "it" | "as" | "va";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The books a chant can come from. `source` is PROVENANCE, not an acquisition
|
|
7
|
+
* unit: a book appears here because some chant the liturgy asks for is found in
|
|
8
|
+
* it, not because the whole book ships.
|
|
9
|
+
*
|
|
10
|
+
* The first seven are the original corpus. The office books after them widened
|
|
11
|
+
* it: they carry antiphons and short responsories that fill weekday office
|
|
12
|
+
* slots — without them a matcher could only bind a slot to a chant the five
|
|
13
|
+
* extracted books happened to hold, which is why 41 Fridays had no Vespers
|
|
14
|
+
* ("Per singulos dies" lives in the Psalterium Monasticum).
|
|
15
|
+
*
|
|
16
|
+
* That widening brought in eight; six are gone again. am1, am2,
|
|
17
|
+
* am3, lr, ar1 and ar2 are bare transcriptions — no episema, no ictus,
|
|
18
|
+
* essentially no mora — so once the office matchers began preferring a
|
|
19
|
+
* rhythmically marked witness, every text they carried was better served by a
|
|
20
|
+
* book we already ship. tonus reads those marks for playback, so a bare chant is
|
|
21
|
+
* a worse copy, not a missing one.
|
|
22
|
+
*
|
|
23
|
+
* ams and psm stayed because they are fully marked, and psm holds "Per singulos
|
|
24
|
+
* dies" — the Friday Vespers antiphon the widening was for. Every chant tonus
|
|
25
|
+
* now ships carries rhythmic notation.
|
|
26
|
+
*/
|
|
27
|
+
export type ChantSource = "gr" | "lu" | "la" | "lh" | "am" | "nr" | "ams" | "psm" | "cse" | "cot";
|
|
6
28
|
export type CanonicalHour = "matutinum" | "laudes" | "prima" | "tertia" | "sexta" | "nona" | "vesperae" | "completorium";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
29
|
+
/** The eight canonical hours in the order they are sung, Matins first. The
|
|
30
|
+
* order is the content: a day's office read out of sequence is not the day's
|
|
31
|
+
* office. `officium` validates `hora` against this, so the list a caller reads
|
|
32
|
+
* and the check it must satisfy cannot drift apart. */
|
|
33
|
+
export declare const HORAE: readonly CanonicalHour[];
|
|
34
|
+
/** The keys cantus() accepts — the base set the day verbs extend. Lives here,
|
|
35
|
+
* cycle-free, so ordinary.ts can build its own key set without importing
|
|
36
|
+
* chant.ts (the two are an import cycle). */
|
|
37
|
+
export declare const CANTUS_QUERY_KEYS: Set<string>;
|
|
38
|
+
export declare const MODI: Readonly<Record<string, string>>;
|
|
39
|
+
export declare const OFFICIA: Readonly<Record<OfficeCode, string>>;
|
|
40
|
+
export declare const ORDINARIA: Readonly<Record<string, string>>;
|
|
41
|
+
export declare const KY_SOURCE: Chant["source"];
|
|
10
42
|
export interface Chant {
|
|
11
43
|
id: string;
|
|
12
44
|
incipit: string;
|
|
@@ -27,7 +59,7 @@ export interface Chant {
|
|
|
27
59
|
year: number | null;
|
|
28
60
|
editor: string | null;
|
|
29
61
|
scanSource?: string | null;
|
|
30
|
-
code?: ChantSource | "user";
|
|
62
|
+
code?: ChantSource | "user" | "ky";
|
|
31
63
|
};
|
|
32
64
|
ordinary?: OrdinaryCode;
|
|
33
65
|
ordinarium?: string;
|
|
@@ -61,12 +93,56 @@ export interface Corpus {
|
|
|
61
93
|
editor: string | null;
|
|
62
94
|
scanSource: string | null;
|
|
63
95
|
count: number;
|
|
96
|
+
total: number | null;
|
|
97
|
+
unique: number | null;
|
|
98
|
+
shared: SharedCount[] | null;
|
|
99
|
+
genera: GenusCount[];
|
|
100
|
+
modes: ModeCount[];
|
|
101
|
+
/**
|
|
102
|
+
* What the book HOLDS, before the cut — the ledger of what was left behind.
|
|
103
|
+
* Same genera/modes shape as the shipped counts above, so an omission is
|
|
104
|
+
* visible rather than merely implied by a smaller number. `null` for a book
|
|
105
|
+
* outside GregoBase (nr, ky), the same "unmeasured, not zero" rule as
|
|
106
|
+
* `total`/`unique`/`shared`.
|
|
107
|
+
*/
|
|
108
|
+
full: CorpusFullCount | null;
|
|
109
|
+
}
|
|
110
|
+
/** A book's pre-cut tally, in the shape `corpus()` reports the shipped one. */
|
|
111
|
+
export interface CorpusFullCount {
|
|
64
112
|
total: number;
|
|
65
|
-
unique: number;
|
|
66
|
-
shared: SharedCount[];
|
|
67
113
|
genera: GenusCount[];
|
|
68
114
|
modes: ModeCount[];
|
|
69
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* The whole shelf: every book's ledger, and the corpus-wide rollup.
|
|
118
|
+
* Returned by `corpus()` with no argument.
|
|
119
|
+
*/
|
|
120
|
+
export interface CorpusLedger {
|
|
121
|
+
/**
|
|
122
|
+
* How many chants tonus holds — every chant it can name, counted once.
|
|
123
|
+
*
|
|
124
|
+
* This is THE number: distinct chants, so it does not move with how many
|
|
125
|
+
* books happen to print the same melody. Listings are `listings` below —
|
|
126
|
+
* a fact about the shelf, not about the repertoire.
|
|
127
|
+
*/
|
|
128
|
+
count: number;
|
|
129
|
+
/**
|
|
130
|
+
* Book listings across the shelf: a chant printed in two books counts twice.
|
|
131
|
+
* `listings - count` is how much the books overlap. Secondary on purpose —
|
|
132
|
+
* it answers "how long is the shelf", not "how much chant is there".
|
|
133
|
+
*/
|
|
134
|
+
listings: number;
|
|
135
|
+
/** Chants the books hold in total, before the cut. */
|
|
136
|
+
total: number;
|
|
137
|
+
genera: GenusCount[];
|
|
138
|
+
modes: ModeCount[];
|
|
139
|
+
/** Per book, in corpus order. */
|
|
140
|
+
books: Corpus[];
|
|
141
|
+
}
|
|
142
|
+
/** `corpus({ book })` — the query form; `corpus(code)` still works. */
|
|
143
|
+
export interface CorpusQuery {
|
|
144
|
+
book?: ChantSource;
|
|
145
|
+
}
|
|
70
146
|
export interface CantusQuery {
|
|
71
147
|
id?: string | string[];
|
|
72
148
|
gabc?: string;
|
|
@@ -74,6 +150,37 @@ export interface CantusQuery {
|
|
|
74
150
|
mode?: number | string | (number | string)[];
|
|
75
151
|
office?: OfficeCode | OfficeCode[];
|
|
76
152
|
source?: ChantSource | ChantSource[];
|
|
153
|
+
/**
|
|
154
|
+
* A part of the Mass ordinary — `"ky"` for the Kyries, `"gl"` the Glorias,
|
|
155
|
+
* and so on. This is the door to the Kyriale, which is addressable but not
|
|
156
|
+
* shelved (it is a partition of the Graduale, not a book), so it is absent
|
|
157
|
+
* from `source` and from an unfiltered search. Asking for an ordinary can
|
|
158
|
+
* only mean one thing, which is why it may reach where `mode` alone does not.
|
|
159
|
+
*
|
|
160
|
+
* For the setting a given DAY calls for, `ordinarium({ feast })` is the verb:
|
|
161
|
+
* it applies the Kyriale's own rubrics. This is the flat retrieval — every
|
|
162
|
+
* Kyrie in the book, whatever day would sing it.
|
|
163
|
+
*/
|
|
164
|
+
ordinary?: OrdinaryCode | OrdinaryCode[];
|
|
165
|
+
/**
|
|
166
|
+
* Only chants ATTESTED by this year — the repertoire as of a date, the
|
|
167
|
+
* analogue of `festum({ before })`, and the two COMPOSE: a Feast resolved by
|
|
168
|
+
* `festum({ date, before })` carries the view, and every day verb (proprium,
|
|
169
|
+
* ordinarium, officium) serves under it without being told the year twice;
|
|
170
|
+
* an own `before` overrides the feast's. Only centuries wholly CLOSED by
|
|
171
|
+
* the year count as witnessed: `before: 1098` keeps what a manuscript of
|
|
172
|
+
* the 10th century or earlier already holds (see attest.ts for why).
|
|
173
|
+
*
|
|
174
|
+
* This is evidence, not existence: the date comes from CANTUS's manuscript
|
|
175
|
+
* index, so it is a terminus ante quem. A chant with no dated witness is
|
|
176
|
+
* excluded rather than assumed old — silence is not evidence of age.
|
|
177
|
+
* This is the ONE time argument: a `century` spelling is deliberately
|
|
178
|
+
* absent, because it was `before: N * 100` in different clothes — one
|
|
179
|
+
* cutoff internally, two spellings at the door (see attest.ts).
|
|
180
|
+
*/
|
|
181
|
+
before?: number;
|
|
182
|
+
/** Only chants transmitted by this cursus; `both` always qualifies. */
|
|
183
|
+
cursus?: "monastic" | "secular";
|
|
77
184
|
limit?: number;
|
|
78
185
|
offset?: number;
|
|
79
186
|
sort?: "incipit" | "mode" | "id";
|
|
@@ -86,13 +193,9 @@ export interface OrdinariumQuery extends CantusQuery {
|
|
|
86
193
|
ordinary?: OrdinaryCode;
|
|
87
194
|
mass?: number;
|
|
88
195
|
}
|
|
89
|
-
/** Which rite's Office to assemble. `romanum` (default) is the Tridentine Roman
|
|
90
|
-
* cursus; `monasticum` is the Benedictine cursus (Antiphonale Monasticum). */
|
|
91
|
-
export type Rite = "romanum" | "monasticum";
|
|
92
196
|
export interface OfficiumQuery extends CantusQuery {
|
|
93
197
|
feast?: Feast | Feast[];
|
|
94
198
|
hora?: CanonicalHour;
|
|
95
|
-
rite?: Rite;
|
|
96
199
|
}
|
|
97
200
|
export interface PsalmusQuery {
|
|
98
201
|
psalm?: number | string;
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
/** The eight canonical hours in the order they are sung, Matins first. The
|
|
2
|
+
* order is the content: a day's office read out of sequence is not the day's
|
|
3
|
+
* office. `officium` validates `hora` against this, so the list a caller reads
|
|
4
|
+
* and the check it must satisfy cannot drift apart. */
|
|
5
|
+
export const HORAE = Object.freeze([
|
|
6
|
+
"matutinum", "laudes", "prima", "tertia", "sexta", "nona",
|
|
7
|
+
"vesperae", "completorium",
|
|
8
|
+
]);
|
|
1
9
|
// ── Display labels ──
|
|
2
|
-
|
|
10
|
+
/** The keys cantus() accepts — the base set the day verbs extend. Lives here,
|
|
11
|
+
* cycle-free, so ordinary.ts can build its own key set without importing
|
|
12
|
+
* chant.ts (the two are an import cycle). */
|
|
13
|
+
export const CANTUS_QUERY_KEYS = new Set([
|
|
14
|
+
"id", "gabc", "incipit", "mode", "office", "source", "limit", "offset", "sort",
|
|
15
|
+
"before", "cursus", "ordinary",
|
|
16
|
+
]);
|
|
17
|
+
export const MODI = Object.freeze({
|
|
3
18
|
"1": "Modus I", "2": "Modus II", "3": "Modus III", "4": "Modus IV",
|
|
4
19
|
"5": "Modus V", "6": "Modus VI", "7": "Modus VII", "8": "Modus VIII",
|
|
5
20
|
});
|
|
6
|
-
export const
|
|
21
|
+
export const OFFICIA = Object.freeze({
|
|
7
22
|
an: "Antiphona",
|
|
8
23
|
al: "Alleluia",
|
|
9
24
|
ca: "Canticum",
|
|
@@ -16,11 +31,15 @@ export const OFFICE_LABELS = Object.freeze({
|
|
|
16
31
|
re: "Responsorium",
|
|
17
32
|
rb: "Responsorium Breve",
|
|
18
33
|
se: "Sequentia",
|
|
34
|
+
// Reported in the pre-cut tallies, not shipped — see OfficeCode above.
|
|
35
|
+
im: "Improperia", // the Good Friday Reproaches (Popule meus)
|
|
36
|
+
pa: "Antiphona Mariana", // Marian antiphons outside the office cycle
|
|
37
|
+
su: "Supplicatio", // litanies and supplications (the Easter Vigil litany)
|
|
19
38
|
tr: "Tractus",
|
|
20
39
|
tp: "Tonus Peregrinus",
|
|
21
40
|
or: "Ordinarium",
|
|
22
41
|
});
|
|
23
|
-
export const
|
|
42
|
+
export const ORDINARIA = Object.freeze({
|
|
24
43
|
ky: "Kyrie eleison",
|
|
25
44
|
gl: "Gloria",
|
|
26
45
|
cr: "Credo",
|
|
@@ -31,4 +50,20 @@ export const ORDINARY_LABELS = Object.freeze({
|
|
|
31
50
|
as: "Asperges",
|
|
32
51
|
va: "Vidi aquam",
|
|
33
52
|
});
|
|
53
|
+
// The Kyriale's bibliographic identity. NOT a ChantSource: `ky` is a partition
|
|
54
|
+
// of the Graduale, not a book of its own (chant.ts, CORPUS), so it is not a
|
|
55
|
+
// value `cantus({ source })` takes and not a row in the shelf. This record
|
|
56
|
+
// still rides every kyriale chant, because a chant should say which book it is
|
|
57
|
+
// printed in — and the Kyriale is what a singer would be holding.
|
|
58
|
+
// Hand-authored (the other books' SOURCE constants ride their generated data
|
|
59
|
+
// files; the kyriale's data file predates its book registration): the chants
|
|
60
|
+
// are the Kyriale section of the 1961 Solesmes Graduale Romanum, extracted
|
|
61
|
+
// from GregoBase GR source pages. Shared by the corpus surface (chant.ts) and
|
|
62
|
+
// the ordinary engine so a kyriale chant carries ONE identity everywhere.
|
|
63
|
+
export const KY_SOURCE = Object.freeze({
|
|
64
|
+
book: "Kyriale (Graduale Romanum)",
|
|
65
|
+
year: 1961,
|
|
66
|
+
editor: "Solesmes",
|
|
67
|
+
code: "ky",
|
|
68
|
+
});
|
|
34
69
|
//# sourceMappingURL=types.js.map
|
|
@@ -63,6 +63,10 @@ export function buildHarmonia(input, opts = {}) {
|
|
|
63
63
|
if (cosmosArray.length === 0) {
|
|
64
64
|
throw new RangeError("harmonia requires at least one Cosmos");
|
|
65
65
|
}
|
|
66
|
+
for (const c of cosmosArray) {
|
|
67
|
+
if (!c || typeof c !== "object" || !Array.isArray(c.bodies))
|
|
68
|
+
throw new Error("harmonia: input must be a Cosmos (from tonus.caelum) — its bodies are what get voiced");
|
|
69
|
+
}
|
|
66
70
|
const temper = opts.temperamentum ?? buildTemper();
|
|
67
71
|
const scale = resolveScale(temper);
|
|
68
72
|
const doctrinaKey = opts.doctrina ?? "boethius";
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
// as [numerator, denominator] pairs.
|
|
8
8
|
//
|
|
9
9
|
// All v1 doctrinae are geocentric — Earth is the silent listener at the
|
|
10
|
-
// center, not a voiced body
|
|
10
|
+
// center, not a voiced body. Pliny's table does assign Earth a boundary tone
|
|
11
|
+
// (proslambanomenos, kept below as documentation), but Earth carries no
|
|
12
|
+
// classical vowel, so the voicing engine leaves it silent there too.
|
|
11
13
|
// The Sun holds the mese position in all four systems.
|
|
12
14
|
//
|
|
13
15
|
// The ratios are reconstructed from the primary texts [biblio:
|
|
@@ -11,6 +11,9 @@ export interface HarmonyTabulaRow {
|
|
|
11
11
|
oct: number;
|
|
12
12
|
spn: string;
|
|
13
13
|
hz: number;
|
|
14
|
+
/** The doctrina's ratio against the mese, [num, den] — what the sphere IS
|
|
15
|
+
* in the scheme, of which spn and hz are the sounding. */
|
|
16
|
+
ratio: readonly [number, number];
|
|
14
17
|
presence: number;
|
|
15
18
|
motion: number;
|
|
16
19
|
velocity: number;
|
|
@@ -13,6 +13,10 @@ export interface VoicedBody extends Body {
|
|
|
13
13
|
presence: number;
|
|
14
14
|
motion: number;
|
|
15
15
|
greekName: string;
|
|
16
|
+
/** The doctrina's own ratio for this sphere, [num, den] against the mese.
|
|
17
|
+
* The pitch is DERIVED from it, so this is the primary datum and the note
|
|
18
|
+
* name is the reading — a caller comparing doctrinae wants the fraction. */
|
|
19
|
+
ratio: readonly [number, number];
|
|
16
20
|
vowel: PlanetVowel;
|
|
17
21
|
}
|
|
18
22
|
export declare function voiceBodies(bodies: Body[], doctrina: Doctrina, scale: Scale): VoicedBody[];
|
|
@@ -5,9 +5,8 @@ import { computePresence, computeMotion } from "./presence.js";
|
|
|
5
5
|
// reference. Ratios in the doctrina are multiplied by this anchor to produce Hz.
|
|
6
6
|
const ANCHOR_MIDI = 69;
|
|
7
7
|
function voiceOne(body, voice, vowel, scale) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const motion = pliny ? 0 : computeMotion(body);
|
|
8
|
+
const presence = computePresence(body);
|
|
9
|
+
const motion = computeMotion(body);
|
|
11
10
|
// Direct Hz from anchor (A4) and doctrina ratio — bypass scale quantization
|
|
12
11
|
// so the ratio's pure Hz relationship is preserved.
|
|
13
12
|
const ratioVal = voice.ratio[0] / voice.ratio[1];
|
|
@@ -28,6 +27,7 @@ function voiceOne(body, voice, vowel, scale) {
|
|
|
28
27
|
presence,
|
|
29
28
|
motion,
|
|
30
29
|
greekName: voice.greekName,
|
|
30
|
+
ratio: voice.ratio,
|
|
31
31
|
vowel,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
@@ -41,9 +41,13 @@ export function voiceBodies(bodies, doctrina, scale) {
|
|
|
41
41
|
const voice = voiceByBody.get(body.name);
|
|
42
42
|
if (!voice)
|
|
43
43
|
continue; // body not part of this doctrina (e.g. Earth in Boethius)
|
|
44
|
+
// Only the seven vowel-bearing planets voice. Earth stays silent even
|
|
45
|
+
// under Pliny, whose table assigns it proslambanomenos — the ratio is
|
|
46
|
+
// kept in the doctrina as documentation, but no classical vowel exists
|
|
47
|
+
// for Earth, and tonus does not invent one.
|
|
44
48
|
const vowel = PLANET_VOWELS[body.name];
|
|
45
49
|
if (!vowel)
|
|
46
|
-
continue;
|
|
50
|
+
continue;
|
|
47
51
|
result.push(voiceOne(body, voice, vowel, scale));
|
|
48
52
|
}
|
|
49
53
|
return result;
|
package/dist/engines/imprint.js
CHANGED
|
@@ -84,6 +84,10 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
84
84
|
const cadenceNotes = opts.cadenceNotes;
|
|
85
85
|
const pcCounts = new Array(12).fill(0);
|
|
86
86
|
let total = 0;
|
|
87
|
+
// For the tessitura signal: the mean MIDI of every note, and the last note.
|
|
88
|
+
let midiSum = 0;
|
|
89
|
+
let noteCount = 0;
|
|
90
|
+
let lastNote = null;
|
|
87
91
|
for (let pi = 0; pi < phrases.length; pi++) {
|
|
88
92
|
const phrase = phrases[pi];
|
|
89
93
|
for (let si = 0; si < phrase.syllables.length; si++) {
|
|
@@ -97,6 +101,9 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
97
101
|
w *= CADENCE_WEIGHT;
|
|
98
102
|
pcCounts[note.pitch.pc] += w;
|
|
99
103
|
total += w;
|
|
104
|
+
midiSum += note.pitch.midi;
|
|
105
|
+
noteCount++;
|
|
106
|
+
lastNote = { pc: note.pitch.pc, midi: note.pitch.midi };
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
}
|
|
@@ -105,11 +112,17 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
105
112
|
pcDistribution[pc] = total > 0 ? pcCounts[pc] / total : 0;
|
|
106
113
|
}
|
|
107
114
|
const firstNotePc = phrases[0]?.syllables[0]?.notes[0]?.pitch.pc;
|
|
115
|
+
// Tessitura = the melody's mean height above where it comes to rest.
|
|
116
|
+
const tessitura = lastNote && noteCount > 0 ? midiSum / noteCount - lastNote.midi : undefined;
|
|
108
117
|
return {
|
|
109
118
|
pcDistribution,
|
|
110
119
|
attractors: computeAttractors(pcDistribution, scale),
|
|
111
120
|
vowelAttractors: computeVowelAttractors(phrases, scale),
|
|
112
|
-
modalAffinity: computeModalAffinity(pcDistribution,
|
|
121
|
+
modalAffinity: computeModalAffinity(pcDistribution, {
|
|
122
|
+
firstNotePc,
|
|
123
|
+
lastNotePc: lastNote?.pc,
|
|
124
|
+
tessitura,
|
|
125
|
+
}),
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
/** Build an Imprint from voiced planetary bodies (presence-weighted pc counts). */
|
|
@@ -34,7 +34,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
34
34
|
datasets: [
|
|
35
35
|
[
|
|
36
36
|
[0.72333566, 0.0000039],
|
|
37
|
-
[0.
|
|
37
|
+
[0.00677672, -0.00004107],
|
|
38
38
|
[3.39467605, -0.0007889],
|
|
39
39
|
[181.9790995, 58517.81538729],
|
|
40
40
|
[131.60246718, 0.00268329],
|
|
@@ -42,7 +42,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
42
42
|
],
|
|
43
43
|
[
|
|
44
44
|
[0.72332102, -0.00000026],
|
|
45
|
-
[
|
|
45
|
+
[0.00676399, -0.00005107],
|
|
46
46
|
[3.39777545, 0.00043494],
|
|
47
47
|
[181.9797085, 58517.8156026],
|
|
48
48
|
[131.76755713, 0.05679648],
|
|
@@ -68,7 +68,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
68
68
|
[0, 0],
|
|
69
69
|
],
|
|
70
70
|
[
|
|
71
|
-
[1.00000018,
|
|
71
|
+
[1.00000018, -0.00000003],
|
|
72
72
|
[0.01673163, -0.00003661],
|
|
73
73
|
[-0.00054346, -0.01337178],
|
|
74
74
|
[100.46691572, 35999.37306329],
|
|
@@ -129,7 +129,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
129
129
|
[14.27495244, 0.18199196],
|
|
130
130
|
[100.29282654, 0.13024619],
|
|
131
131
|
],
|
|
132
|
-
[0.00012452, 0.0606406, -0.35635438, 38.35125],
|
|
132
|
+
[-0.00012452, 0.0606406, -0.35635438, 38.35125],
|
|
133
133
|
],
|
|
134
134
|
radius: 69911,
|
|
135
135
|
rotation_period: 0.41354,
|