public void AddBodyActual(double mass, bool isGravitySource, double diameter, int color, SimPoint startPosition, SimPoint startVelocity) { var velocity = new SimPoint(startVelocity.X / simSpace.VelocityConnversionFactor, startVelocity.Y / simSpace.VelocityConnversionFactor); bodies.Add(new Body(mass, diameter, startPosition, velocity, isGravitySource, simSpace)); renderer.Add(diameter, color, bodies.Last()); }
public TranslateTransform CircleTransform(SimPoint position, double size) { TranslateTransform t = new TranslateTransform(); double circleCenterTranslation = -size / 2.0; t.X = position.X * scaleFactor + simulationCenterTranslation.X + circleCenterTranslation; t.Y = position.Y * -scaleFactor + simulationCenterTranslation.Y + circleCenterTranslation; return(t); }
public Body(double bodyMass, double bodySize, SimPoint bodyStartingPosition, SimPoint bodyStartingVelocity, SimulationSpace space) { Mass = bodyMass; Size = bodySize; Position = bodyStartingPosition; Velocity = bodyStartingVelocity; IsGravitySource = defaultGravitySource; simSpace = space; bodyNumber = currentBodyNumber++; }
/// <summary> /// Puts a body into a clockwise circular orbit /// </summary> /// <param name="startingPosition">Angle, measured in degrees, with 0 degrees at top of orbit</param> /// <param name="orbitRadius"></param> /// <param name="position"></param> /// <param name="velocity"></param> public void InitializeCircularOrbit(double startingPosition, double orbitRadius, double centralMass, out SimPoint position, out SimPoint velocity) { double startingPositionRadians = Math.PI * 2.0 * (startingPosition / 360.0); position = new SimPoint(orbitRadius * Math.Sin(startingPositionRadians), orbitRadius * Math.Cos(startingPositionRadians)); double orbitVelocity = CircularOrbitVelocity(centralMass, orbitRadius); velocity = new SimPoint(orbitVelocity * Math.Cos(startingPositionRadians), -orbitVelocity * Math.Sin(startingPositionRadians)); }
public Body(SimPoint bodyStartingPosition, SimulationSpace space) { Mass = defaultValue; Size = defaultValue; Position = bodyStartingPosition; Velocity = defaultStartingVelocity; IsGravitySource = defaultGravitySource; simSpace = space; bodyNumber = currentBodyNumber++; }
public void Move(SimPoint accel, double deltaT) { if (!((accel.X == 0.0) && (accel.Y == 0.0))) { // Apply linear acceleration during the time interval double newVelocityX = velocity.X + (accel.X * deltaT); position.X += (velocity.X + newVelocityX) / 2 * deltaT; velocity.X = newVelocityX; double newVelocityY = velocity.Y + (accel.Y * deltaT); position.Y += (velocity.Y + newVelocityY) / 2 * deltaT; velocity.Y = newVelocityY; } }
public void PlotTrailDot(SimPoint position) { // Always plot the first dot // Don't plot subsequent dots iff they're in the same position as the previous dot if ((trailsPositions.Count == 0) || !position.Equals(previousTrailPosition)) { previousTrailPosition = position; trailsPositions.Add(position); Rectangle dot = new Rectangle(); dot.Width = dot.Height = dotSize; dot.Fill = trailsBrush; dot.RenderTransform = CircleTransform(position, dotSize); simCanvas.Children.Add(dot); } }
// Updated to be marshalled onto the UI thread public void UpdateMonitoredValues(Body body, double simElapsedTime) { if (UI_UpdatesStopped()) { return; // Stop UI updates while app is suspended or changing scenarios } var ignore = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { SimPoint velocity = body.Velocity * sim.simSpace.VelocityConnversionFactor; velocityTextBlock.Text = "velocity: " + FormatPointToString(velocity) + String.Format(", v = {0:N1} {1}", velocity.Magnitude(), sim.simSpace.VelocityUnitsAbbr); positionTextBlock.Text = "position: " + FormatPointToString(body.Position) + String.Format(", r = {0:N1} {1}", body.Position.Magnitude() - sim.simSpace.DistanceOffset, sim.simSpace.DistanceUnitsAbbr); timeTextBlock.Text = $"time: {TimeDisplay.FormatElapsedTime(simElapsedTime, sim.simSpace.TimeUnits)}"; }); }
public void MoveWithPrecisionCheck(SimPoint accel, double deltaT) { if (!((accel.X == 0.0) && (accel.Y == 0.0))) { // Apply linear acceleration during the time interval double deltaVX = accel.X * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.X, deltaVX)) { DisplayPrecisionIssue(velocity.X, deltaVX, "Adding DeltaV to VX", bodyNumber); } double newVelocityX = velocity.X + deltaVX; double deltaPX = (velocity.X + newVelocityX) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.X, deltaPX)) { DisplayPrecisionIssue(position.X, deltaPX, "Adding to Position.X", bodyNumber); } position.X += deltaPX; velocity.X = newVelocityX; double deltaVY = accel.Y * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.Y, deltaVY)) { DisplayPrecisionIssue(velocity.Y, deltaVY, "Adding DeltaV to VY", bodyNumber); } double newVelocityY = velocity.Y + deltaVY; double deltaPY = (velocity.Y + newVelocityY) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.Y, deltaPY)) { DisplayPrecisionIssue(position.Y, deltaPY, "Adding to Position.Y", bodyNumber); } position.Y += deltaPY; velocity.Y = newVelocityY; } }
// Calculates the mapping from simulation coordinates to XAML coordinates // Called when the simulation is loaded and whenever the window is resized public void SetSimulationTransform(double screenWidth, double screenHeight) { screenDimensions = new Point(screenWidth, screenHeight); double minDimension = Math.Min(screenWidth, screenHeight); // This can be called before any scenarios have been loaded, provide a placeholder value in this case double simBoxDimensions; if (simSpace == null) { simBoxDimensions = 1.0; } else { simBoxDimensions = simSpace.SimBoxHeightAndWidth; } scaleFactor = minDimension / simBoxDimensions; simulationCenterTranslation = new Point(screenWidth / 2, screenHeight / 2); screenSimulationDimensions = new SimPoint(screenWidth / scaleFactor, screenHeight / scaleFactor); scaleFactor = scaleFactor * ZoomFactor; }
// Returns starting position for a body in simulation space coordinates public SimPoint GetStartingPosition(GravitySim.BodyStartPosition startPos) { const double stagePosition = 0.5; // For the "stage" positions - proportion of the way from the center of the stage to the edge // in all directions double simBoxMaxXY = simSpace.SimBoxHeightAndWidth / 2.0; double stageXY = simBoxMaxXY * stagePosition; double screenMaxX = screenSimulationDimensions.X / 2.0; double screenMaxY = screenSimulationDimensions.Y / 2.0; switch (startPos) { case GravitySim.BodyStartPosition.StageLeft: return(new SimPoint(-stageXY, 0.0)); case GravitySim.BodyStartPosition.StageRight: return(new SimPoint(stageXY, 0.0)); case GravitySim.BodyStartPosition.StageTop: return(new SimPoint(0.0, stageXY)); case GravitySim.BodyStartPosition.StageBottom: return(new SimPoint(0.0, -stageXY)); case GravitySim.BodyStartPosition.StageTopLeft: return(new SimPoint(-stageXY, stageXY)); case GravitySim.BodyStartPosition.StageTopRight: return(new SimPoint(stageXY, stageXY)); case GravitySim.BodyStartPosition.StageBottomLeft: return(new SimPoint(-stageXY, -stageXY)); case GravitySim.BodyStartPosition.StageBottomRight: return(new SimPoint(stageXY, -stageXY)); case GravitySim.BodyStartPosition.ScreenLeft: return(new SimPoint(-screenMaxX, 0.0)); case GravitySim.BodyStartPosition.ScreenRight: return(new SimPoint(screenMaxX, 0.0)); case GravitySim.BodyStartPosition.ScreenTop: return(new SimPoint(0.0, screenMaxY)); case GravitySim.BodyStartPosition.ScreenBottom: return(new SimPoint(0.0, -screenMaxY)); case GravitySim.BodyStartPosition.CenterOfTheUniverse: return(new SimPoint(0.0, 0.0)); case GravitySim.BodyStartPosition.RandomStagePosition: return(new SimPoint(rand.Next((int)-simBoxMaxXY, (int)simBoxMaxXY), rand.Next((int)-simBoxMaxXY, (int)simBoxMaxXY))); case GravitySim.BodyStartPosition.RandomScreenPosition: return(new SimPoint(rand.Next((int)-screenMaxX, (int)screenMaxX), rand.Next((int)-screenMaxY, (int)screenMaxY))); // This approach gives us higher density toward the center of the circle case GravitySim.BodyStartPosition.RandomDenseCenterCircularCluster: double length = rand.NextDouble() * simBoxMaxXY * 0.9; double angle = rand.NextDouble() * Math.PI * 2.0; // radians return(new SimPoint(length * Math.Cos(angle), length * Math.Sin(angle))); // This approach gives us uniform density throughout the circle case GravitySim.BodyStartPosition.RandomUniformDensityCircularCluster: SimPoint newBodyPosition; double limitXY = 0.9 * simBoxMaxXY; do { newBodyPosition = new SimPoint(rand.Next((int)-limitXY, (int)limitXY), rand.Next((int)-limitXY, (int)limitXY)); }while (newBodyPosition.Magnitude() > limitXY); return(newBodyPosition); default: return(new SimPoint(0.0, 0.0)); } }
// simRunning - true if sim is auto-running // false if sim is single stepping public void Step(double timeInterval, bool simRunning) { Stopwatch perfStopwatch = new Stopwatch(); long perfIntervalTicks = 0L; bool simStepping = !simRunning; double scaledTimeInterval = timeInterval * SpeedFactor; SetTimeForTrailMark(simElapsedTime); if (simStepping) { Debug.WriteLine("Elapsed times for {0} bodies:", bodies.Count()); perfStopwatch.Start(); } if (accelerations == null) { accelerations = new SimPoint[bodies.Count()]; } if (checkSim) { if (positions == null) { positions = new SimPoint[bodies.Count()]; } if (velocities == null) { velocities = new SimPoint[bodies.Count()]; } } double timeIntervalPerCycle = scaledTimeInterval / (double)simCalcSettings.CalculationCyclesPerFrame; List <SimPoint> otherPositions = new List <SimPoint>(); List <SimPoint> otherAccelerations = new List <SimPoint>(); if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions Before Update"); Validate5BodyCross(velocities, "Velocities Before Update"); } for (int calcCycle = 0; calcCycle < simCalcSettings.CalculationCyclesPerFrame; calcCycle++) { // Calculate NBody acceleration if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); if (simRounding > 0) { accel.X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accel.Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].X, accel.X)) { Body.DisplayPrecisionIssue(accelerations[i].X, accel.X, "Accumulating Accel.X", i); } accelerations[i].X += accel.X; if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].Y, accel.Y)) { Body.DisplayPrecisionIssue(accelerations[i].Y, accel.Y, "Accumulating Accel.Y", i); } accelerations[i].Y += accel.Y; } } } } else if (simRounding > 0) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accelerations[i].Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } } } } else { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += accel.X; accelerations[i].Y += accel.Y; } } } } //if (checkSim) Validate5BodyCross(accelerations, "Accelerations Before Limit and Rounding"); if (accelerationLimitOn) { EnforceAccelerationLimit(accelerations, accelerationLimit); } if (simRounding > 0) { RoundAccelerations(accelerations, simRounding); } if (checkSim) { Validate5BodyCross(accelerations, "Accelerations After Limit and Rounding"); } // Update positons and velocities if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { bodies[i].MoveWithPrecisionCheck(accelerations[i], timeIntervalPerCycle); } } else { for (int i = 0; i < bodies.Count(); i++) { bodies[i].Move(accelerations[i], timeIntervalPerCycle); } } if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions After Update"); Validate5BodyCross(velocities, "Velocities After Update"); } simElapsedTime += timeIntervalPerCycle; if ((MainPage.trailsEnabled) && TimeForTrailsMark(simElapsedTime)) { DrawTrails(); } } if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, String.Format("Compute N-body accelerations, update positions & velocities ({0} iterations)", simCalcSettings.CalculationCyclesPerFrame)); } // Update rendering renderer.BodiesMoved(bodies); simPage.UpdateMonitoredValues(bodies[monitoredBody], simElapsedTime); if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, "Update transforms of XAML shapes & monitored values"); } if (simStepping) { Debug.WriteLine("Total elapsed time = {0:F2} ms", (double)perfIntervalTicks / (double)(Stopwatch.Frequency / 1000L)); Debug.WriteLine(""); } // stepRunning = false; }
public void AddBody(double mass, double size, int color, BodyStartPosition startPosition, SimPoint startVelocity, bool isGravitySource) { bodies.Add(new Body(mass, size, renderer.GetStartingPosition(startPosition), startVelocity, isGravitySource, simSpace)); renderer.Add(size, color, bodies.Last()); }
static string FormatPointToString(SimPoint p) { return(String.Format("x = {0:N1}, y = {1:N1}", p.X, p.Y)); }