Actualización de la simulación de fluidos: se han agregado nuevos comandos para iniciar y detener la simulación de fluidos, así como métodos para manejar su inicio, detención y actualización. Se han modificado las referencias de Emgu.CV a una versión anterior y se han realizado ajustes en los archivos XAML para reflejar cambios en los espacios de nombres.

This commit is contained in:
Miguel 2025-06-13 19:52:43 +02:00
parent f42a4bb5d1
commit b8d3c953e6
8 changed files with 394 additions and 18 deletions

View File

@ -80,9 +80,9 @@
<PackageReference Include="Aether.Physics2D" Version="2.2.0" /> <PackageReference Include="Aether.Physics2D" Version="2.2.0" />
<PackageReference Include="ClosedXML" Version="0.105.0-rc" /> <PackageReference Include="ClosedXML" Version="0.105.0-rc" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Emgu.CV" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.UI" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" />
<PackageReference Include="LanguageDetection" Version="1.2.0" /> <PackageReference Include="LanguageDetection" Version="1.2.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" /> <PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />

9
Icons/fluid.svg Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Fluid</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="fluid" fill="#000000" fill-rule="nonzero">
<path d="M12,2 C8.1,2 5,5.1 5,9 C5,13.9 12,22 12,22 C12,22 19,13.9 19,9 C19,5.1 15.9,2 12,2 Z M12,11.5 C10.6,11.5 9.5,10.4 9.5,9 C9.5,7.6 10.6,6.5 12,6.5 C13.4,6.5 14.5,7.6 14.5,9 C14.5,10.4 13.4,11.5 12,11.5 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 622 B

View File

@ -1,4 +1,5 @@
using System.ComponentModel; using CtrEditor;
using System.ComponentModel;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Ookii.Dialogs.Wpf; using Ookii.Dialogs.Wpf;
@ -83,8 +84,12 @@ namespace CtrEditor
public ICommand StopSimulationCommand { get; } public ICommand StopSimulationCommand { get; }
public ICommand ItemDoubleClickCommand { get; private set; } public ICommand ItemDoubleClickCommand { get; private set; }
public SimulationFluidsViewModel FluidSimulation { get; private set; }
public ICommand TBStartSimulationCommand { get; } public ICommand TBStartSimulationCommand { get; }
public ICommand TBStopSimulationCommand { get; } public ICommand TBStopSimulationCommand { get; }
public ICommand TBStartFluidSimulationCommand { get; }
public ICommand TBStopFluidSimulationCommand { get; }
public ICommand TBSaveCommand { get; } public ICommand TBSaveCommand { get; }
public ICommand TBExtractTagsCommand { get; } public ICommand TBExtractTagsCommand { get; }
@ -358,6 +363,11 @@ namespace CtrEditor
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning); TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning); TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
// Inicializar simulación de fluidos
FluidSimulation = new SimulationFluidsViewModel(this);
TBStartFluidSimulationCommand = new RelayCommand(StartFluidSimulation, () => !FluidSimulation.IsFluidSimulationRunning);
TBStopFluidSimulationCommand = new RelayCommand(StopFluidSimulation, () => FluidSimulation.IsFluidSimulationRunning);
TBSaveCommand = new RelayCommand(Save); TBSaveCommand = new RelayCommand(Save);
TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl); TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl);
@ -749,7 +759,10 @@ namespace CtrEditor
private void StartSimulation() private void StartSimulation()
{ {
IsSimulationRunning = true; // Detener simulación de fluidos si está ejecutándose
StopFluidSimulation();
IsSimulationRunning = true;
// Ocultar rectángulos de selección antes de iniciar la simulación // Ocultar rectángulos de selección antes de iniciar la simulación
_objectManager.UpdateSelectionVisuals(); _objectManager.UpdateSelectionVisuals();
@ -776,12 +789,33 @@ namespace CtrEditor
{ {
simulationManager.Debug_ClearSimulationShapes(); simulationManager.Debug_ClearSimulationShapes();
Debug_SimulacionCreado = false; Debug_SimulacionCreado = false;
} }
_timerSimulacion.Stop(); _timerSimulacion.Stop();
// Restaurar los rectángulos de selección si hay objetos seleccionados // Restaurar los rectángulos de selección si hay objetos seleccionados
_objectManager.UpdateSelectionVisuals(); _objectManager.UpdateSelectionVisuals();
} }
/// <summary>
/// Inicia la simulación de fluidos independiente
/// </summary>
public void StartFluidSimulation()
{
// Detener simulación física si está ejecutándose
StopSimulation();
FluidSimulation.StartFluidSimulation();
CommandManager.InvalidateRequerySuggested();
}
/// <summary>
/// Detiene la simulación de fluidos independiente
/// </summary>
public void StopFluidSimulation()
{
FluidSimulation.StopFluidSimulation();
CommandManager.InvalidateRequerySuggested();
}
private void OnTickSimulacion(object sender, EventArgs e) private void OnTickSimulacion(object sender, EventArgs e)
{ {

View File

@ -223,6 +223,39 @@ namespace CtrEditor.ObjetosSim
} }
} }
/// <summary>
/// Llamado cuando se inicia la simulación de fluidos
/// </summary>
public void OnFluidSimulationStart()
{
// Crear la simulación de fluidos si es necesario
UpdateGeometryStart();
}
/// <summary>
/// Llamado cuando se detiene la simulación de fluidos
/// </summary>
public void OnFluidSimulationStop()
{
// Detener recursos si es necesario
SimulationStop();
}
/// <summary>
/// Actualiza la simulación de fluidos
/// </summary>
public void UpdateFluidSimulation(float deltaTime)
{
// Actualizar la simulación con el delta time
if (_simFluidos != null)
{
_simFluidos.Actualizar(deltaTime);
// Actualizar el control visual
UpdateControl((int)(deltaTime * 1000));
}
}
public override void SimulationStop() public override void SimulationStop()
{ {
// Limpiar recursos si es necesario cuando se detiene la simulación // Limpiar recursos si es necesario cuando se detiene la simulación

View File

@ -1,16 +1,15 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTuberiaFluido" <UserControl x:Class="CtrEditor.ObjetosSim.ucTuberiaFluido"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"> xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
xmlns:ctr="clr-namespace:CtrEditor">
<UserControl.DataContext> <UserControl.DataContext>
<vm:osTuberiaFluido/> <local:osTuberiaFluido/>
</UserControl.DataContext> </UserControl.DataContext>
<Grid> <Grid>
<Path x:Name="TuberiaPath" <Path x:Name="TuberiaPath"
Stroke="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"
StrokeThickness="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Data="{Binding PathData}" Data="{Binding PathData}"
StrokeEndLineCap="Round" StrokeEndLineCap="Round"
StrokeStartLineCap="Round" StrokeStartLineCap="Round"
@ -23,8 +22,6 @@
<!-- Visualización opcional de la densidad del fluido dentro de la tubería --> <!-- Visualización opcional de la densidad del fluido dentro de la tubería -->
<Path x:Name="FluidoPath" <Path x:Name="FluidoPath"
Data="{Binding PathData}" Data="{Binding PathData}"
StrokeThickness="{Binding DiametroInterno, Converter={StaticResource MeterToPixelConverter}}"
Stroke="{Binding ColorFluido, Converter={StaticResource ColorToBrushConverter}}"
Opacity="{Binding DensidadFluido}" Opacity="{Binding DensidadFluido}"
StrokeEndLineCap="Round" StrokeEndLineCap="Round"
StrokeStartLineCap="Round" StrokeStartLineCap="Round"

View File

@ -1,10 +1,10 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucValvulaFluido" <UserControl x:Class="CtrEditor.ObjetosSim.ucValvulaFluido"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"> xmlns:local="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext> <UserControl.DataContext>
<vm:osValvulaFluido/> <local:osValvulaFluido/>
</UserControl.DataContext> </UserControl.DataContext>
<Grid> <Grid>

View File

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using nkast.Aether.Physics2D.Common;
using CtrEditor.Simulacion.Fluids;
using CtrEditor.ObjetosSim;
namespace CtrEditor.Simulacion
{
/// <summary>
/// Gestor para la simulación de fluidos independiente de la simulación física principal
/// </summary>
public class FluidSimulationManager
{
private SimulacionFluidos _simulacion;
private readonly List<Action> _deferredActions = new List<Action>();
private readonly List<osSistemaFluidos> _sistemasRegistrados = new List<osSistemaFluidos>();
private float _defaultWidth = 10.0f;
private float _defaultHeight = 10.0f;
private bool _isRunning = false;
private Stopwatch _stopwatch;
private double _lastUpdateTime;
public bool IsRunning => _isRunning;
/// <summary>
/// Constructor
/// </summary>
public FluidSimulationManager()
{
_stopwatch = new Stopwatch();
}
/// <summary>
/// Inicializa la simulación de fluidos
/// </summary>
public void Initialize()
{
if (_simulacion == null)
{
_simulacion = new SimulacionFluidos(
_defaultWidth,
_defaultHeight,
10000, // max partículas
new Vector2(0, 9.8f) // gravedad por defecto
);
}
}
/// <summary>
/// Inicia la simulación de fluidos
/// </summary>
public void Start()
{
if (!_isRunning)
{
_isRunning = true;
_stopwatch.Start();
_lastUpdateTime = _stopwatch.Elapsed.TotalMilliseconds;
// Notificar a los sistemas de fluidos
foreach (var sistema in _sistemasRegistrados)
{
sistema.OnFluidSimulationStart();
}
}
}
/// <summary>
/// Detiene la simulación de fluidos
/// </summary>
public void Stop()
{
if (_isRunning)
{
_isRunning = false;
_stopwatch.Stop();
_stopwatch.Reset();
// Notificar a los sistemas de fluidos
foreach (var sistema in _sistemasRegistrados)
{
sistema.OnFluidSimulationStop();
}
}
}
/// <summary>
/// Actualiza un paso de la simulación de fluidos
/// </summary>
public void Step()
{
if (!_isRunning) return;
// Calcular delta time
double currentTime = _stopwatch.Elapsed.TotalMilliseconds;
float deltaTime = (float)(currentTime - _lastUpdateTime) / 1000.0f;
_lastUpdateTime = currentTime;
// Asegurar que deltaTime no es demasiado grande (evita inestabilidades)
if (deltaTime > 0.05f) deltaTime = 0.05f;
// Procesar acciones diferidas
foreach (var action in _deferredActions)
{
action();
}
_deferredActions.Clear();
// Actualizar la simulación
_simulacion?.Actualizar(deltaTime);
// Notificar a los sistemas de fluidos
foreach (var sistema in _sistemasRegistrados)
{
sistema.UpdateFluidSimulation(deltaTime);
}
}
/// <summary>
/// Registra un sistema de fluidos para ser actualizado
/// </summary>
public void RegisterFluidSystem(osSistemaFluidos sistema)
{
if (!_sistemasRegistrados.Contains(sistema))
{
_sistemasRegistrados.Add(sistema);
// Si el sistema ya está corriendo, notificar al nuevo sistema
if (_isRunning)
{
sistema.OnFluidSimulationStart();
}
}
}
/// <summary>
/// Elimina un sistema de fluidos del registro
/// </summary>
public void UnregisterFluidSystem(osSistemaFluidos sistema)
{
_sistemasRegistrados.Remove(sistema);
}
/// <summary>
/// Añade una acción para ser ejecutada en el próximo paso de simulación
/// </summary>
public void AddDeferredAction(Action action)
{
_deferredActions.Add(action);
}
/// <summary>
/// Obtiene la instancia de simulación de fluidos
/// </summary>
public SimulacionFluidos GetSimulacion()
{
if (_simulacion == null)
{
Initialize();
}
return _simulacion;
}
/// <summary>
/// Limpia los recursos de la simulación
/// </summary>
public void Clear()
{
Stop();
_simulacion = null;
_sistemasRegistrados.Clear();
_deferredActions.Clear();
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Diagnostics;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Simulacion.Fluids;
using nkast.Aether.Physics2D.Common;
using CtrEditor.ObjetosSim;
namespace CtrEditor
{
/// <summary>
/// ViewModel para controlar la simulación de fluidos de forma independiente
/// </summary>
public partial class SimulationFluidsViewModel : ObservableObject
{
private MainViewModel _mainViewModel;
private readonly DispatcherTimer _timerSimulacionFluidos;
private Stopwatch _stopwatch;
private double _lastUpdateTime;
// Propiedades observables
[ObservableProperty]
private bool isFluidSimulationRunning;
[ObservableProperty]
private float fps;
[ObservableProperty]
private int particulasCount;
/// <summary>
/// Constructor
/// </summary>
/// <param name="mainViewModel">Referencia al ViewModel principal</param>
public SimulationFluidsViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
// Inicializar timer para simulación de fluidos
_timerSimulacionFluidos = new DispatcherTimer();
_timerSimulacionFluidos.Interval = TimeSpan.FromMilliseconds(16); // ~60fps
_timerSimulacionFluidos.Tick += OnTickSimulacionFluidos;
_stopwatch = new Stopwatch();
_stopwatch.Start();
}
/// <summary>
/// Inicia la simulación de fluidos
/// </summary>
public void StartFluidSimulation()
{
if (IsFluidSimulationRunning)
return;
IsFluidSimulationRunning = true;
// Notificar a todos los objetos que usan fluidos que inicie su simulación
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable is osSistemaFluidos sistemaFluidos)
{
sistemaFluidos.UpdateGeometryStart();
}
}
_lastUpdateTime = _stopwatch.Elapsed.TotalMilliseconds;
_timerSimulacionFluidos.Start();
}
/// <summary>
/// Detiene la simulación de fluidos
/// </summary>
public void StopFluidSimulation()
{
if (!IsFluidSimulationRunning)
return;
IsFluidSimulationRunning = false;
_timerSimulacionFluidos.Stop();
// Notificar a todos los objetos que usan fluidos que detenga su simulación
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable is osSistemaFluidos sistemaFluidos)
{
sistemaFluidos.SimulationStop();
}
}
}
/// <summary>
/// Evento que se dispara cada tick de la simulación de fluidos
/// </summary>
private void OnTickSimulacionFluidos(object sender, EventArgs e)
{
// Calcular delta time
double currentTime = _stopwatch.Elapsed.TotalMilliseconds;
float deltaTime = (float)(currentTime - _lastUpdateTime) / 1000.0f; // convertir a segundos
_lastUpdateTime = currentTime;
int totalParticleCount = 0;
// Actualizar todos los sistemas de fluidos
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable is osSistemaFluidos sistemaFluidos && sistemaFluidos.Show_On_This_Page)
{
// Actualizar la simulación con el deltaTime calculado
if (sistemaFluidos._simFluidos != null)
{
sistemaFluidos._simFluidos.Actualizar(deltaTime);
totalParticleCount += sistemaFluidos._simFluidos.ParticlesCount;
}
// Actualizar controles visuales
sistemaFluidos.UpdateControl((int)(deltaTime * 1000));
}
}
// Actualizar estadísticas
Fps = 1.0f / Math.Max(deltaTime, 0.001f);
ParticulasCount = totalParticleCount;
}
}
}