#region ================== Copyright (c) 2023 Boris Iwanski /* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the * * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program.If not, see. */ #endregion using System.Collections.Generic; using System.Drawing; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Rendering; namespace CodeImp.DoomBuilder.SoundPropagationMode { internal class SoundNode { public Vector2D Position { get; set; } public List Neighbors { get; set; } public SoundNode From { get; set; } public double G { get; set; } public double H { get; } public double F { get; set; } // It's G + H, but computing it on the fly is too expensive public bool IsBlocking { get; } public bool IsSkip { get; set; } public SoundNode(Vector2D position) { Position = position; G = double.MaxValue; H = double.MaxValue; IsBlocking = false; IsSkip = false; Neighbors = new List(); } public SoundNode(Vector2D position, SoundNode destination): this(position) { H = Vector2D.Distance(Position, destination.Position); } public SoundNode(Linedef linedef, SoundNode destination) : this(linedef.Line.GetCoordinatesAt(0.5), destination) { IsBlocking = linedef.IsFlagSet(SoundPropagationMode.BlockSoundFlag); } /// /// Recomputes the values for the sound node's neighbors /// /// The open set, the add the neighbor to if necessary /// The start sound node public void ProcessNeighbors(List openset, SoundNode start) { bool blockinginpath = HasBlockingInPath(start); foreach (SoundNode neighbor in Neighbors) { // Skip neighbors that are blocking if there's already a blocking sound node in the path // Also skip neighbors that are set to be skipped if ((neighbor.IsBlocking && blockinginpath) || neighbor.IsSkip) continue; double newg = G + Vector2D.Distance(Position, neighbor.Position); // Compute new values if the path is better if (newg < neighbor.G) { neighbor.From = this; neighbor.G = newg; neighbor.F = neighbor.G + neighbor.H; if (!openset.Contains(neighbor)) openset.Add(neighbor); } } } /// /// Checks if the path from this sound node to the start sound node has a blocking sound node /// /// The start sound node /// true if there is a blocking sound node in the path, false if there isn't private bool HasBlockingInPath(SoundNode start) { SoundNode current = this; while(current != start) { if (current.IsBlocking) return true; current = current.From; } return false; } /// /// Resets the sound node's G and F values, and the sound node that leads here /// public void Reset() { From = null; G = double.MaxValue; F = double.MaxValue; } /// /// Renders the path from this node to the beginning. Traces the path from this sound node back to the start sound node /// /// The Renderer2D to render with internal void RenderPath(IRenderer2D renderer) { SoundNode current = this; // If the current node is null we have reached the beginning while(current != null) { // Do not render the start and end sound nodes if (current != this && current.From != null) { RectangleF rectangle = new RectangleF((float)(current.Position.x - 4 / renderer.Scale), (float)(current.Position.y - 4 / renderer.Scale), 8 / renderer.Scale, 8 / renderer.Scale); renderer.RenderRectangleFilled(rectangle, PixelColor.FromColor(Color.Red), true); } if(current.From != null) renderer.RenderLine(current.Position, current.From.Position, 1.0f, PixelColor.FromColor(Color.Red), true); // One step back current = current.From; } } } }