public bool GetAscendingNode(out Vector3d asc) { var norm = CelestialBodyUtils.CrossProduct(orbitNormal, eclipticNormal); var s = CelestialBodyUtils.DotProduct(CelestialBodyUtils.CrossProduct(norm, semiMajorAxisBasis), orbitNormal) < 0; var ecc = 0d; var trueAnom = Vector3d.Angle(norm, centerPoint) * Mathd.Deg2Rad; if (eccentricity < 1) { var cosT = System.Math.Cos(trueAnom); ecc = System.Math.Acos((eccentricity + cosT) / (1d + eccentricity * cosT)); if (!s) { ecc = Mathd.PI_2 - ecc; } } else { trueAnom = Vector3d.Angle(-norm, centerPoint) * Mathd.Deg2Rad; if (trueAnom >= Mathd.Acos(-1d / eccentricity)) { asc = new Vector3d(); return(false); } var cosT = System.Math.Cos(trueAnom); ecc = CelestialBodyUtils.Acosh((eccentricity + cosT) / (1 + eccentricity * cosT)) * (!s ? -1 : 1); } asc = GetFocalPositionAtEccentricAnomaly(ecc); return(true); }
/// <summary> /// When first time called this method makes orbit clockwise, next time - oposite /// Orbit plane will be unchanged. /// </summary> public void MakeOrbitCircle() { if (attractor) { #if UNITY_EDITOR if (!Application.isPlaying) { FindReferences(); attractor.FindReferences(); Undo.RecordObject(this, "Round orbit"); } #endif var v = CelestialBodyUtils.CalcCircleOrbitVelocity( attractor._position, _position, attractor.mass, mass, orbitData.orbitNormal, simControlRef.gravitationalConstant ); if (relativeVelocity == v) { relativeVelocity = -v; } else { relativeVelocity = v; } orbitData.isDirty = true; } #if UNITY_EDITOR else { Debug.Log("SpaceGravity2D: Can't round orbit. " + name + " has no attractor"); } #endif }
/// <summary> /// Gets the descending node of orbit. /// </summary> /// <param name="desc">The desc.</param> /// <returns><c>true</c> if descending node exists, otherwise <c>false</c></returns> public bool GetDescendingNode(out Vector3d desc) { var norm = CelestialBodyUtils.CrossProduct(OrbitNormal, EclipticNormal); var s = CelestialBodyUtils.DotProduct(CelestialBodyUtils.CrossProduct(norm, SemiMajorAxisBasis), OrbitNormal) < 0; var ecc = 0d; var trueAnom = Vector3d.Angle(norm, -CenterPoint) * Mathd.Deg2Rad; if (Eccentricity < 1) { var cosT = Math.Cos(trueAnom); ecc = Math.Acos((Eccentricity + cosT) / (1d + Eccentricity * cosT)); if (s) { ecc = Mathd.PI_2 - ecc; } } else { trueAnom = Vector3d.Angle(norm, CenterPoint) * Mathd.Deg2Rad; if (trueAnom >= Mathd.Acos(-1d / Eccentricity)) { desc = new Vector3d(); return(false); } var cosT = Math.Cos(trueAnom); ecc = CelestialBodyUtils.Acosh((Eccentricity + cosT) / (1 + Eccentricity * cosT)) * (s ? -1 : 1); } desc = GetFocalPositionAtEccentricAnomaly(ecc); return(true); }
public void SetEccentricAnomaly(double e) { if (!isValidOrbit) { return; } e %= Mathd.PI_2; eccentricAnomaly = e; if (eccentricity < 1) { if (e < 0) { e = Mathd.PI_2 + e; } eccentricAnomaly = e; trueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(e, eccentricity); meanAnomaly = eccentricAnomaly - eccentricity * System.Math.Sin(eccentricAnomaly); } else { trueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(e, eccentricity); meanAnomaly = System.Math.Sinh(eccentricAnomaly) * eccentricity - eccentricAnomaly; } SetPositionByCurrentAnomaly(); SetVelocityByCurrentAnomaly(); }
/// <summary> /// Sets the true anomaly and updates all other anomalies. /// </summary> /// <param name="t">The t.</param> public void SetTrueAnomaly(double t) { if (!IsValidOrbit) { return; } t %= Mathd.PI_2; if (Eccentricity < 1) { if (t < 0) { t += Mathd.PI_2; } EccentricAnomaly = CelestialBodyUtils.ConvertTrueToEccentricAnomaly(t, Eccentricity); MeanAnomaly = EccentricAnomaly - Eccentricity * Math.Sin(EccentricAnomaly); } else { EccentricAnomaly = CelestialBodyUtils.ConvertTrueToEccentricAnomaly(t, Eccentricity); MeanAnomaly = Math.Sinh(EccentricAnomaly) * Eccentricity - EccentricAnomaly; } SetPositionByCurrentAnomaly(); SetVelocityByCurrentAnomaly(); }
/// <summary> /// Sets the eccentric anomaly and updates all other anomalies. /// </summary> /// <param name="e">The e.</param> public void SetEccentricAnomaly(double e) { if (!IsValidOrbit) { return; } e %= Mathd.PI_2; EccentricAnomaly = e; if (Eccentricity < 1) { if (e < 0) { e = Mathd.PI_2 + e; } EccentricAnomaly = e; TrueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(e, Eccentricity); MeanAnomaly = EccentricAnomaly - Eccentricity * Math.Sin(EccentricAnomaly); } else { TrueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(e, Eccentricity); MeanAnomaly = Math.Sinh(EccentricAnomaly) * Eccentricity - EccentricAnomaly; } SetPositionByCurrentAnomaly(); SetVelocityByCurrentAnomaly(); }
private void DrawOrbitInEditorFor(CelestialBody body) { int pointsCount = _simControl.SceneElementsDisplayParameters.OrbitPointsCount; if (body.isActiveAndEnabled) { if (!Application.isPlaying && body.AttractorRef != null && body.OrbitData.IsDirty) { if (body.AttractorRef.Mass <= 0) { body.AttractorRef.Mass = 1e-007;//to avoid div by zero } body.CalculateNewOrbitData(); } Handles.color = Color.white; Vector3d[] points = null; body.GetOrbitPointsNoAlloc(ref points, pointsCount, false, (float)_simControl.SceneElementsDisplayParameters.MaxOrbitDistance); for (int i = 1; i < points.Length; i++) { Handles.DrawLine((Vector3)points[i - 1], (Vector3)points[i]); } if (_simControl.SceneElementsDisplayParameters.DrawOrbitsEclipticProjection && points.Length > 0) { var point1 = points[0] - _simControl.EclipticNormal * CelestialBodyUtils.DotProduct(points[0], _simControl.EclipticNormal); var point2 = Vector3d.zero; Handles.color = Color.gray; for (int i = 1; i < points.Length; i++) { point2 = points[i] - _simControl.EclipticNormal * CelestialBodyUtils.DotProduct(points[i], _simControl.EclipticNormal); Handles.DrawLine((Vector3)point1, (Vector3)point2); point1 = point2; } } } }
/// <summary> /// Orbit plane will be unchanged. /// </summary> public void MakeOrbitCircle(bool clockwise) { if (attractor) { #if UNITY_EDITOR if (!Application.isPlaying) { FindReferences(); attractor.FindReferences(); Undo.RecordObject(this, "Round orbit"); } #endif var dotProduct = CelestialBodyUtils.DotProduct(orbitData.orbitNormal, simControlRef.eclipticNormal); //sign of this value determines orbit orientation if (Mathd.Abs(orbitData.orbitNormal.sqrMagnitude - 1d) > 0.5d) { orbitData.orbitNormal = simControlRef.eclipticNormal; } var v = CelestialBodyUtils.CalcCircleOrbitVelocity( attractor._position, _position, attractor.mass, mass, orbitData.orbitNormal * ( clockwise && dotProduct >= 0 || !clockwise && dotProduct < 0 ? 1 : -1 ), simControlRef.gravitationalConstant ); if (relativeVelocity != v) { relativeVelocity = v; orbitData.isDirty = true; } } }
public void SetTrueAnomaly(double t) { if (!isValidOrbit) { return; } t %= Mathd.PI_2; if (eccentricity < 1) { if (t < 0) { t += Mathd.PI_2; } eccentricAnomaly = CelestialBodyUtils.ConvertTrueToEccentricAnomaly(t, eccentricity); meanAnomaly = eccentricAnomaly - eccentricity * System.Math.Sin(eccentricAnomaly); } else { eccentricAnomaly = CelestialBodyUtils.ConvertTrueToEccentricAnomaly(t, eccentricity); meanAnomaly = System.Math.Sinh(eccentricAnomaly) * eccentricity - eccentricAnomaly; } SetPositionByCurrentAnomaly(); SetVelocityByCurrentAnomaly(); }
/// <summary> /// Draw two crossing lines in scene view. /// </summary> private static void DrawX(Vector3 pos, float size, Color col, Vector3 normal, Vector3 up) { Handles.color = col; Vector3 right = CelestialBodyUtils.CrossProduct(up, normal).normalized; Handles.DrawLine(pos + up * size, pos - up * size); Handles.DrawLine(pos + right * size, pos - right * size); }
public static Vector3[] CalcOrbitPoints(Vector3 attractorPos, Vector3 bodyPos, double attractorMass, double bodyMass, Vector3 relVelocity, double gConst, int pointsCount) { if (pointsCount < 3 || pointsCount > 10000) { return(new Vector3[0]); } var focusPoint = CalcCenterOfMass(attractorPos, attractorMass, bodyPos, bodyMass); var radiusVector = bodyPos - focusPoint; var radiusVectorMagnitude = radiusVector.magnitude; var orbitNormal = CelestialBodyUtils.CrossProduct(radiusVector, relVelocity); var MG = (attractorMass + bodyMass) * gConst; var eccVector = CelestialBodyUtils.CrossProduct(relVelocity, orbitNormal) / (float)MG - radiusVector / radiusVectorMagnitude; var focalParameter = orbitNormal.sqrMagnitude / MG; var eccentricity = eccVector.magnitude; var minorAxisNormal = -CelestialBodyUtils.CrossProduct(orbitNormal, eccVector).normalized; var majorAxisNormal = -CelestialBodyUtils.CrossProduct(orbitNormal, minorAxisNormal).normalized; orbitNormal.Normalize(); double orbitCompressionRatio; double semiMajorAxys; double semiMinorAxys; Vector3 relFocusPoint; Vector3 centerPoint; if (eccentricity < 1) { orbitCompressionRatio = 1 - eccentricity * eccentricity; semiMajorAxys = focalParameter / orbitCompressionRatio; semiMinorAxys = semiMajorAxys * System.Math.Sqrt(orbitCompressionRatio); relFocusPoint = (float)semiMajorAxys * eccVector; centerPoint = focusPoint - relFocusPoint; } else { orbitCompressionRatio = eccentricity * eccentricity - 1f; semiMajorAxys = focalParameter / orbitCompressionRatio; semiMinorAxys = semiMajorAxys * System.Math.Sqrt(orbitCompressionRatio); relFocusPoint = -(float)semiMajorAxys * eccVector; centerPoint = focusPoint - relFocusPoint; } var points = new Vector3[pointsCount]; double eccVar = 0f; for (int i = 0; i < pointsCount; i++) { Vector3 result = eccentricity < 1 ? new Vector3((float)(System.Math.Sin(eccVar) * semiMinorAxys), -(float)(System.Math.Cos(eccVar) * semiMajorAxys)) : new Vector3((float)(System.Math.Sinh(eccVar) * semiMinorAxys), (float)(System.Math.Cosh(eccVar) * semiMajorAxys)); eccVar += Mathf.PI * 2f / (float)(pointsCount - 1); points[i] = minorAxisNormal * result.x + majorAxisNormal * result.y + centerPoint; } return(points); }
private void DrawInclinationMarkForBody(CelestialBody body, float scale) { var norm = CelestialBodyUtils.CrossProduct((Vector3)body.OrbitData.OrbitNormal, (Vector3)_simControl.EclipticNormal); Handles.color = Color.white; var p = CelestialBodyUtils.CrossProduct(norm, (Vector3)_simControl.EclipticNormal).normalized; Handles.DrawLine((Vector3)body.OrbitFocusPoint, (Vector3)body.OrbitFocusPoint + p * 3f * scale); Handles.DrawLine((Vector3)body.OrbitFocusPoint, (Vector3)body.OrbitFocusPoint + CelestialBodyUtils.RotateVectorByAngle(p, (float)body.OrbitData.Inclination, -norm.normalized) * 3f * scale); Handles.DrawWireArc((Vector3)body.OrbitFocusPoint, -norm, p, (float)(body.OrbitData.Inclination * Mathd.Rad2Deg), 1f * scale); DrawLabelScaled((Vector3)body.OrbitFocusPoint + p * scale, (body.OrbitData.Inclination * Mathd.Rad2Deg).ToString("0") + "\u00B0", Color.white, 10); }
public void SetAutoCircleOrbit() { if (AttractorObjectRef != null) { OrbitData.Velocity = CelestialBodyUtils.CalcCircleOrbitVelocity(Vector3d.zero, OrbitData.Position, OrbitData.AttractorMass, 1f, OrbitData.OrbitNormal, OrbitData.GravitationalConstant); OrbitData.CalculateNewOrbitData(); if (VelocityHandleRef != null) { VelocityHandleRef.position = transform.position + (Vector3)OrbitData.Velocity; } } }
/// <summary> /// Return ratio of perturbation force from third body relative to attraction force of mainAttractor /// </summary> /// <param name="targetBody"></param> /// <param name="mainAttractor"></param> /// <param name="perturbatingAttractor"></param> public static double RelativePerturbationRatio(CelestialBody targetBody, CelestialBody mainAttractor, CelestialBody perturbatingAttractor) { double mainAcceleration = CelestialBodyUtils.AccelerationByAttractionForce( targetBody.position, mainAttractor.position, mainAttractor.MG).magnitude; double perturbAcceleration = CelestialBodyUtils.AccelerationByAttractionForce( targetBody.position, perturbatingAttractor.position, perturbatingAttractor.MG).magnitude; return(perturbAcceleration / mainAcceleration); }
public static Vector3 GetRayPlaneIntersectionPoint(Vector3 pointOnPlane, Vector3 normal, Vector3 rayOrigin, Vector3 rayDirection) { var dotProd = CelestialBodyUtils.DotProduct(rayDirection, normal); if (Mathd.Abs(dotProd) < 1e-5) { return(new Vector3()); } var p = rayOrigin + rayDirection * CelestialBodyUtils.DotProduct((pointOnPlane - rayOrigin), normal) / dotProd; p = p - normal * CelestialBodyUtils.DotProduct(p - pointOnPlane, normal); //projection. for better precision return(p); }
/// <summary> /// Get orbit curve points without array allocation, if current orbit state is valid. /// </summary> /// <remarks> /// Note: array allocation may sometimes occur, if specified array is null or lenght is not equal to target points count. /// </remarks> /// <param name="points">Resulting orbit curve array.</param> /// <param name="pointsCount">Max orbit curve points count.</param> /// <param name="origin">World position of attractor (focus of orbit).</param> /// <param name="maxDistance">Max distance for orbit curve points.</param> public void GetOrbitPointsNoAlloc(ref Vector3d[] points, int pointsCount, Vector3d origin, double maxDistance = 1000d) { if (pointsCount < 2) { points = new Vector3d[0]; return; } if (Eccentricity < 1) { if (points == null || points.Length != pointsCount) { points = new Vector3d[pointsCount]; } if (ApoapsisDistance < maxDistance) { for (var i = 0; i < pointsCount; i++) { points[i] = GetFocalPositionAtEccentricAnomaly(i * Mathd.PI_2 / (pointsCount - 1d)) + origin; } } else { var maxAngle = CelestialBodyUtils.CalcTrueAnomalyForDistance(maxDistance, Eccentricity, SemiMajorAxis); for (int i = 0; i < pointsCount; i++) { points[i] = GetFocalPositionAtTrueAnomaly(-maxAngle + i * 2d * maxAngle / (pointsCount - 1)) + origin; } } } else { if (maxDistance < PeriapsisDistance) { points = new Vector3d[0]; return; } if (points == null || points.Length != pointsCount) { points = new Vector3d[pointsCount]; } var maxAngle = CelestialBodyUtils.CalcTrueAnomalyForDistance(maxDistance, Eccentricity, SemiMajorAxis); for (int i = 0; i < pointsCount; i++) { points[i] = GetFocalPositionAtTrueAnomaly(-maxAngle + i * 2d * maxAngle / (pointsCount - 1)) + origin; } } }
/// <summary> /// Calculate and apply n-body gravitational acceleration to target body, using algorythm Runge-Kutta. /// In result body will change it's velocity according to global gravity. /// </summary> /// <param name="body">Target body.</param> /// <param name="dt">Delta time.</param> /// <param name="minRange">Minimal attraction range for attractors.</param> /// <param name="maxRange">Maximal attraction range for attractors.</param> /// <param name="allAttractors">List of all attractors on scene.</param> public static void CalcAccelerationRungeKuttaForBody(CelestialBody body, double dt, double minRange, double maxRange, List <CelestialBody> allAttractors) { Vector3d result = Vector3d.zero; body._position += body.Velocity * (dt / 2d); for (int i = 0; i < allAttractors.Count; i++) { if (allAttractors[i] == body) { continue; } var t1 = CelestialBodyUtils.AccelerationByAttractionForce( body._position, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ) * dt; var t2 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t1 * 0.5d, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ) * dt; var t3 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t2 * 0.5d, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ) * dt; var t4 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t3, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ) * dt; result += new Vector3d( (t1.x + t2.x * 2d + t3.x * 2d + t4.x) / 6d, (t1.y + t2.y * 2d + t3.y * 2d + t4.y) / 6d, (t1.z + t2.z * 2d + t3.z * 2d + t4.z) / 6d); } if (!result.isZero) { body.Velocity += result; } }
public CelestialBody FindMostProperAttractor(CelestialBody body) { if (body == null) { return(null); } CelestialBody resultAttractor = null; #if UNITY_EDITOR if (!Application.isPlaying) { bodies = new List <CelestialBody>(GameObject.FindObjectsOfType <CelestialBody>()); } #endif // Search logic: // calculate mutual perturbation for every pair of attractors in scene and select one, // which attracts the body with biggest force and is least affected by others. foreach (var otherBody in bodies) { if (otherBody == body || !otherBody.isActiveAndEnabled || otherBody.mass < minAttractorMass || (otherBody.position - body.position).magnitude > Mathd.Min(maxAttractionRange, otherBody.maxAttractionRange)) { continue; } #if UNITY_EDITOR if (!Application.isPlaying) { otherBody.FindReferences(); } #endif if (resultAttractor == null) { resultAttractor = otherBody; } else if (CelestialBodyUtils.RelativePerturbationRatio(body, resultAttractor, otherBody) > CelestialBodyUtils.RelativePerturbationRatio(body, otherBody, resultAttractor)) { resultAttractor = otherBody; } } #if UNITY_EDITOR if (!Application.isPlaying) { bodies.Clear(); //bodies must be empty in editor mode } #endif return(resultAttractor); }
/// <summary> /// Draw simple arrow in scene window at given world coordinates /// </summary> private static void DrawArrow(Vector3 from, Vector3 to, Color col, Vector3 normal) { var dir = to - from; float dist = dir.magnitude; var dirNorm = dir / dist; //normalized vector float headSize = dist / 6f; var _colBefore = Handles.color; Handles.color = col; Vector3 sideNormal = CelestialBodyUtils.CrossProduct(dir, normal).normalized; Handles.DrawLine(from, from + dirNorm * (dist - headSize)); Handles.DrawLine(from + dirNorm * (dist - headSize) + sideNormal * headSize / 2f, from + dirNorm * (dist - headSize) - sideNormal * headSize / 2f); Handles.DrawLine(from + dirNorm * (dist - headSize) + sideNormal * headSize / 2f, from + dir); Handles.DrawLine(from + dirNorm * (dist - headSize) - sideNormal * headSize / 2f, from + dir); Handles.color = _colBefore; }
public void CalcAccelerationRungeKuttaForBody(CelestialBody body, double dt) { Vector3d result = Vector3d.zero; body._position += body.velocity * (dt / 2d); for (int i = 0; i < attractorsCache.Count; i++) { if (attractorsCache[i] == body) { continue; } var t1 = CelestialBodyUtils.AccelerationByAttractionForce( body._position, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ) * dt; var t2 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t1 * 0.5d, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ) * dt; var t3 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t2 * 0.5d, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ) * dt; var t4 = CelestialBodyUtils.AccelerationByAttractionForce( body._position + t3, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ) * dt; result += new Vector3d( (t1.x + t2.x * 2d + t3.x * 2d + t4.x) / 6d, (t1.y + t2.y * 2d + t3.y * 2d + t4.y) / 6d, (t1.z + t2.z * 2d + t3.z * 2d + t4.z) / 6d); } body.velocity += result; }
/// <summary> /// Updates the value of orbital anomalies by defined deltatime. /// </summary> /// <param name="deltaTime">The delta time.</param> /// <remarks> /// Only anomalies values will be changed. /// Position and velocity states needs to be updated too after this method call. /// </remarks> public void UpdateOrbitAnomaliesByTime(double deltaTime) { if (Eccentricity < 1) { if (Period > 1e-5) { MeanAnomaly += Mathd.PI_2 * deltaTime / Period; } MeanAnomaly %= Mathd.PI_2; if (MeanAnomaly < 0) { MeanAnomaly = Mathd.PI_2 - MeanAnomaly; } EccentricAnomaly = CelestialBodyUtils.KeplerSolver(MeanAnomaly, Eccentricity); var cosE = Math.Cos(EccentricAnomaly); TrueAnomaly = Math.Acos((cosE - Eccentricity) / (1 - Eccentricity * cosE)); if (MeanAnomaly > Mathd.PI) { TrueAnomaly = Mathd.PI_2 - TrueAnomaly; } if (double.IsNaN(MeanAnomaly) || double.IsInfinity(MeanAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) MEAN ANOMALY"); //litle paranoya Debug.Break(); } if (double.IsNaN(EccentricAnomaly) || double.IsInfinity(EccentricAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) ECC ANOMALY"); Debug.Break(); } if (double.IsNaN(TrueAnomaly) || double.IsInfinity(TrueAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) TRUE ANOMALY"); Debug.Break(); } } else { double n = Math.Sqrt(AttractorMass * GravitationalConstant / Math.Pow(SemiMajorAxis, 3)); MeanAnomaly = MeanAnomaly + n * deltaTime; EccentricAnomaly = CelestialBodyUtils.KeplerSolverHyperbolicCase(MeanAnomaly, Eccentricity); TrueAnomaly = Math.Atan2(Math.Sqrt(Eccentricity * Eccentricity - 1.0) * Math.Sinh(EccentricAnomaly), Eccentricity - Math.Cosh(EccentricAnomaly)); } }
/// <summary> /// Find attractor, which have most gravitational influence at target body. /// </summary> /// <param name="body">Target body.</param> /// <returns>Most proper attractor or null.</returns> /// <remarks> /// Search logic: /// Calculate mutual perturbation for every pair of attractors in scene and select one, /// which attracts the body with biggest force and is least affected by others. /// </remarks> public CelestialBody FindMostProperAttractor(CelestialBody body) { if (body == null) { return(null); } CelestialBody resultAttractor = null; #if UNITY_EDITOR if (!Application.isPlaying) { Bodies = new List <CelestialBody>(GameObject.FindObjectsOfType <CelestialBody>()); } #endif foreach (var otherBody in Bodies) { if (otherBody == body || !otherBody.isActiveAndEnabled || otherBody.Mass < MinAttractorMass || (otherBody.Position - body.Position).magnitude > Mathd.Min(MaxAttractionRange, otherBody.MaxAttractionRange)) { continue; } #if UNITY_EDITOR if (!Application.isPlaying) { otherBody.FindReferences(); } #endif if (resultAttractor == null) { resultAttractor = otherBody; } else if (CelestialBodyUtils.RelativePerturbationRatio(body, resultAttractor, otherBody) > CelestialBodyUtils.RelativePerturbationRatio(body, otherBody, resultAttractor)) { resultAttractor = otherBody; } } #if UNITY_EDITOR if (!Application.isPlaying) { Bodies.Clear(); //bodies must be empty in editor mode } #endif return(resultAttractor); }
public void UpdateOrbitAnomaliesByTime(double deltaTime) { if (eccentricity < 1) { if (period > 1e-5) { meanAnomaly += Mathd.PI_2 * deltaTime / period; } meanAnomaly %= Mathd.PI_2; if (meanAnomaly < 0) { meanAnomaly = Mathd.PI_2 - meanAnomaly; } eccentricAnomaly = CelestialBodyUtils.KeplerSolver(meanAnomaly, eccentricity); var cosE = System.Math.Cos(eccentricAnomaly); trueAnomaly = System.Math.Acos((cosE - eccentricity) / (1 - eccentricity * cosE)); if (meanAnomaly > Mathd.PI) { trueAnomaly = Mathd.PI_2 - trueAnomaly; } if (double.IsNaN(meanAnomaly) || double.IsInfinity(meanAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) MEAN ANOMALY"); //litle paranoya Debug.Break(); } if (double.IsNaN(eccentricAnomaly) || double.IsInfinity(eccentricAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) ECC ANOMALY"); Debug.Break(); } if (double.IsNaN(trueAnomaly) || double.IsInfinity(trueAnomaly)) { Debug.Log("SpaceGravity2D: NaN(INF) TRUE ANOMALY"); Debug.Break(); } } else { double n = System.Math.Sqrt(attractorMass * gravConst / System.Math.Pow(semiMajorAxis, 3)) * Mathd.Sign(orbitNormalDotEclipticNormal); meanAnomaly = meanAnomaly - n * deltaTime; eccentricAnomaly = CelestialBodyUtils.KeplerSolverHyperbolicCase(meanAnomaly, eccentricity); trueAnomaly = System.Math.Atan2(System.Math.Sqrt(eccentricity * eccentricity - 1.0) * System.Math.Sinh(eccentricAnomaly), eccentricity - System.Math.Cosh(eccentricAnomaly)); } }
public void ProjectOntoEclipticPlane() { #if UNITY_EDITOR if (!Application.isPlaying) { Undo.RecordObject(this, "ecliptic projection"); Undo.RecordObject(transformRef, "ecliptic projection"); } #endif var projectedPos = _position - simControlRef.eclipticNormal * CelestialBodyUtils.DotProduct(_position, simControlRef.eclipticNormal); var projectedV = velocity - simControlRef.eclipticNormal * CelestialBodyUtils.DotProduct(velocity, simControlRef.eclipticNormal); _position = projectedPos; transformRef.position = (Vector3)projectedPos; velocity = projectedV; orbitData.isDirty = true; #if UNITY_EDITOR if (!Application.isPlaying) { EditorUtility.SetDirty(this); } #endif }
public void CalcAccelerationEulerForBody(CelestialBody body, double dt) { Vector3d result = Vector3d.zero; for (int i = 0; i < attractorsCache.Count; i++) { if (attractorsCache[i] == body) { continue; } result += CelestialBodyUtils.AccelerationByAttractionForce( body.position, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ); } body.velocity += result * dt; }
public Vector3d CalcAccelerationEulerInPoint(Vector3d pos) { Vector3d result = new Vector3d(); for (int i = 0; i < attractorsCache.Count; i++) { if (attractorsCache[i].position == pos) { continue; } result += CelestialBodyUtils.AccelerationByAttractionForce( pos, attractorsCache[i].position, attractorsCache[i].MG, minAttractionRange, Mathd.Min(maxAttractionRange, attractorsCache[i].maxAttractionRange) ); } return(result); }
/// <summary> /// Calculate n-body gravitational acceleration at specified world point, using Euler algorythm. /// </summary> /// <param name="pos">World position.</param> /// <param name="minRange">Minimal attraction range for attractors.</param> /// <param name="maxRange">Maximal attraction range for attractors.</param> /// <param name="allAttractors">List of all attractors on scene.</param> /// <returns>Sum of accelerations vectors at position.</returns> public static Vector3d CalcAccelerationEulerInPoint(Vector3d pos, double minRange, double maxRange, List <CelestialBody> allAttractors) { Vector3d result = new Vector3d(); for (int i = 0; i < allAttractors.Count; i++) { if (allAttractors[i].Position == pos) { continue; } result += CelestialBodyUtils.AccelerationByAttractionForce( pos, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ); } return(result); }
public Vector3[] GetOrbitPoints(int pointsCount, Vector3 origin, float maxDistance = 1000f) { if (pointsCount < 2) { return(new Vector3[0]); } var result = new Vector3[pointsCount]; if (eccentricity < 1) { if (apoapsisDistance < maxDistance) { for (var i = 0; i < pointsCount; i++) { result[i] = (Vector3)GetFocalPositionAtEccentricAnomaly(i * Mathd.PI_2 / (pointsCount - 1d)) + origin; } } else { var maxAngle = CelestialBodyUtils.CalcTrueAnomalyForDistance(maxDistance, eccentricity, semiMajorAxis); for (int i = 0; i < pointsCount; i++) { result[i] = (Vector3)GetFocalPositionAtTrueAnomaly(-maxAngle + i * 2d * maxAngle / (pointsCount - 1)) + origin; } } } else { if (maxDistance < periapsisDistance) { return(new Vector3[0]); } var maxAngle = CelestialBodyUtils.CalcTrueAnomalyForDistance(maxDistance, eccentricity, semiMajorAxis); for (int i = 0; i < pointsCount; i++) { result[i] = (Vector3)GetFocalPositionAtTrueAnomaly(-maxAngle + i * 2d * maxAngle / (pointsCount - 1)) + origin; } } return(result); }
/// <summary> /// Calculate and apply n-body gravitational acceleration to target body, using Euler algorythm. /// In result body will change it's velocity according to global gravity. /// </summary> /// <param name="body">Target body.</param> /// <param name="dt">Delta time.</param> /// <param name="minRange">Minimal attraction range for attractors.</param> /// <param name="maxRange">Maximal attraction range for attractors.</param> /// <param name="allAttractors">List of all attractors on scene.</param> public static void CalcAccelerationEulerForBody(CelestialBody body, double dt, double minRange, double maxRange, List <CelestialBody> allAttractors) { Vector3d result = Vector3d.zero; for (int i = 0; i < allAttractors.Count; i++) { if (object.ReferenceEquals(allAttractors[i], body)) { continue; } result += CelestialBodyUtils.AccelerationByAttractionForce( body.Position, allAttractors[i].Position, allAttractors[i].MG, minRange, Mathd.Min(maxRange, allAttractors[i].MaxAttractionRange) ); } if (!result.isZero) { body.Velocity += result * dt; } }
/// <summary> /// Sets the mean anomaly and updates all other anomalies. /// </summary> /// <param name="m">The m.</param> public void SetMeanAnomaly(double m) { if (!IsValidOrbit) { return; } MeanAnomaly = m % Mathd.PI_2; if (Eccentricity < 1) { if (MeanAnomaly < 0) { MeanAnomaly += Mathd.PI_2; } EccentricAnomaly = CelestialBodyUtils.KeplerSolver(MeanAnomaly, Eccentricity); TrueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(EccentricAnomaly, Eccentricity); } else { EccentricAnomaly = CelestialBodyUtils.KeplerSolverHyperbolicCase(MeanAnomaly, Eccentricity); TrueAnomaly = CelestialBodyUtils.ConvertEccentricToTrueAnomaly(EccentricAnomaly, Eccentricity); } SetPositionByCurrentAnomaly(); SetVelocityByCurrentAnomaly(); }