UNPKG

@nrweb/astro-calc

Version:

Comprehensive astrological calculations using Swiss Ephemeris and astronomy-engine for positions, aspects, houses, lots, and scoring metrics

1,186 lines 79.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFullAstrologicalData = void 0; exports.getAstrologicalPositions = getAstrologicalPositions; exports.calculateRomanceScore = calculateRomanceScore; exports.calculateCareerScore = calculateCareerScore; exports.calculateWealthScore = calculateWealthScore; exports.calculateMagicScore = calculateMagicScore; exports.calculateFriendshipScore = calculateFriendshipScore; exports.calculateHealthScore = calculateHealthScore; const astronomy_engine_1 = require("astronomy-engine"); const utils_1 = require("./utils"); const aspects_1 = require("./aspects"); const retrograde_1 = require("./retrograde"); const constants_1 = require("./constants"); const SunCalc = __importStar(require("suncalc")); const react_native_swisseph_1 = require("@nrweb/react-native-swisseph"); // @ts-ignore const lune = __importStar(require("lune")); const SE_MEAN_NODE = 10; const SEFLG_SWIEPH = 2; const SEFLG_SPEED = 256; const SE_GREG_CAL = 1; function findRetrogradePeriod(date, planet) { const calendar = (0, retrograde_1.loadCalendar)(); const periods = calendar[planet] || []; const dateStr = date.toISOString().split("T")[0]; return (periods.find((period) => (period.startDate <= dateStr && period.endDate >= dateStr) || // Current retrograde period.startDate > dateStr // Next retrograde ) || null); } function getSunriseSunset(date, latitude, longitude) { const times = SunCalc.getTimes(date, latitude, longitude); return { sunrise: times.sunrise, sunset: times.sunset, }; } // Helper function to calculate houses function calculateHouses(date, latitude, longitude) { const julianDay = (0, react_native_swisseph_1.sweJulday)(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(), date.getUTCHours() + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600, 1); const housesResult = (0, react_native_swisseph_1.sweHouses)(julianDay, SEFLG_SWIEPH, latitude, longitude, "P"); if ("error" in housesResult) { throw new Error(`Failed to calculate houses: ${housesResult.error}`); } const ascendant = housesResult.ascmc[0]; const cusps = housesResult.cusp; // Create houses array (houses 1-12) const houses = []; const zodiacSigns = [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ]; for (let i = 0; i < 12; i++) { const startPosition = cusps[i]; const endPosition = cusps[i + 1] || cusps[0]; // House 12 ends at House 1 (Ascendant) const startSignIndex = Math.floor(startPosition / 30); const startDegrees = startPosition % 30; const startSign = zodiacSigns[startSignIndex]; const endSignIndex = Math.floor(endPosition / 30); const endDegrees = endPosition % 30; const endSign = zodiacSigns[endSignIndex]; houses.push({ number: i + 1, startSign, startDegrees: Math.round(startDegrees * 100) / 100, endSign, endDegrees: Math.round(endDegrees * 100) / 100, bodies: [], }); } return { ascendant, houses }; } const getPlanetPosition = (time, body, sunLongitude) => { const julianDay = (0, react_native_swisseph_1.sweJulday)(time.getUTCFullYear(), time.getUTCMonth() + 1, time.getUTCDate(), time.getUTCHours() + time.getUTCMinutes() / 60 + time.getUTCSeconds() / 3600, SE_GREG_CAL); const sePlanet = constants_1.SE_PLANETS[body]; if (sePlanet === undefined) { throw new Error(`Unsupported planet: ${astronomy_engine_1.Body[body]}`); } const result = (0, react_native_swisseph_1.sweCalcUt)(julianDay, sePlanet, SEFLG_SWIEPH | SEFLG_SPEED); const longitude = result.longitude; const longitudeSpeed = result.longitudeSpeed; // degrees per day const sign = (0, utils_1.getZodiacSign)(longitude); const degreesInSign = longitude % 30; const bodyName = astronomy_engine_1.Body[body]; const planetPosition = { name: bodyName, position: longitude, sign, degrees: degreesInSign, dignity: (0, utils_1.getDignity)(bodyName, sign), sect: constants_1.PLANET_SECTS[bodyName], planetaryMotion: longitudeSpeed > 0 ? "direct" : longitudeSpeed < 0 ? "retrograde" : "stationary", speed: longitudeSpeed, decanRuler: (0, utils_1.getDecanRuler)(sign, degreesInSign), boundRuler: (0, utils_1.getBoundRuler)(sign, degreesInSign), aspects: [], }; // Determine if planet is moving slowly const lowerBound = constants_1.PLANET_SPEED_LOWER_BOUND[body]; if (lowerBound !== undefined) { planetPosition.slowOfMotion = Math.abs(longitudeSpeed) <= lowerBound; } // Determine morning/evening star status by comparing with Sun's longitude if (body !== astronomy_engine_1.Body.Sun && sunLongitude !== undefined) { planetPosition.isMorningStar = longitude >= sunLongitude + 15 && longitude <= sunLongitude + 90; } // Handle retrograde period information if (longitudeSpeed < 0) { // Find when retrograde ends by looking ahead const period = findRetrogradePeriod(time, bodyName); if (period) { const dateStr = time.toISOString().split("T")[0]; if (dateStr >= period.startDate && dateStr <= period.endDate) { planetPosition.retrogradeEnds = period.endDate; } } } else { // Find next retrograde const period = findRetrogradePeriod(time, bodyName); if (period) { const dateStr = time.toISOString().split("T")[0]; if (dateStr < period.startDate) { planetPosition.nextRetrograde = period.startDate; } } } return planetPosition; }; function serializePlanetPosition(pos) { return { ...pos, position: (0, utils_1.decimalToDegreesMinutes)(pos.position), degrees: (0, utils_1.decimalToDegreesMinutes)(pos.degrees), speed: pos.speed.toFixed(2), }; } function serializeHouse(house) { return { ...house, startDegrees: (0, utils_1.decimalToDegreesMinutes)(house.startDegrees), endDegrees: (0, utils_1.decimalToDegreesMinutes)(house.endDegrees), }; } function serializeAspect(aspect) { return { ...aspect, deviation: (0, utils_1.decimalToDegreesMinutes)(Math.abs(aspect.deviation)), }; } // Helper function to assign planets to houses and aspects to planets function assignPlanetsToHouses(positions, houses) { // Calculate all aspects first const allAspects = (0, aspects_1.calculateAspects)(positions); // Create a map of planet names to their aspects const planetAspects = new Map(); // Initialize empty arrays for each planet positions.forEach((planet) => { planetAspects.set(planet.name, []); }); // Assign aspects to each planet allAspects.forEach((aspect) => { const leftPlanet = aspect.planets.left; const rightPlanet = aspect.planets.right; // Add aspect to both planets involved if (planetAspects.has(leftPlanet)) { planetAspects.get(leftPlanet).push(aspect); } if (planetAspects.has(rightPlanet)) { planetAspects.get(rightPlanet).push(aspect); } }); // Create a copy of houses with empty bodies arrays const housesWithBodies = houses.map((house) => ({ ...house, bodies: [], })); // Assign each planet to the appropriate house and add its aspects positions.forEach((planet) => { const planetPosition = planet.position; // Add aspects to the planet const planetWithAspects = { ...planet, aspects: planetAspects.get(planet.name) || [], }; for (let i = 0; i < housesWithBodies.length; i++) { const house = housesWithBodies[i]; const nextHouse = housesWithBodies[(i + 1) % housesWithBodies.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; // Handle case where house spans across 0° Aries if (houseEnd < houseStart) { houseEnd += 360; } // Check if planet is in this house let planetPos = planetPosition; if (houseEnd > 360 && planetPos < houseStart) { planetPos += 360; } if (planetPos >= houseStart && planetPos < houseEnd) { house.bodies.push(planetWithAspects); break; } } }); return housesWithBodies; } // Helper function to get zodiac sign index function getZodiacSignIndex(sign) { const zodiacSigns = [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ]; return zodiacSigns.indexOf(sign); } // Helper function to find which house a position falls in function findHouseForPosition(position, houses) { for (let i = 0; i < houses.length; i++) { const house = houses[i]; const nextHouse = houses[(i + 1) % houses.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; // Handle case where house spans across 0° Aries if (houseEnd < houseStart) { houseEnd += 360; } // Check if position is in this house let pos = position; if (houseEnd > 360 && pos < houseStart) { pos += 360; } if (pos >= houseStart && pos < houseEnd) { return house.number; } } // Fallback to house 1 if not found return 1; } // Helper function to calculate lots function calculateLots(ascendant, sunPosition, moonPosition, venusPosition, marsPosition, jupiterPosition, saturnPosition, midheaven, isDayChart, houses) { const lots = []; // Lot of Fortune const fortunePosition = isDayChart ? (ascendant + moonPosition - sunPosition + 360) % 360 : (ascendant + sunPosition - moonPosition + 360) % 360; lots.push({ name: "Lot of Fortune", position: fortunePosition, sign: (0, utils_1.getZodiacSign)(fortunePosition), degrees: fortunePosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(fortunePosition), fortunePosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(fortunePosition), fortunePosition % 30), house: findHouseForPosition(fortunePosition, houses), aspects: [], }); // Lot of Marriage const marriagePosition = (ascendant + venusPosition - marsPosition + 360) % 360; lots.push({ name: "Lot of Marriage", position: marriagePosition, sign: (0, utils_1.getZodiacSign)(marriagePosition), degrees: marriagePosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(marriagePosition), marriagePosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(marriagePosition), marriagePosition % 30), house: findHouseForPosition(marriagePosition, houses), aspects: [], }); // Lot of Children const childrenPosition = (ascendant + jupiterPosition - saturnPosition + 360) % 360; lots.push({ name: "Lot of Children", position: childrenPosition, sign: (0, utils_1.getZodiacSign)(childrenPosition), degrees: childrenPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(childrenPosition), childrenPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(childrenPosition), childrenPosition % 30), house: findHouseForPosition(childrenPosition, houses), aspects: [], }); // Lot of Career const careerPosition = (ascendant + midheaven - sunPosition + 360) % 360; lots.push({ name: "Lot of Career", position: careerPosition, sign: (0, utils_1.getZodiacSign)(careerPosition), degrees: careerPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(careerPosition), careerPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(careerPosition), careerPosition % 30), house: findHouseForPosition(careerPosition, houses), aspects: [], }); // Lot of Health const healthPosition = (ascendant + moonPosition - saturnPosition + 360) % 360; lots.push({ name: "Lot of Health", position: healthPosition, sign: (0, utils_1.getZodiacSign)(healthPosition), degrees: healthPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(healthPosition), healthPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(healthPosition), healthPosition % 30), house: findHouseForPosition(healthPosition, houses), aspects: [], }); // Lot of Wealth const wealthPosition = (ascendant + venusPosition - jupiterPosition + 360) % 360; lots.push({ name: "Lot of Wealth", position: wealthPosition, sign: (0, utils_1.getZodiacSign)(wealthPosition), degrees: wealthPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(wealthPosition), wealthPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(wealthPosition), wealthPosition % 30), house: findHouseForPosition(wealthPosition, houses), aspects: [], }); // Lot of Love const lovePosition = (venusPosition + jupiterPosition - ascendant + 360) % 360; lots.push({ name: "Lot of Love", position: lovePosition, sign: (0, utils_1.getZodiacSign)(lovePosition), degrees: lovePosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(lovePosition), lovePosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(lovePosition), lovePosition % 30), house: findHouseForPosition(lovePosition, houses), aspects: [], }); // Calculate Lot of Spirit (Ascendant + Sun - Moon) for day, (Ascendant + Moon - Sun) for night let lotOfSpiritPosition = isDayChart ? ascendant + sunPosition - moonPosition : ascendant + moonPosition - sunPosition; lotOfSpiritPosition = ((lotOfSpiritPosition % 360) + 360) % 360; const lotOfSpirit = { name: "Lot of Spirit", position: lotOfSpiritPosition, sign: (0, utils_1.getZodiacSign)(lotOfSpiritPosition), degrees: lotOfSpiritPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(lotOfSpiritPosition), lotOfSpiritPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(lotOfSpiritPosition), lotOfSpiritPosition % 30), house: findHouseForPosition(lotOfSpiritPosition, houses), aspects: [], }; lots.push(lotOfSpirit); // Calculate Lot of Friendship (Moon + (Jupiter - Saturn)) for day, (Moon + (Saturn - Jupiter)) for night let lotOfFriendshipPosition = isDayChart ? moonPosition + (jupiterPosition - saturnPosition) : moonPosition + (saturnPosition - jupiterPosition); lotOfFriendshipPosition = ((lotOfFriendshipPosition % 360) + 360) % 360; const lotOfFriendship = { name: "Lot of Friendship", position: lotOfFriendshipPosition, sign: (0, utils_1.getZodiacSign)(lotOfFriendshipPosition), degrees: lotOfFriendshipPosition % 30, decanRuler: (0, utils_1.getDecanRuler)((0, utils_1.getZodiacSign)(lotOfFriendshipPosition), lotOfFriendshipPosition % 30), boundRuler: (0, utils_1.getBoundRuler)((0, utils_1.getZodiacSign)(lotOfFriendshipPosition), lotOfFriendshipPosition % 30), house: findHouseForPosition(lotOfFriendshipPosition, houses), aspects: [], }; lots.push(lotOfFriendship); return lots; } // Helper function to serialize a lot function serializeLot(lot) { return { ...lot, position: (0, utils_1.decimalToDegreesMinutes)(lot.position), degrees: (0, utils_1.decimalToDegreesMinutes)(lot.degrees), }; } // Helper function to get aspect type function getAspectType(diff) { if (Math.abs(diff - 0) <= 10) return "conjunction"; if (Math.abs(diff - 60) <= 10) return "sextile"; if (Math.abs(diff - 90) <= 10) return "square"; if (Math.abs(diff - 120) <= 10) return "trine"; if (Math.abs(diff - 180) <= 10) return "opposition"; return null; } // Helper function to detect astrological challenges function detectChallenges(positions) { const challenges = []; // Algol is at 26° Taurus (26° + 30° = 56° in zodiac) const algolPosition = 56; // Via Combusta is from 15° Libra to 15° Scorpio (195° to 225° in zodiac) const viaCombustaStart = 195; const viaCombustaEnd = 225; positions.forEach((planet) => { // Check for Algol transit (within 1° of 26° Taurus) const distanceFromAlgol = Math.abs(planet.position - algolPosition); if (distanceFromAlgol <= 1 || distanceFromAlgol >= 359) { challenges.push(`${planet.name} transits Algol`); } // Check for aneretic degree (29° of any sign) const degreesInSign = planet.position % 30; if (Math.floor(degreesInSign) === 29) { challenges.push(`${planet.name} is at Aneretic Degree`); } // Check for Via Combusta (Moon between 15° Libra and 15° Scorpio) if (planet.name === "Moon") { if (planet.position >= viaCombustaStart && planet.position <= viaCombustaEnd) { challenges.push("Moon is in Via Combusta"); } // Check for Void Course of the Moon if (isVoidCourseMoon(planet, positions)) { challenges.push("Void Course of the Moon"); } } }); return challenges; } // Helper function to check if Moon is in void course function isVoidCourseMoon(moon, positions) { const moonPosition = moon.position; const moonSpeed = moon.speed; // Calculate the end of the current sign const currentSignStart = Math.floor(moonPosition / 30) * 30; const signEnd = currentSignStart + 30; const degreesToSignEnd = signEnd - moonPosition; // Calculate how long it takes Moon to reach the end of the sign const timeToSignEnd = degreesToSignEnd / Math.abs(moonSpeed); // Check if Moon will form any aspects before reaching the end of the sign for (let i = 0; i < 24; i++) { // Check next 24 hours const futureTime = i / 24; // Convert hours to days const futureMoonPosition = moonPosition + moonSpeed * futureTime; // If Moon has passed the sign end, it's void course if (futureMoonPosition >= signEnd) { return true; } // Check if Moon forms any aspects in this future position for (const planet of positions) { if (planet.name === "Moon" || planet.name === "Ascendant") continue; const diff = Math.abs(futureMoonPosition - planet.position); const adjustedDiff = diff > 180 ? 360 - diff : diff; const aspectType = getAspectType(adjustedDiff); if (aspectType) { return false; // Moon will form an aspect, so not void course } } } // If we reach here, Moon didn't form any aspects before sign end return true; } // Helper function to calculate planetary hours function calculatePlanetaryHours(date, latitude, longitude) { const planetaryHours = {}; const dateStr = date.toISOString().split("T")[0]; // Get timezone offset in minutes for local time conversion const timezoneOffset = date.getTimezoneOffset(); // Get sunrise and sunset for current day const currentDayTimes = SunCalc.getTimes(date, latitude, longitude); const sunrise = currentDayTimes.sunrise; const sunset = currentDayTimes.sunset; // Get sunrise for next day const nextDay = new Date(date); nextDay.setDate(nextDay.getDate() + 1); const nextDayTimes = SunCalc.getTimes(nextDay, latitude, longitude); const nextSunrise = nextDayTimes.sunrise; // Calculate day and night hour lengths const dayDuration = sunset.getTime() - sunrise.getTime(); const nightDuration = nextSunrise.getTime() - sunset.getTime(); const dayHourLength = dayDuration / 12; // 12 day hours const nightHourLength = nightDuration / 12; // 12 night hours // Determine day ruler based on day of week const dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, etc. const dayRuler = constants_1.DAY_RULERS[dayOfWeek]; // Find starting position in Chaldean order const chaldeanStartIndex = constants_1.CHALDEAN_ORDER.indexOf(dayRuler); // Initialize hours array for this date planetaryHours[dateStr] = []; // Helper function to convert UTC to local time string const toLocalTimeString = (utcDate) => { const localDate = new Date(utcDate.getTime() - timezoneOffset * 60 * 1000); const hours = localDate.getHours(); const minutes = localDate.getMinutes(); const ampm = hours >= 12 ? "PM" : "AM"; const displayHours = hours % 12 || 12; return `${displayHours}:${minutes.toString().padStart(2, "0")} ${ampm}`; }; // Helper function to format duration in minutes const formatDuration = (durationMs) => { const minutes = Math.round(durationMs / (1000 * 60)); return `${minutes} minutes`; }; // Calculate day hours (1-12) for (let i = 0; i < 12; i++) { const rulerIndex = (chaldeanStartIndex + i) % constants_1.CHALDEAN_ORDER.length; const ruler = constants_1.CHALDEAN_ORDER[rulerIndex]; const startTime = new Date(sunrise.getTime() + i * dayHourLength); const endTime = new Date(sunrise.getTime() + (i + 1) * dayHourLength); planetaryHours[dateStr].push({ ruler: astronomy_engine_1.Body[ruler], startTime: toLocalTimeString(startTime), endTime: toLocalTimeString(endTime), length: formatDuration(dayHourLength), }); } // Calculate night hours (13-24) for (let i = 0; i < 12; i++) { const rulerIndex = (chaldeanStartIndex + i) % constants_1.CHALDEAN_ORDER.length; const ruler = constants_1.CHALDEAN_ORDER[rulerIndex]; const startTime = new Date(sunset.getTime() + i * nightHourLength); const endTime = new Date(sunset.getTime() + (i + 1) * nightHourLength); planetaryHours[dateStr].push({ ruler: astronomy_engine_1.Body[ruler], startTime: toLocalTimeString(startTime), endTime: toLocalTimeString(endTime), length: formatDuration(nightHourLength), }); } return planetaryHours; } // Helper function to calculate romance score function calculateRomanceScore(positions, houses, lots, moonPhaseData) { let positiveScore = 0; let negativeScore = 0; // Get Venus, Jupiter, Saturn, Mars, Moon positions const venus = positions.find((p) => p.name === "Venus"); const jupiter = positions.find((p) => p.name === "Jupiter"); const saturn = positions.find((p) => p.name === "Saturn"); const mars = positions.find((p) => p.name === "Mars"); const moon = positions.find((p) => p.name === "Moon"); const lotOfLove = lots.find((l) => l.name === "Lot of Love"); if (!venus) return 0.5; // Default score if Venus not found // Helper function to find which house a planet is in const findHouseForPlanet = (planetPosition) => { for (let i = 0; i < houses.length; i++) { const house = houses[i]; const nextHouse = houses[(i + 1) % houses.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; if (houseEnd < houseStart) { houseEnd += 360; } let planetPos = planetPosition; if (houseEnd > 360 && planetPos < houseStart) { planetPos += 360; } if (planetPos >= houseStart && planetPos < houseEnd) { return i + 1; } } return 0; }; // Helper function to check aspects between two planets const checkAspects = (planet1, planet2, aspectType) => { const diff = Math.abs(planet1.position - planet2.position); const adjustedDiff = diff > 180 ? 360 - diff : diff; const aspect = getAspectType(adjustedDiff); if (!aspect) return null; if (aspectType && aspect !== aspectType) return null; const target = { conjunction: 0, sextile: 60, square: 90, trine: 120, opposition: 180, }[aspect]; const deviation = Math.abs(adjustedDiff - target); const grade = deviation <= 1 ? "perfect" : deviation <= 3 ? "joining" : "assembly"; return { type: aspect, grade }; }; // Helper function to get aspect score const getAspectScore = (aspect, isPositive) => { if (!aspect) return 0; const baseScores = { trine: { perfect: 10, joining: 8, assembly: 6 }, // doubled sextile: { perfect: 6, joining: 4, assembly: 2 }, // doubled square: { perfect: -6, joining: -4, assembly: -2 }, // doubled opposition: { perfect: -10, joining: -8, assembly: -6 }, // doubled }; const score = baseScores[aspect.type]?.[aspect.grade] || 0; return isPositive ? Math.max(0, score) : Math.min(0, score); }; // Venus dignities if (venus.dignity === "Rulership") positiveScore += 6; // doubled if (venus.dignity === "Exaltation") positiveScore += 4; // doubled if (venus.dignity === "Fall") negativeScore += 6; // doubled if (venus.dignity === "Detriment") negativeScore += 4; // doubled if (venus.dignity === "Peregrine") negativeScore += 2; // doubled // Venus house placements const venusHouse = findHouseForPlanet(venus.position); if (venusHouse === 5) positiveScore += 4; // doubled if (venusHouse === 7) positiveScore += 4; // doubled if ([1, 4, 7, 10].includes(venusHouse)) positiveScore += 2; // doubled if ([3, 6, 9, 12].includes(venusHouse)) negativeScore += 2; // doubled // Jupiter house placements if (jupiter) { const jupiterHouse = findHouseForPlanet(jupiter.position); if (jupiterHouse === 5) positiveScore += 2; // doubled if (jupiterHouse === 7) positiveScore += 2; // doubled } // Saturn house placements if (saturn) { const saturnHouse = findHouseForPlanet(saturn.position); if (saturnHouse === 5) negativeScore += 4; // doubled if (saturnHouse === 7) negativeScore += 4; // doubled } // Mars house placements if (mars) { const marsHouse = findHouseForPlanet(mars.position); if (marsHouse === 5) negativeScore += 2; // doubled if (marsHouse === 7) negativeScore += 2; // doubled } // Moon phase const moonPhase = moonPhaseData.phase; if (moonPhase > 0.5 && moonPhase < 1) positiveScore += 4; // doubled if (moonPhase === 1) positiveScore += 2; // doubled if (moonPhase === 0) negativeScore += 4; // doubled if (moonPhase < 0.5 && moonPhase > 0) negativeScore += 2; // doubled // House cusp signs const house5Sign = houses[4].startSign; const house7Sign = houses[6].startSign; if (house5Sign === "Leo") positiveScore += 4; // doubled if (house7Sign === "Libra") positiveScore += 4; // doubled if (house5Sign === "Aquarius") negativeScore += 4; // doubled if (house7Sign === "Pisces") negativeScore += 4; // doubled // House cusp rulerships const sunRuledSigns = ["Leo"]; const venusRuledSigns = ["Taurus", "Libra"]; const sunFallSigns = ["Libra"]; const venusFallSigns = ["Virgo"]; if (sunRuledSigns.includes(house5Sign)) positiveScore += 2; // doubled if (venusRuledSigns.includes(house7Sign)) positiveScore += 2; // doubled if (sunFallSigns.includes(house5Sign)) negativeScore += 2; // doubled if (venusFallSigns.includes(house7Sign)) negativeScore += 2; // doubled // Lot of Love house placement if (lotOfLove) { const lotHouse = findHouseForPlanet(lotOfLove.position); if ([1, 4, 7, 10].includes(lotHouse)) positiveScore += 2; // doubled if ([3, 6, 9, 12].includes(lotHouse)) negativeScore += 2; // doubled } // Venus aspects with Moon if (moon) { const aspect = checkAspects(venus, moon); if (aspect) { if (aspect.type === "trine") { positiveScore += getAspectScore(aspect, true); } else if (aspect.type === "sextile") { positiveScore += getAspectScore(aspect, true); } else if (aspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(aspect, false)); } else if (aspect.type === "square") { negativeScore += Math.abs(getAspectScore(aspect, false)); } } } // Venus aspects with Saturn and Mars (general aspects) if (saturn) { const aspect = checkAspects(venus, saturn); if (aspect) { positiveScore += 4; // doubled negativeScore += 4; // doubled } } if (mars) { const aspect = checkAspects(venus, mars); if (aspect) { positiveScore += 2; // doubled negativeScore += 2; // doubled } } // Lot of Love aspects with Saturn and Mars if (lotOfLove) { if (saturn) { const aspect = checkAspects(lotOfLove, saturn); if (aspect) { positiveScore += 4; // doubled negativeScore += 4; // doubled } } if (mars) { const aspect = checkAspects(lotOfLove, mars); if (aspect) { positiveScore += 2; // doubled negativeScore += 2; // doubled } } } // Venus motion if (venus.planetaryMotion === "direct") positiveScore += 2; // doubled // Venus solar influence if (venus.solarInfluence === "cazimi") positiveScore += 6; // doubled if (venus.solarInfluence === "combust") negativeScore += 6; // doubled // Normalize scores const maxPositiveScore = 50; // Estimate of maximum possible positive score const maxNegativeScore = 30; // Estimate of maximum possible negative score const normalizedPositive = Math.min(positiveScore / maxPositiveScore, 1) * 0.5; const normalizedNegative = Math.min(negativeScore / maxNegativeScore, 1) * 0.5; // Final score: 0.5 + normalized positive - normalized negative const finalScore = Math.max(0, Math.min(1, 0.5 + normalizedPositive - normalizedNegative)); return Math.round(finalScore * 1000) / 1000; // Round to 3 decimal places } // Helper function to calculate career score function calculateCareerScore(positions, houses, lots, moonPhaseData) { let positiveScore = 0; let negativeScore = 0; // Get relevant planets const saturn = positions.find((p) => p.name === "Saturn"); const sun = positions.find((p) => p.name === "Sun"); const mars = positions.find((p) => p.name === "Mars"); const lotOfCareer = lots.find((l) => l.name === "Lot of Career"); if (!saturn || !sun || !mars) return 0.5; // Default score if key planets not found // Helper function to find which house a planet is in const findHouseForPlanet = (planetPosition) => { for (let i = 0; i < houses.length; i++) { const house = houses[i]; const nextHouse = houses[(i + 1) % houses.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; if (houseEnd < houseStart) { houseEnd += 360; } let planetPos = planetPosition; if (houseEnd > 360 && planetPos < houseStart) { planetPos += 360; } if (planetPos >= houseStart && planetPos < houseEnd) { return i + 1; } } return 0; }; // Helper function to check aspects between two planets const checkAspects = (planet1, planet2, aspectType) => { const diff = Math.abs(planet1.position - planet2.position); const adjustedDiff = diff > 180 ? 360 - diff : diff; const aspect = getAspectType(adjustedDiff); if (!aspect) return null; if (aspectType && aspect !== aspectType) return null; const target = { conjunction: 0, sextile: 60, square: 90, trine: 120, opposition: 180, }[aspect]; const deviation = Math.abs(adjustedDiff - target); const grade = deviation <= 1 ? "perfect" : deviation <= 3 ? "joining" : "assembly"; return { type: aspect, grade }; }; // Helper function to get aspect score const getAspectScore = (aspect, isPositive) => { if (!aspect) return 0; const baseScores = { trine: { perfect: 10, joining: 8, assembly: 6 }, // doubled sextile: { perfect: 6, joining: 4, assembly: 2 }, // doubled square: { perfect: -6, joining: -4, assembly: -2 }, // doubled opposition: { perfect: -10, joining: -8, assembly: -6 }, // doubled }; const score = baseScores[aspect.type]?.[aspect.grade] || 0; return isPositive ? Math.max(0, score) : Math.min(0, score); }; // Saturn dignities if (saturn.dignity === "Rulership") positiveScore += 6; // doubled if (saturn.dignity === "Exaltation") positiveScore += 4; // doubled if (saturn.dignity === "Fall") negativeScore += 6; // doubled if (saturn.dignity === "Detriment") negativeScore += 4; // doubled // Sun dignities if (sun.dignity === "Rulership") positiveScore += 6; // doubled if (sun.dignity === "Exaltation") positiveScore += 4; // doubled if (sun.dignity === "Fall") negativeScore += 6; // doubled if (sun.dignity === "Detriment") negativeScore += 4; // doubled // Mars dignities if (mars.dignity === "Rulership") positiveScore += 4; // doubled if (mars.dignity === "Exaltation") positiveScore += 2; // doubled if (mars.dignity === "Fall") negativeScore += 4; // doubled if (mars.dignity === "Detriment") negativeScore += 2; // doubled // House placements const saturnHouse = findHouseForPlanet(saturn.position); const sunHouse = findHouseForPlanet(sun.position); const marsHouse = findHouseForPlanet(mars.position); if (saturnHouse === 10) positiveScore += 6; // doubled if (sunHouse === 10) positiveScore += 4; // doubled if (marsHouse === 10 || marsHouse === 6) positiveScore += 2; // doubled if (saturnHouse === 6 || saturnHouse === 12) negativeScore += 4; // doubled // House cusp signs const house10Sign = houses[9].startSign; if (house10Sign === "Capricorn" || house10Sign === "Aquarius") positiveScore += 4; // doubled if (house10Sign === "Cancer" || house10Sign === "Leo") negativeScore += 4; // doubled // Lot of Career house placement if (lotOfCareer) { const lotHouse = findHouseForPlanet(lotOfCareer.position); if ([1, 4, 7, 10].includes(lotHouse)) positiveScore += 2; // doubled if ([3, 6, 9, 12].includes(lotHouse)) negativeScore += 2; // doubled } // Aspects between Saturn, Sun, and Mars const saturnSunAspect = checkAspects(saturn, sun); const saturnMarsAspect = checkAspects(saturn, mars); const sunMarsAspect = checkAspects(sun, mars); if (saturnSunAspect) { if (saturnSunAspect.type === "trine" || saturnSunAspect.type === "sextile") { positiveScore += getAspectScore(saturnSunAspect, true); } else if (saturnSunAspect.type === "square" || saturnSunAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(saturnSunAspect, false)); } } if (saturnMarsAspect) { if (saturnMarsAspect.type === "trine" || saturnMarsAspect.type === "sextile") { positiveScore += getAspectScore(saturnMarsAspect, true); } else if (saturnMarsAspect.type === "square" || saturnMarsAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(saturnMarsAspect, false)); } } if (sunMarsAspect) { if (sunMarsAspect.type === "trine" || sunMarsAspect.type === "sextile") { positiveScore += getAspectScore(sunMarsAspect, true); } else if (sunMarsAspect.type === "square" || sunMarsAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(sunMarsAspect, false)); } } // Planetary motion if (saturn.planetaryMotion === "direct") positiveScore += 2; // doubled if (sun.planetaryMotion === "direct") positiveScore += 2; // doubled if (saturn.planetaryMotion === "retrograde") negativeScore += 2; // doubled // Solar influence (combust only applies to planets that can be combust) if (mars.solarInfluence === "combust") negativeScore += 4; // doubled // Normalize scores const maxPositiveScore = 40; const maxNegativeScore = 30; const normalizedPositive = Math.min(positiveScore / maxPositiveScore, 1) * 0.5; const normalizedNegative = Math.min(negativeScore / maxNegativeScore, 1) * 0.5; const finalScore = Math.max(0, Math.min(1, 0.5 + normalizedPositive - normalizedNegative)); return Math.round(finalScore * 1000) / 1000; } // Helper function to calculate wealth score function calculateWealthScore(positions, houses, lots, moonPhaseData) { let positiveScore = 0; let negativeScore = 0; // Get relevant planets const jupiter = positions.find((p) => p.name === "Jupiter"); const venus = positions.find((p) => p.name === "Venus"); const moon = positions.find((p) => p.name === "Moon"); const lotOfFortune = lots.find((l) => l.name === "Lot of Fortune"); if (!jupiter || !venus || !moon) return 0.5; // Default score if key planets not found // Helper function to find which house a planet is in const findHouseForPlanet = (planetPosition) => { for (let i = 0; i < houses.length; i++) { const house = houses[i]; const nextHouse = houses[(i + 1) % houses.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; if (houseEnd < houseStart) { houseEnd += 360; } let planetPos = planetPosition; if (houseEnd > 360 && planetPos < houseStart) { planetPos += 360; } if (planetPos >= houseStart && planetPos < houseEnd) { return i + 1; } } return 0; }; // Helper function to check aspects between two planets const checkAspects = (planet1, planet2, aspectType) => { const diff = Math.abs(planet1.position - planet2.position); const adjustedDiff = diff > 180 ? 360 - diff : diff; const aspect = getAspectType(adjustedDiff); if (!aspect) return null; if (aspectType && aspect !== aspectType) return null; const target = { conjunction: 0, sextile: 60, square: 90, trine: 120, opposition: 180, }[aspect]; const deviation = Math.abs(adjustedDiff - target); const grade = deviation <= 1 ? "perfect" : deviation <= 3 ? "joining" : "assembly"; return { type: aspect, grade }; }; // Helper function to get aspect score const getAspectScore = (aspect, isPositive) => { if (!aspect) return 0; const baseScores = { trine: { perfect: 5, joining: 4, assembly: 3 }, sextile: { perfect: 3, joining: 2, assembly: 1 }, square: { perfect: -3, joining: -2, assembly: -1 }, opposition: { perfect: -5, joining: -4, assembly: -3 }, }; const score = baseScores[aspect.type]?.[aspect.grade] || 0; return isPositive ? Math.max(0, score) : Math.min(0, score); }; // Jupiter dignities if (jupiter.dignity === "Rulership") positiveScore += 6; // doubled if (jupiter.dignity === "Exaltation") positiveScore += 4; // doubled if (jupiter.dignity === "Fall") negativeScore += 6; // doubled if (jupiter.dignity === "Detriment") negativeScore += 4; // doubled // Venus dignities if (venus.dignity === "Rulership") positiveScore += 6; // doubled if (venus.dignity === "Exaltation") positiveScore += 4; // doubled if (venus.dignity === "Fall") negativeScore += 4; // doubled if (venus.dignity === "Detriment") negativeScore += 2; // doubled // Moon phase const moonPhase = moonPhaseData.phase; if (moonPhase > 0.5 && moonPhase < 1) positiveScore += 4; // doubled if (moonPhase === 1) positiveScore += 2; // doubled if (moonPhase === 0) negativeScore += 4; // doubled if (moonPhase < 0.5 && moonPhase > 0) negativeScore += 2; // doubled // House placements const jupiterHouse = findHouseForPlanet(jupiter.position); const venusHouse = findHouseForPlanet(venus.position); if (jupiterHouse === 2 || jupiterHouse === 8 || jupiterHouse === 11) positiveScore += 4; // doubled if (venusHouse === 2 || venusHouse === 8 || venusHouse === 11) positiveScore += 4; // doubled if (jupiterHouse === 3 || jupiterHouse === 6 || jupiterHouse === 9 || jupiterHouse === 12) negativeScore += 2; // doubled if (venusHouse === 3 || venusHouse === 6 || venusHouse === 9 || venusHouse === 12) negativeScore += 2; // doubled // Lot of Fortune house placement if (lotOfFortune) { const lotHouse = findHouseForPlanet(lotOfFortune.position); if ([1, 4, 7, 10].includes(lotHouse)) positiveScore += 2; // doubled if ([3, 6, 9, 12].includes(lotHouse)) negativeScore += 2; // doubled } // Aspects between Jupiter, Venus, and Moon const jupiterVenusAspect = checkAspects(jupiter, venus); const venusMoonAspect = checkAspects(venus, moon); const jupiterMoonAspect = checkAspects(jupiter, moon); if (jupiterVenusAspect) { if (jupiterVenusAspect.type === "trine" || jupiterVenusAspect.type === "sextile") { positiveScore += getAspectScore(jupiterVenusAspect, true); } else if (jupiterVenusAspect.type === "square" || jupiterVenusAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(jupiterVenusAspect, false)); } } if (venusMoonAspect) { if (venusMoonAspect.type === "trine" || venusMoonAspect.type === "sextile") { positiveScore += getAspectScore(venusMoonAspect, true); } else if (venusMoonAspect.type === "square" || venusMoonAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(venusMoonAspect, false)); } } if (jupiterMoonAspect) { if (jupiterMoonAspect.type === "trine" || jupiterMoonAspect.type === "sextile") { positiveScore += getAspectScore(jupiterMoonAspect, true); } else if (jupiterMoonAspect.type === "square" || jupiterMoonAspect.type === "opposition") { negativeScore += Math.abs(getAspectScore(jupiterMoonAspect, false)); } } // Planetary motion if (jupiter.planetaryMotion === "direct") positiveScore += 2; // doubled if (venus.planetaryMotion === "direct") positiveScore += 2; // doubled if (jupiter.planetaryMotion === "retrograde") negativeScore += 2; // doubled // Solar influence (combust only applies to planets that can be combust) if (jupiter.solarInfluence === "combust") negativeScore += 6; // doubled if (venus.solarInfluence === "combust") negativeScore += 6; // doubled // Normalize scores const maxPositiveScore = 40; const maxNegativeScore = 30; const normalizedPositive = Math.min(positiveScore / maxPositiveScore, 1) * 0.5; const normalizedNegative = Math.min(negativeScore / maxNegativeScore, 1) * 0.5; const finalScore = Math.max(0, Math.min(1, 0.5 + normalizedPositive - normalizedNegative)); return Math.round(finalScore * 1000) / 1000; } // Helper function to calculate magic score function calculateMagicScore(positions, houses, lots, moonPhaseData) { let positiveScore = 0; let negativeScore = 0; // Get relevant planets const mercury = positions.find((p) => p.name === "Mercury"); const moon = positions.find((p) => p.name === "Moon"); const neptune = positions.find((p) => p.name === "Neptune"); const lotOfSpirit = lots.find((l) => l.name === "Lot of Spirit"); if (!mercury || !moon) return 0.5; // Default score if key planets not found // Helper function to find which house a planet is in const findHouseForPlanet = (planetPosition) => { for (let i = 0; i < houses.length; i++) { const house = houses[i]; const nextHouse = houses[(i + 1) % houses.length]; let houseStart = house.startDegrees + getZodiacSignIndex(house.startSign) * 30; let houseEnd = nextHouse.startDegrees + getZodiacSignIndex(nextHouse.startSign) * 30; if (houseEnd < houseStart) { houseEnd += 360; } let planetPos = planetPosition; if (houseEnd > 360 && planetPos < houseStart) { planetPos += 360; } if (planetPos >= houseStart && planetPos < houseEnd) { return i + 1; } } return 0; }; // Helper function to check aspects between two planets const checkAspects = (planet1, planet2, aspectType) => { const diff = Math.abs(planet1.position - planet2.position); const adjustedDiff = diff > 180 ? 360 - diff : diff; const aspect = getAspectType(adjustedDiff); if (!aspect) return null; if (aspectType && aspect !== aspectType) return null; const target = { conjunction