/** * Jyotish App — 高级渲染函数 v1.0 * Karaka, Arudha, Vargas, Ashtakavarga, Shadbala, Tithi, Dasha三级 */ import { SIGNS, SIGNS_CN, PLANET_CN, PLANET_SYMBOLS } from './jyotish-engine.js'; import { VARGA_DEFS } from './jyotish-advanced.js'; import { renderSouthIndianChart } from './chart-renderer.js'; import { t, signName, planetName, statusName, houseLabel, yearsLabel } from './i18n.js'; import { DASHA_THEMES, NAKSHATRA_DATA, PLANET_DIGNITY, ASCENDANT_TABLE } from './interpretation.js'; const $ = id => document.getElementById(id); // ========== Varga 分盘星座计算(与 jyotish-advanced.js 同逻辑) ========== function vargaSignIdx(degInSign, si, d) { const sw = 30 / d, pi = Math.floor(degInSign / sw); const o = si % 2 === 0; switch (d) { case 2: return pi === 0 ? (o ? 4 : 3) : (o ? 3 : 4); case 3: return o ? (si + pi * 4) % 12 : (si + 8 + pi * 4) % 12; case 4: return o ? (si + pi) % 12 : (si + 8 + pi) % 12; case 7: return o ? (si + pi) % 12 : (si + 6 + pi) % 12; case 9: { const el = [0,9,6,3]; return (el[si % 4] + pi) % 12; } case 10: return o ? (si + pi) % 12 : (si + 8 + pi) % 12; case 12: return (si + pi) % 12; case 16: return ((o ? 0 : 1) + pi) % 12; case 20: return ((o ? 0 : 8) + pi) % 12; case 24: return ((o ? 4 : 3) + pi) % 12; case 27: return ((o ? 0 : 6) + pi) % 12; case 30: { if (o) { return pi<5?0:pi<10?10:pi<18?8:pi<25?2:6; } else { return pi<5?1:pi<12?5:pi<20?9:pi<25?7:11; } } case 40: return ((o ? 0 : 6) + pi) % 12; case 45: return ((o ? 0 : 6) + pi) % 12; case 60: return o ? (si + pi) % 12 : (si + 1 + pi) % 12; default: return (si + pi) % 12; } } // ========== Tithi / Yoga 信息 ========== export function renderTithiInfo(ty) { const el = $('tithi-info-section'); if (!ty) { el.innerHTML = ''; return; } el.innerHTML = `

${t('panch.title')}

Vara 星期
${ty.vara || '-'}
Tithi 月相
${ty.tithi.name}
Paksha 半月
${ty.tithi.paksha}
Karana 半月相
${ty.karana?.name || '-'}
Yoga 日月瑜伽
${ty.yoga.name}
`; } // ========== Arudha 快速概览(本命盘Tab底部) ========== export function renderArudhaQuick(arudha) { const el = $('arudha-quick-section'); if (!arudha || Object.keys(arudha).length === 0) { el.innerHTML = ''; return; } const items = Object.entries(arudha).map(([k, v]) => `
${k}${v.label ? ' ' + v.label : ''}
${signName(v.sign)} ${houseLabel(v.pada_house)}
` ).join(''); el.innerHTML = `

${t('arudha.overview')}

${items}
`; } // ========== Karaka ========== export function renderKaraka(karaka) { renderKarakaGrid($('karaka7-grid'), karaka.karaka7); renderKarakaGrid($('karaka8-grid'), karaka.karaka8); } function renderKarakaGrid(container, data) { if (!container || !data) return; const labels = { AK:'AK 自我灵魂', AmK:'AmK 事业顾问', BK:'BK 兄弟姐妹', MK:'MK 母亲', PK:'PK 子女', GK:'GK 障碍冲突', DK:'DK 配偶', PiK:'PiK 父亲' }; container.innerHTML = Object.entries(data).map(([k, v]) => { const isDK = k === 'DK'; return `
${labels[k] || k}
${PLANET_SYMBOLS[v.planet] || ''} ${planetName(v.planet)}
${v.degree.toFixed(2)}° · ${signName(v.sign)} H${v.house}
`; }).join(''); } // ========== Vargas 分盘 ========== export function renderVargas(allV, planets, ascendant) { const selector = $('varga-selector'); const order = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu']; // 渲染选择器 selector.innerHTML = VARGA_DEFS.map(v => `` ).join(''); selector.querySelectorAll('.varga-btn').forEach(btn => { btn.addEventListener('click', () => { selector.querySelectorAll('.varga-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); renderSingleVarga(btn.dataset.varga, planets, ascendant); }); }); renderSingleVarga('D9', planets, ascendant); } function renderSingleVarga(vargaId, planets, ascendant) { const def = VARGA_DEFS.find(v => v.id === vargaId); if (!def) return; const d = def.d; // 计算分盘上升 const ascSi = SIGNS.indexOf(ascendant.sign); const ascDegInSign = ascendant.degree - ascSi * 30; const vargaAscIdx = d === 1 ? ascSi : vargaSignIdx(ascDegInSign, ascSi, d); const vargaAscSign = SIGNS[vargaAscIdx]; // 构建分盘星盘数据 const vPlanets = {}; for (const [pn, pi] of Object.entries(planets)) { if (pi.error) continue; if (d === 1) { vPlanets[pn] = { ...pi }; continue; } const psi = SIGNS.indexOf(pi.sign); const vi = vargaSignIdx(pi.degree_in_sign, psi, d); const vHouse = ((vi - vargaAscIdx + 12) % 12) + 1; vPlanets[pn] = { sign: SIGNS[vi], sign_cn: SIGNS_CN[SIGNS[vi]], house: vHouse, degree: 0, degree_in_sign: 0, status: '中性', retrograde: false, nakshatra: '', nakshatra_pada: 1, combust: false, }; } const chartData = { ascendant: { sign: vargaAscSign }, planets: vPlanets }; renderSouthIndianChart($('varga-chart'), chartData); // 表格 $('varga-table-title').textContent = `${def.id} ${def.cn} — ${t('varga.positions')}`; const tbody = $('varga-table').querySelector('tbody'); const order = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu']; tbody.innerHTML = order.filter(pn => vPlanets[pn]).map(pn => { const vp = vPlanets[pn]; return `${PLANET_SYMBOLS[pn]||''}${planetName(pn)}${signName(vp.sign)}H${vp.house}`; }).join(''); } // ========== Arudha 详细(分盘Tab底部) ========== export function renderArudha(arudha) { const el = $('arudha-section'); if (!arudha || Object.keys(arudha).length === 0) { el.innerHTML = ''; return; } const items = Object.entries(arudha).map(([k, v]) => `
${k}${v.label ? ' · ' + v.label : ''}
${signName(v.sign)}
${houseLabel(v.pada_house)} · ${t('house.lord')}: ${planetName(v.lord)}
` ).join(''); el.innerHTML = `
${t('arudha.title')}
${items}
`; } // ========== Ashtakavarga ========== export function renderAshtakavarga(av, ascSign) { // 概览 $('av-summary').innerHTML = `
${t('av.savtotal')}: ${av.sav_total} / 337
`; // SAV 图表(南印度风格格子) renderSAVChart(av, ascSign); // 详细卡片 const details = $('av-details'); const pOrder = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn']; details.innerHTML = ''; for (let h = 1; h <= 12; h++) { const sh = av.sav_by_house[h]; if (!sh) continue; const score = sh.score; const pct = Math.round(score / 56 * 100); const bavRow = pOrder.map(pn => { const has = av.bav_by_house[h]?.[pn] ? 'contrib' : ''; return ``; }).join(''); details.innerHTML += `
${houseLabel(h)} · ${sh.sign}
${score}
${bavRow}
`; } } function renderSAVChart(av, ascSign) { const container = $('av-chart'); const ai = SIGNS.indexOf(ascSign); // 南印度布局位置 const layout = [ [0,0],[0,1],[0,2],[0,3],[1,3],[2,3],[3,3],[3,2],[3,1],[3,0],[2,0],[1,0] ]; const size = 400, cell = size / 4; let svg = ``; // 画格子 for (let i = 0; i < 12; i++) { const [r, c] = layout[i]; const signIdx = i; // South Indian: Pisces=0, Aries=1, ... const house = ((signIdx - ai + 12) % 12) + 1; const score = av.sav_by_house[house]?.score || 0; const color = score >= 30 ? 'rgba(22,163,74,0.15)' : score >= 25 ? 'rgba(109,40,217,0.08)' : score < 20 ? 'rgba(220,38,38,0.1)' : '#fff'; const x = c * cell, y = r * cell; svg += ``; svg += `${SIGNS[signIdx].slice(0,3)}`; svg += `${score}`; svg += `H${house}`; } svg += ''; container.innerHTML = svg; } // ========== Shadbala ========== export function renderShadbala(sb) { const tbody = $('shadbala-table').querySelector('tbody'); tbody.innerHTML = ''; for (const pn of ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn']) { const s = sb[pn]; if (!s) continue; const pct = s.percentage; const barClass = pct >= 100 ? 'strong' : pct >= 75 ? 'medium' : 'weak'; tbody.innerHTML += ` ${PLANET_SYMBOLS[pn]||''}${planetName(pn)} ${s.total} R ${s.required} R
${pct}%
${s.status} `; } } // ========== Dasha 三级 ========== export function renderDasha3Level(data) { const container = $('dasha-timeline'); const currentEl = $('dasha-current'); container.innerHTML = ''; if (data.current_dasha) { const cd = data.current_dasha; const curSub = cd.antardasha?.find(a => a.is_current); const curPraty = curSub?.pratyantardasha?.find(p => p.is_current); const theme = DASHA_THEMES[cd.lord]; const themeHtml = theme ? `
${theme.theme}
✦ ${theme.positive}⚠ ${theme.negative}
` : ''; currentEl.innerHTML = `
${t('dasha.maha')}
${planetName(cd.lord)}
${t('dasha.antar')}
${curSub ? planetName(curSub.lord) : '-'}
${t('dasha.praty')}
${curPraty ? planetName(curPraty.lord) : '-'}
${cd.start} ~ ${cd.end}
${themeHtml} `; } for (const d of data.timeline) { const isCur = data.current_dasha && data.current_dasha.lord === d.lord; const bar = document.createElement('div'); bar.className = `dasha-bar${isCur ? ' active' : ''}`; bar.innerHTML = `
${planetName(d.lord)}
${yearsLabel(d.years)}
${d.start.slice(2)}
`; bar.addEventListener('click', () => { container.querySelectorAll('.dasha-bar').forEach(b => b.classList.remove('selected')); bar.classList.add('selected'); renderAntardashaPanel(d.antardasha || []); }); container.appendChild(bar); } if (data.current_dasha?.antardasha) { renderAntardashaPanel(data.current_dasha.antardasha); } } function renderAntardashaPanel(subs) { const panel = $('antardasha-panel'); panel.innerHTML = ''; for (const ad of subs) { const div = document.createElement('div'); div.className = `antardasha-item${ad.is_current ? ' current' : ''}`; div.innerHTML = `
${planetName(ad.lord)}${ad.is_current ? ' ◀' : ''}
${ad.start.slice(5)}~${ad.end.slice(5)}
`; div.addEventListener('click', () => { panel.querySelectorAll('.antardasha-item').forEach(i => i.classList.remove('selected')); div.classList.add('selected'); renderPratyantardasha(ad.pratyantardasha || []); }); panel.appendChild(div); } // 自动展开当前次运的三运 const cur = subs.find(s => s.is_current); if (cur?.pratyantardasha) renderPratyantardasha(cur.pratyantardasha); } function renderPratyantardasha(pratyList) { const panel = $('pratyantardasha-panel'); panel.innerHTML = `

${t('dasha.praty')}

`; const grid = panel.querySelector('.praty-grid'); for (const p of pratyList) { grid.innerHTML += `
${planetName(p.lord)}${p.is_current ? ' ◀' : ''}
${p.start.slice(5)}~${p.end.slice(5)}
`; } } // ========== 行星表更新(含D9列) ========== export function updatePlanetsTable(planets, d9Varga) { const tbody = $('planets-table').querySelector('tbody'); tbody.innerHTML = ''; const order = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu']; for (const pn of order) { const p = planets[pn]; if (!p || p.error) continue; let sc = ''; if (p.status === '入旺') sc = 'status-exalted'; else if (p.status === '落陷') sc = 'status-debilitated'; else if (p.status === '入庙') sc = 'status-own'; const retro = p.retrograde ? ' ℞' : ''; const combust = p.combust ? ' [燃]' : ''; // D9 sign let d9sign = '-'; if (d9Varga?.planets?.[pn]) { d9sign = signName(d9Varga.planets[pn].sign) || '-'; } tbody.innerHTML += ` ${PLANET_SYMBOLS[pn]||''}${pn}${retro}${combust} ${signName(p.sign)} ${p.degree_in_sign.toFixed(2)}° H${p.house} ${statusName(p.status)} ${p.nakshatra} P${p.nakshatra_pada} ${d9sign} `; } } // ========== Extended: Bhava Bala / Vimsopaka / Vaiseshikamsas / Planet States / AV Pinda / Extra Dasas ========== export function renderBhavaBala(bb) { const el = $('bhava-bala-section'); if (!el || !bb) return; const rows = bb.houses.map(h => { const bar = Math.min(100, h.in_rupas / 12 * 100); return `${h.house}${signName(h.sign)}${planetName(h.lord)||h.lord} ${h.in_rupas}${h.lord_bala}${h.dig_bala}${h.drig_bala}
`; }).join(''); el.innerHTML = `

${t('ext.bhava')}

${t('bb.note')}

${rows}
${t('col.house')}${t('col.sign')}${t('col.lord')}Rupas${t('col.lordbala')}${t('col.dig')}${t('col.drig')}${t('col.strength')}

${t('bb.strongest')}: ${signName(bb.ranked[0].sign)}(${bb.ranked[0].in_rupas}) | ${t('bb.weakest')}: ${signName(bb.ranked[bb.ranked.length-1].sign)}(${bb.ranked[bb.ranked.length-1].in_rupas})

`; } export function renderVimsopakaVaiseshikamsa(vim, vais) { const el = $('vimsopaka-section'); if (!el || !vim) return; const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu']; const rows = PL.map(pn => { const d = vim.dasa[pn], s = vim.shodasa[pn], v = vais?.[pn]; return `${planetName(pn)} ${d?d.score:'-'}${d?d.pct+'%':'-'} ${v?v.dasa.name:'-'}${v?v.dasa.level:'-'} ${s?s.score:'-'}${s?s.pct+'%':'-'} ${v?v.shodasa.name:'-'}${v?v.shodasa.level:'-'}`; }).join(''); el.innerHTML = `

${t('ext.vim')}

${rows}
${t('col.planet')} ${t('col.dasa10')}%${t('col.level')}${t('col.level')} ${t('col.shodasa')}%${t('col.level')}${t('col.level')}
`; } export function renderPlanetStates(activity, age, alertness, mood) { const el = $('planet-states-section'); if (!el || !activity) return; const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu']; const rows = PL.map(pn => { const a=activity[pn], ag=age?.[pn], al=alertness?.[pn], m=mood?.[pn]; return `${planetName(pn)} ${a?.activity||'-'}${ag?.age||'-'} ${al?.alertness||'-'}${m?.moods?.join(', ')||'-'}`; }).join(''); el.innerHTML = `

${t('ext.states')}

${rows}
${t('col.planet')}${t('col.activity')}${t('col.age')}${t('col.alertness')}${t('col.mood')}
`; } export function renderAVPinda(pinda) { const el = $('av-pinda-section'); if (!el || !pinda) return; const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Lagna']; const rows = PL.map(pn => { const p = pinda[pn]; return `${pn==='Lagna'?'Lagna':planetName(pn)} ${p?p.sodhya_pinda:'-'}${p?p.rasi_pinda:'-'}${p?p.graha_pinda:'-'}`; }).join(''); el.innerHTML = `

${t('ext.pinda')}

${rows}
${t('col.planet')}Sodhya PindaRasi PindaGraha Pinda
`; } export function renderExtraDasas(dasas) { const el = $('extra-dasa-section'); if (!el || !dasas) return; const systems = Object.entries(dasas); let html = `

${t('ext.dasa')}

`; for (const [key, dasa] of systems) { if (!dasa) continue; const cur = dasa.current; html += `
${dasa.system}${cur?' — 当前: '+(cur.lord_cn||cur.lord)+' ('+cur.start+' ~ '+cur.end+')':''}
`; // Antardasha if (cur?.antardasha) { html += '
'; for (const s of cur.antardasha) { const cls = s.is_current ? 'style="font-weight:bold;color:#222;"' : 'style="color:#888;"'; html += `${s.lord_cn||s.lord} ${s.start}~${s.end} `; } html += '
'; } // Timeline html += '
'; for (const d of dasa.timeline) { const cls = d.is_current ? 'dasha-active' : ''; html += `${d.lord_cn||d.lord}`; } html += '
'; } el.innerHTML = html; }