189 lines
7.0 KiB
C#
189 lines
7.0 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Lógica para ucSistemaFluidos.xaml
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Actualiza la visualización de partículas
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clase que maneja el rendering optimizado de partículas usando DrawingVisual
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Actualiza la visualización de las partículas de fluido
|
|
/// </summary>
|
|
/// <param name="particulas">Lista de partículas a visualizar</param>
|
|
/// <param name="tamañoParticula">Tamaño de las partículas en metros</param>
|
|
/// <param name="colorFluido">Color base del fluido</param>
|
|
/// <param name="opacidadBase">Opacidad base de las partículas</param>
|
|
public void ActualizarParticulasFluido(IEnumerable<Particle> 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mezcla dos colores según un factor (0-1)
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|