using SFML.Graphics; using SFML.System; namespace Car_simulation.UI.Instruments { public class Tachometer { private RectangleShape _background; private RectangleShape _needle; private Font _font; private Text _label; private Text _rpmText; private Color _normalColor = Color.White; private Color _warningColor = new Color(255, 100, 100); public Vector2f Position { get; set; } public float Size { get; set; } = 200f; private float _currentRPM = 0f; private float _maxRpm = 7000f; public Tachometer(Font font, Vector2f position, float size, float maxRpm) { _maxRpm = maxRpm; _font = font; Position = position; Size = size; Initialize(); } private void Initialize() { // Background _background = new RectangleShape(new Vector2f(Size, Size)); _background.Position = Position; _background.FillColor = new Color(40, 40, 50); _background.OutlineThickness = 2; _background.OutlineColor = Color.White; // Needle float needleLength = Size * 0.4f; _needle = new RectangleShape(new Vector2f(needleLength, 4)); _needle.Position = new Vector2f( Position.X + Size / 2, Position.Y + Size / 2 ); _needle.FillColor = Color.Red; _needle.Origin = new Vector2f(needleLength * 0.875f, 2); // Offset origin to create pivot point // Labels _label = new Text("RPM", _font, (uint)(Size * 0.1f)); _label.FillColor = Color.White; _label.Position = new Vector2f( Position.X + Size / 2 - _label.GetLocalBounds().Width / 2, Position.Y + Size * 0.80f ); _rpmText = new Text("0", _font, (uint)(Size * 0.12f)); _rpmText.FillColor = _normalColor; _rpmText.Position = new Vector2f( Position.X + Size / 2 - 20, Position.Y + Size ); } public void Update(float rpm) { _currentRPM = rpm; float rpmRatio = Math.Clamp(rpm / _maxRpm, 0f, 1f); float needleAngle = -45 + (270 * rpmRatio); _needle.Rotation = needleAngle; // Update RPM text _rpmText.DisplayedString = $"{rpm:F0}"; // Center RPM text FloatRect bounds = _rpmText.GetLocalBounds(); _rpmText.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2); _rpmText.Position = new Vector2f( Position.X + Size / 2, Position.Y + Size * 0.35f ); } public void Draw(RenderWindow window) { window.Draw(_background); DrawTickMarks(window); window.Draw(_needle); window.Draw(_label); window.Draw(_rpmText); } private void DrawTickMarks(RenderWindow window) { float targetAngle = 270; int marks = (int)MathF.Ceiling(_maxRpm / 1000) * 2; for (int i = marks; i >= 0; i--) { float angle = 135 + (i * (targetAngle / marks)); // 270° divided into 10 segments float startRadius = Size * 0.45f; float endRadius = Size * 0.42f; if (i % 2 == 0) // Major tick { endRadius = Size * 0.38f; float rpmValue = (i * 1000); Text label = new Text($"{rpmValue / 2000:F0}", _font, 12); label.FillColor = Color.White; float labelRadius = Size * 0.32f; float _radAngle = angle * (float)Math.PI / 180f; Vector2f labelPos = new Vector2f( Position.X + Size / 2 + labelRadius * (float)Math.Cos(_radAngle), Position.Y + Size / 2 + labelRadius * (float)Math.Sin(_radAngle) ); FloatRect bounds = label.GetLocalBounds(); label.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2); label.Position = labelPos; window.Draw(label); } float radAngle = angle * (float)Math.PI / 180f; Vector2f startPos = new Vector2f( Position.X + Size / 2 + startRadius * (float)Math.Cos(radAngle), Position.Y + Size / 2 + startRadius * (float)Math.Sin(radAngle) ); Vector2f endPos = new Vector2f( Position.X + Size / 2 + endRadius * (float)Math.Cos(radAngle), Position.Y + Size / 2 + endRadius * (float)Math.Sin(radAngle) ); Vertex[] line = new Vertex[2] { new Vertex(startPos, Color.White), new Vertex(endPos, Color.White) }; window.Draw(line, PrimitiveType.Lines); } } public void SetPosition(Vector2f position) { Position = position; Initialize(); // Re-initialize with new position } public void SetSize(float size) { Size = size; Initialize(); // Re-initialize with new size } } }