using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using CtrEditor.FuncionesBase; using CtrEditor.Simulacion.Fluids; using tainicom.Aether.Physics2D.Fluids; namespace CtrEditor.ObjetosSim { /// /// Lógica para ucSistemaFluidos.xaml /// public partial class ucSistemaFluidos : UserControl, IDataContainer { public osBase? Datos { get; set; } public int zIndex_fromFrames { get; set; } // Host para el sistema visual de partículas private ParticulasFluidoHost _hostVisual; // Timer para medir FPS private DateTime _ultimaActualizacion = DateTime.Now; private int _contadorFrames = 0; private double _fps = 0; public ucSistemaFluidos() { InitializeComponent(); this.Loaded += OnLoaded; this.Unloaded += OnUnloaded; // Inicializar el host visual y agregarlo al contenedor _hostVisual = new ParticulasFluidoHost(); ContenedorVisual.Children.Add(_hostVisual); // Configurar actualizaciones periódicas para FPS CompositionTarget.Rendering += OnRendering; } private void OnRendering(object sender, EventArgs e) { _contadorFrames++; // Calcular FPS cada segundo TimeSpan elapsed = DateTime.Now - _ultimaActualizacion; if (elapsed.TotalSeconds >= 1) { _fps = _contadorFrames / elapsed.TotalSeconds; _contadorFrames = 0; _ultimaActualizacion = DateTime.Now; if (Datos is osSistemaFluidos vm) { vm.Fps = _fps; } } // Actualizar la visualización de partículas ActualizarVisualizacion(); } private void OnLoaded(object sender, RoutedEventArgs e) { Datos?.ucLoaded(); } private void OnUnloaded(object sender, RoutedEventArgs e) { Datos?.ucUnLoaded(); CompositionTarget.Rendering -= OnRendering; } public void Highlight(bool State) { } public ZIndexEnum ZIndex_Base() { return ZIndexEnum.Dinamicos; } /// /// Actualiza la visualización de partículas /// private void ActualizarVisualizacion() { if (Datos is osSistemaFluidos viewModel) { // Obtener la simulación activa desde el ViewModel var simulacion = viewModel._simFluidos?.SistemaFluido; if (simulacion != null) { // Configurar propiedades visuales float tamañoParticula = viewModel.TamañoParticula; Color colorFluido = viewModel.ColorFluido; // Actualizar la visualización _hostVisual.ActualizarParticulasFluido(simulacion.Particles, tamañoParticula, colorFluido, viewModel.OpacidadParticulas); } } } } /// /// Clase que maneja el rendering optimizado de partículas usando DrawingVisual /// public class ParticulasFluidoHost : FrameworkElement { private readonly DrawingVisual _visual = new DrawingVisual(); private readonly MeterToPixelConverter _converter = new MeterToPixelConverter(); public ParticulasFluidoHost() { AddVisualChild(_visual); } protected override int VisualChildrenCount => 1; protected override Visual GetVisualChild(int index) { if (index != 0) throw new ArgumentOutOfRangeException(); return _visual; } /// /// Actualiza la visualización de las partículas de fluido /// /// Lista de partículas a visualizar /// Tamaño de las partículas en metros /// Color base del fluido /// Opacidad base de las partículas public void ActualizarParticulasFluido(IEnumerable particulas, float tamañoParticula, Color colorFluido, double opacidadBase) { using (DrawingContext dc = _visual.RenderOpen()) { foreach (var particula in particulas) { // Ignorar partículas inactivas u ocultas if (particula == null) continue; // Convertir posición a píxeles float x = (float)_converter.Convert((particula.Position.X / 100) + 5, null, null, null); float y = (float)_converter.Convert(particula.Position.Y / 100, null, null, null); float radio = (float)_converter.Convert(tamañoParticula / 2, null, null, null); // Calcular opacidad basada en densidad double opacidad = Math.Clamp(particula.Density / 15.0, 0.4, 0.9) * opacidadBase; // Calcular color basado en velocidad (opcional) Color colorFinal = colorFluido; double velocidad = Math.Sqrt(particula.Velocity.X * particula.Velocity.X + particula.Velocity.Y * particula.Velocity.Y); if (velocidad > 50) { // Partículas más rápidas tienden a ser más claras float factor = Math.Min(1.0f, (float)velocidad / 400); colorFinal = MezclarColores(colorFluido, Colors.White, factor); } SolidColorBrush brush = new SolidColorBrush(colorFinal) { Opacity = opacidad }; // Dibujar partícula dc.DrawEllipse(brush, null, new Point(x, y), radio, radio); } } InvalidateVisual(); } /// /// Mezcla dos colores según un factor (0-1) /// private Color MezclarColores(Color color1, Color color2, float factor) { byte r = (byte)(color1.R + (color2.R - color1.R) * factor); byte g = (byte)(color1.G + (color2.G - color1.G) * factor); byte b = (byte)(color1.B + (color2.B - color1.B) * factor); return Color.FromRgb(r, g, b); } } }