Files
Jyotisha/jyotish-app/jyotish-extended.js
T
732642856 2457e23271 feat: Jyotish Web App v4.3 完整印度占星前端应用
技术栈: Vite + 原生HTML/JS/CSS + swisseph-wasm
功能模块:
- 排盘引擎: 行星/宫位/Dasha/Yoga/相位/燃烧检测/130+城市
- 高级计算: Karaka/Arudha/16分盘/Ashtakavarga/Shadbala/Panchanga/Chara Dasha
- 73个Yoga识别 (Pancha Mahapurusha + 扩展A/B)
- 生时校正v2.0: K.N.Rao方法论, 多维评分
- Transit v2.0: 日期选择/全行星/AV评分/Sade Sati
- 深度分析7子模块 + 扩展分析5子模块
- 导出v2.0: JSON 100%覆盖Python 13模块 + SVG/PNG
- i18n中英双语 + 点读机95词条 + AI聊天面板
- 认证+订阅 (JWT/Apple Sign-In/IAP)
- 浅色极简主题 (fatetell.com风格)
2026-04-30 03:51:30 +08:00

326 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Jyotish Extended Calculations v1.0
* Bhava Bala, Vimsopaka, Vaiseshikamsas, Planet States, AV Pinda, 8 Extra Dasas
*/
import { SIGNS, SIGNS_CN, SIGN_LORDS, PLANET_CN, NAKSHATRA_LIST, EXALTATION, DEBILITATION, PLANET_RELATIONS } from './jyotish-engine.js';
import { computeAllVargas } from './jyotish-advanced.js';
const _dignity = (pn, sign) => {
if (EXALTATION[pn] === sign) return 3;
if (DEBILITATION[pn] === sign) return 0.5;
if (SIGN_LORDS[sign] === pn) return 2.5;
const rel = PLANET_RELATIONS[pn];
if (rel?.friends?.includes(SIGN_LORDS[sign])) return 2;
if (rel?.enemies?.includes(SIGN_LORDS[sign])) return 1;
return 1.5;
};
// BPHS Vimsopaka Bala — Varga Viswa scoring
// Own/Exalted=20, Great Friend=18, Friend=15, Equal=10, Enemy=7, Bitter Enemy=5
// Compound relationship: check both planet→lord AND lord→planet attitudes
const _vargaViswa = (pn, sign) => {
const lord = SIGN_LORDS[sign];
if (lord === pn) return 20; // Own sign
if (EXALTATION[pn] === sign) return 20; // Exalted
if (DEBILITATION[pn] === sign) return 5; // Debilitated
const rel = PLANET_RELATIONS[pn];
if (!rel) return 10;
const isFriend = rel.friends?.includes(lord);
const isEnemy = rel.enemies?.includes(lord);
const lordRel = PLANET_RELATIONS[lord];
const lordIsFriend = lordRel?.friends?.includes(pn);
const lordIsEnemy = lordRel?.enemies?.includes(pn);
if (isFriend && lordIsFriend) return 18; // Great friend
if (isFriend) return 15; // Friend
if (!isFriend && !isEnemy) return 10; // Neutral
if (isEnemy && lordIsEnemy) return 5; // Bitter enemy
return 7; // Enemy
};
// ===== 1. Bhava Bala =====
export function computeBhavaBala(planets, ascSign, shadbala) {
const ai = SIGNS.indexOf(ascSign);
const houses = [];
for (let h = 1; h <= 12; h++) {
const si = (ai + h - 1) % 12, sn = SIGNS[si], lord = SIGN_LORDS[sn] || '';
const lordBala = shadbala[lord]?.raw_virupas?.totalV || 0;
// Dig Bala
const digMap = {1:30,4:30,7:0,10:60};
const dig = digMap[h] ?? Math.max(0, (6 - Math.min(Math.abs(h-10), 12-Math.abs(h-10))) * 10);
// Drig Bala
let drig = 0;
for (const [on, od] of Object.entries(planets)) {
if (on==='Rahu'||on==='Ketu'||od.error) continue;
const oSI = SIGNS.indexOf(od.sign);
const diff = ((si - oSI) + 12) % 12 + 1;
let has = diff===7;
if (on==='Mars' && [4,8].includes(diff)) has=true;
if (on==='Jupiter' && [5,9].includes(diff)) has=true;
if (on==='Saturn' && [3,10].includes(diff)) has=true;
if (has) {
const v = diff===1?30:15;
if (['Jupiter','Venus','Mercury'].includes(on)) drig+=v;
else if (['Saturn','Mars','Sun'].includes(on)) drig-=v;
else drig+=v*0.5;
}
}
drig = Math.max(-60, Math.min(60, drig));
const total = lordBala + dig + drig;
houses.push({house:h,sign:sn,sign_cn:SIGNS_CN[sn],lord,
bala:Math.round(total*100)/100, in_rupas:Math.round(total/60*100)/100,
lord_bala:Math.round(lordBala*100)/100, dig_bala:dig, drig_bala:Math.round(drig*100)/100});
}
const ranked = [...houses].sort((a,b)=>b.bala-a.bala);
ranked.forEach((b,i)=>b.rank=i+1);
return {houses, ranked};
}
// ===== 2. Vimsopaka Bala =====
// BPHS Dasa Varga (10): D1(3) + D2(1.5) + D3(1.5) + D7(1.5) + D9(1.5) + D10(1.5) + D12(1.5) + D16(1.5) + D30(1.5) + D60(5) = 20
// BPHS Shodasa Varga (16): above + D4(1) + D20(1) + D24(1) + D40(1) + D45(1) + D60 stays 5
// Score = Σ(weight × Varga_Viswa) / 20, max = 20
const DASA_W = [{id:'D1',w:3},{id:'D2',w:1.5},{id:'D3',w:1.5},{id:'D7',w:1.5},{id:'D9',w:1.5},{id:'D10',w:1.5},{id:'D12',w:1.5},{id:'D16',w:1.5},{id:'D30',w:1.5},{id:'D60',w:5}];
const SHODASA_W = [{id:'D1',w:3},{id:'D2',w:1.5},{id:'D3',w:1.5},{id:'D4',w:1},{id:'D7',w:1.5},{id:'D9',w:1.5},{id:'D10',w:1.5},{id:'D12',w:1.5},{id:'D16',w:1.5},{id:'D20',w:1},{id:'D24',w:1},{id:'D30',w:1.5},{id:'D40',w:1},{id:'D45',w:1},{id:'D60',w:5}];
export function computeVimsopaka(planets) {
const av = computeAllVargas(planets);
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const dasa={}, shodasa={};
for (const pn of PL) {
const d1 = planets[pn]?.sign; if (!d1) continue;
let ds=0;
for (const{id,w}of DASA_W) ds += w * _vargaViswa(pn, av[id]?.planets?.[pn]?.sign||d1) / 20;
dasa[pn]={score:Math.round(ds*100)/100, max:20, pct:Math.round(ds/20*10000)/100};
let ss=0;
for (const{id,w}of SHODASA_W) ss += w * _vargaViswa(pn, av[id]?.planets?.[pn]?.sign||d1) / 20;
shodasa[pn]={score:Math.round(ss*100)/100, max:20, pct:Math.round(ss/20*10000)/100};
}
return {dasa, shodasa};
}
// ===== 3. Vaiseshikamsas =====
const V_DASA = [{min:0,max:2,l:1,n:'Kim'},{min:2,max:3,l:2,n:'Paarijaata'},{min:3,max:4,l:3,n:'Uttama'},{min:4,max:5,l:4,n:'Gopura'},{min:5,max:6,l:5,n:'Simhasana/Kanduka'},{min:6,max:7,l:6,n:'Kerala'},{min:7,max:8,l:7,n:'Kalpavriksha'},{min:8,max:99,l:8,n:'Credacted_placeaVana'}];
const V_SHO = [{min:0,max:4,l:1,n:'Kim'},{min:4,max:6,l:2,n:'Paarijaata'},{min:6,max:8,l:3,n:'Uttama'},{min:8,max:10,l:4,n:'Gopura'},{min:10,max:12,l:5,n:'Kanduka'},{min:12,max:14,l:6,n:'Kerala'},{min:14,max:16,l:7,n:'Kalpavriksha'},{min:16,max:99,l:8,n:'Credacted_placeaVana'}];
export function computeVaiseshikamsas(vim) {
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const r = {};
for (const pn of PL) {
const ds = vim.dasa[pn]?.score||0, ss = vim.shodasa[pn]?.score||0;
let dl = V_DASA[V_DASA.length-1]; for (const t of V_DASA) if (ds>=t.min&&ds<t.max){dl=t;break;}
let sl = V_SHO[V_SHO.length-1]; for (const t of V_SHO) if (ss>=t.min&&ss<t.max){sl=t;break;}
r[pn] = {planet_cn:PLANET_CN[pn], dasa:{score:ds,level:dl.l,name:dl.n}, shodasa:{score:ss,level:sl.l,name:sl.n}};
}
return r;
}
// ===== 4. Planet States =====
export function computePlanetActivity(planets) {
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const r = {};
for (const pn of PL) {
const p = planets[pn]; if (!p||p.error) continue;
let a;
if (p.retrograde) a='Gamana (on the move)';
else {
const sp = Math.abs(p.speed||0);
if (sp<0.2) a='Sayana (lying down)';
else if (sp<0.5) a='Aagamana (coming)';
else a='Prakasana (glowing)';
}
if (pn==='Rahu'||pn==='Ketu') a='Prakasana (glowing)';
if (pn==='Sun') a='Aagamana (coming)';
r[pn]={planet_cn:PLANET_CN[pn],activity:a};
}
return r;
}
export function computePlanetAge(planets) {
const AGE = [{max:6,n:'Baala (infant)'},{max:12,n:'Kumara (adolescent)'},{max:18,n:'Yuva (young)'},{max:24,n:'Vriddha (old)'},{max:30,n:'Mrita (dead)'}];
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const r = {};
for (const pn of PL) {
const p = planets[pn]; if (!p||p.error) continue;
const d = p.degree_in_sign;
let age = AGE[AGE.length-1].n;
for (const a of AGE) if (d<a.max){age=a.n;break;}
r[pn]={planet_cn:PLANET_CN[pn],age,degree:d};
}
return r;
}
export function computePlanetAlertness(planets) {
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const r = {};
for (const pn of PL) {
const p = planets[pn]; if (!p||p.error) continue;
const h = p.house;
let s;
if ([1,4,7,10].includes(h)) s='Jaagrita (awake)';
else if ([2,5,8,11].includes(h)) s='Swapna (dreaming)';
else s='Sushupta (asleep)';
r[pn]={planet_cn:PLANET_CN[pn],alertness:s,house:h};
}
return r;
}
export function computePlanetMood(planets) {
const PL = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
const r = {};
for (const pn of PL) {
const p = planets[pn]; if (!p||p.error) continue;
const ml = [];
if (EXALTATION[pn]===p.sign) ml.push('Deepta (glowing)');
if (DEBILITATION[pn]===p.sign) ml.push('Duhkhita (distressed)');
if (SIGN_LORDS[p.sign]===pn) ml.push('Swastha (comfortable)');
if (p.retrograde) ml.push('Garvita (proud)');
const mySI = SIGNS.indexOf(p.sign);
for (const [on,od] of Object.entries(planets)) {
if (on===pn||on==='Rahu'||on==='Ketu'||od.error) continue;
const oSI = SIGNS.indexOf(od.sign), diff = ((mySI-oSI)+12)%12+1;
let has = diff===7;
if (on==='Mars'&&[4,8].includes(diff)) has=true;
if (on==='Jupiter'&&[5,9].includes(diff)) has=true;
if (on==='Saturn'&&[3,10].includes(diff)) has=true;
if (has) {
if (['Jupiter','Venus','Mercury'].includes(on)&&!ml.includes('Mudita (delighted)')) ml.push('Mudita (delighted)');
if (['Saturn','Mars','Sun'].includes(on)&&!ml.includes('Khala (mischievous)')) ml.push('Khala (mischievous)');
}
}
if (!ml.length) ml.push('Deena (sad)');
r[pn]={planet_cn:PLANET_CN[pn],moods:ml};
}
return r;
}
// ===== 5. Ashtakavarga Pinda =====
const RASI_GUNAKAR = [7,10,8,4,10,5,7,8,9,5,11,12]; // Aries..Pisces
const GRAHA_GUNAKAR = {Sun:5,Moon:5,Mars:8,Mercury:5,Jupiter:10,Venus:7,Saturn:5};
export function computeAVPinda(avResult) {
const pOrder = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn'];
const bavRaw = avResult.bav_raw || {};
const r = {};
for (const src of [...pOrder, 'Lagna']) {
const bav = bavRaw[src]; if (!bav) continue;
const totalBindus = bav.reduce((a,b)=>a+b,0);
// Rasi Pinda = sum of (bindus[i] × rasiGunakar[i])
let rasiPinda = 0;
for (let i = 0; i < 12; i++) rasiPinda += bav[i] * RASI_GUNAKAR[i];
// Graha Pinda = totalBindus × grahaGunakar (Lagna has no graha gunakar)
const gG = GRAHA_GUNAKAR[src] || 0;
const grahaPinda = totalBindus * gG;
r[src] = {
sodhya_pinda: totalBindus,
rasi_pinda: rasiPinda,
graha_pinda: grahaPinda,
total_bindus: totalBindus
};
}
return r;
}
// ===== 6. Extra Dasa Systems =====
const TY = 365.24219;
const _jd = (y,m,d) => { if(m<=2){y--;m+=12;} const A=Math.floor(y/100),B=2-A+Math.floor(A/4); return Math.floor(365.25*(y+4716))+Math.floor(30.6001*(m+1))+d+B-1524.5; };
const _jds = (jd) => { let Z=Math.floor(jd+0.5),F=jd+0.5-Z,A; if(Z<2299161)A=Z;else{const a=Math.floor((Z-1867216.25)/36524.25);A=Z+1+a-Math.floor(a/4);} const B=A+1524,C=Math.floor((B-122.1)/365.25),D=Math.floor(365.25*C),E=Math.floor((B-D)/30.6001); const dy=Math.floor(B-D-Math.floor(30.6001*E)+F),mo=E<14?E-1:E-13,yr=mo>2?C-4716:C-4715; return `${yr}-${String(mo).padStart(2,'0')}-${String(dy).padStart(2,'0')}`; };
const ASHT_ORD = ['Sun','Moon','Mars','Mercury','Saturn','Jupiter','Venus','Rahu'];
const ASHT_YR = {Sun:6,Moon:15,Mars:8,Mercury:17,Saturn:10,Jupiter:19,Venus:21,Rahu:12};
const YOG_ORD = ['Moon','Sun','Jupiter','Mars','Mercury','Saturn','Venus','Rahu'];
const YOG_NM = ['Mangala','Pingala','Dhanya','Bhramari','Bhadrika','Ulka','Siddha','Sankata'];
const YOG_YR = {Moon:1,Sun:2,Jupiter:3,Mars:4,Mercury:5,Saturn:6,Venus:7,Rahu:8};
const S_ABBR = ['Ar','Ta','Ge','Cn','Le','Vi','Li','Sc','Sg','Cp','Aq','Pi'];
const S_YRS = [7,10,7,8,6,10,7,8,9,10,7,12];
function _buildSubs(sJD, totalDays, order, yrMap, refJD) {
const totY = order.reduce((s,l)=>s+(yrMap[l]||0),0);
const subs=[]; let c=sJD;
for (const lord of order) {
const d=totalDays*(yrMap[lord]||0)/totY, e=c+d;
subs.push({lord,lord_cn:PLANET_CN[lord],start:_jds(c),end:_jds(e),is_current:c<=refJD&&refJD<e});
c=e;
}
return subs;
}
export function computeAshtottari(moonLon, bDate, rDate) {
const bJD=_jd(...bDate.split('-').map(Number));
const rJD=rDate?_jd(...rDate.split('-').map(Number)):_jd(new Date().getFullYear(),new Date().getMonth()+1,new Date().getDate());
const nk=NAKSHATRA_LIST[Math.floor(moonLon/(360/27))%27];
const prog=(moonLon%(360/27))/(360/27);
let si=ASHT_ORD.indexOf(nk.lord); if(si<0)si=0;
const sJD=bJD-prog*(ASHT_YR[nk.lord]||6)*TY;
const tl=[]; let c=sJD;
for(let i=0;i<8;i++){
const lord=ASHT_ORD[(si+i)%8], yrs=ASHT_YR[lord], e=c+yrs*TY;
const d={lord,lord_cn:PLANET_CN[lord],start:_jds(c),end:_jds(e),years:yrs,antardasha:null};
if(c<=rJD&&rJD<e){d.antardasha=_buildSubs(c,e-c,ASHT_ORD,ASHT_YR,rJD);d.is_current=true;}
tl.push(d); c=e;
}
return{system:'Ashtottari Dasa (108年)',timeline:tl,current:tl.find(d=>d.is_current)||null};
}
export function computeYogini(moonLon, bDate, rDate) {
const bJD=_jd(...bDate.split('-').map(Number));
const rJD=rDate?_jd(...rDate.split('-').map(Number)):_jd(new Date().getFullYear(),new Date().getMonth()+1,new Date().getDate());
const nk=NAKSHATRA_LIST[Math.floor(moonLon/(360/27))%27];
const prog=(moonLon%(360/27))/(360/27);
let si=YOG_ORD.indexOf(nk.lord); if(si<0)si=0;
const sJD=bJD-prog*YOG_YR[YOG_ORD[si]]*TY;
const tl=[]; let c=sJD;
for(let i=0;i<8;i++){
const idx=(si+i)%8, lord=YOG_ORD[idx], yrs=YOG_YR[lord], e=c+yrs*TY;
const d={lord,lord_cn:PLANET_CN[lord],yogini_name:YOG_NM[idx],start:_jds(c),end:_jds(e),years:yrs,antardasha:null};
if(c<=rJD&&rJD<e){d.antardasha=_buildSubs(c,e-c,YOG_ORD,YOG_YR,rJD);d.is_current=true;}
tl.push(d); c=e;
}
return{system:'Yogini Dasa (36年)',timeline:tl,current:tl.find(d=>d.is_current)||null};
}
function _signDasa(name, startSign, bDate, rDate, fwd) {
const bJD=_jd(...bDate.split('-').map(Number));
const rJD=rDate?_jd(...rDate.split('-').map(Number)):_jd(new Date().getFullYear(),new Date().getMonth()+1,new Date().getDate());
const si=SIGNS.indexOf(startSign);
const tl=[]; let c=bJD;
for(let i=0;i<12;i++){
const idx=fwd?(si+i)%12:((si-i)+12)%12;
const sn=SIGNS[idx], yrs=S_YRS[idx], e=c+yrs*TY;
const d={lord:S_ABBR[idx],lord_cn:SIGNS_CN[sn],sign:sn,start:_jds(c),end:_jds(e),years:yrs,antardasha:null};
if(c<=rJD&&rJD<e){
d.is_current=true;
const td=e-c, subs=[]; let sc=c;
for(let j=0;j<12;j++){
const sidx=fwd?(idx+j)%12:((idx-j)+12)%12;
const ssn=SIGNS[sidx], sd=td*S_YRS[sidx]/84, se=sc+sd;
subs.push({lord:S_ABBR[sidx],lord_cn:SIGNS_CN[ssn],sign:ssn,start:_jds(sc),end:_jds(se),is_current:sc<=rJD&&rJD<se});
sc=se;
}
d.antardasha=subs;
}
tl.push(d); c=e;
}
return{system:name,timeline:tl,current:tl.find(d=>d.is_current)||null};
}
export const computeNarayana = (moonSign,bD,rD) => _signDasa('Narayana Dasa (那罗延大运)', moonSign, bD, rD, SIGNS.indexOf(moonSign)%2===0);
export const computeShoola = (ascSign,bD,rD) => { const ni=(SIGNS.indexOf(ascSign)+8)%12; return _signDasa('Shoola Dasa (死亡大运)', SIGNS[ni], bD, rD, false); };
export const computeSudasa = (ascSign,bD,rD) => _signDasa('Sudasa (财富大运)', ascSign, bD, rD, true);
export const computeDrigdasa = (moonSign,bD,rD) => { const ni=(SIGNS.indexOf(moonSign)+8)%12; return _signDasa('Drigdasa (灵性大运)', SIGNS[ni], bD, rD, false); };
export const computeMoola = (ascSign,bD,rD) => _signDasa('Moola Dasa (业力根源)', ascSign, bD, rD, true);
export const computeKalachakra = (moonLon,bD,rD) => _signDasa('Kalachakra Dasa (时轮大运)', SIGNS[Math.floor(moonLon/30)], bD, rD, false);
export function computeAllExtraDasas(planets, ascSign, bDate, rDate) {
const ms=planets.Moon?.sign, ml=planets.Moon?.degree||0;
return {
ashtottari:computeAshtottari(ml,bDate,rDate),
kalachakra:computeKalachakra(ml,bDate,rDate),
moola:computeMoola(ascSign,bDate,rDate),
narayana:computeNarayana(ms,ascSign,bDate,rDate),
sudasa:computeSudasa(ascSign,bDate,rDate),
drigdasa:computeDrigdasa(ms,bDate,rDate),
shoola:computeShoola(ascSign,bDate,rDate),
yogini:computeYogini(ml,bDate,rDate),
};
}