/** * 星盘渲染 — 南印度 + 北印度风格 v3.0 * 浅色极简主题,支持南/北印度风格切换 */ import { SIGNS, SIGNS_CN, SIGNS_EN_SHORT, PLANET_SYMBOLS, PLANET_CN } from './jyotish-engine.js'; import { signName, getLang } from './i18n.js'; // 南印度星盘宫位映射(星座固定位置) const SOUTH_INDIAN_LAYOUT = [ [0,0],[0,1],[0,2],[0,3], // Pisces, Aries, Taurus, Gemini [1,3],[2,3],[3,3], // Cancer, Leo, Virgo [3,2],[3,1],[3,0], // Libra, Scorpio, Sagittarius [2,0],[1,0], // Capricorn, Aquarius ]; // 北印度星盘宫位布局(固定上升在顶部菱形) // 12宫按固定位置排列,上升始终在顶部菱形格 const NORTH_INDIAN_HOUSES = [ // [row, col] — 从12宫开始,顺时针: 12,1,2,3,4,5,6,7,8,9,10,11 // 顶行: 12, 1, 2 // 右侧下行: 3, 4 // 底行: 11, 10, 9, 8(中心偏), 7 // 左侧上行: 6, 5 [0,0],[0,1],[0,2], // H12, H1, H2 [1,2],[2,3], // H3, H4 [3,3],[3,2],[3,1],[3,0], // H5, H6, H7, H8 [2,0],[1,0], // H9, H10 [1,1], // H11 (center-ish) ]; // 浅色主题色值 const THEME = { bg: '#ffffff', bgAsc: 'rgba(109,40,217,0.04)', border: '#c9c8c8', borderAsc: 'rgba(109,40,217,0.35)', signText: '#6b6b6b', signTextAsc: '#6d28d9', houseNum: '#8c8c8c', planetDefault: '#2e2e2e', planetExalted: '#16a34a', planetDebilitated: '#dc2626', planetOwn: '#d97706', ascLabel: '#6d28d9', combust: '#dc2626', }; /** * 渲染南印度风格星盘 */ export function renderSouthIndianChart(container, chartData, options = {}) { const { planets, ascendant } = chartData; const ascIdx = SIGNS.indexOf(ascendant.sign); const size = options.size || 420; const title = options.title || ''; // 按宫位分组行星 const housePlanets = {}; for (const [pname, pinfo] of Object.entries(planets)) { if (pinfo.error) continue; const h = pinfo.house; if (!housePlanets[h]) housePlanets[h] = []; housePlanets[h].push({ name: pname, ...pinfo }); } const cellSize = size / 4; let svg = ''; if (title) { svg += `
${title}
`; } svg += ``; // 背景 svg += ``; // 绘制12个宫格 for (let signIdx = 0; signIdx < 12; signIdx++) { const [row, col] = SOUTH_INDIAN_LAYOUT[signIdx]; const x = col * cellSize; const y = row * cellSize; const signName = SIGNS[signIdx]; const isAscSign = signIdx === ascIdx; // 宫格背景 const bgColor = isAscSign ? THEME.bgAsc : THEME.bg; svg += ``; // 宫格边框 const borderColor = isAscSign ? THEME.borderAsc : THEME.border; svg += ``; // 星座名(小字,左上角) const signColor = isAscSign ? THEME.signTextAsc : THEME.signText; svg += `${SIGNS_EN_SHORT[signName]}`; // 宫位编号 const houseNum = ((signIdx - ascIdx + 12) % 12) + 1; svg += `${houseNum}`; // 该宫的行星 const pInHouse = housePlanets[houseNum] || []; let py = y + 28; for (const p of pInHouse) { const symbol = PLANET_SYMBOLS[p.name] || ''; let pColor = THEME.planetDefault; if (p.status === '入旺') pColor = THEME.planetExalted; else if (p.status === '落陷') pColor = THEME.planetDebilitated; else if (p.status === '入庙') pColor = THEME.planetOwn; const retroMark = p.retrograde ? ' ℞' : ''; const combustMark = p.combust ? ' C' : ''; const degStr = (p.degree_in_sign != null) ? p.degree_in_sign.toFixed(1) + '°' : ''; svg += `${symbol}${degStr}${retroMark}${combustMark}`; py += 16; } // 上升标记 if (isAscSign) { svg += `ASC`; } } // ===== 中心区域 — 核心摘要 ===== const cx = size / 2, cy0 = cellSize, cw = cellSize * 2; const summary = options.summary || null; // 背景框 svg += ``; svg += ``; // Lagna 上升星座 + 度数 const ascSignSymbol = {Aries:'♈',Taurus:'♉',Gemini:'♊',Cancer:'♋',Leo:'♌',Virgo:'♍',Libra:'♎',Scorpio:'♏',Sagittarius:'♐',Capricorn:'♑',Aquarius:'♒',Pisces:'♓'}[ascendant.sign] || ''; svg += `${ascSignSymbol} ${signName(ascendant.sign)} ${(ascendant.degree_in_sign != null ? ascendant.degree_in_sign : ascendant.degree % 30).toFixed(2)}°`; // 月亮星宿 if (summary?.moonNakshatra) { svg += `☽ ${summary.moonNakshatra}`; } // 分隔线 svg += ``; // 当前 Dasha if (summary?.dasha) { const dColor = THEME.planetDefault; svg += `Dasha`; svg += `${summary.dasha.maha}/${summary.dasha.antar}`; svg += `${summary.dasha.period}`; } // AK / DK if (summary?.karaka) { const parts = []; if (summary.karaka.AK) parts.push(`AK:${summary.karaka.AK}`); if (summary.karaka.DK) parts.push(`DK:${summary.karaka.DK}`); if (parts.length) { svg += `${parts.join(' · ')}`; } } // 关键 Yoga(最多3个) if (summary?.topYogas && summary.topYogas.length > 0) { let ty = cy0 + 112; for (const y of summary.topYogas.slice(0, 3)) { const yColor = y.negative ? '#dc2626' : '#16a34a'; svg += `◆ ${y.name_cn}`; ty += 12; } } svg += ''; container.innerHTML = svg; } /** * 渲染北印度风格星盘(Diamond 菱形布局) * 北印盘固定12宫位置,上升始终在顶部中心格 */ export function renderNorthIndianChart(container, chartData, options = {}) { const { planets, ascendant } = chartData; const ascIdx = SIGNS.indexOf(ascendant.sign); const size = options.size || 420; const title = options.title || ''; // 按宫位分组行星 const housePlanets = {}; for (const [pname, pinfo] of Object.entries(planets)) { if (pinfo.error) continue; const h = pinfo.house; if (!housePlanets[h]) housePlanets[h] = []; housePlanets[h].push({ name: pname, ...pinfo }); } // 北印盘布局: 4x4 网格,外圈12格 = 12宫 // 位置定义 (row, col) — 按宫位编号 1-12 const houseCells = { 1: [0,1], 2: [0,2], // 顶行右半 3: [1,3], 4: [2,3], // 右列下半 5: [3,2], 6: [3,1], // 底行左半 7: [2,0], 8: [1,0], // 左列上半 9: [1,1], 10: [1,2], // 内层上 11: [2,2], 12: [2,1], // 内层下 }; const cellSize = size / 4; let svg = ''; if (title) { svg += `
${title}
`; } svg += ``; svg += ``; // 绘制菱形外框线 const cx = size / 2, cy = size / 2, r = size * 0.47; svg += ``; // 内层菱形 const ri = r * 0.5; svg += ``; // 对角线 svg += ``; svg += ``; svg += ``; svg += ``; // 各宫位 for (let house = 1; house <= 12; house++) { const [row, col] = houseCells[house]; const x = col * cellSize; const y = row * cellSize; const isAsc = house === 1; // 计算该宫的星座 const signIdx = (ascIdx + house - 1) % 12; const signName = SIGNS[signIdx]; // 背景高亮上升宫 if (isAsc) { svg += ``; } // 星座名 const signColor = isAsc ? THEME.signTextAsc : THEME.signText; svg += `${SIGNS_EN_SHORT[signName]}`; // 宫位编号 svg += `H${house}`; // 行星 const pInHouse = housePlanets[house] || []; let py = y + 26; for (const p of pInHouse) { const symbol = PLANET_SYMBOLS[p.name] || ''; let pColor = THEME.planetDefault; if (p.status === '入旺') pColor = THEME.planetExalted; else if (p.status === '落陷') pColor = THEME.planetDebilitated; else if (p.status === '入庙') pColor = THEME.planetOwn; const retroMark = p.retrograde ? ' ℞' : ''; const degStr = (p.degree_in_sign != null) ? p.degree_in_sign.toFixed(1) + '°' : ''; svg += `${symbol}${degStr}${retroMark}`; py += 14; } // 上升标记 if (isAsc) { svg += `ASC`; } } svg += ''; container.innerHTML = svg; } // 全局默认渲染函数(可切换风格) let _chartStyle = 'south'; export function setChartStyle(style) { _chartStyle = style; } export function getChartStyle() { return _chartStyle; } export function renderChart(container, chartData, options = {}) { if (_chartStyle === 'north') { renderNorthIndianChart(container, chartData, options); } else { renderSouthIndianChart(container, chartData, options); } }