using System; using SFML.Graphics; using SFML.System; using FluidSim.Components; namespace FluidSim.Tests { public abstract class Scenario { public abstract void Initialize(int sampleRate); public abstract float Process(); public abstract void Draw(RenderWindow target); protected const double AmbientPressure = 101325.0; protected const double AmbientTemperature = 300.0; public double Throttle { get; set; } = 0.0; // ---------- Color from pressure (volumes) ---------- protected Color PressureColor(double pressurePa) { double bar = pressurePa / 1e5; // convert to bar for easier mapping byte r, g, b; if (bar < 1.0) // vacuum → blue to green { double factor = Math.Clamp(bar, 0.0, 1.0); r = 0; g = (byte)(255 * factor); b = (byte)(255 * (1.0 - factor)); } else // above ambient → green to red { double factor = Math.Min((bar - 1.0) / 9.0, 1.0); // 1→10 bar maps to 0→1 r = (byte)(255 * factor); g = (byte)(255 * (1.0 - factor)); b = 0; } return new Color(r, g, b); } // ---------- Color from temperature (pipes) ---------- protected Color TemperatureColor(double temperature) { double t = Math.Clamp(temperature, 0.0, 2000.0); byte r, g, b; if (t < AmbientTemperature) { double factor = t / AmbientTemperature; r = 0; g = (byte)(255 * factor); b = (byte)(255 * (1.0 - factor)); } else { double factor = (t - AmbientTemperature) / (2000.0 - AmbientTemperature); r = (byte)(255 * factor); g = (byte)(255 * (1.0 - factor)); b = 0; } return new Color(r, g, b); } // ---------- Draw a generic volume (e.g. plenum) ---------- protected void DrawVolume(RenderWindow target, Volume0D volume, float centerX, float topY, float width, float height) { var rect = new RectangleShape(new Vector2f(width, height)) { FillColor = PressureColor(volume.Pressure), // ← pressure‑based Position = new Vector2f(centerX - width / 2f, topY) }; target.Draw(rect); var border = new RectangleShape(new Vector2f(width, height)) { FillColor = Color.Transparent, OutlineColor = Color.White, OutlineThickness = 1f, Position = new Vector2f(centerX - width / 2f, topY) }; target.Draw(border); } // ---------- Draw an engine cylinder ---------- protected void DrawCylinder(RenderWindow target, Cylinder cylinder, float centerX, float topY, float width, float maxHeight) { double fraction = cylinder.PistonFraction; float currentHeight = (float)(maxHeight * fraction); // Walls var wall = new RectangleShape(new Vector2f(width, maxHeight)); wall.FillColor = new Color(60, 60, 60); wall.Position = new Vector2f(centerX - width / 2f, topY); target.Draw(wall); // Gas – colored by pressure now float gasTop = topY; var gasRect = new RectangleShape(new Vector2f(width, currentHeight)); gasRect.FillColor = PressureColor(cylinder.Pressure); // ← pressure‑based gasRect.Position = new Vector2f(centerX - width / 2f, gasTop); target.Draw(gasRect); // Piston line var pistonLine = new RectangleShape(new Vector2f(width, 4f)); pistonLine.FillColor = Color.White; pistonLine.Position = new Vector2f(centerX - width / 2f, topY + currentHeight); target.Draw(pistonLine); // Valve indicators float valveW = 6f, valveH = 10f, valveY = topY + 4f; var intakeValve = new RectangleShape(new Vector2f(valveW, valveH)); intakeValve.FillColor = cylinder.IntakeValveArea > 0 ? Color.Green : Color.Red; intakeValve.Position = new Vector2f(centerX - width / 2f - valveW - 2f, valveY); target.Draw(intakeValve); var exhaustValve = new RectangleShape(new Vector2f(valveW, valveH)); exhaustValve.FillColor = cylinder.ExhaustValveArea > 0 ? Color.Green : Color.Red; exhaustValve.Position = new Vector2f(centerX + width / 2f + 2f, valveY); target.Draw(exhaustValve); } // ---------- Draw a pipe (unchanged) ---------- protected void DrawPipe(RenderWindow target, Pipe1D pipe, float pipeCenterY, float pipeStartX, float pipeEndX) { int n = pipe.CellCount; if (n < 2) return; float pipeLengthPx = pipeEndX - pipeStartX; float dx = pipeLengthPx / (n - 1); float baseRadius = 25f; float rangeFactor = 2f; float scaleFactor = 2f; static float SmoothStep(float edge0, float edge1, float x) { float t = Math.Clamp((x - edge0) / (edge1 - edge0), 0f, 1f); return t * t * (3f - 2f * t); } var centers = new float[n]; var radii = new float[n]; var temperatures = new double[n]; double R_gas = 287.0; for (int i = 0; i < n; i++) { double p = pipe.GetCellPressure(i); double rho = pipe.GetCellDensity(i); double T = p / Math.Max(rho * R_gas, 1e-12); temperatures[i] = T; float deviation = (float)Math.Tanh((p - AmbientPressure) / AmbientPressure / rangeFactor); radii[i] = baseRadius * (1f + deviation * scaleFactor); if (radii[i] < 2f) radii[i] = 2f; centers[i] = pipeStartX + i * dx; } int segmentsPerCell = 8; int totalPoints = n + (n - 1) * segmentsPerCell; Vertex[] stripVertices = new Vertex[totalPoints * 2]; int idx = 0; for (int i = 0; i < n; i++) { float x = centers[i]; float r = radii[i]; Color col = TemperatureColor(temperatures[i]); // pipes still use temperature stripVertices[idx++] = new Vertex(new Vector2f(x, pipeCenterY - r), col); stripVertices[idx++] = new Vertex(new Vector2f(x, pipeCenterY + r), col); if (i < n - 1) { for (int s = 1; s <= segmentsPerCell; s++) { float t = s / (float)segmentsPerCell; float st = SmoothStep(0f, 1f, t); float xi = centers[i] + (centers[i + 1] - centers[i]) * t; float ri = radii[i] + (radii[i + 1] - radii[i]) * st; double Ti = temperatures[i] + (temperatures[i + 1] - temperatures[i]) * st; Color coli = TemperatureColor(Ti); stripVertices[idx++] = new Vertex(new Vector2f(xi, pipeCenterY - ri), coli); stripVertices[idx++] = new Vertex(new Vector2f(xi, pipeCenterY + ri), coli); } } } var pipeMesh = new VertexArray(PrimitiveType.TriangleStrip, (uint)stripVertices.Length); for (int i = 0; i < stripVertices.Length; i++) pipeMesh[(uint)i] = stripVertices[i]; target.Draw(pipeMesh); } } }