/**
* 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')}
Paksha 半月
${ty.tithi.paksha}
Karana 半月相
${ty.karana?.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 =>
`${v.id} ${v.cn} `
).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
${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('col.house')} ${t('col.sign')} ${t('col.lord')} Rupas ${t('col.lordbala')} ${t('col.dig')} ${t('col.drig')} ${t('col.strength')} ${rows}
${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')}
${t('col.planet')}
${t('col.dasa10')} % ${t('col.level')} ${t('col.level')}
${t('col.shodasa')} % ${t('col.level')} ${t('col.level')} ${rows}
`;
}
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')}
${t('col.planet')} ${t('col.activity')} ${t('col.age')} ${t('col.alertness')} ${t('col.mood')} ${rows}
`;
}
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')}
${t('col.planet')} Sodhya Pinda Rasi Pinda Graha Pinda ${rows}
`;
}
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;
}