/// <summary> /// Set the integrator required for the chosen algorithm /// </summary> /// <param name="algorithm"></param> public void SetAlgorithmAndForce(GravityEngine.Algorithm algorithm, IForceDelegate forceDelegate) { this.forceDelegate = forceDelegate; // cast may be null if no force selection if (forceDelegate is SelectiveForceBase) { selectiveForce = (SelectiveForceBase)forceDelegate; } switch (algorithm) { case GravityEngine.Algorithm.LEAPFROG: integrator = new LeapFrogIntegrator(forceDelegate); break; //case GravityEngine.Algorithm.LEAPFROG_JOB: // integrator = new LeapFrogJob(forceDelegate); // break; case GravityEngine.Algorithm.HERMITE8: integrator = new HermiteIntegrator(forceDelegate); break; case GravityEngine.Algorithm.AZTRIPLE: integrator = new AZTripleIntegrator(); break; default: Debug.LogError("Unknown algortithm"); break; } }
public HermiteIntegrator(IForceDelegate force) { #pragma warning disable 162 // disable unreachable code warning if (GravityEngine.DEBUG) { Debug.Log("Instantiated with " + force); } #pragma warning restore 162 forceDelegate = force; }
/// <summary> /// Initializes a new instance of the <see cref="LeapFrogIntegrator"/> class. /// An optional force delegate can be provided if non-Newtonian gravity is /// desired. /// </summary> /// <param name="force">Force.</param> public LeapFrogIntegrator(IForceDelegate force) { #pragma warning disable 162 // disable unreachable code warning if (GravityEngine.DEBUG) { Debug.Log("Leapfrog instantiated with force= " + force); } #pragma warning restore 162 forceDelegate = force; }
// Update is called once per frame void Update() { if (!setup) { setup = true; IForceDelegate force = GravityEngine.Instance().GetForceDelegate(); if (force != null) { SelectiveForceBase selectiveForce = (SelectiveForceBase)force; if (selectiveForce != null) { selectiveForce.ForceSelection(a, b, false); } } } }
public static IForceDelegate InstantiateForce(Forces force, GameObject host) { IForceDelegate forceD = null; switch (force) { case Forces.Gravity: // leave as null - GE has gravity built-in break; case Forces.InverseR: forceD = new InverseR(); break; case Forces.InverseR3: forceD = new InverseR3(); break; case Forces.ForceR: forceD = new ForceR(); break; case Forces.ForceR2: forceD = new ForceR2(); break; case Forces.Custom: forceD = host.GetComponent <IForceDelegate>(); if (forceD == null) { Debug.LogError("Custom IForceDelegate is not attached to " + host.name); } break; default: Debug.LogError("Unknown force (was it added to ForceChooser? => " + force); break; } return(forceD); }
public override void OnInspectorGUI() { GUI.changed = false; GravityEngine gravityEngine = (GravityEngine)target; float massScale = gravityEngine.massScale; float timeScale = gravityEngine.timeScale; float lengthScale = gravityEngine.lengthScale; float physToWorldFactor = gravityEngine.physToWorldFactor; bool optimizeMassless = gravityEngine.optimizeMassless; bool detectNbodies = gravityEngine.detectNbodies; bool evolveAtStart = gravityEngine.evolveAtStart; bool scaling = gravityEngine.editorShowScale; bool showAdvanced = gravityEngine.editorShowAdvanced; bool showTrajectory = gravityEngine.editorShowTrajectory; bool cmFoldout = gravityEngine.editorCMfoldout; bool mapToScene = gravityEngine.mapToScene; bool trajectoryPrediction = gravityEngine.trajectoryPrediction; float trajectoryTime = gravityEngine.trajectoryTime; GameObject trajCanvas = gravityEngine.trajectoryCanvas; GameObject markerParent = gravityEngine.markerParent; float computeFactor = gravityEngine.trajectoryComputeFactor; int stepsPerFrame = gravityEngine.stepsPerFrame; int particleStepsPerFrame = gravityEngine.particleStepsPerFrame; GravityEngine.Algorithm algorithm = GravityEngine.Algorithm.LEAPFROG; ForceChooser.Forces force = ForceChooser.Forces.Gravity; GravityScaler.Units units = GravityScaler.Units.DIMENSIONLESS; EditorGUIUtility.labelWidth = 200; mapToScene = EditorGUILayout.Toggle(new GUIContent("Use Transform to Reposition", mapTip), mapToScene); EditorGUIUtility.labelWidth = 150; // SCALING scaling = EditorGUILayout.Foldout(scaling, "Scaling"); if (scaling) { units = (GravityScaler.Units)EditorGUILayout.EnumPopup(new GUIContent("Units", unitsTip), gravityEngine.units); EditorGUILayout.LabelField("Scaling values change when <ENTER> ispressed."); switch (units) { case GravityScaler.Units.DIMENSIONLESS: // only have mass scale in DL case EditorGUILayout.LabelField("Use mass scale to control scene speed."); EditorGUILayout.LabelField("(More massive = faster)"); massScale = EditorGUILayout.DelayedFloatField(new GUIContent("Mass Scale", mTip), gravityEngine.massScale); break; case GravityScaler.Units.SI: // no mass scale is controlled by time exclusivly EditorGUILayout.LabelField("m/kg/sec."); // meters per Unity unit in the case of meters lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per m", timeTip), gravityEngine.lengthScale); timeScale = EditorGUILayout.DelayedFloatField(new GUIContent("Game sec. per sec.", timeTip), gravityEngine.timeScale); break; case GravityScaler.Units.ORBITAL: EditorGUILayout.LabelField("km/1E24 kg/hr"); // Express in km per Unity unit km/U lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per km", timeTip), gravityEngine.lengthScale); timeScale = EditorGUILayout.DelayedFloatField(new GUIContent("Game sec. per hour", timeTip), gravityEngine.timeScale); break; case GravityScaler.Units.SOLAR: EditorGUILayout.LabelField("AU/1E24 kg/year"); lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per AU", timeTip), gravityEngine.lengthScale); timeScale = EditorGUILayout.DelayedFloatField(new GUIContent("Game Sec per year", timeTip), gravityEngine.timeScale); break; } } // TRAJECTORY PREDICTION showTrajectory = EditorGUILayout.Foldout(showTrajectory, "Trajectory Prediction"); if (showTrajectory) { trajectoryPrediction = EditorGUILayout.Toggle(new GUIContent("Trajectory Prediction", trajTip), trajectoryPrediction); trajectoryTime = EditorGUILayout.FloatField(new GUIContent("Trajectory Time", trajTimeTip), trajectoryTime); computeFactor = EditorGUILayout.FloatField(new GUIContent("Re-computes per frame)", trajResetTip), computeFactor); trajCanvas = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Canvas for Text (optional)", trajCanvasTip), trajCanvas, typeof(GameObject), true); markerParent = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Time Marker Parent (optional)", markerTip), markerParent, typeof(GameObject), true); } // ADVANCED showAdvanced = EditorGUILayout.Foldout(showAdvanced, "Advanced"); if (showAdvanced) { algorithm = (GravityEngine.Algorithm)EditorGUILayout.EnumPopup(new GUIContent("Algorithm", algoTip), gravityEngine.algorithm); // Force selection is tangled with choice of integrator and scale - so needs to be here force = (ForceChooser.Forces)EditorGUILayout.EnumPopup(new GUIContent("Force", forceTip), gravityEngine.force); if (force == ForceChooser.Forces.Custom) { IForceDelegate force_delegate = gravityEngine.GetComponent <IForceDelegate>(); if (force_delegate == null) { EditorGUILayout.LabelField(" Attach a Force Delegate to this object.", EditorStyles.boldLabel); } else { EditorGUILayout.LabelField(" Force delegate: " + force_delegate.GetType()); } } if (force != ForceChooser.Forces.Gravity) { EditorGUILayout.LabelField(" Note: Orbit predictors assume Newtonian gravity"); EditorGUILayout.LabelField(" They are not accurate for other forces."); } optimizeMassless = EditorGUILayout.Toggle(new GUIContent("Optimize Massless Bodies", mlessTip), gravityEngine.optimizeMassless); detectNbodies = EditorGUILayout.Toggle(new GUIContent("Automatically Add NBody objects", autoAddTip), gravityEngine.detectNbodies); evolveAtStart = EditorGUILayout.Toggle(new GUIContent("Evolve at Start", autoStartTip), gravityEngine.evolveAtStart); physToWorldFactor = EditorGUILayout.FloatField(new GUIContent("Physics to World Scale", phyWTip), gravityEngine.physToWorldFactor); EditorGUILayout.LabelField("Steps per frame:"); stepsPerFrame = EditorGUILayout.IntField(new GUIContent(" Massive Bodies", stepTip), stepsPerFrame); particleStepsPerFrame = EditorGUILayout.IntField(new GUIContent(" Particles", pstepTip), particleStepsPerFrame); } // Switch bodies list on/off based on option if (!gravityEngine.detectNbodies) { // use native Inspector look & feel for bodies object EditorGUILayout.LabelField("Control Nbody in following gameObjects (and children)", EditorStyles.boldLabel); SerializedProperty bodiesProp = serializedObject.FindProperty("bodies"); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(bodiesProp, true); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } } else { EditorGUILayout.LabelField("NBody objects will be detected automatically", EditorStyles.boldLabel); } // Show the CM and the velocity of the CM cmFoldout = EditorGUILayout.Foldout(cmFoldout, "Center of Mass Info"); if (cmFoldout) { EditorGUILayout.LabelField("Center of Mass:" + gravityEngine.GetWorldCenterOfMass()); EditorGUILayout.LabelField("CM Velocity:" + gravityEngine.GetWorldCenterOfMassVelocity()); } // Checking the Event type lets us update after Undo and Redo commands. // A redo updates _lengthScale but does not run the setter if (Event.current.type == EventType.ExecuteCommand && Event.current.commandName == "UndoRedoPerformed") { // explicitly re-set so setter code will run. gravityEngine.lengthScale = lengthScale; } if (GUI.changed) { Undo.RecordObject(gravityEngine, "GE Change"); gravityEngine.mapToScene = mapToScene; gravityEngine.timeScale = timeScale; gravityEngine.massScale = massScale; // This runs a setter in GE that will do a rescale of the scene if necessary gravityEngine.lengthScale = lengthScale; gravityEngine.units = units; gravityEngine.physToWorldFactor = physToWorldFactor; gravityEngine.optimizeMassless = optimizeMassless; gravityEngine.detectNbodies = detectNbodies; gravityEngine.editorShowTrajectory = showTrajectory; gravityEngine.trajectoryPrediction = trajectoryPrediction; gravityEngine.trajectoryTime = trajectoryTime; gravityEngine.trajectoryCanvas = trajCanvas; gravityEngine.markerParent = markerParent; gravityEngine.trajectoryComputeFactor = computeFactor; gravityEngine.algorithm = algorithm; gravityEngine.force = force; gravityEngine.evolveAtStart = evolveAtStart; gravityEngine.editorShowScale = scaling; gravityEngine.editorShowAdvanced = showAdvanced; gravityEngine.editorCMfoldout = cmFoldout; gravityEngine.stepsPerFrame = stepsPerFrame; gravityEngine.particleStepsPerFrame = particleStepsPerFrame; EditorUtility.SetDirty(gravityEngine); } }
/// <summary> /// Evolves particles using massice bodies the with force delegate provided. /// </summary> /// <returns>The with force.</returns> /// <param name="evolveTime">Evolve time.</param> /// <param name="numBodies">Number bodies.</param> /// <param name="m">M.</param> /// <param name="r_m">R m.</param> /// <param name="size2">Size2.</param> /// <param name="info">Info.</param> /// <param name="force">Force.</param> public double EvolveWithForce(double evolveTime, int numBodies, GravityState gravityState, ref double[] size2, ref byte[] info, IForceDelegate force) { // do nothing if all inactive if (inactive == null || allInactive) { return(evolveTime); // Particle system has not init-ed yet or is done } // (did not work in Start() -> Unity bug? Init sequencing?) if (!playing) { gravityParticles.Play(); playing = true; } double[] m = gravityState.m; double[,] r_m = gravityState.r; ParticleLifeCycleHandler(numBodies, gravityState, ref info); // // physics integration of particles in the field of the masses passed in by NBE // double time = 0; while (time < evolveTime) { time += dt; // Update v and r for (int i = 0; i < particleCount; i++) { if (!inactive[i]) { v[i, 0] += a[i, 0] * 0.5 * dt; r[i, 0] += v[i, 0] * dt; v[i, 1] += a[i, 1] * 0.5 * dt; r[i, 1] += v[i, 1] * dt; v[i, 2] += a[i, 2] * 0.5 * dt; r[i, 2] += v[i, 2] * dt; } } // advance acceleration double[] rji = new double[GravityState.NDIM]; double r2; // r squared double r_sep; // r double accel; // a = 0 for (int i = 0; i < particleCount; i++) { a[i, 0] = 0.0; a[i, 1] = 0.0; a[i, 2] = 0.0; } // calc a for (int i = 0; i < numBodies; i++) { // check mass has inactive clear if ((info[i] & GravityEngine.INACTIVE) == 0) { for (int j = 0; j < particleCount; j++) { // only evolve active particles if (!inactive[j]) { rji[0] = r[j, 0] - r_m[i, 0]; rji[1] = r[j, 1] - r_m[i, 1]; rji[2] = r[j, 2] - r_m[i, 2]; // Particles that have moved outside of view are marked inactive r2 = rji[0] * rji[0] + rji[1] * rji[1] + rji[2] * rji[2]; // Check for incursion on massive bodies and inactivate particles that have collided // (Do not want to incur collider overhead per particle) if (allowRemoval && r2 < size2[i]) { inactive[j] = true; inactiveCount++; #pragma warning disable 162 // disable unreachable code warning if (debugLogs) { Debug.Log(string.Format("Inactivate particle {0} size2={1} due to object at {2} {3} {4} r2={5} count={6} p={7} {8}", j, size2[i], r_m[i, 0], r_m[i, 1], r_m[i, 2], r2, inactiveCount, r[j, 0], r[j, 1])); } #pragma warning restore 162 if (oneTimeBurst) { r[j, 0] = OUT_OF_VIEW; } else { particles[j].remainingLifetime = 0; } continue; } r_sep = System.Math.Sqrt(r2) + EPSILON; accel = force.CalcPseudoForce(r_sep, i, j); a[j, 0] -= m[i] * accel * (rji[0] / r_sep); a[j, 1] -= m[i] * accel * (rji[1] / r_sep); a[j, 2] -= m[i] * accel * (rji[2] / r_sep); } // for j } // info } // for i } // update velocity for (int i = 0; i < particleCount; i++) { if (!inactive[i]) { v[i, 0] += a[i, 0] * 0.5 * dt; v[i, 1] += a[i, 1] * 0.5 * dt; v[i, 2] += a[i, 2] * 0.5 * dt; } } } // while return(time); }
/// <summary> /// Evolve the position of the active massless bodies based on the gravitational field by the /// objects provided in the arguments m, r_m and the specified force. /// /// This does not use the standard integrators since the list of bodies and the loop structure /// differs. /// </summary> /// <returns>The with force.</returns> /// <param name="evolveFor">Evolve for.</param> /// <param name="numMasses">Number masses.</param> /// <param name="m">M.</param> /// <param name="r_m">R m.</param> /// <param name="massive_info">Massive info.</param> /// <param name="force">Force.</param> public double EvolveWithForce(double evolveFor, int numMasses, ref double[] m, ref double[,] r_m, ref byte[] massive_info, IForceDelegate force) { if (numMasses == 0 && numBodies == 0) { return(evolveFor); } // advance acceleration double[] rji = new double[GravityEngine.NDIM]; double r2; // r squared double r_sep; // r double accel; double time = 0.0; // Built in LeapFrog integrator while (time < evolveFor) { time += dt; // Update v using a from last cycle (1/2 step), then update r for (int i = 0; i < numBodies; i++) { if ((info[i] & GravityEngine.INACTIVE) == 0) { for (int k = 0; k < GravityEngine.NDIM; k++) { v[i, k] += a[i, k] * dt / 2.0; r[i, k] += v[i, k] * dt; } } } // a = 0 for (int i = 0; i < numBodies; i++) { a[i, 0] = 0.0; a[i, 1] = 0.0; a[i, 2] = 0.0; } // calc a for (int i = 0; i < numMasses; i++) { if ((massive_info[i] & GravityEngine.INACTIVE) == 0) { for (int j = 0; j < numBodies; j++) { if ((info[j] & GravityEngine.INACTIVE) == 0) { rji[0] = r[j, 0] - r_m[i, 0]; rji[1] = r[j, 1] - r_m[i, 1]; rji[2] = r[j, 2] - r_m[i, 2]; r2 = 0; r2 += rji[0] * rji[0]; r2 += rji[1] * rji[1]; r2 += rji[2] * rji[2]; r_sep = System.Math.Sqrt(r2) + EPSILON; // Force equation accel = force.CalcF(r_sep); a[j, 0] -= m[i] * accel * (rji[0] / r_sep); a[j, 1] -= m[i] * accel * (rji[1] / r_sep); a[j, 2] -= m[i] * accel * (rji[2] / r_sep); } // j } // if info } // i } // update velocity with other half step for (int i = 0; i < numBodies; i++) { if ((info[i] & GravityEngine.INACTIVE) == 0) { v[i, 0] += a[i, 0] * dt / 2.0; v[i, 1] += a[i, 1] * dt / 2.0; v[i, 2] += a[i, 2] * dt / 2.0; } } } return(time); }
/// <summary> /// Initializes a new instance of the <see cref="LeapFrogIntegrator"/> class. /// An optional force delegate can be provided if non-Newtonian gravity is /// desired. /// </summary> /// <param name="force">Force.</param> public LeapFrogIntegrator(IForceDelegate force) { forceDelegate = force; }
/// <summary> /// Clone constructor /// /// Creates a deep copy suitable for independent evolution as a trajectory or for maneuver iterations. /// Maneuvers will be executed but the callback to motify the owner of the maneuver will be skipped (only /// the real evolution will notify). /// </summary> /// <param name="fromState"></param> public GravityState(GravityState fromState) { m = new double[fromState.arraySize]; r = new double[fromState.arraySize, NDIM]; info = new byte[fromState.arraySize]; size2 = new double[fromState.arraySize]; physicalTime = new double[System.Enum.GetNames(typeof(Evolvers)).Length]; arraySize = fromState.arraySize; numBodies = fromState.numBodies; fixedBodiesInIntegrator = fromState.fixedBodiesInIntegrator; onRails = fromState.onRails; // omitting hasTrajectories integrator = fromState.integrator.DeepClone(); // don't copy particles, but need to init list gravityParticles = new List <GravityParticles>(); // DO copy the maneuvers maneuverMgr = new ManeuverMgr(fromState.maneuverMgr); fixedBodies = new List <GravityEngine.FixedBody>(fromState.fixedBodies); for (int i = 0; i < physicalTime.Length; i++) { physicalTime[i] = fromState.physicalTime[i]; } time = fromState.time; forceDelegate = fromState.forceDelegate; selectiveForce = fromState.selectiveForce; keplerDepthChanged = new List <GravityEngine.FixedBody>(fromState.keplerDepthChanged); if (fromState.masslessEngine != null) { masslessEngine = fromState.masslessEngine.DeepClone(); masslessEngine.ResetTrajectories((float)fromState.time); } for (int i = 0; i < arraySize; i++) { m[i] = fromState.m[i]; info[i] = fromState.info[i]; size2[i] = fromState.size2[i]; r[i, 0] = fromState.r[i, 0]; r[i, 1] = fromState.r[i, 1]; r[i, 2] = fromState.r[i, 2]; } // copies do not notify maneuver owners of maneuver completion. They are assumed to be "what if" // evolutions isCopy = true; #pragma warning disable 162 // disable unreachable code warning if (GravityEngine.DEBUG) { Debug.Log("Created new (copy) gravityState"); } #pragma warning restore 162 }