kaabalah
Version:
The de-facto library for any esoteric calculations and tooling
857 lines (854 loc) • 182 kB
JavaScript
'use strict';
var chunkLQX6PW35_js = require('./chunk-LQX6PW35.js');
var chunkVJ6RW5H4_js = require('./chunk-VJ6RW5H4.js');
// src/visual/archeometer.ts
var TAU = Math.PI * 2;
var ARCHEOMETER_DEFAULT_VIEWBOX = {
minX: 0,
minY: 0,
width: 912,
height: 912
};
var ARCHEOMETER_OUTER_CROWN_BLEED = 22;
var DEFAULT_LAYERS = {
degreeCrown: true,
zodiacUtterance: true,
planetaryUtterance: true,
cosmologicalMusic: true,
astralZodiac: true,
astralPlanetary: true,
chromicRays: true,
whiteRays: true,
solarCenter: true
};
var COLOR_RING_FILLS = {
degreeOuter: "#fff8ef",
degreeInner: "#fff2e1",
zodiacUtterance: "#eba0be",
planetaryUtterance: "#fffaf1",
cosmologicalMusic: "#fffaf0",
astralZodiac: "#efc0bc",
astralPlanetary: "#d2e8df",
chromicRays: "#fff3d4",
whiteRays: "#eef1ec",
solarCenter: "#f7d65b"
};
var COLOR_PALETTE = {
paper: "#fffaf1",
ink: "#151515",
subtleInk: "#605a54",
ringStroke: "#171717",
degreeTick: "#171717",
degreeLabel: "#171717",
whiteRay: "#25395a",
ringFills: COLOR_RING_FILLS
};
var DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES = [
{
degree: 0,
utterance: { id: "ph", degree: 0, letter: "P, Ph", number: 80, color: "#f2d54c", triangleId: "wordJesus", gloss: "Utterance / light" },
triangleLabel: { degree: 0, label: "S, Sh", number: 300 },
musicalNote: { degree: 0, note: "Si" },
zodiacSign: { degree: 0, name: "Capricorn", glyph: "\u2651" },
planetaryPoint: { degree: 0, name: "Saturn", glyph: "\u2644", phase: "diurnal" }
},
{
degree: 30,
utterance: { id: "za", degree: 30, letter: "W, OU", number: 70, color: "#b7d56a", triangleId: "ether", gloss: "Ether" },
triangleLabel: { degree: 30, label: "D", number: 4 },
musicalNote: { degree: 30, note: "Do" },
zodiacSign: { degree: 30, name: "Sagittarius", glyph: "\u2650" },
planetaryPoint: { degree: 30, name: "Jupiter", glyph: "\u2643", phase: "diurnal" }
},
{
degree: 60,
utterance: { id: "ma", degree: 60, letter: "M", number: 40, color: "#79bf6a", triangleId: "mary", gloss: "Mary" },
triangleLabel: { degree: 60, label: "C", number: 20 },
musicalNote: { degree: 60, note: "R\xE9" },
zodiacSign: { degree: 60, name: "Scorpio", glyph: "\u264F" },
planetaryPoint: { degree: 60, name: "Mars", glyph: "\u2642", phase: "diurnal" }
},
{
degree: 90,
utterance: { id: "u", degree: 90, letter: "L", number: 30, color: "#69ad74", triangleId: "divineFire", gloss: "Fire" },
triangleLabel: { degree: 90, label: "" },
musicalNote: { degree: 90, note: "Mi" },
zodiacSign: { degree: 90, name: "Libra", glyph: "\u264E" },
planetaryPoint: { degree: 90, name: "Venus", glyph: "\u2640", phase: "diurnal" }
},
{
degree: 120,
utterance: { id: "o", degree: 120, letter: "I, Y, J", number: 10, color: "#efb83e", triangleId: "wordJesus", gloss: "Voice / sound" },
triangleLabel: { degree: 120, label: "Ts", number: 90 },
musicalNote: { degree: 120, note: "Fa" },
zodiacSign: { degree: 120, name: "Virgo", glyph: "\u264D" },
planetaryPoint: { degree: 120, name: "Mercury", glyph: "\u263F", phase: "diurnal" }
},
{
degree: 150,
utterance: { id: "la", degree: 150, letter: "T", number: 9, color: "#ec8d3f", triangleId: "ether", gloss: "Ether" },
triangleLabel: { degree: 150, label: "N", number: 50 },
musicalNote: { degree: 150, note: "Sol" },
zodiacSign: { degree: 150, name: "Leo", glyph: "\u264C" },
planetaryPoint: { degree: 150, name: "Sun", glyph: "\u2609", phase: "diurnal" }
},
{
degree: 180,
utterance: { id: "ri", degree: 180, letter: "E, H", number: 8, color: "#df4d43", triangleId: "mary", gloss: "Descending R" },
triangleLabel: { degree: 180, label: "B", number: 2 },
musicalNote: { degree: 180, note: "La" },
zodiacSign: { degree: 180, name: "Cancer", glyph: "\u264B" },
planetaryPoint: { degree: 180, name: "Moon", glyph: "\u263E", phase: "nocturnal" }
},
{
degree: 210,
utterance: { id: "t", degree: 210, letter: "Z", number: 7, color: "#8b54ad", triangleId: "divineFire", gloss: "Divine fire" },
triangleLabel: { degree: 210, label: "Ts", number: 90 },
musicalNote: { degree: 210, note: "Si" },
zodiacSign: { degree: 210, name: "Gemini", glyph: "\u264A" },
planetaryPoint: { degree: 210, name: "Mercury", glyph: "\u263F", phase: "nocturnal" }
},
{
degree: 240,
utterance: { id: "y", degree: 240, letter: "V, OU", number: 6, color: "#d85c43", triangleId: "wordJesus", gloss: "Word / JeShU" },
triangleLabel: { degree: 240, label: "G", number: 3 },
musicalNote: { degree: 240, note: "Do" },
zodiacSign: { degree: 240, name: "Taurus", glyph: "\u2649" },
planetaryPoint: { degree: 240, name: "Venus", glyph: "\u2640", phase: "nocturnal" }
},
{
degree: 270,
utterance: { id: "ka", degree: 270, letter: "H, E", number: 5, color: "#3c82c2", triangleId: "ether", gloss: "Etheric power" },
triangleLabel: { degree: 270, label: "C", number: 20 },
musicalNote: { degree: 270, note: "R\xE9" },
zodiacSign: { degree: 270, name: "Aries", glyph: "\u2648" },
planetaryPoint: { degree: 270, name: "Mars", glyph: "\u2642", phase: "nocturnal" }
},
{
degree: 300,
utterance: { id: "h", degree: 300, letter: "R", number: 200, color: "#d4579c", triangleId: "mary", gloss: "Living waters" },
triangleLabel: { degree: 300, label: "D", number: 4 },
musicalNote: { degree: 300, note: "Mi" },
zodiacSign: { degree: 300, name: "Pisces", glyph: "\u2653" },
planetaryPoint: { degree: 300, name: "Jupiter", glyph: "\u2643", phase: "nocturnal" }
},
{
degree: 330,
utterance: { id: "hou", degree: 330, letter: "K", number: 100, color: "#7963b8", triangleId: "divineFire", gloss: "Divine fire" },
triangleLabel: { degree: 330, label: "S, Sh", number: 300 },
musicalNote: { degree: 330, note: "Fa" },
zodiacSign: { degree: 330, name: "Aquarius", glyph: "\u2652" },
planetaryPoint: { degree: 330, name: "Saturn", glyph: "\u2644", phase: "nocturnal" }
}
];
var DEFAULT_ARCHEOMETER_UTTERANCE = DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES.map((sector) => sector.utterance);
var DEFAULT_ARCHEOMETER_TRIANGLE_LABELS = DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES.map((sector) => sector.triangleLabel);
var DEFAULT_ARCHEOMETER_TRIANGLES = [
{ id: "wordJesus", title: "Triangle of the Word / JeShU", phrase: "Y-PhO", apex: "north", vertices: [0, 120, 240], fill: "#f2cf45", vertexFills: ["#f2cf45", "#5470a5", "#dd3e38"], stroke: "#171717" },
{ id: "mary", title: "Triangle of Mary / Living Waters", phrase: "Ma-Ri-H", apex: "south", vertices: [180, 300, 60], fill: "#e25b61", vertexFills: ["#cc58a1", "#f28a32", "#78bd79"], stroke: "#171717" },
{ id: "ether", title: "Triangle of the Ether", phrase: "La-Ka-Za", apex: "west", vertices: [270, 30, 150], fill: "#78bd79", vertexFills: ["#e96836", "#b7bd58", "#8f69a3"], stroke: "#171717" },
{ id: "divineFire", title: "Triangle of Divine Fire", phrase: "Hou-U-T", apex: "east", vertices: [90, 210, 330], fill: "#cc58a1", vertexFills: ["#63a890", "#d45375", "#f0b33f"], stroke: "#171717" }
];
var DEFAULT_ARCHEOMETER_MUSICAL_NOTES = DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES.map((sector) => sector.musicalNote);
var DEFAULT_ARCHEOMETER_ZODIAC = DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES.map((sector) => sector.zodiacSign);
var DEFAULT_ARCHEOMETER_PLANETS = DEFAULT_ARCHEOMETER_SECTOR_CORRESPONDENCES.map((sector) => sector.planetaryPoint);
function getArcheometerRenderModel(options = {}) {
const viewBox = normalizeViewBox(options.viewBox);
const scale = Math.min(viewBox.width / ARCHEOMETER_DEFAULT_VIEWBOX.width, viewBox.height / ARCHEOMETER_DEFAULT_VIEWBOX.height);
const padding = options.padding ?? 0;
const center = {
x: round(viewBox.minX + viewBox.width / 2),
y: round(viewBox.minY + viewBox.height / 2)
};
const frameRadius = round(Math.max(0, Math.min(viewBox.width, viewBox.height) / 2 - padding));
const outerRadius = round(Math.max(0, frameRadius - ARCHEOMETER_OUTER_CROWN_BLEED * scale));
return {
viewBox,
center,
outerRadius,
scale,
rings: buildRings(outerRadius, frameRadius),
palette: resolvePalette(options.palette),
rotationDegrees: options.rotationDegrees ?? 0,
layers: { ...DEFAULT_LAYERS, ...options.layers ?? {} },
degreeLabelEvery: options.degreeLabelEvery ?? 15,
utterance: options.utterance ?? DEFAULT_ARCHEOMETER_UTTERANCE,
triangles: options.triangles ?? DEFAULT_ARCHEOMETER_TRIANGLES,
triangleLabels: options.triangleLabels ?? DEFAULT_ARCHEOMETER_TRIANGLE_LABELS,
musicalNotes: options.musicalNotes ?? DEFAULT_ARCHEOMETER_MUSICAL_NOTES,
zodiacSigns: options.zodiacSigns ?? DEFAULT_ARCHEOMETER_ZODIAC,
planetaryPoints: options.planetaryPoints ?? DEFAULT_ARCHEOMETER_PLANETS
};
}
var DEFAULT_ARCHETYPE_UTTERANCE = DEFAULT_ARCHEOMETER_UTTERANCE;
var DEFAULT_ARCHETYPE_TRIANGLES = DEFAULT_ARCHEOMETER_TRIANGLES;
var DEFAULT_ARCHETYPE_MUSICAL_NOTES = DEFAULT_ARCHEOMETER_MUSICAL_NOTES;
var DEFAULT_ARCHETYPE_ZODIAC = DEFAULT_ARCHEOMETER_ZODIAC;
var DEFAULT_ARCHETYPE_PLANETS = DEFAULT_ARCHEOMETER_PLANETS;
function generateArcheometerSvg(options = {}) {
const model = getArcheometerRenderModel(options);
const { viewBox, center, outerRadius, palette } = model;
const background = options.background ?? "transparent";
const lines = [];
const push = (line) => lines.push(line);
const title = options.title ?? "The Cosmological Archeometer";
const attrs = [
`xmlns="http://www.w3.org/2000/svg"`,
options.width !== void 0 ? `width="${escapeAttr(String(options.width))}"` : "",
options.height !== void 0 ? `height="${escapeAttr(String(options.height))}"` : "",
`viewBox="${fmt(viewBox.minX)} ${fmt(viewBox.minY)} ${fmt(viewBox.width)} ${fmt(viewBox.height)}"`,
`preserveAspectRatio="xMidYMid meet"`,
`role="img"`,
`aria-label="${escapeAttr(title)}"`
].filter(Boolean);
push(`<svg ${attrs.join(" ")}>`);
push(`<title>${escapeText(title)}</title>`);
if (background !== "transparent") {
push(`<rect x="${fmt(viewBox.minX)}" y="${fmt(viewBox.minY)}" width="${fmt(viewBox.width)}" height="${fmt(viewBox.height)}" fill="${escapeAttr(background)}"/>`);
}
push(`<circle cx="${fmt(center.x)}" cy="${fmt(center.y)}" r="${fmt(outerRadius)}" fill="${escapeAttr(palette.paper)}"/>`);
renderDefs(push, model);
renderRingGrounds(push, model);
if (model.layers.chromicRays)
renderChromicRays(push, model);
if (model.layers.planetaryUtterance)
renderPlanetaryUtterance(push, model);
if (model.layers.zodiacUtterance)
renderZodiacUtterance(push, model);
if (model.layers.cosmologicalMusic)
renderCosmologicalMusic(push, model);
if (model.layers.astralZodiac)
renderAstralZodiac(push, model);
if (model.layers.astralPlanetary)
renderAstralPlanetary(push, model);
if (model.layers.solarCenter)
renderSolarCenter(push, model);
if (model.layers.whiteRays)
renderWhiteRays(push, model);
if (model.layers.degreeCrown)
renderDegreeCrown(push, model);
renderFrame(push, model);
push(`</svg>`);
return lines.join("\n");
}
function renderDefs(push, model) {
const { rings, center } = model;
const planetaryClipOuter = planetaryTriangleClipOuterRadius(rings);
const planetaryClipInner = planetaryTriangleClipInnerRadius(rings);
const chromicClipOuter = chromicCoreOuterRadius(rings);
const chromicClipInner = chromicCoreInnerRadius(rings);
push(`<defs>`);
push(
`<clipPath id="archeometer-planetary-clip"><path d="${annulusPath(
center,
planetaryClipInner,
planetaryClipOuter
)}" fill-rule="evenodd" clip-rule="evenodd"/></clipPath>`
);
push(
`<clipPath id="archeometer-chromic-clip"><path d="${annulusPath(
center,
chromicClipInner,
chromicClipOuter
)}" fill-rule="evenodd" clip-rule="evenodd"/></clipPath>`
);
push(`</defs>`);
}
function renderRingGrounds(push, model) {
const { rings, center, palette } = model;
const order = [
"degreeOuter",
"degreeInner",
"zodiacUtterance",
"planetaryUtterance",
"cosmologicalMusic",
"astralZodiac",
"astralPlanetary",
"chromicRays",
"whiteRays"
];
push(`<g id="archeometer-ring-grounds">`);
for (const id2 of order) {
const ring = rings[id2];
push(`<path id="archeometer-ring-${id2}" d="${annulusPath(center, ring.r1, ring.r2)}" fill="${escapeAttr(palette.ringFills[id2])}" fill-rule="evenodd" stroke="none"/>`);
}
push(`</g>`);
}
function renderDegreeCrown(push, model) {
const { center, rings, palette, scale } = model;
const outer = rings.degreeOuter;
const inner = rings.degreeInner;
push(`<g id="archeometer-degree-crown" aria-label="dual 360 degree differential numerical protractor">`);
for (let degree = 0; degree < 360; degree += 30) {
const angle = angleOf(model, degree);
const pOuter = polarToXY(center, (outer.r1 + outer.r2) / 2, angle);
const pInner = polarToXY(center, (inner.r1 + inner.r2) / 2, angle);
const rot = tangentRotation(angle);
const outerText = String(normalizeDegrees(345 + degree) || 360);
const innerText = String(normalizeDegrees(15 - degree) || 360);
push(textSvg(outerText, pOuter, 8.4 * scale, palette.degreeLabel, rot, "middle"));
push(textSvg(innerText, pInner, 7.6 * scale, palette.degreeLabel, rot, "middle"));
}
push(`</g>`);
}
function renderZodiacUtterance(push, model) {
const { center, rings, palette, scale } = model;
const ring = rings.zodiacUtterance;
const sorted = sortedByDegree(model.utterance);
push(`<g id="archeometer-zodiacal-utterance" aria-label="zodiacal crown of the utterance">`);
for (let i = 0; i < 12; i++) {
const degree = i * 30;
const sector = annularSectorPath(
center,
ring.r1,
ring.r2,
angleOf(model, degree - 15),
angleOf(model, degree + 15),
30
);
push(
`<path d="${sector}" fill="${escapeAttr(palette.ringFills.zodiacUtterance)}" fill-opacity="1" stroke="${escapeAttr(
palette.ringStroke
)}" stroke-opacity="0.34" stroke-width="${fmt(0.7 * scale)}"/>`
);
}
for (const point of sorted) {
const a = angleOf(model, point.degree);
const p = polarToXY(center, ring.r1 + (ring.r2 - ring.r1) * 0.58, a);
const shieldR = (ring.r2 - ring.r1) * 0.22;
const letterParts = point.letter.split(",").map((part) => part.trim()).filter(Boolean);
const isStacked = letterParts.length > 1;
const letterFontSize = (isStacked ? letterParts.length > 2 ? 8.3 : 10.6 : 14) * scale;
const letterLineHeight = letterFontSize * (isStacked ? 1.02 : 1);
const numberFontSize = (isStacked ? 6.6 : 7.1) * scale;
const numberGap = (isStacked ? 4.8 : 0) * scale;
const stackedNumberY = p.y + (letterParts.length - 1) * letterLineHeight / 2 + numberGap + numberFontSize * 0.42;
const singleLetterY = p.y - shieldR * 0.48;
const firstLineY = isStacked ? p.y - (letterParts.length - 1) * letterLineHeight / 2 - numberFontSize * 0.42 : singleLetterY;
const numberY = isStacked ? stackedNumberY : p.y + shieldR * 0.72;
push(
`<g class="archeometer-utterance-point" data-degree="${fmt(normalizeDegrees(point.degree))}" data-letter="${escapeAttr(
point.letter
)}">`
);
for (const [index, part] of letterParts.entries()) {
push(textSvg(part, { x: p.x, y: firstLineY + index * letterLineHeight }, letterFontSize, palette.ink, 0, "middle", 700));
}
push(textSvg(String(point.number), { x: p.x, y: numberY }, numberFontSize, palette.ink, 0, "middle"));
push(`</g>`);
}
push(`</g>`);
}
function renderPlanetaryUtterance(push, model) {
const { center, rings, palette, scale } = model;
const ring = rings.planetaryUtterance;
const clipOuter = planetaryTriangleClipOuterRadius(rings);
const vertexRadius = clipOuter;
push(`<g id="archeometer-planetary-utterance" aria-label="four trigones of the planetary utterance">`);
push(`<g clip-path="url(#archeometer-planetary-clip)">`);
for (const triangle of model.triangles) {
const vertices = triangle.vertices.map((degree) => polarToXY(center, vertexRadius, angleOf(model, degree)));
if (triangle.vertexFills) {
const centroid = polygonCentroid(vertices);
const midpoints = vertices.map((vertex, index) => midpoint(vertex, vertices[(index + 1) % vertices.length]));
for (const [index, vertex] of vertices.entries()) {
const previousMidpoint = midpoints[(index + vertices.length - 1) % vertices.length];
const nextMidpoint = midpoints[index];
push(`<path class="archeometer-trigone-vertex-fill" data-triangle="${escapeAttr(triangle.id)}" data-degree="${fmt(normalizeDegrees(triangle.vertices[index]))}" d="${polygonPath([vertex, nextMidpoint, centroid, previousMidpoint])}" fill="${escapeAttr(triangle.vertexFills[index])}" fill-opacity="0.54" stroke="none"/>`);
}
push(`<path class="archeometer-trigone" data-triangle="${escapeAttr(triangle.id)}" d="${polygonPath(vertices)}" fill="none" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.82" stroke-width="${fmt(1.35 * scale)}"/>`);
} else {
push(`<path class="archeometer-trigone" data-triangle="${escapeAttr(triangle.id)}" d="${polygonPath(vertices)}" fill="${escapeAttr(triangle.fill)}" fill-opacity="0.52" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.82" stroke-width="${fmt(1.35 * scale)}"/>`);
}
}
push(`</g>`);
for (const label of sortedByDegree(model.triangleLabels)) {
if (!label.label)
continue;
const a = angleOf(model, label.degree);
const labelPoint = polarToXY(center, ring.r1 + (clipOuter - ring.r1) * 0.56, a);
push(textSvg(label.label, labelPoint, 12 * scale, palette.ink, 0, "middle", 700));
if (label.number !== void 0) {
const numberPoint = polarToXY(center, ring.r1 + (clipOuter - ring.r1) * 0.8, a);
push(textSvg(String(label.number), numberPoint, 7.5 * scale, palette.ink, 0, "middle", 600));
}
}
push(`</g>`);
}
function renderCosmologicalMusic(push, model) {
const { center, rings, palette, scale } = model;
const ring = rings.cosmologicalMusic;
const notes = sortedByDegree(model.musicalNotes);
push(`<g id="archeometer-cosmological-music" aria-label="cosmological musical crown">`);
push(`<path id="archeometer-music-backing" d="${annulusPath(center, ring.r1, ring.r2)}" fill="${escapeAttr(palette.paper)}" stroke="${escapeAttr(palette.ringStroke)}" stroke-opacity="0.58" stroke-width="${fmt(0.8 * scale)}" fill-rule="evenodd"/>`);
for (const note of notes) {
const point = nearestUtterance(note.degree, model.utterance);
const start = angleOf(model, note.degree - 15);
const end = angleOf(model, note.degree + 15);
const sector = annularSectorPath(center, ring.r1, ring.r2, start, end, 30);
push(`<path d="${sector}" fill="${escapeAttr(note.color ?? point?.color ?? palette.ringFills.cosmologicalMusic)}" fill-opacity="0.24" stroke="${escapeAttr(palette.ringStroke)}" stroke-opacity="0.3" stroke-width="${fmt(0.55 * scale)}"/>`);
}
for (const note of notes) {
const start = angleOf(model, note.degree - 5.5);
const end = angleOf(model, note.degree + 5.5);
for (let i = 1; i <= 5; i++) {
const r = ring.r1 + (ring.r2 - ring.r1) * i / 6;
push(`<path class="archeometer-music-staff-line" data-degree="${fmt(normalizeDegrees(note.degree))}" d="${arcSegmentPath(center, r, start, end)}" fill="none" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.58" stroke-width="${fmt(0.48 * scale)}" stroke-linecap="round"/>`);
}
}
for (const note of notes) {
if (!note.note)
continue;
const p = polarToXY(center, (ring.r1 + ring.r2) / 2, angleOf(model, note.degree));
push(textSvg(note.note, p, 11.8 * scale, palette.ink, tangentRotation(angleOf(model, note.degree)), "middle", 700));
}
push(`</g>`);
}
function renderAstralZodiac(push, model) {
const { center, rings, palette, scale } = model;
const ring = rings.astralZodiac;
const ringWidth = ring.r2 - ring.r1;
push(`<g id="archeometer-astral-zodiac" aria-label="astral zodiacal crown">`);
for (const sign of sortedByDegree(model.zodiacSigns)) {
const sector = annularSectorPath(
center,
ring.r1,
ring.r2,
angleOf(model, sign.degree - 15),
angleOf(model, sign.degree + 15),
30
);
const pointColor = triangleVertexFillForDegree(model, sign.degree) ?? sign.color ?? nearestUtterance(sign.degree, model.utterance)?.color ?? palette.ringFills.astralZodiac;
push(
`<path d="${sector}" fill="${escapeAttr(pointColor)}" fill-opacity="0.40" stroke="${escapeAttr(
palette.ringStroke
)}" stroke-opacity="0.32" stroke-width="${fmt(0.6 * scale)}"/>`
);
}
const markerRadius = Math.min(ringWidth * 0.33, 13 * scale);
const glyphSize = 17.5 * scale;
for (const sign of sortedByDegree(model.zodiacSigns)) {
const pointColor = triangleVertexFillForDegree(model, sign.degree) ?? sign.color ?? nearestUtterance(sign.degree, model.utterance)?.color ?? palette.ringFills.astralZodiac;
const p = polarToXY(center, (ring.r1 + ring.r2) / 2, angleOf(model, sign.degree));
push(
`<g class="archeometer-zodiac-sign" data-sign="${escapeAttr(sign.name)}" data-degree="${fmt(
normalizeDegrees(sign.degree)
)}">`
);
push(
`<circle cx="${fmt(p.x)}" cy="${fmt(p.y)}" r="${fmt(markerRadius)}" fill="${escapeAttr(
pointColor
)}" fill-opacity="0.48" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(0.75 * scale)}"/>`
);
push(textSvg(sign.glyph, p, glyphSize, palette.ink, 0, "middle"));
push(`</g>`);
}
push(`</g>`);
}
function renderAstralPlanetary(push, model) {
const { center, rings, palette, scale } = model;
const ring = rings.astralPlanetary;
const ringWidth = ring.r2 - ring.r1;
push(`<g id="archeometer-astral-planetary" aria-label="astral planetary crown">`);
for (const planet of sortedByDegree(model.planetaryPoints)) {
const pointColor = triangleVertexFillForDegree(model, planet.degree) ?? planet.color ?? nearestUtterance(planet.degree, model.utterance)?.color ?? palette.ringFills.astralPlanetary;
const sector = annularSectorPath(
center,
ring.r1,
ring.r2,
angleOf(model, planet.degree - 15),
angleOf(model, planet.degree + 15),
30
);
push(
`<path class="archeometer-astral-planetary-sector" data-degree="${fmt(
normalizeDegrees(planet.degree)
)}" d="${sector}" fill="${escapeAttr(pointColor)}" fill-opacity="0.36" stroke="none"/>`
);
}
for (let degree = 15; degree < 360; degree += 30) {
const line = lineFromPolar(center, ring.r1, ring.r2, angleOf(model, degree));
push(lineSvg(line, palette.ringStroke, 0.6 * scale, 0.42, "archeometer-astral-planetary-divider", ` data-degree="${fmt(degree)}"`));
}
const markerRadius = Math.min(ringWidth * 0.33, 13 * scale);
for (const planet of sortedByDegree(model.planetaryPoints)) {
const p = polarToXY(center, (ring.r1 + ring.r2) / 2, angleOf(model, planet.degree));
const color = triangleVertexFillForDegree(model, planet.degree) ?? planet.color ?? nearestUtterance(planet.degree, model.utterance)?.color ?? palette.ink;
const glyphSize = (planet.name === "Moon" ? 17 : 18.5) * scale;
push(
`<g class="archeometer-planet" data-planet="${escapeAttr(planet.name)}" data-degree="${fmt(
normalizeDegrees(planet.degree)
)}">`
);
push(
`<circle cx="${fmt(p.x)}" cy="${fmt(p.y)}" r="${fmt(markerRadius)}" fill="${escapeAttr(
color
)}" fill-opacity="0.44" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(0.8 * scale)}"/>`
);
push(textSvg(planet.glyph, p, glyphSize, palette.ink, 0, "middle"));
push(`</g>`);
}
push(`</g>`);
}
function renderChromicRays(push, model) {
push(`<g id="archeometer-chromic-rays" aria-label="dodecagonal crown of chromic circum-solar rays">`);
push(`<g clip-path="url(#archeometer-chromic-clip)">`);
renderChromicTriangleCore(push, model);
push(`</g>`);
push(`</g>`);
}
function renderChromicTriangleCore(push, model) {
const { center, rings, palette, scale } = model;
const trianglesById = new Map(model.triangles.map((triangle) => [triangle.id, triangle]));
const wordJesus = trianglesById.get("wordJesus");
const mary = trianglesById.get("mary");
const ether = trianglesById.get("ether");
const divineFire = trianglesById.get("divineFire");
const chromicOuter = chromicCoreOuterRadius(rings);
const chromic = (x, y) => scaleArcheometerReferencePoint(center, chromicOuter, x, y);
const path = (...commands) => commands.join(" ");
const move = (x, y) => {
const p = chromic(x, y);
return `M ${fmt(p.x)} ${fmt(p.y)}`;
};
const line = (x, y) => {
const p = chromic(x, y);
return `L ${fmt(p.x)} ${fmt(p.y)}`;
};
const cubic = (x1, y1, x2, y2, x, y) => {
const c1 = chromic(x1, y1);
const c2 = chromic(x2, y2);
const p = chromic(x, y);
return `C ${fmt(c1.x)} ${fmt(c1.y)} ${fmt(c2.x)} ${fmt(c2.y)} ${fmt(p.x)} ${fmt(p.y)}`;
};
const strokeWidth = fmt(1.4 * scale);
const facetStrokeWidth = fmt(0.85 * scale);
const primaryFacetStrokeWidth = fmt(0.95 * scale);
const primaryStroke = wordJesus?.stroke ?? palette.ink;
push(`<g id="archeometer-chromic-triangle-core" aria-label="inner chromic primary triangle core">`);
push(`<path class="archeometer-chromic-foundation" data-triangle="ether" d="${path(move(1.3999, 137.047), line(204.875, 19.5703), line(204.875, 254.523), "Z")}" fill="${escapeAttr(ether?.fill ?? "#78BD79")}" stroke="${escapeAttr(palette.ink)}" stroke-width="${strokeWidth}"/>`);
push(`<path class="archeometer-chromic-foundation" data-triangle="divineFire" d="${path(move(272.7, 137.047), line(69.2251, 254.523), line(69.2251, 19.5703), "Z")}" fill="${escapeAttr(divineFire?.fill ?? "#CC58A1")}" stroke="${escapeAttr(palette.ink)}" stroke-width="${strokeWidth}"/>`);
push(`<path class="archeometer-chromic-foundation" data-triangle="mary" d="${path(move(137.05, 272.702), line(19.5737, 69.2266), line(254.527, 69.2266), "Z")}" fill="${escapeAttr(mary?.fill ?? "#E25B61")}" stroke="${escapeAttr(palette.ink)}" stroke-width="${strokeWidth}"/>`);
for (const triangle of [ether, divineFire, mary]) {
if (!triangle?.vertexFills)
continue;
const vertices = triangle.vertices.map((degree) => polarToXY(center, chromicOuter, angleOf(model, degree)));
const centroid = polygonCentroid(vertices);
const midpoints = vertices.map((vertex, index) => midpoint(vertex, vertices[(index + 1) % vertices.length]));
for (const [index, vertex] of vertices.entries()) {
const previousMidpoint = midpoints[(index + vertices.length - 1) % vertices.length];
const nextMidpoint = midpoints[index];
push(`<path class="archeometer-chromic-trigone-facet" data-triangle="${escapeAttr(triangle.id)}" data-degree="${fmt(normalizeDegrees(triangle.vertices[index]))}" d="${polygonPath([vertex, nextMidpoint, centroid, previousMidpoint])}" fill="${escapeAttr(triangle.vertexFills[index])}" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.72" stroke-width="${facetStrokeWidth}"/>`);
}
}
push(`<path class="archeometer-chromic-primary-facet" data-triangle="wordJesus" data-degree="120" d="${path(move(137.05, 204.874), line(254.527, 204.874), line(195.789, 103.136), cubic(195.789, 103.136, 215.377, 136.525, 195.789, 170.699), cubic(176.2, 204.874, 137.05, 204.874, 137.05, 204.874), "Z")}" fill="${escapeAttr(wordJesus?.vertexFills?.[1] ?? "#5470A5")}" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.78" stroke-width="${primaryFacetStrokeWidth}"/>`);
push(`<path class="archeometer-chromic-primary-facet" data-triangle="wordJesus" data-degree="240" d="${path(move(19.5737, 204.874), line(137.05, 204.874), cubic(137.05, 204.874, 94.7002, 203.927, 78.312, 170.699), cubic(61.9238, 137.472, 78.312, 103.136, 78.312, 103.136), line(19.5737, 204.874), "Z")}" fill="${escapeAttr(wordJesus?.vertexFills?.[2] ?? "#DD3E38")}" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.78" stroke-width="${primaryFacetStrokeWidth}"/>`);
push(`<path class="archeometer-chromic-primary-facet" data-triangle="wordJesus" data-degree="0" fill-rule="evenodd" clip-rule="evenodd" d="${path(move(195.789, 103.136), line(137.05, 1.39844), line(78.312, 103.136), cubic(78.312, 103.136, 100.9, 71.1992, 137.05, 71.1992), cubic(173.2, 71.1992, 195.789, 103.136, 195.789, 103.136), "Z")}" fill="${escapeAttr(wordJesus?.vertexFills?.[0] ?? "#F2CF45")}" stroke="${escapeAttr(palette.ink)}" stroke-opacity="0.78" stroke-width="${primaryFacetStrokeWidth}"/>`);
push(`<path class="archeometer-chromic-primary-outline" data-triangle="wordJesus" d="${path(
move(137.05, 204.874),
line(254.527, 204.874),
line(195.789, 103.136),
move(137.05, 204.874),
line(19.5737, 204.874),
line(78.312, 103.136),
move(137.05, 204.874),
cubic(137.05, 204.874, 94.7002, 203.927, 78.312, 170.699),
cubic(61.9238, 137.472, 78.312, 103.136, 78.312, 103.136),
move(137.05, 204.874),
cubic(137.05, 204.874, 176.2, 204.874, 195.789, 170.699),
cubic(215.377, 136.525, 195.789, 103.136, 195.789, 103.136),
move(195.789, 103.136),
line(137.05, 1.39844),
line(78.312, 103.136),
move(195.789, 103.136),
cubic(195.789, 103.136, 173.2, 71.1992, 137.05, 71.1992),
cubic(100.9, 71.1992, 78.312, 103.136, 78.312, 103.136)
)}" fill="none" stroke="${escapeAttr(primaryStroke)}" stroke-width="${strokeWidth}"/>`);
push(`</g>`);
}
function renderWhiteRays(push, model) {
const { center, rings, palette, scale } = model;
const solarRadius = rings.solarCenter.r2;
const sunCenterCircleRadiusWithPadding = 3 + 0.3;
push(`<g id="archeometer-white-rays" aria-label="crown of white rays and musical staff">`);
for (let degree = 0; degree < 180; degree += 30) {
if (degree === 90 || degree === 270)
continue;
push(lineSvg(lineFromPolar(center, solarRadius, sunCenterCircleRadiusWithPadding, angleOf(model, degree)), palette.whiteRay, 1.55 * scale, 0.92));
push(lineSvg(lineFromPolar(center, solarRadius, sunCenterCircleRadiusWithPadding, angleOf(model, degree + 180)), palette.whiteRay, 1.55 * scale, 0.92));
}
push(`</g>`);
}
function renderSolarCenter(push, model) {
const { center, rings, palette, scale } = model;
const r = rings.solarCenter.r2;
const innerR = r * 0.68;
const arcStart = polarToXY(center, innerR * 0.72, deg2rad(240));
const arcEnd = polarToXY(center, innerR * 0.72, deg2rad(300));
const paddingTop = 22;
const sunCenterCircleRadius = 3;
push(`<g id="archeometer-solar-center" aria-label="solar center Mi">`);
push(`<circle cx="${fmt(center.x)}" cy="${fmt(center.y)}" r="${fmt(r)}" fill="none" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(1.2 * scale)}"/>`);
push(lineSvg({ x1: center.x - innerR - 13, x2: center.x + innerR + 13, y1: center.y - 1.5, y2: center.y - 1.5 }, palette.whiteRay, 1.55 * scale, 0.92));
push(lineSvg({ x1: center.x - innerR - 13, x2: center.x + innerR + 13, y1: center.y, y2: center.y }, palette.whiteRay, 1.55 * scale, 0.92));
push(lineSvg({ x1: center.x - innerR - 13, x2: center.x + innerR + 13, y1: center.y + 1.5, y2: center.y + 1.5 }, palette.whiteRay, 1.55 * scale, 0.92));
push(`<line x1="${fmt(center.x - innerR - 2.5)}" y1="${fmt(center.y + 8 + paddingTop)}" x2="${fmt(center.x + innerR + 2.5)}" y2="${fmt(center.y + 8 + paddingTop)}" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(1.1 * scale)}"/>`);
push(`<line x1="${fmt(center.x - innerR - 7.5)}" y1="${fmt(center.y + paddingTop)}" x2="${fmt(center.x + innerR + 7.5)}" y2="${fmt(center.y + paddingTop)}" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(1.1 * scale)}"/>`);
push(`<line x1="${fmt(center.x - innerR - 11)}" y1="${fmt(center.y - 8 + paddingTop)}" x2="${fmt(center.x + innerR + 11)}" y2="${fmt(center.y - 8 + paddingTop)}" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(1.1 * scale)}"/>`);
push(`<path d="M ${fmt(arcStart.x)} ${fmt(arcStart.y)} A ${fmt(innerR * 0.72)} ${fmt(innerR * 0.72)} 0 0 1 ${fmt(arcEnd.x)} ${fmt(arcEnd.y)}" fill="none" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(1.1 * scale)}"/>`);
push(`<circle cx="${fmt(center.x)}" cy="${fmt(center.y)}" r="${fmt(sunCenterCircleRadius)}" fill="${escapeAttr(palette.paper)}" stroke="${escapeAttr(palette.ink)}" stroke-width="${fmt(0.5 * scale)}"/>`);
push(`</g>`);
}
function renderFrame(push, model) {
const { center, rings, palette, scale } = model;
const radii = Array.from(
new Map(
[
rings.degreeOuter.r2,
rings.degreeOuter.r1,
rings.degreeInner.r1,
rings.zodiacUtterance.r1,
rings.planetaryUtterance.r1,
rings.cosmologicalMusic.r1,
rings.astralZodiac.r1,
rings.astralPlanetary.r1,
rings.chromicRays.r1,
rings.solarCenter.r2
].map((radius) => [fmt(radius), radius])
).values()
);
push(`<g id="archeometer-frame" aria-hidden="true">`);
for (const radius of radii) {
if (radius <= 0)
continue;
push(
`<circle cx="${fmt(center.x)}" cy="${fmt(center.y)}" r="${fmt(radius)}" fill="none" stroke="${escapeAttr(
palette.ringStroke
)}" stroke-opacity="0.82" stroke-width="${fmt(0.85 * scale)}"/>`
);
}
push(
`<circle cx="${fmt(center.x)}" cy="${fmt(center.y)}" r="${fmt(
rings.degreeOuter.r2
)}" fill="none" stroke="${escapeAttr(palette.ringStroke)}" stroke-width="${fmt(2.2 * scale)}"/>`
);
push(`</g>`);
}
function buildRings(contentRadius, frameRadius = contentRadius) {
const ring = (id2, r1, r2, radius = contentRadius) => ({
id: id2,
r1: round(radius * r1),
r2: round(radius * r2)
});
return {
degreeOuter: ring("degreeOuter", 0.955, 1, frameRadius),
degreeInner: {
id: "degreeInner",
r1: round(contentRadius * 0.955),
r2: round(frameRadius * 0.955)
},
zodiacUtterance: ring("zodiacUtterance", 0.8, 0.955),
planetaryUtterance: ring("planetaryUtterance", 0.461, 0.8),
cosmologicalMusic: ring("cosmologicalMusic", 0.395, 0.461),
astralZodiac: ring("astralZodiac", 0.297, 0.395),
astralPlanetary: ring("astralPlanetary", 0.198, 0.297),
chromicRays: ring("chromicRays", 0.198, 0.395),
whiteRays: ring("whiteRays", 0.12, 0.198),
solarCenter: ring("solarCenter", 0, 0.099)
};
}
function planetaryTriangleClipInnerRadius(rings) {
return rings.planetaryUtterance.r1;
}
function planetaryTriangleClipOuterRadius(rings) {
return rings.planetaryUtterance.r2;
}
function chromicCoreInnerRadius(rings) {
return rings.solarCenter.r2;
}
function chromicCoreOuterRadius(rings) {
return rings.whiteRays.r2;
}
function resolvePalette(palette) {
const base = COLOR_PALETTE;
const overrides = typeof palette === "object" ? palette : {};
return {
paper: overrides.paper ?? base.paper,
ink: overrides.ink ?? base.ink,
subtleInk: overrides.subtleInk ?? base.subtleInk,
ringStroke: overrides.ringStroke ?? base.ringStroke,
degreeTick: overrides.degreeTick ?? base.degreeTick,
degreeLabel: overrides.degreeLabel ?? base.degreeLabel,
whiteRay: overrides.whiteRay ?? base.whiteRay,
ringFills: { ...base.ringFills, ...overrides.ringFills ?? {} }
};
}
function normalizeViewBox(viewBox) {
return {
minX: viewBox?.minX ?? ARCHEOMETER_DEFAULT_VIEWBOX.minX,
minY: viewBox?.minY ?? ARCHEOMETER_DEFAULT_VIEWBOX.minY,
width: viewBox?.width ?? ARCHEOMETER_DEFAULT_VIEWBOX.width,
height: viewBox?.height ?? ARCHEOMETER_DEFAULT_VIEWBOX.height
};
}
function angleOf(model, degree) {
return deg2rad(degree + model.rotationDegrees - 90);
}
function polarToXY(center, radius, angleRad) {
return {
x: round(center.x + Math.cos(angleRad) * radius),
y: round(center.y + Math.sin(angleRad) * radius)
};
}
function scaleArcheometerReferencePoint(center, radius, x, y) {
const referenceCenter = 137.05;
const referenceRadius = 135.652;
const scale = radius / referenceRadius;
return {
x: round(center.x + (x - referenceCenter) * scale),
y: round(center.y + (y - referenceCenter) * scale)
};
}
function lineFromPolar(center, r1, r2, angleRad) {
const p1 = polarToXY(center, r1, angleRad);
const p2 = polarToXY(center, r2, angleRad);
return { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };
}
function lineSvg(line, color, strokeWidth, opacity = 1, className, attributes = "") {
const classAttr = className ? ` class="${escapeAttr(className)}"` : "";
return `<line${classAttr}${attributes} x1="${fmt(line.x1)}" y1="${fmt(line.y1)}" x2="${fmt(line.x2)}" y2="${fmt(line.y2)}" stroke="${escapeAttr(color)}" stroke-opacity="${fmt(opacity)}" stroke-width="${fmt(strokeWidth)}" stroke-linecap="round"/>`;
}
function textSvg(value, p, fontSize, color, rotation = 0, anchor = "middle", weight, className, attributes = "") {
const transform = rotation ? ` transform="rotate(${fmt(rotation)} ${fmt(p.x)} ${fmt(p.y)})"` : "";
const weightAttr = weight ? ` font-weight="${weight}"` : "";
const classAttr = "";
return `<text${classAttr}${attributes} x="${fmt(p.x)}" y="${fmt(p.y)}"${transform} font-family="${textFontFamily()}" font-size="${fmt(fontSize)}" text-anchor="${anchor}" dominant-baseline="middle" fill="${escapeAttr(color)}"${weightAttr}>${escapeText(value)}</text>`;
}
function annulusPath(center, r1, r2) {
return [
`M ${fmt(center.x)} ${fmt(center.y - r2)}`,
`A ${fmt(r2)} ${fmt(r2)} 0 1 1 ${fmt(center.x)} ${fmt(center.y + r2)}`,
`A ${fmt(r2)} ${fmt(r2)} 0 1 1 ${fmt(center.x)} ${fmt(center.y - r2)}`,
`M ${fmt(center.x)} ${fmt(center.y - r1)}`,
`A ${fmt(r1)} ${fmt(r1)} 0 1 0 ${fmt(center.x)} ${fmt(center.y + r1)}`,
`A ${fmt(r1)} ${fmt(r1)} 0 1 0 ${fmt(center.x)} ${fmt(center.y - r1)}`,
`Z`
].join(" ");
}
function annularSectorPath(center, r1, r2, startAngle, endAngle, spanDegrees) {
const outerStart = polarToXY(center, r2, startAngle);
const outerEnd = polarToXY(center, r2, endAngle);
const innerEnd = polarToXY(center, r1, endAngle);
const innerStart = polarToXY(center, r1, startAngle);
const largeArc = Math.abs(spanDegrees) > 180 ? 1 : 0;
const sweep = 1 ;
const inverseSweep = 0 ;
return [
`M ${fmt(outerStart.x)} ${fmt(outerStart.y)}`,
`A ${fmt(r2)} ${fmt(r2)} 0 ${largeArc} ${sweep} ${fmt(outerEnd.x)} ${fmt(outerEnd.y)}`,
`L ${fmt(innerEnd.x)} ${fmt(innerEnd.y)}`,
`A ${fmt(r1)} ${fmt(r1)} 0 ${largeArc} ${inverseSweep} ${fmt(innerStart.x)} ${fmt(innerStart.y)}`,
`Z`
].join(" ");
}
function arcSegmentPath(center, radius, startAngle, endAngle) {
const start = polarToXY(center, radius, startAngle);
const end = polarToXY(center, radius, endAngle);
return `M ${fmt(start.x)} ${fmt(start.y)} A ${fmt(radius)} ${fmt(radius)} 0 0 1 ${fmt(end.x)} ${fmt(end.y)}`;
}
function polygonPath(points) {
if (points.length === 0)
return "";
const [first, ...rest] = points;
return [`M ${fmt(first.x)} ${fmt(first.y)}`, ...rest.map((p) => `L ${fmt(p.x)} ${fmt(p.y)}`), "Z"].join(" ");
}
function midpoint(a, b) {
return { x: round((a.x + b.x) / 2), y: round((a.y + b.y) / 2) };
}
function polygonCentroid(points) {
const total = points.reduce(
(sum, point) => ({ x: sum.x + point.x, y: sum.y + point.y }),
{ x: 0, y: 0 }
);
return { x: round(total.x / points.length), y: round(total.y / points.length) };
}
function sortedByDegree(items) {
return [...items].sort((a, b) => normalizeDegrees(a.degree) - normalizeDegrees(b.degree));
}
function nearestUtterance(degree, utterance) {
let best;
let bestDelta = Number.POSITIVE_INFINITY;
for (const point of utterance) {
const delta = Math.abs(shortestAngularDistance(degree, point.degree));
if (delta < bestDelta) {
best = point;
bestDelta = delta;
}
}
return best;
}
function triangleVertexFillForDegree(model, degree) {
for (const triangle of model.triangles) {
if (!triangle.vertexFills)
continue;
for (const [index, vertexDegree] of triangle.vertices.entries()) {
if (Math.abs(shortestAngularDistance(degree, vertexDegree)) < 1e-3) {
return triangle.vertexFills[index];
}
}
}
return void 0;
}
function tangentRotation(angleRad) {
let degrees = rad2deg(angleRad) + 90;
degrees = (degrees % 360 + 360) % 360;
if (degrees > 90 && degrees < 270)
degrees += 180;
return (degrees % 360 + 360) % 360;
}
function shortestAngularDistance(a, b) {
return (normalizeDegrees(a) - normalizeDegrees(b) + 540) % 360 - 180;
}
function normalizeDegrees(value) {
return (value % 360 + 360) % 360;
}
function deg2rad(degrees) {
return degrees * (TAU / 360);
}
function rad2deg(radians) {
return radians * (360 / TAU);
}
function round(value) {
return Math.round(value * 1e3) / 1e3;
}
function fmt(value) {
if (!Number.isFinite(value))
return "0";
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
}
function escapeAttr(value) {
return escapeText(value).replace(/"/g, """);
}
function escapeText(value) {
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
function textFontFamily() {
return "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans Symbols 2', 'Noto Sans Symbols', 'Segoe UI Symbol', 'Apple Symbols', sans-serif";
}
// src/visual/astro-glyph-assets.ts
var rawGlyph = (markup) => [
{ kind: "raw", markup }
];
var raw100 = (markup) => rawGlyph(`<g transform="scale(0.01)">${markup}</g>`);
var GLYPH_FILL = "var(--astro-glyph-fill, #fff)";
var rawPlanet = (markup) => rawGlyph(
`<g transform="scale(0.01)" stroke="currentColor" stroke-width="7" stroke-linecap="round" stroke-linejoin="round">${markup}</g>`
);
var SOURCE_POINT_TEXT_GLYPH = (text) => rawGlyph(
`<g transform="scale(0.01)"><text x="0" y="0" text-anchor="middle" dominant-baseline="central" font-family="Arial Narrow, Liberation Sans Narrow, Arial, Helvetica, sans-serif" font-size="90" font-weight="500" fill="currentColor" stroke="none">${text}</text></g>`
);
var SOURCE_ZODIAC_GLYPH_PRIMITIVES = {
"Aries": [{ kind: "raw", markup: '<g transform="scale(0.0172864) translate(-66.624999 -71.429581)"><path d="M 64.28125,95.179581 L 64.28125,91.398331 C 64.281226,88.419171 63.635393,83.106676 62.34375,75.460831 C 61.760395,71.856687 60.802062,68.106691 59.46875,64.210831 C 58.114565,60.252532 56.6979,57.148369 55.21875,54.898331 C 54.072902,53.190039 52.70832,52.335874 51.125,52.335831 C 49.333324,52.335874 48.083325,53.023373 47.375,54.398331 C 46.72916,55.669204 46.406243,57.054619 46.40625,58.554581 C 46.406243,61.721281 47.541659,64.585861 49.8125,67.148331 L 44.5,67.148331 C 42.666664,64.335862 41.749998,61.367115 41.75,58.242081 C 41.749998,54.971288 42.624997,52.40879 44.375,50.554581 C 46.187494,48.637961 48.385408,47.679628 50.96875,47.679581 C 54.302069,47.679628 56.906233,49.054627 58.78125,51.804581 C 60.906229,54.908788 62.67706,58.679617 64.09375,63.117081 C 65.093725,66.346276 65.937474,69.919189 66.625,73.835831 C 67.312472,69.919189 68.156222,66.346276 69.15625,63.117081 C 70.489553,58.783784 72.260384,55.012954 74.46875,51.804581 C 76.343713,49.054627 78.947878,47.679628 82.28125,47.679581 C 84.864538,47.679628 87.062453,48.637961 88.875,50.554581 C 90.624949,52.40879 91.499948,54.971288 91.5,58.242081 C 91.499948,61.367115 90.583283,64.335862 88.75,67.148331 L 83.4375,67.148331 C 85.708287,64.585861 86.843703,61.721281 86.84375,58.554581 C 86.843703,57.054619 86.520787,55.669204 85.875,54.398331 C 85.166621,53.023373 83.916623,52.335874 82.125,52.335831 C 80.541626,52.335874 79.177044,53.190039 78.03125,54.898331 C 76.552047,57.148369 75.135381,60.252532 73.78125,64.210831 C 72.447884,68.106691 71.489552,71.856687 70.90625,75.460831 C 69.614554,83.106676 68.968721,88.419171 68.96875,91.398331 L 68.96875,95.179581 L 64.28125,95.179581" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round"/></g>' }],
"Taurus": [{ kind: "raw", markup: '<g transform="scale(0.0175287) translate(-155.375000 -71.429581)"><path d="M 140.9375,59.023331 C 140.37499,57.690034 139.57291,56.502535 138.53125,55.460831 C 137.57291,54.502537 136.39583,53.710871 135,53.085831 C 133.79166,52.544206 132.40625,52.273373 130.84375,52.273331 L 130.84375,47.585831 C 132.98958,47.585877 134.96874,47.971294 136.78125,48.742081 C 138.67707,49.554626 140.36457,50.690041 141.84375,52.148331 C 143.34374,53.627538 144.48957,55.325453 145.28125,57.242081 C 145.84373,58.57545 146.64582,59.762949 147.6875,60.804581 C 148.64581,61.762947 149.8229,62.554613 151.21875,63.179581 C 152.42706,63.721278 153.81247,63.992111 155.375,63.992081 C 156.87497,63.992111 158.26039,63.721278 159.53125,63.179581 C 160.86455,62.617112 162.04163,61.825447 163.0625,60.804581 C 164.10413,59.762949 164.90621,58.57545 165.46875,57.242081 C 166.26038,55.325453 167.40621,53.627538 168.90625,52.148331 C 170.38537,50.690041 172.07287,49.554626 173.96875,48.742081 C 175.7812,47.971294 177.76037,47.585877 179.90625,47.585831 L 179.90625,52.273331 C 178.3437,52.273373 176.95828,52.544206 175.75,53.085831 C 174.35412,53.710871 173.17704,54.502537 172.21875,55.460831 C 171.17704,56.502535 170.37496,57.690034 169.8125,59.023331 C 169.02079,60.919197 167.87496,62.617112 166.375,64.117081 C 165.6458,64.846277 164.69788,65.585859 163.53125,66.335831 C 164.69788,67.085858 165.6458,67.825441 166.375,68.554581 C 167.87496,70.054605 169.02079,71.75252 169.8125,73.648331 C 170.58329,75.52335 170.96871,77.533764 170.96875,79.679581 C 170.96871,81.762927 170.58329,83.742091 169.8125,85.617081 C 168.99996,87.533754 167.85413,89.231669 166.375,90.710831 C 164.8958,92.169166 163.2083,93.304582 161.3125,94.117081 C 159.49997,94.887914 157.5208,95.27333 155.375,95.273331 C 153.12498,95.27333 151.14581,94.887914 149.4375,94.117081 C 147.45831,93.221249 145.77082,92.085833 144.375,90.710831 C 142.89582,89.231669 141.74999,87.533754 140.9375,85.617081 C 140.16666,83.742091 139.78124,81.762927 139.78125,79.679581 C 139.78124,77.533764 140.16666,75.52335 140.9375,73.648331 C 141.77082,71.62752 142.91665,69.929605 144.375,68.554581 C 145.27082,67.700441 146.22915,66.960858 147.25,66.335831 C 146.20832,65.710859 145.24998,64.971277 144.375,64.117081 C 142.91665,62.700446 141.77082,61.002531 140.9375,59.023331 M 159.53125,69.492081 C 158.26039,68.950439 156.87497,68.679606 155.375,68.679581 C 153.87497,68.679606 152.48956,68.950439 151.21875,69.492081 C 149.92706,70.075438 148.74998,70.867104 147.6875,71.867081 C 146.60415,72.992102 145.80207,74.179601 145.28125,75.429581 C 144.73957,76.742098 144.46873,78.158764 144.46875,79.679581 C 144.46873,81.137927 144.73957,82.523343 145.28125,83.835831 C 145.80207,85.08584 146.60415,86.273339 147.6875,87.398331 C 148.74998,88.398337 149.92706,89.190003 151.21875,89.773331 C 152.48956,90.315001 153.87497,90.585834 155.375,90.585831 C 156.87497,90.585834 158.26039,90.315001 159.53125,89.773331 C 160.82288,89.190003 161.99997,88.398337 163.0625,87.398331 C 164.1458,86.273339 164.94788,85.08584 165.46875,83.835831 C 166.01038,82.523343 166.28121,81.137927 166.28125,79.679581 C 166.28121,78.158764 166.01038,76.742098 165.46875,75.429581 C 164.94788,74.179601 164.1458,72.992102 163.0625,71.867081 C 161.99997,70.867104 160.82288,70.075438 159.53125,69.492081" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round"/></g>' }],
"Gemini": [{ kind: "raw", markup: '<g transform="scale(0.0177663) translate(-244.125000 -71.429581)"><path d="M 257.25,89.007706 C 260.72912,89.486878 264.12495,90.143127 267.4375,90.976456 L 267.4375,95.632706 C 260.06246,93.77854 252.29163,92.851458 244.125,92.851456 C 235.95831,92.851458 228.18749,93.77854 220.8125,95.632706 L 220.8125,90.976456 C 224.12499,90.143127 227.52082,89.486878 231,89.007706 L 231,53.851456 C 227.52082,53.37233 224.12499,52.716081 220.8125,51.882706 L 220.8125,47.226456 C 228.18749,49.080668 235.95831,50.00775 244.125,50.007706 C 252.29163,50.00775 260.06246,49.080668 267.4375,47.226456 L 267.4375,51.882706 C 264.12495,52.716081 260.72912,53.37233 257.25,53.851456 L 257.25,89.007706 M 252.5625,54.351456 C 249.79163,54.580663 246.97914,54.695246 244.125,54.695206 C 241.27081,54.695246 238.45831,54.580663 235.6875,54.351456 L 235.6875,88.507706 C 238.45831,88.278545 241.27081,88.163962 244.125,88.163956 C 246.97914,88.163962 249.79163,88.278545 252.5625,88.507706 L 252.5625,54.351456" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round"/></g>' }],
"Cancer": [{ kind: "raw", markup: '<g transform="scale(0.0190187) translate(-332.875000 -71.429584)"><path d="M 310.26562,78.913956 C 312.16145,80.122304 313.98437,81.101469 315.73438,81.851456 C 320.19269,83.788967 325.14061,84.757716 330.57812,84.757706 C 333.99476,84.757716 337.23434,84.372299 340.29688,83.601456 C 340.02601,83.3723 339.77601,83.143134 339.54688,82.913956 C 338.60934,81.955635 337.92184,80.934803 337.48438,79.851456 C 337.02601,78.726472 336.79684,77.528556 336.79688,76.257706 C 336.79684,75.007725 337.02601,73.820227 337.48438,72.695206 C 337.96351,71.507729 338.65101,70.486897 339.54688,69.632706 C 340.52601,68.695232 341.53642,68.007732 342.57812,67.570206 C 343.66142,67.1119 344.84892,66.882734 346.14062,66.882706 C 347.49475,66.882734 348.68225,67.1119 349.70312,67.570206 C 350.91141,68.111899 351.92183,68.799398 352.73438,69.632706 C 353.67183,70.591063 354.35933,71.611896 354.79688,72.695206 C 355.25516,73.820227 355.48433,75.007725 355.48438,76.257706 C 355.48433,77.507723 355.25516,78.705638 354.79688,79.851456 C 354.29683,81.101469 353.50516,82.174385 352.42188,83.070206 C 350.901,84.320216 349.16142,85.361882 347.20312,86.195206 C 342.11