using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Blacklight.Core; using Azimuth; using Blacklight.Core.Drawables; using Annulus; namespace Slipstream.Testbed { public partial class MainWindow : Form { public Slipstream.FluidWorld world; public IDemo currentDemo; private int currPlace = 0; private Scalar[] ms = new Scalar[60]; bool demoSetup = false; public MainWindow() { InitializeComponent(); var demoTypes = new List(); foreach (var type in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()) { if (typeof(IDemo).IsAssignableFrom(type) && type.IsClass) { demoTypes.Add(type); } } demoDropBox.ValueMember = "Value"; demoDropBox.DisplayMember = "Text"; demoDropBox.DataSource = demoTypes.Select(x => new {Text = x.Name, Value = x}).ToList(); demoSetup = true; viewport.DrawableSurface.Invalidated += delegate(object sender, EventArgs e) { Draw(); }; GameThread = new System.Threading.Thread(delegate() { while (true) { var startTime = System.Diagnostics.Stopwatch.StartNew(); if (world != null && (turnOnSingleStep || !pauseCheckbox.Checked)) { lock (locker) { var fluidTimer = System.Diagnostics.Stopwatch.StartNew(); currentDemo.Update(world); ms[currPlace++] = ((Scalar)fluidTimer.ElapsedTicks) / ((Scalar)System.Diagnostics.Stopwatch.Frequency) * 1000.0; currPlace = currPlace % 60; if (frameLock.Checked) { while (startTime.ElapsedTicks < (System.Diagnostics.Stopwatch.Frequency / 60)) { System.Threading.Thread.Sleep(0); } } } } if (turnOnSingleStep) { pauseCheckbox.Checked = true; turnOnSingleStep = false; } System.Threading.Thread.Sleep(0); } }); GameThread.Name = "Game sim thread"; } public void StartGameThread() { GameThread.Start(); } private List vortonDrawables = new List(); private List particleDrawables = new List(); private List bodyDrawables = new List(); private System.Threading.Thread GameThread = null; List vortonPositions = new List(); List vortonVorticity = new List(); List particlePositions = new List(); List boundaryBodySilhouettes = new List(); List boundaryBodyTransforms = new List(); List> boundaryBodyControlPoints = new List>(); List> panelSourceStrengths = new List>(); List> panelLengths = new List>(); private void Draw() { Cel demoCel; lock (locker) { vortonPositions.Clear(); world.Vortons.Select(x => x.Position).CopyTo(vortonPositions); particlePositions.Clear(); world.TracerParticles.Select(x => x.Position).CopyTo(particlePositions); vortonVorticity.Clear(); world.Vortons.Select(x => x.Vorticity).CopyTo(vortonVorticity); var boundaryBodies = world.Boundaries.Where(x => x is AbstractBoundaryBody).Cast(); boundaryBodySilhouettes.Clear(); boundaryBodies.Select(x => x.Silhouette).CopyTo(boundaryBodySilhouettes); boundaryBodyTransforms.Clear(); boundaryBodies.Select(x => new AffineMatrix(x.Orientation, x.Position, 1)) .CopyTo(boundaryBodyTransforms); boundaryBodyControlPoints.Clear(); boundaryBodies.Where(x => x is IdealFluid.IdealFluidBoundaryBody) .Cast() .Select(x => x._group.PanelTransforms.ToList()).CopyTo(boundaryBodyControlPoints); panelSourceStrengths.Clear(); boundaryBodies.Where(x => x is IdealFluid.IdealFluidBoundaryBody) .Cast() .Select(x => (IList)x._group.SourceStrengths.ToList()) .CopyTo(panelSourceStrengths); panelLengths.Clear(); boundaryBodies.Where(x => x is IdealFluid.IdealFluidBoundaryBody) .Cast() .Select(x => x._group.PanelLengths.ToList()).CopyTo(panelLengths); demoCel = currentDemo.Draw(world); } Scene _scene = new Scene(); _scene.Cels.Add(demoCel); Cel _cel = new Cel(); _scene.Cels.Add(_cel); Scalar vortonCount = (Scalar)(world.Vortons.Count()); bodyDrawables.Clear(); // Display panels { for (int i = 0; i < boundaryBodySilhouettes.Count; ++i) { var perimeter = boundaryBodySilhouettes[i].Edges.Sum(x => x.Direction.Length()); { var collage = new Blacklight.Core.Drawables.Collage(); var silhouette = new Blacklight.Core.Drawables.Polygon(boundaryBodySilhouettes[i]); silhouette.InsideColor = new Blacklight.Core.Color(1, 0.25, 0.25); silhouette.ShellColor = new Blacklight.Core.Color(1, 0.25, 0.25); silhouette.ShellPixelThickness = 0; silhouette.ShellModelSpaceThickness = 0; silhouette.SmoothBorder = false; collage.AddDrawable(silhouette); for (int j = 0; panelSourceStrengths.Count > i && j < panelSourceStrengths[i].Count; ++j) { var panel = new Blacklight.Core.Drawables.Rectangle { InsideColor = new Blacklight.Core.Color(0, 0, 0), ShellColor = new Blacklight.Core.Color(0, 0, 0), ShellPixelThickness = 0, ShellModelSpaceThickness = 0, SmoothBorder = false, }; collage.AddDrawable(panel); } bodyDrawables.Add(collage); } // Update the panel strengths Scalar drawPanels = displayPanels.Checked ? 0 : 1; { for (int j = 1; j < bodyDrawables[i].Drawables.Count; ++j) { var panel = bodyDrawables[i].GetDrawable(j); var primitive = (Blacklight.Core.Drawables.Rectangle)panel.Item1; var j_1 = Annulus.Polygon.Utilities.CyclicDecrement(j, bodyDrawables[i].Drawables.Count); var strength = panelSourceStrengths[i][j_1]; if (strength == 0) { primitive.InsideColor = new Blacklight.Core.Color(0, 1, 0, drawPanels); } else if (strength < 0) { primitive.InsideColor = new Blacklight.Core.Color(1, 1, 0, drawPanels); } else { primitive.InsideColor = new Blacklight.Core.Color(0, 1, 1, drawPanels); } var scale = Math.Sqrt(Math.Abs(strength / 1e2) + .001); bodyDrawables[i].SetTransformOnDrawable(j, boundaryBodyControlPoints[i][j_1] * new AffineMatrix(new Vector(panelLengths[i][j_1] / 2 * 0.95, 0), new Vector(0, scale), new Vector(0, 0))); } } var entity = new Entity { drawable = bodyDrawables[i], modelToWorld = boundaryBodyTransforms[i], }; _cel.AddEntity(entity); } } if (displayVortons.Checked == true) { for (int i = 0; i < vortonPositions.Count; ++i) { if (vortonDrawables.Count <= i) { var ellipse = new Blacklight.Core.Drawables.Ellipse { InsideColor = new Blacklight.Core.Color(0, 0, (i / vortonCount)), ShellColor = new Blacklight.Core.Color(0, 0, (i / vortonCount)), ShellPixelThickness = 0, ShellModelSpaceThickness = 0, SmoothBorder = false, }; vortonDrawables.Add(ellipse); } if (vortonVorticity[i] == 0) { vortonDrawables[i].InsideColor = new Blacklight.Core.Color(0, 1, 0); } else if (vortonVorticity[i] < 0) { vortonDrawables[i].InsideColor = new Blacklight.Core.Color(1, 0, 0); } else { vortonDrawables[i].InsideColor = new Blacklight.Core.Color(0, 0, 1); } _cel.AddEntity(new Entity { drawable = vortonDrawables[i], modelToWorld = AffineMatrix.BuildFromTranslation(vortonPositions[i]) * AffineMatrix.BuildFromScale(Math.Sqrt(Math.Abs(vortonVorticity[i] / 5e3)) + .1), }); } } for (int i = 0; i < particlePositions.Count; ++i) { if (particleDrawables.Count <= i) { var ellipse = new Blacklight.Core.Drawables.Ellipse { InsideColor = new Blacklight.Core.Color(0, 0.84, 0.84), ShellColor = new Blacklight.Core.Color(0, 0.25, 0.25), ShellPixelThickness = 0, ShellModelSpaceThickness = 0.25, SmoothBorder = false, }; var collage = new Blacklight.Core.Drawables.Collage(); collage.AddDrawable(ellipse, AffineMatrix.BuildFromTranslation(Vector.Zero) * AffineMatrix.BuildFromScale(1)); particleDrawables.Add(collage); } _cel.AddEntity(new Entity { drawable = particleDrawables[i], modelToWorld = AffineMatrix.BuildFromTranslation(particlePositions[i]), }); } viewport.DrawableSurface.FrameReset(); viewport.DrawableSurface.DrawScene(_scene, viewport.Camera); viewport.DrawableSurface.SubmitDrawCalls(); } private System.Diagnostics.Stopwatch lastUpdate = null; private void GameLoopTimer_Tick(object sender, EventArgs e) { Draw(); if (lastUpdate == null) { lastUpdate = System.Diagnostics.Stopwatch.StartNew(); } if (lastUpdate.ElapsedMilliseconds >= 250) { lastUpdate = System.Diagnostics.Stopwatch.StartNew(); this.totalEnergy.Text = "Total Energy: " + totalEnergyCount.ToString("F3"); this.cyclesPerSec.Text = ms.Average(x => x).ToString("F3") + " ms"; // System.Console.WriteLine(ms.Average(x => x).ToString("F3") + " ms"); } } private object locker = new object(); private Scalar totalEnergyCount = 0; private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) { if (GameThread != null) { GameThread.Abort(); GameThread = null; } } private void resetButton_Click(object sender, EventArgs e) { lock (locker) { currentDemo.Reset(world); } } private static volatile bool turnOnSingleStep = false; private void singleStep_Click(object sender, EventArgs e) { lock (locker) { turnOnSingleStep = true; } } private void demoDropBox_SelectedIndexChanged(object sender, EventArgs e) { if (demoSetup) { lock (locker) { var selectedDemo = demoDropBox.SelectedValue; var demo = System.Activator.CreateInstance(selectedDemo as Type) as IDemo; world.Clear(); currentDemo = demo; currentDemo.Start(world); } } } } public static class ExtensionMethods { public static void CopyTo(this IEnumerable input, IList output) { int i = 0; foreach (var thing in input) { if (i >= output.Count) { output.Add(thing); } else { output[i] = thing; } ++i; } } } }